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
@@ -1,6 +1,7 @@
1
1
  import colors from "piccolore";
2
2
  import {
3
3
  collapseDuplicateLeadingSlashes,
4
+ collapseDuplicateSlashes,
4
5
  prependForwardSlash,
5
6
  removeTrailingForwardSlash
6
7
  } from "@astrojs/internal-helpers/path";
@@ -16,7 +17,7 @@ import {
16
17
  pipelineSymbol,
17
18
  responseSentSymbol
18
19
  } from "../constants.js";
19
- import { pushDirective } from "../csp/runtime.js";
20
+ import { normalizeCspResourceEntry, pushDirective } from "../csp/runtime.js";
20
21
  import { generateCspDigest } from "../encryption.js";
21
22
  import { AstroError, AstroErrorData } from "../errors/index.js";
22
23
  import {
@@ -28,7 +29,6 @@ import {
28
29
  import { getParams, getProps } from "../render/index.js";
29
30
  import { Rewrites } from "../rewrites/handler.js";
30
31
  import { isRoute404or500, isRouteServerIsland } from "../routing/match.js";
31
- import { normalizeUrl } from "../util/normalized-url.js";
32
32
  import { MultiLevelEncodingError, validateAndDecodePathname } from "../util/pathname.js";
33
33
  import { getOriginPathname, setOriginPathname } from "../routing/rewrite.js";
34
34
  import { computePathnameFromDomain } from "../i18n/domain.js";
@@ -178,28 +178,29 @@ class FetchState {
178
178
  this.componentInstance = void 0;
179
179
  this.slots = void 0;
180
180
  const url = new URL(request.url);
181
+ const publicPathname = this.#normalizePathname(url.pathname);
182
+ const pathname = this.#computePathname(publicPathname);
183
+ url.pathname = publicPathname;
184
+ url.pathname = collapseDuplicateSlashes(url.pathname);
181
185
  const domainPathname = computePathnameFromDomain(
182
186
  request,
183
187
  url,
184
188
  pipeline.manifest.i18n,
185
189
  pipeline.manifest.base,
186
190
  pipeline.manifest.trailingSlash,
187
- pipeline.logger
191
+ pipeline.logger,
192
+ pathname
188
193
  );
189
194
  if (domainPathname) {
190
195
  this.#domainPathname = domainPathname;
191
- try {
192
- this.pathname = decodeURI(domainPathname);
193
- } catch {
194
- this.pathname = domainPathname;
195
- }
196
+ this.pathname = domainPathname;
196
197
  } else {
197
- this.pathname = this.#computePathname(url);
198
+ this.pathname = pathname;
198
199
  }
199
200
  this.timeStart = performance.now();
200
201
  this.clientAddress = options?.clientAddress;
201
202
  this.locals = options?.locals ?? {};
202
- this.url = normalizeUrl(url);
203
+ this.url = url;
203
204
  this.cookies = new AstroCookies(request);
204
205
  if (pipeline.manifest.allowedDomains && pipeline.manifest.allowedDomains.length > 0 && !this.routeData?.prerender) {
205
206
  this.#applyForwardedHeaders();
@@ -304,12 +305,26 @@ class FetchState {
304
305
  cspDestination: manifest.csp?.cspDestination ?? (routeData.prerender ? "meta" : "header"),
305
306
  shouldInjectCspMetaTags,
306
307
  cspAlgorithm,
308
+ directives: manifest.csp?.directives ? [...manifest.csp.directives] : [],
309
+ // Deprecated flat fields, kept for back-compat. Seeded from the manifest; the runtime CSP
310
+ // API updates the structured `scriptDirective`/`styleDirective` below (which the renderer
311
+ // reads), not these.
307
312
  scriptHashes: manifest.csp?.scriptHashes ? [...manifest.csp.scriptHashes] : [],
308
313
  scriptResources: manifest.csp?.scriptResources ? [...manifest.csp.scriptResources] : [],
309
314
  styleHashes: manifest.csp?.styleHashes ? [...manifest.csp.styleHashes] : [],
310
315
  styleResources: manifest.csp?.styleResources ? [...manifest.csp.styleResources] : [],
311
- directives: manifest.csp?.directives ? [...manifest.csp.directives] : [],
312
316
  isStrictDynamic: manifest.csp?.isStrictDynamic ?? false,
317
+ // Structured fields (source of truth). Arrays are cloned so per-request runtime inserts
318
+ // don't mutate the shared manifest.
319
+ scriptDirective: {
320
+ resources: manifest.csp?.scriptDirective ? [...manifest.csp.scriptDirective.resources] : [],
321
+ hashes: manifest.csp?.scriptDirective ? [...manifest.csp.scriptDirective.hashes] : [],
322
+ strictDynamic: manifest.csp?.scriptDirective?.strictDynamic ?? false
323
+ },
324
+ styleDirective: {
325
+ resources: manifest.csp?.styleDirective ? [...manifest.csp.styleDirective.resources] : [],
326
+ hashes: manifest.csp?.styleDirective ? [...manifest.csp.styleDirective.hashes] : []
327
+ },
313
328
  internalFetchHeaders: manifest.internalFetchHeaders
314
329
  };
315
330
  this.result = result;
@@ -451,23 +466,51 @@ class FetchState {
451
466
  }
452
467
  return void 0;
453
468
  }
469
+ const warnedFallback = /* @__PURE__ */ new Set();
470
+ const warnFallback = (family, kind) => {
471
+ if (kind === "default" || !state.result) {
472
+ return;
473
+ }
474
+ const directive = family === "script" ? state.result.scriptDirective : state.result.styleDirective;
475
+ const defaultResources = directive.resources.map(normalizeCspResourceEntry).filter((entry) => entry.kind === "default").map((entry) => entry.resource);
476
+ if (defaultResources.length === 0) {
477
+ return;
478
+ }
479
+ const key = `${family}:${kind}`;
480
+ if (warnedFallback.has(key)) {
481
+ return;
482
+ }
483
+ warnedFallback.add(key);
484
+ const general = `${family}-src`;
485
+ const specific = `${general}-${kind === "element" ? "elem" : "attr"}`;
486
+ pipeline.logger.warn(
487
+ "csp",
488
+ `A resource was added to \`${specific}\`, but \`${general}\` also defines custom resources (${defaultResources.join(
489
+ " "
490
+ )}). Because \`${specific}\` overrides \`${general}\` for its scope (browsers do not fall back), those resources will not apply there. Add them to \`${specific}\` as well if needed.`
491
+ );
492
+ };
454
493
  return {
455
494
  insertDirective(payload) {
456
495
  if (state.result) {
457
496
  state.result.directives = pushDirective(state.result.directives, payload);
458
497
  }
459
498
  },
460
- insertScriptResource(resource) {
461
- state.result?.scriptResources.push(resource);
499
+ insertScriptResource(payload) {
500
+ if (!state.result) return;
501
+ warnFallback("script", normalizeCspResourceEntry(payload).kind);
502
+ state.result.scriptDirective.resources.push(payload);
462
503
  },
463
- insertStyleResource(resource) {
464
- state.result?.styleResources.push(resource);
504
+ insertStyleResource(payload) {
505
+ if (!state.result) return;
506
+ warnFallback("style", normalizeCspResourceEntry(payload).kind);
507
+ state.result.styleDirective.resources.push(payload);
465
508
  },
466
- insertStyleHash(hash) {
467
- state.result?.styleHashes.push(hash);
509
+ insertStyleHash(payload) {
510
+ state.result?.styleDirective.hashes.push(payload);
468
511
  },
469
- insertScriptHash(hash) {
470
- state.result?.scriptHashes.push(hash);
512
+ insertScriptHash(payload) {
513
+ state.result?.scriptDirective.hashes.push(payload);
471
514
  }
472
515
  };
473
516
  }
@@ -690,32 +733,37 @@ class FetchState {
690
733
  this.#stripHtmlExtension();
691
734
  }
692
735
  /**
693
- * Strips the pipeline's base from the request URL, prepends a forward
694
- * slash, and decodes the pathname. Falls back to the raw (not decoded)
695
- * pathname if `decodeURI` throws.
736
+ * Strips the pipeline's base from a normalized request pathname and prepends
737
+ * a forward slash.
696
738
  *
697
739
  * Mirrors `BaseApp.removeBase`, including the
698
740
  * `collapseDuplicateLeadingSlashes` fix that prevents middleware
699
741
  * authorization bypass when the URL starts with `//`.
700
742
  */
701
- #computePathname(url) {
702
- let pathname = collapseDuplicateLeadingSlashes(url.pathname);
743
+ #computePathname(normalizedPathname) {
744
+ let pathname = collapseDuplicateLeadingSlashes(normalizedPathname);
703
745
  const base = this.pipeline.manifest.base;
704
746
  if (pathname.startsWith(base)) {
705
747
  const baseWithoutTrailingSlash = removeTrailingForwardSlash(base);
706
748
  pathname = pathname.slice(baseWithoutTrailingSlash.length + 1);
707
749
  }
708
- pathname = prependForwardSlash(pathname);
750
+ return prependForwardSlash(pathname);
751
+ }
752
+ /**
753
+ * Decodes and normalizes the public request pathname before deriving the
754
+ * separate pathname used for route matching.
755
+ */
756
+ #normalizePathname(pathname) {
709
757
  try {
710
- return validateAndDecodePathname(pathname);
758
+ pathname = validateAndDecodePathname(pathname);
711
759
  } catch (e) {
712
760
  if (e instanceof MultiLevelEncodingError) {
713
761
  this.invalidEncoding = true;
714
- return pathname;
762
+ } else {
763
+ this.pipeline.logger.error(null, e.toString());
715
764
  }
716
- this.pipeline.logger.error(null, e.toString());
717
- return pathname;
718
765
  }
766
+ return collapseDuplicateSlashes(pathname);
719
767
  }
720
768
  /**
721
769
  * Reads X-Forwarded-Proto, X-Forwarded-Host, and X-Forwarded-Port
@@ -9,4 +9,4 @@ import type { AstroLogger } from '../logger/core.js';
9
9
  * mapped to a locale — in which case normal pathname routing applies.
10
10
  *
11
11
  */
12
- export declare function computePathnameFromDomain(request: Request, url: URL, i18n: SSRManifest['i18n'], base: SSRManifest['base'], trailingSlash: SSRManifest['trailingSlash'], logger: AstroLogger): string | undefined;
12
+ export declare function computePathnameFromDomain(request: Request, url: URL, i18n: SSRManifest['i18n'], base: SSRManifest['base'], trailingSlash: SSRManifest['trailingSlash'], logger: AstroLogger, pathnameFromRequest?: string): string | undefined;
@@ -6,7 +6,7 @@ import {
6
6
  removeTrailingForwardSlash
7
7
  } from "@astrojs/internal-helpers/path";
8
8
  import { normalizeTheLocale } from "../../i18n/path.js";
9
- function computePathnameFromDomain(request, url, i18n, base, trailingSlash, logger) {
9
+ function computePathnameFromDomain(request, url, i18n, base, trailingSlash, logger, pathnameFromRequest) {
10
10
  let pathname = void 0;
11
11
  if (i18n && (i18n.strategy === "domains-prefix-always" || i18n.strategy === "domains-prefix-other-locales" || i18n.strategy === "domains-prefix-always-no-redirect")) {
12
12
  let host = request.headers.get("X-Forwarded-Host");
@@ -32,14 +32,13 @@ function computePathnameFromDomain(request, url, i18n, base, trailingSlash, logg
32
32
  }
33
33
  }
34
34
  if (locale) {
35
- pathname = prependForwardSlash(
36
- joinPaths(normalizeTheLocale(locale), removeBase(url.pathname, base))
37
- );
35
+ const requestPathname = pathnameFromRequest ?? removeBase(url.pathname, base);
36
+ pathname = prependForwardSlash(joinPaths(normalizeTheLocale(locale), requestPathname));
38
37
  if (trailingSlash === "always") {
39
38
  pathname = appendForwardSlash(pathname);
40
39
  } else if (trailingSlash === "never") {
41
40
  pathname = removeTrailingForwardSlash(pathname);
42
- } else if (url.pathname.endsWith("/")) {
41
+ } else if (requestPathname.endsWith("/")) {
43
42
  pathname = appendForwardSlash(pathname);
44
43
  }
45
44
  }
@@ -2,5 +2,5 @@ export interface LoggerHandlerConfig {
2
2
  /** Serializable options used by the driver implementation */
3
3
  config?: Record<string, any> | undefined;
4
4
  /** URL or package import */
5
- entrypoint: string;
5
+ entrypoint: string | URL;
6
6
  }
@@ -1,8 +1,8 @@
1
- export interface AstroLoggerDestination<T = unknown> {
1
+ export interface AstroLoggerDestination {
2
2
  /**
3
3
  * It receives a message and writes it into a destination
4
4
  */
5
- write: (chunk: T) => void;
5
+ write: (chunk: AstroLoggerMessage) => void;
6
6
  /**
7
7
  * It dumps logs without closing the connection to the destination.
8
8
  * Method that can be used by specialized loggers.
@@ -31,7 +31,7 @@ export type AstroLoggerLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
31
31
  declare const AstroLoggerLabels: readonly ["add", "build", "check", "config", "content", "crypto", "deprecated", "markdown", "router", "types", "vite", "watch", "middleware", "preferences", "redirects", "sync", "session", "toolbar", "assets", "env", "update", "adapter", "islands", "cache", "csp", "SKIP_FORMAT"];
32
32
  type AstroLoggerLabel = (typeof AstroLoggerLabels)[number];
33
33
  export interface AstroLogOptions {
34
- destination: AstroLoggerDestination<AstroLoggerMessage>;
34
+ destination: AstroLoggerDestination;
35
35
  level: AstroLoggerLevel;
36
36
  /**
37
37
  * Optional configuration for the logger destination
@@ -78,7 +78,7 @@ export declare class AstroLogger {
78
78
  debug(label: AstroLoggerLabel, ...messages: any[]): void;
79
79
  level(): AstroLoggerLevel;
80
80
  forkIntegrationLogger(label: string): AstroIntegrationLogger;
81
- setDestination(destination: AstroLoggerDestination<AstroLoggerMessage>): void;
81
+ setDestination(destination: AstroLoggerDestination): void;
82
82
  /**
83
83
  * It calls the `close` function of the provided destination, if it exists.
84
84
  */
@@ -1,4 +1,4 @@
1
- import { type AstroLoggerMessage, type AstroLoggerDestination, type AstroLoggerLevel, AstroLogger } from '../core.js';
1
+ import { type AstroLoggerDestination, type AstroLoggerLevel, AstroLogger } from '../core.js';
2
2
  import type { NodeHandlerConfig } from './node.js';
3
3
  export type ConsoleHandlerConfig = {
4
4
  level?: AstroLoggerLevel;
@@ -6,4 +6,4 @@ export type ConsoleHandlerConfig = {
6
6
  export declare function createConsoleLogger({ level }: {
7
7
  level: AstroLoggerLevel;
8
8
  }): AstroLogger;
9
- export default function (options?: NodeHandlerConfig): AstroLoggerDestination<AstroLoggerMessage>;
9
+ export default function (options?: NodeHandlerConfig): AstroLoggerDestination;
@@ -1,4 +1,4 @@
1
- import { AstroLogger, type AstroLoggerDestination, type AstroLoggerLevel, type AstroLoggerMessage } from '../core.js';
1
+ import { AstroLogger, type AstroLoggerDestination, type AstroLoggerLevel } from '../core.js';
2
2
  import type { AstroInlineConfig } from '../../../types/public/index.js';
3
3
  export type JsonHandlerConfig = {
4
4
  /**
@@ -11,5 +11,5 @@ export type JsonHandlerConfig = {
11
11
  level?: AstroLoggerLevel;
12
12
  };
13
13
  export declare const SGR_REGEX: RegExp;
14
- export default function jsonLoggerDestination(config?: JsonHandlerConfig): AstroLoggerDestination<AstroLoggerMessage>;
14
+ export default function jsonLoggerDestination(config?: JsonHandlerConfig): AstroLoggerDestination;
15
15
  export declare function createJsonLoggerFromFlags(config: AstroInlineConfig): AstroLogger;
@@ -8,24 +8,13 @@ function jsonLoggerDestination(config = {}) {
8
8
  const { pretty = false, level = "info" } = config;
9
9
  return {
10
10
  write(event) {
11
- let dest = process.stderr;
12
- if (levels[event.level] < levels["error"]) {
13
- dest = process.stdout;
14
- }
15
11
  if (!matchesLevel(event.level, level)) {
16
12
  return;
17
13
  }
18
- let trailingLine = event.newLine ? "\n" : "";
14
+ const dest = levels[event.level] >= levels["error"] ? console.error : console.info;
19
15
  const message = event.message.replace(SGR_REGEX, "");
20
- if (pretty) {
21
- dest.write(
22
- JSON.stringify({ message, label: event.label, level: event.level }, null, 2) + trailingLine
23
- );
24
- } else {
25
- dest.write(
26
- JSON.stringify({ message, label: event.label, level: event.level }) + trailingLine
27
- );
28
- }
16
+ const payload = pretty ? JSON.stringify({ message, label: event.label, level: event.level }, null, 2) : JSON.stringify({ message, label: event.label, level: event.level });
17
+ dest(payload);
29
18
  }
30
19
  };
31
20
  }
@@ -1,7 +1,7 @@
1
- import { AstroLogger, type AstroLoggerDestination, type AstroLoggerLevel, type AstroLoggerMessage } from '../core.js';
1
+ import { AstroLogger, type AstroLoggerDestination, type AstroLoggerLevel } from '../core.js';
2
2
  import type { AstroInlineConfig } from '../../../types/public/index.js';
3
3
  export type NodeHandlerConfig = {
4
4
  level?: AstroLoggerLevel;
5
5
  };
6
- export default function (options?: NodeHandlerConfig): AstroLoggerDestination<AstroLoggerMessage>;
6
+ export default function (options?: NodeHandlerConfig): AstroLoggerDestination;
7
7
  export declare function createNodeLoggerFromFlags(inlineConfig: AstroInlineConfig): AstroLogger;
@@ -1,7 +1,7 @@
1
- import { AstroLogger, type AstroLoggerLevel } from './core.js';
1
+ import { AstroLogger, type AstroLoggerDestination } from './core.js';
2
2
  import type { LoggerHandlerConfig } from './config.js';
3
3
  import type { AstroConfig, AstroInlineConfig } from '../../types/public/index.js';
4
- export declare function loadLogger(config: LoggerHandlerConfig, level?: AstroLoggerLevel): Promise<AstroLogger>;
4
+ export declare function loadLoggerDestination(config: LoggerHandlerConfig): Promise<AstroLoggerDestination>;
5
5
  /**
6
6
  * It attempts to load a logger from the entrypoint.
7
7
  * If not provided, it creates a new logger instance on the fly.
@@ -5,27 +5,22 @@ import { default as nodeLoggerCreator, createNodeLoggerFromFlags } from "./impls
5
5
  import { default as consoleLoggerCreator } from "./impls/console.js";
6
6
  import { default as jsonLoggerCreator } from "./impls/json.js";
7
7
  import { default as composeLoggerCreator } from "./impls/compose.js";
8
- async function loadLogger(config, level = "info") {
8
+ function normalizeEntrypoint(entrypoint) {
9
+ return entrypoint instanceof URL ? entrypoint.href : entrypoint;
10
+ }
11
+ async function loadLoggerDestination(config) {
9
12
  let cause = void 0;
13
+ const entrypoint = normalizeEntrypoint(config.entrypoint);
10
14
  try {
11
15
  switch (config.entrypoint) {
12
16
  case "astro/logger/node": {
13
- return new AstroLogger({
14
- destination: nodeLoggerCreator(config.config),
15
- level
16
- });
17
+ return nodeLoggerCreator(config.config);
17
18
  }
18
19
  case "astro/logger/console": {
19
- return new AstroLogger({
20
- destination: consoleLoggerCreator(config.config),
21
- level
22
- });
20
+ return consoleLoggerCreator(config.config);
23
21
  }
24
22
  case "astro/logger/json": {
25
- return new AstroLogger({
26
- destination: jsonLoggerCreator(config.config),
27
- level
28
- });
23
+ return jsonLoggerCreator(config.config);
29
24
  }
30
25
  case "astro/logger/compose": {
31
26
  let destinations = [];
@@ -35,26 +30,20 @@ async function loadLogger(config, level = "info") {
35
30
  loggers.map(async (loggerConfig) => {
36
31
  const logger = await import(
37
32
  /* @vite-ignore */
38
- loggerConfig.entrypoint
33
+ normalizeEntrypoint(loggerConfig.entrypoint)
39
34
  );
40
35
  return logger.default(loggerConfig.config);
41
36
  })
42
37
  );
43
38
  }
44
- return new AstroLogger({
45
- destination: composeLoggerCreator(destinations),
46
- level
47
- });
39
+ return composeLoggerCreator(destinations);
48
40
  }
49
41
  default: {
50
- const nodeLogger = await import(
42
+ const logger = await import(
51
43
  /* @vite-ignore */
52
- config.entrypoint
44
+ entrypoint
53
45
  );
54
- return new AstroLogger({
55
- destination: nodeLogger.default(config.config),
56
- level
57
- });
46
+ return logger.default(config.config);
58
47
  }
59
48
  }
60
49
  } catch (e) {
@@ -64,7 +53,7 @@ async function loadLogger(config, level = "info") {
64
53
  }
65
54
  const error = new AstroError({
66
55
  ...UnableToLoadLogger,
67
- message: UnableToLoadLogger.message(config.entrypoint)
56
+ message: UnableToLoadLogger.message(entrypoint)
68
57
  });
69
58
  if (cause) {
70
59
  error.cause = cause;
@@ -75,7 +64,10 @@ async function loadOrCreateNodeLogger(astroConfig, inlineAstroConfig) {
75
64
  if (inlineAstroConfig._logger) return inlineAstroConfig._logger;
76
65
  try {
77
66
  if (astroConfig.logger) {
78
- return await loadLogger(astroConfig.logger, inlineAstroConfig.logLevel);
67
+ return new AstroLogger({
68
+ destination: await loadLoggerDestination(astroConfig.logger),
69
+ level: inlineAstroConfig.logLevel ?? "info"
70
+ });
79
71
  } else {
80
72
  return createNodeLoggerFromFlags(inlineAstroConfig);
81
73
  }
@@ -84,6 +76,6 @@ async function loadOrCreateNodeLogger(astroConfig, inlineAstroConfig) {
84
76
  }
85
77
  }
86
78
  export {
87
- loadLogger,
79
+ loadLoggerDestination,
88
80
  loadOrCreateNodeLogger
89
81
  };
@@ -58,3 +58,11 @@ export declare function printHelp({ commandName, headline, usage, tables, descri
58
58
  description?: string;
59
59
  }): void;
60
60
  export declare function warnIfCspWithShiki(config: AstroConfig, logger: AstroLogger): void;
61
+ /**
62
+ * Warns when a `scriptDirective`/`styleDirective` defines `default`-kind resources alongside
63
+ * `element`/`attribute`-kind entries. Because the more specific directive (`*-src-elem`/`*-src-attr`)
64
+ * overrides the generic one for its scope and browsers do not fall back, the generic resources will
65
+ * not apply there. Astro's generated hashes are folded automatically, so this only concerns
66
+ * user-provided resources.
67
+ */
68
+ export declare function warnIfCspResourceFallbackShadowing(config: AstroConfig, logger: AstroLogger): void;
@@ -1,4 +1,5 @@
1
1
  import colors from "piccolore";
2
+ import { partitionByKind } from "../csp/runtime.js";
2
3
  import { getDocsForError, renderErrorMarkdown } from "../errors/dev/runtime.js";
3
4
  import {
4
5
  AstroError,
@@ -269,7 +270,7 @@ function printHelp({
269
270
  message.push(
270
271
  linebreak(),
271
272
  ` ${bgGreen(black(` ${commandName} `))} ${green(
272
- `v${"7.0.9"}`
273
+ `v${"7.1.1"}`
273
274
  )} ${headline}`
274
275
  );
275
276
  }
@@ -304,6 +305,37 @@ function warnIfCspWithShiki(config, logger) {
304
305
  );
305
306
  }
306
307
  }
308
+ function warnIfCspResourceFallbackShadowing(config, logger) {
309
+ const csp = config.security.csp;
310
+ if (typeof csp !== "object") return;
311
+ const families = [
312
+ { name: "script", directive: csp.scriptDirective },
313
+ { name: "style", directive: csp.styleDirective }
314
+ ];
315
+ for (const { name, directive } of families) {
316
+ const sources = partitionByKind({
317
+ resources: directive?.resources ?? [],
318
+ hashes: directive?.hashes ?? []
319
+ });
320
+ if (sources.default.resources.length === 0) continue;
321
+ const shadowed = [];
322
+ if (sources.element.resources.length > 0 || sources.element.hashes.length > 0) {
323
+ shadowed.push(`\`${name}-src-elem\``);
324
+ }
325
+ if (sources.attribute.resources.length > 0 || sources.attribute.hashes.length > 0) {
326
+ shadowed.push(`\`${name}-src-attr\``);
327
+ }
328
+ if (shadowed.length === 0) continue;
329
+ logger.warn(
330
+ "csp",
331
+ `\`security.csp.${name}Directive\` defines \`${name}-src\` resources (${sources.default.resources.join(
332
+ " "
333
+ )}) as well as ${shadowed.join(" and ")} resources or hashes. Because ${shadowed.join(
334
+ " and "
335
+ )} override \`${name}-src\` for their scope (browsers do not fall back), those \`${name}-src\` resources will not apply there. Add them to the corresponding \`kind\` if needed.`
336
+ );
337
+ }
338
+ }
307
339
  export {
308
340
  actionRequired,
309
341
  cancelled,
@@ -327,5 +359,6 @@ export {
327
359
  telemetryEnabled,
328
360
  telemetryNotice,
329
361
  telemetryReset,
362
+ warnIfCspResourceFallbackShadowing,
330
363
  warnIfCspWithShiki
331
364
  };
@@ -4,11 +4,12 @@ import { getRouteGenerator } from "../routing/generator.js";
4
4
  function generatePaginateFunction(routeMatch, base, trailingSlash) {
5
5
  return function paginateUtility(data, args = {}) {
6
6
  const generate = getRouteGenerator(routeMatch.segments, trailingSlash);
7
- let { pageSize: _pageSize, params: _params, props: _props } = args;
7
+ let { pageSize: _pageSize, params: _params, props: _props, format: _format } = args;
8
8
  const pageSize = _pageSize || 10;
9
9
  const paramName = "page";
10
10
  const additionalParams = _params || {};
11
11
  const additionalProps = _props || {};
12
+ const formatUrl = _format || ((url) => url);
12
13
  let includesFirstPageNumber;
13
14
  if (routeMatch.params.includes(`...${paramName}`)) {
14
15
  includesFirstPageNumber = false;
@@ -29,23 +30,27 @@ function generatePaginateFunction(routeMatch, base, trailingSlash) {
29
30
  ...additionalParams,
30
31
  [paramName]: includesFirstPageNumber || pageNum > 1 ? String(pageNum) : void 0
31
32
  };
32
- const current = addRouteBase(generate({ ...params }), base);
33
- const next = pageNum === lastPage ? void 0 : addRouteBase(generate({ ...params, page: String(pageNum + 1) }), base);
34
- const prev = pageNum === 1 ? void 0 : addRouteBase(
35
- generate({
36
- ...params,
37
- page: !includesFirstPageNumber && pageNum - 1 === 1 ? void 0 : String(pageNum - 1)
38
- }),
39
- base
33
+ const current = formatUrl(addRouteBase(generate({ ...params }), base));
34
+ const next = pageNum === lastPage ? void 0 : formatUrl(addRouteBase(generate({ ...params, page: String(pageNum + 1) }), base));
35
+ const prev = pageNum === 1 ? void 0 : formatUrl(
36
+ addRouteBase(
37
+ generate({
38
+ ...params,
39
+ page: !includesFirstPageNumber && pageNum - 1 === 1 ? void 0 : String(pageNum - 1)
40
+ }),
41
+ base
42
+ )
40
43
  );
41
- const first = pageNum === 1 ? void 0 : addRouteBase(
42
- generate({
43
- ...params,
44
- page: includesFirstPageNumber ? "1" : void 0
45
- }),
46
- base
44
+ const first = pageNum === 1 ? void 0 : formatUrl(
45
+ addRouteBase(
46
+ generate({
47
+ ...params,
48
+ page: includesFirstPageNumber ? "1" : void 0
49
+ }),
50
+ base
51
+ )
47
52
  );
48
- const last = pageNum === lastPage ? void 0 : addRouteBase(generate({ ...params, page: String(lastPage) }), base);
53
+ const last = pageNum === lastPage ? void 0 : formatUrl(addRouteBase(generate({ ...params, page: String(lastPage) }), base));
49
54
  return {
50
55
  params,
51
56
  props: {
@@ -6,7 +6,7 @@ import colors from "piccolore";
6
6
  import { createServer } from "vite";
7
7
  import { syncFonts } from "../../assets/fonts/sync.js";
8
8
  import { CONTENT_TYPES_FILE } from "../../content/consts.js";
9
- import { getDataStoreFile } from "../../content/content-layer.js";
9
+ import { getDataStoreDir, getDataStoreFile } from "../../content/paths.js";
10
10
  import { globalContentLayer } from "../../content/instance.js";
11
11
  import { createContentTypesGenerator } from "../../content/index.js";
12
12
  import { MutableDataStore } from "../../content/mutable-data-store.js";
@@ -59,11 +59,21 @@ async function clearContentLayerCache({
59
59
  fs = fsMod,
60
60
  isDev
61
61
  }) {
62
- const dataStore = getDataStoreFile(settings, isDev);
63
- if (fs.existsSync(dataStore)) {
64
- logger.debug("content", "clearing data store");
65
- await fs.promises.rm(dataStore, { force: true });
66
- logger.warn("content", "data store cleared (force)");
62
+ if (settings.config.experimental.collectionStorage === "chunked") {
63
+ const dataStore = getDataStoreDir(settings, isDev);
64
+ if (fs.existsSync(dataStore)) {
65
+ logger.debug("content", "clearing data store");
66
+ await fs.promises.rm(dataStore, { force: true, recursive: true });
67
+ await fs.promises.mkdir(dataStore, { recursive: true });
68
+ logger.warn("content", "data store cleared (force)");
69
+ }
70
+ } else {
71
+ const dataStore = getDataStoreFile(settings, isDev);
72
+ if (fs.existsSync(dataStore)) {
73
+ logger.debug("content", "clearing data store");
74
+ await fs.promises.rm(dataStore, { force: true });
75
+ logger.warn("content", "data store cleared (force)");
76
+ }
67
77
  }
68
78
  }
69
79
  async function syncInternal({
@@ -88,8 +98,13 @@ async function syncInternal({
88
98
  settings.timer.start("Sync content layer");
89
99
  let store;
90
100
  try {
91
- const dataStoreFile = getDataStoreFile(settings, isDev);
92
- store = await MutableDataStore.fromFile(dataStoreFile);
101
+ if (settings.config.experimental.collectionStorage === "chunked") {
102
+ const dataStoreDir = getDataStoreDir(settings, isDev);
103
+ store = await MutableDataStore.fromDir(dataStoreDir);
104
+ } else {
105
+ const dataStoreFile = getDataStoreFile(settings, isDev);
106
+ store = await MutableDataStore.fromFile(dataStoreFile);
107
+ }
93
108
  } catch (err) {
94
109
  logger.error("content", err.message);
95
110
  }