UNPKG

koas-core

Version:

> [Koa][] + [OpenAPI Specification][] = Koas

108 lines (107 loc) 4.09 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.koas = exports.SchemaValidationError = void 0; const compose = require("koa-compose"); const jsonRefs_1 = require("./jsonRefs"); const matcher_1 = require("./matcher"); const validation_1 = require("./validation"); Object.defineProperty(exports, "SchemaValidationError", { enumerable: true, get: function () { return validation_1.SchemaValidationError; } }); const methods = new Set([ 'delete', 'get', 'head', 'options', 'patch', 'post', 'put', 'trace', ]); /** * This symbol is used internally to specify a middleware should always be run. */ const RUN_ALWAYS = Symbol('This middleware is always run'); /** * Mark that middleware always needs to run, even if there is no matching OpenAPI operation. * * @param middleware - The middleware to mark. * @returns The marked middleware itself. */ function markRunAlways(middleware) { // @ts-expect-error This is an internal hack. // eslint-disable-next-line no-param-reassign middleware[RUN_ALWAYS] = true; return middleware; } /** * Create a Koa middleware from Koas middlewares. * * @param document - The OpenAPI document from which to create an API. * @param middlewares - The Koas middlewares to use for creating an API. * @param options - Advanced options * @returns Koa middleware that processes requests according the the OpenAPI document. */ function koas(document, middlewares = [], { onSchemaValidationError = (error) => ({ message: error.message, errors: error.result.errors }), } = {}) { const resolveRef = (0, jsonRefs_1.createResolver)(document); const matchers = Object.entries(document.paths).map(([pathTemplate, pathItemObject]) => { const matcher = (0, matcher_1.createMatcher)(pathTemplate, resolveRef, pathItemObject.parameters); return [matcher, pathItemObject]; }); const validator = (0, validation_1.createValidator)(document); const validate = (instance, schema, { message = 'JSON schema validation failed', preValidateProperty, rewrite, status, throw: throwError = true, } = {}) => { const result = validator.validate(instance, schema, { base: '#', nestedErrors: true, rewrite, preValidateProperty, }); if (throwError && !result.valid) { throw new validation_1.SchemaValidationError(message, { result, status }); } return result; }; const pluginOptions = { document, resolveRef, runAlways: markRunAlways, validate, }; const injected = middlewares.map((middleware) => middleware(pluginOptions)); const composed = compose(injected); // @ts-expect-error This is an internal hack. const runAlways = compose(injected.filter((middleware) => middleware[RUN_ALWAYS])); return async (ctx, next) => { let params; const match = matchers.find(([matcher]) => { params = matcher(ctx.path); return Boolean(params); }); ctx.openApi = { document, validate }; try { if (!match) { return await runAlways(ctx, next); } const [, pathItemObject] = match; ctx.openApi.pathItemObject = pathItemObject; ctx.params = params; const method = ctx.method.toLowerCase(); if (!methods.has(method)) { return await runAlways(ctx, next); } const operationObject = pathItemObject[method]; if (!operationObject) { return await runAlways(ctx, next); } ctx.openApi.operationObject = operationObject; await composed(ctx, next); } catch (error) { if (error instanceof validation_1.SchemaValidationError) { ctx.status = error.status; ctx.body = onSchemaValidationError(error, ctx); return; } throw error; } }; } exports.koas = koas;