import { join } from "node:path";
import { getContext } from "../../server/context.mjs";
import { getRuntime, runtime$ } from "../../server/runtime.mjs";
import {
COLLECT_CLIENT_MODULES,
COLLECT_STYLESHEETS,
CONFIG_CONTEXT,
CONFIG_ROOT,
HTTP_CONTEXT,
MAIN_MODULE,
MANIFEST,
MODULE_LOADER,
SCROLL_RESTORATION_MODULE,
SOURCEMAP_SUPPORT,
} from "../../server/symbols.mjs";
import * as sys from "../sys.mjs";
const cwd = sys.cwd();
export async function init$(options = {}) {
const [
{ collectStylesheets, collectClientModules },
{ registry: clientRegistry },
{ registry: serverRegistry },
] = await Promise.all([
import("@lazarv/react-server/dist/server/preload-manifest"),
(async () => {
try {
return await import("@lazarv/react-server/dist/client/manifest-registry");
} catch {
return { registry: new Map() };
}
})(),
(async () => {
try {
return await import("@lazarv/react-server/dist/manifest-registry");
} catch {
return { registry: new Map() };
}
})(),
]);
const outDir = options.outDir ?? ".react-server";
const [
{ default: serverLoader },
{ default: clientLoader },
{ default: browserLoader },
] = await Promise.all([
import("@lazarv/react-server/dist/server/server-manifest"),
import("@lazarv/react-server/dist/server/client-manifest"),
import("@lazarv/react-server/dist/client/browser-manifest"),
]);
const [server, client, browser] = await Promise.all([
serverLoader(
sys.toFileUrl(join(cwd, `${outDir}/server/server-manifest.json`))
),
clientLoader(
sys.toFileUrl(join(cwd, `${outDir}/server/client-manifest.json`))
),
browserLoader(
sys.toFileUrl(join(cwd, `${outDir}/client/browser-manifest.json`))
),
]);
const manifest = {
server,
client,
browser,
};
runtime$(MANIFEST, manifest);
// Load build manifest for build metadata (e.g., sourcemap setting)
try {
const { default: buildManifest } =
await import("@lazarv/react-server/dist/server/build-manifest");
if (buildManifest?.sourcemap) {
runtime$(SOURCEMAP_SUPPORT, buildManifest.sourcemap);
}
} catch {
// build-manifest may not exist for older builds
}
// Load action encryption secret from the build artifact.
// Users can override via env var or config (resolved in initSecretFromConfig).
try {
const { default: actionSecret } =
await import("@lazarv/react-server/dist/server/action-secret");
if (actionSecret) {
const { initSecret } = await import("../../server/action-crypto.mjs");
initSecret(actionSecret);
}
} catch {
// action-secret may not exist for builds without server functions
}
// Resolve the action encryption secret from env vars / config / .pem file.
// This runs once at startup — env/config take priority over the build artifact.
try {
const { initSecretFromConfig } =
await import("../../server/action-crypto.mjs");
const config = getRuntime(CONFIG_CONTEXT);
await initSecretFromConfig(config?.[CONFIG_ROOT]);
} catch {
// ignore
}
const mainModule = `/${Object.values(manifest.browser).find((entry) => entry.name === "index")?.file}`;
runtime$(MAIN_MODULE, [mainModule]);
const config = getRuntime(CONFIG_CONTEXT);
const configRoot = config?.[CONFIG_ROOT] ?? {};
if (configRoot.scrollRestoration) {
const scrollRestorationEntry = Object.values(manifest.browser).find(
(entry) => entry.name === "scroll-restoration-init"
);
if (scrollRestorationEntry) {
runtime$(SCROLL_RESTORATION_MODULE, `/${scrollRestorationEntry.file}`);
}
}
const entryCache = new Map();
async function ssrLoadModule($$id, linkQueueStorage) {
const registry = $$id.startsWith("server://")
? serverRegistry
: clientRegistry;
$$id = $$id.replace(/^(server|client):\/\//, "");
const linkQueue = linkQueueStorage?.getStore() ?? new Set();
const httpContext = getContext(HTTP_CONTEXT);
let [id] = (
httpContext
? $$id.replace(
`${httpContext?.request?.headers?.get("x-forwarded-for") || new URL(httpContext?.url).origin}/`,
""
)
: $$id
)
.replace(/^\/+/, "")
.split("#");
try {
const moduleUri = new URL(id);
if (moduleUri.protocol === "http:" || moduleUri.protocol === "https:") {
const entry = Object.values(manifest.browser).find(
(entry) => entry.file && moduleUri.pathname.endsWith(entry.file)
);
if (entry) {
id = entry.file;
} else {
return import(id);
}
}
} catch {
// noop
}
if (entryCache.has(id)) {
const { specifier, links, entry } = entryCache.get(id);
if (links.length > 0) {
linkQueue.add(...links);
}
const { importer } = registry.get(entry.src) || {
importer: () => import(sys.toFileUrl(specifier)),
};
return importer();
}
let entry;
const browserEntry = Object.values(manifest.browser).find(
(entry) => entry.file === id
);
if (browserEntry) {
entry = Object.values(manifest.client).find((entry) => {
try {
return entry.isEntry && browserEntry.src === entry.src;
} catch {
return false;
}
});
}
if (!entry) {
entry = Object.values(manifest.server).find((entry) => entry.src === id);
}
if (!entry) {
const clientEntry = Object.values(manifest.client).find(
(entry) => entry.file === id
);
if (clientEntry) {
entry = clientEntry;
}
}
if (!entry && browserEntry) {
entry = browserEntry;
}
if (!entry) {
throw new Error(`Module not found: ${id}`);
}
const specifier = join(cwd, outDir, entry.file);
const links = collectStylesheets(specifier, manifest.client) ?? [];
entryCache.set(id, { specifier, links, entry });
if (links.length > 0) {
linkQueue.add(...links);
}
const registryEntry = registry.get(entry.src);
const { importer } = registryEntry || {