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 `
${from} ` : ""}to ${relativeLocation}
`;
}
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 };