@vistagenic/vista 0.3.2 → 0.3.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/bin/vista.js +79 -109
- package/dist/ai/embeddings.d.ts +11 -11
- package/dist/ai/embeddings.js +71 -71
- package/dist/ai/index.d.ts +10 -10
- package/dist/ai/index.js +26 -26
- package/dist/ai/providers/base.js +85 -85
- package/dist/ai/react/react-server.d.ts +1 -1
- package/dist/ai/react/react-server.js +17 -17
- package/dist/ai/react/use-agent.js +138 -138
- package/dist/auth/core.d.ts +140 -140
- package/dist/auth/core.js +165 -165
- package/dist/auth/index.d.ts +20 -20
- package/dist/auth/index.js +369 -369
- package/dist/auth/react-server.d.ts +1 -1
- package/dist/auth/react-server.js +10 -10
- package/dist/auth/react.d.ts +18 -18
- package/dist/auth/react.js +71 -71
- package/dist/bin/build-rsc-flashpack.js +6 -0
- package/dist/bin/build-rsc.js +537 -492
- package/dist/bin/build.js +355 -353
- package/dist/bin/generate.js +513 -510
- package/dist/bin/webpack.config.js +4 -3
- package/dist/build/manifest.d.ts +169 -169
- package/dist/build/manifest.js +423 -423
- package/dist/build/rsc/client-manifest.d.ts +62 -62
- package/dist/build/rsc/client-manifest.js +295 -295
- package/dist/build/rsc/compiler.js +5 -4
- package/dist/build/rsc/native-scanner.d.ts +135 -135
- package/dist/build/rsc/native-scanner.js +203 -203
- package/dist/build/rsc/react-client-reference-manifest.d.ts +28 -28
- package/dist/build/rsc/react-client-reference-manifest.js +240 -240
- package/dist/build/standalone.js +260 -254
- package/dist/client/dynamic.react-server.d.ts +2 -2
- package/dist/client/dynamic.react-server.js +23 -23
- package/dist/client/link.react-server.d.ts +2 -2
- package/dist/client/link.react-server.js +11 -11
- package/dist/client/navigation.react-server.d.ts +1 -1
- package/dist/client/navigation.react-server.js +17 -17
- package/dist/client/router.react-server.d.ts +1 -1
- package/dist/client/router.react-server.js +17 -17
- package/dist/client/rsc-router.react-server.d.ts +1 -1
- package/dist/client/rsc-router.react-server.js +17 -17
- package/dist/client/script.react-server.d.ts +2 -2
- package/dist/client/script.react-server.js +23 -23
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -7
- package/dist/deploy/adapters/cloudflare.js +43 -19
- package/dist/deploy/adapters/docker.d.ts +1 -0
- package/dist/deploy/adapters/docker.js +5 -4
- package/dist/deploy/adapters/netlify.js +62 -17
- package/dist/deploy/adapters/vercel.js +80 -50
- package/dist/deploy/preflight.js +4 -3
- package/dist/deploy/runtime-pack.d.ts +11 -0
- package/dist/deploy/runtime-pack.js +300 -0
- package/dist/deploy/utils.d.ts +1 -1
- package/dist/deploy/utils.js +31 -2
- package/dist/flashpack/command.js +1 -0
- package/dist/flashpack/runtime.js +2 -1
- package/dist/index.d.ts +23 -23
- package/dist/index.js +61 -61
- package/dist/server/app-dir.d.ts +22 -0
- package/dist/server/app-dir.js +77 -0
- package/dist/server/cookie-parse.d.ts +4 -4
- package/dist/server/cookie-parse.js +14 -14
- package/dist/server/engine.js +5 -5
- package/dist/server/image-optimizer.js +2 -1
- package/dist/server/index.d.ts +109 -109
- package/dist/server/index.js +315 -315
- package/dist/server/middleware-runner.d.ts +125 -125
- package/dist/server/middleware-runner.js +607 -615
- package/dist/server/middleware-security.d.ts +36 -36
- package/dist/server/middleware-security.js +183 -183
- package/dist/server/module-compile-hook.js +695 -695
- package/dist/server/root-resolver.js +6 -5
- package/dist/server/rsc-engine-flashpack.d.ts +3 -1
- package/dist/server/rsc-engine-flashpack.js +12 -1
- package/dist/server/rsc-engine.d.ts +6 -1
- package/dist/server/rsc-engine.js +203 -219
- package/dist/server/rsc-upstream.js +893 -892
- package/dist/server/static-generator.js +4 -3
- package/dist/server/structure-validator.js +2 -1
- package/dist/server/structure-watch.js +2 -0
- package/dist/server/typed-api-runtime.js +639 -635
- package/dist/server/vista-import-map.js +134 -134
- package/dist/stack/index.d.ts +34 -34
- package/dist/stack/index.js +49 -49
- package/dist/stack/server/caller.d.ts +13 -13
- package/dist/stack/server/caller.js +42 -42
- package/dist/stack/server/executor.d.ts +40 -40
- package/dist/stack/server/executor.js +222 -222
- package/dist/stack/server/index.d.ts +11 -11
- package/dist/stack/server/index.js +24 -24
- package/dist/stack/server/procedure.d.ts +20 -20
- package/dist/stack/server/procedure.js +66 -66
- package/dist/stack/server/types.d.ts +113 -113
- package/dist/theme/react-server.d.ts +10 -10
- package/dist/theme/react-server.js +16 -16
- package/package.json +1 -1
|
@@ -12,6 +12,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.createRSCApp = createRSCApp;
|
|
16
|
+
exports.getRSCRequestListener = getRSCRequestListener;
|
|
15
17
|
exports.startRSCServer = startRSCServer;
|
|
16
18
|
exports.default = startRSCServer;
|
|
17
19
|
const path_1 = __importDefault(require("path"));
|
|
@@ -81,6 +83,7 @@ const app_router_runtime_1 = require("./app-router-runtime");
|
|
|
81
83
|
const fetch_policy_1 = require("./fetch-policy");
|
|
82
84
|
const vista_import_map_1 = require("./vista-import-map");
|
|
83
85
|
const project_alias_resolver_1 = require("./project-alias-resolver");
|
|
86
|
+
const app_dir_1 = require("./app-dir");
|
|
84
87
|
// Support CSS imports on server runtime
|
|
85
88
|
// - Regular .css: ignored (handled by PostCSS)
|
|
86
89
|
// - .module.css: return empty class mapping (webpack build handles real mappings)
|
|
@@ -432,6 +435,46 @@ function loadSSRManifestFromDisk(absolutePath) {
|
|
|
432
435
|
const manifest = (0, react_client_reference_manifest_1.normalizeReactServerConsumerManifest)(JSON.parse(fs_1.default.readFileSync(absolutePath, 'utf-8')));
|
|
433
436
|
return normalizeSSRManifest(manifest);
|
|
434
437
|
}
|
|
438
|
+
/** Stub `{}` manifests written before the first webpack emit are not usable for Flight SSR. */
|
|
439
|
+
function isSSRManifestReady(manifest) {
|
|
440
|
+
if (!manifest || !manifest.moduleMap)
|
|
441
|
+
return false;
|
|
442
|
+
return Object.keys(manifest.moduleMap).length > 0;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Tee an upstream Flight body: one copy for createFromNodeStream (SSR), one
|
|
446
|
+
* buffer for inline hydration bootstrap in the HTML response.
|
|
447
|
+
*/
|
|
448
|
+
function teeFlightReadable(source) {
|
|
449
|
+
const decodeStream = new stream_1.PassThrough();
|
|
450
|
+
const chunks = [];
|
|
451
|
+
let resolveBuffer;
|
|
452
|
+
let rejectBuffer;
|
|
453
|
+
const bufferPromise = new Promise((resolve, reject) => {
|
|
454
|
+
resolveBuffer = resolve;
|
|
455
|
+
rejectBuffer = reject;
|
|
456
|
+
});
|
|
457
|
+
source.on('data', (chunk) => {
|
|
458
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
459
|
+
chunks.push(buf);
|
|
460
|
+
if (!decodeStream.write(buf)) {
|
|
461
|
+
source.pause();
|
|
462
|
+
decodeStream.once('drain', () => source.resume());
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
source.on('end', () => {
|
|
466
|
+
decodeStream.end();
|
|
467
|
+
resolveBuffer(Buffer.concat(chunks));
|
|
468
|
+
});
|
|
469
|
+
source.on('error', (error) => {
|
|
470
|
+
decodeStream.destroy(error);
|
|
471
|
+
rejectBuffer(error);
|
|
472
|
+
});
|
|
473
|
+
return { decodeStream, bufferPromise };
|
|
474
|
+
}
|
|
475
|
+
function buildInlineFlightBootstrapScript(flightText) {
|
|
476
|
+
return `<script>window.${constants_1.RSC_DATA_FLAG}=${JSON.stringify(flightText)};</script>`;
|
|
477
|
+
}
|
|
435
478
|
function matchPattern(pathname, pattern) {
|
|
436
479
|
const patternParts = pattern.split('/').filter(Boolean);
|
|
437
480
|
const pathParts = pathname.split('/').filter(Boolean);
|
|
@@ -563,7 +606,7 @@ function applySegmentBoundaries(dir, element) {
|
|
|
563
606
|
return wrappedElement;
|
|
564
607
|
}
|
|
565
608
|
async function renderAppSubtreeElement(input) {
|
|
566
|
-
const appDir =
|
|
609
|
+
const appDir = (0, app_dir_1.resolveAppDir)(input.cwd);
|
|
567
610
|
let element = await createRenderableRouteModuleElement(input.entryFilePath, {
|
|
568
611
|
params: input.params,
|
|
569
612
|
searchParams: input.searchParams,
|
|
@@ -630,7 +673,7 @@ async function createRouteElement(route, context, isDev, rootLayout, runtimeRoot
|
|
|
630
673
|
metadata = { ...metadata, ...dynamicMeta };
|
|
631
674
|
}
|
|
632
675
|
const element = await renderAppSubtreeElement({
|
|
633
|
-
subtreeRootDir:
|
|
676
|
+
subtreeRootDir: (0, app_dir_1.resolveAppDir)(runtimeRoot),
|
|
634
677
|
entryFilePath: route.pagePath,
|
|
635
678
|
pathname: req.path,
|
|
636
679
|
params,
|
|
@@ -748,8 +791,8 @@ function spawnUpstream(cwd, runtimeRoot, upstreamPort) {
|
|
|
748
791
|
// ---------------------------------------------------------------------------
|
|
749
792
|
/**
|
|
750
793
|
* Fetch the Flight stream from the upstream RSC process, decode it with
|
|
751
|
-
* createFromNodeStream,
|
|
752
|
-
*
|
|
794
|
+
* createFromNodeStream, render HTML via renderToPipeableStream, and inline the
|
|
795
|
+
* raw Flight payload so the browser can hydrate without a second /rsc fetch.
|
|
753
796
|
*/
|
|
754
797
|
async function renderFlightToHTMLStream(upstreamOrigin, pathname, search, metadataHtml, chunkFiles, rootMode, flightSSRClient, ssrManifest, res, isDev) {
|
|
755
798
|
const flightUrl = `${upstreamOrigin}/rsc${pathname}${search ? `?${search}` : ''}`;
|
|
@@ -763,30 +806,20 @@ async function renderFlightToHTMLStream(upstreamOrigin, pathname, search, metada
|
|
|
763
806
|
if (!upstream.body) {
|
|
764
807
|
throw new Error('Upstream returned empty body');
|
|
765
808
|
}
|
|
766
|
-
// 2.
|
|
809
|
+
// 2. Tee: decode stream for SSR + buffer for inline hydration bootstrap
|
|
767
810
|
const nodeStream = stream_1.Readable.fromWeb(upstream.body);
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
element = react_1.default.createElement(react_1.default.Fragment, null, resolvedTree);
|
|
777
|
-
}
|
|
778
|
-
else {
|
|
779
|
-
// 4. Wrap in a component that consumes the Flight response
|
|
780
|
-
function FlightRoot() {
|
|
781
|
-
return react_1.default.use(flightResponse);
|
|
782
|
-
}
|
|
783
|
-
element = react_1.default.createElement(FlightRoot);
|
|
784
|
-
}
|
|
785
|
-
// 5. Build script tags for client chunks
|
|
811
|
+
const { decodeStream, bufferPromise } = teeFlightReadable(nodeStream);
|
|
812
|
+
// 3. Decode Flight stream into a React tree (stream in both dev and prod)
|
|
813
|
+
const flightResponse = flightSSRClient.createFromNodeStream(decodeStream, ssrManifest);
|
|
814
|
+
function FlightRoot() {
|
|
815
|
+
return react_1.default.use(flightResponse);
|
|
816
|
+
}
|
|
817
|
+
const element = react_1.default.createElement(FlightRoot);
|
|
818
|
+
// 4. Build script tags for client chunks
|
|
786
819
|
const scripts = chunkFiles
|
|
787
820
|
.map((chunk) => `<script defer src="${constants_1.STATIC_CHUNKS_PATH}${chunk}"></script>`)
|
|
788
821
|
.join('\n ');
|
|
789
|
-
//
|
|
822
|
+
// 5. Render to a pipeable HTML stream; inject inline Flight before client scripts
|
|
790
823
|
return new Promise((resolve, reject) => {
|
|
791
824
|
let shellSent = false;
|
|
792
825
|
const { pipe } = (0, server_1.renderToPipeableStream)(element, {
|
|
@@ -795,11 +828,10 @@ async function renderFlightToHTMLStream(upstreamOrigin, pathname, search, metada
|
|
|
795
828
|
res.status(upstream.status === 404 ? 404 : 200);
|
|
796
829
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
797
830
|
res.setHeader('Transfer-Encoding', 'chunked');
|
|
798
|
-
|
|
831
|
+
let heldTail = '';
|
|
799
832
|
const transform = new stream_1.Transform({
|
|
800
833
|
transform(chunk, _encoding, callback) {
|
|
801
834
|
let html = chunk.toString();
|
|
802
|
-
// Inject into <head> if present
|
|
803
835
|
if (html.includes('</head>')) {
|
|
804
836
|
const fontHtml = (0, registry_1.getAllFontHTML)();
|
|
805
837
|
const headInjection = `
|
|
@@ -810,15 +842,29 @@ async function renderFlightToHTMLStream(upstreamOrigin, pathname, search, metada
|
|
|
810
842
|
${getCSSLinks()}`;
|
|
811
843
|
html = html.replace('</head>', `${headInjection}\n</head>`);
|
|
812
844
|
}
|
|
813
|
-
//
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
845
|
+
// Hold </body>… so flush can inject Flight + scripts before it.
|
|
846
|
+
const bodyCloseIdx = html.indexOf('</body>');
|
|
847
|
+
if (bodyCloseIdx !== -1) {
|
|
848
|
+
const beforeBodyClose = html.slice(0, bodyCloseIdx);
|
|
849
|
+
heldTail = html.slice(bodyCloseIdx);
|
|
850
|
+
const hydrateFlag = `<script>window.${constants_1.HYDRATE_DOCUMENT_FLAG} = ${rootMode === 'document'};</script>`;
|
|
851
|
+
callback(null, `${beforeBodyClose}\n ${hydrateFlag}\n`);
|
|
852
|
+
return;
|
|
819
853
|
}
|
|
820
854
|
callback(null, html);
|
|
821
855
|
},
|
|
856
|
+
flush(callback) {
|
|
857
|
+
bufferPromise
|
|
858
|
+
.then((buf) => {
|
|
859
|
+
const flightScript = buildInlineFlightBootstrapScript(buf.toString('utf8'));
|
|
860
|
+
// Flight first, then deferred client chunks, then </body></html>
|
|
861
|
+
this.push(`${flightScript}\n ${scripts}\n${heldTail || '</body></html>'}`);
|
|
862
|
+
callback();
|
|
863
|
+
})
|
|
864
|
+
.catch((error) => {
|
|
865
|
+
callback(error instanceof Error ? error : new Error(String(error)));
|
|
866
|
+
});
|
|
867
|
+
},
|
|
822
868
|
});
|
|
823
869
|
pipe(transform);
|
|
824
870
|
transform.pipe(res);
|
|
@@ -927,6 +973,23 @@ function renderCompilePendingHTML() {
|
|
|
927
973
|
</body>
|
|
928
974
|
</html>`;
|
|
929
975
|
}
|
|
976
|
+
function failRSCStartup(message, listen) {
|
|
977
|
+
console.error(message);
|
|
978
|
+
if (!listen) {
|
|
979
|
+
throw new Error(message);
|
|
980
|
+
}
|
|
981
|
+
process.exit(1);
|
|
982
|
+
}
|
|
983
|
+
let cachedRequestListener = null;
|
|
984
|
+
function createRSCApp(options = {}) {
|
|
985
|
+
return startRSCServer({ ...options, listen: false });
|
|
986
|
+
}
|
|
987
|
+
function getRSCRequestListener(options = {}) {
|
|
988
|
+
if (!cachedRequestListener) {
|
|
989
|
+
cachedRequestListener = createRSCApp(options);
|
|
990
|
+
}
|
|
991
|
+
return cachedRequestListener;
|
|
992
|
+
}
|
|
930
993
|
function startRSCServer(options = {}) {
|
|
931
994
|
const app = (0, express_1.default)();
|
|
932
995
|
const cwd = path_1.default.resolve(options.projectRoot || process.env.VISTA_ARTIFACT_ROOT || process.cwd());
|
|
@@ -943,7 +1006,8 @@ function startRSCServer(options = {}) {
|
|
|
943
1006
|
cleanHotUpdateFiles(cwd);
|
|
944
1007
|
// Request logger — logs GET/POST with timing
|
|
945
1008
|
app.use((0, logger_1.requestLogger)());
|
|
946
|
-
const
|
|
1009
|
+
const shouldListen = options.listen !== false;
|
|
1010
|
+
const port = resolvePort(String(options.port || vistaConfig.server?.port || process.env.PORT || 3003), 3003);
|
|
947
1011
|
const upstreamPort = resolvePort(String(process.env.RSC_UPSTREAM_PORT || port + 1), port + 1);
|
|
948
1012
|
const upstreamOrigin = `http://127.0.0.1:${upstreamPort}`;
|
|
949
1013
|
installSingleReactResolution(runtimeRoot);
|
|
@@ -956,8 +1020,7 @@ function startRSCServer(options = {}) {
|
|
|
956
1020
|
installSSRWebpackShim();
|
|
957
1021
|
const serverManifestPath = path_1.default.join(cwd, constants_1.BUILD_DIR, 'server', 'server-manifest.json');
|
|
958
1022
|
if (!fs_1.default.existsSync(serverManifestPath)) {
|
|
959
|
-
|
|
960
|
-
process.exit(1);
|
|
1023
|
+
failRSCStartup(`[vista:rsc] Missing server manifest at ${serverManifestPath}. Run "vista build --rsc" first.`, shouldListen);
|
|
961
1024
|
}
|
|
962
1025
|
try {
|
|
963
1026
|
if (!isDev || !options.compiler) {
|
|
@@ -965,8 +1028,7 @@ function startRSCServer(options = {}) {
|
|
|
965
1028
|
}
|
|
966
1029
|
}
|
|
967
1030
|
catch (error) {
|
|
968
|
-
|
|
969
|
-
process.exit(1);
|
|
1031
|
+
failRSCStartup(error.message, shouldListen);
|
|
970
1032
|
}
|
|
971
1033
|
// ========================================================================
|
|
972
1034
|
// Flight SSR Client + SSR Manifest
|
|
@@ -978,7 +1040,8 @@ function startRSCServer(options = {}) {
|
|
|
978
1040
|
flightSSRClient = require(flightClientPath);
|
|
979
1041
|
}
|
|
980
1042
|
catch (err) {
|
|
981
|
-
// Flight SSR
|
|
1043
|
+
// Flight SSR client missing — requests will fail closed (no renderToString).
|
|
1044
|
+
(0, logger_1.logError)(`[vista:rsc] react-server-dom-webpack/client.node unavailable: ${err?.message || String(err)}`);
|
|
982
1045
|
}
|
|
983
1046
|
const ssrManifestPath = path_1.default.join(cwd, constants_1.BUILD_DIR, 'react-server-manifest.json');
|
|
984
1047
|
const ssrManifestLegacyPath = path_1.default.join(cwd, constants_1.BUILD_DIR, 'react-ssr-manifest.json');
|
|
@@ -988,13 +1051,21 @@ function startRSCServer(options = {}) {
|
|
|
988
1051
|
? ssrManifestLegacyPath
|
|
989
1052
|
: null;
|
|
990
1053
|
if (resolvedSSRManifestPath) {
|
|
991
|
-
|
|
1054
|
+
try {
|
|
1055
|
+
ssrManifest = loadSSRManifestFromDisk(resolvedSSRManifestPath);
|
|
1056
|
+
}
|
|
1057
|
+
catch {
|
|
1058
|
+
ssrManifest = null;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
if (ssrManifest && !isSSRManifestReady(ssrManifest)) {
|
|
1062
|
+
// Stub manifests written before the first webpack emit must not drive SSR.
|
|
1063
|
+
ssrManifest = null;
|
|
992
1064
|
}
|
|
993
|
-
|
|
994
|
-
// Can't use Flight SSR without the manifest
|
|
1065
|
+
if (!resolvedSSRManifestPath && flightSSRClient) {
|
|
995
1066
|
flightSSRClient = null;
|
|
996
1067
|
}
|
|
997
|
-
|
|
1068
|
+
let useFlightSSR = !!flightSSRClient && isSSRManifestReady(ssrManifest);
|
|
998
1069
|
// ========================================================================
|
|
999
1070
|
// Structure Validation (dev + strict-block)
|
|
1000
1071
|
// ========================================================================
|
|
@@ -1098,11 +1169,15 @@ function startRSCServer(options = {}) {
|
|
|
1098
1169
|
httpServer.close();
|
|
1099
1170
|
}
|
|
1100
1171
|
// 6. Force exit after brief grace period (Windows Ctrl+C fix)
|
|
1101
|
-
|
|
1172
|
+
if (shouldListen) {
|
|
1173
|
+
setTimeout(() => process.exit(0), 500);
|
|
1174
|
+
}
|
|
1102
1175
|
};
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1176
|
+
if (shouldListen) {
|
|
1177
|
+
process.on('SIGINT', shutdown);
|
|
1178
|
+
process.on('SIGTERM', shutdown);
|
|
1179
|
+
process.on('exit', shutdown);
|
|
1180
|
+
}
|
|
1106
1181
|
// ========================================================================
|
|
1107
1182
|
// Live-Reload SSE for RSC dev mode
|
|
1108
1183
|
// - Pushes reload events when server components change (fs.watch)
|
|
@@ -1134,6 +1209,7 @@ function startRSCServer(options = {}) {
|
|
|
1134
1209
|
'lib',
|
|
1135
1210
|
'ctx',
|
|
1136
1211
|
'data',
|
|
1212
|
+
'src',
|
|
1137
1213
|
'middleware.ts',
|
|
1138
1214
|
'vista.config.ts',
|
|
1139
1215
|
'content-collections.ts',
|
|
@@ -1228,9 +1304,18 @@ function startRSCServer(options = {}) {
|
|
|
1228
1304
|
if (fs_1.default.existsSync(serverManifestPath)) {
|
|
1229
1305
|
serverManifest = JSON.parse(fs_1.default.readFileSync(serverManifestPath, 'utf-8'));
|
|
1230
1306
|
}
|
|
1231
|
-
// Reload SSR manifest on rebuild
|
|
1307
|
+
// Reload SSR manifest on rebuild; ignore stub `{}` until webpack writes a real map.
|
|
1232
1308
|
if (resolvedSSRManifestPath && fs_1.default.existsSync(resolvedSSRManifestPath)) {
|
|
1233
|
-
|
|
1309
|
+
try {
|
|
1310
|
+
const nextManifest = loadSSRManifestFromDisk(resolvedSSRManifestPath);
|
|
1311
|
+
if (isSSRManifestReady(nextManifest)) {
|
|
1312
|
+
ssrManifest = nextManifest;
|
|
1313
|
+
useFlightSSR = !!flightSSRClient;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
catch {
|
|
1317
|
+
// Manifest may be mid-write during compilation.
|
|
1318
|
+
}
|
|
1234
1319
|
}
|
|
1235
1320
|
});
|
|
1236
1321
|
}
|
|
@@ -1503,28 +1588,50 @@ function startRSCServer(options = {}) {
|
|
|
1503
1588
|
return;
|
|
1504
1589
|
}
|
|
1505
1590
|
// ==================================================================
|
|
1506
|
-
// Flight-Based SSR Path
|
|
1591
|
+
// Flight-Based SSR Path (fail-closed — no renderToString page HTML)
|
|
1507
1592
|
// ==================================================================
|
|
1508
|
-
|
|
1509
|
-
// by fetching the Flight stream from upstream and using
|
|
1510
|
-
// renderToPipeableStream for streaming HTML with proper hydration.
|
|
1511
|
-
// Falls back to legacy renderToString if Flight SSR is unavailable.
|
|
1512
|
-
// ==================================================================
|
|
1513
|
-
if (useFlightSSR) {
|
|
1593
|
+
if (isDev && resolvedSSRManifestPath && fs_1.default.existsSync(resolvedSSRManifestPath)) {
|
|
1514
1594
|
try {
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
catch {
|
|
1520
|
-
// Manifest may be mid-write during compilation; keep the last good in-memory copy.
|
|
1521
|
-
}
|
|
1595
|
+
const nextManifest = loadSSRManifestFromDisk(resolvedSSRManifestPath);
|
|
1596
|
+
if (isSSRManifestReady(nextManifest)) {
|
|
1597
|
+
ssrManifest = nextManifest;
|
|
1598
|
+
useFlightSSR = !!flightSSRClient;
|
|
1522
1599
|
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1600
|
+
}
|
|
1601
|
+
catch {
|
|
1602
|
+
// Manifest may be mid-write during compilation; keep the last good in-memory copy.
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
if (!useFlightSSR || !flightSSRClient || !isSSRManifestReady(ssrManifest)) {
|
|
1606
|
+
if (isDev && options.compiler) {
|
|
1607
|
+
// Wait for a non-empty SSR manifest after the first webpack emit.
|
|
1608
|
+
res.status(503).type('text/html').send(renderCompilePendingHTML());
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
const message = 'Flight SSR is unavailable. A usable react-server-manifest.json is required; Vista no longer falls back to renderToString.';
|
|
1612
|
+
if (isDev) {
|
|
1613
|
+
res.status(500).send((0, dev_error_1.renderErrorHTML)([
|
|
1614
|
+
{
|
|
1615
|
+
type: 'build',
|
|
1616
|
+
source: 'server',
|
|
1617
|
+
message,
|
|
1618
|
+
},
|
|
1619
|
+
]));
|
|
1620
|
+
}
|
|
1621
|
+
else {
|
|
1622
|
+
res.status(500).type('text/plain').send(message);
|
|
1623
|
+
}
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
try {
|
|
1627
|
+
// Metadata extraction: still done locally so we have <head> content.
|
|
1628
|
+
// notFound() here must not skip Flight — upstream renders the nearest
|
|
1629
|
+
// segment not-found as a 404 Flight payload.
|
|
1630
|
+
const rootLayout = (0, root_resolver_1.resolveRootLayout)(runtimeRoot, isDev);
|
|
1631
|
+
const route = currentRoute;
|
|
1632
|
+
let metadataHtml = '';
|
|
1633
|
+
if (route) {
|
|
1634
|
+
try {
|
|
1528
1635
|
if (isDev) {
|
|
1529
1636
|
clearProjectRequireCache(runtimeRoot);
|
|
1530
1637
|
}
|
|
@@ -1542,173 +1649,49 @@ function startRSCServer(options = {}) {
|
|
|
1542
1649
|
const { generateMetadataHtml } = require('../metadata/generate');
|
|
1543
1650
|
metadataHtml = metadata ? generateMetadataHtml(metadata) : '';
|
|
1544
1651
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
}
|
|
1549
|
-
catch (flightError) {
|
|
1550
|
-
if (flightError?.name === 'NotFoundError' && !res.headersSent) {
|
|
1551
|
-
try {
|
|
1552
|
-
const rootLayout = (0, root_resolver_1.resolveRootLayout)(runtimeRoot, isDev);
|
|
1553
|
-
const route = currentRoute;
|
|
1554
|
-
if (route) {
|
|
1555
|
-
const segmentNotFoundPath = (0, app_router_runtime_1.resolveNearestSegmentNotFoundPath)(path_1.default.join(runtimeRoot, 'app'), route.routeDir);
|
|
1556
|
-
if (segmentNotFoundPath) {
|
|
1557
|
-
const params = extractParams(req.path, route);
|
|
1558
|
-
const searchParams = Object.fromEntries(new URLSearchParams(req.query).entries());
|
|
1559
|
-
const notFoundResult = await createRouteElement({
|
|
1560
|
-
...route,
|
|
1561
|
-
pagePath: segmentNotFoundPath,
|
|
1562
|
-
}, { params, searchParams, req }, isDev, rootLayout, runtimeRoot, { disableParallelSlots: true });
|
|
1563
|
-
const html = (0, server_1.renderToString)(notFoundResult.element);
|
|
1564
|
-
const { generateMetadataHtml } = require('../metadata/generate');
|
|
1565
|
-
const metadataHtml = notFoundResult.metadata
|
|
1566
|
-
? generateMetadataHtml(notFoundResult.metadata)
|
|
1567
|
-
: '';
|
|
1568
|
-
res
|
|
1569
|
-
.status(404)
|
|
1570
|
-
.type('text/html')
|
|
1571
|
-
.send(createHtmlDocument(html, metadataHtml, findChunkFiles(cwd, isDev), notFoundResult.rootMode));
|
|
1572
|
-
return;
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
catch (notFoundError) {
|
|
1577
|
-
console.error('[vista:rsc] Failed to render segment not-found fallback:', notFoundError);
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
console.error('[vista:rsc] Flight SSR failed:', flightError.message);
|
|
1581
|
-
// If headers haven't been sent yet, show the error overlay directly.
|
|
1582
|
-
// This is much better than falling through to legacy renderToString,
|
|
1583
|
-
// which will likely hit the same error (e.g. useState in a server component).
|
|
1584
|
-
if (isDev && !res.headersSent) {
|
|
1585
|
-
res.status(500).send((0, dev_error_1.renderErrorHTML)([(0, dev_error_1.fromCaughtError)(flightError, { source: 'server' })]));
|
|
1586
|
-
return;
|
|
1587
|
-
}
|
|
1588
|
-
// If headers were already sent (stream was partially flushed),
|
|
1589
|
-
// we can't change the status code, but we can inject an error
|
|
1590
|
-
// overlay script at the end of the stream.
|
|
1591
|
-
if (isDev && res.headersSent) {
|
|
1592
|
-
try {
|
|
1593
|
-
const errMsg = (flightError.message || 'Flight SSR Error')
|
|
1594
|
-
.replace(/'/g, "\\'")
|
|
1595
|
-
.replace(/\n/g, '\\n');
|
|
1596
|
-
res.write(`<script>document.body.innerHTML='';document.body.style.background='#1a1a2e';document.body.style.color='#ff6b6b';document.body.style.fontFamily='monospace';document.body.style.padding='40px';document.body.innerHTML='<h2 style="color:#ff6b6b">\\u26a0 Server Error</h2><pre style="white-space:pre-wrap;color:#ffa07a">${errMsg}</pre>';</script>`);
|
|
1597
|
-
res.end();
|
|
1652
|
+
catch (metadataError) {
|
|
1653
|
+
if (metadataError?.name !== 'NotFoundError') {
|
|
1654
|
+
throw metadataError;
|
|
1598
1655
|
}
|
|
1599
|
-
catch {
|
|
1600
|
-
res.end();
|
|
1601
|
-
}
|
|
1602
|
-
return;
|
|
1603
1656
|
}
|
|
1604
1657
|
}
|
|
1605
|
-
|
|
1606
|
-
// ==================================================================
|
|
1607
|
-
// Legacy Fallback: Direct renderToString
|
|
1608
|
-
// ==================================================================
|
|
1609
|
-
// Used when Flight SSR is unavailable or fails. This path does NOT
|
|
1610
|
-
// go through the Flight protocol — it requires page modules directly
|
|
1611
|
-
// and renders them with renderToString (synchronous, no streaming).
|
|
1612
|
-
// ==================================================================
|
|
1613
|
-
try {
|
|
1614
|
-
// Check upstream availability for the legacy path
|
|
1615
|
-
await withTimeout(`${upstreamOrigin}/rsc/`, { headers: { Accept: 'text/x-component' } }, 3000);
|
|
1616
|
-
}
|
|
1617
|
-
catch (error) {
|
|
1618
|
-
res.status(503).type('text/plain').send(getUpstreamUnavailableMessage());
|
|
1658
|
+
await renderFlightToHTMLStream(upstreamOrigin, req.path, req.query ? new URLSearchParams(req.query).toString() : '', metadataHtml, findChunkFiles(cwd, isDev), rootLayout.mode, flightSSRClient, ssrManifest, res, isDev);
|
|
1619
1659
|
return;
|
|
1620
1660
|
}
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
const route = currentRoute;
|
|
1624
|
-
(0, request_context_1.setCurrentSegmentConfig)(route?.segmentConfig);
|
|
1625
|
-
if (!route) {
|
|
1626
|
-
const resolvedNotFound = (0, root_resolver_1.resolveNotFoundComponent)(runtimeRoot, rootLayout, isDev);
|
|
1627
|
-
if (resolvedNotFound) {
|
|
1628
|
-
const notFoundElement = react_1.default.createElement(resolvedNotFound.component, {
|
|
1629
|
-
params: {},
|
|
1630
|
-
searchParams: {},
|
|
1631
|
-
});
|
|
1632
|
-
const wrapped = react_1.default.createElement(rootLayout.component, { params: {}, searchParams: {} }, notFoundElement);
|
|
1633
|
-
const html = (0, server_1.renderToString)(wrapped);
|
|
1634
|
-
res
|
|
1635
|
-
.status(404)
|
|
1636
|
-
.type('text/html')
|
|
1637
|
-
.send(createHtmlDocument(html, '', findChunkFiles(cwd, isDev), rootLayout.mode));
|
|
1638
|
-
return;
|
|
1639
|
-
}
|
|
1661
|
+
catch (flightError) {
|
|
1662
|
+
if (flightError?.name === 'NotFoundError' && !res.headersSent) {
|
|
1640
1663
|
res.status(404).type('text/html').send((0, not_found_page_1.getStyledNotFoundHTML)());
|
|
1641
1664
|
return;
|
|
1642
1665
|
}
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
const { generateMetadataHtml } = require('../metadata/generate');
|
|
1648
|
-
const metadataHtml = metadata ? generateMetadataHtml(metadata) : '';
|
|
1649
|
-
res
|
|
1650
|
-
.status(200)
|
|
1651
|
-
.type('text/html')
|
|
1652
|
-
.send(createHtmlDocument(appHtml, metadataHtml, findChunkFiles(cwd, isDev), rootMode));
|
|
1653
|
-
}
|
|
1654
|
-
catch (error) {
|
|
1655
|
-
if (error?.name === 'NotFoundError') {
|
|
1656
|
-
try {
|
|
1657
|
-
const rootLayout = (0, root_resolver_1.resolveRootLayout)(runtimeRoot, isDev);
|
|
1658
|
-
const route = currentRoute;
|
|
1659
|
-
(0, request_context_1.setCurrentSegmentConfig)(route?.segmentConfig);
|
|
1660
|
-
if (route) {
|
|
1661
|
-
const segmentNotFoundPath = (0, app_router_runtime_1.resolveNearestSegmentNotFoundPath)(path_1.default.join(runtimeRoot, 'app'), route.routeDir);
|
|
1662
|
-
if (segmentNotFoundPath) {
|
|
1663
|
-
const params = extractParams(req.path, route);
|
|
1664
|
-
const searchParams = Object.fromEntries(new URLSearchParams(req.query).entries());
|
|
1665
|
-
const notFoundResult = await createRouteElement({
|
|
1666
|
-
...route,
|
|
1667
|
-
pagePath: segmentNotFoundPath,
|
|
1668
|
-
}, { params, searchParams, req }, isDev, rootLayout, runtimeRoot, { disableParallelSlots: true });
|
|
1669
|
-
const html = (0, server_1.renderToString)(notFoundResult.element);
|
|
1670
|
-
const { generateMetadataHtml } = require('../metadata/generate');
|
|
1671
|
-
const metadataHtml = notFoundResult.metadata
|
|
1672
|
-
? generateMetadataHtml(notFoundResult.metadata)
|
|
1673
|
-
: '';
|
|
1674
|
-
res
|
|
1675
|
-
.status(404)
|
|
1676
|
-
.type('text/html')
|
|
1677
|
-
.send(createHtmlDocument(html, metadataHtml, findChunkFiles(cwd, isDev), notFoundResult.rootMode));
|
|
1678
|
-
return;
|
|
1679
|
-
}
|
|
1680
|
-
}
|
|
1681
|
-
const resolvedNotFound = (0, root_resolver_1.resolveNotFoundComponent)(runtimeRoot, rootLayout, isDev);
|
|
1682
|
-
if (resolvedNotFound) {
|
|
1683
|
-
const notFoundElement = react_1.default.createElement(resolvedNotFound.component, {
|
|
1684
|
-
params: {},
|
|
1685
|
-
searchParams: {},
|
|
1686
|
-
});
|
|
1687
|
-
const wrapped = react_1.default.createElement(rootLayout.component, { params: {}, searchParams: {} }, notFoundElement);
|
|
1688
|
-
const html = (0, server_1.renderToString)(wrapped);
|
|
1689
|
-
res
|
|
1690
|
-
.status(404)
|
|
1691
|
-
.type('text/html')
|
|
1692
|
-
.send(createHtmlDocument(html, '', findChunkFiles(cwd, isDev), rootLayout.mode));
|
|
1693
|
-
return;
|
|
1694
|
-
}
|
|
1695
|
-
res.status(404).type('text/html').send((0, not_found_page_1.getStyledNotFoundHTML)());
|
|
1696
|
-
return;
|
|
1666
|
+
console.error('[vista:rsc] Flight SSR failed:', flightError.message);
|
|
1667
|
+
if (!res.headersSent) {
|
|
1668
|
+
if (isDev) {
|
|
1669
|
+
res.status(500).send((0, dev_error_1.renderErrorHTML)([(0, dev_error_1.fromCaughtError)(flightError, { source: 'server' })]));
|
|
1697
1670
|
}
|
|
1698
|
-
|
|
1699
|
-
|
|
1671
|
+
else {
|
|
1672
|
+
res.status(500).send('<h1>Internal Server Error</h1>');
|
|
1700
1673
|
}
|
|
1674
|
+
return;
|
|
1701
1675
|
}
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1676
|
+
if (isDev && res.headersSent) {
|
|
1677
|
+
try {
|
|
1678
|
+
const errMsg = (flightError.message || 'Flight SSR Error')
|
|
1679
|
+
.replace(/'/g, "\\'")
|
|
1680
|
+
.replace(/\n/g, '\\n');
|
|
1681
|
+
res.write(`<script>document.body.innerHTML='';document.body.style.background='#1a1a2e';document.body.style.color='#ff6b6b';document.body.style.fontFamily='monospace';document.body.style.padding='40px';document.body.innerHTML='<h2 style="color:#ff6b6b">\\u26a0 Server Error</h2><pre style="white-space:pre-wrap;color:#ffa07a">${errMsg}</pre>';</script>`);
|
|
1682
|
+
res.end();
|
|
1683
|
+
}
|
|
1684
|
+
catch {
|
|
1685
|
+
res.end();
|
|
1686
|
+
}
|
|
1708
1687
|
}
|
|
1688
|
+
return;
|
|
1709
1689
|
}
|
|
1710
1690
|
});
|
|
1711
1691
|
});
|
|
1692
|
+
if (!shouldListen) {
|
|
1693
|
+
return app;
|
|
1694
|
+
}
|
|
1712
1695
|
const server = app.listen(port, () => {
|
|
1713
1696
|
(0, logger_1.printServerReady)({ port, mode: 'rsc', rscFlight: useFlightSSR });
|
|
1714
1697
|
});
|
|
@@ -1722,4 +1705,5 @@ function startRSCServer(options = {}) {
|
|
|
1722
1705
|
(0, logger_1.logError)(`RSC startup failed: ${error?.message || String(error)}`);
|
|
1723
1706
|
process.exit(1);
|
|
1724
1707
|
});
|
|
1708
|
+
return app;
|
|
1725
1709
|
}
|