wrangler 2.0.27 → 2.1.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 (65) hide show
  1. package/bin/wrangler.js +1 -1
  2. package/miniflare-dist/index.mjs +1141 -369
  3. package/package.json +6 -4
  4. package/src/__tests__/api-dev.test.ts +19 -0
  5. package/src/__tests__/configuration.test.ts +27 -27
  6. package/src/__tests__/dev.test.tsx +8 -6
  7. package/src/__tests__/helpers/hello-world-worker.js +5 -0
  8. package/src/__tests__/helpers/mock-cfetch.ts +4 -4
  9. package/src/__tests__/helpers/mock-console.ts +11 -2
  10. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  11. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  12. package/src/__tests__/index.test.ts +37 -37
  13. package/src/__tests__/init.test.ts +356 -5
  14. package/src/__tests__/jest.setup.ts +13 -0
  15. package/src/__tests__/middleware.test.ts +768 -0
  16. package/src/__tests__/pages.test.ts +829 -104
  17. package/src/__tests__/paths.test.ts +17 -0
  18. package/src/__tests__/publish.test.ts +512 -445
  19. package/src/__tests__/tail.test.ts +79 -72
  20. package/src/__tests__/test-old-node-version.js +3 -3
  21. package/src/__tests__/worker-namespace.test.ts +37 -35
  22. package/src/api/dev.ts +93 -28
  23. package/src/bundle.ts +239 -12
  24. package/src/cfetch/internal.ts +64 -3
  25. package/src/cli.ts +1 -1
  26. package/src/config/environment.ts +1 -1
  27. package/src/config/index.ts +4 -4
  28. package/src/config/validation.ts +3 -3
  29. package/src/create-worker-upload-form.ts +29 -26
  30. package/src/dev/dev.tsx +3 -1
  31. package/src/dev/local.tsx +319 -171
  32. package/src/dev/remote.tsx +16 -4
  33. package/src/dev/start-server.ts +416 -0
  34. package/src/dev/use-esbuild.ts +4 -0
  35. package/src/dev.tsx +340 -166
  36. package/src/dialogs.tsx +12 -0
  37. package/src/{worker-namespace.ts → dispatch-namespace.ts} +18 -18
  38. package/src/entry.ts +2 -1
  39. package/src/index.tsx +59 -12
  40. package/src/init.ts +291 -16
  41. package/src/metrics/send-event.ts +6 -5
  42. package/src/miniflare-cli/assets.ts +130 -476
  43. package/src/miniflare-cli/index.ts +39 -33
  44. package/src/pages/constants.ts +3 -0
  45. package/src/pages/dev.tsx +8 -3
  46. package/src/pages/functions/buildPlugin.ts +2 -1
  47. package/src/pages/functions/buildWorker.ts +2 -1
  48. package/src/pages/functions/routes-transformation.test.ts +12 -1
  49. package/src/pages/functions/routes-transformation.ts +7 -1
  50. package/src/pages/hash.tsx +13 -0
  51. package/src/pages/publish.tsx +82 -38
  52. package/src/pages/upload.tsx +3 -18
  53. package/src/paths.ts +20 -1
  54. package/src/publish.ts +49 -8
  55. package/src/tail/filters.ts +1 -5
  56. package/src/tail/index.ts +6 -3
  57. package/src/worker.ts +10 -9
  58. package/src/zones.ts +91 -0
  59. package/templates/middleware/common.ts +62 -0
  60. package/templates/middleware/loader-modules.ts +84 -0
  61. package/templates/middleware/loader-sw.ts +213 -0
  62. package/templates/middleware/middleware-pretty-error.ts +40 -0
  63. package/templates/middleware/middleware-scheduled.ts +14 -0
  64. package/wrangler-dist/cli.d.ts +22 -8
  65. package/wrangler-dist/cli.js +71020 -65212
@@ -3,7 +3,9 @@ import { Headers, Request } from "undici";
3
3
  import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
4
4
  import { setMockResponse, unsetAllMocks } from "./helpers/mock-cfetch";
5
5
  import { mockConsoleMethods } from "./helpers/mock-console";
6
+ import { mockGetZoneFromHostRequest } from "./helpers/mock-get-zone-from-host";
6
7
  import { useMockIsTTY } from "./helpers/mock-istty";
8
+ import { mockCollectKnownRoutesRequest } from "./helpers/mock-known-routes";
7
9
  import { runInTempDir } from "./helpers/run-in-tmp";
8
10
  import { runWrangler } from "./helpers/run-wrangler";
9
11
  import type {
@@ -12,6 +14,7 @@ import type {
12
14
  ScheduledEvent,
13
15
  AlarmEvent,
14
16
  } from "../tail";
17
+ import type { RequestInit } from "undici";
15
18
  import type WebSocket from "ws";
16
19
 
17
20
  describe("tail", () => {
@@ -42,26 +45,58 @@ describe("tail", () => {
42
45
 
43
46
  it("creates and then delete tails", async () => {
44
47
  const api = mockWebsocketAPIs();
45
- expect(api.requests.creation.count).toStrictEqual(0);
48
+ expect(api.requests.creation.length).toStrictEqual(0);
46
49
 
47
50
  await runWrangler("tail test-worker");
48
51
 
49
52
  await expect(api.ws.connected).resolves.toBeTruthy();
50
- expect(api.requests.creation.count).toStrictEqual(1);
53
+ expect(api.requests.creation.length).toStrictEqual(1);
54
+ expect(api.requests.deletion.count).toStrictEqual(0);
55
+
56
+ api.ws.close();
57
+ expect(api.requests.deletion.count).toStrictEqual(1);
58
+ });
59
+ it("should connect to the worker assigned to a given route", async () => {
60
+ const api = mockWebsocketAPIs();
61
+ expect(api.requests.creation.length).toStrictEqual(0);
62
+
63
+ mockGetZoneFromHostRequest("example.com", "test-zone");
64
+ mockCollectKnownRoutesRequest([
65
+ {
66
+ pattern: "example.com/*",
67
+ script: "test-worker",
68
+ },
69
+ ]);
70
+ await runWrangler("tail example.com/*");
71
+
72
+ await expect(api.ws.connected).resolves.toBeTruthy();
73
+ expect(api.requests.creation.length).toStrictEqual(1);
51
74
  expect(api.requests.deletion.count).toStrictEqual(0);
52
75
 
53
76
  api.ws.close();
54
77
  expect(api.requests.deletion.count).toStrictEqual(1);
55
78
  });
56
79
 
80
+ it("should error if a given route is not assigned to the user's zone", async () => {
81
+ mockGetZoneFromHostRequest("example.com", "test-zone");
82
+ mockCollectKnownRoutesRequest([]);
83
+
84
+ await expect(runWrangler("tail example.com/*")).rejects.toThrow();
85
+ });
86
+ it("should error if a given route is not within the user's zone", async () => {
87
+ mockGetZoneFromHostRequest("example.com");
88
+
89
+ await expect(runWrangler("tail example.com/*")).rejects.toThrow();
90
+ });
91
+
57
92
  it("creates and then delete tails: legacy envs", async () => {
58
93
  const api = mockWebsocketAPIs("some-env", true);
59
- expect(api.requests.creation.count).toStrictEqual(0);
94
+ expect(api.requests.creation.length).toStrictEqual(0);
60
95
 
61
96
  await runWrangler("tail test-worker --env some-env --legacy-env true");
62
97
 
63
98
  await expect(api.ws.connected).resolves.toBeTruthy();
64
- expect(api.requests.creation.count).toStrictEqual(1);
99
+ expect(api.requests.creation.length).toStrictEqual(1);
65
100
  expect(api.requests.deletion.count).toStrictEqual(0);
66
101
 
67
102
  api.ws.close();
@@ -70,12 +105,12 @@ describe("tail", () => {
70
105
 
71
106
  it("creates and then delete tails: service envs", async () => {
72
107
  const api = mockWebsocketAPIs("some-env");
73
- expect(api.requests.creation.count).toStrictEqual(0);
108
+ expect(api.requests.creation.length).toStrictEqual(0);
74
109
 
75
110
  await runWrangler("tail test-worker --env some-env --legacy-env false");
76
111
 
77
112
  await expect(api.ws.connected).resolves.toBeTruthy();
78
- expect(api.requests.creation.count).toStrictEqual(1);
113
+ expect(api.requests.creation.length).toStrictEqual(1);
79
114
  expect(api.requests.deletion.count).toStrictEqual(0);
80
115
 
81
116
  api.ws.close();
@@ -109,65 +144,57 @@ describe("tail", () => {
109
144
  await expect(tooLow).rejects.toThrow();
110
145
 
111
146
  await runWrangler("tail test-worker --sampling-rate 0.25");
112
- await expect(api.nextMessageJson()).resolves.toHaveProperty("filters", [
113
- { sampling_rate: 0.25 },
114
- ]);
147
+ expect(api.requests.creation[0].body).toEqual(
148
+ `{"filters":[{"sampling_rate":0.25}]}`
149
+ );
115
150
  });
116
151
 
117
152
  it("sends single status filters", async () => {
118
153
  const api = mockWebsocketAPIs();
119
154
  await runWrangler("tail test-worker --status error");
120
- await expect(api.nextMessageJson()).resolves.toHaveProperty("filters", [
121
- { outcome: ["exception", "exceededCpu", "exceededMemory", "unknown"] },
122
- ]);
155
+ expect(api.requests.creation[0].body).toEqual(
156
+ `{"filters":[{"outcome":["exception","exceededCpu","exceededMemory","unknown"]}]}`
157
+ );
123
158
  });
124
159
 
125
160
  it("sends multiple status filters", async () => {
126
161
  const api = mockWebsocketAPIs();
127
162
  await runWrangler("tail test-worker --status error --status canceled");
128
- await expect(api.nextMessageJson()).resolves.toHaveProperty("filters", [
129
- {
130
- outcome: [
131
- "exception",
132
- "exceededCpu",
133
- "exceededMemory",
134
- "unknown",
135
- "canceled",
136
- ],
137
- },
138
- ]);
163
+ expect(api.requests.creation[0].body).toEqual(
164
+ `{"filters":[{"outcome":["exception","exceededCpu","exceededMemory","unknown","canceled"]}]}`
165
+ );
139
166
  });
140
167
 
141
168
  it("sends single HTTP method filters", async () => {
142
169
  const api = mockWebsocketAPIs();
143
170
  await runWrangler("tail test-worker --method POST");
144
- await expect(api.nextMessageJson()).resolves.toHaveProperty("filters", [
145
- { method: ["POST"] },
146
- ]);
171
+ expect(api.requests.creation[0].body).toEqual(
172
+ `{"filters":[{"method":["POST"]}]}`
173
+ );
147
174
  });
148
175
 
149
176
  it("sends multiple HTTP method filters", async () => {
150
177
  const api = mockWebsocketAPIs();
151
178
  await runWrangler("tail test-worker --method POST --method GET");
152
- await expect(api.nextMessageJson()).resolves.toHaveProperty("filters", [
153
- { method: ["POST", "GET"] },
154
- ]);
179
+ expect(api.requests.creation[0].body).toEqual(
180
+ `{"filters":[{"method":["POST","GET"]}]}`
181
+ );
155
182
  });
156
183
 
157
184
  it("sends header filters without a query", async () => {
158
185
  const api = mockWebsocketAPIs();
159
186
  await runWrangler("tail test-worker --header X-CUSTOM-HEADER");
160
- await expect(api.nextMessageJson()).resolves.toHaveProperty("filters", [
161
- { header: { key: "X-CUSTOM-HEADER" } },
162
- ]);
187
+ expect(api.requests.creation[0].body).toEqual(
188
+ `{"filters":[{"header":{"key":"X-CUSTOM-HEADER"}}]}`
189
+ );
163
190
  });
164
191
 
165
192
  it("sends header filters with a query", async () => {
166
193
  const api = mockWebsocketAPIs();
167
194
  await runWrangler("tail test-worker --header X-CUSTOM-HEADER:some-value");
168
- await expect(api.nextMessageJson()).resolves.toHaveProperty("filters", [
169
- { header: { key: "X-CUSTOM-HEADER", query: "some-value" } },
170
- ]);
195
+ expect(api.requests.creation[0].body).toEqual(
196
+ `{"filters":[{"header":{"key":"X-CUSTOM-HEADER","query":"some-value"}}]}`
197
+ );
171
198
  });
172
199
 
173
200
  it("sends single IP filters", async () => {
@@ -175,9 +202,9 @@ describe("tail", () => {
175
202
  const fakeIp = "192.0.2.1";
176
203
 
177
204
  await runWrangler(`tail test-worker --ip ${fakeIp}`);
178
- await expect(api.nextMessageJson()).resolves.toHaveProperty("filters", [
179
- { client_ip: [fakeIp] },
180
- ]);
205
+ expect(api.requests.creation[0].body).toEqual(
206
+ `{"filters":[{"client_ip":["${fakeIp}"]}]}`
207
+ );
181
208
  });
182
209
 
183
210
  it("sends multiple IP filters", async () => {
@@ -185,9 +212,9 @@ describe("tail", () => {
185
212
  const fakeIp = "192.0.2.1";
186
213
 
187
214
  await runWrangler(`tail test-worker --ip ${fakeIp} --ip self`);
188
- await expect(api.nextMessageJson()).resolves.toHaveProperty("filters", [
189
- { client_ip: [fakeIp, "self"] },
190
- ]);
215
+ expect(api.requests.creation[0].body).toEqual(
216
+ `{"filters":[{"client_ip":["${fakeIp}","self"]}]}`
217
+ );
191
218
  });
192
219
 
193
220
  it("sends search filters", async () => {
@@ -195,9 +222,9 @@ describe("tail", () => {
195
222
  const search = "filterMe";
196
223
 
197
224
  await runWrangler(`tail test-worker --search ${search}`);
198
- await expect(api.nextMessageJson()).resolves.toHaveProperty("filters", [
199
- { query: search },
200
- ]);
225
+ expect(api.requests.creation[0].body).toEqual(
226
+ `{"filters":[{"query":"${search}"}]}`
227
+ );
201
228
  });
202
229
 
203
230
  it("sends everything but the kitchen sink", async () => {
@@ -218,30 +245,10 @@ describe("tail", () => {
218
245
  `--search ${query} ` +
219
246
  `--debug`;
220
247
 
221
- const expectedWebsocketMessage = {
222
- filters: [
223
- { sampling_rate },
224
- {
225
- outcome: [
226
- "ok",
227
- "exception",
228
- "exceededCpu",
229
- "exceededMemory",
230
- "unknown",
231
- ],
232
- },
233
- { method },
234
- { header: { key: "X-HELLO", query: "world" } },
235
- { client_ip },
236
- { query },
237
- ],
238
- debug: true,
239
- };
248
+ const expectedWebsocketMessage = `{"filters":[{"sampling_rate":0.69},{"outcome":["ok","exception","exceededCpu","exceededMemory","unknown"]},{"method":["GET","POST","PUT"]},{"header":{"key":"X-HELLO","query":"world"}},{"client_ip":["192.0.2.1","self"]},{"query":"onlyTheseMessagesPlease"}]}`;
240
249
 
241
250
  await runWrangler(`tail test-worker ${cliFilters}`);
242
- await expect(api.nextMessageJson()).resolves.toEqual(
243
- expectedWebsocketMessage
244
- );
251
+ expect(api.requests.creation[0].body).toEqual(expectedWebsocketMessage);
245
252
  });
246
253
  });
247
254
 
@@ -555,7 +562,7 @@ function deserializeToJson(message: WebSocket.RawData): string {
555
562
  */
556
563
  type MockAPI = {
557
564
  requests: {
558
- creation: RequestCounter;
565
+ creation: RequestInit[];
559
566
  deletion: RequestCounter;
560
567
  };
561
568
  ws: MockWebSocket;
@@ -581,15 +588,15 @@ function mockCreateTailRequest(
581
588
  websocketURL: string,
582
589
  env?: string,
583
590
  legacyEnv = false
584
- ): RequestCounter {
585
- const requests = { count: 0 };
591
+ ): RequestInit[] {
592
+ const requests: RequestInit[] = [];
586
593
  const servicesOrScripts = env && !legacyEnv ? "services" : "scripts";
587
594
  const environment = env && !legacyEnv ? "/environments/:envName" : "";
588
595
  setMockResponse(
589
596
  `/accounts/:accountId/workers/${servicesOrScripts}/:scriptName${environment}/tails`,
590
597
  "POST",
591
- ([_url, accountId, scriptName, envName]) => {
592
- requests.count++;
598
+ ([_url, accountId, scriptName, envName], req) => {
599
+ requests.push(req);
593
600
  expect(accountId).toEqual("some-account-id");
594
601
  expect(scriptName).toEqual(
595
602
  legacyEnv && env ? `test-worker-${env}` : "test-worker"
@@ -676,7 +683,7 @@ function mockWebsocketAPIs(env?: string, legacyEnv = false): MockAPI {
676
683
  const api: MockAPI = {
677
684
  requests: {
678
685
  deletion: { count: 0 },
679
- creation: { count: 0 },
686
+ creation: [],
680
687
  },
681
688
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
682
689
  ws: null!, // will be set in the `beforeEach()` below.
@@ -1,4 +1,4 @@
1
- // this test has to be run with a version of node.js older than 16.7 to pass
1
+ // this test has to be run with a version of node.js older than 16.13 to pass
2
2
 
3
3
  const { spawn } = require("child_process");
4
4
  const path = require("path");
@@ -11,7 +11,7 @@ const wranglerProcess = spawn(
11
11
  { stdio: "pipe" }
12
12
  );
13
13
 
14
- const messageToMatch = "Wrangler requires at least node.js v16.7.0";
14
+ const messageToMatch = "Wrangler requires at least node.js v16.13.0";
15
15
 
16
16
  wranglerProcess.once("exit", (code) => {
17
17
  try {
@@ -25,7 +25,7 @@ wranglerProcess.once("exit", (code) => {
25
25
  } catch (err) {
26
26
  console.error("Error:", err);
27
27
  throw new Error(
28
- "This test has to be run with a version of node.js under 16.7 to pass"
28
+ "This test has to be run with a version of node.js under 16.13 to pass"
29
29
  );
30
30
  }
31
31
  });
@@ -4,7 +4,7 @@ import { mockConsoleMethods } from "./helpers/mock-console";
4
4
  import { runInTempDir } from "./helpers/run-in-tmp";
5
5
  import { runWrangler } from "./helpers/run-wrangler";
6
6
 
7
- describe("worker-namespace", () => {
7
+ describe("dispatch-namespace", () => {
8
8
  runInTempDir();
9
9
  const std = mockConsoleMethods();
10
10
  mockAccountId();
@@ -14,8 +14,8 @@ describe("worker-namespace", () => {
14
14
  unsetAllMocks();
15
15
  });
16
16
 
17
- it("should should display a list of available subcommands, for worker-namespace with no subcommand", async () => {
18
- await runWrangler("worker-namespace");
17
+ it("should should display a list of available subcommands, for dispatch-namespace with no subcommand", async () => {
18
+ await runWrangler("dispatch-namespace");
19
19
 
20
20
  // wait a tick for the help menu to be printed
21
21
  await new Promise((resolve) => setImmediate(resolve));
@@ -24,16 +24,16 @@ describe("worker-namespace", () => {
24
24
  Object {
25
25
  "debug": "",
26
26
  "err": "",
27
- "out": "wrangler worker-namespace
27
+ "out": "wrangler dispatch-namespace
28
28
 
29
- 📦 Interact with a worker namespace
29
+ 📦 Interact with a dispatch namespace
30
30
 
31
31
  Commands:
32
- wrangler worker-namespace list List all Worker namespaces
33
- wrangler worker-namespace get <name> Get information about a Worker namespace
34
- wrangler worker-namespace create <name> Create a Worker namespace
35
- wrangler worker-namespace delete <name> Delete a Worker namespace
36
- wrangler worker-namespace rename <old-name> <new-name> Rename a Worker namespace
32
+ wrangler dispatch-namespace list List all dispatch namespaces
33
+ wrangler dispatch-namespace get <name> Get information about a dispatch namespace
34
+ wrangler dispatch-namespace create <name> Create a dispatch namespace
35
+ wrangler dispatch-namespace delete <name> Delete a dispatch namespace
36
+ wrangler dispatch-namespace rename <old-name> <new-name> Rename a dispatch namespace
37
37
 
38
38
  Flags:
39
39
  -c, --config Path to .toml configuration file [string]
@@ -73,19 +73,19 @@ describe("worker-namespace", () => {
73
73
 
74
74
  it("should display help for create", async () => {
75
75
  await expect(
76
- runWrangler("worker-namespace create")
76
+ runWrangler("dispatch-namespace create")
77
77
  ).rejects.toThrowErrorMatchingInlineSnapshot(
78
78
  `"Not enough non-option arguments: got 0, need at least 1"`
79
79
  );
80
80
 
81
81
  expect(std.out).toMatchInlineSnapshot(`
82
82
  "
83
- wrangler worker-namespace create <name>
83
+ wrangler dispatch-namespace create <name>
84
84
 
85
- Create a Worker namespace
85
+ Create a dispatch namespace
86
86
 
87
87
  Positionals:
88
- name Name of the Worker namespace [string] [required]
88
+ name Name of the dispatch namespace [string] [required]
89
89
 
90
90
  Flags:
91
91
  -c, --config Path to .toml configuration file [string]
@@ -97,11 +97,11 @@ describe("worker-namespace", () => {
97
97
  it("should attempt to create the given namespace", async () => {
98
98
  const namespaceName = "my-namespace";
99
99
  const requests = mockCreateRequest(namespaceName);
100
- await runWrangler(`worker-namespace create ${namespaceName}`);
100
+ await runWrangler(`dispatch-namespace create ${namespaceName}`);
101
101
  expect(requests.count).toEqual(1);
102
102
 
103
103
  expect(std.out).toMatchInlineSnapshot(
104
- `"Created Worker namespace \\"my-namespace\\" with ID \\"some-namespace-id\\""`
104
+ `"Created dispatch namespace \\"my-namespace\\" with ID \\"some-namespace-id\\""`
105
105
  );
106
106
  });
107
107
  });
@@ -125,19 +125,19 @@ describe("worker-namespace", () => {
125
125
 
126
126
  it("should display help for delete", async () => {
127
127
  await expect(
128
- runWrangler("worker-namespace create")
128
+ runWrangler("dispatch-namespace create")
129
129
  ).rejects.toThrowErrorMatchingInlineSnapshot(
130
130
  `"Not enough non-option arguments: got 0, need at least 1"`
131
131
  );
132
132
 
133
133
  expect(std.out).toMatchInlineSnapshot(`
134
134
  "
135
- wrangler worker-namespace create <name>
135
+ wrangler dispatch-namespace create <name>
136
136
 
137
- Create a Worker namespace
137
+ Create a dispatch namespace
138
138
 
139
139
  Positionals:
140
- name Name of the Worker namespace [string] [required]
140
+ name Name of the dispatch namespace [string] [required]
141
141
 
142
142
  Flags:
143
143
  -c, --config Path to .toml configuration file [string]
@@ -149,11 +149,11 @@ describe("worker-namespace", () => {
149
149
  it("should try to delete the given namespace", async () => {
150
150
  const namespaceName = "my-namespace";
151
151
  const requests = mockDeleteRequest(namespaceName);
152
- await runWrangler(`worker-namespace delete ${namespaceName}`);
152
+ await runWrangler(`dispatch-namespace delete ${namespaceName}`);
153
153
  expect(requests.count).toBe(1);
154
154
 
155
155
  expect(std.out).toMatchInlineSnapshot(
156
- `"Deleted Worker namespace \\"my-namespace\\""`
156
+ `"Deleted dispatch namespace \\"my-namespace\\""`
157
157
  );
158
158
  });
159
159
  });
@@ -183,19 +183,19 @@ describe("worker-namespace", () => {
183
183
 
184
184
  it("should display help for get", async () => {
185
185
  await expect(
186
- runWrangler("worker-namespace get")
186
+ runWrangler("dispatch-namespace get")
187
187
  ).rejects.toThrowErrorMatchingInlineSnapshot(
188
188
  `"Not enough non-option arguments: got 0, need at least 1"`
189
189
  );
190
190
 
191
191
  expect(std.out).toMatchInlineSnapshot(`
192
192
  "
193
- wrangler worker-namespace get <name>
193
+ wrangler dispatch-namespace get <name>
194
194
 
195
- Get information about a Worker namespace
195
+ Get information about a dispatch namespace
196
196
 
197
197
  Positionals:
198
- name Name of the Worker namespace [string] [required]
198
+ name Name of the dispatch namespace [string] [required]
199
199
 
200
200
  Flags:
201
201
  -c, --config Path to .toml configuration file [string]
@@ -207,7 +207,7 @@ describe("worker-namespace", () => {
207
207
  it("should attempt to get info for the given namespace", async () => {
208
208
  const namespaceName = "my-namespace";
209
209
  const requests = mockInfoRequest(namespaceName);
210
- await runWrangler(`worker-namespace get ${namespaceName}`);
210
+ await runWrangler(`dispatch-namespace get ${namespaceName}`);
211
211
  expect(requests.count).toBe(1);
212
212
 
213
213
  expect(std.out).toMatchInlineSnapshot(`
@@ -248,7 +248,7 @@ describe("worker-namespace", () => {
248
248
 
249
249
  it("should list all namespaces", async () => {
250
250
  const requests = mockListRequest();
251
- await runWrangler("worker-namespace list");
251
+ await runWrangler("dispatch-namespace list");
252
252
  expect(requests.count).toBe(1);
253
253
  expect(std.out).toMatchInlineSnapshot(`
254
254
  "[
@@ -291,20 +291,20 @@ describe("worker-namespace", () => {
291
291
 
292
292
  it("should display help for rename", async () => {
293
293
  await expect(
294
- runWrangler("worker-namespace rename")
294
+ runWrangler("dispatch-namespace rename")
295
295
  ).rejects.toThrowErrorMatchingInlineSnapshot(
296
296
  `"Not enough non-option arguments: got 0, need at least 2"`
297
297
  );
298
298
 
299
299
  expect(std.out).toMatchInlineSnapshot(`
300
300
  "
301
- wrangler worker-namespace rename <old-name> <new-name>
301
+ wrangler dispatch-namespace rename <old-name> <new-name>
302
302
 
303
- Rename a Worker namespace
303
+ Rename a dispatch namespace
304
304
 
305
305
  Positionals:
306
- old-name Name of the Worker namespace [string] [required]
307
- new-name New name of the Worker namespace [string] [required]
306
+ old-name Name of the dispatch namespace [string] [required]
307
+ new-name New name of the dispatch namespace [string] [required]
308
308
 
309
309
  Flags:
310
310
  -c, --config Path to .toml configuration file [string]
@@ -317,10 +317,12 @@ describe("worker-namespace", () => {
317
317
  const namespaceName = "my-namespace";
318
318
  const newName = "new-namespace";
319
319
  const requests = mockRenameRequest(namespaceName);
320
- await runWrangler(`worker-namespace rename ${namespaceName} ${newName}`);
320
+ await runWrangler(
321
+ `dispatch-namespace rename ${namespaceName} ${newName}`
322
+ );
321
323
  expect(requests.count).toBe(1);
322
324
  expect(std.out).toMatchInlineSnapshot(
323
- `"Renamed Worker namespace \\"my-namespace\\" to \\"new-namespace\\""`
325
+ `"Renamed dispatch namespace \\"my-namespace\\" to \\"new-namespace\\""`
324
326
  );
325
327
  });
326
328
  });
package/src/api/dev.ts CHANGED
@@ -1,10 +1,12 @@
1
- import { startDev } from "../dev";
1
+ import { fetch } from "undici";
2
+ import { startApiDev, startDev } from "../dev";
2
3
  import { logger } from "../logger";
3
4
 
4
5
  import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli";
5
6
  import type { RequestInit, Response } from "undici";
6
7
 
7
8
  interface DevOptions {
9
+ config?: string;
8
10
  env?: string;
9
11
  ip?: string;
10
12
  port?: number;
@@ -20,7 +22,7 @@ interface DevOptions {
20
22
  experimentalEnableLocalPersistence?: boolean;
21
23
  liveReload?: boolean;
22
24
  watch?: boolean;
23
- vars: {
25
+ vars?: {
24
26
  [key: string]: unknown;
25
27
  };
26
28
  kv?: {
@@ -48,46 +50,109 @@ interface DevOptions {
48
50
  _?: (string | number)[]; //yargs wants this
49
51
  $0?: string; //yargs wants this
50
52
  }
53
+
54
+ interface DevApiOptions {
55
+ testMode?: boolean;
56
+ disableExperimentalWarning?: boolean;
57
+ }
58
+
59
+ interface UnstableDev {
60
+ stop: () => Promise<void>;
61
+ fetch: (init?: RequestInit) => Promise<Response | undefined>;
62
+ waitUntilExit: () => Promise<void>;
63
+ }
51
64
  /**
52
65
  * unstable_dev starts a wrangler dev server, and returns a promise that resolves with utility functions to interact with it.
53
66
  * @param {string} script
54
67
  * @param {DevOptions} options
68
+ * @param {DevApiOptions} apiOptions
69
+ * @returns {Promise<UnstableDev>}
55
70
  */
56
71
  export async function unstable_dev(
57
72
  script: string,
58
- options: DevOptions,
59
- disableExperimentalWarning?: boolean
73
+ options?: DevOptions,
74
+ apiOptions?: DevApiOptions
60
75
  ) {
76
+ const { testMode = true, disableExperimentalWarning = false } =
77
+ apiOptions || {};
61
78
  if (!disableExperimentalWarning) {
62
79
  logger.warn(
63
80
  `unstable_dev() is experimental\nunstable_dev()'s behaviour will likely change in future releases`
64
81
  );
65
82
  }
66
-
67
- return new Promise<{
68
- stop: () => void;
69
- fetch: (init?: RequestInit) => Promise<Response | undefined>;
70
- waitUntilExit: () => Promise<void>;
71
- }>((resolve) => {
72
- //lmao
73
- return new Promise<Awaited<ReturnType<typeof startDev>>>((ready) => {
74
- const devServer = startDev({
75
- script: script,
76
- inspect: false,
77
- logLevel: "none",
78
- showInteractiveDevSession: false,
79
- _: [],
80
- $0: "",
81
- ...options,
82
- local: true,
83
- onReady: () => ready(devServer),
83
+ let readyPort: number;
84
+ let readyAddress: string;
85
+ //due to Pages adoption of unstable_dev, we can't *just* disable rebuilds and watching. instead, we'll have two versions of startDev, which will converge.
86
+ if (testMode) {
87
+ //in testMode, we can run multiple wranglers in parallel, but rebuilds might not work out of the box
88
+ return new Promise<UnstableDev>((resolve) => {
89
+ //lmao
90
+ return new Promise<Awaited<ReturnType<typeof startApiDev>>>((ready) => {
91
+ // once the devServer is ready for requests, we resolve the inner promise
92
+ // (where we've named the resolve function "ready")
93
+ const devServer = startApiDev({
94
+ script: script,
95
+ inspect: false,
96
+ logLevel: "none",
97
+ showInteractiveDevSession: false,
98
+ _: [],
99
+ $0: "",
100
+ port: options?.port ?? 0,
101
+ ...options,
102
+ local: true,
103
+ onReady: (address, port) => {
104
+ readyPort = port;
105
+ readyAddress = address;
106
+ ready(devServer);
107
+ },
108
+ });
109
+ }).then((devServer) => {
110
+ // now that the inner promise has resolved, we can resolve the outer promise
111
+ // with an object that lets you fetch and stop the dev server
112
+ resolve({
113
+ stop: devServer.stop,
114
+ fetch: async (init?: RequestInit) => {
115
+ const urlToFetch = `http://${readyAddress}:${readyPort}/`;
116
+ return await fetch(urlToFetch, init);
117
+ },
118
+ //no-op, does nothing in tests
119
+ waitUntilExit: async () => {
120
+ return;
121
+ },
122
+ });
84
123
  });
85
- }).then((devServer) => {
86
- resolve({
87
- stop: devServer.stop,
88
- fetch: devServer.fetch,
89
- waitUntilExit: devServer.devReactElement.waitUntilExit,
124
+ });
125
+ } else {
126
+ //outside of test mode, rebuilds work fine, but only one instance of wrangler will work at a time
127
+
128
+ return new Promise<UnstableDev>((resolve) => {
129
+ //lmao
130
+ return new Promise<Awaited<ReturnType<typeof startDev>>>((ready) => {
131
+ const devServer = startDev({
132
+ script: script,
133
+ inspect: false,
134
+ logLevel: "none",
135
+ showInteractiveDevSession: false,
136
+ _: [],
137
+ $0: "",
138
+ ...options,
139
+ local: true,
140
+ onReady: (address, port) => {
141
+ readyPort = port;
142
+ readyAddress = address;
143
+ ready(devServer);
144
+ },
145
+ });
146
+ }).then((devServer) => {
147
+ resolve({
148
+ stop: devServer.stop,
149
+ fetch: async (init?: RequestInit) => {
150
+ const urlToFetch = `http://${readyAddress}:${readyPort}/`;
151
+ return await fetch(urlToFetch, init);
152
+ },
153
+ waitUntilExit: devServer.devReactElement.waitUntilExit,
154
+ });
90
155
  });
91
156
  });
92
- });
157
+ }
93
158
  }