heliumts 0.8.3 → 0.8.5

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
@@ -285,6 +285,15 @@ import type { HeliumConfig } from "heliumts/server";
285
285
 
286
286
  const config: HeliumConfig = {
287
287
  trustProxyDepth: 1, // Trust 1 proxy level (e.g., Vercel)
288
+ security: {
289
+ defaultHeaders: true,
290
+ headerOverrides: {
291
+ "X-Frame-Options": "SAMEORIGIN",
292
+ },
293
+ contentSecurityPolicy: "default-src 'self'; frame-ancestors 'self'",
294
+ hsts: true,
295
+ corsOrigins: ["https://app.example.com"],
296
+ },
288
297
  rpc: {
289
298
  encoding: "msgpack", // or "json"
290
299
  compression: {
@@ -8,7 +8,7 @@ import { build as viteBuild } from "vite";
8
8
  import { log } from "../utils/logger.js";
9
9
  import { scanAppShell, scanPageRoutePatterns, scanServerExports, scanSSRPages } from "../vite/scanner.js";
10
10
  import { generateStaticPages } from "../vite/ssg.js";
11
- import { generateServerManifest } from "../vite/virtualServerModule.js";
11
+ import { generateClientModule, generateServerManifest, generateTypeDefinitions } from "../vite/virtualServerModule.js";
12
12
  const cli = cac("helium");
13
13
  const root = process.cwd();
14
14
  cli.command("dev", "Start development server").action(async () => {
@@ -18,6 +18,19 @@ cli.command("dev", "Start development server").action(async () => {
18
18
  });
19
19
  });
20
20
  cli.command("build", "Build for production").action(async () => {
21
+ // Generate type definitions before building so TypeScript can resolve
22
+ // server method types even if the dev server was never started.
23
+ const { methods } = scanServerExports(root);
24
+ const dts = generateTypeDefinitions(methods, root);
25
+ const typesDir = path.join(root, "src", "types");
26
+ const dtsPath = path.join(typesDir, "heliumts-server.d.ts");
27
+ if (!fs.existsSync(typesDir)) {
28
+ fs.mkdirSync(typesDir, { recursive: true });
29
+ }
30
+ if (!fs.existsSync(dtsPath) || fs.readFileSync(dtsPath, "utf-8") !== dts) {
31
+ fs.writeFileSync(dtsPath, dts);
32
+ log("info", `Generated type definitions for ${methods.length} method(s)`);
33
+ }
21
34
  log("info", "--------------------------------");
22
35
  log("info", "Building client...");
23
36
  try {
@@ -248,6 +261,13 @@ export function clearPrefetchCache() {}
248
261
  fs.writeFileSync(entryPath, entryCode);
249
262
  fs.writeFileSync(envLoaderPath, envLoaderCode);
250
263
  fs.writeFileSync(serverModuleSrcPath, serverModuleCode);
264
+ // Generate a client-side stub for heliumts/server that provides method stubs
265
+ // (e.g. { __id: 'subscribeWaitlist' }) so client components that import
266
+ // user-defined methods from 'heliumts/server' resolve correctly during
267
+ // the esbuild server bundle (mirroring the Vite plugin's virtual module).
268
+ const rpcClientStubPath = path.join(heliumDir, "rpc-client-stub.ts");
269
+ const rpcClientStubCode = generateClientModule(serverExports.methods);
270
+ fs.writeFileSync(rpcClientStubPath, rpcClientStubCode);
251
271
  fs.writeFileSync(ssrClientStubPath, ssrClientStubCode);
252
272
  fs.writeFileSync(ssrTransitionsStubPath, ssrTransitionsStubCode);
253
273
  fs.writeFileSync(ssrPrefetchStubPath, ssrPrefetchStubCode);
@@ -268,6 +288,19 @@ export function clearPrefetchCache() {}
268
288
  build.onResolve({ filter: /^heliumts\/client$/ }, () => ({ path: ssrClientStubPath }));
269
289
  build.onResolve({ filter: /^heliumts\/client\/transitions$/ }, () => ({ path: ssrTransitionsStubPath }));
270
290
  build.onResolve({ filter: /^heliumts\/client\/prefetch$/ }, () => ({ path: ssrPrefetchStubPath }));
291
+ // Intercept heliumts/server imports from client code
292
+ // and redirect to the RPC stub module (method stubs).
293
+ // Server-side files and generated server-module resolve normally.
294
+ const serverDirAbs = path.join(root, "src", "server");
295
+ build.onResolve({ filter: /^heliumts\/server$/ }, (args) => {
296
+ const importer = args.importer;
297
+ // Allow the generated server-module and env-loader to
298
+ // import the real heliumts/server framework exports.
299
+ if (importer.startsWith(heliumDir) || importer.startsWith(serverDirAbs)) {
300
+ return undefined; // let esbuild resolve normally
301
+ }
302
+ return { path: rpcClientStubPath };
303
+ });
271
304
  },
272
305
  },
273
306
  ],
@@ -1 +1 @@
1
- {"version":3,"file":"helium.js","sourceRoot":"","sources":["../../src/bin/helium.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { cac } from \"cac\";\nimport { spawn } from \"child_process\";\nimport { build as esbuild } from \"esbuild\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { build as viteBuild } from \"vite\";\n\nimport { log } from \"../utils/logger.js\";\nimport { scanAppShell, scanPageRoutePatterns, scanServerExports, scanSSRPages } from \"../vite/scanner.js\";\nimport { generateStaticPages } from \"../vite/ssg.js\";\nimport { generateServerManifest } from \"../vite/virtualServerModule.js\";\n\nconst cli = cac(\"helium\");\nconst root = process.cwd();\n\ncli.command(\"dev\", \"Start development server\").action(async () => {\n const vite = spawn(\"vite\", [], { stdio: \"inherit\", shell: true });\n vite.on(\"close\", (code) => {\n process.exit(code || 0);\n });\n});\n\ncli.command(\"build\", \"Build for production\").action(async () => {\n log(\"info\", \"--------------------------------\");\n log(\"info\", \"Building client...\");\n\n try {\n const result = await viteBuild({\n root,\n logLevel: \"silent\",\n build: {\n outDir: \"dist\",\n },\n });\n\n // Display build output\n if (result && \"output\" in result) {\n const outputs = result.output;\n const zlib = await import(\"zlib\");\n\n for (const chunk of outputs) {\n if (chunk.type === \"asset\" || chunk.type === \"chunk\") {\n const fileName = chunk.fileName;\n const content = \"code\" in chunk ? chunk.code : chunk.source;\n const size = Buffer.byteLength(content, \"utf-8\");\n const sizeKB = (size / 1024).toFixed(2);\n\n // Calculate gzipped size\n const gzipped = zlib.gzipSync(content);\n const gzipSizeKB = (gzipped.length / 1024).toFixed(2);\n\n log(\"info\", ` ${fileName.padEnd(35)} ${sizeKB.padStart(8)} kB │ gzip: ${gzipSizeKB.padStart(7)} kB`);\n }\n }\n }\n\n log(\"info\", \"Client build complete.\");\n\n // Generate static pages for SSG\n log(\"info\", \"--------------------------------\");\n try {\n // Read the generated index.html as a template for SSG\n const distDir = path.join(root, \"dist\");\n const htmlPath = path.join(distDir, \"index.html\");\n\n if (fs.existsSync(htmlPath)) {\n let htmlTemplate = fs.readFileSync(htmlPath, \"utf-8\");\n\n // Clean up the template for SSG:\n // 1. Remove the build-time HELIUM_CONNECTION_TOKEN (SSG pages don't need it)\n htmlTemplate = htmlTemplate.replace(/<script>window\\.HELIUM_CONNECTION_TOKEN = \"build-time-placeholder\";<\\/script>/g, \"\");\n\n // 2. Clear any existing content in root div from SPA build\n htmlTemplate = htmlTemplate.replace(/<div\\s+id=\"root\"[^>]*>.*?<\\/div>/s, '<div id=\"root\"></div>');\n\n await generateStaticPages(null, root, htmlTemplate, distDir);\n } else {\n log(\"warn\", \"index.html not found in dist, skipping SSG\");\n }\n } catch (e) {\n log(\"warn\", \"SSG generation failed:\", e);\n // Don't fail the build if SSG fails\n }\n log(\"info\", \"--------------------------------\");\n } catch (e) {\n log(\"error\", \"Client build failed:\", e);\n process.exit(1);\n }\n\n log(\"info\", \"Building server...\");\n // Generate server entry\n const serverExports = scanServerExports(root);\n const pageRoutePatterns = scanPageRoutePatterns(root);\n const ssrPages = scanSSRPages(root);\n const appShell = scanAppShell(root);\n const manifestCode = generateServerManifest(\n serverExports.methods,\n serverExports.httpHandlers,\n serverExports.seoMetadata,\n pageRoutePatterns,\n ssrPages,\n appShell,\n serverExports.middleware,\n serverExports.workers\n );\n\n // Create the main server module that will be imported after env is loaded\n const serverModuleCode = `\nimport { startProdServer, loadConfig } from 'heliumts/server';\n${manifestCode}\n\nexport async function start() {\n const config = await loadConfig();\n\n startProdServer({\n config,\n registerHandlers: (registry, httpRouter, seoRouter) => {\n registerAll(registry);\n httpRouter.registerRoutes(httpHandlers);\n seoRouter.registerRoutes(seoMetadataHandlers);\n seoRouter.setPageRoutePatterns(pageRoutePatterns);\n if (middlewareHandler) {\n registry.setMiddleware(middlewareHandler);\n httpRouter.setMiddleware(middlewareHandler);\n }\n },\n ssrPages,\n appShell,\n workers\n });\n}\n`;\n\n // Create the entry loader that loads env first, then imports the server module\n const entryCode = `\n// Load environment variables FIRST, before any other imports\nimport './env-loader.js';\n// Now import and start the server (this ensures handlers load after env)\nimport { start } from './server-module.js';\nawait start();\n`;\n\n const envLoaderCode = `\nimport { loadEnvFiles, injectEnvToProcess, log } from 'heliumts/server';\nconst envRoot = process.cwd();\nlog('info', \\`Loading .env files from: \\${envRoot}\\`);\nconst envVars = loadEnvFiles({ mode: 'production' });\ninjectEnvToProcess(envVars);\nif (Object.keys(envVars).length > 0) {\n log('info', \\`Loaded \\${Object.keys(envVars).length} environment variable(s) from .env files\\`);\n} else {\n log('info', 'No .env files found (using platform environment variables if available)');\n}\n`;\n\n const heliumDir = path.join(root, \"node_modules\", \".heliumts\");\n if (!fs.existsSync(heliumDir)) {\n fs.mkdirSync(heliumDir, { recursive: true });\n }\n const entryPath = path.join(heliumDir, \"server-entry.ts\");\n const envLoaderPath = path.join(heliumDir, \"env-loader.ts\");\n const serverModuleSrcPath = path.join(heliumDir, \"server-module.ts\");\n const ssrClientStubPath = path.join(heliumDir, \"ssr-client-stub.ts\");\n const ssrTransitionsStubPath = path.join(heliumDir, \"ssr-transitions-stub.ts\");\n const ssrPrefetchStubPath = path.join(heliumDir, \"ssr-prefetch-stub.ts\");\n\n const ssrClientStubCode = `\nimport React from 'react';\n\nexport const RouterContext = React.createContext(null);\n\nfunction getSSRRouterSnapshot() {\n const snapshot = globalThis.__HELIUM_SSR_ROUTER__ as\n | { path?: string; params?: Record<string, string | string[]>; search?: string }\n | undefined;\n\n if (!snapshot || typeof snapshot !== 'object') {\n return {\n path: '/',\n params: {},\n search: '',\n };\n }\n\n return {\n path: typeof snapshot.path === 'string' ? snapshot.path : '/',\n params: snapshot.params && typeof snapshot.params === 'object' ? snapshot.params : {},\n search: typeof snapshot.search === 'string' ? snapshot.search : '',\n };\n}\n\nexport function useRouter() {\n const snapshot = getSSRRouterSnapshot();\n return {\n path: snapshot.path,\n params: snapshot.params,\n searchParams: new URLSearchParams(snapshot.search),\n push: () => {},\n replace: () => {},\n on: () => () => {},\n status: 200,\n isNavigating: false,\n isPending: false,\n };\n}\n\nexport function Link(props: { href?: string; children?: React.ReactNode } & Record<string, unknown>) {\n const { href = '#', children, ...rest } = props || {};\n return React.createElement('a', { href, ...rest }, children);\n}\n\nexport function Redirect() {\n return null;\n}\n\nexport function AppRouter() {\n return null;\n}\n\nexport function useCall() {\n return {\n call: async () => null,\n isCalling: false,\n error: null,\n };\n}\n\nexport function useFetch() {\n return {\n data: null,\n isLoading: false,\n error: null,\n refetch: async () => undefined,\n };\n}\n\nexport class RpcError extends Error {}\n\nexport function getRpcTransport() {\n return 'websocket';\n}\n\nexport function isAutoHttpOnMobileEnabled() {\n return false;\n}\n\nexport function preconnect() {}\n\nexport function isSSR() {\n return true;\n}\n`;\n\n const ssrTransitionsStubCode = `\nimport React from 'react';\n\nexport function useDeferredNavigation() {\n return {\n path: '/',\n deferredPath: '/',\n isStale: false,\n isPending: false,\n isTransitioning: false,\n };\n}\n\nexport function PageTransition({ children }: { children?: React.ReactNode }) {\n return React.createElement(React.Fragment, null, children);\n}\n`;\n\n const ssrPrefetchStubCode = `\nexport function prefetchRoute() {}\nexport function clearPrefetchCache() {}\n`;\n\n fs.writeFileSync(entryPath, entryCode);\n fs.writeFileSync(envLoaderPath, envLoaderCode);\n fs.writeFileSync(serverModuleSrcPath, serverModuleCode);\n fs.writeFileSync(ssrClientStubPath, ssrClientStubCode);\n fs.writeFileSync(ssrTransitionsStubPath, ssrTransitionsStubCode);\n fs.writeFileSync(ssrPrefetchStubPath, ssrPrefetchStubCode);\n\n // Bundle with esbuild\n try {\n await esbuild({\n entryPoints: [entryPath],\n outfile: path.join(root, \"dist\", \"server.js\"),\n bundle: true,\n platform: \"node\",\n format: \"esm\",\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n plugins: [\n {\n name: \"helium-ssr-client-alias\",\n setup(build) {\n build.onResolve({ filter: /^heliumts\\/client$/ }, () => ({ path: ssrClientStubPath }));\n build.onResolve({ filter: /^heliumts\\/client\\/transitions$/ }, () => ({ path: ssrTransitionsStubPath }));\n build.onResolve({ filter: /^heliumts\\/client\\/prefetch$/ }, () => ({ path: ssrPrefetchStubPath }));\n },\n },\n ],\n external: [\n // External common database and heavy dependencies\n \"mongodb\",\n \"mongoose\",\n \"pg\",\n \"mysql\",\n \"mysql2\",\n \"sqlite3\",\n \"better-sqlite3\",\n \"redis\",\n // Node.js built-ins are automatically external, but let's be explicit\n \"crypto\",\n \"fs\",\n \"path\",\n \"http\",\n \"https\",\n \"stream\",\n \"zlib\",\n \"util\",\n ],\n target: \"node18\",\n metafile: true,\n banner: {\n js: \"import { createRequire } from 'module'; const require = createRequire(import.meta.url);\",\n },\n });\n\n // Display server build output\n const serverOutputPath = path.relative(root, path.join(root, \"dist\", \"server.js\"));\n const serverStats = fs.statSync(path.join(root, \"dist\", \"server.js\"));\n const serverSizeKB = (serverStats.size / 1024).toFixed(2);\n log(\"info\", ` ${serverOutputPath.padEnd(35)} ${serverSizeKB.padStart(8)} kB`);\n\n log(\"info\", \"Server build complete.\");\n\n // Transpile helium.config.ts to helium.config.js if it exists\n const configTsPath = path.join(root, \"helium.config.ts\");\n if (fs.existsSync(configTsPath)) {\n log(\"info\", \"Transpiling helium.config.ts...\");\n try {\n await esbuild({\n entryPoints: [configTsPath],\n outfile: path.join(root, \"dist\", \"helium.config.js\"),\n bundle: false,\n platform: \"node\",\n format: \"esm\",\n target: \"node18\",\n });\n log(\"info\", \"Config file transpiled to dist/helium.config.js\");\n } catch (e) {\n log(\"warn\", \"Failed to transpile config file:\", e);\n log(\"warn\", \"You may need to manually rename helium.config.ts to helium.config.js\");\n }\n } else {\n // Check if .js or .mjs config exists and copy it to dist\n const configJsPath = path.join(root, \"helium.config.js\");\n const configMjsPath = path.join(root, \"helium.config.mjs\");\n\n if (fs.existsSync(configJsPath)) {\n fs.copyFileSync(configJsPath, path.join(root, \"dist\", \"helium.config.js\"));\n log(\"info\", \"Copied helium.config.js to dist/\");\n } else if (fs.existsSync(configMjsPath)) {\n fs.copyFileSync(configMjsPath, path.join(root, \"dist\", \"helium.config.mjs\"));\n log(\"info\", \"Copied helium.config.mjs to dist/\");\n }\n }\n\n log(\"info\", \"--------------------------------\");\n log(\"info\", \"✓ Build finished successfully.\");\n log(\"info\", \"▶ Run 'helium start' to start the production server.\");\n\n // Exit cleanly after build completes\n process.exit(0);\n } catch (e) {\n log(\"error\", \"Server build failed:\", e);\n process.exit(1);\n }\n});\n\ncli.command(\"start\", \"Start production server\").action(async () => {\n const serverPath = path.join(root, \"dist\", \"server.js\");\n if (!fs.existsSync(serverPath)) {\n log(\"error\", 'Server build not found. Run \"helium build\" first.');\n process.exit(1);\n }\n\n // When running in production, look for config in dist directory first\n // This allows the transpiled config to be found\n const server = spawn(\"node\", [serverPath], {\n stdio: \"inherit\",\n shell: true,\n env: { ...process.env, HELIUM_CONFIG_DIR: path.join(root, \"dist\") },\n });\n server.on(\"close\", (code) => {\n process.exit(code || 0);\n });\n});\n\ncli.help();\ncli.parse();\n"]}
1
+ {"version":3,"file":"helium.js","sourceRoot":"","sources":["../../src/bin/helium.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { cac } from \"cac\";\nimport { spawn } from \"child_process\";\nimport { build as esbuild } from \"esbuild\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { build as viteBuild } from \"vite\";\n\nimport { log } from \"../utils/logger.js\";\nimport { scanAppShell, scanPageRoutePatterns, scanServerExports, scanSSRPages } from \"../vite/scanner.js\";\nimport { generateStaticPages } from \"../vite/ssg.js\";\nimport { generateClientModule, generateServerManifest, generateTypeDefinitions } from \"../vite/virtualServerModule.js\";\n\nconst cli = cac(\"helium\");\nconst root = process.cwd();\n\ncli.command(\"dev\", \"Start development server\").action(async () => {\n const vite = spawn(\"vite\", [], { stdio: \"inherit\", shell: true });\n vite.on(\"close\", (code) => {\n process.exit(code || 0);\n });\n});\n\ncli.command(\"build\", \"Build for production\").action(async () => {\n // Generate type definitions before building so TypeScript can resolve\n // server method types even if the dev server was never started.\n const { methods } = scanServerExports(root);\n const dts = generateTypeDefinitions(methods, root);\n const typesDir = path.join(root, \"src\", \"types\");\n const dtsPath = path.join(typesDir, \"heliumts-server.d.ts\");\n\n if (!fs.existsSync(typesDir)) {\n fs.mkdirSync(typesDir, { recursive: true });\n }\n\n if (!fs.existsSync(dtsPath) || fs.readFileSync(dtsPath, \"utf-8\") !== dts) {\n fs.writeFileSync(dtsPath, dts);\n log(\"info\", `Generated type definitions for ${methods.length} method(s)`);\n }\n\n log(\"info\", \"--------------------------------\");\n log(\"info\", \"Building client...\");\n\n try {\n const result = await viteBuild({\n root,\n logLevel: \"silent\",\n build: {\n outDir: \"dist\",\n },\n });\n\n // Display build output\n if (result && \"output\" in result) {\n const outputs = result.output;\n const zlib = await import(\"zlib\");\n\n for (const chunk of outputs) {\n if (chunk.type === \"asset\" || chunk.type === \"chunk\") {\n const fileName = chunk.fileName;\n const content = \"code\" in chunk ? chunk.code : chunk.source;\n const size = Buffer.byteLength(content, \"utf-8\");\n const sizeKB = (size / 1024).toFixed(2);\n\n // Calculate gzipped size\n const gzipped = zlib.gzipSync(content);\n const gzipSizeKB = (gzipped.length / 1024).toFixed(2);\n\n log(\"info\", ` ${fileName.padEnd(35)} ${sizeKB.padStart(8)} kB │ gzip: ${gzipSizeKB.padStart(7)} kB`);\n }\n }\n }\n\n log(\"info\", \"Client build complete.\");\n\n // Generate static pages for SSG\n log(\"info\", \"--------------------------------\");\n try {\n // Read the generated index.html as a template for SSG\n const distDir = path.join(root, \"dist\");\n const htmlPath = path.join(distDir, \"index.html\");\n\n if (fs.existsSync(htmlPath)) {\n let htmlTemplate = fs.readFileSync(htmlPath, \"utf-8\");\n\n // Clean up the template for SSG:\n // 1. Remove the build-time HELIUM_CONNECTION_TOKEN (SSG pages don't need it)\n htmlTemplate = htmlTemplate.replace(/<script>window\\.HELIUM_CONNECTION_TOKEN = \"build-time-placeholder\";<\\/script>/g, \"\");\n\n // 2. Clear any existing content in root div from SPA build\n htmlTemplate = htmlTemplate.replace(/<div\\s+id=\"root\"[^>]*>.*?<\\/div>/s, '<div id=\"root\"></div>');\n\n await generateStaticPages(null, root, htmlTemplate, distDir);\n } else {\n log(\"warn\", \"index.html not found in dist, skipping SSG\");\n }\n } catch (e) {\n log(\"warn\", \"SSG generation failed:\", e);\n // Don't fail the build if SSG fails\n }\n log(\"info\", \"--------------------------------\");\n } catch (e) {\n log(\"error\", \"Client build failed:\", e);\n process.exit(1);\n }\n\n log(\"info\", \"Building server...\");\n // Generate server entry\n const serverExports = scanServerExports(root);\n const pageRoutePatterns = scanPageRoutePatterns(root);\n const ssrPages = scanSSRPages(root);\n const appShell = scanAppShell(root);\n const manifestCode = generateServerManifest(\n serverExports.methods,\n serverExports.httpHandlers,\n serverExports.seoMetadata,\n pageRoutePatterns,\n ssrPages,\n appShell,\n serverExports.middleware,\n serverExports.workers\n );\n\n // Create the main server module that will be imported after env is loaded\n const serverModuleCode = `\nimport { startProdServer, loadConfig } from 'heliumts/server';\n${manifestCode}\n\nexport async function start() {\n const config = await loadConfig();\n\n startProdServer({\n config,\n registerHandlers: (registry, httpRouter, seoRouter) => {\n registerAll(registry);\n httpRouter.registerRoutes(httpHandlers);\n seoRouter.registerRoutes(seoMetadataHandlers);\n seoRouter.setPageRoutePatterns(pageRoutePatterns);\n if (middlewareHandler) {\n registry.setMiddleware(middlewareHandler);\n httpRouter.setMiddleware(middlewareHandler);\n }\n },\n ssrPages,\n appShell,\n workers\n });\n}\n`;\n\n // Create the entry loader that loads env first, then imports the server module\n const entryCode = `\n// Load environment variables FIRST, before any other imports\nimport './env-loader.js';\n// Now import and start the server (this ensures handlers load after env)\nimport { start } from './server-module.js';\nawait start();\n`;\n\n const envLoaderCode = `\nimport { loadEnvFiles, injectEnvToProcess, log } from 'heliumts/server';\nconst envRoot = process.cwd();\nlog('info', \\`Loading .env files from: \\${envRoot}\\`);\nconst envVars = loadEnvFiles({ mode: 'production' });\ninjectEnvToProcess(envVars);\nif (Object.keys(envVars).length > 0) {\n log('info', \\`Loaded \\${Object.keys(envVars).length} environment variable(s) from .env files\\`);\n} else {\n log('info', 'No .env files found (using platform environment variables if available)');\n}\n`;\n\n const heliumDir = path.join(root, \"node_modules\", \".heliumts\");\n if (!fs.existsSync(heliumDir)) {\n fs.mkdirSync(heliumDir, { recursive: true });\n }\n const entryPath = path.join(heliumDir, \"server-entry.ts\");\n const envLoaderPath = path.join(heliumDir, \"env-loader.ts\");\n const serverModuleSrcPath = path.join(heliumDir, \"server-module.ts\");\n const ssrClientStubPath = path.join(heliumDir, \"ssr-client-stub.ts\");\n const ssrTransitionsStubPath = path.join(heliumDir, \"ssr-transitions-stub.ts\");\n const ssrPrefetchStubPath = path.join(heliumDir, \"ssr-prefetch-stub.ts\");\n\n const ssrClientStubCode = `\nimport React from 'react';\n\nexport const RouterContext = React.createContext(null);\n\nfunction getSSRRouterSnapshot() {\n const snapshot = globalThis.__HELIUM_SSR_ROUTER__ as\n | { path?: string; params?: Record<string, string | string[]>; search?: string }\n | undefined;\n\n if (!snapshot || typeof snapshot !== 'object') {\n return {\n path: '/',\n params: {},\n search: '',\n };\n }\n\n return {\n path: typeof snapshot.path === 'string' ? snapshot.path : '/',\n params: snapshot.params && typeof snapshot.params === 'object' ? snapshot.params : {},\n search: typeof snapshot.search === 'string' ? snapshot.search : '',\n };\n}\n\nexport function useRouter() {\n const snapshot = getSSRRouterSnapshot();\n return {\n path: snapshot.path,\n params: snapshot.params,\n searchParams: new URLSearchParams(snapshot.search),\n push: () => {},\n replace: () => {},\n on: () => () => {},\n status: 200,\n isNavigating: false,\n isPending: false,\n };\n}\n\nexport function Link(props: { href?: string; children?: React.ReactNode } & Record<string, unknown>) {\n const { href = '#', children, ...rest } = props || {};\n return React.createElement('a', { href, ...rest }, children);\n}\n\nexport function Redirect() {\n return null;\n}\n\nexport function AppRouter() {\n return null;\n}\n\nexport function useCall() {\n return {\n call: async () => null,\n isCalling: false,\n error: null,\n };\n}\n\nexport function useFetch() {\n return {\n data: null,\n isLoading: false,\n error: null,\n refetch: async () => undefined,\n };\n}\n\nexport class RpcError extends Error {}\n\nexport function getRpcTransport() {\n return 'websocket';\n}\n\nexport function isAutoHttpOnMobileEnabled() {\n return false;\n}\n\nexport function preconnect() {}\n\nexport function isSSR() {\n return true;\n}\n`;\n\n const ssrTransitionsStubCode = `\nimport React from 'react';\n\nexport function useDeferredNavigation() {\n return {\n path: '/',\n deferredPath: '/',\n isStale: false,\n isPending: false,\n isTransitioning: false,\n };\n}\n\nexport function PageTransition({ children }: { children?: React.ReactNode }) {\n return React.createElement(React.Fragment, null, children);\n}\n`;\n\n const ssrPrefetchStubCode = `\nexport function prefetchRoute() {}\nexport function clearPrefetchCache() {}\n`;\n\n fs.writeFileSync(entryPath, entryCode);\n fs.writeFileSync(envLoaderPath, envLoaderCode);\n fs.writeFileSync(serverModuleSrcPath, serverModuleCode);\n // Generate a client-side stub for heliumts/server that provides method stubs\n // (e.g. { __id: 'subscribeWaitlist' }) so client components that import\n // user-defined methods from 'heliumts/server' resolve correctly during\n // the esbuild server bundle (mirroring the Vite plugin's virtual module).\n const rpcClientStubPath = path.join(heliumDir, \"rpc-client-stub.ts\");\n const rpcClientStubCode = generateClientModule(serverExports.methods);\n fs.writeFileSync(rpcClientStubPath, rpcClientStubCode);\n\n fs.writeFileSync(ssrClientStubPath, ssrClientStubCode);\n fs.writeFileSync(ssrTransitionsStubPath, ssrTransitionsStubCode);\n fs.writeFileSync(ssrPrefetchStubPath, ssrPrefetchStubCode);\n\n // Bundle with esbuild\n try {\n await esbuild({\n entryPoints: [entryPath],\n outfile: path.join(root, \"dist\", \"server.js\"),\n bundle: true,\n platform: \"node\",\n format: \"esm\",\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n plugins: [\n {\n name: \"helium-ssr-client-alias\",\n setup(build) {\n build.onResolve({ filter: /^heliumts\\/client$/ }, () => ({ path: ssrClientStubPath }));\n build.onResolve({ filter: /^heliumts\\/client\\/transitions$/ }, () => ({ path: ssrTransitionsStubPath }));\n build.onResolve({ filter: /^heliumts\\/client\\/prefetch$/ }, () => ({ path: ssrPrefetchStubPath }));\n // Intercept heliumts/server imports from client code\n // and redirect to the RPC stub module (method stubs).\n // Server-side files and generated server-module resolve normally.\n const serverDirAbs = path.join(root, \"src\", \"server\");\n build.onResolve({ filter: /^heliumts\\/server$/ }, (args) => {\n const importer = args.importer;\n // Allow the generated server-module and env-loader to\n // import the real heliumts/server framework exports.\n if (importer.startsWith(heliumDir) || importer.startsWith(serverDirAbs)) {\n return undefined; // let esbuild resolve normally\n }\n return { path: rpcClientStubPath };\n });\n },\n },\n ],\n external: [\n // External common database and heavy dependencies\n \"mongodb\",\n \"mongoose\",\n \"pg\",\n \"mysql\",\n \"mysql2\",\n \"sqlite3\",\n \"better-sqlite3\",\n \"redis\",\n // Node.js built-ins are automatically external, but let's be explicit\n \"crypto\",\n \"fs\",\n \"path\",\n \"http\",\n \"https\",\n \"stream\",\n \"zlib\",\n \"util\",\n ],\n target: \"node18\",\n metafile: true,\n banner: {\n js: \"import { createRequire } from 'module'; const require = createRequire(import.meta.url);\",\n },\n });\n\n // Display server build output\n const serverOutputPath = path.relative(root, path.join(root, \"dist\", \"server.js\"));\n const serverStats = fs.statSync(path.join(root, \"dist\", \"server.js\"));\n const serverSizeKB = (serverStats.size / 1024).toFixed(2);\n log(\"info\", ` ${serverOutputPath.padEnd(35)} ${serverSizeKB.padStart(8)} kB`);\n\n log(\"info\", \"Server build complete.\");\n\n // Transpile helium.config.ts to helium.config.js if it exists\n const configTsPath = path.join(root, \"helium.config.ts\");\n if (fs.existsSync(configTsPath)) {\n log(\"info\", \"Transpiling helium.config.ts...\");\n try {\n await esbuild({\n entryPoints: [configTsPath],\n outfile: path.join(root, \"dist\", \"helium.config.js\"),\n bundle: false,\n platform: \"node\",\n format: \"esm\",\n target: \"node18\",\n });\n log(\"info\", \"Config file transpiled to dist/helium.config.js\");\n } catch (e) {\n log(\"warn\", \"Failed to transpile config file:\", e);\n log(\"warn\", \"You may need to manually rename helium.config.ts to helium.config.js\");\n }\n } else {\n // Check if .js or .mjs config exists and copy it to dist\n const configJsPath = path.join(root, \"helium.config.js\");\n const configMjsPath = path.join(root, \"helium.config.mjs\");\n\n if (fs.existsSync(configJsPath)) {\n fs.copyFileSync(configJsPath, path.join(root, \"dist\", \"helium.config.js\"));\n log(\"info\", \"Copied helium.config.js to dist/\");\n } else if (fs.existsSync(configMjsPath)) {\n fs.copyFileSync(configMjsPath, path.join(root, \"dist\", \"helium.config.mjs\"));\n log(\"info\", \"Copied helium.config.mjs to dist/\");\n }\n }\n\n log(\"info\", \"--------------------------------\");\n log(\"info\", \"✓ Build finished successfully.\");\n log(\"info\", \"▶ Run 'helium start' to start the production server.\");\n\n // Exit cleanly after build completes\n process.exit(0);\n } catch (e) {\n log(\"error\", \"Server build failed:\", e);\n process.exit(1);\n }\n});\n\ncli.command(\"start\", \"Start production server\").action(async () => {\n const serverPath = path.join(root, \"dist\", \"server.js\");\n if (!fs.existsSync(serverPath)) {\n log(\"error\", 'Server build not found. Run \"helium build\" first.');\n process.exit(1);\n }\n\n // When running in production, look for config in dist directory first\n // This allows the transpiled config to be found\n const server = spawn(\"node\", [serverPath], {\n stdio: \"inherit\",\n shell: true,\n env: { ...process.env, HELIUM_CONFIG_DIR: path.join(root, \"dist\") },\n });\n server.on(\"close\", (code) => {\n process.exit(code || 0);\n });\n});\n\ncli.help();\ncli.parse();\n"]}
@@ -60,9 +60,29 @@ export interface HeliumRpcSecurityConfig {
60
60
  * Security configuration for HTTP responses.
61
61
  */
62
62
  export interface HeliumSecurityConfig {
63
+ /**
64
+ * Apply Helium's built-in default security headers.
65
+ *
66
+ * When set to `false`, Helium will not set default security headers,
67
+ * allowing full manual control via `headerOverrides` and your own server logic.
68
+ *
69
+ * @default true
70
+ */
71
+ defaultHeaders?: boolean;
72
+ /**
73
+ * Header overrides applied after Helium's default security headers.
74
+ *
75
+ * - Set a header value to override (or add) that header.
76
+ * - Set a header value to `null` to remove that header.
77
+ *
78
+ * This is useful for custom framing policies, custom permissions policy,
79
+ * or disabling a specific default header.
80
+ */
81
+ headerOverrides?: Record<string, string | null>;
63
82
  /**
64
83
  * Content-Security-Policy header value.
65
84
  * Set to a CSP string to enable, or omit to skip CSP.
85
+ * Applied when `defaultHeaders` is enabled.
66
86
  *
67
87
  * @default undefined (no CSP header)
68
88
  */
@@ -70,6 +90,7 @@ export interface HeliumSecurityConfig {
70
90
  /**
71
91
  * Enable Strict-Transport-Security header.
72
92
  * Set to false to disable HSTS.
93
+ * Applied when `defaultHeaders` is enabled.
73
94
  *
74
95
  * @default true
75
96
  */
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACpC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACpC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IACzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAEhC;;;;;OAKG;IACH,GAAG,CAAC,EAAE;QACF;;;;;;;;;;;;;;;;;;WAkBG;QACH,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;QAE1C;;;;;;;;;WASG;QACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAE3B;;;;;WAKG;QACH,WAAW,CAAC,EAAE,uBAAuB,CAAC;QAEtC;;;;;WAKG;QACH,QAAQ,CAAC,EAAE,uBAAuB,CAAC;QAEnC;;;;;WAKG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QAErB;;;;;WAKG;QACH,YAAY,CAAC,EAAE,MAAM,CAAC;QAEtB;;;;;WAKG;QACH,YAAY,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;CACL;AAgBD;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAAC,IAAI,GAAE,MAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAuCpF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,YAAiB,GAAG,MAAM,CAEpE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,YAAiB,GAAG,QAAQ,CAAC,uBAAuB,CAAC,CASjG;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,YAAiB,GAAG,QAAQ,CAAC,uBAAuB,CAAC,CAOjG;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,YAAiB;;;;;;EAQrD;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACrC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IACzC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,YAAiB,GAAG,wBAAwB,CAMtF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,SAE/B"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACpC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACpC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IAEhD;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IACzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAEhC;;;;;OAKG;IACH,GAAG,CAAC,EAAE;QACF;;;;;;;;;;;;;;;;;;WAkBG;QACH,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;QAE1C;;;;;;;;;WASG;QACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAE3B;;;;;WAKG;QACH,WAAW,CAAC,EAAE,uBAAuB,CAAC;QAEtC;;;;;WAKG;QACH,QAAQ,CAAC,EAAE,uBAAuB,CAAC;QAEnC;;;;;WAKG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QAErB;;;;;WAKG;QACH,YAAY,CAAC,EAAE,MAAM,CAAC;QAEtB;;;;;WAKG;QACH,YAAY,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;CACL;AAgBD;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAAC,IAAI,GAAE,MAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAuCpF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,YAAiB,GAAG,MAAM,CAEpE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,YAAiB,GAAG,QAAQ,CAAC,uBAAuB,CAAC,CASjG;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,YAAiB,GAAG,QAAQ,CAAC,uBAAuB,CAAC,CAOjG;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,YAAiB;;;;;;EAQrD;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACrC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IACzC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,YAAiB,GAAG,wBAAwB,CAMtF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,SAE/B"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAkOpC,MAAM,oBAAoB,GAAsC;IAC5D,mBAAmB,EAAE,EAAE;IACvB,oBAAoB,EAAE,GAAG;IACzB,iBAAiB,EAAE,KAAK;IACxB,eAAe,EAAE,KAAK;CACzB,CAAC;AAEF,MAAM,mBAAmB,GAAsC;IAC3D,OAAO,EAAE,IAAI;IACb,SAAS,EAAE,IAAI;CAClB,CAAC;AAEF,IAAI,YAAY,GAAwB,IAAI,CAAC;AAE7C;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,OAAO,CAAC,GAAG,EAAE;IACzD,IAAI,YAAY,EAAE,CAAC;QACf,OAAO,YAAY,CAAC;IACxB,CAAC;IAED,kEAAkE;IAClE,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC;IAExD,wDAAwD;IACxD,+EAA+E;IAC/E,MAAM,WAAW,GAAG,CAAC,kBAAkB,EAAE,mBAAmB,EAAE,kBAAkB,CAAC,CAAC;IAElF,uEAAuE;IACvE,MAAM,WAAW,GAAG,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAEpE,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACnC,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;YACnC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACrD,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC5B,IAAI,CAAC;oBACD,MAAM,OAAO,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC;oBAC/C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,GAAG,OAAO,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;oBAC7E,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;oBACpC,YAAY,GAAG,MAAM,CAAC;oBACtB,OAAO,MAAM,CAAC;gBAClB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACX,yEAAyE;oBACzE,IAAI,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,YAAY,KAAK,IAAI,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,4BAA4B,EAAE,CAAC;wBACnH,OAAO,CAAC,IAAI,CAAC,wBAAwB,UAAU,8DAA8D,CAAC,CAAC;oBACnH,CAAC;yBAAM,CAAC;wBACJ,OAAO,CAAC,IAAI,CAAC,uCAAuC,UAAU,GAAG,EAAE,GAAG,CAAC,CAAC;oBAC5E,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;IAED,YAAY,GAAG,EAAE,CAAC;IAClB,OAAO,YAAY,CAAC;AACxB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAuB,EAAE;IACxD,OAAO,MAAM,CAAC,eAAe,IAAI,CAAC,CAAC;AACvC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,SAAuB,EAAE;IAC1D,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC;IAEjC,OAAO;QACH,mBAAmB,EAAE,GAAG,EAAE,mBAAmB,IAAI,oBAAoB,CAAC,mBAAmB;QACzF,oBAAoB,EAAE,GAAG,EAAE,oBAAoB,IAAI,oBAAoB,CAAC,oBAAoB;QAC5F,iBAAiB,EAAE,GAAG,EAAE,iBAAiB,IAAI,oBAAoB,CAAC,iBAAiB;QACnF,eAAe,EAAE,GAAG,EAAE,eAAe,IAAI,oBAAoB,CAAC,eAAe;KAChF,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,SAAuB,EAAE;IAC1D,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,EAAE,WAAW,CAAC;IAEpC,OAAO;QACH,OAAO,EAAE,GAAG,EAAE,OAAO,IAAI,mBAAmB,CAAC,OAAO;QACpD,SAAS,EAAE,GAAG,EAAE,SAAS,IAAI,mBAAmB,CAAC,SAAS;KAC7D,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,SAAuB,EAAE;IAClD,OAAO;QACH,WAAW,EAAE,oBAAoB,CAAC,MAAM,CAAC;QACzC,QAAQ,EAAE,oBAAoB,CAAC,MAAM,CAAC;QACtC,WAAW,EAAE,MAAM,CAAC,GAAG,EAAE,WAAW,IAAI,OAAS;QACjD,YAAY,EAAE,MAAM,CAAC,GAAG,EAAE,YAAY,IAAI,EAAE;QAC5C,YAAY,EAAE,MAAM,CAAC,GAAG,EAAE,YAAY,IAAI,OAAS;KACtD,CAAC;AACN,CAAC;AAYD;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAuB,EAAE;IACxD,OAAO;QACH,SAAS,EAAE,MAAM,CAAC,GAAG,EAAE,SAAS,IAAI,WAAW;QAC/C,gBAAgB,EAAE,MAAM,CAAC,GAAG,EAAE,gBAAgB,IAAI,KAAK;QACvD,eAAe,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC,eAAe;KAChE,CAAC;AACN,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC5B,YAAY,GAAG,IAAI,CAAC;AACxB,CAAC","sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport { pathToFileURL } from \"url\";\n\n/**\n * WebSocket per-message compression configuration.\n * Uses the permessage-deflate extension to compress messages on the wire.\n */\nexport interface HeliumCompressionConfig {\n /**\n * Enable WebSocket per-message compression (permessage-deflate extension).\n * When enabled, messages are compressed before sending to reduce bandwidth usage.\n *\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Minimum message size in bytes to apply compression.\n * Messages smaller than this threshold will not be compressed to avoid overhead.\n * Only applies when compression is enabled.\n *\n * @default 1024 (1KB)\n */\n threshold?: number;\n}\n\n/**\n * RPC security and rate limiting configuration.\n * Controls WebSocket connection limits, message rate limits, and token-based authentication.\n */\nexport interface HeliumRpcSecurityConfig {\n /**\n * Maximum number of concurrent WebSocket connections allowed per IP address.\n * Helps prevent a single client from exhausting connection resources.\n * Set to 0 to disable this limit.\n *\n * @default 10\n */\n maxConnectionsPerIP?: number;\n\n /**\n * Maximum number of RPC messages allowed per connection within the time window.\n * Helps prevent abuse by limiting message throughput per connection.\n * Set to 0 to disable rate limiting.\n *\n * @default 100\n */\n maxMessagesPerWindow?: number;\n\n /**\n * Time window in milliseconds for rate limiting.\n * Rate limits reset after this duration.\n *\n * @default 60000 (1 minute)\n */\n rateLimitWindowMs?: number;\n\n /**\n * WebSocket connection token validity duration in milliseconds.\n * Tokens are generated server-side and must be used within this timeframe.\n * Shorter durations improve security but may cause issues with slow networks.\n *\n * @default 30000 (30 seconds)\n */\n tokenValidityMs?: number;\n}\n\n/**\n * Security configuration for HTTP responses.\n */\nexport interface HeliumSecurityConfig {\n /**\n * Content-Security-Policy header value.\n * Set to a CSP string to enable, or omit to skip CSP.\n *\n * @default undefined (no CSP header)\n */\n contentSecurityPolicy?: string;\n\n /**\n * Enable Strict-Transport-Security header.\n * Set to false to disable HSTS.\n *\n * @default true\n */\n hsts?: boolean;\n\n /**\n * Allowed CORS origins.\n * Set to [\"*\"] to allow all origins, or provide specific origins.\n * Empty array or omit to restrict to same-origin only (default, most secure).\n *\n * @default [] (same-origin only)\n */\n corsOrigins?: string[];\n}\n\n/**\n * Helium framework configuration.\n *\n * Configure your Helium application behavior including RPC transport settings,\n * compression, security, and proxy configuration for production deployments.\n */\nexport interface HeliumConfig {\n /**\n * Number of proxy levels to trust when extracting client IP addresses.\n *\n * This setting is crucial for deployments behind reverse proxies, load balancers,\n * or CDNs (like Vercel, Cloudflare, AWS ALB, etc.). It determines how the framework\n * extracts the real client IP from headers like X-Forwarded-For.\n *\n * **How it works:**\n * When behind proxies, the X-Forwarded-For header contains a chain of IPs:\n * `X-Forwarded-For: <client-ip>, <proxy1-ip>, <proxy2-ip>`\n *\n * This setting tells Helium how many proxy IPs to skip from the right to find the real client IP.\n *\n * **Values:**\n * - `0`: Don't trust any proxies, use direct connection IP (default, most secure)\n * - `1`: Trust 1 proxy level (recommended for most platforms: Vercel, Netlify, Railway)\n * - `2+`: Trust multiple proxy levels (for complex setups like Cloudflare → Load Balancer → Your Server)\n *\n * **Common configurations:**\n * - Local development: `0`\n * - Vercel/Netlify/Railway: `1`\n * - Cloudflare → Your server: `1` or `2`\n * - AWS ALB → EC2: `1`\n * - Nginx → Node.js: `1`\n * - Cloudflare → AWS ALB → EC2: `2`\n *\n * **Security note:** Setting this too high can allow IP spoofing. Only trust as many\n * proxy levels as you actually have in your infrastructure.\n *\n * This setting applies to both HTTP requests and WebSocket connections.\n *\n * @default 0\n */\n trustProxyDepth?: number;\n\n /**\n * HTTP response security configuration.\n * Controls CORS, CSP, HSTS, and other security headers.\n */\n security?: HeliumSecurityConfig;\n\n /**\n * RPC transport configuration.\n *\n * Configure the WebSocket-based RPC layer including compression\n * and security settings.\n */\n rpc?: {\n /**\n * Client-side transport mode for RPC calls.\n *\n * - `\"websocket\"` (default): Uses persistent WebSocket connection\n * - ✅ Lower latency for subsequent calls (connection reuse)\n * - ✅ Real-time bidirectional communication ready\n * - ⚠️ Higher initial connection overhead\n *\n * - `\"http\"`: Uses HTTP POST requests for each RPC call\n * - ✅ Better performance on mobile/cellular networks (HTTP/2 optimizations)\n * - ✅ No connection state to maintain\n * - ⚠️ Slightly higher per-request overhead on fast networks\n *\n * - `\"auto\"`: Automatically selects based on network conditions\n * - Uses HTTP on cellular/slow networks when `autoHttpOnMobile` is true\n * - Uses WebSocket on fast networks (WiFi, wired)\n *\n * @default \"websocket\"\n */\n transport?: \"http\" | \"websocket\" | \"auto\";\n\n /**\n * Automatically switch to HTTP transport on mobile/cellular networks.\n *\n * When enabled and `transport` is `\"auto\"`, the client will use HTTP\n * instead of WebSocket on cellular connections (4G/LTE, 5G) and slow\n * connections (2G, 3G). This improves performance on mobile networks\n * where HTTP/2 is more efficient due to carrier network optimizations.\n *\n * @default false\n */\n autoHttpOnMobile?: boolean;\n\n /**\n * WebSocket per-message compression configuration.\n *\n * Enable and configure the permessage-deflate extension to compress\n * messages on the wire, reducing bandwidth usage.\n */\n compression?: HeliumCompressionConfig;\n\n /**\n * RPC security and rate limiting configuration.\n *\n * Configure connection limits, message rate limits, and token validity\n * to protect your RPC endpoints from abuse.\n */\n security?: HeliumRpcSecurityConfig;\n\n /**\n * Maximum HTTP request body size in bytes.\n * Requests exceeding this limit receive a 413 status.\n *\n * @default 1048576 (1 MB)\n */\n maxBodySize?: number;\n\n /**\n * Maximum number of RPC calls in a single batch request.\n * Batches exceeding this limit are rejected.\n *\n * @default 20\n */\n maxBatchSize?: number;\n\n /**\n * Maximum WebSocket message payload size in bytes.\n * Messages exceeding this limit cause the connection to be closed.\n *\n * @default 1048576 (1 MB)\n */\n maxWsPayload?: number;\n };\n}\n\nconst DEFAULT_RPC_SECURITY: Required<HeliumRpcSecurityConfig> = {\n maxConnectionsPerIP: 10,\n maxMessagesPerWindow: 100,\n rateLimitWindowMs: 60000,\n tokenValidityMs: 30000,\n};\n\nconst DEFAULT_COMPRESSION: Required<HeliumCompressionConfig> = {\n enabled: true,\n threshold: 1024,\n};\n\nlet cachedConfig: HeliumConfig | null = null;\n\n/**\n * Load Helium configuration from the project root.\n * Searches for helium.config.js, helium.config.mjs, or helium.config.ts.\n * Results are cached for the lifetime of the process.\n *\n * In production, the build process automatically transpiles .ts config files\n * to .js in the dist directory. The loader checks dist/ first when available.\n *\n * @internal - Used by framework internals only\n */\nexport async function loadConfig(root: string = process.cwd()): Promise<HeliumConfig> {\n if (cachedConfig) {\n return cachedConfig;\n }\n\n // Check if there's a custom config directory (used in production)\n const configDir = process.env.HELIUM_CONFIG_DIR || root;\n\n // Prioritize .js/.mjs (work in both dev and production)\n // .ts files work in dev with Vite but fail in production without transpilation\n const configFiles = [\"helium.config.js\", \"helium.config.mjs\", \"helium.config.ts\"];\n\n // In production with HELIUM_CONFIG_DIR set, check dist directory first\n const searchPaths = configDir !== root ? [configDir, root] : [root];\n\n for (const searchPath of searchPaths) {\n for (const configFile of configFiles) {\n const configPath = path.join(searchPath, configFile);\n if (fs.existsSync(configPath)) {\n try {\n const fileUrl = pathToFileURL(configPath).href;\n const module = await import(/* @vite-ignore */ `${fileUrl}?t=${Date.now()}`);\n const config = module.default || {};\n cachedConfig = config;\n return config;\n } catch (err) {\n // In production, .ts files will fail to load without a TypeScript loader\n if (configFile.endsWith(\".ts\") && err instanceof Error && \"code\" in err && err.code === \"ERR_UNKNOWN_FILE_EXTENSION\") {\n console.warn(`[Helium] Cannot load ${configFile} in production. The build process should have transpiled it.`);\n } else {\n console.warn(`[Helium] Failed to load config from ${configFile}:`, err);\n }\n }\n }\n }\n }\n\n cachedConfig = {};\n return cachedConfig;\n}\n\n/**\n * Get the proxy trust depth from config.\n * Used for extracting client IPs from X-Forwarded-For headers.\n *\n * @internal - Used by framework internals only\n */\nexport function getTrustProxyDepth(config: HeliumConfig = {}): number {\n return config.trustProxyDepth ?? 0;\n}\n\n/**\n * Get RPC security configuration with defaults applied.\n * Returns rate limiting, connection limits, and token settings.\n *\n * @internal - Used by framework internals only\n */\nexport function getRpcSecurityConfig(config: HeliumConfig = {}): Required<HeliumRpcSecurityConfig> {\n const src = config.rpc?.security;\n\n return {\n maxConnectionsPerIP: src?.maxConnectionsPerIP ?? DEFAULT_RPC_SECURITY.maxConnectionsPerIP,\n maxMessagesPerWindow: src?.maxMessagesPerWindow ?? DEFAULT_RPC_SECURITY.maxMessagesPerWindow,\n rateLimitWindowMs: src?.rateLimitWindowMs ?? DEFAULT_RPC_SECURITY.rateLimitWindowMs,\n tokenValidityMs: src?.tokenValidityMs ?? DEFAULT_RPC_SECURITY.tokenValidityMs,\n };\n}\n\n/**\n * Get WebSocket compression configuration with defaults applied.\n *\n * @internal - Used by framework internals only\n */\nexport function getCompressionConfig(config: HeliumConfig = {}): Required<HeliumCompressionConfig> {\n const src = config.rpc?.compression;\n\n return {\n enabled: src?.enabled ?? DEFAULT_COMPRESSION.enabled,\n threshold: src?.threshold ?? DEFAULT_COMPRESSION.threshold,\n };\n}\n\n/**\n * Get complete RPC configuration including compression, and security.\n *\n * @internal - Used by framework internals only\n */\nexport function getRpcConfig(config: HeliumConfig = {}) {\n return {\n compression: getCompressionConfig(config),\n security: getRpcSecurityConfig(config),\n maxBodySize: config.rpc?.maxBodySize ?? 1_048_576,\n maxBatchSize: config.rpc?.maxBatchSize ?? 20,\n maxWsPayload: config.rpc?.maxWsPayload ?? 1_048_576,\n };\n}\n\n/**\n * Client-side RPC transport configuration.\n * This is injected into the client bundle at build time.\n */\nexport interface RpcClientTransportConfig {\n transport: \"http\" | \"websocket\" | \"auto\";\n autoHttpOnMobile: boolean;\n tokenValidityMs: number;\n}\n\n/**\n * Get client-side RPC transport configuration.\n * This configuration is injected into the client bundle via Vite defines.\n *\n * @internal - Used by framework internals only\n */\nexport function getRpcClientConfig(config: HeliumConfig = {}): RpcClientTransportConfig {\n return {\n transport: config.rpc?.transport ?? \"websocket\",\n autoHttpOnMobile: config.rpc?.autoHttpOnMobile ?? false,\n tokenValidityMs: getRpcSecurityConfig(config).tokenValidityMs,\n };\n}\n\n/**\n * Clear the cached configuration.\n * Useful for testing or when you need to reload config.\n *\n * @internal - Used by framework internals only\n */\nexport function clearConfigCache() {\n cachedConfig = null;\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport { pathToFileURL } from \"url\";\n\n/**\n * WebSocket per-message compression configuration.\n * Uses the permessage-deflate extension to compress messages on the wire.\n */\nexport interface HeliumCompressionConfig {\n /**\n * Enable WebSocket per-message compression (permessage-deflate extension).\n * When enabled, messages are compressed before sending to reduce bandwidth usage.\n *\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Minimum message size in bytes to apply compression.\n * Messages smaller than this threshold will not be compressed to avoid overhead.\n * Only applies when compression is enabled.\n *\n * @default 1024 (1KB)\n */\n threshold?: number;\n}\n\n/**\n * RPC security and rate limiting configuration.\n * Controls WebSocket connection limits, message rate limits, and token-based authentication.\n */\nexport interface HeliumRpcSecurityConfig {\n /**\n * Maximum number of concurrent WebSocket connections allowed per IP address.\n * Helps prevent a single client from exhausting connection resources.\n * Set to 0 to disable this limit.\n *\n * @default 10\n */\n maxConnectionsPerIP?: number;\n\n /**\n * Maximum number of RPC messages allowed per connection within the time window.\n * Helps prevent abuse by limiting message throughput per connection.\n * Set to 0 to disable rate limiting.\n *\n * @default 100\n */\n maxMessagesPerWindow?: number;\n\n /**\n * Time window in milliseconds for rate limiting.\n * Rate limits reset after this duration.\n *\n * @default 60000 (1 minute)\n */\n rateLimitWindowMs?: number;\n\n /**\n * WebSocket connection token validity duration in milliseconds.\n * Tokens are generated server-side and must be used within this timeframe.\n * Shorter durations improve security but may cause issues with slow networks.\n *\n * @default 30000 (30 seconds)\n */\n tokenValidityMs?: number;\n}\n\n/**\n * Security configuration for HTTP responses.\n */\nexport interface HeliumSecurityConfig {\n /**\n * Apply Helium's built-in default security headers.\n *\n * When set to `false`, Helium will not set default security headers,\n * allowing full manual control via `headerOverrides` and your own server logic.\n *\n * @default true\n */\n defaultHeaders?: boolean;\n\n /**\n * Header overrides applied after Helium's default security headers.\n *\n * - Set a header value to override (or add) that header.\n * - Set a header value to `null` to remove that header.\n *\n * This is useful for custom framing policies, custom permissions policy,\n * or disabling a specific default header.\n */\n headerOverrides?: Record<string, string | null>;\n\n /**\n * Content-Security-Policy header value.\n * Set to a CSP string to enable, or omit to skip CSP.\n * Applied when `defaultHeaders` is enabled.\n *\n * @default undefined (no CSP header)\n */\n contentSecurityPolicy?: string;\n\n /**\n * Enable Strict-Transport-Security header.\n * Set to false to disable HSTS.\n * Applied when `defaultHeaders` is enabled.\n *\n * @default true\n */\n hsts?: boolean;\n\n /**\n * Allowed CORS origins.\n * Set to [\"*\"] to allow all origins, or provide specific origins.\n * Empty array or omit to restrict to same-origin only (default, most secure).\n *\n * @default [] (same-origin only)\n */\n corsOrigins?: string[];\n}\n\n/**\n * Helium framework configuration.\n *\n * Configure your Helium application behavior including RPC transport settings,\n * compression, security, and proxy configuration for production deployments.\n */\nexport interface HeliumConfig {\n /**\n * Number of proxy levels to trust when extracting client IP addresses.\n *\n * This setting is crucial for deployments behind reverse proxies, load balancers,\n * or CDNs (like Vercel, Cloudflare, AWS ALB, etc.). It determines how the framework\n * extracts the real client IP from headers like X-Forwarded-For.\n *\n * **How it works:**\n * When behind proxies, the X-Forwarded-For header contains a chain of IPs:\n * `X-Forwarded-For: <client-ip>, <proxy1-ip>, <proxy2-ip>`\n *\n * This setting tells Helium how many proxy IPs to skip from the right to find the real client IP.\n *\n * **Values:**\n * - `0`: Don't trust any proxies, use direct connection IP (default, most secure)\n * - `1`: Trust 1 proxy level (recommended for most platforms: Vercel, Netlify, Railway)\n * - `2+`: Trust multiple proxy levels (for complex setups like Cloudflare → Load Balancer → Your Server)\n *\n * **Common configurations:**\n * - Local development: `0`\n * - Vercel/Netlify/Railway: `1`\n * - Cloudflare → Your server: `1` or `2`\n * - AWS ALB → EC2: `1`\n * - Nginx → Node.js: `1`\n * - Cloudflare → AWS ALB → EC2: `2`\n *\n * **Security note:** Setting this too high can allow IP spoofing. Only trust as many\n * proxy levels as you actually have in your infrastructure.\n *\n * This setting applies to both HTTP requests and WebSocket connections.\n *\n * @default 0\n */\n trustProxyDepth?: number;\n\n /**\n * HTTP response security configuration.\n * Controls CORS, CSP, HSTS, and other security headers.\n */\n security?: HeliumSecurityConfig;\n\n /**\n * RPC transport configuration.\n *\n * Configure the WebSocket-based RPC layer including compression\n * and security settings.\n */\n rpc?: {\n /**\n * Client-side transport mode for RPC calls.\n *\n * - `\"websocket\"` (default): Uses persistent WebSocket connection\n * - ✅ Lower latency for subsequent calls (connection reuse)\n * - ✅ Real-time bidirectional communication ready\n * - ⚠️ Higher initial connection overhead\n *\n * - `\"http\"`: Uses HTTP POST requests for each RPC call\n * - ✅ Better performance on mobile/cellular networks (HTTP/2 optimizations)\n * - ✅ No connection state to maintain\n * - ⚠️ Slightly higher per-request overhead on fast networks\n *\n * - `\"auto\"`: Automatically selects based on network conditions\n * - Uses HTTP on cellular/slow networks when `autoHttpOnMobile` is true\n * - Uses WebSocket on fast networks (WiFi, wired)\n *\n * @default \"websocket\"\n */\n transport?: \"http\" | \"websocket\" | \"auto\";\n\n /**\n * Automatically switch to HTTP transport on mobile/cellular networks.\n *\n * When enabled and `transport` is `\"auto\"`, the client will use HTTP\n * instead of WebSocket on cellular connections (4G/LTE, 5G) and slow\n * connections (2G, 3G). This improves performance on mobile networks\n * where HTTP/2 is more efficient due to carrier network optimizations.\n *\n * @default false\n */\n autoHttpOnMobile?: boolean;\n\n /**\n * WebSocket per-message compression configuration.\n *\n * Enable and configure the permessage-deflate extension to compress\n * messages on the wire, reducing bandwidth usage.\n */\n compression?: HeliumCompressionConfig;\n\n /**\n * RPC security and rate limiting configuration.\n *\n * Configure connection limits, message rate limits, and token validity\n * to protect your RPC endpoints from abuse.\n */\n security?: HeliumRpcSecurityConfig;\n\n /**\n * Maximum HTTP request body size in bytes.\n * Requests exceeding this limit receive a 413 status.\n *\n * @default 1048576 (1 MB)\n */\n maxBodySize?: number;\n\n /**\n * Maximum number of RPC calls in a single batch request.\n * Batches exceeding this limit are rejected.\n *\n * @default 20\n */\n maxBatchSize?: number;\n\n /**\n * Maximum WebSocket message payload size in bytes.\n * Messages exceeding this limit cause the connection to be closed.\n *\n * @default 1048576 (1 MB)\n */\n maxWsPayload?: number;\n };\n}\n\nconst DEFAULT_RPC_SECURITY: Required<HeliumRpcSecurityConfig> = {\n maxConnectionsPerIP: 10,\n maxMessagesPerWindow: 100,\n rateLimitWindowMs: 60000,\n tokenValidityMs: 30000,\n};\n\nconst DEFAULT_COMPRESSION: Required<HeliumCompressionConfig> = {\n enabled: true,\n threshold: 1024,\n};\n\nlet cachedConfig: HeliumConfig | null = null;\n\n/**\n * Load Helium configuration from the project root.\n * Searches for helium.config.js, helium.config.mjs, or helium.config.ts.\n * Results are cached for the lifetime of the process.\n *\n * In production, the build process automatically transpiles .ts config files\n * to .js in the dist directory. The loader checks dist/ first when available.\n *\n * @internal - Used by framework internals only\n */\nexport async function loadConfig(root: string = process.cwd()): Promise<HeliumConfig> {\n if (cachedConfig) {\n return cachedConfig;\n }\n\n // Check if there's a custom config directory (used in production)\n const configDir = process.env.HELIUM_CONFIG_DIR || root;\n\n // Prioritize .js/.mjs (work in both dev and production)\n // .ts files work in dev with Vite but fail in production without transpilation\n const configFiles = [\"helium.config.js\", \"helium.config.mjs\", \"helium.config.ts\"];\n\n // In production with HELIUM_CONFIG_DIR set, check dist directory first\n const searchPaths = configDir !== root ? [configDir, root] : [root];\n\n for (const searchPath of searchPaths) {\n for (const configFile of configFiles) {\n const configPath = path.join(searchPath, configFile);\n if (fs.existsSync(configPath)) {\n try {\n const fileUrl = pathToFileURL(configPath).href;\n const module = await import(/* @vite-ignore */ `${fileUrl}?t=${Date.now()}`);\n const config = module.default || {};\n cachedConfig = config;\n return config;\n } catch (err) {\n // In production, .ts files will fail to load without a TypeScript loader\n if (configFile.endsWith(\".ts\") && err instanceof Error && \"code\" in err && err.code === \"ERR_UNKNOWN_FILE_EXTENSION\") {\n console.warn(`[Helium] Cannot load ${configFile} in production. The build process should have transpiled it.`);\n } else {\n console.warn(`[Helium] Failed to load config from ${configFile}:`, err);\n }\n }\n }\n }\n }\n\n cachedConfig = {};\n return cachedConfig;\n}\n\n/**\n * Get the proxy trust depth from config.\n * Used for extracting client IPs from X-Forwarded-For headers.\n *\n * @internal - Used by framework internals only\n */\nexport function getTrustProxyDepth(config: HeliumConfig = {}): number {\n return config.trustProxyDepth ?? 0;\n}\n\n/**\n * Get RPC security configuration with defaults applied.\n * Returns rate limiting, connection limits, and token settings.\n *\n * @internal - Used by framework internals only\n */\nexport function getRpcSecurityConfig(config: HeliumConfig = {}): Required<HeliumRpcSecurityConfig> {\n const src = config.rpc?.security;\n\n return {\n maxConnectionsPerIP: src?.maxConnectionsPerIP ?? DEFAULT_RPC_SECURITY.maxConnectionsPerIP,\n maxMessagesPerWindow: src?.maxMessagesPerWindow ?? DEFAULT_RPC_SECURITY.maxMessagesPerWindow,\n rateLimitWindowMs: src?.rateLimitWindowMs ?? DEFAULT_RPC_SECURITY.rateLimitWindowMs,\n tokenValidityMs: src?.tokenValidityMs ?? DEFAULT_RPC_SECURITY.tokenValidityMs,\n };\n}\n\n/**\n * Get WebSocket compression configuration with defaults applied.\n *\n * @internal - Used by framework internals only\n */\nexport function getCompressionConfig(config: HeliumConfig = {}): Required<HeliumCompressionConfig> {\n const src = config.rpc?.compression;\n\n return {\n enabled: src?.enabled ?? DEFAULT_COMPRESSION.enabled,\n threshold: src?.threshold ?? DEFAULT_COMPRESSION.threshold,\n };\n}\n\n/**\n * Get complete RPC configuration including compression, and security.\n *\n * @internal - Used by framework internals only\n */\nexport function getRpcConfig(config: HeliumConfig = {}) {\n return {\n compression: getCompressionConfig(config),\n security: getRpcSecurityConfig(config),\n maxBodySize: config.rpc?.maxBodySize ?? 1_048_576,\n maxBatchSize: config.rpc?.maxBatchSize ?? 20,\n maxWsPayload: config.rpc?.maxWsPayload ?? 1_048_576,\n };\n}\n\n/**\n * Client-side RPC transport configuration.\n * This is injected into the client bundle at build time.\n */\nexport interface RpcClientTransportConfig {\n transport: \"http\" | \"websocket\" | \"auto\";\n autoHttpOnMobile: boolean;\n tokenValidityMs: number;\n}\n\n/**\n * Get client-side RPC transport configuration.\n * This configuration is injected into the client bundle via Vite defines.\n *\n * @internal - Used by framework internals only\n */\nexport function getRpcClientConfig(config: HeliumConfig = {}): RpcClientTransportConfig {\n return {\n transport: config.rpc?.transport ?? \"websocket\",\n autoHttpOnMobile: config.rpc?.autoHttpOnMobile ?? false,\n tokenValidityMs: getRpcSecurityConfig(config).tokenValidityMs,\n };\n}\n\n/**\n * Clear the cached configuration.\n * Useful for testing or when you need to reload config.\n *\n * @internal - Used by framework internals only\n */\nexport function clearConfigCache() {\n cachedConfig = null;\n}\n"]}
@@ -39,5 +39,12 @@ interface ProdServerOptions {
39
39
  * - Client-side navigation between pages still works via React Router
40
40
  */
41
41
  export declare function startProdServer(options: ProdServerOptions): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
42
+ /**
43
+ * Resolve security headers for the current request from config.
44
+ * Returns a final header map where `null` indicates the header should be removed.
45
+ *
46
+ * @internal
47
+ */
48
+ export declare function getSecurityHeaders(config: HeliumConfig): Record<string, string | null>;
42
49
  export {};
43
50
  //# sourceMappingURL=prodServer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prodServer.d.ts","sourceRoot":"","sources":["../../src/server/prodServer.ts"],"names":[],"mappings":"AAEA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAStD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAA6D,UAAU,EAAE,MAAM,UAAU,CAAC;AAMjG,UAAU,WAAW;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;CAC3B;AAED,UAAU,iBAAiB;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACxG,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,aAAa,CAAC;QAAE,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CACrK;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,wEA6hBzD"}
1
+ {"version":3,"file":"prodServer.d.ts","sourceRoot":"","sources":["../../src/server/prodServer.ts"],"names":[],"mappings":"AAEA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAStD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAA6D,UAAU,EAAE,MAAM,UAAU,CAAC;AAMjG,UAAU,WAAW;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;CAC3B;AAED,UAAU,iBAAiB;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACxG,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,aAAa,CAAC;QAAE,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CACrK;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,wEA6hBzD;AAqBD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CA4BtF"}
@@ -526,18 +526,44 @@ export function startProdServer(options) {
526
526
  * Set default security headers on every HTTP response.
527
527
  */
528
528
  function setSecurityHeaders(res, config) {
529
- res.setHeader("X-Content-Type-Options", "nosniff");
530
- res.setHeader("X-Frame-Options", "DENY");
531
- res.setHeader("X-XSS-Protection", "0");
532
- res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
533
- res.setHeader("Permissions-Policy", "camera=(), microphone=(), geolocation=()");
534
- const csp = config.security?.contentSecurityPolicy;
535
- if (csp) {
536
- res.setHeader("Content-Security-Policy", csp);
529
+ const headers = getSecurityHeaders(config);
530
+ for (const [name, value] of Object.entries(headers)) {
531
+ if (value === null) {
532
+ res.removeHeader(name);
533
+ continue;
534
+ }
535
+ res.setHeader(name, value);
537
536
  }
538
- if (config.security?.hsts !== false) {
539
- res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
537
+ }
538
+ /**
539
+ * Resolve security headers for the current request from config.
540
+ * Returns a final header map where `null` indicates the header should be removed.
541
+ *
542
+ * @internal
543
+ */
544
+ export function getSecurityHeaders(config) {
545
+ const headers = {};
546
+ const securityConfig = config.security;
547
+ if (securityConfig?.defaultHeaders !== false) {
548
+ headers["X-Content-Type-Options"] = "nosniff";
549
+ headers["X-Frame-Options"] = "DENY";
550
+ headers["X-XSS-Protection"] = "0";
551
+ headers["Referrer-Policy"] = "strict-origin-when-cross-origin";
552
+ headers["Permissions-Policy"] = "camera=(), microphone=(), geolocation=()";
553
+ const csp = securityConfig?.contentSecurityPolicy;
554
+ if (csp) {
555
+ headers["Content-Security-Policy"] = csp;
556
+ }
557
+ if (securityConfig?.hsts !== false) {
558
+ headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains";
559
+ }
560
+ }
561
+ if (securityConfig?.headerOverrides) {
562
+ for (const [name, value] of Object.entries(securityConfig.headerOverrides)) {
563
+ headers[name] = value;
564
+ }
540
565
  }
566
+ return headers;
541
567
  }
542
568
  /**
543
569
  * Handle CORS headers based on configuration.
@@ -1 +1 @@
1
- {"version":3,"file":"prodServer.js","sourceRoot":"","sources":["../../src/server/prodServer.ts"],"names":[],"mappings":"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","sourcesContent":["import { encode as msgpackEncode } from \"@msgpack/msgpack\";\nimport fs from \"fs\";\nimport http from \"http\";\nimport path from \"path\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { promisify } from \"util\";\nimport type WebSocket from \"ws\";\nimport { WebSocketServer } from \"ws\";\nimport { brotliCompress, deflate, gzip } from \"zlib\";\n\nimport { SEO_METADATA_RPC_METHOD } from \"../runtime/internalMethods.js\";\nimport { extractClientIP } from \"../utils/ipExtractor.js\";\nimport { log } from \"../utils/logger.js\";\nimport type { HeliumConfig } from \"./config.js\";\nimport { getRpcConfig, getRpcSecurityConfig, getTrustProxyDepth } from \"./config.js\";\nimport type { HeliumContext } from \"./context.js\";\nimport type { HeliumWorkerDef } from \"./defineWorker.js\";\nimport { startWorker, stopAllWorkers } from \"./defineWorker.js\";\nimport { HTTPRouter } from \"./httpRouter.js\";\nimport { injectSocialMetaIntoHtml, loadDefaultSocialMetaFromHtmlFile } from \"./meta.js\";\nimport { RateLimiter } from \"./rateLimiter.js\";\nimport { RpcRegistry } from \"./rpcRegistry.js\";\nimport { generateConnectionToken, initializeSecurity, verifyConnectionToken } from \"./security.js\";\nimport { SEOMetadataRouter } from \"./seoMetadataRouter.js\";\nimport { prepareForMsgpack } from \"./serializer.js\";\nimport { createServerSidePropsRequest, matchSSRPage, renderSSRHTML, SSRPageDef } from \"./ssr.js\";\n\nconst gzipAsync = promisify(gzip);\nconst deflateAsync = promisify(deflate);\nconst brotliCompressAsync = promisify(brotliCompress);\n\ninterface WorkerEntry {\n name: string;\n worker: HeliumWorkerDef;\n}\n\ninterface ProdServerOptions {\n port?: number;\n distDir?: string;\n staticDir?: string;\n registerHandlers: (registry: RpcRegistry, httpRouter: HTTPRouter, seoRouter: SEOMetadataRouter) => void;\n config?: HeliumConfig;\n workers?: WorkerEntry[];\n ssrPages?: SSRPageDef[];\n appShell?: (() => Promise<ComponentType<{ Component: ComponentType<Record<string, unknown>>; pageProps: Record<string, unknown>; children?: ReactNode }>>) | null;\n}\n\n/**\n * Starts a production HTTP server that:\n * - Serves static files from the dist directory\n * - Supports SSG (Static Site Generation) by serving .html files for routes (e.g., /about -> about.html)\n * - Falls back to index.html for client-side routing (SPA)\n * - Handles custom HTTP endpoints (webhooks, auth, etc.)\n * - Hosts WebSocket RPC server\n * - Starts background workers\n *\n * SSG Behavior:\n * - Production correctly serves SSG pages (e.g., /about serves about.html with pre-rendered content)\n * - This ensures search engines and social media crawlers see the correct content\n * - Client-side navigation between pages still works via React Router\n */\nexport function startProdServer(options: ProdServerOptions) {\n const {\n port = Number(process.env.PORT || 3000),\n distDir = \"dist\",\n staticDir = path.resolve(process.cwd(), distDir),\n registerHandlers,\n config = {},\n workers = [],\n ssrPages = [],\n appShell = null,\n } = options;\n\n // Load configuration\n const trustProxyDepth = getTrustProxyDepth(config);\n const rpcSecurity = getRpcSecurityConfig(config);\n const rpcConfig = getRpcConfig(config);\n const compressionConfig = rpcConfig.compression;\n initializeSecurity(rpcSecurity);\n\n // Initialize rate limiter\n const rateLimiter = new RateLimiter(rpcSecurity.maxMessagesPerWindow, rpcSecurity.rateLimitWindowMs, rpcSecurity.maxConnectionsPerIP);\n\n const registry = new RpcRegistry();\n const httpRouter = new HTTPRouter({ maxBodySize: rpcConfig.maxBodySize });\n const seoRouter = new SEOMetadataRouter();\n httpRouter.setTrustProxyDepth(trustProxyDepth);\n registerHandlers(registry, httpRouter, seoRouter);\n registry.setRateLimiter(rateLimiter);\n registry.setMaxBatchSize(rpcConfig.maxBatchSize);\n\n // Security: max body size for HTTP requests (1 MB default)\n const maxBodySize = rpcConfig.maxBodySize ?? 1_048_576;\n let cachedDefaultMeta: Awaited<ReturnType<typeof loadDefaultSocialMetaFromHtmlFile>> | undefined;\n\n const getDefaultMeta = async () => {\n if (cachedDefaultMeta !== undefined) {\n return cachedDefaultMeta;\n }\n\n cachedDefaultMeta = await loadDefaultSocialMetaFromHtmlFile(path.join(staticDir, \"index.html\"));\n return cachedDefaultMeta;\n };\n\n registry.register(SEO_METADATA_RPC_METHOD, {\n __kind: \"method\",\n __id: SEO_METADATA_RPC_METHOD,\n handler: async (args: { path?: string } | undefined, ctx: HeliumContext) => {\n const requestedPath = typeof args?.path === \"string\" ? args.path : \"/\";\n const targetPath = requestedPath.startsWith(\"/\") ? requestedPath : `/${requestedPath}`;\n const metadata = await seoRouter.resolve(ctx.req.raw, ctx, targetPath);\n return metadata ?? (await getDefaultMeta());\n },\n });\n\n // Create HTTP server\n const server = http.createServer(async (req, res) => {\n // Apply security headers to all responses\n setSecurityHeaders(res, config);\n\n // Handle CORS preflight\n if (req.method === \"OPTIONS\") {\n handleCorsHeaders(req, res, config);\n res.writeHead(204);\n res.end();\n return;\n }\n handleCorsHeaders(req, res, config);\n\n // Handle token refresh endpoint\n const requestUrl = new URL(req.url || \"/\", \"http://localhost\");\n const requestPathname = requestUrl.pathname;\n\n if (requestPathname === \"/__helium__/page-props\" && req.method === \"GET\") {\n const pathQuery = requestUrl.searchParams.get(\"path\") || \"/\";\n const targetUrl = new URL(pathQuery, \"http://localhost\");\n const targetPathname = targetUrl.pathname;\n const ssrMatch = matchSSRPage(targetPathname, ssrPages);\n\n if (!ssrMatch) {\n res.writeHead(200, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: false, props: null }));\n return;\n }\n\n const ip = extractClientIP(req, trustProxyDepth);\n const httpCtx: HeliumContext = {\n req: {\n ip,\n headers: req.headers,\n url: req.url,\n method: req.method,\n raw: req,\n },\n };\n\n try {\n let result: Record<string, unknown> | null = {};\n if (ssrMatch.page.getServerSideProps) {\n const request = createServerSidePropsRequest(req, targetPathname, ssrMatch.params);\n result = (await ssrMatch.page.getServerSideProps(request, httpCtx)) ?? {};\n }\n res.writeHead(200, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: true, props: result ?? {} }));\n } catch (error) {\n log(\"error\", \"Failed to resolve SSR page props:\", error);\n res.writeHead(500, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: true, props: null, error: \"Internal server error\" }));\n }\n\n return;\n }\n\n if (req.url === \"/__helium__/refresh-token\") {\n // Security: only allow POST to prevent CSRF via <img>/<script> tags\n if (req.method !== \"POST\") {\n res.writeHead(405, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Method not allowed\" }));\n return;\n }\n // Security: require custom header to prevent cross-origin requests\n if (!req.headers[\"x-requested-with\"]) {\n res.writeHead(403, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Forbidden\" }));\n return;\n }\n const token = generateConnectionToken();\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ token }));\n return;\n }\n\n // Handle HTTP-based RPC endpoint (alternative to WebSocket for mobile networks)\n if (req.url === \"/__helium__/rpc\" && req.method === \"POST\") {\n // Security: verify connection token for HTTP RPC\n const authToken = req.headers[\"x-helium-token\"] as string | undefined;\n if (!authToken || !verifyConnectionToken(authToken)) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: false, error: \"Unauthorized\" }));\n return;\n }\n\n // Security: check Content-Length before reading body\n const contentLength = parseInt(req.headers[\"content-length\"] || \"0\", 10);\n if (contentLength > maxBodySize) {\n res.writeHead(413, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: false, error: \"Request entity too large\" }));\n return;\n }\n\n const chunks: Buffer[] = [];\n let totalSize = 0;\n let aborted = false;\n req.on(\"data\", (chunk: Buffer) => {\n totalSize += chunk.length;\n if (totalSize > maxBodySize) {\n aborted = true;\n req.destroy();\n res.writeHead(413, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: false, error: \"Request entity too large\" }));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", async () => {\n if (aborted) {\n return;\n }\n try {\n const body = Buffer.concat(chunks);\n const ip = extractClientIP(req, trustProxyDepth);\n const result = await registry.handleHttpRequest(body, ip, req);\n\n const encoded = msgpackEncode(prepareForMsgpack(result.response));\n let responseBody = Buffer.from(encoded as Uint8Array);\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/msgpack\",\n \"Cache-Control\": \"no-store\",\n };\n\n // Handle compression\n const acceptEncoding = req.headers[\"accept-encoding\"] as string;\n if (acceptEncoding && responseBody.length > 1024) {\n if (acceptEncoding.includes(\"br\")) {\n responseBody = await brotliCompressAsync(responseBody);\n headers[\"Content-Encoding\"] = \"br\";\n } else if (acceptEncoding.includes(\"gzip\")) {\n responseBody = await gzipAsync(responseBody);\n headers[\"Content-Encoding\"] = \"gzip\";\n } else if (acceptEncoding.includes(\"deflate\")) {\n responseBody = await deflateAsync(responseBody);\n headers[\"Content-Encoding\"] = \"deflate\";\n }\n }\n\n res.writeHead(200, headers);\n res.end(responseBody);\n } catch (error) {\n log(\"error\", \"HTTP RPC error:\", error);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: false, error: \"Internal server error\" }));\n }\n });\n return;\n }\n\n // Try HTTP handlers first (webhooks, auth, etc.)\n const handled = await httpRouter.handleRequest(req, res);\n if (handled) {\n return;\n }\n\n // Serve static files\n const url = req.url || \"/\";\n\n // Block access to sensitive configuration and server files\n const blockedFiles = [\"helium.config.js\", \"helium.config.mjs\", \"helium.config.ts\", \"server.js\", \".env\", \".env.local\", \".env.production\"];\n\n const requestedFile = path.basename(url.split(\"?\")[0]);\n let filePath: string = path.join(staticDir, \"index.html\");\n let is404 = false;\n\n if (blockedFiles.some((blocked) => requestedFile === blocked || requestedFile.startsWith(\".env\"))) {\n // Serve index.html so the SPA router can render the 404 page\n filePath = path.join(staticDir, \"index.html\");\n is404 = true;\n } else {\n // Clean URL (remove query params and trailing slash)\n const cleanUrl = url.split(\"?\")[0].replace(/\\/$/, \"\") || \"/\";\n\n // Security: path traversal prevention — resolve and verify\n const resolvedStaticDir = path.resolve(staticDir);\n const candidatePath = path.resolve(staticDir, \".\" + cleanUrl);\n if (!candidatePath.startsWith(resolvedStaticDir + path.sep) && candidatePath !== resolvedStaticDir) {\n filePath = path.join(staticDir, \"index.html\");\n is404 = true;\n }\n\n // Try different file paths for SSG support\n if (!is404 && cleanUrl === \"/\") {\n // Try index.ssg.html first (if root page has SSG)\n const ssgIndexPath = path.join(staticDir, \"index.ssg.html\");\n if (fs.existsSync(ssgIndexPath)) {\n filePath = ssgIndexPath;\n } else {\n filePath = path.join(staticDir, \"index.html\");\n }\n } else if (!is404) {\n // If cleanUrl has no extension, prioritize .html files for SSG pages\n if (!path.extname(cleanUrl)) {\n const htmlPath = path.join(staticDir, cleanUrl + \".html\");\n if (fs.existsSync(htmlPath)) {\n filePath = htmlPath;\n } else {\n // Fall back to exact path (for assets or directories)\n filePath = path.join(staticDir, cleanUrl);\n }\n } else {\n // Has an extension, try exact path (for assets like /assets/main.js)\n filePath = path.join(staticDir, cleanUrl);\n }\n }\n\n // If file doesn't exist or is a directory, fall back to index.html for SPA routing\n const isFileOrExists = !is404 && filePath && fs.existsSync(filePath) && fs.statSync(filePath).isFile();\n if (!isFileOrExists && !url.startsWith(\"/api\") && !url.startsWith(\"/webhooks\") && !url.startsWith(\"/auth\")) {\n // Fall back to index.html for SPA routing\n // Note: We don't set is404 here because the client-side router will determine\n // if the route exists. If it doesn't, the router will render the 404 page.\n filePath = path.join(staticDir, \"index.html\");\n // Don't set is404 = true here - let the client-side router handle it\n }\n }\n\n // Check if file exists (should always exist now since we fallback to index.html)\n if (!fs.existsSync(filePath)) {\n // This should rarely happen - only if index.html itself is missing\n res.writeHead(404, { \"Content-Type\": \"text/html\" });\n res.end(\"Not found\");\n return;\n }\n\n // Determine content type\n const ext = path.extname(filePath);\n const contentTypes: Record<string, string> = {\n \".html\": \"text/html\",\n \".js\": \"application/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".svg\": \"image/svg+xml\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".eot\": \"application/vnd.ms-fontobject\",\n };\n const contentType = contentTypes[ext] || \"application/octet-stream\";\n\n try {\n const content = fs.readFileSync(filePath);\n let responseBody = content;\n const cleanedPathname = (req.url || \"/\").split(\"?\")[0].replace(/\\/$/, \"\") || \"/\";\n const htmlSsrMatch = req.method === \"GET\" && contentType === \"text/html\" ? matchSSRPage(cleanedPathname, ssrPages) : null;\n\n if (!is404 && contentType === \"text/html\") {\n const ip = extractClientIP(req, trustProxyDepth);\n const httpCtx: HeliumContext = {\n req: {\n ip,\n headers: req.headers,\n url: req.url,\n method: req.method,\n raw: req,\n },\n };\n\n const metadata = await seoRouter.resolve(req, httpCtx);\n let html = content.toString(\"utf-8\");\n\n if (htmlSsrMatch) {\n try {\n const rendered = await renderSSRHTML({\n htmlTemplate: html,\n pathname: cleanedPathname,\n search: requestUrl.search,\n params: htmlSsrMatch.params,\n page: htmlSsrMatch.page,\n req,\n ctx: httpCtx,\n loadAppShell: appShell,\n });\n html = rendered.html;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const isBrowserGlobalError = /\\bwindow is not defined\\b|\\bdocument is not defined\\b|\\bnavigator is not defined\\b/i.test(message);\n\n if (isBrowserGlobalError) {\n log(\n \"warn\",\n `SSR disabled for ${cleanedPathname} due to browser-only import. Render map/browser-only modules on client only (e.g. dynamic import inside useEffect).`\n );\n } else {\n log(\"error\", `Failed to render SSR page for ${cleanedPathname}:`, error);\n }\n }\n }\n\n if (metadata) {\n html = injectSocialMetaIntoHtml(html, metadata);\n }\n\n responseBody = Buffer.from(html, \"utf-8\");\n }\n\n // Set status code to 404 if serving the 404 page\n const statusCode = is404 ? 404 : 200;\n res.writeHead(statusCode, { \"Content-Type\": contentType });\n res.end(responseBody);\n } catch (error) {\n log(\"error\", \"Error serving file:\", error);\n res.writeHead(500, { \"Content-Type\": \"text/plain\" });\n res.end(\"Internal server error\");\n }\n });\n\n // Setup WebSocket server for RPC\n const wss = new WebSocketServer({\n noServer: true,\n maxPayload: rpcConfig.maxWsPayload,\n perMessageDeflate: compressionConfig.enabled\n ? {\n zlibDeflateOptions: {\n chunkSize: 1024,\n memLevel: 7,\n level: 9, // 6 is default compression level (balanced)\n },\n zlibInflateOptions: {\n chunkSize: 10 * 1024,\n },\n threshold: compressionConfig.threshold,\n }\n : false,\n });\n\n wss.on(\"connection\", (socket: WebSocket, req: http.IncomingMessage) => {\n // Extract client IP with proxy configuration\n const ip = extractClientIP(req, trustProxyDepth);\n\n // Store connection metadata for RPC context\n registry.setSocketMetadata(socket, ip, req);\n\n // Track connection and check IP limit\n if (!rateLimiter.trackConnection(socket, ip)) {\n socket.close(1008, \"Too many connections from your IP\");\n return;\n }\n\n // Prevent unhandled errors from crashing the process (e.g. maxPayload exceeded)\n socket.on(\"error\", (err) => {\n log(\"warn\", \"WebSocket error:\", err);\n if (socket.readyState === socket.OPEN || socket.readyState === socket.CLOSING) {\n socket.close(1009, \"Message too large\");\n }\n });\n\n socket.on(\"message\", (msg: WebSocket.RawData, _isBinary: boolean) => {\n // Check rate limit\n if (!rateLimiter.checkRateLimit(socket)) {\n // Parse request to get the ID for proper error response\n try {\n let req: any;\n // Always expect MessagePack\n const buffer = Buffer.isBuffer(msg) ? msg : Buffer.from(msg as any);\n const { decode: msgpackDecode } = require(\"@msgpack/msgpack\");\n req = msgpackDecode(buffer);\n\n const stats = rateLimiter.getConnectionStats(socket);\n const now = Date.now();\n const resetInSeconds = stats ? Math.ceil((stats.resetTimeMs - now) / 1000) : 0;\n\n const createError = (id: string) => ({\n id,\n ok: false,\n stats: {\n remainingRequests: stats ? stats.remainingMessages : 0,\n resetInSeconds,\n },\n error: \"Rate limit exceeded\",\n });\n\n let errorResponse: any;\n if (Array.isArray(req)) {\n errorResponse = req.map((r: any) => createError(r.id));\n } else {\n errorResponse = createError(req.id);\n }\n\n log(\"warn\", `Rate limit exceeded for IP ${ip}, resets in ${resetInSeconds} seconds`);\n socket.send(msgpackEncode(errorResponse) as Buffer);\n } catch {\n // If we can't parse the request, just close the connection\n socket.close();\n }\n return;\n }\n\n registry.handleMessage(socket, Buffer.isBuffer(msg) ? msg : Buffer.from(msg as any));\n });\n });\n\n // Handle WebSocket upgrade requests\n server.on(\"upgrade\", (req, socket, head) => {\n if (req.url?.startsWith(\"/rpc\")) {\n // Security: read token from Sec-WebSocket-Protocol header instead of query string\n const protocols = req.headers[\"sec-websocket-protocol\"];\n const token =\n typeof protocols === \"string\"\n ? protocols\n .split(\",\")\n .map((p) => p.trim())\n .find((p) => p.includes(\".\"))\n : undefined;\n\n if (!token || !verifyConnectionToken(token)) {\n log(\"warn\", \"WebSocket connection rejected - invalid token\");\n socket.write(\"HTTP/1.1 401 Unauthorized\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n\n // Check IP connection limit before upgrading\n const ip = extractClientIP(req, trustProxyDepth);\n if (rpcSecurity.maxConnectionsPerIP > 0) {\n const currentConnections = rateLimiter.getIPConnectionCount(ip);\n if (currentConnections >= rpcSecurity.maxConnectionsPerIP) {\n log(\"warn\", `WebSocket connection rejected - IP ${ip} has ${currentConnections} connections`);\n socket.write(\"HTTP/1.1 429 Too Many Requests\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n }\n\n wss.handleUpgrade(req, socket, head, (ws) => {\n wss.emit(\"connection\", ws, req);\n });\n } else {\n socket.destroy();\n }\n });\n\n // Start server\n server.listen(port, () => {\n log(\"info\", `Production server listening on http://localhost:${port}`);\n log(\"info\", `Serving static files from ${staticDir}`);\n log(\"info\", `WebSocket RPC available at ws://localhost:${port}/rpc`);\n\n // Start workers\n if (workers.length > 0) {\n log(\"info\", `Starting ${workers.length} worker(s)...`);\n for (const { name, worker } of workers) {\n // Use export name if worker name is anonymous\n if (worker.name === \"anonymous\") {\n worker.name = name;\n worker.__id = name;\n worker.options.name = name;\n }\n if (worker.options.autoStart) {\n const createContext = (): HeliumContext => ({\n req: {\n ip: \"127.0.0.1\",\n headers: {},\n url: undefined,\n method: undefined,\n raw: {} as http.IncomingMessage,\n },\n });\n startWorker(worker, createContext).catch((err) => {\n log(\"error\", `Failed to start worker '${worker.name}':`, err);\n });\n }\n }\n }\n });\n\n // Handle graceful shutdown\n const shutdown = async () => {\n log(\"info\", \"Shutting down...\");\n await stopAllWorkers();\n server.close(() => {\n log(\"info\", \"Server closed\");\n process.exit(0);\n });\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n return server;\n}\n\n// ============================================================================\n// Security helper functions\n// ============================================================================\n\n/**\n * Set default security headers on every HTTP response.\n */\nfunction setSecurityHeaders(res: http.ServerResponse, config: HeliumConfig): void {\n res.setHeader(\"X-Content-Type-Options\", \"nosniff\");\n res.setHeader(\"X-Frame-Options\", \"DENY\");\n res.setHeader(\"X-XSS-Protection\", \"0\");\n res.setHeader(\"Referrer-Policy\", \"strict-origin-when-cross-origin\");\n res.setHeader(\"Permissions-Policy\", \"camera=(), microphone=(), geolocation=()\");\n\n const csp = config.security?.contentSecurityPolicy;\n if (csp) {\n res.setHeader(\"Content-Security-Policy\", csp);\n }\n\n if (config.security?.hsts !== false) {\n res.setHeader(\"Strict-Transport-Security\", \"max-age=31536000; includeSubDomains\");\n }\n}\n\n/**\n * Handle CORS headers based on configuration.\n * Default: restrict to same-origin (no CORS header = browser blocks cross-origin).\n */\nfunction handleCorsHeaders(req: http.IncomingMessage, res: http.ServerResponse, config: HeliumConfig): void {\n const allowedOrigins = config.security?.corsOrigins;\n if (!allowedOrigins || allowedOrigins.length === 0) {\n // No CORS configured — same-origin only by default\n return;\n }\n\n const origin = req.headers.origin;\n if (!origin) {\n return;\n }\n\n const isAllowed = allowedOrigins.includes(\"*\") || allowedOrigins.includes(origin);\n if (isAllowed) {\n res.setHeader(\"Access-Control-Allow-Origin\", allowedOrigins.includes(\"*\") ? \"*\" : origin);\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, X-Requested-With, X-Helium-Token\");\n res.setHeader(\"Access-Control-Max-Age\", \"86400\");\n\n if (!allowedOrigins.includes(\"*\")) {\n res.setHeader(\"Vary\", \"Origin\");\n }\n }\n}\n"]}
1
+ {"version":3,"file":"prodServer.js","sourceRoot":"","sources":["../../src/server/prodServer.ts"],"names":[],"mappings":"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","sourcesContent":["import { encode as msgpackEncode } from \"@msgpack/msgpack\";\nimport fs from \"fs\";\nimport http from \"http\";\nimport path from \"path\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { promisify } from \"util\";\nimport type WebSocket from \"ws\";\nimport { WebSocketServer } from \"ws\";\nimport { brotliCompress, deflate, gzip } from \"zlib\";\n\nimport { SEO_METADATA_RPC_METHOD } from \"../runtime/internalMethods.js\";\nimport { extractClientIP } from \"../utils/ipExtractor.js\";\nimport { log } from \"../utils/logger.js\";\nimport type { HeliumConfig } from \"./config.js\";\nimport { getRpcConfig, getRpcSecurityConfig, getTrustProxyDepth } from \"./config.js\";\nimport type { HeliumContext } from \"./context.js\";\nimport type { HeliumWorkerDef } from \"./defineWorker.js\";\nimport { startWorker, stopAllWorkers } from \"./defineWorker.js\";\nimport { HTTPRouter } from \"./httpRouter.js\";\nimport { injectSocialMetaIntoHtml, loadDefaultSocialMetaFromHtmlFile } from \"./meta.js\";\nimport { RateLimiter } from \"./rateLimiter.js\";\nimport { RpcRegistry } from \"./rpcRegistry.js\";\nimport { generateConnectionToken, initializeSecurity, verifyConnectionToken } from \"./security.js\";\nimport { SEOMetadataRouter } from \"./seoMetadataRouter.js\";\nimport { prepareForMsgpack } from \"./serializer.js\";\nimport { createServerSidePropsRequest, matchSSRPage, renderSSRHTML, SSRPageDef } from \"./ssr.js\";\n\nconst gzipAsync = promisify(gzip);\nconst deflateAsync = promisify(deflate);\nconst brotliCompressAsync = promisify(brotliCompress);\n\ninterface WorkerEntry {\n name: string;\n worker: HeliumWorkerDef;\n}\n\ninterface ProdServerOptions {\n port?: number;\n distDir?: string;\n staticDir?: string;\n registerHandlers: (registry: RpcRegistry, httpRouter: HTTPRouter, seoRouter: SEOMetadataRouter) => void;\n config?: HeliumConfig;\n workers?: WorkerEntry[];\n ssrPages?: SSRPageDef[];\n appShell?: (() => Promise<ComponentType<{ Component: ComponentType<Record<string, unknown>>; pageProps: Record<string, unknown>; children?: ReactNode }>>) | null;\n}\n\n/**\n * Starts a production HTTP server that:\n * - Serves static files from the dist directory\n * - Supports SSG (Static Site Generation) by serving .html files for routes (e.g., /about -> about.html)\n * - Falls back to index.html for client-side routing (SPA)\n * - Handles custom HTTP endpoints (webhooks, auth, etc.)\n * - Hosts WebSocket RPC server\n * - Starts background workers\n *\n * SSG Behavior:\n * - Production correctly serves SSG pages (e.g., /about serves about.html with pre-rendered content)\n * - This ensures search engines and social media crawlers see the correct content\n * - Client-side navigation between pages still works via React Router\n */\nexport function startProdServer(options: ProdServerOptions) {\n const {\n port = Number(process.env.PORT || 3000),\n distDir = \"dist\",\n staticDir = path.resolve(process.cwd(), distDir),\n registerHandlers,\n config = {},\n workers = [],\n ssrPages = [],\n appShell = null,\n } = options;\n\n // Load configuration\n const trustProxyDepth = getTrustProxyDepth(config);\n const rpcSecurity = getRpcSecurityConfig(config);\n const rpcConfig = getRpcConfig(config);\n const compressionConfig = rpcConfig.compression;\n initializeSecurity(rpcSecurity);\n\n // Initialize rate limiter\n const rateLimiter = new RateLimiter(rpcSecurity.maxMessagesPerWindow, rpcSecurity.rateLimitWindowMs, rpcSecurity.maxConnectionsPerIP);\n\n const registry = new RpcRegistry();\n const httpRouter = new HTTPRouter({ maxBodySize: rpcConfig.maxBodySize });\n const seoRouter = new SEOMetadataRouter();\n httpRouter.setTrustProxyDepth(trustProxyDepth);\n registerHandlers(registry, httpRouter, seoRouter);\n registry.setRateLimiter(rateLimiter);\n registry.setMaxBatchSize(rpcConfig.maxBatchSize);\n\n // Security: max body size for HTTP requests (1 MB default)\n const maxBodySize = rpcConfig.maxBodySize ?? 1_048_576;\n let cachedDefaultMeta: Awaited<ReturnType<typeof loadDefaultSocialMetaFromHtmlFile>> | undefined;\n\n const getDefaultMeta = async () => {\n if (cachedDefaultMeta !== undefined) {\n return cachedDefaultMeta;\n }\n\n cachedDefaultMeta = await loadDefaultSocialMetaFromHtmlFile(path.join(staticDir, \"index.html\"));\n return cachedDefaultMeta;\n };\n\n registry.register(SEO_METADATA_RPC_METHOD, {\n __kind: \"method\",\n __id: SEO_METADATA_RPC_METHOD,\n handler: async (args: { path?: string } | undefined, ctx: HeliumContext) => {\n const requestedPath = typeof args?.path === \"string\" ? args.path : \"/\";\n const targetPath = requestedPath.startsWith(\"/\") ? requestedPath : `/${requestedPath}`;\n const metadata = await seoRouter.resolve(ctx.req.raw, ctx, targetPath);\n return metadata ?? (await getDefaultMeta());\n },\n });\n\n // Create HTTP server\n const server = http.createServer(async (req, res) => {\n // Apply security headers to all responses\n setSecurityHeaders(res, config);\n\n // Handle CORS preflight\n if (req.method === \"OPTIONS\") {\n handleCorsHeaders(req, res, config);\n res.writeHead(204);\n res.end();\n return;\n }\n handleCorsHeaders(req, res, config);\n\n // Handle token refresh endpoint\n const requestUrl = new URL(req.url || \"/\", \"http://localhost\");\n const requestPathname = requestUrl.pathname;\n\n if (requestPathname === \"/__helium__/page-props\" && req.method === \"GET\") {\n const pathQuery = requestUrl.searchParams.get(\"path\") || \"/\";\n const targetUrl = new URL(pathQuery, \"http://localhost\");\n const targetPathname = targetUrl.pathname;\n const ssrMatch = matchSSRPage(targetPathname, ssrPages);\n\n if (!ssrMatch) {\n res.writeHead(200, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: false, props: null }));\n return;\n }\n\n const ip = extractClientIP(req, trustProxyDepth);\n const httpCtx: HeliumContext = {\n req: {\n ip,\n headers: req.headers,\n url: req.url,\n method: req.method,\n raw: req,\n },\n };\n\n try {\n let result: Record<string, unknown> | null = {};\n if (ssrMatch.page.getServerSideProps) {\n const request = createServerSidePropsRequest(req, targetPathname, ssrMatch.params);\n result = (await ssrMatch.page.getServerSideProps(request, httpCtx)) ?? {};\n }\n res.writeHead(200, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: true, props: result ?? {} }));\n } catch (error) {\n log(\"error\", \"Failed to resolve SSR page props:\", error);\n res.writeHead(500, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: true, props: null, error: \"Internal server error\" }));\n }\n\n return;\n }\n\n if (req.url === \"/__helium__/refresh-token\") {\n // Security: only allow POST to prevent CSRF via <img>/<script> tags\n if (req.method !== \"POST\") {\n res.writeHead(405, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Method not allowed\" }));\n return;\n }\n // Security: require custom header to prevent cross-origin requests\n if (!req.headers[\"x-requested-with\"]) {\n res.writeHead(403, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Forbidden\" }));\n return;\n }\n const token = generateConnectionToken();\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ token }));\n return;\n }\n\n // Handle HTTP-based RPC endpoint (alternative to WebSocket for mobile networks)\n if (req.url === \"/__helium__/rpc\" && req.method === \"POST\") {\n // Security: verify connection token for HTTP RPC\n const authToken = req.headers[\"x-helium-token\"] as string | undefined;\n if (!authToken || !verifyConnectionToken(authToken)) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: false, error: \"Unauthorized\" }));\n return;\n }\n\n // Security: check Content-Length before reading body\n const contentLength = parseInt(req.headers[\"content-length\"] || \"0\", 10);\n if (contentLength > maxBodySize) {\n res.writeHead(413, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: false, error: \"Request entity too large\" }));\n return;\n }\n\n const chunks: Buffer[] = [];\n let totalSize = 0;\n let aborted = false;\n req.on(\"data\", (chunk: Buffer) => {\n totalSize += chunk.length;\n if (totalSize > maxBodySize) {\n aborted = true;\n req.destroy();\n res.writeHead(413, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: false, error: \"Request entity too large\" }));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", async () => {\n if (aborted) {\n return;\n }\n try {\n const body = Buffer.concat(chunks);\n const ip = extractClientIP(req, trustProxyDepth);\n const result = await registry.handleHttpRequest(body, ip, req);\n\n const encoded = msgpackEncode(prepareForMsgpack(result.response));\n let responseBody = Buffer.from(encoded as Uint8Array);\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/msgpack\",\n \"Cache-Control\": \"no-store\",\n };\n\n // Handle compression\n const acceptEncoding = req.headers[\"accept-encoding\"] as string;\n if (acceptEncoding && responseBody.length > 1024) {\n if (acceptEncoding.includes(\"br\")) {\n responseBody = await brotliCompressAsync(responseBody);\n headers[\"Content-Encoding\"] = \"br\";\n } else if (acceptEncoding.includes(\"gzip\")) {\n responseBody = await gzipAsync(responseBody);\n headers[\"Content-Encoding\"] = \"gzip\";\n } else if (acceptEncoding.includes(\"deflate\")) {\n responseBody = await deflateAsync(responseBody);\n headers[\"Content-Encoding\"] = \"deflate\";\n }\n }\n\n res.writeHead(200, headers);\n res.end(responseBody);\n } catch (error) {\n log(\"error\", \"HTTP RPC error:\", error);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: false, error: \"Internal server error\" }));\n }\n });\n return;\n }\n\n // Try HTTP handlers first (webhooks, auth, etc.)\n const handled = await httpRouter.handleRequest(req, res);\n if (handled) {\n return;\n }\n\n // Serve static files\n const url = req.url || \"/\";\n\n // Block access to sensitive configuration and server files\n const blockedFiles = [\"helium.config.js\", \"helium.config.mjs\", \"helium.config.ts\", \"server.js\", \".env\", \".env.local\", \".env.production\"];\n\n const requestedFile = path.basename(url.split(\"?\")[0]);\n let filePath: string = path.join(staticDir, \"index.html\");\n let is404 = false;\n\n if (blockedFiles.some((blocked) => requestedFile === blocked || requestedFile.startsWith(\".env\"))) {\n // Serve index.html so the SPA router can render the 404 page\n filePath = path.join(staticDir, \"index.html\");\n is404 = true;\n } else {\n // Clean URL (remove query params and trailing slash)\n const cleanUrl = url.split(\"?\")[0].replace(/\\/$/, \"\") || \"/\";\n\n // Security: path traversal prevention — resolve and verify\n const resolvedStaticDir = path.resolve(staticDir);\n const candidatePath = path.resolve(staticDir, \".\" + cleanUrl);\n if (!candidatePath.startsWith(resolvedStaticDir + path.sep) && candidatePath !== resolvedStaticDir) {\n filePath = path.join(staticDir, \"index.html\");\n is404 = true;\n }\n\n // Try different file paths for SSG support\n if (!is404 && cleanUrl === \"/\") {\n // Try index.ssg.html first (if root page has SSG)\n const ssgIndexPath = path.join(staticDir, \"index.ssg.html\");\n if (fs.existsSync(ssgIndexPath)) {\n filePath = ssgIndexPath;\n } else {\n filePath = path.join(staticDir, \"index.html\");\n }\n } else if (!is404) {\n // If cleanUrl has no extension, prioritize .html files for SSG pages\n if (!path.extname(cleanUrl)) {\n const htmlPath = path.join(staticDir, cleanUrl + \".html\");\n if (fs.existsSync(htmlPath)) {\n filePath = htmlPath;\n } else {\n // Fall back to exact path (for assets or directories)\n filePath = path.join(staticDir, cleanUrl);\n }\n } else {\n // Has an extension, try exact path (for assets like /assets/main.js)\n filePath = path.join(staticDir, cleanUrl);\n }\n }\n\n // If file doesn't exist or is a directory, fall back to index.html for SPA routing\n const isFileOrExists = !is404 && filePath && fs.existsSync(filePath) && fs.statSync(filePath).isFile();\n if (!isFileOrExists && !url.startsWith(\"/api\") && !url.startsWith(\"/webhooks\") && !url.startsWith(\"/auth\")) {\n // Fall back to index.html for SPA routing\n // Note: We don't set is404 here because the client-side router will determine\n // if the route exists. If it doesn't, the router will render the 404 page.\n filePath = path.join(staticDir, \"index.html\");\n // Don't set is404 = true here - let the client-side router handle it\n }\n }\n\n // Check if file exists (should always exist now since we fallback to index.html)\n if (!fs.existsSync(filePath)) {\n // This should rarely happen - only if index.html itself is missing\n res.writeHead(404, { \"Content-Type\": \"text/html\" });\n res.end(\"Not found\");\n return;\n }\n\n // Determine content type\n const ext = path.extname(filePath);\n const contentTypes: Record<string, string> = {\n \".html\": \"text/html\",\n \".js\": \"application/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".svg\": \"image/svg+xml\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".eot\": \"application/vnd.ms-fontobject\",\n };\n const contentType = contentTypes[ext] || \"application/octet-stream\";\n\n try {\n const content = fs.readFileSync(filePath);\n let responseBody = content;\n const cleanedPathname = (req.url || \"/\").split(\"?\")[0].replace(/\\/$/, \"\") || \"/\";\n const htmlSsrMatch = req.method === \"GET\" && contentType === \"text/html\" ? matchSSRPage(cleanedPathname, ssrPages) : null;\n\n if (!is404 && contentType === \"text/html\") {\n const ip = extractClientIP(req, trustProxyDepth);\n const httpCtx: HeliumContext = {\n req: {\n ip,\n headers: req.headers,\n url: req.url,\n method: req.method,\n raw: req,\n },\n };\n\n const metadata = await seoRouter.resolve(req, httpCtx);\n let html = content.toString(\"utf-8\");\n\n if (htmlSsrMatch) {\n try {\n const rendered = await renderSSRHTML({\n htmlTemplate: html,\n pathname: cleanedPathname,\n search: requestUrl.search,\n params: htmlSsrMatch.params,\n page: htmlSsrMatch.page,\n req,\n ctx: httpCtx,\n loadAppShell: appShell,\n });\n html = rendered.html;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const isBrowserGlobalError = /\\bwindow is not defined\\b|\\bdocument is not defined\\b|\\bnavigator is not defined\\b/i.test(message);\n\n if (isBrowserGlobalError) {\n log(\n \"warn\",\n `SSR disabled for ${cleanedPathname} due to browser-only import. Render map/browser-only modules on client only (e.g. dynamic import inside useEffect).`\n );\n } else {\n log(\"error\", `Failed to render SSR page for ${cleanedPathname}:`, error);\n }\n }\n }\n\n if (metadata) {\n html = injectSocialMetaIntoHtml(html, metadata);\n }\n\n responseBody = Buffer.from(html, \"utf-8\");\n }\n\n // Set status code to 404 if serving the 404 page\n const statusCode = is404 ? 404 : 200;\n res.writeHead(statusCode, { \"Content-Type\": contentType });\n res.end(responseBody);\n } catch (error) {\n log(\"error\", \"Error serving file:\", error);\n res.writeHead(500, { \"Content-Type\": \"text/plain\" });\n res.end(\"Internal server error\");\n }\n });\n\n // Setup WebSocket server for RPC\n const wss = new WebSocketServer({\n noServer: true,\n maxPayload: rpcConfig.maxWsPayload,\n perMessageDeflate: compressionConfig.enabled\n ? {\n zlibDeflateOptions: {\n chunkSize: 1024,\n memLevel: 7,\n level: 9, // 6 is default compression level (balanced)\n },\n zlibInflateOptions: {\n chunkSize: 10 * 1024,\n },\n threshold: compressionConfig.threshold,\n }\n : false,\n });\n\n wss.on(\"connection\", (socket: WebSocket, req: http.IncomingMessage) => {\n // Extract client IP with proxy configuration\n const ip = extractClientIP(req, trustProxyDepth);\n\n // Store connection metadata for RPC context\n registry.setSocketMetadata(socket, ip, req);\n\n // Track connection and check IP limit\n if (!rateLimiter.trackConnection(socket, ip)) {\n socket.close(1008, \"Too many connections from your IP\");\n return;\n }\n\n // Prevent unhandled errors from crashing the process (e.g. maxPayload exceeded)\n socket.on(\"error\", (err) => {\n log(\"warn\", \"WebSocket error:\", err);\n if (socket.readyState === socket.OPEN || socket.readyState === socket.CLOSING) {\n socket.close(1009, \"Message too large\");\n }\n });\n\n socket.on(\"message\", (msg: WebSocket.RawData, _isBinary: boolean) => {\n // Check rate limit\n if (!rateLimiter.checkRateLimit(socket)) {\n // Parse request to get the ID for proper error response\n try {\n let req: any;\n // Always expect MessagePack\n const buffer = Buffer.isBuffer(msg) ? msg : Buffer.from(msg as any);\n const { decode: msgpackDecode } = require(\"@msgpack/msgpack\");\n req = msgpackDecode(buffer);\n\n const stats = rateLimiter.getConnectionStats(socket);\n const now = Date.now();\n const resetInSeconds = stats ? Math.ceil((stats.resetTimeMs - now) / 1000) : 0;\n\n const createError = (id: string) => ({\n id,\n ok: false,\n stats: {\n remainingRequests: stats ? stats.remainingMessages : 0,\n resetInSeconds,\n },\n error: \"Rate limit exceeded\",\n });\n\n let errorResponse: any;\n if (Array.isArray(req)) {\n errorResponse = req.map((r: any) => createError(r.id));\n } else {\n errorResponse = createError(req.id);\n }\n\n log(\"warn\", `Rate limit exceeded for IP ${ip}, resets in ${resetInSeconds} seconds`);\n socket.send(msgpackEncode(errorResponse) as Buffer);\n } catch {\n // If we can't parse the request, just close the connection\n socket.close();\n }\n return;\n }\n\n registry.handleMessage(socket, Buffer.isBuffer(msg) ? msg : Buffer.from(msg as any));\n });\n });\n\n // Handle WebSocket upgrade requests\n server.on(\"upgrade\", (req, socket, head) => {\n if (req.url?.startsWith(\"/rpc\")) {\n // Security: read token from Sec-WebSocket-Protocol header instead of query string\n const protocols = req.headers[\"sec-websocket-protocol\"];\n const token =\n typeof protocols === \"string\"\n ? protocols\n .split(\",\")\n .map((p) => p.trim())\n .find((p) => p.includes(\".\"))\n : undefined;\n\n if (!token || !verifyConnectionToken(token)) {\n log(\"warn\", \"WebSocket connection rejected - invalid token\");\n socket.write(\"HTTP/1.1 401 Unauthorized\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n\n // Check IP connection limit before upgrading\n const ip = extractClientIP(req, trustProxyDepth);\n if (rpcSecurity.maxConnectionsPerIP > 0) {\n const currentConnections = rateLimiter.getIPConnectionCount(ip);\n if (currentConnections >= rpcSecurity.maxConnectionsPerIP) {\n log(\"warn\", `WebSocket connection rejected - IP ${ip} has ${currentConnections} connections`);\n socket.write(\"HTTP/1.1 429 Too Many Requests\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n }\n\n wss.handleUpgrade(req, socket, head, (ws) => {\n wss.emit(\"connection\", ws, req);\n });\n } else {\n socket.destroy();\n }\n });\n\n // Start server\n server.listen(port, () => {\n log(\"info\", `Production server listening on http://localhost:${port}`);\n log(\"info\", `Serving static files from ${staticDir}`);\n log(\"info\", `WebSocket RPC available at ws://localhost:${port}/rpc`);\n\n // Start workers\n if (workers.length > 0) {\n log(\"info\", `Starting ${workers.length} worker(s)...`);\n for (const { name, worker } of workers) {\n // Use export name if worker name is anonymous\n if (worker.name === \"anonymous\") {\n worker.name = name;\n worker.__id = name;\n worker.options.name = name;\n }\n if (worker.options.autoStart) {\n const createContext = (): HeliumContext => ({\n req: {\n ip: \"127.0.0.1\",\n headers: {},\n url: undefined,\n method: undefined,\n raw: {} as http.IncomingMessage,\n },\n });\n startWorker(worker, createContext).catch((err) => {\n log(\"error\", `Failed to start worker '${worker.name}':`, err);\n });\n }\n }\n }\n });\n\n // Handle graceful shutdown\n const shutdown = async () => {\n log(\"info\", \"Shutting down...\");\n await stopAllWorkers();\n server.close(() => {\n log(\"info\", \"Server closed\");\n process.exit(0);\n });\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n return server;\n}\n\n// ============================================================================\n// Security helper functions\n// ============================================================================\n\n/**\n * Set default security headers on every HTTP response.\n */\nfunction setSecurityHeaders(res: http.ServerResponse, config: HeliumConfig): void {\n const headers = getSecurityHeaders(config);\n\n for (const [name, value] of Object.entries(headers)) {\n if (value === null) {\n res.removeHeader(name);\n continue;\n }\n res.setHeader(name, value);\n }\n}\n\n/**\n * Resolve security headers for the current request from config.\n * Returns a final header map where `null` indicates the header should be removed.\n *\n * @internal\n */\nexport function getSecurityHeaders(config: HeliumConfig): Record<string, string | null> {\n const headers: Record<string, string | null> = {};\n const securityConfig = config.security;\n\n if (securityConfig?.defaultHeaders !== false) {\n headers[\"X-Content-Type-Options\"] = \"nosniff\";\n headers[\"X-Frame-Options\"] = \"DENY\";\n headers[\"X-XSS-Protection\"] = \"0\";\n headers[\"Referrer-Policy\"] = \"strict-origin-when-cross-origin\";\n headers[\"Permissions-Policy\"] = \"camera=(), microphone=(), geolocation=()\";\n\n const csp = securityConfig?.contentSecurityPolicy;\n if (csp) {\n headers[\"Content-Security-Policy\"] = csp;\n }\n\n if (securityConfig?.hsts !== false) {\n headers[\"Strict-Transport-Security\"] = \"max-age=31536000; includeSubDomains\";\n }\n }\n\n if (securityConfig?.headerOverrides) {\n for (const [name, value] of Object.entries(securityConfig.headerOverrides)) {\n headers[name] = value;\n }\n }\n\n return headers;\n}\n\n/**\n * Handle CORS headers based on configuration.\n * Default: restrict to same-origin (no CORS header = browser blocks cross-origin).\n */\nfunction handleCorsHeaders(req: http.IncomingMessage, res: http.ServerResponse, config: HeliumConfig): void {\n const allowedOrigins = config.security?.corsOrigins;\n if (!allowedOrigins || allowedOrigins.length === 0) {\n // No CORS configured — same-origin only by default\n return;\n }\n\n const origin = req.headers.origin;\n if (!origin) {\n return;\n }\n\n const isAllowed = allowedOrigins.includes(\"*\") || allowedOrigins.includes(origin);\n if (isAllowed) {\n res.setHeader(\"Access-Control-Allow-Origin\", allowedOrigins.includes(\"*\") ? \"*\" : origin);\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, X-Requested-With, X-Helium-Token\");\n res.setHeader(\"Access-Control-Max-Age\", \"86400\");\n\n if (!allowedOrigins.includes(\"*\")) {\n res.setHeader(\"Vary\", \"Origin\");\n }\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=serverPropsRouter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverPropsRouter.d.ts","sourceRoot":"","sources":["../../src/server/serverPropsRouter.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=serverPropsRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverPropsRouter.js","sourceRoot":"","sources":["../../src/server/serverPropsRouter.ts"],"names":[],"mappings":"","sourcesContent":[""]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heliumts",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "A lightweight full-stack React framework with file-based routing, RPC, and SSG support",
5
5
  "keywords": [
6
6
  "react",
@@ -1,48 +0,0 @@
1
- import type http from "http";
2
- /**
3
- * Extracts the client IP address from an HTTP request, taking into account proxy configurations.
4
- *
5
- * When behind proxies (like Vercel, Cloudflare, AWS ALB, etc.), the X-Forwarded-For header
6
- * contains a chain of IP addresses. The format is: "client, proxy1, proxy2, ..."
7
- *
8
- * @param req - The HTTP request object
9
- * @param trustProxyDepth - Number of proxy levels to trust
10
- * - 0: Only use req.socket.remoteAddress (no proxy trust)
11
- * - 1: Trust 1 proxy level (get the last IP before your server)
12
- * - 2+: Trust multiple proxy levels (for complex setups)
13
- *
14
- * Examples:
15
- * - trustProxyDepth=0: Direct connection, no proxies
16
- * X-Forwarded-For: ignored
17
- * Result: req.socket.remoteAddress
18
- *
19
- * - trustProxyDepth=1: Behind one proxy (e.g., Vercel, Netlify)
20
- * X-Forwarded-For: "203.0.113.1, 198.51.100.1"
21
- * Result: "203.0.113.1" (client IP)
22
- *
23
- * - trustProxyDepth=2: Behind two proxies (e.g., Cloudflare -> Load Balancer)
24
- * X-Forwarded-For: "203.0.113.1, 198.51.100.1, 192.0.2.1"
25
- * Result: "203.0.113.1" (client IP)
26
- *
27
- * Common configurations:
28
- * - Vercel/Netlify/Railway: trustProxyDepth=1
29
- * - Cloudflare -> Origin: trustProxyDepth=1 or 2 (depending on your setup)
30
- * - AWS ALB -> EC2: trustProxyDepth=1
31
- * - Nginx -> Node: trustProxyDepth=1
32
- * - Cloudflare -> Nginx -> Node: trustProxyDepth=2
33
- */
34
- export declare function extractClientIP(req: http.IncomingMessage, trustProxyDepth?: number): string;
35
- /**
36
- * Alternative extraction method that works from the right (trusts the rightmost IPs).
37
- * This is useful when you want to trust the last N proxies in the chain.
38
- *
39
- * @param req - The HTTP request object
40
- * @param trustProxyDepth - Number of proxy levels to trust from the right
41
- *
42
- * Example:
43
- * X-Forwarded-For: "203.0.113.1, 198.51.100.1, 192.0.2.1"
44
- * trustProxyDepth=1: Result is "198.51.100.1" (skip the last trusted proxy)
45
- * trustProxyDepth=2: Result is "203.0.113.1" (skip the last 2 trusted proxies)
46
- */
47
- export declare function extractClientIPFromRight(req: http.IncomingMessage, trustProxyDepth?: number): string;
48
- //# sourceMappingURL=ipExtractor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ipExtractor.d.ts","sourceRoot":"","sources":["../../src/server/ipExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,eAAe,GAAE,MAAU,GAAG,MAAM,CAqC9F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,eAAe,GAAE,MAAU,GAAG,MAAM,CAsBvG"}
@@ -1,96 +0,0 @@
1
- /**
2
- * Extracts the client IP address from an HTTP request, taking into account proxy configurations.
3
- *
4
- * When behind proxies (like Vercel, Cloudflare, AWS ALB, etc.), the X-Forwarded-For header
5
- * contains a chain of IP addresses. The format is: "client, proxy1, proxy2, ..."
6
- *
7
- * @param req - The HTTP request object
8
- * @param trustProxyDepth - Number of proxy levels to trust
9
- * - 0: Only use req.socket.remoteAddress (no proxy trust)
10
- * - 1: Trust 1 proxy level (get the last IP before your server)
11
- * - 2+: Trust multiple proxy levels (for complex setups)
12
- *
13
- * Examples:
14
- * - trustProxyDepth=0: Direct connection, no proxies
15
- * X-Forwarded-For: ignored
16
- * Result: req.socket.remoteAddress
17
- *
18
- * - trustProxyDepth=1: Behind one proxy (e.g., Vercel, Netlify)
19
- * X-Forwarded-For: "203.0.113.1, 198.51.100.1"
20
- * Result: "203.0.113.1" (client IP)
21
- *
22
- * - trustProxyDepth=2: Behind two proxies (e.g., Cloudflare -> Load Balancer)
23
- * X-Forwarded-For: "203.0.113.1, 198.51.100.1, 192.0.2.1"
24
- * Result: "203.0.113.1" (client IP)
25
- *
26
- * Common configurations:
27
- * - Vercel/Netlify/Railway: trustProxyDepth=1
28
- * - Cloudflare -> Origin: trustProxyDepth=1 or 2 (depending on your setup)
29
- * - AWS ALB -> EC2: trustProxyDepth=1
30
- * - Nginx -> Node: trustProxyDepth=1
31
- * - Cloudflare -> Nginx -> Node: trustProxyDepth=2
32
- */
33
- export function extractClientIP(req, trustProxyDepth = 0) {
34
- // If not trusting any proxies, return the direct connection IP
35
- if (trustProxyDepth === 0) {
36
- return req.socket.remoteAddress || "unknown";
37
- }
38
- // Get X-Forwarded-For header
39
- const forwardedFor = req.headers["x-forwarded-for"];
40
- if (!forwardedFor) {
41
- // No X-Forwarded-For header, fall back to direct connection
42
- return req.socket.remoteAddress || "unknown";
43
- }
44
- // Parse X-Forwarded-For header (can be a string or array of strings)
45
- const forwardedIPs = (Array.isArray(forwardedFor) ? forwardedFor.join(",") : forwardedFor)
46
- .split(",")
47
- .map((ip) => ip.trim())
48
- .filter((ip) => ip.length > 0);
49
- if (forwardedIPs.length === 0) {
50
- // Empty X-Forwarded-For, fall back to direct connection
51
- return req.socket.remoteAddress || "unknown";
52
- }
53
- // The client IP is at the beginning of the chain
54
- // We trust the chain up to trustProxyDepth levels
55
- // Format: [clientIP, proxy1, proxy2, ..., lastProxy]
56
- // We want the clientIP, but we need to verify we have enough trusted proxies
57
- if (forwardedIPs.length < trustProxyDepth) {
58
- // Not enough IPs in the chain, the chain might be incomplete or spoofed
59
- // Fall back to direct connection for safety
60
- return req.socket.remoteAddress || "unknown";
61
- }
62
- // Return the client IP (first in the chain)
63
- return forwardedIPs[0];
64
- }
65
- /**
66
- * Alternative extraction method that works from the right (trusts the rightmost IPs).
67
- * This is useful when you want to trust the last N proxies in the chain.
68
- *
69
- * @param req - The HTTP request object
70
- * @param trustProxyDepth - Number of proxy levels to trust from the right
71
- *
72
- * Example:
73
- * X-Forwarded-For: "203.0.113.1, 198.51.100.1, 192.0.2.1"
74
- * trustProxyDepth=1: Result is "198.51.100.1" (skip the last trusted proxy)
75
- * trustProxyDepth=2: Result is "203.0.113.1" (skip the last 2 trusted proxies)
76
- */
77
- export function extractClientIPFromRight(req, trustProxyDepth = 0) {
78
- if (trustProxyDepth === 0) {
79
- return req.socket.remoteAddress || "unknown";
80
- }
81
- const forwardedFor = req.headers["x-forwarded-for"];
82
- if (!forwardedFor) {
83
- return req.socket.remoteAddress || "unknown";
84
- }
85
- const forwardedIPs = (Array.isArray(forwardedFor) ? forwardedFor.join(",") : forwardedFor)
86
- .split(",")
87
- .map((ip) => ip.trim())
88
- .filter((ip) => ip.length > 0);
89
- if (forwardedIPs.length === 0) {
90
- return req.socket.remoteAddress || "unknown";
91
- }
92
- // Calculate which IP to trust by skipping the rightmost N trusted proxies
93
- const clientIPIndex = Math.max(0, forwardedIPs.length - trustProxyDepth - 1);
94
- return forwardedIPs[clientIPIndex];
95
- }
96
- //# sourceMappingURL=ipExtractor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ipExtractor.js","sourceRoot":"","sources":["../../src/server/ipExtractor.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,eAAe,CAAC,GAAyB,EAAE,kBAA0B,CAAC;IAClF,+DAA+D;IAC/D,IAAI,eAAe,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,6BAA6B;IAC7B,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACpD,IAAI,CAAC,YAAY,EAAE,CAAC;QAChB,4DAA4D;QAC5D,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,qEAAqE;IACrE,MAAM,YAAY,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;SACrF,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;SACtB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEnC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,wDAAwD;QACxD,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,iDAAiD;IACjD,kDAAkD;IAClD,qDAAqD;IACrD,6EAA6E;IAE7E,IAAI,YAAY,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;QACxC,wEAAwE;QACxE,4CAA4C;QAC5C,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,4CAA4C;IAC5C,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAyB,EAAE,kBAA0B,CAAC;IAC3F,IAAI,eAAe,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACpD,IAAI,CAAC,YAAY,EAAE,CAAC;QAChB,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,MAAM,YAAY,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;SACrF,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;SACtB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEnC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,0EAA0E;IAC1E,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,MAAM,GAAG,eAAe,GAAG,CAAC,CAAC,CAAC;IAC7E,OAAO,YAAY,CAAC,aAAa,CAAC,CAAC;AACvC,CAAC"}
@@ -1,4 +0,0 @@
1
- export declare function getRequestPath(url: string | undefined): string;
2
- export declare function isDevInternalOrAssetRequest(url: string | undefined): boolean;
3
- export declare function resolveDirectStaticAssetPath(staticDir: string, url: string | undefined): string | null;
4
- //# sourceMappingURL=requestRouting.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"requestRouting.d.ts","sourceRoot":"","sources":["../../src/server/requestRouting.ts"],"names":[],"mappings":"AAsCA,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE9D;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAS5E;AAED,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAwBtG"}
@@ -1,67 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- const STATIC_ASSET_EXTENSIONS = new Set([
4
- ".js",
5
- ".mjs",
6
- ".cjs",
7
- ".ts",
8
- ".tsx",
9
- ".jsx",
10
- ".css",
11
- ".map",
12
- ".json",
13
- ".png",
14
- ".jpg",
15
- ".jpeg",
16
- ".gif",
17
- ".svg",
18
- ".ico",
19
- ".webp",
20
- ".avif",
21
- ".woff",
22
- ".woff2",
23
- ".ttf",
24
- ".eot",
25
- ".otf",
26
- ".mp4",
27
- ".webm",
28
- ".ogg",
29
- ".mp3",
30
- ".wav",
31
- ".txt",
32
- ".xml",
33
- ".wasm",
34
- ]);
35
- const DEV_INTERNAL_PREFIXES = ["/@vite", "/@fs/", "/@id/", "/__vite", "/src/", "/node_modules/"];
36
- export function getRequestPath(url) {
37
- return (url || "/").split("?")[0] || "/";
38
- }
39
- export function isDevInternalOrAssetRequest(url) {
40
- const requestPath = getRequestPath(url);
41
- if (DEV_INTERNAL_PREFIXES.some((prefix) => requestPath.startsWith(prefix))) {
42
- return true;
43
- }
44
- const extension = path.extname(requestPath).toLowerCase();
45
- return extension.length > 0 && STATIC_ASSET_EXTENSIONS.has(extension);
46
- }
47
- export function resolveDirectStaticAssetPath(staticDir, url) {
48
- const requestPath = getRequestPath(url);
49
- const extension = path.extname(requestPath).toLowerCase();
50
- if (!extension) {
51
- return null;
52
- }
53
- const resolvedStaticDir = path.resolve(staticDir);
54
- const candidatePath = path.resolve(staticDir, "." + requestPath);
55
- if (!candidatePath.startsWith(resolvedStaticDir + path.sep) && candidatePath !== resolvedStaticDir) {
56
- return null;
57
- }
58
- if (!fs.existsSync(candidatePath)) {
59
- return null;
60
- }
61
- const stat = fs.statSync(candidatePath);
62
- if (!stat.isFile()) {
63
- return null;
64
- }
65
- return candidatePath;
66
- }
67
- //# sourceMappingURL=requestRouting.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"requestRouting.js","sourceRoot":"","sources":["../../src/server/requestRouting.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC;IACpC,KAAK;IACL,MAAM;IACN,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;IACP,QAAQ;IACR,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;CACV,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC;AAEjG,MAAM,UAAU,cAAc,CAAC,GAAuB;IAClD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,GAAuB;IAC/D,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAExC,IAAI,qBAAqB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QACzE,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1D,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,uBAAuB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,SAAiB,EAAE,GAAuB;IACnF,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;IAE1D,IAAI,CAAC,SAAS,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,GAAG,WAAW,CAAC,CAAC;IACjE,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,aAAa,KAAK,iBAAiB,EAAE,CAAC;QACjG,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,OAAO,aAAa,CAAC;AACzB,CAAC","sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nconst STATIC_ASSET_EXTENSIONS = new Set([\n \".js\",\n \".mjs\",\n \".cjs\",\n \".ts\",\n \".tsx\",\n \".jsx\",\n \".css\",\n \".map\",\n \".json\",\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".svg\",\n \".ico\",\n \".webp\",\n \".avif\",\n \".woff\",\n \".woff2\",\n \".ttf\",\n \".eot\",\n \".otf\",\n \".mp4\",\n \".webm\",\n \".ogg\",\n \".mp3\",\n \".wav\",\n \".txt\",\n \".xml\",\n \".wasm\",\n]);\n\nconst DEV_INTERNAL_PREFIXES = [\"/@vite\", \"/@fs/\", \"/@id/\", \"/__vite\", \"/src/\", \"/node_modules/\"];\n\nexport function getRequestPath(url: string | undefined): string {\n return (url || \"/\").split(\"?\")[0] || \"/\";\n}\n\nexport function isDevInternalOrAssetRequest(url: string | undefined): boolean {\n const requestPath = getRequestPath(url);\n\n if (DEV_INTERNAL_PREFIXES.some((prefix) => requestPath.startsWith(prefix))) {\n return true;\n }\n\n const extension = path.extname(requestPath).toLowerCase();\n return extension.length > 0 && STATIC_ASSET_EXTENSIONS.has(extension);\n}\n\nexport function resolveDirectStaticAssetPath(staticDir: string, url: string | undefined): string | null {\n const requestPath = getRequestPath(url);\n const extension = path.extname(requestPath).toLowerCase();\n\n if (!extension) {\n return null;\n }\n\n const resolvedStaticDir = path.resolve(staticDir);\n const candidatePath = path.resolve(staticDir, \".\" + requestPath);\n if (!candidatePath.startsWith(resolvedStaticDir + path.sep) && candidatePath !== resolvedStaticDir) {\n return null;\n }\n\n if (!fs.existsSync(candidatePath)) {\n return null;\n }\n\n const stat = fs.statSync(candidatePath);\n if (!stat.isFile()) {\n return null;\n }\n\n return candidatePath;\n}\n"]}
@@ -1 +0,0 @@
1
- //# sourceMappingURL=deepEqual.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"deepEqual.d.ts","sourceRoot":"","sources":["../../src/utils/deepEqual.ts"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- "use strict";
2
- //# sourceMappingURL=deepEqual.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"deepEqual.js","sourceRoot":"","sources":["../../src/utils/deepEqual.ts"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export declare function formatError(err: unknown): string;
2
- //# sourceMappingURL=formatError.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"formatError.d.ts","sourceRoot":"","sources":["../../src/utils/formatError.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAgBhD"}
@@ -1,18 +0,0 @@
1
- export function formatError(err) {
2
- console.log("🚀 ~ formatError ~ err:", err);
3
- if (err instanceof Error) {
4
- return err.message;
5
- }
6
- if (typeof err === "object" && err !== null) {
7
- if ("message" in err) {
8
- return String(err.message);
9
- }
10
- // Format Record<string, string> errors
11
- return JSON.stringify(err, null, 2);
12
- }
13
- if (typeof err === "string") {
14
- return err;
15
- }
16
- return String(err);
17
- }
18
- //# sourceMappingURL=formatError.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"formatError.js","sourceRoot":"","sources":["../../src/utils/formatError.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,GAAY;IACpC,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAC;IAC5C,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,OAAO,CAAC;IACvB,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC1C,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;YACnB,OAAO,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QACD,uCAAuC;QACvC,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC"}