astro 6.2.1 → 6.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 (137) hide show
  1. package/dist/actions/handler.d.ts +32 -0
  2. package/dist/actions/handler.js +45 -0
  3. package/dist/actions/runtime/server.js +1 -1
  4. package/dist/assets/build/generate.js +1 -1
  5. package/dist/assets/build/remote.d.ts +3 -2
  6. package/dist/assets/build/remote.js +16 -9
  7. package/dist/assets/endpoint/generic.js +4 -7
  8. package/dist/assets/endpoint/shared.js +7 -2
  9. package/dist/assets/index.d.ts +1 -0
  10. package/dist/assets/index.js +2 -0
  11. package/dist/assets/internal.js +16 -1
  12. package/dist/assets/services/sharp.js +16 -1
  13. package/dist/assets/utils/index.d.ts +1 -0
  14. package/dist/assets/utils/index.js +2 -0
  15. package/dist/assets/utils/redirectValidation.d.ts +48 -0
  16. package/dist/assets/utils/redirectValidation.js +48 -0
  17. package/dist/assets/utils/remoteProbe.js +25 -2
  18. package/dist/cli/add/index.js +7 -4
  19. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  20. package/dist/container/index.js +18 -14
  21. package/dist/content/content-layer.js +3 -4
  22. package/dist/content/loaders/types.d.ts +1 -1
  23. package/dist/content/server-listeners.js +0 -4
  24. package/dist/content/vite-plugin-content-virtual-mod.js +9 -1
  25. package/dist/core/app/base.d.ts +33 -15
  26. package/dist/core/app/base.js +120 -324
  27. package/dist/core/app/dev/app.d.ts +3 -2
  28. package/dist/core/app/dev/app.js +4 -60
  29. package/dist/core/app/entrypoints/virtual/dev.js +2 -0
  30. package/dist/core/app/entrypoints/virtual/prod.js +4 -1
  31. package/dist/core/app/prepare-response.d.ts +11 -0
  32. package/dist/core/app/prepare-response.js +18 -0
  33. package/dist/core/app/render-options.d.ts +11 -0
  34. package/dist/core/app/render-options.js +11 -0
  35. package/dist/core/base-pipeline.d.ts +38 -1
  36. package/dist/core/base-pipeline.js +50 -7
  37. package/dist/core/build/app.d.ts +3 -4
  38. package/dist/core/build/app.js +3 -17
  39. package/dist/core/build/plugins/plugin-css.js +7 -1
  40. package/dist/core/build/plugins/plugin-manifest.js +11 -1
  41. package/dist/core/build/static-build.js +16 -2
  42. package/dist/core/cache/handler.d.ts +29 -0
  43. package/dist/core/cache/handler.js +81 -0
  44. package/dist/core/compile/style.js +18 -1
  45. package/dist/core/config/index.d.ts +1 -1
  46. package/dist/core/config/index.js +4 -1
  47. package/dist/core/config/schemas/base.d.ts +4 -0
  48. package/dist/core/config/schemas/base.js +4 -0
  49. package/dist/core/config/schemas/relative.d.ts +6 -0
  50. package/dist/core/config/settings.js +2 -4
  51. package/dist/core/config/tsconfig.d.ts +24 -9
  52. package/dist/core/config/tsconfig.js +54 -45
  53. package/dist/core/constants.d.ts +27 -1
  54. package/dist/core/constants.js +14 -1
  55. package/dist/core/cookies/cookies.d.ts +7 -2
  56. package/dist/core/cookies/cookies.js +11 -4
  57. package/dist/core/cookies/response.d.ts +1 -1
  58. package/dist/core/cookies/response.js +1 -2
  59. package/dist/core/create-vite.js +15 -0
  60. package/dist/core/csp/runtime.js +6 -4
  61. package/dist/core/dev/dev.js +1 -1
  62. package/dist/core/errors/build-handler.d.ts +17 -0
  63. package/dist/core/errors/build-handler.js +22 -0
  64. package/dist/core/errors/default-handler.d.ts +14 -0
  65. package/dist/core/errors/default-handler.js +144 -0
  66. package/dist/core/errors/dev-handler.d.ts +21 -0
  67. package/dist/core/errors/dev-handler.js +82 -0
  68. package/dist/core/errors/handler.d.ts +9 -0
  69. package/dist/core/errors/handler.js +0 -0
  70. package/dist/core/fetch/default-handler.d.ts +17 -0
  71. package/dist/core/fetch/default-handler.js +45 -0
  72. package/dist/core/fetch/fetch-state.d.ts +244 -0
  73. package/dist/core/fetch/fetch-state.js +779 -0
  74. package/dist/core/fetch/index.d.ts +61 -0
  75. package/dist/core/fetch/index.js +121 -0
  76. package/dist/core/fetch/types.d.ts +6 -0
  77. package/dist/core/fetch/types.js +0 -0
  78. package/dist/core/fetch/vite-plugin.d.ts +5 -0
  79. package/dist/core/fetch/vite-plugin.js +69 -0
  80. package/dist/core/hono/index.d.ts +21 -0
  81. package/dist/core/hono/index.js +98 -0
  82. package/dist/core/i18n/handler.d.ts +18 -0
  83. package/dist/core/i18n/handler.js +119 -0
  84. package/dist/core/logger/core.d.ts +8 -0
  85. package/dist/core/logger/core.js +16 -0
  86. package/dist/core/messages/runtime.js +1 -1
  87. package/dist/core/middleware/astro-middleware.d.ts +27 -0
  88. package/dist/core/middleware/astro-middleware.js +53 -0
  89. package/dist/core/pages/handler.d.ts +20 -0
  90. package/dist/core/pages/handler.js +74 -0
  91. package/dist/core/redirects/render.d.ts +2 -2
  92. package/dist/core/redirects/render.js +7 -8
  93. package/dist/core/render/params-and-props.js +1 -1
  94. package/dist/core/render/slots.js +9 -2
  95. package/dist/core/rewrites/handler.d.ts +37 -0
  96. package/dist/core/rewrites/handler.js +67 -0
  97. package/dist/core/routing/3xx.js +8 -4
  98. package/dist/core/routing/handler.d.ts +17 -0
  99. package/dist/core/routing/handler.js +172 -0
  100. package/dist/core/routing/match.d.ts +0 -7
  101. package/dist/core/routing/match.js +0 -5
  102. package/dist/core/routing/pattern.js +1 -1
  103. package/dist/core/routing/rewrite.js +1 -4
  104. package/dist/core/routing/trailing-slash-handler.d.ts +18 -0
  105. package/dist/core/routing/trailing-slash-handler.js +67 -0
  106. package/dist/core/session/drivers.d.ts +1 -1
  107. package/dist/core/session/handler.d.ts +11 -0
  108. package/dist/core/session/handler.js +33 -0
  109. package/dist/core/session/runtime.js +7 -2
  110. package/dist/core/util/normalized-url.d.ts +10 -0
  111. package/dist/core/util/normalized-url.js +21 -0
  112. package/dist/i18n/middleware.d.ts +10 -0
  113. package/dist/i18n/middleware.js +4 -88
  114. package/dist/i18n/utils.js +2 -2
  115. package/dist/prefetch/index.js +12 -7
  116. package/dist/runtime/server/astro-island.js +57 -20
  117. package/dist/runtime/server/astro-island.prebuilt-dev.d.ts +1 -1
  118. package/dist/runtime/server/astro-island.prebuilt-dev.js +1 -1
  119. package/dist/runtime/server/astro-island.prebuilt.d.ts +1 -1
  120. package/dist/runtime/server/astro-island.prebuilt.js +1 -1
  121. package/dist/runtime/server/render/common.js +10 -4
  122. package/dist/runtime/server/render/server-islands.js +2 -1
  123. package/dist/runtime/server/scripts.js +6 -0
  124. package/dist/types/public/config.d.ts +46 -12
  125. package/dist/types/public/content.d.ts +4 -4
  126. package/dist/types/public/internal.d.ts +1 -1
  127. package/dist/vite-plugin-app/app.d.ts +4 -5
  128. package/dist/vite-plugin-app/app.js +20 -68
  129. package/dist/vite-plugin-astro/compile.js +2 -2
  130. package/dist/vite-plugin-astro/utils.d.ts +1 -0
  131. package/dist/vite-plugin-astro/utils.js +9 -1
  132. package/dist/vite-plugin-head/index.js +36 -19
  133. package/dist/vite-plugin-utils/index.d.ts +1 -0
  134. package/dist/vite-plugin-utils/index.js +3 -0
  135. package/package.json +13 -6
  136. package/dist/core/render-context.d.ts +0 -77
  137. package/dist/core/render-context.js +0 -826
@@ -0,0 +1,779 @@
1
+ import colors from "piccolore";
2
+ import {
3
+ collapseDuplicateLeadingSlashes,
4
+ prependForwardSlash,
5
+ removeTrailingForwardSlash
6
+ } from "@astrojs/internal-helpers/path";
7
+ import { deserializeActionResult } from "../../actions/runtime/client.js";
8
+ import { createCallAction, createGetActionResult, hasActionPayload } from "../../actions/utils.js";
9
+ import { AstroCookies } from "../cookies/index.js";
10
+ import { Slots } from "../render/index.js";
11
+ import {
12
+ ASTRO_GENERATOR,
13
+ DEFAULT_404_COMPONENT,
14
+ fetchStateSymbol,
15
+ originPathnameSymbol,
16
+ pipelineSymbol,
17
+ responseSentSymbol
18
+ } from "../constants.js";
19
+ import { pushDirective } from "../csp/runtime.js";
20
+ import { generateCspDigest } from "../encryption.js";
21
+ import { AstroError, AstroErrorData } from "../errors/index.js";
22
+ import {
23
+ computeCurrentLocale as computeCurrentLocaleUtil,
24
+ computeCurrentLocaleFromParams,
25
+ computePreferredLocale as computePreferredLocaleUtil,
26
+ computePreferredLocaleList as computePreferredLocaleListUtil
27
+ } from "../../i18n/utils.js";
28
+ import { getParams, getProps } from "../render/index.js";
29
+ import { Rewrites } from "../rewrites/handler.js";
30
+ import { isRoute404or500, isRouteServerIsland } from "../routing/match.js";
31
+ import { normalizeUrl } from "../util/normalized-url.js";
32
+ import { getOriginPathname, setOriginPathname } from "../routing/rewrite.js";
33
+ import { routeHasHtmlExtension } from "../routing/helpers.js";
34
+ import { getRenderOptions } from "../app/render-options.js";
35
+ function getFetchStateFromAPIContext(context) {
36
+ const state = context[fetchStateSymbol];
37
+ if (!state) {
38
+ throw new Error(
39
+ "FetchState not found on APIContext. This is an internal error \u2014 the context was not created through Astro's request pipeline."
40
+ );
41
+ }
42
+ return state;
43
+ }
44
+ class FetchState {
45
+ pipeline;
46
+ /**
47
+ * The request to render. Mutated during rewrites so subsequent renders
48
+ * see the rewritten URL.
49
+ */
50
+ request;
51
+ routeData;
52
+ /**
53
+ * The pathname to use for routing and rendering. Starts out as the raw,
54
+ * base-stripped, decoded pathname from the request. May be further
55
+ * normalized by `AstroHandler` after routeData is known (in dev, when
56
+ * the matched route has no `.html` extension, `.html` / `/index.html`
57
+ * suffixes are stripped).
58
+ */
59
+ pathname;
60
+ /** Resolved render options (addCookieHeader, clientAddress, locals, etc.). */
61
+ renderOptions;
62
+ /** When the request started, used to log duration. */
63
+ timeStart;
64
+ /**
65
+ * The route's loaded component module. Set before middleware runs; may
66
+ * be swapped during in-flight rewrites from inside the middleware chain.
67
+ */
68
+ componentInstance;
69
+ /**
70
+ * Slot overrides supplied by the container API. `undefined` for HTTP
71
+ * requests — `PagesHandler` coalesces to `{}` on read so we don't
72
+ * allocate an empty object per request.
73
+ */
74
+ slots;
75
+ /**
76
+ * Default HTTP status for the rendered response. Callers override
77
+ * before rendering runs (e.g. `AstroHandler` sets this from
78
+ * `BaseApp.getDefaultStatusCode`; error handlers set `404` / `500`).
79
+ */
80
+ status = 200;
81
+ /** Whether user middleware should be skipped for this request. */
82
+ skipMiddleware = false;
83
+ /** A flag that tells the render content if the rewriting was triggered. */
84
+ isRewriting = false;
85
+ /** A safety net in case of loops (rewrite counter). */
86
+ counter = 0;
87
+ /** Cookies for this request. Created lazily on first access. */
88
+ cookies;
89
+ /** Route params derived from routeData + pathname. Computed lazily. */
90
+ #params;
91
+ get params() {
92
+ if (!this.#params && this.routeData) {
93
+ this.#params = getParams(this.routeData, this.pathname);
94
+ }
95
+ return this.#params;
96
+ }
97
+ set params(value) {
98
+ this.#params = value;
99
+ }
100
+ /** Normalized URL for this request. */
101
+ url;
102
+ /** Client address for this request. */
103
+ clientAddress;
104
+ /** Whether this is a partial render (container API). */
105
+ partial;
106
+ /** Whether to inject CSP meta tags. */
107
+ shouldInjectCspMetaTags;
108
+ /** Request-scoped locals object, shared with user middleware. */
109
+ locals = {};
110
+ /**
111
+ * Memoized `props` (see `getProps`). `null` means "not yet computed"
112
+ * — using `null` (rather than `undefined`) keeps the hidden class
113
+ * stable and distinct from a valid-but-empty result.
114
+ */
115
+ props = null;
116
+ /** Memoized `ActionAPIContext` (see `getActionAPIContext`). */
117
+ actionApiContext = null;
118
+ /** Memoized `APIContext` (see `getAPIContext`). */
119
+ apiContext = null;
120
+ /** Registered context providers keyed by name. Lazy-initialized on first provide(). */
121
+ #providers;
122
+ /** Cached values from resolved providers. Lazy-initialized on first resolve(). */
123
+ #providersResolvedValues;
124
+ /** Cached promise for lazy component instance loading. */
125
+ #componentInstancePromise;
126
+ /** SSR result for the current page render. */
127
+ result;
128
+ /** Initial props (from container/error handler). */
129
+ initialProps = {};
130
+ /** Rewrites handler instance. Lazy-initialized on first rewrite(). */
131
+ #rewrites;
132
+ /** Memoized Astro page partial. */
133
+ #astroPagePartial;
134
+ /** Memoized current locale. */
135
+ #currentLocale;
136
+ /** Memoized preferred locale. */
137
+ #preferredLocale;
138
+ /** Memoized preferred locale list. */
139
+ #preferredLocaleList;
140
+ constructor(pipeline, request, options) {
141
+ this.pipeline = pipeline;
142
+ this.request = request;
143
+ options ??= getRenderOptions(request);
144
+ this.routeData = options?.routeData;
145
+ this.renderOptions = options ?? {
146
+ addCookieHeader: false,
147
+ clientAddress: void 0,
148
+ locals: void 0,
149
+ prerenderedErrorPageFetch: fetch,
150
+ routeData: void 0,
151
+ waitUntil: void 0
152
+ };
153
+ this.componentInstance = void 0;
154
+ this.slots = void 0;
155
+ const url = new URL(request.url);
156
+ this.pathname = this.#computePathname(url);
157
+ this.timeStart = performance.now();
158
+ this.clientAddress = options?.clientAddress;
159
+ this.locals = options?.locals ?? {};
160
+ this.url = normalizeUrl(url);
161
+ this.cookies = new AstroCookies(request);
162
+ if (!Reflect.get(request, originPathnameSymbol)) {
163
+ setOriginPathname(
164
+ request,
165
+ this.pathname,
166
+ pipeline.manifest.trailingSlash,
167
+ pipeline.manifest.buildFormat
168
+ );
169
+ }
170
+ this.#resolveRouteData();
171
+ }
172
+ /**
173
+ * Triggers a rewrite. Delegates to the Rewrites handler.
174
+ */
175
+ rewrite(payload) {
176
+ return (this.#rewrites ??= new Rewrites()).execute(this, payload);
177
+ }
178
+ /**
179
+ * Creates the SSR result for the current page render.
180
+ */
181
+ async createResult(mod, ctx) {
182
+ const pipeline = this.pipeline;
183
+ const { clientDirectives, inlinedScripts, compressHTML, manifest, renderers, resolve } = pipeline;
184
+ const routeData = this.routeData;
185
+ const { links, scripts, styles } = await pipeline.headElements(routeData);
186
+ const extraStyleHashes = [];
187
+ const extraScriptHashes = [];
188
+ const shouldInjectCspMetaTags = this.shouldInjectCspMetaTags ?? manifest.shouldInjectCspMetaTags;
189
+ const cspAlgorithm = manifest.csp?.algorithm ?? "SHA-256";
190
+ if (shouldInjectCspMetaTags) {
191
+ for (const style of styles) {
192
+ extraStyleHashes.push(await generateCspDigest(style.children, cspAlgorithm));
193
+ }
194
+ for (const script of scripts) {
195
+ extraScriptHashes.push(await generateCspDigest(script.children, cspAlgorithm));
196
+ }
197
+ }
198
+ const componentMetadata = await pipeline.componentMetadata(routeData) ?? manifest.componentMetadata;
199
+ const headers = new Headers({ "Content-Type": "text/html" });
200
+ const partial = typeof this.partial === "boolean" ? this.partial : Boolean(mod.partial);
201
+ const actionResult = hasActionPayload(this.locals) ? deserializeActionResult(this.locals._actionPayload.actionResult) : void 0;
202
+ const status = this.status;
203
+ const response = {
204
+ status: actionResult?.error ? actionResult?.error.status : status,
205
+ statusText: actionResult?.error ? actionResult?.error.type : "OK",
206
+ get headers() {
207
+ return headers;
208
+ },
209
+ set headers(_) {
210
+ throw new AstroError(AstroErrorData.AstroResponseHeadersReassigned);
211
+ }
212
+ };
213
+ const state = this;
214
+ const result = {
215
+ base: manifest.base,
216
+ userAssetsBase: manifest.userAssetsBase,
217
+ cancelled: false,
218
+ clientDirectives,
219
+ inlinedScripts,
220
+ componentMetadata,
221
+ compressHTML,
222
+ cookies: this.cookies,
223
+ createAstro: (props, slots) => state.createAstro(result, props, slots, ctx),
224
+ links,
225
+ // SAFETY: createResult is only called after route resolution, so routeData
226
+ // is always set and the params getter always returns a value.
227
+ params: this.params,
228
+ partial,
229
+ pathname: this.pathname,
230
+ renderers,
231
+ resolve,
232
+ response,
233
+ request: this.request,
234
+ scripts,
235
+ styles,
236
+ actionResult,
237
+ async getServerIslandNameMap() {
238
+ const serverIslands = await pipeline.getServerIslands();
239
+ return serverIslands.serverIslandNameMap ?? /* @__PURE__ */ new Map();
240
+ },
241
+ key: manifest.key,
242
+ trailingSlash: manifest.trailingSlash,
243
+ _experimentalQueuedRendering: {
244
+ pool: pipeline.nodePool,
245
+ htmlStringCache: pipeline.htmlStringCache,
246
+ enabled: manifest.experimentalQueuedRendering?.enabled,
247
+ poolSize: manifest.experimentalQueuedRendering?.poolSize,
248
+ contentCache: manifest.experimentalQueuedRendering?.contentCache
249
+ },
250
+ _metadata: {
251
+ hasHydrationScript: false,
252
+ rendererSpecificHydrationScripts: /* @__PURE__ */ new Set(),
253
+ hasRenderedHead: false,
254
+ renderedScripts: /* @__PURE__ */ new Set(),
255
+ hasDirectives: /* @__PURE__ */ new Set(),
256
+ hasRenderedServerIslandRuntime: false,
257
+ headInTree: false,
258
+ extraHead: [],
259
+ extraStyleHashes,
260
+ extraScriptHashes,
261
+ propagators: /* @__PURE__ */ new Set(),
262
+ templateDepth: 0
263
+ },
264
+ cspDestination: manifest.csp?.cspDestination ?? (routeData.prerender ? "meta" : "header"),
265
+ shouldInjectCspMetaTags,
266
+ cspAlgorithm,
267
+ scriptHashes: manifest.csp?.scriptHashes ? [...manifest.csp.scriptHashes] : [],
268
+ scriptResources: manifest.csp?.scriptResources ? [...manifest.csp.scriptResources] : [],
269
+ styleHashes: manifest.csp?.styleHashes ? [...manifest.csp.styleHashes] : [],
270
+ styleResources: manifest.csp?.styleResources ? [...manifest.csp.styleResources] : [],
271
+ directives: manifest.csp?.directives ? [...manifest.csp.directives] : [],
272
+ isStrictDynamic: manifest.csp?.isStrictDynamic ?? false,
273
+ internalFetchHeaders: manifest.internalFetchHeaders
274
+ };
275
+ this.result = result;
276
+ return result;
277
+ }
278
+ /**
279
+ * Creates the Astro global object for a component render.
280
+ */
281
+ createAstro(result, props, slotValues, apiContext) {
282
+ let astroPagePartial;
283
+ if (this.isRewriting) {
284
+ this.#astroPagePartial = this.createAstroPagePartial(result, apiContext);
285
+ }
286
+ this.#astroPagePartial ??= this.createAstroPagePartial(result, apiContext);
287
+ astroPagePartial = this.#astroPagePartial;
288
+ const astroComponentPartial = { props, self: null };
289
+ const Astro = Object.assign(
290
+ Object.create(astroPagePartial),
291
+ astroComponentPartial
292
+ );
293
+ let _slots;
294
+ Object.defineProperty(Astro, "slots", {
295
+ get: () => {
296
+ if (!_slots) {
297
+ _slots = new Slots(
298
+ result,
299
+ slotValues,
300
+ this.pipeline.logger
301
+ );
302
+ }
303
+ return _slots;
304
+ }
305
+ });
306
+ return Astro;
307
+ }
308
+ /**
309
+ * Creates the Astro page-level partial (prototype for Astro global).
310
+ */
311
+ createAstroPagePartial(result, apiContext) {
312
+ const state = this;
313
+ const { cookies, locals, params, pipeline, url } = this;
314
+ const { response } = result;
315
+ const redirect = (path, status = 302) => {
316
+ if (state.request[responseSentSymbol]) {
317
+ throw new AstroError({
318
+ ...AstroErrorData.ResponseSentError
319
+ });
320
+ }
321
+ return new Response(null, { status, headers: { Location: path } });
322
+ };
323
+ const rewrite = async (reroutePayload) => {
324
+ return await state.rewrite(reroutePayload);
325
+ };
326
+ const callAction = createCallAction(apiContext);
327
+ const partial = {
328
+ generator: ASTRO_GENERATOR,
329
+ routePattern: this.routeData.route,
330
+ isPrerendered: this.routeData.prerender,
331
+ cookies,
332
+ get clientAddress() {
333
+ return state.getClientAddress();
334
+ },
335
+ get currentLocale() {
336
+ return state.computeCurrentLocale();
337
+ },
338
+ params,
339
+ get preferredLocale() {
340
+ return state.computePreferredLocale();
341
+ },
342
+ get preferredLocaleList() {
343
+ return state.computePreferredLocaleList();
344
+ },
345
+ locals,
346
+ redirect,
347
+ rewrite,
348
+ request: this.request,
349
+ response,
350
+ site: pipeline.site,
351
+ getActionResult: createGetActionResult(locals),
352
+ get callAction() {
353
+ return callAction;
354
+ },
355
+ url,
356
+ get originPathname() {
357
+ return getOriginPathname(state.request);
358
+ },
359
+ get csp() {
360
+ return state.getCsp();
361
+ },
362
+ get logger() {
363
+ return {
364
+ info(msg) {
365
+ pipeline.logger.info(null, msg);
366
+ },
367
+ warn(msg) {
368
+ pipeline.logger.warn(null, msg);
369
+ },
370
+ error(msg) {
371
+ pipeline.logger.error(null, msg);
372
+ }
373
+ };
374
+ }
375
+ };
376
+ this.defineProviderGetters(partial);
377
+ return partial;
378
+ }
379
+ getClientAddress() {
380
+ const { pipeline, clientAddress } = this;
381
+ const routeData = this.routeData;
382
+ if (routeData.prerender) {
383
+ throw new AstroError({
384
+ ...AstroErrorData.PrerenderClientAddressNotAvailable,
385
+ message: AstroErrorData.PrerenderClientAddressNotAvailable.message(routeData.component)
386
+ });
387
+ }
388
+ if (clientAddress) {
389
+ return clientAddress;
390
+ }
391
+ if (pipeline.adapterName) {
392
+ throw new AstroError({
393
+ ...AstroErrorData.ClientAddressNotAvailable,
394
+ message: AstroErrorData.ClientAddressNotAvailable.message(pipeline.adapterName)
395
+ });
396
+ }
397
+ throw new AstroError(AstroErrorData.StaticClientAddressNotAvailable);
398
+ }
399
+ getCookies() {
400
+ return this.cookies;
401
+ }
402
+ getCsp() {
403
+ const state = this;
404
+ const { pipeline } = this;
405
+ if (!pipeline.manifest.csp) {
406
+ if (pipeline.runtimeMode === "production") {
407
+ pipeline.logger.warn(
408
+ "csp",
409
+ `context.csp was used when rendering the route ${colors.green(state.routeData.route)}, but CSP was not configured. For more information, see https://docs.astro.build/en/reference/configuration-reference/#securitycsp`
410
+ );
411
+ }
412
+ return void 0;
413
+ }
414
+ return {
415
+ insertDirective(payload) {
416
+ if (state?.result?.directives) {
417
+ state.result.directives = pushDirective(state.result.directives, payload);
418
+ } else {
419
+ state?.result?.directives.push(payload);
420
+ }
421
+ },
422
+ insertScriptResource(resource) {
423
+ state.result?.scriptResources.push(resource);
424
+ },
425
+ insertStyleResource(resource) {
426
+ state.result?.styleResources.push(resource);
427
+ },
428
+ insertStyleHash(hash) {
429
+ state.result?.styleHashes.push(hash);
430
+ },
431
+ insertScriptHash(hash) {
432
+ state.result?.scriptHashes.push(hash);
433
+ }
434
+ };
435
+ }
436
+ computeCurrentLocale() {
437
+ const {
438
+ url,
439
+ pipeline: { i18n },
440
+ routeData
441
+ } = this;
442
+ if (!i18n || !routeData) return;
443
+ const { defaultLocale, locales, strategy } = i18n;
444
+ const fallbackTo = strategy === "pathname-prefix-other-locales" || strategy === "domains-prefix-other-locales" ? defaultLocale : void 0;
445
+ if (this.#currentLocale) {
446
+ return this.#currentLocale;
447
+ }
448
+ let computedLocale;
449
+ if (isRouteServerIsland(routeData)) {
450
+ let referer = this.request.headers.get("referer");
451
+ if (referer) {
452
+ if (URL.canParse(referer)) {
453
+ referer = new URL(referer).pathname;
454
+ }
455
+ computedLocale = computeCurrentLocaleUtil(referer, locales, defaultLocale);
456
+ }
457
+ } else {
458
+ let pathname = routeData.pathname;
459
+ if (url && !routeData.pattern.test(url.pathname)) {
460
+ for (const fallbackRoute of routeData.fallbackRoutes) {
461
+ if (fallbackRoute.pattern.test(url.pathname)) {
462
+ pathname = fallbackRoute.pathname;
463
+ break;
464
+ }
465
+ }
466
+ }
467
+ pathname = pathname && !isRoute404or500(routeData) ? pathname : url.pathname ?? this.pathname;
468
+ computedLocale = computeCurrentLocaleUtil(pathname, locales, defaultLocale);
469
+ if (routeData.params.length > 0) {
470
+ const localeFromParams = computeCurrentLocaleFromParams(this.params, locales);
471
+ if (localeFromParams) {
472
+ computedLocale = localeFromParams;
473
+ }
474
+ }
475
+ }
476
+ this.#currentLocale = computedLocale ?? fallbackTo;
477
+ return this.#currentLocale;
478
+ }
479
+ computePreferredLocale() {
480
+ const {
481
+ pipeline: { i18n },
482
+ request
483
+ } = this;
484
+ if (!i18n) return;
485
+ return this.#preferredLocale ??= computePreferredLocaleUtil(request, i18n.locales);
486
+ }
487
+ computePreferredLocaleList() {
488
+ const {
489
+ pipeline: { i18n },
490
+ request
491
+ } = this;
492
+ if (!i18n) return;
493
+ return this.#preferredLocaleList ??= computePreferredLocaleListUtil(request, i18n.locales);
494
+ }
495
+ /**
496
+ * Lazily loads the route's component module. Returns the cached
497
+ * instance if already loaded. The promise is cached so concurrent
498
+ * callers share the same load.
499
+ */
500
+ async loadComponentInstance() {
501
+ if (this.componentInstance) return this.componentInstance;
502
+ if (this.#componentInstancePromise) return this.#componentInstancePromise;
503
+ this.#componentInstancePromise = this.pipeline.getComponentByRoute(this.routeData).then((mod) => {
504
+ this.componentInstance = mod;
505
+ return mod;
506
+ });
507
+ return this.#componentInstancePromise;
508
+ }
509
+ /**
510
+ * Registers a context provider under the given key. Handlers call
511
+ * this to contribute values to the request context (e.g. sessions).
512
+ * The `create` factory is called lazily on the first `resolve(key)`.
513
+ */
514
+ provide(key, provider) {
515
+ (this.#providers ??= /* @__PURE__ */ new Map()).set(key, provider);
516
+ }
517
+ /**
518
+ * Lazily resolves a provider registered under `key`. Calls
519
+ * `provider.create()` on first access and caches the result.
520
+ * Returns `undefined` if no provider was registered for the key.
521
+ */
522
+ resolve(key) {
523
+ if (this.#providersResolvedValues?.has(key)) {
524
+ return this.#providersResolvedValues.get(key);
525
+ }
526
+ const provider = this.#providers?.get(key);
527
+ if (!provider) return void 0;
528
+ const value = provider.create();
529
+ (this.#providersResolvedValues ??= /* @__PURE__ */ new Map()).set(key, value);
530
+ return value;
531
+ }
532
+ /**
533
+ * Runs all registered `finalize` callbacks. Should be called after
534
+ * the response is produced, typically in a `finally` block.
535
+ *
536
+ * Returns synchronously (no promise allocation) when nothing needs
537
+ * finalizing — important for the hot path where sessions are not used.
538
+ */
539
+ finalizeAll() {
540
+ if (!this.#providersResolvedValues || this.#providersResolvedValues.size === 0) return;
541
+ let chain;
542
+ for (const [key, provider] of this.#providers) {
543
+ if (provider.finalize && this.#providersResolvedValues.has(key)) {
544
+ const result = provider.finalize(this.#providersResolvedValues.get(key));
545
+ if (result) {
546
+ chain = chain ? chain.then(() => result) : result;
547
+ }
548
+ }
549
+ }
550
+ return chain;
551
+ }
552
+ /**
553
+ * Adds lazy getters to `target` for each registered provider key.
554
+ * Used by context creation (APIContext, Astro global) so that
555
+ * provider values like `session` and `cache` appear as properties
556
+ * without hard-coding the keys.
557
+ */
558
+ defineProviderGetters(target) {
559
+ if (!this.#providers) return;
560
+ const state = this;
561
+ for (const key of this.#providers.keys()) {
562
+ Object.defineProperty(target, key, {
563
+ get: () => state.resolve(key),
564
+ enumerable: true,
565
+ configurable: true
566
+ });
567
+ }
568
+ }
569
+ /**
570
+ * Resolves the route to use for this request and stores it on
571
+ * `this.routeData`. If the adapter (or the dev server) provided a
572
+ * `routeData` via render options it's already set and this is a
573
+ * no-op. Otherwise we use the app's synchronous route matcher and
574
+ * fall back to a `404.astro` route so middleware can still run.
575
+ *
576
+ * Called eagerly from the constructor so individual handlers
577
+ * (actions, pages, middleware, etc.) always see a resolved route
578
+ * without the caller needing an extra setup step.
579
+ *
580
+ * Once routeData is known, finalizes `this.pathname`: in dev, if the
581
+ * matched route has no `.html` extension, strip `.html` / `/index.html`
582
+ * suffixes so the rendering pipeline sees the canonical pathname.
583
+ */
584
+ /**
585
+ * Strip `.html` / `/index.html` suffixes from the pathname so the
586
+ * rendering pipeline sees the canonical route path. Skipped when the
587
+ * matched route itself has an `.html` extension in its definition.
588
+ */
589
+ #stripHtmlExtension() {
590
+ if (this.routeData && !routeHasHtmlExtension(this.routeData)) {
591
+ this.pathname = this.pathname.replace(/\/index\.html$/, "/").replace(/\.html$/, "");
592
+ }
593
+ }
594
+ #resolveRouteData() {
595
+ const pipeline = this.pipeline;
596
+ if (this.routeData) {
597
+ this.#stripHtmlExtension();
598
+ return;
599
+ }
600
+ const matched = pipeline.matchRoute(this.pathname);
601
+ if (matched && matched.prerender && pipeline.manifest.serverLike) {
602
+ this.routeData = void 0;
603
+ } else {
604
+ this.routeData = matched;
605
+ }
606
+ pipeline.logger.debug("router", "Astro matched the following route for " + this.request.url);
607
+ pipeline.logger.debug("router", "RouteData:\n" + this.routeData);
608
+ if (!this.routeData) {
609
+ this.routeData = pipeline.manifestData.routes.find(
610
+ (route) => route.component === "404.astro" || route.component === DEFAULT_404_COMPONENT
611
+ );
612
+ }
613
+ if (!this.routeData) {
614
+ pipeline.logger.debug("router", "Astro hasn't found routes that match " + this.request.url);
615
+ pipeline.logger.debug("router", "Here's the available routes:\n", pipeline.manifestData);
616
+ return;
617
+ }
618
+ this.#stripHtmlExtension();
619
+ }
620
+ /**
621
+ * Strips the pipeline's base from the request URL, prepends a forward
622
+ * slash, and decodes the pathname. Falls back to the raw (not decoded)
623
+ * pathname if `decodeURI` throws.
624
+ *
625
+ * Mirrors `BaseApp.removeBase`, including the
626
+ * `collapseDuplicateLeadingSlashes` fix that prevents middleware
627
+ * authorization bypass when the URL starts with `//`.
628
+ */
629
+ #computePathname(url) {
630
+ let pathname = collapseDuplicateLeadingSlashes(url.pathname);
631
+ const base = this.pipeline.manifest.base;
632
+ if (pathname.startsWith(base)) {
633
+ const baseWithoutTrailingSlash = removeTrailingForwardSlash(base);
634
+ pathname = pathname.slice(baseWithoutTrailingSlash.length + 1);
635
+ }
636
+ pathname = prependForwardSlash(pathname);
637
+ try {
638
+ return decodeURI(pathname);
639
+ } catch (e) {
640
+ this.pipeline.logger.error(null, e.toString());
641
+ return pathname;
642
+ }
643
+ }
644
+ /**
645
+ * Returns the resolved `props` for this render, computing them lazily
646
+ * from the route + component module on first access. If the
647
+ * `initialProps` already carries user-supplied props (e.g. the
648
+ * container API) those are used verbatim.
649
+ */
650
+ async getProps() {
651
+ if (this.props !== null) return this.props;
652
+ if (Object.keys(this.initialProps).length > 0) {
653
+ this.props = this.initialProps;
654
+ return this.props;
655
+ }
656
+ const pipeline = this.pipeline;
657
+ const mod = await this.loadComponentInstance();
658
+ this.props = await getProps({
659
+ mod,
660
+ routeData: this.routeData,
661
+ routeCache: pipeline.routeCache,
662
+ pathname: this.pathname,
663
+ logger: pipeline.logger,
664
+ serverLike: pipeline.manifest.serverLike,
665
+ base: pipeline.manifest.base,
666
+ trailingSlash: pipeline.manifest.trailingSlash
667
+ });
668
+ return this.props;
669
+ }
670
+ /**
671
+ * Returns the `ActionAPIContext` for this render, creating it lazily.
672
+ * Used by middleware, actions, and page dispatch.
673
+ */
674
+ getActionAPIContext() {
675
+ if (this.actionApiContext !== null) return this.actionApiContext;
676
+ const state = this;
677
+ const ctx = {
678
+ get cookies() {
679
+ return state.cookies;
680
+ },
681
+ routePattern: this.routeData.route,
682
+ isPrerendered: this.routeData.prerender,
683
+ get clientAddress() {
684
+ return state.getClientAddress();
685
+ },
686
+ get currentLocale() {
687
+ return state.computeCurrentLocale();
688
+ },
689
+ generator: ASTRO_GENERATOR,
690
+ get locals() {
691
+ return state.locals;
692
+ },
693
+ set locals(_) {
694
+ throw new AstroError(AstroErrorData.LocalsReassigned);
695
+ },
696
+ // SAFETY: getActionAPIContext is only called after route resolution,
697
+ // so routeData is always set and the params getter always returns a value.
698
+ params: this.params,
699
+ get preferredLocale() {
700
+ return state.computePreferredLocale();
701
+ },
702
+ get preferredLocaleList() {
703
+ return state.computePreferredLocaleList();
704
+ },
705
+ request: this.request,
706
+ site: this.pipeline.site,
707
+ url: this.url,
708
+ get originPathname() {
709
+ return getOriginPathname(state.request);
710
+ },
711
+ get csp() {
712
+ return state.getCsp();
713
+ },
714
+ get logger() {
715
+ if (!state.pipeline.manifest.experimentalLogger) {
716
+ state.pipeline.logger.warn(
717
+ null,
718
+ "The Astro.logger is available only when experimental.logger is defined."
719
+ );
720
+ return void 0;
721
+ }
722
+ return {
723
+ info(msg) {
724
+ state.pipeline.logger.info(null, msg);
725
+ },
726
+ warn(msg) {
727
+ state.pipeline.logger.warn(null, msg);
728
+ },
729
+ error(msg) {
730
+ state.pipeline.logger.error(null, msg);
731
+ }
732
+ };
733
+ }
734
+ };
735
+ this.defineProviderGetters(ctx);
736
+ this.actionApiContext = ctx;
737
+ return this.actionApiContext;
738
+ }
739
+ /**
740
+ * Returns the `APIContext` for this render, creating it lazily from
741
+ * the memoized props + action context.
742
+ *
743
+ * Callers must ensure `getProps()` has resolved at least once before
744
+ * calling this.
745
+ */
746
+ getAPIContext() {
747
+ if (this.apiContext !== null) return this.apiContext;
748
+ const actionApiContext = this.getActionAPIContext();
749
+ const state = this;
750
+ const redirect = (path, status = 302) => new Response(null, { status, headers: { Location: path } });
751
+ const rewrite = async (reroutePayload) => {
752
+ return await state.rewrite(reroutePayload);
753
+ };
754
+ Reflect.set(actionApiContext, pipelineSymbol, this.pipeline);
755
+ actionApiContext[fetchStateSymbol] = this;
756
+ this.apiContext = Object.assign(actionApiContext, {
757
+ props: this.props,
758
+ redirect,
759
+ rewrite,
760
+ getActionResult: createGetActionResult(actionApiContext.locals),
761
+ callAction: createCallAction(actionApiContext)
762
+ });
763
+ return this.apiContext;
764
+ }
765
+ /**
766
+ * Invalidates the cached `APIContext` so the next `getAPIContext()`
767
+ * call re-derives it from the (possibly mutated) state. Used
768
+ * after an in-flight rewrite swaps the route / request / params.
769
+ */
770
+ invalidateContexts() {
771
+ this.props = null;
772
+ this.actionApiContext = null;
773
+ this.apiContext = null;
774
+ }
775
+ }
776
+ export {
777
+ FetchState,
778
+ getFetchStateFromAPIContext
779
+ };