1import type { ConfigError } from "effect/Config";
2import * as Effect from "effect/Effect";
3import type * as Scope from "effect/Scope";
4import { AlchemyContext } from "./AlchemyContext.ts";
5import * as Apply from "./Apply.ts";
6import type { Input } from "./Input.ts";
7import * as Plan from "./Plan.ts";
8import type {
9 ResourceSelection,
10 SelectionOutput,
11} from "./ResourceSelection.ts";
12import { evalStack, type CompiledStack, type StackEffect } from "./Stack.ts";
13import { Stage } from "./Stage.ts";
14
15export interface DeployOptions<A> extends Plan.MakePlanOptions {
16 stack: StackEffect<CompiledStack<A>, ConfigError, Stage | AlchemyContext>;
17 stage: string;
18 dev?: boolean;
19 /** See {@link evalStack} — when set, scoped resources outlive `deploy`. */
20 scope?: Scope.Scope;
21}
22
23export type DeployResult<A> = Effect.Effect<
24 Input.Resolve<A>,
25 Effect.Error<ReturnType<typeof deployStack<A>>>,
26 Effect.Services<ReturnType<typeof deployStack<A>>>
27>;
28
29export interface FilteredDeployOptions<A>
30 extends Omit<DeployOptions<A>, keyof ResourceSelection>, ResourceSelection {}
31
32/**
33 * Reconcile a stack. Filters select nodes and their transitive dependencies,
34 * not declaration evaluation: the whole stack program still runs. Filtered
35 * deployments return void and leave persisted full-stack outputs unchanged.
36 */
37export function deploy<
38 A,
39 Options extends FilteredDeployOptions<A> = DeployOptions<A>,
40>(
41 options: FilteredDeployOptions<A> & Options,
42): DeployResult<SelectionOutput<A, Options>>;
43export function deploy<A>(options: FilteredDeployOptions<A>) {
44 return deployStack(options);
45}
46
47const deployStack = <A>({
48 stack,
49 stage,
50 dev,
51 scope,
52 force,
53 include,
54 exclude,
55}: FilteredDeployOptions<A>) =>
56 evalStack(
57 stack,
58 (stack) =>
59 Effect.gen(function* () {
60 const plan = yield* Plan.make(stack, { force, include, exclude });
61 const output = yield* Apply.apply(plan);
62 return output;
63 }),
64 { stage, dev, scope },
65 );