wrangler 2.1.13 → 2.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,159 @@
1
+ import * as fs from "fs";
2
+ import { findUpSync } from "find-up";
3
+ import { getEntry } from "./entry";
4
+ import { logger } from "./logger";
5
+ import type { Config } from "./config";
6
+ import type { CfWorkerInit } from "./worker";
7
+
8
+ // Currently includes bindings & rules for declaring modules
9
+ export type PartialConfigToDTS = CfWorkerInit["bindings"] & {
10
+ rules: Config["rules"];
11
+ };
12
+ export async function generateTypes(
13
+ configToDTS: PartialConfigToDTS,
14
+ config: Config
15
+ ) {
16
+ const entry = await getEntry({}, config, "types");
17
+ const envTypeStructure: string[] = [];
18
+
19
+ if (configToDTS.kv_namespaces) {
20
+ for (const kvNamespace of configToDTS.kv_namespaces) {
21
+ envTypeStructure.push(` ${kvNamespace.binding}: KVNamespace;`);
22
+ }
23
+ }
24
+
25
+ if (configToDTS.vars) {
26
+ for (const varName in configToDTS.vars) {
27
+ const varValue = configToDTS.vars[varName];
28
+ if (
29
+ typeof varValue === "string" ||
30
+ typeof varValue === "number" ||
31
+ typeof varValue === "boolean"
32
+ ) {
33
+ envTypeStructure.push(` ${varName}: ${varValue};`);
34
+ }
35
+ if (typeof varValue === "object" && varValue !== null) {
36
+ envTypeStructure.push(` ${varName}: ${JSON.stringify(varValue)};`);
37
+ }
38
+ }
39
+ }
40
+
41
+ if (configToDTS.durable_objects?.bindings) {
42
+ for (const durableObject of configToDTS.durable_objects.bindings) {
43
+ envTypeStructure.push(` ${durableObject.name}: DurableObjectNamespace;`);
44
+ }
45
+ }
46
+
47
+ if (configToDTS.r2_buckets) {
48
+ for (const R2Bucket of configToDTS.r2_buckets) {
49
+ envTypeStructure.push(` ${R2Bucket.binding}: R2Bucket;`);
50
+ }
51
+ }
52
+
53
+ if (configToDTS.d1_databases) {
54
+ for (const d1 of configToDTS.d1_databases) {
55
+ envTypeStructure.push(` ${d1.binding}: D1Database;`);
56
+ }
57
+ }
58
+
59
+ if (configToDTS.services) {
60
+ for (const service of configToDTS.services) {
61
+ envTypeStructure.push(` ${service.binding}: Fetcher;`);
62
+ }
63
+ }
64
+
65
+ if (configToDTS.dispatch_namespaces) {
66
+ for (const namespace of configToDTS.dispatch_namespaces) {
67
+ envTypeStructure.push(` ${namespace.binding}: any;`);
68
+ }
69
+ }
70
+
71
+ if (configToDTS.logfwdr?.schema) {
72
+ envTypeStructure.push(` LOGFWDR_SCHEMA: any;`);
73
+ }
74
+
75
+ if (configToDTS.data_blobs) {
76
+ for (const dataBlobs in configToDTS.data_blobs) {
77
+ envTypeStructure.push(` ${dataBlobs}: ArrayBuffer;`);
78
+ }
79
+ }
80
+
81
+ if (configToDTS.text_blobs) {
82
+ for (const textBlobs in configToDTS.text_blobs) {
83
+ envTypeStructure.push(` ${textBlobs}: string;`);
84
+ }
85
+ }
86
+
87
+ if (configToDTS.unsafe) {
88
+ for (const unsafe of configToDTS.unsafe) {
89
+ envTypeStructure.push(` ${unsafe.name}: any;`);
90
+ }
91
+ }
92
+
93
+ const modulesTypeStructure: string[] = [];
94
+ if (configToDTS.rules) {
95
+ const moduleTypeMap = {
96
+ Text: "string",
97
+ Data: "ArrayBuffer",
98
+ CompiledWasm: "WebAssembly.Module",
99
+ };
100
+ for (const ruleObject of configToDTS.rules) {
101
+ const typeScriptType =
102
+ moduleTypeMap[ruleObject.type as keyof typeof moduleTypeMap];
103
+ if (typeScriptType !== undefined) {
104
+ ruleObject.globs.forEach((glob) => {
105
+ modulesTypeStructure.push(`declare module "*.${glob
106
+ .split(".")
107
+ .at(-1)}" {
108
+ const value: ${typeScriptType};
109
+ export default value;
110
+ }`);
111
+ });
112
+ }
113
+ }
114
+ }
115
+
116
+ function writeDTSFile(
117
+ typesString: string[],
118
+ formatType: "modules" | "service-worker"
119
+ ) {
120
+ const wranglerOverrideDTSPath = findUpSync("worker-configuration.d.ts");
121
+ try {
122
+ if (
123
+ wranglerOverrideDTSPath !== undefined &&
124
+ !fs
125
+ .readFileSync(wranglerOverrideDTSPath, "utf8")
126
+ .includes("***AUTO GENERATED BY WORKERS CLI WRANGLER***")
127
+ ) {
128
+ throw new Error(
129
+ "A non-wrangler worker-configuration.d.ts already exists, please rename and try again."
130
+ );
131
+ }
132
+ } catch (error) {
133
+ if (error instanceof Error && !error.message.includes("not found")) {
134
+ throw error;
135
+ }
136
+ }
137
+
138
+ let combinedTypeStrings = "";
139
+ if (formatType === "modules") {
140
+ combinedTypeStrings = `interface Env {\n${typesString.join(
141
+ "\n"
142
+ )} \n}\n${modulesTypeStructure.join("\n")}`;
143
+ } else {
144
+ combinedTypeStrings = `declare global {\n${typesString.join(
145
+ "\n"
146
+ )} \n}\n${modulesTypeStructure.join("\n")}`;
147
+ }
148
+
149
+ if (envTypeStructure.length || modulesTypeStructure.length) {
150
+ fs.writeFileSync(
151
+ "worker-configuration.d.ts",
152
+ `// Generated by Wrangler on ${new Date()}` + "\n" + combinedTypeStrings
153
+ );
154
+ logger.log(combinedTypeStrings);
155
+ }
156
+ }
157
+
158
+ writeDTSFile(envTypeStructure, entry.format);
159
+ }
@@ -0,0 +1,128 @@
1
+ import { isRoutingRuleMatch } from "../pages-dev-util";
2
+
3
+ describe("isRoutingRuleMatch", () => {
4
+ it("should match rules referencing root level correctly", () => {
5
+ const routingRule = "/";
6
+
7
+ expect(isRoutingRuleMatch("/", routingRule)).toBeTruthy();
8
+ expect(isRoutingRuleMatch("/foo", routingRule)).toBeFalsy();
9
+ expect(isRoutingRuleMatch("/foo/", routingRule)).toBeFalsy();
10
+ expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeFalsy();
11
+ });
12
+
13
+ it("should match include-all rules correctly", () => {
14
+ const routingRule = "/*";
15
+
16
+ expect(isRoutingRuleMatch("/", routingRule)).toBeTruthy();
17
+ expect(isRoutingRuleMatch("/foo", routingRule)).toBeTruthy();
18
+ expect(isRoutingRuleMatch("/foo/", routingRule)).toBeTruthy();
19
+ expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
20
+ expect(isRoutingRuleMatch("/foo/bar/", routingRule)).toBeTruthy();
21
+ expect(isRoutingRuleMatch("/foo/bar/baz", routingRule)).toBeTruthy();
22
+ expect(isRoutingRuleMatch("/foo/bar/baz/", routingRule)).toBeTruthy();
23
+ });
24
+
25
+ it("should match `/*` suffix-ed rules correctly", () => {
26
+ let routingRule = "/foo/*";
27
+
28
+ expect(isRoutingRuleMatch("/foo", routingRule)).toBeTruthy();
29
+ expect(isRoutingRuleMatch("/foo/", routingRule)).toBeTruthy();
30
+ expect(isRoutingRuleMatch("/foobar", routingRule)).toBeFalsy();
31
+ expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
32
+ expect(isRoutingRuleMatch("/foo/bar/baz", routingRule)).toBeTruthy();
33
+ expect(isRoutingRuleMatch("/bar/foo", routingRule)).toBeFalsy();
34
+ expect(isRoutingRuleMatch("/bar/foo/baz", routingRule)).toBeFalsy();
35
+
36
+ routingRule = "/foo/bar/*";
37
+
38
+ expect(isRoutingRuleMatch("/foo", routingRule)).toBeFalsy();
39
+ expect(isRoutingRuleMatch("/foo/", routingRule)).toBeFalsy();
40
+ expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
41
+ expect(isRoutingRuleMatch("/foo/bar/baz", routingRule)).toBeTruthy();
42
+ expect(isRoutingRuleMatch("/foo/barfoo", routingRule)).toBeFalsy();
43
+ expect(isRoutingRuleMatch("baz/foo/bar", routingRule)).toBeFalsy();
44
+ expect(isRoutingRuleMatch("baz/foo/bar/", routingRule)).toBeFalsy();
45
+ });
46
+
47
+ it("should match `/` suffix-ed rules correctly", () => {
48
+ let routingRule = "/foo/";
49
+ expect(isRoutingRuleMatch("/foo/", routingRule)).toBeTruthy();
50
+ expect(isRoutingRuleMatch("/foo", routingRule)).toBeTruthy();
51
+
52
+ routingRule = "/foo/bar/";
53
+ expect(isRoutingRuleMatch("/foo/bar/", routingRule)).toBeTruthy();
54
+ expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
55
+ });
56
+
57
+ it("should match `*` suffix-ed rules correctly", () => {
58
+ let routingRule = "/foo*";
59
+ expect(isRoutingRuleMatch("/foo", routingRule)).toBeTruthy();
60
+ expect(isRoutingRuleMatch("/foo/", routingRule)).toBeTruthy();
61
+ expect(isRoutingRuleMatch("/foobar", routingRule)).toBeTruthy();
62
+ expect(isRoutingRuleMatch("/barfoo", routingRule)).toBeFalsy();
63
+ expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
64
+ expect(isRoutingRuleMatch("/bar/foo", routingRule)).toBeFalsy();
65
+ expect(isRoutingRuleMatch("/bar/foobar", routingRule)).toBeFalsy();
66
+ expect(isRoutingRuleMatch("/foo/bar/baz", routingRule)).toBeTruthy();
67
+ expect(isRoutingRuleMatch("/bar/foo/baz", routingRule)).toBeFalsy();
68
+
69
+ routingRule = "/foo/bar*";
70
+ expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
71
+ expect(isRoutingRuleMatch("/foo/bar/", routingRule)).toBeTruthy();
72
+ expect(isRoutingRuleMatch("/foo/barfoo", routingRule)).toBeTruthy();
73
+ expect(isRoutingRuleMatch("/bar/foo/barfoo", routingRule)).toBeFalsy();
74
+ expect(isRoutingRuleMatch("/foo/bar/baz", routingRule)).toBeTruthy();
75
+ expect(isRoutingRuleMatch("/bar/foo/bar/baz", routingRule)).toBeFalsy();
76
+ });
77
+
78
+ it("should match rules without wildcards correctly", () => {
79
+ let routingRule = "/foo";
80
+
81
+ expect(isRoutingRuleMatch("/foo", routingRule)).toBeTruthy();
82
+ expect(isRoutingRuleMatch("/foo/", routingRule)).toBeTruthy();
83
+ expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeFalsy();
84
+ expect(isRoutingRuleMatch("/bar/foo", routingRule)).toBeFalsy();
85
+
86
+ routingRule = "/foo/bar";
87
+ expect(isRoutingRuleMatch("/foo/bar", routingRule)).toBeTruthy();
88
+ expect(isRoutingRuleMatch("/foo/bar/", routingRule)).toBeTruthy();
89
+ expect(isRoutingRuleMatch("/foo/bar/baz", routingRule)).toBeFalsy();
90
+ expect(isRoutingRuleMatch("/baz/foo/bar", routingRule)).toBeFalsy();
91
+ });
92
+
93
+ it("should throw an error if pathname or routing rule params are missing", () => {
94
+ // MISSING PATHNAME
95
+ expect(() =>
96
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
97
+ // @ts-ignore: sanity check
98
+ isRoutingRuleMatch(undefined, "/*")
99
+ ).toThrow("Pathname is undefined.");
100
+
101
+ expect(() =>
102
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
103
+ // @ts-ignore: sanity check
104
+ isRoutingRuleMatch(null, "/*")
105
+ ).toThrow("Pathname is undefined.");
106
+
107
+ expect(() => isRoutingRuleMatch("", "/*")).toThrow(
108
+ "Pathname is undefined."
109
+ );
110
+
111
+ // MISSING ROUTING RULE
112
+ expect(() =>
113
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
114
+ // @ts-ignore: sanity check
115
+ isRoutingRuleMatch("/foo", undefined)
116
+ ).toThrow("Routing rule is undefined.");
117
+
118
+ expect(() =>
119
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
120
+ // @ts-ignore: sanity check
121
+ isRoutingRuleMatch("/foo", null)
122
+ ).toThrow("Routing rule is undefined.");
123
+
124
+ expect(() => isRoutingRuleMatch("/foo", "")).toThrow(
125
+ "Routing rule is undefined."
126
+ );
127
+ });
128
+ });
@@ -2,30 +2,23 @@
2
2
  import worker from "__ENTRY_POINT__";
3
3
  // @ts-ignore entry point will get replaced
4
4
  export * from "__ENTRY_POINT__";
5
+ import { isRoutingRuleMatch } from "./pages-dev-util";
5
6
 
6
- const transformToRegex = (filter: string) => {
7
- return filter.replace("*", ".*");
8
- };
9
-
10
- const routes = {
11
- // @ts-ignore routes are injected
12
- include: __ROUTES__.include.map(transformToRegex),
13
- // @ts-ignore routes are injected
14
- exclude: __ROUTES__.exclude.map(transformToRegex) || [],
15
- };
7
+ // @ts-ignore routes are injected
8
+ const routes = __ROUTES__;
16
9
 
17
10
  export default {
18
11
  fetch(request, env, context) {
19
12
  const { pathname } = new URL(request.url);
20
13
 
21
14
  for (const exclude of routes.exclude) {
22
- if (pathname.match(exclude)) {
15
+ if (isRoutingRuleMatch(pathname, exclude)) {
23
16
  return env.ASSETS.fetch(request);
24
17
  }
25
18
  }
26
19
 
27
20
  for (const include of routes.include) {
28
- if (pathname.match(include)) {
21
+ if (isRoutingRuleMatch(pathname, include)) {
29
22
  return worker.fetch(request, env, context);
30
23
  }
31
24
  }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @param pathname A pathname string, such as `/foo` or `/foo/bar`
3
+ * @param routingRule The routing rule, such as `/foo/*`
4
+ * @returns True if pathname matches the routing rule
5
+ *
6
+ * / -> /
7
+ * /* -> /*
8
+ * /foo -> /foo
9
+ * /foo* -> /foo, /foo-bar, /foo/*
10
+ * /foo/* -> /foo, /foo/bar
11
+ */
12
+ export function isRoutingRuleMatch(
13
+ pathname: string,
14
+ routingRule: string
15
+ ): boolean {
16
+ // sanity checks
17
+ if (!pathname) {
18
+ throw new Error("Pathname is undefined.");
19
+ }
20
+ if (!routingRule) {
21
+ throw new Error("Routing rule is undefined.");
22
+ }
23
+
24
+ const ruleRegExp = transformRoutingRuleToRegExp(routingRule);
25
+ return pathname.match(ruleRegExp) !== null;
26
+ }
27
+
28
+ function transformRoutingRuleToRegExp(rule: string): RegExp {
29
+ let transformedRule;
30
+
31
+ if (rule === "/" || rule === "/*") {
32
+ transformedRule = rule;
33
+ } else if (rule.endsWith("/*")) {
34
+ // make `/*` an optional group so we can match both /foo/* and /foo
35
+ // /foo/* => /foo(/*)?
36
+ transformedRule = `${rule.substring(0, rule.length - 2)}(/*)?`;
37
+ } else if (rule.endsWith("/")) {
38
+ // make `/` an optional group so we can match both /foo/ and /foo
39
+ // /foo/ => /foo(/)?
40
+ transformedRule = `${rule.substring(0, rule.length - 1)}(/)?`;
41
+ } else if (rule.endsWith("*")) {
42
+ transformedRule = rule;
43
+ } else {
44
+ transformedRule = `${rule}(/)?`;
45
+ }
46
+
47
+ // /foo* => /foo.* => ^/foo.*$
48
+ transformedRule = `^${transformedRule.replace("*", ".*")}$`;
49
+
50
+ // ^/foo.*$ => /^\/foo.*$/
51
+ return new RegExp(transformedRule);
52
+ }
@@ -19,6 +19,7 @@ type EventContext<Env, P extends string, Data> = {
19
19
  request: Request;
20
20
  functionPath: string;
21
21
  waitUntil: (promise: Promise<unknown>) => void;
22
+ passThroughOnException: () => void;
22
23
  next: (input?: Request | string, init?: RequestInit) => Promise<Response>;
23
24
  env: Env & { ASSETS: { fetch: typeof fetch } };
24
25
  params: Params<P>;
@@ -29,6 +30,7 @@ type EventPluginContext<Env, P extends string, Data, PluginArgs> = {
29
30
  request: Request;
30
31
  functionPath: string;
31
32
  waitUntil: (promise: Promise<unknown>) => void;
33
+ passThroughOnException: () => void;
32
34
  next: (input?: Request | string, init?: RequestInit) => Promise<Response>;
33
35
  env: Env & { ASSETS: { fetch: typeof fetch } };
34
36
  params: Params<P>;
@@ -146,6 +148,8 @@ export default function (pluginArgs) {
146
148
  pluginArgs,
147
149
  env,
148
150
  waitUntil: workerContext.waitUntil.bind(workerContext),
151
+ passThroughOnException:
152
+ workerContext.passThroughOnException.bind(workerContext),
149
153
  };
150
154
 
151
155
  const response = await handler(context);
@@ -19,6 +19,7 @@ type EventContext<Env, P extends string, Data> = {
19
19
  request: Request;
20
20
  functionPath: string;
21
21
  waitUntil: (promise: Promise<unknown>) => void;
22
+ passThroughOnException: () => void;
22
23
  next: (input?: Request | string, init?: RequestInit) => Promise<Response>;
23
24
  env: Env & { ASSETS: { fetch: typeof fetch } };
24
25
  params: Params<P>;
@@ -53,6 +54,7 @@ type FetchEnv = {
53
54
 
54
55
  type WorkerContext = {
55
56
  waitUntil: (promise: Promise<unknown>) => void;
57
+ passThroughOnException: () => void;
56
58
  };
57
59
 
58
60
  function* executeRequest(request: Request) {
@@ -111,9 +113,16 @@ function* executeRequest(request: Request) {
111
113
  }
112
114
 
113
115
  export default {
114
- async fetch(request: Request, env: FetchEnv, workerContext: WorkerContext) {
116
+ async fetch(
117
+ originalRequest: Request,
118
+ env: FetchEnv,
119
+ workerContext: WorkerContext
120
+ ) {
121
+ let request = originalRequest;
115
122
  const handlerIterator = executeRequest(request);
116
123
  const data = {}; // arbitrary data the user can set between functions
124
+ let isFailOpen = false;
125
+
117
126
  const next = async (input?: RequestInfo, init?: RequestInit) => {
118
127
  if (input !== undefined) {
119
128
  let url = input;
@@ -135,6 +144,9 @@ export default {
135
144
  data,
136
145
  env,
137
146
  waitUntil: workerContext.waitUntil.bind(workerContext),
147
+ passThroughOnException: () => {
148
+ isFailOpen = true;
149
+ },
138
150
  };
139
151
 
140
152
  const response = await handler(context);
@@ -156,9 +168,14 @@ export default {
156
168
  };
157
169
 
158
170
  try {
159
- return next();
160
- } catch (err) {
161
- return new Response("Internal Error", { status: 500 });
171
+ return await next();
172
+ } catch (error) {
173
+ if (isFailOpen) {
174
+ const response = await env[__FALLBACK_SERVICE__].fetch(request);
175
+ return cloneResponse(response);
176
+ }
177
+
178
+ throw error;
162
179
  }
163
180
  },
164
181
  };
@@ -34,7 +34,8 @@
34
34
  // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
35
35
  // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
36
36
  "types": [
37
- "@cloudflare/workers-types"
37
+ "@cloudflare/workers-types",
38
+ "jest"
38
39
  ] /* Specify type package names to be included without being referenced in a source file. */,
39
40
  // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
40
41
  "resolveJsonModule": true /* Enable importing .json files */,