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.
Files changed (40) hide show
  1. package/bin/alchemy.mjs +156 -160
  2. package/bin/commands/login.ts +2 -5
  3. package/lib/cloudflare/api.d.ts +9 -5
  4. package/lib/cloudflare/api.d.ts.map +1 -1
  5. package/lib/cloudflare/api.js +6 -46
  6. package/lib/cloudflare/api.js.map +1 -1
  7. package/lib/cloudflare/auth.d.ts +16 -35
  8. package/lib/cloudflare/auth.d.ts.map +1 -1
  9. package/lib/cloudflare/auth.js +54 -24
  10. package/lib/cloudflare/auth.js.map +1 -1
  11. package/lib/cloudflare/{auth-wrangler.d.ts → oauth.d.ts} +11 -14
  12. package/lib/cloudflare/oauth.d.ts.map +1 -0
  13. package/lib/cloudflare/{auth-wrangler.js → oauth.js} +98 -109
  14. package/lib/cloudflare/oauth.js.map +1 -0
  15. package/lib/cloudflare/user.d.ts +1 -1
  16. package/lib/cloudflare/user.d.ts.map +1 -1
  17. package/lib/cloudflare/user.js +20 -29
  18. package/lib/cloudflare/user.js.map +1 -1
  19. package/lib/cloudflare/worker.d.ts +1 -0
  20. package/lib/cloudflare/worker.d.ts.map +1 -1
  21. package/lib/cloudflare/worker.js +4 -3
  22. package/lib/cloudflare/worker.js.map +1 -1
  23. package/lib/util/memoize.d.ts +0 -1
  24. package/lib/util/memoize.d.ts.map +1 -1
  25. package/lib/util/memoize.js +0 -13
  26. package/lib/util/memoize.js.map +1 -1
  27. package/lib/util/neverthrow.d.ts +7 -0
  28. package/lib/util/neverthrow.d.ts.map +1 -1
  29. package/lib/util/neverthrow.js +17 -0
  30. package/lib/util/neverthrow.js.map +1 -1
  31. package/package.json +1 -1
  32. package/src/cloudflare/api.ts +18 -57
  33. package/src/cloudflare/auth.ts +84 -68
  34. package/src/cloudflare/{auth-wrangler.ts → oauth.ts} +154 -168
  35. package/src/cloudflare/user.ts +33 -51
  36. package/src/cloudflare/worker.ts +9 -4
  37. package/src/util/memoize.ts +0 -17
  38. package/src/util/neverthrow.ts +28 -0
  39. package/lib/cloudflare/auth-wrangler.d.ts.map +0 -1
  40. 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 assert(condition, msg = "no additional info") {
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 assert;
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 = assert;
5211
+ exports2.assert = assert2;
5212
5212
  exports2.getEnumValues = getEnumValues;
5213
5213
  exports2.joinValues = joinValues;
5214
5214
  exports2.jsonStringifyReplacer = jsonStringifyReplacer;
5215
- exports2.cached = 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 assert(_3) {
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 cached(getter) {
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 = cached(() => {
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 z2 = __importStar(require_external());
20964
- exports2.z = z2;
20963
+ var z3 = __importStar(require_external());
20964
+ exports2.z = z3;
20965
20965
  __exportStar(require_external(), exports2);
20966
- exports2.default = z2;
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 z2 = __importStar(require_external2());
25365
- exports2.z = z2;
25364
+ var z3 = __importStar(require_external2());
25365
+ exports2.z = z3;
25366
25366
  __exportStar(require_external2(), exports2);
25367
- exports2.default = z2;
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 import_trpc_cli2 = __toESM(require_dist2(), 1);
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 z2 = ".".repeat(Math.floor(w2)).slice(0, 3);
37259
- process.stdout.write(`${I2} ${l2}${z2}`);
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/auth-wrangler.ts
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 cached = cache.get(key);
50844
- if (cached) {
50845
- return await cached;
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 cached = results.get(key);
50889
- if (cached) {
50890
- return cached;
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/auth-wrangler.ts
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
- return ResultAsync.fromSafePromise(getWranglerConfigPath()).andThen(
50969
- (path14) => ResultAsync.fromPromise(
50970
- fs15.readFile(path14, "utf-8").then(async (text) => {
50971
- const toml = await Promise.resolve().then(() => __toESM(require_toml(), 1));
50972
- return toml.parse(text.replace(/\r\n/g, "\n"));
50973
- }),
50974
- () => new Error("Failed to read Wrangler config.")
50975
- )
50976
- ).andThen((config) => {
50977
- if (typeof config.oauth_token !== "string" || typeof config.refresh_token !== "string" || !config.expiration_time || !Array.isArray(config.scopes)) {
50978
- return err(new Error("Invalid Wrangler config."));
50979
- }
50980
- return ok({
50981
- oauth_token: config.oauth_token,
50982
- refresh_token: config.refresh_token,
50983
- expiration_time: new Date(config.expiration_time),
50984
- scopes: Array.from(config.scopes)
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 Response.redirect(`${ALCHEMY_BASE_URL}/auth/error`);
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 Response.redirect(`${ALCHEMY_BASE_URL}/auth/error`);
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 Response.redirect(`${ALCHEMY_BASE_URL}/auth/error`);
51095
+ return renderResponse("error");
51151
51096
  }
51152
51097
  ok2(tokens.value);
51153
- return Response.redirect(`${ALCHEMY_BASE_URL}/auth/success`);
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, async () => {
51113
+ return ensure(result, () => {
51169
51114
  clearTimeout(timeout);
51170
- await server.stop();
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
- zod_default.tuple([
51179
- zod_default.object({
51180
- scopes: zod_default.array(zod_default.string()).optional().default([]).describe("Cloudflare OAuth scopes to authorize"),
51181
- defaultScopes: zod_default.boolean().optional().default(true).describe(
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, import_trpc_cli2.createCli)({
51240
+ var cli = (0, import_trpc_cli3.createCli)({
51245
51241
  router,
51246
51242
  name: "alchemy",
51247
51243
  version: getPackageVersion(),
@@ -1,10 +1,7 @@
1
1
  import { cancel, intro, log, outro } from "@clack/prompts";
2
2
  import pc from "picocolors";
3
- import z from "zod";
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
 
@@ -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: CloudflareApiOptions & {
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":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EAEL,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;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAC1C,OAAO,CAAC,aAAa,CAAC,CAsCxB;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,SAAgB,SAAS,EAAE,MAAM,CAAC;IAClC,SAAgB,OAAO,EAAE,MAAM,CAAC;IAChC,SAAgB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,SAAgB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,SAAgB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,SAAgB,WAAW,EAAE,qBAAqB,CAAC;IAEnD;;;;;;OAMG;gBAED,OAAO,EAAE,oBAAoB,GAAG;QAC9B,SAAS,EAAE,MAAM,CAAC;KACnB;IAuBH;;;;;;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"}
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"}
@@ -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 { getCloudflareAccounts, getUserEmailFromApiKey } from "./user.js";
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 apiKey = options.apiKey ??
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 getCloudflareAccounts({
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
- email,
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.apiKey = options.apiKey;
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