wrangler 2.4.4 → 2.6.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 (46) hide show
  1. package/bin/wrangler.js +20 -8
  2. package/miniflare-dist/index.mjs +90 -41
  3. package/package.json +3 -3
  4. package/src/__tests__/configuration.test.ts +211 -0
  5. package/src/__tests__/delete.test.ts +81 -48
  6. package/src/__tests__/dev.test.tsx +25 -8
  7. package/src/__tests__/helpers/mock-oauth-flow.ts +5 -1
  8. package/src/__tests__/helpers/msw/handlers/oauth.ts +13 -18
  9. package/src/__tests__/init.test.ts +18 -3
  10. package/src/__tests__/logout.test.ts +47 -0
  11. package/src/__tests__/metrics.test.ts +88 -43
  12. package/src/__tests__/pages-deployment-tail.test.ts +165 -101
  13. package/src/__tests__/publish.test.ts +94 -7
  14. package/src/__tests__/pubsub.test.ts +208 -88
  15. package/src/__tests__/queues.test.ts +155 -67
  16. package/src/__tests__/tail.test.ts +207 -108
  17. package/src/__tests__/type-generation.test.ts +7 -0
  18. package/src/__tests__/user.test.ts +43 -69
  19. package/src/config/environment.ts +16 -0
  20. package/src/config/index.ts +31 -8
  21. package/src/config/validation.ts +49 -0
  22. package/src/create-worker-upload-form.ts +9 -0
  23. package/src/d1/backups.tsx +7 -2
  24. package/src/d1/delete.tsx +4 -4
  25. package/src/d1/index.ts +2 -0
  26. package/src/d1/migrations/apply.tsx +6 -5
  27. package/src/d1/migrations/helpers.ts +4 -2
  28. package/src/d1/migrations/list.tsx +2 -2
  29. package/src/d1/migrations/options.ts +18 -0
  30. package/src/dev/dev.tsx +46 -22
  31. package/src/dev/local.tsx +63 -29
  32. package/src/dev/start-server.ts +18 -21
  33. package/src/dev.tsx +33 -13
  34. package/src/git-client.ts +15 -4
  35. package/src/index.tsx +1 -0
  36. package/src/init.ts +8 -0
  37. package/src/miniflare-cli/assets.ts +55 -28
  38. package/src/miniflare-cli/index.ts +2 -1
  39. package/src/pages/dev.tsx +7 -0
  40. package/src/proxy.ts +5 -0
  41. package/src/publish/publish.ts +1 -0
  42. package/src/secret/index.ts +1 -0
  43. package/src/type-generation.ts +10 -2
  44. package/src/worker.ts +6 -0
  45. package/wrangler-dist/cli.d.ts +15 -0
  46. package/wrangler-dist/cli.js +2045 -636
@@ -1,7 +1,8 @@
1
+ import { rest } from "msw";
1
2
  import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
2
- import { setMockResponse, unsetAllMocks } from "./helpers/mock-cfetch";
3
3
  import { mockConsoleMethods } from "./helpers/mock-console";
4
4
  import { mockConfirm } from "./helpers/mock-dialogs";
5
+ import { msw } from "./helpers/msw";
5
6
  import { runInTempDir } from "./helpers/run-in-tmp";
6
7
  import { runWrangler } from "./helpers/run-wrangler";
7
8
  import writeWranglerToml from "./helpers/write-wrangler-toml";
@@ -12,10 +13,6 @@ describe("delete", () => {
12
13
  mockApiToken();
13
14
  runInTempDir();
14
15
 
15
- afterEach(() => {
16
- unsetAllMocks();
17
- });
18
-
19
16
  const std = mockConsoleMethods();
20
17
 
21
18
  it("should delete an entire service by name", async () => {
@@ -107,14 +104,19 @@ describe("delete", () => {
107
104
  });
108
105
  mockListKVNamespacesRequest(...kvNamespaces);
109
106
  // it should only try to delete the site namespace associated with this worker
110
- setMockResponse(
111
- "/accounts/:accountId/storage/kv/namespaces/id-for-my-script-site-ns",
112
- "DELETE",
113
- ([_url, accountId]) => {
114
- expect(accountId).toEqual("some-account-id");
115
- return null;
116
- }
107
+ msw.use(
108
+ rest.delete(
109
+ "*/accounts/:accountId/storage/kv/namespaces/id-for-my-script-site-ns",
110
+ (req, res, ctx) => {
111
+ expect(req.params.accountId).toEqual("some-account-id");
112
+ return res.once(
113
+ ctx.status(200),
114
+ ctx.json({ success: true, errors: [], messages: [], result: null })
115
+ );
116
+ }
117
+ )
117
118
  );
119
+
118
120
  mockDeleteWorkerRequest({ name: "my-script" });
119
121
  await runWrangler("delete --name my-script");
120
122
  expect(std).toMatchInlineSnapshot(`
@@ -155,22 +157,40 @@ describe("delete", () => {
155
157
  mockListKVNamespacesRequest(...kvNamespaces);
156
158
  // it should only try to delete the site namespace associated with this worker
157
159
 
158
- setMockResponse(
159
- "/accounts/:accountId/storage/kv/namespaces/id-for-my-script-site-ns",
160
- "DELETE",
161
- ([_url, accountId]) => {
162
- expect(accountId).toEqual("some-account-id");
163
- return null;
164
- }
160
+ msw.use(
161
+ rest.delete(
162
+ "*/accounts/:accountId/storage/kv/namespaces/id-for-my-script-site-ns",
163
+ (req, res, ctx) => {
164
+ expect(req.params.accountId).toEqual("some-account-id");
165
+ return res.once(
166
+ ctx.status(200),
167
+ ctx.json({
168
+ success: true,
169
+ errors: [],
170
+ messages: [],
171
+ result: {},
172
+ })
173
+ );
174
+ }
175
+ )
165
176
  );
166
177
 
167
- setMockResponse(
168
- "/accounts/:accountId/storage/kv/namespaces/id-for-my-script-site-preview-ns",
169
- "DELETE",
170
- ([_url, accountId]) => {
171
- expect(accountId).toEqual("some-account-id");
172
- return null;
173
- }
178
+ msw.use(
179
+ rest.delete(
180
+ "*/accounts/:accountId/storage/kv/namespaces/id-for-my-script-site-preview-ns",
181
+ (req, res, ctx) => {
182
+ expect(req.params.accountId).toEqual("some-account-id");
183
+ return res.once(
184
+ ctx.status(200),
185
+ ctx.json({
186
+ success: true,
187
+ errors: [],
188
+ messages: [],
189
+ result: {},
190
+ })
191
+ );
192
+ }
193
+ )
174
194
  );
175
195
 
176
196
  mockDeleteWorkerRequest({ name: "my-script" });
@@ -197,34 +217,47 @@ function mockDeleteWorkerRequest(
197
217
  } = {}
198
218
  ) {
199
219
  const { env, legacyEnv, name } = options;
200
- setMockResponse(
201
- // there's no special handling for environments yet
202
- "/accounts/:accountId/workers/services/:scriptName",
203
- "DELETE",
204
- async ([_url, accountId, scriptName], { method }, queryParams) => {
205
- expect(accountId).toEqual("some-account-id");
206
- expect(method).toEqual("DELETE");
207
- expect(scriptName).toEqual(
208
- legacyEnv && env
209
- ? `${name || "test-name"}-${env}`
210
- : `${name || "test-name"}`
211
- );
220
+ msw.use(
221
+ rest.delete(
222
+ "*/accounts/:accountId/workers/services/:scriptName",
223
+ (req, res, ctx) => {
224
+ expect(req.params.accountId).toEqual("some-account-id");
225
+ expect(req.params.scriptName).toEqual(
226
+ legacyEnv && env
227
+ ? `${name ?? "test-name"}-${env}`
228
+ : `${name ?? "test-name"}`
229
+ );
212
230
 
213
- expect(queryParams.get("force")).toEqual("true");
231
+ expect(req.url.searchParams.get("force")).toEqual("true");
214
232
 
215
- return null;
216
- }
233
+ return res.once(
234
+ ctx.status(200),
235
+ ctx.json({
236
+ success: true,
237
+ errors: [],
238
+ messages: [],
239
+ result: null,
240
+ })
241
+ );
242
+ }
243
+ )
217
244
  );
218
245
  }
219
246
 
220
247
  /** Create a mock handler for the request to get a list of all KV namespaces. */
221
248
  function mockListKVNamespacesRequest(...namespaces: KVNamespaceInfo[]) {
222
- setMockResponse(
223
- "/accounts/:accountId/storage/kv/namespaces",
224
- "GET",
225
- ([_url, accountId]) => {
226
- expect(accountId).toEqual("some-account-id");
227
- return namespaces;
228
- }
249
+ msw.use(
250
+ rest.get("*/accounts/:accountId/storage/kv/namespaces", (req, res, ctx) => {
251
+ expect(req.params.accountId).toEqual("some-account-id");
252
+ return res.once(
253
+ ctx.status(200),
254
+ ctx.json({
255
+ success: true,
256
+ errors: [],
257
+ messages: [],
258
+ result: namespaces,
259
+ })
260
+ );
261
+ })
229
262
  );
230
263
  }
@@ -3,6 +3,7 @@ import getPort from "get-port";
3
3
  import patchConsole from "patch-console";
4
4
  import dedent from "ts-dedent";
5
5
  import Dev from "../dev/dev";
6
+ import { CI } from "../is-ci";
6
7
  import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
7
8
  import { setMockResponse, unsetAllMocks } from "./helpers/mock-cfetch";
8
9
  import { mockConsoleMethods } from "./helpers/mock-console";
@@ -35,6 +36,22 @@ describe("wrangler dev", () => {
35
36
  unsetAllMocks();
36
37
  });
37
38
 
39
+ describe("authorization", () => {
40
+ mockApiToken({ apiToken: null });
41
+ const isCISpy = jest.spyOn(CI, "isCI").mockReturnValue(true);
42
+
43
+ it("should kick you to the login flow when running wrangler dev in remote mode without authorization", async () => {
44
+ fs.writeFileSync("index.js", `export default {};`);
45
+ await expect(
46
+ runWrangler("dev index.js")
47
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
48
+ `"You must be logged in to use wrangler dev in remote mode. Try logging in, or run wrangler dev --local."`
49
+ );
50
+ });
51
+
52
+ isCISpy.mockClear();
53
+ });
54
+
38
55
  describe("compatibility-date", () => {
39
56
  it("should not warn if there is no wrangler.toml and no compatibility-date specified", async () => {
40
57
  fs.writeFileSync("index.js", `export default {};`);
@@ -792,7 +809,7 @@ describe("wrangler dev", () => {
792
809
  });
793
810
  fs.writeFileSync("index.js", `export default {};`);
794
811
  await runWrangler("dev");
795
- expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("0.0.0.0");
812
+ expect((Dev as jest.Mock).mock.calls[0][0].initialIp).toEqual("0.0.0.0");
796
813
  expect(std.out).toMatchInlineSnapshot(`""`);
797
814
  expect(std.warn).toMatchInlineSnapshot(`""`);
798
815
  expect(std.err).toMatchInlineSnapshot(`""`);
@@ -807,7 +824,7 @@ describe("wrangler dev", () => {
807
824
  });
808
825
  fs.writeFileSync("index.js", `export default {};`);
809
826
  await runWrangler("dev");
810
- expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("1.2.3.4");
827
+ expect((Dev as jest.Mock).mock.calls[0][0].initialIp).toEqual("1.2.3.4");
811
828
  expect(std.out).toMatchInlineSnapshot(`""`);
812
829
  expect(std.warn).toMatchInlineSnapshot(`""`);
813
830
  expect(std.err).toMatchInlineSnapshot(`""`);
@@ -822,7 +839,7 @@ describe("wrangler dev", () => {
822
839
  });
823
840
  fs.writeFileSync("index.js", `export default {};`);
824
841
  await runWrangler("dev --ip=5.6.7.8");
825
- expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("5.6.7.8");
842
+ expect((Dev as jest.Mock).mock.calls[0][0].initialIp).toEqual("5.6.7.8");
826
843
  expect(std.out).toMatchInlineSnapshot(`""`);
827
844
  expect(std.warn).toMatchInlineSnapshot(`""`);
828
845
  expect(std.err).toMatchInlineSnapshot(`""`);
@@ -933,7 +950,7 @@ describe("wrangler dev", () => {
933
950
  });
934
951
  fs.writeFileSync("index.js", `export default {};`);
935
952
  await runWrangler("dev");
936
- expect((Dev as jest.Mock).mock.calls[0][0].port).toEqual(8787);
953
+ expect((Dev as jest.Mock).mock.calls[0][0].initialPort).toEqual(8787);
937
954
  expect(std.out).toMatchInlineSnapshot(`""`);
938
955
  expect(std.warn).toMatchInlineSnapshot(`""`);
939
956
  expect(std.err).toMatchInlineSnapshot(`""`);
@@ -951,7 +968,7 @@ describe("wrangler dev", () => {
951
968
  (getPort as jest.Mock).mockResolvedValue(98765);
952
969
 
953
970
  await runWrangler("dev");
954
- expect((Dev as jest.Mock).mock.calls[0][0].port).toEqual(8888);
971
+ expect((Dev as jest.Mock).mock.calls[0][0].initialPort).toEqual(8888);
955
972
  expect(std.out).toMatchInlineSnapshot(`""`);
956
973
  expect(std.warn).toMatchInlineSnapshot(`""`);
957
974
  expect(std.err).toMatchInlineSnapshot(`""`);
@@ -985,7 +1002,7 @@ describe("wrangler dev", () => {
985
1002
  (getPort as jest.Mock).mockResolvedValue(98765);
986
1003
 
987
1004
  await runWrangler("dev --port=9999");
988
- expect((Dev as jest.Mock).mock.calls[0][0].port).toEqual(9999);
1005
+ expect((Dev as jest.Mock).mock.calls[0][0].initialPort).toEqual(9999);
989
1006
  expect(std.out).toMatchInlineSnapshot(`""`);
990
1007
  expect(std.warn).toMatchInlineSnapshot(`""`);
991
1008
  expect(std.err).toMatchInlineSnapshot(`""`);
@@ -1000,7 +1017,7 @@ describe("wrangler dev", () => {
1000
1017
  (getPort as jest.Mock).mockResolvedValue(98765);
1001
1018
 
1002
1019
  await runWrangler("dev");
1003
- expect((Dev as jest.Mock).mock.calls[0][0].port).toEqual(98765);
1020
+ expect((Dev as jest.Mock).mock.calls[0][0].initialPort).toEqual(98765);
1004
1021
  expect(std.out).toMatchInlineSnapshot(`""`);
1005
1022
  expect(std.warn).toMatchInlineSnapshot(`""`);
1006
1023
  expect(std.err).toMatchInlineSnapshot(`""`);
@@ -1030,7 +1047,7 @@ describe("wrangler dev", () => {
1030
1047
  });
1031
1048
  fs.writeFileSync("index.js", `export default {};`);
1032
1049
  await runWrangler("dev");
1033
- expect((Dev as jest.Mock).mock.calls[0][0].ip).toEqual("0.0.0.0");
1050
+ expect((Dev as jest.Mock).mock.calls[0][0].initialIp).toEqual("0.0.0.0");
1034
1051
  expect(std.out).toMatchInlineSnapshot(`
1035
1052
  "Your worker has access to the following bindings:
1036
1053
  - Durable Objects:
@@ -42,10 +42,13 @@ export const mockOAuthFlow = () => {
42
42
  * at the OAuth URL, it will automatically trigger the callback URL on the mock HttpServer that
43
43
  * we have created as part of the call to `mockOAuthFlow()`.
44
44
  */
45
- const mockOAuthServerCallback = () => {
45
+ const mockOAuthServerCallback = (
46
+ respondWith?: "timeout" | "success" | "failure" | GrantResponseOptions
47
+ ) => {
46
48
  (
47
49
  openInBrowser as jest.MockedFunction<typeof openInBrowser>
48
50
  ).mockImplementation(async (url: string) => {
51
+ if (respondWith) mockGrantAuthorization({ respondWith });
49
52
  // We don't support the grant response timing out.
50
53
  if (oauthGrantResponse === "timeout") {
51
54
  throw "unimplemented";
@@ -68,6 +71,7 @@ export const mockOAuthFlow = () => {
68
71
  });
69
72
  };
70
73
 
74
+ //TODO: this can just handled in `mockOAuthServerCallback`
71
75
  const mockGrantAuthorization = ({
72
76
  respondWith,
73
77
  }: {
@@ -13,24 +13,19 @@ export const mswSuccessOauthHandlers = [
13
13
  );
14
14
  }),
15
15
  // revoke access token
16
- rest.post(
17
- "https://dash.cloudflare.com/oauth2/revoke",
18
- (_, response, context) =>
19
- response.once(context.status(200), context.text(""))
16
+ rest.post("*/oauth2/revoke", (_, response, context) =>
17
+ response.once(context.status(200), context.text(""))
20
18
  ),
21
19
  // exchange (auth code | refresh token) for access token
22
- rest.post(
23
- "https://dash.cloudflare.com/oauth2/token",
24
- (_, response, context) => {
25
- return response.once(
26
- context.status(200),
27
- context.json({
28
- access_token: "test-access-token",
29
- expires_in: 100000,
30
- refresh_token: "test-refresh-token",
31
- scope: "account:read",
32
- })
33
- );
34
- }
35
- ),
20
+ rest.post("*/oauth2/token", (_, response, context) => {
21
+ return response.once(
22
+ context.status(200),
23
+ context.json({
24
+ access_token: "test-access-token",
25
+ expires_in: 100000,
26
+ refresh_token: "test-refresh-token",
27
+ scope: "account:read",
28
+ })
29
+ );
30
+ }),
36
31
  ];
@@ -529,7 +529,7 @@ describe("init", () => {
529
529
  }
530
530
  `);
531
531
  expect((await execa("git", ["branch", "--show-current"])).stdout).toEqual(
532
- "main"
532
+ getDefaultBranchName()
533
533
  );
534
534
  });
535
535
 
@@ -598,7 +598,7 @@ describe("init", () => {
598
598
  "should not offer to initialize a git repo if git is not installed"
599
599
  );
600
600
 
601
- it("should initialize git repo with `main` default branch", async () => {
601
+ it("should initialize git repo with the user's default branch", async () => {
602
602
  mockConfirm(
603
603
  {
604
604
  text: "Would you like to use git to manage this Worker?",
@@ -621,7 +621,7 @@ describe("init", () => {
621
621
  `);
622
622
 
623
623
  expect(execaSync("git", ["symbolic-ref", "HEAD"]).stdout).toEqual(
624
- "refs/heads/main"
624
+ `refs/heads/${getDefaultBranchName()}`
625
625
  );
626
626
  });
627
627
  });
@@ -2810,6 +2810,21 @@ describe("init", () => {
2810
2810
  });
2811
2811
  });
2812
2812
 
2813
+ function getDefaultBranchName() {
2814
+ try {
2815
+ const { stdout: defaultBranchName } = execaSync("git", [
2816
+ "config",
2817
+ "--get",
2818
+ "init.defaultBranch",
2819
+ ]);
2820
+
2821
+ return defaultBranchName;
2822
+ } catch {
2823
+ // ew
2824
+ return "master";
2825
+ }
2826
+ }
2827
+
2813
2828
  /**
2814
2829
  * Change the current working directory, ensuring that this exists.
2815
2830
  */
@@ -0,0 +1,47 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { rest } from "msw";
4
+ import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
5
+ import { USER_AUTH_CONFIG_FILE, writeAuthConfigFile } from "../user";
6
+ import { mockConsoleMethods } from "./helpers/mock-console";
7
+ import { msw } from "./helpers/msw";
8
+ import { runInTempDir } from "./helpers/run-in-tmp";
9
+ import { runWrangler } from "./helpers/run-wrangler";
10
+
11
+ describe("logout", () => {
12
+ runInTempDir();
13
+ const std = mockConsoleMethods();
14
+
15
+ it("should exit with a message stating the user is not logged in", async () => {
16
+ await runWrangler("logout");
17
+ expect(std.out).toMatchInlineSnapshot(`"Not logged in, exiting..."`);
18
+ });
19
+
20
+ it("should logout user that has been properly logged in", async () => {
21
+ writeAuthConfigFile({
22
+ oauth_token: "some-oauth-tok",
23
+ refresh_token: "some-refresh-tok",
24
+ });
25
+ // Make sure that logout removed the config file containing the auth tokens.
26
+ const config = path.join(
27
+ getGlobalWranglerConfigPath(),
28
+ USER_AUTH_CONFIG_FILE
29
+ );
30
+ let counter = 0;
31
+ msw.use(
32
+ rest.post("*/oauth2/revoke", (_, response, context) => {
33
+ // Make sure that we made the request to logout.
34
+ counter += 1;
35
+ response.once(context.status(200), context.text(""));
36
+ })
37
+ );
38
+
39
+ expect(fs.existsSync(config)).toBeTruthy();
40
+
41
+ await runWrangler("logout");
42
+
43
+ expect(std.out).toMatchInlineSnapshot(`"Successfully logged out."`);
44
+ expect(fs.existsSync(config)).toBeFalsy();
45
+ expect(counter).toBe(1);
46
+ });
47
+ });