@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 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 $: ResourceMeta<I, O>;
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 $: DataSourceMeta<I, O>;
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
- return "$" in obj && typeof obj.$ === "object" && obj.$ !== null && "tag" in obj.$ && typeof obj.$.tag === "string";
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.$.tag === "resource";
11
+ return isNode(obj) && obj[nodeMetaSymbol].tag === "resource";
7
12
  };
8
13
  var isDataSource = (obj) => {
9
- return isNode(obj) && obj.$.tag === "data";
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 duplicate = this.children.filter((c) => isResource(c)).find((c) => c.$.type === child.$.type && c.$.logicalId === child.$.logicalId);
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: ${child.$.type}:${child.$.logicalId}`);
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 provider = findProvider(opt.providers, resource.$.provider);
682
- const idempotantToken = createIdempotantToken(appToken, resource.$.urn, "create");
683
- debug2(resource.$.type);
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: resource.$.type,
695
+ type: meta.type,
689
696
  state: input,
690
697
  idempotantToken
691
698
  });
692
699
  } catch (error) {
693
- throw ResourceError.wrap(resource.$.urn, resource.$.type, "create", error);
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: resource.$.type,
699
- provider: resource.$.provider,
700
- input: resource.$.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 provider = findProvider(opt.providers, resource.$.provider);
735
- debug4(resource.$.type);
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: resource.$.type,
748
+ type: meta.type,
741
749
  state: {
742
750
  ...input,
743
- id: resource.$.config?.import
751
+ id: meta.config?.import
744
752
  }
745
753
  });
746
754
  } catch (error) {
747
- throw ResourceError.wrap(resource.$.urn, resource.$.type, "import", error);
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: resource.$.type,
753
- provider: resource.$.provider,
754
- input: resource.$.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 urn = resource.$.urn;
763
- const type = resource.$.type;
764
- const provider = findProvider(opt.providers, resource.$.provider);
765
- const idempotantToken = createIdempotantToken(appToken, resource.$.urn, "replace");
766
- debug5(resource.$.type);
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 (resource.$.config?.retainOnDelete) {
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 provider = findProvider(opt.providers, resource.$.provider);
805
- const idempotantToken = createIdempotantToken(appToken, resource.$.urn, "update");
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(resource.$.type);
817
+ debug6(meta.type);
808
818
  debug6(proposedState);
809
819
  try {
810
820
  result = await provider.updateResource({
811
- type: resource.$.type,
821
+ type: meta.type,
812
822
  priorState,
813
823
  proposedState,
814
824
  idempotantToken
815
825
  });
816
826
  } catch (error) {
817
- throw ResourceError.wrap(resource.$.urn, resource.$.type, "update", error);
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 nodeState = stackState.nodes[node.$.urn];
869
+ const meta = getMeta(node);
870
+ const nodeState = stackState.nodes[meta.urn];
860
871
  if (nodeState && nodeState.output) {
861
- graph.add(node.$.urn, [], async () => {
862
- debug7("hydrate", node.$.urn);
863
- node.$.resolve(nodeState.output);
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.$.urn === urn);
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 dependencies = [...node.$.dependencies];
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.$.config?.retainOnDelete
918
+ retainOnDelete: getMeta(node).config?.retainOnDelete
907
919
  } : undefined
908
920
  };
909
- graph.add(node.$.urn, dependencies, () => {
921
+ graph.add(meta.urn, dependencies, () => {
910
922
  return queue(async () => {
911
- let nodeState = stackState.nodes[node.$.urn];
923
+ let nodeState = stackState.nodes[meta.urn];
912
924
  let input;
913
925
  try {
914
- input = await resolveInputs(node.$.input);
926
+ input = await resolveInputs(meta.input);
915
927
  } catch (error) {
916
- throw ResourceError.wrap(node.$.urn, node.$.type, "resolve", error);
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(node.$, input, opt);
921
- nodeState = stackState.nodes[node.$.urn] = {
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(node.$, input, opt);
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 (node.$.config?.import) {
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[node.$.urn] = {
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[node.$.urn] = {
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, node.$.config?.replaceOnChanges ?? [])) {
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
- node.$.resolve(nodeState.output);
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 nodeState = stackState.nodes[node.$.urn];
1012
+ const meta = getMeta(node);
1013
+ const nodeState = stackState.nodes[meta.urn];
999
1014
  if (nodeState && nodeState.output) {
1000
- node.$.resolve(nodeState.output);
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 $ = createMeta("resource", `custom:${providerId}`, parent, resourceType, id, input, config);
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 $.output((data) => data[key]);
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@terraforge/core",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",