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
@@ -1010,7 +1010,6 @@ export const LiveWorkerProvider = () =>
1010
1010
  const getScriptSubdomain = yield* workers.getScriptSubdomain;
1011
1011
  const getScriptSettings = yield* workers.getScriptScriptAndVersionSetting;
1012
1012
  const getSubdomain = yield* workers.getSubdomain;
1013
- const listScripts = yield* workers.listScripts;
1014
1013
  const putScript = yield* workers.putScript;
1015
1014
  const putDomain = yield* workers.putDomain;
1016
1015
  const listDomains = yield* workers.listDomains;
@@ -1139,9 +1138,7 @@ export const LiveWorkerProvider = () =>
1139
1138
 
1140
1139
  // Detach what's no longer wanted. Use the live list so we
1141
1140
  // don't try to delete domains we no longer track.
1142
- const toRemove = liveAll.filter(
1143
- (d) => !desiredSet.has(d.hostname),
1144
- );
1141
+ const toRemove = liveAll.filter((d) => !desiredSet.has(d.hostname));
1145
1142
  yield* Effect.all(
1146
1143
  toRemove.map((d) =>
1147
1144
  deleteDomain({ accountId, domainId: d.id }).pipe(
@@ -1208,10 +1205,9 @@ export const LiveWorkerProvider = () =>
1208
1205
  };
1209
1206
  });
1210
1207
 
1211
- const applied = yield* Effect.all(
1212
- desired.map(attachDomain),
1213
- { concurrency: "unbounded" },
1214
- );
1208
+ const applied = yield* Effect.all(desired.map(attachDomain), {
1209
+ concurrency: "unbounded",
1210
+ });
1215
1211
  return applied;
1216
1212
  });
1217
1213
 
@@ -1650,12 +1646,7 @@ ${[
1650
1646
  yield* Effect.logInfo(
1651
1647
  `Cloudflare Worker ${olds ? "update" : "create"}: uploading assets for ${name}`,
1652
1648
  );
1653
- const { jwt } = yield* uploadAssets(
1654
- accountId,
1655
- name,
1656
- assets,
1657
- session,
1658
- );
1649
+ const { jwt } = yield* uploadAssets(accountId, name, assets, session);
1659
1650
  metadataAssets = {
1660
1651
  jwt,
1661
1652
  config: assets.config,
@@ -2140,42 +2131,36 @@ ${[
2140
2131
  yield* Effect.logInfo(
2141
2132
  `Cloudflare Worker read: checking ${workerName}`,
2142
2133
  );
2143
- const [worker, subdomain, settings, domainsList] =
2144
- yield* Effect.all([
2145
- listScripts({
2146
- accountId,
2147
- }).pipe(
2148
- Effect.map((workers) =>
2149
- workers.result.find((worker) => worker.id === workerName),
2150
- ),
2151
- ),
2152
- getScriptSubdomain({
2153
- accountId,
2154
- scriptName: workerName,
2155
- }),
2156
- getScriptSettings({
2157
- accountId,
2158
- scriptName: workerName,
2159
- }),
2160
- listDomains({
2161
- accountId,
2162
- service: workerName,
2163
- }).pipe(Effect.map((r) => r.result ?? [])),
2164
- ]);
2165
- if (!worker) {
2166
- yield* Effect.logInfo(
2167
- `Cloudflare Worker read: ${workerName} not found in script list`,
2168
- );
2169
- return undefined;
2170
- }
2134
+ // We deliberately don't call `listScripts({ accountId })` here:
2135
+ // it pulls every Worker on the account back through a strict
2136
+ // schema decode, and a single existing Worker the schema doesn't
2137
+ // know about (e.g. `placement_mode: "targeted"`) breaks the
2138
+ // entire read. `getScriptSettings` already fails with
2139
+ // `WorkerNotFound` if the script doesn't exist, which the
2140
+ // surrounding `Effect.catchTag` turns into `undefined` — that's
2141
+ // all the existence check we need.
2142
+ const [subdomain, settings, domainsList] = yield* Effect.all([
2143
+ getScriptSubdomain({
2144
+ accountId,
2145
+ scriptName: workerName,
2146
+ }),
2147
+ getScriptSettings({
2148
+ accountId,
2149
+ scriptName: workerName,
2150
+ }),
2151
+ listDomains({
2152
+ accountId,
2153
+ service: workerName,
2154
+ }).pipe(Effect.map((r) => r.result ?? [])),
2155
+ ]);
2171
2156
  yield* Effect.logInfo(
2172
2157
  `Cloudflare Worker read: found ${workerName}`,
2173
2158
  );
2174
2159
  return {
2175
2160
  accountId,
2176
- workerId: worker.id ?? workerName,
2161
+ workerId: workerName,
2177
2162
  workerName,
2178
- logpush: worker.logpush ?? undefined,
2163
+ logpush: settings.logpush ?? undefined,
2179
2164
  url: subdomain.enabled
2180
2165
  ? `https://${workerName}.${yield* getAccountSubdomain(accountId)}.workers.dev`
2181
2166
  : undefined,
package/src/Output.ts CHANGED
@@ -499,7 +499,9 @@ export const upstreamAny = (
499
499
  ): {
500
500
  [ID in string]: Resource;
501
501
  } => {
502
- if (isExpr(value)) {
502
+ if (isResource(value)) {
503
+ return { [value.FQN]: value as Resource };
504
+ } else if (isExpr(value)) {
503
505
  return upstream(value);
504
506
  } else if (Array.isArray(value)) {
505
507
  return Object.assign({}, ...value.map(resolveUpstream));
@@ -517,7 +519,11 @@ export const upstreamAny = (
517
519
 
518
520
  // TODO(sam): add a type
519
521
  export const upstream = <E extends Output<any, any>>(expr: E): any => {
520
- if (isResourceExpr(expr)) {
522
+ if (isResource(expr)) {
523
+ return {
524
+ [(expr as unknown as Resource).FQN]: expr,
525
+ };
526
+ } else if (isResourceExpr(expr)) {
521
527
  return {
522
528
  [expr.src.FQN]: expr.src,
523
529
  };
@@ -541,6 +547,8 @@ export const upstream = <E extends Output<any, any>>(expr: E): any => {
541
547
  export const resolveUpstream = <const A>(value: A): any => {
542
548
  if (isPrimitive(value)) {
543
549
  return {} as any;
550
+ } else if (isResource(value)) {
551
+ return { [(value as unknown as Resource).FQN]: value } as any;
544
552
  } else if (isOutput(value)) {
545
553
  return upstream(value) as any;
546
554
  } else if (Array.isArray(value)) {
package/src/Provider.ts CHANGED
@@ -5,7 +5,7 @@ import * as Option from "effect/Option";
5
5
  import type * as Stream from "effect/Stream";
6
6
  import type { Artifacts } from "./Artifacts.ts";
7
7
  import type { Policy } from "./Binding.ts";
8
- import type { ScopedPlanStatusSession } from "./Cli/index.ts";
8
+ import type { ScopedPlanStatusSession } from "./Cli/Cli.ts";
9
9
  import type { Diff } from "./Diff.ts";
10
10
  import type { Input } from "./Input.ts";
11
11
  import type { InstanceId } from "./InstanceId.ts";
package/src/Stack.ts CHANGED
@@ -184,7 +184,7 @@ export const make =
184
184
  ` providers: Cloudflare.providers(),\n` +
185
185
  ` state: Cloudflare.state(), // <-- required\n` +
186
186
  ` }, ...)\n` +
187
- `See https://alchemy.run/concepts/state-store for available state stores.`,
187
+ `See https://v2.alchemy.run/concepts/state-store for available state stores.`,
188
188
  ),
189
189
  );
190
190
  }
@@ -249,7 +249,7 @@ export const CurrentStack = Effect.serviceOption(Stack)
249
249
  const platform = Layer.mergeAll(
250
250
  PlatformServices,
251
251
  FetchHttpClient.layer,
252
- Logger.layer([fileLogger("out")]),
252
+ Logger.layer([fileLogger("out")], { mergeWithExisting: true }),
253
253
  );
254
254
  // override alchemy state store, CLI/reporting, state, and Config
255
255
  const alchemy = Layer.mergeAll(
@@ -1,6 +1,7 @@
1
1
  import * as Effect from "effect/Effect";
2
2
  import * as Layer from "effect/Layer";
3
3
  import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
4
+ import * as OtlpLogger from "effect/unstable/observability/OtlpLogger";
4
5
  import * as OtlpMetrics from "effect/unstable/observability/OtlpMetrics";
5
6
  import * as OtlpSerialization from "effect/unstable/observability/OtlpSerialization";
6
7
  import * as OtlpTracer from "effect/unstable/observability/OtlpTracer";
@@ -11,6 +12,7 @@ import { collectAttributes, isTelemetryDisabled } from "./Attributes.ts";
11
12
 
12
13
  const TRACES_URL = "https://otel.alchemy.run/v1/traces";
13
14
  const METRICS_URL = "https://otel.alchemy.run/v1/metrics";
15
+ const LOGS_URL = "https://otel.alchemy.run/v1/logs";
14
16
 
15
17
  const SERVICE_NAME = "alchemy-cli";
16
18
 
@@ -35,8 +37,18 @@ const buildOtlpLayer = (
35
37
  resource,
36
38
  exportInterval: "1 second",
37
39
  });
40
+ // Replace (don't merge with) the default stdout logger here; downstream
41
+ // commands re-add their own `fileLogger`/`consolePretty` via
42
+ // `Logger.layer([...], { mergeWithExisting: true })`, which stacks on top
43
+ // of this OtlpLogger without resurrecting Effect's default stdout logger.
44
+ const logger = OtlpLogger.layer({
45
+ url: LOGS_URL,
46
+ resource,
47
+ exportInterval: "1 second",
48
+ mergeWithExisting: false,
49
+ });
38
50
 
39
- return Layer.mergeAll(tracer, metrics).pipe(
51
+ return Layer.mergeAll(tracer, metrics, logger).pipe(
40
52
  Layer.provide(OtlpSerialization.layerJson),
41
53
  Layer.provide(FetchHttpClient.layer),
42
54
  );
package/src/Test/Bun.ts CHANGED
@@ -1,163 +1,187 @@
1
1
  import bun from "bun:test";
2
- import { ConfigProvider } from "effect/ConfigProvider";
3
2
  import * as Effect from "effect/Effect";
4
- import * as Layer from "effect/Layer";
5
- import * as Logger from "effect/Logger";
6
- import * as Option from "effect/Option";
7
- import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
8
3
  import type { HookOptions } from "node:test";
9
- import { AlchemyContext, AlchemyContextLive } from "../AlchemyContext.ts";
10
- import { provideFreshArtifactStore } from "../Artifacts.ts";
11
- import { AuthProviders } from "../Auth/AuthProvider.ts";
12
- import { inkCLI } from "../Cli/InkCLI.tsx";
13
- import { deploy as _deploy } from "../Deploy.ts";
14
- import { destroy as _destroy } from "../Destroy.ts";
15
- import {
16
- type CompiledStack,
17
- type StackEffect,
18
- type StackServices,
19
- } from "../Stack.ts";
20
- import * as State from "../State/index.ts";
21
- import { loadConfigProvider } from "../Util/ConfigProvider.ts";
22
- import { PlatformServices } from "../Util/PlatformServices.ts";
23
-
24
- export type ProvidedServices = StackServices;
25
-
26
- type TestEffect<A, Req = never> = StackEffect<A, any, Req>;
27
-
28
- const platform = Layer.mergeAll(PlatformServices, FetchHttpClient.layer);
29
-
30
- // override alchemy state store, CLI/reporting, state, and dotAlchemy
31
- const alchemy = Layer.mergeAll(
32
- inkCLI(),
33
- Logger.layer([Logger.consolePretty()]),
34
- AlchemyContextLive,
35
- );
36
-
37
- const run = <A>(effect: TestEffect<A>) =>
38
- Effect.gen(function* () {
39
- const configProvider = yield* loadConfigProvider(Option.none());
40
-
41
- return yield* effect.pipe(
42
- provideFreshArtifactStore,
43
- Effect.provide(Layer.succeed(ConfigProvider, configProvider)),
44
- );
45
- }).pipe(
46
- Effect.provideService(AuthProviders, {}),
47
- Effect.provide(State.localState()),
48
- Effect.provide(Layer.provideMerge(alchemy, platform)),
49
- Effect.scoped,
50
- Effect.runPromise,
51
- );
52
-
53
- export const it = test;
54
-
55
- export const expect = bun.expect;
56
-
57
- export function test(
58
- name: string,
59
- test: TestEffect<void>,
60
- options?: bun.TestOptions,
61
- ) {
62
- bun.test(name, () => run(test), options);
4
+
5
+ import type { AlchemyContext } from "../AlchemyContext.ts";
6
+ import type { CompiledStack } from "../Stack.ts";
7
+ import type { Stage } from "../Stage.ts";
8
+ import * as Core from "./Core.ts";
9
+
10
+ export type MakeOptions<ROut = any> = Core.MakeOptions<ROut>;
11
+ export type ScratchStack = Core.ScratchStack;
12
+ export type TestEffect<A, R = never> = Core.TestEffect<A, R>;
13
+
14
+ export interface TestApi {
15
+ test: TestFn;
16
+ beforeAll: BeforeAllFn;
17
+ beforeEach: BeforeEachFn;
18
+ afterAll: AfterAllFn;
19
+ afterEach: AfterEachFn;
20
+ deploy: <A>(
21
+ stack: TestEffect<CompiledStack<A>, Stage | AlchemyContext>,
22
+ options?: { stage?: string },
23
+ ) => ReturnType<typeof Core.deploy<A>>;
24
+ destroy: (
25
+ stack: TestEffect<CompiledStack, Stage | AlchemyContext>,
26
+ options?: { stage?: string },
27
+ ) => ReturnType<typeof Core.destroy>;
63
28
  }
64
29
 
65
- export namespace test {
66
- export function skipIf(condition: boolean) {
67
- return (
68
- name: string,
69
- test: TestEffect<void>,
70
- options?: bun.TestOptions,
71
- ) => {
72
- bun.test.skipIf(condition)(name, () => run(test), options);
73
- };
74
- }
75
-
76
- export function skip(
30
+ interface TestFn {
31
+ (name: string, eff: TestEffect<void>, options?: bun.TestOptions): void;
32
+ skip: (
77
33
  name: string,
78
- test: TestEffect<void>,
34
+ eff: TestEffect<void>,
79
35
  options?: bun.TestOptions,
80
- ) {
81
- bun.test.skip(name, () => run(test), options);
82
- }
83
-
84
- export function only(
36
+ ) => void;
37
+ skipIf: (
38
+ condition: boolean,
39
+ ) => (name: string, eff: TestEffect<void>, options?: bun.TestOptions) => void;
40
+ only: (
85
41
  name: string,
86
- test: TestEffect<void>,
42
+ eff: TestEffect<void>,
87
43
  options?: bun.TestOptions,
88
- ) {
89
- bun.test.only(name, () => run(test), options);
90
- }
91
-
92
- export function todo(
44
+ ) => void;
45
+ todo: (
93
46
  name: string,
94
- test: TestEffect<void>,
47
+ eff: TestEffect<void>,
95
48
  options?: bun.TestOptions,
96
- ) {
97
- bun.test.todo(name, () => run(test), options);
98
- }
49
+ ) => void;
50
+ provider: ProviderFn;
99
51
  }
100
52
 
101
- export const describe = bun.describe;
102
-
103
- export function beforeAll<A>(eff: TestEffect<A>, options?: HookOptions) {
104
- let a: A;
105
- bun.beforeAll(
106
- () => run(eff).then((v) => (a = v)),
107
- options ?? {
108
- timeout: 120_000,
109
- },
110
- );
111
- return Effect.sync(() => a);
53
+ interface ProviderFn {
54
+ (
55
+ name: string,
56
+ fn: (stack: ScratchStack) => Effect.Effect<void, any, any>,
57
+ options?: bun.TestOptions,
58
+ ): void;
59
+ skip: (
60
+ name: string,
61
+ fn: (stack: ScratchStack) => Effect.Effect<void, any, any>,
62
+ options?: bun.TestOptions,
63
+ ) => void;
64
+ skipIf: (
65
+ condition: boolean,
66
+ ) => (
67
+ name: string,
68
+ fn: (stack: ScratchStack) => Effect.Effect<void, any, any>,
69
+ options?: bun.TestOptions,
70
+ ) => void;
112
71
  }
113
72
 
114
- export function beforeEach(eff: TestEffect<void>, options?: HookOptions) {
115
- bun.beforeEach(() => run(eff), options);
73
+ interface BeforeAllFn {
74
+ <A>(eff: TestEffect<A>, options?: HookOptions): Effect.Effect<A>;
116
75
  }
117
76
 
118
- export function afterAll(eff: TestEffect<any>, options?: HookOptions) {
119
- bun.afterAll(() => run(eff), options);
77
+ interface BeforeEachFn {
78
+ (eff: TestEffect<void>, options?: HookOptions): void;
120
79
  }
121
80
 
122
- export namespace afterAll {
123
- export const skipIf =
124
- (predicate: boolean) => (test: TestEffect<void>, options?: HookOptions) => {
125
- if (predicate) {
126
- } else {
127
- bun.afterAll(
128
- () => run(test),
129
- options ?? {
130
- timeout: 120_000,
131
- },
132
- );
133
- }
134
- };
81
+ interface AfterAllFn {
82
+ (eff: TestEffect<any>, options?: HookOptions): void;
83
+ skipIf: (
84
+ predicate: boolean,
85
+ ) => (eff: TestEffect<any>, options?: HookOptions) => void;
135
86
  }
136
87
 
137
- export function afterEach(eff: TestEffect<void>, options?: HookOptions) {
138
- bun.afterEach(() => run(eff), options);
88
+ interface AfterEachFn {
89
+ (eff: TestEffect<void>, options?: HookOptions): void;
139
90
  }
140
91
 
141
- export const deploy = <A>(
142
- stack: TestEffect<CompiledStack<A>, AlchemyContext>,
143
- options?: {
144
- /** @default test */
145
- stage?: string;
146
- },
147
- ) =>
148
- _deploy({
149
- stack: stack,
150
- stage: options?.stage ?? "test",
151
- });
152
-
153
- export const destroy = (
154
- stack: TestEffect<CompiledStack, AlchemyContext>,
155
- options?: {
156
- /** @default test */
157
- stage?: string;
158
- },
159
- ) =>
160
- _destroy({
161
- stack,
162
- stage: options?.stage ?? "test",
163
- });
92
+ const DEFAULT_HOOK_TIMEOUT: HookOptions = { timeout: 120_000 };
93
+
94
+ /**
95
+ * Build the per-file test API. Configure providers / state once at the top of
96
+ * the test file:
97
+ *
98
+ * ```ts
99
+ * import * as Test from "alchemy/Test/Bun";
100
+ * import * as Cloudflare from "alchemy/Cloudflare";
101
+ *
102
+ * const { test, deploy, destroy, beforeAll, afterAll } = Test.make({
103
+ * providers: Cloudflare.providers(),
104
+ * state: Cloudflare.state(),
105
+ * });
106
+ * ```
107
+ */
108
+ export const make = <ROut = any>(options: MakeOptions<ROut>): TestApi => {
109
+ const runEff = <A>(eff: TestEffect<A>) => Core.run(eff, options);
110
+
111
+ const test = ((name, eff, opts) => {
112
+ bun.test(name, () => runEff(eff), opts);
113
+ }) as TestFn;
114
+
115
+ test.skip = (name, eff, opts) => {
116
+ bun.test.skip(name, () => runEff(eff), opts);
117
+ };
118
+ test.skipIf = (condition) => (name, eff, opts) => {
119
+ bun.test.skipIf(condition)(name, () => runEff(eff), opts);
120
+ };
121
+ test.only = (name, eff, opts) => {
122
+ bun.test.only(name, () => runEff(eff), opts);
123
+ };
124
+ test.todo = (name, eff, opts) => {
125
+ bun.test.todo(name, () => runEff(eff), opts);
126
+ };
127
+
128
+ const runProvider = (
129
+ name: string,
130
+ fn: (stack: ScratchStack) => Effect.Effect<void, any, any>,
131
+ ) => {
132
+ const scratch = Core.scratchStack(options, name);
133
+ return Core.run(Core.withProviders(fn(scratch), options, scratch.name), {
134
+ ...options,
135
+ state: scratch.state,
136
+ });
137
+ };
138
+
139
+ const provider = ((name, fn, opts) => {
140
+ bun.test(name, () => runProvider(name, fn), opts);
141
+ }) as ProviderFn;
142
+ provider.skip = (name, fn, opts) => {
143
+ bun.test.skip(name, () => runProvider(name, fn), opts);
144
+ };
145
+ provider.skipIf = (condition) => (name, fn, opts) => {
146
+ bun.test.skipIf(condition)(name, () => runProvider(name, fn), opts);
147
+ };
148
+ test.provider = provider;
149
+
150
+ const beforeAll: BeforeAllFn = <A>(
151
+ eff: TestEffect<A>,
152
+ hookOptions?: HookOptions,
153
+ ) => {
154
+ let result: A;
155
+ bun.beforeAll(
156
+ () => runEff(eff).then((v) => (result = v)),
157
+ hookOptions ?? DEFAULT_HOOK_TIMEOUT,
158
+ );
159
+ return Effect.sync(() => result);
160
+ };
161
+
162
+ const beforeEach: BeforeEachFn = (eff, hookOptions) => {
163
+ bun.beforeEach(() => runEff(eff), hookOptions);
164
+ };
165
+
166
+ const afterAll = ((eff, hookOptions) => {
167
+ bun.afterAll(() => runEff(eff), hookOptions ?? DEFAULT_HOOK_TIMEOUT);
168
+ }) as AfterAllFn;
169
+ afterAll.skipIf = (predicate) => (eff, hookOptions) => {
170
+ if (predicate) return;
171
+ bun.afterAll(() => runEff(eff), hookOptions ?? DEFAULT_HOOK_TIMEOUT);
172
+ };
173
+
174
+ const afterEach: AfterEachFn = (eff, hookOptions) => {
175
+ bun.afterEach(() => runEff(eff), hookOptions);
176
+ };
177
+
178
+ return {
179
+ test,
180
+ beforeAll,
181
+ beforeEach,
182
+ afterAll,
183
+ afterEach,
184
+ deploy: (stack, callOpts) => Core.deploy(options, stack, callOpts),
185
+ destroy: (stack, callOpts) => Core.destroy(options, stack, callOpts),
186
+ };
187
+ };