astro 7.0.9 → 7.1.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 (71) hide show
  1. package/dist/assets/fonts/infra/fs-font-file-content-resolver.js +1 -1
  2. package/dist/cli/dev/index.d.ts +17 -0
  3. package/dist/cli/dev/index.js +56 -2
  4. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  5. package/dist/content/consts.d.ts +2 -0
  6. package/dist/content/consts.js +4 -0
  7. package/dist/content/content-layer.d.ts +0 -6
  8. package/dist/content/content-layer.js +4 -9
  9. package/dist/content/data-store-source.d.ts +36 -0
  10. package/dist/content/data-store-source.js +30 -0
  11. package/dist/content/data-store-writer.d.ts +67 -0
  12. package/dist/content/data-store-writer.js +94 -0
  13. package/dist/content/data-store.d.ts +15 -0
  14. package/dist/content/data-store.js +33 -3
  15. package/dist/content/loaders/glob.d.ts +7 -0
  16. package/dist/content/loaders/glob.js +2 -2
  17. package/dist/content/mutable-data-store.d.ts +8 -0
  18. package/dist/content/mutable-data-store.js +52 -15
  19. package/dist/content/paths.d.ts +14 -0
  20. package/dist/content/paths.js +11 -0
  21. package/dist/content/runtime.js +4 -4
  22. package/dist/content/vite-plugin-content-virtual-mod.js +29 -2
  23. package/dist/core/app/dev/pipeline.js +2 -1
  24. package/dist/core/app/types.d.ts +39 -5
  25. package/dist/core/base-pipeline.d.ts +1 -1
  26. package/dist/core/base-pipeline.js +6 -2
  27. package/dist/core/build/index.js +2 -1
  28. package/dist/core/build/plugins/plugin-manifest.js +23 -8
  29. package/dist/core/config/schemas/base.d.ts +38 -5
  30. package/dist/core/config/schemas/base.js +15 -8
  31. package/dist/core/config/schemas/relative.d.ts +73 -15
  32. package/dist/core/constants.js +1 -1
  33. package/dist/core/csp/common.d.ts +7 -7
  34. package/dist/core/csp/common.js +8 -9
  35. package/dist/core/csp/config.d.ts +45 -0
  36. package/dist/core/csp/config.js +44 -2
  37. package/dist/core/csp/runtime.d.ts +21 -1
  38. package/dist/core/csp/runtime.js +31 -0
  39. package/dist/core/dev/dev.js +9 -4
  40. package/dist/core/dev/restart.js +7 -1
  41. package/dist/core/errors/zod-error-map.js +7 -1
  42. package/dist/core/fetch/fetch-state.js +77 -29
  43. package/dist/core/i18n/domain.d.ts +1 -1
  44. package/dist/core/i18n/domain.js +4 -5
  45. package/dist/core/logger/config.d.ts +1 -1
  46. package/dist/core/logger/core.d.ts +4 -4
  47. package/dist/core/logger/impls/console.d.ts +2 -2
  48. package/dist/core/logger/impls/json.d.ts +2 -2
  49. package/dist/core/logger/impls/json.js +3 -14
  50. package/dist/core/logger/impls/node.d.ts +2 -2
  51. package/dist/core/logger/load.d.ts +2 -2
  52. package/dist/core/logger/load.js +19 -27
  53. package/dist/core/messages/runtime.d.ts +8 -0
  54. package/dist/core/messages/runtime.js +34 -1
  55. package/dist/core/render/paginate.js +21 -16
  56. package/dist/core/sync/index.js +23 -8
  57. package/dist/integrations/hooks.js +8 -0
  58. package/dist/manifest/serialized.js +21 -6
  59. package/dist/runtime/server/escape.d.ts +2 -0
  60. package/dist/runtime/server/escape.js +4 -0
  61. package/dist/runtime/server/render/csp.js +80 -19
  62. package/dist/runtime/server/render/server-islands.js +6 -4
  63. package/dist/runtime/server/transition.js +2 -2
  64. package/dist/types/astro.d.ts +2 -3
  65. package/dist/types/public/common.d.ts +13 -0
  66. package/dist/types/public/config.d.ts +196 -15
  67. package/dist/types/public/context.d.ts +27 -11
  68. package/dist/types/public/internal.d.ts +12 -11
  69. package/dist/vite-plugin-app/pipeline.js +2 -1
  70. package/dist/vite-plugin-astro-server/route-guard.js +3 -2
  71. package/package.json +3 -3
@@ -14,6 +14,7 @@ import { validateSetAdapter } from "../core/dev/adapter-validation.js";
14
14
  import { getRouteGenerator } from "../core/routing/generator.js";
15
15
  import { getClientOutputDirectory } from "../prerender/utils.js";
16
16
  import { validateSupportedFeatures } from "./features-validation.js";
17
+ import { loadLoggerDestination } from "../core/logger/load.js";
17
18
  async function withTakingALongTimeMsg({
18
19
  name,
19
20
  hookName,
@@ -139,6 +140,7 @@ async function runHookConfigSetup({
139
140
  let astroJSXRenderer = null;
140
141
  for (let i = 0; i < updatedConfig.integrations.length; i++) {
141
142
  const integration = updatedConfig.integrations[i];
143
+ let isLoggerUpdated = false;
142
144
  const { integrationLogger } = await runHookInternal({
143
145
  integration,
144
146
  hookName: "astro:config:setup",
@@ -169,6 +171,9 @@ async function runHookConfigSetup({
169
171
  updatedSettings.scripts.push({ stage, content });
170
172
  },
171
173
  updateConfig: (newConfig) => {
174
+ if (newConfig.logger?.entrypoint) {
175
+ isLoggerUpdated = true;
176
+ }
172
177
  updatedConfig = mergeConfig(updatedConfig, newConfig);
173
178
  return { ...updatedConfig };
174
179
  },
@@ -254,6 +259,9 @@ async function runHookConfigSetup({
254
259
  }
255
260
  try {
256
261
  updatedConfig = await validateConfigRefined(updatedConfig);
262
+ if (isLoggerUpdated) {
263
+ logger.setDestination(await loadLoggerDestination(updatedConfig.logger));
264
+ }
257
265
  } catch (error) {
258
266
  integrationLogger.error("An error occurred while updating the config");
259
267
  throw error;
@@ -14,6 +14,7 @@ import {
14
14
  getStyleResources,
15
15
  shouldTrackCspHashes
16
16
  } from "../core/csp/common.js";
17
+ import { partitionByKind } from "../core/csp/runtime.js";
17
18
  import { createKey, encodeKey, getEnvironmentKey, hasEnvironmentKey } from "../core/encryption.js";
18
19
  import { MIDDLEWARE_MODULE_ID } from "../core/middleware/vite-plugin.js";
19
20
  import { SERVER_ISLAND_MANIFEST } from "../core/server-islands/vite-plugin-server-islands.js";
@@ -131,15 +132,29 @@ async function createSerializedManifest(settings, encodedKey) {
131
132
  };
132
133
  }
133
134
  if (shouldTrackCspHashes(settings.config.security.csp)) {
135
+ const cspConfig = settings.config.security.csp;
136
+ const scriptDirective = {
137
+ resources: getScriptResources(cspConfig),
138
+ hashes: getScriptHashes(cspConfig),
139
+ strictDynamic: getStrictDynamic(cspConfig)
140
+ };
141
+ const styleDirective = {
142
+ resources: getStyleResources(cspConfig),
143
+ hashes: getStyleHashes(cspConfig)
144
+ };
145
+ const scriptDefault = partitionByKind(scriptDirective).default;
146
+ const styleDefault = partitionByKind(styleDirective).default;
134
147
  csp = {
135
148
  cspDestination: settings.adapter?.adapterFeatures?.staticHeaders ? "adapter" : void 0,
136
- scriptHashes: getScriptHashes(settings.config.security.csp),
137
- scriptResources: getScriptResources(settings.config.security.csp),
138
- styleHashes: getStyleHashes(settings.config.security.csp),
139
- styleResources: getStyleResources(settings.config.security.csp),
140
- algorithm: getAlgorithm(settings.config.security.csp),
149
+ algorithm: getAlgorithm(cspConfig),
141
150
  directives: getDirectives(settings),
142
- isStrictDynamic: getStrictDynamic(settings.config.security.csp)
151
+ scriptHashes: scriptDefault.hashes,
152
+ scriptResources: scriptDefault.resources,
153
+ isStrictDynamic: scriptDirective.strictDynamic,
154
+ styleHashes: styleDefault.hashes,
155
+ styleResources: styleDefault.resources,
156
+ scriptDirective,
157
+ styleDirective
143
158
  };
144
159
  }
145
160
  let loggerConfig = void 0;
@@ -7,6 +7,8 @@ export declare const escapeHTML: (str: string) => string;
7
7
  * @see https://mathiasbynens.be/notes/etago
8
8
  */
9
9
  export declare function stringifyForScript(value: any): string;
10
+ /** Escapes CSS text so it can be embedded inside a `<style>` tag. */
11
+ export declare function escapeStyleText(value: string): string;
10
12
  export declare class HTMLBytes extends Uint8Array {
11
13
  }
12
14
  declare const htmlStringSymbol: unique symbol;
@@ -4,6 +4,9 @@ const escapeHTML = escape;
4
4
  function stringifyForScript(value) {
5
5
  return JSON.stringify(value)?.replace(/</g, "\\u003c");
6
6
  }
7
+ function escapeStyleText(value) {
8
+ return value.replaceAll("<", "\\3C ");
9
+ }
7
10
  class HTMLBytes extends Uint8Array {
8
11
  }
9
12
  Object.defineProperty(HTMLBytes.prototype, Symbol.toStringTag, {
@@ -77,6 +80,7 @@ export {
77
80
  HTMLBytes,
78
81
  HTMLString,
79
82
  escapeHTML,
83
+ escapeStyleText,
80
84
  isHTMLBytes,
81
85
  isHTMLString,
82
86
  markHTMLString,
@@ -1,34 +1,95 @@
1
+ import { partitionByKind } from "../../../core/csp/runtime.js";
1
2
  function renderCspContent(result) {
3
+ const { scriptDirective, styleDirective, directives } = result;
4
+ const script = partitionByKind(scriptDirective);
5
+ const style = partitionByKind(styleDirective);
2
6
  const finalScriptHashes = /* @__PURE__ */ new Set();
3
- const finalStyleHashes = /* @__PURE__ */ new Set();
4
- for (const scriptHash of result.scriptHashes) {
7
+ for (const scriptHash of script.default.hashes) {
5
8
  finalScriptHashes.add(`'${scriptHash}'`);
6
9
  }
7
- for (const styleHash of result.styleHashes) {
10
+ for (const scriptHash of result._metadata.extraScriptHashes) {
11
+ finalScriptHashes.add(`'${scriptHash}'`);
12
+ }
13
+ const finalStyleHashes = /* @__PURE__ */ new Set();
14
+ for (const styleHash of style.default.hashes) {
8
15
  finalStyleHashes.add(`'${styleHash}'`);
9
16
  }
10
17
  for (const styleHash of result._metadata.extraStyleHashes) {
11
18
  finalStyleHashes.add(`'${styleHash}'`);
12
19
  }
13
- for (const scriptHash of result._metadata.extraScriptHashes) {
14
- finalScriptHashes.add(`'${scriptHash}'`);
15
- }
16
- let directives;
17
- if (result.directives.length > 0) {
18
- directives = result.directives.join(";") + ";";
20
+ let directivesContent;
21
+ if (directives.length > 0) {
22
+ directivesContent = directives.join(";") + ";";
19
23
  }
20
- let scriptResources = "'self'";
21
- if (result.scriptResources.length > 0) {
22
- scriptResources = result.scriptResources.map((r) => `${r}`).join(" ");
24
+ const scriptResources = script.default.resources.length > 0 ? script.default.resources.join(" ") : "'self'";
25
+ const styleResources = style.default.resources.length > 0 ? style.default.resources.join(" ") : "'self'";
26
+ const scriptElementDefaultResource = script.default.resources.length > 0 ? "" : "'self'";
27
+ const styleElementDefaultResource = style.default.resources.length > 0 ? "" : "'self'";
28
+ const scriptElemActive = isEnabled(script.element);
29
+ const styleElemActive = isEnabled(style.element);
30
+ const strictDynamicSuffix = scriptDirective.strictDynamic ? ` 'strict-dynamic'` : "";
31
+ const scriptBaselineTokens = [
32
+ ...scriptElemActive ? [] : [...finalScriptHashes],
33
+ ...scriptDirective.strictDynamic ? [`'strict-dynamic'`] : []
34
+ ];
35
+ const scriptSrc = `script-src ${scriptResources} ${scriptBaselineTokens.join(" ")};`;
36
+ const styleSrc = `style-src ${styleResources} ${(styleElemActive ? [] : [...finalStyleHashes]).join(" ")};`;
37
+ const scriptSrcElem = scriptElemActive ? renderSpecificDirective(
38
+ "script-src-elem",
39
+ script.element.resources,
40
+ scriptElementDefaultResource,
41
+ finalScriptHashes,
42
+ script.element.hashes,
43
+ strictDynamicSuffix
44
+ ) : void 0;
45
+ const scriptSrcAttr = isEnabled(script.attribute) ? renderSpecificDirective(
46
+ "script-src-attr",
47
+ script.attribute.resources,
48
+ "'none'",
49
+ void 0,
50
+ script.attribute.hashes
51
+ ) : void 0;
52
+ const styleSrcElem = styleElemActive ? renderSpecificDirective(
53
+ "style-src-elem",
54
+ style.element.resources,
55
+ styleElementDefaultResource,
56
+ finalStyleHashes,
57
+ style.element.hashes
58
+ ) : void 0;
59
+ const styleSrcAttr = isEnabled(style.attribute) ? renderSpecificDirective(
60
+ "style-src-attr",
61
+ style.attribute.resources,
62
+ "'none'",
63
+ void 0,
64
+ style.attribute.hashes
65
+ ) : void 0;
66
+ return [
67
+ directivesContent,
68
+ scriptSrc,
69
+ scriptSrcElem,
70
+ scriptSrcAttr,
71
+ styleSrc,
72
+ styleSrcElem,
73
+ styleSrcAttr
74
+ ].filter(Boolean).join(" ");
75
+ }
76
+ function isEnabled(sources) {
77
+ return sources.resources.length > 0 || sources.hashes.length > 0;
78
+ }
79
+ function renderSpecificDirective(name, resources, defaultResource, sharedHashes, ownHashes, suffix = "") {
80
+ const hashes = new Set(sharedHashes);
81
+ for (const hash of ownHashes) {
82
+ hashes.add(`'${hash}'`);
23
83
  }
24
- let styleResources = "'self'";
25
- if (result.styleResources.length > 0) {
26
- styleResources = result.styleResources.map((r) => `${r}`).join(" ");
84
+ let finalResources;
85
+ if (resources.length > 0) {
86
+ finalResources = resources.map((r) => `${r}`).join(" ");
87
+ } else if (defaultResource === "'none'" && hashes.size > 0) {
88
+ finalResources = "";
89
+ } else {
90
+ finalResources = defaultResource;
27
91
  }
28
- const strictDynamic = result.isStrictDynamic ? ` 'strict-dynamic'` : "";
29
- const scriptSrc = `script-src ${scriptResources} ${Array.from(finalScriptHashes).join(" ")}${strictDynamic};`;
30
- const styleSrc = `style-src ${styleResources} ${Array.from(finalStyleHashes).join(" ")};`;
31
- return [directives, scriptSrc, styleSrc].filter(Boolean).join(" ");
92
+ return `${name} ${[finalResources, ...hashes].filter(Boolean).join(" ")}${suffix};`;
32
93
  }
33
94
  export {
34
95
  renderCspContent
@@ -5,6 +5,7 @@ import { createThinHead } from "./astro/head-and-content.js";
5
5
  import { createRenderInstruction } from "./instruction.js";
6
6
  import { SERVER_ISLAND_START } from "./server-islands-shared.js";
7
7
  import { renderSlotToString } from "./slot.js";
8
+ import { toAttributeString } from "./util.js";
8
9
  const internalProps = /* @__PURE__ */ new Set([
9
10
  "server:component-path",
10
11
  "server:component-export",
@@ -148,16 +149,17 @@ class ServerIslandComponent {
148
149
  serverIslandUrl += "?" + potentialSearchParams.toString();
149
150
  this.result._metadata.extraHead.push(
150
151
  markHTMLString(
151
- `<link rel="preload" as="fetch" href="${serverIslandUrl}" crossorigin="anonymous">`
152
+ `<link rel="preload" as="fetch" href="${toAttributeString(serverIslandUrl)}" crossorigin="anonymous">`
152
153
  )
153
154
  );
154
155
  }
155
156
  const adapterHeaders = this.result.internalFetchHeaders || {};
156
157
  const headersJson = stringifyForScript(adapterHeaders);
158
+ const serverIslandUrlJson = stringifyForScript(serverIslandUrl);
157
159
  const method = useGETRequest ? (
158
160
  // GET request
159
161
  `const headers = new Headers(${headersJson});
160
- let response = await fetch('${serverIslandUrl}', { headers });`
162
+ let response = await fetch(${serverIslandUrlJson}, { headers });`
161
163
  ) : (
162
164
  // POST request
163
165
  `let data = {
@@ -166,13 +168,13 @@ let response = await fetch('${serverIslandUrl}', { headers });`
166
168
  encryptedSlots: ${stringifyForScript(slotsEncrypted)},
167
169
  };
168
170
  const headers = new Headers({ 'Content-Type': 'application/json', ...${headersJson} });
169
- let response = await fetch('${serverIslandUrl}', {
171
+ let response = await fetch(${serverIslandUrlJson}, {
170
172
  method: 'POST',
171
173
  body: JSON.stringify(data),
172
174
  headers,
173
175
  });`
174
176
  );
175
- this.islandContent = `${method}replaceServerIsland('${hostId}', response);`;
177
+ this.islandContent = `${method}replaceServerIsland(${stringifyForScript(hostId)}, response);`;
176
178
  return this.islandContent;
177
179
  }
178
180
  }
@@ -1,6 +1,6 @@
1
1
  import cssesc from "../../transitions/cssesc.js";
2
2
  import { fade, slide } from "../../transitions/index.js";
3
- import { markHTMLString } from "./escape.js";
3
+ import { escapeStyleText, markHTMLString } from "./escape.js";
4
4
  const transitionNameMap = /* @__PURE__ */ new WeakMap();
5
5
  function incrementTransitionNumber(result) {
6
6
  let num = 1;
@@ -56,7 +56,7 @@ function renderTransition(result, hash, animationName, transitionName) {
56
56
  sheet.addAnimationRaw("new", "animation: none; mix-blend-mode: normal;");
57
57
  sheet.addModern("group", "animation: none");
58
58
  }
59
- const css = sheet.toString();
59
+ const css = escapeStyleText(sheet.toString());
60
60
  result._metadata.extraHead.push(markHTMLString(`<style>${css}</style>`));
61
61
  return scope;
62
62
  }
@@ -1,4 +1,5 @@
1
1
  import type { Server } from 'node:http';
2
+ import type { CspHash } from '../core/csp/config.js';
2
3
  import type { AstroTimer } from '../core/config/timer.js';
3
4
  import type { TSConfig } from '../core/config/tsconfig.js';
4
5
  import type { AstroLogger, AstroLoggerLevel } from '../core/logger/core.js';
@@ -18,7 +19,6 @@ export type SerializedRouteData = Omit<RouteData, 'generate' | 'pattern' | 'redi
18
19
  trailingSlash: AstroConfig['trailingSlash'];
19
20
  };
20
21
  };
21
- type CspObject = Required<Exclude<AstroConfig['security']['csp'], boolean>>;
22
22
  export interface AstroSettings {
23
23
  config: AstroConfig;
24
24
  adapter: AstroAdapter | undefined;
@@ -64,7 +64,7 @@ export interface AstroSettings {
64
64
  buildOutput: undefined | 'static' | 'server';
65
65
  injectedCsp: {
66
66
  fontResources: Set<string>;
67
- styleHashes: Required<CspObject['styleDirective']>['hashes'];
67
+ styleHashes: CspHash[];
68
68
  };
69
69
  logLevel: AstroLoggerLevel;
70
70
  fontsHttpServer: Server | null;
@@ -89,4 +89,3 @@ export interface ImportedDevStyle {
89
89
  url: string;
90
90
  content: string;
91
91
  }
92
- export {};
@@ -36,6 +36,19 @@ export interface PaginateOptions<PaginateProps extends Props, PaginateParams ext
36
36
  params?: PaginateParams;
37
37
  /** object of props to forward to `page` result */
38
38
  props?: PaginateProps;
39
+ /**
40
+ * Transform each pagination URL before it is set on the page result.
41
+ * Useful for adding a file extension (e.g. `.html`) when deploying to a
42
+ * static file server that does not support clean URLs.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * paginate(items, {
47
+ * format: (url) => `${url}.html`,
48
+ * })
49
+ * ```
50
+ */
51
+ format?: (url: string) => string;
39
52
  }
40
53
  /**
41
54
  * Represents a single page of data in a paginated collection
@@ -9,7 +9,7 @@ import type { AssetsPrefix } from '../../core/app/types.js';
9
9
  import type { CacheProviderConfig, RouteRules } from '../../core/cache/types.js';
10
10
  import type { AstroConfigType } from '../../core/config/schemas/index.js';
11
11
  import type { REDIRECT_STATUS_CODES } from '../../core/constants.js';
12
- import type { CspAlgorithm, CspDirective, CspHash } from '../../core/csp/config.js';
12
+ import type { CspAlgorithm, CspDirective, CspHash, CspHashEntry, CspKind, CspResourceEntry } from '../../core/csp/config.js';
13
13
  import type { AstroLoggerLevel } from '../../core/logger/core.js';
14
14
  import type { SessionConfig, SessionDriverConfig, SessionDriverName } from '../../core/session/types.js';
15
15
  import type { EnvSchema } from '../../env/schema.js';
@@ -21,16 +21,16 @@ export type Locales = (string | {
21
21
  path: string;
22
22
  })[];
23
23
  export type { FontProvider };
24
- export type { CspAlgorithm, CspHash };
24
+ export type { CspAlgorithm, CspHash, CspKind, CspResourceEntry, CspHashEntry };
25
25
  export type { RemotePattern };
26
26
  export type { SvgOptimizer };
27
27
  export type CspStyleDirective = {
28
- hashes?: CspHash[];
29
- resources?: string[];
28
+ hashes?: CspHashEntry[];
29
+ resources?: CspResourceEntry[];
30
30
  };
31
31
  export type CspScriptDirective = {
32
- hashes?: CspHash[];
33
- resources?: string[];
32
+ hashes?: CspHashEntry[];
33
+ resources?: CspResourceEntry[];
34
34
  strictDynamic?: boolean;
35
35
  };
36
36
  type NormalizeLocales<T extends Locales> = {
@@ -776,7 +776,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
776
776
  * @docs
777
777
  * @name security.csp.styleDirective.hashes
778
778
  * @kind h6
779
- * @type {CspHash[]}
779
+ * @type {CspHashEntry[]}
780
780
  * @default `[]`
781
781
  * @version 6.0.0
782
782
  * @description
@@ -785,6 +785,13 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
785
785
  *
786
786
  * 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.
787
787
  *
788
+ * Since Astro v7.1, each entry can be a string or an object. The object allows changing the scope of the hash via the `kind` field. The `kind` field accepts:
789
+ * - `"element"`: it will store the hash in the `style-src-elem` directive
790
+ * - `"attribute"`: it will store the hash in the `style-src-attr` directive
791
+ * - `"default"`: it will store the hash in the `style-src` directive
792
+ *
793
+ * A `"default"` hash goes on `style-src`, or on `style-src-elem` instead once you use `kind: "element"`. It is never added to `style-src-attr`. Astro's generated hashes follow the same rule.
794
+ *
788
795
  * ```js title="astro.config.mjs"
789
796
  * import { defineConfig } from 'astro/config';
790
797
  *
@@ -813,19 +820,56 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
813
820
  * "
814
821
  * >
815
822
  * ```
823
+ *
824
+ * Scoping a hash to `"element"` stores it in `style-src-elem` instead. Astro's generated hashes move there too:
825
+ *
826
+ * ```js title="astro.config.mjs"
827
+ * import { defineConfig } from 'astro/config';
828
+ *
829
+ * export default defineConfig({
830
+ * security: {
831
+ * csp: {
832
+ * styleDirective: {
833
+ * hashes: [{ hash: "sha256-styleHash", kind: "element" }]
834
+ * }
835
+ * }
836
+ * }
837
+ * });
838
+ * ```
839
+ *
840
+ * After the build, the `<meta>` element will include the hash in the `style-src-elem` directive instead of `style-src`:
841
+ *
842
+ * ```html
843
+ * <meta
844
+ * http-equiv="content-security-policy"
845
+ * content="
846
+ * style-src 'self';
847
+ * style-src-elem 'self' 'sha256-styleHash' 'sha256-generatedByAstro';
848
+ * "
849
+ * >
850
+ * ```
816
851
  */
817
- hashes?: CspHash[];
852
+ hashes?: CspHashEntry[];
818
853
  /**
819
854
  * @docs
820
855
  * @name security.csp.styleDirective.resources
821
856
  * @kind h6
822
- * @type {string[]}
857
+ * @type {CspResourceEntry[]}
823
858
  * @default `[]`
824
859
  * @version 6.0.0
825
860
  * @description
826
861
  *
827
862
  * A list of valid sources for `style-src` directives to override Astro's default sources. This will not include `'self'` by default, and must be included in this list if you wish to keep it. These resources are added to all pages.
828
863
  *
864
+ * Since Astro v7.1, each entry can be a string or an object. The object allows changing the scope of the source via the `kind` field. The `kind` field accepts:
865
+ * - `"element"`: it will store the source in the `style-src-elem` directive
866
+ * - `"attribute"`: it will store the source in the `style-src-attr` directive
867
+ * - `"default"`: it will store the source in the `style-src` directive
868
+ *
869
+ * `"attribute"` sources must be one of `'none'`, `'unsafe-hashes'`, `'unsafe-inline'`, or `'report-sample'`, and `'unsafe-hashes'` cannot be used with `"element"`. A common use is allowing inline `style` attributes (for example, from `define:vars` or Shiki) with `{ resource: "'unsafe-inline'", kind: "attribute" }`.
870
+ *
871
+ * Unlike a hash, a `"default"` source is never moved: it stays on `style-src` only. Where there is a mix of "default" and specific resources (e.g. `"element"` or `"attribute"`), Astro emits a warning.
872
+ *
829
873
  * ```js title="astro.config.mjs"
830
874
  * import { defineConfig } from 'astro/config';
831
875
  *
@@ -856,9 +900,40 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
856
900
  * </head>
857
901
  * ```
858
902
  *
903
+ * A `"default"` source is not copied into the more specific directives. Here, a `"default"` source and an `"element"` source render in different directives:
904
+ *
905
+ * ```js title="astro.config.mjs"
906
+ * import { defineConfig } from 'astro/config';
907
+ *
908
+ * export default defineConfig({
909
+ * security: {
910
+ * csp: {
911
+ * styleDirective: {
912
+ * resources: [
913
+ * "https://styles.cdn.example.com",
914
+ * { resource: "https://elements.cdn.example.com", kind: "element" }
915
+ * ]
916
+ * }
917
+ * }
918
+ * }
919
+ * });
920
+ * ```
921
+ *
922
+ * After the build, the `<meta>` element keeps `https://styles.cdn.example.com` on `style-src` only. This means that this value is not added to `style-src-elem` and does not apply to `<style>` and `<link>` elements (the generated hash, by contrast, does move there):
923
+ *
924
+ * ```html
925
+ * <meta
926
+ * http-equiv="content-security-policy"
927
+ * content="
928
+ * style-src https://styles.cdn.example.com;
929
+ * style-src-elem https://elements.cdn.example.com 'sha256-generatedByAstro';
930
+ * "
931
+ * >
932
+ * ```
933
+ *
859
934
  * When resources are inserted multiple times or from multiple sources (e.g. defined in your `csp` config and added using [the CSP runtime API](/en/reference/api-reference/#csp)), Astro will merge and deduplicate all resources to create your `<meta>` element.
860
935
  */
861
- resources?: string[];
936
+ resources?: CspResourceEntry[];
862
937
  };
863
938
  /**
864
939
  * @docs
@@ -876,7 +951,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
876
951
  * @docs
877
952
  * @name security.csp.scriptDirective.hashes
878
953
  * @kind h6
879
- * @type {CspHash[]}
954
+ * @type {CspHashEntry[]}
880
955
  * @default `[]`
881
956
  * @version 6.0.0
882
957
  * @description
@@ -885,6 +960,13 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
885
960
  *
886
961
  * 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.
887
962
  *
963
+ * Since Astro v7.1, each entry can be a string or an object. The object allows changing the scope of the hash via the `kind` field. The `kind` field accepts:
964
+ * - `"element"`: it will store the hash in the `script-src-elem` directive
965
+ * - `"attribute"`: it will store the hash in the `script-src-attr` directive
966
+ * - `"default"`: it will store the hash in the `script-src` directive
967
+ *
968
+ * A `"default"` hash goes on `script-src`, or on `script-src-elem` instead once you use `kind: "element"`. It is never added to `script-src-attr`. Astro's generated hashes follow the same rule.
969
+ *
888
970
  * ```js title="astro.config.mjs"
889
971
  * import { defineConfig } from 'astro/config';
890
972
  *
@@ -913,19 +995,56 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
913
995
  * "
914
996
  * >
915
997
  * ```
998
+ *
999
+ * Scoping a hash to `"element"` stores it in `script-src-elem` instead. Astro's generated hashes move there too:
1000
+ *
1001
+ * ```js title="astro.config.mjs"
1002
+ * import { defineConfig } from 'astro/config';
1003
+ *
1004
+ * export default defineConfig({
1005
+ * security: {
1006
+ * csp: {
1007
+ * scriptDirective: {
1008
+ * hashes: [{ hash: "sha256-scriptHash", kind: "element" }]
1009
+ * }
1010
+ * }
1011
+ * }
1012
+ * });
1013
+ * ```
1014
+ *
1015
+ * After the build, the `<meta>` element will include the hash in the `script-src-elem` directive instead of `script-src`:
1016
+ *
1017
+ * ```html
1018
+ * <meta
1019
+ * http-equiv="content-security-policy"
1020
+ * content="
1021
+ * script-src 'self';
1022
+ * script-src-elem 'self' 'sha256-scriptHash' 'sha256-generatedByAstro';
1023
+ * "
1024
+ * >
1025
+ * ```
916
1026
  */
917
- hashes?: CspHash[];
1027
+ hashes?: CspHashEntry[];
918
1028
  /**
919
1029
  * @docs
920
1030
  * @name security.csp.scriptDirective.resources
921
1031
  * @kind h6
922
- * @type {string[]}
1032
+ * @type {CspResourceEntry[]}
923
1033
  * @default `[]`
924
1034
  * @version 6.0.0
925
1035
  * @description
926
1036
  *
927
1037
  * A list of valid sources for the `script-src` directives to override Astro's default sources. This will not include `'self'` by default, and must be included in this list if you wish to keep it. These resources are added to all pages.
928
1038
  *
1039
+ * Since Astro v7.1, each entry can be a string or an object. The object allows changing the scope of the source via the `kind` field. The `kind` field accepts:
1040
+ * - `"element"`: it will store the source in the `script-src-elem` directive
1041
+ * - `"attribute"`: it will store the source in the `script-src-attr` directive
1042
+ * - `"default"`: it will store the source in the `script-src` directive
1043
+ *
1044
+ * `"attribute"` sources must be one of `'none'`, `'unsafe-hashes'`, `'unsafe-inline'`, or `'report-sample'`, and `'unsafe-hashes'` cannot be used with `"element"`.
1045
+ *
1046
+ * Unlike a hash, a `"default"` source is never moved and stays on `script-src` only. This does not apply where you also scope `"element"` or `"attribute"` sources, since browsers do not fall back. Astro warns when this happens. Add it to the more specific directive as well, if necessary.
1047
+ *
929
1048
  * ```js title="astro.config.mjs"
930
1049
  * import { defineConfig } from 'astro/config';
931
1050
  *
@@ -955,9 +1074,40 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
955
1074
  * </head>
956
1075
  * ```
957
1076
  *
1077
+ * A `"default"` source is not copied into the more specific directives. Here, a `"default"` source and an `"element"` source render in different directives:
1078
+ *
1079
+ * ```js title="astro.config.mjs"
1080
+ * import { defineConfig } from 'astro/config';
1081
+ *
1082
+ * export default defineConfig({
1083
+ * security: {
1084
+ * csp: {
1085
+ * scriptDirective: {
1086
+ * resources: [
1087
+ * "https://cdn.example.com",
1088
+ * { resource: "https://elements.cdn.example.com", kind: "element" }
1089
+ * ]
1090
+ * }
1091
+ * }
1092
+ * }
1093
+ * });
1094
+ * ```
1095
+ *
1096
+ * After the build, the `<meta>` element keeps `https://cdn.example.com` on `script-src` only. This means that this value is not added to `script-src-elem` and does not apply to `<script>` elements (the generated hash, by contrast, does move there):
1097
+ *
1098
+ * ```html
1099
+ * <meta
1100
+ * http-equiv="content-security-policy"
1101
+ * content="
1102
+ * script-src https://cdn.example.com;
1103
+ * script-src-elem https://elements.cdn.example.com 'sha256-generatedByAstro';
1104
+ * "
1105
+ * >
1106
+ * ```
1107
+ *
958
1108
  * When resources are inserted multiple times or from multiple sources (e.g. defined in your `csp` config and added using [the CSP runtime API](/en/reference/api-reference/#csp)), Astro will merge and deduplicate all resources to create your `<meta>` element.
959
1109
  */
960
- resources?: string[];
1110
+ resources?: CspResourceEntry[];
961
1111
  /**
962
1112
  * @docs
963
1113
  * @name security.csp.scriptDirective.strictDynamic
@@ -982,6 +1132,8 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
982
1132
  * }
983
1133
  * });
984
1134
  * ```
1135
+ *
1136
+ * This applies to `script-src`. When you also scope `script` resources or hashes to `script-src-elem` (using `kind: "element"`), `strict-dynamic` is inherited by `script-src-elem` so that dynamically injected `<script>` elements continue to work.
985
1137
  */
986
1138
  strictDynamic?: boolean;
987
1139
  };
@@ -1379,7 +1531,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
1379
1531
  /**
1380
1532
  * @docs
1381
1533
  * @name logger.entrypoint
1382
- * @type {string}
1534
+ * @type {string | URL}
1383
1535
  * @version 7.0.0
1384
1536
  * @description
1385
1537
  *
@@ -2974,6 +3126,35 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2974
3126
  * See the [experimental SVG optimization docs](https://docs.astro.build/en/reference/experimental-flags/svg-optimization/) for more information.
2975
3127
  */
2976
3128
  svgOptimizer?: SvgOptimizer;
3129
+ /**
3130
+ * @name experimental.collectionStorage
3131
+ * @type {'single-file' | 'chunked'}
3132
+ * @default `'single-file'`
3133
+ * @version 7.1.0
3134
+ * @description
3135
+ *
3136
+ * Controls how Astro persists the content layer data store.
3137
+ *
3138
+ * By default, the entire store is written to a single `data-store.json`
3139
+ * file. For very large content collections, this file can grow large
3140
+ * enough to hit platform file-size limits.
3141
+ *
3142
+ * When set to `'chunked'`, the store is split across many smaller,
3143
+ * content-addressed files so that no single file grows unbounded.
3144
+ *
3145
+ * ```js
3146
+ * import { defineConfig } from 'astro/config';
3147
+ *
3148
+ * export default defineConfig({
3149
+ * experimental: {
3150
+ * collectionStorage: 'chunked',
3151
+ * },
3152
+ * });
3153
+ * ```
3154
+ *
3155
+ * See the [experimental data store chunking documentation](https://docs.astro.build/en/reference/experimental-flags/collection-storage/) for more information.
3156
+ */
3157
+ collectionStorage?: 'single-file' | 'chunked';
2977
3158
  };
2978
3159
  }
2979
3160
  /**