@webtypen/webframez-react 0.0.4 → 0.0.6

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/dist/http.cjs CHANGED
@@ -478,57 +478,19 @@ function parseSearchParams(query) {
478
478
  }
479
479
 
480
480
  // src/http.ts
481
- function normalizeBasePath(basePath) {
482
- if (!basePath || basePath === "/") {
483
- return "";
484
- }
485
- const withLeadingSlash = basePath.startsWith("/") ? basePath : `/${basePath}`;
486
- return withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
487
- }
488
- function stripBasePath(pathname, basePath) {
489
- if (!basePath) {
490
- return pathname;
491
- }
492
- if (pathname === basePath) {
493
- return "/";
494
- }
495
- if (pathname.startsWith(`${basePath}/`)) {
496
- return pathname.slice(basePath.length) || "/";
497
- }
498
- return pathname;
499
- }
500
- function runNodeCommand(args) {
501
- return new Promise((resolve, reject) => {
502
- const execArgs = [
503
- "--conditions",
504
- "react-server",
505
- "-r",
506
- "@webtypen/webframez-react/register",
507
- ...args
508
- ];
509
- (0, import_node_child_process.execFile)(
510
- process.execPath,
511
- execArgs,
512
- { timeout: 1e4, maxBuffer: 1024 * 1024 * 5 },
513
- (error, stdout, stderr) => {
514
- if (error) {
515
- const out = stderr && stderr.trim() !== "" ? stderr : stdout;
516
- reject(new Error(out || error.message));
517
- return;
518
- }
519
- resolve({ stdout, stderr });
520
- }
521
- );
522
- });
481
+ function createInitialHtmlErrorMarkup(message) {
482
+ return `<main style="font-family:system-ui,sans-serif;padding:24px"><h1 style="margin:0 0 12px">500</h1><p style="margin:0">${message}</p></main>`;
523
483
  }
524
- async function renderInitialHtmlInWorker(options) {
525
- const payload = Buffer.from(JSON.stringify(options), "utf8").toString("base64url");
526
- const script = `
484
+ var INITIAL_HTML_WORKER_SCRIPT = `
527
485
  const path = require("node:path");
528
486
  const Module = require("node:module");
529
- const input = JSON.parse(Buffer.from(process.argv[1], "base64url").toString("utf8"));
530
- globalThis.__RSC_BASENAME = input.basename || "";
531
- const appRequire = Module.createRequire(path.join(input.pagesDir, "__webframez_react_worker__.js"));
487
+
488
+ const pagesDir = process.env.WEBFRAMEZ_REACT_PAGES_DIR || "";
489
+ if (!pagesDir) {
490
+ throw new Error("Missing WEBFRAMEZ_REACT_PAGES_DIR");
491
+ }
492
+
493
+ const appRequire = Module.createRequire(path.join(pagesDir, "__webframez_react_worker__.js"));
532
494
  const originalResolveFilename = Module._resolveFilename;
533
495
  const forcedResolutions = new Map([
534
496
  ["react", appRequire.resolve("react")],
@@ -536,35 +498,181 @@ const forcedResolutions = new Map([
536
498
  ["react/jsx-dev-runtime", appRequire.resolve("react/jsx-dev-runtime")],
537
499
  ["react-dom/client", appRequire.resolve("react-dom/client")]
538
500
  ]);
501
+
539
502
  Module._resolveFilename = function(request, parent, isMain, options) {
540
503
  if (forcedResolutions.has(request)) {
541
504
  return forcedResolutions.get(request);
542
505
  }
543
506
  return originalResolveFilename.call(this, request, parent, isMain, options);
544
507
  };
508
+
545
509
  const { createFileRouter } = require("@webtypen/webframez-react/router");
546
510
  const reactDomPkg = require.resolve("react-dom/package.json", {
547
- paths: [process.cwd(), input.pagesDir]
511
+ paths: [process.cwd(), pagesDir]
548
512
  });
549
513
  const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
514
+ const router = createFileRouter({ pagesDir });
550
515
 
551
- (async () => {
552
- const router = createFileRouter({ pagesDir: input.pagesDir });
553
- const resolved = await router.resolve({
554
- pathname: input.pathname,
555
- searchParams: input.searchParams || {},
556
- cookies: input.cookies || {},
557
- });
558
- const html = reactDomServer.renderToString(resolved.model);
559
- process.stdout.write(JSON.stringify({ html }));
560
- })().catch((error) => {
561
- process.stderr.write(error && (error.stack || String(error)) ? (error.stack || String(error)) : "Unknown SSR worker error");
562
- process.exit(1);
516
+ process.on("message", async (message) => {
517
+ if (!message || message.type !== "render") {
518
+ return;
519
+ }
520
+
521
+ const previousBasename = globalThis.__RSC_BASENAME;
522
+ globalThis.__RSC_BASENAME = message.payload.basename || "";
523
+
524
+ try {
525
+ const resolved = await router.resolve({
526
+ pathname: message.payload.pathname,
527
+ searchParams: message.payload.searchParams || {},
528
+ cookies: message.payload.cookies || {},
529
+ });
530
+ const html = reactDomServer.renderToString(resolved.model);
531
+ if (typeof process.send === "function") {
532
+ process.send({ id: message.id, ok: true, html });
533
+ }
534
+ } catch (error) {
535
+ const formatted = error && (error.stack || String(error))
536
+ ? (error.stack || String(error))
537
+ : "Unknown SSR worker error";
538
+ if (typeof process.send === "function") {
539
+ process.send({ id: message.id, ok: false, error: formatted });
540
+ }
541
+ } finally {
542
+ globalThis.__RSC_BASENAME = previousBasename;
543
+ }
563
544
  });
564
545
  `;
565
- const { stdout } = await runNodeCommand(["-e", script, payload]);
566
- const parsed = JSON.parse(stdout || "{}");
567
- return parsed.html || "";
546
+ function normalizeBasePath(basePath) {
547
+ if (!basePath || basePath === "/") {
548
+ return "";
549
+ }
550
+ const withLeadingSlash = basePath.startsWith("/") ? basePath : `/${basePath}`;
551
+ return withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
552
+ }
553
+ function stripBasePath(pathname, basePath) {
554
+ if (!basePath) {
555
+ return pathname;
556
+ }
557
+ if (pathname === basePath) {
558
+ return "/";
559
+ }
560
+ if (pathname.startsWith(`${basePath}/`)) {
561
+ return pathname.slice(basePath.length) || "/";
562
+ }
563
+ return pathname;
564
+ }
565
+ function sanitizeInitialHtmlWorkerNodeOptions(rawNodeOptions) {
566
+ if (!rawNodeOptions || rawNodeOptions.trim() === "") {
567
+ return "";
568
+ }
569
+ return rawNodeOptions.replace(/(^|\s)--conditions\s+react-server(?=\s|$)/g, " ").replace(/(^|\s)-r\s+(\S*webframez-react\/register)(?=\s|$)/g, " ").replace(/\s+/g, " ").trim();
570
+ }
571
+ function createInitialHtmlWorker(pagesDir) {
572
+ let child = null;
573
+ let nextRequestId = 1;
574
+ let stderrBuffer = "";
575
+ const pending = /* @__PURE__ */ new Map();
576
+ const rejectPending = (error) => {
577
+ for (const entry of pending.values()) {
578
+ clearTimeout(entry.timeout);
579
+ entry.reject(error);
580
+ }
581
+ pending.clear();
582
+ };
583
+ const stopWorker = () => {
584
+ if (!child) {
585
+ return;
586
+ }
587
+ child.removeAllListeners();
588
+ if (!child.killed) {
589
+ child.kill();
590
+ }
591
+ child = null;
592
+ };
593
+ const startWorker = () => {
594
+ if (child && child.connected && !child.killed) {
595
+ return child;
596
+ }
597
+ stderrBuffer = "";
598
+ child = (0, import_node_child_process.spawn)(process.execPath, ["-e", INITIAL_HTML_WORKER_SCRIPT], {
599
+ cwd: process.cwd(),
600
+ env: {
601
+ ...process.env,
602
+ NODE_OPTIONS: sanitizeInitialHtmlWorkerNodeOptions(process.env.NODE_OPTIONS),
603
+ WEBFRAMEZ_REACT_PAGES_DIR: pagesDir
604
+ },
605
+ stdio: ["ignore", "ignore", "pipe", "ipc"]
606
+ });
607
+ child.on("message", (message) => {
608
+ if (!message || typeof message.id !== "number") {
609
+ return;
610
+ }
611
+ const entry = pending.get(message.id);
612
+ if (!entry) {
613
+ return;
614
+ }
615
+ pending.delete(message.id);
616
+ clearTimeout(entry.timeout);
617
+ if (message.ok) {
618
+ entry.resolve(message.html);
619
+ return;
620
+ }
621
+ entry.reject(new Error(message.error));
622
+ });
623
+ child.stderr?.on("data", (chunk) => {
624
+ stderrBuffer = `${stderrBuffer}${chunk.toString("utf8")}`.slice(-8192);
625
+ });
626
+ child.on("exit", (code, signal) => {
627
+ const suffix = stderrBuffer.trim() !== "" ? `
628
+ ${stderrBuffer.trim()}` : "";
629
+ rejectPending(
630
+ new Error(
631
+ `[webframez-react] Initial HTML worker exited (${signal ?? code ?? "unknown"})${suffix}`
632
+ )
633
+ );
634
+ child = null;
635
+ });
636
+ child.on("error", (error) => {
637
+ rejectPending(error instanceof Error ? error : new Error(String(error)));
638
+ child = null;
639
+ });
640
+ return child;
641
+ };
642
+ return {
643
+ render(payload) {
644
+ const activeChild = startWorker();
645
+ const requestId = nextRequestId++;
646
+ return new Promise((resolve, reject) => {
647
+ const timeout = setTimeout(() => {
648
+ rejectPending(new Error("[webframez-react] Initial HTML worker timed out"));
649
+ stopWorker();
650
+ }, 1e4);
651
+ pending.set(requestId, { resolve, reject, timeout });
652
+ const request = {
653
+ id: requestId,
654
+ type: "render",
655
+ payload
656
+ };
657
+ activeChild.send(request, (error) => {
658
+ if (!error) {
659
+ return;
660
+ }
661
+ const entry = pending.get(requestId);
662
+ if (!entry) {
663
+ return;
664
+ }
665
+ pending.delete(requestId);
666
+ clearTimeout(entry.timeout);
667
+ entry.reject(error instanceof Error ? error : new Error(String(error)));
668
+ });
669
+ });
670
+ },
671
+ dispose() {
672
+ rejectPending(new Error("[webframez-react] Initial HTML worker disposed"));
673
+ stopWorker();
674
+ }
675
+ };
568
676
  }
569
677
  function withRequestBasename(basename, fn) {
570
678
  const target = globalThis;
@@ -624,6 +732,13 @@ function createNodeRequestHandler(options) {
624
732
  const liveReloadClients = /* @__PURE__ */ new Set();
625
733
  const router = createFileRouter({ pagesDir });
626
734
  const moduleMap = JSON.parse(import_node_fs2.default.readFileSync(manifestPath, "utf-8"));
735
+ const initialHtmlWorker = createInitialHtmlWorker(pagesDir);
736
+ const disposeInitialHtmlWorker = () => {
737
+ initialHtmlWorker.dispose();
738
+ };
739
+ process.once("exit", disposeInitialHtmlWorker);
740
+ process.once("SIGINT", disposeInitialHtmlWorker);
741
+ process.once("SIGTERM", disposeInitialHtmlWorker);
627
742
  return async function handleRequest(req, res) {
628
743
  if (!req.url) {
629
744
  res.statusCode = 400;
@@ -723,8 +838,7 @@ function createNodeRequestHandler(options) {
723
838
  );
724
839
  let rootHtml = "";
725
840
  try {
726
- rootHtml = await renderInitialHtmlInWorker({
727
- pagesDir,
841
+ rootHtml = await initialHtmlWorker.render({
728
842
  pathname: stripBasePath(url.pathname, basePath),
729
843
  searchParams: parseSearchParams(url.searchParams),
730
844
  cookies: requestCookies,
@@ -732,6 +846,32 @@ function createNodeRequestHandler(options) {
732
846
  });
733
847
  } catch (error) {
734
848
  console.error("[webframez-react] Failed to render initial HTML", error);
849
+ try {
850
+ initialHtmlWorker.dispose();
851
+ rootHtml = await initialHtmlWorker.render({
852
+ pathname: stripBasePath(url.pathname, basePath),
853
+ searchParams: parseSearchParams(url.searchParams),
854
+ cookies: requestCookies,
855
+ basename: basePath
856
+ });
857
+ } catch (retryError) {
858
+ console.error("[webframez-react] Retry for initial HTML failed", retryError);
859
+ res.statusCode = 500;
860
+ res.setHeader("Content-Type", "text/html");
861
+ res.end(
862
+ createHTMLShell({
863
+ title: "500 - Initial HTML render failed",
864
+ headTags: "",
865
+ clientScriptUrl,
866
+ rscEndpoint: rscPath,
867
+ rootHtml: createInitialHtmlErrorMarkup("Initial HTML render failed."),
868
+ basename: basePath,
869
+ liveReloadPath: liveReloadPath || void 0,
870
+ liveReloadServerId: liveReloadPath ? devServerId : void 0
871
+ })
872
+ );
873
+ return;
874
+ }
735
875
  }
736
876
  res.statusCode = resolved.statusCode;
737
877
  res.setHeader("Content-Type", "text/html");
package/dist/http.js CHANGED
@@ -9,7 +9,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
9
9
  // src/http.ts
10
10
  import fs2 from "node:fs";
11
11
  import path2 from "node:path";
12
- import { execFile } from "node:child_process";
12
+ import { spawn } from "node:child_process";
13
13
 
14
14
  // src/server.ts
15
15
  import { renderToPipeableStream } from "react-server-dom-webpack/server";
@@ -453,57 +453,19 @@ function parseSearchParams(query) {
453
453
  }
454
454
 
455
455
  // src/http.ts
456
- function normalizeBasePath(basePath) {
457
- if (!basePath || basePath === "/") {
458
- return "";
459
- }
460
- const withLeadingSlash = basePath.startsWith("/") ? basePath : `/${basePath}`;
461
- return withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
462
- }
463
- function stripBasePath(pathname, basePath) {
464
- if (!basePath) {
465
- return pathname;
466
- }
467
- if (pathname === basePath) {
468
- return "/";
469
- }
470
- if (pathname.startsWith(`${basePath}/`)) {
471
- return pathname.slice(basePath.length) || "/";
472
- }
473
- return pathname;
474
- }
475
- function runNodeCommand(args) {
476
- return new Promise((resolve, reject) => {
477
- const execArgs = [
478
- "--conditions",
479
- "react-server",
480
- "-r",
481
- "@webtypen/webframez-react/register",
482
- ...args
483
- ];
484
- execFile(
485
- process.execPath,
486
- execArgs,
487
- { timeout: 1e4, maxBuffer: 1024 * 1024 * 5 },
488
- (error, stdout, stderr) => {
489
- if (error) {
490
- const out = stderr && stderr.trim() !== "" ? stderr : stdout;
491
- reject(new Error(out || error.message));
492
- return;
493
- }
494
- resolve({ stdout, stderr });
495
- }
496
- );
497
- });
456
+ function createInitialHtmlErrorMarkup(message) {
457
+ return `<main style="font-family:system-ui,sans-serif;padding:24px"><h1 style="margin:0 0 12px">500</h1><p style="margin:0">${message}</p></main>`;
498
458
  }
499
- async function renderInitialHtmlInWorker(options) {
500
- const payload = Buffer.from(JSON.stringify(options), "utf8").toString("base64url");
501
- const script = `
459
+ var INITIAL_HTML_WORKER_SCRIPT = `
502
460
  const path = require("node:path");
503
461
  const Module = require("node:module");
504
- const input = JSON.parse(Buffer.from(process.argv[1], "base64url").toString("utf8"));
505
- globalThis.__RSC_BASENAME = input.basename || "";
506
- const appRequire = Module.createRequire(path.join(input.pagesDir, "__webframez_react_worker__.js"));
462
+
463
+ const pagesDir = process.env.WEBFRAMEZ_REACT_PAGES_DIR || "";
464
+ if (!pagesDir) {
465
+ throw new Error("Missing WEBFRAMEZ_REACT_PAGES_DIR");
466
+ }
467
+
468
+ const appRequire = Module.createRequire(path.join(pagesDir, "__webframez_react_worker__.js"));
507
469
  const originalResolveFilename = Module._resolveFilename;
508
470
  const forcedResolutions = new Map([
509
471
  ["react", appRequire.resolve("react")],
@@ -511,35 +473,181 @@ const forcedResolutions = new Map([
511
473
  ["react/jsx-dev-runtime", appRequire.resolve("react/jsx-dev-runtime")],
512
474
  ["react-dom/client", appRequire.resolve("react-dom/client")]
513
475
  ]);
476
+
514
477
  Module._resolveFilename = function(request, parent, isMain, options) {
515
478
  if (forcedResolutions.has(request)) {
516
479
  return forcedResolutions.get(request);
517
480
  }
518
481
  return originalResolveFilename.call(this, request, parent, isMain, options);
519
482
  };
483
+
520
484
  const { createFileRouter } = require("@webtypen/webframez-react/router");
521
485
  const reactDomPkg = require.resolve("react-dom/package.json", {
522
- paths: [process.cwd(), input.pagesDir]
486
+ paths: [process.cwd(), pagesDir]
523
487
  });
524
488
  const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
489
+ const router = createFileRouter({ pagesDir });
525
490
 
526
- (async () => {
527
- const router = createFileRouter({ pagesDir: input.pagesDir });
528
- const resolved = await router.resolve({
529
- pathname: input.pathname,
530
- searchParams: input.searchParams || {},
531
- cookies: input.cookies || {},
532
- });
533
- const html = reactDomServer.renderToString(resolved.model);
534
- process.stdout.write(JSON.stringify({ html }));
535
- })().catch((error) => {
536
- process.stderr.write(error && (error.stack || String(error)) ? (error.stack || String(error)) : "Unknown SSR worker error");
537
- process.exit(1);
491
+ process.on("message", async (message) => {
492
+ if (!message || message.type !== "render") {
493
+ return;
494
+ }
495
+
496
+ const previousBasename = globalThis.__RSC_BASENAME;
497
+ globalThis.__RSC_BASENAME = message.payload.basename || "";
498
+
499
+ try {
500
+ const resolved = await router.resolve({
501
+ pathname: message.payload.pathname,
502
+ searchParams: message.payload.searchParams || {},
503
+ cookies: message.payload.cookies || {},
504
+ });
505
+ const html = reactDomServer.renderToString(resolved.model);
506
+ if (typeof process.send === "function") {
507
+ process.send({ id: message.id, ok: true, html });
508
+ }
509
+ } catch (error) {
510
+ const formatted = error && (error.stack || String(error))
511
+ ? (error.stack || String(error))
512
+ : "Unknown SSR worker error";
513
+ if (typeof process.send === "function") {
514
+ process.send({ id: message.id, ok: false, error: formatted });
515
+ }
516
+ } finally {
517
+ globalThis.__RSC_BASENAME = previousBasename;
518
+ }
538
519
  });
539
520
  `;
540
- const { stdout } = await runNodeCommand(["-e", script, payload]);
541
- const parsed = JSON.parse(stdout || "{}");
542
- return parsed.html || "";
521
+ function normalizeBasePath(basePath) {
522
+ if (!basePath || basePath === "/") {
523
+ return "";
524
+ }
525
+ const withLeadingSlash = basePath.startsWith("/") ? basePath : `/${basePath}`;
526
+ return withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
527
+ }
528
+ function stripBasePath(pathname, basePath) {
529
+ if (!basePath) {
530
+ return pathname;
531
+ }
532
+ if (pathname === basePath) {
533
+ return "/";
534
+ }
535
+ if (pathname.startsWith(`${basePath}/`)) {
536
+ return pathname.slice(basePath.length) || "/";
537
+ }
538
+ return pathname;
539
+ }
540
+ function sanitizeInitialHtmlWorkerNodeOptions(rawNodeOptions) {
541
+ if (!rawNodeOptions || rawNodeOptions.trim() === "") {
542
+ return "";
543
+ }
544
+ return rawNodeOptions.replace(/(^|\s)--conditions\s+react-server(?=\s|$)/g, " ").replace(/(^|\s)-r\s+(\S*webframez-react\/register)(?=\s|$)/g, " ").replace(/\s+/g, " ").trim();
545
+ }
546
+ function createInitialHtmlWorker(pagesDir) {
547
+ let child = null;
548
+ let nextRequestId = 1;
549
+ let stderrBuffer = "";
550
+ const pending = /* @__PURE__ */ new Map();
551
+ const rejectPending = (error) => {
552
+ for (const entry of pending.values()) {
553
+ clearTimeout(entry.timeout);
554
+ entry.reject(error);
555
+ }
556
+ pending.clear();
557
+ };
558
+ const stopWorker = () => {
559
+ if (!child) {
560
+ return;
561
+ }
562
+ child.removeAllListeners();
563
+ if (!child.killed) {
564
+ child.kill();
565
+ }
566
+ child = null;
567
+ };
568
+ const startWorker = () => {
569
+ if (child && child.connected && !child.killed) {
570
+ return child;
571
+ }
572
+ stderrBuffer = "";
573
+ child = spawn(process.execPath, ["-e", INITIAL_HTML_WORKER_SCRIPT], {
574
+ cwd: process.cwd(),
575
+ env: {
576
+ ...process.env,
577
+ NODE_OPTIONS: sanitizeInitialHtmlWorkerNodeOptions(process.env.NODE_OPTIONS),
578
+ WEBFRAMEZ_REACT_PAGES_DIR: pagesDir
579
+ },
580
+ stdio: ["ignore", "ignore", "pipe", "ipc"]
581
+ });
582
+ child.on("message", (message) => {
583
+ if (!message || typeof message.id !== "number") {
584
+ return;
585
+ }
586
+ const entry = pending.get(message.id);
587
+ if (!entry) {
588
+ return;
589
+ }
590
+ pending.delete(message.id);
591
+ clearTimeout(entry.timeout);
592
+ if (message.ok) {
593
+ entry.resolve(message.html);
594
+ return;
595
+ }
596
+ entry.reject(new Error(message.error));
597
+ });
598
+ child.stderr?.on("data", (chunk) => {
599
+ stderrBuffer = `${stderrBuffer}${chunk.toString("utf8")}`.slice(-8192);
600
+ });
601
+ child.on("exit", (code, signal) => {
602
+ const suffix = stderrBuffer.trim() !== "" ? `
603
+ ${stderrBuffer.trim()}` : "";
604
+ rejectPending(
605
+ new Error(
606
+ `[webframez-react] Initial HTML worker exited (${signal ?? code ?? "unknown"})${suffix}`
607
+ )
608
+ );
609
+ child = null;
610
+ });
611
+ child.on("error", (error) => {
612
+ rejectPending(error instanceof Error ? error : new Error(String(error)));
613
+ child = null;
614
+ });
615
+ return child;
616
+ };
617
+ return {
618
+ render(payload) {
619
+ const activeChild = startWorker();
620
+ const requestId = nextRequestId++;
621
+ return new Promise((resolve, reject) => {
622
+ const timeout = setTimeout(() => {
623
+ rejectPending(new Error("[webframez-react] Initial HTML worker timed out"));
624
+ stopWorker();
625
+ }, 1e4);
626
+ pending.set(requestId, { resolve, reject, timeout });
627
+ const request = {
628
+ id: requestId,
629
+ type: "render",
630
+ payload
631
+ };
632
+ activeChild.send(request, (error) => {
633
+ if (!error) {
634
+ return;
635
+ }
636
+ const entry = pending.get(requestId);
637
+ if (!entry) {
638
+ return;
639
+ }
640
+ pending.delete(requestId);
641
+ clearTimeout(entry.timeout);
642
+ entry.reject(error instanceof Error ? error : new Error(String(error)));
643
+ });
644
+ });
645
+ },
646
+ dispose() {
647
+ rejectPending(new Error("[webframez-react] Initial HTML worker disposed"));
648
+ stopWorker();
649
+ }
650
+ };
543
651
  }
544
652
  function withRequestBasename(basename, fn) {
545
653
  const target = globalThis;
@@ -599,6 +707,13 @@ function createNodeRequestHandler(options) {
599
707
  const liveReloadClients = /* @__PURE__ */ new Set();
600
708
  const router = createFileRouter({ pagesDir });
601
709
  const moduleMap = JSON.parse(fs2.readFileSync(manifestPath, "utf-8"));
710
+ const initialHtmlWorker = createInitialHtmlWorker(pagesDir);
711
+ const disposeInitialHtmlWorker = () => {
712
+ initialHtmlWorker.dispose();
713
+ };
714
+ process.once("exit", disposeInitialHtmlWorker);
715
+ process.once("SIGINT", disposeInitialHtmlWorker);
716
+ process.once("SIGTERM", disposeInitialHtmlWorker);
602
717
  return async function handleRequest(req, res) {
603
718
  if (!req.url) {
604
719
  res.statusCode = 400;
@@ -698,8 +813,7 @@ function createNodeRequestHandler(options) {
698
813
  );
699
814
  let rootHtml = "";
700
815
  try {
701
- rootHtml = await renderInitialHtmlInWorker({
702
- pagesDir,
816
+ rootHtml = await initialHtmlWorker.render({
703
817
  pathname: stripBasePath(url.pathname, basePath),
704
818
  searchParams: parseSearchParams(url.searchParams),
705
819
  cookies: requestCookies,
@@ -707,6 +821,32 @@ function createNodeRequestHandler(options) {
707
821
  });
708
822
  } catch (error) {
709
823
  console.error("[webframez-react] Failed to render initial HTML", error);
824
+ try {
825
+ initialHtmlWorker.dispose();
826
+ rootHtml = await initialHtmlWorker.render({
827
+ pathname: stripBasePath(url.pathname, basePath),
828
+ searchParams: parseSearchParams(url.searchParams),
829
+ cookies: requestCookies,
830
+ basename: basePath
831
+ });
832
+ } catch (retryError) {
833
+ console.error("[webframez-react] Retry for initial HTML failed", retryError);
834
+ res.statusCode = 500;
835
+ res.setHeader("Content-Type", "text/html");
836
+ res.end(
837
+ createHTMLShell({
838
+ title: "500 - Initial HTML render failed",
839
+ headTags: "",
840
+ clientScriptUrl,
841
+ rscEndpoint: rscPath,
842
+ rootHtml: createInitialHtmlErrorMarkup("Initial HTML render failed."),
843
+ basename: basePath,
844
+ liveReloadPath: liveReloadPath || void 0,
845
+ liveReloadServerId: liveReloadPath ? devServerId : void 0
846
+ })
847
+ );
848
+ return;
849
+ }
710
850
  }
711
851
  res.statusCode = resolved.statusCode;
712
852
  res.setHeader("Content-Type", "text/html");