alchemy 2.0.0-beta.13 → 2.0.0-beta.14

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 (264) hide show
  1. package/bin/alchemy.js +8386 -8205
  2. package/bin/alchemy.js.map +1 -1
  3. package/bin/alchemy.ts +4 -2
  4. package/bin/cli.js +61 -0
  5. package/bin/commands/deploy.ts +6 -3
  6. package/bin/commands/login.ts +1 -1
  7. package/bin/commands/logs.ts +1 -1
  8. package/bin/commands/state.ts +230 -0
  9. package/bin/commands/tail.ts +1 -1
  10. package/lib/AWS/AutoScaling/LaunchTemplate.d.ts +1 -1
  11. package/lib/AWS/EC2/Instance.d.ts +1 -1
  12. package/lib/AWS/EC2/hosted.d.ts +2 -2
  13. package/lib/AWS/ECS/Task.d.ts +4 -4
  14. package/lib/AWS/ECS/Task.d.ts.map +1 -1
  15. package/lib/AWS/ECS/Task.js +2 -2
  16. package/lib/AWS/ECS/Task.js.map +1 -1
  17. package/lib/AWS/Environment.d.ts +2 -2
  18. package/lib/AWS/Lambda/BucketEventSource.d.ts +1 -1
  19. package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
  20. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts +1 -1
  21. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts.map +1 -1
  22. package/lib/AWS/Lambda/Function.d.ts +2 -2
  23. package/lib/AWS/Lambda/Function.d.ts.map +1 -1
  24. package/lib/AWS/Lambda/Function.js +1 -2
  25. package/lib/AWS/Lambda/Function.js.map +1 -1
  26. package/lib/AWS/Lambda/QueueEventSource.d.ts +1 -1
  27. package/lib/AWS/Lambda/QueueEventSource.d.ts.map +1 -1
  28. package/lib/AWS/Lambda/StreamEventSource.d.ts +1 -1
  29. package/lib/AWS/Lambda/StreamEventSource.d.ts.map +1 -1
  30. package/lib/AWS/Lambda/TableEventSource.d.ts +1 -1
  31. package/lib/AWS/Lambda/TableEventSource.d.ts.map +1 -1
  32. package/lib/AWS/Lambda/TopicEventSource.d.ts +1 -1
  33. package/lib/AWS/Lambda/TopicEventSource.d.ts.map +1 -1
  34. package/lib/AWS/SQS/QueueSink.d.ts.map +1 -1
  35. package/lib/AWS/SQS/QueueSink.js +1 -2
  36. package/lib/AWS/SQS/QueueSink.js.map +1 -1
  37. package/lib/Agent/coding/bash.d.ts +2 -6
  38. package/lib/Agent/coding/bash.d.ts.map +1 -1
  39. package/lib/Agent/coding/bash.js +2 -3
  40. package/lib/Agent/coding/bash.js.map +1 -1
  41. package/lib/Agent/coding/edit.d.ts +1 -1
  42. package/lib/Agent/coding/glob.d.ts +2 -4
  43. package/lib/Agent/coding/glob.d.ts.map +1 -1
  44. package/lib/Agent/coding/glob.js +1 -2
  45. package/lib/Agent/coding/glob.js.map +1 -1
  46. package/lib/Agent/coding/grep.d.ts +2 -4
  47. package/lib/Agent/coding/grep.d.ts.map +1 -1
  48. package/lib/Agent/coding/grep.js +1 -2
  49. package/lib/Agent/coding/grep.js.map +1 -1
  50. package/lib/Agent/coding/read.d.ts +1 -1
  51. package/lib/Agent/coding/write.d.ts +1 -1
  52. package/lib/Agent/util/ripgrep.d.ts +2 -2
  53. package/lib/AlchemyContext.d.ts +13 -0
  54. package/lib/AlchemyContext.d.ts.map +1 -0
  55. package/lib/AlchemyContext.js +18 -0
  56. package/lib/AlchemyContext.js.map +1 -0
  57. package/lib/Auth/AuthProvider.js +1 -1
  58. package/lib/Auth/AuthProvider.js.map +1 -1
  59. package/lib/Auth/Profile.d.ts +1 -1
  60. package/lib/Auth/index.d.ts +3 -1
  61. package/lib/Auth/index.d.ts.map +1 -1
  62. package/lib/Auth/index.js +3 -0
  63. package/lib/Auth/index.js.map +1 -1
  64. package/lib/Build/Command.d.ts +1 -1
  65. package/lib/Build/Memo.d.ts +1 -1
  66. package/lib/Bundle/Docker.d.ts +3 -3
  67. package/lib/Bundle/TempRoot.d.ts +3 -3
  68. package/lib/Cli/InkCLI.d.ts +1 -0
  69. package/lib/Cli/InkCLI.d.ts.map +1 -1
  70. package/lib/Cli/InkCLI.js +5 -0
  71. package/lib/Cli/InkCLI.js.map +1 -1
  72. package/lib/Cli/LoggingCli.d.ts +4 -0
  73. package/lib/Cli/LoggingCli.d.ts.map +1 -0
  74. package/lib/{Test/TestCli.js → Cli/LoggingCli.js} +3 -3
  75. package/lib/Cli/LoggingCli.js.map +1 -0
  76. package/lib/Cloudflare/CloudflareEnvironment.d.ts +1 -1
  77. package/lib/Cloudflare/Container/ContainerApplication.d.ts +2 -2
  78. package/lib/Cloudflare/Container/ContainerApplication.d.ts.map +1 -1
  79. package/lib/Cloudflare/Container/ContainerApplication.js +7 -2
  80. package/lib/Cloudflare/Container/ContainerApplication.js.map +1 -1
  81. package/lib/Cloudflare/EdgeSession.d.ts +53 -0
  82. package/lib/Cloudflare/EdgeSession.d.ts.map +1 -0
  83. package/lib/Cloudflare/EdgeSession.js +78 -0
  84. package/lib/Cloudflare/EdgeSession.js.map +1 -0
  85. package/lib/Cloudflare/Providers.d.ts +1 -1
  86. package/lib/Cloudflare/Providers.d.ts.map +1 -1
  87. package/lib/Cloudflare/StateStore/Api.d.ts +62 -0
  88. package/lib/Cloudflare/StateStore/Api.d.ts.map +1 -0
  89. package/lib/Cloudflare/StateStore/Api.js +101 -0
  90. package/lib/Cloudflare/StateStore/Api.js.map +1 -0
  91. package/lib/Cloudflare/StateStore/State.d.ts +39 -0
  92. package/lib/Cloudflare/StateStore/State.d.ts.map +1 -0
  93. package/lib/Cloudflare/StateStore/State.js +217 -0
  94. package/lib/Cloudflare/StateStore/State.js.map +1 -0
  95. package/lib/Cloudflare/StateStore/Store.d.ts +82 -0
  96. package/lib/Cloudflare/StateStore/Store.d.ts.map +1 -0
  97. package/lib/Cloudflare/StateStore/Store.js +189 -0
  98. package/lib/Cloudflare/StateStore/Store.js.map +1 -0
  99. package/lib/Cloudflare/StateStore/Token.d.ts +41 -0
  100. package/lib/Cloudflare/StateStore/Token.d.ts.map +1 -0
  101. package/lib/Cloudflare/StateStore/Token.js +57 -0
  102. package/lib/Cloudflare/StateStore/Token.js.map +1 -0
  103. package/lib/Cloudflare/StateStore/index.d.ts +2 -0
  104. package/lib/Cloudflare/StateStore/index.d.ts.map +1 -0
  105. package/lib/Cloudflare/StateStore/index.js +2 -0
  106. package/lib/Cloudflare/StateStore/index.js.map +1 -0
  107. package/lib/Cloudflare/Workers/Assets.d.ts +2 -2
  108. package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts +2 -2
  109. package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts.map +1 -1
  110. package/lib/Cloudflare/Workers/DurableObjectNamespace.js +0 -1
  111. package/lib/Cloudflare/Workers/DurableObjectNamespace.js.map +1 -1
  112. package/lib/Cloudflare/Workers/HttpServer.d.ts +1 -1
  113. package/lib/Cloudflare/Workers/HttpServer.d.ts.map +1 -1
  114. package/lib/Cloudflare/Workers/HttpServer.js.map +1 -1
  115. package/lib/Cloudflare/Workers/Worker.d.ts +5 -5
  116. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  117. package/lib/Cloudflare/Workers/Worker.js +0 -1
  118. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  119. package/lib/Cloudflare/index.d.ts +3 -1
  120. package/lib/Cloudflare/index.d.ts.map +1 -1
  121. package/lib/Cloudflare/index.js +3 -1
  122. package/lib/Cloudflare/index.js.map +1 -1
  123. package/lib/Deploy.d.ts +10 -0
  124. package/lib/Deploy.d.ts.map +1 -0
  125. package/lib/Deploy.js +12 -0
  126. package/lib/Deploy.js.map +1 -0
  127. package/lib/Destroy.d.ts +8 -3
  128. package/lib/Destroy.d.ts.map +1 -1
  129. package/lib/Destroy.js +8 -8
  130. package/lib/Destroy.js.map +1 -1
  131. package/lib/Http.d.ts +1 -1
  132. package/lib/Http.d.ts.map +1 -1
  133. package/lib/Http.js +7 -3
  134. package/lib/Http.js.map +1 -1
  135. package/lib/Kubernetes/client.d.ts +10 -10
  136. package/lib/Output.d.ts.map +1 -1
  137. package/lib/Output.js.map +1 -1
  138. package/lib/Platform.d.ts +2 -2
  139. package/lib/Platform.d.ts.map +1 -1
  140. package/lib/Platform.js.map +1 -1
  141. package/lib/Provider.d.ts.map +1 -1
  142. package/lib/Provider.js +0 -1
  143. package/lib/Provider.js.map +1 -1
  144. package/lib/Stack.d.ts +23 -12
  145. package/lib/Stack.d.ts.map +1 -1
  146. package/lib/Stack.js +32 -14
  147. package/lib/Stack.js.map +1 -1
  148. package/lib/State/HttpStateApi.d.ts +96 -0
  149. package/lib/State/HttpStateApi.d.ts.map +1 -0
  150. package/lib/State/HttpStateApi.js +96 -0
  151. package/lib/State/HttpStateApi.js.map +1 -0
  152. package/lib/State/HttpStateStore.d.ts +10 -0
  153. package/lib/State/HttpStateStore.d.ts.map +1 -0
  154. package/lib/State/HttpStateStore.js +102 -0
  155. package/lib/State/HttpStateStore.js.map +1 -0
  156. package/lib/State/InMemoryState.d.ts +2 -30
  157. package/lib/State/InMemoryState.d.ts.map +1 -1
  158. package/lib/State/InMemoryState.js +23 -18
  159. package/lib/State/InMemoryState.js.map +1 -1
  160. package/lib/State/LocalState.d.ts +4 -2
  161. package/lib/State/LocalState.d.ts.map +1 -1
  162. package/lib/State/LocalState.js +9 -41
  163. package/lib/State/LocalState.js.map +1 -1
  164. package/lib/State/State.d.ts +11 -4
  165. package/lib/State/State.d.ts.map +1 -1
  166. package/lib/State/State.js +1 -1
  167. package/lib/State/State.js.map +1 -1
  168. package/lib/State/StateEncoding.d.ts +31 -0
  169. package/lib/State/StateEncoding.d.ts.map +1 -0
  170. package/lib/State/StateEncoding.js +82 -0
  171. package/lib/State/StateEncoding.js.map +1 -0
  172. package/lib/State/Sync.d.ts +23 -0
  173. package/lib/State/Sync.d.ts.map +1 -0
  174. package/lib/State/Sync.js +49 -0
  175. package/lib/State/Sync.js.map +1 -0
  176. package/lib/State/index.d.ts +4 -0
  177. package/lib/State/index.d.ts.map +1 -1
  178. package/lib/State/index.js +4 -0
  179. package/lib/State/index.js.map +1 -1
  180. package/lib/Test/Bun.d.ts +13 -17
  181. package/lib/Test/Bun.d.ts.map +1 -1
  182. package/lib/Test/Bun.js +16 -33
  183. package/lib/Test/Bun.js.map +1 -1
  184. package/lib/Test/TestState.d.ts +1 -2
  185. package/lib/Test/TestState.d.ts.map +1 -1
  186. package/lib/Test/TestState.js +5 -9
  187. package/lib/Test/TestState.js.map +1 -1
  188. package/lib/Test/Vitest.d.ts +3 -2
  189. package/lib/Test/Vitest.d.ts.map +1 -1
  190. package/lib/Test/Vitest.js +20 -15
  191. package/lib/Test/Vitest.js.map +1 -1
  192. package/lib/Test/index.d.ts +0 -1
  193. package/lib/Test/index.d.ts.map +1 -1
  194. package/lib/Test/index.js +0 -1
  195. package/lib/Test/index.js.map +1 -1
  196. package/lib/Util/FileLogger.d.ts +2 -2
  197. package/lib/Util/FileLogger.d.ts.map +1 -1
  198. package/lib/Util/FileLogger.js +2 -2
  199. package/lib/Util/FileLogger.js.map +1 -1
  200. package/lib/cli/index.d.ts +10 -1
  201. package/lib/cli/index.d.ts.map +1 -1
  202. package/lib/cli/index.js +9 -4
  203. package/lib/cli/index.js.map +1 -1
  204. package/lib/index.d.ts +2 -1
  205. package/lib/index.d.ts.map +1 -1
  206. package/lib/index.js +2 -1
  207. package/lib/index.js.map +1 -1
  208. package/package.json +26 -11
  209. package/src/AWS/ECS/Task.ts +4 -4
  210. package/src/AWS/Lambda/Function.ts +2 -3
  211. package/src/AWS/SQS/QueueSink.ts +1 -2
  212. package/src/Agent/coding/bash.ts +2 -3
  213. package/src/Agent/coding/glob.ts +1 -2
  214. package/src/Agent/coding/grep.ts +1 -2
  215. package/src/AlchemyContext.ts +27 -0
  216. package/src/Auth/AuthProvider.ts +1 -1
  217. package/src/Auth/index.ts +3 -1
  218. package/src/Cli/InkCLI.tsx +10 -0
  219. package/src/{Test/TestCli.ts → Cli/LoggingCli.ts} +2 -2
  220. package/src/Cloudflare/Container/ContainerApplication.ts +8 -2
  221. package/src/Cloudflare/EdgeSession.ts +144 -0
  222. package/src/Cloudflare/StateStore/Api.ts +157 -0
  223. package/src/Cloudflare/StateStore/State.ts +318 -0
  224. package/src/Cloudflare/StateStore/Store.ts +288 -0
  225. package/src/Cloudflare/StateStore/Token.ts +62 -0
  226. package/src/Cloudflare/StateStore/index.ts +1 -0
  227. package/src/Cloudflare/Workers/DurableObjectNamespace.ts +1 -6
  228. package/src/Cloudflare/Workers/HttpServer.ts +1 -1
  229. package/src/Cloudflare/Workers/Worker.ts +7 -7
  230. package/src/Cloudflare/index.ts +3 -1
  231. package/src/Deploy.ts +25 -0
  232. package/src/Destroy.ts +25 -13
  233. package/src/Http.ts +31 -16
  234. package/src/Output.ts +0 -2
  235. package/src/Platform.ts +8 -2
  236. package/src/Provider.ts +0 -2
  237. package/src/Stack.ts +122 -47
  238. package/src/State/HttpStateApi.ts +155 -0
  239. package/src/State/HttpStateStore.ts +155 -0
  240. package/src/State/InMemoryState.ts +15 -17
  241. package/src/State/LocalState.ts +13 -45
  242. package/src/State/State.ts +13 -4
  243. package/src/State/StateEncoding.ts +83 -0
  244. package/src/State/Sync.ts +89 -0
  245. package/src/State/index.ts +4 -0
  246. package/src/Test/Bun.ts +29 -87
  247. package/src/Test/TestState.ts +6 -8
  248. package/src/Test/Vitest.ts +32 -23
  249. package/src/Test/index.ts +0 -1
  250. package/src/Util/FileLogger.ts +2 -2
  251. package/src/index.ts +2 -1
  252. package/lib/Config.d.ts +0 -20
  253. package/lib/Config.d.ts.map +0 -1
  254. package/lib/Config.js +0 -22
  255. package/lib/Config.js.map +0 -1
  256. package/lib/Observability.d.ts +0 -2
  257. package/lib/Observability.d.ts.map +0 -1
  258. package/lib/Observability.js +0 -1
  259. package/lib/Observability.js.map +0 -1
  260. package/lib/Test/TestCli.d.ts +0 -4
  261. package/lib/Test/TestCli.d.ts.map +0 -1
  262. package/lib/Test/TestCli.js.map +0 -1
  263. package/src/Config.ts +0 -31
  264. package/src/Observability.ts +0 -0
@@ -9,7 +9,7 @@ export class StateStoreError extends Data.TaggedError("StateStoreError")<{
9
9
  }> {}
10
10
 
11
11
  export class State extends Context.Service<State, StateService>()(
12
- "AWS::Lambda::State",
12
+ "alchemy/State",
13
13
  ) {}
14
14
 
15
15
  /**
@@ -21,8 +21,10 @@ export class State extends Context.Service<State, StateService>()(
21
21
  * for provider operations.
22
22
  */
23
23
  export interface StateService {
24
- listStacks(): Effect.Effect<string[], StateStoreError, never>;
25
- listStages(stack: string): Effect.Effect<string[], StateStoreError, never>;
24
+ listStacks(): Effect.Effect<readonly string[], StateStoreError, never>;
25
+ listStages(
26
+ stack: string,
27
+ ): Effect.Effect<readonly string[], StateStoreError, never>;
26
28
  /**
27
29
  * Get a resource by its FQN (namespace-qualified key).
28
30
  */
@@ -62,11 +64,18 @@ export interface StateService {
62
64
  stage: string;
63
65
  fqn: string;
64
66
  }): Effect.Effect<void, StateStoreError, never>;
67
+ /**
68
+ * Delete an entire stack, or a single stage when `stage` is provided.
69
+ */
70
+ deleteStack(request: {
71
+ stack: string;
72
+ stage?: string;
73
+ }): Effect.Effect<void, StateStoreError, never>;
65
74
  /**
66
75
  * List all resource FQNs in a stack/stage.
67
76
  */
68
77
  list(request: {
69
78
  stack: string;
70
79
  stage: string;
71
- }): Effect.Effect<string[], StateStoreError, never>;
80
+ }): Effect.Effect<readonly string[], StateStoreError, never>;
72
81
  }
@@ -0,0 +1,83 @@
1
+ import * as Redacted from "effect/Redacted";
2
+ import { isResource } from "../Resource.ts";
3
+
4
+ /**
5
+ * JSON marker used to tag a `Redacted<T>` value when writing state.
6
+ * The reviver recognises objects with exactly this key and rebuilds
7
+ * the `Redacted` wrapper on read.
8
+ */
9
+ export const REDACTED_MARKER = "__redacted__";
10
+
11
+ /**
12
+ * Recursively encode a state value for JSON serialisation.
13
+ *
14
+ * - `Redacted<T>` values are wrapped as `{ [REDACTED_MARKER]: <inner> }`
15
+ * so the actual string is persisted rather than the `<redacted>`
16
+ * placeholder produced by the default `toJSON`.
17
+ * - `Resource` instances are flattened to `{ id, type, props, attr }`
18
+ * so persisted state matches the schema used by the loader.
19
+ * - Plain objects and arrays are walked structurally.
20
+ */
21
+ export const encodeState = (value: unknown): unknown => {
22
+ if (value === null || value === undefined) return value;
23
+ if (Redacted.isRedacted(value)) {
24
+ return {
25
+ [REDACTED_MARKER]: encodeState(Redacted.value(value)),
26
+ };
27
+ }
28
+ if (isResource(value)) {
29
+ return {
30
+ id: value.LogicalId,
31
+ type: value.Type,
32
+ props: encodeState(value.Props),
33
+ attr: encodeState(value.Attributes),
34
+ };
35
+ }
36
+ if (Array.isArray(value)) return value.map(encodeState);
37
+ if (typeof value === "object") {
38
+ const result: Record<string, unknown> = {};
39
+ for (const [k, v] of Object.entries(value)) {
40
+ result[k] = encodeState(v);
41
+ }
42
+ return result;
43
+ }
44
+ return value;
45
+ };
46
+
47
+ /**
48
+ * JSON reviver that rebuilds `Redacted<T>` values that were written
49
+ * through {@link encodeState}. Intended for use with `JSON.parse`.
50
+ */
51
+ export const reviveState = (_key: string, value: unknown): unknown => {
52
+ if (
53
+ value !== null &&
54
+ typeof value === "object" &&
55
+ !Array.isArray(value) &&
56
+ REDACTED_MARKER in value
57
+ ) {
58
+ return Redacted.make((value as Record<string, unknown>)[REDACTED_MARKER]);
59
+ }
60
+ return value;
61
+ };
62
+
63
+ /**
64
+ * Recursively walk an already-decoded value and rebuild `Redacted<T>`
65
+ * instances from `{ [REDACTED_MARKER]: <inner> }` envelopes. Mirror
66
+ * image of {@link encodeState} for callers that hold a parsed JS
67
+ * value rather than a JSON string (e.g. the HTTP state-store client,
68
+ * which receives values pre-parsed by `HttpApiClient`).
69
+ */
70
+ export const reviveStateRecursive = (value: unknown): unknown => {
71
+ if (value === null || typeof value !== "object") return value;
72
+ if (Array.isArray(value)) return value.map(reviveStateRecursive);
73
+ const obj = value as Record<string, unknown>;
74
+ const keys = Object.keys(obj);
75
+ if (keys.length === 1 && keys[0] === REDACTED_MARKER) {
76
+ return Redacted.make(reviveStateRecursive(obj[REDACTED_MARKER]));
77
+ }
78
+ const result: Record<string, unknown> = {};
79
+ for (const [k, v] of Object.entries(obj)) {
80
+ result[k] = reviveStateRecursive(v);
81
+ }
82
+ return result;
83
+ };
@@ -0,0 +1,89 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { type StateService } from "./State.ts";
3
+
4
+ /**
5
+ * Synchronize all state (every stack/stage/resource) from `source` into
6
+ * `destination` so that `destination` becomes a mirror of `source`.
7
+ *
8
+ * For each `{ stack, stage, fqn }` present in `source`, the resource is
9
+ * written into `destination`, overwriting any existing entry under the same
10
+ * key. Any keys present in `destination` but absent from `source` are
11
+ * deleted, ensuring the two stores end up structurally identical.
12
+ *
13
+ * Stacks are walked sequentially; stages within a stack and resources
14
+ * within a stage are processed concurrently for throughput.
15
+ */
16
+ export const syncState = Effect.fnUntraced(function* (
17
+ source: StateService,
18
+ destination: StateService,
19
+ options?: {
20
+ stacks?: string[];
21
+ /**
22
+ * Maximum number of resources to copy in parallel within a single stage.
23
+ * @default "unbounded".
24
+ */
25
+ concurrency?: number | "unbounded";
26
+ },
27
+ ) {
28
+ const concurrency = options?.concurrency ?? "unbounded";
29
+ const [sourceStacks, destStacks] = yield* Effect.all([
30
+ source.listStacks(),
31
+ destination.listStacks(),
32
+ ]);
33
+ const sourceStackSet = new Set(
34
+ sourceStacks.filter((stack) => options?.stacks?.includes(stack) ?? true),
35
+ );
36
+
37
+ yield* Effect.forEach(
38
+ sourceStacks,
39
+ Effect.fnUntraced(function* (stack) {
40
+ const [sourceStages, destStages] = yield* Effect.all([
41
+ source.listStages(stack),
42
+ destination.listStages(stack),
43
+ ]);
44
+ const stages = union(sourceStages, destStages);
45
+
46
+ yield* Effect.forEach(
47
+ stages,
48
+ Effect.fnUntraced(function* (stage) {
49
+ const sourceFqns = yield* source.list({ stack, stage });
50
+ const destFqns = yield* destination.list({ stack, stage });
51
+
52
+ const sourceSet = new Set(sourceFqns);
53
+ const toDelete = destFqns.filter((fqn) => !sourceSet.has(fqn));
54
+
55
+ yield* Effect.all(
56
+ [
57
+ Effect.forEach(
58
+ sourceFqns,
59
+ Effect.fnUntraced(function* (fqn) {
60
+ const value = yield* source.get({ stack, stage, fqn });
61
+ if (value) {
62
+ yield* destination.set({ stack, stage, fqn, value });
63
+ }
64
+ }),
65
+ { concurrency },
66
+ ),
67
+ Effect.forEach(
68
+ toDelete,
69
+ (fqn) => destination.delete({ stack, stage, fqn }),
70
+ { concurrency },
71
+ ),
72
+ ],
73
+ { concurrency: "unbounded" },
74
+ );
75
+ }),
76
+ { concurrency: "unbounded" },
77
+ );
78
+ }),
79
+ );
80
+
81
+ yield* Effect.forEach(
82
+ destStacks.filter((stack) => !sourceStackSet.has(stack)),
83
+ (stack) => destination.deleteStack({ stack }),
84
+ );
85
+ });
86
+
87
+ const union = <T>(left: Iterable<T>, right: Iterable<T>) => [
88
+ ...new Set([...left, ...right]),
89
+ ];
@@ -1,4 +1,8 @@
1
+ export * from "./HttpStateApi.ts";
2
+ export * from "./HttpStateStore.ts";
1
3
  export * from "./InMemoryState.ts";
2
4
  export * from "./LocalState.ts";
3
5
  export * from "./ResourceState.ts";
4
6
  export * from "./State.ts";
7
+ export * from "./StateEncoding.ts";
8
+ export * from "./Sync.ts";
package/src/Test/Bun.ts CHANGED
@@ -1,44 +1,37 @@
1
- import { BunServices } from "@effect/platform-bun";
2
1
  import bun from "bun:test";
3
2
  import { ConfigProvider } from "effect/ConfigProvider";
4
3
  import * as Effect from "effect/Effect";
5
4
  import * as Layer from "effect/Layer";
6
5
  import * as Logger from "effect/Logger";
7
6
  import * as Option from "effect/Option";
8
- import type { Scope } from "effect/Scope";
9
7
  import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
10
- import type { HttpClient } from "effect/unstable/http/HttpClient";
11
8
  import type { HookOptions } from "node:test";
12
- import * as Apply from "../Apply.ts";
9
+ import { AlchemyContext, AlchemyContextLive } from "../AlchemyContext.ts";
13
10
  import { provideFreshArtifactStore } from "../Artifacts.ts";
14
11
  import { AuthProviders } from "../Auth/AuthProvider.ts";
15
- import { DotAlchemy, dotAlchemy } from "../Config.ts";
16
- import type { Input } from "../Input.ts";
17
- import * as Plan from "../Plan.ts";
18
- import { type CompiledStack, type StackServices } from "../Stack.ts";
19
- import { Stage } from "../Stage.ts";
12
+ import { deploy as _deploy } from "../Deploy.ts";
13
+ import { destroy as _destroy } from "../Destroy.ts";
14
+ import {
15
+ type CompiledStack,
16
+ type StackEffect,
17
+ type StackServices,
18
+ } from "../Stack.ts";
20
19
  import * as State from "../State/index.ts";
21
20
  import { loadConfigProvider } from "../Util/ConfigProvider.ts";
22
- import { TestCli } from "./TestCli.ts";
21
+ import { PlatformServices } from "../Util/PlatformServices.ts";
23
22
 
24
23
  export type ProvidedServices = StackServices;
25
24
 
26
- type TestEffect<A, Req = never> = Effect.Effect<
27
- A,
28
- any,
29
- BunServices.BunServices | HttpClient | Scope | Req
30
- >;
25
+ type TestEffect<A, Req = never> = StackEffect<A, any, Req>;
31
26
 
32
- const platform = Layer.mergeAll(BunServices.layer, FetchHttpClient.layer);
27
+ const platform = Layer.mergeAll(PlatformServices, FetchHttpClient.layer);
33
28
 
34
- // override alchemy state store, CLI/reporting and dotAlchemy
29
+ // override alchemy state store, CLI/reporting, state, and dotAlchemy
35
30
  const alchemy = Layer.mergeAll(
36
- // TODO(sam): support overriding these
37
- State.LocalState,
38
31
  // CLI.inkCLI(),
39
32
  // optional
40
33
  Logger.layer([Logger.consolePretty()]),
41
- dotAlchemy,
34
+ AlchemyContextLive,
42
35
  );
43
36
 
44
37
  const run = <A>(effect: TestEffect<A>) =>
@@ -51,6 +44,7 @@ const run = <A>(effect: TestEffect<A>) =>
51
44
  );
52
45
  }).pipe(
53
46
  Effect.provideService(AuthProviders, {}),
47
+ Effect.provide(State.localState()),
54
48
  Effect.provide(Layer.provideMerge(alchemy, platform)),
55
49
  Effect.scoped,
56
50
  Effect.runPromise,
@@ -106,10 +100,7 @@ export namespace test {
106
100
 
107
101
  export const describe = bun.describe;
108
102
 
109
- export function beforeAll<A>(
110
- eff: Effect.Effect<A, any>,
111
- options?: HookOptions,
112
- ) {
103
+ export function beforeAll<A>(eff: TestEffect<A>, options?: HookOptions) {
113
104
  let a: A;
114
105
  bun.beforeAll(
115
106
  () => run(eff).then((v) => (a = v)),
@@ -120,21 +111,17 @@ export function beforeAll<A>(
120
111
  return Effect.sync(() => a);
121
112
  }
122
113
 
123
- export function beforeEach(
124
- eff: Effect.Effect<void, any>,
125
- options?: HookOptions,
126
- ) {
114
+ export function beforeEach(eff: TestEffect<void>, options?: HookOptions) {
127
115
  bun.beforeEach(() => run(eff), options);
128
116
  }
129
117
 
130
- export function afterAll(eff: Effect.Effect<any, any>, options?: HookOptions) {
118
+ export function afterAll(eff: TestEffect<any>, options?: HookOptions) {
131
119
  bun.afterAll(() => run(eff), options);
132
120
  }
133
121
 
134
122
  export namespace afterAll {
135
123
  export const skipIf =
136
- (predicate: boolean) =>
137
- (test: Effect.Effect<void, any>, options?: HookOptions) => {
124
+ (predicate: boolean) => (test: TestEffect<void>, options?: HookOptions) => {
138
125
  if (predicate) {
139
126
  } else {
140
127
  bun.afterAll(
@@ -147,75 +134,30 @@ export namespace afterAll {
147
134
  };
148
135
  }
149
136
 
150
- export function afterEach(
151
- eff: Effect.Effect<void, any>,
152
- options?: HookOptions,
153
- ) {
137
+ export function afterEach(eff: TestEffect<void>, options?: HookOptions) {
154
138
  bun.afterEach(() => run(eff), options);
155
139
  }
156
140
 
157
141
  export const deploy = <A>(
158
- effect: TestEffect<CompiledStack<A>, Stage | DotAlchemy>,
142
+ stack: TestEffect<CompiledStack<A>, AlchemyContext>,
159
143
  options?: {
160
144
  /** @default test */
161
145
  stage?: string;
162
146
  },
163
147
  ) =>
164
- exec(
165
- effect,
166
- (stack) =>
167
- Effect.gen(function* () {
168
- const plan = yield* Plan.make(stack);
169
- const output = yield* Apply.apply(plan);
170
- return output as Input.Resolve<A>;
171
- }),
172
- options,
173
- );
148
+ _deploy({
149
+ stack: stack,
150
+ stage: options?.stage ?? "test",
151
+ });
174
152
 
175
153
  export const destroy = (
176
- effect: TestEffect<CompiledStack, Stage | DotAlchemy>,
154
+ stack: TestEffect<CompiledStack, AlchemyContext>,
177
155
  options?: {
178
156
  /** @default test */
179
157
  stage?: string;
180
158
  },
181
159
  ) =>
182
- exec(
183
- effect,
184
- (stack) =>
185
- Effect.gen(function* () {
186
- const plan = yield* Plan.make({
187
- ...stack,
188
- // zero these out (destroy will treat all as orphans)
189
- // TODO(sam): probably better to have Plan.destroy and Plan.update
190
- resources: {},
191
- bindings: {},
192
- output: {},
193
- });
194
- yield* Apply.apply(plan);
195
- }),
196
- options,
197
- );
198
-
199
- const exec = <A, B>(
200
- effect: TestEffect<CompiledStack<A>, Stage | DotAlchemy>,
201
- fn: (stack: CompiledStack<A>) => Effect.Effect<B, any, any>,
202
- options?: {
203
- stage?: string;
204
- },
205
- ) =>
206
- Effect.gen(function* () {
207
- const stack = yield* effect;
208
- const configProvider = yield* loadConfigProvider(Option.none());
209
-
210
- return yield* fn(stack).pipe(
211
- provideFreshArtifactStore,
212
- Effect.provide(stack.services),
213
- Effect.provide(Layer.succeed(ConfigProvider, configProvider)),
214
- );
215
- }).pipe(
216
- Effect.provideService(AuthProviders, {}),
217
- Effect.provide(Layer.succeed(Stage, options?.stage ?? "test")),
218
- Effect.provide(TestCli),
219
- Effect.provide(Layer.provideMerge(alchemy, platform)),
220
- Effect.scoped,
221
- );
160
+ _destroy({
161
+ stack,
162
+ stage: options?.stage ?? "test",
163
+ });
@@ -1,19 +1,17 @@
1
- import * as Effect from "effect/Effect";
2
1
  import * as Layer from "effect/Layer";
3
2
  import { Stack } from "../Stack.ts";
4
- import { Stage } from "../Stage.ts";
5
3
  import * as State from "../State/index.ts";
6
4
 
7
5
  export const state = (resources: Record<string, State.ResourceState> = {}) =>
8
6
  Layer.effect(
9
7
  State.State,
10
- Effect.gen(function* () {
11
- return State.InMemoryService({
12
- [(yield* Stack).name]: {
13
- [yield* Stage]: resources,
8
+ Stack.useSync((stack) =>
9
+ State.InMemoryService({
10
+ [stack.name]: {
11
+ [stack.stage]: resources,
14
12
  },
15
- });
16
- }),
13
+ }),
14
+ ),
17
15
  );
18
16
 
19
17
  export const defaultState = (
@@ -22,6 +22,7 @@ import * as AWS from "../AWS/index.ts";
22
22
  import * as Cloudflare from "../Cloudflare/index.ts";
23
23
 
24
24
  import type { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
25
+ import { AlchemyContext, AlchemyContextLive } from "../AlchemyContext.ts";
25
26
  import { apply } from "../Apply.ts";
26
27
  import { Artifacts, provideFreshArtifactStore } from "../Artifacts.ts";
27
28
  import { AuthProviders } from "../Auth/AuthProvider.ts";
@@ -30,12 +31,12 @@ import * as AWSEnvironment from "../AWS/Environment.ts";
30
31
  import * as AWSRegion from "../AWS/Region.ts";
31
32
  import type { Command } from "../Build/Command.ts";
32
33
  import type { Cli } from "../Cli/index.ts";
34
+ import { LoggingCli } from "../Cli/LoggingCli.ts";
33
35
  import {
34
36
  buildNamespaceTree,
35
37
  flattenTree,
36
38
  type DerivedAction,
37
39
  } from "../Cli/NamespaceTree.ts";
38
- import { DotAlchemy, dotAlchemy } from "../Config.ts";
39
40
  import { ExecutionContext } from "../ExecutionContext.ts";
40
41
  import type { Input } from "../Input.ts";
41
42
  import type { Output } from "../Output.ts";
@@ -47,7 +48,6 @@ import * as Stack from "../Stack.ts";
47
48
  import * as Stage from "../Stage.ts";
48
49
  import * as State from "../State/index.ts";
49
50
  import { PlatformServices } from "../Util/PlatformServices.ts";
50
- import { TestCli } from "./TestCli.ts";
51
51
 
52
52
  export const expectEmptyObject = () =>
53
53
  expect.toSatisfy(
@@ -70,7 +70,7 @@ type Provided =
70
70
  | Stack.Stack
71
71
  | State.State
72
72
  | Stage.Stage
73
- | DotAlchemy
73
+ | AlchemyContext
74
74
  | HttpClient.HttpClient
75
75
  | FileSystem.FileSystem
76
76
  | Path.Path
@@ -101,7 +101,7 @@ const platform = Layer.mergeAll(
101
101
 
102
102
  const awsEnvironment = Layer.unwrap(
103
103
  Effect.gen(function* () {
104
- const profileName = yield* Config.string("AWS_PROFILE").pipe(
104
+ const _profileName = yield* Config.string("AWS_PROFILE").pipe(
105
105
  Config.withDefault("default"),
106
106
  );
107
107
  const LOCAL = yield* Config.boolean("LOCAL").pipe(
@@ -176,8 +176,8 @@ const runWithContext = <A, Err>(
176
176
  );
177
177
 
178
178
  const alchemy = Layer.provideMerge(
179
- Layer.mergeAll(options.state ?? State.LocalState, TestCli),
180
- Layer.mergeAll(stack, dotAlchemy),
179
+ Layer.mergeAll(options.state ?? State.localState(), LoggingCli),
180
+ Layer.mergeAll(stack, AlchemyContextLive),
181
181
  );
182
182
 
183
183
  const context = {
@@ -357,23 +357,22 @@ export namespace test {
357
357
  export const deploy = <A, Err = never, Req = any>(
358
358
  effect: Effect.Effect<A, Err, Req>,
359
359
  ): Effect.Effect<Input.Resolve<A>, Err, Req | Stack.Stack> =>
360
- Stack.Stack.use((stack) => {
361
- return provideFreshArtifactStore(
362
- effect.pipe(
363
- // Effect.tap(Effect.logInfo),
364
- // @ts-expect-error
365
- Stack.make(stack.name, Layer.effectContext(Effect.context<never>()), {
366
- ...stack,
367
- resources: {},
368
- bindings: {},
369
- output: {},
370
- }),
371
- Effect.flatMap(Plan.make),
372
- Effect.tap((plan) => Effect.logInfo(formatPlan(plan))),
373
- Effect.flatMap(apply),
374
- ),
375
- );
376
- });
360
+ Stack.Stack.use((stack) =>
361
+ effect.pipe(
362
+ // Effect.tap(Effect.logInfo),
363
+ // @ts-expect-error
364
+ Stack.make(stack.name, Layer.effectContext(Effect.context<never>()), {
365
+ ...stack,
366
+ resources: {},
367
+ bindings: {},
368
+ output: {},
369
+ }),
370
+ Effect.flatMap(Plan.make),
371
+ Effect.tap((plan) => Effect.logInfo(formatPlan(plan))),
372
+ Effect.flatMap(apply),
373
+ provideFreshArtifactStore,
374
+ ),
375
+ );
377
376
  }
378
377
 
379
378
  type AnyAction = Plan.CRUD["action"] | Plan.BindingAction | DerivedAction;
@@ -528,3 +527,13 @@ export function afterAll<A, E, R extends Provided = never>(
528
527
  options?.timeout,
529
528
  );
530
529
  }
530
+ export const destroy = () =>
531
+ Stack.Stack.use((stack) =>
532
+ Plan.make({
533
+ name: stack.name,
534
+ stage: stack.stage,
535
+ resources: {},
536
+ bindings: {},
537
+ output: {},
538
+ }).pipe(Effect.flatMap(apply), provideFreshArtifactStore),
539
+ );
package/src/Test/index.ts CHANGED
@@ -1,3 +1,2 @@
1
- export * from "./TestCli.ts";
2
1
  export * from "./TestState.ts";
3
2
  export * from "./Vitest.ts";
@@ -2,12 +2,12 @@ import * as Effect from "effect/Effect";
2
2
  import * as FileSystem from "effect/FileSystem";
3
3
  import * as Logger from "effect/Logger";
4
4
  import { Path } from "effect/Path";
5
- import { DotAlchemy } from "../Config.ts";
5
+ import { AlchemyContext } from "../AlchemyContext.ts";
6
6
 
7
7
  export const fileLogger = Effect.fnUntraced(function* (
8
8
  ...segments: ReadonlyArray<string>
9
9
  ) {
10
- const dotAlchemy = yield* DotAlchemy;
10
+ const { dotAlchemy } = yield* AlchemyContext;
11
11
  const fs = yield* FileSystem.FileSystem;
12
12
  const path = yield* Path;
13
13
  const logFile = path.join(dotAlchemy, "log", ...segments);
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * as AdoptPolicy from "./AdoptPolicy.ts";
2
+ export * from "./AlchemyContext.ts";
2
3
  export * from "./Apply.ts";
3
4
  export {
4
5
  Service as BindingService,
@@ -8,7 +9,6 @@ export {
8
9
  type PolicyLike,
9
10
  type PolicyShape,
10
11
  } from "./Binding.ts";
11
- export * from "./Config.ts";
12
12
  export * from "./Destroy.ts";
13
13
  export * from "./Diff.ts";
14
14
  export * from "./Input.ts";
@@ -27,6 +27,7 @@ export * as Server from "./Server/index.ts";
27
27
  export * as Serverless from "./Serverless/index.ts";
28
28
  export { Stack } from "./Stack.ts";
29
29
  export * from "./Stage.ts";
30
+ export { inMemoryState, localState } from "./State/index.ts";
30
31
  export * from "./Trait.ts";
31
32
 
32
33
  export * as Construct from "./Construct.ts";
package/lib/Config.d.ts DELETED
@@ -1,20 +0,0 @@
1
- import * as Context from "effect/Context";
2
- import * as FileSystem from "effect/FileSystem";
3
- import * as Layer from "effect/Layer";
4
- import * as Path from "effect/Path";
5
- export type cwd = typeof cwd;
6
- /**
7
- * Placeholder for referencing the current working directory in module-scoped code.
8
- *
9
- * Will be replaced lazily with the actual current working directory (as provided by Effect context).
10
- */
11
- export declare const cwd: {
12
- readonly type: "cwd";
13
- };
14
- export declare const isCwd: (x: any) => x is cwd;
15
- declare const DotAlchemy_base: Context.ServiceClass<DotAlchemy, ".alchemy", string>;
16
- export declare class DotAlchemy extends DotAlchemy_base {
17
- }
18
- export declare const dotAlchemy: Layer.Layer<DotAlchemy, import("effect/PlatformError").PlatformError, FileSystem.FileSystem | Path.Path>;
19
- export {};
20
- //# sourceMappingURL=Config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../src/Config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAE1C,OAAO,KAAK,UAAU,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AAEpC,MAAM,MAAM,GAAG,GAAG,OAAO,GAAG,CAAC;AAE7B;;;;GAIG;AACH,eAAO,MAAM,GAAG;;CAA2B,CAAC;AAE5C,eAAO,MAAM,KAAK,GAAI,GAAG,GAAG,KAAG,CAAC,IAAI,GAAwB,CAAC;;AAE7D,qBAAa,UAAW,SAAQ,eAE/B;CAAG;AAEJ,eAAO,MAAM,UAAU,0GAStB,CAAC"}
package/lib/Config.js DELETED
@@ -1,22 +0,0 @@
1
- import * as Context 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 from "effect/Path";
6
- /**
7
- * Placeholder for referencing the current working directory in module-scoped code.
8
- *
9
- * Will be replaced lazily with the actual current working directory (as provided by Effect context).
10
- */
11
- export const cwd = { type: "cwd" };
12
- export const isCwd = (x) => x?.type === "cwd";
13
- export class DotAlchemy extends Context.Service()(".alchemy") {
14
- }
15
- export const dotAlchemy = Layer.effect(DotAlchemy, Effect.gen(function* () {
16
- const fs = yield* FileSystem.FileSystem;
17
- const path = yield* Path.Path;
18
- const dir = path.join(process.cwd(), ".alchemy");
19
- yield* fs.makeDirectory(dir, { recursive: true });
20
- return dir;
21
- }));
22
- //# sourceMappingURL=Config.js.map
package/lib/Config.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"Config.js","sourceRoot":"","sources":["../src/Config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AAIpC;;;;GAIG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,IAAI,EAAE,KAAK,EAAW,CAAC;AAE5C,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,CAAM,EAAY,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,KAAK,CAAC;AAE7D,MAAM,OAAO,UAAW,SAAQ,OAAO,CAAC,OAAO,EAAsB,CACnE,UAAU,CACX;CAAG;AAEJ,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CACpC,UAAU,EACV,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;IACxC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;IACjD,KAAK,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC,CACH,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=Observability.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Observability.d.ts","sourceRoot":"","sources":["../src/Observability.ts"],"names":[],"mappings":""}
@@ -1 +0,0 @@
1
- //# sourceMappingURL=Observability.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Observability.js","sourceRoot":"","sources":["../src/Observability.ts"],"names":[],"mappings":""}
@@ -1,4 +0,0 @@
1
- import * as Layer from "effect/Layer";
2
- import { Cli } from "../Cli/Cli.ts";
3
- export declare const TestCli: Layer.Layer<Cli, never, never>;
4
- //# sourceMappingURL=TestCli.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TestCli.d.ts","sourceRoot":"","sources":["../../src/Test/TestCli.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEpC,eAAO,MAAM,OAAO,gCAgBnB,CAAC"}