heliumts 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/rpcClient.d.ts +4 -0
- package/dist/client/rpcClient.d.ts.map +1 -1
- package/dist/client/rpcClient.js +206 -41
- package/dist/client/rpcClient.js.map +1 -1
- package/dist/server/config.d.ts +56 -0
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/config.js +3 -0
- package/dist/server/config.js.map +1 -1
- package/dist/server/devServer.d.ts.map +1 -1
- package/dist/server/devServer.js +48 -3
- package/dist/server/devServer.js.map +1 -1
- package/dist/server/httpRouter.js +29 -5
- package/dist/server/httpRouter.js.map +1 -1
- package/dist/server/prodServer.d.ts.map +1 -1
- package/dist/server/prodServer.js +116 -7
- package/dist/server/prodServer.js.map +1 -1
- package/dist/server/rpcRegistry.d.ts +2 -0
- package/dist/server/rpcRegistry.d.ts.map +1 -1
- package/dist/server/rpcRegistry.js +44 -3
- package/dist/server/rpcRegistry.js.map +1 -1
- package/dist/server/security.d.ts +5 -0
- package/dist/server/security.d.ts.map +1 -1
- package/dist/server/security.js +25 -14
- package/dist/server/security.js.map +1 -1
- package/dist/server/serializer.d.ts +2 -1
- package/dist/server/serializer.d.ts.map +1 -1
- package/dist/server/serializer.js +20 -4
- package/dist/server/serializer.js.map +1 -1
- package/dist/utils/ipExtractor.d.ts.map +1 -1
- package/dist/utils/ipExtractor.js +20 -16
- package/dist/utils/ipExtractor.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
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 * 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 * 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\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 };\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}\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 };\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 * 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}\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 };\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devServer.d.ts","sourceRoot":"","sources":["../../src/server/devServer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,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;AAE7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQ/C,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;AAC9E,KAAK,UAAU,GAAG,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,iBAAiB,CAAC;AAE3F,UAAU,WAAW;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;CAC3B;AAQD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,GAAE,YAAiB,EAAE,OAAO,GAAE,WAAW,EAAO,
|
|
1
|
+
{"version":3,"file":"devServer.d.ts","sourceRoot":"","sources":["../../src/server/devServer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,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;AAE7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQ/C,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;AAC9E,KAAK,UAAU,GAAG,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,iBAAiB,CAAC;AAE3F,UAAU,WAAW;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;CAC3B;AAQD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,GAAE,YAAiB,EAAE,OAAO,GAAE,WAAW,EAAO,QAgU7I"}
|
package/dist/server/devServer.js
CHANGED
|
@@ -41,6 +41,7 @@ export function attachToDevServer(httpServer, loadHandlers, config = {}, workers
|
|
|
41
41
|
httpRouter.setTrustProxyDepth(trustProxyDepth);
|
|
42
42
|
loadHandlers(registry, httpRouter);
|
|
43
43
|
registry.setRateLimiter(rateLimiter);
|
|
44
|
+
registry.setMaxBatchSize(rpcConfig.maxBatchSize);
|
|
44
45
|
currentRegistry = registry;
|
|
45
46
|
currentHttpRouter = httpRouter;
|
|
46
47
|
// Start workers if they changed
|
|
@@ -104,6 +105,7 @@ export function attachToDevServer(httpServer, loadHandlers, config = {}, workers
|
|
|
104
105
|
if (!wss) {
|
|
105
106
|
wss = new WebSocketServer({
|
|
106
107
|
noServer: true,
|
|
108
|
+
maxPayload: rpcConfig.maxWsPayload,
|
|
107
109
|
perMessageDeflate: compressionConfig.enabled
|
|
108
110
|
? {
|
|
109
111
|
zlibDeflateOptions: {
|
|
@@ -176,8 +178,9 @@ export function attachToDevServer(httpServer, loadHandlers, config = {}, workers
|
|
|
176
178
|
// Handle WebSocket upgrade requests
|
|
177
179
|
httpServer.on("upgrade", (req, socket, head) => {
|
|
178
180
|
if (req.url?.startsWith("/rpc")) {
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
+
// Security: read token from Sec-WebSocket-Protocol header instead of query string
|
|
182
|
+
const protocols = req.headers["sec-websocket-protocol"];
|
|
183
|
+
const token = typeof protocols === "string" ? protocols.split(",").map((p) => p.trim()).find((p) => p.includes(".")) : undefined;
|
|
181
184
|
if (!token || !verifyConnectionToken(token)) {
|
|
182
185
|
log("warn", "WebSocket connection rejected - invalid token");
|
|
183
186
|
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
|
@@ -202,6 +205,8 @@ export function attachToDevServer(httpServer, loadHandlers, config = {}, workers
|
|
|
202
205
|
});
|
|
203
206
|
log("info", "WebSocket RPC attached to dev server at /rpc");
|
|
204
207
|
}
|
|
208
|
+
// Security: max body size for HTTP requests
|
|
209
|
+
const maxBodySize = rpcConfig.maxBodySize ?? 1048576;
|
|
205
210
|
// Attach HTTP request handler
|
|
206
211
|
// We need to intercept requests before Vite handles them
|
|
207
212
|
const originalListeners = httpServer.listeners("request").slice();
|
|
@@ -209,6 +214,18 @@ export function attachToDevServer(httpServer, loadHandlers, config = {}, workers
|
|
|
209
214
|
httpServer.on("request", async (req, res) => {
|
|
210
215
|
// Handle token refresh endpoint
|
|
211
216
|
if (req.url === "/__helium__/refresh-token") {
|
|
217
|
+
// Security: only allow POST to prevent CSRF via <img>/<script> tags
|
|
218
|
+
if (req.method !== "POST") {
|
|
219
|
+
res.writeHead(405, { "Content-Type": "application/json" });
|
|
220
|
+
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
// Security: require custom header to prevent cross-origin requests
|
|
224
|
+
if (!req.headers["x-requested-with"]) {
|
|
225
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
226
|
+
res.end(JSON.stringify({ error: "Forbidden" }));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
212
229
|
const { generateConnectionToken } = await import("./security.js");
|
|
213
230
|
const token = generateConnectionToken();
|
|
214
231
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -217,9 +234,37 @@ export function attachToDevServer(httpServer, loadHandlers, config = {}, workers
|
|
|
217
234
|
}
|
|
218
235
|
// Handle HTTP-based RPC endpoint (alternative to WebSocket for mobile networks)
|
|
219
236
|
if (req.url === "/__helium__/rpc" && req.method === "POST") {
|
|
237
|
+
// Security: verify connection token for HTTP RPC
|
|
238
|
+
const authToken = req.headers["x-helium-token"];
|
|
239
|
+
if (!authToken || !verifyConnectionToken(authToken)) {
|
|
240
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
241
|
+
res.end(JSON.stringify({ ok: false, error: "Unauthorized" }));
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
// Security: check Content-Length before reading body
|
|
245
|
+
const contentLength = parseInt(req.headers["content-length"] || "0", 10);
|
|
246
|
+
if (contentLength > maxBodySize) {
|
|
247
|
+
res.writeHead(413, { "Content-Type": "application/json" });
|
|
248
|
+
res.end(JSON.stringify({ ok: false, error: "Request entity too large" }));
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
220
251
|
const chunks = [];
|
|
221
|
-
|
|
252
|
+
let totalSize = 0;
|
|
253
|
+
let aborted = false;
|
|
254
|
+
req.on("data", (chunk) => {
|
|
255
|
+
totalSize += chunk.length;
|
|
256
|
+
if (totalSize > maxBodySize) {
|
|
257
|
+
aborted = true;
|
|
258
|
+
req.destroy();
|
|
259
|
+
res.writeHead(413, { "Content-Type": "application/json" });
|
|
260
|
+
res.end(JSON.stringify({ ok: false, error: "Request entity too large" }));
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
chunks.push(chunk);
|
|
264
|
+
});
|
|
222
265
|
req.on("end", async () => {
|
|
266
|
+
if (aborted)
|
|
267
|
+
return;
|
|
223
268
|
try {
|
|
224
269
|
if (!currentRegistry) {
|
|
225
270
|
res.writeHead(503, { "Content-Type": "application/json" });
|
|
@@ -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 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 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 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 const url = new URL(req.url, \"http://localhost\");\n const token = url.searchParams.get(\"token\");\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 // 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 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 const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", async () => {\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":"AAAA,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAI3D,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGrF,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAClC,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;AACxC,MAAM,mBAAmB,GAAG,SAAS,CAAC,cAAc,CAAC,CAAC;AAUtD,IAAI,eAAe,GAAuB,IAAI,CAAC;AAC/C,IAAI,iBAAiB,GAAsB,IAAI,CAAC;AAChD,IAAI,GAAG,GAA2B,IAAI,CAAC;AACvC,IAAI,WAAW,GAAuB,IAAI,CAAC;AAC3C,IAAI,cAAc,GAAkB,EAAE,CAAC;AAEvC;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAsB,EAAE,YAA4B,EAAE,SAAuB,EAAE,EAAE,UAAyB,EAAE;IAC1I,oDAAoD;IACpD,MAAM,OAAO,GAAG,YAAY,EAAE,CAAC;IAC/B,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAE5B,qBAAqB;IACrB,MAAM,eAAe,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACnD,MAAM,WAAW,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,iBAAiB,GAAG,SAAS,CAAC,WAAW,CAAC;IAChD,kBAAkB,CAAC,WAAW,CAAC,CAAC;IAEhC,qGAAqG;IACrG,WAAW,GAAG,IAAI,WAAW,CAAC,WAAW,CAAC,oBAAoB,EAAE,WAAW,CAAC,iBAAiB,EAAE,WAAW,CAAC,mBAAmB,CAAC,CAAC;IAEhI,MAAM,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAC;IACnC,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;IACpC,UAAU,CAAC,kBAAkB,CAAC,eAAe,CAAC,CAAC;IAC/C,YAAY,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACnC,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IACrC,QAAQ,CAAC,eAAe,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACjD,eAAe,GAAG,QAAQ,CAAC;IAC3B,iBAAiB,GAAG,UAAU,CAAC;IAE/B,gCAAgC;IAChC,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,KAAK,cAAc,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,cAAc,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAExK,IAAI,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvC,qDAAqD;QACrD,cAAc,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;YACvB,oBAAoB;YACpB,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;gBACrC,8CAA8C;gBAC9C,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBAC9B,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;oBACnB,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;oBACnB,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;gBAC/B,CAAC;gBACD,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;oBAC3B,MAAM,aAAa,GAAG,GAAkB,EAAE,CAAC,CAAC;wBACxC,GAAG,EAAE;4BACD,EAAE,EAAE,WAAW;4BACf,OAAO,EAAE,EAAE;4BACX,GAAG,EAAE,SAAS;4BACd,MAAM,EAAE,SAAS;4BACjB,GAAG,EAAE,EAA0B;yBAClC;qBACJ,CAAC,CAAC;oBACH,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;wBAC7C,GAAG,CAAC,OAAO,EAAE,2BAA2B,MAAM,CAAC,IAAI,IAAI,EAAE,GAAG,CAAC,CAAC;oBAClE,CAAC,CAAC,CAAC;gBACP,CAAC;YACL,CAAC;YACD,cAAc,GAAG,OAAO,CAAC;QAC7B,CAAC,CAAC,CAAC;IACP,CAAC;SAAM,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3D,8BAA8B;QAC9B,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;YACrC,8CAA8C;YAC9C,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAC9B,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;gBACnB,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;gBACnB,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;YAC/B,CAAC;YACD,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;gBAC3B,MAAM,aAAa,GAAG,GAAkB,EAAE,CAAC,CAAC;oBACxC,GAAG,EAAE;wBACD,EAAE,EAAE,WAAW;wBACf,OAAO,EAAE,EAAE;wBACX,GAAG,EAAE,SAAS;wBACd,MAAM,EAAE,SAAS;wBACjB,GAAG,EAAE,EAA0B;qBAClC;iBACJ,CAAC,CAAC;gBACH,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;oBAC7C,GAAG,CAAC,OAAO,EAAE,2BAA2B,MAAM,CAAC,IAAI,IAAI,EAAE,GAAG,CAAC,CAAC;gBAClE,CAAC,CAAC,CAAC;YACP,CAAC;QACL,CAAC;QACD,cAAc,GAAG,OAAO,CAAC;IAC7B,CAAC;IAED,kDAAkD;IAClD,IAAI,CAAC,GAAG,EAAE,CAAC;QACP,GAAG,GAAG,IAAI,eAAe,CAAC;YACtB,QAAQ,EAAE,IAAI;YACd,UAAU,EAAE,SAAS,CAAC,YAAY;YAClC,iBAAiB,EAAE,iBAAiB,CAAC,OAAO;gBACxC,CAAC,CAAC;oBACI,kBAAkB,EAAE;wBAChB,SAAS,EAAE,IAAI;wBACf,QAAQ,EAAE,CAAC;wBACX,KAAK,EAAE,CAAC,EAAE,4CAA4C;qBACzD;oBACD,kBAAkB,EAAE;wBAChB,SAAS,EAAE,EAAE,GAAG,IAAI;qBACvB;oBACD,SAAS,EAAE,iBAAiB,CAAC,SAAS;iBACzC;gBACH,CAAC,CAAC,KAAK;SACd,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,MAAiB,EAAE,GAAyB,EAAE,EAAE;YAClE,6CAA6C;YAC7C,MAAM,EAAE,GAAG,eAAe,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;YAEjD,4CAA4C;YAC5C,IAAI,eAAe,EAAE,CAAC;gBAClB,eAAe,CAAC,iBAAiB,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;YACvD,CAAC;YAED,sCAAsC;YACtC,IAAI,WAAW,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC;gBAC1D,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,mCAAmC,CAAC,CAAC;gBACxD,OAAO;YACX,CAAC;YAED,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAsB,EAAE,SAAkB,EAAE,EAAE;gBAChE,mBAAmB;gBACnB,IAAI,WAAW,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;oBACrD,wDAAwD;oBACxD,IAAI,CAAC;wBACD,IAAI,GAAQ,CAAC;wBACb,4BAA4B;wBAC5B,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAU,CAAC,CAAC;wBACpE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;wBAC9D,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;wBAE5B,MAAM,KAAK,GAAG,WAAW,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;wBACrD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;wBACvB,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;wBAE/E,MAAM,WAAW,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,CAAC;4BACjC,EAAE;4BACF,EAAE,EAAE,KAAK;4BACT,KAAK,EAAE;gCACH,iBAAiB,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;gCACtD,cAAc;6BACjB;4BACD,KAAK,EAAE,qBAAqB;yBAC/B,CAAC,CAAC;wBAEH,IAAI,aAAkB,CAAC;wBACvB,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;4BACrB,aAAa,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;wBAC3D,CAAC;6BAAM,CAAC;4BACJ,aAAa,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;wBACxC,CAAC;wBAED,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAW,CAAC,CAAC;oBACxD,CAAC;oBAAC,MAAM,CAAC;wBACL,2DAA2D;wBAC3D,MAAM,CAAC,KAAK,EAAE,CAAC;oBACnB,CAAC;oBACD,OAAO;gBACX,CAAC;gBAED,0DAA0D;gBAC1D,IAAI,eAAe,EAAE,CAAC;oBAClB,eAAe,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAU,CAAC,CAAC,CAAC;gBAChG,CAAC;YACL,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;QAEH,oCAAoC;QACpC,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;YAC3C,IAAI,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC9B,kFAAkF;gBAClF,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;gBACxD,MAAM,KAAK,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBAEjI,IAAI,CAAC,KAAK,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1C,GAAG,CAAC,MAAM,EAAE,+CAA+C,CAAC,CAAC;oBAC7D,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;oBAClD,MAAM,CAAC,OAAO,EAAE,CAAC;oBACjB,OAAO;gBACX,CAAC;gBAED,6CAA6C;gBAC7C,MAAM,EAAE,GAAG,eAAe,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;gBACjD,IAAI,WAAW,IAAI,WAAW,CAAC,mBAAmB,GAAG,CAAC,EAAE,CAAC;oBACrD,MAAM,kBAAkB,GAAG,WAAW,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC;oBAChE,IAAI,kBAAkB,IAAI,WAAW,CAAC,mBAAmB,EAAE,CAAC;wBACxD,GAAG,CAAC,MAAM,EAAE,sCAAsC,EAAE,QAAQ,kBAAkB,cAAc,CAAC,CAAC;wBAC9F,MAAM,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;wBACvD,MAAM,CAAC,OAAO,EAAE,CAAC;wBACjB,OAAO;oBACX,CAAC;gBACL,CAAC;gBAED,GAAI,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE;oBACzC,GAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;gBACrC,CAAC,CAAC,CAAC;YACP,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,MAAM,EAAE,8CAA8C,CAAC,CAAC;IAChE,CAAC;IAED,4CAA4C;IAC5C,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,IAAI,OAAS,CAAC;IAEvD,8BAA8B;IAC9B,yDAAyD;IACzD,MAAM,iBAAiB,GAAG,UAAU,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC;IAClE,UAAU,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAEzC,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,GAAQ,EAAE,GAAQ,EAAE,EAAE;QAClD,gCAAgC;QAChC,IAAI,GAAG,CAAC,GAAG,KAAK,2BAA2B,EAAE,CAAC;YAC1C,oEAAoE;YACpE,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBACxB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC;gBACzD,OAAO;YACX,CAAC;YACD,mEAAmE;YACnE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBACnC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;gBAChD,OAAO;YACX,CAAC;YACD,MAAM,EAAE,uBAAuB,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;YAClE,MAAM,KAAK,GAAG,uBAAuB,EAAE,CAAC;YACxC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;YACnC,OAAO;QACX,CAAC;QAED,gFAAgF;QAChF,IAAI,GAAG,CAAC,GAAG,KAAK,iBAAiB,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YACzD,iDAAiD;YACjD,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAuB,CAAC;YACtE,IAAI,CAAC,SAAS,IAAI,CAAC,qBAAqB,CAAC,SAAS,CAAC,EAAE,CAAC;gBAClD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;gBAC9D,OAAO;YACX,CAAC;YAED,qDAAqD;YACrD,MAAM,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;YACzE,IAAI,aAAa,GAAG,WAAW,EAAE,CAAC;gBAC9B,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAC,CAAC;gBAC1E,OAAO;YACX,CAAC;YAED,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,SAAS,GAAG,CAAC,CAAC;YAClB,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBAC7B,SAAS,IAAI,KAAK,CAAC,MAAM,CAAC;gBAC1B,IAAI,SAAS,GAAG,WAAW,EAAE,CAAC;oBAC1B,OAAO,GAAG,IAAI,CAAC;oBACf,GAAG,CAAC,OAAO,EAAE,CAAC;oBACd,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAC,CAAC;oBAC1E,OAAO;gBACX,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC,CAAC,CAAC;YACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;gBACrB,IAAI,OAAO;oBAAE,OAAO;gBACpB,IAAI,CAAC;oBACD,IAAI,CAAC,eAAe,EAAE,CAAC;wBACnB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;wBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC;wBAClE,OAAO;oBACX,CAAC;oBAED,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBACnC,MAAM,EAAE,GAAG,eAAe,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;oBACjD,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,iBAAiB,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;oBAEtE,MAAM,OAAO,GAAG,aAAa,CAAC,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;oBAClE,IAAI,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,OAAqB,CAAC,CAAC;oBACtD,MAAM,OAAO,GAA2B;wBACpC,cAAc,EAAE,qBAAqB;wBACrC,eAAe,EAAE,UAAU;qBAC9B,CAAC;oBAEF,qBAAqB;oBACrB,MAAM,cAAc,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAW,CAAC;oBAChE,IAAI,cAAc,IAAI,YAAY,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;wBAC/C,IAAI,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;4BAChC,YAAY,GAAG,MAAM,mBAAmB,CAAC,YAAY,CAAC,CAAC;4BACvD,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI,CAAC;wBACvC,CAAC;6BAAM,IAAI,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;4BACzC,YAAY,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,CAAC;4BAC7C,OAAO,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAAC;wBACzC,CAAC;6BAAM,IAAI,cAAc,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;4BAC5C,YAAY,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,CAAC;4BAChD,OAAO,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAC;wBAC5C,CAAC;oBACL,CAAC;oBAED,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;oBAC5B,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;gBAC1B,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACb,GAAG,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAC;oBACvC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC,CAAC;gBAC3E,CAAC;YACL,CAAC,CAAC,CAAC;YACH,OAAO;QACX,CAAC;QAED,0BAA0B;QAC1B,IAAI,iBAAiB,EAAE,CAAC;YACpB,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAChE,IAAI,OAAO,EAAE,CAAC;gBACV,OAAO;YACX,CAAC;QACL,CAAC;QAED,wDAAwD;QACxD,KAAK,MAAM,QAAQ,IAAI,iBAAiB,EAAE,CAAC;YACtC,QAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAChC,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC","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 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 = typeof protocols === \"string\" ? protocols.split(\",\").map((p) => p.trim()).find((p) => p.includes(\".\")) : 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) return;\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"]}
|
|
@@ -119,12 +119,16 @@ export class HTTPRouter {
|
|
|
119
119
|
}
|
|
120
120
|
function pathToRegex(path) {
|
|
121
121
|
const keys = [];
|
|
122
|
+
const multiSegmentToken = "__WILDCARD_MULTI__";
|
|
122
123
|
const pattern = path
|
|
124
|
+
.replace(/\/\*\*/g, `/${multiSegmentToken}`)
|
|
123
125
|
.replace(/\/:([^/]+)/g, (_, key) => {
|
|
124
126
|
keys.push(key);
|
|
125
127
|
return "/([^/]+)";
|
|
126
128
|
})
|
|
127
|
-
|
|
129
|
+
// * matches a single path segment, /** matches across segments.
|
|
130
|
+
.replace(/\*/g, "[^/]*")
|
|
131
|
+
.replace(new RegExp(multiSegmentToken, "g"), ".*")
|
|
128
132
|
.replace(/\//g, "\\/");
|
|
129
133
|
return {
|
|
130
134
|
pattern: new RegExp(`^${pattern}$`),
|
|
@@ -158,7 +162,12 @@ async function createHTTPRequest(req, query, params) {
|
|
|
158
162
|
cookies,
|
|
159
163
|
json: async () => {
|
|
160
164
|
const body = await getBody();
|
|
161
|
-
|
|
165
|
+
try {
|
|
166
|
+
return JSON.parse(body.toString("utf-8"));
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
throw new Error("Invalid JSON in request body");
|
|
170
|
+
}
|
|
162
171
|
},
|
|
163
172
|
text: async () => {
|
|
164
173
|
const body = await getBody();
|
|
@@ -200,10 +209,19 @@ async function createHTTPRequest(req, query, params) {
|
|
|
200
209
|
},
|
|
201
210
|
};
|
|
202
211
|
}
|
|
203
|
-
function readBody(req) {
|
|
212
|
+
function readBody(req, maxBytes = 1048576) {
|
|
204
213
|
return new Promise((resolve, reject) => {
|
|
205
214
|
const chunks = [];
|
|
206
|
-
|
|
215
|
+
let totalSize = 0;
|
|
216
|
+
req.on("data", (chunk) => {
|
|
217
|
+
totalSize += chunk.length;
|
|
218
|
+
if (totalSize > maxBytes) {
|
|
219
|
+
req.destroy();
|
|
220
|
+
reject(new Error("Request entity too large"));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
chunks.push(chunk);
|
|
224
|
+
});
|
|
207
225
|
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
208
226
|
req.on("error", reject);
|
|
209
227
|
});
|
|
@@ -217,7 +235,13 @@ function parseCookies(cookieHeader) {
|
|
|
217
235
|
for (const pair of pairs) {
|
|
218
236
|
const [key, value] = pair.split("=").map((s) => s.trim());
|
|
219
237
|
if (key && value) {
|
|
220
|
-
|
|
238
|
+
try {
|
|
239
|
+
cookies[key] = decodeURIComponent(value);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// Malformed encoding (e.g. %ZZ) — use raw value
|
|
243
|
+
cookies[key] = value;
|
|
244
|
+
}
|
|
221
245
|
}
|
|
222
246
|
}
|
|
223
247
|
return cookies;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpRouter.js","sourceRoot":"","sources":["../../src/server/httpRouter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from \"http\";\nimport { parse as parseUrl } from \"url\";\n\nimport { extractClientIP } from \"../utils/ipExtractor.js\";\nimport { log } from \"../utils/logger.js\";\nimport type { HeliumContext } from \"./context.js\";\nimport type { HeliumHTTPDef, HTTPRequest } from \"./defineHTTPRequest.js\";\nimport type { HeliumMiddleware } from \"./middleware.js\";\n\nexport interface HTTPRoute {\n name: string;\n handler: HeliumHTTPDef;\n}\n\nexport class HTTPRouter {\n private routes: Array<{\n method: string;\n pattern: RegExp;\n keys: string[];\n handler: HeliumHTTPDef;\n }> = [];\n private middleware: HeliumMiddleware | null = null;\n private trustProxyDepth: number = 0;\n\n setTrustProxyDepth(depth: number) {\n this.trustProxyDepth = depth;\n }\n\n registerRoutes(routes: HTTPRoute[]) {\n for (const route of routes) {\n const { method, path } = route.handler;\n const { pattern, keys } = pathToRegex(path);\n this.routes.push({\n method: method.toUpperCase(),\n pattern,\n keys,\n handler: route.handler,\n });\n }\n }\n\n setMiddleware(middleware: HeliumMiddleware) {\n this.middleware = middleware;\n }\n\n async handleRequest(req: IncomingMessage, res: ServerResponse, ctx?: unknown): Promise<boolean> {\n const method = req.method?.toUpperCase() || \"GET\";\n const url = parseUrl(req.url || \"\", true);\n const pathname = url.pathname || \"/\";\n\n for (const route of this.routes) {\n if (route.method !== \"ALL\" && route.method !== method) {\n continue;\n }\n\n const match = pathname.match(route.pattern);\n if (!match) {\n continue;\n }\n\n // Extract path parameters\n const params: Record<string, string> = {};\n for (let i = 0; i < route.keys.length; i++) {\n params[route.keys[i]] = match[i + 1];\n }\n\n try {\n const query: Record<string, string | string[]> = {};\n if (url.query) {\n for (const [key, value] of Object.entries(url.query)) {\n if (value !== undefined) {\n query[key] = value;\n }\n }\n }\n const httpRequest = await createHTTPRequest(req, query, params);\n\n let result: any;\n // Build context with request metadata\n const ip = extractClientIP(req, this.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 ...(ctx as Record<string, unknown>),\n };\n\n // Execute middleware if present\n if (this.middleware) {\n let nextCalled = false;\n await this.middleware.handler(\n {\n ctx: httpCtx,\n type: \"http\",\n httpMethod: method,\n httpPath: pathname,\n },\n async () => {\n nextCalled = true;\n result = await route.handler.handler(httpRequest, httpCtx);\n }\n );\n\n // If next() was not called, the middleware blocked the request\n if (!nextCalled) {\n res.writeHead(403, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Request blocked by middleware\" }));\n return true;\n }\n } else {\n // No middleware, execute handler directly\n result = await route.handler.handler(httpRequest, httpCtx);\n }\n\n if (isWebResponse(result)) {\n res.statusCode = result.status;\n result.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n\n if (result.body) {\n const arrayBuf = await result.arrayBuffer();\n res.end(Buffer.from(arrayBuf));\n } else {\n res.end();\n }\n return true;\n }\n\n // Send response\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n return true;\n } catch (error) {\n log(\"error\", \"Error handling request:\", error);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal server error\" }));\n return true;\n }\n }\n\n return false; // No route matched\n }\n}\n\nfunction pathToRegex(path: string): { pattern: RegExp; keys: string[] } {\n const keys: string[] = [];\n const pattern = path\n .replace(/\\/:([^/]+)/g, (_, key) => {\n keys.push(key);\n return \"/([^/]+)\";\n })\n .replace(/\\*/g, \".*\")\n .replace(/\\//g, \"\\\\/\");\n\n return {\n pattern: new RegExp(`^${pattern}$`),\n keys,\n };\n}\n\nasync function createHTTPRequest(req: IncomingMessage, query: Record<string, string | string[]>, params: Record<string, string>): Promise<HTTPRequest> {\n const headers: Record<string, string | string[] | undefined> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n headers[key.toLowerCase()] = value;\n }\n\n const cookies = parseCookies(req.headers.cookie || \"\");\n\n // Normalize query to always be string\n const normalizedQuery: Record<string, string> = {};\n for (const [key, value] of Object.entries(query)) {\n normalizedQuery[key] = Array.isArray(value) ? value[0] : value;\n }\n\n let bodyBuffer: Buffer | null = null;\n const getBody = async (): Promise<Buffer> => {\n if (bodyBuffer === null) {\n bodyBuffer = await readBody(req);\n }\n return bodyBuffer;\n };\n\n return {\n method: req.method || \"GET\",\n path: req.url || \"/\",\n headers,\n query: normalizedQuery,\n params,\n cookies,\n json: async () => {\n const body = await getBody();\n return JSON.parse(body.toString(\"utf-8\"));\n },\n text: async () => {\n const body = await getBody();\n return body.toString(\"utf-8\");\n },\n formData: async () => {\n throw new Error(\"FormData not yet implemented\");\n },\n /**\n * Convert the normalized HTTPRequest into a standard Web `Request`.\n * This mirrors the shape used in defineHTTPRequest's interface and\n * is useful for passing the request into code that expects the Web\n * Fetch Request API (for example third-party handlers or libraries).\n */\n toWebRequest: async () => {\n const protocol = (req.headers[\"x-forwarded-proto\"] as string) || \"http\";\n const host = (req.headers[\"host\"] as string) || \"localhost\";\n const url = `${protocol}://${host}${req.url || \"/\"}`;\n\n const webHeaders = new Headers();\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) {\n continue;\n }\n if (Array.isArray(value)) {\n for (const v of value) {\n webHeaders.append(key, v);\n }\n } else {\n webHeaders.set(key, value);\n }\n }\n\n const body = req.method !== \"GET\" && req.method !== \"HEAD\" ? await getBody() : undefined;\n\n return new Request(url, {\n method: req.method,\n headers: webHeaders,\n body: body as any,\n });\n },\n };\n}\n\nfunction readBody(req: IncomingMessage): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks)));\n req.on(\"error\", reject);\n });\n}\n\nfunction parseCookies(cookieHeader: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n if (!cookieHeader) {\n return cookies;\n }\n\n const pairs = cookieHeader.split(\";\");\n for (const pair of pairs) {\n const [key, value] = pair.split(\"=\").map((s) => s.trim());\n if (key && value) {\n cookies[key] = decodeURIComponent(value);\n }\n }\n return cookies;\n}\n\n/**\n * Detect a Web `Response` object using duck-typing instead of `instanceof`.\n *\n * In Vite's SSR environment the handler code runs inside a separate module\n * context (`ssrLoadModule`), so the `Response` constructor available there\n * may be a *different reference* than the global `Response` that\n * `httpRouter.ts` sees. The classic `instanceof Response` check therefore\n * fails, causing the framework to fall through to `JSON.stringify(result)`\n * which serialises a Response into a tiny broken payload (~126 bytes).\n *\n * By checking for the characteristic properties (`status`, `headers` as a\n * `Headers`-like object, and `arrayBuffer` method) we reliably detect\n * Response objects regardless of which realm they were created in.\n */\nfunction isWebResponse(value: unknown): value is Response {\n if (value instanceof Response) {\n return true;\n }\n\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n const candidate = value as Record<string, unknown>;\n return (\n typeof candidate.status === \"number\" &&\n typeof candidate.arrayBuffer === \"function\" &&\n typeof candidate.headers === \"object\" &&\n candidate.headers !== null &&\n typeof (candidate.headers as Record<string, unknown>).forEach === \"function\"\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"httpRouter.js","sourceRoot":"","sources":["../../src/server/httpRouter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from \"http\";\nimport { parse as parseUrl } from \"url\";\n\nimport { extractClientIP } from \"../utils/ipExtractor.js\";\nimport { log } from \"../utils/logger.js\";\nimport type { HeliumContext } from \"./context.js\";\nimport type { HeliumHTTPDef, HTTPRequest } from \"./defineHTTPRequest.js\";\nimport type { HeliumMiddleware } from \"./middleware.js\";\n\nexport interface HTTPRoute {\n name: string;\n handler: HeliumHTTPDef;\n}\n\nexport class HTTPRouter {\n private routes: Array<{\n method: string;\n pattern: RegExp;\n keys: string[];\n handler: HeliumHTTPDef;\n }> = [];\n private middleware: HeliumMiddleware | null = null;\n private trustProxyDepth: number = 0;\n\n setTrustProxyDepth(depth: number) {\n this.trustProxyDepth = depth;\n }\n\n registerRoutes(routes: HTTPRoute[]) {\n for (const route of routes) {\n const { method, path } = route.handler;\n const { pattern, keys } = pathToRegex(path);\n this.routes.push({\n method: method.toUpperCase(),\n pattern,\n keys,\n handler: route.handler,\n });\n }\n }\n\n setMiddleware(middleware: HeliumMiddleware) {\n this.middleware = middleware;\n }\n\n async handleRequest(req: IncomingMessage, res: ServerResponse, ctx?: unknown): Promise<boolean> {\n const method = req.method?.toUpperCase() || \"GET\";\n const url = parseUrl(req.url || \"\", true);\n const pathname = url.pathname || \"/\";\n\n for (const route of this.routes) {\n if (route.method !== \"ALL\" && route.method !== method) {\n continue;\n }\n\n const match = pathname.match(route.pattern);\n if (!match) {\n continue;\n }\n\n // Extract path parameters\n const params: Record<string, string> = {};\n for (let i = 0; i < route.keys.length; i++) {\n params[route.keys[i]] = match[i + 1];\n }\n\n try {\n const query: Record<string, string | string[]> = {};\n if (url.query) {\n for (const [key, value] of Object.entries(url.query)) {\n if (value !== undefined) {\n query[key] = value;\n }\n }\n }\n const httpRequest = await createHTTPRequest(req, query, params);\n\n let result: any;\n // Build context with request metadata\n const ip = extractClientIP(req, this.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 ...(ctx as Record<string, unknown>),\n };\n\n // Execute middleware if present\n if (this.middleware) {\n let nextCalled = false;\n await this.middleware.handler(\n {\n ctx: httpCtx,\n type: \"http\",\n httpMethod: method,\n httpPath: pathname,\n },\n async () => {\n nextCalled = true;\n result = await route.handler.handler(httpRequest, httpCtx);\n }\n );\n\n // If next() was not called, the middleware blocked the request\n if (!nextCalled) {\n res.writeHead(403, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Request blocked by middleware\" }));\n return true;\n }\n } else {\n // No middleware, execute handler directly\n result = await route.handler.handler(httpRequest, httpCtx);\n }\n\n if (isWebResponse(result)) {\n res.statusCode = result.status;\n result.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n\n if (result.body) {\n const arrayBuf = await result.arrayBuffer();\n res.end(Buffer.from(arrayBuf));\n } else {\n res.end();\n }\n return true;\n }\n\n // Send response\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n return true;\n } catch (error) {\n log(\"error\", \"Error handling request:\", error);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal server error\" }));\n return true;\n }\n }\n\n return false; // No route matched\n }\n}\n\nfunction pathToRegex(path: string): { pattern: RegExp; keys: string[] } {\n const keys: string[] = [];\n const multiSegmentToken = \"__WILDCARD_MULTI__\";\n const pattern = path\n .replace(/\\/\\*\\*/g, `/${multiSegmentToken}`)\n .replace(/\\/:([^/]+)/g, (_, key) => {\n keys.push(key);\n return \"/([^/]+)\";\n })\n // * matches a single path segment, /** matches across segments.\n .replace(/\\*/g, \"[^/]*\")\n .replace(new RegExp(multiSegmentToken, \"g\"), \".*\")\n .replace(/\\//g, \"\\\\/\");\n\n return {\n pattern: new RegExp(`^${pattern}$`),\n keys,\n };\n}\n\nasync function createHTTPRequest(req: IncomingMessage, query: Record<string, string | string[]>, params: Record<string, string>): Promise<HTTPRequest> {\n const headers: Record<string, string | string[] | undefined> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n headers[key.toLowerCase()] = value;\n }\n\n const cookies = parseCookies(req.headers.cookie || \"\");\n\n // Normalize query to always be string\n const normalizedQuery: Record<string, string> = {};\n for (const [key, value] of Object.entries(query)) {\n normalizedQuery[key] = Array.isArray(value) ? value[0] : value;\n }\n\n let bodyBuffer: Buffer | null = null;\n const getBody = async (): Promise<Buffer> => {\n if (bodyBuffer === null) {\n bodyBuffer = await readBody(req);\n }\n return bodyBuffer;\n };\n\n return {\n method: req.method || \"GET\",\n path: req.url || \"/\",\n headers,\n query: normalizedQuery,\n params,\n cookies,\n json: async () => {\n const body = await getBody();\n try {\n return JSON.parse(body.toString(\"utf-8\"));\n } catch {\n throw new Error(\"Invalid JSON in request body\");\n }\n },\n text: async () => {\n const body = await getBody();\n return body.toString(\"utf-8\");\n },\n formData: async () => {\n throw new Error(\"FormData not yet implemented\");\n },\n /**\n * Convert the normalized HTTPRequest into a standard Web `Request`.\n * This mirrors the shape used in defineHTTPRequest's interface and\n * is useful for passing the request into code that expects the Web\n * Fetch Request API (for example third-party handlers or libraries).\n */\n toWebRequest: async () => {\n const protocol = (req.headers[\"x-forwarded-proto\"] as string) || \"http\";\n const host = (req.headers[\"host\"] as string) || \"localhost\";\n const url = `${protocol}://${host}${req.url || \"/\"}`;\n\n const webHeaders = new Headers();\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) {\n continue;\n }\n if (Array.isArray(value)) {\n for (const v of value) {\n webHeaders.append(key, v);\n }\n } else {\n webHeaders.set(key, value);\n }\n }\n\n const body = req.method !== \"GET\" && req.method !== \"HEAD\" ? await getBody() : undefined;\n\n return new Request(url, {\n method: req.method,\n headers: webHeaders,\n body: body as any,\n });\n },\n };\n}\n\nfunction readBody(req: IncomingMessage, maxBytes: number = 1_048_576): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalSize = 0;\n req.on(\"data\", (chunk) => {\n totalSize += chunk.length;\n if (totalSize > maxBytes) {\n req.destroy();\n reject(new Error(\"Request entity too large\"));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => resolve(Buffer.concat(chunks)));\n req.on(\"error\", reject);\n });\n}\n\nfunction parseCookies(cookieHeader: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n if (!cookieHeader) {\n return cookies;\n }\n\n const pairs = cookieHeader.split(\";\");\n for (const pair of pairs) {\n const [key, value] = pair.split(\"=\").map((s) => s.trim());\n if (key && value) {\n try {\n cookies[key] = decodeURIComponent(value);\n } catch {\n // Malformed encoding (e.g. %ZZ) — use raw value\n cookies[key] = value;\n }\n }\n }\n return cookies;\n}\n\n/**\n * Detect a Web `Response` object using duck-typing instead of `instanceof`.\n *\n * In Vite's SSR environment the handler code runs inside a separate module\n * context (`ssrLoadModule`), so the `Response` constructor available there\n * may be a *different reference* than the global `Response` that\n * `httpRouter.ts` sees. The classic `instanceof Response` check therefore\n * fails, causing the framework to fall through to `JSON.stringify(result)`\n * which serialises a Response into a tiny broken payload (~126 bytes).\n *\n * By checking for the characteristic properties (`status`, `headers` as a\n * `Headers`-like object, and `arrayBuffer` method) we reliably detect\n * Response objects regardless of which realm they were created in.\n */\nfunction isWebResponse(value: unknown): value is Response {\n if (value instanceof Response) {\n return true;\n }\n\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n const candidate = value as Record<string, unknown>;\n return (\n typeof candidate.status === \"number\" &&\n typeof candidate.arrayBuffer === \"function\" &&\n typeof candidate.headers === \"object\" &&\n candidate.headers !== null &&\n typeof (candidate.headers as Record<string, unknown>).forEach === \"function\"\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prodServer.d.ts","sourceRoot":"","sources":["../../src/server/prodServer.ts"],"names":[],"mappings":"AAEA,OAAO,IAAI,MAAM,MAAM,CAAC;AASxB,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;AAE7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQ/C,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,KAAK,IAAI,CAAC;IAC1E,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;CAC3B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"prodServer.d.ts","sourceRoot":"","sources":["../../src/server/prodServer.ts"],"names":[],"mappings":"AAEA,OAAO,IAAI,MAAM,MAAM,CAAC;AASxB,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;AAE7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQ/C,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,KAAK,IAAI,CAAC;IAC1E,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;CAC3B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,wEAgZzD"}
|