wrangler 0.0.0-e7a0cd5b → 0.0.0-e7cf0caa

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 (237) hide show
  1. package/README.md +3 -3
  2. package/bin/wrangler.js +30 -10
  3. package/miniflare-dist/index.mjs +126 -74
  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 +567 -86
  9. package/src/__tests__/d1/d1.test.ts +82 -0
  10. package/src/__tests__/d1/execute.test.ts +66 -0
  11. package/src/__tests__/d1/migrate.test.ts +157 -0
  12. package/src/__tests__/d1/splitter.test.ts +255 -0
  13. package/src/__tests__/delete.test.ts +91 -49
  14. package/src/__tests__/deployments.test.ts +330 -56
  15. package/src/__tests__/dev.test.tsx +198 -90
  16. package/src/__tests__/generate.test.ts +12 -10
  17. package/src/__tests__/guess-worker-format.test.ts +35 -0
  18. package/src/__tests__/helpers/end-event-loop.ts +6 -0
  19. package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
  20. package/src/__tests__/helpers/mock-console.ts +6 -0
  21. package/src/__tests__/helpers/mock-dialogs.ts +71 -85
  22. package/src/__tests__/helpers/mock-get-pages-upload-token.ts +25 -0
  23. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -5
  24. package/src/__tests__/helpers/mock-known-routes.ts +7 -2
  25. package/src/__tests__/helpers/mock-kv.ts +29 -16
  26. package/src/__tests__/helpers/mock-oauth-flow.ts +103 -92
  27. package/src/__tests__/helpers/mock-process.ts +8 -13
  28. package/src/__tests__/helpers/mock-set-timeout.ts +16 -0
  29. package/src/__tests__/helpers/msw/blob-worker.cjs +19 -0
  30. package/src/__tests__/helpers/msw/handlers/deployments.ts +128 -27
  31. package/src/__tests__/helpers/msw/handlers/namespaces.ts +18 -41
  32. package/src/__tests__/helpers/msw/handlers/oauth.ts +13 -18
  33. package/src/__tests__/helpers/msw/handlers/r2.ts +14 -34
  34. package/src/__tests__/helpers/msw/handlers/script.ts +9 -28
  35. package/src/__tests__/helpers/msw/handlers/user.ts +13 -24
  36. package/src/__tests__/helpers/msw/handlers/zones.ts +6 -8
  37. package/src/__tests__/helpers/msw/index.ts +32 -1
  38. package/src/__tests__/helpers/msw/read-file-sync.js +61 -0
  39. package/src/__tests__/helpers/string-dynamic-values-matcher.ts +28 -0
  40. package/src/__tests__/helpers/worker-scripts/parent-worker.js +4 -1
  41. package/src/__tests__/index.test.ts +90 -72
  42. package/src/__tests__/init.test.ts +927 -631
  43. package/src/__tests__/jest.setup.ts +19 -42
  44. package/src/__tests__/kv.test.ts +355 -231
  45. package/src/__tests__/logger.test.ts +68 -2
  46. package/src/__tests__/logout.test.ts +47 -0
  47. package/src/__tests__/metrics.test.ts +94 -51
  48. package/src/__tests__/middleware.scheduled.test.ts +42 -32
  49. package/src/__tests__/middleware.test.ts +212 -122
  50. package/src/__tests__/mtls-certificates.test.ts +588 -0
  51. package/src/__tests__/pages/deployment-list.test.ts +80 -0
  52. package/src/__tests__/pages/functions-build.test.ts +452 -0
  53. package/src/__tests__/pages/pages.test.ts +81 -0
  54. package/src/__tests__/pages/project-create.test.ts +63 -0
  55. package/src/__tests__/pages/project-list.test.ts +110 -0
  56. package/src/__tests__/pages/project-upload.test.ts +500 -0
  57. package/src/__tests__/pages/publish.test.ts +2864 -0
  58. package/src/__tests__/pages-deployment-tail.test.ts +955 -0
  59. package/src/__tests__/parse.test.ts +106 -0
  60. package/src/__tests__/publish.test.ts +1615 -852
  61. package/src/__tests__/pubsub.test.ts +226 -100
  62. package/src/__tests__/queues.test.ts +238 -77
  63. package/src/__tests__/r2.test.ts +55 -57
  64. package/src/__tests__/secret.test.ts +179 -130
  65. package/src/__tests__/tail.test.ts +267 -109
  66. package/src/__tests__/test-old-node-version.js +3 -3
  67. package/src/__tests__/traverse-module-graph.test.ts +220 -0
  68. package/src/__tests__/tsconfig-sanity.ts +12 -0
  69. package/src/__tests__/tsconfig.json +8 -0
  70. package/src/__tests__/tsconfig.tsbuildinfo +1 -0
  71. package/src/__tests__/type-generation.test.ts +43 -26
  72. package/src/__tests__/user.test.ts +35 -72
  73. package/src/__tests__/whoami.test.tsx +8 -114
  74. package/src/__tests__/worker-namespace.test.ts +83 -69
  75. package/src/api/dev.ts +168 -60
  76. package/src/api/index.ts +10 -1
  77. package/src/api/mtls-certificate.ts +148 -0
  78. package/src/api/pages/create-worker-bundle-contents.ts +76 -0
  79. package/src/api/pages/index.ts +5 -0
  80. package/src/api/pages/publish.tsx +355 -0
  81. package/src/bundle.ts +263 -140
  82. package/src/cfetch/index.ts +0 -2
  83. package/src/cfetch/internal.ts +16 -21
  84. package/src/cli.ts +4 -4
  85. package/src/config/config.ts +14 -7
  86. package/src/config/environment.ts +79 -24
  87. package/src/config/index.ts +99 -38
  88. package/src/config/validation-helpers.ts +1 -1
  89. package/src/config/validation.ts +248 -19
  90. package/src/create-worker-preview.ts +19 -8
  91. package/src/create-worker-upload-form.ts +60 -22
  92. package/src/d1/backups.tsx +43 -42
  93. package/src/d1/constants.ts +2 -0
  94. package/src/d1/create.tsx +44 -30
  95. package/src/d1/{delete.tsx → delete.ts} +11 -14
  96. package/src/d1/execute.tsx +209 -151
  97. package/src/d1/index.ts +31 -7
  98. package/src/d1/list.tsx +22 -10
  99. package/src/d1/migrations/apply.tsx +212 -0
  100. package/src/d1/migrations/create.tsx +79 -0
  101. package/src/d1/migrations/helpers.ts +172 -0
  102. package/src/d1/migrations/index.ts +3 -0
  103. package/src/d1/migrations/list.tsx +95 -0
  104. package/src/d1/migrations/options.ts +23 -0
  105. package/src/d1/options.ts +12 -2
  106. package/src/d1/splitter.ts +161 -0
  107. package/src/d1/{types.tsx → types.ts} +11 -0
  108. package/src/d1/utils.ts +12 -2
  109. package/src/delete.ts +8 -16
  110. package/src/deployments.ts +340 -46
  111. package/src/deprecated/index.ts +8 -302
  112. package/src/dev/dev.tsx +57 -44
  113. package/src/dev/local.tsx +294 -91
  114. package/src/dev/remote.tsx +32 -3
  115. package/src/dev/start-server.ts +89 -68
  116. package/src/dev/use-esbuild.ts +17 -18
  117. package/src/dev/validate-dev-props.ts +9 -0
  118. package/src/{dev-registry.tsx → dev-registry.ts} +2 -1
  119. package/src/dev.tsx +178 -118
  120. package/src/dialogs.ts +136 -0
  121. package/src/dispatch-namespace.ts +21 -17
  122. package/src/docs/index.ts +97 -0
  123. package/src/entry.ts +19 -11
  124. package/src/environment-variables/factory.ts +88 -0
  125. package/src/environment-variables/misc-variables.ts +30 -0
  126. package/src/generate/index.ts +298 -0
  127. package/src/git-client.ts +15 -4
  128. package/src/{index.tsx → index.ts} +137 -63
  129. package/src/init.ts +249 -187
  130. package/src/inspect.ts +16 -1
  131. package/src/jest.d.ts +4 -0
  132. package/src/kv/index.ts +15 -17
  133. package/src/logger.ts +32 -5
  134. package/src/metrics/metrics-config.ts +3 -3
  135. package/src/metrics/send-event.ts +6 -1
  136. package/src/miniflare-cli/assets.ts +64 -28
  137. package/src/miniflare-cli/index.ts +4 -7
  138. package/src/miniflare-cli/tsconfig.json +9 -0
  139. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
  140. package/src/miniflare-cli/types.ts +11 -0
  141. package/src/module-collection.ts +91 -25
  142. package/src/mtls-certificate/cli.ts +155 -0
  143. package/src/pages/build.ts +410 -0
  144. package/src/pages/buildFunctions.ts +140 -0
  145. package/src/pages/deployment-tails.ts +281 -0
  146. package/src/pages/deployments.tsx +11 -33
  147. package/src/pages/{dev.tsx → dev.ts} +147 -124
  148. package/src/pages/errors.ts +1 -0
  149. package/src/pages/functions/buildPlugin.ts +72 -69
  150. package/src/pages/functions/buildWorker.ts +249 -114
  151. package/src/pages/functions/tsconfig.json +8 -0
  152. package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
  153. package/src/pages/{functions.tsx → functions.ts} +8 -4
  154. package/src/pages/{index.tsx → index.ts} +11 -3
  155. package/src/pages/projects.tsx +12 -22
  156. package/src/pages/prompt-select-project.tsx +31 -0
  157. package/src/pages/publish.tsx +45 -214
  158. package/src/pages/types.ts +6 -9
  159. package/src/pages/upload.tsx +58 -14
  160. package/src/pages/utils.ts +14 -3
  161. package/src/parse.ts +23 -1
  162. package/src/paths.ts +1 -1
  163. package/src/proxy.ts +5 -0
  164. package/src/publish/index.ts +20 -16
  165. package/src/publish/publish.ts +72 -47
  166. package/src/pubsub/{pubsub-commands.tsx → pubsub-commands.ts} +19 -20
  167. package/src/queues/cli/commands/consumer/add.ts +24 -24
  168. package/src/queues/cli/commands/consumer/index.ts +3 -6
  169. package/src/queues/cli/commands/consumer/remove.ts +11 -18
  170. package/src/queues/cli/commands/create.ts +8 -8
  171. package/src/queues/cli/commands/delete.ts +8 -8
  172. package/src/queues/cli/commands/index.ts +6 -4
  173. package/src/queues/cli/commands/list.ts +8 -8
  174. package/src/queues/client.ts +1 -0
  175. package/src/queues/utils.ts +18 -0
  176. package/src/r2/constants.ts +4 -0
  177. package/src/r2/helpers.ts +1 -0
  178. package/src/r2/index.ts +40 -28
  179. package/src/secret/index.ts +14 -17
  180. package/src/{sites.tsx → sites.ts} +1 -1
  181. package/src/tail/createTail.ts +94 -3
  182. package/src/tail/filters.ts +1 -1
  183. package/src/tail/index.ts +8 -11
  184. package/src/tail/printing.ts +15 -0
  185. package/src/traverse-module-graph.ts +53 -0
  186. package/src/tsconfig-sanity.ts +16 -0
  187. package/src/type-generation.ts +79 -57
  188. package/src/user/access.ts +0 -1
  189. package/src/user/auth-variables.ts +113 -0
  190. package/src/user/choose-account.tsx +1 -31
  191. package/src/user/index.ts +0 -1
  192. package/src/user/{user.tsx → user.ts} +123 -82
  193. package/src/utils/render.ts +93 -0
  194. package/src/{whoami.tsx → whoami.ts} +37 -71
  195. package/src/worker.ts +35 -3
  196. package/src/yargs-types.ts +22 -5
  197. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  198. package/templates/__tests__/tsconfig.json +8 -0
  199. package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
  200. package/templates/checked-fetch.js +9 -3
  201. package/templates/d1-beta-facade.js +95 -25
  202. package/templates/facade.d.ts +14 -0
  203. package/templates/first-party-worker-module-facade.ts +4 -3
  204. package/templates/format-dev-errors.ts +8 -6
  205. package/templates/gitignore +1 -0
  206. package/templates/init-tests/test-jest-new-worker.js +3 -5
  207. package/templates/init-tests/test-vitest-new-worker.js +3 -5
  208. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  209. package/templates/middleware/loader-modules.ts +2 -2
  210. package/templates/middleware/loader-sw.ts +51 -31
  211. package/templates/middleware/middleware-miniflare3-json-error.ts +32 -0
  212. package/templates/new-worker.ts +3 -0
  213. package/templates/pages-dev-pipeline.ts +4 -1
  214. package/templates/pages-shim.ts +4 -1
  215. package/templates/pages-template-plugin.ts +19 -10
  216. package/templates/serve-static-assets.ts +17 -14
  217. package/templates/service-bindings-module-facade.js +1 -0
  218. package/templates/tsconfig-sanity.ts +11 -0
  219. package/templates/tsconfig.init.json +105 -0
  220. package/templates/tsconfig.json +5 -103
  221. package/templates/tsconfig.tsbuildinfo +1 -0
  222. package/wrangler-dist/cli.d.ts +826 -77
  223. package/wrangler-dist/cli.js +130734 -140838
  224. package/wrangler-dist/wasm-sync.wasm +0 -0
  225. package/src/__tests__/api-devregistry.test.js +0 -57
  226. package/src/__tests__/d1.test.ts +0 -72
  227. package/src/__tests__/dialogs.test.tsx +0 -40
  228. package/src/__tests__/helpers/mock-cfetch.ts +0 -333
  229. package/src/__tests__/pages.test.ts +0 -2223
  230. package/src/dialogs.tsx +0 -163
  231. package/src/environment-variables.ts +0 -50
  232. package/src/pages/build.tsx +0 -238
  233. package/src/user/env-vars.ts +0 -46
  234. package/templates/experimental-local-cache-stubs.js +0 -27
  235. /package/src/{bundle-reporter.tsx → bundle-reporter.ts} +0 -0
  236. /package/src/dev/{get-local-persistence-path.tsx → get-local-persistence-path.ts} +0 -0
  237. /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,7 @@ 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",
316
350
  } as const;
317
351
 
318
352
  /**
@@ -330,11 +364,6 @@ export function validateScopeKeys(
330
364
  return scopes.every((scope) => scope in Scopes);
331
365
  }
332
366
 
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
367
  /**
339
368
  * To allow OAuth callbacks in environments such as WebContainer we need to
340
369
  * create a host URL which only resolves `localhost` to a WebContainer
@@ -374,7 +403,7 @@ function getAuthTokens(config?: UserAuthConfig): AuthTokens | undefined {
374
403
  };
375
404
  } else if (api_token) {
376
405
  logger.warn(
377
- "It looks like you have used Wrangler 1's `config` command to login with an API token.\n" +
406
+ "It looks like you have used Wrangler v1's `config` command to login with an API token.\n" +
378
407
  "This is no longer supported in the current version of Wrangler.\n" +
379
408
  "If you wish to authenticate via an API token then please set the `CLOUDFLARE_API_TOKEN` environment variable."
380
409
  );
@@ -386,7 +415,7 @@ function getAuthTokens(config?: UserAuthConfig): AuthTokens | undefined {
386
415
  }
387
416
 
388
417
  /**
389
- * Run the initialisation of the auth state, in the case that something changed.
418
+ * Run the initialization of the auth state, in the case that something changed.
390
419
  *
391
420
  * This runs automatically whenever `writeAuthConfigFile` is run, so generally
392
421
  * you won't need to call it yourself.
@@ -625,8 +654,8 @@ export async function getAuthURL(scopes = ScopeKeys): Promise<string> {
625
654
  });
626
655
 
627
656
  return generateAuthUrl({
628
- authUrl: AUTH_URL,
629
- clientId: CLIENT_ID,
657
+ authUrl: getAuthUrlFromEnv(),
658
+ clientId: getClientIdFromEnv(),
630
659
  callbackUrl: CALLBACK_URL,
631
660
  scopes,
632
661
  stateQueryParam,
@@ -653,19 +682,14 @@ async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
653
682
  logger.warn("No refresh token is present.");
654
683
  }
655
684
 
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
- },
685
+ const params = new URLSearchParams({
686
+ grant_type: "refresh_token",
687
+ refresh_token: LocalState.refreshToken?.value ?? "",
688
+ client_id: getClientIdFromEnv(),
667
689
  });
668
690
 
691
+ const response = await fetchAuthToken(params);
692
+
669
693
  if (response.status >= 400) {
670
694
  let tokenExchangeResErr = undefined;
671
695
 
@@ -743,20 +767,15 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
743
767
  logger.warn("No authorization grant code is being passed.");
744
768
  }
745
769
 
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
- },
770
+ const params = new URLSearchParams({
771
+ grant_type: `authorization_code`,
772
+ code: authorizationCode ?? "",
773
+ redirect_uri: CALLBACK_URL,
774
+ client_id: getClientIdFromEnv(),
775
+ code_verifier: codeVerifier,
759
776
  });
777
+
778
+ const response = await fetchAuthToken(params);
760
779
  if (!response.ok) {
761
780
  const { error } = (await response.json()) as { error: string };
762
781
  // .catch((_) => ({ error: "invalid_json" }));
@@ -875,6 +894,7 @@ export function readAuthConfigFile(): UserAuthConfig {
875
894
 
876
895
  type LoginProps = {
877
896
  scopes?: Scope[];
897
+ browser: boolean;
878
898
  };
879
899
 
880
900
  export async function loginOrRefreshIfRequired(): Promise<boolean> {
@@ -901,7 +921,9 @@ export async function loginOrRefreshIfRequired(): Promise<boolean> {
901
921
  }
902
922
  }
903
923
 
904
- export async function login(props?: LoginProps): Promise<boolean> {
924
+ export async function login(
925
+ props: LoginProps = { browser: true }
926
+ ): Promise<boolean> {
905
927
  logger.log("Attempting to login via OAuth...");
906
928
  const urlToOpen = await getAuthURL(props?.scopes);
907
929
  let server: http.Server;
@@ -914,7 +936,7 @@ export async function login(props?: LoginProps): Promise<boolean> {
914
936
  server.close();
915
937
  clearTimeout(loginTimeoutHandle);
916
938
  resolve(false);
917
- }, 60000); // wait for 60 seconds for the user to authorize
939
+ }, 120000); // wait for 120 seconds for the user to authorize
918
940
  });
919
941
 
920
942
  const loginPromise = new Promise<boolean>((resolve, reject) => {
@@ -986,9 +1008,12 @@ export async function login(props?: LoginProps): Promise<boolean> {
986
1008
 
987
1009
  server.listen(8976);
988
1010
  });
989
-
990
- logger.log(`Opening a link in your default browser: ${urlToOpen}`);
991
- await openInBrowser(urlToOpen);
1011
+ if (props?.browser) {
1012
+ logger.log(`Opening a link in your default browser: ${urlToOpen}`);
1013
+ await openInBrowser(urlToOpen);
1014
+ } else {
1015
+ logger.log(`Visit this link to authenticate: ${urlToOpen}`);
1016
+ }
992
1017
 
993
1018
  return Promise.race([timerPromise, loginPromise]);
994
1019
  }
@@ -1032,11 +1057,11 @@ export async function logout(): Promise<void> {
1032
1057
  }
1033
1058
 
1034
1059
  const body =
1035
- `client_id=${encodeURIComponent(CLIENT_ID)}&` +
1060
+ `client_id=${encodeURIComponent(getClientIdFromEnv())}&` +
1036
1061
  `token_type_hint=refresh_token&` +
1037
1062
  `token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
1038
1063
 
1039
- const response = await fetch(REVOKE_URL, {
1064
+ const response = await fetch(getRevokeUrlFromEnv(), {
1040
1065
  method: "POST",
1041
1066
  body,
1042
1067
  headers: {
@@ -1049,11 +1074,11 @@ export async function logout(): Promise<void> {
1049
1074
  );
1050
1075
  }
1051
1076
  const body =
1052
- `client_id=${encodeURIComponent(CLIENT_ID)}&` +
1077
+ `client_id=${encodeURIComponent(getClientIdFromEnv())}&` +
1053
1078
  `token_type_hint=refresh_token&` +
1054
1079
  `token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
1055
1080
 
1056
- const response = await fetch(REVOKE_URL, {
1081
+ const response = await fetch(getRevokeUrlFromEnv(), {
1057
1082
  method: "POST",
1058
1083
  body,
1059
1084
  headers: {
@@ -1071,8 +1096,7 @@ export function listScopes(message = "💁 Available scopes:"): void {
1071
1096
  Scope: scope,
1072
1097
  Description: Scopes[scope],
1073
1098
  }));
1074
- const { unmount } = render(<Table data={data} />);
1075
- unmount();
1099
+ logger.table(data);
1076
1100
  // TODO: maybe a good idea to show usage here
1077
1101
  }
1078
1102
 
@@ -1091,36 +1115,32 @@ export async function getAccountId(): Promise<string | undefined> {
1091
1115
  return accounts[0].id;
1092
1116
  }
1093
1117
 
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;
1118
+ try {
1119
+ const accountID = await select("Select an account", {
1120
+ choices: accounts.map((account) => ({
1121
+ title: account.name,
1122
+ value: account.id,
1123
+ })),
1124
+ });
1125
+ const account = accounts.find(
1126
+ (a) => a.id === accountID
1127
+ ) as ChooseAccountItem;
1128
+ saveAccountToCache({ id: account.id, name: account.name });
1129
+ return accountID;
1130
+ } catch (e) {
1131
+ // Did we try to select an account in CI or a non-interactive terminal?
1132
+ if (e instanceof NoDefaultValueProvided) {
1133
+ throw new Error(
1134
+ `More than one account available but unable to select one in non-interactive mode.
1135
+ Please set the appropriate \`account_id\` in your \`wrangler.toml\` file.
1136
+ Available accounts are (\`<name>\`: \`<account_id>\`):
1137
+ ${accounts
1138
+ .map((account) => ` \`${account.name}\`: \`${account.id}\``)
1139
+ .join("\n")}`
1140
+ );
1141
+ }
1142
+ throw e;
1114
1143
  }
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
1144
  }
1125
1145
 
1126
1146
  /**
@@ -1133,7 +1153,7 @@ export async function requireAuth(config: {
1133
1153
  if (!loggedIn) {
1134
1154
  if (!isInteractive() || CI.isCI()) {
1135
1155
  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."
1156
+ "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
1157
  );
1138
1158
  } else {
1139
1159
  // didn't login, let's just quit
@@ -1190,3 +1210,24 @@ export function getAccountFromCache():
1190
1210
  export function getScopes(): Scope[] | undefined {
1191
1211
  return LocalState.scopes;
1192
1212
  }
1213
+
1214
+ /**
1215
+ * Make a request to the Cloudflare OAuth endpoint to get a token.
1216
+ *
1217
+ * Note that the `body` of the POST request is form-urlencoded so
1218
+ * can be represented by a URLSearchParams object.
1219
+ */
1220
+ async function fetchAuthToken(body: URLSearchParams) {
1221
+ const headers: Record<string, string> = {
1222
+ "Content-Type": "application/x-www-form-urlencoded",
1223
+ };
1224
+ if (await domainUsesAccess(getAuthDomainFromEnv())) {
1225
+ // We are trying to access the staging API so we need an "access token".
1226
+ headers["Cookie"] = `CF_Authorization=${await getCloudflareAccessToken()}`;
1227
+ }
1228
+ return await fetch(getTokenUrlFromEnv(), {
1229
+ method: "POST",
1230
+ body: body.toString(),
1231
+ headers,
1232
+ });
1233
+ }
@@ -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;
package/src/worker.ts CHANGED
@@ -79,6 +79,15 @@ export interface CfKvNamespace {
79
79
  id: string;
80
80
  }
81
81
 
82
+ /**
83
+ * A binding to send email.
84
+ */
85
+ export interface CfSendEmailBindings {
86
+ name: string;
87
+ destination_address?: string;
88
+ allowed_destination_addresses?: string[];
89
+ }
90
+
82
91
  /**
83
92
  * A binding to a wasm module (in service-worker format)
84
93
  */
@@ -126,12 +135,16 @@ export interface CfR2Bucket {
126
135
  export const D1_BETA_PREFIX = `__D1_BETA__` as const;
127
136
  export type D1PrefixedBinding = `${typeof D1_BETA_PREFIX}${string}`;
128
137
 
138
+ // TODO: figure out if this is duplicated in packages/wrangler/src/config/environment.ts
129
139
  export interface CfD1Database {
130
- // For now, all D1 bindings are beta
140
+ // For now, all D1 bindings are alpha
131
141
  binding: D1PrefixedBinding;
132
142
  database_id: string;
133
143
  database_name?: string;
134
144
  preview_database_id?: string;
145
+ database_internal_env?: string;
146
+ migrations_table?: string;
147
+ migrations_dir?: string;
135
148
  }
136
149
 
137
150
  interface CfService {
@@ -140,11 +153,21 @@ interface CfService {
140
153
  environment?: string;
141
154
  }
142
155
 
156
+ interface CfAnalyticsEngineDataset {
157
+ binding: string;
158
+ dataset?: string;
159
+ }
160
+
143
161
  interface CfDispatchNamespace {
144
162
  binding: string;
145
163
  namespace: string;
146
164
  }
147
165
 
166
+ interface CfMTlsCertificate {
167
+ binding: string;
168
+ certificate_id: string;
169
+ }
170
+
148
171
  interface CfLogfwdr {
149
172
  schema: string | undefined;
150
173
  bindings: CfLogfwdrBinding[];
@@ -160,6 +183,13 @@ interface CfUnsafeBinding {
160
183
  type: string;
161
184
  }
162
185
 
186
+ type CfUnsafeMetadata = Record<string, unknown>;
187
+
188
+ interface CfUnsafe {
189
+ bindings: CfUnsafeBinding[] | undefined;
190
+ metadata: CfUnsafeMetadata | undefined;
191
+ }
192
+
163
193
  export interface CfDurableObjectMigrations {
164
194
  old_tag?: string;
165
195
  new_tag: string;
@@ -194,8 +224,8 @@ export interface CfWorkerInit {
194
224
  */
195
225
  bindings: {
196
226
  vars: CfVars | undefined;
197
- metadata_binding: string | undefined;
198
227
  kv_namespaces: CfKvNamespace[] | undefined;
228
+ send_email: CfSendEmailBindings[] | undefined;
199
229
  wasm_modules: CfWasmModuleBindings | undefined;
200
230
  text_blobs: CfTextBlobBindings | undefined;
201
231
  data_blobs: CfDataBlobBindings | undefined;
@@ -204,9 +234,11 @@ export interface CfWorkerInit {
204
234
  r2_buckets: CfR2Bucket[] | undefined;
205
235
  d1_databases: CfD1Database[] | undefined;
206
236
  services: CfService[] | undefined;
237
+ analytics_engine_datasets: CfAnalyticsEngineDataset[] | undefined;
207
238
  dispatch_namespaces: CfDispatchNamespace[] | undefined;
239
+ mtls_certificates: CfMTlsCertificate[] | undefined;
208
240
  logfwdr: CfLogfwdr | undefined;
209
- unsafe: CfUnsafeBinding[] | undefined;
241
+ unsafe: CfUnsafe | undefined;
210
242
  };
211
243
  migrations: CfDurableObjectMigrations | undefined;
212
244
  compatibility_date: string | undefined;