@cdklabs/cdk-appmod-catalog-blueprints 1.0.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 (105) hide show
  1. package/.jsii +8644 -0
  2. package/LICENSE +202 -0
  3. package/README.md +212 -0
  4. package/lib/document-processing/agentic-document-processing.d.ts +16 -0
  5. package/lib/document-processing/agentic-document-processing.js +90 -0
  6. package/lib/document-processing/base-document-processing.d.ts +189 -0
  7. package/lib/document-processing/base-document-processing.js +509 -0
  8. package/lib/document-processing/bedrock-document-processing.d.ts +167 -0
  9. package/lib/document-processing/bedrock-document-processing.js +297 -0
  10. package/lib/document-processing/index.d.ts +3 -0
  11. package/lib/document-processing/index.js +20 -0
  12. package/lib/document-processing/resources/default-bedrock-invoke/index.py +63 -0
  13. package/lib/document-processing/resources/default-bedrock-invoke/requirements.txt +4 -0
  14. package/lib/document-processing/resources/default-doc-retrieval-lambda/index.mjs +92 -0
  15. package/lib/document-processing/resources/default-doc-retrieval-lambda/package.json +10 -0
  16. package/lib/document-processing/resources/default-error-handler/index.js +46 -0
  17. package/lib/document-processing/resources/default-error-handler/package.json +4 -0
  18. package/lib/document-processing/resources/default-image-processor/classifier.mjs +665 -0
  19. package/lib/document-processing/resources/default-image-processor/extractors.mjs +465 -0
  20. package/lib/document-processing/resources/default-image-processor/index.mjs +143 -0
  21. package/lib/document-processing/resources/default-image-processor/package-lock.json +12 -0
  22. package/lib/document-processing/resources/default-image-processor/package.json +4 -0
  23. package/lib/document-processing/resources/default-image-validator/index.mjs +76 -0
  24. package/lib/document-processing/resources/default-image-validator/package-lock.json +154 -0
  25. package/lib/document-processing/resources/default-image-validator/package.json +7 -0
  26. package/lib/document-processing/resources/default-pdf-processor/index.js +46 -0
  27. package/lib/document-processing/resources/default-pdf-validator/index.js +36 -0
  28. package/lib/document-processing/resources/default-sqs-consumer/index.py +111 -0
  29. package/lib/document-processing/resources/default-sqs-consumer/requirements.txt +4 -0
  30. package/lib/document-processing/resources/default-sqs-consumer/sample_payload.json +20 -0
  31. package/lib/document-processing/resources/default-sqs-consumer/sample_payload_multi.json +24 -0
  32. package/lib/document-processing/resources/default-strands-agent/index.py +111 -0
  33. package/lib/document-processing/resources/default-strands-agent/requirements.txt +6 -0
  34. package/lib/document-processing/tests/agentic-document-processing-nag.test.d.ts +1 -0
  35. package/lib/document-processing/tests/agentic-document-processing-nag.test.js +107 -0
  36. package/lib/document-processing/tests/agentic-document-processing.test.d.ts +1 -0
  37. package/lib/document-processing/tests/agentic-document-processing.test.js +125 -0
  38. package/lib/document-processing/tests/bedrock-document-processing-nag.test.d.ts +1 -0
  39. package/lib/document-processing/tests/bedrock-document-processing-nag.test.js +101 -0
  40. package/lib/document-processing/tests/bedrock-document-processing.test.d.ts +1 -0
  41. package/lib/document-processing/tests/bedrock-document-processing.test.js +79 -0
  42. package/lib/framework/custom-resource/default-runtimes.d.ts +21 -0
  43. package/lib/framework/custom-resource/default-runtimes.js +34 -0
  44. package/lib/framework/custom-resource/index.d.ts +1 -0
  45. package/lib/framework/custom-resource/index.js +18 -0
  46. package/lib/framework/foundation/access-log.d.ts +69 -0
  47. package/lib/framework/foundation/access-log.js +121 -0
  48. package/lib/framework/foundation/eventbridge-broker.d.ts +18 -0
  49. package/lib/framework/foundation/eventbridge-broker.js +42 -0
  50. package/lib/framework/foundation/index.d.ts +3 -0
  51. package/lib/framework/foundation/index.js +20 -0
  52. package/lib/framework/foundation/network.d.ts +19 -0
  53. package/lib/framework/foundation/network.js +83 -0
  54. package/lib/framework/index.d.ts +2 -0
  55. package/lib/framework/index.js +19 -0
  56. package/lib/framework/quickstart/base-quickstart.d.ts +30 -0
  57. package/lib/framework/quickstart/base-quickstart.js +30 -0
  58. package/lib/index.d.ts +4 -0
  59. package/lib/index.js +21 -0
  60. package/lib/tsconfig.tsbuildinfo +1 -0
  61. package/lib/utilities/cdk-nag-config.d.ts +42 -0
  62. package/lib/utilities/cdk-nag-config.js +194 -0
  63. package/lib/utilities/data-loader-lambda/index.py +282 -0
  64. package/lib/utilities/data-loader-lambda/requirements.txt +3 -0
  65. package/lib/utilities/data-loader.d.ts +173 -0
  66. package/lib/utilities/data-loader.js +447 -0
  67. package/lib/utilities/index.d.ts +3 -0
  68. package/lib/utilities/index.js +20 -0
  69. package/lib/utilities/lambda-iam-utils.d.ts +145 -0
  70. package/lib/utilities/lambda-iam-utils.js +235 -0
  71. package/lib/utilities/lambda_layers/data-masking/layer-construct.d.ts +42 -0
  72. package/lib/utilities/lambda_layers/data-masking/layer-construct.js +53 -0
  73. package/lib/utilities/lambda_layers/data-masking/layer-construct.ts +88 -0
  74. package/lib/utilities/observability/bedrock-observability.d.ts +18 -0
  75. package/lib/utilities/observability/bedrock-observability.js +131 -0
  76. package/lib/utilities/observability/cloudfront-distribution-observability-property-injector.d.ts +6 -0
  77. package/lib/utilities/observability/cloudfront-distribution-observability-property-injector.js +22 -0
  78. package/lib/utilities/observability/index.d.ts +6 -0
  79. package/lib/utilities/observability/index.js +25 -0
  80. package/lib/utilities/observability/lambda-observability-property-injector.d.ts +8 -0
  81. package/lib/utilities/observability/lambda-observability-property-injector.js +43 -0
  82. package/lib/utilities/observability/log-group-data-protection-props.d.ts +19 -0
  83. package/lib/utilities/observability/log-group-data-protection-props.js +5 -0
  84. package/lib/utilities/observability/observability.d.ts +83 -0
  85. package/lib/utilities/observability/observability.js +278 -0
  86. package/lib/utilities/observability/observable.d.ts +32 -0
  87. package/lib/utilities/observability/observable.js +3 -0
  88. package/lib/utilities/observability/powertools-config.d.ts +3 -0
  89. package/lib/utilities/observability/powertools-config.js +25 -0
  90. package/lib/utilities/observability/resources/bedrock-manage-logging-configuration/index.py +27 -0
  91. package/lib/utilities/observability/state-machine-observability-property-injector.d.ts +8 -0
  92. package/lib/utilities/observability/state-machine-observability-property-injector.js +49 -0
  93. package/lib/utilities/tests/data-loader-nag.test.d.ts +1 -0
  94. package/lib/utilities/tests/data-loader-nag.test.js +432 -0
  95. package/lib/utilities/tests/data-loader.test.d.ts +1 -0
  96. package/lib/utilities/tests/data-loader.test.js +284 -0
  97. package/lib/webapp/frontend-construct.d.ts +136 -0
  98. package/lib/webapp/frontend-construct.js +253 -0
  99. package/lib/webapp/index.d.ts +1 -0
  100. package/lib/webapp/index.js +18 -0
  101. package/lib/webapp/tests/frontend-construct-nag.test.d.ts +1 -0
  102. package/lib/webapp/tests/frontend-construct-nag.test.js +266 -0
  103. package/lib/webapp/tests/frontend-construct.test.d.ts +1 -0
  104. package/lib/webapp/tests/frontend-construct.test.js +385 -0
  105. package/package.json +183 -0
@@ -0,0 +1,284 @@
1
+ "use strict";
2
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
6
+ const assertions_1 = require("aws-cdk-lib/assertions");
7
+ const aws_ec2_1 = require("aws-cdk-lib/aws-ec2");
8
+ const aws_rds_1 = require("aws-cdk-lib/aws-rds");
9
+ const aws_secretsmanager_1 = require("aws-cdk-lib/aws-secretsmanager");
10
+ const data_loader_1 = require("../data-loader");
11
+ describe('DataLoader', () => {
12
+ let app;
13
+ let stack;
14
+ let vpc;
15
+ let cluster;
16
+ let secret;
17
+ let securityGroup;
18
+ let template;
19
+ // Use beforeAll instead of beforeEach to avoid recreating infrastructure
20
+ beforeAll(() => {
21
+ app = new aws_cdk_lib_1.App();
22
+ stack = new aws_cdk_lib_1.Stack(app, 'TestStack');
23
+ // Create VPC
24
+ vpc = new aws_ec2_1.Vpc(stack, 'TestVpc');
25
+ // Create security group for database access
26
+ securityGroup = new aws_ec2_1.SecurityGroup(stack, 'DatabaseSecurityGroup', {
27
+ vpc: vpc,
28
+ description: 'Security group for database access',
29
+ });
30
+ // Create database secret
31
+ secret = new aws_secretsmanager_1.Secret(stack, 'DatabaseSecret', {
32
+ generateSecretString: {
33
+ secretStringTemplate: JSON.stringify({ username: 'testuser' }),
34
+ generateStringKey: 'password',
35
+ excludeCharacters: '"@/\\',
36
+ },
37
+ });
38
+ // Create Aurora cluster
39
+ cluster = new aws_rds_1.DatabaseCluster(stack, 'TestCluster', {
40
+ engine: aws_rds_1.DatabaseClusterEngine.auroraPostgres({
41
+ version: aws_rds_1.AuroraPostgresEngineVersion.VER_13_7,
42
+ }),
43
+ credentials: {
44
+ username: 'testuser',
45
+ password: secret.secretValueFromJson('password'),
46
+ },
47
+ vpc,
48
+ writer: aws_rds_1.ClusterInstance.provisioned('writer', {
49
+ instanceType: aws_ec2_1.InstanceType.of(aws_ec2_1.InstanceClass.T3, aws_ec2_1.InstanceSize.MEDIUM),
50
+ }),
51
+ });
52
+ // Create all DataLoader instances first
53
+ new data_loader_1.DataLoader(stack, 'BasicDataLoader', {
54
+ databaseConfig: {
55
+ engine: data_loader_1.DatabaseEngine.POSTGRESQL,
56
+ cluster: cluster,
57
+ secret: secret,
58
+ databaseName: 'testdb',
59
+ vpc: vpc,
60
+ securityGroup: securityGroup,
61
+ },
62
+ fileInputs: [
63
+ {
64
+ filePath: 's3://test-bucket/test-data.sql',
65
+ fileType: data_loader_1.FileType.SQL,
66
+ },
67
+ ],
68
+ });
69
+ new data_loader_1.DataLoader(stack, 'MultiFileDataLoader', {
70
+ databaseConfig: {
71
+ engine: data_loader_1.DatabaseEngine.MYSQL,
72
+ cluster: cluster,
73
+ secret: secret,
74
+ databaseName: 'testdb',
75
+ vpc: vpc,
76
+ securityGroup: securityGroup,
77
+ },
78
+ fileInputs: [
79
+ {
80
+ filePath: 's3://test-bucket/schema.sql',
81
+ fileType: data_loader_1.FileType.SQL,
82
+ executionOrder: 1,
83
+ },
84
+ {
85
+ filePath: 's3://test-bucket/data.sql',
86
+ fileType: data_loader_1.FileType.MYSQLDUMP,
87
+ executionOrder: 2,
88
+ },
89
+ ],
90
+ });
91
+ new data_loader_1.DataLoader(stack, 'CustomDataLoader', {
92
+ databaseConfig: {
93
+ engine: data_loader_1.DatabaseEngine.POSTGRESQL,
94
+ cluster: cluster,
95
+ secret: secret,
96
+ databaseName: 'testdb',
97
+ vpc: vpc,
98
+ securityGroup: securityGroup,
99
+ },
100
+ fileInputs: [
101
+ {
102
+ filePath: 's3://test-bucket/test-data.sql',
103
+ fileType: data_loader_1.FileType.SQL,
104
+ },
105
+ ],
106
+ removalPolicy: aws_cdk_lib_1.RemovalPolicy.RETAIN,
107
+ memorySize: 2048,
108
+ });
109
+ // Generate template once after all constructs are created
110
+ template = assertions_1.Template.fromStack(stack);
111
+ });
112
+ describe('Basic functionality', () => {
113
+ test('creates DataLoader construct with minimal configuration', () => {
114
+ expect(stack.node.findChild('BasicDataLoader')).toBeDefined();
115
+ });
116
+ test('creates expected AWS resources', () => {
117
+ template.hasResourceProperties('AWS::S3::Bucket', {
118
+ BucketEncryption: {
119
+ ServerSideEncryptionConfiguration: [
120
+ {
121
+ ServerSideEncryptionByDefault: {
122
+ SSEAlgorithm: 'AES256',
123
+ },
124
+ },
125
+ ],
126
+ },
127
+ });
128
+ template.hasResourceProperties('AWS::StepFunctions::StateMachine', {});
129
+ template.hasResourceProperties('AWS::Lambda::Function', {
130
+ Runtime: 'python3.11',
131
+ Handler: 'index.handler',
132
+ });
133
+ });
134
+ test('supports multiple file inputs with execution order', () => {
135
+ expect(stack.node.findChild('MultiFileDataLoader')).toBeDefined();
136
+ });
137
+ test('supports custom configuration options', () => {
138
+ expect(stack.node.findChild('CustomDataLoader')).toBeDefined();
139
+ template.hasResourceProperties('AWS::Lambda::Function', {
140
+ MemorySize: 2048,
141
+ });
142
+ });
143
+ });
144
+ describe('Validation', () => {
145
+ test('throws error when databaseConfig is missing', () => {
146
+ expect(() => {
147
+ new data_loader_1.DataLoader(stack, 'InvalidConfigDataLoader', {
148
+ databaseConfig: undefined,
149
+ fileInputs: [
150
+ {
151
+ filePath: 's3://test-bucket/test-data.sql',
152
+ fileType: data_loader_1.FileType.SQL,
153
+ },
154
+ ],
155
+ });
156
+ }).toThrow('databaseConfig is required');
157
+ });
158
+ test('throws error when both cluster and instance are missing', () => {
159
+ expect(() => {
160
+ new data_loader_1.DataLoader(stack, 'NoClusterDataLoader', {
161
+ databaseConfig: {
162
+ engine: data_loader_1.DatabaseEngine.POSTGRESQL,
163
+ secret: secret,
164
+ databaseName: 'testdb',
165
+ vpc: vpc,
166
+ securityGroup: securityGroup,
167
+ },
168
+ fileInputs: [
169
+ {
170
+ filePath: 's3://test-bucket/test-data.sql',
171
+ fileType: data_loader_1.FileType.SQL,
172
+ },
173
+ ],
174
+ });
175
+ }).toThrow('Either cluster or instance must be provided in databaseConfig');
176
+ });
177
+ test('throws error when fileInputs is empty', () => {
178
+ expect(() => {
179
+ new data_loader_1.DataLoader(stack, 'EmptyInputsDataLoader', {
180
+ databaseConfig: {
181
+ engine: data_loader_1.DatabaseEngine.POSTGRESQL,
182
+ cluster: cluster,
183
+ secret: secret,
184
+ databaseName: 'testdb',
185
+ vpc: vpc,
186
+ securityGroup: securityGroup,
187
+ },
188
+ fileInputs: [],
189
+ });
190
+ }).toThrow('At least one file input is required');
191
+ });
192
+ test('throws error when MySQL engine is used with PostgreSQL dump', () => {
193
+ expect(() => {
194
+ new data_loader_1.DataLoader(stack, 'MySQLPgDumpDataLoader', {
195
+ databaseConfig: {
196
+ engine: data_loader_1.DatabaseEngine.MYSQL,
197
+ cluster: cluster,
198
+ secret: secret,
199
+ databaseName: 'testdb',
200
+ vpc: vpc,
201
+ securityGroup: securityGroup,
202
+ },
203
+ fileInputs: [
204
+ {
205
+ filePath: 's3://test-bucket/test-data.sql',
206
+ fileType: data_loader_1.FileType.PGDUMP,
207
+ },
208
+ ],
209
+ });
210
+ }).toThrow('PostgreSQL dump files cannot be used with MySQL databases');
211
+ });
212
+ test('throws error when PostgreSQL engine is used with MySQL dump', () => {
213
+ expect(() => {
214
+ new data_loader_1.DataLoader(stack, 'PostgreSQLMySQLDumpDataLoader', {
215
+ databaseConfig: {
216
+ engine: data_loader_1.DatabaseEngine.POSTGRESQL,
217
+ cluster: cluster,
218
+ secret: secret,
219
+ databaseName: 'testdb',
220
+ vpc: vpc,
221
+ securityGroup: securityGroup,
222
+ },
223
+ fileInputs: [
224
+ {
225
+ filePath: 's3://test-bucket/test-data.sql',
226
+ fileType: data_loader_1.FileType.MYSQLDUMP,
227
+ },
228
+ ],
229
+ });
230
+ }).toThrow('MySQL dump files cannot be used with PostgreSQL databases');
231
+ });
232
+ });
233
+ describe('Removal Policy', () => {
234
+ test('applies custom removal policy to resources', () => {
235
+ expect(stack.node.findChild('CustomDataLoader')).toBeDefined();
236
+ template.hasResource('AWS::S3::Bucket', {
237
+ DeletionPolicy: 'Retain',
238
+ UpdateReplacePolicy: 'Retain',
239
+ });
240
+ });
241
+ test('uses default DESTROY removal policy when not specified', () => {
242
+ expect(stack.node.findChild('BasicDataLoader')).toBeDefined();
243
+ template.hasResource('AWS::S3::Bucket', {
244
+ DeletionPolicy: 'Delete',
245
+ UpdateReplacePolicy: 'Delete',
246
+ });
247
+ });
248
+ });
249
+ describe('Security', () => {
250
+ test('creates resources with proper security configurations', () => {
251
+ template.hasResourceProperties('AWS::S3::Bucket', {
252
+ PublicAccessBlockConfiguration: {
253
+ BlockPublicAcls: true,
254
+ BlockPublicPolicy: true,
255
+ IgnorePublicAcls: true,
256
+ RestrictPublicBuckets: true,
257
+ },
258
+ });
259
+ template.hasResourceProperties('AWS::Lambda::Function', {
260
+ Handler: 'index.handler',
261
+ Runtime: 'python3.11',
262
+ });
263
+ const lambdaFunctions = template.findResources('AWS::Lambda::Function');
264
+ const hasVpcConfig = Object.values(lambdaFunctions).some((fn) => fn.Properties?.VpcConfig?.SubnetIds);
265
+ expect(hasVpcConfig).toBe(true);
266
+ });
267
+ test('grants appropriate IAM permissions', () => {
268
+ template.hasResourceProperties('AWS::IAM::Role', {
269
+ AssumeRolePolicyDocument: {
270
+ Statement: [
271
+ {
272
+ Action: 'sts:AssumeRole',
273
+ Effect: 'Allow',
274
+ Principal: {
275
+ Service: 'lambda.amazonaws.com',
276
+ },
277
+ },
278
+ ],
279
+ },
280
+ });
281
+ });
282
+ });
283
+ });
284
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YS1sb2FkZXIudGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3VzZS1jYXNlcy91dGlsaXRpZXMvdGVzdHMvZGF0YS1sb2FkZXIudGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEscUVBQXFFO0FBQ3JFLHNDQUFzQzs7QUFFdEMsNkNBQXdEO0FBQ3hELHVEQUFrRDtBQUNsRCxpREFBb0c7QUFDcEcsaURBQTJIO0FBQzNILHVFQUF3RDtBQUN4RCxnREFBc0U7QUFFdEUsUUFBUSxDQUFDLFlBQVksRUFBRSxHQUFHLEVBQUU7SUFDMUIsSUFBSSxHQUFRLENBQUM7SUFDYixJQUFJLEtBQVksQ0FBQztJQUNqQixJQUFJLEdBQVEsQ0FBQztJQUNiLElBQUksT0FBd0IsQ0FBQztJQUM3QixJQUFJLE1BQWMsQ0FBQztJQUNuQixJQUFJLGFBQTRCLENBQUM7SUFDakMsSUFBSSxRQUFrQixDQUFDO0lBRXZCLHlFQUF5RTtJQUN6RSxTQUFTLENBQUMsR0FBRyxFQUFFO1FBQ2IsR0FBRyxHQUFHLElBQUksaUJBQUcsRUFBRSxDQUFDO1FBQ2hCLEtBQUssR0FBRyxJQUFJLG1CQUFLLENBQUMsR0FBRyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBRXBDLGFBQWE7UUFDYixHQUFHLEdBQUcsSUFBSSxhQUFHLENBQUMsS0FBSyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBRWhDLDRDQUE0QztRQUM1QyxhQUFhLEdBQUcsSUFBSSx1QkFBYSxDQUFDLEtBQUssRUFBRSx1QkFBdUIsRUFBRTtZQUNoRSxHQUFHLEVBQUUsR0FBRztZQUNSLFdBQVcsRUFBRSxvQ0FBb0M7U0FDbEQsQ0FBQyxDQUFDO1FBRUgseUJBQXlCO1FBQ3pCLE1BQU0sR0FBRyxJQUFJLDJCQUFNLENBQUMsS0FBSyxFQUFFLGdCQUFnQixFQUFFO1lBQzNDLG9CQUFvQixFQUFFO2dCQUNwQixvQkFBb0IsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsUUFBUSxFQUFF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QUFJO29CQUN0QixxQkFBcUIsRUFBRSxJQUFJO2lCQUM1QjthQUNGLENBQUMsQ0FBQztZQUVILFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyx1QkFBdUIsRUFBRTtnQkFDdEQsT0FBTyxFQUFFLGVBQWU7Z0JBQ3hCLE9BQU8sRUFBRSxZQUFZO2FBQ3RCLENBQUMsQ0FBQztZQUVILE1BQU0sZUFBZSxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsdUJBQXVCLENBQUMsQ0FBQztZQUN4RSxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEVBQU8sRUFBRSxFQUFFLENBQ25FLEVBQUUsQ0FBQyxVQUFVLEVBQUUsU0FBUyxFQUFFLFNBQVMsQ0FDcEMsQ0FBQztZQUNGLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDbEMsQ0FBQyxDQUFDLENBQUM7UUFFSCxJQUFJLENBQUMsb0NBQW9DLEVBQUUsR0FBRyxFQUFFO1lBQzlDLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyxnQkFBZ0IsRUFBRTtnQkFDL0Msd0JBQXdCLEVBQUU7b0JBQ3hCLFNBQVMsRUFBRTt3QkFDVDs0QkFDRSxNQUFNLEVBQUUsZ0JBQWdCOzRCQUN4QixNQUFNLEVBQUUsT0FBTzs0QkFDZixTQUFTLEVBQUU7Z0NBQ1QsT0FBTyxFQUFFLHNCQUFzQjs2QkFDaEM7eUJBQ0Y7cUJBQ0Y7aUJBQ0Y7YUFDRixDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgQW1hem9uLmNvbSwgSW5jLiBvciBpdHMgYWZmaWxpYXRlcy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IEFwcCwgUmVtb3ZhbFBvbGljeSwgU3RhY2sgfSBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgeyBUZW1wbGF0ZSB9IGZyb20gJ2F3cy1jZGstbGliL2Fzc2VydGlvbnMnO1xuaW1wb3J0IHsgSW5zdGFuY2VDbGFzcywgSW5zdGFuY2VTaXplLCBJbnN0YW5jZVR5cGUsIFZwYywgU2VjdXJpdHlHcm91cCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1lYzInO1xuaW1wb3J0IHsgRGF0YWJhc2VDbHVzdGVyLCBEYXRhYmFzZUNsdXN0ZXJFbmdpbmUsIEF1cm9yYVBvc3RncmVzRW5naW5lVmVyc2lvbiwgQ2x1c3Rlckluc3RhbmNlIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXJkcyc7XG5pbXBvcnQgeyBTZWNyZXQgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtc2VjcmV0c21hbmFnZXInO1xuaW1wb3J0IHsgRGF0YUxvYWRlciwgRGF0YWJhc2VFbmdpbmUsIEZpbGVUeXBlIH0gZnJvbSAnLi4vZGF0YS1sb2FkZXInO1xuXG5kZXNjcmliZSgnRGF0YUxvYWRlcicsICgpID0+IHtcbiAgbGV0IGFwcDogQXBwO1xuICBsZXQgc3RhY2s6IFN0YWNrO1xuICBsZXQgdnBjOiBWcGM7XG4gIGxldCBjbHVzdGVyOiBEYXRhYmFzZUNsdXN0ZXI7XG4gIGxldCBzZWNyZXQ6IFNlY3JldDtcbiAgbGV0IHNlY3VyaXR5R3JvdXA6IFNlY3VyaXR5R3JvdXA7XG4gIGxldCB0ZW1wbGF0ZTogVGVtcGxhdGU7XG5cbiAgLy8gVXNlIGJlZm9yZUFsbCBpbnN0ZWFkIG9mIGJlZm9yZUVhY2ggdG8gYXZvaWQgcmVjcmVhdGluZyBpbmZyYXN0cnVjdHVyZVxuICBiZWZvcmVBbGwoKCkgPT4ge1xuICAgIGFwcCA9IG5ldyBBcHAoKTtcbiAgICBzdGFjayA9IG5ldyBTdGFjayhhcHAsICdUZXN0U3RhY2snKTtcblxuICAgIC8vIENyZWF0ZSBWUENcbiAgICB2cGMgPSBuZXcgVnBjKHN0YWNrLCAnVGVzdFZwYycpO1xuXG4gICAgLy8gQ3JlYXRlIHNlY3VyaXR5IGdyb3VwIGZvciBkYXRhYmFzZSBhY2Nlc3NcbiAgICBzZWN1cml0eUdyb3VwID0gbmV3IFNlY3VyaXR5R3JvdXAoc3RhY2ssICdEYXRhYmFzZVNlY3VyaXR5R3JvdXAnLCB7XG4gICAgICB2cGM6IHZwYyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnU2VjdXJpdHkgZ3JvdXAgZm9yIGRhdGFiYXNlIGFjY2VzcycsXG4gICAgfSk7XG5cbiAgICAvLyBDcmVhdGUgZGF0YWJhc2Ugc2VjcmV0XG4gICAgc2VjcmV0ID0gbmV3IFNlY3JldChzdGFjaywgJ0RhdGFiYXNlU2VjcmV0Jywge1xuICAgICAgZ2VuZXJhdGVTZWNyZXRTdHJpbmc6IHtcbiAgICAgICAgc2VjcmV0U3RyaW5nVGVtcGxhdGU6IEpTT04uc3RyaW5naWZ5KHsgdXNlcm5hbWU6ICd0ZXN0dXNlcicgfSksXG4gICAgICAgIGdlbmVyYXRlU3RyaW5nS2V5OiAncGFzc3dvcmQnLFxuICAgICAgICBleGNsdWRlQ2hhcmFjdGVyczogJ1wiQC9cXFxcJyxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICAvLyBDcmVhdGUgQXVyb3JhIGNsdXN0ZXJcbiAgICBjbHVzdGVyID0gbmV3IERhdGFiYXNlQ2x1c3RlcihzdGFjaywgJ1Rlc3RDbHVzdGVyJywge1xuICAgICAgZW5naW5lOiBEYXRhYmFzZUNsdXN0ZXJFbmdpbmUuYXVyb3JhUG9zdGdyZXMoe1xuICAgICAgICB2ZXJzaW9uOiBBdXJvcmFQb3N0Z3Jlc0VuZ2luZVZlcnNpb24uVkVSXzEzXzcsXG4gICAgICB9KSxcbiAgICAgIGNyZWRlbnRpYWxzOiB7XG4gICAgICAgIHVzZXJuYW1lOiAndGVzdHVzZXInLFxuICAgICAgICBwYXNzd29yZDogc2VjcmV0LnNlY3JldFZhbHVlRnJvbUpzb24oJ3Bhc3N3b3JkJyksXG4gICAgICB9LFxuICAgICAgdnBjLFxuICAgICAgd3JpdGVyOiBDbHVzdGVySW5zdGFuY2UucHJvdmlzaW9uZWQoJ3dyaXRlcicsIHtcbiAgICAgICAgaW5zdGFuY2VUeXBlOiBJbnN0YW5jZVR5cGUub2YoSW5zdGFuY2VDbGFzcy5UMywgSW5zdGFuY2VTaXplLk1FRElVTSksXG4gICAgICB9KSxcbiAgICB9KTtcblxuICAgIC8vIENyZWF0ZSBhbGwgRGF0YUxvYWRlciBpbnN0YW5jZXMgZmlyc3RcbiAgICBuZXcgRGF0YUxvYWRlcihzdGFjaywgJ0Jhc2ljRGF0YUxvYWRlcicsIHtcbiAgICAgIGRhdGFiYXNlQ29uZmlnOiB7XG4gICAgICAgIGVuZ2luZTogRGF0YWJhc2VFbmdpbmUuUE9TVEdSRVNRTCxcbiAgICAgICAgY2x1c3RlcjogY2x1c3RlcixcbiAgICAgICAgc2VjcmV0OiBzZWNyZXQsXG4gICAgICAgIGRhdGFiYXNlTmFtZTogJ3Rlc3RkYicsXG4gICAgICAgIHZwYzogdnBjLFxuICAgICAgICBzZWN1cml0eUdyb3VwOiBzZWN1cml0eUdyb3VwLFxuICAgICAgfSxcbiAgICAgIGZpbGVJbnB1dHM6IFtcbiAgICAgICAge1xuICAgICAgICAgIGZpbGVQYXRoOiAnczM6Ly90ZXN0LWJ1Y2tldC90ZXN0LWRhdGEuc3FsJyxcbiAgICAgICAgICBmaWxlVHlwZTogRmlsZVR5cGUuU1FMLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9KTtcblxuICAgIG5ldyBEYXRhTG9hZGVyKHN0YWNrLCAnTXVsdGlGaWxlRGF0YUxvYWRlcicsIHtcbiAgICAgIGRhdGFiYXNlQ29uZmlnOiB7XG4gICAgICAgIGVuZ2luZTogRGF0YWJhc2VFbmdpbmUuTVlTUUwsXG4gICAgICAgIGNsdXN0ZXI6IGNsdXN0ZXIsXG4gICAgICAgIHNlY3JldDogc2VjcmV0LFxuICAgICAgICBkYXRhYmFzZU5hbWU6ICd0ZXN0ZGInLFxuICAgICAgICB2cGM6IHZwYyxcbiAgICAgICAgc2VjdXJpdHlHcm91cDogc2VjdXJpdHlHcm91cCxcbiAgICAgIH0sXG4gICAgICBmaWxlSW5wdXRzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBmaWxlUGF0aDogJ3MzOi8vdGVzdC1idWNrZXQvc2NoZW1hLnNxbCcsXG4gICAgICAgICAgZmlsZVR5cGU6IEZpbGVUeXBlLlNRTCxcbiAgICAgICAgICBleGVjdXRpb25PcmRlcjogMSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGZpbGVQYXRoOiAnczM6Ly90ZXN0LWJ1Y2tldC9kYXRhLnNxbCcsXG4gICAgICAgICAgZmlsZVR5cGU6IEZpbGVUeXBlLk1ZU1FMRFVNUCxcbiAgICAgICAgICBleGVjdXRpb25PcmRlcjogMixcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG5cbiAgICBuZXcgRGF0YUxvYWRlcihzdGFjaywgJ0N1c3RvbURhdGFMb2FkZXInLCB7XG4gICAgICBkYXRhYmFzZUNvbmZpZzoge1xuICAgICAgICBlbmdpbmU6IERhdGFiYXNlRW5naW5lLlBPU1RHUkVTUUwsXG4gICAgICAgIGNsdXN0ZXI6IGNsdXN0ZXIsXG4gICAgICAgIHNlY3JldDogc2VjcmV0LFxuICAgICAgICBkYXRhYmFzZU5hbWU6ICd0ZXN0ZGInLFxuICAgICAgICB2cGM6IHZwYyxcbiAgICAgICAgc2VjdXJpdHlHcm91cDogc2VjdXJpdHlHcm91cCxcbiAgICAgIH0sXG4gICAgICBmaWxlSW5wdXRzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBmaWxlUGF0aDogJ3MzOi8vdGVzdC1idWNrZXQvdGVzdC1kYXRhLnNxbCcsXG4gICAgICAgICAgZmlsZVR5cGU6IEZpbGVUeXBlLlNRTCxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgICByZW1vdmFsUG9saWN5OiBSZW1vdmFsUG9saWN5LlJFVEFJTixcbiAgICAgIG1lbW9yeVNpemU6IDIwNDgsXG4gICAgfSk7XG5cbiAgICAvLyBHZW5lcmF0ZSB0ZW1wbGF0ZSBvbmNlIGFmdGVyIGFsbCBjb25zdHJ1Y3RzIGFyZSBjcmVhdGVkXG4gICAgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuICB9KTtcblxuICBkZXNjcmliZSgnQmFzaWMgZnVuY3Rpb25hbGl0eScsICgpID0+IHtcblxuICAgIHRlc3QoJ2NyZWF0ZXMgRGF0YUxvYWRlciBjb25zdHJ1Y3Qgd2l0aCBtaW5pbWFsIGNvbmZpZ3VyYXRpb24nLCAoKSA9PiB7XG4gICAgICBleHBlY3Qoc3RhY2subm9kZS5maW5kQ2hpbGQoJ0Jhc2ljRGF0YUxvYWRlcicpKS50b0JlRGVmaW5lZCgpO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnY3JlYXRlcyBleHBlY3RlZCBBV1MgcmVzb3VyY2VzJywgKCkgPT4ge1xuICAgICAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OlMzOjpCdWNrZXQnLCB7XG4gICAgICAgIEJ1Y2tldEVuY3J5cHRpb246IHtcbiAgICAgICAgICBTZXJ2ZXJTaWRlRW5jcnlwdGlvbkNvbmZpZ3VyYXRpb246IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgU2VydmVyU2lkZUVuY3J5cHRpb25CeURlZmF1bHQ6IHtcbiAgICAgICAgICAgICAgICBTU0VBbGdvcml0aG06ICdBRVMyNTYnLFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgfSk7XG5cbiAgICAgIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpTdGVwRnVuY3Rpb25zOjpTdGF0ZU1hY2hpbmUnLCB7fSk7XG4gICAgICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6TGFtYmRhOjpGdW5jdGlvbicsIHtcbiAgICAgICAgUnVudGltZTogJ3B5dGhvbjMuMTEnLFxuICAgICAgICBIYW5kbGVyOiAnaW5kZXguaGFuZGxlcicsXG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIHRlc3QoJ3N1cHBvcnRzIG11bHRpcGxlIGZpbGUgaW5wdXRzIHdpdGggZXhlY3V0aW9uIG9yZGVyJywgKCkgPT4ge1xuICAgICAgZXhwZWN0KHN0YWNrLm5vZGUuZmluZENoaWxkKCdNdWx0aUZpbGVEYXRhTG9hZGVyJykpLnRvQmVEZWZpbmVkKCk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCdzdXBwb3J0cyBjdXN0b20gY29uZmlndXJhdGlvbiBvcHRpb25zJywgKCkgPT4ge1xuICAgICAgZXhwZWN0KHN0YWNrLm5vZGUuZmluZENoaWxkKCdDdXN0b21EYXRhTG9hZGVyJykpLnRvQmVEZWZpbmVkKCk7XG4gICAgICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6TGFtYmRhOjpGdW5jdGlvbicsIHtcbiAgICAgICAgTWVtb3J5U2l6ZTogMjA0OCxcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgnVmFsaWRhdGlvbicsICgpID0+IHtcbiAgICB0ZXN0KCd0aHJvd3MgZXJyb3Igd2hlbiBkYXRhYmFzZUNvbmZpZyBpcyBtaXNzaW5nJywgKCkgPT4ge1xuICAgICAgZXhwZWN0KCgpID0+IHtcbiAgICAgICAgbmV3IERhdGFMb2FkZXIoc3RhY2ssICdJbnZhbGlkQ29uZmlnRGF0YUxvYWRlcicsIHtcbiAgICAgICAgICBkYXRhYmFzZUNvbmZpZzogdW5kZWZpbmVkIGFzIGFueSxcbiAgICAgICAgICBmaWxlSW5wdXRzOiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGZpbGVQYXRoOiAnczM6Ly90ZXN0LWJ1Y2tldC90ZXN0LWRhdGEuc3FsJyxcbiAgICAgICAgICAgICAgZmlsZVR5cGU6IEZpbGVUeXBlLlNRTCxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgXSxcbiAgICAgICAgfSk7XG4gICAgICB9KS50b1Rocm93KCdkYXRhYmFzZUNvbmZpZyBpcyByZXF1aXJlZCcpO1xuICAgIH0pO1xuXG4gICAgdGVzdCgndGhyb3dzIGVycm9yIHdoZW4gYm90aCBjbHVzdGVyIGFuZCBpbnN0YW5jZSBhcmUgbWlzc2luZycsICgpID0+IHtcbiAgICAgIGV4cGVjdCgoKSA9PiB7XG4gICAgICAgIG5ldyBEYXRhTG9hZGVyKHN0YWNrLCAnTm9DbHVzdGVyRGF0YUxvYWRlcicsIHtcbiAgICAgICAgICBkYXRhYmFzZUNvbmZpZzoge1xuICAgICAgICAgICAgZW5naW5lOiBEYXRhYmFzZUVuZ2luZS5QT1NUR1JFU1FMLFxuICAgICAgICAgICAgc2VjcmV0OiBzZWNyZXQsXG4gICAgICAgICAgICBkYXRhYmFzZU5hbWU6ICd0ZXN0ZGInLFxuICAgICAgICAgICAgdnBjOiB2cGMsXG4gICAgICAgICAgICBzZWN1cml0eUdyb3VwOiBzZWN1cml0eUdyb3VwLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgZmlsZUlucHV0czogW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBmaWxlUGF0aDogJ3MzOi8vdGVzdC1idWNrZXQvdGVzdC1kYXRhLnNxbCcsXG4gICAgICAgICAgICAgIGZpbGVUeXBlOiBGaWxlVHlwZS5TUUwsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIF0sXG4gICAgICAgIH0pO1xuICAgICAgfSkudG9UaHJvdygnRWl0aGVyIGNsdXN0ZXIgb3IgaW5zdGFuY2UgbXVzdCBiZSBwcm92aWRlZCBpbiBkYXRhYmFzZUNvbmZpZycpO1xuICAgIH0pO1xuXG4gICAgdGVzdCgndGhyb3dzIGVycm9yIHdoZW4gZmlsZUlucHV0cyBpcyBlbXB0eScsICgpID0+IHtcbiAgICAgIGV4cGVjdCgoKSA9PiB7XG4gICAgICAgIG5ldyBEYXRhTG9hZGVyKHN0YWNrLCAnRW1wdHlJbnB1dHNEYXRhTG9hZGVyJywge1xuICAgICAgICAgIGRhdGFiYXNlQ29uZmlnOiB7XG4gICAgICAgICAgICBlbmdpbmU6IERhdGFiYXNlRW5naW5lLlBPU1RHUkVTUUwsXG4gICAgICAgICAgICBjbHVzdGVyOiBjbHVzdGVyLFxuICAgICAgICAgICAgc2VjcmV0OiBzZWNyZXQsXG4gICAgICAgICAgICBkYXRhYmFzZU5hbWU6ICd0ZXN0ZGInLFxuICAgICAgICAgICAgdnBjOiB2cGMsXG4gICAgICAgICAgICBzZWN1cml0eUdyb3VwOiBzZWN1cml0eUdyb3VwLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgZmlsZUlucHV0czogW10sXG4gICAgICAgIH0pO1xuICAgICAgfSkudG9UaHJvdygnQXQgbGVhc3Qgb25lIGZpbGUgaW5wdXQgaXMgcmVxdWlyZWQnKTtcbiAgICB9KTtcblxuICAgIHRlc3QoJ3Rocm93cyBlcnJvciB3aGVuIE15U1FMIGVuZ2luZSBpcyB1c2VkIHdpdGggUG9zdGdyZVNRTCBkdW1wJywgKCkgPT4ge1xuICAgICAgZXhwZWN0KCgpID0+IHtcbiAgICAgICAgbmV3IERhdGFMb2FkZXIoc3RhY2ssICdNeVNRTFBnRHVtcERhdGFMb2FkZXInLCB7XG4gICAgICAgICAgZGF0YWJhc2VDb25maWc6IHtcbiAgICAgICAgICAgIGVuZ2luZTogRGF0YWJhc2VFbmdpbmUuTVlTUUwsXG4gICAgICAgICAgICBjbHVzdGVyOiBjbHVzdGVyLFxuICAgICAgICAgICAgc2VjcmV0OiBzZWNyZXQsXG4gICAgICAgICAgICBkYXRhYmFzZU5hbWU6ICd0ZXN0ZGInLFxuICAgICAgICAgICAgdnBjOiB2cGMsXG4gICAgICAgICAgICBzZWN1cml0eUdyb3VwOiBzZWN1cml0eUdyb3VwLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgZmlsZUlucHV0czogW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBmaWxlUGF0aDogJ3MzOi8vdGVzdC1idWNrZXQvdGVzdC1kYXRhLnNxbCcsXG4gICAgICAgICAgICAgIGZpbGVUeXBlOiBGaWxlVHlwZS5QR0RVTVAsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIF0sXG4gICAgICAgIH0pO1xuICAgICAgfSkudG9UaHJvdygnUG9zdGdyZVNRTCBkdW1wIGZpbGVzIGNhbm5vdCBiZSB1c2VkIHdpdGggTXlTUUwgZGF0YWJhc2VzJyk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCd0aHJvd3MgZXJyb3Igd2hlbiBQb3N0Z3JlU1FMIGVuZ2luZSBpcyB1c2VkIHdpdGggTXlTUUwgZHVtcCcsICgpID0+IHtcbiAgICAgIGV4cGVjdCgoKSA9PiB7XG4gICAgICAgIG5ldyBEYXRhTG9hZGVyKHN0YWNrLCAnUG9zdGdyZVNRTE15U1FMRHVtcERhdGFMb2FkZXInLCB7XG4gICAgICAgICAgZGF0YWJhc2VDb25maWc6IHtcbiAgICAgICAgICAgIGVuZ2luZTogRGF0YWJhc2VFbmdpbmUuUE9TVEdSRVNRTCxcbiAgICAgICAgICAgIGNsdXN0ZXI6IGNsdXN0ZXIsXG4gICAgICAgICAgICBzZWNyZXQ6IHNlY3JldCxcbiAgICAgICAgICAgIGRhdGFiYXNlTmFtZTogJ3Rlc3RkYicsXG4gICAgICAgICAgICB2cGM6IHZwYyxcbiAgICAgICAgICAgIHNlY3VyaXR5R3JvdXA6IHNlY3VyaXR5R3JvdXAsXG4gICAgICAgICAgfSxcbiAgICAgICAgICBmaWxlSW5wdXRzOiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGZpbGVQYXRoOiAnczM6Ly90ZXN0LWJ1Y2tldC90ZXN0LWRhdGEuc3FsJyxcbiAgICAgICAgICAgICAgZmlsZVR5cGU6IEZpbGVUeXBlLk1ZU1FMRFVNUCxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgXSxcbiAgICAgICAgfSk7XG4gICAgICB9KS50b1Rocm93KCdNeVNRTCBkdW1wIGZpbGVzIGNhbm5vdCBiZSB1c2VkIHdpdGggUG9zdGdyZVNRTCBkYXRhYmFzZXMnKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJ1JlbW92YWwgUG9saWN5JywgKCkgPT4ge1xuICAgIHRlc3QoJ2FwcGxpZXMgY3VzdG9tIHJlbW92YWwgcG9saWN5IHRvIHJlc291cmNlcycsICgpID0+IHtcbiAgICAgIGV4cGVjdChzdGFjay5ub2RlLmZpbmRDaGlsZCgnQ3VzdG9tRGF0YUxvYWRlcicpKS50b0JlRGVmaW5lZCgpO1xuXG4gICAgICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZSgnQVdTOjpTMzo6QnVja2V0Jywge1xuICAgICAgICBEZWxldGlvblBvbGljeTogJ1JldGFpbicsXG4gICAgICAgIFVwZGF0ZVJlcGxhY2VQb2xpY3k6ICdSZXRhaW4nLFxuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCd1c2VzIGRlZmF1bHQgREVTVFJPWSByZW1vdmFsIHBvbGljeSB3aGVuIG5vdCBzcGVjaWZpZWQnLCAoKSA9PiB7XG4gICAgICBleHBlY3Qoc3RhY2subm9kZS5maW5kQ2hpbGQoJ0Jhc2ljRGF0YUxvYWRlcicpKS50b0JlRGVmaW5lZCgpO1xuXG4gICAgICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZSgnQVdTOjpTMzo6QnVja2V0Jywge1xuICAgICAgICBEZWxldGlvblBvbGljeTogJ0RlbGV0ZScsXG4gICAgICAgIFVwZGF0ZVJlcGxhY2VQb2xpY3k6ICdEZWxldGUnLFxuICAgICAgfSk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCdTZWN1cml0eScsICgpID0+IHtcbiAgICB0ZXN0KCdjcmVhdGVzIHJlc291cmNlcyB3aXRoIHByb3BlciBzZWN1cml0eSBjb25maWd1cmF0aW9ucycsICgpID0+IHtcbiAgICAgIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpTMzo6QnVja2V0Jywge1xuICAgICAgICBQdWJsaWNBY2Nlc3NCbG9ja0NvbmZpZ3VyYXRpb246IHtcbiAgICAgICAgICBCbG9ja1B1YmxpY0FjbHM6IHRydWUsXG4gICAgICAgICAgQmxvY2tQdWJsaWNQb2xpY3k6IHRydWUsXG4gICAgICAgICAgSWdub3JlUHVibGljQWNsczogdHJ1ZSxcbiAgICAgICAgICBSZXN0cmljdFB1YmxpY0J1Y2tldHM6IHRydWUsXG4gICAgICAgIH0sXG4gICAgICB9KTtcblxuICAgICAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OkxhbWJkYTo6RnVuY3Rpb24nLCB7XG4gICAgICAgIEhhbmRsZXI6ICdpbmRleC5oYW5kbGVyJyxcbiAgICAgICAgUnVudGltZTogJ3B5dGhvbjMuMTEnLFxuICAgICAgfSk7XG5cbiAgICAgIGNvbnN0IGxhbWJkYUZ1bmN0aW9ucyA9IHRlbXBsYXRlLmZpbmRSZXNvdXJjZXMoJ0FXUzo6TGFtYmRhOjpGdW5jdGlvbicpO1xuICAgICAgY29uc3QgaGFzVnBjQ29uZmlnID0gT2JqZWN0LnZhbHVlcyhsYW1iZGFGdW5jdGlvbnMpLnNvbWUoKGZuOiBhbnkpID0+XG4gICAgICAgIGZuLlByb3BlcnRpZXM/LlZwY0NvbmZpZz8uU3VibmV0SWRzLFxuICAgICAgKTtcbiAgICAgIGV4cGVjdChoYXNWcGNDb25maWcpLnRvQmUodHJ1ZSk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCdncmFudHMgYXBwcm9wcmlhdGUgSUFNIHBlcm1pc3Npb25zJywgKCkgPT4ge1xuICAgICAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OklBTTo6Um9sZScsIHtcbiAgICAgICAgQXNzdW1lUm9sZVBvbGljeURvY3VtZW50OiB7XG4gICAgICAgICAgU3RhdGVtZW50OiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIEFjdGlvbjogJ3N0czpBc3N1bWVSb2xlJyxcbiAgICAgICAgICAgICAgRWZmZWN0OiAnQWxsb3cnLFxuICAgICAgICAgICAgICBQcmluY2lwYWw6IHtcbiAgICAgICAgICAgICAgICBTZXJ2aWNlOiAnbGFtYmRhLmFtYXpvbmF3cy5jb20nLFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgfSk7XG4gIH0pO1xufSk7XG4iXX0=
@@ -0,0 +1,136 @@
1
+ import { RemovalPolicy } from 'aws-cdk-lib';
2
+ import { ICertificate } from 'aws-cdk-lib/aws-certificatemanager';
3
+ import { Distribution, ErrorResponse, PriceClass } from 'aws-cdk-lib/aws-cloudfront';
4
+ import { IHostedZone } from 'aws-cdk-lib/aws-route53';
5
+ import { Bucket } from 'aws-cdk-lib/aws-s3';
6
+ import { Asset } from 'aws-cdk-lib/aws-s3-assets';
7
+ import { BucketDeployment } from 'aws-cdk-lib/aws-s3-deployment';
8
+ import { Construct } from 'constructs';
9
+ /**
10
+ * Default CloudFront error responses for Single Page Applications
11
+ */
12
+ export declare const DEFAULT_SPA_ERROR_RESPONSES: ErrorResponse[];
13
+ /**
14
+ * Custom domain configuration for the frontend
15
+ */
16
+ export interface CustomDomainConfig {
17
+ /** Domain name for the frontend (e.g., 'app.example.com') */
18
+ readonly domainName: string;
19
+ /** SSL certificate for the domain (required when domainName is provided) */
20
+ readonly certificate: ICertificate;
21
+ /** Optional hosted zone for automatic DNS record creation */
22
+ readonly hostedZone?: IHostedZone;
23
+ }
24
+ /**
25
+ * Additional CloudFront distribution properties
26
+ */
27
+ export interface AdditionalDistributionProps {
28
+ /** Optional comment for the distribution */
29
+ readonly comment?: string;
30
+ /** Optional enabled flag for the distribution */
31
+ readonly enabled?: boolean;
32
+ /** Optional price class for the distribution */
33
+ readonly priceClass?: PriceClass;
34
+ /** Optional web ACL ID for the distribution */
35
+ readonly webAclId?: string;
36
+ }
37
+ /**
38
+ * Properties for the Frontend construct
39
+ */
40
+ export interface FrontendProps {
41
+ /** Base directory of the frontend source code */
42
+ readonly sourceDirectory: string;
43
+ /** Directory where build artifacts are located after build command completes (defaults to '{sourceDirectory}/build') */
44
+ readonly buildOutputDirectory?: string;
45
+ /** Optional build command (defaults to 'npm run build') */
46
+ readonly buildCommand?: string;
47
+ /** Optional custom domain configuration */
48
+ readonly customDomain?: CustomDomainConfig;
49
+ /** Optional CloudFront error responses (defaults to SPA-friendly responses) */
50
+ readonly errorResponses?: ErrorResponse[];
51
+ /** Optional additional CloudFront distribution properties */
52
+ readonly distributionProps?: AdditionalDistributionProps;
53
+ /** Optional flag to skip the build process (useful for pre-built artifacts) */
54
+ readonly skipBuild?: boolean;
55
+ /** Optional removal policy for all resources (defaults to DESTROY) */
56
+ readonly removalPolicy?: RemovalPolicy;
57
+ /**
58
+ * Enable logging and tracing for all supporting resource
59
+ * @default false
60
+ */
61
+ readonly enableObservability?: boolean;
62
+ }
63
+ /**
64
+ * Frontend construct that deploys a frontend application to S3 and CloudFront
65
+ *
66
+ * This construct provides a complete solution for hosting static frontend applications
67
+ * with the following features:
68
+ * - S3 bucket for hosting static assets with security best practices
69
+ * - CloudFront distribution for global content delivery
70
+ * - Optional custom domain with SSL certificate
71
+ * - Automatic build process execution
72
+ * - SPA-friendly error handling by default
73
+ * - Security configurations
74
+ */
75
+ export declare class Frontend extends Construct {
76
+ /** The S3 bucket hosting the frontend assets */
77
+ readonly bucket: Bucket;
78
+ /** The CloudFront distribution */
79
+ readonly distribution: Distribution;
80
+ /** The bucket deployment that uploads the frontend assets */
81
+ readonly bucketDeployment: BucketDeployment;
82
+ /** The custom domain name (if configured) */
83
+ readonly domainName?: string;
84
+ /** The Asset containing the frontend source code */
85
+ readonly asset?: Asset;
86
+ /**
87
+ * Creates a new Frontend
88
+ * @param scope The construct scope
89
+ * @param id The construct ID
90
+ * @param props The frontend properties
91
+ */
92
+ constructor(scope: Construct, id: string, props: FrontendProps);
93
+ /**
94
+ * Validates the construct properties
95
+ * @param props The frontend properties
96
+ * @private
97
+ */
98
+ private _validateProps;
99
+ /**
100
+ * Creates an Asset for the frontend source code with bundling
101
+ * @param props The frontend properties
102
+ * @returns The Asset containing the built frontend
103
+ * @private
104
+ */
105
+ private _createAsset;
106
+ /**
107
+ * Creates the CloudFront distribution
108
+ * @param props The frontend properties
109
+ * @param removalPolicy The removal policy to apply
110
+ * @returns The CloudFront distribution
111
+ * @private
112
+ */
113
+ private _createDistribution;
114
+ /**
115
+ * Sets up custom domain with Route53 record
116
+ * @param customDomain The custom domain configuration
117
+ * @param removalPolicy The removal policy to apply
118
+ * @private
119
+ */
120
+ private _setupCustomDomain;
121
+ /**
122
+ * Gets the URL of the frontend application
123
+ * @returns The frontend URL
124
+ */
125
+ url(): string;
126
+ /**
127
+ * Gets the CloudFront distribution domain name
128
+ * @returns The CloudFront domain name
129
+ */
130
+ distributionDomainName(): string;
131
+ /**
132
+ * Gets the S3 bucket name
133
+ * @returns The S3 bucket name
134
+ */
135
+ bucketName(): string;
136
+ }