alchemy 0.68.0 → 0.69.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/alchemy.js +32 -33
- package/bin/commands/init.ts +41 -55
- package/lib/aws/control/properties.d.ts +64 -0
- package/lib/aws/control/properties.d.ts.map +1 -1
- package/lib/aws/control/properties.js +554 -39
- package/lib/aws/control/properties.js.map +1 -1
- package/lib/aws/control/resource.d.ts.map +1 -1
- package/lib/aws/control/resource.js +33 -0
- package/lib/aws/control/resource.js.map +1 -1
- package/lib/aws/control/types.d.ts +5451 -1587
- package/lib/aws/control/types.d.ts.map +1 -1
- package/lib/aws/control/types.js.map +1 -1
- package/lib/aws/control/update-types.d.ts +12913 -0
- package/lib/aws/control/update-types.d.ts.map +1 -0
- package/lib/aws/control/update-types.js +12916 -0
- package/lib/aws/control/update-types.js.map +1 -0
- package/lib/cloudflare/api.d.ts +1 -1
- package/lib/cloudflare/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +9 -10
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/astro/astro.d.ts.map +1 -1
- package/lib/cloudflare/astro/astro.js +3 -3
- package/lib/cloudflare/astro/astro.js.map +1 -1
- package/lib/cloudflare/bucket-object.d.ts +86 -0
- package/lib/cloudflare/bucket-object.d.ts.map +1 -0
- package/lib/cloudflare/bucket-object.js +67 -0
- package/lib/cloudflare/bucket-object.js.map +1 -0
- package/lib/cloudflare/bucket.d.ts +36 -6
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +154 -18
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/container.d.ts.map +1 -1
- package/lib/cloudflare/container.js +2 -2
- package/lib/cloudflare/container.js.map +1 -1
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +44 -12
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -1
- package/lib/cloudflare/next.js.map +1 -1
- package/lib/cloudflare/nuxt/nuxt.d.ts.map +1 -1
- package/lib/cloudflare/nuxt/nuxt.js +3 -3
- package/lib/cloudflare/nuxt/nuxt.js.map +1 -1
- package/lib/cloudflare/tanstack-start/plugin.d.ts +2 -15
- package/lib/cloudflare/tanstack-start/plugin.d.ts.map +1 -1
- package/lib/cloudflare/tanstack-start/plugin.js +5 -25
- package/lib/cloudflare/tanstack-start/plugin.js.map +1 -1
- package/lib/cloudflare/tanstack-start/tanstack-start.d.ts.map +1 -1
- package/lib/cloudflare/tanstack-start/tanstack-start.js +15 -4
- package/lib/cloudflare/tanstack-start/tanstack-start.js.map +1 -1
- package/lib/cloudflare/vite/vite.d.ts.map +1 -1
- package/lib/cloudflare/vite/vite.js +3 -3
- package/lib/cloudflare/vite/vite.js.map +1 -1
- package/lib/cloudflare/website.d.ts +7 -1
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +20 -1
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts +4 -1
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +2 -0
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +6 -0
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +1 -0
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +34 -9
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +12 -1
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/docker/api.d.ts +22 -1
- package/lib/docker/api.d.ts.map +1 -1
- package/lib/docker/api.js +64 -2
- package/lib/docker/api.js.map +1 -1
- package/lib/docker/container.d.ts +88 -0
- package/lib/docker/container.d.ts.map +1 -1
- package/lib/docker/container.js +36 -0
- package/lib/docker/container.js.map +1 -1
- package/lib/llms.cloudflare.txt +2 -2
- package/lib/neon/branch.d.ts +19 -3
- package/lib/neon/branch.d.ts.map +1 -1
- package/lib/neon/branch.js +4 -4
- package/lib/neon/branch.js.map +1 -1
- package/lib/neon/project.d.ts +17 -1
- package/lib/neon/project.d.ts.map +1 -1
- package/lib/neon/project.js +4 -4
- package/lib/neon/project.js.map +1 -1
- package/lib/serde.d.ts +3 -0
- package/lib/serde.d.ts.map +1 -1
- package/lib/serde.js +40 -0
- package/lib/serde.js.map +1 -1
- package/lib/test/bun.js.map +1 -1
- package/package.json +1 -1
- package/src/aws/control/properties.ts +554 -39
- package/src/aws/control/resource.ts +58 -0
- package/src/aws/control/types.ts +85281 -84914
- package/src/aws/control/update-types.ts +12917 -0
- package/src/cloudflare/api.ts +9 -10
- package/src/cloudflare/astro/astro.ts +8 -3
- package/src/cloudflare/bucket-object.ts +127 -0
- package/src/cloudflare/bucket.ts +218 -18
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/container.ts +4 -5
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +57 -13
- package/src/cloudflare/next.ts +2 -2
- package/src/cloudflare/nuxt/nuxt.ts +8 -3
- package/src/cloudflare/tanstack-start/plugin.ts +7 -28
- package/src/cloudflare/tanstack-start/tanstack-start.ts +15 -4
- package/src/cloudflare/vite/vite.ts +8 -3
- package/src/cloudflare/website.ts +29 -2
- package/src/cloudflare/worker-metadata.ts +6 -1
- package/src/cloudflare/worker.ts +7 -0
- package/src/cloudflare/wrangler.json.ts +47 -18
- package/src/docker/api.ts +86 -2
- package/src/docker/container.ts +97 -0
- package/src/llms.cloudflare.txt +2 -2
- package/src/neon/branch.ts +6 -5
- package/src/neon/project.ts +4 -4
- package/src/serde.ts +50 -0
- package/src/test/bun.ts +1 -1
- package/templates/rwsdk/.cursor/rules/alchemy_cloudflare.mdc +2 -2
- package/templates/tanstack-start/.cta.json +13 -0
- package/templates/tanstack-start/README.md +267 -49
- package/templates/tanstack-start/_gitignore +9 -18
- package/templates/tanstack-start/alchemy.run.ts +2 -2
- package/templates/tanstack-start/package.json +24 -19
- package/templates/tanstack-start/public/favicon.ico +0 -0
- package/templates/tanstack-start/public/logo192.png +0 -0
- package/templates/tanstack-start/public/logo512.png +0 -0
- package/templates/tanstack-start/public/manifest.json +25 -0
- package/templates/tanstack-start/public/robots.txt +3 -0
- package/templates/tanstack-start/src/components/Header.tsx +21 -0
- package/templates/tanstack-start/src/logo.svg +12 -0
- package/templates/tanstack-start/src/router.tsx +7 -16
- package/templates/tanstack-start/src/routes/__root.tsx +13 -114
- package/templates/tanstack-start/src/routes/api.demo-names.ts +15 -0
- package/templates/tanstack-start/src/routes/demo.start.api-request.tsx +44 -0
- package/templates/tanstack-start/src/routes/demo.start.server-funcs.tsx +97 -0
- package/templates/tanstack-start/src/routes/index.tsx +31 -4
- package/templates/tanstack-start/src/styles.css +15 -0
- package/templates/tanstack-start/tsconfig.json +19 -22
- package/templates/tanstack-start/types/env.d.ts +2 -2
- package/templates/tanstack-start/vite.config.ts +16 -25
- package/workers/tunnel-proxy.js +1 -1
- package/templates/tanstack-start/_prettierignore +0 -4
- package/templates/tanstack-start/public/android-chrome-192x192.png +0 -0
- package/templates/tanstack-start/public/android-chrome-512x512.png +0 -0
- package/templates/tanstack-start/public/apple-touch-icon.png +0 -0
- package/templates/tanstack-start/public/favicon-16x16.png +0 -0
- package/templates/tanstack-start/public/favicon-32x32.png +0 -0
- package/templates/tanstack-start/public/favicon.png +0 -0
- package/templates/tanstack-start/public/site.webmanifest +0 -19
- package/templates/tanstack-start/src/components/DefaultCatchBoundary.tsx +0 -53
- package/templates/tanstack-start/src/components/NotFound.tsx +0 -25
- package/templates/tanstack-start/src/components/PostError.tsx +0 -5
- package/templates/tanstack-start/src/components/UserError.tsx +0 -5
- package/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-a.tsx +0 -10
- package/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-b.tsx +0 -10
- package/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout.tsx +0 -34
- package/templates/tanstack-start/src/routes/_pathlessLayout.tsx +0 -16
- package/templates/tanstack-start/src/routes/api/users.$userId.ts +0 -28
- package/templates/tanstack-start/src/routes/api/users.ts +0 -59
- package/templates/tanstack-start/src/routes/customScript[.]js.ts +0 -10
- package/templates/tanstack-start/src/routes/deferred.tsx +0 -62
- package/templates/tanstack-start/src/routes/posts.$postId.tsx +0 -34
- package/templates/tanstack-start/src/routes/posts.index.tsx +0 -8
- package/templates/tanstack-start/src/routes/posts.tsx +0 -38
- package/templates/tanstack-start/src/routes/posts_.$postId.deep.tsx +0 -29
- package/templates/tanstack-start/src/routes/redirect.tsx +0 -9
- package/templates/tanstack-start/src/routes/users.$userId.tsx +0 -44
- package/templates/tanstack-start/src/routes/users.index.tsx +0 -18
- package/templates/tanstack-start/src/routes/users.tsx +0 -49
- package/templates/tanstack-start/src/styles/app.css +0 -12
- package/templates/tanstack-start/src/utils/loggingMiddleware.tsx +0 -41
- package/templates/tanstack-start/src/utils/posts.tsx +0 -40
- package/templates/tanstack-start/src/utils/seo.ts +0 -33
- package/templates/tanstack-start/src/utils/users.tsx +0 -5
package/src/cloudflare/api.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Provider, type Credentials } from "../auth.ts";
|
|
2
2
|
import type { Secret } from "../secret.ts";
|
|
3
|
+
import { isBinary } from "../serde.ts";
|
|
3
4
|
import { memoize } from "../util/memoize.ts";
|
|
4
5
|
import { withExponentialBackoff } from "../util/retry.ts";
|
|
5
6
|
import { safeFetch } from "../util/safe-fetch.ts";
|
|
@@ -257,7 +258,7 @@ export class CloudflareApi {
|
|
|
257
258
|
return this.fetch(path, {
|
|
258
259
|
...init,
|
|
259
260
|
method: "POST",
|
|
260
|
-
body: this.toBody(body),
|
|
261
|
+
body: await this.toBody(body),
|
|
261
262
|
});
|
|
262
263
|
}
|
|
263
264
|
|
|
@@ -272,18 +273,16 @@ export class CloudflareApi {
|
|
|
272
273
|
return this.fetch(path, {
|
|
273
274
|
...init,
|
|
274
275
|
method: "PUT",
|
|
275
|
-
body: this.toBody(body),
|
|
276
|
+
body: await this.toBody(body),
|
|
276
277
|
});
|
|
277
278
|
}
|
|
278
279
|
|
|
279
|
-
toBody(body: BodyInit): BodyInit {
|
|
280
|
-
return body instanceof FormData
|
|
280
|
+
async toBody(body: BodyInit): Promise<BodyInit> {
|
|
281
|
+
return body instanceof FormData ||
|
|
282
|
+
typeof body === "string" ||
|
|
283
|
+
isBinary(body)
|
|
281
284
|
? body
|
|
282
|
-
:
|
|
283
|
-
? body
|
|
284
|
-
: body instanceof ReadableStream
|
|
285
|
-
? body
|
|
286
|
-
: JSON.stringify(body);
|
|
285
|
+
: JSON.stringify(body);
|
|
287
286
|
}
|
|
288
287
|
|
|
289
288
|
/**
|
|
@@ -297,7 +296,7 @@ export class CloudflareApi {
|
|
|
297
296
|
return this.fetch(path, {
|
|
298
297
|
...init,
|
|
299
298
|
method: "PATCH",
|
|
300
|
-
body: this.toBody(body),
|
|
299
|
+
body: await this.toBody(body),
|
|
301
300
|
});
|
|
302
301
|
}
|
|
303
302
|
|
|
@@ -3,7 +3,12 @@ import { getPackageManagerRunner } from "../../util/detect-package-manager.ts";
|
|
|
3
3
|
import type { Assets } from "../assets.ts";
|
|
4
4
|
import type { Bindings } from "../bindings.ts";
|
|
5
5
|
import { withSkipPathValidation } from "../miniflare/paths.ts";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
spreadBuildProps,
|
|
8
|
+
spreadDevProps,
|
|
9
|
+
Website,
|
|
10
|
+
type WebsiteProps,
|
|
11
|
+
} from "../website.ts";
|
|
7
12
|
import type { Worker } from "../worker.ts";
|
|
8
13
|
|
|
9
14
|
/**
|
|
@@ -75,8 +80,8 @@ export async function Astro<B extends Bindings>(
|
|
|
75
80
|
return await Website(id, {
|
|
76
81
|
...props,
|
|
77
82
|
noBundle: props.noBundle ?? true,
|
|
78
|
-
build: props
|
|
79
|
-
dev: props
|
|
83
|
+
build: spreadBuildProps(props, `${runner} astro build`),
|
|
84
|
+
dev: spreadDevProps(props, `${runner} astro dev`),
|
|
80
85
|
entrypoint:
|
|
81
86
|
props.entrypoint ??
|
|
82
87
|
(output === "server" ? "dist/_worker.js/index.js" : undefined),
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { R2PutOptions } from "@cloudflare/workers-types/experimental/index.ts";
|
|
2
|
+
import type { Context } from "../context.ts";
|
|
3
|
+
import { Resource } from "../resource.ts";
|
|
4
|
+
import { createCloudflareApi, type CloudflareApiOptions } from "./api.ts";
|
|
5
|
+
import { deleteObject, type PutObjectObject, type R2Bucket } from "./bucket.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Properties for creating or updating an R2 Object
|
|
9
|
+
*/
|
|
10
|
+
export interface R2ObjectProps extends CloudflareApiOptions {
|
|
11
|
+
/**
|
|
12
|
+
* The R2 bucket where the object will be stored
|
|
13
|
+
*/
|
|
14
|
+
bucket: R2Bucket;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The key (path/name) of the object within the bucket
|
|
18
|
+
* Must be a valid object key as defined by R2 storage requirements
|
|
19
|
+
*/
|
|
20
|
+
key: string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The content to store in the object
|
|
24
|
+
* Supports various data types including streams, buffers, strings, and blobs
|
|
25
|
+
*/
|
|
26
|
+
content: PutObjectObject;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The options to use for the object
|
|
30
|
+
*/
|
|
31
|
+
options?: Pick<R2PutOptions, "httpMetadata">;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Output returned after R2 Object creation/update
|
|
36
|
+
*/
|
|
37
|
+
export interface R2Object {
|
|
38
|
+
/**
|
|
39
|
+
* The resource identifier
|
|
40
|
+
*/
|
|
41
|
+
id: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The key (path/name) of the object within the bucket
|
|
45
|
+
*/
|
|
46
|
+
key: string;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The etag of the object
|
|
50
|
+
*/
|
|
51
|
+
etag: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Creates and manages objects within Cloudflare R2 Buckets.
|
|
56
|
+
*
|
|
57
|
+
* R2 Objects represent individual files or data stored within an R2 bucket.
|
|
58
|
+
* This resource provides lifecycle management for object creation, updates, and deletion.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* // Create a simple text object in a bucket
|
|
62
|
+
* const textObject = await R2Object("readme", {
|
|
63
|
+
* bucket: myBucket,
|
|
64
|
+
* key: "docs/README.txt",
|
|
65
|
+
* content: "Welcome to our application!"
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* // Store JSON data as an object
|
|
70
|
+
* const configObject = await R2Object("app-config", {
|
|
71
|
+
* bucket: configBucket,
|
|
72
|
+
* key: "config/app.json",
|
|
73
|
+
* content: JSON.stringify({ version: "1.0.0", debug: false })
|
|
74
|
+
* });
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // Store binary data from a buffer
|
|
78
|
+
* const imageObject = await R2Object("user-avatar", {
|
|
79
|
+
* bucket: assetsBucket,
|
|
80
|
+
* key: "avatars/user-123.png",
|
|
81
|
+
* content: imageBuffer
|
|
82
|
+
* });
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* // Store data from a readable stream
|
|
86
|
+
* const streamObject = await R2Object("upload", {
|
|
87
|
+
* bucket: uploadsBucket,
|
|
88
|
+
* key: "uploads/file.dat",
|
|
89
|
+
* content: readableStream
|
|
90
|
+
* });
|
|
91
|
+
*
|
|
92
|
+
* @see https://developers.cloudflare.com/r2/api/workers/workers-api-reference/
|
|
93
|
+
*/
|
|
94
|
+
export const R2Object = Resource(
|
|
95
|
+
"cloudflare::R2Object",
|
|
96
|
+
async function (
|
|
97
|
+
this: Context<R2Object>,
|
|
98
|
+
id: string,
|
|
99
|
+
props: R2ObjectProps,
|
|
100
|
+
): Promise<R2Object> {
|
|
101
|
+
if (this.phase === "delete") {
|
|
102
|
+
const api = await createCloudflareApi(props);
|
|
103
|
+
// Delete the object from the bucket
|
|
104
|
+
await deleteObject(api, {
|
|
105
|
+
bucketName: props.bucket.name,
|
|
106
|
+
key: props.key,
|
|
107
|
+
});
|
|
108
|
+
return this.destroy();
|
|
109
|
+
} else {
|
|
110
|
+
if (this.phase === "update" && this.output?.key !== props.key) {
|
|
111
|
+
this.replace();
|
|
112
|
+
}
|
|
113
|
+
// Create or update the object in the bucket
|
|
114
|
+
const response = await props.bucket.put(
|
|
115
|
+
props.key,
|
|
116
|
+
props.content,
|
|
117
|
+
props.options,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
id,
|
|
122
|
+
key: props.key,
|
|
123
|
+
etag: response.etag,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
);
|
package/src/cloudflare/bucket.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import type { R2PutOptions } from "@cloudflare/workers-types/experimental/index.ts";
|
|
2
|
+
import * as mf from "miniflare";
|
|
1
3
|
import { isDeepStrictEqual } from "node:util";
|
|
2
4
|
import type { Context } from "../context.ts";
|
|
3
5
|
import { Resource, ResourceKind } from "../resource.ts";
|
|
4
6
|
import { Scope } from "../scope.ts";
|
|
7
|
+
import { streamToBuffer } from "../serde.ts";
|
|
5
8
|
import { isRetryableError } from "../state/r2-rest-state-store.ts";
|
|
6
9
|
import { withExponentialBackoff } from "../util/retry.ts";
|
|
7
10
|
import { CloudflareApiError, handleApiError } from "./api-error.ts";
|
|
@@ -15,6 +18,7 @@ import {
|
|
|
15
18
|
type CloudflareApiOptions,
|
|
16
19
|
} from "./api.ts";
|
|
17
20
|
import { deleteMiniflareBinding } from "./miniflare/delete.ts";
|
|
21
|
+
import { getDefaultPersistPath } from "./miniflare/paths.ts";
|
|
18
22
|
|
|
19
23
|
export type R2BucketJurisdiction = "default" | "eu" | "fedramp";
|
|
20
24
|
|
|
@@ -89,6 +93,11 @@ export interface BucketProps extends CloudflareApiOptions {
|
|
|
89
93
|
*/
|
|
90
94
|
lock?: R2BucketLockRule[];
|
|
91
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Enable data catalog for bucket
|
|
98
|
+
*/
|
|
99
|
+
dataCatalog?: boolean;
|
|
100
|
+
|
|
92
101
|
/**
|
|
93
102
|
* Whether to emulate the bucket locally when Alchemy is running in watch mode.
|
|
94
103
|
*/
|
|
@@ -245,9 +254,10 @@ export type R2ObjectMetadata = {
|
|
|
245
254
|
etag: string;
|
|
246
255
|
uploaded: Date;
|
|
247
256
|
size: number;
|
|
257
|
+
httpMetadata?: R2HTTPMetadata;
|
|
248
258
|
};
|
|
249
259
|
|
|
250
|
-
export type
|
|
260
|
+
export type R2ObjectContent = R2ObjectMetadata & {
|
|
251
261
|
arrayBuffer(): Promise<ArrayBuffer>;
|
|
252
262
|
bytes(): Promise<Uint8Array>;
|
|
253
263
|
text(): Promise<string>;
|
|
@@ -264,7 +274,7 @@ export type PutR2ObjectResponse = {
|
|
|
264
274
|
};
|
|
265
275
|
|
|
266
276
|
export type R2Objects = {
|
|
267
|
-
objects: R2ObjectMetadata[];
|
|
277
|
+
objects: Omit<R2ObjectMetadata, "httpMetadata">[];
|
|
268
278
|
} & (
|
|
269
279
|
| {
|
|
270
280
|
truncated: true;
|
|
@@ -278,7 +288,7 @@ export type R2Objects = {
|
|
|
278
288
|
|
|
279
289
|
export type R2Bucket = _R2Bucket & {
|
|
280
290
|
head(key: string): Promise<R2ObjectMetadata | null>;
|
|
281
|
-
get(key: string): Promise<
|
|
291
|
+
get(key: string): Promise<R2ObjectContent | null>;
|
|
282
292
|
put(
|
|
283
293
|
key: string,
|
|
284
294
|
value:
|
|
@@ -288,6 +298,7 @@ export type R2Bucket = _R2Bucket & {
|
|
|
288
298
|
| string
|
|
289
299
|
| null
|
|
290
300
|
| Blob,
|
|
301
|
+
options?: Pick<R2PutOptions, "httpMetadata">,
|
|
291
302
|
): Promise<PutR2ObjectResponse>;
|
|
292
303
|
delete(key: string): Promise<Response>;
|
|
293
304
|
list(options?: R2ListOptions): Promise<R2Objects>;
|
|
@@ -337,6 +348,24 @@ type _R2Bucket = Omit<BucketProps, "delete" | "dev"> & {
|
|
|
337
348
|
*/
|
|
338
349
|
remote: boolean;
|
|
339
350
|
};
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Data catalog for the bucket
|
|
354
|
+
*/
|
|
355
|
+
catalog?: {
|
|
356
|
+
/**
|
|
357
|
+
* ID of the data catalog
|
|
358
|
+
*/
|
|
359
|
+
id: string;
|
|
360
|
+
/**
|
|
361
|
+
* Name of the data catalog
|
|
362
|
+
*/
|
|
363
|
+
name: string;
|
|
364
|
+
/**
|
|
365
|
+
* Host of the data catalog
|
|
366
|
+
*/
|
|
367
|
+
host: string;
|
|
368
|
+
};
|
|
340
369
|
};
|
|
341
370
|
|
|
342
371
|
export function isBucket(resource: any): resource is R2Bucket {
|
|
@@ -391,6 +420,8 @@ export async function R2Bucket(
|
|
|
391
420
|
id: string,
|
|
392
421
|
props: BucketProps = {},
|
|
393
422
|
): Promise<R2Bucket> {
|
|
423
|
+
const scope = Scope.current;
|
|
424
|
+
const isLocal = scope.local && props.dev?.remote !== true;
|
|
394
425
|
const api = await createCloudflareApi(props);
|
|
395
426
|
const bucket = await _R2Bucket(id, {
|
|
396
427
|
...props,
|
|
@@ -399,14 +430,54 @@ export async function R2Bucket(
|
|
|
399
430
|
force: Scope.current.local,
|
|
400
431
|
},
|
|
401
432
|
});
|
|
433
|
+
|
|
434
|
+
let _miniflare: mf.Miniflare | undefined;
|
|
435
|
+
const miniflare = () => {
|
|
436
|
+
if (_miniflare) {
|
|
437
|
+
return _miniflare;
|
|
438
|
+
}
|
|
439
|
+
_miniflare = new mf.Miniflare({
|
|
440
|
+
script: "",
|
|
441
|
+
modules: true,
|
|
442
|
+
defaultPersistRoot: getDefaultPersistPath(scope.rootDir),
|
|
443
|
+
r2Buckets: [bucket.dev.id],
|
|
444
|
+
log: process.env.DEBUG ? new mf.Log(mf.LogLevel.DEBUG) : undefined,
|
|
445
|
+
});
|
|
446
|
+
scope.onCleanup(async () => _miniflare?.dispose());
|
|
447
|
+
return _miniflare;
|
|
448
|
+
};
|
|
449
|
+
const localBucket = () => miniflare().getR2Bucket(bucket.dev.id);
|
|
450
|
+
|
|
402
451
|
return {
|
|
403
452
|
...bucket,
|
|
404
|
-
head: async (key: string) =>
|
|
405
|
-
|
|
453
|
+
head: async (key: string) => {
|
|
454
|
+
if (isLocal) {
|
|
455
|
+
const result = await (await localBucket()).head(key);
|
|
456
|
+
if (result) {
|
|
457
|
+
return {
|
|
458
|
+
key: result.key,
|
|
459
|
+
etag: result.etag,
|
|
460
|
+
uploaded: result.uploaded,
|
|
461
|
+
size: result.size,
|
|
462
|
+
httpMetadata: result.httpMetadata,
|
|
463
|
+
} as R2ObjectMetadata;
|
|
464
|
+
}
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
return headObject(api, {
|
|
406
468
|
bucketName: bucket.name,
|
|
407
469
|
key,
|
|
408
|
-
})
|
|
470
|
+
});
|
|
471
|
+
},
|
|
409
472
|
get: async (key: string) => {
|
|
473
|
+
if (isLocal) {
|
|
474
|
+
const result = await (await localBucket()).get(key);
|
|
475
|
+
if (result) {
|
|
476
|
+
// cast because workers vs node built-ins
|
|
477
|
+
return result as unknown as R2ObjectContent;
|
|
478
|
+
}
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
410
481
|
const response = await getObject(api, {
|
|
411
482
|
bucketName: bucket.name,
|
|
412
483
|
key,
|
|
@@ -419,19 +490,42 @@ export async function R2Bucket(
|
|
|
419
490
|
throw await handleApiError(response, "get", "object", key);
|
|
420
491
|
}
|
|
421
492
|
},
|
|
422
|
-
list: async (options?: R2ListOptions): Promise<R2Objects> =>
|
|
423
|
-
|
|
493
|
+
list: async (options?: R2ListOptions): Promise<R2Objects> => {
|
|
494
|
+
if (isLocal) {
|
|
495
|
+
return (await localBucket()).list(options);
|
|
496
|
+
}
|
|
497
|
+
return listObjects(api, bucket.name, {
|
|
424
498
|
...options,
|
|
425
499
|
jurisdiction: bucket.jurisdiction,
|
|
426
|
-
})
|
|
500
|
+
});
|
|
501
|
+
},
|
|
427
502
|
put: async (
|
|
428
503
|
key: string,
|
|
429
504
|
value: PutObjectObject,
|
|
505
|
+
options?: Pick<R2PutOptions, "httpMetadata">,
|
|
430
506
|
): Promise<PutR2ObjectResponse> => {
|
|
507
|
+
if (isLocal) {
|
|
508
|
+
return await (await localBucket()).put(
|
|
509
|
+
key,
|
|
510
|
+
typeof value === "string"
|
|
511
|
+
? value
|
|
512
|
+
: Buffer.isBuffer(value) ||
|
|
513
|
+
value instanceof Uint8Array ||
|
|
514
|
+
value instanceof ArrayBuffer
|
|
515
|
+
? new Uint8Array(value)
|
|
516
|
+
: value instanceof Blob
|
|
517
|
+
? new Uint8Array(await value.arrayBuffer())
|
|
518
|
+
: value instanceof ReadableStream
|
|
519
|
+
? new Uint8Array(await streamToBuffer(value))
|
|
520
|
+
: value,
|
|
521
|
+
options,
|
|
522
|
+
);
|
|
523
|
+
}
|
|
431
524
|
const response = await putObject(api, {
|
|
432
525
|
bucketName: bucket.name,
|
|
433
526
|
key: key,
|
|
434
527
|
object: value,
|
|
528
|
+
options: options,
|
|
435
529
|
});
|
|
436
530
|
const body = (await response.json()) as {
|
|
437
531
|
result: {
|
|
@@ -450,19 +544,24 @@ export async function R2Bucket(
|
|
|
450
544
|
size: Number(body.result.size),
|
|
451
545
|
};
|
|
452
546
|
},
|
|
453
|
-
delete: async (key: string) =>
|
|
454
|
-
|
|
547
|
+
delete: async (key: string) => {
|
|
548
|
+
if (isLocal) {
|
|
549
|
+
await (await localBucket()).delete(key);
|
|
550
|
+
}
|
|
551
|
+
return deleteObject(api, {
|
|
455
552
|
bucketName: bucket.name,
|
|
456
553
|
key: key,
|
|
457
|
-
})
|
|
554
|
+
});
|
|
555
|
+
},
|
|
458
556
|
};
|
|
459
557
|
}
|
|
460
558
|
|
|
461
|
-
const parseR2Object = (key: string, response: Response):
|
|
559
|
+
const parseR2Object = (key: string, response: Response): R2ObjectContent => ({
|
|
462
560
|
etag: response.headers.get("ETag")!,
|
|
463
561
|
uploaded: parseDate(response.headers),
|
|
464
562
|
key,
|
|
465
563
|
size: Number(response.headers.get("Content-Length")),
|
|
564
|
+
httpMetadata: mapHeadersToHttpMetadata(response.headers),
|
|
466
565
|
arrayBuffer: () => response.arrayBuffer(),
|
|
467
566
|
bytes: () => response.bytes(),
|
|
468
567
|
text: () => response.text(),
|
|
@@ -481,7 +580,7 @@ const _R2Bucket = Resource(
|
|
|
481
580
|
props: BucketProps = {},
|
|
482
581
|
): Promise<_R2Bucket> {
|
|
483
582
|
const bucketName =
|
|
484
|
-
props.name ?? this.output?.name
|
|
583
|
+
props.name ?? (this.output?.name || this.scope.createPhysicalName(id));
|
|
485
584
|
|
|
486
585
|
if (this.phase === "update" && this.output?.name !== bucketName) {
|
|
487
586
|
this.replace();
|
|
@@ -513,6 +612,9 @@ const _R2Bucket = Resource(
|
|
|
513
612
|
|
|
514
613
|
if (this.phase === "delete") {
|
|
515
614
|
if (props.delete !== false) {
|
|
615
|
+
if (this.output?.catalog) {
|
|
616
|
+
await disableDataCatalog(api, bucketName);
|
|
617
|
+
}
|
|
516
618
|
if (this.output.dev?.id) {
|
|
517
619
|
await deleteMiniflareBinding(this.scope, "r2", this.output.dev.id);
|
|
518
620
|
}
|
|
@@ -552,6 +654,16 @@ const _R2Bucket = Resource(
|
|
|
552
654
|
if (props.lock?.length) {
|
|
553
655
|
await putBucketLockRules(api, bucketName, props);
|
|
554
656
|
}
|
|
657
|
+
let dataCatalog:
|
|
658
|
+
| {
|
|
659
|
+
id: string;
|
|
660
|
+
name: string;
|
|
661
|
+
host: string;
|
|
662
|
+
}
|
|
663
|
+
| undefined;
|
|
664
|
+
if (props.dataCatalog) {
|
|
665
|
+
dataCatalog = await enableDataCatalog(api, bucketName);
|
|
666
|
+
}
|
|
555
667
|
return {
|
|
556
668
|
name: bucketName,
|
|
557
669
|
location: bucket.location,
|
|
@@ -565,6 +677,7 @@ const _R2Bucket = Resource(
|
|
|
565
677
|
lock: props.lock,
|
|
566
678
|
cors: props.cors,
|
|
567
679
|
dev,
|
|
680
|
+
catalog: dataCatalog,
|
|
568
681
|
};
|
|
569
682
|
} else {
|
|
570
683
|
if (bucketName !== this.output.name) {
|
|
@@ -572,6 +685,13 @@ const _R2Bucket = Resource(
|
|
|
572
685
|
`Cannot update R2Bucket name after creation. Bucket name is immutable. Before: ${this.output.name}, After: ${bucketName}`,
|
|
573
686
|
);
|
|
574
687
|
}
|
|
688
|
+
if (this.output?.catalog && !props.dataCatalog) {
|
|
689
|
+
await disableDataCatalog(api, bucketName);
|
|
690
|
+
this.output.catalog = undefined;
|
|
691
|
+
}
|
|
692
|
+
if (!this.output.catalog && props.dataCatalog) {
|
|
693
|
+
this.output.catalog = await enableDataCatalog(api, bucketName);
|
|
694
|
+
}
|
|
575
695
|
let domain = this.output.domain;
|
|
576
696
|
if (!!domain !== allowPublicAccess) {
|
|
577
697
|
domain = await putManagedDomain(
|
|
@@ -1051,6 +1171,7 @@ export async function headObject(
|
|
|
1051
1171
|
etag: response.headers.get("ETag")?.replace(/"/g, "")!,
|
|
1052
1172
|
uploaded: parseDate(response.headers),
|
|
1053
1173
|
size: Number(response.headers.get("Content-Length")),
|
|
1174
|
+
httpMetadata: mapHeadersToHttpMetadata(response.headers),
|
|
1054
1175
|
};
|
|
1055
1176
|
}
|
|
1056
1177
|
|
|
@@ -1079,34 +1200,85 @@ export async function getObject(
|
|
|
1079
1200
|
});
|
|
1080
1201
|
}
|
|
1081
1202
|
|
|
1082
|
-
type PutObjectObject =
|
|
1203
|
+
export type PutObjectObject =
|
|
1083
1204
|
| ReadableStream
|
|
1084
1205
|
| ArrayBuffer
|
|
1085
1206
|
| ArrayBufferView
|
|
1207
|
+
| Uint8Array
|
|
1086
1208
|
| string
|
|
1209
|
+
| Buffer
|
|
1087
1210
|
| Blob;
|
|
1088
1211
|
|
|
1212
|
+
function mapHttpMetadataToHeaders(
|
|
1213
|
+
httpMetadata: R2PutOptions["httpMetadata"],
|
|
1214
|
+
): Record<string, string> {
|
|
1215
|
+
const headers: Record<string, string> = {};
|
|
1216
|
+
|
|
1217
|
+
if (httpMetadata instanceof Headers) {
|
|
1218
|
+
httpMetadata.forEach((value, key) => {
|
|
1219
|
+
headers[key] = value;
|
|
1220
|
+
});
|
|
1221
|
+
} else {
|
|
1222
|
+
const {
|
|
1223
|
+
contentType,
|
|
1224
|
+
contentLanguage,
|
|
1225
|
+
contentDisposition,
|
|
1226
|
+
contentEncoding,
|
|
1227
|
+
cacheControl,
|
|
1228
|
+
cacheExpiry,
|
|
1229
|
+
} = httpMetadata as R2HTTPMetadata;
|
|
1230
|
+
if (contentType) headers["Content-Type"] = contentType;
|
|
1231
|
+
if (contentLanguage) headers["Content-Language"] = contentLanguage;
|
|
1232
|
+
if (contentDisposition) headers["Content-Disposition"] = contentDisposition;
|
|
1233
|
+
if (contentEncoding) headers["Content-Encoding"] = contentEncoding;
|
|
1234
|
+
if (cacheControl) headers["Cache-Control"] = cacheControl;
|
|
1235
|
+
if (cacheExpiry) headers.Expires = cacheExpiry.toUTCString();
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
return headers;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
function mapHeadersToHttpMetadata(headers: Headers): R2HTTPMetadata {
|
|
1242
|
+
return {
|
|
1243
|
+
contentType: headers.get("Content-Type") ?? undefined,
|
|
1244
|
+
contentLanguage: headers.get("Content-Language") ?? undefined,
|
|
1245
|
+
contentDisposition: headers.get("Content-Disposition") ?? undefined,
|
|
1246
|
+
contentEncoding: headers.get("Content-Encoding") ?? undefined,
|
|
1247
|
+
cacheControl: headers.get("Cache-Control") ?? undefined,
|
|
1248
|
+
cacheExpiry: headers.get("Expires")
|
|
1249
|
+
? new Date(headers.get("Expires")!)
|
|
1250
|
+
: undefined,
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1089
1254
|
export async function putObject(
|
|
1090
1255
|
api: CloudflareApi,
|
|
1091
1256
|
{
|
|
1092
1257
|
bucketName,
|
|
1093
1258
|
key,
|
|
1094
1259
|
object,
|
|
1260
|
+
options,
|
|
1095
1261
|
}: {
|
|
1096
1262
|
bucketName: string;
|
|
1097
1263
|
key: string;
|
|
1098
1264
|
object: PutObjectObject;
|
|
1265
|
+
options?: Pick<R2PutOptions, "httpMetadata">;
|
|
1099
1266
|
},
|
|
1100
1267
|
): Promise<Response> {
|
|
1101
1268
|
// Using withExponentialBackoff for reliability
|
|
1102
1269
|
return await withRetries(async () => {
|
|
1270
|
+
const headers: Record<string, string> = {
|
|
1271
|
+
"Content-Type": "application/octet-stream",
|
|
1272
|
+
...(options?.httpMetadata
|
|
1273
|
+
? mapHttpMetadataToHeaders(options?.httpMetadata)
|
|
1274
|
+
: {}),
|
|
1275
|
+
};
|
|
1276
|
+
|
|
1103
1277
|
const response = await api.put(
|
|
1104
1278
|
`/accounts/${api.accountId}/r2/buckets/${bucketName}/objects/${key}`,
|
|
1105
1279
|
object,
|
|
1106
1280
|
{
|
|
1107
|
-
headers
|
|
1108
|
-
"Content-Type": "application/octet-stream",
|
|
1109
|
-
},
|
|
1281
|
+
headers,
|
|
1110
1282
|
},
|
|
1111
1283
|
);
|
|
1112
1284
|
if (!response.ok) {
|
|
@@ -1132,3 +1304,31 @@ export async function deleteObject(
|
|
|
1132
1304
|
return response;
|
|
1133
1305
|
});
|
|
1134
1306
|
}
|
|
1307
|
+
|
|
1308
|
+
export async function enableDataCatalog(
|
|
1309
|
+
api: CloudflareApi,
|
|
1310
|
+
bucketName: string,
|
|
1311
|
+
) {
|
|
1312
|
+
const response = await extractCloudflareResult<{
|
|
1313
|
+
id: string;
|
|
1314
|
+
name: string;
|
|
1315
|
+
}>(
|
|
1316
|
+
`enable data catalog for bucket "${bucketName}"`,
|
|
1317
|
+
api.post(`/accounts/${api.accountId}/r2-catalog/${bucketName}/enable`, {}),
|
|
1318
|
+
);
|
|
1319
|
+
return {
|
|
1320
|
+
id: response.id,
|
|
1321
|
+
name: response.name,
|
|
1322
|
+
host: `https://catalog.cloudflarestorage.com/${response.name.replace("_", "/")}`,
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
export async function disableDataCatalog(
|
|
1327
|
+
api: CloudflareApi,
|
|
1328
|
+
bucketName: string,
|
|
1329
|
+
) {
|
|
1330
|
+
await api.post(
|
|
1331
|
+
`/accounts/${api.accountId}/r2-catalog/${bucketName}/disable`,
|
|
1332
|
+
{},
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
@@ -950,14 +950,13 @@ export async function updateContainerApplication(
|
|
|
950
950
|
);
|
|
951
951
|
const result = (await response.json()) as {
|
|
952
952
|
result: ContainerApplicationData;
|
|
953
|
-
errors: { message: string }[];
|
|
953
|
+
errors: { message: string; code: number }[];
|
|
954
954
|
};
|
|
955
955
|
if (response.ok) {
|
|
956
956
|
return result.result;
|
|
957
957
|
}
|
|
958
|
-
|
|
959
958
|
throw Error(
|
|
960
|
-
`Failed to create container application: ${result.errors?.map((e
|
|
959
|
+
`Failed to create container application: ${result.errors?.map((e) => `[${e.code}] ${e.message}`).join(", ") ?? "Unknown error"}`,
|
|
961
960
|
);
|
|
962
961
|
}
|
|
963
962
|
|
|
@@ -1045,13 +1044,13 @@ export async function createContainerApplicationRollout(
|
|
|
1045
1044
|
);
|
|
1046
1045
|
const result = (await response.json()) as {
|
|
1047
1046
|
result: CreateRolloutApplicationResponse;
|
|
1048
|
-
errors: { message: string }[];
|
|
1047
|
+
errors: { message: string; code: number }[];
|
|
1049
1048
|
};
|
|
1050
1049
|
if (response.ok) {
|
|
1051
1050
|
return result.result;
|
|
1052
1051
|
}
|
|
1053
1052
|
throw Error(
|
|
1054
|
-
`Failed to create container application rollout: ${result.errors.map((e
|
|
1053
|
+
`Failed to create container application rollout: ${result.errors.map((e) => `[${e.code}] ${e.message}`).join(", ")}`,
|
|
1055
1054
|
);
|
|
1056
1055
|
}
|
|
1057
1056
|
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from "./astro/astro.ts";
|
|
|
13
13
|
export * from "./bindings.ts";
|
|
14
14
|
export * from "./bound.ts";
|
|
15
15
|
export * from "./browser-rendering.ts";
|
|
16
|
+
export * from "./bucket-object.ts";
|
|
16
17
|
export * from "./bucket.ts";
|
|
17
18
|
export * from "./certificate-pack.ts";
|
|
18
19
|
export * from "./cloudflare-env-proxy.ts";
|