UNPKG

@universal-middleware/fastify

Version:
353 lines (347 loc) 11.7 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // ../../node_modules/.pnpm/fastify-plugin@5.0.1/node_modules/fastify-plugin/lib/getPluginName.js var require_getPluginName = __commonJS({ "../../node_modules/.pnpm/fastify-plugin@5.0.1/node_modules/fastify-plugin/lib/getPluginName.js"(exports, module) { "use strict"; var fpStackTracePattern = /at\s{1}(?:.*\.)?plugin\s{1}.*\n\s*(.*)/; var fileNamePattern = /(\w*(\.\w*)*)\..*/; module.exports = function getPluginName(fn) { if (fn.name.length > 0) return fn.name; const stackTraceLimit = Error.stackTraceLimit; Error.stackTraceLimit = 10; try { throw new Error("anonymous function"); } catch (e) { Error.stackTraceLimit = stackTraceLimit; return extractPluginName(e.stack); } }; function extractPluginName(stack) { const m = stack.match(fpStackTracePattern); return m ? m[1].split(/[/\\]/).slice(-1)[0].match(fileNamePattern)[1] : "anonymous"; } module.exports.extractPluginName = extractPluginName; } }); // ../../node_modules/.pnpm/fastify-plugin@5.0.1/node_modules/fastify-plugin/lib/toCamelCase.js var require_toCamelCase = __commonJS({ "../../node_modules/.pnpm/fastify-plugin@5.0.1/node_modules/fastify-plugin/lib/toCamelCase.js"(exports, module) { "use strict"; module.exports = function toCamelCase(name) { if (name[0] === "@") { name = name.slice(1).replace("/", "-"); } return name.replace(/-(.)/g, function(match, g1) { return g1.toUpperCase(); }); }; } }); // ../../node_modules/.pnpm/fastify-plugin@5.0.1/node_modules/fastify-plugin/plugin.js var require_plugin = __commonJS({ "../../node_modules/.pnpm/fastify-plugin@5.0.1/node_modules/fastify-plugin/plugin.js"(exports, module) { "use strict"; var getPluginName = require_getPluginName(); var toCamelCase = require_toCamelCase(); var count = 0; function plugin(fn, options = {}) { let autoName = false; if (fn.default !== void 0) { fn = fn.default; } if (typeof fn !== "function") { throw new TypeError( `fastify-plugin expects a function, instead got a '${typeof fn}'` ); } if (typeof options === "string") { options = { fastify: options }; } if (typeof options !== "object" || Array.isArray(options) || options === null) { throw new TypeError("The options object should be an object"); } if (options.fastify !== void 0 && typeof options.fastify !== "string") { throw new TypeError(`fastify-plugin expects a version string, instead got '${typeof options.fastify}'`); } if (!options.name) { autoName = true; options.name = getPluginName(fn) + "-auto-" + count++; } fn[Symbol.for("skip-override")] = options.encapsulate !== true; fn[Symbol.for("fastify.display-name")] = options.name; fn[Symbol.for("plugin-meta")] = options; if (!fn.default) { fn.default = fn; } const camelCase = toCamelCase(options.name); if (!autoName && !fn[camelCase]) { fn[camelCase] = fn; } return fn; } module.exports = plugin; module.exports.default = plugin; module.exports.fastifyPlugin = plugin; } }); // src/common.ts var import_fastify_plugin = __toESM(require_plugin(), 1); import { attachUniversal, bindUniversal, contextSymbol, getAdapterRuntime, isBodyInit, mergeHeadersInto, universalSymbol } from "@universal-middleware/core"; import { createRequestAdapter } from "@universal-middleware/express"; var pendingMiddlewaresSymbol = Symbol.for("unPendingMiddlewares"); var wrappedResponseSymbol = Symbol.for("unWrappedResponse"); function patchBody(response) { Object.defineProperty(response, "body", { value: new ReadableStream({ start(controller) { controller.close(); } }), writable: false, configurable: true }); return response; } function getHeaders(reply) { const ret = new Headers(); const headers = reply.getHeaders(); let setCookie = reply.getHeader("set-cookie"); if (typeof setCookie === "string") { setCookie = [setCookie]; } if (Array.isArray(setCookie)) { for (const cookie of setCookie) { ret.append("set-cookie", cookie); } } for (const [key, value] of Object.entries(headers)) { if (key === "set-cookie") continue; if (typeof value === "string") { ret.set(key, value); } else if (typeof value === "number") { ret.set(key, String(value)); } else if (Array.isArray(value)) { if (value.length === 1) { ret.set(key, value[0]); } else if (value.length > 1) { console.warn(`Header "${key}" should not be an array. Only last value will be sent`); ret.set(key, value.at(-1)); } } } return ret; } function getRawRequest(req) { if (!req.body || "rawBody" in req.raw) return req.raw; if ("rawBody" in req) { Object.defineProperty(req.raw, "rawBody", { get() { return req.rawBody; }, configurable: true, enumerable: true }); } else { Object.defineProperty(req.raw, "rawBody", { get() { throw new Error( "rawBody not Found. Please install fastify-raw-body plugin: https://github.com/Eomm/fastify-raw-body" ); }, configurable: true, enumerable: true }); } return req.raw; } function createHandler(handlerFactory) { const requestAdapter = createRequestAdapter(); return (...args) => { const handler = handlerFactory(...args); return bindUniversal(handler, async function universalHandlerFastify(request, reply) { const ctx = initContext(request); const response = await this[universalSymbol]( requestAdapter(getRawRequest(request)), ctx, getRuntime(request, reply) ); if (response) { if (!response.body) { patchBody(response); } return reply.send(response); } reply.callNotFound(); }); }; } function createMiddleware(middlewareFactory) { const requestAdapter = createRequestAdapter(); return (...args) => { const middleware = middlewareFactory(...args); return attachUniversal( middleware, (0, import_fastify_plugin.default)(async (instance) => { instance.addHook( "preHandler", bindUniversal( middleware, async function universalMiddlewareFastify(request, reply) { const ctx = initContext(request); const response = await this[universalSymbol]( requestAdapter(getRawRequest(request)), ctx, getRuntime(request, reply) ); if (!response) { return; } if (typeof response === "function") { if (reply.sent) { throw new Error( "Universal Middleware called after headers have been sent. Please open an issue at https://github.com/magne4000/universal-middleware" ); } request[pendingMiddlewaresSymbol] ??= []; request[wrappedResponseSymbol] = false; request[pendingMiddlewaresSymbol].push(response); } else if (response instanceof Response) { if (!response.body) { patchBody(response); } await reply.send(response); } else { setContext(request, response); } } ) ); instance.addHook("onSend", async (request, reply, payload) => { if (request[wrappedResponseSymbol] !== false) return payload; request[wrappedResponseSymbol] = true; if (payload instanceof Response) { mergeHeadersInto(payload.headers, getHeaders(reply)); } else if (isBodyInit(payload)) { payload = new Response(payload, { headers: new Headers(getHeaders(reply)), status: reply.statusCode }); } else if (payload !== void 0) { throw new TypeError("Payload is not a Response or BodyInit compatible"); } const middlewares = request[pendingMiddlewaresSymbol]; delete request[pendingMiddlewaresSymbol]; if (!payload) { reply.callNotFound(); } const newResponse = await middlewares?.reduce( async (prev, curr) => { const p = await prev; const newR = await curr(p); return newR ?? p; }, Promise.resolve(payload) ); const r = newResponse ?? payload; if (!r) { reply.callNotFound(); } else { return r; } }); }) ); }; } function initContext(req) { const config = req.routeOptions.config; config[contextSymbol] ??= {}; return config[contextSymbol]; } function getContext(req) { const config = req.routeOptions.config; return config[contextSymbol]; } function setContext(req, newContext) { const config = req.routeOptions.config; config[contextSymbol] = newContext; } function getRuntime(request, reply) { return getAdapterRuntime("fastify", { params: request.params, req: request.raw, res: reply.raw, fastify: Object.freeze({ request, reply }) }); } // src/router.ts import { applyAsync as applyAsyncCore, getUniversal, UniversalRouter, universalSymbol as universalSymbol2 } from "@universal-middleware/core"; var UniversalFastifyRouter = class extends UniversalRouter { #app; constructor(app) { super(false); this.#app = app; } // @ts-expect-error ReturnType mismatch with UniversalRouter async use(middleware) { this.#app.register(createMiddleware(() => getUniversal(middleware))()); return this; } // @ts-expect-error ReturnType mismatch with UniversalRouter async applyCatchAll() { this.#app.all("/*", createHandler(() => this[universalSymbol2])()); return this; } }; function apply(app, middlewares) { const router = new UniversalFastifyRouter(app); return applyAsyncCore(router, middlewares); } export { apply, createHandler, createMiddleware, getContext };