@l-v-yonsama/multi-platform-database-drivers 1.2.0 → 1.3.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.
Files changed (120) hide show
  1. package/built/src/drivers/AwsDriver.d.ts +6 -0
  2. package/built/src/drivers/AwsDriver.js +84 -4
  3. package/built/src/drivers/AwsDriver.js.map +1 -1
  4. package/built/src/drivers/aws/AwsCloudFormationServiceClient.d.ts +35 -0
  5. package/built/src/drivers/aws/AwsCloudFormationServiceClient.js +197 -0
  6. package/built/src/drivers/aws/AwsCloudFormationServiceClient.js.map +1 -0
  7. package/built/src/drivers/aws/AwsCloudwatchServiceClient.js +1 -4
  8. package/built/src/drivers/aws/AwsCloudwatchServiceClient.js.map +1 -1
  9. package/built/src/drivers/aws/AwsDynamoServiceClient.js +1 -4
  10. package/built/src/drivers/aws/AwsDynamoServiceClient.js.map +1 -1
  11. package/built/src/drivers/aws/AwsS3ServiceClient.js +4 -6
  12. package/built/src/drivers/aws/AwsS3ServiceClient.js.map +1 -1
  13. package/built/src/drivers/aws/AwsSESServiceClient.js +23 -0
  14. package/built/src/drivers/aws/AwsSESServiceClient.js.map +1 -1
  15. package/built/src/drivers/aws/AwsSQSServiceClient.d.ts +1 -0
  16. package/built/src/drivers/aws/AwsSQSServiceClient.js +32 -0
  17. package/built/src/drivers/aws/AwsSQSServiceClient.js.map +1 -1
  18. package/built/src/drivers/aws/AwsSecretsManagerServiceClient.d.ts +19 -0
  19. package/built/src/drivers/aws/AwsSecretsManagerServiceClient.js +118 -0
  20. package/built/src/drivers/aws/AwsSecretsManagerServiceClient.js.map +1 -0
  21. package/built/src/drivers/aws/AwsServiceClient.d.ts +1 -0
  22. package/built/src/drivers/aws/AwsServiceClient.js +11 -0
  23. package/built/src/drivers/aws/AwsServiceClient.js.map +1 -1
  24. package/built/src/drivers/aws/AwsSsmServiceClient.d.ts +19 -0
  25. package/built/src/drivers/aws/AwsSsmServiceClient.js +116 -0
  26. package/built/src/drivers/aws/AwsSsmServiceClient.js.map +1 -0
  27. package/built/src/drivers/aws/index.d.ts +3 -0
  28. package/built/src/drivers/aws/index.js +3 -0
  29. package/built/src/drivers/aws/index.js.map +1 -1
  30. package/built/src/helpers/prompts/AwsPromptHelper.js +93 -2
  31. package/built/src/helpers/prompts/AwsPromptHelper.js.map +1 -1
  32. package/built/src/resource/DbResource.d.ts +21 -3
  33. package/built/src/resource/DbResource.js +82 -1
  34. package/built/src/resource/DbResource.js.map +1 -1
  35. package/built/src/types/drivers/ScanParams.d.ts +15 -1
  36. package/built/src/types/resource/AwsCfnStackAttributes.d.ts +164 -0
  37. package/built/src/types/resource/AwsCfnStackAttributes.js +56 -0
  38. package/built/src/types/resource/AwsCfnStackAttributes.js.map +1 -0
  39. package/built/src/types/resource/AwsSESIdentityAttributes.d.ts +17 -0
  40. package/built/src/types/resource/AwsSESIdentityAttributes.js +3 -0
  41. package/built/src/types/resource/AwsSESIdentityAttributes.js.map +1 -0
  42. package/built/src/types/resource/AwsSQSAttributes.d.ts +1 -0
  43. package/built/src/types/resource/AwsSecretAttributes.d.ts +6 -0
  44. package/built/src/types/resource/AwsSecretAttributes.js +3 -0
  45. package/built/src/types/resource/AwsSecretAttributes.js.map +1 -0
  46. package/built/src/types/resource/AwsServiceType.d.ts +4 -1
  47. package/built/src/types/resource/AwsServiceType.js +3 -0
  48. package/built/src/types/resource/AwsServiceType.js.map +1 -1
  49. package/built/src/types/resource/AwsSsmParameterAttributes.d.ts +12 -0
  50. package/built/src/types/resource/AwsSsmParameterAttributes.js +3 -0
  51. package/built/src/types/resource/AwsSsmParameterAttributes.js.map +1 -0
  52. package/built/src/types/resource/ConnectionSetting.d.ts +2 -2
  53. package/built/src/types/resource/ConnectionSetting.js.map +1 -1
  54. package/built/src/types/resource/ResourceType.d.ts +5 -0
  55. package/built/src/types/resource/ResourceType.js +5 -0
  56. package/built/src/types/resource/ResourceType.js.map +1 -1
  57. package/built/src/types/resource/index.d.ts +4 -0
  58. package/built/src/types/resource/index.js +4 -0
  59. package/built/src/types/resource/index.js.map +1 -1
  60. package/built/src/utils/cfn/applicationDiagram.d.ts +2 -0
  61. package/built/src/utils/cfn/applicationDiagram.js +134 -0
  62. package/built/src/utils/cfn/applicationDiagram.js.map +1 -0
  63. package/built/src/utils/cfn/applicationRelations.d.ts +28 -0
  64. package/built/src/utils/cfn/applicationRelations.js +338 -0
  65. package/built/src/utils/cfn/applicationRelations.js.map +1 -0
  66. package/built/src/utils/cfn/architectureDiagram.d.ts +3 -0
  67. package/built/src/utils/cfn/architectureDiagram.js +238 -0
  68. package/built/src/utils/cfn/architectureDiagram.js.map +1 -0
  69. package/built/src/utils/cfn/architectureTopology.d.ts +128 -0
  70. package/built/src/utils/cfn/architectureTopology.js +523 -0
  71. package/built/src/utils/cfn/architectureTopology.js.map +1 -0
  72. package/built/src/utils/cfn/cfnDependencyGraphDiagram.d.ts +2 -0
  73. package/built/src/utils/cfn/cfnDependencyGraphDiagram.js +198 -0
  74. package/built/src/utils/cfn/cfnDependencyGraphDiagram.js.map +1 -0
  75. package/built/src/utils/cfn/diagramFileModel.d.ts +2 -0
  76. package/built/src/utils/cfn/diagramFileModel.js +178 -0
  77. package/built/src/utils/cfn/diagramFileModel.js.map +1 -0
  78. package/built/src/utils/cfn/drawioApplicationDiagram.d.ts +2 -0
  79. package/built/src/utils/cfn/drawioApplicationDiagram.js +197 -0
  80. package/built/src/utils/cfn/drawioApplicationDiagram.js.map +1 -0
  81. package/built/src/utils/cfn/drawioInfrastructureDiagrams.d.ts +4 -0
  82. package/built/src/utils/cfn/drawioInfrastructureDiagrams.js +444 -0
  83. package/built/src/utils/cfn/drawioInfrastructureDiagrams.js.map +1 -0
  84. package/built/src/utils/cfn/drawioXml.d.ts +25 -0
  85. package/built/src/utils/cfn/drawioXml.js +53 -0
  86. package/built/src/utils/cfn/drawioXml.js.map +1 -0
  87. package/built/src/utils/cfn/generateDiagram.d.ts +2 -0
  88. package/built/src/utils/cfn/generateDiagram.js +23 -0
  89. package/built/src/utils/cfn/generateDiagram.js.map +1 -0
  90. package/built/src/utils/cfn/icons.d.ts +1 -0
  91. package/built/src/utils/cfn/icons.js +60 -0
  92. package/built/src/utils/cfn/icons.js.map +1 -0
  93. package/built/src/utils/cfn/index.d.ts +11 -0
  94. package/built/src/utils/cfn/index.js +33 -0
  95. package/built/src/utils/cfn/index.js.map +1 -0
  96. package/built/src/utils/cfn/intrinsics.d.ts +12 -0
  97. package/built/src/utils/cfn/intrinsics.js +158 -0
  98. package/built/src/utils/cfn/intrinsics.js.map +1 -0
  99. package/built/src/utils/cfn/mermaidFlowchart.d.ts +3 -0
  100. package/built/src/utils/cfn/mermaidFlowchart.js +14 -0
  101. package/built/src/utils/cfn/mermaidFlowchart.js.map +1 -0
  102. package/built/src/utils/cfn/multiAzDeploymentDataPaths.d.ts +27 -0
  103. package/built/src/utils/cfn/multiAzDeploymentDataPaths.js +257 -0
  104. package/built/src/utils/cfn/multiAzDeploymentDataPaths.js.map +1 -0
  105. package/built/src/utils/cfn/naming.d.ts +6 -0
  106. package/built/src/utils/cfn/naming.js +33 -0
  107. package/built/src/utils/cfn/naming.js.map +1 -0
  108. package/built/src/utils/cfn/resourceDependencies.d.ts +2 -0
  109. package/built/src/utils/cfn/resourceDependencies.js +33 -0
  110. package/built/src/utils/cfn/resourceDependencies.js.map +1 -0
  111. package/built/src/utils/cfn/templateParsing.d.ts +3 -0
  112. package/built/src/utils/cfn/templateParsing.js +73 -0
  113. package/built/src/utils/cfn/templateParsing.js.map +1 -0
  114. package/built/src/utils/cfn/viewpoints.d.ts +3 -0
  115. package/built/src/utils/cfn/viewpoints.js +122 -0
  116. package/built/src/utils/cfn/viewpoints.js.map +1 -0
  117. package/built/src/utils/index.d.ts +1 -0
  118. package/built/src/utils/index.js +1 -0
  119. package/built/src/utils/index.js.map +1 -1
  120. package/package.json +9 -1
@@ -0,0 +1,128 @@
1
+ import { DiagramFile, RefValue, TemplateResource } from '../../types';
2
+ type SubnetConnectivity = 'public' | 'private' | 'isolated';
3
+ type TopologySubnetResource = {
4
+ fileIndex: number;
5
+ logicalId: string;
6
+ exportedName?: string;
7
+ detail: TemplateResource;
8
+ };
9
+ type TopologyVpcResource = TopologySubnetResource & {
10
+ placement: string;
11
+ candidateSubnets: TopologySubnet[];
12
+ multiAz?: boolean;
13
+ };
14
+ type TopologyDefaultRoute = {
15
+ kind: 'internet-gateway' | 'nat-gateway' | 'egress-only-internet-gateway';
16
+ fileIndex: number;
17
+ logicalId?: string;
18
+ };
19
+ type TopologySubnet = {
20
+ fileIndex: number;
21
+ logicalId: string;
22
+ exportedName?: string;
23
+ connectivity: SubnetConnectivity;
24
+ cidrBlock: string;
25
+ resources: TopologySubnetResource[];
26
+ defaultRoute?: TopologyDefaultRoute;
27
+ };
28
+ type TopologyAvailabilityZone = {
29
+ id: string;
30
+ name: string;
31
+ subnets: TopologySubnet[];
32
+ };
33
+ type TopologyTarget = {
34
+ sourceFileIndex: number;
35
+ logicalId: RefValue;
36
+ };
37
+ type TopologyTargetGroup = {
38
+ fileIndex: number;
39
+ logicalId: string;
40
+ protocol?: string;
41
+ port?: string;
42
+ targetType?: string;
43
+ targets: TopologyTarget[];
44
+ };
45
+ type TopologyLoadBalancer = {
46
+ fileIndex: number;
47
+ logicalId: string;
48
+ internetFacing: boolean;
49
+ subnets: TopologySubnet[];
50
+ targetGroups: TopologyTargetGroup[];
51
+ listeners: TopologyListener[];
52
+ };
53
+ type TopologyListenerRule = {
54
+ fileIndex: number;
55
+ logicalId: string;
56
+ conditions: string[];
57
+ priority?: string;
58
+ targetGroups: TopologyTargetGroup[];
59
+ };
60
+ type TopologyListener = {
61
+ fileIndex: number;
62
+ logicalId: string;
63
+ protocol?: string;
64
+ port?: string;
65
+ targetGroups: TopologyTargetGroup[];
66
+ rules: TopologyListenerRule[];
67
+ };
68
+ type TopologyIgw = {
69
+ fileIndex: number;
70
+ logicalId: string;
71
+ };
72
+ type TopologyNatGateway = {
73
+ fileIndex: number;
74
+ logicalId: string;
75
+ subnet: TopologySubnet;
76
+ };
77
+ type TopologyVpc = {
78
+ readonly fileIndex: number;
79
+ readonly logicalId: string;
80
+ exportedName?: string;
81
+ readonly cidrBlock: string;
82
+ readonly availabilityZones: TopologyAvailabilityZone[];
83
+ readonly resources: TopologyVpcResource[];
84
+ readonly loadBalancers: TopologyLoadBalancer[];
85
+ readonly natGateways: TopologyNatGateway[];
86
+ igw?: TopologyIgw;
87
+ };
88
+ type TopologyStandaloneResource = {
89
+ fileIndex: number;
90
+ logicalId: string;
91
+ detail: TemplateResource;
92
+ };
93
+ export declare class CfnArchitectureDiagramStructure {
94
+ private readonly diagramFiles;
95
+ readonly vpcs: TopologyVpc[];
96
+ readonly standaloneResources: TopologyStandaloneResource[];
97
+ constructor(diagramFiles: DiagramFile[]);
98
+ getResourceFrom(reference: TopologyTarget): {
99
+ vpc: TopologyVpc;
100
+ subnet?: TopologySubnet;
101
+ resource: TopologySubnetResource | TopologyVpcResource;
102
+ } | undefined;
103
+ private collectVpcs;
104
+ private collectSubnets;
105
+ private getOrCreateAvailabilityZone;
106
+ private classifySubnetConnectivity;
107
+ private attachSubnetAndVpcResources;
108
+ private attachEc2Instance;
109
+ private attachNatGateway;
110
+ private attachDatabase;
111
+ private attachEcsService;
112
+ private attachInternetGateway;
113
+ private collectLoadBalancers;
114
+ private listenersForLoadBalancer;
115
+ private targetGroupsFromActions;
116
+ private targetGroupsForLoadBalancer;
117
+ private toTopologyTargetGroup;
118
+ private topologyResource;
119
+ private addStandalone;
120
+ private normalizeRef;
121
+ private findTemplateResource;
122
+ private getVpc;
123
+ private getSubnet;
124
+ private resourceMatches;
125
+ private vpcForSubnet;
126
+ private allSubnets;
127
+ }
128
+ export {};
@@ -0,0 +1,523 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CfnArchitectureDiagramStructure = void 0;
4
+ const intrinsics_1 = require("./intrinsics");
5
+ const naming_1 = require("./naming");
6
+ const sortSubnets = (a, b) => {
7
+ const priority = {
8
+ public: 0,
9
+ private: 1,
10
+ isolated: 2,
11
+ };
12
+ return priority[a.connectivity] - priority[b.connectivity] ||
13
+ a.cidrBlock.localeCompare(b.cidrBlock);
14
+ };
15
+ const uniqueByIdentity = (values) => Array.from(new Map(values.map((value) => [
16
+ `${value.fileIndex}:${value.logicalId}`,
17
+ value,
18
+ ])).values());
19
+ const displayProperty = (value) => {
20
+ const resolved = (0, intrinsics_1.resolveCfnString)(value);
21
+ if (resolved !== undefined)
22
+ return resolved;
23
+ if (value === undefined || value === null)
24
+ return undefined;
25
+ const serialized = JSON.stringify(value);
26
+ return serialized.length > 80 ? `${serialized.slice(0, 77)}...` : serialized;
27
+ };
28
+ const listenerRuleConditions = (conditions) => {
29
+ if (!Array.isArray(conditions))
30
+ return [];
31
+ return conditions.flatMap((condition) => {
32
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
33
+ const field = (_a = displayProperty(condition === null || condition === void 0 ? void 0 : condition.Field)) !== null && _a !== void 0 ? _a : 'condition';
34
+ const values = (_k = (_h = (_f = (_d = (_b = condition === null || condition === void 0 ? void 0 : condition.Values) !== null && _b !== void 0 ? _b : (_c = condition === null || condition === void 0 ? void 0 : condition.PathPatternConfig) === null || _c === void 0 ? void 0 : _c.Values) !== null && _d !== void 0 ? _d : (_e = condition === null || condition === void 0 ? void 0 : condition.HostHeaderConfig) === null || _e === void 0 ? void 0 : _e.Values) !== null && _f !== void 0 ? _f : (_g = condition === null || condition === void 0 ? void 0 : condition.HttpHeaderConfig) === null || _g === void 0 ? void 0 : _g.Values) !== null && _h !== void 0 ? _h : (_j = condition === null || condition === void 0 ? void 0 : condition.QueryStringConfig) === null || _j === void 0 ? void 0 : _j.Values) !== null && _k !== void 0 ? _k : (_l = condition === null || condition === void 0 ? void 0 : condition.SourceIpConfig) === null || _l === void 0 ? void 0 : _l.Values;
35
+ if (!Array.isArray(values) || values.length === 0)
36
+ return [field];
37
+ return [`${field}: ${values.map((value) => { var _a; return (_a = displayProperty(value)) !== null && _a !== void 0 ? _a : '?'; }).join(', ')}`];
38
+ });
39
+ };
40
+ const availabilityZoneName = (value, file) => {
41
+ var _a, _b, _c, _d, _e;
42
+ if (typeof value === 'string')
43
+ return value;
44
+ const resolutionContext = {
45
+ parameters: file.cfnTemplate.Parameters,
46
+ parameterValues: file.parameterValues,
47
+ pseudoParameters: Object.assign({ 'AWS::StackName': file.groupName }, file.pseudoParameterValues),
48
+ };
49
+ const resolved = (0, intrinsics_1.resolveCfnString)(value, resolutionContext);
50
+ if (resolved)
51
+ return resolved;
52
+ const select = (_a = value === null || value === void 0 ? void 0 : value['Fn::Select']) !== null && _a !== void 0 ? _a : value === null || value === void 0 ? void 0 : value['!Select'];
53
+ if (Array.isArray(select) && select.length >= 2) {
54
+ const index = (0, intrinsics_1.resolveCfnString)(select[0], resolutionContext);
55
+ const getAzs = (_c = (_b = select[1]) === null || _b === void 0 ? void 0 : _b['Fn::GetAZs']) !== null && _c !== void 0 ? _c : (_d = select[1]) === null || _d === void 0 ? void 0 : _d['!GetAZs'];
56
+ if (index !== undefined && getAzs !== undefined) {
57
+ const selectedRegion = (0, intrinsics_1.resolveCfnString)(getAzs, resolutionContext);
58
+ const region = selectedRegion || ((_e = file.pseudoParameterValues) === null || _e === void 0 ? void 0 : _e['AWS::Region']);
59
+ return region
60
+ ? `${region} AZ index ${index}`
61
+ : `Availability Zone index ${index}`;
62
+ }
63
+ }
64
+ return value ? JSON.stringify(value) : 'unspecified';
65
+ };
66
+ class CfnArchitectureDiagramStructure {
67
+ constructor(diagramFiles) {
68
+ this.diagramFiles = diagramFiles;
69
+ this.vpcs = [];
70
+ this.standaloneResources = [];
71
+ diagramFiles.forEach((file) => this.collectVpcs(file));
72
+ diagramFiles.forEach((file) => this.collectSubnets(file));
73
+ diagramFiles.forEach((file) => this.classifySubnetConnectivity(file));
74
+ diagramFiles.forEach((file) => this.attachSubnetAndVpcResources(file));
75
+ diagramFiles.forEach((file) => this.attachInternetGateway(file));
76
+ diagramFiles.forEach((file) => this.collectLoadBalancers(file));
77
+ }
78
+ getResourceFrom(reference) {
79
+ const normalized = this.normalizeRef(reference.logicalId.rawValue, reference.sourceFileIndex);
80
+ for (const vpc of this.vpcs) {
81
+ for (const resource of vpc.resources) {
82
+ if (this.resourceMatches(resource, normalized, reference.sourceFileIndex)) {
83
+ return { vpc, resource };
84
+ }
85
+ }
86
+ for (const az of vpc.availabilityZones) {
87
+ for (const subnet of az.subnets) {
88
+ const resource = subnet.resources.find((candidate) => this.resourceMatches(candidate, normalized, reference.sourceFileIndex));
89
+ if (resource)
90
+ return { vpc, subnet, resource };
91
+ }
92
+ }
93
+ }
94
+ return undefined;
95
+ }
96
+ collectVpcs(file) {
97
+ Object.entries(file.cfnTemplate.Resources)
98
+ .filter(([, resource]) => resource.Type === 'AWS::EC2::VPC')
99
+ .forEach(([logicalId, resource]) => {
100
+ this.vpcs.push({
101
+ fileIndex: file.fileIndex,
102
+ logicalId,
103
+ cidrBlock: (0, naming_1.getCidrBlock)(resource.Properties),
104
+ availabilityZones: [],
105
+ resources: [],
106
+ loadBalancers: [],
107
+ natGateways: [],
108
+ });
109
+ });
110
+ file.outputs.forEach((output) => {
111
+ const vpc = this.vpcs.find((candidate) => candidate.fileIndex === file.fileIndex &&
112
+ candidate.logicalId === output.value.logicalId);
113
+ if (vpc && output.export.rawName)
114
+ vpc.exportedName = output.export.rawName;
115
+ });
116
+ }
117
+ collectSubnets(file) {
118
+ Object.entries(file.cfnTemplate.Resources)
119
+ .filter(([, resource]) => resource.Type === 'AWS::EC2::Subnet')
120
+ .forEach(([logicalId, resource]) => {
121
+ var _a;
122
+ const vpc = this.getVpc(this.normalizeRef((_a = resource.Properties) === null || _a === void 0 ? void 0 : _a.VpcId, file.fileIndex), file.fileIndex);
123
+ if (!vpc) {
124
+ console.warn(`VPC not found for subnet ${file.fileName}:${logicalId}.`);
125
+ return;
126
+ }
127
+ const az = this.getOrCreateAvailabilityZone(vpc, resource, file);
128
+ az.subnets.push({
129
+ fileIndex: file.fileIndex,
130
+ logicalId,
131
+ connectivity: 'isolated',
132
+ cidrBlock: (0, naming_1.getCidrBlock)(resource.Properties),
133
+ resources: [],
134
+ });
135
+ });
136
+ file.outputs.forEach((output) => {
137
+ const subnet = this.allSubnets().find((candidate) => candidate.fileIndex === file.fileIndex &&
138
+ candidate.logicalId === output.value.logicalId);
139
+ if (subnet && output.export.rawName)
140
+ subnet.exportedName = output.export.rawName;
141
+ });
142
+ }
143
+ getOrCreateAvailabilityZone(vpc, subnetResource, file) {
144
+ var _a;
145
+ const availabilityZone = (_a = subnetResource.Properties) === null || _a === void 0 ? void 0 : _a.AvailabilityZone;
146
+ const azName = availabilityZoneName(availabilityZone, file);
147
+ const azId = (0, naming_1.sanitizeLogicalId)(azName);
148
+ let az = vpc.availabilityZones.find((candidate) => candidate.id === azId);
149
+ if (!az) {
150
+ az = { id: azId, name: azName, subnets: [] };
151
+ vpc.availabilityZones.push(az);
152
+ }
153
+ return az;
154
+ }
155
+ classifySubnetConnectivity(file) {
156
+ const resources = file.cfnTemplate.Resources;
157
+ Object.values(resources)
158
+ .filter((resource) => resource.Type === 'AWS::EC2::SubnetRouteTableAssociation')
159
+ .forEach((association) => {
160
+ var _a, _b, _c, _d, _e;
161
+ const subnet = this.getSubnet(this.normalizeRef((_a = association.Properties) === null || _a === void 0 ? void 0 : _a.SubnetId, file.fileIndex), file.fileIndex);
162
+ const routeTableRef = this.normalizeRef((_b = association.Properties) === null || _b === void 0 ? void 0 : _b.RouteTableId, file.fileIndex);
163
+ if (!subnet || routeTableRef.type === 'ImportValue')
164
+ return;
165
+ const routes = Object.values(resources).filter((resource) => {
166
+ var _a, _b, _c;
167
+ return resource.Type === 'AWS::EC2::Route' &&
168
+ this.normalizeRef((_a = resource.Properties) === null || _a === void 0 ? void 0 : _a.RouteTableId, file.fileIndex).value === routeTableRef.value &&
169
+ (((_b = resource.Properties) === null || _b === void 0 ? void 0 : _b.DestinationCidrBlock) === '0.0.0.0/0' ||
170
+ ((_c = resource.Properties) === null || _c === void 0 ? void 0 : _c.DestinationIpv6CidrBlock) === '::/0');
171
+ });
172
+ const internetRoute = routes.find((route) => {
173
+ var _a;
174
+ const gatewayValue = (_a = route.Properties) === null || _a === void 0 ? void 0 : _a.GatewayId;
175
+ if (typeof gatewayValue === 'string')
176
+ return gatewayValue.startsWith('igw-');
177
+ const gateway = this.findTemplateResource(this.normalizeRef(gatewayValue, file.fileIndex), file.fileIndex);
178
+ return (gateway === null || gateway === void 0 ? void 0 : gateway.detail.Type) === 'AWS::EC2::InternetGateway';
179
+ });
180
+ const natRoute = routes.find((route) => { var _a; return ((_a = route.Properties) === null || _a === void 0 ? void 0 : _a.NatGatewayId) !== undefined; });
181
+ const egressOnlyRoute = routes.find((route) => { var _a; return ((_a = route.Properties) === null || _a === void 0 ? void 0 : _a.EgressOnlyInternetGatewayId) !== undefined; });
182
+ if (internetRoute) {
183
+ subnet.connectivity = 'public';
184
+ const gatewayRef = this.normalizeRef((_c = internetRoute.Properties) === null || _c === void 0 ? void 0 : _c.GatewayId, file.fileIndex);
185
+ subnet.defaultRoute = {
186
+ kind: 'internet-gateway',
187
+ fileIndex: file.fileIndex,
188
+ logicalId: gatewayRef.value || undefined,
189
+ };
190
+ }
191
+ else if (natRoute) {
192
+ subnet.connectivity = 'private';
193
+ const natRef = this.normalizeRef((_d = natRoute.Properties) === null || _d === void 0 ? void 0 : _d.NatGatewayId, file.fileIndex);
194
+ subnet.defaultRoute = {
195
+ kind: 'nat-gateway',
196
+ fileIndex: file.fileIndex,
197
+ logicalId: natRef.value || undefined,
198
+ };
199
+ }
200
+ else if (egressOnlyRoute) {
201
+ subnet.connectivity = 'private';
202
+ const gatewayRef = this.normalizeRef((_e = egressOnlyRoute.Properties) === null || _e === void 0 ? void 0 : _e.EgressOnlyInternetGatewayId, file.fileIndex);
203
+ subnet.defaultRoute = {
204
+ kind: 'egress-only-internet-gateway',
205
+ fileIndex: file.fileIndex,
206
+ logicalId: gatewayRef.value || undefined,
207
+ };
208
+ }
209
+ else {
210
+ subnet.connectivity = 'isolated';
211
+ subnet.defaultRoute = undefined;
212
+ }
213
+ });
214
+ this.vpcs.forEach((vpc) => vpc.availabilityZones.forEach((az) => az.subnets.sort(sortSubnets)));
215
+ }
216
+ attachSubnetAndVpcResources(file) {
217
+ Object.entries(file.cfnTemplate.Resources).forEach(([logicalId, resource]) => {
218
+ switch (resource.Type) {
219
+ case 'AWS::EC2::Instance':
220
+ this.attachEc2Instance(file, logicalId, resource);
221
+ break;
222
+ case 'AWS::EC2::NatGateway':
223
+ this.attachNatGateway(file, logicalId, resource);
224
+ break;
225
+ case 'AWS::RDS::DBInstance':
226
+ case 'AWS::RDS::DBCluster':
227
+ this.attachDatabase(file, logicalId, resource);
228
+ break;
229
+ case 'AWS::ECS::Service':
230
+ this.attachEcsService(file, logicalId, resource);
231
+ break;
232
+ }
233
+ });
234
+ }
235
+ attachEc2Instance(file, logicalId, resource) {
236
+ var _a, _b, _c, _d, _e;
237
+ const subnetValue = (_b = (_a = resource.Properties) === null || _a === void 0 ? void 0 : _a.SubnetId) !== null && _b !== void 0 ? _b : (_e = (_d = (_c = resource.Properties) === null || _c === void 0 ? void 0 : _c.NetworkInterfaces) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.SubnetId;
238
+ const subnet = this.getSubnet(this.normalizeRef(subnetValue, file.fileIndex), file.fileIndex);
239
+ if (!subnet) {
240
+ this.addStandalone(file, logicalId, resource, 'EC2 Instance has no resolvable subnet');
241
+ return;
242
+ }
243
+ subnet.resources.push(this.topologyResource(file, logicalId, resource));
244
+ }
245
+ attachNatGateway(file, logicalId, resource) {
246
+ var _a;
247
+ const subnet = this.getSubnet(this.normalizeRef((_a = resource.Properties) === null || _a === void 0 ? void 0 : _a.SubnetId, file.fileIndex), file.fileIndex);
248
+ if (!subnet)
249
+ return;
250
+ const vpc = this.vpcForSubnet(subnet);
251
+ if (!vpc)
252
+ return;
253
+ const topologyResource = this.topologyResource(file, logicalId, resource);
254
+ subnet.resources.push(topologyResource);
255
+ vpc.natGateways.push({ fileIndex: file.fileIndex, logicalId, subnet });
256
+ }
257
+ attachDatabase(file, logicalId, resource) {
258
+ var _a, _b, _c;
259
+ const subnetGroupRef = this.normalizeRef((_a = resource.Properties) === null || _a === void 0 ? void 0 : _a.DBSubnetGroupName, file.fileIndex);
260
+ const subnetGroup = this.findTemplateResource(subnetGroupRef, file.fileIndex);
261
+ const subnetIds = (_b = subnetGroup === null || subnetGroup === void 0 ? void 0 : subnetGroup.detail.Properties) === null || _b === void 0 ? void 0 : _b.SubnetIds;
262
+ if (!subnetGroup || !Array.isArray(subnetIds)) {
263
+ this.addStandalone(file, logicalId, resource, 'database has no resolvable DB subnet group');
264
+ return;
265
+ }
266
+ const subnets = subnetIds
267
+ .map((value) => this.getSubnet(this.normalizeRef(value, subnetGroup.file.fileIndex), subnetGroup.file.fileIndex))
268
+ .filter((subnet) => Boolean(subnet));
269
+ const vpcs = uniqueByIdentity(subnets.flatMap((subnet) => {
270
+ const vpc = this.vpcForSubnet(subnet);
271
+ return vpc ? [vpc] : [];
272
+ }));
273
+ if (subnets.length === 0 || vpcs.length !== 1) {
274
+ this.addStandalone(file, logicalId, resource, 'database subnet group does not resolve to one VPC');
275
+ return;
276
+ }
277
+ vpcs[0].resources.push(Object.assign(Object.assign({}, this.topologyResource(file, logicalId, resource)), { placement: `DB subnet group ${subnetGroup.logicalId}: ${subnets.length} candidate subnets`, candidateSubnets: subnets, multiAz: ((_c = resource.Properties) === null || _c === void 0 ? void 0 : _c.MultiAZ) === true || resource.Type === 'AWS::RDS::DBCluster' }));
278
+ }
279
+ attachEcsService(file, logicalId, resource) {
280
+ var _a, _b, _c;
281
+ const subnetValues = (_c = (_b = (_a = resource.Properties) === null || _a === void 0 ? void 0 : _a.NetworkConfiguration) === null || _b === void 0 ? void 0 : _b.AwsvpcConfiguration) === null || _c === void 0 ? void 0 : _c.Subnets;
282
+ if (!Array.isArray(subnetValues))
283
+ return;
284
+ const subnets = subnetValues
285
+ .map((value) => this.getSubnet(this.normalizeRef(value, file.fileIndex), file.fileIndex))
286
+ .filter((subnet) => Boolean(subnet));
287
+ const vpcs = uniqueByIdentity(subnets.flatMap((subnet) => {
288
+ const vpc = this.vpcForSubnet(subnet);
289
+ return vpc ? [vpc] : [];
290
+ }));
291
+ if (vpcs.length !== 1)
292
+ return;
293
+ vpcs[0].resources.push(Object.assign(Object.assign({}, this.topologyResource(file, logicalId, resource)), { placement: `ECS service: ${subnets.length} configured subnets`, candidateSubnets: subnets }));
294
+ }
295
+ attachInternetGateway(file) {
296
+ Object.values(file.cfnTemplate.Resources)
297
+ .filter((resource) => resource.Type === 'AWS::EC2::VPCGatewayAttachment')
298
+ .forEach((attachment) => {
299
+ var _a, _b;
300
+ const vpc = this.getVpc(this.normalizeRef((_a = attachment.Properties) === null || _a === void 0 ? void 0 : _a.VpcId, file.fileIndex), file.fileIndex);
301
+ const gateway = this.findTemplateResource(this.normalizeRef((_b = attachment.Properties) === null || _b === void 0 ? void 0 : _b.InternetGatewayId, file.fileIndex), file.fileIndex);
302
+ if (vpc && (gateway === null || gateway === void 0 ? void 0 : gateway.detail.Type) === 'AWS::EC2::InternetGateway') {
303
+ vpc.igw = {
304
+ fileIndex: gateway.file.fileIndex,
305
+ logicalId: gateway.logicalId,
306
+ };
307
+ }
308
+ });
309
+ }
310
+ collectLoadBalancers(file) {
311
+ Object.entries(file.cfnTemplate.Resources)
312
+ .filter(([, resource]) => resource.Type === 'AWS::ElasticLoadBalancingV2::LoadBalancer')
313
+ .forEach(([logicalId, resource]) => {
314
+ var _a, _b, _c, _d, _e;
315
+ const subnetValues = (_b = (_a = resource.Properties) === null || _a === void 0 ? void 0 : _a.Subnets) !== null && _b !== void 0 ? _b : (_d = (_c = resource.Properties) === null || _c === void 0 ? void 0 : _c.SubnetMappings) === null || _d === void 0 ? void 0 : _d.map((mapping) => mapping.SubnetId);
316
+ if (!Array.isArray(subnetValues))
317
+ return;
318
+ const subnets = subnetValues
319
+ .map((value) => this.getSubnet(this.normalizeRef(value, file.fileIndex), file.fileIndex))
320
+ .filter((subnet) => Boolean(subnet));
321
+ const vpcs = uniqueByIdentity(subnets.flatMap((subnet) => {
322
+ const vpc = this.vpcForSubnet(subnet);
323
+ return vpc ? [vpc] : [];
324
+ }));
325
+ if (vpcs.length !== 1)
326
+ return;
327
+ const targetGroups = this.targetGroupsForLoadBalancer(file, logicalId);
328
+ vpcs[0].loadBalancers.push({
329
+ fileIndex: file.fileIndex,
330
+ logicalId,
331
+ internetFacing: ((_e = resource.Properties) === null || _e === void 0 ? void 0 : _e.Scheme) !== 'internal',
332
+ subnets,
333
+ targetGroups,
334
+ listeners: this.listenersForLoadBalancer(file, logicalId),
335
+ });
336
+ });
337
+ }
338
+ listenersForLoadBalancer(file, loadBalancerLogicalId) {
339
+ return Object.entries(file.cfnTemplate.Resources)
340
+ .filter(([, resource]) => {
341
+ var _a;
342
+ return resource.Type === 'AWS::ElasticLoadBalancingV2::Listener' &&
343
+ this.normalizeRef((_a = resource.Properties) === null || _a === void 0 ? void 0 : _a.LoadBalancerArn, file.fileIndex).value === loadBalancerLogicalId;
344
+ })
345
+ .map(([logicalId, listener]) => {
346
+ var _a, _b, _c;
347
+ const targetGroups = this.targetGroupsFromActions(file, Array.isArray((_a = listener.Properties) === null || _a === void 0 ? void 0 : _a.DefaultActions)
348
+ ? listener.Properties.DefaultActions
349
+ : []);
350
+ const rules = Object.entries(file.cfnTemplate.Resources)
351
+ .filter(([, resource]) => {
352
+ var _a;
353
+ return resource.Type === 'AWS::ElasticLoadBalancingV2::ListenerRule' &&
354
+ this.normalizeRef((_a = resource.Properties) === null || _a === void 0 ? void 0 : _a.ListenerArn, file.fileIndex).value === logicalId;
355
+ })
356
+ .map(([ruleLogicalId, rule]) => {
357
+ var _a, _b, _c;
358
+ return ({
359
+ fileIndex: file.fileIndex,
360
+ logicalId: ruleLogicalId,
361
+ conditions: listenerRuleConditions((_a = rule.Properties) === null || _a === void 0 ? void 0 : _a.Conditions),
362
+ priority: displayProperty((_b = rule.Properties) === null || _b === void 0 ? void 0 : _b.Priority),
363
+ targetGroups: this.targetGroupsFromActions(file, Array.isArray((_c = rule.Properties) === null || _c === void 0 ? void 0 : _c.Actions) ? rule.Properties.Actions : []),
364
+ });
365
+ });
366
+ return {
367
+ fileIndex: file.fileIndex,
368
+ logicalId,
369
+ protocol: displayProperty((_b = listener.Properties) === null || _b === void 0 ? void 0 : _b.Protocol),
370
+ port: displayProperty((_c = listener.Properties) === null || _c === void 0 ? void 0 : _c.Port),
371
+ targetGroups,
372
+ rules,
373
+ };
374
+ });
375
+ }
376
+ targetGroupsFromActions(file, actions) {
377
+ const targets = actions
378
+ .flatMap((action) => {
379
+ var _a;
380
+ return [
381
+ ...(action.TargetGroupArn ? [action.TargetGroupArn] : []),
382
+ ...(Array.isArray((_a = action.ForwardConfig) === null || _a === void 0 ? void 0 : _a.TargetGroups)
383
+ ? action.ForwardConfig.TargetGroups.flatMap((target) => target.TargetGroupArn ? [target.TargetGroupArn] : [])
384
+ : []),
385
+ ];
386
+ })
387
+ .map((value) => this.findTemplateResource(this.normalizeRef(value, file.fileIndex), file.fileIndex))
388
+ .filter((target) => Boolean(target && target.detail.Type === 'AWS::ElasticLoadBalancingV2::TargetGroup'));
389
+ return uniqueByIdentity(targets.map((target) => this.toTopologyTargetGroup(target)));
390
+ }
391
+ targetGroupsForLoadBalancer(file, loadBalancerLogicalId) {
392
+ const listeners = Object.entries(file.cfnTemplate.Resources)
393
+ .filter(([, resource]) => {
394
+ var _a;
395
+ return resource.Type === 'AWS::ElasticLoadBalancingV2::Listener' &&
396
+ this.normalizeRef((_a = resource.Properties) === null || _a === void 0 ? void 0 : _a.LoadBalancerArn, file.fileIndex).value === loadBalancerLogicalId;
397
+ });
398
+ const listenerIds = new Set(listeners.map(([logicalId]) => logicalId));
399
+ const listenerActions = listeners.flatMap(([, listener]) => {
400
+ var _a;
401
+ return Array.isArray((_a = listener.Properties) === null || _a === void 0 ? void 0 : _a.DefaultActions)
402
+ ? listener.Properties.DefaultActions
403
+ : [];
404
+ });
405
+ const listenerRuleActions = Object.values(file.cfnTemplate.Resources)
406
+ .filter((resource) => {
407
+ var _a;
408
+ return resource.Type === 'AWS::ElasticLoadBalancingV2::ListenerRule' &&
409
+ listenerIds.has(this.normalizeRef((_a = resource.Properties) === null || _a === void 0 ? void 0 : _a.ListenerArn, file.fileIndex).value);
410
+ })
411
+ .flatMap((rule) => { var _a; return Array.isArray((_a = rule.Properties) === null || _a === void 0 ? void 0 : _a.Actions) ? rule.Properties.Actions : []; });
412
+ const targetGroupResources = [...listenerActions, ...listenerRuleActions]
413
+ .flatMap((action) => {
414
+ var _a;
415
+ return [
416
+ ...(action.TargetGroupArn ? [action.TargetGroupArn] : []),
417
+ ...(Array.isArray((_a = action.ForwardConfig) === null || _a === void 0 ? void 0 : _a.TargetGroups)
418
+ ? action.ForwardConfig.TargetGroups.flatMap((target) => target.TargetGroupArn ? [target.TargetGroupArn] : [])
419
+ : []),
420
+ ];
421
+ })
422
+ .map((value) => this.findTemplateResource(this.normalizeRef(value, file.fileIndex), file.fileIndex))
423
+ .filter((target) => Boolean(target && target.detail.Type === 'AWS::ElasticLoadBalancingV2::TargetGroup'));
424
+ return uniqueByIdentity(targetGroupResources.map((targetGroup) => this.toTopologyTargetGroup(targetGroup)));
425
+ }
426
+ toTopologyTargetGroup(targetGroup) {
427
+ var _a, _b, _c, _d;
428
+ const staticTargets = Array.isArray((_a = targetGroup.detail.Properties) === null || _a === void 0 ? void 0 : _a.Targets)
429
+ ? targetGroup.detail.Properties.Targets.map((target) => ({
430
+ sourceFileIndex: targetGroup.file.fileIndex,
431
+ logicalId: this.normalizeRef(target.Id, targetGroup.file.fileIndex),
432
+ }))
433
+ : [];
434
+ const ecsTargets = this.diagramFiles.flatMap((candidateFile) => Object.entries(candidateFile.cfnTemplate.Resources).flatMap(([logicalId, resource]) => {
435
+ var _a;
436
+ if (resource.Type !== 'AWS::ECS::Service' || !Array.isArray((_a = resource.Properties) === null || _a === void 0 ? void 0 : _a.LoadBalancers)) {
437
+ return [];
438
+ }
439
+ const matches = resource.Properties.LoadBalancers.some((binding) => {
440
+ const resolved = this.findTemplateResource(this.normalizeRef(binding.TargetGroupArn, candidateFile.fileIndex), candidateFile.fileIndex);
441
+ return (resolved === null || resolved === void 0 ? void 0 : resolved.file.fileIndex) === targetGroup.file.fileIndex &&
442
+ resolved.logicalId === targetGroup.logicalId;
443
+ });
444
+ return matches ? [{
445
+ sourceFileIndex: candidateFile.fileIndex,
446
+ logicalId: this.normalizeRef({ Ref: logicalId }, candidateFile.fileIndex),
447
+ }] : [];
448
+ }));
449
+ return {
450
+ fileIndex: targetGroup.file.fileIndex,
451
+ logicalId: targetGroup.logicalId,
452
+ protocol: displayProperty((_b = targetGroup.detail.Properties) === null || _b === void 0 ? void 0 : _b.Protocol),
453
+ port: displayProperty((_c = targetGroup.detail.Properties) === null || _c === void 0 ? void 0 : _c.Port),
454
+ targetType: displayProperty((_d = targetGroup.detail.Properties) === null || _d === void 0 ? void 0 : _d.TargetType),
455
+ targets: [...staticTargets, ...ecsTargets],
456
+ };
457
+ }
458
+ topologyResource(file, logicalId, detail) {
459
+ var _a;
460
+ return {
461
+ fileIndex: file.fileIndex,
462
+ logicalId,
463
+ exportedName: (_a = file.outputs.find((output) => output.value.logicalId === logicalId)) === null || _a === void 0 ? void 0 : _a.export.rawName,
464
+ detail,
465
+ };
466
+ }
467
+ addStandalone(file, logicalId, detail, reason) {
468
+ console.warn(`${file.fileName}:${logicalId} ${reason} - rendering it standalone.`);
469
+ this.standaloneResources.push({ fileIndex: file.fileIndex, logicalId, detail });
470
+ }
471
+ normalizeRef(value, sourceFileIndex) {
472
+ var _a, _b;
473
+ const parsed = (0, intrinsics_1.parseRefValue)(value);
474
+ if (parsed.type !== 'ImportValue')
475
+ return parsed;
476
+ const importExpression = (_a = value === null || value === void 0 ? void 0 : value['Fn::ImportValue']) !== null && _a !== void 0 ? _a : value === null || value === void 0 ? void 0 : value['!ImportValue'];
477
+ const sourceFile = this.diagramFiles[sourceFileIndex];
478
+ return Object.assign(Object.assign({}, parsed), { value: (_b = (0, intrinsics_1.resolveCfnString)(importExpression, {
479
+ parameters: sourceFile === null || sourceFile === void 0 ? void 0 : sourceFile.cfnTemplate.Parameters,
480
+ parameterValues: sourceFile === null || sourceFile === void 0 ? void 0 : sourceFile.parameterValues,
481
+ pseudoParameters: sourceFile ? Object.assign({ 'AWS::StackName': sourceFile.groupName }, sourceFile.pseudoParameterValues) : undefined,
482
+ })) !== null && _b !== void 0 ? _b : parsed.value });
483
+ }
484
+ findTemplateResource(reference, sourceFileIndex) {
485
+ if (reference.type === 'ImportValue') {
486
+ for (const file of this.diagramFiles) {
487
+ const output = file.outputs.find((candidate) => candidate.export.rawName === reference.value);
488
+ if (output) {
489
+ const detail = file.cfnTemplate.Resources[output.value.logicalId];
490
+ if (detail)
491
+ return { file, logicalId: output.value.logicalId, detail };
492
+ }
493
+ }
494
+ return undefined;
495
+ }
496
+ const file = this.diagramFiles[sourceFileIndex];
497
+ const detail = file === null || file === void 0 ? void 0 : file.cfnTemplate.Resources[reference.value];
498
+ return detail ? { file, logicalId: reference.value, detail } : undefined;
499
+ }
500
+ getVpc(reference, sourceFileIndex) {
501
+ return this.vpcs.find((vpc) => reference.type === 'ImportValue'
502
+ ? vpc.exportedName === reference.value
503
+ : vpc.fileIndex === sourceFileIndex && vpc.logicalId === reference.value);
504
+ }
505
+ getSubnet(reference, sourceFileIndex) {
506
+ return this.allSubnets().find((subnet) => reference.type === 'ImportValue'
507
+ ? subnet.exportedName === reference.value
508
+ : subnet.fileIndex === sourceFileIndex && subnet.logicalId === reference.value);
509
+ }
510
+ resourceMatches(resource, reference, sourceFileIndex) {
511
+ return reference.type === 'ImportValue'
512
+ ? resource.exportedName === reference.value
513
+ : resource.fileIndex === sourceFileIndex && resource.logicalId === reference.value;
514
+ }
515
+ vpcForSubnet(target) {
516
+ return this.vpcs.find((vpc) => vpc.availabilityZones.some((az) => az.subnets.includes(target)));
517
+ }
518
+ allSubnets() {
519
+ return this.vpcs.flatMap((vpc) => vpc.availabilityZones.flatMap((az) => az.subnets));
520
+ }
521
+ }
522
+ exports.CfnArchitectureDiagramStructure = CfnArchitectureDiagramStructure;
523
+ //# sourceMappingURL=architectureTopology.js.map