astro 6.2.2 → 6.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/actions/handler.d.ts +32 -0
  2. package/dist/actions/handler.js +45 -0
  3. package/dist/actions/runtime/server.js +1 -1
  4. package/dist/assets/build/generate.js +1 -1
  5. package/dist/assets/build/remote.d.ts +3 -2
  6. package/dist/assets/build/remote.js +16 -9
  7. package/dist/assets/endpoint/generic.js +8 -20
  8. package/dist/assets/endpoint/loadImage.d.ts +11 -0
  9. package/dist/assets/endpoint/loadImage.js +19 -0
  10. package/dist/assets/endpoint/shared.js +7 -2
  11. package/dist/assets/index.d.ts +1 -0
  12. package/dist/assets/index.js +2 -0
  13. package/dist/assets/services/sharp.js +7 -0
  14. package/dist/assets/utils/index.d.ts +1 -0
  15. package/dist/assets/utils/index.js +2 -0
  16. package/dist/assets/utils/redirectValidation.d.ts +48 -0
  17. package/dist/assets/utils/redirectValidation.js +48 -0
  18. package/dist/assets/utils/remoteProbe.js +25 -2
  19. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  20. package/dist/container/index.js +18 -14
  21. package/dist/content/content-layer.js +3 -4
  22. package/dist/content/server-listeners.js +0 -4
  23. package/dist/content/vite-plugin-content-virtual-mod.js +9 -1
  24. package/dist/core/app/base.d.ts +33 -15
  25. package/dist/core/app/base.js +120 -324
  26. package/dist/core/app/dev/app.d.ts +3 -2
  27. package/dist/core/app/dev/app.js +4 -60
  28. package/dist/core/app/entrypoints/virtual/dev.js +2 -0
  29. package/dist/core/app/entrypoints/virtual/prod.js +4 -1
  30. package/dist/core/app/prepare-response.d.ts +11 -0
  31. package/dist/core/app/prepare-response.js +18 -0
  32. package/dist/core/app/render-options.d.ts +11 -0
  33. package/dist/core/app/render-options.js +11 -0
  34. package/dist/core/base-pipeline.d.ts +38 -1
  35. package/dist/core/base-pipeline.js +50 -7
  36. package/dist/core/build/app.d.ts +3 -4
  37. package/dist/core/build/app.js +3 -17
  38. package/dist/core/cache/handler.d.ts +29 -0
  39. package/dist/core/cache/handler.js +81 -0
  40. package/dist/core/config/schemas/base.d.ts +4 -0
  41. package/dist/core/config/schemas/base.js +4 -0
  42. package/dist/core/config/schemas/relative.d.ts +6 -0
  43. package/dist/core/constants.d.ts +27 -1
  44. package/dist/core/constants.js +14 -1
  45. package/dist/core/cookies/cookies.d.ts +7 -2
  46. package/dist/core/cookies/cookies.js +11 -4
  47. package/dist/core/cookies/response.d.ts +1 -1
  48. package/dist/core/cookies/response.js +1 -2
  49. package/dist/core/create-vite.js +15 -0
  50. package/dist/core/csp/runtime.js +6 -4
  51. package/dist/core/dev/dev.js +1 -1
  52. package/dist/core/errors/build-handler.d.ts +17 -0
  53. package/dist/core/errors/build-handler.js +22 -0
  54. package/dist/core/errors/default-handler.d.ts +14 -0
  55. package/dist/core/errors/default-handler.js +144 -0
  56. package/dist/core/errors/dev-handler.d.ts +21 -0
  57. package/dist/core/errors/dev-handler.js +82 -0
  58. package/dist/core/errors/handler.d.ts +9 -0
  59. package/dist/core/errors/handler.js +0 -0
  60. package/dist/core/fetch/default-handler.d.ts +17 -0
  61. package/dist/core/fetch/default-handler.js +45 -0
  62. package/dist/core/fetch/fetch-state.d.ts +244 -0
  63. package/dist/core/fetch/fetch-state.js +779 -0
  64. package/dist/core/fetch/index.d.ts +61 -0
  65. package/dist/core/fetch/index.js +121 -0
  66. package/dist/core/fetch/types.d.ts +6 -0
  67. package/dist/core/fetch/types.js +0 -0
  68. package/dist/core/fetch/vite-plugin.d.ts +5 -0
  69. package/dist/core/fetch/vite-plugin.js +69 -0
  70. package/dist/core/hono/index.d.ts +21 -0
  71. package/dist/core/hono/index.js +98 -0
  72. package/dist/core/i18n/handler.d.ts +18 -0
  73. package/dist/core/i18n/handler.js +119 -0
  74. package/dist/core/logger/core.d.ts +8 -0
  75. package/dist/core/logger/core.js +16 -0
  76. package/dist/core/messages/runtime.js +1 -1
  77. package/dist/core/middleware/astro-middleware.d.ts +27 -0
  78. package/dist/core/middleware/astro-middleware.js +53 -0
  79. package/dist/core/pages/handler.d.ts +20 -0
  80. package/dist/core/pages/handler.js +74 -0
  81. package/dist/core/preview/static-preview-server.js +3 -1
  82. package/dist/core/redirects/render.d.ts +2 -2
  83. package/dist/core/redirects/render.js +7 -8
  84. package/dist/core/rewrites/handler.d.ts +37 -0
  85. package/dist/core/rewrites/handler.js +67 -0
  86. package/dist/core/routing/3xx.js +8 -4
  87. package/dist/core/routing/handler.d.ts +17 -0
  88. package/dist/core/routing/handler.js +172 -0
  89. package/dist/core/routing/match.d.ts +0 -7
  90. package/dist/core/routing/match.js +0 -5
  91. package/dist/core/routing/trailing-slash-handler.d.ts +18 -0
  92. package/dist/core/routing/trailing-slash-handler.js +67 -0
  93. package/dist/core/session/drivers.d.ts +1 -1
  94. package/dist/core/session/handler.d.ts +11 -0
  95. package/dist/core/session/handler.js +33 -0
  96. package/dist/core/util/normalized-url.d.ts +10 -0
  97. package/dist/core/util/normalized-url.js +21 -0
  98. package/dist/i18n/middleware.d.ts +10 -0
  99. package/dist/i18n/middleware.js +4 -88
  100. package/dist/i18n/utils.js +2 -2
  101. package/dist/runtime/server/astro-island.js +57 -20
  102. package/dist/runtime/server/astro-island.prebuilt-dev.d.ts +1 -1
  103. package/dist/runtime/server/astro-island.prebuilt-dev.js +1 -1
  104. package/dist/runtime/server/astro-island.prebuilt.d.ts +1 -1
  105. package/dist/runtime/server/astro-island.prebuilt.js +1 -1
  106. package/dist/runtime/server/render/server-islands.js +2 -1
  107. package/dist/types/public/config.d.ts +46 -12
  108. package/dist/types/public/internal.d.ts +1 -1
  109. package/dist/vite-plugin-app/app.d.ts +4 -5
  110. package/dist/vite-plugin-app/app.js +20 -65
  111. package/package.json +11 -7
  112. package/dist/core/render-context.d.ts +0 -77
  113. package/dist/core/render-context.js +0 -826
@@ -35,6 +35,17 @@ export declare const NOOP_MIDDLEWARE_HEADER = "X-Astro-Noop";
35
35
  * The name for the header used to help i18n middleware, which only needs to act on "page" and "fallback" route types.
36
36
  */
37
37
  export declare const ROUTE_TYPE_HEADER = "X-Astro-Route-Type";
38
+ /**
39
+ * Internal headers that should be stripped from the response before
40
+ * sending it to the user agent. Add new internal headers here so
41
+ * `prepareResponse` removes them automatically.
42
+ */
43
+ export declare const INTERNAL_RESPONSE_HEADERS: readonly ["X-Astro-Reroute", "X-Astro-Rewrite", "X-Astro-Noop", "X-Astro-Route-Type"];
44
+ /**
45
+ * Set by internal handlers (e.g. PagesHandler) to signal that a
46
+ * response should be replaced with the corresponding error page.
47
+ */
48
+ export declare const ASTRO_ERROR_HEADER = "X-Astro-Error";
38
49
  /**
39
50
  * The value of the `component` field of the default 404 page, which is used when there is no user-provided 404.astro page.
40
51
  */
@@ -55,7 +66,7 @@ export declare const REROUTABLE_STATUS_CODES: number[];
55
66
  export declare const clientAddressSymbol: unique symbol;
56
67
  /**
57
68
  * The symbol used as a field on the request object to store the object to be made available to Astro APIs as `locals`.
58
- * Use judiciously, as locals are now stored within `RenderContext` by default. Tacking it onto request is no longer necessary.
69
+ * Use judiciously, as locals are now stored within `FetchState` by default. Tacking it onto request is no longer necessary.
59
70
  */
60
71
  export declare const clientLocalsSymbol: unique symbol;
61
72
  /**
@@ -66,6 +77,21 @@ export declare const originPathnameSymbol: unique symbol;
66
77
  * Use this symbol to set and retrieve the pipeline.
67
78
  */
68
79
  export declare const pipelineSymbol: unique symbol;
80
+ /**
81
+ * Use this symbol to stash the active `FetchState` on an `APIContext`
82
+ * (or `ActionAPIContext`). Consumed by internal shims that need access
83
+ * to per-request state without appearing in the public context shape
84
+ * — e.g. the manual-strategy i18n middleware wrapper in
85
+ * `src/i18n/middleware.ts`.
86
+ */
87
+ export declare const fetchStateSymbol: unique symbol;
88
+ /**
89
+ * Use this symbol to stash the `BaseApp` on an incoming `Request` at the
90
+ * top of the pipeline. Fetch handlers loaded from `virtual:astro:fetchable`
91
+ * (including `DefaultFetchHandler`) read it to find the app associated
92
+ * with the current request without needing App passed to their constructor.
93
+ */
94
+ export declare const appSymbol: unique symbol;
69
95
  /**
70
96
  * Use this symbol to opt into handling prerender routes in Astro core dev middleware.
71
97
  */
@@ -1,10 +1,17 @@
1
- const ASTRO_VERSION = "6.2.2";
1
+ const ASTRO_VERSION = "6.3.1";
2
2
  const ASTRO_GENERATOR = `Astro v${ASTRO_VERSION}`;
3
3
  const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
4
4
  const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite";
5
5
  const REWRITE_DIRECTIVE_HEADER_VALUE = "yes";
6
6
  const NOOP_MIDDLEWARE_HEADER = "X-Astro-Noop";
7
7
  const ROUTE_TYPE_HEADER = "X-Astro-Route-Type";
8
+ const INTERNAL_RESPONSE_HEADERS = [
9
+ REROUTE_DIRECTIVE_HEADER,
10
+ REWRITE_DIRECTIVE_HEADER_KEY,
11
+ NOOP_MIDDLEWARE_HEADER,
12
+ ROUTE_TYPE_HEADER
13
+ ];
14
+ const ASTRO_ERROR_HEADER = "X-Astro-Error";
8
15
  const DEFAULT_404_COMPONENT = "astro-default-404.astro";
9
16
  const REDIRECT_STATUS_CODES = [301, 302, 303, 307, 308, 300, 304];
10
17
  const REROUTABLE_STATUS_CODES = [404, 500];
@@ -12,6 +19,8 @@ const clientAddressSymbol = /* @__PURE__ */ Symbol.for("astro.clientAddress");
12
19
  const clientLocalsSymbol = /* @__PURE__ */ Symbol.for("astro.locals");
13
20
  const originPathnameSymbol = /* @__PURE__ */ Symbol.for("astro.originPathname");
14
21
  const pipelineSymbol = /* @__PURE__ */ Symbol.for("astro.pipeline");
22
+ const fetchStateSymbol = /* @__PURE__ */ Symbol.for("astro.fetchState");
23
+ const appSymbol = /* @__PURE__ */ Symbol.for("astro.app");
15
24
  const devPrerenderMiddlewareSymbol = /* @__PURE__ */ Symbol.for("astro.devPrerenderMiddleware");
16
25
  const nodeRequestAbortControllerCleanupSymbol = /* @__PURE__ */ Symbol.for(
17
26
  "astro.nodeRequestAbortControllerCleanup"
@@ -43,10 +52,12 @@ const ASTRO_VITE_ENVIRONMENT_NAMES = {
43
52
  prerender: "prerender"
44
53
  };
45
54
  export {
55
+ ASTRO_ERROR_HEADER,
46
56
  ASTRO_GENERATOR,
47
57
  ASTRO_VERSION,
48
58
  ASTRO_VITE_ENVIRONMENT_NAMES,
49
59
  DEFAULT_404_COMPONENT,
60
+ INTERNAL_RESPONSE_HEADERS,
50
61
  MIDDLEWARE_PATH_SEGMENT_NAME,
51
62
  NOOP_MIDDLEWARE_HEADER,
52
63
  REDIRECT_STATUS_CODES,
@@ -56,9 +67,11 @@ export {
56
67
  REWRITE_DIRECTIVE_HEADER_VALUE,
57
68
  ROUTE_TYPE_HEADER,
58
69
  SUPPORTED_MARKDOWN_FILE_EXTENSIONS,
70
+ appSymbol,
59
71
  clientAddressSymbol,
60
72
  clientLocalsSymbol,
61
73
  devPrerenderMiddlewareSymbol,
74
+ fetchStateSymbol,
62
75
  nodeRequestAbortControllerCleanupSymbol,
63
76
  originPathnameSymbol,
64
77
  pipelineSymbol,
@@ -72,8 +72,13 @@ declare class AstroCookies implements AstroCookiesInterface {
72
72
  */
73
73
  headers(): Generator<string, void, unknown>;
74
74
  /**
75
- * Behaves the same as AstroCookies.prototype.headers(),
76
- * but allows a warning when cookies are set after the instance is consumed.
75
+ * Marks the cookies as consumed and returns the header values.
76
+ * After consumption, any subsequent `set()` calls will warn.
77
+ */
78
+ consume(): Generator<string, void, unknown>;
79
+ /**
80
+ * @deprecated Use the instance method `cookies.consume()` instead.
81
+ * Kept for backward compatibility with adapters.
77
82
  */
78
83
  static consume(cookies: AstroCookies): Generator<string, void, unknown>;
79
84
  }
@@ -173,12 +173,19 @@ class AstroCookies {
173
173
  }
174
174
  }
175
175
  /**
176
- * Behaves the same as AstroCookies.prototype.headers(),
177
- * but allows a warning when cookies are set after the instance is consumed.
176
+ * Marks the cookies as consumed and returns the header values.
177
+ * After consumption, any subsequent `set()` calls will warn.
178
+ */
179
+ consume() {
180
+ this.#consumed = true;
181
+ return this.headers();
182
+ }
183
+ /**
184
+ * @deprecated Use the instance method `cookies.consume()` instead.
185
+ * Kept for backward compatibility with adapters.
178
186
  */
179
187
  static consume(cookies) {
180
- cookies.#consumed = true;
181
- return cookies.headers();
188
+ return cookies.consume();
182
189
  }
183
190
  #ensureParsed() {
184
191
  if (!this.#requestValues) {
@@ -1,4 +1,4 @@
1
- import { AstroCookies } from './cookies.js';
1
+ import type { AstroCookies } from './cookies.js';
2
2
  export declare function attachCookiesToResponse(response: Response, cookies: AstroCookies): void;
3
3
  export declare function getCookiesFromResponse(response: Response): AstroCookies | undefined;
4
4
  export declare function getSetCookiesFromResponse(response: Response): Generator<string, string[]>;
@@ -1,4 +1,3 @@
1
- import { AstroCookies } from "./cookies.js";
2
1
  const astroCookiesSymbol = /* @__PURE__ */ Symbol.for("astro.cookies");
3
2
  function attachCookiesToResponse(response, cookies) {
4
3
  Reflect.set(response, astroCookiesSymbol, cookies);
@@ -16,7 +15,7 @@ function* getSetCookiesFromResponse(response) {
16
15
  if (!cookies) {
17
16
  return [];
18
17
  }
19
- for (const headerValue of AstroCookies.consume(cookies)) {
18
+ for (const headerValue of cookies.consume()) {
20
19
  yield headerValue;
21
20
  }
22
21
  return [];
@@ -23,6 +23,7 @@ import astroDevToolbar from "../toolbar/vite-plugin-dev-toolbar.js";
23
23
  import astroTransitions from "../transitions/vite-plugin-transitions.js";
24
24
  import { vitePluginAdapterConfig } from "../vite-plugin-adapter-config/index.js";
25
25
  import { vitePluginApp } from "../vite-plugin-app/index.js";
26
+ import { vitePluginFetchable } from "./fetch/vite-plugin.js";
26
27
  import astroVitePlugin from "../vite-plugin-astro/index.js";
27
28
  import { vitePluginAstroServer } from "../vite-plugin-astro-server/index.js";
28
29
  import configAliasVitePlugin from "../vite-plugin-config-alias/index.js";
@@ -118,6 +119,19 @@ async function createVite(commandConfig, { settings, logger, mode, command, fs =
118
119
  customLogger: createViteLogger(logger, settings.config.vite.logLevel),
119
120
  appType: "custom",
120
121
  plugins: [
122
+ // Raise the watcher's maxListeners limit before any other plugin's
123
+ // configureServer hook can add listeners. Astro registers 12+ change
124
+ // listeners across its built-in Vite plugins, easily exceeding
125
+ // Node's default limit of 10.
126
+ {
127
+ name: "astro:watcher-max-listeners",
128
+ configureServer(server) {
129
+ const current = server.watcher.getMaxListeners();
130
+ if (current !== 0 && current < 50) {
131
+ server.watcher.setMaxListeners(50);
132
+ }
133
+ }
134
+ },
121
135
  serializedManifestPlugin({ settings, command, sync }),
122
136
  vitePluginRenderers({
123
137
  settings,
@@ -138,6 +152,7 @@ async function createVite(commandConfig, { settings, logger, mode, command, fs =
138
152
  // The server plugin is for dev only and having it run during the build causes
139
153
  // the build to run very slow as the filewatcher is triggered often.
140
154
  vitePluginApp(),
155
+ vitePluginFetchable({ settings }),
141
156
  command === "dev" && vitePluginAstroServer({ settings, logger }),
142
157
  command === "dev" && vitePluginAstroServerClient(),
143
158
  astroDevCssPlugin({ routesList, command }),
@@ -8,25 +8,27 @@ function deduplicateDirectiveValues(existingDirective, newDirective) {
8
8
  return `${directiveName} ${finalDirectives.join(" ")}`;
9
9
  }
10
10
  function pushDirective(directives, newDirective) {
11
- let deduplicated = false;
12
11
  if (directives.length === 0) {
13
12
  return [newDirective];
14
13
  }
15
14
  const finalDirectives = [];
15
+ let matched = false;
16
16
  for (const directive of directives) {
17
- if (deduplicated) {
17
+ if (matched) {
18
18
  finalDirectives.push(directive);
19
19
  continue;
20
20
  }
21
21
  const result = deduplicateDirectiveValues(directive, newDirective);
22
22
  if (result) {
23
23
  finalDirectives.push(result);
24
- deduplicated = true;
24
+ matched = true;
25
25
  } else {
26
26
  finalDirectives.push(directive);
27
- finalDirectives.push(newDirective);
28
27
  }
29
28
  }
29
+ if (!matched) {
30
+ finalDirectives.push(newDirective);
31
+ }
30
32
  return finalDirectives;
31
33
  }
32
34
  export {
@@ -37,7 +37,7 @@ async function dev(inlineConfig) {
37
37
  await telemetry.record([]);
38
38
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
39
39
  const logger = restart.container.logger;
40
- const currentVersion = "6.2.2";
40
+ const currentVersion = "6.3.1";
41
41
  const isPrerelease = currentVersion.includes("-");
42
42
  if (!isPrerelease) {
43
43
  try {
@@ -0,0 +1,17 @@
1
+ import type { BaseApp, RenderErrorOptions } from '../app/base.js';
2
+ import type { Pipeline } from '../base-pipeline.js';
3
+ import type { ErrorHandler } from './handler.js';
4
+ /**
5
+ * The error handler used during static build / prerendering.
6
+ *
7
+ * - For 500 errors, returns the original response if present, otherwise
8
+ * throws so the build surfaces the underlying error to the developer.
9
+ * - For other errors (e.g. 404), delegates to `DefaultErrorHandler` with
10
+ * `prerenderedErrorPageFetch` cleared (the build pipeline can't fetch
11
+ * prerendered pages the way production SSR can).
12
+ */
13
+ export declare class BuildErrorHandler implements ErrorHandler {
14
+ #private;
15
+ constructor(app: BaseApp<Pipeline>);
16
+ renderError(request: Request, options: RenderErrorOptions): Promise<Response>;
17
+ }
@@ -0,0 +1,22 @@
1
+ import { DefaultErrorHandler } from "./default-handler.js";
2
+ class BuildErrorHandler {
3
+ #default;
4
+ constructor(app) {
5
+ this.#default = new DefaultErrorHandler(app);
6
+ }
7
+ async renderError(request, options) {
8
+ if (options.status === 500) {
9
+ if (options.response) {
10
+ return options.response;
11
+ }
12
+ throw options.error;
13
+ }
14
+ return this.#default.renderError(request, {
15
+ ...options,
16
+ prerenderedErrorPageFetch: void 0
17
+ });
18
+ }
19
+ }
20
+ export {
21
+ BuildErrorHandler
22
+ };
@@ -0,0 +1,14 @@
1
+ import type { BaseApp, RenderErrorOptions } from '../app/base.js';
2
+ import type { Pipeline } from '../base-pipeline.js';
3
+ import type { ErrorHandler } from './handler.js';
4
+ /**
5
+ * The default error handler used in production SSR. Attempts to render the
6
+ * matching error route (404.astro / 500.astro), falling back to a plain
7
+ * response with the given status. Handles prerendered error pages via
8
+ * `prerenderedErrorPageFetch`.
9
+ */
10
+ export declare class DefaultErrorHandler implements ErrorHandler {
11
+ #private;
12
+ constructor(app: BaseApp<Pipeline>);
13
+ renderError(request: Request, { status, response: originalResponse, skipMiddleware, error, pathname, ...resolvedRenderOptions }: RenderErrorOptions): Promise<Response>;
14
+ }
@@ -0,0 +1,144 @@
1
+ import { FetchState } from "../fetch/fetch-state.js";
2
+ import { prepareResponse } from "../app/prepare-response.js";
3
+ import { attachCookiesToResponse } from "../cookies/index.js";
4
+ import { getCookiesFromResponse } from "../cookies/response.js";
5
+ import { AstroMiddleware } from "../middleware/astro-middleware.js";
6
+ import { PagesHandler } from "../pages/handler.js";
7
+ import { matchRoute } from "../routing/match.js";
8
+ import { provideSession } from "../session/handler.js";
9
+ class DefaultErrorHandler {
10
+ #app;
11
+ #astroMiddleware;
12
+ #pagesHandler;
13
+ constructor(app) {
14
+ this.#app = app;
15
+ this.#astroMiddleware = new AstroMiddleware(app.pipeline);
16
+ this.#pagesHandler = new PagesHandler(app.pipeline);
17
+ }
18
+ async renderError(request, {
19
+ status,
20
+ response: originalResponse,
21
+ skipMiddleware = false,
22
+ error,
23
+ pathname,
24
+ ...resolvedRenderOptions
25
+ }) {
26
+ const app = this.#app;
27
+ const resolvedPathname = pathname ?? new FetchState(app.pipeline, request).pathname;
28
+ const errorRoutePath = `/${status}${app.manifest.trailingSlash === "always" ? "/" : ""}`;
29
+ const errorRouteData = matchRoute(errorRoutePath, app.manifestData);
30
+ const url = new URL(request.url);
31
+ if (errorRouteData) {
32
+ if (errorRouteData.prerender) {
33
+ const maybeDotHtml = errorRouteData.route.endsWith(`/${status}`) ? ".html" : "";
34
+ const statusURL = new URL(`${app.baseWithoutTrailingSlash}/${status}${maybeDotHtml}`, url);
35
+ if (statusURL.toString() !== request.url && resolvedRenderOptions.prerenderedErrorPageFetch) {
36
+ const response2 = await resolvedRenderOptions.prerenderedErrorPageFetch(
37
+ statusURL.toString()
38
+ );
39
+ const override = { status, removeContentEncodingHeaders: true };
40
+ const newResponse = mergeResponses(response2, originalResponse, override);
41
+ prepareResponse(newResponse, resolvedRenderOptions);
42
+ return newResponse;
43
+ }
44
+ }
45
+ const mod = await app.pipeline.getComponentByRoute(errorRouteData);
46
+ const errorState = new FetchState(app.pipeline, request);
47
+ errorState.skipMiddleware = skipMiddleware;
48
+ errorState.clientAddress = resolvedRenderOptions.clientAddress;
49
+ errorState.routeData = errorRouteData;
50
+ errorState.pathname = resolvedPathname;
51
+ errorState.status = status;
52
+ errorState.componentInstance = mod;
53
+ errorState.locals = resolvedRenderOptions.locals ?? {};
54
+ errorState.initialProps = { error };
55
+ try {
56
+ await provideSession(errorState);
57
+ const response2 = await this.#astroMiddleware.handle(
58
+ errorState,
59
+ this.#pagesHandler.handle.bind(this.#pagesHandler)
60
+ );
61
+ const newResponse = mergeResponses(response2, originalResponse);
62
+ prepareResponse(newResponse, resolvedRenderOptions);
63
+ return newResponse;
64
+ } catch {
65
+ if (skipMiddleware === false) {
66
+ return this.renderError(request, {
67
+ ...resolvedRenderOptions,
68
+ status,
69
+ response: originalResponse,
70
+ skipMiddleware: true,
71
+ pathname: resolvedPathname
72
+ });
73
+ }
74
+ } finally {
75
+ await errorState.finalizeAll();
76
+ }
77
+ }
78
+ const response = mergeResponses(new Response(null, { status }), originalResponse);
79
+ prepareResponse(response, resolvedRenderOptions);
80
+ return response;
81
+ }
82
+ }
83
+ function mergeResponses(newResponse, originalResponse, override) {
84
+ let newResponseHeaders = newResponse.headers;
85
+ if (override?.removeContentEncodingHeaders) {
86
+ newResponseHeaders = new Headers(newResponseHeaders);
87
+ newResponseHeaders.delete("Content-Encoding");
88
+ newResponseHeaders.delete("Content-Length");
89
+ }
90
+ if (!originalResponse) {
91
+ if (override !== void 0) {
92
+ return new Response(newResponse.body, {
93
+ status: override.status,
94
+ statusText: newResponse.statusText,
95
+ headers: newResponseHeaders
96
+ });
97
+ }
98
+ return newResponse;
99
+ }
100
+ const status = override?.status ? override.status : originalResponse.status === 200 ? newResponse.status : originalResponse.status;
101
+ try {
102
+ originalResponse.headers.delete("Content-type");
103
+ originalResponse.headers.delete("Content-Length");
104
+ originalResponse.headers.delete("Transfer-Encoding");
105
+ } catch {
106
+ }
107
+ const newHeaders = new Headers();
108
+ const seen = /* @__PURE__ */ new Set();
109
+ for (const [name, value] of originalResponse.headers) {
110
+ newHeaders.append(name, value);
111
+ seen.add(name.toLowerCase());
112
+ }
113
+ for (const [name, value] of newResponseHeaders) {
114
+ if (!seen.has(name.toLowerCase())) {
115
+ newHeaders.append(name, value);
116
+ }
117
+ }
118
+ const mergedResponse = new Response(newResponse.body, {
119
+ status,
120
+ statusText: status === 200 ? newResponse.statusText : originalResponse.statusText,
121
+ // If you're looking at here for possible bugs, it means that it's not a bug.
122
+ // With the middleware, users can meddle with headers, and we should pass to the 404/500.
123
+ // If users see something weird, it's because they are setting some headers they should not.
124
+ //
125
+ // Although, we don't want it to replace the content-type, because the error page must return `text/html`
126
+ headers: newHeaders
127
+ });
128
+ const originalCookies = getCookiesFromResponse(originalResponse);
129
+ const newCookies = getCookiesFromResponse(newResponse);
130
+ if (originalCookies) {
131
+ if (newCookies) {
132
+ for (const cookieValue of newCookies.consume()) {
133
+ originalResponse.headers.append("set-cookie", cookieValue);
134
+ }
135
+ }
136
+ attachCookiesToResponse(mergedResponse, originalCookies);
137
+ } else if (newCookies) {
138
+ attachCookiesToResponse(mergedResponse, newCookies);
139
+ }
140
+ return mergedResponse;
141
+ }
142
+ export {
143
+ DefaultErrorHandler
144
+ };
@@ -0,0 +1,21 @@
1
+ import type { BaseApp, RenderErrorOptions } from '../app/base.js';
2
+ import type { Pipeline } from '../base-pipeline.js';
3
+ import type { ErrorHandler } from './handler.js';
4
+ export interface DevErrorHandlerOptions {
5
+ /**
6
+ * Whether to inject CSP meta tags into the rendered error page response.
7
+ * The Vite dev server injects them; the non-runnable dev pipeline does not.
8
+ */
9
+ shouldInjectCspMetaTags: boolean;
10
+ }
11
+ /**
12
+ * The dev-server error handler. Renders custom 404/500 routes if the user
13
+ * has them, otherwise throws so Vite's dev overlay is shown. Shared between
14
+ * the Vite dev server (`AstroServerApp`) and the non-runnable dev pipeline
15
+ * (`DevApp`); only `shouldInjectCspMetaTags` differs between them.
16
+ */
17
+ export declare class DevErrorHandler implements ErrorHandler {
18
+ #private;
19
+ constructor(app: BaseApp<Pipeline>, options: DevErrorHandlerOptions);
20
+ renderError(request: Request, { skipMiddleware, error, status, response: _response, pathname, ...resolvedRenderOptions }: RenderErrorOptions): Promise<Response>;
21
+ }
@@ -0,0 +1,82 @@
1
+ import { FetchState } from "../fetch/fetch-state.js";
2
+ import { AstroMiddleware } from "../middleware/astro-middleware.js";
3
+ import { PagesHandler } from "../pages/handler.js";
4
+ import { getCustom404Route, getCustom500Route } from "../routing/helpers.js";
5
+ import { isAstroError } from "./index.js";
6
+ import { MiddlewareNoDataOrNextCalled, MiddlewareNotAResponse } from "./errors-data.js";
7
+ class DevErrorHandler {
8
+ #app;
9
+ #shouldInjectCspMetaTags;
10
+ #astroMiddleware;
11
+ #pagesHandler;
12
+ constructor(app, options) {
13
+ this.#app = app;
14
+ this.#shouldInjectCspMetaTags = options.shouldInjectCspMetaTags;
15
+ this.#astroMiddleware = new AstroMiddleware(app.pipeline);
16
+ this.#pagesHandler = new PagesHandler(app.pipeline);
17
+ }
18
+ async renderError(request, {
19
+ skipMiddleware = false,
20
+ error,
21
+ status,
22
+ response: _response,
23
+ pathname,
24
+ ...resolvedRenderOptions
25
+ }) {
26
+ if (isAstroError(error) && [MiddlewareNoDataOrNextCalled.name, MiddlewareNotAResponse.name].includes(error.name)) {
27
+ throw error;
28
+ }
29
+ const app = this.#app;
30
+ const shouldInjectCspMetaTags = this.#shouldInjectCspMetaTags;
31
+ const resolvedPathname = pathname ?? new FetchState(app.pipeline, request).pathname;
32
+ const renderRoute = async (routeData) => {
33
+ try {
34
+ const preloadedComponent = await app.pipeline.getComponentByRoute(routeData);
35
+ const errorState = new FetchState(app.pipeline, request);
36
+ errorState.skipMiddleware = skipMiddleware;
37
+ errorState.clientAddress = resolvedRenderOptions.clientAddress;
38
+ errorState.shouldInjectCspMetaTags = shouldInjectCspMetaTags ? !!app.manifest.csp : false;
39
+ errorState.routeData = routeData;
40
+ errorState.pathname = resolvedPathname;
41
+ errorState.status = status;
42
+ errorState.componentInstance = preloadedComponent;
43
+ errorState.locals = resolvedRenderOptions.locals ?? {};
44
+ errorState.initialProps = { error };
45
+ const response = await this.#astroMiddleware.handle(
46
+ errorState,
47
+ this.#pagesHandler.handle.bind(this.#pagesHandler)
48
+ );
49
+ if (error) {
50
+ app.logger.error("router", error.stack || error.message);
51
+ }
52
+ return response;
53
+ } catch (_err) {
54
+ if (skipMiddleware === false) {
55
+ return this.renderError(request, {
56
+ ...resolvedRenderOptions,
57
+ status: 500,
58
+ skipMiddleware: true,
59
+ error: _err,
60
+ pathname: resolvedPathname
61
+ });
62
+ }
63
+ throw _err;
64
+ }
65
+ };
66
+ if (status === 404) {
67
+ const custom404 = getCustom404Route(app.manifestData);
68
+ if (custom404) {
69
+ return renderRoute(custom404);
70
+ }
71
+ }
72
+ const custom500 = getCustom500Route(app.manifestData);
73
+ if (!custom500) {
74
+ throw error;
75
+ } else {
76
+ return renderRoute(custom500);
77
+ }
78
+ }
79
+ }
80
+ export {
81
+ DevErrorHandler
82
+ };
@@ -0,0 +1,9 @@
1
+ import type { RenderErrorOptions } from '../app/base.js';
2
+ /**
3
+ * A strategy for rendering error responses (404, 500, etc.). Each execution
4
+ * environment (prod SSR, build/prerender, dev server) supplies its own
5
+ * implementation rather than overriding a method on the app.
6
+ */
7
+ export interface ErrorHandler {
8
+ renderError(request: Request, options: RenderErrorOptions): Promise<Response>;
9
+ }
File without changes
@@ -0,0 +1,17 @@
1
+ import type { BaseApp, ResolvedRenderOptions } from '../app/base.js';
2
+ import type { Pipeline } from '../base-pipeline.js';
3
+ import type { FetchHandler } from './types.js';
4
+ /**
5
+ * The default request handler for `BaseApp`. Builds the per-request
6
+ * `FetchState` and delegates to an `AstroHandler`.
7
+ */
8
+ export declare class DefaultFetchHandler {
9
+ #private;
10
+ constructor(app?: BaseApp<Pipeline>);
11
+ /**
12
+ * Fast path: called directly by `BaseApp.render()` with pre-resolved
13
+ * options, avoiding the `Reflect.set/get` round-trip through the request.
14
+ */
15
+ renderWithOptions(request: Request, options: ResolvedRenderOptions): Promise<Response>;
16
+ fetch: FetchHandler;
17
+ }
@@ -0,0 +1,45 @@
1
+ import { FetchState } from "./fetch-state.js";
2
+ import { appSymbol } from "../constants.js";
3
+ import { AstroHandler } from "../routing/handler.js";
4
+ class DefaultFetchHandler {
5
+ #app;
6
+ #handler;
7
+ constructor(app) {
8
+ this.#app = app ?? null;
9
+ this.#handler = app ? new AstroHandler(app) : null;
10
+ }
11
+ /**
12
+ * Fast path: called directly by `BaseApp.render()` with pre-resolved
13
+ * options, avoiding the `Reflect.set/get` round-trip through the request.
14
+ */
15
+ renderWithOptions(request, options) {
16
+ if (!this.#app) {
17
+ const app = Reflect.get(request, appSymbol);
18
+ if (!app) {
19
+ throw new Error("No fetch handler provided.");
20
+ }
21
+ this.#app = app;
22
+ this.#handler = new AstroHandler(app);
23
+ }
24
+ const state = new FetchState(this.#app.pipeline, request, options);
25
+ return this.#handler.handle(state);
26
+ }
27
+ fetch = (request) => {
28
+ if (!this.#app) {
29
+ const app = Reflect.get(request, appSymbol);
30
+ if (!app) {
31
+ throw new Error("No fetch handler provided.");
32
+ }
33
+ this.#app = app;
34
+ this.#handler = new AstroHandler(app);
35
+ }
36
+ const state = new FetchState(this.#app.pipeline, request);
37
+ if (!this.#handler) {
38
+ throw new Error("No fetch handler provided.");
39
+ }
40
+ return this.#handler.handle(state);
41
+ };
42
+ }
43
+ export {
44
+ DefaultFetchHandler
45
+ };