astro 5.9.2 → 5.9.4

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 (33) hide show
  1. package/dist/container/pipeline.js +2 -1
  2. package/dist/content/content-layer.js +3 -3
  3. package/dist/core/app/pipeline.js +2 -1
  4. package/dist/core/app/types.d.ts +1 -0
  5. package/dist/core/base-pipeline.d.ts +1 -2
  6. package/dist/core/build/generate.js +31 -13
  7. package/dist/core/build/pipeline.js +2 -1
  8. package/dist/core/build/plugins/plugin-manifest.js +1 -0
  9. package/dist/core/config/schemas/base.d.ts +5 -5
  10. package/dist/core/config/schemas/base.js +1 -1
  11. package/dist/core/config/schemas/relative.d.ts +7 -7
  12. package/dist/core/constants.js +1 -1
  13. package/dist/core/csp/config.d.ts +1 -1
  14. package/dist/core/dev/dev.js +1 -1
  15. package/dist/core/messages.js +2 -2
  16. package/dist/core/render-context.js +1 -0
  17. package/dist/core/routing/manifest/create.js +1 -1
  18. package/dist/core/routing/rewrite.d.ts +2 -1
  19. package/dist/core/routing/rewrite.js +14 -2
  20. package/dist/core/session.d.ts +1 -1
  21. package/dist/core/session.js +10 -9
  22. package/dist/integrations/hooks.d.ts +4 -2
  23. package/dist/integrations/hooks.js +5 -3
  24. package/dist/runtime/server/render/csp.js +1 -1
  25. package/dist/runtime/server/render/head.js +15 -14
  26. package/dist/runtime/server/render/page.js +12 -4
  27. package/dist/runtime/server/render/server-islands.js +1 -1
  28. package/dist/types/public/config.d.ts +1 -1
  29. package/dist/types/public/integrations.d.ts +12 -1
  30. package/dist/types/public/internal.d.ts +12 -2
  31. package/dist/vite-plugin-astro-server/pipeline.js +2 -1
  32. package/dist/vite-plugin-astro-server/plugin.js +2 -1
  33. package/package.json +3 -3
@@ -56,7 +56,8 @@ class ContainerPipeline extends Pipeline {
56
56
  routes: this.manifest?.routes.map((r) => r.routeData),
57
57
  trailingSlash: this.manifest.trailingSlash,
58
58
  buildFormat: this.manifest.buildFormat,
59
- base: this.manifest.base
59
+ base: this.manifest.base,
60
+ outDir: this.manifest.outDir
60
61
  });
61
62
  const componentInstance = await this.getComponentByRoute(routeData);
62
63
  return { componentInstance, routeData, newUrl, pathname };
@@ -164,7 +164,7 @@ ${contentConfig.error.message}`);
164
164
  logger.info("Content config changed");
165
165
  shouldClear = true;
166
166
  }
167
- if (previousAstroVersion && previousAstroVersion !== "5.9.2") {
167
+ if (previousAstroVersion && previousAstroVersion !== "5.9.4") {
168
168
  logger.info("Astro version changed");
169
169
  shouldClear = true;
170
170
  }
@@ -172,8 +172,8 @@ ${contentConfig.error.message}`);
172
172
  logger.info("Clearing content store");
173
173
  this.#store.clearAll();
174
174
  }
175
- if ("5.9.2") {
176
- await this.#store.metaStore().set("astro-version", "5.9.2");
175
+ if ("5.9.4") {
176
+ await this.#store.metaStore().set("astro-version", "5.9.4");
177
177
  }
178
178
  if (currentConfigDigest) {
179
179
  await this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -65,7 +65,8 @@ class AppPipeline extends Pipeline {
65
65
  routes: this.manifest?.routes.map((r) => r.routeData),
66
66
  trailingSlash: this.manifest.trailingSlash,
67
67
  buildFormat: this.manifest.buildFormat,
68
- base: this.manifest.base
68
+ base: this.manifest.base,
69
+ outDir: this.manifest.outDir
69
70
  });
70
71
  const componentInstance = await this.getComponentByRoute(routeData);
71
72
  return { newUrl, pathname, componentInstance, routeData };
@@ -91,6 +91,7 @@ export type SSRManifestI18n = {
91
91
  domainLookupTable: Record<string, string>;
92
92
  };
93
93
  export type SSRManifestCSP = {
94
+ cspDestination: 'adapter' | 'meta' | 'header' | undefined;
94
95
  algorithm: CspAlgorithm;
95
96
  scriptHashes: string[];
96
97
  scriptResources: string[];
@@ -3,8 +3,7 @@ import type { ActionAccept, ActionClient } from '../actions/runtime/virtual/serv
3
3
  import type { ComponentInstance } from '../types/astro.js';
4
4
  import type { MiddlewareHandler, RewritePayload } from '../types/public/common.js';
5
5
  import type { RuntimeMode } from '../types/public/config.js';
6
- import type { RouteData, SSRLoadedRenderer, SSRManifest, SSRResult } from '../types/public/internal.js';
7
- import type { SSRActions } from './app/types.js';
6
+ import type { RouteData, SSRActions, SSRLoadedRenderer, SSRManifest, SSRResult } from '../types/public/internal.js';
8
7
  import type { Logger } from './logger/core.js';
9
8
  import { RouteCache } from './render/route-cache.js';
10
9
  /**
@@ -16,7 +16,7 @@ import {
16
16
  removeTrailingForwardSlash
17
17
  } from "../../core/path.js";
18
18
  import { toFallbackType, toRoutingStrategy } from "../../i18n/utils.js";
19
- import { runHookBuildGenerated } from "../../integrations/hooks.js";
19
+ import { runHookBuildGenerated, toIntegrationResolvedRoute } from "../../integrations/hooks.js";
20
20
  import { getServerOutputDirectory } from "../../prerender/utils.js";
21
21
  import {
22
22
  getAlgorithm,
@@ -77,6 +77,7 @@ async function generatePages(options, internals) {
77
77
  ${bgGreen(black(` ${verb} static routes `))}`);
78
78
  const builtPaths = /* @__PURE__ */ new Set();
79
79
  const pagesToGenerate = pipeline.retrieveRoutesToGenerate();
80
+ const routeToHeaders = /* @__PURE__ */ new Map();
80
81
  if (ssr) {
81
82
  for (const [pageData, filePath] of pagesToGenerate) {
82
83
  if (pageData.route.prerender) {
@@ -88,13 +89,13 @@ ${bgGreen(black(` ${verb} static routes `))}`);
88
89
  }
89
90
  const ssrEntryPage = await pipeline.retrieveSsrEntry(pageData.route, filePath);
90
91
  const ssrEntry = ssrEntryPage;
91
- await generatePage(pageData, ssrEntry, builtPaths, pipeline);
92
+ await generatePage(pageData, ssrEntry, builtPaths, pipeline, routeToHeaders);
92
93
  }
93
94
  }
94
95
  } else {
95
96
  for (const [pageData, filePath] of pagesToGenerate) {
96
97
  const entry = await pipeline.retrieveSsrEntry(pageData.route, filePath);
97
- await generatePage(pageData, entry, builtPaths, pipeline);
98
+ await generatePage(pageData, entry, builtPaths, pipeline, routeToHeaders);
98
99
  }
99
100
  }
100
101
  logger.info(
@@ -121,10 +122,14 @@ ${bgGreen(black(` ${verb} static routes `))}`);
121
122
  `));
122
123
  delete globalThis?.astroAsset?.addStaticImage;
123
124
  }
124
- await runHookBuildGenerated({ settings: options.settings, logger });
125
+ await runHookBuildGenerated({
126
+ settings: options.settings,
127
+ logger,
128
+ experimentalRouteToHeaders: routeToHeaders
129
+ });
125
130
  }
126
131
  const THRESHOLD_SLOW_RENDER_TIME_MS = 500;
127
- async function generatePage(pageData, ssrEntry, builtPaths, pipeline) {
132
+ async function generatePage(pageData, ssrEntry, builtPaths, pipeline, routeToHeaders) {
128
133
  const { config, logger } = pipeline;
129
134
  const pageModulePromise = ssrEntry.page;
130
135
  const styles = pageData.styles.sort(cssOrder).map(({ sheet }) => sheet).reduce(mergeInlineCss, []);
@@ -150,7 +155,7 @@ async function generatePage(pageData, ssrEntry, builtPaths, pipeline) {
150
155
  if (!isConcurrent) {
151
156
  logger.info(null, ` ${blue(lineIcon)} ${dim(filePath)}`, false);
152
157
  }
153
- const created = await generatePath(path, pipeline, generationOptions, route);
158
+ const created = await generatePath(path, pipeline, generationOptions, route, routeToHeaders);
154
159
  const timeEnd = performance.now();
155
160
  const isSlow = timeEnd - timeStart > THRESHOLD_SLOW_RENDER_TIME_MS;
156
161
  const timeIncrease = (isSlow ? red : dim)(`(+${getTimeStat(timeStart, timeEnd)})`);
@@ -287,18 +292,27 @@ function getUrlForPath(pathname, base, origin, format, trailingSlash, routeType)
287
292
  }
288
293
  return new URL(buildPathname, origin);
289
294
  }
290
- async function generatePath(pathname, pipeline, gopts, route) {
295
+ async function generatePath(pathname, pipeline, gopts, route, routeToHeaders) {
291
296
  const { mod } = gopts;
292
297
  const { config, logger, options } = pipeline;
293
298
  logger.debug("build", `Generating: ${pathname}`);
294
299
  if (route.type === "page") {
295
300
  addPageName(pathname, options);
296
301
  }
297
- if (route.type === "fallback" && // If route is index page, continue rendering. The index page should
298
- // always be rendered
299
- route.pathname !== "/" && // Check if there is a translated page with the same path
300
- Object.values(options.allPages).some((val) => val.route.pattern.test(pathname))) {
301
- return void 0;
302
+ if (route.type === "fallback" && route.pathname !== "/") {
303
+ let locale = removeLeadingForwardSlash(pathname).split("/")[0];
304
+ if (Object.values(options.allPages).some((val) => {
305
+ if (val.route.pattern.test(pathname)) {
306
+ if (val.route.segments && val.route.segments.length !== 0) {
307
+ if (val.route.segments[0][0].content !== locale) return false;
308
+ }
309
+ return true;
310
+ } else {
311
+ return false;
312
+ }
313
+ })) {
314
+ return void 0;
315
+ }
302
316
  }
303
317
  const url = getUrlForPath(
304
318
  pathname,
@@ -332,6 +346,9 @@ async function generatePath(pathname, pipeline, gopts, route) {
332
346
  }
333
347
  throw err;
334
348
  }
349
+ if (pipeline.settings.adapter?.adapterFeatures?.experimentalStaticHeaders && pipeline.settings.config.experimental?.csp) {
350
+ routeToHeaders.set(toIntegrationResolvedRoute(route), response.headers);
351
+ }
335
352
  if (response.status >= 300 && response.status < 400) {
336
353
  if (routeIsRedirect(route) && !config.build.redirects) {
337
354
  return void 0;
@@ -401,6 +418,7 @@ async function createBuildManifest(settings, internals, renderers, middleware, a
401
418
  ...await trackStyleHashes(internals, settings, algorithm)
402
419
  ];
403
420
  csp = {
421
+ cspDestination: settings.adapter?.adapterFeatures?.experimentalStaticHeaders ? "adapter" : void 0,
404
422
  styleHashes,
405
423
  styleResources: getStyleResources(settings.config.experimental.csp),
406
424
  scriptHashes,
@@ -423,7 +441,7 @@ async function createBuildManifest(settings, internals, renderers, middleware, a
423
441
  entryModules: Object.fromEntries(internals.entrySpecifierToBundleMap.entries()),
424
442
  inlinedScripts: internals.inlinedScripts,
425
443
  routes: [],
426
- adapterName: "",
444
+ adapterName: settings.adapter?.name ?? "",
427
445
  clientDirectives: settings.clientDirectives,
428
446
  compressHTML: settings.config.compressHTML,
429
447
  renderers,
@@ -201,7 +201,8 @@ class BuildPipeline extends Pipeline {
201
201
  routes: this.options.routesList.routes,
202
202
  trailingSlash: this.config.trailingSlash,
203
203
  buildFormat: this.config.build.format,
204
- base: this.config.base
204
+ base: this.config.base,
205
+ outDir: this.manifest.outDir
205
206
  });
206
207
  const componentInstance = await this.getComponentByRoute(routeData);
207
208
  return { routeData, componentInstance, newUrl, pathname };
@@ -245,6 +245,7 @@ async function buildManifest(opts, internals, staticFiles, encodedKey) {
245
245
  ...await trackStyleHashes(internals, settings, algorithm)
246
246
  ];
247
247
  csp = {
248
+ cspDestination: settings.adapter?.adapterFeatures?.experimentalStaticHeaders ? "adapter" : void 0,
248
249
  scriptHashes,
249
250
  scriptResources: getScriptResources(settings.config.experimental.csp),
250
251
  styleHashes,
@@ -659,7 +659,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
659
659
  })> | undefined;
660
660
  }>>>;
661
661
  session: z.ZodOptional<z.ZodObject<{
662
- driver: z.ZodString;
662
+ driver: z.ZodOptional<z.ZodString>;
663
663
  options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
664
664
  cookie: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodObject<{
665
665
  name: z.ZodOptional<z.ZodString>;
@@ -699,8 +699,8 @@ export declare const AstroConfigSchema: z.ZodObject<{
699
699
  }>>;
700
700
  ttl: z.ZodOptional<z.ZodNumber>;
701
701
  }, "strip", z.ZodTypeAny, {
702
- driver: string;
703
702
  options?: Record<string, any> | undefined;
703
+ driver?: string | undefined;
704
704
  cookie?: {
705
705
  name?: string | undefined;
706
706
  path?: string | undefined;
@@ -711,8 +711,8 @@ export declare const AstroConfigSchema: z.ZodObject<{
711
711
  } | undefined;
712
712
  ttl?: number | undefined;
713
713
  }, {
714
- driver: string;
715
714
  options?: Record<string, any> | undefined;
715
+ driver?: string | undefined;
716
716
  cookie?: string | {
717
717
  name?: string | undefined;
718
718
  path?: string | undefined;
@@ -1342,8 +1342,8 @@ export declare const AstroConfigSchema: z.ZodObject<{
1342
1342
  collections: boolean;
1343
1343
  };
1344
1344
  session?: {
1345
- driver: string;
1346
1345
  options?: Record<string, any> | undefined;
1346
+ driver?: string | undefined;
1347
1347
  cookie?: {
1348
1348
  name?: string | undefined;
1349
1349
  path?: string | undefined;
@@ -1421,8 +1421,8 @@ export declare const AstroConfigSchema: z.ZodObject<{
1421
1421
  destination: string;
1422
1422
  }> | undefined;
1423
1423
  session?: {
1424
- driver: string;
1425
1424
  options?: Record<string, any> | undefined;
1425
+ driver?: string | undefined;
1426
1426
  cookie?: string | {
1427
1427
  name?: string | undefined;
1428
1428
  path?: string | undefined;
@@ -244,7 +244,7 @@ const AstroConfigSchema = z.object({
244
244
  validateSecrets: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.env.validateSecrets)
245
245
  }).strict().optional().default(ASTRO_CONFIG_DEFAULTS.env),
246
246
  session: z.object({
247
- driver: z.string(),
247
+ driver: z.string().optional(),
248
248
  options: z.record(z.any()).optional(),
249
249
  cookie: z.object({
250
250
  name: z.string().optional(),
@@ -582,7 +582,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
582
582
  })> | undefined;
583
583
  }>>>;
584
584
  session: z.ZodOptional<z.ZodObject<{
585
- driver: z.ZodString;
585
+ driver: z.ZodOptional<z.ZodString>;
586
586
  options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
587
587
  cookie: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodObject<{
588
588
  name: z.ZodOptional<z.ZodString>;
@@ -622,8 +622,8 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
622
622
  }>>;
623
623
  ttl: z.ZodOptional<z.ZodNumber>;
624
624
  }, "strip", z.ZodTypeAny, {
625
- driver: string;
626
625
  options?: Record<string, any> | undefined;
626
+ driver?: string | undefined;
627
627
  cookie?: {
628
628
  name?: string | undefined;
629
629
  path?: string | undefined;
@@ -634,8 +634,8 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
634
634
  } | undefined;
635
635
  ttl?: number | undefined;
636
636
  }, {
637
- driver: string;
638
637
  options?: Record<string, any> | undefined;
638
+ driver?: string | undefined;
639
639
  cookie?: string | {
640
640
  name?: string | undefined;
641
641
  path?: string | undefined;
@@ -1343,8 +1343,8 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1343
1343
  collections: boolean;
1344
1344
  };
1345
1345
  session?: {
1346
- driver: string;
1347
1346
  options?: Record<string, any> | undefined;
1347
+ driver?: string | undefined;
1348
1348
  cookie?: {
1349
1349
  name?: string | undefined;
1350
1350
  path?: string | undefined;
@@ -1422,8 +1422,8 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1422
1422
  destination: string;
1423
1423
  }> | undefined;
1424
1424
  session?: {
1425
- driver: string;
1426
1425
  options?: Record<string, any> | undefined;
1426
+ driver?: string | undefined;
1427
1427
  cookie?: string | {
1428
1428
  name?: string | undefined;
1429
1429
  path?: string | undefined;
@@ -1827,8 +1827,8 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1827
1827
  collections: boolean;
1828
1828
  };
1829
1829
  session?: {
1830
- driver: string;
1831
1830
  options?: Record<string, any> | undefined;
1831
+ driver?: string | undefined;
1832
1832
  cookie?: {
1833
1833
  name?: string | undefined;
1834
1834
  path?: string | undefined;
@@ -1906,8 +1906,8 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
1906
1906
  destination: string;
1907
1907
  }> | undefined;
1908
1908
  session?: {
1909
- driver: string;
1910
1909
  options?: Record<string, any> | undefined;
1910
+ driver?: string | undefined;
1911
1911
  cookie?: string | {
1912
1912
  name?: string | undefined;
1913
1913
  path?: string | undefined;
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "5.9.2";
1
+ const ASTRO_VERSION = "5.9.4";
2
2
  const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
3
3
  const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite";
4
4
  const REWRITE_DIRECTIVE_HEADER_VALUE = "yes";
@@ -10,7 +10,7 @@ export declare const cspAlgorithmSchema: z.ZodDefault<z.ZodOptional<z.ZodEnum<["
10
10
  export declare const cspHashSchema: z.ZodType<`sha256-${string}` | `sha384-${string}` | `sha512-${string}`, z.ZodTypeDef, `sha256-${string}` | `sha384-${string}` | `sha512-${string}`>;
11
11
  export type CspHash = z.infer<typeof cspHashSchema>;
12
12
  declare const ALLOWED_DIRECTIVES: readonly ["base-uri", "child-src", "connect-src", "default-src", "fenced-frame-src", "font-src", "form-action", "frame-ancestors", "frame-src", "img-src", "manifest-src", "media-src", "object-src", "referrer", "report-to", "require-trusted-types-for", "sandbox", "trusted-types", "upgrade-insecure-requests", "worker-src"];
13
+ type AllowedDirectives = (typeof ALLOWED_DIRECTIVES)[number];
13
14
  export type CspDirective = `${AllowedDirectives} ${string}`;
14
15
  export declare const allowedDirectivesSchema: z.ZodType<`base-uri ${string}` | `child-src ${string}` | `connect-src ${string}` | `default-src ${string}` | `fenced-frame-src ${string}` | `font-src ${string}` | `form-action ${string}` | `frame-ancestors ${string}` | `frame-src ${string}` | `img-src ${string}` | `manifest-src ${string}` | `media-src ${string}` | `object-src ${string}` | `referrer ${string}` | `report-to ${string}` | `require-trusted-types-for ${string}` | `sandbox ${string}` | `trusted-types ${string}` | `upgrade-insecure-requests ${string}` | `worker-src ${string}`, z.ZodTypeDef, `base-uri ${string}` | `child-src ${string}` | `connect-src ${string}` | `default-src ${string}` | `fenced-frame-src ${string}` | `font-src ${string}` | `form-action ${string}` | `frame-ancestors ${string}` | `frame-src ${string}` | `img-src ${string}` | `manifest-src ${string}` | `media-src ${string}` | `object-src ${string}` | `referrer ${string}` | `report-to ${string}` | `require-trusted-types-for ${string}` | `sandbox ${string}` | `trusted-types ${string}` | `upgrade-insecure-requests ${string}` | `worker-src ${string}`>;
15
- type AllowedDirectives = (typeof ALLOWED_DIRECTIVES)[number];
16
16
  export {};
@@ -22,7 +22,7 @@ async function dev(inlineConfig) {
22
22
  await telemetry.record([]);
23
23
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
24
24
  const logger = restart.container.logger;
25
- const currentVersion = "5.9.2";
25
+ const currentVersion = "5.9.4";
26
26
  const isPrerelease = currentVersion.includes("-");
27
27
  if (!isPrerelease) {
28
28
  try {
@@ -37,7 +37,7 @@ function serverStart({
37
37
  host,
38
38
  base
39
39
  }) {
40
- const version = "5.9.2";
40
+ const version = "5.9.4";
41
41
  const localPrefix = `${dim("\u2503")} Local `;
42
42
  const networkPrefix = `${dim("\u2503")} Network `;
43
43
  const emptyPrefix = " ".repeat(11);
@@ -274,7 +274,7 @@ function printHelp({
274
274
  message.push(
275
275
  linebreak(),
276
276
  ` ${bgGreen(black(` ${commandName} `))} ${green(
277
- `v${"5.9.2"}`
277
+ `v${"5.9.4"}`
278
278
  )} ${headline}`
279
279
  );
280
280
  }
@@ -415,6 +415,7 @@ class RenderContext {
415
415
  extraScriptHashes: [],
416
416
  propagators: /* @__PURE__ */ new Set()
417
417
  },
418
+ cspDestination: manifest.csp?.cspDestination ?? (routeData.prerender ? "meta" : "header"),
418
419
  shouldInjectCspMetaTags: !!manifest.csp,
419
420
  cspAlgorithm: manifest.csp?.algorithm ?? "SHA-256",
420
421
  // The following arrays must be cloned, otherwise they become mutable across routes.
@@ -253,7 +253,7 @@ function createRedirectRoutes({ settings }, routeMap) {
253
253
  component: from,
254
254
  generate,
255
255
  pathname: pathname || void 0,
256
- prerender: false,
256
+ prerender: getPrerenderDefault(config),
257
257
  redirect: to,
258
258
  redirectRoute: routeMap.get(destination),
259
259
  fallbackRoutes: [],
@@ -9,6 +9,7 @@ type FindRouteToRewrite = {
9
9
  trailingSlash: AstroConfig['trailingSlash'];
10
10
  buildFormat: AstroConfig['build']['format'];
11
11
  base: AstroConfig['base'];
12
+ outDir: AstroConfig['outDir'] | string;
12
13
  };
13
14
  interface FindRouteToRewriteResult {
14
15
  routeData: RouteData;
@@ -20,7 +21,7 @@ interface FindRouteToRewriteResult {
20
21
  * 1. The new `Request` object. It contains `base`
21
22
  * 2.
22
23
  */
23
- export declare function findRouteToRewrite({ payload, routes, request, trailingSlash, buildFormat, base, }: FindRouteToRewrite): FindRouteToRewriteResult;
24
+ export declare function findRouteToRewrite({ payload, routes, request, trailingSlash, buildFormat, base, outDir, }: FindRouteToRewrite): FindRouteToRewriteResult;
24
25
  /**
25
26
  * Utility function that creates a new `Request` with a new URL from an old `Request`.
26
27
  *
@@ -5,7 +5,8 @@ import {
5
5
  appendForwardSlash,
6
6
  joinPaths,
7
7
  prependForwardSlash,
8
- removeTrailingForwardSlash
8
+ removeTrailingForwardSlash,
9
+ trimSlashes
9
10
  } from "../path.js";
10
11
  import { createRequest } from "../request.js";
11
12
  import { DEFAULT_404_ROUTE } from "./astro-designed-error-pages.js";
@@ -15,7 +16,8 @@ function findRouteToRewrite({
15
16
  request,
16
17
  trailingSlash,
17
18
  buildFormat,
18
- base
19
+ base,
20
+ outDir
19
21
  }) {
20
22
  let newUrl = void 0;
21
23
  if (payload instanceof URL) {
@@ -42,6 +44,9 @@ function findRouteToRewrite({
42
44
  if (pathname === "/" && base !== "/" && !shouldAppendSlash) {
43
45
  pathname = "";
44
46
  }
47
+ if (buildFormat === "file") {
48
+ pathname = pathname.replace(/\.html$/, "");
49
+ }
45
50
  if (base !== "/" && (pathname === "" || pathname === "/") && !shouldAppendSlash) {
46
51
  newUrl.pathname = removeTrailingForwardSlash(base);
47
52
  } else {
@@ -51,6 +56,13 @@ function findRouteToRewrite({
51
56
  let foundRoute;
52
57
  for (const route of routes) {
53
58
  if (route.pattern.test(decodedPathname)) {
59
+ if (route.params && route.params.length !== 0 && route.distURL && route.distURL.length !== 0) {
60
+ if (!route.distURL.find(
61
+ (url) => url.href.replace(outDir.toString(), "").replace(/(?:\/index\.html|\.html)$/, "") == trimSlashes(decodedPathname)
62
+ )) {
63
+ continue;
64
+ }
65
+ }
54
66
  foundRoute = route;
55
67
  break;
56
68
  }
@@ -3,7 +3,7 @@ import type { AstroCookies } from './cookies/cookies.js';
3
3
  export declare const PERSIST_SYMBOL: unique symbol;
4
4
  export declare class AstroSession<TDriver extends SessionDriverName = any> {
5
5
  #private;
6
- constructor(cookies: AstroCookies, { cookie: cookieConfig, ...config }: Exclude<ResolvedSessionConfig<TDriver>, undefined>, runtimeMode?: RuntimeMode);
6
+ constructor(cookies: AstroCookies, { cookie: cookieConfig, ...config }: NonNullable<ResolvedSessionConfig<TDriver>>, runtimeMode?: RuntimeMode);
7
7
  /**
8
8
  * Gets a session value. Returns `undefined` if the session or value does not exist.
9
9
  */
@@ -52,6 +52,15 @@ class AstroSession {
52
52
  cookie: cookieConfig = DEFAULT_COOKIE_NAME,
53
53
  ...config
54
54
  }, runtimeMode) {
55
+ const { driver } = config;
56
+ if (!driver) {
57
+ throw new AstroError({
58
+ ...SessionStorageInitError,
59
+ message: SessionStorageInitError.message(
60
+ "No driver was defined in the session configuration and the adapter did not provide a default driver."
61
+ )
62
+ });
63
+ }
55
64
  this.#cookies = cookies;
56
65
  let cookieConfigObject;
57
66
  if (typeof cookieConfig === "object") {
@@ -68,7 +77,7 @@ class AstroSession {
68
77
  ...cookieConfigObject,
69
78
  httpOnly: true
70
79
  };
71
- this.#config = config;
80
+ this.#config = { ...config, driver };
72
81
  }
73
82
  /**
74
83
  * Gets a session value. Returns `undefined` if the session or value does not exist.
@@ -335,14 +344,6 @@ class AstroSession {
335
344
  this.#config.driver = "fs-lite";
336
345
  this.#config.options.base ??= ".astro/session";
337
346
  }
338
- if (!this.#config?.driver) {
339
- throw new AstroError({
340
- ...SessionStorageInitError,
341
- message: SessionStorageInitError.message(
342
- "No driver was defined in the session configuration and the adapter did not provide a default driver."
343
- )
344
- });
345
- }
346
347
  let driver = null;
347
348
  try {
348
349
  if (this.#config.driverModule) {
@@ -6,7 +6,7 @@ import type { PageBuildData } from '../core/build/types.js';
6
6
  import type { Logger } from '../core/logger/core.js';
7
7
  import type { AstroSettings } from '../types/astro.js';
8
8
  import type { AstroConfig } from '../types/public/config.js';
9
- import type { RouteOptions } from '../types/public/integrations.js';
9
+ import type { IntegrationResolvedRoute, RouteOptions } from '../types/public/integrations.js';
10
10
  import type { RouteData } from '../types/public/internal.js';
11
11
  export declare function getToolbarServerCommunicationHelpers(server: ViteDevServer): {
12
12
  /**
@@ -84,9 +84,10 @@ type RunHookBuildSsr = {
84
84
  middlewareEntryPoint: URL | undefined;
85
85
  };
86
86
  export declare function runHookBuildSsr({ config, manifest, logger, entryPoints, middlewareEntryPoint, }: RunHookBuildSsr): Promise<void>;
87
- export declare function runHookBuildGenerated({ settings, logger, }: {
87
+ export declare function runHookBuildGenerated({ settings, logger, experimentalRouteToHeaders, }: {
88
88
  settings: AstroSettings;
89
89
  logger: Logger;
90
+ experimentalRouteToHeaders: Map<IntegrationResolvedRoute, Headers>;
90
91
  }): Promise<void>;
91
92
  type RunHookBuildDone = {
92
93
  settings: AstroSettings;
@@ -105,4 +106,5 @@ export declare function runHookRoutesResolved({ routes, settings, logger, }: {
105
106
  settings: AstroSettings;
106
107
  logger: Logger;
107
108
  }): Promise<void>;
109
+ export declare function toIntegrationResolvedRoute(route: RouteData): IntegrationResolvedRoute;
108
110
  export {};
@@ -432,7 +432,8 @@ async function runHookBuildSsr({
432
432
  }
433
433
  async function runHookBuildGenerated({
434
434
  settings,
435
- logger
435
+ logger,
436
+ experimentalRouteToHeaders
436
437
  }) {
437
438
  const dir = settings.buildOutput === "server" ? settings.config.build.client : settings.config.outDir;
438
439
  for (const integration of settings.config.integrations) {
@@ -440,7 +441,7 @@ async function runHookBuildGenerated({
440
441
  integration,
441
442
  hookName: "astro:build:generated",
442
443
  logger,
443
- params: () => ({ dir })
444
+ params: () => ({ dir, experimentalRouteToHeaders })
444
445
  });
445
446
  }
446
447
  }
@@ -553,5 +554,6 @@ export {
553
554
  runHookRoutesResolved,
554
555
  runHookServerDone,
555
556
  runHookServerSetup,
556
- runHookServerStart
557
+ runHookServerStart,
558
+ toIntegrationResolvedRoute
557
559
  };
@@ -25,7 +25,7 @@ function renderCspContent(result) {
25
25
  if (result.styleResources.length > 0) {
26
26
  styleResources = result.styleResources.map((r) => `${r}`).join(" ");
27
27
  }
28
- const strictDynamic = result.isStrictDynamic ? ` strict-dynamic` : "";
28
+ const strictDynamic = result.isStrictDynamic ? ` 'strict-dynamic'` : "";
29
29
  const scriptSrc = `script-src ${scriptResources} ${Array.from(finalScriptHashes).join(" ")}${strictDynamic};`;
30
30
  const styleSrc = `style-src ${styleResources} ${Array.from(finalStyleHashes).join(" ")};`;
31
31
  return `${directives} ${scriptSrc} ${styleSrc}`;
@@ -9,6 +9,20 @@ const uniqueElements = (item, index, all) => {
9
9
  };
10
10
  function renderAllHeadContent(result) {
11
11
  result._metadata.hasRenderedHead = true;
12
+ let content = "";
13
+ if (result.shouldInjectCspMetaTags && result.cspDestination === "meta") {
14
+ content += renderElement(
15
+ "meta",
16
+ {
17
+ props: {
18
+ "http-equiv": "content-security-policy",
19
+ content: renderCspContent(result)
20
+ },
21
+ children: ""
22
+ },
23
+ false
24
+ );
25
+ }
12
26
  const styles = Array.from(result.styles).filter(uniqueElements).map(
13
27
  (style) => style.props.rel === "stylesheet" ? renderElement("link", style) : renderElement("style", style)
14
28
  );
@@ -20,25 +34,12 @@ function renderAllHeadContent(result) {
20
34
  return renderElement("script", script, false);
21
35
  });
22
36
  const links = Array.from(result.links).filter(uniqueElements).map((link) => renderElement("link", link, false));
23
- let content = styles.join("\n") + links.join("\n") + scripts.join("\n");
37
+ content += styles.join("\n") + links.join("\n") + scripts.join("\n");
24
38
  if (result._metadata.extraHead.length > 0) {
25
39
  for (const part of result._metadata.extraHead) {
26
40
  content += part;
27
41
  }
28
42
  }
29
- if (result.shouldInjectCspMetaTags) {
30
- content += renderElement(
31
- "meta",
32
- {
33
- props: {
34
- "http-equiv": "content-security-policy",
35
- content: renderCspContent(result)
36
- },
37
- children: ""
38
- },
39
- false
40
- );
41
- }
42
43
  return markHTMLString(content);
43
44
  }
44
45
  function renderHead() {
@@ -2,6 +2,7 @@ import { isAstroComponentFactory } from "./astro/index.js";
2
2
  import { renderToAsyncIterable, renderToReadableStream, renderToString } from "./astro/render.js";
3
3
  import { encoder } from "./common.js";
4
4
  import { renderComponentToString } from "./component.js";
5
+ import { renderCspContent } from "./csp.js";
5
6
  import { isDeno, isNode } from "./util.js";
6
7
  async function renderPage(result, componentFactory, props, children, streaming, route) {
7
8
  if (!isAstroComponentFactory(componentFactory)) {
@@ -17,11 +18,15 @@ async function renderPage(result, componentFactory, props, children, streaming,
17
18
  route
18
19
  );
19
20
  const bytes = encoder.encode(str);
21
+ const headers2 = new Headers([
22
+ ["Content-Type", "text/html"],
23
+ ["Content-Length", bytes.byteLength.toString()]
24
+ ]);
25
+ if (result.cspDestination === "header" || result.cspDestination === "adapter") {
26
+ headers2.set("content-security-policy", renderCspContent(result));
27
+ }
20
28
  return new Response(bytes, {
21
- headers: new Headers([
22
- ["Content-Type", "text/html"],
23
- ["Content-Length", bytes.byteLength.toString()]
24
- ])
29
+ headers: headers2
25
30
  });
26
31
  }
27
32
  result._metadata.headInTree = result.componentMetadata.get(componentFactory.moduleId)?.containsHead ?? false;
@@ -46,6 +51,9 @@ async function renderPage(result, componentFactory, props, children, streaming,
46
51
  if (body instanceof Response) return body;
47
52
  const init = result.response;
48
53
  const headers = new Headers(init.headers);
54
+ if (result.shouldInjectCspMetaTags && result.cspDestination === "header" || result.cspDestination === "adapter") {
55
+ headers.set("content-security-policy", renderCspContent(result));
56
+ }
49
57
  if (!streaming && typeof body === "string") {
50
58
  body = encoder.encode(body);
51
59
  headers.set("Content-Length", body.byteLength.toString());
@@ -99,7 +99,7 @@ let response = await fetch('${serverIslandUrl}', {
99
99
  });`
100
100
  );
101
101
  const content = `${method}replaceServerIsland('${hostId}', response);`;
102
- if (this.result.shouldInjectCspMetaTags) {
102
+ if (this.result.cspDestination) {
103
103
  this.result._metadata.extraScriptHashes.push(
104
104
  await generateCspDigest(SERVER_ISLAND_REPLACER, this.result.cspAlgorithm)
105
105
  );
@@ -123,7 +123,7 @@ interface BuiltinSessionConfig<TDriver extends keyof BuiltinDriverOptions> exten
123
123
  }
124
124
  interface CustomSessionConfig extends CommonSessionConfig {
125
125
  /** Entrypoint for a custom session driver */
126
- driver: string;
126
+ driver?: string;
127
127
  options?: Record<string, unknown>;
128
128
  }
129
129
  interface TestSessionConfig extends CommonSessionConfig {
@@ -73,6 +73,16 @@ export interface AstroAdapterFeatures {
73
73
  * Determine the type of build output the adapter is intended for. Defaults to `server`;
74
74
  */
75
75
  buildOutput?: 'static' | 'server';
76
+ /**
77
+ * If supported by the adapter and enabled, Astro won't add any `<meta http-equiv>` tags
78
+ * in the static pages, instead it will return a mapping in the
79
+ * `astro:build:generated` hook, so adapters can consume them and add them inside
80
+ * their hosting headers configuration file.
81
+ *
82
+ * NOTE: the semantics and list of headers might change until the feature
83
+ * is out of experimental
84
+ */
85
+ experimentalStaticHeaders?: boolean;
76
86
  }
77
87
  export interface AstroAdapter {
78
88
  name: string;
@@ -90,7 +100,7 @@ export interface AstroAdapter {
90
100
  }
91
101
  export type AstroAdapterFeatureMap = {
92
102
  /**
93
- * The adapter is able serve static pages
103
+ * The adapter is able to serve static pages
94
104
  */
95
105
  staticOutput?: AdapterSupport;
96
106
  /**
@@ -194,6 +204,7 @@ export interface BaseIntegrationHooks {
194
204
  'astro:build:generated': (options: {
195
205
  dir: URL;
196
206
  logger: AstroIntegrationLogger;
207
+ experimentalRouteToHeaders: Map<IntegrationResolvedRoute, Headers>;
197
208
  }) => void | Promise<void>;
198
209
  'astro:build:done': (options: {
199
210
  pages: {
@@ -6,7 +6,7 @@ import type { Params } from './common.js';
6
6
  import type { AstroConfig, RedirectConfig } from './config.js';
7
7
  import type { AstroGlobal, AstroGlobalPartial } from './context.js';
8
8
  import type { AstroRenderer } from './integrations.js';
9
- export type { SSRManifest } from '../../core/app/types.js';
9
+ export type { SSRManifest, SSRManifestCSP, SSRActions } from '../../core/app/types.js';
10
10
  export interface NamedSSRLoadedRendererValue extends SSRLoadedRendererValue {
11
11
  name: string;
12
12
  }
@@ -225,8 +225,18 @@ export interface SSRResult {
225
225
  key: Promise<CryptoKey>;
226
226
  _metadata: SSRMetadata;
227
227
  /**
228
- * Whether Astro should inject the CSP <meta> tag into the head of the component.
228
+ * `header`:
229
+ * - <meta> for static pages
230
+ * - Response header for dynamic pages
231
+ *
232
+ * `meta`:
233
+ * - <meta> for all pages
234
+ *
235
+ * `adapter`:
236
+ * - nothing for static pages (the adapter does this)
237
+ * - Response header for dynamic pages
229
238
  */
239
+ cspDestination: NonNullable<SSRManifestCSP['cspDestination']>;
230
240
  shouldInjectCspMetaTags: boolean;
231
241
  cspAlgorithm: SSRManifestCSP['algorithm'];
232
242
  scriptHashes: SSRManifestCSP['scriptHashes'];
@@ -146,7 +146,8 @@ class DevPipeline extends Pipeline {
146
146
  routes: this.routesList?.routes,
147
147
  trailingSlash: this.config.trailingSlash,
148
148
  buildFormat: this.config.build.format,
149
- base: this.config.base
149
+ base: this.config.base,
150
+ outDir: this.manifest.outDir
150
151
  });
151
152
  const componentInstance = await this.getComponentByRoute(routeData);
152
153
  return { newUrl, pathname, componentInstance, routeData };
@@ -144,6 +144,7 @@ function createDevelopmentManifest(settings) {
144
144
  }
145
145
  if (shouldTrackCspHashes(settings.config.experimental.csp)) {
146
146
  csp = {
147
+ cspDestination: settings.adapter?.adapterFeatures?.experimentalStaticHeaders ? "adapter" : void 0,
147
148
  scriptHashes: getScriptHashes(settings.config.experimental.csp),
148
149
  scriptResources: getScriptResources(settings.config.experimental.csp),
149
150
  styleHashes: getStyleHashes(settings.config.experimental.csp),
@@ -167,7 +168,7 @@ function createDevelopmentManifest(settings) {
167
168
  assets: /* @__PURE__ */ new Set(),
168
169
  entryModules: {},
169
170
  routes: [],
170
- adapterName: settings?.adapter?.name || "",
171
+ adapterName: settings?.adapter?.name ?? "",
171
172
  clientDirectives: settings.clientDirectives,
172
173
  renderers: [],
173
174
  base: settings.config.base,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "5.9.2",
3
+ "version": "5.9.4",
4
4
  "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
5
5
  "type": "module",
6
6
  "author": "withastro",
@@ -157,8 +157,8 @@
157
157
  "zod-to-json-schema": "^3.24.5",
158
158
  "zod-to-ts": "^1.2.0",
159
159
  "@astrojs/internal-helpers": "0.6.1",
160
- "@astrojs/telemetry": "3.3.0",
161
- "@astrojs/markdown-remark": "6.3.2"
160
+ "@astrojs/markdown-remark": "6.3.2",
161
+ "@astrojs/telemetry": "3.3.0"
162
162
  },
163
163
  "optionalDependencies": {
164
164
  "sharp": "^0.33.3"