import destr from "destr"; import { createApp, createRouter, fetchWithEvent, isEvent, lazyEventHandler, toNodeListener } from "h3"; import { createHooks } from "hookable"; import { Headers, createFetch } from "ofetch"; import { fetchNodeRequestHandler, callNodeRequestHandler } from "node-mock-http"; import { cachedEventHandler } from "./cache.mjs"; import { useRuntimeConfig } from "./config.mjs"; import { nitroAsyncContext } from "./context.mjs"; import { createRouteRulesHandler, getRouteRulesForPath } from "./route-rules.mjs"; import { normalizeFetchResponse } from "./utils.mjs"; import errorHandler from "#nitro-internal-virtual/error-handler"; import { plugins } from "#nitro-internal-virtual/plugins"; import { handlers } from "#nitro-internal-virtual/server-handlers"; function createNitroApp() { const config = useRuntimeConfig(); const hooks = createHooks(); const captureError = (error, context = {}) => { const promise = hooks.callHookParallel("error", error, context).catch((error_) => { console.error("Error while capturing another error", error_); }); if (context.event && isEvent(context.event)) { const errors = context.event.context.nitro?.errors; if (errors) { errors.push({ error, context }); } if (context.event.waitUntil) { context.event.waitUntil(promise); } } }; const h3App = createApp({ debug: destr(process.env.DEBUG), onError: (error, event) => { captureError(error, { event, tags: ["request"] }); return errorHandler(error, event); }, onRequest: async (event) => { event.context.nitro = event.context.nitro || { errors: [] }; const fetchContext = event.node.req?.__unenv__; if (fetchContext?._platform) { event.context = { _platform: fetchContext?._platform, // #3335 ...fetchContext._platform, ...event.context }; } if (!event.context.waitUntil && fetchContext?.waitUntil) { event.context.waitUntil = fetchContext.waitUntil; } event.fetch = (req, init) => fetchWithEvent(event, req, init, { fetch: localFetch }); event.$fetch = (req, init) => fetchWithEvent(event, req, init, { fetch: $fetch }); event.waitUntil = (promise) => { if (!event.context.nitro._waitUntilPromises) { event.context.nitro._waitUntilPromises = []; } event.context.nitro._waitUntilPromises.push(promise); if (event.context.waitUntil) { event.context.waitUntil(promise); } }; event.captureError = (error, context) => { captureError(error, { event, ...context }); }; await nitroApp.hooks.callHook("request", event).catch((error) => { captureError(error, { event, tags: ["request"] }); }); }, onBeforeResponse: async (event, response) => { await nitroApp.hooks.callHook("beforeResponse", event, response).catch((error) => { captureError(error, { event, tags: ["request", "response"] }); }); }, onAfterResponse: async (event, response) => { await nitroApp.hooks.callHook("afterResponse", event, response).catch((error) => { captureError(error, { event, tags: ["request", "response"] }); }); } }); const router = createRouter({ preemptive: true }); const nodeHandler = toNodeListener(h3App); const localCall = (aRequest) => callNodeRequestHandler(nodeHandler, aRequest); const localFetch = (input, init) => { if (!input.toString().startsWith("/")) { return globalThis.fetch(input, init); } return fetchNodeRequestHandler( nodeHandler, input, init ).then((response) => normalizeFetchResponse(response)); }; const $fetch = createFetch({ fetch: localFetch, Headers, defaults: { baseURL: config.app.baseURL } }); globalThis.$fetch = $fetch; h3App.use(createRouteRulesHandler({ localFetch })); for (const h of handlers) { let handler = h.lazy ? lazyEventHandler(h.handler) : h.handler; if (h.middleware || !h.route) { const middlewareBase = (config.app.baseURL + (h.route || "/")).replace( /\/+/g, "/" ); h3App.use(middlewareBase, handler); } else { const routeRules = getRouteRulesForPath( h.route.replace(/:\w+|\*\*/g, "_") ); if (routeRules.cache) { handler = cachedEventHandler(handler, { group: "nitro/routes", ...routeRules.cache }); } router.use(h.route, handler, h.method); } } h3App.use(config.app.baseURL, router.handler); if (import.meta._asyncContext) { const _handler = h3App.handler; h3App.handler = (event) => { const ctx = { event }; return nitroAsyncContext.callAsync(ctx, () => _handler(event)); }; } const app = { hooks, h3App, router, localCall, localFetch, captureError }; return app; } function runNitroPlugins(nitroApp2) { for (const plugin of plugins) { try { plugin(nitroApp2); } catch (error) { nitroApp2.captureError(error, { tags: ["plugin"] }); throw error; } } } export const nitroApp = createNitroApp(); export function useNitroApp() { return nitroApp; } runNitroPlugins(nitroApp);