| 1 | import * as Effect from "effect/Effect"; |
| 2 | import * as FileSystem from "effect/FileSystem"; |
| 3 | import * as Stream from "effect/Stream"; |
| 4 | import * as ChildProcess from "effect/process/ChildProcess"; |
| 5 | import { ChildProcessSpawner } from "effect/process/ChildProcessSpawner"; |
| 6 | |
| 7 | import * as Cloudflare from "alchemy/Cloudflare"; |
| 8 | |
| 9 | export class DevBox extends Cloudflare.Container< |
| 10 | DevBox, |
| 11 | { |
| 12 | readFile: (path: string) => Effect.Effect<string>; |
| 13 | writeFile: (path: string, contents: string) => Effect.Effect<void>; |
| 14 | exec: (command: string) => Effect.Effect<{ |
| 15 | exitCode: number; |
| 16 | stdout: string; |
| 17 | stderr: string; |
| 18 | }>; |
| 19 | } |
| 20 | >()("DevBox") {} |
| 21 | |
| 22 | export default DevBox.make( |
| 23 | { |
| 24 | main: import.meta.url, |
| 25 | image: "oven/bun:1.3", |
| 26 | }, |
| 27 | Effect.gen(function* () { |
| 28 | const fs = yield* FileSystem.FileSystem; |
| 29 | const cp = yield* ChildProcessSpawner; |
| 30 | |
| 31 | return { |
| 32 | readFile: (path: string) => fs.readFileString(path).pipe(Effect.orDie), |
| 33 | writeFile: (path: string, contents: string) => |
| 34 | fs.writeFileString(path, contents).pipe(Effect.orDie), |
| 35 | exec: (command: string) => |
| 36 | cp.spawn(ChildProcess.make(command, { shell: true })).pipe( |
| 37 | Effect.flatMap(({ exitCode, stdout, stderr }) => |
| 38 | Effect.all({ |
| 39 | exitCode, |
| 40 | stdout: stdout.pipe(Stream.decodeText, Stream.mkString), |
| 41 | stderr: stderr.pipe(Stream.decodeText, Stream.mkString), |
| 42 | }), |
| 43 | ), |
| 44 | Effect.scoped, |
| 45 | Effect.orDie, |
| 46 | ), |
| 47 | }; |
| 48 | }), |
| 49 | ); |