| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129 |
- globalThis.process ??= {}; globalThis.process.env ??= {};
- import { f as fileExtension, j as joinPaths, s as slash, p as prependForwardSlash, r as removeTrailingForwardSlash, a as appendForwardSlash, b as isInternalPath, c as collapseDuplicateTrailingSlashes, h as hasFileExtension } from './path_CH3auf61.mjs';
- import { m as matchPattern } from './remote_BC1y8RCW.mjs';
- import { r as requestIs404Or500, i as isRequestServerIsland, n as notFound, a as redirectToFallback, b as redirectToDefaultLocale, c as requestHasLocale, d as normalizeTheLocale, e as defineMiddleware, S as SERVER_ISLAND_COMPONENT, f as SERVER_ISLAND_ROUTE, g as createEndpoint, R as RouteCache, s as sequence, h as findRouteToRewrite, m as matchRoute, j as RenderContext, P as PERSIST_SYMBOL, k as getSetCookiesFromResponse } from './index_BfW_FHpJ.mjs';
- import { R as ROUTE_TYPE_HEADER, q as REROUTE_DIRECTIVE_HEADER, D as DEFAULT_404_COMPONENT, A as AstroError, v as ActionNotFoundError, w as bold, x as red, y as yellow, z as dim, B as blue, C as clientAddressSymbol, G as LocalsNotAnObject, H as REROUTABLE_STATUS_CODES, J as responseSentSymbol } from './astro/server_WO3f6Mge.mjs';
- import { D as DEFAULT_404_ROUTE, d as default404Instance, e as ensure404Route } from './astro-designed-error-pages_CuMapJD2.mjs';
- import { N as NOOP_MIDDLEWARE_FN } from './noop-middleware_C9xlyid3.mjs';
- import 'cloudflare:workers';
- function createI18nMiddleware(i18n, base, trailingSlash, format) {
- if (!i18n) return (_, next) => next();
- const payload = {
- ...i18n,
- trailingSlash,
- base,
- format};
- const _redirectToDefaultLocale = redirectToDefaultLocale(payload);
- const _noFoundForNonLocaleRoute = notFound(payload);
- const _requestHasLocale = requestHasLocale(payload.locales);
- const _redirectToFallback = redirectToFallback(payload);
- const prefixAlways = (context, response) => {
- const url = context.url;
- if (url.pathname === base + "/" || url.pathname === base) {
- return _redirectToDefaultLocale(context);
- } else if (!_requestHasLocale(context)) {
- return _noFoundForNonLocaleRoute(context, response);
- }
- return void 0;
- };
- const prefixOtherLocales = (context, response) => {
- let pathnameContainsDefaultLocale = false;
- const url = context.url;
- for (const segment of url.pathname.split("/")) {
- if (normalizeTheLocale(segment) === normalizeTheLocale(i18n.defaultLocale)) {
- pathnameContainsDefaultLocale = true;
- break;
- }
- }
- if (pathnameContainsDefaultLocale) {
- const newLocation = url.pathname.replace(`/${i18n.defaultLocale}`, "");
- response.headers.set("Location", newLocation);
- return _noFoundForNonLocaleRoute(context);
- }
- return void 0;
- };
- return async (context, next) => {
- const response = await next();
- const type = response.headers.get(ROUTE_TYPE_HEADER);
- const isReroute = response.headers.get(REROUTE_DIRECTIVE_HEADER);
- if (isReroute === "no" && typeof i18n.fallback === "undefined") {
- return response;
- }
- if (type !== "page" && type !== "fallback") {
- return response;
- }
- if (requestIs404Or500(context.request, base)) {
- return response;
- }
- if (isRequestServerIsland(context.request, base)) {
- return response;
- }
- const { currentLocale } = context;
- switch (i18n.strategy) {
- // NOTE: theoretically, we should never hit this code path
- case "manual": {
- return response;
- }
- case "domains-prefix-other-locales": {
- if (localeHasntDomain(i18n, currentLocale)) {
- const result = prefixOtherLocales(context, response);
- if (result) {
- return result;
- }
- }
- break;
- }
- case "pathname-prefix-other-locales": {
- const result = prefixOtherLocales(context, response);
- if (result) {
- return result;
- }
- break;
- }
- case "domains-prefix-always-no-redirect": {
- if (localeHasntDomain(i18n, currentLocale)) {
- const result = _noFoundForNonLocaleRoute(context, response);
- if (result) {
- return result;
- }
- }
- break;
- }
- case "pathname-prefix-always-no-redirect": {
- const result = _noFoundForNonLocaleRoute(context, response);
- if (result) {
- return result;
- }
- break;
- }
- case "pathname-prefix-always": {
- const result = prefixAlways(context, response);
- if (result) {
- return result;
- }
- break;
- }
- case "domains-prefix-always": {
- if (localeHasntDomain(i18n, currentLocale)) {
- const result = prefixAlways(context, response);
- if (result) {
- return result;
- }
- }
- break;
- }
- }
- return _redirectToFallback(context, response);
- };
- }
- function localeHasntDomain(i18n, currentLocale) {
- for (const domainLocale of Object.values(i18n.domainLookupTable)) {
- if (domainLocale === currentLocale) {
- return false;
- }
- }
- return true;
- }
- const NOOP_ACTIONS_MOD = {
- server: {}
- };
- const FORM_CONTENT_TYPES = [
- "application/x-www-form-urlencoded",
- "multipart/form-data",
- "text/plain"
- ];
- const SAFE_METHODS = ["GET", "HEAD", "OPTIONS"];
- function createOriginCheckMiddleware() {
- return defineMiddleware((context, next) => {
- const { request, url, isPrerendered } = context;
- if (isPrerendered) {
- return next();
- }
- if (SAFE_METHODS.includes(request.method)) {
- return next();
- }
- const isSameOrigin = request.headers.get("origin") === url.origin;
- const hasContentType = request.headers.has("content-type");
- if (hasContentType) {
- const formLikeHeader = hasFormLikeHeader(request.headers.get("content-type"));
- if (formLikeHeader && !isSameOrigin) {
- return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
- status: 403
- });
- }
- } else {
- if (!isSameOrigin) {
- return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
- status: 403
- });
- }
- }
- return next();
- });
- }
- function hasFormLikeHeader(contentType) {
- if (contentType) {
- for (const FORM_CONTENT_TYPE of FORM_CONTENT_TYPES) {
- if (contentType.toLowerCase().includes(FORM_CONTENT_TYPE)) {
- return true;
- }
- }
- }
- return false;
- }
- function createDefaultRoutes(manifest) {
- const root = new URL(manifest.hrefRoot);
- return [
- {
- instance: default404Instance,
- matchesComponent: (filePath) => filePath.href === new URL(DEFAULT_404_COMPONENT, root).href,
- route: DEFAULT_404_ROUTE.route,
- component: DEFAULT_404_COMPONENT
- },
- {
- instance: createEndpoint(manifest),
- matchesComponent: (filePath) => filePath.href === new URL(SERVER_ISLAND_COMPONENT, root).href,
- route: SERVER_ISLAND_ROUTE,
- component: SERVER_ISLAND_COMPONENT
- }
- ];
- }
- class Pipeline {
- constructor(logger, manifest, runtimeMode, renderers, resolve, serverLike, streaming, adapterName = manifest.adapterName, clientDirectives = manifest.clientDirectives, inlinedScripts = manifest.inlinedScripts, compressHTML = manifest.compressHTML, i18n = manifest.i18n, middleware = manifest.middleware, routeCache = new RouteCache(logger, runtimeMode), site = manifest.site ? new URL(manifest.site) : void 0, defaultRoutes = createDefaultRoutes(manifest), actions = manifest.actions) {
- this.logger = logger;
- this.manifest = manifest;
- this.runtimeMode = runtimeMode;
- this.renderers = renderers;
- this.resolve = resolve;
- this.serverLike = serverLike;
- this.streaming = streaming;
- this.adapterName = adapterName;
- this.clientDirectives = clientDirectives;
- this.inlinedScripts = inlinedScripts;
- this.compressHTML = compressHTML;
- this.i18n = i18n;
- this.middleware = middleware;
- this.routeCache = routeCache;
- this.site = site;
- this.defaultRoutes = defaultRoutes;
- this.actions = actions;
- this.internalMiddleware = [];
- if (i18n?.strategy !== "manual") {
- this.internalMiddleware.push(
- createI18nMiddleware(i18n, manifest.base, manifest.trailingSlash, manifest.buildFormat)
- );
- }
- }
- internalMiddleware;
- resolvedMiddleware = void 0;
- resolvedActions = void 0;
- /**
- * Resolves the middleware from the manifest, and returns the `onRequest` function. If `onRequest` isn't there,
- * it returns a no-op function
- */
- async getMiddleware() {
- if (this.resolvedMiddleware) {
- return this.resolvedMiddleware;
- } else if (this.middleware) {
- const middlewareInstance = await this.middleware();
- const onRequest = middlewareInstance.onRequest ?? NOOP_MIDDLEWARE_FN;
- const internalMiddlewares = [onRequest];
- if (this.manifest.checkOrigin) {
- internalMiddlewares.unshift(createOriginCheckMiddleware());
- }
- this.resolvedMiddleware = sequence(...internalMiddlewares);
- return this.resolvedMiddleware;
- } else {
- this.resolvedMiddleware = NOOP_MIDDLEWARE_FN;
- return this.resolvedMiddleware;
- }
- }
- setActions(actions) {
- this.resolvedActions = actions;
- }
- async getActions() {
- if (this.resolvedActions) {
- return this.resolvedActions;
- } else if (this.actions) {
- return await this.actions();
- }
- return NOOP_ACTIONS_MOD;
- }
- async getAction(path) {
- const pathKeys = path.split(".").map((key) => decodeURIComponent(key));
- let { server } = await this.getActions();
- if (!server || !(typeof server === "object")) {
- throw new TypeError(
- `Expected \`server\` export in actions file to be an object. Received ${typeof server}.`
- );
- }
- for (const key of pathKeys) {
- if (!(key in server)) {
- throw new AstroError({
- ...ActionNotFoundError,
- message: ActionNotFoundError.message(pathKeys.join("."))
- });
- }
- server = server[key];
- }
- if (typeof server !== "function") {
- throw new TypeError(
- `Expected handler for action ${pathKeys.join(".")} to be a function. Received ${typeof server}.`
- );
- }
- return server;
- }
- }
- const RedirectComponentInstance = {
- default() {
- return new Response(null, {
- status: 301
- });
- }
- };
- const RedirectSinglePageBuiltModule = {
- page: () => Promise.resolve(RedirectComponentInstance),
- onRequest: (_, next) => next(),
- renderers: []
- };
- const dateTimeFormat = new Intl.DateTimeFormat([], {
- hour: "2-digit",
- minute: "2-digit",
- second: "2-digit",
- hour12: false
- });
- const levels = {
- debug: 20,
- info: 30,
- warn: 40,
- error: 50,
- silent: 90
- };
- function log(opts, level, label, message, newLine = true) {
- const logLevel = opts.level;
- const dest = opts.dest;
- const event = {
- label,
- level,
- message,
- newLine
- };
- if (!isLogLevelEnabled(logLevel, level)) {
- return;
- }
- dest.write(event);
- }
- function isLogLevelEnabled(configuredLogLevel, level) {
- return levels[configuredLogLevel] <= levels[level];
- }
- function info(opts, label, message, newLine = true) {
- return log(opts, "info", label, message, newLine);
- }
- function warn(opts, label, message, newLine = true) {
- return log(opts, "warn", label, message, newLine);
- }
- function error(opts, label, message, newLine = true) {
- return log(opts, "error", label, message, newLine);
- }
- function debug(...args) {
- if ("_astroGlobalDebug" in globalThis) {
- globalThis._astroGlobalDebug(...args);
- }
- }
- function getEventPrefix({ level, label }) {
- const timestamp = `${dateTimeFormat.format(/* @__PURE__ */ new Date())}`;
- const prefix = [];
- if (level === "error" || level === "warn") {
- prefix.push(bold(timestamp));
- prefix.push(`[${level.toUpperCase()}]`);
- } else {
- prefix.push(timestamp);
- }
- if (label) {
- prefix.push(`[${label}]`);
- }
- if (level === "error") {
- return red(prefix.join(" "));
- }
- if (level === "warn") {
- return yellow(prefix.join(" "));
- }
- if (prefix.length === 1) {
- return dim(prefix[0]);
- }
- return dim(prefix[0]) + " " + blue(prefix.splice(1).join(" "));
- }
- class Logger {
- options;
- constructor(options) {
- this.options = options;
- }
- info(label, message, newLine = true) {
- info(this.options, label, message, newLine);
- }
- warn(label, message, newLine = true) {
- warn(this.options, label, message, newLine);
- }
- error(label, message, newLine = true) {
- error(this.options, label, message, newLine);
- }
- debug(label, ...messages) {
- debug(label, ...messages);
- }
- level() {
- return this.options.level;
- }
- forkIntegrationLogger(label) {
- return new AstroIntegrationLogger(this.options, label);
- }
- }
- class AstroIntegrationLogger {
- options;
- label;
- constructor(logging, label) {
- this.options = logging;
- this.label = label;
- }
- /**
- * Creates a new logger instance with a new label, but the same log options.
- */
- fork(label) {
- return new AstroIntegrationLogger(this.options, label);
- }
- info(message) {
- info(this.options, this.label, message);
- }
- warn(message) {
- warn(this.options, this.label, message);
- }
- error(message) {
- error(this.options, this.label, message);
- }
- debug(message) {
- debug(this.label, message);
- }
- }
- const consoleLogDestination = {
- write(event) {
- let dest = console.error;
- if (levels[event.level] < levels["error"]) {
- dest = console.info;
- }
- if (event.label === "SKIP_FORMAT") {
- dest(event.message);
- } else {
- dest(getEventPrefix(event) + " " + event.message);
- }
- return true;
- }
- };
- function getAssetsPrefix(fileExtension, assetsPrefix) {
- if (!assetsPrefix) return "";
- if (typeof assetsPrefix === "string") return assetsPrefix;
- const dotLessFileExtension = fileExtension.slice(1);
- if (assetsPrefix[dotLessFileExtension]) {
- return assetsPrefix[dotLessFileExtension];
- }
- return assetsPrefix.fallback;
- }
- function createAssetLink(href, base, assetsPrefix) {
- if (assetsPrefix) {
- const pf = getAssetsPrefix(fileExtension(href), assetsPrefix);
- return joinPaths(pf, slash(href));
- } else if (base) {
- return prependForwardSlash(joinPaths(base, slash(href)));
- } else {
- return href;
- }
- }
- function createStylesheetElement(stylesheet, base, assetsPrefix) {
- if (stylesheet.type === "inline") {
- return {
- props: {},
- children: stylesheet.content
- };
- } else {
- return {
- props: {
- rel: "stylesheet",
- href: createAssetLink(stylesheet.src, base, assetsPrefix)
- },
- children: ""
- };
- }
- }
- function createStylesheetElementSet(stylesheets, base, assetsPrefix) {
- return new Set(stylesheets.map((s) => createStylesheetElement(s, base, assetsPrefix)));
- }
- function createModuleScriptElement(script, base, assetsPrefix) {
- if (script.type === "external") {
- return createModuleScriptElementWithSrc(script.value, base, assetsPrefix);
- } else {
- return {
- props: {
- type: "module"
- },
- children: script.value
- };
- }
- }
- function createModuleScriptElementWithSrc(src, base, assetsPrefix) {
- return {
- props: {
- type: "module",
- src: createAssetLink(src, base, assetsPrefix)
- },
- children: ""
- };
- }
- function redirectTemplate({
- status,
- absoluteLocation,
- relativeLocation,
- from
- }) {
- const delay = status === 302 ? 2 : 0;
- return `<!doctype html>
- <title>Redirecting to: ${relativeLocation}</title>
- <meta http-equiv="refresh" content="${delay};url=${relativeLocation}">
- <meta name="robots" content="noindex">
- <link rel="canonical" href="${absoluteLocation}">
- <body>
- <a href="${relativeLocation}">Redirecting ${from ? `from <code>${from}</code> ` : ""}to <code>${relativeLocation}</code></a>
- </body>`;
- }
- class AppPipeline extends Pipeline {
- static create({
- logger,
- manifest,
- runtimeMode,
- renderers,
- resolve,
- serverLike,
- streaming,
- defaultRoutes
- }) {
- const pipeline = new AppPipeline(
- logger,
- manifest,
- runtimeMode,
- renderers,
- resolve,
- serverLike,
- streaming,
- void 0,
- void 0,
- void 0,
- void 0,
- void 0,
- void 0,
- void 0,
- void 0,
- defaultRoutes
- );
- return pipeline;
- }
- headElements(routeData) {
- const routeInfo = this.manifest.routes.find((route) => route.routeData === routeData);
- const links = /* @__PURE__ */ new Set();
- const scripts = /* @__PURE__ */ new Set();
- const styles = createStylesheetElementSet(routeInfo?.styles ?? []);
- for (const script of routeInfo?.scripts ?? []) {
- if ("stage" in script) {
- if (script.stage === "head-inline") {
- scripts.add({
- props: {},
- children: script.children
- });
- }
- } else {
- scripts.add(createModuleScriptElement(script));
- }
- }
- return { links, styles, scripts };
- }
- componentMetadata() {
- }
- async getComponentByRoute(routeData) {
- const module = await this.getModuleForRoute(routeData);
- return module.page();
- }
- async tryRewrite(payload, request) {
- const { newUrl, pathname, routeData } = findRouteToRewrite({
- payload,
- request,
- routes: this.manifest?.routes.map((r) => r.routeData),
- trailingSlash: this.manifest.trailingSlash,
- buildFormat: this.manifest.buildFormat,
- base: this.manifest.base,
- outDir: this.serverLike ? this.manifest.buildClientDir : this.manifest.outDir
- });
- const componentInstance = await this.getComponentByRoute(routeData);
- return { newUrl, pathname, componentInstance, routeData };
- }
- async getModuleForRoute(route) {
- for (const defaultRoute of this.defaultRoutes) {
- if (route.component === defaultRoute.component) {
- return {
- page: () => Promise.resolve(defaultRoute.instance),
- renderers: []
- };
- }
- }
- if (route.type === "redirect") {
- return RedirectSinglePageBuiltModule;
- } else {
- if (this.manifest.pageMap) {
- const importComponentInstance = this.manifest.pageMap.get(route.component);
- if (!importComponentInstance) {
- throw new Error(
- `Unexpectedly unable to find a component instance for route ${route.route}`
- );
- }
- return await importComponentInstance();
- } else if (this.manifest.pageModule) {
- return this.manifest.pageModule;
- }
- throw new Error(
- "Astro couldn't find the correct page to render, probably because it wasn't correctly mapped for SSR usage. This is an internal error, please file an issue."
- );
- }
- }
- }
- class App {
- #manifest;
- #manifestData;
- #logger = new Logger({
- dest: consoleLogDestination,
- level: "info"
- });
- #baseWithoutTrailingSlash;
- #pipeline;
- #adapterLogger;
- constructor(manifest, streaming = true) {
- this.#manifest = manifest;
- this.#manifestData = {
- routes: manifest.routes.map((route) => route.routeData)
- };
- ensure404Route(this.#manifestData);
- this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
- this.#pipeline = this.#createPipeline(streaming);
- this.#adapterLogger = new AstroIntegrationLogger(
- this.#logger.options,
- this.#manifest.adapterName
- );
- }
- getAdapterLogger() {
- return this.#adapterLogger;
- }
- getAllowedDomains() {
- return this.#manifest.allowedDomains;
- }
- get manifest() {
- return this.#manifest;
- }
- set manifest(value) {
- this.#manifest = value;
- }
- matchesAllowedDomains(forwardedHost, protocol) {
- return App.validateForwardedHost(forwardedHost, this.#manifest.allowedDomains, protocol);
- }
- static validateForwardedHost(forwardedHost, allowedDomains, protocol) {
- if (!allowedDomains || allowedDomains.length === 0) {
- return false;
- }
- try {
- const testUrl = new URL(`${protocol || "https"}://${forwardedHost}`);
- return allowedDomains.some((pattern) => {
- return matchPattern(testUrl, pattern);
- });
- } catch {
- return false;
- }
- }
- /**
- * Creates a pipeline by reading the stored manifest
- *
- * @param streaming
- * @private
- */
- #createPipeline(streaming = false) {
- return AppPipeline.create({
- logger: this.#logger,
- manifest: this.#manifest,
- runtimeMode: "production",
- renderers: this.#manifest.renderers,
- defaultRoutes: createDefaultRoutes(this.#manifest),
- resolve: async (specifier) => {
- if (!(specifier in this.#manifest.entryModules)) {
- throw new Error(`Unable to resolve [${specifier}]`);
- }
- const bundlePath = this.#manifest.entryModules[specifier];
- if (bundlePath.startsWith("data:") || bundlePath.length === 0) {
- return bundlePath;
- } else {
- return createAssetLink(bundlePath, this.#manifest.base, this.#manifest.assetsPrefix);
- }
- },
- serverLike: true,
- streaming
- });
- }
- set setManifestData(newManifestData) {
- this.#manifestData = newManifestData;
- }
- removeBase(pathname) {
- if (pathname.startsWith(this.#manifest.base)) {
- return pathname.slice(this.#baseWithoutTrailingSlash.length + 1);
- }
- return pathname;
- }
- /**
- * It removes the base from the request URL, prepends it with a forward slash and attempts to decoded it.
- *
- * If the decoding fails, it logs the error and return the pathname as is.
- * @param request
- * @private
- */
- #getPathnameFromRequest(request) {
- const url = new URL(request.url);
- const pathname = prependForwardSlash(this.removeBase(url.pathname));
- try {
- return decodeURI(pathname);
- } catch (e) {
- this.getAdapterLogger().error(e.toString());
- return pathname;
- }
- }
- /**
- * Given a `Request`, it returns the `RouteData` that matches its `pathname`. By default, prerendered
- * routes aren't returned, even if they are matched.
- *
- * When `allowPrerenderedRoutes` is `true`, the function returns matched prerendered routes too.
- * @param request
- * @param allowPrerenderedRoutes
- */
- match(request, allowPrerenderedRoutes = false) {
- const url = new URL(request.url);
- if (this.#manifest.assets.has(url.pathname)) return void 0;
- let pathname = this.#computePathnameFromDomain(request);
- if (!pathname) {
- pathname = prependForwardSlash(this.removeBase(url.pathname));
- }
- let routeData = matchRoute(decodeURI(pathname), this.#manifestData);
- if (!routeData) return void 0;
- if (allowPrerenderedRoutes) {
- return routeData;
- } else if (routeData.prerender) {
- return void 0;
- }
- return routeData;
- }
- #computePathnameFromDomain(request) {
- let pathname = void 0;
- const url = new URL(request.url);
- if (this.#manifest.i18n && (this.#manifest.i18n.strategy === "domains-prefix-always" || this.#manifest.i18n.strategy === "domains-prefix-other-locales" || this.#manifest.i18n.strategy === "domains-prefix-always-no-redirect")) {
- let forwardedHost = request.headers.get("X-Forwarded-Host");
- let protocol = request.headers.get("X-Forwarded-Proto");
- if (protocol) {
- protocol = protocol + ":";
- } else {
- protocol = url.protocol;
- }
- if (forwardedHost && !this.matchesAllowedDomains(forwardedHost, protocol?.replace(":", ""))) {
- forwardedHost = null;
- }
- let host = forwardedHost;
- if (!host) {
- host = request.headers.get("Host");
- }
- if (host && protocol) {
- host = host.split(":")[0];
- try {
- let locale;
- const hostAsUrl = new URL(`${protocol}//${host}`);
- for (const [domainKey, localeValue] of Object.entries(
- this.#manifest.i18n.domainLookupTable
- )) {
- const domainKeyAsUrl = new URL(domainKey);
- if (hostAsUrl.host === domainKeyAsUrl.host && hostAsUrl.protocol === domainKeyAsUrl.protocol) {
- locale = localeValue;
- break;
- }
- }
- if (locale) {
- pathname = prependForwardSlash(
- joinPaths(normalizeTheLocale(locale), this.removeBase(url.pathname))
- );
- if (url.pathname.endsWith("/")) {
- pathname = appendForwardSlash(pathname);
- }
- }
- } catch (e) {
- this.#logger.error(
- "router",
- `Astro tried to parse ${protocol}//${host} as an URL, but it threw a parsing error. Check the X-Forwarded-Host and X-Forwarded-Proto headers.`
- );
- this.#logger.error("router", `Error: ${e}`);
- }
- }
- }
- return pathname;
- }
- #redirectTrailingSlash(pathname) {
- const { trailingSlash } = this.#manifest;
- if (pathname === "/" || isInternalPath(pathname)) {
- return pathname;
- }
- const path = collapseDuplicateTrailingSlashes(pathname, trailingSlash !== "never");
- if (path !== pathname) {
- return path;
- }
- if (trailingSlash === "ignore") {
- return pathname;
- }
- if (trailingSlash === "always" && !hasFileExtension(pathname)) {
- return appendForwardSlash(pathname);
- }
- if (trailingSlash === "never") {
- return removeTrailingForwardSlash(pathname);
- }
- return pathname;
- }
- async render(request, renderOptions) {
- let routeData;
- let locals;
- let clientAddress;
- let addCookieHeader;
- const url = new URL(request.url);
- const redirect = this.#redirectTrailingSlash(url.pathname);
- const prerenderedErrorPageFetch = renderOptions?.prerenderedErrorPageFetch ?? fetch;
- if (redirect !== url.pathname) {
- const status = request.method === "GET" ? 301 : 308;
- return new Response(
- redirectTemplate({
- status,
- relativeLocation: url.pathname,
- absoluteLocation: redirect,
- from: request.url
- }),
- {
- status,
- headers: {
- location: redirect + url.search
- }
- }
- );
- }
- addCookieHeader = renderOptions?.addCookieHeader;
- clientAddress = renderOptions?.clientAddress ?? Reflect.get(request, clientAddressSymbol);
- routeData = renderOptions?.routeData;
- locals = renderOptions?.locals;
- if (routeData) {
- this.#logger.debug(
- "router",
- "The adapter " + this.#manifest.adapterName + " provided a custom RouteData for ",
- request.url
- );
- this.#logger.debug("router", "RouteData:\n" + routeData);
- }
- if (locals) {
- if (typeof locals !== "object") {
- const error = new AstroError(LocalsNotAnObject);
- this.#logger.error(null, error.stack);
- return this.#renderError(request, {
- status: 500,
- error,
- clientAddress,
- prerenderedErrorPageFetch
- });
- }
- }
- if (!routeData) {
- routeData = this.match(request);
- this.#logger.debug("router", "Astro matched the following route for " + request.url);
- this.#logger.debug("router", "RouteData:\n" + routeData);
- }
- if (!routeData) {
- routeData = this.#manifestData.routes.find(
- (route) => route.component === "404.astro" || route.component === DEFAULT_404_COMPONENT
- );
- }
- if (!routeData) {
- this.#logger.debug("router", "Astro hasn't found routes that match " + request.url);
- this.#logger.debug("router", "Here's the available routes:\n", this.#manifestData);
- return this.#renderError(request, {
- locals,
- status: 404,
- clientAddress,
- prerenderedErrorPageFetch
- });
- }
- const pathname = this.#getPathnameFromRequest(request);
- const defaultStatus = this.#getDefaultStatusCode(routeData, pathname);
- let response;
- let session;
- try {
- const mod = await this.#pipeline.getModuleForRoute(routeData);
- const renderContext = await RenderContext.create({
- pipeline: this.#pipeline,
- locals,
- pathname,
- request,
- routeData,
- status: defaultStatus,
- clientAddress
- });
- session = renderContext.session;
- response = await renderContext.render(await mod.page());
- } catch (err) {
- this.#logger.error(null, err.stack || err.message || String(err));
- return this.#renderError(request, {
- locals,
- status: 500,
- error: err,
- clientAddress,
- prerenderedErrorPageFetch
- });
- } finally {
- await session?.[PERSIST_SYMBOL]();
- }
- if (REROUTABLE_STATUS_CODES.includes(response.status) && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== "no") {
- return this.#renderError(request, {
- locals,
- response,
- status: response.status,
- // We don't have an error to report here. Passing null means we pass nothing intentionally
- // while undefined means there's no error
- error: response.status === 500 ? null : void 0,
- clientAddress,
- prerenderedErrorPageFetch
- });
- }
- if (response.headers.has(REROUTE_DIRECTIVE_HEADER)) {
- response.headers.delete(REROUTE_DIRECTIVE_HEADER);
- }
- if (addCookieHeader) {
- for (const setCookieHeaderValue of App.getSetCookieFromResponse(response)) {
- response.headers.append("set-cookie", setCookieHeaderValue);
- }
- }
- Reflect.set(response, responseSentSymbol, true);
- return response;
- }
- setCookieHeaders(response) {
- return getSetCookiesFromResponse(response);
- }
- /**
- * Reads all the cookies written by `Astro.cookie.set()` onto the passed response.
- * For example,
- * ```ts
- * for (const cookie_ of App.getSetCookieFromResponse(response)) {
- * const cookie: string = cookie_
- * }
- * ```
- * @param response The response to read cookies from.
- * @returns An iterator that yields key-value pairs as equal-sign-separated strings.
- */
- static getSetCookieFromResponse = getSetCookiesFromResponse;
- /**
- * If it is a known error code, try sending the according page (e.g. 404.astro / 500.astro).
- * This also handles pre-rendered /404 or /500 routes
- */
- async #renderError(request, {
- locals,
- status,
- response: originalResponse,
- skipMiddleware = false,
- error,
- clientAddress,
- prerenderedErrorPageFetch
- }) {
- const errorRoutePath = `/${status}${this.#manifest.trailingSlash === "always" ? "/" : ""}`;
- const errorRouteData = matchRoute(errorRoutePath, this.#manifestData);
- const url = new URL(request.url);
- if (errorRouteData) {
- if (errorRouteData.prerender) {
- const maybeDotHtml = errorRouteData.route.endsWith(`/${status}`) ? ".html" : "";
- const statusURL = new URL(
- `${this.#baseWithoutTrailingSlash}/${status}${maybeDotHtml}`,
- url
- );
- if (statusURL.toString() !== request.url) {
- const response2 = await prerenderedErrorPageFetch(statusURL.toString());
- const override = { status, removeContentEncodingHeaders: true };
- return this.#mergeResponses(response2, originalResponse, override);
- }
- }
- const mod = await this.#pipeline.getModuleForRoute(errorRouteData);
- let session;
- try {
- const renderContext = await RenderContext.create({
- locals,
- pipeline: this.#pipeline,
- middleware: skipMiddleware ? NOOP_MIDDLEWARE_FN : void 0,
- pathname: this.#getPathnameFromRequest(request),
- request,
- routeData: errorRouteData,
- status,
- props: { error },
- clientAddress
- });
- session = renderContext.session;
- const response2 = await renderContext.render(await mod.page());
- return this.#mergeResponses(response2, originalResponse);
- } catch {
- if (skipMiddleware === false) {
- return this.#renderError(request, {
- locals,
- status,
- response: originalResponse,
- skipMiddleware: true,
- clientAddress,
- prerenderedErrorPageFetch
- });
- }
- } finally {
- await session?.[PERSIST_SYMBOL]();
- }
- }
- const response = this.#mergeResponses(new Response(null, { status }), originalResponse);
- Reflect.set(response, responseSentSymbol, true);
- return response;
- }
- #mergeResponses(newResponse, originalResponse, override) {
- let newResponseHeaders = newResponse.headers;
- if (override?.removeContentEncodingHeaders) {
- newResponseHeaders = new Headers(newResponseHeaders);
- newResponseHeaders.delete("Content-Encoding");
- newResponseHeaders.delete("Content-Length");
- }
- if (!originalResponse) {
- if (override !== void 0) {
- return new Response(newResponse.body, {
- status: override.status,
- statusText: newResponse.statusText,
- headers: newResponseHeaders
- });
- }
- return newResponse;
- }
- const status = override?.status ? override.status : originalResponse.status === 200 ? newResponse.status : originalResponse.status;
- try {
- originalResponse.headers.delete("Content-type");
- } catch {
- }
- const mergedHeaders = new Map([
- ...Array.from(newResponseHeaders),
- ...Array.from(originalResponse.headers)
- ]);
- const newHeaders = new Headers();
- for (const [name, value] of mergedHeaders) {
- newHeaders.set(name, value);
- }
- return new Response(newResponse.body, {
- status,
- statusText: status === 200 ? newResponse.statusText : originalResponse.statusText,
- // If you're looking at here for possible bugs, it means that it's not a bug.
- // With the middleware, users can meddle with headers, and we should pass to the 404/500.
- // If users see something weird, it's because they are setting some headers they should not.
- //
- // Although, we don't want it to replace the content-type, because the error page must return `text/html`
- headers: newHeaders
- });
- }
- #getDefaultStatusCode(routeData, pathname) {
- if (!routeData.pattern.test(pathname)) {
- for (const fallbackRoute of routeData.fallbackRoutes) {
- if (fallbackRoute.pattern.test(pathname)) {
- return 302;
- }
- }
- }
- const route = removeTrailingForwardSlash(routeData.route);
- if (route.endsWith("/404")) return 404;
- if (route.endsWith("/500")) return 500;
- return 200;
- }
- }
- async function handle(manifest, app, request, env, context) {
- const { pathname } = new URL(request.url);
- const bindingName = "SESSION";
- globalThis.__env__ ??= {};
- globalThis.__env__[bindingName] = env[bindingName];
- if (manifest.assets.has(pathname)) {
- return env.ASSETS.fetch(request.url.replace(/\.html$/, ""));
- }
- const routeData = app.match(request);
- if (!routeData) {
- const asset = await env.ASSETS.fetch(
- request.url.replace(/index.html$/, "").replace(/\.html$/, "")
- );
- if (asset.status !== 404) {
- return asset;
- }
- }
- Reflect.set(request, Symbol.for("astro.clientAddress"), request.headers.get("cf-connecting-ip"));
- const locals = {
- runtime: {
- env,
- cf: request.cf,
- caches,
- ctx: {
- waitUntil: (promise) => context.waitUntil(promise),
- // Currently not available: https://developers.cloudflare.com/pages/platform/known-issues/#pages-functions
- passThroughOnException: () => {
- throw new Error(
- "`passThroughOnException` is currently not available in Cloudflare Pages. See https://developers.cloudflare.com/pages/platform/known-issues/#pages-functions."
- );
- },
- props: {}
- }
- }
- };
- const response = await app.render(
- request,
- {
- routeData,
- locals,
- prerenderedErrorPageFetch: async (url) => {
- return env.ASSETS.fetch(url.replace(/\.html$/, ""));
- }
- }
- );
- if (app.setCookieHeaders) {
- for (const setCookieHeader of app.setCookieHeaders(response)) {
- response.headers.append("Set-Cookie", setCookieHeader);
- }
- }
- return response;
- }
- function createExports(manifest) {
- const app = new App(manifest);
- const fetch = async (request, env, context) => {
- return await handle(manifest, app, request, env, context);
- };
- return { default: { fetch } };
- }
- const serverEntrypointModule = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
- __proto__: null,
- createExports
- }, Symbol.toStringTag, { value: 'Module' }));
- export { createExports as c, serverEntrypointModule as s };
|