alchemy 0.12.21 → 0.14.0

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 (319) hide show
  1. package/lib/ai/client.d.ts.map +1 -1
  2. package/lib/ai/client.js +3 -5
  3. package/lib/ai/client.js.map +1 -1
  4. package/lib/alchemy.d.ts +2 -1
  5. package/lib/alchemy.d.ts.map +1 -1
  6. package/lib/alchemy.js +87 -92
  7. package/lib/alchemy.js.map +1 -1
  8. package/lib/apply.d.ts.map +1 -1
  9. package/lib/apply.js +6 -1
  10. package/lib/apply.js.map +1 -1
  11. package/lib/aws/bucket.d.ts.map +1 -1
  12. package/lib/aws/bucket.js +55 -57
  13. package/lib/aws/bucket.js.map +1 -1
  14. package/lib/aws/function.d.ts.map +1 -1
  15. package/lib/aws/function.js +250 -252
  16. package/lib/aws/function.js.map +1 -1
  17. package/lib/aws/policy-attachment.d.ts.map +1 -1
  18. package/lib/aws/policy-attachment.js +4 -6
  19. package/lib/aws/policy-attachment.js.map +1 -1
  20. package/lib/aws/policy.d.ts.map +1 -1
  21. package/lib/aws/policy.js +63 -65
  22. package/lib/aws/policy.js.map +1 -1
  23. package/lib/aws/queue.js +88 -90
  24. package/lib/aws/queue.js.map +1 -1
  25. package/lib/aws/table.d.ts.map +1 -1
  26. package/lib/aws/table.js +95 -99
  27. package/lib/aws/table.js.map +1 -1
  28. package/lib/cloudflare/account-api-token.d.ts +1 -1
  29. package/lib/cloudflare/account-api-token.d.ts.map +1 -1
  30. package/lib/cloudflare/account-api-token.js +0 -1
  31. package/lib/cloudflare/account-api-token.js.map +1 -1
  32. package/lib/cloudflare/ai-gateway.js.map +1 -1
  33. package/lib/cloudflare/ai.d.ts +1 -1
  34. package/lib/cloudflare/ai.d.ts.map +1 -1
  35. package/lib/cloudflare/ai.js.map +1 -1
  36. package/lib/cloudflare/api.d.ts +5 -5
  37. package/lib/cloudflare/api.d.ts.map +1 -1
  38. package/lib/cloudflare/api.js +52 -15
  39. package/lib/cloudflare/api.js.map +1 -1
  40. package/lib/cloudflare/auth.d.ts +24 -55
  41. package/lib/cloudflare/auth.d.ts.map +1 -1
  42. package/lib/cloudflare/auth.js +15 -72
  43. package/lib/cloudflare/auth.js.map +1 -1
  44. package/lib/cloudflare/bucket.d.ts +2 -2
  45. package/lib/cloudflare/bucket.d.ts.map +1 -1
  46. package/lib/cloudflare/bucket.js +22 -24
  47. package/lib/cloudflare/bucket.js.map +1 -1
  48. package/lib/cloudflare/bundle/alias-plugin.d.ts +6 -0
  49. package/lib/cloudflare/bundle/alias-plugin.d.ts.map +1 -0
  50. package/lib/cloudflare/bundle/alias-plugin.js +36 -0
  51. package/lib/cloudflare/bundle/alias-plugin.js.map +1 -0
  52. package/lib/cloudflare/bundle/build-failures.d.ts +12 -0
  53. package/lib/cloudflare/bundle/build-failures.d.ts.map +1 -0
  54. package/lib/cloudflare/bundle/build-failures.js +45 -0
  55. package/lib/cloudflare/bundle/build-failures.js.map +1 -0
  56. package/lib/cloudflare/bundle/bundle-worker.d.ts +7 -0
  57. package/lib/cloudflare/bundle/bundle-worker.d.ts.map +1 -0
  58. package/lib/cloudflare/bundle/bundle-worker.js +69 -0
  59. package/lib/cloudflare/bundle/bundle-worker.js.map +1 -0
  60. package/lib/cloudflare/bundle/external.d.ts +1 -0
  61. package/lib/cloudflare/bundle/external.d.ts.map +1 -1
  62. package/lib/cloudflare/bundle/external.js +8 -1
  63. package/lib/cloudflare/bundle/external.js.map +1 -1
  64. package/lib/cloudflare/bundle/local-dev-cloudflare-shim.d.ts +1 -1
  65. package/lib/cloudflare/bundle/local-dev-cloudflare-shim.d.ts.map +1 -1
  66. package/lib/cloudflare/bundle/local-dev-cloudflare-shim.js +6 -6
  67. package/lib/cloudflare/bundle/local-dev-cloudflare-shim.js.map +1 -1
  68. package/lib/cloudflare/bundle/nodejs-compat-mode.d.ts +17 -0
  69. package/lib/cloudflare/bundle/nodejs-compat-mode.d.ts.map +1 -0
  70. package/lib/cloudflare/bundle/nodejs-compat-mode.js +27 -0
  71. package/lib/cloudflare/bundle/nodejs-compat-mode.js.map +1 -0
  72. package/lib/cloudflare/bundle/{hybrid-nodejs-compat.d.ts → nodejs-compat.d.ts} +2 -2
  73. package/lib/cloudflare/bundle/nodejs-compat.d.ts.map +1 -0
  74. package/lib/cloudflare/bundle/{hybrid-nodejs-compat.js → nodejs-compat.js} +2 -2
  75. package/lib/cloudflare/bundle/nodejs-compat.js.map +1 -0
  76. package/lib/cloudflare/custom-domain.d.ts.map +1 -1
  77. package/lib/cloudflare/custom-domain.js +3 -5
  78. package/lib/cloudflare/custom-domain.js.map +1 -1
  79. package/lib/cloudflare/d1-database.d.ts +1 -1
  80. package/lib/cloudflare/d1-database.d.ts.map +1 -1
  81. package/lib/cloudflare/d1-database.js +70 -72
  82. package/lib/cloudflare/d1-database.js.map +1 -1
  83. package/lib/cloudflare/d1-migrations.d.ts +1 -1
  84. package/lib/cloudflare/d1-migrations.d.ts.map +1 -1
  85. package/lib/cloudflare/d1-migrations.js +2 -3
  86. package/lib/cloudflare/d1-migrations.js.map +1 -1
  87. package/lib/cloudflare/dns-records.d.ts.map +1 -1
  88. package/lib/cloudflare/dns-records.js +2 -4
  89. package/lib/cloudflare/dns-records.js.map +1 -1
  90. package/lib/cloudflare/kv-namespace.d.ts +1 -1
  91. package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
  92. package/lib/cloudflare/kv-namespace.js +24 -27
  93. package/lib/cloudflare/kv-namespace.js.map +1 -1
  94. package/lib/cloudflare/pipeline.d.ts +1 -1
  95. package/lib/cloudflare/pipeline.d.ts.map +1 -1
  96. package/lib/cloudflare/pipeline.js +32 -34
  97. package/lib/cloudflare/pipeline.js.map +1 -1
  98. package/lib/cloudflare/queue-consumer.d.ts +1 -1
  99. package/lib/cloudflare/queue-consumer.d.ts.map +1 -1
  100. package/lib/cloudflare/queue-consumer.js +27 -33
  101. package/lib/cloudflare/queue-consumer.js.map +1 -1
  102. package/lib/cloudflare/queue.d.ts +1 -1
  103. package/lib/cloudflare/queue.d.ts.map +1 -1
  104. package/lib/cloudflare/queue.js +36 -38
  105. package/lib/cloudflare/queue.js.map +1 -1
  106. package/lib/cloudflare/r2-rest-state-store.js +4 -8
  107. package/lib/cloudflare/r2-rest-state-store.js.map +1 -1
  108. package/lib/cloudflare/types.d.ts.map +1 -1
  109. package/lib/cloudflare/types.js +1 -3
  110. package/lib/cloudflare/types.js.map +1 -1
  111. package/lib/cloudflare/user.d.ts +51 -0
  112. package/lib/cloudflare/user.d.ts.map +1 -0
  113. package/lib/cloudflare/user.js +62 -0
  114. package/lib/cloudflare/user.js.map +1 -0
  115. package/lib/cloudflare/vectorize-index.d.ts.map +1 -1
  116. package/lib/cloudflare/vectorize-index.js +37 -39
  117. package/lib/cloudflare/vectorize-index.js.map +1 -1
  118. package/lib/cloudflare/vectorize-metadata-index.d.ts +1 -1
  119. package/lib/cloudflare/vectorize-metadata-index.d.ts.map +1 -1
  120. package/lib/cloudflare/vectorize-metadata-index.js +10 -13
  121. package/lib/cloudflare/vectorize-metadata-index.js.map +1 -1
  122. package/lib/cloudflare/worker.d.ts +18 -3
  123. package/lib/cloudflare/worker.d.ts.map +1 -1
  124. package/lib/cloudflare/worker.js +11 -54
  125. package/lib/cloudflare/worker.js.map +1 -1
  126. package/lib/cloudflare/workflow.d.ts +1 -1
  127. package/lib/cloudflare/workflow.d.ts.map +1 -1
  128. package/lib/cloudflare/workflow.js.map +1 -1
  129. package/lib/cloudflare/zone.d.ts.map +1 -1
  130. package/lib/cloudflare/zone.js +48 -50
  131. package/lib/cloudflare/zone.js.map +1 -1
  132. package/lib/dns/godaddy.js +1 -1
  133. package/lib/dns/import-dns.js +1 -1
  134. package/lib/dns/import-dns.js.map +1 -1
  135. package/lib/env.js +7 -9
  136. package/lib/env.js.map +1 -1
  137. package/lib/esbuild/bundle.d.ts.map +1 -1
  138. package/lib/esbuild/bundle.js +8 -11
  139. package/lib/esbuild/bundle.js.map +1 -1
  140. package/lib/fs/copy-file.d.ts.map +1 -1
  141. package/lib/fs/copy-file.js +27 -29
  142. package/lib/fs/copy-file.js.map +1 -1
  143. package/lib/fs/file.d.ts +1 -1
  144. package/lib/fs/file.d.ts.map +1 -1
  145. package/lib/fs/file.js +1 -1
  146. package/lib/fs/file.js.map +1 -1
  147. package/lib/fs/folder.d.ts.map +1 -1
  148. package/lib/fs/folder.js +1 -3
  149. package/lib/fs/folder.js.map +1 -1
  150. package/lib/github/client.d.ts.map +1 -1
  151. package/lib/github/client.js +3 -5
  152. package/lib/github/client.js.map +1 -1
  153. package/lib/github/repository-environment.d.ts.map +1 -1
  154. package/lib/github/repository-environment.js +173 -176
  155. package/lib/github/repository-environment.js.map +1 -1
  156. package/lib/github/secret.d.ts.map +1 -1
  157. package/lib/github/secret.js +97 -99
  158. package/lib/github/secret.js.map +1 -1
  159. package/lib/internal/docs/providers.d.ts.map +1 -1
  160. package/lib/internal/docs/providers.js +5 -7
  161. package/lib/internal/docs/providers.js.map +1 -1
  162. package/lib/neon/api.js +1 -2
  163. package/lib/neon/api.js.map +1 -1
  164. package/lib/neon/project.js +2 -2
  165. package/lib/neon/project.js.map +1 -1
  166. package/lib/os/exec.d.ts +5 -0
  167. package/lib/os/exec.d.ts.map +1 -1
  168. package/lib/os/exec.js +88 -56
  169. package/lib/os/exec.js.map +1 -1
  170. package/lib/scope.d.ts +3 -0
  171. package/lib/scope.d.ts.map +1 -1
  172. package/lib/scope.js +10 -3
  173. package/lib/scope.js.map +1 -1
  174. package/lib/secret.d.ts.map +1 -1
  175. package/lib/secret.js +1 -3
  176. package/lib/secret.js.map +1 -1
  177. package/lib/serde.d.ts.map +1 -1
  178. package/lib/serde.js +13 -15
  179. package/lib/serde.js.map +1 -1
  180. package/lib/stripe/price.d.ts.map +1 -1
  181. package/lib/stripe/price.js +69 -73
  182. package/lib/stripe/price.js.map +1 -1
  183. package/lib/stripe/product.d.ts.map +1 -1
  184. package/lib/stripe/product.js +50 -52
  185. package/lib/stripe/product.js.map +1 -1
  186. package/lib/stripe/webhook.d.ts.map +1 -1
  187. package/lib/stripe/webhook.js +51 -53
  188. package/lib/stripe/webhook.js.map +1 -1
  189. package/lib/test/bun.d.ts +1 -1
  190. package/lib/test/bun.d.ts.map +1 -1
  191. package/lib/test/bun.js +3 -8
  192. package/lib/test/bun.js.map +1 -1
  193. package/lib/test/prune.d.ts.map +1 -1
  194. package/lib/test/prune.js.map +1 -1
  195. package/lib/util/dedent.js +4 -4
  196. package/lib/util/dedent.js.map +1 -1
  197. package/lib/web/astro.js +1 -1
  198. package/lib/web/shadcn.js +4 -4
  199. package/lib/web/shadcn.js.map +1 -1
  200. package/lib/web/tailwind.d.ts.map +1 -1
  201. package/lib/web/tailwind.js +0 -1
  202. package/lib/web/tailwind.js.map +1 -1
  203. package/lib/web/vite.js +3 -3
  204. package/lib/web/vitepress/home-page.d.ts.map +1 -1
  205. package/lib/web/vitepress/home-page.js +6 -8
  206. package/lib/web/vitepress/home-page.js.map +1 -1
  207. package/lib/web/vitepress/process-front-matter-files.js +1 -1
  208. package/lib/web/vitepress/process-front-matter-files.js.map +1 -1
  209. package/lib/web/vitepress/vitepress.js +2 -2
  210. package/lib/web/vitepress/vitepress.js.map +1 -1
  211. package/package.json +4 -1
  212. package/src/ai/astro-file.ts +4 -4
  213. package/src/ai/client.ts +4 -5
  214. package/src/ai/css-file.ts +4 -4
  215. package/src/ai/document.ts +3 -3
  216. package/src/ai/html-file.ts +4 -4
  217. package/src/ai/json-file.ts +1 -1
  218. package/src/ai/typescript-file.ts +4 -4
  219. package/src/ai/vue-file.ts +4 -4
  220. package/src/ai/yaml-file.ts +1 -1
  221. package/src/alchemy.ts +111 -104
  222. package/src/apply.ts +13 -6
  223. package/src/aws/bucket.ts +72 -75
  224. package/src/aws/function.ts +296 -297
  225. package/src/aws/oidc/github-oidc-provider.ts +1 -1
  226. package/src/aws/oidc/oidc-provider.ts +15 -15
  227. package/src/aws/policy-attachment.ts +10 -11
  228. package/src/aws/policy.ts +91 -94
  229. package/src/aws/queue.ts +111 -111
  230. package/src/aws/role.ts +24 -24
  231. package/src/aws/ses.ts +17 -17
  232. package/src/aws/table.ts +114 -115
  233. package/src/cloudflare/account-api-token.ts +11 -11
  234. package/src/cloudflare/ai-gateway.ts +4 -4
  235. package/src/cloudflare/ai.ts +1 -1
  236. package/src/cloudflare/api-error.ts +1 -1
  237. package/src/cloudflare/api.ts +70 -25
  238. package/src/cloudflare/assets.ts +4 -4
  239. package/src/cloudflare/auth.ts +49 -164
  240. package/src/cloudflare/bucket.ts +62 -63
  241. package/src/cloudflare/bundle/alias-plugin.ts +48 -0
  242. package/src/cloudflare/bundle/build-failures.ts +55 -0
  243. package/src/cloudflare/bundle/bundle-worker.ts +89 -0
  244. package/src/cloudflare/bundle/external.ts +9 -1
  245. package/src/cloudflare/bundle/local-dev-cloudflare-shim.ts +7 -6
  246. package/src/cloudflare/bundle/nodejs-compat-mode.ts +49 -0
  247. package/src/cloudflare/bundle/{hybrid-nodejs-compat.ts → nodejs-compat.ts} +10 -10
  248. package/src/cloudflare/custom-domain.ts +27 -28
  249. package/src/cloudflare/d1-database.ts +101 -102
  250. package/src/cloudflare/d1-migrations.ts +10 -10
  251. package/src/cloudflare/dns-records.ts +23 -24
  252. package/src/cloudflare/durable-object-namespace.ts +1 -1
  253. package/src/cloudflare/event-source.ts +1 -1
  254. package/src/cloudflare/hyperdrive.ts +3 -3
  255. package/src/cloudflare/kv-namespace.ts +36 -37
  256. package/src/cloudflare/nuxt.ts +1 -1
  257. package/src/cloudflare/permission-groups.ts +7 -7
  258. package/src/cloudflare/pipeline.ts +55 -56
  259. package/src/cloudflare/queue-consumer.ts +46 -53
  260. package/src/cloudflare/queue.ts +56 -57
  261. package/src/cloudflare/r2-rest-state-store.ts +11 -11
  262. package/src/cloudflare/redwood.ts +1 -1
  263. package/src/cloudflare/tanstack-start.ts +1 -1
  264. package/src/cloudflare/types.ts +1 -2
  265. package/src/cloudflare/user.ts +142 -0
  266. package/src/cloudflare/vectorize-index.ts +60 -61
  267. package/src/cloudflare/vectorize-metadata-index.ts +25 -25
  268. package/src/cloudflare/vite.ts +1 -1
  269. package/src/cloudflare/website.ts +3 -3
  270. package/src/cloudflare/worker.ts +83 -108
  271. package/src/cloudflare/workflow.ts +4 -4
  272. package/src/cloudflare/wrangler.json.ts +4 -4
  273. package/src/cloudflare/zone.ts +69 -70
  274. package/src/destroy.ts +4 -4
  275. package/src/dns/godaddy.ts +1 -1
  276. package/src/dns/import-dns.ts +5 -5
  277. package/src/encrypt.ts +6 -6
  278. package/src/env.ts +10 -10
  279. package/src/esbuild/bundle.ts +14 -15
  280. package/src/fs/copy-file.ts +35 -36
  281. package/src/fs/file-system-state-store.ts +4 -4
  282. package/src/fs/file.ts +9 -8
  283. package/src/fs/folder.ts +6 -7
  284. package/src/github/client.ts +15 -14
  285. package/src/github/repository-environment.ts +206 -210
  286. package/src/github/secret.ts +111 -116
  287. package/src/internal/docs/providers.ts +16 -17
  288. package/src/neon/api-error.ts +1 -1
  289. package/src/neon/api.ts +6 -6
  290. package/src/neon/project.ts +18 -17
  291. package/src/os/exec.ts +107 -66
  292. package/src/resource.ts +3 -3
  293. package/src/scope.ts +14 -4
  294. package/src/secret.ts +2 -3
  295. package/src/serde.ts +30 -22
  296. package/src/stripe/price.ts +74 -77
  297. package/src/stripe/product.ts +55 -56
  298. package/src/stripe/webhook.ts +56 -57
  299. package/src/test/bun.ts +9 -13
  300. package/src/test/prune.ts +4 -4
  301. package/src/util/dedent.ts +4 -4
  302. package/src/web/astro.ts +10 -10
  303. package/src/web/shadcn-component.ts +4 -4
  304. package/src/web/shadcn.ts +10 -10
  305. package/src/web/tailwind.ts +2 -3
  306. package/src/web/vite.ts +11 -11
  307. package/src/web/vitepress/config.ts +2 -2
  308. package/src/web/vitepress/custom-theme.ts +7 -7
  309. package/src/web/vitepress/dependencies.ts +4 -4
  310. package/src/web/vitepress/home-page.ts +8 -9
  311. package/src/web/vitepress/process-front-matter-files.ts +5 -5
  312. package/src/web/vitepress/vitepress.ts +7 -7
  313. package/lib/cloudflare/account-id.d.ts +0 -6
  314. package/lib/cloudflare/account-id.d.ts.map +0 -1
  315. package/lib/cloudflare/account-id.js +0 -6
  316. package/lib/cloudflare/account-id.js.map +0 -1
  317. package/lib/cloudflare/bundle/hybrid-nodejs-compat.d.ts.map +0 -1
  318. package/lib/cloudflare/bundle/hybrid-nodejs-compat.js.map +0 -1
  319. package/src/cloudflare/account-id.ts +0 -12
@@ -154,7 +154,7 @@ export const AiGateway = Resource(
154
154
  async function (
155
155
  this: Context<AiGateway>,
156
156
  id: string,
157
- props: AiGatewayProps = {}
157
+ props: AiGatewayProps = {},
158
158
  ): Promise<AiGateway> {
159
159
  const api = await createCloudflareApi(props);
160
160
  const gatewayPath = `/accounts/${api.accountId}/ai-gateway/gateways/${id}`;
@@ -199,7 +199,7 @@ export const AiGateway = Resource(
199
199
  if (getResponse.status === 200) {
200
200
  // Gateway exists, treat as update (PUT)
201
201
  console.log(
202
- `AI Gateway '${id}' already exists. Updating existing resource.`
202
+ `AI Gateway '${id}' already exists. Updating existing resource.`,
203
203
  );
204
204
  const requestBody = mapPropsToApi(id, mergedProps, false);
205
205
  response = await api.put(gatewayPath, requestBody);
@@ -248,14 +248,14 @@ export const AiGateway = Resource(
248
248
  logpushPublicKey: apiResource.logpush_public_key,
249
249
  type: "ai_gateway",
250
250
  });
251
- }
251
+ },
252
252
  );
253
253
 
254
254
  // Helper function to map props to the API request body format
255
255
  function mapPropsToApi(
256
256
  id: string,
257
257
  props: AiGatewayProps,
258
- includeIdInBody: boolean = false
258
+ includeIdInBody = false,
259
259
  ): Record<string, any> {
260
260
  const body: Record<string, any> = {};
261
261
  if (includeIdInBody) {
@@ -2,7 +2,7 @@
2
2
  * @see https://developers.cloudflare.com/workers-ai/
3
3
  */
4
4
  export class Ai<Models extends AiModelListType = AiModelListType> {
5
- public readonly type: "ai" = "ai";
5
+ public readonly type = "ai";
6
6
 
7
7
  /**
8
8
  * @internal
@@ -46,7 +46,7 @@ export async function handleApiError(
46
46
  response: Response,
47
47
  action: string,
48
48
  resourceType: string,
49
- resourceName: string
49
+ resourceName: string,
50
50
  ): Promise<never> {
51
51
  const text = await response.text();
52
52
  let json: any;
@@ -1,6 +1,11 @@
1
+ import { alchemy } from "../alchemy.js";
1
2
  import type { Secret } from "../secret.js";
2
3
  import { withExponentialBackoff } from "../util/retry.js";
3
- import { getCloudflareAuthHeaders, getCloudflareUserInfo } from "./auth.js";
4
+ import {
5
+ getCloudflareAuthHeaders,
6
+ type CloudflareAuthOptions,
7
+ } from "./auth.js";
8
+ import { getCloudflareUserInfo, getUserEmailFromApiKey } from "./user.js";
4
9
 
5
10
  /**
6
11
  * Options for Cloudflare API requests
@@ -29,11 +34,6 @@ export interface CloudflareApiOptions {
29
34
  */
30
35
  accountId?: string;
31
36
 
32
- /**
33
- * Zone ID to use (overrides CLOUDFLARE_ZONE_ID env var)
34
- */
35
- zoneId?: string;
36
-
37
37
  /**
38
38
  * Email to use with API Key authentication
39
39
  * If not provided, will attempt to discover from Cloudflare API
@@ -48,16 +48,43 @@ export interface CloudflareApiOptions {
48
48
  * @returns Promise resolving to a CloudflareApi instance
49
49
  */
50
50
  export async function createCloudflareApi(
51
- options: Partial<CloudflareApiOptions> = {}
51
+ options: Partial<CloudflareApiOptions> = {},
52
52
  ): Promise<CloudflareApi> {
53
- const userInfo = await getCloudflareUserInfo(options);
53
+ const apiKey =
54
+ options.apiKey ??
55
+ (process.env.CLOUDFLARE_API_KEY
56
+ ? alchemy.secret(process.env.CLOUDFLARE_API_KEY)
57
+ : undefined);
58
+ const apiToken =
59
+ options.apiToken ??
60
+ (process.env.CLOUDFLARE_API_TOKEN
61
+ ? alchemy.secret(process.env.CLOUDFLARE_API_TOKEN)
62
+ : undefined);
63
+ let email = options.email ?? process.env.CLOUDFLARE_EMAIL;
64
+ if (apiKey && !email) {
65
+ email = await getUserEmailFromApiKey(apiKey.unencrypted);
66
+ }
67
+ const accountId =
68
+ options.accountId ??
69
+ process.env.CLOUDFLARE_ACCOUNT_ID ??
70
+ (
71
+ await getCloudflareUserInfo({
72
+ apiKey,
73
+ apiToken,
74
+ email,
75
+ } as CloudflareAuthOptions)
76
+ ).accounts[0]?.id;
77
+ if (accountId === undefined) {
78
+ throw new Error(
79
+ "Either accountId or CLOUDFLARE_ACCOUNT_ID must be provided",
80
+ );
81
+ }
54
82
  return new CloudflareApi({
55
83
  baseUrl: options.baseUrl,
56
- accountId: options.accountId ?? userInfo.accounts[0].id!,
57
- email: userInfo.email!,
58
- apiKey: userInfo.apiKey,
59
- apiToken: userInfo.apiToken,
60
- zoneId: options.zoneId,
84
+ accountId,
85
+ email,
86
+ apiKey,
87
+ apiToken,
61
88
  });
62
89
  }
63
90
 
@@ -67,6 +94,11 @@ export async function createCloudflareApi(
67
94
  export class CloudflareApi {
68
95
  public readonly accountId: string;
69
96
  public readonly baseUrl: string;
97
+ public readonly apiKey: Secret | undefined;
98
+ public readonly apiToken: Secret | undefined;
99
+ public readonly email: string | undefined;
100
+ public readonly authOptions: CloudflareAuthOptions;
101
+
70
102
  /**
71
103
  * Create a new Cloudflare API client
72
104
  * Use createCloudflareApi factory function instead of direct constructor
@@ -75,12 +107,29 @@ export class CloudflareApi {
75
107
  * @param options API options
76
108
  */
77
109
  constructor(
78
- private readonly options: CloudflareApiOptions & {
110
+ options: CloudflareApiOptions & {
79
111
  accountId: string;
80
- }
112
+ },
81
113
  ) {
82
114
  this.accountId = options.accountId;
83
115
  this.baseUrl = options.baseUrl ?? "https://api.cloudflare.com/client/v4";
116
+ this.apiKey = options.apiKey;
117
+ this.apiToken = options.apiToken;
118
+ this.email = options.email;
119
+
120
+ if (this.apiKey && this.apiToken) {
121
+ throw new Error("'apiKey' and 'apiToken' cannot both be provided");
122
+ } else if (this.apiKey && !this.email) {
123
+ throw new Error("'email' must be provided if 'apiKey' is provided");
124
+ }
125
+ this.authOptions = this.apiKey
126
+ ? {
127
+ apiKey: this.apiKey,
128
+ email: this.email!,
129
+ }
130
+ : {
131
+ apiToken: this.apiToken,
132
+ };
84
133
  }
85
134
 
86
135
  /**
@@ -106,7 +155,7 @@ export class CloudflareApi {
106
155
  headers = init.headers;
107
156
  }
108
157
  headers = {
109
- ...(await getCloudflareAuthHeaders(this.options)),
158
+ ...(await getCloudflareAuthHeaders(this.authOptions)),
110
159
  ...headers,
111
160
  };
112
161
 
@@ -130,7 +179,7 @@ export class CloudflareApi {
130
179
  // transient errors should be retried aggressively
131
180
  (error) => error instanceof InternalError,
132
181
  5, // Maximum 5 attempts (1 initial + 4 retries)
133
- 1000 // Start with 1s delay, will exponentially increase
182
+ 1000, // Start with 1s delay, will exponentially increase
134
183
  );
135
184
  }
136
185
 
@@ -153,7 +202,7 @@ export class CloudflareApi {
153
202
  async post(
154
203
  path: string,
155
204
  body: any,
156
- init: RequestInit = {}
205
+ init: RequestInit = {},
157
206
  ): Promise<Response> {
158
207
  const requestBody =
159
208
  body instanceof FormData
@@ -170,7 +219,7 @@ export class CloudflareApi {
170
219
  async put(
171
220
  path: string,
172
221
  body: any,
173
- init: RequestInit = {}
222
+ init: RequestInit = {},
174
223
  ): Promise<Response> {
175
224
  const requestBody = body instanceof FormData ? body : JSON.stringify(body);
176
225
  return this.fetch(path, { ...init, method: "PUT", body: requestBody });
@@ -182,7 +231,7 @@ export class CloudflareApi {
182
231
  async patch(
183
232
  path: string,
184
233
  body: any,
185
- init: RequestInit = {}
234
+ init: RequestInit = {},
186
235
  ): Promise<Response> {
187
236
  return this.fetch(path, {
188
237
  ...init,
@@ -199,8 +248,4 @@ export class CloudflareApi {
199
248
  }
200
249
  }
201
250
 
202
- class InternalError extends Error {
203
- constructor(message: string) {
204
- super(message);
205
- }
206
- }
251
+ class InternalError extends Error {}
@@ -92,7 +92,7 @@ export const Assets = Resource(
92
92
  async function (
93
93
  this: Context<Assets>,
94
94
  id: string,
95
- props: AssetsProps
95
+ props: AssetsProps,
96
96
  ): Promise<Assets> {
97
97
  if (this.phase === "delete") {
98
98
  return this.destroy();
@@ -106,7 +106,7 @@ export const Assets = Resource(
106
106
  }
107
107
  } catch (error) {
108
108
  throw new Error(
109
- `Assets directory ${props.path} does not exist or is not accessible`
109
+ `Assets directory ${props.path} does not exist or is not accessible`,
110
110
  );
111
111
  }
112
112
 
@@ -137,7 +137,7 @@ export const Assets = Resource(
137
137
  createdAt: this.output?.createdAt || now,
138
138
  updatedAt: now,
139
139
  });
140
- }
140
+ },
141
141
  );
142
142
 
143
143
  // Helper functions for file operations
@@ -151,7 +151,7 @@ async function getFilesRecursively(dir: string): Promise<string[]> {
151
151
  return getFilesRecursively(path);
152
152
  }
153
153
  return path;
154
- })
154
+ }),
155
155
  );
156
156
 
157
157
  return allFiles.flat();
@@ -2,203 +2,88 @@ import fs from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import type { Secret } from "../secret.js";
5
- import type { CloudflareAccountId } from "./account-id.js";
6
- import { handleApiError } from "./api-error.js";
7
5
  /**
8
6
  * Authentication options for Cloudflare API
9
7
  */
10
- export interface CloudflareAuthOptions {
8
+ export type CloudflareAuthOptions =
9
+ | CloudflareApiKeyAuthOptions
10
+ | CloudflareApiTokenAuthOptions;
11
+
12
+ export type CloudflareApiKeyAuthOptions = {
11
13
  /**
12
14
  * API Key to use with API Key
13
15
  */
14
- apiKey?: Secret;
15
-
16
+ apiKey: Secret;
16
17
  /**
17
- * API Token to use with API Key
18
+ * Email to use with API Key
19
+ * If not provided, will attempt to discover from Cloudflare API
18
20
  */
19
- apiToken?: Secret;
21
+ email: string;
20
22
 
21
23
  /**
22
- * Email to use with API Key
23
- * If not provided, will attempt to discover from Cloudflare API
24
+ * API Token to use with API Key
24
25
  */
25
- email?: string;
26
- }
26
+ apiToken?: undefined;
27
+ };
27
28
 
28
- export interface CloudflareAccount {
29
- name: string;
30
- id: CloudflareAccountId;
31
- type: "standard" | "zero_rating" | "full_control";
32
- settings: {
33
- enforce_twofactor: boolean;
34
- api_access_enabled: null;
35
- access_approval_expiry: null;
36
- abuse_contact_email: null;
37
- };
38
- legacy_flags: {
39
- enterprise_zone_quota: {
40
- maximum: number;
41
- current: number;
42
- available: number;
43
- };
44
- };
29
+ export function isCloudflareApiKeyAuthOptions(
30
+ options: CloudflareAuthOptions | undefined,
31
+ ): options is CloudflareApiKeyAuthOptions {
32
+ return options !== undefined && options.apiKey !== undefined;
45
33
  }
46
34
 
47
- export interface CloudflareUserInfo {
35
+ export type CloudflareApiTokenAuthOptions = {
36
+ /**
37
+ * API Token to use with API Key
38
+ */
48
39
  apiToken?: Secret;
49
- apiKey?: Secret;
50
- email: string;
51
- username: string;
52
- accounts: CloudflareAccount[];
53
- organizations: CloudflareOrganization[];
54
- tokenPermissions: string[] | undefined;
55
- first_name: string | null;
56
- last_name: string | null;
57
- telephone: string | null;
58
- country: string | null;
59
- zipcode: string | null;
60
- two_factor_authentication_enabled: boolean;
61
- two_factor_authentication_locked: boolean;
62
- has_pro_zones: boolean;
63
- has_business_zones: boolean;
64
- has_enterprise_zones: boolean;
65
- suspended: boolean;
66
- betas: string[];
67
- }
68
40
 
69
- export interface CloudflareOrganization {
70
- id: string;
71
- name: string;
72
- status: string;
73
- permissions: string[];
74
- roles: string[];
75
- }
41
+ /**
42
+ * API Key to use with API Token
43
+ */
44
+ apiKey?: undefined;
76
45
 
77
- const userInfoCache: Record<string, CloudflareUserInfo> = {};
46
+ /**
47
+ * Email to use with API Token
48
+ */
49
+ email?: undefined;
50
+ };
78
51
 
79
- export async function getCloudflareUserInfo(
80
- options: CloudflareAuthOptions
81
- ): Promise<CloudflareUserInfo> {
82
- const cacheKey = JSON.stringify({
83
- apiKey: options.apiKey?.unencrypted,
84
- apiToken: options.apiToken?.unencrypted,
85
- email: options.email,
86
- });
87
- if (userInfoCache[cacheKey]) {
88
- return userInfoCache[cacheKey];
89
- }
90
- const headers = await getCloudflareAuthHeaders(options);
91
- const user = await fetch("https://api.cloudflare.com/client/v4/user", {
92
- headers,
93
- });
94
- if (!user.ok) {
95
- if (user.status === 403) {
96
- throw new Error(
97
- "Cloudflare authentication required. Did you forget to login with `wrangler login` or set CLOUDFLARE_API_TOKEN, CLOUDFLARE_API_KEY, or CLOUDFLARE_EMAIL, or CLOUDFLARE_API_KEY?"
98
- );
99
- }
100
- await handleApiError(user, "getting", "user", "user");
101
- }
102
- const accounts = await fetch(
103
- "https://api.cloudflare.com/client/v4/accounts",
104
- {
105
- headers,
106
- }
107
- );
108
- const userInfo: CloudflareUserInfo = {
109
- ...((await user.json()) as any).result,
110
- accounts: ((await accounts.json()) as any).result,
111
- };
112
- userInfoCache[cacheKey] = userInfo;
113
- return userInfo;
52
+ export function isCloudflareApiTokenAuthOptions(
53
+ options: CloudflareAuthOptions | undefined,
54
+ ): options is CloudflareApiTokenAuthOptions {
55
+ return options !== undefined && options.apiToken !== undefined;
114
56
  }
115
57
 
116
58
  export async function getCloudflareAuthHeaders(
117
- options: CloudflareAuthOptions = {}
59
+ options: CloudflareAuthOptions | undefined,
118
60
  ): Promise<Record<string, string>> {
119
- // Check for API Token (preferred method)
120
- const apiToken =
121
- options.apiToken?.unencrypted ?? process.env.CLOUDFLARE_API_TOKEN;
122
- if (apiToken) {
61
+ if (isCloudflareApiKeyAuthOptions(options)) {
62
+ // Global API Key
123
63
  return {
124
- Authorization: `Bearer ${apiToken}`,
64
+ "X-Auth-Key": options.apiKey.unencrypted,
65
+ "X-Auth-Email": options.email,
125
66
  };
126
- }
127
-
128
- // Check for API Key
129
- const apiKey = options.apiKey?.unencrypted ?? process.env.CLOUDFLARE_API_KEY;
130
- if (apiKey) {
67
+ } else if (isCloudflareApiTokenAuthOptions(options)) {
68
+ // API Token
131
69
  return {
132
- "X-Auth-Key": apiKey,
133
- "X-Auth-Email":
134
- options.email ??
135
- process.env.CLOUDFLARE_EMAIL ??
136
- (await getUserEmailFromApiKey(apiKey)),
70
+ Authorization: `Bearer ${options.apiToken?.unencrypted}`,
137
71
  };
138
72
  }
139
-
73
+ // Wrangler OAuth Token
140
74
  const authConfig = await getRefreshedAuthConfig();
141
75
  if (authConfig.oauth_token) {
142
76
  return {
143
77
  Authorization: `Bearer ${authConfig.oauth_token}`,
144
78
  };
145
79
  }
146
-
147
80
  throw new Error(
148
- "Cloudflare authentication required. Did you forget to login with `wrangler login` or set CLOUDFLARE_API_TOKEN, CLOUDFLARE_API_KEY, or CLOUDFLARE_EMAIL, or CLOUDFLARE_API_KEY?"
81
+ "Cloudflare authentication required. Did you forget to login with `wrangler login` or set CLOUDFLARE_API_TOKEN, CLOUDFLARE_API_KEY?",
149
82
  );
150
83
  }
151
84
 
152
- const emailCache: Record<string, string> = {};
153
-
154
- async function getUserEmailFromApiKey(apiKey: string): Promise<string> {
155
- if (emailCache[apiKey]) {
156
- return emailCache[apiKey];
157
- }
158
-
159
- try {
160
- const baseUrl = "https://api.cloudflare.com/client/v4";
161
-
162
- // Call the /user endpoint to get user information
163
- const response = await fetch(`${baseUrl}/user`, {
164
- headers: {
165
- "Content-Type": "application/json",
166
- "X-Auth-Key": apiKey,
167
- },
168
- });
169
-
170
- if (!response.ok) {
171
- throw new Error(
172
- `Failed to get user information: ${response.status} ${response.statusText}`
173
- );
174
- }
175
-
176
- const data = (await response.json()) as {
177
- success: boolean;
178
- result: {
179
- id: string;
180
- email: string;
181
- name: string;
182
- [key: string]: any;
183
- };
184
- };
185
-
186
- if (!data.success || !data.result || !data.result.email) {
187
- throw new Error("Cloudflare API did not return valid user information");
188
- }
189
-
190
- emailCache[apiKey] = data.result.email;
191
- return data.result.email;
192
- } catch (error) {
193
- console.error("Error retrieving email from Cloudflare API:", error);
194
- throw new Error(
195
- "Failed to automatically discover email for API Key authentication"
196
- );
197
- }
198
- }
199
-
200
85
  async function refreshAuthToken(
201
- options: WranglerConfig
86
+ options: WranglerConfig,
202
87
  ): Promise<WranglerConfig> {
203
88
  const response = await fetch("https://dash.cloudflare.com/oauth2/token", {
204
89
  method: "POST",
@@ -214,7 +99,7 @@ async function refreshAuthToken(
214
99
 
215
100
  if (!response.ok) {
216
101
  throw new Error(
217
- `Failed to refresh auth token: ${response.status} ${response.statusText}`
102
+ `Failed to refresh auth token: ${response.status} ${response.statusText}`,
218
103
  );
219
104
  }
220
105
 
@@ -226,7 +111,7 @@ async function refreshAuthToken(
226
111
  options.oauth_token = data.access_token;
227
112
  options.refresh_token = data.refresh_token;
228
113
  options.expiration_time = new Date(
229
- Date.now() + data.expires_in * 1000
114
+ Date.now() + data.expires_in * 1000,
230
115
  ).toISOString();
231
116
  options.scopes = data.scope?.split(" ") || [];
232
117
 
@@ -280,7 +165,7 @@ async function readWranglerConfig(): Promise<WranglerConfig> {
280
165
  const configPath = await findWranglerConfig();
281
166
  try {
282
167
  const config = (authConfigCache[configPath] ??= await parseTOML(
283
- await fs.readFile(configPath, "utf-8")
168
+ await fs.readFile(configPath, "utf-8"),
284
169
  ));
285
170
  config.path = configPath;
286
171
 
@@ -307,7 +192,7 @@ async function findWranglerConfig(): Promise<string> {
307
192
  const environment = process.env.WRANGLER_API_ENVIRONMENT ?? "production";
308
193
  const filePath = path.join(
309
194
  "config",
310
- `${environment === "production" ? "default.toml" : `${environment}.toml`}`
195
+ `${environment === "production" ? "default.toml" : `${environment}.toml`}`,
311
196
  );
312
197
 
313
198
  const xdgAppPaths = (await import("xdg-app-paths")).default;
@@ -318,7 +203,7 @@ async function findWranglerConfig(): Promise<string> {
318
203
  // Check for the .wrangler directory in root if it is not there then use the XDG compliant path.
319
204
  wranglerConfigPath = path.join(
320
205
  (await isDirectory(legacyConfigDir)) ? legacyConfigDir : configDir,
321
- filePath
206
+ filePath,
322
207
  );
323
208
  return wranglerConfigPath;
324
209
  }