@jaypie/constructs 1.1.28 → 1.1.30

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, provisionedConcurrentExecutions, 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
@@ -132,11 +389,19 @@ class JaypieLambda extends Construct {
132
389
  const datadogApiKey = secretsmanager.Secret.fromSecretCompleteArn(this, "DatadogApiKeyGrant", resolvedDatadogApiKeyArn);
133
390
  datadogApiKey.grantRead(this._lambda);
134
391
  }
392
+ // Configure provisioned concurrency if specified
393
+ if (provisionedConcurrentExecutions !== undefined) {
394
+ new lambda.Alias(this, "ProvisionedAlias", {
395
+ aliasName: "provisioned",
396
+ version: this._lambda.currentVersion,
397
+ provisionedConcurrentExecutions,
398
+ });
399
+ }
135
400
  if (roleTag) {
136
- Tags.of(this._lambda).add(CDK.TAG.ROLE, roleTag);
401
+ Tags.of(this._lambda).add(CDK$2.TAG.ROLE, roleTag);
137
402
  }
138
403
  if (vendorTag) {
139
- Tags.of(this._lambda).add(CDK.TAG.VENDOR, vendorTag);
404
+ Tags.of(this._lambda).add(CDK$2.TAG.VENDOR, vendorTag);
140
405
  }
141
406
  }
142
407
  // Public accessors
@@ -246,7 +511,7 @@ class JaypieLambda extends Construct {
246
511
  class JaypieQueuedLambda extends Construct {
247
512
  constructor(scope, id, props) {
248
513
  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;
514
+ 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
515
  // Create SQS Queue
251
516
  this._queue = new sqs.Queue(this, "Queue", {
252
517
  fifo,
@@ -255,10 +520,10 @@ class JaypieQueuedLambda extends Construct {
255
520
  : visibilityTimeout,
256
521
  });
257
522
  if (roleTag) {
258
- Tags.of(this._queue).add(CDK.TAG.ROLE, roleTag);
523
+ Tags.of(this._queue).add(CDK$2.TAG.ROLE, roleTag);
259
524
  }
260
525
  if (vendorTag) {
261
- Tags.of(this._queue).add(CDK.TAG.VENDOR, vendorTag);
526
+ Tags.of(this._queue).add(CDK$2.TAG.VENDOR, vendorTag);
262
527
  }
263
528
  // Create Lambda with JaypieLambda
264
529
  this._lambdaConstruct = new JaypieLambda(this, "Function", {
@@ -464,10 +729,10 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
464
729
  });
465
730
  // Add tags to bucket
466
731
  if (roleTag) {
467
- Tags.of(this._bucket).add(CDK.TAG.ROLE, roleTag);
732
+ Tags.of(this._bucket).add(CDK$2.TAG.ROLE, roleTag);
468
733
  }
469
734
  if (vendorTag) {
470
- Tags.of(this._bucket).add(CDK.TAG.VENDOR, vendorTag);
735
+ Tags.of(this._bucket).add(CDK$2.TAG.VENDOR, vendorTag);
471
736
  }
472
737
  // Add an event notification from the bucket to the queue
473
738
  this._bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.SqsDestination(this.queue));
@@ -632,13 +897,13 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
632
897
 
633
898
  // It is a consumer if the environment is ephemeral
634
899
  function checkEnvIsConsumer(env = process.env) {
635
- return (env.PROJECT_ENV === CDK.ENV.PERSONAL ||
900
+ return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
636
901
  !!env.CDK_ENV_PERSONAL ||
637
902
  /** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
638
903
  /** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
639
904
  }
640
905
  function checkEnvIsProvider(env = process.env) {
641
- return env.PROJECT_ENV === CDK.ENV.SANDBOX;
906
+ return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
642
907
  }
643
908
  function cleanName(name) {
644
909
  return name.replace(/[^a-zA-Z0-9:-]/g, "");
@@ -652,7 +917,7 @@ function exportEnvName(name, env = process.env) {
652
917
  }
653
918
  else {
654
919
  if (checkEnvIsConsumer(env)) {
655
- rawName = `env-${CDK.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
920
+ rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
656
921
  }
657
922
  else {
658
923
  rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
@@ -689,10 +954,10 @@ class JaypieEnvSecret extends Construct {
689
954
  };
690
955
  this._secret = new secretsmanager.Secret(this, id, secretProps);
691
956
  if (roleTag) {
692
- Tags.of(this._secret).add(CDK.TAG.ROLE, roleTag);
957
+ Tags.of(this._secret).add(CDK$2.TAG.ROLE, roleTag);
693
958
  }
694
959
  if (vendorTag) {
695
- Tags.of(this._secret).add(CDK.TAG.VENDOR, vendorTag);
960
+ Tags.of(this._secret).add(CDK$2.TAG.VENDOR, vendorTag);
696
961
  }
697
962
  if (provider) {
698
963
  new CfnOutput(this, `ProvidedName`, {
@@ -766,8 +1031,8 @@ class JaypieDatadogSecret extends JaypieEnvSecret {
766
1031
  constructor(scope, id = "MongoConnectionString", props) {
767
1032
  const defaultProps = {
768
1033
  envKey: "DATADOG_API_KEY",
769
- roleTag: CDK.ROLE.MONITORING,
770
- vendorTag: CDK.VENDOR.DATADOG,
1034
+ roleTag: CDK$2.ROLE.MONITORING,
1035
+ vendorTag: CDK$2.VENDOR.DATADOG,
771
1036
  ...props,
772
1037
  };
773
1038
  super(scope, id, defaultProps);
@@ -777,8 +1042,8 @@ class JaypieDatadogSecret extends JaypieEnvSecret {
777
1042
  class JaypieExpressLambda extends JaypieLambda {
778
1043
  constructor(scope, id, props) {
779
1044
  super(scope, id, {
780
- timeout: Duration.seconds(CDK.DURATION.EXPRESS_API),
781
- roleTag: CDK.ROLE.API,
1045
+ timeout: Duration.seconds(CDK$2.DURATION.EXPRESS_API),
1046
+ roleTag: CDK$2.ROLE.API,
782
1047
  ...props,
783
1048
  });
784
1049
  }
@@ -794,7 +1059,7 @@ class JaypieHostedZone extends Construct {
794
1059
  constructor(scope, id, props) {
795
1060
  super(scope, id);
796
1061
  const { destination, zoneName, project } = props;
797
- const service = props.service || CDK.SERVICE.INFRASTRUCTURE;
1062
+ const service = props.service || CDK$2.SERVICE.INFRASTRUCTURE;
798
1063
  // Create the log group
799
1064
  this.logGroup = new LogGroup(this, "LogGroup", {
800
1065
  logGroupName: process.env.PROJECT_NONCE
@@ -803,10 +1068,10 @@ class JaypieHostedZone extends Construct {
803
1068
  retention: RetentionDays.ONE_WEEK,
804
1069
  });
805
1070
  // 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);
1071
+ cdk.Tags.of(this.logGroup).add(CDK$2.TAG.SERVICE, service);
1072
+ cdk.Tags.of(this.logGroup).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
808
1073
  if (project) {
809
- cdk.Tags.of(this.logGroup).add(CDK.TAG.PROJECT, project);
1074
+ cdk.Tags.of(this.logGroup).add(CDK$2.TAG.PROJECT, project);
810
1075
  }
811
1076
  // Grant Route 53 permissions to write to the log group
812
1077
  this.logGroup.grantWrite(new ServicePrincipal(SERVICE.ROUTE53));
@@ -823,10 +1088,30 @@ class JaypieHostedZone extends Construct {
823
1088
  zoneName,
824
1089
  });
825
1090
  // 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);
1091
+ cdk.Tags.of(this.hostedZone).add(CDK$2.TAG.SERVICE, service);
1092
+ cdk.Tags.of(this.hostedZone).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
828
1093
  if (project) {
829
- cdk.Tags.of(this.hostedZone).add(CDK.TAG.PROJECT, project);
1094
+ cdk.Tags.of(this.hostedZone).add(CDK$2.TAG.PROJECT, project);
1095
+ }
1096
+ }
1097
+ }
1098
+
1099
+ const CDK = {
1100
+ TAG: {
1101
+ STACK_SHA: "stackSha",
1102
+ },
1103
+ };
1104
+ class JaypieInfrastructureStack extends JaypieStack {
1105
+ constructor(scope, id, props = {}) {
1106
+ const { key = "infra", ...stackProps } = props;
1107
+ // Handle stackName
1108
+ if (!stackProps.stackName) {
1109
+ stackProps.stackName = constructStackName(key);
1110
+ }
1111
+ super(scope, id, { key, ...stackProps });
1112
+ // Add infrastructure-specific tag
1113
+ if (process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA) {
1114
+ Tags.of(this).add(CDK.TAG.STACK_SHA, process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA);
830
1115
  }
831
1116
  }
832
1117
  }
@@ -835,8 +1120,8 @@ class JaypieMongoDbSecret extends JaypieEnvSecret {
835
1120
  constructor(scope, id = "MongoConnectionString", props) {
836
1121
  const defaultProps = {
837
1122
  envKey: "MONGODB_URI",
838
- roleTag: CDK.ROLE.STORAGE,
839
- vendorTag: CDK.VENDOR.MONGODB,
1123
+ roleTag: CDK$2.ROLE.STORAGE,
1124
+ vendorTag: CDK$2.VENDOR.MONGODB,
840
1125
  ...props,
841
1126
  };
842
1127
  super(scope, id, defaultProps);
@@ -847,8 +1132,8 @@ class JaypieOpenAiSecret extends JaypieEnvSecret {
847
1132
  constructor(scope, id = "OpenAiApiKey", props) {
848
1133
  const defaultProps = {
849
1134
  envKey: "OPENAI_API_KEY",
850
- roleTag: CDK.ROLE.PROCESSING,
851
- vendorTag: CDK.VENDOR.OPENAI,
1135
+ roleTag: CDK$2.ROLE.PROCESSING,
1136
+ vendorTag: CDK$2.VENDOR.OPENAI,
852
1137
  ...props,
853
1138
  };
854
1139
  super(scope, id, defaultProps);
@@ -912,7 +1197,7 @@ class JaypieSsoGroups extends Construct {
912
1197
  managedPolicies: ["arn:aws:iam::aws:policy/AdministratorAccess"],
913
1198
  inlinePolicy: mergedPolicy,
914
1199
  });
915
- Tags.of(permissionSet).add(CDK.TAG.SERVICE, CDK.SERVICE.SSO);
1200
+ Tags.of(permissionSet).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
916
1201
  this.permissionSets[PermissionSetType.ADMINISTRATOR] = permissionSet;
917
1202
  }
918
1203
  /**
@@ -949,7 +1234,7 @@ class JaypieSsoGroups extends Construct {
949
1234
  managedPolicies: ["arn:aws:iam::aws:policy/ReadOnlyAccess"],
950
1235
  inlinePolicy: mergedPolicy,
951
1236
  });
952
- Tags.of(permissionSet).add(CDK.TAG.SERVICE, CDK.SERVICE.SSO);
1237
+ Tags.of(permissionSet).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
953
1238
  this.permissionSets[PermissionSetType.ANALYST] = permissionSet;
954
1239
  }
955
1240
  /**
@@ -1003,7 +1288,7 @@ class JaypieSsoGroups extends Construct {
1003
1288
  ],
1004
1289
  inlinePolicy: mergedPolicy,
1005
1290
  });
1006
- Tags.of(permissionSet).add(CDK.TAG.SERVICE, CDK.SERVICE.SSO);
1291
+ Tags.of(permissionSet).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1007
1292
  this.permissionSets[PermissionSetType.DEVELOPER] = permissionSet;
1008
1293
  }
1009
1294
  /**
@@ -1070,7 +1355,7 @@ class JaypieSsoGroups extends Construct {
1070
1355
  targetId: accountId,
1071
1356
  targetType: "AWS_ACCOUNT",
1072
1357
  });
1073
- Tags.of(assignment).add(CDK.TAG.SERVICE, CDK.SERVICE.SSO);
1358
+ Tags.of(assignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1074
1359
  Tags.of(assignment).add("Group", "administrators");
1075
1360
  });
1076
1361
  }
@@ -1097,7 +1382,7 @@ class JaypieSsoGroups extends Construct {
1097
1382
  targetId: accountId,
1098
1383
  targetType: "AWS_ACCOUNT",
1099
1384
  });
1100
- Tags.of(assignment).add(CDK.TAG.SERVICE, CDK.SERVICE.SSO);
1385
+ Tags.of(assignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1101
1386
  Tags.of(assignment).add("Group", "analysts");
1102
1387
  });
1103
1388
  }
@@ -1123,7 +1408,7 @@ class JaypieSsoGroups extends Construct {
1123
1408
  targetId: accountId,
1124
1409
  targetType: "AWS_ACCOUNT",
1125
1410
  });
1126
- Tags.of(assignment).add(CDK.TAG.SERVICE, CDK.SERVICE.SSO);
1411
+ Tags.of(assignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
1127
1412
  Tags.of(assignment).add("Group", "developers");
1128
1413
  });
1129
1414
  }
@@ -1133,13 +1418,258 @@ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
1133
1418
  constructor(scope, id = "TraceSigningKey", props) {
1134
1419
  const defaultProps = {
1135
1420
  envKey: "TRACE_SIGNING_KEY",
1136
- roleTag: CDK.ROLE.API,
1137
- vendorTag: CDK.VENDOR.KNOWTRACE,
1421
+ roleTag: CDK$2.ROLE.API,
1422
+ vendorTag: CDK$2.VENDOR.KNOWTRACE,
1138
1423
  ...props,
1139
1424
  };
1140
1425
  super(scope, id, defaultProps);
1141
1426
  }
1142
1427
  }
1143
1428
 
1144
- export { JaypieBucketQueuedLambda, JaypieDatadogSecret, JaypieEnvSecret, JaypieExpressLambda, JaypieHostedZone, JaypieLambda, JaypieMongoDbSecret, JaypieOpenAiSecret, JaypieQueuedLambda, JaypieSsoGroups, JaypieTraceSigningKeySecret, PermissionSetType };
1429
+ class JaypieWebDeploymentBucket extends Construct {
1430
+ constructor(scope, id, props = {}) {
1431
+ super(scope, id);
1432
+ const roleTag = props.roleTag || CDK$2.ROLE.HOSTING;
1433
+ // Environment variable validation
1434
+ if (process.env.CDK_ENV_WEB_SUBDOMAIN &&
1435
+ !isValidSubdomain(process.env.CDK_ENV_WEB_SUBDOMAIN)) {
1436
+ throw new ConfigurationError("CDK_ENV_WEB_SUBDOMAIN is not a valid subdomain");
1437
+ }
1438
+ if (process.env.CDK_ENV_WEB_HOSTED_ZONE &&
1439
+ !isValidHostname(process.env.CDK_ENV_WEB_HOSTED_ZONE)) {
1440
+ throw new ConfigurationError("CDK_ENV_WEB_HOSTED_ZONE is not a valid hostname");
1441
+ }
1442
+ if (process.env.CDK_ENV_HOSTED_ZONE &&
1443
+ !isValidHostname(process.env.CDK_ENV_HOSTED_ZONE)) {
1444
+ throw new ConfigurationError("CDK_ENV_HOSTED_ZONE is not a valid hostname");
1445
+ }
1446
+ // Determine host from props or environment
1447
+ let host = props.host;
1448
+ if (!host) {
1449
+ try {
1450
+ host =
1451
+ process.env.CDK_ENV_WEB_HOST ||
1452
+ mergeDomain(process.env.CDK_ENV_WEB_SUBDOMAIN || "", process.env.CDK_ENV_WEB_HOSTED_ZONE ||
1453
+ process.env.CDK_ENV_HOSTED_ZONE ||
1454
+ "");
1455
+ }
1456
+ catch {
1457
+ host = undefined;
1458
+ }
1459
+ }
1460
+ if (host && !isValidHostname(host)) {
1461
+ throw new ConfigurationError("Host is not a valid hostname");
1462
+ }
1463
+ // Determine zone from props or environment
1464
+ const zone = props.zone ||
1465
+ process.env.CDK_ENV_WEB_HOSTED_ZONE ||
1466
+ process.env.CDK_ENV_HOSTED_ZONE;
1467
+ // Create the S3 bucket
1468
+ this.bucket = new s3.Bucket(this, "DestinationBucket", {
1469
+ accessControl: s3.BucketAccessControl.BUCKET_OWNER_FULL_CONTROL,
1470
+ autoDeleteObjects: true,
1471
+ blockPublicAccess: s3.BlockPublicAccess.BLOCK_ACLS,
1472
+ bucketName: props.name || constructEnvName("web"),
1473
+ publicReadAccess: true,
1474
+ removalPolicy: RemovalPolicy.DESTROY,
1475
+ versioned: false,
1476
+ websiteErrorDocument: "index.html",
1477
+ websiteIndexDocument: "index.html",
1478
+ ...props,
1479
+ });
1480
+ // Delegate IBucket properties to the bucket
1481
+ this.bucketArn = this.bucket.bucketArn;
1482
+ this.bucketDomainName = this.bucket.bucketDomainName;
1483
+ this.bucketDualStackDomainName = this.bucket.bucketDualStackDomainName;
1484
+ this.bucketName = this.bucket.bucketName;
1485
+ this.bucketRegionalDomainName = this.bucket.bucketRegionalDomainName;
1486
+ this.bucketWebsiteDomainName = this.bucket.bucketWebsiteDomainName;
1487
+ this.bucketWebsiteUrl = this.bucket.bucketWebsiteUrl;
1488
+ this.encryptionKey = this.bucket.encryptionKey;
1489
+ this.isWebsite = this.bucket.isWebsite;
1490
+ this.notificationsHandlerRole = undefined;
1491
+ this.policy = this.bucket.policy;
1492
+ Tags.of(this.bucket).add(CDK$2.TAG.ROLE, roleTag);
1493
+ // Create deployment role if repository is configured
1494
+ let repo;
1495
+ if (process.env.CDK_ENV_REPO) {
1496
+ repo = `repo:${process.env.CDK_ENV_REPO}:*`;
1497
+ }
1498
+ if (repo) {
1499
+ const bucketDeployRole = new Role(this, "DestinationBucketDeployRole", {
1500
+ assumedBy: new FederatedPrincipal(Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), {
1501
+ StringLike: {
1502
+ "token.actions.githubusercontent.com:sub": repo,
1503
+ },
1504
+ }, "sts:AssumeRoleWithWebIdentity"),
1505
+ maxSessionDuration: Duration.hours(1),
1506
+ });
1507
+ Tags.of(bucketDeployRole).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
1508
+ // Allow the role to write to the bucket
1509
+ bucketDeployRole.addToPolicy(new PolicyStatement({
1510
+ effect: Effect.ALLOW,
1511
+ actions: [
1512
+ "s3:DeleteObject",
1513
+ "s3:GetObject",
1514
+ "s3:ListObjectsV2",
1515
+ "s3:PutObject",
1516
+ ],
1517
+ resources: [`${this.bucket.bucketArn}/*`],
1518
+ }));
1519
+ bucketDeployRole.addToPolicy(new PolicyStatement({
1520
+ effect: Effect.ALLOW,
1521
+ actions: ["s3:ListBucket"],
1522
+ resources: [this.bucket.bucketArn],
1523
+ }));
1524
+ // Allow the role to deploy CDK apps
1525
+ bucketDeployRole.addToPolicy(new PolicyStatement({
1526
+ actions: ["cloudformation:DescribeStacks"],
1527
+ effect: Effect.ALLOW,
1528
+ resources: ["*"], // TODO: restrict to this stack
1529
+ }));
1530
+ this.deployRoleArn = bucketDeployRole.roleArn;
1531
+ // Output the deploy role ARN
1532
+ new CfnOutput(this, "DestinationBucketDeployRoleArn", {
1533
+ value: bucketDeployRole.roleArn,
1534
+ });
1535
+ }
1536
+ // Create CloudFront distribution and certificate if host and zone are provided
1537
+ if (host && zone) {
1538
+ const hostedZone = typeof zone === "string"
1539
+ ? route53.HostedZone.fromLookup(this, "HostedZone", {
1540
+ domainName: zone,
1541
+ })
1542
+ : zone;
1543
+ // Create certificate if not provided
1544
+ if (props.certificate !== false) {
1545
+ this.certificate =
1546
+ typeof props.certificate === "object"
1547
+ ? props.certificate
1548
+ : new acm.Certificate(this, "Certificate", {
1549
+ domainName: host,
1550
+ validation: acm.CertificateValidation.fromDns(hostedZone),
1551
+ });
1552
+ new CfnOutput(this, "CertificateArn", {
1553
+ value: this.certificate.certificateArn,
1554
+ });
1555
+ Tags.of(this.certificate).add(CDK$2.TAG.ROLE, roleTag);
1556
+ }
1557
+ // Create CloudFront distribution
1558
+ this.distribution = new cloudfront.Distribution(this, "Distribution", {
1559
+ defaultBehavior: {
1560
+ cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
1561
+ origin: new origins.S3Origin(this.bucket),
1562
+ viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
1563
+ },
1564
+ certificate: this.certificate,
1565
+ domainNames: [host],
1566
+ });
1567
+ Tags.of(this.distribution).add(CDK$2.TAG.ROLE, roleTag);
1568
+ // If this is production, enable caching on everything but index.html
1569
+ if (isProductionEnv()) {
1570
+ this.distribution.addBehavior("/*", new origins.S3Origin(this.bucket), {
1571
+ viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
1572
+ cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
1573
+ });
1574
+ }
1575
+ // Create DNS record
1576
+ const record = new route53.ARecord(this, "AliasRecord", {
1577
+ recordName: host,
1578
+ target: route53.RecordTarget.fromAlias(new route53Targets.CloudFrontTarget(this.distribution)),
1579
+ zone: hostedZone,
1580
+ });
1581
+ Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
1582
+ this.distributionDomainName = this.distribution.distributionDomainName;
1583
+ }
1584
+ }
1585
+ // Implement remaining IBucket methods by delegating to the bucket
1586
+ addEventNotification(event, dest, ...filters) {
1587
+ this.bucket.addEventNotification(event, dest, ...filters);
1588
+ }
1589
+ addObjectCreatedNotification(dest, ...filters) {
1590
+ this.bucket.addObjectCreatedNotification(dest, ...filters);
1591
+ }
1592
+ addObjectRemovedNotification(dest, ...filters) {
1593
+ this.bucket.addObjectRemovedNotification(dest, ...filters);
1594
+ }
1595
+ addToResourcePolicy(permission) {
1596
+ return this.bucket.addToResourcePolicy(permission);
1597
+ }
1598
+ arnForObjects(keyPattern) {
1599
+ return this.bucket.arnForObjects(keyPattern);
1600
+ }
1601
+ grantDelete(identity, objectsKeyPattern) {
1602
+ return this.bucket.grantDelete(identity, objectsKeyPattern);
1603
+ }
1604
+ grantPublicAccess(allowedActions, keyPrefix) {
1605
+ return keyPrefix
1606
+ ? this.bucket.grantPublicAccess(allowedActions, keyPrefix)
1607
+ : this.bucket.grantPublicAccess(allowedActions);
1608
+ }
1609
+ grantPut(identity, objectsKeyPattern) {
1610
+ return this.bucket.grantPut(identity, objectsKeyPattern);
1611
+ }
1612
+ grantPutAcl(identity, objectsKeyPattern) {
1613
+ return this.bucket.grantPutAcl(identity, objectsKeyPattern);
1614
+ }
1615
+ grantRead(identity, objectsKeyPattern) {
1616
+ return this.bucket.grantRead(identity, objectsKeyPattern);
1617
+ }
1618
+ grantReadWrite(identity, objectsKeyPattern) {
1619
+ return this.bucket.grantReadWrite(identity, objectsKeyPattern);
1620
+ }
1621
+ grantWrite(identity, objectsKeyPattern) {
1622
+ return this.bucket.grantWrite(identity, objectsKeyPattern);
1623
+ }
1624
+ s3UrlForObject(key) {
1625
+ return this.bucket.s3UrlForObject(key);
1626
+ }
1627
+ urlForObject(key) {
1628
+ return this.bucket.urlForObject(key);
1629
+ }
1630
+ virtualHostedUrlForObject(key, options) {
1631
+ return this.bucket.virtualHostedUrlForObject(key, options);
1632
+ }
1633
+ transferAccelerationUrlForObject(key) {
1634
+ return this.bucket.transferAccelerationUrlForObject(key);
1635
+ }
1636
+ onCloudTrailEvent(id, options) {
1637
+ return this.bucket.onCloudTrailEvent(id, options);
1638
+ }
1639
+ onCloudTrailPutObject(id, options) {
1640
+ return this.bucket.onCloudTrailPutObject(id, options);
1641
+ }
1642
+ onCloudTrailWriteObject(id, options) {
1643
+ return this.bucket.onCloudTrailWriteObject(id, options);
1644
+ }
1645
+ addCorsRule(rule) {
1646
+ this.bucket.addCorsRule(rule);
1647
+ }
1648
+ addInventory(inventory) {
1649
+ this.bucket.addInventory(inventory);
1650
+ }
1651
+ addLifecycleRule(rule) {
1652
+ this.bucket.addLifecycleRule(rule);
1653
+ }
1654
+ addMetric(metric) {
1655
+ this.bucket.addMetric(metric);
1656
+ }
1657
+ enableEventBridgeNotification() {
1658
+ this.bucket.enableEventBridgeNotification();
1659
+ }
1660
+ addReplicationPolicy(policy) {
1661
+ this.bucket.addReplicationPolicy(policy);
1662
+ }
1663
+ get stack() {
1664
+ return this.bucket.stack;
1665
+ }
1666
+ get env() {
1667
+ return this.bucket.env;
1668
+ }
1669
+ applyRemovalPolicy(policy) {
1670
+ this.bucket.applyRemovalPolicy(policy);
1671
+ }
1672
+ }
1673
+
1674
+ 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
1675
  //# sourceMappingURL=index.js.map