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.
- 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/createRscRenderHelpers.ts +1 -1
- 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,533 +1,510 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* ARCHITECTURE OVERVIEW:
|
|
7
|
-
*
|
|
8
|
-
* SERVER-SIDE vs CLIENT-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. Create headless RSC stream (for .rsc file)
|
|
14
|
-
* 2. Create full RSC stream (for HTML generation)
|
|
15
|
-
* 3. Create HTML transform stream that converts RSC to HTML
|
|
16
|
-
* 4. Both streams are piped to file writers
|
|
17
|
-
*
|
|
18
|
-
* SIMPLIFIED APPROACH:
|
|
19
|
-
* This implementation follows the same simple pattern as the client side,
|
|
20
|
-
* avoiding complex backpressure handling and race conditions.
|
|
2
|
+
* vite-plugin-react-server
|
|
3
|
+
* Copyright (c) Nico Brinkkemper
|
|
4
|
+
* MIT License
|
|
21
5
|
*/
|
|
22
|
-
import { createRenderMetrics } from
|
|
23
|
-
import { routeToURL } from
|
|
24
|
-
import { handleError } from
|
|
25
|
-
import { assertReactServer } from
|
|
26
|
-
import { renderRscStream } from
|
|
27
|
-
import { createMainThreadHandlers } from
|
|
28
|
-
import { createRscToHtmlStream } from
|
|
29
|
-
import { resolveComponent } from
|
|
30
|
-
import { resolvePageAndProps } from
|
|
31
|
-
import { Root
|
|
32
|
-
import { Html
|
|
33
|
-
import { createStreamMetrics } from
|
|
34
|
-
import { join } from
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
// Load Root component
|
|
132
|
-
if (handlerOptions.rootPath) {
|
|
133
|
-
try {
|
|
134
|
-
const rootResult = await resolveComponent({
|
|
135
|
-
componentPath: handlerOptions.rootPath,
|
|
136
|
-
exportName: handlerOptions.rootExportName,
|
|
137
|
-
loader: handlerOptions.loader,
|
|
138
|
-
});
|
|
139
|
-
if (rootResult.type === "success") {
|
|
140
|
-
RootComponent = rootResult.component;
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
handlerOptions.logger?.warn(`Failed to load Root component from ${handlerOptions.rootPath}: ${rootResult.error?.message || "Unknown error"}`);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
catch (error) {
|
|
147
|
-
handlerOptions.logger?.warn(`Error loading Root component from ${handlerOptions.rootPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
// Load Html component
|
|
151
|
-
if (handlerOptions.htmlPath) {
|
|
152
|
-
try {
|
|
153
|
-
const htmlResult = await resolveComponent({
|
|
154
|
-
componentPath: handlerOptions.htmlPath,
|
|
155
|
-
exportName: handlerOptions.htmlExportName,
|
|
156
|
-
loader: handlerOptions.loader,
|
|
157
|
-
});
|
|
158
|
-
if (htmlResult.type === "success") {
|
|
159
|
-
HtmlComponent = htmlResult.component;
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
handlerOptions.logger?.warn(`Failed to load Html component from ${handlerOptions.htmlPath}: ${htmlResult.error?.message || "Unknown error"}`);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
handlerOptions.logger?.warn(`Error loading Html component from ${handlerOptions.htmlPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
167
|
-
}
|
|
6
|
+
import { createRenderMetrics } from '../metrics/createRenderMetrics.js';
|
|
7
|
+
import { routeToURL } from '../utils/routeToURL.js';
|
|
8
|
+
import { handleError } from '../error/handleError.js';
|
|
9
|
+
import { assertReactServer } from '../config/getCondition.js';
|
|
10
|
+
import { renderRscStream } from '../stream/renderRscStream.server.js';
|
|
11
|
+
import { createMainThreadHandlers } from '../stream/createMainThreadHandlers.js';
|
|
12
|
+
import { createRscToHtmlStream } from './rscToHtmlStream.server.js';
|
|
13
|
+
import { resolveComponent } from '../helpers/resolveComponent.js';
|
|
14
|
+
import { resolvePageAndProps } from '../helpers/resolvePageAndProps.js';
|
|
15
|
+
import { Root } from '../components/root.js';
|
|
16
|
+
import { Html } from '../components/html.js';
|
|
17
|
+
import { createStreamMetrics } from '../metrics/createStreamMetrics.js';
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
import { trackHeadlessStreamError, createHeadlessStreamState, hasHeadlessStreamError } from '../helpers/headlessStreamState.js';
|
|
20
|
+
|
|
21
|
+
const renderPage = async function* renderPage2(handlerOptions) {
|
|
22
|
+
assertReactServer();
|
|
23
|
+
const baseDir = join(
|
|
24
|
+
handlerOptions.build.outDir,
|
|
25
|
+
handlerOptions.build.static
|
|
26
|
+
);
|
|
27
|
+
const routePath = handlerOptions.route.replace(/^\//, "");
|
|
28
|
+
const htmlMetrics = createRenderMetrics({
|
|
29
|
+
route: handlerOptions.route,
|
|
30
|
+
type: "html",
|
|
31
|
+
fromMainThread: false,
|
|
32
|
+
// Server: HTML rendered in worker
|
|
33
|
+
fromRscWorker: false,
|
|
34
|
+
fromHtmlWorker: true,
|
|
35
|
+
baseDir,
|
|
36
|
+
routePath,
|
|
37
|
+
fileName: handlerOptions.build.htmlOutputPath,
|
|
38
|
+
outputPath: join(baseDir, routePath, handlerOptions.build.htmlOutputPath)
|
|
39
|
+
});
|
|
40
|
+
const rscFullMetrics = createRenderMetrics({
|
|
41
|
+
route: handlerOptions.route,
|
|
42
|
+
type: "rsc-full",
|
|
43
|
+
fromMainThread: true,
|
|
44
|
+
// Server: RSC rendered on main thread
|
|
45
|
+
fromRscWorker: false,
|
|
46
|
+
fromHtmlWorker: false
|
|
47
|
+
});
|
|
48
|
+
const rscHeadlessMetrics = createRenderMetrics({
|
|
49
|
+
route: handlerOptions.route,
|
|
50
|
+
type: "rsc-headless",
|
|
51
|
+
fromMainThread: true,
|
|
52
|
+
// Server: RSC rendered on main thread
|
|
53
|
+
fromRscWorker: false,
|
|
54
|
+
fromHtmlWorker: false,
|
|
55
|
+
baseDir,
|
|
56
|
+
routePath,
|
|
57
|
+
fileName: handlerOptions.build.rscOutputPath,
|
|
58
|
+
outputPath: join(baseDir, routePath, handlerOptions.build.rscOutputPath)
|
|
59
|
+
});
|
|
60
|
+
let headlessRscHandler = null;
|
|
61
|
+
let fullRscHandler = null;
|
|
62
|
+
let htmlTransformStream = null;
|
|
63
|
+
let headlessStreamErrored = false;
|
|
64
|
+
let headlessError = null;
|
|
65
|
+
let htmlStreamErrored = false;
|
|
66
|
+
let htmlStreamError = null;
|
|
67
|
+
const headlessStreamState = createHeadlessStreamState();
|
|
68
|
+
try {
|
|
69
|
+
if (handlerOptions.verbose) {
|
|
70
|
+
handlerOptions.logger?.info(
|
|
71
|
+
`[renderPage.server] Server-side rendering for route: ${handlerOptions.route}`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
if (!handlerOptions.url) {
|
|
75
|
+
handlerOptions.url = routeToURL(
|
|
76
|
+
handlerOptions.route,
|
|
77
|
+
handlerOptions.moduleBaseURL,
|
|
78
|
+
handlerOptions.build.rscOutputPath
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
let PageComponent = null;
|
|
82
|
+
let RootComponent = null;
|
|
83
|
+
let HtmlComponent = null;
|
|
84
|
+
let pageProps = {};
|
|
85
|
+
if (handlerOptions.pagePath) {
|
|
86
|
+
try {
|
|
87
|
+
const pageAndPropsResult = await resolvePageAndProps({
|
|
88
|
+
pagePath: handlerOptions.pagePath,
|
|
89
|
+
pageExportName: handlerOptions.pageExportName,
|
|
90
|
+
propsPath: handlerOptions.propsPath,
|
|
91
|
+
propsExportName: handlerOptions.propsExportName,
|
|
92
|
+
loader: handlerOptions.loader,
|
|
93
|
+
verbose: handlerOptions.verbose,
|
|
94
|
+
logger: handlerOptions.logger,
|
|
95
|
+
route: handlerOptions.route,
|
|
96
|
+
url: handlerOptions.url,
|
|
97
|
+
moduleBaseURL: handlerOptions.moduleBaseURL,
|
|
98
|
+
build: {
|
|
99
|
+
rscOutputPath: handlerOptions.build.rscOutputPath
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
if (pageAndPropsResult.type === "success") {
|
|
103
|
+
PageComponent = pageAndPropsResult.PageComponent;
|
|
104
|
+
pageProps = pageAndPropsResult.pageProps || {};
|
|
105
|
+
if (handlerOptions.verbose) {
|
|
106
|
+
handlerOptions.logger?.info(
|
|
107
|
+
`[renderPage.server] Successfully loaded page and props for route ${handlerOptions.route}: pageProps=${JSON.stringify(pageProps)}`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
handlerOptions.logger?.warn(
|
|
112
|
+
`Failed to load page and props from ${handlerOptions.pagePath}: ${pageAndPropsResult.error?.message || "Unknown error"}`
|
|
113
|
+
);
|
|
168
114
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
115
|
+
} catch (error) {
|
|
116
|
+
handlerOptions.logger?.warn(
|
|
117
|
+
`Error loading page and props from ${handlerOptions.pagePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (handlerOptions.rootPath) {
|
|
122
|
+
try {
|
|
123
|
+
const rootResult = await resolveComponent({
|
|
124
|
+
componentPath: handlerOptions.rootPath,
|
|
125
|
+
exportName: handlerOptions.rootExportName,
|
|
126
|
+
loader: handlerOptions.loader
|
|
127
|
+
});
|
|
128
|
+
if (rootResult.type === "success") {
|
|
129
|
+
RootComponent = rootResult.component;
|
|
130
|
+
} else {
|
|
131
|
+
handlerOptions.logger?.warn(
|
|
132
|
+
`Failed to load Root component from ${handlerOptions.rootPath}: ${rootResult.error?.message || "Unknown error"}`
|
|
133
|
+
);
|
|
172
134
|
}
|
|
173
|
-
|
|
174
|
-
|
|
135
|
+
} catch (error) {
|
|
136
|
+
handlerOptions.logger?.warn(
|
|
137
|
+
`Error loading Root component from ${handlerOptions.rootPath}: ${error instanceof Error ? error.message : String(error)}`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (handlerOptions.htmlPath) {
|
|
142
|
+
try {
|
|
143
|
+
const htmlResult = await resolveComponent({
|
|
144
|
+
componentPath: handlerOptions.htmlPath,
|
|
145
|
+
exportName: handlerOptions.htmlExportName,
|
|
146
|
+
loader: handlerOptions.loader
|
|
147
|
+
});
|
|
148
|
+
if (htmlResult.type === "success") {
|
|
149
|
+
HtmlComponent = htmlResult.component;
|
|
150
|
+
} else {
|
|
151
|
+
handlerOptions.logger?.warn(
|
|
152
|
+
`Failed to load Html component from ${handlerOptions.htmlPath}: ${htmlResult.error?.message || "Unknown error"}`
|
|
153
|
+
);
|
|
175
154
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
155
|
+
} catch (error) {
|
|
156
|
+
handlerOptions.logger?.warn(
|
|
157
|
+
`Error loading Html component from ${handlerOptions.htmlPath}: ${error instanceof Error ? error.message : String(error)}`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (!RootComponent) {
|
|
162
|
+
RootComponent = Root;
|
|
163
|
+
}
|
|
164
|
+
if (!HtmlComponent) {
|
|
165
|
+
HtmlComponent = Html;
|
|
166
|
+
}
|
|
167
|
+
if (!PageComponent || !RootComponent || !HtmlComponent) {
|
|
168
|
+
yield {
|
|
169
|
+
type: "error",
|
|
170
|
+
error: new Error(
|
|
171
|
+
`Component resolution failed: missing required components (Page: ${!!PageComponent}, Root: ${!!RootComponent}, Html: ${!!HtmlComponent})`
|
|
172
|
+
),
|
|
173
|
+
metrics: {
|
|
174
|
+
rscFull: rscFullMetrics,
|
|
175
|
+
rscHeadless: rscHeadlessMetrics,
|
|
176
|
+
html: htmlMetrics
|
|
188
177
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
178
|
+
};
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const uniqueId = handlerOptions.id ?? `${handlerOptions.route}?id=${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
182
|
+
const newHandlerOptions = {
|
|
183
|
+
...handlerOptions,
|
|
184
|
+
id: uniqueId,
|
|
185
|
+
url: `${handlerOptions.url}`,
|
|
186
|
+
route: `${handlerOptions.route}`,
|
|
187
|
+
PageComponent,
|
|
188
|
+
RootComponent,
|
|
189
|
+
HtmlComponent,
|
|
190
|
+
pageProps
|
|
191
|
+
};
|
|
192
|
+
if (handlerOptions.verbose) {
|
|
193
|
+
handlerOptions.logger?.info(
|
|
194
|
+
`[renderPage.server] Created newHandlerOptions for route ${handlerOptions.route} with pageProps: ${JSON.stringify(pageProps)}`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
const headlessHandlers = createMainThreadHandlers(
|
|
198
|
+
handlerOptions,
|
|
199
|
+
(error, isPanic) => {
|
|
201
200
|
if (handlerOptions.verbose) {
|
|
202
|
-
|
|
201
|
+
handlerOptions.logger?.info(
|
|
202
|
+
`[renderPage.server] Headless stream error handler called for route ${handlerOptions.route}: ${error.message}, isPanic: ${isPanic}`
|
|
203
|
+
);
|
|
203
204
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
handlerOptions.logger?.info(`[renderPage.server] Headless stream error handler called for route ${handlerOptions.route}: ${error.message}, isPanic: ${isPanic}`);
|
|
208
|
-
}
|
|
209
|
-
// Track if the headless stream had errors
|
|
210
|
-
headlessStreamErrored = true;
|
|
211
|
-
headlessError = error instanceof Error ? error : new Error("Headless RSC stream failed");
|
|
212
|
-
// Track headless stream errors for conditional reuse logic (like RSC worker)
|
|
213
|
-
trackHeadlessStreamError(headlessStreamState, handlerOptions.route, headlessError);
|
|
214
|
-
if (handlerOptions.verbose) {
|
|
215
|
-
handlerOptions.logger?.info(`[renderPage.server] Stored headless stream error for route ${handlerOptions.route} in headlessStreamErrors map`);
|
|
216
|
-
}
|
|
217
|
-
// Store panic errors for later handling
|
|
218
|
-
if (isPanic) {
|
|
219
|
-
// For panic threshold "all_errors", the panic error will be handled by renderPages
|
|
220
|
-
if (handlerOptions.verbose) {
|
|
221
|
-
handlerOptions.logger?.info(`[renderPage.server] Panic error detected for route ${handlerOptions.route}, will be handled by renderPages`);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
// Override onData to track metrics
|
|
226
|
-
headlessHandlers.onData = (_id, chunk) => {
|
|
227
|
-
rscHeadlessMetrics.chunks++;
|
|
228
|
-
rscHeadlessMetrics.streamMetrics.bytes += chunk.length;
|
|
229
|
-
};
|
|
230
|
-
headlessRscHandler = renderRscStream({
|
|
231
|
-
...newHandlerOptions,
|
|
232
|
-
htmlPath: '', // Headless RSC - no HTML wrapper
|
|
233
|
-
// If we expect errors, provide a safe Page component that doesn't throw
|
|
234
|
-
PageComponent: newHandlerOptions.PageComponent, // Use original for now, will be overridden if errors occur
|
|
235
|
-
}, headlessHandlers);
|
|
236
|
-
// Note: Panic errors will be yielded from the error handler when they occur
|
|
237
|
-
// No need to check shouldYieldPanicError here as it's set asynchronously
|
|
238
|
-
// Store PageComponent for reuse when headless stream completes (like RSC worker)
|
|
239
|
-
headlessRscHandler.rscStream.on('end', () => {
|
|
240
|
-
// Only store if this is a headless stream and no errors occurred (like RSC worker)
|
|
241
|
-
if (!hasHeadlessStreamError(headlessStreamState, handlerOptions.route)) {
|
|
242
|
-
headlessStreamState.elements.set(uniqueId, {
|
|
243
|
-
PageComponent: newHandlerOptions.PageComponent,
|
|
244
|
-
errored: false
|
|
245
|
-
});
|
|
246
|
-
if (handlerOptions.verbose) {
|
|
247
|
-
handlerOptions.logger?.info(`[renderPage.server] Stored PageComponent for headless stream ${uniqueId}`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
else {
|
|
251
|
-
if (handlerOptions.verbose) {
|
|
252
|
-
handlerOptions.logger?.info(`[renderPage.server] Headless stream errored for route ${handlerOptions.route}, not storing PageComponent for reuse`);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
// Create full RSC handler (for HTML generation) - reuse headless stream elements if no errors
|
|
257
|
-
// For server-side, we create both streams in parallel like the client-side
|
|
258
|
-
let fullPanicError = null;
|
|
259
|
-
const fullHandlers = createMainThreadHandlers(handlerOptions, (error, isPanic) => {
|
|
260
|
-
// If this is a panic error, store it to be handled later
|
|
261
|
-
if (isPanic) {
|
|
262
|
-
fullPanicError = error instanceof Error ? error : new Error("Full RSC stream failed");
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
// Override onData to track metrics
|
|
266
|
-
fullHandlers.onData = (_id, chunk) => {
|
|
267
|
-
rscFullMetrics.chunks++;
|
|
268
|
-
rscFullMetrics.streamMetrics.bytes += chunk.length;
|
|
269
|
-
};
|
|
270
|
-
// Create full RSC handler options - use React.Fragment if headless stream had errors (like RSC worker)
|
|
271
|
-
// Check if there are any existing headless stream errors for this route
|
|
272
|
-
const hasExistingHeadlessError = hasHeadlessStreamError(headlessStreamState, handlerOptions.route);
|
|
273
|
-
const shouldUseFallback = headlessStreamErrored || hasExistingHeadlessError;
|
|
205
|
+
headlessStreamErrored = true;
|
|
206
|
+
headlessError = error instanceof Error ? error : new Error("Headless RSC stream failed");
|
|
207
|
+
trackHeadlessStreamError(headlessStreamState, handlerOptions.route, headlessError);
|
|
274
208
|
if (handlerOptions.verbose) {
|
|
275
|
-
|
|
209
|
+
handlerOptions.logger?.info(
|
|
210
|
+
`[renderPage.server] Stored headless stream error for route ${handlerOptions.route} in headlessStreamErrors map`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
if (isPanic) {
|
|
214
|
+
if (handlerOptions.verbose) {
|
|
215
|
+
handlerOptions.logger?.info(
|
|
216
|
+
`[renderPage.server] Panic error detected for route ${handlerOptions.route}, will be handled by renderPages`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
276
219
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
handlerOptions.logger?.info(`[renderPage.server] Headless stream completed successfully for route ${handlerOptions.route}`);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
220
|
+
}
|
|
221
|
+
);
|
|
222
|
+
headlessHandlers.onData = (_id, chunk) => {
|
|
223
|
+
rscHeadlessMetrics.chunks++;
|
|
224
|
+
rscHeadlessMetrics.streamMetrics.bytes += chunk.length;
|
|
225
|
+
};
|
|
226
|
+
headlessRscHandler = renderRscStream(
|
|
227
|
+
{
|
|
228
|
+
...newHandlerOptions,
|
|
229
|
+
htmlPath: "",
|
|
230
|
+
// Headless RSC - no HTML wrapper
|
|
231
|
+
// If we expect errors, provide a safe Page component that doesn't throw
|
|
232
|
+
PageComponent: newHandlerOptions.PageComponent
|
|
233
|
+
// Use original for now, will be overridden if errors occur
|
|
234
|
+
},
|
|
235
|
+
headlessHandlers
|
|
236
|
+
);
|
|
237
|
+
headlessRscHandler.rscStream.on("end", () => {
|
|
238
|
+
if (!hasHeadlessStreamError(headlessStreamState, handlerOptions.route)) {
|
|
239
|
+
headlessStreamState.elements.set(uniqueId, {
|
|
240
|
+
PageComponent: newHandlerOptions.PageComponent,
|
|
241
|
+
errored: false
|
|
302
242
|
});
|
|
303
243
|
if (handlerOptions.verbose) {
|
|
304
|
-
|
|
244
|
+
handlerOptions.logger?.info(`[renderPage.server] Stored PageComponent for headless stream ${uniqueId}`);
|
|
305
245
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
yield {
|
|
310
|
-
type: "error",
|
|
311
|
-
error: fullPanicError,
|
|
312
|
-
metrics: {
|
|
313
|
-
rscFull: rscFullMetrics,
|
|
314
|
-
rscHeadless: rscHeadlessMetrics,
|
|
315
|
-
html: htmlMetrics,
|
|
316
|
-
},
|
|
317
|
-
};
|
|
318
|
-
return;
|
|
246
|
+
} else {
|
|
247
|
+
if (handlerOptions.verbose) {
|
|
248
|
+
handlerOptions.logger?.info(`[renderPage.server] Headless stream errored for route ${handlerOptions.route}, not storing PageComponent for reuse`);
|
|
319
249
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
moduleBaseURL: handlerOptions.moduleBaseURL,
|
|
329
|
-
projectRoot: handlerOptions.projectRoot,
|
|
330
|
-
build: handlerOptions.build,
|
|
331
|
-
panicThreshold: handlerOptions.panicThreshold,
|
|
332
|
-
verbose: handlerOptions.verbose,
|
|
333
|
-
signal: handlerOptions.signal,
|
|
334
|
-
logger: handlerOptions.logger,
|
|
335
|
-
htmlWorker: handlerOptions.htmlWorker,
|
|
336
|
-
clientPipeableStreamOptions: handlerOptions.clientPipeableStreamOptions,
|
|
337
|
-
onMetrics: handlerOptions.onMetrics,
|
|
338
|
-
htmlTimeout: handlerOptions.htmlTimeout || 15000,
|
|
339
|
-
rscStream: fullRscHandler.rscStream,
|
|
340
|
-
onError: (error, isPanic) => {
|
|
341
|
-
// Track HTML stream errors
|
|
342
|
-
htmlStreamErrored = true;
|
|
343
|
-
htmlStreamError = error;
|
|
344
|
-
if (isPanic) {
|
|
345
|
-
// This is a panic error, it should be yielded as an error result
|
|
346
|
-
if (handlerOptions.verbose) {
|
|
347
|
-
handlerOptions.logger?.error(`[renderPage.server] HTML stream panic error for route ${handlerOptions.route}: ${error.message}`);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
else {
|
|
351
|
-
// For non-panic errors, just log them
|
|
352
|
-
if (handlerOptions.verbose) {
|
|
353
|
-
handlerOptions.logger?.warn(`[renderPage.server] HTML stream error for route ${handlerOptions.route}: ${error.message}`);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
});
|
|
358
|
-
// Create stream wrappers for file writing - simplified like client side
|
|
359
|
-
const rscStreamWrapper = {
|
|
360
|
-
pipe: (destination) => {
|
|
361
|
-
const streamMetrics = createStreamMetrics();
|
|
362
|
-
streamMetrics.startTime = performance.now();
|
|
363
|
-
// Use the headless RSC stream directly for the .rsc file
|
|
364
|
-
const rscFileStream = headlessRscHandler.rscStream;
|
|
365
|
-
rscFileStream.on("data", (chunk) => {
|
|
366
|
-
streamMetrics.chunks++;
|
|
367
|
-
streamMetrics.bytes += chunk.length;
|
|
368
|
-
});
|
|
369
|
-
rscFileStream.on("end", () => {
|
|
370
|
-
streamMetrics.duration = performance.now() - streamMetrics.startTime;
|
|
371
|
-
streamMetrics.endTime = performance.now();
|
|
372
|
-
rscHeadlessMetrics.streamMetrics = streamMetrics;
|
|
373
|
-
rscHeadlessMetrics.chunkRate = streamMetrics.chunks / (streamMetrics.duration / 1000);
|
|
374
|
-
rscHeadlessMetrics.processingTime = streamMetrics.duration;
|
|
375
|
-
rscHeadlessMetrics.memoryUsage = process.memoryUsage();
|
|
376
|
-
rscHeadlessMetrics.chunks = streamMetrics.chunks;
|
|
377
|
-
});
|
|
378
|
-
rscFileStream.pipe(destination);
|
|
379
|
-
return destination;
|
|
380
|
-
},
|
|
381
|
-
abort: () => headlessRscHandler.abort(),
|
|
382
|
-
};
|
|
383
|
-
const htmlStreamWrapper = {
|
|
384
|
-
pipe: (destination) => {
|
|
385
|
-
// Use the HTML transform stream's pipe method directly (same as client side)
|
|
386
|
-
return htmlTransformStream.pipe(destination);
|
|
387
|
-
},
|
|
388
|
-
abort: () => {
|
|
389
|
-
htmlTransformStream.abort();
|
|
390
|
-
},
|
|
391
|
-
on: (event, listener) => {
|
|
392
|
-
// Forward error events from the HTML transform stream to the wrapper
|
|
393
|
-
if (event === 'error') {
|
|
394
|
-
// Access the actual stream from the transform result
|
|
395
|
-
const htmlStream = htmlTransformStream.htmlStream;
|
|
396
|
-
if (htmlStream && typeof htmlStream.on === 'function') {
|
|
397
|
-
htmlStream.on('error', listener);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
return htmlStreamWrapper;
|
|
401
|
-
},
|
|
402
|
-
};
|
|
403
|
-
// Wait for HTML stream to complete or error before yielding success
|
|
404
|
-
// This ensures that any errors from the HTML stream are caught before we yield success
|
|
405
|
-
await new Promise((resolve, reject) => {
|
|
406
|
-
const timeout = setTimeout(() => {
|
|
407
|
-
reject(new Error(`HTML stream timeout for route ${handlerOptions.route}`));
|
|
408
|
-
}, handlerOptions.htmlTimeout || 15000);
|
|
409
|
-
// Check if HTML stream already errored
|
|
410
|
-
if (htmlStreamErrored) {
|
|
411
|
-
clearTimeout(timeout);
|
|
412
|
-
resolve(); // Let the panic threshold logic handle this at the renderPages level
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
// Set up a flag to track if we've resolved
|
|
416
|
-
let resolved = false;
|
|
417
|
-
// Create a wrapper that resolves the promise when the stream completes
|
|
418
|
-
const originalPipe = htmlTransformStream.pipe;
|
|
419
|
-
htmlTransformStream.pipe = function (destination) {
|
|
420
|
-
const result = originalPipe.call(this, destination);
|
|
421
|
-
// Listen for the destination stream to end
|
|
422
|
-
destination.on('finish', () => {
|
|
423
|
-
if (!resolved) {
|
|
424
|
-
resolved = true;
|
|
425
|
-
clearTimeout(timeout);
|
|
426
|
-
resolve();
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
destination.on('error', (error) => {
|
|
430
|
-
if (!resolved) {
|
|
431
|
-
resolved = true;
|
|
432
|
-
clearTimeout(timeout);
|
|
433
|
-
reject(error);
|
|
434
|
-
}
|
|
435
|
-
});
|
|
436
|
-
return result;
|
|
437
|
-
};
|
|
438
|
-
// If we don't have a destination yet, resolve after a short delay
|
|
439
|
-
// This handles the case where the stream is created but not yet piped
|
|
440
|
-
setTimeout(() => {
|
|
441
|
-
if (!resolved) {
|
|
442
|
-
resolved = true;
|
|
443
|
-
clearTimeout(timeout);
|
|
444
|
-
resolve();
|
|
445
|
-
}
|
|
446
|
-
}, 100);
|
|
447
|
-
});
|
|
448
|
-
// Check for HTML stream errors after waiting for completion
|
|
449
|
-
if (htmlStreamErrored) {
|
|
450
|
-
yield {
|
|
451
|
-
type: "error",
|
|
452
|
-
error: htmlStreamError || new Error("HTML stream failed"),
|
|
453
|
-
metrics: {
|
|
454
|
-
rscFull: rscFullMetrics,
|
|
455
|
-
rscHeadless: rscHeadlessMetrics,
|
|
456
|
-
html: htmlMetrics,
|
|
457
|
-
},
|
|
458
|
-
};
|
|
459
|
-
return;
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
let fullPanicError = null;
|
|
253
|
+
const fullHandlers = createMainThreadHandlers(
|
|
254
|
+
handlerOptions,
|
|
255
|
+
(error, isPanic) => {
|
|
256
|
+
if (isPanic) {
|
|
257
|
+
fullPanicError = error instanceof Error ? error : new Error("Full RSC stream failed");
|
|
460
258
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
fullHandlers.onData = (_id, chunk) => {
|
|
262
|
+
rscFullMetrics.chunks++;
|
|
263
|
+
rscFullMetrics.streamMetrics.bytes += chunk.length;
|
|
264
|
+
};
|
|
265
|
+
const hasExistingHeadlessError = hasHeadlessStreamError(headlessStreamState, handlerOptions.route);
|
|
266
|
+
const shouldUseFallback = headlessStreamErrored || hasExistingHeadlessError;
|
|
267
|
+
if (handlerOptions.verbose) {
|
|
268
|
+
handlerOptions.logger?.info(
|
|
269
|
+
`[renderPage.server] Creating full RSC handler options for route ${handlerOptions.route}: headlessStreamErrored=${headlessStreamErrored}, hasExistingHeadlessError=${hasExistingHeadlessError}, shouldUseFallback=${shouldUseFallback}`
|
|
270
|
+
);
|
|
472
271
|
}
|
|
473
|
-
|
|
272
|
+
const SafePageComponent = (props) => {
|
|
273
|
+
const hasError = hasHeadlessStreamError(headlessStreamState, handlerOptions.route);
|
|
274
|
+
if (hasError) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
return newHandlerOptions.PageComponent(props);
|
|
278
|
+
};
|
|
279
|
+
const fullRscHandlerOptions = {
|
|
280
|
+
...newHandlerOptions,
|
|
281
|
+
htmlPath: void 0,
|
|
282
|
+
// Full RSC - include HTML wrapper
|
|
283
|
+
headlessStreamElements: headlessStreamState.elements,
|
|
284
|
+
// Pass the storage map for reuse
|
|
285
|
+
// Use SafePageComponent that returns null when there are headless stream errors
|
|
286
|
+
PageComponent: SafePageComponent
|
|
287
|
+
};
|
|
288
|
+
headlessRscHandler.rscStream.on("end", () => {
|
|
289
|
+
if (!hasHeadlessStreamError(headlessStreamState, handlerOptions.route)) {
|
|
474
290
|
if (handlerOptions.verbose) {
|
|
475
|
-
|
|
291
|
+
handlerOptions.logger?.info(
|
|
292
|
+
`[renderPage.server] Headless stream completed successfully for route ${handlerOptions.route}`
|
|
293
|
+
);
|
|
476
294
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
if (handlerOptions.verbose) {
|
|
298
|
+
handlerOptions.logger?.info(
|
|
299
|
+
`[renderPage.server] Created PageComponent that uses React.use() to consume headless stream for route ${handlerOptions.route}`
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
fullRscHandler = renderRscStream(fullRscHandlerOptions, fullHandlers);
|
|
303
|
+
if (fullPanicError) {
|
|
304
|
+
yield {
|
|
305
|
+
type: "error",
|
|
306
|
+
error: fullPanicError,
|
|
307
|
+
metrics: {
|
|
308
|
+
rscFull: rscFullMetrics,
|
|
309
|
+
rscHeadless: rscHeadlessMetrics,
|
|
310
|
+
html: htmlMetrics
|
|
485
311
|
}
|
|
486
|
-
|
|
487
|
-
|
|
312
|
+
};
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
htmlTransformStream = createRscToHtmlStream({
|
|
316
|
+
id: handlerOptions.id,
|
|
317
|
+
worker: handlerOptions.worker,
|
|
318
|
+
route: handlerOptions.route,
|
|
319
|
+
url: handlerOptions.url,
|
|
320
|
+
moduleRootPath: handlerOptions.moduleRootPath,
|
|
321
|
+
moduleBasePath: handlerOptions.moduleBasePath,
|
|
322
|
+
moduleBaseURL: handlerOptions.moduleBaseURL,
|
|
323
|
+
projectRoot: handlerOptions.projectRoot,
|
|
324
|
+
build: handlerOptions.build,
|
|
325
|
+
panicThreshold: handlerOptions.panicThreshold,
|
|
326
|
+
verbose: handlerOptions.verbose,
|
|
327
|
+
signal: handlerOptions.signal,
|
|
328
|
+
logger: handlerOptions.logger,
|
|
329
|
+
htmlWorker: handlerOptions.htmlWorker,
|
|
330
|
+
clientPipeableStreamOptions: handlerOptions.clientPipeableStreamOptions,
|
|
331
|
+
onMetrics: handlerOptions.onMetrics,
|
|
332
|
+
htmlTimeout: handlerOptions.htmlTimeout || 15e3,
|
|
333
|
+
rscStream: fullRscHandler.rscStream,
|
|
334
|
+
onError: (error, isPanic) => {
|
|
335
|
+
htmlStreamErrored = true;
|
|
336
|
+
htmlStreamError = error;
|
|
337
|
+
if (isPanic) {
|
|
338
|
+
if (handlerOptions.verbose) {
|
|
339
|
+
handlerOptions.logger?.error(
|
|
340
|
+
`[renderPage.server] HTML stream panic error for route ${handlerOptions.route}: ${error.message}`
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
if (handlerOptions.verbose) {
|
|
345
|
+
handlerOptions.logger?.warn(
|
|
346
|
+
`[renderPage.server] HTML stream error for route ${handlerOptions.route}: ${error.message}`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
const rscStreamWrapper = {
|
|
353
|
+
pipe: (destination) => {
|
|
354
|
+
const streamMetrics = createStreamMetrics();
|
|
355
|
+
streamMetrics.startTime = performance.now();
|
|
356
|
+
const rscFileStream = headlessRscHandler.rscStream;
|
|
357
|
+
rscFileStream.on("data", (chunk) => {
|
|
358
|
+
streamMetrics.chunks++;
|
|
359
|
+
streamMetrics.bytes += chunk.length;
|
|
360
|
+
});
|
|
361
|
+
rscFileStream.on("end", () => {
|
|
362
|
+
streamMetrics.duration = performance.now() - streamMetrics.startTime;
|
|
363
|
+
streamMetrics.endTime = performance.now();
|
|
364
|
+
rscHeadlessMetrics.streamMetrics = streamMetrics;
|
|
365
|
+
rscHeadlessMetrics.chunkRate = streamMetrics.chunks / (streamMetrics.duration / 1e3);
|
|
366
|
+
rscHeadlessMetrics.processingTime = streamMetrics.duration;
|
|
367
|
+
rscHeadlessMetrics.memoryUsage = process.memoryUsage();
|
|
368
|
+
rscHeadlessMetrics.chunks = streamMetrics.chunks;
|
|
369
|
+
});
|
|
370
|
+
rscFileStream.pipe(destination);
|
|
371
|
+
return destination;
|
|
372
|
+
},
|
|
373
|
+
abort: () => headlessRscHandler.abort()
|
|
374
|
+
};
|
|
375
|
+
const htmlStreamWrapper = {
|
|
376
|
+
pipe: (destination) => {
|
|
377
|
+
return htmlTransformStream.pipe(destination);
|
|
378
|
+
},
|
|
379
|
+
abort: () => {
|
|
380
|
+
htmlTransformStream.abort();
|
|
381
|
+
},
|
|
382
|
+
on: (event, listener) => {
|
|
383
|
+
if (event === "error") {
|
|
384
|
+
const htmlStream = htmlTransformStream.htmlStream;
|
|
385
|
+
if (htmlStream && typeof htmlStream.on === "function") {
|
|
386
|
+
htmlStream.on("error", listener);
|
|
387
|
+
}
|
|
488
388
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
389
|
+
return htmlStreamWrapper;
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
await new Promise((resolve, reject) => {
|
|
393
|
+
const timeout = setTimeout(() => {
|
|
394
|
+
reject(new Error(`HTML stream timeout for route ${handlerOptions.route}`));
|
|
395
|
+
}, handlerOptions.htmlTimeout || 15e3);
|
|
396
|
+
if (htmlStreamErrored) {
|
|
397
|
+
clearTimeout(timeout);
|
|
398
|
+
resolve();
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
let resolved = false;
|
|
402
|
+
const originalPipe = htmlTransformStream.pipe;
|
|
403
|
+
htmlTransformStream.pipe = function(destination) {
|
|
404
|
+
const result = originalPipe.call(this, destination);
|
|
405
|
+
destination.on("finish", () => {
|
|
406
|
+
if (!resolved) {
|
|
407
|
+
resolved = true;
|
|
408
|
+
clearTimeout(timeout);
|
|
409
|
+
resolve();
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
destination.on("error", (error) => {
|
|
413
|
+
if (!resolved) {
|
|
414
|
+
resolved = true;
|
|
415
|
+
clearTimeout(timeout);
|
|
416
|
+
reject(error);
|
|
417
|
+
}
|
|
495
418
|
});
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
419
|
+
return result;
|
|
420
|
+
};
|
|
421
|
+
setTimeout(() => {
|
|
422
|
+
if (!resolved) {
|
|
423
|
+
resolved = true;
|
|
424
|
+
clearTimeout(timeout);
|
|
425
|
+
resolve();
|
|
426
|
+
}
|
|
427
|
+
}, 100);
|
|
428
|
+
});
|
|
429
|
+
if (htmlStreamErrored) {
|
|
430
|
+
yield {
|
|
431
|
+
type: "error",
|
|
432
|
+
error: htmlStreamError || new Error("HTML stream failed"),
|
|
433
|
+
metrics: {
|
|
434
|
+
rscFull: rscFullMetrics,
|
|
435
|
+
rscHeadless: rscHeadlessMetrics,
|
|
436
|
+
html: htmlMetrics
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
yield {
|
|
442
|
+
type: "success",
|
|
443
|
+
html: htmlStreamWrapper,
|
|
444
|
+
rsc: rscStreamWrapper,
|
|
445
|
+
metrics: {
|
|
446
|
+
rscFull: rscFullMetrics,
|
|
447
|
+
rscHeadless: rscHeadlessMetrics,
|
|
448
|
+
html: htmlMetrics
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
} catch (err) {
|
|
452
|
+
if (handlerOptions.verbose) {
|
|
453
|
+
handlerOptions.logger?.error(`[renderPage.server] Error: ${JSON.stringify(err)}`);
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
if (headlessRscHandler) headlessRscHandler.abort();
|
|
457
|
+
if (fullRscHandler) fullRscHandler.abort();
|
|
458
|
+
if (htmlTransformStream) htmlTransformStream.abort();
|
|
459
|
+
} catch (cleanupError) {
|
|
460
|
+
handlerOptions.logger?.warn(`Failed to cleanup streams on error: ${cleanupError}`);
|
|
461
|
+
}
|
|
462
|
+
const panicError = handleError({
|
|
463
|
+
error: err,
|
|
464
|
+
critical: false,
|
|
465
|
+
logger: handlerOptions.logger,
|
|
466
|
+
panicThreshold: handlerOptions.panicThreshold,
|
|
467
|
+
context: `RenderPage Error (${handlerOptions.route})`
|
|
468
|
+
});
|
|
469
|
+
if (panicError != null) {
|
|
470
|
+
yield {
|
|
471
|
+
type: "error",
|
|
472
|
+
error: panicError,
|
|
473
|
+
metrics: {
|
|
474
|
+
rscFull: rscFullMetrics,
|
|
475
|
+
rscHeadless: rscHeadlessMetrics,
|
|
476
|
+
html: htmlMetrics
|
|
506
477
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
478
|
+
};
|
|
479
|
+
} else {
|
|
480
|
+
yield {
|
|
481
|
+
type: "skip",
|
|
482
|
+
reason: err,
|
|
483
|
+
html: {
|
|
484
|
+
pipe: (destination) => {
|
|
485
|
+
destination.end();
|
|
486
|
+
return destination;
|
|
487
|
+
},
|
|
488
|
+
abort: () => {
|
|
489
|
+
}
|
|
490
|
+
},
|
|
491
|
+
rsc: {
|
|
492
|
+
pipe: (destination) => {
|
|
493
|
+
destination.end();
|
|
494
|
+
return destination;
|
|
495
|
+
},
|
|
496
|
+
abort: () => {
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
metrics: {
|
|
500
|
+
rscFull: rscFullMetrics,
|
|
501
|
+
rscHeadless: rscHeadlessMetrics,
|
|
502
|
+
html: htmlMetrics
|
|
531
503
|
}
|
|
504
|
+
};
|
|
532
505
|
}
|
|
506
|
+
}
|
|
533
507
|
};
|
|
508
|
+
|
|
509
|
+
export { renderPage };
|
|
510
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVuZGVyUGFnZS5zZXJ2ZXIuanMiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3BsdWdpbi9yZWFjdC1zdGF0aWMvcmVuZGVyUGFnZS5zZXJ2ZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiByZW5kZXJQYWdlLnNlcnZlci50c1xuICpcbiAqIFBVUlBPU0U6IFNlcnZlci1zaWRlIHN0YXRpYyBwYWdlIHJlbmRlcmluZyBmb3IgUmVhY3QgU2VydmVyIENvbXBvbmVudHNcbiAqXG4gKiBBUkNISVRFQ1RVUkUgT1ZFUlZJRVc6XG4gKiBcbiAqIFNFUlZFUi1TSURFIHZzIENMSUVOVC1TSURFOlxuICogLSBTZXJ2ZXItc2lkZTogUlNDIGdlbmVyYXRpb24gaW4gbWFpbiB0aHJlYWQsIEhUTUwgZ2VuZXJhdGlvbiBpbiB3b3JrZXJcbiAqIC0gQ2xpZW50LXNpZGU6IFJTQyBnZW5lcmF0aW9uIGluIHdvcmtlciwgSFRNTCBnZW5lcmF0aW9uIGluIG1haW4gdGhyZWFkXG4gKiBcbiAqIEZMT1c6XG4gKiAxLiBDcmVhdGUgaGVhZGxlc3MgUlNDIHN0cmVhbSAoZm9yIC5yc2MgZmlsZSlcbiAqIDIuIENyZWF0ZSBmdWxsIFJTQyBzdHJlYW0gKGZvciBIVE1MIGdlbmVyYXRpb24pXG4gKiAzLiBDcmVhdGUgSFRNTCB0cmFuc2Zvcm0gc3RyZWFtIHRoYXQgY29udmVydHMgUlNDIHRvIEhUTUxcbiAqIDQuIEJvdGggc3RyZWFtcyBhcmUgcGlwZWQgdG8gZmlsZSB3cml0ZXJzXG4gKiBcbiAqIFNJTVBMSUZJRUQgQVBQUk9BQ0g6XG4gKiBUaGlzIGltcGxlbWVudGF0aW9uIGZvbGxvd3MgdGhlIHNhbWUgc2ltcGxlIHBhdHRlcm4gYXMgdGhlIGNsaWVudCBzaWRlLFxuICogYXZvaWRpbmcgY29tcGxleCBiYWNrcHJlc3N1cmUgaGFuZGxpbmcgYW5kIHJhY2UgY29uZGl0aW9ucy5cbiAqL1xuXG5pbXBvcnQgeyBjcmVhdGVSZW5kZXJNZXRyaWNzIH0gZnJvbSBcIi4uL21ldHJpY3MvY3JlYXRlUmVuZGVyTWV0cmljcy5qc1wiO1xuaW1wb3J0IHsgcm91dGVUb1VSTCB9IGZyb20gXCIuLi91dGlscy9yb3V0ZVRvVVJMLmpzXCI7XG5pbXBvcnQgdHlwZSB7IFJlbmRlclBhZ2VGbiB9IGZyb20gXCIuL3R5cGVzLmpzXCI7XG5pbXBvcnQgeyBoYW5kbGVFcnJvciB9IGZyb20gXCIuLi9lcnJvci9oYW5kbGVFcnJvci5qc1wiO1xuaW1wb3J0IHsgYXNzZXJ0UmVhY3RTZXJ2ZXIgfSBmcm9tIFwiLi4vY29uZmlnL2dldENvbmRpdGlvbi5qc1wiO1xuXG5pbXBvcnQgeyByZW5kZXJSc2NTdHJlYW0gfSBmcm9tIFwiLi4vc3RyZWFtL3JlbmRlclJzY1N0cmVhbS5zZXJ2ZXIuanNcIjtcbmltcG9ydCB7IGNyZWF0ZU1haW5UaHJlYWRIYW5kbGVycyB9IGZyb20gXCIuLi9zdHJlYW0vY3JlYXRlTWFpblRocmVhZEhhbmRsZXJzLmpzXCI7XG5pbXBvcnQgeyBjcmVhdGVSc2NUb0h0bWxTdHJlYW0gfSBmcm9tIFwiLi9yc2NUb0h0bWxTdHJlYW0uc2VydmVyLmpzXCI7XG5pbXBvcnQgeyByZXNvbHZlQ29tcG9uZW50IH0gZnJvbSBcIi4uL2hlbHBlcnMvcmVzb2x2ZUNvbXBvbmVudC5qc1wiO1xuaW1wb3J0IHsgcmVzb2x2ZVBhZ2VBbmRQcm9wcyB9IGZyb20gXCIuLi9oZWxwZXJzL3Jlc29sdmVQYWdlQW5kUHJvcHMuanNcIjtcbmltcG9ydCB7IFJvb3QgYXMgRGVmYXVsdFJvb3QgfSBmcm9tIFwiLi4vY29tcG9uZW50cy9yb290LmpzXCI7XG5pbXBvcnQgeyBIdG1sIGFzIERlZmF1bHRIdG1sIH0gZnJvbSBcIi4uL2NvbXBvbmVudHMvaHRtbC5qc1wiO1xuaW1wb3J0IHsgY3JlYXRlU3RyZWFtTWV0cmljcyB9IGZyb20gXCIuLi9tZXRyaWNzL2NyZWF0ZVN0cmVhbU1ldHJpY3MuanNcIjtcbmltcG9ydCB7IGpvaW4gfSBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgeyBjcmVhdGVIZWFkbGVzc1N0cmVhbVN0YXRlLCB0cmFja0hlYWRsZXNzU3RyZWFtRXJyb3IsIGhhc0hlYWRsZXNzU3RyZWFtRXJyb3IgfSBmcm9tIFwiLi4vaGVscGVycy9oZWFkbGVzc1N0cmVhbVN0YXRlLmpzXCI7XG5cbmV4cG9ydCBjb25zdCByZW5kZXJQYWdlOiBSZW5kZXJQYWdlRm4gPSBhc3luYyBmdW5jdGlvbiogcmVuZGVyUGFnZShcbiAgaGFuZGxlck9wdGlvbnNcbikge1xuICAvLyBFbnN1cmUgd2UncmUgaW4gdGhlIGNvcnJlY3QgZW52aXJvbm1lbnRcbiAgYXNzZXJ0UmVhY3RTZXJ2ZXIoKTtcblxuICAvLyBDcmVhdGUgbWV0cmljcyB1cGZyb250IHdpdGggcHJvcGVyIHR5cGVzXG4gIGNvbnN0IGJhc2VEaXIgPSBqb2luKFxuICAgIGhhbmRsZXJPcHRpb25zLmJ1aWxkLm91dERpcixcbiAgICBoYW5kbGVyT3B0aW9ucy5idWlsZC5zdGF0aWNcbiAgKTtcbiAgY29uc3Qgcm91dGVQYXRoID0gaGFuZGxlck9wdGlvbnMucm91dGUucmVwbGFjZSgvXlxcLy8sIFwiXCIpO1xuXG4gIGNvbnN0IGh0bWxNZXRyaWNzID0gY3JlYXRlUmVuZGVyTWV0cmljcyh7XG4gICAgcm91dGU6IGhhbmRsZXJPcHRpb25zLnJvdXRlLFxuICAgIHR5cGU6IFwiaHRtbFwiLFxuICAgIGZyb21NYWluVGhyZWFkOiBmYWxzZSwgLy8gU2VydmVyOiBIVE1MIHJlbmRlcmVkIGluIHdvcmtlclxuICAgIGZyb21Sc2NXb3JrZXI6IGZhbHNlLFxuICAgIGZyb21IdG1sV29ya2VyOiB0cnVlLFxuICAgIGJhc2VEaXIsXG4gICAgcm91dGVQYXRoLFxuICAgIGZpbGVOYW1lOiBoYW5kbGVyT3B0aW9ucy5idWlsZC5odG1sT3V0cHV0UGF0aCxcbiAgICBvdXRwdXRQYXRoOiBqb2luKGJhc2VEaXIsIHJvdXRlUGF0aCwgaGFuZGxlck9wdGlvbnMuYnVpbGQuaHRtbE91dHB1dFBhdGgpLFxuICB9KTtcbiAgXG4gIGNvbnN0IHJzY0Z1bGxNZXRyaWNzID0gY3JlYXRlUmVuZGVyTWV0cmljcyh7XG4gICAgcm91dGU6IGhhbmRsZXJPcHRpb25zLnJvdXRlLFxuICAgIHR5cGU6IFwicnNjLWZ1bGxcIixcbiAgICBmcm9tTWFpblRocmVhZDogdHJ1ZSwgLy8gU2VydmVyOiBSU0MgcmVuZGVyZWQgb24gbWFpbiB0aHJlYWRcbiAgICBmcm9tUnNjV29ya2VyOiBmYWxzZSxcbiAgICBmcm9tSHRtbFdvcmtlcjogZmFsc2UsXG4gIH0pO1xuICBcbiAgY29uc3QgcnNjSGVhZGxlc3NNZXRyaWNzID0gY3JlYXRlUmVuZGVyTWV0cmljcyh7XG4gICAgcm91dGU6IGhhbmRsZXJPcHRpb25zLnJvdXRlLFxuICAgIHR5cGU6IFwicnNjLWhlYWRsZXNzXCIsXG4gICAgZnJvbU1haW5UaHJlYWQ6IHRydWUsIC8vIFNlcnZlcjogUlNDIHJlbmRlcmVkIG9uIG1haW4gdGhyZWFkXG4gICAgZnJvbVJzY1dvcmtlcjogZmFsc2UsXG4gICAgZnJvbUh0bWxXb3JrZXI6IGZhbHNlLFxuICAgIGJhc2VEaXIsXG4gICAgcm91dGVQYXRoLFxuICAgIGZpbGVOYW1lOiBoYW5kbGVyT3B0aW9ucy5idWlsZC5yc2NPdXRwdXRQYXRoLFxuICAgIG91dHB1dFBhdGg6IGpvaW4oYmFzZURpciwgcm91dGVQYXRoLCBoYW5kbGVyT3B0aW9ucy5idWlsZC5yc2NPdXRwdXRQYXRoKSxcbiAgfSk7XG5cbiAgLy8gRGVjbGFyZSB2YXJpYWJsZXMgb3V0c2lkZSB0cnkgYmxvY2tcbiAgbGV0IGhlYWRsZXNzUnNjSGFuZGxlcjogYW55ID0gbnVsbDtcbiAgbGV0IGZ1bGxSc2NIYW5kbGVyOiBhbnkgPSBudWxsO1xuICBsZXQgaHRtbFRyYW5zZm9ybVN0cmVhbTogYW55ID0gbnVsbDtcbiAgXG4gIC8vIEVycm9yIHRyYWNraW5nIHZhcmlhYmxlcyBmb3IgaGVhZGxlc3Mgc3RyZWFtXG4gIGxldCBoZWFkbGVzc1N0cmVhbUVycm9yZWQgPSBmYWxzZTtcbiAgbGV0IGhlYWRsZXNzRXJyb3I6IEVycm9yIHwgbnVsbCA9IG51bGw7XG4gIFxuICAvLyBFcnJvciB0cmFja2luZyB2YXJpYWJsZXMgZm9yIEhUTUwgc3RyZWFtXG4gIGxldCBodG1sU3RyZWFtRXJyb3JlZCA9IGZhbHNlO1xuICBsZXQgaHRtbFN0cmVhbUVycm9yOiBFcnJvciB8IG51bGwgPSBudWxsO1xuXG4gIC8vIFNlcnZlci1zaWRlIHN0cmVhbSByZXVzZSBzdG9yYWdlIChzaW1pbGFyIHRvIGNsaWVudC1zaWRlIGhlYWRsZXNzU3RyZWFtRWxlbWVudHMpXG4gIGNvbnN0IGhlYWRsZXNzU3RyZWFtU3RhdGUgPSBjcmVhdGVIZWFkbGVzc1N0cmVhbVN0YXRlKCk7XG5cbiAgdHJ5IHtcbiAgICBpZiAoaGFuZGxlck9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgaGFuZGxlck9wdGlvbnMubG9nZ2VyPy5pbmZvKFxuICAgICAgICBgW3JlbmRlclBhZ2Uuc2VydmVyXSBTZXJ2ZXItc2lkZSByZW5kZXJpbmcgZm9yIHJvdXRlOiAke2hhbmRsZXJPcHRpb25zLnJvdXRlfWBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gU2V0IFVSTCBpZiBub3QgcHJvdmlkZWRcbiAgICBpZiAoIWhhbmRsZXJPcHRpb25zLnVybCkge1xuICAgICAgaGFuZGxlck9wdGlvbnMudXJsID0gcm91dGVUb1VSTChcbiAgICAgICAgaGFuZGxlck9wdGlvbnMucm91dGUsXG4gICAgICAgIGhhbmRsZXJPcHRpb25zLm1vZHVsZUJhc2VVUkwsXG4gICAgICAgIGhhbmRsZXJPcHRpb25zLmJ1aWxkLnJzY091dHB1dFBhdGhcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gUmVzb2x2ZSBjb21wb25lbnRzIGFuZCBwcm9wcyB1c2luZyB0aGUgcHJvcGVyIGhlbHBlclxuICAgIGxldCBQYWdlQ29tcG9uZW50OiBhbnkgPSBudWxsO1xuICAgIGxldCBSb290Q29tcG9uZW50OiBhbnkgPSBudWxsO1xuICAgIGxldCBIdG1sQ29tcG9uZW50OiBhbnkgPSBudWxsO1xuICAgIGxldCBwYWdlUHJvcHM6IGFueSA9IHt9OyAvLyBJbml0aWFsaXplIGFzIGVtcHR5IG9iamVjdCAtIHByb3BzIGZ1bmN0aW9uIHdpbGwgcG9wdWxhdGUgaXRcblxuICAgIC8vIFVzZSByZXNvbHZlUGFnZUFuZFByb3BzIGhlbHBlciB0byBwcm9wZXJseSBsb2FkIHBhZ2UgYW5kIHByb3BzXG4gICAgaWYgKGhhbmRsZXJPcHRpb25zLnBhZ2VQYXRoKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBwYWdlQW5kUHJvcHNSZXN1bHQgPSBhd2FpdCByZXNvbHZlUGFnZUFuZFByb3BzKHtcbiAgICAgICAgICBwYWdlUGF0aDogaGFuZGxlck9wdGlvbnMucGFnZVBhdGgsXG4gICAgICAgICAgcGFnZUV4cG9ydE5hbWU6IGhhbmRsZXJPcHRpb25zLnBhZ2VFeHBvcnROYW1lLFxuICAgICAgICAgIHByb3BzUGF0aDogaGFuZGxlck9wdGlvbnMucHJvcHNQYXRoLFxuICAgICAgICAgIHByb3BzRXhwb3J0TmFtZTogaGFuZGxlck9wdGlvbnMucHJvcHNFeHBvcnROYW1lLFxuICAgICAgICAgIGxvYWRlcjogaGFuZGxlck9wdGlvbnMubG9hZGVyLFxuICAgICAgICAgIHZlcmJvc2U6IGhhbmRsZXJPcHRpb25zLnZlcmJvc2UsXG4gICAgICAgICAgbG9nZ2VyOiBoYW5kbGVyT3B0aW9ucy5sb2dnZXIsXG4gICAgICAgICAgcm91dGU6IGhhbmRsZXJPcHRpb25zLnJvdXRlLFxuICAgICAgICAgIHVybDogaGFuZGxlck9wdGlvbnMudXJsLFxuICAgICAgICAgIG1vZHVsZUJhc2VVUkw6IGhhbmRsZXJPcHRpb25zLm1vZHVsZUJhc2VVUkwsXG4gICAgICAgICAgYnVpbGQ6IHtcbiAgICAgICAgICAgIHJzY091dHB1dFBhdGg6IGhhbmRsZXJPcHRpb25zLmJ1aWxkLnJzY091dHB1dFBhdGgsXG4gICAgICAgICAgfSxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgaWYgKHBhZ2VBbmRQcm9wc1Jlc3VsdC50eXBlID09PSBcInN1Y2Nlc3NcIikge1xuICAgICAgICAgIFBhZ2VDb21wb25lbnQgPSBwYWdlQW5kUHJvcHNSZXN1bHQuUGFnZUNvbXBvbmVudDtcbiAgICAgICAgICAvLyBBbHdheXMgdXNlIHRoZSBwcm9wcyByZXR1cm5lZCBmcm9tIHRoZSBwcm9wcyBmdW5jdGlvblxuICAgICAgICAgIC8vIFJvb3QgY29tcG9uZW50cyBjYW4gaGFuZGxlIGVtcHR5IHByb3BzIHdpdGggdGhlaXIgZGVmYXVsdHNcbiAgICAgICAgICBwYWdlUHJvcHMgPSBwYWdlQW5kUHJvcHNSZXN1bHQucGFnZVByb3BzIHx8IHt9O1xuICAgICAgICAgIFxuICAgICAgICAgIGlmIChoYW5kbGVyT3B0aW9ucy52ZXJib3NlKSB7XG4gICAgICAgICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICAgICAgICAgIGBbcmVuZGVyUGFnZS5zZXJ2ZXJdIFN1Y2Nlc3NmdWxseSBsb2FkZWQgcGFnZSBhbmQgcHJvcHMgZm9yIHJvdXRlICR7aGFuZGxlck9wdGlvbnMucm91dGV9OiBwYWdlUHJvcHM9JHtKU09OLnN0cmluZ2lmeShwYWdlUHJvcHMpfWBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGhhbmRsZXJPcHRpb25zLmxvZ2dlcj8ud2FybihcbiAgICAgICAgICAgIGBGYWlsZWQgdG8gbG9hZCBwYWdlIGFuZCBwcm9wcyBmcm9tICR7aGFuZGxlck9wdGlvbnMucGFnZVBhdGh9OiAke1xuICAgICAgICAgICAgICBwYWdlQW5kUHJvcHNSZXN1bHQuZXJyb3I/Lm1lc3NhZ2UgfHwgXCJVbmtub3duIGVycm9yXCJcbiAgICAgICAgICAgIH1gXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgaGFuZGxlck9wdGlvbnMubG9nZ2VyPy53YXJuKFxuICAgICAgICAgIGBFcnJvciBsb2FkaW5nIHBhZ2UgYW5kIHByb3BzIGZyb20gJHtoYW5kbGVyT3B0aW9ucy5wYWdlUGF0aH06ICR7XG4gICAgICAgICAgICBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcilcbiAgICAgICAgICB9YFxuICAgICAgICApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIExvYWQgUm9vdCBjb21wb25lbnRcbiAgICBpZiAoaGFuZGxlck9wdGlvbnMucm9vdFBhdGgpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJvb3RSZXN1bHQgPSBhd2FpdCByZXNvbHZlQ29tcG9uZW50KHtcbiAgICAgICAgICBjb21wb25lbnRQYXRoOiBoYW5kbGVyT3B0aW9ucy5yb290UGF0aCxcbiAgICAgICAgICBleHBvcnROYW1lOiBoYW5kbGVyT3B0aW9ucy5yb290RXhwb3J0TmFtZSxcbiAgICAgICAgICBsb2FkZXI6IGhhbmRsZXJPcHRpb25zLmxvYWRlcixcbiAgICAgICAgfSk7XG4gICAgICAgIGlmIChyb290UmVzdWx0LnR5cGUgPT09IFwic3VjY2Vzc1wiKSB7XG4gICAgICAgICAgUm9vdENvbXBvbmVudCA9IHJvb3RSZXN1bHQuY29tcG9uZW50O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGhhbmRsZXJPcHRpb25zLmxvZ2dlcj8ud2FybihcbiAgICAgICAgICAgIGBGYWlsZWQgdG8gbG9hZCBSb290IGNvbXBvbmVudCBmcm9tICR7aGFuZGxlck9wdGlvbnMucm9vdFBhdGh9OiAke1xuICAgICAgICAgICAgICByb290UmVzdWx0LmVycm9yPy5tZXNzYWdlIHx8IFwiVW5rbm93biBlcnJvclwiXG4gICAgICAgICAgICB9YFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGhhbmRsZXJPcHRpb25zLmxvZ2dlcj8ud2FybihcbiAgICAgICAgICBgRXJyb3IgbG9hZGluZyBSb290IGNvbXBvbmVudCBmcm9tICR7aGFuZGxlck9wdGlvbnMucm9vdFBhdGh9OiAke1xuICAgICAgICAgICAgZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpXG4gICAgICAgICAgfWBcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBMb2FkIEh0bWwgY29tcG9uZW50XG4gICAgaWYgKGhhbmRsZXJPcHRpb25zLmh0bWxQYXRoKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBodG1sUmVzdWx0ID0gYXdhaXQgcmVzb2x2ZUNvbXBvbmVudCh7XG4gICAgICAgICAgY29tcG9uZW50UGF0aDogaGFuZGxlck9wdGlvbnMuaHRtbFBhdGgsXG4gICAgICAgICAgZXhwb3J0TmFtZTogaGFuZGxlck9wdGlvbnMuaHRtbEV4cG9ydE5hbWUsXG4gICAgICAgICAgbG9hZGVyOiBoYW5kbGVyT3B0aW9ucy5sb2FkZXIsXG4gICAgICAgIH0pO1xuICAgICAgICBpZiAoaHRtbFJlc3VsdC50eXBlID09PSBcInN1Y2Nlc3NcIikge1xuICAgICAgICAgIEh0bWxDb21wb25lbnQgPSBodG1sUmVzdWx0LmNvbXBvbmVudDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/Lndhcm4oXG4gICAgICAgICAgICBgRmFpbGVkIHRvIGxvYWQgSHRtbCBjb21wb25lbnQgZnJvbSAke2hhbmRsZXJPcHRpb25zLmh0bWxQYXRofTogJHtcbiAgICAgICAgICAgICAgaHRtbFJlc3VsdC5lcnJvcj8ubWVzc2FnZSB8fCBcIlVua25vd24gZXJyb3JcIlxuICAgICAgICAgICAgfWBcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/Lndhcm4oXG4gICAgICAgICAgYEVycm9yIGxvYWRpbmcgSHRtbCBjb21wb25lbnQgZnJvbSAke2hhbmRsZXJPcHRpb25zLmh0bWxQYXRofTogJHtcbiAgICAgICAgICAgIGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKVxuICAgICAgICAgIH1gXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gVXNlIGRlZmF1bHRzIGlmIGNvbXBvbmVudHMgYXJlIHN0aWxsIG5vdCBsb2FkZWRcbiAgICBpZiAoIVJvb3RDb21wb25lbnQpIHtcbiAgICAgIFJvb3RDb21wb25lbnQgPSBEZWZhdWx0Um9vdCBhcyBhbnk7XG4gICAgfVxuICAgIGlmICghSHRtbENvbXBvbmVudCkge1xuICAgICAgSHRtbENvbXBvbmVudCA9IERlZmF1bHRIdG1sIGFzIGFueTtcbiAgICB9XG5cbiAgICAvLyBFbnN1cmUgd2UgaGF2ZSBhbGwgcmVxdWlyZWQgY29tcG9uZW50c1xuICAgIGlmICghUGFnZUNvbXBvbmVudCB8fCAhUm9vdENvbXBvbmVudCB8fCAhSHRtbENvbXBvbmVudCkge1xuICAgICAgeWllbGQge1xuICAgICAgICB0eXBlOiBcImVycm9yXCIsXG4gICAgICAgIGVycm9yOiBuZXcgRXJyb3IoXG4gICAgICAgICAgYENvbXBvbmVudCByZXNvbHV0aW9uIGZhaWxlZDogbWlzc2luZyByZXF1aXJlZCBjb21wb25lbnRzIChQYWdlOiAkeyEhUGFnZUNvbXBvbmVudH0sIFJvb3Q6ICR7ISFSb290Q29tcG9uZW50fSwgSHRtbDogJHshIUh0bWxDb21wb25lbnR9KWBcbiAgICAgICAgKSxcbiAgICAgICAgbWV0cmljczoge1xuICAgICAgICAgIHJzY0Z1bGw6IHJzY0Z1bGxNZXRyaWNzLFxuICAgICAgICAgIHJzY0hlYWRsZXNzOiByc2NIZWFkbGVzc01ldHJpY3MsXG4gICAgICAgICAgaHRtbDogaHRtbE1ldHJpY3MsXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIENyZWF0ZSBoYW5kbGVyIG9wdGlvbnMgd2l0aCByZXNvbHZlZCBjb21wb25lbnRzIGFuZCBwcm9wc1xuICAgIGNvbnN0IHVuaXF1ZUlkID0gaGFuZGxlck9wdGlvbnMuaWQgPz8gYCR7aGFuZGxlck9wdGlvbnMucm91dGV9P2lkPSR7RGF0ZS5ub3coKX0tJHtNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHJpbmcoMiwgMTEpfWA7XG4gICAgY29uc3QgbmV3SGFuZGxlck9wdGlvbnMgPSB7XG4gICAgICAuLi5oYW5kbGVyT3B0aW9ucyxcbiAgICAgIGlkOiB1bmlxdWVJZCxcbiAgICAgIHVybDogYCR7aGFuZGxlck9wdGlvbnMudXJsfWAsXG4gICAgICByb3V0ZTogYCR7aGFuZGxlck9wdGlvbnMucm91dGV9YCxcbiAgICAgIFBhZ2VDb21wb25lbnQsXG4gICAgICBSb290Q29tcG9uZW50LFxuICAgICAgSHRtbENvbXBvbmVudCxcbiAgICAgIHBhZ2VQcm9wcyxcbiAgICB9O1xuICAgIFxuICAgIGlmIChoYW5kbGVyT3B0aW9ucy52ZXJib3NlKSB7XG4gICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICAgIGBbcmVuZGVyUGFnZS5zZXJ2ZXJdIENyZWF0ZWQgbmV3SGFuZGxlck9wdGlvbnMgZm9yIHJvdXRlICR7aGFuZGxlck9wdGlvbnMucm91dGV9IHdpdGggcGFnZVByb3BzOiAke0pTT04uc3RyaW5naWZ5KHBhZ2VQcm9wcyl9YFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBDcmVhdGUgaGVhZGxlc3MgUlNDIGhhbmRsZXIgKGZvciAucnNjIGZpbGUpIC0gd2l0aCBwcm9wZXIgZXJyb3IgaGFuZGxpbmdcbiAgICBjb25zdCBoZWFkbGVzc0hhbmRsZXJzID0gY3JlYXRlTWFpblRocmVhZEhhbmRsZXJzKFxuICAgICAgaGFuZGxlck9wdGlvbnMsXG4gICAgICAoZXJyb3IsIGlzUGFuaWMpID0+IHtcbiAgICAgICAgaWYgKGhhbmRsZXJPcHRpb25zLnZlcmJvc2UpIHtcbiAgICAgICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICAgICAgICBgW3JlbmRlclBhZ2Uuc2VydmVyXSBIZWFkbGVzcyBzdHJlYW0gZXJyb3IgaGFuZGxlciBjYWxsZWQgZm9yIHJvdXRlICR7aGFuZGxlck9wdGlvbnMucm91dGV9OiAke2Vycm9yLm1lc3NhZ2V9LCBpc1BhbmljOiAke2lzUGFuaWN9YFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIC8vIFRyYWNrIGlmIHRoZSBoZWFkbGVzcyBzdHJlYW0gaGFkIGVycm9yc1xuICAgICAgICBoZWFkbGVzc1N0cmVhbUVycm9yZWQgPSB0cnVlO1xuICAgICAgICBoZWFkbGVzc0Vycm9yID0gZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yIDogbmV3IEVycm9yKFwiSGVhZGxlc3MgUlNDIHN0cmVhbSBmYWlsZWRcIik7XG4gICAgICAgIFxuICAgICAgICAvLyBUcmFjayBoZWFkbGVzcyBzdHJlYW0gZXJyb3JzIGZvciBjb25kaXRpb25hbCByZXVzZSBsb2dpYyAobGlrZSBSU0Mgd29ya2VyKVxuICAgICAgICB0cmFja0hlYWRsZXNzU3RyZWFtRXJyb3IoaGVhZGxlc3NTdHJlYW1TdGF0ZSwgaGFuZGxlck9wdGlvbnMucm91dGUsIGhlYWRsZXNzRXJyb3IpO1xuICAgICAgICBcbiAgICAgICAgaWYgKGhhbmRsZXJPcHRpb25zLnZlcmJvc2UpIHtcbiAgICAgICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICAgICAgICBgW3JlbmRlclBhZ2Uuc2VydmVyXSBTdG9yZWQgaGVhZGxlc3Mgc3RyZWFtIGVycm9yIGZvciByb3V0ZSAke2hhbmRsZXJPcHRpb25zLnJvdXRlfSBpbiBoZWFkbGVzc1N0cmVhbUVycm9ycyBtYXBgXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgLy8gU3RvcmUgcGFuaWMgZXJyb3JzIGZvciBsYXRlciBoYW5kbGluZ1xuICAgICAgICBpZiAoaXNQYW5pYykge1xuICAgICAgICAgIC8vIEZvciBwYW5pYyB0aHJlc2hvbGQgXCJhbGxfZXJyb3JzXCIsIHRoZSBwYW5pYyBlcnJvciB3aWxsIGJlIGhhbmRsZWQgYnkgcmVuZGVyUGFnZXNcbiAgICAgICAgICBpZiAoaGFuZGxlck9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgICAgICAgaGFuZGxlck9wdGlvbnMubG9nZ2VyPy5pbmZvKFxuICAgICAgICAgICAgICBgW3JlbmRlclBhZ2Uuc2VydmVyXSBQYW5pYyBlcnJvciBkZXRlY3RlZCBmb3Igcm91dGUgJHtoYW5kbGVyT3B0aW9ucy5yb3V0ZX0sIHdpbGwgYmUgaGFuZGxlZCBieSByZW5kZXJQYWdlc2BcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgKTtcbiAgICBcbiAgICAvLyBPdmVycmlkZSBvbkRhdGEgdG8gdHJhY2sgbWV0cmljc1xuICAgIGhlYWRsZXNzSGFuZGxlcnMub25EYXRhID0gKF9pZCwgY2h1bmspID0+IHtcbiAgICAgIHJzY0hlYWRsZXNzTWV0cmljcy5jaHVua3MrKztcbiAgICAgIHJzY0hlYWRsZXNzTWV0cmljcy5zdHJlYW1NZXRyaWNzLmJ5dGVzICs9IGNodW5rLmxlbmd0aDtcbiAgICB9O1xuICAgIFxuICAgIGhlYWRsZXNzUnNjSGFuZGxlciA9IHJlbmRlclJzY1N0cmVhbShcbiAgICAgIHtcbiAgICAgICAgLi4ubmV3SGFuZGxlck9wdGlvbnMsXG4gICAgICAgIGh0bWxQYXRoOiAnJywgLy8gSGVhZGxlc3MgUlNDIC0gbm8gSFRNTCB3cmFwcGVyXG4gICAgICAgIC8vIElmIHdlIGV4cGVjdCBlcnJvcnMsIHByb3ZpZGUgYSBzYWZlIFBhZ2UgY29tcG9uZW50IHRoYXQgZG9lc24ndCB0aHJvd1xuICAgICAgICBQYWdlQ29tcG9uZW50OiBuZXdIYW5kbGVyT3B0aW9ucy5QYWdlQ29tcG9uZW50LCAvLyBVc2Ugb3JpZ2luYWwgZm9yIG5vdywgd2lsbCBiZSBvdmVycmlkZGVuIGlmIGVycm9ycyBvY2N1clxuICAgICAgfSxcbiAgICAgIGhlYWRsZXNzSGFuZGxlcnNcbiAgICApO1xuICAgIFxuICAgIC8vIE5vdGU6IFBhbmljIGVycm9ycyB3aWxsIGJlIHlpZWxkZWQgZnJvbSB0aGUgZXJyb3IgaGFuZGxlciB3aGVuIHRoZXkgb2NjdXJcbiAgICAvLyBObyBuZWVkIHRvIGNoZWNrIHNob3VsZFlpZWxkUGFuaWNFcnJvciBoZXJlIGFzIGl0J3Mgc2V0IGFzeW5jaHJvbm91c2x5XG5cbiAgICAvLyBTdG9yZSBQYWdlQ29tcG9uZW50IGZvciByZXVzZSB3aGVuIGhlYWRsZXNzIHN0cmVhbSBjb21wbGV0ZXMgKGxpa2UgUlNDIHdvcmtlcilcbiAgICBoZWFkbGVzc1JzY0hhbmRsZXIucnNjU3RyZWFtLm9uKCdlbmQnLCAoKSA9PiB7XG4gICAgICAvLyBPbmx5IHN0b3JlIGlmIHRoaXMgaXMgYSBoZWFkbGVzcyBzdHJlYW0gYW5kIG5vIGVycm9ycyBvY2N1cnJlZCAobGlrZSBSU0Mgd29ya2VyKVxuICAgICAgaWYgKCFoYXNIZWFkbGVzc1N0cmVhbUVycm9yKGhlYWRsZXNzU3RyZWFtU3RhdGUsIGhhbmRsZXJPcHRpb25zLnJvdXRlKSkge1xuICAgICAgICBoZWFkbGVzc1N0cmVhbVN0YXRlLmVsZW1lbnRzLnNldCh1bmlxdWVJZCwge1xuICAgICAgICAgIFBhZ2VDb21wb25lbnQ6IG5ld0hhbmRsZXJPcHRpb25zLlBhZ2VDb21wb25lbnQsXG4gICAgICAgICAgZXJyb3JlZDogZmFsc2VcbiAgICAgICAgfSk7XG4gICAgICAgIGlmIChoYW5kbGVyT3B0aW9ucy52ZXJib3NlKSB7XG4gICAgICAgICAgaGFuZGxlck9wdGlvbnMubG9nZ2VyPy5pbmZvKGBbcmVuZGVyUGFnZS5zZXJ2ZXJdIFN0b3JlZCBQYWdlQ29tcG9uZW50IGZvciBoZWFkbGVzcyBzdHJlYW0gJHt1bmlxdWVJZH1gKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKGhhbmRsZXJPcHRpb25zLnZlcmJvc2UpIHtcbiAgICAgICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oYFtyZW5kZXJQYWdlLnNlcnZlcl0gSGVhZGxlc3Mgc3RyZWFtIGVycm9yZWQgZm9yIHJvdXRlICR7aGFuZGxlck9wdGlvbnMucm91dGV9LCBub3Qgc3RvcmluZyBQYWdlQ29tcG9uZW50IGZvciByZXVzZWApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICAvLyBDcmVhdGUgZnVsbCBSU0MgaGFuZGxlciAoZm9yIEhUTUwgZ2VuZXJhdGlvbikgLSByZXVzZSBoZWFkbGVzcyBzdHJlYW0gZWxlbWVudHMgaWYgbm8gZXJyb3JzXG4gICAgLy8gRm9yIHNlcnZlci1zaWRlLCB3ZSBjcmVhdGUgYm90aCBzdHJlYW1zIGluIHBhcmFsbGVsIGxpa2UgdGhlIGNsaWVudC1zaWRlXG4gICAgbGV0IGZ1bGxQYW5pY0Vycm9yOiBFcnJvciB8IG51bGwgPSBudWxsO1xuICAgIGNvbnN0IGZ1bGxIYW5kbGVycyA9IGNyZWF0ZU1haW5UaHJlYWRIYW5kbGVycyhcbiAgICAgIGhhbmRsZXJPcHRpb25zLFxuICAgICAgKGVycm9yLCBpc1BhbmljKSA9PiB7XG4gICAgICAgIC8vIElmIHRoaXMgaXMgYSBwYW5pYyBlcnJvciwgc3RvcmUgaXQgdG8gYmUgaGFuZGxlZCBsYXRlclxuICAgICAgICBpZiAoaXNQYW5pYykge1xuICAgICAgICAgIGZ1bGxQYW5pY0Vycm9yID0gZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yIDogbmV3IEVycm9yKFwiRnVsbCBSU0Mgc3RyZWFtIGZhaWxlZFwiKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICk7XG4gICAgXG4gICAgLy8gT3ZlcnJpZGUgb25EYXRhIHRvIHRyYWNrIG1ldHJpY3NcbiAgICBmdWxsSGFuZGxlcnMub25EYXRhID0gKF9pZCwgY2h1bmspID0+IHtcbiAgICAgIHJzY0Z1bGxNZXRyaWNzLmNodW5rcysrO1xuICAgICAgcnNjRnVsbE1ldHJpY3Muc3RyZWFtTWV0cmljcy5ieXRlcyArPSBjaHVuay5sZW5ndGg7XG4gICAgfTtcbiAgICBcbiAgICAvLyBDcmVhdGUgZnVsbCBSU0MgaGFuZGxlciBvcHRpb25zIC0gdXNlIFJlYWN0LkZyYWdtZW50IGlmIGhlYWRsZXNzIHN0cmVhbSBoYWQgZXJyb3JzIChsaWtlIFJTQyB3b3JrZXIpXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgYXJlIGFueSBleGlzdGluZyBoZWFkbGVzcyBzdHJlYW0gZXJyb3JzIGZvciB0aGlzIHJvdXRlXG4gICAgY29uc3QgaGFzRXhpc3RpbmdIZWFkbGVzc0Vycm9yID0gaGFzSGVhZGxlc3NTdHJlYW1FcnJvcihoZWFkbGVzc1N0cmVhbVN0YXRlLCBoYW5kbGVyT3B0aW9ucy5yb3V0ZSk7XG4gICAgY29uc3Qgc2hvdWxkVXNlRmFsbGJhY2sgPSBoZWFkbGVzc1N0cmVhbUVycm9yZWQgfHwgaGFzRXhpc3RpbmdIZWFkbGVzc0Vycm9yO1xuICAgIFxuICAgIGlmIChoYW5kbGVyT3B0aW9ucy52ZXJib3NlKSB7XG4gICAgICBoYW5kbGVyT3B0aW9ucy5sb2dnZXI/LmluZm8oXG4gICAgICAgIGBbcmVuZGVyUGFnZS5zZXJ2ZXJdIENyZWF0aW5nIGZ1bGwgUlNDIGhhbmRsZXIgb3B0aW9ucyBmb3Igcm91dGUgJHtoYW5kbGVyT3B0aW9ucy5yb3V0ZX06IGhlYWRsZXNzU3RyZWFtRXJyb3JlZD0ke2hlYWRsZXNzU3RyZWFtRXJyb3JlZH0sIGhhc0V4aXN0aW5nSGVhZGxlc3NFcnJvcj0ke2hhc0V4aXN0aW5nSGVhZGxlc3NFcnJvcn0sIHNob3VsZFVzZUZhbGxiYWNrPSR7c2hvdWxkVXNlRmFsbGJhY2t9YFxuICAgICAgKTtcbiAgICB9XG4gICAgXG4gICAgLy8gQ3JlYXRlIGEgd3JhcHBlciBQYWdlQ29tcG9uZW50IHRoYXQgcmV0dXJucyBudWxsIGlmIHRoZXJlIGFyZSBoZWFkbGVzcyBzdHJlYW0gZXJyb3JzXG4gICAgY29uc3QgU2FmZVBhZ2VDb21wb25lbnQgPSAocHJvcHM6IGFueSkgPT4ge1xuICAgICAgLy8gQ2hlY2sgaWYgdGhlcmUgYXJlIGFueSBoZWFkbGVzcyBzdHJlYW0gZXJyb3JzIGZvciB0aGlzIHJvdXRlXG4gICAgICBjb25zdCBoYXNFcnJvciA9IGhhc0hlYWRsZXNzU3RyZWFtRXJyb3IoaGVhZGxlc3NTdHJlYW1TdGF0ZSwgaGFuZGxlck9wdGlvbnMucm91dGUpO1xuICAgICAgaWYgKGhhc0Vycm9yKSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG5ld0hhbmRsZXJPcHRpb25zLlBhZ2VDb21wb25lbnQocHJvcHMpO1xuICAgIH07XG4gICAgXG4gICAgY29uc3QgZnVsbFJzY0hhbmRsZXJPcHRpb25zID0ge1xuICAgICAgLi4ubmV3SGFuZGxlck9wdGlvbnMsXG4gICAgICBodG1sUGF0aDogdW5kZWZpbmVkLCAvLyBGdWxsIFJTQyAtIGluY2x1ZGUgSFRNTCB3cmFwcGVyXG4gICAgICBoZWFkbGVzc1N0cmVhbUVsZW1lbnRzOiBoZWFkbGVzc1N0cmVhbVN0YXRlLmVsZW1lbnRzLCAvLyBQYXNzIHRoZSBzdG9yYWdlIG1hcCBmb3IgcmV1c2VcbiAgICAgIC8vIFVzZSBTYWZlUGFnZUNvbXBvbmVudCB0aGF0IHJldHVybnMgbnVsbCB3aGVuIHRoZXJlIGFyZSBoZWFkbGVzcyBzdHJlYW0gZXJyb3JzXG4gICAgICBQYWdlQ29tcG9uZW50OiBTYWZlUGFnZUNvbXBvbmVudCxcbiAgICB9O1xuICAgIFxuICAgIFxuICAgIC8vIENyZWF0ZSBhIFBhZ2VDb21wb25lbnQgdGhhdCB1c2VzIFJlYWN0LnVzZSgpIHRvIGNvbnN1bWUgdGhlIGhlYWRsZXNzIHN0cmVhbSBhbmQgY2hlY2sgZm9yIGVycm9yc1xuXG4gICAgLy8gU3RvcmUgdGhlIGhlYWRsZXNzIHN0cmVhbSBlbGVtZW50cyBmb3IgcmV1c2UgKGxpa2UgUlNDIHdvcmtlciBkb2VzKVxuICAgIFxuICAgIC8vIExpc3RlbiBmb3IgdGhlIGhlYWRsZXNzIHN0cmVhbSB0byBjb21wbGV0ZSBhbmQgc3RvcmUgaXRzIGVsZW1lbnRzXG4gICAgaGVhZGxlc3NSc2NIYW5kbGVyLnJzY1N0cmVhbS5vbignZW5kJywgKCkgPT4ge1xuICAgICAgaWYgKCFoYXNIZWFkbGVzc1N0cmVhbUVycm9yKGhlYWRsZXNzU3RyZWFtU3RhdGUsIGhhbmRsZXJPcHRpb25zLnJvdXRlKSkge1xuICAgICAgICBpZiAoaGFuZGxlck9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgICAgIGhhbmRsZXJPcHRpb25zLmxvZ2dlcj8uaW5mbyhcbiAgICAgICAgICAgIGBbcmVuZGVyUGFnZS5zZXJ2ZXJdIEhlYWRsZXNzIHN0cmVhbSBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5IGZvciByb3V0ZSAke2hhbmRsZXJPcHRpb25zLnJvdXRlfWBcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICBcbiAgICBpZiAoaGFuZGxlck9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgaGFuZGxlck9wdGlvbnMubG9nZ2VyPy5pbmZvKFxuICAgICAgICBgW3JlbmRlclBhZ2Uuc2VydmVyXSBDcmVhdGVkIFBhZ2VDb21wb25lbnQgdGhhdCB1c2VzIFJlYWN0LnVzZSgpIHRvIGNvbnN1bWUgaGVhZGxlc3Mgc3RyZWFtIGZvciByb3V0ZSAke2hhbmRsZXJPcHRpb25zLnJvdXRlfWBcbiAgICAgICk7XG4gICAgfVxuICAgIFxuICAgIGZ1bGxSc2NIYW5kbGVyID0gcmVuZGVyUnNjU3RyZWFtKGZ1bGxSc2NIYW5kbGVyT3B0aW9ucywgZnVsbEhhbmRsZXJzKTtcbiAgICBcbiAgICAvLyBDaGVjayBmb3IgcGFuaWMgZXJyb3IgYWZ0ZXIgY3JlYXRpbmcgdGhlIGhhbmRsZXJcbiAgICBpZiAoZnVsbFBhbmljRXJyb3IpIHtcbiAgICAgIHlpZWxkIHtcbiAgICAgICAgdHlwZTogXCJlcnJvclwiLFxuICAgICAgICBlcnJvcjogZnVsbFBhbmljRXJyb3IsXG4gICAgICAgIG1ldHJpY3M6IHtcbiAgICAgICAgICByc2NGdWxsOiByc2NGdWxsTWV0cmljcyxcbiAgICAgICAgICByc2NIZWFkbGVzczogcnNjSGVhZGxlc3NNZXRyaWNzLFxuICAgICAgICAgIGh0bWw6IGh0bWxNZXRyaWNzLFxuICAgICAgICB9LFxuICAgICAgfTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBDcmVhdGUgSFRNTCB0cmFuc2Zvcm0gc3RyZWFtIC0gbmVlZCBjcmVhdGVSc2NUb0h0bWxTdHJlYW0gZm9yIGFzeW5jIHNlcnZlciBhY3Rpb25zXG4gICAgaHRtbFRyYW5zZm9ybVN0cmVhbSA9IGNyZWF0ZVJzY1RvSHRtbFN0cmVhbSh7XG4gICAgICBpZDogaGFuZGxlck9wdGlvbnMuaWQsXG4gICAgICB3b3JrZXI6IGhhbmRsZXJPcHRpb25zLndvcmtlcixcbiAgICAgIHJvdXRlOiBoYW5kbGVyT3B0aW9ucy5yb3V0ZSxcbiAgICAgIHVybDogaGFuZGxlck9wdGlvbnMudXJsLFxuICAgICAgbW9kdWxlUm9vdFBhdGg6IGhhbmRsZXJPcHRpb25zLm1vZHVsZVJvb3RQYXRoLFxuICAgICAgbW9kdWxlQmFzZVBhdGg6IGhhbmRsZXJPcHRpb25zLm1vZHVsZUJhc2VQYXRoLFxuICAgICAgbW9kdWxlQmFzZVVSTDogaGFuZGxlck9wdGlvbnMubW9kdWxlQmFzZVVSTCxcbiAgICAgIHByb2plY3RSb290OiBoYW5kbGVyT3B0aW9ucy5wcm9qZWN0Um9vdCxcbiAgICAgIGJ1aWxkOiBoYW5kbGVyT3B0aW9ucy5idWlsZCxcbiAgICAgIHBhbmljVGhyZXNob2xkOiBoYW5kbGVyT3B0aW9ucy5wYW5pY1RocmVzaG9sZCxcbiAgICAgIHZlcmJvc2U6IGhhbmRsZXJPcHRpb25zLnZlcmJvc2UsXG4gICAgICBzaWduYWw6IGhhbmRsZXJPcHRpb25zLnNpZ25hbCxcbiAgICAgIGxvZ2dlcjogaGFuZGxlck9wdGlvbnMubG9nZ2VyLFxuICAgICAgaHRtbFdvcmtlcjogaGFuZGxlck9wdGlvbnMuaHRtbFdvcmtlcixcbiAgICAgIGNsaWVudFBpcGVhYmxlU3RyZWFtT3B0aW9uczogaGFuZGxlck9wdGlvbnMuY2xpZW50UGlwZWFibGVTdHJlYW1PcHRpb25zLFxuICAgICAgb25NZXRyaWNzOiBoYW5kbGVyT3B0aW9ucy5vbk1ldHJpY3MsXG4gICAgICBodG1sVGltZW91dDogaGFuZGxlck9wdGlvbnMuaHRtbFRpbWVvdXQgfHwgMTUwMDAsXG4gICAgICByc2NTdHJlYW06IGZ1bGxSc2NIYW5kbGVyLnJzY1N0cmVhbSxcbiAgICAgIG9uRXJyb3I6IChlcnJvciwgaXNQYW5pYykgPT4ge1xuICAgICAgICAvLyBUcmFjayBIVE1MIHN0cmVhbSBlcnJvcnNcbiAgICAgICAgaHRtbFN0cmVhbUVycm9yZWQgPSB0cnVlO1xuICAgICAgICBodG1sU3RyZWFtRXJyb3IgPSBlcnJvcjtcbiAgICAgICAgXG4gICAgICAgIGlmIChpc1BhbmljKSB7XG4gICAgICAgICAgLy8gVGhpcyBpcyBhIHBhbmljIGVycm9yLCBpdCBzaG91bGQgYmUgeWllbGRlZCBhcyBhbiBlcnJvciByZXN1bHRcbiAgICAgICAgICBpZiAoaGFuZGxlck9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgICAgICAgaGFuZGxlck9wdGlvbnMubG9nZ2VyPy5lcnJvcihcbiAgICAgICAgICAgICAgYFtyZW5kZXJQYWdlLnNlcnZlcl0gSFRNTCBzdHJlYW0gcGFuaWMgZXJyb3IgZm9yIHJvdXRlICR7aGFuZGxlck9wdGlvbnMucm91dGV9OiAke2Vycm9yLm1lc3NhZ2V9YFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gRm9yIG5vbi1wYW5pYyBlcnJvcnMsIGp1c3QgbG9nIHRoZW1cbiAgICAgICAgICBpZiAoaGFuZGxlck9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgICAgICAgaGFuZGxlck9wdGlvbnMubG9nZ2VyPy53YXJuKFxuICAgICAgICAgICAgICBgW3JlbmRlclBhZ2Uuc2VydmVyXSBIVE1MIHN0cmVhbSBlcnJvciBmb3Igcm91dGUgJHtoYW5kbGVyT3B0aW9ucy5yb3V0ZX06ICR7ZXJyb3IubWVzc2FnZX1gXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIC8vIENyZWF0ZSBzdHJlYW0gd3JhcHBlcnMgZm9yIGZpbGUgd3JpdGluZyAtIHNpbXBsaWZpZWQgbGlrZSBjbGllbnQgc2lkZVxuICAgIGNvbnN0IHJzY1N0cmVhbVdyYXBwZXIgPSB7XG4gICAgICBwaXBlOiA8V3JpdGFibGUgZXh0ZW5kcyBOb2RlSlMuV3JpdGFibGVTdHJlYW0+KGRlc3RpbmF0aW9uOiBXcml0YWJsZSkgPT4ge1xuICAgICAgICBjb25zdCBzdHJlYW1NZXRyaWNzID0gY3JlYXRlU3RyZWFtTWV0cmljcygpO1xuICAgICAgICBzdHJlYW1NZXRyaWNzLnN0YXJ0VGltZSA9IHBlcmZvcm1hbmNlLm5vdygpO1xuXG4gICAgICAgIC8vIFVzZSB0aGUgaGVhZGxlc3MgUlNDIHN0cmVhbSBkaXJlY3RseSBmb3IgdGhlIC5yc2MgZmlsZVxuICAgICAgICBjb25zdCByc2NGaWxlU3RyZWFtID0gaGVhZGxlc3NSc2NIYW5kbGVyLnJzY1N0cmVhbTtcblxuICAgICAgICByc2NGaWxlU3RyZWFtLm9uKFwiZGF0YVwiLCAoY2h1bms6IEJ1ZmZlcikgPT4ge1xuICAgICAgICAgIHN0cmVhbU1ldHJpY3MuY2h1bmtzKys7XG4gICAgICAgICAgc3RyZWFtTWV0cmljcy5ieXRlcyArPSBjaHVuay5sZW5ndGg7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJzY0ZpbGVTdHJlYW0ub24oXCJlbmRcIiwgKCkgPT4ge1xuICAgICAgICAgIHN0cmVhbU1ldHJpY3MuZHVyYXRpb24gPSBwZXJmb3JtYW5jZS5ub3coKSAtIHN0cmVhbU1ldHJpY3Muc3RhcnRUaW1lO1xuICAgICAgICAgIHN0cmVhbU1ldHJpY3MuZW5kVGltZSA9IHBlcmZvcm1hbmNlLm5vdygpO1xuXG4gICAgICAgICAgcnNjSGVhZGxlc3NNZXRyaWNzLnN0cmVhbU1ldHJpY3MgPSBzdHJlYW1NZXRyaWNzO1xuICAgICAgICAgIHJzY0hlYWRsZXNzTWV0cmljcy5jaHVua1JhdGUgPSBzdHJlYW1NZXRyaWNzLmNodW5rcyAvIChzdHJlYW1NZXRyaWNzLmR1cmF0aW9uIC8gMTAwMCk7XG4gICAgICAgICAgcnNjSGVhZGxlc3NNZXRyaWNzLnByb2Nlc3NpbmdUaW1lID0gc3RyZWFtTWV0cmljcy5kdXJhdGlvbjtcbiAgICAgICAgICByc2NIZWFkbGVzc01ldHJpY3MubWVtb3J5VXNhZ2UgPSBwcm9jZXNzLm1lbW9yeVVzYWdlKCk7XG4gICAgICAgICAgcnNjSGVhZGxlc3NNZXRyaWNzLmNodW5rcyA9IHN0cmVhbU1ldHJpY3MuY2h1bmtzO1xuICAgICAgICB9KTtcblxuICAgICAgICByc2NGaWxlU3RyZWFtLnBpcGUoZGVzdGluYXRpb24pO1xuICAgICAgICByZXR1cm4gZGVzdGluYXRpb247XG4gICAgICB9LFxuICAgICAgYWJvcnQ6ICgpID0+IGhlYWRsZXNzUnNjSGFuZGxlci5hYm9ydCgpLFxuICAgIH07XG5cbiAgICBjb25zdCBodG1sU3RyZWFtV3JhcHBlciA9IHtcbiAgICAgIHBpcGU6IDxXcml0YWJsZSBleHRlbmRzIE5vZGVKUy5Xcml0YWJsZVN0cmVhbT4oZGVzdGluYXRpb246IFdyaXRhYmxlKSA9PiB7XG4gICAgICAgIC8vIFVzZSB0aGUgSFRNTCB0cmFuc2Zvcm0gc3RyZWFtJ3MgcGlwZSBtZXRob2QgZGlyZWN0bHkgKHNhbWUgYXMgY2xpZW50IHNpZGUpXG4gICAgICAgIHJldHVybiBodG1sVHJhbnNmb3JtU3RyZWFtLnBpcGUoZGVzdGluYXRpb24pO1xuICAgICAgfSxcbiAgICAgIGFib3J0OiAoKSA9PiB7XG4gICAgICAgIGh0bWxUcmFuc2Zvcm1TdHJlYW0uYWJvcnQoKTtcbiAgICAgIH0sXG4gICAgICBvbjogKGV2ZW50OiBzdHJpbmcsIGxpc3RlbmVyOiAoLi4uYXJnczogYW55W10pID0+IHZvaWQpID0+IHtcbiAgICAgICAgLy8gRm9yd2FyZCBlcnJvciBldmVudHMgZnJvbSB0aGUgSFRNTCB0cmFuc2Zvcm0gc3RyZWFtIHRvIHRoZSB3cmFwcGVyXG4gICAgICAgIGlmIChldmVudCA9PT0gJ2Vycm9yJykge1xuICAgICAgICAgIC8vIEFjY2VzcyB0aGUgYWN0dWFsIHN0cmVhbSBmcm9tIHRoZSB0cmFuc2Zvcm0gcmVzdWx0XG4gICAgICAgICAgY29uc3QgaHRtbFN0cmVhbSA9IChodG1sVHJhbnNmb3JtU3RyZWFtIGFzIGFueSkuaHRtbFN0cmVhbTtcbiAgICAgICAgICBpZiAoaHRtbFN0cmVhbSAmJiB0eXBlb2YgaHRtbFN0cmVhbS5vbiA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgaHRtbFN0cmVhbS5vbignZXJyb3InLCBsaXN0ZW5lcik7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBodG1sU3RyZWFtV3JhcHBlcjtcbiAgICAgIH0sXG4gICAgfTtcblxuICAgIC8vIFdhaXQgZm9yIEhUTUwgc3RyZWFtIHRvIGNvbXBsZXRlIG9yIGVycm9yIGJlZm9yZSB5aWVsZGluZyBzdWNjZXNzXG4gICAgLy8gVGhpcyBlbnN1cmVzIHRoYXQgYW55IGVycm9ycyBmcm9tIHRoZSBIVE1MIHN0cmVhbSBhcmUgY2F1Z2h0IGJlZm9yZSB3ZSB5aWVsZCBzdWNjZXNzXG4gICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3QgdGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICByZWplY3QobmV3IEVycm9yKGBIVE1MIHN0cmVhbSB0aW1lb3V0IGZvciByb3V0ZSAke2hhbmRsZXJPcHRpb25zLnJvdXRlfWApKTtcbiAgICAgIH0sIGhhbmRsZXJPcHRpb25zLmh0bWxUaW1lb3V0IHx8IDE1MDAwKTtcblxuICAgICAgLy8gQ2hlY2sgaWYgSFRNTCBzdHJlYW0gYWxyZWFkeSBlcnJvcmVkXG4gICAgICBpZiAoaHRtbFN0cmVhbUVycm9yZWQpIHtcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuICAgICAgICByZXNvbHZlKCk7IC8vIExldCB0aGUgcGFuaWMgdGhyZXNob2xkIGxvZ2ljIGhhbmRsZSB0aGlzIGF0IHRoZSByZW5kZXJQYWdlcyBsZXZlbFxuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIC8vIFNldCB1cCBhIGZsYWcgdG8gdHJhY2sgaWYgd2UndmUgcmVzb2x2ZWRcbiAgICAgIGxldCByZXNvbHZlZCA9IGZhbHNlO1xuICAgICAgXG4gICAgICAvLyBDcmVhdGUgYSB3cmFwcGVyIHRoYXQgcmVzb2x2ZXMgdGhlIHByb21pc2Ugd2hlbiB0aGUgc3RyZWFtIGNvbXBsZXRlc1xuICAgICAgY29uc3Qgb3JpZ2luYWxQaXBlID0gaHRtbFRyYW5zZm9ybVN0cmVhbS5waXBlO1xuICAgICAgaHRtbFRyYW5zZm9ybVN0cmVhbS5waXBlID0gZnVuY3Rpb24oZGVzdGluYXRpb246IGFueSkge1xuICAgICAgICBjb25zdCByZXN1bHQgPSBvcmlnaW5hbFBpcGUuY2FsbCh0aGlzLCBkZXN0aW5hdGlvbik7XG4gICAgICAgIFxuICAgICAgICAvLyBMaXN0ZW4gZm9yIHRoZSBkZXN0aW5hdGlvbiBzdHJlYW0gdG8gZW5kXG4gICAgICAgIGRlc3RpbmF0aW9uLm9uKCdmaW5pc2gnLCAoKSA9PiB7XG4gICAgICAgICAgaWYgKCFyZXNvbHZlZCkge1xuICAgICAgICAgICAgcmVzb2x2ZWQgPSB0cnVlO1xuICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuICAgICAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICBkZXN0aW5hdGlvbi5vbignZXJyb3InLCAoZXJyb3I6IEVycm9yKSA9PiB7XG4gICAgICAgICAgaWYgKCFyZXNvbHZlZCkge1xuICAgICAgICAgICAgcmVzb2x2ZWQgPSB0cnVlO1xuICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICBcbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgIH07XG4gICAgICBcbiAgICAgIC8vIElmIHdlIGRvbid0IGhhdmUgYSBkZXN0aW5hdGlvbiB5ZXQsIHJlc29sdmUgYWZ0ZXIgYSBzaG9ydCBkZWxheVxuICAgICAgLy8gVGhpcyBoYW5kbGVzIHRoZSBjYXNlIHdoZXJlIHRoZSBzdHJlYW0gaXMgY3JlYXRlZCBidXQgbm90IHlldCBwaXBlZFxuICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIGlmICghcmVzb2x2ZWQpIHtcbiAgICAgICAgICByZXNvbHZlZCA9IHRydWU7XG4gICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgfVxuICAgICAgfSwgMTAwKTtcbiAgICB9KTtcblxuICAgIC8vIENoZWNrIGZvciBIVE1MIHN0cmVhbSBlcnJvcnMgYWZ0ZXIgd2FpdGluZyBmb3IgY29tcGxldGlvblxuICAgIGlmIChodG1sU3RyZWFtRXJyb3JlZCkge1xuICAgICAgeWllbGQge1xuICAgICAgICB0eXBlOiBcImVycm9yXCIsXG4gICAgICAgIGVycm9yOiBodG1sU3RyZWFtRXJyb3IgfHwgbmV3IEVycm9yKFwiSFRNTCBzdHJlYW0gZmFpbGVkXCIpLFxuICAgICAgICBtZXRyaWNzOiB7XG4gICAgICAgICAgcnNjRnVsbDogcnNjRnVsbE1ldHJpY3MsXG4gICAgICAgICAgcnNjSGVhZGxlc3M6IHJzY0hlYWRsZXNzTWV0cmljcyxcbiAgICAgICAgICBodG1sOiBodG1sTWV0cmljcyxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gWWllbGQgc3VjY2VzcyByZXN1bHQgLSBzaW1wbGlmaWVkIGxpa2UgY2xpZW50IHNpZGVcbiAgICB5aWVsZCB7XG4gICAgICB0eXBlOiBcInN1Y2Nlc3NcIixcbiAgICAgIGh0bWw6IGh0bWxTdHJlYW1XcmFwcGVyLFxuICAgICAgcnNjOiByc2NTdHJlYW1XcmFwcGVyLFxuICAgICAgbWV0cmljczoge1xuICAgICAgICByc2NGdWxsOiByc2NGdWxsTWV0cmljcyxcbiAgICAgICAgcnNjSGVhZGxlc3M6IHJzY0hlYWRsZXNzTWV0cmljcyxcbiAgICAgICAgaHRtbDogaHRtbE1ldHJpY3MsXG4gICAgICB9LFxuICAgIH0gYXMgY29uc3Q7XG5cbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgaWYgKGhhbmRsZXJPcHRpb25zLnZlcmJvc2UpIHtcbiAgICAgIGhhbmRsZXJPcHRpb25zLmxvZ2dlcj8uZXJyb3IoYFtyZW5kZXJQYWdlLnNlcnZlcl0gRXJyb3I6ICR7SlNPTi5zdHJpbmdpZnkoZXJyKX1gKTtcbiAgICB9XG5cbiAgICAvLyBDbGVhbiB1cCBhbnkgcmVzb3VyY2VzXG4gICAgdHJ5IHtcbiAgICAgIGlmIChoZWFkbGVzc1JzY0hhbmRsZXIpIGhlYWRsZXNzUnNjSGFuZGxlci5hYm9ydCgpO1xuICAgICAgaWYgKGZ1bGxSc2NIYW5kbGVyKSBmdWxsUnNjSGFuZGxlci5hYm9ydCgpO1xuICAgICAgaWYgKGh0bWxUcmFuc2Zvcm1TdHJlYW0pIGh0bWxUcmFuc2Zvcm1TdHJlYW0uYWJvcnQoKTtcbiAgICB9IGNhdGNoIChjbGVhbnVwRXJyb3I6IHVua25vd24pIHtcbiAgICAgIGhhbmRsZXJPcHRpb25zLmxvZ2dlcj8ud2FybihgRmFpbGVkIHRvIGNsZWFudXAgc3RyZWFtcyBvbiBlcnJvcjogJHtjbGVhbnVwRXJyb3J9YCk7XG4gICAgfVxuXG4gICAgY29uc3QgcGFuaWNFcnJvciA9IGhhbmRsZUVycm9yKHtcbiAgICAgIGVycm9yOiBlcnIsXG4gICAgICBjcml0aWNhbDogZmFsc2UsXG4gICAgICBsb2dnZXI6IGhhbmRsZXJPcHRpb25zLmxvZ2dlcixcbiAgICAgIHBhbmljVGhyZXNob2xkOiBoYW5kbGVyT3B0aW9ucy5wYW5pY1RocmVzaG9sZCxcbiAgICAgIGNvbnRleHQ6IGBSZW5kZXJQYWdlIEVycm9yICgke2hhbmRsZXJPcHRpb25zLnJvdXRlfSlgLFxuICAgIH0pO1xuXG4gICAgaWYgKHBhbmljRXJyb3IgIT0gbnVsbCkge1xuICAgICAgeWllbGQge1xuICAgICAgICB0eXBlOiBcImVycm9yXCIsXG4gICAgICAgIGVycm9yOiBwYW5pY0Vycm9yLFxuICAgICAgICBtZXRyaWNzOiB7XG4gICAgICAgICAgcnNjRnVsbDogcnNjRnVsbE1ldHJpY3MsXG4gICAgICAgICAgcnNjSGVhZGxlc3M6IHJzY0hlYWRsZXNzTWV0cmljcyxcbiAgICAgICAgICBodG1sOiBodG1sTWV0cmljcyxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIHlpZWxkIHtcbiAgICAgICAgdHlwZTogXCJza2lwXCIsXG4gICAgICAgIHJlYXNvbjogZXJyLFxuICAgICAgICBodG1sOiB7XG4gICAgICAgICAgcGlwZTogPFdyaXRhYmxlIGV4dGVuZHMgTm9kZUpTLldyaXRhYmxlU3RyZWFtPihkZXN0aW5hdGlvbjogV3JpdGFibGUpID0+IHtcbiAgICAgICAgICAgIGRlc3RpbmF0aW9uLmVuZCgpO1xuICAgICAgICAgICAgcmV0dXJuIGRlc3RpbmF0aW9uO1xuICAgICAgICAgIH0sXG4gICAgICAgICAgYWJvcnQ6ICgpID0+IHt9LFxuICAgICAgICB9LFxuICAgICAgICByc2M6IHtcbiAgICAgICAgICBwaXBlOiA8V3JpdGFibGUgZXh0ZW5kcyBOb2RlSlMuV3JpdGFibGVTdHJlYW0+KGRlc3RpbmF0aW9uOiBXcml0YWJsZSkgPT4ge1xuICAgICAgICAgICAgZGVzdGluYXRpb24uZW5kKCk7XG4gICAgICAgICAgICByZXR1cm4gZGVzdGluYXRpb247XG4gICAgICAgICAgfSxcbiAgICAgICAgICBhYm9ydDogKCkgPT4ge30sXG4gICAgICAgIH0sXG4gICAgICAgIG1ldHJpY3M6IHtcbiAgICAgICAgICByc2NGdWxsOiByc2NGdWxsTWV0cmljcyxcbiAgICAgICAgICByc2NIZWFkbGVzczogcnNjSGVhZGxlc3NNZXRyaWNzLFxuICAgICAgICAgIGh0bWw6IGh0bWxNZXRyaWNzLFxuICAgICAgICB9LFxuICAgICAgfTtcbiAgICB9XG4gIH1cbn07Il0sIm5hbWVzIjpbInJlbmRlclBhZ2UiLCJEZWZhdWx0Um9vdCIsIkRlZmF1bHRIdG1sIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXVDYSxNQUFBLFVBQUEsR0FBMkIsZ0JBQWdCQSxXQUFBQSxDQUN0RCxjQUNBLEVBQUE7QUFFQSxFQUFrQixpQkFBQSxFQUFBO0FBR2xCLEVBQUEsTUFBTSxPQUFVLEdBQUEsSUFBQTtBQUFBLElBQ2QsZUFBZSxLQUFNLENBQUEsTUFBQTtBQUFBLElBQ3JCLGVBQWUsS0FBTSxDQUFBO0FBQUEsR0FDdkI7QUFDQSxFQUFBLE1BQU0sU0FBWSxHQUFBLGNBQUEsQ0FBZSxLQUFNLENBQUEsT0FBQSxDQUFRLE9BQU8sRUFBRSxDQUFBO0FBRXhELEVBQUEsTUFBTSxjQUFjLG1CQUFvQixDQUFBO0FBQUEsSUFDdEMsT0FBTyxjQUFlLENBQUEsS0FBQTtBQUFBLElBQ3RCLElBQU0sRUFBQSxNQUFBO0FBQUEsSUFDTixjQUFnQixFQUFBLEtBQUE7QUFBQTtBQUFBLElBQ2hCLGFBQWUsRUFBQSxLQUFBO0FBQUEsSUFDZixjQUFnQixFQUFBLElBQUE7QUFBQSxJQUNoQixPQUFBO0FBQUEsSUFDQSxTQUFBO0FBQUEsSUFDQSxRQUFBLEVBQVUsZUFBZSxLQUFNLENBQUEsY0FBQTtBQUFBLElBQy9CLFlBQVksSUFBSyxDQUFBLE9BQUEsRUFBUyxTQUFXLEVBQUEsY0FBQSxDQUFlLE1BQU0sY0FBYztBQUFBLEdBQ3pFLENBQUE7QUFFRCxFQUFBLE1BQU0saUJBQWlCLG1CQUFvQixDQUFBO0FBQUEsSUFDekMsT0FBTyxjQUFlLENBQUEsS0FBQTtBQUFBLElBQ3RCLElBQU0sRUFBQSxVQUFBO0FBQUEsSUFDTixjQUFnQixFQUFBLElBQUE7QUFBQTtBQUFBLElBQ2hCLGFBQWUsRUFBQSxLQUFBO0FBQUEsSUFDZixjQUFn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
|