alchemy 0.3.1 → 0.4.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 (113) hide show
  1. package/README.md +8 -3
  2. package/lib/ai/document.d.ts +12 -1
  3. package/lib/ai/document.js +10 -1
  4. package/lib/ai/index.d.ts +0 -2
  5. package/lib/ai/index.js +0 -2
  6. package/lib/alchemy.d.ts +5 -0
  7. package/lib/alchemy.js +44 -1
  8. package/lib/apply.js +17 -10
  9. package/lib/aws/account-id.d.ts +4 -1
  10. package/lib/aws/account-id.js +1 -1
  11. package/lib/aws/credentials.d.ts +5 -0
  12. package/lib/aws/credentials.js +0 -0
  13. package/lib/aws/oidc/oidc-provider.js +2 -2
  14. package/lib/aws/role.js +35 -23
  15. package/lib/cloudflare/account-api-token.d.ts +168 -0
  16. package/lib/cloudflare/account-api-token.js +179 -0
  17. package/lib/cloudflare/account-id.d.ts +1 -0
  18. package/lib/cloudflare/account-id.js +0 -0
  19. package/lib/cloudflare/api.d.ts +38 -1
  20. package/lib/cloudflare/api.js +55 -4
  21. package/lib/cloudflare/assets.d.ts +74 -0
  22. package/lib/cloudflare/assets.js +73 -0
  23. package/lib/cloudflare/bindings.d.ts +6 -1
  24. package/lib/cloudflare/bindings.js +6 -0
  25. package/lib/cloudflare/bucket.d.ts +118 -3
  26. package/lib/cloudflare/bucket.js +269 -89
  27. package/lib/cloudflare/custom-domain.d.ts +75 -0
  28. package/lib/cloudflare/custom-domain.js +154 -0
  29. package/lib/cloudflare/{dns.js → dns-records.js} +14 -1
  30. package/lib/cloudflare/index.d.ts +6 -1
  31. package/lib/cloudflare/index.js +6 -1
  32. package/lib/cloudflare/permission-groups.d.ts +78 -0
  33. package/lib/cloudflare/permission-groups.js +48 -0
  34. package/lib/cloudflare/r2-rest-state-store.d.ts +2 -1
  35. package/lib/cloudflare/r2-rest-state-store.js +55 -24
  36. package/lib/cloudflare/static-site.d.ts +17 -18
  37. package/lib/cloudflare/static-site.js +19 -10
  38. package/lib/cloudflare/worker.d.ts +14 -0
  39. package/lib/cloudflare/worker.js +150 -3
  40. package/lib/{util/encrypt.d.ts → encrypt.d.ts} +1 -1
  41. package/lib/{util/encrypt.js → encrypt.js} +1 -1
  42. package/lib/fs/file-system-state-store.js +1 -1
  43. package/lib/fs/file.d.ts +18 -0
  44. package/lib/fs/file.js +6 -0
  45. package/lib/internal/{providers.d.ts → docs/providers.d.ts} +10 -4
  46. package/lib/internal/docs/providers.js +196 -0
  47. package/lib/secret.d.ts +3 -0
  48. package/lib/secret.js +13 -0
  49. package/lib/{util/serde.d.ts → serde.d.ts} +1 -1
  50. package/lib/{util/serde.js → serde.js} +12 -4
  51. package/lib/test/bun.js +3 -2
  52. package/lib/util/sha256.d.ts +1 -0
  53. package/lib/util/sha256.js +4 -0
  54. package/lib/web/vitepress/config.d.ts +2 -1
  55. package/lib/web/vitepress/config.js +1 -0
  56. package/lib/web/vitepress/index.d.ts +1 -0
  57. package/lib/web/vitepress/index.js +1 -0
  58. package/lib/web/vitepress/process-front-matter-files.d.ts +18 -0
  59. package/lib/web/vitepress/process-front-matter-files.js +68 -0
  60. package/lib/web/vitepress/vitepress.js +3 -2
  61. package/package.json +8 -4
  62. package/src/ai/document.ts +26 -2
  63. package/src/ai/index.ts +0 -2
  64. package/src/alchemy.ts +56 -2
  65. package/src/apply.ts +21 -20
  66. package/src/aws/account-id.ts +6 -2
  67. package/src/aws/credentials.ts +6 -0
  68. package/src/aws/oidc/oidc-provider.ts +17 -17
  69. package/src/aws/role.ts +68 -54
  70. package/src/cloudflare/account-api-token.ts +365 -0
  71. package/src/cloudflare/account-id.ts +0 -0
  72. package/src/cloudflare/api.ts +88 -17
  73. package/src/cloudflare/assets.ts +155 -0
  74. package/src/cloudflare/bindings.ts +11 -2
  75. package/src/cloudflare/bucket.ts +493 -133
  76. package/src/cloudflare/custom-domain.ts +318 -0
  77. package/src/cloudflare/{dns.ts → dns-records.ts} +36 -22
  78. package/src/cloudflare/index.ts +6 -1
  79. package/src/cloudflare/permission-groups.ts +137 -0
  80. package/src/cloudflare/r2-rest-state-store.ts +87 -42
  81. package/src/cloudflare/static-site.ts +20 -32
  82. package/src/cloudflare/worker.ts +253 -5
  83. package/src/dns/import-dns.ts +4 -4
  84. package/src/{util/encrypt.ts → encrypt.ts} +7 -10
  85. package/src/fs/file-system-state-store.ts +5 -5
  86. package/src/fs/file.ts +29 -0
  87. package/src/internal/docs/providers.ts +281 -0
  88. package/src/secret.ts +16 -0
  89. package/src/{util/serde.ts → serde.ts} +16 -10
  90. package/src/test/bun.ts +8 -7
  91. package/src/util/sha256.ts +5 -0
  92. package/src/web/vitepress/config.ts +3 -1
  93. package/src/web/vitepress/index.ts +1 -0
  94. package/src/web/vitepress/process-front-matter-files.ts +98 -0
  95. package/src/web/vitepress/vitepress.ts +3 -2
  96. package/lib/ai/approve.d.ts +0 -99
  97. package/lib/ai/approve.js +0 -76
  98. package/lib/ai/review.d.ts +0 -122
  99. package/lib/ai/review.js +0 -101
  100. package/lib/internal/getting-started.d.ts +0 -21
  101. package/lib/internal/getting-started.js +0 -87
  102. package/lib/internal/index.d.ts +0 -3
  103. package/lib/internal/index.js +0 -3
  104. package/lib/internal/providers.js +0 -172
  105. package/lib/internal/tutorial.d.ts +0 -104
  106. package/lib/internal/tutorial.js +0 -251
  107. package/src/ai/approve.ts +0 -163
  108. package/src/ai/review.ts +0 -213
  109. package/src/internal/getting-started.ts +0 -115
  110. package/src/internal/index.ts +0 -3
  111. package/src/internal/providers.ts +0 -241
  112. package/src/internal/tutorial.ts +0 -392
  113. /package/lib/cloudflare/{dns.d.ts → dns-records.d.ts} +0 -0
@@ -0,0 +1,365 @@
1
+ import { alchemy } from "../alchemy";
2
+ import type { Context } from "../context";
3
+ import { Resource } from "../resource";
4
+ import { Secret } from "../secret";
5
+ import { sha256 } from "../util/sha256";
6
+ import { createCloudflareApi } from "./api";
7
+
8
+ /**
9
+ * Permission group for a token policy
10
+ */
11
+ export interface TokenPolicyPermissionGroup {
12
+ /**
13
+ * ID of the permission group
14
+ */
15
+ id: string;
16
+
17
+ /**
18
+ * Optional metadata for the permission group
19
+ */
20
+ meta?: Record<string, any>;
21
+ }
22
+
23
+ /**
24
+ * Policy that defines what the token can access
25
+ */
26
+ export interface TokenPolicy {
27
+ /**
28
+ * Effect of the policy
29
+ */
30
+ effect: "allow" | "deny";
31
+
32
+ /**
33
+ * Permission groups to include in the policy
34
+ */
35
+ permissionGroups: TokenPolicyPermissionGroup[];
36
+
37
+ /**
38
+ * Resources the policy applies to
39
+ */
40
+ resources: Record<string, string>;
41
+ }
42
+
43
+ /**
44
+ * Condition for token usage (e.g., IP restrictions)
45
+ */
46
+ export interface TokenCondition {
47
+ /**
48
+ * IP address conditions
49
+ */
50
+ requestIp?: {
51
+ /**
52
+ * IP ranges to allow
53
+ */
54
+ in?: string[];
55
+
56
+ /**
57
+ * IP ranges to deny
58
+ */
59
+ notIn?: string[];
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Properties for creating or updating an Account API Token
65
+ */
66
+ export interface AccountApiTokenProps {
67
+ /**
68
+ * Name of the token
69
+ */
70
+ name: string;
71
+
72
+ /**
73
+ * Policies that define what the token can access
74
+ */
75
+ policies: TokenPolicy[];
76
+
77
+ /**
78
+ * Optional expiration date for the token (ISO format)
79
+ */
80
+ expiresOn?: string;
81
+
82
+ /**
83
+ * Optional "not before" date (token is not valid before this date) (ISO format)
84
+ */
85
+ notBefore?: string;
86
+
87
+ /**
88
+ * Optional conditions for token use (like IP restrictions)
89
+ */
90
+ condition?: TokenCondition;
91
+ }
92
+
93
+ /**
94
+ * Cloudflare API token format as returned by the API
95
+ */
96
+ interface CloudflareApiToken {
97
+ id: string;
98
+ name: string;
99
+ status: string;
100
+ policies: {
101
+ effect: "allow" | "deny";
102
+ permission_groups: {
103
+ id: string;
104
+ meta: Record<string, any>;
105
+ }[];
106
+ resources: Record<string, string>;
107
+ }[];
108
+ expires_on?: string;
109
+ not_before?: string;
110
+ condition?: {
111
+ request_ip?: {
112
+ in?: string[];
113
+ not_in?: string[];
114
+ };
115
+ };
116
+ value?: string;
117
+ }
118
+
119
+ /**
120
+ * Output returned after Account API Token creation/update
121
+ */
122
+ export interface AccountApiToken
123
+ extends Resource<"cloudflare::AccountApiToken">,
124
+ AccountApiTokenProps {
125
+ /**
126
+ * The ID of the token
127
+ *
128
+ * Equiv. to ACCESS_KEY_ID
129
+ */
130
+ id: string;
131
+
132
+ /**
133
+ * Status of the token
134
+ */
135
+ status: string;
136
+
137
+ /**
138
+ * Actual token value (only available on creation)
139
+ * Stored as a Secret for security
140
+ *
141
+ * Equiv. to SECRET_ACCESS_KEY
142
+ */
143
+ value?: Secret;
144
+
145
+ /**
146
+ * Access key ID for the token
147
+ *
148
+ * An alias of {@link id}
149
+ */
150
+ accessKeyId: string;
151
+
152
+ /**
153
+ * Secret access key for the token
154
+ *
155
+ * The SHA-256 hash of the token {@link value}
156
+ *
157
+ * @see https://developers.cloudflare.com/r2/api/tokens/#get-s3-api-credentials-from-an-api-token
158
+ */
159
+ secretAccessKey: string;
160
+ }
161
+
162
+ /**
163
+ * Creates a Cloudflare Account API Token with specified permissions.
164
+ *
165
+ * @see https://developers.cloudflare.com/api/resources/accounts/subresources/tokens/methods/create/
166
+ *
167
+ * @example
168
+ * // First, fetch all permission groups
169
+ * const permissions = await PermissionGroups("cloudflare-permissions", {
170
+ * accountId: cfAccountId,
171
+ * });
172
+ *
173
+ * // Create a token with read-only permissions for specific zones
174
+ * const readOnlyToken = await AccountApiToken("readonly-token", {
175
+ * name: "Readonly Zone Token",
176
+ * policies: [
177
+ * {
178
+ * effect: "allow",
179
+ * permissionGroups: [
180
+ * { id: permissions["Zone Read"].id },
181
+ * { id: permissions["Analytics Read"].id }
182
+ * ],
183
+ * resources: {
184
+ * "com.cloudflare.api.account.zone.22b1de5f1c0e4b3ea97bb1e963b06a43": "*",
185
+ * "com.cloudflare.api.account.zone.eb78d65290b24279ba6f44721b3ea3c4": "*"
186
+ * }
187
+ * }
188
+ * ],
189
+ * expiresOn: "2024-12-31T23:59:59Z"
190
+ * });
191
+ *
192
+ * @example
193
+ * // Create a token with time and IP restrictions
194
+ * const restrictedToken = await AccountApiToken("restricted-token", {
195
+ * name: "Restricted Access Token",
196
+ * policies: [
197
+ * {
198
+ * effect: "allow",
199
+ * permissionGroups: [
200
+ * { id: permissions["Worker Routes Edit"].id }
201
+ * ],
202
+ * resources: {
203
+ * "com.cloudflare.api.account.worker.route.*": "*"
204
+ * }
205
+ * }
206
+ * ],
207
+ * notBefore: "2023-01-01T00:00:00Z",
208
+ * expiresOn: "2023-12-31T23:59:59Z",
209
+ * condition: {
210
+ * requestIp: {
211
+ * in: ["192.168.1.0/24", "10.0.0.0/8"],
212
+ * notIn: ["192.168.1.100/32"]
213
+ * }
214
+ * }
215
+ * });
216
+ */
217
+ export const AccountApiToken = Resource(
218
+ "cloudflare::AccountApiToken",
219
+ async function (
220
+ this: Context<AccountApiToken>,
221
+ id: string,
222
+ props: AccountApiTokenProps
223
+ ): Promise<AccountApiToken> {
224
+ // Create Cloudflare API client with automatic account discovery
225
+ const api = await createCloudflareApi();
226
+
227
+ if (this.phase === "delete") {
228
+ // Delete token if we have an ID
229
+ if (this.output?.id) {
230
+ try {
231
+ const deleteResponse = await api.delete(
232
+ `/accounts/${api.accountId}/tokens/${this.output.id}`
233
+ );
234
+
235
+ if (!deleteResponse.ok && deleteResponse.status !== 404) {
236
+ const errorData: any = await deleteResponse.json().catch(() => ({
237
+ errors: [{ message: deleteResponse.statusText }],
238
+ }));
239
+ console.error(`Error deleting token '${props.name}':`, errorData);
240
+ }
241
+ } catch (error) {
242
+ console.error(`Error deleting token '${props.name}':`, error);
243
+ }
244
+ }
245
+
246
+ // Return destroyed state
247
+ return this.destroy();
248
+ }
249
+
250
+ // Transform our properties to API format
251
+ const apiPayload = {
252
+ name: props.name,
253
+ policies: props.policies.map((policy) => ({
254
+ effect: policy.effect,
255
+ permission_groups: policy.permissionGroups.map((pg) => ({
256
+ id: pg.id,
257
+ meta: pg.meta || {},
258
+ })),
259
+ resources: policy.resources,
260
+ })),
261
+ // Format dates for Cloudflare API (removing milliseconds)
262
+ ...(props.expiresOn
263
+ ? { expires_on: formatCloudflareDate(props.expiresOn) }
264
+ : {}),
265
+ ...(props.notBefore
266
+ ? { not_before: formatCloudflareDate(props.notBefore) }
267
+ : {}),
268
+ ...(props.condition
269
+ ? {
270
+ condition: {
271
+ request_ip: props.condition.requestIp
272
+ ? {
273
+ in: props.condition.requestIp.in || [],
274
+ not_in: props.condition.requestIp.notIn || [],
275
+ }
276
+ : undefined,
277
+ },
278
+ }
279
+ : {}),
280
+ };
281
+
282
+ /**
283
+ * Formats a date string for Cloudflare API by removing milliseconds
284
+ * Converts from "2023-01-01T00:00:00.000Z" to "2023-01-01T00:00:00Z"
285
+ */
286
+ function formatCloudflareDate(dateStr: string): string {
287
+ return dateStr.replace(/\.\d{3}Z$/, "Z");
288
+ }
289
+
290
+ let response;
291
+ let tokenValue;
292
+
293
+ if (this.phase === "update" && this.output?.id) {
294
+ // Update existing token
295
+ response = await api.put(
296
+ `/accounts/${api.accountId}/tokens/${this.output.id}`,
297
+ apiPayload
298
+ );
299
+ } else {
300
+ // Create new token
301
+ response = await api.post(
302
+ `/accounts/${api.accountId}/tokens`,
303
+ apiPayload
304
+ );
305
+ }
306
+
307
+ if (!response.ok) {
308
+ const errorData: any = await response.json().catch(() => ({
309
+ errors: [{ message: response.statusText }],
310
+ }));
311
+
312
+ throw new Error(
313
+ `Error ${this.phase === "update" ? "updating" : "creating"} token '${props.name}': ${
314
+ errorData.errors?.[0]?.message || response.statusText
315
+ }`
316
+ );
317
+ }
318
+
319
+ const result: { result: CloudflareApiToken } = await response.json();
320
+ const tokenData = result.result;
321
+
322
+ if (tokenData.value) {
323
+ tokenValue = alchemy.secret(tokenData.value);
324
+ } else {
325
+ if (!this.output?.value) {
326
+ throw new Error(
327
+ `Token '${props.name}' was created but we have no record of its value. Try deleting and recreating the token.`
328
+ );
329
+ }
330
+ tokenValue = this.output?.value;
331
+ }
332
+
333
+ // Transform API response to our format
334
+ return this({
335
+ id: tokenData.id,
336
+ name: tokenData.name,
337
+ status: tokenData.status,
338
+ policies: tokenData.policies.map((policy) => ({
339
+ effect: policy.effect,
340
+ permissionGroups: policy.permission_groups.map((pg) => ({
341
+ id: pg.id,
342
+ meta: pg.meta,
343
+ })),
344
+ resources: policy.resources,
345
+ })),
346
+ ...(tokenData.expires_on ? { expiresOn: tokenData.expires_on } : {}),
347
+ ...(tokenData.not_before ? { notBefore: tokenData.not_before } : {}),
348
+ ...(tokenData.condition
349
+ ? {
350
+ condition: {
351
+ requestIp: tokenData.condition.request_ip
352
+ ? {
353
+ in: tokenData.condition.request_ip.in || [],
354
+ notIn: tokenData.condition.request_ip.not_in || [],
355
+ }
356
+ : undefined,
357
+ },
358
+ }
359
+ : {}),
360
+ value: tokenValue,
361
+ accessKeyId: tokenData.id,
362
+ secretAccessKey: sha256(tokenValue.unencrypted),
363
+ });
364
+ }
365
+ );
File without changes
@@ -1,3 +1,4 @@
1
+ import type { Secret } from "../secret";
1
2
  import { type CloudflareAuthOptions, getCloudflareHeaders } from "./auth";
2
3
 
3
4
  /**
@@ -7,7 +8,7 @@ export interface CloudflareApiOptions {
7
8
  /**
8
9
  * API Key to use (overrides CLOUDFLARE_API_KEY env var)
9
10
  */
10
- apiKey?: string;
11
+ apiKey?: Secret;
11
12
 
12
13
  /**
13
14
  * Account ID to use (overrides CLOUDFLARE_ACCOUNT_ID env var)
@@ -35,6 +36,65 @@ interface CloudflareAccount {
35
36
  name: string;
36
37
  }
37
38
 
39
+ /**
40
+ * Custom error class for Cloudflare API errors
41
+ * Includes HTTP status information from the Response
42
+ */
43
+ export class CloudflareApiError extends Error {
44
+ /**
45
+ * HTTP status code
46
+ */
47
+ status: number;
48
+
49
+ /**
50
+ * HTTP status text
51
+ */
52
+ statusText: string;
53
+
54
+ /**
55
+ * Raw error data from the API
56
+ */
57
+ errorData?: any;
58
+
59
+ /**
60
+ * Create a new CloudflareApiError
61
+ */
62
+ constructor(message: string, response: Response, errorData?: any) {
63
+ super(message);
64
+ this.name = "CloudflareApiError";
65
+ this.status = response.status;
66
+ this.statusText = response.statusText;
67
+ this.errorData = errorData;
68
+
69
+ // Ensure instanceof works correctly
70
+ Object.setPrototypeOf(this, CloudflareApiError.prototype);
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Helper function to handle API errors
76
+ *
77
+ * @param response The fetch Response object
78
+ * @param action The action being performed (e.g., "creating", "deleting")
79
+ * @param resourceType The type of resource being acted upon (e.g., "R2 bucket", "Worker")
80
+ * @param resourceName The name/identifier of the specific resource
81
+ * @returns Never returns - always throws an error
82
+ */
83
+ export async function handleApiError(
84
+ response: Response,
85
+ action: string,
86
+ resourceType: string,
87
+ resourceName: string
88
+ ): Promise<never> {
89
+ const json: any = await response.json();
90
+ console.log(json);
91
+ const errorData: any = json.errors || [{ message: response.statusText }];
92
+
93
+ const errorMessage = `Error ${action} ${resourceType} '${resourceName}': ${errorData.errors?.[0]?.message || response.statusText}`;
94
+
95
+ throw new CloudflareApiError(errorMessage, response, errorData);
96
+ }
97
+
38
98
  /**
39
99
  * Creates a CloudflareApi instance with automatic account ID discovery if not provided
40
100
  *
@@ -42,24 +102,33 @@ interface CloudflareAccount {
42
102
  * @returns Promise resolving to a CloudflareApi instance
43
103
  */
44
104
  export async function createCloudflareApi(
45
- options: CloudflareApiOptions = {},
105
+ options: CloudflareApiOptions = {}
46
106
  ): Promise<CloudflareApi> {
47
107
  try {
48
108
  return new CloudflareApi({
49
109
  ...options,
50
- accountId:
51
- options.accountId ||
52
- process.env.CLOUDFLARE_ACCOUNT_ID ||
53
- (await fetchAccountId()),
110
+ accountId: await CloudflareAccountId(options),
54
111
  });
55
112
  } catch (error) {
56
113
  console.error("Error during Cloudflare account ID discovery:", error);
57
114
  throw new Error(
58
- "Failed to automatically discover Cloudflare account ID. Please provide an account ID explicitly or ensure your API token/key has sufficient permissions.",
115
+ "Failed to automatically discover Cloudflare account ID. Please provide an account ID explicitly or ensure your API token/key has sufficient permissions."
59
116
  );
60
117
  }
61
118
  }
62
119
 
120
+ export type CloudflareAccountId = string & {
121
+ readonly __brand: "CloudflareAccountId";
122
+ };
123
+
124
+ export async function CloudflareAccountId(
125
+ options: CloudflareApiOptions = {}
126
+ ): Promise<CloudflareAccountId> {
127
+ return (options.accountId ||
128
+ process.env.CLOUDFLARE_ACCOUNT_ID ||
129
+ (await fetchAccountId())) as CloudflareAccountId;
130
+ }
131
+
63
132
  /**
64
133
  * Cloudflare API client using raw fetch
65
134
  */
@@ -93,10 +162,12 @@ export class CloudflareApi {
93
162
  this.baseUrl = "https://api.cloudflare.com/client/v4";
94
163
 
95
164
  const apiKey =
96
- options.apiKey || process.env.CLOUDFLARE_API_KEY || undefined;
165
+ options.apiKey?.unencrypted ||
166
+ process.env.CLOUDFLARE_API_KEY ||
167
+ undefined;
97
168
  if (!apiKey) {
98
169
  throw new Error(
99
- "No API key provided. Use createCloudflareApi() instead for automatic account discovery.",
170
+ "No API key provided. Use createCloudflareApi() instead for automatic account discovery."
100
171
  );
101
172
  }
102
173
  this.apiKey = apiKey;
@@ -104,7 +175,7 @@ export class CloudflareApi {
104
175
  this.email = options.email || process.env.CLOUDFLARE_EMAIL;
105
176
  if (!this.email) {
106
177
  throw new Error(
107
- "No email provided. Use createCloudflareApi() instead for automatic account discovery.",
178
+ "No email provided. Use createCloudflareApi() instead for automatic account discovery."
108
179
  );
109
180
  }
110
181
 
@@ -113,7 +184,7 @@ export class CloudflareApi {
113
184
  options.accountId || process.env.CLOUDFLARE_ACCOUNT_ID || "";
114
185
  if (!this.accountId) {
115
186
  throw new Error(
116
- "No account ID provided. Use createCloudflareApi() instead for automatic account discovery.",
187
+ "No account ID provided. Use createCloudflareApi() instead for automatic account discovery."
117
188
  );
118
189
  }
119
190
 
@@ -146,7 +217,7 @@ export class CloudflareApi {
146
217
  // Get auth headers (async now)
147
218
  const authHeaders = await getCloudflareHeaders(
148
219
  contentType,
149
- this.authOptions,
220
+ this.authOptions
150
221
  );
151
222
 
152
223
  // Combine all headers
@@ -195,7 +266,7 @@ export class CloudflareApi {
195
266
  async post(
196
267
  path: string,
197
268
  body: any,
198
- init: RequestInit = {},
269
+ init: RequestInit = {}
199
270
  ): Promise<Response> {
200
271
  const requestBody =
201
272
  body instanceof FormData
@@ -212,7 +283,7 @@ export class CloudflareApi {
212
283
  async put(
213
284
  path: string,
214
285
  body: any,
215
- init: RequestInit = {},
286
+ init: RequestInit = {}
216
287
  ): Promise<Response> {
217
288
  const requestBody = body instanceof FormData ? body : JSON.stringify(body);
218
289
  return this.fetch(path, { ...init, method: "PUT", body: requestBody });
@@ -224,7 +295,7 @@ export class CloudflareApi {
224
295
  async patch(
225
296
  path: string,
226
297
  body: any,
227
- init: RequestInit = {},
298
+ init: RequestInit = {}
228
299
  ): Promise<Response> {
229
300
  return this.fetch(path, {
230
301
  ...init,
@@ -264,7 +335,7 @@ async function fetchAccountId(): Promise<string> {
264
335
  }));
265
336
 
266
337
  throw new Error(
267
- `Error fetching Cloudflare accounts: ${errorData.errors?.[0]?.message || response.statusText}`,
338
+ `Error fetching Cloudflare accounts: ${errorData.errors?.[0]?.message || response.statusText}`
268
339
  );
269
340
  }
270
341
 
@@ -273,7 +344,7 @@ async function fetchAccountId(): Promise<string> {
273
344
 
274
345
  if (!accounts || accounts.length === 0) {
275
346
  throw new Error(
276
- "No Cloudflare accounts found. Check your API token/key permissions.",
347
+ "No Cloudflare accounts found. Check your API token/key permissions."
277
348
  );
278
349
  }
279
350