UNPKG

@tsed/platform-koa

Version:
161 lines (160 loc) 5.28 kB
import "@tsed/platform-multer/koa"; import KoaRouter from "@koa/router"; import { catchAsyncError, isFunction } from "@tsed/core"; import { constant, inject, runInContext } from "@tsed/di"; import { PlatformExceptions } from "@tsed/platform-exceptions"; import { adapter, application, createContext, PlatformAdapter, PlatformBuilder, PlatformHandler, PlatformRequest, PlatformResponse } from "@tsed/platform-http"; import { PlatformHandlerType } from "@tsed/platform-router"; import Koa from "koa"; import koaBodyParser from "koa-bodyparser"; // @ts-ignore import koaQs from "koa-qs"; import { staticsMiddleware } from "../middlewares/staticsMiddleware.js"; import { PlatformKoaHandler } from "../services/PlatformKoaHandler.js"; import { PlatformKoaRequest } from "../services/PlatformKoaRequest.js"; import { PlatformKoaResponse } from "../services/PlatformKoaResponse.js"; import { convertPath } from "../utils/convertPath.js"; const koaRouterProto = KoaRouter.prototype; koaRouterProto.$$match = koaRouterProto.match; koaRouterProto.match = function match(...args) { const matched = this.$$match(...args); if (matched) { if (matched.path.length) { matched.route = true; } } return matched; }; /** * @platform * @koa */ export class PlatformKoa extends PlatformAdapter { constructor() { super(...arguments); this.NAME = "koa"; } /** * Create new serverless application. In this mode, the component scan are disabled. * @param module * @param settings */ static create(module, settings = {}) { return PlatformBuilder.create(module, { ...settings, adapter: PlatformKoa }); } /** * Bootstrap a server application * @param module * @param settings */ static bootstrap(module, settings = {}) { return PlatformBuilder.bootstrap(module, { ...settings, adapter: PlatformKoa }); } onInit() { this.app.getApp().silent = true; } mapLayers(layers) { const options = constant("koa.router", {}); const rawRouter = new KoaRouter(options); for (const layer of layers) { const { path, wildcard } = convertPath(layer.path); layer.path = path; if (layer.method === "statics") { rawRouter.use(path, this.statics(layer.path, layer.opts)); continue; } const handlers = layer.getArgs(false); if (wildcard === "*") { handlers.unshift(((koaContext, next) => { koaContext.request.params["*"] = koaContext.request.params["0"]; return next(); })); } rawRouter[layer.method](path, ...handlers); } application().getApp().use(rawRouter.routes()).use(rawRouter.allowedMethods()); } mapHandler(handler, metadata) { return async (koaContext, next) => { const { $ctx } = koaContext.request; $ctx.next = next; const error = await catchAsyncError(() => handler($ctx)); if (error) { $ctx.error = error; } if (metadata.type !== PlatformHandlerType.RESPONSE_FN) { return $ctx.next && $ctx.error ? $ctx.next($ctx.error) : $ctx.next(); } }; } useContext() { const invoke = createContext(); const platformExceptions = inject(PlatformExceptions); application().use((koaContext, next) => { const $ctx = invoke({ request: koaContext.request, response: koaContext.response, koaContext }); return runInContext($ctx, async () => { try { await $ctx.start(); await next(); const status = koaContext.status || 404; if (status === 404 && !$ctx.isDone()) { platformExceptions?.resourceNotFound($ctx); } } catch (error) { platformExceptions?.catch(error, $ctx); } finally { await $ctx.finish(); } }); }); } createApp() { const app = constant("koa.app") || new Koa(); koaQs(app, "extended"); return { app, callback() { return app.callback(); } }; } statics(endpoint, options) { return staticsMiddleware(options); } bodyParser(_, additionalOptions = {}) { const opts = constant(`koa.bodyParser`); let parser = koaBodyParser; let options = {}; if (isFunction(opts)) { parser = opts; options = {}; } return parser({ ...options, ...additionalOptions }); } } adapter(PlatformKoa, [ { token: PlatformResponse, useClass: PlatformKoaResponse }, { token: PlatformRequest, useClass: PlatformKoaRequest }, { token: PlatformHandler, useClass: PlatformKoaHandler } ]);