UNPKG

@universal-middleware/h3

Version:
151 lines (149 loc) 4.23 kB
// src/common.ts import { bindUniversal, contextSymbol, getAdapterRuntime, isBodyInit, mergeHeadersInto, nodeHeadersToWeb, universalSymbol } from "@universal-middleware/core"; import { defineResponseMiddleware, eventHandler, getResponseHeaders, getResponseStatus, getResponseStatusText, sendWebResponse, toWebRequest } from "h3"; var pendingMiddlewaresSymbol = Symbol.for("unPendingMiddlewares"); var wrappedResponseSymbol = Symbol.for("unWrappedResponse"); function memToWebRequest(event) { if (!event.web?.request) { event.web ??= {}; event.web.request = toWebRequest(event); } return event.web.request; } function createHandler(handlerFactory) { return (...args) => { const handler = handlerFactory(...args); return bindUniversal( handler, eventHandler(function universalHandlerH3(event) { const ctx = initContext(event); return this[universalSymbol](memToWebRequest(event), ctx, getRuntime(event)); }), eventHandler ); }; } var universalOnBeforeResponse = defineResponseMiddleware( async (event, response) => { if (response.body instanceof Error) return; if (event.context[wrappedResponseSymbol] !== false) return; event.context[wrappedResponseSymbol] = true; if (response.body instanceof Response) { mergeHeadersInto(response.body.headers, nodeHeadersToWeb(getResponseHeaders(event))); } else if (isBodyInit(response.body)) { response.body = new Response(response.body, { headers: nodeHeadersToWeb(getResponseHeaders(event)), status: getResponseStatus(event), statusText: getResponseStatusText(event) }); } else { throw new TypeError("Payload is not a Response or BodyInit compatible"); } const middlewares = event.context[pendingMiddlewaresSymbol]; delete event.context[pendingMiddlewaresSymbol]; const newResponse = await middlewares?.reduce( async (prev, curr) => { const p = await prev; const newR = await curr(p); return newR ?? p; }, Promise.resolve(response.body) ); if (newResponse) { await sendWebResponse(event, newResponse); } } ); function createMiddleware(middlewareFactory) { return (...args) => { const middleware = middlewareFactory(...args); return bindUniversal( middleware, eventHandler(async function universalMiddlewareH3(event) { const ctx = initContext(event); const response = await this[universalSymbol](memToWebRequest(event), ctx, getRuntime(event)); if (typeof response === "function") { event.context[pendingMiddlewaresSymbol] ??= []; event.context[wrappedResponseSymbol] = false; event.context[pendingMiddlewaresSymbol].push(response); } else if (response !== null && typeof response === "object") { if (response instanceof Response) { return response; } event.context[contextSymbol] = response; } }), eventHandler ); }; } function initContext(event) { event.context[contextSymbol] ??= {}; return event.context[contextSymbol]; } function getContext(event) { return event.context[contextSymbol]; } function getRuntime(event) { return getAdapterRuntime( "h3", { params: event.context.params, h3: event }, { req: event.node.req, res: event.node.res } ); } // src/router.ts import { apply as applyCore, getUniversal, UniversalRouter, universalSymbol as universalSymbol2 } from "@universal-middleware/core"; var UniversalH3Router = class extends UniversalRouter { #app; constructor(app) { super(false); this.#app = app; } use(middleware) { this.#app.use(createMiddleware(() => getUniversal(middleware))()); return this; } applyCatchAll() { this.#app.use(createHandler(() => this[universalSymbol2])()); return this; } }; function apply(app, middlewares) { app.options.onBeforeResponse = universalOnBeforeResponse; const router = new UniversalH3Router(app); applyCore(router, middlewares); } export { apply, createHandler, createMiddleware, getContext, universalOnBeforeResponse };