wrangler 0.0.0-e6733a3 → 0.0.0-e7a0cd5b

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 (302) hide show
  1. package/Cloudflare_CA.pem +18 -0
  2. package/README.md +47 -14
  3. package/bin/wrangler.js +160 -28
  4. package/kv-asset-handler.js +1 -0
  5. package/miniflare-dist/index.mjs +6316 -0
  6. package/package.json +190 -124
  7. package/src/__tests__/access.test.ts +25 -0
  8. package/src/__tests__/api-dev.test.ts +209 -0
  9. package/src/__tests__/api-devregistry.test.js +57 -0
  10. package/src/__tests__/api.test.ts +80 -0
  11. package/src/__tests__/config-cache-without-cache-dir.test.ts +38 -0
  12. package/src/__tests__/config-cache.test.ts +42 -0
  13. package/src/__tests__/configuration.test.ts +4028 -0
  14. package/src/__tests__/d1.test.ts +72 -0
  15. package/src/__tests__/delete.test.ts +230 -0
  16. package/src/__tests__/deployments.test.ts +95 -0
  17. package/src/__tests__/dev.test.tsx +1505 -62
  18. package/src/__tests__/dialogs.test.tsx +40 -0
  19. package/src/__tests__/generate.test.ts +235 -0
  20. package/src/__tests__/get-host-from-url.test.ts +16 -0
  21. package/src/__tests__/guess-worker-format.test.ts +85 -0
  22. package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
  23. package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
  24. package/src/__tests__/helpers/mock-account-id.ts +48 -0
  25. package/src/__tests__/helpers/mock-bin.ts +36 -0
  26. package/src/__tests__/helpers/mock-cfetch.ts +333 -0
  27. package/src/__tests__/helpers/mock-console.ts +106 -0
  28. package/src/__tests__/helpers/mock-dialogs.ts +153 -0
  29. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  30. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  31. package/src/__tests__/helpers/mock-istty.ts +74 -0
  32. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  33. package/src/__tests__/helpers/mock-kv.ts +33 -0
  34. package/src/__tests__/helpers/mock-oauth-flow.ts +252 -0
  35. package/src/__tests__/helpers/mock-process.ts +39 -0
  36. package/src/__tests__/helpers/mock-stdin.ts +108 -0
  37. package/src/__tests__/helpers/mock-web-socket.ts +29 -0
  38. package/src/__tests__/helpers/msw/handlers/access.ts +13 -0
  39. package/src/__tests__/helpers/msw/handlers/deployments.ts +59 -0
  40. package/src/__tests__/helpers/msw/handlers/namespaces.ts +104 -0
  41. package/src/__tests__/helpers/msw/handlers/oauth.ts +36 -0
  42. package/src/__tests__/helpers/msw/handlers/r2.ts +80 -0
  43. package/src/__tests__/helpers/msw/handlers/script.ts +75 -0
  44. package/src/__tests__/helpers/msw/handlers/user.ts +63 -0
  45. package/src/__tests__/helpers/msw/handlers/zones.ts +22 -0
  46. package/src/__tests__/helpers/msw/index.ts +21 -0
  47. package/src/__tests__/helpers/run-in-tmp.ts +38 -0
  48. package/src/__tests__/helpers/run-wrangler.ts +16 -0
  49. package/src/__tests__/helpers/worker-scripts/child-wrangler.toml +1 -0
  50. package/src/__tests__/helpers/worker-scripts/hello-world-worker.js +5 -0
  51. package/src/__tests__/helpers/worker-scripts/hello-world-wrangler.toml +1 -0
  52. package/src/__tests__/helpers/worker-scripts/parent-worker.js +8 -0
  53. package/src/__tests__/helpers/worker-scripts/parent-wrangler.toml +5 -0
  54. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  55. package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
  56. package/src/__tests__/https-options.test.ts +163 -0
  57. package/src/__tests__/index.test.ts +259 -284
  58. package/src/__tests__/init.test.ts +2900 -0
  59. package/src/__tests__/jest.setup.ts +190 -10
  60. package/src/__tests__/kv.test.ts +1668 -1091
  61. package/src/__tests__/logger.test.ts +141 -0
  62. package/src/__tests__/metrics.test.ts +450 -0
  63. package/src/__tests__/middleware.scheduled.test.ts +135 -0
  64. package/src/__tests__/middleware.test.ts +726 -0
  65. package/src/__tests__/package-manager.test.ts +353 -0
  66. package/src/__tests__/pages.test.ts +2223 -0
  67. package/src/__tests__/parse.test.ts +330 -0
  68. package/src/__tests__/paths.test.ts +39 -0
  69. package/src/__tests__/publish.test.ts +7660 -0
  70. package/src/__tests__/pubsub.test.ts +370 -0
  71. package/src/__tests__/queues.test.ts +371 -0
  72. package/src/__tests__/r2.test.ts +376 -0
  73. package/src/__tests__/route.test.ts +45 -0
  74. package/src/__tests__/secret.test.ts +644 -0
  75. package/src/__tests__/tail.test.ts +800 -0
  76. package/src/__tests__/test-old-node-version.js +31 -0
  77. package/src/__tests__/type-generation.test.ts +217 -0
  78. package/src/__tests__/user.test.ts +155 -0
  79. package/src/__tests__/utils-collectKeyValues.test.ts +47 -0
  80. package/src/__tests__/validate-dev-props.test.ts +56 -0
  81. package/src/__tests__/version.test.ts +35 -0
  82. package/src/__tests__/whoami.test.tsx +278 -0
  83. package/src/__tests__/worker-namespace.test.ts +326 -0
  84. package/src/abort.d.ts +3 -0
  85. package/src/api/dev.ts +208 -0
  86. package/src/api/index.ts +2 -0
  87. package/src/bundle-reporter.tsx +68 -0
  88. package/src/bundle.ts +785 -0
  89. package/src/cfetch/index.ts +128 -70
  90. package/src/cfetch/internal.ts +240 -46
  91. package/src/cli.ts +25 -6
  92. package/src/config/README.md +107 -0
  93. package/src/config/config.ts +275 -0
  94. package/src/config/diagnostics.ts +80 -0
  95. package/src/config/environment.ts +563 -0
  96. package/src/config/index.ts +342 -0
  97. package/src/config/validation-helpers.ts +597 -0
  98. package/src/config/validation.ts +2113 -0
  99. package/src/config-cache.ts +85 -0
  100. package/src/create-worker-preview.ts +282 -0
  101. package/src/create-worker-upload-form.ts +321 -0
  102. package/src/d1/backups.tsx +218 -0
  103. package/src/d1/create.tsx +54 -0
  104. package/src/d1/delete.tsx +56 -0
  105. package/src/d1/execute.tsx +299 -0
  106. package/src/d1/formatTimeAgo.ts +14 -0
  107. package/src/d1/index.ts +76 -0
  108. package/src/d1/list.tsx +48 -0
  109. package/src/d1/options.ts +12 -0
  110. package/src/d1/types.tsx +14 -0
  111. package/src/d1/utils.ts +39 -0
  112. package/src/delete.ts +108 -0
  113. package/src/deployments.ts +74 -0
  114. package/src/deprecated/index.ts +438 -0
  115. package/src/dev/dev-vars.ts +39 -0
  116. package/src/dev/dev.tsx +587 -0
  117. package/src/dev/get-local-persistence-path.tsx +31 -0
  118. package/src/dev/local.tsx +717 -0
  119. package/src/dev/remote.tsx +603 -0
  120. package/src/dev/start-server.ts +513 -0
  121. package/src/dev/use-esbuild.ts +204 -0
  122. package/src/dev/validate-dev-props.ts +31 -0
  123. package/src/dev-registry.tsx +201 -0
  124. package/src/dev.tsx +848 -750
  125. package/src/dialogs.tsx +146 -60
  126. package/src/dispatch-namespace.ts +207 -0
  127. package/src/durable.ts +102 -0
  128. package/src/entry.ts +329 -0
  129. package/src/environment-variables.ts +50 -0
  130. package/src/errors.ts +11 -0
  131. package/src/git-client.ts +124 -0
  132. package/src/global-wrangler-config-path.ts +26 -0
  133. package/src/https-options.ts +127 -0
  134. package/src/index.tsx +669 -1959
  135. package/src/init.ts +970 -0
  136. package/src/inspect.ts +720 -477
  137. package/src/intl-polyfill.d.ts +139 -0
  138. package/src/is-ci.ts +14 -0
  139. package/src/is-interactive.ts +16 -0
  140. package/src/kv/helpers.ts +430 -0
  141. package/src/kv/index.ts +596 -0
  142. package/src/logger.ts +92 -0
  143. package/src/metrics/index.ts +5 -0
  144. package/src/metrics/metrics-config.ts +239 -0
  145. package/src/metrics/metrics-dispatcher.ts +96 -0
  146. package/src/metrics/metrics-usage-headers.ts +24 -0
  147. package/src/metrics/send-event.ts +94 -0
  148. package/src/miniflare-cli/README.md +30 -0
  149. package/src/miniflare-cli/assets.ts +215 -0
  150. package/src/miniflare-cli/index.ts +213 -0
  151. package/src/miniflare-cli/request-context.ts +40 -0
  152. package/src/module-collection.ts +256 -53
  153. package/src/open-in-browser.ts +17 -0
  154. package/src/package-manager.ts +219 -0
  155. package/src/pages/build.tsx +238 -0
  156. package/src/pages/constants.ts +18 -0
  157. package/src/pages/deployments.tsx +106 -0
  158. package/src/pages/dev.tsx +693 -0
  159. package/src/pages/errors.ts +66 -0
  160. package/src/pages/functions/buildPlugin.ts +110 -0
  161. package/src/pages/functions/buildWorker.ts +156 -0
  162. package/src/pages/functions/filepath-routing.test.ts +234 -0
  163. package/src/pages/functions/filepath-routing.ts +189 -0
  164. package/src/pages/functions/identifiers.ts +78 -0
  165. package/src/pages/functions/routes-consolidation.test.ts +250 -0
  166. package/src/pages/functions/routes-consolidation.ts +73 -0
  167. package/src/pages/functions/routes-transformation.test.ts +282 -0
  168. package/src/pages/functions/routes-transformation.ts +115 -0
  169. package/src/pages/functions/routes-validation.test.ts +403 -0
  170. package/src/pages/functions/routes-validation.ts +202 -0
  171. package/src/pages/functions/routes.ts +151 -0
  172. package/src/pages/functions.tsx +82 -0
  173. package/src/pages/hash.tsx +13 -0
  174. package/src/pages/index.tsx +94 -0
  175. package/src/pages/projects.tsx +169 -0
  176. package/src/pages/publish.tsx +436 -0
  177. package/src/pages/types.ts +49 -0
  178. package/src/pages/upload.tsx +425 -0
  179. package/src/pages/utils.ts +12 -0
  180. package/src/parse.ts +286 -0
  181. package/src/paths.ts +71 -0
  182. package/src/proxy.ts +647 -252
  183. package/src/publish/index.ts +270 -0
  184. package/src/publish/publish.ts +1035 -0
  185. package/src/pubsub/index.ts +286 -0
  186. package/src/pubsub/pubsub-commands.tsx +624 -0
  187. package/src/queues/cli/commands/consumer/add.ts +71 -0
  188. package/src/queues/cli/commands/consumer/index.ts +22 -0
  189. package/src/queues/cli/commands/consumer/remove.ts +38 -0
  190. package/src/queues/cli/commands/create.ts +25 -0
  191. package/src/queues/cli/commands/delete.ts +26 -0
  192. package/src/queues/cli/commands/index.ts +33 -0
  193. package/src/queues/cli/commands/list.ts +25 -0
  194. package/src/queues/client.ts +135 -0
  195. package/src/r2/helpers.ts +131 -0
  196. package/src/r2/index.ts +277 -0
  197. package/src/routes.ts +140 -0
  198. package/src/secret/index.ts +379 -0
  199. package/src/selfsigned.d.ts +29 -0
  200. package/src/sites.tsx +357 -99
  201. package/src/tail/createTail.ts +315 -0
  202. package/src/tail/filters.ts +277 -0
  203. package/src/tail/index.ts +214 -0
  204. package/src/tail/printing.ts +107 -0
  205. package/src/type-generation.ts +159 -0
  206. package/src/update-check.ts +19 -0
  207. package/src/user/access.ts +69 -0
  208. package/src/user/choose-account.tsx +69 -0
  209. package/src/user/env-vars.ts +46 -0
  210. package/src/user/generate-auth-url.ts +33 -0
  211. package/src/user/generate-random-state.ts +16 -0
  212. package/src/user/index.ts +3 -0
  213. package/src/user/user.tsx +1192 -0
  214. package/src/utils/collectKeyValues.ts +14 -0
  215. package/src/whoami.tsx +169 -0
  216. package/src/worker.ts +242 -0
  217. package/src/yargs-types.ts +20 -0
  218. package/src/zones.ts +191 -0
  219. package/templates/__tests__/pages-dev-util.test.ts +128 -0
  220. package/templates/checked-fetch.js +17 -0
  221. package/templates/d1-beta-facade.js +174 -0
  222. package/templates/experimental-local-cache-stubs.js +27 -0
  223. package/templates/first-party-worker-module-facade.ts +18 -0
  224. package/templates/format-dev-errors.ts +32 -0
  225. package/templates/gitignore +171 -0
  226. package/templates/init-tests/test-jest-new-worker.js +25 -0
  227. package/templates/init-tests/test-vitest-new-worker.js +26 -0
  228. package/templates/middleware/common.ts +62 -0
  229. package/templates/middleware/loader-modules.ts +84 -0
  230. package/templates/middleware/loader-sw.ts +213 -0
  231. package/templates/middleware/middleware-pretty-error.ts +40 -0
  232. package/templates/middleware/middleware-scheduled.ts +15 -0
  233. package/templates/new-worker-scheduled.js +17 -0
  234. package/templates/new-worker-scheduled.ts +32 -0
  235. package/templates/new-worker.js +15 -0
  236. package/templates/new-worker.ts +30 -0
  237. package/templates/no-op-worker.js +10 -0
  238. package/templates/pages-dev-pipeline.ts +28 -0
  239. package/templates/pages-dev-util.ts +52 -0
  240. package/templates/pages-shim.ts +9 -0
  241. package/templates/pages-template-plugin.ts +171 -0
  242. package/templates/pages-template-worker.ts +189 -0
  243. package/templates/serve-static-assets.ts +63 -0
  244. package/templates/service-bindings-module-facade.js +54 -0
  245. package/templates/service-bindings-sw-facade.js +42 -0
  246. package/templates/tsconfig.json +106 -0
  247. package/wrangler-dist/cli.d.ts +892 -0
  248. package/wrangler-dist/cli.js +142402 -103276
  249. package/pages/functions/buildWorker.ts +0 -62
  250. package/pages/functions/filepath-routing.test.ts +0 -39
  251. package/pages/functions/filepath-routing.ts +0 -221
  252. package/pages/functions/identifiers.ts +0 -78
  253. package/pages/functions/routes.ts +0 -158
  254. package/pages/functions/template-worker.ts +0 -144
  255. package/src/__tests__/mock-cfetch.ts +0 -171
  256. package/src/__tests__/mock-dialogs.ts +0 -65
  257. package/src/__tests__/run-in-tmp.ts +0 -19
  258. package/src/__tests__/run-wrangler.ts +0 -32
  259. package/src/api/form_data.ts +0 -131
  260. package/src/api/preview.ts +0 -128
  261. package/src/api/worker.ts +0 -155
  262. package/src/config.ts +0 -487
  263. package/src/kv.tsx +0 -267
  264. package/src/pages.tsx +0 -1031
  265. package/src/publish.ts +0 -358
  266. package/src/tail.tsx +0 -73
  267. package/src/user.tsx +0 -1025
  268. package/static-asset-facade.js +0 -47
  269. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  270. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  271. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  272. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  273. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  274. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  275. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  276. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  277. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  278. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  279. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  280. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  281. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  282. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  283. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  284. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  285. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  286. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  287. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  288. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  289. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  290. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  291. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  292. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  293. package/vendor/wrangler-mime/LICENSE +0 -21
  294. package/vendor/wrangler-mime/Mime.js +0 -97
  295. package/vendor/wrangler-mime/README.md +0 -187
  296. package/vendor/wrangler-mime/cli.js +0 -46
  297. package/vendor/wrangler-mime/index.js +0 -4
  298. package/vendor/wrangler-mime/lite.js +0 -4
  299. package/vendor/wrangler-mime/package.json +0 -52
  300. package/vendor/wrangler-mime/types/other.js +0 -1
  301. package/vendor/wrangler-mime/types/standard.js +0 -1
  302. package/wrangler-dist/cli.js.map +0 -7
package/src/index.tsx CHANGED
@@ -1,1974 +1,684 @@
1
- import React from "react";
2
- import { render } from "ink";
3
- import Dev from "./dev";
4
- import { readFile } from "node:fs/promises";
5
- import makeCLI from "yargs";
6
- import type Yargs from "yargs";
7
- import { findUp } from "find-up";
1
+ import os from "node:os";
8
2
  import TOML from "@iarna/toml";
9
- import type { Config } from "./config";
10
- import { confirm, prompt } from "./dialogs";
3
+ import chalk from "chalk";
4
+ import supportsColor from "supports-color";
5
+ import { ProxyAgent, setGlobalDispatcher } from "undici";
6
+ import makeCLI from "yargs";
11
7
  import { version as wranglerVersion } from "../package.json";
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";
12
13
  import {
13
- login,
14
- logout,
15
- listScopes,
16
- initialise as initialiseUserConfig,
17
- loginOrRefreshIfRequired,
18
- getAccountId,
19
- } from "./user";
14
+ buildHandler,
15
+ buildOptions,
16
+ configHandler,
17
+ noOpOptions,
18
+ generateHandler,
19
+ generateOptions,
20
+ previewHandler,
21
+ previewOptions,
22
+ route,
23
+ routeHandler,
24
+ subdomainHandler,
25
+ subdomainOptions,
26
+ } from "./deprecated";
27
+ import { devHandler, devOptions } from "./dev";
28
+ import { workerNamespaceCommands } from "./dispatch-namespace";
29
+ import { initHandler, initOptions } from "./init";
30
+ import { kvNamespace, kvKey, kvBulk } from "./kv";
31
+ import { logBuildFailure, logger } from "./logger";
32
+ import * as metrics from "./metrics";
33
+ import { pages } from "./pages";
34
+ import { formatMessage, ParseError } from "./parse";
35
+ import { publishOptions, publishHandler } from "./publish";
36
+ import { pubSubCommands } from "./pubsub/pubsub-commands";
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";
42
+ import { updateCheck } from "./update-check";
20
43
  import {
21
- getNamespaceId,
22
- listNamespaces,
23
- listNamespaceKeys,
24
- putKeyValue,
25
- putBulkKeyValue,
26
- deleteBulkKeyValue,
27
- createNamespace,
28
- isValidNamespaceBinding,
29
- } from "./kv";
44
+ listScopes,
45
+ login,
46
+ logout,
47
+ requireAuth,
48
+ validateScopeKeys,
49
+ } from "./user";
50
+ import { whoami } from "./whoami";
30
51
 
31
- import { pages } from "./pages";
52
+ import type { Config } from "./config";
53
+ import type { PartialConfigToDTS } from "./type-generation";
54
+ import type { CommonYargsOptions } from "./yargs-types";
55
+ import type { ArgumentsCamelCase } from "yargs";
56
+ import type Yargs from "yargs";
57
+
58
+ export type ConfigPath = string | undefined;
59
+
60
+ const resetColor = "\x1b[0m";
61
+ const fgGreenColor = "\x1b[32m";
62
+ export const DEFAULT_LOCAL_PORT = 8787;
63
+ export const DEFAULT_INSPECTOR_PORT = 9229;
64
+
65
+ const proxy =
66
+ process.env.https_proxy ||
67
+ process.env.HTTPS_PROXY ||
68
+ process.env.http_proxy ||
69
+ process.env.HTTP_PROXY ||
70
+ undefined;
71
+
72
+ if (proxy) {
73
+ setGlobalDispatcher(new ProxyAgent(proxy));
74
+ logger.log(
75
+ `Proxy environment variables detected. We'll use your proxy for fetch requests.`
76
+ );
77
+ }
78
+
79
+ export function getRules(config: Config): Config["rules"] {
80
+ const rules = config.rules ?? config.build?.upload?.rules ?? [];
32
81
 
33
- import { fetchResult, fetchRaw } from "./cfetch";
34
-
35
- import publish from "./publish";
36
- import path from "path/posix";
37
- import { writeFile } from "node:fs/promises";
38
- import { toFormData } from "./api/form_data";
39
-
40
- import { createTail } from "./tail";
41
- import onExit from "signal-exit";
42
- import { setTimeout } from "node:timers/promises";
43
- import * as fs from "node:fs";
44
- import { execa } from "execa";
45
-
46
- async function readConfig(configPath?: string): Promise<Config> {
47
- const config: Config = {};
48
- if (!configPath) {
49
- configPath = await findUp("wrangler.toml");
50
- // TODO - terminate this early instead of going all the way to the root
51
- }
52
-
53
- if (configPath) {
54
- const tml: string = await readFile(configPath, "utf-8");
55
- const parsed = TOML.parse(tml) as Config;
56
- Object.assign(config, parsed);
57
- }
58
-
59
- const inheritedFields = [
60
- "name",
61
- "account_id",
62
- "workers_dev",
63
- "compatibility_date",
64
- "compatibility_flags",
65
- "zone_id",
66
- "routes",
67
- "route",
68
- "jsx_factory",
69
- "jsx_fragment",
70
- "site",
71
- "triggers",
72
- "usage_model",
73
- ];
74
-
75
- Object.keys(config.env || {}).forEach((env) => {
76
- inheritedFields.forEach((field) => {
77
- if (config[field] !== undefined && config.env[env][field] === undefined) {
78
- config.env[env][field] = config[field]; // TODO: - shallow copy?
79
- }
80
- });
81
- });
82
-
83
- const mirroredFields = [
84
- "vars",
85
- "kv_namespaces",
86
- "durable_objects",
87
- "experimental_services",
88
- ];
89
- Object.keys(config.env || {}).forEach((env) => {
90
- mirroredFields.forEach((field) => {
91
- // if it exists on top level, it should exist on env definitions
92
- Object.keys(config[field] || {}).forEach((fieldKey) => {
93
- if (!(fieldKey in config.env[env][field])) {
94
- console.warn(
95
- `In your configuration, "${field}.${fieldKey}" exists at a top level, but not on "env.${env}". This is not what you probably want, since the field "${field}" is not inherited by environments. Please add "${field}.${fieldKey}" to "env.${env}".`
96
- );
97
- }
98
- });
99
- });
100
- });
101
-
102
- if ("experimental_services" in config) {
103
- console.warn(
104
- "The experimental_services field is only for cloudflare internal usage right now, and is subject to change. Please do not use this on production projects"
105
- );
106
- }
107
-
108
- // todo: validate, add defaults
109
- // let's just do some basics for now
110
-
111
- // @ts-expect-error we're being sneaky here for now
112
- config.__path__ = configPath;
113
-
114
- return config;
82
+ if (config.rules && config.build?.upload?.rules) {
83
+ throw new Error(
84
+ `You cannot configure both [rules] and [build.upload.rules] in your wrangler.toml. Delete the \`build.upload\` section.`
85
+ );
86
+ }
87
+
88
+ if (config.build?.upload?.rules) {
89
+ logger.warn(
90
+ `Deprecation: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this:
91
+
92
+ ${TOML.stringify({ rules: config.build.upload.rules })}`
93
+ );
94
+ }
95
+ return rules;
96
+ }
97
+
98
+ export async function printWranglerBanner() {
99
+ // Let's not print this in tests
100
+ if (typeof jest !== "undefined") {
101
+ return;
102
+ }
103
+
104
+ const text = ` ⛅️ wrangler ${wranglerVersion} ${await updateCheck()}`;
105
+
106
+ logger.log(
107
+ text +
108
+ "\n" +
109
+ (supportsColor.stdout
110
+ ? chalk.hex("#FF8800")("-".repeat(text.length))
111
+ : "-".repeat(text.length))
112
+ );
113
+ }
114
+
115
+ export function isLegacyEnv(config: Config): boolean {
116
+ // We only read from config here, because we've already accounted for
117
+ // args["legacy-env"] in https://github.com/cloudflare/wrangler2/blob/b24aeb5722370c2e04bce97a84a1fa1e55725d79/packages/wrangler/src/config/validation.ts#L94-L98
118
+ return config.legacy_env;
119
+ }
120
+
121
+ export function getScriptName(
122
+ args: { name: string | undefined; env: string | undefined },
123
+ config: Config
124
+ ): string | undefined {
125
+ if (args.name && isLegacyEnv(config) && args.env) {
126
+ throw new CommandLineArgsError(
127
+ "In legacy environment mode you cannot use --name and --env together. If you want to specify a Worker name for a specific environment you can add the following to your wrangler.toml config:" +
128
+ `
129
+ [env.${args.env}]
130
+ name = "${args.name}"
131
+ `
132
+ );
133
+ }
134
+
135
+ return args.name ?? config.name;
136
+ }
137
+
138
+ /**
139
+ * Alternative to the getScriptName() because special Legacy cases allowed "name", and "env" together in Wrangler1
140
+ */
141
+ export function getLegacyScriptName(
142
+ args: { name: string | undefined; env: string | undefined },
143
+ config: Config
144
+ ) {
145
+ return args.name && args.env && isLegacyEnv(config)
146
+ ? `${args.name}-${args.env}`
147
+ : args.name ?? config.name;
115
148
  }
116
149
 
117
150
  // a helper to demand one of a set of options
118
151
  // via https://github.com/yargs/yargs/issues/1093#issuecomment-491299261
119
- function demandOneOfOption(...options: string[]) {
120
- return function (argv: Yargs.Arguments) {
121
- const count = options.filter((option) => argv[option]).length;
122
- const lastOption = options.pop();
123
-
124
- if (count === 0) {
125
- throw new CommandLineArgsError(
126
- `Exactly one of the arguments ${options.join(
127
- ", "
128
- )} and ${lastOption} is required`
129
- );
130
- } else if (count > 1) {
131
- throw new CommandLineArgsError(
132
- `Arguments ${options.join(
133
- ", "
134
- )} and ${lastOption} are mutually exclusive`
135
- );
136
- }
137
-
138
- return true;
139
- };
152
+ export function demandOneOfOption(...options: string[]) {
153
+ return function (argv: Yargs.Arguments) {
154
+ const count = options.filter((option) => argv[option]).length;
155
+ const lastOption = options.pop();
156
+
157
+ if (count === 0) {
158
+ throw new CommandLineArgsError(
159
+ `Exactly one of the arguments ${options.join(
160
+ ", "
161
+ )} and ${lastOption} is required`
162
+ );
163
+ } else if (count > 1) {
164
+ throw new CommandLineArgsError(
165
+ `Arguments ${options.join(
166
+ ", "
167
+ )} and ${lastOption} are mutually exclusive`
168
+ );
169
+ }
170
+
171
+ return true;
172
+ };
140
173
  }
141
174
 
142
- class CommandLineArgsError extends Error {}
143
- class DeprecationError extends Error {}
144
- class NotImplementedError extends Error {}
175
+ export class CommandLineArgsError extends Error {}
176
+
177
+ export function createCLIParser(argv: string[]) {
178
+ // Type check result against CommonYargsOptions to make sure we've included
179
+ // all common options
180
+ const wrangler: Yargs.Argv<CommonYargsOptions> = makeCLI(argv)
181
+ .strict()
182
+ // We handle errors ourselves in a try-catch around `yargs.parse`.
183
+ // If you want the "help info" to be displayed then throw an instance of `CommandLineArgsError`.
184
+ // Otherwise we just log the error that was thrown without any "help info".
185
+ .showHelpOnFail(false)
186
+ .fail((msg, error) => {
187
+ if (!error || error.name === "YError") {
188
+ // If there is no error or the error is a "YError", then this came from yargs own validation
189
+ // Wrap it in a `CommandLineArgsError` so that we can handle it appropriately further up.
190
+ error = new CommandLineArgsError(msg);
191
+ }
192
+ throw error;
193
+ })
194
+ .scriptName("wrangler")
195
+ .wrap(null)
196
+ // Define global options here, so they get included in the `Argv` type of
197
+ // the `wrangler` variable
198
+ .version(false)
199
+ .option("v", {
200
+ describe: "Show version number",
201
+ alias: "version",
202
+ type: "boolean",
203
+ })
204
+ .option("config", {
205
+ alias: "c",
206
+ describe: "Path to .toml configuration file",
207
+ type: "string",
208
+ requiresArg: true,
209
+ })
210
+ .option("env", {
211
+ alias: "e",
212
+ describe: "Environment to use for operations and .env files",
213
+ type: "string",
214
+ requiresArg: true,
215
+ })
216
+ .check((args) => {
217
+ // Grab locally specified env params from `.env` file
218
+ const loaded = loadDotEnv(".env", args.env);
219
+ for (const [key, value] of Object.entries(loaded?.parsed ?? {})) {
220
+ if (!(key in process.env)) process.env[key] = value;
221
+ }
222
+ return true;
223
+ });
224
+
225
+ wrangler.group(["config", "env", "help", "version"], "Flags:");
226
+ wrangler.help().alias("h", "help");
227
+
228
+ // Default help command that supports the subcommands
229
+ const subHelp: Yargs.CommandModule<CommonYargsOptions, CommonYargsOptions> = {
230
+ command: ["*"],
231
+ handler: async (args) => {
232
+ setImmediate(() =>
233
+ wrangler.parse([...args._.map((a) => `${a}`), "--help"])
234
+ );
235
+ },
236
+ };
237
+ wrangler.command(
238
+ ["*"],
239
+ false,
240
+ () => {},
241
+ async (args) => {
242
+ if (args._.length > 0) {
243
+ throw new CommandLineArgsError(`Unknown command: ${args._}.`);
244
+ } else {
245
+ // args.v will exist and be true in the case that no command is called, and the -v
246
+ // option is present. This is to allow for running asynchronous printWranglerBanner
247
+ // in the version command.
248
+ if (args.v) {
249
+ if (process.stdout.isTTY) {
250
+ await printWranglerBanner();
251
+ } else {
252
+ logger.log(wranglerVersion);
253
+ }
254
+ } else {
255
+ wrangler.showHelp("log");
256
+ }
257
+ }
258
+ }
259
+ );
260
+
261
+ // You will note that we use the form for all commands where we use the builder function
262
+ // to define options and subcommands.
263
+ // Further we return the result of this builder even though it's not completely necessary.
264
+ // The reason is that it's required for type inference of the args in the handle function.
265
+ // I wish we could enforce this pattern, but this comment will have to do for now.
266
+ // (It's also annoying that choices[] doesn't get inferred as an enum. 🤷‍♂.)
267
+
268
+ // [DEPRECATED] generate
269
+ wrangler.command(
270
+ // we definitely want to move away from us cloning github templates
271
+ // we can do something better here, let's see
272
+ "generate [name] [template]",
273
+ false,
274
+ generateOptions,
275
+ generateHandler
276
+ );
277
+
278
+ // init
279
+ wrangler.command(
280
+ "init [name]",
281
+ "📥 Create a wrangler.toml configuration file",
282
+ initOptions,
283
+ initHandler
284
+ );
285
+
286
+ // [DEPRECATED] build
287
+ wrangler.command("build", false, buildOptions, buildHandler);
288
+
289
+ // [DEPRECATED] config
290
+ wrangler.command("config", false, noOpOptions, configHandler);
291
+
292
+ // dev
293
+ wrangler.command(
294
+ "dev [script]",
295
+ "👂 Start a local server for developing your worker",
296
+ devOptions,
297
+ devHandler
298
+ );
299
+
300
+ // publish
301
+ wrangler.command(
302
+ "publish [script]",
303
+ "🆙 Publish your Worker to Cloudflare.",
304
+ publishOptions,
305
+ publishHandler
306
+ );
307
+
308
+ // delete
309
+ wrangler.command(
310
+ "delete [script]",
311
+ "🗑 Delete your Worker from Cloudflare.",
312
+ deleteOptions,
313
+ deleteHandler
314
+ );
315
+
316
+ // tail
317
+ wrangler.command(
318
+ "tail [worker]",
319
+ "🦚 Starts a log tailing session for a published Worker.",
320
+ tailOptions,
321
+ tailHandler
322
+ );
323
+
324
+ // [DEPRECATED] preview
325
+ wrangler.command(
326
+ "preview [method] [body]",
327
+ false,
328
+ previewOptions,
329
+ previewHandler
330
+ );
331
+
332
+ // [DEPRECATED] route
333
+ wrangler.command(
334
+ "route",
335
+ false, // I think we want to hide this command
336
+ // "➡️ List or delete worker routes",
337
+ (routeYargs) => {
338
+ return route(routeYargs);
339
+ },
340
+ routeHandler
341
+ );
342
+
343
+ // [DEPRECATED] subdomain
344
+ wrangler.command(
345
+ "subdomain [name]",
346
+ false,
347
+ // "👷 Create or change your workers.dev subdomain.",
348
+ subdomainOptions,
349
+ subdomainHandler
350
+ );
351
+
352
+ // secret
353
+ wrangler.command(
354
+ "secret",
355
+ "🤫 Generate a secret that can be referenced in a Worker",
356
+ (secretYargs) => {
357
+ return secret(secretYargs.command(subHelp));
358
+ }
359
+ );
360
+
361
+ wrangler.command(
362
+ "secret:bulk <json>",
363
+ "🗄️ Bulk upload secrets for a Worker",
364
+ secretBulkOptions,
365
+ secretBulkHandler
366
+ );
367
+
368
+ // kv namespace
369
+ wrangler.command(
370
+ "kv:namespace",
371
+ "🗂️ Interact with your Workers KV Namespaces",
372
+ (namespaceYargs) => {
373
+ return kvNamespace(namespaceYargs.command(subHelp));
374
+ }
375
+ );
376
+
377
+ // kv key
378
+ wrangler.command(
379
+ "kv:key",
380
+ "🔑 Individually manage Workers KV key-value pairs",
381
+ (keyYargs) => {
382
+ return kvKey(keyYargs.command(subHelp));
383
+ }
384
+ );
385
+
386
+ // kv bulk
387
+ wrangler.command(
388
+ "kv:bulk",
389
+ "💪 Interact with multiple Workers KV key-value pairs at once",
390
+ (bulkYargs) => {
391
+ return kvBulk(bulkYargs.command(subHelp));
392
+ }
393
+ );
394
+
395
+ // pages
396
+ wrangler.command("pages", "⚡️ Configure Cloudflare Pages", (pagesYargs) => {
397
+ return pages(pagesYargs.command(subHelp));
398
+ });
399
+
400
+ // queues
401
+ wrangler.command("queues", "🆀 Configure Workers Queues", (queuesYargs) => {
402
+ return queues(queuesYargs.command(subHelp));
403
+ });
404
+
405
+ // r2
406
+ wrangler.command("r2", "📦 Interact with an R2 store", (r2Yargs) => {
407
+ return r2(r2Yargs.command(subHelp));
408
+ });
409
+
410
+ // dispatch-namespace
411
+ wrangler.command(
412
+ "dispatch-namespace",
413
+ "📦 Interact with a dispatch namespace",
414
+ (workerNamespaceYargs) => {
415
+ return workerNamespaceCommands(workerNamespaceYargs, subHelp);
416
+ }
417
+ );
418
+
419
+ // d1
420
+ wrangler.command("d1", "🗄 Interact with a D1 database", (d1Yargs) => {
421
+ return d1(d1Yargs.command(subHelp));
422
+ });
423
+
424
+ // pubsub
425
+ wrangler.command(
426
+ "pubsub",
427
+ "📮 Interact and manage Pub/Sub Brokers",
428
+ (pubsubYargs) => {
429
+ return pubSubCommands(pubsubYargs, subHelp);
430
+ }
431
+ );
432
+
433
+ /**
434
+ * User Group: login, logout, and whoami
435
+ * TODO: group commands into User group similar to .group() for flags in yargs
436
+ */
437
+ // login
438
+ wrangler.command(
439
+ // this needs scopes as an option?
440
+ "login",
441
+ "🔓 Login to Cloudflare",
442
+ (yargs) => {
443
+ // TODO: This needs some copy editing
444
+ // I mean, this entire app does, but this too.
445
+ return yargs
446
+ .option("scopes-list", {
447
+ describe: "List all the available OAuth scopes with descriptions",
448
+ })
449
+ .option("scopes", {
450
+ describe: "Pick the set of applicable OAuth scopes when logging in",
451
+ array: true,
452
+ type: "string",
453
+ requiresArg: true,
454
+ });
455
+
456
+ // TODO: scopes
457
+ },
458
+ async (args) => {
459
+ await printWranglerBanner();
460
+ if (args["scopes-list"]) {
461
+ listScopes();
462
+ return;
463
+ }
464
+ if (args.scopes) {
465
+ if (args.scopes.length === 0) {
466
+ // don't allow no scopes to be passed, that would be weird
467
+ listScopes();
468
+ return;
469
+ }
470
+ if (!validateScopeKeys(args.scopes)) {
471
+ throw new CommandLineArgsError(
472
+ `One of ${args.scopes} is not a valid authentication scope. Run "wrangler login --list-scopes" to see the valid scopes.`
473
+ );
474
+ }
475
+ await login({ scopes: args.scopes });
476
+ return;
477
+ }
478
+ await login();
479
+ const config = readConfig(args.config as ConfigPath, args);
480
+ await metrics.sendMetricsEvent("login user", {
481
+ sendMetrics: config.send_metrics,
482
+ });
483
+
484
+ // TODO: would be nice if it optionally saved login
485
+ // credentials inside node_modules/.cache or something
486
+ // this way you could have multiple users on a single machine
487
+ }
488
+ );
489
+
490
+ // logout
491
+ wrangler.command(
492
+ // this needs scopes as an option?
493
+ "logout",
494
+ "🚪 Logout from Cloudflare",
495
+ () => {},
496
+ async () => {
497
+ await printWranglerBanner();
498
+ await logout();
499
+ const config = readConfig(undefined, {});
500
+ await metrics.sendMetricsEvent("logout user", {
501
+ sendMetrics: config.send_metrics,
502
+ });
503
+ }
504
+ );
505
+
506
+ // whoami
507
+ wrangler.command(
508
+ "whoami",
509
+ "🕵️ Retrieve your user info and test your auth config",
510
+ () => {},
511
+ async () => {
512
+ await printWranglerBanner();
513
+ await whoami();
514
+ const config = readConfig(undefined, {});
515
+ await metrics.sendMetricsEvent("view accounts", {
516
+ sendMetrics: config.send_metrics,
517
+ });
518
+ }
519
+ );
520
+
521
+ // type generation
522
+ wrangler.command(
523
+ "types",
524
+ "📝 Generate types from bindings & module rules in config",
525
+ () => {},
526
+ async () => {
527
+ await printWranglerBanner();
528
+ const config = readConfig(undefined, {});
529
+
530
+ const configBindings: PartialConfigToDTS = {
531
+ kv_namespaces: config.kv_namespaces ?? [],
532
+ vars: { ...config.vars },
533
+ wasm_modules: config.wasm_modules,
534
+ text_blobs: {
535
+ ...config.text_blobs,
536
+ },
537
+ data_blobs: config.data_blobs,
538
+ durable_objects: config.durable_objects,
539
+ r2_buckets: config.r2_buckets,
540
+ // @ts-expect-error - We don't want the type generated to inlcude the Beta prefix
541
+ d1_databases: config.d1_databases,
542
+ services: config.services,
543
+ dispatch_namespaces: config.dispatch_namespaces,
544
+ logfwdr: config.logfwdr,
545
+ unsafe: config.unsafe?.bindings,
546
+ rules: config.rules,
547
+ };
548
+
549
+ await generateTypes(configBindings, config);
550
+ }
551
+ );
552
+
553
+ //deployments
554
+ const deploymentsWarning =
555
+ "🚧`wrangler deployments` is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose";
556
+ wrangler.command(
557
+ "deployments",
558
+ false,
559
+ // "🚢 Logs the 10 most recent deployments with 'Version ID', 'Version number','Author email', 'Created on' and 'Latest deploy'",
560
+ (yargs) => {
561
+ yargs
562
+ .option("name", {
563
+ describe: "The name of your worker",
564
+ type: "string",
565
+ })
566
+ .epilogue(deploymentsWarning);
567
+ },
568
+ async (deploymentsYargs: ArgumentsCamelCase<{ name: string }>) => {
569
+ await printWranglerBanner();
570
+ const config = readConfig(
571
+ deploymentsYargs.config as ConfigPath,
572
+ deploymentsYargs
573
+ );
574
+ const accountId = await requireAuth(config);
575
+ const scriptName = getScriptName(
576
+ { name: deploymentsYargs.name, env: undefined },
577
+ config
578
+ );
579
+
580
+ logger.log(`${deploymentsWarning}\n`);
581
+ await deployments(accountId, scriptName);
582
+ }
583
+ );
584
+
585
+ // This set to false to allow overwrite of default behaviour
586
+ wrangler.version(false);
587
+
588
+ // version
589
+ wrangler.command(
590
+ "version",
591
+ false,
592
+ () => {},
593
+ async () => {
594
+ if (process.stdout.isTTY) {
595
+ await printWranglerBanner();
596
+ } else {
597
+ logger.log(wranglerVersion);
598
+ }
599
+ }
600
+ );
601
+
602
+ wrangler.exitProcess(false);
603
+
604
+ return wrangler;
605
+ }
145
606
 
146
607
  export async function main(argv: string[]): Promise<void> {
147
- const wrangler = makeCLI(argv)
148
- // We handle errors ourselves in a try-catch around `yargs.parse`.
149
- // If you want the "help info" to be displayed then throw an instance of `CommandLineArgsError`.
150
- // Otherwise we just log the error that was thrown without any "help info".
151
- .showHelpOnFail(false)
152
- .fail((msg, error) => {
153
- if (!error) {
154
- // If there is only a `msg` then this came from yargs own validation, so wrap in a `CommandLineArgsError`.
155
- error = new CommandLineArgsError(msg);
156
- }
157
- throw error;
158
- })
159
- .scriptName("wrangler")
160
- .wrap(null);
161
-
162
- // the default is to simply print the help menu
163
- wrangler.command(
164
- ["*"],
165
- false,
166
- () => {},
167
- (args) => {
168
- if (args._.length > 0) {
169
- throw new CommandLineArgsError(`Unknown command: ${args._}.`);
170
- } else {
171
- wrangler.showHelp("log");
172
- }
173
- }
174
- );
175
-
176
- // You will note that we use the form for all commands where we use the builder function
177
- // to define options and subcommands.
178
- // Further we return the result of this builder even though it's not completely necessary.
179
- // The reason is that it's required for type inference of the args in the handle function.
180
- // I wish we could enforce this pattern, but this comment will have to do for now.
181
- // (It's also annoying that choices[] doesn't get inferred as an enum. 🤷‍♂.)
182
-
183
- // [DEPRECATED] generate
184
- wrangler.command(
185
- // we definitely want to move away from us cloning github templates
186
- // we can do something better here, let's see
187
- "generate [name] [template]",
188
- false,
189
- (yargs) => {
190
- return yargs
191
- .positional("name", {
192
- describe: "Name of the Workers project",
193
- default: "worker",
194
- })
195
- .positional("template", {
196
- describe: "a link to a GitHub template",
197
- default: "https://github.com/cloudflare/worker-template",
198
- });
199
- },
200
- () => {
201
- // "👯 [DEPRECATED]. Scaffold a Cloudflare Workers project from a public GitHub repository.",
202
- throw new DeprecationError(
203
- "`wrangler generate` has been deprecated, please refer to TODO://some/path for alternatives"
204
- );
205
- }
206
- );
207
-
208
- // init
209
- wrangler.command(
210
- "init [name]",
211
- "📥 Create a wrangler.toml configuration file",
212
- (yargs) => {
213
- return yargs.positional("name", {
214
- describe: "The name of your worker.",
215
- type: "string",
216
- });
217
- },
218
- async (args) => {
219
- if ("type" in args) {
220
- let message = "The --type option is no longer supported.";
221
- if (args.type === "webpack") {
222
- message +=
223
- "\nIf you wish to use webpack then you will need to create a custom build.";
224
- // TODO: Add a link to docs
225
- }
226
- throw new CommandLineArgsError(message);
227
- }
228
-
229
- const destination = path.join(process.cwd(), "wrangler.toml");
230
- if (fs.existsSync(destination)) {
231
- console.warn(`${destination} file already exists!`);
232
- const result = await confirm(
233
- "Do you want to continue initializing this project?"
234
- );
235
- if (!result) {
236
- return;
237
- }
238
- }
239
-
240
- const compatibilityDate = new Date().toISOString().substring(0, 10);
241
- try {
242
- await writeFile(
243
- destination,
244
- `compatibility_date = "${compatibilityDate}"` + "\n"
245
- );
246
- console.log(`✨ Successfully created wrangler.toml`);
247
- // TODO: suggest next steps?
248
- } catch (err) {
249
- throw new Error(
250
- `Failed to create wrangler.toml.\n${err.message ?? err}`
251
- );
252
- }
253
-
254
- // if no package.json, ask, and if yes, create one
255
- let pathToPackageJson = await findUp("package.json");
256
-
257
- if (!pathToPackageJson) {
258
- if (
259
- await confirm("No package.json found. Would you like to create one?")
260
- ) {
261
- await writeFile(
262
- path.join(process.cwd(), "package.json"),
263
- JSON.stringify(
264
- {
265
- name: "worker",
266
- version: "0.0.1",
267
- },
268
- null,
269
- " "
270
- ) + "\n"
271
- );
272
- console.log(`✨ Created package.json`);
273
- pathToPackageJson = path.join(process.cwd(), "package.json");
274
- } else {
275
- return;
276
- }
277
- }
278
-
279
- // if workers-types doesn't exist as a dependency
280
- // offer to install it
281
- // and make a tsconfig?
282
- let pathToTSConfig = await findUp("tsconfig.json");
283
- if (!pathToTSConfig) {
284
- if (await confirm("Would you like to use typescript?")) {
285
- await writeFile(
286
- path.join(process.cwd(), "tsconfig.json"),
287
- JSON.stringify(
288
- {
289
- compilerOptions: {
290
- target: "esnext",
291
- module: "esnext",
292
- moduleResolution: "node",
293
- esModuleInterop: true,
294
- allowJs: true,
295
- allowSyntheticDefaultImports: true,
296
- isolatedModules: true,
297
- noEmit: true,
298
- lib: ["esnext"],
299
- jsx: "react",
300
- resolveJsonModule: true,
301
- types: ["@cloudflare/workers-types"],
302
- },
303
- },
304
- null,
305
- " "
306
- ) + "\n"
307
- );
308
- await execa("npm", [
309
- "install",
310
- "@cloudflare/workers-types",
311
- "--save-dev",
312
- ]);
313
- console.log(
314
- `✨ Created tsconfig.json, installed @cloudflare/workers-types into devDependencies`
315
- );
316
- pathToTSConfig = path.join(process.cwd(), "tsconfig.json");
317
- }
318
- }
319
- }
320
- );
321
-
322
- // build
323
- wrangler.command(
324
- "build",
325
- false,
326
- (yargs) => {
327
- return yargs.option("env", {
328
- describe: "Perform on a specific environment",
329
- });
330
- },
331
- () => {
332
- // "[DEPRECATED] 🦀 Build your project (if applicable)",
333
- throw new DeprecationError(
334
- "`wrangler build` has been deprecated, please refer to TODO://some/path for alternatives"
335
- );
336
- }
337
- );
338
-
339
- // login
340
- wrangler.command(
341
- // this needs scopes as an option?
342
- "login",
343
- false, // we don't need to show this in the menu
344
- // "🔓 Login to Cloudflare",
345
- (yargs) => {
346
- // TODO: This needs some copy editing
347
- // I mean, this entire app does, but this too.
348
- return yargs
349
- .option("scopes-list", {
350
- describe: "list all the available OAuth scopes with descriptions.",
351
- })
352
- .option("scopes", {
353
- describe: "allows to choose your set of OAuth scopes.",
354
- array: true,
355
- type: "string",
356
- });
357
-
358
- // TODO: scopes
359
- },
360
- async (args) => {
361
- if (args["scopes-list"]) {
362
- listScopes();
363
- return;
364
- }
365
- if (args.scopes) {
366
- if (args.scopes.length === 0) {
367
- // don't allow no scopes to be passed, that would be weird
368
- listScopes();
369
- return;
370
- }
371
- await login({ scopes: args.scopes });
372
- return;
373
- }
374
- await login();
375
-
376
- // TODO: would be nice if it optionally saved login
377
- // credentials inside node_modules/.cache or something
378
- // this way you could have multiple users on a single machine
379
- }
380
- );
381
-
382
- // logout
383
- wrangler.command(
384
- // this needs scopes as an option?
385
- "logout",
386
- false, // we don't need to show this in the menu
387
- // "🚪 Logout from Cloudflare",
388
- () => {},
389
- async () => {
390
- await logout();
391
- }
392
- );
393
-
394
- // whoami
395
- wrangler.command(
396
- "whoami",
397
- false, // we don't need to show this the menu
398
- // "🕵️ Retrieve your user info and test your auth config",
399
- () => {},
400
- (args) => {
401
- console.log(":whoami", args);
402
- }
403
- );
404
-
405
- // config
406
- wrangler.command(
407
- "config",
408
- false,
409
- () => {},
410
- () => {
411
- // "🕵️ Authenticate Wrangler with a Cloudflare API Token",
412
- throw new DeprecationError(
413
- "`wrangler config` has been deprecated, please refer to TODO://some/path for alternatives"
414
- );
415
- }
416
- );
417
-
418
- // dev
419
- wrangler.command(
420
- "dev <filename>",
421
- "👂 Start a local server for developing your worker",
422
- (yargs) => {
423
- return yargs
424
- .positional("filename", { describe: "entry point", type: "string" })
425
- .option("name", {
426
- describe: "name of the script",
427
- type: "string",
428
- })
429
- .option("format", {
430
- choices: ["modules", "service-worker"] as const,
431
- describe: "Choose an entry type",
432
- })
433
- .option("env", {
434
- describe: "Perform on a specific environment",
435
- type: "string",
436
- // TODO: get choices for the toml file?
437
- })
438
- .option("compatibility-date", {
439
- describe: "Date to use for compatibility checks",
440
- type: "string",
441
- })
442
- .option("compatibility-flags", {
443
- describe: "Flags to use for compatibility checks",
444
- type: "array",
445
- alias: "compatibility-flag",
446
- })
447
- .option("latest", {
448
- describe: "Use the latest version of the worker runtime",
449
- type: "boolean",
450
- default: true,
451
- })
452
- .option("ip", {
453
- describe: "IP address to listen on",
454
- type: "string",
455
- default: "127.0.0.1",
456
- })
457
- .option("port", {
458
- describe: "Port to listen on, defaults to 8787",
459
- type: "number",
460
- default: 8787,
461
- })
462
- .option("host", {
463
- type: "string",
464
- describe:
465
- "Host to forward requests to, defaults to the zone of project",
466
- })
467
- .option("local-protocol", {
468
- default: "http",
469
- describe: "Protocol to listen to requests on, defaults to http.",
470
- choices: ["http", "https"],
471
- })
472
- .option("experimental-public", {
473
- describe: "Static assets to be served",
474
- type: "string",
475
- })
476
- .option("site", {
477
- describe: "Root folder of static assets for Workers Sites",
478
- type: "string",
479
- })
480
- .option("upstream-protocol", {
481
- default: "https",
482
- describe:
483
- "Protocol to forward requests to host on, defaults to https.",
484
- choices: ["http", "https"],
485
- })
486
- .option("jsx-factory", {
487
- describe: "The function that is called for each JSX element",
488
- type: "string",
489
- })
490
- .option("jsx-fragment", {
491
- describe: "The function that is called for each JSX fragment",
492
- type: "string",
493
- });
494
- },
495
- async (args) => {
496
- const { filename, format } = args;
497
- const config = args.config as Config;
498
-
499
- if (args["experimental-public"]) {
500
- console.warn(
501
- "🚨 The --experimental-public field is experimental and will change in the future."
502
- );
503
- }
504
-
505
- if (args.public) {
506
- throw new Error(
507
- "🚨 The --public field has been renamed to --experimental-public, and will change behaviour in the future."
508
- );
509
- }
510
-
511
- // -- snip, extract --
512
-
513
- if (!args.local) {
514
- const loggedIn = await loginOrRefreshIfRequired();
515
- if (!loggedIn) {
516
- // didn't login, let's just quit
517
- console.log("Did not login, quitting...");
518
- return;
519
- }
520
- if (!config.account_id) {
521
- config.account_id = await getAccountId();
522
- if (!config.account_id) {
523
- throw new Error("No account id found, quitting...");
524
- }
525
- }
526
- }
527
-
528
- // -- snip, end --
529
-
530
- const envRootObj = args.env ? config.env[args.env] || {} : config;
531
-
532
- // TODO: this error shouldn't actually happen,
533
- // but we haven't fixed it internally yet
534
- if ("durable_objects" in envRootObj) {
535
- if (!(args.name || config.name)) {
536
- console.warn(
537
- 'A worker with durable objects needs to be named, or it may not work as expected. Add a "name" into wrangler.toml, or pass it in the command line with --name.'
538
- );
539
- }
540
- // TODO: if not already published, publish a draft worker
541
- }
542
-
543
- const { waitUntilExit } = render(
544
- <Dev
545
- name={args.name || config.name}
546
- entry={path.relative(process.cwd(), filename)}
547
- buildCommand={config.build || {}}
548
- format={format}
549
- initialMode={args.local ? "local" : "remote"}
550
- jsxFactory={args["jsx-factory"] || envRootObj?.jsx_factory}
551
- jsxFragment={args["jsx-fragment"] || envRootObj?.jsx_fragment}
552
- accountId={config.account_id}
553
- site={args.site || config.site?.bucket}
554
- port={args.port || config.dev?.port}
555
- public={args["experimental-public"]}
556
- compatibilityDate={
557
- args["compatibility-date"] ||
558
- config.compatibility_date ||
559
- new Date().toISOString().substring(0, 10)
560
- }
561
- compatibilityFlags={
562
- (args["compatibility-flags"] as string[]) ||
563
- config.compatibility_flags
564
- }
565
- usageModel={config.usage_model}
566
- bindings={{
567
- kv_namespaces: envRootObj.kv_namespaces?.map(
568
- ({ binding, preview_id, id: _id }) => {
569
- // In `dev`, we make folks use a separate kv namespace called
570
- // `preview_id` instead of `id` so that they don't
571
- // break production data. So here we check that a `preview_id`
572
- // has actually been configured.
573
- // This whole block of code will be obsoleted in the future
574
- // when we have copy-on-write for previews on edge workers.
575
- if (!preview_id) {
576
- // TODO: This error has to be a _lot_ better, ideally just asking
577
- // to create a preview namespace for the user automatically
578
- throw new Error(
579
- `In development, you should use a separate kv namespace than the one you'd use in production. Please create a new kv namespace with "wrangler kv:namespace create <name> --preview" and add its id as preview_id to the kv_namespace "${binding}" in your wrangler.toml`
580
- ); // Ugh, I really don't like this message very much
581
- }
582
- return {
583
- binding,
584
- id: preview_id,
585
- };
586
- }
587
- ),
588
- vars: envRootObj.vars,
589
- durable_objects: envRootObj.durable_objects,
590
- services: envRootObj.experimental_services,
591
- }}
592
- />
593
- );
594
- await waitUntilExit();
595
- }
596
- );
597
-
598
- // publish
599
- wrangler.command(
600
- "publish [script]",
601
- "🆙 Publish your Worker to Cloudflare.",
602
- (yargs) => {
603
- return yargs
604
- .option("env", {
605
- type: "string",
606
- describe: "Perform on a specific environment",
607
- })
608
- .positional("script", {
609
- describe: "script to upload",
610
- type: "string",
611
- })
612
- .option("name", {
613
- describe: "name to use when uploading",
614
- type: "string",
615
- })
616
- .option("compatibility-date", {
617
- describe: "Date to use for compatibility checks",
618
- type: "string",
619
- })
620
- .option("compatibility-flags", {
621
- describe: "Flags to use for compatibility checks",
622
- type: "array",
623
- alias: "compatibility-flag",
624
- })
625
- .option("latest", {
626
- describe: "Use the latest version of the worker runtime",
627
- type: "boolean",
628
- default: false,
629
- })
630
- .option("experimental-public", {
631
- describe: "Static assets to be served",
632
- type: "string",
633
- })
634
- .option("site", {
635
- describe: "Root folder of static assets for Workers Sites",
636
- type: "string",
637
- })
638
- .option("triggers", {
639
- describe: "cron schedules to attach",
640
- alias: ["schedule", "schedules"],
641
- type: "array",
642
- })
643
- .option("routes", {
644
- describe: "routes to upload",
645
- alias: "route",
646
- type: "array",
647
- })
648
- .option("services", {
649
- describe: "experimental support for services",
650
- type: "boolean",
651
- default: "false",
652
- hidden: true,
653
- })
654
- .option("jsx-factory", {
655
- describe: "The function that is called for each JSX element",
656
- type: "string",
657
- })
658
- .option("jsx-fragment", {
659
- describe: "The function that is called for each JSX fragment",
660
- type: "string",
661
- });
662
- },
663
- async (args) => {
664
- if (args.local) {
665
- throw new NotImplementedError(
666
- "🚫 Local publishing is not yet supported"
667
- );
668
- }
669
-
670
- if (args["experimental-public"]) {
671
- console.warn(
672
- "🚨 The --experimental-public field is experimental and will change in the future."
673
- );
674
- }
675
- if (args.public) {
676
- throw new Error(
677
- "🚨 The --public field has been renamed to --experimental-public, and will change behaviour in the future."
678
- );
679
- }
680
-
681
- const config = args.config as Config;
682
-
683
- if (args.latest) {
684
- console.warn(
685
- "⚠️ 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"
686
- );
687
- }
688
-
689
- // -- snip, extract --
690
- if (!args.local) {
691
- const loggedIn = await loginOrRefreshIfRequired();
692
- if (!loggedIn) {
693
- // didn't login, let's just quit
694
- console.log("Did not login, quitting...");
695
- return;
696
- }
697
- if (!config.account_id) {
698
- config.account_id = await getAccountId();
699
- if (!config.account_id) {
700
- throw new Error("No account id found, quitting...");
701
- }
702
- }
703
- }
704
-
705
- // -- snip, end --
706
-
707
- await publish({
708
- config: args.config as Config,
709
- name: args.name,
710
- script: args.script,
711
- env: args.env,
712
- compatibilityDate: args.latest
713
- ? new Date().toISOString().substring(0, 10)
714
- : args["compatibility-date"],
715
- compatibilityFlags: args["compatibility-flags"] as string[],
716
- triggers: args.triggers,
717
- jsxFactory: args["jsx-factory"],
718
- jsxFragment: args["jsx-fragment"],
719
- routes: args.routes,
720
- public: args["experimental-public"],
721
- site: args.site,
722
- });
723
- }
724
- );
725
-
726
- // tail
727
- wrangler.command(
728
- "tail [name]",
729
- "🦚 Starts a log tailing session for a deployed Worker.",
730
- (yargs) => {
731
- return (
732
- yargs
733
- .positional("name", {
734
- describe: "name of the worker",
735
- type: "string",
736
- })
737
- // TODO: auto-detect if this should be json or pretty based on atty
738
- .option("format", {
739
- default: "json",
740
- choices: ["json", "pretty"],
741
- describe: "The format of log entries",
742
- })
743
- .option("status", {
744
- choices: ["ok", "error", "canceled"],
745
- describe: "Filter by invocation status",
746
- })
747
- .option("header", {
748
- type: "string",
749
- describe: "Filter by HTTP header",
750
- })
751
- .option("method", {
752
- type: "string",
753
- describe: "Filter by HTTP method",
754
- })
755
- .option("sampling-rate", {
756
- type: "number",
757
- describe: "Adds a percentage of requests to log sampling rate",
758
- })
759
- .option("search", {
760
- type: "string",
761
- describe: "Filter by a text match in console.log messages",
762
- })
763
- .option("env", {
764
- type: "string",
765
- describe: "Perform on a specific environment",
766
- })
767
- );
768
- // TODO: filter by client ip, which can be 'self' or an ip address
769
- },
770
- async (args) => {
771
- const config = args.config as Config;
772
-
773
- if (!(args.name || config.name)) {
774
- throw new Error("Missing script name");
775
- }
776
- const scriptName = `${args.name || config.name}${
777
- args.env ? `-${args.env}` : ""
778
- }`;
779
-
780
- // -- snip, extract --
781
-
782
- if (!args.local) {
783
- const loggedIn = await loginOrRefreshIfRequired();
784
- if (!loggedIn) {
785
- // didn't login, let's just quit
786
- console.log("Did not login, quitting...");
787
- return;
788
- }
789
- if (!config.account_id) {
790
- config.account_id = await getAccountId();
791
- if (!config.account_id) {
792
- throw new Error("No account id found, quitting...");
793
- }
794
- }
795
- }
796
-
797
- // -- snip, end --
798
-
799
- const accountId = config.account_id;
800
-
801
- const filters = {
802
- status: args.status as "ok" | "error" | "canceled",
803
- header: args.header,
804
- method: args.method,
805
- "sampling-rate": args["sampling-rate"],
806
- search: args.search,
807
- };
808
-
809
- const { tail, expiration, /* sendHeartbeat, */ deleteTail } =
810
- await createTail(accountId, scriptName, filters);
811
-
812
- console.log(
813
- `successfully created tail, expires at ${expiration.toLocaleString()}`
814
- );
815
-
816
- onExit(async () => {
817
- tail.terminate();
818
- await deleteTail();
819
- });
820
-
821
- tail.on("message", (data) => {
822
- console.log(JSON.stringify(JSON.parse(data.toString()), null, " "));
823
- });
824
-
825
- while (tail.readyState !== tail.OPEN) {
826
- switch (tail.readyState) {
827
- case tail.CONNECTING:
828
- await setTimeout(1000);
829
- break;
830
- case tail.CLOSING:
831
- await setTimeout(1000);
832
- break;
833
- case tail.CLOSED:
834
- process.exit(1);
835
- }
836
- }
837
-
838
- console.log(`Connected to ${scriptName}, waiting for logs...`);
839
- }
840
- );
841
-
842
- // preview
843
- wrangler.command(
844
- "preview [method] [body]",
845
- false,
846
- (yargs) => {
847
- return yargs
848
- .positional("method", {
849
- describe: "Type of request to preview your worker",
850
- choices: ["GET", "POST"],
851
- default: ["GET"],
852
- })
853
- .positional("body", {
854
- type: "string",
855
- describe: "Body string to post to your preview worker request.",
856
- default: "Null",
857
- })
858
- .option("env", {
859
- type: "string",
860
- describe: "Perform on a specific environment",
861
- })
862
- .option("watch", {
863
- default: true,
864
- describe: "Enable live preview",
865
- type: "boolean",
866
- });
867
- },
868
- () => {
869
- // "🔬 [DEPRECATED] Preview your code temporarily on https://cloudflareworkers.com"
870
- throw new DeprecationError(
871
- "`wrangler preview` has been deprecated, please refer to TODO://some/path for alternatives"
872
- );
873
- }
874
- );
875
-
876
- // route
877
- wrangler.command(
878
- "route",
879
- false, // I think we want to hide this command
880
- // "➡️ List or delete worker routes",
881
- (routeYargs) => {
882
- return routeYargs
883
- .command(
884
- "list",
885
- "List a route associated with a zone",
886
- (yargs) => {
887
- return yargs
888
- .option("env", {
889
- type: "string",
890
- describe: "Perform on a specific environment",
891
- })
892
- .option("zone", {
893
- type: "string",
894
- describe: "zone id",
895
- })
896
- .positional("zone", {
897
- describe: "zone id",
898
- type: "string",
899
- });
900
- },
901
- async (args) => {
902
- console.log(":route list", args);
903
- // TODO: use environment (current wrangler doesn't do so?)
904
- const zone = args.zone || (args.config as Config).zone_id;
905
- if (!zone) {
906
- throw new Error("missing zone id");
907
- }
908
-
909
- console.log(await fetchResult(`/zones/${zone}/workers/routes`));
910
- }
911
- )
912
- .command(
913
- "delete <id>",
914
- "Delete a route associated with a zone",
915
- (yargs) => {
916
- return yargs
917
- .positional("id", {
918
- describe: "The hash of the route ID to delete.",
919
- type: "string",
920
- })
921
- .option("zone", {
922
- type: "string",
923
- describe: "zone id",
924
- })
925
- .option("env", {
926
- type: "string",
927
- describe: "Perform on a specific environment",
928
- });
929
- },
930
- async (args) => {
931
- console.log(":route delete", args);
932
- // TODO: use environment (current wrangler doesn't do so?)
933
- const zone = args.zone || (args.config as Config).zone_id;
934
- if (!zone) {
935
- throw new Error("missing zone id");
936
- }
937
-
938
- console.log(
939
- await fetchResult(`/zones/${zone}/workers/routes/${args.id}`, {
940
- method: "DELETE",
941
- })
942
- );
943
- }
944
- );
945
- }
946
- );
947
-
948
- // subdomain
949
- wrangler.command(
950
- "subdomain [name]",
951
- false,
952
- // "👷 Create or change your workers.dev subdomain.",
953
- (yargs) => {
954
- return yargs.positional("name", { type: "string" });
955
- },
956
- () => {
957
- throw new DeprecationError(
958
- "`wrangler subdomain` has been deprecated, please refer to TODO://some/path for alternatives"
959
- );
960
- }
961
- );
962
-
963
- // secret
964
- wrangler.command(
965
- "secret",
966
- "🤫 Generate a secret that can be referenced in the worker script",
967
- (secretYargs) => {
968
- return secretYargs
969
- .command(
970
- "put <key>",
971
- "Create or update a secret variable for a script",
972
- (yargs) => {
973
- return yargs
974
- .positional("key", {
975
- describe: "The variable name to be accessible in the script.",
976
- type: "string",
977
- })
978
- .option("name", {
979
- describe: "name of the script",
980
- type: "string",
981
- })
982
- .option("env", {
983
- type: "string",
984
- describe:
985
- "Binds the secret to the script of the specific environment.",
986
- });
987
- },
988
- async (args) => {
989
- if (args.local) {
990
- throw new NotImplementedError(
991
- "--local not implemented for this command yet"
992
- );
993
- }
994
- const config = args.config as Config;
995
-
996
- // TODO: use environment (how does current wrangler do it?)
997
- const scriptName = args.name || config.name;
998
- if (!scriptName) {
999
- throw new Error("Missing script name");
1000
- }
1001
-
1002
- // -- snip, extract --
1003
-
1004
- if (!args.local) {
1005
- const loggedIn = await loginOrRefreshIfRequired();
1006
- if (!loggedIn) {
1007
- // didn't login, let's just quit
1008
- console.log("Did not login, quitting...");
1009
- return;
1010
- }
1011
- if (!config.account_id) {
1012
- config.account_id = await getAccountId();
1013
- if (!config.account_id) {
1014
- throw new Error("No account id found, quitting...");
1015
- }
1016
- }
1017
- }
1018
-
1019
- // -- snip, end --
1020
-
1021
- const secretValue = await prompt(
1022
- "Enter a secret value:",
1023
- "password"
1024
- );
1025
- async function submitSecret() {
1026
- return await fetchResult(
1027
- `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets/`,
1028
- {
1029
- method: "PUT",
1030
- headers: { "Content-Type": "application/json" },
1031
- body: JSON.stringify({
1032
- name: args.key,
1033
- text: secretValue,
1034
- type: "secret_text",
1035
- }),
1036
- }
1037
- );
1038
- }
1039
-
1040
- try {
1041
- console.log(await submitSecret());
1042
- } catch (e) {
1043
- if (e.code === 10007) {
1044
- // upload a draft worker
1045
- await fetchResult(
1046
- `/accounts/${config.account_id}/workers/scripts/${scriptName}`,
1047
- {
1048
- method: "PUT",
1049
- // @ts-expect-error TODO: fix this error!
1050
- body: toFormData({
1051
- main: {
1052
- name: scriptName,
1053
- content: `export default { fetch() {} }`,
1054
- type: "esm",
1055
- },
1056
- bindings: {
1057
- kv_namespaces: [],
1058
- vars: {},
1059
- durable_objects: { bindings: [] },
1060
- },
1061
- modules: [],
1062
- }),
1063
- }
1064
- );
1065
-
1066
- // and then try again
1067
- console.log(await submitSecret());
1068
- // TODO: delete the draft worker if this failed too?
1069
- }
1070
- }
1071
- }
1072
- )
1073
- .command(
1074
- "delete <key>",
1075
- "Delete a secret variable from a script",
1076
- (yargs) => {
1077
- return yargs
1078
- .positional("key", {
1079
- describe: "The variable name to be accessible in the script.",
1080
- type: "string",
1081
- })
1082
- .option("name", {
1083
- describe: "name of the script",
1084
- type: "string",
1085
- })
1086
- .option("env", {
1087
- type: "string",
1088
- describe:
1089
- "Binds the secret to the script of the specific environment.",
1090
- });
1091
- },
1092
- async (args) => {
1093
- if (args.local) {
1094
- throw new NotImplementedError(
1095
- "--local not implemented for this command yet"
1096
- );
1097
- }
1098
- const config = args.config as Config;
1099
-
1100
- // TODO: use environment (how does current wrangler do it?)
1101
- const scriptName = args.name || config.name;
1102
- if (!scriptName) {
1103
- throw new Error("Missing script name");
1104
- }
1105
-
1106
- // -- snip, extract --
1107
-
1108
- if (!args.local) {
1109
- const loggedIn = await loginOrRefreshIfRequired();
1110
- if (!loggedIn) {
1111
- // didn't login, let's just quit
1112
- console.log("Did not login, quitting...");
1113
- return;
1114
- }
1115
- if (!config.account_id) {
1116
- config.account_id = await getAccountId();
1117
- if (!config.account_id) {
1118
- throw new Error("No account id found, quitting...");
1119
- }
1120
- }
1121
- }
1122
-
1123
- // -- snip, end --
1124
-
1125
- if (await confirm("Are you sure you want to delete this secret?")) {
1126
- console.log(
1127
- `Deleting the secret ${args.key} on script ${scriptName}.`
1128
- );
1129
-
1130
- console.log(
1131
- await fetchResult(
1132
- `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets/${args.key}`,
1133
- { method: "DELETE" }
1134
- )
1135
- );
1136
- }
1137
- }
1138
- )
1139
- .command(
1140
- "list",
1141
- "List all secrets for a script",
1142
- (yargs) => {
1143
- return yargs
1144
- .option("name", {
1145
- describe: "name of the script",
1146
- type: "string",
1147
- })
1148
- .option("env", {
1149
- type: "string",
1150
- describe:
1151
- "Binds the secret to the script of the specific environment.",
1152
- });
1153
- },
1154
- async (args) => {
1155
- if (args.local) {
1156
- throw new NotImplementedError(
1157
- "--local not implemented for this command yet"
1158
- );
1159
- }
1160
- const config = args.config as Config;
1161
-
1162
- // TODO: use environment (how does current wrangler do it?)
1163
- const scriptName = args.name || config.name;
1164
- if (!scriptName) {
1165
- throw new Error("Missing script name");
1166
- }
1167
-
1168
- // -- snip, extract --
1169
-
1170
- if (!args.local) {
1171
- const loggedIn = await loginOrRefreshIfRequired();
1172
- if (!loggedIn) {
1173
- // didn't login, let's just quit
1174
- console.log("Did not login, quitting...");
1175
- return;
1176
- }
1177
- if (!config.account_id) {
1178
- config.account_id = await getAccountId();
1179
- if (!config.account_id) {
1180
- throw new Error("No account id found, quitting...");
1181
- }
1182
- }
1183
- }
1184
-
1185
- // -- snip, end --
1186
-
1187
- console.log(
1188
- await fetchResult(
1189
- `/accounts/${config.account_id}/workers/scripts/${scriptName}/secrets`
1190
- )
1191
- );
1192
- }
1193
- );
1194
- }
1195
- );
1196
-
1197
- // kv
1198
- // :namespace
1199
- wrangler.command(
1200
- "kv:namespace",
1201
- "🗂️ Interact with your Workers KV Namespaces",
1202
- (namespaceYargs) => {
1203
- return namespaceYargs
1204
- .command(
1205
- "create <namespace>",
1206
- "Create a new namespace",
1207
- (yargs) => {
1208
- return yargs
1209
- .positional("namespace", {
1210
- describe: "The name of the new namespace",
1211
- type: "string",
1212
- })
1213
- .option("env", {
1214
- type: "string",
1215
- describe: "Perform on a specific environment",
1216
- })
1217
- .option("preview", {
1218
- type: "boolean",
1219
- describe: "Interact with a preview namespace",
1220
- });
1221
- },
1222
- async (args) => {
1223
- if (args._.length > 2) {
1224
- const extraArgs = args._.slice(2).join(" ");
1225
- throw new CommandLineArgsError(
1226
- `Unexpected additional positional arguments "${extraArgs}".`
1227
- );
1228
- }
1229
-
1230
- if (!isValidNamespaceBinding(args.namespace)) {
1231
- throw new CommandLineArgsError(
1232
- `The namespace binding name "${args.namespace}" is invalid. It can only have alphanumeric and _ characters, and cannot begin with a number.`
1233
- );
1234
- }
1235
-
1236
- const config = args.config as Config;
1237
- if (!config.name) {
1238
- console.warn(
1239
- "No configured name present, using `worker` as a prefix for the title"
1240
- );
1241
- }
1242
-
1243
- const name = config.name || "worker";
1244
- const environment = args.env ? `-${args.env}` : "";
1245
- const preview = args.preview ? "_preview" : "";
1246
- const title = `${name}${environment}-${args.namespace}${preview}`;
1247
-
1248
- if (args.local) {
1249
- const { Miniflare } = await import("miniflare");
1250
- const mf = new Miniflare({
1251
- kvPersist: (args.kvPersist as string) || true,
1252
- // TODO: these options shouldn't be required
1253
- script: ` `, // has to be a string with at least one char
1254
- });
1255
- await mf.getKVNamespace(title); // this should "create" the namespace
1256
- console.log(`✨ Success! Created KV namespace ${title}`);
1257
- return;
1258
- }
1259
-
1260
- // -- snip, extract --
1261
-
1262
- if (!args.local) {
1263
- const loggedIn = await loginOrRefreshIfRequired();
1264
- if (!loggedIn) {
1265
- // didn't login, let's just quit
1266
- console.log("Did not login, quitting...");
1267
- return;
1268
- }
1269
- if (!config.account_id) {
1270
- config.account_id = await getAccountId();
1271
- if (!config.account_id) {
1272
- throw new Error("No account id found, quitting...");
1273
- }
1274
- }
1275
- }
1276
-
1277
- // -- snip, end --
1278
-
1279
- // TODO: generate a binding name stripping non alphanumeric chars
1280
-
1281
- console.log(`🌀 Creating namespace with title "${title}"`);
1282
- const namespaceId = await createNamespace(config.account_id, title);
1283
-
1284
- console.log("✨ Success!");
1285
- const envString = args.env ? ` under [env.${args.env}]` : "";
1286
- const previewString = args.preview ? "preview_" : "";
1287
- console.log(
1288
- `Add the following to your configuration file in your kv_namespaces array${envString}:`
1289
- );
1290
- console.log(
1291
- `{ binding = "${args.namespace}", ${previewString}id = "${namespaceId}" }`
1292
- );
1293
-
1294
- // TODO: automatically write this block to the wrangler.toml config file??
1295
- }
1296
- )
1297
- .command(
1298
- "list",
1299
- "Outputs a list of all KV namespaces associated with your account id.",
1300
- {},
1301
- async (args) => {
1302
- if (args.local) {
1303
- throw new NotImplementedError(
1304
- `local mode is not yet supported for this command`
1305
- );
1306
- }
1307
-
1308
- const config = args.config as Config;
1309
-
1310
- // -- snip, extract --
1311
-
1312
- if (!args.local) {
1313
- const loggedIn = await loginOrRefreshIfRequired();
1314
- if (!loggedIn) {
1315
- // didn't login, let's just quit
1316
- console.log("Did not login, quitting...");
1317
- return;
1318
- }
1319
- if (!config.account_id) {
1320
- config.account_id = await getAccountId();
1321
- if (!config.account_id) {
1322
- throw new Error("No account id found, quitting...");
1323
- }
1324
- }
1325
- }
1326
-
1327
- // -- snip, end --
1328
-
1329
- // TODO: we should show bindings if they exist for given ids
1330
-
1331
- console.log(
1332
- JSON.stringify(
1333
- await listNamespaces(config.account_id),
1334
- null,
1335
- " "
1336
- )
1337
- );
1338
- }
1339
- )
1340
- .command(
1341
- "delete",
1342
- "Deletes a given namespace.",
1343
- (yargs) => {
1344
- return yargs
1345
- .option("binding", {
1346
- type: "string",
1347
- describe: "The name of the namespace to delete",
1348
- })
1349
- .option("namespace-id", {
1350
- type: "string",
1351
- describe: "The id of the namespace to delete",
1352
- })
1353
- .check(demandOneOfOption("binding", "namespace-id"))
1354
- .option("env", {
1355
- type: "string",
1356
- describe: "Perform on a specific environment",
1357
- })
1358
- .option("preview", {
1359
- type: "boolean",
1360
- describe: "Interact with a preview namespace",
1361
- });
1362
- },
1363
- async (args) => {
1364
- if (args.local) {
1365
- throw new NotImplementedError(
1366
- `local mode is not yet supported for this command`
1367
- );
1368
- }
1369
- const config = args.config as Config;
1370
-
1371
- let id;
1372
- try {
1373
- id = getNamespaceId(args);
1374
- } catch (e) {
1375
- throw new CommandLineArgsError(
1376
- "Not able to delete namespace.\n" + e.message
1377
- );
1378
- }
1379
-
1380
- // -- snip, extract --
1381
-
1382
- if (!args.local) {
1383
- const loggedIn = await loginOrRefreshIfRequired();
1384
- if (!loggedIn) {
1385
- // didn't login, let's just quit
1386
- console.log("Did not login, quitting...");
1387
- return;
1388
- }
1389
-
1390
- if (!config.account_id) {
1391
- config.account_id = await getAccountId();
1392
- if (!config.account_id) {
1393
- throw new Error("No account id found, quitting...");
1394
- }
1395
- }
1396
- }
1397
-
1398
- // -- snip, end --
1399
-
1400
- await fetchResult<{ id: string }>(
1401
- `/accounts/${config.account_id}/storage/kv/namespaces/${id}`,
1402
- { method: "DELETE" }
1403
- );
1404
-
1405
- // TODO: recommend they remove it from wrangler.toml
1406
-
1407
- // test-mf wrangler kv:namespace delete --namespace-id 2a7d3d8b23fc4159b5afa489d6cfd388
1408
- // Are you sure you want to delete namespace 2a7d3d8b23fc4159b5afa489d6cfd388? [y/n]
1409
- // n
1410
- // 💁 Not deleting namespace 2a7d3d8b23fc4159b5afa489d6cfd388
1411
- // ➜ test-mf wrangler kv:namespace delete --namespace-id 2a7d3d8b23fc4159b5afa489d6cfd388
1412
- // Are you sure you want to delete namespace 2a7d3d8b23fc4159b5afa489d6cfd388? [y/n]
1413
- // y
1414
- // 🌀 Deleting namespace 2a7d3d8b23fc4159b5afa489d6cfd388
1415
- // ✨ Success
1416
- // ⚠️ Make sure to remove this "kv-namespace" entry from your configuration file!
1417
- // ➜ test-mf
1418
-
1419
- // TODO: do it automatically
1420
-
1421
- // TODO: delete the preview namespace as well?
1422
- }
1423
- );
1424
- }
1425
- );
1426
-
1427
- // :key
1428
- wrangler.command(
1429
- "kv:key",
1430
- "🔑 Individually manage Workers KV key-value pairs",
1431
- (kvKeyYargs) => {
1432
- return kvKeyYargs
1433
- .command(
1434
- "put <key> [value]",
1435
- "Writes a single key/value pair to the given namespace.",
1436
- (yargs) => {
1437
- return yargs
1438
- .positional("key", {
1439
- type: "string",
1440
- describe: "The key to write to.",
1441
- })
1442
- .positional("value", {
1443
- type: "string",
1444
- describe: "The value to write.",
1445
- })
1446
- .option("binding", {
1447
- type: "string",
1448
- describe: "The binding of the namespace to write to.",
1449
- })
1450
- .option("namespace-id", {
1451
- type: "string",
1452
- describe: "The id of the namespace to write to.",
1453
- })
1454
- .check(demandOneOfOption("binding", "namespace-id"))
1455
- .option("env", {
1456
- type: "string",
1457
- describe: "Perform on a specific environment",
1458
- })
1459
- .option("preview", {
1460
- type: "boolean",
1461
- describe: "Interact with a preview namespace",
1462
- })
1463
- .option("ttl", {
1464
- type: "number",
1465
- describe: "Time for which the entries should be visible.",
1466
- })
1467
- .option("expiration", {
1468
- type: "number",
1469
- describe:
1470
- "Time since the UNIX epoch after which the entry expires",
1471
- })
1472
- .option("path", {
1473
- type: "string",
1474
- describe: "Read value from the file at a given path.",
1475
- })
1476
- .check(demandOneOfOption("value", "path"));
1477
- },
1478
- async ({ key, ttl, expiration, ...args }) => {
1479
- const namespaceId = getNamespaceId(args);
1480
- const value = args.path
1481
- ? await readFile(args.path, "utf-8")
1482
- : args.value;
1483
- const config = args.config as Config;
1484
-
1485
- if (args.path) {
1486
- console.log(
1487
- `writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}`
1488
- );
1489
- } else {
1490
- console.log(
1491
- `writing the value "${value}" to key "${key}" on namespace ${namespaceId}`
1492
- );
1493
- }
1494
-
1495
- if (args.local) {
1496
- const { Miniflare } = await import("miniflare");
1497
- const mf = new Miniflare({
1498
- kvPersist: (args.kvPersist as string) || true,
1499
- // TODO: these options shouldn't be required
1500
- script: ` `, // has to be a string with at least one char
1501
- });
1502
- const ns = await mf.getKVNamespace(namespaceId);
1503
- await ns.put(key, value, { expiration, expirationTtl: ttl });
1504
- return;
1505
- }
1506
- // -- snip, extract --
1507
-
1508
- if (!args.local) {
1509
- const loggedIn = await loginOrRefreshIfRequired();
1510
- if (!loggedIn) {
1511
- // didn't login, let's just quit
1512
- console.log("Did not login, quitting...");
1513
- return;
1514
- }
1515
-
1516
- if (!config.account_id) {
1517
- config.account_id = await getAccountId();
1518
- if (!config.account_id) {
1519
- throw new Error("No account id found, quitting...");
1520
- }
1521
- }
1522
- }
1523
-
1524
- // -- snip, end --
1525
-
1526
- await putKeyValue(config.account_id, namespaceId, key, value, {
1527
- expiration,
1528
- expiration_ttl: ttl,
1529
- });
1530
- }
1531
- )
1532
- .command(
1533
- "list",
1534
- "Outputs a list of all keys in a given namespace.",
1535
- (yargs) => {
1536
- return yargs
1537
- .option("binding", {
1538
- type: "string",
1539
- describe: "The name of the namespace to list",
1540
- })
1541
- .option("namespace-id", {
1542
- type: "string",
1543
- describe: "The id of the namespace to list",
1544
- })
1545
- .check(demandOneOfOption("binding", "namespace-id"))
1546
- .option("env", {
1547
- type: "string",
1548
- describe: "Perform on a specific environment",
1549
- })
1550
- .option("preview", {
1551
- type: "boolean",
1552
- // In the case of listing keys we will default to non-preview mode
1553
- default: false,
1554
- describe: "Interact with a preview namespace",
1555
- })
1556
- .option("prefix", {
1557
- type: "string",
1558
- describe: "A prefix to filter listed keys",
1559
- });
1560
- },
1561
- async ({ prefix, ...args }) => {
1562
- // TODO: support for limit+cursor (pagination)
1563
-
1564
- const namespaceId = getNamespaceId(args);
1565
- const config = args.config as Config;
1566
-
1567
- if (args.local) {
1568
- const { Miniflare } = await import("miniflare");
1569
- const mf = new Miniflare({
1570
- kvPersist: (args.kvPersist as string) || true,
1571
- // TODO: these options shouldn't be required
1572
- script: ` `, // has to be a string with at least one char
1573
- });
1574
- const ns = await mf.getKVNamespace(namespaceId);
1575
- const listResponse = await ns.list({ prefix });
1576
- console.log(JSON.stringify(listResponse.keys, null, " ")); // TODO: paginate, collate
1577
- return;
1578
- }
1579
-
1580
- // -- snip, extract --
1581
-
1582
- if (!args.local) {
1583
- const loggedIn = await loginOrRefreshIfRequired();
1584
- if (!loggedIn) {
1585
- // didn't login, let's just quit
1586
- console.log("Did not login, quitting...");
1587
- return;
1588
- }
1589
-
1590
- if (!config.account_id) {
1591
- config.account_id = await getAccountId();
1592
- if (!config.account_id) {
1593
- throw new Error("No account id found, quitting...");
1594
- }
1595
- }
1596
- }
1597
-
1598
- // -- snip, end --
1599
-
1600
- console.log(
1601
- await listNamespaceKeys(config.account_id, namespaceId, prefix)
1602
- );
1603
- }
1604
- )
1605
- .command(
1606
- "get <key>",
1607
- "Reads a single value by key from the given namespace.",
1608
- (yargs) => {
1609
- return yargs
1610
- .positional("key", {
1611
- describe: "The key value to get.",
1612
- type: "string",
1613
- })
1614
- .option("binding", {
1615
- type: "string",
1616
- describe: "The name of the namespace to get from",
1617
- })
1618
- .option("namespace-id", {
1619
- type: "string",
1620
- describe: "The id of the namespace to get from",
1621
- })
1622
- .check(demandOneOfOption("binding", "namespace-id"))
1623
- .option("env", {
1624
- type: "string",
1625
- describe: "Perform on a specific environment",
1626
- })
1627
- .option("preview", {
1628
- type: "boolean",
1629
- describe: "Interact with a preview namespace",
1630
- })
1631
- .option("preview", {
1632
- type: "boolean",
1633
- // In the case of getting key values we will default to non-preview mode
1634
- default: false,
1635
- describe: "Interact with a preview namespace",
1636
- });
1637
- },
1638
- async ({ key, ...args }) => {
1639
- const namespaceId = getNamespaceId(args);
1640
- const config = args.config as Config;
1641
-
1642
- if (args.local) {
1643
- const { Miniflare } = await import("miniflare");
1644
- const mf = new Miniflare({
1645
- kvPersist: (args.kvPersist as string) || true,
1646
- // TODO: these options shouldn't be required
1647
- script: ` `, // has to be a string with at least one char
1648
- });
1649
- const ns = await mf.getKVNamespace(namespaceId);
1650
- console.log(await ns.get(key));
1651
- return;
1652
- }
1653
-
1654
- // -- snip, extract --
1655
-
1656
- if (!args.local) {
1657
- const loggedIn = await loginOrRefreshIfRequired();
1658
- if (!loggedIn) {
1659
- // didn't login, let's just quit
1660
- console.log("Did not login, quitting...");
1661
- return;
1662
- }
1663
-
1664
- if (!config.account_id) {
1665
- config.account_id = await getAccountId();
1666
- if (!config.account_id) {
1667
- throw new Error("No account id found, quitting...");
1668
- }
1669
- }
1670
- }
1671
-
1672
- // -- snip, end --
1673
-
1674
- console.log(
1675
- await fetchRaw(
1676
- `/accounts/${config.account_id}/storage/kv/namespaces/${namespaceId}/values/${key}`
1677
- )
1678
- );
1679
- }
1680
- )
1681
- .command(
1682
- "delete <key>",
1683
- "Removes a single key value pair from the given namespace.",
1684
- (yargs) => {
1685
- return yargs
1686
- .positional("key", {
1687
- describe: "The key value to delete",
1688
- type: "string",
1689
- })
1690
- .option("binding", {
1691
- type: "string",
1692
- describe: "The name of the namespace to delete from",
1693
- })
1694
- .option("namespace-id", {
1695
- type: "string",
1696
- describe: "The id of the namespace to delete from",
1697
- })
1698
- .check(demandOneOfOption("binding", "namespace-id"))
1699
- .option("env", {
1700
- type: "string",
1701
- describe: "Perform on a specific environment",
1702
- })
1703
- .option("preview", {
1704
- type: "boolean",
1705
- describe: "Interact with a preview namespace",
1706
- });
1707
- },
1708
- async ({ key, ...args }) => {
1709
- const namespaceId = getNamespaceId(args);
1710
-
1711
- console.log(
1712
- `deleting the key "${key}" on namespace ${namespaceId}`
1713
- );
1714
-
1715
- if (args.local) {
1716
- const { Miniflare } = await import("miniflare");
1717
- const mf = new Miniflare({
1718
- kvPersist: (args.kvPersist as string) || true,
1719
- // TODO: these options shouldn't be required
1720
- script: ` `, // has to be a string with at least one char
1721
- });
1722
- const ns = await mf.getKVNamespace(namespaceId);
1723
- console.log(await ns.delete(key));
1724
- return;
1725
- }
1726
-
1727
- const config = args.config as Config;
1728
-
1729
- // -- snip, extract --
1730
-
1731
- if (!args.local) {
1732
- const loggedIn = await loginOrRefreshIfRequired();
1733
- if (!loggedIn) {
1734
- // didn't login, let's just quit
1735
- console.log("Did not login, quitting...");
1736
- return;
1737
- }
1738
-
1739
- if (!config.account_id) {
1740
- config.account_id = await getAccountId();
1741
- if (!config.account_id) {
1742
- throw new Error("No account id found, quitting...");
1743
- }
1744
- }
1745
- }
1746
-
1747
- // -- snip, end --
1748
-
1749
- await fetchResult(
1750
- `/accounts/${config.account_id}/storage/kv/namespaces/${namespaceId}/values/${key}`,
1751
- { method: "DELETE" }
1752
- );
1753
- }
1754
- );
1755
- }
1756
- );
1757
-
1758
- // :bulk
1759
- wrangler.command(
1760
- "kv:bulk",
1761
- "💪 Interact with multiple Workers KV key-value pairs at once",
1762
- (kvBulkYargs) => {
1763
- return kvBulkYargs
1764
- .command(
1765
- "put <filename>",
1766
- "Upload multiple key-value pairs to a namespace",
1767
- (yargs) => {
1768
- return yargs
1769
- .positional("filename", {
1770
- describe: `The JSON file of key-value pairs to upload, in form [{"key":..., "value":...}"...]`,
1771
- type: "string",
1772
- })
1773
- .option("binding", {
1774
- type: "string",
1775
- describe: "The name of the namespace to put to",
1776
- })
1777
- .option("namespace-id", {
1778
- type: "string",
1779
- describe: "The id of the namespace to put to",
1780
- })
1781
- .check(demandOneOfOption("binding", "namespace-id"))
1782
- .option("env", {
1783
- type: "string",
1784
- describe: "Perform on a specific environment",
1785
- })
1786
- .option("preview", {
1787
- type: "boolean",
1788
- describe: "Interact with a preview namespace",
1789
- });
1790
- },
1791
- async ({ filename, ...args }) => {
1792
- // The simplest implementation I could think of.
1793
- // This could be made more efficient with a streaming parser/uploader
1794
- // but we'll do that in the future if needed.
1795
-
1796
- const namespaceId = getNamespaceId(args);
1797
- const config = args.config as Config;
1798
- const content = await readFile(filename, "utf-8");
1799
- let parsedContent;
1800
- try {
1801
- parsedContent = JSON.parse(content);
1802
- } catch (err) {
1803
- throw new Error(
1804
- `Could not parse json from ${filename}.\n${err.message ?? err}`
1805
- );
1806
- }
1807
-
1808
- if (args.local) {
1809
- const { Miniflare } = await import("miniflare");
1810
- const mf = new Miniflare({
1811
- kvPersist: (args.kvPersist as string) || true,
1812
- // TODO: these options shouldn't be required
1813
- script: ` `, // has to be a string with at least one char
1814
- });
1815
- const ns = await mf.getKVNamespace(namespaceId);
1816
- for (const {
1817
- key,
1818
- value,
1819
- expiration,
1820
- expiration_ttl,
1821
- } of parsedContent) {
1822
- await ns.put(key, value, {
1823
- expiration,
1824
- expirationTtl: expiration_ttl,
1825
- });
1826
- }
1827
-
1828
- return;
1829
- }
1830
-
1831
- // -- snip, extract --
1832
-
1833
- if (!args.local) {
1834
- const loggedIn = await loginOrRefreshIfRequired();
1835
- if (!loggedIn) {
1836
- // didn't login, let's just quit
1837
- console.log("Did not login, quitting...");
1838
- return;
1839
- }
1840
-
1841
- if (!config.account_id) {
1842
- config.account_id = await getAccountId();
1843
- if (!config.account_id) {
1844
- throw new Error("No account id found, quitting...");
1845
- }
1846
- }
1847
- }
1848
-
1849
- // -- snip, end --
1850
-
1851
- console.log(
1852
- await putBulkKeyValue(config.account_id, namespaceId, content)
1853
- );
1854
- }
1855
- )
1856
- .command(
1857
- "delete <filename>",
1858
- "Upload multiple key-value pairs to a namespace",
1859
- (yargs) => {
1860
- return yargs
1861
- .positional("filename", {
1862
- describe: `The JSON file of key-value pairs to upload, in form ["key1", "key2", ...]`,
1863
- type: "string",
1864
- })
1865
- .option("binding", {
1866
- type: "string",
1867
- describe: "The name of the namespace to delete from",
1868
- })
1869
- .option("namespace-id", {
1870
- type: "string",
1871
- describe: "The id of the namespace to delete from",
1872
- })
1873
- .check(demandOneOfOption("binding", "namespace-id"))
1874
- .option("env", {
1875
- type: "string",
1876
- describe: "Perform on a specific environment",
1877
- })
1878
- .option("preview", {
1879
- type: "boolean",
1880
- describe: "Interact with a preview namespace",
1881
- });
1882
- },
1883
- async ({ filename, ...args }) => {
1884
- const namespaceId = getNamespaceId(args);
1885
- const config = args.config as Config;
1886
- const content = await readFile(filename, "utf-8");
1887
- let parsedContent;
1888
- try {
1889
- parsedContent = JSON.parse(content);
1890
- } catch (err) {
1891
- throw new Error(
1892
- `Could not parse json from ${filename}.\n${err.message ?? err}`
1893
- );
1894
- }
1895
-
1896
- if (args.local) {
1897
- const { Miniflare } = await import("miniflare");
1898
- const mf = new Miniflare({
1899
- kvPersist: (args.kvPersist as string) || true,
1900
- // TODO: these options shouldn't be required
1901
- script: ` `, // has to be a string with at least one char
1902
- });
1903
- const ns = await mf.getKVNamespace(namespaceId);
1904
- for (const key of parsedContent) {
1905
- await ns.delete(key);
1906
- }
1907
-
1908
- return;
1909
- }
1910
-
1911
- // -- snip, extract --
1912
-
1913
- if (!args.local) {
1914
- const loggedIn = await loginOrRefreshIfRequired();
1915
- if (!loggedIn) {
1916
- // didn't login, let's just quit
1917
- console.log("Did not login, quitting...");
1918
- return;
1919
- }
1920
-
1921
- if (!config.account_id) {
1922
- config.account_id = await getAccountId();
1923
- if (!config.account_id) {
1924
- throw new Error("No account id found, quitting...");
1925
- }
1926
- }
1927
- }
1928
-
1929
- // -- snip, end --
1930
-
1931
- console.log(
1932
- await deleteBulkKeyValue(config.account_id, namespaceId, content)
1933
- );
1934
- }
1935
- );
1936
- }
1937
- );
1938
-
1939
- wrangler.command("pages", "⚡️ Configure Cloudflare Pages", pages);
1940
-
1941
- wrangler
1942
- .option("config", {
1943
- alias: "c",
1944
- describe: "Path to .toml configuration file",
1945
- type: "string",
1946
- async coerce(arg) {
1947
- return await readConfig(arg);
1948
- },
1949
- })
1950
- .option("local", {
1951
- alias: "l",
1952
- describe: "Run on my machine",
1953
- type: "boolean",
1954
- default: false, // I bet this will a point of contention. We'll revisit it.
1955
- });
1956
-
1957
- wrangler.group(["config", "help", "version"], "Flags:");
1958
- wrangler.help().alias("h", "help");
1959
- wrangler.version(wranglerVersion).alias("v", "version");
1960
- wrangler.exitProcess(false);
1961
-
1962
- await initialiseUserConfig();
1963
-
1964
- try {
1965
- await wrangler.parse();
1966
- } catch (e) {
1967
- if (e instanceof CommandLineArgsError) {
1968
- wrangler.showHelp("error");
1969
- console.error(""); // Just adds a bit of space
1970
- }
1971
- console.error(e.message);
1972
- throw e;
1973
- }
608
+ const wrangler = createCLIParser(argv);
609
+ try {
610
+ await wrangler.parse();
611
+ } catch (e) {
612
+ logger.log(""); // Just adds a bit of space
613
+ if (e instanceof CommandLineArgsError) {
614
+ logger.error(e.message);
615
+ // We are not able to ask the `wrangler` CLI parser to show help for a subcommand programmatically.
616
+ // The workaround is to re-run the parsing with an additional `--help` flag, which will result in the correct help message being displayed.
617
+ // The `wrangler` object is "frozen"; we cannot reuse that with different args, so we must create a new CLI parser to generate the help message.
618
+ await createCLIParser([...argv, "--help"]).parse();
619
+ } else if (e instanceof ParseError) {
620
+ e.notes.push({
621
+ text: "\nIf you think this is a bug, please open an issue at: https://github.com/cloudflare/wrangler2/issues/new/choose",
622
+ });
623
+ logger.log(formatMessage(e));
624
+ } else if (
625
+ e instanceof Error &&
626
+ e.message.includes("Raw mode is not supported on")
627
+ ) {
628
+ // the current terminal doesn't support raw mode, which Ink needs to render
629
+ // Ink doesn't throw a typed error or subclass or anything, so we just check the message content.
630
+ // https://github.com/vadimdemedes/ink/blob/546fe16541fd05ad4e638d6842ca4cbe88b4092b/src/components/App.tsx#L138-L148
631
+
632
+ const currentPlatform = os.platform();
633
+
634
+ const thisTerminalIsUnsupported =
635
+ "This terminal doesn't support raw mode.";
636
+ const soWranglerWontWork =
637
+ "Wrangler uses raw mode to read user input and write output to the terminal, and won't function correctly without it.";
638
+ const tryRunningItIn =
639
+ "Try running your previous command in a terminal that supports raw mode";
640
+ const oneOfThese =
641
+ currentPlatform === "win32"
642
+ ? ", such as Command Prompt or Powershell."
643
+ : currentPlatform === "darwin"
644
+ ? ", such as Terminal.app or iTerm."
645
+ : "."; // linux user detected, hand holding disengaged.
646
+
647
+ logger.error(
648
+ `${thisTerminalIsUnsupported}\n${soWranglerWontWork}\n${tryRunningItIn}${oneOfThese}`
649
+ );
650
+ } else if (isBuildFailure(e)) {
651
+ logBuildFailure(e);
652
+ logger.error(e.message);
653
+ } else {
654
+ logger.error(e instanceof Error ? e.message : e);
655
+ logger.log(
656
+ `${fgGreenColor}%s${resetColor}`,
657
+ "If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new/choose"
658
+ );
659
+ }
660
+ throw e;
661
+ }
662
+ }
663
+
664
+ export function getDevCompatibilityDate(
665
+ config: Config,
666
+ compatibilityDate = config.compatibility_date
667
+ ) {
668
+ const currentDate = new Date().toISOString().substring(0, 10);
669
+ if (config.configPath !== undefined && compatibilityDate === undefined) {
670
+ logger.warn(
671
+ `No compatibility_date was specified. Using today's date: ${currentDate}.\n` +
672
+ "Add one to your wrangler.toml file:\n" +
673
+ "```\n" +
674
+ `compatibility_date = "${currentDate}"\n` +
675
+ "```\n" +
676
+ "or pass it in your terminal:\n" +
677
+ "```\n" +
678
+ `--compatibility-date=${currentDate}\n` +
679
+ "```\n" +
680
+ "See https://developers.cloudflare.com/workers/platform/compatibility-dates/ for more information."
681
+ );
682
+ }
683
+ return compatibilityDate ?? currentDate;
1974
684
  }