wrangler 2.1.3 → 2.1.5

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.
Files changed (46) hide show
  1. package/miniflare-dist/index.mjs +4 -1
  2. package/package.json +1 -1
  3. package/src/__tests__/api-dev.test.ts +171 -1
  4. package/src/__tests__/api-devregistry.test.js +56 -0
  5. package/src/__tests__/api.test.ts +80 -0
  6. package/src/__tests__/dev.test.tsx +39 -1
  7. package/src/__tests__/helpers/worker-scripts/child-wrangler.toml +1 -0
  8. package/src/__tests__/helpers/{hello-world-worker.js → worker-scripts/hello-world-worker.js} +0 -0
  9. package/src/__tests__/helpers/worker-scripts/hello-world-wrangler.toml +1 -0
  10. package/src/__tests__/helpers/worker-scripts/parent-worker.js +8 -0
  11. package/src/__tests__/helpers/worker-scripts/parent-wrangler.toml +5 -0
  12. package/src/__tests__/init.test.ts +127 -3
  13. package/src/__tests__/middleware.scheduled.test.ts +135 -0
  14. package/src/__tests__/middleware.test.ts +703 -745
  15. package/src/__tests__/pages.test.ts +35 -40
  16. package/src/__tests__/secret.test.ts +76 -2
  17. package/src/api/dev.ts +58 -12
  18. package/src/api/index.ts +1 -0
  19. package/src/bundle.ts +14 -16
  20. package/src/cli.ts +2 -0
  21. package/src/create-worker-preview.ts +21 -3
  22. package/src/dev/dev.tsx +12 -21
  23. package/src/dev/local.tsx +1 -0
  24. package/src/dev/remote.tsx +37 -49
  25. package/src/dev/start-server.ts +43 -8
  26. package/src/dev/use-esbuild.ts +4 -0
  27. package/src/dev-registry.tsx +30 -0
  28. package/src/dev.tsx +17 -2
  29. package/src/index.tsx +11 -2
  30. package/src/init.ts +189 -121
  31. package/src/inspect.ts +26 -26
  32. package/src/miniflare-cli/assets.ts +8 -1
  33. package/src/pages/constants.ts +2 -1
  34. package/src/pages/dev.tsx +133 -10
  35. package/src/pages/errors.ts +48 -4
  36. package/src/pages/functions/routes-transformation.ts +1 -14
  37. package/src/pages/functions/routes-validation.test.ts +403 -0
  38. package/src/pages/functions/routes-validation.ts +202 -0
  39. package/src/pages/functions.tsx +4 -18
  40. package/src/pages/publish.tsx +6 -22
  41. package/templates/init-tests/test-jest-new-worker.js +25 -0
  42. package/templates/init-tests/test-vitest-new-worker.js +26 -0
  43. package/templates/middleware/middleware-scheduled.ts +2 -1
  44. package/templates/pages-dev-pipeline.ts +35 -0
  45. package/wrangler-dist/cli.d.ts +63 -3
  46. package/wrangler-dist/cli.js +825 -474
@@ -6099,7 +6099,10 @@ async function generateAssetsFetch(directory, log) {
6099
6099
  );
6100
6100
  }
6101
6101
  const body = readFileSync5(filepath);
6102
- const contentType = (0, import_mime.getType)(filepath) || "application/octet-stream";
6102
+ let contentType = (0, import_mime.getType)(filepath) || "application/octet-stream";
6103
+ if (contentType.startsWith("text/") && !contentType.includes("charset")) {
6104
+ contentType = `${contentType}; charset=utf-8`;
6105
+ }
6103
6106
  return { body, contentType };
6104
6107
  }
6105
6108
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wrangler",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "Command-line interface for all things Cloudflare Workers",
5
5
  "keywords": [
6
6
  "wrangler",
@@ -1,11 +1,14 @@
1
+ import * as fs from "node:fs";
2
+ import { Request } from "undici";
1
3
  import { unstable_dev } from "../api";
4
+ import { runInTempDir } from "./helpers/run-in-tmp";
2
5
 
3
6
  jest.unmock("undici");
4
7
 
5
8
  describe("unstable_dev", () => {
6
9
  it("should return Hello World", async () => {
7
10
  const worker = await unstable_dev(
8
- "src/__tests__/helpers/hello-world-worker.js",
11
+ "src/__tests__/helpers/worker-scripts/hello-world-worker.js",
9
12
  {},
10
13
  { disableExperimentalWarning: true }
11
14
  );
@@ -17,3 +20,170 @@ describe("unstable_dev", () => {
17
20
  await worker.stop();
18
21
  });
19
22
  });
23
+
24
+ describe("unstable dev fetch input protocol", () => {
25
+ it("should use http localProtocol", async () => {
26
+ const worker = await unstable_dev(
27
+ "src/__tests__/helpers/worker-scripts/hello-world-worker.js",
28
+ { localProtocol: "http" },
29
+ { disableExperimentalWarning: true }
30
+ );
31
+ const res = await worker.fetch();
32
+ if (res) {
33
+ const text = await res.text();
34
+ expect(text).toMatchInlineSnapshot(`"Hello World!"`);
35
+ }
36
+ await worker.stop();
37
+ });
38
+
39
+ it("should use undefined localProtocol", async () => {
40
+ const worker = await unstable_dev(
41
+ "src/__tests__/helpers/worker-scripts/hello-world-worker.js",
42
+ { localProtocol: undefined },
43
+ { disableExperimentalWarning: true }
44
+ );
45
+ const res = await worker.fetch();
46
+ if (res) {
47
+ const text = await res.text();
48
+ expect(text).toMatchInlineSnapshot(`"Hello World!"`);
49
+ }
50
+ await worker.stop();
51
+ });
52
+ });
53
+
54
+ describe("unstable dev fetch input parsing", () => {
55
+ runInTempDir();
56
+
57
+ it("should pass in a request object unchanged", async () => {
58
+ const scriptContent = `
59
+ export default {
60
+ fetch(request, env, ctx) {
61
+ const url = new URL(request.url);
62
+ if (url.pathname === "/test") {
63
+ if (request.method === "POST") {
64
+ return new Response("requestPOST");
65
+ }
66
+ return new Response("requestGET");
67
+ }
68
+ return new Response('Hello world');
69
+ }
70
+ };
71
+ `;
72
+ fs.writeFileSync("index.js", scriptContent);
73
+ const port = 21213;
74
+ const worker = await unstable_dev(
75
+ "index.js",
76
+ { port },
77
+ { disableExperimentalWarning: true }
78
+ );
79
+ const req = new Request("http://0.0.0.0:21213/test", {
80
+ method: "POST",
81
+ });
82
+ const resp = await worker.fetch(req);
83
+ let text;
84
+ if (resp) text = await resp.text();
85
+ expect(text).toMatchInlineSnapshot(`"requestPOST"`);
86
+ await worker.stop();
87
+ });
88
+
89
+ it("should strip back to pathname for URL objects", async () => {
90
+ const scriptContent = `
91
+ export default {
92
+ fetch(request, env, ctx) {
93
+ const url = new URL(request.url);
94
+ if (url.pathname === "/test") {
95
+ return new Response("request");
96
+ }
97
+ return new Response('Hello world');
98
+ }
99
+ };
100
+ `;
101
+ fs.writeFileSync("index.js", scriptContent);
102
+ const worker = await unstable_dev(
103
+ "index.js",
104
+ {},
105
+ { disableExperimentalWarning: true }
106
+ );
107
+ const url = new URL("http://localhost:80/test");
108
+ const resp = await worker.fetch(url);
109
+ let text;
110
+ if (resp) text = await resp.text();
111
+ expect(text).toMatchInlineSnapshot(`"request"`);
112
+ await worker.stop();
113
+ });
114
+
115
+ it("should allow full url passed in string, and stripped back to pathname", async () => {
116
+ const scriptContent = `
117
+ export default {
118
+ fetch(request, env, ctx) {
119
+ const url = new URL(request.url);
120
+ if (url.pathname === "/test") {
121
+ return new Response("request");
122
+ }
123
+ return new Response('Hello world');
124
+ }
125
+ };
126
+ `;
127
+ fs.writeFileSync("index.js", scriptContent);
128
+ const worker = await unstable_dev(
129
+ "index.js",
130
+ {},
131
+ { disableExperimentalWarning: true }
132
+ );
133
+ const resp = await worker.fetch("http://example.com/test");
134
+ let text;
135
+ if (resp) text = await resp.text();
136
+ expect(text).toMatchInlineSnapshot(`"request"`);
137
+ await worker.stop();
138
+ });
139
+
140
+ it("should allow pathname to be passed in", async () => {
141
+ const scriptContent = `
142
+ export default {
143
+ fetch(request, env, ctx) {
144
+ const url = new URL(request.url);
145
+ if (url.pathname === "/test") {
146
+ return new Response("request");
147
+ }
148
+ return new Response('Hello world');
149
+ }
150
+ };
151
+ `;
152
+ fs.writeFileSync("index.js", scriptContent);
153
+ const worker = await unstable_dev(
154
+ "index.js",
155
+ {},
156
+ { disableExperimentalWarning: true }
157
+ );
158
+ const resp = await worker.fetch("/test");
159
+ let text;
160
+ if (resp) text = await resp.text();
161
+ expect(text).toMatchInlineSnapshot(`"request"`);
162
+ await worker.stop();
163
+ });
164
+
165
+ it("should allow no input be passed in", async () => {
166
+ const scriptContent = `
167
+ export default {
168
+ fetch(request, env, ctx) {
169
+ const url = new URL(request.url);
170
+ if (url.pathname === "/test") {
171
+ return new Response("request");
172
+ }
173
+ return new Response('Hello world');
174
+ }
175
+ };
176
+ `;
177
+ fs.writeFileSync("index.js", scriptContent);
178
+ const worker = await unstable_dev(
179
+ "index.js",
180
+ {},
181
+ { disableExperimentalWarning: true }
182
+ );
183
+ const resp = await worker.fetch("");
184
+ let text;
185
+ if (resp) text = await resp.text();
186
+ expect(text).toMatchInlineSnapshot(`"Hello world"`);
187
+ await worker.stop();
188
+ });
189
+ });
@@ -0,0 +1,56 @@
1
+ import { unstable_dev } from "../api";
2
+ import { fetch } from "undici";
3
+
4
+ jest.unmock("undici");
5
+
6
+ /**
7
+ * a huge caveat to how testing multi-worker scripts works:
8
+ * you can't shutdown the first worker you spun up, or it'll kill the devRegistry
9
+ */
10
+ describe("multi-worker testing", () => {
11
+ let childWorker;
12
+ let parentWorker;
13
+
14
+ beforeAll(async () => {
15
+ childWorker = await unstable_dev(
16
+ "src/__tests__/helpers/worker-scripts/hello-world-worker.js",
17
+ { config: "src/__tests__/helpers/worker-scripts/child-wrangler.toml" },
18
+ { disableExperimentalWarning: true }
19
+ );
20
+ parentWorker = await unstable_dev(
21
+ "src/__tests__/helpers/worker-scripts/parent-worker.js",
22
+ { config: "src/__tests__/helpers/worker-scripts/parent-wrangler.toml" },
23
+ { disableExperimentalWarning: true }
24
+ );
25
+ });
26
+
27
+ afterAll(async () => {
28
+ await childWorker.stop();
29
+ await parentWorker.stop();
30
+ });
31
+
32
+ it("parentWorker and childWorker should be added devRegistry", async () => {
33
+ const resp = await fetch("http://localhost:6284/workers");
34
+ if (resp) {
35
+ const parsedResp = await resp.json();
36
+ expect(parsedResp.parent).toBeTruthy();
37
+ expect(parsedResp.child).toBeTruthy();
38
+ }
39
+ });
40
+
41
+ it("childWorker should return Hello World itself", async () => {
42
+ const resp = await childWorker.fetch();
43
+ if (resp) {
44
+ const text = await resp.text();
45
+ expect(text).toMatchInlineSnapshot(`"Hello World!"`);
46
+ }
47
+ });
48
+
49
+ it("parentWorker should return Hello World by invoking the child worker", async () => {
50
+ const resp = await parentWorker.fetch();
51
+ if (resp) {
52
+ const parsedResp = await resp.text();
53
+ expect(parsedResp).toEqual("Parent worker sees: Hello World!");
54
+ }
55
+ });
56
+ });
@@ -0,0 +1,80 @@
1
+ import { Request } from "undici";
2
+ import { parseRequestInput } from "../api/dev";
3
+
4
+ describe("parseRequestInput for fetch on unstable dev", () => {
5
+ it("should allow no input to be passed in", () => {
6
+ const [input, _] = parseRequestInput("0.0.0.0", 8080);
7
+
8
+ expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080"`);
9
+ });
10
+
11
+ it("should allow string of pathname to be passed in", () => {
12
+ const [input, _] = parseRequestInput("0.0.0.0", 8080, "/test");
13
+
14
+ expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/test"`);
15
+ });
16
+
17
+ it("should allow full url to be passed in as string and stripped", () => {
18
+ const [input, _] = parseRequestInput(
19
+ "0.0.0.0",
20
+ 8080,
21
+ "http://cloudflare.com/test"
22
+ );
23
+
24
+ expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/test"`);
25
+ });
26
+
27
+ it("should allow URL object without pathname to be passed in and stripped", () => {
28
+ const [input, _] = parseRequestInput(
29
+ "0.0.0.0",
30
+ 8080,
31
+ new URL("http://cloudflare.com")
32
+ );
33
+
34
+ expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/"`);
35
+ });
36
+
37
+ it("should allow URL object with pathname to be passed in and stripped", () => {
38
+ const [input, _] = parseRequestInput(
39
+ "0.0.0.0",
40
+ 8080,
41
+ new URL("http://cloudflare.com/test")
42
+ );
43
+
44
+ expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/test"`);
45
+ });
46
+
47
+ it("should allow request object to be passed in", () => {
48
+ const [input, init] = parseRequestInput(
49
+ "0.0.0.0",
50
+ 8080,
51
+ new Request("http://cloudflare.com/test", { method: "POST" })
52
+ );
53
+
54
+ expect(init).toBeUndefined();
55
+ expect(input).toBeInstanceOf(Request);
56
+ // We don't expect the request to be modified
57
+ expect((input as Request).url).toMatchInlineSnapshot(
58
+ `"http://cloudflare.com/test"`
59
+ );
60
+ expect((input as Request).method).toMatchInlineSnapshot(`"POST"`);
61
+ });
62
+
63
+ it("should parse to give https url with localProtocol = https", () => {
64
+ const [input, _] = parseRequestInput("0.0.0.0", 8080, "/test", {}, "https");
65
+
66
+ expect(input).toMatchInlineSnapshot(`"https://0.0.0.0:8080/test"`);
67
+ });
68
+
69
+ it("should parse to give http url with localProtocol = http", () => {
70
+ const [input, _] = parseRequestInput("0.0.0.0", 8080, "/test", {}, "http");
71
+
72
+ expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/test"`);
73
+ });
74
+
75
+ it("should parse to give http url with localProtocol not set", () => {
76
+ const [input, _] = parseRequestInput("0.0.0.0", 8080, "/test", {});
77
+
78
+ expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/test"`);
79
+ });
80
+ });
@@ -1027,7 +1027,9 @@ describe("wrangler dev", () => {
1027
1027
  --node-compat Enable node.js compatibility [boolean]
1028
1028
  --persist Enable persistence for local mode, using default path: .wrangler/state [boolean]
1029
1029
  --persist-to Specify directory to use for local persistence (implies --persist) [string]
1030
- --inspect Enable dev tools [deprecated] [boolean]",
1030
+ --inspect Enable dev tools [deprecated] [boolean]
1031
+ --test-scheduled Test scheduled events by visiting /__scheduled in browser [boolean] [default: false]
1032
+ --log-level Specify logging level [choices: \\"debug\\", \\"info\\", \\"log\\", \\"warn\\", \\"error\\", \\"none\\"] [default: \\"log\\"]",
1031
1033
  "warn": "",
1032
1034
  }
1033
1035
  `);
@@ -1211,6 +1213,42 @@ describe("wrangler dev", () => {
1211
1213
  });
1212
1214
  });
1213
1215
 
1216
+ describe("--log-level", () => {
1217
+ it("should not output warnings with log-level 'none'", async () => {
1218
+ fs.writeFileSync("index.js", `export default {};`);
1219
+ await runWrangler("dev index.js --inspect --log-level none");
1220
+ expect(std).toMatchInlineSnapshot(`
1221
+ Object {
1222
+ "debug": "",
1223
+ "err": "",
1224
+ "out": "",
1225
+ "warn": "",
1226
+ }
1227
+ `);
1228
+ });
1229
+
1230
+ it("should output warnings with log-level 'warn'", async () => {
1231
+ fs.writeFileSync("index.js", `export default {};`);
1232
+ await runWrangler("dev index.js --inspect --log-level warn");
1233
+ expect(std).toMatchInlineSnapshot(`
1234
+ Object {
1235
+ "debug": "",
1236
+ "err": "",
1237
+ "out": "",
1238
+ "warn": "▲ [WARNING] Passing --inspect is unnecessary, now you can always connect to devtools.
1239
+
1240
+ ",
1241
+ }
1242
+ `);
1243
+ });
1244
+
1245
+ it("should not output Errors with log-level error", async () => {
1246
+ fs.writeFileSync("index.js", `export default {};`);
1247
+ await runWrangler("dev index.js --inspect --log-level debug");
1248
+ expect(std.debug.length > 1).toBe(true);
1249
+ });
1250
+ });
1251
+
1214
1252
  describe("service bindings", () => {
1215
1253
  it("should warn when using service bindings", async () => {
1216
1254
  writeWranglerToml({
@@ -0,0 +1 @@
1
+ name = 'child'
@@ -0,0 +1 @@
1
+ name = 'hello-world'
@@ -0,0 +1,8 @@
1
+ export default {
2
+ async fetch(req, env) {
3
+ const resp = await env.CHILD.fetch(req);
4
+ const text = await resp.text();
5
+ console.log("text: ", text);
6
+ return new Response(`Parent worker sees: ${text}`);
7
+ },
8
+ };
@@ -0,0 +1,5 @@
1
+ name = 'parent'
2
+
3
+ [[services]]
4
+ binding = "CHILD"
5
+ service = 'child'
@@ -982,7 +982,8 @@ describe("init", () => {
982
982
  {
983
983
  text: "Would you like to use TypeScript?",
984
984
  result: false,
985
- }
985
+ },
986
+ { text: "Would you like us to write your first test?", result: false }
986
987
  );
987
988
  mockSelect({
988
989
  text: "Would you like to create a Worker at src/index.js?",
@@ -1562,7 +1563,8 @@ describe("init", () => {
1562
1563
  {
1563
1564
  text: "Would you like to use TypeScript?",
1564
1565
  result: false,
1565
- }
1566
+ },
1567
+ { text: "Would you like us to write your first test?", result: false }
1566
1568
  );
1567
1569
  mockSelect({
1568
1570
  text: "Would you like to create a Worker at src/index.js?",
@@ -1596,6 +1598,127 @@ describe("init", () => {
1596
1598
  To publish your Worker to the Internet, run \`npm run deploy\`"
1597
1599
  `);
1598
1600
  });
1601
+ it("should add a jest test for a non-ts project with .js extension", async () => {
1602
+ mockConfirm(
1603
+ {
1604
+ text: "Would you like to use git to manage this Worker?",
1605
+ result: false,
1606
+ },
1607
+ {
1608
+ text: "No package.json found. Would you like to create one?",
1609
+ result: true,
1610
+ },
1611
+ {
1612
+ text: "Would you like to install wrangler into package.json?",
1613
+ result: false,
1614
+ },
1615
+ {
1616
+ text: "Would you like to use TypeScript?",
1617
+ result: false,
1618
+ },
1619
+ { text: "Would you like us to write your first test?", result: true }
1620
+ );
1621
+ mockSelect(
1622
+ {
1623
+ text: "Would you like to create a Worker at src/index.js?",
1624
+ result: "fetch",
1625
+ },
1626
+ { text: "Which test runner would you like to use?", result: "jest" }
1627
+ );
1628
+
1629
+ await runWrangler("init");
1630
+
1631
+ checkFiles({
1632
+ items: {
1633
+ "src/index.js": true,
1634
+ "src/index.test.js": true,
1635
+ "src/index.ts": false,
1636
+ "package.json": {
1637
+ contents: expect.objectContaining({
1638
+ name: expect.stringContaining("wrangler-tests"),
1639
+ version: "0.0.0",
1640
+ scripts: {
1641
+ start: "wrangler dev",
1642
+ deploy: "wrangler publish",
1643
+ test: "jest",
1644
+ },
1645
+ }),
1646
+ },
1647
+ },
1648
+ });
1649
+ expect(std.out).toMatchInlineSnapshot(`
1650
+ "✨ Created wrangler.toml
1651
+ ✨ Created package.json
1652
+ ✨ Created src/index.js
1653
+ ✨ Created src/index.test.js
1654
+ ✨ Installed jest into devDependencies
1655
+
1656
+ To start developing your Worker, run \`npm start\`
1657
+ To start testing your Worker, run \`npm test\`
1658
+ To publish your Worker to the Internet, run \`npm run deploy\`"
1659
+ `);
1660
+ });
1661
+
1662
+ it("should add a vitest test for a non-ts project with .js extension", async () => {
1663
+ mockConfirm(
1664
+ {
1665
+ text: "Would you like to use git to manage this Worker?",
1666
+ result: false,
1667
+ },
1668
+ {
1669
+ text: "No package.json found. Would you like to create one?",
1670
+ result: true,
1671
+ },
1672
+ {
1673
+ text: "Would you like to install wrangler into package.json?",
1674
+ result: false,
1675
+ },
1676
+ {
1677
+ text: "Would you like to use TypeScript?",
1678
+ result: false,
1679
+ },
1680
+ { text: "Would you like us to write your first test?", result: true }
1681
+ );
1682
+ mockSelect(
1683
+ {
1684
+ text: "Would you like to create a Worker at src/index.js?",
1685
+ result: "fetch",
1686
+ },
1687
+ { text: "Which test runner would you like to use?", result: "vitest" }
1688
+ );
1689
+
1690
+ await runWrangler("init");
1691
+
1692
+ checkFiles({
1693
+ items: {
1694
+ "src/index.js": true,
1695
+ "src/index.test.js": true,
1696
+ "src/index.ts": false,
1697
+ "package.json": {
1698
+ contents: expect.objectContaining({
1699
+ name: expect.stringContaining("wrangler-tests"),
1700
+ version: "0.0.0",
1701
+ scripts: {
1702
+ start: "wrangler dev",
1703
+ deploy: "wrangler publish",
1704
+ test: "vitest",
1705
+ },
1706
+ }),
1707
+ },
1708
+ },
1709
+ });
1710
+ expect(std.out).toMatchInlineSnapshot(`
1711
+ "✨ Created wrangler.toml
1712
+ ✨ Created package.json
1713
+ ✨ Created src/index.js
1714
+ ✨ Created src/index.test.js
1715
+ ✨ Installed vitest into devDependencies
1716
+
1717
+ To start developing your Worker, run \`npm start\`
1718
+ To start testing your Worker, run \`npm test\`
1719
+ To publish your Worker to the Internet, run \`npm run deploy\`"
1720
+ `);
1721
+ });
1599
1722
 
1600
1723
  it("should not overwrite package.json scripts for a non-ts project with .js extension", async () => {
1601
1724
  mockConfirm(
@@ -1610,7 +1733,8 @@ describe("init", () => {
1610
1733
  {
1611
1734
  text: "Would you like to use TypeScript?",
1612
1735
  result: false,
1613
- }
1736
+ },
1737
+ { text: "Would you like us to write your first test?", result: false }
1614
1738
  );
1615
1739
  mockSelect({
1616
1740
  text: "Would you like to create a Worker at src/index.js?",