alchemy-effect 0.3.0 → 0.4.0

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 (158) hide show
  1. package/bin/alchemy-effect.js +539 -223
  2. package/bin/alchemy-effect.js.map +1 -1
  3. package/lib/apply.d.ts +4 -4
  4. package/lib/apply.d.ts.map +1 -1
  5. package/lib/apply.js +411 -131
  6. package/lib/apply.js.map +1 -1
  7. package/lib/aws/dynamodb/table.provider.d.ts.map +1 -1
  8. package/lib/aws/dynamodb/table.provider.js +1 -0
  9. package/lib/aws/dynamodb/table.provider.js.map +1 -1
  10. package/lib/aws/ec2/index.d.ts +8 -0
  11. package/lib/aws/ec2/index.d.ts.map +1 -1
  12. package/lib/aws/ec2/index.js +8 -0
  13. package/lib/aws/ec2/index.js.map +1 -1
  14. package/lib/aws/ec2/internet-gateway.d.ts +65 -0
  15. package/lib/aws/ec2/internet-gateway.d.ts.map +1 -0
  16. package/lib/aws/ec2/internet-gateway.js +4 -0
  17. package/lib/aws/ec2/internet-gateway.js.map +1 -0
  18. package/lib/aws/ec2/internet-gateway.provider.d.ts +6 -0
  19. package/lib/aws/ec2/internet-gateway.provider.d.ts.map +1 -0
  20. package/lib/aws/ec2/internet-gateway.provider.js +193 -0
  21. package/lib/aws/ec2/internet-gateway.provider.js.map +1 -0
  22. package/lib/aws/ec2/route-table-association.d.ts +63 -0
  23. package/lib/aws/ec2/route-table-association.d.ts.map +1 -0
  24. package/lib/aws/ec2/route-table-association.js +4 -0
  25. package/lib/aws/ec2/route-table-association.js.map +1 -0
  26. package/lib/aws/ec2/route-table-association.provider.d.ts +4 -0
  27. package/lib/aws/ec2/route-table-association.provider.d.ts.map +1 -0
  28. package/lib/aws/ec2/route-table-association.provider.js +121 -0
  29. package/lib/aws/ec2/route-table-association.provider.js.map +1 -0
  30. package/lib/aws/ec2/route-table.d.ts +159 -0
  31. package/lib/aws/ec2/route-table.d.ts.map +1 -0
  32. package/lib/aws/ec2/route-table.js +4 -0
  33. package/lib/aws/ec2/route-table.js.map +1 -0
  34. package/lib/aws/ec2/route-table.provider.d.ts +6 -0
  35. package/lib/aws/ec2/route-table.provider.d.ts.map +1 -0
  36. package/lib/aws/ec2/route-table.provider.js +213 -0
  37. package/lib/aws/ec2/route-table.provider.js.map +1 -0
  38. package/lib/aws/ec2/route.d.ts +155 -0
  39. package/lib/aws/ec2/route.d.ts.map +1 -0
  40. package/lib/aws/ec2/route.js +3 -0
  41. package/lib/aws/ec2/route.js.map +1 -0
  42. package/lib/aws/ec2/route.provider.d.ts +4 -0
  43. package/lib/aws/ec2/route.provider.d.ts.map +1 -0
  44. package/lib/aws/ec2/route.provider.js +166 -0
  45. package/lib/aws/ec2/route.provider.js.map +1 -0
  46. package/lib/aws/ec2/subnet.provider.d.ts.map +1 -1
  47. package/lib/aws/ec2/subnet.provider.js +1 -1
  48. package/lib/aws/ec2/subnet.provider.js.map +1 -1
  49. package/lib/aws/ec2/vpc.d.ts +1 -0
  50. package/lib/aws/ec2/vpc.d.ts.map +1 -1
  51. package/lib/aws/ec2/vpc.provider.d.ts +2 -2
  52. package/lib/aws/ec2/vpc.provider.d.ts.map +1 -1
  53. package/lib/aws/ec2/vpc.provider.js +38 -15
  54. package/lib/aws/ec2/vpc.provider.js.map +1 -1
  55. package/lib/aws/index.d.ts +2 -3
  56. package/lib/aws/index.d.ts.map +1 -1
  57. package/lib/aws/index.js +2 -1
  58. package/lib/aws/index.js.map +1 -1
  59. package/lib/aws/lambda/function.provider.d.ts +2 -2
  60. package/lib/aws/lambda/function.provider.d.ts.map +1 -1
  61. package/lib/aws/lambda/function.provider.js +21 -20
  62. package/lib/aws/lambda/function.provider.js.map +1 -1
  63. package/lib/aws/sqs/queue.provider.d.ts +2 -2
  64. package/lib/aws/sqs/queue.provider.d.ts.map +1 -1
  65. package/lib/aws/sqs/queue.provider.js +3 -2
  66. package/lib/aws/sqs/queue.provider.js.map +1 -1
  67. package/lib/cli/index.d.ts +178 -99
  68. package/lib/cli/index.d.ts.map +1 -1
  69. package/lib/cloudflare/kv/namespace.client.d.ts +1 -1
  70. package/lib/cloudflare/kv/namespace.provider.d.ts.map +1 -1
  71. package/lib/cloudflare/kv/namespace.provider.js +1 -0
  72. package/lib/cloudflare/kv/namespace.provider.js.map +1 -1
  73. package/lib/cloudflare/r2/bucket.provider.d.ts.map +1 -1
  74. package/lib/cloudflare/r2/bucket.provider.js +6 -1
  75. package/lib/cloudflare/r2/bucket.provider.js.map +1 -1
  76. package/lib/cloudflare/worker/worker.provider.d.ts +1 -1
  77. package/lib/cloudflare/worker/worker.provider.d.ts.map +1 -1
  78. package/lib/cloudflare/worker/worker.provider.js +6 -2
  79. package/lib/cloudflare/worker/worker.provider.js.map +1 -1
  80. package/lib/diff.d.ts +8 -6
  81. package/lib/diff.d.ts.map +1 -1
  82. package/lib/diff.js +13 -0
  83. package/lib/diff.js.map +1 -1
  84. package/lib/event.d.ts +1 -1
  85. package/lib/event.d.ts.map +1 -1
  86. package/lib/instance-id.d.ts +8 -0
  87. package/lib/instance-id.d.ts.map +1 -0
  88. package/lib/instance-id.js +12 -0
  89. package/lib/instance-id.js.map +1 -0
  90. package/lib/output.d.ts +4 -2
  91. package/lib/output.d.ts.map +1 -1
  92. package/lib/output.js +18 -4
  93. package/lib/output.js.map +1 -1
  94. package/lib/physical-name.d.ts +14 -1
  95. package/lib/physical-name.d.ts.map +1 -1
  96. package/lib/physical-name.js +41 -2
  97. package/lib/physical-name.js.map +1 -1
  98. package/lib/plan.d.ts +49 -42
  99. package/lib/plan.d.ts.map +1 -1
  100. package/lib/plan.js +359 -127
  101. package/lib/plan.js.map +1 -1
  102. package/lib/provider.d.ts +26 -9
  103. package/lib/provider.d.ts.map +1 -1
  104. package/lib/provider.js +9 -0
  105. package/lib/provider.js.map +1 -1
  106. package/lib/resource.d.ts +2 -2
  107. package/lib/resource.d.ts.map +1 -1
  108. package/lib/resource.js.map +1 -1
  109. package/lib/state.d.ts +86 -9
  110. package/lib/state.d.ts.map +1 -1
  111. package/lib/state.js +21 -18
  112. package/lib/state.js.map +1 -1
  113. package/lib/tags.d.ts +15 -0
  114. package/lib/tags.d.ts.map +1 -1
  115. package/lib/tags.js +27 -0
  116. package/lib/tags.js.map +1 -1
  117. package/lib/test.d.ts +2 -2
  118. package/lib/test.d.ts.map +1 -1
  119. package/lib/test.js +4 -4
  120. package/lib/test.js.map +1 -1
  121. package/lib/todo.d.ts +3 -0
  122. package/lib/todo.d.ts.map +1 -0
  123. package/lib/todo.js +3 -0
  124. package/lib/todo.js.map +1 -0
  125. package/lib/tsconfig.test.tsbuildinfo +1 -1
  126. package/package.json +2 -2
  127. package/src/apply.ts +758 -374
  128. package/src/aws/dynamodb/table.provider.ts +1 -0
  129. package/src/aws/ec2/index.ts +8 -0
  130. package/src/aws/ec2/internet-gateway.provider.ts +316 -0
  131. package/src/aws/ec2/internet-gateway.ts +79 -0
  132. package/src/aws/ec2/route-table-association.provider.ts +214 -0
  133. package/src/aws/ec2/route-table-association.ts +82 -0
  134. package/src/aws/ec2/route-table.provider.ts +306 -0
  135. package/src/aws/ec2/route-table.ts +175 -0
  136. package/src/aws/ec2/route.provider.ts +213 -0
  137. package/src/aws/ec2/route.ts +192 -0
  138. package/src/aws/ec2/subnet.provider.ts +2 -2
  139. package/src/aws/ec2/vpc.provider.ts +43 -19
  140. package/src/aws/ec2/vpc.ts +2 -0
  141. package/src/aws/index.ts +4 -1
  142. package/src/aws/lambda/function.provider.ts +25 -23
  143. package/src/aws/sqs/queue.provider.ts +3 -2
  144. package/src/cloudflare/kv/namespace.provider.ts +1 -0
  145. package/src/cloudflare/r2/bucket.provider.ts +7 -1
  146. package/src/cloudflare/worker/worker.provider.ts +6 -2
  147. package/src/diff.ts +35 -17
  148. package/src/event.ts +6 -0
  149. package/src/instance-id.ts +16 -0
  150. package/src/output.ts +29 -5
  151. package/src/physical-name.ts +57 -2
  152. package/src/plan.ts +488 -197
  153. package/src/provider.ts +46 -9
  154. package/src/resource.ts +50 -4
  155. package/src/state.ts +150 -35
  156. package/src/tags.ts +31 -0
  157. package/src/test.ts +5 -5
  158. package/src/todo.ts +4 -0
@@ -14042,7 +14042,7 @@ const asEffect = (effect) => Effect.isEffect(effect) ? effect : Effect.succeed(e
14042
14042
 
14043
14043
  //#endregion
14044
14044
  //#region package.json
14045
- var version = "0.3.0";
14045
+ var version = "0.4.0";
14046
14046
 
14047
14047
  //#endregion
14048
14048
  //#region src/resource.ts
@@ -14064,10 +14064,17 @@ const localFs = Layer.effect(State, Effect.gen(function* () {
14064
14064
  const stage$1 = ({ stack, stage: stage$2 }) => path$3.join(stateDir, stack, stage$2);
14065
14065
  const resource = ({ stack, stage: stage$2, resourceId }) => path$3.join(stateDir, stack, stage$2, `${resourceId}.json`);
14066
14066
  const ensure = yield* Effect.cachedFunction((dir) => fs$1.makeDirectory(dir, { recursive: true }));
14067
- return {
14068
- listApps: () => fs$1.readDirectory(stateDir).pipe(recover, Effect.map((files) => files ?? [])),
14067
+ const state = {
14068
+ listStacks: () => fs$1.readDirectory(stateDir).pipe(recover, Effect.map((files) => files ?? [])),
14069
14069
  listStages: (stack) => fs$1.readDirectory(path$3.join(stateDir, stack)).pipe(recover, Effect.map((files) => files ?? [])),
14070
14070
  get: (request) => fs$1.readFile(resource(request)).pipe(Effect.map((file$5) => JSON.parse(file$5.toString())), recover),
14071
+ getReplacedResources: Effect.fnUntraced(function* (request) {
14072
+ return (yield* Effect.all((yield* state.list(request)).map((resourceId) => state.get({
14073
+ stack: request.stack,
14074
+ stage: request.stage,
14075
+ resourceId
14076
+ })))).filter((r) => r?.status === "replaced");
14077
+ }),
14071
14078
  set: (request) => ensure(stage$1(request)).pipe(Effect.flatMap(() => fs$1.writeFileString(resource(request), JSON.stringify(request.value, (k, v) => {
14072
14079
  if (isResource(v)) return {
14073
14080
  id: v.id,
@@ -14080,6 +14087,7 @@ const localFs = Layer.effect(State, Effect.gen(function* () {
14080
14087
  delete: (request) => fs$1.remove(resource(request)).pipe(recover),
14081
14088
  list: (request) => fs$1.readDirectory(stage$1(request)).pipe(recover, Effect.map((files) => files?.map((file$5) => file$5.replace(/\.json$/, "")) ?? []))
14082
14089
  };
14090
+ return state;
14083
14091
  }));
14084
14092
 
14085
14093
  //#endregion
@@ -14088,6 +14096,21 @@ var App$1 = class extends Context.Tag("App")() {};
14088
14096
  const app = (input) => Layer.succeed(App$1, App$1.of(input));
14089
14097
  const make = app;
14090
14098
 
14099
+ //#endregion
14100
+ //#region src/cli/service.ts
14101
+ var CLI = class extends Context.Tag("CLIService")() {};
14102
+
14103
+ //#endregion
14104
+ //#region src/instance-id.ts
14105
+ /**
14106
+ * @returns Hex-encoded instance ID (16 random bytes)
14107
+ */
14108
+ const generateInstanceId = () => Effect.sync(() => {
14109
+ const bytes = new Uint8Array(16);
14110
+ crypto.getRandomValues(bytes);
14111
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
14112
+ });
14113
+
14091
14114
  //#endregion
14092
14115
  //#region src/data.ts
14093
14116
  const isPrimitive = (value) => value === void 0 || value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string" || typeof value === "symbol" || typeof value === "bigint";
@@ -14106,7 +14129,6 @@ const proxy = (self$1) => {
14106
14129
  if (self$1.identifier === "apply") return new ApplyExpr(self$1.expr, args[0]);
14107
14130
  else if (self$1.identifier === "effect") return new EffectExpr(self$1.expr, args[0]);
14108
14131
  }
14109
- throw new Error("Not callable");
14110
14132
  }
14111
14133
  });
14112
14134
  return proxy$1;
@@ -14201,11 +14223,18 @@ const evaluate = (expr, upstream$1) => Effect.gen(function* () {
14201
14223
  stage: stage$1,
14202
14224
  resourceId: expr.resourceId
14203
14225
  }));
14204
- return resource.output;
14226
+ return resource.attr;
14205
14227
  } else if (Array.isArray(expr)) return yield* Effect.all(expr.map((item) => evaluate(item, upstream$1)));
14206
14228
  else if (typeof expr === "object" && expr !== null) return Object.fromEntries(yield* Effect.all(Object.entries(expr).map(([key, value]) => evaluate(value, upstream$1).pipe(Effect.map((value$1) => [key, value$1])))));
14207
14229
  return expr;
14208
14230
  });
14231
+ const hasOutputs = (value) => Object.keys(upstreamAny(value)).length > 0;
14232
+ const upstreamAny = (value) => {
14233
+ if (isExpr(value)) return upstream(value);
14234
+ else if (Array.isArray(value)) return Object.assign({}, ...value.map(resolveUpstream));
14235
+ else if (value && (typeof value === "object" || typeof value === "function")) return Object.assign({}, ...Object.values(value).map((value$1) => resolveUpstream(value$1)));
14236
+ return {};
14237
+ };
14209
14238
  const upstream = (expr) => _upstream(expr);
14210
14239
  const _upstream = (expr) => {
14211
14240
  if (isResourceExpr(expr)) return { [expr.src.id]: expr.src };
@@ -14228,6 +14257,14 @@ const resolveUpstream = (value) => {
14228
14257
  return {};
14229
14258
  };
14230
14259
 
14260
+ //#endregion
14261
+ //#region src/provider.ts
14262
+ const getProviderByType = Effect.fnUntraced(function* (resourceType) {
14263
+ const provider = (yield* Effect.context()).unsafeMap.get(resourceType);
14264
+ if (!provider) return yield* Effect.die(/* @__PURE__ */ new Error(`Provider not found for ${resourceType}`));
14265
+ return provider;
14266
+ });
14267
+
14231
14268
  //#endregion
14232
14269
  //#region src/service.ts
14233
14270
  const isService = (resource) => {
@@ -14236,30 +14273,29 @@ const isService = (resource) => {
14236
14273
 
14237
14274
  //#endregion
14238
14275
  //#region src/plan.ts
14239
- const Node$1 = (node) => ({
14240
- ...node,
14241
- toString() {
14242
- return `${this.action.charAt(0).toUpperCase()}${this.action.slice(1)}(${this.resource})`;
14243
- },
14244
- [Symbol.toStringTag]() {
14245
- return this.toString();
14246
- }
14247
- });
14248
- const plan = (...resources) => Effect.gen(function* () {
14276
+ const plan = (..._resources) => Effect.gen(function* () {
14249
14277
  const state = yield* State;
14278
+ const findResources = (resource, visited) => {
14279
+ if (visited.has(resource.id)) return [];
14280
+ visited.add(resource.id);
14281
+ const upstream$1 = Object.values(upstreamAny(resource.props));
14282
+ return [
14283
+ resource,
14284
+ ...upstream$1,
14285
+ ...upstream$1.flatMap((r) => findResources(r, visited))
14286
+ ];
14287
+ };
14288
+ const resources = _resources.flatMap((r) => findResources(r, /* @__PURE__ */ new Set())).filter((r, i, arr) => arr.findIndex((r2) => r2.id === r.id) === i);
14250
14289
  const app$1 = yield* App$1;
14251
14290
  const resourceIds = yield* state.list({
14252
14291
  stack: app$1.name,
14253
14292
  stage: app$1.stage
14254
14293
  });
14255
- const downstream = (yield* Effect.all(resourceIds.map((id) => state.get({
14294
+ const oldResources = yield* Effect.all(resourceIds.map((id) => state.get({
14256
14295
  stack: app$1.name,
14257
14296
  stage: app$1.stage,
14258
14297
  resourceId: id
14259
- })))).filter((resource) => !!resource?.bindings).flatMap((resource) => resource.bindings.flatMap(({ binding }) => [[binding.capability.resource.id, binding.capability.resource]])).reduce((acc, [id, resourceId]) => ({
14260
- ...acc,
14261
- [id]: [...acc[id] ?? [], resourceId]
14262
- }), {});
14298
+ })));
14263
14299
  const resolvedResources = {};
14264
14300
  const resolveResource = (resourceExpr) => Effect.gen(function* () {
14265
14301
  return yield* resolvedResources[resourceExpr.src.id] ??= yield* Effect.cached(Effect.gen(function* () {
@@ -14271,21 +14307,23 @@ const plan = (...resources) => Effect.gen(function* () {
14271
14307
  stage: app$1.stage,
14272
14308
  resourceId: resource.id
14273
14309
  });
14274
- if (!oldState) return resourceExpr;
14310
+ if (!oldState || oldState.status === "creating") return resourceExpr;
14311
+ const oldProps = oldState.status === "created" || oldState.status === "updated" || oldState.status === "replaced" ? oldState.props : oldState.status === "updating" || oldState.status === "replacing" ? oldState.old.props : oldState.props;
14275
14312
  const diff$1 = yield* provider.diff ? provider.diff({
14276
14313
  id: resource.id,
14277
- olds: oldState.props,
14314
+ olds: oldProps,
14315
+ instanceId: oldState.instanceId,
14278
14316
  news: props,
14279
- output: oldState.output
14317
+ output: oldState.attr
14280
14318
  }) : Effect.succeed(void 0);
14319
+ const stables = [...provider.stables ?? [], ...diff$1?.stables ?? []];
14320
+ const withStables = (output) => stables.length > 0 ? new ResourceExpr(resourceExpr.src, Object.fromEntries(stables.map((stable) => [stable, output?.[stable]]))) : resourceExpr;
14281
14321
  if (diff$1 == null) {
14282
- if (arePropsChanged(oldState, props)) return resourceExpr;
14283
- } else if (diff$1.action === "update") {
14284
- const output = oldState?.output;
14285
- if (diff$1.stables) return new ResourceExpr(resourceExpr.src, Object.fromEntries(diff$1.stables.map((stable) => [stable, output?.[stable]])));
14286
- else return resourceExpr;
14287
- } else if (diff$1.action === "replace") {}
14288
- return oldState?.output;
14322
+ if (arePropsChanged(oldProps, props)) return withStables(oldState?.attr);
14323
+ } else if (diff$1.action === "update") return withStables(oldState?.attr);
14324
+ else if (diff$1.action === "replace") return resourceExpr;
14325
+ if (oldState.status === "created" || oldState.status === "updated" || oldState.status === "replaced") return oldState?.attr;
14326
+ else return resourceExpr;
14289
14327
  }));
14290
14328
  });
14291
14329
  const resolveInput = (input) => Effect.gen(function* () {
@@ -14300,22 +14338,19 @@ const plan = (...resources) => Effect.gen(function* () {
14300
14338
  else if (isPropExpr(expr)) return (yield* resolveOutput(expr.expr))?.[expr.identifier];
14301
14339
  else if (isApplyExpr(expr)) {
14302
14340
  const upstream$1 = yield* resolveOutput(expr.expr);
14303
- return isOutput(upstream$1) ? expr : expr.f(upstream$1);
14341
+ return hasOutputs(upstream$1) ? expr : expr.f(upstream$1);
14304
14342
  } else if (isEffectExpr(expr)) {
14305
14343
  const upstream$1 = yield* resolveOutput(expr.expr);
14306
- return isOutput(upstream$1) ? expr : yield* expr.f(upstream$1);
14344
+ return hasOutputs(upstream$1) ? expr : yield* expr.f(upstream$1);
14307
14345
  } else if (isAllExpr(expr)) return yield* Effect.all(expr.outs.map(resolveOutput));
14308
14346
  return yield* Effect.die(/* @__PURE__ */ new Error("Not implemented yet"));
14309
14347
  });
14310
- const resolveUpstream$1 = (value) => {
14311
- if (isExpr(value)) return upstream(value);
14312
- else if (Array.isArray(value)) return Object.assign({}, ...value.map(resolveUpstream$1));
14313
- else if (value && (typeof value === "object" || typeof value === "function")) return Object.assign({}, ...Object.values(value).map((value$1) => resolveUpstream$1(value$1)));
14314
- return {};
14315
- };
14348
+ const oldDownstreamDependencies = Object.fromEntries(oldResources.filter((resource) => !!resource).map((resource) => [resource.logicalId, resource.downstream]));
14349
+ const newUpstreamDependencies = Object.fromEntries(resources.map((resource) => [resource.id, [...Object.values(upstreamAny(resource.props)).map((r) => r.id), ...isService(resource) ? resource.props.bindings.capabilities.map((cap) => cap.resource.id) : []]]));
14350
+ const newDownstreamDependencies = Object.fromEntries(resources.map((resource) => [resource.id, Object.entries(newUpstreamDependencies).filter(([_, downstream]) => downstream.includes(resource.id)).map(([id]) => id)]));
14316
14351
  const resourceGraph = Object.fromEntries((yield* Effect.all(resources.flatMap((resource) => [
14317
14352
  ...isService(resource) ? resource.props.bindings.capabilities.map((cap) => cap.resource) : [],
14318
- ...Object.values(resolveUpstream$1(resource.props)),
14353
+ ...Object.values(upstreamAny(resource.props)),
14319
14354
  resource
14320
14355
  ]).filter((node, i, arr) => arr.findIndex((n) => n.id === node.id) === i).map(Effect.fn(function* (node) {
14321
14356
  const id = node.id;
@@ -14327,68 +14362,113 @@ const plan = (...resources) => Effect.gen(function* () {
14327
14362
  resourceId: id
14328
14363
  });
14329
14364
  const provider = yield* resource.provider.tag;
14365
+ const downstream = newDownstreamDependencies[id] ?? [];
14330
14366
  const bindings = isService(node) ? yield* diffBindings({
14331
14367
  oldState,
14332
14368
  bindings: node.props.bindings.bindings,
14333
14369
  target: {
14334
14370
  id: node.id,
14335
14371
  props: node.props,
14336
- oldAttr: oldState?.output,
14372
+ oldAttr: oldState?.attr,
14337
14373
  oldProps: oldState?.props
14338
14374
  }
14339
14375
  }) : [];
14340
- if (oldState === void 0 || oldState.status === "creating") return Node$1({
14341
- action: "create",
14342
- news,
14376
+ const Node$1 = (node$1) => ({
14377
+ ...node$1,
14343
14378
  provider,
14344
14379
  resource,
14345
14380
  bindings,
14346
- attributes: void 0
14381
+ downstream
14347
14382
  });
14348
- const diff$1 = provider.diff ? yield* (() => {
14349
- const diff$2 = provider.diff({
14350
- id,
14351
- olds: oldState.props,
14352
- news,
14353
- output: oldState.output
14354
- });
14355
- return Effect.isEffect(diff$2) ? diff$2 : Effect.succeed(diff$2);
14356
- })() : void 0;
14357
- if (!diff$1 && arePropsChanged(oldState, news)) return Node$1({
14383
+ if (oldState === void 0) return Node$1({
14384
+ action: "create",
14385
+ props: news,
14386
+ state: oldState
14387
+ });
14388
+ const oldProps = oldState.props;
14389
+ const diff$1 = yield* asEffect(provider.diff ? provider.diff({
14390
+ id,
14391
+ olds: oldProps,
14392
+ instanceId: oldState.instanceId,
14393
+ output: oldState.attr,
14394
+ news
14395
+ }) : void 0).pipe(Effect.map((diff$2) => diff$2 ?? { action: arePropsChanged(oldProps, news) ? "update" : "noop" }));
14396
+ if (oldState.status === "creating") if (diff$1.action === "noop") return Node$1({
14397
+ action: "create",
14398
+ props: news,
14399
+ state: oldState
14400
+ });
14401
+ else if (diff$1.action === "update") return Node$1({
14402
+ action: "create",
14403
+ props: news,
14404
+ state: oldState
14405
+ });
14406
+ else return Node$1({
14407
+ action: "replace",
14408
+ props: news,
14409
+ deleteFirst: diff$1.deleteFirst ?? false,
14410
+ state: oldState
14411
+ });
14412
+ else if (oldState.status === "updating") if (diff$1.action === "update" || diff$1.action === "noop") return Node$1({
14358
14413
  action: "update",
14359
- olds: oldState.props,
14360
- news,
14361
- output: oldState.output,
14362
- provider,
14363
- resource,
14364
- bindings,
14365
- attributes: void 0
14414
+ props: news,
14415
+ state: oldState
14366
14416
  });
14367
- else if (diff$1?.action === "replace") return Node$1({
14417
+ else return Node$1({
14368
14418
  action: "replace",
14369
- olds: oldState.props,
14370
- news,
14371
- output: oldState.output,
14372
- provider,
14373
- resource,
14374
- bindings,
14375
- attributes: void 0
14419
+ deleteFirst: diff$1.deleteFirst ?? false,
14420
+ props: news,
14421
+ state: oldState
14422
+ });
14423
+ else if (oldState.status === "replacing") if (diff$1.action === "noop") return Node$1({
14424
+ action: "replace",
14425
+ deleteFirst: oldState.deleteFirst,
14426
+ props: news,
14427
+ state: oldState
14376
14428
  });
14377
- else if (diff$1?.action === "update") return Node$1({
14429
+ else if (diff$1.action === "update") return Node$1({
14430
+ action: "replace",
14431
+ deleteFirst: oldState.deleteFirst,
14432
+ props: news,
14433
+ state: oldState
14434
+ });
14435
+ else return yield* Effect.fail(new CannotReplacePartiallyReplacedResource(id));
14436
+ else if (oldState.status === "replaced") if (diff$1.action === "noop") return Node$1({
14437
+ action: "replace",
14438
+ deleteFirst: oldState.deleteFirst,
14439
+ props: news,
14440
+ state: oldState
14441
+ });
14442
+ else if (diff$1.action === "update") return Node$1({
14378
14443
  action: "update",
14379
- olds: oldState.props,
14380
- news,
14381
- output: oldState.output,
14382
- provider,
14383
- resource,
14384
- bindings,
14385
- attributes: void 0
14444
+ props: news,
14445
+ state: oldState
14446
+ });
14447
+ else return yield* Effect.fail(new CannotReplacePartiallyReplacedResource(id));
14448
+ else if (oldState.status === "deleting") if (diff$1.action === "noop" || diff$1.action === "update") return Node$1({
14449
+ action: "create",
14450
+ props: news,
14451
+ state: {
14452
+ ...oldState,
14453
+ status: "creating",
14454
+ props: news
14455
+ }
14456
+ });
14457
+ else return yield* Effect.fail(new CannotReplacePartiallyReplacedResource(id));
14458
+ else if (diff$1.action === "update") return Node$1({
14459
+ action: "update",
14460
+ props: news,
14461
+ state: oldState
14462
+ });
14463
+ else if (diff$1.action === "replace") return Node$1({
14464
+ action: "replace",
14465
+ props: news,
14466
+ state: oldState,
14467
+ deleteFirst: diff$1?.deleteFirst ?? false
14386
14468
  });
14387
14469
  else return Node$1({
14388
14470
  action: "noop",
14389
- resource,
14390
- bindings,
14391
- attributes: void 0
14471
+ state: oldState
14392
14472
  });
14393
14473
  })))).map((update) => [update.resource.id, update]));
14394
14474
  const deletions = Object.fromEntries((yield* Effect.all((yield* state.list({
@@ -14401,29 +14481,22 @@ const plan = (...resources) => Effect.gen(function* () {
14401
14481
  stage: app$1.stage,
14402
14482
  resourceId: id
14403
14483
  });
14404
- const context = yield* Effect.context();
14405
- if (oldState) {
14406
- const provider = context.unsafeMap.get(oldState?.type);
14407
- if (!provider) yield* Effect.die(/* @__PURE__ */ new Error(`Provider not found for ${oldState?.type}`));
14408
- return [id, {
14409
- action: "delete",
14410
- olds: oldState.props,
14411
- output: oldState.output,
14412
- provider,
14413
- attributes: oldState?.output,
14414
- bindings: [],
14415
- resource: {
14416
- id,
14417
- type: oldState.type,
14418
- attr: oldState.output,
14419
- props: oldState.props
14420
- },
14421
- downstream: downstream[id] ?? []
14422
- }];
14423
- }
14484
+ if (oldState) return [id, {
14485
+ action: "delete",
14486
+ state: oldState,
14487
+ bindings: [],
14488
+ provider: yield* getProviderByType(oldState.resourceType),
14489
+ resource: {
14490
+ id,
14491
+ type: oldState.resourceType,
14492
+ attr: oldState.attr,
14493
+ props: oldState.props
14494
+ },
14495
+ downstream: oldDownstreamDependencies[id] ?? []
14496
+ }];
14424
14497
  })))).filter((v) => !!v));
14425
14498
  for (const [resourceId, deletion] of Object.entries(deletions)) {
14426
- const dependencies = deletion.downstream.filter((d) => d in resourceGraph);
14499
+ const dependencies = deletion.state.downstream.filter((d) => d in resourceGraph);
14427
14500
  if (dependencies.length > 0) return yield* Effect.fail(new DeleteResourceHasDownstreamDependencies({
14428
14501
  message: `Resource ${resourceId} has downstream dependencies`,
14429
14502
  resourceId,
@@ -14435,13 +14508,20 @@ const plan = (...resources) => Effect.gen(function* () {
14435
14508
  deletions
14436
14509
  };
14437
14510
  });
14511
+ var CannotReplacePartiallyReplacedResource = class extends Data.TaggedError("CannotReplacePartiallyReplacedResource") {
14512
+ constructor(logicalId) {
14513
+ super({
14514
+ message: `Resource '${logicalId}' did not finish being replaced in a previous deployment and is expected to be replaced again in this deployment. You should revert its properties and try again after a successful deployment.`,
14515
+ logicalId
14516
+ });
14517
+ }
14518
+ };
14438
14519
  var DeleteResourceHasDownstreamDependencies = class extends Data.TaggedError("DeleteResourceHasDownstreamDependencies") {};
14439
- const arePropsChanged = (oldState, newProps) => {
14440
- return JSON.stringify(omit(oldState?.props ?? {}, "bindings")) !== JSON.stringify(omit(newProps ?? {}, "bindings"));
14520
+ const arePropsChanged = (oldProps, newProps) => {
14521
+ return hasOutputs(newProps) || JSON.stringify(omit(oldProps ?? {}, "bindings")) !== JSON.stringify(omit(newProps ?? {}, "bindings"));
14441
14522
  };
14442
14523
  const diffBindings = Effect.fn(function* ({ oldState, bindings, target }) {
14443
14524
  const oldBindings = oldState?.bindings;
14444
- new Set(oldBindings?.map(({ binding }) => binding.capability.sid));
14445
14525
  const diffBinding = Effect.fn(function* (binding) {
14446
14526
  const cap = binding.capability;
14447
14527
  const sid = cap.sid ?? `${cap.action}:${cap.resource.ID}`;
@@ -14484,7 +14564,7 @@ const isBindingDiff = Effect.fn(function* ({ target, oldBinding: { binding: oldB
14484
14564
  id: oldCap.resource.id,
14485
14565
  props: newCap.resource.props,
14486
14566
  oldProps: oldState?.props,
14487
- oldAttr: oldState?.output
14567
+ oldAttr: oldState?.attr
14488
14568
  },
14489
14569
  props: newBinding.props,
14490
14570
  attr: oldBinding.attr,
@@ -14495,18 +14575,20 @@ const isBindingDiff = Effect.fn(function* ({ target, oldBinding: { binding: oldB
14495
14575
  return { action: oldBinding.capability.action !== newBinding.capability.action || oldBinding.capability?.resource?.id !== newBinding.capability?.resource?.id ? "update" : "noop" };
14496
14576
  });
14497
14577
 
14498
- //#endregion
14499
- //#region src/cli/service.ts
14500
- var CLI = class extends Context.Tag("CLIService")() {};
14501
-
14502
14578
  //#endregion
14503
14579
  //#region src/apply.ts
14504
14580
  const applyPlan = (plan$1) => Effect.gen(function* () {
14581
+ const session = yield* (yield* CLI).startApplySession(plan$1);
14582
+ const resources = yield* expandAndPivot(plan$1, session);
14583
+ yield* collectGarbage(plan$1, session);
14584
+ yield* session.done();
14585
+ if (Object.keys(plan$1.resources).length === 0) return;
14586
+ return resources;
14587
+ });
14588
+ const expandAndPivot = Effect.fnUntraced(function* (plan$1, session) {
14505
14589
  const state = yield* State;
14506
14590
  const app$1 = yield* App$1;
14507
14591
  const outputs = {};
14508
- const session = yield* (yield* CLI).startApplySession(plan$1);
14509
- const { emit: emit$1, done } = session;
14510
14592
  const resolveUpstream$1 = Effect.fn(function* (resourceId) {
14511
14593
  const upstreamNode = plan$1.resources[resourceId];
14512
14594
  return {
@@ -14574,19 +14656,12 @@ const applyPlan = (plan$1) => Effect.gen(function* () {
14574
14656
  return oldBindingOutput;
14575
14657
  })));
14576
14658
  const apply = (node) => Effect.gen(function* () {
14577
- const saveState = ({ output, bindings = node.bindings, news }) => state.set({
14659
+ const commit = (value) => state.set({
14578
14660
  stack: app$1.name,
14579
14661
  stage: app$1.stage,
14580
14662
  resourceId: node.resource.id,
14581
- value: {
14582
- id: node.resource.id,
14583
- type: node.resource.type,
14584
- status: node.action === "create" ? "created" : "updated",
14585
- props: news,
14586
- output,
14587
- bindings
14588
- }
14589
- }).pipe(Effect.map(() => output));
14663
+ value
14664
+ });
14590
14665
  const id = node.resource.id;
14591
14666
  const resource = node.resource;
14592
14667
  const scopedSession = {
@@ -14598,38 +14673,95 @@ const applyPlan = (plan$1) => Effect.gen(function* () {
14598
14673
  })
14599
14674
  };
14600
14675
  return yield* outputs[id] ??= yield* Effect.cached(Effect.gen(function* () {
14601
- const report = (status) => emit$1({
14676
+ const report = (status) => session.emit({
14602
14677
  kind: "status-change",
14603
14678
  id,
14604
14679
  type: node.resource.type,
14605
14680
  status
14606
14681
  });
14607
- const createOrUpdate = Effect.fn(function* ({ node: node$1, attr, phase }) {
14608
- const upstream$1 = Object.fromEntries(yield* Effect.all(Object.entries(resolveUpstream(node$1.news)).map(([id$1]) => resolveUpstream$1(id$1).pipe(Effect.map(({ upstreamAttr }) => [id$1, upstreamAttr])))));
14609
- const news = yield* evaluate(node$1.news, upstream$1);
14610
- yield* report(phase === "create" ? "creating" : "updating");
14682
+ if (node.action === "noop") return node.state.attr;
14683
+ const upstream$1 = Object.fromEntries(yield* Effect.all(Object.entries(resolveUpstream(node.props)).map(([id$1]) => resolveUpstream$1(id$1).pipe(Effect.map(({ upstreamAttr }) => [id$1, upstreamAttr])))));
14684
+ const instanceId = yield* Effect.gen(function* () {
14685
+ if (node.action === "create" && !node.state?.instanceId) {
14686
+ const instanceId$1 = yield* generateInstanceId();
14687
+ yield* commit({
14688
+ status: "creating",
14689
+ instanceId: instanceId$1,
14690
+ logicalId: id,
14691
+ downstream: node.downstream,
14692
+ props: node.props,
14693
+ providerVersion: node.provider.version ?? 0,
14694
+ resourceType: node.resource.type,
14695
+ bindings: node.bindings
14696
+ });
14697
+ return instanceId$1;
14698
+ } else if (node.action === "replace") {
14699
+ if (node.state.status === "replaced" || node.state.status === "replacing") return node.state.instanceId;
14700
+ const instanceId$1 = yield* generateInstanceId();
14701
+ yield* commit({
14702
+ status: "replacing",
14703
+ instanceId: instanceId$1,
14704
+ logicalId: id,
14705
+ downstream: node.downstream,
14706
+ props: node.props,
14707
+ providerVersion: node.provider.version ?? 0,
14708
+ resourceType: node.resource.type,
14709
+ bindings: node.bindings,
14710
+ old: node.state,
14711
+ deleteFirst: node.deleteFirst
14712
+ });
14713
+ return instanceId$1;
14714
+ } else if (node.state?.instanceId) return node.state.instanceId;
14715
+ return yield* Effect.dieMessage(`Instance ID not found for resource '${id}' and action is '${node.action}'`);
14716
+ });
14717
+ if (node.action === "create") {
14718
+ const news = yield* evaluate(node.props, upstream$1);
14719
+ const checkpoint = (attr$1) => commit({
14720
+ status: "creating",
14721
+ logicalId: id,
14722
+ instanceId,
14723
+ resourceType: node.resource.type,
14724
+ props: news,
14725
+ attr: attr$1,
14726
+ providerVersion: node.provider.version ?? 0,
14727
+ bindings: node.bindings,
14728
+ downstream: node.downstream
14729
+ });
14730
+ if (!node.state) yield* checkpoint(void 0);
14731
+ let attr;
14732
+ if (node.action === "create" && node.provider.precreate && node.state?.attr === void 0) {
14733
+ yield* report("pre-creating");
14734
+ attr = yield* node.provider.precreate({
14735
+ id,
14736
+ news: node.props,
14737
+ session: scopedSession,
14738
+ instanceId
14739
+ });
14740
+ yield* checkpoint(attr);
14741
+ }
14742
+ yield* report("attaching");
14611
14743
  let bindingOutputs = yield* attachBindings({
14612
14744
  resource,
14613
- bindings: node$1.bindings,
14745
+ bindings: node.bindings,
14614
14746
  target: {
14615
14747
  id,
14616
14748
  props: news,
14617
14749
  attr
14618
14750
  }
14619
14751
  });
14620
- const output = yield* (phase === "create" ? node$1.provider.create : node$1.provider.update)({
14752
+ yield* report("creating");
14753
+ attr = yield* node.provider.create({
14621
14754
  id,
14622
14755
  news,
14756
+ instanceId,
14623
14757
  bindings: bindingOutputs,
14624
- session: scopedSession,
14625
- ...node$1.action === "update" ? {
14626
- output: node$1.output,
14627
- olds: node$1.olds
14628
- } : {}
14629
- }).pipe(Effect.tap(() => report(phase === "create" ? "created" : "updated")));
14758
+ session: scopedSession
14759
+ });
14760
+ yield* checkpoint(attr);
14761
+ yield* report("post-attach");
14630
14762
  bindingOutputs = yield* postAttachBindings({
14631
14763
  resource,
14632
- bindings: node$1.bindings,
14764
+ bindings: node.bindings,
14633
14765
  bindingOutputs,
14634
14766
  target: {
14635
14767
  id,
@@ -14637,107 +14769,291 @@ const applyPlan = (plan$1) => Effect.gen(function* () {
14637
14769
  attr
14638
14770
  }
14639
14771
  });
14640
- yield* saveState({
14641
- news,
14642
- output,
14643
- bindings: node$1.bindings.map((binding, i) => ({
14772
+ yield* commit({
14773
+ status: "created",
14774
+ logicalId: id,
14775
+ instanceId,
14776
+ resourceType: node.resource.type,
14777
+ props: news,
14778
+ attr,
14779
+ bindings: node.bindings.map((binding, i) => ({
14644
14780
  ...binding,
14645
14781
  attr: bindingOutputs[i]
14646
- }))
14647
- });
14648
- return output;
14649
- });
14650
- if (node.action === "noop") return (yield* state.get({
14651
- stack: app$1.name,
14652
- stage: app$1.stage,
14653
- resourceId: id
14654
- }))?.output;
14655
- else if (node.action === "create") {
14656
- let attr;
14657
- if (node.provider.precreate) {
14658
- yield* Effect.logDebug("precreate", id);
14659
- attr = yield* node.provider.precreate({
14660
- id,
14661
- news: node.news,
14662
- session: scopedSession
14663
- });
14664
- }
14665
- yield* Effect.logDebug("create", id);
14666
- return yield* createOrUpdate({
14667
- node,
14668
- attr,
14669
- phase: "create"
14782
+ })),
14783
+ providerVersion: node.provider.version ?? 0,
14784
+ downstream: node.downstream
14670
14785
  });
14786
+ yield* report("created");
14787
+ return attr;
14671
14788
  } else if (node.action === "update") {
14672
- yield* Effect.logDebug("update", id);
14673
- return yield* createOrUpdate({
14674
- node,
14675
- attr: node.attributes,
14676
- phase: "update"
14789
+ const upstream$2 = Object.fromEntries(yield* Effect.all(Object.entries(resolveUpstream(node.props)).map(([id$1]) => resolveUpstream$1(id$1).pipe(Effect.map(({ upstreamAttr }) => [id$1, upstreamAttr])))));
14790
+ const news = yield* evaluate(node.props, upstream$2);
14791
+ const checkpoint = (attr$1) => {
14792
+ if (node.state.status === "replaced") return commit({
14793
+ ...node.state,
14794
+ attr: attr$1,
14795
+ props: news
14796
+ });
14797
+ else return commit({
14798
+ status: "updating",
14799
+ logicalId: id,
14800
+ instanceId,
14801
+ resourceType: node.resource.type,
14802
+ props: news,
14803
+ attr: attr$1,
14804
+ providerVersion: node.provider.version ?? 0,
14805
+ bindings: node.bindings,
14806
+ downstream: node.downstream,
14807
+ old: node.state.status === "updating" ? node.state.old : node.state
14808
+ });
14809
+ };
14810
+ yield* checkpoint(node.state.attr);
14811
+ yield* report("attaching");
14812
+ let bindingOutputs = yield* attachBindings({
14813
+ resource,
14814
+ bindings: node.bindings,
14815
+ target: {
14816
+ id,
14817
+ props: news,
14818
+ attr: node.state.attr
14819
+ }
14677
14820
  });
14678
- } else if (node.action === "delete") {
14679
- yield* Effect.logDebug("delete", id);
14680
- yield* Effect.all(node.downstream.map((dep) => dep in plan$1.resources ? apply(plan$1.resources[dep]) : Effect.void));
14681
- yield* report("deleting");
14682
- return yield* node.provider.delete({
14821
+ yield* report("updating");
14822
+ const attr = yield* node.provider.update({
14683
14823
  id,
14684
- olds: node.olds,
14685
- output: node.output,
14824
+ news,
14825
+ instanceId,
14826
+ bindings: bindingOutputs,
14686
14827
  session: scopedSession,
14687
- bindings: []
14688
- }).pipe(Effect.flatMap(() => state.delete({
14689
- stack: app$1.name,
14690
- stage: app$1.stage,
14691
- resourceId: id
14692
- })), Effect.tap(() => report("deleted")));
14828
+ olds: node.state.status === "created" || node.state.status === "updated" || node.state.status === "replaced" ? node.state.props : node.state.old.props,
14829
+ output: node.state.attr
14830
+ });
14831
+ yield* checkpoint(attr);
14832
+ yield* report("post-attach");
14833
+ bindingOutputs = yield* postAttachBindings({
14834
+ resource,
14835
+ bindings: node.bindings,
14836
+ bindingOutputs,
14837
+ target: {
14838
+ id,
14839
+ props: news,
14840
+ attr
14841
+ }
14842
+ });
14843
+ if (node.state.status === "replaced") yield* commit({
14844
+ ...node.state,
14845
+ attr,
14846
+ props: news
14847
+ });
14848
+ else yield* commit({
14849
+ status: "updated",
14850
+ logicalId: id,
14851
+ instanceId,
14852
+ resourceType: node.resource.type,
14853
+ props: news,
14854
+ attr,
14855
+ bindings: node.bindings.map((binding, i) => ({
14856
+ ...binding,
14857
+ attr: bindingOutputs[i]
14858
+ })),
14859
+ providerVersion: node.provider.version ?? 0,
14860
+ downstream: node.downstream
14861
+ });
14862
+ yield* report("updated");
14863
+ return attr;
14693
14864
  } else if (node.action === "replace") {
14694
- const destroy = Effect.gen(function* () {
14695
- yield* report("deleting");
14696
- return yield* node.provider.delete({
14865
+ if (node.state.status === "replaced") return node.state.attr;
14866
+ let state$1;
14867
+ if (node.state.status !== "replacing") yield* commit(state$1 = {
14868
+ status: "replacing",
14869
+ logicalId: id,
14870
+ instanceId,
14871
+ resourceType: node.resource.type,
14872
+ props: node.props,
14873
+ attr: node.state.attr,
14874
+ providerVersion: node.provider.version ?? 0,
14875
+ deleteFirst: node.deleteFirst,
14876
+ old: node.state,
14877
+ downstream: node.downstream
14878
+ });
14879
+ else state$1 = node.state;
14880
+ const upstream$2 = Object.fromEntries(yield* Effect.all(Object.entries(resolveUpstream(node.props)).map(([id$1]) => resolveUpstream$1(id$1).pipe(Effect.map(({ upstreamAttr }) => [id$1, upstreamAttr])))));
14881
+ const news = yield* evaluate(node.props, upstream$2);
14882
+ const checkpoint = ({ status, attr: attr$1, bindings }) => commit({
14883
+ status,
14884
+ logicalId: id,
14885
+ instanceId,
14886
+ resourceType: node.resource.type,
14887
+ props: news,
14888
+ attr: attr$1,
14889
+ providerVersion: node.provider.version ?? 0,
14890
+ bindings: bindings ?? node.bindings,
14891
+ downstream: node.downstream,
14892
+ old: state$1.old,
14893
+ deleteFirst: node.deleteFirst
14894
+ });
14895
+ let attr;
14896
+ if (node.provider.precreate && node.state?.attr === void 0) {
14897
+ yield* report("pre-creating");
14898
+ attr = yield* node.provider.precreate({
14697
14899
  id,
14698
- olds: node.olds,
14699
- output: node.output,
14900
+ news: node.props,
14700
14901
  session: scopedSession,
14701
- bindings: []
14902
+ instanceId
14702
14903
  });
14904
+ yield* checkpoint({
14905
+ status: "replacing",
14906
+ attr
14907
+ });
14908
+ }
14909
+ yield* report("attaching");
14910
+ let bindingOutputs = yield* attachBindings({
14911
+ resource,
14912
+ bindings: node.bindings,
14913
+ target: {
14914
+ id,
14915
+ props: news,
14916
+ attr
14917
+ }
14918
+ });
14919
+ yield* report("creating replacement");
14920
+ attr = yield* node.provider.create({
14921
+ id,
14922
+ news,
14923
+ instanceId,
14924
+ bindings: bindingOutputs,
14925
+ session: scopedSession
14926
+ });
14927
+ yield* checkpoint({
14928
+ status: "replacing",
14929
+ attr
14703
14930
  });
14704
- const create$1 = Effect.gen(function* () {
14705
- yield* report("creating");
14706
- return yield* node.provider.create({
14931
+ yield* report("post-attach");
14932
+ bindingOutputs = yield* postAttachBindings({
14933
+ resource,
14934
+ bindings: node.bindings,
14935
+ bindingOutputs,
14936
+ target: {
14707
14937
  id,
14708
- news: node.news,
14709
- bindings: yield* attachBindings({
14710
- resource,
14711
- bindings: node.bindings,
14712
- target: {
14713
- id,
14714
- props: node.news,
14715
- attr: node.attributes
14716
- }
14717
- }),
14718
- session: scopedSession
14719
- }).pipe(Effect.tap((output) => saveState({
14720
- news: node.news,
14721
- output
14722
- })));
14938
+ props: news,
14939
+ attr
14940
+ }
14723
14941
  });
14724
- if (!node.deleteFirst) {
14725
- yield* destroy;
14726
- return outputs;
14727
- } else {
14728
- yield* destroy;
14729
- return yield* create$1;
14730
- }
14942
+ yield* checkpoint({
14943
+ status: "replaced",
14944
+ attr,
14945
+ bindings: node.bindings.map((binding, i) => ({
14946
+ ...binding,
14947
+ attr: bindingOutputs[i]
14948
+ }))
14949
+ });
14950
+ yield* report("created");
14951
+ return attr;
14731
14952
  }
14953
+ return yield* Effect.dieMessage(`Unknown action: ${node.action}`);
14732
14954
  }));
14733
14955
  });
14734
- const nodes = [...Object.entries(plan$1.resources), ...Object.entries(plan$1.deletions)];
14735
- const resources = Object.fromEntries(yield* Effect.all(nodes.map(Effect.fn(function* ([id, node]) {
14956
+ return Object.fromEntries(yield* Effect.all(Object.entries(plan$1.resources).map(Effect.fn(function* ([id, node]) {
14736
14957
  return [id, yield* apply(node)];
14737
14958
  }))));
14738
- yield* done();
14739
- if (Object.keys(plan$1.resources).length === 0) return;
14740
- return resources;
14959
+ });
14960
+ const collectGarbage = Effect.fnUntraced(function* (plan$1, session) {
14961
+ const state = yield* State;
14962
+ const app$1 = yield* App$1;
14963
+ const deletions = {};
14964
+ const replacedResources = yield* state.getReplacedResources({
14965
+ stack: app$1.name,
14966
+ stage: app$1.stage
14967
+ });
14968
+ const deletionGraph = {
14969
+ ...plan$1.deletions,
14970
+ ...Object.fromEntries(replacedResources.map((replaced) => [replaced.logicalId, replaced]))
14971
+ };
14972
+ const deleteResource = Effect.fnUntraced(function* (node) {
14973
+ const isDeleteNode = (node$1) => "action" in node$1;
14974
+ const { logicalId, resourceType, instanceId, downstream, props, attr, provider } = isDeleteNode(node) ? {
14975
+ logicalId: node.resource.id,
14976
+ resourceType: node.resource.type,
14977
+ instanceId: node.state.instanceId,
14978
+ downstream: node.downstream,
14979
+ props: node.state.props,
14980
+ attr: node.state.attr,
14981
+ provider: node.provider
14982
+ } : {
14983
+ logicalId: node.logicalId,
14984
+ resourceType: node.old.resourceType,
14985
+ instanceId: node.old.instanceId,
14986
+ downstream: node.old.downstream,
14987
+ props: node.old.props,
14988
+ attr: node.old.attr,
14989
+ provider: yield* getProviderByType(node.old.resourceType)
14990
+ };
14991
+ const commit = (value) => state.set({
14992
+ stack: app$1.name,
14993
+ stage: app$1.stage,
14994
+ resourceId: logicalId,
14995
+ value
14996
+ });
14997
+ const report = (status) => session.emit({
14998
+ kind: "status-change",
14999
+ id: logicalId,
15000
+ type: resourceType,
15001
+ status
15002
+ });
15003
+ const scopedSession = {
15004
+ ...session,
15005
+ note: (note) => session.emit({
15006
+ id: logicalId,
15007
+ kind: "annotate",
15008
+ message: note
15009
+ })
15010
+ };
15011
+ return yield* deletions[logicalId] ??= yield* Effect.cached(Effect.gen(function* () {
15012
+ yield* Effect.all(downstream.map((dep) => dep in deletionGraph ? deleteResource(deletionGraph[dep]) : Effect.void));
15013
+ yield* report("deleting");
15014
+ if (isDeleteNode(node)) yield* commit({
15015
+ status: "deleting",
15016
+ logicalId,
15017
+ instanceId,
15018
+ resourceType,
15019
+ props,
15020
+ attr,
15021
+ downstream,
15022
+ providerVersion: provider.version ?? 0,
15023
+ bindings: node.bindings
15024
+ });
15025
+ yield* provider.delete({
15026
+ id: logicalId,
15027
+ instanceId,
15028
+ olds: props,
15029
+ output: attr,
15030
+ session: scopedSession,
15031
+ bindings: []
15032
+ });
15033
+ if (isDeleteNode(node)) {
15034
+ yield* state.delete({
15035
+ stack: app$1.name,
15036
+ stage: app$1.stage,
15037
+ resourceId: logicalId
15038
+ });
15039
+ yield* report("deleted");
15040
+ } else {
15041
+ yield* commit({
15042
+ status: "created",
15043
+ logicalId,
15044
+ instanceId,
15045
+ resourceType,
15046
+ props: node.props,
15047
+ attr: node.attr,
15048
+ providerVersion: provider.version ?? 0,
15049
+ downstream: node.downstream,
15050
+ bindings: node.bindings
15051
+ });
15052
+ yield* report("replaced");
15053
+ }
15054
+ }));
15055
+ });
15056
+ yield* Effect.all(Object.values(deletionGraph).filter((node) => node !== void 0).map(deleteResource));
14741
15057
  });
14742
15058
 
14743
15059
  //#endregion
@@ -40518,7 +40834,7 @@ var require_backend = /* @__PURE__ */ __commonJS({ "../node_modules/react-devtoo
40518
40834
  }),
40519
40835
  695: ((module$1, __unused_webpack_exports, __webpack_require__$1) => {
40520
40836
  module$1.exports = Yallist;
40521
- Yallist.Node = Node$2;
40837
+ Yallist.Node = Node$1;
40522
40838
  Yallist.create = Yallist;
40523
40839
  function Yallist(list$3) {
40524
40840
  var self$1 = this;
@@ -40731,24 +41047,24 @@ var require_backend = /* @__PURE__ */ __commonJS({ "../node_modules/react-devtoo
40731
41047
  return this;
40732
41048
  };
40733
41049
  function insert(self$1, node, value) {
40734
- var inserted = node === self$1.head ? new Node$2(value, null, node, self$1) : new Node$2(value, node, node.next, self$1);
41050
+ var inserted = node === self$1.head ? new Node$1(value, null, node, self$1) : new Node$1(value, node, node.next, self$1);
40735
41051
  if (inserted.next === null) self$1.tail = inserted;
40736
41052
  if (inserted.prev === null) self$1.head = inserted;
40737
41053
  self$1.length++;
40738
41054
  return inserted;
40739
41055
  }
40740
41056
  function push$2(self$1, item) {
40741
- self$1.tail = new Node$2(item, self$1.tail, null, self$1);
41057
+ self$1.tail = new Node$1(item, self$1.tail, null, self$1);
40742
41058
  if (!self$1.head) self$1.head = self$1.tail;
40743
41059
  self$1.length++;
40744
41060
  }
40745
41061
  function unshift(self$1, item) {
40746
- self$1.head = new Node$2(item, null, self$1.head, self$1);
41062
+ self$1.head = new Node$1(item, null, self$1.head, self$1);
40747
41063
  if (!self$1.tail) self$1.tail = self$1.head;
40748
41064
  self$1.length++;
40749
41065
  }
40750
- function Node$2(value, prev, next, list$3) {
40751
- if (!(this instanceof Node$2)) return new Node$2(value, prev, next, list$3);
41066
+ function Node$1(value, prev, next, list$3) {
41067
+ if (!(this instanceof Node$1)) return new Node$1(value, prev, next, list$3);
40752
41068
  this.list = list$3;
40753
41069
  this.value = value;
40754
41070
  if (prev) {