UNPKG

@curveball/kernel

Version:

Curveball is a framework writting in Typescript for Node.js

123 lines 4.04 kB
import { EventEmitter } from 'node:events'; import { isHttpError } from '@curveball/http-errors'; import { Context } from './context.js'; import MemoryRequest from './memory-request.js'; import MemoryResponse from './memory-response.js'; import NotFoundMw from './middleware/not-found.js'; import { curveballResponseToFetchResponse, fetchRequestToCurveballRequest } from './fetch-util.js'; import { getGlobalOrigin } from './global-origin.js'; /** * Package version * * This line gets automatically replaced during the build phase */ export const VERSION = 'Curveball/1.0.0'; /** * The middleware-call Symbol is a special symbol that might exist as a * property on an object. * * If it exists, the object can be used as a middleware. */ const middlewareCall = Symbol('middleware-call'); export { middlewareCall }; // Calls a series of middlewares, in order. export async function invokeMiddlewares(ctx, fns) { if (fns.length === 0) { return; } const mw = fns[0]; let mwFunc; if (isMiddlewareObject(mw)) { mwFunc = mw[middlewareCall].bind(fns[0]); } else { mwFunc = mw; } return mwFunc(ctx, async () => { await invokeMiddlewares(ctx, fns.slice(1)); }); } function isMiddlewareObject(input) { return input[middlewareCall] !== undefined; } export default class Application extends EventEmitter { middlewares = []; /** * Add a middleware to the application. * * Middlewares are called in the order they are added. */ use(...middleware) { this.middlewares.push(...middleware); } /** * Handles a single request and calls all middleware. */ async handle(ctx) { ctx.response.headers.set('Server', VERSION); ctx.response.type = 'application/hal+json'; await invokeMiddlewares(ctx, [...this.middlewares, NotFoundMw]); } /** * Executes a request on the server using the standard browser Request and * Response objects from the fetch() standard. * * Node will probably provide these out of the box in Node 18. If you're on * an older version, you'll need a polyfill. * * A use-case for this is allowing test frameworks to make fetch-like * requests without actually having to go over the network. */ async fetch(request) { const response = await this.subRequest(await fetchRequestToCurveballRequest(request, this.origin)); return curveballResponseToFetchResponse(response); } async subRequest(arg1, path, headers, body = '') { let request; if (typeof arg1 === 'string') { request = new MemoryRequest(arg1, path, this.origin, headers, body); } else { request = arg1; } const context = new Context(request, new MemoryResponse(this.origin)); try { await this.handle(context); } catch (err) { console.error(err); if (this.listenerCount('error')) { this.emit('error', err); } if (isHttpError(err)) { context.response.status = err.httpStatus; } else { context.response.status = 500; } context.response.body = 'Uncaught exception. No middleware was defined to handle it. We got the following HTTP status: ' + context.response.status; } return context.response; } _origin; /** * The public base url of the application. * * This can be auto-detected, but will often be wrong when your server is * running behind a reverse proxy or load balancer. * * To provide this, set the process.env.PUBLIC_URI property. */ get origin() { if (this._origin) { return this._origin; } return getGlobalOrigin(); } set origin(baseUrl) { this._origin = new URL(baseUrl).origin; } } //# sourceMappingURL=application.js.map