@morojs/moro 1.8.8 → 1.8.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/core/cluster/thread-cluster.d.ts +178 -0
  2. package/dist/core/cluster/thread-cluster.js +389 -0
  3. package/dist/core/cluster/thread-cluster.js.map +1 -0
  4. package/dist/core/framework.js.map +1 -1
  5. package/dist/core/http/moro-engine-server.d.ts +29 -1
  6. package/dist/core/http/moro-engine-server.js +375 -68
  7. package/dist/core/http/moro-engine-server.js.map +1 -1
  8. package/dist/core/http/utils/compression.js +12 -6
  9. package/dist/core/http/utils/compression.js.map +1 -1
  10. package/dist/core/http/utils/uws-worker-clustering.js +10 -6
  11. package/dist/core/http/utils/uws-worker-clustering.js.map +1 -1
  12. package/dist/core/http/uws-http-server.d.ts +1 -0
  13. package/dist/core/http/uws-http-server.js +105 -22
  14. package/dist/core/http/uws-http-server.js.map +1 -1
  15. package/dist/core/jobs/leader-election.js +16 -5
  16. package/dist/core/jobs/leader-election.js.map +1 -1
  17. package/dist/core/networking/websocket-manager.js +4 -2
  18. package/dist/core/networking/websocket-manager.js.map +1 -1
  19. package/dist/core/routing/unified-router.js +60 -29
  20. package/dist/core/routing/unified-router.js.map +1 -1
  21. package/dist/core/runtime/node-adapter.d.ts +1 -1
  22. package/dist/core/runtime/node-adapter.js.map +1 -1
  23. package/dist/core/utilities/builtin.d.ts +1 -0
  24. package/dist/core/utilities/builtin.js +25 -0
  25. package/dist/core/utilities/builtin.js.map +1 -0
  26. package/dist/core/utilities/package-utils.d.ts +28 -3
  27. package/dist/core/utilities/package-utils.js +24 -12
  28. package/dist/core/utilities/package-utils.js.map +1 -1
  29. package/dist/core/workers/worker-manager.js +4 -3
  30. package/dist/core/workers/worker-manager.js.map +1 -1
  31. package/dist/moro.d.ts +5 -0
  32. package/dist/moro.js +364 -204
  33. package/dist/moro.js.map +1 -1
  34. package/dist/types/http.d.ts +1 -1
  35. package/dist/types/http.js.map +1 -1
  36. package/package.json +1 -1
@@ -8,11 +8,17 @@
8
8
  * engine's Content-Length short-write detection. That is a deliberate,
9
9
  * documented non-goal.
10
10
  */
11
- import { gzip, deflate, brotliCompress, constants as zlibConstants } from 'zlib';
12
11
  import { promisify } from 'util';
13
- const gzipAsync = promisify(gzip);
14
- const deflateAsync = promisify(deflate);
15
- const brotliAsync = promisify(brotliCompress);
12
+ import { requireBuiltin } from '../../utilities/builtin.js';
13
+ // zlib is loaded on the first compressed response, not at startup
14
+ // (core/utilities/builtin.ts): compression is off by default.
15
+ const z = () => requireBuiltin('zlib');
16
+ let gzipFn;
17
+ let deflateFn;
18
+ let brotliFn;
19
+ const gzipAsync = (buf, opts) => (gzipFn ??= promisify(z().gzip))(buf, opts);
20
+ const deflateAsync = (buf, opts) => (deflateFn ??= promisify(z().deflate))(buf, opts);
21
+ const brotliAsync = (buf, opts) => (brotliFn ??= promisify(z().brotliCompress))(buf, opts);
16
22
  export const DEFAULT_ENCODINGS = ['br', 'gzip', 'deflate'];
17
23
  /**
18
24
  * Build CompressionSettings from the framework's performance config (the
@@ -93,8 +99,8 @@ export async function compressBuffer(data, encoding, level = 6) {
93
99
  case 'br':
94
100
  return brotliAsync(buf, {
95
101
  params: {
96
- [zlibConstants.BROTLI_PARAM_QUALITY]: Math.max(0, Math.min(11, Math.round((level * 11) / 9))),
97
- [zlibConstants.BROTLI_PARAM_SIZE_HINT]: buf.length,
102
+ [z().constants.BROTLI_PARAM_QUALITY]: Math.max(0, Math.min(11, Math.round((level * 11) / 9))),
103
+ [z().constants.BROTLI_PARAM_SIZE_HINT]: buf.length,
98
104
  },
99
105
  });
100
106
  case 'gzip':
@@ -1 +1 @@
1
- {"version":3,"file":"compression.js","sourceRoot":"","sources":["../../../../src/core/http/utils/compression.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared response compression, used by every runtime (Node, uWS, HTTP/2, and\n * the native engine) so switching engines never silently drops compression.\n *\n * Scope: BUFFERED responses only (json/send/end with a full body). The\n * streaming path (writeHead + write + write + end) stays uncompressed on all\n * runtimes — per-chunk flush semantics would break SSE latency and the\n * engine's Content-Length short-write detection. That is a deliberate,\n * documented non-goal.\n */\nimport { gzip, deflate, brotliCompress, constants as zlibConstants } from 'zlib';\nimport { promisify } from 'util';\n\nconst gzipAsync = promisify(gzip);\nconst deflateAsync = promisify(deflate);\nconst brotliAsync = promisify(brotliCompress);\n\nexport type Encoding = 'br' | 'gzip' | 'deflate';\n\nexport interface CompressionSettings {\n enabled: boolean;\n threshold: number;\n level: number; // 1-9 (mapped to brotli quality)\n encodings: Encoding[]; // server preference order\n}\n\nexport const DEFAULT_ENCODINGS: Encoding[] = ['br', 'gzip', 'deflate'];\n\n/**\n * Build CompressionSettings from the framework's performance config (the\n * shape passed to every server's configurePerformance). Shared so all four\n * runtimes interpret `performance.compression` identically.\n */\nexport function resolveCompressionSettings(config?: {\n compression?: { enabled?: boolean; threshold?: number; level?: number; encodings?: Encoding[] };\n minimal?: boolean;\n}): CompressionSettings {\n const c = config?.compression;\n // Minimal mode disables all response overhead for pure throughput.\n const enabled = config?.minimal ? false : c?.enabled !== false && c !== undefined;\n return {\n enabled,\n threshold: c?.threshold ?? 1024,\n level: c?.level ?? 6,\n encodings: c?.encodings ?? DEFAULT_ENCODINGS,\n };\n}\n\n/**\n * Content types worth compressing. Text and structured formats yes; already\n * compressed binary (images, video, zip, octet-stream) no.\n */\nexport function isCompressible(contentType: string | undefined): boolean {\n if (!contentType) return false;\n const ct = (contentType.split(';')[0] ?? '').trim().toLowerCase();\n if (ct.startsWith('text/')) return true;\n if (ct.endsWith('+json') || ct.endsWith('+xml')) return true;\n return (\n ct === 'application/json' ||\n ct === 'application/javascript' ||\n ct === 'application/xml' ||\n ct === 'application/ld+json' ||\n ct === 'application/manifest+json' ||\n ct === 'image/svg+xml'\n );\n}\n\n/**\n * Pick the best encoding the client accepts from the server's preference\n * list, honoring q-values (q=0 excludes). Returns null when the client\n * accepts none / only identity.\n */\nexport function negotiateEncoding(\n acceptEncoding: string | undefined,\n allowed: Encoding[] = DEFAULT_ENCODINGS\n): Encoding | null {\n if (!acceptEncoding) return null;\n\n // Parse \"gzip;q=0.8, br, deflate;q=0\" into a q-value map.\n const q = new Map<string, number>();\n for (const part of acceptEncoding.split(',')) {\n const [tokenRaw, ...params] = part.trim().split(';');\n const token = (tokenRaw ?? '').trim().toLowerCase();\n if (!token) continue;\n let weight = 1;\n for (const p of params) {\n const m = /^\\s*q=([0-9.]+)\\s*$/i.exec(p);\n if (m && m[1] !== undefined) weight = parseFloat(m[1]);\n }\n q.set(token, weight);\n }\n\n const wildcard = q.get('*');\n const accepts = (enc: Encoding): boolean => {\n if (q.has(enc)) return (q.get(enc) as number) > 0;\n if (wildcard !== undefined) return wildcard > 0;\n return false;\n };\n\n for (const enc of allowed) {\n if (accepts(enc)) return enc;\n }\n return null;\n}\n\n/** Compress a buffer/string with the chosen encoding. */\nexport async function compressBuffer(\n data: Buffer | string,\n encoding: Encoding,\n level = 6\n): Promise<Buffer> {\n const buf = typeof data === 'string' ? Buffer.from(data, 'utf8') : data;\n switch (encoding) {\n case 'br':\n return brotliAsync(buf, {\n params: {\n [zlibConstants.BROTLI_PARAM_QUALITY]: Math.max(\n 0,\n Math.min(11, Math.round((level * 11) / 9))\n ),\n [zlibConstants.BROTLI_PARAM_SIZE_HINT]: buf.length,\n },\n });\n case 'gzip':\n return gzipAsync(buf, { level });\n case 'deflate':\n return deflateAsync(buf, { level });\n }\n}\n\nexport interface MaybeCompressResult {\n body: Buffer | string;\n encoding: Encoding | null;\n}\n\n/**\n * The one-call helper the servers use: decide whether to compress `body`\n * given the response content type, the client's Accept-Encoding, and the\n * server's compression settings. Returns the (possibly compressed) body plus\n * the chosen encoding (null = sent as-is). Never throws — a compression error\n * degrades to the original body.\n */\nexport async function maybeCompress(\n body: Buffer | string,\n contentType: string | undefined,\n acceptEncoding: string | undefined,\n settings: CompressionSettings\n): Promise<MaybeCompressResult> {\n if (!settings.enabled) return { body, encoding: null };\n const byteLen = typeof body === 'string' ? Buffer.byteLength(body) : body.length;\n if (byteLen < settings.threshold) return { body, encoding: null };\n if (!isCompressible(contentType)) return { body, encoding: null };\n\n const encoding = negotiateEncoding(acceptEncoding, settings.encodings);\n if (!encoding) return { body, encoding: null };\n\n try {\n const compressed = await compressBuffer(body, encoding, settings.level);\n return { body: compressed, encoding };\n } catch {\n return { body, encoding: null };\n }\n}\n"]}
1
+ {"version":3,"file":"compression.js","sourceRoot":"","sources":["../../../../src/core/http/utils/compression.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared response compression, used by every runtime (Node, uWS, HTTP/2, and\n * the native engine) so switching engines never silently drops compression.\n *\n * Scope: BUFFERED responses only (json/send/end with a full body). The\n * streaming path (writeHead + write + write + end) stays uncompressed on all\n * runtimes — per-chunk flush semantics would break SSE latency and the\n * engine's Content-Length short-write detection. That is a deliberate,\n * documented non-goal.\n */\nimport { promisify } from 'util';\nimport { requireBuiltin } from '../../utilities/builtin.js';\n\n// zlib is loaded on the first compressed response, not at startup\n// (core/utilities/builtin.ts): compression is off by default.\nconst z = () => requireBuiltin<typeof import('zlib')>('zlib');\ntype Compressor = (buf: Buffer, opts: any) => Promise<Buffer>;\nlet gzipFn: Compressor | undefined;\nlet deflateFn: Compressor | undefined;\nlet brotliFn: Compressor | undefined;\nconst gzipAsync: Compressor = (buf, opts) =>\n (gzipFn ??= promisify(z().gzip) as unknown as Compressor)(buf, opts);\nconst deflateAsync: Compressor = (buf, opts) =>\n (deflateFn ??= promisify(z().deflate) as unknown as Compressor)(buf, opts);\nconst brotliAsync: Compressor = (buf, opts) =>\n (brotliFn ??= promisify(z().brotliCompress) as unknown as Compressor)(buf, opts);\n\nexport type Encoding = 'br' | 'gzip' | 'deflate';\n\nexport interface CompressionSettings {\n enabled: boolean;\n threshold: number;\n level: number; // 1-9 (mapped to brotli quality)\n encodings: Encoding[]; // server preference order\n}\n\nexport const DEFAULT_ENCODINGS: Encoding[] = ['br', 'gzip', 'deflate'];\n\n/**\n * Build CompressionSettings from the framework's performance config (the\n * shape passed to every server's configurePerformance). Shared so all four\n * runtimes interpret `performance.compression` identically.\n */\nexport function resolveCompressionSettings(config?: {\n compression?: { enabled?: boolean; threshold?: number; level?: number; encodings?: Encoding[] };\n minimal?: boolean;\n}): CompressionSettings {\n const c = config?.compression;\n // Minimal mode disables all response overhead for pure throughput.\n const enabled = config?.minimal ? false : c?.enabled !== false && c !== undefined;\n return {\n enabled,\n threshold: c?.threshold ?? 1024,\n level: c?.level ?? 6,\n encodings: c?.encodings ?? DEFAULT_ENCODINGS,\n };\n}\n\n/**\n * Content types worth compressing. Text and structured formats yes; already\n * compressed binary (images, video, zip, octet-stream) no.\n */\nexport function isCompressible(contentType: string | undefined): boolean {\n if (!contentType) return false;\n const ct = (contentType.split(';')[0] ?? '').trim().toLowerCase();\n if (ct.startsWith('text/')) return true;\n if (ct.endsWith('+json') || ct.endsWith('+xml')) return true;\n return (\n ct === 'application/json' ||\n ct === 'application/javascript' ||\n ct === 'application/xml' ||\n ct === 'application/ld+json' ||\n ct === 'application/manifest+json' ||\n ct === 'image/svg+xml'\n );\n}\n\n/**\n * Pick the best encoding the client accepts from the server's preference\n * list, honoring q-values (q=0 excludes). Returns null when the client\n * accepts none / only identity.\n */\nexport function negotiateEncoding(\n acceptEncoding: string | undefined,\n allowed: Encoding[] = DEFAULT_ENCODINGS\n): Encoding | null {\n if (!acceptEncoding) return null;\n\n // Parse \"gzip;q=0.8, br, deflate;q=0\" into a q-value map.\n const q = new Map<string, number>();\n for (const part of acceptEncoding.split(',')) {\n const [tokenRaw, ...params] = part.trim().split(';');\n const token = (tokenRaw ?? '').trim().toLowerCase();\n if (!token) continue;\n let weight = 1;\n for (const p of params) {\n const m = /^\\s*q=([0-9.]+)\\s*$/i.exec(p);\n if (m && m[1] !== undefined) weight = parseFloat(m[1]);\n }\n q.set(token, weight);\n }\n\n const wildcard = q.get('*');\n const accepts = (enc: Encoding): boolean => {\n if (q.has(enc)) return (q.get(enc) as number) > 0;\n if (wildcard !== undefined) return wildcard > 0;\n return false;\n };\n\n for (const enc of allowed) {\n if (accepts(enc)) return enc;\n }\n return null;\n}\n\n/** Compress a buffer/string with the chosen encoding. */\nexport async function compressBuffer(\n data: Buffer | string,\n encoding: Encoding,\n level = 6\n): Promise<Buffer> {\n const buf = typeof data === 'string' ? Buffer.from(data, 'utf8') : data;\n switch (encoding) {\n case 'br':\n return brotliAsync(buf, {\n params: {\n [z().constants.BROTLI_PARAM_QUALITY]: Math.max(\n 0,\n Math.min(11, Math.round((level * 11) / 9))\n ),\n [z().constants.BROTLI_PARAM_SIZE_HINT]: buf.length,\n },\n });\n case 'gzip':\n return gzipAsync(buf, { level });\n case 'deflate':\n return deflateAsync(buf, { level });\n }\n}\n\nexport interface MaybeCompressResult {\n body: Buffer | string;\n encoding: Encoding | null;\n}\n\n/**\n * The one-call helper the servers use: decide whether to compress `body`\n * given the response content type, the client's Accept-Encoding, and the\n * server's compression settings. Returns the (possibly compressed) body plus\n * the chosen encoding (null = sent as-is). Never throws — a compression error\n * degrades to the original body.\n */\nexport async function maybeCompress(\n body: Buffer | string,\n contentType: string | undefined,\n acceptEncoding: string | undefined,\n settings: CompressionSettings\n): Promise<MaybeCompressResult> {\n if (!settings.enabled) return { body, encoding: null };\n const byteLen = typeof body === 'string' ? Buffer.byteLength(body) : body.length;\n if (byteLen < settings.threshold) return { body, encoding: null };\n if (!isCompressible(contentType)) return { body, encoding: null };\n\n const encoding = negotiateEncoding(acceptEncoding, settings.encodings);\n if (!encoding) return { body, encoding: null };\n\n try {\n const compressed = await compressBuffer(body, encoding, settings.level);\n return { body: compressed, encoding };\n } catch {\n return { body, encoding: null };\n }\n}\n"]}
@@ -7,7 +7,9 @@
7
7
  // through JavaScript APIs. These warnings are harmless - the process will still exit cleanly.
8
8
  // To suppress these warnings, you can run Node.js with: --no-warnings or
9
9
  // NODE_NO_WARNINGS=1 environment variable.
10
- import { Worker, isMainThread, threadId, parentPort } from 'worker_threads';
10
+ import { requireBuiltin } from '../../utilities/builtin.js';
11
+ // worker_threads is loaded on first use (core/utilities/builtin.ts).
12
+ const wt = () => requireBuiltin('worker_threads');
11
13
  import os from 'os';
12
14
  import { createFrameworkLogger } from '../../logger/index.js';
13
15
  import { loadNativeEngine } from '../../utilities/package-utils.js';
@@ -43,7 +45,7 @@ export class UWSWorkerClusterManager {
43
45
  return workerCount;
44
46
  }
45
47
  async startAcceptorAndWorkers(_appFactory) {
46
- if (!isMainThread) {
48
+ if (!wt().isMainThread) {
47
49
  throw new Error('startAcceptorAndWorkers can only be called from main thread');
48
50
  }
49
51
  try {
@@ -74,10 +76,10 @@ export class UWSWorkerClusterManager {
74
76
  this.acceptorApp.listen(port, (token) => {
75
77
  if (token) {
76
78
  this.acceptorListenSocket = token;
77
- logger.info(`Acceptor listening on port ${port} from thread ${threadId}`, 'Acceptor');
79
+ logger.info(`Acceptor listening on port ${port} from thread ${wt().threadId}`, 'Acceptor');
78
80
  }
79
81
  else {
80
- throw new Error(`Failed to listen on port ${port} from thread ${threadId}`);
82
+ throw new Error(`Failed to listen on port ${port} from thread ${wt().threadId}`);
81
83
  }
82
84
  });
83
85
  // Spawn workers
@@ -102,7 +104,7 @@ export class UWSWorkerClusterManager {
102
104
  reject(new Error('Cannot spawn worker: process.argv[1] (entry script) is unavailable'));
103
105
  return;
104
106
  }
105
- const worker = new Worker(scriptPath, {
107
+ const worker = new (wt().Worker)(scriptPath, {
106
108
  workerData: {
107
109
  isUWSWorker: true,
108
110
  workerIndex: index,
@@ -260,9 +262,10 @@ export class UWSWorkerClusterManager {
260
262
  process.on('SIGTERM', () => void gracefulShutdown());
261
263
  }
262
264
  static isUWSWorker() {
263
- return !isMainThread && process.env.UWS_WORKER_MODE === 'true';
265
+ return !wt().isMainThread && process.env.UWS_WORKER_MODE === 'true';
264
266
  }
265
267
  static async sendDescriptorToAcceptor(app) {
268
+ const { isMainThread, parentPort, threadId } = wt();
266
269
  if (isMainThread || !parentPort) {
267
270
  throw new Error('sendDescriptorToAcceptor can only be called from worker thread');
268
271
  }
@@ -288,6 +291,7 @@ export class UWSWorkerClusterManager {
288
291
  }
289
292
  }
290
293
  static setupWorkerShutdownHandler(closeCallback) {
294
+ const { parentPort, threadId } = wt();
291
295
  if (!parentPort) {
292
296
  return;
293
297
  }
@@ -1 +1 @@
1
- {"version":3,"file":"uws-worker-clustering.js","sourceRoot":"","sources":["../../../../src/core/http/utils/uws-worker-clustering.ts"],"names":[],"mappings":"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","sourcesContent":["// uWebSockets.js Worker Thread Clustering Implementation\n// Uses worker threads with acceptor pattern for maximum performance\n//\n// NOTE: When terminating, you may see \"uv_loop_close() while having open handles\" warnings\n// from Node.js. This is a known limitation when using uWebSockets.js with worker threads.\n// The uWS C++ addon maintains internal libuv poll handles that cannot be cleanly closed\n// through JavaScript APIs. These warnings are harmless - the process will still exit cleanly.\n// To suppress these warnings, you can run Node.js with: --no-warnings or\n// NODE_NO_WARNINGS=1 environment variable.\n\nimport { Worker, isMainThread, threadId, parentPort } from 'worker_threads';\nimport os from 'os';\nimport { createFrameworkLogger } from '../../logger/index.js';\nimport { loadNativeEngine } from '../../utilities/package-utils.js';\n\nconst logger = createFrameworkLogger('UWSClustering');\n\nexport interface UWSClusterConfig {\n workers?: number | 'auto';\n memoryPerWorkerGB?: number;\n port: number;\n host?: string;\n ssl?: {\n key_file_name?: string;\n cert_file_name?: string;\n passphrase?: string;\n };\n}\n\nexport class UWSWorkerClusterManager {\n private workers: Worker[] = [];\n private acceptorApp: any = null;\n private acceptorListenSocket: any = null;\n private uws: any = null;\n private config: UWSClusterConfig;\n private workerCount: number;\n private isShuttingDown = false;\n\n constructor(config: UWSClusterConfig) {\n this.config = config;\n this.workerCount = this.calculateWorkerCount();\n }\n\n private calculateWorkerCount(): number {\n let workerCount = this.config.workers || os.cpus().length;\n\n if (workerCount === 'auto') {\n const cpuCount = os.cpus().length;\n const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);\n\n let memoryPerWorkerGB = this.config.memoryPerWorkerGB;\n\n if (!memoryPerWorkerGB) {\n const headroomGB = 4;\n memoryPerWorkerGB = Math.max(0.5, Math.floor((totalMemoryGB - headroomGB) / cpuCount));\n }\n\n workerCount = Math.min(cpuCount, Math.floor(totalMemoryGB / memoryPerWorkerGB));\n\n logger.info(\n `Auto-calculated worker count: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB, ${memoryPerWorkerGB}GB per worker)`,\n 'WorkerCalculation'\n );\n } else if (typeof workerCount === 'number') {\n logger.info(`Using specified worker count: ${workerCount}`, 'WorkerCalculation');\n }\n\n return workerCount;\n }\n\n async startAcceptorAndWorkers(_appFactory: () => any): Promise<void> {\n if (!isMainThread) {\n throw new Error('startAcceptorAndWorkers can only be called from main thread');\n }\n\n try {\n // Worker clustering uses the uWS-shaped App/SSLApp + SO_REUSEPORT API,\n // so it needs uWebSockets.js specifically (not @morojs/engine).\n const engine = loadNativeEngine({ candidates: ['uWebSockets.js'] });\n if (!engine) {\n throw new Error('uWebSockets.js is not available for uWS worker clustering');\n }\n const uwsModule = engine.module;\n this.uws = uwsModule.default || uwsModule;\n\n // Create acceptor app - must match SSL config of worker apps\n const ssl = this.config.ssl;\n if (ssl && ssl.key_file_name && ssl.cert_file_name) {\n this.acceptorApp = this.uws.SSLApp({\n key_file_name: ssl.key_file_name,\n cert_file_name: ssl.cert_file_name,\n passphrase: ssl.passphrase,\n });\n logger.info('uWebSockets SSL acceptor app created', 'Acceptor');\n } else {\n this.acceptorApp = this.uws.App();\n logger.info('uWebSockets acceptor app created', 'Acceptor');\n }\n\n // Start listening on acceptor\n const port = this.config.port;\n this.acceptorApp.listen(port, (token: any) => {\n if (token) {\n this.acceptorListenSocket = token;\n logger.info(`Acceptor listening on port ${port} from thread ${threadId}`, 'Acceptor');\n } else {\n throw new Error(`Failed to listen on port ${port} from thread ${threadId}`);\n }\n });\n\n // Spawn workers\n logger.info(`Starting ${this.workerCount} worker threads`, 'Workers');\n\n for (let i = 0; i < this.workerCount; i++) {\n await this.spawnWorker(i);\n }\n\n // Setup graceful shutdown\n this.setupGracefulShutdown();\n } catch (error) {\n logger.error('Failed to start acceptor and workers', 'Startup', {\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n private async spawnWorker(index: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const scriptPath = process.argv[1];\n if (scriptPath === undefined) {\n reject(new Error('Cannot spawn worker: process.argv[1] (entry script) is unavailable'));\n return;\n }\n const worker = new Worker(scriptPath, {\n workerData: {\n isUWSWorker: true,\n workerIndex: index,\n config: this.config,\n },\n argv: process.argv.slice(2),\n env: {\n ...process.env,\n UWS_WORKER_MODE: 'true',\n UWS_WORKER_INDEX: String(index),\n },\n });\n\n let descriptorReceived = false;\n\n worker.on('message', (message: any) => {\n if (message.type === 'descriptor' && message.descriptor) {\n if (!this.acceptorApp) {\n logger.error('Acceptor app not initialized', 'WorkerSpawn');\n reject(new Error('Acceptor app not initialized'));\n return;\n }\n\n try {\n this.acceptorApp.addChildAppDescriptor(message.descriptor);\n descriptorReceived = true;\n logger.info(\n `Worker ${index} (thread ${message.threadId}) registered with acceptor`,\n 'WorkerSpawn'\n );\n resolve();\n } catch (error) {\n logger.error('Failed to add child app descriptor', 'WorkerSpawn', {\n error: error instanceof Error ? error.message : String(error),\n });\n reject(error);\n }\n } else if (message.type === 'error') {\n logger.error(`Worker ${index} initialization error: ${message.error}`, 'WorkerSpawn');\n reject(new Error(message.error));\n }\n });\n\n worker.on('error', (error: Error) => {\n logger.error(`Worker ${index} error`, 'Worker', {\n error: error.message,\n });\n if (!descriptorReceived) {\n reject(error);\n }\n });\n\n worker.on('exit', (code: number) => {\n // Worker threads don't have exitedAfterDisconnect like cluster workers\n // Restart on any non-zero exit code\n if (code !== 0) {\n logger.warn(`Worker ${index} died unexpectedly (code: ${code}). Restarting...`, 'Worker');\n\n // Remove from workers array\n const workerIdx = this.workers.indexOf(worker);\n if (workerIdx > -1) {\n this.workers.splice(workerIdx, 1);\n }\n\n // Restart worker\n this.spawnWorker(index).catch(error => {\n logger.error('Failed to restart worker', 'Worker', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n }\n });\n\n this.workers.push(worker);\n\n // Timeout for descriptor reception\n setTimeout(() => {\n if (!descriptorReceived) {\n logger.error(`Worker ${index} failed to send descriptor within timeout`, 'WorkerSpawn');\n void worker.terminate();\n reject(new Error(`Worker ${index} initialization timeout`));\n }\n }, 10000); // 10 second timeout\n });\n }\n\n private setupGracefulShutdown(): void {\n const gracefulShutdown = async () => {\n if (this.isShuttingDown) return;\n this.isShuttingDown = true;\n\n logger.info('Gracefully shutting down worker cluster...', 'Shutdown');\n\n // Close acceptor listen socket first\n if (this.acceptorListenSocket && this.uws) {\n try {\n this.uws.us_listen_socket_close(this.acceptorListenSocket);\n this.acceptorListenSocket = null;\n logger.info('Acceptor listen socket closed', 'Shutdown');\n } catch (error) {\n logger.error('Error closing acceptor socket', 'Shutdown', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Send shutdown message to all workers and wait for them to close\n const shutdownPromises = this.workers.map((worker, index) => {\n return new Promise<void>(resolve => {\n let workerExited = false;\n\n const timeout = setTimeout(() => {\n if (!workerExited) {\n logger.info(\n `Worker ${index} taking too long to exit, terminating forcefully`,\n 'Shutdown'\n );\n try {\n // Terminate is the last resort for stuck workers\n void worker.terminate();\n } catch {\n logger.error(`Error terminating worker ${index}`, 'Shutdown');\n }\n }\n resolve();\n }, 2000); // Give 2 seconds for graceful exit\n\n worker.once('exit', code => {\n workerExited = true;\n clearTimeout(timeout);\n if (code === 0) {\n logger.info(`Worker ${index} exited cleanly`, 'Shutdown');\n } else if (code === 1) {\n logger.info(`Worker ${index} terminated (exit code ${code})`, 'Shutdown');\n } else {\n logger.warn(`Worker ${index} exited with unexpected code ${code}`, 'Shutdown');\n }\n resolve();\n });\n\n // Send shutdown message\n try {\n worker.postMessage({ type: 'shutdown' });\n } catch {\n logger.error(`Failed to send shutdown message to worker ${index}`, 'Shutdown');\n clearTimeout(timeout);\n try {\n void worker.terminate();\n } catch {\n // Ignore termination errors\n }\n resolve();\n }\n });\n });\n\n await Promise.all(shutdownPromises);\n logger.info('All workers shut down', 'Shutdown');\n\n // Clear acceptor app reference to allow uWebSockets to cleanup\n if (this.acceptorApp) {\n this.acceptorApp = null;\n }\n\n // Give uWebSockets event loop time to fully cleanup before exiting\n await new Promise(resolve => setTimeout(resolve, 200));\n\n logger.info('Shutdown complete', 'Shutdown');\n process.exit(0);\n };\n\n process.on('SIGINT', () => void gracefulShutdown());\n process.on('SIGTERM', () => void gracefulShutdown());\n }\n\n static isUWSWorker(): boolean {\n return !isMainThread && process.env.UWS_WORKER_MODE === 'true';\n }\n\n static async sendDescriptorToAcceptor(app: any): Promise<void> {\n if (isMainThread || !parentPort) {\n throw new Error('sendDescriptorToAcceptor can only be called from worker thread');\n }\n\n try {\n const descriptor = app.getDescriptor();\n\n parentPort.postMessage({\n type: 'descriptor',\n descriptor,\n threadId,\n });\n\n logger.info(`Worker thread ${threadId} sent descriptor to acceptor`, 'Worker');\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('Failed to send descriptor to acceptor', 'Worker', { error: errorMessage });\n\n if (parentPort) {\n parentPort.postMessage({\n type: 'error',\n error: errorMessage,\n });\n }\n\n throw error;\n }\n }\n\n static setupWorkerShutdownHandler(closeCallback: () => void | Promise<void>): void {\n if (!parentPort) {\n return;\n }\n\n parentPort.on('message', (message: any) => {\n void (async () => {\n if (message.type === 'shutdown') {\n logger.info(`Worker thread ${threadId} shutting down...`, 'Worker');\n\n try {\n // Call the close callback and wait if it's async\n const result = closeCallback();\n if (result && typeof result.then === 'function') {\n await result;\n }\n\n // Give uWebSockets event loop time to fully cleanup\n await new Promise(resolve => setTimeout(resolve, 150));\n\n // Close parent port to signal we're ready to exit\n if (parentPort) {\n parentPort.close();\n }\n\n logger.info(`Worker thread ${threadId} cleanup complete`, 'Worker');\n\n // DON'T call process.exit() - let Node.js exit naturally\n // This allows the event loop to properly drain all uWebSockets handles\n // The worker will exit on its own once all handles are closed\n } catch (error) {\n logger.error('Error during worker shutdown', 'Worker', {\n error: error instanceof Error ? error.message : String(error),\n });\n // Still don't force exit - let it drain naturally\n }\n }\n })();\n });\n }\n}\n"]}
1
+ {"version":3,"file":"uws-worker-clustering.js","sourceRoot":"","sources":["../../../../src/core/http/utils/uws-worker-clustering.ts"],"names":[],"mappings":"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","sourcesContent":["// uWebSockets.js Worker Thread Clustering Implementation\n// Uses worker threads with acceptor pattern for maximum performance\n//\n// NOTE: When terminating, you may see \"uv_loop_close() while having open handles\" warnings\n// from Node.js. This is a known limitation when using uWebSockets.js with worker threads.\n// The uWS C++ addon maintains internal libuv poll handles that cannot be cleanly closed\n// through JavaScript APIs. These warnings are harmless - the process will still exit cleanly.\n// To suppress these warnings, you can run Node.js with: --no-warnings or\n// NODE_NO_WARNINGS=1 environment variable.\n\nimport type { Worker } from 'worker_threads';\nimport { requireBuiltin } from '../../utilities/builtin.js';\n\n// worker_threads is loaded on first use (core/utilities/builtin.ts).\nconst wt = () => requireBuiltin<typeof import('worker_threads')>('worker_threads');\nimport os from 'os';\nimport { createFrameworkLogger } from '../../logger/index.js';\nimport { loadNativeEngine } from '../../utilities/package-utils.js';\n\nconst logger = createFrameworkLogger('UWSClustering');\n\nexport interface UWSClusterConfig {\n workers?: number | 'auto';\n memoryPerWorkerGB?: number;\n port: number;\n host?: string;\n ssl?: {\n key_file_name?: string;\n cert_file_name?: string;\n passphrase?: string;\n };\n}\n\nexport class UWSWorkerClusterManager {\n private workers: Worker[] = [];\n private acceptorApp: any = null;\n private acceptorListenSocket: any = null;\n private uws: any = null;\n private config: UWSClusterConfig;\n private workerCount: number;\n private isShuttingDown = false;\n\n constructor(config: UWSClusterConfig) {\n this.config = config;\n this.workerCount = this.calculateWorkerCount();\n }\n\n private calculateWorkerCount(): number {\n let workerCount = this.config.workers || os.cpus().length;\n\n if (workerCount === 'auto') {\n const cpuCount = os.cpus().length;\n const totalMemoryGB = os.totalmem() / (1024 * 1024 * 1024);\n\n let memoryPerWorkerGB = this.config.memoryPerWorkerGB;\n\n if (!memoryPerWorkerGB) {\n const headroomGB = 4;\n memoryPerWorkerGB = Math.max(0.5, Math.floor((totalMemoryGB - headroomGB) / cpuCount));\n }\n\n workerCount = Math.min(cpuCount, Math.floor(totalMemoryGB / memoryPerWorkerGB));\n\n logger.info(\n `Auto-calculated worker count: ${workerCount} (CPU: ${cpuCount}, RAM: ${totalMemoryGB.toFixed(1)}GB, ${memoryPerWorkerGB}GB per worker)`,\n 'WorkerCalculation'\n );\n } else if (typeof workerCount === 'number') {\n logger.info(`Using specified worker count: ${workerCount}`, 'WorkerCalculation');\n }\n\n return workerCount;\n }\n\n async startAcceptorAndWorkers(_appFactory: () => any): Promise<void> {\n if (!wt().isMainThread) {\n throw new Error('startAcceptorAndWorkers can only be called from main thread');\n }\n\n try {\n // Worker clustering uses the uWS-shaped App/SSLApp + SO_REUSEPORT API,\n // so it needs uWebSockets.js specifically (not @morojs/engine).\n const engine = loadNativeEngine({ candidates: ['uWebSockets.js'] });\n if (!engine) {\n throw new Error('uWebSockets.js is not available for uWS worker clustering');\n }\n const uwsModule = engine.module;\n this.uws = uwsModule.default || uwsModule;\n\n // Create acceptor app - must match SSL config of worker apps\n const ssl = this.config.ssl;\n if (ssl && ssl.key_file_name && ssl.cert_file_name) {\n this.acceptorApp = this.uws.SSLApp({\n key_file_name: ssl.key_file_name,\n cert_file_name: ssl.cert_file_name,\n passphrase: ssl.passphrase,\n });\n logger.info('uWebSockets SSL acceptor app created', 'Acceptor');\n } else {\n this.acceptorApp = this.uws.App();\n logger.info('uWebSockets acceptor app created', 'Acceptor');\n }\n\n // Start listening on acceptor\n const port = this.config.port;\n this.acceptorApp.listen(port, (token: any) => {\n if (token) {\n this.acceptorListenSocket = token;\n logger.info(\n `Acceptor listening on port ${port} from thread ${wt().threadId}`,\n 'Acceptor'\n );\n } else {\n throw new Error(`Failed to listen on port ${port} from thread ${wt().threadId}`);\n }\n });\n\n // Spawn workers\n logger.info(`Starting ${this.workerCount} worker threads`, 'Workers');\n\n for (let i = 0; i < this.workerCount; i++) {\n await this.spawnWorker(i);\n }\n\n // Setup graceful shutdown\n this.setupGracefulShutdown();\n } catch (error) {\n logger.error('Failed to start acceptor and workers', 'Startup', {\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n private async spawnWorker(index: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const scriptPath = process.argv[1];\n if (scriptPath === undefined) {\n reject(new Error('Cannot spawn worker: process.argv[1] (entry script) is unavailable'));\n return;\n }\n const worker = new (wt().Worker)(scriptPath, {\n workerData: {\n isUWSWorker: true,\n workerIndex: index,\n config: this.config,\n },\n argv: process.argv.slice(2),\n env: {\n ...process.env,\n UWS_WORKER_MODE: 'true',\n UWS_WORKER_INDEX: String(index),\n },\n });\n\n let descriptorReceived = false;\n\n worker.on('message', (message: any) => {\n if (message.type === 'descriptor' && message.descriptor) {\n if (!this.acceptorApp) {\n logger.error('Acceptor app not initialized', 'WorkerSpawn');\n reject(new Error('Acceptor app not initialized'));\n return;\n }\n\n try {\n this.acceptorApp.addChildAppDescriptor(message.descriptor);\n descriptorReceived = true;\n logger.info(\n `Worker ${index} (thread ${message.threadId}) registered with acceptor`,\n 'WorkerSpawn'\n );\n resolve();\n } catch (error) {\n logger.error('Failed to add child app descriptor', 'WorkerSpawn', {\n error: error instanceof Error ? error.message : String(error),\n });\n reject(error);\n }\n } else if (message.type === 'error') {\n logger.error(`Worker ${index} initialization error: ${message.error}`, 'WorkerSpawn');\n reject(new Error(message.error));\n }\n });\n\n worker.on('error', (error: Error) => {\n logger.error(`Worker ${index} error`, 'Worker', {\n error: error.message,\n });\n if (!descriptorReceived) {\n reject(error);\n }\n });\n\n worker.on('exit', (code: number) => {\n // Worker threads don't have exitedAfterDisconnect like cluster workers\n // Restart on any non-zero exit code\n if (code !== 0) {\n logger.warn(`Worker ${index} died unexpectedly (code: ${code}). Restarting...`, 'Worker');\n\n // Remove from workers array\n const workerIdx = this.workers.indexOf(worker);\n if (workerIdx > -1) {\n this.workers.splice(workerIdx, 1);\n }\n\n // Restart worker\n this.spawnWorker(index).catch(error => {\n logger.error('Failed to restart worker', 'Worker', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n }\n });\n\n this.workers.push(worker);\n\n // Timeout for descriptor reception\n setTimeout(() => {\n if (!descriptorReceived) {\n logger.error(`Worker ${index} failed to send descriptor within timeout`, 'WorkerSpawn');\n void worker.terminate();\n reject(new Error(`Worker ${index} initialization timeout`));\n }\n }, 10000); // 10 second timeout\n });\n }\n\n private setupGracefulShutdown(): void {\n const gracefulShutdown = async () => {\n if (this.isShuttingDown) return;\n this.isShuttingDown = true;\n\n logger.info('Gracefully shutting down worker cluster...', 'Shutdown');\n\n // Close acceptor listen socket first\n if (this.acceptorListenSocket && this.uws) {\n try {\n this.uws.us_listen_socket_close(this.acceptorListenSocket);\n this.acceptorListenSocket = null;\n logger.info('Acceptor listen socket closed', 'Shutdown');\n } catch (error) {\n logger.error('Error closing acceptor socket', 'Shutdown', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Send shutdown message to all workers and wait for them to close\n const shutdownPromises = this.workers.map((worker, index) => {\n return new Promise<void>(resolve => {\n let workerExited = false;\n\n const timeout = setTimeout(() => {\n if (!workerExited) {\n logger.info(\n `Worker ${index} taking too long to exit, terminating forcefully`,\n 'Shutdown'\n );\n try {\n // Terminate is the last resort for stuck workers\n void worker.terminate();\n } catch {\n logger.error(`Error terminating worker ${index}`, 'Shutdown');\n }\n }\n resolve();\n }, 2000); // Give 2 seconds for graceful exit\n\n worker.once('exit', code => {\n workerExited = true;\n clearTimeout(timeout);\n if (code === 0) {\n logger.info(`Worker ${index} exited cleanly`, 'Shutdown');\n } else if (code === 1) {\n logger.info(`Worker ${index} terminated (exit code ${code})`, 'Shutdown');\n } else {\n logger.warn(`Worker ${index} exited with unexpected code ${code}`, 'Shutdown');\n }\n resolve();\n });\n\n // Send shutdown message\n try {\n worker.postMessage({ type: 'shutdown' });\n } catch {\n logger.error(`Failed to send shutdown message to worker ${index}`, 'Shutdown');\n clearTimeout(timeout);\n try {\n void worker.terminate();\n } catch {\n // Ignore termination errors\n }\n resolve();\n }\n });\n });\n\n await Promise.all(shutdownPromises);\n logger.info('All workers shut down', 'Shutdown');\n\n // Clear acceptor app reference to allow uWebSockets to cleanup\n if (this.acceptorApp) {\n this.acceptorApp = null;\n }\n\n // Give uWebSockets event loop time to fully cleanup before exiting\n await new Promise(resolve => setTimeout(resolve, 200));\n\n logger.info('Shutdown complete', 'Shutdown');\n process.exit(0);\n };\n\n process.on('SIGINT', () => void gracefulShutdown());\n process.on('SIGTERM', () => void gracefulShutdown());\n }\n\n static isUWSWorker(): boolean {\n return !wt().isMainThread && process.env.UWS_WORKER_MODE === 'true';\n }\n\n static async sendDescriptorToAcceptor(app: any): Promise<void> {\n const { isMainThread, parentPort, threadId } = wt();\n if (isMainThread || !parentPort) {\n throw new Error('sendDescriptorToAcceptor can only be called from worker thread');\n }\n\n try {\n const descriptor = app.getDescriptor();\n\n parentPort.postMessage({\n type: 'descriptor',\n descriptor,\n threadId,\n });\n\n logger.info(`Worker thread ${threadId} sent descriptor to acceptor`, 'Worker');\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('Failed to send descriptor to acceptor', 'Worker', { error: errorMessage });\n\n if (parentPort) {\n parentPort.postMessage({\n type: 'error',\n error: errorMessage,\n });\n }\n\n throw error;\n }\n }\n\n static setupWorkerShutdownHandler(closeCallback: () => void | Promise<void>): void {\n const { parentPort, threadId } = wt();\n if (!parentPort) {\n return;\n }\n\n parentPort.on('message', (message: any) => {\n void (async () => {\n if (message.type === 'shutdown') {\n logger.info(`Worker thread ${threadId} shutting down...`, 'Worker');\n\n try {\n // Call the close callback and wait if it's async\n const result = closeCallback();\n if (result && typeof result.then === 'function') {\n await result;\n }\n\n // Give uWebSockets event loop time to fully cleanup\n await new Promise(resolve => setTimeout(resolve, 150));\n\n // Close parent port to signal we're ready to exit\n if (parentPort) {\n parentPort.close();\n }\n\n logger.info(`Worker thread ${threadId} cleanup complete`, 'Worker');\n\n // DON'T call process.exit() - let Node.js exit naturally\n // This allows the event loop to properly drain all uWebSockets handles\n // The worker will exit on its own once all handles are closed\n } catch (error) {\n logger.error('Error during worker shutdown', 'Worker', {\n error: error instanceof Error ? error.message : String(error),\n });\n // Still don't force exit - let it drain naturally\n }\n }\n })();\n });\n }\n}\n"]}
@@ -127,6 +127,7 @@ export declare class UWebSocketsHttpServer {
127
127
  private createMoroResponse;
128
128
  private readBody;
129
129
  private executeMiddleware;
130
+ private dispatchMiddleware;
130
131
  use(middleware: Middleware): void;
131
132
  setRequestTracking(enabled: boolean): void;
132
133
  setHookManager(hookManager: any): void;
@@ -1,6 +1,5 @@
1
1
  // uWebSockets.js HTTP Server Implementation for Moro Framework
2
2
  // Provides high-performance HTTP and WebSocket server using uWebSockets.js
3
- import cluster from 'cluster';
4
3
  import { randomUUID } from 'crypto';
5
4
  import { createFrameworkLogger } from '../logger/index.js';
6
5
  import { loadNativeEngine, getNativeEngineLoadErrors } from '../utilities/package-utils.js';
@@ -673,10 +672,14 @@ export class UWebSocketsHttpServer {
673
672
  // rejection). Registration of the response hooks above still stands.
674
673
  if (httpRes.headersSent)
675
674
  return;
676
- // Execute global middleware chain
675
+ // Execute global middleware chain. executeMiddleware returns undefined
676
+ // when the whole chain completed synchronously, so a sync chain costs
677
+ // zero promise allocations.
677
678
  if (this.globalMiddleware.length > 0) {
678
679
  httpReq.materialize();
679
- await this.executeMiddleware(this.globalMiddleware, httpReq, httpRes);
680
+ const mwResult = this.executeMiddleware(this.globalMiddleware, httpReq, httpRes);
681
+ if (mwResult)
682
+ await mwResult;
680
683
  if (httpRes.headersSent)
681
684
  return;
682
685
  }
@@ -1586,27 +1589,105 @@ export class UWebSocketsHttpServer {
1586
1589
  }
1587
1590
  });
1588
1591
  }
1589
- async executeMiddleware(middleware, req, res) {
1590
- for (const mw of middleware) {
1592
+ // Middleware execution with Express-compatible error propagation - identical
1593
+ // semantics to MoroHttpServer/MoroEngineServer so a route behaves the same on
1594
+ // every backend. Supports 3-arg (req, res, next), 4-arg error middleware
1595
+ // (err, req, res, next), and next(err) to skip forward to the next error
1596
+ // middleware. Previously this adapter called EVERY middleware with three
1597
+ // arguments, so registering a 4-arg error handler passed `req` as `err` and
1598
+ // broke every ordinary request; with no 4-arg middleware registered the
1599
+ // behaviour here is unchanged (next(err) aborts the chain and propagates to
1600
+ // the request handler's catch).
1601
+ //
1602
+ // Sync-aware dispatch: middleware that calls next() synchronously (or throws
1603
+ // synchronously) advances the chain in a plain loop with ZERO promise
1604
+ // allocations; a promise is only created when a middleware actually completes
1605
+ // asynchronously. Returns undefined when the whole chain ran synchronously so
1606
+ // the caller can skip its await.
1607
+ executeMiddleware(middleware, req, res) {
1608
+ return this.dispatchMiddleware(middleware, req, res, 0, undefined);
1609
+ }
1610
+ dispatchMiddleware(middleware, req, res, startIndex, initialError) {
1611
+ const len = middleware.length;
1612
+ let activeError = initialError;
1613
+ let i = startIndex;
1614
+ while (i < len) {
1591
1615
  if (res.headersSent)
1592
- break;
1593
- await new Promise((resolve, reject) => {
1594
- try {
1595
- const result = mw(req, res, (err) => {
1596
- if (err)
1597
- reject(err);
1598
- else
1599
- resolve();
1600
- });
1601
- // Handle async middleware
1602
- if (result && typeof result.then === 'function') {
1603
- result.then(() => resolve()).catch(reject);
1604
- }
1616
+ return;
1617
+ const mw = middleware[i];
1618
+ i++;
1619
+ const isErrorHandler = mw.length >= 4;
1620
+ // Non-error middleware is skipped while an error is active; error middleware
1621
+ // is skipped while no error is active. Matches Express semantics.
1622
+ if (activeError !== undefined && !isErrorHandler)
1623
+ continue;
1624
+ if (activeError === undefined && isErrorHandler)
1625
+ continue;
1626
+ let settled = false;
1627
+ let settledError = undefined;
1628
+ let asyncResolve;
1629
+ const next = (err) => {
1630
+ if (settled)
1631
+ return;
1632
+ settled = true;
1633
+ settledError = err;
1634
+ if (asyncResolve)
1635
+ asyncResolve(err);
1636
+ };
1637
+ let result;
1638
+ try {
1639
+ result = isErrorHandler ? mw(activeError, req, res, next) : mw(req, res, next);
1640
+ }
1641
+ catch (err) {
1642
+ if (!settled) {
1643
+ settled = true;
1644
+ settledError = err;
1605
1645
  }
1606
- catch (error) {
1607
- reject(error);
1646
+ }
1647
+ const isThenable = result && typeof result.then === 'function';
1648
+ if (settled) {
1649
+ // Completed synchronously (next() called sync, or threw sync). A still
1650
+ // pending returned promise no longer gates the chain once next() has
1651
+ // been called; swallow late rejections so they never go unhandled.
1652
+ if (isThenable) {
1653
+ result.then(undefined, () => { });
1608
1654
  }
1609
- });
1655
+ activeError = settledError;
1656
+ continue;
1657
+ }
1658
+ if (!isThenable && res.headersSent) {
1659
+ // Terminal middleware (Express style): it answered the request and
1660
+ // deliberately never calls next(). The chain is finished - skip
1661
+ // allocating a promise that nothing would ever settle.
1662
+ return;
1663
+ }
1664
+ if (!isThenable) {
1665
+ // Sync-looking middleware that neither called next() nor returned a
1666
+ // promise: it will call next() later (e.g. from an event callback).
1667
+ // Fall back to a promise for the remainder of the chain.
1668
+ const resumeIndex = i;
1669
+ return new Promise(resolve => {
1670
+ asyncResolve = resolve;
1671
+ }).then(err => this.dispatchMiddleware(middleware, req, res, resumeIndex, err));
1672
+ }
1673
+ // Async middleware: completion is next() OR promise settle, whichever
1674
+ // happens first (same semantics as the previous implementation).
1675
+ const resumeIndex = i;
1676
+ return new Promise(resolve => {
1677
+ asyncResolve = resolve;
1678
+ result.then(() => {
1679
+ if (!settled)
1680
+ next();
1681
+ }, (err) => {
1682
+ if (!settled)
1683
+ next(err);
1684
+ });
1685
+ }).then(err => this.dispatchMiddleware(middleware, req, res, resumeIndex, err));
1686
+ }
1687
+ // If an error is still unhandled after the chain, re-throw so the request
1688
+ // handler's catch can invoke the registered errorHandler / default 500.
1689
+ if (activeError !== undefined) {
1690
+ throw activeError;
1610
1691
  }
1611
1692
  }
1612
1693
  // Public API - matches MoroHttpServer interface
@@ -1662,7 +1743,9 @@ export class UWebSocketsHttpServer {
1662
1743
  this.logger.warn(`Native fast-path route registration failed, falling back to catchall: ${error instanceof Error ? error.message : String(error)}`, 'NativeRoutes');
1663
1744
  }
1664
1745
  // Check if we're in a cluster environment
1665
- const isClusterWorker = cluster.isWorker;
1746
+ // node:cluster's own definition of a worker, without loading node:cluster
1747
+ // (which drags child_process/dgram/net into every uWS app).
1748
+ const isClusterWorker = process.env.NODE_UNIQUE_ID !== undefined;
1666
1749
  // uWebSockets.js automatically enables SO_REUSEPORT when in cluster mode
1667
1750
  // Do NOT pass listenOptions in cluster mode - let uWS handle it automatically
1668
1751
  // In non-cluster mode, we don't need SO_REUSEPORT