wrangler 2.12.3 → 2.14.0

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 (49) hide show
  1. package/package.json +11 -7
  2. package/src/__tests__/api-devregistry.test.ts +121 -0
  3. package/src/__tests__/api.test.ts +27 -5
  4. package/src/__tests__/configuration.test.ts +59 -12
  5. package/src/__tests__/deployments.test.ts +335 -95
  6. package/src/__tests__/helpers/msw/handlers/deployments.ts +70 -3
  7. package/src/__tests__/helpers/msw/index.ts +4 -2
  8. package/src/__tests__/index.test.ts +10 -4
  9. package/src/__tests__/jest.setup.ts +4 -0
  10. package/src/__tests__/mtls-certificates.test.ts +5 -2
  11. package/src/__tests__/pages/publish.test.ts +67 -23
  12. package/src/__tests__/publish.test.ts +138 -59
  13. package/src/__tests__/queues.test.ts +5 -2
  14. package/src/__tests__/traverse-module-graph.test.ts +220 -0
  15. package/src/api/dev.ts +7 -18
  16. package/src/api/pages/create-worker-bundle-contents.ts +1 -0
  17. package/src/bundle.ts +19 -5
  18. package/src/config/environment.ts +27 -0
  19. package/src/config/index.ts +18 -0
  20. package/src/config/validation.ts +91 -1
  21. package/src/create-worker-upload-form.ts +18 -1
  22. package/src/d1/execute.tsx +1 -1
  23. package/src/d1/migrations/apply.tsx +2 -1
  24. package/src/deployments.ts +260 -8
  25. package/src/dev/start-server.ts +2 -8
  26. package/src/dev/use-esbuild.ts +2 -8
  27. package/src/dev-registry.ts +2 -1
  28. package/src/dev.tsx +1 -0
  29. package/src/entry.ts +18 -8
  30. package/src/index.ts +75 -22
  31. package/src/init.ts +144 -135
  32. package/src/metrics/send-event.ts +2 -1
  33. package/src/module-collection.ts +91 -25
  34. package/src/pages/functions/buildPlugin.ts +1 -0
  35. package/src/pages/functions/buildWorker.ts +2 -8
  36. package/src/publish/publish.ts +10 -26
  37. package/src/queues/cli/commands/consumer/add.ts +6 -0
  38. package/src/queues/client.ts +1 -0
  39. package/src/secret/index.ts +1 -0
  40. package/src/traverse-module-graph.ts +53 -0
  41. package/src/worker.ts +10 -0
  42. package/wrangler-dist/cli.d.ts +24 -0
  43. package/wrangler-dist/cli.js +19083 -18680
  44. package/src/__tests__/api-devregistry.test.js +0 -64
  45. package/src/__tests__/tsconfig.tsbuildinfo +0 -1
  46. package/src/miniflare-cli/tsconfig.tsbuildinfo +0 -1
  47. package/src/pages/functions/tsconfig.tsbuildinfo +0 -1
  48. package/templates/__tests__/tsconfig.tsbuildinfo +0 -1
  49. package/templates/tsconfig.tsbuildinfo +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wrangler",
3
- "version": "2.12.3",
3
+ "version": "2.14.0",
4
4
  "description": "Command-line interface for all things Cloudflare Workers",
5
5
  "keywords": [
6
6
  "wrangler",
@@ -66,7 +66,8 @@
66
66
  "test": "npm run assert-git-version && jest",
67
67
  "test-watch": "npm run test -- --runInBand --testTimeout=50000 --watch",
68
68
  "test:ci": "npm run test -- --coverage",
69
- "test:debug": "npm run test -- --silent=false --verbose=true"
69
+ "test:debug": "npm run test -- --silent=false --verbose=true",
70
+ "test:e2e": "vitest --no-threads ./e2e"
70
71
  },
71
72
  "jest": {
72
73
  "coverageReporters": [
@@ -83,7 +84,7 @@
83
84
  "setupFilesAfterEnv": [
84
85
  "<rootDir>/src/__tests__/jest.setup.ts"
85
86
  ],
86
- "testRegex": ".*.(test|spec)\\.[jt]sx?$",
87
+ "testRegex": "src/__tests__/.*\\.(test|spec)\\.[jt]sx?$",
87
88
  "testTimeout": 30000,
88
89
  "transform": {
89
90
  "^.+\\.c?(t|j)sx?$": [
@@ -101,13 +102,13 @@
101
102
  "@cloudflare/kv-asset-handler": "^0.2.0",
102
103
  "@esbuild-plugins/node-globals-polyfill": "^0.1.1",
103
104
  "@esbuild-plugins/node-modules-polyfill": "^0.1.4",
104
- "@miniflare/core": "2.12.1",
105
- "@miniflare/d1": "2.12.1",
106
- "@miniflare/durable-objects": "2.12.1",
105
+ "@miniflare/core": "2.13.0",
106
+ "@miniflare/d1": "2.13.0",
107
+ "@miniflare/durable-objects": "2.13.0",
107
108
  "blake3-wasm": "^2.1.5",
108
109
  "chokidar": "^3.5.3",
109
110
  "esbuild": "0.16.3",
110
- "miniflare": "2.12.1",
111
+ "miniflare": "2.13.0",
111
112
  "nanoid": "^3.3.3",
112
113
  "path-to-regexp": "^6.2.0",
113
114
  "selfsigned": "^2.0.1",
@@ -175,13 +176,16 @@
175
176
  "remove-accents-esm": "^0.0.1",
176
177
  "semiver": "^1.1.0",
177
178
  "serve-static": "^1.15.0",
179
+ "shellac": "^0.7.3",
178
180
  "signal-exit": "^3.0.7",
181
+ "strip-ansi": "^7.0.1",
179
182
  "supports-color": "^9.2.2",
180
183
  "timeago.js": "^4.0.2",
181
184
  "tmp-promise": "^3.0.3",
182
185
  "ts-dedent": "^2.2.0",
183
186
  "undici": "5.20.0",
184
187
  "update-check": "^1.5.4",
188
+ "vitest": "^0.29.2",
185
189
  "ws": "^8.5.0",
186
190
  "xdg-app-paths": "^7.3.0",
187
191
  "yargs": "^17.4.1",
@@ -0,0 +1,121 @@
1
+ import { fetch } from "undici";
2
+ import { unstable_dev } from "../api";
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
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
+ let childWorker: any;
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ let parentWorker: any;
15
+
16
+ beforeAll(async () => {
17
+ childWorker = await unstable_dev(
18
+ "src/__tests__/helpers/worker-scripts/hello-world-worker.js",
19
+ {
20
+ config: "src/__tests__/helpers/worker-scripts/child-wrangler.toml",
21
+ experimental: {
22
+ disableExperimentalWarning: true,
23
+ },
24
+ }
25
+ );
26
+ parentWorker = await unstable_dev(
27
+ "src/__tests__/helpers/worker-scripts/parent-worker.js",
28
+ {
29
+ config: "src/__tests__/helpers/worker-scripts/parent-wrangler.toml",
30
+ experimental: {
31
+ disableExperimentalWarning: true,
32
+ },
33
+ }
34
+ );
35
+ });
36
+
37
+ afterAll(async () => {
38
+ await childWorker.stop();
39
+ await parentWorker.stop();
40
+ });
41
+
42
+ it("parentWorker and childWorker should be added devRegistry", async () => {
43
+ const resp = await fetch("http://localhost:6284/workers");
44
+ if (resp) {
45
+ const parsedResp = (await resp.json()) as {
46
+ parent: unknown;
47
+ child: unknown;
48
+ };
49
+ expect(parsedResp.parent).toBeTruthy();
50
+ expect(parsedResp.child).toBeTruthy();
51
+ }
52
+ });
53
+
54
+ it("childWorker should return Hello World itself", async () => {
55
+ const resp = await childWorker.fetch();
56
+ if (resp) {
57
+ const text = await resp.text();
58
+ expect(text).toMatchInlineSnapshot(`"Hello World!"`);
59
+ }
60
+ });
61
+
62
+ it("parentWorker should return Hello World by invoking the child worker", async () => {
63
+ const resp = await parentWorker.fetch();
64
+ if (resp) {
65
+ const parsedResp = await resp.text();
66
+ expect(parsedResp).toEqual("Parent worker sees: Hello World!");
67
+ }
68
+ });
69
+
70
+ it("should be able to stop and start the server with no warning logs", async () => {
71
+ // Spy on all the console methods
72
+ let logs = "";
73
+ (["debug", "info", "log", "warn", "error"] as const).forEach((method) =>
74
+ jest
75
+ .spyOn(console, method)
76
+ .mockImplementation((...args: unknown[]) => (logs += `\n${args}`))
77
+ );
78
+
79
+ // Spy on the std out that is written to by Miniflare 2
80
+ jest
81
+ .spyOn(process.stdout, "write")
82
+ .mockImplementation((chunk: unknown) => ((logs += `\n${chunk}`), true));
83
+
84
+ async function startWorker() {
85
+ return await unstable_dev(
86
+ "src/__tests__/helpers/worker-scripts/hello-world-worker.js",
87
+ {
88
+ // We need the wrangler.toml config to specify a Worker name
89
+ // otherwise unstable_dev will not register the worker with the DevRegistry
90
+ config: "src/__tests__/helpers/worker-scripts/child-wrangler.toml",
91
+ // We need debug logs because this is where the message is written if registering the worker fails.
92
+ logLevel: "debug",
93
+ experimental: {
94
+ disableExperimentalWarning: true,
95
+ },
96
+ }
97
+ );
98
+ }
99
+
100
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
101
+ let worker: any;
102
+ try {
103
+ worker = await startWorker();
104
+
105
+ // Stop the worker and start it again
106
+ await worker.stop();
107
+ await new Promise((r) => setTimeout(r, 2000));
108
+
109
+ worker = await startWorker();
110
+
111
+ const resp = await worker.fetch();
112
+ expect(resp).not.toBe(undefined);
113
+
114
+ expect(logs).not.toMatch(
115
+ /Failed to register worker in local service registry/
116
+ );
117
+ } finally {
118
+ await worker?.stop();
119
+ }
120
+ }, 10000);
121
+ });
@@ -5,7 +5,7 @@ describe("parseRequestInput for fetch on unstable dev", () => {
5
5
  it("should allow no input to be passed in", () => {
6
6
  const [input, _] = parseRequestInput("0.0.0.0", 8080);
7
7
 
8
- expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080"`);
8
+ expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/"`);
9
9
  });
10
10
 
11
11
  it("should allow string of pathname to be passed in", () => {
@@ -14,14 +14,24 @@ describe("parseRequestInput for fetch on unstable dev", () => {
14
14
  expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/test"`);
15
15
  });
16
16
 
17
+ it("should allow string of pathname and querystring to be passed in", () => {
18
+ const [input, _] = parseRequestInput("0.0.0.0", 8080, "/test?q=testparam");
19
+
20
+ expect(input).toMatchInlineSnapshot(
21
+ `"http://0.0.0.0:8080/test?q=testparam"`
22
+ );
23
+ });
24
+
17
25
  it("should allow full url to be passed in as string and stripped", () => {
18
26
  const [input, _] = parseRequestInput(
19
27
  "0.0.0.0",
20
28
  8080,
21
- "http://cloudflare.com/test"
29
+ "http://cloudflare.com/test?q=testparam"
22
30
  );
23
31
 
24
- expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/test"`);
32
+ expect(input).toMatchInlineSnapshot(
33
+ `"http://0.0.0.0:8080/test?q=testparam"`
34
+ );
25
35
  });
26
36
 
27
37
  it("should allow URL object without pathname to be passed in and stripped", () => {
@@ -44,18 +54,30 @@ describe("parseRequestInput for fetch on unstable dev", () => {
44
54
  expect(input).toMatchInlineSnapshot(`"http://0.0.0.0:8080/test"`);
45
55
  });
46
56
 
57
+ it("should allow URL object with pathname and querystring to be passed in and stripped", () => {
58
+ const [input, _] = parseRequestInput(
59
+ "0.0.0.0",
60
+ 8080,
61
+ new URL("http://cloudflare.com/test?q=testparam")
62
+ );
63
+
64
+ expect(input).toMatchInlineSnapshot(
65
+ `"http://0.0.0.0:8080/test?q=testparam"`
66
+ );
67
+ });
68
+
47
69
  it("should allow request object to be passed in", () => {
48
70
  const [input, init] = parseRequestInput(
49
71
  "0.0.0.0",
50
72
  8080,
51
- new Request("http://cloudflare.com/test", { method: "POST" })
73
+ new Request("http://cloudflare.com/test?q=testparam", { method: "POST" })
52
74
  );
53
75
 
54
76
  expect(init).toBeUndefined();
55
77
  expect(input).toBeInstanceOf(Request);
56
78
  // We don't expect the request to be modified
57
79
  expect((input as Request).url).toMatchInlineSnapshot(
58
- `"http://cloudflare.com/test"`
80
+ `"http://cloudflare.com/test?q=testparam"`
59
81
  );
60
82
  expect((input as Request).method).toMatchInlineSnapshot(`"POST"`);
61
83
  });
@@ -40,6 +40,7 @@ describe("normalizeAndValidateConfig()", () => {
40
40
  jsx_fragment: "React.Fragment",
41
41
  tsconfig: undefined,
42
42
  kv_namespaces: [],
43
+ send_email: [],
43
44
  legacy_env: true,
44
45
  logfwdr: {
45
46
  bindings: [],
@@ -903,6 +904,17 @@ describe("normalizeAndValidateConfig()", () => {
903
904
  preview_id: "KV_PREVIEW_1",
904
905
  },
905
906
  ],
907
+ send_email: [
908
+ { name: "SEB_TARGET", destination_address: "teste@example.com" },
909
+ { name: "SEB_UNRESTRICTED" },
910
+ {
911
+ name: "SEB_ALLOWLIST",
912
+ allowed_destination_addresses: [
913
+ "email1@example.com",
914
+ "email2@example.com",
915
+ ],
916
+ },
917
+ ],
906
918
  r2_buckets: [
907
919
  { binding: "R2_BINDING_1", bucket_name: "R2_BUCKET_1" },
908
920
  {
@@ -1629,6 +1641,39 @@ describe("normalizeAndValidateConfig()", () => {
1629
1641
  });
1630
1642
  });
1631
1643
 
1644
+ it("should error if send_email.bindings are not valid", () => {
1645
+ const { diagnostics } = normalizeAndValidateConfig(
1646
+ {
1647
+ send_email: [
1648
+ {},
1649
+ { binding: "VALID" },
1650
+ { name: "SEB", destination_address: 123 },
1651
+ {
1652
+ name: "SEB2",
1653
+ allowed_destination_addresses: 123,
1654
+ },
1655
+ {
1656
+ name: "SEB3",
1657
+ destination_address: "email@example.com",
1658
+ allowed_destination_addresses: ["email@example.com"],
1659
+ },
1660
+ ],
1661
+ } as unknown as RawConfig,
1662
+ undefined,
1663
+ { env: undefined }
1664
+ );
1665
+
1666
+ expect(diagnostics.hasWarnings()).toBe(false);
1667
+ expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1668
+ "Processing wrangler configuration:
1669
+ - \\"send_email[0]\\" bindings should have a string \\"name\\" field but got {}.
1670
+ - \\"send_email[1]\\" bindings should have a string \\"name\\" field but got {\\"binding\\":\\"VALID\\"}.
1671
+ - \\"send_email[2]\\" bindings should, optionally, have a string \\"destination_address\\" field but got {\\"name\\":\\"SEB\\",\\"destination_address\\":123}.
1672
+ - \\"send_email[3]\\" bindings should, optionally, have a []string \\"allowed_destination_addresses\\" field but got {\\"name\\":\\"SEB2\\",\\"allowed_destination_addresses\\":123}.
1673
+ - \\"send_email[4]\\" bindings should have either a \\"destination_address\\" or \\"allowed_destination_addresses\\" field, but not both."
1674
+ `);
1675
+ });
1676
+
1632
1677
  describe("[d1_databases]", () => {
1633
1678
  it("should error if d1_databases is an object", () => {
1634
1679
  const { diagnostics } = normalizeAndValidateConfig(
@@ -1791,6 +1836,7 @@ describe("normalizeAndValidateConfig()", () => {
1791
1836
  max_batch_timeout: null,
1792
1837
  max_retries: "hello",
1793
1838
  dead_letter_queue: 5,
1839
+ max_concurrency: "hello",
1794
1840
  },
1795
1841
  ],
1796
1842
  },
@@ -1806,20 +1852,21 @@ describe("normalizeAndValidateConfig()", () => {
1806
1852
  );
1807
1853
  expect(diagnostics.hasWarnings()).toBe(true);
1808
1854
  expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
1809
- "Processing wrangler configuration:
1810
- - Unexpected fields found in queues field: \\"invalidField\\"
1811
- - Unexpected fields found in queues.consumers[2] field: \\"invalidField\\""
1812
- `);
1855
+ "Processing wrangler configuration:
1856
+ - Unexpected fields found in queues field: \\"invalidField\\"
1857
+ - Unexpected fields found in queues.consumers[2] field: \\"invalidField\\""
1858
+ `);
1813
1859
 
1814
1860
  expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
1815
- "Processing wrangler configuration:
1816
- - \\"queues.consumers[0]\\" should have a string \\"queue\\" field but got {}.
1817
- - \\"queues.consumers[1]\\" should have a string \\"queue\\" field but got {\\"queue\\":22}.
1818
- - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_batch_size\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5}.
1819
- - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_batch_timeout\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5}.
1820
- - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_retries\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5}.
1821
- - \\"queues.consumers[3]\\" should, optionally, have a string \\"dead_letter_queue\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5}."
1822
- `);
1861
+ "Processing wrangler configuration:
1862
+ - \\"queues.consumers[0]\\" should have a string \\"queue\\" field but got {}.
1863
+ - \\"queues.consumers[1]\\" should have a string \\"queue\\" field but got {\\"queue\\":22}.
1864
+ - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_batch_size\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5,\\"max_concurrency\\":\\"hello\\"}.
1865
+ - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_batch_timeout\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5,\\"max_concurrency\\":\\"hello\\"}.
1866
+ - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_retries\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5,\\"max_concurrency\\":\\"hello\\"}.
1867
+ - \\"queues.consumers[3]\\" should, optionally, have a string \\"dead_letter_queue\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5,\\"max_concurrency\\":\\"hello\\"}.
1868
+ - \\"queues.consumers[3]\\" should, optionally, have a number \\"max_concurrency\\" field but got {\\"queue\\":\\"myQueue\\",\\"max_batch_size\\":\\"3\\",\\"max_batch_timeout\\":null,\\"max_retries\\":\\"hello\\",\\"dead_letter_queue\\":5,\\"max_concurrency\\":\\"hello\\"}."
1869
+ `);
1823
1870
  });
1824
1871
  });
1825
1872