alchemy-effect 0.3.0 → 0.5.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.
- package/bin/alchemy-effect.js +618 -260
- package/bin/alchemy-effect.js.map +1 -1
- package/lib/apply.d.ts +4 -4
- package/lib/apply.d.ts.map +1 -1
- package/lib/apply.js +444 -159
- package/lib/apply.js.map +1 -1
- package/lib/aws/dynamodb/table.provider.d.ts.map +1 -1
- package/lib/aws/dynamodb/table.provider.js +11 -2
- package/lib/aws/dynamodb/table.provider.js.map +1 -1
- package/lib/aws/ec2/index.d.ts +8 -0
- package/lib/aws/ec2/index.d.ts.map +1 -1
- package/lib/aws/ec2/index.js +8 -0
- package/lib/aws/ec2/index.js.map +1 -1
- package/lib/aws/ec2/internet-gateway.d.ts +65 -0
- package/lib/aws/ec2/internet-gateway.d.ts.map +1 -0
- package/lib/aws/ec2/internet-gateway.js +4 -0
- package/lib/aws/ec2/internet-gateway.js.map +1 -0
- package/lib/aws/ec2/internet-gateway.provider.d.ts +6 -0
- package/lib/aws/ec2/internet-gateway.provider.d.ts.map +1 -0
- package/lib/aws/ec2/internet-gateway.provider.js +193 -0
- package/lib/aws/ec2/internet-gateway.provider.js.map +1 -0
- package/lib/aws/ec2/route-table-association.d.ts +63 -0
- package/lib/aws/ec2/route-table-association.d.ts.map +1 -0
- package/lib/aws/ec2/route-table-association.js +4 -0
- package/lib/aws/ec2/route-table-association.js.map +1 -0
- package/lib/aws/ec2/route-table-association.provider.d.ts +4 -0
- package/lib/aws/ec2/route-table-association.provider.d.ts.map +1 -0
- package/lib/aws/ec2/route-table-association.provider.js +121 -0
- package/lib/aws/ec2/route-table-association.provider.js.map +1 -0
- package/lib/aws/ec2/route-table.d.ts +159 -0
- package/lib/aws/ec2/route-table.d.ts.map +1 -0
- package/lib/aws/ec2/route-table.js +4 -0
- package/lib/aws/ec2/route-table.js.map +1 -0
- package/lib/aws/ec2/route-table.provider.d.ts +6 -0
- package/lib/aws/ec2/route-table.provider.d.ts.map +1 -0
- package/lib/aws/ec2/route-table.provider.js +213 -0
- package/lib/aws/ec2/route-table.provider.js.map +1 -0
- package/lib/aws/ec2/route.d.ts +155 -0
- package/lib/aws/ec2/route.d.ts.map +1 -0
- package/lib/aws/ec2/route.js +3 -0
- package/lib/aws/ec2/route.js.map +1 -0
- package/lib/aws/ec2/route.provider.d.ts +4 -0
- package/lib/aws/ec2/route.provider.d.ts.map +1 -0
- package/lib/aws/ec2/route.provider.js +166 -0
- package/lib/aws/ec2/route.provider.js.map +1 -0
- package/lib/aws/ec2/subnet.provider.d.ts.map +1 -1
- package/lib/aws/ec2/subnet.provider.js +1 -1
- package/lib/aws/ec2/subnet.provider.js.map +1 -1
- package/lib/aws/ec2/vpc.d.ts +1 -0
- package/lib/aws/ec2/vpc.d.ts.map +1 -1
- package/lib/aws/ec2/vpc.provider.d.ts.map +1 -1
- package/lib/aws/ec2/vpc.provider.js +32 -10
- package/lib/aws/ec2/vpc.provider.js.map +1 -1
- package/lib/aws/index.d.ts +2 -3
- package/lib/aws/index.d.ts.map +1 -1
- package/lib/aws/index.js +2 -1
- package/lib/aws/index.js.map +1 -1
- package/lib/aws/lambda/function.provider.d.ts +2 -2
- package/lib/aws/lambda/function.provider.d.ts.map +1 -1
- package/lib/aws/lambda/function.provider.js +39 -42
- package/lib/aws/lambda/function.provider.js.map +1 -1
- package/lib/aws/sqs/queue.provider.d.ts +3 -4
- package/lib/aws/sqs/queue.provider.d.ts.map +1 -1
- package/lib/aws/sqs/queue.provider.js +17 -9
- package/lib/aws/sqs/queue.provider.js.map +1 -1
- package/lib/cli/index.d.ts +183 -100
- package/lib/cli/index.d.ts.map +1 -1
- package/lib/cloudflare/kv/namespace.client.d.ts +1 -1
- package/lib/cloudflare/kv/namespace.provider.d.ts.map +1 -1
- package/lib/cloudflare/kv/namespace.provider.js +12 -6
- package/lib/cloudflare/kv/namespace.provider.js.map +1 -1
- package/lib/cloudflare/r2/bucket.binding.js +1 -1
- package/lib/cloudflare/r2/bucket.binding.js.map +1 -1
- package/lib/cloudflare/r2/bucket.d.ts +1 -1
- package/lib/cloudflare/r2/bucket.d.ts.map +1 -1
- package/lib/cloudflare/r2/bucket.provider.d.ts +1 -2
- package/lib/cloudflare/r2/bucket.provider.d.ts.map +1 -1
- package/lib/cloudflare/r2/bucket.provider.js +23 -12
- package/lib/cloudflare/r2/bucket.provider.js.map +1 -1
- package/lib/cloudflare/worker/worker.d.ts +2 -2
- package/lib/cloudflare/worker/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker/worker.provider.d.ts +2 -3
- package/lib/cloudflare/worker/worker.provider.d.ts.map +1 -1
- package/lib/cloudflare/worker/worker.provider.js +44 -13
- package/lib/cloudflare/worker/worker.provider.js.map +1 -1
- package/lib/diff.d.ts +8 -6
- package/lib/diff.d.ts.map +1 -1
- package/lib/diff.js +13 -0
- package/lib/diff.js.map +1 -1
- package/lib/event.d.ts +1 -1
- package/lib/event.d.ts.map +1 -1
- package/lib/instance-id.d.ts +12 -0
- package/lib/instance-id.d.ts.map +1 -0
- package/lib/instance-id.js +16 -0
- package/lib/instance-id.js.map +1 -0
- package/lib/output.d.ts +4 -2
- package/lib/output.d.ts.map +1 -1
- package/lib/output.js +18 -4
- package/lib/output.js.map +1 -1
- package/lib/physical-name.d.ts +25 -1
- package/lib/physical-name.d.ts.map +1 -1
- package/lib/physical-name.js +50 -2
- package/lib/physical-name.js.map +1 -1
- package/lib/plan.d.ts +49 -42
- package/lib/plan.d.ts.map +1 -1
- package/lib/plan.js +417 -137
- package/lib/plan.js.map +1 -1
- package/lib/provider.d.ts +26 -10
- package/lib/provider.d.ts.map +1 -1
- package/lib/provider.js +9 -0
- package/lib/provider.js.map +1 -1
- package/lib/resource.d.ts +3 -2
- package/lib/resource.d.ts.map +1 -1
- package/lib/resource.js.map +1 -1
- package/lib/state.d.ts +86 -9
- package/lib/state.d.ts.map +1 -1
- package/lib/state.js +21 -18
- package/lib/state.js.map +1 -1
- package/lib/tags.d.ts +15 -0
- package/lib/tags.d.ts.map +1 -1
- package/lib/tags.js +27 -0
- package/lib/tags.js.map +1 -1
- package/lib/test.d.ts +2 -2
- package/lib/test.d.ts.map +1 -1
- package/lib/test.js +4 -4
- package/lib/test.js.map +1 -1
- package/lib/todo.d.ts +3 -0
- package/lib/todo.d.ts.map +1 -0
- package/lib/todo.js +3 -0
- package/lib/todo.js.map +1 -0
- package/lib/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/apply.ts +742 -348
- package/src/aws/dynamodb/table.provider.ts +16 -4
- package/src/aws/ec2/index.ts +8 -0
- package/src/aws/ec2/internet-gateway.provider.ts +316 -0
- package/src/aws/ec2/internet-gateway.ts +79 -0
- package/src/aws/ec2/route-table-association.provider.ts +214 -0
- package/src/aws/ec2/route-table-association.ts +82 -0
- package/src/aws/ec2/route-table.provider.ts +306 -0
- package/src/aws/ec2/route-table.ts +175 -0
- package/src/aws/ec2/route.provider.ts +213 -0
- package/src/aws/ec2/route.ts +192 -0
- package/src/aws/ec2/subnet.provider.ts +2 -2
- package/src/aws/ec2/vpc.provider.ts +36 -11
- package/src/aws/ec2/vpc.ts +2 -0
- package/src/aws/index.ts +4 -1
- package/src/aws/lambda/function.provider.ts +66 -53
- package/src/aws/sqs/queue.provider.ts +18 -11
- package/src/cloudflare/kv/namespace.provider.ts +19 -14
- package/src/cloudflare/r2/bucket.binding.ts +1 -1
- package/src/cloudflare/r2/bucket.provider.ts +34 -24
- package/src/cloudflare/r2/bucket.ts +1 -1
- package/src/cloudflare/worker/worker.provider.ts +43 -13
- package/src/cloudflare/worker/worker.ts +2 -2
- package/src/diff.ts +35 -17
- package/src/event.ts +6 -0
- package/src/instance-id.ts +20 -0
- package/src/output.ts +29 -5
- package/src/physical-name.ts +79 -2
- package/src/plan.ts +566 -214
- package/src/provider.ts +46 -10
- package/src/resource.ts +65 -8
- package/src/state.ts +150 -35
- package/src/tags.ts +31 -0
- package/src/test.ts +5 -5
- package/src/todo.ts +4 -0
package/lib/apply.js
CHANGED
|
@@ -1,20 +1,36 @@
|
|
|
1
1
|
import * as Context from "effect/Context";
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
|
+
import { App } from "./app.js";
|
|
4
|
+
import { CLI, } from "./cli/service.js";
|
|
5
|
+
import { generateInstanceId, InstanceId } from "./instance-id.js";
|
|
3
6
|
import * as Output from "./output.js";
|
|
4
7
|
import { plan, } from "./plan.js";
|
|
5
|
-
import { State } from "./state.js";
|
|
6
|
-
import { App } from "./app.js";
|
|
8
|
+
import { State, StateStoreError, } from "./state.js";
|
|
7
9
|
import { asEffect } from "./util.js";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
+
import { getProviderByType } from "./provider.js";
|
|
11
|
+
import { Layer } from "effect";
|
|
12
|
+
export const apply = (...resources) => plan(...resources).pipe(Effect.flatMap((p) => applyPlan(p)));
|
|
10
13
|
export const applyPlan = (plan) => Effect.gen(function* () {
|
|
14
|
+
const cli = yield* CLI;
|
|
15
|
+
const session = yield* cli.startApplySession(plan);
|
|
16
|
+
// 1. expand the graph (create new resources, update existing and create replacements)
|
|
17
|
+
const resources = yield* expandAndPivot(plan, session);
|
|
18
|
+
// TODO(sam): support roll back to previous state if errors occur during expansion
|
|
19
|
+
// -> RISK: some UPDATEs may not be reverisble (i.e. trigger replacements)
|
|
20
|
+
// TODO(sam): should pivot be done separately? E.g shift traffic?
|
|
21
|
+
// 2. delete orphans and replaced resources
|
|
22
|
+
yield* collectGarbage(plan, session);
|
|
23
|
+
yield* session.done();
|
|
24
|
+
if (Object.keys(plan.resources).length === 0) {
|
|
25
|
+
// all resources are deleted, return undefined
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return resources;
|
|
29
|
+
});
|
|
30
|
+
const expandAndPivot = Effect.fnUntraced(function* (plan, session) {
|
|
11
31
|
const state = yield* State;
|
|
12
|
-
// TODO(sam): rename terminology to Stack
|
|
13
32
|
const app = yield* App;
|
|
14
33
|
const outputs = {};
|
|
15
|
-
const cli = yield* CLI;
|
|
16
|
-
const session = yield* cli.startApplySession(plan);
|
|
17
|
-
const { emit, done } = session;
|
|
18
34
|
const resolveUpstream = Effect.fn(function* (resourceId) {
|
|
19
35
|
const upstreamNode = plan.resources[resourceId];
|
|
20
36
|
const upstreamAttr = upstreamNode
|
|
@@ -88,21 +104,12 @@ export const applyPlan = (plan) => Effect.gen(function* () {
|
|
|
88
104
|
return oldBindingOutput;
|
|
89
105
|
})));
|
|
90
106
|
const apply = (node) => Effect.gen(function* () {
|
|
91
|
-
const
|
|
92
|
-
.set({
|
|
107
|
+
const commit = (value) => state.set({
|
|
93
108
|
stack: app.name,
|
|
94
109
|
stage: app.stage,
|
|
95
110
|
resourceId: node.resource.id,
|
|
96
|
-
value
|
|
97
|
-
|
|
98
|
-
type: node.resource.type,
|
|
99
|
-
status: node.action === "create" ? "created" : "updated",
|
|
100
|
-
props: news,
|
|
101
|
-
output,
|
|
102
|
-
bindings,
|
|
103
|
-
},
|
|
104
|
-
})
|
|
105
|
-
.pipe(Effect.map(() => output));
|
|
111
|
+
value,
|
|
112
|
+
});
|
|
106
113
|
const id = node.resource.id;
|
|
107
114
|
const resource = node.resource;
|
|
108
115
|
const scopedSession = {
|
|
@@ -114,169 +121,447 @@ export const applyPlan = (plan) => Effect.gen(function* () {
|
|
|
114
121
|
}),
|
|
115
122
|
};
|
|
116
123
|
return yield* (outputs[id] ??= yield* Effect.cached(Effect.gen(function* () {
|
|
117
|
-
const report = (status) => emit({
|
|
124
|
+
const report = (status) => session.emit({
|
|
118
125
|
kind: "status-change",
|
|
119
126
|
id,
|
|
120
127
|
type: node.resource.type,
|
|
121
128
|
status,
|
|
122
129
|
});
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
if (node.action === "noop") {
|
|
131
|
+
return node.state.attr;
|
|
132
|
+
}
|
|
133
|
+
// resolve upstream dependencies before committing any changes to state
|
|
134
|
+
const upstream = Object.fromEntries(yield* Effect.all(Object.entries(Output.resolveUpstream(node.props)).map(([id]) => resolveUpstream(id).pipe(Effect.map(({ upstreamAttr }) => [id, upstreamAttr])))));
|
|
135
|
+
const instanceId = yield* Effect.gen(function* () {
|
|
136
|
+
if (node.action === "create" && !node.state?.instanceId) {
|
|
137
|
+
const instanceId = yield* generateInstanceId();
|
|
138
|
+
yield* commit({
|
|
139
|
+
status: "creating",
|
|
140
|
+
instanceId,
|
|
141
|
+
logicalId: id,
|
|
142
|
+
downstream: node.downstream,
|
|
143
|
+
props: node.props,
|
|
144
|
+
providerVersion: node.provider.version ?? 0,
|
|
145
|
+
resourceType: node.resource.type,
|
|
146
|
+
bindings: node.bindings,
|
|
147
|
+
});
|
|
148
|
+
return instanceId;
|
|
149
|
+
}
|
|
150
|
+
else if (node.action === "replace") {
|
|
151
|
+
if (node.state.status === "replaced" ||
|
|
152
|
+
node.state.status === "replacing") {
|
|
153
|
+
// replace has already begun and we have the new instanceId, do not re-create it
|
|
154
|
+
return node.state.instanceId;
|
|
155
|
+
}
|
|
156
|
+
const instanceId = yield* generateInstanceId();
|
|
157
|
+
yield* commit({
|
|
158
|
+
status: "replacing",
|
|
159
|
+
instanceId,
|
|
160
|
+
logicalId: id,
|
|
161
|
+
downstream: node.downstream,
|
|
162
|
+
props: node.props,
|
|
163
|
+
providerVersion: node.provider.version ?? 0,
|
|
164
|
+
resourceType: node.resource.type,
|
|
165
|
+
bindings: node.bindings,
|
|
166
|
+
old: node.state,
|
|
167
|
+
deleteFirst: node.deleteFirst,
|
|
168
|
+
});
|
|
169
|
+
return instanceId;
|
|
170
|
+
}
|
|
171
|
+
else if (node.state?.instanceId) {
|
|
172
|
+
// we're in a create, update or delete state with a stable instanceId, use it
|
|
173
|
+
return node.state.instanceId;
|
|
174
|
+
}
|
|
175
|
+
// this should never happen
|
|
176
|
+
return yield* Effect.dieMessage(`Instance ID not found for resource '${id}' and action is '${node.action}'`);
|
|
177
|
+
});
|
|
178
|
+
const apply = Effect.gen(function* () {
|
|
179
|
+
if (node.action === "create") {
|
|
180
|
+
const news = (yield* Output.evaluate(node.props, upstream));
|
|
181
|
+
const checkpoint = (attr) => commit({
|
|
182
|
+
status: "creating",
|
|
183
|
+
logicalId: id,
|
|
184
|
+
instanceId,
|
|
185
|
+
resourceType: node.resource.type,
|
|
186
|
+
props: news,
|
|
187
|
+
attr,
|
|
188
|
+
providerVersion: node.provider.version ?? 0,
|
|
189
|
+
bindings: node.bindings,
|
|
190
|
+
downstream: node.downstream,
|
|
191
|
+
});
|
|
192
|
+
if (!node.state) {
|
|
193
|
+
yield* checkpoint(undefined);
|
|
194
|
+
}
|
|
195
|
+
let attr;
|
|
196
|
+
if (node.action === "create" &&
|
|
197
|
+
node.provider.precreate &&
|
|
198
|
+
// pre-create is only designed to ensure the resource exists, if we have state.attr, then it already exists and should be skipped
|
|
199
|
+
node.state?.attr === undefined) {
|
|
200
|
+
yield* report("pre-creating");
|
|
201
|
+
// stub the resource prior to resolving upstream resources or bindings if a stub is available
|
|
202
|
+
attr = yield* node.provider.precreate({
|
|
203
|
+
id,
|
|
204
|
+
news: node.props,
|
|
205
|
+
session: scopedSession,
|
|
206
|
+
instanceId,
|
|
207
|
+
});
|
|
208
|
+
yield* checkpoint(attr);
|
|
209
|
+
}
|
|
210
|
+
yield* report("attaching");
|
|
211
|
+
let bindingOutputs = yield* attachBindings({
|
|
212
|
+
resource,
|
|
213
|
+
bindings: node.bindings,
|
|
214
|
+
target: {
|
|
215
|
+
id,
|
|
216
|
+
props: news,
|
|
217
|
+
attr,
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
yield* report("creating");
|
|
221
|
+
attr = yield* node.provider.create({
|
|
131
222
|
id,
|
|
223
|
+
news,
|
|
224
|
+
instanceId,
|
|
225
|
+
bindings: bindingOutputs,
|
|
226
|
+
session: scopedSession,
|
|
227
|
+
});
|
|
228
|
+
yield* checkpoint(attr);
|
|
229
|
+
yield* report("post-attach");
|
|
230
|
+
bindingOutputs = yield* postAttachBindings({
|
|
231
|
+
resource,
|
|
232
|
+
bindings: node.bindings,
|
|
233
|
+
bindingOutputs,
|
|
234
|
+
target: {
|
|
235
|
+
id,
|
|
236
|
+
props: news,
|
|
237
|
+
attr,
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
yield* commit({
|
|
241
|
+
status: "created",
|
|
242
|
+
logicalId: id,
|
|
243
|
+
instanceId,
|
|
244
|
+
resourceType: node.resource.type,
|
|
132
245
|
props: news,
|
|
133
246
|
attr,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
247
|
+
bindings: node.bindings.map((binding, i) => ({
|
|
248
|
+
...binding,
|
|
249
|
+
attr: bindingOutputs[i],
|
|
250
|
+
})),
|
|
251
|
+
providerVersion: node.provider.version ?? 0,
|
|
252
|
+
downstream: node.downstream,
|
|
253
|
+
});
|
|
254
|
+
yield* report("created");
|
|
255
|
+
return attr;
|
|
256
|
+
}
|
|
257
|
+
else if (node.action === "update") {
|
|
258
|
+
const upstream = Object.fromEntries(yield* Effect.all(Object.entries(Output.resolveUpstream(node.props)).map(([id]) => resolveUpstream(id).pipe(Effect.map(({ upstreamAttr }) => [id, upstreamAttr])))));
|
|
259
|
+
const news = (yield* Output.evaluate(node.props, upstream));
|
|
260
|
+
const checkpoint = (attr) => {
|
|
261
|
+
if (node.state.status === "replaced") {
|
|
262
|
+
return commit({
|
|
263
|
+
...node.state,
|
|
264
|
+
attr,
|
|
265
|
+
props: news,
|
|
266
|
+
});
|
|
145
267
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
268
|
+
else {
|
|
269
|
+
return commit({
|
|
270
|
+
status: "updating",
|
|
271
|
+
logicalId: id,
|
|
272
|
+
instanceId,
|
|
273
|
+
resourceType: node.resource.type,
|
|
274
|
+
props: news,
|
|
275
|
+
attr,
|
|
276
|
+
providerVersion: node.provider.version ?? 0,
|
|
277
|
+
bindings: node.bindings,
|
|
278
|
+
downstream: node.downstream,
|
|
279
|
+
old: node.state.status === "updating"
|
|
280
|
+
? node.state.old
|
|
281
|
+
: node.state,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
yield* checkpoint(node.state.attr);
|
|
286
|
+
yield* report("attaching");
|
|
287
|
+
let bindingOutputs = yield* attachBindings({
|
|
288
|
+
resource,
|
|
289
|
+
bindings: node.bindings,
|
|
290
|
+
target: {
|
|
291
|
+
id,
|
|
292
|
+
props: news,
|
|
293
|
+
attr: node.state.attr,
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
yield* report("updating");
|
|
297
|
+
const attr = yield* node.provider.update({
|
|
156
298
|
id,
|
|
299
|
+
news,
|
|
300
|
+
instanceId,
|
|
301
|
+
bindings: bindingOutputs,
|
|
302
|
+
session: scopedSession,
|
|
303
|
+
olds: node.state.status === "created" ||
|
|
304
|
+
node.state.status === "updated" ||
|
|
305
|
+
node.state.status === "replaced"
|
|
306
|
+
? node.state.props
|
|
307
|
+
: node.state.old.props,
|
|
308
|
+
output: node.state.attr,
|
|
309
|
+
});
|
|
310
|
+
yield* checkpoint(attr);
|
|
311
|
+
yield* report("post-attach");
|
|
312
|
+
bindingOutputs = yield* postAttachBindings({
|
|
313
|
+
resource,
|
|
314
|
+
bindings: node.bindings,
|
|
315
|
+
bindingOutputs,
|
|
316
|
+
target: {
|
|
317
|
+
id,
|
|
318
|
+
props: news,
|
|
319
|
+
attr,
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
if (node.state.status === "replaced") {
|
|
323
|
+
yield* commit({
|
|
324
|
+
...node.state,
|
|
325
|
+
attr,
|
|
326
|
+
props: news,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
yield* commit({
|
|
331
|
+
status: "updated",
|
|
332
|
+
logicalId: id,
|
|
333
|
+
instanceId,
|
|
334
|
+
resourceType: node.resource.type,
|
|
335
|
+
props: news,
|
|
336
|
+
attr,
|
|
337
|
+
bindings: node.bindings.map((binding, i) => ({
|
|
338
|
+
...binding,
|
|
339
|
+
attr: bindingOutputs[i],
|
|
340
|
+
})),
|
|
341
|
+
providerVersion: node.provider.version ?? 0,
|
|
342
|
+
downstream: node.downstream,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
yield* report("updated");
|
|
346
|
+
return attr;
|
|
347
|
+
}
|
|
348
|
+
else if (node.action === "replace") {
|
|
349
|
+
if (node.state.status === "replaced") {
|
|
350
|
+
// we've already created the replacement resource, return the output
|
|
351
|
+
return node.state.attr;
|
|
352
|
+
}
|
|
353
|
+
let state;
|
|
354
|
+
if (node.state.status !== "replacing") {
|
|
355
|
+
yield* commit((state = {
|
|
356
|
+
status: "replacing",
|
|
357
|
+
logicalId: id,
|
|
358
|
+
instanceId,
|
|
359
|
+
resourceType: node.resource.type,
|
|
360
|
+
props: node.props,
|
|
361
|
+
attr: node.state.attr,
|
|
362
|
+
providerVersion: node.provider.version ?? 0,
|
|
363
|
+
deleteFirst: node.deleteFirst,
|
|
364
|
+
old: node.state,
|
|
365
|
+
downstream: node.downstream,
|
|
366
|
+
}));
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
state = node.state;
|
|
370
|
+
}
|
|
371
|
+
const upstream = Object.fromEntries(yield* Effect.all(Object.entries(Output.resolveUpstream(node.props)).map(([id]) => resolveUpstream(id).pipe(Effect.map(({ upstreamAttr }) => [id, upstreamAttr])))));
|
|
372
|
+
const news = (yield* Output.evaluate(node.props, upstream));
|
|
373
|
+
const checkpoint = ({ status, attr, bindings, }) => commit({
|
|
374
|
+
status,
|
|
375
|
+
logicalId: id,
|
|
376
|
+
instanceId,
|
|
377
|
+
resourceType: node.resource.type,
|
|
157
378
|
props: news,
|
|
158
379
|
attr,
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
380
|
+
providerVersion: node.provider.version ?? 0,
|
|
381
|
+
bindings: bindings ?? node.bindings,
|
|
382
|
+
downstream: node.downstream,
|
|
383
|
+
old: state.old,
|
|
384
|
+
deleteFirst: node.deleteFirst,
|
|
385
|
+
});
|
|
386
|
+
let attr;
|
|
387
|
+
if (node.provider.precreate &&
|
|
388
|
+
// pre-create is only designed to ensure the resource exists, if we have state.attr, then it already exists and should be skipped
|
|
389
|
+
node.state?.attr === undefined) {
|
|
390
|
+
yield* report("pre-creating");
|
|
391
|
+
// stub the resource prior to resolving upstream resources or bindings if a stub is available
|
|
392
|
+
attr = yield* node.provider.precreate({
|
|
393
|
+
id,
|
|
394
|
+
news: node.props,
|
|
395
|
+
session: scopedSession,
|
|
396
|
+
instanceId,
|
|
397
|
+
});
|
|
398
|
+
yield* checkpoint({
|
|
399
|
+
status: "replacing",
|
|
400
|
+
attr,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
yield* report("attaching");
|
|
404
|
+
let bindingOutputs = yield* attachBindings({
|
|
405
|
+
resource,
|
|
406
|
+
bindings: node.bindings,
|
|
407
|
+
target: {
|
|
408
|
+
id,
|
|
409
|
+
props: news,
|
|
410
|
+
attr,
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
yield* report("creating replacement");
|
|
414
|
+
attr = yield* node.provider.create({
|
|
184
415
|
id,
|
|
185
|
-
news
|
|
416
|
+
news,
|
|
417
|
+
instanceId,
|
|
418
|
+
bindings: bindingOutputs,
|
|
186
419
|
session: scopedSession,
|
|
187
420
|
});
|
|
421
|
+
yield* checkpoint({
|
|
422
|
+
status: "replacing",
|
|
423
|
+
attr,
|
|
424
|
+
});
|
|
425
|
+
yield* report("post-attach");
|
|
426
|
+
bindingOutputs = yield* postAttachBindings({
|
|
427
|
+
resource,
|
|
428
|
+
bindings: node.bindings,
|
|
429
|
+
bindingOutputs,
|
|
430
|
+
target: {
|
|
431
|
+
id,
|
|
432
|
+
props: news,
|
|
433
|
+
attr,
|
|
434
|
+
},
|
|
435
|
+
});
|
|
436
|
+
yield* checkpoint({
|
|
437
|
+
status: "replaced",
|
|
438
|
+
attr,
|
|
439
|
+
bindings: node.bindings.map((binding, i) => ({
|
|
440
|
+
...binding,
|
|
441
|
+
attr: bindingOutputs[i],
|
|
442
|
+
})),
|
|
443
|
+
});
|
|
444
|
+
yield* report("created");
|
|
445
|
+
return attr;
|
|
188
446
|
}
|
|
189
|
-
|
|
190
|
-
return yield*
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
447
|
+
// @ts-expect-error
|
|
448
|
+
return yield* Effect.dieMessage(`Unknown action: ${node.action}`);
|
|
449
|
+
});
|
|
450
|
+
// provide the resource-specific context (InstanceId, etc.)
|
|
451
|
+
return yield* apply.pipe(Effect.provide(Layer.succeed(InstanceId, instanceId)));
|
|
452
|
+
})));
|
|
453
|
+
});
|
|
454
|
+
return Object.fromEntries(yield* Effect.all(Object.entries(plan.resources).map(Effect.fn(function* ([id, node]) {
|
|
455
|
+
return [id, yield* apply(node)];
|
|
456
|
+
}))));
|
|
457
|
+
});
|
|
458
|
+
const collectGarbage = Effect.fnUntraced(function* (plan, session) {
|
|
459
|
+
const state = yield* State;
|
|
460
|
+
const app = yield* App;
|
|
461
|
+
const deletions = {};
|
|
462
|
+
// delete all replaced resources
|
|
463
|
+
const replacedResources = yield* state.getReplacedResources({
|
|
464
|
+
stack: app.name,
|
|
465
|
+
stage: app.stage,
|
|
466
|
+
});
|
|
467
|
+
const deletionGraph = {
|
|
468
|
+
...plan.deletions,
|
|
469
|
+
...Object.fromEntries(replacedResources.map((replaced) => [replaced.logicalId, replaced])),
|
|
470
|
+
};
|
|
471
|
+
const deleteResource = Effect.fnUntraced(function* (node) {
|
|
472
|
+
const isDeleteNode = (node) => "action" in node;
|
|
473
|
+
const { logicalId, resourceType, instanceId, downstream, props, attr, provider, } = isDeleteNode(node)
|
|
474
|
+
? {
|
|
475
|
+
logicalId: node.resource.id,
|
|
476
|
+
resourceType: node.resource.type,
|
|
477
|
+
instanceId: node.state.instanceId,
|
|
478
|
+
downstream: node.downstream,
|
|
479
|
+
props: node.state.props,
|
|
480
|
+
attr: node.state.attr,
|
|
481
|
+
provider: node.provider,
|
|
195
482
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
483
|
+
: {
|
|
484
|
+
logicalId: node.logicalId,
|
|
485
|
+
resourceType: node.old.resourceType,
|
|
486
|
+
instanceId: node.old.instanceId,
|
|
487
|
+
downstream: node.old.downstream,
|
|
488
|
+
props: node.old.props,
|
|
489
|
+
attr: node.old.attr,
|
|
490
|
+
provider: yield* getProviderByType(node.old.resourceType),
|
|
491
|
+
};
|
|
492
|
+
const commit = (value) => state.set({
|
|
493
|
+
stack: app.name,
|
|
494
|
+
stage: app.stage,
|
|
495
|
+
resourceId: logicalId,
|
|
496
|
+
value,
|
|
497
|
+
});
|
|
498
|
+
const report = (status) => session.emit({
|
|
499
|
+
kind: "status-change",
|
|
500
|
+
id: logicalId,
|
|
501
|
+
type: resourceType,
|
|
502
|
+
status,
|
|
503
|
+
});
|
|
504
|
+
const scopedSession = {
|
|
505
|
+
...session,
|
|
506
|
+
note: (note) => session.emit({
|
|
507
|
+
id: logicalId,
|
|
508
|
+
kind: "annotate",
|
|
509
|
+
message: note,
|
|
510
|
+
}),
|
|
511
|
+
};
|
|
512
|
+
return yield* (deletions[logicalId] ??= yield* Effect.cached(Effect.gen(function* () {
|
|
513
|
+
yield* Effect.all(downstream.map((dep) => dep in deletionGraph
|
|
514
|
+
? deleteResource(deletionGraph[dep])
|
|
515
|
+
: Effect.void));
|
|
516
|
+
yield* report("deleting");
|
|
517
|
+
if (isDeleteNode(node)) {
|
|
518
|
+
yield* commit({
|
|
519
|
+
status: "deleting",
|
|
520
|
+
logicalId,
|
|
521
|
+
instanceId,
|
|
522
|
+
resourceType,
|
|
523
|
+
props,
|
|
524
|
+
attr,
|
|
525
|
+
downstream,
|
|
526
|
+
providerVersion: provider.version ?? 0,
|
|
527
|
+
bindings: node.bindings,
|
|
202
528
|
});
|
|
203
529
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
session: scopedSession,
|
|
216
|
-
bindings: [],
|
|
217
|
-
})
|
|
218
|
-
.pipe(Effect.flatMap(() => state.delete({
|
|
530
|
+
yield* provider.delete({
|
|
531
|
+
id: logicalId,
|
|
532
|
+
instanceId,
|
|
533
|
+
olds: props,
|
|
534
|
+
output: attr,
|
|
535
|
+
session: scopedSession,
|
|
536
|
+
bindings: [],
|
|
537
|
+
});
|
|
538
|
+
if (isDeleteNode(node)) {
|
|
539
|
+
// TODO(sam): should we commit a tombstone instead? and then clean up tombstones after all deletions are complete?
|
|
540
|
+
yield* state.delete({
|
|
219
541
|
stack: app.name,
|
|
220
542
|
stage: app.stage,
|
|
221
|
-
resourceId:
|
|
222
|
-
})), Effect.tap(() => report("deleted")));
|
|
223
|
-
}
|
|
224
|
-
else if (node.action === "replace") {
|
|
225
|
-
const destroy = Effect.gen(function* () {
|
|
226
|
-
yield* report("deleting");
|
|
227
|
-
return yield* node.provider.delete({
|
|
228
|
-
id,
|
|
229
|
-
olds: node.olds,
|
|
230
|
-
output: node.output,
|
|
231
|
-
session: scopedSession,
|
|
232
|
-
bindings: [],
|
|
233
|
-
});
|
|
543
|
+
resourceId: logicalId,
|
|
234
544
|
});
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
// TODO(sam): resolve the news
|
|
249
|
-
props: node.news,
|
|
250
|
-
attr: node.attributes,
|
|
251
|
-
},
|
|
252
|
-
}),
|
|
253
|
-
session: scopedSession,
|
|
254
|
-
})
|
|
255
|
-
.pipe(Effect.tap((output) => saveState({ news: node.news, output })));
|
|
545
|
+
yield* report("deleted");
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
yield* commit({
|
|
549
|
+
status: "created",
|
|
550
|
+
logicalId,
|
|
551
|
+
instanceId,
|
|
552
|
+
resourceType,
|
|
553
|
+
props: node.props,
|
|
554
|
+
attr: node.attr,
|
|
555
|
+
providerVersion: provider.version ?? 0,
|
|
556
|
+
downstream: node.downstream,
|
|
557
|
+
bindings: node.bindings,
|
|
256
558
|
});
|
|
257
|
-
|
|
258
|
-
yield* destroy;
|
|
259
|
-
return outputs;
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
yield* destroy;
|
|
263
|
-
return yield* create;
|
|
264
|
-
}
|
|
559
|
+
yield* report("replaced");
|
|
265
560
|
}
|
|
266
|
-
})));
|
|
561
|
+
}).pipe(Effect.provide(Layer.succeed(InstanceId, instanceId)))));
|
|
267
562
|
});
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
];
|
|
272
|
-
const resources = Object.fromEntries(yield* Effect.all(nodes.map(Effect.fn(function* ([id, node]) {
|
|
273
|
-
return [id, yield* apply(node)];
|
|
274
|
-
}))));
|
|
275
|
-
yield* done();
|
|
276
|
-
if (Object.keys(plan.resources).length === 0) {
|
|
277
|
-
// all resources are deleted, return undefined
|
|
278
|
-
return undefined;
|
|
279
|
-
}
|
|
280
|
-
return resources;
|
|
563
|
+
yield* Effect.all(Object.values(deletionGraph)
|
|
564
|
+
.filter((node) => node !== undefined)
|
|
565
|
+
.map(deleteResource));
|
|
281
566
|
});
|
|
282
567
|
//# sourceMappingURL=apply.js.map
|