astro 7.0.0-beta.4 → 7.0.0-beta.6

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 (47) hide show
  1. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  2. package/dist/content/content-layer.js +3 -3
  3. package/dist/core/app/prepare-response.d.ts +2 -3
  4. package/dist/core/app/prepare-response.js +1 -6
  5. package/dist/core/build/generate.js +1 -1
  6. package/dist/core/build/plugins/plugin-manifest.js +1 -4
  7. package/dist/core/build/vite-build-config.js +6 -0
  8. package/dist/core/cache/config.d.ts +7 -6
  9. package/dist/core/cache/runtime/noop.js +1 -1
  10. package/dist/core/cache/utils.d.ts +3 -3
  11. package/dist/core/cache/vite-plugin.js +1 -1
  12. package/dist/core/config/schemas/base.d.ts +13 -13
  13. package/dist/core/config/schemas/base.js +4 -4
  14. package/dist/core/config/schemas/relative.d.ts +36 -36
  15. package/dist/core/constants.d.ts +0 -41
  16. package/dist/core/constants.js +1 -18
  17. package/dist/core/dev/dev.js +1 -1
  18. package/dist/core/errors/default-handler.js +1 -0
  19. package/dist/core/errors/errors-data.js +2 -2
  20. package/dist/core/fetch/fetch-state.d.ts +11 -0
  21. package/dist/core/fetch/fetch-state.js +19 -1
  22. package/dist/core/fetch/index.d.ts +8 -3
  23. package/dist/core/fetch/index.js +2 -2
  24. package/dist/core/i18n/handler.js +7 -13
  25. package/dist/core/messages/runtime.js +1 -1
  26. package/dist/core/middleware/astro-middleware.d.ts +19 -0
  27. package/dist/core/middleware/astro-middleware.js +43 -0
  28. package/dist/core/middleware/noop-middleware.js +0 -2
  29. package/dist/core/pages/handler.d.ts +13 -0
  30. package/dist/core/pages/handler.js +35 -14
  31. package/dist/core/routing/handler.js +6 -8
  32. package/dist/core/routing/rewrite.js +2 -1
  33. package/dist/core/util/pathname.d.ts +17 -16
  34. package/dist/core/util/pathname.js +6 -2
  35. package/dist/i18n/index.js +5 -7
  36. package/dist/manifest/serialized.js +2 -5
  37. package/dist/runtime/server/endpoint.d.ts +2 -1
  38. package/dist/runtime/server/endpoint.js +4 -13
  39. package/dist/types/public/config.d.ts +85 -83
  40. package/dist/virtual-modules/container.d.ts +1 -1
  41. package/dist/vite-plugin-config-alias/index.d.ts +2 -2
  42. package/dist/vite-plugin-config-alias/index.js +66 -61
  43. package/dist/vite-plugin-head/index.js +5 -11
  44. package/package.json +6 -6
  45. package/templates/content/module.mjs +0 -1
  46. package/dist/core/head-propagation/hint.d.ts +0 -4
  47. package/dist/core/head-propagation/hint.js +0 -7
@@ -1,5 +1,6 @@
1
1
  import type { FetchState } from '../fetch/fetch-state.js';
2
2
  import type { APIContext } from '../../types/public/context.js';
3
+ import type { BaseApp } from '../app/base.js';
3
4
  import { type Pipeline } from '../base-pipeline.js';
4
5
  /**
5
6
  * Callback invoked at the bottom of the middleware chain to dispatch the
@@ -24,4 +25,22 @@ export declare class AstroMiddleware {
24
25
  #private;
25
26
  constructor(pipeline: Pipeline);
26
27
  handle(state: FetchState, renderRouteCallback: RenderRouteCallback): Promise<Response>;
28
+ /**
29
+ * Like `handle`, but mirrors the app-level error handling that
30
+ * `AstroHandler` provides on the standard path, the same way
31
+ * `PagesHandler.handleWithErrorFallback` does for `pages()`. When no
32
+ * route matched it returns a 404 marked with `X-Astro-Error` for the
33
+ * app's post-check; when Astro's own middleware chain throws it logs the
34
+ * error and renders the custom `500.astro`.
35
+ *
36
+ * Errors surfaced through `renderRouteCallback` (the host framework's
37
+ * `next`, e.g. host middleware mounted below `middleware()`) are
38
+ * re-thrown instead, so the host's own error handling still runs rather
39
+ * than being swallowed into Astro's 500 page. A sentinel tells the two
40
+ * apart.
41
+ *
42
+ * Used by the composable `astro/fetch` `middleware()` entry point, where
43
+ * there is no surrounding `AstroHandler` to supply this fallback.
44
+ */
45
+ handleWithErrorFallback(app: BaseApp<Pipeline>, state: FetchState, renderRouteCallback: RenderRouteCallback): Promise<Response>;
27
46
  }
@@ -1,4 +1,5 @@
1
1
  import { PipelineFeatures } from "../base-pipeline.js";
2
+ import { ASTRO_ERROR_HEADER } from "../constants.js";
2
3
  import { attachCookiesToResponse } from "../cookies/index.js";
3
4
  import { applyRewriteToState } from "../rewrites/handler.js";
4
5
  import { callMiddleware } from "./callMiddleware.js";
@@ -41,6 +42,48 @@ class AstroMiddleware {
41
42
  state.response = response;
42
43
  return response;
43
44
  }
45
+ /**
46
+ * Like `handle`, but mirrors the app-level error handling that
47
+ * `AstroHandler` provides on the standard path, the same way
48
+ * `PagesHandler.handleWithErrorFallback` does for `pages()`. When no
49
+ * route matched it returns a 404 marked with `X-Astro-Error` for the
50
+ * app's post-check; when Astro's own middleware chain throws it logs the
51
+ * error and renders the custom `500.astro`.
52
+ *
53
+ * Errors surfaced through `renderRouteCallback` (the host framework's
54
+ * `next`, e.g. host middleware mounted below `middleware()`) are
55
+ * re-thrown instead, so the host's own error handling still runs rather
56
+ * than being swallowed into Astro's 500 page. A sentinel tells the two
57
+ * apart.
58
+ *
59
+ * Used by the composable `astro/fetch` `middleware()` entry point, where
60
+ * there is no surrounding `AstroHandler` to supply this fallback.
61
+ */
62
+ async handleWithErrorFallback(app, state, renderRouteCallback) {
63
+ if (!state.routeData) {
64
+ return new Response(null, { status: 404, headers: { [ASTRO_ERROR_HEADER]: "true" } });
65
+ }
66
+ let nextError;
67
+ try {
68
+ return await this.handle(state, async (s, ctx) => {
69
+ try {
70
+ return await renderRouteCallback(s, ctx);
71
+ } catch (err) {
72
+ nextError = err;
73
+ throw err;
74
+ }
75
+ });
76
+ } catch (err) {
77
+ if (err === nextError) throw err;
78
+ app.logger.error(null, err.stack || err.message || String(err));
79
+ return app.renderError(state.request, {
80
+ ...state.renderOptions,
81
+ status: 500,
82
+ error: err,
83
+ pathname: state.pathname
84
+ });
85
+ }
86
+ }
44
87
  #finalize(state, response) {
45
88
  attachCookiesToResponse(response, state.cookies);
46
89
  return response;
@@ -1,7 +1,5 @@
1
- import { NOOP_MIDDLEWARE_HEADER } from "../constants.js";
2
1
  const NOOP_MIDDLEWARE_FN = async (_ctx, next) => {
3
2
  const response = await next();
4
- response.headers.set(NOOP_MIDDLEWARE_HEADER, "true");
5
3
  return response;
6
4
  };
7
5
  export {
@@ -1,4 +1,5 @@
1
1
  import type { APIContext } from '../../types/public/context.js';
2
+ import type { BaseApp } from '../app/base.js';
2
3
  import type { FetchState } from '../fetch/fetch-state.js';
3
4
  import type { Pipeline } from '../base-pipeline.js';
4
5
  /**
@@ -17,4 +18,16 @@ export declare class PagesHandler {
17
18
  #private;
18
19
  constructor(pipeline: Pipeline);
19
20
  handle(state: FetchState, ctx: APIContext): Promise<Response>;
21
+ /**
22
+ * Like `handle`, but mirrors the app-level error handling that
23
+ * `AstroHandler` provides on the standard path: unmatched routes
24
+ * return a 404 marked with `X-Astro-Error` for the app's post-check
25
+ * to render the 404 error page, and render-time errors are logged
26
+ * and render the 500 error page instead of propagating to the host
27
+ * framework.
28
+ *
29
+ * Used by the composable `astro/fetch` `pages()` entry point, where
30
+ * there is no surrounding `AstroHandler` to supply this fallback.
31
+ */
32
+ handleWithErrorFallback(app: BaseApp<Pipeline>, state: FetchState): Promise<Response>;
20
33
  }
@@ -1,12 +1,6 @@
1
1
  import { renderEndpoint } from "../../runtime/server/endpoint.js";
2
2
  import { renderPage } from "../../runtime/server/index.js";
3
- import {
4
- ASTRO_ERROR_HEADER,
5
- REROUTE_DIRECTIVE_HEADER,
6
- REWRITE_DIRECTIVE_HEADER_KEY,
7
- REWRITE_DIRECTIVE_HEADER_VALUE,
8
- ROUTE_TYPE_HEADER
9
- } from "../constants.js";
3
+ import { ASTRO_ERROR_HEADER } from "../constants.js";
10
4
  import { getCookiesFromResponse } from "../cookies/response.js";
11
5
  const EMPTY_SLOTS = Object.freeze({});
12
6
  class PagesHandler {
@@ -17,6 +11,7 @@ class PagesHandler {
17
11
  async handle(state, ctx) {
18
12
  const pipeline = this.#pipeline;
19
13
  const { logger, streaming } = pipeline;
14
+ state.resetResponseMetadata();
20
15
  let response;
21
16
  const componentInstance = await state.loadComponentInstance();
22
17
  switch (state.routeData.type) {
@@ -25,7 +20,8 @@ class PagesHandler {
25
20
  componentInstance,
26
21
  ctx,
27
22
  state.routeData.prerender,
28
- logger
23
+ logger,
24
+ state
29
25
  );
30
26
  break;
31
27
  }
@@ -46,12 +42,9 @@ class PagesHandler {
46
42
  result.cancelled = true;
47
43
  throw e;
48
44
  }
49
- response.headers.set(ROUTE_TYPE_HEADER, "page");
45
+ state.responseRouteType = "page";
50
46
  if (state.routeData.route === "/404" || state.routeData.route === "/500") {
51
- response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
52
- }
53
- if (state.isRewriting) {
54
- response.headers.set(REWRITE_DIRECTIVE_HEADER_KEY, REWRITE_DIRECTIVE_HEADER_VALUE);
47
+ state.skipErrorReroute = true;
55
48
  }
56
49
  break;
57
50
  }
@@ -59,7 +52,8 @@ class PagesHandler {
59
52
  return new Response(null, { status: 404, headers: { [ASTRO_ERROR_HEADER]: "true" } });
60
53
  }
61
54
  case "fallback": {
62
- return new Response(null, { status: 500, headers: { [ROUTE_TYPE_HEADER]: "fallback" } });
55
+ state.responseRouteType = "fallback";
56
+ return new Response(null, { status: 500 });
63
57
  }
64
58
  }
65
59
  const responseCookies = getCookiesFromResponse(response);
@@ -69,6 +63,33 @@ class PagesHandler {
69
63
  state.response = response;
70
64
  return response;
71
65
  }
66
+ /**
67
+ * Like `handle`, but mirrors the app-level error handling that
68
+ * `AstroHandler` provides on the standard path: unmatched routes
69
+ * return a 404 marked with `X-Astro-Error` for the app's post-check
70
+ * to render the 404 error page, and render-time errors are logged
71
+ * and render the 500 error page instead of propagating to the host
72
+ * framework.
73
+ *
74
+ * Used by the composable `astro/fetch` `pages()` entry point, where
75
+ * there is no surrounding `AstroHandler` to supply this fallback.
76
+ */
77
+ async handleWithErrorFallback(app, state) {
78
+ if (!state.routeData) {
79
+ return new Response(null, { status: 404, headers: { [ASTRO_ERROR_HEADER]: "true" } });
80
+ }
81
+ try {
82
+ return await this.handle(state, state.getAPIContext());
83
+ } catch (err) {
84
+ app.logger.error(null, err.stack || err.message || String(err));
85
+ return app.renderError(state.request, {
86
+ ...state.renderOptions,
87
+ status: 500,
88
+ error: err,
89
+ pathname: state.pathname
90
+ });
91
+ }
92
+ }
72
93
  }
73
94
  export {
74
95
  PagesHandler
@@ -1,9 +1,5 @@
1
1
  import { ActionHandler } from "../../actions/handler.js";
2
- import {
3
- REROUTABLE_STATUS_CODES,
4
- REROUTE_DIRECTIVE_HEADER,
5
- REWRITE_DIRECTIVE_HEADER_KEY
6
- } from "../constants.js";
2
+ import { REROUTABLE_STATUS_CODES } from "../constants.js";
7
3
  import { TrailingSlashHandler } from "./trailing-slash-handler.js";
8
4
  import { CacheHandler, provideCache } from "../cache/handler.js";
9
5
  import { I18n } from "../i18n/handler.js";
@@ -65,6 +61,9 @@ class AstroHandler {
65
61
  }
66
62
  async handle(state) {
67
63
  state.pipeline.usedFeatures |= ALL_PIPELINE_FEATURES;
64
+ if (state.invalidEncoding) {
65
+ return new Response(null, { status: 400, statusText: "Bad Request" });
66
+ }
68
67
  const trailingSlashRedirect = this.#trailingSlashHandler.handle(state);
69
68
  if (trailingSlashRedirect) {
70
69
  return trailingSlashRedirect;
@@ -128,12 +127,11 @@ class AstroHandler {
128
127
  };
129
128
  response = await this.#cacheHandler.handle(state, runPipeline);
130
129
  }
131
- const isRewrite = response.headers.has(REWRITE_DIRECTIVE_HEADER_KEY);
132
130
  this.#app.logThisRequest({
133
131
  pathname,
134
132
  method: request.method,
135
133
  statusCode: response.status,
136
- isRewrite,
134
+ isRewrite: state.isRewriting,
137
135
  timeStart: state.timeStart
138
136
  });
139
137
  } catch (err) {
@@ -150,7 +148,7 @@ class AstroHandler {
150
148
  }
151
149
  if (REROUTABLE_STATUS_CODES.includes(response.status) && // If the body isn't null, that means the user sets the 404 status
152
150
  // but uses the current route to handle the 404
153
- response.body === null && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== "no") {
151
+ response.body === null && !state.skipErrorReroute) {
154
152
  return this.#app.renderError(request, {
155
153
  ...state.renderOptions,
156
154
  response,
@@ -10,6 +10,7 @@ import {
10
10
  trimSlashes
11
11
  } from "../path.js";
12
12
  import { createRequest } from "../request.js";
13
+ import { validateAndDecodePathname } from "../util/pathname.js";
13
14
  import { DEFAULT_404_ROUTE } from "./internal/astro-designed-error-pages.js";
14
15
  import { isRoute404, isRoute500 } from "./internal/route-errors.js";
15
16
  function findRouteToRewrite({
@@ -36,7 +37,7 @@ function findRouteToRewrite({
36
37
  buildFormat
37
38
  );
38
39
  newUrl.pathname = resolvedUrlPathname;
39
- const decodedPathname = decodeURI(pathname);
40
+ const decodedPathname = validateAndDecodePathname(pathname);
40
41
  if (isRoute404(decodedPathname)) {
41
42
  const errorRoute = routes.find((route) => route.route === "/404");
42
43
  if (errorRoute) {
@@ -1,25 +1,26 @@
1
1
  /**
2
- * Error thrown when multi-level URL encoding is detected in a pathname.
3
- * This is a distinct error type so callers can handle it specifically
4
- * (e.g., returning a 400 response) rather than falling back to partial decoding.
5
- *
6
- * @deprecated No longer thrown internally — multi-level encoding is now
7
- * decoded iteratively instead of rejected. Kept for backwards compatibility
8
- * in case third-party code references the class.
2
+ * Thrown when a URL path is encoded so many times that we give up decoding it
3
+ * (see {@link validateAndDecodePathname}). When this happens we reject the
4
+ * request with a `400` instead of guessing the path. If we let a half-decoded
5
+ * path through, your middleware might check one path while Astro routes to a
6
+ * different one.
9
7
  */
10
8
  export declare class MultiLevelEncodingError extends Error {
11
9
  constructor();
12
10
  }
13
11
  /**
14
- * Decodes a pathname iteratively until stable, collapsing all levels of
15
- * percent-encoding into a single canonical form. This prevents
16
- * double/triple encoding from bypassing middleware authorization checks
17
- * (CVE-2025-66202)instead of rejecting multi-level encoding, we
18
- * fully resolve it so middleware always sees the true decoded path.
12
+ * Decodes a URL path over and over until it stops changing, so a path that was
13
+ * encoded several times ends up as a single, final path. This stops someone
14
+ * from sneaking a path like `/admin` past middleware by encoding it multiple
15
+ * timesmiddleware always sees the real, decoded path.
19
16
  *
20
- * @param pathname - The pathname to decode
21
- * @returns The fully decoded pathname
22
- * @throws Error if the pathname contains invalid URL encoding that
23
- * cannot be decoded at all (e.g., a bare `%` not followed by hex digits)
17
+ * @param pathname - The path to decode
18
+ * @returns The final, fully decoded path
19
+ * @throws Error if the path has broken encoding that can't be decoded at all
20
+ * (for example a lone `%` that isn't followed by two hex digits)
21
+ * @throws MultiLevelEncodingError if the path is still changing after
22
+ * {@link MAX_DECODE_ITERATIONS} tries (it was encoded too many times).
23
+ * Handing back a half-decoded path here would bring back the security hole
24
+ * this function exists to close.
24
25
  */
25
26
  export declare function validateAndDecodePathname(pathname: string): string;
@@ -1,9 +1,10 @@
1
1
  class MultiLevelEncodingError extends Error {
2
2
  constructor() {
3
- super("Multi-level URL encoding is not allowed");
3
+ super("URL encoding depth exceeded the maximum number of decode iterations");
4
4
  this.name = "MultiLevelEncodingError";
5
5
  }
6
6
  }
7
+ const MAX_DECODE_ITERATIONS = 10;
7
8
  function validateAndDecodePathname(pathname) {
8
9
  let decoded;
9
10
  try {
@@ -12,7 +13,10 @@ function validateAndDecodePathname(pathname) {
12
13
  throw new Error("Invalid URL encoding");
13
14
  }
14
15
  let iterations = 0;
15
- while (decoded !== pathname && iterations < 10) {
16
+ while (decoded !== pathname) {
17
+ if (iterations >= MAX_DECODE_ITERATIONS) {
18
+ throw new MultiLevelEncodingError();
19
+ }
16
20
  pathname = decoded;
17
21
  try {
18
22
  decoded = decodeURI(pathname);
@@ -4,7 +4,7 @@ import {
4
4
  removeTrailingForwardSlash
5
5
  } from "@astrojs/internal-helpers/path";
6
6
  import { shouldAppendForwardSlash } from "../core/build/util.js";
7
- import { REROUTE_DIRECTIVE_HEADER } from "../core/constants.js";
7
+ import { getFetchStateFromAPIContext } from "../core/fetch/fetch-state.js";
8
8
  import { i18nNoLocaleFoundInPath, MissingLocale } from "../core/errors/errors-data.js";
9
9
  import { AstroError } from "../core/errors/index.js";
10
10
  import { createI18nMiddleware } from "./middleware.js";
@@ -200,24 +200,22 @@ function redirectToDefaultLocale({
200
200
  }
201
201
  function notFound({ base, locales, fallback }) {
202
202
  return function(context, response) {
203
- if (response?.headers.get(REROUTE_DIRECTIVE_HEADER) === "no" && typeof fallback === "undefined") {
203
+ const fetchState = getFetchStateFromAPIContext(context);
204
+ if (fetchState.skipErrorReroute && typeof fallback === "undefined") {
204
205
  return response;
205
206
  }
206
207
  const url = context.url;
207
208
  const isRoot = url.pathname === base + "/" || url.pathname === base;
208
209
  if (!(isRoot || pathHasLocale(url.pathname, locales))) {
210
+ fetchState.skipErrorReroute = true;
209
211
  if (response) {
210
- response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
211
212
  return new Response(response.body, {
212
213
  status: 404,
213
214
  headers: response.headers
214
215
  });
215
216
  } else {
216
217
  return new Response(null, {
217
- status: 404,
218
- headers: {
219
- [REROUTE_DIRECTIVE_HEADER]: "no"
220
- }
218
+ status: 404
221
219
  });
222
220
  }
223
221
  }
@@ -83,7 +83,7 @@ function serializedManifestPlugin({
83
83
  const serialized = await createSerializedManifest(settings, await getEncodedKey());
84
84
  manifestData = JSON.stringify(serialized);
85
85
  }
86
- const hasCacheConfig = !!settings.config.experimental?.cache?.provider;
86
+ const hasCacheConfig = !!settings.config.cache?.provider;
87
87
  const cacheProviderLine = hasCacheConfig ? `cacheProvider: () => import('${VIRTUAL_CACHE_PROVIDER_ID}'),` : "";
88
88
  const code = `
89
89
  import { deserializeManifest as _deserializeManifest } from 'astro/app';
@@ -181,10 +181,7 @@ async function createSerializedManifest(settings, encodedKey) {
181
181
  // 1mb default
182
182
  key: encodedKey ?? await encodeKey(hasEnvironmentKey() ? await getEnvironmentKey() : await createKey()),
183
183
  sessionConfig: sessionConfigToManifest(settings.config.session),
184
- cacheConfig: cacheConfigToManifest(
185
- settings.config.experimental?.cache,
186
- settings.config.experimental?.routeRules
187
- ),
184
+ cacheConfig: cacheConfigToManifest(settings.config.cache, settings.config.routeRules),
188
185
  csp,
189
186
  image: {
190
187
  objectFit: settings.config.image.objectFit,
@@ -1,7 +1,8 @@
1
+ import type { FetchState } from '../../core/fetch/fetch-state.js';
1
2
  import type { AstroLogger } from '../../core/logger/core.js';
2
3
  import type { APIRoute } from '../../types/public/common.js';
3
4
  import type { APIContext } from '../../types/public/context.js';
4
5
  /** Renders an endpoint request to completion, returning the body. */
5
6
  export declare function renderEndpoint(mod: {
6
7
  [method: string]: APIRoute;
7
- }, context: APIContext, isPrerendered: boolean, logger: AstroLogger): Promise<Response>;
8
+ }, context: APIContext, isPrerendered: boolean, logger: AstroLogger, state?: FetchState): Promise<Response>;
@@ -1,8 +1,8 @@
1
1
  import colors from "piccolore";
2
- import { REROUTABLE_STATUS_CODES, REROUTE_DIRECTIVE_HEADER } from "../../core/constants.js";
2
+ import { REROUTABLE_STATUS_CODES } from "../../core/constants.js";
3
3
  import { AstroError } from "../../core/errors/errors.js";
4
4
  import { EndpointDidNotReturnAResponse } from "../../core/errors/errors-data.js";
5
- async function renderEndpoint(mod, context, isPrerendered, logger) {
5
+ async function renderEndpoint(mod, context, isPrerendered, logger, state) {
6
6
  const { request, url } = context;
7
7
  const method = request.method.toUpperCase();
8
8
  let handler = mod[method] ?? mod["ALL"];
@@ -38,17 +38,8 @@ Found handlers: ${Object.keys(mod).map((exp) => JSON.stringify(exp)).join(", ")}
38
38
  if (!response || response instanceof Response === false) {
39
39
  throw new AstroError(EndpointDidNotReturnAResponse);
40
40
  }
41
- if (REROUTABLE_STATUS_CODES.includes(response.status)) {
42
- try {
43
- response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
44
- } catch (err) {
45
- if (err.message?.includes("immutable")) {
46
- response = new Response(response.body, response);
47
- response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
48
- } else {
49
- throw err;
50
- }
51
- }
41
+ if (state && REROUTABLE_STATUS_CODES.includes(response.status)) {
42
+ state.skipErrorReroute = true;
52
43
  }
53
44
  if (method === "HEAD") {
54
45
  return new Response(null, response);
@@ -420,22 +420,22 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
420
420
  * @docs
421
421
  * @name compressHTML
422
422
  * @type {boolean | "jsx"}
423
- * @default `true`
423
+ * @default `'jsx'`
424
424
  * @description
425
425
  *
426
426
  * Controls how Astro handles whitespace in your HTML. This affects both development mode and the final build output.
427
427
  *
428
- * By default, Astro removes whitespace from your HTML, including line breaks, in a lossless manner from `.astro` components. Some whitespace may be preserved as needed to maintain the visual rendering of your HTML.
428
+ * Since v7.0, Astro applies by default the JSX whitespace rules used by frameworks like React. This removes whitespace and line breaks around elements, collapses multi-line text onto a single line, and preserves whitespace within a single line (e.g. a space between two inline elements). To keep a space that would otherwise be removed, include it explicitly in the source through constructs such as `{" "}`.
429
429
  *
430
- * Since 6.2.0, this option can also be set to `"jsx"`, Astro will apply the JSX whitespace stripping rules used by frameworks like React. Leading and trailing whitespace is only preserved when explicitly included in the source code through constructs such as `{" "}`, and is otherwise removed entirely.
430
+ * Setting this option to `true` instead removes whitespace, including line breaks, in a lossless manner from `.astro` components. Some whitespace may be preserved as needed to maintain the visual rendering of your HTML.
431
431
  *
432
- * Setting this option to false disables HTML compression and preserves all whitespace.
432
+ * Setting this option to `false` disables HTML compression and preserves all whitespace.
433
433
  *
434
434
  * ```js
435
435
  * {
436
- * compressHTML: false
436
+ * compressHTML: true
437
437
  * // or:
438
- * // compressHTML: 'jsx'
438
+ * // compressHTML: false
439
439
  * }
440
440
  * ```
441
441
  */
@@ -2752,6 +2752,85 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2752
2752
  fonts?: [TFontProviders] extends [never] ? Array<FontFamily> : {
2753
2753
  [K in keyof TFontProviders]: FontFamily<TFontProviders[K]>;
2754
2754
  };
2755
+ /**
2756
+ * @docs
2757
+ * @kind heading
2758
+ * @name cache
2759
+ * @type {object}
2760
+ * @default `undefined`
2761
+ * @version 7.0.0
2762
+ * @description
2763
+ *
2764
+ * Enables route caching for SSR responses. Provides a platform-agnostic API
2765
+ * for caching rendered pages and API responses, with pluggable providers
2766
+ * that adapters can configure automatically.
2767
+ *
2768
+ * ```js
2769
+ * // astro.config.mjs
2770
+ * import { memoryCache } from 'astro/config';
2771
+ *
2772
+ * {
2773
+ * cache: {
2774
+ * provider: memoryCache(),
2775
+ * },
2776
+ * routeRules: {
2777
+ * '/blog/[...path]': { maxAge: 300, swr: 60 },
2778
+ * },
2779
+ * }
2780
+ * ```
2781
+ *
2782
+ * Use `Astro.cache.set()` in routes and `context.cache.set()` in middleware
2783
+ * or API routes to control caching per-request.
2784
+ */
2785
+ cache?: {
2786
+ /**
2787
+ * @docs
2788
+ * @name cache.provider
2789
+ * @type {CacheProviderConfig}
2790
+ * @version 7.0.0
2791
+ * @description
2792
+ *
2793
+ * A provider that controls how responses are cached.
2794
+ *
2795
+ * Use the provider's config function to get type-safe configuration:
2796
+ *
2797
+ * ```js
2798
+ * import { defineConfig, memoryCache } from 'astro/config';
2799
+ *
2800
+ * export default defineConfig({
2801
+ * cache: { provider: memoryCache() },
2802
+ * });
2803
+ * ```
2804
+ */
2805
+ provider?: CacheProviderConfig;
2806
+ };
2807
+ /**
2808
+ * @docs
2809
+ * @kind heading
2810
+ * @name routeRules
2811
+ * @type {Record<string, RouteRule>}
2812
+ * @default `undefined`
2813
+ * @version 7.0.0
2814
+ * @description
2815
+ *
2816
+ * Route patterns mapped to cache rules.
2817
+ * Uses the same `[param]` and `[...rest]` syntax as [file-based routing](/en/guides/routing/#route-priority-order).
2818
+ * Use a `[...rest]` parameter to match a group of routes:
2819
+ *
2820
+ * ```js
2821
+ * // astro.config.mjs
2822
+ * import { memoryCache } from 'astro/config';
2823
+ *
2824
+ * {
2825
+ * cache: { provider: memoryCache() },
2826
+ * routeRules: {
2827
+ * '/api/[...path]': { swr: 600 },
2828
+ * '/products/[...slug]': { maxAge: 3600, tags: ['products'] },
2829
+ * },
2830
+ * }
2831
+ * ```
2832
+ */
2833
+ routeRules?: RouteRules;
2755
2834
  /**
2756
2835
  *
2757
2836
  * @kind heading
@@ -2895,83 +2974,6 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2895
2974
  * See the [experimental SVG optimization docs](https://docs.astro.build/en/reference/experimental-flags/svg-optimization/) for more information.
2896
2975
  */
2897
2976
  svgOptimizer?: SvgOptimizer;
2898
- /**
2899
- * @name experimental.cache
2900
- * @type {object}
2901
- * @default `undefined`
2902
- * @description
2903
- *
2904
- * Enables route caching for SSR responses. Provides a platform-agnostic API
2905
- * for caching rendered pages and API responses, with pluggable providers
2906
- * that adapters can configure automatically.
2907
- *
2908
- * ```js
2909
- * // astro.config.mjs
2910
- * import { memoryCache } from 'astro/config';
2911
- *
2912
- * {
2913
- * experimental: {
2914
- * cache: {
2915
- * provider: memoryCache(),
2916
- * },
2917
- * routeRules: {
2918
- * '/blog/[...path]': { maxAge: 300, swr: 60 },
2919
- * },
2920
- * },
2921
- * }
2922
- * ```
2923
- *
2924
- * Use `Astro.cache.set()` in routes and `context.cache.set()` in middleware
2925
- * or API routes to control caching per-request.
2926
- */
2927
- cache?: {
2928
- /**
2929
- * @name experimental.cache.provider
2930
- * @type {import('../../core/cache/types.js').CacheProviderConfig}
2931
- * @description
2932
- *
2933
- * The cache provider. Adapters typically set a default, but you can
2934
- * override it with your own.
2935
- *
2936
- * Use the provider's config function to get type-safe configuration:
2937
- *
2938
- * ```js
2939
- * import { memoryCache } from 'astro/config';
2940
- *
2941
- * export default defineConfig({
2942
- * experimental: {
2943
- * cache: { provider: memoryCache() },
2944
- * },
2945
- * });
2946
- * ```
2947
- */
2948
- provider?: CacheProviderConfig;
2949
- };
2950
- /**
2951
- * @name experimental.routeRules
2952
- * @type {Record<string, RouteRule>}
2953
- * @default `undefined`
2954
- * @description
2955
- *
2956
- * Route patterns mapped to cache rules.
2957
- * Uses the same `[param]` and `[...rest]` syntax as file-based routing.
2958
- *
2959
- * ```js
2960
- * // astro.config.mjs
2961
- * import { memoryCache } from 'astro/config';
2962
- *
2963
- * {
2964
- * experimental: {
2965
- * cache: { provider: memoryCache() },
2966
- * routeRules: {
2967
- * '/api/*': { swr: 600 },
2968
- * '/products/*': { maxAge: 3600, tags: ['products'] },
2969
- * },
2970
- * },
2971
- * }
2972
- * ```
2973
- */
2974
- routeRules?: RouteRules;
2975
2977
  };
2976
2978
  }
2977
2979
  /**
@@ -4,7 +4,7 @@ import type { SSRLoadedRenderer } from '../types/public/internal.js';
4
4
  * Use this function to provide renderers to the `AstroContainer`:
5
5
  *
6
6
  * ```js
7
- * import { getContainerRenderer } from "@astrojs/react";
7
+ * import { getContainerRenderer } from "@astrojs/react/container-renderer";
8
8
  * import { experimental_AstroContainer as AstroContainer } from "astro/container";
9
9
  * import { loadRenderers } from "astro:container"; // use this only when using vite/vitest
10
10
  *
@@ -1,6 +1,6 @@
1
1
  import { type Plugin as VitePlugin } from 'vite';
2
2
  import type { AstroSettings } from '../types/astro.js';
3
- /** Returns a Vite plugin used to alias paths from tsconfig.json and jsconfig.json. */
3
+ /** Returns Vite plugins used to alias paths from tsconfig.json and jsconfig.json. */
4
4
  export default function configAliasVitePlugin({ settings, }: {
5
5
  settings: AstroSettings;
6
- }): VitePlugin | null;
6
+ }): VitePlugin[] | null;