@webtypen/webframez-react 0.0.49 → 0.0.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -491,3 +491,64 @@ Watch mode for package development:
491
491
  ```bash
492
492
  npm run build:watch
493
493
  ```
494
+
495
+ ## Persistent navigation in consuming applications
496
+
497
+ Use the host router's Link component for URLs. Keep the Suite renderer mounted
498
+ across paths inside one authorized project; do not add `key={path}` or conditionally
499
+ unmount it while reloading project context. Revalidate/reset the context when the
500
+ project changes, and continue enforcing authorization on every server request.
501
+ Only the page content resets for a new screen. The renderer retains its layout and
502
+ prior page while a same-project request is pending, with stale interactions disabled.
503
+
504
+ For webframez-react, configure the DOM link adapter at the host boundary:
505
+
506
+ ```tsx
507
+ import React from "react";
508
+ import { Link } from "@webtypen/webframez-react/navigation";
509
+
510
+ const SuiteLink = React.forwardRef<HTMLAnchorElement,
511
+ React.AnchorHTMLAttributes<HTMLAnchorElement>
512
+ >(function SuiteLink({ href, ...props }, ref) {
513
+ return <Link {...props} ref={ref} to={href || "#"} basename="" />;
514
+ });
515
+
516
+ // Inside an already authorized project context; paths are fully qualified.
517
+ <NativeDesignProvider
518
+ key={projectId}
519
+ suite={{ endpoint: "/manager", navigation: {
520
+ pathname, linkComponent: SuiteLink,
521
+ onNavigate: to => router.push(to),
522
+ } }}
523
+ >
524
+ <WebframezSuiteRenderer />
525
+ </NativeDesignProvider>
526
+ ```
527
+
528
+ Screen definitions remain JSON-serializable: links use `to`/`path`; React Link
529
+ components belong in the consuming application's adapter, never in server schemas.
530
+ See `AGENTS.md` for the navigation invariants and regression-test requirements.
531
+
532
+
533
+ ## Core router basename
534
+
535
+ With the updated Core router, set `application.router.basename` in the Core
536
+ configuration. `initWebframezReact(Route).renderReact("/", options)` inherits it;
537
+ no duplicate `basePath`, `assetsPrefix`, `rscPath`, `clientScriptUrl`, or
538
+ `Head.basename` is needed. Nested render routes and `Route.group` prefixes are
539
+ included in the runtime mount while build target names and directories stay
540
+ stable. Explicit external asset URLs remain unchanged. Without Core, the Node
541
+ handler still supports `basePath` and explicit transport URLs.
542
+
543
+ For API calls, form actions, raw links and redirects in shared/client code:
544
+
545
+ ```ts
546
+ import { appPath, appRelativePath, getBasename } from "@webtypen/webframez-react/paths";
547
+ fetch(appPath("/api/items"));
548
+ ```
549
+
550
+ These browser-safe helpers bundle Core's URL logic and read the runtime mount;
551
+ no server dependencies are included. `Link` and `Redirect` inherit it
552
+ implicitly. Server requests use an async-local context, and HTML rendering and
553
+ client navigation receive the same basename through the Flight head and HTML
554
+ shell. External URLs and already-prefixed URLs are preserved.
@@ -1,9 +1,12 @@
1
1
  const fs = require("fs");
2
2
  const path = require("path");
3
- const ReactFlightWebpackPlugin = require("react-server-dom-webpack/plugin");
3
+ const { createRequire } = require("node:module");
4
4
  const { fileURLToPath, pathToFileURL } = require("url");
5
5
 
6
6
  const projectRoot = process.cwd();
7
+ const projectRequire = createRequire(path.join(projectRoot, "package.json"));
8
+ const ReactFlightWebpackPlugin = projectRequire("react-server-dom-webpack/plugin");
9
+ const reactDomRequire = createRequire(projectRequire.resolve("react-dom/package.json"));
7
10
  const frameworkDistDir = path.resolve(__dirname, "..", "dist");
8
11
  const clientEntry = process.env.WEBFRAMEZ_REACT_CLIENT_ENTRY
9
12
  ? path.resolve(projectRoot, process.env.WEBFRAMEZ_REACT_CLIENT_ENTRY)
@@ -266,7 +269,8 @@ module.exports = {
266
269
  "react-dom": path.resolve(projectRoot, "node_modules", "react-dom"),
267
270
  "react/jsx-runtime": path.resolve(projectRoot, "node_modules", "react", "jsx-runtime.js"),
268
271
  "react/jsx-dev-runtime": path.resolve(projectRoot, "node_modules", "react", "jsx-dev-runtime.js"),
269
- scheduler: path.resolve(projectRoot, "node_modules", "scheduler"),
272
+ // Resolve React DOM's own scheduler, including pnpm's isolated layout.
273
+ scheduler: path.dirname(reactDomRequire.resolve("scheduler/package.json")),
270
274
  },
271
275
  },
272
276
  watchOptions: {
package/dist/http.cjs CHANGED
@@ -29,9 +29,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  // src/http.ts
30
30
  var http_exports = {};
31
31
  __export(http_exports, {
32
- createNodeRequestHandler: () => createNodeRequestHandler
32
+ createNodeRequestHandler: () => createNodeRequestHandler,
33
+ disposeReactHtmlRenderer: () => disposeReactHtmlRenderer,
34
+ renderReactToHtml: () => renderReactToHtml
33
35
  });
34
36
  module.exports = __toCommonJS(http_exports);
37
+ var import_node_async_hooks = require("node:async_hooks");
35
38
  var import_node_fs2 = __toESM(require("node:fs"), 1);
36
39
  var import_node_path3 = __toESM(require("node:path"), 1);
37
40
  var import_node_url = require("node:url");
@@ -341,8 +344,7 @@ var FORCED_PACKAGE_REQUESTS = [
341
344
  "react-server-dom-webpack",
342
345
  "react-server-dom-webpack/server",
343
346
  "react-server-dom-webpack/client",
344
- "react-server-dom-webpack/client.node",
345
- "scheduler"
347
+ "react-server-dom-webpack/client.node"
346
348
  ];
347
349
  var forcedPackageResolutionInstalled = false;
348
350
  function normalizePathname(pathname) {
@@ -993,6 +995,7 @@ const rootParent = {
993
995
  path: process.cwd(),
994
996
  paths: Module._nodeModulePaths(process.cwd()),
995
997
  };
998
+ // Transitive dependencies (e.g. scheduler) must resolve from their importer.
996
999
  const forcedPackageRequests = [
997
1000
  "@webtypen/webframez-core",
998
1001
  "@webtypen/webframez-react",
@@ -1007,7 +1010,6 @@ const forcedPackageRequests = [
1007
1010
  "react-server-dom-webpack/server",
1008
1011
  "react-server-dom-webpack/client",
1009
1012
  "react-server-dom-webpack/client.node",
1010
- "scheduler"
1011
1013
  ];
1012
1014
 
1013
1015
  function shouldForcePackageResolution(request) {
@@ -1199,13 +1201,17 @@ async function renderHtmlFromFlightData(flightData, moduleMap) {
1199
1201
  ? payload.head.basename
1200
1202
  : "";
1201
1203
 
1202
- const previousBasename = globalThis.__RSC_BASENAME;
1203
- globalThis.__RSC_BASENAME = basename;
1204
- try {
1205
- return await renderHtml(model);
1206
- } finally {
1207
- globalThis.__RSC_BASENAME = previousBasename;
1208
- }
1204
+ const routingContext = globalThis.__WEBFRAMEZ_ROUTING_CONTEXT__ ??=
1205
+ new (require("node:async_hooks").AsyncLocalStorage)();
1206
+ return routingContext.run(basename, async () => {
1207
+ const previousBasename = globalThis.__RSC_BASENAME;
1208
+ globalThis.__RSC_BASENAME = basename;
1209
+ try {
1210
+ return await renderHtml(model);
1211
+ } finally {
1212
+ globalThis.__RSC_BASENAME = previousBasename;
1213
+ }
1214
+ });
1209
1215
  }
1210
1216
 
1211
1217
  process.on("message", async (message) => {
@@ -1441,24 +1447,10 @@ ${stderrBuffer.trim()}` : "";
1441
1447
  }
1442
1448
  };
1443
1449
  }
1450
+ var routingRuntime = globalThis;
1451
+ var basenameContext = routingRuntime.__WEBFRAMEZ_ROUTING_CONTEXT__ ??= new import_node_async_hooks.AsyncLocalStorage();
1444
1452
  function withRequestBasename(basename, fn) {
1445
- const target = globalThis;
1446
- const previous = target.__RSC_BASENAME;
1447
- target.__RSC_BASENAME = basename;
1448
- const finish = () => {
1449
- target.__RSC_BASENAME = previous;
1450
- };
1451
- try {
1452
- const result = fn();
1453
- if (result && typeof result.then === "function") {
1454
- return result.finally(finish);
1455
- }
1456
- finish();
1457
- return result;
1458
- } catch (error) {
1459
- finish();
1460
- throw error;
1461
- }
1453
+ return basenameContext.run(basename, fn);
1462
1454
  }
1463
1455
  function parseCookies(rawCookieHeader) {
1464
1456
  const raw = Array.isArray(rawCookieHeader) ? rawCookieHeader.join("; ") : rawCookieHeader ?? "";
@@ -1493,12 +1485,27 @@ function normalizeClientManifest(manifest, options) {
1493
1485
  }
1494
1486
  };
1495
1487
  for (const [key, value] of Object.entries(manifest)) {
1488
+ const hashIndex = key.indexOf("#");
1489
+ const moduleKey = hashIndex < 0 ? key : key.slice(0, hashIndex);
1490
+ const exportSuffix = hashIndex < 0 ? "" : key.slice(hashIndex);
1491
+ if (!import_node_path3.default.isAbsolute(moduleKey) && !moduleKey.includes(":") && !moduleKey.startsWith("file://")) {
1492
+ const runtimePath = import_node_path3.default.resolve(options.cwd, moduleKey);
1493
+ const allowedRoots = [options.distRootDir, ...candidateNodeModulesDirs];
1494
+ const insideArtifact = allowedRoots.some((root) => {
1495
+ const relative = import_node_path3.default.relative(import_node_path3.default.resolve(root), runtimePath);
1496
+ return relative !== "" && relative !== ".." && !relative.startsWith(`..${import_node_path3.default.sep}`) && !import_node_path3.default.isAbsolute(relative);
1497
+ });
1498
+ if (insideArtifact) {
1499
+ addAlias(`${runtimePath}${exportSuffix}`, value);
1500
+ addAlias(`${(0, import_node_url.pathToFileURL)(runtimePath).href}${exportSuffix}`, value);
1501
+ }
1502
+ }
1496
1503
  if (!key.startsWith("file://")) {
1497
1504
  continue;
1498
1505
  }
1499
1506
  let absolutePath = "";
1500
1507
  try {
1501
- absolutePath = (0, import_node_url.fileURLToPath)(key);
1508
+ absolutePath = (0, import_node_url.fileURLToPath)(moduleKey);
1502
1509
  } catch {
1503
1510
  continue;
1504
1511
  }
@@ -1509,13 +1516,13 @@ function normalizeClientManifest(manifest, options) {
1509
1516
  }
1510
1517
  const relativeModulePath = absolutePath.slice(markerIndex + marker.length);
1511
1518
  const relativeModulePathPosix = relativeModulePath.split(import_node_path3.default.sep).join("/");
1512
- addAlias(`./node_modules/${relativeModulePathPosix}`, value);
1513
- addAlias(`node_modules/${relativeModulePathPosix}`, value);
1514
- addAlias(absolutePath, value);
1519
+ addAlias(`./node_modules/${relativeModulePathPosix}${exportSuffix}`, value);
1520
+ addAlias(`node_modules/${relativeModulePathPosix}${exportSuffix}`, value);
1521
+ addAlias(`${absolutePath}${exportSuffix}`, value);
1515
1522
  for (const nodeModulesDir of candidateNodeModulesDirs) {
1516
1523
  const aliasPath = import_node_path3.default.join(nodeModulesDir, relativeModulePath);
1517
- addAlias(aliasPath, value);
1518
- addAlias((0, import_node_url.pathToFileURL)(aliasPath).href, value);
1524
+ addAlias(`${aliasPath}${exportSuffix}`, value);
1525
+ addAlias(`${(0, import_node_url.pathToFileURL)(aliasPath).href}${exportSuffix}`, value);
1519
1526
  }
1520
1527
  }
1521
1528
  return normalized;
@@ -1660,10 +1667,10 @@ function createNodeRequestHandler(options) {
1660
1667
  const manifestPath = import_node_path3.default.resolve(
1661
1668
  options.manifestPath ?? import_node_path3.default.join(distRootDir, "react-client-manifest.json")
1662
1669
  );
1663
- const assetsPrefix = options.assetsPrefix ?? "/assets/";
1664
- const rscPath = options.rscPath ?? "/rsc";
1665
- const clientScriptUrl = options.clientScriptUrl ?? "/assets/client.js";
1666
1670
  const basePath = normalizeBasePath(options.basePath);
1671
+ const assetsPrefix = options.assetsPrefix ?? `${basePath}/assets/`;
1672
+ const rscPath = options.rscPath ?? `${basePath}/rsc`;
1673
+ const clientScriptUrl = options.clientScriptUrl ?? `${basePath}/assets/client.js`;
1667
1674
  const nodeEnv = process.env.NODE_ENV || "";
1668
1675
  const runningInWatchMode = Array.isArray(process.execArgv) && process.execArgv.includes("--watch");
1669
1676
  const liveReloadEnabled = options.liveReloadPath !== false && (nodeEnv === "development" || runningInWatchMode);
@@ -1682,7 +1689,7 @@ function createNodeRequestHandler(options) {
1682
1689
  process.once("exit", disposeInitialHtmlWorker);
1683
1690
  process.once("SIGINT", disposeInitialHtmlWorker);
1684
1691
  process.once("SIGTERM", disposeInitialHtmlWorker);
1685
- return async function handleRequest(req, res) {
1692
+ const handleRequest = async (req, res) => {
1686
1693
  if (!req.url) {
1687
1694
  res.statusCode = 400;
1688
1695
  res.end("Bad request");
@@ -1746,6 +1753,7 @@ function createNodeRequestHandler(options) {
1746
1753
  request: requestContext
1747
1754
  })
1748
1755
  );
1756
+ resolved2.head = { ...resolved2.head, basename: resolved2.head.basename ?? basePath };
1749
1757
  attachResolvedContextToCoreRequest(req, resolved2.context);
1750
1758
  const payload = {
1751
1759
  model: resolved2.model,
@@ -1821,6 +1829,7 @@ function createNodeRequestHandler(options) {
1821
1829
  )
1822
1830
  })
1823
1831
  );
1832
+ resolved.head = { ...resolved.head, basename: resolved.head.basename ?? basePath };
1824
1833
  attachResolvedContextToCoreRequest(req, resolved.context);
1825
1834
  const initialPayload = {
1826
1835
  model: resolved.model,
@@ -1906,8 +1915,30 @@ function createNodeRequestHandler(options) {
1906
1915
  }
1907
1916
  );
1908
1917
  };
1918
+ return (req, res) => withRequestBasename(basePath, () => handleRequest(req, res));
1919
+ }
1920
+ var standaloneHtmlWorker;
1921
+ async function renderReactToHtml(element) {
1922
+ if (!standaloneHtmlWorker) {
1923
+ standaloneHtmlWorker = createInitialHtmlWorker(process.cwd());
1924
+ process.once("exit", disposeReactHtmlRenderer);
1925
+ }
1926
+ const flightData = await renderRSCToString({ model: element }, {
1927
+ moduleMap: {},
1928
+ onError: (error) => {
1929
+ console.error("[webframez-react] HTML render failed", error);
1930
+ }
1931
+ });
1932
+ return standaloneHtmlWorker.renderFromFlightData({ flightData, moduleMap: {} });
1933
+ }
1934
+ function disposeReactHtmlRenderer() {
1935
+ process.removeListener("exit", disposeReactHtmlRenderer);
1936
+ standaloneHtmlWorker?.dispose();
1937
+ standaloneHtmlWorker = void 0;
1909
1938
  }
1910
1939
  // Annotate the CommonJS export names for ESM import in node:
1911
1940
  0 && (module.exports = {
1912
- createNodeRequestHandler
1941
+ createNodeRequestHandler,
1942
+ disposeReactHtmlRenderer,
1943
+ renderReactToHtml
1913
1944
  });
package/dist/http.d.ts CHANGED
@@ -27,3 +27,6 @@ export interface CreateNodeHandlerOptions
27
27
  export function createNodeRequestHandler(
28
28
  options: CreateNodeHandlerOptions
29
29
  ): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
30
+
31
+ export function renderReactToHtml(element: unknown): Promise<string>;
32
+ export function disposeReactHtmlRenderer(): void;
package/dist/http.js CHANGED
@@ -7,6 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  });
8
8
 
9
9
  // src/http.ts
10
+ import { AsyncLocalStorage } from "node:async_hooks";
10
11
  import fs2 from "node:fs";
11
12
  import path3 from "node:path";
12
13
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -321,8 +322,7 @@ var FORCED_PACKAGE_REQUESTS = [
321
322
  "react-server-dom-webpack",
322
323
  "react-server-dom-webpack/server",
323
324
  "react-server-dom-webpack/client",
324
- "react-server-dom-webpack/client.node",
325
- "scheduler"
325
+ "react-server-dom-webpack/client.node"
326
326
  ];
327
327
  var forcedPackageResolutionInstalled = false;
328
328
  function normalizePathname(pathname) {
@@ -973,6 +973,7 @@ const rootParent = {
973
973
  path: process.cwd(),
974
974
  paths: Module._nodeModulePaths(process.cwd()),
975
975
  };
976
+ // Transitive dependencies (e.g. scheduler) must resolve from their importer.
976
977
  const forcedPackageRequests = [
977
978
  "@webtypen/webframez-core",
978
979
  "@webtypen/webframez-react",
@@ -987,7 +988,6 @@ const forcedPackageRequests = [
987
988
  "react-server-dom-webpack/server",
988
989
  "react-server-dom-webpack/client",
989
990
  "react-server-dom-webpack/client.node",
990
- "scheduler"
991
991
  ];
992
992
 
993
993
  function shouldForcePackageResolution(request) {
@@ -1179,13 +1179,17 @@ async function renderHtmlFromFlightData(flightData, moduleMap) {
1179
1179
  ? payload.head.basename
1180
1180
  : "";
1181
1181
 
1182
- const previousBasename = globalThis.__RSC_BASENAME;
1183
- globalThis.__RSC_BASENAME = basename;
1184
- try {
1185
- return await renderHtml(model);
1186
- } finally {
1187
- globalThis.__RSC_BASENAME = previousBasename;
1188
- }
1182
+ const routingContext = globalThis.__WEBFRAMEZ_ROUTING_CONTEXT__ ??=
1183
+ new (require("node:async_hooks").AsyncLocalStorage)();
1184
+ return routingContext.run(basename, async () => {
1185
+ const previousBasename = globalThis.__RSC_BASENAME;
1186
+ globalThis.__RSC_BASENAME = basename;
1187
+ try {
1188
+ return await renderHtml(model);
1189
+ } finally {
1190
+ globalThis.__RSC_BASENAME = previousBasename;
1191
+ }
1192
+ });
1189
1193
  }
1190
1194
 
1191
1195
  process.on("message", async (message) => {
@@ -1421,24 +1425,10 @@ ${stderrBuffer.trim()}` : "";
1421
1425
  }
1422
1426
  };
1423
1427
  }
1428
+ var routingRuntime = globalThis;
1429
+ var basenameContext = routingRuntime.__WEBFRAMEZ_ROUTING_CONTEXT__ ??= new AsyncLocalStorage();
1424
1430
  function withRequestBasename(basename, fn) {
1425
- const target = globalThis;
1426
- const previous = target.__RSC_BASENAME;
1427
- target.__RSC_BASENAME = basename;
1428
- const finish = () => {
1429
- target.__RSC_BASENAME = previous;
1430
- };
1431
- try {
1432
- const result = fn();
1433
- if (result && typeof result.then === "function") {
1434
- return result.finally(finish);
1435
- }
1436
- finish();
1437
- return result;
1438
- } catch (error) {
1439
- finish();
1440
- throw error;
1441
- }
1431
+ return basenameContext.run(basename, fn);
1442
1432
  }
1443
1433
  function parseCookies(rawCookieHeader) {
1444
1434
  const raw = Array.isArray(rawCookieHeader) ? rawCookieHeader.join("; ") : rawCookieHeader ?? "";
@@ -1473,12 +1463,27 @@ function normalizeClientManifest(manifest, options) {
1473
1463
  }
1474
1464
  };
1475
1465
  for (const [key, value] of Object.entries(manifest)) {
1466
+ const hashIndex = key.indexOf("#");
1467
+ const moduleKey = hashIndex < 0 ? key : key.slice(0, hashIndex);
1468
+ const exportSuffix = hashIndex < 0 ? "" : key.slice(hashIndex);
1469
+ if (!path3.isAbsolute(moduleKey) && !moduleKey.includes(":") && !moduleKey.startsWith("file://")) {
1470
+ const runtimePath = path3.resolve(options.cwd, moduleKey);
1471
+ const allowedRoots = [options.distRootDir, ...candidateNodeModulesDirs];
1472
+ const insideArtifact = allowedRoots.some((root) => {
1473
+ const relative = path3.relative(path3.resolve(root), runtimePath);
1474
+ return relative !== "" && relative !== ".." && !relative.startsWith(`..${path3.sep}`) && !path3.isAbsolute(relative);
1475
+ });
1476
+ if (insideArtifact) {
1477
+ addAlias(`${runtimePath}${exportSuffix}`, value);
1478
+ addAlias(`${pathToFileURL(runtimePath).href}${exportSuffix}`, value);
1479
+ }
1480
+ }
1476
1481
  if (!key.startsWith("file://")) {
1477
1482
  continue;
1478
1483
  }
1479
1484
  let absolutePath = "";
1480
1485
  try {
1481
- absolutePath = fileURLToPath(key);
1486
+ absolutePath = fileURLToPath(moduleKey);
1482
1487
  } catch {
1483
1488
  continue;
1484
1489
  }
@@ -1489,13 +1494,13 @@ function normalizeClientManifest(manifest, options) {
1489
1494
  }
1490
1495
  const relativeModulePath = absolutePath.slice(markerIndex + marker.length);
1491
1496
  const relativeModulePathPosix = relativeModulePath.split(path3.sep).join("/");
1492
- addAlias(`./node_modules/${relativeModulePathPosix}`, value);
1493
- addAlias(`node_modules/${relativeModulePathPosix}`, value);
1494
- addAlias(absolutePath, value);
1497
+ addAlias(`./node_modules/${relativeModulePathPosix}${exportSuffix}`, value);
1498
+ addAlias(`node_modules/${relativeModulePathPosix}${exportSuffix}`, value);
1499
+ addAlias(`${absolutePath}${exportSuffix}`, value);
1495
1500
  for (const nodeModulesDir of candidateNodeModulesDirs) {
1496
1501
  const aliasPath = path3.join(nodeModulesDir, relativeModulePath);
1497
- addAlias(aliasPath, value);
1498
- addAlias(pathToFileURL(aliasPath).href, value);
1502
+ addAlias(`${aliasPath}${exportSuffix}`, value);
1503
+ addAlias(`${pathToFileURL(aliasPath).href}${exportSuffix}`, value);
1499
1504
  }
1500
1505
  }
1501
1506
  return normalized;
@@ -1640,10 +1645,10 @@ function createNodeRequestHandler(options) {
1640
1645
  const manifestPath = path3.resolve(
1641
1646
  options.manifestPath ?? path3.join(distRootDir, "react-client-manifest.json")
1642
1647
  );
1643
- const assetsPrefix = options.assetsPrefix ?? "/assets/";
1644
- const rscPath = options.rscPath ?? "/rsc";
1645
- const clientScriptUrl = options.clientScriptUrl ?? "/assets/client.js";
1646
1648
  const basePath = normalizeBasePath(options.basePath);
1649
+ const assetsPrefix = options.assetsPrefix ?? `${basePath}/assets/`;
1650
+ const rscPath = options.rscPath ?? `${basePath}/rsc`;
1651
+ const clientScriptUrl = options.clientScriptUrl ?? `${basePath}/assets/client.js`;
1647
1652
  const nodeEnv = process.env.NODE_ENV || "";
1648
1653
  const runningInWatchMode = Array.isArray(process.execArgv) && process.execArgv.includes("--watch");
1649
1654
  const liveReloadEnabled = options.liveReloadPath !== false && (nodeEnv === "development" || runningInWatchMode);
@@ -1662,7 +1667,7 @@ function createNodeRequestHandler(options) {
1662
1667
  process.once("exit", disposeInitialHtmlWorker);
1663
1668
  process.once("SIGINT", disposeInitialHtmlWorker);
1664
1669
  process.once("SIGTERM", disposeInitialHtmlWorker);
1665
- return async function handleRequest(req, res) {
1670
+ const handleRequest = async (req, res) => {
1666
1671
  if (!req.url) {
1667
1672
  res.statusCode = 400;
1668
1673
  res.end("Bad request");
@@ -1726,6 +1731,7 @@ function createNodeRequestHandler(options) {
1726
1731
  request: requestContext
1727
1732
  })
1728
1733
  );
1734
+ resolved2.head = { ...resolved2.head, basename: resolved2.head.basename ?? basePath };
1729
1735
  attachResolvedContextToCoreRequest(req, resolved2.context);
1730
1736
  const payload = {
1731
1737
  model: resolved2.model,
@@ -1801,6 +1807,7 @@ function createNodeRequestHandler(options) {
1801
1807
  )
1802
1808
  })
1803
1809
  );
1810
+ resolved.head = { ...resolved.head, basename: resolved.head.basename ?? basePath };
1804
1811
  attachResolvedContextToCoreRequest(req, resolved.context);
1805
1812
  const initialPayload = {
1806
1813
  model: resolved.model,
@@ -1886,7 +1893,29 @@ function createNodeRequestHandler(options) {
1886
1893
  }
1887
1894
  );
1888
1895
  };
1896
+ return (req, res) => withRequestBasename(basePath, () => handleRequest(req, res));
1897
+ }
1898
+ var standaloneHtmlWorker;
1899
+ async function renderReactToHtml(element) {
1900
+ if (!standaloneHtmlWorker) {
1901
+ standaloneHtmlWorker = createInitialHtmlWorker(process.cwd());
1902
+ process.once("exit", disposeReactHtmlRenderer);
1903
+ }
1904
+ const flightData = await renderRSCToString({ model: element }, {
1905
+ moduleMap: {},
1906
+ onError: (error) => {
1907
+ console.error("[webframez-react] HTML render failed", error);
1908
+ }
1909
+ });
1910
+ return standaloneHtmlWorker.renderFromFlightData({ flightData, moduleMap: {} });
1911
+ }
1912
+ function disposeReactHtmlRenderer() {
1913
+ process.removeListener("exit", disposeReactHtmlRenderer);
1914
+ standaloneHtmlWorker?.dispose();
1915
+ standaloneHtmlWorker = void 0;
1889
1916
  }
1890
1917
  export {
1891
- createNodeRequestHandler
1918
+ createNodeRequestHandler,
1919
+ disposeReactHtmlRenderer,
1920
+ renderReactToHtml
1892
1921
  };