alchemy 0.46.0 → 0.46.1
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/bin/alchemy.mjs +156 -160
- package/bin/commands/login.ts +2 -5
- package/lib/cloudflare/api.d.ts +9 -5
- package/lib/cloudflare/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +6 -46
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/auth.d.ts +16 -35
- package/lib/cloudflare/auth.d.ts.map +1 -1
- package/lib/cloudflare/auth.js +54 -24
- package/lib/cloudflare/auth.js.map +1 -1
- package/lib/cloudflare/{auth-wrangler.d.ts → oauth.d.ts} +11 -14
- package/lib/cloudflare/oauth.d.ts.map +1 -0
- package/lib/cloudflare/{auth-wrangler.js → oauth.js} +98 -109
- package/lib/cloudflare/oauth.js.map +1 -0
- package/lib/cloudflare/user.d.ts +1 -1
- package/lib/cloudflare/user.d.ts.map +1 -1
- package/lib/cloudflare/user.js +20 -29
- package/lib/cloudflare/user.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +1 -0
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +4 -3
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/util/memoize.d.ts +0 -1
- package/lib/util/memoize.d.ts.map +1 -1
- package/lib/util/memoize.js +0 -13
- package/lib/util/memoize.js.map +1 -1
- package/lib/util/neverthrow.d.ts +7 -0
- package/lib/util/neverthrow.d.ts.map +1 -1
- package/lib/util/neverthrow.js +17 -0
- package/lib/util/neverthrow.js.map +1 -1
- package/package.json +1 -1
- package/src/cloudflare/api.ts +18 -57
- package/src/cloudflare/auth.ts +84 -68
- package/src/cloudflare/{auth-wrangler.ts → oauth.ts} +154 -168
- package/src/cloudflare/user.ts +33 -51
- package/src/cloudflare/worker.ts +9 -4
- package/src/util/memoize.ts +0 -17
- package/src/util/neverthrow.ts +28 -0
- package/lib/cloudflare/auth-wrangler.d.ts.map +0 -1
- package/lib/cloudflare/auth-wrangler.js.map +0 -1
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import kleur from "kleur";
|
|
2
|
-
import { err, ok, ResultAsync } from "neverthrow";
|
|
2
|
+
import { err, ok, okAsync, ResultAsync } from "neverthrow";
|
|
3
|
+
import assert from "node:assert";
|
|
3
4
|
import crypto from "node:crypto";
|
|
4
5
|
import fs from "node:fs/promises";
|
|
5
6
|
import os from "node:os";
|
|
6
7
|
import path from "node:path";
|
|
7
8
|
import open from "open";
|
|
8
9
|
import { HTTPServer } from "../util/http-server.ts";
|
|
9
|
-
import { memoize
|
|
10
|
-
import {
|
|
10
|
+
import { memoize } from "../util/memoize.ts";
|
|
11
|
+
import {
|
|
12
|
+
detached,
|
|
13
|
+
ensure,
|
|
14
|
+
fetchJSON,
|
|
15
|
+
HTTPError,
|
|
16
|
+
singleFlight,
|
|
17
|
+
} from "../util/neverthrow.ts";
|
|
11
18
|
import { createXdgAppPaths } from "../util/xdg-paths.ts";
|
|
12
19
|
|
|
13
20
|
const CLIENT_ID = "54d11594-84e4-41aa-b438-e81b8fa78ee7";
|
|
14
21
|
const REDIRECT_URI = "http://localhost:8976/oauth/callback";
|
|
15
|
-
const ALCHEMY_BASE_URL = "https://alchemy.run";
|
|
16
22
|
export const DEFAULT_SCOPES = [
|
|
17
23
|
"account:read",
|
|
18
24
|
"user:read",
|
|
@@ -32,6 +38,7 @@ export const DEFAULT_SCOPES = [
|
|
|
32
38
|
"containers:write",
|
|
33
39
|
"cloudchamber:write",
|
|
34
40
|
];
|
|
41
|
+
const ALCHEMY_BASE_URL = "https://alchemy.run";
|
|
35
42
|
|
|
36
43
|
interface WranglerConfig {
|
|
37
44
|
oauth_token: string;
|
|
@@ -40,6 +47,28 @@ interface WranglerConfig {
|
|
|
40
47
|
scopes: string[];
|
|
41
48
|
}
|
|
42
49
|
|
|
50
|
+
let cached: WranglerConfig | undefined;
|
|
51
|
+
|
|
52
|
+
export const getRefreshedWranglerConfig = singleFlight(() =>
|
|
53
|
+
readWranglerConfig()
|
|
54
|
+
.andThen((config) => {
|
|
55
|
+
if (config.expiration_time.getTime() > Date.now() + 10 * 1000) {
|
|
56
|
+
return ok(config);
|
|
57
|
+
}
|
|
58
|
+
return fetchToken({
|
|
59
|
+
grant_type: "refresh_token",
|
|
60
|
+
refresh_token: config.refresh_token,
|
|
61
|
+
client_id: CLIENT_ID,
|
|
62
|
+
});
|
|
63
|
+
})
|
|
64
|
+
.orElse((error) => {
|
|
65
|
+
if (isInteractive()) {
|
|
66
|
+
return wranglerLogin();
|
|
67
|
+
}
|
|
68
|
+
return err(error);
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
|
|
43
72
|
const getWranglerConfigPath = memoize(async () => {
|
|
44
73
|
const xdgConfigDir = createXdgAppPaths(".wrangler").config();
|
|
45
74
|
const legacyConfigDir = path.join(os.homedir(), ".wrangler");
|
|
@@ -53,35 +82,31 @@ const getWranglerConfigPath = memoize(async () => {
|
|
|
53
82
|
});
|
|
54
83
|
|
|
55
84
|
const readWranglerConfig = () => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
typeof config.refresh_token !== "string" ||
|
|
70
|
-
!config.expiration_time ||
|
|
71
|
-
!Array.isArray(config.scopes)
|
|
72
|
-
) {
|
|
73
|
-
return err(new Error("Invalid Wrangler config."));
|
|
74
|
-
}
|
|
75
|
-
return ok({
|
|
85
|
+
if (cached) {
|
|
86
|
+
return okAsync(cached);
|
|
87
|
+
}
|
|
88
|
+
return ResultAsync.fromPromise(
|
|
89
|
+
getWranglerConfigPath().then(async (path): Promise<WranglerConfig> => {
|
|
90
|
+
const text = await fs.readFile(path, "utf-8");
|
|
91
|
+
const toml = await import("@iarna/toml");
|
|
92
|
+
const config = toml.parse(text.replace(/\r\n/g, "\n"));
|
|
93
|
+
assert(typeof config.oauth_token === "string");
|
|
94
|
+
assert(typeof config.refresh_token === "string");
|
|
95
|
+
assert(config.expiration_time instanceof Date);
|
|
96
|
+
assert(Array.isArray(config.scopes));
|
|
97
|
+
return {
|
|
76
98
|
oauth_token: config.oauth_token,
|
|
77
99
|
refresh_token: config.refresh_token,
|
|
78
|
-
expiration_time: new Date(config.expiration_time
|
|
79
|
-
scopes:
|
|
80
|
-
}
|
|
81
|
-
})
|
|
100
|
+
expiration_time: new Date(config.expiration_time),
|
|
101
|
+
scopes: config.scopes as string[],
|
|
102
|
+
};
|
|
103
|
+
}),
|
|
104
|
+
() => new Error("Cloudflare credentials are missing or invalid."),
|
|
105
|
+
);
|
|
82
106
|
};
|
|
83
107
|
|
|
84
108
|
const writeWranglerConfig = (config: WranglerConfig) => {
|
|
109
|
+
cached = config;
|
|
85
110
|
return ResultAsync.fromSafePromise(getWranglerConfigPath()).map(
|
|
86
111
|
async (configPath) => {
|
|
87
112
|
const toml = await import("@iarna/toml");
|
|
@@ -98,140 +123,6 @@ const writeWranglerConfig = (config: WranglerConfig) => {
|
|
|
98
123
|
);
|
|
99
124
|
};
|
|
100
125
|
|
|
101
|
-
const fetchToken = (
|
|
102
|
-
body:
|
|
103
|
-
| {
|
|
104
|
-
grant_type: "refresh_token";
|
|
105
|
-
refresh_token: string;
|
|
106
|
-
client_id: string;
|
|
107
|
-
}
|
|
108
|
-
| {
|
|
109
|
-
grant_type: "authorization_code";
|
|
110
|
-
code: string;
|
|
111
|
-
code_verifier: string;
|
|
112
|
-
client_id: string;
|
|
113
|
-
redirect_uri: string;
|
|
114
|
-
},
|
|
115
|
-
) => {
|
|
116
|
-
return ResultAsync.fromPromise(
|
|
117
|
-
fetch("https://dash.cloudflare.com/oauth2/token", {
|
|
118
|
-
method: "POST",
|
|
119
|
-
headers: {
|
|
120
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
121
|
-
},
|
|
122
|
-
body: new URLSearchParams(body),
|
|
123
|
-
}),
|
|
124
|
-
() =>
|
|
125
|
-
new OAuthError({
|
|
126
|
-
error: "fetch_error",
|
|
127
|
-
error_description: "Failed to fetch OAuth token.",
|
|
128
|
-
status_code: 0,
|
|
129
|
-
}),
|
|
130
|
-
)
|
|
131
|
-
.andThen((res) =>
|
|
132
|
-
ResultAsync.fromPromise(
|
|
133
|
-
res.json(),
|
|
134
|
-
() =>
|
|
135
|
-
new OAuthError({
|
|
136
|
-
error: "invalid_response",
|
|
137
|
-
error_description: "The server returned an invalid response.",
|
|
138
|
-
status_code: res.status,
|
|
139
|
-
}),
|
|
140
|
-
).andThen((json) =>
|
|
141
|
-
res.ok ? ok(json as OAuthTokens) : err(new OAuthError(json as any)),
|
|
142
|
-
),
|
|
143
|
-
)
|
|
144
|
-
.map(
|
|
145
|
-
(tokens): WranglerConfig => ({
|
|
146
|
-
oauth_token: tokens.access_token,
|
|
147
|
-
refresh_token: tokens.refresh_token,
|
|
148
|
-
expiration_time: new Date(Date.now() + tokens.expires_in * 1000),
|
|
149
|
-
scopes: tokens.scope.split(" "),
|
|
150
|
-
}),
|
|
151
|
-
)
|
|
152
|
-
.andTee((config) => writeWranglerConfig(config));
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
interface OAuthTokens {
|
|
156
|
-
access_token: string;
|
|
157
|
-
refresh_token: string;
|
|
158
|
-
expires_in: number;
|
|
159
|
-
scope: string;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export const getRefreshedWranglerConfig = singleFlight(
|
|
163
|
-
memoizeSync(
|
|
164
|
-
() => {
|
|
165
|
-
return readWranglerConfig()
|
|
166
|
-
.andThen((config) => {
|
|
167
|
-
if (config.expiration_time.getTime() > Date.now() + 10 * 1000) {
|
|
168
|
-
return ok(config);
|
|
169
|
-
}
|
|
170
|
-
return fetchToken({
|
|
171
|
-
grant_type: "refresh_token",
|
|
172
|
-
refresh_token: config.refresh_token,
|
|
173
|
-
client_id: CLIENT_ID,
|
|
174
|
-
});
|
|
175
|
-
})
|
|
176
|
-
.orElse((error) => {
|
|
177
|
-
if (isInteractive()) {
|
|
178
|
-
return wranglerLogin();
|
|
179
|
-
}
|
|
180
|
-
if (error instanceof OAuthError) {
|
|
181
|
-
return err(error);
|
|
182
|
-
}
|
|
183
|
-
return ok(null);
|
|
184
|
-
});
|
|
185
|
-
},
|
|
186
|
-
() => Math.floor(Date.now() / 5000).toString(),
|
|
187
|
-
),
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
const isInteractive = () => {
|
|
191
|
-
return process.stdin.isTTY && process.stdout.isTTY && !process.env.CI;
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
const generateAuthorizationURL = (scopes: string[]) => {
|
|
195
|
-
const state = crypto.randomBytes(32).toString("base64url");
|
|
196
|
-
const verifier = crypto.randomBytes(96).toString("base64url");
|
|
197
|
-
const challenge = crypto
|
|
198
|
-
.createHash("sha256")
|
|
199
|
-
.update(verifier)
|
|
200
|
-
.digest("base64url");
|
|
201
|
-
const url = new URL("https://dash.cloudflare.com/oauth2/auth");
|
|
202
|
-
url.search = new URLSearchParams({
|
|
203
|
-
response_type: "code",
|
|
204
|
-
client_id: CLIENT_ID,
|
|
205
|
-
redirect_uri: REDIRECT_URI,
|
|
206
|
-
scope: [...scopes, "offline_access"].join(" "),
|
|
207
|
-
state,
|
|
208
|
-
code_challenge: challenge,
|
|
209
|
-
code_challenge_method: "S256",
|
|
210
|
-
}).toString();
|
|
211
|
-
return { url: url.toString(), state, verifier };
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
class OAuthError extends Error {
|
|
215
|
-
readonly error: string;
|
|
216
|
-
readonly error_verbose?: string;
|
|
217
|
-
readonly error_hint?: string;
|
|
218
|
-
readonly status_code: number;
|
|
219
|
-
constructor(params: {
|
|
220
|
-
error: string;
|
|
221
|
-
error_verbose?: string;
|
|
222
|
-
error_description: string;
|
|
223
|
-
error_hint?: string;
|
|
224
|
-
status_code: number;
|
|
225
|
-
}) {
|
|
226
|
-
console.dir(params, { depth: null });
|
|
227
|
-
super(params.error_description);
|
|
228
|
-
this.error = params.error;
|
|
229
|
-
this.error_verbose = params.error_verbose;
|
|
230
|
-
this.error_hint = params.error_hint;
|
|
231
|
-
this.status_code = params.status_code;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
126
|
export const wranglerLogin = (
|
|
236
127
|
scopes: string[] = DEFAULT_SCOPES,
|
|
237
128
|
log: (message: string) => void = console.log, // using console.log instead of logger.log to avoid bundling hell with CLI
|
|
@@ -249,6 +140,9 @@ export const wranglerLogin = (
|
|
|
249
140
|
);
|
|
250
141
|
open(challenge.url);
|
|
251
142
|
const { result, ok, err } = detached<WranglerConfig, OAuthError>();
|
|
143
|
+
const renderResponse = async (type: "success" | "error") => {
|
|
144
|
+
return Response.redirect(`${ALCHEMY_BASE_URL}/auth/${type}`);
|
|
145
|
+
};
|
|
252
146
|
const server = new HTTPServer({
|
|
253
147
|
port: 8976,
|
|
254
148
|
fetch: async (request) => {
|
|
@@ -268,7 +162,7 @@ export const wranglerLogin = (
|
|
|
268
162
|
status_code: 400,
|
|
269
163
|
}),
|
|
270
164
|
);
|
|
271
|
-
return
|
|
165
|
+
return renderResponse("error");
|
|
272
166
|
}
|
|
273
167
|
if (!code || !state || state !== challenge.state) {
|
|
274
168
|
err(
|
|
@@ -278,7 +172,7 @@ export const wranglerLogin = (
|
|
|
278
172
|
status_code: 400,
|
|
279
173
|
}),
|
|
280
174
|
);
|
|
281
|
-
return
|
|
175
|
+
return renderResponse("error");
|
|
282
176
|
}
|
|
283
177
|
const tokens = await fetchToken({
|
|
284
178
|
grant_type: "authorization_code",
|
|
@@ -289,10 +183,10 @@ export const wranglerLogin = (
|
|
|
289
183
|
});
|
|
290
184
|
if (tokens.isErr()) {
|
|
291
185
|
err(tokens.error);
|
|
292
|
-
return
|
|
186
|
+
return renderResponse("error");
|
|
293
187
|
}
|
|
294
188
|
ok(tokens.value);
|
|
295
|
-
return
|
|
189
|
+
return renderResponse("success");
|
|
296
190
|
},
|
|
297
191
|
});
|
|
298
192
|
const timeout = setTimeout(
|
|
@@ -307,8 +201,100 @@ export const wranglerLogin = (
|
|
|
307
201
|
},
|
|
308
202
|
1000 * 60 * 5,
|
|
309
203
|
);
|
|
310
|
-
return ensure(result,
|
|
204
|
+
return ensure(result, () => {
|
|
311
205
|
clearTimeout(timeout);
|
|
312
|
-
|
|
206
|
+
void server.stop();
|
|
313
207
|
});
|
|
314
208
|
};
|
|
209
|
+
|
|
210
|
+
const generateAuthorizationURL = (scopes: string[]) => {
|
|
211
|
+
const state = crypto.randomBytes(32).toString("base64url");
|
|
212
|
+
const verifier = crypto.randomBytes(96).toString("base64url");
|
|
213
|
+
const challenge = crypto
|
|
214
|
+
.createHash("sha256")
|
|
215
|
+
.update(verifier)
|
|
216
|
+
.digest("base64url");
|
|
217
|
+
const url = new URL("https://dash.cloudflare.com/oauth2/auth");
|
|
218
|
+
url.search = new URLSearchParams({
|
|
219
|
+
response_type: "code",
|
|
220
|
+
client_id: CLIENT_ID,
|
|
221
|
+
redirect_uri: "http://localhost:8976/oauth/callback",
|
|
222
|
+
scope: [...scopes, "offline_access"].join(" "),
|
|
223
|
+
state,
|
|
224
|
+
code_challenge: challenge,
|
|
225
|
+
code_challenge_method: "S256",
|
|
226
|
+
}).toString();
|
|
227
|
+
return { url: url.toString(), state, verifier };
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
interface OAuthTokens {
|
|
231
|
+
access_token: string;
|
|
232
|
+
refresh_token: string;
|
|
233
|
+
expires_in: number;
|
|
234
|
+
scope: string;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
interface OAuthErrorParams {
|
|
238
|
+
error: string;
|
|
239
|
+
error_description: string;
|
|
240
|
+
error_verbose?: string;
|
|
241
|
+
error_hint?: string;
|
|
242
|
+
status_code: number;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
class OAuthError extends Error {
|
|
246
|
+
constructor(params: OAuthErrorParams) {
|
|
247
|
+
super(
|
|
248
|
+
`Cloudflare authentication failed with error ${params.error} (${params.status_code}): ${params.error_description}`,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const fetchToken = (
|
|
254
|
+
body:
|
|
255
|
+
| {
|
|
256
|
+
grant_type: "refresh_token";
|
|
257
|
+
refresh_token: string;
|
|
258
|
+
client_id: string;
|
|
259
|
+
}
|
|
260
|
+
| {
|
|
261
|
+
grant_type: "authorization_code";
|
|
262
|
+
code: string;
|
|
263
|
+
code_verifier: string;
|
|
264
|
+
client_id: string;
|
|
265
|
+
redirect_uri: string;
|
|
266
|
+
},
|
|
267
|
+
) => {
|
|
268
|
+
return fetchJSON<OAuthTokens, OAuthErrorParams>(
|
|
269
|
+
"https://dash.cloudflare.com/oauth2/token",
|
|
270
|
+
{
|
|
271
|
+
method: "POST",
|
|
272
|
+
headers: {
|
|
273
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
274
|
+
},
|
|
275
|
+
body: new URLSearchParams(body),
|
|
276
|
+
},
|
|
277
|
+
)
|
|
278
|
+
.map(
|
|
279
|
+
(tokens): WranglerConfig => ({
|
|
280
|
+
oauth_token: tokens.access_token,
|
|
281
|
+
refresh_token: tokens.refresh_token,
|
|
282
|
+
expiration_time: new Date(Date.now() + tokens.expires_in * 1000),
|
|
283
|
+
scopes: tokens.scope.split(" "),
|
|
284
|
+
}),
|
|
285
|
+
)
|
|
286
|
+
.mapErr((error) =>
|
|
287
|
+
error instanceof HTTPError
|
|
288
|
+
? new OAuthError({
|
|
289
|
+
error: "http_error",
|
|
290
|
+
error_description: error.message,
|
|
291
|
+
status_code: error.status,
|
|
292
|
+
})
|
|
293
|
+
: new OAuthError(error),
|
|
294
|
+
)
|
|
295
|
+
.andTee((config) => writeWranglerConfig(config));
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const isInteractive = () => {
|
|
299
|
+
return process.stdin.isTTY && process.stdout.isTTY && !process.env.CI;
|
|
300
|
+
};
|
package/src/cloudflare/user.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Secret } from "../secret.ts";
|
|
2
2
|
import { logger } from "../util/logger.ts";
|
|
3
3
|
import { memoize } from "../util/memoize.ts";
|
|
4
|
-
import {
|
|
4
|
+
import { extractCloudflareResult } from "./api-response.ts";
|
|
5
5
|
import {
|
|
6
6
|
getCloudflareAuthHeaders,
|
|
7
7
|
type CloudflareAuthOptions,
|
|
@@ -56,66 +56,48 @@ export interface CloudflareOrganization {
|
|
|
56
56
|
roles: string[];
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export const
|
|
60
|
-
async (options: CloudflareAuthOptions): Promise<
|
|
59
|
+
export const getCloudflareAccountId = memoize(
|
|
60
|
+
async (options: CloudflareAuthOptions): Promise<string> => {
|
|
61
61
|
const headers = await getCloudflareAuthHeaders(options);
|
|
62
|
-
const accounts = await
|
|
63
|
-
"
|
|
64
|
-
{
|
|
62
|
+
const accounts = await extractCloudflareResult<CloudflareAccount[]>(
|
|
63
|
+
"get accounts for authorized user",
|
|
64
|
+
fetch("https://api.cloudflare.com/client/v4/accounts", {
|
|
65
65
|
headers,
|
|
66
|
-
},
|
|
66
|
+
}),
|
|
67
67
|
);
|
|
68
|
-
if (accounts
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
if (!accounts[0]) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
"No accounts found for authorized user. Please make sure you're authenticated (see: https://alchemy.run/guides/cloudflare/) or explicitly set the Cloudflare Account ID (see: https://alchemy.run/guides/cloudflare/#account-id)",
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
if (accounts.length > 1) {
|
|
74
|
+
logger.warnOnce(
|
|
75
|
+
[
|
|
76
|
+
"Multiple Cloudflare accounts found for authorized user:",
|
|
77
|
+
accounts.map((a, i) => `${i + 1}: ${a.name} (${a.id})`).join("\n"),
|
|
78
|
+
"The first account will be used by default. To use a different account, explicitly set the Cloudflare Account ID (see: https://alchemy.run/guides/cloudflare/#account-id)",
|
|
79
|
+
].join("\n"),
|
|
74
80
|
);
|
|
75
81
|
}
|
|
82
|
+
return accounts[0].id;
|
|
76
83
|
},
|
|
77
84
|
);
|
|
78
85
|
|
|
79
86
|
export const getUserEmailFromApiKey = memoize(
|
|
80
87
|
async (apiKey: string): Promise<string> => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
const result = await extractCloudflareResult<{
|
|
89
|
+
id: string;
|
|
90
|
+
name: string;
|
|
91
|
+
email: string;
|
|
92
|
+
}>(
|
|
93
|
+
"automatically discover email for API Key authentication",
|
|
94
|
+
fetch("https://api.cloudflare.com/client/v4/user", {
|
|
95
|
+
headers: {
|
|
96
|
+
"Content-Type": "application/json",
|
|
97
|
+
"X-Auth-Key": apiKey,
|
|
90
98
|
},
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
throw new Error(
|
|
95
|
-
`Failed to get user information: ${response.status} ${response.statusText}`,
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const data = (await response.json()) as {
|
|
100
|
-
success: boolean;
|
|
101
|
-
result: {
|
|
102
|
-
id: string;
|
|
103
|
-
email: string;
|
|
104
|
-
name: string;
|
|
105
|
-
[key: string]: any;
|
|
106
|
-
};
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
if (!data.success || !data.result || !data.result.email) {
|
|
110
|
-
throw new Error("Cloudflare API did not return valid user information");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return data.result.email;
|
|
114
|
-
} catch (error) {
|
|
115
|
-
logger.error("Error retrieving email from Cloudflare API:", error);
|
|
116
|
-
throw new Error(
|
|
117
|
-
"Failed to automatically discover email for API Key authentication",
|
|
118
|
-
);
|
|
119
|
-
}
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
return result.email;
|
|
120
102
|
},
|
|
121
103
|
);
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { CloudflareApiError, handleApiError } from "./api-error.ts";
|
|
|
28
28
|
import {
|
|
29
29
|
type CloudflareApi,
|
|
30
30
|
type CloudflareApiOptions,
|
|
31
|
+
type InternalCloudflareApiOptions,
|
|
31
32
|
createCloudflareApi,
|
|
32
33
|
} from "./api.ts";
|
|
33
34
|
import type { Assets } from "./assets.ts";
|
|
@@ -88,6 +89,10 @@ import { WorkerSubdomain, disableWorkerSubdomain } from "./worker-subdomain.ts";
|
|
|
88
89
|
import { createTail } from "./worker-tail.ts";
|
|
89
90
|
import { Workflow, isWorkflow, upsertWorkflow } from "./workflow.ts";
|
|
90
91
|
|
|
92
|
+
// Previous versions of `Worker` used the `Bundle` resource.
|
|
93
|
+
// This import is here to avoid errors when destroying the `Bundle` resource.
|
|
94
|
+
import "../esbuild/bundle.ts";
|
|
95
|
+
|
|
91
96
|
/**
|
|
92
97
|
* Configuration options for static assets
|
|
93
98
|
*/
|
|
@@ -1487,12 +1492,12 @@ const normalizeExportBindings = (
|
|
|
1487
1492
|
);
|
|
1488
1493
|
};
|
|
1489
1494
|
|
|
1490
|
-
const normalizeApiOptions = (
|
|
1495
|
+
const normalizeApiOptions = (
|
|
1496
|
+
api: CloudflareApi,
|
|
1497
|
+
): InternalCloudflareApiOptions => ({
|
|
1491
1498
|
accountId: api.accountId,
|
|
1492
|
-
apiKey: api.apiKey,
|
|
1493
|
-
apiToken: api.apiToken,
|
|
1494
|
-
email: api.email,
|
|
1495
1499
|
baseUrl: api.baseUrl,
|
|
1500
|
+
...api.authOptions,
|
|
1496
1501
|
});
|
|
1497
1502
|
|
|
1498
1503
|
async function provisionContainers(
|
package/src/util/memoize.ts
CHANGED
|
@@ -24,23 +24,6 @@ export function memoize<F extends (...args: any[]) => Promise<any>>(
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function memoizeSync<F extends (...args: any[]) => any>(
|
|
28
|
-
fn: F,
|
|
29
|
-
keyFn: (...args: Parameters<F>) => string = defaultKeyFn,
|
|
30
|
-
) {
|
|
31
|
-
const cache = new Map<string, ReturnType<F>>();
|
|
32
|
-
return (...args: Parameters<F>): ReturnType<F> => {
|
|
33
|
-
const key = keyFn(...args);
|
|
34
|
-
const cached = cache.get(key);
|
|
35
|
-
if (cached) {
|
|
36
|
-
return cached;
|
|
37
|
-
}
|
|
38
|
-
const result = fn(...args);
|
|
39
|
-
cache.set(key, result);
|
|
40
|
-
return result;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
27
|
export const defaultKeyFn = (...args: any[]) => {
|
|
45
28
|
return createHash("sha256").update(JSON.stringify(args)).digest("hex");
|
|
46
29
|
};
|
package/src/util/neverthrow.ts
CHANGED
|
@@ -34,3 +34,31 @@ export const singleFlight = <Args extends any[], Value, Error>(
|
|
|
34
34
|
});
|
|
35
35
|
};
|
|
36
36
|
};
|
|
37
|
+
|
|
38
|
+
export class HTTPError extends Error {
|
|
39
|
+
constructor(
|
|
40
|
+
message: string,
|
|
41
|
+
public readonly status: number,
|
|
42
|
+
) {
|
|
43
|
+
super(message);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// TODO: merge this with safe-fetch?
|
|
48
|
+
export const fetchResult = (url: string, init: RequestInit = {}) => {
|
|
49
|
+
return ResultAsync.fromPromise(
|
|
50
|
+
fetch(url, init),
|
|
51
|
+
() => new HTTPError(`Failed to ${init.method ?? "GET"} ${url}`, 0),
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const fetchJSON = <T, E>(url: string, init: RequestInit = {}) => {
|
|
56
|
+
return fetchResult(url, init).andThen(decodeJSON<T, E>);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const decodeJSON = <T, E>(response: Response) => {
|
|
60
|
+
return ResultAsync.fromPromise(
|
|
61
|
+
response.json(),
|
|
62
|
+
() => new HTTPError("Failed to decode JSON", response.status),
|
|
63
|
+
).andThen((json) => (response.ok ? ok(json as T) : err(json as E)));
|
|
64
|
+
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-wrangler.d.ts","sourceRoot":"","sources":["../../src/cloudflare/auth-wrangler.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,WAAW,EAAE,MAAM,YAAY,CAAC;AAclD,eAAO,MAAM,cAAc,UAkB1B,CAAC;AAEF,UAAU,cAAc;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAyHD,eAAO,MAAM,0BAA0B,sDA0BtC,CAAC;AA0BF,cAAM,UAAW,SAAQ,KAAK;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBACjB,MAAM,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB;CAQF;AAED,eAAO,MAAM,aAAa,GACxB,SAAQ,MAAM,EAAmB,EACjC,MAAK,CAAC,OAAO,EAAE,MAAM,KAAK,IAAkB,4CA6E7C,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-wrangler.js","sourceRoot":"","sources":["../../src/cloudflare/auth-wrangler.ts"],"names":[],"mappings":"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"}
|