alchemy-effect 0.7.0 → 0.8.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.
- package/bin/alchemy-effect.js +9 -7
- package/bin/alchemy-effect.js.map +1 -1
- package/bin/alchemy-effect.ts +11 -0
- package/package.json +1 -1
- package/src/AWS/AutoScaling/LaunchTemplate.ts +5 -4
- package/src/AWS/EC2/Instance.ts +3 -4
- package/src/AWS/ECS/Task.ts +36 -34
- package/src/AWS/Lambda/Function.ts +68 -68
- package/src/Binding.ts +10 -3
- package/src/Cloudflare/Container.ts +68 -67
- package/src/Cloudflare/D1/D1Database.ts +32 -32
- package/src/Cloudflare/KV/Delete.ts +7 -5
- package/src/Cloudflare/KV/Get.ts +5 -5
- package/src/Cloudflare/KV/GetWithMetadata.ts +5 -5
- package/src/Cloudflare/KV/{Namespace.ts → KVNamespace.ts} +6 -6
- package/src/Cloudflare/KV/{NamespaceBinding.ts → KVNamespaceBinding.ts} +2 -2
- package/src/Cloudflare/KV/List.ts +5 -5
- package/src/Cloudflare/KV/Put.ts +5 -5
- package/src/Cloudflare/KV/index.ts +1 -1
- package/src/Cloudflare/Providers.ts +2 -8
- package/src/Cloudflare/R2/{Bucket.ts → R2Bucket.ts} +19 -19
- package/src/Cloudflare/R2/R2BucketBinding.ts +299 -0
- package/src/Cloudflare/R2/index.ts +2 -9
- package/src/Cloudflare/Workers/InferEnv.ts +23 -0
- package/src/Cloudflare/Workers/Worker.ts +185 -108
- package/src/Cloudflare/Workers/index.ts +1 -0
- package/src/Cloudflare/index.ts +2 -2
- package/src/Plan.ts +12 -0
- package/src/Platform.ts +39 -55
- package/src/Test/Vitest.ts +15 -2
- package/src/Util/effect.ts +24 -0
- package/src/Cloudflare/R2/BucketBinding.ts +0 -31
- package/src/Cloudflare/R2/CreateMultipartUpload.ts +0 -59
- package/src/Cloudflare/R2/DeleteObject.ts +0 -41
- package/src/Cloudflare/R2/GetObject.ts +0 -47
- package/src/Cloudflare/R2/HeadObject.ts +0 -41
- package/src/Cloudflare/R2/ListObjects.ts +0 -45
- package/src/Cloudflare/R2/MultipartUploadClient.ts +0 -40
- package/src/Cloudflare/R2/PutObject.ts +0 -55
- package/src/Cloudflare/R2/ResumeMultipartUpload.ts +0 -48
- package/src/Cloudflare/R2/UploadValue.ts +0 -10
|
@@ -3,15 +3,15 @@ import * as Effect from "effect/Effect";
|
|
|
3
3
|
import * as Layer from "effect/Layer";
|
|
4
4
|
import * as Binding from "../../Binding.ts";
|
|
5
5
|
import { WorkerEnvironment } from "../Workers/Worker.ts";
|
|
6
|
-
import type {
|
|
7
|
-
import { NamespaceBinding } from "./
|
|
6
|
+
import type { KVNamespace } from "./KVNamespace.ts";
|
|
7
|
+
import { NamespaceBinding } from "./KVNamespaceBinding.ts";
|
|
8
8
|
|
|
9
9
|
export interface GetWithMetadataOptions extends runtime.KVNamespaceGetOptions<undefined> {}
|
|
10
10
|
|
|
11
11
|
export class GetWithMetadata extends Binding.Service<
|
|
12
12
|
GetWithMetadata,
|
|
13
13
|
(
|
|
14
|
-
namespace:
|
|
14
|
+
namespace: KVNamespace,
|
|
15
15
|
) => Effect.Effect<
|
|
16
16
|
<Metadata = unknown>(
|
|
17
17
|
key: string,
|
|
@@ -28,7 +28,7 @@ export const GetWithMetadataLive = Layer.effect(
|
|
|
28
28
|
const Policy = yield* GetWithMetadataPolicy;
|
|
29
29
|
const env = yield* WorkerEnvironment;
|
|
30
30
|
|
|
31
|
-
return Effect.fn(function* (namespace:
|
|
31
|
+
return Effect.fn(function* (namespace: KVNamespace) {
|
|
32
32
|
yield* Policy(namespace);
|
|
33
33
|
const kvNamespace = (env as Record<string, runtime.KVNamespace>)[
|
|
34
34
|
namespace.LogicalId
|
|
@@ -47,7 +47,7 @@ export const GetWithMetadataLive = Layer.effect(
|
|
|
47
47
|
|
|
48
48
|
export class GetWithMetadataPolicy extends Binding.Policy<
|
|
49
49
|
GetWithMetadataPolicy,
|
|
50
|
-
(namespace:
|
|
50
|
+
(namespace: KVNamespace) => Effect.Effect<void>
|
|
51
51
|
>()("Cloudflare.KV.GetWithMetadata") {}
|
|
52
52
|
|
|
53
53
|
export const GetWithMetadataPolicyLive =
|
|
@@ -21,8 +21,8 @@ export type NamespaceProps = {
|
|
|
21
21
|
title?: string;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
export
|
|
25
|
-
"Cloudflare.
|
|
24
|
+
export type KVNamespace = Resource<
|
|
25
|
+
"Cloudflare.KVNamespace",
|
|
26
26
|
NamespaceProps,
|
|
27
27
|
{
|
|
28
28
|
title: string;
|
|
@@ -30,16 +30,16 @@ export interface Namespace extends Resource<
|
|
|
30
30
|
supportsUrlEncoding: boolean | undefined;
|
|
31
31
|
accountId: string;
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
>;
|
|
34
34
|
|
|
35
|
-
export const
|
|
35
|
+
export const KVNamespace = Resource<KVNamespace>("Cloudflare.KVNamespace");
|
|
36
36
|
|
|
37
37
|
export const NamespaceProvider = (): Layer<
|
|
38
|
-
Provider<
|
|
38
|
+
Provider<KVNamespace>,
|
|
39
39
|
never,
|
|
40
40
|
Account | Credentials | HttpClient | Stack | Stage
|
|
41
41
|
> =>
|
|
42
|
-
|
|
42
|
+
KVNamespace.provider.effect(
|
|
43
43
|
Effect.gen(function* () {
|
|
44
44
|
const accountId = yield* Account;
|
|
45
45
|
const createNamespace = yield* kv.createNamespace;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import type { ResourceLike } from "../../Resource.ts";
|
|
3
3
|
import { isWorker } from "../Workers/Worker.ts";
|
|
4
|
-
import type {
|
|
4
|
+
import type { KVNamespace } from "./KVNamespace.ts";
|
|
5
5
|
|
|
6
6
|
export const NamespaceBinding = Effect.fn(function* (
|
|
7
7
|
host: ResourceLike,
|
|
8
|
-
namespace:
|
|
8
|
+
namespace: KVNamespace,
|
|
9
9
|
) {
|
|
10
10
|
if (isWorker(host)) {
|
|
11
11
|
yield* host.bind`Bind(${namespace})`({
|
|
@@ -3,15 +3,15 @@ import * as Effect from "effect/Effect";
|
|
|
3
3
|
import * as Layer from "effect/Layer";
|
|
4
4
|
import * as Binding from "../../Binding.ts";
|
|
5
5
|
import { WorkerEnvironment } from "../Workers/Worker.ts";
|
|
6
|
-
import type {
|
|
7
|
-
import { NamespaceBinding } from "./
|
|
6
|
+
import type { KVNamespace } from "./KVNamespace.ts";
|
|
7
|
+
import { NamespaceBinding } from "./KVNamespaceBinding.ts";
|
|
8
8
|
|
|
9
9
|
export interface ListOptions extends runtime.KVNamespaceListOptions {}
|
|
10
10
|
|
|
11
11
|
export class List extends Binding.Service<
|
|
12
12
|
List,
|
|
13
13
|
(
|
|
14
|
-
namespace:
|
|
14
|
+
namespace: KVNamespace,
|
|
15
15
|
) => Effect.Effect<
|
|
16
16
|
<Metadata = unknown>(
|
|
17
17
|
options?: ListOptions,
|
|
@@ -25,7 +25,7 @@ export const ListLive = Layer.effect(
|
|
|
25
25
|
const Policy = yield* ListPolicy;
|
|
26
26
|
const env = yield* WorkerEnvironment;
|
|
27
27
|
|
|
28
|
-
return Effect.fn(function* (namespace:
|
|
28
|
+
return Effect.fn(function* (namespace: KVNamespace) {
|
|
29
29
|
yield* Policy(namespace);
|
|
30
30
|
const kvNamespace = (env as Record<string, runtime.KVNamespace>)[
|
|
31
31
|
namespace.LogicalId
|
|
@@ -39,7 +39,7 @@ export const ListLive = Layer.effect(
|
|
|
39
39
|
|
|
40
40
|
export class ListPolicy extends Binding.Policy<
|
|
41
41
|
ListPolicy,
|
|
42
|
-
(namespace:
|
|
42
|
+
(namespace: KVNamespace) => Effect.Effect<void>
|
|
43
43
|
>()("Cloudflare.KV.List") {}
|
|
44
44
|
|
|
45
45
|
export const ListPolicyLive = ListPolicy.layer.succeed(NamespaceBinding);
|
package/src/Cloudflare/KV/Put.ts
CHANGED
|
@@ -5,8 +5,8 @@ import type * as Stream from "effect/Stream";
|
|
|
5
5
|
import * as Binding from "../../Binding.ts";
|
|
6
6
|
import { WorkerEnvironment } from "../Workers/Worker.ts";
|
|
7
7
|
import { replaceEffectStream } from "../stream.ts";
|
|
8
|
-
import type {
|
|
9
|
-
import { NamespaceBinding } from "./
|
|
8
|
+
import type { KVNamespace } from "./KVNamespace.ts";
|
|
9
|
+
import { NamespaceBinding } from "./KVNamespaceBinding.ts";
|
|
10
10
|
|
|
11
11
|
export type PutValue =
|
|
12
12
|
| string
|
|
@@ -20,7 +20,7 @@ export interface PutOptions extends runtime.KVNamespacePutOptions {}
|
|
|
20
20
|
export class Put extends Binding.Service<
|
|
21
21
|
Put,
|
|
22
22
|
(
|
|
23
|
-
namespace:
|
|
23
|
+
namespace: KVNamespace,
|
|
24
24
|
) => Effect.Effect<
|
|
25
25
|
(key: string, value: PutValue, options?: PutOptions) => Effect.Effect<void>
|
|
26
26
|
>
|
|
@@ -32,7 +32,7 @@ export const PutLive = Layer.effect(
|
|
|
32
32
|
const Policy = yield* PutPolicy;
|
|
33
33
|
const env = yield* WorkerEnvironment;
|
|
34
34
|
|
|
35
|
-
return Effect.fn(function* (namespace:
|
|
35
|
+
return Effect.fn(function* (namespace: KVNamespace) {
|
|
36
36
|
yield* Policy(namespace);
|
|
37
37
|
const kvNamespace = (env as Record<string, runtime.KVNamespace>)[
|
|
38
38
|
namespace.LogicalId
|
|
@@ -53,7 +53,7 @@ export const PutLive = Layer.effect(
|
|
|
53
53
|
|
|
54
54
|
export class PutPolicy extends Binding.Policy<
|
|
55
55
|
PutPolicy,
|
|
56
|
-
(namespace:
|
|
56
|
+
(namespace: KVNamespace) => Effect.Effect<void>
|
|
57
57
|
>()("Cloudflare.KV.Put") {}
|
|
58
58
|
|
|
59
59
|
export const PutPolicyLive = PutPolicy.layer.succeed(NamespaceBinding);
|
|
@@ -53,7 +53,7 @@ export const resources = () =>
|
|
|
53
53
|
WorkflowProvider(),
|
|
54
54
|
D1.DatabaseProvider(),
|
|
55
55
|
KV.NamespaceProvider(),
|
|
56
|
-
R2.
|
|
56
|
+
R2.R2BucketProvider(),
|
|
57
57
|
);
|
|
58
58
|
|
|
59
59
|
/**
|
|
@@ -62,13 +62,7 @@ export const resources = () =>
|
|
|
62
62
|
export const bindings = () =>
|
|
63
63
|
Layer.mergeAll(
|
|
64
64
|
D1.D1ConnectionPolicyLive,
|
|
65
|
-
R2.
|
|
66
|
-
R2.PutObjectPolicyLive,
|
|
67
|
-
R2.DeleteObjectPolicyLive,
|
|
68
|
-
R2.HeadObjectPolicyLive,
|
|
69
|
-
R2.ListObjectsPolicyLive,
|
|
70
|
-
R2.CreateMultipartUploadPolicyLive,
|
|
71
|
-
R2.ResumeMultipartUploadPolicyLive,
|
|
65
|
+
R2.R2BucketBindingPolicyLive,
|
|
72
66
|
KV.GetPolicyLive,
|
|
73
67
|
KV.PutPolicyLive,
|
|
74
68
|
KV.DeletePolicyLive,
|
|
@@ -6,9 +6,9 @@ import { createPhysicalName } from "../../PhysicalName.ts";
|
|
|
6
6
|
import { Resource } from "../../Resource.ts";
|
|
7
7
|
import { Account } from "../Account.ts";
|
|
8
8
|
|
|
9
|
-
export type
|
|
9
|
+
export type R2BucketName = string;
|
|
10
10
|
|
|
11
|
-
export type
|
|
11
|
+
export type R2BucketProps = {
|
|
12
12
|
/**
|
|
13
13
|
* Name of the bucket. If omitted, a unique name will be generated.
|
|
14
14
|
* @default ${app}-${stage}-${id}
|
|
@@ -18,40 +18,40 @@ export type BucketProps = {
|
|
|
18
18
|
* Storage class for newly uploaded objects.
|
|
19
19
|
* @default "Standard"
|
|
20
20
|
*/
|
|
21
|
-
storageClass?:
|
|
21
|
+
storageClass?: R2Bucket.StorageClass;
|
|
22
22
|
/**
|
|
23
23
|
* Jurisdiction where objects in this bucket are guaranteed to be stored.
|
|
24
24
|
* @default "default"
|
|
25
25
|
*/
|
|
26
|
-
jurisdiction?:
|
|
26
|
+
jurisdiction?: R2Bucket.Jurisdiction;
|
|
27
27
|
/**
|
|
28
28
|
* Location hint for the bucket.
|
|
29
29
|
*/
|
|
30
|
-
locationHint?:
|
|
30
|
+
locationHint?: R2Bucket.Location;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
export
|
|
34
|
-
"Cloudflare.
|
|
35
|
-
|
|
33
|
+
export type R2Bucket = Resource<
|
|
34
|
+
"Cloudflare.R2Bucket",
|
|
35
|
+
R2BucketProps,
|
|
36
36
|
{
|
|
37
|
-
bucketName:
|
|
38
|
-
storageClass:
|
|
39
|
-
jurisdiction:
|
|
40
|
-
location:
|
|
37
|
+
bucketName: R2BucketName;
|
|
38
|
+
storageClass: R2Bucket.StorageClass;
|
|
39
|
+
jurisdiction: R2Bucket.Jurisdiction;
|
|
40
|
+
location: R2Bucket.Location | undefined;
|
|
41
41
|
accountId: string;
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
>;
|
|
44
44
|
|
|
45
|
-
export const
|
|
45
|
+
export const R2Bucket = Resource<R2Bucket>("Cloudflare.R2Bucket");
|
|
46
46
|
|
|
47
|
-
export declare namespace
|
|
47
|
+
export declare namespace R2Bucket {
|
|
48
48
|
export type StorageClass = "Standard" | "InfrequentAccess";
|
|
49
49
|
export type Jurisdiction = "default" | "eu" | "fedramp";
|
|
50
50
|
export type Location = "apac" | "eeur" | "enam" | "weur" | "wnam" | "oc";
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
export const
|
|
54
|
-
|
|
53
|
+
export const R2BucketProvider = () =>
|
|
54
|
+
R2Bucket.provider.effect(
|
|
55
55
|
Effect.gen(function* () {
|
|
56
56
|
const accountId = yield* Account;
|
|
57
57
|
const createBucket = yield* r2.createBucket;
|
|
@@ -70,9 +70,9 @@ export const BucketProvider = () =>
|
|
|
70
70
|
|
|
71
71
|
const normalizeLocation = (
|
|
72
72
|
location: string | undefined | null,
|
|
73
|
-
):
|
|
73
|
+
): R2Bucket.Location | undefined => {
|
|
74
74
|
if (!location) return undefined;
|
|
75
|
-
return location.toLowerCase() as
|
|
75
|
+
return location.toLowerCase() as R2Bucket.Location;
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
return {
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import type * as runtime from "@cloudflare/workers-types";
|
|
2
|
+
import * as Data from "effect/Data";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Layer from "effect/Layer";
|
|
5
|
+
import * as Stream from "effect/Stream";
|
|
6
|
+
import * as Binding from "../../Binding.ts";
|
|
7
|
+
import * as Output from "../../Output.ts";
|
|
8
|
+
import type { ResourceLike } from "../../Resource.ts";
|
|
9
|
+
import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts";
|
|
10
|
+
import type { R2Bucket } from "./R2Bucket.ts";
|
|
11
|
+
|
|
12
|
+
export interface R2Object extends Omit<runtime.R2Object, "writeHttpMetadata"> {
|
|
13
|
+
writeHttpMetadata(headers: Headers): Effect.Effect<void>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface R2ObjectBody extends R2Object {
|
|
17
|
+
get body(): Stream.Stream<Uint8Array, R2Error>;
|
|
18
|
+
get bodyUsed(): boolean;
|
|
19
|
+
arrayBuffer(): Effect.Effect<ArrayBuffer, R2Error>;
|
|
20
|
+
bytes(): Effect.Effect<Uint8Array, R2Error>;
|
|
21
|
+
text(): Effect.Effect<string, R2Error>;
|
|
22
|
+
json<T>(): Effect.Effect<T, R2Error>;
|
|
23
|
+
blob(): Effect.Effect<runtime.Blob, R2Error>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type R2GetOptions = runtime.R2GetOptions;
|
|
27
|
+
export type R2PutOptions = runtime.R2PutOptions;
|
|
28
|
+
|
|
29
|
+
export type R2ListOptions = runtime.R2ListOptions;
|
|
30
|
+
export type R2Objects = {
|
|
31
|
+
objects: R2Object[];
|
|
32
|
+
delimitedPrefixes: string[];
|
|
33
|
+
} & (
|
|
34
|
+
| {
|
|
35
|
+
truncated: true;
|
|
36
|
+
cursor: string;
|
|
37
|
+
}
|
|
38
|
+
| {
|
|
39
|
+
truncated: false;
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
export type R2Conditional = runtime.R2Conditional;
|
|
43
|
+
|
|
44
|
+
export class R2Error extends Data.TaggedError("R2Error")<{
|
|
45
|
+
message: string;
|
|
46
|
+
cause: Error;
|
|
47
|
+
}> {}
|
|
48
|
+
|
|
49
|
+
export interface R2MultipartUpload {
|
|
50
|
+
raw: runtime.R2MultipartUpload;
|
|
51
|
+
readonly key: string;
|
|
52
|
+
readonly uploadId: string;
|
|
53
|
+
uploadPart(
|
|
54
|
+
partNumber: number,
|
|
55
|
+
value: ReadableStream | (ArrayBuffer | ArrayBufferView) | string | Blob,
|
|
56
|
+
options?: R2UploadPartOptions,
|
|
57
|
+
): Effect.Effect<R2UploadedPart, R2Error>;
|
|
58
|
+
abort(): Effect.Effect<void, R2Error>;
|
|
59
|
+
complete(uploadedParts: R2UploadedPart[]): Effect.Effect<R2Object, R2Error>;
|
|
60
|
+
}
|
|
61
|
+
export type R2MultipartOptions = runtime.R2MultipartOptions;
|
|
62
|
+
export type R2UploadedPart = runtime.R2UploadedPart;
|
|
63
|
+
export interface R2UploadPartOptions extends runtime.R2UploadPartOptions {}
|
|
64
|
+
|
|
65
|
+
export interface R2BucketClient {
|
|
66
|
+
raw: runtime.R2Bucket;
|
|
67
|
+
head(key: string): Effect.Effect<R2Object | null, R2Error>;
|
|
68
|
+
get(
|
|
69
|
+
key: string,
|
|
70
|
+
options: R2GetOptions & {
|
|
71
|
+
onlyIf: runtime.R2Conditional | Headers;
|
|
72
|
+
},
|
|
73
|
+
): Effect.Effect<R2ObjectBody | R2Object | null, R2Error>;
|
|
74
|
+
get(
|
|
75
|
+
key: string,
|
|
76
|
+
options?: R2GetOptions,
|
|
77
|
+
): Effect.Effect<R2ObjectBody | null, R2Error>;
|
|
78
|
+
put(
|
|
79
|
+
key: string,
|
|
80
|
+
value:
|
|
81
|
+
| ReadableStream
|
|
82
|
+
| ArrayBuffer
|
|
83
|
+
| ArrayBufferView
|
|
84
|
+
| string
|
|
85
|
+
| null
|
|
86
|
+
| Blob
|
|
87
|
+
| Stream.Stream<Uint8Array>,
|
|
88
|
+
options?: R2PutOptions & {
|
|
89
|
+
onlyIf: R2Conditional | Headers;
|
|
90
|
+
},
|
|
91
|
+
): Effect.Effect<R2Object | null, R2Error>;
|
|
92
|
+
put(
|
|
93
|
+
key: string,
|
|
94
|
+
value:
|
|
95
|
+
| ReadableStream
|
|
96
|
+
| ArrayBuffer
|
|
97
|
+
| ArrayBufferView
|
|
98
|
+
| string
|
|
99
|
+
| null
|
|
100
|
+
| Blob
|
|
101
|
+
| Stream.Stream<Uint8Array>,
|
|
102
|
+
options?: R2PutOptions,
|
|
103
|
+
): Effect.Effect<R2Object, R2Error>;
|
|
104
|
+
delete(keys: string | string[]): Effect.Effect<void, R2Error>;
|
|
105
|
+
list(options?: R2ListOptions): Effect.Effect<R2Objects, R2Error>;
|
|
106
|
+
createMultipartUpload(
|
|
107
|
+
key: string,
|
|
108
|
+
options?: R2MultipartOptions,
|
|
109
|
+
): Effect.Effect<R2MultipartUpload, R2Error>;
|
|
110
|
+
resumeMultipartUpload(
|
|
111
|
+
key: string,
|
|
112
|
+
uploadId: string,
|
|
113
|
+
): Effect.Effect<R2MultipartUpload, R2Error>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export class R2BucketBinding extends Binding.Service<
|
|
117
|
+
R2BucketBinding,
|
|
118
|
+
(bucket: R2Bucket) => Effect.Effect<R2BucketClient>
|
|
119
|
+
>()("Cloudflare.R2Bucket") {}
|
|
120
|
+
|
|
121
|
+
export const R2BucketBindingLive = Layer.effect(
|
|
122
|
+
R2BucketBinding,
|
|
123
|
+
Effect.gen(function* () {
|
|
124
|
+
const bind = yield* BucketBindingPolicy;
|
|
125
|
+
const env = yield* WorkerEnvironment;
|
|
126
|
+
|
|
127
|
+
return Effect.fn(function* (bucket: R2Bucket) {
|
|
128
|
+
yield* bind(bucket);
|
|
129
|
+
const raw = (env as Record<string, runtime.R2Bucket>)[bucket.LogicalId];
|
|
130
|
+
const tryPromise = <T>(fn: () => Promise<T>): Effect.Effect<T, R2Error> =>
|
|
131
|
+
Effect.tryPromise({
|
|
132
|
+
try: fn,
|
|
133
|
+
catch: (error: any) =>
|
|
134
|
+
new R2Error({
|
|
135
|
+
message: error.message ?? "Unknown error",
|
|
136
|
+
cause: error,
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const wrapR2Object = (object: runtime.R2Object): R2Object => ({
|
|
141
|
+
...object,
|
|
142
|
+
writeHttpMetadata: (headers: Headers) =>
|
|
143
|
+
Effect.sync(() => object.writeHttpMetadata(headers)),
|
|
144
|
+
});
|
|
145
|
+
const wrapR2ObjectBody = (
|
|
146
|
+
object: runtime.R2ObjectBody,
|
|
147
|
+
): R2ObjectBody => ({
|
|
148
|
+
...wrapR2Object(object),
|
|
149
|
+
body: Stream.fromReadableStream({
|
|
150
|
+
evaluate: () =>
|
|
151
|
+
object.body as any as ReadableStream<Uint8Array<ArrayBufferLike>>,
|
|
152
|
+
onError: (error: any) =>
|
|
153
|
+
new R2Error({
|
|
154
|
+
message: error.message ?? "Unknown error",
|
|
155
|
+
cause: error,
|
|
156
|
+
}),
|
|
157
|
+
}),
|
|
158
|
+
bodyUsed: object.bodyUsed,
|
|
159
|
+
arrayBuffer: () => tryPromise(() => object.arrayBuffer()),
|
|
160
|
+
bytes: () => tryPromise(() => object.bytes()),
|
|
161
|
+
text: () => tryPromise(() => object.text()),
|
|
162
|
+
json: <T>() => tryPromise(() => object.json<T>()),
|
|
163
|
+
blob: () => tryPromise(() => object.blob()),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const wrapR2Objects = (objects: runtime.R2Objects): R2Objects =>
|
|
167
|
+
({
|
|
168
|
+
objects: objects.objects.map(wrapR2Object),
|
|
169
|
+
delimitedPrefixes: objects.delimitedPrefixes,
|
|
170
|
+
...("cursor" in objects ? { cursor: objects.cursor } : {}),
|
|
171
|
+
...("truncated" in objects ? { truncated: objects.truncated } : {}),
|
|
172
|
+
}) as R2Objects;
|
|
173
|
+
|
|
174
|
+
const wrapR2ObjectOrBody = (
|
|
175
|
+
object: runtime.R2Object | runtime.R2ObjectBody | null,
|
|
176
|
+
): R2Object | R2ObjectBody | null =>
|
|
177
|
+
object === null
|
|
178
|
+
? object
|
|
179
|
+
: isR2ObjectBody(object)
|
|
180
|
+
? wrapR2ObjectBody(object)
|
|
181
|
+
: wrapR2Object(object);
|
|
182
|
+
|
|
183
|
+
const wrapR2MultipartUpload = (
|
|
184
|
+
upload: runtime.R2MultipartUpload,
|
|
185
|
+
): R2MultipartUpload => ({
|
|
186
|
+
...upload,
|
|
187
|
+
raw: upload,
|
|
188
|
+
uploadId: upload.uploadId,
|
|
189
|
+
abort: () => tryPromise(() => upload.abort()),
|
|
190
|
+
complete: (uploadedParts: R2UploadedPart[]) =>
|
|
191
|
+
tryPromise(() => upload.complete(uploadedParts)).pipe(
|
|
192
|
+
Effect.map(wrapR2Object),
|
|
193
|
+
),
|
|
194
|
+
uploadPart: (
|
|
195
|
+
partNumber: number,
|
|
196
|
+
value:
|
|
197
|
+
| ReadableStream
|
|
198
|
+
| ArrayBuffer
|
|
199
|
+
| ArrayBufferView
|
|
200
|
+
| string
|
|
201
|
+
| Blob
|
|
202
|
+
| Stream.Stream<Uint8Array>,
|
|
203
|
+
options?: R2UploadPartOptions,
|
|
204
|
+
) =>
|
|
205
|
+
tryPromise(() =>
|
|
206
|
+
upload.uploadPart(
|
|
207
|
+
partNumber,
|
|
208
|
+
Stream.isStream(value)
|
|
209
|
+
? value.pipe(Stream.toReadableStream())
|
|
210
|
+
: (value as any),
|
|
211
|
+
options,
|
|
212
|
+
),
|
|
213
|
+
),
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const isR2ObjectBody = (object: any): object is runtime.R2ObjectBody =>
|
|
217
|
+
object !== null && typeof object === "object" && "body" in object;
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
raw: raw,
|
|
221
|
+
head: (key: string) =>
|
|
222
|
+
tryPromise(() => raw.head(key)).pipe(
|
|
223
|
+
Effect.map((object) => (object ? wrapR2Object(object) : object)),
|
|
224
|
+
),
|
|
225
|
+
get: (key: string, options?: R2GetOptions) =>
|
|
226
|
+
tryPromise(() => raw.get(key, options)).pipe(
|
|
227
|
+
Effect.map(wrapR2ObjectOrBody),
|
|
228
|
+
) as any,
|
|
229
|
+
put: (
|
|
230
|
+
key: string,
|
|
231
|
+
value:
|
|
232
|
+
| ReadableStream
|
|
233
|
+
| ArrayBuffer
|
|
234
|
+
| ArrayBufferView
|
|
235
|
+
| string
|
|
236
|
+
| null
|
|
237
|
+
| Blob
|
|
238
|
+
| Stream.Stream<Uint8Array>,
|
|
239
|
+
options?: R2PutOptions & { onlyIf: R2Conditional | Headers },
|
|
240
|
+
) =>
|
|
241
|
+
tryPromise(() =>
|
|
242
|
+
raw.put(
|
|
243
|
+
key,
|
|
244
|
+
Stream.isStream(value)
|
|
245
|
+
? value.pipe(Stream.toReadableStream())
|
|
246
|
+
: (value as any),
|
|
247
|
+
options,
|
|
248
|
+
),
|
|
249
|
+
).pipe(Effect.map(wrapR2ObjectOrBody)) as any,
|
|
250
|
+
delete: (keys: string | string[]) => tryPromise(() => raw.delete(keys)),
|
|
251
|
+
list: (options?: R2ListOptions) =>
|
|
252
|
+
tryPromise(() => raw.list(options)).pipe(Effect.map(wrapR2Objects)),
|
|
253
|
+
createMultipartUpload: (key: string, options?: R2MultipartOptions) =>
|
|
254
|
+
tryPromise(() => raw.createMultipartUpload(key, options)).pipe(
|
|
255
|
+
Effect.map(wrapR2MultipartUpload),
|
|
256
|
+
),
|
|
257
|
+
resumeMultipartUpload: (key: string, uploadId: string) =>
|
|
258
|
+
Effect.sync(() => raw.resumeMultipartUpload(key, uploadId)).pipe(
|
|
259
|
+
Effect.map(wrapR2MultipartUpload),
|
|
260
|
+
),
|
|
261
|
+
} satisfies R2BucketClient as R2BucketClient;
|
|
262
|
+
});
|
|
263
|
+
}),
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
export class BucketBindingPolicy extends Binding.Policy<
|
|
267
|
+
BucketBindingPolicy,
|
|
268
|
+
(bucket: R2Bucket) => Effect.Effect<void>
|
|
269
|
+
>()("Cloudflare.R2Bucket") {}
|
|
270
|
+
|
|
271
|
+
export const R2BucketBindingPolicyLive = BucketBindingPolicy.layer.succeed(
|
|
272
|
+
(host, bucket) => R2BucketClient(host, bucket),
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
export const R2BucketClient = Effect.fn(function* (
|
|
276
|
+
host: ResourceLike,
|
|
277
|
+
bucket: R2Bucket,
|
|
278
|
+
) {
|
|
279
|
+
if (isWorker(host)) {
|
|
280
|
+
yield* host.bind`Bind(${bucket})`({
|
|
281
|
+
bindings: [
|
|
282
|
+
{
|
|
283
|
+
type: "r2_bucket",
|
|
284
|
+
name: bucket.LogicalId,
|
|
285
|
+
bucketName: bucket.bucketName,
|
|
286
|
+
jurisdiction: bucket.jurisdiction.pipe(
|
|
287
|
+
Output.map((jurisdiction) =>
|
|
288
|
+
jurisdiction === "default" ? undefined : jurisdiction,
|
|
289
|
+
),
|
|
290
|
+
),
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
});
|
|
294
|
+
} else {
|
|
295
|
+
return yield* Effect.die(
|
|
296
|
+
new Error(`BucketBinding does not support runtime '${host.Type}'`),
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
@@ -1,9 +1,2 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./
|
|
3
|
-
export * from "./DeleteObject.ts";
|
|
4
|
-
export * from "./GetObject.ts";
|
|
5
|
-
export * from "./HeadObject.ts";
|
|
6
|
-
export * from "./ListObjects.ts";
|
|
7
|
-
export * from "./MultipartUploadClient.ts";
|
|
8
|
-
export * from "./PutObject.ts";
|
|
9
|
-
export * from "./ResumeMultipartUpload.ts";
|
|
1
|
+
export * from "./R2Bucket.ts";
|
|
2
|
+
export * from "./R2BucketBinding.ts";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
+
// import type * as cf from "@cloudflare/workers-types";
|
|
3
|
+
|
|
4
|
+
import type * as Effect from "effect/Effect";
|
|
5
|
+
import type { UnwrapEffect } from "../../Util/effect.ts";
|
|
6
|
+
import type * as Cloudflare from "../index.ts";
|
|
7
|
+
import type { Worker } from "./Worker.ts";
|
|
8
|
+
|
|
9
|
+
export type InferEnv<W> = W extends
|
|
10
|
+
| Worker<infer Bindings>
|
|
11
|
+
| Effect.Effect<Worker<infer Bindings>, any, any>
|
|
12
|
+
? {
|
|
13
|
+
[K in keyof Bindings]: GetBindingType<UnwrapEffect<Bindings[K]>>;
|
|
14
|
+
}
|
|
15
|
+
: never;
|
|
16
|
+
|
|
17
|
+
type GetBindingType<T> = T extends Cloudflare.D1Database
|
|
18
|
+
? D1Database
|
|
19
|
+
: T extends Cloudflare.R2Bucket
|
|
20
|
+
? R2Bucket
|
|
21
|
+
: T extends Cloudflare.KVNamespace
|
|
22
|
+
? KVNamespace
|
|
23
|
+
: never;
|