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/src/provider.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as Context from "effect/Context";
|
|
2
|
-
import
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import type { ScopedPlanStatusSession } from "./cli/service.ts";
|
|
3
4
|
import type { Diff } from "./diff.ts";
|
|
4
5
|
import type { Input } from "./input.ts";
|
|
5
6
|
import type { Resource } from "./resource.ts";
|
|
6
7
|
import type { Runtime } from "./runtime.ts";
|
|
7
8
|
import type { Service } from "./service.ts";
|
|
8
|
-
import type { ScopedPlanStatusSession } from "./cli/service.ts";
|
|
9
9
|
|
|
10
10
|
export interface Provider<R extends Resource | Service>
|
|
11
11
|
extends Context.TagClass<
|
|
@@ -25,7 +25,21 @@ type BindingData<Res extends Resource> = [Res] extends [Runtime]
|
|
|
25
25
|
|
|
26
26
|
type Props<Res extends Resource> = Input.ResolveOpaque<Res["props"]>;
|
|
27
27
|
|
|
28
|
-
export interface ProviderService<
|
|
28
|
+
export interface ProviderService<
|
|
29
|
+
Res extends Resource = Resource,
|
|
30
|
+
ReadReq = never,
|
|
31
|
+
DiffReq = never,
|
|
32
|
+
PrecreateReq = never,
|
|
33
|
+
CreateReq = never,
|
|
34
|
+
UpdateReq = never,
|
|
35
|
+
DeleteReq = never,
|
|
36
|
+
> {
|
|
37
|
+
/**
|
|
38
|
+
* The version of the provider.
|
|
39
|
+
*
|
|
40
|
+
* @default 0
|
|
41
|
+
*/
|
|
42
|
+
version?: number;
|
|
29
43
|
// tail();
|
|
30
44
|
// watch();
|
|
31
45
|
// replace(): Effect.Effect<void, never, never>;
|
|
@@ -33,44 +47,67 @@ export interface ProviderService<Res extends Resource = Resource> {
|
|
|
33
47
|
// run?() {}
|
|
34
48
|
read?(input: {
|
|
35
49
|
id: string;
|
|
50
|
+
instanceId: string;
|
|
36
51
|
olds: Props<Res> | undefined;
|
|
37
52
|
// what is the ARN?
|
|
38
53
|
output: Res["attr"] | undefined; // current state -> synced state
|
|
39
54
|
session: ScopedPlanStatusSession;
|
|
40
55
|
bindings: BindingData<Res>;
|
|
41
|
-
}): Effect.Effect<Res["attr"] | undefined, any,
|
|
56
|
+
}): Effect.Effect<Res["attr"] | undefined, any, ReadReq>;
|
|
57
|
+
/**
|
|
58
|
+
* Properties that are always stable across any update.
|
|
59
|
+
*/
|
|
60
|
+
stables?: Extract<keyof Res["attr"], string>[];
|
|
42
61
|
diff?(input: {
|
|
43
62
|
id: string;
|
|
44
63
|
olds: Props<Res>;
|
|
64
|
+
instanceId: string;
|
|
45
65
|
// Note: we do not resolve (Props<Res>) here because diff runs during plan
|
|
46
66
|
// -> we need a way for the diff handlers to work with Outputs
|
|
47
67
|
news: Res["props"];
|
|
48
68
|
output: Res["attr"];
|
|
49
|
-
}): Effect.Effect<Diff | void, never,
|
|
69
|
+
}): Effect.Effect<Diff | void, never, DiffReq>;
|
|
50
70
|
precreate?(input: {
|
|
51
71
|
id: string;
|
|
52
72
|
news: Props<Res>;
|
|
73
|
+
instanceId: string;
|
|
53
74
|
session: ScopedPlanStatusSession;
|
|
54
|
-
}): Effect.Effect<Res["attr"], any,
|
|
75
|
+
}): Effect.Effect<Res["attr"], any, PrecreateReq>;
|
|
55
76
|
create(input: {
|
|
56
77
|
id: string;
|
|
78
|
+
instanceId: string;
|
|
57
79
|
news: Props<Res>;
|
|
58
80
|
session: ScopedPlanStatusSession;
|
|
59
81
|
bindings: BindingData<Res>;
|
|
60
|
-
}): Effect.Effect<Res["attr"], any,
|
|
82
|
+
}): Effect.Effect<Res["attr"], any, CreateReq>;
|
|
61
83
|
update(input: {
|
|
62
84
|
id: string;
|
|
85
|
+
instanceId: string;
|
|
63
86
|
news: Props<Res>;
|
|
64
87
|
olds: Props<Res>;
|
|
65
88
|
output: Res["attr"];
|
|
66
89
|
session: ScopedPlanStatusSession;
|
|
67
90
|
bindings: BindingData<Res>;
|
|
68
|
-
}): Effect.Effect<Res["attr"], any,
|
|
91
|
+
}): Effect.Effect<Res["attr"], any, UpdateReq>;
|
|
69
92
|
delete(input: {
|
|
70
93
|
id: string;
|
|
94
|
+
instanceId: string;
|
|
71
95
|
olds: Props<Res>;
|
|
72
96
|
output: Res["attr"];
|
|
73
97
|
session: ScopedPlanStatusSession;
|
|
74
98
|
bindings: BindingData<Res>;
|
|
75
|
-
}): Effect.Effect<void, any,
|
|
99
|
+
}): Effect.Effect<void, any, DeleteReq>;
|
|
76
100
|
}
|
|
101
|
+
|
|
102
|
+
export const getProviderByType = Effect.fnUntraced(function* (
|
|
103
|
+
resourceType: string,
|
|
104
|
+
) {
|
|
105
|
+
const context = yield* Effect.context<never>();
|
|
106
|
+
const provider: ProviderService = context.unsafeMap.get(resourceType);
|
|
107
|
+
if (!provider) {
|
|
108
|
+
return yield* Effect.die(
|
|
109
|
+
new Error(`Provider not found for ${resourceType}`),
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return provider;
|
|
113
|
+
});
|
package/src/resource.ts
CHANGED
|
@@ -40,10 +40,56 @@ export interface Resource<
|
|
|
40
40
|
export interface ResourceTags<R extends Resource<string, string, any, any>> {
|
|
41
41
|
of<S extends ProviderService<R>>(service: S): S;
|
|
42
42
|
tag: Provider<R>;
|
|
43
|
-
effect<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
effect<
|
|
44
|
+
Err,
|
|
45
|
+
Req,
|
|
46
|
+
ReadReq = never,
|
|
47
|
+
DiffReq = never,
|
|
48
|
+
PrecreateReq = never,
|
|
49
|
+
CreateReq = never,
|
|
50
|
+
UpdateReq = never,
|
|
51
|
+
DeleteReq = never,
|
|
52
|
+
>(
|
|
53
|
+
eff: Effect<
|
|
54
|
+
ProviderService<
|
|
55
|
+
R,
|
|
56
|
+
ReadReq,
|
|
57
|
+
DiffReq,
|
|
58
|
+
PrecreateReq,
|
|
59
|
+
CreateReq,
|
|
60
|
+
UpdateReq,
|
|
61
|
+
DeleteReq
|
|
62
|
+
>,
|
|
63
|
+
Err,
|
|
64
|
+
Req
|
|
65
|
+
>,
|
|
66
|
+
): Layer.Layer<
|
|
67
|
+
Provider<R>,
|
|
68
|
+
Err,
|
|
69
|
+
Req | ReadReq | DiffReq | PrecreateReq | CreateReq | UpdateReq | DeleteReq
|
|
70
|
+
>;
|
|
71
|
+
succeed<
|
|
72
|
+
ReadReq = never,
|
|
73
|
+
DiffReq = never,
|
|
74
|
+
PrecreateReq = never,
|
|
75
|
+
CreateReq = never,
|
|
76
|
+
UpdateReq = never,
|
|
77
|
+
DeleteReq = never,
|
|
78
|
+
>(
|
|
79
|
+
service: ProviderService<
|
|
80
|
+
R,
|
|
81
|
+
ReadReq,
|
|
82
|
+
DiffReq,
|
|
83
|
+
PrecreateReq,
|
|
84
|
+
CreateReq,
|
|
85
|
+
UpdateReq,
|
|
86
|
+
DeleteReq
|
|
87
|
+
>,
|
|
88
|
+
): Layer.Layer<
|
|
89
|
+
Provider<R>,
|
|
90
|
+
never,
|
|
91
|
+
ReadReq | DiffReq | PrecreateReq | CreateReq | UpdateReq | DeleteReq
|
|
92
|
+
>;
|
|
47
93
|
}
|
|
48
94
|
|
|
49
95
|
export const Resource = <Ctor extends (id: string, props: any) => Resource>(
|
package/src/state.ts
CHANGED
|
@@ -52,22 +52,114 @@ import { isResource } from "./resource.ts";
|
|
|
52
52
|
|
|
53
53
|
// Scrap the "key-value" store on State/Scope
|
|
54
54
|
|
|
55
|
-
export type
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
export type Props = Record<string, any>;
|
|
56
|
+
export type Attr = Record<string, any>;
|
|
57
|
+
|
|
58
|
+
export type ResourceStatus = ResourceState["status"];
|
|
59
|
+
|
|
60
|
+
interface BaseResourceState {
|
|
61
|
+
resourceType: string;
|
|
62
|
+
/** Logical ID of the Resource (stable across creates, updates, deletes and replaces) */
|
|
63
|
+
logicalId: string;
|
|
64
|
+
/** A unique randomly generated token used to seed ID generation (only changes when replaced) */
|
|
65
|
+
instanceId: string;
|
|
66
|
+
/** The version of the provider that was used to create/update the resource. */
|
|
67
|
+
providerVersion: number;
|
|
68
|
+
/** Current status of the logical Resource */
|
|
66
69
|
status: ResourceStatus;
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
/** List of logical IDs of resources that depend on this resource */
|
|
71
|
+
downstream: string[];
|
|
72
|
+
/** List of Bindings attached to this Resource */
|
|
69
73
|
bindings?: BindNode[];
|
|
70
|
-
|
|
74
|
+
/** Desired state (input props) of this Resource */
|
|
75
|
+
props?: Props;
|
|
76
|
+
/** The output attributes of this Resource (if it has been created) */
|
|
77
|
+
attr?: Attr;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface CreatingResourceState extends BaseResourceState {
|
|
81
|
+
status: "creating";
|
|
82
|
+
/** The new resource properties that are being (or have been) applied. */
|
|
83
|
+
props: Props;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface CreatedResourceState extends BaseResourceState {
|
|
87
|
+
status: "created";
|
|
88
|
+
/** The new resource properties that have been applied. */
|
|
89
|
+
props: Props;
|
|
90
|
+
/** The output attributes of the created resource */
|
|
91
|
+
attr: Attr;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface UpdatingReourceState extends BaseResourceState {
|
|
95
|
+
status: "updating";
|
|
96
|
+
/** The new resource properties that are being (or have been) applied. */
|
|
97
|
+
props: Props;
|
|
98
|
+
old: {
|
|
99
|
+
/** The old resource properties that have been successfully applied. */
|
|
100
|
+
props: Props;
|
|
101
|
+
/** The old output properties that have been successfully applied. */
|
|
102
|
+
attr: Attr;
|
|
103
|
+
// TODO(sam): do I need to track the old downstream edges?
|
|
104
|
+
// downstream: string[];
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface UpdatedResourceState extends BaseResourceState {
|
|
109
|
+
status: "updated";
|
|
110
|
+
/** The new resource properties that are being (or have been) applied. */
|
|
111
|
+
props: Props;
|
|
112
|
+
/** The output attributes of the created resource */
|
|
113
|
+
attr: Attr;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface DeletingResourceState extends BaseResourceState {
|
|
117
|
+
status: "deleting";
|
|
118
|
+
/** Attributes of the resource being deleted */
|
|
119
|
+
attr: Attr | undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface ReplacingResourceState extends BaseResourceState {
|
|
123
|
+
status: "replacing";
|
|
124
|
+
/** Desired properties of the new resource (the replacement) */
|
|
125
|
+
props: Props;
|
|
126
|
+
/** Reference to the state of the old resource (the one being replaced) */
|
|
127
|
+
old:
|
|
128
|
+
| CreatedResourceState
|
|
129
|
+
| UpdatedResourceState
|
|
130
|
+
| CreatingResourceState
|
|
131
|
+
| UpdatingReourceState
|
|
132
|
+
| DeletingResourceState;
|
|
133
|
+
/** Whether the resource should be deleted before or after replacements */
|
|
134
|
+
deleteFirst: boolean;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface ReplacedResourceState extends BaseResourceState {
|
|
138
|
+
status: "replaced";
|
|
139
|
+
/** Desired properties of the new resource (the replacement) */
|
|
140
|
+
props: Props;
|
|
141
|
+
/** Output attributes of the new resource (the replacement) */
|
|
142
|
+
attr: Attr;
|
|
143
|
+
/** Reference to the state of the old resource (the one being replaced) */
|
|
144
|
+
old:
|
|
145
|
+
| CreatingResourceState
|
|
146
|
+
| CreatedResourceState
|
|
147
|
+
| UpdatingReourceState
|
|
148
|
+
| UpdatedResourceState
|
|
149
|
+
| DeletingResourceState;
|
|
150
|
+
/** Whether the resource should be deleted before or after replacements */
|
|
151
|
+
deleteFirst: boolean;
|
|
152
|
+
// .. will (finally) transition to `CreatedResourceState` after finalizing
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export type ResourceState =
|
|
156
|
+
| CreatingResourceState
|
|
157
|
+
| CreatedResourceState
|
|
158
|
+
| UpdatingReourceState
|
|
159
|
+
| UpdatedResourceState
|
|
160
|
+
| DeletingResourceState
|
|
161
|
+
| ReplacingResourceState
|
|
162
|
+
| ReplacedResourceState;
|
|
71
163
|
|
|
72
164
|
export class StateStoreError extends Data.TaggedError("StateStoreError")<{
|
|
73
165
|
message: string;
|
|
@@ -82,6 +174,10 @@ export interface StateService {
|
|
|
82
174
|
stage: string;
|
|
83
175
|
resourceId: string;
|
|
84
176
|
}): Effect.Effect<ResourceState | undefined, StateStoreError, never>;
|
|
177
|
+
getReplacedResources(request: {
|
|
178
|
+
stack: string;
|
|
179
|
+
stage: string;
|
|
180
|
+
}): Effect.Effect<ReplacedResourceState[], StateStoreError, never>;
|
|
85
181
|
set<V extends ResourceState>(request: {
|
|
86
182
|
stack: string;
|
|
87
183
|
stage: string;
|
|
@@ -107,7 +203,6 @@ export class State extends Context.Tag("AWS::Lambda::State")<
|
|
|
107
203
|
// TODO(sam): implement with SQLite3
|
|
108
204
|
export const localFs = Layer.effect(
|
|
109
205
|
State,
|
|
110
|
-
// @ts-expect-error -
|
|
111
206
|
Effect.gen(function* () {
|
|
112
207
|
const fs = yield* FileSystem.FileSystem;
|
|
113
208
|
const path = yield* Path.Path;
|
|
@@ -146,8 +241,8 @@ export const localFs = Layer.effect(
|
|
|
146
241
|
fs.makeDirectory(dir, { recursive: true }),
|
|
147
242
|
);
|
|
148
243
|
|
|
149
|
-
|
|
150
|
-
|
|
244
|
+
const state: StateService = {
|
|
245
|
+
listStacks: () =>
|
|
151
246
|
fs.readDirectory(stateDir).pipe(
|
|
152
247
|
recover,
|
|
153
248
|
Effect.map((files) => files ?? []),
|
|
@@ -162,6 +257,17 @@ export const localFs = Layer.effect(
|
|
|
162
257
|
Effect.map((file) => JSON.parse(file.toString())),
|
|
163
258
|
recover,
|
|
164
259
|
),
|
|
260
|
+
getReplacedResources: Effect.fnUntraced(function* (request) {
|
|
261
|
+
return (yield* Effect.all(
|
|
262
|
+
(yield* state.list(request)).map((resourceId) =>
|
|
263
|
+
state.get({
|
|
264
|
+
stack: request.stack,
|
|
265
|
+
stage: request.stage,
|
|
266
|
+
resourceId,
|
|
267
|
+
}),
|
|
268
|
+
),
|
|
269
|
+
)).filter((r) => r?.status === "replaced");
|
|
270
|
+
}),
|
|
165
271
|
set: (request) =>
|
|
166
272
|
ensure(stage(request)).pipe(
|
|
167
273
|
Effect.flatMap(() =>
|
|
@@ -196,6 +302,7 @@ export const localFs = Layer.effect(
|
|
|
196
302
|
),
|
|
197
303
|
),
|
|
198
304
|
};
|
|
305
|
+
return state;
|
|
199
306
|
}),
|
|
200
307
|
);
|
|
201
308
|
|
|
@@ -221,22 +328,14 @@ export const inMemoryService = (
|
|
|
221
328
|
Record<StageId, Record<ResourceId, ResourceState>>
|
|
222
329
|
> = {},
|
|
223
330
|
) => {
|
|
224
|
-
const state =
|
|
225
|
-
Object.entries(initialState).map(([stack, stages]) => [
|
|
226
|
-
stack,
|
|
227
|
-
new Map(
|
|
228
|
-
Object.entries(stages).map(([stage, resources]) => [
|
|
229
|
-
stage,
|
|
230
|
-
new Map(Object.entries(resources)),
|
|
231
|
-
]),
|
|
232
|
-
),
|
|
233
|
-
]),
|
|
234
|
-
);
|
|
331
|
+
const state = initialState;
|
|
235
332
|
return {
|
|
236
|
-
listStacks: () => Effect.succeed(Array.from(
|
|
333
|
+
listStacks: () => Effect.succeed(Array.from(Object.keys(state))),
|
|
237
334
|
// oxlint-disable-next-line require-yield
|
|
238
335
|
listStages: (stack: string) =>
|
|
239
|
-
Effect.succeed(
|
|
336
|
+
Effect.succeed(
|
|
337
|
+
Array.from(stack in state ? Object.keys(state[stack]) : []),
|
|
338
|
+
),
|
|
240
339
|
get: ({
|
|
241
340
|
stack,
|
|
242
341
|
stage,
|
|
@@ -245,7 +344,19 @@ export const inMemoryService = (
|
|
|
245
344
|
stack: string;
|
|
246
345
|
stage: string;
|
|
247
346
|
resourceId: string;
|
|
248
|
-
}) => Effect.succeed(state
|
|
347
|
+
}) => Effect.succeed(state[stack]?.[stage]?.[resourceId]),
|
|
348
|
+
getReplacedResources: ({
|
|
349
|
+
stack,
|
|
350
|
+
stage,
|
|
351
|
+
}: {
|
|
352
|
+
stack: string;
|
|
353
|
+
stage: string;
|
|
354
|
+
}) =>
|
|
355
|
+
Effect.succeed(
|
|
356
|
+
Array.from(Object.values(state[stack]?.[stage] ?? {}) ?? []).filter(
|
|
357
|
+
(s) => s.status === "replaced",
|
|
358
|
+
),
|
|
359
|
+
),
|
|
249
360
|
set: <V extends ResourceState>({
|
|
250
361
|
stack,
|
|
251
362
|
stage,
|
|
@@ -257,7 +368,9 @@ export const inMemoryService = (
|
|
|
257
368
|
resourceId: string;
|
|
258
369
|
value: V;
|
|
259
370
|
}) => {
|
|
260
|
-
state
|
|
371
|
+
const stackState = (state[stack] ??= {});
|
|
372
|
+
const stageState = (stackState[stage] ??= {});
|
|
373
|
+
stageState[resourceId] = value;
|
|
261
374
|
return Effect.succeed(value);
|
|
262
375
|
},
|
|
263
376
|
delete: ({
|
|
@@ -268,8 +381,10 @@ export const inMemoryService = (
|
|
|
268
381
|
stack: string;
|
|
269
382
|
stage: string;
|
|
270
383
|
resourceId: string;
|
|
271
|
-
}) => Effect.succeed(state
|
|
384
|
+
}) => Effect.succeed(delete state[stack]?.[stage]?.[resourceId]),
|
|
272
385
|
list: ({ stack, stage }: { stack: string; stage: string }) =>
|
|
273
|
-
Effect.succeed(
|
|
274
|
-
|
|
386
|
+
Effect.succeed(
|
|
387
|
+
Array.from(Object.keys(state[stack]?.[stage] ?? {}) ?? []),
|
|
388
|
+
),
|
|
389
|
+
} satisfies StateService;
|
|
275
390
|
};
|
package/src/tags.ts
CHANGED
|
@@ -36,3 +36,34 @@ export const createTagger = Effect.fn(function* () {
|
|
|
36
36
|
"alchemy::id": id,
|
|
37
37
|
});
|
|
38
38
|
});
|
|
39
|
+
|
|
40
|
+
export const diffTags = (
|
|
41
|
+
oldTags: Record<string, string>,
|
|
42
|
+
newTags: Record<string, string>,
|
|
43
|
+
) => {
|
|
44
|
+
const removed: string[] = [];
|
|
45
|
+
const updated: { Key: string; Value: string }[] = [];
|
|
46
|
+
const added: { Key: string; Value: string }[] = [];
|
|
47
|
+
for (const key in oldTags) {
|
|
48
|
+
if (!(key in newTags)) {
|
|
49
|
+
removed.push(key);
|
|
50
|
+
} else if (oldTags[key] !== newTags[key]) {
|
|
51
|
+
updated.push({ Key: key, Value: newTags[key] });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (const key in newTags) {
|
|
55
|
+
if (!(key in oldTags)) {
|
|
56
|
+
added.push({ Key: key, Value: newTags[key] });
|
|
57
|
+
} else if (oldTags[key] !== newTags[key]) {
|
|
58
|
+
updated.push({ Key: key, Value: newTags[key] });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
added,
|
|
63
|
+
removed,
|
|
64
|
+
updated,
|
|
65
|
+
upsert: [...added, ...updated].filter(
|
|
66
|
+
(tag, index, self) => self.findIndex((t) => t.Key === tag.Key) === index,
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
};
|
package/src/test.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { FetchHttpClient, FileSystem, HttpClient } from "@effect/platform";
|
|
2
|
-
import * as PlatformConfigProvider from "@effect/platform/PlatformConfigProvider";
|
|
3
2
|
import { NodeContext } from "@effect/platform-node";
|
|
4
3
|
import * as Path from "@effect/platform/Path";
|
|
5
|
-
import
|
|
4
|
+
import * as PlatformConfigProvider from "@effect/platform/PlatformConfigProvider";
|
|
5
|
+
import { expect, it } from "@effect/vitest";
|
|
6
6
|
import { ConfigProvider, LogLevel } from "effect";
|
|
7
7
|
import * as Effect from "effect/Effect";
|
|
8
8
|
import * as Layer from "effect/Layer";
|
|
9
9
|
import * as Logger from "effect/Logger";
|
|
10
10
|
import * as Scope from "effect/Scope";
|
|
11
11
|
import * as App from "./app.ts";
|
|
12
|
+
import { CLI } from "./cli/service.ts";
|
|
12
13
|
import { DotAlchemy, dotAlchemy } from "./dot-alchemy.ts";
|
|
13
|
-
import * as State from "./state.ts";
|
|
14
14
|
import type { Resource } from "./resource.ts";
|
|
15
|
-
import
|
|
15
|
+
import * as State from "./state.ts";
|
|
16
16
|
|
|
17
17
|
declare module "@effect/vitest" {
|
|
18
18
|
interface ExpectStatic {
|
|
@@ -165,7 +165,7 @@ export const testCLI = Layer.succeed(
|
|
|
165
165
|
Effect.log(
|
|
166
166
|
event.kind === "status-change"
|
|
167
167
|
? `${event.status} ${event.id}(${event.type})`
|
|
168
|
-
: event.message
|
|
168
|
+
: `${event.id}: ${event.message}`,
|
|
169
169
|
),
|
|
170
170
|
}),
|
|
171
171
|
}),
|
package/src/todo.ts
ADDED