heliumts 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/bin/helium.js +6 -3
  2. package/dist/bin/helium.js.map +1 -1
  3. package/dist/client/Router.d.ts.map +1 -1
  4. package/dist/client/Router.js +10 -9
  5. package/dist/client/Router.js.map +1 -1
  6. package/dist/client/cache.d.ts.map +1 -1
  7. package/dist/client/cache.js +2 -1
  8. package/dist/client/cache.js.map +1 -1
  9. package/dist/client/env.d.ts +5 -0
  10. package/dist/client/env.d.ts.map +1 -0
  11. package/dist/client/env.js +14 -0
  12. package/dist/client/env.js.map +1 -0
  13. package/dist/server/defineSEOMetadata.d.ts +23 -0
  14. package/dist/server/defineSEOMetadata.d.ts.map +1 -0
  15. package/dist/server/defineSEOMetadata.js +20 -0
  16. package/dist/server/defineSEOMetadata.js.map +1 -0
  17. package/dist/server/devServer.d.ts +2 -1
  18. package/dist/server/devServer.d.ts.map +1 -1
  19. package/dist/server/devServer.js +96 -1
  20. package/dist/server/devServer.js.map +1 -1
  21. package/dist/server/index.d.ts +2 -0
  22. package/dist/server/index.d.ts.map +1 -1
  23. package/dist/server/index.js +2 -0
  24. package/dist/server/index.js.map +1 -1
  25. package/dist/server/ipExtractor.d.ts +48 -0
  26. package/dist/server/ipExtractor.d.ts.map +1 -0
  27. package/dist/server/ipExtractor.js +96 -0
  28. package/dist/server/ipExtractor.js.map +1 -0
  29. package/dist/server/meta.d.ts +16 -0
  30. package/dist/server/meta.d.ts.map +1 -0
  31. package/dist/server/meta.js +69 -0
  32. package/dist/server/meta.js.map +1 -0
  33. package/dist/server/prodServer.d.ts +2 -1
  34. package/dist/server/prodServer.d.ts.map +1 -1
  35. package/dist/server/prodServer.js +23 -2
  36. package/dist/server/prodServer.js.map +1 -1
  37. package/dist/server/requestRouting.d.ts +4 -0
  38. package/dist/server/requestRouting.d.ts.map +1 -0
  39. package/dist/server/requestRouting.js +67 -0
  40. package/dist/server/requestRouting.js.map +1 -0
  41. package/dist/server/seoMetadataRouter.d.ts +22 -0
  42. package/dist/server/seoMetadataRouter.d.ts.map +1 -0
  43. package/dist/server/seoMetadataRouter.js +132 -0
  44. package/dist/server/seoMetadataRouter.js.map +1 -0
  45. package/dist/utils/deepEqual.d.ts +1 -0
  46. package/dist/utils/deepEqual.d.ts.map +1 -0
  47. package/dist/utils/deepEqual.js +2 -0
  48. package/dist/utils/deepEqual.js.map +1 -0
  49. package/dist/utils/formatError.d.ts +2 -0
  50. package/dist/utils/formatError.d.ts.map +1 -0
  51. package/dist/utils/formatError.js +18 -0
  52. package/dist/utils/formatError.js.map +1 -0
  53. package/dist/vite/heliumPlugin.d.ts.map +1 -1
  54. package/dist/vite/heliumPlugin.js +15 -5
  55. package/dist/vite/heliumPlugin.js.map +1 -1
  56. package/dist/vite/scanner.d.ts +6 -0
  57. package/dist/vite/scanner.d.ts.map +1 -1
  58. package/dist/vite/scanner.js +42 -2
  59. package/dist/vite/scanner.js.map +1 -1
  60. package/dist/vite/virtualServerModule.d.ts +2 -2
  61. package/dist/vite/virtualServerModule.d.ts.map +1 -1
  62. package/dist/vite/virtualServerModule.js +10 -1
  63. package/dist/vite/virtualServerModule.js.map +1 -1
  64. package/package.json +2 -1
@@ -8,8 +8,10 @@ import { log } from "../utils/logger.js";
8
8
  import { getRpcConfig, getRpcSecurityConfig, getTrustProxyDepth } from "./config.js";
9
9
  import { startWorker, stopAllWorkers } from "./defineWorker.js";
10
10
  import { HTTPRouter } from "./httpRouter.js";
11
+ import { injectSocialMetaIntoHtml } from "./meta.js";
11
12
  import { RateLimiter } from "./rateLimiter.js";
12
13
  import { RpcRegistry } from "./rpcRegistry.js";
14
+ import { SEOMetadataRouter } from "./seoMetadataRouter.js";
13
15
  import { initializeSecurity, verifyConnectionToken } from "./security.js";
14
16
  import { prepareForMsgpack } from "./serializer.js";
15
17
  const gzipAsync = promisify(gzip);
@@ -17,6 +19,7 @@ const deflateAsync = promisify(deflate);
17
19
  const brotliCompressAsync = promisify(brotliCompress);
18
20
  let currentRegistry = null;
19
21
  let currentHttpRouter = null;
22
+ let currentSEORouter = null;
20
23
  let wss = null;
21
24
  let rateLimiter = null;
22
25
  let currentWorkers = [];
@@ -38,12 +41,14 @@ export function attachToDevServer(httpServer, loadHandlers, config = {}, workers
38
41
  rateLimiter = new RateLimiter(rpcSecurity.maxMessagesPerWindow, rpcSecurity.rateLimitWindowMs, rpcSecurity.maxConnectionsPerIP);
39
42
  const registry = new RpcRegistry();
40
43
  const httpRouter = new HTTPRouter();
44
+ const seoRouter = new SEOMetadataRouter();
41
45
  httpRouter.setTrustProxyDepth(trustProxyDepth);
42
- loadHandlers(registry, httpRouter);
46
+ loadHandlers(registry, httpRouter, seoRouter);
43
47
  registry.setRateLimiter(rateLimiter);
44
48
  registry.setMaxBatchSize(rpcConfig.maxBatchSize);
45
49
  currentRegistry = registry;
46
50
  currentHttpRouter = httpRouter;
51
+ currentSEORouter = seoRouter;
47
52
  // Start workers if they changed
48
53
  const workersChanged = workers.length !== currentWorkers.length || workers.some((w, i) => w.name !== currentWorkers[i]?.name || w.worker !== currentWorkers[i]?.worker);
49
54
  if (workersChanged && workers.length > 0) {
@@ -327,10 +332,100 @@ export function attachToDevServer(httpServer, loadHandlers, config = {}, workers
327
332
  return;
328
333
  }
329
334
  }
335
+ let devResolvedMetadata = null;
336
+ if (currentSEORouter) {
337
+ const ip = extractClientIP(req, trustProxyDepth);
338
+ const httpCtx = {
339
+ req: {
340
+ ip,
341
+ headers: req.headers,
342
+ url: req.url,
343
+ method: req.method,
344
+ raw: req,
345
+ },
346
+ };
347
+ devResolvedMetadata = await currentSEORouter.resolve(req, httpCtx);
348
+ }
349
+ if (devResolvedMetadata) {
350
+ const originalWriteHead = res.writeHead.bind(res);
351
+ const originalWrite = res.write.bind(res);
352
+ const originalEnd = res.end.bind(res);
353
+ const bufferedChunks = [];
354
+ let capturedContentType = "";
355
+ res.writeHead = (statusCode, statusMessageOrHeaders, headers) => {
356
+ const providedHeaders = typeof statusMessageOrHeaders === "string"
357
+ ? headers
358
+ : statusMessageOrHeaders;
359
+ if (providedHeaders) {
360
+ if (Array.isArray(providedHeaders)) {
361
+ for (let i = 0; i < providedHeaders.length; i += 2) {
362
+ if (String(providedHeaders[i]).toLowerCase() === "content-type") {
363
+ capturedContentType = String(providedHeaders[i + 1] || "").toLowerCase();
364
+ break;
365
+ }
366
+ }
367
+ }
368
+ else if (typeof providedHeaders === "object") {
369
+ const maybeContentType = providedHeaders["Content-Type"] ?? providedHeaders["content-type"];
370
+ if (maybeContentType) {
371
+ capturedContentType = String(maybeContentType).toLowerCase();
372
+ }
373
+ }
374
+ }
375
+ return originalWriteHead(statusCode, statusMessageOrHeaders, headers);
376
+ };
377
+ res.write = (chunk, encoding, callback) => {
378
+ if (chunk !== undefined && chunk !== null) {
379
+ bufferedChunks.push(toBuffer(chunk, typeof encoding === "string" ? encoding : undefined));
380
+ }
381
+ if (typeof encoding === "function") {
382
+ encoding();
383
+ }
384
+ else if (typeof callback === "function") {
385
+ callback();
386
+ }
387
+ return true;
388
+ };
389
+ res.end = (chunk, encoding, callback) => {
390
+ if (chunk !== undefined && chunk !== null) {
391
+ bufferedChunks.push(toBuffer(chunk, typeof encoding === "string" ? encoding : undefined));
392
+ }
393
+ if (typeof encoding === "function") {
394
+ encoding();
395
+ }
396
+ else if (typeof callback === "function") {
397
+ callback();
398
+ }
399
+ const bodyBuffer = Buffer.concat(bufferedChunks);
400
+ const bodyText = bodyBuffer.toString("utf-8");
401
+ const contentType = capturedContentType || String(res.getHeader("content-type") || "").toLowerCase();
402
+ const looksLikeHtml = /^\s*<!doctype\s+html|^\s*<html[\s>]/i.test(bodyText);
403
+ res.writeHead = originalWriteHead;
404
+ res.write = originalWrite;
405
+ res.end = originalEnd;
406
+ if (contentType.includes("text/html") || looksLikeHtml) {
407
+ const injectedHtml = injectSocialMetaIntoHtml(bodyText, devResolvedMetadata);
408
+ return originalEnd(Buffer.from(injectedHtml, "utf-8"));
409
+ }
410
+ return originalEnd(bodyBuffer);
411
+ };
412
+ }
330
413
  // If no handler matched, pass to original Vite handlers
331
414
  for (const listener of originalListeners) {
332
415
  listener(req, res);
333
416
  }
334
417
  });
335
418
  }
419
+ function toBuffer(chunk, encoding) {
420
+ if (Buffer.isBuffer(chunk)) {
421
+ return chunk;
422
+ }
423
+ if (chunk instanceof Uint8Array) {
424
+ return Buffer.from(chunk);
425
+ }
426
+ if (typeof chunk === "string") {
427
+ return Buffer.from(chunk, encoding);
428
+ }
429
+ return Buffer.from(String(chunk), encoding);
430
+ }
336
431
  //# sourceMappingURL=devServer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"devServer.js","sourceRoot":"","sources":["../../src/server/devServer.ts"],"names":[],"mappings":"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","sourcesContent":["import { encode as msgpackEncode } from \"@msgpack/msgpack\";\nimport type http from \"http\";\nimport type http2 from \"http2\";\nimport type https from \"https\";\nimport { promisify } from \"util\";\nimport type WebSocket from \"ws\";\nimport { WebSocketServer } from \"ws\";\nimport { brotliCompress, deflate, gzip } from \"zlib\";\n\nimport { injectEnvToProcess, loadEnvFiles } from \"../utils/envLoader.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 { RateLimiter } from \"./rateLimiter.js\";\nimport { RpcRegistry } from \"./rpcRegistry.js\";\nimport { initializeSecurity, verifyConnectionToken } from \"./security.js\";\nimport { prepareForMsgpack } from \"./serializer.js\";\n\nconst gzipAsync = promisify(gzip);\nconst deflateAsync = promisify(deflate);\nconst brotliCompressAsync = promisify(brotliCompress);\n\ntype LoadHandlersFn = (registry: RpcRegistry, httpRouter: HTTPRouter) => void;\ntype HttpServer = http.Server | https.Server | http2.Http2Server | http2.Http2SecureServer;\n\ninterface WorkerEntry {\n name: string;\n worker: HeliumWorkerDef;\n}\n\nlet currentRegistry: RpcRegistry | null = null;\nlet currentHttpRouter: HTTPRouter | null = null;\nlet wss: WebSocketServer | null = null;\nlet rateLimiter: RateLimiter | null = null;\nlet currentWorkers: WorkerEntry[] = [];\n\n/**\n * Attaches HeliumTS HTTP handlers and WebSocket RPC server to an existing HTTP server.\n * This is used in dev mode to attach to Vite's dev server.\n */\nexport function attachToDevServer(httpServer: HttpServer, loadHandlers: LoadHandlersFn, config: HeliumConfig = {}, workers: WorkerEntry[] = []) {\n // Load environment variables for server-side access\n const envVars = loadEnvFiles();\n injectEnvToProcess(envVars);\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 // Re-initialize rate limiter with new config (always recreate in dev mode to pick up config changes)\n rateLimiter = new RateLimiter(rpcSecurity.maxMessagesPerWindow, rpcSecurity.rateLimitWindowMs, rpcSecurity.maxConnectionsPerIP);\n\n const registry = new RpcRegistry();\n const httpRouter = new HTTPRouter();\n httpRouter.setTrustProxyDepth(trustProxyDepth);\n loadHandlers(registry, httpRouter);\n registry.setRateLimiter(rateLimiter);\n registry.setMaxBatchSize(rpcConfig.maxBatchSize);\n currentRegistry = registry;\n currentHttpRouter = httpRouter;\n\n // Start workers if they changed\n const workersChanged = workers.length !== currentWorkers.length || workers.some((w, i) => w.name !== currentWorkers[i]?.name || w.worker !== currentWorkers[i]?.worker);\n\n if (workersChanged && workers.length > 0) {\n // Stop all existing workers before starting new ones\n stopAllWorkers().then(() => {\n // Start new workers\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 currentWorkers = workers;\n });\n } else if (currentWorkers.length === 0 && workers.length > 0) {\n // First time starting workers\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 currentWorkers = workers;\n }\n\n // Attach WebSocket server if not already attached\n if (!wss) {\n 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 if (currentRegistry) {\n currentRegistry.setSocketMetadata(socket, ip, req);\n }\n\n // Track connection and check IP limit\n if (rateLimiter && !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 && !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 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 // Always use the current registry (may have been updated)\n if (currentRegistry) {\n currentRegistry.handleMessage(socket, Buffer.isBuffer(msg) ? msg : Buffer.from(msg as any));\n }\n });\n });\n\n // Handle WebSocket upgrade requests\n httpServer.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 (rateLimiter && 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 }\n });\n\n log(\"info\", \"WebSocket RPC attached to dev server at /rpc\");\n }\n\n // Security: max body size for HTTP requests\n const maxBodySize = rpcConfig.maxBodySize ?? 1_048_576;\n\n // Attach HTTP request handler\n // We need to intercept requests before Vite handles them\n const originalListeners = httpServer.listeners(\"request\").slice();\n httpServer.removeAllListeners(\"request\");\n\n httpServer.on(\"request\", async (req: any, res: any) => {\n // Handle token refresh endpoint\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 { generateConnectionToken } = await import(\"./security.js\");\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 if (!currentRegistry) {\n res.writeHead(503, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: false, error: \"Server not ready\" }));\n return;\n }\n\n const body = Buffer.concat(chunks);\n const ip = extractClientIP(req, trustProxyDepth);\n const result = await currentRegistry.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\n if (currentHttpRouter) {\n const handled = await currentHttpRouter.handleRequest(req, res);\n if (handled) {\n return;\n }\n }\n\n // If no handler matched, pass to original Vite handlers\n for (const listener of originalListeners) {\n (listener as any)(req, res);\n }\n });\n}\n"]}
1
+ {"version":3,"file":"devServer.js","sourceRoot":"","sources":["../../src/server/devServer.ts"],"names":[],"mappings":"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","sourcesContent":["import { encode as msgpackEncode } from \"@msgpack/msgpack\";\nimport type http from \"http\";\nimport type http2 from \"http2\";\nimport type https from \"https\";\nimport { promisify } from \"util\";\nimport type WebSocket from \"ws\";\nimport { WebSocketServer } from \"ws\";\nimport { brotliCompress, deflate, gzip } from \"zlib\";\n\nimport { injectEnvToProcess, loadEnvFiles } from \"../utils/envLoader.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 } from \"./meta.js\";\nimport { RateLimiter } from \"./rateLimiter.js\";\nimport { RpcRegistry } from \"./rpcRegistry.js\";\nimport { SEOMetadataRouter } from \"./seoMetadataRouter.js\";\nimport { initializeSecurity, verifyConnectionToken } from \"./security.js\";\nimport { prepareForMsgpack } from \"./serializer.js\";\n\nconst gzipAsync = promisify(gzip);\nconst deflateAsync = promisify(deflate);\nconst brotliCompressAsync = promisify(brotliCompress);\n\ntype LoadHandlersFn = (registry: RpcRegistry, httpRouter: HTTPRouter, seoRouter: SEOMetadataRouter) => void;\ntype HttpServer = http.Server | https.Server | http2.Http2Server | http2.Http2SecureServer;\n\ninterface WorkerEntry {\n name: string;\n worker: HeliumWorkerDef;\n}\n\nlet currentRegistry: RpcRegistry | null = null;\nlet currentHttpRouter: HTTPRouter | null = null;\nlet currentSEORouter: SEOMetadataRouter | null = null;\nlet wss: WebSocketServer | null = null;\nlet rateLimiter: RateLimiter | null = null;\nlet currentWorkers: WorkerEntry[] = [];\n\n/**\n * Attaches HeliumTS HTTP handlers and WebSocket RPC server to an existing HTTP server.\n * This is used in dev mode to attach to Vite's dev server.\n */\nexport function attachToDevServer(httpServer: HttpServer, loadHandlers: LoadHandlersFn, config: HeliumConfig = {}, workers: WorkerEntry[] = []) {\n // Load environment variables for server-side access\n const envVars = loadEnvFiles();\n injectEnvToProcess(envVars);\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 // Re-initialize rate limiter with new config (always recreate in dev mode to pick up config changes)\n rateLimiter = new RateLimiter(rpcSecurity.maxMessagesPerWindow, rpcSecurity.rateLimitWindowMs, rpcSecurity.maxConnectionsPerIP);\n\n const registry = new RpcRegistry();\n const httpRouter = new HTTPRouter();\n const seoRouter = new SEOMetadataRouter();\n httpRouter.setTrustProxyDepth(trustProxyDepth);\n loadHandlers(registry, httpRouter, seoRouter);\n registry.setRateLimiter(rateLimiter);\n registry.setMaxBatchSize(rpcConfig.maxBatchSize);\n currentRegistry = registry;\n currentHttpRouter = httpRouter;\n currentSEORouter = seoRouter;\n\n // Start workers if they changed\n const workersChanged = workers.length !== currentWorkers.length || workers.some((w, i) => w.name !== currentWorkers[i]?.name || w.worker !== currentWorkers[i]?.worker);\n\n if (workersChanged && workers.length > 0) {\n // Stop all existing workers before starting new ones\n stopAllWorkers().then(() => {\n // Start new workers\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 currentWorkers = workers;\n });\n } else if (currentWorkers.length === 0 && workers.length > 0) {\n // First time starting workers\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 currentWorkers = workers;\n }\n\n // Attach WebSocket server if not already attached\n if (!wss) {\n 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 if (currentRegistry) {\n currentRegistry.setSocketMetadata(socket, ip, req);\n }\n\n // Track connection and check IP limit\n if (rateLimiter && !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 && !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 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 // Always use the current registry (may have been updated)\n if (currentRegistry) {\n currentRegistry.handleMessage(socket, Buffer.isBuffer(msg) ? msg : Buffer.from(msg as any));\n }\n });\n });\n\n // Handle WebSocket upgrade requests\n httpServer.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 (rateLimiter && 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 }\n });\n\n log(\"info\", \"WebSocket RPC attached to dev server at /rpc\");\n }\n\n // Security: max body size for HTTP requests\n const maxBodySize = rpcConfig.maxBodySize ?? 1_048_576;\n\n // Attach HTTP request handler\n // We need to intercept requests before Vite handles them\n const originalListeners = httpServer.listeners(\"request\").slice();\n httpServer.removeAllListeners(\"request\");\n\n httpServer.on(\"request\", async (req: any, res: any) => {\n // Handle token refresh endpoint\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 { generateConnectionToken } = await import(\"./security.js\");\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 if (!currentRegistry) {\n res.writeHead(503, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: false, error: \"Server not ready\" }));\n return;\n }\n\n const body = Buffer.concat(chunks);\n const ip = extractClientIP(req, trustProxyDepth);\n const result = await currentRegistry.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\n if (currentHttpRouter) {\n const handled = await currentHttpRouter.handleRequest(req, res);\n if (handled) {\n return;\n }\n }\n\n let devResolvedMetadata: Awaited<ReturnType<SEOMetadataRouter[\"resolve\"]>> = null;\n if (currentSEORouter) {\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 devResolvedMetadata = await currentSEORouter.resolve(req, httpCtx);\n }\n\n if (devResolvedMetadata) {\n const originalWriteHead = res.writeHead.bind(res);\n const originalWrite = res.write.bind(res);\n const originalEnd = res.end.bind(res);\n const bufferedChunks: Buffer[] = [];\n let capturedContentType = \"\";\n\n res.writeHead = (statusCode: number, statusMessageOrHeaders?: any, headers?: any) => {\n const providedHeaders =\n typeof statusMessageOrHeaders === \"string\"\n ? headers\n : statusMessageOrHeaders;\n\n if (providedHeaders) {\n if (Array.isArray(providedHeaders)) {\n for (let i = 0; i < providedHeaders.length; i += 2) {\n if (String(providedHeaders[i]).toLowerCase() === \"content-type\") {\n capturedContentType = String(providedHeaders[i + 1] || \"\").toLowerCase();\n break;\n }\n }\n } else if (typeof providedHeaders === \"object\") {\n const maybeContentType = providedHeaders[\"Content-Type\"] ?? providedHeaders[\"content-type\"];\n if (maybeContentType) {\n capturedContentType = String(maybeContentType).toLowerCase();\n }\n }\n }\n\n return originalWriteHead(statusCode, statusMessageOrHeaders, headers);\n };\n\n res.write = (chunk: any, encoding?: BufferEncoding | (() => void), callback?: () => void) => {\n if (chunk !== undefined && chunk !== null) {\n bufferedChunks.push(toBuffer(chunk, typeof encoding === \"string\" ? encoding : undefined));\n }\n\n if (typeof encoding === \"function\") {\n encoding();\n } else if (typeof callback === \"function\") {\n callback();\n }\n\n return true;\n };\n\n res.end = (chunk?: any, encoding?: BufferEncoding | (() => void), callback?: () => void) => {\n if (chunk !== undefined && chunk !== null) {\n bufferedChunks.push(toBuffer(chunk, typeof encoding === \"string\" ? encoding : undefined));\n }\n\n if (typeof encoding === \"function\") {\n encoding();\n } else if (typeof callback === \"function\") {\n callback();\n }\n\n const bodyBuffer = Buffer.concat(bufferedChunks);\n const bodyText = bodyBuffer.toString(\"utf-8\");\n const contentType = capturedContentType || String(res.getHeader(\"content-type\") || \"\").toLowerCase();\n const looksLikeHtml = /^\\s*<!doctype\\s+html|^\\s*<html[\\s>]/i.test(bodyText);\n\n res.writeHead = originalWriteHead;\n res.write = originalWrite;\n res.end = originalEnd;\n\n if (contentType.includes(\"text/html\") || looksLikeHtml) {\n const injectedHtml = injectSocialMetaIntoHtml(bodyText, devResolvedMetadata);\n return originalEnd(Buffer.from(injectedHtml, \"utf-8\"));\n }\n\n return originalEnd(bodyBuffer);\n };\n }\n\n // If no handler matched, pass to original Vite handlers\n for (const listener of originalListeners) {\n (listener as any)(req, res);\n }\n });\n}\n\nfunction toBuffer(chunk: unknown, encoding?: BufferEncoding): Buffer {\n if (Buffer.isBuffer(chunk)) {\n return chunk;\n }\n\n if (chunk instanceof Uint8Array) {\n return Buffer.from(chunk);\n }\n\n if (typeof chunk === \"string\") {\n return Buffer.from(chunk, encoding);\n }\n\n return Buffer.from(String(chunk), encoding);\n}\n"]}
@@ -2,7 +2,9 @@ export * from "./config.js";
2
2
  export * from "./context.js";
3
3
  export * from "./defineHTTPRequest.js";
4
4
  export * from "./defineMethod.js";
5
+ export * from "./defineSEOMetadata.js";
5
6
  export * from "./defineWorker.js";
7
+ export * from "./meta.js";
6
8
  export * from "./middleware.js";
7
9
  export type { HTTPRouter } from "./httpRouter.js";
8
10
  export { startProdServer } from "./prodServer.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAGhC,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAI/C,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAGhC,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAI/C,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC"}
@@ -3,7 +3,9 @@ export * from "./config.js";
3
3
  export * from "./context.js";
4
4
  export * from "./defineHTTPRequest.js";
5
5
  export * from "./defineMethod.js";
6
+ export * from "./defineSEOMetadata.js";
6
7
  export * from "./defineWorker.js";
8
+ export * from "./meta.js";
7
9
  export * from "./middleware.js";
8
10
  export { startProdServer } from "./prodServer.js";
9
11
  export { PublicError } from "./rpcRegistry.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAIhC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,yEAAyE;AACzE,8FAA8F;AAC9F,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["// Public API for users\nexport * from \"./config.js\";\nexport * from \"./context.js\";\nexport * from \"./defineHTTPRequest.js\";\nexport * from \"./defineMethod.js\";\nexport * from \"./defineWorker.js\";\nexport * from \"./middleware.js\";\n\n// Production server API\nexport type { HTTPRouter } from \"./httpRouter.js\";\nexport { startProdServer } from \"./prodServer.js\";\nexport type { RpcRegistry } from \"./rpcRegistry.js\";\nexport { PublicError } from \"./rpcRegistry.js\";\n\n// Internal utilities needed by generated server code (from helium build)\n// Note: These are exported for framework-generated code only, not for direct user consumption\nexport { injectEnvToProcess, loadEnvFiles } from \"../utils/envLoader.js\";\nexport { log } from \"../utils/logger.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAIhC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,yEAAyE;AACzE,8FAA8F;AAC9F,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["// Public API for users\nexport * from \"./config.js\";\nexport * from \"./context.js\";\nexport * from \"./defineHTTPRequest.js\";\nexport * from \"./defineMethod.js\";\nexport * from \"./defineSEOMetadata.js\";\nexport * from \"./defineWorker.js\";\nexport * from \"./meta.js\";\nexport * from \"./middleware.js\";\n\n// Production server API\nexport type { HTTPRouter } from \"./httpRouter.js\";\nexport { startProdServer } from \"./prodServer.js\";\nexport type { RpcRegistry } from \"./rpcRegistry.js\";\nexport { PublicError } from \"./rpcRegistry.js\";\n\n// Internal utilities needed by generated server code (from helium build)\n// Note: These are exported for framework-generated code only, not for direct user consumption\nexport { injectEnvToProcess, loadEnvFiles } from \"../utils/envLoader.js\";\nexport { log } from \"../utils/logger.js\";\n"]}
@@ -0,0 +1,48 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,96 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,16 @@
1
+ export interface SocialMeta {
2
+ title: string;
3
+ description?: string;
4
+ image?: string;
5
+ canonicalUrl?: string;
6
+ siteName?: string;
7
+ type?: string;
8
+ robots?: string;
9
+ twitterCard?: "summary" | "summary_large_image" | "app" | "player";
10
+ twitterSite?: string;
11
+ twitterCreator?: string;
12
+ }
13
+ export declare function escapeHtml(value: string): string;
14
+ export declare function buildSocialMetaTags(meta: SocialMeta): string;
15
+ export declare function injectSocialMetaIntoHtml(html: string, meta: SocialMeta): string;
16
+ //# sourceMappingURL=meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../src/server/meta.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,SAAS,GAAG,qBAAqB,GAAG,KAAK,GAAG,QAAQ,CAAC;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUhD;AAYD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CA6C5D;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAc/E"}
@@ -0,0 +1,69 @@
1
+ export function escapeHtml(value) {
2
+ const htmlEscapes = {
3
+ "&": "&amp;",
4
+ "<": "&lt;",
5
+ ">": "&gt;",
6
+ '"': "&quot;",
7
+ "'": "&#39;",
8
+ };
9
+ return value.replace(/[&<>"']/g, (character) => htmlEscapes[character]);
10
+ }
11
+ function stripExistingHeadMeta(headContent) {
12
+ return headContent
13
+ .replace(/<title[^>]*>[\s\S]*?<\/title>/gi, "")
14
+ .replace(/<meta\s+name=["']description["'][^>]*>/gi, "")
15
+ .replace(/<meta\s+name=["']robots["'][^>]*>/gi, "")
16
+ .replace(/<meta\s+property=["']og:[^"']+["'][^>]*>/gi, "")
17
+ .replace(/<meta\s+name=["']twitter:[^"']+["'][^>]*>/gi, "")
18
+ .replace(/<link\s+rel=["']canonical["'][^>]*>/gi, "");
19
+ }
20
+ export function buildSocialMetaTags(meta) {
21
+ const type = meta.type ?? "website";
22
+ const twitterCard = meta.twitterCard ?? "summary_large_image";
23
+ const tags = [
24
+ `<title>${escapeHtml(meta.title)}</title>`,
25
+ `<meta property="og:title" content="${escapeHtml(meta.title)}" />`,
26
+ `<meta property="og:type" content="${escapeHtml(type)}" />`,
27
+ `<meta name="twitter:card" content="${escapeHtml(twitterCard)}" />`,
28
+ `<meta name="twitter:title" content="${escapeHtml(meta.title)}" />`,
29
+ ];
30
+ if (meta.description) {
31
+ tags.push(`<meta name="description" content="${escapeHtml(meta.description)}" />`);
32
+ tags.push(`<meta property="og:description" content="${escapeHtml(meta.description)}" />`);
33
+ tags.push(`<meta name="twitter:description" content="${escapeHtml(meta.description)}" />`);
34
+ }
35
+ if (meta.image) {
36
+ tags.push(`<meta property="og:image" content="${escapeHtml(meta.image)}" />`);
37
+ tags.push(`<meta name="twitter:image" content="${escapeHtml(meta.image)}" />`);
38
+ }
39
+ if (meta.canonicalUrl) {
40
+ tags.push(`<meta property="og:url" content="${escapeHtml(meta.canonicalUrl)}" />`);
41
+ tags.push(`<link rel="canonical" href="${escapeHtml(meta.canonicalUrl)}" />`);
42
+ }
43
+ if (meta.siteName) {
44
+ tags.push(`<meta property="og:site_name" content="${escapeHtml(meta.siteName)}" />`);
45
+ }
46
+ if (meta.robots) {
47
+ tags.push(`<meta name="robots" content="${escapeHtml(meta.robots)}" />`);
48
+ }
49
+ if (meta.twitterSite) {
50
+ tags.push(`<meta name="twitter:site" content="${escapeHtml(meta.twitterSite)}" />`);
51
+ }
52
+ if (meta.twitterCreator) {
53
+ tags.push(`<meta name="twitter:creator" content="${escapeHtml(meta.twitterCreator)}" />`);
54
+ }
55
+ return tags.join("\n");
56
+ }
57
+ export function injectSocialMetaIntoHtml(html, meta) {
58
+ const generated = buildSocialMetaTags(meta);
59
+ const headMatch = html.match(/<head[^>]*>([\s\S]*?)<\/head>/i);
60
+ if (!headMatch) {
61
+ return `${generated}\n${html}`;
62
+ }
63
+ const fullHead = headMatch[0];
64
+ const innerHead = headMatch[1];
65
+ const cleanedInnerHead = stripExistingHeadMeta(innerHead).trim();
66
+ const replacementHead = `<head>\n${generated}${cleanedInnerHead ? `\n${cleanedInnerHead}` : ""}\n</head>`;
67
+ return html.replace(fullHead, replacementHead);
68
+ }
69
+ //# sourceMappingURL=meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta.js","sourceRoot":"","sources":["../../src/server/meta.ts"],"names":[],"mappings":"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","sourcesContent":["export interface SocialMeta {\n title: string;\n description?: string;\n image?: string;\n canonicalUrl?: string;\n siteName?: string;\n type?: string;\n robots?: string;\n twitterCard?: \"summary\" | \"summary_large_image\" | \"app\" | \"player\";\n twitterSite?: string;\n twitterCreator?: string;\n}\n\nexport function escapeHtml(value: string): string {\n const htmlEscapes: Record<string, string> = {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\",\n };\n\n return value.replace(/[&<>\"']/g, (character) => htmlEscapes[character]);\n}\n\nfunction stripExistingHeadMeta(headContent: string): string {\n return headContent\n .replace(/<title[^>]*>[\\s\\S]*?<\\/title>/gi, \"\")\n .replace(/<meta\\s+name=[\"']description[\"'][^>]*>/gi, \"\")\n .replace(/<meta\\s+name=[\"']robots[\"'][^>]*>/gi, \"\")\n .replace(/<meta\\s+property=[\"']og:[^\"']+[\"'][^>]*>/gi, \"\")\n .replace(/<meta\\s+name=[\"']twitter:[^\"']+[\"'][^>]*>/gi, \"\")\n .replace(/<link\\s+rel=[\"']canonical[\"'][^>]*>/gi, \"\");\n}\n\nexport function buildSocialMetaTags(meta: SocialMeta): string {\n const type = meta.type ?? \"website\";\n const twitterCard = meta.twitterCard ?? \"summary_large_image\";\n\n const tags = [\n `<title>${escapeHtml(meta.title)}</title>`,\n `<meta property=\"og:title\" content=\"${escapeHtml(meta.title)}\" />`,\n `<meta property=\"og:type\" content=\"${escapeHtml(type)}\" />`,\n `<meta name=\"twitter:card\" content=\"${escapeHtml(twitterCard)}\" />`,\n `<meta name=\"twitter:title\" content=\"${escapeHtml(meta.title)}\" />`,\n ];\n\n if (meta.description) {\n tags.push(`<meta name=\"description\" content=\"${escapeHtml(meta.description)}\" />`);\n tags.push(`<meta property=\"og:description\" content=\"${escapeHtml(meta.description)}\" />`);\n tags.push(`<meta name=\"twitter:description\" content=\"${escapeHtml(meta.description)}\" />`);\n }\n\n if (meta.image) {\n tags.push(`<meta property=\"og:image\" content=\"${escapeHtml(meta.image)}\" />`);\n tags.push(`<meta name=\"twitter:image\" content=\"${escapeHtml(meta.image)}\" />`);\n }\n\n if (meta.canonicalUrl) {\n tags.push(`<meta property=\"og:url\" content=\"${escapeHtml(meta.canonicalUrl)}\" />`);\n tags.push(`<link rel=\"canonical\" href=\"${escapeHtml(meta.canonicalUrl)}\" />`);\n }\n\n if (meta.siteName) {\n tags.push(`<meta property=\"og:site_name\" content=\"${escapeHtml(meta.siteName)}\" />`);\n }\n\n if (meta.robots) {\n tags.push(`<meta name=\"robots\" content=\"${escapeHtml(meta.robots)}\" />`);\n }\n\n if (meta.twitterSite) {\n tags.push(`<meta name=\"twitter:site\" content=\"${escapeHtml(meta.twitterSite)}\" />`);\n }\n\n if (meta.twitterCreator) {\n tags.push(`<meta name=\"twitter:creator\" content=\"${escapeHtml(meta.twitterCreator)}\" />`);\n }\n\n return tags.join(\"\\n\");\n}\n\nexport function injectSocialMetaIntoHtml(html: string, meta: SocialMeta): string {\n const generated = buildSocialMetaTags(meta);\n const headMatch = html.match(/<head[^>]*>([\\s\\S]*?)<\\/head>/i);\n\n if (!headMatch) {\n return `${generated}\\n${html}`;\n }\n\n const fullHead = headMatch[0];\n const innerHead = headMatch[1];\n const cleanedInnerHead = stripExistingHeadMeta(innerHead).trim();\n const replacementHead = `<head>\\n${generated}${cleanedInnerHead ? `\\n${cleanedInnerHead}` : \"\"}\\n</head>`;\n\n return html.replace(fullHead, replacementHead);\n}\n"]}
@@ -3,6 +3,7 @@ import type { HeliumConfig } from "./config.js";
3
3
  import type { HeliumWorkerDef } from "./defineWorker.js";
4
4
  import { HTTPRouter } from "./httpRouter.js";
5
5
  import { RpcRegistry } from "./rpcRegistry.js";
6
+ import { SEOMetadataRouter } from "./seoMetadataRouter.js";
6
7
  interface WorkerEntry {
7
8
  name: string;
8
9
  worker: HeliumWorkerDef;
@@ -11,7 +12,7 @@ interface ProdServerOptions {
11
12
  port?: number;
12
13
  distDir?: string;
13
14
  staticDir?: string;
14
- registerHandlers: (registry: RpcRegistry, httpRouter: HTTPRouter) => void;
15
+ registerHandlers: (registry: RpcRegistry, httpRouter: HTTPRouter, seoRouter: SEOMetadataRouter) => void;
15
16
  config?: HeliumConfig;
16
17
  workers?: WorkerEntry[];
17
18
  }