wrangler 0.0.0-e2a26a0 → 0.0.0-e3cab74

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 (68) hide show
  1. package/package.json +16 -7
  2. package/pages/functions/filepath-routing.test.ts +112 -36
  3. package/pages/functions/filepath-routing.ts +66 -119
  4. package/pages/functions/routes.ts +8 -17
  5. package/pages/functions/template-worker.ts +3 -9
  6. package/src/__tests__/configuration.test.ts +2185 -0
  7. package/src/__tests__/dev.test.tsx +48 -6
  8. package/src/__tests__/guess-worker-format.test.ts +66 -0
  9. package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
  10. package/src/__tests__/helpers/mock-account-id.ts +6 -6
  11. package/src/__tests__/helpers/mock-bin.ts +4 -1
  12. package/src/__tests__/helpers/mock-cfetch.ts +2 -2
  13. package/src/__tests__/helpers/mock-console.ts +11 -5
  14. package/src/__tests__/helpers/mock-web-socket.ts +29 -0
  15. package/src/__tests__/helpers/run-wrangler.ts +5 -3
  16. package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
  17. package/src/__tests__/index.test.ts +343 -20
  18. package/src/__tests__/jest.setup.ts +21 -1
  19. package/src/__tests__/kv.test.ts +82 -80
  20. package/src/__tests__/package-manager.test.ts +2 -2
  21. package/src/__tests__/parse.test.ts +322 -0
  22. package/src/__tests__/publish.test.ts +1428 -244
  23. package/src/__tests__/r2.test.ts +198 -0
  24. package/src/__tests__/secret.test.ts +176 -60
  25. package/src/__tests__/sentry.test.ts +146 -0
  26. package/src/__tests__/tail.test.ts +564 -0
  27. package/src/api/form_data.ts +106 -23
  28. package/src/api/preview.ts +32 -13
  29. package/src/api/worker.ts +49 -18
  30. package/src/bundle.ts +133 -0
  31. package/src/cfetch/index.ts +8 -6
  32. package/src/cfetch/internal.ts +49 -17
  33. package/src/cli.ts +10 -0
  34. package/src/config/README.md +107 -0
  35. package/src/config/config.ts +265 -0
  36. package/src/config/diagnostics.ts +80 -0
  37. package/src/config/environment.ts +305 -0
  38. package/src/config/index.ts +52 -0
  39. package/src/config/validation-helpers.ts +370 -0
  40. package/src/config/validation.ts +1017 -0
  41. package/src/dev.tsx +338 -211
  42. package/src/entry.ts +57 -0
  43. package/src/environment-variables.ts +33 -0
  44. package/src/guess-worker-format.ts +68 -0
  45. package/src/index.tsx +860 -634
  46. package/src/inspect.ts +16 -3
  47. package/src/kv.tsx +20 -29
  48. package/src/module-collection.ts +141 -34
  49. package/src/open-in-browser.ts +13 -0
  50. package/src/package-manager.ts +21 -15
  51. package/src/pages.tsx +9 -10
  52. package/src/parse.ts +183 -0
  53. package/src/proxy.ts +11 -5
  54. package/src/publish.ts +272 -314
  55. package/src/r2.ts +50 -0
  56. package/src/reporting.ts +115 -0
  57. package/src/sites.tsx +16 -15
  58. package/src/tail/filters.ts +279 -0
  59. package/src/tail/index.ts +299 -0
  60. package/src/tail/printing.ts +62 -0
  61. package/src/user.tsx +74 -44
  62. package/templates/new-worker.js +1 -1
  63. package/templates/new-worker.ts +1 -1
  64. package/templates/tsconfig.json +105 -0
  65. package/wrangler-dist/cli.js +51308 -36440
  66. package/wrangler-dist/cli.js.map +3 -3
  67. package/src/config.ts +0 -641
  68. package/src/tail.tsx +0 -73
@@ -0,0 +1,299 @@
1
+ import WebSocket from "ws";
2
+ import { version as packageVersion } from "../../package.json";
3
+ import { fetchResult } from "../cfetch";
4
+ import type { TailFilterMessage, Outcome } from "./filters";
5
+ export type { TailCLIFilters } from "./filters";
6
+ export { translateCLICommandToFilterMessage } from "./filters";
7
+ export { jsonPrintLogs, prettyPrintLogs } from "./printing";
8
+ import type { Request } from "undici";
9
+
10
+ /**
11
+ * When creating a Tail, the response from the API contains
12
+ * - an ID used for identifying the tail
13
+ * - a URL to a WebSocket connection available for the tail to connect to
14
+ * - an expiration date when the tail is no longer guaranteed to be valid
15
+ */
16
+ type TailCreationApiResponse = {
17
+ id: string;
18
+ url: string;
19
+ expires_at: Date;
20
+ };
21
+
22
+ /**
23
+ * Generate a URL that, when `cfetch`ed, creates a tail.
24
+ *
25
+ * https://api.cloudflare.com/#worker-tail-logs-start-tail
26
+ *
27
+ * @param accountId the account ID associated with the worker to tail
28
+ * @param workerName the name of the worker to tail
29
+ * @returns a `cfetch`-ready URL for creating a new tail
30
+ */
31
+ function makeCreateTailUrl(
32
+ accountId: string,
33
+ workerName: string,
34
+ env: string | undefined
35
+ ): string {
36
+ return env
37
+ ? `/accounts/${accountId}/workers/services/${workerName}/environments/${env}/tails`
38
+ : `/accounts/${accountId}/workers/scripts/${workerName}/tails`;
39
+ }
40
+
41
+ /**
42
+ * Generate a URL that, when `cfetch`ed, deletes a tail
43
+ *
44
+ * https://api.cloudflare.com/#worker-tail-logs-delete-tail
45
+ *
46
+ * @param accountId the account ID associated with the worker we're tailing
47
+ * @param workerName the name of the worker we're tailing
48
+ * @param tailId the ID of the tail we want to delete
49
+ * @returns a `cfetch`-ready URL for deleting a tail
50
+ */
51
+ function makeDeleteTailUrl(
52
+ accountId: string,
53
+ workerName: string,
54
+ tailId: string,
55
+ env: string | undefined
56
+ ): string {
57
+ return env
58
+ ? `/accounts/${accountId}/workers/services/${workerName}/environments/${env}/tails/${tailId}`
59
+ : `/accounts/${accountId}/workers/scripts/${workerName}/tails/${tailId}`;
60
+ }
61
+
62
+ /**
63
+ * Create and connect to a tail.
64
+ *
65
+ * Under the hood, this function
66
+ * - Registers a new Tail with the API
67
+ * - Connects to the tail worker
68
+ * - Sends any filters over the connection
69
+ *
70
+ * @param accountId the account ID associated with the worker to tail
71
+ * @param workerName the name of the worker to tail
72
+ * @param message a `TailFilterMessage` to send up to the tail worker
73
+ * @returns a websocket connection, an expiration, and a function to call to delete the tail
74
+ */
75
+ export async function createTail(
76
+ accountId: string,
77
+ workerName: string,
78
+ message: TailFilterMessage,
79
+ env: string | undefined
80
+ ): Promise<{
81
+ tail: WebSocket;
82
+ expiration: Date;
83
+ deleteTail: () => Promise<void>;
84
+ }> {
85
+ // create the tail
86
+ const createTailUrl = makeCreateTailUrl(accountId, workerName, env);
87
+ const {
88
+ id: tailId,
89
+ url: websocketUrl,
90
+ expires_at: expiration,
91
+ } = await fetchResult<TailCreationApiResponse>(createTailUrl, {
92
+ method: "POST",
93
+ });
94
+
95
+ // delete the tail (not yet!)
96
+ const deleteUrl = makeDeleteTailUrl(accountId, workerName, tailId, env);
97
+ async function deleteTail() {
98
+ await fetchResult(deleteUrl, { method: "DELETE" });
99
+ }
100
+
101
+ // connect to the tail
102
+ const tail = new WebSocket(websocketUrl, "trace-v1", {
103
+ headers: {
104
+ "Sec-WebSocket-Protocol": "trace-v1", // needs to be `trace-v1` to be accepted
105
+ "User-Agent": `wrangler-js/${packageVersion}`,
106
+ },
107
+ });
108
+
109
+ // send filters when we open up
110
+ tail.on("open", function () {
111
+ tail.send(
112
+ JSON.stringify(message),
113
+ { binary: false, compress: false, mask: false, fin: true },
114
+ (err) => {
115
+ if (err) {
116
+ throw err;
117
+ }
118
+ }
119
+ );
120
+ });
121
+
122
+ return { tail, expiration, deleteTail };
123
+ }
124
+
125
+ /**
126
+ * Everything captured by the trace worker and sent to us via
127
+ * `wrangler tail` is structured JSON that deserializes to this type.
128
+ */
129
+ export type TailEventMessage = {
130
+ /**
131
+ * Whether the execution of this worker succeeded or failed
132
+ */
133
+ outcome: Outcome;
134
+
135
+ /**
136
+ * The name of the script we're tailing
137
+ */
138
+ scriptName?: string;
139
+
140
+ /**
141
+ * Any exceptions raised by the worker
142
+ */
143
+ exceptions: {
144
+ /**
145
+ * The name of the exception.
146
+ */
147
+ name: string;
148
+
149
+ /**
150
+ * The error message
151
+ */
152
+ message: unknown;
153
+
154
+ /**
155
+ * When the exception was raised/thrown
156
+ */
157
+ timestamp: number;
158
+ }[];
159
+
160
+ /**
161
+ * Any logs sent out by the worker
162
+ */
163
+ logs: {
164
+ message: unknown;
165
+ level: string; // TODO: make this a union of possible values
166
+ timestamp: number;
167
+ }[];
168
+
169
+ /**
170
+ * When the event was triggered
171
+ */
172
+ eventTimestamp: number;
173
+
174
+ /**
175
+ * The event that triggered the worker. In the case of an HTTP request,
176
+ * this will be a RequestEvent. If it's a cron trigger, it'll be a
177
+ * ScheduledEvent.
178
+ *
179
+ * Until workers-types exposes individual types for export, we'll have
180
+ * to just re-define these types ourselves.
181
+ */
182
+ event: RequestEvent | ScheduledEvent;
183
+ };
184
+
185
+ /**
186
+ * A request that triggered worker execution
187
+ */
188
+
189
+ export type RequestEvent = {
190
+ request: Pick<Request, "url" | "method" | "headers"> & {
191
+ /**
192
+ * Cloudflare-specific properties
193
+ * https://developers.cloudflare.com/workers/runtime-apis/request#incomingrequestcfproperties
194
+ */
195
+ cf: {
196
+ /**
197
+ * How long (in ms) it took for the client's TCP connection to make a
198
+ * round trip to the worker and back. For all my gamers out there,
199
+ * this is the request's ping
200
+ */
201
+ clientTcpRtt?: number;
202
+
203
+ /**
204
+ * Longitude and Latitude of where the request originated from
205
+ */
206
+ longitude?: string;
207
+ latitude?: string;
208
+
209
+ /**
210
+ * What cipher was used to establish the TLS connection
211
+ */
212
+ tlsCipher: string;
213
+
214
+ /**
215
+ * Which continent the request came from.
216
+ */
217
+ continent?: "NA";
218
+
219
+ /**
220
+ * ASN of the incoming request
221
+ */
222
+ asn: number;
223
+
224
+ /**
225
+ * The country the incoming request is coming from
226
+ */
227
+ country?: string;
228
+
229
+ /**
230
+ * The TLS version the connection used
231
+ */
232
+ tlsVersion: string;
233
+
234
+ /**
235
+ * The colo that processed the request (i.e. the airport code
236
+ * of the closest city to the server that spun up the worker)
237
+ */
238
+ colo: string;
239
+
240
+ /**
241
+ * The timezone where the request came from
242
+ */
243
+ timezone?: string;
244
+
245
+ /**
246
+ * The city where the request came from
247
+ */
248
+ city?: string;
249
+
250
+ /**
251
+ * The browser-requested prioritization information in the request object
252
+ */
253
+ requestPriority?: string;
254
+
255
+ /**
256
+ * Which version of HTTP the request came over e.g. "HTTP/2"
257
+ */
258
+ httpProtocol: string;
259
+
260
+ /**
261
+ * The region where the request originated from
262
+ */
263
+ region?: string;
264
+ regionCode?: string;
265
+
266
+ /**
267
+ * The organization which owns the ASN of the incoming request, for example, Google Cloud.
268
+ */
269
+ asOrganization: string;
270
+
271
+ /**
272
+ * Metro code (DMA) of the incoming request, for example, "635".
273
+ */
274
+ metroCode?: string;
275
+
276
+ /**
277
+ * Postal code of the incoming request, for example, "78701".
278
+ */
279
+ postalCode?: string;
280
+ };
281
+ };
282
+ };
283
+
284
+ /**
285
+ * An event that was triggered at a certain time
286
+ */
287
+ export type ScheduledEvent = {
288
+ /**
289
+ * The cron pattern that matched when this event fired
290
+ */
291
+ cron: string;
292
+
293
+ /**
294
+ * The time this worker was scheduled to run.
295
+ * For some reason, this doesn't...work correctly when we
296
+ * do it directly as a Date. So parse it later on your own.
297
+ */
298
+ scheduledTime: number;
299
+ };
@@ -0,0 +1,62 @@
1
+ import type { RequestEvent, ScheduledEvent, TailEventMessage } from ".";
2
+ import type { Outcome } from "./filters";
3
+ import type WebSocket from "ws";
4
+
5
+ export function prettyPrintLogs(data: WebSocket.RawData): void {
6
+ const eventMessage: TailEventMessage = JSON.parse(data.toString());
7
+
8
+ if (isScheduledEvent(eventMessage.event)) {
9
+ const cronPattern = eventMessage.event.cron;
10
+ const datetime = new Date(
11
+ eventMessage.event.scheduledTime
12
+ ).toLocaleString();
13
+ const outcome = prettifyOutcome(eventMessage.outcome);
14
+
15
+ console.log(`"${cronPattern}" @ ${datetime} - ${outcome}`);
16
+ } else {
17
+ const requestMethod = eventMessage.event.request.method.toUpperCase();
18
+ const url = eventMessage.event.request.url;
19
+ const outcome = prettifyOutcome(eventMessage.outcome);
20
+ const datetime = new Date(eventMessage.eventTimestamp).toLocaleString();
21
+
22
+ console.log(`${requestMethod} ${url} - ${outcome} @ ${datetime}`);
23
+ }
24
+
25
+ if (eventMessage.logs.length > 0) {
26
+ eventMessage.logs.forEach(({ level, message }) => {
27
+ console.log(` (${level}) ${message}`);
28
+ });
29
+ }
30
+
31
+ if (eventMessage.exceptions.length > 0) {
32
+ eventMessage.exceptions.forEach(({ name, message }) => {
33
+ console.error(` ${name}: ${message}`);
34
+ });
35
+ }
36
+ }
37
+
38
+ export function jsonPrintLogs(data: WebSocket.RawData): void {
39
+ console.log(JSON.stringify(JSON.parse(data.toString()), null, 2));
40
+ }
41
+
42
+ function isScheduledEvent(
43
+ event: RequestEvent | ScheduledEvent
44
+ ): event is ScheduledEvent {
45
+ return "cron" in event;
46
+ }
47
+
48
+ function prettifyOutcome(outcome: Outcome): string {
49
+ switch (outcome) {
50
+ case "ok":
51
+ return "Ok";
52
+ case "canceled":
53
+ return "Canceled";
54
+ case "exceededCpu":
55
+ return "Exceeded CPU Limit";
56
+ case "exception":
57
+ return "Exception Thrown";
58
+ case "unknown":
59
+ default:
60
+ return "Unknown";
61
+ }
62
+ }
package/src/user.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  /* Based heavily on code from https://github.com/BitySA/oauth2-auth-code-pkce */
2
2
 
3
- /*
3
+ /*
4
4
 
5
5
  Apache License
6
6
  Version 2.0, January 2004
@@ -211,20 +211,37 @@ import { readFile, writeFile, rm, mkdir } from "node:fs/promises";
211
211
  import http from "node:http";
212
212
  import os from "node:os";
213
213
  import path from "node:path";
214
- import process from "node:process";
215
214
  import url from "node:url";
216
215
  import { TextEncoder } from "node:util";
217
216
  import TOML from "@iarna/toml";
218
217
  import { render, Text } from "ink";
219
218
  import SelectInput from "ink-select-input";
220
219
  import Table from "ink-table";
221
- import open from "open";
222
220
  import React from "react";
223
221
  import { fetch } from "undici";
224
- import { CF_API_BASE_URL } from "./cfetch";
222
+ import { getCloudflareApiBaseUrl } from "./cfetch";
223
+ import { getEnvironmentVariableFactory } from "./environment-variables";
224
+ import openInBrowser from "./open-in-browser";
225
+ import type { Item as SelectInputItem } from "ink-select-input/build/SelectInput";
225
226
  import type { ParsedUrlQuery } from "node:querystring";
226
227
  import type { Response } from "undici";
227
228
 
229
+ /**
230
+ * Try to read the API token from the environment.
231
+ */
232
+ const getCloudflareAPITokenFromEnv = getEnvironmentVariableFactory({
233
+ variableName: "CLOUDFLARE_API_TOKEN",
234
+ deprecatedName: "CF_API_TOKEN",
235
+ });
236
+
237
+ /**
238
+ * Try to read the account ID from the environment.
239
+ */
240
+ const getCloudflareAccountIdFromEnv = getEnvironmentVariableFactory({
241
+ variableName: "CLOUDFLARE_ACCOUNT_ID",
242
+ deprecatedName: "CF_ACCOUNT_ID",
243
+ });
244
+
228
245
  /**
229
246
  * An implementation of rfc6749#section-4.1 and rfc7636.
230
247
  */
@@ -276,14 +293,14 @@ const Scopes = {
276
293
  *
277
294
  * "offline_access" is automatically included.
278
295
  */
279
- type Scope = keyof typeof Scopes | "offline_access";
296
+ type Scope = keyof typeof Scopes;
280
297
 
281
298
  const ScopeKeys = Object.keys(Scopes) as Scope[];
282
299
 
283
300
  export function validateScopeKeys(
284
301
  scopes: string[]
285
302
  ): scopes is typeof ScopeKeys {
286
- return scopes.every((scope) => Scopes[scope]);
303
+ return scopes.every((scope) => scope in Scopes);
287
304
  }
288
305
 
289
306
  const CLIENT_ID = "54d11594-84e4-41aa-b438-e81b8fa78ee7";
@@ -301,10 +318,11 @@ let initialised = false;
301
318
  export async function initialise(): Promise<void> {
302
319
  // get refreshToken/accessToken from fs if exists
303
320
  try {
304
- // if CF_API_TOKEN available, use that
305
- if (process.env.CF_API_TOKEN) {
321
+ // if the environment variable is available, use that
322
+ const apiTokenFromEnv = getCloudflareAPITokenFromEnv();
323
+ if (apiTokenFromEnv) {
306
324
  LocalState.accessToken = {
307
- value: process.env.CF_API_TOKEN,
325
+ value: apiTokenFromEnv,
308
326
  expiry: "3021-12-31T23:59:59+00:00",
309
327
  };
310
328
  initialised = true;
@@ -344,9 +362,6 @@ function throwIfNotInitialised() {
344
362
  }
345
363
 
346
364
  export function getAPIToken(): string | undefined {
347
- if (process.env.CF_API_TOKEN) {
348
- return process.env.CF_API_TOKEN;
349
- }
350
365
  throwIfNotInitialised();
351
366
  return LocalState.accessToken?.value;
352
367
  }
@@ -561,6 +576,7 @@ export async function getAuthURL(scopes = ScopeKeys): Promise<string> {
561
576
  `?response_type=code&` +
562
577
  `client_id=${encodeURIComponent(CLIENT_ID)}&` +
563
578
  `redirect_uri=${encodeURIComponent(CALLBACK_URL)}&` +
579
+ // @ts-expect-error we add offline_access manually
564
580
  `scope=${encodeURIComponent(scopes.concat("offline_access").join(" "))}&` +
565
581
  `state=${stateQueryParam}&` +
566
582
  `code_challenge=${encodeURIComponent(codeChallenge)}&` +
@@ -568,6 +584,17 @@ export async function getAuthURL(scopes = ScopeKeys): Promise<string> {
568
584
  );
569
585
  }
570
586
 
587
+ type TokenResponse =
588
+ | {
589
+ access_token: string;
590
+ expires_in: number;
591
+ refresh_token: string;
592
+ scope: string;
593
+ }
594
+ | {
595
+ error: string;
596
+ };
597
+
571
598
  /**
572
599
  * Refresh an access token from the remote service.
573
600
  */
@@ -592,13 +619,12 @@ async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
592
619
  throw await response.json();
593
620
  } else {
594
621
  try {
595
- const json = await response.json();
596
- const { access_token, expires_in, refresh_token, scope } = json as {
597
- access_token: string;
598
- expires_in: number;
599
- refresh_token: string;
600
- scope: string;
601
- };
622
+ const json = (await response.json()) as TokenResponse;
623
+ if ("error" in json) {
624
+ throw json.error;
625
+ }
626
+
627
+ const { access_token, expires_in, refresh_token, scope } = json;
602
628
  let scopes: Scope[] = [];
603
629
 
604
630
  const accessToken: AccessToken = {
@@ -626,20 +652,24 @@ async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
626
652
  refreshToken: LocalState.refreshToken,
627
653
  };
628
654
  return accessContext;
629
- } catch (err) {
630
- const error = err?.error || "There was a network error.";
655
+ } catch (error) {
631
656
  switch (error) {
632
657
  case "invalid_grant":
633
- console.log(
658
+ console.warn(
634
659
  "Expired! Auth code or refresh token needs to be renewed."
635
660
  );
636
661
  // alert("Redirecting to auth server to obtain a new auth grant code.");
637
662
  // TODO: return refreshAuthCodeOrRefreshToken();
638
663
  break;
639
664
  default:
665
+ console.error(error);
640
666
  break;
641
667
  }
642
- throw toErrorClass(error);
668
+ if (typeof error === "string") {
669
+ throw toErrorClass(error);
670
+ } else {
671
+ throw error;
672
+ }
643
673
  }
644
674
  }
645
675
  }
@@ -680,13 +710,11 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
680
710
  }
681
711
  throw toErrorClass(error);
682
712
  }
683
- const json = await response.json();
684
- const { access_token, expires_in, refresh_token, scope } = json as {
685
- access_token: string;
686
- expires_in: number;
687
- refresh_token: string;
688
- scope: string;
689
- };
713
+ const json = (await response.json()) as TokenResponse;
714
+ if ("error" in json) {
715
+ throw new Error(json.error);
716
+ }
717
+ const { access_token, expires_in, refresh_token, scope } = json;
690
718
  let scopes: Scope[] = [];
691
719
  LocalState.hasAuthCodeBeenExchangedForAccessToken = true;
692
720
 
@@ -804,18 +832,18 @@ export async function loginOrRefreshIfRequired(): Promise<boolean> {
804
832
 
805
833
  export async function login(props?: LoginProps): Promise<boolean> {
806
834
  const urlToOpen = await getAuthURL(props?.scopes);
807
- await open(urlToOpen);
808
- // TODO: log url only if on system where it's unreliable/unavailable
809
- // console.log(`💁 Opened ${urlToOpen}`);
810
- let server;
811
- let loginTimeoutHandle;
835
+ await openInBrowser(urlToOpen);
836
+ let server: http.Server;
837
+ let loginTimeoutHandle: NodeJS.Timeout;
812
838
  const timerPromise = new Promise<boolean>((resolve) => {
813
839
  loginTimeoutHandle = setTimeout(() => {
814
- console.error("Timed out waiting for authorization code.");
840
+ console.error(
841
+ "Timed out waiting for authorization code, please try again."
842
+ );
815
843
  server.close();
816
844
  clearTimeout(loginTimeoutHandle);
817
845
  resolve(false);
818
- }, 60000); // wait for 30 seconds for the user to authorize
846
+ }, 60000); // wait for 60 seconds for the user to authorize
819
847
  });
820
848
 
821
849
  const loginPromise = new Promise<boolean>((resolve, reject) => {
@@ -846,8 +874,9 @@ export async function login(props?: LoginProps): Promise<boolean> {
846
874
  finish(false);
847
875
  });
848
876
  console.log(
849
- "Error: Consent denied. You must grant consent to Wrangler in order to login. If you don't want to do this consider passing an API token with CF_API_TOKEN variable"
850
- ); // TODO: implement wrangler config lol
877
+ "Error: Consent denied. You must grant consent to Wrangler in order to login.\n" +
878
+ "If you don't want to do this consider passing an API token via the `CLOUDFLARE_API_TOKEN` environment variable"
879
+ );
851
880
 
852
881
  return;
853
882
  } else {
@@ -939,7 +968,7 @@ export async function logout(): Promise<void> {
939
968
  export function listScopes(): void {
940
969
  throwIfNotInitialised();
941
970
  console.log("💁 Available scopes:");
942
- const data = ScopeKeys.map((scope) => ({
971
+ const data = ScopeKeys.map((scope: Scope) => ({
943
972
  Scope: scope,
944
973
  Description: Scopes[scope],
945
974
  }));
@@ -951,13 +980,14 @@ export async function getAccountId() {
951
980
  const apiToken = getAPIToken();
952
981
  if (!apiToken) return;
953
982
 
954
- if (process.env.CF_ACCOUNT_ID) {
955
- return process.env.CF_ACCOUNT_ID;
983
+ const accountIdFromEnv = getCloudflareAccountIdFromEnv();
984
+ if (accountIdFromEnv) {
985
+ return accountIdFromEnv;
956
986
  }
957
987
 
958
988
  let response: Response;
959
989
  try {
960
- response = await fetch(`${CF_API_BASE_URL}/memberships`, {
990
+ response = await fetch(`${getCloudflareApiBaseUrl()}/memberships`, {
961
991
  method: "GET",
962
992
  headers: {
963
993
  Authorization: "Bearer " + apiToken,
@@ -1001,7 +1031,7 @@ type ChooseAccountItem = {
1001
1031
  };
1002
1032
  export function ChooseAccount(props: {
1003
1033
  accounts: ChooseAccountItem[];
1004
- onSelect: (item) => void;
1034
+ onSelect: (item: SelectInputItem<ChooseAccountItem>) => void;
1005
1035
  }) {
1006
1036
  return (
1007
1037
  <>
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * - Run `npx wrangler dev src/index.js` in your terminal to start a development server
5
5
  * - Open a browser tab at http://localhost:8787/ to see your worker in action
6
- * - Run `npx wrangler publish src/index.js --name my-worker` to deploy your worker
6
+ * - Run `npx wrangler publish src/index.js --name my-worker` to publish your worker
7
7
  *
8
8
  * Learn more at https://developers.cloudflare.com/workers/
9
9
  */
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * - Run `wrangler dev src/index.ts` in your terminal to start a development server
5
5
  * - Open a browser tab at http://localhost:8787/ to see your worker in action
6
- * - Run `wrangler publish src/index.ts --name my-worker` to deploy your worker
6
+ * - Run `wrangler publish src/index.ts --name my-worker` to publish your worker
7
7
  *
8
8
  * Learn more at https://developers.cloudflare.com/workers/
9
9
  */