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