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.
Files changed (41) hide show
  1. package/bin/alchemy-effect.js +9 -7
  2. package/bin/alchemy-effect.js.map +1 -1
  3. package/bin/alchemy-effect.ts +11 -0
  4. package/package.json +1 -1
  5. package/src/AWS/AutoScaling/LaunchTemplate.ts +5 -4
  6. package/src/AWS/EC2/Instance.ts +3 -4
  7. package/src/AWS/ECS/Task.ts +36 -34
  8. package/src/AWS/Lambda/Function.ts +68 -68
  9. package/src/Binding.ts +10 -3
  10. package/src/Cloudflare/Container.ts +68 -67
  11. package/src/Cloudflare/D1/D1Database.ts +32 -32
  12. package/src/Cloudflare/KV/Delete.ts +7 -5
  13. package/src/Cloudflare/KV/Get.ts +5 -5
  14. package/src/Cloudflare/KV/GetWithMetadata.ts +5 -5
  15. package/src/Cloudflare/KV/{Namespace.ts → KVNamespace.ts} +6 -6
  16. package/src/Cloudflare/KV/{NamespaceBinding.ts → KVNamespaceBinding.ts} +2 -2
  17. package/src/Cloudflare/KV/List.ts +5 -5
  18. package/src/Cloudflare/KV/Put.ts +5 -5
  19. package/src/Cloudflare/KV/index.ts +1 -1
  20. package/src/Cloudflare/Providers.ts +2 -8
  21. package/src/Cloudflare/R2/{Bucket.ts → R2Bucket.ts} +19 -19
  22. package/src/Cloudflare/R2/R2BucketBinding.ts +299 -0
  23. package/src/Cloudflare/R2/index.ts +2 -9
  24. package/src/Cloudflare/Workers/InferEnv.ts +23 -0
  25. package/src/Cloudflare/Workers/Worker.ts +185 -108
  26. package/src/Cloudflare/Workers/index.ts +1 -0
  27. package/src/Cloudflare/index.ts +2 -2
  28. package/src/Plan.ts +12 -0
  29. package/src/Platform.ts +39 -55
  30. package/src/Test/Vitest.ts +15 -2
  31. package/src/Util/effect.ts +24 -0
  32. package/src/Cloudflare/R2/BucketBinding.ts +0 -31
  33. package/src/Cloudflare/R2/CreateMultipartUpload.ts +0 -59
  34. package/src/Cloudflare/R2/DeleteObject.ts +0 -41
  35. package/src/Cloudflare/R2/GetObject.ts +0 -47
  36. package/src/Cloudflare/R2/HeadObject.ts +0 -41
  37. package/src/Cloudflare/R2/ListObjects.ts +0 -45
  38. package/src/Cloudflare/R2/MultipartUploadClient.ts +0 -40
  39. package/src/Cloudflare/R2/PutObject.ts +0 -55
  40. package/src/Cloudflare/R2/ResumeMultipartUpload.ts +0 -48
  41. 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 { Namespace } from "./Namespace.ts";
7
- import { NamespaceBinding } from "./NamespaceBinding.ts";
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: 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: 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: Namespace) => Effect.Effect<void>
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 interface Namespace extends Resource<
25
- "Cloudflare.KV.Namespace",
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 Namespace = Resource<Namespace>("Cloudflare.KV.Namespace");
35
+ export const KVNamespace = Resource<KVNamespace>("Cloudflare.KVNamespace");
36
36
 
37
37
  export const NamespaceProvider = (): Layer<
38
- Provider<Namespace>,
38
+ Provider<KVNamespace>,
39
39
  never,
40
40
  Account | Credentials | HttpClient | Stack | Stage
41
41
  > =>
42
- Namespace.provider.effect(
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 { Namespace } from "./Namespace.ts";
4
+ import type { KVNamespace } from "./KVNamespace.ts";
5
5
 
6
6
  export const NamespaceBinding = Effect.fn(function* (
7
7
  host: ResourceLike,
8
- namespace: 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 { Namespace } from "./Namespace.ts";
7
- import { NamespaceBinding } from "./NamespaceBinding.ts";
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: 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: 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: Namespace) => Effect.Effect<void>
42
+ (namespace: KVNamespace) => Effect.Effect<void>
43
43
  >()("Cloudflare.KV.List") {}
44
44
 
45
45
  export const ListPolicyLive = ListPolicy.layer.succeed(NamespaceBinding);
@@ -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 { Namespace } from "./Namespace.ts";
9
- import { NamespaceBinding } from "./NamespaceBinding.ts";
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: 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: 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: Namespace) => Effect.Effect<void>
56
+ (namespace: KVNamespace) => Effect.Effect<void>
57
57
  >()("Cloudflare.KV.Put") {}
58
58
 
59
59
  export const PutPolicyLive = PutPolicy.layer.succeed(NamespaceBinding);
@@ -1,6 +1,6 @@
1
1
  export * from "./Delete.ts";
2
2
  export * from "./Get.ts";
3
3
  export * from "./GetWithMetadata.ts";
4
+ export * from "./KVNamespace.ts";
4
5
  export * from "./List.ts";
5
- export * from "./Namespace.ts";
6
6
  export * from "./Put.ts";
@@ -53,7 +53,7 @@ export const resources = () =>
53
53
  WorkflowProvider(),
54
54
  D1.DatabaseProvider(),
55
55
  KV.NamespaceProvider(),
56
- R2.BucketProvider(),
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.GetObjectPolicyLive,
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 BucketName = string;
9
+ export type R2BucketName = string;
10
10
 
11
- export type BucketProps = {
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?: Bucket.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?: Bucket.Jurisdiction;
26
+ jurisdiction?: R2Bucket.Jurisdiction;
27
27
  /**
28
28
  * Location hint for the bucket.
29
29
  */
30
- locationHint?: Bucket.Location;
30
+ locationHint?: R2Bucket.Location;
31
31
  };
32
32
 
33
- export interface Bucket extends Resource<
34
- "Cloudflare.R2.Bucket",
35
- BucketProps,
33
+ export type R2Bucket = Resource<
34
+ "Cloudflare.R2Bucket",
35
+ R2BucketProps,
36
36
  {
37
- bucketName: BucketName;
38
- storageClass: Bucket.StorageClass;
39
- jurisdiction: Bucket.Jurisdiction;
40
- location: Bucket.Location | undefined;
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 Bucket = Resource<Bucket>("Cloudflare.R2.Bucket");
45
+ export const R2Bucket = Resource<R2Bucket>("Cloudflare.R2Bucket");
46
46
 
47
- export declare namespace Bucket {
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 BucketProvider = () =>
54
- Bucket.provider.effect(
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
- ): Bucket.Location | undefined => {
73
+ ): R2Bucket.Location | undefined => {
74
74
  if (!location) return undefined;
75
- return location.toLowerCase() as Bucket.Location;
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 "./Bucket.ts";
2
- export * from "./CreateMultipartUpload.ts";
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;