{"version":3,"sources":["../../../../../src/server/adapters/express/index.ts"],"sourcesContent":["import http from 'http'\n\nimport cookie from 'cookie-parser'\nimport cors from 'cors'\nimport express from 'express'\nimport fileUpload from 'express-fileupload'\nimport { rateLimit } from 'express-rate-limit'\n// import slowDown from 'express-slow-down'\nimport helmet from 'helmet'\nimport { pinoHttp } from 'pino-http'\n\nimport { Instance } from '../../../instance'\nimport { configurable, getMediaDuration } from '../../../utilities'\nimport { Request, Response } from '../../requests'\nimport { StatusCodes, type IncomingFile, type MethodsEnum } from '../../types'\nimport { type RawResponder, Server, serverConfigPipe } from '../base'\n\nexport class ExpressServer extends configurable(serverConfigPipe, Server) {\n\t#app: express.Express\n\t#httpServer: http.Server\n\n\tprotected constructor(config: typeof ExpressServer.Config) {\n\t\tsuper(config)\n\t\tconst app = express()\n\t\tconst instance = Instance.get()\n\t\tconst httpServer = http.createServer(app)\n\t\tthis.#app = app\n\t\tthis.#httpServer = httpServer\n\n\t\tthis.setup(httpServer, config)\n\n\t\tapp.disable('x-powered-by')\n\t\tif (config.requests.log) app.use(pinoHttp({ logger: instance.log }))\n\t\tapp.use(express.json())\n\t\tapp.use(express.text())\n\t\tapp.use(cookie())\n\t\tapp.use(\n\t\t\thelmet({\n\t\t\t\tcrossOriginResourcePolicy: { policy: 'cross-origin' },\n\t\t\t\tcontentSecurityPolicy: false,\n\t\t\t}),\n\t\t)\n\t\tapp.use(cors(this.cors))\n\t\tapp.use(express.urlencoded({ extended: false }))\n\t\tif (config.publicPath) app.use(express.static(config.publicPath))\n\t\tapp.use(\n\t\t\tfileUpload({\n\t\t\t\tlimits: { fileSize: instance.settings.utils.maxFileUploadSizeInMb * 1024 * 1024 },\n\t\t\t\tuseTempFiles: false,\n\t\t\t}),\n\t\t)\n\t\tif (config.requests.rateLimit.enabled)\n\t\t\tapp.use(\n\t\t\t\trateLimit({\n\t\t\t\t\twindowMs: config.requests.rateLimit.periodInMs,\n\t\t\t\t\tlimit: config.requests.rateLimit.limit,\n\t\t\t\t\thandler: (_: express.Request, res: express.Response) =>\n\t\t\t\t\t\tres.status(StatusCodes.TooManyRequests).json([{ message: 'Too Many Requests' }]),\n\t\t\t\t}),\n\t\t\t)\n\t\t/* if (this.settings.slowdown.enabled) app.use(slowDown({\n\t\t\t\twindowMs: this.settings.slowdown.periodInMs,\n\t\t\t\tdelayAfter: this.settings.slowdown.delayAfter,\n\t\t\t\tdelayMs: this.settings.slowdown.delayInMs\n\t\t\t})) */\n\t}\n\n\tprotected async parseRequest(req: any) {\n\t\tconst files = Object.fromEntries(\n\t\t\tawait Promise.all(\n\t\t\t\tObject.entries(req.files ?? {}).map(async ([key, file]) => {\n\t\t\t\t\tconst uploads = Array.isArray(file) ? file : [file]\n\t\t\t\t\tconst fileArray: IncomingFile[] = await Promise.all(\n\t\t\t\t\t\tuploads.map(async (f: any) => ({\n\t\t\t\t\t\t\tname: f.name,\n\t\t\t\t\t\t\ttype: f.mimetype,\n\t\t\t\t\t\t\tsize: f.size,\n\t\t\t\t\t\t\tisTruncated: f.truncated,\n\t\t\t\t\t\t\tdata: f.data,\n\t\t\t\t\t\t\tduration: await getMediaDuration(f.data),\n\t\t\t\t\t\t})),\n\t\t\t\t\t)\n\t\t\t\t\treturn <const>[key, fileArray]\n\t\t\t\t}),\n\t\t\t),\n\t\t)\n\n\t\treturn new Request<any>({\n\t\t\tip: req.ip,\n\t\t\tbody: req.body ?? {},\n\t\t\tcookies: req.cookies ?? {},\n\t\t\tparams: req.params ?? {},\n\t\t\tquery: req.query ?? {},\n\t\t\tmethod: <any>req.method,\n\t\t\tpath: req.path,\n\t\t\theaders: req.headers,\n\t\t\tfiles,\n\t\t})\n\t}\n\n\tprotected async handleResponse(res: any, response: Response<any>) {\n\t\tif (!response.piped) {\n\t\t\tObject.entries(response.headers).forEach(([key, value]) => res.header(key, value as string))\n\t\t\tObject.entries(response.cookies).forEach(([key, { value, ...opts }]) => res.cookie(key, value, opts))\n\t\t\tconst type = response.body === null || response.body === undefined ? 'json' : 'send'\n\t\t\tres.status(response.status)[type](response.body).end()\n\t\t} else {\n\t\t\tresponse.body.pipe(res)\n\t\t}\n\t}\n\n\tprotected createRawResponder(req: express.Request, res: express.Response): RawResponder {\n\t\treturn this.createNodeRawResponder({ request: req, response: res })\n\t}\n\n\tprotected registerRoute(method: MethodsEnum, path: string, cb: (req: any, res: any) => Promise<void>) {\n\t\tthis.#app[method]?.(path, async (req: any, res: any) => cb(req, res))\n\t}\n\n\tprotected registerErrorHandler(cb: (error: Error, req: any, res: any) => Promise<void>) {\n\t\tthis.#app.use(async (err: any, req: any, res: any, _next: any) => cb(err, req, res))\n\t}\n\n\tprotected registerNotFoundHandler(cb: (req: any, res: any) => Promise<void>) {\n\t\tthis.#app.use(async (req: any, res: any, _next: any) => cb(req, res))\n\t}\n\n\tprotected startServer(port: number) {\n\t\treturn new Promise<boolean>((resolve: (s: boolean) => void, reject: (e: Error) => void) => {\n\t\t\ttry {\n\t\t\t\tconst server = this.#httpServer.listen({ host: '0.0.0.0', port }, async () => resolve(true))\n\t\t\t\tInstance.on('close', server.close)\n\t\t\t} catch (err) {\n\t\t\t\treject(<Error>err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, expect, test, vi } = import.meta.vitest\n\n\tconst testInstanceState = globalThis as typeof globalThis & { __equippedTestInstanceAliased?: boolean }\n\n\tfunction ensureTestInstance() {\n\t\tconst instance = Instance.maybeGet() ?? Instance.create({ app: { name: 'equipped-test' }, log: { level: 'silent' } })\n\t\tif (!testInstanceState.__equippedTestInstanceAliased) {\n\t\t\tinstance.alias('equipped-test')\n\t\t\ttestInstanceState.__equippedTestInstanceAliased = true\n\t\t}\n\t}\n\n\tfunction createExpressResponse() {\n\t\treturn {\n\t\t\tstatusCode: 200,\n\t\t\theadersSent: false,\n\t\t\twritableEnded: false,\n\t\t\tsetHeader: vi.fn(),\n\t\t\tend(chunk?: unknown) {\n\t\t\t\tvoid chunk\n\t\t\t\tthis.headersSent = true\n\t\t\t\tthis.writableEnded = true\n\t\t\t},\n\t\t}\n\t}\n\n\tclass TestExpressServer extends ExpressServer {\n\t\texposeRawResponder(req: express.Request, res: express.Response) {\n\t\t\treturn this.createRawResponder(req, res)\n\t\t}\n\t}\n\n\tdescribe('ExpressServer raw responder', () => {\n\t\ttest('passes the Express request and response to the raw Node handler', async () => {\n\t\t\tensureTestInstance()\n\t\t\tconst server = TestExpressServer.create({ port: 0, requests: { log: false } })\n\t\t\tconst req = { marker: 'request' } as unknown as express.Request\n\t\t\tconst res = createExpressResponse() as unknown as express.Response\n\t\t\tlet received: { request: unknown; response: unknown } | null = null\n\n\t\t\tawait server.exposeRawResponder(\n\t\t\t\treq,\n\t\t\t\tres,\n\t\t\t)(async (request, response) => {\n\t\t\t\treceived = { request, response }\n\t\t\t\tresponse.end('ok')\n\t\t\t})\n\n\t\t\texpect(received).toEqual({ request: req, response: res })\n\t\t})\n\t})\n}\n"],"mappings":"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","names":["http","cookie","cors","express","fileUpload","rateLimit","helmet","pinoHttp","ExpressServer","configurable","serverConfigPipe","Server","#app","#httpServer","config","app","express","instance","Instance","httpServer","http","pinoHttp","cookie","helmet","cors","fileUpload","rateLimit","_","res","StatusCodes","req","files","key","file","uploads","fileArray","f","getMediaDuration","Request","response","value","opts","type","method","path","cb","err","_next","port","resolve","reject","server"]}