wrangler 0.0.0-e7a0cd5b → 0.0.0-e87ad068

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 (255) hide show
  1. package/README.md +3 -3
  2. package/bin/wrangler.js +30 -10
  3. package/miniflare-dist/index.mjs +254 -128
  4. package/package.json +27 -19
  5. package/src/__tests__/api-dev.test.ts +65 -36
  6. package/src/__tests__/api-devregistry.test.ts +121 -0
  7. package/src/__tests__/api.test.ts +27 -5
  8. package/src/__tests__/configuration.test.ts +797 -98
  9. package/src/__tests__/constellation.test.ts +371 -0
  10. package/src/__tests__/d1/d1.test.ts +82 -0
  11. package/src/__tests__/d1/execute.test.ts +66 -0
  12. package/src/__tests__/d1/migrate.test.ts +257 -0
  13. package/src/__tests__/d1/splitter.test.ts +255 -0
  14. package/src/__tests__/delete.test.ts +91 -49
  15. package/src/__tests__/deployments.test.ts +330 -56
  16. package/src/__tests__/dev.test.tsx +198 -90
  17. package/src/__tests__/generate.test.ts +12 -10
  18. package/src/__tests__/guess-worker-format.test.ts +35 -0
  19. package/src/__tests__/helpers/end-event-loop.ts +6 -0
  20. package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
  21. package/src/__tests__/helpers/mock-console.ts +6 -0
  22. package/src/__tests__/helpers/mock-dialogs.ts +71 -85
  23. package/src/__tests__/helpers/mock-get-pages-upload-token.ts +25 -0
  24. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -5
  25. package/src/__tests__/helpers/mock-known-routes.ts +7 -2
  26. package/src/__tests__/helpers/mock-kv.ts +29 -16
  27. package/src/__tests__/helpers/mock-oauth-flow.ts +103 -92
  28. package/src/__tests__/helpers/mock-process.ts +8 -13
  29. package/src/__tests__/helpers/mock-set-timeout.ts +16 -0
  30. package/src/__tests__/helpers/msw/blob-worker.cjs +19 -0
  31. package/src/__tests__/helpers/msw/handlers/deployments.ts +128 -27
  32. package/src/__tests__/helpers/msw/handlers/namespaces.ts +18 -41
  33. package/src/__tests__/helpers/msw/handlers/oauth.ts +13 -18
  34. package/src/__tests__/helpers/msw/handlers/r2.ts +14 -34
  35. package/src/__tests__/helpers/msw/handlers/script.ts +9 -28
  36. package/src/__tests__/helpers/msw/handlers/user.ts +13 -24
  37. package/src/__tests__/helpers/msw/handlers/zones.ts +6 -8
  38. package/src/__tests__/helpers/msw/index.ts +32 -1
  39. package/src/__tests__/helpers/msw/read-file-sync.js +61 -0
  40. package/src/__tests__/helpers/string-dynamic-values-matcher.ts +28 -0
  41. package/src/__tests__/helpers/worker-scripts/parent-worker.js +4 -1
  42. package/src/__tests__/index.test.ts +92 -72
  43. package/src/__tests__/init.test.ts +1033 -631
  44. package/src/__tests__/jest.setup.ts +19 -42
  45. package/src/__tests__/kv.test.ts +355 -231
  46. package/src/__tests__/logger.test.ts +68 -2
  47. package/src/__tests__/logout.test.ts +47 -0
  48. package/src/__tests__/metrics.test.ts +94 -51
  49. package/src/__tests__/middleware.scheduled.test.ts +42 -32
  50. package/src/__tests__/middleware.test.ts +212 -122
  51. package/src/__tests__/mtls-certificates.test.ts +589 -0
  52. package/src/__tests__/pages/deployment-list.test.ts +80 -0
  53. package/src/__tests__/pages/functions-build.test.ts +529 -0
  54. package/src/__tests__/pages/pages.test.ts +81 -0
  55. package/src/__tests__/pages/project-create.test.ts +63 -0
  56. package/src/__tests__/pages/project-list.test.ts +110 -0
  57. package/src/__tests__/pages/project-upload.test.ts +500 -0
  58. package/src/__tests__/pages/publish.test.ts +2864 -0
  59. package/src/__tests__/pages-deployment-tail.test.ts +957 -0
  60. package/src/__tests__/parse.test.ts +106 -0
  61. package/src/__tests__/publish.test.ts +2227 -1005
  62. package/src/__tests__/pubsub.test.ts +226 -100
  63. package/src/__tests__/queues.test.ts +238 -77
  64. package/src/__tests__/r2.test.ts +55 -57
  65. package/src/__tests__/secret.test.ts +179 -130
  66. package/src/__tests__/tail.test.ts +313 -113
  67. package/src/__tests__/test-old-node-version.js +3 -3
  68. package/src/__tests__/traverse-module-graph.test.ts +220 -0
  69. package/src/__tests__/tsconfig-sanity.ts +12 -0
  70. package/src/__tests__/tsconfig.json +8 -0
  71. package/src/__tests__/tsconfig.tsbuildinfo +1 -0
  72. package/src/__tests__/type-generation.test.ts +43 -26
  73. package/src/__tests__/unstableDev.test.ts +61 -0
  74. package/src/__tests__/user.test.ts +35 -72
  75. package/src/__tests__/whoami.test.tsx +8 -114
  76. package/src/__tests__/worker-namespace.test.ts +83 -69
  77. package/src/api/dev.ts +180 -60
  78. package/src/api/index.ts +10 -1
  79. package/src/api/mtls-certificate.ts +148 -0
  80. package/src/api/pages/create-worker-bundle-contents.ts +80 -0
  81. package/src/api/pages/index.ts +5 -0
  82. package/src/api/pages/publish.tsx +371 -0
  83. package/src/bundle.ts +315 -148
  84. package/src/cfetch/index.ts +0 -2
  85. package/src/cfetch/internal.ts +33 -33
  86. package/src/cli.ts +4 -4
  87. package/src/config/config.ts +14 -7
  88. package/src/config/environment.ts +120 -24
  89. package/src/config/index.ts +117 -35
  90. package/src/config/validation-helpers.ts +1 -1
  91. package/src/config/validation.ts +430 -19
  92. package/src/constellation/createProject.tsx +51 -0
  93. package/src/constellation/deleteProject.ts +51 -0
  94. package/src/constellation/deleteProjectModel.ts +76 -0
  95. package/src/constellation/index.ts +75 -0
  96. package/src/constellation/listCatalog.tsx +35 -0
  97. package/src/constellation/listModel.tsx +49 -0
  98. package/src/constellation/listProject.tsx +28 -0
  99. package/src/constellation/listRuntime.tsx +28 -0
  100. package/src/constellation/options.ts +17 -0
  101. package/src/constellation/types.ts +17 -0
  102. package/src/constellation/uploadModel.tsx +84 -0
  103. package/src/constellation/utils.ts +90 -0
  104. package/src/create-worker-preview.ts +19 -8
  105. package/src/create-worker-upload-form.ts +81 -18
  106. package/src/d1/backups.tsx +47 -46
  107. package/src/d1/constants.ts +5 -0
  108. package/src/d1/create.tsx +85 -37
  109. package/src/d1/{delete.tsx → delete.ts} +12 -15
  110. package/src/d1/execute.tsx +228 -156
  111. package/src/d1/index.ts +31 -7
  112. package/src/d1/list.tsx +27 -13
  113. package/src/d1/migrations/apply.tsx +235 -0
  114. package/src/d1/migrations/create.tsx +79 -0
  115. package/src/d1/migrations/helpers.ts +171 -0
  116. package/src/d1/migrations/index.ts +3 -0
  117. package/src/d1/migrations/list.tsx +95 -0
  118. package/src/d1/migrations/options.ts +23 -0
  119. package/src/d1/options.ts +12 -2
  120. package/src/d1/splitter.ts +161 -0
  121. package/src/d1/types.ts +32 -0
  122. package/src/d1/utils.ts +12 -2
  123. package/src/delete.ts +8 -16
  124. package/src/deployments.ts +340 -46
  125. package/src/deprecated/index.ts +8 -302
  126. package/src/dev/dev.tsx +66 -46
  127. package/src/dev/local.tsx +339 -104
  128. package/src/dev/remote.tsx +36 -3
  129. package/src/dev/start-server.ts +146 -101
  130. package/src/dev/use-esbuild.ts +82 -56
  131. package/src/dev/validate-dev-props.ts +9 -0
  132. package/src/{dev-registry.tsx → dev-registry.ts} +13 -1
  133. package/src/dev.tsx +202 -125
  134. package/src/dialogs.ts +136 -0
  135. package/src/dispatch-namespace.ts +21 -17
  136. package/src/docs/helpers.ts +50 -0
  137. package/src/docs/index.ts +54 -0
  138. package/src/entry.ts +26 -11
  139. package/src/environment-variables/factory.ts +89 -0
  140. package/src/environment-variables/misc-variables.ts +30 -0
  141. package/src/generate/index.ts +303 -0
  142. package/src/git-client.ts +15 -4
  143. package/src/{index.tsx → index.ts} +148 -63
  144. package/src/init.ts +301 -197
  145. package/src/inspect.ts +122 -6
  146. package/src/jest.d.ts +4 -0
  147. package/src/kv/helpers.ts +7 -4
  148. package/src/kv/index.ts +15 -17
  149. package/src/logger.ts +36 -5
  150. package/src/metrics/metrics-config.ts +3 -3
  151. package/src/metrics/send-event.ts +6 -1
  152. package/src/miniflare-cli/assets.ts +64 -28
  153. package/src/miniflare-cli/index.ts +6 -9
  154. package/src/miniflare-cli/tsconfig.json +9 -0
  155. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
  156. package/src/miniflare-cli/types.ts +11 -0
  157. package/src/module-collection.ts +92 -26
  158. package/src/mtls-certificate/cli.ts +155 -0
  159. package/src/pages/build.ts +423 -0
  160. package/src/pages/buildFunctions.ts +140 -0
  161. package/src/pages/deployment-tails.ts +281 -0
  162. package/src/pages/deployments.tsx +11 -33
  163. package/src/pages/{dev.tsx → dev.ts} +192 -126
  164. package/src/pages/errors.ts +1 -0
  165. package/src/pages/functions/buildPlugin.ts +73 -69
  166. package/src/pages/functions/buildWorker.ts +342 -117
  167. package/src/pages/functions/tsconfig.json +8 -0
  168. package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
  169. package/src/pages/{functions.tsx → functions.ts} +8 -4
  170. package/src/pages/{index.tsx → index.ts} +11 -3
  171. package/src/pages/projects.tsx +12 -22
  172. package/src/pages/prompt-select-project.tsx +31 -0
  173. package/src/pages/publish.tsx +45 -214
  174. package/src/pages/types.ts +6 -9
  175. package/src/pages/upload.tsx +58 -14
  176. package/src/pages/utils.ts +14 -3
  177. package/src/parse.ts +23 -1
  178. package/src/paths.ts +1 -1
  179. package/src/proxy.ts +5 -0
  180. package/src/publish/index.ts +20 -16
  181. package/src/publish/publish.ts +81 -48
  182. package/src/pubsub/{pubsub-commands.tsx → pubsub-commands.ts} +19 -20
  183. package/src/queues/cli/commands/consumer/add.ts +24 -24
  184. package/src/queues/cli/commands/consumer/index.ts +3 -6
  185. package/src/queues/cli/commands/consumer/remove.ts +11 -18
  186. package/src/queues/cli/commands/create.ts +8 -8
  187. package/src/queues/cli/commands/delete.ts +8 -8
  188. package/src/queues/cli/commands/index.ts +6 -4
  189. package/src/queues/cli/commands/list.ts +8 -8
  190. package/src/queues/client.ts +1 -0
  191. package/src/queues/utils.ts +18 -0
  192. package/src/r2/constants.ts +4 -0
  193. package/src/r2/helpers.ts +1 -0
  194. package/src/r2/index.ts +40 -28
  195. package/src/secret/index.ts +18 -17
  196. package/src/{sites.tsx → sites.ts} +165 -53
  197. package/src/tail/createTail.ts +103 -3
  198. package/src/tail/filters.ts +1 -1
  199. package/src/tail/index.ts +8 -11
  200. package/src/tail/printing.ts +27 -0
  201. package/src/traverse-module-graph.ts +54 -0
  202. package/src/tsconfig-sanity.ts +16 -0
  203. package/src/type-generation.ts +85 -57
  204. package/src/user/access.ts +0 -1
  205. package/src/user/auth-variables.ts +113 -0
  206. package/src/user/choose-account.tsx +1 -31
  207. package/src/user/index.ts +0 -1
  208. package/src/user/{user.tsx → user.ts} +124 -82
  209. package/src/utils/render.ts +93 -0
  210. package/src/{whoami.tsx → whoami.ts} +37 -71
  211. package/src/worker.ts +61 -3
  212. package/src/yargs-types.ts +22 -5
  213. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  214. package/templates/__tests__/tsconfig.json +8 -0
  215. package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
  216. package/templates/checked-fetch.js +9 -3
  217. package/templates/d1-beta-facade.js +95 -25
  218. package/templates/facade.d.ts +14 -0
  219. package/templates/first-party-worker-module-facade.ts +4 -3
  220. package/templates/format-dev-errors.ts +8 -6
  221. package/templates/gitignore +1 -0
  222. package/templates/init-tests/test-jest-new-worker.js +3 -5
  223. package/templates/init-tests/test-vitest-new-worker.js +3 -5
  224. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  225. package/templates/middleware/loader-modules.ts +2 -2
  226. package/templates/middleware/loader-sw.ts +51 -31
  227. package/templates/middleware/middleware-miniflare3-json-error.ts +32 -0
  228. package/templates/new-worker.ts +3 -0
  229. package/templates/pages-dev-pipeline.ts +4 -1
  230. package/templates/pages-dev-util.ts +4 -1
  231. package/templates/pages-shim.ts +4 -1
  232. package/templates/pages-template-plugin.ts +19 -10
  233. package/templates/serve-static-assets.ts +17 -14
  234. package/templates/service-bindings-module-facade.js +1 -0
  235. package/templates/tsconfig-sanity.ts +11 -0
  236. package/templates/tsconfig.init.json +105 -0
  237. package/templates/tsconfig.json +5 -103
  238. package/templates/tsconfig.tsbuildinfo +1 -0
  239. package/wrangler-dist/cli.d.ts +902 -77
  240. package/wrangler-dist/cli.js +131685 -140615
  241. package/wrangler-dist/wasm-sync.wasm +0 -0
  242. package/src/__tests__/api-devregistry.test.js +0 -57
  243. package/src/__tests__/d1.test.ts +0 -72
  244. package/src/__tests__/dialogs.test.tsx +0 -40
  245. package/src/__tests__/helpers/mock-cfetch.ts +0 -333
  246. package/src/__tests__/pages.test.ts +0 -2223
  247. package/src/d1/types.tsx +0 -14
  248. package/src/dialogs.tsx +0 -163
  249. package/src/environment-variables.ts +0 -50
  250. package/src/pages/build.tsx +0 -238
  251. package/src/user/env-vars.ts +0 -46
  252. package/templates/experimental-local-cache-stubs.js +0 -27
  253. /package/src/{bundle-reporter.tsx → bundle-reporter.ts} +0 -0
  254. /package/src/dev/{get-local-persistence-path.tsx → get-local-persistence-path.ts} +0 -0
  255. /package/src/pages/{hash.tsx → hash.ts} +0 -0
@@ -214,28 +214,61 @@ import url from "node:url";
214
214
  import { TextEncoder } from "node:util";
215
215
  import TOML from "@iarna/toml";
216
216
  import { HostURL } from "@webcontainer/env";
217
- import { render } from "ink";
218
- import Table from "ink-table";
219
- import React from "react";
220
217
  import { fetch } from "undici";
221
218
  import {
222
219
  getConfigCache,
223
220
  purgeConfigCaches,
224
221
  saveToConfigCache,
225
222
  } from "../config-cache";
223
+ import { NoDefaultValueProvided, select } from "../dialogs";
226
224
  import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
227
225
  import { CI } from "../is-ci";
228
226
  import isInteractive from "../is-interactive";
229
227
  import { logger } from "../logger";
230
228
  import openInBrowser from "../open-in-browser";
231
229
  import { parseTOML, readFileSync } from "../parse";
232
- import { ChooseAccount, getAccountChoices } from "./choose-account";
233
- import { getAuthFromEnv } from "./env-vars";
230
+ import { domainUsesAccess } from "./access";
231
+ import {
232
+ getAuthDomainFromEnv,
233
+ getAuthUrlFromEnv,
234
+ getClientIdFromEnv,
235
+ getCloudflareAccessToken,
236
+ getCloudflareAPITokenFromEnv,
237
+ getCloudflareGlobalAuthEmailFromEnv,
238
+ getCloudflareGlobalAuthKeyFromEnv,
239
+ getRevokeUrlFromEnv,
240
+ getTokenUrlFromEnv,
241
+ } from "./auth-variables";
242
+ import { getAccountChoices } from "./choose-account";
234
243
  import { generateAuthUrl } from "./generate-auth-url";
235
244
  import { generateRandomState } from "./generate-random-state";
236
- import type { ApiCredentials } from "./env-vars";
245
+ import type { ChooseAccountItem } from "./choose-account";
237
246
  import type { ParsedUrlQuery } from "node:querystring";
238
247
 
248
+ export type ApiCredentials =
249
+ | {
250
+ apiToken: string;
251
+ }
252
+ | {
253
+ authKey: string;
254
+ authEmail: string;
255
+ };
256
+
257
+ /**
258
+ * Try to read an API token or Global Auth from the environment.
259
+ */
260
+ export function getAuthFromEnv(): ApiCredentials | undefined {
261
+ const globalApiKey = getCloudflareGlobalAuthKeyFromEnv();
262
+ const globalApiEmail = getCloudflareGlobalAuthEmailFromEnv();
263
+ const apiToken = getCloudflareAPITokenFromEnv();
264
+
265
+ if (globalApiKey && globalApiEmail) {
266
+ return { authKey: globalApiKey, authEmail: globalApiEmail };
267
+ } else if (apiToken) {
268
+ return { apiToken };
269
+ }
270
+ }
271
+
239
272
  /**
240
273
  * An implementation of rfc6749#section-4.1 and rfc7636.
241
274
  */
@@ -264,7 +297,7 @@ interface AuthTokens {
264
297
  accessToken?: AccessToken;
265
298
  refreshToken?: RefreshToken;
266
299
  scopes?: Scope[];
267
- /** @deprecated - this field was only provided by the deprecated `wrangler1 config` command. */
300
+ /** @deprecated - this field was only provided by the deprecated v1 `wrangler config` command. */
268
301
  apiToken?: string;
269
302
  }
270
303
 
@@ -282,7 +315,7 @@ export interface UserAuthConfig {
282
315
  refresh_token?: string;
283
316
  expiration_time?: string;
284
317
  scopes?: string[];
285
- /** @deprecated - this field was only provided by the deprecated `wrangler1 config` command. */
318
+ /** @deprecated - this field was only provided by the deprecated v1 `wrangler config` command. */
286
319
  api_token?: string;
287
320
  }
288
321
 
@@ -313,6 +346,8 @@ const Scopes = {
313
346
  "pages:write":
314
347
  "See and change Cloudflare Pages projects, settings and deployments.",
315
348
  "zone:read": "Grants read level access to account zone.",
349
+ "ssl_certs:write": "See and manage mTLS certificates for your account",
350
+ "constellation:write": "Manage Constellation projects/models",
316
351
  } as const;
317
352
 
318
353
  /**
@@ -330,11 +365,6 @@ export function validateScopeKeys(
330
365
  return scopes.every((scope) => scope in Scopes);
331
366
  }
332
367
 
333
- const CLIENT_ID = "54d11594-84e4-41aa-b438-e81b8fa78ee7";
334
- const AUTH_URL = "https://dash.cloudflare.com/oauth2/auth";
335
- const TOKEN_URL = "https://dash.cloudflare.com/oauth2/token";
336
- const REVOKE_URL = "https://dash.cloudflare.com/oauth2/revoke";
337
-
338
368
  /**
339
369
  * To allow OAuth callbacks in environments such as WebContainer we need to
340
370
  * create a host URL which only resolves `localhost` to a WebContainer
@@ -374,7 +404,7 @@ function getAuthTokens(config?: UserAuthConfig): AuthTokens | undefined {
374
404
  };
375
405
  } else if (api_token) {
376
406
  logger.warn(
377
- "It looks like you have used Wrangler 1's `config` command to login with an API token.\n" +
407
+ "It looks like you have used Wrangler v1's `config` command to login with an API token.\n" +
378
408
  "This is no longer supported in the current version of Wrangler.\n" +
379
409
  "If you wish to authenticate via an API token then please set the `CLOUDFLARE_API_TOKEN` environment variable."
380
410
  );
@@ -386,7 +416,7 @@ function getAuthTokens(config?: UserAuthConfig): AuthTokens | undefined {
386
416
  }
387
417
 
388
418
  /**
389
- * Run the initialisation of the auth state, in the case that something changed.
419
+ * Run the initialization of the auth state, in the case that something changed.
390
420
  *
391
421
  * This runs automatically whenever `writeAuthConfigFile` is run, so generally
392
422
  * you won't need to call it yourself.
@@ -625,8 +655,8 @@ export async function getAuthURL(scopes = ScopeKeys): Promise<string> {
625
655
  });
626
656
 
627
657
  return generateAuthUrl({
628
- authUrl: AUTH_URL,
629
- clientId: CLIENT_ID,
658
+ authUrl: getAuthUrlFromEnv(),
659
+ clientId: getClientIdFromEnv(),
630
660
  callbackUrl: CALLBACK_URL,
631
661
  scopes,
632
662
  stateQueryParam,
@@ -653,19 +683,14 @@ async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
653
683
  logger.warn("No refresh token is present.");
654
684
  }
655
685
 
656
- const body =
657
- `grant_type=refresh_token&` +
658
- `refresh_token=${LocalState.refreshToken?.value}&` +
659
- `client_id=${CLIENT_ID}`;
660
-
661
- const response = await fetch(TOKEN_URL, {
662
- method: "POST",
663
- body,
664
- headers: {
665
- "Content-Type": "application/x-www-form-urlencoded",
666
- },
686
+ const params = new URLSearchParams({
687
+ grant_type: "refresh_token",
688
+ refresh_token: LocalState.refreshToken?.value ?? "",
689
+ client_id: getClientIdFromEnv(),
667
690
  });
668
691
 
692
+ const response = await fetchAuthToken(params);
693
+
669
694
  if (response.status >= 400) {
670
695
  let tokenExchangeResErr = undefined;
671
696
 
@@ -743,20 +768,15 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
743
768
  logger.warn("No authorization grant code is being passed.");
744
769
  }
745
770
 
746
- const body =
747
- `grant_type=authorization_code&` +
748
- `code=${encodeURIComponent(authorizationCode || "")}&` +
749
- `redirect_uri=${encodeURIComponent(CALLBACK_URL)}&` +
750
- `client_id=${encodeURIComponent(CLIENT_ID)}&` +
751
- `code_verifier=${codeVerifier}`;
752
-
753
- const response = await fetch(TOKEN_URL, {
754
- method: "POST",
755
- body,
756
- headers: {
757
- "Content-Type": "application/x-www-form-urlencoded",
758
- },
771
+ const params = new URLSearchParams({
772
+ grant_type: `authorization_code`,
773
+ code: authorizationCode ?? "",
774
+ redirect_uri: CALLBACK_URL,
775
+ client_id: getClientIdFromEnv(),
776
+ code_verifier: codeVerifier,
759
777
  });
778
+
779
+ const response = await fetchAuthToken(params);
760
780
  if (!response.ok) {
761
781
  const { error } = (await response.json()) as { error: string };
762
782
  // .catch((_) => ({ error: "invalid_json" }));
@@ -875,6 +895,7 @@ export function readAuthConfigFile(): UserAuthConfig {
875
895
 
876
896
  type LoginProps = {
877
897
  scopes?: Scope[];
898
+ browser: boolean;
878
899
  };
879
900
 
880
901
  export async function loginOrRefreshIfRequired(): Promise<boolean> {
@@ -901,7 +922,9 @@ export async function loginOrRefreshIfRequired(): Promise<boolean> {
901
922
  }
902
923
  }
903
924
 
904
- export async function login(props?: LoginProps): Promise<boolean> {
925
+ export async function login(
926
+ props: LoginProps = { browser: true }
927
+ ): Promise<boolean> {
905
928
  logger.log("Attempting to login via OAuth...");
906
929
  const urlToOpen = await getAuthURL(props?.scopes);
907
930
  let server: http.Server;
@@ -914,7 +937,7 @@ export async function login(props?: LoginProps): Promise<boolean> {
914
937
  server.close();
915
938
  clearTimeout(loginTimeoutHandle);
916
939
  resolve(false);
917
- }, 60000); // wait for 60 seconds for the user to authorize
940
+ }, 120000); // wait for 120 seconds for the user to authorize
918
941
  });
919
942
 
920
943
  const loginPromise = new Promise<boolean>((resolve, reject) => {
@@ -986,9 +1009,12 @@ export async function login(props?: LoginProps): Promise<boolean> {
986
1009
 
987
1010
  server.listen(8976);
988
1011
  });
989
-
990
- logger.log(`Opening a link in your default browser: ${urlToOpen}`);
991
- await openInBrowser(urlToOpen);
1012
+ if (props?.browser) {
1013
+ logger.log(`Opening a link in your default browser: ${urlToOpen}`);
1014
+ await openInBrowser(urlToOpen);
1015
+ } else {
1016
+ logger.log(`Visit this link to authenticate: ${urlToOpen}`);
1017
+ }
992
1018
 
993
1019
  return Promise.race([timerPromise, loginPromise]);
994
1020
  }
@@ -1032,11 +1058,11 @@ export async function logout(): Promise<void> {
1032
1058
  }
1033
1059
 
1034
1060
  const body =
1035
- `client_id=${encodeURIComponent(CLIENT_ID)}&` +
1061
+ `client_id=${encodeURIComponent(getClientIdFromEnv())}&` +
1036
1062
  `token_type_hint=refresh_token&` +
1037
1063
  `token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
1038
1064
 
1039
- const response = await fetch(REVOKE_URL, {
1065
+ const response = await fetch(getRevokeUrlFromEnv(), {
1040
1066
  method: "POST",
1041
1067
  body,
1042
1068
  headers: {
@@ -1049,11 +1075,11 @@ export async function logout(): Promise<void> {
1049
1075
  );
1050
1076
  }
1051
1077
  const body =
1052
- `client_id=${encodeURIComponent(CLIENT_ID)}&` +
1078
+ `client_id=${encodeURIComponent(getClientIdFromEnv())}&` +
1053
1079
  `token_type_hint=refresh_token&` +
1054
1080
  `token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
1055
1081
 
1056
- const response = await fetch(REVOKE_URL, {
1082
+ const response = await fetch(getRevokeUrlFromEnv(), {
1057
1083
  method: "POST",
1058
1084
  body,
1059
1085
  headers: {
@@ -1071,8 +1097,7 @@ export function listScopes(message = "💁 Available scopes:"): void {
1071
1097
  Scope: scope,
1072
1098
  Description: Scopes[scope],
1073
1099
  }));
1074
- const { unmount } = render(<Table data={data} />);
1075
- unmount();
1100
+ logger.table(data);
1076
1101
  // TODO: maybe a good idea to show usage here
1077
1102
  }
1078
1103
 
@@ -1091,36 +1116,32 @@ export async function getAccountId(): Promise<string | undefined> {
1091
1116
  return accounts[0].id;
1092
1117
  }
1093
1118
 
1094
- if (isInteractive() && !CI.isCI()) {
1095
- const account = await new Promise<{ id: string; name: string }>(
1096
- (resolve, reject) => {
1097
- const { unmount } = render(
1098
- <ChooseAccount
1099
- accounts={accounts}
1100
- onSelect={async (selected) => {
1101
- saveAccountToCache(selected);
1102
- resolve(selected);
1103
- unmount();
1104
- }}
1105
- onError={(err) => {
1106
- reject(err);
1107
- unmount();
1108
- }}
1109
- />
1110
- );
1111
- }
1112
- );
1113
- return account.id;
1119
+ try {
1120
+ const accountID = await select("Select an account", {
1121
+ choices: accounts.map((account) => ({
1122
+ title: account.name,
1123
+ value: account.id,
1124
+ })),
1125
+ });
1126
+ const account = accounts.find(
1127
+ (a) => a.id === accountID
1128
+ ) as ChooseAccountItem;
1129
+ saveAccountToCache({ id: account.id, name: account.name });
1130
+ return accountID;
1131
+ } catch (e) {
1132
+ // Did we try to select an account in CI or a non-interactive terminal?
1133
+ if (e instanceof NoDefaultValueProvided) {
1134
+ throw new Error(
1135
+ `More than one account available but unable to select one in non-interactive mode.
1136
+ Please set the appropriate \`account_id\` in your \`wrangler.toml\` file.
1137
+ Available accounts are (\`<name>\`: \`<account_id>\`):
1138
+ ${accounts
1139
+ .map((account) => ` \`${account.name}\`: \`${account.id}\``)
1140
+ .join("\n")}`
1141
+ );
1142
+ }
1143
+ throw e;
1114
1144
  }
1115
-
1116
- throw new Error(
1117
- "More than one account available but unable to select one in non-interactive mode.\n" +
1118
- `Please set the appropriate \`account_id\` in your \`wrangler.toml\` file.\n` +
1119
- `Available accounts are ("<name>" - "<id>"):\n` +
1120
- accounts
1121
- .map((account) => ` "${account.name}" - "${account.id}")`)
1122
- .join("\n")
1123
- );
1124
1145
  }
1125
1146
 
1126
1147
  /**
@@ -1133,7 +1154,7 @@ export async function requireAuth(config: {
1133
1154
  if (!loggedIn) {
1134
1155
  if (!isInteractive() || CI.isCI()) {
1135
1156
  throw new Error(
1136
- "In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/api/tokens/create/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."
1157
+ "In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/fundamentals/api/get-started/create-token/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."
1137
1158
  );
1138
1159
  } else {
1139
1160
  // didn't login, let's just quit
@@ -1190,3 +1211,24 @@ export function getAccountFromCache():
1190
1211
  export function getScopes(): Scope[] | undefined {
1191
1212
  return LocalState.scopes;
1192
1213
  }
1214
+
1215
+ /**
1216
+ * Make a request to the Cloudflare OAuth endpoint to get a token.
1217
+ *
1218
+ * Note that the `body` of the POST request is form-urlencoded so
1219
+ * can be represented by a URLSearchParams object.
1220
+ */
1221
+ async function fetchAuthToken(body: URLSearchParams) {
1222
+ const headers: Record<string, string> = {
1223
+ "Content-Type": "application/x-www-form-urlencoded",
1224
+ };
1225
+ if (await domainUsesAccess(getAuthDomainFromEnv())) {
1226
+ // We are trying to access the staging API so we need an "access token".
1227
+ headers["Cookie"] = `CF_Authorization=${await getCloudflareAccessToken()}`;
1228
+ }
1229
+ return await fetch(getTokenUrlFromEnv(), {
1230
+ method: "POST",
1231
+ body: body.toString(),
1232
+ headers,
1233
+ });
1234
+ }
@@ -0,0 +1,93 @@
1
+ import { EventEmitter } from "events";
2
+ import { render as inkRender } from "ink";
3
+ import type { ReactElement } from "react";
4
+ import type { WriteStream } from "tty";
5
+
6
+ export function renderToString(tree: ReactElement): string {
7
+ const { output, cleanup } = render(tree);
8
+ cleanup();
9
+ return output;
10
+ }
11
+
12
+ // The code below is mostly copied from ink-render-string.
13
+ // See https://github.com/zhanwang626/ink-render-string.git.
14
+
15
+ // MIT License
16
+
17
+ // Copyright (c) 2021 Zhan Wang
18
+
19
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
20
+ // of this software and associated documentation files (the "Software"), to deal
21
+ // in the Software without restriction, including without limitation the rights
22
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23
+ // copies of the Software, and to permit persons to whom the Software is
24
+ // furnished to do so, subject to the following conditions:
25
+
26
+ // The above copyright notice and this permission notice shall be included in all
27
+ // copies or substantial portions of the Software.
28
+
29
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35
+ // SOFTWARE.
36
+
37
+ interface Instance {
38
+ output: string;
39
+ unmount: () => void;
40
+ cleanup: () => void;
41
+ stdout: OutputStream;
42
+ stderr: OutputStream;
43
+ frames: string[];
44
+ }
45
+
46
+ const LASTFRAME_UNDEFINED = "value of stdout.lastframe() is undefined";
47
+
48
+ export class OutputStream extends EventEmitter {
49
+ readonly frames: string[] = [];
50
+ private _lastFrame?: string;
51
+
52
+ get columns(): number {
53
+ return this._originalStream.columns;
54
+ }
55
+ get rows(): number {
56
+ return this._originalStream.rows;
57
+ }
58
+
59
+ constructor(private _originalStream: WriteStream) {
60
+ super();
61
+ }
62
+
63
+ write = (frame: string) => {
64
+ this.frames.push(frame);
65
+ this._lastFrame = frame;
66
+ };
67
+
68
+ lastFrame = () => {
69
+ return this._lastFrame;
70
+ };
71
+ }
72
+
73
+ export const render = (tree: ReactElement): Instance => {
74
+ const stdout = new OutputStream(process.stdout);
75
+ const stderr = new OutputStream(process.stderr);
76
+
77
+ const instance = inkRender(tree, {
78
+ stdout: stdout as unknown as WriteStream,
79
+ stderr: stderr as unknown as WriteStream,
80
+ debug: true,
81
+ exitOnCtrlC: false,
82
+ patchConsole: false,
83
+ });
84
+
85
+ return {
86
+ output: stdout.lastFrame() ?? LASTFRAME_UNDEFINED,
87
+ stdout,
88
+ stderr,
89
+ cleanup: instance.cleanup,
90
+ unmount: instance.unmount,
91
+ frames: stdout.frames,
92
+ };
93
+ };
@@ -1,6 +1,4 @@
1
- import { Text, render } from "ink";
2
- import Table from "ink-table";
3
- import React, { Fragment } from "react";
1
+ import chalk from "chalk";
4
2
  import { fetchListResult, fetchResult } from "./cfetch";
5
3
  import { logger } from "./logger";
6
4
  import { getAPIToken, getAuthFromEnv, getScopes } from "./user";
@@ -8,82 +6,50 @@ import { getAPIToken, getAuthFromEnv, getScopes } from "./user";
8
6
  export async function whoami() {
9
7
  logger.log("Getting User settings...");
10
8
  const user = await getUserInfo();
11
- const { unmount } = render(<WhoAmI user={user}></WhoAmI>);
12
- unmount();
13
- }
14
-
15
- export function WhoAmI({ user }: { user: UserInfo | undefined }) {
16
- return user ? (
17
- <>
18
- <Email tokenType={user.authType} email={user.email}></Email>
19
- <Accounts accounts={user.accounts}></Accounts>
20
- <Permissions
21
- tokenType={user.authType}
22
- tokenPermissions={user.tokenPermissions}
23
- />
24
- </>
25
- ) : (
26
- <Text>You are not authenticated. Please run `wrangler login`.</Text>
27
- );
28
- }
9
+ if (user === undefined) {
10
+ return void logger.log(
11
+ "You are not authenticated. Please run `wrangler login`."
12
+ );
13
+ }
29
14
 
30
- function Email(props: { tokenType: string; email: string | undefined }) {
31
- return props.email === undefined ? (
32
- <Text>
33
- 👋 You are logged in with an {props.tokenType}. Unable to retrieve email
34
- for this user. Are you missing the `User-&gt;User Details-&gt;Read`
35
- permission?
36
- </Text>
37
- ) : (
38
- <Text>
39
- 👋 You are logged in with an {props.tokenType}, associated with the email
40
- &apos;{props.email}&apos;!
41
- </Text>
15
+ if (user.email !== undefined) {
16
+ logger.log(
17
+ `👋 You are logged in with an ${
18
+ user.authType
19
+ }, associated with the email ${chalk.blue(user.email)}!`
20
+ );
21
+ } else {
22
+ logger.log(
23
+ `👋 You are logged in with an ${user.authType}. Unable to retrieve email for this user. Are you missing the \`User->User Details->Read\` permission?`
24
+ );
25
+ }
26
+ logger.table(
27
+ user.accounts.map((account) => ({
28
+ "Account Name": account.name,
29
+ "Account ID": account.id,
30
+ }))
42
31
  );
43
- }
44
-
45
- function Accounts(props: { accounts: AccountInfo[] }) {
46
- const accounts = props.accounts.map((account) => ({
47
- "Account Name": account.name,
48
- "Account ID": account.id,
49
- }));
50
- return <Table data={accounts}></Table>;
51
- }
52
-
53
- function Permissions(props: {
54
- tokenPermissions: string[] | undefined;
55
- tokenType: string;
56
- }) {
57
32
  const permissions =
58
- props.tokenPermissions?.map((scope) => scope.split(":")) || [];
59
- return props.tokenType === "OAuth Token" ? (
60
- props.tokenPermissions ? (
61
- <>
62
- <Text>
63
- 🔓 Token Permissions: If scopes are missing, you may need to logout
64
- and re-login.
65
- </Text>
66
- <Text>Scope (Access)</Text>
67
- {permissions.map(([scope, access], index) => (
68
- <Fragment key={`${scope}${index}`}>
69
- <Text>
70
- - {scope} {access && `(${access})`}
71
- </Text>
72
- </Fragment>
73
- ))}
74
- </>
75
- ) : null
76
- ) : (
77
- <Text>
78
- 🔓 To see token permissions visit
79
- https://dash.cloudflare.com/profile/api-tokens
80
- </Text>
33
+ user.tokenPermissions?.map((scope) => scope.split(":")) ?? [];
34
+
35
+ if (user.authType !== "OAuth Token") {
36
+ return void logger.log(
37
+ `🔓 To see token permissions visit https://dash.cloudflare.com/profile/api-tokens`
38
+ );
39
+ }
40
+ logger.log(
41
+ `🔓 Token Permissions: If scopes are missing, you may need to logout and re-login.`
81
42
  );
43
+ logger.log(`Scope (Access)`);
44
+ for (const [scope, access] of permissions) {
45
+ logger.log(`- ${scope} ${access ? `(${access})` : ``}`);
46
+ }
82
47
  }
83
48
 
49
+ type AuthType = "Global API Key" | "API Token" | "OAuth Token";
84
50
  export interface UserInfo {
85
51
  apiToken: string;
86
- authType: string;
52
+ authType: AuthType;
87
53
  email: string | undefined;
88
54
  accounts: AccountInfo[];
89
55
  tokenPermissions: string[] | undefined;