@tsed/platform-koa
Version:
Koa package for Ts.ED framework
161 lines (160 loc) • 5.26 kB
JavaScript
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";
// @ts-ignore
KoaRouter.prototype.$$match = KoaRouter.prototype.match;
KoaRouter.prototype.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);
layers.forEach((layer) => {
const { path, wildcard } = convertPath(layer.path);
layer.path = path;
if (layer.method === "statics") {
rawRouter.use(path, this.statics(layer.path, layer.opts));
return;
}
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
}
]);