wrangler 2.0.16 → 2.0.19

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 (51) hide show
  1. package/bin/wrangler.js +42 -8
  2. package/miniflare-dist/index.mjs +1 -0
  3. package/package.json +65 -63
  4. package/src/__tests__/configuration.test.ts +11 -7
  5. package/src/__tests__/helpers/run-in-tmp.ts +22 -23
  6. package/src/__tests__/https-options.test.ts +51 -18
  7. package/src/__tests__/index.test.ts +13 -13
  8. package/src/__tests__/jest.setup.ts +13 -0
  9. package/src/__tests__/kv.test.ts +33 -2
  10. package/src/__tests__/metrics.test.ts +415 -0
  11. package/src/__tests__/pages.test.ts +15 -8
  12. package/src/__tests__/publish.test.ts +49 -23
  13. package/src/__tests__/secret.test.ts +84 -78
  14. package/src/__tests__/tail.test.ts +8 -0
  15. package/src/__tests__/test-old-node-version.js +31 -0
  16. package/src/__tests__/user.test.ts +7 -7
  17. package/src/__tests__/whoami.test.tsx +11 -1
  18. package/src/api/dev.ts +4 -1
  19. package/src/cli.ts +1 -1
  20. package/src/config/config.ts +8 -0
  21. package/src/config/validation.ts +9 -0
  22. package/src/config-cache.ts +2 -1
  23. package/src/dev/local.tsx +31 -25
  24. package/src/dev/remote.tsx +2 -2
  25. package/src/dev.tsx +6 -0
  26. package/src/entry.ts +1 -1
  27. package/src/{__tests__/helpers/faye-websocket.d.ts → faye-websocket.d.ts} +0 -0
  28. package/src/global-wrangler-config-path.ts +26 -0
  29. package/src/https-options.ts +8 -4
  30. package/src/index.tsx +120 -22
  31. package/src/kv.ts +23 -1
  32. package/src/metrics/index.ts +4 -0
  33. package/src/metrics/metrics-config.ts +222 -0
  34. package/src/metrics/metrics-dispatcher.ts +93 -0
  35. package/src/metrics/send-event.ts +80 -0
  36. package/src/miniflare-cli/index.ts +1 -0
  37. package/src/package-manager.ts +45 -0
  38. package/src/pages/build.tsx +2 -0
  39. package/src/pages/deployments.tsx +2 -0
  40. package/src/pages/dev.tsx +4 -0
  41. package/src/pages/projects.tsx +3 -0
  42. package/src/pages/publish.tsx +3 -0
  43. package/src/pages/upload.tsx +26 -14
  44. package/src/publish.ts +28 -15
  45. package/src/pubsub/pubsub-commands.tsx +43 -0
  46. package/src/user/user.tsx +29 -20
  47. package/src/worker-namespace.ts +16 -0
  48. package/templates/static-asset-facade.js +11 -5
  49. package/templates/tsconfig.json +2 -2
  50. package/wrangler-dist/cli.d.ts +298 -0
  51. package/wrangler-dist/cli.js +2589 -1659
package/bin/wrangler.js CHANGED
@@ -3,9 +3,12 @@ const { spawn } = require("child_process");
3
3
  const path = require("path");
4
4
  const fs = require("fs");
5
5
  const os = require("os");
6
- const semiver = require("semiver");
7
6
 
8
7
  const MIN_NODE_VERSION = "16.7.0";
8
+ const debug =
9
+ process.env["WRANGLER_LOG"] === "debug"
10
+ ? (...args) => console.log(...args)
11
+ : () => {};
9
12
 
10
13
  let wranglerProcess;
11
14
 
@@ -17,8 +20,8 @@ function runWrangler() {
17
20
  // Note Volta and nvm are also recommended in the official docs:
18
21
  // https://developers.cloudflare.com/workers/get-started/guide#2-install-the-workers-cli
19
22
  console.error(
20
- `Wrangler requires at least Node.js v${MIN_NODE_VERSION}. You are using v${process.versions.node}.
21
- You should use the latest Node.js version if possible, as Cloudflare Workers use a very up-to-date version of V8.
23
+ `Wrangler requires at least node.js v${MIN_NODE_VERSION}. You are using v${process.versions.node}. Please update your version of node.js.
24
+
22
25
  Consider using a Node.js version manager such as https://volta.sh/ or https://github.com/nvm-sh/nvm.`
23
26
  );
24
27
  process.exitCode = 1;
@@ -84,9 +87,8 @@ function runDelegatedWrangler() {
84
87
  } = JSON.parse(fs.readFileSync(packageJsonPath));
85
88
  const resolvedBinaryPath = path.resolve(packageJsonPath, "..", binaryPath);
86
89
 
87
- console.log(
88
- `Delegating to locally-installed version of wrangler @ v${version}`
89
- );
90
+ debug(`Delegating to locally-installed version of wrangler @ v${version}`);
91
+
90
92
  // this call to `spawn` is simpler because the delegated version will do all
91
93
  // of the other work.
92
94
  return spawn(
@@ -125,10 +127,42 @@ async function main() {
125
127
  }
126
128
 
127
129
  process.on("SIGINT", () => {
128
- wranglerProcess?.kill();
130
+ wranglerProcess && wranglerProcess.kill();
129
131
  });
130
132
  process.on("SIGTERM", () => {
131
- wranglerProcess?.kill();
133
+ wranglerProcess && wranglerProcess.kill();
132
134
  });
133
135
 
136
+ // semiver implementation via https://github.com/lukeed/semiver/blob/ae7eebe6053c96be63032b14fb0b68e2553fcac4/src/index.js
137
+
138
+ /**
139
+ MIT License
140
+
141
+ Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.com)
142
+
143
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
144
+
145
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
146
+
147
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
148
+
149
+ */
150
+
151
+ var fn = new Intl.Collator(0, { numeric: 1 }).compare;
152
+
153
+ function semiver(a, b, bool) {
154
+ a = a.split(".");
155
+ b = b.split(".");
156
+
157
+ return (
158
+ fn(a[0], b[0]) ||
159
+ fn(a[1], b[1]) ||
160
+ ((b[2] = b.slice(2).join(".")),
161
+ (bool = /[.-]/.test((a[2] = a.slice(2).join(".")))),
162
+ bool == /[.-]/.test(b[2]) ? fn(a[2], b[2]) : bool ? -1 : 1)
163
+ );
164
+ }
165
+
166
+ // end semiver implementation
167
+
134
168
  void main();
@@ -4871,6 +4871,7 @@ async function main() {
4871
4871
  try {
4872
4872
  await mf.startServer();
4873
4873
  await mf.startScheduler();
4874
+ process.send && process.send("ready");
4874
4875
  } catch (e) {
4875
4876
  mf.log.error(e);
4876
4877
  process.exitCode = 1;
package/package.json CHANGED
@@ -1,18 +1,7 @@
1
1
  {
2
2
  "name": "wrangler",
3
- "version": "2.0.16",
4
- "author": "wrangler@cloudflare.com",
3
+ "version": "2.0.19",
5
4
  "description": "Command-line interface for all things Cloudflare Workers",
6
- "bin": {
7
- "wrangler": "./bin/wrangler.js",
8
- "wrangler2": "./bin/wrangler.js"
9
- },
10
- "main": "wrangler-dist/cli.js",
11
- "license": "MIT OR Apache-2.0",
12
- "bugs": {
13
- "url": "https://github.com/cloudflare/wrangler2/issues"
14
- },
15
- "homepage": "https://github.com/cloudflare/wrangler2#readme",
16
5
  "keywords": [
17
6
  "wrangler",
18
7
  "cloudflare",
@@ -29,31 +18,86 @@
29
18
  "webassembly",
30
19
  "rust",
31
20
  "emscripten",
32
- "rust",
33
21
  "typescript",
34
22
  "graphql",
35
23
  "router",
36
24
  "http",
37
25
  "cli"
38
26
  ],
27
+ "homepage": "https://github.com/cloudflare/wrangler2#readme",
28
+ "bugs": {
29
+ "url": "https://github.com/cloudflare/wrangler2/issues"
30
+ },
31
+ "license": "MIT OR Apache-2.0",
32
+ "author": "wrangler@cloudflare.com",
33
+ "main": "wrangler-dist/cli.js",
34
+ "types": "wrangler-dist/cli.d.ts",
35
+ "bin": {
36
+ "wrangler": "./bin/wrangler.js",
37
+ "wrangler2": "./bin/wrangler.js"
38
+ },
39
+ "files": [
40
+ "src",
41
+ "bin",
42
+ "miniflare-config-stubs",
43
+ "miniflare-dist",
44
+ "wrangler-dist",
45
+ "templates",
46
+ "vendor",
47
+ "import_meta_url.js",
48
+ "kv-asset-handler.js",
49
+ "Cloudflare_CA.pem"
50
+ ],
51
+ "scripts": {
52
+ "build": "npm run clean && npm run bundle && npm run emit-types",
53
+ "bundle": "node -r esbuild-register scripts/bundle.ts",
54
+ "check:type": "tsc",
55
+ "clean": "rm -rf wrangler-dist miniflare-dist emitted-types",
56
+ "dev": "npm run clean && concurrently -c black,blue 'npm run bundle -- --watch' 'npm run check:type -- --watch --preserveWatchOutput'",
57
+ "emit-types": "tsc -p tsconfig.emit.json && node -r esbuild-register scripts/emit-types.ts",
58
+ "prepublishOnly": "SOURCEMAPS=false npm run build",
59
+ "start": "npm run bundle && NODE_OPTIONS=--enable-source-maps ./bin/wrangler.js",
60
+ "test": "jest --silent=false --verbose=true",
61
+ "test-watch": "npm run test -- --runInBand --testTimeout=50000 --watch"
62
+ },
63
+ "jest": {
64
+ "moduleNameMapper": {
65
+ "clipboardy": "<rootDir>/src/__tests__/helpers/clipboardy-mock.js",
66
+ "miniflare/cli": "<rootDir>/../../node_modules/miniflare/dist/src/cli.js"
67
+ },
68
+ "restoreMocks": true,
69
+ "setupFilesAfterEnv": [
70
+ "<rootDir>/src/__tests__/jest.setup.ts"
71
+ ],
72
+ "testRegex": ".*.(test|spec)\\.[jt]sx?$",
73
+ "testTimeout": 30000,
74
+ "transform": {
75
+ "^.+\\.c?(t|j)sx?$": [
76
+ "esbuild-jest",
77
+ {
78
+ "sourcemap": true
79
+ }
80
+ ]
81
+ },
82
+ "transformIgnorePatterns": [
83
+ "node_modules/(?!find-up|locate-path|p-locate|p-limit|p-timeout|p-queue|yocto-queue|path-exists|execa|strip-final-newline|npm-run-path|path-key|onetime|mimic-fn|human-signals|is-stream|get-port|supports-color|pretty-bytes)"
84
+ ]
85
+ },
39
86
  "dependencies": {
40
87
  "@cloudflare/kv-asset-handler": "^0.2.0",
41
88
  "@esbuild-plugins/node-globals-polyfill": "^0.1.1",
42
89
  "@esbuild-plugins/node-modules-polyfill": "^0.1.4",
43
90
  "blake3-wasm": "^2.1.5",
44
91
  "esbuild": "0.14.47",
45
- "miniflare": "^2.5.1",
92
+ "miniflare": "^2.6.0",
46
93
  "nanoid": "^3.3.3",
47
94
  "path-to-regexp": "^6.2.0",
48
95
  "selfsigned": "^2.0.1",
49
- "semiver": "^1.1.0",
50
96
  "xxhash-wasm": "^1.0.1"
51
97
  },
52
- "optionalDependencies": {
53
- "fsevents": "~2.3.2"
54
- },
55
98
  "devDependencies": {
56
99
  "@iarna/toml": "^3.0.0",
100
+ "@microsoft/api-extractor": "^7.28.3",
57
101
  "@types/command-exists": "^1.2.0",
58
102
  "@types/glob-to-regexp": "0.4.1",
59
103
  "@types/mime": "^2.0.3",
@@ -104,55 +148,13 @@
104
148
  "undici": "^5.5.1",
105
149
  "update-check": "^1.5.4",
106
150
  "ws": "^8.5.0",
151
+ "xdg-app-paths": "^7.3.0",
107
152
  "yargs": "^17.4.1"
108
153
  },
109
- "files": [
110
- "src",
111
- "bin",
112
- "miniflare-config-stubs",
113
- "miniflare-dist",
114
- "wrangler-dist",
115
- "templates",
116
- "vendor",
117
- "import_meta_url.js",
118
- "kv-asset-handler.js",
119
- "Cloudflare_CA.pem"
120
- ],
121
- "scripts": {
122
- "clean": "rm -rf wrangler-dist miniflare-dist",
123
- "check:type": "tsc",
124
- "bundle": "node -r esbuild-register scripts/bundle.ts",
125
- "build": "npm run clean && npm run bundle",
126
- "dev": "npm run clean && concurrently -c black,blue 'npm run bundle -- --watch' 'npm run check:type -- --watch --preserveWatchOutput'",
127
- "prepublishOnly": "SOURCEMAPS=false npm run build",
128
- "start": "npm run bundle && NODE_OPTIONS=--enable-source-maps ./bin/wrangler.js",
129
- "test": "jest --silent=false --verbose=true",
130
- "test-watch": "npm run test -- --runInBand --testTimeout=50000 --watch"
154
+ "optionalDependencies": {
155
+ "fsevents": "~2.3.2"
131
156
  },
132
157
  "engines": {
133
158
  "node": ">=16.7.0"
134
- },
135
- "jest": {
136
- "restoreMocks": true,
137
- "testTimeout": 30000,
138
- "testRegex": ".*.(test|spec)\\.[jt]sx?$",
139
- "transformIgnorePatterns": [
140
- "node_modules/(?!find-up|locate-path|p-locate|p-limit|p-timeout|p-queue|yocto-queue|path-exists|execa|strip-final-newline|npm-run-path|path-key|onetime|mimic-fn|human-signals|is-stream|get-port|supports-color|pretty-bytes)"
141
- ],
142
- "moduleNameMapper": {
143
- "clipboardy": "<rootDir>/src/__tests__/helpers/clipboardy-mock.js",
144
- "miniflare/cli": "<rootDir>/../../node_modules/miniflare/dist/src/cli.js"
145
- },
146
- "transform": {
147
- "^.+\\.c?(t|j)sx?$": [
148
- "esbuild-jest",
149
- {
150
- "sourcemap": true
151
- }
152
- ]
153
- },
154
- "setupFilesAfterEnv": [
155
- "<rootDir>/src/__tests__/jest.setup.ts"
156
- ]
157
159
  }
158
160
  }
@@ -40,6 +40,7 @@ describe("normalizeAndValidateConfig()", () => {
40
40
  tsconfig: undefined,
41
41
  kv_namespaces: [],
42
42
  legacy_env: true,
43
+ send_metrics: undefined,
43
44
  main: undefined,
44
45
  migrations: [],
45
46
  name: undefined,
@@ -76,6 +77,7 @@ describe("normalizeAndValidateConfig()", () => {
76
77
  it("should override config defaults with provided values", () => {
77
78
  const expectedConfig: Partial<ConfigFields<RawDevConfig>> = {
78
79
  legacy_env: true,
80
+ send_metrics: false,
79
81
  dev: {
80
82
  ip: "255.255.255.255",
81
83
  port: 9999,
@@ -98,6 +100,7 @@ describe("normalizeAndValidateConfig()", () => {
98
100
  it("should error on invalid top level fields", () => {
99
101
  const expectedConfig = {
100
102
  legacy_env: "FOO",
103
+ send_metrics: "BAD",
101
104
  dev: {
102
105
  ip: 222,
103
106
  port: "FOO",
@@ -117,13 +120,14 @@ describe("normalizeAndValidateConfig()", () => {
117
120
  );
118
121
  expect(diagnostics.hasWarnings()).toBe(false);
119
122
  expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
120
- "Processing wrangler configuration:
121
- - Expected \\"legacy_env\\" to be of type boolean but got \\"FOO\\".
122
- - Expected \\"dev.ip\\" to be of type string but got 222.
123
- - Expected \\"dev.port\\" to be of type number but got \\"FOO\\".
124
- - Expected \\"dev.local_protocol\\" field to be one of [\\"http\\",\\"https\\"] but got \\"wss\\".
125
- - Expected \\"dev.upstream_protocol\\" field to be one of [\\"http\\",\\"https\\"] but got \\"ws\\"."
126
- `);
123
+ "Processing wrangler configuration:
124
+ - Expected \\"legacy_env\\" to be of type boolean but got \\"FOO\\".
125
+ - Expected \\"send_metrics\\" to be of type boolean but got \\"BAD\\".
126
+ - Expected \\"dev.ip\\" to be of type string but got 222.
127
+ - Expected \\"dev.port\\" to be of type number but got \\"FOO\\".
128
+ - Expected \\"dev.local_protocol\\" field to be one of [\\"http\\",\\"https\\"] but got \\"wss\\".
129
+ - Expected \\"dev.upstream_protocol\\" field to be one of [\\"http\\",\\"https\\"] but got \\"ws\\"."
130
+ `);
127
131
  });
128
132
 
129
133
  it("should warn on and remove unexpected top level fields", () => {
@@ -5,35 +5,34 @@ import { reinitialiseAuthTokens } from "../../user";
5
5
 
6
6
  const originalCwd = process.cwd();
7
7
 
8
- export function runInTempDir({ homedir }: { homedir?: string } = {}) {
8
+ export function runInTempDir({ homedir } = { homedir: "./home" }) {
9
9
  let tmpDir: string;
10
10
 
11
- beforeAll(() => {
12
- if (tmpDir !== undefined) {
13
- process.chdir(originalCwd);
14
- fs.rmSync(tmpDir, { recursive: true });
15
- }
16
- });
17
-
18
11
  beforeEach(() => {
19
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "wrangler-tests"));
12
+ // Use realpath because the temporary path can point to a symlink rather than the actual path.
13
+ tmpDir = fs.realpathSync(
14
+ fs.mkdtempSync(path.join(os.tmpdir(), "wrangler-tests"))
15
+ );
16
+
20
17
  process.chdir(tmpDir);
21
- if (homedir !== undefined) {
22
- // Override where the home directory is so that we can write our own user config,
23
- // without destroying the real thing.
24
- fs.mkdirSync(homedir);
25
- // Note it is very important that we use the "default" value from "node:os" (e.g. `import os from "node:os";`)
26
- // rather than an alias to the module (e.g. `import * as os from "node:os";`).
27
- // This is because the module gets transpiled so that the "method" `homedir()` gets converted to a
28
- // getter that is not configurable (and so cannot be spied upon).
29
- jest.spyOn(os, "homedir").mockReturnValue(homedir);
30
- // Now that we have changed the home directory location, we must reinitialize the user auth state
31
- reinitialiseAuthTokens();
32
- }
18
+ // The path that is returned from `homedir()` should be absolute.
19
+ const absHomedir = path.resolve(tmpDir, homedir);
20
+ // Override where the home directory is so that we can write our own user config,
21
+ // without destroying the real thing.
22
+ fs.mkdirSync(absHomedir, { recursive: true });
23
+ // Note it is very important that we use the "default" value from "node:os" (e.g. `import os from "node:os";`)
24
+ // rather than an alias to the module (e.g. `import * as os from "node:os";`).
25
+ // This is because the module gets transpiled so that the "method" `homedir()` gets converted to a
26
+ // getter that is not configurable (and so cannot be spied upon).
27
+ jest.spyOn(os, "homedir")?.mockReturnValue(absHomedir);
28
+ // Now that we have changed the home directory location, we must reinitialize the user auth state
29
+ reinitialiseAuthTokens();
33
30
  });
34
31
 
35
32
  afterEach(() => {
36
- process.chdir(originalCwd);
37
- fs.rmSync(tmpDir, { recursive: true });
33
+ if (fs.existsSync(tmpDir)) {
34
+ process.chdir(originalCwd);
35
+ fs.rmSync(tmpDir, { recursive: true });
36
+ }
38
37
  });
39
38
  }
@@ -1,24 +1,25 @@
1
1
  import fs from "node:fs";
2
2
  import os from "node:os";
3
- import { resolve } from "node:path";
3
+ import path from "node:path";
4
+ import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
4
5
  import { getHttpsOptions } from "../https-options";
5
6
  import { mockConsoleMethods } from "./helpers/mock-console";
6
7
  import { runInTempDir } from "./helpers/run-in-tmp";
7
8
 
8
9
  describe("getHttpsOptions()", () => {
9
- runInTempDir({ homedir: "./home" });
10
+ runInTempDir();
10
11
  const std = mockConsoleMethods();
11
12
 
12
13
  it("should use cached values if they have not expired", async () => {
13
- fs.mkdirSync(resolve(os.homedir(), ".wrangler/local-cert"), {
14
+ fs.mkdirSync(path.resolve(getGlobalWranglerConfigPath(), "local-cert"), {
14
15
  recursive: true,
15
16
  });
16
17
  fs.writeFileSync(
17
- resolve(os.homedir(), ".wrangler/local-cert/key.pem"),
18
+ path.resolve(getGlobalWranglerConfigPath(), "local-cert/key.pem"),
18
19
  "PRIVATE KEY"
19
20
  );
20
21
  fs.writeFileSync(
21
- resolve(os.homedir(), ".wrangler/local-cert/cert.pem"),
22
+ path.resolve(getGlobalWranglerConfigPath(), "local-cert/cert.pem"),
22
23
  "PUBLIC KEY"
23
24
  );
24
25
  const result = await getHttpsOptions();
@@ -32,11 +33,11 @@ describe("getHttpsOptions()", () => {
32
33
  it("should generate and cache new keys if none are cached", async () => {
33
34
  const result = await getHttpsOptions();
34
35
  const key = fs.readFileSync(
35
- resolve(os.homedir(), ".wrangler/local-cert/key.pem"),
36
+ path.resolve(getGlobalWranglerConfigPath(), "local-cert/key.pem"),
36
37
  "utf8"
37
38
  );
38
39
  const cert = fs.readFileSync(
39
- resolve(os.homedir(), ".wrangler/local-cert/cert.pem"),
40
+ path.resolve(getGlobalWranglerConfigPath(), "local-cert/cert.pem"),
40
41
  "utf8"
41
42
  );
42
43
  expect(result.key).toEqual(key);
@@ -49,28 +50,28 @@ describe("getHttpsOptions()", () => {
49
50
  });
50
51
 
51
52
  it("should generate and cache new keys if cached files have expired", async () => {
52
- fs.mkdirSync(resolve(os.homedir(), ".wrangler/local-cert"), {
53
+ fs.mkdirSync(path.resolve(getGlobalWranglerConfigPath(), "local-cert"), {
53
54
  recursive: true,
54
55
  });
55
56
  const ORIGINAL_KEY = "EXPIRED PRIVATE KEY";
56
57
  const ORIGINAL_CERT = "EXPIRED PUBLIC KEY";
57
58
  fs.writeFileSync(
58
- resolve(os.homedir(), ".wrangler/local-cert/key.pem"),
59
+ path.resolve(getGlobalWranglerConfigPath(), "local-cert/key.pem"),
59
60
  ORIGINAL_KEY
60
61
  );
61
62
  fs.writeFileSync(
62
- resolve(os.homedir(), ".wrangler/local-cert/cert.pem"),
63
+ path.resolve(getGlobalWranglerConfigPath(), "local-cert/cert.pem"),
63
64
  ORIGINAL_CERT
64
65
  );
65
66
  mockStatSync(/\.pem$/, { mtimeMs: new Date(2000).valueOf() });
66
67
 
67
68
  const result = await getHttpsOptions();
68
69
  const key = fs.readFileSync(
69
- resolve(os.homedir(), ".wrangler/local-cert/key.pem"),
70
+ path.resolve(getGlobalWranglerConfigPath(), "local-cert/key.pem"),
70
71
  "utf8"
71
72
  );
72
73
  const cert = fs.readFileSync(
73
- resolve(os.homedir(), ".wrangler/local-cert/cert.pem"),
74
+ path.resolve(getGlobalWranglerConfigPath(), "local-cert/cert.pem"),
74
75
  "utf8"
75
76
  );
76
77
  expect(key).not.toEqual(ORIGINAL_KEY);
@@ -84,25 +85,57 @@ describe("getHttpsOptions()", () => {
84
85
  expect(std.err).toMatchInlineSnapshot(`""`);
85
86
  });
86
87
 
88
+ it("should warn if not able to write to the cache (legacy config path)", async () => {
89
+ fs.mkdirSync(path.join(os.homedir(), ".wrangler"));
90
+ mockWriteFileSyncThrow(/\.pem$/);
91
+ await getHttpsOptions();
92
+ expect(
93
+ fs.existsSync(
94
+ path.resolve(getGlobalWranglerConfigPath(), "local-cert/key.pem")
95
+ )
96
+ ).toBe(false);
97
+ expect(
98
+ fs.existsSync(
99
+ path.resolve(getGlobalWranglerConfigPath(), "local-cert/cert.pem")
100
+ )
101
+ ).toBe(false);
102
+ expect(std.out).toMatchInlineSnapshot(
103
+ `"Generating new self-signed certificate..."`
104
+ );
105
+ expect(std.warn).toMatchInlineSnapshot(`
106
+ "▲ [WARNING] Unable to cache generated self-signed certificate in home/.wrangler/local-cert.
107
+
108
+ ERROR: Cannot write file
109
+
110
+ "
111
+ `);
112
+ expect(std.err).toMatchInlineSnapshot(`""`);
113
+ fs.rmSync(path.join(os.homedir(), ".wrangler"), { recursive: true });
114
+ });
115
+
87
116
  it("should warn if not able to write to the cache", async () => {
88
117
  mockWriteFileSyncThrow(/\.pem$/);
89
118
  await getHttpsOptions();
90
119
  expect(
91
- fs.existsSync(resolve(os.homedir(), ".wrangler/local-cert/key.pem"))
120
+ fs.existsSync(
121
+ path.resolve(getGlobalWranglerConfigPath(), "local-cert/key.pem")
122
+ )
92
123
  ).toBe(false);
93
124
  expect(
94
- fs.existsSync(resolve(os.homedir(), ".wrangler/local-cert/cert.pem"))
125
+ fs.existsSync(
126
+ path.resolve(getGlobalWranglerConfigPath(), "local-cert/cert.pem")
127
+ )
95
128
  ).toBe(false);
96
129
  expect(std.out).toMatchInlineSnapshot(
97
130
  `"Generating new self-signed certificate..."`
98
131
  );
99
132
  expect(std.warn).toMatchInlineSnapshot(`
100
- "▲ [WARNING] Unable to cache generated self-signed certificate in home/.wrangler/local-cert.
133
+ "▲ [WARNING] Unable to cache generated self-signed certificate in test-xdg-config/local-cert.
101
134
 
102
- ERROR: Cannot write file
135
+ ERROR: Cannot write file
103
136
 
104
- "
105
- `);
137
+ "
138
+ `);
106
139
  expect(std.err).toMatchInlineSnapshot(`""`);
107
140
  });
108
141
  });
@@ -35,7 +35,7 @@ describe("wrangler", () => {
35
35
  wrangler dev [script] 👂 Start a local server for developing your worker
36
36
  wrangler publish [script] 🆙 Publish your Worker to Cloudflare.
37
37
  wrangler tail [name] 🦚 Starts a log tailing session for a published Worker.
38
- wrangler secret 🤫 Generate a secret that can be referenced in the worker script
38
+ wrangler secret 🤫 Generate a secret that can be referenced in a Worker
39
39
  wrangler kv:namespace 🗂️ Interact with your Workers KV Namespaces
40
40
  wrangler kv:key 🔑 Individually manage Workers KV key-value pairs
41
41
  wrangler kv:bulk 💪 Interact with multiple Workers KV key-value pairs at once
@@ -74,7 +74,7 @@ describe("wrangler", () => {
74
74
  wrangler dev [script] 👂 Start a local server for developing your worker
75
75
  wrangler publish [script] 🆙 Publish your Worker to Cloudflare.
76
76
  wrangler tail [name] 🦚 Starts a log tailing session for a published Worker.
77
- wrangler secret 🤫 Generate a secret that can be referenced in the worker script
77
+ wrangler secret 🤫 Generate a secret that can be referenced in a Worker
78
78
  wrangler kv:namespace 🗂️ Interact with your Workers KV Namespaces
79
79
  wrangler kv:key 🔑 Individually manage Workers KV key-value pairs
80
80
  wrangler kv:bulk 💪 Interact with multiple Workers KV key-value pairs at once
@@ -123,20 +123,20 @@ describe("wrangler", () => {
123
123
  await runWrangler("secret");
124
124
  await endEventLoop();
125
125
  expect(std.out).toMatchInlineSnapshot(`
126
- "wrangler secret
126
+ "wrangler secret
127
127
 
128
- 🤫 Generate a secret that can be referenced in the worker script
128
+ 🤫 Generate a secret that can be referenced in a Worker
129
129
 
130
- Commands:
131
- wrangler secret put <key> Create or update a secret variable for a script
132
- wrangler secret delete <key> Delete a secret variable from a script
133
- wrangler secret list List all secrets for a script
130
+ Commands:
131
+ wrangler secret put <key> Create or update a secret variable for a Worker
132
+ wrangler secret delete <key> Delete a secret variable from a Worker
133
+ wrangler secret list List all secrets for a Worker
134
134
 
135
- Flags:
136
- -c, --config Path to .toml configuration file [string]
137
- -h, --help Show help [boolean]
138
- -v, --version Show version number [boolean]"
139
- `);
135
+ Flags:
136
+ -c, --config Path to .toml configuration file [string]
137
+ -h, --help Show help [boolean]
138
+ -v, --version Show version number [boolean]"
139
+ `);
140
140
  });
141
141
 
142
142
  it("no subcommand 'kv:namespace' should display a list of available subcommands", async () => {
@@ -105,3 +105,16 @@ jest.mock("../user/generate-random-state", () => {
105
105
  generateRandomState: jest.fn().mockImplementation(() => "MOCK_STATE_PARAM"),
106
106
  };
107
107
  });
108
+
109
+ jest.mock("xdg-app-paths", () => {
110
+ return {
111
+ __esModule: true,
112
+ default: jest.fn().mockImplementation(() => {
113
+ return {
114
+ config() {
115
+ return jest.requireActual("node:path").resolve("test-xdg-config");
116
+ },
117
+ };
118
+ }),
119
+ };
120
+ });
@@ -1,5 +1,5 @@
1
1
  import { writeFileSync } from "node:fs";
2
- import { Headers } from "undici";
2
+ import { FormData, Headers } from "undici";
3
3
  import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
4
4
  import {
5
5
  setMockResponse,
@@ -330,7 +330,15 @@ describe("wrangler", () => {
330
330
  expect(accountId).toEqual("some-account-id");
331
331
  expect(namespaceId).toEqual(expectedNamespaceId);
332
332
  expect(key).toEqual(expectedKV.key);
333
- expect(body).toEqual(expectedKV.value);
333
+ if (expectedKV.metadata) {
334
+ expect(body).toBeInstanceOf(FormData);
335
+ expect((body as FormData).get("value")).toEqual(expectedKV.value);
336
+ expect((body as FormData).get("metadata")).toEqual(
337
+ JSON.stringify(expectedKV.metadata)
338
+ );
339
+ } else {
340
+ expect(body).toEqual(expectedKV.value);
341
+ }
334
342
  if (expectedKV.expiration !== undefined) {
335
343
  expect(query.get("expiration")).toEqual(
336
344
  `${expectedKV.expiration}`
@@ -488,6 +496,24 @@ describe("wrangler", () => {
488
496
  expect(requests.count).toEqual(1);
489
497
  });
490
498
 
499
+ it("should put a key with metadata", async () => {
500
+ const requests = mockKeyPutRequest("some-namespace-id", {
501
+ key: "dKey",
502
+ value: "dVal",
503
+ metadata: {
504
+ mKey: "mValue",
505
+ },
506
+ });
507
+ await runWrangler(
508
+ `kv:key put dKey dVal --namespace-id some-namespace-id --metadata {"mKey":"mValue"}`
509
+ );
510
+ expect(requests.count).toEqual(1);
511
+ expect(std.out).toMatchInlineSnapshot(
512
+ `"Writing the value \\"dVal\\" to key \\"dKey\\" on namespace some-namespace-id with metadata \\"{\\"mKey\\":\\"mValue\\"}\\"."`
513
+ );
514
+ expect(std.err).toMatchInlineSnapshot(`""`);
515
+ });
516
+
491
517
  it("should error if no key is provided", async () => {
492
518
  await expect(
493
519
  runWrangler("kv:key put")
@@ -517,6 +543,7 @@ describe("wrangler", () => {
517
543
  --preview Interact with a preview namespace [boolean]
518
544
  --ttl Time for which the entries should be visible [number]
519
545
  --expiration Time since the UNIX epoch after which the entry expires [number]
546
+ --metadata Arbitrary JSON that is associated with a key [string]
520
547
  --path Read value from the file at a given path [string]"
521
548
  `);
522
549
  expect(std.err).toMatchInlineSnapshot(`
@@ -555,6 +582,7 @@ describe("wrangler", () => {
555
582
  --preview Interact with a preview namespace [boolean]
556
583
  --ttl Time for which the entries should be visible [number]
557
584
  --expiration Time since the UNIX epoch after which the entry expires [number]
585
+ --metadata Arbitrary JSON that is associated with a key [string]
558
586
  --path Read value from the file at a given path [string]"
559
587
  `);
560
588
  expect(std.err).toMatchInlineSnapshot(`
@@ -593,6 +621,7 @@ describe("wrangler", () => {
593
621
  --preview Interact with a preview namespace [boolean]
594
622
  --ttl Time for which the entries should be visible [number]
595
623
  --expiration Time since the UNIX epoch after which the entry expires [number]
624
+ --metadata Arbitrary JSON that is associated with a key [string]
596
625
  --path Read value from the file at a given path [string]"
597
626
  `);
598
627
  expect(std.err).toMatchInlineSnapshot(`
@@ -631,6 +660,7 @@ describe("wrangler", () => {
631
660
  --preview Interact with a preview namespace [boolean]
632
661
  --ttl Time for which the entries should be visible [number]
633
662
  --expiration Time since the UNIX epoch after which the entry expires [number]
663
+ --metadata Arbitrary JSON that is associated with a key [string]
634
664
  --path Read value from the file at a given path [string]"
635
665
  `);
636
666
  expect(std.err).toMatchInlineSnapshot(`
@@ -669,6 +699,7 @@ describe("wrangler", () => {
669
699
  --preview Interact with a preview namespace [boolean]
670
700
  --ttl Time for which the entries should be visible [number]
671
701
  --expiration Time since the UNIX epoch after which the entry expires [number]
702
+ --metadata Arbitrary JSON that is associated with a key [string]
672
703
  --path Read value from the file at a given path [string]"
673
704
  `);
674
705
  expect(std.err).toMatchInlineSnapshot(`