summaryrefslogtreecommitdiff
path: root/node_modules/wrangler/templates/pages-template-worker.ts
diff options
context:
space:
mode:
authorakiyamn2023-09-24 23:22:21 +1000
committerakiyamn2023-09-24 23:22:21 +1000
commit4e87195739f2a5d9a05451b48773c8afdc680765 (patch)
tree9cba501844a4a11dcbdffc4050ed8189561c55ed /node_modules/wrangler/templates/pages-template-worker.ts
downloadprice-tracker-worker-4e87195739f2a5d9a05451b48773c8afdc680765.tar.gz
price-tracker-worker-4e87195739f2a5d9a05451b48773c8afdc680765.zip
Initial commit (by create-cloudflare CLI)
Diffstat (limited to 'node_modules/wrangler/templates/pages-template-worker.ts')
-rw-r--r--node_modules/wrangler/templates/pages-template-worker.ts198
1 files changed, 198 insertions, 0 deletions
diff --git a/node_modules/wrangler/templates/pages-template-worker.ts b/node_modules/wrangler/templates/pages-template-worker.ts
new file mode 100644
index 0000000..67e6cee
--- /dev/null
+++ b/node_modules/wrangler/templates/pages-template-worker.ts
@@ -0,0 +1,198 @@
+import { match } from "path-to-regexp";
+
+//note: this explicitly does not include the * character, as pages requires this
+const escapeRegex = /[.+?^${}()|[\]\\]/g;
+
+type HTTPMethod =
+ | "HEAD"
+ | "OPTIONS"
+ | "GET"
+ | "POST"
+ | "PUT"
+ | "PATCH"
+ | "DELETE";
+
+/* TODO: Grab these from @cloudflare/workers-types instead */
+type Params<P extends string = string> = Record<P, string | string[]>;
+
+type EventContext<Env, P extends string, Data> = {
+ request: Request;
+ functionPath: string;
+ waitUntil: (promise: Promise<unknown>) => void;
+ passThroughOnException: () => void;
+ next: (input?: Request | string, init?: RequestInit) => Promise<Response>;
+ env: Env & { ASSETS: { fetch: typeof fetch } };
+ params: Params<P>;
+ data: Data;
+};
+
+declare type PagesFunction<
+ Env = unknown,
+ P extends string = string,
+ Data extends Record<string, unknown> = Record<string, unknown>
+> = (context: EventContext<Env, P, Data>) => Response | Promise<Response>;
+/* end @cloudflare/workers-types */
+
+type RouteHandler = {
+ routePath: string;
+ mountPath: string;
+ method?: HTTPMethod;
+ modules: PagesFunction[];
+ middlewares: PagesFunction[];
+};
+
+// inject `routes` via ESBuild
+declare const routes: RouteHandler[];
+// define `__FALLBACK_SERVICE__` via ESBuild
+declare const __FALLBACK_SERVICE__: string;
+
+// expect an ASSETS fetcher binding pointing to the asset-server stage
+type FetchEnv = {
+ [name: string]: { fetch: typeof fetch };
+ ASSETS: { fetch: typeof fetch };
+};
+
+type WorkerContext = {
+ waitUntil: (promise: Promise<unknown>) => void;
+ passThroughOnException: () => void;
+};
+
+function* executeRequest(request: Request) {
+ const requestPath = new URL(request.url).pathname;
+
+ // First, iterate through the routes (backwards) and execute "middlewares" on partial route matches
+ for (const route of [...routes].reverse()) {
+ if (route.method && route.method !== request.method) {
+ continue;
+ }
+
+ // replaces with "\\$&", this prepends a backslash to the matched string, e.g. "[" becomes "\["
+ const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
+ end: false,
+ });
+ const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
+ end: false,
+ });
+ const matchResult = routeMatcher(requestPath);
+ const mountMatchResult = mountMatcher(requestPath);
+ if (matchResult && mountMatchResult) {
+ for (const handler of route.middlewares.flat()) {
+ yield {
+ handler,
+ params: matchResult.params as Params,
+ path: mountMatchResult.path,
+ };
+ }
+ }
+ }
+
+ // Then look for the first exact route match and execute its "modules"
+ for (const route of routes) {
+ if (route.method && route.method !== request.method) {
+ continue;
+ }
+ const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
+ end: true,
+ });
+ const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
+ end: false,
+ });
+ const matchResult = routeMatcher(requestPath);
+ const mountMatchResult = mountMatcher(requestPath);
+ if (matchResult && mountMatchResult && route.modules.length) {
+ for (const handler of route.modules.flat()) {
+ yield {
+ handler,
+ params: matchResult.params as Params,
+ path: matchResult.path,
+ };
+ }
+ break;
+ }
+ }
+}
+
+export default {
+ async fetch(
+ originalRequest: Request,
+ env: FetchEnv,
+ workerContext: WorkerContext
+ ) {
+ let request = originalRequest;
+ const handlerIterator = executeRequest(request);
+ let data = {}; // arbitrary data the user can set between functions
+ let isFailOpen = false;
+
+ const next = async (input?: RequestInfo, init?: RequestInit) => {
+ if (input !== undefined) {
+ let url = input;
+ if (typeof input === "string") {
+ url = new URL(input, request.url).toString();
+ }
+ request = new Request(url, init);
+ }
+
+ const result = handlerIterator.next();
+ // Note we can't use `!result.done` because this doesn't narrow to the correct type
+ if (result.done === false) {
+ const { handler, params, path } = result.value;
+ const context = {
+ request: new Request(request.clone()),
+ functionPath: path,
+ next,
+ params,
+ get data() {
+ return data;
+ },
+ set data(value) {
+ if (typeof value !== "object" || value === null) {
+ throw new Error("context.data must be an object");
+ }
+ // user has overriden context.data, so we need to merge it with the existing data
+ data = value;
+ },
+ env,
+ waitUntil: workerContext.waitUntil.bind(workerContext),
+ passThroughOnException: () => {
+ isFailOpen = true;
+ },
+ };
+
+ const response = await handler(context);
+
+ if (!(response instanceof Response)) {
+ throw new Error("Your Pages function should return a Response");
+ }
+
+ return cloneResponse(response);
+ } else if (__FALLBACK_SERVICE__) {
+ // There are no more handlers so finish with the fallback service (`env.ASSETS.fetch` in Pages' case)
+ const response = await env[__FALLBACK_SERVICE__].fetch(request);
+ return cloneResponse(response);
+ } else {
+ // There was not fallback service so actually make the request to the origin.
+ const response = await fetch(request);
+ return cloneResponse(response);
+ }
+ };
+
+ try {
+ return await next();
+ } catch (error) {
+ if (isFailOpen) {
+ const response = await env[__FALLBACK_SERVICE__].fetch(request);
+ return cloneResponse(response);
+ }
+
+ throw error;
+ }
+ },
+};
+
+// This makes a Response mutable
+const cloneResponse = (response: Response) =>
+ // https://fetch.spec.whatwg.org/#null-body-status
+ new Response(
+ [101, 204, 205, 304].includes(response.status) ? null : response.body,
+ response
+ );