-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
worker.ts
140 lines (123 loc) · 3.55 KB
/
worker.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Needed because the @cloudflare/workers-types do not include the `process` global
/// <reference types="@types/node" />
import type { Options as KvAssetHandlerOptions } from "@cloudflare/kv-asset-handler";
import {
getAssetFromKV,
MethodNotAllowedError,
NotFoundError,
} from "@cloudflare/kv-asset-handler";
import type { AppLoadContext, ServerBuild } from "@remix-run/cloudflare";
import { createRequestHandler as createRemixRequestHandler } from "@remix-run/cloudflare";
/**
* A function that returns the value to use as `context` in route `loader` and
* `action` functions.
*
* You can think of this as an escape hatch that allows you to pass
* environment/platform-specific values through to your loader/action.
*/
export type GetLoadContextFunction = (event: FetchEvent) => AppLoadContext;
export type RequestHandler = (event: FetchEvent) => Promise<Response>;
/**
* Returns a request handler for the Cloudflare runtime that serves the
* Remix SSR response.
*/
export function createRequestHandler({
build,
getLoadContext,
mode,
}: {
build: ServerBuild;
getLoadContext?: GetLoadContextFunction;
mode?: string;
}): RequestHandler {
let handleRequest = createRemixRequestHandler(build, mode);
return (event: FetchEvent) => {
let loadContext = getLoadContext?.(event);
return handleRequest(event.request, loadContext);
};
}
export async function handleAsset(
event: FetchEvent,
build: ServerBuild,
options?: Partial<KvAssetHandlerOptions>
) {
try {
if (process.env.NODE_ENV === "development") {
return await getAssetFromKV(event, {
cacheControl: {
bypassCache: true,
},
...options,
});
}
let cacheControl = {};
let url = new URL(event.request.url);
let assetpath = build.assets.url.split("/").slice(0, -1).join("/");
let requestpath = url.pathname.split("/").slice(0, -1).join("/");
if (requestpath.startsWith(assetpath)) {
// Assets are hashed by Remix so are safe to cache in the browser
// And they're also hashed in KV storage, so are safe to cache on the edge
cacheControl = {
bypassCache: false,
edgeTTL: 31536000,
browserTTL: 31536000,
};
} else {
// Assets are not necessarily hashed in the request URL, so we cannot cache in the browser
// But they are hashed in KV storage, so we can cache on the edge
cacheControl = {
bypassCache: false,
edgeTTL: 31536000,
};
}
return await getAssetFromKV(event, {
cacheControl,
...options,
});
} catch (error: unknown) {
if (
error instanceof MethodNotAllowedError ||
error instanceof NotFoundError
) {
return null;
}
throw error;
}
}
export function createEventHandler({
build,
getLoadContext,
mode,
}: {
build: ServerBuild;
getLoadContext?: GetLoadContextFunction;
mode?: string;
}) {
let handleRequest = createRequestHandler({
build,
getLoadContext,
mode,
});
let handleEvent = async (event: FetchEvent) => {
let response = await handleAsset(event, build);
if (!response) {
response = await handleRequest(event);
}
return response;
};
return (event: FetchEvent) => {
try {
event.respondWith(handleEvent(event));
} catch (e: any) {
if (process.env.NODE_ENV === "development") {
event.respondWith(
new Response(e.message || e.toString(), {
status: 500,
})
);
return;
}
event.respondWith(new Response("Internal Error", { status: 500 }));
}
};
}