alchemy 0.49.1 → 0.51.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 (92) hide show
  1. package/lib/apply.js +10 -9
  2. package/lib/apply.js.map +1 -1
  3. package/lib/aws/control/types.d.ts +4 -4
  4. package/lib/cloudflare/api-gateway-operation.d.ts +141 -0
  5. package/lib/cloudflare/api-gateway-operation.d.ts.map +1 -0
  6. package/lib/cloudflare/api-gateway-operation.js +153 -0
  7. package/lib/cloudflare/api-gateway-operation.js.map +1 -0
  8. package/lib/cloudflare/api-mitigation.d.ts +19 -0
  9. package/lib/cloudflare/api-mitigation.d.ts.map +1 -0
  10. package/lib/cloudflare/api-mitigation.js +1 -0
  11. package/lib/cloudflare/api-mitigation.js.map +1 -0
  12. package/lib/cloudflare/api-response.d.ts +2 -2
  13. package/lib/cloudflare/api-response.d.ts.map +1 -1
  14. package/lib/cloudflare/api-response.js +2 -1
  15. package/lib/cloudflare/api-response.js.map +1 -1
  16. package/lib/cloudflare/api-schema.d.ts +137 -0
  17. package/lib/cloudflare/api-schema.d.ts.map +1 -0
  18. package/lib/cloudflare/api-schema.js +197 -0
  19. package/lib/cloudflare/api-schema.js.map +1 -0
  20. package/lib/cloudflare/api-shield.d.ts +366 -0
  21. package/lib/cloudflare/api-shield.d.ts.map +1 -0
  22. package/lib/cloudflare/api-shield.js +427 -0
  23. package/lib/cloudflare/api-shield.js.map +1 -0
  24. package/lib/cloudflare/bucket.d.ts +70 -105
  25. package/lib/cloudflare/bucket.d.ts.map +1 -1
  26. package/lib/cloudflare/bucket.js +127 -244
  27. package/lib/cloudflare/bucket.js.map +1 -1
  28. package/lib/cloudflare/certificate-pack.d.ts +1 -1
  29. package/lib/cloudflare/certificate-pack.d.ts.map +1 -1
  30. package/lib/cloudflare/certificate-pack.js +18 -88
  31. package/lib/cloudflare/certificate-pack.js.map +1 -1
  32. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  33. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  34. package/lib/cloudflare/index.d.ts +3 -0
  35. package/lib/cloudflare/index.d.ts.map +1 -1
  36. package/lib/cloudflare/index.js +3 -0
  37. package/lib/cloudflare/index.js.map +1 -1
  38. package/lib/cloudflare/nuxt.js +1 -1
  39. package/lib/cloudflare/nuxt.js.map +1 -1
  40. package/lib/cloudflare/orange.d.ts.map +1 -1
  41. package/lib/cloudflare/orange.js +4 -1
  42. package/lib/cloudflare/orange.js.map +1 -1
  43. package/lib/cloudflare/redwood.d.ts.map +1 -1
  44. package/lib/cloudflare/redwood.js +5 -1
  45. package/lib/cloudflare/redwood.js.map +1 -1
  46. package/lib/cloudflare/vite.d.ts.map +1 -1
  47. package/lib/cloudflare/vite.js +2 -10
  48. package/lib/cloudflare/vite.js.map +1 -1
  49. package/lib/cloudflare/website.d.ts.map +1 -1
  50. package/lib/cloudflare/website.js +1 -0
  51. package/lib/cloudflare/website.js.map +1 -1
  52. package/lib/cloudflare/worker.d.ts.map +1 -1
  53. package/lib/cloudflare/worker.js +1 -0
  54. package/lib/cloudflare/worker.js.map +1 -1
  55. package/lib/cloudflare/zone.d.ts +12 -0
  56. package/lib/cloudflare/zone.d.ts.map +1 -1
  57. package/lib/cloudflare/zone.js +35 -0
  58. package/lib/cloudflare/zone.js.map +1 -1
  59. package/lib/destroy.d.ts.map +1 -1
  60. package/lib/destroy.js +26 -18
  61. package/lib/destroy.js.map +1 -1
  62. package/lib/os/exec.d.ts +1 -1
  63. package/lib/os/exec.d.ts.map +1 -1
  64. package/lib/os/exec.js +1 -1
  65. package/lib/os/exec.js.map +1 -1
  66. package/lib/scope.d.ts.map +1 -1
  67. package/lib/scope.js +2 -2
  68. package/lib/scope.js.map +1 -1
  69. package/package.json +2 -1
  70. package/src/apply.ts +11 -12
  71. package/src/aws/control/types.ts +4 -4
  72. package/src/cloudflare/api-gateway-operation.ts +340 -0
  73. package/src/cloudflare/api-mitigation.ts +22 -0
  74. package/src/cloudflare/api-response.ts +7 -3
  75. package/src/cloudflare/api-schema.ts +364 -0
  76. package/src/cloudflare/api-shield.ts +676 -0
  77. package/src/cloudflare/bucket.ts +276 -468
  78. package/src/cloudflare/certificate-pack.ts +28 -152
  79. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  80. package/src/cloudflare/index.ts +3 -0
  81. package/src/cloudflare/nuxt.ts +1 -1
  82. package/src/cloudflare/orange.ts +4 -1
  83. package/src/cloudflare/redwood.ts +6 -1
  84. package/src/cloudflare/vite.ts +2 -10
  85. package/src/cloudflare/website.ts +1 -0
  86. package/src/cloudflare/worker.ts +2 -1
  87. package/src/cloudflare/zone.ts +52 -0
  88. package/src/destroy.ts +26 -20
  89. package/src/os/exec.ts +2 -2
  90. package/src/scope.ts +3 -3
  91. package/templates/tanstack-start/package.json +3 -3
  92. package/templates/tanstack-start/vite.config.ts +2 -6
@@ -0,0 +1,427 @@
1
+ import fs from "node:fs/promises";
2
+ import * as yaml from "yaml";
3
+ import { Resource } from "../resource.js";
4
+ import { handleApiError } from "./api-error.js";
5
+ import { APIGatewayOperation } from "./api-gateway-operation.js";
6
+ import { APISchema } from "./api-schema.js";
7
+ import { createCloudflareApi, } from "./api.js";
8
+ import { findZoneForHostname } from "./zone.js";
9
+ /**
10
+ * Cloudflare Schema Validation protects your API endpoints by validating incoming requests
11
+ * against an OpenAPI v3 schema. It can log or block requests that don't match your schema,
12
+ * helping prevent malformed requests and potential security issues.
13
+ *
14
+ * @example
15
+ * ## Basic schema validation with inline YAML
16
+ *
17
+ * Enable schema validation with a simple OpenAPI schema as YAML string
18
+ *
19
+ * const apiSchema = await APISchema("my-schema", {
20
+ * zone: myZone,
21
+ * schema: `
22
+ * openapi: 3.0.0
23
+ * info:
24
+ * title: My API
25
+ * version: 1.0.0
26
+ * servers:
27
+ * - url: https://api.example.com
28
+ * paths:
29
+ * /users:
30
+ * get:
31
+ * operationId: getUsers
32
+ * responses:
33
+ * '200':
34
+ * description: Success
35
+ * /users/{id}:
36
+ * get:
37
+ * operationId: getUser
38
+ * parameters:
39
+ * - name: id
40
+ * in: path
41
+ * required: true
42
+ * schema:
43
+ * type: string
44
+ * `,
45
+ * });
46
+ *
47
+ * const shield = await APIShield("api-validation", {
48
+ * zone: myZone,
49
+ * schema: apiSchema,
50
+ * defaultAction: "none"
51
+ * });
52
+ *
53
+ * @example
54
+ * ## API Shield with typed OpenAPI object
55
+ *
56
+ * Use strongly-typed OpenAPI v3 objects for better IDE support
57
+ *
58
+ * import type { OpenAPIV3 } from "openapi-types";
59
+ *
60
+ * const apiSchema: OpenAPIV3.Document = {
61
+ * openapi: "3.0.0",
62
+ * info: {
63
+ * title: "My API",
64
+ * version: "1.0.0",
65
+ * },
66
+ * servers: [
67
+ * { url: "https://api.example.com" }
68
+ * ],
69
+ * paths: {
70
+ * "/users": {
71
+ * get: {
72
+ * operationId: "getUsers",
73
+ * responses: {
74
+ * "200": {
75
+ * description: "Success",
76
+ * content: {
77
+ * "application/json": {
78
+ * schema: {
79
+ * type: "array",
80
+ * items: {
81
+ * type: "object",
82
+ * properties: {
83
+ * id: { type: "string" },
84
+ * name: { type: "string" }
85
+ * }
86
+ * }
87
+ * }
88
+ * }
89
+ * }
90
+ * }
91
+ * }
92
+ * }
93
+ * }
94
+ * }
95
+ * };
96
+ *
97
+ * const schema = await APISchema("my-schema", {
98
+ * zone: myZone,
99
+ * schema: apiSchema,
100
+ * });
101
+ *
102
+ * const shield = await APIShield("api-validation", {
103
+ * zone: myZone,
104
+ * schema: schema,
105
+ * defaultAction: "none"
106
+ * });
107
+ *
108
+ * @example
109
+ * ## API Shield with file
110
+ *
111
+ * Load schema from an external file with custom settings
112
+ *
113
+ * const schema = await APISchema("my-schema", {
114
+ * zone: "example.com",
115
+ * schema: new URL("file:///path/to/openapi.yaml"),
116
+ * name: "production-api-v2",
117
+ * });
118
+ *
119
+ * const shield = await APIShield("api-validation", {
120
+ * zone: "example.com",
121
+ * schema: schema,
122
+ * defaultAction: "none",
123
+ * mitigations: {
124
+ * "/users": {
125
+ * get: "none", // No mitigation for read operations
126
+ * post: "log", // Log violations for writes (requires paid plan)
127
+ * },
128
+ * "/users/{id}": {
129
+ * delete: "block" // Block destructive operations (requires paid plan)
130
+ * }
131
+ * },
132
+ * unknownOperationAction: "none"
133
+ * });
134
+ *
135
+ * @example
136
+ * ## Monitor API traffic without impact
137
+ *
138
+ * Use validation in monitoring mode to understand traffic patterns
139
+ *
140
+ * const schema = await APISchema("my-schema", {
141
+ * zone: myZone,
142
+ * schema: new URL("file:///path/to/api-schema.json"),
143
+ * });
144
+ *
145
+ * const monitoring = await APIShield("api-monitoring", {
146
+ * zone: myZone,
147
+ * schema: schema,
148
+ * defaultAction: "none"
149
+ * });
150
+ *
151
+ * @example
152
+ * ## Log schema violations
153
+ *
154
+ * Track non-compliant requests without blocking (requires paid plan)
155
+ *
156
+ * const schema = await APISchema("my-schema", {
157
+ * zone: myZone,
158
+ * schema: new URL("file:///path/to/api-schema.json"),
159
+ * });
160
+ *
161
+ * const withLogging = await APIShield("api-logging", {
162
+ * zone: myZone,
163
+ * schema: schema,
164
+ * defaultAction: "log"
165
+ * });
166
+ *
167
+ * @example
168
+ * ## Protect critical endpoints with blanket mitigations
169
+ *
170
+ * Apply mitigations to entire paths or specific methods (requires paid plan)
171
+ *
172
+ * const schema = await APISchema("my-schema", {
173
+ * zone: myZone,
174
+ * schema: new URL("file:///path/to/api-schema.json"),
175
+ * });
176
+ *
177
+ * const protection = await APIShield("api-protection", {
178
+ * zone: myZone,
179
+ * schema: schema,
180
+ * defaultAction: "log",
181
+ * mitigations: {
182
+ * "/admin": "block", // Block all methods on admin endpoints
183
+ * "/payments": {
184
+ * post: "block", // Block payment creation
185
+ * put: "block" // Block payment updates
186
+ * },
187
+ * "/users/{id}": {
188
+ * delete: "block" // Block user deletion
189
+ * },
190
+ * "/public": "none", // Allow all methods on public endpoints
191
+ * "/products": "none" // Allow all methods on products
192
+ * }
193
+ * });
194
+ *
195
+ * @see https://developers.cloudflare.com/api-shield/security/schema-validation/
196
+ */
197
+ export async function APIShield(id, props) {
198
+ return (await _APIShield(id, {
199
+ ...props,
200
+ // resolve file URLs to documents prior to passing input to the resource
201
+ // so that updates to the schema trigger changes to the resource
202
+ schema: await loadSchemaContent(props.schema),
203
+ }));
204
+ }
205
+ const _APIShield = Resource("cloudflare::APIShield", {
206
+ // delete the api gateway operations in parallel
207
+ destroyStrategy: "parallel",
208
+ }, async function (id, props) {
209
+ const api = await createCloudflareApi(props);
210
+ // Resolve zone ID and name
211
+ const zoneId = typeof props.zone === "string"
212
+ ? (await findZoneForHostname(api, props.zone)).zoneId
213
+ : props.zone.id;
214
+ if (this.phase === "delete") {
215
+ // Reset settings to default
216
+ await updateGlobalSettings(api, zoneId, {
217
+ validation_default_mitigation_action: "none",
218
+ });
219
+ return this.destroy();
220
+ }
221
+ // Update global settings
222
+ const defaultAction = props.defaultMitigation || "none";
223
+ await updateGlobalSettings(api, zoneId, {
224
+ validation_default_mitigation_action: defaultAction,
225
+ validation_override_mitigation_action: props.unknownOperationMitigation,
226
+ });
227
+ const schema = await APISchema("schema", {
228
+ schema: props.schema,
229
+ zone: props.zone,
230
+ name: props.name ?? id,
231
+ enabled: props.enabled,
232
+ accountId: props.accountId,
233
+ apiKey: props.apiKey,
234
+ apiToken: props.apiToken,
235
+ baseUrl: props.baseUrl,
236
+ email: props.email,
237
+ });
238
+ return this({
239
+ zoneId,
240
+ schema,
241
+ operations: await Promise.all(parseSchemaOperations(schema.schema).map(async (parsedOp) => {
242
+ let operationAction = defaultAction;
243
+ const method = parsedOp.method.toLowerCase();
244
+ if (props.mitigations) {
245
+ const pathActions = props.mitigations[parsedOp.endpoint];
246
+ if (typeof pathActions === "string") {
247
+ // Blanket action for all methods on this path
248
+ operationAction = pathActions;
249
+ }
250
+ else if (pathActions && typeof pathActions === "object") {
251
+ // Per-method configuration
252
+ const methodAction = pathActions[method];
253
+ if (methodAction) {
254
+ operationAction = methodAction;
255
+ }
256
+ }
257
+ }
258
+ return APIGatewayOperation(
259
+ // Create a deterministic ID for the operation
260
+ `${id}-${method}-${parsedOp.endpoint.replace(/[^a-z0-9]/gi, "-")}`, {
261
+ zone: zoneId,
262
+ endpoint: parsedOp.endpoint,
263
+ host: parsedOp.host,
264
+ method: parsedOp.method,
265
+ mitigation: operationAction,
266
+ });
267
+ })),
268
+ });
269
+ });
270
+ // Helper functions for API calls
271
+ async function getGlobalSettings(api, zoneId) {
272
+ const response = await api.get(`/zones/${zoneId}/api_gateway/settings/schema_validation`);
273
+ if (!response.ok) {
274
+ await handleApiError(response, "getting", "global settings");
275
+ }
276
+ const data = (await response.json());
277
+ return data.result;
278
+ }
279
+ async function updateGlobalSettings(api, zoneId, params) {
280
+ const response = await api.put(`/zones/${zoneId}/api_gateway/settings/schema_validation`, params);
281
+ if (!response.ok) {
282
+ await handleApiError(response, "updating", "global settings");
283
+ }
284
+ }
285
+ /**
286
+ * Get global schema validation settings for a zone
287
+ */
288
+ export async function getGlobalSettingsForZone(api, zoneId) {
289
+ return getGlobalSettings(api, zoneId);
290
+ }
291
+ /**
292
+ * Extract operations from an OpenAPI schema
293
+ */
294
+ export function parseSchemaOperations(schema) {
295
+ const operations = [];
296
+ if (!schema?.paths) {
297
+ return operations;
298
+ }
299
+ // Determine the host from servers
300
+ const defaultHost = extractHostFromSchema(schema);
301
+ // Extract operations from each path
302
+ for (const [path, pathItem] of Object.entries(schema.paths)) {
303
+ if (!pathItem || typeof pathItem !== "object")
304
+ continue;
305
+ // Check each HTTP method
306
+ for (const method of [
307
+ "get",
308
+ "post",
309
+ "put",
310
+ "patch",
311
+ "delete",
312
+ "head",
313
+ "options",
314
+ "trace",
315
+ ]) {
316
+ const operation = pathItem[method];
317
+ if (operation &&
318
+ typeof operation === "object" &&
319
+ !Array.isArray(operation)) {
320
+ // Determine host for this operation (operation-level servers override global)
321
+ const operationHost = operation.servers?.[0]?.url
322
+ ? extractHostFromUrl(operation.servers[0].url)
323
+ : defaultHost;
324
+ operations.push({
325
+ method: method.toUpperCase(),
326
+ endpoint: path,
327
+ operationId: operation.operationId,
328
+ host: operationHost,
329
+ });
330
+ }
331
+ }
332
+ }
333
+ return operations;
334
+ }
335
+ /**
336
+ * Extract host from OpenAPI schema servers
337
+ */
338
+ function extractHostFromSchema(schema) {
339
+ if (schema.servers && schema.servers.length > 0) {
340
+ return extractHostFromUrl(schema.servers[0].url);
341
+ }
342
+ // Fallback to a default host
343
+ return "api.example.com";
344
+ }
345
+ /**
346
+ * Extract hostname from a URL
347
+ */
348
+ function extractHostFromUrl(url) {
349
+ try {
350
+ const urlObj = new URL(url);
351
+ return urlObj.hostname;
352
+ }
353
+ catch {
354
+ // If URL parsing fails, return the URL as-is (might be relative)
355
+ return url;
356
+ }
357
+ }
358
+ /**
359
+ * Helper function to load schema content from various sources
360
+ */
361
+ async function loadSchemaContent(schema) {
362
+ // Handle string content (YAML/JSON)
363
+ try {
364
+ if (typeof schema === "string") {
365
+ if (schema.startsWith("http://") || schema.startsWith("https://")) {
366
+ return loadSchemaContent(new URL(schema));
367
+ }
368
+ else if (schema.includes("\n")) {
369
+ // json or YAML
370
+ return tryParse(schema);
371
+ }
372
+ else {
373
+ try {
374
+ return tryParse(await fs.readFile(schema, "utf-8"));
375
+ }
376
+ catch {
377
+ // maybe minified JSON
378
+ return tryParse(schema);
379
+ }
380
+ }
381
+ }
382
+ else if (schema instanceof URL) {
383
+ return yaml.parse(await fetchUrl(schema));
384
+ }
385
+ else if (typeof schema === "object") {
386
+ return schema;
387
+ }
388
+ else {
389
+ // should be unreachable
390
+ throw SchemaError(new Error(`Unsupported schema type: ${typeof schema}`));
391
+ }
392
+ }
393
+ catch (err) {
394
+ throw SchemaError(err);
395
+ }
396
+ }
397
+ const SchemaError = (cause) => new Error(`Invalid OpenAPI schema. Please provide a:
398
+ 1. a string containing OpenAPI v3.0 schema (in YAML or JSON format)
399
+ 2. a string path to a file containing OpenAPI v3.0 schema (in YAML or JSON format)
400
+ 3. a file://, http:// or https:// URL pointing to an OpenAPI v3.0 schema (in YAML or JSON format)
401
+ 4. a literal OpenAPI v3.0 schema object`, { cause });
402
+ function tryParse(schema) {
403
+ try {
404
+ return yaml.parse(schema);
405
+ }
406
+ catch {
407
+ return JSON.parse(schema);
408
+ }
409
+ }
410
+ async function fetchUrl(url) {
411
+ if (url.protocol === "file:") {
412
+ // Read from local filesystem for file:// URLs
413
+ return await fs.readFile(url.pathname, "utf-8");
414
+ }
415
+ else if (url.protocol === "http:" || url.protocol === "https:") {
416
+ // Fetch from remote for http/https URLs
417
+ const response = await fetch(url.toString());
418
+ if (!response.ok) {
419
+ throw new Error(`Failed to fetch schema from URL: ${response.statusText}`);
420
+ }
421
+ return await response.text();
422
+ }
423
+ else {
424
+ throw new Error(`Unsupported URL protocol: ${url.protocol}. Only http:, https:, and file: are supported.`);
425
+ }
426
+ }
427
+ //# sourceMappingURL=api-shield.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-shield.js","sourceRoot":"","sources":["../../src/cloudflare/api-shield.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,10 @@
1
- import { AwsClient } from "aws4fetch";
2
1
  import { Resource } from "../resource.ts";
3
- import type { Secret } from "../secret.ts";
4
- import { type CloudflareApi } from "./api.ts";
2
+ import { type CloudflareApi, type CloudflareApiOptions } from "./api.ts";
5
3
  import type { Bound } from "./bound.ts";
6
4
  /**
7
5
  * Properties for creating or updating an R2 Bucket
8
6
  */
9
- export interface BucketProps {
7
+ export interface BucketProps extends CloudflareApiOptions {
10
8
  /**
11
9
  * Name of the bucket
12
10
  * Names can only contain lowercase letters (a-z), numbers (0-9), and hyphens (-)
@@ -20,6 +18,11 @@ export interface BucketProps {
20
18
  * Indicates the primary geographical location data will be accessed from
21
19
  */
22
20
  locationHint?: string;
21
+ /**
22
+ * Optional storage class for the bucket
23
+ * Indicates the storage class for the bucket
24
+ */
25
+ storageClass?: "Standard" | "InfrequentAccess";
23
26
  /**
24
27
  * Optional jurisdiction for the bucket
25
28
  * Determines the regulatory jurisdiction the bucket data falls under
@@ -42,34 +45,14 @@ export interface BucketProps {
42
45
  * @default false
43
46
  */
44
47
  empty?: boolean;
45
- /**
46
- * API Token to use for the bucket
47
- */
48
- apiToken?: Secret;
49
- /**
50
- * API Key to use for the bucket
51
- */
52
- apiKey?: Secret;
53
- /**
54
- * Email to use for the bucket
55
- */
56
- email?: string;
57
- /**
58
- * Account ID to use for the bucket
59
- */
60
- accountId?: string;
61
- /**
62
- * Access Key to use for the bucket
63
- */
64
- accessKey?: Secret;
65
- /**
66
- * Secret Access Key to use for the bucket
67
- */
68
- secretAccessKey?: Secret;
69
48
  /**
70
49
  * Whether to adopt an existing bucket
71
50
  */
72
51
  adopt?: boolean;
52
+ /**
53
+ * CORS rules for the bucket
54
+ */
55
+ cors?: R2BucketCORSRule[];
73
56
  /**
74
57
  * Whether to emulate the bucket locally when Alchemy is running in watch mode.
75
58
  */
@@ -81,6 +64,37 @@ export interface BucketProps {
81
64
  remote?: boolean;
82
65
  };
83
66
  }
67
+ interface R2BucketCORSRule {
68
+ /**
69
+ * Identifier for this rule.
70
+ */
71
+ id?: string;
72
+ /**
73
+ * Object specifying allowed origins, methods and headers for this CORS rule.
74
+ */
75
+ allowed: {
76
+ /**
77
+ * Specifies the value for the Access-Control-Allow-Methods header R2 sets when requesting objects in a bucket from a browser.
78
+ */
79
+ methods: ("GET" | "PUT" | "POST" | "DELETE" | "HEAD")[];
80
+ /**
81
+ * Specifies the value for the Access-Control-Allow-Origin header R2 sets when requesting objects in a bucket from a browser.
82
+ */
83
+ origins: string[];
84
+ /**
85
+ * Specifies the value for the Access-Control-Allow-Headers header R2 sets when requesting objects in this bucket from a browser. Cross-origin requests that include custom headers (e.g. x-user-id) should specify these headers as AllowedHeaders.
86
+ */
87
+ headers?: string[];
88
+ };
89
+ /**
90
+ * Specifies the headers that can be exposed back, and accessed by, the JavaScript making the cross-origin request. If you need to access headers beyond the safelisted response headers, such as Content-Encoding or cf-cache-status, you must specify it here.
91
+ */
92
+ exposeHeaders?: string[];
93
+ /**
94
+ * Specifies the amount of time (in seconds) browsers are allowed to cache CORS preflight responses. Browsers may limit this to 2 hours or less, even if the maximum value (86400) is specified.
95
+ */
96
+ maxAgeSeconds?: number;
97
+ }
84
98
  /**
85
99
  * Output returned after R2 Bucket creation/update
86
100
  */
@@ -101,6 +115,10 @@ export type R2BucketResource = Resource<"cloudflare::R2Bucket"> & Omit<BucketPro
101
115
  * Time at which the bucket was created
102
116
  */
103
117
  creationDate: Date;
118
+ /**
119
+ * The `r2.dev` subdomain for the bucket, if `allowPublicAccess` is true
120
+ */
121
+ domain: string | undefined;
104
122
  };
105
123
  export declare function isBucket(resource: Resource): resource is R2BucketResource;
106
124
  export type R2Bucket = R2BucketResource & Bound<R2BucketResource>;
@@ -150,40 +168,15 @@ export type R2Bucket = R2BucketResource & Bound<R2BucketResource>;
150
168
  */
151
169
  export declare function R2Bucket(name: string, props?: BucketProps): Promise<R2Bucket>;
152
170
  /**
153
- * Configuration for R2 client to connect to Cloudflare R2
171
+ * The bucket information returned from the Cloudflare REST API
172
+ * @see https://developers.cloudflare.com/api/node/resources/r2/subresources/buckets/models/bucket/#(schema)
154
173
  */
155
- export interface R2ClientConfig {
156
- accountId: string;
157
- accessKeyId?: Secret;
158
- secretAccessKey?: Secret;
159
- jurisdiction?: string;
160
- }
161
- type R2Client = AwsClient & {
162
- accountId: string;
163
- };
164
- /**
165
- * Creates an aws4fetch client configured for Cloudflare R2
166
- *
167
- * @see https://developers.cloudflare.com/r2/examples/aws/aws-sdk-js-v3/
168
- */
169
- export declare function createR2Client(config?: R2ClientConfig): Promise<R2Client>;
170
- interface CloudflareBucketResponse {
171
- /**
172
- * The bucket information returned from the Cloudflare REST API
173
- * @see https://developers.cloudflare.com/api/node/resources/r2/subresources/buckets/models/bucket/#(schema)
174
- */
175
- result: {
176
- creation_date: string;
177
- location?: "apac" | "eeur" | "enam" | "weur" | "wnam" | "oc";
178
- name: string;
179
- storage_class?: "Standard" | "InfrequentAccess";
180
- };
181
- success: boolean;
182
- errors: Array<{
183
- code: number;
184
- message: string;
185
- }>;
186
- messages: string[];
174
+ interface R2BucketResult {
175
+ creation_date: string;
176
+ location: "apac" | "eeur" | "enam" | "weur" | "wnam" | "oc";
177
+ name: string;
178
+ storage_class: "Standard" | "InfrequentAccess";
179
+ jurisdiction: "default" | "eu" | "fedramp";
187
180
  }
188
181
  /**
189
182
  * Adds jurisdiction header to the headers object if specified in props
@@ -192,64 +185,32 @@ interface CloudflareBucketResponse {
192
185
  * @param props Props or jurisdiction string
193
186
  * @returns Modified headers object
194
187
  */
195
- export declare function withJurisdiction(headers: Record<string, string>, props: BucketProps | {
188
+ export declare function withJurisdiction(props: {
196
189
  jurisdiction?: string;
197
- } | string | undefined): Record<string, string>;
190
+ }, headers?: Record<string, string>): Record<string, string>;
198
191
  /**
199
192
  * Get a bucket
200
193
  */
201
- export declare function getBucket(api: CloudflareApi, bucketName: string, props?: BucketProps): Promise<CloudflareBucketResponse>;
194
+ export declare function getBucket(api: CloudflareApi, bucketName: string, props?: BucketProps): Promise<R2BucketResult>;
202
195
  /**
203
196
  * Create a new bucket
204
197
  */
205
- export declare function createBucket(api: CloudflareApi, bucketName: string, props?: BucketProps): Promise<CloudflareBucketResponse>;
198
+ export declare function createBucket(api: CloudflareApi, bucketName: string, props?: BucketProps): Promise<R2BucketResult>;
206
199
  /**
207
200
  * Delete a bucket
208
201
  */
209
202
  export declare function deleteBucket(api: CloudflareApi, bucketName: string, props: BucketProps): Promise<void>;
210
203
  /**
211
- * List objects in an R2 bucket
212
- *
213
- * @param r2 R2Client instance
214
- * @param bucketName Name of the bucket
215
- * @param continuationToken Optional token for pagination
216
- * @param jurisdiction Optional jurisdiction for the bucket
217
- * @returns Object containing the list of objects and the next continuation token
218
- */
219
- export declare function listObjects(r2: R2Client, bucketName: string, continuationToken?: string, jurisdiction?: string): Promise<{
220
- objects: {
221
- Key: string;
222
- }[];
223
- continuationToken?: string;
224
- }>;
225
- /**
226
- * Helper function to empty a bucket by deleting all objects
227
- */
228
- export declare function emptyBucket(r2: R2Client, bucketName: string, jurisdiction?: string): Promise<void>;
229
- /**
230
- * Update public access setting for a bucket
231
- *
232
- * This operation is not available through the S3 API for R2,
233
- * so we still use the Cloudflare API directly.
234
- */
235
- export declare function updatePublicAccess(api: CloudflareApi, bucketName: string, allowPublicAccess: boolean, jurisdiction?: string): Promise<void>;
236
- /**
237
- * Set CORS configuration for a bucket using aws4fetch
204
+ * Update the managed domain setting for a bucket
238
205
  */
239
- export declare function setCorsConfiguration(r2: R2Client, bucketName: string, allowedOrigins?: string[], allowedMethods?: string[], allowedHeaders?: string[], maxAgeSeconds?: number, jurisdiction?: string): Promise<void>;
206
+ export declare function putManagedDomain(api: CloudflareApi, bucketName: string, enabled: boolean, jurisdiction?: string): Promise<string | undefined>;
240
207
  /**
241
- * Information about an R2 bucket returned by list operations
208
+ * Returns an async iterable of all object keys in a bucket,
209
+ * handling pagination automatically.
242
210
  */
243
- export interface R2BucketInfo {
244
- /**
245
- * Name of the bucket
246
- */
247
- Name: string;
248
- /**
249
- * Creation date of the bucket
250
- */
251
- CreationDate: Date;
252
- }
211
+ export declare function listObjects(api: CloudflareApi, bucketName: string, props: {
212
+ jurisdiction?: string;
213
+ }, cursor?: string): AsyncGenerator<string>;
253
214
  /**
254
215
  * List all R2 buckets in an account
255
216
  *
@@ -263,6 +224,10 @@ export declare function listBuckets(api: CloudflareApi, options?: {
263
224
  cursor?: string;
264
225
  direction?: "asc" | "desc";
265
226
  jurisdiction?: string;
266
- }): Promise<R2BucketInfo[]>;
227
+ }): Promise<{
228
+ name: string;
229
+ creation_date: string;
230
+ }[]>;
231
+ export declare function putBucketCORS(api: CloudflareApi, bucketName: string, props: BucketProps): Promise<void>;
267
232
  export {};
268
233
  //# sourceMappingURL=bucket.d.ts.map