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.
package/README.md CHANGED
@@ -82,7 +82,3 @@ For more commands and options, refer to the [documentation](https://developers.c
82
82
  ## Documentation
83
83
 
84
84
  For the latest Wrangler documentation, [click here](https://developers.cloudflare.com/workers/wrangler/).
85
-
86
- ```
87
-
88
- ```
@@ -746,10 +746,11 @@ import {
746
746
  DurableObjectStub
747
747
  } from "@miniflare/durable-objects";
748
748
  import {
749
- Log,
749
+ Log as MiniflareLog,
750
+ LogLevel as MiniflareLogLevel,
750
751
  Miniflare,
751
- Response as MiniflareResponse3,
752
- Request as MiniflareRequest3
752
+ Request as MiniflareRequest3,
753
+ Response as MiniflareResponse3
753
754
  } from "miniflare";
754
755
 
755
756
  // ../../node_modules/yargs/lib/platform-shims/esm.mjs
@@ -6145,7 +6146,7 @@ var getRequestContextCheckOptions = async () => {
6145
6146
  };
6146
6147
 
6147
6148
  // src/miniflare-cli/index.ts
6148
- var NoOpLog = class extends Log {
6149
+ var MiniflareNoOpLog = class extends MiniflareLog {
6149
6150
  log() {
6150
6151
  }
6151
6152
  error(message) {
@@ -6159,9 +6160,12 @@ async function main() {
6159
6160
  ...JSON.parse(args._[0] ?? "{}"),
6160
6161
  ...requestContextCheckOptions
6161
6162
  };
6162
- const logLevel = config.logLevel.toUpperCase();
6163
- config.log = config.logLevel === "none" ? new NoOpLog() : new Log(logLevel, config.logOptions);
6164
- if (logLevel === "DEBUG" || logLevel === "VERBOSE") {
6163
+ let logLevelString = config.logLevel.toUpperCase();
6164
+ if (logLevelString === "LOG")
6165
+ logLevelString = "INFO";
6166
+ const logLevel = MiniflareLogLevel[logLevelString];
6167
+ config.log = logLevel === MiniflareLogLevel.NONE ? new MiniflareNoOpLog() : new MiniflareLog(logLevel, config.logOptions);
6168
+ if (logLevel === MiniflareLogLevel.DEBUG) {
6165
6169
  console.log("MINIFLARE OPTIONS:\n", JSON.stringify(config, null, 2));
6166
6170
  }
6167
6171
  config.bindings = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wrangler",
3
- "version": "2.1.13",
3
+ "version": "2.1.15",
4
4
  "description": "Command-line interface for all things Cloudflare Workers",
5
5
  "keywords": [
6
6
  "wrangler",
@@ -160,6 +160,7 @@
160
160
  "jest-fetch-mock": "^3.0.3",
161
161
  "jest-websocket-mock": "^2.3.0",
162
162
  "mime": "^3.0.0",
163
+ "minimatch": "^5.1.0",
163
164
  "msw": "^0.47.1",
164
165
  "npx-import": "^1.1.3",
165
166
  "open": "^8.4.0",
@@ -5,6 +5,7 @@ import { mockConfirm } from "./helpers/mock-dialogs";
5
5
  import { runInTempDir } from "./helpers/run-in-tmp";
6
6
  import { runWrangler } from "./helpers/run-wrangler";
7
7
  import writeWranglerToml from "./helpers/write-wrangler-toml";
8
+ import type { KVNamespaceInfo } from "../kv/helpers";
8
9
 
9
10
  describe("delete", () => {
10
11
  mockAccountId();
@@ -22,6 +23,7 @@ describe("delete", () => {
22
23
  text: `Are you sure you want to delete my-script? This action cannot be undone.`,
23
24
  result: true,
24
25
  });
26
+ mockListKVNamespacesRequest();
25
27
  mockDeleteWorkerRequest({ name: "my-script" });
26
28
  await runWrangler("delete --name my-script");
27
29
 
@@ -41,6 +43,7 @@ describe("delete", () => {
41
43
  result: true,
42
44
  });
43
45
  writeWranglerToml();
46
+ mockListKVNamespacesRequest();
44
47
  mockDeleteWorkerRequest();
45
48
  await runWrangler("delete");
46
49
 
@@ -84,6 +87,105 @@ describe("delete", () => {
84
87
  }
85
88
  `);
86
89
  });
90
+
91
+ it("should delete a site namespace associated with a worker", async () => {
92
+ const kvNamespaces = [
93
+ {
94
+ title: "__my-script-workers_sites_assets",
95
+ id: "id-for-my-script-site-ns",
96
+ },
97
+ // this one isn't associated with the worker
98
+ {
99
+ title: "__test-name-workers_sites_assets",
100
+ id: "id-for-another-site-ns",
101
+ },
102
+ ];
103
+
104
+ mockConfirm({
105
+ text: `Are you sure you want to delete my-script? This action cannot be undone.`,
106
+ result: true,
107
+ });
108
+ mockListKVNamespacesRequest(...kvNamespaces);
109
+ // 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
+ }
117
+ );
118
+ mockDeleteWorkerRequest({ name: "my-script" });
119
+ await runWrangler("delete --name my-script");
120
+ expect(std).toMatchInlineSnapshot(`
121
+ Object {
122
+ "debug": "",
123
+ "err": "",
124
+ "out": "🌀 Deleted asset namespace for Workers Site \\"__my-script-workers_sites_assets\\"
125
+ Successfully deleted my-script",
126
+ "warn": "",
127
+ }
128
+ `);
129
+ });
130
+
131
+ it("should delete a site namespace associated with a worker, including it's preview namespace", async () => {
132
+ // This is the same test as the previous one, but it includes a preview namespace
133
+ const kvNamespaces = [
134
+ {
135
+ title: "__my-script-workers_sites_assets",
136
+ id: "id-for-my-script-site-ns",
137
+ },
138
+ // this is the preview namespace
139
+ {
140
+ title: "__my-script-workers_sites_assets_preview",
141
+ id: "id-for-my-script-site-preview-ns",
142
+ },
143
+
144
+ // this one isn't associated with the worker
145
+ {
146
+ title: "__test-name-workers_sites_assets",
147
+ id: "id-for-another-site-ns",
148
+ },
149
+ ];
150
+
151
+ mockConfirm({
152
+ text: `Are you sure you want to delete my-script? This action cannot be undone.`,
153
+ result: true,
154
+ });
155
+ mockListKVNamespacesRequest(...kvNamespaces);
156
+ // it should only try to delete the site namespace associated with this worker
157
+
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
+ }
165
+ );
166
+
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
+ }
174
+ );
175
+
176
+ mockDeleteWorkerRequest({ name: "my-script" });
177
+ await runWrangler("delete --name my-script");
178
+ expect(std).toMatchInlineSnapshot(`
179
+ Object {
180
+ "debug": "",
181
+ "err": "",
182
+ "out": "🌀 Deleted asset namespace for Workers Site \\"__my-script-workers_sites_assets\\"
183
+ 🌀 Deleted asset namespace for Workers Site \\"__my-script-workers_sites_assets_preview\\"
184
+ Successfully deleted my-script",
185
+ "warn": "",
186
+ }
187
+ `);
188
+ });
87
189
  });
88
190
 
89
191
  /** Create a mock handler for the request to upload a worker script. */
@@ -114,3 +216,15 @@ function mockDeleteWorkerRequest(
114
216
  }
115
217
  );
116
218
  }
219
+
220
+ /** Create a mock handler for the request to get a list of all KV namespaces. */
221
+ 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
+ }
229
+ );
230
+ }
@@ -0,0 +1,91 @@
1
+ // import * as fs from "fs";
2
+ // import * as TOML from "@iarna/toml";
3
+ import { rest } from "msw";
4
+ import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
5
+ import { mockConsoleMethods } from "./helpers/mock-console";
6
+ import {
7
+ msw,
8
+ mswSuccessOauthHandlers,
9
+ mswSuccessUserHandlers,
10
+ } from "./helpers/msw";
11
+ import { mswSuccessDeployments } from "./helpers/msw";
12
+ import { runInTempDir } from "./helpers/run-in-tmp";
13
+ import { runWrangler } from "./helpers/run-wrangler";
14
+
15
+ describe("deployments", () => {
16
+ const std = mockConsoleMethods();
17
+ runInTempDir();
18
+ mockAccountId();
19
+ mockApiToken();
20
+ runInTempDir();
21
+
22
+ beforeEach(() => {
23
+ msw.use(
24
+ ...mswSuccessDeployments,
25
+ ...mswSuccessOauthHandlers,
26
+ ...mswSuccessUserHandlers,
27
+ rest.get(
28
+ "*/accounts/:accountId/workers/services/:scriptName",
29
+ (request, response, context) => {
30
+ expect(["undefined", "somethingElse"]).toContain(
31
+ request.params.scriptName
32
+ );
33
+
34
+ return response.once(
35
+ context.status(200),
36
+ context.json({
37
+ success: true,
38
+ errors: [],
39
+ messages: [],
40
+ result: {
41
+ default_environment: {
42
+ script: {
43
+ tag: "MOCK-TAG",
44
+ },
45
+ },
46
+ },
47
+ })
48
+ );
49
+ }
50
+ )
51
+ );
52
+ });
53
+
54
+ it("should log deployments", async () => {
55
+ await runWrangler("deployments");
56
+ expect(std.out).toMatchInlineSnapshot(`
57
+ "
58
+ Version ID: Galaxy-Class
59
+ Version number: 1701-E
60
+ Created on: 2021-01-01T00:00:00.000000Z
61
+ Author email: Jean-Luc-Picard@federation.org
62
+ Latest deploy: true
63
+
64
+ Version ID: Intrepid-Class
65
+ Version number: NCC-74656
66
+ Created on: 2021-02-02T00:00:00.000000Z
67
+ Author email: Kathryn-Janeway@federation.org
68
+ Latest deploy: false
69
+ "
70
+ `);
71
+ });
72
+
73
+ it("should log deployments for script with passed in name option", async () => {
74
+ await runWrangler("deployments --name somethingElse");
75
+ expect(std.out).toMatchInlineSnapshot(`
76
+ "
77
+ Version ID: Galaxy-Class
78
+ Version number: 1701-E
79
+ Created on: 2021-01-01T00:00:00.000000Z
80
+ Author email: Jean-Luc-Picard@federation.org
81
+ Latest deploy: true
82
+
83
+ Version ID: Intrepid-Class
84
+ Version number: NCC-74656
85
+ Created on: 2021-02-02T00:00:00.000000Z
86
+ Author email: Kathryn-Janeway@federation.org
87
+ Latest deploy: false
88
+ "
89
+ `);
90
+ });
91
+ });
@@ -0,0 +1,80 @@
1
+ import { rest } from "msw";
2
+
3
+ export type DeploymentListRes = {
4
+ versions: {
5
+ version_id: string;
6
+ version_number: string;
7
+ metadata: {
8
+ author_id: string;
9
+ author_email: string;
10
+ source: "api" | "dash" | "wrangler" | "terraform" | "other";
11
+ created_on: string;
12
+ modified_on: string;
13
+ };
14
+ preview: {
15
+ active: boolean;
16
+ url: string;
17
+ };
18
+ resources: {
19
+ script: string;
20
+ bindings: unknown[];
21
+ };
22
+ }[];
23
+ };
24
+
25
+ export const mswSuccessDeployments = [
26
+ rest.get(
27
+ "*/accounts/:accountId/workers/versions/by-script/:scriptTag",
28
+ (_, response, context) =>
29
+ response.once(
30
+ context.status(200),
31
+ context.json({
32
+ success: true,
33
+ errors: [],
34
+ messages: [],
35
+ result: {
36
+ versions: [
37
+ {
38
+ version_id: "Galaxy-Class",
39
+ version_number: "1701-E",
40
+ metadata: {
41
+ author_id: "Picard-Gamma-6-0-7-3",
42
+ author_email: "Jean-Luc-Picard@federation.org",
43
+ source: "wrangler",
44
+ created_on: "2021-01-01T00:00:00.000000Z",
45
+ modified_on: "2021-01-01T00:00:00.000000Z",
46
+ },
47
+ preview: {
48
+ active: true,
49
+ url: "https://example.com",
50
+ },
51
+ resources: {
52
+ script: "script.js",
53
+ bindings: [],
54
+ },
55
+ },
56
+ {
57
+ version_id: "Intrepid-Class",
58
+ version_number: "NCC-74656",
59
+ metadata: {
60
+ author_id: "Kathryn-Jane-Gamma-6-0-7-3",
61
+ author_email: "Kathryn-Janeway@federation.org",
62
+ source: "wrangler",
63
+ created_on: "2021-02-02T00:00:00.000000Z",
64
+ modified_on: "2021-02-02T00:00:00.000000Z",
65
+ },
66
+ preview: {
67
+ active: true,
68
+ url: "https://example.com",
69
+ },
70
+ resources: {
71
+ script: "script.js",
72
+ bindings: [],
73
+ },
74
+ },
75
+ ],
76
+ },
77
+ })
78
+ )
79
+ ),
80
+ ];
@@ -1,8 +1,9 @@
1
1
  import { setupServer } from "msw/node";
2
+ import { mswSuccessDeployments } from "./handlers/deployments";
2
3
  import { mswSuccessNamespacesHandlers } from "./handlers/namespaces";
3
4
  import { mswSuccessOauthHandlers } from "./handlers/oauth";
4
5
  import { mswSuccessR2handlers } from "./handlers/r2";
5
- import { default as mswScriptHandlers } from "./handlers/script";
6
+ import { default as mswSucessScriptHandlers } from "./handlers/script";
6
7
  import { mswSuccessUserHandlers } from "./handlers/user";
7
8
  export const msw = setupServer();
8
9
 
@@ -11,5 +12,6 @@ export {
11
12
  mswSuccessR2handlers,
12
13
  mswSuccessOauthHandlers,
13
14
  mswSuccessNamespacesHandlers,
14
- mswScriptHandlers,
15
+ mswSucessScriptHandlers,
16
+ mswSuccessDeployments,
15
17
  };
@@ -34,7 +34,7 @@ describe("wrangler", () => {
34
34
  wrangler init [name] 📥 Create a wrangler.toml configuration file
35
35
  wrangler dev [script] 👂 Start a local server for developing your worker
36
36
  wrangler publish [script] 🆙 Publish your Worker to Cloudflare.
37
- wrangler delete [script] 🗑 Delete your Worker from Cloudflare.
37
+ wrangler delete [script] 🗑 Delete your Worker from Cloudflare.
38
38
  wrangler tail [worker] 🦚 Starts a log tailing session for a published Worker.
39
39
  wrangler secret 🤫 Generate a secret that can be referenced in a Worker
40
40
  wrangler secret:bulk <json> 🗄️ Bulk upload secrets for a Worker
@@ -49,6 +49,7 @@ describe("wrangler", () => {
49
49
  wrangler login 🔓 Login to Cloudflare
50
50
  wrangler logout 🚪 Logout from Cloudflare
51
51
  wrangler whoami 🕵️ Retrieve your user info and test your auth config
52
+ wrangler types 📝 Generate types from bindings & module rules in config
52
53
 
53
54
  Flags:
54
55
  -c, --config Path to .toml configuration file [string]
@@ -76,7 +77,7 @@ describe("wrangler", () => {
76
77
  wrangler init [name] 📥 Create a wrangler.toml configuration file
77
78
  wrangler dev [script] 👂 Start a local server for developing your worker
78
79
  wrangler publish [script] 🆙 Publish your Worker to Cloudflare.
79
- wrangler delete [script] 🗑 Delete your Worker from Cloudflare.
80
+ wrangler delete [script] 🗑 Delete your Worker from Cloudflare.
80
81
  wrangler tail [worker] 🦚 Starts a log tailing session for a published Worker.
81
82
  wrangler secret 🤫 Generate a secret that can be referenced in a Worker
82
83
  wrangler secret:bulk <json> 🗄️ Bulk upload secrets for a Worker
@@ -91,6 +92,7 @@ describe("wrangler", () => {
91
92
  wrangler login 🔓 Login to Cloudflare
92
93
  wrangler logout 🚪 Logout from Cloudflare
93
94
  wrangler whoami 🕵️ Retrieve your user info and test your auth config
95
+ wrangler types 📝 Generate types from bindings & module rules in config
94
96
 
95
97
  Flags:
96
98
  -c, --config Path to .toml configuration file [string]
@@ -1306,7 +1306,7 @@ describe("init", () => {
1306
1306
  contents: {
1307
1307
  config: {
1308
1308
  compilerOptions: expect.objectContaining({
1309
- types: ["@cloudflare/workers-types"],
1309
+ types: ["@cloudflare/workers-types", "jest"],
1310
1310
  }),
1311
1311
  },
1312
1312
  error: undefined,
@@ -1905,6 +1905,116 @@ and that at least one include rule is provided.
1905
1905
  `);
1906
1906
  });
1907
1907
 
1908
+ it("should avoid uploading some files", async () => {
1909
+ mkdirSync("some_dir/node_modules", { recursive: true });
1910
+ mkdirSync("some_dir/functions", { recursive: true });
1911
+
1912
+ writeFileSync("logo.png", "foobar");
1913
+ writeFileSync("some_dir/functions/foo.js", "func");
1914
+ writeFileSync("some_dir/_headers", "headersfile");
1915
+
1916
+ writeFileSync("_headers", "headersfile");
1917
+ writeFileSync("_redirects", "redirectsfile");
1918
+ writeFileSync("_worker.js", "workerfile");
1919
+ writeFileSync("_routes.json", "routesfile");
1920
+ mkdirSync(".git");
1921
+ writeFileSync(".git/foo", "gitfile");
1922
+ writeFileSync("some_dir/node_modules/some_package", "nodefile");
1923
+ mkdirSync("functions");
1924
+ writeFileSync("functions/foo.js", "func");
1925
+
1926
+ setMockResponse(
1927
+ "/pages/assets/check-missing",
1928
+ "POST",
1929
+ async (_, init) => {
1930
+ const body = JSON.parse(init.body as string) as { hashes: string[] };
1931
+ assertLater(() => {
1932
+ expect(init.headers).toMatchObject({
1933
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
1934
+ });
1935
+ expect(body).toMatchObject({
1936
+ hashes: [
1937
+ "2082190357cfd3617ccfe04f340c6247",
1938
+ "95dedb64e6d4940fc2e0f11f711cc2f4",
1939
+ "09a79777abda8ccc8bdd51dd3ff8e9e9",
1940
+ ],
1941
+ });
1942
+ });
1943
+ return body.hashes;
1944
+ }
1945
+ );
1946
+
1947
+ // Accumulate multiple requests then assert afterwards
1948
+ const requests: RequestInit[] = [];
1949
+ setMockRawResponse("/pages/assets/upload", "POST", async (_, init) => {
1950
+ requests.push(init);
1951
+
1952
+ return createFetchResult(null, true);
1953
+ });
1954
+
1955
+ assertLater(() => {
1956
+ expect(requests.length).toBe(3);
1957
+
1958
+ expect(requests[0].headers).toMatchObject({
1959
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
1960
+ });
1961
+
1962
+ let body = JSON.parse(
1963
+ requests[0].body as string
1964
+ ) as UploadPayloadFile[];
1965
+ expect(body).toMatchObject([
1966
+ {
1967
+ key: "95dedb64e6d4940fc2e0f11f711cc2f4",
1968
+ value: Buffer.from("headersfile").toString("base64"),
1969
+ metadata: {
1970
+ contentType: "application/octet-stream",
1971
+ },
1972
+ base64: true,
1973
+ },
1974
+ ]);
1975
+
1976
+ expect(requests[1].headers).toMatchObject({
1977
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
1978
+ });
1979
+
1980
+ body = JSON.parse(requests[1].body as string) as UploadPayloadFile[];
1981
+ expect(body).toMatchObject([
1982
+ {
1983
+ key: "2082190357cfd3617ccfe04f340c6247",
1984
+ value: Buffer.from("foobar").toString("base64"),
1985
+ metadata: {
1986
+ contentType: "image/png",
1987
+ },
1988
+ base64: true,
1989
+ },
1990
+ ]);
1991
+
1992
+ expect(requests[2].headers).toMatchObject({
1993
+ Authorization: "Bearer <<funfetti-auth-jwt>>",
1994
+ });
1995
+
1996
+ body = JSON.parse(requests[2].body as string) as UploadPayloadFile[];
1997
+ expect(body).toMatchObject([
1998
+ {
1999
+ key: "09a79777abda8ccc8bdd51dd3ff8e9e9",
2000
+ value: Buffer.from("func").toString("base64"),
2001
+ metadata: {
2002
+ contentType: "application/javascript",
2003
+ },
2004
+ base64: true,
2005
+ },
2006
+ ]);
2007
+ });
2008
+
2009
+ await runWrangler("pages project upload .");
2010
+
2011
+ expect(std.out).toMatchInlineSnapshot(`
2012
+ "✨ Success! Uploaded 3 files (TIMINGS)
2013
+
2014
+ ✨ Upload complete!"
2015
+ `);
2016
+ });
2017
+
1908
2018
  it("should retry uploads", async () => {
1909
2019
  writeFileSync("logo.txt", "foobar");
1910
2020
 
@@ -6,7 +6,7 @@ import { mockConsoleMethods } from "./helpers/mock-console";
6
6
  import { mockGetZoneFromHostRequest } from "./helpers/mock-get-zone-from-host";
7
7
  import { useMockIsTTY } from "./helpers/mock-istty";
8
8
  import { mockCollectKnownRoutesRequest } from "./helpers/mock-known-routes";
9
- import { msw, mswScriptHandlers } from "./helpers/msw";
9
+ import { msw, mswSucessScriptHandlers } from "./helpers/msw";
10
10
  import { runInTempDir } from "./helpers/run-in-tmp";
11
11
  import { runWrangler } from "./helpers/run-wrangler";
12
12
  import type {
@@ -18,7 +18,7 @@ import type {
18
18
  import type { RequestInit } from "undici";
19
19
  import type WebSocket from "ws";
20
20
  describe("tail", () => {
21
- beforeEach(() => msw.use(...mswScriptHandlers));
21
+ beforeEach(() => msw.use(...mswSucessScriptHandlers));
22
22
  runInTempDir();
23
23
  mockAccountId();
24
24
  mockApiToken();