astro 5.8.2 → 5.9.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 (73) hide show
  1. package/dist/actions/runtime/utils.d.ts +1 -1
  2. package/dist/assets/fonts/config.d.ts +21 -21
  3. package/dist/container/index.js +2 -1
  4. package/dist/content/content-layer.js +14 -3
  5. package/dist/content/loaders/types.d.ts +3 -0
  6. package/dist/content/utils.d.ts +26 -10
  7. package/dist/content/utils.js +1 -0
  8. package/dist/core/app/index.js +9 -3
  9. package/dist/core/app/types.d.ts +12 -1
  10. package/dist/core/base-pipeline.js +3 -3
  11. package/dist/core/build/generate.d.ts +1 -1
  12. package/dist/core/build/generate.js +38 -4
  13. package/dist/core/build/internal.d.ts +1 -0
  14. package/dist/core/build/internal.js +2 -1
  15. package/dist/core/build/plugins/index.js +1 -1
  16. package/dist/core/build/plugins/plugin-internals.d.ts +2 -1
  17. package/dist/core/build/plugins/plugin-internals.js +7 -4
  18. package/dist/core/build/plugins/plugin-manifest.js +37 -3
  19. package/dist/core/config/merge.js +1 -6
  20. package/dist/core/config/schemas/base.d.ts +435 -331
  21. package/dist/core/config/schemas/base.js +20 -2
  22. package/dist/core/config/schemas/index.d.ts +1 -1
  23. package/dist/core/config/schemas/index.js +4 -1
  24. package/dist/core/config/schemas/relative.d.ts +681 -552
  25. package/dist/core/constants.js +1 -1
  26. package/dist/core/csp/common.d.ts +16 -0
  27. package/dist/core/csp/common.js +116 -0
  28. package/dist/core/csp/config.d.ts +16 -0
  29. package/dist/core/csp/config.js +52 -0
  30. package/dist/core/dev/dev.js +1 -1
  31. package/dist/core/encryption.d.ts +8 -0
  32. package/dist/core/encryption.js +7 -0
  33. package/dist/core/errors/errors-data.d.ts +12 -0
  34. package/dist/core/errors/errors-data.js +6 -0
  35. package/dist/core/messages.js +2 -2
  36. package/dist/core/middleware/index.js +10 -0
  37. package/dist/core/render-context.d.ts +1 -0
  38. package/dist/core/render-context.js +77 -5
  39. package/dist/core/util.d.ts +1 -0
  40. package/dist/core/util.js +6 -1
  41. package/dist/env/schema.d.ts +34 -34
  42. package/dist/integrations/features-validation.js +36 -30
  43. package/dist/integrations/hooks.d.ts +3 -2
  44. package/dist/runtime/server/astro-island-styles.d.ts +1 -0
  45. package/dist/runtime/server/astro-island-styles.js +4 -0
  46. package/dist/runtime/server/index.d.ts +1 -0
  47. package/dist/runtime/server/render/astro/factory.d.ts +3 -2
  48. package/dist/runtime/server/render/astro/factory.js +6 -1
  49. package/dist/runtime/server/render/astro/head-and-content.d.ts +7 -0
  50. package/dist/runtime/server/render/astro/head-and-content.js +6 -0
  51. package/dist/runtime/server/render/astro/render.d.ts +1 -0
  52. package/dist/runtime/server/render/astro/render.js +2 -1
  53. package/dist/runtime/server/render/common.d.ts +1 -1
  54. package/dist/runtime/server/render/common.js +5 -3
  55. package/dist/runtime/server/render/component.js +8 -2
  56. package/dist/runtime/server/render/csp.d.ts +2 -0
  57. package/dist/runtime/server/render/csp.js +35 -0
  58. package/dist/runtime/server/render/head.js +14 -0
  59. package/dist/runtime/server/render/page.d.ts +1 -1
  60. package/dist/runtime/server/render/page.js +1 -1
  61. package/dist/runtime/server/render/server-islands.d.ts +14 -3
  62. package/dist/runtime/server/render/server-islands.js +100 -69
  63. package/dist/runtime/server/scripts.d.ts +1 -1
  64. package/dist/runtime/server/scripts.js +2 -5
  65. package/dist/runtime/server/transition.d.ts +1 -1
  66. package/dist/runtime/server/transition.js +7 -2
  67. package/dist/types/public/config.d.ts +240 -7
  68. package/dist/types/public/context.d.ts +51 -0
  69. package/dist/types/public/integrations.d.ts +9 -0
  70. package/dist/types/public/internal.d.ts +24 -2
  71. package/dist/vite-plugin-astro-server/css.js +3 -3
  72. package/dist/vite-plugin-astro-server/plugin.js +26 -4
  73. package/package.json +3 -3
@@ -1,6 +1,7 @@
1
- import { encryptString } from "../../../core/encryption.js";
1
+ import { encryptString, generateCspDigest } from "../../../core/encryption.js";
2
2
  import { markHTMLString } from "../escape.js";
3
3
  import { renderChild } from "./any.js";
4
+ import { createThinHead } from "./astro/head-and-content.js";
4
5
  import { createRenderInstruction } from "./instruction.js";
5
6
  import { renderSlotToString } from "./slot.js";
6
7
  const internalProps = /* @__PURE__ */ new Set([
@@ -31,54 +32,63 @@ function isWithinURLLimit(pathname, params) {
31
32
  const chars = url.length;
32
33
  return chars < 2048;
33
34
  }
34
- function renderServerIsland(result, _displayName, props, slots) {
35
- return {
36
- async render(destination) {
37
- const componentPath = props["server:component-path"];
38
- const componentExport = props["server:component-export"];
39
- const componentId = result.serverIslandNameMap.get(componentPath);
40
- if (!componentId) {
41
- throw new Error(`Could not find server component name`);
42
- }
43
- for (const key2 of Object.keys(props)) {
44
- if (internalProps.has(key2)) {
45
- delete props[key2];
46
- }
35
+ class ServerIslandComponent {
36
+ result;
37
+ props;
38
+ slots;
39
+ displayName;
40
+ hostId;
41
+ islandContent;
42
+ constructor(result, props, slots, displayName) {
43
+ this.result = result;
44
+ this.props = props;
45
+ this.slots = slots;
46
+ this.displayName = displayName;
47
+ }
48
+ async init() {
49
+ const componentPath = this.props["server:component-path"];
50
+ const componentExport = this.props["server:component-export"];
51
+ const componentId = this.result.serverIslandNameMap.get(componentPath);
52
+ if (!componentId) {
53
+ throw new Error(`Could not find server component name`);
54
+ }
55
+ for (const key2 of Object.keys(this.props)) {
56
+ if (internalProps.has(key2)) {
57
+ delete this.props[key2];
47
58
  }
48
- destination.write(createRenderInstruction({ type: "server-island-runtime" }));
49
- destination.write("<!--[if astro]>server-island-start<![endif]-->");
50
- const renderedSlots = {};
51
- for (const name in slots) {
52
- if (name !== "fallback") {
53
- const content = await renderSlotToString(result, slots[name]);
54
- renderedSlots[name] = content.toString();
55
- } else {
56
- await renderChild(destination, slots.fallback(result));
57
- }
59
+ }
60
+ const renderedSlots = {};
61
+ for (const name in this.slots) {
62
+ if (name !== "fallback") {
63
+ const content2 = await renderSlotToString(this.result, this.slots[name]);
64
+ renderedSlots[name] = content2.toString();
58
65
  }
59
- const key = await result.key;
60
- const propsEncrypted = Object.keys(props).length === 0 ? "" : await encryptString(key, JSON.stringify(props));
61
- const hostId = crypto.randomUUID();
62
- const slash = result.base.endsWith("/") ? "" : "/";
63
- let serverIslandUrl = `${result.base}${slash}_server-islands/${componentId}${result.trailingSlash === "always" ? "/" : ""}`;
64
- const potentialSearchParams = createSearchParams(
65
- componentExport,
66
- propsEncrypted,
67
- safeJsonStringify(renderedSlots)
68
- );
69
- const useGETRequest = isWithinURLLimit(serverIslandUrl, potentialSearchParams);
70
- if (useGETRequest) {
71
- serverIslandUrl += "?" + potentialSearchParams.toString();
72
- destination.write(
66
+ }
67
+ const key = await this.result.key;
68
+ const propsEncrypted = Object.keys(this.props).length === 0 ? "" : await encryptString(key, JSON.stringify(this.props));
69
+ const hostId = crypto.randomUUID();
70
+ const slash = this.result.base.endsWith("/") ? "" : "/";
71
+ let serverIslandUrl = `${this.result.base}${slash}_server-islands/${componentId}${this.result.trailingSlash === "always" ? "/" : ""}`;
72
+ const potentialSearchParams = createSearchParams(
73
+ componentExport,
74
+ propsEncrypted,
75
+ safeJsonStringify(renderedSlots)
76
+ );
77
+ const useGETRequest = isWithinURLLimit(serverIslandUrl, potentialSearchParams);
78
+ if (useGETRequest) {
79
+ serverIslandUrl += "?" + potentialSearchParams.toString();
80
+ this.result._metadata.extraHead.push(
81
+ markHTMLString(
73
82
  `<link rel="preload" as="fetch" href="${serverIslandUrl}" crossorigin="anonymous">`
74
- );
75
- }
76
- destination.write(`<script type="module" data-astro-rerun data-island-id="${hostId}">${useGETRequest ? (
77
- // GET request
78
- `let response = await fetch('${serverIslandUrl}');`
79
- ) : (
80
- // POST request
81
- `let data = {
83
+ )
84
+ );
85
+ }
86
+ const method = useGETRequest ? (
87
+ // GET request
88
+ `let response = await fetch('${serverIslandUrl}');`
89
+ ) : (
90
+ // POST request
91
+ `let data = {
82
92
  componentExport: ${safeJsonStringify(componentExport)},
83
93
  encryptedProps: ${safeJsonStringify(propsEncrypted)},
84
94
  slots: ${safeJsonStringify(renderedSlots)},
@@ -87,33 +97,54 @@ let response = await fetch('${serverIslandUrl}', {
87
97
  method: 'POST',
88
98
  body: JSON.stringify(data),
89
99
  });`
90
- )}
91
- replaceServerIsland('${hostId}', response);</script>`);
100
+ );
101
+ const content = `${method}replaceServerIsland('${hostId}', response);`;
102
+ if (this.result.shouldInjectCspMetaTags) {
103
+ this.result._metadata.extraScriptHashes.push(
104
+ await generateCspDigest(SERVER_ISLAND_REPLACER, this.result.cspAlgorithm)
105
+ );
106
+ const contentDigest = await generateCspDigest(content, this.result.cspAlgorithm);
107
+ this.result._metadata.extraScriptHashes.push(contentDigest);
92
108
  }
93
- };
109
+ this.islandContent = content;
110
+ this.hostId = hostId;
111
+ return createThinHead();
112
+ }
113
+ async render(destination) {
114
+ destination.write(createRenderInstruction({ type: "server-island-runtime" }));
115
+ destination.write("<!--[if astro]>server-island-start<![endif]-->");
116
+ for (const name in this.slots) {
117
+ if (name === "fallback") {
118
+ await renderChild(destination, this.slots.fallback(this.result));
119
+ }
120
+ }
121
+ destination.write(
122
+ `<script type="module" data-astro-rerun data-island-id="${this.hostId}">${this.islandContent}</script>`
123
+ );
124
+ }
94
125
  }
95
- const renderServerIslandRuntime = () => markHTMLString(
96
- `
97
- <script>
98
- async function replaceServerIsland(id, r) {
99
- let s = document.querySelector(\`script[data-island-id="\${id}"]\`);
100
- // If there's no matching script, or the request fails then return
101
- if (!s || r.status !== 200 || r.headers.get('content-type')?.split(';')[0].trim() !== 'text/html') return;
102
- // Load the HTML before modifying the DOM in case of errors
103
- let html = await r.text();
104
- // Remove any placeholder content before the island script
105
- while (s.previousSibling && s.previousSibling.nodeType !== 8 && s.previousSibling.data !== '[if astro]>server-island-start<![endif]')
106
- s.previousSibling.remove();
107
- s.previousSibling?.remove();
108
- // Insert the new HTML
109
- s.before(document.createRange().createContextualFragment(html));
110
- // Remove the script. Prior to v5.4.2, this was the trick to force rerun of scripts. Keeping it to minimize change to the existing behavior.
111
- s.remove();
112
- }
113
- </script>`.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("//")).join(" ")
126
+ const renderServerIslandRuntime = () => {
127
+ return `<script>${SERVER_ISLAND_REPLACER}</script>`;
128
+ };
129
+ const SERVER_ISLAND_REPLACER = markHTMLString(
130
+ `async function replaceServerIsland(id, r) {
131
+ let s = document.querySelector(\`script[data-island-id="\${id}"]\`);
132
+ // If there's no matching script, or the request fails then return
133
+ if (!s || r.status !== 200 || r.headers.get('content-type')?.split(';')[0].trim() !== 'text/html') return;
134
+ // Load the HTML before modifying the DOM in case of errors
135
+ let html = await r.text();
136
+ // Remove any placeholder content before the island script
137
+ while (s.previousSibling && s.previousSibling.nodeType !== 8 && s.previousSibling.data !== '[if astro]>server-island-start<![endif]')
138
+ s.previousSibling.remove();
139
+ s.previousSibling?.remove();
140
+ // Insert the new HTML
141
+ s.before(document.createRange().createContextualFragment(html));
142
+ // Remove the script. Prior to v5.4.2, this was the trick to force rerun of scripts. Keeping it to minimize change to the existing behavior.
143
+ s.remove();
144
+ }`.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("//")).join(" ")
114
145
  );
115
146
  export {
147
+ ServerIslandComponent,
116
148
  containsServerDirective,
117
- renderServerIsland,
118
149
  renderServerIslandRuntime
119
150
  };
@@ -1,5 +1,5 @@
1
1
  import type { SSRResult } from '../../types/public/internal.js';
2
2
  export declare function determineIfNeedsHydrationScript(result: SSRResult): boolean;
3
3
  export declare function determinesIfNeedsDirectiveScript(result: SSRResult, directive: string): boolean;
4
- export type PrescriptType = null | 'both' | 'directive';
4
+ export type PrescriptType = 'both' | 'directive';
5
5
  export declare function getPrescripts(result: SSRResult, type: PrescriptType, directive: string): string;
@@ -1,6 +1,6 @@
1
+ import { ISLAND_STYLES } from "./astro-island-styles.js";
1
2
  import islandScriptDev from "./astro-island.prebuilt-dev.js";
2
3
  import islandScript from "./astro-island.prebuilt.js";
3
- const ISLAND_STYLES = `<style>astro-island,astro-slot,astro-static-slot{display:contents}</style>`;
4
4
  function determineIfNeedsHydrationScript(result) {
5
5
  if (result._metadata.hasHydrationScript) {
6
6
  return false;
@@ -25,13 +25,10 @@ function getDirectiveScriptText(result, directive) {
25
25
  function getPrescripts(result, type, directive) {
26
26
  switch (type) {
27
27
  case "both":
28
- return `${ISLAND_STYLES}<script>${getDirectiveScriptText(result, directive)};${process.env.NODE_ENV === "development" ? islandScriptDev : islandScript}</script>`;
28
+ return `<style>${ISLAND_STYLES}</style><script>${getDirectiveScriptText(result, directive)}</script><script>${process.env.NODE_ENV === "development" ? islandScriptDev : islandScript}</script>`;
29
29
  case "directive":
30
30
  return `<script>${getDirectiveScriptText(result, directive)}</script>`;
31
- case null:
32
- break;
33
31
  }
34
- return "";
35
32
  }
36
33
  export {
37
34
  determineIfNeedsHydrationScript,
@@ -1,7 +1,7 @@
1
1
  import type { SSRResult } from '../../types/public/internal.js';
2
2
  import type { TransitionAnimationPair, TransitionAnimationValue } from '../../types/public/view-transitions.js';
3
3
  export declare function createTransitionScope(result: SSRResult, hash: string): string;
4
- export declare function renderTransition(result: SSRResult, hash: string, animationName: TransitionAnimationValue | undefined, transitionName: string): string;
4
+ export declare function renderTransition(result: SSRResult, hash: string, animationName: TransitionAnimationValue | undefined, transitionName: string): Promise<string>;
5
5
  export declare function createAnimationScope(transitionName: string, animations: Record<string, TransitionAnimationPair>): {
6
6
  scope: string;
7
7
  styles: string;
@@ -1,4 +1,5 @@
1
1
  import cssesc from "cssesc";
2
+ import { generateCspDigest } from "../../core/encryption.js";
2
3
  import { fade, slide } from "../../transitions/index.js";
3
4
  import { markHTMLString } from "./escape.js";
4
5
  const transitionNameMap = /* @__PURE__ */ new WeakMap();
@@ -39,7 +40,7 @@ function reEncode(s) {
39
40
  }
40
41
  return reEncodeInValidStart[result.codePointAt(0) ?? 0] ? "_" + result : result;
41
42
  }
42
- function renderTransition(result, hash, animationName, transitionName) {
43
+ async function renderTransition(result, hash, animationName, transitionName) {
43
44
  if (typeof (transitionName ?? "") !== "string") {
44
45
  throw new Error(`Invalid transition name {${transitionName}}`);
45
46
  }
@@ -56,7 +57,11 @@ function renderTransition(result, hash, animationName, transitionName) {
56
57
  sheet.addAnimationRaw("new", "animation: none; mix-blend-mode: normal;");
57
58
  sheet.addModern("group", "animation: none");
58
59
  }
59
- result._metadata.extraHead.push(markHTMLString(`<style>${sheet.toString()}</style>`));
60
+ const css = sheet.toString();
61
+ if (result.shouldInjectCspMetaTags) {
62
+ result._metadata.extraStyleHashes.push(await generateCspDigest(css, result.cspAlgorithm));
63
+ }
64
+ result._metadata.extraHead.push(markHTMLString(`<style>${css}</style>`));
60
65
  return scope;
61
66
  }
62
67
  function createAnimationScope(transitionName, animations) {
@@ -9,6 +9,7 @@ import type { AssetsPrefix } from '../../core/app/types.js';
9
9
  import type { AstroConfigType } from '../../core/config/schemas/index.js';
10
10
  import type { REDIRECT_STATUS_CODES } from '../../core/constants.js';
11
11
  import type { AstroCookieSetOptions } from '../../core/cookies/cookies.js';
12
+ import type { CspAlgorithm, CspDirective, CspHash } from '../../core/csp/config.js';
12
13
  import type { LoggerLevel } from '../../core/logger/core.js';
13
14
  import type { EnvSchema } from '../../env/schema.js';
14
15
  import type { AstroIntegration } from './integrations.js';
@@ -17,6 +18,7 @@ export type Locales = (string | {
17
18
  path: string;
18
19
  })[];
19
20
  export type { AstroFontProvider as FontProvider };
21
+ export type { CspAlgorithm };
20
22
  type NormalizeLocales<T extends Locales> = {
21
23
  [K in keyof T]: T[K] extends string ? T[K] : T[K] extends {
22
24
  codes: Array<string>;
@@ -1747,8 +1749,6 @@ export interface ViteUserConfig extends OriginalViteUserConfig {
1747
1749
  */
1748
1750
  domains?: [TLocales] extends [never] ? Record<string, string> : Partial<Record<NormalizeLocales<NoInfer<TLocales>>, string>>;
1749
1751
  };
1750
- /** ! WARNING: SUBJECT TO CHANGE */
1751
- db?: Config.Database;
1752
1752
  /**
1753
1753
  * @docs
1754
1754
  * @kind heading
@@ -2118,6 +2118,244 @@ export interface ViteUserConfig extends OriginalViteUserConfig {
2118
2118
  * include a trailing `-`, matching standard behavior in other Markdown tooling.
2119
2119
  */
2120
2120
  headingIdCompat?: boolean;
2121
+ /**
2122
+ * @name experimental.csp
2123
+ * @type {boolean | object}
2124
+ * @default `false`
2125
+ * @version 5.9.0
2126
+ * @description
2127
+ *
2128
+ * Enables built-in support for Content Security Policy (CSP). For more information,
2129
+ * refer to the [experimental CSP documentation](https://docs.astro.build/en/reference/experimental-flags/csp/)
2130
+ *
2131
+ */
2132
+ csp?: boolean | {
2133
+ /**
2134
+ * @name experimental.csp.algorithm
2135
+ * @type {"SHA-256" | "SHA-384" | "SHA-512"}
2136
+ * @default `'SHA-256'`
2137
+ * @version 5.9.0
2138
+ * @description
2139
+ *
2140
+ * The [hash function](https://developer.mozilla.org/en-US/docs/Glossary/Hash_function) to use to generate the hashes of the styles and scripts emitted by Astro.
2141
+ *
2142
+ * ```js
2143
+ * import { defineConfig } from 'astro/config';
2144
+ *
2145
+ * export default defineConfig({
2146
+ * experimental: {
2147
+ * csp: {
2148
+ * algorithm: 'SHA-512'
2149
+ * }
2150
+ * }
2151
+ * });
2152
+ * ```
2153
+ */
2154
+ algorithm?: CspAlgorithm;
2155
+ /**
2156
+ * @name experimental.csp.styleDirective
2157
+ * @type {{ hashes?: CspHash[], resources?: string[] }}
2158
+ * @default `undefined`
2159
+ * @version 5.9.0
2160
+ * @description
2161
+ *
2162
+ * A configuration object that allows you to override the default sources for the `style-src` directive
2163
+ * with the `resources` property, or to provide additional `hashes` to be rendered.
2164
+ *
2165
+ * These properties are added to all pages and completely override Astro's default resources, not add to them.
2166
+ * Therefore, you must explicitly specify any default values that you want to be included.
2167
+ */
2168
+ styleDirective?: {
2169
+ /**
2170
+ * @name experimental.csp.styleDirective.hashes
2171
+ * @type {CspHash[]}
2172
+ * @default `[]`
2173
+ * @version 5.9.0
2174
+ * @description
2175
+ *
2176
+ * A list of additional hashes added to the `style-src` directive.
2177
+ *
2178
+ * If you have external styles that aren't generated by Astro, this configuration option allows you to provide additional hashes to be rendered.
2179
+ *
2180
+ * You must provide hashes that start with `sha384-`, `sha512-` or `sha256-`. Other values will cause a validation error. These hashes are added to all pages.
2181
+ *
2182
+ * ```js
2183
+ * import { defineConfig } from 'astro/config';
2184
+ *
2185
+ * export default defineConfig({
2186
+ * experimental: {
2187
+ * csp: {
2188
+ * styleDirective: {
2189
+ * hashes: [
2190
+ * "sha384-styleHash",
2191
+ * "sha512-styleHash",
2192
+ * "sha256-styleHash"
2193
+ * ]
2194
+ * }
2195
+ * }
2196
+ * }
2197
+ * });
2198
+ * ```
2199
+ */
2200
+ hashes?: CspHash[];
2201
+ /**
2202
+ * @name experimental.csp.styleDirective.resources
2203
+ * @type {string[]}
2204
+ * @default `[]`
2205
+ * @version 5.9.0
2206
+ * @description
2207
+ *
2208
+ * A list of resources applied to the `style-src` directive. These resources are added to all pages and will override Astro's defaults.
2209
+ *
2210
+ * ```js
2211
+ * import { defineConfig } from 'astro/config';
2212
+ *
2213
+ * export default defineConfig({
2214
+ * experimental: {
2215
+ * csp: {
2216
+ * styleDirective: {
2217
+ * resources: [
2218
+ * "self",
2219
+ * "https://styles.cdn.example.com"
2220
+ * ]
2221
+ * }
2222
+ * }
2223
+ * }
2224
+ * });
2225
+ * ```
2226
+ */
2227
+ resources?: string[];
2228
+ };
2229
+ /**
2230
+ * @name experimental.csp.scriptDirective
2231
+ * @type {{ hashes?: CspHash[], resources?: string[], strictDynamic?: boolean }}
2232
+ * @default `undefined`
2233
+ * @version 5.9.0
2234
+ * @description
2235
+ *
2236
+ * A configuration object that allows you to override the default sources for the `script-src` directive
2237
+ * with the `resources` property, or to provide additional `hashes` to be rendered.
2238
+ *
2239
+ * These properties are added to all pages and completely override Astro's default resources, not add to them.
2240
+ * Therefore, you must explicitly specify any default values that you want to be included.
2241
+ *
2242
+ */
2243
+ scriptDirective?: {
2244
+ /**
2245
+ * @name experimental.csp.scriptDirective.hashes
2246
+ * @type {CspHash[]}
2247
+ * @default `[]`
2248
+ * @version 5.9.0
2249
+ * @description
2250
+ *
2251
+ * A list of additional hashes added to the `script-src` directive.
2252
+ *
2253
+ * If you have external scripts that aren't generated by Astro, or inline scripts, this configuration option allows you to provide additional hashes to be rendered.
2254
+ *
2255
+ * You must provide hashes that start with `sha384-`, `sha512-` or `sha256-`. Other values will cause a validation error. These hashes are added to all pages.
2256
+ *
2257
+ * ```js
2258
+ * import { defineConfig } from 'astro/config';
2259
+ *
2260
+ * export default defineConfig({
2261
+ * experimental: {
2262
+ * csp: {
2263
+ * scriptDirective: {
2264
+ * hashes: [
2265
+ * "sha384-scriptHash",
2266
+ * "sha512-scriptHash",
2267
+ * "sha256-scriptHash"
2268
+ * ]
2269
+ * }
2270
+ * }
2271
+ * }
2272
+ * });
2273
+ * ```
2274
+ */
2275
+ hashes?: CspHash[];
2276
+ /**
2277
+ * @name experimental.csp.scriptDirective.resources
2278
+ * @type {string[]}
2279
+ * @default `[]`
2280
+ * @version 5.9.0
2281
+ * @description
2282
+ *
2283
+ * A list of resources applied to the `script-src` directive. These resources are added to all pages and will override Astro's defaults.
2284
+ *
2285
+ * ```js
2286
+ * import { defineConfig } from 'astro/config';
2287
+ *
2288
+ * export default defineConfig({
2289
+ * experimental: {
2290
+ * csp: {
2291
+ * scriptDirective: {
2292
+ * resources: [
2293
+ * "self",
2294
+ * "https://cdn.example.com"
2295
+ * ]
2296
+ * }
2297
+ * }
2298
+ * }
2299
+ * });
2300
+ * ```
2301
+ *
2302
+ */
2303
+ resources?: string[];
2304
+ /**
2305
+ * @name experimental.csp.scriptDirective.strictDynamic
2306
+ * @type {boolean}
2307
+ * @default `false`
2308
+ * @version 5.9.0
2309
+ * @description
2310
+ *
2311
+ * Enables the keyword `strict-dynamic` to support the dynamic injection of scripts.
2312
+ *
2313
+ * ```js
2314
+ * import { defineConfig } from 'astro/config';
2315
+ *
2316
+ * export default defineConfig({
2317
+ * experimental: {
2318
+ * csp: {
2319
+ * scriptDirective: {
2320
+ * strictDynamic: true
2321
+ * }
2322
+ * }
2323
+ * }
2324
+ * });
2325
+ * ```
2326
+ */
2327
+ strictDynamic?: boolean;
2328
+ };
2329
+ /**
2330
+ * @name experimental.csp.directives
2331
+ * @type {string[]}
2332
+ * @default `[]`
2333
+ * @version 5.9.0
2334
+ * @description
2335
+ *
2336
+ * An array of additional directives to add the content of the `Content-Security-Policy` `<meta>` element.
2337
+ *
2338
+ * Use this configuration to add other directive definitions such as `default-src`, `image-src`, etc.
2339
+ *
2340
+ * ##### Example
2341
+ *
2342
+ * You can define a directive to fetch images only from a CDN `cdn.example.com`.
2343
+ *
2344
+ * ```js
2345
+ * export default defineConfig({
2346
+ * experimental: {
2347
+ * csp: {
2348
+ * directives: [
2349
+ * "image-src 'https://cdn.example.com"
2350
+ * ]
2351
+ * }
2352
+ * }
2353
+ * })
2354
+ * ```
2355
+ *
2356
+ */
2357
+ directives?: CspDirective[];
2358
+ };
2121
2359
  /**
2122
2360
  * @name experimental.preserveScriptOrder
2123
2361
  * @type {boolean}
@@ -2212,8 +2450,3 @@ export interface AstroInlineOnlyConfig {
2212
2450
  */
2213
2451
  force?: boolean;
2214
2452
  }
2215
- declare global {
2216
- namespace Config {
2217
- type Database = Record<string, any>;
2218
- }
2219
- }
@@ -2,6 +2,7 @@ import type { z } from 'zod';
2
2
  import type { ActionAccept, ActionClient, ActionReturnType } from '../../actions/runtime/virtual/server.js';
3
3
  import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from '../../core/constants.js';
4
4
  import type { AstroCookies } from '../../core/cookies/cookies.js';
5
+ import type { CspDirective, CspHash } from '../../core/csp/config.js';
5
6
  import type { AstroSession } from '../../core/session.js';
6
7
  import type { AstroComponentFactory } from '../../runtime/server/index.js';
7
8
  import type { Params, RewritePayload } from './common.js';
@@ -310,6 +311,56 @@ export interface AstroSharedContext<Props extends Record<string, any> = Record<s
310
311
  * Whether the current route is prerendered or not.
311
312
  */
312
313
  isPrerendered: boolean;
314
+ /**
315
+ * It adds a specific CSP directive to the route being rendered.
316
+ *
317
+ * ## Example
318
+ *
319
+ * ```js
320
+ * ctx.insertDirective("default-src 'self' 'unsafe-inline' https://example.com")
321
+ * ```
322
+ */
323
+ insertDirective: (directive: CspDirective) => void;
324
+ /**
325
+ * It set the resource for the directive `style-src` in the route being rendered. It overrides Astro's default.
326
+ *
327
+ * ## Example
328
+ *
329
+ * ```js
330
+ * ctx.insertStyleResource("https://styles.cdn.example.com/")
331
+ * ```
332
+ */
333
+ insertStyleResource: (payload: string) => void;
334
+ /**
335
+ * Insert a single style hash to the route being rendered.
336
+ *
337
+ * ## Example
338
+ *
339
+ * ```js
340
+ * ctx.insertStyleHash("sha256-1234567890abcdef1234567890")
341
+ * ```
342
+ */
343
+ insertStyleHash: (hash: CspHash) => void;
344
+ /**
345
+ * It set the resource for the directive `script-src` in the route being rendered.
346
+ *
347
+ * ## Example
348
+ *
349
+ * ```js
350
+ * ctx.insertScriptResource("https://scripts.cdn.example.com/")
351
+ * ```
352
+ */
353
+ insertScriptResource: (resource: string) => void;
354
+ /**
355
+ * Insert a single script hash to the route being rendered.
356
+ *
357
+ * ## Example
358
+ *
359
+ * ```js
360
+ * ctx.insertScriptHash("sha256-1234567890abcdef1234567890")
361
+ * ```
362
+ */
363
+ insertScriptHash: (hash: CspHash) => void;
313
364
  }
314
365
  /**
315
366
  * The `APIContext` is the object made available to endpoints and middleware.
@@ -53,6 +53,15 @@ export type AdapterSupportsKind = (typeof AdapterFeatureStability)[keyof typeof
53
53
  export type AdapterSupportWithMessage = {
54
54
  support: Exclude<AdapterSupportsKind, 'stable'>;
55
55
  message: string;
56
+ /**
57
+ * Determines if a feature support warning/error in the adapter should be suppressed:
58
+ * - `"default"`: Suppresses the default warning/error message.
59
+ * - `"all"`: Suppresses both the custom and the default warning/error message.
60
+ *
61
+ * This is useful when the warning/error might not be applicable in certain contexts,
62
+ * or the default message might cause confusion and conflict with a custom one.
63
+ */
64
+ suppress?: 'all' | 'default';
56
65
  };
57
66
  export type AdapterSupport = AdapterSupportsKind | AdapterSupportWithMessage;
58
67
  export interface AstroAdapterFeatures {