astro 7.2.9 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/assets/build/generate.d.ts +1 -1
  2. package/dist/assets/build/generate.js +3 -1
  3. package/dist/assets/endpoint/dev.js +6 -5
  4. package/dist/assets/endpoint/generic.js +5 -4
  5. package/dist/assets/endpoint/node.js +3 -3
  6. package/dist/assets/endpoint/shared.d.ts +3 -1
  7. package/dist/assets/endpoint/shared.js +9 -3
  8. package/dist/assets/fonts/config.d.ts +2 -2
  9. package/dist/assets/internal.d.ts +2 -1
  10. package/dist/assets/internal.js +11 -10
  11. package/dist/assets/services/service.d.ts +8 -7
  12. package/dist/assets/services/sharp.js +5 -5
  13. package/dist/assets/vite-plugin-assets.js +22 -5
  14. package/dist/cli/add/index.js +3 -1
  15. package/dist/cli/index.js +1 -1
  16. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  17. package/dist/cli/preferences/index.js +32 -30
  18. package/dist/cli/preview/index.js +44 -1
  19. package/dist/cli/telemetry/index.d.ts +2 -1
  20. package/dist/cli/telemetry/index.js +2 -2
  21. package/dist/content/content-layer.js +3 -3
  22. package/dist/content/data-store-writer.d.ts +18 -5
  23. package/dist/content/data-store-writer.js +11 -3
  24. package/dist/content/index.d.ts +1 -1
  25. package/dist/content/index.js +5 -1
  26. package/dist/content/mutable-data-store.d.ts +13 -1
  27. package/dist/content/mutable-data-store.js +35 -5
  28. package/dist/content/runtime.d.ts +8 -3
  29. package/dist/content/runtime.js +43 -32
  30. package/dist/content/types-generator.js +2 -1
  31. package/dist/content/utils.d.ts +5 -35
  32. package/dist/content/utils.js +71 -65
  33. package/dist/content/vite-plugin-content-imports.js +2 -1
  34. package/dist/content/vite-plugin-content-virtual-mod.d.ts +11 -1
  35. package/dist/content/vite-plugin-content-virtual-mod.js +36 -3
  36. package/dist/core/app/entrypoints/index.d.ts +4 -2
  37. package/dist/core/app/entrypoints/index.js +13 -11
  38. package/dist/core/app/entrypoints/virtual/dev.js +2 -2
  39. package/dist/core/app/prerender.d.ts +32 -0
  40. package/dist/core/app/prerender.js +22 -0
  41. package/dist/core/build/default-prerenderer.js +5 -2
  42. package/dist/core/build/generate.d.ts +3 -1
  43. package/dist/core/build/generate.js +40 -62
  44. package/dist/core/build/plugins/plugin-manifest.js +1 -1
  45. package/dist/core/cache/handler.js +12 -1
  46. package/dist/core/cache/memory-provider.js +35 -8
  47. package/dist/core/cache/runtime/cache.js +3 -0
  48. package/dist/core/cache/types.d.ts +2 -0
  49. package/dist/core/config/schemas/base.d.ts +5 -5
  50. package/dist/core/config/schemas/relative.d.ts +7 -7
  51. package/dist/core/constants.js +1 -1
  52. package/dist/core/cookies/cookies.d.ts +2 -1
  53. package/dist/core/cookies/cookies.js +5 -4
  54. package/dist/core/dev/dev.js +5 -3
  55. package/dist/core/fetch/fetch-state.d.ts +1 -1
  56. package/dist/core/fetch/fetch-state.js +4 -23
  57. package/dist/core/logger/core.d.ts +2 -0
  58. package/dist/core/logger/core.js +14 -0
  59. package/dist/core/logger/vite-plugin.d.ts +13 -2
  60. package/dist/core/logger/vite-plugin.js +8 -7
  61. package/dist/core/messages/runtime.js +1 -1
  62. package/dist/core/middleware/index.js +14 -1
  63. package/dist/core/render-scope/collect.d.ts +24 -0
  64. package/dist/core/render-scope/collect.js +27 -0
  65. package/dist/core/render-scope/node-scope.d.ts +10 -0
  66. package/dist/core/render-scope/node-scope.js +8 -0
  67. package/dist/core/render-scope/record.d.ts +14 -0
  68. package/dist/core/render-scope/record.js +12 -0
  69. package/dist/core/render-scope/scope.d.ts +38 -0
  70. package/dist/core/render-scope/scope.js +28 -0
  71. package/dist/core/rewrites/handler.js +1 -1
  72. package/dist/core/routing/create-manifest.js +5 -1
  73. package/dist/core/server-islands/endpoint.js +4 -1
  74. package/dist/core/server-islands/vite-plugin-server-islands.js +2 -2
  75. package/dist/core/sync/index.js +1 -1
  76. package/dist/core/util.d.ts +0 -9
  77. package/dist/core/util.js +0 -34
  78. package/dist/core/viteUtils.d.ts +1 -13
  79. package/dist/core/viteUtils.js +3 -33
  80. package/dist/types/public/integrations.d.ts +20 -13
  81. package/dist/vite-plugin-app/createAstroServerApp.js +1 -1
  82. package/dist/vite-plugin-routes/index.js +22 -8
  83. package/package.json +6 -6
  84. package/templates/content/module.mjs +13 -5
  85. package/dist/core/build/incremental-content-collector.d.ts +0 -10
  86. package/dist/core/build/incremental-content-collector.js +0 -33
  87. package/dist/core/build/incremental-image-collector.d.ts +0 -11
  88. package/dist/core/build/incremental-image-collector.js +0 -32
@@ -89,7 +89,17 @@ function getPathFromCacheKey(key, queryConfig) {
89
89
  const url = new URL(urlPart);
90
90
  return `${url.pathname}${buildQueryString(url, queryConfig)}`;
91
91
  }
92
- const IGNORED_VARY_HEADERS = /* @__PURE__ */ new Set(["cookie", "set-cookie"]);
92
+ const IGNORED_VARY_HEADERS = /* @__PURE__ */ new Set(["set-cookie"]);
93
+ function getUncacheableVaryHeader(response) {
94
+ const vary = response.headers.get("Vary");
95
+ if (!vary) return void 0;
96
+ for (const header of vary.split(",")) {
97
+ const normalized = header.trim().toLowerCase();
98
+ if (normalized === "cookie") return "Cookie";
99
+ if (normalized === "*") return "*";
100
+ }
101
+ return void 0;
102
+ }
93
103
  function parseVaryHeader(response) {
94
104
  const vary = response.headers.get("Vary");
95
105
  if (!vary || vary.trim() === "*") return void 0;
@@ -118,9 +128,14 @@ function hasSetCookieHeader(response) {
118
128
  if (response.headers.has("set-cookie")) return true;
119
129
  return hasAtLeastOneCookie(getCookiesFromResponse(response));
120
130
  }
121
- function warnSkippedSetCookie(url) {
122
- console.warn(
123
- `[astro:cache] Skipping cache for ${url.pathname}${url.search} because response includes Set-Cookie.`
131
+ function warnSkippedSetCookie(logger, url) {
132
+ logger.warn(
133
+ `Skipping cache for ${url.pathname}${url.search} because response includes Set-Cookie.`
134
+ );
135
+ }
136
+ function warnSkippedVary(logger, url, header) {
137
+ logger.warn(
138
+ `Skipping cache for ${url.pathname}${url.search} because response includes Vary: ${header}.`
124
139
  );
125
140
  }
126
141
  class LRUMap {
@@ -232,7 +247,13 @@ const memoryProvider = ((config) => {
232
247
  const { maxAge: newMaxAge, swr: newSwr } = parseCdnCacheControl(cdnCC2);
233
248
  if (newMaxAge > 0) {
234
249
  if (hasSetCookieHeader(freshResponse)) {
235
- warnSkippedSetCookie(requestUrl);
250
+ warnSkippedSetCookie(context.logger, requestUrl);
251
+ return;
252
+ }
253
+ const uncacheableVary = getUncacheableVaryHeader(freshResponse);
254
+ if (uncacheableVary) {
255
+ cache.delete(key);
256
+ warnSkippedVary(context.logger, requestUrl, uncacheableVary);
236
257
  return;
237
258
  }
238
259
  const newTags = parseCacheTags(freshResponse.headers.get("Cache-Tag"));
@@ -249,8 +270,8 @@ const memoryProvider = ((config) => {
249
270
  cache.set(key, newEntry);
250
271
  }
251
272
  }).catch((error) => {
252
- console.warn(
253
- `[astro:cache] Background revalidation failed for ${requestUrl.pathname}${requestUrl.search}: ${String(
273
+ context.logger.warn(
274
+ `Background revalidation failed for ${requestUrl.pathname}${requestUrl.search}: ${String(
254
275
  error
255
276
  )}`
256
277
  );
@@ -266,7 +287,13 @@ const memoryProvider = ((config) => {
266
287
  const { maxAge, swr } = parseCdnCacheControl(cdnCC);
267
288
  if (maxAge > 0) {
268
289
  if (hasSetCookieHeader(response)) {
269
- warnSkippedSetCookie(requestUrl);
290
+ warnSkippedSetCookie(context.logger, requestUrl);
291
+ return response;
292
+ }
293
+ const uncacheableVary = getUncacheableVaryHeader(response);
294
+ if (uncacheableVary) {
295
+ cache.delete(key);
296
+ warnSkippedVary(context.logger, requestUrl, uncacheableVary);
270
297
  return response;
271
298
  }
272
299
  const tags = parseCacheTags(response.headers.get("Cache-Tag"));
@@ -75,6 +75,9 @@ class AstroCache {
75
75
  for (const [key, value] of headers) {
76
76
  response.headers.set(key, value);
77
77
  }
78
+ if (!response.headers.has("Cache-Control") && !response.headers.has("Expires") && (response.headers.has("Last-Modified") || response.headers.has("ETag"))) {
79
+ response.headers.set("Cache-Control", "no-cache");
80
+ }
78
81
  }
79
82
  /** @internal */
80
83
  get [IS_ACTIVE]() {
@@ -1,4 +1,5 @@
1
1
  import type { MiddlewareNext } from '../../types/public/common.js';
2
+ import type { AstroRuntimeLogger } from '../../types/public/context.js';
2
3
  import type { WaitUntilHook } from '../wait-until.js';
3
4
  export interface CacheOptions {
4
5
  maxAge?: number;
@@ -27,6 +28,7 @@ export interface CacheProvider {
27
28
  request: Request;
28
29
  url: URL;
29
30
  waitUntil?: WaitUntilHook;
31
+ logger: AstroRuntimeLogger;
30
32
  }, next: MiddlewareNext): Promise<Response>;
31
33
  invalidate(options: InvalidateOptions): Promise<void>;
32
34
  }
@@ -34,10 +34,10 @@ export declare const AstroConfigSchema: z.ZodObject<{
34
34
  name: z.ZodString;
35
35
  hooks: z.ZodDefault<z.ZodObject<{}, z.core.$loose>>;
36
36
  }, z.core.$strip>>;
37
- integrations: z.ZodDefault<z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodArray<z.ZodObject<{
37
+ integrations: z.ZodDefault<z.ZodOptional<z.ZodPreprocess<z.ZodArray<z.ZodObject<{
38
38
  name: z.ZodString;
39
39
  hooks: z.ZodDefault<z.ZodObject<{}, z.core.$loose>>;
40
- }, z.core.$strip>>>>>;
40
+ }, z.core.$strip>>, unknown>>>;
41
41
  build: z.ZodPrefault<z.ZodObject<{
42
42
  format: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"file">, z.ZodLiteral<"directory">, z.ZodLiteral<"preserve">]>>>;
43
43
  client: z.ZodPipe<z.ZodDefault<z.ZodOptional<z.ZodString>>, z.ZodTransform<URL, string>>;
@@ -55,13 +55,13 @@ export declare const AstroConfigSchema: z.ZodObject<{
55
55
  }>>>;
56
56
  concurrency: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
57
57
  }, z.core.$strip>>;
58
- server: z.ZodPrefault<z.ZodPipe<z.ZodTransform<any, unknown>, z.ZodObject<{
58
+ server: z.ZodPrefault<z.ZodPreprocess<z.ZodObject<{
59
59
  open: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>>;
60
60
  host: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>>;
61
61
  port: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
62
62
  headers: z.ZodOptional<z.ZodCustom<OutgoingHttpHeaders, OutgoingHttpHeaders>>;
63
63
  allowedHosts: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodLiteral<true>]>>>;
64
- }, z.core.$strip>>>;
64
+ }, z.core.$strip>, unknown>>;
65
65
  redirects: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
66
66
  status: z.ZodUnion<readonly [z.ZodLiteral<300>, z.ZodLiteral<301>, z.ZodLiteral<302>, z.ZodLiteral<303>, z.ZodLiteral<304>, z.ZodLiteral<307>, z.ZodLiteral<308>]>;
67
67
  destination: z.ZodString;
@@ -456,8 +456,8 @@ export declare const AstroConfigSchema: z.ZodObject<{
456
456
  fallbacks: z.ZodOptional<z.ZodArray<z.ZodString>>;
457
457
  optimizedFallbacks: z.ZodOptional<z.ZodBoolean>;
458
458
  display: z.ZodOptional<z.ZodEnum<{
459
- auto: "auto";
460
459
  optional: "optional";
460
+ auto: "auto";
461
461
  fallback: "fallback";
462
462
  block: "block";
463
463
  swap: "swap";
@@ -10,10 +10,10 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
10
10
  name: z.ZodString;
11
11
  hooks: z.ZodDefault<z.ZodObject<{}, z.core.$loose>>;
12
12
  }, z.core.$strip>>;
13
- integrations: z.ZodDefault<z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodArray<z.ZodObject<{
13
+ integrations: z.ZodDefault<z.ZodOptional<z.ZodPreprocess<z.ZodArray<z.ZodObject<{
14
14
  name: z.ZodString;
15
15
  hooks: z.ZodDefault<z.ZodObject<{}, z.core.$loose>>;
16
- }, z.core.$strip>>>>>;
16
+ }, z.core.$strip>>, unknown>>>;
17
17
  redirects: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
18
18
  status: z.ZodUnion<readonly [z.ZodLiteral<300>, z.ZodLiteral<301>, z.ZodLiteral<302>, z.ZodLiteral<303>, z.ZodLiteral<304>, z.ZodLiteral<307>, z.ZodLiteral<308>]>;
19
19
  destination: z.ZodString;
@@ -408,8 +408,8 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
408
408
  fallbacks: z.ZodOptional<z.ZodArray<z.ZodString>>;
409
409
  optimizedFallbacks: z.ZodOptional<z.ZodBoolean>;
410
410
  display: z.ZodOptional<z.ZodEnum<{
411
- auto: "auto";
412
411
  optional: "optional";
412
+ auto: "auto";
413
413
  fallback: "fallback";
414
414
  block: "block";
415
415
  swap: "swap";
@@ -472,13 +472,13 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
472
472
  }>>>;
473
473
  concurrency: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
474
474
  }, z.core.$strip>>>;
475
- server: z.ZodPrefault<z.ZodPipe<z.ZodTransform<any, unknown>, z.ZodObject<{
475
+ server: z.ZodPrefault<z.ZodPreprocess<z.ZodObject<{
476
476
  open: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>>;
477
477
  host: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodBoolean]>>>;
478
478
  port: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
479
479
  headers: z.ZodOptional<z.ZodCustom<OutgoingHttpHeaders, OutgoingHttpHeaders>>;
480
480
  allowedHosts: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodLiteral<true>]>>>;
481
- }, z.core.$strip>>>;
481
+ }, z.core.$strip>, unknown>>;
482
482
  }, z.core.$strip>, z.ZodTransform<{
483
483
  base: string;
484
484
  trailingSlash: "never" | "ignore" | "always";
@@ -727,7 +727,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
727
727
  formats?: ["woff2" | "woff" | "otf" | "ttf" | "eot", ...("woff2" | "woff" | "otf" | "ttf" | "eot")[]] | undefined;
728
728
  fallbacks?: string[] | undefined;
729
729
  optimizedFallbacks?: boolean | undefined;
730
- display?: "auto" | "optional" | "fallback" | "block" | "swap" | undefined;
730
+ display?: "optional" | "auto" | "fallback" | "block" | "swap" | undefined;
731
731
  stretch?: string | undefined;
732
732
  featureSettings?: string | undefined;
733
733
  variationSettings?: string | undefined;
@@ -994,7 +994,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
994
994
  formats?: ["woff2" | "woff" | "otf" | "ttf" | "eot", ...("woff2" | "woff" | "otf" | "ttf" | "eot")[]] | undefined;
995
995
  fallbacks?: string[] | undefined;
996
996
  optimizedFallbacks?: boolean | undefined;
997
- display?: "auto" | "optional" | "fallback" | "block" | "swap" | undefined;
997
+ display?: "optional" | "auto" | "fallback" | "block" | "swap" | undefined;
998
998
  stretch?: string | undefined;
999
999
  featureSettings?: string | undefined;
1000
1000
  variationSettings?: string | undefined;
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "7.2.9";
1
+ const ASTRO_VERSION = "7.3.0";
2
2
  const ASTRO_GENERATOR = `Astro v${ASTRO_VERSION}`;
3
3
  const ASTRO_ERROR_HEADER = "X-Astro-Error";
4
4
  const DEFAULT_404_COMPONENT = "astro-default-404.astro";
@@ -1,4 +1,5 @@
1
1
  import type { SerializeOptions } from 'cookie';
2
+ import type { AstroLogger } from '../logger/core.js';
2
3
  export type AstroCookieSetOptions = Pick<SerializeOptions, 'domain' | 'path' | 'expires' | 'maxAge' | 'httpOnly' | 'sameSite' | 'secure' | 'encode' | 'partitioned'>;
3
4
  export interface AstroCookieGetOptions {
4
5
  decode?: (value: string) => string;
@@ -25,7 +26,7 @@ declare class AstroCookie implements AstroCookieInterface {
25
26
  }
26
27
  declare class AstroCookies implements AstroCookiesInterface {
27
28
  #private;
28
- constructor(request: Request);
29
+ constructor(request: Request, logger: Pick<AstroLogger, 'warn'>);
29
30
  /**
30
31
  * Astro.cookies.delete(key) is used to delete a cookie. Using this method will result
31
32
  * in a Set-Cookie header added to the response.
@@ -29,11 +29,13 @@ class AstroCookies {
29
29
  #requestValues;
30
30
  #outgoing;
31
31
  #consumed;
32
- constructor(request) {
32
+ #logger;
33
+ constructor(request, logger) {
33
34
  this.#request = request;
34
35
  this.#requestValues = null;
35
36
  this.#outgoing = null;
36
37
  this.#consumed = false;
38
+ this.#logger = logger;
37
39
  }
38
40
  /**
39
41
  * Astro.cookies.delete(key) is used to delete a cookie. Using this method will result
@@ -112,11 +114,10 @@ class AstroCookies {
112
114
  */
113
115
  set(key, value, options) {
114
116
  if (this.#consumed) {
115
- const warning = new Error(
117
+ this.#logger.warn(
118
+ "SKIP_FORMAT",
116
119
  "Astro.cookies.set() was called after the cookies had already been sent to the browser.\nThis may have happened if this method was called in an imported component.\nPlease make sure that Astro.cookies.set() is only called in the frontmatter of the main page."
117
120
  );
118
- warning.name = "Warning";
119
- console.warn(warning);
120
121
  }
121
122
  let serializedValue;
122
123
  if (typeof value === "string") {
@@ -4,7 +4,7 @@ import colors from "piccolore";
4
4
  import { gt, major, minor, patch } from "semver";
5
5
  import { getDataStoreChunkSize, getDataStoreDir, getDataStoreFile } from "../../content/paths.js";
6
6
  import { globalContentLayer } from "../../content/instance.js";
7
- import { attachContentServerListeners } from "../../content/index.js";
7
+ import { attachContentServerListeners, attachDataStoreInvalidation } from "../../content/index.js";
8
8
  import { MutableDataStore } from "../../content/mutable-data-store.js";
9
9
  import { globalContentConfigObserver } from "../../content/utils.js";
10
10
  import { telemetry } from "../../events/index.js";
@@ -26,7 +26,7 @@ async function dev(inlineConfig) {
26
26
  await telemetry.record([]);
27
27
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
28
28
  const logger = restart.container.logger;
29
- const currentVersion = "7.2.9";
29
+ const currentVersion = "7.3.0";
30
30
  const isPrerelease = currentVersion.includes("-");
31
31
  if (!isPrerelease) {
32
32
  try {
@@ -59,7 +59,7 @@ async function dev(inlineConfig) {
59
59
  const chunkSize = getDataStoreChunkSize(restart.container.settings);
60
60
  if (chunkSize !== void 0) {
61
61
  const dataStoreDir = getDataStoreDir(restart.container.settings, true);
62
- store = await MutableDataStore.fromDir(dataStoreDir, chunkSize);
62
+ store = await MutableDataStore.fromDir(dataStoreDir, chunkSize, logger);
63
63
  } else {
64
64
  const dataStoreFile = getDataStoreFile(restart.container.settings, true);
65
65
  store = await MutableDataStore.fromFile(dataStoreFile);
@@ -69,6 +69,8 @@ async function dev(inlineConfig) {
69
69
  }
70
70
  if (!store) {
71
71
  logger.error("content", "Failed to create data store");
72
+ } else {
73
+ attachDataStoreInvalidation(store, restart.container.viteServer, restart.container.settings);
72
74
  }
73
75
  await attachContentServerListeners(restart.container);
74
76
  const config = globalContentConfigObserver.get();
@@ -7,7 +7,7 @@ import { AstroCookies } from '../cookies/index.js';
7
7
  import type { RenderErrorOptions, ResolvedRenderOptions } from '../app/base.js';
8
8
  import type { SSRManifest } from '../app/types.js';
9
9
  import { type RequestLogPayload } from '../environment/index.js';
10
- import type { AstroLogger } from '../logger/core.js';
10
+ import { type AstroLogger } from '../logger/core.js';
11
11
  /**
12
12
  * Per-render facade inputs passed by `BaseApp.render`'s fast path to the
13
13
  * internal `FetchState` constructor and stored as plain state fields.
@@ -35,6 +35,7 @@ import { getRenderOptions } from "../app/render-options.js";
35
35
  import { getFirstForwardedValue, validateForwardedHeaders } from "../app/validate-headers.js";
36
36
  import { getEnvironment } from "../environment/index.js";
37
37
  import { getLogger } from "../logger/manifest-logger.js";
38
+ import { astroToRuntimeLogger } from "../logger/core.js";
38
39
  import { getSite } from "../manifest/derived.js";
39
40
  import { getRouteCache } from "../render/route-cache.js";
40
41
  import { getRouteTable, matchAllRoutes, matchRoute } from "../routing/route-table.js";
@@ -232,7 +233,7 @@ class FetchState {
232
233
  this.clientAddress = options?.clientAddress;
233
234
  this.locals = options?.locals ?? {};
234
235
  this.url = url;
235
- this.cookies = new AstroCookies(request);
236
+ this.cookies = new AstroCookies(request, this.logger);
236
237
  if (manifest.allowedDomains && manifest.allowedDomains.length > 0 && !this.routeData?.prerender) {
237
238
  this.#applyForwardedHeaders();
238
239
  }
@@ -441,17 +442,7 @@ class FetchState {
441
442
  return state.getCsp();
442
443
  },
443
444
  get logger() {
444
- return {
445
- info(msg) {
446
- logger.info(null, msg);
447
- },
448
- warn(msg) {
449
- logger.warn(null, msg);
450
- },
451
- error(msg) {
452
- logger.error(null, msg);
453
- }
454
- };
445
+ return astroToRuntimeLogger(logger);
455
446
  }
456
447
  };
457
448
  this.defineProviderGetters(partial);
@@ -906,17 +897,7 @@ class FetchState {
906
897
  return state.getCsp();
907
898
  },
908
899
  get logger() {
909
- return {
910
- info(msg) {
911
- state.logger.info(null, msg);
912
- },
913
- warn(msg) {
914
- state.logger.warn(null, msg);
915
- },
916
- error(msg) {
917
- state.logger.error(null, msg);
918
- }
919
- };
900
+ return astroToRuntimeLogger(state.logger);
920
901
  }
921
902
  };
922
903
  this.defineProviderGetters(ctx);
@@ -1,3 +1,4 @@
1
+ import type { AstroRuntimeLogger } from '../../types/public/context.js';
1
2
  export interface AstroLoggerDestination {
2
3
  /**
3
4
  * It receives a message and writes it into a destination
@@ -109,4 +110,5 @@ export declare class AstroIntegrationLogger {
109
110
  */
110
111
  close(): void;
111
112
  }
113
+ export declare function astroToRuntimeLogger(logger: AstroLogger): AstroRuntimeLogger;
112
114
  export {};
@@ -186,9 +186,23 @@ class AstroIntegrationLogger {
186
186
  }
187
187
  }
188
188
  }
189
+ function astroToRuntimeLogger(logger) {
190
+ return {
191
+ info(msg) {
192
+ logger.info(null, msg);
193
+ },
194
+ warn(msg) {
195
+ logger.warn(null, msg);
196
+ },
197
+ error(msg) {
198
+ logger.error(null, msg);
199
+ }
200
+ };
201
+ }
189
202
  export {
190
203
  AstroIntegrationLogger,
191
204
  AstroLogger,
205
+ astroToRuntimeLogger,
192
206
  dateTimeFormat,
193
207
  debug,
194
208
  getEventPrefix,
@@ -24,7 +24,18 @@ export declare function emitDestination(config: NormalizedLoggerConfig, resolveE
24
24
  * destinations continue numbering where their parent left off.
25
25
  */
26
26
  nameOffset?: number): Promise<EmittedDestination>;
27
- export declare function vitePluginLogger({ settings, }: {
27
+ /**
28
+ * Emits `virtual:astro:logger`: the configured destination as the default export
29
+ * (`null` when the user configured none) plus the resolved log level.
30
+ *
31
+ * The module is always registered, even without a `logger` config, so that code
32
+ * generated ahead of the user's config — `astro:content`, which is built from a
33
+ * static template — can import it unconditionally. It deliberately imports
34
+ * nothing but the user's destination entrypoint: unlike `virtual:astro:manifest`
35
+ * it is a leaf, so reaching a logger through it cannot pull renderers and pages
36
+ * into an importer's module graph.
37
+ */
38
+ export declare function vitePluginLogger({ settings }: {
28
39
  settings: AstroSettings;
29
- }): VitePlugin | undefined;
40
+ }): VitePlugin;
30
41
  export {};
@@ -35,13 +35,8 @@ async function emitDestination(config, resolveEntrypoint, nameOffset = 0) {
35
35
  }
36
36
  return { expression: `${name}(${JSON.stringify(config.config) ?? "undefined"})`, imports };
37
37
  }
38
- function vitePluginLogger({
39
- settings
40
- }) {
38
+ function vitePluginLogger({ settings }) {
41
39
  const loggerConfig = settings.config.logger;
42
- if (!loggerConfig) {
43
- return;
44
- }
45
40
  return {
46
41
  name: VIRTUAL_LOGGER_ID,
47
42
  enforce: "pre",
@@ -58,6 +53,12 @@ function vitePluginLogger({
58
53
  id: new RegExp(`^${RESOLVED_VIRTUAL_LOGGER_ID}$`)
59
54
  },
60
55
  async handler() {
56
+ const level = `export const level = ${JSON.stringify(settings.logLevel)};
57
+ `;
58
+ if (!loggerConfig) {
59
+ return { code: `${level}export default null;
60
+ ` };
61
+ }
61
62
  const importerPath = fileURLToPath(new URL("package.json", settings.config.root));
62
63
  const { expression, imports } = await emitDestination(
63
64
  normalizeLoggerConfig(loggerConfig, settings.config.root),
@@ -65,7 +66,7 @@ function vitePluginLogger({
65
66
  );
66
67
  return {
67
68
  code: `${imports.join("\n")}
68
- export default ${expression};
69
+ ${level}export default ${expression};
69
70
  `
70
71
  };
71
72
  }
@@ -270,7 +270,7 @@ function printHelp({
270
270
  message.push(
271
271
  linebreak(),
272
272
  ` ${bgGreen(black(` ${commandName} `))} ${green(
273
- `v${"7.2.9"}`
273
+ `v${"7.3.0"}`
274
274
  )} ${headline}`
275
275
  );
276
276
  }
@@ -8,8 +8,21 @@ import { DisabledAstroCache } from "../cache/runtime/noop.js";
8
8
  import { ASTRO_GENERATOR } from "../constants.js";
9
9
  import { AstroCookies } from "../cookies/index.js";
10
10
  import { AstroError, AstroErrorData } from "../errors/index.js";
11
+ import { getLogger } from "../logger/manifest-logger.js";
12
+ import { tryGetAmbientManifest } from "../manifest/ambient.js";
11
13
  import { getOriginPathname } from "../routing/rewrite.js";
12
14
  import { sequence } from "./sequence.js";
15
+ function getMiddlewareLogger() {
16
+ const manifest = tryGetAmbientManifest();
17
+ if (manifest) {
18
+ return getLogger(manifest);
19
+ }
20
+ return {
21
+ warn(_label, message) {
22
+ console.warn(message);
23
+ }
24
+ };
25
+ }
13
26
  function createContext({
14
27
  request,
15
28
  params = {},
@@ -27,7 +40,7 @@ function createContext({
27
40
  return Promise.resolve(new Response(null));
28
41
  };
29
42
  const context = {
30
- cookies: new AstroCookies(request),
43
+ cookies: new AstroCookies(request, getMiddlewareLogger()),
31
44
  request,
32
45
  params,
33
46
  site: void 0,
@@ -0,0 +1,24 @@
1
+ import type { SerializedStaticImage } from '../../assets/types.js';
2
+ import type { AstroLogger } from '../logger/core.js';
3
+ export interface CollectedPrerenderMetadata {
4
+ contentEntryKeys: string[];
5
+ staticImages: SerializedStaticImage[];
6
+ }
7
+ /**
8
+ * Runs `fn` inside a fresh per-render collectors store and returns its value
9
+ * together with a snapshot of everything recorded while it ran.
10
+ *
11
+ * When no render scope is installed, collection degrades to *not collecting*:
12
+ * warn once per process, run `fn` bare, and return `metadata: undefined`
13
+ * ("not tracked") — never wrong attribution.
14
+ *
15
+ * Invariant: `fn` must not resolve until all recordable work is done — in
16
+ * practice, until the response body is fully buffered inside `fn`. The
17
+ * snapshot is taken by copy when `fn` resolves, so a late-arriving record (a
18
+ * floating promise carrying the async context past buffering) mutates only the
19
+ * abandoned store, never the returned metadata.
20
+ */
21
+ export declare function collectPrerenderMetadata<T>(fn: () => Promise<T>, logger: AstroLogger): Promise<{
22
+ value: T;
23
+ metadata: CollectedPrerenderMetadata | undefined;
24
+ }>;
@@ -0,0 +1,27 @@
1
+ import { getInstalledRenderScope } from "./scope.js";
2
+ let warnedNoScope = false;
3
+ async function collectPrerenderMetadata(fn, logger) {
4
+ const scope = getInstalledRenderScope();
5
+ if (!scope) {
6
+ if (!warnedNoScope) {
7
+ warnedNoScope = true;
8
+ logger.warn(
9
+ "build",
10
+ "A prerenderer requested metadata collection but no render scope is installed; install one with `installRenderScope` from `astro/app` \u2014 incremental metadata will not be collected for prerendered paths."
11
+ );
12
+ }
13
+ return { value: await fn(), metadata: void 0 };
14
+ }
15
+ const store = { contentEntries: /* @__PURE__ */ new Set(), staticImages: [] };
16
+ const value = await scope.run(store, fn);
17
+ return {
18
+ value,
19
+ metadata: {
20
+ contentEntryKeys: [...store.contentEntries],
21
+ staticImages: [...store.staticImages]
22
+ }
23
+ };
24
+ }
25
+ export {
26
+ collectPrerenderMetadata
27
+ };
@@ -0,0 +1,10 @@
1
+ import type { RenderCollectorScope } from './scope.js';
2
+ /**
3
+ * Installs (first-wins) an AsyncLocalStorage-backed render scope and returns
4
+ * the installed scope.
5
+ *
6
+ * This module is the ONE static `node:async_hooks` import in core and must be
7
+ * imported ONLY by `core/build/default-prerenderer.ts` — orchestrator-only
8
+ * code that is never bundled into prerender, server, or adapter output.
9
+ */
10
+ export declare function ensureAsyncRenderScope(): RenderCollectorScope;
@@ -0,0 +1,8 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { installRenderScope } from "./scope.js";
3
+ function ensureAsyncRenderScope() {
4
+ return installRenderScope(new AsyncLocalStorage());
5
+ }
6
+ export {
7
+ ensureAsyncRenderScope
8
+ };
@@ -0,0 +1,14 @@
1
+ import type { SerializedStaticImage } from '../../assets/types.js';
2
+ /**
3
+ * Records that a content entry was rendered, keyed by its root-relative
4
+ * `filePath`. No-op when no scope is installed (dev, production SSR,
5
+ * non-incremental builds) or no render is in scope (`getStaticPaths`, module
6
+ * top-level).
7
+ */
8
+ export declare function recordContentEntryRender(filePath: string | undefined): void;
9
+ /**
10
+ * Records a resolved image transform, dedup hits included, preserving
11
+ * duplicates (array push, not a set — replay depends on every record
12
+ * arriving).
13
+ */
14
+ export declare function recordStaticImage(image: SerializedStaticImage): void;
@@ -0,0 +1,12 @@
1
+ import { getInstalledRenderScope } from "./scope.js";
2
+ function recordContentEntryRender(filePath) {
3
+ if (!filePath) return;
4
+ getInstalledRenderScope()?.getStore()?.contentEntries?.add(filePath);
5
+ }
6
+ function recordStaticImage(image) {
7
+ getInstalledRenderScope()?.getStore()?.staticImages?.push(image);
8
+ }
9
+ export {
10
+ recordContentEntryRender,
11
+ recordStaticImage
12
+ };
@@ -0,0 +1,38 @@
1
+ import type { SerializedStaticImage } from '../../assets/types.js';
2
+ /**
3
+ * The per-render store. One instance is created per collecting render and is
4
+ * reachable only through the installed {@link RenderCollectorScope} while that
5
+ * render's async execution is in scope. Fields are optional so record helpers
6
+ * tolerate stores created by a different astro module instance (or version)
7
+ * that only knows a subset of collectors.
8
+ */
9
+ export interface RenderCollectors {
10
+ /** Root-relative `filePath`s of the content entries rendered. */
11
+ contentEntries?: Set<string>;
12
+ /**
13
+ * Every image transform resolved, dedup hits included; array push,
14
+ * duplicates preserved.
15
+ */
16
+ staticImages?: SerializedStaticImage[];
17
+ }
18
+ /**
19
+ * Structurally satisfied by `AsyncLocalStorage<RenderCollectors>` — deliberate:
20
+ * installing an ALS instance directly IS the only shipped implementation.
21
+ */
22
+ export interface RenderCollectorScope {
23
+ run<T>(store: RenderCollectors, fn: () => T): T;
24
+ getStore(): RenderCollectors | undefined;
25
+ }
26
+ /**
27
+ * Installs a render scope on the process-wide channel and returns the installed
28
+ * scope. First-wins: when a scope is already installed (possibly by another
29
+ * module instance), the existing scope is returned and the argument discarded,
30
+ * so callers that both awaited an import converge on one scope.
31
+ */
32
+ export declare function installRenderScope(scope: RenderCollectorScope): RenderCollectorScope;
33
+ /** The installed render scope, or `undefined` when none was installed. */
34
+ export declare function getInstalledRenderScope(): RenderCollectorScope | undefined;
35
+ /** Test-only: remove the installed scope so unit tests can reset the channel. */
36
+ export declare function uninstallRenderScope(): void;
37
+ /** The current render's collectors store, or `undefined` when not collecting. */
38
+ export declare function getRenderCollectors(): RenderCollectors | undefined;