1import * as Alchemy from "alchemy";
2import * as AWS from "alchemy/AWS";
3import * as S3 from "alchemy/AWS/S3";
4import * as Effect from "effect/Effect";
5import * as Stream from "effect/Stream";
6import ApiFunction from "./src/ApiFunction.ts";
7
8export default Alchemy.Stack(
9 "aws-dev",
10 {
11 providers: AWS.providers(),
12 state: Alchemy.localState(),
13 },
14 Effect.gen(function* () {
15 const api = yield* ApiFunction;
16
17 // Deploy-time Action data plane: seed an object into a bucket during
18 // apply and read it back, reporting which AWS account served the calls.
19 // Under `alchemy dev` the calls MUST land on the floci emulator — the
20 // dummy account id (000000000000) in the stack outputs is the proof.
21 // CLI-path counterpart of the harness regression in
22 // packages/alchemy/test/AWS/S3/Bucket.data-plane.test.ts.
23 const bucket = yield* S3.Bucket("SeedBucket", { forceDestroy: true });
24 const SeedObject = Alchemy.Action(
25 "SeedObject",
26 Effect.gen(function* () {
27 const putObject = yield* S3.PutObject(bucket);
28 const getObject = yield* S3.GetObject(bucket);
29 return Effect.fn(function* (input: { key: string; body: string }) {
30 const environment = yield* AWS.AWSEnvironment.current;
31 yield* putObject({ Key: input.key, Body: input.body });
32 const object = yield* getObject({ Key: input.key });
33 const text = yield* (
34 object.Body?.pipe(Stream.decodeText, Stream.mkString) ??
35 Effect.succeed("")
36 );
37 return { accountId: environment.accountId, text };
38 });
39 }).pipe(Effect.provide([S3.PutObjectHttp, S3.GetObjectHttp])),
40 );
41 const seeded = yield* SeedObject({
42 key: "seed.txt",
43 body: "seed-object-body-v1",
44 });
45
46 return {
47 api: api.functionUrl,
48 seedAccount: seeded.accountId,
49 seedText: seeded.text,
50 };
51 }),
52);