diff --git a/.changeset/tall-comics-punch.md b/.changeset/tall-comics-punch.md new file mode 100644 index 000000000000..3d16bfb263f4 --- /dev/null +++ b/.changeset/tall-comics-punch.md @@ -0,0 +1,5 @@ +--- +"astro": patch +--- + +Simplifies internals that handle endpoints. diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts index 5403aef557a2..f4a7a249e4f2 100644 --- a/packages/astro/src/core/app/index.ts +++ b/packages/astro/src/core/app/index.ts @@ -25,7 +25,7 @@ import { createStylesheetElementSet, } from '../render/ssr-element.js'; import { matchRoute } from '../routing/match.js'; -import { EndpointNotFoundError, SSRRoutePipeline } from './ssrPipeline.js'; +import { SSRRoutePipeline } from './ssrPipeline.js'; import type { RouteInfo } from './types.js'; export { deserializeManifest } from './common.js'; @@ -272,28 +272,27 @@ export class App { } response = await this.#pipeline.renderRoute(renderContext, pageModule); } catch (err: any) { - if (err instanceof EndpointNotFoundError) { - return this.#renderError(request, { status: 404, response: err.originalResponse }); - } else { - this.#logger.error(null, err.stack || err.message || String(err)); - return this.#renderError(request, { status: 500 }); - } + this.#logger.error(null, err.stack || err.message || String(err)); + return this.#renderError(request, { status: 500 }); } - // endpoints do not participate in implicit rerouting - if (routeData.type === 'page' || routeData.type === 'redirect') { - if (REROUTABLE_STATUS_CODES.has(response.status)) { - return this.#renderError(request, { - response, - status: response.status as 404 | 500, - }); - } + if (REROUTABLE_STATUS_CODES.has(response.status) && response.headers.get("X-Astro-Reroute") !== "no") { + return this.#renderError(request, { + response, + status: response.status as 404 | 500, + }); + } + + if (response.headers.has("X-Astro-Reroute")) { + response.headers.delete("X-Astro-Reroute"); } + if (addCookieHeader) { for (const setCookieHeaderValue of App.getSetCookieFromResponse(response)) { response.headers.append('set-cookie', setCookieHeaderValue); } } + Reflect.set(response, responseSentSymbol, true); return response; } diff --git a/packages/astro/src/core/app/ssrPipeline.ts b/packages/astro/src/core/app/ssrPipeline.ts index 94e8c9139cd2..f31636f9a51f 100644 --- a/packages/astro/src/core/app/ssrPipeline.ts +++ b/packages/astro/src/core/app/ssrPipeline.ts @@ -1,28 +1,3 @@ import { Pipeline } from '../pipeline.js'; -import type { Environment } from '../render/index.js'; -/** - * Thrown when an endpoint contains a response with the header "X-Astro-Response" === 'Not-Found' - */ -export class EndpointNotFoundError extends Error { - originalResponse: Response; - constructor(originalResponse: Response) { - super(); - this.originalResponse = originalResponse; - } -} - -export class SSRRoutePipeline extends Pipeline { - constructor(env: Environment) { - super(env); - this.setEndpointHandler(this.#ssrEndpointHandler); - } - - // This function is responsible for handling the result coming from an endpoint. - async #ssrEndpointHandler(request: Request, response: Response): Promise { - if (response.headers.get('X-Astro-Response') === 'Not-Found') { - throw new EndpointNotFoundError(response); - } - return response; - } -} +export class SSRRoutePipeline extends Pipeline {} diff --git a/packages/astro/src/core/build/buildPipeline.ts b/packages/astro/src/core/build/buildPipeline.ts index 166e42a2f8cb..c5a499d7023a 100644 --- a/packages/astro/src/core/build/buildPipeline.ts +++ b/packages/astro/src/core/build/buildPipeline.ts @@ -67,7 +67,6 @@ export class BuildPipeline extends Pipeline { this.#internals = internals; this.#staticBuildOptions = staticBuildOptions; this.#manifest = manifest; - this.setEndpointHandler(this.#handleEndpointResult); } getInternals(): Readonly { @@ -193,8 +192,4 @@ export class BuildPipeline extends Pipeline { return pages; } - - async #handleEndpointResult(_: Request, response: Response): Promise { - return response; - } } diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index a7f6425cd7e2..e3b93ea44f62 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -487,7 +487,8 @@ async function generatePath( ) { const { mod, scripts: hoistedScripts, styles: _styles } = gopts; const manifest = pipeline.getManifest(); - pipeline.getEnvironment().logger.debug('build', `Generating: ${pathname}`); + const logger = pipeline.getLogger(); + logger.debug('build', `Generating: ${pathname}`); const links = new Set(); const scripts = createModuleScriptsSet( diff --git a/packages/astro/src/core/pipeline.ts b/packages/astro/src/core/pipeline.ts index 67081a47e93f..570d3b9ff266 100644 --- a/packages/astro/src/core/pipeline.ts +++ b/packages/astro/src/core/pipeline.ts @@ -4,10 +4,6 @@ import { callMiddleware } from './middleware/callMiddleware.js'; import { renderPage } from './render/core.js'; import { type Environment, type RenderContext } from './render/index.js'; -type EndpointResultHandler = ( - originalRequest: Request, - result: Response -) => Promise | Response; type PipelineHooks = { before: PipelineHookFunction[]; @@ -26,11 +22,6 @@ export class Pipeline { #hooks: PipelineHooks = { before: [], }; - /** - * The handler accepts the *original* `Request` and result returned by the endpoint. - * It must return a `Response`. - */ - #endpointHandler?: EndpointResultHandler; /** * When creating a pipeline, an environment is mandatory. @@ -42,15 +33,6 @@ export class Pipeline { setEnvironment() {} - /** - * When rendering a route, an "endpoint" will a type that needs to be handled and transformed into a `Response`. - * - * Each consumer might have different needs; use this function to set up the handler. - */ - setEndpointHandler(handler: EndpointResultHandler) { - this.#endpointHandler = handler; - } - /** * A middleware function that will be called before each request. */ @@ -81,22 +63,12 @@ export class Pipeline { for (const hook of this.#hooks.before) { hook(renderContext, componentInstance); } - const result = await this.#tryRenderRoute( + return await this.#tryRenderRoute( renderContext, this.env, componentInstance, this.#onRequest ); - if (renderContext.route.type === 'endpoint') { - if (!this.#endpointHandler) { - throw new Error( - 'You created a pipeline that does not know how to handle the result coming from an endpoint.' - ); - } - return this.#endpointHandler(renderContext.request, result); - } else { - return result; - } } /** diff --git a/packages/astro/src/runtime/server/endpoint.ts b/packages/astro/src/runtime/server/endpoint.ts index 851d9bf0906d..534133945630 100644 --- a/packages/astro/src/runtime/server/endpoint.ts +++ b/packages/astro/src/runtime/server/endpoint.ts @@ -33,16 +33,14 @@ export async function renderEndpoint( ? `One of the exported handlers is "all" (lowercase), did you mean to export 'ALL'?\n` : '') ); - // No handler found, so this should be a 404. Using a custom header - // to signal to the renderer that this is an internal 404 that should - // be handled by a custom 404 route if possible. - return new Response(null, { - status: 404, - headers: { - 'X-Astro-Response': 'Not-Found', - }, - }); + // No handler matching the verb found, so this should be a + // 404. Should be handled by 404.astro route if possible. + return new Response(null, { status: 404 }); } - return handler.call(mod, context); + const response = await handler.call(mod, context); + // Endpoints explicitly returning 404 or 500 response status should + // NOT be subject to rerouting to 404.astro or 500.astro. + response.headers.set("X-Astro-Reroute", "no"); + return response; } diff --git a/packages/astro/src/vite-plugin-astro-server/devPipeline.ts b/packages/astro/src/vite-plugin-astro-server/devPipeline.ts index d9758f4783f2..409851eafdc5 100644 --- a/packages/astro/src/vite-plugin-astro-server/devPipeline.ts +++ b/packages/astro/src/vite-plugin-astro-server/devPipeline.ts @@ -35,7 +35,6 @@ export default class DevPipeline extends Pipeline { this.#devLogger = logger; this.#settings = settings; this.#loader = loader; - this.setEndpointHandler(this.#handleEndpointResult); } clearRouteCache() { @@ -88,9 +87,5 @@ export default class DevPipeline extends Pipeline { }); } - async #handleEndpointResult(_: Request, response: Response): Promise { - return response; - } - async handleFallback() {} }