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.
- package/package.json +16 -7
- package/pages/functions/filepath-routing.test.ts +112 -36
- package/pages/functions/filepath-routing.ts +66 -119
- package/pages/functions/routes.ts +8 -17
- package/pages/functions/template-worker.ts +3 -9
- package/src/__tests__/configuration.test.ts +2185 -0
- package/src/__tests__/dev.test.tsx +48 -6
- package/src/__tests__/guess-worker-format.test.ts +66 -0
- package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
- package/src/__tests__/helpers/mock-account-id.ts +6 -6
- package/src/__tests__/helpers/mock-bin.ts +4 -1
- package/src/__tests__/helpers/mock-cfetch.ts +2 -2
- package/src/__tests__/helpers/mock-console.ts +11 -5
- package/src/__tests__/helpers/mock-web-socket.ts +29 -0
- package/src/__tests__/helpers/run-wrangler.ts +5 -3
- package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
- package/src/__tests__/index.test.ts +343 -20
- package/src/__tests__/jest.setup.ts +21 -1
- package/src/__tests__/kv.test.ts +82 -80
- package/src/__tests__/package-manager.test.ts +2 -2
- package/src/__tests__/parse.test.ts +322 -0
- package/src/__tests__/publish.test.ts +1428 -244
- package/src/__tests__/r2.test.ts +198 -0
- package/src/__tests__/secret.test.ts +176 -60
- package/src/__tests__/sentry.test.ts +146 -0
- package/src/__tests__/tail.test.ts +564 -0
- package/src/api/form_data.ts +106 -23
- package/src/api/preview.ts +32 -13
- package/src/api/worker.ts +49 -18
- package/src/bundle.ts +133 -0
- package/src/cfetch/index.ts +8 -6
- package/src/cfetch/internal.ts +49 -17
- package/src/cli.ts +10 -0
- package/src/config/README.md +107 -0
- package/src/config/config.ts +265 -0
- package/src/config/diagnostics.ts +80 -0
- package/src/config/environment.ts +305 -0
- package/src/config/index.ts +52 -0
- package/src/config/validation-helpers.ts +370 -0
- package/src/config/validation.ts +1017 -0
- package/src/dev.tsx +338 -211
- package/src/entry.ts +57 -0
- package/src/environment-variables.ts +33 -0
- package/src/guess-worker-format.ts +68 -0
- package/src/index.tsx +860 -634
- package/src/inspect.ts +16 -3
- package/src/kv.tsx +20 -29
- package/src/module-collection.ts +141 -34
- package/src/open-in-browser.ts +13 -0
- package/src/package-manager.ts +21 -15
- package/src/pages.tsx +9 -10
- package/src/parse.ts +183 -0
- package/src/proxy.ts +11 -5
- package/src/publish.ts +272 -314
- package/src/r2.ts +50 -0
- package/src/reporting.ts +115 -0
- package/src/sites.tsx +16 -15
- package/src/tail/filters.ts +279 -0
- package/src/tail/index.ts +299 -0
- package/src/tail/printing.ts +62 -0
- package/src/user.tsx +74 -44
- package/templates/new-worker.js +1 -1
- package/templates/new-worker.ts +1 -1
- package/templates/tsconfig.json +105 -0
- package/wrangler-dist/cli.js +51308 -36440
- package/wrangler-dist/cli.js.map +3 -3
- package/src/config.ts +0 -641
- 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 {
|
|
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
|
|
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
|
|
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
|
|
305
|
-
|
|
321
|
+
// if the environment variable is available, use that
|
|
322
|
+
const apiTokenFromEnv = getCloudflareAPITokenFromEnv();
|
|
323
|
+
if (apiTokenFromEnv) {
|
|
306
324
|
LocalState.accessToken = {
|
|
307
|
-
value:
|
|
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
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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 (
|
|
630
|
-
const error = err?.error || "There was a network error.";
|
|
655
|
+
} catch (error) {
|
|
631
656
|
switch (error) {
|
|
632
657
|
case "invalid_grant":
|
|
633
|
-
console.
|
|
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
|
-
|
|
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
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
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
|
|
808
|
-
|
|
809
|
-
|
|
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(
|
|
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
|
|
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
|
|
850
|
-
|
|
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
|
-
|
|
955
|
-
|
|
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(`${
|
|
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
|
<>
|
package/templates/new-worker.js
CHANGED
|
@@ -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
|
|
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
|
*/
|
package/templates/new-worker.ts
CHANGED
|
@@ -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
|
|
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
|
*/
|