@morojs/moro 1.8.12 → 1.8.14

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 (90) hide show
  1. package/README.md +4 -2
  2. package/dist/core/cluster/thread-cluster.d.ts +25 -0
  3. package/dist/core/cluster/thread-cluster.js +95 -3
  4. package/dist/core/cluster/thread-cluster.js.map +1 -1
  5. package/dist/core/config/config-validator.js +20 -0
  6. package/dist/core/config/config-validator.js.map +1 -1
  7. package/dist/core/config/index.js +19 -2
  8. package/dist/core/config/index.js.map +1 -1
  9. package/dist/core/config/schema.js +1 -0
  10. package/dist/core/config/schema.js.map +1 -1
  11. package/dist/core/framework.d.ts +26 -0
  12. package/dist/core/framework.js +195 -6
  13. package/dist/core/framework.js.map +1 -1
  14. package/dist/core/http/http-server.d.ts +19 -0
  15. package/dist/core/http/http-server.js +75 -1
  16. package/dist/core/http/http-server.js.map +1 -1
  17. package/dist/core/http/http2-server.d.ts +15 -0
  18. package/dist/core/http/http2-server.js +36 -3
  19. package/dist/core/http/http2-server.js.map +1 -1
  20. package/dist/core/http/moro-engine-server.d.ts +28 -0
  21. package/dist/core/http/moro-engine-server.js +138 -12
  22. package/dist/core/http/moro-engine-server.js.map +1 -1
  23. package/dist/core/http/utils/body-type.d.ts +12 -0
  24. package/dist/core/http/utils/body-type.js +70 -0
  25. package/dist/core/http/utils/body-type.js.map +1 -0
  26. package/dist/core/http/utils/compression.d.ts +21 -0
  27. package/dist/core/http/utils/compression.js +53 -7
  28. package/dist/core/http/utils/compression.js.map +1 -1
  29. package/dist/core/http/utils/ssl-config.d.ts +11 -0
  30. package/dist/core/http/utils/ssl-config.js +3 -0
  31. package/dist/core/http/utils/ssl-config.js.map +1 -1
  32. package/dist/core/http/utils/uws-worker-clustering.js +5 -4
  33. package/dist/core/http/utils/uws-worker-clustering.js.map +1 -1
  34. package/dist/core/http/uws-http-server.d.ts +3 -0
  35. package/dist/core/http/uws-http-server.js +47 -2
  36. package/dist/core/http/uws-http-server.js.map +1 -1
  37. package/dist/core/middleware/built-in/body-parsers/index.d.ts +18 -0
  38. package/dist/core/middleware/built-in/body-parsers/index.js +41 -0
  39. package/dist/core/middleware/built-in/body-parsers/index.js.map +1 -1
  40. package/dist/core/middleware/built-in/cache/adapters/cache/redis.d.ts +32 -8
  41. package/dist/core/middleware/built-in/cache/adapters/cache/redis.js +117 -25
  42. package/dist/core/middleware/built-in/cache/adapters/cache/redis.js.map +1 -1
  43. package/dist/core/middleware/built-in/compression/core.d.ts +7 -0
  44. package/dist/core/middleware/built-in/compression/core.js +32 -1
  45. package/dist/core/middleware/built-in/compression/core.js.map +1 -1
  46. package/dist/core/middleware/built-in/session/core.d.ts +1 -0
  47. package/dist/core/middleware/built-in/session/core.js.map +1 -1
  48. package/dist/core/middleware/built-in/static/core.d.ts +18 -0
  49. package/dist/core/middleware/built-in/static/core.js +128 -7
  50. package/dist/core/middleware/built-in/static/core.js.map +1 -1
  51. package/dist/core/networking/adapters/engine-adapter.d.ts +2 -2
  52. package/dist/core/networking/adapters/engine-adapter.js +40 -1
  53. package/dist/core/networking/adapters/engine-adapter.js.map +1 -1
  54. package/dist/core/networking/adapters/socketio-adapter.d.ts +2 -2
  55. package/dist/core/networking/adapters/socketio-adapter.js +10 -1
  56. package/dist/core/networking/adapters/socketio-adapter.js.map +1 -1
  57. package/dist/core/networking/adapters/uws-adapter.d.ts +2 -2
  58. package/dist/core/networking/adapters/uws-adapter.js +54 -3
  59. package/dist/core/networking/adapters/uws-adapter.js.map +1 -1
  60. package/dist/core/networking/adapters/ws-adapter.d.ts +9 -2
  61. package/dist/core/networking/adapters/ws-adapter.js +97 -12
  62. package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
  63. package/dist/core/networking/websocket-adapter.d.ts +19 -1
  64. package/dist/core/networking/websocket-adapter.js.map +1 -1
  65. package/dist/core/routing/index.d.ts +12 -8
  66. package/dist/core/routing/index.js +7 -2
  67. package/dist/core/routing/index.js.map +1 -1
  68. package/dist/core/routing/unified-router.d.ts +46 -8
  69. package/dist/core/routing/unified-router.js +108 -32
  70. package/dist/core/routing/unified-router.js.map +1 -1
  71. package/dist/core/utilities/package-utils.d.ts +8 -1
  72. package/dist/core/utilities/package-utils.js +2 -0
  73. package/dist/core/utilities/package-utils.js.map +1 -1
  74. package/dist/core/workers/worker-manager.js +2 -2
  75. package/dist/core/workers/worker-manager.js.map +1 -1
  76. package/dist/index.d.ts +4 -3
  77. package/dist/index.js +2 -1
  78. package/dist/index.js.map +1 -1
  79. package/dist/moro.d.ts +12 -1
  80. package/dist/moro.js +63 -7
  81. package/dist/moro.js.map +1 -1
  82. package/dist/types/cache.d.ts +2 -0
  83. package/dist/types/cache.js.map +1 -1
  84. package/dist/types/config.d.ts +16 -0
  85. package/dist/types/config.js.map +1 -1
  86. package/dist/types/http.d.ts +6 -0
  87. package/dist/types/http.js.map +1 -1
  88. package/dist/types/session.d.ts +1 -0
  89. package/dist/types/session.js.map +1 -1
  90. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../../src/core/middleware/built-in/session/core.ts"],"names":[],"mappings":"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","sourcesContent":["// Session Core - Reusable session management logic\nimport crypto from 'crypto';\nimport { createFrameworkLogger } from '../../../logger/index.js';\nimport { CacheAdapter } from '../../../../types/cache.js';\nimport { MemoryCacheAdapter } from '../cache/adapters/cache/memory.js';\nimport { RedisCacheAdapter } from '../cache/adapters/cache/redis.js';\nimport { FileCacheAdapter } from '../cache/adapters/cache/file.js';\nimport { HttpRequest, HttpResponse } from '../../../../types/http.js';\nimport { signCookieValue, unsignCookieValue, isSignedCookieValue } from '../cookie/core.js';\n\nconst logger = createFrameworkLogger('SessionCore');\n\n// ===== Types =====\n\nexport interface SessionOptions {\n // Session store configuration\n store?: 'memory' | 'redis' | 'file' | CacheAdapter;\n storeOptions?: {\n // Redis options\n host?: string;\n port?: number;\n password?: string;\n keyPrefix?: string;\n // File options\n path?: string;\n // Memory options\n max?: number;\n };\n\n // Session configuration\n secret?: string;\n name?: string; // Session cookie name\n genid?: () => string; // Session ID generator\n rolling?: boolean; // Reset expiry on each request\n resave?: boolean; // Save session even if not modified\n saveUninitialized?: boolean; // Save new but not modified sessions\n\n // Cookie configuration\n cookie?: {\n maxAge?: number; // Session timeout in ms\n expires?: Date; // Absolute expiry\n httpOnly?: boolean; // Prevent XSS access\n secure?: boolean; // HTTPS only\n sameSite?: 'strict' | 'lax' | 'none';\n domain?: string;\n path?: string;\n };\n\n // Security\n proxy?: boolean; // Trust proxy for secure cookies\n unset?: 'destroy' | 'keep'; // What to do when session is unset\n}\n\nexport interface SessionData {\n [key: string]: any;\n cookie?: {\n originalMaxAge?: number;\n expires?: Date;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: string;\n };\n}\n\n// ===== Session Class =====\n\nexport class Session {\n private data: SessionData = {};\n private id: string;\n private store: CacheAdapter;\n private options: SessionOptions;\n private isNew: boolean = false;\n private isModified: boolean = false;\n\n constructor(id: string, store: CacheAdapter, options: SessionOptions, isNew: boolean = false) {\n this.id = id;\n this.store = store;\n this.options = options;\n this.isNew = isNew;\n }\n\n // Proxy to make session.prop = value work\n static create(\n id: string,\n store: CacheAdapter,\n options: SessionOptions,\n data: SessionData = {},\n isNew: boolean = false\n ): Session {\n const session = new Session(id, store, options, isNew);\n session.data = data;\n\n return new Proxy(session, {\n get(target, prop) {\n if (prop in target) {\n return target[prop as keyof Session];\n }\n return target.data[prop as string];\n },\n\n set(target, prop, value) {\n if (prop in target) {\n (target as any)[prop] = value;\n } else {\n target.data[prop as string] = value;\n target.isModified = true;\n }\n return true;\n },\n\n has(target, prop) {\n return prop in target || prop in target.data;\n },\n\n deleteProperty(target, prop) {\n if (prop in target.data) {\n delete target.data[prop as string];\n target.isModified = true;\n return true;\n }\n return false;\n },\n });\n }\n\n async save(): Promise<void> {\n if (this.isModified || this.isNew || this.options.resave) {\n const ttl = this.options.cookie?.maxAge\n ? Math.floor(this.options.cookie.maxAge / 1000)\n : 86400; // 24h default\n await this.store.set(this.id, this.data, ttl);\n this.isModified = false;\n this.isNew = false;\n logger.debug(`Session saved: ${this.id}`, 'SessionSave');\n }\n }\n\n async destroy(): Promise<void> {\n await this.store.del(this.id);\n this.data = {};\n this.isModified = false;\n logger.debug(`Session destroyed: ${this.id}`, 'SessionDestroy');\n }\n\n async regenerate(): Promise<string> {\n await this.destroy();\n this.id = this.generateId();\n this.isNew = true;\n this.isModified = true;\n logger.debug(`Session regenerated: ${this.id}`, 'SessionRegenerate');\n return this.id;\n }\n\n async touch(): Promise<void> {\n if (this.options.rolling) {\n this.isModified = true;\n await this.save();\n }\n }\n\n private generateId(): string {\n if (this.options.genid) {\n return this.options.genid();\n }\n\n return crypto.randomBytes(24).toString('hex');\n }\n\n get sessionID(): string {\n return this.id;\n }\n}\n\n// ===== SessionCore =====\n\n/**\n * SessionCore - Core session management logic\n * Used directly by the router for route-based session handling\n * Can be instantiated for use in middleware or hooks\n */\nexport class SessionCore {\n private store: CacheAdapter;\n private options: SessionOptions;\n\n constructor(options: SessionOptions = {}) {\n this.options = {\n store: 'memory',\n name: 'connect.sid',\n rolling: false,\n resave: false,\n saveUninitialized: false,\n cookie: {\n maxAge: 24 * 60 * 60 * 1000, // 24 hours\n httpOnly: true,\n secure: false,\n sameSite: 'lax',\n path: '/',\n },\n unset: 'keep',\n ...options,\n };\n\n // Initialize store\n if (typeof this.options.store === 'string') {\n switch (this.options.store) {\n case 'redis':\n this.store = new RedisCacheAdapter({\n keyPrefix: 'sess:',\n ...this.options.storeOptions,\n });\n break;\n case 'file':\n this.store = new FileCacheAdapter({\n cacheDir: this.options.storeOptions?.path || './sessions',\n });\n break;\n case 'memory':\n default:\n this.store = new MemoryCacheAdapter();\n break;\n }\n } else {\n this.store = this.options.store as CacheAdapter;\n }\n }\n\n generateSessionId(): string {\n if (this.options.genid) {\n return this.options.genid();\n }\n return crypto.randomBytes(24).toString('hex');\n }\n\n async loadSession(sessionId: string): Promise<SessionData | null> {\n if (!sessionId) {\n return null;\n }\n\n try {\n const data = await this.store.get(sessionId);\n logger.debug(`Session loaded: ${sessionId}`, 'SessionLoad');\n return data || null;\n } catch (error) {\n logger.warn(`Failed to load session: ${sessionId}`, 'SessionLoadError', { error });\n return null;\n }\n }\n\n /** Cookie value carrying an id — signed when a secret is configured. */\n private encodeSessionId(id: string): string {\n return this.options.secret ? signCookieValue(id, this.options.secret) : id;\n }\n\n /**\n * The session id the request carries, or undefined when there is none and\n * when a signed id fails verification — a tampered id must start a fresh\n * session rather than load someone else's.\n */\n readSessionId(req: HttpRequest): string | undefined {\n const name = this.options.name || 'connect.sid';\n // The cookie middleware verifies and strips signatures when it shares the\n // secret, in which case the id arrives already unwrapped.\n const verified = (req as any).signedCookies?.[name] as string | undefined;\n const raw = verified ?? req.cookies?.[name];\n if (!raw) return undefined;\n\n const secret = this.options.secret;\n if (!secret) return raw;\n\n if (isSignedCookieValue(raw)) {\n return unsignCookieValue(raw, secret) ?? undefined;\n }\n\n // A bare value is only trustworthy if it was verified upstream\n return verified;\n }\n\n async createSession(req: HttpRequest, res: HttpResponse, sessionId?: string): Promise<Session> {\n const id = sessionId || this.generateSessionId();\n const session = Session.create(id, this.store, this.options, {}, true);\n\n // Set session cookie\n res.cookie(this.options.name || 'connect.sid', this.encodeSessionId(id), {\n ...this.options.cookie,\n secure: !!(\n this.options.cookie?.secure ||\n (this.options.proxy && req.headers['x-forwarded-proto'] === 'https')\n ),\n });\n\n return session;\n }\n\n async attachSession(req: HttpRequest, res: HttpResponse, sessionId?: string): Promise<Session> {\n let id = sessionId;\n let sessionData: SessionData = {};\n let isNew = false;\n\n if (id) {\n sessionData = (await this.loadSession(id)) || {};\n // Fast empty check without Object.keys\n if (!sessionData) {\n id = this.generateSessionId();\n isNew = true;\n } else {\n let hasData = false;\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n for (const _ in sessionData) {\n hasData = true;\n break;\n }\n if (!hasData) {\n id = this.generateSessionId();\n isNew = true;\n }\n }\n } else {\n id = this.generateSessionId();\n isNew = true;\n }\n\n const session = Session.create(id, this.store, this.options, sessionData, isNew);\n\n // Set session cookie if new or rolling\n if (isNew || this.options.rolling) {\n res.cookie(this.options.name || 'connect.sid', this.encodeSessionId(id), {\n ...this.options.cookie,\n secure: !!(\n this.options.cookie?.secure ||\n (this.options.proxy && req.headers['x-forwarded-proto'] === 'https')\n ),\n });\n }\n\n return session;\n }\n\n async saveSession(session: Session): Promise<void> {\n if (!session) {\n return;\n }\n\n try {\n if (\n this.options.saveUninitialized ||\n !(session as any).isNew ||\n (session as any).isModified\n ) {\n await session.save();\n }\n } catch (error) {\n logger.error('Failed to save session', 'SessionSaveError', { error });\n }\n }\n\n getStore(): CacheAdapter {\n return this.store;\n }\n\n getOptions(): SessionOptions {\n return this.options;\n }\n}\n"]}
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../../src/core/middleware/built-in/session/core.ts"],"names":[],"mappings":"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","sourcesContent":["// Session Core - Reusable session management logic\nimport crypto from 'crypto';\nimport { createFrameworkLogger } from '../../../logger/index.js';\nimport { CacheAdapter } from '../../../../types/cache.js';\nimport { MemoryCacheAdapter } from '../cache/adapters/cache/memory.js';\nimport { RedisCacheAdapter } from '../cache/adapters/cache/redis.js';\nimport { FileCacheAdapter } from '../cache/adapters/cache/file.js';\nimport { HttpRequest, HttpResponse } from '../../../../types/http.js';\nimport { signCookieValue, unsignCookieValue, isSignedCookieValue } from '../cookie/core.js';\n\nconst logger = createFrameworkLogger('SessionCore');\n\n// ===== Types =====\n\nexport interface SessionOptions {\n // Session store configuration\n store?: 'memory' | 'redis' | 'file' | CacheAdapter;\n storeOptions?: {\n // Redis options (node-redis 4+; url wins over host/port)\n url?: string;\n host?: string;\n port?: number;\n password?: string;\n keyPrefix?: string;\n // File options\n path?: string;\n // Memory options\n max?: number;\n };\n\n // Session configuration\n secret?: string;\n name?: string; // Session cookie name\n genid?: () => string; // Session ID generator\n rolling?: boolean; // Reset expiry on each request\n resave?: boolean; // Save session even if not modified\n saveUninitialized?: boolean; // Save new but not modified sessions\n\n // Cookie configuration\n cookie?: {\n maxAge?: number; // Session timeout in ms\n expires?: Date; // Absolute expiry\n httpOnly?: boolean; // Prevent XSS access\n secure?: boolean; // HTTPS only\n sameSite?: 'strict' | 'lax' | 'none';\n domain?: string;\n path?: string;\n };\n\n // Security\n proxy?: boolean; // Trust proxy for secure cookies\n unset?: 'destroy' | 'keep'; // What to do when session is unset\n}\n\nexport interface SessionData {\n [key: string]: any;\n cookie?: {\n originalMaxAge?: number;\n expires?: Date;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: string;\n };\n}\n\n// ===== Session Class =====\n\nexport class Session {\n private data: SessionData = {};\n private id: string;\n private store: CacheAdapter;\n private options: SessionOptions;\n private isNew: boolean = false;\n private isModified: boolean = false;\n\n constructor(id: string, store: CacheAdapter, options: SessionOptions, isNew: boolean = false) {\n this.id = id;\n this.store = store;\n this.options = options;\n this.isNew = isNew;\n }\n\n // Proxy to make session.prop = value work\n static create(\n id: string,\n store: CacheAdapter,\n options: SessionOptions,\n data: SessionData = {},\n isNew: boolean = false\n ): Session {\n const session = new Session(id, store, options, isNew);\n session.data = data;\n\n return new Proxy(session, {\n get(target, prop) {\n if (prop in target) {\n return target[prop as keyof Session];\n }\n return target.data[prop as string];\n },\n\n set(target, prop, value) {\n if (prop in target) {\n (target as any)[prop] = value;\n } else {\n target.data[prop as string] = value;\n target.isModified = true;\n }\n return true;\n },\n\n has(target, prop) {\n return prop in target || prop in target.data;\n },\n\n deleteProperty(target, prop) {\n if (prop in target.data) {\n delete target.data[prop as string];\n target.isModified = true;\n return true;\n }\n return false;\n },\n });\n }\n\n async save(): Promise<void> {\n if (this.isModified || this.isNew || this.options.resave) {\n const ttl = this.options.cookie?.maxAge\n ? Math.floor(this.options.cookie.maxAge / 1000)\n : 86400; // 24h default\n await this.store.set(this.id, this.data, ttl);\n this.isModified = false;\n this.isNew = false;\n logger.debug(`Session saved: ${this.id}`, 'SessionSave');\n }\n }\n\n async destroy(): Promise<void> {\n await this.store.del(this.id);\n this.data = {};\n this.isModified = false;\n logger.debug(`Session destroyed: ${this.id}`, 'SessionDestroy');\n }\n\n async regenerate(): Promise<string> {\n await this.destroy();\n this.id = this.generateId();\n this.isNew = true;\n this.isModified = true;\n logger.debug(`Session regenerated: ${this.id}`, 'SessionRegenerate');\n return this.id;\n }\n\n async touch(): Promise<void> {\n if (this.options.rolling) {\n this.isModified = true;\n await this.save();\n }\n }\n\n private generateId(): string {\n if (this.options.genid) {\n return this.options.genid();\n }\n\n return crypto.randomBytes(24).toString('hex');\n }\n\n get sessionID(): string {\n return this.id;\n }\n}\n\n// ===== SessionCore =====\n\n/**\n * SessionCore - Core session management logic\n * Used directly by the router for route-based session handling\n * Can be instantiated for use in middleware or hooks\n */\nexport class SessionCore {\n private store: CacheAdapter;\n private options: SessionOptions;\n\n constructor(options: SessionOptions = {}) {\n this.options = {\n store: 'memory',\n name: 'connect.sid',\n rolling: false,\n resave: false,\n saveUninitialized: false,\n cookie: {\n maxAge: 24 * 60 * 60 * 1000, // 24 hours\n httpOnly: true,\n secure: false,\n sameSite: 'lax',\n path: '/',\n },\n unset: 'keep',\n ...options,\n };\n\n // Initialize store\n if (typeof this.options.store === 'string') {\n switch (this.options.store) {\n case 'redis':\n this.store = new RedisCacheAdapter({\n keyPrefix: 'sess:',\n ...this.options.storeOptions,\n });\n break;\n case 'file':\n this.store = new FileCacheAdapter({\n cacheDir: this.options.storeOptions?.path || './sessions',\n });\n break;\n case 'memory':\n default:\n this.store = new MemoryCacheAdapter();\n break;\n }\n } else {\n this.store = this.options.store as CacheAdapter;\n }\n }\n\n generateSessionId(): string {\n if (this.options.genid) {\n return this.options.genid();\n }\n return crypto.randomBytes(24).toString('hex');\n }\n\n async loadSession(sessionId: string): Promise<SessionData | null> {\n if (!sessionId) {\n return null;\n }\n\n try {\n const data = await this.store.get(sessionId);\n logger.debug(`Session loaded: ${sessionId}`, 'SessionLoad');\n return data || null;\n } catch (error) {\n logger.warn(`Failed to load session: ${sessionId}`, 'SessionLoadError', { error });\n return null;\n }\n }\n\n /** Cookie value carrying an id — signed when a secret is configured. */\n private encodeSessionId(id: string): string {\n return this.options.secret ? signCookieValue(id, this.options.secret) : id;\n }\n\n /**\n * The session id the request carries, or undefined when there is none and\n * when a signed id fails verification — a tampered id must start a fresh\n * session rather than load someone else's.\n */\n readSessionId(req: HttpRequest): string | undefined {\n const name = this.options.name || 'connect.sid';\n // The cookie middleware verifies and strips signatures when it shares the\n // secret, in which case the id arrives already unwrapped.\n const verified = (req as any).signedCookies?.[name] as string | undefined;\n const raw = verified ?? req.cookies?.[name];\n if (!raw) return undefined;\n\n const secret = this.options.secret;\n if (!secret) return raw;\n\n if (isSignedCookieValue(raw)) {\n return unsignCookieValue(raw, secret) ?? undefined;\n }\n\n // A bare value is only trustworthy if it was verified upstream\n return verified;\n }\n\n async createSession(req: HttpRequest, res: HttpResponse, sessionId?: string): Promise<Session> {\n const id = sessionId || this.generateSessionId();\n const session = Session.create(id, this.store, this.options, {}, true);\n\n // Set session cookie\n res.cookie(this.options.name || 'connect.sid', this.encodeSessionId(id), {\n ...this.options.cookie,\n secure: !!(\n this.options.cookie?.secure ||\n (this.options.proxy && req.headers['x-forwarded-proto'] === 'https')\n ),\n });\n\n return session;\n }\n\n async attachSession(req: HttpRequest, res: HttpResponse, sessionId?: string): Promise<Session> {\n let id = sessionId;\n let sessionData: SessionData = {};\n let isNew = false;\n\n if (id) {\n sessionData = (await this.loadSession(id)) || {};\n // Fast empty check without Object.keys\n if (!sessionData) {\n id = this.generateSessionId();\n isNew = true;\n } else {\n let hasData = false;\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n for (const _ in sessionData) {\n hasData = true;\n break;\n }\n if (!hasData) {\n id = this.generateSessionId();\n isNew = true;\n }\n }\n } else {\n id = this.generateSessionId();\n isNew = true;\n }\n\n const session = Session.create(id, this.store, this.options, sessionData, isNew);\n\n // Set session cookie if new or rolling\n if (isNew || this.options.rolling) {\n res.cookie(this.options.name || 'connect.sid', this.encodeSessionId(id), {\n ...this.options.cookie,\n secure: !!(\n this.options.cookie?.secure ||\n (this.options.proxy && req.headers['x-forwarded-proto'] === 'https')\n ),\n });\n }\n\n return session;\n }\n\n async saveSession(session: Session): Promise<void> {\n if (!session) {\n return;\n }\n\n try {\n if (\n this.options.saveUninitialized ||\n !(session as any).isNew ||\n (session as any).isModified\n ) {\n await session.save();\n }\n } catch (error) {\n logger.error('Failed to save session', 'SessionSaveError', { error });\n }\n }\n\n getStore(): CacheAdapter {\n return this.store;\n }\n\n getOptions(): SessionOptions {\n return this.options;\n }\n}\n"]}
@@ -23,6 +23,15 @@ export interface StaticOptions {
23
23
  * multiple ranges is answered with the whole entity. Default true.
24
24
  */
25
25
  acceptRanges?: boolean;
26
+ /**
27
+ * Serve precompressed sidecars: when `app.js.br` or `app.js.gz` sits next
28
+ * to `app.js` and the client accepts that encoding, the sidecar is sent
29
+ * with the original's Content-Type, the matching Content-Encoding and
30
+ * `Vary: Accept-Encoding` (brotli preferred, then gzip). A sidecar older
31
+ * than its source is ignored, so a replaced file is never served stale.
32
+ * Range requests always get the identity file. Default false.
33
+ */
34
+ precompressed?: boolean;
26
35
  }
27
36
  export declare class StaticCore {
28
37
  private root;
@@ -33,11 +42,20 @@ export declare class StaticCore {
33
42
  private etag;
34
43
  private lastModified;
35
44
  private acceptRanges;
45
+ private precompressed;
36
46
  /** Files at or below this size are read in one go instead of streamed. */
37
47
  private static readonly STREAM_THRESHOLD;
38
48
  private mimeTypes;
39
49
  constructor(options: StaticOptions);
40
50
  handleRequest(req: HttpRequest, res: HttpResponse): Promise<boolean>;
51
+ /**
52
+ * The best precompressed sidecar for `filePath` that the client accepts, or
53
+ * null. A sidecar must be a regular file INSIDE the root (its real path is
54
+ * checked like the source's, so a symlinked `.br` cannot reach outside) and
55
+ * at least as new as the source, so a replaced asset never ships a stale
56
+ * compressed body.
57
+ */
58
+ private findSidecar;
41
59
  /**
42
60
  * Whether the client's cached copy is still current. `If-None-Match` wins
43
61
  * over `If-Modified-Since` when both are present (RFC 9110 13.1.3).
@@ -2,6 +2,12 @@ import * as fs from 'fs/promises';
2
2
  import { createReadStream } from 'fs';
3
3
  import * as path from 'path';
4
4
  import * as crypto from 'crypto';
5
+ import { isCompressible, negotiateEncoding } from '../../../http/utils/compression.js';
6
+ /** Sidecar suffix per encoding, in preference order. */
7
+ const SIDECARS = [
8
+ { encoding: 'br', suffix: '.br' },
9
+ { encoding: 'gzip', suffix: '.gz' },
10
+ ];
5
11
  export class StaticCore {
6
12
  root;
7
13
  prefix;
@@ -11,26 +17,68 @@ export class StaticCore {
11
17
  etag;
12
18
  lastModified;
13
19
  acceptRanges;
20
+ precompressed;
14
21
  /** Files at or below this size are read in one go instead of streamed. */
15
22
  static STREAM_THRESHOLD = 512 * 1024;
16
23
  mimeTypes = {
24
+ // Documents / text
17
25
  '.html': 'text/html',
26
+ '.htm': 'text/html',
27
+ '.xhtml': 'application/xhtml+xml',
18
28
  '.css': 'text/css',
19
29
  '.js': 'application/javascript',
30
+ '.mjs': 'application/javascript',
31
+ '.cjs': 'application/javascript',
32
+ '.map': 'application/json',
20
33
  '.json': 'application/json',
34
+ '.jsonld': 'application/ld+json',
35
+ '.webmanifest': 'application/manifest+json',
36
+ '.xml': 'application/xml',
37
+ '.txt': 'text/plain',
38
+ '.md': 'text/markdown',
39
+ '.csv': 'text/csv',
40
+ '.yaml': 'application/yaml',
41
+ '.yml': 'application/yaml',
42
+ '.pdf': 'application/pdf',
43
+ '.wasm': 'application/wasm',
44
+ // Images
21
45
  '.png': 'image/png',
46
+ '.apng': 'image/apng',
22
47
  '.jpg': 'image/jpeg',
23
48
  '.jpeg': 'image/jpeg',
24
49
  '.gif': 'image/gif',
50
+ '.webp': 'image/webp',
51
+ '.avif': 'image/avif',
25
52
  '.svg': 'image/svg+xml',
26
53
  '.ico': 'image/x-icon',
27
- '.pdf': 'application/pdf',
28
- '.txt': 'text/plain',
29
- '.xml': 'application/xml',
54
+ '.bmp': 'image/bmp',
55
+ '.tif': 'image/tiff',
56
+ '.tiff': 'image/tiff',
57
+ // Fonts
30
58
  '.woff': 'font/woff',
31
59
  '.woff2': 'font/woff2',
32
60
  '.ttf': 'font/ttf',
61
+ '.otf': 'font/otf',
33
62
  '.eot': 'application/vnd.ms-fontobject',
63
+ // Audio / video
64
+ '.mp3': 'audio/mpeg',
65
+ '.wav': 'audio/wav',
66
+ '.flac': 'audio/flac',
67
+ '.m4a': 'audio/mp4',
68
+ '.aac': 'audio/aac',
69
+ '.oga': 'audio/ogg',
70
+ '.opus': 'audio/ogg',
71
+ '.weba': 'audio/webm',
72
+ '.mp4': 'video/mp4',
73
+ '.m4v': 'video/mp4',
74
+ '.webm': 'video/webm',
75
+ '.ogg': 'video/ogg',
76
+ '.ogv': 'video/ogg',
77
+ '.mov': 'video/quicktime',
78
+ // Archives
79
+ '.zip': 'application/zip',
80
+ '.gz': 'application/gzip',
81
+ '.tar': 'application/x-tar',
34
82
  };
35
83
  constructor(options) {
36
84
  this.root = path.resolve(options.root);
@@ -41,6 +89,7 @@ export class StaticCore {
41
89
  this.etag = options.etag !== false;
42
90
  this.lastModified = options.lastModified !== false;
43
91
  this.acceptRanges = options.acceptRanges !== false;
92
+ this.precompressed = options.precompressed === true;
44
93
  }
45
94
  async handleRequest(req, res) {
46
95
  // Only handle GET and HEAD requests
@@ -119,6 +168,18 @@ export class StaticCore {
119
168
  const baseMimeType = this.mimeTypes[ext.toLowerCase()] || 'application/octet-stream';
120
169
  const contentType = this.addCharsetIfNeeded(baseMimeType);
121
170
  res.setHeader('Content-Type', contentType);
171
+ // Precompressed sidecar (app.js.br / app.js.gz next to app.js). Chosen
172
+ // before the validators so the ETag names the encoded representation;
173
+ // Range requests keep the identity file (byte offsets refer to it).
174
+ let sidecar = null;
175
+ if (this.precompressed) {
176
+ // The response now varies on Accept-Encoding whether or not a sidecar
177
+ // exists for this particular file - caches must key on it either way.
178
+ res.setHeader('Vary', 'Accept-Encoding');
179
+ if (!req.headers.range) {
180
+ sidecar = await this.findSidecar(filePath, stats.mtime, req.headers['accept-encoding']);
181
+ }
182
+ }
122
183
  // Cache headers
123
184
  if (this.maxAge > 0) {
124
185
  res.setHeader('Cache-Control', `public, max-age=${this.maxAge}`);
@@ -131,10 +192,12 @@ export class StaticCore {
131
192
  }
132
193
  let etag;
133
194
  if (this.etag) {
195
+ // A strong ETag must differ between representations, so the encoded
196
+ // sidecar gets its own (RFC 9110 8.8.1)
134
197
  etag = `"${crypto
135
198
  .createHash('md5')
136
199
  .update(`${stats.mtime.getTime()}-${stats.size}`)
137
- .digest('hex')}"`;
200
+ .digest('hex')}${sidecar ? `-${sidecar.encoding}` : ''}"`;
138
201
  res.setHeader('ETag', etag);
139
202
  }
140
203
  // Conditional requests. Content-Length is deliberately not set for a 304 —
@@ -167,9 +230,24 @@ export class StaticCore {
167
230
  }
168
231
  return true;
169
232
  }
170
- res.setHeader('Content-Length', stats.size);
233
+ if (sidecar) {
234
+ res.setHeader('Content-Encoding', sidecar.encoding);
235
+ res.setHeader('Content-Length', sidecar.stats.size);
236
+ if (req.method === 'HEAD') {
237
+ res.end();
238
+ return true;
239
+ }
240
+ if (sidecar.stats.size <= StaticCore.STREAM_THRESHOLD || !StaticCore.canStream(res)) {
241
+ res.end(await fs.readFile(sidecar.path));
242
+ }
243
+ else {
244
+ await this.streamFile(sidecar.path, res);
245
+ }
246
+ return true;
247
+ }
171
248
  // Handle HEAD requests
172
249
  if (req.method === 'HEAD') {
250
+ res.setHeader('Content-Length', stats.size);
173
251
  res.end();
174
252
  return true;
175
253
  }
@@ -178,9 +256,19 @@ export class StaticCore {
178
256
  // doesn't cost its full size in resident memory per request.
179
257
  if (stats.size <= StaticCore.STREAM_THRESHOLD || !StaticCore.canStream(res)) {
180
258
  const data = await fs.readFile(filePath);
181
- res.end(data);
259
+ if (isCompressible(baseMimeType) && typeof res.send === 'function') {
260
+ // Through send() the body joins the server's response compression
261
+ // (performance.compression / the compression middleware) exactly
262
+ // like a handler's body would; send() sets Content-Length itself.
263
+ res.send(data);
264
+ }
265
+ else {
266
+ res.setHeader('Content-Length', stats.size);
267
+ res.end(data);
268
+ }
182
269
  }
183
270
  else {
271
+ res.setHeader('Content-Length', stats.size);
184
272
  await this.streamFile(filePath, res);
185
273
  }
186
274
  return true;
@@ -190,6 +278,36 @@ export class StaticCore {
190
278
  return true;
191
279
  }
192
280
  }
281
+ /**
282
+ * The best precompressed sidecar for `filePath` that the client accepts, or
283
+ * null. A sidecar must be a regular file INSIDE the root (its real path is
284
+ * checked like the source's, so a symlinked `.br` cannot reach outside) and
285
+ * at least as new as the source, so a replaced asset never ships a stale
286
+ * compressed body.
287
+ */
288
+ async findSidecar(filePath, sourceMtime, acceptEncoding) {
289
+ if (!acceptEncoding)
290
+ return null;
291
+ const rootWithSep = this.root.endsWith(path.sep) ? this.root : this.root + path.sep;
292
+ for (const { encoding, suffix } of SIDECARS) {
293
+ if (negotiateEncoding(acceptEncoding, [encoding]) !== encoding)
294
+ continue;
295
+ const candidate = filePath + suffix;
296
+ try {
297
+ const real = await fs.realpath(candidate);
298
+ if (!real.startsWith(rootWithSep))
299
+ continue;
300
+ const st = await fs.stat(real);
301
+ if (!st.isFile() || st.mtime.getTime() < sourceMtime.getTime())
302
+ continue;
303
+ return { path: real, encoding, stats: { size: st.size } };
304
+ }
305
+ catch {
306
+ // no sidecar for this encoding
307
+ }
308
+ }
309
+ return null;
310
+ }
193
311
  /**
194
312
  * Whether the client's cached copy is still current. `If-None-Match` wins
195
313
  * over `If-Modified-Since` when both are present (RFC 9110 13.1.3).
@@ -344,9 +462,12 @@ export class StaticCore {
344
462
  'application/json',
345
463
  'application/javascript',
346
464
  'application/xml',
465
+ 'application/yaml',
347
466
  'image/svg+xml',
348
467
  ];
349
- const needsCharset = textTypes.some(type => mimeType.startsWith(type));
468
+ const needsCharset = textTypes.some(type => mimeType.startsWith(type)) ||
469
+ mimeType.endsWith('+json') ||
470
+ mimeType.endsWith('+xml');
350
471
  return needsCharset ? `${mimeType}; charset=utf-8` : mimeType;
351
472
  }
352
473
  getRoot() {
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../../src/core/middleware/built-in/static/core.ts"],"names":[],"mappings":"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","sourcesContent":["// Static File Serving Core Logic\nimport { HttpRequest, HttpResponse } from '../../../../types/http.js';\nimport * as fs from 'fs/promises';\nimport { createReadStream } from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\n\nexport interface StaticOptions {\n root: string;\n /**\n * URL path the files are served under, e.g. `/assets`. Requests outside the\n * prefix fall through untouched; inside it, the prefix is stripped before the\n * path is resolved against `root` (`/assets/app.css` -> `<root>/app.css`).\n * Matching is exact and case-sensitive. Defaults to the URL root.\n */\n prefix?: string;\n maxAge?: number;\n index?: string[];\n dotfiles?: 'allow' | 'deny' | 'ignore';\n etag?: boolean;\n /**\n * Send `Last-Modified` and answer `If-Modified-Since` with a 304.\n * Default true.\n */\n lastModified?: boolean;\n /**\n * Advertise `Accept-Ranges: bytes` and serve `Range` requests as 206\n * responses — what media seeking and resumable downloads need. A request for\n * multiple ranges is answered with the whole entity. Default true.\n */\n acceptRanges?: boolean;\n}\n\nexport class StaticCore {\n private root: string;\n private prefix: string;\n private maxAge: number;\n private index: string[];\n private dotfiles: 'allow' | 'deny' | 'ignore';\n private etag: boolean;\n private lastModified: boolean;\n private acceptRanges: boolean;\n\n /** Files at or below this size are read in one go instead of streamed. */\n private static readonly STREAM_THRESHOLD = 512 * 1024;\n\n private mimeTypes: Record<string, string> = {\n '.html': 'text/html',\n '.css': 'text/css',\n '.js': 'application/javascript',\n '.json': 'application/json',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.pdf': 'application/pdf',\n '.txt': 'text/plain',\n '.xml': 'application/xml',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.eot': 'application/vnd.ms-fontobject',\n };\n\n constructor(options: StaticOptions) {\n this.root = path.resolve(options.root);\n this.prefix = StaticCore.normalizePrefix(options.prefix);\n this.maxAge = options.maxAge || 0;\n this.index = options.index || ['index.html', 'index.htm'];\n this.dotfiles = options.dotfiles || 'ignore';\n this.etag = options.etag !== false;\n this.lastModified = options.lastModified !== false;\n this.acceptRanges = options.acceptRanges !== false;\n }\n\n async handleRequest(req: HttpRequest, res: HttpResponse): Promise<boolean> {\n // Only handle GET and HEAD requests\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n return false;\n }\n\n // Strip the mount prefix; anything outside it is not ours to serve\n const urlPath = this.stripPrefix(req.path);\n if (urlPath === null) {\n return false;\n }\n\n try {\n let filePath = path.join(this.root, urlPath);\n\n // Security: prevent directory traversal. The trailing separator is required —\n // a bare prefix check would allow sibling dirs like `/app/static-backups` to\n // satisfy `startsWith('/app/static')` and escape the root.\n const rootWithSep = this.root.endsWith(path.sep) ? this.root : this.root + path.sep;\n if (filePath !== this.root && !filePath.startsWith(rootWithSep)) {\n res.status(403).json({ success: false, error: 'Forbidden' });\n return true;\n }\n\n // Security: resolve symlinks and re-check path to prevent symlink-based traversal\n try {\n const realPath = await fs.realpath(filePath);\n if (realPath !== this.root && !realPath.startsWith(rootWithSep)) {\n res.status(403).json({ success: false, error: 'Forbidden' });\n return true;\n }\n filePath = realPath;\n } catch {\n // realpath fails if file doesn't exist — handled by stat below\n }\n\n // Handle dotfiles\n const basename = path.basename(filePath);\n if (basename.startsWith('.')) {\n if (this.dotfiles === 'deny') {\n res.status(403).json({ success: false, error: 'Forbidden' });\n return true;\n } else if (this.dotfiles === 'ignore') {\n return false;\n }\n }\n\n let stats;\n try {\n stats = await fs.stat(filePath);\n } catch {\n return false; // File not found, let other middleware handle\n }\n\n // Handle directories\n if (stats.isDirectory()) {\n let indexFound = false;\n\n for (const indexFile of this.index) {\n const indexPath = path.join(filePath, indexFile);\n try {\n const indexStats = await fs.stat(indexPath);\n if (indexStats.isFile()) {\n filePath = indexPath;\n stats = indexStats;\n indexFound = true;\n break;\n }\n } catch {\n // Continue to next index file\n }\n }\n\n if (!indexFound) {\n return false;\n }\n }\n\n // Get mime type and add charset for text files\n const ext = path.extname(filePath);\n const baseMimeType = this.mimeTypes[ext.toLowerCase()] || 'application/octet-stream';\n const contentType = this.addCharsetIfNeeded(baseMimeType);\n\n res.setHeader('Content-Type', contentType);\n\n // Cache headers\n if (this.maxAge > 0) {\n res.setHeader('Cache-Control', `public, max-age=${this.maxAge}`);\n }\n\n if (this.lastModified) {\n res.setHeader('Last-Modified', stats.mtime.toUTCString());\n }\n\n if (this.acceptRanges) {\n res.setHeader('Accept-Ranges', 'bytes');\n }\n\n let etag: string | undefined;\n if (this.etag) {\n etag = `\"${crypto\n .createHash('md5')\n .update(`${stats.mtime.getTime()}-${stats.size}`)\n .digest('hex')}\"`;\n res.setHeader('ETag', etag);\n }\n\n // Conditional requests. Content-Length is deliberately not set for a 304 —\n // the response carries no body.\n if (this.isNotModified(req, stats.mtime, etag)) {\n res.statusCode = 304;\n res.end();\n return true;\n }\n\n const range = this.acceptRanges ? this.resolveRange(req, stats, etag) : null;\n\n if (range === 'unsatisfiable') {\n res.setHeader('Content-Range', `bytes */${stats.size}`);\n res.status(416).json({ success: false, error: 'Range not satisfiable' });\n return true;\n }\n\n if (range) {\n const chunkSize = range.end - range.start + 1;\n res.statusCode = 206;\n res.setHeader('Content-Range', `bytes ${range.start}-${range.end}/${stats.size}`);\n res.setHeader('Content-Length', chunkSize);\n\n if (req.method === 'HEAD') {\n res.end();\n return true;\n }\n\n if (StaticCore.canStream(res)) {\n await this.streamFile(filePath, res, range);\n } else {\n res.end(await StaticCore.readSlice(filePath, range.start, chunkSize));\n }\n return true;\n }\n\n res.setHeader('Content-Length', stats.size);\n\n // Handle HEAD requests\n if (req.method === 'HEAD') {\n res.end();\n return true;\n }\n\n // Small files take the one-read/one-write path — the common case for\n // assets, and the cheapest. Larger ones stream, so serving a big file\n // doesn't cost its full size in resident memory per request.\n if (stats.size <= StaticCore.STREAM_THRESHOLD || !StaticCore.canStream(res)) {\n const data = await fs.readFile(filePath);\n res.end(data);\n } else {\n await this.streamFile(filePath, res);\n }\n return true;\n } catch {\n res.status(500).json({ success: false, error: 'Internal server error' });\n return true;\n }\n }\n\n /**\n * Whether the client's cached copy is still current. `If-None-Match` wins\n * over `If-Modified-Since` when both are present (RFC 9110 13.1.3).\n */\n private isNotModified(req: HttpRequest, mtime: Date, etag?: string): boolean {\n const ifNoneMatch = req.headers['if-none-match'];\n if (etag && ifNoneMatch) {\n if (ifNoneMatch === '*') return true;\n return ifNoneMatch\n .split(',')\n .some(candidate => candidate.trim().replace(/^W\\//, '') === etag);\n }\n\n if (this.lastModified) {\n const ifModifiedSince = req.headers['if-modified-since'];\n if (ifModifiedSince) {\n const since = Date.parse(ifModifiedSince);\n // HTTP dates carry second precision, so compare at that resolution\n if (!Number.isNaN(since)) {\n return Math.floor(mtime.getTime() / 1000) <= Math.floor(since / 1000);\n }\n }\n }\n\n return false;\n }\n\n /**\n * The byte range to serve, `'unsatisfiable'` for a 416, or null to send the\n * whole entity — which is also the answer for a multi-range request and for\n * an `If-Range` that no longer matches.\n */\n private resolveRange(\n req: HttpRequest,\n stats: { size: number; mtime: Date },\n etag?: string\n ): { start: number; end: number } | 'unsatisfiable' | null {\n const rangeHeader = req.headers.range;\n if (!rangeHeader) return null;\n\n const ifRange = req.headers['if-range'];\n if (ifRange) {\n const asDate = Date.parse(ifRange);\n const matches =\n (etag !== undefined && ifRange.trim() === etag) ||\n (!Number.isNaN(asDate) &&\n Math.floor(stats.mtime.getTime() / 1000) <= Math.floor(asDate / 1000));\n if (!matches) return null;\n }\n\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(rangeHeader.trim());\n if (!match) return null;\n\n const size = stats.size;\n const [, rawStart, rawEnd] = match;\n let start: number;\n let end: number;\n\n if (rawStart === '') {\n // Suffix form: the last N bytes\n const suffix = parseInt(rawEnd as string, 10);\n if (!suffix) return 'unsatisfiable';\n start = Math.max(size - suffix, 0);\n end = size - 1;\n } else {\n start = parseInt(rawStart as string, 10);\n end = rawEnd === '' ? size - 1 : Math.min(parseInt(rawEnd as string, 10), size - 1);\n }\n\n if (Number.isNaN(start) || Number.isNaN(end) || start > end || start >= size) {\n return 'unsatisfiable';\n }\n\n return { start, end };\n }\n\n /**\n * Pipe a file (or a slice of one) to the response, closing it on abort.\n * Resolves once the response is done — the caller must not return before\n * then, or the router would see an untouched response and 404 the request.\n */\n private streamFile(\n filePath: string,\n res: HttpResponse,\n range?: { start: number; end: number }\n ): Promise<void> {\n const stream = range\n ? createReadStream(filePath, { start: range.start, end: range.end })\n : createReadStream(filePath);\n\n return new Promise<void>(resolve => {\n let settled = false;\n const done = () => {\n if (settled) return;\n settled = true;\n resolve();\n };\n\n // A client that disconnects mid-download must not leak the descriptor\n (res as any).once?.('close', () => {\n stream.destroy();\n done();\n });\n\n stream.on('error', () => {\n if (!res.headersSent) {\n res.status(500).json({ success: false, error: 'Internal server error' });\n } else {\n res.end();\n }\n done();\n });\n\n stream.on('end', done);\n stream.pipe(res as any);\n });\n }\n\n /** Read just the requested slice — the fallback where piping isn't available. */\n private static async readSlice(filePath: string, start: number, length: number): Promise<Buffer> {\n const handle = await fs.open(filePath, 'r');\n try {\n const buffer = Buffer.allocUnsafe(length);\n const { bytesRead } = await handle.read(buffer, 0, length, start);\n return bytesRead === length ? buffer : buffer.subarray(0, bytesRead);\n } finally {\n await handle.close();\n }\n }\n\n /** HTTP/2 responses are plain objects with no stream plumbing to pipe into. */\n private static canStream(res: HttpResponse): boolean {\n const writable = res as any;\n return typeof writable.write === 'function' && typeof writable.on === 'function';\n }\n\n /**\n * `/assets/` -> `/assets`, `/` and `''` -> `''` (serve from the URL root).\n * A prefix given without a leading slash still mounts where you'd expect.\n */\n private static normalizePrefix(prefix?: string): string {\n if (!prefix) return '';\n let normalized = prefix.trim();\n if (!normalized.startsWith('/')) normalized = `/${normalized}`;\n while (normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n return normalized === '/' ? '' : normalized;\n }\n\n /**\n * Request path relative to the mount point, or `null` when the request falls\n * outside it. `/assets` itself maps to `/` so the directory index still\n * resolves; `/assetsfoo` is a different route and is left alone.\n */\n private stripPrefix(reqPath: string): string | null {\n if (!this.prefix) return reqPath;\n if (reqPath === this.prefix) return '/';\n if (reqPath.startsWith(`${this.prefix}/`)) return reqPath.slice(this.prefix.length);\n return null;\n }\n\n private addCharsetIfNeeded(mimeType: string): string {\n const textTypes = [\n 'text/',\n 'application/json',\n 'application/javascript',\n 'application/xml',\n 'image/svg+xml',\n ];\n const needsCharset = textTypes.some(type => mimeType.startsWith(type));\n return needsCharset ? `${mimeType}; charset=utf-8` : mimeType;\n }\n\n getRoot(): string {\n return this.root;\n }\n\n getPrefix(): string {\n return this.prefix;\n }\n}\n"]}
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../../src/core/middleware/built-in/static/core.ts"],"names":[],"mappings":"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","sourcesContent":["// Static File Serving Core Logic\nimport { HttpRequest, HttpResponse } from '../../../../types/http.js';\nimport * as fs from 'fs/promises';\nimport { createReadStream } from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport { isCompressible, negotiateEncoding } from '../../../http/utils/compression.js';\n\nexport interface StaticOptions {\n root: string;\n /**\n * URL path the files are served under, e.g. `/assets`. Requests outside the\n * prefix fall through untouched; inside it, the prefix is stripped before the\n * path is resolved against `root` (`/assets/app.css` -> `<root>/app.css`).\n * Matching is exact and case-sensitive. Defaults to the URL root.\n */\n prefix?: string;\n maxAge?: number;\n index?: string[];\n dotfiles?: 'allow' | 'deny' | 'ignore';\n etag?: boolean;\n /**\n * Send `Last-Modified` and answer `If-Modified-Since` with a 304.\n * Default true.\n */\n lastModified?: boolean;\n /**\n * Advertise `Accept-Ranges: bytes` and serve `Range` requests as 206\n * responses — what media seeking and resumable downloads need. A request for\n * multiple ranges is answered with the whole entity. Default true.\n */\n acceptRanges?: boolean;\n /**\n * Serve precompressed sidecars: when `app.js.br` or `app.js.gz` sits next\n * to `app.js` and the client accepts that encoding, the sidecar is sent\n * with the original's Content-Type, the matching Content-Encoding and\n * `Vary: Accept-Encoding` (brotli preferred, then gzip). A sidecar older\n * than its source is ignored, so a replaced file is never served stale.\n * Range requests always get the identity file. Default false.\n */\n precompressed?: boolean;\n}\n\n/** Sidecar suffix per encoding, in preference order. */\nconst SIDECARS: Array<{ encoding: 'br' | 'gzip'; suffix: string }> = [\n { encoding: 'br', suffix: '.br' },\n { encoding: 'gzip', suffix: '.gz' },\n];\n\nexport class StaticCore {\n private root: string;\n private prefix: string;\n private maxAge: number;\n private index: string[];\n private dotfiles: 'allow' | 'deny' | 'ignore';\n private etag: boolean;\n private lastModified: boolean;\n private acceptRanges: boolean;\n private precompressed: boolean;\n\n /** Files at or below this size are read in one go instead of streamed. */\n private static readonly STREAM_THRESHOLD = 512 * 1024;\n\n private mimeTypes: Record<string, string> = {\n // Documents / text\n '.html': 'text/html',\n '.htm': 'text/html',\n '.xhtml': 'application/xhtml+xml',\n '.css': 'text/css',\n '.js': 'application/javascript',\n '.mjs': 'application/javascript',\n '.cjs': 'application/javascript',\n '.map': 'application/json',\n '.json': 'application/json',\n '.jsonld': 'application/ld+json',\n '.webmanifest': 'application/manifest+json',\n '.xml': 'application/xml',\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.csv': 'text/csv',\n '.yaml': 'application/yaml',\n '.yml': 'application/yaml',\n '.pdf': 'application/pdf',\n '.wasm': 'application/wasm',\n // Images\n '.png': 'image/png',\n '.apng': 'image/apng',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.avif': 'image/avif',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.bmp': 'image/bmp',\n '.tif': 'image/tiff',\n '.tiff': 'image/tiff',\n // Fonts\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.otf': 'font/otf',\n '.eot': 'application/vnd.ms-fontobject',\n // Audio / video\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.flac': 'audio/flac',\n '.m4a': 'audio/mp4',\n '.aac': 'audio/aac',\n '.oga': 'audio/ogg',\n '.opus': 'audio/ogg',\n '.weba': 'audio/webm',\n '.mp4': 'video/mp4',\n '.m4v': 'video/mp4',\n '.webm': 'video/webm',\n '.ogg': 'video/ogg',\n '.ogv': 'video/ogg',\n '.mov': 'video/quicktime',\n // Archives\n '.zip': 'application/zip',\n '.gz': 'application/gzip',\n '.tar': 'application/x-tar',\n };\n\n constructor(options: StaticOptions) {\n this.root = path.resolve(options.root);\n this.prefix = StaticCore.normalizePrefix(options.prefix);\n this.maxAge = options.maxAge || 0;\n this.index = options.index || ['index.html', 'index.htm'];\n this.dotfiles = options.dotfiles || 'ignore';\n this.etag = options.etag !== false;\n this.lastModified = options.lastModified !== false;\n this.acceptRanges = options.acceptRanges !== false;\n this.precompressed = options.precompressed === true;\n }\n\n async handleRequest(req: HttpRequest, res: HttpResponse): Promise<boolean> {\n // Only handle GET and HEAD requests\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n return false;\n }\n\n // Strip the mount prefix; anything outside it is not ours to serve\n const urlPath = this.stripPrefix(req.path);\n if (urlPath === null) {\n return false;\n }\n\n try {\n let filePath = path.join(this.root, urlPath);\n\n // Security: prevent directory traversal. The trailing separator is required —\n // a bare prefix check would allow sibling dirs like `/app/static-backups` to\n // satisfy `startsWith('/app/static')` and escape the root.\n const rootWithSep = this.root.endsWith(path.sep) ? this.root : this.root + path.sep;\n if (filePath !== this.root && !filePath.startsWith(rootWithSep)) {\n res.status(403).json({ success: false, error: 'Forbidden' });\n return true;\n }\n\n // Security: resolve symlinks and re-check path to prevent symlink-based traversal\n try {\n const realPath = await fs.realpath(filePath);\n if (realPath !== this.root && !realPath.startsWith(rootWithSep)) {\n res.status(403).json({ success: false, error: 'Forbidden' });\n return true;\n }\n filePath = realPath;\n } catch {\n // realpath fails if file doesn't exist — handled by stat below\n }\n\n // Handle dotfiles\n const basename = path.basename(filePath);\n if (basename.startsWith('.')) {\n if (this.dotfiles === 'deny') {\n res.status(403).json({ success: false, error: 'Forbidden' });\n return true;\n } else if (this.dotfiles === 'ignore') {\n return false;\n }\n }\n\n let stats;\n try {\n stats = await fs.stat(filePath);\n } catch {\n return false; // File not found, let other middleware handle\n }\n\n // Handle directories\n if (stats.isDirectory()) {\n let indexFound = false;\n\n for (const indexFile of this.index) {\n const indexPath = path.join(filePath, indexFile);\n try {\n const indexStats = await fs.stat(indexPath);\n if (indexStats.isFile()) {\n filePath = indexPath;\n stats = indexStats;\n indexFound = true;\n break;\n }\n } catch {\n // Continue to next index file\n }\n }\n\n if (!indexFound) {\n return false;\n }\n }\n\n // Get mime type and add charset for text files\n const ext = path.extname(filePath);\n const baseMimeType = this.mimeTypes[ext.toLowerCase()] || 'application/octet-stream';\n const contentType = this.addCharsetIfNeeded(baseMimeType);\n\n res.setHeader('Content-Type', contentType);\n\n // Precompressed sidecar (app.js.br / app.js.gz next to app.js). Chosen\n // before the validators so the ETag names the encoded representation;\n // Range requests keep the identity file (byte offsets refer to it).\n let sidecar: { path: string; encoding: string; stats: { size: number } } | null = null;\n if (this.precompressed) {\n // The response now varies on Accept-Encoding whether or not a sidecar\n // exists for this particular file - caches must key on it either way.\n res.setHeader('Vary', 'Accept-Encoding');\n if (!req.headers.range) {\n sidecar = await this.findSidecar(filePath, stats.mtime, req.headers['accept-encoding']);\n }\n }\n\n // Cache headers\n if (this.maxAge > 0) {\n res.setHeader('Cache-Control', `public, max-age=${this.maxAge}`);\n }\n\n if (this.lastModified) {\n res.setHeader('Last-Modified', stats.mtime.toUTCString());\n }\n\n if (this.acceptRanges) {\n res.setHeader('Accept-Ranges', 'bytes');\n }\n\n let etag: string | undefined;\n if (this.etag) {\n // A strong ETag must differ between representations, so the encoded\n // sidecar gets its own (RFC 9110 8.8.1)\n etag = `\"${crypto\n .createHash('md5')\n .update(`${stats.mtime.getTime()}-${stats.size}`)\n .digest('hex')}${sidecar ? `-${sidecar.encoding}` : ''}\"`;\n res.setHeader('ETag', etag);\n }\n\n // Conditional requests. Content-Length is deliberately not set for a 304 —\n // the response carries no body.\n if (this.isNotModified(req, stats.mtime, etag)) {\n res.statusCode = 304;\n res.end();\n return true;\n }\n\n const range = this.acceptRanges ? this.resolveRange(req, stats, etag) : null;\n\n if (range === 'unsatisfiable') {\n res.setHeader('Content-Range', `bytes */${stats.size}`);\n res.status(416).json({ success: false, error: 'Range not satisfiable' });\n return true;\n }\n\n if (range) {\n const chunkSize = range.end - range.start + 1;\n res.statusCode = 206;\n res.setHeader('Content-Range', `bytes ${range.start}-${range.end}/${stats.size}`);\n res.setHeader('Content-Length', chunkSize);\n\n if (req.method === 'HEAD') {\n res.end();\n return true;\n }\n\n if (StaticCore.canStream(res)) {\n await this.streamFile(filePath, res, range);\n } else {\n res.end(await StaticCore.readSlice(filePath, range.start, chunkSize));\n }\n return true;\n }\n\n if (sidecar) {\n res.setHeader('Content-Encoding', sidecar.encoding);\n res.setHeader('Content-Length', sidecar.stats.size);\n if (req.method === 'HEAD') {\n res.end();\n return true;\n }\n if (sidecar.stats.size <= StaticCore.STREAM_THRESHOLD || !StaticCore.canStream(res)) {\n res.end(await fs.readFile(sidecar.path));\n } else {\n await this.streamFile(sidecar.path, res);\n }\n return true;\n }\n\n // Handle HEAD requests\n if (req.method === 'HEAD') {\n res.setHeader('Content-Length', stats.size);\n res.end();\n return true;\n }\n\n // Small files take the one-read/one-write path — the common case for\n // assets, and the cheapest. Larger ones stream, so serving a big file\n // doesn't cost its full size in resident memory per request.\n if (stats.size <= StaticCore.STREAM_THRESHOLD || !StaticCore.canStream(res)) {\n const data = await fs.readFile(filePath);\n if (isCompressible(baseMimeType) && typeof (res as any).send === 'function') {\n // Through send() the body joins the server's response compression\n // (performance.compression / the compression middleware) exactly\n // like a handler's body would; send() sets Content-Length itself.\n (res as any).send(data);\n } else {\n res.setHeader('Content-Length', stats.size);\n res.end(data);\n }\n } else {\n res.setHeader('Content-Length', stats.size);\n await this.streamFile(filePath, res);\n }\n return true;\n } catch {\n res.status(500).json({ success: false, error: 'Internal server error' });\n return true;\n }\n }\n\n /**\n * The best precompressed sidecar for `filePath` that the client accepts, or\n * null. A sidecar must be a regular file INSIDE the root (its real path is\n * checked like the source's, so a symlinked `.br` cannot reach outside) and\n * at least as new as the source, so a replaced asset never ships a stale\n * compressed body.\n */\n private async findSidecar(\n filePath: string,\n sourceMtime: Date,\n acceptEncoding: string | undefined\n ): Promise<{ path: string; encoding: string; stats: { size: number } } | null> {\n if (!acceptEncoding) return null;\n const rootWithSep = this.root.endsWith(path.sep) ? this.root : this.root + path.sep;\n for (const { encoding, suffix } of SIDECARS) {\n if (negotiateEncoding(acceptEncoding, [encoding]) !== encoding) continue;\n const candidate = filePath + suffix;\n try {\n const real = await fs.realpath(candidate);\n if (!real.startsWith(rootWithSep)) continue;\n const st = await fs.stat(real);\n if (!st.isFile() || st.mtime.getTime() < sourceMtime.getTime()) continue;\n return { path: real, encoding, stats: { size: st.size } };\n } catch {\n // no sidecar for this encoding\n }\n }\n return null;\n }\n\n /**\n * Whether the client's cached copy is still current. `If-None-Match` wins\n * over `If-Modified-Since` when both are present (RFC 9110 13.1.3).\n */\n private isNotModified(req: HttpRequest, mtime: Date, etag?: string): boolean {\n const ifNoneMatch = req.headers['if-none-match'];\n if (etag && ifNoneMatch) {\n if (ifNoneMatch === '*') return true;\n return ifNoneMatch\n .split(',')\n .some(candidate => candidate.trim().replace(/^W\\//, '') === etag);\n }\n\n if (this.lastModified) {\n const ifModifiedSince = req.headers['if-modified-since'];\n if (ifModifiedSince) {\n const since = Date.parse(ifModifiedSince);\n // HTTP dates carry second precision, so compare at that resolution\n if (!Number.isNaN(since)) {\n return Math.floor(mtime.getTime() / 1000) <= Math.floor(since / 1000);\n }\n }\n }\n\n return false;\n }\n\n /**\n * The byte range to serve, `'unsatisfiable'` for a 416, or null to send the\n * whole entity — which is also the answer for a multi-range request and for\n * an `If-Range` that no longer matches.\n */\n private resolveRange(\n req: HttpRequest,\n stats: { size: number; mtime: Date },\n etag?: string\n ): { start: number; end: number } | 'unsatisfiable' | null {\n const rangeHeader = req.headers.range;\n if (!rangeHeader) return null;\n\n const ifRange = req.headers['if-range'];\n if (ifRange) {\n const asDate = Date.parse(ifRange);\n const matches =\n (etag !== undefined && ifRange.trim() === etag) ||\n (!Number.isNaN(asDate) &&\n Math.floor(stats.mtime.getTime() / 1000) <= Math.floor(asDate / 1000));\n if (!matches) return null;\n }\n\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(rangeHeader.trim());\n if (!match) return null;\n\n const size = stats.size;\n const [, rawStart, rawEnd] = match;\n let start: number;\n let end: number;\n\n if (rawStart === '') {\n // Suffix form: the last N bytes\n const suffix = parseInt(rawEnd as string, 10);\n if (!suffix) return 'unsatisfiable';\n start = Math.max(size - suffix, 0);\n end = size - 1;\n } else {\n start = parseInt(rawStart as string, 10);\n end = rawEnd === '' ? size - 1 : Math.min(parseInt(rawEnd as string, 10), size - 1);\n }\n\n if (Number.isNaN(start) || Number.isNaN(end) || start > end || start >= size) {\n return 'unsatisfiable';\n }\n\n return { start, end };\n }\n\n /**\n * Pipe a file (or a slice of one) to the response, closing it on abort.\n * Resolves once the response is done — the caller must not return before\n * then, or the router would see an untouched response and 404 the request.\n */\n private streamFile(\n filePath: string,\n res: HttpResponse,\n range?: { start: number; end: number }\n ): Promise<void> {\n const stream = range\n ? createReadStream(filePath, { start: range.start, end: range.end })\n : createReadStream(filePath);\n\n return new Promise<void>(resolve => {\n let settled = false;\n const done = () => {\n if (settled) return;\n settled = true;\n resolve();\n };\n\n // A client that disconnects mid-download must not leak the descriptor\n (res as any).once?.('close', () => {\n stream.destroy();\n done();\n });\n\n stream.on('error', () => {\n if (!res.headersSent) {\n res.status(500).json({ success: false, error: 'Internal server error' });\n } else {\n res.end();\n }\n done();\n });\n\n stream.on('end', done);\n stream.pipe(res as any);\n });\n }\n\n /** Read just the requested slice — the fallback where piping isn't available. */\n private static async readSlice(filePath: string, start: number, length: number): Promise<Buffer> {\n const handle = await fs.open(filePath, 'r');\n try {\n const buffer = Buffer.allocUnsafe(length);\n const { bytesRead } = await handle.read(buffer, 0, length, start);\n return bytesRead === length ? buffer : buffer.subarray(0, bytesRead);\n } finally {\n await handle.close();\n }\n }\n\n /** HTTP/2 responses are plain objects with no stream plumbing to pipe into. */\n private static canStream(res: HttpResponse): boolean {\n const writable = res as any;\n return typeof writable.write === 'function' && typeof writable.on === 'function';\n }\n\n /**\n * `/assets/` -> `/assets`, `/` and `''` -> `''` (serve from the URL root).\n * A prefix given without a leading slash still mounts where you'd expect.\n */\n private static normalizePrefix(prefix?: string): string {\n if (!prefix) return '';\n let normalized = prefix.trim();\n if (!normalized.startsWith('/')) normalized = `/${normalized}`;\n while (normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n return normalized === '/' ? '' : normalized;\n }\n\n /**\n * Request path relative to the mount point, or `null` when the request falls\n * outside it. `/assets` itself maps to `/` so the directory index still\n * resolves; `/assetsfoo` is a different route and is left alone.\n */\n private stripPrefix(reqPath: string): string | null {\n if (!this.prefix) return reqPath;\n if (reqPath === this.prefix) return '/';\n if (reqPath.startsWith(`${this.prefix}/`)) return reqPath.slice(this.prefix.length);\n return null;\n }\n\n private addCharsetIfNeeded(mimeType: string): string {\n const textTypes = [\n 'text/',\n 'application/json',\n 'application/javascript',\n 'application/xml',\n 'application/yaml',\n 'image/svg+xml',\n ];\n const needsCharset =\n textTypes.some(type => mimeType.startsWith(type)) ||\n mimeType.endsWith('+json') ||\n mimeType.endsWith('+xml');\n return needsCharset ? `${mimeType}; charset=utf-8` : mimeType;\n }\n\n getRoot(): string {\n return this.root;\n }\n\n getPrefix(): string {\n return this.prefix;\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { WebSocketAdapter, WebSocketAdapterOptions, WebSocketNamespace } from '../websocket-adapter.js';
1
+ import { WebSocketAdapter, WebSocketAdapterOptions, WebSocketNamespace, WebSocketNamespaceOptions } from '../websocket-adapter.js';
2
2
  export declare class EngineWebSocketAdapter implements WebSocketAdapter {
3
3
  private server;
4
4
  private namespaces;
@@ -13,7 +13,7 @@ export declare class EngineWebSocketAdapter implements WebSocketAdapter {
13
13
  private handleClose;
14
14
  private generateId;
15
15
  private getDefaultNamespaceImpl;
16
- createNamespace(namespace: string): WebSocketNamespace;
16
+ createNamespace(namespace: string, options?: WebSocketNamespaceOptions): WebSocketNamespace;
17
17
  getDefaultNamespace(): WebSocketNamespace;
18
18
  close(): Promise<void>;
19
19
  setCompression(_enabled: boolean, _options?: any): void;
@@ -52,12 +52,21 @@ export class EngineWebSocketAdapter {
52
52
  const nsPath = (path || '/').split(/[?#]/)[0] || '/';
53
53
  const ns = this.namespaces.get(nsPath) ??
54
54
  this.getDefaultNamespaceImpl();
55
+ connection.raw = ns.raw;
55
56
  ns.handleConnection(connection);
56
57
  }
57
58
  handleMessage(wsId, data, isBinary) {
58
59
  const connection = this.byWsId.get(wsId);
59
60
  if (!connection)
60
61
  return;
62
+ if (connection.raw) {
63
+ // Raw namespace: no envelope, no parse - the frame is the message
64
+ if (isBinary)
65
+ connection.handleBinary(Buffer.isBuffer(data) ? data : Buffer.from(data));
66
+ else
67
+ connection.handleRawText(typeof data === 'string' ? data : Buffer.from(data).toString('utf-8'));
68
+ return;
69
+ }
61
70
  if (isBinary) {
62
71
  connection.handleBinary(data);
63
72
  return;
@@ -91,12 +100,14 @@ export class EngineWebSocketAdapter {
91
100
  getDefaultNamespaceImpl() {
92
101
  return this.createNamespace('/');
93
102
  }
94
- createNamespace(namespace) {
103
+ createNamespace(namespace, options) {
95
104
  let ns = this.namespaces.get(namespace);
96
105
  if (!ns) {
97
106
  ns = new EngineNamespaceWrapper(namespace, this.connections);
98
107
  this.namespaces.set(namespace, ns);
99
108
  }
109
+ if (options?.raw)
110
+ ns.raw = true;
100
111
  return ns;
101
112
  }
102
113
  getDefaultNamespace() {
@@ -130,6 +141,8 @@ export class EngineWebSocketAdapter {
130
141
  class EngineNamespaceWrapper {
131
142
  namespace;
132
143
  connections;
144
+ /** Raw framing for connections routed here (see WebSocketNamespaceOptions) */
145
+ raw = false;
133
146
  connectionHandlers = [];
134
147
  middlewares = [];
135
148
  logger = createFrameworkLogger('ENGINE_WS_NS');
@@ -212,6 +225,8 @@ class EngineConnectionWrapper {
212
225
  namespaces;
213
226
  data = {};
214
227
  connected = true;
228
+ /** Raw framing (set from the namespace at open) */
229
+ raw = false;
215
230
  headers;
216
231
  query;
217
232
  rooms = new Set();
@@ -271,6 +286,30 @@ class EngineConnectionWrapper {
271
286
  compressedEmit(event, data) {
272
287
  this.emit(event, data);
273
288
  }
289
+ send(data, isBinary) {
290
+ if (!this.connected)
291
+ return;
292
+ try {
293
+ const binary = isBinary ?? typeof data !== 'string';
294
+ const sent = this.server.wsSend(this.wsId, data, binary);
295
+ if (!sent)
296
+ this.logger.warn(`Backpressure for connection ${this.id}`, 'Backpressure');
297
+ }
298
+ catch (error) {
299
+ this.logger.error(`Failed to send frame: ${error instanceof Error ? error.message : String(error)}`, 'Send');
300
+ }
301
+ }
302
+ /** Raw namespace: a text frame reaches the 'message' handlers verbatim */
303
+ handleRawText(text) {
304
+ if (!this.connected)
305
+ return;
306
+ for (const handler of this.anyHandlers)
307
+ this.runHandler(handler, 'message', text);
308
+ const handlers = this.eventHandlers.get('message');
309
+ if (handlers)
310
+ for (const handler of handlers)
311
+ this.runHandler(handler, text);
312
+ }
274
313
  // A user handler throwing must not kill the connection, skip sibling
275
314
  // handlers, or (via the native callback) get swallowed silently.
276
315
  runHandler(fn, ...args) {