index_BfW_FHpJ.mjs 102 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164
  1. globalThis.process ??= {}; globalThis.process.env ??= {};
  2. import { K as decryptString, O as createSlotValueFromString, P as isAstroComponentFactory, k as renderComponent, r as renderTemplate, q as REROUTE_DIRECTIVE_HEADER, A as AstroError, Q as i18nNoLocaleFoundInPath, S as ResponseSentError, T as originPathnameSymbol, V as RewriteWithBodyUsed, W as GetStaticPathsRequired, X as InvalidGetStaticPathsReturn, Y as InvalidGetStaticPathsEntry, Z as GetStaticPathsExpectedParams, _ as GetStaticPathsInvalidRouteParam, $ as PageNumberParamNotFound, D as DEFAULT_404_COMPONENT, a0 as NoMatchingStaticPathFound, a1 as PrerenderDynamicEndpointPathCollide, a2 as ReservedSlotName, a3 as renderSlotToString, a4 as renderJSX, a5 as chunkToString, a6 as isRenderInstruction, a7 as MiddlewareNoDataOrNextCalled, a8 as MiddlewareNotAResponse, a9 as SessionStorageInitError, aa as SessionStorageSaveError, R as ROUTE_TYPE_HEADER, ab as ForbiddenRewrite, ac as ASTRO_VERSION, ad as CspNotEnabled, ae as green, af as LocalsReassigned, ag as generateCspDigest, ah as PrerenderClientAddressNotAvailable, C as clientAddressSymbol, ai as ClientAddressNotAvailable, aj as StaticClientAddressNotAvailable, ak as AstroResponseHeadersReassigned, J as responseSentSymbol$1, al as renderPage, am as REWRITE_DIRECTIVE_HEADER_KEY, an as REWRITE_DIRECTIVE_HEADER_VALUE, ao as renderEndpoint } from './astro/server_WO3f6Mge.mjs';
  3. import { g as getActionQueryString, a as deserializeActionResult, b as distExports, D as DEFAULT_404_ROUTE, A as ActionError, s as serializeActionResult, c as ACTION_RPC_ROUTE_PATTERN, f as ACTION_QUERY_PARAMS, u as unflatten$1, h as stringify$2 } from './astro-designed-error-pages_CuMapJD2.mjs';
  4. import { a as appendForwardSlash, j as joinPaths, r as removeTrailingForwardSlash, p as prependForwardSlash, t as trimSlashes } from './path_CH3auf61.mjs';
  5. const ACTION_API_CONTEXT_SYMBOL = Symbol.for("astro.actionAPIContext");
  6. const formContentTypes = ["application/x-www-form-urlencoded", "multipart/form-data"];
  7. function hasContentType(contentType, expected) {
  8. const type = contentType.split(";")[0].toLowerCase();
  9. return expected.some((t) => type === t);
  10. }
  11. function hasActionPayload(locals) {
  12. return "_actionPayload" in locals;
  13. }
  14. function createGetActionResult(locals) {
  15. return (actionFn) => {
  16. if (!hasActionPayload(locals) || actionFn.toString() !== getActionQueryString(locals._actionPayload.actionName)) {
  17. return void 0;
  18. }
  19. return deserializeActionResult(locals._actionPayload.actionResult);
  20. };
  21. }
  22. function createCallAction(context) {
  23. return (baseAction, input) => {
  24. Reflect.set(context, ACTION_API_CONTEXT_SYMBOL, true);
  25. const action = baseAction.bind(context);
  26. return action(input);
  27. };
  28. }
  29. function shouldAppendForwardSlash(trailingSlash, buildFormat) {
  30. switch (trailingSlash) {
  31. case "always":
  32. return true;
  33. case "never":
  34. return false;
  35. case "ignore": {
  36. switch (buildFormat) {
  37. case "directory":
  38. return true;
  39. case "preserve":
  40. case "file":
  41. return false;
  42. }
  43. }
  44. }
  45. }
  46. function redirectIsExternal(redirect) {
  47. if (typeof redirect === "string") {
  48. return redirect.startsWith("http://") || redirect.startsWith("https://");
  49. } else {
  50. return redirect.destination.startsWith("http://") || redirect.destination.startsWith("https://");
  51. }
  52. }
  53. async function renderRedirect(renderContext) {
  54. const {
  55. request: { method },
  56. routeData
  57. } = renderContext;
  58. const { redirect, redirectRoute } = routeData;
  59. const status = redirectRoute && typeof redirect === "object" ? redirect.status : method === "GET" ? 301 : 308;
  60. const headers = { location: encodeURI(redirectRouteGenerate(renderContext)) };
  61. if (redirect && redirectIsExternal(redirect)) {
  62. if (typeof redirect === "string") {
  63. return Response.redirect(redirect, status);
  64. } else {
  65. return Response.redirect(redirect.destination, status);
  66. }
  67. }
  68. return new Response(null, { status, headers });
  69. }
  70. function redirectRouteGenerate(renderContext) {
  71. const {
  72. params,
  73. routeData: { redirect, redirectRoute }
  74. } = renderContext;
  75. if (typeof redirectRoute !== "undefined") {
  76. return redirectRoute?.generate(params) || redirectRoute?.pathname || "/";
  77. } else if (typeof redirect === "string") {
  78. if (redirectIsExternal(redirect)) {
  79. return redirect;
  80. } else {
  81. let target = redirect;
  82. for (const param of Object.keys(params)) {
  83. const paramValue = params[param];
  84. target = target.replace(`[${param}]`, paramValue).replace(`[...${param}]`, paramValue);
  85. }
  86. return target;
  87. }
  88. } else if (typeof redirect === "undefined") {
  89. return "/";
  90. }
  91. return redirect.destination;
  92. }
  93. const SERVER_ISLAND_ROUTE = "/_server-islands/[name]";
  94. const SERVER_ISLAND_COMPONENT = "_server-islands.astro";
  95. const SERVER_ISLAND_BASE_PREFIX = "_server-islands";
  96. function badRequest(reason) {
  97. return new Response(null, {
  98. status: 400,
  99. statusText: "Bad request: " + reason
  100. });
  101. }
  102. async function getRequestData(request) {
  103. switch (request.method) {
  104. case "GET": {
  105. const url = new URL(request.url);
  106. const params = url.searchParams;
  107. if (!params.has("s") || !params.has("e") || !params.has("p")) {
  108. return badRequest("Missing required query parameters.");
  109. }
  110. const rawSlots = params.get("s");
  111. try {
  112. return {
  113. componentExport: params.get("e"),
  114. encryptedProps: params.get("p"),
  115. slots: JSON.parse(rawSlots)
  116. };
  117. } catch {
  118. return badRequest("Invalid slots format.");
  119. }
  120. }
  121. case "POST": {
  122. try {
  123. const raw = await request.text();
  124. const data = JSON.parse(raw);
  125. return data;
  126. } catch {
  127. return badRequest("Request format is invalid.");
  128. }
  129. }
  130. default: {
  131. return new Response(null, { status: 405 });
  132. }
  133. }
  134. }
  135. function createEndpoint(manifest) {
  136. const page = async (result) => {
  137. const params = result.params;
  138. if (!params.name) {
  139. return new Response(null, {
  140. status: 400,
  141. statusText: "Bad request"
  142. });
  143. }
  144. const componentId = params.name;
  145. const data = await getRequestData(result.request);
  146. if (data instanceof Response) {
  147. return data;
  148. }
  149. const imp = manifest.serverIslandMap?.get(componentId);
  150. if (!imp) {
  151. return new Response(null, {
  152. status: 404,
  153. statusText: "Not found"
  154. });
  155. }
  156. const key = await manifest.key;
  157. const encryptedProps = data.encryptedProps;
  158. const propString = encryptedProps === "" ? "{}" : await decryptString(key, encryptedProps);
  159. const props = JSON.parse(propString);
  160. const componentModule = await imp();
  161. let Component = componentModule[data.componentExport];
  162. const slots = {};
  163. for (const prop in data.slots) {
  164. slots[prop] = createSlotValueFromString(data.slots[prop]);
  165. }
  166. result.response.headers.set("X-Robots-Tag", "noindex");
  167. if (isAstroComponentFactory(Component)) {
  168. const ServerIsland = Component;
  169. Component = function(...args) {
  170. return ServerIsland.apply(this, args);
  171. };
  172. Object.assign(Component, ServerIsland);
  173. Component.propagation = "self";
  174. }
  175. return renderTemplate`${renderComponent(result, "Component", Component, props, slots)}`;
  176. };
  177. page.isAstroComponentFactory = true;
  178. const instance = {
  179. default: page,
  180. partial: true
  181. };
  182. return instance;
  183. }
  184. function matchRoute(pathname, manifest) {
  185. return manifest.routes.find((route) => {
  186. return route.pattern.test(pathname) || route.fallbackRoutes.some((fallbackRoute) => fallbackRoute.pattern.test(pathname));
  187. });
  188. }
  189. const ROUTE404_RE = /^\/404\/?$/;
  190. const ROUTE500_RE = /^\/500\/?$/;
  191. function isRoute404(route) {
  192. return ROUTE404_RE.test(route);
  193. }
  194. function isRoute500(route) {
  195. return ROUTE500_RE.test(route);
  196. }
  197. function isRoute404or500(route) {
  198. return isRoute404(route.route) || isRoute500(route.route);
  199. }
  200. function isRouteServerIsland(route) {
  201. return route.component === SERVER_ISLAND_COMPONENT;
  202. }
  203. function isRequestServerIsland(request, base = "") {
  204. const url = new URL(request.url);
  205. const pathname = base === "/" ? url.pathname.slice(base.length) : url.pathname.slice(base.length + 1);
  206. return pathname.startsWith(SERVER_ISLAND_BASE_PREFIX);
  207. }
  208. function requestIs404Or500(request, base = "") {
  209. const url = new URL(request.url);
  210. const pathname = url.pathname.slice(base.length);
  211. return isRoute404(pathname) || isRoute500(pathname);
  212. }
  213. function isRouteExternalRedirect(route) {
  214. return !!(route.type === "redirect" && route.redirect && redirectIsExternal(route.redirect));
  215. }
  216. function requestHasLocale(locales) {
  217. return function(context) {
  218. return pathHasLocale(context.url.pathname, locales);
  219. };
  220. }
  221. function pathHasLocale(path, locales) {
  222. const segments = path.split("/").map(normalizeThePath);
  223. for (const segment of segments) {
  224. for (const locale of locales) {
  225. if (typeof locale === "string") {
  226. if (normalizeTheLocale(segment) === normalizeTheLocale(locale)) {
  227. return true;
  228. }
  229. } else if (segment === locale.path) {
  230. return true;
  231. }
  232. }
  233. }
  234. return false;
  235. }
  236. function getPathByLocale(locale, locales) {
  237. for (const loopLocale of locales) {
  238. if (typeof loopLocale === "string") {
  239. if (loopLocale === locale) {
  240. return loopLocale;
  241. }
  242. } else {
  243. for (const code of loopLocale.codes) {
  244. if (code === locale) {
  245. return loopLocale.path;
  246. }
  247. }
  248. }
  249. }
  250. throw new AstroError(i18nNoLocaleFoundInPath);
  251. }
  252. function normalizeTheLocale(locale) {
  253. return locale.replaceAll("_", "-").toLowerCase();
  254. }
  255. function normalizeThePath(path) {
  256. return path.endsWith(".html") ? path.slice(0, -5) : path;
  257. }
  258. function getAllCodes(locales) {
  259. const result = [];
  260. for (const loopLocale of locales) {
  261. if (typeof loopLocale === "string") {
  262. result.push(loopLocale);
  263. } else {
  264. result.push(...loopLocale.codes);
  265. }
  266. }
  267. return result;
  268. }
  269. function redirectToDefaultLocale({
  270. trailingSlash,
  271. format,
  272. base,
  273. defaultLocale
  274. }) {
  275. return function(context, statusCode) {
  276. if (shouldAppendForwardSlash(trailingSlash, format)) {
  277. return context.redirect(`${appendForwardSlash(joinPaths(base, defaultLocale))}`, statusCode);
  278. } else {
  279. return context.redirect(`${joinPaths(base, defaultLocale)}`, statusCode);
  280. }
  281. };
  282. }
  283. function notFound({ base, locales, fallback }) {
  284. return function(context, response) {
  285. if (response?.headers.get(REROUTE_DIRECTIVE_HEADER) === "no" && typeof fallback === "undefined") {
  286. return response;
  287. }
  288. const url = context.url;
  289. const isRoot = url.pathname === base + "/" || url.pathname === base;
  290. if (!(isRoot || pathHasLocale(url.pathname, locales))) {
  291. if (response) {
  292. response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
  293. return new Response(response.body, {
  294. status: 404,
  295. headers: response.headers
  296. });
  297. } else {
  298. return new Response(null, {
  299. status: 404,
  300. headers: {
  301. [REROUTE_DIRECTIVE_HEADER]: "no"
  302. }
  303. });
  304. }
  305. }
  306. return void 0;
  307. };
  308. }
  309. function redirectToFallback({
  310. fallback,
  311. locales,
  312. defaultLocale,
  313. strategy,
  314. base,
  315. fallbackType
  316. }) {
  317. return async function(context, response) {
  318. if (response.status >= 300 && fallback) {
  319. const fallbackKeys = fallback ? Object.keys(fallback) : [];
  320. const segments = context.url.pathname.split("/");
  321. const urlLocale = segments.find((segment) => {
  322. for (const locale of locales) {
  323. if (typeof locale === "string") {
  324. if (locale === segment) {
  325. return true;
  326. }
  327. } else if (locale.path === segment) {
  328. return true;
  329. }
  330. }
  331. return false;
  332. });
  333. if (urlLocale && fallbackKeys.includes(urlLocale)) {
  334. const fallbackLocale = fallback[urlLocale];
  335. const pathFallbackLocale = getPathByLocale(fallbackLocale, locales);
  336. let newPathname;
  337. if (pathFallbackLocale === defaultLocale && strategy === "pathname-prefix-other-locales") {
  338. if (context.url.pathname.includes(`${base}`)) {
  339. newPathname = context.url.pathname.replace(`/${urlLocale}`, ``);
  340. if (newPathname === "") {
  341. newPathname = "/";
  342. }
  343. } else {
  344. newPathname = context.url.pathname.replace(`/${urlLocale}`, `/`);
  345. }
  346. } else {
  347. newPathname = context.url.pathname.replace(`/${urlLocale}`, `/${pathFallbackLocale}`);
  348. }
  349. if (fallbackType === "rewrite") {
  350. return await context.rewrite(newPathname + context.url.search);
  351. } else {
  352. return context.redirect(newPathname + context.url.search);
  353. }
  354. }
  355. }
  356. return response;
  357. };
  358. }
  359. function parseLocale(header) {
  360. if (header === "*") {
  361. return [{ locale: header, qualityValue: void 0 }];
  362. }
  363. const result = [];
  364. const localeValues = header.split(",").map((str) => str.trim());
  365. for (const localeValue of localeValues) {
  366. const split = localeValue.split(";").map((str) => str.trim());
  367. const localeName = split[0];
  368. const qualityValue = split[1];
  369. if (!split) {
  370. continue;
  371. }
  372. if (qualityValue && qualityValue.startsWith("q=")) {
  373. const qualityValueAsFloat = Number.parseFloat(qualityValue.slice("q=".length));
  374. if (Number.isNaN(qualityValueAsFloat) || qualityValueAsFloat > 1) {
  375. result.push({
  376. locale: localeName,
  377. qualityValue: void 0
  378. });
  379. } else {
  380. result.push({
  381. locale: localeName,
  382. qualityValue: qualityValueAsFloat
  383. });
  384. }
  385. } else {
  386. result.push({
  387. locale: localeName,
  388. qualityValue: void 0
  389. });
  390. }
  391. }
  392. return result;
  393. }
  394. function sortAndFilterLocales(browserLocaleList, locales) {
  395. const normalizedLocales = getAllCodes(locales).map(normalizeTheLocale);
  396. return browserLocaleList.filter((browserLocale) => {
  397. if (browserLocale.locale !== "*") {
  398. return normalizedLocales.includes(normalizeTheLocale(browserLocale.locale));
  399. }
  400. return true;
  401. }).sort((a, b) => {
  402. if (a.qualityValue && b.qualityValue) {
  403. return Math.sign(b.qualityValue - a.qualityValue);
  404. }
  405. return 0;
  406. });
  407. }
  408. function computePreferredLocale(request, locales) {
  409. const acceptHeader = request.headers.get("Accept-Language");
  410. let result = void 0;
  411. if (acceptHeader) {
  412. const browserLocaleList = sortAndFilterLocales(parseLocale(acceptHeader), locales);
  413. const firstResult = browserLocaleList.at(0);
  414. if (firstResult && firstResult.locale !== "*") {
  415. for (const currentLocale of locales) {
  416. if (typeof currentLocale === "string") {
  417. if (normalizeTheLocale(currentLocale) === normalizeTheLocale(firstResult.locale)) {
  418. result = currentLocale;
  419. break;
  420. }
  421. } else {
  422. for (const currentCode of currentLocale.codes) {
  423. if (normalizeTheLocale(currentCode) === normalizeTheLocale(firstResult.locale)) {
  424. result = currentCode;
  425. break;
  426. }
  427. }
  428. }
  429. }
  430. }
  431. }
  432. return result;
  433. }
  434. function computePreferredLocaleList(request, locales) {
  435. const acceptHeader = request.headers.get("Accept-Language");
  436. let result = [];
  437. if (acceptHeader) {
  438. const browserLocaleList = sortAndFilterLocales(parseLocale(acceptHeader), locales);
  439. if (browserLocaleList.length === 1 && browserLocaleList.at(0).locale === "*") {
  440. return getAllCodes(locales);
  441. } else if (browserLocaleList.length > 0) {
  442. for (const browserLocale of browserLocaleList) {
  443. for (const loopLocale of locales) {
  444. if (typeof loopLocale === "string") {
  445. if (normalizeTheLocale(loopLocale) === normalizeTheLocale(browserLocale.locale)) {
  446. result.push(loopLocale);
  447. }
  448. } else {
  449. for (const code of loopLocale.codes) {
  450. if (code === browserLocale.locale) {
  451. result.push(code);
  452. }
  453. }
  454. }
  455. }
  456. }
  457. }
  458. }
  459. return result;
  460. }
  461. function computeCurrentLocale(pathname, locales, defaultLocale) {
  462. for (const segment of pathname.split("/").map(normalizeThePath)) {
  463. for (const locale of locales) {
  464. if (typeof locale === "string") {
  465. if (!segment.includes(locale)) continue;
  466. if (normalizeTheLocale(locale) === normalizeTheLocale(segment)) {
  467. return locale;
  468. }
  469. } else {
  470. if (locale.path === segment) {
  471. return locale.codes.at(0);
  472. } else {
  473. for (const code of locale.codes) {
  474. if (normalizeTheLocale(code) === normalizeTheLocale(segment)) {
  475. return code;
  476. }
  477. }
  478. }
  479. }
  480. }
  481. }
  482. for (const locale of locales) {
  483. if (typeof locale === "string") {
  484. if (locale === defaultLocale) {
  485. return locale;
  486. }
  487. } else {
  488. if (locale.path === defaultLocale) {
  489. return locale.codes.at(0);
  490. }
  491. }
  492. }
  493. }
  494. const DELETED_EXPIRATION = /* @__PURE__ */ new Date(0);
  495. const DELETED_VALUE = "deleted";
  496. const responseSentSymbol = Symbol.for("astro.responseSent");
  497. const identity = (value) => value;
  498. class AstroCookie {
  499. constructor(value) {
  500. this.value = value;
  501. }
  502. json() {
  503. if (this.value === void 0) {
  504. throw new Error(`Cannot convert undefined to an object.`);
  505. }
  506. return JSON.parse(this.value);
  507. }
  508. number() {
  509. return Number(this.value);
  510. }
  511. boolean() {
  512. if (this.value === "false") return false;
  513. if (this.value === "0") return false;
  514. return Boolean(this.value);
  515. }
  516. }
  517. class AstroCookies {
  518. #request;
  519. #requestValues;
  520. #outgoing;
  521. #consumed;
  522. constructor(request) {
  523. this.#request = request;
  524. this.#requestValues = null;
  525. this.#outgoing = null;
  526. this.#consumed = false;
  527. }
  528. /**
  529. * Astro.cookies.delete(key) is used to delete a cookie. Using this method will result
  530. * in a Set-Cookie header added to the response.
  531. * @param key The cookie to delete
  532. * @param options Options related to this deletion, such as the path of the cookie.
  533. */
  534. delete(key, options) {
  535. const {
  536. // @ts-expect-error
  537. maxAge: _ignoredMaxAge,
  538. // @ts-expect-error
  539. expires: _ignoredExpires,
  540. ...sanitizedOptions
  541. } = options || {};
  542. const serializeOptions = {
  543. expires: DELETED_EXPIRATION,
  544. ...sanitizedOptions
  545. };
  546. this.#ensureOutgoingMap().set(key, [
  547. DELETED_VALUE,
  548. distExports.serialize(key, DELETED_VALUE, serializeOptions),
  549. false
  550. ]);
  551. }
  552. /**
  553. * Astro.cookies.get(key) is used to get a cookie value. The cookie value is read from the
  554. * request. If you have set a cookie via Astro.cookies.set(key, value), the value will be taken
  555. * from that set call, overriding any values already part of the request.
  556. * @param key The cookie to get.
  557. * @returns An object containing the cookie value as well as convenience methods for converting its value.
  558. */
  559. get(key, options = void 0) {
  560. if (this.#outgoing?.has(key)) {
  561. let [serializedValue, , isSetValue] = this.#outgoing.get(key);
  562. if (isSetValue) {
  563. return new AstroCookie(serializedValue);
  564. } else {
  565. return void 0;
  566. }
  567. }
  568. const decode = options?.decode ?? decodeURIComponent;
  569. const values = this.#ensureParsed();
  570. if (key in values) {
  571. const value = values[key];
  572. if (value) {
  573. return new AstroCookie(decode(value));
  574. }
  575. }
  576. }
  577. /**
  578. * Astro.cookies.has(key) returns a boolean indicating whether this cookie is either
  579. * part of the initial request or set via Astro.cookies.set(key)
  580. * @param key The cookie to check for.
  581. * @param _options This parameter is no longer used.
  582. * @returns
  583. */
  584. has(key, _options) {
  585. if (this.#outgoing?.has(key)) {
  586. let [, , isSetValue] = this.#outgoing.get(key);
  587. return isSetValue;
  588. }
  589. const values = this.#ensureParsed();
  590. return values[key] !== void 0;
  591. }
  592. /**
  593. * Astro.cookies.set(key, value) is used to set a cookie's value. If provided
  594. * an object it will be stringified via JSON.stringify(value). Additionally you
  595. * can provide options customizing how this cookie will be set, such as setting httpOnly
  596. * in order to prevent the cookie from being read in client-side JavaScript.
  597. * @param key The name of the cookie to set.
  598. * @param value A value, either a string or other primitive or an object.
  599. * @param options Options for the cookie, such as the path and security settings.
  600. */
  601. set(key, value, options) {
  602. if (this.#consumed) {
  603. const warning = new Error(
  604. "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."
  605. );
  606. warning.name = "Warning";
  607. console.warn(warning);
  608. }
  609. let serializedValue;
  610. if (typeof value === "string") {
  611. serializedValue = value;
  612. } else {
  613. let toStringValue = value.toString();
  614. if (toStringValue === Object.prototype.toString.call(value)) {
  615. serializedValue = JSON.stringify(value);
  616. } else {
  617. serializedValue = toStringValue;
  618. }
  619. }
  620. const serializeOptions = {};
  621. if (options) {
  622. Object.assign(serializeOptions, options);
  623. }
  624. this.#ensureOutgoingMap().set(key, [
  625. serializedValue,
  626. distExports.serialize(key, serializedValue, serializeOptions),
  627. true
  628. ]);
  629. if (this.#request[responseSentSymbol]) {
  630. throw new AstroError({
  631. ...ResponseSentError
  632. });
  633. }
  634. }
  635. /**
  636. * Merges a new AstroCookies instance into the current instance. Any new cookies
  637. * will be added to the current instance, overwriting any existing cookies with the same name.
  638. */
  639. merge(cookies) {
  640. const outgoing = cookies.#outgoing;
  641. if (outgoing) {
  642. for (const [key, value] of outgoing) {
  643. this.#ensureOutgoingMap().set(key, value);
  644. }
  645. }
  646. }
  647. /**
  648. * Astro.cookies.header() returns an iterator for the cookies that have previously
  649. * been set by either Astro.cookies.set() or Astro.cookies.delete().
  650. * This method is primarily used by adapters to set the header on outgoing responses.
  651. * @returns
  652. */
  653. *headers() {
  654. if (this.#outgoing == null) return;
  655. for (const [, value] of this.#outgoing) {
  656. yield value[1];
  657. }
  658. }
  659. /**
  660. * Behaves the same as AstroCookies.prototype.headers(),
  661. * but allows a warning when cookies are set after the instance is consumed.
  662. */
  663. static consume(cookies) {
  664. cookies.#consumed = true;
  665. return cookies.headers();
  666. }
  667. #ensureParsed() {
  668. if (!this.#requestValues) {
  669. this.#parse();
  670. }
  671. if (!this.#requestValues) {
  672. this.#requestValues = {};
  673. }
  674. return this.#requestValues;
  675. }
  676. #ensureOutgoingMap() {
  677. if (!this.#outgoing) {
  678. this.#outgoing = /* @__PURE__ */ new Map();
  679. }
  680. return this.#outgoing;
  681. }
  682. #parse() {
  683. const raw = this.#request.headers.get("cookie");
  684. if (!raw) {
  685. return;
  686. }
  687. this.#requestValues = distExports.parse(raw, { decode: identity });
  688. }
  689. }
  690. const astroCookiesSymbol = Symbol.for("astro.cookies");
  691. function attachCookiesToResponse(response, cookies) {
  692. Reflect.set(response, astroCookiesSymbol, cookies);
  693. }
  694. function getCookiesFromResponse(response) {
  695. let cookies = Reflect.get(response, astroCookiesSymbol);
  696. if (cookies != null) {
  697. return cookies;
  698. } else {
  699. return void 0;
  700. }
  701. }
  702. function* getSetCookiesFromResponse(response) {
  703. const cookies = getCookiesFromResponse(response);
  704. if (!cookies) {
  705. return [];
  706. }
  707. for (const headerValue of AstroCookies.consume(cookies)) {
  708. yield headerValue;
  709. }
  710. return [];
  711. }
  712. function createRequest({
  713. url,
  714. headers,
  715. method = "GET",
  716. body = void 0,
  717. logger,
  718. isPrerendered = false,
  719. routePattern,
  720. init
  721. }) {
  722. const headersObj = isPrerendered ? void 0 : headers instanceof Headers ? headers : new Headers(
  723. // Filter out HTTP/2 pseudo-headers. These are internally-generated headers added to all HTTP/2 requests with trusted metadata about the request.
  724. // Examples include `:method`, `:scheme`, `:authority`, and `:path`.
  725. // 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.
  726. // See https://httpwg.org/specs/rfc7540.html#HttpRequest
  727. Object.entries(headers).filter(([name]) => !name.startsWith(":"))
  728. );
  729. if (typeof url === "string") url = new URL(url);
  730. if (isPrerendered) {
  731. url.search = "";
  732. }
  733. const request = new Request(url, {
  734. method,
  735. headers: headersObj,
  736. // body is made available only if the request is for a page that will be on-demand rendered
  737. body: isPrerendered ? null : body,
  738. ...init
  739. });
  740. if (isPrerendered) {
  741. let _headers = request.headers;
  742. const { value, writable, ...headersDesc } = Object.getOwnPropertyDescriptor(request, "headers") || {};
  743. Object.defineProperty(request, "headers", {
  744. ...headersDesc,
  745. get() {
  746. logger.warn(
  747. null,
  748. `\`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.`
  749. );
  750. return _headers;
  751. },
  752. set(newHeaders) {
  753. _headers = newHeaders;
  754. }
  755. });
  756. }
  757. return request;
  758. }
  759. function findRouteToRewrite({
  760. payload,
  761. routes,
  762. request,
  763. trailingSlash,
  764. buildFormat,
  765. base,
  766. outDir
  767. }) {
  768. let newUrl = void 0;
  769. if (payload instanceof URL) {
  770. newUrl = payload;
  771. } else if (payload instanceof Request) {
  772. newUrl = new URL(payload.url);
  773. } else {
  774. newUrl = new URL(payload, new URL(request.url).origin);
  775. }
  776. let pathname = newUrl.pathname;
  777. const shouldAppendSlash = shouldAppendForwardSlash(trailingSlash, buildFormat);
  778. if (base !== "/") {
  779. const isBasePathRequest = newUrl.pathname === base || newUrl.pathname === removeTrailingForwardSlash(base);
  780. if (isBasePathRequest) {
  781. pathname = shouldAppendSlash ? "/" : "";
  782. } else if (newUrl.pathname.startsWith(base)) {
  783. pathname = shouldAppendSlash ? appendForwardSlash(newUrl.pathname) : removeTrailingForwardSlash(newUrl.pathname);
  784. pathname = pathname.slice(base.length);
  785. }
  786. }
  787. if (!pathname.startsWith("/") && shouldAppendSlash && newUrl.pathname.endsWith("/")) {
  788. pathname = prependForwardSlash(pathname);
  789. }
  790. if (pathname === "/" && base !== "/" && !shouldAppendSlash) {
  791. pathname = "";
  792. }
  793. if (buildFormat === "file") {
  794. pathname = pathname.replace(/\.html$/, "");
  795. }
  796. if (base !== "/" && (pathname === "" || pathname === "/") && !shouldAppendSlash) {
  797. newUrl.pathname = removeTrailingForwardSlash(base);
  798. } else {
  799. newUrl.pathname = joinPaths(...[base, pathname].filter(Boolean));
  800. }
  801. const decodedPathname = decodeURI(pathname);
  802. let foundRoute;
  803. for (const route of routes) {
  804. if (route.pattern.test(decodedPathname)) {
  805. if (route.params && route.params.length !== 0 && route.distURL && route.distURL.length !== 0) {
  806. if (!route.distURL.find(
  807. (url) => url.href.replace(outDir.toString(), "").replace(/(?:\/index\.html|\.html)$/, "") == trimSlashes(decodedPathname)
  808. )) {
  809. continue;
  810. }
  811. }
  812. foundRoute = route;
  813. break;
  814. }
  815. }
  816. if (foundRoute) {
  817. return {
  818. routeData: foundRoute,
  819. newUrl,
  820. pathname: decodedPathname
  821. };
  822. } else {
  823. const custom404 = routes.find((route) => route.route === "/404");
  824. if (custom404) {
  825. return { routeData: custom404, newUrl, pathname };
  826. } else {
  827. return { routeData: DEFAULT_404_ROUTE, newUrl, pathname };
  828. }
  829. }
  830. }
  831. function copyRequest(newUrl, oldRequest, isPrerendered, logger, routePattern) {
  832. if (oldRequest.bodyUsed) {
  833. throw new AstroError(RewriteWithBodyUsed);
  834. }
  835. return createRequest({
  836. url: newUrl,
  837. method: oldRequest.method,
  838. body: oldRequest.body,
  839. isPrerendered,
  840. logger,
  841. headers: isPrerendered ? {} : oldRequest.headers,
  842. routePattern,
  843. init: {
  844. referrer: oldRequest.referrer,
  845. referrerPolicy: oldRequest.referrerPolicy,
  846. mode: oldRequest.mode,
  847. credentials: oldRequest.credentials,
  848. cache: oldRequest.cache,
  849. redirect: oldRequest.redirect,
  850. integrity: oldRequest.integrity,
  851. signal: oldRequest.signal,
  852. keepalive: oldRequest.keepalive,
  853. // https://fetch.spec.whatwg.org/#dom-request-duplex
  854. // @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
  855. duplex: "half"
  856. }
  857. });
  858. }
  859. function setOriginPathname(request, pathname, trailingSlash, buildFormat) {
  860. if (!pathname) {
  861. pathname = "/";
  862. }
  863. const shouldAppendSlash = shouldAppendForwardSlash(trailingSlash, buildFormat);
  864. let finalPathname;
  865. if (pathname === "/") {
  866. finalPathname = "/";
  867. } else if (shouldAppendSlash) {
  868. finalPathname = appendForwardSlash(pathname);
  869. } else {
  870. finalPathname = removeTrailingForwardSlash(pathname);
  871. }
  872. Reflect.set(request, originPathnameSymbol, encodeURIComponent(finalPathname));
  873. }
  874. function getOriginPathname(request) {
  875. const origin = Reflect.get(request, originPathnameSymbol);
  876. if (origin) {
  877. return decodeURIComponent(origin);
  878. }
  879. return new URL(request.url).pathname;
  880. }
  881. const VALID_PARAM_TYPES = ["string", "number", "undefined"];
  882. function validateGetStaticPathsParameter([key, value], route) {
  883. if (!VALID_PARAM_TYPES.includes(typeof value)) {
  884. throw new AstroError({
  885. ...GetStaticPathsInvalidRouteParam,
  886. message: GetStaticPathsInvalidRouteParam.message(key, value, typeof value),
  887. location: {
  888. file: route
  889. }
  890. });
  891. }
  892. }
  893. function validateDynamicRouteModule(mod, {
  894. ssr,
  895. route
  896. }) {
  897. if ((!ssr || route.prerender) && !mod.getStaticPaths) {
  898. throw new AstroError({
  899. ...GetStaticPathsRequired,
  900. location: { file: route.component }
  901. });
  902. }
  903. }
  904. function validateGetStaticPathsResult(result, logger, route) {
  905. if (!Array.isArray(result)) {
  906. throw new AstroError({
  907. ...InvalidGetStaticPathsReturn,
  908. message: InvalidGetStaticPathsReturn.message(typeof result),
  909. location: {
  910. file: route.component
  911. }
  912. });
  913. }
  914. result.forEach((pathObject) => {
  915. if (typeof pathObject === "object" && Array.isArray(pathObject) || pathObject === null) {
  916. throw new AstroError({
  917. ...InvalidGetStaticPathsEntry,
  918. message: InvalidGetStaticPathsEntry.message(
  919. Array.isArray(pathObject) ? "array" : typeof pathObject
  920. )
  921. });
  922. }
  923. if (pathObject.params === void 0 || pathObject.params === null || pathObject.params && Object.keys(pathObject.params).length === 0) {
  924. throw new AstroError({
  925. ...GetStaticPathsExpectedParams,
  926. location: {
  927. file: route.component
  928. }
  929. });
  930. }
  931. for (const [key, val] of Object.entries(pathObject.params)) {
  932. if (!(typeof val === "undefined" || typeof val === "string" || typeof val === "number")) {
  933. logger.warn(
  934. "router",
  935. `getStaticPaths() returned an invalid path param: "${key}". A string, number or undefined value was expected, but got \`${JSON.stringify(
  936. val
  937. )}\`.`
  938. );
  939. }
  940. if (typeof val === "string" && val === "") {
  941. logger.warn(
  942. "router",
  943. `getStaticPaths() returned an invalid path param: "${key}". \`undefined\` expected for an optional param, but got empty string.`
  944. );
  945. }
  946. }
  947. });
  948. }
  949. function stringifyParams(params, route) {
  950. const validatedParams = Object.entries(params).reduce((acc, next) => {
  951. validateGetStaticPathsParameter(next, route.component);
  952. const [key, value] = next;
  953. if (value !== void 0) {
  954. acc[key] = typeof value === "string" ? trimSlashes(value) : value.toString();
  955. }
  956. return acc;
  957. }, {});
  958. return route.generate(validatedParams);
  959. }
  960. function generatePaginateFunction(routeMatch, base) {
  961. return function paginateUtility(data, args = {}) {
  962. let { pageSize: _pageSize, params: _params, props: _props } = args;
  963. const pageSize = _pageSize || 10;
  964. const paramName = "page";
  965. const additionalParams = _params || {};
  966. const additionalProps = _props || {};
  967. let includesFirstPageNumber;
  968. if (routeMatch.params.includes(`...${paramName}`)) {
  969. includesFirstPageNumber = false;
  970. } else if (routeMatch.params.includes(`${paramName}`)) {
  971. includesFirstPageNumber = true;
  972. } else {
  973. throw new AstroError({
  974. ...PageNumberParamNotFound,
  975. message: PageNumberParamNotFound.message(paramName)
  976. });
  977. }
  978. const lastPage = Math.max(1, Math.ceil(data.length / pageSize));
  979. const result = [...Array(lastPage).keys()].map((num) => {
  980. const pageNum = num + 1;
  981. const start = pageSize === Infinity ? 0 : (pageNum - 1) * pageSize;
  982. const end = Math.min(start + pageSize, data.length);
  983. const params = {
  984. ...additionalParams,
  985. [paramName]: includesFirstPageNumber || pageNum > 1 ? String(pageNum) : void 0
  986. };
  987. const current = addRouteBase(routeMatch.generate({ ...params }), base);
  988. const next = pageNum === lastPage ? void 0 : addRouteBase(routeMatch.generate({ ...params, page: String(pageNum + 1) }), base);
  989. const prev = pageNum === 1 ? void 0 : addRouteBase(
  990. routeMatch.generate({
  991. ...params,
  992. page: !includesFirstPageNumber && pageNum - 1 === 1 ? void 0 : String(pageNum - 1)
  993. }),
  994. base
  995. );
  996. const first = pageNum === 1 ? void 0 : addRouteBase(
  997. routeMatch.generate({
  998. ...params,
  999. page: includesFirstPageNumber ? "1" : void 0
  1000. }),
  1001. base
  1002. );
  1003. const last = pageNum === lastPage ? void 0 : addRouteBase(routeMatch.generate({ ...params, page: String(lastPage) }), base);
  1004. return {
  1005. params,
  1006. props: {
  1007. ...additionalProps,
  1008. page: {
  1009. data: data.slice(start, end),
  1010. start,
  1011. end: end - 1,
  1012. size: pageSize,
  1013. total: data.length,
  1014. currentPage: pageNum,
  1015. lastPage,
  1016. url: { current, next, prev, first, last }
  1017. }
  1018. }
  1019. };
  1020. });
  1021. return result;
  1022. };
  1023. }
  1024. function addRouteBase(route, base) {
  1025. let routeWithBase = joinPaths(base, route);
  1026. if (routeWithBase === "") routeWithBase = "/";
  1027. return routeWithBase;
  1028. }
  1029. async function callGetStaticPaths({
  1030. mod,
  1031. route,
  1032. routeCache,
  1033. logger,
  1034. ssr,
  1035. base
  1036. }) {
  1037. const cached = routeCache.get(route);
  1038. if (!mod) {
  1039. throw new Error("This is an error caused by Astro and not your code. Please file an issue.");
  1040. }
  1041. if (cached?.staticPaths) {
  1042. return cached.staticPaths;
  1043. }
  1044. validateDynamicRouteModule(mod, { ssr, route });
  1045. if (ssr && !route.prerender) {
  1046. const entry = Object.assign([], { keyed: /* @__PURE__ */ new Map() });
  1047. routeCache.set(route, { ...cached, staticPaths: entry });
  1048. return entry;
  1049. }
  1050. let staticPaths = [];
  1051. if (!mod.getStaticPaths) {
  1052. throw new Error("Unexpected Error.");
  1053. }
  1054. staticPaths = await mod.getStaticPaths({
  1055. // Q: Why the cast?
  1056. // A: So users downstream can have nicer typings, we have to make some sacrifice in our internal typings, which necessitate a cast here
  1057. paginate: generatePaginateFunction(route, base),
  1058. routePattern: route.route
  1059. });
  1060. validateGetStaticPathsResult(staticPaths, logger, route);
  1061. const keyedStaticPaths = staticPaths;
  1062. keyedStaticPaths.keyed = /* @__PURE__ */ new Map();
  1063. for (const sp of keyedStaticPaths) {
  1064. const paramsKey = stringifyParams(sp.params, route);
  1065. keyedStaticPaths.keyed.set(paramsKey, sp);
  1066. }
  1067. routeCache.set(route, { ...cached, staticPaths: keyedStaticPaths });
  1068. return keyedStaticPaths;
  1069. }
  1070. class RouteCache {
  1071. logger;
  1072. cache = {};
  1073. runtimeMode;
  1074. constructor(logger, runtimeMode = "production") {
  1075. this.logger = logger;
  1076. this.runtimeMode = runtimeMode;
  1077. }
  1078. /** Clear the cache. */
  1079. clearAll() {
  1080. this.cache = {};
  1081. }
  1082. set(route, entry) {
  1083. const key = this.key(route);
  1084. if (this.runtimeMode === "production" && this.cache[key]?.staticPaths) {
  1085. this.logger.warn(null, `Internal Warning: route cache overwritten. (${key})`);
  1086. }
  1087. this.cache[key] = entry;
  1088. }
  1089. get(route) {
  1090. return this.cache[this.key(route)];
  1091. }
  1092. key(route) {
  1093. return `${route.route}_${route.component}`;
  1094. }
  1095. }
  1096. function findPathItemByKey(staticPaths, params, route, logger) {
  1097. const paramsKey = stringifyParams(params, route);
  1098. const matchedStaticPath = staticPaths.keyed.get(paramsKey);
  1099. if (matchedStaticPath) {
  1100. return matchedStaticPath;
  1101. }
  1102. logger.debug("router", `findPathItemByKey() - Unexpected cache miss looking for ${paramsKey}`);
  1103. }
  1104. function routeIsRedirect(route) {
  1105. return route?.type === "redirect";
  1106. }
  1107. function routeIsFallback(route) {
  1108. return route?.type === "fallback";
  1109. }
  1110. async function getProps(opts) {
  1111. const { logger, mod, routeData: route, routeCache, pathname, serverLike, base } = opts;
  1112. if (!route || route.pathname) {
  1113. return {};
  1114. }
  1115. if (routeIsRedirect(route) || routeIsFallback(route) || route.component === DEFAULT_404_COMPONENT) {
  1116. return {};
  1117. }
  1118. const staticPaths = await callGetStaticPaths({
  1119. mod,
  1120. route,
  1121. routeCache,
  1122. logger,
  1123. ssr: serverLike,
  1124. base
  1125. });
  1126. const params = getParams(route, pathname);
  1127. const matchedStaticPath = findPathItemByKey(staticPaths, params, route, logger);
  1128. if (!matchedStaticPath && (serverLike ? route.prerender : true)) {
  1129. throw new AstroError({
  1130. ...NoMatchingStaticPathFound,
  1131. message: NoMatchingStaticPathFound.message(pathname),
  1132. hint: NoMatchingStaticPathFound.hint([route.component])
  1133. });
  1134. }
  1135. if (mod) {
  1136. validatePrerenderEndpointCollision(route, mod, params);
  1137. }
  1138. const props = matchedStaticPath?.props ? { ...matchedStaticPath.props } : {};
  1139. return props;
  1140. }
  1141. function getParams(route, pathname) {
  1142. if (!route.params.length) return {};
  1143. const paramsMatch = route.pattern.exec(pathname) || route.fallbackRoutes.map((fallbackRoute) => fallbackRoute.pattern.exec(pathname)).find((x) => x);
  1144. if (!paramsMatch) return {};
  1145. const params = {};
  1146. route.params.forEach((key, i) => {
  1147. if (key.startsWith("...")) {
  1148. params[key.slice(3)] = paramsMatch[i + 1] ? paramsMatch[i + 1] : void 0;
  1149. } else {
  1150. params[key] = paramsMatch[i + 1];
  1151. }
  1152. });
  1153. return params;
  1154. }
  1155. function validatePrerenderEndpointCollision(route, mod, params) {
  1156. if (route.type === "endpoint" && mod.getStaticPaths) {
  1157. const lastSegment = route.segments[route.segments.length - 1];
  1158. const paramValues = Object.values(params);
  1159. const lastParam = paramValues[paramValues.length - 1];
  1160. if (lastSegment.length === 1 && lastSegment[0].dynamic && lastParam === void 0) {
  1161. throw new AstroError({
  1162. ...PrerenderDynamicEndpointPathCollide,
  1163. message: PrerenderDynamicEndpointPathCollide.message(route.route),
  1164. hint: PrerenderDynamicEndpointPathCollide.hint(route.component),
  1165. location: {
  1166. file: route.component
  1167. }
  1168. });
  1169. }
  1170. }
  1171. }
  1172. function getFunctionExpression(slot) {
  1173. if (!slot) return;
  1174. const expressions = slot?.expressions?.filter((e) => isRenderInstruction(e) === false);
  1175. if (expressions?.length !== 1) return;
  1176. return expressions[0];
  1177. }
  1178. class Slots {
  1179. #result;
  1180. #slots;
  1181. #logger;
  1182. constructor(result, slots, logger) {
  1183. this.#result = result;
  1184. this.#slots = slots;
  1185. this.#logger = logger;
  1186. if (slots) {
  1187. for (const key of Object.keys(slots)) {
  1188. if (this[key] !== void 0) {
  1189. throw new AstroError({
  1190. ...ReservedSlotName,
  1191. message: ReservedSlotName.message(key)
  1192. });
  1193. }
  1194. Object.defineProperty(this, key, {
  1195. get() {
  1196. return true;
  1197. },
  1198. enumerable: true
  1199. });
  1200. }
  1201. }
  1202. }
  1203. has(name) {
  1204. if (!this.#slots) return false;
  1205. return Boolean(this.#slots[name]);
  1206. }
  1207. async render(name, args = []) {
  1208. if (!this.#slots || !this.has(name)) return;
  1209. const result = this.#result;
  1210. if (!Array.isArray(args)) {
  1211. this.#logger.warn(
  1212. null,
  1213. `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"]])`
  1214. );
  1215. } else if (args.length > 0) {
  1216. const slotValue = this.#slots[name];
  1217. const component = typeof slotValue === "function" ? await slotValue(result) : await slotValue;
  1218. const expression = getFunctionExpression(component);
  1219. if (expression) {
  1220. const slot = async () => typeof expression === "function" ? expression(...args) : expression;
  1221. return await renderSlotToString(result, slot).then((res) => {
  1222. return res;
  1223. });
  1224. }
  1225. if (typeof component === "function") {
  1226. return await renderJSX(result, component(...args)).then(
  1227. (res) => res != null ? String(res) : res
  1228. );
  1229. }
  1230. }
  1231. const content = await renderSlotToString(result, this.#slots[name]);
  1232. const outHTML = chunkToString(result, content);
  1233. return outHTML;
  1234. }
  1235. }
  1236. function getActionContext(context) {
  1237. const callerInfo = getCallerInfo(context);
  1238. const actionResultAlreadySet = Boolean(context.locals._actionPayload);
  1239. let action = void 0;
  1240. if (callerInfo && context.request.method === "POST" && !actionResultAlreadySet) {
  1241. action = {
  1242. calledFrom: callerInfo.from,
  1243. name: callerInfo.name,
  1244. handler: async () => {
  1245. const pipeline = Reflect.get(context, apiContextRoutesSymbol);
  1246. const callerInfoName = shouldAppendForwardSlash(
  1247. pipeline.manifest.trailingSlash,
  1248. pipeline.manifest.buildFormat
  1249. ) ? removeTrailingForwardSlash(callerInfo.name) : callerInfo.name;
  1250. const baseAction = await pipeline.getAction(callerInfoName);
  1251. let input;
  1252. try {
  1253. input = await parseRequestBody(context.request);
  1254. } catch (e) {
  1255. if (e instanceof TypeError) {
  1256. return { data: void 0, error: new ActionError({ code: "UNSUPPORTED_MEDIA_TYPE" }) };
  1257. }
  1258. throw e;
  1259. }
  1260. const omitKeys = ["props", "getActionResult", "callAction", "redirect"];
  1261. const actionAPIContext = Object.create(
  1262. Object.getPrototypeOf(context),
  1263. Object.fromEntries(
  1264. Object.entries(Object.getOwnPropertyDescriptors(context)).filter(
  1265. ([key]) => !omitKeys.includes(key)
  1266. )
  1267. )
  1268. );
  1269. Reflect.set(actionAPIContext, ACTION_API_CONTEXT_SYMBOL, true);
  1270. const handler = baseAction.bind(actionAPIContext);
  1271. return handler(input);
  1272. }
  1273. };
  1274. }
  1275. function setActionResult(actionName, actionResult) {
  1276. context.locals._actionPayload = {
  1277. actionResult,
  1278. actionName
  1279. };
  1280. }
  1281. return {
  1282. action,
  1283. setActionResult,
  1284. serializeActionResult,
  1285. deserializeActionResult
  1286. };
  1287. }
  1288. function getCallerInfo(ctx) {
  1289. if (ctx.routePattern === ACTION_RPC_ROUTE_PATTERN) {
  1290. return { from: "rpc", name: ctx.url.pathname.replace(/^.*\/_actions\//, "") };
  1291. }
  1292. const queryParam = ctx.url.searchParams.get(ACTION_QUERY_PARAMS.actionName);
  1293. if (queryParam) {
  1294. return { from: "form", name: queryParam };
  1295. }
  1296. return void 0;
  1297. }
  1298. async function parseRequestBody(request) {
  1299. const contentType = request.headers.get("content-type");
  1300. const contentLength = request.headers.get("Content-Length");
  1301. if (!contentType) return void 0;
  1302. if (hasContentType(contentType, formContentTypes)) {
  1303. return await request.clone().formData();
  1304. }
  1305. if (hasContentType(contentType, ["application/json"])) {
  1306. return contentLength === "0" ? void 0 : await request.clone().json();
  1307. }
  1308. throw new TypeError("Unsupported content type");
  1309. }
  1310. async function callMiddleware(onRequest, apiContext, responseFunction) {
  1311. let nextCalled = false;
  1312. let responseFunctionPromise = void 0;
  1313. const next = async (payload) => {
  1314. nextCalled = true;
  1315. responseFunctionPromise = responseFunction(apiContext, payload);
  1316. return responseFunctionPromise;
  1317. };
  1318. let middlewarePromise = onRequest(apiContext, next);
  1319. return await Promise.resolve(middlewarePromise).then(async (value) => {
  1320. if (nextCalled) {
  1321. if (typeof value !== "undefined") {
  1322. if (value instanceof Response === false) {
  1323. throw new AstroError(MiddlewareNotAResponse);
  1324. }
  1325. return value;
  1326. } else {
  1327. if (responseFunctionPromise) {
  1328. return responseFunctionPromise;
  1329. } else {
  1330. throw new AstroError(MiddlewareNotAResponse);
  1331. }
  1332. }
  1333. } else if (typeof value === "undefined") {
  1334. throw new AstroError(MiddlewareNoDataOrNextCalled);
  1335. } else if (value instanceof Response === false) {
  1336. throw new AstroError(MiddlewareNotAResponse);
  1337. } else {
  1338. return value;
  1339. }
  1340. });
  1341. }
  1342. const suspectProtoRx = /"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/;
  1343. const suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;
  1344. const JsonSigRx = /^\s*["[{]|^\s*-?\d{1,16}(\.\d{1,17})?([Ee][+-]?\d+)?\s*$/;
  1345. function jsonParseTransform(key, value) {
  1346. if (key === "__proto__" || key === "constructor" && value && typeof value === "object" && "prototype" in value) {
  1347. warnKeyDropped(key);
  1348. return;
  1349. }
  1350. return value;
  1351. }
  1352. function warnKeyDropped(key) {
  1353. console.warn(`[destr] Dropping "${key}" key to prevent prototype pollution.`);
  1354. }
  1355. function destr(value, options = {}) {
  1356. if (typeof value !== "string") {
  1357. return value;
  1358. }
  1359. if (value[0] === '"' && value[value.length - 1] === '"' && value.indexOf("\\") === -1) {
  1360. return value.slice(1, -1);
  1361. }
  1362. const _value = value.trim();
  1363. if (_value.length <= 9) {
  1364. switch (_value.toLowerCase()) {
  1365. case "true": {
  1366. return true;
  1367. }
  1368. case "false": {
  1369. return false;
  1370. }
  1371. case "undefined": {
  1372. return void 0;
  1373. }
  1374. case "null": {
  1375. return null;
  1376. }
  1377. case "nan": {
  1378. return Number.NaN;
  1379. }
  1380. case "infinity": {
  1381. return Number.POSITIVE_INFINITY;
  1382. }
  1383. case "-infinity": {
  1384. return Number.NEGATIVE_INFINITY;
  1385. }
  1386. }
  1387. }
  1388. if (!JsonSigRx.test(value)) {
  1389. if (options.strict) {
  1390. throw new SyntaxError("[destr] Invalid JSON");
  1391. }
  1392. return value;
  1393. }
  1394. try {
  1395. if (suspectProtoRx.test(value) || suspectConstructorRx.test(value)) {
  1396. if (options.strict) {
  1397. throw new Error("[destr] Possible prototype pollution");
  1398. }
  1399. return JSON.parse(value, jsonParseTransform);
  1400. }
  1401. return JSON.parse(value);
  1402. } catch (error) {
  1403. if (options.strict) {
  1404. throw error;
  1405. }
  1406. return value;
  1407. }
  1408. }
  1409. function wrapToPromise(value) {
  1410. if (!value || typeof value.then !== "function") {
  1411. return Promise.resolve(value);
  1412. }
  1413. return value;
  1414. }
  1415. function asyncCall(function_, ...arguments_) {
  1416. try {
  1417. return wrapToPromise(function_(...arguments_));
  1418. } catch (error) {
  1419. return Promise.reject(error);
  1420. }
  1421. }
  1422. function isPrimitive(value) {
  1423. const type = typeof value;
  1424. return value === null || type !== "object" && type !== "function";
  1425. }
  1426. function isPureObject(value) {
  1427. const proto = Object.getPrototypeOf(value);
  1428. return !proto || proto.isPrototypeOf(Object);
  1429. }
  1430. function stringify$1(value) {
  1431. if (isPrimitive(value)) {
  1432. return String(value);
  1433. }
  1434. if (isPureObject(value) || Array.isArray(value)) {
  1435. return JSON.stringify(value);
  1436. }
  1437. if (typeof value.toJSON === "function") {
  1438. return stringify$1(value.toJSON());
  1439. }
  1440. throw new Error("[unstorage] Cannot stringify value!");
  1441. }
  1442. const BASE64_PREFIX = "base64:";
  1443. function serializeRaw(value) {
  1444. if (typeof value === "string") {
  1445. return value;
  1446. }
  1447. return BASE64_PREFIX + base64Encode(value);
  1448. }
  1449. function deserializeRaw(value) {
  1450. if (typeof value !== "string") {
  1451. return value;
  1452. }
  1453. if (!value.startsWith(BASE64_PREFIX)) {
  1454. return value;
  1455. }
  1456. return base64Decode(value.slice(BASE64_PREFIX.length));
  1457. }
  1458. function base64Decode(input) {
  1459. if (globalThis.Buffer) {
  1460. return Buffer.from(input, "base64");
  1461. }
  1462. return Uint8Array.from(
  1463. globalThis.atob(input),
  1464. (c) => c.codePointAt(0)
  1465. );
  1466. }
  1467. function base64Encode(input) {
  1468. if (globalThis.Buffer) {
  1469. return Buffer.from(input).toString("base64");
  1470. }
  1471. return globalThis.btoa(String.fromCodePoint(...input));
  1472. }
  1473. function normalizeKey(key) {
  1474. if (!key) {
  1475. return "";
  1476. }
  1477. return key.split("?")[0]?.replace(/[/\\]/g, ":").replace(/:+/g, ":").replace(/^:|:$/g, "") || "";
  1478. }
  1479. function joinKeys(...keys) {
  1480. return normalizeKey(keys.join(":"));
  1481. }
  1482. function normalizeBaseKey(base) {
  1483. base = normalizeKey(base);
  1484. return base ? base + ":" : "";
  1485. }
  1486. function filterKeyByDepth(key, depth) {
  1487. if (depth === void 0) {
  1488. return true;
  1489. }
  1490. let substrCount = 0;
  1491. let index = key.indexOf(":");
  1492. while (index > -1) {
  1493. substrCount++;
  1494. index = key.indexOf(":", index + 1);
  1495. }
  1496. return substrCount <= depth;
  1497. }
  1498. function filterKeyByBase(key, base) {
  1499. if (base) {
  1500. return key.startsWith(base) && key[key.length - 1] !== "$";
  1501. }
  1502. return key[key.length - 1] !== "$";
  1503. }
  1504. function defineDriver(factory) {
  1505. return factory;
  1506. }
  1507. const DRIVER_NAME = "memory";
  1508. const memory = defineDriver(() => {
  1509. const data = /* @__PURE__ */ new Map();
  1510. return {
  1511. name: DRIVER_NAME,
  1512. getInstance: () => data,
  1513. hasItem(key) {
  1514. return data.has(key);
  1515. },
  1516. getItem(key) {
  1517. return data.get(key) ?? null;
  1518. },
  1519. getItemRaw(key) {
  1520. return data.get(key) ?? null;
  1521. },
  1522. setItem(key, value) {
  1523. data.set(key, value);
  1524. },
  1525. setItemRaw(key, value) {
  1526. data.set(key, value);
  1527. },
  1528. removeItem(key) {
  1529. data.delete(key);
  1530. },
  1531. getKeys() {
  1532. return [...data.keys()];
  1533. },
  1534. clear() {
  1535. data.clear();
  1536. },
  1537. dispose() {
  1538. data.clear();
  1539. }
  1540. };
  1541. });
  1542. function createStorage(options = {}) {
  1543. const context = {
  1544. mounts: { "": options.driver || memory() },
  1545. mountpoints: [""],
  1546. watching: false,
  1547. watchListeners: [],
  1548. unwatch: {}
  1549. };
  1550. const getMount = (key) => {
  1551. for (const base of context.mountpoints) {
  1552. if (key.startsWith(base)) {
  1553. return {
  1554. base,
  1555. relativeKey: key.slice(base.length),
  1556. driver: context.mounts[base]
  1557. };
  1558. }
  1559. }
  1560. return {
  1561. base: "",
  1562. relativeKey: key,
  1563. driver: context.mounts[""]
  1564. };
  1565. };
  1566. const getMounts = (base, includeParent) => {
  1567. return context.mountpoints.filter(
  1568. (mountpoint) => mountpoint.startsWith(base) || includeParent && base.startsWith(mountpoint)
  1569. ).map((mountpoint) => ({
  1570. relativeBase: base.length > mountpoint.length ? base.slice(mountpoint.length) : void 0,
  1571. mountpoint,
  1572. driver: context.mounts[mountpoint]
  1573. }));
  1574. };
  1575. const onChange = (event, key) => {
  1576. if (!context.watching) {
  1577. return;
  1578. }
  1579. key = normalizeKey(key);
  1580. for (const listener of context.watchListeners) {
  1581. listener(event, key);
  1582. }
  1583. };
  1584. const startWatch = async () => {
  1585. if (context.watching) {
  1586. return;
  1587. }
  1588. context.watching = true;
  1589. for (const mountpoint in context.mounts) {
  1590. context.unwatch[mountpoint] = await watch(
  1591. context.mounts[mountpoint],
  1592. onChange,
  1593. mountpoint
  1594. );
  1595. }
  1596. };
  1597. const stopWatch = async () => {
  1598. if (!context.watching) {
  1599. return;
  1600. }
  1601. for (const mountpoint in context.unwatch) {
  1602. await context.unwatch[mountpoint]();
  1603. }
  1604. context.unwatch = {};
  1605. context.watching = false;
  1606. };
  1607. const runBatch = (items, commonOptions, cb) => {
  1608. const batches = /* @__PURE__ */ new Map();
  1609. const getBatch = (mount) => {
  1610. let batch = batches.get(mount.base);
  1611. if (!batch) {
  1612. batch = {
  1613. driver: mount.driver,
  1614. base: mount.base,
  1615. items: []
  1616. };
  1617. batches.set(mount.base, batch);
  1618. }
  1619. return batch;
  1620. };
  1621. for (const item of items) {
  1622. const isStringItem = typeof item === "string";
  1623. const key = normalizeKey(isStringItem ? item : item.key);
  1624. const value = isStringItem ? void 0 : item.value;
  1625. const options2 = isStringItem || !item.options ? commonOptions : { ...commonOptions, ...item.options };
  1626. const mount = getMount(key);
  1627. getBatch(mount).items.push({
  1628. key,
  1629. value,
  1630. relativeKey: mount.relativeKey,
  1631. options: options2
  1632. });
  1633. }
  1634. return Promise.all([...batches.values()].map((batch) => cb(batch))).then(
  1635. (r) => r.flat()
  1636. );
  1637. };
  1638. const storage = {
  1639. // Item
  1640. hasItem(key, opts = {}) {
  1641. key = normalizeKey(key);
  1642. const { relativeKey, driver } = getMount(key);
  1643. return asyncCall(driver.hasItem, relativeKey, opts);
  1644. },
  1645. getItem(key, opts = {}) {
  1646. key = normalizeKey(key);
  1647. const { relativeKey, driver } = getMount(key);
  1648. return asyncCall(driver.getItem, relativeKey, opts).then(
  1649. (value) => destr(value)
  1650. );
  1651. },
  1652. getItems(items, commonOptions = {}) {
  1653. return runBatch(items, commonOptions, (batch) => {
  1654. if (batch.driver.getItems) {
  1655. return asyncCall(
  1656. batch.driver.getItems,
  1657. batch.items.map((item) => ({
  1658. key: item.relativeKey,
  1659. options: item.options
  1660. })),
  1661. commonOptions
  1662. ).then(
  1663. (r) => r.map((item) => ({
  1664. key: joinKeys(batch.base, item.key),
  1665. value: destr(item.value)
  1666. }))
  1667. );
  1668. }
  1669. return Promise.all(
  1670. batch.items.map((item) => {
  1671. return asyncCall(
  1672. batch.driver.getItem,
  1673. item.relativeKey,
  1674. item.options
  1675. ).then((value) => ({
  1676. key: item.key,
  1677. value: destr(value)
  1678. }));
  1679. })
  1680. );
  1681. });
  1682. },
  1683. getItemRaw(key, opts = {}) {
  1684. key = normalizeKey(key);
  1685. const { relativeKey, driver } = getMount(key);
  1686. if (driver.getItemRaw) {
  1687. return asyncCall(driver.getItemRaw, relativeKey, opts);
  1688. }
  1689. return asyncCall(driver.getItem, relativeKey, opts).then(
  1690. (value) => deserializeRaw(value)
  1691. );
  1692. },
  1693. async setItem(key, value, opts = {}) {
  1694. if (value === void 0) {
  1695. return storage.removeItem(key);
  1696. }
  1697. key = normalizeKey(key);
  1698. const { relativeKey, driver } = getMount(key);
  1699. if (!driver.setItem) {
  1700. return;
  1701. }
  1702. await asyncCall(driver.setItem, relativeKey, stringify$1(value), opts);
  1703. if (!driver.watch) {
  1704. onChange("update", key);
  1705. }
  1706. },
  1707. async setItems(items, commonOptions) {
  1708. await runBatch(items, commonOptions, async (batch) => {
  1709. if (batch.driver.setItems) {
  1710. return asyncCall(
  1711. batch.driver.setItems,
  1712. batch.items.map((item) => ({
  1713. key: item.relativeKey,
  1714. value: stringify$1(item.value),
  1715. options: item.options
  1716. })),
  1717. commonOptions
  1718. );
  1719. }
  1720. if (!batch.driver.setItem) {
  1721. return;
  1722. }
  1723. await Promise.all(
  1724. batch.items.map((item) => {
  1725. return asyncCall(
  1726. batch.driver.setItem,
  1727. item.relativeKey,
  1728. stringify$1(item.value),
  1729. item.options
  1730. );
  1731. })
  1732. );
  1733. });
  1734. },
  1735. async setItemRaw(key, value, opts = {}) {
  1736. if (value === void 0) {
  1737. return storage.removeItem(key, opts);
  1738. }
  1739. key = normalizeKey(key);
  1740. const { relativeKey, driver } = getMount(key);
  1741. if (driver.setItemRaw) {
  1742. await asyncCall(driver.setItemRaw, relativeKey, value, opts);
  1743. } else if (driver.setItem) {
  1744. await asyncCall(driver.setItem, relativeKey, serializeRaw(value), opts);
  1745. } else {
  1746. return;
  1747. }
  1748. if (!driver.watch) {
  1749. onChange("update", key);
  1750. }
  1751. },
  1752. async removeItem(key, opts = {}) {
  1753. if (typeof opts === "boolean") {
  1754. opts = { removeMeta: opts };
  1755. }
  1756. key = normalizeKey(key);
  1757. const { relativeKey, driver } = getMount(key);
  1758. if (!driver.removeItem) {
  1759. return;
  1760. }
  1761. await asyncCall(driver.removeItem, relativeKey, opts);
  1762. if (opts.removeMeta || opts.removeMata) {
  1763. await asyncCall(driver.removeItem, relativeKey + "$", opts);
  1764. }
  1765. if (!driver.watch) {
  1766. onChange("remove", key);
  1767. }
  1768. },
  1769. // Meta
  1770. async getMeta(key, opts = {}) {
  1771. if (typeof opts === "boolean") {
  1772. opts = { nativeOnly: opts };
  1773. }
  1774. key = normalizeKey(key);
  1775. const { relativeKey, driver } = getMount(key);
  1776. const meta = /* @__PURE__ */ Object.create(null);
  1777. if (driver.getMeta) {
  1778. Object.assign(meta, await asyncCall(driver.getMeta, relativeKey, opts));
  1779. }
  1780. if (!opts.nativeOnly) {
  1781. const value = await asyncCall(
  1782. driver.getItem,
  1783. relativeKey + "$",
  1784. opts
  1785. ).then((value_) => destr(value_));
  1786. if (value && typeof value === "object") {
  1787. if (typeof value.atime === "string") {
  1788. value.atime = new Date(value.atime);
  1789. }
  1790. if (typeof value.mtime === "string") {
  1791. value.mtime = new Date(value.mtime);
  1792. }
  1793. Object.assign(meta, value);
  1794. }
  1795. }
  1796. return meta;
  1797. },
  1798. setMeta(key, value, opts = {}) {
  1799. return this.setItem(key + "$", value, opts);
  1800. },
  1801. removeMeta(key, opts = {}) {
  1802. return this.removeItem(key + "$", opts);
  1803. },
  1804. // Keys
  1805. async getKeys(base, opts = {}) {
  1806. base = normalizeBaseKey(base);
  1807. const mounts = getMounts(base, true);
  1808. let maskedMounts = [];
  1809. const allKeys = [];
  1810. let allMountsSupportMaxDepth = true;
  1811. for (const mount of mounts) {
  1812. if (!mount.driver.flags?.maxDepth) {
  1813. allMountsSupportMaxDepth = false;
  1814. }
  1815. const rawKeys = await asyncCall(
  1816. mount.driver.getKeys,
  1817. mount.relativeBase,
  1818. opts
  1819. );
  1820. for (const key of rawKeys) {
  1821. const fullKey = mount.mountpoint + normalizeKey(key);
  1822. if (!maskedMounts.some((p) => fullKey.startsWith(p))) {
  1823. allKeys.push(fullKey);
  1824. }
  1825. }
  1826. maskedMounts = [
  1827. mount.mountpoint,
  1828. ...maskedMounts.filter((p) => !p.startsWith(mount.mountpoint))
  1829. ];
  1830. }
  1831. const shouldFilterByDepth = opts.maxDepth !== void 0 && !allMountsSupportMaxDepth;
  1832. return allKeys.filter(
  1833. (key) => (!shouldFilterByDepth || filterKeyByDepth(key, opts.maxDepth)) && filterKeyByBase(key, base)
  1834. );
  1835. },
  1836. // Utils
  1837. async clear(base, opts = {}) {
  1838. base = normalizeBaseKey(base);
  1839. await Promise.all(
  1840. getMounts(base, false).map(async (m) => {
  1841. if (m.driver.clear) {
  1842. return asyncCall(m.driver.clear, m.relativeBase, opts);
  1843. }
  1844. if (m.driver.removeItem) {
  1845. const keys = await m.driver.getKeys(m.relativeBase || "", opts);
  1846. return Promise.all(
  1847. keys.map((key) => m.driver.removeItem(key, opts))
  1848. );
  1849. }
  1850. })
  1851. );
  1852. },
  1853. async dispose() {
  1854. await Promise.all(
  1855. Object.values(context.mounts).map((driver) => dispose(driver))
  1856. );
  1857. },
  1858. async watch(callback) {
  1859. await startWatch();
  1860. context.watchListeners.push(callback);
  1861. return async () => {
  1862. context.watchListeners = context.watchListeners.filter(
  1863. (listener) => listener !== callback
  1864. );
  1865. if (context.watchListeners.length === 0) {
  1866. await stopWatch();
  1867. }
  1868. };
  1869. },
  1870. async unwatch() {
  1871. context.watchListeners = [];
  1872. await stopWatch();
  1873. },
  1874. // Mount
  1875. mount(base, driver) {
  1876. base = normalizeBaseKey(base);
  1877. if (base && context.mounts[base]) {
  1878. throw new Error(`already mounted at ${base}`);
  1879. }
  1880. if (base) {
  1881. context.mountpoints.push(base);
  1882. context.mountpoints.sort((a, b) => b.length - a.length);
  1883. }
  1884. context.mounts[base] = driver;
  1885. if (context.watching) {
  1886. Promise.resolve(watch(driver, onChange, base)).then((unwatcher) => {
  1887. context.unwatch[base] = unwatcher;
  1888. }).catch(console.error);
  1889. }
  1890. return storage;
  1891. },
  1892. async unmount(base, _dispose = true) {
  1893. base = normalizeBaseKey(base);
  1894. if (!base || !context.mounts[base]) {
  1895. return;
  1896. }
  1897. if (context.watching && base in context.unwatch) {
  1898. context.unwatch[base]?.();
  1899. delete context.unwatch[base];
  1900. }
  1901. if (_dispose) {
  1902. await dispose(context.mounts[base]);
  1903. }
  1904. context.mountpoints = context.mountpoints.filter((key) => key !== base);
  1905. delete context.mounts[base];
  1906. },
  1907. getMount(key = "") {
  1908. key = normalizeKey(key) + ":";
  1909. const m = getMount(key);
  1910. return {
  1911. driver: m.driver,
  1912. base: m.base
  1913. };
  1914. },
  1915. getMounts(base = "", opts = {}) {
  1916. base = normalizeKey(base);
  1917. const mounts = getMounts(base, opts.parents);
  1918. return mounts.map((m) => ({
  1919. driver: m.driver,
  1920. base: m.mountpoint
  1921. }));
  1922. },
  1923. // Aliases
  1924. keys: (base, opts = {}) => storage.getKeys(base, opts),
  1925. get: (key, opts = {}) => storage.getItem(key, opts),
  1926. set: (key, value, opts = {}) => storage.setItem(key, value, opts),
  1927. has: (key, opts = {}) => storage.hasItem(key, opts),
  1928. del: (key, opts = {}) => storage.removeItem(key, opts),
  1929. remove: (key, opts = {}) => storage.removeItem(key, opts)
  1930. };
  1931. return storage;
  1932. }
  1933. function watch(driver, onChange, base) {
  1934. return driver.watch ? driver.watch((event, key) => onChange(event, base + key)) : () => {
  1935. };
  1936. }
  1937. async function dispose(driver) {
  1938. if (typeof driver.dispose === "function") {
  1939. await asyncCall(driver.dispose);
  1940. }
  1941. }
  1942. const builtinDrivers = {
  1943. "azure-app-configuration": "unstorage/drivers/azure-app-configuration",
  1944. "azureAppConfiguration": "unstorage/drivers/azure-app-configuration",
  1945. "azure-cosmos": "unstorage/drivers/azure-cosmos",
  1946. "azureCosmos": "unstorage/drivers/azure-cosmos",
  1947. "azure-key-vault": "unstorage/drivers/azure-key-vault",
  1948. "azureKeyVault": "unstorage/drivers/azure-key-vault",
  1949. "azure-storage-blob": "unstorage/drivers/azure-storage-blob",
  1950. "azureStorageBlob": "unstorage/drivers/azure-storage-blob",
  1951. "azure-storage-table": "unstorage/drivers/azure-storage-table",
  1952. "azureStorageTable": "unstorage/drivers/azure-storage-table",
  1953. "capacitor-preferences": "unstorage/drivers/capacitor-preferences",
  1954. "capacitorPreferences": "unstorage/drivers/capacitor-preferences",
  1955. "cloudflare-kv-binding": "unstorage/drivers/cloudflare-kv-binding",
  1956. "cloudflareKVBinding": "unstorage/drivers/cloudflare-kv-binding",
  1957. "cloudflare-kv-http": "unstorage/drivers/cloudflare-kv-http",
  1958. "cloudflareKVHttp": "unstorage/drivers/cloudflare-kv-http",
  1959. "cloudflare-r2-binding": "unstorage/drivers/cloudflare-r2-binding",
  1960. "cloudflareR2Binding": "unstorage/drivers/cloudflare-r2-binding",
  1961. "db0": "unstorage/drivers/db0",
  1962. "deno-kv-node": "unstorage/drivers/deno-kv-node",
  1963. "denoKVNode": "unstorage/drivers/deno-kv-node",
  1964. "deno-kv": "unstorage/drivers/deno-kv",
  1965. "denoKV": "unstorage/drivers/deno-kv",
  1966. "fs-lite": "unstorage/drivers/fs-lite",
  1967. "fsLite": "unstorage/drivers/fs-lite",
  1968. "fs": "unstorage/drivers/fs",
  1969. "github": "unstorage/drivers/github",
  1970. "http": "unstorage/drivers/http",
  1971. "indexedb": "unstorage/drivers/indexedb",
  1972. "localstorage": "unstorage/drivers/localstorage",
  1973. "lru-cache": "unstorage/drivers/lru-cache",
  1974. "lruCache": "unstorage/drivers/lru-cache",
  1975. "memory": "unstorage/drivers/memory",
  1976. "mongodb": "unstorage/drivers/mongodb",
  1977. "netlify-blobs": "unstorage/drivers/netlify-blobs",
  1978. "netlifyBlobs": "unstorage/drivers/netlify-blobs",
  1979. "null": "unstorage/drivers/null",
  1980. "overlay": "unstorage/drivers/overlay",
  1981. "planetscale": "unstorage/drivers/planetscale",
  1982. "redis": "unstorage/drivers/redis",
  1983. "s3": "unstorage/drivers/s3",
  1984. "session-storage": "unstorage/drivers/session-storage",
  1985. "sessionStorage": "unstorage/drivers/session-storage",
  1986. "uploadthing": "unstorage/drivers/uploadthing",
  1987. "upstash": "unstorage/drivers/upstash",
  1988. "vercel-blob": "unstorage/drivers/vercel-blob",
  1989. "vercelBlob": "unstorage/drivers/vercel-blob",
  1990. "vercel-kv": "unstorage/drivers/vercel-kv",
  1991. "vercelKV": "unstorage/drivers/vercel-kv",
  1992. "vercel-runtime-cache": "unstorage/drivers/vercel-runtime-cache",
  1993. "vercelRuntimeCache": "unstorage/drivers/vercel-runtime-cache"
  1994. };
  1995. const PERSIST_SYMBOL = Symbol();
  1996. const DEFAULT_COOKIE_NAME = "astro-session";
  1997. const VALID_COOKIE_REGEX = /^[\w-]+$/;
  1998. const unflatten = (parsed, _) => {
  1999. return unflatten$1(parsed, {
  2000. URL: (href) => new URL(href)
  2001. });
  2002. };
  2003. const stringify = (data, _) => {
  2004. return stringify$2(data, {
  2005. // Support URL objects
  2006. URL: (val) => val instanceof URL && val.href
  2007. });
  2008. };
  2009. class AstroSession {
  2010. // The cookies object.
  2011. #cookies;
  2012. // The session configuration.
  2013. #config;
  2014. // The cookie config
  2015. #cookieConfig;
  2016. // The cookie name
  2017. #cookieName;
  2018. // The unstorage object for the session driver.
  2019. #storage;
  2020. #data;
  2021. // The session ID. A v4 UUID.
  2022. #sessionID;
  2023. // Sessions to destroy. Needed because we won't have the old session ID after it's destroyed locally.
  2024. #toDestroy = /* @__PURE__ */ new Set();
  2025. // Session keys to delete. Used for partial data sets to avoid overwriting the deleted value.
  2026. #toDelete = /* @__PURE__ */ new Set();
  2027. // Whether the session is dirty and needs to be saved.
  2028. #dirty = false;
  2029. // Whether the session cookie has been set.
  2030. #cookieSet = false;
  2031. // The local data is "partial" if it has not been loaded from storage yet and only
  2032. // contains values that have been set or deleted in-memory locally.
  2033. // We do this to avoid the need to block on loading data when it is only being set.
  2034. // When we load the data from storage, we need to merge it with the local partial data,
  2035. // preserving in-memory changes and deletions.
  2036. #partial = true;
  2037. static #sharedStorage = /* @__PURE__ */ new Map();
  2038. constructor(cookies, {
  2039. cookie: cookieConfig = DEFAULT_COOKIE_NAME,
  2040. ...config
  2041. }, runtimeMode) {
  2042. const { driver } = config;
  2043. if (!driver) {
  2044. throw new AstroError({
  2045. ...SessionStorageInitError,
  2046. message: SessionStorageInitError.message(
  2047. "No driver was defined in the session configuration and the adapter did not provide a default driver."
  2048. )
  2049. });
  2050. }
  2051. this.#cookies = cookies;
  2052. let cookieConfigObject;
  2053. if (typeof cookieConfig === "object") {
  2054. const { name = DEFAULT_COOKIE_NAME, ...rest } = cookieConfig;
  2055. this.#cookieName = name;
  2056. cookieConfigObject = rest;
  2057. } else {
  2058. this.#cookieName = cookieConfig || DEFAULT_COOKIE_NAME;
  2059. }
  2060. this.#cookieConfig = {
  2061. sameSite: "lax",
  2062. secure: runtimeMode === "production",
  2063. path: "/",
  2064. ...cookieConfigObject,
  2065. httpOnly: true
  2066. };
  2067. this.#config = { ...config, driver };
  2068. }
  2069. /**
  2070. * Gets a session value. Returns `undefined` if the session or value does not exist.
  2071. */
  2072. async get(key) {
  2073. return (await this.#ensureData()).get(key)?.data;
  2074. }
  2075. /**
  2076. * Checks if a session value exists.
  2077. */
  2078. async has(key) {
  2079. return (await this.#ensureData()).has(key);
  2080. }
  2081. /**
  2082. * Gets all session values.
  2083. */
  2084. async keys() {
  2085. return (await this.#ensureData()).keys();
  2086. }
  2087. /**
  2088. * Gets all session values.
  2089. */
  2090. async values() {
  2091. return [...(await this.#ensureData()).values()].map((entry) => entry.data);
  2092. }
  2093. /**
  2094. * Gets all session entries.
  2095. */
  2096. async entries() {
  2097. return [...(await this.#ensureData()).entries()].map(([key, entry]) => [key, entry.data]);
  2098. }
  2099. /**
  2100. * Deletes a session value.
  2101. */
  2102. delete(key) {
  2103. this.#data?.delete(key);
  2104. if (this.#partial) {
  2105. this.#toDelete.add(key);
  2106. }
  2107. this.#dirty = true;
  2108. }
  2109. /**
  2110. * Sets a session value. The session is created if it does not exist.
  2111. */
  2112. set(key, value, { ttl } = {}) {
  2113. if (!key) {
  2114. throw new AstroError({
  2115. ...SessionStorageSaveError,
  2116. message: "The session key was not provided."
  2117. });
  2118. }
  2119. let cloned;
  2120. try {
  2121. cloned = unflatten(JSON.parse(stringify(value)));
  2122. } catch (err) {
  2123. throw new AstroError(
  2124. {
  2125. ...SessionStorageSaveError,
  2126. message: `The session data for ${key} could not be serialized.`,
  2127. hint: "See the devalue library for all supported types: https://github.com/rich-harris/devalue"
  2128. },
  2129. { cause: err }
  2130. );
  2131. }
  2132. if (!this.#cookieSet) {
  2133. this.#setCookie();
  2134. this.#cookieSet = true;
  2135. }
  2136. this.#data ??= /* @__PURE__ */ new Map();
  2137. const lifetime = ttl ?? this.#config.ttl;
  2138. const expires = typeof lifetime === "number" ? Date.now() + lifetime * 1e3 : lifetime;
  2139. this.#data.set(key, {
  2140. data: cloned,
  2141. expires
  2142. });
  2143. this.#dirty = true;
  2144. }
  2145. /**
  2146. * Destroys the session, clearing the cookie and storage if it exists.
  2147. */
  2148. destroy() {
  2149. const sessionId = this.#sessionID ?? this.#cookies.get(this.#cookieName)?.value;
  2150. if (sessionId) {
  2151. this.#toDestroy.add(sessionId);
  2152. }
  2153. this.#cookies.delete(this.#cookieName, this.#cookieConfig);
  2154. this.#sessionID = void 0;
  2155. this.#data = void 0;
  2156. this.#dirty = true;
  2157. }
  2158. /**
  2159. * Regenerates the session, creating a new session ID. The existing session data is preserved.
  2160. */
  2161. async regenerate() {
  2162. let data = /* @__PURE__ */ new Map();
  2163. try {
  2164. data = await this.#ensureData();
  2165. } catch (err) {
  2166. console.error("Failed to load session data during regeneration:", err);
  2167. }
  2168. const oldSessionId = this.#sessionID;
  2169. this.#sessionID = crypto.randomUUID();
  2170. this.#data = data;
  2171. await this.#setCookie();
  2172. if (oldSessionId && this.#storage) {
  2173. this.#storage.removeItem(oldSessionId).catch((err) => {
  2174. console.error("Failed to remove old session data:", err);
  2175. });
  2176. }
  2177. }
  2178. // Persists the session data to storage.
  2179. // This is called automatically at the end of the request.
  2180. // Uses a symbol to prevent users from calling it directly.
  2181. async [PERSIST_SYMBOL]() {
  2182. if (!this.#dirty && !this.#toDestroy.size) {
  2183. return;
  2184. }
  2185. const storage = await this.#ensureStorage();
  2186. if (this.#dirty && this.#data) {
  2187. const data = await this.#ensureData();
  2188. this.#toDelete.forEach((key2) => data.delete(key2));
  2189. const key = this.#ensureSessionID();
  2190. let serialized;
  2191. try {
  2192. serialized = stringify(data);
  2193. } catch (err) {
  2194. throw new AstroError(
  2195. {
  2196. ...SessionStorageSaveError,
  2197. message: SessionStorageSaveError.message(
  2198. "The session data could not be serialized.",
  2199. this.#config.driver
  2200. )
  2201. },
  2202. { cause: err }
  2203. );
  2204. }
  2205. await storage.setItem(key, serialized);
  2206. this.#dirty = false;
  2207. }
  2208. if (this.#toDestroy.size > 0) {
  2209. const cleanupPromises = [...this.#toDestroy].map(
  2210. (sessionId) => storage.removeItem(sessionId).catch((err) => {
  2211. console.error(`Failed to clean up session ${sessionId}:`, err);
  2212. })
  2213. );
  2214. await Promise.all(cleanupPromises);
  2215. this.#toDestroy.clear();
  2216. }
  2217. }
  2218. get sessionID() {
  2219. return this.#sessionID;
  2220. }
  2221. /**
  2222. * Loads a session from storage with the given ID, and replaces the current session.
  2223. * Any changes made to the current session will be lost.
  2224. * This is not normally needed, as the session is automatically loaded using the cookie.
  2225. * However it can be used to restore a session where the ID has been recorded somewhere
  2226. * else (e.g. in a database).
  2227. */
  2228. async load(sessionID) {
  2229. this.#sessionID = sessionID;
  2230. this.#data = void 0;
  2231. await this.#setCookie();
  2232. await this.#ensureData();
  2233. }
  2234. /**
  2235. * Sets the session cookie.
  2236. */
  2237. async #setCookie() {
  2238. if (!VALID_COOKIE_REGEX.test(this.#cookieName)) {
  2239. throw new AstroError({
  2240. ...SessionStorageSaveError,
  2241. message: "Invalid cookie name. Cookie names can only contain letters, numbers, and dashes."
  2242. });
  2243. }
  2244. const value = this.#ensureSessionID();
  2245. this.#cookies.set(this.#cookieName, value, this.#cookieConfig);
  2246. }
  2247. /**
  2248. * Attempts to load the session data from storage, or creates a new data object if none exists.
  2249. * If there is existing partial data, it will be merged into the new data object.
  2250. */
  2251. async #ensureData() {
  2252. const storage = await this.#ensureStorage();
  2253. if (this.#data && !this.#partial) {
  2254. return this.#data;
  2255. }
  2256. this.#data ??= /* @__PURE__ */ new Map();
  2257. const raw = await storage.get(this.#ensureSessionID());
  2258. if (!raw) {
  2259. return this.#data;
  2260. }
  2261. try {
  2262. const storedMap = unflatten(raw);
  2263. if (!(storedMap instanceof Map)) {
  2264. await this.destroy();
  2265. throw new AstroError({
  2266. ...SessionStorageInitError,
  2267. message: SessionStorageInitError.message(
  2268. "The session data was an invalid type.",
  2269. this.#config.driver
  2270. )
  2271. });
  2272. }
  2273. const now = Date.now();
  2274. for (const [key, value] of storedMap) {
  2275. const expired = typeof value.expires === "number" && value.expires < now;
  2276. if (!this.#data.has(key) && !this.#toDelete.has(key) && !expired) {
  2277. this.#data.set(key, value);
  2278. }
  2279. }
  2280. this.#partial = false;
  2281. return this.#data;
  2282. } catch (err) {
  2283. await this.destroy();
  2284. if (err instanceof AstroError) {
  2285. throw err;
  2286. }
  2287. throw new AstroError(
  2288. {
  2289. ...SessionStorageInitError,
  2290. message: SessionStorageInitError.message(
  2291. "The session data could not be parsed.",
  2292. this.#config.driver
  2293. )
  2294. },
  2295. { cause: err }
  2296. );
  2297. }
  2298. }
  2299. /**
  2300. * Returns the session ID, generating a new one if it does not exist.
  2301. */
  2302. #ensureSessionID() {
  2303. this.#sessionID ??= this.#cookies.get(this.#cookieName)?.value ?? crypto.randomUUID();
  2304. return this.#sessionID;
  2305. }
  2306. /**
  2307. * Ensures the storage is initialized.
  2308. * This is called automatically when a storage operation is needed.
  2309. */
  2310. async #ensureStorage() {
  2311. if (this.#storage) {
  2312. return this.#storage;
  2313. }
  2314. if (AstroSession.#sharedStorage.has(this.#config.driver)) {
  2315. this.#storage = AstroSession.#sharedStorage.get(this.#config.driver);
  2316. return this.#storage;
  2317. }
  2318. if (this.#config.driver === "test") {
  2319. this.#storage = this.#config.options.mockStorage;
  2320. return this.#storage;
  2321. }
  2322. if (this.#config.driver === "fs" || this.#config.driver === "fsLite" || this.#config.driver === "fs-lite") {
  2323. this.#config.options ??= {};
  2324. this.#config.driver = "fs-lite";
  2325. this.#config.options.base ??= ".astro/session";
  2326. }
  2327. let driver = null;
  2328. try {
  2329. if (this.#config.driverModule) {
  2330. driver = (await this.#config.driverModule()).default;
  2331. } else if (this.#config.driver) {
  2332. const driverName = resolveSessionDriverName(this.#config.driver);
  2333. if (driverName) {
  2334. driver = (await import(driverName)).default;
  2335. }
  2336. }
  2337. } catch (err) {
  2338. if (err.code === "ERR_MODULE_NOT_FOUND") {
  2339. throw new AstroError(
  2340. {
  2341. ...SessionStorageInitError,
  2342. message: SessionStorageInitError.message(
  2343. err.message.includes(`Cannot find package`) ? "The driver module could not be found." : err.message,
  2344. this.#config.driver
  2345. )
  2346. },
  2347. { cause: err }
  2348. );
  2349. }
  2350. throw err;
  2351. }
  2352. if (!driver) {
  2353. throw new AstroError({
  2354. ...SessionStorageInitError,
  2355. message: SessionStorageInitError.message(
  2356. "The module did not export a driver.",
  2357. this.#config.driver
  2358. )
  2359. });
  2360. }
  2361. try {
  2362. this.#storage = createStorage({
  2363. driver: driver(this.#config.options)
  2364. });
  2365. AstroSession.#sharedStorage.set(this.#config.driver, this.#storage);
  2366. return this.#storage;
  2367. } catch (err) {
  2368. throw new AstroError(
  2369. {
  2370. ...SessionStorageInitError,
  2371. message: SessionStorageInitError.message("Unknown error", this.#config.driver)
  2372. },
  2373. { cause: err }
  2374. );
  2375. }
  2376. }
  2377. }
  2378. function resolveSessionDriverName(driver) {
  2379. if (!driver) {
  2380. return null;
  2381. }
  2382. try {
  2383. if (driver === "fs") {
  2384. return builtinDrivers.fsLite;
  2385. }
  2386. if (driver in builtinDrivers) {
  2387. return builtinDrivers[driver];
  2388. }
  2389. } catch {
  2390. return null;
  2391. }
  2392. return driver;
  2393. }
  2394. const apiContextRoutesSymbol = Symbol.for("context.routes");
  2395. class RenderContext {
  2396. constructor(pipeline, locals, middleware, actions, pathname, request, routeData, status, clientAddress, cookies = new AstroCookies(request), params = getParams(routeData, pathname), url = new URL(request.url), props = {}, partial = void 0, shouldInjectCspMetaTags = !!pipeline.manifest.csp, session = pipeline.manifest.sessionConfig ? new AstroSession(cookies, pipeline.manifest.sessionConfig, pipeline.runtimeMode) : void 0) {
  2397. this.pipeline = pipeline;
  2398. this.locals = locals;
  2399. this.middleware = middleware;
  2400. this.actions = actions;
  2401. this.pathname = pathname;
  2402. this.request = request;
  2403. this.routeData = routeData;
  2404. this.status = status;
  2405. this.clientAddress = clientAddress;
  2406. this.cookies = cookies;
  2407. this.params = params;
  2408. this.url = url;
  2409. this.props = props;
  2410. this.partial = partial;
  2411. this.shouldInjectCspMetaTags = shouldInjectCspMetaTags;
  2412. this.session = session;
  2413. }
  2414. /**
  2415. * A flag that tells the render content if the rewriting was triggered
  2416. */
  2417. isRewriting = false;
  2418. /**
  2419. * A safety net in case of loops
  2420. */
  2421. counter = 0;
  2422. result = void 0;
  2423. static async create({
  2424. locals = {},
  2425. middleware,
  2426. pathname,
  2427. pipeline,
  2428. request,
  2429. routeData,
  2430. clientAddress,
  2431. status = 200,
  2432. props,
  2433. partial = void 0,
  2434. actions,
  2435. shouldInjectCspMetaTags
  2436. }) {
  2437. const pipelineMiddleware = await pipeline.getMiddleware();
  2438. const pipelineActions = actions ?? await pipeline.getActions();
  2439. setOriginPathname(
  2440. request,
  2441. pathname,
  2442. pipeline.manifest.trailingSlash,
  2443. pipeline.manifest.buildFormat
  2444. );
  2445. return new RenderContext(
  2446. pipeline,
  2447. locals,
  2448. sequence(...pipeline.internalMiddleware, middleware ?? pipelineMiddleware),
  2449. pipelineActions,
  2450. pathname,
  2451. request,
  2452. routeData,
  2453. status,
  2454. clientAddress,
  2455. void 0,
  2456. void 0,
  2457. void 0,
  2458. props,
  2459. partial,
  2460. shouldInjectCspMetaTags ?? !!pipeline.manifest.csp
  2461. );
  2462. }
  2463. /**
  2464. * The main function of the RenderContext.
  2465. *
  2466. * Use this function to render any route known to Astro.
  2467. * It attempts to render a route. A route can be a:
  2468. *
  2469. * - page
  2470. * - redirect
  2471. * - endpoint
  2472. * - fallback
  2473. */
  2474. async render(componentInstance, slots = {}) {
  2475. const { middleware, pipeline } = this;
  2476. const { logger, serverLike, streaming, manifest } = pipeline;
  2477. const props = Object.keys(this.props).length > 0 ? this.props : await getProps({
  2478. mod: componentInstance,
  2479. routeData: this.routeData,
  2480. routeCache: this.pipeline.routeCache,
  2481. pathname: this.pathname,
  2482. logger,
  2483. serverLike,
  2484. base: manifest.base
  2485. });
  2486. const actionApiContext = this.createActionAPIContext();
  2487. const apiContext = this.createAPIContext(props, actionApiContext);
  2488. this.counter++;
  2489. if (this.counter === 4) {
  2490. return new Response("Loop Detected", {
  2491. // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/508
  2492. status: 508,
  2493. statusText: "Astro detected a loop where you tried to call the rewriting logic more than four times."
  2494. });
  2495. }
  2496. const lastNext = async (ctx, payload) => {
  2497. if (payload) {
  2498. const oldPathname = this.pathname;
  2499. pipeline.logger.debug("router", "Called rewriting to:", payload);
  2500. const {
  2501. routeData,
  2502. componentInstance: newComponent,
  2503. pathname,
  2504. newUrl
  2505. } = await pipeline.tryRewrite(payload, this.request);
  2506. if (this.pipeline.serverLike === true && this.routeData.prerender === false && routeData.prerender === true) {
  2507. throw new AstroError({
  2508. ...ForbiddenRewrite,
  2509. message: ForbiddenRewrite.message(this.pathname, pathname, routeData.component),
  2510. hint: ForbiddenRewrite.hint(routeData.component)
  2511. });
  2512. }
  2513. this.routeData = routeData;
  2514. componentInstance = newComponent;
  2515. if (payload instanceof Request) {
  2516. this.request = payload;
  2517. } else {
  2518. this.request = copyRequest(
  2519. newUrl,
  2520. this.request,
  2521. // need to send the flag of the previous routeData
  2522. routeData.prerender,
  2523. this.pipeline.logger,
  2524. this.routeData.route
  2525. );
  2526. }
  2527. this.isRewriting = true;
  2528. this.url = new URL(this.request.url);
  2529. this.params = getParams(routeData, pathname);
  2530. this.pathname = pathname;
  2531. this.status = 200;
  2532. setOriginPathname(
  2533. this.request,
  2534. oldPathname,
  2535. this.pipeline.manifest.trailingSlash,
  2536. this.pipeline.manifest.buildFormat
  2537. );
  2538. }
  2539. let response2;
  2540. if (!ctx.isPrerendered) {
  2541. const { action, setActionResult, serializeActionResult } = getActionContext(ctx);
  2542. if (action?.calledFrom === "form") {
  2543. const actionResult = await action.handler();
  2544. setActionResult(action.name, serializeActionResult(actionResult));
  2545. }
  2546. }
  2547. switch (this.routeData.type) {
  2548. case "endpoint": {
  2549. response2 = await renderEndpoint(
  2550. componentInstance,
  2551. ctx,
  2552. this.routeData.prerender,
  2553. logger
  2554. );
  2555. break;
  2556. }
  2557. case "redirect":
  2558. return renderRedirect(this);
  2559. case "page": {
  2560. this.result = await this.createResult(componentInstance, actionApiContext);
  2561. try {
  2562. response2 = await renderPage(
  2563. this.result,
  2564. componentInstance?.default,
  2565. props,
  2566. slots,
  2567. streaming,
  2568. this.routeData
  2569. );
  2570. } catch (e) {
  2571. this.result.cancelled = true;
  2572. throw e;
  2573. }
  2574. response2.headers.set(ROUTE_TYPE_HEADER, "page");
  2575. if (this.routeData.route === "/404" || this.routeData.route === "/500") {
  2576. response2.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
  2577. }
  2578. if (this.isRewriting) {
  2579. response2.headers.set(REWRITE_DIRECTIVE_HEADER_KEY, REWRITE_DIRECTIVE_HEADER_VALUE);
  2580. }
  2581. break;
  2582. }
  2583. case "fallback": {
  2584. return new Response(null, { status: 500, headers: { [ROUTE_TYPE_HEADER]: "fallback" } });
  2585. }
  2586. }
  2587. const responseCookies = getCookiesFromResponse(response2);
  2588. if (responseCookies) {
  2589. this.cookies.merge(responseCookies);
  2590. }
  2591. return response2;
  2592. };
  2593. if (isRouteExternalRedirect(this.routeData)) {
  2594. return renderRedirect(this);
  2595. }
  2596. const response = await callMiddleware(middleware, apiContext, lastNext);
  2597. if (response.headers.get(ROUTE_TYPE_HEADER)) {
  2598. response.headers.delete(ROUTE_TYPE_HEADER);
  2599. }
  2600. attachCookiesToResponse(response, this.cookies);
  2601. return response;
  2602. }
  2603. createAPIContext(props, context) {
  2604. const redirect = (path, status = 302) => new Response(null, { status, headers: { Location: path } });
  2605. Reflect.set(context, apiContextRoutesSymbol, this.pipeline);
  2606. return Object.assign(context, {
  2607. props,
  2608. redirect,
  2609. getActionResult: createGetActionResult(context.locals),
  2610. callAction: createCallAction(context)
  2611. });
  2612. }
  2613. async #executeRewrite(reroutePayload) {
  2614. this.pipeline.logger.debug("router", "Calling rewrite: ", reroutePayload);
  2615. const oldPathname = this.pathname;
  2616. const { routeData, componentInstance, newUrl, pathname } = await this.pipeline.tryRewrite(
  2617. reroutePayload,
  2618. this.request
  2619. );
  2620. const isI18nFallback = routeData.fallbackRoutes && routeData.fallbackRoutes.length > 0;
  2621. if (this.pipeline.serverLike && !this.routeData.prerender && routeData.prerender && !isI18nFallback) {
  2622. throw new AstroError({
  2623. ...ForbiddenRewrite,
  2624. message: ForbiddenRewrite.message(this.pathname, pathname, routeData.component),
  2625. hint: ForbiddenRewrite.hint(routeData.component)
  2626. });
  2627. }
  2628. this.routeData = routeData;
  2629. if (reroutePayload instanceof Request) {
  2630. this.request = reroutePayload;
  2631. } else {
  2632. this.request = copyRequest(
  2633. newUrl,
  2634. this.request,
  2635. // need to send the flag of the previous routeData
  2636. routeData.prerender,
  2637. this.pipeline.logger,
  2638. this.routeData.route
  2639. );
  2640. }
  2641. this.url = new URL(this.request.url);
  2642. const newCookies = new AstroCookies(this.request);
  2643. if (this.cookies) {
  2644. newCookies.merge(this.cookies);
  2645. }
  2646. this.cookies = newCookies;
  2647. this.params = getParams(routeData, pathname);
  2648. this.pathname = pathname;
  2649. this.isRewriting = true;
  2650. this.status = 200;
  2651. setOriginPathname(
  2652. this.request,
  2653. oldPathname,
  2654. this.pipeline.manifest.trailingSlash,
  2655. this.pipeline.manifest.buildFormat
  2656. );
  2657. return await this.render(componentInstance);
  2658. }
  2659. createActionAPIContext() {
  2660. const renderContext = this;
  2661. const { params, pipeline, url } = this;
  2662. const generator = `Astro v${ASTRO_VERSION}`;
  2663. const rewrite = async (reroutePayload) => {
  2664. return await this.#executeRewrite(reroutePayload);
  2665. };
  2666. return {
  2667. // Don't allow reassignment of cookies because it doesn't work
  2668. get cookies() {
  2669. return renderContext.cookies;
  2670. },
  2671. routePattern: this.routeData.route,
  2672. isPrerendered: this.routeData.prerender,
  2673. get clientAddress() {
  2674. return renderContext.getClientAddress();
  2675. },
  2676. get currentLocale() {
  2677. return renderContext.computeCurrentLocale();
  2678. },
  2679. generator,
  2680. get locals() {
  2681. return renderContext.locals;
  2682. },
  2683. set locals(_) {
  2684. throw new AstroError(LocalsReassigned);
  2685. },
  2686. params,
  2687. get preferredLocale() {
  2688. return renderContext.computePreferredLocale();
  2689. },
  2690. get preferredLocaleList() {
  2691. return renderContext.computePreferredLocaleList();
  2692. },
  2693. rewrite,
  2694. request: this.request,
  2695. site: pipeline.site,
  2696. url,
  2697. get originPathname() {
  2698. return getOriginPathname(renderContext.request);
  2699. },
  2700. get session() {
  2701. if (this.isPrerendered) {
  2702. pipeline.logger.warn(
  2703. "session",
  2704. `context.session was used when rendering the route ${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/`
  2705. );
  2706. return void 0;
  2707. }
  2708. if (!renderContext.session) {
  2709. pipeline.logger.warn(
  2710. "session",
  2711. `context.session was used when rendering the route ${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/`
  2712. );
  2713. return void 0;
  2714. }
  2715. return renderContext.session;
  2716. },
  2717. get csp() {
  2718. return {
  2719. insertDirective(payload) {
  2720. if (!pipeline.manifest.csp) {
  2721. throw new AstroError(CspNotEnabled);
  2722. }
  2723. renderContext.result?.directives.push(payload);
  2724. },
  2725. insertScriptResource(resource) {
  2726. if (!pipeline.manifest.csp) {
  2727. throw new AstroError(CspNotEnabled);
  2728. }
  2729. renderContext.result?.scriptResources.push(resource);
  2730. },
  2731. insertStyleResource(resource) {
  2732. if (!pipeline.manifest.csp) {
  2733. throw new AstroError(CspNotEnabled);
  2734. }
  2735. renderContext.result?.styleResources.push(resource);
  2736. },
  2737. insertStyleHash(hash) {
  2738. if (!pipeline.manifest.csp) {
  2739. throw new AstroError(CspNotEnabled);
  2740. }
  2741. renderContext.result?.styleHashes.push(hash);
  2742. },
  2743. insertScriptHash(hash) {
  2744. if (!pipeline.manifest.csp) {
  2745. throw new AstroError(CspNotEnabled);
  2746. }
  2747. renderContext.result?.scriptHashes.push(hash);
  2748. }
  2749. };
  2750. }
  2751. };
  2752. }
  2753. async createResult(mod, ctx) {
  2754. const { cookies, pathname, pipeline, routeData, status } = this;
  2755. const { clientDirectives, inlinedScripts, compressHTML, manifest, renderers, resolve } = pipeline;
  2756. const { links, scripts, styles } = await pipeline.headElements(routeData);
  2757. const extraStyleHashes = [];
  2758. const extraScriptHashes = [];
  2759. const shouldInjectCspMetaTags = this.shouldInjectCspMetaTags;
  2760. const cspAlgorithm = manifest.csp?.algorithm ?? "SHA-256";
  2761. if (shouldInjectCspMetaTags) {
  2762. for (const style of styles) {
  2763. extraStyleHashes.push(await generateCspDigest(style.children, cspAlgorithm));
  2764. }
  2765. for (const script of scripts) {
  2766. extraScriptHashes.push(await generateCspDigest(script.children, cspAlgorithm));
  2767. }
  2768. }
  2769. const componentMetadata = await pipeline.componentMetadata(routeData) ?? manifest.componentMetadata;
  2770. const headers = new Headers({ "Content-Type": "text/html" });
  2771. const partial = typeof this.partial === "boolean" ? this.partial : Boolean(mod.partial);
  2772. const actionResult = hasActionPayload(this.locals) ? deserializeActionResult(this.locals._actionPayload.actionResult) : void 0;
  2773. const response = {
  2774. status: actionResult?.error ? actionResult?.error.status : status,
  2775. statusText: actionResult?.error ? actionResult?.error.type : "OK",
  2776. get headers() {
  2777. return headers;
  2778. },
  2779. // Disallow `Astro.response.headers = new Headers`
  2780. set headers(_) {
  2781. throw new AstroError(AstroResponseHeadersReassigned);
  2782. }
  2783. };
  2784. const result = {
  2785. base: manifest.base,
  2786. userAssetsBase: manifest.userAssetsBase,
  2787. cancelled: false,
  2788. clientDirectives,
  2789. inlinedScripts,
  2790. componentMetadata,
  2791. compressHTML,
  2792. cookies,
  2793. /** This function returns the `Astro` faux-global */
  2794. createAstro: (astroGlobal, props, slots) => this.createAstro(result, astroGlobal, props, slots, ctx),
  2795. links,
  2796. params: this.params,
  2797. partial,
  2798. pathname,
  2799. renderers,
  2800. resolve,
  2801. response,
  2802. request: this.request,
  2803. scripts,
  2804. styles,
  2805. actionResult,
  2806. serverIslandNameMap: manifest.serverIslandNameMap ?? /* @__PURE__ */ new Map(),
  2807. key: manifest.key,
  2808. trailingSlash: manifest.trailingSlash,
  2809. _metadata: {
  2810. hasHydrationScript: false,
  2811. rendererSpecificHydrationScripts: /* @__PURE__ */ new Set(),
  2812. hasRenderedHead: false,
  2813. renderedScripts: /* @__PURE__ */ new Set(),
  2814. hasDirectives: /* @__PURE__ */ new Set(),
  2815. hasRenderedServerIslandRuntime: false,
  2816. headInTree: false,
  2817. extraHead: [],
  2818. extraStyleHashes,
  2819. extraScriptHashes,
  2820. propagators: /* @__PURE__ */ new Set()
  2821. },
  2822. cspDestination: manifest.csp?.cspDestination ?? (routeData.prerender ? "meta" : "header"),
  2823. shouldInjectCspMetaTags,
  2824. cspAlgorithm,
  2825. // The following arrays must be cloned, otherwise they become mutable across routes.
  2826. scriptHashes: manifest.csp?.scriptHashes ? [...manifest.csp.scriptHashes] : [],
  2827. scriptResources: manifest.csp?.scriptResources ? [...manifest.csp.scriptResources] : [],
  2828. styleHashes: manifest.csp?.styleHashes ? [...manifest.csp.styleHashes] : [],
  2829. styleResources: manifest.csp?.styleResources ? [...manifest.csp.styleResources] : [],
  2830. directives: manifest.csp?.directives ? [...manifest.csp.directives] : [],
  2831. isStrictDynamic: manifest.csp?.isStrictDynamic ?? false
  2832. };
  2833. return result;
  2834. }
  2835. #astroPagePartial;
  2836. /**
  2837. * The Astro global is sourced in 3 different phases:
  2838. * - **Static**: `.generator` and `.glob` is printed by the compiler, instantiated once per process per astro file
  2839. * - **Page-level**: `.request`, `.cookies`, `.locals` etc. These remain the same for the duration of the request.
  2840. * - **Component-level**: `.props`, `.slots`, and `.self` are unique to each _use_ of each component.
  2841. *
  2842. * 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.
  2843. */
  2844. createAstro(result, astroStaticPartial, props, slotValues, apiContext) {
  2845. let astroPagePartial;
  2846. if (this.isRewriting) {
  2847. astroPagePartial = this.#astroPagePartial = this.createAstroPagePartial(
  2848. result,
  2849. astroStaticPartial,
  2850. apiContext
  2851. );
  2852. } else {
  2853. astroPagePartial = this.#astroPagePartial ??= this.createAstroPagePartial(
  2854. result,
  2855. astroStaticPartial,
  2856. apiContext
  2857. );
  2858. }
  2859. const astroComponentPartial = { props, self: null };
  2860. const Astro = Object.assign(
  2861. Object.create(astroPagePartial),
  2862. astroComponentPartial
  2863. );
  2864. let _slots;
  2865. Object.defineProperty(Astro, "slots", {
  2866. get: () => {
  2867. if (!_slots) {
  2868. _slots = new Slots(
  2869. result,
  2870. slotValues,
  2871. this.pipeline.logger
  2872. );
  2873. }
  2874. return _slots;
  2875. }
  2876. });
  2877. return Astro;
  2878. }
  2879. createAstroPagePartial(result, astroStaticPartial, apiContext) {
  2880. const renderContext = this;
  2881. const { cookies, locals, params, pipeline, url } = this;
  2882. const { response } = result;
  2883. const redirect = (path, status = 302) => {
  2884. if (this.request[responseSentSymbol$1]) {
  2885. throw new AstroError({
  2886. ...ResponseSentError
  2887. });
  2888. }
  2889. return new Response(null, { status, headers: { Location: path } });
  2890. };
  2891. const rewrite = async (reroutePayload) => {
  2892. return await this.#executeRewrite(reroutePayload);
  2893. };
  2894. const callAction = createCallAction(apiContext);
  2895. return {
  2896. generator: astroStaticPartial.generator,
  2897. glob: astroStaticPartial.glob,
  2898. routePattern: this.routeData.route,
  2899. isPrerendered: this.routeData.prerender,
  2900. cookies,
  2901. get session() {
  2902. if (this.isPrerendered) {
  2903. pipeline.logger.warn(
  2904. "session",
  2905. `Astro.session was used when rendering the route ${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/`
  2906. );
  2907. return void 0;
  2908. }
  2909. if (!renderContext.session) {
  2910. pipeline.logger.warn(
  2911. "session",
  2912. `Astro.session was used when rendering the route ${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/`
  2913. );
  2914. return void 0;
  2915. }
  2916. return renderContext.session;
  2917. },
  2918. get clientAddress() {
  2919. return renderContext.getClientAddress();
  2920. },
  2921. get currentLocale() {
  2922. return renderContext.computeCurrentLocale();
  2923. },
  2924. params,
  2925. get preferredLocale() {
  2926. return renderContext.computePreferredLocale();
  2927. },
  2928. get preferredLocaleList() {
  2929. return renderContext.computePreferredLocaleList();
  2930. },
  2931. locals,
  2932. redirect,
  2933. rewrite,
  2934. request: this.request,
  2935. response,
  2936. site: pipeline.site,
  2937. getActionResult: createGetActionResult(locals),
  2938. get callAction() {
  2939. return callAction;
  2940. },
  2941. url,
  2942. get originPathname() {
  2943. return getOriginPathname(renderContext.request);
  2944. },
  2945. get csp() {
  2946. return {
  2947. insertDirective(payload) {
  2948. if (!pipeline.manifest.csp) {
  2949. throw new AstroError(CspNotEnabled);
  2950. }
  2951. renderContext.result?.directives.push(payload);
  2952. },
  2953. insertScriptResource(resource) {
  2954. if (!pipeline.manifest.csp) {
  2955. throw new AstroError(CspNotEnabled);
  2956. }
  2957. renderContext.result?.scriptResources.push(resource);
  2958. },
  2959. insertStyleResource(resource) {
  2960. if (!pipeline.manifest.csp) {
  2961. throw new AstroError(CspNotEnabled);
  2962. }
  2963. renderContext.result?.styleResources.push(resource);
  2964. },
  2965. insertStyleHash(hash) {
  2966. if (!pipeline.manifest.csp) {
  2967. throw new AstroError(CspNotEnabled);
  2968. }
  2969. renderContext.result?.styleHashes.push(hash);
  2970. },
  2971. insertScriptHash(hash) {
  2972. if (!pipeline.manifest.csp) {
  2973. throw new AstroError(CspNotEnabled);
  2974. }
  2975. renderContext.result?.scriptHashes.push(hash);
  2976. }
  2977. };
  2978. }
  2979. };
  2980. }
  2981. getClientAddress() {
  2982. const { pipeline, request, routeData, clientAddress } = this;
  2983. if (routeData.prerender) {
  2984. throw new AstroError({
  2985. ...PrerenderClientAddressNotAvailable,
  2986. message: PrerenderClientAddressNotAvailable.message(routeData.component)
  2987. });
  2988. }
  2989. if (clientAddress) {
  2990. return clientAddress;
  2991. }
  2992. if (clientAddressSymbol in request) {
  2993. return Reflect.get(request, clientAddressSymbol);
  2994. }
  2995. if (pipeline.adapterName) {
  2996. throw new AstroError({
  2997. ...ClientAddressNotAvailable,
  2998. message: ClientAddressNotAvailable.message(pipeline.adapterName)
  2999. });
  3000. }
  3001. throw new AstroError(StaticClientAddressNotAvailable);
  3002. }
  3003. /**
  3004. * API Context may be created multiple times per request, i18n data needs to be computed only once.
  3005. * So, it is computed and saved here on creation of the first APIContext and reused for later ones.
  3006. */
  3007. #currentLocale;
  3008. computeCurrentLocale() {
  3009. const {
  3010. url,
  3011. pipeline: { i18n },
  3012. routeData
  3013. } = this;
  3014. if (!i18n) return;
  3015. const { defaultLocale, locales, strategy } = i18n;
  3016. const fallbackTo = strategy === "pathname-prefix-other-locales" || strategy === "domains-prefix-other-locales" ? defaultLocale : void 0;
  3017. if (this.#currentLocale) {
  3018. return this.#currentLocale;
  3019. }
  3020. let computedLocale;
  3021. if (isRouteServerIsland(routeData)) {
  3022. let referer = this.request.headers.get("referer");
  3023. if (referer) {
  3024. if (URL.canParse(referer)) {
  3025. referer = new URL(referer).pathname;
  3026. }
  3027. computedLocale = computeCurrentLocale(referer, locales, defaultLocale);
  3028. }
  3029. } else {
  3030. let pathname = routeData.pathname;
  3031. if (!routeData.pattern.test(url.pathname)) {
  3032. for (const fallbackRoute of routeData.fallbackRoutes) {
  3033. if (fallbackRoute.pattern.test(url.pathname)) {
  3034. pathname = fallbackRoute.pathname;
  3035. break;
  3036. }
  3037. }
  3038. }
  3039. pathname = pathname && !isRoute404or500(routeData) ? pathname : url.pathname;
  3040. computedLocale = computeCurrentLocale(pathname, locales, defaultLocale);
  3041. }
  3042. this.#currentLocale = computedLocale ?? fallbackTo;
  3043. return this.#currentLocale;
  3044. }
  3045. #preferredLocale;
  3046. computePreferredLocale() {
  3047. const {
  3048. pipeline: { i18n },
  3049. request
  3050. } = this;
  3051. if (!i18n) return;
  3052. return this.#preferredLocale ??= computePreferredLocale(request, i18n.locales);
  3053. }
  3054. #preferredLocaleList;
  3055. computePreferredLocaleList() {
  3056. const {
  3057. pipeline: { i18n },
  3058. request
  3059. } = this;
  3060. if (!i18n) return;
  3061. return this.#preferredLocaleList ??= computePreferredLocaleList(request, i18n.locales);
  3062. }
  3063. }
  3064. function sequence(...handlers) {
  3065. const filtered = handlers.filter((h) => !!h);
  3066. const length = filtered.length;
  3067. if (!length) {
  3068. return defineMiddleware((_context, next) => {
  3069. return next();
  3070. });
  3071. }
  3072. return defineMiddleware((context, next) => {
  3073. let carriedPayload = void 0;
  3074. return applyHandle(0, context);
  3075. function applyHandle(i, handleContext) {
  3076. const handle = filtered[i];
  3077. const result = handle(handleContext, async (payload) => {
  3078. if (i < length - 1) {
  3079. if (payload) {
  3080. let newRequest;
  3081. if (payload instanceof Request) {
  3082. newRequest = payload;
  3083. } else if (payload instanceof URL) {
  3084. newRequest = new Request(payload, handleContext.request.clone());
  3085. } else {
  3086. newRequest = new Request(
  3087. new URL(payload, handleContext.url.origin),
  3088. handleContext.request.clone()
  3089. );
  3090. }
  3091. const oldPathname = handleContext.url.pathname;
  3092. const pipeline = Reflect.get(handleContext, apiContextRoutesSymbol);
  3093. const { routeData, pathname } = await pipeline.tryRewrite(
  3094. payload,
  3095. handleContext.request
  3096. );
  3097. if (pipeline.serverLike === true && handleContext.isPrerendered === false && routeData.prerender === true) {
  3098. throw new AstroError({
  3099. ...ForbiddenRewrite,
  3100. message: ForbiddenRewrite.message(
  3101. handleContext.url.pathname,
  3102. pathname,
  3103. routeData.component
  3104. ),
  3105. hint: ForbiddenRewrite.hint(routeData.component)
  3106. });
  3107. }
  3108. carriedPayload = payload;
  3109. handleContext.request = newRequest;
  3110. handleContext.url = new URL(newRequest.url);
  3111. handleContext.params = getParams(routeData, pathname);
  3112. handleContext.routePattern = routeData.route;
  3113. setOriginPathname(
  3114. handleContext.request,
  3115. oldPathname,
  3116. pipeline.manifest.trailingSlash,
  3117. pipeline.manifest.buildFormat
  3118. );
  3119. }
  3120. return applyHandle(i + 1, handleContext);
  3121. } else {
  3122. return next(payload ?? carriedPayload);
  3123. }
  3124. });
  3125. return result;
  3126. }
  3127. });
  3128. }
  3129. function defineMiddleware(fn) {
  3130. return fn;
  3131. }
  3132. export { PERSIST_SYMBOL as P, RouteCache as R, SERVER_ISLAND_COMPONENT as S, redirectToFallback as a, redirectToDefaultLocale as b, requestHasLocale as c, normalizeTheLocale as d, defineMiddleware as e, SERVER_ISLAND_ROUTE as f, createEndpoint as g, findRouteToRewrite as h, isRequestServerIsland as i, RenderContext as j, getSetCookiesFromResponse as k, matchRoute as m, notFound as n, requestIs404Or500 as r, sequence as s };