1// Alchemy modifications are licensed under Apache-2.0.
2// This file includes third-party code; see /THIRD_PARTY_LICENSES.md.
3/**
4 * Vendored from `@astrojs/cloudflare` v14.1.3 (`src/utils/handler.ts`).
5 *
6 * The Worker `fetch` handler. Imports zero wrangler code; bindings are read
7 * from `cloudflare:workers` env and the `virtual:astro-cloudflare:config`
8 * virtual module served by this package's config plugin.
9 */
10import { env as globalEnv } from "cloudflare:workers";
11import type { RouteData } from "astro";
12import type { RenderOptions } from "astro/app";
13import { createApp } from "astro/app/entrypoint";
14import { setGetEnv } from "astro/env/setup";
15import {
16 cacheProviderEnabled,
17 compileImageConfig,
18 isPrerender,
19} from "virtual:astro-cloudflare:config";
20import {
21 createErrorPageFetch,
22 createLocals,
23 fallbackToAssets,
24 getClientAddress,
25 injectSessionBinding,
26 matchStaticAsset,
27 type Runtime,
28} from "./cf.ts";
29import { createGetEnv } from "./env.ts";
30import {
31 handlePrerenderRequest,
32 handleStaticImagesRequest,
33 handleStaticPathsRequest,
34 installPrerenderErrorPropagation,
35 isPrerenderRequest,
36 isStaticImagesRequest,
37 isStaticPathsRequest,
38} from "./prerender.ts";
39
40export type { Runtime };
41
42setGetEnv(createGetEnv(globalEnv));
43
44declare global {
45 // This is not a real global, but is injected using Vite define to allow us to specify the Images binding name in the config.
46 var __ASTRO_IMAGES_BINDING_NAME: string;
47}
48
49type CfResponse = Awaited<ReturnType<Required<ExportedHandler<Env>>["fetch"]>>;
50
51const app = createApp();
52
53if (isPrerender) {
54 installPrerenderErrorPropagation(app);
55}
56
57export async function handle(
58 request: Request,
59 env: Env,
60 context: ExecutionContext,
61): Promise<CfResponse> {
62 // Sessions must be wired before the prerender branch below — prerender
63 // requests return from `handlePrerenderRequest` without falling through,
64 // and session-touching pages render inside it.
65 injectSessionBinding(app.manifest, env);
66
67 // Handle prerender endpoints (only active during build prerender phase)
68 if (isPrerender) {
69 if (compileImageConfig) {
70 const { installAddStaticImage } =
71 await import("./static-image-collection.ts");
72 installAddStaticImage(compileImageConfig);
73 }
74
75 if (isStaticPathsRequest(request)) {
76 return handleStaticPathsRequest(app) as unknown as CfResponse;
77 }
78 if (isPrerenderRequest(request)) {
79 return handlePrerenderRequest(app, request) as unknown as CfResponse;
80 }
81 if (isStaticImagesRequest(request)) {
82 return handleStaticImagesRequest() as unknown as CfResponse;
83 }
84 }
85
86 // NOTE this ASSETS binding path is needed for users who are using `run_worker_first` routing
87 const staticAsset = matchStaticAsset(app.manifest, request.url, env);
88 if (staticAsset) return staticAsset as CfResponse;
89
90 let routeData: RouteData | undefined = undefined;
91 if (app.isDev()) {
92 const result = await app.devMatch(app.getPathnameFromRequest(request));
93 if (result) {
94 routeData = result.routeData;
95 }
96 } else {
97 routeData = app.match(request);
98 }
99
100 if (!routeData) {
101 // NOTE this ASSETS binding path is needed for users who are using `run_worker_first` routing
102 const asset = await fallbackToAssets(request.url, env);
103 if (asset) return asset as CfResponse;
104 }
105
106 const locals = createLocals(context);
107
108 const waitUntil: RenderOptions["waitUntil"] = context.waitUntil.bind(context);
109
110 const response = await app.render(request, {
111 routeData,
112 locals,
113 waitUntil,
114 prerenderedErrorPageFetch: createErrorPageFetch(env),
115 clientAddress: getClientAddress(request),
116 });
117
118 if (app.setCookieHeaders) {
119 for (const setCookieHeader of app.setCookieHeaders(response)) {
120 response.headers.append("Set-Cookie", setCookieHeader);
121 }
122 }
123
124 // When the Cloudflare cache provider is configured, default uncached
125 // responses to `no-store` so opting in to route caching never
126 // accidentally caches a route that didn't set any cache intent.
127 if (
128 cacheProviderEnabled &&
129 !response.headers.has("Cloudflare-CDN-Cache-Control")
130 ) {
131 response.headers.set("Cloudflare-CDN-Cache-Control", "no-store");
132 }
133
134 return response;
135}