@screenbook/ui 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/.astro/content-assets.mjs +1 -0
  2. package/.astro/content-modules.mjs +1 -0
  3. package/.astro/content.d.ts +199 -0
  4. package/.astro/data-store.json +1 -0
  5. package/.astro/settings.json +5 -0
  6. package/.astro/types.d.ts +2 -0
  7. package/.prettierrc +15 -0
  8. package/.screenbook/coverage.json +37 -0
  9. package/.screenbook/screens.json +90 -0
  10. package/LICENSE +21 -0
  11. package/astro.config.mjs +18 -0
  12. package/dist/client/_astro/_baseUniq.BGai4mcc.js +1 -0
  13. package/dist/client/_astro/arc.DUp0dfXj.js +1 -0
  14. package/dist/client/_astro/architectureDiagram-VXUJARFQ.De_Gt-YC.js +36 -0
  15. package/dist/client/_astro/blockDiagram-VD42YOAC.BBt_VNhR.js +122 -0
  16. package/dist/client/_astro/c4Diagram-YG6GDRKO.DfgUlHvt.js +10 -0
  17. package/dist/client/_astro/channel.CNyr52v1.js +1 -0
  18. package/dist/client/_astro/chunk-4BX2VUAB.BL0ar6du.js +1 -0
  19. package/dist/client/_astro/chunk-55IACEB6.CI6SkZkY.js +1 -0
  20. package/dist/client/_astro/chunk-B4BG7PRW.Z25N80K6.js +165 -0
  21. package/dist/client/_astro/chunk-DI55MBZ5.BqjPVmi1.js +220 -0
  22. package/dist/client/_astro/chunk-FMBD7UC4.bZ9DWnFm.js +15 -0
  23. package/dist/client/_astro/chunk-QN33PNHL.BkzuUgWB.js +1 -0
  24. package/dist/client/_astro/chunk-QZHKN3VN.C__d68N_.js +1 -0
  25. package/dist/client/_astro/chunk-TZMSLE5B.BIpu8bMn.js +1 -0
  26. package/dist/client/_astro/classDiagram-2ON5EDUG.CxT3aW-h.js +1 -0
  27. package/dist/client/_astro/classDiagram-v2-WZHVMYZB.CxT3aW-h.js +1 -0
  28. package/dist/client/_astro/clone.U_lSZ6fe.js +1 -0
  29. package/dist/client/_astro/cose-bilkent-S5V4N54A.D48yfMll.js +1 -0
  30. package/dist/client/_astro/coverage.CKIVg4LY.css +1 -0
  31. package/dist/client/_astro/coverage.DDJMzKCq.css +1 -0
  32. package/dist/client/_astro/cytoscape.esm.DtBltrT8.js +331 -0
  33. package/dist/client/_astro/dagre-6UL2VRFP.LKVH7b30.js +4 -0
  34. package/dist/client/_astro/defaultLocale.C4B-KCzX.js +1 -0
  35. package/dist/client/_astro/diagram-PSM6KHXK.AHgUjH56.js +24 -0
  36. package/dist/client/_astro/diagram-QEK2KX5R.DvS33xWZ.js +43 -0
  37. package/dist/client/_astro/diagram-S2PKOQOG.BWisaYrQ.js +24 -0
  38. package/dist/client/_astro/erDiagram-Q2GNP2WA.B7oID6oT.js +60 -0
  39. package/dist/client/_astro/flowDiagram-NV44I4VS.Bb1qJLxr.js +162 -0
  40. package/dist/client/_astro/ganttDiagram-JELNMOA3.3vGHETyo.js +267 -0
  41. package/dist/client/_astro/gitGraphDiagram-NY62KEGX.Co2SKcif.js +65 -0
  42. package/dist/client/_astro/graph.B5fevUwB.js +1 -0
  43. package/dist/client/_astro/graph.astro_astro_type_script_index_0_lang.1HlATQ1g.js +1 -0
  44. package/dist/client/_astro/impact.Cvhl64u1.css +1 -0
  45. package/dist/client/_astro/impact.astro_astro_type_script_index_0_lang.D4cAR9AL.js +6 -0
  46. package/dist/client/_astro/infoDiagram-WHAUD3N6.B6ULtps1.js +2 -0
  47. package/dist/client/_astro/init.Gi6I4Gst.js +1 -0
  48. package/dist/client/_astro/journeyDiagram-XKPGCS4Q.BSOCzWmw.js +139 -0
  49. package/dist/client/_astro/kanban-definition-3W4ZIXB7.D8KKGc1o.js +89 -0
  50. package/dist/client/_astro/katex.XbL3y5x-.js +261 -0
  51. package/dist/client/_astro/layout.8vv24qEg.js +1 -0
  52. package/dist/client/_astro/linear.B6O9ymuK.js +1 -0
  53. package/dist/client/_astro/mermaid.core.CReXU7YN.js +256 -0
  54. package/dist/client/_astro/min.CdGMGVU0.js +1 -0
  55. package/dist/client/_astro/mindmap-definition-VGOIOE7T.G14HgtDw.js +68 -0
  56. package/dist/client/_astro/ordinal.BYWQX77i.js +1 -0
  57. package/dist/client/_astro/pieDiagram-ADFJNKIX.bC2VkyoW.js +30 -0
  58. package/dist/client/_astro/quadrantDiagram-AYHSOK5B.BlLaQQxO.js +7 -0
  59. package/dist/client/_astro/requirementDiagram-UZGBJVZJ.DHRnMofO.js +64 -0
  60. package/dist/client/_astro/sankeyDiagram-TZEHDZUN.BMuaJBmt.js +10 -0
  61. package/dist/client/_astro/sequenceDiagram-WL72ISMW.CnU62wqy.js +145 -0
  62. package/dist/client/_astro/stateDiagram-FKZM4ZOC.CewI55YO.js +1 -0
  63. package/dist/client/_astro/stateDiagram-v2-4FDKWEC3.7xUQqoNr.js +1 -0
  64. package/dist/client/_astro/timeline-definition-IT6M3QCI.D1PLRwss.js +61 -0
  65. package/dist/client/_astro/treemap-KMMF4GRG.D3VNVvXF.js +128 -0
  66. package/dist/client/_astro/xychartDiagram-PRI3JC2R.CQex0-ul.js +7 -0
  67. package/dist/client/logo.svg +5 -0
  68. package/dist/server/_@astrojs-ssr-adapter.mjs +1 -0
  69. package/dist/server/_noop-middleware.mjs +3 -0
  70. package/dist/server/chunks/_@astrojs-ssr-adapter_DgsYudHz.mjs +4385 -0
  71. package/dist/server/chunks/astro/server_m7yT4wCr.mjs +2787 -0
  72. package/dist/server/chunks/astro-designed-error-pages_BvPhMmw0.mjs +364 -0
  73. package/dist/server/chunks/fs-lite_COtHaKzy.mjs +157 -0
  74. package/dist/server/chunks/impactAnalysis_Bz5lMdmy.mjs +188 -0
  75. package/dist/server/chunks/loadScreens_DJf-tycc.mjs +39 -0
  76. package/dist/server/chunks/node_DoTkMCOi.mjs +1673 -0
  77. package/dist/server/chunks/remote_B3W5fv4r.mjs +188 -0
  78. package/dist/server/chunks/sharp_DHNfMLYY.mjs +99 -0
  79. package/dist/server/entry.mjs +47 -0
  80. package/dist/server/manifest_-V1HEnR8.mjs +101 -0
  81. package/dist/server/noop-entrypoint.mjs +3 -0
  82. package/dist/server/pages/_image.astro.mjs +2 -0
  83. package/dist/server/pages/coverage.astro.mjs +65 -0
  84. package/dist/server/pages/graph.astro.mjs +107 -0
  85. package/dist/server/pages/impact.astro.mjs +52 -0
  86. package/dist/server/pages/index.astro.mjs +28 -0
  87. package/dist/server/pages/screen/_id_.astro.mjs +52 -0
  88. package/dist/server/renderers.mjs +3 -0
  89. package/package.json +42 -0
  90. package/public/logo.svg +5 -0
  91. package/src/layouts/Layout.astro +60 -0
  92. package/src/pages/coverage.astro +399 -0
  93. package/src/pages/graph.astro +330 -0
  94. package/src/pages/impact.astro +459 -0
  95. package/src/pages/index.astro +167 -0
  96. package/src/pages/screen/[id].astro +186 -0
  97. package/src/styles/global.css +862 -0
  98. package/src/utils/impactAnalysis.ts +297 -0
  99. package/src/utils/loadCoverage.ts +30 -0
  100. package/src/utils/loadScreens.ts +18 -0
  101. package/tsconfig.json +9 -0
@@ -0,0 +1,4385 @@
1
+ import { v as decryptString, w as createSlotValueFromString, x as isAstroComponentFactory, k as renderComponent, r as renderTemplate, R as ROUTE_TYPE_HEADER, y as REROUTE_DIRECTIVE_HEADER, A as AstroError, z as i18nNoLocaleFoundInPath, B as ResponseSentError, C as ActionNotFoundError, D as MiddlewareNoDataOrNextCalled, G as MiddlewareNotAResponse, H as originPathnameSymbol, J as RewriteWithBodyUsed, K as GetStaticPathsRequired, O as InvalidGetStaticPathsReturn, P as InvalidGetStaticPathsEntry, Q as GetStaticPathsExpectedParams, S as GetStaticPathsInvalidRouteParam, T as PageNumberParamNotFound, V as DEFAULT_404_COMPONENT, W as NoMatchingStaticPathFound, X as PrerenderDynamicEndpointPathCollide, Y as ReservedSlotName, Z as renderSlotToString, _ as renderJSX, $ as chunkToString, a0 as isRenderInstruction, a1 as ForbiddenRewrite, a2 as SessionStorageInitError, a3 as SessionStorageSaveError, a4 as ASTRO_VERSION, a5 as CspNotEnabled, a6 as LocalsReassigned, a7 as generateCspDigest, a8 as PrerenderClientAddressNotAvailable, a9 as clientAddressSymbol, aa as ClientAddressNotAvailable, ab as StaticClientAddressNotAvailable, ac as AstroResponseHeadersReassigned, ad as responseSentSymbol$1, ae as renderPage, af as REWRITE_DIRECTIVE_HEADER_KEY, ag as REWRITE_DIRECTIVE_HEADER_VALUE, ah as renderEndpoint, ai as LocalsNotAnObject, aj as REROUTABLE_STATUS_CODES, ak as nodeRequestAbortControllerCleanupSymbol } from './astro/server_m7yT4wCr.mjs';
2
+ import colors from 'piccolore';
3
+ import 'clsx';
4
+ import { A as ActionError, d as deserializeActionResult, s as serializeActionResult, a as ACTION_RPC_ROUTE_PATTERN, b as ACTION_QUERY_PARAMS, g as getActionQueryString, D as DEFAULT_404_ROUTE, c as default404Instance, N as NOOP_MIDDLEWARE_FN, e as ensure404Route } from './astro-designed-error-pages_BvPhMmw0.mjs';
5
+ import 'es-module-lexer';
6
+ import buffer from 'node:buffer';
7
+ import crypto$1 from 'node:crypto';
8
+ import fs, { existsSync, readFileSync } from 'node:fs';
9
+ import { Http2ServerResponse } from 'node:http2';
10
+ import { c as appendForwardSlash$1, j as joinPaths, f as fileExtension, s as slash, p as prependForwardSlash$1, d as removeTrailingForwardSlash, t as trimSlashes, m as matchPattern, e as isInternalPath, g as collapseDuplicateTrailingSlashes, h as hasFileExtension } from './remote_B3W5fv4r.mjs';
11
+ import { serialize, parse } from 'cookie';
12
+ import { unflatten as unflatten$1, stringify as stringify$1 } from 'devalue';
13
+ import { createStorage, builtinDrivers } from 'unstorage';
14
+ import { AsyncLocalStorage } from 'node:async_hooks';
15
+ import http from 'node:http';
16
+ import https from 'node:https';
17
+ import enableDestroy from 'server-destroy';
18
+ import os from 'node:os';
19
+ import path from 'node:path';
20
+ import url from 'node:url';
21
+ import send from 'send';
22
+
23
+ function shouldAppendForwardSlash(trailingSlash, buildFormat) {
24
+ switch (trailingSlash) {
25
+ case "always":
26
+ return true;
27
+ case "never":
28
+ return false;
29
+ case "ignore": {
30
+ switch (buildFormat) {
31
+ case "directory":
32
+ return true;
33
+ case "preserve":
34
+ case "file":
35
+ return false;
36
+ }
37
+ }
38
+ }
39
+ }
40
+
41
+ function redirectIsExternal(redirect) {
42
+ if (typeof redirect === "string") {
43
+ return redirect.startsWith("http://") || redirect.startsWith("https://");
44
+ } else {
45
+ return redirect.destination.startsWith("http://") || redirect.destination.startsWith("https://");
46
+ }
47
+ }
48
+ async function renderRedirect(renderContext) {
49
+ const {
50
+ request: { method },
51
+ routeData
52
+ } = renderContext;
53
+ const { redirect, redirectRoute } = routeData;
54
+ const status = redirectRoute && typeof redirect === "object" ? redirect.status : method === "GET" ? 301 : 308;
55
+ const headers = { location: encodeURI(redirectRouteGenerate(renderContext)) };
56
+ if (redirect && redirectIsExternal(redirect)) {
57
+ if (typeof redirect === "string") {
58
+ return Response.redirect(redirect, status);
59
+ } else {
60
+ return Response.redirect(redirect.destination, status);
61
+ }
62
+ }
63
+ return new Response(null, { status, headers });
64
+ }
65
+ function redirectRouteGenerate(renderContext) {
66
+ const {
67
+ params,
68
+ routeData: { redirect, redirectRoute }
69
+ } = renderContext;
70
+ if (typeof redirectRoute !== "undefined") {
71
+ return redirectRoute?.generate(params) || redirectRoute?.pathname || "/";
72
+ } else if (typeof redirect === "string") {
73
+ if (redirectIsExternal(redirect)) {
74
+ return redirect;
75
+ } else {
76
+ let target = redirect;
77
+ for (const param of Object.keys(params)) {
78
+ const paramValue = params[param];
79
+ target = target.replace(`[${param}]`, paramValue).replace(`[...${param}]`, paramValue);
80
+ }
81
+ return target;
82
+ }
83
+ } else if (typeof redirect === "undefined") {
84
+ return "/";
85
+ }
86
+ return redirect.destination;
87
+ }
88
+
89
+ const SERVER_ISLAND_ROUTE = "/_server-islands/[name]";
90
+ const SERVER_ISLAND_COMPONENT = "_server-islands.astro";
91
+ const SERVER_ISLAND_BASE_PREFIX = "_server-islands";
92
+ function badRequest(reason) {
93
+ return new Response(null, {
94
+ status: 400,
95
+ statusText: "Bad request: " + reason
96
+ });
97
+ }
98
+ async function getRequestData(request) {
99
+ switch (request.method) {
100
+ case "GET": {
101
+ const url = new URL(request.url);
102
+ const params = url.searchParams;
103
+ if (!params.has("s") || !params.has("e") || !params.has("p")) {
104
+ return badRequest("Missing required query parameters.");
105
+ }
106
+ const encryptedSlots = params.get("s");
107
+ return {
108
+ componentExport: params.get("e"),
109
+ encryptedProps: params.get("p"),
110
+ encryptedSlots
111
+ };
112
+ }
113
+ case "POST": {
114
+ try {
115
+ const raw = await request.text();
116
+ const data = JSON.parse(raw);
117
+ if ("slots" in data && typeof data.slots === "object") {
118
+ return badRequest("Plaintext slots are not allowed. Slots must be encrypted.");
119
+ }
120
+ return data;
121
+ } catch (e) {
122
+ if (e instanceof SyntaxError) {
123
+ return badRequest("Request format is invalid.");
124
+ }
125
+ throw e;
126
+ }
127
+ }
128
+ default: {
129
+ return new Response(null, { status: 405 });
130
+ }
131
+ }
132
+ }
133
+ function createEndpoint(manifest) {
134
+ const page = async (result) => {
135
+ const params = result.params;
136
+ if (!params.name) {
137
+ return new Response(null, {
138
+ status: 400,
139
+ statusText: "Bad request"
140
+ });
141
+ }
142
+ const componentId = params.name;
143
+ const data = await getRequestData(result.request);
144
+ if (data instanceof Response) {
145
+ return data;
146
+ }
147
+ const imp = manifest.serverIslandMap?.get(componentId);
148
+ if (!imp) {
149
+ return new Response(null, {
150
+ status: 404,
151
+ statusText: "Not found"
152
+ });
153
+ }
154
+ const key = await manifest.key;
155
+ const encryptedProps = data.encryptedProps;
156
+ let props = {};
157
+ if (encryptedProps !== "") {
158
+ try {
159
+ const propString = await decryptString(key, encryptedProps);
160
+ props = JSON.parse(propString);
161
+ } catch (_e) {
162
+ return badRequest("Encrypted props value is invalid.");
163
+ }
164
+ }
165
+ let decryptedSlots = {};
166
+ const encryptedSlots = data.encryptedSlots;
167
+ if (encryptedSlots !== "") {
168
+ try {
169
+ const slotsString = await decryptString(key, encryptedSlots);
170
+ decryptedSlots = JSON.parse(slotsString);
171
+ } catch (_e) {
172
+ return badRequest("Encrypted slots value is invalid.");
173
+ }
174
+ }
175
+ const componentModule = await imp();
176
+ let Component = componentModule[data.componentExport];
177
+ const slots = {};
178
+ for (const prop in decryptedSlots) {
179
+ slots[prop] = createSlotValueFromString(decryptedSlots[prop]);
180
+ }
181
+ result.response.headers.set("X-Robots-Tag", "noindex");
182
+ if (isAstroComponentFactory(Component)) {
183
+ const ServerIsland = Component;
184
+ Component = function(...args) {
185
+ return ServerIsland.apply(this, args);
186
+ };
187
+ Object.assign(Component, ServerIsland);
188
+ Component.propagation = "self";
189
+ }
190
+ return renderTemplate`${renderComponent(result, "Component", Component, props, slots)}`;
191
+ };
192
+ page.isAstroComponentFactory = true;
193
+ const instance = {
194
+ default: page,
195
+ partial: true
196
+ };
197
+ return instance;
198
+ }
199
+
200
+ function matchRoute(pathname, manifest) {
201
+ return manifest.routes.find((route) => {
202
+ return route.pattern.test(pathname) || route.fallbackRoutes.some((fallbackRoute) => fallbackRoute.pattern.test(pathname));
203
+ });
204
+ }
205
+ const ROUTE404_RE = /^\/404\/?$/;
206
+ const ROUTE500_RE = /^\/500\/?$/;
207
+ function isRoute404(route) {
208
+ return ROUTE404_RE.test(route);
209
+ }
210
+ function isRoute500(route) {
211
+ return ROUTE500_RE.test(route);
212
+ }
213
+ function isRoute404or500(route) {
214
+ return isRoute404(route.route) || isRoute500(route.route);
215
+ }
216
+ function isRouteServerIsland(route) {
217
+ return route.component === SERVER_ISLAND_COMPONENT;
218
+ }
219
+ function isRequestServerIsland(request, base = "") {
220
+ const url = new URL(request.url);
221
+ const pathname = base === "/" ? url.pathname.slice(base.length) : url.pathname.slice(base.length + 1);
222
+ return pathname.startsWith(SERVER_ISLAND_BASE_PREFIX);
223
+ }
224
+ function requestIs404Or500(request, base = "") {
225
+ const url = new URL(request.url);
226
+ const pathname = url.pathname.slice(base.length);
227
+ return isRoute404(pathname) || isRoute500(pathname);
228
+ }
229
+ function isRouteExternalRedirect(route) {
230
+ return !!(route.type === "redirect" && route.redirect && redirectIsExternal(route.redirect));
231
+ }
232
+
233
+ function createI18nMiddleware(i18n, base, trailingSlash, format) {
234
+ if (!i18n) return (_, next) => next();
235
+ const payload = {
236
+ ...i18n,
237
+ trailingSlash,
238
+ base,
239
+ format};
240
+ const _redirectToDefaultLocale = redirectToDefaultLocale(payload);
241
+ const _noFoundForNonLocaleRoute = notFound(payload);
242
+ const _requestHasLocale = requestHasLocale(payload.locales);
243
+ const _redirectToFallback = redirectToFallback(payload);
244
+ const prefixAlways = (context, response) => {
245
+ const url = context.url;
246
+ if (url.pathname === base + "/" || url.pathname === base) {
247
+ return _redirectToDefaultLocale(context);
248
+ } else if (!_requestHasLocale(context)) {
249
+ return _noFoundForNonLocaleRoute(context, response);
250
+ }
251
+ return void 0;
252
+ };
253
+ const prefixOtherLocales = (context, response) => {
254
+ let pathnameContainsDefaultLocale = false;
255
+ const url = context.url;
256
+ for (const segment of url.pathname.split("/")) {
257
+ if (normalizeTheLocale(segment) === normalizeTheLocale(i18n.defaultLocale)) {
258
+ pathnameContainsDefaultLocale = true;
259
+ break;
260
+ }
261
+ }
262
+ if (pathnameContainsDefaultLocale) {
263
+ const newLocation = url.pathname.replace(`/${i18n.defaultLocale}`, "");
264
+ response.headers.set("Location", newLocation);
265
+ return _noFoundForNonLocaleRoute(context);
266
+ }
267
+ return void 0;
268
+ };
269
+ return async (context, next) => {
270
+ const response = await next();
271
+ const type = response.headers.get(ROUTE_TYPE_HEADER);
272
+ const isReroute = response.headers.get(REROUTE_DIRECTIVE_HEADER);
273
+ if (isReroute === "no" && typeof i18n.fallback === "undefined") {
274
+ return response;
275
+ }
276
+ if (type !== "page" && type !== "fallback") {
277
+ return response;
278
+ }
279
+ if (requestIs404Or500(context.request, base)) {
280
+ return response;
281
+ }
282
+ if (isRequestServerIsland(context.request, base)) {
283
+ return response;
284
+ }
285
+ const { currentLocale } = context;
286
+ switch (i18n.strategy) {
287
+ // NOTE: theoretically, we should never hit this code path
288
+ case "manual": {
289
+ return response;
290
+ }
291
+ case "domains-prefix-other-locales": {
292
+ if (localeHasntDomain(i18n, currentLocale)) {
293
+ const result = prefixOtherLocales(context, response);
294
+ if (result) {
295
+ return result;
296
+ }
297
+ }
298
+ break;
299
+ }
300
+ case "pathname-prefix-other-locales": {
301
+ const result = prefixOtherLocales(context, response);
302
+ if (result) {
303
+ return result;
304
+ }
305
+ break;
306
+ }
307
+ case "domains-prefix-always-no-redirect": {
308
+ if (localeHasntDomain(i18n, currentLocale)) {
309
+ const result = _noFoundForNonLocaleRoute(context, response);
310
+ if (result) {
311
+ return result;
312
+ }
313
+ }
314
+ break;
315
+ }
316
+ case "pathname-prefix-always-no-redirect": {
317
+ const result = _noFoundForNonLocaleRoute(context, response);
318
+ if (result) {
319
+ return result;
320
+ }
321
+ break;
322
+ }
323
+ case "pathname-prefix-always": {
324
+ const result = prefixAlways(context, response);
325
+ if (result) {
326
+ return result;
327
+ }
328
+ break;
329
+ }
330
+ case "domains-prefix-always": {
331
+ if (localeHasntDomain(i18n, currentLocale)) {
332
+ const result = prefixAlways(context, response);
333
+ if (result) {
334
+ return result;
335
+ }
336
+ }
337
+ break;
338
+ }
339
+ }
340
+ return _redirectToFallback(context, response);
341
+ };
342
+ }
343
+ function localeHasntDomain(i18n, currentLocale) {
344
+ for (const domainLocale of Object.values(i18n.domainLookupTable)) {
345
+ if (domainLocale === currentLocale) {
346
+ return false;
347
+ }
348
+ }
349
+ return true;
350
+ }
351
+
352
+ function requestHasLocale(locales) {
353
+ return function(context) {
354
+ return pathHasLocale(context.url.pathname, locales);
355
+ };
356
+ }
357
+ function pathHasLocale(path, locales) {
358
+ const segments = path.split("/").map(normalizeThePath);
359
+ for (const segment of segments) {
360
+ for (const locale of locales) {
361
+ if (typeof locale === "string") {
362
+ if (normalizeTheLocale(segment) === normalizeTheLocale(locale)) {
363
+ return true;
364
+ }
365
+ } else if (segment === locale.path) {
366
+ return true;
367
+ }
368
+ }
369
+ }
370
+ return false;
371
+ }
372
+ function getPathByLocale(locale, locales) {
373
+ for (const loopLocale of locales) {
374
+ if (typeof loopLocale === "string") {
375
+ if (loopLocale === locale) {
376
+ return loopLocale;
377
+ }
378
+ } else {
379
+ for (const code of loopLocale.codes) {
380
+ if (code === locale) {
381
+ return loopLocale.path;
382
+ }
383
+ }
384
+ }
385
+ }
386
+ throw new AstroError(i18nNoLocaleFoundInPath);
387
+ }
388
+ function normalizeTheLocale(locale) {
389
+ return locale.replaceAll("_", "-").toLowerCase();
390
+ }
391
+ function normalizeThePath(path) {
392
+ return path.endsWith(".html") ? path.slice(0, -5) : path;
393
+ }
394
+ function getAllCodes(locales) {
395
+ const result = [];
396
+ for (const loopLocale of locales) {
397
+ if (typeof loopLocale === "string") {
398
+ result.push(loopLocale);
399
+ } else {
400
+ result.push(...loopLocale.codes);
401
+ }
402
+ }
403
+ return result;
404
+ }
405
+ function redirectToDefaultLocale({
406
+ trailingSlash,
407
+ format,
408
+ base,
409
+ defaultLocale
410
+ }) {
411
+ return function(context, statusCode) {
412
+ if (shouldAppendForwardSlash(trailingSlash, format)) {
413
+ return context.redirect(`${appendForwardSlash$1(joinPaths(base, defaultLocale))}`, statusCode);
414
+ } else {
415
+ return context.redirect(`${joinPaths(base, defaultLocale)}`, statusCode);
416
+ }
417
+ };
418
+ }
419
+ function notFound({ base, locales, fallback }) {
420
+ return function(context, response) {
421
+ if (response?.headers.get(REROUTE_DIRECTIVE_HEADER) === "no" && typeof fallback === "undefined") {
422
+ return response;
423
+ }
424
+ const url = context.url;
425
+ const isRoot = url.pathname === base + "/" || url.pathname === base;
426
+ if (!(isRoot || pathHasLocale(url.pathname, locales))) {
427
+ if (response) {
428
+ response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
429
+ return new Response(response.body, {
430
+ status: 404,
431
+ headers: response.headers
432
+ });
433
+ } else {
434
+ return new Response(null, {
435
+ status: 404,
436
+ headers: {
437
+ [REROUTE_DIRECTIVE_HEADER]: "no"
438
+ }
439
+ });
440
+ }
441
+ }
442
+ return void 0;
443
+ };
444
+ }
445
+ function redirectToFallback({
446
+ fallback,
447
+ locales,
448
+ defaultLocale,
449
+ strategy,
450
+ base,
451
+ fallbackType
452
+ }) {
453
+ return async function(context, response) {
454
+ if (response.status >= 300 && fallback) {
455
+ const fallbackKeys = fallback ? Object.keys(fallback) : [];
456
+ const segments = context.url.pathname.split("/");
457
+ const urlLocale = segments.find((segment) => {
458
+ for (const locale of locales) {
459
+ if (typeof locale === "string") {
460
+ if (locale === segment) {
461
+ return true;
462
+ }
463
+ } else if (locale.path === segment) {
464
+ return true;
465
+ }
466
+ }
467
+ return false;
468
+ });
469
+ if (urlLocale && fallbackKeys.includes(urlLocale)) {
470
+ const fallbackLocale = fallback[urlLocale];
471
+ const pathFallbackLocale = getPathByLocale(fallbackLocale, locales);
472
+ let newPathname;
473
+ if (pathFallbackLocale === defaultLocale && strategy === "pathname-prefix-other-locales") {
474
+ if (context.url.pathname.includes(`${base}`)) {
475
+ newPathname = context.url.pathname.replace(`/${urlLocale}`, ``);
476
+ if (newPathname === "") {
477
+ newPathname = "/";
478
+ }
479
+ } else {
480
+ newPathname = context.url.pathname.replace(`/${urlLocale}`, `/`);
481
+ }
482
+ } else {
483
+ newPathname = context.url.pathname.replace(`/${urlLocale}`, `/${pathFallbackLocale}`);
484
+ }
485
+ if (fallbackType === "rewrite") {
486
+ return await context.rewrite(newPathname + context.url.search);
487
+ } else {
488
+ return context.redirect(newPathname + context.url.search);
489
+ }
490
+ }
491
+ }
492
+ return response;
493
+ };
494
+ }
495
+
496
+ const DELETED_EXPIRATION = /* @__PURE__ */ new Date(0);
497
+ const DELETED_VALUE = "deleted";
498
+ const responseSentSymbol = Symbol.for("astro.responseSent");
499
+ const identity = (value) => value;
500
+ class AstroCookie {
501
+ constructor(value) {
502
+ this.value = value;
503
+ }
504
+ json() {
505
+ if (this.value === void 0) {
506
+ throw new Error(`Cannot convert undefined to an object.`);
507
+ }
508
+ return JSON.parse(this.value);
509
+ }
510
+ number() {
511
+ return Number(this.value);
512
+ }
513
+ boolean() {
514
+ if (this.value === "false") return false;
515
+ if (this.value === "0") return false;
516
+ return Boolean(this.value);
517
+ }
518
+ }
519
+ class AstroCookies {
520
+ #request;
521
+ #requestValues;
522
+ #outgoing;
523
+ #consumed;
524
+ constructor(request) {
525
+ this.#request = request;
526
+ this.#requestValues = null;
527
+ this.#outgoing = null;
528
+ this.#consumed = false;
529
+ }
530
+ /**
531
+ * Astro.cookies.delete(key) is used to delete a cookie. Using this method will result
532
+ * in a Set-Cookie header added to the response.
533
+ * @param key The cookie to delete
534
+ * @param options Options related to this deletion, such as the path of the cookie.
535
+ */
536
+ delete(key, options) {
537
+ const {
538
+ // @ts-expect-error
539
+ maxAge: _ignoredMaxAge,
540
+ // @ts-expect-error
541
+ expires: _ignoredExpires,
542
+ ...sanitizedOptions
543
+ } = options || {};
544
+ const serializeOptions = {
545
+ expires: DELETED_EXPIRATION,
546
+ ...sanitizedOptions
547
+ };
548
+ this.#ensureOutgoingMap().set(key, [
549
+ DELETED_VALUE,
550
+ serialize(key, DELETED_VALUE, serializeOptions),
551
+ false
552
+ ]);
553
+ }
554
+ /**
555
+ * Astro.cookies.get(key) is used to get a cookie value. The cookie value is read from the
556
+ * request. If you have set a cookie via Astro.cookies.set(key, value), the value will be taken
557
+ * from that set call, overriding any values already part of the request.
558
+ * @param key The cookie to get.
559
+ * @returns An object containing the cookie value as well as convenience methods for converting its value.
560
+ */
561
+ get(key, options = void 0) {
562
+ if (this.#outgoing?.has(key)) {
563
+ let [serializedValue, , isSetValue] = this.#outgoing.get(key);
564
+ if (isSetValue) {
565
+ return new AstroCookie(serializedValue);
566
+ } else {
567
+ return void 0;
568
+ }
569
+ }
570
+ const decode = options?.decode ?? decodeURIComponent;
571
+ const values = this.#ensureParsed();
572
+ if (key in values) {
573
+ const value = values[key];
574
+ if (value) {
575
+ let decodedValue;
576
+ try {
577
+ decodedValue = decode(value);
578
+ } catch (_error) {
579
+ decodedValue = value;
580
+ }
581
+ return new AstroCookie(decodedValue);
582
+ }
583
+ }
584
+ }
585
+ /**
586
+ * Astro.cookies.has(key) returns a boolean indicating whether this cookie is either
587
+ * part of the initial request or set via Astro.cookies.set(key)
588
+ * @param key The cookie to check for.
589
+ * @param _options This parameter is no longer used.
590
+ * @returns
591
+ */
592
+ has(key, _options) {
593
+ if (this.#outgoing?.has(key)) {
594
+ let [, , isSetValue] = this.#outgoing.get(key);
595
+ return isSetValue;
596
+ }
597
+ const values = this.#ensureParsed();
598
+ return values[key] !== void 0;
599
+ }
600
+ /**
601
+ * Astro.cookies.set(key, value) is used to set a cookie's value. If provided
602
+ * an object it will be stringified via JSON.stringify(value). Additionally you
603
+ * can provide options customizing how this cookie will be set, such as setting httpOnly
604
+ * in order to prevent the cookie from being read in client-side JavaScript.
605
+ * @param key The name of the cookie to set.
606
+ * @param value A value, either a string or other primitive or an object.
607
+ * @param options Options for the cookie, such as the path and security settings.
608
+ */
609
+ set(key, value, options) {
610
+ if (this.#consumed) {
611
+ const warning = new Error(
612
+ "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."
613
+ );
614
+ warning.name = "Warning";
615
+ console.warn(warning);
616
+ }
617
+ let serializedValue;
618
+ if (typeof value === "string") {
619
+ serializedValue = value;
620
+ } else {
621
+ let toStringValue = value.toString();
622
+ if (toStringValue === Object.prototype.toString.call(value)) {
623
+ serializedValue = JSON.stringify(value);
624
+ } else {
625
+ serializedValue = toStringValue;
626
+ }
627
+ }
628
+ const serializeOptions = {};
629
+ if (options) {
630
+ Object.assign(serializeOptions, options);
631
+ }
632
+ this.#ensureOutgoingMap().set(key, [
633
+ serializedValue,
634
+ serialize(key, serializedValue, serializeOptions),
635
+ true
636
+ ]);
637
+ if (this.#request[responseSentSymbol]) {
638
+ throw new AstroError({
639
+ ...ResponseSentError
640
+ });
641
+ }
642
+ }
643
+ /**
644
+ * Merges a new AstroCookies instance into the current instance. Any new cookies
645
+ * will be added to the current instance, overwriting any existing cookies with the same name.
646
+ */
647
+ merge(cookies) {
648
+ const outgoing = cookies.#outgoing;
649
+ if (outgoing) {
650
+ for (const [key, value] of outgoing) {
651
+ this.#ensureOutgoingMap().set(key, value);
652
+ }
653
+ }
654
+ }
655
+ /**
656
+ * Astro.cookies.header() returns an iterator for the cookies that have previously
657
+ * been set by either Astro.cookies.set() or Astro.cookies.delete().
658
+ * This method is primarily used by adapters to set the header on outgoing responses.
659
+ * @returns
660
+ */
661
+ *headers() {
662
+ if (this.#outgoing == null) return;
663
+ for (const [, value] of this.#outgoing) {
664
+ yield value[1];
665
+ }
666
+ }
667
+ /**
668
+ * Behaves the same as AstroCookies.prototype.headers(),
669
+ * but allows a warning when cookies are set after the instance is consumed.
670
+ */
671
+ static consume(cookies) {
672
+ cookies.#consumed = true;
673
+ return cookies.headers();
674
+ }
675
+ #ensureParsed() {
676
+ if (!this.#requestValues) {
677
+ this.#parse();
678
+ }
679
+ if (!this.#requestValues) {
680
+ this.#requestValues = {};
681
+ }
682
+ return this.#requestValues;
683
+ }
684
+ #ensureOutgoingMap() {
685
+ if (!this.#outgoing) {
686
+ this.#outgoing = /* @__PURE__ */ new Map();
687
+ }
688
+ return this.#outgoing;
689
+ }
690
+ #parse() {
691
+ const raw = this.#request.headers.get("cookie");
692
+ if (!raw) {
693
+ return;
694
+ }
695
+ this.#requestValues = parse(raw, { decode: identity });
696
+ }
697
+ }
698
+
699
+ const astroCookiesSymbol = Symbol.for("astro.cookies");
700
+ function attachCookiesToResponse(response, cookies) {
701
+ Reflect.set(response, astroCookiesSymbol, cookies);
702
+ }
703
+ function getCookiesFromResponse(response) {
704
+ let cookies = Reflect.get(response, astroCookiesSymbol);
705
+ if (cookies != null) {
706
+ return cookies;
707
+ } else {
708
+ return void 0;
709
+ }
710
+ }
711
+ function* getSetCookiesFromResponse(response) {
712
+ const cookies = getCookiesFromResponse(response);
713
+ if (!cookies) {
714
+ return [];
715
+ }
716
+ for (const headerValue of AstroCookies.consume(cookies)) {
717
+ yield headerValue;
718
+ }
719
+ return [];
720
+ }
721
+
722
+ const dateTimeFormat = new Intl.DateTimeFormat([], {
723
+ hour: "2-digit",
724
+ minute: "2-digit",
725
+ second: "2-digit",
726
+ hour12: false
727
+ });
728
+ const levels = {
729
+ debug: 20,
730
+ info: 30,
731
+ warn: 40,
732
+ error: 50,
733
+ silent: 90
734
+ };
735
+ function log(opts, level, label, message, newLine = true) {
736
+ const logLevel = opts.level;
737
+ const dest = opts.dest;
738
+ const event = {
739
+ label,
740
+ level,
741
+ message,
742
+ newLine
743
+ };
744
+ if (!isLogLevelEnabled(logLevel, level)) {
745
+ return;
746
+ }
747
+ dest.write(event);
748
+ }
749
+ function isLogLevelEnabled(configuredLogLevel, level) {
750
+ return levels[configuredLogLevel] <= levels[level];
751
+ }
752
+ function info(opts, label, message, newLine = true) {
753
+ return log(opts, "info", label, message, newLine);
754
+ }
755
+ function warn(opts, label, message, newLine = true) {
756
+ return log(opts, "warn", label, message, newLine);
757
+ }
758
+ function error(opts, label, message, newLine = true) {
759
+ return log(opts, "error", label, message, newLine);
760
+ }
761
+ function debug(...args) {
762
+ if ("_astroGlobalDebug" in globalThis) {
763
+ globalThis._astroGlobalDebug(...args);
764
+ }
765
+ }
766
+ function getEventPrefix({ level, label }) {
767
+ const timestamp = `${dateTimeFormat.format(/* @__PURE__ */ new Date())}`;
768
+ const prefix = [];
769
+ if (level === "error" || level === "warn") {
770
+ prefix.push(colors.bold(timestamp));
771
+ prefix.push(`[${level.toUpperCase()}]`);
772
+ } else {
773
+ prefix.push(timestamp);
774
+ }
775
+ if (label) {
776
+ prefix.push(`[${label}]`);
777
+ }
778
+ if (level === "error") {
779
+ return colors.red(prefix.join(" "));
780
+ }
781
+ if (level === "warn") {
782
+ return colors.yellow(prefix.join(" "));
783
+ }
784
+ if (prefix.length === 1) {
785
+ return colors.dim(prefix[0]);
786
+ }
787
+ return colors.dim(prefix[0]) + " " + colors.blue(prefix.splice(1).join(" "));
788
+ }
789
+ class Logger {
790
+ options;
791
+ constructor(options) {
792
+ this.options = options;
793
+ }
794
+ info(label, message, newLine = true) {
795
+ info(this.options, label, message, newLine);
796
+ }
797
+ warn(label, message, newLine = true) {
798
+ warn(this.options, label, message, newLine);
799
+ }
800
+ error(label, message, newLine = true) {
801
+ error(this.options, label, message, newLine);
802
+ }
803
+ debug(label, ...messages) {
804
+ debug(label, ...messages);
805
+ }
806
+ level() {
807
+ return this.options.level;
808
+ }
809
+ forkIntegrationLogger(label) {
810
+ return new AstroIntegrationLogger(this.options, label);
811
+ }
812
+ }
813
+ class AstroIntegrationLogger {
814
+ options;
815
+ label;
816
+ constructor(logging, label) {
817
+ this.options = logging;
818
+ this.label = label;
819
+ }
820
+ /**
821
+ * Creates a new logger instance with a new label, but the same log options.
822
+ */
823
+ fork(label) {
824
+ return new AstroIntegrationLogger(this.options, label);
825
+ }
826
+ info(message) {
827
+ info(this.options, this.label, message);
828
+ }
829
+ warn(message) {
830
+ warn(this.options, this.label, message);
831
+ }
832
+ error(message) {
833
+ error(this.options, this.label, message);
834
+ }
835
+ debug(message) {
836
+ debug(this.label, message);
837
+ }
838
+ }
839
+
840
+ const consoleLogDestination = {
841
+ write(event) {
842
+ let dest = console.error;
843
+ if (levels[event.level] < levels["error"]) {
844
+ dest = console.info;
845
+ }
846
+ if (event.label === "SKIP_FORMAT") {
847
+ dest(event.message);
848
+ } else {
849
+ dest(getEventPrefix(event) + " " + event.message);
850
+ }
851
+ return true;
852
+ }
853
+ };
854
+
855
+ function getAssetsPrefix(fileExtension, assetsPrefix) {
856
+ let prefix = "";
857
+ if (!assetsPrefix) {
858
+ prefix = "";
859
+ } else if (typeof assetsPrefix === "string") {
860
+ prefix = assetsPrefix;
861
+ } else {
862
+ const dotLessFileExtension = fileExtension.slice(1);
863
+ prefix = assetsPrefix[dotLessFileExtension] || assetsPrefix.fallback;
864
+ }
865
+ return prefix;
866
+ }
867
+
868
+ function createAssetLink(href, base, assetsPrefix, queryParams) {
869
+ let url = "";
870
+ if (assetsPrefix) {
871
+ const pf = getAssetsPrefix(fileExtension(href), assetsPrefix);
872
+ url = joinPaths(pf, slash(href));
873
+ } else if (base) {
874
+ url = prependForwardSlash$1(joinPaths(base, slash(href)));
875
+ } else {
876
+ url = href;
877
+ }
878
+ return url;
879
+ }
880
+ function createStylesheetElement(stylesheet, base, assetsPrefix, queryParams) {
881
+ if (stylesheet.type === "inline") {
882
+ return {
883
+ props: {},
884
+ children: stylesheet.content
885
+ };
886
+ } else {
887
+ return {
888
+ props: {
889
+ rel: "stylesheet",
890
+ href: createAssetLink(stylesheet.src, base, assetsPrefix)
891
+ },
892
+ children: ""
893
+ };
894
+ }
895
+ }
896
+ function createStylesheetElementSet(stylesheets, base, assetsPrefix, queryParams) {
897
+ return new Set(
898
+ stylesheets.map((s) => createStylesheetElement(s, base, assetsPrefix))
899
+ );
900
+ }
901
+ function createModuleScriptElement(script, base, assetsPrefix, queryParams) {
902
+ if (script.type === "external") {
903
+ return createModuleScriptElementWithSrc(script.value, base, assetsPrefix);
904
+ } else {
905
+ return {
906
+ props: {
907
+ type: "module"
908
+ },
909
+ children: script.value
910
+ };
911
+ }
912
+ }
913
+ function createModuleScriptElementWithSrc(src, base, assetsPrefix, queryParams) {
914
+ return {
915
+ props: {
916
+ type: "module",
917
+ src: createAssetLink(src, base, assetsPrefix)
918
+ },
919
+ children: ""
920
+ };
921
+ }
922
+
923
+ const ACTION_API_CONTEXT_SYMBOL = Symbol.for("astro.actionAPIContext");
924
+ const formContentTypes = ["application/x-www-form-urlencoded", "multipart/form-data"];
925
+ function hasContentType(contentType, expected) {
926
+ const type = contentType.split(";")[0].toLowerCase();
927
+ return expected.some((t) => type === t);
928
+ }
929
+
930
+ function getActionContext(context) {
931
+ const callerInfo = getCallerInfo(context);
932
+ const actionResultAlreadySet = Boolean(context.locals._actionPayload);
933
+ let action = void 0;
934
+ if (callerInfo && context.request.method === "POST" && !actionResultAlreadySet) {
935
+ action = {
936
+ calledFrom: callerInfo.from,
937
+ name: callerInfo.name,
938
+ handler: async () => {
939
+ const pipeline = Reflect.get(context, apiContextRoutesSymbol);
940
+ const callerInfoName = shouldAppendForwardSlash(
941
+ pipeline.manifest.trailingSlash,
942
+ pipeline.manifest.buildFormat
943
+ ) ? removeTrailingForwardSlash(callerInfo.name) : callerInfo.name;
944
+ let baseAction;
945
+ try {
946
+ baseAction = await pipeline.getAction(callerInfoName);
947
+ } catch (error) {
948
+ if (error instanceof Error && "name" in error && typeof error.name === "string" && error.name === ActionNotFoundError.name) {
949
+ return { data: void 0, error: new ActionError({ code: "NOT_FOUND" }) };
950
+ }
951
+ throw error;
952
+ }
953
+ let input;
954
+ try {
955
+ input = await parseRequestBody(context.request);
956
+ } catch (e) {
957
+ if (e instanceof TypeError) {
958
+ return { data: void 0, error: new ActionError({ code: "UNSUPPORTED_MEDIA_TYPE" }) };
959
+ }
960
+ throw e;
961
+ }
962
+ const omitKeys = ["props", "getActionResult", "callAction", "redirect"];
963
+ const actionAPIContext = Object.create(
964
+ Object.getPrototypeOf(context),
965
+ Object.fromEntries(
966
+ Object.entries(Object.getOwnPropertyDescriptors(context)).filter(
967
+ ([key]) => !omitKeys.includes(key)
968
+ )
969
+ )
970
+ );
971
+ Reflect.set(actionAPIContext, ACTION_API_CONTEXT_SYMBOL, true);
972
+ const handler = baseAction.bind(actionAPIContext);
973
+ return handler(input);
974
+ }
975
+ };
976
+ }
977
+ function setActionResult(actionName, actionResult) {
978
+ context.locals._actionPayload = {
979
+ actionResult,
980
+ actionName
981
+ };
982
+ }
983
+ return {
984
+ action,
985
+ setActionResult,
986
+ serializeActionResult,
987
+ deserializeActionResult
988
+ };
989
+ }
990
+ function getCallerInfo(ctx) {
991
+ if (ctx.routePattern === ACTION_RPC_ROUTE_PATTERN) {
992
+ return { from: "rpc", name: ctx.url.pathname.replace(/^.*\/_actions\//, "") };
993
+ }
994
+ const queryParam = ctx.url.searchParams.get(ACTION_QUERY_PARAMS.actionName);
995
+ if (queryParam) {
996
+ return { from: "form", name: queryParam };
997
+ }
998
+ return void 0;
999
+ }
1000
+ async function parseRequestBody(request) {
1001
+ const contentType = request.headers.get("content-type");
1002
+ const contentLength = request.headers.get("Content-Length");
1003
+ if (!contentType) return void 0;
1004
+ if (hasContentType(contentType, formContentTypes)) {
1005
+ return await request.clone().formData();
1006
+ }
1007
+ if (hasContentType(contentType, ["application/json"])) {
1008
+ return contentLength === "0" ? void 0 : await request.clone().json();
1009
+ }
1010
+ throw new TypeError("Unsupported content type");
1011
+ }
1012
+
1013
+ function hasActionPayload(locals) {
1014
+ return "_actionPayload" in locals;
1015
+ }
1016
+ function createGetActionResult(locals) {
1017
+ return (actionFn) => {
1018
+ if (!hasActionPayload(locals) || actionFn.toString() !== getActionQueryString(locals._actionPayload.actionName)) {
1019
+ return void 0;
1020
+ }
1021
+ return deserializeActionResult(locals._actionPayload.actionResult);
1022
+ };
1023
+ }
1024
+ function createCallAction(context) {
1025
+ return (baseAction, input) => {
1026
+ Reflect.set(context, ACTION_API_CONTEXT_SYMBOL, true);
1027
+ const action = baseAction.bind(context);
1028
+ return action(input);
1029
+ };
1030
+ }
1031
+
1032
+ function parseLocale(header) {
1033
+ if (header === "*") {
1034
+ return [{ locale: header, qualityValue: void 0 }];
1035
+ }
1036
+ const result = [];
1037
+ const localeValues = header.split(",").map((str) => str.trim());
1038
+ for (const localeValue of localeValues) {
1039
+ const split = localeValue.split(";").map((str) => str.trim());
1040
+ const localeName = split[0];
1041
+ const qualityValue = split[1];
1042
+ if (!split) {
1043
+ continue;
1044
+ }
1045
+ if (qualityValue && qualityValue.startsWith("q=")) {
1046
+ const qualityValueAsFloat = Number.parseFloat(qualityValue.slice("q=".length));
1047
+ if (Number.isNaN(qualityValueAsFloat) || qualityValueAsFloat > 1) {
1048
+ result.push({
1049
+ locale: localeName,
1050
+ qualityValue: void 0
1051
+ });
1052
+ } else {
1053
+ result.push({
1054
+ locale: localeName,
1055
+ qualityValue: qualityValueAsFloat
1056
+ });
1057
+ }
1058
+ } else {
1059
+ result.push({
1060
+ locale: localeName,
1061
+ qualityValue: void 0
1062
+ });
1063
+ }
1064
+ }
1065
+ return result;
1066
+ }
1067
+ function sortAndFilterLocales(browserLocaleList, locales) {
1068
+ const normalizedLocales = getAllCodes(locales).map(normalizeTheLocale);
1069
+ return browserLocaleList.filter((browserLocale) => {
1070
+ if (browserLocale.locale !== "*") {
1071
+ return normalizedLocales.includes(normalizeTheLocale(browserLocale.locale));
1072
+ }
1073
+ return true;
1074
+ }).sort((a, b) => {
1075
+ if (a.qualityValue && b.qualityValue) {
1076
+ return Math.sign(b.qualityValue - a.qualityValue);
1077
+ }
1078
+ return 0;
1079
+ });
1080
+ }
1081
+ function computePreferredLocale(request, locales) {
1082
+ const acceptHeader = request.headers.get("Accept-Language");
1083
+ let result = void 0;
1084
+ if (acceptHeader) {
1085
+ const browserLocaleList = sortAndFilterLocales(parseLocale(acceptHeader), locales);
1086
+ const firstResult = browserLocaleList.at(0);
1087
+ if (firstResult && firstResult.locale !== "*") {
1088
+ for (const currentLocale of locales) {
1089
+ if (typeof currentLocale === "string") {
1090
+ if (normalizeTheLocale(currentLocale) === normalizeTheLocale(firstResult.locale)) {
1091
+ result = currentLocale;
1092
+ break;
1093
+ }
1094
+ } else {
1095
+ for (const currentCode of currentLocale.codes) {
1096
+ if (normalizeTheLocale(currentCode) === normalizeTheLocale(firstResult.locale)) {
1097
+ result = currentCode;
1098
+ break;
1099
+ }
1100
+ }
1101
+ }
1102
+ }
1103
+ }
1104
+ }
1105
+ return result;
1106
+ }
1107
+ function computePreferredLocaleList(request, locales) {
1108
+ const acceptHeader = request.headers.get("Accept-Language");
1109
+ let result = [];
1110
+ if (acceptHeader) {
1111
+ const browserLocaleList = sortAndFilterLocales(parseLocale(acceptHeader), locales);
1112
+ if (browserLocaleList.length === 1 && browserLocaleList.at(0).locale === "*") {
1113
+ return getAllCodes(locales);
1114
+ } else if (browserLocaleList.length > 0) {
1115
+ for (const browserLocale of browserLocaleList) {
1116
+ for (const loopLocale of locales) {
1117
+ if (typeof loopLocale === "string") {
1118
+ if (normalizeTheLocale(loopLocale) === normalizeTheLocale(browserLocale.locale)) {
1119
+ result.push(loopLocale);
1120
+ }
1121
+ } else {
1122
+ for (const code of loopLocale.codes) {
1123
+ if (code === browserLocale.locale) {
1124
+ result.push(code);
1125
+ }
1126
+ }
1127
+ }
1128
+ }
1129
+ }
1130
+ }
1131
+ }
1132
+ return result;
1133
+ }
1134
+ function computeCurrentLocale(pathname, locales, defaultLocale) {
1135
+ for (const segment of pathname.split("/").map(normalizeThePath)) {
1136
+ for (const locale of locales) {
1137
+ if (typeof locale === "string") {
1138
+ if (!segment.includes(locale)) continue;
1139
+ if (normalizeTheLocale(locale) === normalizeTheLocale(segment)) {
1140
+ return locale;
1141
+ }
1142
+ } else {
1143
+ if (locale.path === segment) {
1144
+ return locale.codes.at(0);
1145
+ } else {
1146
+ for (const code of locale.codes) {
1147
+ if (normalizeTheLocale(code) === normalizeTheLocale(segment)) {
1148
+ return code;
1149
+ }
1150
+ }
1151
+ }
1152
+ }
1153
+ }
1154
+ }
1155
+ for (const locale of locales) {
1156
+ if (typeof locale === "string") {
1157
+ if (locale === defaultLocale) {
1158
+ return locale;
1159
+ }
1160
+ } else {
1161
+ if (locale.path === defaultLocale) {
1162
+ return locale.codes.at(0);
1163
+ }
1164
+ }
1165
+ }
1166
+ }
1167
+
1168
+ function deduplicateDirectiveValues(existingDirective, newDirective) {
1169
+ const [directiveName, ...existingValues] = existingDirective.split(/\s+/).filter(Boolean);
1170
+ const [newDirectiveName, ...newValues] = newDirective.split(/\s+/).filter(Boolean);
1171
+ if (directiveName !== newDirectiveName) {
1172
+ return void 0;
1173
+ }
1174
+ const finalDirectives = Array.from(/* @__PURE__ */ new Set([...existingValues, ...newValues]));
1175
+ return `${directiveName} ${finalDirectives.join(" ")}`;
1176
+ }
1177
+ function pushDirective(directives, newDirective) {
1178
+ let deduplicated = false;
1179
+ if (directives.length === 0) {
1180
+ return [newDirective];
1181
+ }
1182
+ const finalDirectives = [];
1183
+ for (const directive of directives) {
1184
+ if (deduplicated) {
1185
+ finalDirectives.push(directive);
1186
+ continue;
1187
+ }
1188
+ const result = deduplicateDirectiveValues(directive, newDirective);
1189
+ if (result) {
1190
+ finalDirectives.push(result);
1191
+ deduplicated = true;
1192
+ } else {
1193
+ finalDirectives.push(directive);
1194
+ finalDirectives.push(newDirective);
1195
+ }
1196
+ }
1197
+ return finalDirectives;
1198
+ }
1199
+
1200
+ async function callMiddleware(onRequest, apiContext, responseFunction) {
1201
+ let nextCalled = false;
1202
+ let responseFunctionPromise = void 0;
1203
+ const next = async (payload) => {
1204
+ nextCalled = true;
1205
+ responseFunctionPromise = responseFunction(apiContext, payload);
1206
+ return responseFunctionPromise;
1207
+ };
1208
+ let middlewarePromise = onRequest(apiContext, next);
1209
+ return await Promise.resolve(middlewarePromise).then(async (value) => {
1210
+ if (nextCalled) {
1211
+ if (typeof value !== "undefined") {
1212
+ if (value instanceof Response === false) {
1213
+ throw new AstroError(MiddlewareNotAResponse);
1214
+ }
1215
+ return value;
1216
+ } else {
1217
+ if (responseFunctionPromise) {
1218
+ return responseFunctionPromise;
1219
+ } else {
1220
+ throw new AstroError(MiddlewareNotAResponse);
1221
+ }
1222
+ }
1223
+ } else if (typeof value === "undefined") {
1224
+ throw new AstroError(MiddlewareNoDataOrNextCalled);
1225
+ } else if (value instanceof Response === false) {
1226
+ throw new AstroError(MiddlewareNotAResponse);
1227
+ } else {
1228
+ return value;
1229
+ }
1230
+ });
1231
+ }
1232
+
1233
+ function createRequest({
1234
+ url,
1235
+ headers,
1236
+ method = "GET",
1237
+ body = void 0,
1238
+ logger,
1239
+ isPrerendered = false,
1240
+ routePattern,
1241
+ init
1242
+ }) {
1243
+ const headersObj = isPrerendered ? void 0 : headers instanceof Headers ? headers : new Headers(
1244
+ // Filter out HTTP/2 pseudo-headers. These are internally-generated headers added to all HTTP/2 requests with trusted metadata about the request.
1245
+ // Examples include `:method`, `:scheme`, `:authority`, and `:path`.
1246
+ // They are always prefixed with a colon to distinguish them from other headers, and it is an error to add the to a Headers object manually.
1247
+ // See https://httpwg.org/specs/rfc7540.html#HttpRequest
1248
+ Object.entries(headers).filter(([name]) => !name.startsWith(":"))
1249
+ );
1250
+ if (typeof url === "string") url = new URL(url);
1251
+ if (isPrerendered) {
1252
+ url.search = "";
1253
+ }
1254
+ const request = new Request(url, {
1255
+ method,
1256
+ headers: headersObj,
1257
+ // body is made available only if the request is for a page that will be on-demand rendered
1258
+ body: isPrerendered ? null : body,
1259
+ ...init
1260
+ });
1261
+ if (isPrerendered) {
1262
+ let _headers = request.headers;
1263
+ const { value, writable, ...headersDesc } = Object.getOwnPropertyDescriptor(request, "headers") || {};
1264
+ Object.defineProperty(request, "headers", {
1265
+ ...headersDesc,
1266
+ get() {
1267
+ logger.warn(
1268
+ null,
1269
+ `\`Astro.request.headers\` was used when rendering the route \`${routePattern}'\`. \`Astro.request.headers\` is not available on prerendered pages. If you need access to request headers, make sure that the page is server-rendered using \`export const prerender = false;\` or by setting \`output\` to \`"server"\` in your Astro config to make all your pages server-rendered by default.`
1270
+ );
1271
+ return _headers;
1272
+ },
1273
+ set(newHeaders) {
1274
+ _headers = newHeaders;
1275
+ }
1276
+ });
1277
+ }
1278
+ return request;
1279
+ }
1280
+
1281
+ function findRouteToRewrite({
1282
+ payload,
1283
+ routes,
1284
+ request,
1285
+ trailingSlash,
1286
+ buildFormat,
1287
+ base,
1288
+ outDir
1289
+ }) {
1290
+ let newUrl = void 0;
1291
+ if (payload instanceof URL) {
1292
+ newUrl = payload;
1293
+ } else if (payload instanceof Request) {
1294
+ newUrl = new URL(payload.url);
1295
+ } else {
1296
+ newUrl = new URL(payload, new URL(request.url).origin);
1297
+ }
1298
+ let pathname = newUrl.pathname;
1299
+ const shouldAppendSlash = shouldAppendForwardSlash(trailingSlash, buildFormat);
1300
+ if (base !== "/") {
1301
+ const isBasePathRequest = newUrl.pathname === base || newUrl.pathname === removeTrailingForwardSlash(base);
1302
+ if (isBasePathRequest) {
1303
+ pathname = shouldAppendSlash ? "/" : "";
1304
+ } else if (newUrl.pathname.startsWith(base)) {
1305
+ pathname = shouldAppendSlash ? appendForwardSlash$1(newUrl.pathname) : removeTrailingForwardSlash(newUrl.pathname);
1306
+ pathname = pathname.slice(base.length);
1307
+ }
1308
+ }
1309
+ if (!pathname.startsWith("/") && shouldAppendSlash && newUrl.pathname.endsWith("/")) {
1310
+ pathname = prependForwardSlash$1(pathname);
1311
+ }
1312
+ if (pathname === "/" && base !== "/" && !shouldAppendSlash) {
1313
+ pathname = "";
1314
+ }
1315
+ if (buildFormat === "file") {
1316
+ pathname = pathname.replace(/\.html$/, "");
1317
+ }
1318
+ if (base !== "/" && (pathname === "" || pathname === "/") && !shouldAppendSlash) {
1319
+ newUrl.pathname = removeTrailingForwardSlash(base);
1320
+ } else {
1321
+ newUrl.pathname = joinPaths(...[base, pathname].filter(Boolean));
1322
+ }
1323
+ const decodedPathname = decodeURI(pathname);
1324
+ let foundRoute;
1325
+ for (const route of routes) {
1326
+ if (route.pattern.test(decodedPathname)) {
1327
+ if (route.params && route.params.length !== 0 && route.distURL && route.distURL.length !== 0) {
1328
+ if (!route.distURL.find(
1329
+ (url) => url.href.replace(outDir.toString(), "").replace(/(?:\/index\.html|\.html)$/, "") == trimSlashes(decodedPathname)
1330
+ )) {
1331
+ continue;
1332
+ }
1333
+ }
1334
+ foundRoute = route;
1335
+ break;
1336
+ }
1337
+ }
1338
+ if (foundRoute) {
1339
+ return {
1340
+ routeData: foundRoute,
1341
+ newUrl,
1342
+ pathname: decodedPathname
1343
+ };
1344
+ } else {
1345
+ const custom404 = routes.find((route) => route.route === "/404");
1346
+ if (custom404) {
1347
+ return { routeData: custom404, newUrl, pathname };
1348
+ } else {
1349
+ return { routeData: DEFAULT_404_ROUTE, newUrl, pathname };
1350
+ }
1351
+ }
1352
+ }
1353
+ function copyRequest(newUrl, oldRequest, isPrerendered, logger, routePattern) {
1354
+ if (oldRequest.bodyUsed) {
1355
+ throw new AstroError(RewriteWithBodyUsed);
1356
+ }
1357
+ return createRequest({
1358
+ url: newUrl,
1359
+ method: oldRequest.method,
1360
+ body: oldRequest.body,
1361
+ isPrerendered,
1362
+ logger,
1363
+ headers: isPrerendered ? {} : oldRequest.headers,
1364
+ routePattern,
1365
+ init: {
1366
+ referrer: oldRequest.referrer,
1367
+ referrerPolicy: oldRequest.referrerPolicy,
1368
+ mode: oldRequest.mode,
1369
+ credentials: oldRequest.credentials,
1370
+ cache: oldRequest.cache,
1371
+ redirect: oldRequest.redirect,
1372
+ integrity: oldRequest.integrity,
1373
+ signal: oldRequest.signal,
1374
+ keepalive: oldRequest.keepalive,
1375
+ // https://fetch.spec.whatwg.org/#dom-request-duplex
1376
+ // @ts-expect-error It isn't part of the types, but undici accepts it and it allows to carry over the body to a new request
1377
+ duplex: "half"
1378
+ }
1379
+ });
1380
+ }
1381
+ function setOriginPathname(request, pathname, trailingSlash, buildFormat) {
1382
+ if (!pathname) {
1383
+ pathname = "/";
1384
+ }
1385
+ const shouldAppendSlash = shouldAppendForwardSlash(trailingSlash, buildFormat);
1386
+ let finalPathname;
1387
+ if (pathname === "/") {
1388
+ finalPathname = "/";
1389
+ } else if (shouldAppendSlash) {
1390
+ finalPathname = appendForwardSlash$1(pathname);
1391
+ } else {
1392
+ finalPathname = removeTrailingForwardSlash(pathname);
1393
+ }
1394
+ Reflect.set(request, originPathnameSymbol, encodeURIComponent(finalPathname));
1395
+ }
1396
+ function getOriginPathname(request) {
1397
+ const origin = Reflect.get(request, originPathnameSymbol);
1398
+ if (origin) {
1399
+ return decodeURIComponent(origin);
1400
+ }
1401
+ return new URL(request.url).pathname;
1402
+ }
1403
+
1404
+ const NOOP_ACTIONS_MOD = {
1405
+ server: {}
1406
+ };
1407
+
1408
+ const FORM_CONTENT_TYPES = [
1409
+ "application/x-www-form-urlencoded",
1410
+ "multipart/form-data",
1411
+ "text/plain"
1412
+ ];
1413
+ const SAFE_METHODS = ["GET", "HEAD", "OPTIONS"];
1414
+ function createOriginCheckMiddleware() {
1415
+ return defineMiddleware((context, next) => {
1416
+ const { request, url, isPrerendered } = context;
1417
+ if (isPrerendered) {
1418
+ return next();
1419
+ }
1420
+ if (SAFE_METHODS.includes(request.method)) {
1421
+ return next();
1422
+ }
1423
+ const isSameOrigin = request.headers.get("origin") === url.origin;
1424
+ const hasContentType = request.headers.has("content-type");
1425
+ if (hasContentType) {
1426
+ const formLikeHeader = hasFormLikeHeader(request.headers.get("content-type"));
1427
+ if (formLikeHeader && !isSameOrigin) {
1428
+ return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
1429
+ status: 403
1430
+ });
1431
+ }
1432
+ } else {
1433
+ if (!isSameOrigin) {
1434
+ return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
1435
+ status: 403
1436
+ });
1437
+ }
1438
+ }
1439
+ return next();
1440
+ });
1441
+ }
1442
+ function hasFormLikeHeader(contentType) {
1443
+ if (contentType) {
1444
+ for (const FORM_CONTENT_TYPE of FORM_CONTENT_TYPES) {
1445
+ if (contentType.toLowerCase().includes(FORM_CONTENT_TYPE)) {
1446
+ return true;
1447
+ }
1448
+ }
1449
+ }
1450
+ return false;
1451
+ }
1452
+
1453
+ const VALID_PARAM_TYPES = ["string", "number", "undefined"];
1454
+ function validateGetStaticPathsParameter([key, value], route) {
1455
+ if (!VALID_PARAM_TYPES.includes(typeof value)) {
1456
+ throw new AstroError({
1457
+ ...GetStaticPathsInvalidRouteParam,
1458
+ message: GetStaticPathsInvalidRouteParam.message(key, value, typeof value),
1459
+ location: {
1460
+ file: route
1461
+ }
1462
+ });
1463
+ }
1464
+ }
1465
+ function validateDynamicRouteModule(mod, {
1466
+ ssr,
1467
+ route
1468
+ }) {
1469
+ if ((!ssr || route.prerender) && !mod.getStaticPaths) {
1470
+ throw new AstroError({
1471
+ ...GetStaticPathsRequired,
1472
+ location: { file: route.component }
1473
+ });
1474
+ }
1475
+ }
1476
+ function validateGetStaticPathsResult(result, logger, route) {
1477
+ if (!Array.isArray(result)) {
1478
+ throw new AstroError({
1479
+ ...InvalidGetStaticPathsReturn,
1480
+ message: InvalidGetStaticPathsReturn.message(typeof result),
1481
+ location: {
1482
+ file: route.component
1483
+ }
1484
+ });
1485
+ }
1486
+ result.forEach((pathObject) => {
1487
+ if (typeof pathObject === "object" && Array.isArray(pathObject) || pathObject === null) {
1488
+ throw new AstroError({
1489
+ ...InvalidGetStaticPathsEntry,
1490
+ message: InvalidGetStaticPathsEntry.message(
1491
+ Array.isArray(pathObject) ? "array" : typeof pathObject
1492
+ )
1493
+ });
1494
+ }
1495
+ if (pathObject.params === void 0 || pathObject.params === null || pathObject.params && Object.keys(pathObject.params).length === 0) {
1496
+ throw new AstroError({
1497
+ ...GetStaticPathsExpectedParams,
1498
+ location: {
1499
+ file: route.component
1500
+ }
1501
+ });
1502
+ }
1503
+ for (const [key, val] of Object.entries(pathObject.params)) {
1504
+ if (!(typeof val === "undefined" || typeof val === "string" || typeof val === "number")) {
1505
+ logger.warn(
1506
+ "router",
1507
+ `getStaticPaths() returned an invalid path param: "${key}". A string, number or undefined value was expected, but got \`${JSON.stringify(
1508
+ val
1509
+ )}\`.`
1510
+ );
1511
+ }
1512
+ if (typeof val === "string" && val === "") {
1513
+ logger.warn(
1514
+ "router",
1515
+ `getStaticPaths() returned an invalid path param: "${key}". \`undefined\` expected for an optional param, but got empty string.`
1516
+ );
1517
+ }
1518
+ }
1519
+ });
1520
+ }
1521
+
1522
+ function stringifyParams(params, route) {
1523
+ const validatedParams = Object.entries(params).reduce((acc, next) => {
1524
+ validateGetStaticPathsParameter(next, route.component);
1525
+ const [key, value] = next;
1526
+ if (value !== void 0) {
1527
+ acc[key] = typeof value === "string" ? trimSlashes(value) : value.toString();
1528
+ }
1529
+ return acc;
1530
+ }, {});
1531
+ return route.generate(validatedParams);
1532
+ }
1533
+
1534
+ function generatePaginateFunction(routeMatch, base) {
1535
+ return function paginateUtility(data, args = {}) {
1536
+ let { pageSize: _pageSize, params: _params, props: _props } = args;
1537
+ const pageSize = _pageSize || 10;
1538
+ const paramName = "page";
1539
+ const additionalParams = _params || {};
1540
+ const additionalProps = _props || {};
1541
+ let includesFirstPageNumber;
1542
+ if (routeMatch.params.includes(`...${paramName}`)) {
1543
+ includesFirstPageNumber = false;
1544
+ } else if (routeMatch.params.includes(`${paramName}`)) {
1545
+ includesFirstPageNumber = true;
1546
+ } else {
1547
+ throw new AstroError({
1548
+ ...PageNumberParamNotFound,
1549
+ message: PageNumberParamNotFound.message(paramName)
1550
+ });
1551
+ }
1552
+ const lastPage = Math.max(1, Math.ceil(data.length / pageSize));
1553
+ const result = [...Array(lastPage).keys()].map((num) => {
1554
+ const pageNum = num + 1;
1555
+ const start = pageSize === Infinity ? 0 : (pageNum - 1) * pageSize;
1556
+ const end = Math.min(start + pageSize, data.length);
1557
+ const params = {
1558
+ ...additionalParams,
1559
+ [paramName]: includesFirstPageNumber || pageNum > 1 ? String(pageNum) : void 0
1560
+ };
1561
+ const current = addRouteBase(routeMatch.generate({ ...params }), base);
1562
+ const next = pageNum === lastPage ? void 0 : addRouteBase(routeMatch.generate({ ...params, page: String(pageNum + 1) }), base);
1563
+ const prev = pageNum === 1 ? void 0 : addRouteBase(
1564
+ routeMatch.generate({
1565
+ ...params,
1566
+ page: !includesFirstPageNumber && pageNum - 1 === 1 ? void 0 : String(pageNum - 1)
1567
+ }),
1568
+ base
1569
+ );
1570
+ const first = pageNum === 1 ? void 0 : addRouteBase(
1571
+ routeMatch.generate({
1572
+ ...params,
1573
+ page: includesFirstPageNumber ? "1" : void 0
1574
+ }),
1575
+ base
1576
+ );
1577
+ const last = pageNum === lastPage ? void 0 : addRouteBase(routeMatch.generate({ ...params, page: String(lastPage) }), base);
1578
+ return {
1579
+ params,
1580
+ props: {
1581
+ ...additionalProps,
1582
+ page: {
1583
+ data: data.slice(start, end),
1584
+ start,
1585
+ end: end - 1,
1586
+ size: pageSize,
1587
+ total: data.length,
1588
+ currentPage: pageNum,
1589
+ lastPage,
1590
+ url: { current, next, prev, first, last }
1591
+ }
1592
+ }
1593
+ };
1594
+ });
1595
+ return result;
1596
+ };
1597
+ }
1598
+ function addRouteBase(route, base) {
1599
+ let routeWithBase = joinPaths(base, route);
1600
+ if (routeWithBase === "") routeWithBase = "/";
1601
+ return routeWithBase;
1602
+ }
1603
+
1604
+ async function callGetStaticPaths({
1605
+ mod,
1606
+ route,
1607
+ routeCache,
1608
+ logger,
1609
+ ssr,
1610
+ base
1611
+ }) {
1612
+ const cached = routeCache.get(route);
1613
+ if (!mod) {
1614
+ throw new Error("This is an error caused by Astro and not your code. Please file an issue.");
1615
+ }
1616
+ if (cached?.staticPaths) {
1617
+ return cached.staticPaths;
1618
+ }
1619
+ validateDynamicRouteModule(mod, { ssr, route });
1620
+ if (ssr && !route.prerender) {
1621
+ const entry = Object.assign([], { keyed: /* @__PURE__ */ new Map() });
1622
+ routeCache.set(route, { ...cached, staticPaths: entry });
1623
+ return entry;
1624
+ }
1625
+ let staticPaths = [];
1626
+ if (!mod.getStaticPaths) {
1627
+ throw new Error("Unexpected Error.");
1628
+ }
1629
+ staticPaths = await mod.getStaticPaths({
1630
+ // Q: Why the cast?
1631
+ // A: So users downstream can have nicer typings, we have to make some sacrifice in our internal typings, which necessitate a cast here
1632
+ paginate: generatePaginateFunction(route, base),
1633
+ routePattern: route.route
1634
+ });
1635
+ validateGetStaticPathsResult(staticPaths, logger, route);
1636
+ const keyedStaticPaths = staticPaths;
1637
+ keyedStaticPaths.keyed = /* @__PURE__ */ new Map();
1638
+ for (const sp of keyedStaticPaths) {
1639
+ const paramsKey = stringifyParams(sp.params, route);
1640
+ keyedStaticPaths.keyed.set(paramsKey, sp);
1641
+ }
1642
+ routeCache.set(route, { ...cached, staticPaths: keyedStaticPaths });
1643
+ return keyedStaticPaths;
1644
+ }
1645
+ class RouteCache {
1646
+ logger;
1647
+ cache = {};
1648
+ runtimeMode;
1649
+ constructor(logger, runtimeMode = "production") {
1650
+ this.logger = logger;
1651
+ this.runtimeMode = runtimeMode;
1652
+ }
1653
+ /** Clear the cache. */
1654
+ clearAll() {
1655
+ this.cache = {};
1656
+ }
1657
+ set(route, entry) {
1658
+ const key = this.key(route);
1659
+ if (this.runtimeMode === "production" && this.cache[key]?.staticPaths) {
1660
+ this.logger.warn(null, `Internal Warning: route cache overwritten. (${key})`);
1661
+ }
1662
+ this.cache[key] = entry;
1663
+ }
1664
+ get(route) {
1665
+ return this.cache[this.key(route)];
1666
+ }
1667
+ key(route) {
1668
+ return `${route.route}_${route.component}`;
1669
+ }
1670
+ }
1671
+ function findPathItemByKey(staticPaths, params, route, logger) {
1672
+ const paramsKey = stringifyParams(params, route);
1673
+ const matchedStaticPath = staticPaths.keyed.get(paramsKey);
1674
+ if (matchedStaticPath) {
1675
+ return matchedStaticPath;
1676
+ }
1677
+ logger.debug("router", `findPathItemByKey() - Unexpected cache miss looking for ${paramsKey}`);
1678
+ }
1679
+
1680
+ function createDefaultRoutes(manifest) {
1681
+ const root = new URL(manifest.hrefRoot);
1682
+ return [
1683
+ {
1684
+ instance: default404Instance,
1685
+ matchesComponent: (filePath) => filePath.href === new URL(DEFAULT_404_COMPONENT, root).href,
1686
+ route: DEFAULT_404_ROUTE.route,
1687
+ component: DEFAULT_404_COMPONENT
1688
+ },
1689
+ {
1690
+ instance: createEndpoint(manifest),
1691
+ matchesComponent: (filePath) => filePath.href === new URL(SERVER_ISLAND_COMPONENT, root).href,
1692
+ route: SERVER_ISLAND_ROUTE,
1693
+ component: SERVER_ISLAND_COMPONENT
1694
+ }
1695
+ ];
1696
+ }
1697
+
1698
+ class Pipeline {
1699
+ constructor(logger, manifest, runtimeMode, renderers, resolve, serverLike, streaming, adapterName = manifest.adapterName, clientDirectives = manifest.clientDirectives, inlinedScripts = manifest.inlinedScripts, compressHTML = manifest.compressHTML, i18n = manifest.i18n, middleware = manifest.middleware, routeCache = new RouteCache(logger, runtimeMode), site = manifest.site ? new URL(manifest.site) : void 0, defaultRoutes = createDefaultRoutes(manifest), actions = manifest.actions) {
1700
+ this.logger = logger;
1701
+ this.manifest = manifest;
1702
+ this.runtimeMode = runtimeMode;
1703
+ this.renderers = renderers;
1704
+ this.resolve = resolve;
1705
+ this.serverLike = serverLike;
1706
+ this.streaming = streaming;
1707
+ this.adapterName = adapterName;
1708
+ this.clientDirectives = clientDirectives;
1709
+ this.inlinedScripts = inlinedScripts;
1710
+ this.compressHTML = compressHTML;
1711
+ this.i18n = i18n;
1712
+ this.middleware = middleware;
1713
+ this.routeCache = routeCache;
1714
+ this.site = site;
1715
+ this.defaultRoutes = defaultRoutes;
1716
+ this.actions = actions;
1717
+ this.internalMiddleware = [];
1718
+ if (i18n?.strategy !== "manual") {
1719
+ this.internalMiddleware.push(
1720
+ createI18nMiddleware(i18n, manifest.base, manifest.trailingSlash, manifest.buildFormat)
1721
+ );
1722
+ }
1723
+ }
1724
+ internalMiddleware;
1725
+ resolvedMiddleware = void 0;
1726
+ resolvedActions = void 0;
1727
+ /**
1728
+ * Resolves the middleware from the manifest, and returns the `onRequest` function. If `onRequest` isn't there,
1729
+ * it returns a no-op function
1730
+ */
1731
+ async getMiddleware() {
1732
+ if (this.resolvedMiddleware) {
1733
+ return this.resolvedMiddleware;
1734
+ } else if (this.middleware) {
1735
+ const middlewareInstance = await this.middleware();
1736
+ const onRequest = middlewareInstance.onRequest ?? NOOP_MIDDLEWARE_FN;
1737
+ const internalMiddlewares = [onRequest];
1738
+ if (this.manifest.checkOrigin) {
1739
+ internalMiddlewares.unshift(createOriginCheckMiddleware());
1740
+ }
1741
+ this.resolvedMiddleware = sequence(...internalMiddlewares);
1742
+ return this.resolvedMiddleware;
1743
+ } else {
1744
+ this.resolvedMiddleware = NOOP_MIDDLEWARE_FN;
1745
+ return this.resolvedMiddleware;
1746
+ }
1747
+ }
1748
+ setActions(actions) {
1749
+ this.resolvedActions = actions;
1750
+ }
1751
+ async getActions() {
1752
+ if (this.resolvedActions) {
1753
+ return this.resolvedActions;
1754
+ } else if (this.actions) {
1755
+ return await this.actions();
1756
+ }
1757
+ return NOOP_ACTIONS_MOD;
1758
+ }
1759
+ async getAction(path) {
1760
+ const pathKeys = path.split(".").map((key) => decodeURIComponent(key));
1761
+ let { server } = await this.getActions();
1762
+ if (!server || !(typeof server === "object")) {
1763
+ throw new TypeError(
1764
+ `Expected \`server\` export in actions file to be an object. Received ${typeof server}.`
1765
+ );
1766
+ }
1767
+ for (const key of pathKeys) {
1768
+ if (!(key in server)) {
1769
+ throw new AstroError({
1770
+ ...ActionNotFoundError,
1771
+ message: ActionNotFoundError.message(pathKeys.join("."))
1772
+ });
1773
+ }
1774
+ server = server[key];
1775
+ }
1776
+ if (typeof server !== "function") {
1777
+ throw new TypeError(
1778
+ `Expected handler for action ${pathKeys.join(".")} to be a function. Received ${typeof server}.`
1779
+ );
1780
+ }
1781
+ return server;
1782
+ }
1783
+ }
1784
+
1785
+ function routeIsRedirect(route) {
1786
+ return route?.type === "redirect";
1787
+ }
1788
+ function routeIsFallback(route) {
1789
+ return route?.type === "fallback";
1790
+ }
1791
+
1792
+ const RedirectComponentInstance = {
1793
+ default() {
1794
+ return new Response(null, {
1795
+ status: 301
1796
+ });
1797
+ }
1798
+ };
1799
+ const RedirectSinglePageBuiltModule = {
1800
+ page: () => Promise.resolve(RedirectComponentInstance),
1801
+ onRequest: (_, next) => next(),
1802
+ renderers: []
1803
+ };
1804
+
1805
+ async function getProps(opts) {
1806
+ const { logger, mod, routeData: route, routeCache, pathname, serverLike, base } = opts;
1807
+ if (!route || route.pathname) {
1808
+ return {};
1809
+ }
1810
+ if (routeIsRedirect(route) || routeIsFallback(route) || route.component === DEFAULT_404_COMPONENT) {
1811
+ return {};
1812
+ }
1813
+ const staticPaths = await callGetStaticPaths({
1814
+ mod,
1815
+ route,
1816
+ routeCache,
1817
+ logger,
1818
+ ssr: serverLike,
1819
+ base
1820
+ });
1821
+ const params = getParams(route, pathname);
1822
+ const matchedStaticPath = findPathItemByKey(staticPaths, params, route, logger);
1823
+ if (!matchedStaticPath && (serverLike ? route.prerender : true)) {
1824
+ throw new AstroError({
1825
+ ...NoMatchingStaticPathFound,
1826
+ message: NoMatchingStaticPathFound.message(pathname),
1827
+ hint: NoMatchingStaticPathFound.hint([route.component])
1828
+ });
1829
+ }
1830
+ if (mod) {
1831
+ validatePrerenderEndpointCollision(route, mod, params);
1832
+ }
1833
+ const props = matchedStaticPath?.props ? { ...matchedStaticPath.props } : {};
1834
+ return props;
1835
+ }
1836
+ function getParams(route, pathname) {
1837
+ if (!route.params.length) return {};
1838
+ const paramsMatch = route.pattern.exec(pathname) || route.fallbackRoutes.map((fallbackRoute) => fallbackRoute.pattern.exec(pathname)).find((x) => x);
1839
+ if (!paramsMatch) return {};
1840
+ const params = {};
1841
+ route.params.forEach((key, i) => {
1842
+ if (key.startsWith("...")) {
1843
+ params[key.slice(3)] = paramsMatch[i + 1] ? paramsMatch[i + 1] : void 0;
1844
+ } else {
1845
+ params[key] = paramsMatch[i + 1];
1846
+ }
1847
+ });
1848
+ return params;
1849
+ }
1850
+ function validatePrerenderEndpointCollision(route, mod, params) {
1851
+ if (route.type === "endpoint" && mod.getStaticPaths) {
1852
+ const lastSegment = route.segments[route.segments.length - 1];
1853
+ const paramValues = Object.values(params);
1854
+ const lastParam = paramValues[paramValues.length - 1];
1855
+ if (lastSegment.length === 1 && lastSegment[0].dynamic && lastParam === void 0) {
1856
+ throw new AstroError({
1857
+ ...PrerenderDynamicEndpointPathCollide,
1858
+ message: PrerenderDynamicEndpointPathCollide.message(route.route),
1859
+ hint: PrerenderDynamicEndpointPathCollide.hint(route.component),
1860
+ location: {
1861
+ file: route.component
1862
+ }
1863
+ });
1864
+ }
1865
+ }
1866
+ }
1867
+
1868
+ function getFunctionExpression(slot) {
1869
+ if (!slot) return;
1870
+ const expressions = slot?.expressions?.filter((e) => isRenderInstruction(e) === false);
1871
+ if (expressions?.length !== 1) return;
1872
+ return expressions[0];
1873
+ }
1874
+ class Slots {
1875
+ #result;
1876
+ #slots;
1877
+ #logger;
1878
+ constructor(result, slots, logger) {
1879
+ this.#result = result;
1880
+ this.#slots = slots;
1881
+ this.#logger = logger;
1882
+ if (slots) {
1883
+ for (const key of Object.keys(slots)) {
1884
+ if (this[key] !== void 0) {
1885
+ throw new AstroError({
1886
+ ...ReservedSlotName,
1887
+ message: ReservedSlotName.message(key)
1888
+ });
1889
+ }
1890
+ Object.defineProperty(this, key, {
1891
+ get() {
1892
+ return true;
1893
+ },
1894
+ enumerable: true
1895
+ });
1896
+ }
1897
+ }
1898
+ }
1899
+ has(name) {
1900
+ if (!this.#slots) return false;
1901
+ return Boolean(this.#slots[name]);
1902
+ }
1903
+ async render(name, args = []) {
1904
+ if (!this.#slots || !this.has(name)) return;
1905
+ const result = this.#result;
1906
+ if (!Array.isArray(args)) {
1907
+ this.#logger.warn(
1908
+ null,
1909
+ `Expected second parameter to be an array, received a ${typeof args}. If you're trying to pass an array as a single argument and getting unexpected results, make sure you're passing your array as a item of an array. Ex: Astro.slots.render('default', [["Hello", "World"]])`
1910
+ );
1911
+ } else if (args.length > 0) {
1912
+ const slotValue = this.#slots[name];
1913
+ const component = typeof slotValue === "function" ? await slotValue(result) : await slotValue;
1914
+ const expression = getFunctionExpression(component);
1915
+ if (expression) {
1916
+ const slot = async () => typeof expression === "function" ? expression(...args) : expression;
1917
+ return await renderSlotToString(result, slot).then((res) => {
1918
+ return res;
1919
+ });
1920
+ }
1921
+ if (typeof component === "function") {
1922
+ return await renderJSX(result, component(...args)).then(
1923
+ (res) => res != null ? String(res) : res
1924
+ );
1925
+ }
1926
+ }
1927
+ const content = await renderSlotToString(result, this.#slots[name]);
1928
+ const outHTML = chunkToString(result, content);
1929
+ return outHTML;
1930
+ }
1931
+ }
1932
+
1933
+ function sequence(...handlers) {
1934
+ const filtered = handlers.filter((h) => !!h);
1935
+ const length = filtered.length;
1936
+ if (!length) {
1937
+ return defineMiddleware((_context, next) => {
1938
+ return next();
1939
+ });
1940
+ }
1941
+ return defineMiddleware((context, next) => {
1942
+ let carriedPayload = void 0;
1943
+ return applyHandle(0, context);
1944
+ function applyHandle(i, handleContext) {
1945
+ const handle = filtered[i];
1946
+ const result = handle(handleContext, async (payload) => {
1947
+ if (i < length - 1) {
1948
+ if (payload) {
1949
+ let newRequest;
1950
+ if (payload instanceof Request) {
1951
+ newRequest = payload;
1952
+ } else if (payload instanceof URL) {
1953
+ newRequest = new Request(payload, handleContext.request.clone());
1954
+ } else {
1955
+ newRequest = new Request(
1956
+ new URL(payload, handleContext.url.origin),
1957
+ handleContext.request.clone()
1958
+ );
1959
+ }
1960
+ const oldPathname = handleContext.url.pathname;
1961
+ const pipeline = Reflect.get(handleContext, apiContextRoutesSymbol);
1962
+ const { routeData, pathname } = await pipeline.tryRewrite(
1963
+ payload,
1964
+ handleContext.request
1965
+ );
1966
+ if (pipeline.serverLike === true && handleContext.isPrerendered === false && routeData.prerender === true) {
1967
+ throw new AstroError({
1968
+ ...ForbiddenRewrite,
1969
+ message: ForbiddenRewrite.message(
1970
+ handleContext.url.pathname,
1971
+ pathname,
1972
+ routeData.component
1973
+ ),
1974
+ hint: ForbiddenRewrite.hint(routeData.component)
1975
+ });
1976
+ }
1977
+ carriedPayload = payload;
1978
+ handleContext.request = newRequest;
1979
+ handleContext.url = new URL(newRequest.url);
1980
+ handleContext.params = getParams(routeData, pathname);
1981
+ handleContext.routePattern = routeData.route;
1982
+ setOriginPathname(
1983
+ handleContext.request,
1984
+ oldPathname,
1985
+ pipeline.manifest.trailingSlash,
1986
+ pipeline.manifest.buildFormat
1987
+ );
1988
+ }
1989
+ return applyHandle(i + 1, handleContext);
1990
+ } else {
1991
+ return next(payload ?? carriedPayload);
1992
+ }
1993
+ });
1994
+ return result;
1995
+ }
1996
+ });
1997
+ }
1998
+
1999
+ function defineMiddleware(fn) {
2000
+ return fn;
2001
+ }
2002
+
2003
+ const PERSIST_SYMBOL = Symbol();
2004
+ const DEFAULT_COOKIE_NAME = "astro-session";
2005
+ const VALID_COOKIE_REGEX = /^[\w-]+$/;
2006
+ const unflatten = (parsed, _) => {
2007
+ return unflatten$1(parsed, {
2008
+ URL: (href) => new URL(href)
2009
+ });
2010
+ };
2011
+ const stringify = (data, _) => {
2012
+ return stringify$1(data, {
2013
+ // Support URL objects
2014
+ URL: (val) => val instanceof URL && val.href
2015
+ });
2016
+ };
2017
+ class AstroSession {
2018
+ // The cookies object.
2019
+ #cookies;
2020
+ // The session configuration.
2021
+ #config;
2022
+ // The cookie config
2023
+ #cookieConfig;
2024
+ // The cookie name
2025
+ #cookieName;
2026
+ // The unstorage object for the session driver.
2027
+ #storage;
2028
+ #data;
2029
+ // The session ID. A v4 UUID.
2030
+ #sessionID;
2031
+ // Sessions to destroy. Needed because we won't have the old session ID after it's destroyed locally.
2032
+ #toDestroy = /* @__PURE__ */ new Set();
2033
+ // Session keys to delete. Used for partial data sets to avoid overwriting the deleted value.
2034
+ #toDelete = /* @__PURE__ */ new Set();
2035
+ // Whether the session is dirty and needs to be saved.
2036
+ #dirty = false;
2037
+ // Whether the session cookie has been set.
2038
+ #cookieSet = false;
2039
+ // The local data is "partial" if it has not been loaded from storage yet and only
2040
+ // contains values that have been set or deleted in-memory locally.
2041
+ // We do this to avoid the need to block on loading data when it is only being set.
2042
+ // When we load the data from storage, we need to merge it with the local partial data,
2043
+ // preserving in-memory changes and deletions.
2044
+ #partial = true;
2045
+ static #sharedStorage = /* @__PURE__ */ new Map();
2046
+ constructor(cookies, {
2047
+ cookie: cookieConfig = DEFAULT_COOKIE_NAME,
2048
+ ...config
2049
+ }, runtimeMode) {
2050
+ const { driver } = config;
2051
+ if (!driver) {
2052
+ throw new AstroError({
2053
+ ...SessionStorageInitError,
2054
+ message: SessionStorageInitError.message(
2055
+ "No driver was defined in the session configuration and the adapter did not provide a default driver."
2056
+ )
2057
+ });
2058
+ }
2059
+ this.#cookies = cookies;
2060
+ let cookieConfigObject;
2061
+ if (typeof cookieConfig === "object") {
2062
+ const { name = DEFAULT_COOKIE_NAME, ...rest } = cookieConfig;
2063
+ this.#cookieName = name;
2064
+ cookieConfigObject = rest;
2065
+ } else {
2066
+ this.#cookieName = cookieConfig || DEFAULT_COOKIE_NAME;
2067
+ }
2068
+ this.#cookieConfig = {
2069
+ sameSite: "lax",
2070
+ secure: runtimeMode === "production",
2071
+ path: "/",
2072
+ ...cookieConfigObject,
2073
+ httpOnly: true
2074
+ };
2075
+ this.#config = { ...config, driver };
2076
+ }
2077
+ /**
2078
+ * Gets a session value. Returns `undefined` if the session or value does not exist.
2079
+ */
2080
+ async get(key) {
2081
+ return (await this.#ensureData()).get(key)?.data;
2082
+ }
2083
+ /**
2084
+ * Checks if a session value exists.
2085
+ */
2086
+ async has(key) {
2087
+ return (await this.#ensureData()).has(key);
2088
+ }
2089
+ /**
2090
+ * Gets all session values.
2091
+ */
2092
+ async keys() {
2093
+ return (await this.#ensureData()).keys();
2094
+ }
2095
+ /**
2096
+ * Gets all session values.
2097
+ */
2098
+ async values() {
2099
+ return [...(await this.#ensureData()).values()].map((entry) => entry.data);
2100
+ }
2101
+ /**
2102
+ * Gets all session entries.
2103
+ */
2104
+ async entries() {
2105
+ return [...(await this.#ensureData()).entries()].map(([key, entry]) => [key, entry.data]);
2106
+ }
2107
+ /**
2108
+ * Deletes a session value.
2109
+ */
2110
+ delete(key) {
2111
+ this.#data?.delete(key);
2112
+ if (this.#partial) {
2113
+ this.#toDelete.add(key);
2114
+ }
2115
+ this.#dirty = true;
2116
+ }
2117
+ /**
2118
+ * Sets a session value. The session is created if it does not exist.
2119
+ */
2120
+ set(key, value, { ttl } = {}) {
2121
+ if (!key) {
2122
+ throw new AstroError({
2123
+ ...SessionStorageSaveError,
2124
+ message: "The session key was not provided."
2125
+ });
2126
+ }
2127
+ let cloned;
2128
+ try {
2129
+ cloned = unflatten(JSON.parse(stringify(value)));
2130
+ } catch (err) {
2131
+ throw new AstroError(
2132
+ {
2133
+ ...SessionStorageSaveError,
2134
+ message: `The session data for ${key} could not be serialized.`,
2135
+ hint: "See the devalue library for all supported types: https://github.com/rich-harris/devalue"
2136
+ },
2137
+ { cause: err }
2138
+ );
2139
+ }
2140
+ if (!this.#cookieSet) {
2141
+ this.#setCookie();
2142
+ this.#cookieSet = true;
2143
+ }
2144
+ this.#data ??= /* @__PURE__ */ new Map();
2145
+ const lifetime = ttl ?? this.#config.ttl;
2146
+ const expires = typeof lifetime === "number" ? Date.now() + lifetime * 1e3 : lifetime;
2147
+ this.#data.set(key, {
2148
+ data: cloned,
2149
+ expires
2150
+ });
2151
+ this.#dirty = true;
2152
+ }
2153
+ /**
2154
+ * Destroys the session, clearing the cookie and storage if it exists.
2155
+ */
2156
+ destroy() {
2157
+ const sessionId = this.#sessionID ?? this.#cookies.get(this.#cookieName)?.value;
2158
+ if (sessionId) {
2159
+ this.#toDestroy.add(sessionId);
2160
+ }
2161
+ this.#cookies.delete(this.#cookieName, this.#cookieConfig);
2162
+ this.#sessionID = void 0;
2163
+ this.#data = void 0;
2164
+ this.#dirty = true;
2165
+ }
2166
+ /**
2167
+ * Regenerates the session, creating a new session ID. The existing session data is preserved.
2168
+ */
2169
+ async regenerate() {
2170
+ let data = /* @__PURE__ */ new Map();
2171
+ try {
2172
+ data = await this.#ensureData();
2173
+ } catch (err) {
2174
+ console.error("Failed to load session data during regeneration:", err);
2175
+ }
2176
+ const oldSessionId = this.#sessionID;
2177
+ this.#sessionID = crypto.randomUUID();
2178
+ this.#data = data;
2179
+ await this.#setCookie();
2180
+ if (oldSessionId && this.#storage) {
2181
+ this.#storage.removeItem(oldSessionId).catch((err) => {
2182
+ console.error("Failed to remove old session data:", err);
2183
+ });
2184
+ }
2185
+ }
2186
+ // Persists the session data to storage.
2187
+ // This is called automatically at the end of the request.
2188
+ // Uses a symbol to prevent users from calling it directly.
2189
+ async [PERSIST_SYMBOL]() {
2190
+ if (!this.#dirty && !this.#toDestroy.size) {
2191
+ return;
2192
+ }
2193
+ const storage = await this.#ensureStorage();
2194
+ if (this.#dirty && this.#data) {
2195
+ const data = await this.#ensureData();
2196
+ this.#toDelete.forEach((key2) => data.delete(key2));
2197
+ const key = this.#ensureSessionID();
2198
+ let serialized;
2199
+ try {
2200
+ serialized = stringify(data);
2201
+ } catch (err) {
2202
+ throw new AstroError(
2203
+ {
2204
+ ...SessionStorageSaveError,
2205
+ message: SessionStorageSaveError.message(
2206
+ "The session data could not be serialized.",
2207
+ this.#config.driver
2208
+ )
2209
+ },
2210
+ { cause: err }
2211
+ );
2212
+ }
2213
+ await storage.setItem(key, serialized);
2214
+ this.#dirty = false;
2215
+ }
2216
+ if (this.#toDestroy.size > 0) {
2217
+ const cleanupPromises = [...this.#toDestroy].map(
2218
+ (sessionId) => storage.removeItem(sessionId).catch((err) => {
2219
+ console.error(`Failed to clean up session ${sessionId}:`, err);
2220
+ })
2221
+ );
2222
+ await Promise.all(cleanupPromises);
2223
+ this.#toDestroy.clear();
2224
+ }
2225
+ }
2226
+ get sessionID() {
2227
+ return this.#sessionID;
2228
+ }
2229
+ /**
2230
+ * Loads a session from storage with the given ID, and replaces the current session.
2231
+ * Any changes made to the current session will be lost.
2232
+ * This is not normally needed, as the session is automatically loaded using the cookie.
2233
+ * However it can be used to restore a session where the ID has been recorded somewhere
2234
+ * else (e.g. in a database).
2235
+ */
2236
+ async load(sessionID) {
2237
+ this.#sessionID = sessionID;
2238
+ this.#data = void 0;
2239
+ await this.#setCookie();
2240
+ await this.#ensureData();
2241
+ }
2242
+ /**
2243
+ * Sets the session cookie.
2244
+ */
2245
+ async #setCookie() {
2246
+ if (!VALID_COOKIE_REGEX.test(this.#cookieName)) {
2247
+ throw new AstroError({
2248
+ ...SessionStorageSaveError,
2249
+ message: "Invalid cookie name. Cookie names can only contain letters, numbers, and dashes."
2250
+ });
2251
+ }
2252
+ const value = this.#ensureSessionID();
2253
+ this.#cookies.set(this.#cookieName, value, this.#cookieConfig);
2254
+ }
2255
+ /**
2256
+ * Attempts to load the session data from storage, or creates a new data object if none exists.
2257
+ * If there is existing partial data, it will be merged into the new data object.
2258
+ */
2259
+ async #ensureData() {
2260
+ const storage = await this.#ensureStorage();
2261
+ if (this.#data && !this.#partial) {
2262
+ return this.#data;
2263
+ }
2264
+ this.#data ??= /* @__PURE__ */ new Map();
2265
+ const raw = await storage.get(this.#ensureSessionID());
2266
+ if (!raw) {
2267
+ return this.#data;
2268
+ }
2269
+ try {
2270
+ const storedMap = unflatten(raw);
2271
+ if (!(storedMap instanceof Map)) {
2272
+ await this.destroy();
2273
+ throw new AstroError({
2274
+ ...SessionStorageInitError,
2275
+ message: SessionStorageInitError.message(
2276
+ "The session data was an invalid type.",
2277
+ this.#config.driver
2278
+ )
2279
+ });
2280
+ }
2281
+ const now = Date.now();
2282
+ for (const [key, value] of storedMap) {
2283
+ const expired = typeof value.expires === "number" && value.expires < now;
2284
+ if (!this.#data.has(key) && !this.#toDelete.has(key) && !expired) {
2285
+ this.#data.set(key, value);
2286
+ }
2287
+ }
2288
+ this.#partial = false;
2289
+ return this.#data;
2290
+ } catch (err) {
2291
+ await this.destroy();
2292
+ if (err instanceof AstroError) {
2293
+ throw err;
2294
+ }
2295
+ throw new AstroError(
2296
+ {
2297
+ ...SessionStorageInitError,
2298
+ message: SessionStorageInitError.message(
2299
+ "The session data could not be parsed.",
2300
+ this.#config.driver
2301
+ )
2302
+ },
2303
+ { cause: err }
2304
+ );
2305
+ }
2306
+ }
2307
+ /**
2308
+ * Returns the session ID, generating a new one if it does not exist.
2309
+ */
2310
+ #ensureSessionID() {
2311
+ this.#sessionID ??= this.#cookies.get(this.#cookieName)?.value ?? crypto.randomUUID();
2312
+ return this.#sessionID;
2313
+ }
2314
+ /**
2315
+ * Ensures the storage is initialized.
2316
+ * This is called automatically when a storage operation is needed.
2317
+ */
2318
+ async #ensureStorage() {
2319
+ if (this.#storage) {
2320
+ return this.#storage;
2321
+ }
2322
+ if (AstroSession.#sharedStorage.has(this.#config.driver)) {
2323
+ this.#storage = AstroSession.#sharedStorage.get(this.#config.driver);
2324
+ return this.#storage;
2325
+ }
2326
+ if (this.#config.driver === "test") {
2327
+ this.#storage = this.#config.options.mockStorage;
2328
+ return this.#storage;
2329
+ }
2330
+ if (this.#config.driver === "fs" || this.#config.driver === "fsLite" || this.#config.driver === "fs-lite") {
2331
+ this.#config.options ??= {};
2332
+ this.#config.driver = "fs-lite";
2333
+ this.#config.options.base ??= ".astro/session";
2334
+ }
2335
+ let driver = null;
2336
+ try {
2337
+ if (this.#config.driverModule) {
2338
+ driver = (await this.#config.driverModule()).default;
2339
+ } else if (this.#config.driver) {
2340
+ const driverName = resolveSessionDriverName(this.#config.driver);
2341
+ if (driverName) {
2342
+ driver = (await import(driverName)).default;
2343
+ }
2344
+ }
2345
+ } catch (err) {
2346
+ if (err.code === "ERR_MODULE_NOT_FOUND") {
2347
+ throw new AstroError(
2348
+ {
2349
+ ...SessionStorageInitError,
2350
+ message: SessionStorageInitError.message(
2351
+ err.message.includes(`Cannot find package`) ? "The driver module could not be found." : err.message,
2352
+ this.#config.driver
2353
+ )
2354
+ },
2355
+ { cause: err }
2356
+ );
2357
+ }
2358
+ throw err;
2359
+ }
2360
+ if (!driver) {
2361
+ throw new AstroError({
2362
+ ...SessionStorageInitError,
2363
+ message: SessionStorageInitError.message(
2364
+ "The module did not export a driver.",
2365
+ this.#config.driver
2366
+ )
2367
+ });
2368
+ }
2369
+ try {
2370
+ this.#storage = createStorage({
2371
+ driver: driver(this.#config.options)
2372
+ });
2373
+ AstroSession.#sharedStorage.set(this.#config.driver, this.#storage);
2374
+ return this.#storage;
2375
+ } catch (err) {
2376
+ throw new AstroError(
2377
+ {
2378
+ ...SessionStorageInitError,
2379
+ message: SessionStorageInitError.message("Unknown error", this.#config.driver)
2380
+ },
2381
+ { cause: err }
2382
+ );
2383
+ }
2384
+ }
2385
+ }
2386
+ function resolveSessionDriverName(driver) {
2387
+ if (!driver) {
2388
+ return null;
2389
+ }
2390
+ try {
2391
+ if (driver === "fs") {
2392
+ return builtinDrivers.fsLite;
2393
+ }
2394
+ if (driver in builtinDrivers) {
2395
+ return builtinDrivers[driver];
2396
+ }
2397
+ } catch {
2398
+ return null;
2399
+ }
2400
+ return driver;
2401
+ }
2402
+
2403
+ function validateAndDecodePathname(pathname) {
2404
+ let decoded;
2405
+ try {
2406
+ decoded = decodeURI(pathname);
2407
+ } catch (_e) {
2408
+ throw new Error("Invalid URL encoding");
2409
+ }
2410
+ const hasDecoding = decoded !== pathname;
2411
+ const decodedStillHasEncoding = /%[0-9a-fA-F]{2}/.test(decoded);
2412
+ if (hasDecoding && decodedStillHasEncoding) {
2413
+ throw new Error("Multi-level URL encoding is not allowed");
2414
+ }
2415
+ return decoded;
2416
+ }
2417
+
2418
+ const apiContextRoutesSymbol = Symbol.for("context.routes");
2419
+ class RenderContext {
2420
+ constructor(pipeline, locals, middleware, actions, pathname, request, routeData, status, clientAddress, cookies = new AstroCookies(request), params = getParams(routeData, pathname), url = RenderContext.#createNormalizedUrl(request.url), props = {}, partial = void 0, shouldInjectCspMetaTags = !!pipeline.manifest.csp, session = pipeline.manifest.sessionConfig ? new AstroSession(cookies, pipeline.manifest.sessionConfig, pipeline.runtimeMode) : void 0) {
2421
+ this.pipeline = pipeline;
2422
+ this.locals = locals;
2423
+ this.middleware = middleware;
2424
+ this.actions = actions;
2425
+ this.pathname = pathname;
2426
+ this.request = request;
2427
+ this.routeData = routeData;
2428
+ this.status = status;
2429
+ this.clientAddress = clientAddress;
2430
+ this.cookies = cookies;
2431
+ this.params = params;
2432
+ this.url = url;
2433
+ this.props = props;
2434
+ this.partial = partial;
2435
+ this.shouldInjectCspMetaTags = shouldInjectCspMetaTags;
2436
+ this.session = session;
2437
+ }
2438
+ static #createNormalizedUrl(requestUrl) {
2439
+ const url = new URL(requestUrl);
2440
+ try {
2441
+ url.pathname = validateAndDecodePathname(url.pathname);
2442
+ } catch {
2443
+ try {
2444
+ url.pathname = decodeURI(url.pathname);
2445
+ } catch {
2446
+ }
2447
+ }
2448
+ return url;
2449
+ }
2450
+ /**
2451
+ * A flag that tells the render content if the rewriting was triggered
2452
+ */
2453
+ isRewriting = false;
2454
+ /**
2455
+ * A safety net in case of loops
2456
+ */
2457
+ counter = 0;
2458
+ result = void 0;
2459
+ static async create({
2460
+ locals = {},
2461
+ middleware,
2462
+ pathname,
2463
+ pipeline,
2464
+ request,
2465
+ routeData,
2466
+ clientAddress,
2467
+ status = 200,
2468
+ props,
2469
+ partial = void 0,
2470
+ actions,
2471
+ shouldInjectCspMetaTags
2472
+ }) {
2473
+ const pipelineMiddleware = await pipeline.getMiddleware();
2474
+ const pipelineActions = actions ?? await pipeline.getActions();
2475
+ setOriginPathname(
2476
+ request,
2477
+ pathname,
2478
+ pipeline.manifest.trailingSlash,
2479
+ pipeline.manifest.buildFormat
2480
+ );
2481
+ return new RenderContext(
2482
+ pipeline,
2483
+ locals,
2484
+ sequence(...pipeline.internalMiddleware, middleware ?? pipelineMiddleware),
2485
+ pipelineActions,
2486
+ pathname,
2487
+ request,
2488
+ routeData,
2489
+ status,
2490
+ clientAddress,
2491
+ void 0,
2492
+ void 0,
2493
+ void 0,
2494
+ props,
2495
+ partial,
2496
+ shouldInjectCspMetaTags ?? !!pipeline.manifest.csp
2497
+ );
2498
+ }
2499
+ /**
2500
+ * The main function of the RenderContext.
2501
+ *
2502
+ * Use this function to render any route known to Astro.
2503
+ * It attempts to render a route. A route can be a:
2504
+ *
2505
+ * - page
2506
+ * - redirect
2507
+ * - endpoint
2508
+ * - fallback
2509
+ */
2510
+ async render(componentInstance, slots = {}) {
2511
+ const { middleware, pipeline } = this;
2512
+ const { logger, serverLike, streaming, manifest } = pipeline;
2513
+ const props = Object.keys(this.props).length > 0 ? this.props : await getProps({
2514
+ mod: componentInstance,
2515
+ routeData: this.routeData,
2516
+ routeCache: this.pipeline.routeCache,
2517
+ pathname: this.pathname,
2518
+ logger,
2519
+ serverLike,
2520
+ base: manifest.base
2521
+ });
2522
+ const actionApiContext = this.createActionAPIContext();
2523
+ const apiContext = this.createAPIContext(props, actionApiContext);
2524
+ this.counter++;
2525
+ if (this.counter === 4) {
2526
+ return new Response("Loop Detected", {
2527
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/508
2528
+ status: 508,
2529
+ statusText: "Astro detected a loop where you tried to call the rewriting logic more than four times."
2530
+ });
2531
+ }
2532
+ const lastNext = async (ctx, payload) => {
2533
+ if (payload) {
2534
+ const oldPathname = this.pathname;
2535
+ pipeline.logger.debug("router", "Called rewriting to:", payload);
2536
+ const {
2537
+ routeData,
2538
+ componentInstance: newComponent,
2539
+ pathname,
2540
+ newUrl
2541
+ } = await pipeline.tryRewrite(payload, this.request);
2542
+ if (this.pipeline.serverLike === true && this.routeData.prerender === false && routeData.prerender === true) {
2543
+ throw new AstroError({
2544
+ ...ForbiddenRewrite,
2545
+ message: ForbiddenRewrite.message(this.pathname, pathname, routeData.component),
2546
+ hint: ForbiddenRewrite.hint(routeData.component)
2547
+ });
2548
+ }
2549
+ this.routeData = routeData;
2550
+ componentInstance = newComponent;
2551
+ if (payload instanceof Request) {
2552
+ this.request = payload;
2553
+ } else {
2554
+ this.request = copyRequest(
2555
+ newUrl,
2556
+ this.request,
2557
+ // need to send the flag of the previous routeData
2558
+ routeData.prerender,
2559
+ this.pipeline.logger,
2560
+ this.routeData.route
2561
+ );
2562
+ }
2563
+ this.isRewriting = true;
2564
+ this.url = RenderContext.#createNormalizedUrl(this.request.url);
2565
+ this.params = getParams(routeData, pathname);
2566
+ this.pathname = pathname;
2567
+ this.status = 200;
2568
+ setOriginPathname(
2569
+ this.request,
2570
+ oldPathname,
2571
+ this.pipeline.manifest.trailingSlash,
2572
+ this.pipeline.manifest.buildFormat
2573
+ );
2574
+ }
2575
+ let response2;
2576
+ if (!ctx.isPrerendered) {
2577
+ const { action, setActionResult, serializeActionResult } = getActionContext(ctx);
2578
+ if (action?.calledFrom === "form") {
2579
+ const actionResult = await action.handler();
2580
+ setActionResult(action.name, serializeActionResult(actionResult));
2581
+ }
2582
+ }
2583
+ switch (this.routeData.type) {
2584
+ case "endpoint": {
2585
+ response2 = await renderEndpoint(
2586
+ componentInstance,
2587
+ ctx,
2588
+ this.routeData.prerender,
2589
+ logger
2590
+ );
2591
+ break;
2592
+ }
2593
+ case "redirect":
2594
+ return renderRedirect(this);
2595
+ case "page": {
2596
+ this.result = await this.createResult(componentInstance, actionApiContext);
2597
+ try {
2598
+ response2 = await renderPage(
2599
+ this.result,
2600
+ componentInstance?.default,
2601
+ props,
2602
+ slots,
2603
+ streaming,
2604
+ this.routeData
2605
+ );
2606
+ } catch (e) {
2607
+ this.result.cancelled = true;
2608
+ throw e;
2609
+ }
2610
+ response2.headers.set(ROUTE_TYPE_HEADER, "page");
2611
+ if (this.routeData.route === "/404" || this.routeData.route === "/500") {
2612
+ response2.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
2613
+ }
2614
+ if (this.isRewriting) {
2615
+ response2.headers.set(REWRITE_DIRECTIVE_HEADER_KEY, REWRITE_DIRECTIVE_HEADER_VALUE);
2616
+ }
2617
+ break;
2618
+ }
2619
+ case "fallback": {
2620
+ return new Response(null, { status: 500, headers: { [ROUTE_TYPE_HEADER]: "fallback" } });
2621
+ }
2622
+ }
2623
+ const responseCookies = getCookiesFromResponse(response2);
2624
+ if (responseCookies) {
2625
+ this.cookies.merge(responseCookies);
2626
+ }
2627
+ return response2;
2628
+ };
2629
+ if (isRouteExternalRedirect(this.routeData)) {
2630
+ return renderRedirect(this);
2631
+ }
2632
+ const response = await callMiddleware(middleware, apiContext, lastNext);
2633
+ if (response.headers.get(ROUTE_TYPE_HEADER)) {
2634
+ response.headers.delete(ROUTE_TYPE_HEADER);
2635
+ }
2636
+ attachCookiesToResponse(response, this.cookies);
2637
+ return response;
2638
+ }
2639
+ createAPIContext(props, context) {
2640
+ const redirect = (path, status = 302) => new Response(null, { status, headers: { Location: path } });
2641
+ Reflect.set(context, apiContextRoutesSymbol, this.pipeline);
2642
+ return Object.assign(context, {
2643
+ props,
2644
+ redirect,
2645
+ getActionResult: createGetActionResult(context.locals),
2646
+ callAction: createCallAction(context)
2647
+ });
2648
+ }
2649
+ async #executeRewrite(reroutePayload) {
2650
+ this.pipeline.logger.debug("router", "Calling rewrite: ", reroutePayload);
2651
+ const oldPathname = this.pathname;
2652
+ const { routeData, componentInstance, newUrl, pathname } = await this.pipeline.tryRewrite(
2653
+ reroutePayload,
2654
+ this.request
2655
+ );
2656
+ const isI18nFallback = routeData.fallbackRoutes && routeData.fallbackRoutes.length > 0;
2657
+ if (this.pipeline.serverLike && !this.routeData.prerender && routeData.prerender && !isI18nFallback) {
2658
+ throw new AstroError({
2659
+ ...ForbiddenRewrite,
2660
+ message: ForbiddenRewrite.message(this.pathname, pathname, routeData.component),
2661
+ hint: ForbiddenRewrite.hint(routeData.component)
2662
+ });
2663
+ }
2664
+ this.routeData = routeData;
2665
+ if (reroutePayload instanceof Request) {
2666
+ this.request = reroutePayload;
2667
+ } else {
2668
+ this.request = copyRequest(
2669
+ newUrl,
2670
+ this.request,
2671
+ // need to send the flag of the previous routeData
2672
+ routeData.prerender,
2673
+ this.pipeline.logger,
2674
+ this.routeData.route
2675
+ );
2676
+ }
2677
+ this.url = RenderContext.#createNormalizedUrl(this.request.url);
2678
+ const newCookies = new AstroCookies(this.request);
2679
+ if (this.cookies) {
2680
+ newCookies.merge(this.cookies);
2681
+ }
2682
+ this.cookies = newCookies;
2683
+ this.params = getParams(routeData, pathname);
2684
+ this.pathname = pathname;
2685
+ this.isRewriting = true;
2686
+ this.status = 200;
2687
+ setOriginPathname(
2688
+ this.request,
2689
+ oldPathname,
2690
+ this.pipeline.manifest.trailingSlash,
2691
+ this.pipeline.manifest.buildFormat
2692
+ );
2693
+ return await this.render(componentInstance);
2694
+ }
2695
+ createActionAPIContext() {
2696
+ const renderContext = this;
2697
+ const { params, pipeline, url } = this;
2698
+ const generator = `Astro v${ASTRO_VERSION}`;
2699
+ const rewrite = async (reroutePayload) => {
2700
+ return await this.#executeRewrite(reroutePayload);
2701
+ };
2702
+ return {
2703
+ // Don't allow reassignment of cookies because it doesn't work
2704
+ get cookies() {
2705
+ return renderContext.cookies;
2706
+ },
2707
+ routePattern: this.routeData.route,
2708
+ isPrerendered: this.routeData.prerender,
2709
+ get clientAddress() {
2710
+ return renderContext.getClientAddress();
2711
+ },
2712
+ get currentLocale() {
2713
+ return renderContext.computeCurrentLocale();
2714
+ },
2715
+ generator,
2716
+ get locals() {
2717
+ return renderContext.locals;
2718
+ },
2719
+ set locals(_) {
2720
+ throw new AstroError(LocalsReassigned);
2721
+ },
2722
+ params,
2723
+ get preferredLocale() {
2724
+ return renderContext.computePreferredLocale();
2725
+ },
2726
+ get preferredLocaleList() {
2727
+ return renderContext.computePreferredLocaleList();
2728
+ },
2729
+ rewrite,
2730
+ request: this.request,
2731
+ site: pipeline.site,
2732
+ url,
2733
+ get originPathname() {
2734
+ return getOriginPathname(renderContext.request);
2735
+ },
2736
+ get session() {
2737
+ if (this.isPrerendered) {
2738
+ pipeline.logger.warn(
2739
+ "session",
2740
+ `context.session was used when rendering the route ${colors.green(this.routePattern)}, but it is not available on prerendered routes. If you need access to sessions, make sure that the route is server-rendered using \`export const prerender = false;\` or by setting \`output\` to \`"server"\` in your Astro config to make all your routes server-rendered by default. For more information, see https://docs.astro.build/en/guides/sessions/`
2741
+ );
2742
+ return void 0;
2743
+ }
2744
+ if (!renderContext.session) {
2745
+ pipeline.logger.warn(
2746
+ "session",
2747
+ `context.session was used when rendering the route ${colors.green(this.routePattern)}, but no storage configuration was provided. Either configure the storage manually or use an adapter that provides session storage. For more information, see https://docs.astro.build/en/guides/sessions/`
2748
+ );
2749
+ return void 0;
2750
+ }
2751
+ return renderContext.session;
2752
+ },
2753
+ get csp() {
2754
+ return {
2755
+ insertDirective(payload) {
2756
+ if (!pipeline.manifest.csp) {
2757
+ throw new AstroError(CspNotEnabled);
2758
+ }
2759
+ if (renderContext?.result?.directives) {
2760
+ renderContext.result.directives = pushDirective(
2761
+ renderContext.result.directives,
2762
+ payload
2763
+ );
2764
+ } else {
2765
+ renderContext?.result?.directives.push(payload);
2766
+ }
2767
+ },
2768
+ insertScriptResource(resource) {
2769
+ if (!pipeline.manifest.csp) {
2770
+ throw new AstroError(CspNotEnabled);
2771
+ }
2772
+ renderContext.result?.scriptResources.push(resource);
2773
+ },
2774
+ insertStyleResource(resource) {
2775
+ if (!pipeline.manifest.csp) {
2776
+ throw new AstroError(CspNotEnabled);
2777
+ }
2778
+ renderContext.result?.styleResources.push(resource);
2779
+ },
2780
+ insertStyleHash(hash) {
2781
+ if (!pipeline.manifest.csp) {
2782
+ throw new AstroError(CspNotEnabled);
2783
+ }
2784
+ renderContext.result?.styleHashes.push(hash);
2785
+ },
2786
+ insertScriptHash(hash) {
2787
+ if (!pipeline.manifest.csp) {
2788
+ throw new AstroError(CspNotEnabled);
2789
+ }
2790
+ renderContext.result?.scriptHashes.push(hash);
2791
+ }
2792
+ };
2793
+ }
2794
+ };
2795
+ }
2796
+ async createResult(mod, ctx) {
2797
+ const { cookies, pathname, pipeline, routeData, status } = this;
2798
+ const { clientDirectives, inlinedScripts, compressHTML, manifest, renderers, resolve } = pipeline;
2799
+ const { links, scripts, styles } = await pipeline.headElements(routeData);
2800
+ const extraStyleHashes = [];
2801
+ const extraScriptHashes = [];
2802
+ const shouldInjectCspMetaTags = this.shouldInjectCspMetaTags;
2803
+ const cspAlgorithm = manifest.csp?.algorithm ?? "SHA-256";
2804
+ if (shouldInjectCspMetaTags) {
2805
+ for (const style of styles) {
2806
+ extraStyleHashes.push(await generateCspDigest(style.children, cspAlgorithm));
2807
+ }
2808
+ for (const script of scripts) {
2809
+ extraScriptHashes.push(await generateCspDigest(script.children, cspAlgorithm));
2810
+ }
2811
+ }
2812
+ const componentMetadata = await pipeline.componentMetadata(routeData) ?? manifest.componentMetadata;
2813
+ const headers = new Headers({ "Content-Type": "text/html" });
2814
+ const partial = typeof this.partial === "boolean" ? this.partial : Boolean(mod.partial);
2815
+ const actionResult = hasActionPayload(this.locals) ? deserializeActionResult(this.locals._actionPayload.actionResult) : void 0;
2816
+ const response = {
2817
+ status: actionResult?.error ? actionResult?.error.status : status,
2818
+ statusText: actionResult?.error ? actionResult?.error.type : "OK",
2819
+ get headers() {
2820
+ return headers;
2821
+ },
2822
+ // Disallow `Astro.response.headers = new Headers`
2823
+ set headers(_) {
2824
+ throw new AstroError(AstroResponseHeadersReassigned);
2825
+ }
2826
+ };
2827
+ const result = {
2828
+ base: manifest.base,
2829
+ userAssetsBase: manifest.userAssetsBase,
2830
+ cancelled: false,
2831
+ clientDirectives,
2832
+ inlinedScripts,
2833
+ componentMetadata,
2834
+ compressHTML,
2835
+ cookies,
2836
+ /** This function returns the `Astro` faux-global */
2837
+ createAstro: (astroGlobal, props, slots) => this.createAstro(result, astroGlobal, props, slots, ctx),
2838
+ links,
2839
+ params: this.params,
2840
+ partial,
2841
+ pathname,
2842
+ renderers,
2843
+ resolve,
2844
+ response,
2845
+ request: this.request,
2846
+ scripts,
2847
+ styles,
2848
+ actionResult,
2849
+ serverIslandNameMap: manifest.serverIslandNameMap ?? /* @__PURE__ */ new Map(),
2850
+ key: manifest.key,
2851
+ trailingSlash: manifest.trailingSlash,
2852
+ _metadata: {
2853
+ hasHydrationScript: false,
2854
+ rendererSpecificHydrationScripts: /* @__PURE__ */ new Set(),
2855
+ hasRenderedHead: false,
2856
+ renderedScripts: /* @__PURE__ */ new Set(),
2857
+ hasDirectives: /* @__PURE__ */ new Set(),
2858
+ hasRenderedServerIslandRuntime: false,
2859
+ headInTree: false,
2860
+ extraHead: [],
2861
+ extraStyleHashes,
2862
+ extraScriptHashes,
2863
+ propagators: /* @__PURE__ */ new Set()
2864
+ },
2865
+ cspDestination: manifest.csp?.cspDestination ?? (routeData.prerender ? "meta" : "header"),
2866
+ shouldInjectCspMetaTags,
2867
+ cspAlgorithm,
2868
+ // The following arrays must be cloned, otherwise they become mutable across routes.
2869
+ scriptHashes: manifest.csp?.scriptHashes ? [...manifest.csp.scriptHashes] : [],
2870
+ scriptResources: manifest.csp?.scriptResources ? [...manifest.csp.scriptResources] : [],
2871
+ styleHashes: manifest.csp?.styleHashes ? [...manifest.csp.styleHashes] : [],
2872
+ styleResources: manifest.csp?.styleResources ? [...manifest.csp.styleResources] : [],
2873
+ directives: manifest.csp?.directives ? [...manifest.csp.directives] : [],
2874
+ isStrictDynamic: manifest.csp?.isStrictDynamic ?? false,
2875
+ internalFetchHeaders: manifest.internalFetchHeaders
2876
+ };
2877
+ return result;
2878
+ }
2879
+ #astroPagePartial;
2880
+ /**
2881
+ * The Astro global is sourced in 3 different phases:
2882
+ * - **Static**: `.generator` and `.glob` is printed by the compiler, instantiated once per process per astro file
2883
+ * - **Page-level**: `.request`, `.cookies`, `.locals` etc. These remain the same for the duration of the request.
2884
+ * - **Component-level**: `.props`, `.slots`, and `.self` are unique to each _use_ of each component.
2885
+ *
2886
+ * The page level partial is used as the prototype of the user-visible `Astro` global object, which is instantiated once per use of a component.
2887
+ */
2888
+ createAstro(result, astroStaticPartial, props, slotValues, apiContext) {
2889
+ let astroPagePartial;
2890
+ if (this.isRewriting) {
2891
+ astroPagePartial = this.#astroPagePartial = this.createAstroPagePartial(
2892
+ result,
2893
+ astroStaticPartial,
2894
+ apiContext
2895
+ );
2896
+ } else {
2897
+ astroPagePartial = this.#astroPagePartial ??= this.createAstroPagePartial(
2898
+ result,
2899
+ astroStaticPartial,
2900
+ apiContext
2901
+ );
2902
+ }
2903
+ const astroComponentPartial = { props, self: null };
2904
+ const Astro = Object.assign(
2905
+ Object.create(astroPagePartial),
2906
+ astroComponentPartial
2907
+ );
2908
+ let _slots;
2909
+ Object.defineProperty(Astro, "slots", {
2910
+ get: () => {
2911
+ if (!_slots) {
2912
+ _slots = new Slots(
2913
+ result,
2914
+ slotValues,
2915
+ this.pipeline.logger
2916
+ );
2917
+ }
2918
+ return _slots;
2919
+ }
2920
+ });
2921
+ return Astro;
2922
+ }
2923
+ createAstroPagePartial(result, astroStaticPartial, apiContext) {
2924
+ const renderContext = this;
2925
+ const { cookies, locals, params, pipeline, url } = this;
2926
+ const { response } = result;
2927
+ const redirect = (path, status = 302) => {
2928
+ if (this.request[responseSentSymbol$1]) {
2929
+ throw new AstroError({
2930
+ ...ResponseSentError
2931
+ });
2932
+ }
2933
+ return new Response(null, { status, headers: { Location: path } });
2934
+ };
2935
+ const rewrite = async (reroutePayload) => {
2936
+ return await this.#executeRewrite(reroutePayload);
2937
+ };
2938
+ const callAction = createCallAction(apiContext);
2939
+ return {
2940
+ generator: astroStaticPartial.generator,
2941
+ glob: astroStaticPartial.glob,
2942
+ routePattern: this.routeData.route,
2943
+ isPrerendered: this.routeData.prerender,
2944
+ cookies,
2945
+ get session() {
2946
+ if (this.isPrerendered) {
2947
+ pipeline.logger.warn(
2948
+ "session",
2949
+ `Astro.session was used when rendering the route ${colors.green(this.routePattern)}, but it is not available on prerendered pages. If you need access to sessions, make sure that the page is server-rendered using \`export const prerender = false;\` or by setting \`output\` to \`"server"\` in your Astro config to make all your pages server-rendered by default. For more information, see https://docs.astro.build/en/guides/sessions/`
2950
+ );
2951
+ return void 0;
2952
+ }
2953
+ if (!renderContext.session) {
2954
+ pipeline.logger.warn(
2955
+ "session",
2956
+ `Astro.session was used when rendering the route ${colors.green(this.routePattern)}, but no storage configuration was provided. Either configure the storage manually or use an adapter that provides session storage. For more information, see https://docs.astro.build/en/guides/sessions/`
2957
+ );
2958
+ return void 0;
2959
+ }
2960
+ return renderContext.session;
2961
+ },
2962
+ get clientAddress() {
2963
+ return renderContext.getClientAddress();
2964
+ },
2965
+ get currentLocale() {
2966
+ return renderContext.computeCurrentLocale();
2967
+ },
2968
+ params,
2969
+ get preferredLocale() {
2970
+ return renderContext.computePreferredLocale();
2971
+ },
2972
+ get preferredLocaleList() {
2973
+ return renderContext.computePreferredLocaleList();
2974
+ },
2975
+ locals,
2976
+ redirect,
2977
+ rewrite,
2978
+ request: this.request,
2979
+ response,
2980
+ site: pipeline.site,
2981
+ getActionResult: createGetActionResult(locals),
2982
+ get callAction() {
2983
+ return callAction;
2984
+ },
2985
+ url,
2986
+ get originPathname() {
2987
+ return getOriginPathname(renderContext.request);
2988
+ },
2989
+ get csp() {
2990
+ return {
2991
+ insertDirective(payload) {
2992
+ if (!pipeline.manifest.csp) {
2993
+ throw new AstroError(CspNotEnabled);
2994
+ }
2995
+ if (renderContext?.result?.directives) {
2996
+ renderContext.result.directives = pushDirective(
2997
+ renderContext.result.directives,
2998
+ payload
2999
+ );
3000
+ } else {
3001
+ renderContext?.result?.directives.push(payload);
3002
+ }
3003
+ },
3004
+ insertScriptResource(resource) {
3005
+ if (!pipeline.manifest.csp) {
3006
+ throw new AstroError(CspNotEnabled);
3007
+ }
3008
+ renderContext.result?.scriptResources.push(resource);
3009
+ },
3010
+ insertStyleResource(resource) {
3011
+ if (!pipeline.manifest.csp) {
3012
+ throw new AstroError(CspNotEnabled);
3013
+ }
3014
+ renderContext.result?.styleResources.push(resource);
3015
+ },
3016
+ insertStyleHash(hash) {
3017
+ if (!pipeline.manifest.csp) {
3018
+ throw new AstroError(CspNotEnabled);
3019
+ }
3020
+ renderContext.result?.styleHashes.push(hash);
3021
+ },
3022
+ insertScriptHash(hash) {
3023
+ if (!pipeline.manifest.csp) {
3024
+ throw new AstroError(CspNotEnabled);
3025
+ }
3026
+ renderContext.result?.scriptHashes.push(hash);
3027
+ }
3028
+ };
3029
+ }
3030
+ };
3031
+ }
3032
+ getClientAddress() {
3033
+ const { pipeline, request, routeData, clientAddress } = this;
3034
+ if (routeData.prerender) {
3035
+ throw new AstroError({
3036
+ ...PrerenderClientAddressNotAvailable,
3037
+ message: PrerenderClientAddressNotAvailable.message(routeData.component)
3038
+ });
3039
+ }
3040
+ if (clientAddress) {
3041
+ return clientAddress;
3042
+ }
3043
+ if (clientAddressSymbol in request) {
3044
+ return Reflect.get(request, clientAddressSymbol);
3045
+ }
3046
+ if (pipeline.adapterName) {
3047
+ throw new AstroError({
3048
+ ...ClientAddressNotAvailable,
3049
+ message: ClientAddressNotAvailable.message(pipeline.adapterName)
3050
+ });
3051
+ }
3052
+ throw new AstroError(StaticClientAddressNotAvailable);
3053
+ }
3054
+ /**
3055
+ * API Context may be created multiple times per request, i18n data needs to be computed only once.
3056
+ * So, it is computed and saved here on creation of the first APIContext and reused for later ones.
3057
+ */
3058
+ #currentLocale;
3059
+ computeCurrentLocale() {
3060
+ const {
3061
+ url,
3062
+ pipeline: { i18n },
3063
+ routeData
3064
+ } = this;
3065
+ if (!i18n) return;
3066
+ const { defaultLocale, locales, strategy } = i18n;
3067
+ const fallbackTo = strategy === "pathname-prefix-other-locales" || strategy === "domains-prefix-other-locales" ? defaultLocale : void 0;
3068
+ if (this.#currentLocale) {
3069
+ return this.#currentLocale;
3070
+ }
3071
+ let computedLocale;
3072
+ if (isRouteServerIsland(routeData)) {
3073
+ let referer = this.request.headers.get("referer");
3074
+ if (referer) {
3075
+ if (URL.canParse(referer)) {
3076
+ referer = new URL(referer).pathname;
3077
+ }
3078
+ computedLocale = computeCurrentLocale(referer, locales, defaultLocale);
3079
+ }
3080
+ } else {
3081
+ let pathname = routeData.pathname;
3082
+ if (!routeData.pattern.test(url.pathname)) {
3083
+ for (const fallbackRoute of routeData.fallbackRoutes) {
3084
+ if (fallbackRoute.pattern.test(url.pathname)) {
3085
+ pathname = fallbackRoute.pathname;
3086
+ break;
3087
+ }
3088
+ }
3089
+ }
3090
+ pathname = pathname && !isRoute404or500(routeData) ? pathname : url.pathname;
3091
+ computedLocale = computeCurrentLocale(pathname, locales, defaultLocale);
3092
+ }
3093
+ this.#currentLocale = computedLocale ?? fallbackTo;
3094
+ return this.#currentLocale;
3095
+ }
3096
+ #preferredLocale;
3097
+ computePreferredLocale() {
3098
+ const {
3099
+ pipeline: { i18n },
3100
+ request
3101
+ } = this;
3102
+ if (!i18n) return;
3103
+ return this.#preferredLocale ??= computePreferredLocale(request, i18n.locales);
3104
+ }
3105
+ #preferredLocaleList;
3106
+ computePreferredLocaleList() {
3107
+ const {
3108
+ pipeline: { i18n },
3109
+ request
3110
+ } = this;
3111
+ if (!i18n) return;
3112
+ return this.#preferredLocaleList ??= computePreferredLocaleList(request, i18n.locales);
3113
+ }
3114
+ }
3115
+
3116
+ function redirectTemplate({
3117
+ status,
3118
+ absoluteLocation,
3119
+ relativeLocation,
3120
+ from
3121
+ }) {
3122
+ const delay = status === 302 ? 2 : 0;
3123
+ return `<!doctype html>
3124
+ <title>Redirecting to: ${relativeLocation}</title>
3125
+ <meta http-equiv="refresh" content="${delay};url=${relativeLocation}">
3126
+ <meta name="robots" content="noindex">
3127
+ <link rel="canonical" href="${absoluteLocation}">
3128
+ <body>
3129
+ <a href="${relativeLocation}">Redirecting ${from ? `from <code>${from}</code> ` : ""}to <code>${relativeLocation}</code></a>
3130
+ </body>`;
3131
+ }
3132
+
3133
+ class AppPipeline extends Pipeline {
3134
+ static create({
3135
+ logger,
3136
+ manifest,
3137
+ runtimeMode,
3138
+ renderers,
3139
+ resolve,
3140
+ serverLike,
3141
+ streaming,
3142
+ defaultRoutes
3143
+ }) {
3144
+ const pipeline = new AppPipeline(
3145
+ logger,
3146
+ manifest,
3147
+ runtimeMode,
3148
+ renderers,
3149
+ resolve,
3150
+ serverLike,
3151
+ streaming,
3152
+ void 0,
3153
+ void 0,
3154
+ void 0,
3155
+ void 0,
3156
+ void 0,
3157
+ void 0,
3158
+ void 0,
3159
+ void 0,
3160
+ defaultRoutes
3161
+ );
3162
+ return pipeline;
3163
+ }
3164
+ headElements(routeData) {
3165
+ const routeInfo = this.manifest.routes.find((route) => route.routeData === routeData);
3166
+ const links = /* @__PURE__ */ new Set();
3167
+ const scripts = /* @__PURE__ */ new Set();
3168
+ const styles = createStylesheetElementSet(routeInfo?.styles ?? []);
3169
+ for (const script of routeInfo?.scripts ?? []) {
3170
+ if ("stage" in script) {
3171
+ if (script.stage === "head-inline") {
3172
+ scripts.add({
3173
+ props: {},
3174
+ children: script.children
3175
+ });
3176
+ }
3177
+ } else {
3178
+ scripts.add(createModuleScriptElement(script));
3179
+ }
3180
+ }
3181
+ return { links, styles, scripts };
3182
+ }
3183
+ componentMetadata() {
3184
+ }
3185
+ async getComponentByRoute(routeData) {
3186
+ const module = await this.getModuleForRoute(routeData);
3187
+ return module.page();
3188
+ }
3189
+ async tryRewrite(payload, request) {
3190
+ const { newUrl, pathname, routeData } = findRouteToRewrite({
3191
+ payload,
3192
+ request,
3193
+ routes: this.manifest?.routes.map((r) => r.routeData),
3194
+ trailingSlash: this.manifest.trailingSlash,
3195
+ buildFormat: this.manifest.buildFormat,
3196
+ base: this.manifest.base,
3197
+ outDir: this.serverLike ? this.manifest.buildClientDir : this.manifest.outDir
3198
+ });
3199
+ const componentInstance = await this.getComponentByRoute(routeData);
3200
+ return { newUrl, pathname, componentInstance, routeData };
3201
+ }
3202
+ async getModuleForRoute(route) {
3203
+ for (const defaultRoute of this.defaultRoutes) {
3204
+ if (route.component === defaultRoute.component) {
3205
+ return {
3206
+ page: () => Promise.resolve(defaultRoute.instance),
3207
+ renderers: []
3208
+ };
3209
+ }
3210
+ }
3211
+ if (route.type === "redirect") {
3212
+ return RedirectSinglePageBuiltModule;
3213
+ } else {
3214
+ if (this.manifest.pageMap) {
3215
+ const importComponentInstance = this.manifest.pageMap.get(route.component);
3216
+ if (!importComponentInstance) {
3217
+ throw new Error(
3218
+ `Unexpectedly unable to find a component instance for route ${route.route}`
3219
+ );
3220
+ }
3221
+ return await importComponentInstance();
3222
+ } else if (this.manifest.pageModule) {
3223
+ return this.manifest.pageModule;
3224
+ }
3225
+ throw new Error(
3226
+ "Astro couldn't find the correct page to render, probably because it wasn't correctly mapped for SSR usage. This is an internal error, please file an issue."
3227
+ );
3228
+ }
3229
+ }
3230
+ }
3231
+
3232
+ class App {
3233
+ #manifest;
3234
+ #manifestData;
3235
+ #logger = new Logger({
3236
+ dest: consoleLogDestination,
3237
+ level: "info"
3238
+ });
3239
+ #baseWithoutTrailingSlash;
3240
+ #pipeline;
3241
+ #adapterLogger;
3242
+ constructor(manifest, streaming = true) {
3243
+ this.#manifest = manifest;
3244
+ this.#manifestData = {
3245
+ routes: manifest.routes.map((route) => route.routeData)
3246
+ };
3247
+ ensure404Route(this.#manifestData);
3248
+ this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
3249
+ this.#pipeline = this.#createPipeline(streaming);
3250
+ this.#adapterLogger = new AstroIntegrationLogger(
3251
+ this.#logger.options,
3252
+ this.#manifest.adapterName
3253
+ );
3254
+ }
3255
+ getAdapterLogger() {
3256
+ return this.#adapterLogger;
3257
+ }
3258
+ getAllowedDomains() {
3259
+ return this.#manifest.allowedDomains;
3260
+ }
3261
+ get manifest() {
3262
+ return this.#manifest;
3263
+ }
3264
+ set manifest(value) {
3265
+ this.#manifest = value;
3266
+ }
3267
+ matchesAllowedDomains(forwardedHost, protocol) {
3268
+ return App.validateForwardedHost(forwardedHost, this.#manifest.allowedDomains, protocol);
3269
+ }
3270
+ static validateForwardedHost(forwardedHost, allowedDomains, protocol) {
3271
+ if (!allowedDomains || allowedDomains.length === 0) {
3272
+ return false;
3273
+ }
3274
+ try {
3275
+ const testUrl = new URL(`${protocol || "https"}://${forwardedHost}`);
3276
+ return allowedDomains.some((pattern) => {
3277
+ return matchPattern(testUrl, pattern);
3278
+ });
3279
+ } catch {
3280
+ return false;
3281
+ }
3282
+ }
3283
+ /**
3284
+ * Validate a hostname by rejecting any with path separators.
3285
+ * Prevents path injection attacks. Invalid hostnames return undefined.
3286
+ */
3287
+ static sanitizeHost(hostname) {
3288
+ if (!hostname) return void 0;
3289
+ if (/[/\\]/.test(hostname)) return void 0;
3290
+ return hostname;
3291
+ }
3292
+ /**
3293
+ * Validate forwarded headers (proto, host, port) against allowedDomains.
3294
+ * Returns validated values or undefined for rejected headers.
3295
+ * Uses strict defaults: http/https only for proto, rejects port if not in allowedDomains.
3296
+ */
3297
+ static validateForwardedHeaders(forwardedProtocol, forwardedHost, forwardedPort, allowedDomains) {
3298
+ const result = {};
3299
+ if (forwardedProtocol) {
3300
+ if (allowedDomains && allowedDomains.length > 0) {
3301
+ const hasProtocolPatterns = allowedDomains.some(
3302
+ (pattern) => pattern.protocol !== void 0
3303
+ );
3304
+ if (hasProtocolPatterns) {
3305
+ try {
3306
+ const testUrl = new URL(`${forwardedProtocol}://example.com`);
3307
+ const isAllowed = allowedDomains.some((pattern) => matchPattern(testUrl, pattern));
3308
+ if (isAllowed) {
3309
+ result.protocol = forwardedProtocol;
3310
+ }
3311
+ } catch {
3312
+ }
3313
+ } else if (/^https?$/.test(forwardedProtocol)) {
3314
+ result.protocol = forwardedProtocol;
3315
+ }
3316
+ } else if (/^https?$/.test(forwardedProtocol)) {
3317
+ result.protocol = forwardedProtocol;
3318
+ }
3319
+ }
3320
+ if (forwardedPort && allowedDomains && allowedDomains.length > 0) {
3321
+ const hasPortPatterns = allowedDomains.some((pattern) => pattern.port !== void 0);
3322
+ if (hasPortPatterns) {
3323
+ const isAllowed = allowedDomains.some((pattern) => pattern.port === forwardedPort);
3324
+ if (isAllowed) {
3325
+ result.port = forwardedPort;
3326
+ }
3327
+ }
3328
+ }
3329
+ if (forwardedHost && forwardedHost.length > 0 && allowedDomains && allowedDomains.length > 0) {
3330
+ const protoForValidation = result.protocol || "https";
3331
+ const sanitized = App.sanitizeHost(forwardedHost);
3332
+ if (sanitized) {
3333
+ try {
3334
+ const hostnameOnly = sanitized.split(":")[0];
3335
+ const portFromHost = sanitized.includes(":") ? sanitized.split(":")[1] : void 0;
3336
+ const portForValidation = result.port || portFromHost;
3337
+ const hostWithPort = portForValidation ? `${hostnameOnly}:${portForValidation}` : hostnameOnly;
3338
+ const testUrl = new URL(`${protoForValidation}://${hostWithPort}`);
3339
+ const isAllowed = allowedDomains.some((pattern) => matchPattern(testUrl, pattern));
3340
+ if (isAllowed) {
3341
+ result.host = sanitized;
3342
+ }
3343
+ } catch {
3344
+ }
3345
+ }
3346
+ }
3347
+ return result;
3348
+ }
3349
+ /**
3350
+ * Creates a pipeline by reading the stored manifest
3351
+ *
3352
+ * @param streaming
3353
+ * @private
3354
+ */
3355
+ #createPipeline(streaming = false) {
3356
+ return AppPipeline.create({
3357
+ logger: this.#logger,
3358
+ manifest: this.#manifest,
3359
+ runtimeMode: "production",
3360
+ renderers: this.#manifest.renderers,
3361
+ defaultRoutes: createDefaultRoutes(this.#manifest),
3362
+ resolve: async (specifier) => {
3363
+ if (!(specifier in this.#manifest.entryModules)) {
3364
+ throw new Error(`Unable to resolve [${specifier}]`);
3365
+ }
3366
+ const bundlePath = this.#manifest.entryModules[specifier];
3367
+ if (bundlePath.startsWith("data:") || bundlePath.length === 0) {
3368
+ return bundlePath;
3369
+ } else {
3370
+ return createAssetLink(bundlePath, this.#manifest.base, this.#manifest.assetsPrefix);
3371
+ }
3372
+ },
3373
+ serverLike: true,
3374
+ streaming
3375
+ });
3376
+ }
3377
+ set setManifestData(newManifestData) {
3378
+ this.#manifestData = newManifestData;
3379
+ }
3380
+ removeBase(pathname) {
3381
+ if (pathname.startsWith(this.#manifest.base)) {
3382
+ return pathname.slice(this.#baseWithoutTrailingSlash.length + 1);
3383
+ }
3384
+ return pathname;
3385
+ }
3386
+ /**
3387
+ * It removes the base from the request URL, prepends it with a forward slash and attempts to decoded it.
3388
+ *
3389
+ * If the decoding fails, it logs the error and return the pathname as is.
3390
+ * @param request
3391
+ * @private
3392
+ */
3393
+ #getPathnameFromRequest(request) {
3394
+ const url = new URL(request.url);
3395
+ const pathname = prependForwardSlash$1(this.removeBase(url.pathname));
3396
+ try {
3397
+ return validateAndDecodePathname(pathname);
3398
+ } catch (e) {
3399
+ this.getAdapterLogger().error(e.toString());
3400
+ return pathname;
3401
+ }
3402
+ }
3403
+ /**
3404
+ * Given a `Request`, it returns the `RouteData` that matches its `pathname`. By default, prerendered
3405
+ * routes aren't returned, even if they are matched.
3406
+ *
3407
+ * When `allowPrerenderedRoutes` is `true`, the function returns matched prerendered routes too.
3408
+ * @param request
3409
+ * @param allowPrerenderedRoutes
3410
+ */
3411
+ match(request, allowPrerenderedRoutes = false) {
3412
+ const url = new URL(request.url);
3413
+ if (this.#manifest.assets.has(url.pathname)) return void 0;
3414
+ let pathname = this.#computePathnameFromDomain(request);
3415
+ if (!pathname) {
3416
+ pathname = prependForwardSlash$1(this.removeBase(url.pathname));
3417
+ }
3418
+ try {
3419
+ pathname = validateAndDecodePathname(pathname);
3420
+ } catch {
3421
+ return void 0;
3422
+ }
3423
+ let routeData = matchRoute(pathname, this.#manifestData);
3424
+ if (!routeData) return void 0;
3425
+ if (allowPrerenderedRoutes) {
3426
+ return routeData;
3427
+ } else if (routeData.prerender) {
3428
+ return void 0;
3429
+ }
3430
+ return routeData;
3431
+ }
3432
+ #computePathnameFromDomain(request) {
3433
+ let pathname = void 0;
3434
+ const url = new URL(request.url);
3435
+ if (this.#manifest.i18n && (this.#manifest.i18n.strategy === "domains-prefix-always" || this.#manifest.i18n.strategy === "domains-prefix-other-locales" || this.#manifest.i18n.strategy === "domains-prefix-always-no-redirect")) {
3436
+ const validated = App.validateForwardedHeaders(
3437
+ request.headers.get("X-Forwarded-Proto") ?? void 0,
3438
+ request.headers.get("X-Forwarded-Host") ?? void 0,
3439
+ request.headers.get("X-Forwarded-Port") ?? void 0,
3440
+ this.#manifest.allowedDomains
3441
+ );
3442
+ let protocol = validated.protocol ? validated.protocol + ":" : url.protocol;
3443
+ let host = validated.host ?? request.headers.get("Host");
3444
+ if (host && protocol) {
3445
+ host = host.split(":")[0];
3446
+ try {
3447
+ let locale;
3448
+ const hostAsUrl = new URL(`${protocol}//${host}`);
3449
+ for (const [domainKey, localeValue] of Object.entries(
3450
+ this.#manifest.i18n.domainLookupTable
3451
+ )) {
3452
+ const domainKeyAsUrl = new URL(domainKey);
3453
+ if (hostAsUrl.host === domainKeyAsUrl.host && hostAsUrl.protocol === domainKeyAsUrl.protocol) {
3454
+ locale = localeValue;
3455
+ break;
3456
+ }
3457
+ }
3458
+ if (locale) {
3459
+ pathname = prependForwardSlash$1(
3460
+ joinPaths(normalizeTheLocale(locale), this.removeBase(url.pathname))
3461
+ );
3462
+ if (url.pathname.endsWith("/")) {
3463
+ pathname = appendForwardSlash$1(pathname);
3464
+ }
3465
+ }
3466
+ } catch (e) {
3467
+ this.#logger.error(
3468
+ "router",
3469
+ `Astro tried to parse ${protocol}//${host} as an URL, but it threw a parsing error. Check the X-Forwarded-Host and X-Forwarded-Proto headers.`
3470
+ );
3471
+ this.#logger.error("router", `Error: ${e}`);
3472
+ }
3473
+ }
3474
+ }
3475
+ return pathname;
3476
+ }
3477
+ #redirectTrailingSlash(pathname) {
3478
+ const { trailingSlash } = this.#manifest;
3479
+ if (pathname === "/" || isInternalPath(pathname)) {
3480
+ return pathname;
3481
+ }
3482
+ const path = collapseDuplicateTrailingSlashes(pathname, trailingSlash !== "never");
3483
+ if (path !== pathname) {
3484
+ return path;
3485
+ }
3486
+ if (trailingSlash === "ignore") {
3487
+ return pathname;
3488
+ }
3489
+ if (trailingSlash === "always" && !hasFileExtension(pathname)) {
3490
+ return appendForwardSlash$1(pathname);
3491
+ }
3492
+ if (trailingSlash === "never") {
3493
+ return removeTrailingForwardSlash(pathname);
3494
+ }
3495
+ return pathname;
3496
+ }
3497
+ async render(request, renderOptions) {
3498
+ let routeData;
3499
+ let locals;
3500
+ let clientAddress;
3501
+ let addCookieHeader;
3502
+ const url = new URL(request.url);
3503
+ const redirect = this.#redirectTrailingSlash(url.pathname);
3504
+ const prerenderedErrorPageFetch = renderOptions?.prerenderedErrorPageFetch ?? fetch;
3505
+ if (redirect !== url.pathname) {
3506
+ const status = request.method === "GET" ? 301 : 308;
3507
+ return new Response(
3508
+ redirectTemplate({
3509
+ status,
3510
+ relativeLocation: url.pathname,
3511
+ absoluteLocation: redirect,
3512
+ from: request.url
3513
+ }),
3514
+ {
3515
+ status,
3516
+ headers: {
3517
+ location: redirect + url.search
3518
+ }
3519
+ }
3520
+ );
3521
+ }
3522
+ addCookieHeader = renderOptions?.addCookieHeader;
3523
+ clientAddress = renderOptions?.clientAddress ?? Reflect.get(request, clientAddressSymbol);
3524
+ routeData = renderOptions?.routeData;
3525
+ locals = renderOptions?.locals;
3526
+ if (routeData) {
3527
+ this.#logger.debug(
3528
+ "router",
3529
+ "The adapter " + this.#manifest.adapterName + " provided a custom RouteData for ",
3530
+ request.url
3531
+ );
3532
+ this.#logger.debug("router", "RouteData:\n" + routeData);
3533
+ }
3534
+ if (locals) {
3535
+ if (typeof locals !== "object") {
3536
+ const error = new AstroError(LocalsNotAnObject);
3537
+ this.#logger.error(null, error.stack);
3538
+ return this.#renderError(request, {
3539
+ status: 500,
3540
+ error,
3541
+ clientAddress,
3542
+ prerenderedErrorPageFetch
3543
+ });
3544
+ }
3545
+ }
3546
+ if (!routeData) {
3547
+ routeData = this.match(request);
3548
+ this.#logger.debug("router", "Astro matched the following route for " + request.url);
3549
+ this.#logger.debug("router", "RouteData:\n" + routeData);
3550
+ }
3551
+ if (!routeData) {
3552
+ routeData = this.#manifestData.routes.find(
3553
+ (route) => route.component === "404.astro" || route.component === DEFAULT_404_COMPONENT
3554
+ );
3555
+ }
3556
+ if (!routeData) {
3557
+ this.#logger.debug("router", "Astro hasn't found routes that match " + request.url);
3558
+ this.#logger.debug("router", "Here's the available routes:\n", this.#manifestData);
3559
+ return this.#renderError(request, {
3560
+ locals,
3561
+ status: 404,
3562
+ clientAddress,
3563
+ prerenderedErrorPageFetch
3564
+ });
3565
+ }
3566
+ const pathname = this.#getPathnameFromRequest(request);
3567
+ const defaultStatus = this.#getDefaultStatusCode(routeData, pathname);
3568
+ let response;
3569
+ let session;
3570
+ try {
3571
+ const mod = await this.#pipeline.getModuleForRoute(routeData);
3572
+ const renderContext = await RenderContext.create({
3573
+ pipeline: this.#pipeline,
3574
+ locals,
3575
+ pathname,
3576
+ request,
3577
+ routeData,
3578
+ status: defaultStatus,
3579
+ clientAddress
3580
+ });
3581
+ session = renderContext.session;
3582
+ response = await renderContext.render(await mod.page());
3583
+ } catch (err) {
3584
+ this.#logger.error(null, err.stack || err.message || String(err));
3585
+ return this.#renderError(request, {
3586
+ locals,
3587
+ status: 500,
3588
+ error: err,
3589
+ clientAddress,
3590
+ prerenderedErrorPageFetch
3591
+ });
3592
+ } finally {
3593
+ await session?.[PERSIST_SYMBOL]();
3594
+ }
3595
+ if (REROUTABLE_STATUS_CODES.includes(response.status) && // If the body isn't null, that means the user sets the 404 status
3596
+ // but uses the current route to handle the 404
3597
+ response.body === null && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== "no") {
3598
+ return this.#renderError(request, {
3599
+ locals,
3600
+ response,
3601
+ status: response.status,
3602
+ // We don't have an error to report here. Passing null means we pass nothing intentionally
3603
+ // while undefined means there's no error
3604
+ error: response.status === 500 ? null : void 0,
3605
+ clientAddress,
3606
+ prerenderedErrorPageFetch
3607
+ });
3608
+ }
3609
+ if (response.headers.has(REROUTE_DIRECTIVE_HEADER)) {
3610
+ response.headers.delete(REROUTE_DIRECTIVE_HEADER);
3611
+ }
3612
+ if (addCookieHeader) {
3613
+ for (const setCookieHeaderValue of App.getSetCookieFromResponse(response)) {
3614
+ response.headers.append("set-cookie", setCookieHeaderValue);
3615
+ }
3616
+ }
3617
+ Reflect.set(response, responseSentSymbol$1, true);
3618
+ return response;
3619
+ }
3620
+ setCookieHeaders(response) {
3621
+ return getSetCookiesFromResponse(response);
3622
+ }
3623
+ /**
3624
+ * Reads all the cookies written by `Astro.cookie.set()` onto the passed response.
3625
+ * For example,
3626
+ * ```ts
3627
+ * for (const cookie_ of App.getSetCookieFromResponse(response)) {
3628
+ * const cookie: string = cookie_
3629
+ * }
3630
+ * ```
3631
+ * @param response The response to read cookies from.
3632
+ * @returns An iterator that yields key-value pairs as equal-sign-separated strings.
3633
+ */
3634
+ static getSetCookieFromResponse = getSetCookiesFromResponse;
3635
+ /**
3636
+ * If it is a known error code, try sending the according page (e.g. 404.astro / 500.astro).
3637
+ * This also handles pre-rendered /404 or /500 routes
3638
+ */
3639
+ async #renderError(request, {
3640
+ locals,
3641
+ status,
3642
+ response: originalResponse,
3643
+ skipMiddleware = false,
3644
+ error,
3645
+ clientAddress,
3646
+ prerenderedErrorPageFetch
3647
+ }) {
3648
+ const errorRoutePath = `/${status}${this.#manifest.trailingSlash === "always" ? "/" : ""}`;
3649
+ const errorRouteData = matchRoute(errorRoutePath, this.#manifestData);
3650
+ const url = new URL(request.url);
3651
+ if (errorRouteData) {
3652
+ if (errorRouteData.prerender) {
3653
+ const maybeDotHtml = errorRouteData.route.endsWith(`/${status}`) ? ".html" : "";
3654
+ const statusURL = new URL(
3655
+ `${this.#baseWithoutTrailingSlash}/${status}${maybeDotHtml}`,
3656
+ url
3657
+ );
3658
+ if (statusURL.toString() !== request.url) {
3659
+ const response2 = await prerenderedErrorPageFetch(statusURL.toString());
3660
+ const override = { status, removeContentEncodingHeaders: true };
3661
+ return this.#mergeResponses(response2, originalResponse, override);
3662
+ }
3663
+ }
3664
+ const mod = await this.#pipeline.getModuleForRoute(errorRouteData);
3665
+ let session;
3666
+ try {
3667
+ const renderContext = await RenderContext.create({
3668
+ locals,
3669
+ pipeline: this.#pipeline,
3670
+ middleware: skipMiddleware ? NOOP_MIDDLEWARE_FN : void 0,
3671
+ pathname: this.#getPathnameFromRequest(request),
3672
+ request,
3673
+ routeData: errorRouteData,
3674
+ status,
3675
+ props: { error },
3676
+ clientAddress
3677
+ });
3678
+ session = renderContext.session;
3679
+ const response2 = await renderContext.render(await mod.page());
3680
+ return this.#mergeResponses(response2, originalResponse);
3681
+ } catch {
3682
+ if (skipMiddleware === false) {
3683
+ return this.#renderError(request, {
3684
+ locals,
3685
+ status,
3686
+ response: originalResponse,
3687
+ skipMiddleware: true,
3688
+ clientAddress,
3689
+ prerenderedErrorPageFetch
3690
+ });
3691
+ }
3692
+ } finally {
3693
+ await session?.[PERSIST_SYMBOL]();
3694
+ }
3695
+ }
3696
+ const response = this.#mergeResponses(new Response(null, { status }), originalResponse);
3697
+ Reflect.set(response, responseSentSymbol$1, true);
3698
+ return response;
3699
+ }
3700
+ #mergeResponses(newResponse, originalResponse, override) {
3701
+ let newResponseHeaders = newResponse.headers;
3702
+ if (override?.removeContentEncodingHeaders) {
3703
+ newResponseHeaders = new Headers(newResponseHeaders);
3704
+ newResponseHeaders.delete("Content-Encoding");
3705
+ newResponseHeaders.delete("Content-Length");
3706
+ }
3707
+ if (!originalResponse) {
3708
+ if (override !== void 0) {
3709
+ return new Response(newResponse.body, {
3710
+ status: override.status,
3711
+ statusText: newResponse.statusText,
3712
+ headers: newResponseHeaders
3713
+ });
3714
+ }
3715
+ return newResponse;
3716
+ }
3717
+ const status = override?.status ? override.status : originalResponse.status === 200 ? newResponse.status : originalResponse.status;
3718
+ try {
3719
+ originalResponse.headers.delete("Content-type");
3720
+ } catch {
3721
+ }
3722
+ const mergedHeaders = new Map([
3723
+ ...Array.from(newResponseHeaders),
3724
+ ...Array.from(originalResponse.headers)
3725
+ ]);
3726
+ const newHeaders = new Headers();
3727
+ for (const [name, value] of mergedHeaders) {
3728
+ newHeaders.set(name, value);
3729
+ }
3730
+ return new Response(newResponse.body, {
3731
+ status,
3732
+ statusText: status === 200 ? newResponse.statusText : originalResponse.statusText,
3733
+ // If you're looking at here for possible bugs, it means that it's not a bug.
3734
+ // With the middleware, users can meddle with headers, and we should pass to the 404/500.
3735
+ // If users see something weird, it's because they are setting some headers they should not.
3736
+ //
3737
+ // Although, we don't want it to replace the content-type, because the error page must return `text/html`
3738
+ headers: newHeaders
3739
+ });
3740
+ }
3741
+ #getDefaultStatusCode(routeData, pathname) {
3742
+ if (!routeData.pattern.test(pathname)) {
3743
+ for (const fallbackRoute of routeData.fallbackRoutes) {
3744
+ if (fallbackRoute.pattern.test(pathname)) {
3745
+ return 302;
3746
+ }
3747
+ }
3748
+ }
3749
+ const route = removeTrailingForwardSlash(routeData.route);
3750
+ if (route.endsWith("/404")) return 404;
3751
+ if (route.endsWith("/500")) return 500;
3752
+ return 200;
3753
+ }
3754
+ }
3755
+
3756
+ const createOutgoingHttpHeaders = (headers) => {
3757
+ if (!headers) {
3758
+ return void 0;
3759
+ }
3760
+ const nodeHeaders = Object.fromEntries(headers.entries());
3761
+ if (Object.keys(nodeHeaders).length === 0) {
3762
+ return void 0;
3763
+ }
3764
+ if (headers.has("set-cookie")) {
3765
+ const cookieHeaders = headers.getSetCookie();
3766
+ if (cookieHeaders.length > 1) {
3767
+ nodeHeaders["set-cookie"] = cookieHeaders;
3768
+ }
3769
+ }
3770
+ return nodeHeaders;
3771
+ };
3772
+
3773
+ function apply() {
3774
+ if (!globalThis.crypto) {
3775
+ Object.defineProperty(globalThis, "crypto", {
3776
+ value: crypto$1.webcrypto
3777
+ });
3778
+ }
3779
+ if (!globalThis.File) {
3780
+ Object.defineProperty(globalThis, "File", {
3781
+ value: buffer.File
3782
+ });
3783
+ }
3784
+ }
3785
+
3786
+ class NodeApp extends App {
3787
+ headersMap = void 0;
3788
+ setHeadersMap(headers) {
3789
+ this.headersMap = headers;
3790
+ }
3791
+ match(req, allowPrerenderedRoutes = false) {
3792
+ if (!(req instanceof Request)) {
3793
+ req = NodeApp.createRequest(req, {
3794
+ skipBody: true,
3795
+ allowedDomains: this.manifest.allowedDomains
3796
+ });
3797
+ }
3798
+ return super.match(req, allowPrerenderedRoutes);
3799
+ }
3800
+ render(req, routeDataOrOptions, maybeLocals) {
3801
+ if (!(req instanceof Request)) {
3802
+ req = NodeApp.createRequest(req, {
3803
+ allowedDomains: this.manifest.allowedDomains
3804
+ });
3805
+ }
3806
+ return super.render(req, routeDataOrOptions, maybeLocals);
3807
+ }
3808
+ /**
3809
+ * Converts a NodeJS IncomingMessage into a web standard Request.
3810
+ * ```js
3811
+ * import { NodeApp } from 'astro/app/node';
3812
+ * import { createServer } from 'node:http';
3813
+ *
3814
+ * const server = createServer(async (req, res) => {
3815
+ * const request = NodeApp.createRequest(req);
3816
+ * const response = await app.render(request);
3817
+ * await NodeApp.writeResponse(response, res);
3818
+ * })
3819
+ * ```
3820
+ */
3821
+ static createRequest(req, {
3822
+ skipBody = false,
3823
+ allowedDomains = []
3824
+ } = {}) {
3825
+ const controller = new AbortController();
3826
+ const isEncrypted = "encrypted" in req.socket && req.socket.encrypted;
3827
+ const getFirstForwardedValue = (multiValueHeader) => {
3828
+ return multiValueHeader?.toString()?.split(",").map((e) => e.trim())?.[0];
3829
+ };
3830
+ const providedProtocol = isEncrypted ? "https" : "http";
3831
+ const providedHostname = req.headers.host ?? req.headers[":authority"];
3832
+ const validated = App.validateForwardedHeaders(
3833
+ getFirstForwardedValue(req.headers["x-forwarded-proto"]),
3834
+ getFirstForwardedValue(req.headers["x-forwarded-host"]),
3835
+ getFirstForwardedValue(req.headers["x-forwarded-port"]),
3836
+ allowedDomains
3837
+ );
3838
+ const protocol = validated.protocol ?? providedProtocol;
3839
+ const sanitizedProvidedHostname = App.sanitizeHost(
3840
+ typeof providedHostname === "string" ? providedHostname : void 0
3841
+ );
3842
+ const hostname = validated.host ?? sanitizedProvidedHostname;
3843
+ const port = validated.port;
3844
+ let url;
3845
+ try {
3846
+ const hostnamePort = getHostnamePort(hostname, port);
3847
+ url = new URL(`${protocol}://${hostnamePort}${req.url}`);
3848
+ } catch {
3849
+ const hostnamePort = getHostnamePort(providedHostname, port);
3850
+ url = new URL(`${providedProtocol}://${hostnamePort}`);
3851
+ }
3852
+ const options = {
3853
+ method: req.method || "GET",
3854
+ headers: makeRequestHeaders(req),
3855
+ signal: controller.signal
3856
+ };
3857
+ const bodyAllowed = options.method !== "HEAD" && options.method !== "GET" && skipBody === false;
3858
+ if (bodyAllowed) {
3859
+ Object.assign(options, makeRequestBody(req));
3860
+ }
3861
+ const request = new Request(url, options);
3862
+ const socket = getRequestSocket(req);
3863
+ if (socket && typeof socket.on === "function") {
3864
+ const existingCleanup = getAbortControllerCleanup(req);
3865
+ if (existingCleanup) {
3866
+ existingCleanup();
3867
+ }
3868
+ let cleanedUp = false;
3869
+ const removeSocketListener = () => {
3870
+ if (typeof socket.off === "function") {
3871
+ socket.off("close", onSocketClose);
3872
+ } else if (typeof socket.removeListener === "function") {
3873
+ socket.removeListener("close", onSocketClose);
3874
+ }
3875
+ };
3876
+ const cleanup = () => {
3877
+ if (cleanedUp) return;
3878
+ cleanedUp = true;
3879
+ removeSocketListener();
3880
+ controller.signal.removeEventListener("abort", cleanup);
3881
+ Reflect.deleteProperty(req, nodeRequestAbortControllerCleanupSymbol);
3882
+ };
3883
+ const onSocketClose = () => {
3884
+ cleanup();
3885
+ if (!controller.signal.aborted) {
3886
+ controller.abort();
3887
+ }
3888
+ };
3889
+ socket.on("close", onSocketClose);
3890
+ controller.signal.addEventListener("abort", cleanup, { once: true });
3891
+ Reflect.set(req, nodeRequestAbortControllerCleanupSymbol, cleanup);
3892
+ if (socket.destroyed) {
3893
+ onSocketClose();
3894
+ }
3895
+ }
3896
+ const forwardedClientIp = getFirstForwardedValue(req.headers["x-forwarded-for"]);
3897
+ const clientIp = forwardedClientIp || req.socket?.remoteAddress;
3898
+ if (clientIp) {
3899
+ Reflect.set(request, clientAddressSymbol, clientIp);
3900
+ }
3901
+ return request;
3902
+ }
3903
+ /**
3904
+ * Streams a web-standard Response into a NodeJS Server Response.
3905
+ * ```js
3906
+ * import { NodeApp } from 'astro/app/node';
3907
+ * import { createServer } from 'node:http';
3908
+ *
3909
+ * const server = createServer(async (req, res) => {
3910
+ * const request = NodeApp.createRequest(req);
3911
+ * const response = await app.render(request);
3912
+ * await NodeApp.writeResponse(response, res);
3913
+ * })
3914
+ * ```
3915
+ * @param source WhatWG Response
3916
+ * @param destination NodeJS ServerResponse
3917
+ */
3918
+ static async writeResponse(source, destination) {
3919
+ const { status, headers, body, statusText } = source;
3920
+ if (!(destination instanceof Http2ServerResponse)) {
3921
+ destination.statusMessage = statusText;
3922
+ }
3923
+ destination.writeHead(status, createOutgoingHttpHeaders(headers));
3924
+ const cleanupAbortFromDestination = getAbortControllerCleanup(
3925
+ destination.req ?? void 0
3926
+ );
3927
+ if (cleanupAbortFromDestination) {
3928
+ const runCleanup = () => {
3929
+ cleanupAbortFromDestination();
3930
+ if (typeof destination.off === "function") {
3931
+ destination.off("finish", runCleanup);
3932
+ destination.off("close", runCleanup);
3933
+ } else {
3934
+ destination.removeListener?.("finish", runCleanup);
3935
+ destination.removeListener?.("close", runCleanup);
3936
+ }
3937
+ };
3938
+ destination.on("finish", runCleanup);
3939
+ destination.on("close", runCleanup);
3940
+ }
3941
+ if (!body) return destination.end();
3942
+ try {
3943
+ const reader = body.getReader();
3944
+ destination.on("close", () => {
3945
+ reader.cancel().catch((err) => {
3946
+ console.error(
3947
+ `There was an uncaught error in the middle of the stream while rendering ${destination.req.url}.`,
3948
+ err
3949
+ );
3950
+ });
3951
+ });
3952
+ let result = await reader.read();
3953
+ while (!result.done) {
3954
+ destination.write(result.value);
3955
+ result = await reader.read();
3956
+ }
3957
+ destination.end();
3958
+ } catch (err) {
3959
+ destination.write("Internal server error", () => {
3960
+ err instanceof Error ? destination.destroy(err) : destination.destroy();
3961
+ });
3962
+ }
3963
+ }
3964
+ }
3965
+ function getHostnamePort(hostname, port) {
3966
+ const portInHostname = typeof hostname === "string" && /:\d+$/.test(hostname);
3967
+ const hostnamePort = portInHostname ? hostname : `${hostname}${port ? `:${port}` : ""}`;
3968
+ return hostnamePort;
3969
+ }
3970
+ function makeRequestHeaders(req) {
3971
+ const headers = new Headers();
3972
+ for (const [name, value] of Object.entries(req.headers)) {
3973
+ if (value === void 0) {
3974
+ continue;
3975
+ }
3976
+ if (Array.isArray(value)) {
3977
+ for (const item of value) {
3978
+ headers.append(name, item);
3979
+ }
3980
+ } else {
3981
+ headers.append(name, value);
3982
+ }
3983
+ }
3984
+ return headers;
3985
+ }
3986
+ function makeRequestBody(req) {
3987
+ if (req.body !== void 0) {
3988
+ if (typeof req.body === "string" && req.body.length > 0) {
3989
+ return { body: Buffer.from(req.body) };
3990
+ }
3991
+ if (typeof req.body === "object" && req.body !== null && Object.keys(req.body).length > 0) {
3992
+ return { body: Buffer.from(JSON.stringify(req.body)) };
3993
+ }
3994
+ if (typeof req.body === "object" && req.body !== null && typeof req.body[Symbol.asyncIterator] !== "undefined") {
3995
+ return asyncIterableToBodyProps(req.body);
3996
+ }
3997
+ }
3998
+ return asyncIterableToBodyProps(req);
3999
+ }
4000
+ function asyncIterableToBodyProps(iterable) {
4001
+ return {
4002
+ // Node uses undici for the Request implementation. Undici accepts
4003
+ // a non-standard async iterable for the body.
4004
+ // @ts-expect-error
4005
+ body: iterable,
4006
+ // The duplex property is required when using a ReadableStream or async
4007
+ // iterable for the body. The type definitions do not include the duplex
4008
+ // property because they are not up-to-date.
4009
+ duplex: "half"
4010
+ };
4011
+ }
4012
+ function getAbortControllerCleanup(req) {
4013
+ if (!req) return void 0;
4014
+ const cleanup = Reflect.get(req, nodeRequestAbortControllerCleanupSymbol);
4015
+ return typeof cleanup === "function" ? cleanup : void 0;
4016
+ }
4017
+ function getRequestSocket(req) {
4018
+ if (req.socket && typeof req.socket.on === "function") {
4019
+ return req.socket;
4020
+ }
4021
+ const http2Socket = req.stream?.session?.socket;
4022
+ if (http2Socket && typeof http2Socket.on === "function") {
4023
+ return http2Socket;
4024
+ }
4025
+ return void 0;
4026
+ }
4027
+
4028
+ apply();
4029
+
4030
+ function createAppHandler(app, options) {
4031
+ const als = new AsyncLocalStorage();
4032
+ const logger = app.getAdapterLogger();
4033
+ process.on("unhandledRejection", (reason) => {
4034
+ const requestUrl = als.getStore();
4035
+ logger.error(`Unhandled rejection while rendering ${requestUrl}`);
4036
+ console.error(reason);
4037
+ });
4038
+ const originUrl = options.experimentalErrorPageHost ? new URL(options.experimentalErrorPageHost) : void 0;
4039
+ const prerenderedErrorPageFetch = originUrl ? (url) => {
4040
+ const errorPageUrl = new URL(url);
4041
+ errorPageUrl.protocol = originUrl.protocol;
4042
+ errorPageUrl.host = originUrl.host;
4043
+ return fetch(errorPageUrl);
4044
+ } : void 0;
4045
+ return async (req, res, next, locals) => {
4046
+ let request;
4047
+ try {
4048
+ request = NodeApp.createRequest(req, {
4049
+ allowedDomains: app.getAllowedDomains?.() ?? []
4050
+ });
4051
+ } catch (err) {
4052
+ logger.error(`Could not render ${req.url}`);
4053
+ console.error(err);
4054
+ res.statusCode = 500;
4055
+ res.end("Internal Server Error");
4056
+ return;
4057
+ }
4058
+ const routeData = app.match(request, true);
4059
+ if (routeData) {
4060
+ const response = await als.run(
4061
+ request.url,
4062
+ () => app.render(request, {
4063
+ addCookieHeader: true,
4064
+ locals,
4065
+ routeData,
4066
+ prerenderedErrorPageFetch
4067
+ })
4068
+ );
4069
+ await NodeApp.writeResponse(response, res);
4070
+ } else if (next) {
4071
+ return next();
4072
+ } else {
4073
+ const response = await app.render(req, { addCookieHeader: true, prerenderedErrorPageFetch });
4074
+ await NodeApp.writeResponse(response, res);
4075
+ }
4076
+ };
4077
+ }
4078
+
4079
+ function createMiddleware(app, options) {
4080
+ const handler = createAppHandler(app, options);
4081
+ const logger = app.getAdapterLogger();
4082
+ return async (...args) => {
4083
+ const [req, res, next, locals] = args;
4084
+ if (req instanceof Error) {
4085
+ const error = req;
4086
+ if (next) {
4087
+ return next(error);
4088
+ } else {
4089
+ throw error;
4090
+ }
4091
+ }
4092
+ try {
4093
+ await handler(req, res, next, locals);
4094
+ } catch (err) {
4095
+ logger.error(`Could not render ${req.url}`);
4096
+ console.error(err);
4097
+ if (!res.headersSent) {
4098
+ res.writeHead(500, `Server error`);
4099
+ res.end();
4100
+ }
4101
+ }
4102
+ };
4103
+ }
4104
+
4105
+ const STATIC_HEADERS_FILE = "_experimentalHeaders.json";
4106
+
4107
+ const wildcardHosts = /* @__PURE__ */ new Set(["0.0.0.0", "::", "0000:0000:0000:0000:0000:0000:0000:0000"]);
4108
+ async function logListeningOn(logger, server, configuredHost) {
4109
+ await new Promise((resolve) => server.once("listening", resolve));
4110
+ const protocol = server instanceof https.Server ? "https" : "http";
4111
+ const host = getResolvedHostForHttpServer(configuredHost);
4112
+ const { port } = server.address();
4113
+ const address = getNetworkAddress(protocol, host, port);
4114
+ if (host === void 0 || wildcardHosts.has(host)) {
4115
+ logger.info(
4116
+ `Server listening on
4117
+ local: ${address.local[0]}
4118
+ network: ${address.network[0]}
4119
+ `
4120
+ );
4121
+ } else {
4122
+ logger.info(`Server listening on ${address.local[0]}`);
4123
+ }
4124
+ }
4125
+ function getResolvedHostForHttpServer(host) {
4126
+ if (host === false) {
4127
+ return "localhost";
4128
+ } else if (host === true) {
4129
+ return void 0;
4130
+ } else {
4131
+ return host;
4132
+ }
4133
+ }
4134
+ function getNetworkAddress(protocol = "http", hostname, port, base) {
4135
+ const NetworkAddress = {
4136
+ local: [],
4137
+ network: []
4138
+ };
4139
+ Object.values(os.networkInterfaces()).flatMap((nInterface) => nInterface ?? []).filter(
4140
+ (detail) => detail && detail.address && (detail.family === "IPv4" || // @ts-expect-error Node 18.0 - 18.3 returns number
4141
+ detail.family === 4)
4142
+ ).forEach((detail) => {
4143
+ let host = detail.address.replace(
4144
+ "127.0.0.1",
4145
+ hostname === void 0 || wildcardHosts.has(hostname) ? "localhost" : hostname
4146
+ );
4147
+ if (host.includes(":")) {
4148
+ host = `[${host}]`;
4149
+ }
4150
+ const url = `${protocol}://${host}:${port}${""}`;
4151
+ if (detail.address.includes("127.0.0.1")) {
4152
+ NetworkAddress.local.push(url);
4153
+ } else {
4154
+ NetworkAddress.network.push(url);
4155
+ }
4156
+ });
4157
+ return NetworkAddress;
4158
+ }
4159
+
4160
+ function createStaticHandler(app, options) {
4161
+ const client = resolveClientDir(options);
4162
+ return (req, res, ssr) => {
4163
+ if (req.url) {
4164
+ const [urlPath, urlQuery] = req.url.split("?");
4165
+ const filePath = path.join(client, app.removeBase(urlPath));
4166
+ let isDirectory = false;
4167
+ try {
4168
+ isDirectory = fs.lstatSync(filePath).isDirectory();
4169
+ } catch {
4170
+ }
4171
+ const { trailingSlash = "ignore" } = options;
4172
+ const hasSlash = urlPath.endsWith("/");
4173
+ let pathname = urlPath;
4174
+ if (app.headersMap && app.headersMap.length > 0) {
4175
+ const routeData = app.match(req, true);
4176
+ if (routeData && routeData.prerender) {
4177
+ const matchedRoute = app.headersMap.find((header) => header.pathname.includes(pathname));
4178
+ if (matchedRoute) {
4179
+ for (const header of matchedRoute.headers) {
4180
+ res.setHeader(header.key, header.value);
4181
+ }
4182
+ }
4183
+ }
4184
+ }
4185
+ switch (trailingSlash) {
4186
+ case "never": {
4187
+ if (isDirectory && urlPath !== "/" && hasSlash) {
4188
+ pathname = urlPath.slice(0, -1) + (urlQuery ? "?" + urlQuery : "");
4189
+ res.statusCode = 301;
4190
+ res.setHeader("Location", pathname);
4191
+ return res.end();
4192
+ }
4193
+ if (isDirectory && !hasSlash) {
4194
+ pathname = `${urlPath}/index.html`;
4195
+ }
4196
+ break;
4197
+ }
4198
+ case "ignore": {
4199
+ if (isDirectory && !hasSlash) {
4200
+ pathname = `${urlPath}/index.html`;
4201
+ }
4202
+ break;
4203
+ }
4204
+ case "always": {
4205
+ if (!hasSlash && !hasFileExtension(urlPath) && !isInternalPath(urlPath)) {
4206
+ pathname = urlPath + "/" + (urlQuery ? "?" + urlQuery : "");
4207
+ res.statusCode = 301;
4208
+ res.setHeader("Location", pathname);
4209
+ return res.end();
4210
+ }
4211
+ break;
4212
+ }
4213
+ }
4214
+ pathname = prependForwardSlash(app.removeBase(pathname));
4215
+ const stream = send(req, pathname, {
4216
+ root: client,
4217
+ dotfiles: pathname.startsWith("/.well-known/") ? "allow" : "deny"
4218
+ });
4219
+ let forwardError = false;
4220
+ stream.on("error", (err) => {
4221
+ if (forwardError) {
4222
+ console.error(err.toString());
4223
+ res.writeHead(500);
4224
+ res.end("Internal server error");
4225
+ return;
4226
+ }
4227
+ ssr();
4228
+ });
4229
+ stream.on("headers", (_res) => {
4230
+ if (pathname.startsWith(`/${options.assets}/`)) {
4231
+ _res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
4232
+ }
4233
+ });
4234
+ stream.on("file", () => {
4235
+ forwardError = true;
4236
+ });
4237
+ stream.pipe(res);
4238
+ } else {
4239
+ ssr();
4240
+ }
4241
+ };
4242
+ }
4243
+ function resolveClientDir(options) {
4244
+ const clientURLRaw = new URL(options.client);
4245
+ const serverURLRaw = new URL(options.server);
4246
+ const rel = path.relative(url.fileURLToPath(serverURLRaw), url.fileURLToPath(clientURLRaw));
4247
+ const serverFolder = path.basename(options.server);
4248
+ let serverEntryFolderURL = path.dirname(import.meta.url);
4249
+ while (!serverEntryFolderURL.endsWith(serverFolder)) {
4250
+ serverEntryFolderURL = path.dirname(serverEntryFolderURL);
4251
+ }
4252
+ const serverEntryURL = serverEntryFolderURL + "/entry.mjs";
4253
+ const clientURL = new URL(appendForwardSlash(rel), serverEntryURL);
4254
+ const client = url.fileURLToPath(clientURL);
4255
+ return client;
4256
+ }
4257
+ function prependForwardSlash(pth) {
4258
+ return pth.startsWith("/") ? pth : "/" + pth;
4259
+ }
4260
+ function appendForwardSlash(pth) {
4261
+ return pth.endsWith("/") ? pth : pth + "/";
4262
+ }
4263
+
4264
+ const hostOptions = (host) => {
4265
+ if (typeof host === "boolean") {
4266
+ return host ? "0.0.0.0" : "localhost";
4267
+ }
4268
+ return host;
4269
+ };
4270
+ function standalone(app, options) {
4271
+ const port = process.env.PORT ? Number(process.env.PORT) : options.port ?? 8080;
4272
+ const host = process.env.HOST ?? hostOptions(options.host);
4273
+ const handler = createStandaloneHandler(app, options);
4274
+ const server = createServer(handler, host, port);
4275
+ server.server.listen(port, host);
4276
+ if (process.env.ASTRO_NODE_LOGGING !== "disabled") {
4277
+ logListeningOn(app.getAdapterLogger(), server.server, host);
4278
+ }
4279
+ return {
4280
+ server,
4281
+ done: server.closed()
4282
+ };
4283
+ }
4284
+ function createStandaloneHandler(app, options) {
4285
+ const appHandler = createAppHandler(app, options);
4286
+ const staticHandler = createStaticHandler(app, options);
4287
+ return (req, res) => {
4288
+ try {
4289
+ decodeURI(req.url);
4290
+ } catch {
4291
+ res.writeHead(400);
4292
+ res.end("Bad request.");
4293
+ return;
4294
+ }
4295
+ staticHandler(req, res, () => appHandler(req, res));
4296
+ };
4297
+ }
4298
+ function createServer(listener, host, port) {
4299
+ let httpServer;
4300
+ if (process.env.SERVER_CERT_PATH && process.env.SERVER_KEY_PATH) {
4301
+ httpServer = https.createServer(
4302
+ {
4303
+ key: fs.readFileSync(process.env.SERVER_KEY_PATH),
4304
+ cert: fs.readFileSync(process.env.SERVER_CERT_PATH)
4305
+ },
4306
+ listener
4307
+ );
4308
+ } else {
4309
+ httpServer = http.createServer(listener);
4310
+ }
4311
+ enableDestroy(httpServer);
4312
+ const closed = new Promise((resolve, reject) => {
4313
+ httpServer.addListener("close", resolve);
4314
+ httpServer.addListener("error", reject);
4315
+ });
4316
+ const previewable = {
4317
+ host,
4318
+ port,
4319
+ closed() {
4320
+ return closed;
4321
+ },
4322
+ async stop() {
4323
+ await new Promise((resolve, reject) => {
4324
+ httpServer.destroy((err) => err ? reject(err) : resolve(void 0));
4325
+ });
4326
+ }
4327
+ };
4328
+ return {
4329
+ server: httpServer,
4330
+ ...previewable
4331
+ };
4332
+ }
4333
+
4334
+ function createExports(manifest, options) {
4335
+ const app = new NodeApp(manifest, !options.experimentalDisableStreaming);
4336
+ let headersMap = void 0;
4337
+ if (options.experimentalStaticHeaders) {
4338
+ headersMap = readHeadersJson(manifest.outDir);
4339
+ }
4340
+ if (headersMap) {
4341
+ app.setHeadersMap(headersMap);
4342
+ }
4343
+ options.trailingSlash = manifest.trailingSlash;
4344
+ return {
4345
+ options,
4346
+ handler: options.mode === "middleware" ? createMiddleware(app, options) : createStandaloneHandler(app, options),
4347
+ startServer: () => standalone(app, options)
4348
+ };
4349
+ }
4350
+ function start(manifest, options) {
4351
+ if (options.mode !== "standalone" || process.env.ASTRO_NODE_AUTOSTART === "disabled") {
4352
+ return;
4353
+ }
4354
+ let headersMap = void 0;
4355
+ if (options.experimentalStaticHeaders) {
4356
+ headersMap = readHeadersJson(manifest.outDir);
4357
+ }
4358
+ const app = new NodeApp(manifest, !options.experimentalDisableStreaming);
4359
+ if (headersMap) {
4360
+ app.setHeadersMap(headersMap);
4361
+ }
4362
+ standalone(app, options);
4363
+ }
4364
+ function readHeadersJson(outDir) {
4365
+ let headersMap = void 0;
4366
+ const headersUrl = new URL(STATIC_HEADERS_FILE, outDir);
4367
+ if (existsSync(headersUrl)) {
4368
+ const content = readFileSync(headersUrl, "utf-8");
4369
+ try {
4370
+ headersMap = JSON.parse(content);
4371
+ } catch (e) {
4372
+ console.error("[@astrojs/node] Error parsing _headers.json: " + e.message);
4373
+ console.error("[@astrojs/node] Please make sure your _headers.json is valid JSON.");
4374
+ }
4375
+ }
4376
+ return headersMap;
4377
+ }
4378
+
4379
+ const serverEntrypointModule = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
4380
+ __proto__: null,
4381
+ createExports,
4382
+ start
4383
+ }, Symbol.toStringTag, { value: 'Module' }));
4384
+
4385
+ export { start as a, createExports as c, serverEntrypointModule as s };