@jaypie/constructs 1.1.28 → 1.1.29

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/dist/esm/index.js CHANGED
@@ -1,22 +1,279 @@
1
+ import { Construct } from 'constructs';
1
2
  import * as cdk from 'aws-cdk-lib';
2
- import { Duration, Stack, Tags, RemovalPolicy, Fn, CfnOutput, SecretValue } from 'aws-cdk-lib';
3
+ import { Tags, Stack, Duration, RemovalPolicy, Fn, CfnOutput, SecretValue } from 'aws-cdk-lib';
4
+ import * as acm from 'aws-cdk-lib/aws-certificatemanager';
5
+ import * as apiGateway from 'aws-cdk-lib/aws-apigateway';
6
+ import * as route53 from 'aws-cdk-lib/aws-route53';
7
+ import { HostedZone } from 'aws-cdk-lib/aws-route53';
8
+ import * as route53Targets from 'aws-cdk-lib/aws-route53-targets';
9
+ import { CDK as CDK$2, isValidSubdomain, ConfigurationError, isValidHostname, mergeDomain } from '@jaypie/cdk';
3
10
  import * as s3 from 'aws-cdk-lib/aws-s3';
4
11
  import * as s3n from 'aws-cdk-lib/aws-s3-notifications';
5
- import { CDK } from '@jaypie/cdk';
6
- import { Construct } from 'constructs';
7
12
  import * as lambda from 'aws-cdk-lib/aws-lambda';
8
13
  import * as sqs from 'aws-cdk-lib/aws-sqs';
9
14
  import * as lambdaEventSources from 'aws-cdk-lib/aws-lambda-event-sources';
10
15
  import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
11
- import { ServicePrincipal } from 'aws-cdk-lib/aws-iam';
16
+ import { ServicePrincipal, Role, FederatedPrincipal, PolicyStatement, Effect } from 'aws-cdk-lib/aws-iam';
12
17
  import { LogGroup, RetentionDays, FilterPattern } from 'aws-cdk-lib/aws-logs';
13
- import { HostedZone } from 'aws-cdk-lib/aws-route53';
14
18
  import * as sso from 'aws-cdk-lib/aws-sso';
19
+ import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
20
+ import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
21
+
22
+ function constructEnvName(name, opts) {
23
+ const env = opts?.env ?? process.env.PROJECT_ENV ?? "build";
24
+ const key = opts?.key ?? process.env.PROJECT_KEY ?? "project";
25
+ const nonce = opts?.nonce ?? process.env.PROJECT_NONCE ?? "cfe2";
26
+ return `${env}-${key}-${name}-${nonce}`;
27
+ }
28
+
29
+ function constructStackName(key) {
30
+ if (!key) {
31
+ return `cdk-${process.env.PROJECT_SPONSOR}-${process.env.PROJECT_KEY}-${process.env.PROJECT_ENV}-${process.env.PROJECT_NONCE}`;
32
+ }
33
+ else {
34
+ return `cdk-${process.env.PROJECT_SPONSOR}-${process.env.PROJECT_KEY}-${process.env.PROJECT_ENV}-${process.env.PROJECT_NONCE}-${key}`;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Check if the current environment matches the given environment
40
+ */
41
+ function isEnv(env) {
42
+ return process.env.PROJECT_ENV === env;
43
+ }
44
+ /**
45
+ * Check if the current environment is production
46
+ */
47
+ function isProductionEnv() {
48
+ return isEnv(CDK$2.ENV.PRODUCTION);
49
+ }
50
+ /**
51
+ * Check if the current environment is sandbox
52
+ */
53
+ function isSandboxEnv() {
54
+ return isEnv(CDK$2.ENV.SANDBOX);
55
+ }
56
+
57
+ const CDK$1 = {
58
+ CREATION: {
59
+ CDK: "cdk",
60
+ },
61
+ ROLE: {
62
+ STACK: "stack",
63
+ },
64
+ TAG: {
65
+ BUILD_DATE: "buildDate",
66
+ BUILD_HEX: "buildHex",
67
+ BUILD_TIME: "buildTime",
68
+ COMMIT: "commit",
69
+ CREATION: "creation",
70
+ ENV: "env",
71
+ NONCE: "nonce",
72
+ PROJECT: "project",
73
+ ROLE: "role",
74
+ SERVICE: "service",
75
+ SPONSOR: "sponsor",
76
+ STACK: "stack",
77
+ VERSION: "version",
78
+ },
79
+ };
80
+ function stackTagger(stack, { name } = {}) {
81
+ const stackName = name || constructStackName();
82
+ const version = process.env.npm_package_version || process.env.PROJECT_VERSION || null;
83
+ if (process.env.PROJECT_COMMIT && process.env.PROJECT_COMMIT.length > 8) {
84
+ Tags.of(stack).add(CDK$1.TAG.BUILD_HEX, process.env.PROJECT_COMMIT.slice(0, 8));
85
+ }
86
+ Tags.of(stack).add(CDK$1.TAG.BUILD_DATE, new Date().toISOString());
87
+ Tags.of(stack).add(CDK$1.TAG.BUILD_TIME, Date.now().toString());
88
+ if (process.env.PROJECT_COMMIT)
89
+ Tags.of(stack).add(CDK$1.TAG.COMMIT, process.env.PROJECT_COMMIT);
90
+ Tags.of(stack).add(CDK$1.TAG.CREATION, CDK$1.CREATION.CDK);
91
+ if (process.env.PROJECT_ENV)
92
+ Tags.of(stack).add(CDK$1.TAG.ENV, process.env.PROJECT_ENV);
93
+ if (process.env.PROJECT_NONCE)
94
+ Tags.of(stack).add(CDK$1.TAG.NONCE, process.env.PROJECT_NONCE);
95
+ if (process.env.PROJECT_KEY)
96
+ Tags.of(stack).add(CDK$1.TAG.PROJECT, process.env.PROJECT_KEY);
97
+ Tags.of(stack).add(CDK$1.TAG.ROLE, CDK$1.ROLE.STACK);
98
+ if (process.env.PROJECT_SERVICE)
99
+ Tags.of(stack).add(CDK$1.TAG.SERVICE, process.env.PROJECT_SERVICE);
100
+ if (process.env.PROJECT_SPONSOR)
101
+ Tags.of(stack).add(CDK$1.TAG.SPONSOR, process.env.PROJECT_SPONSOR);
102
+ if (stackName)
103
+ Tags.of(stack).add(CDK$1.TAG.STACK, stackName);
104
+ if (version)
105
+ Tags.of(stack).add(CDK$1.TAG.VERSION, version);
106
+ return true;
107
+ }
108
+
109
+ class JaypieApiGateway extends Construct {
110
+ constructor(scope, id, props) {
111
+ super(scope, id);
112
+ const { certificate = true, handler, host, name, roleTag = CDK$2.ROLE.API, zone, } = props;
113
+ const apiGatewayName = name || constructEnvName("ApiGateway");
114
+ const certificateName = constructEnvName("Certificate");
115
+ const apiDomainName = constructEnvName("ApiDomainName");
116
+ let hostedZone;
117
+ let certificateToUse;
118
+ if (host && zone) {
119
+ if (typeof zone === "string") {
120
+ hostedZone = route53.HostedZone.fromLookup(this, "HostedZone", {
121
+ domainName: zone,
122
+ });
123
+ }
124
+ else {
125
+ hostedZone = zone;
126
+ }
127
+ if (certificate === true) {
128
+ certificateToUse = new acm.Certificate(this, certificateName, {
129
+ domainName: host,
130
+ validation: acm.CertificateValidation.fromDns(hostedZone),
131
+ });
132
+ Tags.of(certificateToUse).add(CDK$2.TAG.ROLE, CDK$2.ROLE.HOSTING);
133
+ }
134
+ else if (typeof certificate === "object") {
135
+ certificateToUse = certificate;
136
+ }
137
+ this._certificate = certificateToUse;
138
+ this._host = host;
139
+ }
140
+ const {
141
+ // * `...lambdaRestApiProps` cannot be moved to the first const destructuring because it needs to exclude the custom properties first.
142
+ // Ignore the variables we already assigned to other properties
143
+ /* eslint-disable @typescript-eslint/no-unused-vars */
144
+ certificate: _certificate, host: _host, name: _name, roleTag: _roleTag, zone: _zone, handler: _handler,
145
+ /* eslint-enable @typescript-eslint/no-unused-vars */
146
+ ...lambdaRestApiProps } = props;
147
+ this._api = new apiGateway.LambdaRestApi(this, apiGatewayName, {
148
+ handler,
149
+ ...lambdaRestApiProps,
150
+ });
151
+ Tags.of(this._api).add(CDK$2.TAG.ROLE, roleTag);
152
+ if (host && certificateToUse && hostedZone) {
153
+ this._domainName = this._api.addDomainName(apiDomainName, {
154
+ domainName: host,
155
+ certificate: certificateToUse,
156
+ });
157
+ Tags.of(this._domainName).add(CDK$2.TAG.ROLE, roleTag);
158
+ const record = new route53.ARecord(this, "AliasRecord", {
159
+ recordName: host,
160
+ target: route53.RecordTarget.fromAlias(new route53Targets.ApiGatewayDomain(this._domainName)),
161
+ zone: hostedZone,
162
+ });
163
+ Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
164
+ }
165
+ }
166
+ get api() {
167
+ return this._api;
168
+ }
169
+ get url() {
170
+ return this._api.url;
171
+ }
172
+ get certificateArn() {
173
+ return this._certificate?.certificateArn;
174
+ }
175
+ get domainName() {
176
+ return this._domainName?.domainName;
177
+ }
178
+ get host() {
179
+ return this._host;
180
+ }
181
+ get restApiId() {
182
+ return this._api.restApiId;
183
+ }
184
+ get restApiName() {
185
+ return this._api.restApiName;
186
+ }
187
+ get restApiRootResourceId() {
188
+ return this._api.restApiRootResourceId;
189
+ }
190
+ get deploymentStage() {
191
+ return this._api.deploymentStage;
192
+ }
193
+ get domainNameAliasDomainName() {
194
+ return this._domainName?.domainNameAliasDomainName;
195
+ }
196
+ get domainNameAliasHostedZoneId() {
197
+ return this._domainName?.domainNameAliasHostedZoneId;
198
+ }
199
+ get root() {
200
+ return this._api.root;
201
+ }
202
+ get env() {
203
+ return {
204
+ account: Stack.of(this).account,
205
+ region: Stack.of(this).region,
206
+ };
207
+ }
208
+ get stack() {
209
+ return this._api.stack;
210
+ }
211
+ arnForExecuteApi(method, path, stage) {
212
+ return this._api.arnForExecuteApi(method, path, stage);
213
+ }
214
+ metric(metricName, props) {
215
+ return this._api.metric(metricName, props);
216
+ }
217
+ metricCacheHitCount(props) {
218
+ return this._api.metricCacheHitCount(props);
219
+ }
220
+ metricCacheMissCount(props) {
221
+ return this._api.metricCacheMissCount(props);
222
+ }
223
+ metricClientError(props) {
224
+ return this._api.metricClientError(props);
225
+ }
226
+ metricCount(props) {
227
+ return this._api.metricCount(props);
228
+ }
229
+ metricIntegrationLatency(props) {
230
+ return this._api.metricIntegrationLatency(props);
231
+ }
232
+ metricLatency(props) {
233
+ return this._api.metricLatency(props);
234
+ }
235
+ metricServerError(props) {
236
+ return this._api.metricServerError(props);
237
+ }
238
+ applyRemovalPolicy(policy) {
239
+ this._api.applyRemovalPolicy(policy);
240
+ }
241
+ }
242
+
243
+ class JaypieStack extends Stack {
244
+ constructor(scope, id, props = {}) {
245
+ const { key, ...stackProps } = props;
246
+ // Handle stackName
247
+ if (!stackProps.stackName) {
248
+ stackProps.stackName = constructStackName(key);
249
+ }
250
+ // Handle env
251
+ stackProps.env = {
252
+ account: process.env.CDK_DEFAULT_ACCOUNT,
253
+ region: process.env.CDK_DEFAULT_REGION,
254
+ ...stackProps.env,
255
+ };
256
+ super(scope, id, stackProps);
257
+ // Apply tags
258
+ stackTagger(this, { name: stackProps.stackName });
259
+ }
260
+ }
261
+
262
+ class JaypieAppStack extends JaypieStack {
263
+ constructor(scope, id, props = {}) {
264
+ const { key = "app", ...stackProps } = props;
265
+ // Handle stackName
266
+ if (!stackProps.stackName) {
267
+ stackProps.stackName = constructStackName(key);
268
+ }
269
+ super(scope, id, { key, ...stackProps });
270
+ }
271
+ }
15
272
 
16
273
  class JaypieLambda extends Construct {
17
274
  constructor(scope, id, props) {
18
275
  super(scope, id);
19
- const { code, datadogApiKeyArn, environment: initialEnvironment = {}, envSecrets = {}, handler = "index.handler", layers = [], logRetention = CDK.LAMBDA.LOG_RETENTION, memorySize = CDK.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, reservedConcurrentExecutions, roleTag, runtime = lambda.Runtime.NODEJS_22_X, secrets = [], timeout = Duration.seconds(CDK.DURATION.LAMBDA_WORKER), vendorTag, } = props;
276
+ const { code, datadogApiKeyArn, environment: initialEnvironment = {}, envSecrets = {}, handler = "index.handler", layers = [], logRetention = CDK$2.LAMBDA.LOG_RETENTION, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, reservedConcurrentExecutions, roleTag = CDK$2.ROLE.PROCESSING, runtime = lambda.Runtime.NODEJS_22_X, secrets = [], timeout = Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), vendorTag, } = props;
20
277
  // Create a mutable copy of the environment variables
21
278
  let environment = { ...initialEnvironment };
22
279
  // Default environment variables from process.env if present
@@ -50,18 +307,18 @@ class JaypieLambda extends Construct {
50
307
  // Add Datadog integration if API key is available
51
308
  if (resolvedDatadogApiKeyArn) {
52
309
  // Add Datadog Node.js layer
53
- const datadogNodeLayer = lambda.LayerVersion.fromLayerVersionArn(this, "DatadogNodeLayer", `arn:aws:lambda:${Stack.of(this).region}:464622532012:layer:Datadog-Node20-x:${CDK.DATADOG.LAYER.NODE}`);
310
+ const datadogNodeLayer = lambda.LayerVersion.fromLayerVersionArn(this, "DatadogNodeLayer", `arn:aws:lambda:${Stack.of(this).region}:464622532012:layer:Datadog-Node20-x:${CDK$2.DATADOG.LAYER.NODE}`);
54
311
  resolvedLayers.push(datadogNodeLayer);
55
312
  // Add Datadog Extension layer
56
- const datadogExtensionLayer = lambda.LayerVersion.fromLayerVersionArn(this, "DatadogExtensionLayer", `arn:aws:lambda:${Stack.of(this).region}:464622532012:layer:Datadog-Extension:${CDK.DATADOG.LAYER.EXTENSION}`);
313
+ const datadogExtensionLayer = lambda.LayerVersion.fromLayerVersionArn(this, "DatadogExtensionLayer", `arn:aws:lambda:${Stack.of(this).region}:464622532012:layer:Datadog-Extension:${CDK$2.DATADOG.LAYER.EXTENSION}`);
57
314
  resolvedLayers.push(datadogExtensionLayer);
58
315
  // Set Datadog environment variables
59
316
  Object.assign(environment, {
60
317
  DD_API_KEY_SECRET_ARN: resolvedDatadogApiKeyArn,
61
318
  DD_ENV: process.env.PROJECT_ENV || "",
62
319
  DD_SERVICE: process.env.PROJECT_SERVICE || "",
63
- DD_SITE: CDK.DATADOG.SITE,
64
- DD_TAGS: `${CDK.TAG.SPONSOR}:${process.env.PROJECT_SPONSOR || ""}`,
320
+ DD_SITE: CDK$2.DATADOG.SITE,
321
+ DD_TAGS: `${CDK$2.TAG.SPONSOR}:${process.env.PROJECT_SPONSOR || ""}`,
65
322
  });
66
323
  }
67
324
  // Configure ParamsAndSecrets layer
@@ -133,10 +390,10 @@ class JaypieLambda extends Construct {
133
390
  datadogApiKey.grantRead(this._lambda);
134
391
  }
135
392
  if (roleTag) {
136
- Tags.of(this._lambda).add(CDK.TAG.ROLE, roleTag);
393
+ Tags.of(this._lambda).add(CDK$2.TAG.ROLE, roleTag);
137
394
  }
138
395
  if (vendorTag) {
139
- Tags.of(this._lambda).add(CDK.TAG.VENDOR, vendorTag);
396
+ Tags.of(this._lambda).add(CDK$2.TAG.VENDOR, vendorTag);
140
397
  }
141
398
  }
142
399
  // Public accessors
@@ -246,7 +503,7 @@ class JaypieLambda extends Construct {
246
503
  class JaypieQueuedLambda extends Construct {
247
504
  constructor(scope, id, props) {
248
505
  super(scope, id);
249
- const { batchSize = 1, code, environment = {}, envSecrets = {}, fifo = true, handler = "index.handler", layers = [], logRetention = CDK.LAMBDA.LOG_RETENTION, memorySize = CDK.LAMBDA.MEMORY_SIZE, paramsAndSecrets, reservedConcurrentExecutions, roleTag, runtime = lambda.Runtime.NODEJS_22_X, secrets = [], timeout = Duration.seconds(CDK.DURATION.LAMBDA_WORKER), vendorTag, visibilityTimeout = Duration.seconds(CDK.DURATION.LAMBDA_WORKER), } = props;
506
+ const { batchSize = 1, code, environment = {}, envSecrets = {}, fifo = true, handler = "index.handler", layers = [], logRetention = CDK$2.LAMBDA.LOG_RETENTION, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, reservedConcurrentExecutions, roleTag, runtime = lambda.Runtime.NODEJS_22_X, secrets = [], timeout = Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), vendorTag, visibilityTimeout = Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), } = props;
250
507
  // Create SQS Queue
251
508
  this._queue = new sqs.Queue(this, "Queue", {
252
509
  fifo,
@@ -255,10 +512,10 @@ class JaypieQueuedLambda extends Construct {
255
512
  : visibilityTimeout,
256
513
  });
257
514
  if (roleTag) {
258
- Tags.of(this._queue).add(CDK.TAG.ROLE, roleTag);
515
+ Tags.of(this._queue).add(CDK$2.TAG.ROLE, roleTag);
259
516
  }
260
517
  if (vendorTag) {
261
- Tags.of(this._queue).add(CDK.TAG.VENDOR, vendorTag);
518
+ Tags.of(this._queue).add(CDK$2.TAG.VENDOR, vendorTag);
262
519
  }
263
520
  // Create Lambda with JaypieLambda
264
521
  this._lambdaConstruct = new JaypieLambda(this, "Function", {
@@ -464,10 +721,10 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
464
721
  });
465
722
  // Add tags to bucket
466
723
  if (roleTag) {
467
- Tags.of(this._bucket).add(CDK.TAG.ROLE, roleTag);
724
+ Tags.of(this._bucket).add(CDK$2.TAG.ROLE, roleTag);
468
725
  }
469
726
  if (vendorTag) {
470
- Tags.of(this._bucket).add(CDK.TAG.VENDOR, vendorTag);
727
+ Tags.of(this._bucket).add(CDK$2.TAG.VENDOR, vendorTag);
471
728
  }
472
729
  // Add an event notification from the bucket to the queue
473
730
  this._bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.SqsDestination(this.queue));
@@ -632,13 +889,13 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
632
889
 
633
890
  // It is a consumer if the environment is ephemeral
634
891
  function checkEnvIsConsumer(env = process.env) {
635
- return (env.PROJECT_ENV === CDK.ENV.PERSONAL ||
892
+ return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
636
893
  !!env.CDK_ENV_PERSONAL ||
637
894
  /** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
638
895
  /** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
639
896
  }
640
897
  function checkEnvIsProvider(env = process.env) {
641
- return env.PROJECT_ENV === CDK.ENV.SANDBOX;
898
+ return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
642
899
  }
643
900
  function cleanName(name) {
644
901
  return name.replace(/[^a-zA-Z0-9:-]/g, "");
@@ -652,7 +909,7 @@ function exportEnvName(name, env = process.env) {
652
909
  }
653
910
  else {
654
911
  if (checkEnvIsConsumer(env)) {
655
- rawName = `env-${CDK.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
912
+ rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
656
913
  }
657
914
  else {
658
915
  rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
@@ -689,10 +946,10 @@ class JaypieEnvSecret extends Construct {
689
946
  };
690
947
  this._secret = new secretsmanager.Secret(this, id, secretProps);
691
948
  if (roleTag) {
692
- Tags.of(this._secret).add(CDK.TAG.ROLE, roleTag);
949
+ Tags.of(this._secret).add(CDK$2.TAG.ROLE, roleTag);
693
950
  }
694
951
  if (vendorTag) {
695
- Tags.of(this._secret).add(CDK.TAG.VENDOR, vendorTag);
952
+ Tags.of(this._secret).add(CDK$2.TAG.VENDOR, vendorTag);
696
953
  }
697
954
  if (provider) {
698
955
  new CfnOutput(this, `ProvidedName`, {
@@ -766,8 +1023,8 @@ class JaypieDatadogSecret extends JaypieEnvSecret {
766
1023
  constructor(scope, id = "MongoConnectionString", props) {
767
1024
  const defaultProps = {
768
1025
  envKey: "DATADOG_API_KEY",
769
- roleTag: CDK.ROLE.MONITORING,
770
- vendorTag: CDK.VENDOR.DATADOG,
1026
+ roleTag: CDK$2.ROLE.MONITORING,
1027
+ vendorTag: CDK$2.VENDOR.DATADOG,
771
1028
  ...props,
772
1029
  };
773
1030
  super(scope, id, defaultProps);
@@ -777,8 +1034,8 @@ class JaypieDatadogSecret extends JaypieEnvSecret {
777
1034
  class JaypieExpressLambda extends JaypieLambda {
778
1035
  constructor(scope, id, props) {
779
1036
  super(scope, id, {
780
- timeout: Duration.seconds(CDK.DURATION.EXPRESS_API),
781
- roleTag: CDK.ROLE.API,
1037
+ timeout: Duration.seconds(CDK$2.DURATION.EXPRESS_API),
1038
+ roleTag: CDK$2.ROLE.API,
782
1039
  ...props,
783
1040
  });
784
1041
  }
@@ -794,7 +1051,7 @@ class JaypieHostedZone extends Construct {
794
1051
  constructor(scope, id, props) {
795
1052
  super(scope, id);
796
1053
  const { destination, zoneName, project } = props;
797
- const service = props.service || CDK.SERVICE.INFRASTRUCTURE;
1054
+ const service = props.service || CDK$2.SERVICE.INFRASTRUCTURE;
798
1055
  // Create the log group
799
1056
  this.logGroup = new LogGroup(this, "LogGroup", {
800
1057
  logGroupName: process.env.PROJECT_NONCE
@@ -803,10 +1060,10 @@ class JaypieHostedZone extends Construct {
803
1060
  retention: RetentionDays.ONE_WEEK,
804
1061
  });
805
1062
  // Add tags
806
- cdk.Tags.of(this.logGroup).add(CDK.TAG.SERVICE, service);
807
- cdk.Tags.of(this.logGroup).add(CDK.TAG.ROLE, CDK.ROLE.NETWORKING);
1063
+ cdk.Tags.of(this.logGroup).add(CDK$2.TAG.SERVICE, service);
1064
+ cdk.Tags.of(this.logGroup).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
808
1065
  if (project) {
809
- cdk.Tags.of(this.logGroup).add(CDK.TAG.PROJECT, project);
1066
+ cdk.Tags.of(this.logGroup).add(CDK$2.TAG.PROJECT, project);
810
1067
  }
811
1068
  // Grant Route 53 permissions to write to the log group
812
1069
  this.logGroup.grantWrite(new ServicePrincipal(SERVICE.ROUTE53));
@@ -823,10 +1080,30 @@ class JaypieHostedZone extends Construct {
823
1080
  zoneName,
824
1081
  });
825
1082
  // Add tags
826
- cdk.Tags.of(this.hostedZone).add(CDK.TAG.SERVICE, service);
827
- cdk.Tags.of(this.hostedZone).add(CDK.TAG.ROLE, CDK.ROLE.NETWORKING);
1083
+ cdk.Tags.of(this.hostedZone).add(CDK$2.TAG.SERVICE, service);
1084
+ cdk.Tags.of(this.hostedZone).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
828
1085
  if (project) {
829
- cdk.Tags.of(this.hostedZone).add(CDK.TAG.PROJECT, project);
1086
+ cdk.Tags.of(this.hostedZone).add(CDK$2.TAG.PROJECT, project);
1087
+ }
1088
+ }
1089
+ }
1090
+
1091
+ const CDK = {
1092
+ TAG: {
1093
+ STACK_SHA: "stackSha",
1094
+ },
1095
+ };
1096
+ class JaypieInfrastructureStack extends JaypieStack {
1097
+ constructor(scope, id, props = {}) {
1098
+ const { key = "infra", ...stackProps } = props;
1099
+ // Handle stackName
1100
+ if (!stackProps.stackName) {
1101
+ stackProps.stackName = constructStackName(key);
1102
+ }
1103
+ super(scope, id, { key, ...stackProps });
1104
+ // Add infrastructure-specific tag
1105
+ if (process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA) {
1106
+ Tags.of(this).add(CDK.TAG.STACK_SHA, process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA);
830
1107
  }
831
1108
  }
832
1109
  }
@@ -835,8 +1112,8 @@ class JaypieMongoDbSecret extends JaypieEnvSecret {
835
1112
  constructor(scope, id = "MongoConnectionString", props) {
836
1113
  const defaultProps = {
837
1114
  envKey: "MONGODB_URI",
838
- roleTag: CDK.ROLE.STORAGE,
839
- vendorTag: CDK.VENDOR.MONGODB,
1115
+ roleTag: CDK$2.ROLE.STORAGE,
1116
+ vendorTag: CDK$2.VENDOR.MONGODB,
840
1117
  ...props,
841
1118
  };
842
1119
  super(scope, id, defaultProps);
@@ -847,8 +1124,8 @@ class JaypieOpenAiSecret extends JaypieEnvSecret {
847
1124
  constructor(scope, id = "OpenAiApiKey", props) {
848
1125
  const defaultProps = {
849
1126
  envKey: "OPENAI_API_KEY",
850
- roleTag: CDK.ROLE.PROCESSING,
851
- vendorTag: CDK.VENDOR.OPENAI,
1127
+ roleTag: CDK$2.ROLE.PROCESSING,
1128
+ vendorTag: CDK$2.VENDOR.OPENAI,
852
1129
  ...props,
853
1130
  };
854
1131
  super(scope, id, defaultProps);
@@ -912,7 +1189,7 @@ class JaypieSsoGroups extends Construct {
912
1189
  managedPolicies: ["arn:aws:iam::aws:policy/AdministratorAccess"],
913
1190
  inlinePolicy: mergedPolicy,
914
1191
  });
915
- Tags.of(permissionSet).add(CDK.TAG.SERVICE, CDK.SERVICE.SSO);
1192
+ Tags.of(permissionSet).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
916
1193
  this.permissionSets[PermissionSetType.ADMINISTRATOR] = permissionSet;
917
1194
  }
918
1195
  /**
@@ -949,7 +1226,7 @@ class JaypieSsoGroups extends Construct {
949
1226
  managedPolicies: ["arn:aws:iam::aws:policy/ReadOnlyAccess"],
950
1227
  inlinePolicy: mergedPolicy,
951
1228
  });
952
- Tags.of(permissionSet).add(CDK.TAG.SERVICE, CDK.SERVICE.SSO);
1229
+ Tags.of(permissionSet).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
953
1230
  this.permissionSets[PermissionSetType.ANALYST] = permissionSet;
954
1231
  }
955
1232
  /**
@@ -1003,7 +1280,7 @@ class JaypieSsoGroups extends Construct {
1003
1280
  ],
1004
1281
  inlinePolicy: mergedPolicy,
1005
1282
  });
1006
- Tags.of(permissionSet).add(CDK.TAG.SERVICE, CDK.SERVICE.SSO);
1283
+ Tags.of(permissionSet).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1007
1284
  this.permissionSets[PermissionSetType.DEVELOPER] = permissionSet;
1008
1285
  }
1009
1286
  /**
@@ -1070,7 +1347,7 @@ class JaypieSsoGroups extends Construct {
1070
1347
  targetId: accountId,
1071
1348
  targetType: "AWS_ACCOUNT",
1072
1349
  });
1073
- Tags.of(assignment).add(CDK.TAG.SERVICE, CDK.SERVICE.SSO);
1350
+ Tags.of(assignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1074
1351
  Tags.of(assignment).add("Group", "administrators");
1075
1352
  });
1076
1353
  }
@@ -1097,7 +1374,7 @@ class JaypieSsoGroups extends Construct {
1097
1374
  targetId: accountId,
1098
1375
  targetType: "AWS_ACCOUNT",
1099
1376
  });
1100
- Tags.of(assignment).add(CDK.TAG.SERVICE, CDK.SERVICE.SSO);
1377
+ Tags.of(assignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1101
1378
  Tags.of(assignment).add("Group", "analysts");
1102
1379
  });
1103
1380
  }
@@ -1123,7 +1400,7 @@ class JaypieSsoGroups extends Construct {
1123
1400
  targetId: accountId,
1124
1401
  targetType: "AWS_ACCOUNT",
1125
1402
  });
1126
- Tags.of(assignment).add(CDK.TAG.SERVICE, CDK.SERVICE.SSO);
1403
+ Tags.of(assignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1127
1404
  Tags.of(assignment).add("Group", "developers");
1128
1405
  });
1129
1406
  }
@@ -1133,13 +1410,258 @@ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
1133
1410
  constructor(scope, id = "TraceSigningKey", props) {
1134
1411
  const defaultProps = {
1135
1412
  envKey: "TRACE_SIGNING_KEY",
1136
- roleTag: CDK.ROLE.API,
1137
- vendorTag: CDK.VENDOR.KNOWTRACE,
1413
+ roleTag: CDK$2.ROLE.API,
1414
+ vendorTag: CDK$2.VENDOR.KNOWTRACE,
1138
1415
  ...props,
1139
1416
  };
1140
1417
  super(scope, id, defaultProps);
1141
1418
  }
1142
1419
  }
1143
1420
 
1144
- export { JaypieBucketQueuedLambda, JaypieDatadogSecret, JaypieEnvSecret, JaypieExpressLambda, JaypieHostedZone, JaypieLambda, JaypieMongoDbSecret, JaypieOpenAiSecret, JaypieQueuedLambda, JaypieSsoGroups, JaypieTraceSigningKeySecret, PermissionSetType };
1421
+ class JaypieWebDeploymentBucket extends Construct {
1422
+ constructor(scope, id, props = {}) {
1423
+ super(scope, id);
1424
+ const roleTag = props.roleTag || CDK$2.ROLE.HOSTING;
1425
+ // Environment variable validation
1426
+ if (process.env.CDK_ENV_WEB_SUBDOMAIN &&
1427
+ !isValidSubdomain(process.env.CDK_ENV_WEB_SUBDOMAIN)) {
1428
+ throw new ConfigurationError("CDK_ENV_WEB_SUBDOMAIN is not a valid subdomain");
1429
+ }
1430
+ if (process.env.CDK_ENV_WEB_HOSTED_ZONE &&
1431
+ !isValidHostname(process.env.CDK_ENV_WEB_HOSTED_ZONE)) {
1432
+ throw new ConfigurationError("CDK_ENV_WEB_HOSTED_ZONE is not a valid hostname");
1433
+ }
1434
+ if (process.env.CDK_ENV_HOSTED_ZONE &&
1435
+ !isValidHostname(process.env.CDK_ENV_HOSTED_ZONE)) {
1436
+ throw new ConfigurationError("CDK_ENV_HOSTED_ZONE is not a valid hostname");
1437
+ }
1438
+ // Determine host from props or environment
1439
+ let host = props.host;
1440
+ if (!host) {
1441
+ try {
1442
+ host =
1443
+ process.env.CDK_ENV_WEB_HOST ||
1444
+ mergeDomain(process.env.CDK_ENV_WEB_SUBDOMAIN || "", process.env.CDK_ENV_WEB_HOSTED_ZONE ||
1445
+ process.env.CDK_ENV_HOSTED_ZONE ||
1446
+ "");
1447
+ }
1448
+ catch {
1449
+ host = undefined;
1450
+ }
1451
+ }
1452
+ if (host && !isValidHostname(host)) {
1453
+ throw new ConfigurationError("Host is not a valid hostname");
1454
+ }
1455
+ // Determine zone from props or environment
1456
+ const zone = props.zone ||
1457
+ process.env.CDK_ENV_WEB_HOSTED_ZONE ||
1458
+ process.env.CDK_ENV_HOSTED_ZONE;
1459
+ // Create the S3 bucket
1460
+ this.bucket = new s3.Bucket(this, "DestinationBucket", {
1461
+ accessControl: s3.BucketAccessControl.BUCKET_OWNER_FULL_CONTROL,
1462
+ autoDeleteObjects: true,
1463
+ blockPublicAccess: s3.BlockPublicAccess.BLOCK_ACLS,
1464
+ bucketName: props.name || constructEnvName("web"),
1465
+ publicReadAccess: true,
1466
+ removalPolicy: RemovalPolicy.DESTROY,
1467
+ versioned: false,
1468
+ websiteErrorDocument: "index.html",
1469
+ websiteIndexDocument: "index.html",
1470
+ ...props,
1471
+ });
1472
+ // Delegate IBucket properties to the bucket
1473
+ this.bucketArn = this.bucket.bucketArn;
1474
+ this.bucketDomainName = this.bucket.bucketDomainName;
1475
+ this.bucketDualStackDomainName = this.bucket.bucketDualStackDomainName;
1476
+ this.bucketName = this.bucket.bucketName;
1477
+ this.bucketRegionalDomainName = this.bucket.bucketRegionalDomainName;
1478
+ this.bucketWebsiteDomainName = this.bucket.bucketWebsiteDomainName;
1479
+ this.bucketWebsiteUrl = this.bucket.bucketWebsiteUrl;
1480
+ this.encryptionKey = this.bucket.encryptionKey;
1481
+ this.isWebsite = this.bucket.isWebsite;
1482
+ this.notificationsHandlerRole = undefined;
1483
+ this.policy = this.bucket.policy;
1484
+ Tags.of(this.bucket).add(CDK$2.TAG.ROLE, roleTag);
1485
+ // Create deployment role if repository is configured
1486
+ let repo;
1487
+ if (process.env.CDK_ENV_REPO) {
1488
+ repo = `repo:${process.env.CDK_ENV_REPO}:*`;
1489
+ }
1490
+ if (repo) {
1491
+ const bucketDeployRole = new Role(this, "DestinationBucketDeployRole", {
1492
+ assumedBy: new FederatedPrincipal(Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), {
1493
+ StringLike: {
1494
+ "token.actions.githubusercontent.com:sub": repo,
1495
+ },
1496
+ }, "sts:AssumeRoleWithWebIdentity"),
1497
+ maxSessionDuration: Duration.hours(1),
1498
+ });
1499
+ Tags.of(bucketDeployRole).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
1500
+ // Allow the role to write to the bucket
1501
+ bucketDeployRole.addToPolicy(new PolicyStatement({
1502
+ effect: Effect.ALLOW,
1503
+ actions: [
1504
+ "s3:DeleteObject",
1505
+ "s3:GetObject",
1506
+ "s3:ListObjectsV2",
1507
+ "s3:PutObject",
1508
+ ],
1509
+ resources: [`${this.bucket.bucketArn}/*`],
1510
+ }));
1511
+ bucketDeployRole.addToPolicy(new PolicyStatement({
1512
+ effect: Effect.ALLOW,
1513
+ actions: ["s3:ListBucket"],
1514
+ resources: [this.bucket.bucketArn],
1515
+ }));
1516
+ // Allow the role to deploy CDK apps
1517
+ bucketDeployRole.addToPolicy(new PolicyStatement({
1518
+ actions: ["cloudformation:DescribeStacks"],
1519
+ effect: Effect.ALLOW,
1520
+ resources: ["*"], // TODO: restrict to this stack
1521
+ }));
1522
+ this.deployRoleArn = bucketDeployRole.roleArn;
1523
+ // Output the deploy role ARN
1524
+ new CfnOutput(this, "DestinationBucketDeployRoleArn", {
1525
+ value: bucketDeployRole.roleArn,
1526
+ });
1527
+ }
1528
+ // Create CloudFront distribution and certificate if host and zone are provided
1529
+ if (host && zone) {
1530
+ const hostedZone = typeof zone === "string"
1531
+ ? route53.HostedZone.fromLookup(this, "HostedZone", {
1532
+ domainName: zone,
1533
+ })
1534
+ : zone;
1535
+ // Create certificate if not provided
1536
+ if (props.certificate !== false) {
1537
+ this.certificate =
1538
+ typeof props.certificate === "object"
1539
+ ? props.certificate
1540
+ : new acm.Certificate(this, "Certificate", {
1541
+ domainName: host,
1542
+ validation: acm.CertificateValidation.fromDns(hostedZone),
1543
+ });
1544
+ new CfnOutput(this, "CertificateArn", {
1545
+ value: this.certificate.certificateArn,
1546
+ });
1547
+ Tags.of(this.certificate).add(CDK$2.TAG.ROLE, roleTag);
1548
+ }
1549
+ // Create CloudFront distribution
1550
+ this.distribution = new cloudfront.Distribution(this, "Distribution", {
1551
+ defaultBehavior: {
1552
+ cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
1553
+ origin: new origins.S3Origin(this.bucket),
1554
+ viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
1555
+ },
1556
+ certificate: this.certificate,
1557
+ domainNames: [host],
1558
+ });
1559
+ Tags.of(this.distribution).add(CDK$2.TAG.ROLE, roleTag);
1560
+ // If this is production, enable caching on everything but index.html
1561
+ if (isProductionEnv()) {
1562
+ this.distribution.addBehavior("/*", new origins.S3Origin(this.bucket), {
1563
+ viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
1564
+ cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
1565
+ });
1566
+ }
1567
+ // Create DNS record
1568
+ const record = new route53.ARecord(this, "AliasRecord", {
1569
+ recordName: host,
1570
+ target: route53.RecordTarget.fromAlias(new route53Targets.CloudFrontTarget(this.distribution)),
1571
+ zone: hostedZone,
1572
+ });
1573
+ Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
1574
+ this.distributionDomainName = this.distribution.distributionDomainName;
1575
+ }
1576
+ }
1577
+ // Implement remaining IBucket methods by delegating to the bucket
1578
+ addEventNotification(event, dest, ...filters) {
1579
+ this.bucket.addEventNotification(event, dest, ...filters);
1580
+ }
1581
+ addObjectCreatedNotification(dest, ...filters) {
1582
+ this.bucket.addObjectCreatedNotification(dest, ...filters);
1583
+ }
1584
+ addObjectRemovedNotification(dest, ...filters) {
1585
+ this.bucket.addObjectRemovedNotification(dest, ...filters);
1586
+ }
1587
+ addToResourcePolicy(permission) {
1588
+ return this.bucket.addToResourcePolicy(permission);
1589
+ }
1590
+ arnForObjects(keyPattern) {
1591
+ return this.bucket.arnForObjects(keyPattern);
1592
+ }
1593
+ grantDelete(identity, objectsKeyPattern) {
1594
+ return this.bucket.grantDelete(identity, objectsKeyPattern);
1595
+ }
1596
+ grantPublicAccess(allowedActions, keyPrefix) {
1597
+ return keyPrefix
1598
+ ? this.bucket.grantPublicAccess(allowedActions, keyPrefix)
1599
+ : this.bucket.grantPublicAccess(allowedActions);
1600
+ }
1601
+ grantPut(identity, objectsKeyPattern) {
1602
+ return this.bucket.grantPut(identity, objectsKeyPattern);
1603
+ }
1604
+ grantPutAcl(identity, objectsKeyPattern) {
1605
+ return this.bucket.grantPutAcl(identity, objectsKeyPattern);
1606
+ }
1607
+ grantRead(identity, objectsKeyPattern) {
1608
+ return this.bucket.grantRead(identity, objectsKeyPattern);
1609
+ }
1610
+ grantReadWrite(identity, objectsKeyPattern) {
1611
+ return this.bucket.grantReadWrite(identity, objectsKeyPattern);
1612
+ }
1613
+ grantWrite(identity, objectsKeyPattern) {
1614
+ return this.bucket.grantWrite(identity, objectsKeyPattern);
1615
+ }
1616
+ s3UrlForObject(key) {
1617
+ return this.bucket.s3UrlForObject(key);
1618
+ }
1619
+ urlForObject(key) {
1620
+ return this.bucket.urlForObject(key);
1621
+ }
1622
+ virtualHostedUrlForObject(key, options) {
1623
+ return this.bucket.virtualHostedUrlForObject(key, options);
1624
+ }
1625
+ transferAccelerationUrlForObject(key) {
1626
+ return this.bucket.transferAccelerationUrlForObject(key);
1627
+ }
1628
+ onCloudTrailEvent(id, options) {
1629
+ return this.bucket.onCloudTrailEvent(id, options);
1630
+ }
1631
+ onCloudTrailPutObject(id, options) {
1632
+ return this.bucket.onCloudTrailPutObject(id, options);
1633
+ }
1634
+ onCloudTrailWriteObject(id, options) {
1635
+ return this.bucket.onCloudTrailWriteObject(id, options);
1636
+ }
1637
+ addCorsRule(rule) {
1638
+ this.bucket.addCorsRule(rule);
1639
+ }
1640
+ addInventory(inventory) {
1641
+ this.bucket.addInventory(inventory);
1642
+ }
1643
+ addLifecycleRule(rule) {
1644
+ this.bucket.addLifecycleRule(rule);
1645
+ }
1646
+ addMetric(metric) {
1647
+ this.bucket.addMetric(metric);
1648
+ }
1649
+ enableEventBridgeNotification() {
1650
+ this.bucket.enableEventBridgeNotification();
1651
+ }
1652
+ addReplicationPolicy(policy) {
1653
+ this.bucket.addReplicationPolicy(policy);
1654
+ }
1655
+ get stack() {
1656
+ return this.bucket.stack;
1657
+ }
1658
+ get env() {
1659
+ return this.bucket.env;
1660
+ }
1661
+ applyRemovalPolicy(policy) {
1662
+ this.bucket.applyRemovalPolicy(policy);
1663
+ }
1664
+ }
1665
+
1666
+ export { JaypieApiGateway, JaypieAppStack, JaypieBucketQueuedLambda, JaypieDatadogSecret, JaypieEnvSecret, JaypieExpressLambda, JaypieHostedZone, JaypieInfrastructureStack, JaypieLambda, JaypieMongoDbSecret, JaypieOpenAiSecret, JaypieQueuedLambda, JaypieSsoGroups, JaypieStack, JaypieTraceSigningKeySecret, JaypieWebDeploymentBucket, PermissionSetType, constructEnvName, constructStackName, isEnv, isProductionEnv, isSandboxEnv, stackTagger };
1145
1667
  //# sourceMappingURL=index.js.map