@morojs/moro 1.8.15 → 1.8.17

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.
@@ -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';\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
+ {"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, watch as watchDir, type FSWatcher, type Stats } from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport { isCompressible, negotiateEncoding } from '../../../http/utils/compression.js';\n\nexport interface StaticCacheOptions {\n /**\n * Largest file kept in memory, in bytes. Bigger files take the uncached\n * path (read or streamed from disk per request). Default 1 MiB.\n */\n maxFileBytes?: number;\n /**\n * Budget for everything cached, in bytes, originals and precompressed\n * sidecars together; the oldest entries leave when it is exceeded.\n * Default 64 MiB.\n */\n maxBytes?: number;\n /**\n * How long a cached file is served without looking at the disk again, in\n * milliseconds. Past it, the next request stats the file (and the\n * sidecars it may have) and reloads what changed, so a replaced file is\n * served within this window at the latest. Default 1000. `0` stats on\n * every request.\n */\n revalidateMs?: number;\n /**\n * Watch the root directory and revalidate everything the moment a change\n * is reported, ahead of `revalidateMs`. Default true where the platform\n * supports it; a watcher that cannot be started is simply not used.\n */\n watch?: boolean;\n}\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 * Keep the files in memory. Without it every request resolves, stats and\n * reads the file (and its sidecars) from disk - five to eight filesystem\n * round trips through the threadpool per response, which is what bounds a\n * busy static route. With it, a file is read once and served from memory\n * with its validators and sidecars; the disk is consulted again at most\n * once per `revalidateMs` (default every second) or as soon as the root\n * directory reports a change, so a replaced file is still served promptly.\n * `true` takes the defaults; an object tunes them. Default false.\n */\n cache?: boolean | StaticCacheOptions;\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\n/** What identifies one file on disk: a replacement of the same length still differs here. */\ninterface FileStamp {\n mtimeMs: number;\n size: number;\n ino: number;\n}\n\n/** One representation of a cached file: the original, or a precompressed sidecar. */\ninterface CachedVariant {\n /** '' for the identity representation */\n encoding: '' | 'br' | 'gzip';\n path: string;\n stamp: FileStamp;\n body: Buffer;\n etag: string;\n}\n\ninterface CacheEntry {\n /** The resolved (real) path of the original, the key's target on disk */\n filePath: string;\n baseMimeType: string;\n contentType: string;\n mtime: Date;\n lastModified: string;\n identity: CachedVariant;\n /** The sidecars that existed (and were current) when the entry was built, in SIDECARS order */\n sidecars: CachedVariant[];\n /** Sidecar paths probed when the entry was built, present or not: absent ones must be re-probed too */\n sidecarStamps: Array<FileStamp | null>;\n bytes: number;\n checkedAt: number;\n}\n\nconst stampOf = (st: { mtimeMs: number; size: number; ino: number }): FileStamp => ({\n mtimeMs: st.mtimeMs,\n size: st.size,\n ino: st.ino,\n});\nconst sameStamp = (a: FileStamp | null, b: FileStamp | null): boolean =>\n a === b ||\n (a !== null && b !== null && a.mtimeMs === b.mtimeMs && a.size === b.size && a.ino === b.ino);\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 // The in-memory cache (see StaticOptions.cache), keyed by the request path\n // under the prefix. Null when caching is off.\n private cache: Map<string, CacheEntry> | null = null;\n private cacheMaxFileBytes = 1024 * 1024;\n private cacheMaxBytes = 64 * 1024 * 1024;\n private cacheRevalidateMs = 1000;\n private cacheBytes = 0;\n private watcher: FSWatcher | null = null;\n private watchWanted = true;\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 if (options.cache) {\n const c = typeof options.cache === 'object' ? options.cache : {};\n this.cache = new Map();\n if (c.maxFileBytes !== undefined) this.cacheMaxFileBytes = Math.max(0, c.maxFileBytes);\n if (c.maxBytes !== undefined) this.cacheMaxBytes = Math.max(0, c.maxBytes);\n if (c.revalidateMs !== undefined) this.cacheRevalidateMs = Math.max(0, c.revalidateMs);\n this.watchWanted = c.watch !== false;\n }\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 // Cached: the common case is a hit that needs no filesystem call at all.\n // A Range request keeps the uncached path (byte offsets refer to the\n // identity file on disk).\n const cache = this.cache;\n if (cache !== null) {\n const entry = cache.get(urlPath);\n if (entry !== undefined && req.headers.range === undefined) {\n if (Date.now() - entry.checkedAt < this.cacheRevalidateMs) {\n return this.serveCached(req, res, entry);\n }\n // Past the revalidation window: one look at the disk. Unchanged, the\n // entry serves on; changed or gone, it is dropped and the request\n // takes the resolving path below, which rebuilds it.\n if (await this.stillCurrent(entry)) {\n entry.checkedAt = Date.now();\n return this.serveCached(req, res, entry);\n }\n this.evict(urlPath, entry);\n }\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 // A file that fits the cache is read once, with its sidecars, and the\n // response comes from the entry - this request's and every later one's.\n if (\n cache !== null &&\n stats.isFile() &&\n stats.size <= this.cacheMaxFileBytes &&\n req.headers.range === undefined\n ) {\n const entry = await this.buildEntry(filePath, stats, baseMimeType, contentType);\n if (entry !== null) {\n this.insert(urlPath, entry);\n return this.serveCached(req, res, entry);\n }\n }\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 = StaticCore.etagFor(stats, 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 this.sendBody(res, data, baseMimeType);\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 // ---- the in-memory cache ----\n\n /**\n * Read the file and whichever current sidecars exist beside it into one\n * entry. Null when the file could not be read (gone between stat and\n * read), in which case the request takes the uncached path's answer.\n */\n private async buildEntry(\n filePath: string,\n stats: { mtimeMs: number; size: number; ino: number; mtime: Date },\n baseMimeType: string,\n contentType: string\n ): Promise<CacheEntry | null> {\n let body: Buffer;\n try {\n body = await fs.readFile(filePath);\n } catch {\n return null;\n }\n const stamp = stampOf(stats);\n const identity: CachedVariant = {\n encoding: '',\n path: filePath,\n stamp,\n body,\n etag: StaticCore.etagFor(stats, ''),\n };\n const sidecars: CachedVariant[] = [];\n const sidecarStamps: Array<FileStamp | null> = [];\n let bytes = body.length;\n if (this.precompressed) {\n const rootWithSep = this.root.endsWith(path.sep) ? this.root : this.root + path.sep;\n for (const { encoding, suffix } of SIDECARS) {\n const found = await this.statSidecar(filePath + suffix, rootWithSep);\n sidecarStamps.push(found ? stampOf(found.stats) : null);\n // A sidecar older than its source is a stale body for a replaced\n // asset: not served, and its stamp is still remembered so a later\n // touch of it is noticed.\n if (!found || found.stats.mtimeMs < stats.mtimeMs) continue;\n let sideBody: Buffer;\n try {\n sideBody = await fs.readFile(found.real);\n } catch {\n continue;\n }\n sidecars.push({\n encoding,\n path: found.real,\n stamp: stampOf(found.stats),\n body: sideBody,\n etag: StaticCore.etagFor(stats, encoding),\n });\n bytes += sideBody.length;\n }\n }\n return {\n filePath,\n baseMimeType,\n contentType,\n mtime: stats.mtime,\n lastModified: stats.mtime.toUTCString(),\n identity,\n sidecars,\n sidecarStamps,\n bytes,\n checkedAt: Date.now(),\n };\n }\n\n /** The sidecar at `candidate` when it is a regular file inside the root. */\n private async statSidecar(\n candidate: string,\n rootWithSep: string\n ): Promise<{ real: string; stats: Stats } | null> {\n try {\n const real = await fs.realpath(candidate);\n if (!real.startsWith(rootWithSep)) return null;\n const stats = await fs.stat(real);\n if (!stats.isFile()) return null;\n return { real, stats };\n } catch {\n return null;\n }\n }\n\n /**\n * Whether the disk still matches the entry: the original and every sidecar\n * position (present or absent when the entry was built) are stat'ed and\n * compared on mtime, size and inode, so a same-length replacement counts\n * as a change and a sidecar that appeared or vanished does too.\n */\n private async stillCurrent(entry: CacheEntry): Promise<boolean> {\n let stats;\n try {\n stats = await fs.stat(entry.filePath);\n } catch {\n return false;\n }\n if (!stats.isFile() || !sameStamp(stampOf(stats), entry.identity.stamp)) return false;\n if (this.precompressed) {\n for (let i = 0; i < SIDECARS.length; i++) {\n let now: FileStamp | null = null;\n try {\n const st = await fs.stat(entry.filePath + (SIDECARS[i] as { suffix: string }).suffix);\n if (st.isFile()) now = stampOf(st);\n } catch {\n // absent\n }\n if (!sameStamp(now, entry.sidecarStamps[i] ?? null)) return false;\n }\n }\n return true;\n }\n\n private insert(key: string, entry: CacheEntry): void {\n const cache = this.cache;\n if (cache === null) return;\n const old = cache.get(key);\n if (old !== undefined) this.evict(key, old);\n cache.set(key, entry);\n this.cacheBytes += entry.bytes;\n // Over budget: the oldest entries go first (insertion order).\n for (const [k, e] of cache) {\n if (this.cacheBytes <= this.cacheMaxBytes || k === key) break;\n this.evict(k, e);\n }\n this.ensureWatcher();\n }\n\n private evict(key: string, entry: CacheEntry): void {\n if (this.cache !== null && this.cache.delete(key)) this.cacheBytes -= entry.bytes;\n }\n\n /**\n * One watcher on the root directory: any change reported there marks every\n * entry for revalidation on its next request, ahead of `revalidateMs`.\n * Non-persistent, so it never keeps the process alive, and best effort: a\n * platform or filesystem without change events simply falls back to the\n * timed revalidation.\n */\n private ensureWatcher(): void {\n if (!this.watchWanted || this.watcher !== null) return;\n try {\n const watcher = watchDir(this.root, { persistent: false }, () => {\n if (this.cache === null) return;\n for (const entry of this.cache.values()) entry.checkedAt = 0;\n });\n watcher.on('error', () => {\n this.watcher = null;\n this.watchWanted = false; // a dead watcher is not retried\n });\n this.watcher = watcher;\n } catch {\n this.watchWanted = false;\n }\n }\n\n /** Stop the directory watcher and drop every cached file. */\n close(): void {\n if (this.watcher !== null) {\n try {\n this.watcher.close();\n } catch {\n // already closed\n }\n this.watcher = null;\n }\n if (this.cache !== null) {\n this.cache.clear();\n this.cacheBytes = 0;\n }\n }\n\n /** Answer a request from a cache entry: the same headers and bytes the uncached path sends. */\n private serveCached(req: HttpRequest, res: HttpResponse, entry: CacheEntry): boolean {\n res.setHeader('Content-Type', entry.contentType);\n let variant = entry.identity;\n if (this.precompressed) {\n res.setHeader('Vary', 'Accept-Encoding');\n const sidecars = entry.sidecars;\n if (sidecars.length > 0) {\n const acceptEncoding = req.headers['accept-encoding'];\n if (acceptEncoding) {\n for (let i = 0; i < sidecars.length; i++) {\n const candidate = sidecars[i] as CachedVariant;\n if (negotiateEncoding(acceptEncoding, [candidate.encoding as 'br' | 'gzip']) !== null) {\n variant = candidate;\n break;\n }\n }\n }\n }\n }\n if (this.maxAge > 0) {\n res.setHeader('Cache-Control', `public, max-age=${this.maxAge}`);\n }\n if (this.lastModified) {\n res.setHeader('Last-Modified', entry.lastModified);\n }\n if (this.acceptRanges) {\n res.setHeader('Accept-Ranges', 'bytes');\n }\n const etag = this.etag ? variant.etag : undefined;\n if (etag !== undefined) res.setHeader('ETag', etag);\n if (this.isNotModified(req, entry.mtime, etag)) {\n res.statusCode = 304;\n res.end();\n return true;\n }\n if (variant.encoding !== '') {\n res.setHeader('Content-Encoding', variant.encoding);\n res.setHeader('Content-Length', variant.body.length);\n if (req.method === 'HEAD') {\n res.end();\n return true;\n }\n res.end(variant.body);\n return true;\n }\n if (req.method === 'HEAD') {\n res.setHeader('Content-Length', variant.body.length);\n res.end();\n return true;\n }\n this.sendBody(res, variant.body, entry.baseMimeType);\n return true;\n }\n\n /**\n * The identity body of a small file. A compressible one goes through\n * send() so it joins the server's response compression (the compression\n * middleware or performance.compression) exactly like a handler's body\n * would; send() sets Content-Length itself. Binary bodies are ended as they\n * are, with their length.\n */\n private sendBody(res: HttpResponse, data: Buffer, baseMimeType: string): void {\n if (isCompressible(baseMimeType) && typeof (res as any).send === 'function') {\n (res as any).send(data);\n } else {\n res.setHeader('Content-Length', data.length);\n res.end(data);\n }\n }\n\n /**\n * The strong ETag of one representation: the file's mtime and size, plus\n * the encoding for a sidecar, since a strong ETag must differ between\n * representations (RFC 9110 8.8.1).\n */\n private static etagFor(stats: { mtimeMs: number; size: number }, encoding: string): string {\n return `\"${crypto\n .createHash('md5')\n .update(`${Math.trunc(stats.mtimeMs)}-${stats.size}`)\n .digest('hex')}${encoding ? `-${encoding}` : ''}\"`;\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"]}
@@ -5,6 +5,41 @@
5
5
  // a default namespace, so app.websocket() handlers work identically.
6
6
  import crypto from 'crypto';
7
7
  import { createFrameworkLogger } from '../../logger/index.js';
8
+ // One logger per role for the whole process, not one per namespace or per
9
+ // connection: a child logger was being built on every upgrade.
10
+ const nsLogger = createFrameworkLogger('ENGINE_WS_NS');
11
+ const connLogger = createFrameworkLogger('ENGINE_WS_CONN');
12
+ // Random bytes for connection ids, drawn from a pool refilled in one call:
13
+ // crypto.randomBytes(4) per upgrade was a CSPRNG call and a Buffer each.
14
+ const ID_POOL = Buffer.allocUnsafe(4096);
15
+ let idPoolOffset = ID_POOL.length;
16
+ function randomIdSuffix() {
17
+ if (idPoolOffset + 4 > ID_POOL.length) {
18
+ crypto.randomFillSync(ID_POOL);
19
+ idPoolOffset = 0;
20
+ }
21
+ const hex = ID_POOL.toString('hex', idPoolOffset, idPoolOffset + 4);
22
+ idPoolOffset += 4;
23
+ return hex;
24
+ }
25
+ // The handshake headers as the EngineRequest.headers getter builds them:
26
+ // keys already lowercased, duplicate lines joined ('; ' for cookie, ', '
27
+ // otherwise) - last-wins would let a client send two X-Forwarded-For lines
28
+ // and hide the earlier one from IP-based WS auth / rate-limiting.
29
+ function headersFromFlat(flat) {
30
+ const headers = {};
31
+ if (!flat)
32
+ return headers;
33
+ for (let i = 0; i + 1 < flat.length; i += 2) {
34
+ const key = flat[i];
35
+ const val = flat[i + 1];
36
+ if (key === undefined || val === undefined)
37
+ continue;
38
+ const existing = headers[key];
39
+ headers[key] = existing === undefined ? val : existing + (key === 'cookie' ? '; ' : ', ') + val;
40
+ }
41
+ return headers;
42
+ }
8
43
  // Parse a raw upgrade query string (`orgId=1&x=y`, with or without a leading
9
44
  // `?`) into a flat object for `socket.handshake.query`. Last value wins on
10
45
  // duplicate keys, matching URLSearchParams / Socket.IO.
@@ -93,9 +128,7 @@ export class EngineWebSocketAdapter {
93
128
  this.connections.delete(connection.id);
94
129
  }
95
130
  generateId() {
96
- return `${Date.now().toString(36)}-${(++this.connectionCounter).toString(36)}-${crypto
97
- .randomBytes(4)
98
- .toString('hex')}`;
131
+ return `${Date.now().toString(36)}-${(++this.connectionCounter).toString(36)}-${randomIdSuffix()}`;
99
132
  }
100
133
  getDefaultNamespaceImpl() {
101
134
  return this.createNamespace('/');
@@ -145,7 +178,7 @@ class EngineNamespaceWrapper {
145
178
  raw = false;
146
179
  connectionHandlers = [];
147
180
  middlewares = [];
148
- logger = createFrameworkLogger('ENGINE_WS_NS');
181
+ logger = nsLogger;
149
182
  constructor(namespace, connections) {
150
183
  this.namespace = namespace;
151
184
  this.connections = connections;
@@ -227,13 +260,20 @@ class EngineConnectionWrapper {
227
260
  connected = true;
228
261
  /** Raw framing (set from the namespace at open) */
229
262
  raw = false;
230
- headers;
231
- query;
263
+ _flat;
264
+ _headers = undefined;
265
+ _query = undefined;
266
+ _queryString;
267
+ // The engine upgraded this socket itself (wsAutoUpgrade): the handshake is
268
+ // fetched from the engine the first time ip, headers or query is read. It
269
+ // stays readable through the disconnect handler; a read after that gets
270
+ // the empty values.
271
+ _lazy;
232
272
  rooms = new Set();
233
273
  eventHandlers = new Map();
234
274
  anyHandlers = [];
235
275
  disconnectHandlers = [];
236
- logger = createFrameworkLogger('ENGINE_WS_CONN');
276
+ logger = connLogger;
237
277
  _ip;
238
278
  constructor(server, wsId, id, path, namespaces, handshake) {
239
279
  this.server = server;
@@ -242,19 +282,59 @@ class EngineConnectionWrapper {
242
282
  this.path = path;
243
283
  this.namespaces = namespaces;
244
284
  // Handshake snapshot (captured before the upgrade invalidated the reqId):
245
- // real values for IP-based rate limiting and header/token connection auth.
285
+ // real values for IP-based rate limiting and header/token connection
286
+ // auth. The header object and the parsed query are built on first read.
287
+ // Without a snapshot the engine performed the upgrade and holds the
288
+ // handshake: it is fetched on first read (see _lazy).
289
+ this._lazy = handshake === undefined;
246
290
  this._ip = handshake?.ip ?? '';
247
- this.headers = handshake?.headers ?? {};
248
- this.query = parseQueryString(handshake?.query ?? '');
291
+ this._flat = handshake?.flat;
292
+ this._queryString = handshake?.query ?? '';
293
+ }
294
+ materialize() {
295
+ this._lazy = false;
296
+ const hs = typeof this.server.wsHandshake === 'function'
297
+ ? this.server.wsHandshake(this.wsId)
298
+ : undefined;
299
+ if (hs) {
300
+ this._ip = hs.ip;
301
+ this._flat = hs.flat;
302
+ this._queryString = hs.query;
303
+ }
249
304
  }
250
305
  get ip() {
306
+ if (this._lazy)
307
+ this.materialize();
251
308
  return this._ip;
252
309
  }
310
+ /** The handshake request's headers, keys lowercased, duplicates joined. */
311
+ get headers() {
312
+ let h = this._headers;
313
+ if (h === undefined) {
314
+ if (this._lazy)
315
+ this.materialize();
316
+ h = headersFromFlat(this._flat);
317
+ this._flat = undefined;
318
+ this._headers = h;
319
+ }
320
+ return h;
321
+ }
322
+ /** The handshake request's query parameters. */
323
+ get query() {
324
+ let q = this._query;
325
+ if (q === undefined) {
326
+ if (this._lazy)
327
+ this.materialize();
328
+ q = parseQueryString(this._queryString);
329
+ this._query = q;
330
+ }
331
+ return q;
332
+ }
253
333
  // Socket.IO-parity handshake view so `socket.handshake.query`/`.headers`
254
334
  // handlers (e.g. reading an `orgId` query param) work against the engine
255
335
  // adapter unchanged.
256
336
  get handshake() {
257
- return { query: this.query, headers: this.headers, address: this._ip };
337
+ return { query: this.query, headers: this.headers, address: this.ip };
258
338
  }
259
339
  on(event, handler) {
260
340
  if (event === 'disconnect') {
@@ -1 +1 @@
1
- {"version":3,"file":"engine-adapter.js","sourceRoot":"","sources":["../../../../src/core/networking/adapters/engine-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["// @morojs/engine WebSocket adapter.\n// Bridges the native engine's RFC 6455 WebSocket callbacks (onWsOpen/Message/\n// Close, wsSend/wsClose on MoroEngineServer) to Moro's WebSocketAdapter model.\n// Message framing mirrors the uWS adapter: JSON { event, data } envelopes with\n// a default namespace, so app.websocket() handlers work identically.\n\nimport crypto from 'crypto';\nimport {\n WebSocketAdapter,\n WebSocketAdapterOptions,\n WebSocketNamespace,\n WebSocketNamespaceOptions,\n WebSocketConnection,\n WebSocketEmitter,\n WebSocketMiddleware,\n} from '../websocket-adapter.js';\nimport { createFrameworkLogger } from '../../logger/index.js';\n\n// Parse a raw upgrade query string (`orgId=1&x=y`, with or without a leading\n// `?`) into a flat object for `socket.handshake.query`. Last value wins on\n// duplicate keys, matching URLSearchParams / Socket.IO.\nfunction parseQueryString(qs: string): Record<string, string> {\n const out: Record<string, string> = {};\n if (!qs) return out;\n const params = new URLSearchParams(qs.startsWith('?') ? qs.slice(1) : qs);\n for (const [key, value] of params) out[key] = value;\n return out;\n}\n\nexport class EngineWebSocketAdapter implements WebSocketAdapter {\n private server: any; // MoroEngineServer\n private namespaces = new Map<string, EngineNamespaceWrapper>();\n private connections = new Map<string, EngineConnectionWrapper>();\n private byWsId = new Map<number, EngineConnectionWrapper>();\n private logger = createFrameworkLogger('ENGINE_WS');\n private customIdGenerator?: () => string;\n private connectionCounter = 0;\n\n async initialize(httpServer: any, _options: WebSocketAdapterOptions = {}): Promise<void> {\n if (!httpServer || typeof httpServer.enableWebSocket !== 'function') {\n throw new Error(\n 'EngineWebSocketAdapter requires the @morojs/engine HTTP server (MoroEngineServer)'\n );\n }\n this.server = httpServer;\n\n // Register the native WS bridge: the engine drives these on the event loop.\n this.server.enableWebSocket({\n onOpen: (\n wsId: number,\n path: string,\n info?: { ip: string; headers: Record<string, string>; query?: string }\n ) => this.handleOpen(wsId, path, info),\n onMessage: (wsId: number, data: any, isBinary: boolean) =>\n this.handleMessage(wsId, data, isBinary),\n onClose: (wsId: number, code: number) => this.handleClose(wsId, code),\n });\n\n this.createNamespace('/');\n this.logger.info('Engine WebSocket adapter initialized', 'Init');\n }\n\n private handleOpen(\n wsId: number,\n path: string,\n info?: { ip: string; headers: Record<string, string>; query?: string }\n ): void {\n const id = this.customIdGenerator ? this.customIdGenerator() : this.generateId();\n const connection = new EngineConnectionWrapper(\n this.server,\n wsId,\n id,\n path,\n this.namespaces,\n info\n );\n this.connections.set(id, connection);\n this.byWsId.set(wsId, connection);\n // Route to the namespace whose registered path matches the upgrade URL\n // (Socket.IO parity for `app.websocket('/name', ...)`), falling back to the\n // default namespace when nothing matches. Query/hash are stripped so a\n // client connecting to `/name?token=…` still resolves to the `/name`\n // namespace.\n const nsPath = (path || '/').split(/[?#]/)[0] || '/';\n const ns =\n (this.namespaces.get(nsPath) as EngineNamespaceWrapper | undefined) ??\n this.getDefaultNamespaceImpl();\n connection.raw = ns.raw;\n ns.handleConnection(connection);\n }\n\n private handleMessage(wsId: number, data: any, isBinary: boolean): void {\n const connection = this.byWsId.get(wsId);\n if (!connection) return;\n if (connection.raw) {\n // Raw namespace: no envelope, no parse - the frame is the message\n if (isBinary) connection.handleBinary(Buffer.isBuffer(data) ? data : Buffer.from(data));\n else\n connection.handleRawText(\n typeof data === 'string' ? data : Buffer.from(data).toString('utf-8')\n );\n return;\n }\n if (isBinary) {\n connection.handleBinary(data);\n return;\n }\n // Text frames carry JSON { event, data } envelopes (uWS-adapter parity)\n const text = typeof data === 'string' ? data : Buffer.from(data).toString('utf-8');\n let parsed: any;\n try {\n parsed = JSON.parse(text);\n } catch {\n this.logger.warn(`Non-JSON message from ${connection.id}`, 'Message');\n return;\n }\n connection.handleMessage(parsed);\n }\n\n private handleClose(wsId: number, code: number): void {\n const connection = this.byWsId.get(wsId);\n if (!connection) return;\n connection.connected = false;\n connection.handleDisconnect(code);\n this.byWsId.delete(wsId);\n this.connections.delete(connection.id);\n }\n\n private generateId(): string {\n return `${Date.now().toString(36)}-${(++this.connectionCounter).toString(36)}-${crypto\n .randomBytes(4)\n .toString('hex')}`;\n }\n\n private getDefaultNamespaceImpl(): EngineNamespaceWrapper {\n return this.createNamespace('/') as EngineNamespaceWrapper;\n }\n\n createNamespace(namespace: string, options?: WebSocketNamespaceOptions): WebSocketNamespace {\n let ns = this.namespaces.get(namespace);\n if (!ns) {\n ns = new EngineNamespaceWrapper(namespace, this.connections);\n this.namespaces.set(namespace, ns);\n }\n if (options?.raw) ns.raw = true;\n return ns;\n }\n\n getDefaultNamespace(): WebSocketNamespace {\n return this.createNamespace('/');\n }\n\n async close(): Promise<void> {\n for (const conn of this.connections.values()) {\n conn.disconnect();\n }\n this.connections.clear();\n this.byWsId.clear();\n }\n\n setCompression(_enabled: boolean, _options?: any): void {\n // Compression (permessage-deflate) is negotiated natively; no-op for now.\n }\n\n setCustomIdGenerator(generator: () => string): void {\n this.customIdGenerator = generator;\n }\n\n getAdapterName(): string {\n return '@morojs/engine';\n }\n\n getConnectionCount(): number {\n let count = 0;\n for (const conn of this.connections.values()) {\n if (conn.connected) count++;\n }\n return count;\n }\n}\n\nclass EngineNamespaceWrapper implements WebSocketNamespace {\n /** Raw framing for connections routed here (see WebSocketNamespaceOptions) */\n raw = false;\n private connectionHandlers: ((socket: WebSocketConnection) => void)[] = [];\n private middlewares: WebSocketMiddleware[] = [];\n private logger = createFrameworkLogger('ENGINE_WS_NS');\n\n constructor(\n private namespace: string,\n private connections: Map<string, EngineConnectionWrapper>\n ) {}\n\n on(event: 'connection', handler: (socket: WebSocketConnection) => void): void {\n if (event === 'connection') this.connectionHandlers.push(handler);\n }\n\n handleConnection(connection: EngineConnectionWrapper): void {\n this.runMiddlewares(connection, err => {\n if (err) {\n connection.disconnect(true);\n return;\n }\n // A throwing connection handler must not take down the socket/loop.\n for (const handler of this.connectionHandlers) {\n try {\n handler(connection);\n } catch (error) {\n this.logger.error(\n `WebSocket connection handler threw: ${error instanceof Error ? error.message : String(error)}`,\n 'Connection'\n );\n }\n }\n });\n }\n\n private runMiddlewares(\n connection: EngineConnectionWrapper,\n finalCallback: (err?: Error) => void\n ): void {\n let index = 0;\n const next = (err?: Error) => {\n if (err) return finalCallback(err);\n if (index >= this.middlewares.length) return finalCallback();\n const middleware = this.middlewares[index++];\n middleware?.(connection, next);\n };\n next();\n }\n\n emit(event: string, data: any): void {\n for (const connection of this.connections.values()) {\n if (connection.connected) connection.emit(event, data);\n }\n }\n\n to(room: string | string[]): WebSocketEmitter {\n return new EngineEmitterWrapper(this.connections, {\n rooms: Array.isArray(room) ? room : [room],\n });\n }\n\n except(room: string | string[]): WebSocketEmitter {\n return new EngineEmitterWrapper(this.connections, {\n exceptRooms: Array.isArray(room) ? room : [room],\n });\n }\n\n getSockets(): WebSocketConnection[] {\n const result: WebSocketConnection[] = [];\n for (const conn of this.connections.values()) if (conn.connected) result.push(conn);\n return result;\n }\n\n getConnectionCount(): number {\n let count = 0;\n for (const conn of this.connections.values()) if (conn.connected) count++;\n return count;\n }\n\n use(middleware: WebSocketMiddleware): void {\n this.middlewares.push(middleware);\n }\n}\n\nclass EngineConnectionWrapper implements WebSocketConnection {\n public data: Record<string, any> = {};\n public connected = true;\n /** Raw framing (set from the namespace at open) */\n public raw = false;\n public readonly headers: Record<string, string>;\n public readonly query: Record<string, string>;\n private rooms = new Set<string>();\n private eventHandlers = new Map<\n string,\n Array<(data: any, callback?: (response?: any) => void) => void>\n >();\n private anyHandlers: Array<(event: string, ...args: any[]) => void> = [];\n private disconnectHandlers: Array<(reason?: any) => void> = [];\n private logger = createFrameworkLogger('ENGINE_WS_CONN');\n private readonly _ip: string;\n\n constructor(\n private server: any,\n private wsId: number,\n public readonly id: string,\n public readonly path: string,\n private namespaces: Map<string, EngineNamespaceWrapper>,\n handshake?: { ip: string; headers: Record<string, string>; query?: string }\n ) {\n // Handshake snapshot (captured before the upgrade invalidated the reqId):\n // real values for IP-based rate limiting and header/token connection auth.\n this._ip = handshake?.ip ?? '';\n this.headers = handshake?.headers ?? {};\n this.query = parseQueryString(handshake?.query ?? '');\n }\n\n get ip(): string {\n return this._ip;\n }\n\n // Socket.IO-parity handshake view so `socket.handshake.query`/`.headers`\n // handlers (e.g. reading an `orgId` query param) work against the engine\n // adapter unchanged.\n get handshake(): {\n query: Record<string, string>;\n headers: Record<string, string>;\n address: string;\n } {\n return { query: this.query, headers: this.headers, address: this._ip };\n }\n\n on(event: string, handler: (data: any, callback?: (response?: any) => void) => void): void {\n if (event === 'disconnect') {\n this.disconnectHandlers.push(handler as any);\n return;\n }\n let handlers = this.eventHandlers.get(event);\n if (!handlers) {\n handlers = [];\n this.eventHandlers.set(event, handlers);\n }\n handlers.push(handler);\n }\n\n onAny(handler: (event: string, ...args: any[]) => void): void {\n this.anyHandlers.push(handler);\n }\n\n emit(event: string, data: any): void {\n if (!this.connected) return;\n try {\n const sent = this.server.wsSend(this.wsId, JSON.stringify({ event, data }), false);\n if (!sent) this.logger.warn(`Backpressure for connection ${this.id}`, 'Backpressure');\n } catch (error) {\n this.logger.error(\n `Failed to emit ${event}: ${error instanceof Error ? error.message : String(error)}`,\n 'Emit'\n );\n }\n }\n\n compressedEmit(event: string, data: any): void {\n this.emit(event, data);\n }\n\n send(data: string | Buffer | ArrayBuffer | Uint8Array, isBinary?: boolean): void {\n if (!this.connected) return;\n try {\n const binary = isBinary ?? typeof data !== 'string';\n const sent = this.server.wsSend(this.wsId, data, binary);\n if (!sent) this.logger.warn(`Backpressure for connection ${this.id}`, 'Backpressure');\n } catch (error) {\n this.logger.error(\n `Failed to send frame: ${error instanceof Error ? error.message : String(error)}`,\n 'Send'\n );\n }\n }\n\n /** Raw namespace: a text frame reaches the 'message' handlers verbatim */\n handleRawText(text: string): void {\n if (!this.connected) return;\n for (const handler of this.anyHandlers) this.runHandler(handler, 'message', text);\n const handlers = this.eventHandlers.get('message');\n if (handlers) for (const handler of handlers) this.runHandler(handler, text);\n }\n\n // A user handler throwing must not kill the connection, skip sibling\n // handlers, or (via the native callback) get swallowed silently.\n private runHandler(fn: (...args: any[]) => void, ...args: any[]): void {\n try {\n fn(...args);\n } catch (error) {\n this.logger.error(\n `WebSocket handler threw: ${error instanceof Error ? error.message : String(error)}`,\n 'Handler'\n );\n }\n }\n\n handleMessage(data: any): void {\n // A frame delivered after the socket was closed (e.g. a second frame in the\n // same TCP read whose first frame's handler called disconnect()) must not\n // reach app handlers on a dead socket.\n if (!this.connected) return;\n if (!data || typeof data !== 'object') return;\n const { event, data: eventData, callback: hasCallback } = data;\n if (!event) return;\n\n const callback = hasCallback\n ? (response?: any) => this.emit(`${event}:response`, response)\n : undefined;\n\n for (const handler of this.anyHandlers) this.runHandler(handler, event, eventData);\n const handlers = this.eventHandlers.get(event);\n if (handlers) for (const handler of handlers) this.runHandler(handler, eventData, callback);\n }\n\n handleBinary(data: any): void {\n if (!this.connected) return;\n const handlers = this.eventHandlers.get('binary');\n if (handlers) for (const handler of handlers) this.runHandler(handler, data);\n }\n\n handleDisconnect(reason?: any): void {\n for (const handler of this.disconnectHandlers) this.runHandler(handler, reason);\n }\n\n join(room: string | string[]): void {\n for (const r of Array.isArray(room) ? room : [room]) this.rooms.add(r);\n }\n\n leave(room: string | string[]): void {\n for (const r of Array.isArray(room) ? room : [room]) this.rooms.delete(r);\n }\n\n getRooms(): Set<string> {\n return new Set(this.rooms);\n }\n\n isInRoom(room: string): boolean {\n return this.rooms.has(room);\n }\n\n disconnect(_close?: boolean): void {\n if (!this.connected) return;\n this.connected = false;\n try {\n this.server.wsClose(this.wsId, 1000, '');\n } catch {\n // already gone\n }\n }\n\n to(room: string | string[]): WebSocketEmitter {\n return new EngineEmitterWrapper(this.allConnections(), {\n rooms: Array.isArray(room) ? room : [room],\n });\n }\n\n // Emit to everyone in the default namespace except this socket\n get broadcast(): WebSocketEmitter {\n return new EngineEmitterWrapper(this.allConnections(), { exceptId: this.id });\n }\n\n private allConnections(): Map<string, EngineConnectionWrapper> {\n return (this.namespaces.get('/') as any)?.['connections'] ?? new Map();\n }\n}\n\nclass EngineEmitterWrapper implements WebSocketEmitter {\n constructor(\n private connections: Map<string, EngineConnectionWrapper>,\n private filter: { rooms?: string[]; exceptRooms?: string[]; exceptId?: string }\n ) {}\n\n emit(event: string, data: any): void {\n for (const conn of this.connections.values()) {\n if (!conn.connected) continue;\n if (this.filter.exceptId && conn.id === this.filter.exceptId) continue;\n if (this.filter.rooms && !this.filter.rooms.some(r => conn.isInRoom(r))) continue;\n if (this.filter.exceptRooms && this.filter.exceptRooms.some(r => conn.isInRoom(r))) continue;\n conn.emit(event, data);\n }\n }\n\n to(room: string | string[]): WebSocketEmitter {\n const rooms = Array.isArray(room) ? room : [room];\n return new EngineEmitterWrapper(this.connections, {\n ...this.filter,\n rooms: [...(this.filter.rooms ?? []), ...rooms],\n });\n }\n\n except(room: string | string[]): WebSocketEmitter {\n const rooms = Array.isArray(room) ? room : [room];\n return new EngineEmitterWrapper(this.connections, {\n ...this.filter,\n exceptRooms: [...(this.filter.exceptRooms ?? []), ...rooms],\n });\n }\n\n compress(_compress: boolean): WebSocketEmitter {\n return this; // native negotiation; chaining preserved\n }\n}\n"]}
1
+ {"version":3,"file":"engine-adapter.js","sourceRoot":"","sources":["../../../../src/core/networking/adapters/engine-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["// @morojs/engine WebSocket adapter.\n// Bridges the native engine's RFC 6455 WebSocket callbacks (onWsOpen/Message/\n// Close, wsSend/wsClose on MoroEngineServer) to Moro's WebSocketAdapter model.\n// Message framing mirrors the uWS adapter: JSON { event, data } envelopes with\n// a default namespace, so app.websocket() handlers work identically.\n\nimport crypto from 'crypto';\nimport {\n WebSocketAdapter,\n WebSocketAdapterOptions,\n WebSocketNamespace,\n WebSocketNamespaceOptions,\n WebSocketConnection,\n WebSocketEmitter,\n WebSocketMiddleware,\n} from '../websocket-adapter.js';\nimport { createFrameworkLogger } from '../../logger/index.js';\n\n// One logger per role for the whole process, not one per namespace or per\n// connection: a child logger was being built on every upgrade.\nconst nsLogger = createFrameworkLogger('ENGINE_WS_NS');\nconst connLogger = createFrameworkLogger('ENGINE_WS_CONN');\n\n// Random bytes for connection ids, drawn from a pool refilled in one call:\n// crypto.randomBytes(4) per upgrade was a CSPRNG call and a Buffer each.\nconst ID_POOL = Buffer.allocUnsafe(4096);\nlet idPoolOffset = ID_POOL.length;\nfunction randomIdSuffix(): string {\n if (idPoolOffset + 4 > ID_POOL.length) {\n crypto.randomFillSync(ID_POOL);\n idPoolOffset = 0;\n }\n const hex = ID_POOL.toString('hex', idPoolOffset, idPoolOffset + 4);\n idPoolOffset += 4;\n return hex;\n}\n\n// The handshake headers as the EngineRequest.headers getter builds them:\n// keys already lowercased, duplicate lines joined ('; ' for cookie, ', '\n// otherwise) - last-wins would let a client send two X-Forwarded-For lines\n// and hide the earlier one from IP-based WS auth / rate-limiting.\nfunction headersFromFlat(flat: string[] | undefined): Record<string, string> {\n const headers: Record<string, string> = {};\n if (!flat) return headers;\n for (let i = 0; i + 1 < flat.length; i += 2) {\n const key = flat[i];\n const val = flat[i + 1];\n if (key === undefined || val === undefined) continue;\n const existing = headers[key];\n headers[key] = existing === undefined ? val : existing + (key === 'cookie' ? '; ' : ', ') + val;\n }\n return headers;\n}\n\n// Parse a raw upgrade query string (`orgId=1&x=y`, with or without a leading\n// `?`) into a flat object for `socket.handshake.query`. Last value wins on\n// duplicate keys, matching URLSearchParams / Socket.IO.\nfunction parseQueryString(qs: string): Record<string, string> {\n const out: Record<string, string> = {};\n if (!qs) return out;\n const params = new URLSearchParams(qs.startsWith('?') ? qs.slice(1) : qs);\n for (const [key, value] of params) out[key] = value;\n return out;\n}\n\nexport class EngineWebSocketAdapter implements WebSocketAdapter {\n private server: any; // MoroEngineServer\n private namespaces = new Map<string, EngineNamespaceWrapper>();\n private connections = new Map<string, EngineConnectionWrapper>();\n private byWsId = new Map<number, EngineConnectionWrapper>();\n private logger = createFrameworkLogger('ENGINE_WS');\n private customIdGenerator?: () => string;\n private connectionCounter = 0;\n\n async initialize(httpServer: any, _options: WebSocketAdapterOptions = {}): Promise<void> {\n if (!httpServer || typeof httpServer.enableWebSocket !== 'function') {\n throw new Error(\n 'EngineWebSocketAdapter requires the @morojs/engine HTTP server (MoroEngineServer)'\n );\n }\n this.server = httpServer;\n\n // Register the native WS bridge: the engine drives these on the event loop.\n this.server.enableWebSocket({\n onOpen: (\n wsId: number,\n path: string,\n info?: { ip: string; flat?: string[] | undefined; query?: string }\n ) => this.handleOpen(wsId, path, info),\n onMessage: (wsId: number, data: any, isBinary: boolean) =>\n this.handleMessage(wsId, data, isBinary),\n onClose: (wsId: number, code: number) => this.handleClose(wsId, code),\n });\n\n this.createNamespace('/');\n this.logger.info('Engine WebSocket adapter initialized', 'Init');\n }\n\n private handleOpen(\n wsId: number,\n path: string,\n info?: { ip: string; flat?: string[] | undefined; query?: string }\n ): void {\n const id = this.customIdGenerator ? this.customIdGenerator() : this.generateId();\n const connection = new EngineConnectionWrapper(\n this.server,\n wsId,\n id,\n path,\n this.namespaces,\n info\n );\n this.connections.set(id, connection);\n this.byWsId.set(wsId, connection);\n // Route to the namespace whose registered path matches the upgrade URL\n // (Socket.IO parity for `app.websocket('/name', ...)`), falling back to the\n // default namespace when nothing matches. Query/hash are stripped so a\n // client connecting to `/name?token=…` still resolves to the `/name`\n // namespace.\n const nsPath = (path || '/').split(/[?#]/)[0] || '/';\n const ns =\n (this.namespaces.get(nsPath) as EngineNamespaceWrapper | undefined) ??\n this.getDefaultNamespaceImpl();\n connection.raw = ns.raw;\n ns.handleConnection(connection);\n }\n\n private handleMessage(wsId: number, data: any, isBinary: boolean): void {\n const connection = this.byWsId.get(wsId);\n if (!connection) return;\n if (connection.raw) {\n // Raw namespace: no envelope, no parse - the frame is the message\n if (isBinary) connection.handleBinary(Buffer.isBuffer(data) ? data : Buffer.from(data));\n else\n connection.handleRawText(\n typeof data === 'string' ? data : Buffer.from(data).toString('utf-8')\n );\n return;\n }\n if (isBinary) {\n connection.handleBinary(data);\n return;\n }\n // Text frames carry JSON { event, data } envelopes (uWS-adapter parity)\n const text = typeof data === 'string' ? data : Buffer.from(data).toString('utf-8');\n let parsed: any;\n try {\n parsed = JSON.parse(text);\n } catch {\n this.logger.warn(`Non-JSON message from ${connection.id}`, 'Message');\n return;\n }\n connection.handleMessage(parsed);\n }\n\n private handleClose(wsId: number, code: number): void {\n const connection = this.byWsId.get(wsId);\n if (!connection) return;\n connection.connected = false;\n connection.handleDisconnect(code);\n this.byWsId.delete(wsId);\n this.connections.delete(connection.id);\n }\n\n private generateId(): string {\n return `${Date.now().toString(36)}-${(++this.connectionCounter).toString(36)}-${randomIdSuffix()}`;\n }\n\n private getDefaultNamespaceImpl(): EngineNamespaceWrapper {\n return this.createNamespace('/') as EngineNamespaceWrapper;\n }\n\n createNamespace(namespace: string, options?: WebSocketNamespaceOptions): WebSocketNamespace {\n let ns = this.namespaces.get(namespace);\n if (!ns) {\n ns = new EngineNamespaceWrapper(namespace, this.connections);\n this.namespaces.set(namespace, ns);\n }\n if (options?.raw) ns.raw = true;\n return ns;\n }\n\n getDefaultNamespace(): WebSocketNamespace {\n return this.createNamespace('/');\n }\n\n async close(): Promise<void> {\n for (const conn of this.connections.values()) {\n conn.disconnect();\n }\n this.connections.clear();\n this.byWsId.clear();\n }\n\n setCompression(_enabled: boolean, _options?: any): void {\n // Compression (permessage-deflate) is negotiated natively; no-op for now.\n }\n\n setCustomIdGenerator(generator: () => string): void {\n this.customIdGenerator = generator;\n }\n\n getAdapterName(): string {\n return '@morojs/engine';\n }\n\n getConnectionCount(): number {\n let count = 0;\n for (const conn of this.connections.values()) {\n if (conn.connected) count++;\n }\n return count;\n }\n}\n\nclass EngineNamespaceWrapper implements WebSocketNamespace {\n /** Raw framing for connections routed here (see WebSocketNamespaceOptions) */\n raw = false;\n private connectionHandlers: ((socket: WebSocketConnection) => void)[] = [];\n private middlewares: WebSocketMiddleware[] = [];\n private logger = nsLogger;\n\n constructor(\n private namespace: string,\n private connections: Map<string, EngineConnectionWrapper>\n ) {}\n\n on(event: 'connection', handler: (socket: WebSocketConnection) => void): void {\n if (event === 'connection') this.connectionHandlers.push(handler);\n }\n\n handleConnection(connection: EngineConnectionWrapper): void {\n this.runMiddlewares(connection, err => {\n if (err) {\n connection.disconnect(true);\n return;\n }\n // A throwing connection handler must not take down the socket/loop.\n for (const handler of this.connectionHandlers) {\n try {\n handler(connection);\n } catch (error) {\n this.logger.error(\n `WebSocket connection handler threw: ${error instanceof Error ? error.message : String(error)}`,\n 'Connection'\n );\n }\n }\n });\n }\n\n private runMiddlewares(\n connection: EngineConnectionWrapper,\n finalCallback: (err?: Error) => void\n ): void {\n let index = 0;\n const next = (err?: Error) => {\n if (err) return finalCallback(err);\n if (index >= this.middlewares.length) return finalCallback();\n const middleware = this.middlewares[index++];\n middleware?.(connection, next);\n };\n next();\n }\n\n emit(event: string, data: any): void {\n for (const connection of this.connections.values()) {\n if (connection.connected) connection.emit(event, data);\n }\n }\n\n to(room: string | string[]): WebSocketEmitter {\n return new EngineEmitterWrapper(this.connections, {\n rooms: Array.isArray(room) ? room : [room],\n });\n }\n\n except(room: string | string[]): WebSocketEmitter {\n return new EngineEmitterWrapper(this.connections, {\n exceptRooms: Array.isArray(room) ? room : [room],\n });\n }\n\n getSockets(): WebSocketConnection[] {\n const result: WebSocketConnection[] = [];\n for (const conn of this.connections.values()) if (conn.connected) result.push(conn);\n return result;\n }\n\n getConnectionCount(): number {\n let count = 0;\n for (const conn of this.connections.values()) if (conn.connected) count++;\n return count;\n }\n\n use(middleware: WebSocketMiddleware): void {\n this.middlewares.push(middleware);\n }\n}\n\nclass EngineConnectionWrapper implements WebSocketConnection {\n public data: Record<string, any> = {};\n public connected = true;\n /** Raw framing (set from the namespace at open) */\n public raw = false;\n private _flat: string[] | undefined;\n private _headers: Record<string, string> | undefined = undefined;\n private _query: Record<string, string> | undefined = undefined;\n private _queryString: string;\n // The engine upgraded this socket itself (wsAutoUpgrade): the handshake is\n // fetched from the engine the first time ip, headers or query is read. It\n // stays readable through the disconnect handler; a read after that gets\n // the empty values.\n private _lazy: boolean;\n private rooms = new Set<string>();\n private eventHandlers = new Map<\n string,\n Array<(data: any, callback?: (response?: any) => void) => void>\n >();\n private anyHandlers: Array<(event: string, ...args: any[]) => void> = [];\n private disconnectHandlers: Array<(reason?: any) => void> = [];\n private logger = connLogger;\n private _ip: string;\n\n constructor(\n private server: any,\n private wsId: number,\n public readonly id: string,\n public readonly path: string,\n private namespaces: Map<string, EngineNamespaceWrapper>,\n handshake?: { ip: string; flat?: string[] | undefined; query?: string }\n ) {\n // Handshake snapshot (captured before the upgrade invalidated the reqId):\n // real values for IP-based rate limiting and header/token connection\n // auth. The header object and the parsed query are built on first read.\n // Without a snapshot the engine performed the upgrade and holds the\n // handshake: it is fetched on first read (see _lazy).\n this._lazy = handshake === undefined;\n this._ip = handshake?.ip ?? '';\n this._flat = handshake?.flat;\n this._queryString = handshake?.query ?? '';\n }\n\n private materialize(): void {\n this._lazy = false;\n const hs =\n typeof this.server.wsHandshake === 'function'\n ? this.server.wsHandshake(this.wsId)\n : undefined;\n if (hs) {\n this._ip = hs.ip;\n this._flat = hs.flat;\n this._queryString = hs.query;\n }\n }\n\n get ip(): string {\n if (this._lazy) this.materialize();\n return this._ip;\n }\n\n /** The handshake request's headers, keys lowercased, duplicates joined. */\n get headers(): Record<string, string> {\n let h = this._headers;\n if (h === undefined) {\n if (this._lazy) this.materialize();\n h = headersFromFlat(this._flat);\n this._flat = undefined;\n this._headers = h;\n }\n return h;\n }\n\n /** The handshake request's query parameters. */\n get query(): Record<string, string> {\n let q = this._query;\n if (q === undefined) {\n if (this._lazy) this.materialize();\n q = parseQueryString(this._queryString);\n this._query = q;\n }\n return q;\n }\n\n // Socket.IO-parity handshake view so `socket.handshake.query`/`.headers`\n // handlers (e.g. reading an `orgId` query param) work against the engine\n // adapter unchanged.\n get handshake(): {\n query: Record<string, string>;\n headers: Record<string, string>;\n address: string;\n } {\n return { query: this.query, headers: this.headers, address: this.ip };\n }\n\n on(event: string, handler: (data: any, callback?: (response?: any) => void) => void): void {\n if (event === 'disconnect') {\n this.disconnectHandlers.push(handler as any);\n return;\n }\n let handlers = this.eventHandlers.get(event);\n if (!handlers) {\n handlers = [];\n this.eventHandlers.set(event, handlers);\n }\n handlers.push(handler);\n }\n\n onAny(handler: (event: string, ...args: any[]) => void): void {\n this.anyHandlers.push(handler);\n }\n\n emit(event: string, data: any): void {\n if (!this.connected) return;\n try {\n const sent = this.server.wsSend(this.wsId, JSON.stringify({ event, data }), false);\n if (!sent) this.logger.warn(`Backpressure for connection ${this.id}`, 'Backpressure');\n } catch (error) {\n this.logger.error(\n `Failed to emit ${event}: ${error instanceof Error ? error.message : String(error)}`,\n 'Emit'\n );\n }\n }\n\n compressedEmit(event: string, data: any): void {\n this.emit(event, data);\n }\n\n send(data: string | Buffer | ArrayBuffer | Uint8Array, isBinary?: boolean): void {\n if (!this.connected) return;\n try {\n const binary = isBinary ?? typeof data !== 'string';\n const sent = this.server.wsSend(this.wsId, data, binary);\n if (!sent) this.logger.warn(`Backpressure for connection ${this.id}`, 'Backpressure');\n } catch (error) {\n this.logger.error(\n `Failed to send frame: ${error instanceof Error ? error.message : String(error)}`,\n 'Send'\n );\n }\n }\n\n /** Raw namespace: a text frame reaches the 'message' handlers verbatim */\n handleRawText(text: string): void {\n if (!this.connected) return;\n for (const handler of this.anyHandlers) this.runHandler(handler, 'message', text);\n const handlers = this.eventHandlers.get('message');\n if (handlers) for (const handler of handlers) this.runHandler(handler, text);\n }\n\n // A user handler throwing must not kill the connection, skip sibling\n // handlers, or (via the native callback) get swallowed silently.\n private runHandler(fn: (...args: any[]) => void, ...args: any[]): void {\n try {\n fn(...args);\n } catch (error) {\n this.logger.error(\n `WebSocket handler threw: ${error instanceof Error ? error.message : String(error)}`,\n 'Handler'\n );\n }\n }\n\n handleMessage(data: any): void {\n // A frame delivered after the socket was closed (e.g. a second frame in the\n // same TCP read whose first frame's handler called disconnect()) must not\n // reach app handlers on a dead socket.\n if (!this.connected) return;\n if (!data || typeof data !== 'object') return;\n const { event, data: eventData, callback: hasCallback } = data;\n if (!event) return;\n\n const callback = hasCallback\n ? (response?: any) => this.emit(`${event}:response`, response)\n : undefined;\n\n for (const handler of this.anyHandlers) this.runHandler(handler, event, eventData);\n const handlers = this.eventHandlers.get(event);\n if (handlers) for (const handler of handlers) this.runHandler(handler, eventData, callback);\n }\n\n handleBinary(data: any): void {\n if (!this.connected) return;\n const handlers = this.eventHandlers.get('binary');\n if (handlers) for (const handler of handlers) this.runHandler(handler, data);\n }\n\n handleDisconnect(reason?: any): void {\n for (const handler of this.disconnectHandlers) this.runHandler(handler, reason);\n }\n\n join(room: string | string[]): void {\n for (const r of Array.isArray(room) ? room : [room]) this.rooms.add(r);\n }\n\n leave(room: string | string[]): void {\n for (const r of Array.isArray(room) ? room : [room]) this.rooms.delete(r);\n }\n\n getRooms(): Set<string> {\n return new Set(this.rooms);\n }\n\n isInRoom(room: string): boolean {\n return this.rooms.has(room);\n }\n\n disconnect(_close?: boolean): void {\n if (!this.connected) return;\n this.connected = false;\n try {\n this.server.wsClose(this.wsId, 1000, '');\n } catch {\n // already gone\n }\n }\n\n to(room: string | string[]): WebSocketEmitter {\n return new EngineEmitterWrapper(this.allConnections(), {\n rooms: Array.isArray(room) ? room : [room],\n });\n }\n\n // Emit to everyone in the default namespace except this socket\n get broadcast(): WebSocketEmitter {\n return new EngineEmitterWrapper(this.allConnections(), { exceptId: this.id });\n }\n\n private allConnections(): Map<string, EngineConnectionWrapper> {\n return (this.namespaces.get('/') as any)?.['connections'] ?? new Map();\n }\n}\n\nclass EngineEmitterWrapper implements WebSocketEmitter {\n constructor(\n private connections: Map<string, EngineConnectionWrapper>,\n private filter: { rooms?: string[]; exceptRooms?: string[]; exceptId?: string }\n ) {}\n\n emit(event: string, data: any): void {\n for (const conn of this.connections.values()) {\n if (!conn.connected) continue;\n if (this.filter.exceptId && conn.id === this.filter.exceptId) continue;\n if (this.filter.rooms && !this.filter.rooms.some(r => conn.isInRoom(r))) continue;\n if (this.filter.exceptRooms && this.filter.exceptRooms.some(r => conn.isInRoom(r))) continue;\n conn.emit(event, data);\n }\n }\n\n to(room: string | string[]): WebSocketEmitter {\n const rooms = Array.isArray(room) ? room : [room];\n return new EngineEmitterWrapper(this.connections, {\n ...this.filter,\n rooms: [...(this.filter.rooms ?? []), ...rooms],\n });\n }\n\n except(room: string | string[]): WebSocketEmitter {\n const rooms = Array.isArray(room) ? room : [room];\n return new EngineEmitterWrapper(this.connections, {\n ...this.filter,\n exceptRooms: [...(this.filter.exceptRooms ?? []), ...rooms],\n });\n }\n\n compress(_compress: boolean): WebSocketEmitter {\n return this; // native negotiation; chaining preserved\n }\n}\n"]}
@@ -89,6 +89,11 @@ export interface EngineCapabilities {
89
89
  * with one variable path segment, echoed as the body, answered inside the
90
90
  * engine. Used for param(name) handlers on bare routes. */
91
91
  paramRoutes: boolean;
92
+ /** setWsAutoUpgrade()/wsGetHeaders()/wsGetQuery()/wsGetRemoteAddress()
93
+ * exist (engine >= 1.1.15): the engine answers a valid WebSocket
94
+ * handshake itself and fires onWsOpen, so an upgrade never round-trips
95
+ * through onRequest; the handshake stays readable on the socket. */
96
+ wsAutoUpgrade: boolean;
92
97
  /** I/O transport in use ('epoll', 'uv', 'uring'), when the engine reports it. */
93
98
  transport?: string;
94
99
  }
@@ -86,6 +86,7 @@ const CAP_KEYS = [
86
86
  'callbackScope',
87
87
  'batchDispatch',
88
88
  'paramRoutes',
89
+ 'wsAutoUpgrade',
89
90
  ];
90
91
  const NO_CAPABILITIES = Object.freeze(Object.fromEntries(CAP_KEYS.map(k => [k, false])));
91
92
  const capabilitiesCache = new WeakMap();