1import * as Effect from "effect/Effect";
2import * as Layer from "effect/Layer";
3import * as Provider from "../Provider.ts";
4import { Resource } from "../Resource.ts";
5import { BuildProvider } from "./Build.ts";
6import { CommandExecutorLive } from "./Command.ts";
7import { DevProvider } from "./Dev.ts";
8import { ExecProvider } from "./Exec.ts";
9
10export const providers = () =>
11 Layer.mergeAll(
12 BuildProvider(),
13 DevProvider(),
14 ExecProvider(),
15 // TODO: Remove before v2.0.0 stable release.
16 Renamed({ from: "Build.Command", to: "Command.Build" }),
17 Renamed({ from: "Build.DevServer", to: "Command.Dev" }),
18 ).pipe(Layer.provide(CommandExecutorLive()));
19
20/**
21 * Constructs a no-op provider for a resource that has been renamed.
22 * This prevents errors when attempting to tear down the old resource.
23 * The type is Layer.Layer<never> to avoid polluting the type signature.
24 */
25const Renamed = (input: { from: string; to: string }) => {
26 const message = (id: string) =>
27 `The "${input.from}" resource has been renamed to "${input.to}". Please update "${id}" to use the new resource.`;
28
29 return Provider.succeed(Resource(input.from), {
30 // The only one of these methods that would be called normally is `delete`, if you have an old version in state that needs to be removed.
31 // The other methods are defensive to prevent undefined behavior.
32 // You'll only end up calling these if you're using a `Build` resource manually;
33 // if you're using a `StaticSite` resource, the Alchemy engine migrates the old resource automatically.
34 diff: ({ id }) =>
35 Effect.logWarning(message(id)).pipe(Effect.as({ action: "noop" })),
36 precreate: ({ id }) => Effect.die(message(id)),
37 reconcile: ({ id }) => Effect.die(message(id)),
38 delete: () => Effect.void,
39 list: () => Effect.succeed([]),
40 }) as Layer.Layer<never>;
41};