alchemy 2.0.0-cli-shim.1 → 2.0.0-cli-bundle-fix.1

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 (58) hide show
  1. package/bin/alchemy.js +968 -814
  2. package/bin/alchemy.js.map +1 -1
  3. package/bin/alchemy.ts +4 -0
  4. package/bin/commands/deploy.ts +0 -1
  5. package/bin/commands/state.ts +230 -0
  6. package/lib/AWS/Lambda/BucketEventSource.d.ts +1 -1
  7. package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
  8. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts +1 -1
  9. package/lib/AWS/Lambda/EventBridgeEventSource.d.ts.map +1 -1
  10. package/lib/AWS/Lambda/QueueEventSource.d.ts +1 -1
  11. package/lib/AWS/Lambda/QueueEventSource.d.ts.map +1 -1
  12. package/lib/AWS/Lambda/StreamEventSource.d.ts +1 -1
  13. package/lib/AWS/Lambda/StreamEventSource.d.ts.map +1 -1
  14. package/lib/AWS/Lambda/TableEventSource.d.ts +1 -1
  15. package/lib/AWS/Lambda/TableEventSource.d.ts.map +1 -1
  16. package/lib/AWS/Lambda/TopicEventSource.d.ts +1 -1
  17. package/lib/AWS/Lambda/TopicEventSource.d.ts.map +1 -1
  18. package/lib/Agent/lsp/client.d.ts +2 -2
  19. package/lib/Cli/InkCLI.d.ts +0 -1
  20. package/lib/Cli/InkCLI.d.ts.map +1 -1
  21. package/lib/Cli/InkCLI.js +0 -5
  22. package/lib/Cli/InkCLI.js.map +1 -1
  23. package/lib/Cli/index.d.ts +0 -1
  24. package/lib/Cli/index.d.ts.map +1 -1
  25. package/lib/Cli/index.js +4 -1
  26. package/lib/Cli/index.js.map +1 -1
  27. package/lib/Cloudflare/StateStore/State.d.ts +1 -1
  28. package/lib/Cloudflare/StateStore/State.d.ts.map +1 -1
  29. package/lib/Deploy.d.ts +1 -1
  30. package/lib/Deploy.d.ts.map +1 -1
  31. package/lib/Destroy.d.ts +1 -1
  32. package/lib/Destroy.d.ts.map +1 -1
  33. package/lib/Stack.d.ts +5 -4
  34. package/lib/Stack.d.ts.map +1 -1
  35. package/lib/Stack.js +27 -8
  36. package/lib/Stack.js.map +1 -1
  37. package/lib/State/HttpStateStore.js +32 -6
  38. package/lib/State/HttpStateStore.js.map +1 -1
  39. package/lib/State/Sync.d.ts +1 -0
  40. package/lib/State/Sync.d.ts.map +1 -1
  41. package/lib/State/Sync.js +1 -1
  42. package/lib/State/Sync.js.map +1 -1
  43. package/lib/Test/Bun.d.ts +2 -2
  44. package/lib/Test/Bun.d.ts.map +1 -1
  45. package/lib/Test/Bun.js +2 -4
  46. package/lib/Test/Bun.js.map +1 -1
  47. package/lib/Test/Vitest.d.ts +1 -1
  48. package/lib/cli/index.d.ts +2 -6
  49. package/lib/cli/index.d.ts.map +1 -1
  50. package/lib/cli/index.js +2 -43050
  51. package/lib/cli/index.js.map +1 -1
  52. package/package.json +2 -2
  53. package/src/Cli/InkCLI.tsx +0 -10
  54. package/src/Cli/index.ts +4 -1
  55. package/src/Stack.ts +33 -7
  56. package/src/State/HttpStateStore.ts +30 -7
  57. package/src/State/Sync.ts +4 -1
  58. package/src/Test/Bun.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alchemy",
3
- "version": "2.0.0-cli-shim.1",
3
+ "version": "2.0.0-cli-bundle-fix.1",
4
4
  "homepage": "https://alchemy.run",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Sam Goodwin <sam@alchemy.run>",
@@ -255,7 +255,6 @@
255
255
  "jszip": "^3.10.1",
256
256
  "libsodium-wrappers": "^0.8.3",
257
257
  "rolldown": "1.0.0-rc.13",
258
- "solid-js": "latest",
259
258
  "sonda": "^0.11.1",
260
259
  "web-tree-sitter": "0.25.10",
261
260
  "workerd": "1.20260417.1",
@@ -277,6 +276,7 @@
277
276
  "pathe": "^2.0.3",
278
277
  "react": "^19.2.0",
279
278
  "react-devtools-core": "^7.0.1",
279
+ "solid-js": "latest",
280
280
  "tsconfig-paths": "^4.2.0",
281
281
  "tsdown": "^0.15.4",
282
282
  "tsx": "^4.20.6",
@@ -19,16 +19,6 @@ export const inkCLI = () =>
19
19
  }),
20
20
  );
21
21
 
22
- export const inkCLIAutoApprove = () =>
23
- Layer.succeed(
24
- Cli,
25
- Cli.of({
26
- approvePlan: () => Effect.succeed(true),
27
- displayPlan,
28
- startApplySession,
29
- }),
30
- );
31
-
32
22
  const approvePlan = Effect.fn(function* <P extends Plan>(plan: P) {
33
23
  let approved = false;
34
24
  const { waitUntilExit } = render(
package/src/Cli/index.ts CHANGED
@@ -1,2 +1,5 @@
1
1
  export * from "./Cli.ts";
2
- export * from "./InkCLI.tsx";
2
+ // Note: do NOT re-export InkCLI here. The Ink-based renderer pulls in
3
+ // `react`/`ink`, which we do not want consumers of `alchemy/Cli` to
4
+ // transitively depend on. Import `alchemy/Cli/InkCLI` directly when needed
5
+ // (currently only from `bin/alchemy.ts`, which is bundled).
package/src/Stack.ts CHANGED
@@ -13,7 +13,7 @@ import type { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSp
13
13
  import { AlchemyContext, AlchemyContextLive } from "./AlchemyContext.ts";
14
14
  import { provideFreshArtifactStore } from "./Artifacts.ts";
15
15
  import { AuthProviders } from "./Auth/AuthProvider.ts";
16
- import { inkCLIAutoApprove } from "./Cli/InkCLI.tsx";
16
+ import { Cli } from "./Cli/Cli.ts";
17
17
  import type { Input, InputProps } from "./Input.ts";
18
18
  import * as Output from "./Output.ts";
19
19
  import { ref } from "./Ref.ts";
@@ -34,7 +34,8 @@ export type StackServices =
34
34
  | AlchemyContext
35
35
  | HttpClient
36
36
  | ChildProcessSpawner
37
- | AuthProviders;
37
+ | AuthProviders
38
+ | Cli;
38
39
 
39
40
  export type StackEffect<A, Err = never, Req = never> = Effect.Effect<
40
41
  A,
@@ -44,6 +45,7 @@ export type StackEffect<A, Err = never, Req = never> = Effect.Effect<
44
45
  | Scope
45
46
  | AuthProviders
46
47
  | AlchemyContext
48
+ | Cli
47
49
  | State
48
50
  | Req
49
51
  >;
@@ -172,8 +174,33 @@ export const make =
172
174
  effect: Effect.Effect<A, Err, Req>,
173
175
  ) =>
174
176
  Effect.scope.pipe(
175
- Effect.flatMap((scope) =>
176
- options.providers.pipe(
177
+ Effect.flatMap((scope) => {
178
+ if (options.state == null) {
179
+ return Effect.die(
180
+ new Error(
181
+ `Stack "${options.name}" is missing a state store. ` +
182
+ `Add a \`state\` layer to the stack options, e.g.:\n` +
183
+ ` Alchemy.Stack("${options.name}", {\n` +
184
+ ` providers: Cloudflare.providers(),\n` +
185
+ ` state: Cloudflare.state(), // <-- required\n` +
186
+ ` }, ...)\n` +
187
+ `See https://alchemy.run/concepts/state-store for available state stores.`,
188
+ ),
189
+ );
190
+ }
191
+ if (options.providers == null) {
192
+ return Effect.die(
193
+ new Error(
194
+ `Stack "${options.name}" is missing a providers layer. ` +
195
+ `Add a \`providers\` layer to the stack options, e.g.:\n` +
196
+ ` Alchemy.Stack("${options.name}", {\n` +
197
+ ` providers: Cloudflare.providers(), // <-- required\n` +
198
+ ` state: Cloudflare.state(),\n` +
199
+ ` }, ...)`,
200
+ ),
201
+ );
202
+ }
203
+ return options.providers.pipe(
177
204
  Layer.provideMerge(options.state),
178
205
  Layer.provideMerge(
179
206
  Layer.effect(
@@ -192,8 +219,8 @@ export const make =
192
219
  ),
193
220
  ),
194
221
  Layer.buildWithScope(scope),
195
- ),
196
- ),
222
+ );
223
+ }),
197
224
  Effect.flatMap((context) =>
198
225
  Effect.all([
199
226
  effect,
@@ -257,7 +284,6 @@ export const evalStack = <A, B, Err, Req>(
257
284
  ),
258
285
  ),
259
286
  Effect.provide(Layer.succeed(Stage, options.stage)),
260
- Effect.provide(inkCLIAutoApprove()),
261
287
  Effect.provide(Layer.provideMerge(alchemy, platform)),
262
288
  Effect.scoped,
263
289
  );
@@ -104,16 +104,39 @@ export const makeHttpStateStore = ({ url, authToken }: HttpStateStoreProps) =>
104
104
  return service;
105
105
  });
106
106
 
107
+ /**
108
+ * Predicate over an `HttpClientError`-shaped failure that returns `true`
109
+ * for failures we expect to clear up on their own.
110
+ *
111
+ * We retry:
112
+ * - all transport-level errors (no `response` — DNS, TCP reset, TLS,
113
+ * abort, etc.)
114
+ * - 408 (request timeout) and 429 (rate limit)
115
+ * - 404, which is normal in the seconds after a worker is first
116
+ * deployed and the route hasn't propagated yet
117
+ * - every 5xx
118
+ *
119
+ * Anything else (400/401/403/etc.) is a real client-side problem and
120
+ * shouldn't be hidden behind retries.
121
+ */
122
+ const isTransient = (e: any): boolean => {
123
+ if (e?._tag !== "HttpClientError") return false;
124
+ const status: number | undefined = e.response?.status;
125
+ if (status == null) return true;
126
+ if (status === 404 || status === 408 || status === 429) return true;
127
+ return status >= 500 && status < 600;
128
+ };
129
+
107
130
  const retryTransient = <A, Err, Req>(eff: Effect.Effect<A, Err, Req>) =>
108
131
  Effect.retry(eff, {
109
- while: (e: any) =>
110
- e._tag === "HttpClientError" &&
111
- (e.response?.status === 500 ||
112
- e.response?.status === 502 ||
113
- // not founds are usually after the worker has just been created
114
- e.response?.status === 404),
132
+ while: isTransient,
133
+ // Exponential backoff capped at 2s, no attempt limit. The state
134
+ // store is on the deploy critical path; we'd rather block than
135
+ // surface a transient blip as a hard failure (which historically
136
+ // stranded `syncState` mid-flight, leaving the remote store
137
+ // partially populated).
115
138
  schedule: Schedule.exponential(100).pipe(
116
- Schedule.both(Schedule.recurs(10)),
139
+ Schedule.either(Schedule.spaced("2 seconds")),
117
140
  ),
118
141
  });
119
142
 
package/src/State/Sync.ts CHANGED
@@ -17,6 +17,7 @@ export const syncState = Effect.fnUntraced(function* (
17
17
  source: StateService,
18
18
  destination: StateService,
19
19
  options?: {
20
+ stacks?: string[];
20
21
  /**
21
22
  * Maximum number of resources to copy in parallel within a single stage.
22
23
  * @default "unbounded".
@@ -29,7 +30,9 @@ export const syncState = Effect.fnUntraced(function* (
29
30
  source.listStacks(),
30
31
  destination.listStacks(),
31
32
  ]);
32
- const sourceStackSet = new Set(sourceStacks);
33
+ const sourceStackSet = new Set(
34
+ sourceStacks.filter((stack) => options?.stacks?.includes(stack) ?? true),
35
+ );
33
36
 
34
37
  yield* Effect.forEach(
35
38
  sourceStacks,
package/src/Test/Bun.ts CHANGED
@@ -9,6 +9,7 @@ import type { HookOptions } from "node:test";
9
9
  import { AlchemyContext, AlchemyContextLive } from "../AlchemyContext.ts";
10
10
  import { provideFreshArtifactStore } from "../Artifacts.ts";
11
11
  import { AuthProviders } from "../Auth/AuthProvider.ts";
12
+ import { inkCLI } from "../Cli/InkCLI.tsx";
12
13
  import { deploy as _deploy } from "../Deploy.ts";
13
14
  import { destroy as _destroy } from "../Destroy.ts";
14
15
  import {
@@ -28,8 +29,7 @@ const platform = Layer.mergeAll(PlatformServices, FetchHttpClient.layer);
28
29
 
29
30
  // override alchemy state store, CLI/reporting, state, and dotAlchemy
30
31
  const alchemy = Layer.mergeAll(
31
- // CLI.inkCLI(),
32
- // optional
32
+ inkCLI(),
33
33
  Logger.layer([Logger.consolePretty()]),
34
34
  AlchemyContextLive,
35
35
  );