wrangler 0.0.0-e178d6fb → 0.0.0-e1c2f5b9

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 (239) hide show
  1. package/README.md +16 -2
  2. package/bin/wrangler.js +31 -11
  3. package/miniflare-dist/index.mjs +1340 -330
  4. package/package.json +40 -13
  5. package/src/__tests__/access.test.ts +25 -0
  6. package/src/__tests__/api-dev.test.ts +238 -0
  7. package/src/__tests__/api-devregistry.test.js +64 -0
  8. package/src/__tests__/api.test.ts +80 -0
  9. package/src/__tests__/configuration.test.ts +683 -470
  10. package/src/__tests__/d1.test.ts +123 -0
  11. package/src/__tests__/delete.test.ts +263 -0
  12. package/src/__tests__/deployments.test.ts +105 -0
  13. package/src/__tests__/dev.test.tsx +453 -70
  14. package/src/__tests__/dialogs.test.tsx +40 -0
  15. package/src/__tests__/generate.test.ts +235 -0
  16. package/src/__tests__/get-host-from-url.test.ts +16 -0
  17. package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
  18. package/src/__tests__/helpers/mock-cfetch.ts +92 -26
  19. package/src/__tests__/helpers/mock-console.ts +11 -2
  20. package/src/__tests__/helpers/mock-dialogs.ts +1 -1
  21. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  22. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  23. package/src/__tests__/helpers/mock-kv.ts +1 -1
  24. package/src/__tests__/helpers/mock-oauth-flow.ts +74 -53
  25. package/src/__tests__/helpers/mock-process.ts +8 -13
  26. package/src/__tests__/helpers/msw/handlers/access.ts +13 -0
  27. package/src/__tests__/helpers/msw/handlers/deployments.ts +59 -0
  28. package/src/__tests__/helpers/msw/handlers/namespaces.ts +104 -0
  29. package/src/__tests__/helpers/msw/handlers/oauth.ts +31 -0
  30. package/src/__tests__/helpers/msw/handlers/r2.ts +80 -0
  31. package/src/__tests__/helpers/msw/handlers/script.ts +75 -0
  32. package/src/__tests__/helpers/msw/handlers/user.ts +63 -0
  33. package/src/__tests__/helpers/msw/handlers/zones.ts +22 -0
  34. package/src/__tests__/helpers/msw/index.ts +21 -0
  35. package/src/__tests__/helpers/worker-scripts/child-wrangler.toml +1 -0
  36. package/src/__tests__/helpers/worker-scripts/hello-world-worker.js +5 -0
  37. package/src/__tests__/helpers/worker-scripts/hello-world-wrangler.toml +1 -0
  38. package/src/__tests__/helpers/worker-scripts/parent-worker.js +8 -0
  39. package/src/__tests__/helpers/worker-scripts/parent-wrangler.toml +5 -0
  40. package/src/__tests__/index.test.ts +103 -97
  41. package/src/__tests__/init.test.ts +1484 -382
  42. package/src/__tests__/jest.setup.ts +88 -5
  43. package/src/__tests__/kv.test.ts +655 -540
  44. package/src/__tests__/logout.test.ts +47 -0
  45. package/src/__tests__/metrics.test.ts +95 -45
  46. package/src/__tests__/middleware.scheduled.test.ts +145 -0
  47. package/src/__tests__/middleware.test.ts +750 -0
  48. package/src/__tests__/pages-deployment-tail.test.ts +884 -0
  49. package/src/__tests__/pages.test.ts +1301 -119
  50. package/src/__tests__/paths.test.ts +39 -0
  51. package/src/__tests__/publish.test.ts +2266 -1180
  52. package/src/__tests__/pubsub.test.ts +211 -88
  53. package/src/__tests__/queues.test.ts +522 -0
  54. package/src/__tests__/r2.test.ts +363 -203
  55. package/src/__tests__/secret.test.ts +261 -59
  56. package/src/__tests__/tail.test.ts +352 -151
  57. package/src/__tests__/test-old-node-version.js +3 -3
  58. package/src/__tests__/tsconfig-sanity.ts +12 -0
  59. package/src/__tests__/tsconfig.json +8 -0
  60. package/src/__tests__/tsconfig.tsbuildinfo +1 -0
  61. package/src/__tests__/type-generation.test.ts +233 -0
  62. package/src/__tests__/user.test.ts +59 -73
  63. package/src/__tests__/utils-collectKeyValues.test.ts +47 -0
  64. package/src/__tests__/validate-dev-props.test.ts +56 -0
  65. package/src/__tests__/whoami.test.tsx +114 -66
  66. package/src/__tests__/worker-namespace.test.ts +154 -155
  67. package/src/api/dev.ts +170 -38
  68. package/src/api/index.ts +1 -0
  69. package/src/bundle-reporter.tsx +41 -2
  70. package/src/bundle.ts +668 -81
  71. package/src/cfetch/index.ts +40 -3
  72. package/src/cfetch/internal.ts +144 -32
  73. package/src/cli.ts +4 -3
  74. package/src/config/config.ts +27 -3
  75. package/src/config/environment.ts +131 -7
  76. package/src/config/index.ts +128 -8
  77. package/src/config/validation-helpers.ts +19 -6
  78. package/src/config/validation.ts +411 -14
  79. package/src/config-cache.ts +3 -2
  80. package/src/create-worker-preview.ts +31 -6
  81. package/src/create-worker-upload-form.ts +84 -22
  82. package/src/d1/backups.tsx +223 -0
  83. package/src/d1/constants.ts +2 -0
  84. package/src/d1/create.tsx +63 -0
  85. package/src/d1/delete.tsx +56 -0
  86. package/src/d1/execute.tsx +352 -0
  87. package/src/d1/formatTimeAgo.ts +14 -0
  88. package/src/d1/index.ts +101 -0
  89. package/src/d1/list.tsx +48 -0
  90. package/src/d1/migrations/apply.tsx +196 -0
  91. package/src/d1/migrations/create.tsx +77 -0
  92. package/src/d1/migrations/helpers.ts +147 -0
  93. package/src/d1/migrations/index.tsx +3 -0
  94. package/src/d1/migrations/list.tsx +79 -0
  95. package/src/d1/migrations/options.ts +18 -0
  96. package/src/d1/options.ts +22 -0
  97. package/src/d1/types.tsx +24 -0
  98. package/src/d1/utils.ts +48 -0
  99. package/src/delete.ts +108 -0
  100. package/src/deployments.ts +83 -0
  101. package/src/deprecated/index.ts +145 -0
  102. package/src/dev/dev-vars.ts +11 -8
  103. package/src/dev/dev.tsx +220 -58
  104. package/src/dev/get-local-persistence-path.tsx +31 -0
  105. package/src/dev/local.tsx +740 -181
  106. package/src/dev/remote.tsx +340 -123
  107. package/src/dev/start-server.ts +543 -0
  108. package/src/dev/use-esbuild.ts +61 -7
  109. package/src/dev/validate-dev-props.ts +31 -0
  110. package/src/dev-registry.tsx +201 -0
  111. package/src/dev.tsx +614 -258
  112. package/src/dialogs.tsx +41 -8
  113. package/src/{worker-namespace.ts → dispatch-namespace.ts} +23 -22
  114. package/src/docs/index.ts +97 -0
  115. package/src/entry.ts +3 -2
  116. package/src/environment-variables/factory.ts +88 -0
  117. package/src/environment-variables/misc-variables.ts +30 -0
  118. package/src/generate/index.ts +300 -0
  119. package/src/git-client.ts +100 -7
  120. package/src/index.tsx +249 -1560
  121. package/src/init.ts +626 -150
  122. package/src/inspect.ts +137 -32
  123. package/src/{metrics/is-ci.ts → is-ci.ts} +0 -0
  124. package/src/is-interactive.ts +4 -0
  125. package/src/jest.d.ts +4 -0
  126. package/src/{kv.ts → kv/helpers.ts} +3 -3
  127. package/src/kv/index.ts +596 -0
  128. package/src/logger.ts +27 -8
  129. package/src/metrics/index.ts +1 -0
  130. package/src/metrics/metrics-config.ts +4 -4
  131. package/src/metrics/metrics-dispatcher.ts +1 -0
  132. package/src/metrics/metrics-usage-headers.ts +24 -0
  133. package/src/metrics/send-event.ts +13 -8
  134. package/src/miniflare-cli/assets.ts +172 -465
  135. package/src/miniflare-cli/index.ts +149 -24
  136. package/src/miniflare-cli/tsconfig.json +9 -0
  137. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
  138. package/src/miniflare-cli/types.ts +11 -0
  139. package/src/module-collection.ts +12 -3
  140. package/src/pages/build.tsx +108 -42
  141. package/src/pages/constants.ts +11 -0
  142. package/src/pages/deployment-tails.tsx +284 -0
  143. package/src/pages/deployments.tsx +9 -31
  144. package/src/pages/dev.tsx +417 -108
  145. package/src/pages/errors.ts +66 -0
  146. package/src/pages/functions/buildPlugin.ts +86 -84
  147. package/src/pages/functions/buildWorker.ts +121 -106
  148. package/src/pages/functions/routes-consolidation.test.ts +250 -0
  149. package/src/pages/functions/routes-consolidation.ts +73 -0
  150. package/src/pages/functions/routes-transformation.test.ts +282 -0
  151. package/src/pages/functions/routes-transformation.ts +115 -0
  152. package/src/pages/functions/routes-validation.test.ts +403 -0
  153. package/src/pages/functions/routes-validation.ts +202 -0
  154. package/src/pages/functions/tsconfig.json +8 -0
  155. package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
  156. package/src/pages/functions.tsx +82 -0
  157. package/src/pages/hash.tsx +13 -0
  158. package/src/pages/index.tsx +73 -55
  159. package/src/pages/projects.tsx +9 -3
  160. package/src/pages/prompt-select-project.tsx +31 -0
  161. package/src/pages/publish.tsx +183 -60
  162. package/src/pages/types.ts +1 -0
  163. package/src/pages/upload.tsx +107 -56
  164. package/src/pages/utils.ts +13 -2
  165. package/src/paths.ts +49 -4
  166. package/src/proxy.ts +375 -157
  167. package/src/publish/index.ts +270 -0
  168. package/src/{publish.ts → publish/publish.ts} +431 -188
  169. package/src/pubsub/pubsub-commands.tsx +4 -3
  170. package/src/queues/cli/commands/consumer/add.ts +71 -0
  171. package/src/queues/cli/commands/consumer/index.ts +22 -0
  172. package/src/queues/cli/commands/consumer/remove.ts +38 -0
  173. package/src/queues/cli/commands/create.ts +25 -0
  174. package/src/queues/cli/commands/delete.ts +26 -0
  175. package/src/queues/cli/commands/index.ts +36 -0
  176. package/src/queues/cli/commands/list.ts +25 -0
  177. package/src/queues/client.ts +135 -0
  178. package/src/queues/utils.ts +18 -0
  179. package/src/r2/constants.ts +4 -0
  180. package/src/r2/helpers.ts +131 -0
  181. package/src/r2/index.ts +289 -0
  182. package/src/routes.ts +140 -0
  183. package/src/secret/index.ts +379 -0
  184. package/src/sites.tsx +3 -3
  185. package/src/tail/createTail.ts +379 -0
  186. package/src/tail/filters.ts +4 -6
  187. package/src/tail/index.ts +199 -284
  188. package/src/tail/printing.ts +43 -3
  189. package/src/tsconfig-sanity.ts +16 -0
  190. package/src/type-generation.ts +181 -0
  191. package/src/user/access.ts +68 -0
  192. package/src/user/auth-variables.ts +113 -0
  193. package/src/user/choose-account.tsx +1 -1
  194. package/src/user/index.ts +0 -1
  195. package/src/user/user.tsx +116 -49
  196. package/src/utils/collectKeyValues.ts +14 -0
  197. package/src/whoami.tsx +80 -2
  198. package/src/worker.ts +69 -9
  199. package/src/yargs-types.ts +24 -0
  200. package/src/zones.ts +124 -9
  201. package/templates/__tests__/pages-dev-util.test.ts +128 -0
  202. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  203. package/templates/__tests__/tsconfig.json +8 -0
  204. package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
  205. package/templates/d1-beta-facade.js +205 -0
  206. package/templates/facade.d.ts +14 -0
  207. package/templates/first-party-worker-module-facade.ts +19 -0
  208. package/templates/format-dev-errors.ts +33 -0
  209. package/templates/init-tests/test-jest-new-worker.js +23 -0
  210. package/templates/init-tests/test-vitest-new-worker.js +24 -0
  211. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  212. package/templates/middleware/common.ts +62 -0
  213. package/templates/middleware/loader-modules.ts +82 -0
  214. package/templates/middleware/loader-sw.ts +233 -0
  215. package/templates/middleware/middleware-miniflare3-json-error.ts +20 -0
  216. package/templates/middleware/middleware-pretty-error.ts +40 -0
  217. package/templates/middleware/middleware-scheduled.ts +15 -0
  218. package/templates/new-worker.js +1 -1
  219. package/templates/pages-dev-pipeline.ts +31 -0
  220. package/templates/pages-dev-util.ts +52 -0
  221. package/templates/pages-shim.ts +4 -1
  222. package/templates/pages-template-plugin.ts +21 -5
  223. package/templates/pages-template-worker.ts +37 -9
  224. package/templates/{static-asset-facade.js → serve-static-assets.ts} +23 -7
  225. package/templates/service-bindings-module-facade.js +54 -0
  226. package/templates/service-bindings-sw-facade.js +42 -0
  227. package/templates/tsconfig-sanity.ts +11 -0
  228. package/templates/tsconfig.init.json +106 -0
  229. package/templates/tsconfig.json +5 -102
  230. package/templates/tsconfig.tsbuildinfo +1 -0
  231. package/wrangler-dist/cli.d.ts +621 -42
  232. package/wrangler-dist/cli.js +144441 -102606
  233. package/src/environment-variables.ts +0 -35
  234. package/src/faye-websocket.d.ts +0 -6
  235. package/src/generate.ts +0 -33
  236. package/src/miniflare-cli/enum-keys.ts +0 -17
  237. package/src/preview.ts +0 -31
  238. package/src/r2.ts +0 -50
  239. package/src/user/env-vars.ts +0 -46
package/src/index.tsx CHANGED
@@ -1,74 +1,57 @@
1
- import path from "node:path";
2
- import { StringDecoder } from "node:string_decoder";
3
- import { setTimeout } from "node:timers/promises";
1
+ import os from "node:os";
4
2
  import TOML from "@iarna/toml";
5
3
  import chalk from "chalk";
6
- import onExit from "signal-exit";
7
4
  import supportsColor from "supports-color";
8
- import { setGlobalDispatcher, ProxyAgent } from "undici";
5
+ import { ProxyAgent, setGlobalDispatcher } from "undici";
9
6
  import makeCLI from "yargs";
10
7
  import { version as wranglerVersion } from "../package.json";
11
- import { fetchResult } from "./cfetch";
12
- import { findWranglerToml, readConfig } from "./config";
13
- import { createWorkerUploadForm } from "./create-worker-upload-form";
8
+ import { isBuildFailure } from "./bundle";
9
+ import { loadDotEnv, readConfig } from "./config";
10
+ import { d1 } from "./d1";
11
+ import { deleteHandler, deleteOptions } from "./delete";
12
+ import { deployments } from "./deployments";
13
+ import {
14
+ buildHandler,
15
+ buildOptions,
16
+ configHandler,
17
+ noOpOptions,
18
+ previewHandler,
19
+ previewOptions,
20
+ route,
21
+ routeHandler,
22
+ subdomainHandler,
23
+ subdomainOptions,
24
+ } from "./deprecated";
14
25
  import { devHandler, devOptions } from "./dev";
15
- import { confirm, prompt } from "./dialogs";
16
- import { getEntry } from "./entry";
17
- import { DeprecationError } from "./errors";
26
+ import { workerNamespaceCommands } from "./dispatch-namespace";
27
+ import { docsHandler, docsOptions } from "./docs";
18
28
  import { generateHandler, generateOptions } from "./generate";
19
- import { initOptions, initHandler } from "./init";
20
- import {
21
- getKVNamespaceId,
22
- listKVNamespaces,
23
- listKVNamespaceKeys,
24
- putKVKeyValue,
25
- putKVBulkKeyValue,
26
- deleteKVBulkKeyValue,
27
- createKVNamespace,
28
- isValidKVNamespaceBinding,
29
- getKVKeyValue,
30
- isKVKeyValue,
31
- unexpectedKVKeyValueProps,
32
- deleteKVNamespace,
33
- deleteKVKeyValue,
34
- } from "./kv";
35
- import { logger } from "./logger";
29
+ import { initHandler, initOptions } from "./init";
30
+ import { kvNamespace, kvKey, kvBulk } from "./kv";
31
+ import { logBuildFailure, logger } from "./logger";
36
32
  import * as metrics from "./metrics";
37
33
  import { pages } from "./pages";
38
- import {
39
- formatMessage,
40
- ParseError,
41
- parseJSON,
42
- readFileSync,
43
- readFileSyncToBuffer,
44
- } from "./parse";
45
- import { previewHandler, previewOptions } from "./preview";
46
- import publish from "./publish";
34
+ import { formatMessage, ParseError } from "./parse";
35
+ import { publishOptions, publishHandler } from "./publish";
47
36
  import { pubSubCommands } from "./pubsub/pubsub-commands";
48
- import { createR2Bucket, deleteR2Bucket, listR2Buckets } from "./r2";
49
- import { getAssetPaths, getSiteAssetPaths } from "./sites";
50
- import {
51
- createTail,
52
- jsonPrintLogs,
53
- prettyPrintLogs,
54
- translateCLICommandToFilterMessage,
55
- } from "./tail";
37
+ import { queues } from "./queues/cli/commands";
38
+ import { r2 } from "./r2";
39
+ import { secret, secretBulkHandler, secretBulkOptions } from "./secret";
40
+ import { tailOptions, tailHandler } from "./tail";
41
+ import { generateTypes } from "./type-generation";
56
42
  import { updateCheck } from "./update-check";
57
43
  import {
44
+ listScopes,
58
45
  login,
59
46
  logout,
60
- listScopes,
61
- validateScopeKeys,
62
47
  requireAuth,
48
+ validateScopeKeys,
63
49
  } from "./user";
64
50
  import { whoami } from "./whoami";
65
51
 
66
- import { workerNamespaceCommands } from "./worker-namespace";
67
52
  import type { Config } from "./config";
68
- import type { KeyValue } from "./kv";
69
- import type { TailCLIFilters } from "./tail";
70
- import type { RawData } from "ws";
71
- import type { CommandModule } from "yargs";
53
+ import type { CommonYargsOptions } from "./yargs-types";
54
+ import type { ArgumentsCamelCase } from "yargs";
72
55
  import type Yargs from "yargs";
73
56
 
74
57
  export type ConfigPath = string | undefined;
@@ -76,6 +59,7 @@ export type ConfigPath = string | undefined;
76
59
  const resetColor = "\x1b[0m";
77
60
  const fgGreenColor = "\x1b[32m";
78
61
  export const DEFAULT_LOCAL_PORT = 8787;
62
+ export const DEFAULT_INSPECTOR_PORT = 9229;
79
63
 
80
64
  const proxy =
81
65
  process.env.https_proxy ||
@@ -86,6 +70,9 @@ const proxy =
86
70
 
87
71
  if (proxy) {
88
72
  setGlobalDispatcher(new ProxyAgent(proxy));
73
+ logger.log(
74
+ `Proxy environment variables detected. We'll use your proxy for fetch requests.`
75
+ );
89
76
  }
90
77
 
91
78
  export function getRules(config: Config): Config["rules"] {
@@ -150,7 +137,7 @@ export function getScriptName(
150
137
  /**
151
138
  * Alternative to the getScriptName() because special Legacy cases allowed "name", and "env" together in Wrangler1
152
139
  */
153
- function getLegacyScriptName(
140
+ export function getLegacyScriptName(
154
141
  args: { name: string | undefined; env: string | undefined },
155
142
  config: Config
156
143
  ) {
@@ -159,42 +146,9 @@ function getLegacyScriptName(
159
146
  : args.name ?? config.name;
160
147
  }
161
148
 
162
- /**
163
- * Get a promise to the streamed input from stdin.
164
- *
165
- * This function can be used to grab the incoming stream of data from, say,
166
- * piping the output of another process into the wrangler process.
167
- */
168
- function readFromStdin(): Promise<string> {
169
- return new Promise((resolve, reject) => {
170
- const stdin = process.stdin;
171
- const chunks: string[] = [];
172
-
173
- // When there is data ready to be read, the `readable` event will be triggered.
174
- // In the handler for `readable` we call `read()` over and over until all the available data has been read.
175
- stdin.on("readable", () => {
176
- let chunk;
177
- while (null !== (chunk = stdin.read())) {
178
- chunks.push(chunk);
179
- }
180
- });
181
-
182
- // When the streamed data is complete the `end` event will be triggered.
183
- // In the handler for `end` we join the chunks together and resolve the promise.
184
- stdin.on("end", () => {
185
- resolve(chunks.join(""));
186
- });
187
-
188
- // If there is an `error` event then the handler will reject the promise.
189
- stdin.on("error", (err) => {
190
- reject(err);
191
- });
192
- });
193
- }
194
-
195
149
  // a helper to demand one of a set of options
196
150
  // via https://github.com/yargs/yargs/issues/1093#issuecomment-491299261
197
- function demandOneOfOption(...options: string[]) {
151
+ export function demandOneOfOption(...options: string[]) {
198
152
  return function (argv: Yargs.Arguments) {
199
153
  const count = options.filter((option) => argv[option]).length;
200
154
  const lastOption = options.pop();
@@ -217,18 +171,12 @@ function demandOneOfOption(...options: string[]) {
217
171
  };
218
172
  }
219
173
 
220
- /**
221
- * Remove trailing white space from inputs.
222
- * Matching Wrangler legacy behavior with handling inputs
223
- */
224
- function trimTrailingWhitespace(str: string) {
225
- return str.trimEnd();
226
- }
227
-
228
174
  export class CommandLineArgsError extends Error {}
229
175
 
230
- function createCLIParser(argv: string[]) {
231
- const wrangler = makeCLI(argv)
176
+ export function createCLIParser(argv: string[]) {
177
+ // Type check result against CommonYargsOptions to make sure we've included
178
+ // all common options
179
+ const wrangler: Yargs.Argv<CommonYargsOptions> = makeCLI(argv)
232
180
  .strict()
233
181
  // We handle errors ourselves in a try-catch around `yargs.parse`.
234
182
  // If you want the "help info" to be displayed then throw an instance of `CommandLineArgsError`.
@@ -243,10 +191,41 @@ function createCLIParser(argv: string[]) {
243
191
  throw error;
244
192
  })
245
193
  .scriptName("wrangler")
246
- .wrap(null);
194
+ .wrap(null)
195
+ // Define global options here, so they get included in the `Argv` type of
196
+ // the `wrangler` variable
197
+ .version(false)
198
+ .option("v", {
199
+ describe: "Show version number",
200
+ alias: "version",
201
+ type: "boolean",
202
+ })
203
+ .option("config", {
204
+ alias: "c",
205
+ describe: "Path to .toml configuration file",
206
+ type: "string",
207
+ requiresArg: true,
208
+ })
209
+ .option("env", {
210
+ alias: "e",
211
+ describe: "Environment to use for operations and .env files",
212
+ type: "string",
213
+ requiresArg: true,
214
+ })
215
+ .check((args) => {
216
+ // Grab locally specified env params from `.env` file
217
+ const loaded = loadDotEnv(".env", args.env);
218
+ for (const [key, value] of Object.entries(loaded?.parsed ?? {})) {
219
+ if (!(key in process.env)) process.env[key] = value;
220
+ }
221
+ return true;
222
+ });
223
+
224
+ wrangler.group(["config", "env", "help", "version"], "Flags:");
225
+ wrangler.help().alias("h", "help");
247
226
 
248
227
  // Default help command that supports the subcommands
249
- const subHelp: CommandModule = {
228
+ const subHelp: Yargs.CommandModule<CommonYargsOptions, CommonYargsOptions> = {
250
229
  command: ["*"],
251
230
  handler: async (args) => {
252
231
  setImmediate(() =>
@@ -285,74 +264,35 @@ function createCLIParser(argv: string[]) {
285
264
  // I wish we could enforce this pattern, but this comment will have to do for now.
286
265
  // (It's also annoying that choices[] doesn't get inferred as an enum. 🤷‍♂.)
287
266
 
288
- // [DEPRECATED] generate
267
+ // docs
289
268
  wrangler.command(
290
- // we definitely want to move away from us cloning github templates
291
- // we can do something better here, let's see
292
- "generate [name] [template]",
293
- false,
294
- generateOptions,
295
- generateHandler
269
+ "docs [command]",
270
+ "📚 Open wrangler's docs in your browser",
271
+ docsOptions,
272
+ docsHandler
296
273
  );
297
274
 
298
275
  // init
299
276
  wrangler.command(
300
277
  "init [name]",
301
- "📥 Create a wrangler.toml configuration file",
278
+ "📥 Initialize a basic Worker project, including a wrangler.toml file",
302
279
  initOptions,
303
280
  initHandler
304
281
  );
305
282
 
306
- // build
283
+ // generate
307
284
  wrangler.command(
308
- "build",
309
- false,
310
- (yargs) => {
311
- return yargs.option("env", {
312
- describe: "Perform on a specific environment",
313
- type: "string",
314
- });
315
- },
316
- async (buildArgs) => {
317
- // "[DEPRECATED] 🦀 Build your project (if applicable)",
318
-
319
- const envFlag = buildArgs.env ? ` --env=${buildArgs.env}` : "";
320
- logger.log(
321
- formatMessage({
322
- kind: "warning",
323
- text: "Deprecation: `wrangler build` has been deprecated.",
324
- notes: [
325
- {
326
- text: "Please refer to https://developers.cloudflare.com/workers/wrangler/migration/deprecations/#build for more information.",
327
- },
328
- {
329
- text: `Attempting to run \`wrangler publish --dry-run --outdir=dist${envFlag}\` for you instead:`,
330
- },
331
- ],
332
- })
333
- );
334
-
335
- await createCLIParser([
336
- "publish",
337
- "--dry-run",
338
- "--outdir=dist",
339
- ...(buildArgs.env ? ["--env", buildArgs.env] : []),
340
- ]).parse();
341
- }
285
+ "generate [name] [template]",
286
+ "✨ Generate a new Worker project from an existing Worker template. See https://github.com/cloudflare/templates",
287
+ generateOptions,
288
+ generateHandler
342
289
  );
343
290
 
344
- // config
345
- wrangler.command(
346
- "config",
347
- false,
348
- () => {},
349
- () => {
350
- // "🕵️ Authenticate Wrangler with a Cloudflare API Token",
351
- throw new DeprecationError(
352
- "`wrangler config` has been deprecated, please refer to https://developers.cloudflare.com/workers/wrangler/migration/deprecations/#config for alternatives"
353
- );
354
- }
355
- );
291
+ // [DEPRECATED] build
292
+ wrangler.command("build", false, buildOptions, buildHandler);
293
+
294
+ // [DEPRECATED] config
295
+ wrangler.command("config", false, noOpOptions, configHandler);
356
296
 
357
297
  // dev
358
298
  wrangler.command(
@@ -366,386 +306,27 @@ function createCLIParser(argv: string[]) {
366
306
  wrangler.command(
367
307
  "publish [script]",
368
308
  "🆙 Publish your Worker to Cloudflare.",
369
- (yargs) => {
370
- return (
371
- yargs
372
- .option("env", {
373
- type: "string",
374
- requiresArg: true,
375
- describe: "Perform on a specific environment",
376
- alias: "e",
377
- })
378
- .positional("script", {
379
- describe: "The path to an entry point for your worker",
380
- type: "string",
381
- requiresArg: true,
382
- })
383
- .option("name", {
384
- describe: "Name of the worker",
385
- type: "string",
386
- requiresArg: true,
387
- })
388
- // We want to have a --no-bundle flag, but yargs requires that
389
- // we also have a --bundle flag (that it adds the --no to by itself)
390
- // So we make a --bundle flag, but hide it, and then add a --no-bundle flag
391
- // that's visible to the user but doesn't "do" anything.
392
- .option("bundle", {
393
- describe: "Run wrangler's compilation step before publishing",
394
- type: "boolean",
395
- hidden: true,
396
- })
397
- .option("no-bundle", {
398
- describe: "Skip internal build steps and directly publish Worker",
399
- type: "boolean",
400
- default: false,
401
- })
402
- .option("outdir", {
403
- describe: "Output directory for the bundled worker",
404
- type: "string",
405
- requiresArg: true,
406
- })
407
- .option("format", {
408
- choices: ["modules", "service-worker"] as const,
409
- describe: "Choose an entry type",
410
- deprecated: true,
411
- })
412
- .option("compatibility-date", {
413
- describe: "Date to use for compatibility checks",
414
- type: "string",
415
- requiresArg: true,
416
- })
417
- .option("compatibility-flags", {
418
- describe: "Flags to use for compatibility checks",
419
- alias: "compatibility-flag",
420
- type: "string",
421
- requiresArg: true,
422
- array: true,
423
- })
424
- .option("latest", {
425
- describe: "Use the latest version of the worker runtime",
426
- type: "boolean",
427
- default: false,
428
- })
429
- .option("experimental-public", {
430
- describe: "Static assets to be served",
431
- type: "string",
432
- requiresArg: true,
433
- deprecated: true,
434
- hidden: true,
435
- })
436
- .option("assets", {
437
- describe: "Static assets to be served",
438
- type: "string",
439
- requiresArg: true,
440
- })
441
- .option("site", {
442
- describe: "Root folder of static assets for Workers Sites",
443
- type: "string",
444
- requiresArg: true,
445
- })
446
- .option("site-include", {
447
- describe:
448
- "Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.",
449
- type: "string",
450
- requiresArg: true,
451
- array: true,
452
- })
453
- .option("site-exclude", {
454
- describe:
455
- "Array of .gitignore-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded.",
456
- type: "string",
457
- requiresArg: true,
458
- array: true,
459
- })
460
- .option("triggers", {
461
- describe: "cron schedules to attach",
462
- alias: ["schedule", "schedules"],
463
- type: "string",
464
- requiresArg: true,
465
- array: true,
466
- })
467
- .option("routes", {
468
- describe: "Routes to upload",
469
- alias: "route",
470
- type: "string",
471
- requiresArg: true,
472
- array: true,
473
- })
474
- .option("jsx-factory", {
475
- describe: "The function that is called for each JSX element",
476
- type: "string",
477
- requiresArg: true,
478
- })
479
- .option("jsx-fragment", {
480
- describe: "The function that is called for each JSX fragment",
481
- type: "string",
482
- requiresArg: true,
483
- })
484
- .option("tsconfig", {
485
- describe: "Path to a custom tsconfig.json file",
486
- type: "string",
487
- requiresArg: true,
488
- })
489
- .option("minify", {
490
- describe: "Minify the Worker",
491
- type: "boolean",
492
- })
493
- .option("node-compat", {
494
- describe: "Enable node.js compatibility",
495
- type: "boolean",
496
- })
497
- .option("dry-run", {
498
- describe: "Don't actually publish",
499
- type: "boolean",
500
- })
501
- .option("legacy-env", {
502
- type: "boolean",
503
- describe: "Use legacy environments",
504
- hidden: true,
505
- })
506
- );
507
- },
508
- async (args) => {
509
- await printWranglerBanner();
510
-
511
- const configPath =
512
- (args.config as ConfigPath) ||
513
- (args.script && findWranglerToml(path.dirname(args.script)));
514
- const config = readConfig(configPath, args);
515
- await metrics.sendMetricsEvent("deploy worker script", {
516
- sendMetrics: config.send_metrics,
517
- });
518
- const entry = await getEntry(args, config, "publish");
519
-
520
- if (args.public) {
521
- throw new Error("The --public field has been renamed to --assets");
522
- }
523
- if (args["experimental-public"]) {
524
- throw new Error(
525
- "The --experimental-public field has been renamed to --assets"
526
- );
527
- }
528
-
529
- if ((args.assets || config.assets) && (args.site || config.site)) {
530
- throw new Error(
531
- "Cannot use Assets and Workers Sites in the same Worker."
532
- );
533
- }
534
-
535
- if (args.assets) {
536
- logger.warn(
537
- "The --assets argument is experimental and may change or break at any time"
538
- );
539
- }
540
-
541
- if (args.latest) {
542
- logger.warn(
543
- "Using the latest version of the Workers runtime. To silence this warning, please choose a specific version of the runtime with --compatibility-date, or add a compatibility_date to your wrangler.toml.\n"
544
- );
545
- }
309
+ publishOptions,
310
+ publishHandler
311
+ );
546
312
 
547
- const accountId = args.dryRun ? undefined : await requireAuth(config);
548
-
549
- const assetPaths =
550
- args.assets || config.assets
551
- ? getAssetPaths(config, args.assets)
552
- : getSiteAssetPaths(
553
- config,
554
- args.site,
555
- args.siteInclude,
556
- args.siteExclude
557
- );
558
-
559
- await publish({
560
- config,
561
- accountId,
562
- name: getScriptName(args, config),
563
- rules: getRules(config),
564
- entry,
565
- env: args.env,
566
- compatibilityDate: args.latest
567
- ? new Date().toISOString().substring(0, 10)
568
- : args["compatibility-date"],
569
- compatibilityFlags: args["compatibility-flags"],
570
- triggers: args.triggers,
571
- jsxFactory: args["jsx-factory"],
572
- jsxFragment: args["jsx-fragment"],
573
- tsconfig: args.tsconfig,
574
- routes: args.routes,
575
- assetPaths,
576
- legacyEnv: isLegacyEnv(config),
577
- minify: args.minify,
578
- nodeCompat: args.nodeCompat,
579
- isWorkersSite: Boolean(args.site || config.site),
580
- outDir: args.outdir,
581
- dryRun: args.dryRun,
582
- noBundle: !(args.bundle ?? !config.no_bundle),
583
- });
584
- }
313
+ // delete
314
+ wrangler.command(
315
+ "delete [script]",
316
+ "🗑 Delete your Worker from Cloudflare.",
317
+ deleteOptions,
318
+ deleteHandler
585
319
  );
586
320
 
587
321
  // tail
588
322
  wrangler.command(
589
- "tail [name]",
323
+ "tail [worker]",
590
324
  "🦚 Starts a log tailing session for a published Worker.",
591
- (yargs) => {
592
- return yargs
593
- .positional("name", {
594
- describe: "Name of the worker",
595
- type: "string",
596
- })
597
- .option("format", {
598
- default: process.stdout.isTTY ? "pretty" : "json",
599
- choices: ["json", "pretty"],
600
- describe: "The format of log entries",
601
- })
602
- .option("status", {
603
- choices: ["ok", "error", "canceled"],
604
- describe: "Filter by invocation status",
605
- array: true,
606
- })
607
- .option("header", {
608
- type: "string",
609
- requiresArg: true,
610
- describe: "Filter by HTTP header",
611
- })
612
- .option("method", {
613
- type: "string",
614
- requiresArg: true,
615
- describe: "Filter by HTTP method",
616
- array: true,
617
- })
618
- .option("sampling-rate", {
619
- type: "number",
620
- describe: "Adds a percentage of requests to log sampling rate",
621
- })
622
- .option("search", {
623
- type: "string",
624
- requiresArg: true,
625
- describe: "Filter by a text match in console.log messages",
626
- })
627
- .option("ip", {
628
- type: "string",
629
- requiresArg: true,
630
- describe:
631
- 'Filter by the IP address the request originates from. Use "self" to filter for your own IP',
632
- array: true,
633
- })
634
- .option("env", {
635
- type: "string",
636
- requiresArg: true,
637
- describe: "Perform on a specific environment",
638
- alias: "e",
639
- })
640
- .option("debug", {
641
- type: "boolean",
642
- hidden: true,
643
- default: false,
644
- describe:
645
- "If a log would have been filtered out, send it through anyway alongside the filter which would have blocked it.",
646
- })
647
- .option("legacy-env", {
648
- type: "boolean",
649
- describe: "Use legacy environments",
650
- hidden: true,
651
- });
652
- },
653
- async (args) => {
654
- if (args.format === "pretty") {
655
- await printWranglerBanner();
656
- }
657
- const config = readConfig(args.config as ConfigPath, args);
658
- await metrics.sendMetricsEvent("begin log stream", {
659
- sendMetrics: config.send_metrics,
660
- });
661
-
662
- const scriptName = getLegacyScriptName(args, config);
663
-
664
- if (!scriptName) {
665
- throw new Error(
666
- "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `wrangler tail <worker-name>`"
667
- );
668
- }
669
-
670
- const accountId = await requireAuth(config);
671
-
672
- const cliFilters: TailCLIFilters = {
673
- status: args.status as ("ok" | "error" | "canceled")[] | undefined,
674
- header: args.header,
675
- method: args.method,
676
- samplingRate: args["sampling-rate"],
677
- search: args.search,
678
- clientIp: args.ip,
679
- };
680
-
681
- const filters = translateCLICommandToFilterMessage(
682
- cliFilters,
683
- args.debug
684
- );
685
-
686
- const { tail, expiration, deleteTail } = await createTail(
687
- accountId,
688
- scriptName,
689
- filters,
690
- !isLegacyEnv(config) ? args.env : undefined
691
- );
692
-
693
- const scriptDisplayName = `${scriptName}${
694
- args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
695
- }`;
696
-
697
- if (args.format === "pretty") {
698
- logger.log(
699
- `Successfully created tail, expires at ${expiration.toLocaleString()}`
700
- );
701
- }
702
-
703
- onExit(async () => {
704
- tail.terminate();
705
- await deleteTail();
706
- await metrics.sendMetricsEvent("end log stream", {
707
- sendMetrics: config.send_metrics,
708
- });
709
- });
710
-
711
- const printLog: (data: RawData) => void =
712
- args.format === "pretty" ? prettyPrintLogs : jsonPrintLogs;
713
-
714
- tail.on("message", printLog);
715
-
716
- while (tail.readyState !== tail.OPEN) {
717
- switch (tail.readyState) {
718
- case tail.CONNECTING:
719
- await setTimeout(100);
720
- break;
721
- case tail.CLOSING:
722
- await setTimeout(100);
723
- break;
724
- case tail.CLOSED:
725
- await metrics.sendMetricsEvent("end log stream", {
726
- sendMetrics: config.send_metrics,
727
- });
728
- throw new Error(
729
- `Connection to ${scriptDisplayName} closed unexpectedly.`
730
- );
731
- }
732
- }
733
-
734
- if (args.format === "pretty") {
735
- logger.log(`Connected to ${scriptDisplayName}, waiting for logs...`);
736
- }
737
-
738
- tail.on("close", async () => {
739
- tail.terminate();
740
- await deleteTail();
741
- await metrics.sendMetricsEvent("end log stream", {
742
- sendMetrics: config.send_metrics,
743
- });
744
- });
745
- }
325
+ tailOptions,
326
+ tailHandler
746
327
  );
747
328
 
748
- // preview
329
+ // [DEPRECATED] preview
749
330
  wrangler.command(
750
331
  "preview [method] [body]",
751
332
  false,
@@ -759,92 +340,18 @@ function createCLIParser(argv: string[]) {
759
340
  false, // I think we want to hide this command
760
341
  // "➡️ List or delete worker routes",
761
342
  (routeYargs) => {
762
- return routeYargs
763
- .command(
764
- "list",
765
- "List the routes associated with a zone",
766
- (yargs) => {
767
- return yargs
768
- .option("env", {
769
- type: "string",
770
- requiresArg: true,
771
- describe: "Perform on a specific environment",
772
- })
773
- .option("zone", {
774
- type: "string",
775
- requiresArg: true,
776
- describe: "Zone id",
777
- })
778
- .positional("zone", {
779
- describe: "Zone id",
780
- type: "string",
781
- });
782
- },
783
- () => {
784
- // "👯 [DEPRECATED]. Use wrangler.toml to manage routes.
785
- const deprecationNotice =
786
- "`wrangler route list` has been deprecated.";
787
- const futureRoutes =
788
- "Refer to wrangler.toml for a list of routes the worker will be deployed to upon publishing.";
789
- const presentRoutes =
790
- "Refer to the Cloudflare Dashboard to see the routes this worker is currently running on.";
791
- throw new DeprecationError(
792
- `${deprecationNotice}\n${futureRoutes}\n${presentRoutes}`
793
- );
794
- }
795
- )
796
- .command(
797
- "delete [id]",
798
- "Delete a route associated with a zone",
799
- (yargs) => {
800
- return yargs
801
- .positional("id", {
802
- describe: "The hash of the route ID to delete.",
803
- type: "string",
804
- })
805
- .option("zone", {
806
- type: "string",
807
- requiresArg: true,
808
- describe: "zone id",
809
- })
810
- .option("env", {
811
- type: "string",
812
- requiresArg: true,
813
- describe: "Perform on a specific environment",
814
- });
815
- },
816
- () => {
817
- // "👯 [DEPRECATED]. Use wrangler.toml to manage routes.
818
- const deprecationNotice =
819
- "`wrangler route delete` has been deprecated.";
820
- const shouldDo =
821
- "Remove the unwanted route(s) from wrangler.toml and run `wrangler publish` to remove your worker from those routes.";
822
- throw new DeprecationError(`${deprecationNotice}\n${shouldDo}`);
823
- }
824
- );
343
+ return route(routeYargs);
825
344
  },
826
- () => {
827
- // "👯 [DEPRECATED]. Use wrangler.toml to manage routes.
828
- const deprecationNotice = "`wrangler route` has been deprecated.";
829
- const shouldDo =
830
- "Please use wrangler.toml and/or `wrangler publish --routes` to modify routes";
831
- throw new DeprecationError(`${deprecationNotice}\n${shouldDo}`);
832
- }
345
+ routeHandler
833
346
  );
834
347
 
835
- // subdomain
348
+ // [DEPRECATED] subdomain
836
349
  wrangler.command(
837
350
  "subdomain [name]",
838
351
  false,
839
352
  // "👷 Create or change your workers.dev subdomain.",
840
- (yargs) => {
841
- return yargs.positional("name", { type: "string" });
842
- },
843
- () => {
844
- throw new DeprecationError(
845
- "`wrangler subdomain` has been deprecated, please refer to https://developers.cloudflare.com/workers/wrangler/migration/deprecations/#subdomain for alternatives"
846
- );
847
- }
353
+ subdomainOptions,
354
+ subdomainHandler
848
355
  );
849
356
 
850
357
  // secret
@@ -852,974 +359,74 @@ function createCLIParser(argv: string[]) {
852
359
  "secret",
853
360
  "🤫 Generate a secret that can be referenced in a Worker",
854
361
  (secretYargs) => {
855
- return secretYargs
856
- .command(subHelp)
857
- .option("legacy-env", {
858
- type: "boolean",
859
- describe: "Use legacy environments",
860
- hidden: true,
861
- })
862
- .command(
863
- "put <key>",
864
- "Create or update a secret variable for a Worker",
865
- (yargs) => {
866
- return yargs
867
- .positional("key", {
868
- describe: "The variable name to be accessible in the Worker",
869
- type: "string",
870
- })
871
- .option("name", {
872
- describe: "Name of the Worker",
873
- type: "string",
874
- requiresArg: true,
875
- })
876
- .option("env", {
877
- type: "string",
878
- requiresArg: true,
879
- describe:
880
- "Binds the secret to the Worker of the specific environment",
881
- alias: "e",
882
- });
883
- },
884
- async (args) => {
885
- await printWranglerBanner();
886
- const config = readConfig(args.config as ConfigPath, args);
887
-
888
- const scriptName = getLegacyScriptName(args, config);
889
- if (!scriptName) {
890
- throw new Error(
891
- "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `--name <worker-name>`"
892
- );
893
- }
894
-
895
- const accountId = await requireAuth(config);
896
-
897
- const isInteractive = process.stdin.isTTY;
898
- const secretValue = trimTrailingWhitespace(
899
- isInteractive
900
- ? await prompt("Enter a secret value:", "password")
901
- : await readFromStdin()
902
- );
903
-
904
- logger.log(
905
- `🌀 Creating the secret for the Worker "${scriptName}" ${
906
- args.env && !isLegacyEnv(config) ? `(${args.env})` : ""
907
- }`
908
- );
909
-
910
- async function submitSecret() {
911
- const url =
912
- !args.env || isLegacyEnv(config)
913
- ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
914
- : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
915
-
916
- return await fetchResult(url, {
917
- method: "PUT",
918
- headers: { "Content-Type": "application/json" },
919
- body: JSON.stringify({
920
- name: args.key,
921
- text: secretValue,
922
- type: "secret_text",
923
- }),
924
- });
925
- }
926
-
927
- const createDraftWorker = async () => {
928
- // TODO: log a warning
929
- await fetchResult(
930
- !isLegacyEnv(config) && args.env
931
- ? `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}`
932
- : `/accounts/${accountId}/workers/scripts/${scriptName}`,
933
- {
934
- method: "PUT",
935
- body: createWorkerUploadForm({
936
- name: scriptName,
937
- main: {
938
- name: scriptName,
939
- content: `export default { fetch() {} }`,
940
- type: "esm",
941
- },
942
- bindings: {
943
- kv_namespaces: [],
944
- vars: {},
945
- durable_objects: { bindings: [] },
946
- r2_buckets: [],
947
- services: [],
948
- wasm_modules: {},
949
- text_blobs: {},
950
- data_blobs: {},
951
- worker_namespaces: [],
952
- unsafe: [],
953
- },
954
- modules: [],
955
- migrations: undefined,
956
- compatibility_date: undefined,
957
- compatibility_flags: undefined,
958
- usage_model: undefined,
959
- }),
960
- }
961
- );
962
- };
963
-
964
- function isMissingWorkerError(e: unknown): e is { code: 10007 } {
965
- return (
966
- typeof e === "object" &&
967
- e !== null &&
968
- (e as { code: number }).code === 10007
969
- );
970
- }
971
-
972
- try {
973
- await submitSecret();
974
- await metrics.sendMetricsEvent("create encrypted variable", {
975
- sendMetrics: config.send_metrics,
976
- });
977
- } catch (e) {
978
- if (isMissingWorkerError(e)) {
979
- // create a draft worker and try again
980
- await createDraftWorker();
981
- await submitSecret();
982
- // TODO: delete the draft worker if this failed too?
983
- } else {
984
- throw e;
985
- }
986
- }
987
-
988
- logger.log(`✨ Success! Uploaded secret ${args.key}`);
989
- }
990
- )
991
- .command(
992
- "delete <key>",
993
- "Delete a secret variable from a Worker",
994
- async (yargs) => {
995
- await printWranglerBanner();
996
- return yargs
997
- .positional("key", {
998
- describe: "The variable name to be accessible in the Worker",
999
- type: "string",
1000
- })
1001
- .option("name", {
1002
- describe: "Name of the Worker",
1003
- type: "string",
1004
- requiresArg: true,
1005
- })
1006
- .option("env", {
1007
- type: "string",
1008
- requiresArg: true,
1009
- describe:
1010
- "Binds the secret to the Worker of the specific environment",
1011
- alias: "e",
1012
- });
1013
- },
1014
- async (args) => {
1015
- const config = readConfig(args.config as ConfigPath, args);
1016
-
1017
- const scriptName = getLegacyScriptName(args, config);
1018
- if (!scriptName) {
1019
- throw new Error(
1020
- "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `--name <worker-name>`"
1021
- );
1022
- }
1023
-
1024
- const accountId = await requireAuth(config);
1025
-
1026
- if (
1027
- await confirm(
1028
- `Are you sure you want to permanently delete the secret ${
1029
- args.key
1030
- } on the Worker ${scriptName}${
1031
- args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1032
- }?`
1033
- )
1034
- ) {
1035
- logger.log(
1036
- `🌀 Deleting the secret ${
1037
- args.key
1038
- } on the Worker ${scriptName}${
1039
- args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1040
- }`
1041
- );
1042
-
1043
- const url =
1044
- !args.env || isLegacyEnv(config)
1045
- ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1046
- : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1047
-
1048
- await fetchResult(`${url}/${args.key}`, { method: "DELETE" });
1049
- await metrics.sendMetricsEvent("delete encrypted variable", {
1050
- sendMetrics: config.send_metrics,
1051
- });
1052
- logger.log(`✨ Success! Deleted secret ${args.key}`);
1053
- }
1054
- }
1055
- )
1056
- .command(
1057
- "list",
1058
- "List all secrets for a Worker",
1059
- (yargs) => {
1060
- return yargs
1061
- .option("name", {
1062
- describe: "Name of the Worker",
1063
- type: "string",
1064
- requiresArg: true,
1065
- })
1066
- .option("env", {
1067
- type: "string",
1068
- requiresArg: true,
1069
- describe:
1070
- "Binds the secret to the Worker of the specific environment.",
1071
- alias: "e",
1072
- });
1073
- },
1074
- async (args) => {
1075
- const config = readConfig(args.config as ConfigPath, args);
1076
-
1077
- const scriptName = getLegacyScriptName(args, config);
1078
- if (!scriptName) {
1079
- throw new Error(
1080
- "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `--name <worker-name>`"
1081
- );
1082
- }
1083
-
1084
- const accountId = await requireAuth(config);
1085
-
1086
- const url =
1087
- !args.env || isLegacyEnv(config)
1088
- ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1089
- : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1090
-
1091
- logger.log(JSON.stringify(await fetchResult(url), null, " "));
1092
- await metrics.sendMetricsEvent("list encrypted variables", {
1093
- sendMetrics: config.send_metrics,
1094
- });
1095
- }
1096
- );
362
+ return secret(secretYargs.command(subHelp));
1097
363
  }
1098
364
  );
1099
365
 
1100
- // kv
1101
- // :namespace
366
+ wrangler.command(
367
+ "secret:bulk <json>",
368
+ "🗄️ Bulk upload secrets for a Worker",
369
+ secretBulkOptions,
370
+ secretBulkHandler
371
+ );
372
+
373
+ // kv namespace
1102
374
  wrangler.command(
1103
375
  "kv:namespace",
1104
376
  "🗂️ Interact with your Workers KV Namespaces",
1105
377
  (namespaceYargs) => {
1106
- return namespaceYargs
1107
- .command(subHelp)
1108
- .command(
1109
- "create <namespace>",
1110
- "Create a new namespace",
1111
- (yargs) => {
1112
- return yargs
1113
- .positional("namespace", {
1114
- describe: "The name of the new namespace",
1115
- type: "string",
1116
- demandOption: true,
1117
- })
1118
- .option("env", {
1119
- type: "string",
1120
- requiresArg: true,
1121
- describe: "Perform on a specific environment",
1122
- alias: "e",
1123
- })
1124
- .option("preview", {
1125
- type: "boolean",
1126
- describe: "Interact with a preview namespace",
1127
- });
1128
- },
1129
- async (args) => {
1130
- await printWranglerBanner();
1131
-
1132
- if (!isValidKVNamespaceBinding(args.namespace)) {
1133
- throw new CommandLineArgsError(
1134
- `The namespace binding name "${args.namespace}" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number.`
1135
- );
1136
- }
1137
-
1138
- const config = readConfig(args.config as ConfigPath, args);
1139
- if (!config.name) {
1140
- logger.warn(
1141
- "No configured name present, using `worker` as a prefix for the title"
1142
- );
1143
- }
1144
-
1145
- const name = config.name || "worker";
1146
- const environment = args.env ? `-${args.env}` : "";
1147
- const preview = args.preview ? "_preview" : "";
1148
- const title = `${name}${environment}-${args.namespace}${preview}`;
1149
-
1150
- const accountId = await requireAuth(config);
1151
-
1152
- // TODO: generate a binding name stripping non alphanumeric chars
1153
-
1154
- logger.log(`🌀 Creating namespace with title "${title}"`);
1155
- const namespaceId = await createKVNamespace(accountId, title);
1156
- await metrics.sendMetricsEvent("create kv namespace", {
1157
- sendMetrics: config.send_metrics,
1158
- });
1159
-
1160
- logger.log("✨ Success!");
1161
- const envString = args.env ? ` under [env.${args.env}]` : "";
1162
- const previewString = args.preview ? "preview_" : "";
1163
- logger.log(
1164
- `Add the following to your configuration file in your kv_namespaces array${envString}:`
1165
- );
1166
- logger.log(
1167
- `{ binding = "${args.namespace}", ${previewString}id = "${namespaceId}" }`
1168
- );
1169
-
1170
- // TODO: automatically write this block to the wrangler.toml config file??
1171
- }
1172
- )
1173
- .command(
1174
- "list",
1175
- "Outputs a list of all KV namespaces associated with your account id.",
1176
- {},
1177
- async (args) => {
1178
- const config = readConfig(args.config as ConfigPath, args);
1179
-
1180
- const accountId = await requireAuth(config);
1181
-
1182
- // TODO: we should show bindings if they exist for given ids
1183
-
1184
- logger.log(
1185
- JSON.stringify(await listKVNamespaces(accountId), null, " ")
1186
- );
1187
- await metrics.sendMetricsEvent("list kv namespaces", {
1188
- sendMetrics: config.send_metrics,
1189
- });
1190
- }
1191
- )
1192
- .command(
1193
- "delete",
1194
- "Deletes a given namespace.",
1195
- (yargs) => {
1196
- return yargs
1197
- .option("binding", {
1198
- type: "string",
1199
- requiresArg: true,
1200
- describe: "The name of the namespace to delete",
1201
- })
1202
- .option("namespace-id", {
1203
- type: "string",
1204
- requiresArg: true,
1205
- describe: "The id of the namespace to delete",
1206
- })
1207
- .check(demandOneOfOption("binding", "namespace-id"))
1208
- .option("env", {
1209
- type: "string",
1210
- requiresArg: true,
1211
- describe: "Perform on a specific environment",
1212
- alias: "e",
1213
- })
1214
- .option("preview", {
1215
- type: "boolean",
1216
- describe: "Interact with a preview namespace",
1217
- });
1218
- },
1219
- async (args) => {
1220
- await printWranglerBanner();
1221
- const config = readConfig(args.config as ConfigPath, args);
1222
-
1223
- let id;
1224
- try {
1225
- id = getKVNamespaceId(args, config);
1226
- } catch (e) {
1227
- throw new CommandLineArgsError(
1228
- "Not able to delete namespace.\n" + ((e as Error).message ?? e)
1229
- );
1230
- }
1231
-
1232
- const accountId = await requireAuth(config);
1233
-
1234
- logger.log(`Deleting KV namespace ${id}.`);
1235
- await deleteKVNamespace(accountId, id);
1236
- logger.log(`Deleted KV namespace ${id}.`);
1237
- await metrics.sendMetricsEvent("delete kv namespace", {
1238
- sendMetrics: config.send_metrics,
1239
- });
1240
-
1241
- // TODO: recommend they remove it from wrangler.toml
1242
-
1243
- // test-mf wrangler kv:namespace delete --namespace-id 2a7d3d8b23fc4159b5afa489d6cfd388
1244
- // Are you sure you want to delete namespace 2a7d3d8b23fc4159b5afa489d6cfd388? [y/n]
1245
- // n
1246
- // 💁 Not deleting namespace 2a7d3d8b23fc4159b5afa489d6cfd388
1247
- // ➜ test-mf wrangler kv:namespace delete --namespace-id 2a7d3d8b23fc4159b5afa489d6cfd388
1248
- // Are you sure you want to delete namespace 2a7d3d8b23fc4159b5afa489d6cfd388? [y/n]
1249
- // y
1250
- // 🌀 Deleting namespace 2a7d3d8b23fc4159b5afa489d6cfd388
1251
- // ✨ Success
1252
- // ⚠️ Make sure to remove this "kv-namespace" entry from your configuration file!
1253
- // ➜ test-mf
1254
-
1255
- // TODO: do it automatically
1256
-
1257
- // TODO: delete the preview namespace as well?
1258
- }
1259
- );
378
+ return kvNamespace(namespaceYargs.command(subHelp));
1260
379
  }
1261
380
  );
1262
381
 
1263
- // :key
382
+ // kv key
1264
383
  wrangler.command(
1265
384
  "kv:key",
1266
385
  "🔑 Individually manage Workers KV key-value pairs",
1267
- (kvKeyYargs) => {
1268
- return kvKeyYargs
1269
- .command(subHelp)
1270
- .command(
1271
- "put <key> [value]",
1272
- "Writes a single key/value pair to the given namespace.",
1273
- (yargs) => {
1274
- return yargs
1275
- .positional("key", {
1276
- type: "string",
1277
- describe: "The key to write to",
1278
- demandOption: true,
1279
- })
1280
- .positional("value", {
1281
- type: "string",
1282
- describe: "The value to write",
1283
- })
1284
- .option("binding", {
1285
- type: "string",
1286
- requiresArg: true,
1287
- describe: "The binding of the namespace to write to",
1288
- })
1289
- .option("namespace-id", {
1290
- type: "string",
1291
- requiresArg: true,
1292
- describe: "The id of the namespace to write to",
1293
- })
1294
- .check(demandOneOfOption("binding", "namespace-id"))
1295
- .option("env", {
1296
- type: "string",
1297
- requiresArg: true,
1298
- describe: "Perform on a specific environment",
1299
- alias: "e",
1300
- })
1301
- .option("preview", {
1302
- type: "boolean",
1303
- describe: "Interact with a preview namespace",
1304
- })
1305
- .option("ttl", {
1306
- type: "number",
1307
- describe: "Time for which the entries should be visible",
1308
- })
1309
- .option("expiration", {
1310
- type: "number",
1311
- describe:
1312
- "Time since the UNIX epoch after which the entry expires",
1313
- })
1314
- .option("metadata", {
1315
- type: "string",
1316
- describe: "Arbitrary JSON that is associated with a key",
1317
- coerce: (jsonStr: string): KeyValue["metadata"] => {
1318
- try {
1319
- return JSON.parse(jsonStr);
1320
- } catch (_) {}
1321
- },
1322
- })
1323
- .option("path", {
1324
- type: "string",
1325
- requiresArg: true,
1326
- describe: "Read value from the file at a given path",
1327
- })
1328
- .check(demandOneOfOption("value", "path"));
1329
- },
1330
- async ({ key, ttl, expiration, metadata, ...args }) => {
1331
- await printWranglerBanner();
1332
- const config = readConfig(args.config as ConfigPath, args);
1333
- const namespaceId = getKVNamespaceId(args, config);
1334
- // One of `args.path` and `args.value` must be defined
1335
- const value = args.path
1336
- ? readFileSyncToBuffer(args.path)
1337
- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1338
- args.value!;
1339
-
1340
- const metadataLog = metadata
1341
- ? ` with metadata "${JSON.stringify(metadata)}"`
1342
- : "";
1343
-
1344
- if (args.path) {
1345
- logger.log(
1346
- `Writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}${metadataLog}.`
1347
- );
1348
- } else {
1349
- logger.log(
1350
- `Writing the value "${value}" to key "${key}" on namespace ${namespaceId}${metadataLog}.`
1351
- );
1352
- }
1353
-
1354
- const accountId = await requireAuth(config);
1355
-
1356
- await putKVKeyValue(accountId, namespaceId, {
1357
- key,
1358
- value,
1359
- expiration,
1360
- expiration_ttl: ttl,
1361
- metadata: metadata as KeyValue["metadata"],
1362
- });
1363
- await metrics.sendMetricsEvent("write kv key-value", {
1364
- sendMetrics: config.send_metrics,
1365
- });
1366
- }
1367
- )
1368
- .command(
1369
- "list",
1370
- "Outputs a list of all keys in a given namespace.",
1371
- (yargs) => {
1372
- return yargs
1373
- .option("binding", {
1374
- type: "string",
1375
- requiresArg: true,
1376
- describe: "The name of the namespace to list",
1377
- })
1378
- .option("namespace-id", {
1379
- type: "string",
1380
- requiresArg: true,
1381
- describe: "The id of the namespace to list",
1382
- })
1383
- .check(demandOneOfOption("binding", "namespace-id"))
1384
- .option("env", {
1385
- type: "string",
1386
- requiresArg: true,
1387
- describe: "Perform on a specific environment",
1388
- alias: "e",
1389
- })
1390
- .option("preview", {
1391
- type: "boolean",
1392
- // In the case of listing keys we will default to non-preview mode
1393
- default: false,
1394
- describe: "Interact with a preview namespace",
1395
- })
1396
- .option("prefix", {
1397
- type: "string",
1398
- requiresArg: true,
1399
- describe: "A prefix to filter listed keys",
1400
- });
1401
- },
1402
- async ({ prefix, ...args }) => {
1403
- // TODO: support for limit+cursor (pagination)
1404
- const config = readConfig(args.config as ConfigPath, args);
1405
- const namespaceId = getKVNamespaceId(args, config);
1406
-
1407
- const accountId = await requireAuth(config);
1408
-
1409
- const results = await listKVNamespaceKeys(
1410
- accountId,
1411
- namespaceId,
1412
- prefix
1413
- );
1414
- logger.log(JSON.stringify(results, undefined, 2));
1415
- await metrics.sendMetricsEvent("list kv keys", {
1416
- sendMetrics: config.send_metrics,
1417
- });
1418
- }
1419
- )
1420
- .command(
1421
- "get <key>",
1422
- "Reads a single value by key from the given namespace.",
1423
- (yargs) => {
1424
- return yargs
1425
- .positional("key", {
1426
- describe: "The key value to get.",
1427
- type: "string",
1428
- demandOption: true,
1429
- })
1430
- .option("binding", {
1431
- type: "string",
1432
- requiresArg: true,
1433
- describe: "The name of the namespace to get from",
1434
- })
1435
- .option("namespace-id", {
1436
- type: "string",
1437
- requiresArg: true,
1438
- describe: "The id of the namespace to get from",
1439
- })
1440
- .check(demandOneOfOption("binding", "namespace-id"))
1441
- .option("env", {
1442
- type: "string",
1443
- requiresArg: true,
1444
- describe: "Perform on a specific environment",
1445
- alias: "e",
1446
- })
1447
- .option("preview", {
1448
- type: "boolean",
1449
- describe: "Interact with a preview namespace",
1450
- })
1451
- .option("preview", {
1452
- type: "boolean",
1453
- // In the case of getting key values we will default to non-preview mode
1454
- default: false,
1455
- describe: "Interact with a preview namespace",
1456
- })
1457
- .option("text", {
1458
- type: "boolean",
1459
- default: false,
1460
- describe: "Decode the returned value as a utf8 string",
1461
- });
1462
- },
1463
- async ({ key, ...args }) => {
1464
- const config = readConfig(args.config as ConfigPath, args);
1465
- const namespaceId = getKVNamespaceId(args, config);
1466
-
1467
- const accountId = await requireAuth(config);
1468
- const bufferKVValue = Buffer.from(
1469
- await getKVKeyValue(accountId, namespaceId, key)
1470
- );
1471
-
1472
- if (args.text) {
1473
- const decoder = new StringDecoder("utf8");
1474
- logger.log(decoder.write(bufferKVValue));
1475
- } else {
1476
- process.stdout.write(bufferKVValue);
1477
- }
1478
- await metrics.sendMetricsEvent("read kv value", {
1479
- sendMetrics: config.send_metrics,
1480
- });
1481
- }
1482
- )
1483
- .command(
1484
- "delete <key>",
1485
- "Removes a single key value pair from the given namespace.",
1486
- (yargs) => {
1487
- return yargs
1488
- .positional("key", {
1489
- describe: "The key value to delete",
1490
- type: "string",
1491
- demandOption: true,
1492
- })
1493
- .option("binding", {
1494
- type: "string",
1495
- requiresArg: true,
1496
- describe: "The name of the namespace to delete from",
1497
- })
1498
- .option("namespace-id", {
1499
- type: "string",
1500
- requiresArg: true,
1501
- describe: "The id of the namespace to delete from",
1502
- })
1503
- .check(demandOneOfOption("binding", "namespace-id"))
1504
- .option("env", {
1505
- type: "string",
1506
- requiresArg: true,
1507
- describe: "Perform on a specific environment",
1508
- alias: "e",
1509
- })
1510
- .option("preview", {
1511
- type: "boolean",
1512
- describe: "Interact with a preview namespace",
1513
- });
1514
- },
1515
- async ({ key, ...args }) => {
1516
- await printWranglerBanner();
1517
- const config = readConfig(args.config as ConfigPath, args);
1518
- const namespaceId = getKVNamespaceId(args, config);
1519
-
1520
- logger.log(
1521
- `Deleting the key "${key}" on namespace ${namespaceId}.`
1522
- );
1523
-
1524
- const accountId = await requireAuth(config);
1525
-
1526
- await deleteKVKeyValue(accountId, namespaceId, key);
1527
- await metrics.sendMetricsEvent("delete kv key-value", {
1528
- sendMetrics: config.send_metrics,
1529
- });
1530
- }
1531
- );
386
+ (keyYargs) => {
387
+ return kvKey(keyYargs.command(subHelp));
1532
388
  }
1533
389
  );
1534
390
 
1535
- // :bulk
391
+ // kv bulk
1536
392
  wrangler.command(
1537
393
  "kv:bulk",
1538
394
  "💪 Interact with multiple Workers KV key-value pairs at once",
1539
- (kvBulkYargs) => {
1540
- return kvBulkYargs
1541
- .command(subHelp)
1542
- .command(
1543
- "put <filename>",
1544
- "Upload multiple key-value pairs to a namespace",
1545
- (yargs) => {
1546
- return yargs
1547
- .positional("filename", {
1548
- describe: `The JSON file of key-value pairs to upload, in form [{"key":..., "value":...}"...]`,
1549
- type: "string",
1550
- demandOption: true,
1551
- })
1552
- .option("binding", {
1553
- type: "string",
1554
- requiresArg: true,
1555
- describe: "The name of the namespace to insert values into",
1556
- })
1557
- .option("namespace-id", {
1558
- type: "string",
1559
- requiresArg: true,
1560
- describe: "The id of the namespace to insert values into",
1561
- })
1562
- .check(demandOneOfOption("binding", "namespace-id"))
1563
- .option("env", {
1564
- type: "string",
1565
- requiresArg: true,
1566
- describe: "Perform on a specific environment",
1567
- alias: "e",
1568
- })
1569
- .option("preview", {
1570
- type: "boolean",
1571
- describe: "Interact with a preview namespace",
1572
- });
1573
- },
1574
- async ({ filename, ...args }) => {
1575
- await printWranglerBanner();
1576
- // The simplest implementation I could think of.
1577
- // This could be made more efficient with a streaming parser/uploader
1578
- // but we'll do that in the future if needed.
1579
-
1580
- const config = readConfig(args.config as ConfigPath, args);
1581
- const namespaceId = getKVNamespaceId(args, config);
1582
- const content = parseJSON(readFileSync(filename), filename);
1583
-
1584
- if (!Array.isArray(content)) {
1585
- throw new Error(
1586
- `Unexpected JSON input from "${filename}".\n` +
1587
- `Expected an array of key-value objects but got type "${typeof content}".`
1588
- );
1589
- }
1590
-
1591
- const errors: string[] = [];
1592
- const warnings: string[] = [];
1593
- for (let i = 0; i < content.length; i++) {
1594
- const keyValue = content[i];
1595
- if (!isKVKeyValue(keyValue)) {
1596
- errors.push(
1597
- `The item at index ${i} is ${JSON.stringify(keyValue)}`
1598
- );
1599
- } else {
1600
- const props = unexpectedKVKeyValueProps(keyValue);
1601
- if (props.length > 0) {
1602
- warnings.push(
1603
- `The item at index ${i} contains unexpected properties: ${JSON.stringify(
1604
- props
1605
- )}.`
1606
- );
1607
- }
1608
- }
1609
- }
1610
- if (warnings.length > 0) {
1611
- logger.warn(
1612
- `Unexpected key-value properties in "${filename}".\n` +
1613
- warnings.join("\n")
1614
- );
1615
- }
1616
- if (errors.length > 0) {
1617
- throw new Error(
1618
- `Unexpected JSON input from "${filename}".\n` +
1619
- `Each item in the array should be an object that matches:\n\n` +
1620
- `interface KeyValue {\n` +
1621
- ` key: string;\n` +
1622
- ` value: string;\n` +
1623
- ` expiration?: number;\n` +
1624
- ` expiration_ttl?: number;\n` +
1625
- ` metadata?: object;\n` +
1626
- ` base64?: boolean;\n` +
1627
- `}\n\n` +
1628
- errors.join("\n")
1629
- );
1630
- }
1631
-
1632
- const accountId = await requireAuth(config);
1633
- await putKVBulkKeyValue(accountId, namespaceId, content);
1634
- await metrics.sendMetricsEvent("write kv key-values (bulk)", {
1635
- sendMetrics: config.send_metrics,
1636
- });
1637
-
1638
- logger.log("Success!");
1639
- }
1640
- )
1641
- .command(
1642
- "delete <filename>",
1643
- "Delete multiple key-value pairs from a namespace",
1644
- (yargs) => {
1645
- return yargs
1646
- .positional("filename", {
1647
- describe: `The JSON file of keys to delete, in the form ["key1", "key2", ...]`,
1648
- type: "string",
1649
- demandOption: true,
1650
- })
1651
- .option("binding", {
1652
- type: "string",
1653
- requiresArg: true,
1654
- describe: "The name of the namespace to delete from",
1655
- })
1656
- .option("namespace-id", {
1657
- type: "string",
1658
- requiresArg: true,
1659
- describe: "The id of the namespace to delete from",
1660
- })
1661
- .check(demandOneOfOption("binding", "namespace-id"))
1662
- .option("env", {
1663
- type: "string",
1664
- requiresArg: true,
1665
- describe: "Perform on a specific environment",
1666
- alias: "e",
1667
- })
1668
- .option("preview", {
1669
- type: "boolean",
1670
- describe: "Interact with a preview namespace",
1671
- })
1672
- .option("force", {
1673
- type: "boolean",
1674
- alias: "f",
1675
- describe: "Do not ask for confirmation before deleting",
1676
- });
1677
- },
1678
- async ({ filename, ...args }) => {
1679
- await printWranglerBanner();
1680
- const config = readConfig(args.config as ConfigPath, args);
1681
- const namespaceId = getKVNamespaceId(args, config);
1682
-
1683
- if (!args.force) {
1684
- const result = await confirm(
1685
- `Are you sure you want to delete all the keys read from "${filename}" from kv-namespace with id "${namespaceId}"?`
1686
- );
1687
- if (!result) {
1688
- logger.log(`Not deleting keys read from "${filename}".`);
1689
- return;
1690
- }
1691
- }
1692
-
1693
- const content = parseJSON(
1694
- readFileSync(filename),
1695
- filename
1696
- ) as string[];
1697
-
1698
- if (!Array.isArray(content)) {
1699
- throw new Error(
1700
- `Unexpected JSON input from "${filename}".\n` +
1701
- `Expected an array of strings but got:\n${content}`
1702
- );
1703
- }
1704
-
1705
- const errors: string[] = [];
1706
- for (let i = 0; i < content.length; i++) {
1707
- const key = content[i];
1708
- if (typeof key !== "string") {
1709
- errors.push(
1710
- `The item at index ${i} is type: "${typeof key}" - ${JSON.stringify(
1711
- key
1712
- )}`
1713
- );
1714
- }
1715
- }
1716
- if (errors.length > 0) {
1717
- throw new Error(
1718
- `Unexpected JSON input from "${filename}".\n` +
1719
- `Expected an array of strings.\n` +
1720
- errors.join("\n")
1721
- );
1722
- }
1723
-
1724
- const accountId = await requireAuth(config);
1725
-
1726
- await deleteKVBulkKeyValue(accountId, namespaceId, content);
1727
- await metrics.sendMetricsEvent("delete kv key-values (bulk)", {
1728
- sendMetrics: config.send_metrics,
1729
- });
1730
-
1731
- logger.log("Success!");
1732
- }
1733
- );
395
+ (bulkYargs) => {
396
+ return kvBulk(bulkYargs.command(subHelp));
1734
397
  }
1735
398
  );
1736
399
 
1737
- wrangler.command(
1738
- "pages",
1739
- "⚡️ Configure Cloudflare Pages",
1740
- async (pagesYargs) => {
1741
- await pages(pagesYargs.command(subHelp));
1742
- }
1743
- );
1744
-
1745
- wrangler.command("r2", "📦 Interact with an R2 store", (r2Yargs) => {
1746
- return r2Yargs
1747
- .command(subHelp)
1748
- .command("bucket", "Manage R2 buckets", (r2BucketYargs) => {
1749
- r2BucketYargs.command(
1750
- "create <name>",
1751
- "Create a new R2 bucket",
1752
- (yargs) => {
1753
- return yargs.positional("name", {
1754
- describe: "The name of the new bucket",
1755
- type: "string",
1756
- demandOption: true,
1757
- });
1758
- },
1759
- async (args) => {
1760
- await printWranglerBanner();
1761
-
1762
- const config = readConfig(args.config as ConfigPath, args);
1763
-
1764
- const accountId = await requireAuth(config);
1765
-
1766
- logger.log(`Creating bucket ${args.name}.`);
1767
- await createR2Bucket(accountId, args.name);
1768
- logger.log(`Created bucket ${args.name}.`);
1769
- await metrics.sendMetricsEvent("create r2 bucket", {
1770
- sendMetrics: config.send_metrics,
1771
- });
1772
- }
1773
- );
1774
-
1775
- r2BucketYargs.command("list", "List R2 buckets", {}, async (args) => {
1776
- const config = readConfig(args.config as ConfigPath, args);
1777
-
1778
- const accountId = await requireAuth(config);
1779
-
1780
- logger.log(JSON.stringify(await listR2Buckets(accountId), null, 2));
1781
- await metrics.sendMetricsEvent("list r2 buckets", {
1782
- sendMetrics: config.send_metrics,
1783
- });
1784
- });
1785
-
1786
- r2BucketYargs.command(
1787
- "delete <name>",
1788
- "Delete an R2 bucket",
1789
- (yargs) => {
1790
- return yargs.positional("name", {
1791
- describe: "The name of the bucket to delete",
1792
- type: "string",
1793
- demandOption: true,
1794
- });
1795
- },
1796
- async (args) => {
1797
- await printWranglerBanner();
1798
-
1799
- const config = readConfig(args.config as ConfigPath, args);
400
+ // pages
401
+ wrangler.command("pages", "⚡️ Configure Cloudflare Pages", (pagesYargs) => {
402
+ return pages(pagesYargs.command(subHelp));
403
+ });
1800
404
 
1801
- const accountId = await requireAuth(config);
405
+ // queues
406
+ wrangler.command("queues", "🇶 Configure Workers Queues", (queuesYargs) => {
407
+ return queues(queuesYargs.command(subHelp));
408
+ });
1802
409
 
1803
- logger.log(`Deleting bucket ${args.name}.`);
1804
- await deleteR2Bucket(accountId, args.name);
1805
- logger.log(`Deleted bucket ${args.name}.`);
1806
- await metrics.sendMetricsEvent("delete r2 bucket", {
1807
- sendMetrics: config.send_metrics,
1808
- });
1809
- }
1810
- );
1811
- return r2BucketYargs;
1812
- });
410
+ // r2
411
+ wrangler.command("r2", "📦 Interact with an R2 store", (r2Yargs) => {
412
+ return r2(r2Yargs.command(subHelp));
1813
413
  });
1814
414
 
415
+ // dispatch-namespace
1815
416
  wrangler.command(
1816
- "worker-namespace",
1817
- "📦 Interact with a worker namespace",
417
+ "dispatch-namespace",
418
+ "📦 Interact with a dispatch namespace",
1818
419
  (workerNamespaceYargs) => {
1819
420
  return workerNamespaceCommands(workerNamespaceYargs, subHelp);
1820
421
  }
1821
422
  );
1822
423
 
424
+ // d1
425
+ wrangler.command("d1", "🗄 Interact with a D1 database", (d1Yargs) => {
426
+ return d1(d1Yargs.command(subHelp));
427
+ });
428
+
429
+ // pubsub
1823
430
  wrangler.command(
1824
431
  "pubsub",
1825
432
  "📮 Interact and manage Pub/Sub Brokers",
@@ -1844,6 +451,11 @@ function createCLIParser(argv: string[]) {
1844
451
  .option("scopes-list", {
1845
452
  describe: "List all the available OAuth scopes with descriptions",
1846
453
  })
454
+ .option("browser", {
455
+ default: true,
456
+ type: "boolean",
457
+ describe: "Automatically open the OAuth link in a browser",
458
+ })
1847
459
  .option("scopes", {
1848
460
  describe: "Pick the set of applicable OAuth scopes when logging in",
1849
461
  array: true,
@@ -1870,10 +482,10 @@ function createCLIParser(argv: string[]) {
1870
482
  `One of ${args.scopes} is not a valid authentication scope. Run "wrangler login --list-scopes" to see the valid scopes.`
1871
483
  );
1872
484
  }
1873
- await login({ scopes: args.scopes });
485
+ await login({ scopes: args.scopes, browser: args.browser });
1874
486
  return;
1875
487
  }
1876
- await login();
488
+ await login({ browser: args.browser });
1877
489
  const config = readConfig(args.config as ConfigPath, args);
1878
490
  await metrics.sendMetricsEvent("login user", {
1879
491
  sendMetrics: config.send_metrics,
@@ -1916,6 +528,70 @@ function createCLIParser(argv: string[]) {
1916
528
  }
1917
529
  );
1918
530
 
531
+ // type generation
532
+ wrangler.command(
533
+ "types",
534
+ "📝 Generate types from bindings & module rules in config",
535
+ () => {},
536
+ async () => {
537
+ await printWranglerBanner();
538
+ const config = readConfig(undefined, {});
539
+
540
+ const configBindings: Partial<Config> = {
541
+ kv_namespaces: config.kv_namespaces ?? [],
542
+ vars: { ...config.vars },
543
+ wasm_modules: config.wasm_modules,
544
+ text_blobs: {
545
+ ...config.text_blobs,
546
+ },
547
+ data_blobs: config.data_blobs,
548
+ durable_objects: config.durable_objects,
549
+ r2_buckets: config.r2_buckets,
550
+ d1_databases: config.d1_databases,
551
+ services: config.services,
552
+ analytics_engine_datasets: config.analytics_engine_datasets,
553
+ dispatch_namespaces: config.dispatch_namespaces,
554
+ logfwdr: config.logfwdr,
555
+ unsafe: { bindings: config.unsafe?.bindings },
556
+ rules: config.rules,
557
+ queues: config.queues,
558
+ };
559
+
560
+ await generateTypes(configBindings, config);
561
+ }
562
+ );
563
+
564
+ //deployments
565
+ const deploymentsWarning =
566
+ "🚧`wrangler deployments` is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose";
567
+ wrangler.command(
568
+ "deployments",
569
+ "🚢 Displays the 10 most recent deployments for a worker",
570
+ (yargs) => {
571
+ yargs
572
+ .option("name", {
573
+ describe: "The name of your worker",
574
+ type: "string",
575
+ })
576
+ .epilogue(deploymentsWarning);
577
+ },
578
+ async (deploymentsYargs: ArgumentsCamelCase<{ name: string }>) => {
579
+ await printWranglerBanner();
580
+ const config = readConfig(
581
+ deploymentsYargs.config as ConfigPath,
582
+ deploymentsYargs
583
+ );
584
+ const accountId = await requireAuth(config);
585
+ const scriptName = getScriptName(
586
+ { name: deploymentsYargs.name, env: undefined },
587
+ config
588
+ );
589
+
590
+ logger.log(`${deploymentsWarning}\n`);
591
+ await deployments(accountId, scriptName);
592
+ }
593
+ );
594
+
1919
595
  // This set to false to allow overwrite of default behaviour
1920
596
  wrangler.version(false);
1921
597
 
@@ -1933,22 +609,6 @@ function createCLIParser(argv: string[]) {
1933
609
  }
1934
610
  );
1935
611
 
1936
- wrangler.option("v", {
1937
- describe: "Show version number",
1938
- alias: "version",
1939
- type: "boolean",
1940
- });
1941
-
1942
- wrangler.option("config", {
1943
- alias: "c",
1944
- describe: "Path to .toml configuration file",
1945
- type: "string",
1946
- requiresArg: true,
1947
- });
1948
-
1949
- wrangler.group(["config", "help", "version"], "Flags:");
1950
- wrangler.help().alias("h", "help");
1951
-
1952
612
  wrangler.exitProcess(false);
1953
613
 
1954
614
  return wrangler;
@@ -1971,6 +631,35 @@ export async function main(argv: string[]): Promise<void> {
1971
631
  text: "\nIf you think this is a bug, please open an issue at: https://github.com/cloudflare/wrangler2/issues/new/choose",
1972
632
  });
1973
633
  logger.log(formatMessage(e));
634
+ } else if (
635
+ e instanceof Error &&
636
+ e.message.includes("Raw mode is not supported on")
637
+ ) {
638
+ // the current terminal doesn't support raw mode, which Ink needs to render
639
+ // Ink doesn't throw a typed error or subclass or anything, so we just check the message content.
640
+ // https://github.com/vadimdemedes/ink/blob/546fe16541fd05ad4e638d6842ca4cbe88b4092b/src/components/App.tsx#L138-L148
641
+
642
+ const currentPlatform = os.platform();
643
+
644
+ const thisTerminalIsUnsupported =
645
+ "This terminal doesn't support raw mode.";
646
+ const soWranglerWontWork =
647
+ "Wrangler uses raw mode to read user input and write output to the terminal, and won't function correctly without it.";
648
+ const tryRunningItIn =
649
+ "Try running your previous command in a terminal that supports raw mode";
650
+ const oneOfThese =
651
+ currentPlatform === "win32"
652
+ ? ", such as Command Prompt or Powershell."
653
+ : currentPlatform === "darwin"
654
+ ? ", such as Terminal.app or iTerm."
655
+ : "."; // linux user detected, hand holding disengaged.
656
+
657
+ logger.error(
658
+ `${thisTerminalIsUnsupported}\n${soWranglerWontWork}\n${tryRunningItIn}${oneOfThese}`
659
+ );
660
+ } else if (isBuildFailure(e)) {
661
+ logBuildFailure(e);
662
+ logger.error(e.message);
1974
663
  } else {
1975
664
  logger.error(e instanceof Error ? e.message : e);
1976
665
  logger.log(
@@ -1998,7 +687,7 @@ export function getDevCompatibilityDate(
1998
687
  "```\n" +
1999
688
  `--compatibility-date=${currentDate}\n` +
2000
689
  "```\n" +
2001
- "See https://developers.cloudflare.com/workers/platform/compatibility-dates for more information."
690
+ "See https://developers.cloudflare.com/workers/platform/compatibility-dates/ for more information."
2002
691
  );
2003
692
  }
2004
693
  return compatibilityDate ?? currentDate;