{"version":3,"sources":["../../../../../src/server/adapters/fastify/index.ts"],"sourcesContent":["import fastifyCookie from '@fastify/cookie'\nimport fastifyCors from '@fastify/cors'\nimport fastifyFormBody from '@fastify/formbody'\nimport fastifyHelmet from '@fastify/helmet'\nimport fastifyMultipart from '@fastify/multipart'\nimport fastifyRateLimit from '@fastify/rate-limit'\nimport fastifyStatic from '@fastify/static'\nimport Fastify, { type FastifyReply, type FastifyRequest } from 'fastify'\n// import fastifySlowDown from 'fastify-slow-down'\nimport qs from 'qs'\n\nimport { ValidationError } from '../../../errors'\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 FastifyServer extends configurable(serverConfigPipe, Server) {\n\t#app: ReturnType<typeof Fastify>\n\n\tprotected constructor(config: typeof FastifyServer.Config) {\n\t\tsuper(config)\n\t\tconst instance = Instance.get()\n\t\tconst app = Fastify({\n\t\t\tdisableRequestLogging: !config.requests.log,\n\t\t\tloggerInstance: config.requests.log ? instance.log : undefined,\n\t\t\tajv: { customOptions: { coerceTypes: false } },\n\t\t\trouterOptions: {\n\t\t\t\tignoreTrailingSlash: true,\n\t\t\t\tcaseSensitive: false,\n\t\t\t},\n\t\t\tschemaErrorFormatter: (errors, data) =>\n\t\t\t\tnew ValidationError(\n\t\t\t\t\terrors.map((error) => ({\n\t\t\t\t\t\tmessages: [error.message ?? ''],\n\t\t\t\t\t\tfield: `${data}${error.instancePath}`.replaceAll('/', '.'),\n\t\t\t\t\t})),\n\t\t\t\t),\n\t\t})\n\t\tthis.#app = app\n\n\t\tthis.setup(app.server, config)\n\n\t\tapp.decorateRequest('savedReq', null)\n\t\tapp.setValidatorCompiler(() => () => true)\n\t\tapp.setSerializerCompiler(() => (data) => JSON.stringify(data))\n\t\tif (config.publicPath) app.register(fastifyStatic, { root: config.publicPath })\n\t\tapp.register(fastifyCookie, {})\n\t\tapp.register(fastifyCors, this.cors)\n\t\tapp.register(fastifyFormBody, { parser: (str) => qs.parse(str) })\n\t\tapp.register(fastifyHelmet, { crossOriginResourcePolicy: { policy: 'cross-origin' }, contentSecurityPolicy: false })\n\t\tapp.register(fastifyMultipart, {\n\t\t\tattachFieldsToBody: 'keyValues',\n\t\t\tthrowFileSizeLimit: false,\n\t\t\tlimits: { fileSize: instance.settings.utils.maxFileUploadSizeInMb * 1024 * 1024 },\n\t\t\tonFile: async (f) => {\n\t\t\t\tconst buffer = await f.toBuffer()\n\t\t\t\tconst parsed: IncomingFile = {\n\t\t\t\t\tname: f.filename,\n\t\t\t\t\ttype: f.mimetype,\n\t\t\t\t\tsize: buffer.byteLength,\n\t\t\t\t\tisTruncated: f.file.truncated,\n\t\t\t\t\tdata: buffer,\n\t\t\t\t\tduration: await getMediaDuration(buffer),\n\t\t\t\t}\n\t\t\t\t// @ts-ignore\n\t\t\t\tf.value = parsed\n\t\t\t},\n\t\t})\n\t\t/* if (this.settings.slowdown.enabled) app.register(fastifySlowDown, {\n\t\t\t\ttimeWindow: this.settings.slowdown.periodInMs,\n\t\t\t\tdelayAfter: this.settings.slowdown.delayAfter,\n\t\t\t\tdelay: this.settings.slowdown.delayInMs\n\t\t\t}) */\n\t\tif (config.requests.rateLimit.enabled)\n\t\t\tapp.register(fastifyRateLimit, {\n\t\t\t\tmax: config.requests.rateLimit.limit,\n\t\t\t\ttimeWindow: config.requests.rateLimit.periodInMs,\n\t\t\t\terrorResponseBuilder: (_, context) => ({\n\t\t\t\t\tstatusCode: StatusCodes.TooManyRequests,\n\t\t\t\t\tmessage: JSON.stringify([{ message: `Too Many Requests. Retry in ${context.after}` }]),\n\t\t\t\t}),\n\t\t\t})\n\t}\n\n\tprotected async parseRequest(req: any) {\n\t\tconst { body, files } = excludeBufferKeys(req.body ?? {})\n\n\t\treturn new Request({\n\t\t\tip: req.ip,\n\t\t\tbody,\n\t\t\tcookies: req.cookies ?? {},\n\t\t\tparams: req.params ?? <any>{},\n\t\t\tquery: req.query ?? {},\n\t\t\tmethod: <any>req.method,\n\t\t\tpath: req.url,\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\tfor (const [key, { value, ...opts }] of Object.entries(response.cookies)) res = res.setCookie(key, value, opts)\n\t\tawait res.status(response.status).headers(response.headers).send(response.body)\n\t}\n\n\tprotected createRawResponder(req: FastifyRequest, res: FastifyReply): RawResponder {\n\t\treturn this.createNodeRawResponder({ request: req.raw, response: res.raw, beforeHandle: () => res.hijack() })\n\t}\n\n\tprotected registerRoute(method: MethodsEnum, path: string, cb: (req: any, res: any) => Promise<void>) {\n\t\tthis.#app.register(async (inst) => {\n\t\t\tinst.route({ url: path, method, handler: cb })\n\t\t})\n\t}\n\n\tprotected registerErrorHandler(cb: (error: Error, req: any, res: any) => Promise<void>) {\n\t\tthis.#app.setErrorHandler(cb)\n\t}\n\n\tprotected registerNotFoundHandler(cb: (req: any, res: any) => Promise<void>) {\n\t\tthis.#app.setNotFoundHandler(cb)\n\t}\n\n\tprotected async startServer(port: number) {\n\t\tawait this.#app.ready()\n\t\tawait this.#app.listen({ port, host: '0.0.0.0' })\n\t\tInstance.on('close', this.#app.close)\n\t\treturn true\n\t}\n}\n\nfunction excludeBufferKeys<T>(body: T) {\n\tif (typeof body !== 'object') return { body, files: {} }\n\tconst entries = Object.entries(body ?? {})\n\tconst isFile = (val: any) => (Array.isArray(val) ? isFile(val.at(0)) : Buffer.isBuffer(val?.data))\n\tconst fileEntries = entries.filter(([_, value]) => isFile(value)).map(([key, value]) => [key, Array.isArray(value) ? value : [value]])\n\tconst nonFileEntries = entries.filter(([_, value]) => !isFile(value))\n\treturn {\n\t\tbody: <T>Object.fromEntries(nonFileEntries),\n\t\tfiles: <Record<string, IncomingFile[]>>Object.fromEntries(fileEntries),\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 createRawResponse() {\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 TestFastifyServer extends FastifyServer {\n\t\texposeRawResponder(req: FastifyRequest, res: FastifyReply) {\n\t\t\treturn this.createRawResponder(req, res)\n\t\t}\n\t}\n\n\tdescribe('FastifyServer raw responder', () => {\n\t\ttest('hijacks the Fastify reply before passing Node raw request and response to the handler', async () => {\n\t\t\tensureTestInstance()\n\t\t\tconst server = TestFastifyServer.create({ port: 0, requests: { log: false } })\n\t\t\tconst rawRequest = { marker: 'request' }\n\t\t\tconst rawResponse = createRawResponse()\n\t\t\tconst reply = { raw: rawResponse, hijack: vi.fn() } as unknown as FastifyReply\n\t\t\tconst request = { raw: rawRequest } as unknown as FastifyRequest\n\t\t\tlet received: { request: unknown; response: unknown } | null = null\n\n\t\t\tawait server.exposeRawResponder(\n\t\t\t\trequest,\n\t\t\t\treply,\n\t\t\t)(async (req, res) => {\n\t\t\t\treceived = { request: req, response: res }\n\t\t\t\tres.end('ok')\n\t\t\t})\n\n\t\t\texpect(reply.hijack).toHaveBeenCalledOnce()\n\t\t\texpect(received).toEqual({ request: rawRequest, response: rawResponse })\n\t\t})\n\t})\n}\n"],"mappings":"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","names":["ensureTestInstance","createRawResponse"]}