alchemy 2.0.0-beta.25 → 2.0.0-beta.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/bin/alchemy.d.ts +2 -0
  2. package/bin/alchemy.d.ts.map +1 -0
  3. package/bin/alchemy.js +3 -57288
  4. package/bin/alchemy.js.map +1 -1
  5. package/bin/alchemy.ts +3 -59
  6. package/bin/exec.d.ts +2 -0
  7. package/bin/exec.d.ts.map +1 -0
  8. package/bin/exec.js +2360 -0
  9. package/bin/exec.js.map +1 -0
  10. package/bin/exec.ts +3 -21
  11. package/lib/AWS/AuthProvider.d.ts +1 -1
  12. package/lib/AWS/AuthProvider.d.ts.map +1 -1
  13. package/lib/AWS/Lambda/BucketEventSource.d.ts +1 -1
  14. package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
  15. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts +1 -1
  16. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts.map +1 -1
  17. package/lib/AWS/Lambda/QueueEventSource.d.ts +1 -1
  18. package/lib/AWS/Lambda/QueueEventSource.d.ts.map +1 -1
  19. package/lib/AWS/Lambda/StreamEventSource.d.ts +1 -1
  20. package/lib/AWS/Lambda/StreamEventSource.d.ts.map +1 -1
  21. package/lib/AWS/Lambda/TableEventSource.d.ts +1 -1
  22. package/lib/AWS/Lambda/TableEventSource.d.ts.map +1 -1
  23. package/lib/AWS/Lambda/TopicEventSource.d.ts +1 -1
  24. package/lib/AWS/Lambda/TopicEventSource.d.ts.map +1 -1
  25. package/lib/Auth/AuthProvider.d.ts.map +1 -1
  26. package/lib/Auth/AuthProvider.js +20 -3
  27. package/lib/Auth/AuthProvider.js.map +1 -1
  28. package/lib/Auth/Lock.d.ts +12 -0
  29. package/lib/Auth/Lock.d.ts.map +1 -0
  30. package/lib/Auth/Lock.js +27 -0
  31. package/lib/Auth/Lock.js.map +1 -0
  32. package/lib/Axiom/AuthProvider.d.ts +1 -1
  33. package/lib/Axiom/AuthProvider.d.ts.map +1 -1
  34. package/lib/Axiom/Credentials.d.ts +1 -1
  35. package/lib/Axiom/Credentials.d.ts.map +1 -1
  36. package/lib/Bundle/Bundle.d.ts +3 -3
  37. package/lib/Cli/commands/_shared.d.ts +51 -0
  38. package/lib/Cli/commands/_shared.d.ts.map +1 -0
  39. package/lib/Cli/commands/_shared.js +176 -0
  40. package/lib/Cli/commands/_shared.js.map +1 -0
  41. package/lib/Cli/commands/bootstrap.d.ts +6 -0
  42. package/lib/Cli/commands/bootstrap.d.ts.map +1 -0
  43. package/lib/Cli/commands/bootstrap.js +106 -0
  44. package/lib/Cli/commands/bootstrap.js.map +1 -0
  45. package/lib/Cli/commands/deploy.d.ts +56 -0
  46. package/lib/Cli/commands/deploy.d.ts.map +1 -0
  47. package/lib/Cli/commands/deploy.js +128 -0
  48. package/lib/Cli/commands/deploy.js.map +1 -0
  49. package/lib/Cli/commands/dev.d.ts +10 -0
  50. package/lib/Cli/commands/dev.d.ts.map +1 -0
  51. package/lib/Cli/commands/dev.js +45 -0
  52. package/lib/Cli/commands/dev.js.map +1 -0
  53. package/lib/Cli/commands/login.d.ts +10 -0
  54. package/lib/Cli/commands/login.d.ts.map +1 -0
  55. package/lib/Cli/commands/login.js +59 -0
  56. package/lib/Cli/commands/login.js.map +1 -0
  57. package/lib/Cli/commands/logs.d.ts +12 -0
  58. package/lib/Cli/commands/logs.d.ts.map +1 -0
  59. package/lib/Cli/commands/logs.js +99 -0
  60. package/lib/Cli/commands/logs.js.map +1 -0
  61. package/lib/Cli/commands/profile.d.ts +3 -0
  62. package/lib/Cli/commands/profile.d.ts.map +1 -0
  63. package/lib/Cli/commands/profile.js +76 -0
  64. package/lib/Cli/commands/profile.js.map +1 -0
  65. package/lib/Cli/commands/state.d.ts +3 -0
  66. package/lib/Cli/commands/state.d.ts.map +1 -0
  67. package/lib/Cli/commands/state.js +221 -0
  68. package/lib/Cli/commands/state.js.map +1 -0
  69. package/lib/Cli/commands/tail.d.ts +9 -0
  70. package/lib/Cli/commands/tail.d.ts.map +1 -0
  71. package/lib/Cli/commands/tail.js +93 -0
  72. package/lib/Cli/commands/tail.js.map +1 -0
  73. package/lib/Cli/exec.d.ts +3 -0
  74. package/lib/Cli/exec.d.ts.map +1 -0
  75. package/lib/Cli/exec.js +12 -0
  76. package/lib/Cli/exec.js.map +1 -0
  77. package/lib/Cli/index.d.ts +2 -1
  78. package/lib/Cli/index.d.ts.map +1 -1
  79. package/lib/Cli/index.js +2 -5
  80. package/lib/Cli/index.js.map +1 -1
  81. package/lib/Cli/main.d.ts +3 -0
  82. package/lib/Cli/main.d.ts.map +1 -0
  83. package/lib/Cli/main.js +40 -0
  84. package/lib/Cli/main.js.map +1 -0
  85. package/lib/Cli/{InkCLI.d.ts → tui/InkCLI.d.ts} +1 -1
  86. package/lib/Cli/tui/InkCLI.d.ts.map +1 -0
  87. package/lib/Cli/{InkCLI.js → tui/InkCLI.js} +1 -1
  88. package/lib/Cli/tui/InkCLI.js.map +1 -0
  89. package/lib/Cli/{components → tui/components}/ApprovePlan.d.ts +1 -1
  90. package/lib/Cli/tui/components/ApprovePlan.d.ts.map +1 -0
  91. package/lib/Cli/tui/components/ApprovePlan.js.map +1 -0
  92. package/lib/Cli/{components → tui/components}/Plan.d.ts +1 -1
  93. package/lib/Cli/tui/components/Plan.d.ts.map +1 -0
  94. package/lib/Cli/{components → tui/components}/Plan.js +1 -1
  95. package/lib/Cli/tui/components/Plan.js.map +1 -0
  96. package/lib/Cli/{components → tui/components}/PlanProgress.d.ts +3 -3
  97. package/lib/Cli/tui/components/PlanProgress.d.ts.map +1 -0
  98. package/lib/Cli/{components → tui/components}/PlanProgress.js +1 -1
  99. package/lib/Cli/tui/components/PlanProgress.js.map +1 -0
  100. package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts +1 -1
  101. package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts.map +1 -1
  102. package/lib/Cloudflare/ApiToken/UserApiToken.d.ts +1 -1
  103. package/lib/Cloudflare/ApiToken/UserApiToken.d.ts.map +1 -1
  104. package/lib/Cloudflare/Auth/AuthProvider.d.ts +1 -1
  105. package/lib/Cloudflare/Auth/AuthProvider.d.ts.map +1 -1
  106. package/lib/Cloudflare/Auth/AuthProvider.js +0 -4
  107. package/lib/Cloudflare/Auth/AuthProvider.js.map +1 -1
  108. package/lib/Cloudflare/CloudflareEnvironment.d.ts +1 -1
  109. package/lib/Cloudflare/CloudflareEnvironment.d.ts.map +1 -1
  110. package/lib/Cloudflare/Container/ContainerApplication.d.ts +1 -1
  111. package/lib/Cloudflare/Container/ContainerApplication.d.ts.map +1 -1
  112. package/lib/Cloudflare/Credentials.d.ts +1 -1
  113. package/lib/Cloudflare/Credentials.d.ts.map +1 -1
  114. package/lib/Cloudflare/D1/D1Clone.d.ts +1 -1
  115. package/lib/Cloudflare/D1/D1Clone.d.ts.map +1 -1
  116. package/lib/Cloudflare/D1/D1Import.d.ts +1 -1
  117. package/lib/Cloudflare/D1/D1Import.d.ts.map +1 -1
  118. package/lib/Cloudflare/D1/D1Migrations.d.ts +1 -1
  119. package/lib/Cloudflare/D1/D1Migrations.d.ts.map +1 -1
  120. package/lib/Cloudflare/KV/KVNamespace.d.ts +1 -1
  121. package/lib/Cloudflare/KV/KVNamespace.d.ts.map +1 -1
  122. package/lib/Cloudflare/Logs.d.ts +1 -1
  123. package/lib/Cloudflare/Logs.d.ts.map +1 -1
  124. package/lib/Cloudflare/Queue/Queue.d.ts +1 -1
  125. package/lib/Cloudflare/Queue/Queue.d.ts.map +1 -1
  126. package/lib/Cloudflare/Queue/QueueConsumer.d.ts +1 -1
  127. package/lib/Cloudflare/Queue/QueueConsumer.d.ts.map +1 -1
  128. package/lib/Cloudflare/SecretsStore/Secret.d.ts +1 -1
  129. package/lib/Cloudflare/SecretsStore/Secret.d.ts.map +1 -1
  130. package/lib/Cloudflare/SecretsStore/SecretsStore.d.ts +1 -1
  131. package/lib/Cloudflare/SecretsStore/SecretsStore.d.ts.map +1 -1
  132. package/lib/Cloudflare/SecretsStore/SecretsStore.js +5 -5
  133. package/lib/Cloudflare/SecretsStore/SecretsStore.js.map +1 -1
  134. package/lib/Cloudflare/StateStore/Api.d.ts +13 -0
  135. package/lib/Cloudflare/StateStore/Api.d.ts.map +1 -1
  136. package/lib/Cloudflare/StateStore/Api.js +15 -2
  137. package/lib/Cloudflare/StateStore/Api.js.map +1 -1
  138. package/lib/Cloudflare/StateStore/State.d.ts +3 -3
  139. package/lib/Cloudflare/StateStore/State.d.ts.map +1 -1
  140. package/lib/Cloudflare/StateStore/State.js.map +1 -1
  141. package/lib/Cloudflare/Workers/Assets.d.ts +1 -1
  142. package/lib/Cloudflare/Workers/Assets.d.ts.map +1 -1
  143. package/lib/Cloudflare/Workers/Worker.d.ts +2 -2
  144. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  145. package/lib/Cloudflare/Workers/Worker.js +14 -12
  146. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  147. package/lib/Cloudflare/Workers/Workflow.d.ts +1 -1
  148. package/lib/Cloudflare/Workers/Workflow.d.ts.map +1 -1
  149. package/lib/Deploy.d.ts +1 -1
  150. package/lib/Deploy.d.ts.map +1 -1
  151. package/lib/Destroy.d.ts +1 -1
  152. package/lib/Destroy.d.ts.map +1 -1
  153. package/lib/GitHub/AuthProvider.d.ts +1 -1
  154. package/lib/GitHub/AuthProvider.d.ts.map +1 -1
  155. package/lib/GitHub/Credentials.d.ts +1 -1
  156. package/lib/GitHub/Credentials.d.ts.map +1 -1
  157. package/lib/GitHub/Providers.d.ts +1 -1
  158. package/lib/GitHub/Providers.d.ts.map +1 -1
  159. package/lib/Output.d.ts.map +1 -1
  160. package/lib/Output.js +13 -2
  161. package/lib/Output.js.map +1 -1
  162. package/lib/Provider.d.ts +1 -1
  163. package/lib/Provider.d.ts.map +1 -1
  164. package/lib/Stack.js +2 -2
  165. package/lib/Stack.js.map +1 -1
  166. package/lib/Telemetry/Layer.d.ts.map +1 -1
  167. package/lib/Telemetry/Layer.js +13 -1
  168. package/lib/Telemetry/Layer.js.map +1 -1
  169. package/lib/Test/Bun.d.ts +59 -28
  170. package/lib/Test/Bun.d.ts.map +1 -1
  171. package/lib/Test/Bun.js +77 -81
  172. package/lib/Test/Bun.js.map +1 -1
  173. package/lib/Test/Core.d.ts +73 -0
  174. package/lib/Test/Core.d.ts.map +1 -0
  175. package/lib/Test/Core.js +98 -0
  176. package/lib/Test/Core.js.map +1 -0
  177. package/lib/Test/Vitest.d.ts +53 -72
  178. package/lib/Test/Vitest.d.ts.map +1 -1
  179. package/lib/Test/Vitest.js +73 -232
  180. package/lib/Test/Vitest.js.map +1 -1
  181. package/lib/Test/index.d.ts +0 -1
  182. package/lib/Test/index.d.ts.map +1 -1
  183. package/lib/Test/index.js +0 -1
  184. package/lib/Test/index.js.map +1 -1
  185. package/lib/Util/index.d.ts +1 -0
  186. package/lib/Util/index.d.ts.map +1 -1
  187. package/lib/Util/index.js +1 -0
  188. package/lib/Util/index.js.map +1 -1
  189. package/lib/index.d.ts +7 -0
  190. package/lib/index.d.ts.map +1 -1
  191. package/lib/index.js +6 -0
  192. package/lib/index.js.map +1 -1
  193. package/lib/tsconfig.test.tsbuildinfo +1 -0
  194. package/package.json +68 -52
  195. package/src/Auth/AuthProvider.ts +22 -4
  196. package/src/Auth/Lock.ts +35 -0
  197. package/{bin → src/Cli}/commands/_shared.ts +5 -5
  198. package/{bin → src/Cli}/commands/bootstrap.ts +18 -14
  199. package/{bin → src/Cli}/commands/deploy.ts +16 -16
  200. package/{bin → src/Cli}/commands/dev.ts +13 -3
  201. package/{bin → src/Cli}/commands/login.ts +9 -9
  202. package/{bin → src/Cli}/commands/logs.ts +11 -11
  203. package/{bin → src/Cli}/commands/profile.ts +8 -8
  204. package/{bin → src/Cli}/commands/state.ts +47 -26
  205. package/{bin → src/Cli}/commands/tail.ts +11 -11
  206. package/src/Cli/exec.ts +23 -0
  207. package/src/Cli/index.ts +2 -5
  208. package/src/Cli/main.ts +60 -0
  209. package/src/Cli/{InkCLI.tsx → tui/InkCLI.tsx} +3 -3
  210. package/src/Cli/{components → tui/components}/ApprovePlan.tsx +1 -1
  211. package/src/Cli/{components → tui/components}/Plan.tsx +6 -2
  212. package/src/Cli/{components → tui/components}/PlanProgress.tsx +7 -3
  213. package/src/Cloudflare/Auth/AuthProvider.ts +0 -5
  214. package/src/Cloudflare/SecretsStore/SecretsStore.ts +5 -5
  215. package/src/Cloudflare/StateStore/Api.ts +15 -2
  216. package/src/Cloudflare/StateStore/State.ts +3 -9
  217. package/src/Cloudflare/Workers/Worker.ts +29 -44
  218. package/src/Output.ts +10 -2
  219. package/src/Provider.ts +1 -1
  220. package/src/Stack.ts +2 -2
  221. package/src/Telemetry/Layer.ts +13 -1
  222. package/src/Test/Bun.ts +164 -140
  223. package/src/Test/Core.ts +225 -0
  224. package/src/Test/Vitest.ts +157 -509
  225. package/src/Test/index.ts +0 -1
  226. package/src/Util/index.ts +1 -0
  227. package/src/index.ts +14 -0
  228. package/lib/Cli/InkCLI.d.ts.map +0 -1
  229. package/lib/Cli/InkCLI.js.map +0 -1
  230. package/lib/Cli/components/ApprovePlan.d.ts.map +0 -1
  231. package/lib/Cli/components/ApprovePlan.js.map +0 -1
  232. package/lib/Cli/components/Plan.d.ts.map +0 -1
  233. package/lib/Cli/components/Plan.js.map +0 -1
  234. package/lib/Cli/components/PlanProgress.d.ts.map +0 -1
  235. package/lib/Cli/components/PlanProgress.js.map +0 -1
  236. package/lib/cli/index.d.ts +0 -413
  237. package/lib/cli/index.d.ts.map +0 -1
  238. package/lib/cli/index.js +0 -10
  239. package/lib/cli/index.js.map +0 -1
  240. /package/lib/Cli/{components → tui/components}/ApprovePlan.js +0 -0
package/bin/exec.js ADDED
@@ -0,0 +1,2360 @@
1
+ import * as EffectContext from "effect/Context";
2
+ import * as Effect from "effect/Effect";
3
+ import * as FileSystem from "effect/FileSystem";
4
+ import * as Layer from "effect/Layer";
5
+ import * as Path$1 from "effect/Path";
6
+ import { Path } from "effect/Path";
7
+ import * as Data from "effect/Data";
8
+ import { pipe } from "effect/Function";
9
+ import * as Redacted from "effect/Redacted";
10
+ import { SingleShotGen } from "effect/Utils";
11
+ import * as Option from "effect/Option";
12
+ import { pipeArguments } from "effect/Pipeable";
13
+ import * as ConfigProvider from "effect/ConfigProvider";
14
+ import * as Logger from "effect/Logger";
15
+ import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
16
+ import * as Deferred from "effect/Deferred";
17
+ import * as S from "effect/Schema";
18
+ import path from "pathe";
19
+ import * as Config from "effect/Config";
20
+ import os from "node:os";
21
+ import * as Console from "effect/Console";
22
+ import * as Command from "effect/unstable/cli/Command";
23
+ import * as Flag from "effect/unstable/cli/Flag";
24
+ import * as Duration from "effect/Duration";
25
+ import * as Exit from "effect/Exit";
26
+ import * as Metric from "effect/Metric";
27
+ import "effect/Cause";
28
+ import * as Argument from "effect/unstable/cli/Argument";
29
+ import * as CliError from "effect/unstable/cli/CliError";
30
+ import { jsx, jsxs } from "react/jsx-runtime";
31
+ import { Box, Text, render, useApp, useInput } from "ink";
32
+ import { useEffect, useMemo, useRef, useState } from "react";
33
+
34
+ //#region lib/AlchemyContext.js
35
+ var AlchemyContext = class extends EffectContext.Service()("alchemy/Context") {};
36
+ const AlchemyContextLive = Layer.effect(AlchemyContext, Effect.gen(function* () {
37
+ const fs = yield* FileSystem.FileSystem;
38
+ const dir = (yield* Path$1.Path).join(process.cwd(), ".alchemy");
39
+ yield* fs.makeDirectory(dir, { recursive: true });
40
+ return {
41
+ dotAlchemy: dir,
42
+ updateStateStore: false,
43
+ dev: false
44
+ };
45
+ }));
46
+
47
+ //#endregion
48
+ //#region lib/Util/data.js
49
+ const isPrimitive = (value) => value === void 0 || value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string" || typeof value === "symbol" || typeof value === "bigint";
50
+
51
+ //#endregion
52
+ //#region lib/Util/PlatformServices.js
53
+ const isBun = typeof Bun !== "undefined";
54
+ const PlatformServices = Effect.promise(async () => {
55
+ if (isBun) {
56
+ const [BunServices, BunSocket] = await Promise.all([import("@effect/platform-bun/BunServices"), import("@effect/platform-bun/BunSocket")]);
57
+ return Layer.merge(BunServices.layer, BunSocket.layerWebSocketConstructor);
58
+ } else {
59
+ const [NodeServices, NodeSocket] = await Promise.all([import("@effect/platform-node/NodeServices"), import("@effect/platform-node/NodeSocket")]);
60
+ return Layer.merge(NodeServices.layer, NodeSocket.layerWebSocketConstructor);
61
+ }
62
+ }).pipe(Layer.unwrap);
63
+ const runMain = (effect, options) => {
64
+ if (isBun) import("@effect/platform-bun/BunRuntime").then((BunRuntime) => BunRuntime.runMain(effect, options));
65
+ else import("@effect/platform-node/NodeRuntime").then((NodeRuntime) => NodeRuntime.runMain(effect, options));
66
+ };
67
+
68
+ //#endregion
69
+ //#region lib/Util/service.js
70
+ const GenericService = () => (Kind) => {
71
+ const service = EffectContext.Service(Kind);
72
+ const make$2 = (Type) => EffectContext.Service(`${Kind}<${Type}>`);
73
+ return Object.assign(Object.setPrototypeOf(make$2, service), service);
74
+ };
75
+
76
+ //#endregion
77
+ //#region lib/Util/types.js
78
+ const asEffect = (effect) => Effect.isEffect(effect) ? effect : Effect.succeed(effect);
79
+
80
+ //#endregion
81
+ //#region lib/ExecutionContext.js
82
+ const ExecutionContext = GenericService()("Alchemy::ExecutionContext");
83
+ const CurrentExecutionContext = Effect.serviceOption(ExecutionContext).pipe(Effect.map(Option.getOrUndefined));
84
+
85
+ //#endregion
86
+ //#region lib/Ref.js
87
+ const RefMetadata = Symbol.for("alchemy/RefMetadata");
88
+ const isRef = (s) => s && s[RefMetadata] !== void 0;
89
+ const getRefMetadata = (ref$1) => ref$1[RefMetadata];
90
+ const ref = ({ stack, id, stage: stage$1 }) => {
91
+ const ref$1 = new Proxy({}, { get: (_, prop) => {
92
+ if (prop === RefMetadata) return {
93
+ stack,
94
+ stage: stage$1,
95
+ id
96
+ };
97
+ return of(ref$1)[prop];
98
+ } });
99
+ return ref$1;
100
+ };
101
+
102
+ //#endregion
103
+ //#region lib/FQN.js
104
+ /**
105
+ * Separator used in FQN strings.
106
+ */
107
+ const FQN_SEPARATOR = "/";
108
+ /**
109
+ * Convert a NamespaceNode chain to an array of namespace IDs, from root to leaf.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const ns = { Id: "Child", Parent: { Id: "Parent", Parent: { Id: "Root" } } };
114
+ * toPath(ns); // ["Root", "Parent", "Child"]
115
+ * ```
116
+ */
117
+ const toPath = (ns) => {
118
+ if (!ns) return [];
119
+ const path$1 = [];
120
+ let current = ns;
121
+ while (current) {
122
+ path$1.unshift(current.Id);
123
+ current = current.Parent;
124
+ }
125
+ return path$1;
126
+ };
127
+ /**
128
+ * Create a fully-qualified name (FQN) from a namespace and logical ID.
129
+ * The FQN is a flat key suitable for state storage.
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * const ns = { Id: "Child", Parent: { Id: "Parent" } };
134
+ * toFqn(ns, "MyResource"); // "Parent/Child/MyResource"
135
+ * toFqn(undefined, "MyResource"); // "MyResource"
136
+ * ```
137
+ */
138
+ const toFqn = (ns, logicalId) => {
139
+ const path$1 = toPath(ns);
140
+ path$1.push(logicalId);
141
+ return path$1.join(FQN_SEPARATOR);
142
+ };
143
+ /**
144
+ * Parse an FQN back into namespace path segments and the logical ID.
145
+ *
146
+ * @example
147
+ * ```ts
148
+ * parseFqn("Parent/Child/MyResource"); // { path: ["Parent", "Child"], logicalId: "MyResource" }
149
+ * parseFqn("MyResource"); // { path: [], logicalId: "MyResource" }
150
+ * ```
151
+ */
152
+ const parseFqn = (fqn) => {
153
+ const parts = fqn.split(FQN_SEPARATOR);
154
+ return {
155
+ path: parts,
156
+ logicalId: parts.pop()
157
+ };
158
+ };
159
+
160
+ //#endregion
161
+ //#region lib/Provider.js
162
+ const Provider = (type) => EffectContext.Service()(type);
163
+ const isProviderCollectionService = (value) => {
164
+ return typeof value === "object" && value !== null && "kind" in value && value.kind === "ProviderCollection";
165
+ };
166
+ const findProviderByType = (type) => tryFindProviderByType(type).pipe(Effect.flatMap(Option.match({
167
+ onNone: () => Effect.die(`Provider not found for ${type}`),
168
+ onSome: (provider) => Effect.succeed(provider)
169
+ })));
170
+ const tryFindProviderByType = Effect.fnUntraced(function* (resourceType) {
171
+ const Tag = Provider(resourceType);
172
+ const direct = yield* Effect.serviceOption(Tag);
173
+ if (Option.isSome(direct)) return direct;
174
+ const context = yield* Effect.context();
175
+ for (const value of context.mapUnsafe.values()) if (isProviderCollectionService(value)) {
176
+ const provider = value.get(resourceType);
177
+ if (provider) return Option.some(provider);
178
+ }
179
+ return Option.none();
180
+ });
181
+
182
+ //#endregion
183
+ //#region lib/Artifacts.js
184
+ /**
185
+ * Per-resource in-memory artifacts shared across a single `Plan.make -> apply`
186
+ * execution.
187
+ *
188
+ * The engine scopes this service by resource `FQN` before invoking lifecycle
189
+ * handlers, so providers should treat it as a resource-local bag for expensive,
190
+ * deterministic intermediate results that can be reused across phases.
191
+ *
192
+ * Expected usage:
193
+ *
194
+ * - `diff` computes an expensive artifact once and stores it
195
+ * - `create` / `update` reads the same artifact and skips recomputing it
196
+ * - artifacts are ephemeral and must never be required for correctness on a
197
+ * later deploy because the bag is reset between runs
198
+ *
199
+ * Example:
200
+ *
201
+ * ```ts
202
+ * const artifacts = yield* Artifacts;
203
+ * const cached = yield* artifacts.get<PreparedBundle>("bundle");
204
+ * if (cached) return cached;
205
+ *
206
+ * const bundle = yield* prepareBundle();
207
+ * yield* artifacts.set("bundle", bundle);
208
+ * return bundle;
209
+ * ```
210
+ */
211
+ var Artifacts = class extends EffectContext.Service()("Artifacts") {};
212
+ var ArtifactStore = class extends EffectContext.Service()("Artifacts/Store") {};
213
+ /**
214
+ * Create a fresh root store for one deploy/test run.
215
+ */
216
+ const createArtifactStore = () => /* @__PURE__ */ new Map();
217
+ const getOrCreateBag = (store, fqn) => {
218
+ const existing = store.get(fqn);
219
+ if (existing) return existing;
220
+ const bag = /* @__PURE__ */ new Map();
221
+ store.set(fqn, bag);
222
+ return bag;
223
+ };
224
+ const makeScopedArtifacts = (store, fqn) => {
225
+ const bag = getOrCreateBag(store, fqn);
226
+ return {
227
+ get: (key) => Effect.sync(() => bag.get(key)),
228
+ set: (key, value) => Effect.sync(() => {
229
+ bag.set(key, value);
230
+ }),
231
+ delete: (key) => Effect.sync(() => {
232
+ bag.delete(key);
233
+ })
234
+ };
235
+ };
236
+ /**
237
+ * Ensure an artifact root exists, reusing the ambient store when one is already
238
+ * present. This lets nested helpers participate in the same run-scoped cache.
239
+ */
240
+ const ensureArtifactStore = (effect) => Effect.serviceOption(ArtifactStore).pipe(Effect.map(Option.getOrUndefined), Effect.flatMap((existing) => effect.pipe(Effect.provideService(ArtifactStore, existing ?? createArtifactStore()))));
241
+
242
+ //#endregion
243
+ //#region lib/Auth/Profile.js
244
+ const rootDir = path.join(os.homedir(), ".alchemy");
245
+ const configFilePath = path.join(rootDir, "profiles.json");
246
+ /**
247
+ * Config key consulted by the various `fromAuthProvider` /
248
+ * `fromEnvironment` layers to pick which named profile in
249
+ * `~/.alchemy/profiles.json` to use. Defaults to `"default"`.
250
+ */
251
+ const ALCHEMY_PROFILE = Config.string("ALCHEMY_PROFILE").pipe(Config.withDefault("default"));
252
+ const CONFIG_VERSION = 0;
253
+ const emptyConfig = () => ({
254
+ version: CONFIG_VERSION,
255
+ profiles: {}
256
+ });
257
+ const readConfig = FileSystem.FileSystem.asEffect().pipe(Effect.flatMap((fs) => fs.readFileString(configFilePath)), Effect.flatMap((data) => Effect.try({
258
+ try: () => {
259
+ const parsed = JSON.parse(data);
260
+ if (parsed?.version !== CONFIG_VERSION) return emptyConfig();
261
+ return parsed;
262
+ },
263
+ catch: emptyConfig
264
+ })), Effect.orElseSucceed(emptyConfig));
265
+ /**
266
+ * Returns a `ConfigProvider` that overrides `ALCHEMY_PROFILE` with the
267
+ * given `profile` (when explicitly passed via the CLI `--profile` flag),
268
+ * falling through to `base` for everything else.
269
+ *
270
+ * Use this to let the CLI's `--profile <name>` win over `$ALCHEMY_PROFILE`
271
+ * without disturbing other config lookups.
272
+ */
273
+ const withProfileOverride = (base, profile$1) => {
274
+ if (profile$1 === void 0) return base;
275
+ const overrides = { ALCHEMY_PROFILE: profile$1 };
276
+ const overrideProvider = ConfigProvider.make((path$1) => Effect.succeed(path$1.length === 1 && typeof path$1[0] === "string" && path$1[0] in overrides ? ConfigProvider.makeValue(overrides[path$1[0]]) : void 0));
277
+ return ConfigProvider.orElse(base)(overrideProvider);
278
+ };
279
+
280
+ //#endregion
281
+ //#region lib/Auth/AuthProvider.js
282
+ var AuthProviders = class extends EffectContext.Service()("AuthProviders") {};
283
+
284
+ //#endregion
285
+ //#region lib/Cli/Cli.js
286
+ var Cli = class extends EffectContext.Service()("CLI") {};
287
+
288
+ //#endregion
289
+ //#region lib/Stage.js
290
+ var Stage = class extends EffectContext.Service()("Stage") {};
291
+
292
+ //#endregion
293
+ //#region lib/Util/ConfigProvider.js
294
+ const loadConfigProvider = (envFile$1) => {
295
+ if (Option.isSome(envFile$1)) return ConfigProvider.fromDotEnv({ path: envFile$1.value }).pipe(Effect.map((dotEnv) => ConfigProvider.orElse(dotEnv, ConfigProvider.fromEnv())));
296
+ return Effect.gen(function* () {
297
+ if (!(yield* (yield* FileSystem.FileSystem).exists(".env"))) return ConfigProvider.fromEnv();
298
+ return ConfigProvider.orElse(yield* ConfigProvider.fromDotEnv({ path: ".env" }), ConfigProvider.fromEnv());
299
+ });
300
+ };
301
+
302
+ //#endregion
303
+ //#region lib/Util/effect.js
304
+ const taggedFunction = (tag, fn) => {
305
+ const overrides = {
306
+ asEffect: () => tag.asEffect(),
307
+ [Symbol.iterator]: () => tag[Symbol.iterator](),
308
+ pipe: (...fns) => pipeArguments(tag.asEffect(), fns),
309
+ toString: () => `${tag.toString()}.${fn.name}`
310
+ };
311
+ return new Proxy(fn, {
312
+ get: (target, prop, receiver) => Reflect.has(overrides, prop) ? Reflect.get(overrides, prop, receiver) : Reflect.has(target, prop) ? Reflect.get(target, prop, receiver) : Reflect.get(tag, prop, tag),
313
+ has: (target, prop) => Reflect.has(overrides, prop) || Reflect.has(target, prop) || Reflect.has(tag, prop)
314
+ });
315
+ };
316
+
317
+ //#endregion
318
+ //#region lib/Util/FileLogger.js
319
+ const fileLogger = Effect.fnUntraced(function* (...segments) {
320
+ const { dotAlchemy } = yield* AlchemyContext;
321
+ const fs = yield* FileSystem.FileSystem;
322
+ const path$1 = yield* Path;
323
+ const logFile = path$1.join(dotAlchemy, "log", ...segments);
324
+ yield* fs.makeDirectory(path$1.dirname(logFile), { recursive: true });
325
+ return yield* Logger.formatLogFmt.pipe(Logger.toFile(logFile, { flag: "a" }));
326
+ });
327
+
328
+ //#endregion
329
+ //#region lib/Stack.js
330
+ const Stack = Object.assign(taggedFunction(EffectContext.Service()("Stack"), (stackName, options, eff) => eff.pipe(make$1({
331
+ name: stackName,
332
+ ...options
333
+ }), (eff$1) => Object.assign(eff$1, { stage: new Proxy({}, { get: (_, stage$1) => ref({
334
+ stack: stackName,
335
+ stage: stage$1,
336
+ id: stackName
337
+ }) }) }))));
338
+ const StackName = Stack.use((stack) => Effect.succeed(stack.name));
339
+ const make$1 = (options) => (effect) => Effect.scope.pipe(Effect.flatMap((scope) => {
340
+ if (options.state == null) return Effect.die(/* @__PURE__ */ new Error(`Stack "${options.name}" is missing a state store. Add a \`state\` layer to the stack options, e.g.:\n Alchemy.Stack("${options.name}", {\n providers: Cloudflare.providers(),\n state: Cloudflare.state(), // <-- required\n }, ...)\nSee https://v2.alchemy.run/concepts/state-store for available state stores.`));
341
+ if (options.providers == null) return Effect.die(/* @__PURE__ */ new Error(`Stack "${options.name}" is missing a providers layer. Add a \`providers\` layer to the stack options, e.g.:\n Alchemy.Stack("${options.name}", {\n providers: Cloudflare.providers(), // <-- required\n state: Cloudflare.state(),\n }, ...)`));
342
+ return options.providers.pipe(Layer.provideMerge(options.state), Layer.provideMerge(Layer.effect(Stack, Stage.asEffect().pipe(Effect.map((stage$1) => options.stack ?? {
343
+ name: options.name,
344
+ stage: stage$1,
345
+ resources: {},
346
+ bindings: {}
347
+ })))), Layer.buildWithScope(scope));
348
+ }), Effect.flatMap((context) => Effect.all([
349
+ effect,
350
+ Stack.asEffect(),
351
+ Effect.context()
352
+ ]).pipe(Effect.map(([output, stack, services$1]) => ({
353
+ ...stack,
354
+ output,
355
+ services: EffectContext.merge(services$1, context)
356
+ })), Effect.provideContext(context))));
357
+ const CurrentStack = Effect.serviceOption(Stack).asEffect().pipe(Effect.map(Option.getOrUndefined));
358
+ const platform = Layer.mergeAll(PlatformServices, FetchHttpClient.layer, Logger.layer([fileLogger("out")], { mergeWithExisting: true }));
359
+ const alchemy = Layer.mergeAll(AlchemyContextLive);
360
+
361
+ //#endregion
362
+ //#region lib/Resource.js
363
+ const isResource = (value) => {
364
+ return typeof value === "object" && value !== null && "Type" in value;
365
+ };
366
+
367
+ //#endregion
368
+ //#region lib/State/State.js
369
+ var State = class extends EffectContext.Service()("alchemy/State") {};
370
+
371
+ //#endregion
372
+ //#region lib/Output.js
373
+ const inspect = Symbol.for("nodejs.util.inspect.custom");
374
+ const of = (resource) => {
375
+ if (isRef(resource)) {
376
+ const metadata = getRefMetadata(resource);
377
+ return new RefExpr(metadata.stack, metadata.stage, metadata.id);
378
+ }
379
+ return new ResourceExpr(resource);
380
+ };
381
+ const isOutput = (value) => value && (typeof value === "object" || typeof value === "function") && ExprSymbol in value;
382
+ const ExprSymbol = Symbol.for("alchemy/Expr");
383
+ const isExpr = (value) => value && (typeof value === "object" || typeof value === "function") && ExprSymbol in value;
384
+ var BaseExpr = class {
385
+ constructor() {}
386
+ as() {
387
+ return this;
388
+ }
389
+ [Symbol.iterator]() {
390
+ return new SingleShotGen(this);
391
+ }
392
+ asEffect() {
393
+ return this.bind(this.toString());
394
+ }
395
+ bind(id) {
396
+ return ExecutionContext.asEffect().pipe(Effect.flatMap((ctx) => Effect.map(ctx.set(id, this), (key) => ctx.get(key))));
397
+ }
398
+ pipe(...fns) {
399
+ return pipe(this, ...fns);
400
+ }
401
+ toString() {
402
+ return this[inspect]();
403
+ }
404
+ };
405
+ const isResourceExpr = (node) => node?.kind === "ResourceExpr";
406
+ var ResourceExpr = class extends BaseExpr {
407
+ src;
408
+ stables;
409
+ kind = "ResourceExpr";
410
+ constructor(src, stables) {
411
+ super();
412
+ this.src = src;
413
+ this.stables = stables;
414
+ return proxy(this);
415
+ }
416
+ [inspect]() {
417
+ return this.src.LogicalId;
418
+ }
419
+ };
420
+ const isPropExpr = (node) => node?.kind === "PropExpr";
421
+ var PropExpr = class extends BaseExpr {
422
+ expr;
423
+ identifier;
424
+ kind = "PropExpr";
425
+ constructor(expr, identifier) {
426
+ super();
427
+ this.expr = expr;
428
+ this.identifier = identifier;
429
+ return proxy(this);
430
+ }
431
+ [inspect]() {
432
+ return `${this.expr[inspect]()}.${this.identifier.toString()}`;
433
+ }
434
+ };
435
+ const isLiteralExpr = (node) => node?.kind === "LiteralExpr";
436
+ var LiteralExpr = class extends BaseExpr {
437
+ value;
438
+ kind = "LiteralExpr";
439
+ constructor(value) {
440
+ super();
441
+ this.value = value;
442
+ return proxy(this);
443
+ }
444
+ [inspect]() {
445
+ return String(this.value);
446
+ }
447
+ };
448
+ const VoidExpr = new LiteralExpr(void 0);
449
+ const isApplyExpr = (node) => node?.kind === "ApplyExpr";
450
+ var ApplyExpr = class extends BaseExpr {
451
+ expr;
452
+ f;
453
+ kind = "ApplyExpr";
454
+ constructor(expr, f) {
455
+ super();
456
+ this.expr = expr;
457
+ this.f = f;
458
+ return proxy(this);
459
+ }
460
+ [inspect]() {
461
+ return `${this.expr[inspect]()}.map(${this.f.toString()})`;
462
+ }
463
+ };
464
+ const isEffectExpr = (node) => node?.kind === "EffectExpr";
465
+ var EffectExpr = class extends BaseExpr {
466
+ expr;
467
+ f;
468
+ kind = "EffectExpr";
469
+ constructor(expr, f) {
470
+ super();
471
+ this.expr = expr;
472
+ this.f = f;
473
+ return proxy(this);
474
+ }
475
+ [inspect]() {
476
+ return `${this.expr[inspect]()}.mapEffect(${this.f.toString()})`;
477
+ }
478
+ };
479
+ const isAllExpr = (node) => node?.kind === "AllExpr";
480
+ var AllExpr = class extends BaseExpr {
481
+ outs;
482
+ kind = "AllExpr";
483
+ constructor(outs) {
484
+ super();
485
+ this.outs = outs;
486
+ return proxy(this);
487
+ }
488
+ [inspect]() {
489
+ return `all(${this.outs.map((out) => out[inspect]()).join(", ")})`;
490
+ }
491
+ };
492
+ const isRefExpr = (node) => node?.kind === "RefExpr";
493
+ var RefExpr = class extends BaseExpr {
494
+ stack;
495
+ stage;
496
+ resourceId;
497
+ kind = "RefExpr";
498
+ constructor(stack, stage$1, resourceId) {
499
+ super();
500
+ this.stack = stack;
501
+ this.stage = stage$1;
502
+ this.resourceId = resourceId;
503
+ return proxy(this);
504
+ }
505
+ [inspect]() {
506
+ return `ref(${this.resourceId}, { stack: ${this.stack}, stage: ${this.stage} })`;
507
+ }
508
+ };
509
+ function proxy(self) {
510
+ const target = Object.assign(() => {}, self);
511
+ if (inspect in self) Object.defineProperty(target, inspect, {
512
+ value: self[inspect].bind(self),
513
+ configurable: true
514
+ });
515
+ const proxy$1 = new Proxy(target, {
516
+ has: (_, prop) => prop === ExprSymbol || prop === inspect ? true : prop in self,
517
+ get: (target$1, prop) => prop === Symbol.toPrimitive ? (hint) => hint === "number" ? NaN : self.toString() : prop === ExprSymbol ? self : prop === inspect ? target$1[inspect] : isResourceExpr(self) && self.stables && prop in self.stables ? self.stables[prop] : prop === "apply" ? self[prop] : prop in self ? typeof self[prop] === "function" && !("kind" in self) ? new PropExpr(proxy$1, prop) : self[prop] : new PropExpr(proxy$1, prop),
518
+ apply: (_, thisArg, args) => {
519
+ if (isPropExpr(self)) {
520
+ if (self.identifier === "apply") return new ApplyExpr(self.expr, args[0]);
521
+ else if (self.identifier === "effect") return new EffectExpr(self.expr, args[0]);
522
+ }
523
+ }
524
+ });
525
+ return proxy$1;
526
+ }
527
+ var MissingSourceError = class extends Data.TaggedError("MissingSourceError") {};
528
+ var InvalidReferenceError = class extends Data.TaggedError("InvalidReferenceError") {};
529
+ const evaluate = (expr, upstream$1) => Effect.gen(function* () {
530
+ if (isResource(expr)) {
531
+ const srcId = expr.FQN;
532
+ const src = upstream$1[srcId];
533
+ if (!src) return yield* new MissingSourceError({
534
+ message: `Source ${srcId} not found`,
535
+ srcId
536
+ });
537
+ return src;
538
+ } else if (isOutput(expr)) {
539
+ if (isResourceExpr(expr)) {
540
+ const srcId = expr.src.FQN;
541
+ const src = upstream$1[srcId];
542
+ if (!src) return yield* new MissingSourceError({
543
+ message: `Source ${srcId} not found`,
544
+ srcId
545
+ });
546
+ return src;
547
+ } else if (isLiteralExpr(expr)) return expr.value;
548
+ else if (isApplyExpr(expr)) return expr.f(yield* evaluate(expr.expr, upstream$1));
549
+ else if (isEffectExpr(expr)) return yield* expr.f(yield* evaluate(expr.expr, upstream$1));
550
+ else if (isAllExpr(expr)) return yield* Effect.all(expr.outs.map((out) => evaluate(out, upstream$1)));
551
+ else if (isPropExpr(expr)) return (yield* evaluate(expr.expr, upstream$1))?.[expr.identifier];
552
+ else if (isRefExpr(expr)) {
553
+ const state = yield* State;
554
+ const stack = expr.stack ?? (yield* Stack).name;
555
+ const stage$1 = expr.stage ?? (yield* Stage);
556
+ const resource = yield* state.get({
557
+ stack,
558
+ stage: stage$1,
559
+ fqn: expr.resourceId
560
+ });
561
+ if (!resource) return yield* Effect.fail(new InvalidReferenceError({
562
+ message: `Reference to '${expr.resourceId}' in stack '${stack}' and stage '${stage$1}' not found. Have you deployed '${stage$1}' or '${stack}'?`,
563
+ stack,
564
+ stage: stage$1,
565
+ resourceId: expr.resourceId
566
+ }));
567
+ return resource.attr;
568
+ }
569
+ }
570
+ if (Array.isArray(expr)) return yield* Effect.all(expr.map((item) => evaluate(item, upstream$1)));
571
+ else if (Redacted.isRedacted(expr)) return expr;
572
+ else if (typeof expr === "object" && expr !== null) return Object.fromEntries(yield* Effect.all(Object.entries(expr).map(([key, value]) => evaluate(value, upstream$1).pipe(Effect.map((value$1) => [key, value$1])))));
573
+ return expr;
574
+ });
575
+ const hasOutputs = (value) => Object.keys(upstreamAny(value)).length > 0;
576
+ const upstreamAny = (value) => {
577
+ if (isResource(value)) return { [value.FQN]: value };
578
+ else if (isExpr(value)) return upstream(value);
579
+ else if (Array.isArray(value)) return Object.assign({}, ...value.map(resolveUpstream));
580
+ else if (value && (typeof value === "object" || typeof value === "function")) return Object.assign({}, ...Object.values(value).map((value$1) => resolveUpstream(value$1)));
581
+ return {};
582
+ };
583
+ const upstream = (expr) => {
584
+ if (isResource(expr)) return { [expr.FQN]: expr };
585
+ else if (isResourceExpr(expr)) return { [expr.src.FQN]: expr.src };
586
+ else if (isPropExpr(expr)) return upstream(expr.expr);
587
+ else if (isAllExpr(expr)) return Object.assign({}, ...expr.outs.map((out) => upstream(out)));
588
+ else if (isEffectExpr(expr) || isApplyExpr(expr)) return upstream(expr.expr);
589
+ else if (Array.isArray(expr)) return expr.map(upstream).reduce(toObject, {});
590
+ else if (typeof expr === "object" && expr !== null) return Object.values(expr).map((v) => upstream(v)).reduce(toObject, {});
591
+ return {};
592
+ };
593
+ const resolveUpstream = (value) => {
594
+ if (isPrimitive(value)) return {};
595
+ else if (isResource(value)) return { [value.FQN]: value };
596
+ else if (isOutput(value)) return upstream(value);
597
+ else if (Array.isArray(value)) return Object.fromEntries(value.map((v) => resolveUpstream(v)).flatMap(Object.entries));
598
+ else if (typeof value === "object" || typeof value === "function") return Object.fromEntries(Object.values(value).map(resolveUpstream).flatMap(Object.entries));
599
+ return {};
600
+ };
601
+ const toObject = (acc, v) => ({
602
+ ...acc,
603
+ ...v
604
+ });
605
+
606
+ //#endregion
607
+ //#region lib/AdoptPolicy.js
608
+ var AdoptPolicy = class extends EffectContext.Service()("AdoptPolicy") {};
609
+
610
+ //#endregion
611
+ //#region lib/Diff.js
612
+ const havePropsChanged = (oldProps, newProps) => hasOutputs(newProps) || JSON.stringify(canonicalize(oldProps ?? {})) !== JSON.stringify(canonicalize(newProps ?? {}));
613
+ const canonicalize = (value) => {
614
+ if (Array.isArray(value)) return value.map(canonicalize);
615
+ if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, nested]) => [key, canonicalize(nested)]));
616
+ return value;
617
+ };
618
+ const diffBindings = (oldBindings, newBindings) => {
619
+ const oldMap = new Map(oldBindings.map((b) => [b.sid, b]));
620
+ const newMap = new Map(newBindings.map((b) => [b.sid, b]));
621
+ return [...Array.from(oldMap).filter(([sid]) => !newMap.has(sid)).map(([sid, old]) => ({
622
+ sid,
623
+ action: "delete",
624
+ data: old.data
625
+ })), ...Array.from(newMap).map(([sid, binding]) => {
626
+ const old = oldMap.get(sid);
627
+ return {
628
+ sid,
629
+ action: !old ? "create" : havePropsChanged(old.data, binding.data) ? "update" : "noop",
630
+ data: binding.data
631
+ };
632
+ })];
633
+ };
634
+
635
+ //#endregion
636
+ //#region lib/InstanceId.js
637
+ /** A 16-byte (128-bit) random hex-encoded string representing an physical instance of a logical resource */
638
+ var InstanceId = class extends EffectContext.Service()("instance-id") {};
639
+ /**
640
+ * @returns Hex-encoded instance ID (16 random bytes)
641
+ */
642
+ const generateInstanceId = () => Effect.sync(() => {
643
+ const bytes = new Uint8Array(16);
644
+ crypto.getRandomValues(bytes);
645
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
646
+ });
647
+
648
+ //#endregion
649
+ //#region lib/Plan.js
650
+ const make = (stack, options = {}) => Effect.gen(function* () {
651
+ const state = yield* State;
652
+ const resources = Object.values(stack.resources);
653
+ const stackName = stack.name;
654
+ const stage$1 = stack.stage;
655
+ const resourceFqns = yield* state.list({
656
+ stack: stackName,
657
+ stage: stage$1
658
+ });
659
+ const oldResources = yield* Effect.all(resourceFqns.map((fqn) => state.get({
660
+ stack: stackName,
661
+ stage: stage$1,
662
+ fqn
663
+ })), { concurrency: "unbounded" });
664
+ const resolvedResources = {};
665
+ const resolveResource = (resourceExpr) => Effect.gen(function* () {
666
+ return yield* resolvedResources[resourceExpr.src.FQN] ??= yield* Effect.cached(Effect.gen(function* () {
667
+ const resource = resourceExpr.src;
668
+ const provider = yield* findProviderByType(resource.Type);
669
+ const props = yield* resolveInput(resource.Props);
670
+ const oldState = yield* state.get({
671
+ stack: stackName,
672
+ stage: stage$1,
673
+ fqn: resource.FQN
674
+ });
675
+ if (!oldState || oldState.status === "creating") return resourceExpr;
676
+ const oldProps = oldState.status === "updating" ? oldState.old.props : oldState.props;
677
+ const oldBindings = oldState.bindings ?? [];
678
+ const newBindings = stack.bindings[resource.FQN] ?? [];
679
+ const diff = yield* provider.diff ? provider.diff({
680
+ id: resource.LogicalId,
681
+ olds: oldProps,
682
+ instanceId: oldState.instanceId,
683
+ news: props,
684
+ output: oldState.attr,
685
+ oldBindings,
686
+ newBindings
687
+ }).pipe(providePlanScope(resource.FQN, oldState.instanceId)) : Effect.succeed(void 0);
688
+ const stables = [...provider.stables ?? [], ...diff?.stables ?? []];
689
+ const withStables = (output) => stables.length > 0 ? new ResourceExpr(resourceExpr.src, Object.fromEntries(stables.map((stable) => [stable, output?.[stable]]))) : resourceExpr;
690
+ if (diff == null) {
691
+ if (havePropsChanged(oldProps, props)) return withStables(oldState?.attr);
692
+ } else if (diff.action === "update") return withStables(oldState?.attr);
693
+ else if (diff.action === "replace") return resourceExpr;
694
+ if (oldState.status === "created" || oldState.status === "updated" || oldState.status === "replaced") return oldState?.attr;
695
+ else return resourceExpr;
696
+ }));
697
+ });
698
+ const resolveInput = (input) => Effect.gen(function* () {
699
+ if (!input) return input;
700
+ else if (isExpr(input)) return yield* resolveOutput(input);
701
+ else if (Redacted.isRedacted(input)) return input;
702
+ else if (Array.isArray(input)) return yield* Effect.all(input.map(resolveInput), { concurrency: "unbounded" });
703
+ else if (isResource(input)) return yield* resolveInput(yield* resolveOutput(of(input)));
704
+ else if (typeof input === "object") return Object.fromEntries(yield* Effect.all(Object.entries(input).map(([key, value]) => resolveInput(value).pipe(Effect.map((value$1) => [key, value$1]))), { concurrency: "unbounded" }));
705
+ return input;
706
+ });
707
+ const resolveOutput = (expr) => Effect.gen(function* () {
708
+ if (isResourceExpr(expr)) return yield* resolveResource(expr);
709
+ else if (isPropExpr(expr)) return (yield* resolveOutput(expr.expr))?.[expr.identifier];
710
+ else if (isApplyExpr(expr)) {
711
+ const upstream$1 = yield* resolveOutput(expr.expr);
712
+ return hasOutputs(upstream$1) ? expr : expr.f(upstream$1);
713
+ } else if (isEffectExpr(expr)) {
714
+ const upstream$1 = yield* resolveOutput(expr.expr);
715
+ return hasOutputs(upstream$1) ? expr : yield* expr.f(upstream$1);
716
+ } else if (isAllExpr(expr)) return yield* Effect.all(expr.outs.map(resolveOutput), { concurrency: "unbounded" });
717
+ else if (isLiteralExpr(expr)) return expr.value;
718
+ return yield* Effect.die(/* @__PURE__ */ new Error("Not implemented yet"));
719
+ });
720
+ const oldDownstreamDependencies = Object.fromEntries(oldResources.filter((resource) => !!resource).map((resource) => [resource.fqn, resource.downstream]));
721
+ const newResourceFqns = new Set(resources.map((r) => r.FQN));
722
+ const newUpstreamDependencies = Object.fromEntries(resources.map((resource) => [resource.FQN, Object.values(upstreamAny(resource.Props)).map((r) => r.FQN)]));
723
+ const bindingUpstreamDependencies = Object.fromEntries(resources.map((resource) => [resource.FQN, Object.values(upstreamAny(stack.bindings[resource.FQN] ?? [])).map((r) => r.FQN)]));
724
+ const rawUpstreamDependencies = Object.fromEntries(resources.map((resource) => {
725
+ const fqn = resource.FQN;
726
+ const propDeps = newUpstreamDependencies[fqn] ?? [];
727
+ const bindDeps = bindingUpstreamDependencies[fqn] ?? [];
728
+ return [fqn, [...new Set([...propDeps, ...bindDeps])]];
729
+ }));
730
+ const allUpstreamDependencies = Object.fromEntries(resources.map((resource) => {
731
+ const fqn = resource.FQN;
732
+ return [fqn, (rawUpstreamDependencies[fqn] ?? []).filter((dep) => newResourceFqns.has(dep))];
733
+ }));
734
+ const newDownstreamDependencies = Object.fromEntries(resources.map((resource) => [resource.FQN, Object.entries(newUpstreamDependencies).filter(([_, upstream$1]) => upstream$1.includes(resource.FQN)).map(([depFqn]) => depFqn)]));
735
+ const resourceGraph = Object.fromEntries((yield* Effect.all(resources.map(Effect.fn("plan.diff.resource")(function* (resource) {
736
+ const provider = yield* findProviderByType(resource.Type);
737
+ const id = resource.LogicalId;
738
+ const fqn = resource.FQN;
739
+ const news = yield* resolveInput(resource.Props);
740
+ const downstream = newDownstreamDependencies[fqn] ?? [];
741
+ const newBindings = yield* resolveInput(stack.bindings[fqn] ?? []);
742
+ const oldState = yield* state.get({
743
+ stack: stackName,
744
+ stage: stage$1,
745
+ fqn
746
+ });
747
+ const oldBindings = oldState?.bindings ?? [];
748
+ const bindingDiffs = diffBindings(oldBindings, newBindings);
749
+ const Node = (node) => ({
750
+ ...node,
751
+ provider,
752
+ resource,
753
+ bindings: bindingDiffs,
754
+ downstream
755
+ });
756
+ if (oldState === void 0) return Node({
757
+ action: "create",
758
+ props: news,
759
+ state: oldState
760
+ });
761
+ else if (oldState.status === "creating" && oldState.attr === void 0) {
762
+ if (provider.read) {
763
+ const attr = yield* provider.read({
764
+ id,
765
+ instanceId: oldState.instanceId,
766
+ olds: oldState.props,
767
+ output: oldState.attr
768
+ }).pipe(providePlanScope(fqn, oldState.instanceId));
769
+ if (attr) return Node({
770
+ action: "create",
771
+ props: news,
772
+ state: {
773
+ ...oldState,
774
+ attr
775
+ }
776
+ });
777
+ }
778
+ }
779
+ const oldProps = oldState.props;
780
+ const diff = yield* asEffect(provider?.diff?.({
781
+ id,
782
+ olds: oldProps,
783
+ instanceId: oldState.instanceId,
784
+ output: oldState.attr,
785
+ news,
786
+ oldBindings,
787
+ newBindings
788
+ }).pipe(providePlanScope(fqn, oldState.instanceId))).pipe(Effect.map((diff$1) => diff$1 ?? { action: havePropsChanged(oldProps, news) || bindingDiffs.some((b) => b.action !== "noop") ? "update" : "noop" }), Effect.map((diff$1) => options.force && diff$1.action === "noop" ? { action: "update" } : diff$1));
789
+ if (oldState.status === "creating") if (diff.action === "noop") return Node({
790
+ action: "create",
791
+ props: news,
792
+ state: oldState
793
+ });
794
+ else if (diff.action === "update") return Node({
795
+ action: "create",
796
+ props: news,
797
+ state: oldState
798
+ });
799
+ else return Node({
800
+ action: "replace",
801
+ props: news,
802
+ deleteFirst: diff.deleteFirst ?? false,
803
+ state: oldState
804
+ });
805
+ else if (oldState.status === "updating") if (diff.action === "update" || diff.action === "noop") return Node({
806
+ action: "update",
807
+ props: news,
808
+ state: oldState
809
+ });
810
+ else return Node({
811
+ action: "replace",
812
+ deleteFirst: diff.deleteFirst ?? false,
813
+ props: news,
814
+ state: oldState
815
+ });
816
+ else if (oldState.status === "replacing") if (diff.action === "noop") return Node({
817
+ action: "replace",
818
+ deleteFirst: oldState.deleteFirst,
819
+ props: news,
820
+ state: oldState
821
+ });
822
+ else if (diff.action === "update") return Node({
823
+ action: "replace",
824
+ deleteFirst: oldState.deleteFirst,
825
+ props: news,
826
+ state: oldState
827
+ });
828
+ else return Node({
829
+ restart: true,
830
+ action: "replace",
831
+ deleteFirst: diff.deleteFirst ?? oldState.deleteFirst,
832
+ props: news,
833
+ state: oldState
834
+ });
835
+ else if (oldState.status === "replaced") if (diff.action === "noop") return Node({
836
+ action: "replace",
837
+ deleteFirst: oldState.deleteFirst,
838
+ props: news,
839
+ state: oldState
840
+ });
841
+ else if (diff.action === "update") return Node({
842
+ action: "update",
843
+ props: news,
844
+ state: oldState
845
+ });
846
+ else return Node({
847
+ restart: true,
848
+ action: "replace",
849
+ deleteFirst: diff.deleteFirst ?? oldState.deleteFirst,
850
+ props: news,
851
+ state: oldState
852
+ });
853
+ else if (oldState.status === "deleting") return Node({
854
+ action: "create",
855
+ props: news,
856
+ state: {
857
+ ...oldState,
858
+ status: "creating",
859
+ props: news
860
+ }
861
+ });
862
+ else if (diff.action === "update") return Node({
863
+ action: "update",
864
+ props: news,
865
+ state: oldState
866
+ });
867
+ else if (diff.action === "replace") return Node({
868
+ action: "replace",
869
+ props: news,
870
+ state: oldState,
871
+ deleteFirst: diff?.deleteFirst ?? false
872
+ });
873
+ else return Node({
874
+ action: "noop",
875
+ state: oldState
876
+ });
877
+ })), { concurrency: "unbounded" })).map((update) => [update.resource.FQN, update]));
878
+ {
879
+ const createReplaceNodes = new Set(Object.entries(resourceGraph).filter(([_, node]) => node.action === "create" || node.action === "replace").map(([fqn]) => fqn));
880
+ if (createReplaceNodes.size > 0) {
881
+ const hasPrecreate = new Set([...createReplaceNodes].filter((fqn) => !!resourceGraph[fqn]?.provider?.precreate));
882
+ const resolved = new Set(hasPrecreate);
883
+ let changed = true;
884
+ while (changed) {
885
+ changed = false;
886
+ for (const fqn of createReplaceNodes) {
887
+ if (resolved.has(fqn)) continue;
888
+ if ((allUpstreamDependencies[fqn] ?? []).filter((dep) => createReplaceNodes.has(dep)).every((dep) => resolved.has(dep))) {
889
+ resolved.add(fqn);
890
+ changed = true;
891
+ }
892
+ }
893
+ }
894
+ const deadlocked = [...createReplaceNodes].filter((fqn) => !resolved.has(fqn));
895
+ if (deadlocked.length > 0) {
896
+ const missingPrecreate = deadlocked.filter((fqn) => !hasPrecreate.has(fqn));
897
+ return yield* Effect.die(new UnsatisfiedResourceCycle({
898
+ message: `Circular dependency detected that cannot be resolved: [${deadlocked.join(", ")}]. Resources lacking a precreate handler: [${missingPrecreate.join(", ")}]. All resources in a dependency cycle must implement precreate to allow early signaling.`,
899
+ cycle: deadlocked,
900
+ missingPrecreate
901
+ }));
902
+ }
903
+ }
904
+ }
905
+ const deletions = Object.fromEntries((yield* Effect.all((yield* state.list({
906
+ stack: stackName,
907
+ stage: stage$1
908
+ })).map(Effect.fn("plan.diff.deletion")(function* (fqn) {
909
+ if (newResourceFqns.has(fqn)) return;
910
+ const oldState = yield* state.get({
911
+ stack: stackName,
912
+ stage: stage$1,
913
+ fqn
914
+ });
915
+ let attr = oldState?.attr;
916
+ if (oldState) {
917
+ const { logicalId } = parseFqn(fqn);
918
+ const resourceType = oldState.resourceType;
919
+ const provider = yield* findProviderByType(resourceType);
920
+ if (oldState.attr === void 0) {
921
+ if (provider.read) attr = yield* provider.read({
922
+ id: logicalId,
923
+ instanceId: oldState.instanceId,
924
+ olds: oldState.props,
925
+ output: oldState.attr
926
+ }).pipe(providePlanScope(fqn, oldState.instanceId));
927
+ }
928
+ return [fqn, {
929
+ action: "delete",
930
+ state: {
931
+ ...oldState,
932
+ attr
933
+ },
934
+ provider,
935
+ resource: {
936
+ Namespace: oldState.namespace,
937
+ FQN: fqn,
938
+ LogicalId: logicalId,
939
+ Type: oldState.resourceType,
940
+ Attributes: attr,
941
+ Props: oldState.props,
942
+ Binding: void 0,
943
+ Provider: Provider(resourceType),
944
+ RemovalPolicy: oldState.removalPolicy,
945
+ ExecutionContext: void 0,
946
+ Providers: void 0
947
+ },
948
+ downstream: oldDownstreamDependencies[fqn] ?? [],
949
+ bindings: oldState.bindings.map((binding) => ({
950
+ sid: binding.sid,
951
+ action: "delete",
952
+ data: binding.data
953
+ }))
954
+ }];
955
+ }
956
+ })), { concurrency: "unbounded" })).filter((v) => !!v));
957
+ for (const resourceFqn of Object.keys(deletions)) {
958
+ const dependencies = Object.entries(rawUpstreamDependencies).filter(([survivorFqn, upstream$1]) => survivorFqn in resourceGraph && upstream$1.includes(resourceFqn)).map(([survivorFqn]) => survivorFqn);
959
+ if (dependencies.length > 0) return yield* new DeleteResourceHasDownstreamDependencies({
960
+ message: `Resource ${resourceFqn} has downstream dependencies`,
961
+ resourceId: resourceFqn,
962
+ dependencies
963
+ });
964
+ }
965
+ return {
966
+ resources: resourceGraph,
967
+ deletions,
968
+ output: stack.output
969
+ };
970
+ }).pipe(ensureArtifactStore, Effect.withSpan("plan.make", { attributes: {
971
+ "alchemy.stack": stack.name,
972
+ "alchemy.stage": stack.stage,
973
+ "alchemy.resources.count": Object.keys(stack.resources).length,
974
+ "alchemy.force": !!options.force
975
+ } }));
976
+ const providePlanScope = (fqn, instanceId) => (effect) => Effect.serviceOption(ArtifactStore).pipe(Effect.map(Option.getOrElse(createArtifactStore)), Effect.flatMap((store) => effect.pipe(Effect.provideService(Artifacts, makeScopedArtifacts(store, fqn)), Effect.provideService(InstanceId, instanceId))));
977
+ var DeleteResourceHasDownstreamDependencies = class extends Data.TaggedError("DeleteResourceHasDownstreamDependencies") {};
978
+ var UnsatisfiedResourceCycle = class extends Data.TaggedError("UnsatisfiedResourceCycle") {};
979
+
980
+ //#endregion
981
+ //#region lib/Telemetry/Metrics.js
982
+ /**
983
+ * Counter for resource lifecycle operations. Tagged per call with
984
+ * `resource_type`, `op` (`precreate`/`create`/`update`/`delete`/`read`),
985
+ * and `status` (`success`/`error`).
986
+ */
987
+ const resourceCounter = Metric.counter("alchemy.resource.operations", {
988
+ description: "Number of resource lifecycle operations dispatched by alchemy apply.",
989
+ incremental: true
990
+ });
991
+ /**
992
+ * Histogram of how long each lifecycle operation takes.
993
+ */
994
+ const resourceDuration = Metric.timer("alchemy.resource.duration", { description: "Wall-clock duration of resource lifecycle operations dispatched by alchemy apply." });
995
+ /**
996
+ * Counter for CLI command invocations.
997
+ */
998
+ const cliCounter = Metric.counter("alchemy.cli.invocations", {
999
+ description: "Number of alchemy CLI command invocations.",
1000
+ incremental: true
1001
+ });
1002
+ /**
1003
+ * Counter for Cloudflare State Store bootstrap/deploy operations.
1004
+ * Tagged per call with `op` (e.g. `deploy`) and `status`
1005
+ * (`success`/`error`) so that the deploy success rate of the
1006
+ * state store itself can be tracked separately from regular
1007
+ * resource lifecycle ops.
1008
+ */
1009
+ const stateStoreCounter = Metric.counter("alchemy.state_store.operations", {
1010
+ description: "Number of Cloudflare State Store deploy/bootstrap operations dispatched by alchemy.",
1011
+ incremental: true
1012
+ });
1013
+ /**
1014
+ * Wraps a resource lifecycle Effect to record a counter + timer entry,
1015
+ * tagged with `resource_type`, `op`, and `status` (`success`/`error`).
1016
+ *
1017
+ * Usage:
1018
+ * ```ts
1019
+ * provider.create(input).pipe(recordResourceOp(node.resource.Type, "create"))
1020
+ * ```
1021
+ */
1022
+ const recordResourceOp = (resourceType, op) => (self) => Effect.suspend(() => {
1023
+ const startNs = process.hrtime.bigint();
1024
+ const baseAttrs = {
1025
+ resource_type: resourceType,
1026
+ op
1027
+ };
1028
+ return self.pipe(Effect.onExit((exit) => recordOutcome(baseAttrs, Exit.isSuccess(exit) ? "success" : "error", elapsed(startNs))));
1029
+ });
1030
+ /**
1031
+ * Wraps a CLI command Effect to bump {@link cliCounter} with the
1032
+ * outcome (`success`/`error`).
1033
+ */
1034
+ const recordCli = (command) => (self) => self.pipe(Effect.onExit((exit) => Metric.update(Metric.withAttributes(cliCounter, {
1035
+ command,
1036
+ status: Exit.isSuccess(exit) ? "success" : "error"
1037
+ }), 1)));
1038
+ const elapsed = (startNs) => Duration.nanos(process.hrtime.bigint() - startNs);
1039
+ const recordOutcome = (baseAttrs, status, duration) => {
1040
+ const attrs = {
1041
+ ...baseAttrs,
1042
+ status
1043
+ };
1044
+ return Effect.all([Metric.update(Metric.withAttributes(resourceCounter, attrs), 1), Metric.update(Metric.withAttributes(resourceDuration, attrs), duration)], { discard: true });
1045
+ };
1046
+
1047
+ //#endregion
1048
+ //#region lib/Apply.js
1049
+ const provideLifecycleScope = (fqn, instanceId) => (effect) => Effect.serviceOption(ArtifactStore).pipe(Effect.map(Option.getOrElse(createArtifactStore)), Effect.flatMap((store) => effect.pipe(Effect.provideService(Artifacts, makeScopedArtifacts(store, fqn)), Effect.provideService(InstanceId, instanceId))));
1050
+ /**
1051
+ * Instruments a single provider lifecycle call with an OTel span
1052
+ * (`provider.<op>`), the resource counter / duration histogram, and the
1053
+ * scoped artifacts/instance services normally supplied by
1054
+ * {@link provideLifecycleScope}.
1055
+ *
1056
+ * This is the only call site through which provider lifecycle methods
1057
+ * are dispatched, so wrapping it here gives us a fully-instrumented
1058
+ * toolchain without touching any individual provider implementation.
1059
+ */
1060
+ const instrumentLifecycle = (op, fqn, resourceType, logicalId, instanceId) => (effect) => effect.pipe(provideLifecycleScope(fqn, instanceId), recordResourceOp(resourceType, op), Effect.withSpan(`provider.${op}`, { attributes: {
1061
+ "alchemy.resource.fqn": fqn,
1062
+ "alchemy.resource.type": resourceType,
1063
+ "alchemy.resource.logical_id": logicalId,
1064
+ "alchemy.resource.instance_id": instanceId,
1065
+ "alchemy.resource.op": op
1066
+ } }));
1067
+ const apply = (plan) => Effect.gen(function* () {
1068
+ const session = yield* (yield* Cli).startApplySession(plan);
1069
+ const state = yield* State;
1070
+ const stack = yield* Stack;
1071
+ const stage$1 = yield* Stage;
1072
+ const stackName = stack.name;
1073
+ const tracker = {};
1074
+ const terminalStatuses = /* @__PURE__ */ new Map();
1075
+ yield* executePlan(plan, tracker, terminalStatuses, session, state, stackName, stage$1);
1076
+ yield* collectGarbage(plan, session);
1077
+ yield* converge(plan, tracker, terminalStatuses, session, state, stackName, stage$1);
1078
+ yield* Effect.forEach(Array.from(terminalStatuses.values()), ({ id, type, status }) => session.emit({
1079
+ kind: "status-change",
1080
+ id,
1081
+ type,
1082
+ status
1083
+ }), { concurrency: "unbounded" });
1084
+ yield* session.done();
1085
+ if (!plan.output) return;
1086
+ const outputs = Object.fromEntries(Object.entries(tracker).map(([fqn, t]) => [fqn, t.output]));
1087
+ return yield* evaluate(plan.output, outputs);
1088
+ }).pipe(ensureArtifactStore, Effect.withSpan("apply", { attributes: {
1089
+ "alchemy.resources.count": Object.keys(plan.resources).length,
1090
+ "alchemy.deletions.count": Object.keys(plan.deletions).length
1091
+ } }));
1092
+ const executePlan = Effect.fnUntraced(function* (plan, tracker, terminalStatuses, session, state, stackName, stage$1) {
1093
+ const ready = Object.fromEntries(yield* Effect.all(Object.keys(plan.resources).map((fqn) => Effect.map(Deferred.make(), (d) => [fqn, d]))));
1094
+ const getOutputs = () => Object.fromEntries(Object.entries(tracker).map(([fqn, t]) => [fqn, t.output]));
1095
+ const waitForDeps = (fqns) => Effect.all(fqns.filter((fqn) => fqn in ready).map((fqn) => Deferred.await(ready[fqn])), { concurrency: "unbounded" });
1096
+ yield* Effect.all(Object.entries(plan.resources).map(([fqn, node]) => executeNode(fqn, node, tracker, ready, terminalStatuses, session, state, stackName, stage$1, getOutputs, waitForDeps)), { concurrency: "unbounded" });
1097
+ });
1098
+ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state, stackName, stage$1, getOutputs, waitForDeps) => Effect.gen(function* () {
1099
+ const logicalId = node.resource.LogicalId;
1100
+ const namespace = node.resource.Namespace;
1101
+ const commit = (value) => state.set({
1102
+ stack: stackName,
1103
+ stage: stage$1,
1104
+ fqn,
1105
+ value: {
1106
+ ...value,
1107
+ namespace
1108
+ }
1109
+ });
1110
+ const scopedSession = {
1111
+ ...session,
1112
+ note: (note) => session.emit({
1113
+ id: logicalId,
1114
+ kind: "annotate",
1115
+ message: note
1116
+ })
1117
+ };
1118
+ const report = (status) => session.emit({
1119
+ kind: "status-change",
1120
+ id: logicalId,
1121
+ type: node.resource.Type,
1122
+ status
1123
+ });
1124
+ const markTerminal = (status) => Effect.sync(() => {
1125
+ terminalStatuses.set(fqn, {
1126
+ id: logicalId,
1127
+ type: node.resource.Type,
1128
+ status
1129
+ });
1130
+ });
1131
+ const signalReady = Deferred.succeed(ready[fqn], void 0);
1132
+ const storeAndSignal = (t) => Effect.gen(function* () {
1133
+ tracker[fqn] = t;
1134
+ yield* signalReady;
1135
+ });
1136
+ if (node.action === "noop") {
1137
+ yield* storeAndSignal({
1138
+ output: node.state.attr,
1139
+ props: node.state.props,
1140
+ bindings: node.state.bindings ?? [],
1141
+ instanceId: node.state.instanceId
1142
+ });
1143
+ return;
1144
+ }
1145
+ const allUpstreamFqns = () => {
1146
+ const propDeps = Object.keys(resolveUpstream(node.props));
1147
+ const bindingDeps = Object.keys(resolveUpstream(node.bindings));
1148
+ return [...new Set([...propDeps, ...bindingDeps])];
1149
+ };
1150
+ const instanceId = yield* Effect.gen(function* () {
1151
+ if (node.action === "create" && !node.state?.instanceId) {
1152
+ const id = yield* generateInstanceId();
1153
+ yield* commit({
1154
+ status: "creating",
1155
+ fqn,
1156
+ logicalId,
1157
+ instanceId: id,
1158
+ downstream: node.downstream,
1159
+ props: node.props,
1160
+ providerVersion: node.provider.version ?? 0,
1161
+ resourceType: node.resource.Type,
1162
+ bindings: excludeDeletedBindings(node.bindings),
1163
+ removalPolicy: node.resource.RemovalPolicy
1164
+ });
1165
+ return id;
1166
+ } else if (node.action === "replace") {
1167
+ if ((node.state.status === "replaced" || node.state.status === "replacing") && !node.restart) return node.state.instanceId;
1168
+ const id = yield* generateInstanceId();
1169
+ yield* commit({
1170
+ status: "replacing",
1171
+ fqn,
1172
+ logicalId,
1173
+ instanceId: id,
1174
+ downstream: node.downstream,
1175
+ props: node.props,
1176
+ providerVersion: node.provider.version ?? 0,
1177
+ resourceType: node.resource.Type,
1178
+ bindings: excludeDeletedBindings(node.bindings),
1179
+ old: node.state,
1180
+ deleteFirst: node.deleteFirst,
1181
+ removalPolicy: node.resource.RemovalPolicy
1182
+ });
1183
+ return id;
1184
+ } else if (node.state?.instanceId) return node.state.instanceId;
1185
+ return yield* Effect.die(`Instance ID not found for resource '${logicalId}' and action is '${node.action}'`);
1186
+ });
1187
+ yield* Effect.gen(function* () {
1188
+ if (node.action === "create") {
1189
+ if (!node.state) yield* commit({
1190
+ status: "creating",
1191
+ fqn,
1192
+ logicalId,
1193
+ instanceId,
1194
+ resourceType: node.resource.Type,
1195
+ props: node.props,
1196
+ attr: void 0,
1197
+ providerVersion: node.provider.version ?? 0,
1198
+ bindings: excludeDeletedBindings(node.bindings),
1199
+ downstream: node.downstream,
1200
+ removalPolicy: node.resource.RemovalPolicy
1201
+ });
1202
+ let attr = node.state?.attr;
1203
+ if (attr !== void 0) yield* storeAndSignal({
1204
+ output: attr,
1205
+ props: {},
1206
+ bindings: [],
1207
+ instanceId
1208
+ });
1209
+ if (node.provider.precreate && attr === void 0) {
1210
+ yield* report("pre-creating");
1211
+ attr = yield* node.provider.precreate({
1212
+ id: logicalId,
1213
+ news: node.props,
1214
+ session: scopedSession,
1215
+ instanceId,
1216
+ bindings: excludeDeletedBindings(node.bindings)
1217
+ }).pipe(instrumentLifecycle("precreate", fqn, node.resource.Type, logicalId, instanceId));
1218
+ yield* commit({
1219
+ status: "creating",
1220
+ fqn,
1221
+ logicalId,
1222
+ instanceId,
1223
+ resourceType: node.resource.Type,
1224
+ props: node.props,
1225
+ attr,
1226
+ providerVersion: node.provider.version ?? 0,
1227
+ bindings: excludeDeletedBindings(node.bindings),
1228
+ downstream: node.downstream,
1229
+ removalPolicy: node.resource.RemovalPolicy
1230
+ });
1231
+ yield* storeAndSignal({
1232
+ output: attr,
1233
+ props: {},
1234
+ bindings: [],
1235
+ instanceId
1236
+ });
1237
+ }
1238
+ yield* report("creating");
1239
+ yield* waitForDeps(allUpstreamFqns());
1240
+ const outputs = getOutputs();
1241
+ const news = yield* evaluate(node.props, outputs);
1242
+ const bindingOutputs = excludeDeletedBindings(yield* evaluate(node.bindings, outputs));
1243
+ attr = yield* node.provider.create({
1244
+ id: logicalId,
1245
+ news,
1246
+ instanceId,
1247
+ bindings: bindingOutputs,
1248
+ session: scopedSession,
1249
+ output: attr
1250
+ }).pipe(instrumentLifecycle("create", fqn, node.resource.Type, logicalId, instanceId));
1251
+ yield* commit({
1252
+ status: "created",
1253
+ fqn,
1254
+ logicalId,
1255
+ instanceId,
1256
+ resourceType: node.resource.Type,
1257
+ props: news,
1258
+ attr,
1259
+ bindings: excludeDeletedBindings(node.bindings),
1260
+ providerVersion: node.provider.version ?? 0,
1261
+ downstream: node.downstream,
1262
+ removalPolicy: node.resource.RemovalPolicy
1263
+ });
1264
+ tracker[fqn] = {
1265
+ output: attr,
1266
+ props: news,
1267
+ bindings: bindingOutputs,
1268
+ instanceId
1269
+ };
1270
+ yield* signalReady;
1271
+ yield* markTerminal("created");
1272
+ return;
1273
+ }
1274
+ if (node.action === "update") {
1275
+ yield* storeAndSignal({
1276
+ output: node.state.attr,
1277
+ props: node.state.props,
1278
+ bindings: node.state.bindings ?? [],
1279
+ instanceId
1280
+ });
1281
+ yield* waitForDeps(allUpstreamFqns());
1282
+ const outputs = getOutputs();
1283
+ const news = yield* evaluate(node.props, outputs);
1284
+ yield* node.state.status === "replaced" ? commit({
1285
+ ...node.state,
1286
+ attr: node.state.attr,
1287
+ props: news
1288
+ }) : commit({
1289
+ status: "updating",
1290
+ fqn,
1291
+ logicalId,
1292
+ instanceId,
1293
+ resourceType: node.resource.Type,
1294
+ props: news,
1295
+ attr: node.state.attr,
1296
+ providerVersion: node.provider.version ?? 0,
1297
+ bindings: excludeDeletedBindings(node.bindings),
1298
+ downstream: node.downstream,
1299
+ old: node.state.status === "updating" ? node.state.old : node.state,
1300
+ removalPolicy: node.resource.RemovalPolicy
1301
+ });
1302
+ yield* report("updating");
1303
+ const previousProps = node.state.status === "created" || node.state.status === "updated" || node.state.status === "replaced" ? node.state.props : node.state.old.props;
1304
+ const bindingOutputs = excludeDeletedBindings(yield* evaluate(node.bindings, outputs));
1305
+ const attr = yield* node.provider.update({
1306
+ id: logicalId,
1307
+ news,
1308
+ instanceId,
1309
+ bindings: bindingOutputs,
1310
+ session: scopedSession,
1311
+ olds: previousProps,
1312
+ output: node.state.attr
1313
+ }).pipe(instrumentLifecycle("update", fqn, node.resource.Type, logicalId, instanceId));
1314
+ if (node.state.status === "replaced") yield* commit({
1315
+ ...node.state,
1316
+ attr,
1317
+ props: news
1318
+ });
1319
+ else yield* commit({
1320
+ status: "updated",
1321
+ fqn,
1322
+ logicalId,
1323
+ instanceId,
1324
+ resourceType: node.resource.Type,
1325
+ props: news,
1326
+ attr,
1327
+ bindings: excludeDeletedBindings(node.bindings),
1328
+ providerVersion: node.provider.version ?? 0,
1329
+ downstream: node.downstream,
1330
+ removalPolicy: node.resource.RemovalPolicy
1331
+ });
1332
+ tracker[fqn] = {
1333
+ output: attr,
1334
+ props: news,
1335
+ bindings: bindingOutputs,
1336
+ instanceId
1337
+ };
1338
+ yield* markTerminal("updated");
1339
+ return;
1340
+ }
1341
+ if (node.action === "replace") {
1342
+ if (node.state.status === "replaced" && !node.restart) {
1343
+ tracker[fqn] = {
1344
+ output: node.state.attr,
1345
+ props: node.state.props,
1346
+ bindings: node.state.bindings ?? [],
1347
+ instanceId
1348
+ };
1349
+ yield* signalReady;
1350
+ yield* markTerminal("created");
1351
+ return;
1352
+ }
1353
+ let replState;
1354
+ if (node.state.status !== "replacing" || node.restart) replState = yield* commit({
1355
+ status: "replacing",
1356
+ fqn,
1357
+ logicalId,
1358
+ instanceId,
1359
+ resourceType: node.resource.Type,
1360
+ props: node.props,
1361
+ bindings: excludeDeletedBindings(node.bindings),
1362
+ attr: void 0,
1363
+ providerVersion: node.provider.version ?? 0,
1364
+ deleteFirst: node.deleteFirst,
1365
+ old: node.state,
1366
+ downstream: node.downstream,
1367
+ removalPolicy: node.resource.RemovalPolicy
1368
+ });
1369
+ else replState = node.state;
1370
+ let attr = replState.attr;
1371
+ if (attr !== void 0) yield* storeAndSignal({
1372
+ output: attr,
1373
+ props: {},
1374
+ bindings: [],
1375
+ instanceId
1376
+ });
1377
+ if (node.provider.precreate && attr === void 0) {
1378
+ yield* report("pre-creating");
1379
+ attr = yield* node.provider.precreate({
1380
+ id: logicalId,
1381
+ news: node.props,
1382
+ session: scopedSession,
1383
+ instanceId,
1384
+ bindings: excludeDeletedBindings(node.bindings)
1385
+ }).pipe(instrumentLifecycle("precreate", fqn, node.resource.Type, logicalId, instanceId));
1386
+ yield* commit({
1387
+ status: "replacing",
1388
+ fqn,
1389
+ logicalId,
1390
+ instanceId,
1391
+ resourceType: node.resource.Type,
1392
+ props: node.props,
1393
+ attr,
1394
+ providerVersion: node.provider.version ?? 0,
1395
+ bindings: excludeDeletedBindings(node.bindings),
1396
+ downstream: node.downstream,
1397
+ old: replState.old,
1398
+ deleteFirst: node.deleteFirst,
1399
+ removalPolicy: node.resource.RemovalPolicy
1400
+ });
1401
+ yield* storeAndSignal({
1402
+ output: attr,
1403
+ props: {},
1404
+ bindings: [],
1405
+ instanceId
1406
+ });
1407
+ }
1408
+ yield* report("creating replacement");
1409
+ yield* waitForDeps(allUpstreamFqns());
1410
+ const outputs = getOutputs();
1411
+ const news = yield* evaluate(node.props, outputs);
1412
+ const bindingOutputs = excludeDeletedBindings(yield* evaluate(node.bindings, outputs));
1413
+ attr = yield* node.provider.create({
1414
+ id: logicalId,
1415
+ news,
1416
+ instanceId,
1417
+ bindings: bindingOutputs,
1418
+ session: scopedSession,
1419
+ output: attr
1420
+ }).pipe(instrumentLifecycle("create", fqn, node.resource.Type, logicalId, instanceId));
1421
+ yield* commit({
1422
+ status: "replaced",
1423
+ fqn,
1424
+ logicalId,
1425
+ instanceId,
1426
+ resourceType: node.resource.Type,
1427
+ props: news,
1428
+ attr,
1429
+ providerVersion: node.provider.version ?? 0,
1430
+ bindings: excludeDeletedBindings(node.bindings),
1431
+ downstream: node.downstream,
1432
+ old: replState.old,
1433
+ deleteFirst: node.deleteFirst,
1434
+ removalPolicy: node.resource.RemovalPolicy
1435
+ });
1436
+ tracker[fqn] = {
1437
+ output: attr,
1438
+ props: news,
1439
+ bindings: bindingOutputs,
1440
+ instanceId
1441
+ };
1442
+ yield* signalReady;
1443
+ yield* markTerminal("created");
1444
+ return;
1445
+ }
1446
+ return yield* Effect.die(`Unknown action: ${node.action}`);
1447
+ });
1448
+ }).pipe(Effect.withSpan("apply.resource", { attributes: {
1449
+ "alchemy.resource.fqn": fqn,
1450
+ "alchemy.resource.type": node.resource.Type,
1451
+ "alchemy.resource.logical_id": node.resource.LogicalId,
1452
+ "alchemy.resource.action": node.action
1453
+ } }));
1454
+ const converge = Effect.fnUntraced(function* (plan, tracker, terminalStatuses, session, state, stackName, stage$1) {
1455
+ for (;;) {
1456
+ let anyUpdated = false;
1457
+ for (const [fqn, node] of Object.entries(plan.resources)) {
1458
+ if (node.action === "noop") continue;
1459
+ if (!tracker[fqn]) continue;
1460
+ const outputs = Object.fromEntries(Object.entries(tracker).map(([k, t]) => [k, t.output]));
1461
+ const newProps = yield* evaluate(node.props, outputs);
1462
+ const newBindings = excludeDeletedBindings(yield* evaluate(node.bindings, outputs));
1463
+ const oldProps = tracker[fqn].props;
1464
+ const oldBindings = tracker[fqn].bindings;
1465
+ const propsChanged = havePropsChanged(oldProps, newProps);
1466
+ const bindingsChanged = JSON.stringify(oldBindings) !== JSON.stringify(newBindings);
1467
+ if (!propsChanged && !bindingsChanged) continue;
1468
+ anyUpdated = true;
1469
+ const logicalId = node.resource.LogicalId;
1470
+ const namespace = node.resource.Namespace;
1471
+ const instanceId = tracker[fqn].instanceId;
1472
+ const scopedSession = {
1473
+ ...session,
1474
+ note: (note) => session.emit({
1475
+ id: logicalId,
1476
+ kind: "annotate",
1477
+ message: note
1478
+ })
1479
+ };
1480
+ const attr = yield* node.provider.update({
1481
+ id: logicalId,
1482
+ news: newProps,
1483
+ instanceId,
1484
+ bindings: newBindings,
1485
+ session: scopedSession,
1486
+ olds: oldProps,
1487
+ output: tracker[fqn].output
1488
+ }).pipe(instrumentLifecycle("update", fqn, node.resource.Type, logicalId, instanceId));
1489
+ tracker[fqn] = {
1490
+ output: attr,
1491
+ props: newProps,
1492
+ bindings: newBindings,
1493
+ instanceId
1494
+ };
1495
+ yield* state.set({
1496
+ stack: stackName,
1497
+ stage: stage$1,
1498
+ fqn,
1499
+ value: {
1500
+ status: "updated",
1501
+ fqn,
1502
+ logicalId,
1503
+ instanceId,
1504
+ resourceType: node.resource.Type,
1505
+ props: newProps,
1506
+ attr,
1507
+ providerVersion: node.provider.version ?? 0,
1508
+ bindings: excludeDeletedBindings(node.bindings),
1509
+ downstream: node.downstream,
1510
+ namespace,
1511
+ removalPolicy: node.resource.RemovalPolicy
1512
+ }
1513
+ });
1514
+ terminalStatuses.set(fqn, {
1515
+ id: logicalId,
1516
+ type: node.resource.Type,
1517
+ status: "updated"
1518
+ });
1519
+ }
1520
+ if (!anyUpdated) break;
1521
+ }
1522
+ });
1523
+ const collectGarbage = Effect.fnUntraced(function* (plan, session) {
1524
+ const state = yield* State;
1525
+ const stackName = (yield* Stack).name;
1526
+ const stage$1 = yield* Stage;
1527
+ const deleteGraph = Effect.fnUntraced(function* (deletionGraph) {
1528
+ const deletions = {};
1529
+ const deleteResource = (node, ancestors = /* @__PURE__ */ new Set()) => Effect.gen(function* () {
1530
+ const isDeleteNode = (node$1) => "action" in node$1;
1531
+ const { logicalId, namespace, resourceType, instanceId, downstream, props, attr, provider } = isDeleteNode(node) ? {
1532
+ logicalId: node.resource.LogicalId,
1533
+ namespace: node.resource.Namespace,
1534
+ resourceType: node.resource.Type,
1535
+ instanceId: node.state.instanceId,
1536
+ downstream: node.downstream,
1537
+ props: node.state.props,
1538
+ attr: node.state.attr,
1539
+ provider: node.provider
1540
+ } : {
1541
+ logicalId: node.logicalId,
1542
+ namespace: node.namespace,
1543
+ resourceType: node.old.resourceType,
1544
+ instanceId: node.old.instanceId,
1545
+ downstream: node.old.downstream,
1546
+ props: node.old.props,
1547
+ attr: node.old.attr,
1548
+ provider: yield* findProviderByType(node.old.resourceType)
1549
+ };
1550
+ const fqn = toFqn(namespace, logicalId);
1551
+ const nextAncestors = new Set(ancestors).add(fqn);
1552
+ const commit = (value) => state.set({
1553
+ stack: stackName,
1554
+ stage: stage$1,
1555
+ fqn,
1556
+ value: {
1557
+ ...value,
1558
+ namespace
1559
+ }
1560
+ });
1561
+ const report = (status) => session.emit({
1562
+ kind: "status-change",
1563
+ id: logicalId,
1564
+ type: resourceType,
1565
+ status
1566
+ });
1567
+ const scopedSession = {
1568
+ ...session,
1569
+ note: (note) => session.emit({
1570
+ id: logicalId,
1571
+ kind: "annotate",
1572
+ message: note
1573
+ })
1574
+ };
1575
+ return yield* deletions[fqn] ??= yield* Effect.cached(Effect.gen(function* () {
1576
+ yield* Effect.all(downstream.map((dep) => dep !== fqn && dep in deletionGraph && !ancestors.has(dep) ? deleteResource(deletionGraph[dep], nextAncestors) : Effect.void), { concurrency: "unbounded" });
1577
+ if (isDeleteNode(node)) {
1578
+ yield* report("deleting");
1579
+ if (node.resource.RemovalPolicy === "retain") {
1580
+ yield* state.delete({
1581
+ stack: stackName,
1582
+ stage: stage$1,
1583
+ fqn
1584
+ });
1585
+ yield* report("deleted");
1586
+ return;
1587
+ }
1588
+ yield* commit({
1589
+ status: "deleting",
1590
+ fqn,
1591
+ logicalId,
1592
+ instanceId,
1593
+ resourceType,
1594
+ props,
1595
+ attr,
1596
+ downstream,
1597
+ providerVersion: provider.version ?? 0,
1598
+ bindings: excludeDeletedBindings(node.bindings),
1599
+ removalPolicy: node.resource.RemovalPolicy
1600
+ });
1601
+ }
1602
+ if (attr !== void 0) yield* provider.delete({
1603
+ id: logicalId,
1604
+ instanceId,
1605
+ olds: props,
1606
+ output: attr,
1607
+ session: scopedSession,
1608
+ bindings: []
1609
+ }).pipe(instrumentLifecycle("delete", fqn, resourceType, logicalId, instanceId));
1610
+ if (isDeleteNode(node)) {
1611
+ yield* state.delete({
1612
+ stack: stackName,
1613
+ stage: stage$1,
1614
+ fqn
1615
+ });
1616
+ yield* report("deleted");
1617
+ } else {
1618
+ yield* scopedSession.note("Cleaning up replaced resource...");
1619
+ if (node.old.status === "replacing" || node.old.status === "replaced") yield* commit({
1620
+ status: "replaced",
1621
+ fqn,
1622
+ logicalId: node.logicalId,
1623
+ instanceId: node.instanceId,
1624
+ resourceType: node.resourceType,
1625
+ props: node.props,
1626
+ attr: node.attr,
1627
+ providerVersion: node.providerVersion,
1628
+ downstream: node.downstream,
1629
+ bindings: excludeDeletedBindings(node.bindings),
1630
+ old: node.old.old,
1631
+ deleteFirst: node.deleteFirst,
1632
+ removalPolicy: node.removalPolicy
1633
+ });
1634
+ else yield* commit({
1635
+ status: "created",
1636
+ fqn,
1637
+ logicalId: node.logicalId,
1638
+ instanceId: node.instanceId,
1639
+ resourceType: node.resourceType,
1640
+ props: node.props,
1641
+ attr: node.attr,
1642
+ providerVersion: node.providerVersion,
1643
+ downstream: node.downstream,
1644
+ bindings: excludeDeletedBindings(node.bindings),
1645
+ removalPolicy: node.removalPolicy
1646
+ });
1647
+ yield* scopedSession.note("Replaced resource cleanup complete.");
1648
+ }
1649
+ }));
1650
+ });
1651
+ yield* Effect.all(Object.values(deletionGraph).filter((node) => node !== void 0).map((node) => deleteResource(node)), { concurrency: "unbounded" });
1652
+ });
1653
+ let first = true;
1654
+ while (true) {
1655
+ const remainingReplacedResources = yield* state.getReplacedResources({
1656
+ stack: stackName,
1657
+ stage: stage$1
1658
+ });
1659
+ if (!first && remainingReplacedResources.length === 0) break;
1660
+ yield* deleteGraph({
1661
+ ...first ? plan.deletions : {},
1662
+ ...Object.fromEntries(remainingReplacedResources.map((replaced) => [toFqn(replaced.namespace, replaced.logicalId), replaced]))
1663
+ });
1664
+ first = false;
1665
+ }
1666
+ });
1667
+ const excludeDeletedBindings = (bindings) => bindings.flatMap(({ action, sid, data }) => action === "delete" ? [] : [{
1668
+ sid,
1669
+ data
1670
+ }]);
1671
+
1672
+ //#endregion
1673
+ //#region lib/Cli/commands/_shared.js
1674
+ var __rewriteRelativeImportExtension = void 0 && (void 0).__rewriteRelativeImportExtension || function(path$1, preserveJsx) {
1675
+ if (typeof path$1 === "string" && /^\.\.?\//.test(path$1)) return path$1.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
1676
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
1677
+ });
1678
+ return path$1;
1679
+ };
1680
+ const USER = Config.string("USER").pipe(Config.orElse(() => Config.string("USERNAME")), Config.withDefault("unknown"));
1681
+ const STAGE = Config.string("stage").pipe(Config.option, (a) => a.asEffect(), Effect.map(Option.getOrUndefined));
1682
+ const stage = Flag.string("stage").pipe(Flag.withSchema(S.String.check(S.isPattern(/^[a-z0-9]+([-_a-z0-9]+)*$/gi))), Flag.withDescription("Stage to deploy to, defaults to dev_${USER}"), Flag.optional, Flag.map(Option.getOrUndefined), Flag.mapEffect(Effect.fn(function* (stage$1) {
1683
+ if (stage$1) return stage$1;
1684
+ return yield* STAGE.pipe(Effect.catch(() => Effect.fail(new CliError.MissingOption({ option: "stage" }))), Effect.flatMap((s) => s === void 0 ? USER.asEffect().pipe(Effect.map((user) => `dev_${user}`), Effect.catch(() => Effect.succeed("unknown"))) : Effect.succeed(s)));
1685
+ })));
1686
+ const envFile = Flag.file("env-file").pipe(Flag.optional, Flag.withDescription("File to load environment variables from, defaults to .env"));
1687
+ const dryRun = Flag.boolean("dry-run").pipe(Flag.withDescription("Dry run the deployment, do not actually deploy"), Flag.withDefault(false));
1688
+ const yes = Flag.boolean("yes").pipe(Flag.withDescription("Yes to all prompts"), Flag.withDefault(false));
1689
+ const force = Flag.boolean("force").pipe(Flag.withDescription("Force updates for resources that would otherwise no-op"), Flag.withDefault(false));
1690
+ const script = Argument.file("main", { mustExist: true }).pipe(Argument.withDescription("Main file to deploy, defaults to alchemy.run.ts"), Argument.withDefault("alchemy.run.ts"));
1691
+ const profile = Flag.string("profile").pipe(Flag.withDescription("Auth profile to use (~/.alchemy/profiles.json). Defaults to 'default' or $ALCHEMY_PROFILE."), Flag.optional, Flag.map(Option.getOrElse(() => "default")));
1692
+ const resourceFilter = Flag.string("filter").pipe(Flag.withDescription("Comma-separated logical resource IDs (e.g. Api,Sandbox). Only those resources are included."), Flag.optional, Flag.map(Option.getOrUndefined));
1693
+ /**
1694
+ * Wraps a CLI command handler with a top-level OpenTelemetry span
1695
+ * (`cli.<command>`) and bumps the `alchemy.cli.invocations` counter.
1696
+ *
1697
+ * `attrs` runs against the parsed command args and contributes
1698
+ * additional attributes to the span (e.g. stage, profile, dry-run flag).
1699
+ *
1700
+ * Usage:
1701
+ * ```ts
1702
+ * Command.make(
1703
+ * "deploy",
1704
+ * { ...flags },
1705
+ * instrumentCommand("deploy", (a) => ({
1706
+ * "alchemy.stage": a.stage,
1707
+ * "alchemy.profile": a.profile,
1708
+ * }))(execStack),
1709
+ * );
1710
+ * ```
1711
+ */
1712
+ const instrumentCommand = (command, attrs) => (handler) => (args) => handler(args).pipe(Effect.withSpan(`cli.${command}`, { attributes: attrs ? attrs(args) : {} }), recordCli(command));
1713
+ const importStack = Effect.fn(function* (main) {
1714
+ const path$1 = yield* Path$1.Path;
1715
+ const stackEffect = (yield* Effect.promise(() => import(__rewriteRelativeImportExtension(path$1.resolve(process.cwd(), main))))).default;
1716
+ if (!Effect.isEffect(stackEffect)) return yield* Effect.die(/* @__PURE__ */ new Error(`Main file '${main}' must export a default stack definition (export default Alchemy.Stack({...}))`));
1717
+ return stackEffect;
1718
+ });
1719
+
1720
+ //#endregion
1721
+ //#region lib/Cli/commands/deploy.js
1722
+ const ExecStackOptions = S.Struct({
1723
+ main: S.String,
1724
+ stage: S.String,
1725
+ envFile: S.OptionFromOptional(S.String),
1726
+ profile: S.optional(S.String),
1727
+ dryRun: S.optional(S.Boolean),
1728
+ force: S.optional(S.Boolean),
1729
+ yes: S.optional(S.Boolean),
1730
+ destroy: S.optional(S.Boolean),
1731
+ dev: S.optional(S.Boolean),
1732
+ adopt: S.optional(S.Boolean)
1733
+ });
1734
+ const stackSpanAttrs = (args) => ({
1735
+ "alchemy.stage": args.stage,
1736
+ "alchemy.profile": args.profile,
1737
+ "alchemy.main": args.main,
1738
+ "alchemy.dry_run": !!args.dryRun,
1739
+ "alchemy.force": !!args.force,
1740
+ "alchemy.destroy": !!args.destroy,
1741
+ "alchemy.dev": !!args.dev,
1742
+ "alchemy.adopt": !!args.adopt
1743
+ });
1744
+ const adopt = Flag.boolean("adopt").pipe(Flag.withDescription("Adopt pre-existing cloud resources that conflict with this stack instead of failing. Useful for re-importing infrastructure into a fresh state store."), Flag.withDefault(false));
1745
+ const execStack = Effect.fn(function* ({ main, stage: stage$1, envFile: envFile$1, profile: profile$1, dryRun: dryRun$1 = false, force: force$1 = false, yes: yes$1 = false, destroy = false, dev = false, adopt: adopt$1 = false }) {
1746
+ const stackEffect = yield* importStack(main);
1747
+ const services$1 = Layer.mergeAll(Layer.effect(AlchemyContext, AlchemyContext.asEffect().pipe(Effect.map((ctx) => ({
1748
+ ...ctx,
1749
+ dev
1750
+ })))), Layer.succeed(AdoptPolicy, adopt$1), Layer.succeed(ArtifactStore, createArtifactStore()), Layer.succeed(AuthProviders, yield* Effect.serviceOption(AuthProviders).pipe(Effect.map(Option.getOrElse(() => ({}))))), ConfigProvider.layer(withProfileOverride(yield* loadConfigProvider(envFile$1), profile$1)), Logger.layer([fileLogger("out")], { mergeWithExisting: true }), Layer.succeed(Stage, stage$1));
1751
+ yield* Effect.gen(function* () {
1752
+ const cli = yield* Cli;
1753
+ const stack = yield* stackEffect;
1754
+ yield* Effect.gen(function* () {
1755
+ const updatePlan = yield* make(destroy ? {
1756
+ ...stack,
1757
+ resources: {},
1758
+ bindings: {},
1759
+ output: {}
1760
+ } : stack, { force: force$1 });
1761
+ if (dryRun$1) yield* cli.displayPlan(updatePlan);
1762
+ else {
1763
+ const hasChanges = Object.keys(updatePlan.deletions).length > 0 || Object.values(updatePlan.resources).some((node) => node.action !== "noop" || node.bindings.some((b) => b.action !== "noop"));
1764
+ if (!yes$1 && hasChanges) {
1765
+ if (!(yield* cli.approvePlan(updatePlan))) return;
1766
+ }
1767
+ const outputs = yield* apply(updatePlan);
1768
+ if (outputs !== void 0) yield* Console.log(outputs);
1769
+ if (dev) yield* Effect.never;
1770
+ }
1771
+ }).pipe(Effect.provide(stack.services));
1772
+ }).pipe(Effect.provide(services$1));
1773
+ });
1774
+ const deployCommand = Command.make("deploy", {
1775
+ dryRun,
1776
+ force,
1777
+ main: script,
1778
+ envFile,
1779
+ stage,
1780
+ yes,
1781
+ profile,
1782
+ adopt
1783
+ }, instrumentCommand("deploy", stackSpanAttrs)(execStack));
1784
+ const destroyCommand = Command.make("destroy", {
1785
+ dryRun,
1786
+ main: script,
1787
+ envFile,
1788
+ stage,
1789
+ yes,
1790
+ profile
1791
+ }, instrumentCommand("destroy", stackSpanAttrs)((args) => execStack({
1792
+ ...args,
1793
+ destroy: true
1794
+ })));
1795
+ const planCommand = Command.make("plan", {
1796
+ main: script,
1797
+ envFile,
1798
+ stage,
1799
+ profile
1800
+ }, instrumentCommand("plan", stackSpanAttrs)((args) => execStack({
1801
+ ...args,
1802
+ dryRun: true
1803
+ })));
1804
+
1805
+ //#endregion
1806
+ //#region lib/Cli/NamespaceTree.js
1807
+ function buildNamespaceTree(items) {
1808
+ const root = {
1809
+ id: "",
1810
+ path: [],
1811
+ children: /* @__PURE__ */ new Map(),
1812
+ resources: []
1813
+ };
1814
+ const getNode = (path$1) => {
1815
+ let current = root;
1816
+ for (let i = 0; i < path$1.length; i++) {
1817
+ const segment = path$1[i];
1818
+ let child = current.children.get(segment);
1819
+ if (!child) {
1820
+ child = {
1821
+ id: segment,
1822
+ path: path$1.slice(0, i + 1),
1823
+ children: /* @__PURE__ */ new Map(),
1824
+ resources: []
1825
+ };
1826
+ current.children.set(segment, child);
1827
+ }
1828
+ current = child;
1829
+ }
1830
+ return current;
1831
+ };
1832
+ for (const item of items) getNode(toPath(item.resource.Namespace)).resources.push(item);
1833
+ return root;
1834
+ }
1835
+ function deriveNamespaceAction(node) {
1836
+ const actions = /* @__PURE__ */ new Set();
1837
+ for (const resource of node.resources) actions.add(deriveResourceChildrenAction(resource, node));
1838
+ for (const child of node.children.values()) {
1839
+ const childAction = deriveNamespaceAction(child);
1840
+ if (childAction === "mixed") return "mixed";
1841
+ actions.add(childAction);
1842
+ }
1843
+ return deriveAction(actions);
1844
+ }
1845
+ function flattenTree(node, depth = 0, result = []) {
1846
+ flattenNamespace(node, depth, result);
1847
+ return result;
1848
+ }
1849
+ const flattenNamespace = (node, depth, result) => {
1850
+ const sortedResources = [...node.resources].sort((a, b) => a.resource.LogicalId.localeCompare(b.resource.LogicalId));
1851
+ const sortedChildren = Array.from(node.children.entries()).sort(([a], [b]) => a.localeCompare(b));
1852
+ const resourceIds = new Set(sortedResources.map((resource) => resource.resource.LogicalId));
1853
+ for (const [id, child] of sortedChildren) {
1854
+ if (resourceIds.has(id) || isEmpty(child)) continue;
1855
+ result.push({
1856
+ type: "namespace",
1857
+ depth,
1858
+ id,
1859
+ path: child.path,
1860
+ action: deriveNamespaceAction(child),
1861
+ hasChildren: true
1862
+ });
1863
+ flattenNamespace(child, depth + 1, result);
1864
+ }
1865
+ for (const resource of sortedResources) {
1866
+ const childNamespace = node.children.get(resource.resource.LogicalId);
1867
+ result.push({
1868
+ type: "resource",
1869
+ depth,
1870
+ id: resource.resource.LogicalId,
1871
+ path: [...node.path, resource.resource.LogicalId],
1872
+ action: resource.action,
1873
+ resourceType: resource.resource.Type,
1874
+ bindingCount: resource.bindings.length
1875
+ });
1876
+ for (const binding of [...resource.bindings].sort((a, b) => a.sid.localeCompare(b.sid))) result.push({
1877
+ type: "binding",
1878
+ depth: depth + 1,
1879
+ id: binding.sid,
1880
+ path: [
1881
+ ...node.path,
1882
+ resource.resource.LogicalId,
1883
+ binding.sid
1884
+ ],
1885
+ action: binding.action,
1886
+ bindingSid: binding.sid
1887
+ });
1888
+ if (childNamespace) flattenNamespace(childNamespace, depth + 1, result);
1889
+ }
1890
+ };
1891
+ const isEmpty = (node) => node.resources.length === 0 && Array.from(node.children.values()).every(isEmpty);
1892
+ const deriveResourceChildrenAction = (resource, node) => {
1893
+ const actions = new Set([resource.action]);
1894
+ for (const binding of resource.bindings) actions.add(binding.action);
1895
+ const childNamespace = node.children.get(resource.resource.LogicalId);
1896
+ if (childNamespace) actions.add(deriveNamespaceAction(childNamespace));
1897
+ return deriveAction(actions);
1898
+ };
1899
+ const deriveAction = (actions) => {
1900
+ if (actions.size === 0) return "noop";
1901
+ if (actions.has("replace")) return actions.size === 1 ? "replace" : "mixed";
1902
+ if (actions.has("delete")) return actions.size === 1 ? "delete" : "mixed";
1903
+ if (actions.has("create")) return actions.size === 1 ? "create" : "mixed";
1904
+ if (actions.has("update")) return actions.size === 1 ? "update" : "mixed";
1905
+ return "noop";
1906
+ };
1907
+
1908
+ //#endregion
1909
+ //#region lib/Cli/tui/components/Plan.js
1910
+ function Plan({ plan }) {
1911
+ const items = useMemo(() => [...Object.values(plan.resources), ...Object.values(plan.deletions)], [plan]);
1912
+ const flatItems = useMemo(() => {
1913
+ return flattenTree(buildNamespaceTree(items));
1914
+ }, [items]);
1915
+ if (items.length === 0) return jsx(Text, {
1916
+ color: "gray",
1917
+ children: "No changes planned"
1918
+ });
1919
+ const counts = items.reduce((acc, item) => (acc[item.action]++, acc), {
1920
+ create: 0,
1921
+ update: 0,
1922
+ delete: 0,
1923
+ noop: 0,
1924
+ replace: 0
1925
+ });
1926
+ const actions = [
1927
+ "create",
1928
+ "update",
1929
+ "delete",
1930
+ "replace"
1931
+ ].filter((action) => counts[action] > 0);
1932
+ return jsxs(Box, {
1933
+ flexDirection: "column",
1934
+ children: [jsxs(Box, {
1935
+ marginTop: 1,
1936
+ children: [
1937
+ jsx(Text, {
1938
+ underline: true,
1939
+ children: "Plan"
1940
+ }),
1941
+ jsx(Text, { children: ": " }),
1942
+ actions.flatMap((action, i) => {
1943
+ const count = counts[action];
1944
+ const color = actionColor(action);
1945
+ if (count === 0) return [];
1946
+ const box = jsx(Box, { children: jsxs(Text, {
1947
+ color,
1948
+ children: [
1949
+ count,
1950
+ " to ",
1951
+ action
1952
+ ]
1953
+ }) }, action);
1954
+ return i === actions.length - 1 ? [box] : [box, jsx(Text, { children: " | " }, `${action}-separator`)];
1955
+ })
1956
+ ]
1957
+ }), jsx(Box, {
1958
+ flexDirection: "column",
1959
+ marginTop: 1,
1960
+ children: flatItems.map((item) => {
1961
+ const indent = " ".repeat(item.depth);
1962
+ const color = getActionColor(item.action);
1963
+ const icon = getActionIcon(item.action);
1964
+ const key = item.path.join("/");
1965
+ if (item.type === "namespace") return jsxs(Box, {
1966
+ flexDirection: "row",
1967
+ children: [
1968
+ jsx(Text, { children: indent }),
1969
+ jsx(Box, {
1970
+ width: 2,
1971
+ children: jsxs(Text, {
1972
+ color,
1973
+ children: [icon, " "]
1974
+ })
1975
+ }),
1976
+ jsx(Text, {
1977
+ color: "blueBright",
1978
+ children: item.id
1979
+ })
1980
+ ]
1981
+ }, key);
1982
+ if (item.type === "binding") return jsxs(Box, {
1983
+ flexDirection: "row",
1984
+ children: [
1985
+ jsx(Text, { children: indent }),
1986
+ jsx(Box, {
1987
+ width: 2,
1988
+ children: jsxs(Text, {
1989
+ color,
1990
+ children: [icon, " "]
1991
+ })
1992
+ }),
1993
+ jsx(Text, {
1994
+ color: "cyan",
1995
+ children: item.bindingSid
1996
+ })
1997
+ ]
1998
+ }, key);
1999
+ return jsxs(Box, {
2000
+ flexDirection: "row",
2001
+ children: [
2002
+ jsx(Text, { children: indent }),
2003
+ jsx(Box, {
2004
+ width: 2,
2005
+ children: jsxs(Text, {
2006
+ color,
2007
+ children: [icon, " "]
2008
+ })
2009
+ }),
2010
+ jsx(Box, { children: jsx(Text, {
2011
+ bold: true,
2012
+ children: item.id
2013
+ }) }),
2014
+ jsx(Box, {
2015
+ marginLeft: 1,
2016
+ children: jsxs(Text, {
2017
+ color: "blackBright",
2018
+ children: [
2019
+ "(",
2020
+ item.resourceType,
2021
+ ")"
2022
+ ]
2023
+ })
2024
+ }),
2025
+ item.bindingCount !== void 0 && item.bindingCount > 0 && jsx(Box, {
2026
+ marginLeft: 1,
2027
+ children: jsxs(Text, {
2028
+ color: "cyan",
2029
+ children: [
2030
+ "(",
2031
+ item.bindingCount,
2032
+ " bindings)"
2033
+ ]
2034
+ })
2035
+ })
2036
+ ]
2037
+ }, key);
2038
+ })
2039
+ })]
2040
+ });
2041
+ }
2042
+ const getActionColor = (action) => ({
2043
+ noop: "gray",
2044
+ create: "green",
2045
+ update: "yellow",
2046
+ delete: "red",
2047
+ replace: "magenta",
2048
+ mixed: "cyan"
2049
+ })[action] ?? "gray";
2050
+ const getActionIcon = (action) => ({
2051
+ create: "+",
2052
+ update: "~",
2053
+ delete: "-",
2054
+ noop: "•",
2055
+ replace: "!",
2056
+ mixed: "*"
2057
+ })[action] ?? "?";
2058
+ const actionColor = (action) => getActionColor(action);
2059
+
2060
+ //#endregion
2061
+ //#region lib/Cli/tui/components/ApprovePlan.js
2062
+ function ApprovePlan(props) {
2063
+ const { plan, approve } = props;
2064
+ const [selected, setSelected] = useState(0);
2065
+ const { exit } = useApp();
2066
+ useInput((_input, key) => {
2067
+ if (key.leftArrow || key.rightArrow) setSelected((prev) => prev === 0 ? 1 : 0);
2068
+ else if (key.return) {
2069
+ approve(selected === 0);
2070
+ exit();
2071
+ }
2072
+ });
2073
+ return jsxs(Box, {
2074
+ flexDirection: "column",
2075
+ children: [
2076
+ jsx(Plan, { plan }),
2077
+ jsx(Box, {
2078
+ marginTop: 1,
2079
+ children: jsx(Text, { children: "Proceed?" })
2080
+ }),
2081
+ jsxs(Box, {
2082
+ gap: 1,
2083
+ children: [jsxs(Text, {
2084
+ color: selected === 0 ? "green" : "gray",
2085
+ children: [selected === 0 ? "◉" : "○", " Yes"]
2086
+ }), jsxs(Text, {
2087
+ color: selected === 1 ? "red" : "gray",
2088
+ children: [selected === 1 ? "◉" : "○", " No"]
2089
+ })]
2090
+ })
2091
+ ]
2092
+ });
2093
+ }
2094
+
2095
+ //#endregion
2096
+ //#region lib/Cli/tui/components/PlanProgress.js
2097
+ const getTaskKey = (item) => item.path.join("/");
2098
+ const buildProgressRows = (plan) => {
2099
+ return flattenTree(buildNamespaceTree([...Object.values(plan.resources), ...Object.values(plan.deletions).filter((item) => item !== void 0)])).filter((item) => item.type !== "binding").map((item) => item.type === "namespace" ? {
2100
+ key: getTaskKey(item),
2101
+ type: "namespace",
2102
+ id: item.id,
2103
+ depth: item.depth,
2104
+ action: item.action
2105
+ } : {
2106
+ key: getTaskKey(item),
2107
+ type: "resource",
2108
+ id: item.id,
2109
+ depth: item.depth,
2110
+ resourceType: item.resourceType ?? "unknown",
2111
+ action: item.action,
2112
+ persistedApplyStatus: item.action === "noop" ? (() => {
2113
+ const crud = findCrudByLogicalId(plan, item.id);
2114
+ return crud?.action === "noop" ? crud.state.status : void 0;
2115
+ })() : void 0
2116
+ });
2117
+ };
2118
+ const buildLogicalIdIndex = (rows) => {
2119
+ const index = /* @__PURE__ */ new Map();
2120
+ for (const row of rows) {
2121
+ if (row.type !== "resource") continue;
2122
+ const keys = index.get(row.id);
2123
+ if (keys) keys.push(row.key);
2124
+ else index.set(row.id, [row.key]);
2125
+ }
2126
+ return index;
2127
+ };
2128
+ function toPlanTask(rowOrId, planItem) {
2129
+ if (typeof rowOrId === "string") return {
2130
+ key: rowOrId,
2131
+ id: rowOrId,
2132
+ type: planItem.resource.Type,
2133
+ status: planItem.action === "noop" ? planItem.state.status : "pending",
2134
+ updatedAt: Date.now()
2135
+ };
2136
+ return {
2137
+ key: rowOrId.key,
2138
+ id: rowOrId.id,
2139
+ type: rowOrId.resourceType,
2140
+ status: rowOrId.action === "noop" ? rowOrId.persistedApplyStatus ?? "created" : "pending",
2141
+ updatedAt: Date.now()
2142
+ };
2143
+ }
2144
+ const buildInitialTasks = (rows) => new Map(rows.flatMap((row) => row.type === "resource" ? [[row.key, toPlanTask(row)]] : []));
2145
+ function PlanProgress(props) {
2146
+ const { source, plan } = props;
2147
+ const spinner = useGlobalSpinner();
2148
+ const rows = useMemo(() => buildProgressRows(plan), [plan]);
2149
+ const logicalIdIndex = useMemo(() => buildLogicalIdIndex(rows), [rows]);
2150
+ const [tasks, setTasks] = useState(() => buildInitialTasks(rows));
2151
+ const unsubscribeRef = useRef(null);
2152
+ useEffect(() => {
2153
+ unsubscribeRef.current?.();
2154
+ unsubscribeRef.current = source.subscribe((event) => {
2155
+ setTasks((prev) => {
2156
+ const next = new Map(prev);
2157
+ const keys = logicalIdIndex.get(event.id) ?? [];
2158
+ if (event.kind === "status-change") {
2159
+ if (!event.bindingId) for (const key of keys) {
2160
+ const current = next.get(key);
2161
+ next.set(key, {
2162
+ key,
2163
+ id: event.id,
2164
+ type: event.type,
2165
+ status: event.status,
2166
+ message: event.message ?? current?.message,
2167
+ updatedAt: Date.now()
2168
+ });
2169
+ }
2170
+ } else for (const key of keys) {
2171
+ const current = next.get(key);
2172
+ if (!current) continue;
2173
+ next.set(key, {
2174
+ ...current,
2175
+ message: event.message,
2176
+ updatedAt: Date.now()
2177
+ });
2178
+ }
2179
+ return next;
2180
+ });
2181
+ });
2182
+ return () => {
2183
+ unsubscribeRef.current?.();
2184
+ unsubscribeRef.current = null;
2185
+ };
2186
+ }, [logicalIdIndex, source]);
2187
+ useEffect(() => {
2188
+ setTasks(buildInitialTasks(rows));
2189
+ }, [rows]);
2190
+ return jsx(Box, {
2191
+ flexDirection: "column",
2192
+ children: rows.map((row) => {
2193
+ const indent = " ".repeat(row.depth);
2194
+ if (row.type === "namespace") return jsxs(Box, {
2195
+ flexDirection: "row",
2196
+ children: [
2197
+ jsx(Text, { children: indent }),
2198
+ jsx(Box, {
2199
+ width: 2,
2200
+ children: jsx(Text, {
2201
+ color: "blueBright",
2202
+ children: "↳ "
2203
+ })
2204
+ }),
2205
+ jsx(Text, {
2206
+ color: "blueBright",
2207
+ children: row.id
2208
+ })
2209
+ ]
2210
+ }, row.key);
2211
+ const task = tasks.get(row.key) ?? toPlanTask(row);
2212
+ const displayStatus = getDisplayStatus(row, task.status);
2213
+ const color = statusColor(displayStatus);
2214
+ const icon = statusIcon(task.status, spinner);
2215
+ return jsxs(Box, {
2216
+ flexDirection: "column",
2217
+ children: [jsxs(Box, {
2218
+ flexDirection: "row",
2219
+ children: [
2220
+ jsx(Text, { children: indent }),
2221
+ jsx(Box, {
2222
+ width: 2,
2223
+ children: jsxs(Text, {
2224
+ color,
2225
+ children: [icon, " "]
2226
+ })
2227
+ }),
2228
+ jsx(Text, {
2229
+ bold: true,
2230
+ children: task.id
2231
+ }),
2232
+ jsxs(Text, {
2233
+ dimColor: true,
2234
+ children: [
2235
+ " (",
2236
+ task.type,
2237
+ ")"
2238
+ ]
2239
+ }),
2240
+ jsxs(Text, {
2241
+ color,
2242
+ children: [" ", displayStatus]
2243
+ })
2244
+ ]
2245
+ }), task.message ? jsx(Box, {
2246
+ paddingLeft: row.depth * 2 + 2,
2247
+ children: jsxs(Text, {
2248
+ dimColor: true,
2249
+ children: ["• ", task.message]
2250
+ })
2251
+ }) : null]
2252
+ }, row.key);
2253
+ })
2254
+ });
2255
+ }
2256
+ function getDisplayStatus(row, status) {
2257
+ if (row.action === "noop" && (status === "created" || status === "updated")) return "no change";
2258
+ return status;
2259
+ }
2260
+ function statusColor(status) {
2261
+ switch (status) {
2262
+ case "no change": return "gray";
2263
+ case "pending": return "gray";
2264
+ case "creating":
2265
+ case "created": return "green";
2266
+ case "updating":
2267
+ case "updated": return "yellow";
2268
+ case "deleting":
2269
+ case "deleted": return "red";
2270
+ case "fail": return "redBright";
2271
+ default: return;
2272
+ }
2273
+ }
2274
+ function statusIcon(status, spinnerChar) {
2275
+ if (isInProgress(status)) return spinnerChar;
2276
+ if (status === "fail") return "✗";
2277
+ return "✓";
2278
+ }
2279
+ function isInProgress(status) {
2280
+ return status === "pending" || status === "creating" || status === "updating" || status === "deleting";
2281
+ }
2282
+ const spinnerFrames = [
2283
+ "⠋",
2284
+ "⠙",
2285
+ "⠹",
2286
+ "⠸",
2287
+ "⠼",
2288
+ "⠴",
2289
+ "⠦",
2290
+ "⠧",
2291
+ "⠇",
2292
+ "⠏"
2293
+ ];
2294
+ function useGlobalSpinner(intervalMs = 80) {
2295
+ const [index, setIndex] = useState(0);
2296
+ useEffect(() => {
2297
+ const timer = setInterval(() => {
2298
+ setIndex((i) => (i + 1) % spinnerFrames.length);
2299
+ }, intervalMs);
2300
+ return () => clearInterval(timer);
2301
+ }, [intervalMs]);
2302
+ return spinnerFrames[index];
2303
+ }
2304
+ const findCrudByLogicalId = (plan, logicalId) => {
2305
+ for (const node of Object.values(plan.resources)) if (node.resource.LogicalId === logicalId) return node;
2306
+ for (const node of Object.values(plan.deletions)) if (node?.resource.LogicalId === logicalId) return node;
2307
+ };
2308
+
2309
+ //#endregion
2310
+ //#region lib/Cli/tui/InkCLI.js
2311
+ const inkCLI = () => Layer.succeed(Cli, Cli.of({
2312
+ approvePlan,
2313
+ displayPlan,
2314
+ startApplySession
2315
+ }));
2316
+ const approvePlan = Effect.fn(function* (plan) {
2317
+ let approved = false;
2318
+ const { waitUntilExit } = render(jsx(ApprovePlan, {
2319
+ plan,
2320
+ approve: (a) => approved = a
2321
+ }));
2322
+ yield* Effect.promise(waitUntilExit);
2323
+ return approved;
2324
+ });
2325
+ const displayPlan = (plan) => Effect.sync(() => {
2326
+ const { unmount } = render(jsx(Plan, { plan }));
2327
+ unmount();
2328
+ });
2329
+ const startApplySession = Effect.fn(function* (plan) {
2330
+ const listeners = /* @__PURE__ */ new Set();
2331
+ const { unmount } = render(jsx(PlanProgress, {
2332
+ plan,
2333
+ source: { subscribe(listener) {
2334
+ listeners.add(listener);
2335
+ return () => listeners.delete(listener);
2336
+ } }
2337
+ }));
2338
+ return {
2339
+ done: Effect.fn(function* () {
2340
+ yield* Effect.sleep(10);
2341
+ yield* Effect.sync(() => unmount());
2342
+ }),
2343
+ emit: (event) => Effect.sync(() => {
2344
+ for (const listener of listeners) listener(event);
2345
+ })
2346
+ };
2347
+ });
2348
+
2349
+ //#endregion
2350
+ //#region lib/Cli/exec.js
2351
+ const services = Layer.mergeAll(Layer.provideMerge(AlchemyContextLive, PlatformServices), FetchHttpClient.layer, ConfigProvider.layer(ConfigProvider.fromEnv()), inkCLI());
2352
+ const exec = () => execStack(S.decodeSync(ExecStackOptions)(JSON.parse(process.env.ALCHEMY_EXEC_OPTIONS))).pipe(Effect.provide(services), Effect.scoped);
2353
+
2354
+ //#endregion
2355
+ //#region bin/exec.ts
2356
+ exec().pipe(runMain);
2357
+
2358
+ //#endregion
2359
+ export { };
2360
+ //# sourceMappingURL=exec.js.map