astro 4.5.18 → 4.6.0

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 (54) hide show
  1. package/dist/@types/astro.d.ts +96 -3
  2. package/dist/core/app/index.js +8 -0
  3. package/dist/core/app/middlewares.d.ts +7 -0
  4. package/dist/core/app/middlewares.js +26 -0
  5. package/dist/core/app/types.d.ts +2 -1
  6. package/dist/core/base-pipeline.js +6 -3
  7. package/dist/core/build/generate.js +3 -2
  8. package/dist/core/build/plugins/plugin-manifest.js +3 -2
  9. package/dist/core/config/schema.d.ts +168 -65
  10. package/dist/core/config/schema.js +20 -13
  11. package/dist/core/constants.js +1 -1
  12. package/dist/core/cookies/cookies.d.ts +3 -11
  13. package/dist/core/cookies/cookies.js +9 -7
  14. package/dist/core/dev/dev.js +1 -1
  15. package/dist/core/errors/dev/utils.js +1 -1
  16. package/dist/core/errors/errors-data.d.ts +23 -0
  17. package/dist/core/errors/errors-data.js +13 -1
  18. package/dist/core/errors/overlay.js +2 -1
  19. package/dist/core/messages.js +2 -2
  20. package/dist/core/middleware/vite-plugin.js +8 -0
  21. package/dist/core/render/index.d.ts +1 -1
  22. package/dist/core/render/index.js +1 -1
  23. package/dist/core/render/{result.js → slots.js} +4 -2
  24. package/dist/core/routing/manifest/create.js +1 -1
  25. package/dist/i18n/index.d.ts +18 -1
  26. package/dist/i18n/index.js +107 -0
  27. package/dist/i18n/middleware.d.ts +1 -1
  28. package/dist/i18n/middleware.js +40 -81
  29. package/dist/i18n/utils.d.ts +2 -2
  30. package/dist/i18n/utils.js +20 -21
  31. package/dist/runtime/client/dev-toolbar/apps/astro.js +6 -1
  32. package/dist/runtime/client/dev-toolbar/apps/audit/rules/a11y.js +1 -1
  33. package/dist/runtime/client/dev-toolbar/apps/settings.js +38 -1
  34. package/dist/runtime/client/dev-toolbar/apps/utils/window.d.ts +2 -1
  35. package/dist/runtime/client/dev-toolbar/apps/utils/window.js +18 -2
  36. package/dist/runtime/client/dev-toolbar/apps/xray.js +6 -1
  37. package/dist/runtime/client/dev-toolbar/entrypoint.js +3 -1
  38. package/dist/runtime/client/dev-toolbar/settings.d.ts +4 -1
  39. package/dist/runtime/client/dev-toolbar/settings.js +2 -1
  40. package/dist/runtime/client/dev-toolbar/toolbar.d.ts +2 -0
  41. package/dist/runtime/client/dev-toolbar/toolbar.js +25 -3
  42. package/dist/runtime/client/dev-toolbar/ui-library/index.d.ts +1 -0
  43. package/dist/runtime/client/dev-toolbar/ui-library/index.js +2 -0
  44. package/dist/runtime/client/dev-toolbar/ui-library/select.d.ts +15 -0
  45. package/dist/runtime/client/dev-toolbar/ui-library/select.js +100 -0
  46. package/dist/runtime/client/dev-toolbar/ui-library/window.d.ts +9 -0
  47. package/dist/runtime/client/dev-toolbar/ui-library/window.js +53 -3
  48. package/dist/runtime/server/render/astro/render-template.d.ts +1 -1
  49. package/dist/virtual-modules/i18n.d.ts +102 -0
  50. package/dist/virtual-modules/i18n.js +86 -6
  51. package/dist/vite-plugin-astro-server/plugin.js +2 -1
  52. package/dist/vite-plugin-astro-server/route.js +1 -1
  53. package/package.json +6 -5
  54. /package/dist/core/render/{result.d.ts → slots.d.ts} +0 -0
@@ -17,7 +17,7 @@ import type { AstroIntegrationLogger, Logger, LoggerLevel } from '../core/logger
17
17
  import type { AstroPreferences } from '../preferences/index.js';
18
18
  import type { AstroDevToolbar, DevToolbarCanvas } from '../runtime/client/dev-toolbar/toolbar.js';
19
19
  import type { Icon } from '../runtime/client/dev-toolbar/ui-library/icons.js';
20
- import type { DevToolbarBadge, DevToolbarButton, DevToolbarCard, DevToolbarHighlight, DevToolbarIcon, DevToolbarToggle, DevToolbarTooltip, DevToolbarWindow } from '../runtime/client/dev-toolbar/ui-library/index.js';
20
+ import type { DevToolbarBadge, DevToolbarButton, DevToolbarCard, DevToolbarHighlight, DevToolbarIcon, DevToolbarSelect, DevToolbarToggle, DevToolbarTooltip, DevToolbarWindow } from '../runtime/client/dev-toolbar/ui-library/index.js';
21
21
  import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server/index.js';
22
22
  import type { TransitionBeforePreparationEvent, TransitionBeforeSwapEvent } from '../transitions/events.js';
23
23
  import type { DeepPartial, OmitIndexSignature, Simplify } from '../type-utils.js';
@@ -1375,6 +1375,7 @@ export interface AstroUserConfig {
1375
1375
  * @description
1376
1376
  *
1377
1377
  * Controls the routing strategy to determine your site URLs. Set this based on your folder/URL path configuration for your default language.
1378
+ *
1378
1379
  */
1379
1380
  routing?: {
1380
1381
  /**
@@ -1393,6 +1394,18 @@ export interface AstroUserConfig {
1393
1394
  * When `true`, all URLs will display a language prefix.
1394
1395
  * URLs will be of the form `example.com/[locale]/content/` for every route, including the default language.
1395
1396
  * Localized folders are used for every language, including the default.
1397
+ *
1398
+ * ```js
1399
+ * export default defineConfig({
1400
+ * i18n: {
1401
+ * defaultLocale: "en",
1402
+ * locales: ["en", "fr", "pt-br", "es"],
1403
+ * routing: {
1404
+ * prefixDefaultLocale: true,
1405
+ * }
1406
+ * }
1407
+ * })
1408
+ * ```
1396
1409
  */
1397
1410
  prefixDefaultLocale?: boolean;
1398
1411
  /**
@@ -1433,7 +1446,32 @@ export interface AstroUserConfig {
1433
1446
  * - `"pathname": The strategy is applied to the pathname of the URLs
1434
1447
  */
1435
1448
  strategy?: 'pathname';
1436
- };
1449
+ } |
1450
+ /**
1451
+ *
1452
+ * @docs
1453
+ * @name i18n.routing.manual
1454
+ * @kind h4
1455
+ * @type {string}
1456
+ * @version 4.6.0
1457
+ * @description
1458
+ * When this option is enabled, Astro will **disable** its i18n middleware so that you can implement your own custom logic. No other `routing` options (e.g. `prefixDefaultLocale`) may be configured with `routing: "manual"`.
1459
+ *
1460
+ * You will be responsible for writing your own routing logic, or executing Astro's i18n middleware manually alongside your own.
1461
+ *
1462
+ * ```js
1463
+ * export default defineConfig({
1464
+ * i18n: {
1465
+ * defaultLocale: "en",
1466
+ * locales: ["en", "fr", "pt-br", "es"],
1467
+ * routing: {
1468
+ * prefixDefaultLocale: true,
1469
+ * }
1470
+ * }
1471
+ * })
1472
+ * ```
1473
+ */
1474
+ 'manual';
1437
1475
  /**
1438
1476
  * @name i18n.domains
1439
1477
  * @type {Record<string, string> }
@@ -1468,7 +1506,7 @@ export interface AstroUserConfig {
1468
1506
  * })
1469
1507
  * ```
1470
1508
  *
1471
- * Both page routes built and URLs returned by the `astro:i18n` helper functions [`getAbsoluteLocaleUrl()`](https://docs.astro.build/en/guides/internationalization/#getabsolutelocaleurl) and [`getAbsoluteLocaleUrlList()`](https://docs.astro.build/en/guides/internationalization/#getabsolutelocaleurllist) will use the options set in `i18n.domains`.
1509
+ * Both page routes built and URLs returned by the `astro:i18n` helper functions [`getAbsoluteLocaleUrl()`](https://docs.astro.build/en/reference/api-reference/#getabsolutelocaleurl) and [`getAbsoluteLocaleUrlList()`](https://docs.astro.build/en/reference/api-reference/#getabsolutelocaleurllist) will use the options set in `i18n.domains`.
1472
1510
  *
1473
1511
  * See the [Internationalization Guide](https://docs.astro.build/en/guides/internationalization/#domains) for more details, including the limitations of this feature.
1474
1512
  */
@@ -1692,6 +1730,60 @@ export interface AstroUserConfig {
1692
1730
  * See the [Internationalization Guide](https://docs.astro.build/en/guides/internationalization/#domains-experimental) for more details, including the limitations of this experimental feature.
1693
1731
  */
1694
1732
  i18nDomains?: boolean;
1733
+ /**
1734
+ * @docs
1735
+ * @name experimental.security
1736
+ * @type {boolean}
1737
+ * @default `false`
1738
+ * @version 4.6.0
1739
+ * @description
1740
+ *
1741
+ * Enables CSRF protection for Astro websites.
1742
+ *
1743
+ * The CSRF protection works only for pages rendered on demand (SSR) using `server` or `hybrid` mode. The pages must opt out of prerendering in `hybrid` mode.
1744
+ *
1745
+ * ```js
1746
+ * // astro.config.mjs
1747
+ * export default defineConfig({
1748
+ * output: "server",
1749
+ * experimental: {
1750
+ * security: {
1751
+ * csrfProtection: {
1752
+ * origin: true
1753
+ * }
1754
+ * }
1755
+ * }
1756
+ * })
1757
+ * ```
1758
+ */
1759
+ security?: {
1760
+ /**
1761
+ * @name security.csrfProtection
1762
+ * @type {object}
1763
+ * @default '{}'
1764
+ * @version 4.6.0
1765
+ * @description
1766
+ *
1767
+ * Allows you to enable security measures to prevent CSRF attacks: https://owasp.org/www-community/attacks/csrf
1768
+ */
1769
+ csrfProtection?: {
1770
+ /**
1771
+ * @name security.csrfProtection.origin
1772
+ * @type {boolean}
1773
+ * @default 'false'
1774
+ * @version 4.6.0
1775
+ * @description
1776
+ *
1777
+ * When enabled, performs a check that the "origin" header, automatically passed by all modern browsers, matches the URL sent by each `Request`.
1778
+ *
1779
+ * The "origin" check is executed only for pages rendered on demand, and only for the requests `POST, `PATCH`, `DELETE` and `PUT` with
1780
+ * the following `content-type` header: 'application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain'.
1781
+ *
1782
+ * If the "origin" header doesn't match the `pathname` of the request, Astro will return a 403 status code and will not render the page.
1783
+ */
1784
+ origin?: boolean;
1785
+ };
1786
+ };
1695
1787
  };
1696
1788
  }
1697
1789
  /**
@@ -2628,6 +2720,7 @@ declare global {
2628
2720
  'astro-dev-toolbar-button': DevToolbarButton;
2629
2721
  'astro-dev-toolbar-icon': DevToolbarIcon;
2630
2722
  'astro-dev-toolbar-card': DevToolbarCard;
2723
+ 'astro-dev-toolbar-select': DevToolbarSelect;
2631
2724
  'astro-dev-overlay': AstroDevToolbar;
2632
2725
  'astro-dev-overlay-window': DevToolbarWindow;
2633
2726
  'astro-dev-overlay-plugin-canvas': DevToolbarCanvas;
@@ -11,6 +11,7 @@ import { getSetCookiesFromResponse } from "../cookies/index.js";
11
11
  import { AstroError, AstroErrorData } from "../errors/index.js";
12
12
  import { consoleLogDestination } from "../logger/console.js";
13
13
  import { AstroIntegrationLogger, Logger } from "../logger/core.js";
14
+ import { sequence } from "../middleware/index.js";
14
15
  import {
15
16
  appendForwardSlash,
16
17
  collapseDuplicateSlashes,
@@ -23,6 +24,7 @@ import { RenderContext } from "../render-context.js";
23
24
  import { createAssetLink } from "../render/ssr-element.js";
24
25
  import { ensure404Route } from "../routing/astro-designed-error-pages.js";
25
26
  import { matchRoute } from "../routing/match.js";
27
+ import { createOriginCheckMiddleware } from "./middlewares.js";
26
28
  import { AppPipeline } from "./pipeline.js";
27
29
  import { deserializeManifest } from "./common.js";
28
30
  class App {
@@ -58,6 +60,12 @@ class App {
58
60
  * @private
59
61
  */
60
62
  #createPipeline(streaming = false) {
63
+ if (this.#manifest.checkOrigin) {
64
+ this.#manifest.middleware = sequence(
65
+ createOriginCheckMiddleware(),
66
+ this.#manifest.middleware
67
+ );
68
+ }
61
69
  return AppPipeline.create({
62
70
  logger: this.#logger,
63
71
  manifest: this.#manifest,
@@ -0,0 +1,7 @@
1
+ import type { MiddlewareHandler } from '../../@types/astro.js';
2
+ /**
3
+ * Returns a middleware function in charge to check the `origin` header.
4
+ *
5
+ * @private
6
+ */
7
+ export declare function createOriginCheckMiddleware(): MiddlewareHandler;
@@ -0,0 +1,26 @@
1
+ import { defineMiddleware } from "../middleware/index.js";
2
+ const FORM_CONTENT_TYPES = [
3
+ "application/x-www-form-urlencoded",
4
+ "multipart/form-data",
5
+ "text/plain"
6
+ ];
7
+ function createOriginCheckMiddleware() {
8
+ return defineMiddleware((context, next) => {
9
+ const { request, url } = context;
10
+ const contentType = request.headers.get("content-type");
11
+ if (contentType) {
12
+ if (FORM_CONTENT_TYPES.includes(contentType.toLowerCase())) {
13
+ const forbidden = (request.method === "POST" || request.method === "PUT" || request.method === "PATCH" || request.method === "DELETE") && request.headers.get("origin") !== url.origin;
14
+ if (forbidden) {
15
+ return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
16
+ status: 403
17
+ });
18
+ }
19
+ }
20
+ }
21
+ return next();
22
+ });
23
+ }
24
+ export {
25
+ createOriginCheckMiddleware
26
+ };
@@ -51,9 +51,10 @@ export type SSRManifest = {
51
51
  pageMap?: Map<ComponentPath, ImportComponentInstance>;
52
52
  i18n: SSRManifestI18n | undefined;
53
53
  middleware: MiddlewareHandler;
54
+ checkOrigin: boolean;
54
55
  };
55
56
  export type SSRManifestI18n = {
56
- fallback?: Record<string, string>;
57
+ fallback: Record<string, string> | undefined;
57
58
  strategy: RoutingStrategies;
58
59
  locales: Locales;
59
60
  defaultLocale: string;
@@ -17,9 +17,12 @@ class Pipeline {
17
17
  this.middleware = middleware;
18
18
  this.routeCache = routeCache;
19
19
  this.site = site;
20
- this.internalMiddleware = [
21
- createI18nMiddleware(i18n, manifest.base, manifest.trailingSlash, manifest.buildFormat)
22
- ];
20
+ this.internalMiddleware = [];
21
+ if (i18n?.strategy !== "manual") {
22
+ this.internalMiddleware.push(
23
+ createI18nMiddleware(i18n, manifest.base, manifest.trailingSlash, manifest.buildFormat)
24
+ );
25
+ }
23
26
  }
24
27
  internalMiddleware;
25
28
  }
@@ -411,7 +411,7 @@ function createBuildManifest(settings, internals, renderers, middleware) {
411
411
  if (settings.config.i18n) {
412
412
  i18nManifest = {
413
413
  fallback: settings.config.i18n.fallback,
414
- strategy: toRoutingStrategy(settings.config.i18n),
414
+ strategy: toRoutingStrategy(settings.config.i18n.routing, settings.config.i18n.domains),
415
415
  defaultLocale: settings.config.i18n.defaultLocale,
416
416
  locales: settings.config.i18n.locales,
417
417
  domainLookupTable: {}
@@ -433,7 +433,8 @@ function createBuildManifest(settings, internals, renderers, middleware) {
433
433
  componentMetadata: internals.componentMetadata,
434
434
  i18n: i18nManifest,
435
435
  buildFormat: settings.config.build.format,
436
- middleware
436
+ middleware,
437
+ checkOrigin: settings.config.experimental.security?.csrfProtection?.origin ?? false
437
438
  };
438
439
  }
439
440
  export {
@@ -192,7 +192,7 @@ function buildManifest(opts, internals, staticFiles) {
192
192
  if (settings.config.i18n) {
193
193
  i18nManifest = {
194
194
  fallback: settings.config.i18n.fallback,
195
- strategy: toRoutingStrategy(settings.config.i18n),
195
+ strategy: toRoutingStrategy(settings.config.i18n.routing, settings.config.i18n.domains),
196
196
  locales: settings.config.i18n.locales,
197
197
  defaultLocale: settings.config.i18n.defaultLocale,
198
198
  domainLookupTable
@@ -213,7 +213,8 @@ function buildManifest(opts, internals, staticFiles) {
213
213
  inlinedScripts: Array.from(internals.inlinedScripts),
214
214
  assets: staticFiles.map(prefixAssetPath),
215
215
  i18n: i18nManifest,
216
- buildFormat: settings.config.build.format
216
+ buildFormat: settings.config.build.format,
217
+ checkOrigin: settings.config.experimental.security?.csrfProtection?.origin ?? false
217
218
  };
218
219
  }
219
220
  export {