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
package/bin/alchemy.mjs
CHANGED
|
@@ -112,7 +112,7 @@ var require_utils_BhNVZA_c = __commonJS({
|
|
|
112
112
|
function identity3(it) {
|
|
113
113
|
return it;
|
|
114
114
|
}
|
|
115
|
-
function
|
|
115
|
+
function assert2(condition, msg = "no additional info") {
|
|
116
116
|
if (!condition) throw new Error(`AssertionError: ${msg}`);
|
|
117
117
|
}
|
|
118
118
|
function sleep(ms = 0) {
|
|
@@ -155,7 +155,7 @@ var require_utils_BhNVZA_c = __commonJS({
|
|
|
155
155
|
Object.defineProperty(exports2, "assert", {
|
|
156
156
|
enumerable: true,
|
|
157
157
|
get: function() {
|
|
158
|
-
return
|
|
158
|
+
return assert2;
|
|
159
159
|
}
|
|
160
160
|
});
|
|
161
161
|
Object.defineProperty(exports2, "identity", {
|
|
@@ -5208,11 +5208,11 @@ var require_util = __commonJS({
|
|
|
5208
5208
|
exports2.assertNotEqual = assertNotEqual;
|
|
5209
5209
|
exports2.assertIs = assertIs;
|
|
5210
5210
|
exports2.assertNever = assertNever;
|
|
5211
|
-
exports2.assert =
|
|
5211
|
+
exports2.assert = assert2;
|
|
5212
5212
|
exports2.getEnumValues = getEnumValues;
|
|
5213
5213
|
exports2.joinValues = joinValues;
|
|
5214
5214
|
exports2.jsonStringifyReplacer = jsonStringifyReplacer;
|
|
5215
|
-
exports2.cached =
|
|
5215
|
+
exports2.cached = cached2;
|
|
5216
5216
|
exports2.nullish = nullish;
|
|
5217
5217
|
exports2.cleanRegex = cleanRegex;
|
|
5218
5218
|
exports2.floatSafeRemainder = floatSafeRemainder2;
|
|
@@ -5256,7 +5256,7 @@ var require_util = __commonJS({
|
|
|
5256
5256
|
function assertNever(_x) {
|
|
5257
5257
|
throw new Error();
|
|
5258
5258
|
}
|
|
5259
|
-
function
|
|
5259
|
+
function assert2(_3) {
|
|
5260
5260
|
}
|
|
5261
5261
|
function getEnumValues(entries) {
|
|
5262
5262
|
const numericValues = Object.values(entries).filter((v2) => typeof v2 === "number");
|
|
@@ -5271,7 +5271,7 @@ var require_util = __commonJS({
|
|
|
5271
5271
|
return value.toString();
|
|
5272
5272
|
return value;
|
|
5273
5273
|
}
|
|
5274
|
-
function
|
|
5274
|
+
function cached2(getter) {
|
|
5275
5275
|
const set = false;
|
|
5276
5276
|
return {
|
|
5277
5277
|
get value() {
|
|
@@ -5360,7 +5360,7 @@ var require_util = __commonJS({
|
|
|
5360
5360
|
function isObject2(data) {
|
|
5361
5361
|
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
5362
5362
|
}
|
|
5363
|
-
exports2.allowsEval =
|
|
5363
|
+
exports2.allowsEval = cached2(() => {
|
|
5364
5364
|
if (typeof navigator !== "undefined" && navigator?.userAgent?.includes("Cloudflare")) {
|
|
5365
5365
|
return false;
|
|
5366
5366
|
}
|
|
@@ -20960,10 +20960,10 @@ var require_zod = __commonJS({
|
|
|
20960
20960
|
};
|
|
20961
20961
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20962
20962
|
exports2.z = void 0;
|
|
20963
|
-
var
|
|
20964
|
-
exports2.z =
|
|
20963
|
+
var z3 = __importStar(require_external());
|
|
20964
|
+
exports2.z = z3;
|
|
20965
20965
|
__exportStar(require_external(), exports2);
|
|
20966
|
-
exports2.default =
|
|
20966
|
+
exports2.default = z3;
|
|
20967
20967
|
}
|
|
20968
20968
|
});
|
|
20969
20969
|
|
|
@@ -25361,10 +25361,10 @@ var require_classic = __commonJS({
|
|
|
25361
25361
|
};
|
|
25362
25362
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
25363
25363
|
exports2.z = void 0;
|
|
25364
|
-
var
|
|
25365
|
-
exports2.z =
|
|
25364
|
+
var z3 = __importStar(require_external2());
|
|
25365
|
+
exports2.z = z3;
|
|
25366
25366
|
__exportStar(require_external2(), exports2);
|
|
25367
|
-
exports2.default =
|
|
25367
|
+
exports2.default = z3;
|
|
25368
25368
|
}
|
|
25369
25369
|
});
|
|
25370
25370
|
|
|
@@ -36648,7 +36648,7 @@ var require_toml = __commonJS({
|
|
|
36648
36648
|
});
|
|
36649
36649
|
|
|
36650
36650
|
// bin/alchemy.ts
|
|
36651
|
-
var
|
|
36651
|
+
var import_trpc_cli3 = __toESM(require_dist2(), 1);
|
|
36652
36652
|
|
|
36653
36653
|
// ../node_modules/@clack/prompts/dist/index.mjs
|
|
36654
36654
|
import { stripVTControlCharacters as S2 } from "node:util";
|
|
@@ -37255,8 +37255,8 @@ var Y2 = ({ indicator: t2 = "dots" } = {}) => {
|
|
|
37255
37255
|
if (i2) process.stdout.write(`${I2} ${l2}...`);
|
|
37256
37256
|
else if (t2 === "timer") process.stdout.write(`${I2} ${l2} ${O2(g2)}`);
|
|
37257
37257
|
else {
|
|
37258
|
-
const
|
|
37259
|
-
process.stdout.write(`${I2} ${l2}${
|
|
37258
|
+
const z3 = ".".repeat(Math.floor(w2)).slice(0, 3);
|
|
37259
|
+
process.stdout.write(`${I2} ${l2}${z3}`);
|
|
37260
37260
|
}
|
|
37261
37261
|
h3 = h3 + 1 < n2.length ? h3 + 1 : 0, w2 = w2 < n2.length ? w2 + 0.125 : 0;
|
|
37262
37262
|
}, r2);
|
|
@@ -49673,6 +49673,7 @@ var dev = t.procedure.meta({
|
|
|
49673
49673
|
|
|
49674
49674
|
// bin/commands/login.ts
|
|
49675
49675
|
var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
49676
|
+
var import_trpc_cli2 = __toESM(require_dist2(), 1);
|
|
49676
49677
|
|
|
49677
49678
|
// ../node_modules/kleur/index.mjs
|
|
49678
49679
|
var FORCE_COLOR;
|
|
@@ -50039,6 +50040,9 @@ var ResultAsync = class _ResultAsync {
|
|
|
50039
50040
|
});
|
|
50040
50041
|
}
|
|
50041
50042
|
};
|
|
50043
|
+
function okAsync(value) {
|
|
50044
|
+
return new ResultAsync(Promise.resolve(new Ok(value)));
|
|
50045
|
+
}
|
|
50042
50046
|
function errAsync(err2) {
|
|
50043
50047
|
return new ResultAsync(Promise.resolve(new Err(err2)));
|
|
50044
50048
|
}
|
|
@@ -50250,7 +50254,8 @@ var Err = class {
|
|
|
50250
50254
|
};
|
|
50251
50255
|
var fromThrowable = Result.fromThrowable;
|
|
50252
50256
|
|
|
50253
|
-
// src/cloudflare/
|
|
50257
|
+
// src/cloudflare/oauth.ts
|
|
50258
|
+
import assert from "node:assert";
|
|
50254
50259
|
import crypto from "node:crypto";
|
|
50255
50260
|
import fs15 from "node:fs/promises";
|
|
50256
50261
|
import os2 from "node:os";
|
|
@@ -50840,9 +50845,9 @@ function memoize(fn, keyFn = defaultKeyFn) {
|
|
|
50840
50845
|
const cache = /* @__PURE__ */ new Map();
|
|
50841
50846
|
return async (...args) => {
|
|
50842
50847
|
const key = keyFn(...args);
|
|
50843
|
-
const
|
|
50844
|
-
if (
|
|
50845
|
-
return await
|
|
50848
|
+
const cached2 = cache.get(key);
|
|
50849
|
+
if (cached2) {
|
|
50850
|
+
return await cached2;
|
|
50846
50851
|
}
|
|
50847
50852
|
const promise = fn(...args).catch((error) => {
|
|
50848
50853
|
cache.delete(key);
|
|
@@ -50852,19 +50857,6 @@ function memoize(fn, keyFn = defaultKeyFn) {
|
|
|
50852
50857
|
return await promise;
|
|
50853
50858
|
};
|
|
50854
50859
|
}
|
|
50855
|
-
function memoizeSync(fn, keyFn = defaultKeyFn) {
|
|
50856
|
-
const cache = /* @__PURE__ */ new Map();
|
|
50857
|
-
return (...args) => {
|
|
50858
|
-
const key = keyFn(...args);
|
|
50859
|
-
const cached = cache.get(key);
|
|
50860
|
-
if (cached) {
|
|
50861
|
-
return cached;
|
|
50862
|
-
}
|
|
50863
|
-
const result = fn(...args);
|
|
50864
|
-
cache.set(key, result);
|
|
50865
|
-
return result;
|
|
50866
|
-
};
|
|
50867
|
-
}
|
|
50868
50860
|
var defaultKeyFn = (...args) => {
|
|
50869
50861
|
return createHash("sha256").update(JSON.stringify(args)).digest("hex");
|
|
50870
50862
|
};
|
|
@@ -50885,9 +50877,9 @@ var singleFlight = (fn) => {
|
|
|
50885
50877
|
const results = /* @__PURE__ */ new Map();
|
|
50886
50878
|
return (...args) => {
|
|
50887
50879
|
const key = JSON.stringify(args ?? []);
|
|
50888
|
-
const
|
|
50889
|
-
if (
|
|
50890
|
-
return
|
|
50880
|
+
const cached2 = results.get(key);
|
|
50881
|
+
if (cached2) {
|
|
50882
|
+
return cached2;
|
|
50891
50883
|
}
|
|
50892
50884
|
const result = fn(...args);
|
|
50893
50885
|
results.set(key, result);
|
|
@@ -50896,6 +50888,27 @@ var singleFlight = (fn) => {
|
|
|
50896
50888
|
});
|
|
50897
50889
|
};
|
|
50898
50890
|
};
|
|
50891
|
+
var HTTPError = class extends Error {
|
|
50892
|
+
constructor(message, status) {
|
|
50893
|
+
super(message);
|
|
50894
|
+
this.status = status;
|
|
50895
|
+
}
|
|
50896
|
+
};
|
|
50897
|
+
var fetchResult = (url, init2 = {}) => {
|
|
50898
|
+
return ResultAsync.fromPromise(
|
|
50899
|
+
fetch(url, init2),
|
|
50900
|
+
() => new HTTPError(`Failed to ${init2.method ?? "GET"} ${url}`, 0)
|
|
50901
|
+
);
|
|
50902
|
+
};
|
|
50903
|
+
var fetchJSON = (url, init2 = {}) => {
|
|
50904
|
+
return fetchResult(url, init2).andThen(decodeJSON);
|
|
50905
|
+
};
|
|
50906
|
+
var decodeJSON = (response) => {
|
|
50907
|
+
return ResultAsync.fromPromise(
|
|
50908
|
+
response.json(),
|
|
50909
|
+
() => new HTTPError("Failed to decode JSON", response.status)
|
|
50910
|
+
).andThen((json) => response.ok ? ok(json) : err(json));
|
|
50911
|
+
};
|
|
50899
50912
|
|
|
50900
50913
|
// src/util/xdg-paths.ts
|
|
50901
50914
|
import { join as join4 } from "node:path";
|
|
@@ -50935,10 +50948,9 @@ function createXdgAppPaths(appName) {
|
|
|
50935
50948
|
};
|
|
50936
50949
|
}
|
|
50937
50950
|
|
|
50938
|
-
// src/cloudflare/
|
|
50951
|
+
// src/cloudflare/oauth.ts
|
|
50939
50952
|
var CLIENT_ID = "54d11594-84e4-41aa-b438-e81b8fa78ee7";
|
|
50940
50953
|
var REDIRECT_URI = "http://localhost:8976/oauth/callback";
|
|
50941
|
-
var ALCHEMY_BASE_URL = "https://alchemy.run";
|
|
50942
50954
|
var DEFAULT_SCOPES = [
|
|
50943
50955
|
"account:read",
|
|
50944
50956
|
"user:read",
|
|
@@ -50958,6 +50970,25 @@ var DEFAULT_SCOPES = [
|
|
|
50958
50970
|
"containers:write",
|
|
50959
50971
|
"cloudchamber:write"
|
|
50960
50972
|
];
|
|
50973
|
+
var ALCHEMY_BASE_URL = "https://alchemy.run";
|
|
50974
|
+
var cached;
|
|
50975
|
+
var getRefreshedWranglerConfig = singleFlight(
|
|
50976
|
+
() => readWranglerConfig().andThen((config) => {
|
|
50977
|
+
if (config.expiration_time.getTime() > Date.now() + 10 * 1e3) {
|
|
50978
|
+
return ok(config);
|
|
50979
|
+
}
|
|
50980
|
+
return fetchToken({
|
|
50981
|
+
grant_type: "refresh_token",
|
|
50982
|
+
refresh_token: config.refresh_token,
|
|
50983
|
+
client_id: CLIENT_ID
|
|
50984
|
+
});
|
|
50985
|
+
}).orElse((error) => {
|
|
50986
|
+
if (isInteractive()) {
|
|
50987
|
+
return wranglerLogin();
|
|
50988
|
+
}
|
|
50989
|
+
return err(error);
|
|
50990
|
+
})
|
|
50991
|
+
);
|
|
50961
50992
|
var getWranglerConfigPath = memoize(async () => {
|
|
50962
50993
|
const xdgConfigDir = createXdgAppPaths(".wrangler").config();
|
|
50963
50994
|
const legacyConfigDir = path13.join(os2.homedir(), ".wrangler");
|
|
@@ -50965,27 +50996,30 @@ var getWranglerConfigPath = memoize(async () => {
|
|
|
50965
50996
|
return path13.join(configDir, "config", "default.toml");
|
|
50966
50997
|
});
|
|
50967
50998
|
var readWranglerConfig = () => {
|
|
50968
|
-
|
|
50969
|
-
(
|
|
50970
|
-
|
|
50971
|
-
|
|
50972
|
-
|
|
50973
|
-
|
|
50974
|
-
() =>
|
|
50975
|
-
|
|
50976
|
-
|
|
50977
|
-
|
|
50978
|
-
|
|
50979
|
-
|
|
50980
|
-
|
|
50981
|
-
|
|
50982
|
-
|
|
50983
|
-
|
|
50984
|
-
|
|
50985
|
-
|
|
50986
|
-
|
|
50999
|
+
if (cached) {
|
|
51000
|
+
return okAsync(cached);
|
|
51001
|
+
}
|
|
51002
|
+
return ResultAsync.fromPromise(
|
|
51003
|
+
getWranglerConfigPath().then(async (path14) => {
|
|
51004
|
+
const text = await fs15.readFile(path14, "utf-8");
|
|
51005
|
+
const toml = await Promise.resolve().then(() => __toESM(require_toml(), 1));
|
|
51006
|
+
const config = toml.parse(text.replace(/\r\n/g, "\n"));
|
|
51007
|
+
assert(typeof config.oauth_token === "string");
|
|
51008
|
+
assert(typeof config.refresh_token === "string");
|
|
51009
|
+
assert(config.expiration_time instanceof Date);
|
|
51010
|
+
assert(Array.isArray(config.scopes));
|
|
51011
|
+
return {
|
|
51012
|
+
oauth_token: config.oauth_token,
|
|
51013
|
+
refresh_token: config.refresh_token,
|
|
51014
|
+
expiration_time: new Date(config.expiration_time),
|
|
51015
|
+
scopes: config.scopes
|
|
51016
|
+
};
|
|
51017
|
+
}),
|
|
51018
|
+
() => new Error("Cloudflare credentials are missing or invalid.")
|
|
51019
|
+
);
|
|
50987
51020
|
};
|
|
50988
51021
|
var writeWranglerConfig = (config) => {
|
|
51022
|
+
cached = config;
|
|
50989
51023
|
return ResultAsync.fromSafePromise(getWranglerConfigPath()).map(
|
|
50990
51024
|
async (configPath) => {
|
|
50991
51025
|
const toml = await Promise.resolve().then(() => __toESM(require_toml(), 1));
|
|
@@ -51001,98 +51035,6 @@ var writeWranglerConfig = (config) => {
|
|
|
51001
51035
|
}
|
|
51002
51036
|
);
|
|
51003
51037
|
};
|
|
51004
|
-
var fetchToken = (body) => {
|
|
51005
|
-
return ResultAsync.fromPromise(
|
|
51006
|
-
fetch("https://dash.cloudflare.com/oauth2/token", {
|
|
51007
|
-
method: "POST",
|
|
51008
|
-
headers: {
|
|
51009
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
51010
|
-
},
|
|
51011
|
-
body: new URLSearchParams(body)
|
|
51012
|
-
}),
|
|
51013
|
-
() => new OAuthError({
|
|
51014
|
-
error: "fetch_error",
|
|
51015
|
-
error_description: "Failed to fetch OAuth token.",
|
|
51016
|
-
status_code: 0
|
|
51017
|
-
})
|
|
51018
|
-
).andThen(
|
|
51019
|
-
(res) => ResultAsync.fromPromise(
|
|
51020
|
-
res.json(),
|
|
51021
|
-
() => new OAuthError({
|
|
51022
|
-
error: "invalid_response",
|
|
51023
|
-
error_description: "The server returned an invalid response.",
|
|
51024
|
-
status_code: res.status
|
|
51025
|
-
})
|
|
51026
|
-
).andThen(
|
|
51027
|
-
(json) => res.ok ? ok(json) : err(new OAuthError(json))
|
|
51028
|
-
)
|
|
51029
|
-
).map(
|
|
51030
|
-
(tokens) => ({
|
|
51031
|
-
oauth_token: tokens.access_token,
|
|
51032
|
-
refresh_token: tokens.refresh_token,
|
|
51033
|
-
expiration_time: new Date(Date.now() + tokens.expires_in * 1e3),
|
|
51034
|
-
scopes: tokens.scope.split(" ")
|
|
51035
|
-
})
|
|
51036
|
-
).andTee((config) => writeWranglerConfig(config));
|
|
51037
|
-
};
|
|
51038
|
-
var getRefreshedWranglerConfig = singleFlight(
|
|
51039
|
-
memoizeSync(
|
|
51040
|
-
() => {
|
|
51041
|
-
return readWranglerConfig().andThen((config) => {
|
|
51042
|
-
if (config.expiration_time.getTime() > Date.now() + 10 * 1e3) {
|
|
51043
|
-
return ok(config);
|
|
51044
|
-
}
|
|
51045
|
-
return fetchToken({
|
|
51046
|
-
grant_type: "refresh_token",
|
|
51047
|
-
refresh_token: config.refresh_token,
|
|
51048
|
-
client_id: CLIENT_ID
|
|
51049
|
-
});
|
|
51050
|
-
}).orElse((error) => {
|
|
51051
|
-
if (isInteractive()) {
|
|
51052
|
-
return wranglerLogin();
|
|
51053
|
-
}
|
|
51054
|
-
if (error instanceof OAuthError) {
|
|
51055
|
-
return err(error);
|
|
51056
|
-
}
|
|
51057
|
-
return ok(null);
|
|
51058
|
-
});
|
|
51059
|
-
},
|
|
51060
|
-
() => Math.floor(Date.now() / 5e3).toString()
|
|
51061
|
-
)
|
|
51062
|
-
);
|
|
51063
|
-
var isInteractive = () => {
|
|
51064
|
-
return process.stdin.isTTY && process.stdout.isTTY && !process.env.CI;
|
|
51065
|
-
};
|
|
51066
|
-
var generateAuthorizationURL = (scopes) => {
|
|
51067
|
-
const state = crypto.randomBytes(32).toString("base64url");
|
|
51068
|
-
const verifier = crypto.randomBytes(96).toString("base64url");
|
|
51069
|
-
const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");
|
|
51070
|
-
const url = new URL("https://dash.cloudflare.com/oauth2/auth");
|
|
51071
|
-
url.search = new URLSearchParams({
|
|
51072
|
-
response_type: "code",
|
|
51073
|
-
client_id: CLIENT_ID,
|
|
51074
|
-
redirect_uri: REDIRECT_URI,
|
|
51075
|
-
scope: [...scopes, "offline_access"].join(" "),
|
|
51076
|
-
state,
|
|
51077
|
-
code_challenge: challenge,
|
|
51078
|
-
code_challenge_method: "S256"
|
|
51079
|
-
}).toString();
|
|
51080
|
-
return { url: url.toString(), state, verifier };
|
|
51081
|
-
};
|
|
51082
|
-
var OAuthError = class extends Error {
|
|
51083
|
-
error;
|
|
51084
|
-
error_verbose;
|
|
51085
|
-
error_hint;
|
|
51086
|
-
status_code;
|
|
51087
|
-
constructor(params) {
|
|
51088
|
-
console.dir(params, { depth: null });
|
|
51089
|
-
super(params.error_description);
|
|
51090
|
-
this.error = params.error;
|
|
51091
|
-
this.error_verbose = params.error_verbose;
|
|
51092
|
-
this.error_hint = params.error_hint;
|
|
51093
|
-
this.status_code = params.status_code;
|
|
51094
|
-
}
|
|
51095
|
-
};
|
|
51096
51038
|
var wranglerLogin = (scopes = DEFAULT_SCOPES, log = console.log) => {
|
|
51097
51039
|
const challenge = generateAuthorizationURL(scopes);
|
|
51098
51040
|
log(
|
|
@@ -51107,6 +51049,9 @@ var wranglerLogin = (scopes = DEFAULT_SCOPES, log = console.log) => {
|
|
|
51107
51049
|
);
|
|
51108
51050
|
open_default(challenge.url);
|
|
51109
51051
|
const { result, ok: ok2, err: err2 } = detached();
|
|
51052
|
+
const renderResponse = async (type) => {
|
|
51053
|
+
return Response.redirect(`${ALCHEMY_BASE_URL}/auth/${type}`);
|
|
51054
|
+
};
|
|
51110
51055
|
const server = new HTTPServer({
|
|
51111
51056
|
port: 8976,
|
|
51112
51057
|
fetch: async (request) => {
|
|
@@ -51126,7 +51071,7 @@ var wranglerLogin = (scopes = DEFAULT_SCOPES, log = console.log) => {
|
|
|
51126
51071
|
status_code: 400
|
|
51127
51072
|
})
|
|
51128
51073
|
);
|
|
51129
|
-
return
|
|
51074
|
+
return renderResponse("error");
|
|
51130
51075
|
}
|
|
51131
51076
|
if (!code || !state || state !== challenge.state) {
|
|
51132
51077
|
err2(
|
|
@@ -51136,7 +51081,7 @@ var wranglerLogin = (scopes = DEFAULT_SCOPES, log = console.log) => {
|
|
|
51136
51081
|
status_code: 400
|
|
51137
51082
|
})
|
|
51138
51083
|
);
|
|
51139
|
-
return
|
|
51084
|
+
return renderResponse("error");
|
|
51140
51085
|
}
|
|
51141
51086
|
const tokens = await fetchToken({
|
|
51142
51087
|
grant_type: "authorization_code",
|
|
@@ -51147,10 +51092,10 @@ var wranglerLogin = (scopes = DEFAULT_SCOPES, log = console.log) => {
|
|
|
51147
51092
|
});
|
|
51148
51093
|
if (tokens.isErr()) {
|
|
51149
51094
|
err2(tokens.error);
|
|
51150
|
-
return
|
|
51095
|
+
return renderResponse("error");
|
|
51151
51096
|
}
|
|
51152
51097
|
ok2(tokens.value);
|
|
51153
|
-
return
|
|
51098
|
+
return renderResponse("success");
|
|
51154
51099
|
}
|
|
51155
51100
|
});
|
|
51156
51101
|
const timeout = setTimeout(
|
|
@@ -51165,20 +51110,71 @@ var wranglerLogin = (scopes = DEFAULT_SCOPES, log = console.log) => {
|
|
|
51165
51110
|
},
|
|
51166
51111
|
1e3 * 60 * 5
|
|
51167
51112
|
);
|
|
51168
|
-
return ensure(result,
|
|
51113
|
+
return ensure(result, () => {
|
|
51169
51114
|
clearTimeout(timeout);
|
|
51170
|
-
|
|
51115
|
+
void server.stop();
|
|
51171
51116
|
});
|
|
51172
51117
|
};
|
|
51118
|
+
var generateAuthorizationURL = (scopes) => {
|
|
51119
|
+
const state = crypto.randomBytes(32).toString("base64url");
|
|
51120
|
+
const verifier = crypto.randomBytes(96).toString("base64url");
|
|
51121
|
+
const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");
|
|
51122
|
+
const url = new URL("https://dash.cloudflare.com/oauth2/auth");
|
|
51123
|
+
url.search = new URLSearchParams({
|
|
51124
|
+
response_type: "code",
|
|
51125
|
+
client_id: CLIENT_ID,
|
|
51126
|
+
redirect_uri: "http://localhost:8976/oauth/callback",
|
|
51127
|
+
scope: [...scopes, "offline_access"].join(" "),
|
|
51128
|
+
state,
|
|
51129
|
+
code_challenge: challenge,
|
|
51130
|
+
code_challenge_method: "S256"
|
|
51131
|
+
}).toString();
|
|
51132
|
+
return { url: url.toString(), state, verifier };
|
|
51133
|
+
};
|
|
51134
|
+
var OAuthError = class extends Error {
|
|
51135
|
+
constructor(params) {
|
|
51136
|
+
super(
|
|
51137
|
+
`Cloudflare authentication failed with error ${params.error} (${params.status_code}): ${params.error_description}`
|
|
51138
|
+
);
|
|
51139
|
+
}
|
|
51140
|
+
};
|
|
51141
|
+
var fetchToken = (body) => {
|
|
51142
|
+
return fetchJSON(
|
|
51143
|
+
"https://dash.cloudflare.com/oauth2/token",
|
|
51144
|
+
{
|
|
51145
|
+
method: "POST",
|
|
51146
|
+
headers: {
|
|
51147
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
51148
|
+
},
|
|
51149
|
+
body: new URLSearchParams(body)
|
|
51150
|
+
}
|
|
51151
|
+
).map(
|
|
51152
|
+
(tokens) => ({
|
|
51153
|
+
oauth_token: tokens.access_token,
|
|
51154
|
+
refresh_token: tokens.refresh_token,
|
|
51155
|
+
expiration_time: new Date(Date.now() + tokens.expires_in * 1e3),
|
|
51156
|
+
scopes: tokens.scope.split(" ")
|
|
51157
|
+
})
|
|
51158
|
+
).mapErr(
|
|
51159
|
+
(error) => error instanceof HTTPError ? new OAuthError({
|
|
51160
|
+
error: "http_error",
|
|
51161
|
+
error_description: error.message,
|
|
51162
|
+
status_code: error.status
|
|
51163
|
+
}) : new OAuthError(error)
|
|
51164
|
+
).andTee((config) => writeWranglerConfig(config));
|
|
51165
|
+
};
|
|
51166
|
+
var isInteractive = () => {
|
|
51167
|
+
return process.stdin.isTTY && process.stdout.isTTY && !process.env.CI;
|
|
51168
|
+
};
|
|
51173
51169
|
|
|
51174
51170
|
// bin/commands/login.ts
|
|
51175
51171
|
var login = t.procedure.meta({
|
|
51176
51172
|
description: "Login to Cloudflare"
|
|
51177
51173
|
}).input(
|
|
51178
|
-
|
|
51179
|
-
|
|
51180
|
-
scopes:
|
|
51181
|
-
defaultScopes:
|
|
51174
|
+
import_trpc_cli2.zod.tuple([
|
|
51175
|
+
import_trpc_cli2.zod.object({
|
|
51176
|
+
scopes: import_trpc_cli2.zod.array(import_trpc_cli2.zod.string()).optional().default([]).describe("Cloudflare OAuth scopes to authorize"),
|
|
51177
|
+
defaultScopes: import_trpc_cli2.zod.boolean().optional().default(true).describe(
|
|
51182
51178
|
"Whether to include the default Wrangler scopes when authenticating"
|
|
51183
51179
|
)
|
|
51184
51180
|
})
|
|
@@ -51241,7 +51237,7 @@ var router = t.router({
|
|
|
51241
51237
|
dev,
|
|
51242
51238
|
run: run2
|
|
51243
51239
|
});
|
|
51244
|
-
var cli = (0,
|
|
51240
|
+
var cli = (0, import_trpc_cli3.createCli)({
|
|
51245
51241
|
router,
|
|
51246
51242
|
name: "alchemy",
|
|
51247
51243
|
version: getPackageVersion(),
|
package/bin/commands/login.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { cancel, intro, log, outro } from "@clack/prompts";
|
|
2
2
|
import pc from "picocolors";
|
|
3
|
-
import z from "
|
|
4
|
-
import {
|
|
5
|
-
DEFAULT_SCOPES,
|
|
6
|
-
wranglerLogin,
|
|
7
|
-
} from "../../src/cloudflare/auth-wrangler.ts";
|
|
3
|
+
import { zod as z } from "trpc-cli";
|
|
4
|
+
import { DEFAULT_SCOPES, wranglerLogin } from "../../src/cloudflare/oauth.ts";
|
|
8
5
|
import { throwWithContext } from "../errors.ts";
|
|
9
6
|
import { t } from "../trpc.ts";
|
|
10
7
|
|
package/lib/cloudflare/api.d.ts
CHANGED
|
@@ -29,22 +29,24 @@ export interface CloudflareApiOptions {
|
|
|
29
29
|
*/
|
|
30
30
|
email?: string;
|
|
31
31
|
}
|
|
32
|
+
/** Used to propagate normalized auth options from a parent resource to `createCloudflareApi` in a child resource */
|
|
33
|
+
export type InternalCloudflareApiOptions = CloudflareAuthOptions & {
|
|
34
|
+
baseUrl?: string;
|
|
35
|
+
accountId: string;
|
|
36
|
+
};
|
|
32
37
|
/**
|
|
33
38
|
* Creates a CloudflareApi instance with automatic account ID discovery if not provided
|
|
34
39
|
*
|
|
35
40
|
* @param options API options
|
|
36
41
|
* @returns Promise resolving to a CloudflareApi instance
|
|
37
42
|
*/
|
|
38
|
-
export declare function createCloudflareApi(options?: Partial<CloudflareApiOptions>): Promise<CloudflareApi>;
|
|
43
|
+
export declare function createCloudflareApi(options?: Partial<CloudflareApiOptions> | InternalCloudflareApiOptions): Promise<CloudflareApi>;
|
|
39
44
|
/**
|
|
40
45
|
* Cloudflare API client using raw fetch
|
|
41
46
|
*/
|
|
42
47
|
export declare class CloudflareApi {
|
|
43
48
|
readonly accountId: string;
|
|
44
49
|
readonly baseUrl: string;
|
|
45
|
-
readonly apiKey: Secret | undefined;
|
|
46
|
-
readonly apiToken: Secret | undefined;
|
|
47
|
-
readonly email: string | undefined;
|
|
48
50
|
readonly authOptions: CloudflareAuthOptions;
|
|
49
51
|
/**
|
|
50
52
|
* Create a new Cloudflare API client
|
|
@@ -53,8 +55,10 @@ export declare class CloudflareApi {
|
|
|
53
55
|
*
|
|
54
56
|
* @param options API options
|
|
55
57
|
*/
|
|
56
|
-
constructor(options:
|
|
58
|
+
constructor(options: {
|
|
59
|
+
baseUrl?: string;
|
|
57
60
|
accountId: string;
|
|
61
|
+
authOptions: CloudflareAuthOptions;
|
|
58
62
|
});
|
|
59
63
|
/**
|
|
60
64
|
* Make a fetch request to the Cloudflare API
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/cloudflare/api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/cloudflare/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,WAAW,CAAC;AAGnB;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,oHAAoH;AACpH,MAAM,MAAM,4BAA4B,GAAG,qBAAqB,GAAG;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,GAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,4BAAiC,GACzE,OAAO,CAAC,aAAa,CAAC,CAYxB;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,SAAgB,SAAS,EAAE,MAAM,CAAC;IAClC,SAAgB,OAAO,EAAE,MAAM,CAAC;IAChC,SAAgB,WAAW,EAAE,qBAAqB,CAAC;IAEnD;;;;;;OAMG;gBACS,OAAO,EAAE;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,qBAAqB,CAAC;KACpC;IAMD;;;;;;OAMG;IACU,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IA8D3E;;OAEG;IACG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIlE;;OAEG;IACG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAGnE;;OAEG;IACG,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,GAAG,EACT,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,QAAQ,CAAC;IAUpB;;OAEG;IACG,GAAG,CACP,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,GAAG,EACT,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,QAAQ,CAAC;IAKpB;;OAEG;IACG,KAAK,CACT,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,GAAG,EACT,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,QAAQ,CAAC;IAQpB;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;CAGtE"}
|
package/lib/cloudflare/api.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { alchemy } from "../alchemy.js";
|
|
2
1
|
import { withExponentialBackoff } from "../util/retry.js";
|
|
3
2
|
import { safeFetch } from "../util/safe-fetch.js";
|
|
4
|
-
import { getCloudflareAuthHeaders, } from "./auth.js";
|
|
5
|
-
import {
|
|
3
|
+
import { getCloudflareAuthHeaders, normalizeAuthOptions, } from "./auth.js";
|
|
4
|
+
import { getCloudflareAccountId } from "./user.js";
|
|
6
5
|
/**
|
|
7
6
|
* Creates a CloudflareApi instance with automatic account ID discovery if not provided
|
|
8
7
|
*
|
|
@@ -10,35 +9,15 @@ import { getCloudflareAccounts, getUserEmailFromApiKey } from "./user.js";
|
|
|
10
9
|
* @returns Promise resolving to a CloudflareApi instance
|
|
11
10
|
*/
|
|
12
11
|
export async function createCloudflareApi(options = {}) {
|
|
13
|
-
const
|
|
14
|
-
(process.env.CLOUDFLARE_API_KEY
|
|
15
|
-
? alchemy.secret(process.env.CLOUDFLARE_API_KEY)
|
|
16
|
-
: undefined);
|
|
17
|
-
const apiToken = options.apiToken ??
|
|
18
|
-
(process.env.CLOUDFLARE_API_TOKEN
|
|
19
|
-
? alchemy.secret(process.env.CLOUDFLARE_API_TOKEN)
|
|
20
|
-
: undefined);
|
|
21
|
-
let email = options.email ?? process.env.CLOUDFLARE_EMAIL;
|
|
22
|
-
if (apiKey && !email) {
|
|
23
|
-
email = await getUserEmailFromApiKey(apiKey.unencrypted);
|
|
24
|
-
}
|
|
12
|
+
const authOptions = await normalizeAuthOptions(options);
|
|
25
13
|
const accountId = options.accountId ??
|
|
26
14
|
process.env.CLOUDFLARE_ACCOUNT_ID ??
|
|
27
15
|
process.env.CF_ACCOUNT_ID ??
|
|
28
|
-
(await
|
|
29
|
-
apiKey,
|
|
30
|
-
apiToken,
|
|
31
|
-
email,
|
|
32
|
-
}))[0]?.id;
|
|
33
|
-
if (accountId === undefined) {
|
|
34
|
-
throw new Error("Either accountId or CLOUDFLARE_ACCOUNT_ID must be provided");
|
|
35
|
-
}
|
|
16
|
+
(await getCloudflareAccountId(authOptions));
|
|
36
17
|
return new CloudflareApi({
|
|
37
18
|
baseUrl: options.baseUrl,
|
|
38
19
|
accountId,
|
|
39
|
-
|
|
40
|
-
apiKey,
|
|
41
|
-
apiToken,
|
|
20
|
+
authOptions,
|
|
42
21
|
});
|
|
43
22
|
}
|
|
44
23
|
/**
|
|
@@ -47,9 +26,6 @@ export async function createCloudflareApi(options = {}) {
|
|
|
47
26
|
export class CloudflareApi {
|
|
48
27
|
accountId;
|
|
49
28
|
baseUrl;
|
|
50
|
-
apiKey;
|
|
51
|
-
apiToken;
|
|
52
|
-
email;
|
|
53
29
|
authOptions;
|
|
54
30
|
/**
|
|
55
31
|
* Create a new Cloudflare API client
|
|
@@ -61,23 +37,7 @@ export class CloudflareApi {
|
|
|
61
37
|
constructor(options) {
|
|
62
38
|
this.accountId = options.accountId;
|
|
63
39
|
this.baseUrl = options.baseUrl ?? "https://api.cloudflare.com/client/v4";
|
|
64
|
-
this.
|
|
65
|
-
this.apiToken = options.apiToken;
|
|
66
|
-
this.email = options.email;
|
|
67
|
-
if (this.apiKey && this.apiToken) {
|
|
68
|
-
throw new Error("'apiKey' and 'apiToken' cannot both be provided");
|
|
69
|
-
}
|
|
70
|
-
else if (this.apiKey && !this.email) {
|
|
71
|
-
throw new Error("'email' must be provided if 'apiKey' is provided");
|
|
72
|
-
}
|
|
73
|
-
this.authOptions = this.apiKey
|
|
74
|
-
? {
|
|
75
|
-
apiKey: this.apiKey,
|
|
76
|
-
email: this.email,
|
|
77
|
-
}
|
|
78
|
-
: {
|
|
79
|
-
apiToken: this.apiToken,
|
|
80
|
-
};
|
|
40
|
+
this.authOptions = options.authOptions;
|
|
81
41
|
}
|
|
82
42
|
/**
|
|
83
43
|
* Make a fetch request to the Cloudflare API
|