@vistagenic/vista 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/bin/vista.js +79 -109
  2. package/dist/ai/embeddings.d.ts +11 -11
  3. package/dist/ai/embeddings.js +71 -71
  4. package/dist/ai/index.d.ts +10 -10
  5. package/dist/ai/index.js +26 -26
  6. package/dist/ai/providers/base.js +85 -85
  7. package/dist/ai/react/react-server.d.ts +1 -1
  8. package/dist/ai/react/react-server.js +17 -17
  9. package/dist/ai/react/use-agent.js +138 -138
  10. package/dist/auth/core.d.ts +140 -140
  11. package/dist/auth/core.js +165 -165
  12. package/dist/auth/index.d.ts +20 -20
  13. package/dist/auth/index.js +369 -369
  14. package/dist/auth/react-server.d.ts +1 -1
  15. package/dist/auth/react-server.js +10 -10
  16. package/dist/auth/react.d.ts +18 -18
  17. package/dist/auth/react.js +71 -71
  18. package/dist/bin/build-rsc-flashpack.js +6 -0
  19. package/dist/bin/build-rsc.js +555 -492
  20. package/dist/bin/build.js +365 -353
  21. package/dist/bin/generate.js +513 -510
  22. package/dist/bin/webpack.config.d.ts +3 -1
  23. package/dist/bin/webpack.config.js +7 -4
  24. package/dist/build/manifest.d.ts +169 -169
  25. package/dist/build/manifest.js +423 -423
  26. package/dist/build/rsc/client-manifest.d.ts +62 -62
  27. package/dist/build/rsc/client-manifest.js +295 -295
  28. package/dist/build/rsc/compiler.d.ts +3 -1
  29. package/dist/build/rsc/compiler.js +12 -7
  30. package/dist/build/rsc/native-scanner.d.ts +135 -135
  31. package/dist/build/rsc/native-scanner.js +203 -203
  32. package/dist/build/rsc/react-client-reference-manifest.d.ts +35 -28
  33. package/dist/build/rsc/react-client-reference-manifest.js +366 -240
  34. package/dist/build/standalone.js +260 -254
  35. package/dist/client/dynamic.react-server.d.ts +2 -2
  36. package/dist/client/dynamic.react-server.js +23 -23
  37. package/dist/client/link.js +1 -1
  38. package/dist/client/link.react-server.d.ts +2 -2
  39. package/dist/client/link.react-server.js +11 -11
  40. package/dist/client/navigation.js +2 -2
  41. package/dist/client/navigation.react-server.d.ts +1 -1
  42. package/dist/client/navigation.react-server.js +17 -17
  43. package/dist/client/router.react-server.d.ts +1 -1
  44. package/dist/client/router.react-server.js +17 -17
  45. package/dist/client/rsc-router.d.ts +1 -3
  46. package/dist/client/rsc-router.js +98 -45
  47. package/dist/client/rsc-router.react-server.d.ts +1 -1
  48. package/dist/client/rsc-router.react-server.js +17 -17
  49. package/dist/client/script.react-server.d.ts +2 -2
  50. package/dist/client/script.react-server.js +23 -23
  51. package/dist/config.d.ts +1 -1
  52. package/dist/config.js +1 -7
  53. package/dist/deploy/adapters/cloudflare.js +42 -41
  54. package/dist/deploy/adapters/docker.d.ts +1 -0
  55. package/dist/deploy/adapters/docker.js +5 -4
  56. package/dist/deploy/adapters/netlify.js +60 -27
  57. package/dist/deploy/adapters/vercel.js +80 -50
  58. package/dist/deploy/preflight.js +4 -3
  59. package/dist/deploy/runtime-pack.d.ts +11 -0
  60. package/dist/deploy/runtime-pack.js +300 -0
  61. package/dist/deploy/utils.d.ts +7 -1
  62. package/dist/deploy/utils.js +95 -3
  63. package/dist/flashpack/command.js +1 -0
  64. package/dist/flashpack/runtime.js +2 -1
  65. package/dist/image/get-img-props.js +21 -11
  66. package/dist/image/image-config.d.ts +2 -0
  67. package/dist/image/image-config.js +17 -0
  68. package/dist/image/index.d.ts +18 -3
  69. package/dist/image/index.js +1 -1
  70. package/dist/image/react-server.d.ts +1 -2
  71. package/dist/image/react-server.js +1 -1
  72. package/dist/index.d.ts +23 -23
  73. package/dist/index.js +61 -61
  74. package/dist/server/app-dir.d.ts +22 -0
  75. package/dist/server/app-dir.js +77 -0
  76. package/dist/server/app-router-runtime.d.ts +1 -0
  77. package/dist/server/app-router-runtime.js +34 -9
  78. package/dist/server/cookie-parse.d.ts +4 -4
  79. package/dist/server/cookie-parse.js +14 -14
  80. package/dist/server/engine.js +5 -5
  81. package/dist/server/hydration-chunks.d.ts +7 -0
  82. package/dist/server/hydration-chunks.js +49 -0
  83. package/dist/server/image-optimizer.js +2 -1
  84. package/dist/server/index.d.ts +109 -109
  85. package/dist/server/index.js +315 -315
  86. package/dist/server/middleware-runner.d.ts +125 -125
  87. package/dist/server/middleware-runner.js +607 -615
  88. package/dist/server/middleware-security.d.ts +36 -36
  89. package/dist/server/middleware-security.js +183 -183
  90. package/dist/server/module-compile-hook.js +706 -695
  91. package/dist/server/root-resolver.js +6 -5
  92. package/dist/server/rsc-engine-flashpack.d.ts +3 -1
  93. package/dist/server/rsc-engine-flashpack.js +12 -1
  94. package/dist/server/rsc-engine.d.ts +6 -1
  95. package/dist/server/rsc-engine.js +217 -280
  96. package/dist/server/rsc-upstream.js +900 -892
  97. package/dist/server/ssr-webpack-shim.d.ts +6 -0
  98. package/dist/server/ssr-webpack-shim.js +29 -0
  99. package/dist/server/static-generator.d.ts +2 -0
  100. package/dist/server/static-generator.js +33 -32
  101. package/dist/server/structure-validator.js +2 -1
  102. package/dist/server/structure-watch.js +2 -0
  103. package/dist/server/typed-api-runtime.js +639 -635
  104. package/dist/server/vista-import-map.js +134 -134
  105. package/dist/stack/index.d.ts +34 -34
  106. package/dist/stack/index.js +49 -49
  107. package/dist/stack/server/caller.d.ts +13 -13
  108. package/dist/stack/server/caller.js +42 -42
  109. package/dist/stack/server/executor.d.ts +40 -40
  110. package/dist/stack/server/executor.js +222 -222
  111. package/dist/stack/server/index.d.ts +11 -11
  112. package/dist/stack/server/index.js +24 -24
  113. package/dist/stack/server/procedure.d.ts +20 -20
  114. package/dist/stack/server/procedure.js +66 -66
  115. package/dist/stack/server/types.d.ts +113 -113
  116. package/dist/theme/react-server.d.ts +10 -10
  117. package/dist/theme/react-server.js +16 -16
  118. 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"));
@@ -22,7 +24,7 @@ const server_1 = require("react-dom/server");
22
24
  const webpack_dev_middleware_1 = __importDefault(require("webpack-dev-middleware"));
23
25
  const stream_1 = require("stream");
24
26
  const child_process_1 = require("child_process");
25
- const url_1 = require("url");
27
+ const ssr_webpack_shim_1 = require("./ssr-webpack-shim");
26
28
  const middleware_runner_1 = require("./middleware-runner");
27
29
  const image_optimizer_1 = require("./image-optimizer");
28
30
  const ppr_1 = require("./ppr");
@@ -31,39 +33,10 @@ const logger_1 = require("./logger");
31
33
  const not_found_page_1 = require("./not-found-page");
32
34
  const static_cache_1 = require("./static-cache");
33
35
  const static_generator_1 = require("./static-generator");
36
+ const hydration_chunks_1 = require("./hydration-chunks");
34
37
  const react_client_reference_manifest_1 = require("../build/rsc/react-client-reference-manifest");
35
38
  const constants_1 = require("../constants");
36
39
  const CjsModule = require('module');
37
- // ---------------------------------------------------------------------------
38
- // SSR Webpack Shim
39
- // ---------------------------------------------------------------------------
40
- // The Flight SSR decoder (react-server-dom-webpack/client.node) calls
41
- // __webpack_require__(specifier) to load client components during SSR.
42
- // In Vista the SSR process runs in plain Node.js (not through webpack),
43
- // so we provide a shim that converts file:// URLs to absolute paths and
44
- // delegates to Node's require().
45
- // ---------------------------------------------------------------------------
46
- function installSSRWebpackShim() {
47
- if (typeof globalThis.__webpack_require__ === 'function')
48
- return;
49
- globalThis.__webpack_require__ = function ssrWebpackRequire(specifier) {
50
- let modulePath = specifier;
51
- // Convert file:// URLs to absolute paths
52
- if (specifier.startsWith('file://')) {
53
- try {
54
- modulePath = (0, url_1.fileURLToPath)(specifier);
55
- }
56
- catch {
57
- modulePath = specifier;
58
- }
59
- }
60
- return require(modulePath);
61
- };
62
- // Chunk loading is a no-op on the server — all code is already local.
63
- globalThis.__webpack_chunk_load__ = function ssrChunkLoad(_chunkId) {
64
- return Promise.resolve();
65
- };
66
- }
67
40
  const config_1 = require("../config");
68
41
  const dev_error_1 = require("../dev-error");
69
42
  const artifact_validator_1 = require("./artifact-validator");
@@ -81,6 +54,7 @@ const app_router_runtime_1 = require("./app-router-runtime");
81
54
  const fetch_policy_1 = require("./fetch-policy");
82
55
  const vista_import_map_1 = require("./vista-import-map");
83
56
  const project_alias_resolver_1 = require("./project-alias-resolver");
57
+ const app_dir_1 = require("./app-dir");
84
58
  // Support CSS imports on server runtime
85
59
  // - Regular .css: ignored (handled by PostCSS)
86
60
  // - .module.css: return empty class mapping (webpack build handles real mappings)
@@ -335,32 +309,7 @@ function cleanHotUpdateFiles(cwd) {
335
309
  }
336
310
  }
337
311
  function findChunkFiles(cwd, isDev) {
338
- const chunksDir = path_1.default.join(cwd, constants_1.BUILD_DIR, 'static', 'chunks');
339
- if (!fs_1.default.existsSync(chunksDir))
340
- return [];
341
- const files = fs_1.default
342
- .readdirSync(chunksDir)
343
- .filter((name) => name.endsWith('.js') && !name.endsWith('.map') && !name.includes('.hot-update.'));
344
- // In dev, avoid loading stale production artifacts left from a previous build.
345
- // Production chunks end with a hash suffix like `main-1a2b3c4d.js`.
346
- const normalizedFiles = isDev
347
- ? files.filter((name) => !/-[0-9a-f]{8,}\.js$/i.test(name))
348
- : files;
349
- // Load webpack runtime first, then framework, then the rest alphabetically.
350
- // This ensures the chunk registry (__webpack_require__) is available before
351
- // any deferred chunk tries to self-register.
352
- const priority = ['webpack.js', 'framework.js', 'vendor.js'];
353
- return normalizedFiles.sort((a, b) => {
354
- const ai = priority.indexOf(a);
355
- const bi = priority.indexOf(b);
356
- if (ai !== -1 && bi !== -1)
357
- return ai - bi;
358
- if (ai !== -1)
359
- return -1;
360
- if (bi !== -1)
361
- return 1;
362
- return a.localeCompare(b);
363
- });
312
+ return (0, hydration_chunks_1.listHydrationChunkFiles)(cwd, isDev);
364
313
  }
365
314
  function normalizeSSRManifest(manifest) {
366
315
  if (!manifest || !manifest.moduleMap) {
@@ -432,6 +381,46 @@ function loadSSRManifestFromDisk(absolutePath) {
432
381
  const manifest = (0, react_client_reference_manifest_1.normalizeReactServerConsumerManifest)(JSON.parse(fs_1.default.readFileSync(absolutePath, 'utf-8')));
433
382
  return normalizeSSRManifest(manifest);
434
383
  }
384
+ /** Stub `{}` manifests written before the first webpack emit are not usable for Flight SSR. */
385
+ function isSSRManifestReady(manifest) {
386
+ if (!manifest || !manifest.moduleMap)
387
+ return false;
388
+ return Object.keys(manifest.moduleMap).length > 0;
389
+ }
390
+ /**
391
+ * Tee an upstream Flight body: one copy for createFromNodeStream (SSR), one
392
+ * buffer for inline hydration bootstrap in the HTML response.
393
+ */
394
+ function teeFlightReadable(source) {
395
+ const decodeStream = new stream_1.PassThrough();
396
+ const chunks = [];
397
+ let resolveBuffer;
398
+ let rejectBuffer;
399
+ const bufferPromise = new Promise((resolve, reject) => {
400
+ resolveBuffer = resolve;
401
+ rejectBuffer = reject;
402
+ });
403
+ source.on('data', (chunk) => {
404
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
405
+ chunks.push(buf);
406
+ if (!decodeStream.write(buf)) {
407
+ source.pause();
408
+ decodeStream.once('drain', () => source.resume());
409
+ }
410
+ });
411
+ source.on('end', () => {
412
+ decodeStream.end();
413
+ resolveBuffer(Buffer.concat(chunks));
414
+ });
415
+ source.on('error', (error) => {
416
+ decodeStream.destroy(error);
417
+ rejectBuffer(error);
418
+ });
419
+ return { decodeStream, bufferPromise };
420
+ }
421
+ function buildInlineFlightBootstrapScript(flightText) {
422
+ return `<script>window.${constants_1.RSC_DATA_FLAG}=${JSON.stringify(flightText)};</script>`;
423
+ }
435
424
  function matchPattern(pathname, pattern) {
436
425
  const patternParts = pattern.split('/').filter(Boolean);
437
426
  const pathParts = pathname.split('/').filter(Boolean);
@@ -563,7 +552,7 @@ function applySegmentBoundaries(dir, element) {
563
552
  return wrappedElement;
564
553
  }
565
554
  async function renderAppSubtreeElement(input) {
566
- const appDir = path_1.default.join(input.cwd, 'app');
555
+ const appDir = (0, app_dir_1.resolveAppDir)(input.cwd);
567
556
  let element = await createRenderableRouteModuleElement(input.entryFilePath, {
568
557
  params: input.params,
569
558
  searchParams: input.searchParams,
@@ -573,9 +562,15 @@ async function renderAppSubtreeElement(input) {
573
562
  });
574
563
  const directoryChain = (0, app_router_runtime_1.resolveDirectoryChain)(input.subtreeRootDir, input.entryFilePath);
575
564
  for (let i = directoryChain.length - 1; i >= 0; i--) {
576
- const dir = directoryChain[i];
577
- element = applySegmentBoundaries(dir, element);
578
- const layoutPath = (0, app_router_runtime_1.resolveConventionModule)(dir, 'root') ?? (0, app_router_runtime_1.resolveConventionModule)(dir, 'layout');
565
+ element = applySegmentBoundaries(directoryChain[i], element);
566
+ }
567
+ const layoutPaths = input.layoutPaths && input.layoutPaths.length > 0
568
+ ? input.layoutPaths
569
+ : directoryChain
570
+ .map((dir) => (0, app_router_runtime_1.resolveConventionModule)(dir, 'root') ?? (0, app_router_runtime_1.resolveConventionModule)(dir, 'layout'))
571
+ .filter((layoutPath) => Boolean(layoutPath));
572
+ for (let i = layoutPaths.length - 1; i >= 0; i--) {
573
+ const layoutPath = layoutPaths[i];
579
574
  if (!layoutPath || path_1.default.resolve(layoutPath) === path_1.default.resolve(input.entryFilePath)) {
580
575
  continue;
581
576
  }
@@ -630,7 +625,7 @@ async function createRouteElement(route, context, isDev, rootLayout, runtimeRoot
630
625
  metadata = { ...metadata, ...dynamicMeta };
631
626
  }
632
627
  const element = await renderAppSubtreeElement({
633
- subtreeRootDir: path_1.default.join(runtimeRoot, 'app'),
628
+ subtreeRootDir: (0, app_dir_1.resolveAppDir)(runtimeRoot),
634
629
  entryFilePath: route.pagePath,
635
630
  pathname: req.path,
636
631
  params,
@@ -639,6 +634,7 @@ async function createRouteElement(route, context, isDev, rootLayout, runtimeRoot
639
634
  cwd: runtimeRoot,
640
635
  evaluateLeafMetadata: false,
641
636
  disableParallelSlots: options.disableParallelSlots,
637
+ layoutPaths: route.layoutPaths,
642
638
  });
643
639
  return { element, metadata, rootMode: rootLayout.mode };
644
640
  }
@@ -748,8 +744,8 @@ function spawnUpstream(cwd, runtimeRoot, upstreamPort) {
748
744
  // ---------------------------------------------------------------------------
749
745
  /**
750
746
  * Fetch the Flight stream from the upstream RSC process, decode it with
751
- * createFromNodeStream, and render the resulting React tree to an HTML stream
752
- * via renderToPipeableStream.
747
+ * createFromNodeStream, render HTML via renderToPipeableStream, and inline the
748
+ * raw Flight payload so the browser can hydrate without a second /rsc fetch.
753
749
  */
754
750
  async function renderFlightToHTMLStream(upstreamOrigin, pathname, search, metadataHtml, chunkFiles, rootMode, flightSSRClient, ssrManifest, res, isDev) {
755
751
  const flightUrl = `${upstreamOrigin}/rsc${pathname}${search ? `?${search}` : ''}`;
@@ -763,30 +759,20 @@ async function renderFlightToHTMLStream(upstreamOrigin, pathname, search, metada
763
759
  if (!upstream.body) {
764
760
  throw new Error('Upstream returned empty body');
765
761
  }
766
- // 2. Convert Web ReadableStream to Node.js Readable
762
+ // 2. Tee: decode stream for SSR + buffer for inline hydration bootstrap
767
763
  const nodeStream = stream_1.Readable.fromWeb(upstream.body);
768
- // 3. Decode Flight stream into a React tree
769
- const flightResponse = flightSSRClient.createFromNodeStream(nodeStream, ssrManifest);
770
- let element;
771
- // In dev, decode the flight payload before React.use() render path.
772
- // This keeps manifest mismatch errors in the request try/catch boundary
773
- // instead of crashing the whole process with an uncaught exception.
774
- if (isDev) {
775
- const resolvedTree = await flightResponse;
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
764
+ const { decodeStream, bufferPromise } = teeFlightReadable(nodeStream);
765
+ // 3. Decode Flight stream into a React tree (stream in both dev and prod)
766
+ const flightResponse = flightSSRClient.createFromNodeStream(decodeStream, ssrManifest);
767
+ function FlightRoot() {
768
+ return react_1.default.use(flightResponse);
769
+ }
770
+ const element = react_1.default.createElement(FlightRoot);
771
+ // 4. Build script tags for client chunks
786
772
  const scripts = chunkFiles
787
773
  .map((chunk) => `<script defer src="${constants_1.STATIC_CHUNKS_PATH}${chunk}"></script>`)
788
774
  .join('\n ');
789
- // 6. Render to a pipeable HTML stream
775
+ // 5. Render to a pipeable HTML stream; inject inline Flight before client scripts
790
776
  return new Promise((resolve, reject) => {
791
777
  let shellSent = false;
792
778
  const { pipe } = (0, server_1.renderToPipeableStream)(element, {
@@ -795,11 +781,10 @@ async function renderFlightToHTMLStream(upstreamOrigin, pathname, search, metada
795
781
  res.status(upstream.status === 404 ? 404 : 200);
796
782
  res.setHeader('Content-Type', 'text/html; charset=utf-8');
797
783
  res.setHeader('Transfer-Encoding', 'chunked');
798
- // Create a transform stream to inject head/body content
784
+ let heldTail = '';
799
785
  const transform = new stream_1.Transform({
800
786
  transform(chunk, _encoding, callback) {
801
787
  let html = chunk.toString();
802
- // Inject into <head> if present
803
788
  if (html.includes('</head>')) {
804
789
  const fontHtml = (0, registry_1.getAllFontHTML)();
805
790
  const headInjection = `
@@ -810,15 +795,29 @@ async function renderFlightToHTMLStream(upstreamOrigin, pathname, search, metada
810
795
  ${getCSSLinks()}`;
811
796
  html = html.replace('</head>', `${headInjection}\n</head>`);
812
797
  }
813
- // Inject scripts before </body>
814
- if (html.includes('</body>')) {
815
- const bodyInjection = `
816
- <script>window.${constants_1.HYDRATE_DOCUMENT_FLAG} = ${rootMode === 'document'};</script>
817
- ${scripts}`;
818
- html = html.replace('</body>', `${bodyInjection}\n</body>`);
798
+ // Hold </body>… so flush can inject Flight + scripts before it.
799
+ const bodyCloseIdx = html.indexOf('</body>');
800
+ if (bodyCloseIdx !== -1) {
801
+ const beforeBodyClose = html.slice(0, bodyCloseIdx);
802
+ heldTail = html.slice(bodyCloseIdx);
803
+ const hydrateFlag = `<script>window.${constants_1.HYDRATE_DOCUMENT_FLAG} = ${rootMode === 'document'};</script>`;
804
+ callback(null, `${beforeBodyClose}\n ${hydrateFlag}\n`);
805
+ return;
819
806
  }
820
807
  callback(null, html);
821
808
  },
809
+ flush(callback) {
810
+ bufferPromise
811
+ .then((buf) => {
812
+ const flightScript = buildInlineFlightBootstrapScript(buf.toString('utf8'));
813
+ // Flight first, then deferred client chunks, then </body></html>
814
+ this.push(`${flightScript}\n ${scripts}\n${heldTail || '</body></html>'}`);
815
+ callback();
816
+ })
817
+ .catch((error) => {
818
+ callback(error instanceof Error ? error : new Error(String(error)));
819
+ });
820
+ },
822
821
  });
823
822
  pipe(transform);
824
823
  transform.pipe(res);
@@ -927,6 +926,23 @@ function renderCompilePendingHTML() {
927
926
  </body>
928
927
  </html>`;
929
928
  }
929
+ function failRSCStartup(message, listen) {
930
+ console.error(message);
931
+ if (!listen) {
932
+ throw new Error(message);
933
+ }
934
+ process.exit(1);
935
+ }
936
+ let cachedRequestListener = null;
937
+ function createRSCApp(options = {}) {
938
+ return startRSCServer({ ...options, listen: false });
939
+ }
940
+ function getRSCRequestListener(options = {}) {
941
+ if (!cachedRequestListener) {
942
+ cachedRequestListener = createRSCApp(options);
943
+ }
944
+ return cachedRequestListener;
945
+ }
930
946
  function startRSCServer(options = {}) {
931
947
  const app = (0, express_1.default)();
932
948
  const cwd = path_1.default.resolve(options.projectRoot || process.env.VISTA_ARTIFACT_ROOT || process.cwd());
@@ -943,7 +959,8 @@ function startRSCServer(options = {}) {
943
959
  cleanHotUpdateFiles(cwd);
944
960
  // Request logger — logs GET/POST with timing
945
961
  app.use((0, logger_1.requestLogger)());
946
- const port = resolvePort(String(options.port || vistaConfig.server?.port || 3003), 3003);
962
+ const shouldListen = options.listen !== false;
963
+ const port = resolvePort(String(options.port || vistaConfig.server?.port || process.env.PORT || 3003), 3003);
947
964
  const upstreamPort = resolvePort(String(process.env.RSC_UPSTREAM_PORT || port + 1), port + 1);
948
965
  const upstreamOrigin = `http://127.0.0.1:${upstreamPort}`;
949
966
  installSingleReactResolution(runtimeRoot);
@@ -953,11 +970,10 @@ function startRSCServer(options = {}) {
953
970
  cacheComponentsEnabled: cacheComponentsConfig.enabled,
954
971
  });
955
972
  (0, fetch_policy_1.installSegmentFetchPolicyShim)();
956
- installSSRWebpackShim();
973
+ (0, ssr_webpack_shim_1.installSSRWebpackShim)();
957
974
  const serverManifestPath = path_1.default.join(cwd, constants_1.BUILD_DIR, 'server', 'server-manifest.json');
958
975
  if (!fs_1.default.existsSync(serverManifestPath)) {
959
- console.error(`[vista:rsc] Missing server manifest at ${serverManifestPath}. Run "vista build --rsc" first.`);
960
- process.exit(1);
976
+ failRSCStartup(`[vista:rsc] Missing server manifest at ${serverManifestPath}. Run "vista build --rsc" first.`, shouldListen);
961
977
  }
962
978
  try {
963
979
  if (!isDev || !options.compiler) {
@@ -965,8 +981,7 @@ function startRSCServer(options = {}) {
965
981
  }
966
982
  }
967
983
  catch (error) {
968
- console.error(error.message);
969
- process.exit(1);
984
+ failRSCStartup(error.message, shouldListen);
970
985
  }
971
986
  // ========================================================================
972
987
  // Flight SSR Client + SSR Manifest
@@ -978,7 +993,8 @@ function startRSCServer(options = {}) {
978
993
  flightSSRClient = require(flightClientPath);
979
994
  }
980
995
  catch (err) {
981
- // Flight SSR not availablefallback to renderToString
996
+ // Flight SSR client missingrequests will fail closed (no renderToString).
997
+ (0, logger_1.logError)(`[vista:rsc] react-server-dom-webpack/client.node unavailable: ${err?.message || String(err)}`);
982
998
  }
983
999
  const ssrManifestPath = path_1.default.join(cwd, constants_1.BUILD_DIR, 'react-server-manifest.json');
984
1000
  const ssrManifestLegacyPath = path_1.default.join(cwd, constants_1.BUILD_DIR, 'react-ssr-manifest.json');
@@ -988,13 +1004,21 @@ function startRSCServer(options = {}) {
988
1004
  ? ssrManifestLegacyPath
989
1005
  : null;
990
1006
  if (resolvedSSRManifestPath) {
991
- ssrManifest = loadSSRManifestFromDisk(resolvedSSRManifestPath);
1007
+ try {
1008
+ ssrManifest = loadSSRManifestFromDisk(resolvedSSRManifestPath);
1009
+ }
1010
+ catch {
1011
+ ssrManifest = null;
1012
+ }
1013
+ }
1014
+ if (ssrManifest && !isSSRManifestReady(ssrManifest)) {
1015
+ // Stub manifests written before the first webpack emit must not drive SSR.
1016
+ ssrManifest = null;
992
1017
  }
993
- else if (flightSSRClient) {
994
- // Can't use Flight SSR without the manifest
1018
+ if (!resolvedSSRManifestPath && flightSSRClient) {
995
1019
  flightSSRClient = null;
996
1020
  }
997
- const useFlightSSR = !!flightSSRClient && !!ssrManifest;
1021
+ let useFlightSSR = !!flightSSRClient && isSSRManifestReady(ssrManifest);
998
1022
  // ========================================================================
999
1023
  // Structure Validation (dev + strict-block)
1000
1024
  // ========================================================================
@@ -1098,11 +1122,15 @@ function startRSCServer(options = {}) {
1098
1122
  httpServer.close();
1099
1123
  }
1100
1124
  // 6. Force exit after brief grace period (Windows Ctrl+C fix)
1101
- setTimeout(() => process.exit(0), 500);
1125
+ if (shouldListen) {
1126
+ setTimeout(() => process.exit(0), 500);
1127
+ }
1102
1128
  };
1103
- process.on('SIGINT', shutdown);
1104
- process.on('SIGTERM', shutdown);
1105
- process.on('exit', shutdown);
1129
+ if (shouldListen) {
1130
+ process.on('SIGINT', shutdown);
1131
+ process.on('SIGTERM', shutdown);
1132
+ process.on('exit', shutdown);
1133
+ }
1106
1134
  // ========================================================================
1107
1135
  // Live-Reload SSE for RSC dev mode
1108
1136
  // - Pushes reload events when server components change (fs.watch)
@@ -1134,6 +1162,7 @@ function startRSCServer(options = {}) {
1134
1162
  'lib',
1135
1163
  'ctx',
1136
1164
  'data',
1165
+ 'src',
1137
1166
  'middleware.ts',
1138
1167
  'vista.config.ts',
1139
1168
  'content-collections.ts',
@@ -1228,9 +1257,18 @@ function startRSCServer(options = {}) {
1228
1257
  if (fs_1.default.existsSync(serverManifestPath)) {
1229
1258
  serverManifest = JSON.parse(fs_1.default.readFileSync(serverManifestPath, 'utf-8'));
1230
1259
  }
1231
- // Reload SSR manifest on rebuild too
1260
+ // Reload SSR manifest on rebuild; ignore stub `{}` until webpack writes a real map.
1232
1261
  if (resolvedSSRManifestPath && fs_1.default.existsSync(resolvedSSRManifestPath)) {
1233
- ssrManifest = loadSSRManifestFromDisk(resolvedSSRManifestPath);
1262
+ try {
1263
+ const nextManifest = loadSSRManifestFromDisk(resolvedSSRManifestPath);
1264
+ if (isSSRManifestReady(nextManifest)) {
1265
+ ssrManifest = nextManifest;
1266
+ useFlightSSR = !!flightSSRClient;
1267
+ }
1268
+ }
1269
+ catch {
1270
+ // Manifest may be mid-write during compilation.
1271
+ }
1234
1272
  }
1235
1273
  });
1236
1274
  }
@@ -1503,28 +1541,50 @@ function startRSCServer(options = {}) {
1503
1541
  return;
1504
1542
  }
1505
1543
  // ==================================================================
1506
- // Flight-Based SSR Path
1544
+ // Flight-Based SSR Path (fail-closed — no renderToString page HTML)
1507
1545
  // ==================================================================
1508
- // If the Flight SSR client + SSR manifest are available, render pages
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) {
1546
+ if (isDev && resolvedSSRManifestPath && fs_1.default.existsSync(resolvedSSRManifestPath)) {
1514
1547
  try {
1515
- if (isDev && resolvedSSRManifestPath && fs_1.default.existsSync(resolvedSSRManifestPath)) {
1516
- try {
1517
- ssrManifest = loadSSRManifestFromDisk(resolvedSSRManifestPath);
1518
- }
1519
- catch {
1520
- // Manifest may be mid-write during compilation; keep the last good in-memory copy.
1521
- }
1548
+ const nextManifest = loadSSRManifestFromDisk(resolvedSSRManifestPath);
1549
+ if (isSSRManifestReady(nextManifest)) {
1550
+ ssrManifest = nextManifest;
1551
+ useFlightSSR = !!flightSSRClient;
1522
1552
  }
1523
- // Metadata extraction: still done locally so we have <head> content
1524
- const rootLayout = (0, root_resolver_1.resolveRootLayout)(runtimeRoot, isDev);
1525
- const route = currentRoute;
1526
- let metadataHtml = '';
1527
- if (route) {
1553
+ }
1554
+ catch {
1555
+ // Manifest may be mid-write during compilation; keep the last good in-memory copy.
1556
+ }
1557
+ }
1558
+ if (!useFlightSSR || !flightSSRClient || !isSSRManifestReady(ssrManifest)) {
1559
+ if (isDev && options.compiler) {
1560
+ // Wait for a non-empty SSR manifest after the first webpack emit.
1561
+ res.status(503).type('text/html').send(renderCompilePendingHTML());
1562
+ return;
1563
+ }
1564
+ const message = 'Flight SSR is unavailable. A usable react-server-manifest.json is required; Vista no longer falls back to renderToString.';
1565
+ if (isDev) {
1566
+ res.status(500).send((0, dev_error_1.renderErrorHTML)([
1567
+ {
1568
+ type: 'build',
1569
+ source: 'server',
1570
+ message,
1571
+ },
1572
+ ]));
1573
+ }
1574
+ else {
1575
+ res.status(500).type('text/plain').send(message);
1576
+ }
1577
+ return;
1578
+ }
1579
+ try {
1580
+ // Metadata extraction: still done locally so we have <head> content.
1581
+ // notFound() here must not skip Flight — upstream renders the nearest
1582
+ // segment not-found as a 404 Flight payload.
1583
+ const rootLayout = (0, root_resolver_1.resolveRootLayout)(runtimeRoot, isDev);
1584
+ const route = currentRoute;
1585
+ let metadataHtml = '';
1586
+ if (route) {
1587
+ try {
1528
1588
  if (isDev) {
1529
1589
  clearProjectRequireCache(runtimeRoot);
1530
1590
  }
@@ -1542,173 +1602,49 @@ function startRSCServer(options = {}) {
1542
1602
  const { generateMetadataHtml } = require('../metadata/generate');
1543
1603
  metadataHtml = metadata ? generateMetadataHtml(metadata) : '';
1544
1604
  }
1545
- // Render the page via Flight stream → SSR
1546
- await renderFlightToHTMLStream(upstreamOrigin, req.path, req.query ? new URLSearchParams(req.query).toString() : '', metadataHtml, findChunkFiles(cwd, isDev), rootLayout.mode, flightSSRClient, ssrManifest, res, isDev);
1547
- return;
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();
1605
+ catch (metadataError) {
1606
+ if (metadataError?.name !== 'NotFoundError') {
1607
+ throw metadataError;
1598
1608
  }
1599
- catch {
1600
- res.end();
1601
- }
1602
- return;
1603
1609
  }
1604
1610
  }
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());
1611
+ await renderFlightToHTMLStream(upstreamOrigin, req.path, req.query ? new URLSearchParams(req.query).toString() : '', metadataHtml, findChunkFiles(cwd, isDev), rootLayout.mode, flightSSRClient, ssrManifest, res, isDev);
1619
1612
  return;
1620
1613
  }
1621
- try {
1622
- const rootLayout = (0, root_resolver_1.resolveRootLayout)(runtimeRoot, isDev);
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
- }
1614
+ catch (flightError) {
1615
+ if (flightError?.name === 'NotFoundError' && !res.headersSent) {
1640
1616
  res.status(404).type('text/html').send((0, not_found_page_1.getStyledNotFoundHTML)());
1641
1617
  return;
1642
1618
  }
1643
- const params = extractParams(req.path, route);
1644
- const searchParams = Object.fromEntries(new URLSearchParams(req.query).entries());
1645
- const { element, metadata, rootMode } = await createRouteElement(route, { params, searchParams, req }, isDev, rootLayout, runtimeRoot);
1646
- const appHtml = (0, server_1.renderToString)(element);
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;
1619
+ console.error('[vista:rsc] Flight SSR failed:', flightError.message);
1620
+ if (!res.headersSent) {
1621
+ if (isDev) {
1622
+ res.status(500).send((0, dev_error_1.renderErrorHTML)([(0, dev_error_1.fromCaughtError)(flightError, { source: 'server' })]));
1697
1623
  }
1698
- catch (notFoundError) {
1699
- console.error('[vista:rsc] Failed to render NotFoundError fallback:', notFoundError);
1624
+ else {
1625
+ res.status(500).send('<h1>Internal Server Error</h1>');
1700
1626
  }
1627
+ return;
1701
1628
  }
1702
- console.error('[vista:rsc] Render error:', error);
1703
- if (isDev) {
1704
- res.status(500).send((0, dev_error_1.renderErrorHTML)([(0, dev_error_1.fromCaughtError)(error, { source: 'server' })]));
1705
- }
1706
- else {
1707
- res.status(500).send('<h1>Internal Server Error</h1>');
1629
+ if (isDev && res.headersSent) {
1630
+ try {
1631
+ const errMsg = (flightError.message || 'Flight SSR Error')
1632
+ .replace(/'/g, "\\'")
1633
+ .replace(/\n/g, '\\n');
1634
+ 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>`);
1635
+ res.end();
1636
+ }
1637
+ catch {
1638
+ res.end();
1639
+ }
1708
1640
  }
1641
+ return;
1709
1642
  }
1710
1643
  });
1711
1644
  });
1645
+ if (!shouldListen) {
1646
+ return app;
1647
+ }
1712
1648
  const server = app.listen(port, () => {
1713
1649
  (0, logger_1.printServerReady)({ port, mode: 'rsc', rscFlight: useFlightSSR });
1714
1650
  });
@@ -1722,4 +1658,5 @@ function startRSCServer(options = {}) {
1722
1658
  (0, logger_1.logError)(`RSC startup failed: ${error?.message || String(error)}`);
1723
1659
  process.exit(1);
1724
1660
  });
1661
+ return app;
1725
1662
  }