@terraforge/core 0.0.2 → 0.0.3
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/README.md +0 -3
- package/dist/index.d.ts +20 -18
- package/dist/index.js +78 -58
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,9 +7,6 @@ The core of Terraforge lives in `@terraforge/core`, with the Terraform bridge in
|
|
|
7
7
|
|
|
8
8
|
The most used IaC solutions are slow & don't effectively leverage diffing to speed up their deployments.
|
|
9
9
|
|
|
10
|
-
## Todo's
|
|
11
|
-
- When a resource is being deleted inside a deployment we need to make sure that resources that depends on our deleted resource will be updated first.
|
|
12
|
-
|
|
13
10
|
## Setup
|
|
14
11
|
|
|
15
12
|
Install with (NPM):
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,23 @@ declare class Output<T = unknown> extends Future<T> {
|
|
|
21
21
|
}
|
|
22
22
|
declare const deferredOutput: unknown;
|
|
23
23
|
declare const output: unknown;
|
|
24
|
+
declare const nodeMetaSymbol: unknown;
|
|
25
|
+
type Node<
|
|
26
|
+
T extends Tag = Tag,
|
|
27
|
+
I extends State = State,
|
|
28
|
+
O extends State = any,
|
|
29
|
+
C extends Config = Config
|
|
30
|
+
> = {
|
|
31
|
+
[nodeMetaSymbol]: Meta<T, I, O, C>;
|
|
32
|
+
} & O;
|
|
33
|
+
declare const isNode: (obj: object) => obj is {
|
|
34
|
+
[nodeMetaSymbol]: Meta;
|
|
35
|
+
};
|
|
36
|
+
declare function getMeta(node: Resource): ResourceMeta;
|
|
37
|
+
declare function getMeta(node: DataSource): DataSourceMeta;
|
|
38
|
+
declare function getMeta(node: Node): Meta;
|
|
39
|
+
declare const isResource: (obj: object) => obj is Resource;
|
|
40
|
+
declare const isDataSource: (obj: object) => obj is DataSource;
|
|
24
41
|
type ResourceConfig = Config & {
|
|
25
42
|
/** Import an existing resource instead of creating a new resource. */
|
|
26
43
|
import?: string;
|
|
@@ -39,7 +56,7 @@ type Resource<
|
|
|
39
56
|
I extends State = State,
|
|
40
57
|
O extends State = State
|
|
41
58
|
> = O & {
|
|
42
|
-
readonly
|
|
59
|
+
readonly [nodeMetaSymbol]: ResourceMeta<I, O>;
|
|
43
60
|
};
|
|
44
61
|
type ResourceClass<
|
|
45
62
|
I extends State = State,
|
|
@@ -95,27 +112,12 @@ type DataSource<
|
|
|
95
112
|
I extends State = State,
|
|
96
113
|
O extends State = State
|
|
97
114
|
> = {
|
|
98
|
-
readonly
|
|
115
|
+
readonly [nodeMetaSymbol]: DataSourceMeta<I, O>;
|
|
99
116
|
} & O;
|
|
100
117
|
type DataSourceFunction<
|
|
101
118
|
I extends State = State,
|
|
102
119
|
O extends State = State
|
|
103
120
|
> = (parent: Group, id: string, input: I, config?: Config) => DataSource<I, O>;
|
|
104
|
-
type Node<
|
|
105
|
-
T extends Tag = Tag,
|
|
106
|
-
I extends State = State,
|
|
107
|
-
O extends State = State,
|
|
108
|
-
C extends Config = Config
|
|
109
|
-
> = {
|
|
110
|
-
$: Meta<T, I, O, C>;
|
|
111
|
-
} & O;
|
|
112
|
-
declare const isNode: (obj: object) => obj is {
|
|
113
|
-
$: {
|
|
114
|
-
tag: string;
|
|
115
|
-
};
|
|
116
|
-
};
|
|
117
|
-
declare const isResource: (obj: object) => obj is Resource;
|
|
118
|
-
declare const isDataSource: (obj: object) => obj is DataSource;
|
|
119
121
|
declare class Group {
|
|
120
122
|
readonly parent: Group | undefined;
|
|
121
123
|
readonly type: string;
|
|
@@ -328,4 +330,4 @@ type CustomResourceProvider = Partial<{
|
|
|
328
330
|
getData?(props: Omit<GetDataProps, "type">): Promise<State>;
|
|
329
331
|
}>;
|
|
330
332
|
declare const createCustomProvider: (providerId: string, resourceProviders: Record<string, CustomResourceProvider>) => Provider;
|
|
331
|
-
export { resolveInputs, output, isResource, isNode, isDataSource, findInputDeps, enableDebug, deferredOutput, createMeta, createDebugger, createCustomResourceClass, createCustomProvider, WorkSpaceOptions, WorkSpace, UpdateProps, URN, Tag, StateBackend, State, Stack, S3StateBackend, ResourceNotFound, ResourceMeta, ResourceError, ResourceConfig, ResourceClass, ResourceAlreadyExists, Resource, Provider, ProcedureOptions, Output, OptionalOutput, OptionalInput, Node, Meta, MemoryStateBackend, MemoryLockBackend, LockBackend, Input, Group, GetProps, GetDataProps, Future, FileStateBackend, FileLockBackend, DynamoLockBackend, DeleteProps, DataSourceMeta, DataSourceFunction, DataSource, CustomResourceProvider, CreateProps, Config, AppError, App };
|
|
333
|
+
export { resolveInputs, output, nodeMetaSymbol, isResource, isNode, isDataSource, getMeta, findInputDeps, enableDebug, deferredOutput, createMeta, createDebugger, createCustomResourceClass, createCustomProvider, WorkSpaceOptions, WorkSpace, UpdateProps, URN, Tag, StateBackend, State, Stack, S3StateBackend, ResourceNotFound, ResourceMeta, ResourceError, ResourceConfig, ResourceClass, ResourceAlreadyExists, Resource, Provider, ProcedureOptions, Output, OptionalOutput, OptionalInput, Node, Meta, MemoryStateBackend, MemoryLockBackend, LockBackend, Input, Group, GetProps, GetDataProps, Future, FileStateBackend, FileLockBackend, DynamoLockBackend, DeleteProps, DataSourceMeta, DataSourceFunction, DataSource, CustomResourceProvider, CreateProps, Config, AppError, App };
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
// src/node.ts
|
|
2
|
+
var nodeMetaSymbol = Symbol("metadata");
|
|
2
3
|
var isNode = (obj) => {
|
|
3
|
-
|
|
4
|
+
const meta = obj[nodeMetaSymbol];
|
|
5
|
+
return meta && typeof meta === "object" && meta !== null && "tag" in meta && typeof meta.tag === "string";
|
|
4
6
|
};
|
|
7
|
+
function getMeta(node) {
|
|
8
|
+
return node[nodeMetaSymbol];
|
|
9
|
+
}
|
|
5
10
|
var isResource = (obj) => {
|
|
6
|
-
return isNode(obj) && obj
|
|
11
|
+
return isNode(obj) && obj[nodeMetaSymbol].tag === "resource";
|
|
7
12
|
};
|
|
8
13
|
var isDataSource = (obj) => {
|
|
9
|
-
return isNode(obj) && obj
|
|
14
|
+
return isNode(obj) && obj[nodeMetaSymbol].tag === "data";
|
|
10
15
|
};
|
|
11
16
|
|
|
12
17
|
// src/group.ts
|
|
@@ -27,9 +32,10 @@ class Group {
|
|
|
27
32
|
}
|
|
28
33
|
addChild(child) {
|
|
29
34
|
if (isNode(child)) {
|
|
30
|
-
const
|
|
35
|
+
const meta = getMeta(child);
|
|
36
|
+
const duplicate = this.children.filter((c) => isResource(c)).map((c) => getMeta(c)).find((c) => c.type === meta.type && c.logicalId === meta.logicalId);
|
|
31
37
|
if (duplicate) {
|
|
32
|
-
throw new Error(`Duplicate node found: ${
|
|
38
|
+
throw new Error(`Duplicate node found: ${meta.type}:${meta.logicalId}`);
|
|
33
39
|
}
|
|
34
40
|
}
|
|
35
41
|
if (child instanceof Group) {
|
|
@@ -678,26 +684,27 @@ var requiresReplacement = (priorState, proposedState, replaceOnChanges) => {
|
|
|
678
684
|
// src/workspace/procedure/create-resource.ts
|
|
679
685
|
var debug2 = createDebugger("Create");
|
|
680
686
|
var createResource = async (resource, appToken, input, opt) => {
|
|
681
|
-
const
|
|
682
|
-
const
|
|
683
|
-
|
|
687
|
+
const meta = getMeta(resource);
|
|
688
|
+
const provider = findProvider(opt.providers, meta.provider);
|
|
689
|
+
const idempotantToken = createIdempotantToken(appToken, meta.urn, "create");
|
|
690
|
+
debug2(meta.type);
|
|
684
691
|
debug2(input);
|
|
685
692
|
let result;
|
|
686
693
|
try {
|
|
687
694
|
result = await provider.createResource({
|
|
688
|
-
type:
|
|
695
|
+
type: meta.type,
|
|
689
696
|
state: input,
|
|
690
697
|
idempotantToken
|
|
691
698
|
});
|
|
692
699
|
} catch (error) {
|
|
693
|
-
throw ResourceError.wrap(
|
|
700
|
+
throw ResourceError.wrap(meta.urn, meta.type, "create", error);
|
|
694
701
|
}
|
|
695
702
|
return {
|
|
696
703
|
tag: "resource",
|
|
697
704
|
version: result.version,
|
|
698
|
-
type:
|
|
699
|
-
provider:
|
|
700
|
-
input:
|
|
705
|
+
type: meta.type,
|
|
706
|
+
provider: meta.provider,
|
|
707
|
+
input: meta.input,
|
|
701
708
|
output: result.state
|
|
702
709
|
};
|
|
703
710
|
};
|
|
@@ -731,27 +738,28 @@ var getDataSource = async (dataSource, input, opt) => {
|
|
|
731
738
|
// src/workspace/procedure/import-resource.ts
|
|
732
739
|
var debug4 = createDebugger("Import");
|
|
733
740
|
var importResource = async (resource, input, opt) => {
|
|
734
|
-
const
|
|
735
|
-
|
|
741
|
+
const meta = getMeta(resource);
|
|
742
|
+
const provider = findProvider(opt.providers, meta.provider);
|
|
743
|
+
debug4(meta.type);
|
|
736
744
|
debug4(input);
|
|
737
745
|
let result;
|
|
738
746
|
try {
|
|
739
747
|
result = await provider.getResource({
|
|
740
|
-
type:
|
|
748
|
+
type: meta.type,
|
|
741
749
|
state: {
|
|
742
750
|
...input,
|
|
743
|
-
id:
|
|
751
|
+
id: meta.config?.import
|
|
744
752
|
}
|
|
745
753
|
});
|
|
746
754
|
} catch (error) {
|
|
747
|
-
throw ResourceError.wrap(
|
|
755
|
+
throw ResourceError.wrap(meta.urn, meta.type, "import", error);
|
|
748
756
|
}
|
|
749
757
|
return {
|
|
750
758
|
tag: "resource",
|
|
751
759
|
version: result.version,
|
|
752
|
-
type:
|
|
753
|
-
provider:
|
|
754
|
-
input:
|
|
760
|
+
type: meta.type,
|
|
761
|
+
provider: meta.provider,
|
|
762
|
+
input: meta.input,
|
|
755
763
|
output: result.state
|
|
756
764
|
};
|
|
757
765
|
};
|
|
@@ -759,13 +767,14 @@ var importResource = async (resource, input, opt) => {
|
|
|
759
767
|
// src/workspace/procedure/replace-resource.ts
|
|
760
768
|
var debug5 = createDebugger("Replace");
|
|
761
769
|
var replaceResource = async (resource, appToken, priorState, proposedState, opt) => {
|
|
762
|
-
const
|
|
763
|
-
const
|
|
764
|
-
const
|
|
765
|
-
const
|
|
766
|
-
|
|
770
|
+
const meta = getMeta(resource);
|
|
771
|
+
const urn = meta.urn;
|
|
772
|
+
const type = meta.type;
|
|
773
|
+
const provider = findProvider(opt.providers, meta.provider);
|
|
774
|
+
const idempotantToken = createIdempotantToken(appToken, meta.urn, "replace");
|
|
775
|
+
debug5(meta.type);
|
|
767
776
|
debug5(proposedState);
|
|
768
|
-
if (
|
|
777
|
+
if (meta.config?.retainOnDelete) {
|
|
769
778
|
debug5("retain", type);
|
|
770
779
|
} else {
|
|
771
780
|
try {
|
|
@@ -801,20 +810,21 @@ var replaceResource = async (resource, appToken, priorState, proposedState, opt)
|
|
|
801
810
|
// src/workspace/procedure/update-resource.ts
|
|
802
811
|
var debug6 = createDebugger("Update");
|
|
803
812
|
var updateResource = async (resource, appToken, priorState, proposedState, opt) => {
|
|
804
|
-
const
|
|
805
|
-
const
|
|
813
|
+
const meta = getMeta(resource);
|
|
814
|
+
const provider = findProvider(opt.providers, meta.provider);
|
|
815
|
+
const idempotantToken = createIdempotantToken(appToken, meta.urn, "update");
|
|
806
816
|
let result;
|
|
807
|
-
debug6(
|
|
817
|
+
debug6(meta.type);
|
|
808
818
|
debug6(proposedState);
|
|
809
819
|
try {
|
|
810
820
|
result = await provider.updateResource({
|
|
811
|
-
type:
|
|
821
|
+
type: meta.type,
|
|
812
822
|
priorState,
|
|
813
823
|
proposedState,
|
|
814
824
|
idempotantToken
|
|
815
825
|
});
|
|
816
826
|
} catch (error) {
|
|
817
|
-
throw ResourceError.wrap(
|
|
827
|
+
throw ResourceError.wrap(meta.urn, meta.type, "update", error);
|
|
818
828
|
}
|
|
819
829
|
return {
|
|
820
830
|
version: result.version,
|
|
@@ -856,11 +866,12 @@ var deployApp = async (app, opt) => {
|
|
|
856
866
|
const stackState = appState.stacks[stack.urn];
|
|
857
867
|
if (stackState) {
|
|
858
868
|
for (const node of stack.nodes) {
|
|
859
|
-
const
|
|
869
|
+
const meta = getMeta(node);
|
|
870
|
+
const nodeState = stackState.nodes[meta.urn];
|
|
860
871
|
if (nodeState && nodeState.output) {
|
|
861
|
-
graph.add(
|
|
862
|
-
debug7("hydrate",
|
|
863
|
-
|
|
872
|
+
graph.add(meta.urn, [], async () => {
|
|
873
|
+
debug7("hydrate", meta.urn);
|
|
874
|
+
meta.resolve(nodeState.output);
|
|
864
875
|
});
|
|
865
876
|
}
|
|
866
877
|
}
|
|
@@ -888,7 +899,7 @@ var deployApp = async (app, opt) => {
|
|
|
888
899
|
nodes: {}
|
|
889
900
|
};
|
|
890
901
|
for (const [urn, nodeState] of entries(stackState.nodes)) {
|
|
891
|
-
const resource = stack.nodes.find((r) => r
|
|
902
|
+
const resource = stack.nodes.find((r) => getMeta(r).urn === urn);
|
|
892
903
|
if (!resource) {
|
|
893
904
|
graph.add(urn, dependentsOn(allNodes, urn), async () => {
|
|
894
905
|
if (nodeState.tag === "resource") {
|
|
@@ -899,31 +910,33 @@ var deployApp = async (app, opt) => {
|
|
|
899
910
|
}
|
|
900
911
|
}
|
|
901
912
|
for (const node of stack.nodes) {
|
|
902
|
-
const
|
|
913
|
+
const meta = getMeta(node);
|
|
914
|
+
const dependencies = [...meta.dependencies];
|
|
903
915
|
const partialNewResourceState = {
|
|
904
916
|
dependencies,
|
|
905
917
|
lifecycle: isResource(node) ? {
|
|
906
|
-
retainOnDelete: node
|
|
918
|
+
retainOnDelete: getMeta(node).config?.retainOnDelete
|
|
907
919
|
} : undefined
|
|
908
920
|
};
|
|
909
|
-
graph.add(
|
|
921
|
+
graph.add(meta.urn, dependencies, () => {
|
|
910
922
|
return queue(async () => {
|
|
911
|
-
let nodeState = stackState.nodes[
|
|
923
|
+
let nodeState = stackState.nodes[meta.urn];
|
|
912
924
|
let input;
|
|
913
925
|
try {
|
|
914
|
-
input = await resolveInputs(
|
|
926
|
+
input = await resolveInputs(meta.input);
|
|
915
927
|
} catch (error) {
|
|
916
|
-
throw ResourceError.wrap(
|
|
928
|
+
throw ResourceError.wrap(meta.urn, meta.type, "resolve", error);
|
|
917
929
|
}
|
|
918
930
|
if (isDataSource(node)) {
|
|
931
|
+
const meta2 = getMeta(node);
|
|
919
932
|
if (!nodeState) {
|
|
920
|
-
const dataSourceState = await getDataSource(
|
|
921
|
-
nodeState = stackState.nodes[
|
|
933
|
+
const dataSourceState = await getDataSource(meta2, input, opt);
|
|
934
|
+
nodeState = stackState.nodes[meta2.urn] = {
|
|
922
935
|
...dataSourceState,
|
|
923
936
|
...partialNewResourceState
|
|
924
937
|
};
|
|
925
938
|
} else if (!compareState(nodeState.input, input)) {
|
|
926
|
-
const dataSourceState = await getDataSource(
|
|
939
|
+
const dataSourceState = await getDataSource(meta2, input, opt);
|
|
927
940
|
Object.assign(nodeState, {
|
|
928
941
|
...dataSourceState,
|
|
929
942
|
...partialNewResourceState
|
|
@@ -933,25 +946,26 @@ var deployApp = async (app, opt) => {
|
|
|
933
946
|
}
|
|
934
947
|
}
|
|
935
948
|
if (isResource(node)) {
|
|
949
|
+
const meta2 = getMeta(node);
|
|
936
950
|
if (!nodeState) {
|
|
937
|
-
if (
|
|
951
|
+
if (meta2.config?.import) {
|
|
938
952
|
const importedState = await importResource(node, input, opt);
|
|
939
953
|
const newResourceState = await updateResource(node, appState.idempotentToken, importedState.output, input, opt);
|
|
940
|
-
nodeState = stackState.nodes[
|
|
954
|
+
nodeState = stackState.nodes[meta2.urn] = {
|
|
941
955
|
...importedState,
|
|
942
956
|
...newResourceState,
|
|
943
957
|
...partialNewResourceState
|
|
944
958
|
};
|
|
945
959
|
} else {
|
|
946
960
|
const newResourceState = await createResource(node, appState.idempotentToken, input, opt);
|
|
947
|
-
nodeState = stackState.nodes[
|
|
961
|
+
nodeState = stackState.nodes[meta2.urn] = {
|
|
948
962
|
...newResourceState,
|
|
949
963
|
...partialNewResourceState
|
|
950
964
|
};
|
|
951
965
|
}
|
|
952
966
|
} else if (!compareState(nodeState.input, input)) {
|
|
953
967
|
let newResourceState;
|
|
954
|
-
if (requiresReplacement(nodeState.input, input,
|
|
968
|
+
if (requiresReplacement(nodeState.input, input, meta2.config?.replaceOnChanges ?? [])) {
|
|
955
969
|
newResourceState = await replaceResource(node, appState.idempotentToken, nodeState.output, input, opt);
|
|
956
970
|
} else {
|
|
957
971
|
newResourceState = await updateResource(node, appState.idempotentToken, nodeState.output, input, opt);
|
|
@@ -966,7 +980,7 @@ var deployApp = async (app, opt) => {
|
|
|
966
980
|
}
|
|
967
981
|
}
|
|
968
982
|
if (nodeState?.output) {
|
|
969
|
-
|
|
983
|
+
meta.resolve(nodeState.output);
|
|
970
984
|
}
|
|
971
985
|
});
|
|
972
986
|
});
|
|
@@ -995,9 +1009,10 @@ var hydrate = async (app, opt) => {
|
|
|
995
1009
|
const stackState = appState.stacks[stack.urn];
|
|
996
1010
|
if (stackState) {
|
|
997
1011
|
for (const node of stack.nodes) {
|
|
998
|
-
const
|
|
1012
|
+
const meta = getMeta(node);
|
|
1013
|
+
const nodeState = stackState.nodes[meta.urn];
|
|
999
1014
|
if (nodeState && nodeState.output) {
|
|
1000
|
-
|
|
1015
|
+
meta.resolve(nodeState.output);
|
|
1001
1016
|
}
|
|
1002
1017
|
}
|
|
1003
1018
|
}
|
|
@@ -1285,13 +1300,16 @@ var createCustomResourceClass = (providerId, resourceType) => {
|
|
|
1285
1300
|
return new Proxy(class {
|
|
1286
1301
|
}, {
|
|
1287
1302
|
construct(_, [parent, id, input, config]) {
|
|
1288
|
-
const
|
|
1289
|
-
const node = new Proxy({
|
|
1303
|
+
const meta = createMeta("resource", `custom:${providerId}`, parent, resourceType, id, input, config);
|
|
1304
|
+
const node = new Proxy({}, {
|
|
1290
1305
|
get(_2, key) {
|
|
1291
|
-
if (key ===
|
|
1292
|
-
return
|
|
1306
|
+
if (key === nodeMetaSymbol) {
|
|
1307
|
+
return meta;
|
|
1308
|
+
}
|
|
1309
|
+
if (typeof key === "symbol") {
|
|
1310
|
+
return;
|
|
1293
1311
|
}
|
|
1294
|
-
return
|
|
1312
|
+
return meta.output((data) => data[key]);
|
|
1295
1313
|
}
|
|
1296
1314
|
});
|
|
1297
1315
|
parent.add(node);
|
|
@@ -1366,9 +1384,11 @@ var createCustomProvider = (providerId, resourceProviders) => {
|
|
|
1366
1384
|
export {
|
|
1367
1385
|
resolveInputs,
|
|
1368
1386
|
output,
|
|
1387
|
+
nodeMetaSymbol,
|
|
1369
1388
|
isResource,
|
|
1370
1389
|
isNode,
|
|
1371
1390
|
isDataSource,
|
|
1391
|
+
getMeta,
|
|
1372
1392
|
findInputDeps,
|
|
1373
1393
|
enableDebug,
|
|
1374
1394
|
deferredOutput,
|