alchemy 2.0.0-beta.35 → 2.0.0-beta.37
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/exec.js +176 -26
- package/bin/exec.js.map +1 -1
- package/lib/AWS/Lambda/Function.d.ts.map +1 -1
- package/lib/AWS/Lambda/Function.js +22 -2
- package/lib/AWS/Lambda/Function.js.map +1 -1
- package/lib/Apply.d.ts.map +1 -1
- package/lib/Apply.js +6 -1
- package/lib/Apply.js.map +1 -1
- package/lib/Cli/main.d.ts.map +1 -1
- package/lib/Cli/main.js +2 -0
- package/lib/Cli/main.js.map +1 -1
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.d.ts +54 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.d.ts.map +1 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.js +17 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.js.map +1 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.d.ts +37 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.d.ts.map +1 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.js +45 -0
- package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.js.map +1 -0
- package/lib/Cloudflare/AnalyticsEngine/index.d.ts +3 -0
- package/lib/Cloudflare/AnalyticsEngine/index.d.ts.map +1 -0
- package/lib/Cloudflare/AnalyticsEngine/index.js +3 -0
- package/lib/Cloudflare/AnalyticsEngine/index.js.map +1 -0
- package/lib/Cloudflare/D1/D1Connection.d.ts +47 -3
- package/lib/Cloudflare/D1/D1Connection.d.ts.map +1 -1
- package/lib/Cloudflare/D1/D1Connection.js +62 -5
- package/lib/Cloudflare/D1/D1Connection.js.map +1 -1
- package/lib/Cloudflare/Images/Images.d.ts +103 -0
- package/lib/Cloudflare/Images/Images.d.ts.map +1 -0
- package/lib/Cloudflare/Images/Images.js +82 -0
- package/lib/Cloudflare/Images/Images.js.map +1 -0
- package/lib/Cloudflare/Images/ImagesBinding.d.ts +73 -0
- package/lib/Cloudflare/Images/ImagesBinding.d.ts.map +1 -0
- package/lib/Cloudflare/Images/ImagesBinding.js +83 -0
- package/lib/Cloudflare/Images/ImagesBinding.js.map +1 -0
- package/lib/Cloudflare/Images/index.d.ts +3 -0
- package/lib/Cloudflare/Images/index.d.ts.map +1 -0
- package/lib/Cloudflare/Images/index.js +3 -0
- package/lib/Cloudflare/Images/index.js.map +1 -0
- package/lib/Cloudflare/Local/SidecarHandlers.js +9 -1
- package/lib/Cloudflare/Local/SidecarHandlers.js.map +1 -1
- package/lib/Cloudflare/Providers.d.ts.map +1 -1
- package/lib/Cloudflare/Providers.js +6 -1
- package/lib/Cloudflare/Providers.js.map +1 -1
- package/lib/Cloudflare/R2/R2Bucket.d.ts +135 -0
- package/lib/Cloudflare/R2/R2Bucket.d.ts.map +1 -1
- package/lib/Cloudflare/R2/R2Bucket.js +146 -7
- package/lib/Cloudflare/R2/R2Bucket.js.map +1 -1
- package/lib/Cloudflare/StateStore/Api.d.ts +6 -1
- package/lib/Cloudflare/StateStore/Api.d.ts.map +1 -1
- package/lib/Cloudflare/StateStore/Api.js +23 -1
- package/lib/Cloudflare/StateStore/Api.js.map +1 -1
- package/lib/Cloudflare/StateStore/Store.d.ts +16 -1
- package/lib/Cloudflare/StateStore/Store.d.ts.map +1 -1
- package/lib/Cloudflare/StateStore/Store.js +22 -5
- package/lib/Cloudflare/StateStore/Store.js.map +1 -1
- package/lib/Cloudflare/Workers/CronEventSource.d.ts +35 -0
- package/lib/Cloudflare/Workers/CronEventSource.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/CronEventSource.js +55 -0
- package/lib/Cloudflare/Workers/CronEventSource.js.map +1 -0
- package/lib/Cloudflare/Workers/InferEnv.d.ts +37 -2
- package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js +2 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
- package/lib/Cloudflare/Workers/Rpc.d.ts +76 -3
- package/lib/Cloudflare/Workers/Rpc.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Rpc.js +197 -13
- package/lib/Cloudflare/Workers/Rpc.js.map +1 -1
- package/lib/Cloudflare/Workers/Worker.d.ts +26 -6
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js +183 -41
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBundle.js +15 -5
- package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
- package/lib/Cloudflare/Workers/Workflow.d.ts +50 -47
- package/lib/Cloudflare/Workers/Workflow.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Workflow.js +41 -35
- package/lib/Cloudflare/Workers/Workflow.js.map +1 -1
- package/lib/Cloudflare/Workers/index.d.ts +1 -0
- package/lib/Cloudflare/Workers/index.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/index.js +1 -0
- package/lib/Cloudflare/Workers/index.js.map +1 -1
- package/lib/Cloudflare/index.d.ts +2 -0
- package/lib/Cloudflare/index.d.ts.map +1 -1
- package/lib/Cloudflare/index.js +2 -0
- package/lib/Cloudflare/index.js.map +1 -1
- package/lib/Deploy.d.ts +4 -1
- package/lib/Deploy.d.ts.map +1 -1
- package/lib/Deploy.js +2 -2
- package/lib/Deploy.js.map +1 -1
- package/lib/Destroy.d.ts +4 -1
- package/lib/Destroy.d.ts.map +1 -1
- package/lib/Destroy.js +2 -2
- package/lib/Destroy.js.map +1 -1
- package/lib/Output.d.ts +45 -5
- package/lib/Output.d.ts.map +1 -1
- package/lib/Output.js +100 -4
- package/lib/Output.js.map +1 -1
- package/lib/Plan.d.ts.map +1 -1
- package/lib/Plan.js +93 -12
- package/lib/Plan.js.map +1 -1
- package/lib/Platform.d.ts.map +1 -1
- package/lib/Platform.js +16 -3
- package/lib/Platform.js.map +1 -1
- package/lib/Ref.d.ts +4 -1
- package/lib/Ref.d.ts.map +1 -1
- package/lib/Ref.js +1 -1
- package/lib/Ref.js.map +1 -1
- package/lib/Resource.d.ts +8 -0
- package/lib/Resource.d.ts.map +1 -1
- package/lib/Resource.js +10 -0
- package/lib/Resource.js.map +1 -1
- package/lib/RuntimeContext.d.ts +3 -1
- package/lib/RuntimeContext.d.ts.map +1 -1
- package/lib/RuntimeContext.js.map +1 -1
- package/lib/Secret.d.ts +50 -0
- package/lib/Secret.d.ts.map +1 -0
- package/lib/Secret.js +23 -0
- package/lib/Secret.js.map +1 -0
- package/lib/Serverless/Function.d.ts +9 -1
- package/lib/Serverless/Function.d.ts.map +1 -1
- package/lib/Stack.d.ts +14 -5
- package/lib/Stack.d.ts.map +1 -1
- package/lib/Stack.js +31 -19
- package/lib/Stack.js.map +1 -1
- package/lib/State/HttpStateApi.d.ts +15 -1
- package/lib/State/HttpStateApi.d.ts.map +1 -1
- package/lib/State/HttpStateApi.js +11 -0
- package/lib/State/HttpStateApi.js.map +1 -1
- package/lib/State/HttpStateStore.d.ts.map +1 -1
- package/lib/State/HttpStateStore.js +11 -0
- package/lib/State/HttpStateStore.js.map +1 -1
- package/lib/State/InMemoryState.d.ts +2 -2
- package/lib/State/InMemoryState.d.ts.map +1 -1
- package/lib/State/InMemoryState.js +12 -6
- package/lib/State/InMemoryState.js.map +1 -1
- package/lib/State/LocalState.d.ts.map +1 -1
- package/lib/State/LocalState.js +11 -2
- package/lib/State/LocalState.js.map +1 -1
- package/lib/State/State.d.ts +21 -0
- package/lib/State/State.d.ts.map +1 -1
- package/lib/Test/Bun.d.ts.map +1 -1
- package/lib/Test/Bun.js +30 -7
- package/lib/Test/Bun.js.map +1 -1
- package/lib/Test/Core.d.ts +17 -2
- package/lib/Test/Core.d.ts.map +1 -1
- package/lib/Test/Core.js +29 -11
- package/lib/Test/Core.js.map +1 -1
- package/lib/Test/Vitest.d.ts.map +1 -1
- package/lib/Test/Vitest.js +26 -5
- package/lib/Test/Vitest.js.map +1 -1
- package/lib/Util/LocalhostDns.d.ts +23 -0
- package/lib/Util/LocalhostDns.d.ts.map +1 -0
- package/lib/Util/LocalhostDns.js +46 -0
- package/lib/Util/LocalhostDns.js.map +1 -0
- package/lib/Util/PlatformServices.d.ts.map +1 -1
- package/lib/Util/PlatformServices.js +2 -0
- package/lib/Util/PlatformServices.js.map +1 -1
- package/lib/Util/effect.d.ts +7 -0
- package/lib/Util/effect.d.ts.map +1 -1
- package/lib/Util/effect.js +6 -0
- package/lib/Util/effect.js.map +1 -1
- package/lib/Variable.d.ts +29 -0
- package/lib/Variable.d.ts.map +1 -0
- package/lib/Variable.js +9 -0
- package/lib/Variable.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/AWS/Lambda/Function.ts +30 -2
- package/src/Apply.ts +8 -1
- package/src/Cli/main.ts +3 -0
- package/src/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.ts +82 -0
- package/src/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.ts +106 -0
- package/src/Cloudflare/AnalyticsEngine/index.ts +2 -0
- package/src/Cloudflare/D1/D1Connection.ts +95 -12
- package/src/Cloudflare/Images/Images.ts +121 -0
- package/src/Cloudflare/Images/ImagesBinding.ts +192 -0
- package/src/Cloudflare/Images/index.ts +2 -0
- package/src/Cloudflare/Local/SidecarHandlers.ts +9 -1
- package/src/Cloudflare/Providers.ts +8 -0
- package/src/Cloudflare/R2/R2Bucket.ts +271 -7
- package/src/Cloudflare/StateStore/Api.ts +34 -1
- package/src/Cloudflare/StateStore/Store.ts +40 -10
- package/src/Cloudflare/Workers/CronEventSource.ts +89 -0
- package/src/Cloudflare/Workers/InferEnv.ts +94 -16
- package/src/Cloudflare/Workers/LocalWorkerProvider.ts +9 -1
- package/src/Cloudflare/Workers/Rpc.ts +227 -18
- package/src/Cloudflare/Workers/Worker.ts +295 -62
- package/src/Cloudflare/Workers/WorkerBundle.ts +15 -5
- package/src/Cloudflare/Workers/Workflow.ts +74 -70
- package/src/Cloudflare/Workers/index.ts +1 -0
- package/src/Cloudflare/index.ts +2 -0
- package/src/Deploy.ts +5 -1
- package/src/Destroy.ts +5 -1
- package/src/Output.ts +121 -12
- package/src/Plan.ts +101 -12
- package/src/Platform.ts +19 -4
- package/src/Ref.ts +10 -5
- package/src/Resource.ts +22 -0
- package/src/RuntimeContext.ts +1 -0
- package/src/Secret.ts +87 -0
- package/src/Serverless/Function.ts +10 -1
- package/src/Stack.ts +52 -29
- package/src/State/HttpStateApi.ts +21 -0
- package/src/State/HttpStateStore.ts +21 -0
- package/src/State/InMemoryState.ts +29 -8
- package/src/State/LocalState.ts +28 -4
- package/src/State/State.ts +21 -0
- package/src/Test/Bun.ts +43 -7
- package/src/Test/Core.ts +29 -8
- package/src/Test/Vitest.ts +36 -4
- package/src/Util/LocalhostDns.ts +52 -0
- package/src/Util/PlatformServices.ts +2 -0
- package/src/Util/effect.ts +25 -0
- package/src/Variable.ts +44 -0
- package/src/index.ts +3 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type * as cf 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 type { ResourceLike } from "../../Resource.ts";
|
|
8
|
+
import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts";
|
|
9
|
+
import { type Images as ImagesLike } from "./Images.ts";
|
|
10
|
+
|
|
11
|
+
export class ImagesError extends Data.TaggedError("ImagesError")<{
|
|
12
|
+
message: string;
|
|
13
|
+
code?: number;
|
|
14
|
+
cause: unknown;
|
|
15
|
+
}> {}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Effect-native handle to the result of `input(...).output(...)`.
|
|
19
|
+
* Mirrors the runtime `ImageTransformationResult` but exposes side
|
|
20
|
+
* effects (response/image/contentType reads) as plain sync effects.
|
|
21
|
+
*/
|
|
22
|
+
export interface ImageTransformationResultClient {
|
|
23
|
+
raw: cf.ImageTransformationResult;
|
|
24
|
+
response: Effect.Effect<cf.Response>;
|
|
25
|
+
contentType: Effect.Effect<string>;
|
|
26
|
+
image(
|
|
27
|
+
options?: cf.ImageTransformationOutputOptions,
|
|
28
|
+
): Effect.Effect<cf.ReadableStream<Uint8Array>>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Effect-native chainable transformer. `transform`/`draw` are pure
|
|
33
|
+
* (return a new client wrapping the next runtime transformer);
|
|
34
|
+
* `output` is the only step that crosses into Cloudflare's runtime
|
|
35
|
+
* and therefore returns an Effect.
|
|
36
|
+
*/
|
|
37
|
+
export interface ImageTransformerClient {
|
|
38
|
+
raw: cf.ImageTransformer;
|
|
39
|
+
transform(transform: cf.ImageTransform): ImageTransformerClient;
|
|
40
|
+
draw<E = never, R = never>(
|
|
41
|
+
image: Stream.Stream<Uint8Array, E, R> | ImageTransformerClient,
|
|
42
|
+
options?: cf.ImageDrawOptions,
|
|
43
|
+
): Effect.Effect<ImageTransformerClient, never, R>;
|
|
44
|
+
output(
|
|
45
|
+
options: cf.ImageOutputOptions,
|
|
46
|
+
): Effect.Effect<ImageTransformationResultClient, ImagesError>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Effect-native client for a Cloudflare Images binding.
|
|
51
|
+
*
|
|
52
|
+
* Wraps the runtime {@link cf.ImagesBinding} so each method returns
|
|
53
|
+
* an Effect tagged with {@link ImagesError}. Use
|
|
54
|
+
* `Cloudflare.Images.bind(images)` inside a Worker's init phase.
|
|
55
|
+
*/
|
|
56
|
+
export interface ImagesClient {
|
|
57
|
+
/** Effect resolving to the raw Cloudflare runtime binding. */
|
|
58
|
+
raw: Effect.Effect<cf.ImagesBinding, never, WorkerEnvironment>;
|
|
59
|
+
/**
|
|
60
|
+
* Read image format and dimensions from a stream of bytes.
|
|
61
|
+
* Fails with {@link ImagesError} (code 9412) if the input is not
|
|
62
|
+
* a recognized image.
|
|
63
|
+
*/
|
|
64
|
+
info<E = never, R = never>(
|
|
65
|
+
stream: Stream.Stream<Uint8Array, E, R>,
|
|
66
|
+
options?: cf.ImageInputOptions,
|
|
67
|
+
): Effect.Effect<cf.ImageInfoResponse, ImagesError, WorkerEnvironment | R>;
|
|
68
|
+
/**
|
|
69
|
+
* Begin a transformation pipeline. Subsequent `.transform()` /
|
|
70
|
+
* `.draw()` calls are pure; `.output(opts)` runs the pipeline.
|
|
71
|
+
*/
|
|
72
|
+
input<E = never, R = never>(
|
|
73
|
+
stream: Stream.Stream<Uint8Array, E, R>,
|
|
74
|
+
options?: cf.ImageInputOptions,
|
|
75
|
+
): Effect.Effect<ImageTransformerClient, never, WorkerEnvironment | R>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class ImagesBinding extends Binding.Service<
|
|
79
|
+
ImagesBinding,
|
|
80
|
+
(images: ImagesLike) => Effect.Effect<ImagesClient>
|
|
81
|
+
>()("Cloudflare.Images.Binding") {}
|
|
82
|
+
|
|
83
|
+
export const ImagesBindingLive = Layer.effect(
|
|
84
|
+
ImagesBinding,
|
|
85
|
+
Effect.gen(function* () {
|
|
86
|
+
const Policy = yield* ImagesBindingPolicy;
|
|
87
|
+
|
|
88
|
+
return Effect.fn(function* (images: ImagesLike) {
|
|
89
|
+
yield* Policy(images);
|
|
90
|
+
const env = WorkerEnvironment.asEffect();
|
|
91
|
+
const raw = env.pipe(
|
|
92
|
+
Effect.map(
|
|
93
|
+
(env) => (env as Record<string, cf.ImagesBinding>)[images.name]!,
|
|
94
|
+
),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
raw,
|
|
99
|
+
info: (stream, options) =>
|
|
100
|
+
Effect.gen(function* () {
|
|
101
|
+
const binding = yield* raw;
|
|
102
|
+
const readable = yield* toCfReadable(stream);
|
|
103
|
+
return yield* tryPromise(() => binding.info(readable, options));
|
|
104
|
+
}),
|
|
105
|
+
input: (stream, options) =>
|
|
106
|
+
Effect.gen(function* () {
|
|
107
|
+
const binding = yield* raw;
|
|
108
|
+
const readable = yield* toCfReadable(stream);
|
|
109
|
+
return wrapTransformer(binding.input(readable, options));
|
|
110
|
+
}),
|
|
111
|
+
} satisfies ImagesClient;
|
|
112
|
+
});
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
export class ImagesBindingPolicy extends Binding.Policy<
|
|
117
|
+
ImagesBindingPolicy,
|
|
118
|
+
(images: ImagesLike) => Effect.Effect<void>
|
|
119
|
+
>()("Cloudflare.Images.Binding") {}
|
|
120
|
+
|
|
121
|
+
export const ImagesBindingPolicyLive = ImagesBindingPolicy.layer.succeed(
|
|
122
|
+
Effect.fn(function* (host: ResourceLike, images: ImagesLike) {
|
|
123
|
+
if (isWorker(host)) {
|
|
124
|
+
yield* host.bind(images.name, {
|
|
125
|
+
bindings: [
|
|
126
|
+
{
|
|
127
|
+
type: "images",
|
|
128
|
+
name: images.name,
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
});
|
|
132
|
+
} else {
|
|
133
|
+
return yield* Effect.die(
|
|
134
|
+
new Error(`ImagesBinding does not support runtime '${host.Type}'`),
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const tryPromise = <T>(fn: () => Promise<T>): Effect.Effect<T, ImagesError> =>
|
|
141
|
+
Effect.tryPromise({
|
|
142
|
+
try: fn,
|
|
143
|
+
catch: (error: any) =>
|
|
144
|
+
new ImagesError({
|
|
145
|
+
message: error?.message ?? "Unknown error",
|
|
146
|
+
code: typeof error?.code === "number" ? error.code : undefined,
|
|
147
|
+
cause: error,
|
|
148
|
+
}),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Convert an Effect `Stream<Uint8Array>` into the `cf.ReadableStream<Uint8Array>`
|
|
153
|
+
* shape that the Cloudflare Images runtime binding expects. The two
|
|
154
|
+
* `ReadableStream` types only differ at the type level; at runtime they
|
|
155
|
+
* are the same Web Streams API.
|
|
156
|
+
*/
|
|
157
|
+
const toCfReadable = <E, R>(
|
|
158
|
+
stream: Stream.Stream<Uint8Array, E, R>,
|
|
159
|
+
): Effect.Effect<cf.ReadableStream<Uint8Array>, never, R> =>
|
|
160
|
+
Stream.toReadableStreamEffect(stream).pipe(
|
|
161
|
+
Effect.map((s) => s as unknown as cf.ReadableStream<Uint8Array>),
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const isTransformerClient = (image: unknown): image is ImageTransformerClient =>
|
|
165
|
+
typeof image === "object" && image !== null && "raw" in image;
|
|
166
|
+
|
|
167
|
+
const wrapTransformer = (raw: cf.ImageTransformer): ImageTransformerClient => ({
|
|
168
|
+
raw,
|
|
169
|
+
transform: (transform) => wrapTransformer(raw.transform(transform)),
|
|
170
|
+
draw: <E, R>(
|
|
171
|
+
image: Stream.Stream<Uint8Array, E, R> | ImageTransformerClient,
|
|
172
|
+
options?: cf.ImageDrawOptions,
|
|
173
|
+
): Effect.Effect<ImageTransformerClient, never, R> => {
|
|
174
|
+
if (isTransformerClient(image)) {
|
|
175
|
+
return Effect.succeed(wrapTransformer(raw.draw(image.raw, options)));
|
|
176
|
+
}
|
|
177
|
+
return toCfReadable(image).pipe(
|
|
178
|
+
Effect.map((readable) => wrapTransformer(raw.draw(readable, options))),
|
|
179
|
+
);
|
|
180
|
+
},
|
|
181
|
+
output: (options) =>
|
|
182
|
+
tryPromise(() => raw.output(options)).pipe(Effect.map(wrapResult)),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const wrapResult = (
|
|
186
|
+
raw: cf.ImageTransformationResult,
|
|
187
|
+
): ImageTransformationResultClient => ({
|
|
188
|
+
raw,
|
|
189
|
+
response: Effect.sync(() => raw.response()),
|
|
190
|
+
contentType: Effect.sync(() => raw.contentType()),
|
|
191
|
+
image: (options) => Effect.sync(() => raw.image(options)),
|
|
192
|
+
});
|
|
@@ -169,7 +169,15 @@ function bundleOutputToWorkerModules(
|
|
|
169
169
|
): WorkerModule[] {
|
|
170
170
|
const modules: WorkerModule[] = [];
|
|
171
171
|
for (const file of bundle.files) {
|
|
172
|
-
if (file.path.endsWith(".map")
|
|
172
|
+
if (file.path.endsWith(".map")) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (file.content instanceof Uint8Array) {
|
|
176
|
+
modules.push({
|
|
177
|
+
name: file.path,
|
|
178
|
+
type: file.path.endsWith(".wasm") ? "Wasm" : "Data",
|
|
179
|
+
content: file.content,
|
|
180
|
+
});
|
|
173
181
|
continue;
|
|
174
182
|
}
|
|
175
183
|
modules.push({
|
|
@@ -13,6 +13,7 @@ import { ProfileLive } from "../Auth/Profile.ts";
|
|
|
13
13
|
import { Random, RandomProvider } from "../Random.ts";
|
|
14
14
|
import * as Access from "./Access.ts";
|
|
15
15
|
import * as AiGateway from "./AiGateway/index.ts";
|
|
16
|
+
import * as AnalyticsEngine from "./AnalyticsEngine/index.ts";
|
|
16
17
|
import * as ApiToken from "./ApiToken/index.ts";
|
|
17
18
|
import * as Artifacts from "./Artifacts/index.ts";
|
|
18
19
|
import { CloudflareAuth } from "./Auth/AuthProvider.ts";
|
|
@@ -21,6 +22,7 @@ import * as Containers from "./Container/index.ts";
|
|
|
21
22
|
import * as Credentials from "./Credentials.ts";
|
|
22
23
|
import * as D1 from "./D1/index.ts";
|
|
23
24
|
import * as Hyperdrive from "./Hyperdrive/index.ts";
|
|
25
|
+
import * as Images from "./Images/index.ts";
|
|
24
26
|
import * as KV from "./KV/index.ts";
|
|
25
27
|
import * as Queue from "./Queue/index.ts";
|
|
26
28
|
import * as R2 from "./R2/index.ts";
|
|
@@ -49,6 +51,7 @@ export const providers = () =>
|
|
|
49
51
|
ApiToken.UserApiToken,
|
|
50
52
|
AiGateway.AiGateway,
|
|
51
53
|
AiGateway.AiGatewayBindingPolicy,
|
|
54
|
+
AnalyticsEngine.AnalyticsEngineDatasetBindingPolicy,
|
|
52
55
|
Artifacts.ArtifactsBindingPolicy,
|
|
53
56
|
Command,
|
|
54
57
|
Containers.Container,
|
|
@@ -56,6 +59,7 @@ export const providers = () =>
|
|
|
56
59
|
D1.D1Database,
|
|
57
60
|
Hyperdrive.Hyperdrive,
|
|
58
61
|
Hyperdrive.HyperdriveBindingPolicy,
|
|
62
|
+
Images.ImagesBindingPolicy,
|
|
59
63
|
KV.KVNamespace,
|
|
60
64
|
KV.KVNamespaceBindingPolicy,
|
|
61
65
|
Queue.Queue,
|
|
@@ -71,6 +75,7 @@ export const providers = () =>
|
|
|
71
75
|
VpcService.VpcService,
|
|
72
76
|
Random,
|
|
73
77
|
Workers.BindWorkerPolicy,
|
|
78
|
+
Workers.CronEventSourcePolicy,
|
|
74
79
|
Workers.FetchPolicy,
|
|
75
80
|
Workers.Worker,
|
|
76
81
|
Workflows.WorkflowResource,
|
|
@@ -82,12 +87,14 @@ export const providers = () =>
|
|
|
82
87
|
ApiToken.UserApiTokenProvider(),
|
|
83
88
|
AiGateway.AiGatewayProvider(),
|
|
84
89
|
AiGateway.AiGatewayBindingPolicyLive,
|
|
90
|
+
AnalyticsEngine.AnalyticsEngineDatasetBindingPolicyLive,
|
|
85
91
|
Artifacts.ArtifactsBindingPolicyLive,
|
|
86
92
|
Containers.ContainerProvider(),
|
|
87
93
|
D1.D1ConnectionPolicyLive,
|
|
88
94
|
D1.DatabaseProvider(),
|
|
89
95
|
Hyperdrive.HyperdriveBindingPolicyLive,
|
|
90
96
|
Hyperdrive.HyperdriveProvider(),
|
|
97
|
+
Images.ImagesBindingPolicyLive,
|
|
91
98
|
KV.KVNamespaceBindingPolicyLive,
|
|
92
99
|
KV.KVNamespaceProvider(),
|
|
93
100
|
Queue.QueueBindingPolicyLive,
|
|
@@ -102,6 +109,7 @@ export const providers = () =>
|
|
|
102
109
|
Tunnel.TunnelProvider(),
|
|
103
110
|
VpcService.VpcServiceProvider(),
|
|
104
111
|
Workers.BindWorkerPolicyLive,
|
|
112
|
+
Workers.CronEventSourcePolicyLive,
|
|
105
113
|
Workers.FetchPolicyLive,
|
|
106
114
|
Workers.WorkerProvider(),
|
|
107
115
|
Workflows.WorkflowProvider(),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as r2 from "@distilled.cloud/cloudflare/r2";
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import * as Schedule from "effect/Schedule";
|
|
4
|
+
import * as Stream from "effect/Stream";
|
|
4
5
|
|
|
5
6
|
import { deepEqual, isResolved } from "../../Diff.ts";
|
|
6
7
|
import { createPhysicalName } from "../../PhysicalName.ts";
|
|
@@ -42,6 +43,60 @@ export type R2BucketCustomDomain = {
|
|
|
42
43
|
minTLS?: "1.0" | "1.1" | "1.2" | "1.3";
|
|
43
44
|
};
|
|
44
45
|
|
|
46
|
+
export type R2BucketLifecycleCondition =
|
|
47
|
+
| {
|
|
48
|
+
type: "Age";
|
|
49
|
+
/**
|
|
50
|
+
* Maximum age of an object, in seconds, before the rule's action applies.
|
|
51
|
+
*/
|
|
52
|
+
maxAge: number;
|
|
53
|
+
}
|
|
54
|
+
| {
|
|
55
|
+
type: "Date";
|
|
56
|
+
/**
|
|
57
|
+
* Absolute date (ISO 8601) at which the rule's action applies.
|
|
58
|
+
*/
|
|
59
|
+
date: string;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type R2BucketLifecycleRule = {
|
|
63
|
+
/**
|
|
64
|
+
* Unique identifier for the rule within the bucket.
|
|
65
|
+
*/
|
|
66
|
+
id: string;
|
|
67
|
+
/**
|
|
68
|
+
* Whether the rule is enabled.
|
|
69
|
+
* @default true
|
|
70
|
+
*/
|
|
71
|
+
enabled?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Object key prefix the rule applies to. Use `""` (or omit) to match all
|
|
74
|
+
* objects in the bucket.
|
|
75
|
+
* @default ""
|
|
76
|
+
*/
|
|
77
|
+
prefix?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Abort incomplete multipart uploads after the configured age.
|
|
80
|
+
*/
|
|
81
|
+
abortMultipartUploadsTransition?: {
|
|
82
|
+
condition?: { type: "Age"; maxAge: number };
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Delete matching objects after the configured age or on a specific date.
|
|
86
|
+
*/
|
|
87
|
+
deleteObjectsTransition?: {
|
|
88
|
+
condition?: R2BucketLifecycleCondition;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Transition matching objects to a different storage class. Cloudflare R2
|
|
92
|
+
* only supports transitioning to `InfrequentAccess` today.
|
|
93
|
+
*/
|
|
94
|
+
storageClassTransitions?: {
|
|
95
|
+
condition: R2BucketLifecycleCondition;
|
|
96
|
+
storageClass: "InfrequentAccess";
|
|
97
|
+
}[];
|
|
98
|
+
};
|
|
99
|
+
|
|
45
100
|
export type R2BucketProps = {
|
|
46
101
|
/**
|
|
47
102
|
* Name of the bucket. If omitted, a unique name will be generated.
|
|
@@ -67,6 +122,12 @@ export type R2BucketProps = {
|
|
|
67
122
|
* to remove all custom domains.
|
|
68
123
|
*/
|
|
69
124
|
domains?: R2BucketCustomDomain[];
|
|
125
|
+
/**
|
|
126
|
+
* Object lifecycle rules applied to the bucket. Pass an empty array (or
|
|
127
|
+
* omit) to clear all lifecycle rules. See the Cloudflare R2 docs for
|
|
128
|
+
* supported transitions.
|
|
129
|
+
*/
|
|
130
|
+
lifecycleRules?: R2BucketLifecycleRule[];
|
|
70
131
|
};
|
|
71
132
|
|
|
72
133
|
export type R2Bucket = Resource<
|
|
@@ -79,6 +140,7 @@ export type R2Bucket = Resource<
|
|
|
79
140
|
location: R2Bucket.Location | undefined;
|
|
80
141
|
accountId: string;
|
|
81
142
|
domains: R2Bucket.CustomDomain[];
|
|
143
|
+
lifecycleRules: R2Bucket.LifecycleRule[];
|
|
82
144
|
},
|
|
83
145
|
never,
|
|
84
146
|
Cloudflare.Providers
|
|
@@ -175,6 +237,63 @@ export type R2Bucket = Resource<
|
|
|
175
237
|
* ],
|
|
176
238
|
* });
|
|
177
239
|
* ```
|
|
240
|
+
*
|
|
241
|
+
* @section Object Lifecycle Rules
|
|
242
|
+
*
|
|
243
|
+
* Configure lifecycle rules to automatically delete objects, abort
|
|
244
|
+
* incomplete multipart uploads, or transition objects to InfrequentAccess
|
|
245
|
+
* storage. Pass an empty array (or omit) to clear all rules. See the
|
|
246
|
+
* [Cloudflare R2 docs](https://developers.cloudflare.com/r2/buckets/object-lifecycles/)
|
|
247
|
+
* for details and limits (max 1000 rules per bucket).
|
|
248
|
+
*
|
|
249
|
+
* @example Delete objects 30 days after upload
|
|
250
|
+
* ```typescript
|
|
251
|
+
* const bucket = yield* Cloudflare.R2Bucket("MyBucket", {
|
|
252
|
+
* lifecycleRules: [
|
|
253
|
+
* {
|
|
254
|
+
* id: "expire-old-objects",
|
|
255
|
+
* deleteObjectsTransition: {
|
|
256
|
+
* condition: { type: "Age", maxAge: 60 * 60 * 24 * 30 },
|
|
257
|
+
* },
|
|
258
|
+
* },
|
|
259
|
+
* ],
|
|
260
|
+
* });
|
|
261
|
+
* ```
|
|
262
|
+
*
|
|
263
|
+
* @example Transition to InfrequentAccess after 60 days, delete after 365
|
|
264
|
+
* ```typescript
|
|
265
|
+
* const bucket = yield* Cloudflare.R2Bucket("MyBucket", {
|
|
266
|
+
* lifecycleRules: [
|
|
267
|
+
* {
|
|
268
|
+
* id: "archive-then-delete",
|
|
269
|
+
* prefix: "logs/",
|
|
270
|
+
* storageClassTransitions: [
|
|
271
|
+
* {
|
|
272
|
+
* condition: { type: "Age", maxAge: 60 * 60 * 24 * 60 },
|
|
273
|
+
* storageClass: "InfrequentAccess",
|
|
274
|
+
* },
|
|
275
|
+
* ],
|
|
276
|
+
* deleteObjectsTransition: {
|
|
277
|
+
* condition: { type: "Age", maxAge: 60 * 60 * 24 * 365 },
|
|
278
|
+
* },
|
|
279
|
+
* },
|
|
280
|
+
* ],
|
|
281
|
+
* });
|
|
282
|
+
* ```
|
|
283
|
+
*
|
|
284
|
+
* @example Abort incomplete multipart uploads after 7 days
|
|
285
|
+
* ```typescript
|
|
286
|
+
* const bucket = yield* Cloudflare.R2Bucket("MyBucket", {
|
|
287
|
+
* lifecycleRules: [
|
|
288
|
+
* {
|
|
289
|
+
* id: "abort-stale-uploads",
|
|
290
|
+
* abortMultipartUploadsTransition: {
|
|
291
|
+
* condition: { type: "Age", maxAge: 60 * 60 * 24 * 7 },
|
|
292
|
+
* },
|
|
293
|
+
* },
|
|
294
|
+
* ],
|
|
295
|
+
* });
|
|
296
|
+
* ```
|
|
178
297
|
*/
|
|
179
298
|
export const R2Bucket = Resource<R2Bucket>("Cloudflare.R2Bucket")({
|
|
180
299
|
bind: R2BucketBinding.bind,
|
|
@@ -184,6 +303,23 @@ export declare namespace R2Bucket {
|
|
|
184
303
|
export type StorageClass = "Standard" | "InfrequentAccess";
|
|
185
304
|
export type Jurisdiction = "default" | "eu" | "fedramp";
|
|
186
305
|
export type Location = "apac" | "eeur" | "enam" | "weur" | "wnam" | "oc";
|
|
306
|
+
export type LifecycleRule = {
|
|
307
|
+
id: string;
|
|
308
|
+
enabled: boolean;
|
|
309
|
+
prefix: string;
|
|
310
|
+
abortMultipartUploadsTransition:
|
|
311
|
+
| { condition: { type: "Age"; maxAge: number } | undefined }
|
|
312
|
+
| undefined;
|
|
313
|
+
deleteObjectsTransition:
|
|
314
|
+
| { condition: R2BucketLifecycleCondition | undefined }
|
|
315
|
+
| undefined;
|
|
316
|
+
storageClassTransitions:
|
|
317
|
+
| {
|
|
318
|
+
condition: R2BucketLifecycleCondition;
|
|
319
|
+
storageClass: "InfrequentAccess";
|
|
320
|
+
}[]
|
|
321
|
+
| undefined;
|
|
322
|
+
};
|
|
187
323
|
export type CustomDomain = {
|
|
188
324
|
domain: string;
|
|
189
325
|
zoneId: string | undefined;
|
|
@@ -220,10 +356,41 @@ export const R2BucketProvider = () =>
|
|
|
220
356
|
const patchBucket = yield* r2.patchBucket;
|
|
221
357
|
const deleteBucket = yield* r2.deleteBucket;
|
|
222
358
|
const getBucket = yield* r2.getBucket;
|
|
359
|
+
const deleteObjects = yield* r2.deleteObjects;
|
|
223
360
|
const listBucketDomainCustoms = yield* r2.listBucketDomainCustoms;
|
|
224
361
|
const createBucketDomainCustom = yield* r2.createBucketDomainCustom;
|
|
225
362
|
const updateBucketDomainCustom = yield* r2.updateBucketDomainCustom;
|
|
226
363
|
const deleteBucketDomainCustom = yield* r2.deleteBucketDomainCustom;
|
|
364
|
+
const getBucketLifecycle = yield* r2.getBucketLifecycle;
|
|
365
|
+
const putBucketLifecycle = yield* r2.putBucketLifecycle;
|
|
366
|
+
|
|
367
|
+
const emptyBucket = (
|
|
368
|
+
bucketName: string,
|
|
369
|
+
jurisdiction: R2Bucket.Jurisdiction,
|
|
370
|
+
) =>
|
|
371
|
+
r2.listObjects
|
|
372
|
+
.items({
|
|
373
|
+
accountId,
|
|
374
|
+
bucketName,
|
|
375
|
+
cfR2Jurisdiction: jurisdiction,
|
|
376
|
+
perPage: 1000,
|
|
377
|
+
})
|
|
378
|
+
.pipe(
|
|
379
|
+
Stream.filter(
|
|
380
|
+
(o): o is typeof o & { key: string } =>
|
|
381
|
+
typeof o.key === "string" && o.key !== "",
|
|
382
|
+
),
|
|
383
|
+
Stream.map((o) => o.key),
|
|
384
|
+
Stream.runForEachArray((chunk) =>
|
|
385
|
+
deleteObjects({
|
|
386
|
+
accountId,
|
|
387
|
+
bucketName,
|
|
388
|
+
cfR2Jurisdiction: jurisdiction,
|
|
389
|
+
body: [...chunk],
|
|
390
|
+
}),
|
|
391
|
+
),
|
|
392
|
+
Effect.catchTag("NoSuchBucket", () => Effect.void),
|
|
393
|
+
);
|
|
227
394
|
|
|
228
395
|
const createBucketName = (id: string, name: string | undefined) =>
|
|
229
396
|
Effect.gen(function* () {
|
|
@@ -252,7 +419,7 @@ export const R2BucketProvider = () =>
|
|
|
252
419
|
}).pipe(
|
|
253
420
|
Effect.retry({
|
|
254
421
|
while: isNoSuchBucket,
|
|
255
|
-
schedule:
|
|
422
|
+
schedule: r2BucketEndpointConsistencySchedule,
|
|
256
423
|
}),
|
|
257
424
|
Effect.map((response) =>
|
|
258
425
|
response.domains.map(toCustomDomainAttributes),
|
|
@@ -351,7 +518,7 @@ export const R2BucketProvider = () =>
|
|
|
351
518
|
}).pipe(
|
|
352
519
|
Effect.retry({
|
|
353
520
|
while: isNoSuchBucket,
|
|
354
|
-
schedule:
|
|
521
|
+
schedule: r2BucketEndpointConsistencySchedule,
|
|
355
522
|
}),
|
|
356
523
|
);
|
|
357
524
|
return toCustomDomainAttributes({ ...created, zoneId });
|
|
@@ -368,7 +535,7 @@ export const R2BucketProvider = () =>
|
|
|
368
535
|
}).pipe(
|
|
369
536
|
Effect.retry({
|
|
370
537
|
while: isNoSuchBucket,
|
|
371
|
-
schedule:
|
|
538
|
+
schedule: r2BucketEndpointConsistencySchedule,
|
|
372
539
|
}),
|
|
373
540
|
);
|
|
374
541
|
return toCustomDomainAttributes({
|
|
@@ -383,6 +550,45 @@ export const R2BucketProvider = () =>
|
|
|
383
550
|
return applied.sort((a, b) => a.domain.localeCompare(b.domain));
|
|
384
551
|
});
|
|
385
552
|
|
|
553
|
+
const reconcileLifecycleRules = (
|
|
554
|
+
bucketName: string,
|
|
555
|
+
jurisdiction: R2Bucket.Jurisdiction,
|
|
556
|
+
desired: R2BucketLifecycleRule[],
|
|
557
|
+
) =>
|
|
558
|
+
Effect.gen(function* () {
|
|
559
|
+
const observed = yield* getBucketLifecycle({
|
|
560
|
+
accountId,
|
|
561
|
+
bucketName,
|
|
562
|
+
jurisdiction,
|
|
563
|
+
}).pipe(
|
|
564
|
+
Effect.retry({
|
|
565
|
+
while: isNoSuchBucket,
|
|
566
|
+
schedule: r2BucketEndpointConsistencySchedule,
|
|
567
|
+
}),
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
const observedRules = (observed.rules ?? []).map(toLifecycleRule);
|
|
571
|
+
const desiredRules = desired.map(normalizeLifecycleRule);
|
|
572
|
+
|
|
573
|
+
if (deepEqual(observedRules, desiredRules)) {
|
|
574
|
+
return desiredRules;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
yield* putBucketLifecycle({
|
|
578
|
+
accountId,
|
|
579
|
+
bucketName,
|
|
580
|
+
jurisdiction,
|
|
581
|
+
rules: desired.map(toLifecyclePutPayload),
|
|
582
|
+
}).pipe(
|
|
583
|
+
Effect.retry({
|
|
584
|
+
while: isNoSuchBucket,
|
|
585
|
+
schedule: r2BucketEndpointConsistencySchedule,
|
|
586
|
+
}),
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
return desiredRules;
|
|
590
|
+
});
|
|
591
|
+
|
|
386
592
|
return {
|
|
387
593
|
stables: ["bucketName", "accountId"],
|
|
388
594
|
diff: Effect.fn(function* ({ id, olds = {}, news = {}, output }) {
|
|
@@ -412,6 +618,9 @@ export const R2BucketProvider = () =>
|
|
|
412
618
|
if (!deepEqual(olds.domains, news.domains)) {
|
|
413
619
|
return { action: "update" } as const;
|
|
414
620
|
}
|
|
621
|
+
if (!deepEqual(olds.lifecycleRules, news.lifecycleRules)) {
|
|
622
|
+
return { action: "update" } as const;
|
|
623
|
+
}
|
|
415
624
|
}),
|
|
416
625
|
reconcile: Effect.fn(function* ({ id, news = {}, output }) {
|
|
417
626
|
const name = yield* createBucketName(id, news.name);
|
|
@@ -481,9 +690,16 @@ export const R2BucketProvider = () =>
|
|
|
481
690
|
output?.domains ?? [],
|
|
482
691
|
);
|
|
483
692
|
|
|
693
|
+
const lifecycleRules = yield* reconcileLifecycleRules(
|
|
694
|
+
attrs.bucketName,
|
|
695
|
+
attrs.jurisdiction,
|
|
696
|
+
news.lifecycleRules ?? [],
|
|
697
|
+
);
|
|
698
|
+
|
|
484
699
|
return {
|
|
485
700
|
...attrs,
|
|
486
701
|
domains,
|
|
702
|
+
lifecycleRules,
|
|
487
703
|
};
|
|
488
704
|
}),
|
|
489
705
|
delete: Effect.fn(function* ({ output }) {
|
|
@@ -497,6 +713,7 @@ export const R2BucketProvider = () =>
|
|
|
497
713
|
Effect.catchIf(isMissingCustomDomainOrBucket, () => Effect.void),
|
|
498
714
|
);
|
|
499
715
|
}
|
|
716
|
+
yield* emptyBucket(output.bucketName, output.jurisdiction);
|
|
500
717
|
yield* deleteBucket({
|
|
501
718
|
accountId: output.accountId,
|
|
502
719
|
bucketName: output.bucketName,
|
|
@@ -519,6 +736,7 @@ export const R2BucketProvider = () =>
|
|
|
519
736
|
location: normalizeLocation(bucket.location),
|
|
520
737
|
accountId: acct,
|
|
521
738
|
domains: output?.domains ?? [],
|
|
739
|
+
lifecycleRules: output?.lifecycleRules ?? [],
|
|
522
740
|
})),
|
|
523
741
|
Effect.catchTag("NoSuchBucket", () => Effect.succeed(undefined)),
|
|
524
742
|
);
|
|
@@ -527,10 +745,11 @@ export const R2BucketProvider = () =>
|
|
|
527
745
|
}),
|
|
528
746
|
);
|
|
529
747
|
|
|
530
|
-
// R2 can make a newly-created bucket visible to `getBucket` before
|
|
531
|
-
//
|
|
532
|
-
// not-found
|
|
533
|
-
|
|
748
|
+
// R2 can make a newly-created bucket visible to `getBucket` before its
|
|
749
|
+
// sub-resource endpoints (custom domains, lifecycle) accept it. Retry only
|
|
750
|
+
// that narrow `NoSuchBucket` lag here; not-found sub-resources are still
|
|
751
|
+
// treated as terminal for idempotent deletes.
|
|
752
|
+
const r2BucketEndpointConsistencySchedule = Schedule.exponential(100).pipe(
|
|
534
753
|
Schedule.both(Schedule.recurs(5)),
|
|
535
754
|
);
|
|
536
755
|
|
|
@@ -573,6 +792,51 @@ const isMissingCustomDomainOrBucket = (error: unknown): boolean =>
|
|
|
573
792
|
((error as { _tag: unknown })._tag === "DomainNotFound" ||
|
|
574
793
|
(error as { _tag: unknown })._tag === "NoSuchBucket")));
|
|
575
794
|
|
|
795
|
+
type LifecycleRuleResponse = NonNullable<
|
|
796
|
+
r2.GetBucketLifecycleResponse["rules"]
|
|
797
|
+
>[number];
|
|
798
|
+
|
|
799
|
+
const toLifecycleRule = (
|
|
800
|
+
rule: LifecycleRuleResponse,
|
|
801
|
+
): R2Bucket.LifecycleRule => ({
|
|
802
|
+
id: rule.id,
|
|
803
|
+
enabled: rule.enabled,
|
|
804
|
+
prefix: rule.conditions.prefix ?? "",
|
|
805
|
+
abortMultipartUploadsTransition: rule.abortMultipartUploadsTransition
|
|
806
|
+
? { condition: rule.abortMultipartUploadsTransition.condition ?? undefined }
|
|
807
|
+
: undefined,
|
|
808
|
+
deleteObjectsTransition: rule.deleteObjectsTransition
|
|
809
|
+
? { condition: rule.deleteObjectsTransition.condition ?? undefined }
|
|
810
|
+
: undefined,
|
|
811
|
+
storageClassTransitions: rule.storageClassTransitions ?? undefined,
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
const normalizeLifecycleRule = (
|
|
815
|
+
rule: R2BucketLifecycleRule,
|
|
816
|
+
): R2Bucket.LifecycleRule => ({
|
|
817
|
+
id: rule.id,
|
|
818
|
+
enabled: rule.enabled ?? true,
|
|
819
|
+
prefix: rule.prefix ?? "",
|
|
820
|
+
abortMultipartUploadsTransition: rule.abortMultipartUploadsTransition
|
|
821
|
+
? { condition: rule.abortMultipartUploadsTransition.condition }
|
|
822
|
+
: undefined,
|
|
823
|
+
deleteObjectsTransition: rule.deleteObjectsTransition
|
|
824
|
+
? { condition: rule.deleteObjectsTransition.condition }
|
|
825
|
+
: undefined,
|
|
826
|
+
storageClassTransitions: rule.storageClassTransitions,
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
const toLifecyclePutPayload = (
|
|
830
|
+
rule: R2BucketLifecycleRule,
|
|
831
|
+
): NonNullable<r2.PutBucketLifecycleRequest["rules"]>[number] => ({
|
|
832
|
+
id: rule.id,
|
|
833
|
+
enabled: rule.enabled ?? true,
|
|
834
|
+
conditions: { prefix: rule.prefix ?? "" },
|
|
835
|
+
abortMultipartUploadsTransition: rule.abortMultipartUploadsTransition,
|
|
836
|
+
deleteObjectsTransition: rule.deleteObjectsTransition,
|
|
837
|
+
storageClassTransitions: rule.storageClassTransitions,
|
|
838
|
+
});
|
|
839
|
+
|
|
576
840
|
const isNoSuchBucket = (error: unknown): boolean =>
|
|
577
841
|
typeof error === "object" &&
|
|
578
842
|
error !== null &&
|