alchemy 2.0.0-beta.36 → 2.0.0-beta.37

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 (198) hide show
  1. package/bin/exec.js +166 -43
  2. package/bin/exec.js.map +1 -1
  3. package/lib/AWS/Lambda/Function.d.ts.map +1 -1
  4. package/lib/AWS/Lambda/Function.js +22 -2
  5. package/lib/AWS/Lambda/Function.js.map +1 -1
  6. package/lib/Apply.d.ts.map +1 -1
  7. package/lib/Apply.js +6 -1
  8. package/lib/Apply.js.map +1 -1
  9. package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.d.ts +54 -0
  10. package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.d.ts.map +1 -0
  11. package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.js +17 -0
  12. package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.js.map +1 -0
  13. package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.d.ts +37 -0
  14. package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.d.ts.map +1 -0
  15. package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.js +45 -0
  16. package/lib/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.js.map +1 -0
  17. package/lib/Cloudflare/AnalyticsEngine/index.d.ts +3 -0
  18. package/lib/Cloudflare/AnalyticsEngine/index.d.ts.map +1 -0
  19. package/lib/Cloudflare/AnalyticsEngine/index.js +3 -0
  20. package/lib/Cloudflare/AnalyticsEngine/index.js.map +1 -0
  21. package/lib/Cloudflare/D1/D1Connection.d.ts +47 -3
  22. package/lib/Cloudflare/D1/D1Connection.d.ts.map +1 -1
  23. package/lib/Cloudflare/D1/D1Connection.js +62 -5
  24. package/lib/Cloudflare/D1/D1Connection.js.map +1 -1
  25. package/lib/Cloudflare/Local/SidecarHandlers.js +9 -1
  26. package/lib/Cloudflare/Local/SidecarHandlers.js.map +1 -1
  27. package/lib/Cloudflare/Providers.d.ts.map +1 -1
  28. package/lib/Cloudflare/Providers.js +4 -1
  29. package/lib/Cloudflare/Providers.js.map +1 -1
  30. package/lib/Cloudflare/R2/R2Bucket.d.ts.map +1 -1
  31. package/lib/Cloudflare/R2/R2Bucket.js +16 -0
  32. package/lib/Cloudflare/R2/R2Bucket.js.map +1 -1
  33. package/lib/Cloudflare/StateStore/Api.d.ts +6 -1
  34. package/lib/Cloudflare/StateStore/Api.d.ts.map +1 -1
  35. package/lib/Cloudflare/StateStore/Api.js +23 -1
  36. package/lib/Cloudflare/StateStore/Api.js.map +1 -1
  37. package/lib/Cloudflare/StateStore/Store.d.ts +16 -1
  38. package/lib/Cloudflare/StateStore/Store.d.ts.map +1 -1
  39. package/lib/Cloudflare/StateStore/Store.js +22 -5
  40. package/lib/Cloudflare/StateStore/Store.js.map +1 -1
  41. package/lib/Cloudflare/Workers/CronEventSource.d.ts +35 -0
  42. package/lib/Cloudflare/Workers/CronEventSource.d.ts.map +1 -0
  43. package/lib/Cloudflare/Workers/CronEventSource.js +55 -0
  44. package/lib/Cloudflare/Workers/CronEventSource.js.map +1 -0
  45. package/lib/Cloudflare/Workers/InferEnv.d.ts +37 -2
  46. package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
  47. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
  48. package/lib/Cloudflare/Workers/LocalWorkerProvider.js +2 -1
  49. package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
  50. package/lib/Cloudflare/Workers/Rpc.d.ts +76 -3
  51. package/lib/Cloudflare/Workers/Rpc.d.ts.map +1 -1
  52. package/lib/Cloudflare/Workers/Rpc.js +197 -13
  53. package/lib/Cloudflare/Workers/Rpc.js.map +1 -1
  54. package/lib/Cloudflare/Workers/Worker.d.ts +22 -2
  55. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  56. package/lib/Cloudflare/Workers/Worker.js +174 -43
  57. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  58. package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
  59. package/lib/Cloudflare/Workers/WorkerBundle.js +15 -5
  60. package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
  61. package/lib/Cloudflare/Workers/Workflow.d.ts +50 -47
  62. package/lib/Cloudflare/Workers/Workflow.d.ts.map +1 -1
  63. package/lib/Cloudflare/Workers/Workflow.js +41 -35
  64. package/lib/Cloudflare/Workers/Workflow.js.map +1 -1
  65. package/lib/Cloudflare/Workers/index.d.ts +1 -0
  66. package/lib/Cloudflare/Workers/index.d.ts.map +1 -1
  67. package/lib/Cloudflare/Workers/index.js +1 -0
  68. package/lib/Cloudflare/Workers/index.js.map +1 -1
  69. package/lib/Cloudflare/index.d.ts +1 -0
  70. package/lib/Cloudflare/index.d.ts.map +1 -1
  71. package/lib/Cloudflare/index.js +1 -0
  72. package/lib/Cloudflare/index.js.map +1 -1
  73. package/lib/Deploy.d.ts +4 -1
  74. package/lib/Deploy.d.ts.map +1 -1
  75. package/lib/Deploy.js +2 -2
  76. package/lib/Deploy.js.map +1 -1
  77. package/lib/Destroy.d.ts +4 -1
  78. package/lib/Destroy.d.ts.map +1 -1
  79. package/lib/Destroy.js +2 -2
  80. package/lib/Destroy.js.map +1 -1
  81. package/lib/Output.d.ts +45 -5
  82. package/lib/Output.d.ts.map +1 -1
  83. package/lib/Output.js +100 -4
  84. package/lib/Output.js.map +1 -1
  85. package/lib/Plan.d.ts.map +1 -1
  86. package/lib/Plan.js +93 -12
  87. package/lib/Plan.js.map +1 -1
  88. package/lib/Platform.d.ts.map +1 -1
  89. package/lib/Platform.js +16 -3
  90. package/lib/Platform.js.map +1 -1
  91. package/lib/Ref.d.ts +4 -1
  92. package/lib/Ref.d.ts.map +1 -1
  93. package/lib/Ref.js +1 -1
  94. package/lib/Ref.js.map +1 -1
  95. package/lib/Resource.d.ts +8 -0
  96. package/lib/Resource.d.ts.map +1 -1
  97. package/lib/Resource.js +10 -0
  98. package/lib/Resource.js.map +1 -1
  99. package/lib/RuntimeContext.d.ts +3 -1
  100. package/lib/RuntimeContext.d.ts.map +1 -1
  101. package/lib/RuntimeContext.js.map +1 -1
  102. package/lib/Secret.d.ts +50 -0
  103. package/lib/Secret.d.ts.map +1 -0
  104. package/lib/Secret.js +23 -0
  105. package/lib/Secret.js.map +1 -0
  106. package/lib/Serverless/Function.d.ts +9 -1
  107. package/lib/Serverless/Function.d.ts.map +1 -1
  108. package/lib/Stack.d.ts +14 -5
  109. package/lib/Stack.d.ts.map +1 -1
  110. package/lib/Stack.js +31 -19
  111. package/lib/Stack.js.map +1 -1
  112. package/lib/State/HttpStateApi.d.ts +15 -1
  113. package/lib/State/HttpStateApi.d.ts.map +1 -1
  114. package/lib/State/HttpStateApi.js +11 -0
  115. package/lib/State/HttpStateApi.js.map +1 -1
  116. package/lib/State/HttpStateStore.d.ts.map +1 -1
  117. package/lib/State/HttpStateStore.js +11 -0
  118. package/lib/State/HttpStateStore.js.map +1 -1
  119. package/lib/State/InMemoryState.d.ts +2 -2
  120. package/lib/State/InMemoryState.d.ts.map +1 -1
  121. package/lib/State/InMemoryState.js +12 -6
  122. package/lib/State/InMemoryState.js.map +1 -1
  123. package/lib/State/LocalState.d.ts.map +1 -1
  124. package/lib/State/LocalState.js +11 -2
  125. package/lib/State/LocalState.js.map +1 -1
  126. package/lib/State/State.d.ts +21 -0
  127. package/lib/State/State.d.ts.map +1 -1
  128. package/lib/Test/Bun.d.ts.map +1 -1
  129. package/lib/Test/Bun.js +30 -7
  130. package/lib/Test/Bun.js.map +1 -1
  131. package/lib/Test/Core.d.ts +17 -2
  132. package/lib/Test/Core.d.ts.map +1 -1
  133. package/lib/Test/Core.js +29 -11
  134. package/lib/Test/Core.js.map +1 -1
  135. package/lib/Test/Vitest.d.ts.map +1 -1
  136. package/lib/Test/Vitest.js +24 -5
  137. package/lib/Test/Vitest.js.map +1 -1
  138. package/lib/Util/LocalhostDns.d.ts +14 -10
  139. package/lib/Util/LocalhostDns.d.ts.map +1 -1
  140. package/lib/Util/LocalhostDns.js +27 -36
  141. package/lib/Util/LocalhostDns.js.map +1 -1
  142. package/lib/Util/effect.d.ts +7 -0
  143. package/lib/Util/effect.d.ts.map +1 -1
  144. package/lib/Util/effect.js +6 -0
  145. package/lib/Util/effect.js.map +1 -1
  146. package/lib/Variable.d.ts +29 -0
  147. package/lib/Variable.d.ts.map +1 -0
  148. package/lib/Variable.js +9 -0
  149. package/lib/Variable.js.map +1 -0
  150. package/lib/index.d.ts +3 -0
  151. package/lib/index.d.ts.map +1 -1
  152. package/lib/index.js +3 -0
  153. package/lib/index.js.map +1 -1
  154. package/lib/tsconfig.test.tsbuildinfo +1 -1
  155. package/package.json +6 -7
  156. package/src/AWS/Lambda/Function.ts +30 -2
  157. package/src/Apply.ts +8 -1
  158. package/src/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.ts +82 -0
  159. package/src/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.ts +106 -0
  160. package/src/Cloudflare/AnalyticsEngine/index.ts +2 -0
  161. package/src/Cloudflare/D1/D1Connection.ts +95 -12
  162. package/src/Cloudflare/Local/SidecarHandlers.ts +9 -1
  163. package/src/Cloudflare/Providers.ts +5 -0
  164. package/src/Cloudflare/R2/R2Bucket.ts +31 -0
  165. package/src/Cloudflare/StateStore/Api.ts +34 -1
  166. package/src/Cloudflare/StateStore/Store.ts +40 -10
  167. package/src/Cloudflare/Workers/CronEventSource.ts +89 -0
  168. package/src/Cloudflare/Workers/InferEnv.ts +94 -20
  169. package/src/Cloudflare/Workers/LocalWorkerProvider.ts +9 -1
  170. package/src/Cloudflare/Workers/Rpc.ts +227 -18
  171. package/src/Cloudflare/Workers/Worker.ts +277 -63
  172. package/src/Cloudflare/Workers/WorkerBundle.ts +15 -5
  173. package/src/Cloudflare/Workers/Workflow.ts +74 -70
  174. package/src/Cloudflare/Workers/index.ts +1 -0
  175. package/src/Cloudflare/index.ts +1 -0
  176. package/src/Deploy.ts +5 -1
  177. package/src/Destroy.ts +5 -1
  178. package/src/Output.ts +121 -12
  179. package/src/Plan.ts +101 -12
  180. package/src/Platform.ts +19 -4
  181. package/src/Ref.ts +10 -5
  182. package/src/Resource.ts +22 -0
  183. package/src/RuntimeContext.ts +1 -0
  184. package/src/Secret.ts +87 -0
  185. package/src/Serverless/Function.ts +10 -1
  186. package/src/Stack.ts +52 -29
  187. package/src/State/HttpStateApi.ts +21 -0
  188. package/src/State/HttpStateStore.ts +21 -0
  189. package/src/State/InMemoryState.ts +29 -8
  190. package/src/State/LocalState.ts +28 -4
  191. package/src/State/State.ts +21 -0
  192. package/src/Test/Bun.ts +43 -7
  193. package/src/Test/Core.ts +29 -8
  194. package/src/Test/Vitest.ts +33 -4
  195. package/src/Util/LocalhostDns.ts +30 -36
  196. package/src/Util/effect.ts +25 -0
  197. package/src/Variable.ts +44 -0
  198. package/src/index.ts +3 -0
package/bin/exec.js CHANGED
@@ -1,4 +1,3 @@
1
- import { createRequire } from "node:module";
2
1
  import * as ConfigProvider from "effect/ConfigProvider";
3
2
  import * as Effect from "effect/Effect";
4
3
  import * as Layer from "effect/Layer";
@@ -12,6 +11,8 @@ import * as Redacted from "effect/Redacted";
12
11
  import path from "pathe";
13
12
  import * as Config from "effect/Config";
14
13
  import os from "node:os";
14
+ import * as dns from "node:dns";
15
+ import * as undici from "undici";
15
16
  import * as Console from "effect/Console";
16
17
  import * as Logger from "effect/Logger";
17
18
  import * as Option from "effect/Option";
@@ -23,6 +24,7 @@ import * as Deferred from "effect/Deferred";
23
24
  import { pipe } from "effect/Function";
24
25
  import { SingleShotGen } from "effect/Utils";
25
26
  import { pipeArguments } from "effect/Pipeable";
27
+ import "effect/Scope";
26
28
  import * as Duration from "effect/Duration";
27
29
  import * as Exit from "effect/Exit";
28
30
  import * as Metric from "effect/Metric";
@@ -152,22 +154,16 @@ const isLocalhost = (hostname) => hostname === "localhost" || hostname === "[::1
152
154
  const installLocalhostDns = () => {
153
155
  if (installed) return;
154
156
  installed = true;
155
- if (typeof globalThis.Bun !== "undefined") return;
156
- try {
157
- const require = createRequire(import.meta.url);
158
- const undici = require("undici");
159
- const dns = require("node:dns");
160
- undici.setGlobalDispatcher(new undici.Agent({ connect: { lookup: (hostname, options, cb) => {
161
- if (isLocalhost(hostname)) {
162
- if (options && options.all) return cb(null, [{
163
- address: "127.0.0.1",
164
- family: 4
165
- }]);
166
- return cb(null, "127.0.0.1", 4);
167
- }
168
- return dns.lookup(hostname, options, cb);
169
- } } }));
170
- } catch {}
157
+ undici.setGlobalDispatcher(new undici.Agent({ connect: { lookup: (hostname, options, cb) => {
158
+ if (isLocalhost(hostname)) {
159
+ if (options && options.all) return cb(null, [{
160
+ address: "127.0.0.1",
161
+ family: 4
162
+ }]);
163
+ return cb(null, "127.0.0.1", 4);
164
+ }
165
+ return dns.lookup(hostname, options, cb);
166
+ } } }));
171
167
  };
172
168
 
173
169
  //#endregion
@@ -358,18 +354,7 @@ var Cli = class extends EffectContext.Service()("CLI") {};
358
354
  //#region lib/Ref.js
359
355
  const RefMetadata = Symbol.for("alchemy/RefMetadata");
360
356
  const isRef = (s) => s && s[RefMetadata] !== void 0;
361
- const getRefMetadata = (ref$1) => ref$1[RefMetadata];
362
- const ref = ({ stack, id, stage: stage$1 }) => {
363
- const ref$1 = new Proxy({}, { get: (_, prop) => {
364
- if (prop === RefMetadata) return {
365
- stack,
366
- stage: stage$1,
367
- id
368
- };
369
- return of(ref$1)[prop];
370
- } });
371
- return ref$1;
372
- };
357
+ const getRefMetadata = (ref) => ref[RefMetadata];
373
358
 
374
359
  //#endregion
375
360
  //#region lib/FQN.js
@@ -479,6 +464,17 @@ const loadConfigProvider = (envFile$1) => {
479
464
 
480
465
  //#endregion
481
466
  //#region lib/Util/effect.js
467
+ const effectClass = ((impl) => impl === void 0 ? (innerImpl) => effectClass(innerImpl) : Object.assign(class {
468
+ static asEffect() {
469
+ return impl;
470
+ }
471
+ static [Symbol.iterator]() {
472
+ return new SingleShotGen(this);
473
+ }
474
+ static pipe(...fns) {
475
+ return pipeArguments(this.asEffect(), fns);
476
+ }
477
+ }, impl));
482
478
  const taggedFunction = (tag$1, fn) => {
483
479
  const overrides = {
484
480
  asEffect: () => tag$1.asEffect(),
@@ -505,14 +501,17 @@ const fileLogger = Effect.fnUntraced(function* (...segments) {
505
501
 
506
502
  //#endregion
507
503
  //#region lib/Stack.js
508
- const Stack = Object.assign(taggedFunction(EffectContext.Service()("Stack"), (stackName, options, eff) => eff.pipe(make$1({
509
- name: stackName,
510
- ...options
511
- }), (eff$1) => Object.assign(eff$1, { stage: new Proxy({}, { get: (_, stage$1) => ref({
512
- stack: stackName,
513
- stage: stage$1,
514
- id: stackName
515
- }) }) }))));
504
+ const Stack = Object.assign(taggedFunction(EffectContext.Service()("Stack"), (stackName, options, eff) => {
505
+ if (!stackName) return (stackName$1) => Object.assign(stackRef(stackName$1).pipe(effectClass), {
506
+ stage: createStageProxy(stackName$1),
507
+ make: (options$1, eff$1) => Stack(stackName$1, options$1, eff$1)
508
+ });
509
+ return eff.pipe(make$1({
510
+ name: stackName,
511
+ ...options
512
+ }), (eff$1) => Object.assign(eff$1, { stage: createStageProxy(stackName) }));
513
+ }));
514
+ const createStageProxy = (stackName) => new Proxy({}, { get: (_, stage$1) => stackRef(stackName, { stage: stage$1 }) });
516
515
  const StackName = Stack.use((stack) => Effect.succeed(stack.name));
517
516
  const make$1 = (options) => (effect) => Effect.scope.pipe(Effect.flatMap((scope) => {
518
517
  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.`));
@@ -667,6 +666,28 @@ var EffectExpr = class extends BaseExpr {
667
666
  return `${this.expr[inspect]()}.mapEffect(${this.f.toString()})`;
668
667
  }
669
668
  };
669
+ const isNamedExpr = (node) => node?.kind === "NamedExpr";
670
+ /**
671
+ * Wraps another `Expr` and overrides its `toString()` / inspect output.
672
+ *
673
+ * `BaseExpr.asEffect()` derives the binding id from `this.toString()`, so
674
+ * wrapping an expression in `NamedExpr` makes that derived id stable and
675
+ * caller-controlled (e.g. an env var name like `"API_KEY"`).
676
+ */
677
+ var NamedExpr = class extends BaseExpr {
678
+ expr;
679
+ bindingName;
680
+ kind = "NamedExpr";
681
+ constructor(expr, bindingName) {
682
+ super();
683
+ this.expr = expr;
684
+ this.bindingName = bindingName;
685
+ return proxy(this);
686
+ }
687
+ [inspect]() {
688
+ return this.bindingName;
689
+ }
690
+ };
670
691
  const isAllExpr = (node) => node?.kind === "AllExpr";
671
692
  var AllExpr = class extends BaseExpr {
672
693
  outs;
@@ -697,6 +718,39 @@ var RefExpr = class extends BaseExpr {
697
718
  return `ref(${this.resourceId}, { stack: ${this.stack}, stage: ${this.stage} })`;
698
719
  }
699
720
  };
721
+ const isStackRefExpr = (node) => node?.kind === "StackRefExpr";
722
+ /**
723
+ * A reference to the persisted output of a Stack at `(stack, stage)`.
724
+ *
725
+ * Resolved at evaluation time by reading `state.getOutput({ stack,
726
+ * stage })`. Distinct from {@link RefExpr}, which references a
727
+ * single resource's attributes within a stack/stage. `stage` may be
728
+ * `undefined`, in which case it falls back to the current stage.
729
+ */
730
+ var StackRefExpr = class extends BaseExpr {
731
+ stack;
732
+ stage;
733
+ kind = "StackRefExpr";
734
+ constructor(stack, stage$1) {
735
+ super();
736
+ this.stack = stack;
737
+ this.stage = stage$1;
738
+ return proxy(this);
739
+ }
740
+ [inspect]() {
741
+ return `stackRef(${this.stack}${this.stage ? `, { stage: ${this.stage} }` : ""})`;
742
+ }
743
+ };
744
+ /**
745
+ * Build an `Output<A>` referencing the persisted output of another
746
+ * Stack. The returned Effect resolves to a lazy `Output<A>` whose
747
+ * value is read from the state store at plan/apply time.
748
+ *
749
+ * Returns `Effect<Output<A>>` (not `Output<A>` directly) so that
750
+ * `yield* Output.stackRef(...)` reads ergonomically inside an Effect
751
+ * generator and lines up with `Resource.ref` and `Stack.stage.<name>`.
752
+ */
753
+ const stackRef = (stack, options = {}) => Effect.succeed(new StackRefExpr(stack, options.stage));
700
754
  function proxy(self) {
701
755
  const target = Object.assign(() => {}, self);
702
756
  if (inspect in self) Object.defineProperty(target, inspect, {
@@ -705,7 +759,9 @@ function proxy(self) {
705
759
  });
706
760
  const proxy$1 = new Proxy(target, {
707
761
  has: (_, prop) => prop === ExprSymbol || prop === inspect ? true : prop in self,
708
- 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),
762
+ get: (target$1, prop) => prop === Symbol.toPrimitive ? (hint) => {
763
+ throw new Error(`Cannot coerce Output<${self[inspect]()}> to a ${hint === "number" ? "number" : "string"} via JS coercion. Use Output.interpolate\`...\` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.`);
764
+ } : 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),
709
765
  apply: (_, thisArg, args) => {
710
766
  if (isPropExpr(self)) {
711
767
  if (self.identifier === "apply") return new ApplyExpr(self.expr, args[0]);
@@ -740,6 +796,7 @@ const evaluate = (expr, upstream$1) => Effect.gen(function* () {
740
796
  else if (isEffectExpr(expr)) return yield* expr.f(yield* evaluate(expr.expr, upstream$1));
741
797
  else if (isAllExpr(expr)) return yield* Effect.all(expr.outs.map((out) => evaluate(out, upstream$1)));
742
798
  else if (isPropExpr(expr)) return (yield* evaluate(expr.expr, upstream$1))?.[expr.identifier];
799
+ else if (isNamedExpr(expr)) return yield* evaluate(expr.expr, upstream$1);
743
800
  else if (isRefExpr(expr)) {
744
801
  const state = yield* State;
745
802
  const stack = expr.stack ?? (yield* Stack).name;
@@ -756,6 +813,21 @@ const evaluate = (expr, upstream$1) => Effect.gen(function* () {
756
813
  resourceId: expr.resourceId
757
814
  }));
758
815
  return resource.attr;
816
+ } else if (isStackRefExpr(expr)) {
817
+ const state = yield* State;
818
+ const stack = expr.stack;
819
+ const stage$1 = expr.stage ?? (yield* Stage);
820
+ const output = yield* state.getOutput({
821
+ stack,
822
+ stage: stage$1
823
+ });
824
+ if (output == null) return yield* Effect.fail(new InvalidReferenceError({
825
+ message: `Reference to stack '${stack}' at stage '${stage$1}' not found. Have you deployed stage '${stage$1}' of '${stack}'?`,
826
+ stack,
827
+ stage: stage$1,
828
+ resourceId: stack
829
+ }));
830
+ return output;
759
831
  }
760
832
  }
761
833
  if (Array.isArray(expr)) return yield* Effect.all(expr.map((item) => evaluate(item, upstream$1)));
@@ -776,7 +848,7 @@ const upstream = (expr) => {
776
848
  else if (isResourceExpr(expr)) return { [expr.src.FQN]: expr.src };
777
849
  else if (isPropExpr(expr)) return upstream(expr.expr);
778
850
  else if (isAllExpr(expr)) return Object.assign({}, ...expr.outs.map((out) => upstream(out)));
779
- else if (isEffectExpr(expr) || isApplyExpr(expr)) return upstream(expr.expr);
851
+ else if (isEffectExpr(expr) || isApplyExpr(expr) || isNamedExpr(expr)) return upstream(expr.expr);
780
852
  else if (Array.isArray(expr)) return expr.map(upstream).reduce(toObject, {});
781
853
  else if (typeof expr === "object" && expr !== null) return Object.values(expr).map((v) => upstream(v)).reduce(toObject, {});
782
854
  return {};
@@ -1074,7 +1146,37 @@ const make = (stack, options = {}) => Effect.gen(function* () {
1074
1146
  return hasOutputs(upstream$1) ? expr : yield* expr.f(upstream$1);
1075
1147
  } else if (isAllExpr(expr)) return yield* Effect.all(expr.outs.map(resolveOutput), { concurrency: "unbounded" });
1076
1148
  else if (isLiteralExpr(expr)) return expr.value;
1077
- return yield* Effect.die(/* @__PURE__ */ new Error("Not implemented yet"));
1149
+ else if (isRefExpr(expr)) {
1150
+ const refStack = expr.stack ?? stackName;
1151
+ const refStage = expr.stage ?? stage$1;
1152
+ const refState = yield* state.get({
1153
+ stack: refStack,
1154
+ stage: refStage,
1155
+ fqn: expr.resourceId
1156
+ }).pipe(Effect.orDie);
1157
+ if (!refState) return yield* Effect.die(new InvalidReferenceError({
1158
+ message: `Reference to '${expr.resourceId}' in stack '${refStack}' and stage '${refStage}' not found. Have you deployed '${refStage}' of '${refStack}'?`,
1159
+ stack: refStack,
1160
+ stage: refStage,
1161
+ resourceId: expr.resourceId
1162
+ }));
1163
+ return refState.attr;
1164
+ } else if (isStackRefExpr(expr)) {
1165
+ const refStack = expr.stack;
1166
+ const refStage = expr.stage ?? stage$1;
1167
+ const output = yield* state.getOutput({
1168
+ stack: refStack,
1169
+ stage: refStage
1170
+ }).pipe(Effect.orDie);
1171
+ if (output == null) return yield* Effect.die(new InvalidReferenceError({
1172
+ message: `Reference to stack '${refStack}' at stage '${refStage}' not found. Have you deployed stage '${refStage}' of '${refStack}'?`,
1173
+ stack: refStack,
1174
+ stage: refStage,
1175
+ resourceId: refStack
1176
+ }));
1177
+ return output;
1178
+ } else if (isNamedExpr(expr)) return yield* resolveOutput(expr.expr);
1179
+ return yield* Effect.die(/* @__PURE__ */ new Error("Not implemented yet" + expr.kind));
1078
1180
  });
1079
1181
  const oldDownstreamDependencies = Object.fromEntries(oldResources.filter((resource) => !!resource).map((resource) => [resource.fqn, resource.downstream]));
1080
1182
  const newResourceFqns = new Set(resources.map((r) => r.FQN));
@@ -1090,7 +1192,22 @@ const make = (stack, options = {}) => Effect.gen(function* () {
1090
1192
  const fqn = resource.FQN;
1091
1193
  return [fqn, (rawUpstreamDependencies[fqn] ?? []).filter((dep) => newResourceFqns.has(dep))];
1092
1194
  }));
1093
- const newDownstreamDependencies = Object.fromEntries(resources.map((resource) => [resource.FQN, Object.entries(newUpstreamDependencies).filter(([_, upstream$1]) => upstream$1.includes(resource.FQN)).map(([depFqn]) => depFqn)]));
1195
+ const combinedCycleMembers = findCycleMembers(allUpstreamDependencies);
1196
+ const newDownstreamDependencies = Object.fromEntries(resources.map((resource) => {
1197
+ const upFqn = resource.FQN;
1198
+ const downstream = [];
1199
+ for (const [downFqn, upstreams] of Object.entries(rawUpstreamDependencies)) {
1200
+ if (downFqn === upFqn) continue;
1201
+ if (!upstreams.includes(upFqn)) continue;
1202
+ if ((newUpstreamDependencies[downFqn] ?? []).includes(upFqn)) {
1203
+ downstream.push(downFqn);
1204
+ continue;
1205
+ }
1206
+ if (combinedCycleMembers.has(upFqn) && combinedCycleMembers.has(downFqn)) continue;
1207
+ downstream.push(downFqn);
1208
+ }
1209
+ return [upFqn, downstream];
1210
+ }));
1094
1211
  const resourceGraph = Object.fromEntries((yield* Effect.all(resources.map(Effect.fn("plan.diff.resource")(function* (resource) {
1095
1212
  const provider = yield* findProviderByType(resource.Type);
1096
1213
  const id = resource.LogicalId;
@@ -1273,7 +1390,7 @@ const make = (stack, options = {}) => Effect.gen(function* () {
1273
1390
  state: oldState
1274
1391
  });
1275
1392
  })), { concurrency: "unbounded" })).map((update) => [update.resource.FQN, update]));
1276
- const cycleMembers = findCycleMembers(allUpstreamDependencies);
1393
+ const cycleMembers = combinedCycleMembers;
1277
1394
  {
1278
1395
  const createReplaceNodes = new Set(Object.entries(resourceGraph).filter(([_, node]) => node.action === "create" || node.action === "replace").map(([fqn]) => fqn));
1279
1396
  if (createReplaceNodes.size > 0) {
@@ -1417,7 +1534,13 @@ const apply = (plan) => Effect.gen(function* () {
1417
1534
  yield* session.done();
1418
1535
  if (!plan.output) return;
1419
1536
  const outputs = Object.fromEntries(Object.entries(tracker).map(([fqn, t]) => [fqn, t.output]));
1420
- return yield* evaluate(plan.output, outputs);
1537
+ const resolved = yield* evaluate(plan.output, outputs);
1538
+ yield* state.setOutput({
1539
+ stack: stackName,
1540
+ stage: stage$1,
1541
+ value: resolved
1542
+ });
1543
+ return resolved;
1421
1544
  }).pipe(ensureArtifactStore, Effect.withSpan("apply", { attributes: {
1422
1545
  "alchemy.resources.count": Object.keys(plan.resources).length,
1423
1546
  "alchemy.deletions.count": Object.keys(plan.deletions).length