1import * as EffectContext from "effect/Context";
2import * as Effect from "effect/Effect";
3import * as FileSystem from "effect/FileSystem";
4import * as Layer from "effect/Layer";
5import * as Option from "effect/Option";
6import path from "pathe";
7
8export class AlchemyContext extends EffectContext.Service<
9 AlchemyContext,
10 {
11 dotAlchemy: string;
12 dev: boolean;
13 /**
14 * Global default for the {@link import("./AdoptPolicy.ts").AdoptPolicy}
15 * service. When `true`, resources without prior state will be adopted by
16 * calling their `read` lifecycle operation; if that returns attributes
17 * (and does not fail with `OwnedBySomeoneElse`), those attributes are
18 * persisted as the resource's initial `created` state.
19 *
20 * The CLI's `--adopt` flag flows in through this field. Per-resource
21 * overrides via the `adopt(enabled)` combinator still take precedence.
22 */
23 adopt: boolean;
24 /**
25 * When `true`, an out-of-date Cloudflare state store is upgraded
26 * automatically instead of prompting for confirmation (and the upgrade
27 * proceeds even in CI). The CLI's `--yes` flag flows in through this field.
28 * @default false
29 */
30 updateStateStore?: boolean;
31 }
32>()("alchemy/Context") {}
33
34/** Use the configured runtime directory, with a relative fallback for standalone callers. */
35export const dotAlchemyDirectory = Effect.serviceOption(AlchemyContext).pipe(
36 Effect.map((context) =>
37 Option.isSome(context) ? context.value.dotAlchemy : ".alchemy",
38 ),
39);
40
41export const AlchemyContextLive = Layer.effect(
42 AlchemyContext,
43 Effect.gen(function* () {
44 const fs = yield* FileSystem.FileSystem;
45 const dir = path.resolve(yield* dotAlchemyDirectory);
46 yield* fs.makeDirectory(dir, { recursive: true });
47 return {
48 dotAlchemy: dir,
49 updateStateStore: false,
50 dev: false,
51 adopt: false,
52 };
53 }),
54);