heliumts 0.8.6 → 0.8.7

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.
File without changes
@@ -1,4 +1,9 @@
1
1
  import type { HeliumContext } from "./context.js";
2
+ export type WorkerCleanup = (() => Promise<void> | void) | void;
3
+ export interface WorkerLifecycle {
4
+ signal: AbortSignal;
5
+ onCleanup: (cleanup: WorkerCleanup) => void;
6
+ }
2
7
  export interface WorkerOptions {
3
8
  /**
4
9
  * The name of the worker, used for logging and identification.
@@ -27,7 +32,7 @@ export interface WorkerOptions {
27
32
  */
28
33
  autoStart?: boolean;
29
34
  }
30
- export type WorkerHandler = (ctx: HeliumContext) => Promise<void> | void;
35
+ export type WorkerHandler = (ctx: HeliumContext, lifecycle: WorkerLifecycle) => Promise<WorkerCleanup> | WorkerCleanup;
31
36
  export type HeliumWorkerDef = {
32
37
  __kind: "worker";
33
38
  __id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"defineWorker.d.ts","sourceRoot":"","sources":["../../src/server/defineWorker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,aAAa;IAC1B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEzE,MAAM,MAAM,eAAe,GAAG;IAC1B,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC;IACzD,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,KAAK,CAAC;IAClB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,GAAE,aAAkB,GAAG,eAAe,CAsBjG;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAyFtH;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUhD;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAQpD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,cAAc,EAAE,CAElD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEtE"}
1
+ {"version":3,"file":"defineWorker.d.ts","sourceRoot":"","sources":["../../src/server/defineWorker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;AAEhE,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;CAC/C;AAED,MAAM,WAAW,aAAa;IAC1B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,KAAK,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAAC;AAEvH,MAAM,MAAM,eAAe,GAAG;IAC1B,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC;IACzD,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,KAAK,CAAC;IAClB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAoED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,GAAE,aAAkB,GAAG,eAAe,CAsBjG;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CA8GtH;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAchD;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CASpD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,cAAc,EAAE,CAElD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEtE"}
@@ -1,6 +1,41 @@
1
1
  import { log } from "../utils/logger.js";
2
2
  // Map to track running worker instances
3
3
  const runningWorkers = new Map();
4
+ function isCleanupHandler(cleanup) {
5
+ return typeof cleanup === "function";
6
+ }
7
+ function registerCleanup(workerState, cleanup) {
8
+ if (!isCleanupHandler(cleanup)) {
9
+ return;
10
+ }
11
+ workerState.cleanupHandlers.push(cleanup);
12
+ }
13
+ async function runWorkerCleanup(name, workerState) {
14
+ if (workerState.cleanupPromise) {
15
+ await workerState.cleanupPromise;
16
+ return;
17
+ }
18
+ workerState.cleanupPromise = (async () => {
19
+ const cleanupHandlers = workerState.cleanupHandlers.splice(0).reverse();
20
+ for (const cleanup of cleanupHandlers) {
21
+ try {
22
+ await cleanup();
23
+ }
24
+ catch (error) {
25
+ log("error", `Worker '${name}' cleanup failed:`, error);
26
+ }
27
+ }
28
+ })();
29
+ await workerState.cleanupPromise;
30
+ }
31
+ function waitForAbort(signal) {
32
+ if (signal.aborted) {
33
+ return Promise.resolve();
34
+ }
35
+ return new Promise((resolve) => {
36
+ signal.addEventListener("abort", () => resolve(), { once: true });
37
+ });
38
+ }
4
39
  /**
5
40
  * Create a Helium background worker definition.
6
41
  *
@@ -79,6 +114,13 @@ export async function startWorker(worker, createContext) {
79
114
  }
80
115
  const abortController = new AbortController();
81
116
  let restartCount = 0;
117
+ const workerState = {
118
+ abortController,
119
+ promise: Promise.resolve(),
120
+ instance: undefined,
121
+ cleanupHandlers: [],
122
+ cleanupPromise: null,
123
+ };
82
124
  const instance = {
83
125
  id: name,
84
126
  name,
@@ -88,10 +130,12 @@ export async function startWorker(worker, createContext) {
88
130
  stop: async () => {
89
131
  abortController.abort();
90
132
  instance.status = "stopped";
133
+ await runWorkerCleanup(name, workerState);
91
134
  runningWorkers.delete(name);
92
135
  log("info", `Worker '${name}' stopped`);
93
136
  },
94
137
  };
138
+ workerState.instance = instance;
95
139
  const runWorker = async () => {
96
140
  while (!abortController.signal.aborted) {
97
141
  try {
@@ -99,9 +143,20 @@ export async function startWorker(worker, createContext) {
99
143
  instance.startedAt = new Date();
100
144
  log("info", `Starting worker '${name}'`);
101
145
  const ctx = createContext();
102
- await handler(ctx);
146
+ const lifecycle = {
147
+ signal: abortController.signal,
148
+ onCleanup: (cleanup) => {
149
+ registerCleanup(workerState, cleanup);
150
+ },
151
+ };
152
+ const cleanup = await handler(ctx, lifecycle);
153
+ registerCleanup(workerState, cleanup);
154
+ if (isCleanupHandler(cleanup) && !abortController.signal.aborted) {
155
+ await waitForAbort(abortController.signal);
156
+ }
103
157
  // If handler completes normally, exit the loop
104
158
  if (!abortController.signal.aborted) {
159
+ await runWorkerCleanup(name, workerState);
105
160
  log("info", `Worker '${name}' completed successfully`);
106
161
  instance.status = "stopped";
107
162
  runningWorkers.delete(name);
@@ -113,6 +168,8 @@ export async function startWorker(worker, createContext) {
113
168
  // Worker was intentionally stopped
114
169
  break;
115
170
  }
171
+ await runWorkerCleanup(name, workerState);
172
+ workerState.cleanupPromise = null;
116
173
  instance.lastError = error instanceof Error ? error : new Error(String(error));
117
174
  instance.status = "crashed";
118
175
  restartCount++;
@@ -137,11 +194,8 @@ export async function startWorker(worker, createContext) {
137
194
  }
138
195
  };
139
196
  const promise = runWorker();
140
- runningWorkers.set(name, {
141
- abortController,
142
- promise,
143
- instance,
144
- });
197
+ workerState.promise = promise;
198
+ runningWorkers.set(name, workerState);
145
199
  return instance;
146
200
  }
147
201
  /**
@@ -152,8 +206,12 @@ export function stopWorker(name) {
152
206
  if (worker) {
153
207
  worker.abortController.abort();
154
208
  worker.instance.status = "stopped";
155
- runningWorkers.delete(name);
156
- log("info", `Worker '${name}' stopped`);
209
+ void runWorkerCleanup(name, worker)
210
+ .catch(() => { })
211
+ .finally(() => {
212
+ runningWorkers.delete(name);
213
+ log("info", `Worker '${name}' stopped`);
214
+ });
157
215
  return true;
158
216
  }
159
217
  return false;
@@ -167,6 +225,7 @@ export async function stopAllWorkers() {
167
225
  worker.abortController.abort();
168
226
  worker.instance.status = "stopped";
169
227
  }
228
+ await Promise.all(workers.map(async (worker) => runWorkerCleanup(worker.instance.name, worker)));
170
229
  runningWorkers.clear();
171
230
  log("info", `Stopped ${workers.length} worker(s)`);
172
231
  }
@@ -1 +1 @@
1
- {"version":3,"file":"defineWorker.js","sourceRoot":"","sources":["../../src/server/defineWorker.ts"],"names":[],"mappings":"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","sourcesContent":["import { log } from \"../utils/logger.js\";\nimport type { HeliumContext } from \"./context.js\";\n\nexport interface WorkerOptions {\n /**\n * The name of the worker, used for logging and identification.\n * If not provided, the handler function name will be used.\n */\n name?: string;\n\n /**\n * Whether the worker should automatically restart if it crashes.\n * Default: true\n */\n autoRestart?: boolean;\n\n /**\n * Delay in milliseconds before restarting the worker after a crash.\n * Default: 5000 (5 seconds)\n */\n restartDelayMs?: number;\n\n /**\n * Maximum number of restart attempts before giving up.\n * Set to 0 for unlimited restarts.\n * Default: 0 (unlimited)\n */\n maxRestarts?: number;\n\n /**\n * Whether to start the worker automatically on server startup.\n * Default: true\n */\n autoStart?: boolean;\n}\n\nexport type WorkerHandler = (ctx: HeliumContext) => Promise<void> | void;\n\nexport type HeliumWorkerDef = {\n __kind: \"worker\";\n __id: string;\n name: string;\n handler: WorkerHandler;\n options: Required<WorkerOptions>;\n};\n\nexport interface WorkerInstance {\n id: string;\n name: string;\n status: \"running\" | \"stopped\" | \"crashed\" | \"restarting\";\n startedAt: Date | null;\n restartCount: number;\n lastError?: Error;\n stop: () => Promise<void>;\n}\n\n// Map to track running worker instances\nconst runningWorkers = new Map<\n string,\n {\n abortController: AbortController;\n promise: Promise<void>;\n instance: WorkerInstance;\n }\n>();\n\n/**\n * Create a Helium background worker definition.\n *\n * Workers are long-running background processes that start when the server\n * starts and continue running until the server shuts down. They are ideal for:\n * - Queue consumers (processing jobs from Redis, RabbitMQ, SQS, etc.)\n * - Background task processors\n * - Scheduled jobs and cron-like tasks\n * - Real-time data synchronization\n * - WebSocket connection managers\n * - Cache warmers and data pre-loaders\n *\n * @example\n * ```typescript\n * // Basic worker\n * export const queueConsumer = defineWorker(async (ctx) => {\n * while (true) {\n * const job = await queue.pop();\n * if (job) {\n * await processJob(job);\n * }\n * await sleep(1000);\n * }\n * }, { name: 'queueConsumer' });\n *\n * // Worker with options\n * export const dataSync = defineWorker(async (ctx) => {\n * // Sync data every 30 seconds\n * while (true) {\n * await syncData();\n * await sleep(30000);\n * }\n * }, {\n * name: 'dataSync',\n * autoRestart: true,\n * restartDelayMs: 10000,\n * maxRestarts: 5\n * });\n * ```\n *\n * @param handler - The worker function that runs in the background\n * @param options - Configuration options for the worker\n * @returns A HeliumWorkerDef that will be started by the server runtime\n */\nexport function defineWorker(handler: WorkerHandler, options: WorkerOptions = {}): HeliumWorkerDef {\n if (!handler) {\n throw new Error(\"defineWorker requires a handler\");\n }\n\n const name = options.name || handler.name || \"anonymous\";\n\n const resolvedOptions: Required<WorkerOptions> = {\n name,\n autoRestart: options.autoRestart ?? true,\n restartDelayMs: options.restartDelayMs ?? 5000,\n maxRestarts: options.maxRestarts ?? 0,\n autoStart: options.autoStart ?? true,\n };\n\n return {\n __kind: \"worker\",\n __id: name,\n name,\n handler,\n options: resolvedOptions,\n };\n}\n\n/**\n * Start a worker and manage its lifecycle.\n * @internal\n */\nexport async function startWorker(worker: HeliumWorkerDef, createContext: () => HeliumContext): Promise<WorkerInstance> {\n const { name, handler, options } = worker;\n\n // Check if worker is already running\n if (runningWorkers.has(name)) {\n const existing = runningWorkers.get(name)!;\n log(\"warn\", `Worker '${name}' is already running`);\n return existing.instance;\n }\n\n const abortController = new AbortController();\n let restartCount = 0;\n\n const instance: WorkerInstance = {\n id: name,\n name,\n status: \"running\",\n startedAt: new Date(),\n restartCount: 0,\n stop: async () => {\n abortController.abort();\n instance.status = \"stopped\";\n runningWorkers.delete(name);\n log(\"info\", `Worker '${name}' stopped`);\n },\n };\n\n const runWorker = async (): Promise<void> => {\n while (!abortController.signal.aborted) {\n try {\n instance.status = \"running\";\n instance.startedAt = new Date();\n log(\"info\", `Starting worker '${name}'`);\n\n const ctx = createContext();\n await handler(ctx);\n\n // If handler completes normally, exit the loop\n if (!abortController.signal.aborted) {\n log(\"info\", `Worker '${name}' completed successfully`);\n instance.status = \"stopped\";\n runningWorkers.delete(name);\n }\n break;\n } catch (error) {\n if (abortController.signal.aborted) {\n // Worker was intentionally stopped\n break;\n }\n\n instance.lastError = error instanceof Error ? error : new Error(String(error));\n instance.status = \"crashed\";\n restartCount++;\n instance.restartCount = restartCount;\n\n log(\"error\", `Worker '${name}' crashed:`, error);\n\n // Check if we should restart\n if (options.autoRestart) {\n if (options.maxRestarts > 0 && restartCount >= options.maxRestarts) {\n log(\"error\", `Worker '${name}' exceeded max restarts (${options.maxRestarts}), giving up`);\n runningWorkers.delete(name);\n break;\n }\n\n instance.status = \"restarting\";\n log(\n \"info\",\n `Worker '${name}' will restart in ${options.restartDelayMs}ms (attempt ${restartCount}${options.maxRestarts > 0 ? `/${options.maxRestarts}` : \"\"})`\n );\n\n await new Promise((resolve) => setTimeout(resolve, options.restartDelayMs));\n } else {\n runningWorkers.delete(name);\n break;\n }\n }\n }\n };\n\n const promise = runWorker();\n\n runningWorkers.set(name, {\n abortController,\n promise,\n instance,\n });\n\n return instance;\n}\n\n/**\n * Stop a running worker by name.\n */\nexport function stopWorker(name: string): boolean {\n const worker = runningWorkers.get(name);\n if (worker) {\n worker.abortController.abort();\n worker.instance.status = \"stopped\";\n runningWorkers.delete(name);\n log(\"info\", `Worker '${name}' stopped`);\n return true;\n }\n return false;\n}\n\n/**\n * Stop all running workers.\n */\nexport async function stopAllWorkers(): Promise<void> {\n const workers = Array.from(runningWorkers.values());\n for (const worker of workers) {\n worker.abortController.abort();\n worker.instance.status = \"stopped\";\n }\n runningWorkers.clear();\n log(\"info\", `Stopped ${workers.length} worker(s)`);\n}\n\n/**\n * Get the status of all workers.\n */\nexport function getWorkerStatus(): WorkerInstance[] {\n return Array.from(runningWorkers.values()).map((w) => w.instance);\n}\n\n/**\n * Get a specific worker's status.\n */\nexport function getWorkerById(name: string): WorkerInstance | undefined {\n return runningWorkers.get(name)?.instance;\n}\n"]}
1
+ {"version":3,"file":"defineWorker.js","sourceRoot":"","sources":["../../src/server/defineWorker.ts"],"names":[],"mappings":"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","sourcesContent":["import { log } from \"../utils/logger.js\";\nimport type { HeliumContext } from \"./context.js\";\n\nexport type WorkerCleanup = (() => Promise<void> | void) | void;\n\nexport interface WorkerLifecycle {\n signal: AbortSignal;\n onCleanup: (cleanup: WorkerCleanup) => void;\n}\n\nexport interface WorkerOptions {\n /**\n * The name of the worker, used for logging and identification.\n * If not provided, the handler function name will be used.\n */\n name?: string;\n\n /**\n * Whether the worker should automatically restart if it crashes.\n * Default: true\n */\n autoRestart?: boolean;\n\n /**\n * Delay in milliseconds before restarting the worker after a crash.\n * Default: 5000 (5 seconds)\n */\n restartDelayMs?: number;\n\n /**\n * Maximum number of restart attempts before giving up.\n * Set to 0 for unlimited restarts.\n * Default: 0 (unlimited)\n */\n maxRestarts?: number;\n\n /**\n * Whether to start the worker automatically on server startup.\n * Default: true\n */\n autoStart?: boolean;\n}\n\nexport type WorkerHandler = (ctx: HeliumContext, lifecycle: WorkerLifecycle) => Promise<WorkerCleanup> | WorkerCleanup;\n\nexport type HeliumWorkerDef = {\n __kind: \"worker\";\n __id: string;\n name: string;\n handler: WorkerHandler;\n options: Required<WorkerOptions>;\n};\n\nexport interface WorkerInstance {\n id: string;\n name: string;\n status: \"running\" | \"stopped\" | \"crashed\" | \"restarting\";\n startedAt: Date | null;\n restartCount: number;\n lastError?: Error;\n stop: () => Promise<void>;\n}\n\n// Map to track running worker instances\nconst runningWorkers = new Map<\n string,\n {\n abortController: AbortController;\n promise: Promise<void>;\n instance: WorkerInstance;\n cleanupHandlers: Array<() => Promise<void> | void>;\n cleanupPromise: Promise<void> | null;\n }\n>();\n\nfunction isCleanupHandler(cleanup: WorkerCleanup): cleanup is () => Promise<void> | void {\n return typeof cleanup === \"function\";\n}\n\nfunction registerCleanup(\n workerState: {\n cleanupHandlers: Array<() => Promise<void> | void>;\n },\n cleanup: WorkerCleanup\n) {\n if (!isCleanupHandler(cleanup)) {\n return;\n }\n\n workerState.cleanupHandlers.push(cleanup);\n}\n\nasync function runWorkerCleanup(\n name: string,\n workerState: {\n cleanupHandlers: Array<() => Promise<void> | void>;\n cleanupPromise: Promise<void> | null;\n }\n): Promise<void> {\n if (workerState.cleanupPromise) {\n await workerState.cleanupPromise;\n return;\n }\n\n workerState.cleanupPromise = (async () => {\n const cleanupHandlers = workerState.cleanupHandlers.splice(0).reverse();\n\n for (const cleanup of cleanupHandlers) {\n try {\n await cleanup();\n } catch (error) {\n log(\"error\", `Worker '${name}' cleanup failed:`, error);\n }\n }\n })();\n\n await workerState.cleanupPromise;\n}\n\nfunction waitForAbort(signal: AbortSignal): Promise<void> {\n if (signal.aborted) {\n return Promise.resolve();\n }\n\n return new Promise((resolve) => {\n signal.addEventListener(\"abort\", () => resolve(), { once: true });\n });\n}\n\n/**\n * Create a Helium background worker definition.\n *\n * Workers are long-running background processes that start when the server\n * starts and continue running until the server shuts down. They are ideal for:\n * - Queue consumers (processing jobs from Redis, RabbitMQ, SQS, etc.)\n * - Background task processors\n * - Scheduled jobs and cron-like tasks\n * - Real-time data synchronization\n * - WebSocket connection managers\n * - Cache warmers and data pre-loaders\n *\n * @example\n * ```typescript\n * // Basic worker\n * export const queueConsumer = defineWorker(async (ctx) => {\n * while (true) {\n * const job = await queue.pop();\n * if (job) {\n * await processJob(job);\n * }\n * await sleep(1000);\n * }\n * }, { name: 'queueConsumer' });\n *\n * // Worker with options\n * export const dataSync = defineWorker(async (ctx) => {\n * // Sync data every 30 seconds\n * while (true) {\n * await syncData();\n * await sleep(30000);\n * }\n * }, {\n * name: 'dataSync',\n * autoRestart: true,\n * restartDelayMs: 10000,\n * maxRestarts: 5\n * });\n * ```\n *\n * @param handler - The worker function that runs in the background\n * @param options - Configuration options for the worker\n * @returns A HeliumWorkerDef that will be started by the server runtime\n */\nexport function defineWorker(handler: WorkerHandler, options: WorkerOptions = {}): HeliumWorkerDef {\n if (!handler) {\n throw new Error(\"defineWorker requires a handler\");\n }\n\n const name = options.name || handler.name || \"anonymous\";\n\n const resolvedOptions: Required<WorkerOptions> = {\n name,\n autoRestart: options.autoRestart ?? true,\n restartDelayMs: options.restartDelayMs ?? 5000,\n maxRestarts: options.maxRestarts ?? 0,\n autoStart: options.autoStart ?? true,\n };\n\n return {\n __kind: \"worker\",\n __id: name,\n name,\n handler,\n options: resolvedOptions,\n };\n}\n\n/**\n * Start a worker and manage its lifecycle.\n * @internal\n */\nexport async function startWorker(worker: HeliumWorkerDef, createContext: () => HeliumContext): Promise<WorkerInstance> {\n const { name, handler, options } = worker;\n\n // Check if worker is already running\n if (runningWorkers.has(name)) {\n const existing = runningWorkers.get(name)!;\n log(\"warn\", `Worker '${name}' is already running`);\n return existing.instance;\n }\n\n const abortController = new AbortController();\n let restartCount = 0;\n const workerState = {\n abortController,\n promise: Promise.resolve(),\n instance: undefined as unknown as WorkerInstance,\n cleanupHandlers: [] as Array<() => Promise<void> | void>,\n cleanupPromise: null as Promise<void> | null,\n };\n\n const instance: WorkerInstance = {\n id: name,\n name,\n status: \"running\",\n startedAt: new Date(),\n restartCount: 0,\n stop: async () => {\n abortController.abort();\n instance.status = \"stopped\";\n await runWorkerCleanup(name, workerState);\n runningWorkers.delete(name);\n log(\"info\", `Worker '${name}' stopped`);\n },\n };\n workerState.instance = instance;\n\n const runWorker = async (): Promise<void> => {\n while (!abortController.signal.aborted) {\n try {\n instance.status = \"running\";\n instance.startedAt = new Date();\n log(\"info\", `Starting worker '${name}'`);\n\n const ctx = createContext();\n const lifecycle: WorkerLifecycle = {\n signal: abortController.signal,\n onCleanup: (cleanup) => {\n registerCleanup(workerState, cleanup);\n },\n };\n const cleanup = await handler(ctx, lifecycle);\n registerCleanup(workerState, cleanup);\n\n if (isCleanupHandler(cleanup) && !abortController.signal.aborted) {\n await waitForAbort(abortController.signal);\n }\n\n // If handler completes normally, exit the loop\n if (!abortController.signal.aborted) {\n await runWorkerCleanup(name, workerState);\n log(\"info\", `Worker '${name}' completed successfully`);\n instance.status = \"stopped\";\n runningWorkers.delete(name);\n }\n break;\n } catch (error) {\n if (abortController.signal.aborted) {\n // Worker was intentionally stopped\n break;\n }\n\n await runWorkerCleanup(name, workerState);\n workerState.cleanupPromise = null;\n\n instance.lastError = error instanceof Error ? error : new Error(String(error));\n instance.status = \"crashed\";\n restartCount++;\n instance.restartCount = restartCount;\n\n log(\"error\", `Worker '${name}' crashed:`, error);\n\n // Check if we should restart\n if (options.autoRestart) {\n if (options.maxRestarts > 0 && restartCount >= options.maxRestarts) {\n log(\"error\", `Worker '${name}' exceeded max restarts (${options.maxRestarts}), giving up`);\n runningWorkers.delete(name);\n break;\n }\n\n instance.status = \"restarting\";\n log(\n \"info\",\n `Worker '${name}' will restart in ${options.restartDelayMs}ms (attempt ${restartCount}${options.maxRestarts > 0 ? `/${options.maxRestarts}` : \"\"})`\n );\n\n await new Promise((resolve) => setTimeout(resolve, options.restartDelayMs));\n } else {\n runningWorkers.delete(name);\n break;\n }\n }\n }\n };\n\n const promise = runWorker();\n workerState.promise = promise;\n\n runningWorkers.set(name, workerState);\n\n return instance;\n}\n\n/**\n * Stop a running worker by name.\n */\nexport function stopWorker(name: string): boolean {\n const worker = runningWorkers.get(name);\n if (worker) {\n worker.abortController.abort();\n worker.instance.status = \"stopped\";\n void runWorkerCleanup(name, worker)\n .catch(() => {})\n .finally(() => {\n runningWorkers.delete(name);\n log(\"info\", `Worker '${name}' stopped`);\n });\n return true;\n }\n return false;\n}\n\n/**\n * Stop all running workers.\n */\nexport async function stopAllWorkers(): Promise<void> {\n const workers = Array.from(runningWorkers.values());\n for (const worker of workers) {\n worker.abortController.abort();\n worker.instance.status = \"stopped\";\n }\n await Promise.all(workers.map(async (worker) => runWorkerCleanup(worker.instance.name, worker)));\n runningWorkers.clear();\n log(\"info\", `Stopped ${workers.length} worker(s)`);\n}\n\n/**\n * Get the status of all workers.\n */\nexport function getWorkerStatus(): WorkerInstance[] {\n return Array.from(runningWorkers.values()).map((w) => w.instance);\n}\n\n/**\n * Get a specific worker's status.\n */\nexport function getWorkerById(name: string): WorkerInstance | undefined {\n return runningWorkers.get(name)?.instance;\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;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAUtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAuD,UAAU,EAAE,MAAM,UAAU,CAAC;AAM3F,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAC5G,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;AAED,KAAK,cAAc,GAAG,CAAC,MAAM,OAAO,CAAC,aAAa,CAAC;IAAE,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;AAY7K;;;GAGG;AACH,wBAAgB,iBAAiB,CAC7B,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,cAAc,EAC5B,MAAM,GAAE,YAAiB,EACzB,OAAO,GAAE,WAAW,EAAO,EAC3B,QAAQ,GAAE,UAAU,EAAO,EAC3B,QAAQ,GAAE,cAAqB,QA8lBlC"}
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;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAUtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAuD,UAAU,EAAE,MAAM,UAAU,CAAC;AAM3F,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAC5G,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;AAED,KAAK,cAAc,GAAG,CAAC,MAAM,OAAO,CAAC,aAAa,CAAC;IAAE,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;AAqD7K;;;GAGG;AACH,wBAAgB,iBAAiB,CAC7B,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,cAAc,EAC5B,MAAM,GAAE,YAAiB,EACzB,OAAO,GAAE,WAAW,EAAO,EAC3B,QAAQ,GAAE,UAAU,EAAO,EAC3B,QAAQ,GAAE,cAAqB,QA4iBlC"}
@@ -29,6 +29,39 @@ let wss = null;
29
29
  let rateLimiter = null;
30
30
  let currentWorkers = [];
31
31
  let cachedDefaultMeta;
32
+ let workerReloadPromise = Promise.resolve();
33
+ function createWorkerContext() {
34
+ return {
35
+ req: {
36
+ ip: "127.0.0.1",
37
+ headers: {},
38
+ url: undefined,
39
+ method: undefined,
40
+ raw: {},
41
+ },
42
+ };
43
+ }
44
+ function normalizeWorkerName(name, worker) {
45
+ if (worker.name !== "anonymous") {
46
+ return;
47
+ }
48
+ worker.name = name;
49
+ worker.__id = name;
50
+ worker.options.name = name;
51
+ }
52
+ async function reloadWorkers(workers) {
53
+ await stopAllWorkers();
54
+ for (const { name, worker } of workers) {
55
+ normalizeWorkerName(name, worker);
56
+ if (!worker.options.autoStart) {
57
+ continue;
58
+ }
59
+ startWorker(worker, createWorkerContext).catch((err) => {
60
+ log("error", `Failed to start worker '${worker.name}':`, err);
61
+ });
62
+ }
63
+ currentWorkers = workers;
64
+ }
32
65
  /**
33
66
  * Attaches HeliumTS HTTP handlers and WebSocket RPC server to an existing HTTP server.
34
67
  * This is used in dev mode to attach to Vite's dev server.
@@ -76,63 +109,13 @@ export function attachToDevServer(httpServer, loadHandlers, config = {}, workers
76
109
  },
77
110
  });
78
111
  currentSEORouter = seoRouter;
79
- // Start workers if they changed
80
- const workersChanged = workers.length !== currentWorkers.length || workers.some((w, i) => w.name !== currentWorkers[i]?.name || w.worker !== currentWorkers[i]?.worker);
81
- if (workersChanged && workers.length > 0) {
82
- // Stop all existing workers before starting new ones
83
- stopAllWorkers().then(() => {
84
- // Start new workers
85
- for (const { name, worker } of workers) {
86
- // Use export name if worker name is anonymous
87
- if (worker.name === "anonymous") {
88
- worker.name = name;
89
- worker.__id = name;
90
- worker.options.name = name;
91
- }
92
- if (worker.options.autoStart) {
93
- const createContext = () => ({
94
- req: {
95
- ip: "127.0.0.1",
96
- headers: {},
97
- url: undefined,
98
- method: undefined,
99
- raw: {},
100
- },
101
- });
102
- startWorker(worker, createContext).catch((err) => {
103
- log("error", `Failed to start worker '${worker.name}':`, err);
104
- });
105
- }
106
- }
107
- currentWorkers = workers;
112
+ if (workers.length > 0 || currentWorkers.length > 0) {
113
+ workerReloadPromise = workerReloadPromise
114
+ .then(() => reloadWorkers(workers))
115
+ .catch((error) => {
116
+ log("error", "Failed to reload workers in dev mode", error);
108
117
  });
109
118
  }
110
- else if (currentWorkers.length === 0 && workers.length > 0) {
111
- // First time starting workers
112
- for (const { name, worker } of workers) {
113
- // Use export name if worker name is anonymous
114
- if (worker.name === "anonymous") {
115
- worker.name = name;
116
- worker.__id = name;
117
- worker.options.name = name;
118
- }
119
- if (worker.options.autoStart) {
120
- const createContext = () => ({
121
- req: {
122
- ip: "127.0.0.1",
123
- headers: {},
124
- url: undefined,
125
- method: undefined,
126
- raw: {},
127
- },
128
- });
129
- startWorker(worker, createContext).catch((err) => {
130
- log("error", `Failed to start worker '${worker.name}':`, err);
131
- });
132
- }
133
- }
134
- currentWorkers = workers;
135
- }
136
119
  // Attach WebSocket server if not already attached
137
120
  if (!wss) {
138
121
  wss = new WebSocketServer({
@@ -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 path from \"path\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { promisify } from \"util\";\nimport type WebSocket from \"ws\";\nimport { WebSocketServer } from \"ws\";\nimport { brotliCompress, deflate, gzip } from \"zlib\";\n\nimport { SEO_METADATA_RPC_METHOD } from \"../runtime/internalMethods.js\";\nimport { 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, loadDefaultSocialMetaFromHtmlFile } from \"./meta.js\";\nimport { RateLimiter } from \"./rateLimiter.js\";\nimport { RpcRegistry } from \"./rpcRegistry.js\";\nimport { initializeSecurity, verifyConnectionToken } from \"./security.js\";\nimport { SEOMetadataRouter } from \"./seoMetadataRouter.js\";\nimport { prepareForMsgpack } from \"./serializer.js\";\nimport { matchSSRPage, renderSSRHTML, resolveServerSideProps, SSRPageDef } from \"./ssr.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\ntype AppShellLoader = (() => Promise<ComponentType<{ Component: ComponentType<Record<string, unknown>>; pageProps: Record<string, unknown>; children?: ReactNode }>>) | null;\n\nlet currentRegistry: RpcRegistry | null = null;\nlet currentHttpRouter: HTTPRouter | null = null;\nlet currentSEORouter: SEOMetadataRouter | null = null;\nlet currentSSRPages: SSRPageDef[] = [];\nlet currentAppShell: AppShellLoader = null;\nlet wss: WebSocketServer | null = null;\nlet rateLimiter: RateLimiter | null = null;\nlet currentWorkers: WorkerEntry[] = [];\nlet cachedDefaultMeta: Awaited<ReturnType<typeof loadDefaultSocialMetaFromHtmlFile>> | undefined;\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(\n httpServer: HttpServer,\n loadHandlers: LoadHandlersFn,\n config: HeliumConfig = {},\n workers: WorkerEntry[] = [],\n ssrPages: SSRPageDef[] = [],\n appShell: AppShellLoader = null\n) {\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({ maxBodySize: rpcConfig.maxBodySize });\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 currentSSRPages = ssrPages;\n currentAppShell = appShell;\n cachedDefaultMeta = undefined;\n\n const getDefaultMeta = async () => {\n if (cachedDefaultMeta !== undefined) {\n return cachedDefaultMeta;\n }\n\n cachedDefaultMeta = await loadDefaultSocialMetaFromHtmlFile(`${process.cwd()}/index.html`);\n return cachedDefaultMeta;\n };\n\n registry.register(SEO_METADATA_RPC_METHOD, {\n __kind: \"method\",\n __id: SEO_METADATA_RPC_METHOD,\n handler: async (args: { path?: string } | undefined, ctx: HeliumContext) => {\n const requestedPath = typeof args?.path === \"string\" ? args.path : \"/\";\n const targetPath = requestedPath.startsWith(\"/\") ? requestedPath : `/${requestedPath}`;\n const metadata = await seoRouter.resolve(ctx.req.raw, ctx, targetPath);\n return metadata ?? (await getDefaultMeta());\n },\n });\n 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 const reqUrl = new URL(req.url || \"/\", \"http://localhost\");\n const pathname = reqUrl.pathname;\n const search = reqUrl.search;\n\n if (pathname === \"/__helium__/page-props\" && req.method === \"GET\") {\n const pathQuery = reqUrl.searchParams.get(\"path\") || \"/\";\n const targetUrl = new URL(pathQuery, \"http://localhost\");\n const targetPathname = targetUrl.pathname;\n\n const ssrMatch = matchSSRPage(targetPathname, currentSSRPages);\n if (!ssrMatch) {\n res.writeHead(200, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: false, props: null }));\n return;\n }\n\n const ip = extractClientIP(req, trustProxyDepth);\n const httpCtx: HeliumContext = {\n req: {\n ip,\n headers: req.headers,\n url: req.url,\n method: req.method,\n raw: req,\n },\n };\n\n try {\n const ssrResult = await resolveServerSideProps({\n req,\n pathname: targetPathname,\n params: ssrMatch.params,\n page: ssrMatch.page,\n ctx: httpCtx,\n });\n\n if (ssrResult.kind === \"redirect\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: true, redirect: ssrResult.redirect }));\n return;\n }\n\n res.writeHead(200, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: true, props: ssrResult.props }));\n } catch (error) {\n log(\"error\", \"Failed to resolve SSR page props:\", error);\n res.writeHead(500, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: true, props: null, error: \"Internal server error\" }));\n }\n\n return;\n }\n\n // 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 const acceptsHtml = String(req.headers[\"accept\"] || \"\").includes(\"text/html\") || String(req.headers[\"accept\"] || \"\") === \"*/*\";\n const isEligibleHtmlRequest =\n req.method === \"GET\" &&\n acceptsHtml &&\n path.extname(pathname) === \"\" &&\n !pathname.startsWith(\"/api\") &&\n !pathname.startsWith(\"/webhooks\") &&\n !pathname.startsWith(\"/auth\") &&\n !pathname.startsWith(\"/@\") &&\n !pathname.startsWith(\"/__helium__\");\n const ssrMatch = isEligibleHtmlRequest ? matchSSRPage(pathname, currentSSRPages) : null;\n\n if (devResolvedMetadata || ssrMatch) {\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 let pendingWriteHead:\n | {\n statusCode: number;\n statusMessageOrHeaders?: string | Record<string, string | number | readonly string[]>;\n headers?: Record<string, string | number | readonly string[]>;\n }\n | undefined;\n\n const flushPendingWriteHead = () => {\n if (!pendingWriteHead) {\n return;\n }\n\n const { statusCode, statusMessageOrHeaders, headers } = pendingWriteHead;\n\n if (typeof statusMessageOrHeaders === \"string\") {\n originalWriteHead(statusCode, statusMessageOrHeaders, headers);\n return;\n }\n\n originalWriteHead(statusCode, statusMessageOrHeaders);\n };\n\n res.writeHead = (statusCode: number, statusMessageOrHeaders?: any, headers?: any) => {\n const providedHeaders = typeof statusMessageOrHeaders === \"string\" ? headers : 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 pendingWriteHead = {\n statusCode,\n statusMessageOrHeaders,\n headers,\n };\n\n return res;\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 finalize = async () => {\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 flushPendingWriteHead();\n originalEnd(bodyBuffer);\n return;\n }\n\n let html = bodyText;\n\n if (ssrMatch) {\n const ip = extractClientIP(req, trustProxyDepth);\n const httpCtx: HeliumContext = {\n req: {\n ip,\n headers: req.headers,\n url: req.url,\n method: req.method,\n raw: req,\n },\n };\n\n try {\n const rendered = await renderSSRHTML({\n htmlTemplate: html,\n pathname,\n search,\n params: ssrMatch.params,\n page: ssrMatch.page,\n req,\n ctx: httpCtx,\n loadAppShell: currentAppShell,\n });\n\n if (\"redirect\" in rendered) {\n originalWriteHead(rendered.redirect.statusCode, {\n Location: rendered.redirect.destination,\n \"Cache-Control\": \"no-store\",\n });\n originalEnd();\n return;\n }\n\n html = rendered.html;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const isBrowserGlobalError = /\\bwindow is not defined\\b|\\bdocument is not defined\\b|\\bnavigator is not defined\\b/i.test(message);\n\n if (isBrowserGlobalError) {\n log(\n \"warn\",\n `SSR disabled for ${pathname} due to browser-only import. Render map/browser-only modules on client only (e.g. dynamic import inside useEffect).`\n );\n } else {\n log(\"error\", `Failed to render SSR page for ${pathname}:`, error);\n }\n }\n }\n\n if (devResolvedMetadata) {\n html = injectSocialMetaIntoHtml(html, devResolvedMetadata);\n }\n\n flushPendingWriteHead();\n originalEnd(Buffer.from(html, \"utf-8\"));\n };\n\n void finalize();\n return true;\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"]}
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 path from \"path\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { promisify } from \"util\";\nimport type WebSocket from \"ws\";\nimport { WebSocketServer } from \"ws\";\nimport { brotliCompress, deflate, gzip } from \"zlib\";\n\nimport { SEO_METADATA_RPC_METHOD } from \"../runtime/internalMethods.js\";\nimport { 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, loadDefaultSocialMetaFromHtmlFile } from \"./meta.js\";\nimport { RateLimiter } from \"./rateLimiter.js\";\nimport { RpcRegistry } from \"./rpcRegistry.js\";\nimport { initializeSecurity, verifyConnectionToken } from \"./security.js\";\nimport { SEOMetadataRouter } from \"./seoMetadataRouter.js\";\nimport { prepareForMsgpack } from \"./serializer.js\";\nimport { matchSSRPage, renderSSRHTML, resolveServerSideProps, SSRPageDef } from \"./ssr.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\ntype AppShellLoader = (() => Promise<ComponentType<{ Component: ComponentType<Record<string, unknown>>; pageProps: Record<string, unknown>; children?: ReactNode }>>) | null;\n\nlet currentRegistry: RpcRegistry | null = null;\nlet currentHttpRouter: HTTPRouter | null = null;\nlet currentSEORouter: SEOMetadataRouter | null = null;\nlet currentSSRPages: SSRPageDef[] = [];\nlet currentAppShell: AppShellLoader = null;\nlet wss: WebSocketServer | null = null;\nlet rateLimiter: RateLimiter | null = null;\nlet currentWorkers: WorkerEntry[] = [];\nlet cachedDefaultMeta: Awaited<ReturnType<typeof loadDefaultSocialMetaFromHtmlFile>> | undefined;\nlet workerReloadPromise: Promise<void> = Promise.resolve();\n\nfunction createWorkerContext(): HeliumContext {\n return {\n req: {\n ip: \"127.0.0.1\",\n headers: {},\n url: undefined,\n method: undefined,\n raw: {} as http.IncomingMessage,\n },\n };\n}\n\nfunction normalizeWorkerName(name: string, worker: HeliumWorkerDef) {\n if (worker.name !== \"anonymous\") {\n return;\n }\n\n worker.name = name;\n worker.__id = name;\n worker.options.name = name;\n}\n\nasync function reloadWorkers(workers: WorkerEntry[]) {\n await stopAllWorkers();\n\n for (const { name, worker } of workers) {\n normalizeWorkerName(name, worker);\n\n if (!worker.options.autoStart) {\n continue;\n }\n\n startWorker(worker, createWorkerContext).catch((err) => {\n log(\"error\", `Failed to start worker '${worker.name}':`, err);\n });\n }\n\n currentWorkers = workers;\n}\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(\n httpServer: HttpServer,\n loadHandlers: LoadHandlersFn,\n config: HeliumConfig = {},\n workers: WorkerEntry[] = [],\n ssrPages: SSRPageDef[] = [],\n appShell: AppShellLoader = null\n) {\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({ maxBodySize: rpcConfig.maxBodySize });\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 currentSSRPages = ssrPages;\n currentAppShell = appShell;\n cachedDefaultMeta = undefined;\n\n const getDefaultMeta = async () => {\n if (cachedDefaultMeta !== undefined) {\n return cachedDefaultMeta;\n }\n\n cachedDefaultMeta = await loadDefaultSocialMetaFromHtmlFile(`${process.cwd()}/index.html`);\n return cachedDefaultMeta;\n };\n\n registry.register(SEO_METADATA_RPC_METHOD, {\n __kind: \"method\",\n __id: SEO_METADATA_RPC_METHOD,\n handler: async (args: { path?: string } | undefined, ctx: HeliumContext) => {\n const requestedPath = typeof args?.path === \"string\" ? args.path : \"/\";\n const targetPath = requestedPath.startsWith(\"/\") ? requestedPath : `/${requestedPath}`;\n const metadata = await seoRouter.resolve(ctx.req.raw, ctx, targetPath);\n return metadata ?? (await getDefaultMeta());\n },\n });\n currentSEORouter = seoRouter;\n\n if (workers.length > 0 || currentWorkers.length > 0) {\n workerReloadPromise = workerReloadPromise\n .then(() => reloadWorkers(workers))\n .catch((error) => {\n log(\"error\", \"Failed to reload workers in dev mode\", error);\n });\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 const reqUrl = new URL(req.url || \"/\", \"http://localhost\");\n const pathname = reqUrl.pathname;\n const search = reqUrl.search;\n\n if (pathname === \"/__helium__/page-props\" && req.method === \"GET\") {\n const pathQuery = reqUrl.searchParams.get(\"path\") || \"/\";\n const targetUrl = new URL(pathQuery, \"http://localhost\");\n const targetPathname = targetUrl.pathname;\n\n const ssrMatch = matchSSRPage(targetPathname, currentSSRPages);\n if (!ssrMatch) {\n res.writeHead(200, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: false, props: null }));\n return;\n }\n\n const ip = extractClientIP(req, trustProxyDepth);\n const httpCtx: HeliumContext = {\n req: {\n ip,\n headers: req.headers,\n url: req.url,\n method: req.method,\n raw: req,\n },\n };\n\n try {\n const ssrResult = await resolveServerSideProps({\n req,\n pathname: targetPathname,\n params: ssrMatch.params,\n page: ssrMatch.page,\n ctx: httpCtx,\n });\n\n if (ssrResult.kind === \"redirect\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: true, redirect: ssrResult.redirect }));\n return;\n }\n\n res.writeHead(200, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: true, props: ssrResult.props }));\n } catch (error) {\n log(\"error\", \"Failed to resolve SSR page props:\", error);\n res.writeHead(500, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify({ ssr: true, props: null, error: \"Internal server error\" }));\n }\n\n return;\n }\n\n // 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 const acceptsHtml = String(req.headers[\"accept\"] || \"\").includes(\"text/html\") || String(req.headers[\"accept\"] || \"\") === \"*/*\";\n const isEligibleHtmlRequest =\n req.method === \"GET\" &&\n acceptsHtml &&\n path.extname(pathname) === \"\" &&\n !pathname.startsWith(\"/api\") &&\n !pathname.startsWith(\"/webhooks\") &&\n !pathname.startsWith(\"/auth\") &&\n !pathname.startsWith(\"/@\") &&\n !pathname.startsWith(\"/__helium__\");\n const ssrMatch = isEligibleHtmlRequest ? matchSSRPage(pathname, currentSSRPages) : null;\n\n if (devResolvedMetadata || ssrMatch) {\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 let pendingWriteHead:\n | {\n statusCode: number;\n statusMessageOrHeaders?: string | Record<string, string | number | readonly string[]>;\n headers?: Record<string, string | number | readonly string[]>;\n }\n | undefined;\n\n const flushPendingWriteHead = () => {\n if (!pendingWriteHead) {\n return;\n }\n\n const { statusCode, statusMessageOrHeaders, headers } = pendingWriteHead;\n\n if (typeof statusMessageOrHeaders === \"string\") {\n originalWriteHead(statusCode, statusMessageOrHeaders, headers);\n return;\n }\n\n originalWriteHead(statusCode, statusMessageOrHeaders);\n };\n\n res.writeHead = (statusCode: number, statusMessageOrHeaders?: any, headers?: any) => {\n const providedHeaders = typeof statusMessageOrHeaders === \"string\" ? headers : 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 pendingWriteHead = {\n statusCode,\n statusMessageOrHeaders,\n headers,\n };\n\n return res;\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 finalize = async () => {\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 flushPendingWriteHead();\n originalEnd(bodyBuffer);\n return;\n }\n\n let html = bodyText;\n\n if (ssrMatch) {\n const ip = extractClientIP(req, trustProxyDepth);\n const httpCtx: HeliumContext = {\n req: {\n ip,\n headers: req.headers,\n url: req.url,\n method: req.method,\n raw: req,\n },\n };\n\n try {\n const rendered = await renderSSRHTML({\n htmlTemplate: html,\n pathname,\n search,\n params: ssrMatch.params,\n page: ssrMatch.page,\n req,\n ctx: httpCtx,\n loadAppShell: currentAppShell,\n });\n\n if (\"redirect\" in rendered) {\n originalWriteHead(rendered.redirect.statusCode, {\n Location: rendered.redirect.destination,\n \"Cache-Control\": \"no-store\",\n });\n originalEnd();\n return;\n }\n\n html = rendered.html;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const isBrowserGlobalError = /\\bwindow is not defined\\b|\\bdocument is not defined\\b|\\bnavigator is not defined\\b/i.test(message);\n\n if (isBrowserGlobalError) {\n log(\n \"warn\",\n `SSR disabled for ${pathname} due to browser-only import. Render map/browser-only modules on client only (e.g. dynamic import inside useEffect).`\n );\n } else {\n log(\"error\", `Failed to render SSR page for ${pathname}:`, error);\n }\n }\n }\n\n if (devResolvedMetadata) {\n html = injectSocialMetaIntoHtml(html, devResolvedMetadata);\n }\n\n flushPendingWriteHead();\n originalEnd(Buffer.from(html, \"utf-8\"));\n };\n\n void finalize();\n return true;\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"]}
@@ -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,4 @@
1
+ export declare function getRequestPath(url: string | undefined): string;
2
+ export declare function isDevInternalOrAssetRequest(url: string | undefined): boolean;
3
+ export declare function resolveDirectStaticAssetPath(staticDir: string, url: string | undefined): string | null;
4
+ //# sourceMappingURL=requestRouting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestRouting.d.ts","sourceRoot":"","sources":["../../src/server/requestRouting.ts"],"names":[],"mappings":"AAsCA,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE9D;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAS5E;AAED,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAwBtG"}
@@ -0,0 +1,67 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ const STATIC_ASSET_EXTENSIONS = new Set([
4
+ ".js",
5
+ ".mjs",
6
+ ".cjs",
7
+ ".ts",
8
+ ".tsx",
9
+ ".jsx",
10
+ ".css",
11
+ ".map",
12
+ ".json",
13
+ ".png",
14
+ ".jpg",
15
+ ".jpeg",
16
+ ".gif",
17
+ ".svg",
18
+ ".ico",
19
+ ".webp",
20
+ ".avif",
21
+ ".woff",
22
+ ".woff2",
23
+ ".ttf",
24
+ ".eot",
25
+ ".otf",
26
+ ".mp4",
27
+ ".webm",
28
+ ".ogg",
29
+ ".mp3",
30
+ ".wav",
31
+ ".txt",
32
+ ".xml",
33
+ ".wasm",
34
+ ]);
35
+ const DEV_INTERNAL_PREFIXES = ["/@vite", "/@fs/", "/@id/", "/__vite", "/src/", "/node_modules/"];
36
+ export function getRequestPath(url) {
37
+ return (url || "/").split("?")[0] || "/";
38
+ }
39
+ export function isDevInternalOrAssetRequest(url) {
40
+ const requestPath = getRequestPath(url);
41
+ if (DEV_INTERNAL_PREFIXES.some((prefix) => requestPath.startsWith(prefix))) {
42
+ return true;
43
+ }
44
+ const extension = path.extname(requestPath).toLowerCase();
45
+ return extension.length > 0 && STATIC_ASSET_EXTENSIONS.has(extension);
46
+ }
47
+ export function resolveDirectStaticAssetPath(staticDir, url) {
48
+ const requestPath = getRequestPath(url);
49
+ const extension = path.extname(requestPath).toLowerCase();
50
+ if (!extension) {
51
+ return null;
52
+ }
53
+ const resolvedStaticDir = path.resolve(staticDir);
54
+ const candidatePath = path.resolve(staticDir, "." + requestPath);
55
+ if (!candidatePath.startsWith(resolvedStaticDir + path.sep) && candidatePath !== resolvedStaticDir) {
56
+ return null;
57
+ }
58
+ if (!fs.existsSync(candidatePath)) {
59
+ return null;
60
+ }
61
+ const stat = fs.statSync(candidatePath);
62
+ if (!stat.isFile()) {
63
+ return null;
64
+ }
65
+ return candidatePath;
66
+ }
67
+ //# sourceMappingURL=requestRouting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestRouting.js","sourceRoot":"","sources":["../../src/server/requestRouting.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC;IACpC,KAAK;IACL,MAAM;IACN,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;IACP,QAAQ;IACR,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;CACV,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC;AAEjG,MAAM,UAAU,cAAc,CAAC,GAAuB;IAClD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,GAAuB;IAC/D,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAExC,IAAI,qBAAqB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QACzE,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1D,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,uBAAuB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,SAAiB,EAAE,GAAuB;IACnF,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;IAE1D,IAAI,CAAC,SAAS,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,GAAG,WAAW,CAAC,CAAC;IACjE,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,aAAa,KAAK,iBAAiB,EAAE,CAAC;QACjG,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,OAAO,aAAa,CAAC;AACzB,CAAC","sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nconst STATIC_ASSET_EXTENSIONS = new Set([\n \".js\",\n \".mjs\",\n \".cjs\",\n \".ts\",\n \".tsx\",\n \".jsx\",\n \".css\",\n \".map\",\n \".json\",\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".svg\",\n \".ico\",\n \".webp\",\n \".avif\",\n \".woff\",\n \".woff2\",\n \".ttf\",\n \".eot\",\n \".otf\",\n \".mp4\",\n \".webm\",\n \".ogg\",\n \".mp3\",\n \".wav\",\n \".txt\",\n \".xml\",\n \".wasm\",\n]);\n\nconst DEV_INTERNAL_PREFIXES = [\"/@vite\", \"/@fs/\", \"/@id/\", \"/__vite\", \"/src/\", \"/node_modules/\"];\n\nexport function getRequestPath(url: string | undefined): string {\n return (url || \"/\").split(\"?\")[0] || \"/\";\n}\n\nexport function isDevInternalOrAssetRequest(url: string | undefined): boolean {\n const requestPath = getRequestPath(url);\n\n if (DEV_INTERNAL_PREFIXES.some((prefix) => requestPath.startsWith(prefix))) {\n return true;\n }\n\n const extension = path.extname(requestPath).toLowerCase();\n return extension.length > 0 && STATIC_ASSET_EXTENSIONS.has(extension);\n}\n\nexport function resolveDirectStaticAssetPath(staticDir: string, url: string | undefined): string | null {\n const requestPath = getRequestPath(url);\n const extension = path.extname(requestPath).toLowerCase();\n\n if (!extension) {\n return null;\n }\n\n const resolvedStaticDir = path.resolve(staticDir);\n const candidatePath = path.resolve(staticDir, \".\" + requestPath);\n if (!candidatePath.startsWith(resolvedStaticDir + path.sep) && candidatePath !== resolvedStaticDir) {\n return null;\n }\n\n if (!fs.existsSync(candidatePath)) {\n return null;\n }\n\n const stat = fs.statSync(candidatePath);\n if (!stat.isFile()) {\n return null;\n }\n\n return candidatePath;\n}\n"]}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=deepEqual.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepEqual.d.ts","sourceRoot":"","sources":["../../src/utils/deepEqual.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=deepEqual.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepEqual.js","sourceRoot":"","sources":["../../src/utils/deepEqual.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare function formatError(err: unknown): string;
2
+ //# sourceMappingURL=formatError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatError.d.ts","sourceRoot":"","sources":["../../src/utils/formatError.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAgBhD"}
@@ -0,0 +1,18 @@
1
+ export function formatError(err) {
2
+ console.log("🚀 ~ formatError ~ err:", err);
3
+ if (err instanceof Error) {
4
+ return err.message;
5
+ }
6
+ if (typeof err === "object" && err !== null) {
7
+ if ("message" in err) {
8
+ return String(err.message);
9
+ }
10
+ // Format Record<string, string> errors
11
+ return JSON.stringify(err, null, 2);
12
+ }
13
+ if (typeof err === "string") {
14
+ return err;
15
+ }
16
+ return String(err);
17
+ }
18
+ //# sourceMappingURL=formatError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatError.js","sourceRoot":"","sources":["../../src/utils/formatError.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,GAAY;IACpC,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAC;IAC5C,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,OAAO,CAAC;IACvB,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC1C,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;YACnB,OAAO,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QACD,uCAAuC;QACvC,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heliumts",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "A lightweight full-stack React framework with file-based routing, RPC, and SSG support",
5
5
  "keywords": [
6
6
  "react",
@@ -1 +0,0 @@
1
- //# sourceMappingURL=serverPropsRouter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"serverPropsRouter.d.ts","sourceRoot":"","sources":["../../src/server/serverPropsRouter.ts"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- "use strict";
2
- //# sourceMappingURL=serverPropsRouter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"serverPropsRouter.js","sourceRoot":"","sources":["../../src/server/serverPropsRouter.ts"],"names":[],"mappings":"","sourcesContent":[""]}