@jaypie/constructs 1.1.52 → 1.1.54

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.
@@ -1,25 +1,27 @@
1
1
  'use strict';
2
2
 
3
- var cdk = require('@jaypie/cdk');
3
+ var cdk = require('aws-cdk-lib');
4
+ var s3 = require('aws-cdk-lib/aws-s3');
4
5
  var constructs = require('constructs');
5
- var cdk$1 = require('aws-cdk-lib');
6
6
  var acm = require('aws-cdk-lib/aws-certificatemanager');
7
7
  var apiGateway = require('aws-cdk-lib/aws-apigateway');
8
8
  var route53 = require('aws-cdk-lib/aws-route53');
9
9
  var route53Targets = require('aws-cdk-lib/aws-route53-targets');
10
10
  var secretsmanager = require('aws-cdk-lib/aws-secretsmanager');
11
11
  var datadogCdkConstructsV2 = require('datadog-cdk-constructs-v2');
12
+ var errors = require('@jaypie/errors');
13
+ var awsIam = require('aws-cdk-lib/aws-iam');
12
14
  var lambda = require('aws-cdk-lib/aws-lambda');
13
15
  var logDestinations = require('aws-cdk-lib/aws-logs-destinations');
14
- var s3 = require('aws-cdk-lib/aws-s3');
15
16
  var s3n = require('aws-cdk-lib/aws-s3-notifications');
16
17
  var sqs = require('aws-cdk-lib/aws-sqs');
17
18
  var lambdaEventSources = require('aws-cdk-lib/aws-lambda-event-sources');
18
- var awsIam = require('aws-cdk-lib/aws-iam');
19
+ var awsEvents = require('aws-cdk-lib/aws-events');
20
+ var awsEventsTargets = require('aws-cdk-lib/aws-events-targets');
19
21
  var awsLogs = require('aws-cdk-lib/aws-logs');
22
+ var awsCloudtrail = require('aws-cdk-lib/aws-cloudtrail');
20
23
  var awsSso = require('aws-cdk-lib/aws-sso');
21
24
  var awsSam = require('aws-cdk-lib/aws-sam');
22
- var errors = require('@jaypie/errors');
23
25
  var cloudfront = require('aws-cdk-lib/aws-cloudfront');
24
26
  var origins = require('aws-cdk-lib/aws-cloudfront-origins');
25
27
 
@@ -40,7 +42,8 @@ function _interopNamespaceDefault(e) {
40
42
  return Object.freeze(n);
41
43
  }
42
44
 
43
- var cdk__namespace = /*#__PURE__*/_interopNamespaceDefault(cdk$1);
45
+ var cdk__namespace = /*#__PURE__*/_interopNamespaceDefault(cdk);
46
+ var s3__namespace = /*#__PURE__*/_interopNamespaceDefault(s3);
44
47
  var acm__namespace = /*#__PURE__*/_interopNamespaceDefault(acm);
45
48
  var apiGateway__namespace = /*#__PURE__*/_interopNamespaceDefault(apiGateway);
46
49
  var route53__namespace = /*#__PURE__*/_interopNamespaceDefault(route53);
@@ -48,13 +51,231 @@ var route53Targets__namespace = /*#__PURE__*/_interopNamespaceDefault(route53Tar
48
51
  var secretsmanager__namespace = /*#__PURE__*/_interopNamespaceDefault(secretsmanager);
49
52
  var lambda__namespace = /*#__PURE__*/_interopNamespaceDefault(lambda);
50
53
  var logDestinations__namespace = /*#__PURE__*/_interopNamespaceDefault(logDestinations);
51
- var s3__namespace = /*#__PURE__*/_interopNamespaceDefault(s3);
52
54
  var s3n__namespace = /*#__PURE__*/_interopNamespaceDefault(s3n);
53
55
  var sqs__namespace = /*#__PURE__*/_interopNamespaceDefault(sqs);
54
56
  var lambdaEventSources__namespace = /*#__PURE__*/_interopNamespaceDefault(lambdaEventSources);
55
57
  var cloudfront__namespace = /*#__PURE__*/_interopNamespaceDefault(cloudfront);
56
58
  var origins__namespace = /*#__PURE__*/_interopNamespaceDefault(origins);
57
59
 
60
+ const CDK$2 = {
61
+ ACCOUNT: {
62
+ DEVELOPMENT: "development",
63
+ MANAGEMENT: "management",
64
+ OPERATIONS: "operations",
65
+ PRODUCTION: "production",
66
+ SANDBOX: "sandbox",
67
+ SECURITY: "security",
68
+ STAGE: "stage",
69
+ },
70
+ BUILD: {
71
+ CONFIG: {
72
+ ALL: "all",
73
+ API: "api",
74
+ INFRASTRUCTURE: "infrastructure",
75
+ NONE: "none",
76
+ WEB: "web",
77
+ },
78
+ PERSONAL: "personal",
79
+ /**
80
+ * @deprecated rename "ephemeral" to "personal" (since 2/24/2025)
81
+ */
82
+ EPHEMERAL: "ephemeral",
83
+ /**
84
+ * @deprecated as even "ephemeral" builds have static assets (since 7/6/2024)
85
+ */
86
+ STATIC: "static",
87
+ },
88
+ CREATION: {
89
+ CDK: "cdk",
90
+ CLOUDFORMATION_TEMPLATE: "template",
91
+ MANUAL: "manual",
92
+ },
93
+ DATADOG: {
94
+ SITE: "datadoghq.com",
95
+ LAYER: {
96
+ // https://docs.datadoghq.com/serverless/aws_lambda/installation/nodejs/?tab=awscdk
97
+ NODE: 127, // 127 on 9/12/2025
98
+ EXTENSION: 86, // 86 on 9/12/2025
99
+ },
100
+ },
101
+ DEFAULT: {
102
+ REGION: "us-east-1",
103
+ },
104
+ DNS: {
105
+ CONFIG: {
106
+ TTL: 300, // 5 minutes in seconds for Route53
107
+ },
108
+ RECORD: {
109
+ A: "A",
110
+ CNAME: "CNAME",
111
+ MX: "MX",
112
+ NS: "NS",
113
+ TXT: "TXT",
114
+ },
115
+ },
116
+ DURATION: {
117
+ EXPRESS_API: 30,
118
+ LAMBDA_MAXIMUM: 900,
119
+ LAMBDA_WORKER: 900,
120
+ },
121
+ ENV: {
122
+ DEMO: "demo", // Mirror of production
123
+ DEVELOPMENT: "development", // Internal most stable development space
124
+ /** @deprecated */ EPHEMERAL: "ephemeral", // Alias for "build"
125
+ LOCAL: "local",
126
+ /** @deprecated */ MAIN: "main", // Alias for development
127
+ META: "meta", // For non-environment/infrastructure stacks
128
+ PERSONAL: "personal", // Personal builds using resources provided by sandbox
129
+ PREVIEW: "preview", // External next thing to be released
130
+ PRODUCTION: "production",
131
+ RELEASE: "release", // Internal next thing to be released
132
+ REVIEW: "review", // Internal place to collaborate on issues
133
+ SANDBOX: "sandbox", // Internal build space with no guaranteed longevity
134
+ TRAINING: "training", // aka "test"; mirror of production for external audiences
135
+ },
136
+ HOST: {
137
+ APEX: "@",
138
+ },
139
+ IMPORT: {
140
+ DATADOG_LOG_FORWARDER: "account-datadog-forwarder",
141
+ DATADOG_ROLE: "account-datadog-role",
142
+ DATADOG_SECRET: "account-datadog-secret",
143
+ LOG_BUCKET: "account-log-bucket",
144
+ OIDC_PROVIDER: "github-oidc-provider",
145
+ },
146
+ LAMBDA: {
147
+ LOG_RETENTION: 90,
148
+ MEMORY_SIZE: 1024,
149
+ },
150
+ PRINCIPAL: {
151
+ ROUTE53: "route53.amazonaws.com",
152
+ },
153
+ PRINCIPAL_TYPE: {
154
+ GROUP: "GROUP",
155
+ USER: "USER",
156
+ },
157
+ PROJECT: {
158
+ INFRASTRUCTURE: "infrastructure",
159
+ },
160
+ ROLE: {
161
+ API: "api",
162
+ DEPLOY: "deploy",
163
+ HOSTING: "hosting",
164
+ MONITORING: "monitoring",
165
+ NETWORKING: "networking",
166
+ PROCESSING: "processing",
167
+ SECURITY: "security",
168
+ STACK: "stack",
169
+ STORAGE: "storage",
170
+ TOY: "toy",
171
+ },
172
+ SERVICE: {
173
+ DATADOG: "datadog",
174
+ INFRASTRUCTURE: "infrastructure",
175
+ LIBRARIES: "libraries",
176
+ NONE: "none",
177
+ SSO: "sso",
178
+ TRACE: "trace",
179
+ },
180
+ TAG: {
181
+ BUILD_DATE: "buildDate",
182
+ BUILD_HEX: "buildHex",
183
+ BUILD_NUMBER: "buildNumber",
184
+ BUILD_TIME: "buildTime",
185
+ BUILD_TYPE: "buildType",
186
+ COMMIT: "commit",
187
+ CREATION: "creation",
188
+ ENV: "env",
189
+ NONCE: "nonce",
190
+ PROJECT: "project",
191
+ ROLE: "role",
192
+ SERVICE: "service",
193
+ SPONSOR: "sponsor",
194
+ STACK: "stack",
195
+ STACK_SHA: "stackSha",
196
+ VENDOR: "vendor",
197
+ VERSION: "version",
198
+ },
199
+ TARGET_TYPE: {
200
+ AWS_ACCOUNT: "AWS_ACCOUNT",
201
+ },
202
+ VENDOR: {
203
+ ANTHROPIC: "anthropic",
204
+ AUTH0: "auth0",
205
+ DATADOG: "datadog",
206
+ KNOWTRACE: "knowtrace",
207
+ MONGODB: "mongodb",
208
+ OPENAI: "openai",
209
+ SPLINTERLANDS: "splinterlands",
210
+ },
211
+ };
212
+
213
+ class JaypieAccountLoggingBucket extends constructs.Construct {
214
+ /**
215
+ * Create a new account-wide logging S3 bucket with lifecycle policies and export
216
+ */
217
+ constructor(scope, idOrProps, propsOrUndefined) {
218
+ // Handle overloaded constructor signatures
219
+ let props;
220
+ let id;
221
+ if (typeof idOrProps === "string") {
222
+ // First param is ID, second is props
223
+ props = propsOrUndefined || {};
224
+ id = idOrProps;
225
+ }
226
+ else {
227
+ // First param is props
228
+ props = idOrProps || {};
229
+ id = props.id || "AccountLoggingBucket";
230
+ }
231
+ super(scope, id);
232
+ // Generate default bucket name with PROJECT_NONCE
233
+ const defaultBucketName = process.env.PROJECT_NONCE
234
+ ? `account-logging-stack-${process.env.PROJECT_NONCE.toLowerCase()}`
235
+ : "account-logging-stack";
236
+ // Extract Jaypie-specific options
237
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
238
+ const { bucketName = defaultBucketName, createOutput = true, expirationDays = 365, exportName = CDK$2.IMPORT.LOG_BUCKET, glacierTransitionDays = 180, id: _id, infrequentAccessTransitionDays = 30, outputDescription = "Account-wide logging bucket", project, service = CDK$2.SERVICE.INFRASTRUCTURE, ...bucketProps } = props;
239
+ // Create the bucket with lifecycle rules
240
+ this.bucket = new s3.Bucket(this, "Bucket", {
241
+ accessControl: s3.BucketAccessControl.LOG_DELIVERY_WRITE,
242
+ bucketName,
243
+ lifecycleRules: [
244
+ {
245
+ expiration: cdk__namespace.Duration.days(expirationDays),
246
+ transitions: [
247
+ {
248
+ storageClass: s3.StorageClass.INFREQUENT_ACCESS,
249
+ transitionAfter: cdk__namespace.Duration.days(infrequentAccessTransitionDays),
250
+ },
251
+ {
252
+ storageClass: s3.StorageClass.GLACIER,
253
+ transitionAfter: cdk__namespace.Duration.days(glacierTransitionDays),
254
+ },
255
+ ],
256
+ },
257
+ ],
258
+ ...bucketProps,
259
+ });
260
+ // Add tags
261
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
262
+ if (service) {
263
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
264
+ }
265
+ if (project) {
266
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
267
+ }
268
+ // Create CloudFormation output if enabled
269
+ if (createOutput) {
270
+ new cdk__namespace.CfnOutput(this, "BucketNameOutput", {
271
+ description: outputDescription,
272
+ exportName,
273
+ value: this.bucket.bucketName,
274
+ });
275
+ }
276
+ }
277
+ }
278
+
58
279
  function addDatadogLayers(lambdaFunction, options = {}) {
59
280
  const datadogApiKeyArn = options?.datadogApiKeyArn;
60
281
  const resolvedDatadogApiKeyArn = datadogApiKeyArn ||
@@ -71,8 +292,8 @@ function addDatadogLayers(lambdaFunction, options = {}) {
71
292
  DD_PROFILING_ENABLED: "false",
72
293
  DD_SERVERLESS_APPSEC_ENABLED: "false",
73
294
  DD_SERVICE: process.env.PROJECT_SERVICE || "",
74
- DD_SITE: cdk.CDK.DATADOG.SITE,
75
- DD_TAGS: `${cdk.CDK.TAG.SPONSOR}:${process.env.PROJECT_SPONSOR || ""}`,
295
+ DD_SITE: CDK$2.DATADOG.SITE,
296
+ DD_TAGS: `${CDK$2.TAG.SPONSOR}:${process.env.PROJECT_SPONSOR || ""}`,
76
297
  DD_TRACE_OTEL_ENABLED: "false",
77
298
  };
78
299
  // Add environment variables only if they don't already exist
@@ -82,8 +303,8 @@ function addDatadogLayers(lambdaFunction, options = {}) {
82
303
  const datadogApiKeySecret = secretsmanager__namespace.Secret.fromSecretCompleteArn(lambdaFunction, "DatadogApiKey", resolvedDatadogApiKeyArn);
83
304
  const datadogLambda = new datadogCdkConstructsV2.DatadogLambda(lambdaFunction, "DatadogLambda", {
84
305
  apiKeySecret: datadogApiKeySecret, // apiKeySecret auto-grants secret access to the added lambdas
85
- nodeLayerVersion: cdk.CDK.DATADOG.LAYER.NODE,
86
- extensionLayerVersion: cdk.CDK.DATADOG.LAYER.EXTENSION,
306
+ nodeLayerVersion: CDK$2.DATADOG.LAYER.NODE,
307
+ extensionLayerVersion: CDK$2.DATADOG.LAYER.EXTENSION,
87
308
  env: process.env.PROJECT_ENV,
88
309
  service: process.env.PROJECT_SERVICE,
89
310
  version: process.env.PROJECT_VERSION,
@@ -135,35 +356,35 @@ function constructTagger(construct, { name } = {}) {
135
356
  const stackName = name || constructStackName();
136
357
  const version = process.env.npm_package_version || process.env.PROJECT_VERSION || null;
137
358
  if (process.env.PROJECT_COMMIT && process.env.PROJECT_COMMIT.length > 8) {
138
- cdk$1.Tags.of(construct).add(CDK$1.TAG.BUILD_HEX, process.env.PROJECT_COMMIT.slice(0, 8));
359
+ cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_HEX, process.env.PROJECT_COMMIT.slice(0, 8));
139
360
  }
140
- cdk$1.Tags.of(construct).add(CDK$1.TAG.BUILD_DATE, new Date().toISOString());
141
- cdk$1.Tags.of(construct).add(CDK$1.TAG.BUILD_TIME, Date.now().toString());
361
+ cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_DATE, new Date().toISOString());
362
+ cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_TIME, Date.now().toString());
142
363
  if (process.env.PROJECT_COMMIT)
143
- cdk$1.Tags.of(construct).add(CDK$1.TAG.COMMIT, process.env.PROJECT_COMMIT);
144
- cdk$1.Tags.of(construct).add(CDK$1.TAG.CREATION, CDK$1.CREATION.CDK);
364
+ cdk.Tags.of(construct).add(CDK$1.TAG.COMMIT, process.env.PROJECT_COMMIT);
365
+ cdk.Tags.of(construct).add(CDK$1.TAG.CREATION, CDK$1.CREATION.CDK);
145
366
  if (process.env.PROJECT_ENV)
146
- cdk$1.Tags.of(construct).add(CDK$1.TAG.ENV, process.env.PROJECT_ENV);
367
+ cdk.Tags.of(construct).add(CDK$1.TAG.ENV, process.env.PROJECT_ENV);
147
368
  if (process.env.PROJECT_NONCE)
148
- cdk$1.Tags.of(construct).add(CDK$1.TAG.NONCE, process.env.PROJECT_NONCE);
369
+ cdk.Tags.of(construct).add(CDK$1.TAG.NONCE, process.env.PROJECT_NONCE);
149
370
  if (process.env.PROJECT_KEY)
150
- cdk$1.Tags.of(construct).add(CDK$1.TAG.PROJECT, process.env.PROJECT_KEY);
151
- cdk$1.Tags.of(construct).add(CDK$1.TAG.ROLE, CDK$1.ROLE.STACK);
371
+ cdk.Tags.of(construct).add(CDK$1.TAG.PROJECT, process.env.PROJECT_KEY);
372
+ cdk.Tags.of(construct).add(CDK$1.TAG.ROLE, CDK$1.ROLE.STACK);
152
373
  if (process.env.PROJECT_SERVICE)
153
- cdk$1.Tags.of(construct).add(CDK$1.TAG.SERVICE, process.env.PROJECT_SERVICE);
374
+ cdk.Tags.of(construct).add(CDK$1.TAG.SERVICE, process.env.PROJECT_SERVICE);
154
375
  if (process.env.PROJECT_SPONSOR)
155
- cdk$1.Tags.of(construct).add(CDK$1.TAG.SPONSOR, process.env.PROJECT_SPONSOR);
376
+ cdk.Tags.of(construct).add(CDK$1.TAG.SPONSOR, process.env.PROJECT_SPONSOR);
156
377
  if (stackName)
157
- cdk$1.Tags.of(construct).add(CDK$1.TAG.STACK, stackName);
378
+ cdk.Tags.of(construct).add(CDK$1.TAG.STACK, stackName);
158
379
  if (version)
159
- cdk$1.Tags.of(construct).add(CDK$1.TAG.VERSION, version);
380
+ cdk.Tags.of(construct).add(CDK$1.TAG.VERSION, version);
160
381
  return true;
161
382
  }
162
383
 
163
384
  function envHostname({ component, domain, env, subdomain, } = {}) {
164
385
  const resolvedDomain = domain || process.env.CDK_ENV_DOMAIN || process.env.CDK_ENV_HOSTED_ZONE;
165
386
  if (!resolvedDomain) {
166
- throw new cdk.ConfigurationError("No hostname `domain` provided. Set CDK_ENV_DOMAIN or CDK_ENV_HOSTED_ZONE to use environment domain");
387
+ throw new errors.ConfigurationError("No hostname `domain` provided. Set CDK_ENV_DOMAIN or CDK_ENV_HOSTED_ZONE to use environment domain");
167
388
  }
168
389
  const resolvedComponent = component === "@" || component === "" ? undefined : component;
169
390
  const resolvedSubdomain = subdomain || process.env.CDK_ENV_SUBDOMAIN;
@@ -177,6 +398,55 @@ function envHostname({ component, domain, env, subdomain, } = {}) {
177
398
  return parts.join(".");
178
399
  }
179
400
 
401
+ /**
402
+ * Extends the Datadog IAM role with additional permissions
403
+ *
404
+ * Checks for CDK_ENV_DATADOG_ROLE_ARN environment variable.
405
+ * If found, creates a custom policy with:
406
+ * - budgets:ViewBudget
407
+ * - logs:DescribeLogGroups
408
+ *
409
+ * @param scope - The construct scope
410
+ * @param options - Configuration options
411
+ * @returns The created Policy, or undefined if CDK_ENV_DATADOG_ROLE_ARN is not set
412
+ */
413
+ function extendDatadogRole(scope, options) {
414
+ const datadogRoleArn = process.env.CDK_ENV_DATADOG_ROLE_ARN;
415
+ // Early return if no Datadog role ARN is configured
416
+ if (!datadogRoleArn) {
417
+ return undefined;
418
+ }
419
+ const { id = "DatadogCustomPolicy", project, service = CDK$2.SERVICE.DATADOG, } = options || {};
420
+ // Lookup the Datadog role
421
+ const datadogRole = awsIam.Role.fromRoleArn(scope, "DatadogRole", datadogRoleArn);
422
+ // Build policy statements
423
+ const statements = [
424
+ // Allow view budget
425
+ new awsIam.PolicyStatement({
426
+ actions: ["budgets:ViewBudget"],
427
+ resources: ["*"],
428
+ }),
429
+ // Allow describe log groups
430
+ new awsIam.PolicyStatement({
431
+ actions: ["logs:DescribeLogGroups"],
432
+ resources: ["*"],
433
+ }),
434
+ ];
435
+ // Create the custom policy
436
+ const datadogCustomPolicy = new awsIam.Policy(scope, id, {
437
+ roles: [datadogRole],
438
+ statements,
439
+ });
440
+ // Add tags
441
+ cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.SERVICE, service);
442
+ cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
443
+ cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
444
+ if (project) {
445
+ cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.PROJECT, project);
446
+ }
447
+ return datadogCustomPolicy;
448
+ }
449
+
180
450
  /**
181
451
  * Check if the current environment matches the given environment
182
452
  */
@@ -187,13 +457,86 @@ function isEnv(env) {
187
457
  * Check if the current environment is production
188
458
  */
189
459
  function isProductionEnv() {
190
- return isEnv(cdk.CDK.ENV.PRODUCTION);
460
+ return isEnv(CDK$2.ENV.PRODUCTION);
191
461
  }
192
462
  /**
193
463
  * Check if the current environment is sandbox
194
464
  */
195
465
  function isSandboxEnv() {
196
- return isEnv(cdk.CDK.ENV.SANDBOX);
466
+ return isEnv(CDK$2.ENV.SANDBOX);
467
+ }
468
+
469
+ // In short the RFC 1035 standard for valid hostnames is:
470
+ // 1. Must be less than 253 characters
471
+ // 2. Must start with a letter
472
+ // 3. Must end with a letter or number
473
+ // 4. Can only contain letters, numbers, and hyphens
474
+ // 5. Last part of the domain must be at least 2 characters
475
+ function validPart$1(part) {
476
+ if (!part.match(/^[a-z]/))
477
+ return false;
478
+ if (!part.match(/[a-z0-9]$/))
479
+ return false;
480
+ return /^[a-zA-Z0-9-]+$/.test(part);
481
+ }
482
+ function isValidHostname$1(hostname) {
483
+ // Check hostname is a string
484
+ if (typeof hostname !== "string")
485
+ return false;
486
+ // Convert hostname to lowercase
487
+ const check = hostname.toString().toLowerCase();
488
+ // Check hostname is less than 253 characters
489
+ if (check.length > 253)
490
+ return false;
491
+ // Split on dots
492
+ const parts = check.split(".");
493
+ // Check each part is validPart
494
+ const validParts = parts.map(validPart$1);
495
+ // Confirm all parts are valid
496
+ if (!validParts.every((part) => part))
497
+ return false;
498
+ // Confirm last part is at least 2 characters
499
+ const lastPart = parts[parts.length - 1];
500
+ if (lastPart.length < 2)
501
+ return false;
502
+ // Confirm last part is all letters
503
+ if (!lastPart.match(/^[a-z]+$/))
504
+ return false;
505
+ // This is a valid hostname
506
+ return true;
507
+ }
508
+
509
+ function validPart(part) {
510
+ if (!part.match(/^[a-z]/))
511
+ return false;
512
+ if (!part.match(/[a-z0-9]$/))
513
+ return false;
514
+ return /^[a-zA-Z0-9-]+$/.test(part);
515
+ }
516
+ function isValidSubdomain(subdomain) {
517
+ // Check subdomain is a string
518
+ if (typeof subdomain !== "string")
519
+ return false;
520
+ // Special case for apex
521
+ if (subdomain === CDK$2.HOST.APEX)
522
+ return true;
523
+ // Convert subdomain to lowercase
524
+ const check = subdomain.toString().toLowerCase();
525
+ // Check subdomain is less than 250 characters
526
+ // We use 250 instead of 253 because we need to leave room for the dot top-level domain
527
+ if (check.length > 250)
528
+ return false;
529
+ // Split on dots
530
+ const parts = check.split(".");
531
+ // Check each part is validPart
532
+ const validParts = parts.map(validPart);
533
+ // Confirm all parts are valid
534
+ if (!validParts.every((part) => part))
535
+ return false;
536
+ // Do not care if last part is at least 2 characters
537
+ // Do not care if last part is all letters
538
+ // This is a valid subdomain
539
+ return true;
197
540
  }
198
541
 
199
542
  function jaypieLambdaEnv(options = {}) {
@@ -250,6 +593,21 @@ function jaypieLambdaEnv(options = {}) {
250
593
  return environment;
251
594
  }
252
595
 
596
+ function mergeDomain(subDomain, hostedZone) {
597
+ if (!hostedZone) {
598
+ throw new errors.ConfigurationError("hostedZone is required");
599
+ }
600
+ if (!subDomain) {
601
+ // Return hostedZone if subDomain is not passed
602
+ // Pass CDK.HOST.APEX to explicitly indicate apex domain
603
+ return hostedZone;
604
+ }
605
+ if (subDomain === CDK$2.HOST.APEX) {
606
+ return hostedZone;
607
+ }
608
+ return `${subDomain}.${hostedZone}`;
609
+ }
610
+
253
611
  const DEFAULT_FUNCTION_NAME$1 = "DatadogForwarderFunction";
254
612
  // Cache to store resolved functions
255
613
  // Using nested structure to support multiple functions per scope with automatic GC
@@ -257,7 +615,7 @@ const functionCache = new WeakMap();
257
615
  function resolveDatadogForwarderFunction(scope, options) {
258
616
  const { import: importValue, name } = options || {};
259
617
  const functionName = name || DEFAULT_FUNCTION_NAME$1;
260
- const importKey = importValue || cdk.CDK.IMPORT.DATADOG_LOG_FORWARDER;
618
+ const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
261
619
  // Create a cache key based on name and import
262
620
  const cacheKey = `${functionName}:${importKey}`;
263
621
  // Get or create scope cache
@@ -279,7 +637,7 @@ function resolveDatadogForwarderFunction(scope, options) {
279
637
 
280
638
  function resolveDatadogLayers(scope, options = {}) {
281
639
  const { datadogApiKeyArn, uniqueId } = options;
282
- let resolvedRegion = cdk$1.Stack.of(scope).region || "us-east-1";
640
+ let resolvedRegion = cdk.Stack.of(scope).region || "us-east-1";
283
641
  // Resolve the Datadog API key ARN from multiple sources
284
642
  const resolvedDatadogApiKeyArn = datadogApiKeyArn ||
285
643
  process.env.DATADOG_API_KEY_ARN ||
@@ -290,9 +648,9 @@ function resolveDatadogLayers(scope, options = {}) {
290
648
  }
291
649
  const layerIdSuffix = uniqueId || process.env.PROJECT_NONCE || Date.now().toString();
292
650
  // Create Datadog Node.js layer
293
- const datadogNodeLayer = lambda__namespace.LayerVersion.fromLayerVersionArn(scope, `DatadogNodeLayer-${layerIdSuffix}`, `arn:aws:lambda:${resolvedRegion}:464622532012:layer:Datadog-Node20-x:${cdk.CDK.DATADOG.LAYER.NODE}`);
651
+ const datadogNodeLayer = lambda__namespace.LayerVersion.fromLayerVersionArn(scope, `DatadogNodeLayer-${layerIdSuffix}`, `arn:aws:lambda:${resolvedRegion}:464622532012:layer:Datadog-Node20-x:${CDK$2.DATADOG.LAYER.NODE}`);
294
652
  // Create Datadog Extension layer
295
- const datadogExtensionLayer = lambda__namespace.LayerVersion.fromLayerVersionArn(scope, `DatadogExtensionLayer-${layerIdSuffix}`, `arn:aws:lambda:${resolvedRegion}:464622532012:layer:Datadog-Extension:${cdk.CDK.DATADOG.LAYER.EXTENSION}`);
653
+ const datadogExtensionLayer = lambda__namespace.LayerVersion.fromLayerVersionArn(scope, `DatadogExtensionLayer-${layerIdSuffix}`, `arn:aws:lambda:${resolvedRegion}:464622532012:layer:Datadog-Extension:${CDK$2.DATADOG.LAYER.EXTENSION}`);
296
654
  return [datadogNodeLayer, datadogExtensionLayer];
297
655
  }
298
656
 
@@ -304,7 +662,7 @@ function resolveDatadogLoggingDestination(scope, options) {
304
662
  const { import: importValue, name } = options || {};
305
663
  // Create a cache key based on name and import (same as forwarder function)
306
664
  const functionName = name || DEFAULT_FUNCTION_NAME;
307
- const importKey = importValue || cdk.CDK.IMPORT.DATADOG_LOG_FORWARDER;
665
+ const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
308
666
  const cacheKey = `${functionName}:${importKey}`;
309
667
  // Get or create scope cache
310
668
  let scopeCache = destinationCache.get(scope);
@@ -327,7 +685,7 @@ function resolveDatadogLoggingDestination(scope, options) {
327
685
 
328
686
  function resolveHostedZone(scope, { name = "HostedZone", zone = process.env.CDK_ENV_HOSTED_ZONE, } = {}) {
329
687
  if (!zone) {
330
- throw new cdk.ConfigurationError("No `zone` provided. Set CDK_ENV_HOSTED_ZONE to use environment zone");
688
+ throw new errors.ConfigurationError("No `zone` provided. Set CDK_ENV_HOSTED_ZONE to use environment zone");
331
689
  }
332
690
  if (typeof zone === "string") {
333
691
  return route53__namespace.HostedZone.fromLookup(scope, name, {
@@ -360,7 +718,7 @@ const resolveParamsAndSecrets = ({ paramsAndSecrets, options, }) => {
360
718
  class JaypieApiGateway extends constructs.Construct {
361
719
  constructor(scope, id, props) {
362
720
  super(scope, id);
363
- const { certificate = true, handler, host: propsHost, name, roleTag = cdk.CDK.ROLE.API, zone: propsZone, } = props;
721
+ const { certificate = true, handler, host: propsHost, name, roleTag = CDK$2.ROLE.API, zone: propsZone, } = props;
364
722
  // Determine zone from props or environment
365
723
  let zone = propsZone;
366
724
  if (!zone && process.env.CDK_ENV_API_HOSTED_ZONE) {
@@ -374,7 +732,7 @@ class JaypieApiGateway extends constructs.Construct {
374
732
  }
375
733
  else if (process.env.CDK_ENV_API_SUBDOMAIN &&
376
734
  process.env.CDK_ENV_API_HOSTED_ZONE) {
377
- host = cdk.mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE);
735
+ host = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE);
378
736
  }
379
737
  }
380
738
  const apiGatewayName = name || constructEnvName("ApiGateway");
@@ -389,7 +747,7 @@ class JaypieApiGateway extends constructs.Construct {
389
747
  domainName: host,
390
748
  validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
391
749
  });
392
- cdk$1.Tags.of(certificateToUse).add(cdk.CDK.TAG.ROLE, cdk.CDK.ROLE.HOSTING);
750
+ cdk.Tags.of(certificateToUse).add(CDK$2.TAG.ROLE, CDK$2.ROLE.HOSTING);
393
751
  }
394
752
  else if (typeof certificate === "object") {
395
753
  certificateToUse = certificate;
@@ -408,19 +766,19 @@ class JaypieApiGateway extends constructs.Construct {
408
766
  handler,
409
767
  ...lambdaRestApiProps,
410
768
  });
411
- cdk$1.Tags.of(this._api).add(cdk.CDK.TAG.ROLE, roleTag);
769
+ cdk.Tags.of(this._api).add(CDK$2.TAG.ROLE, roleTag);
412
770
  if (host && certificateToUse && hostedZone) {
413
771
  this._domainName = this._api.addDomainName(apiDomainName, {
414
772
  domainName: host,
415
773
  certificate: certificateToUse,
416
774
  });
417
- cdk$1.Tags.of(this._domainName).add(cdk.CDK.TAG.ROLE, roleTag);
775
+ cdk.Tags.of(this._domainName).add(CDK$2.TAG.ROLE, roleTag);
418
776
  const record = new route53__namespace.ARecord(this, "AliasRecord", {
419
777
  recordName: host,
420
778
  target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.ApiGatewayDomain(this._domainName)),
421
779
  zone: hostedZone,
422
780
  });
423
- cdk$1.Tags.of(record).add(cdk.CDK.TAG.ROLE, cdk.CDK.ROLE.NETWORKING);
781
+ cdk.Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
424
782
  }
425
783
  }
426
784
  get api() {
@@ -461,8 +819,8 @@ class JaypieApiGateway extends constructs.Construct {
461
819
  }
462
820
  get env() {
463
821
  return {
464
- account: cdk$1.Stack.of(this).account,
465
- region: cdk$1.Stack.of(this).region,
822
+ account: cdk.Stack.of(this).account,
823
+ region: cdk.Stack.of(this).region,
466
824
  };
467
825
  }
468
826
  get stack() {
@@ -505,7 +863,7 @@ class JaypieApiGateway extends constructs.Construct {
505
863
  }
506
864
  }
507
865
 
508
- class JaypieStack extends cdk$1.Stack {
866
+ class JaypieStack extends cdk.Stack {
509
867
  constructor(scope, id, props = {}) {
510
868
  const { key, ...stackProps } = props;
511
869
  // Handle stackName
@@ -538,7 +896,7 @@ class JaypieAppStack extends JaypieStack {
538
896
  class JaypieLambda extends constructs.Construct {
539
897
  constructor(scope, id, props) {
540
898
  super(scope, id);
541
- const { allowAllOutbound, allowPublicSubnet, architecture = lambda__namespace.Architecture.X86_64, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment: initialEnvironment = {}, envSecrets = {}, ephemeralStorageSize, filesystem, handler = "index.handler", initialPolicy, layers = [], logRetention = cdk.CDK.LAMBDA.LOG_RETENTION, logRetentionRole, logRetentionRetryOptions, maxEventAge, memorySize = cdk.CDK.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag = cdk.CDK.ROLE.PROCESSING, runtime = lambda__namespace.Runtime.NODEJS_22_X, runtimeManagementMode, secrets = [], securityGroups, timeout = cdk$1.Duration.seconds(cdk.CDK.DURATION.LAMBDA_WORKER), tracing, vendorTag, vpc, vpcSubnets, } = props;
899
+ const { allowAllOutbound, allowPublicSubnet, architecture = lambda__namespace.Architecture.X86_64, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment: initialEnvironment = {}, envSecrets = {}, ephemeralStorageSize, filesystem, handler = "index.handler", initialPolicy, layers = [], logRetention = CDK$2.LAMBDA.LOG_RETENTION, logRetentionRole, logRetentionRetryOptions, maxEventAge, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag = CDK$2.ROLE.PROCESSING, runtime = lambda__namespace.Runtime.NODEJS_22_X, runtimeManagementMode, secrets = [], securityGroups, timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, vpc, vpcSubnets, } = props;
542
900
  // Get base environment with defaults
543
901
  const environment = jaypieLambdaEnv({ initialEnvironment });
544
902
  const codeAsset = typeof code === "string" ? lambda__namespace.Code.fromAsset(code) : code;
@@ -597,14 +955,14 @@ class JaypieLambda extends constructs.Construct {
597
955
  runtime,
598
956
  runtimeManagementMode,
599
957
  securityGroups,
600
- timeout: typeof timeout === "number" ? cdk$1.Duration.seconds(timeout) : timeout,
958
+ timeout: typeof timeout === "number" ? cdk.Duration.seconds(timeout) : timeout,
601
959
  tracing,
602
960
  vpc,
603
961
  vpcSubnets,
604
962
  // Enable auto-publishing of versions when using provisioned concurrency
605
963
  currentVersionOptions: provisionedConcurrentExecutions !== undefined
606
964
  ? {
607
- removalPolicy: cdk$1.RemovalPolicy.RETAIN,
965
+ removalPolicy: cdk.RemovalPolicy.RETAIN,
608
966
  description: "Auto-published version for provisioned concurrency",
609
967
  // Don't set provisioned concurrency here - it will be set on the alias
610
968
  }
@@ -633,10 +991,10 @@ class JaypieLambda extends constructs.Construct {
633
991
  this._provisioned.node.addDependency(version);
634
992
  }
635
993
  if (roleTag) {
636
- cdk$1.Tags.of(this._lambda).add(cdk.CDK.TAG.ROLE, roleTag);
994
+ cdk.Tags.of(this._lambda).add(CDK$2.TAG.ROLE, roleTag);
637
995
  }
638
996
  if (vendorTag) {
639
- cdk$1.Tags.of(this._lambda).add(cdk.CDK.TAG.VENDOR, vendorTag);
997
+ cdk.Tags.of(this._lambda).add(CDK$2.TAG.VENDOR, vendorTag);
640
998
  }
641
999
  // Assign _reference based on provisioned state
642
1000
  this._reference =
@@ -739,8 +1097,8 @@ class JaypieLambda extends constructs.Construct {
739
1097
  }
740
1098
  get env() {
741
1099
  return {
742
- account: cdk$1.Stack.of(this).account,
743
- region: cdk$1.Stack.of(this).region,
1100
+ account: cdk.Stack.of(this).account,
1101
+ region: cdk.Stack.of(this).region,
744
1102
  };
745
1103
  }
746
1104
  get stack() {
@@ -757,19 +1115,19 @@ class JaypieLambda extends constructs.Construct {
757
1115
  class JaypieQueuedLambda extends constructs.Construct {
758
1116
  constructor(scope, id, props) {
759
1117
  super(scope, id);
760
- const { allowAllOutbound, allowPublicSubnet, architecture, batchSize = 1, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment = {}, envSecrets = {}, ephemeralStorageSize, fifo = true, filesystem, handler = "index.handler", initialPolicy, layers = [], logRetention = cdk.CDK.LAMBDA.LOG_RETENTION, logRetentionRole, logRetentionRetryOptions, maxEventAge, memorySize = cdk.CDK.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag, runtime = lambda__namespace.Runtime.NODEJS_22_X, runtimeManagementMode, secrets = [], securityGroups, timeout = cdk$1.Duration.seconds(cdk.CDK.DURATION.LAMBDA_WORKER), tracing, vendorTag, visibilityTimeout = cdk$1.Duration.seconds(cdk.CDK.DURATION.LAMBDA_WORKER), vpc, vpcSubnets, } = props;
1118
+ const { allowAllOutbound, allowPublicSubnet, architecture, batchSize = 1, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment = {}, envSecrets = {}, ephemeralStorageSize, fifo = true, filesystem, handler = "index.handler", initialPolicy, layers = [], logRetention = CDK$2.LAMBDA.LOG_RETENTION, logRetentionRole, logRetentionRetryOptions, maxEventAge, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag, runtime = lambda__namespace.Runtime.NODEJS_22_X, runtimeManagementMode, secrets = [], securityGroups, timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, visibilityTimeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), vpc, vpcSubnets, } = props;
761
1119
  // Create SQS Queue
762
1120
  this._queue = new sqs__namespace.Queue(this, "Queue", {
763
1121
  fifo,
764
1122
  visibilityTimeout: typeof visibilityTimeout === "number"
765
- ? cdk$1.Duration.seconds(visibilityTimeout)
1123
+ ? cdk.Duration.seconds(visibilityTimeout)
766
1124
  : visibilityTimeout,
767
1125
  });
768
1126
  if (roleTag) {
769
- cdk$1.Tags.of(this._queue).add(cdk.CDK.TAG.ROLE, roleTag);
1127
+ cdk.Tags.of(this._queue).add(CDK$2.TAG.ROLE, roleTag);
770
1128
  }
771
1129
  if (vendorTag) {
772
- cdk$1.Tags.of(this._queue).add(cdk.CDK.TAG.VENDOR, vendorTag);
1130
+ cdk.Tags.of(this._queue).add(CDK$2.TAG.VENDOR, vendorTag);
773
1131
  }
774
1132
  // Create Lambda with JaypieLambda
775
1133
  this._lambdaConstruct = new JaypieLambda(this, "Function", {
@@ -914,12 +1272,12 @@ class JaypieQueuedLambda extends constructs.Construct {
914
1272
  }
915
1273
  get env() {
916
1274
  return {
917
- account: cdk$1.Stack.of(this).account,
918
- region: cdk$1.Stack.of(this).region,
1275
+ account: cdk.Stack.of(this).account,
1276
+ region: cdk.Stack.of(this).region,
919
1277
  };
920
1278
  }
921
1279
  get stack() {
922
- return cdk$1.Stack.of(this);
1280
+ return cdk.Stack.of(this);
923
1281
  }
924
1282
  applyRemovalPolicy(policy) {
925
1283
  this._lambdaConstruct.applyRemovalPolicy(policy);
@@ -997,15 +1355,15 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
997
1355
  // Create S3 Bucket
998
1356
  this._bucket = new s3__namespace.Bucket(this, "Bucket", {
999
1357
  bucketName: bucketOptions.bucketName || bucketName,
1000
- removalPolicy: bucketOptions.removalPolicy || cdk$1.RemovalPolicy.RETAIN,
1358
+ removalPolicy: bucketOptions.removalPolicy || cdk.RemovalPolicy.RETAIN,
1001
1359
  ...bucketOptions,
1002
1360
  });
1003
1361
  // Add tags to bucket
1004
1362
  if (roleTag) {
1005
- cdk$1.Tags.of(this._bucket).add(cdk.CDK.TAG.ROLE, roleTag);
1363
+ cdk.Tags.of(this._bucket).add(CDK$2.TAG.ROLE, roleTag);
1006
1364
  }
1007
1365
  if (vendorTag) {
1008
- cdk$1.Tags.of(this._bucket).add(cdk.CDK.TAG.VENDOR, vendorTag);
1366
+ cdk.Tags.of(this._bucket).add(CDK$2.TAG.VENDOR, vendorTag);
1009
1367
  }
1010
1368
  // Add an event notification from the bucket to the queue
1011
1369
  this._bucket.addEventNotification(s3__namespace.EventType.OBJECT_CREATED, new s3n__namespace.SqsDestination(this.queue));
@@ -1128,15 +1486,184 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
1128
1486
  }
1129
1487
  }
1130
1488
 
1489
+ class JaypieDatadogBucket extends constructs.Construct {
1490
+ /**
1491
+ * Create a new S3 bucket for Datadog log archiving with automatic IAM permissions
1492
+ */
1493
+ constructor(scope, idOrProps, propsOrUndefined) {
1494
+ // Handle overloaded constructor signatures
1495
+ let props;
1496
+ let id;
1497
+ if (typeof idOrProps === "string") {
1498
+ // First param is ID, second is props
1499
+ props = propsOrUndefined || {};
1500
+ id = idOrProps;
1501
+ }
1502
+ else {
1503
+ // First param is props
1504
+ props = idOrProps || {};
1505
+ id = props.id || "DatadogArchiveBucket";
1506
+ }
1507
+ super(scope, id);
1508
+ // Extract Jaypie-specific options
1509
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1510
+ const { grantDatadogAccess = true, id: _id, project, service = CDK$2.SERVICE.DATADOG, ...bucketProps } = props;
1511
+ // Create the bucket
1512
+ this.bucket = new s3.Bucket(this, "Bucket", bucketProps);
1513
+ // Add tags to bucket
1514
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
1515
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1516
+ if (project) {
1517
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
1518
+ }
1519
+ // Grant Datadog role access to bucket if enabled
1520
+ if (grantDatadogAccess) {
1521
+ this.policy = this.grantDatadogRoleBucketAccess({ project, service });
1522
+ }
1523
+ }
1524
+ /**
1525
+ * Grants the Datadog IAM role access to this bucket
1526
+ *
1527
+ * Checks for CDK_ENV_DATADOG_ROLE_ARN environment variable.
1528
+ * If found, creates a custom policy with:
1529
+ * - s3:ListBucket on bucket
1530
+ * - s3:GetObject and s3:PutObject on bucket/*
1531
+ *
1532
+ * @param options - Configuration options
1533
+ * @returns The created Policy, or undefined if CDK_ENV_DATADOG_ROLE_ARN is not set
1534
+ */
1535
+ grantDatadogRoleBucketAccess(options) {
1536
+ const datadogRoleArn = process.env.CDK_ENV_DATADOG_ROLE_ARN;
1537
+ // Early return if no Datadog role ARN is configured
1538
+ if (!datadogRoleArn) {
1539
+ return undefined;
1540
+ }
1541
+ const { project, service = CDK$2.SERVICE.DATADOG } = options || {};
1542
+ // Lookup the Datadog role
1543
+ const datadogRole = awsIam.Role.fromRoleArn(this, "DatadogRole", datadogRoleArn);
1544
+ // Build policy statements for bucket access
1545
+ const statements = [
1546
+ // Allow list bucket
1547
+ new awsIam.PolicyStatement({
1548
+ actions: ["s3:ListBucket"],
1549
+ resources: [this.bucket.bucketArn],
1550
+ }),
1551
+ // Allow read and write to the bucket
1552
+ new awsIam.PolicyStatement({
1553
+ actions: ["s3:GetObject", "s3:PutObject"],
1554
+ resources: [`${this.bucket.bucketArn}/*`],
1555
+ }),
1556
+ ];
1557
+ // Create the custom policy
1558
+ const datadogBucketPolicy = new awsIam.Policy(this, "DatadogBucketPolicy", {
1559
+ roles: [datadogRole],
1560
+ statements,
1561
+ });
1562
+ // Add tags
1563
+ cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.SERVICE, service);
1564
+ cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1565
+ cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
1566
+ if (project) {
1567
+ cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.PROJECT, project);
1568
+ }
1569
+ return datadogBucketPolicy;
1570
+ }
1571
+ }
1572
+
1573
+ const DATADOG_FORWARDER_TEMPLATE_URL = "https://datadog-cloudformation-template.s3.amazonaws.com/aws/forwarder/latest.yaml";
1574
+ const DEFAULT_RESERVED_CONCURRENCY = "10";
1575
+ class JaypieDatadogForwarder extends constructs.Construct {
1576
+ /**
1577
+ * Create a new Datadog forwarder with CloudFormation nested stack
1578
+ */
1579
+ constructor(scope, idOrProps, propsOrUndefined) {
1580
+ // Handle overloaded constructor signatures
1581
+ let props;
1582
+ let id;
1583
+ if (typeof idOrProps === "string") {
1584
+ // First param is ID, second is props
1585
+ props = propsOrUndefined || {};
1586
+ id = idOrProps;
1587
+ }
1588
+ else {
1589
+ // First param is props
1590
+ props = idOrProps || {};
1591
+ id = props.id || "DatadogForwarder";
1592
+ }
1593
+ super(scope, id);
1594
+ // Resolve options with defaults
1595
+ const { account = process.env.CDK_ENV_ACCOUNT, additionalTags, createOutput = true, datadogApiKey = process.env.CDK_ENV_DATADOG_API_KEY, enableCloudFormationEvents = true, enableRoleExtension = true, exportName = CDK$2.IMPORT.DATADOG_LOG_FORWARDER, project, reservedConcurrency = DEFAULT_RESERVED_CONCURRENCY, service = CDK$2.VENDOR.DATADOG, templateUrl = DATADOG_FORWARDER_TEMPLATE_URL, } = props;
1596
+ // Validate required parameters
1597
+ if (!datadogApiKey) {
1598
+ throw new Error("Datadog API key is required. Provide via datadogApiKey prop or CDK_ENV_DATADOG_API_KEY environment variable.");
1599
+ }
1600
+ // Build Datadog tags
1601
+ let ddTags = account ? `account:${account}` : "";
1602
+ if (additionalTags) {
1603
+ ddTags = ddTags ? `${ddTags},${additionalTags}` : additionalTags;
1604
+ }
1605
+ // Deploy Datadog CloudFormation stack
1606
+ this.cfnStack = new cdk.CfnStack(this, "Stack", {
1607
+ parameters: {
1608
+ DdApiKey: datadogApiKey,
1609
+ DdTags: ddTags,
1610
+ ReservedConcurrency: reservedConcurrency,
1611
+ },
1612
+ templateUrl,
1613
+ });
1614
+ // Add tags to stack
1615
+ cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1616
+ cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.SERVICE, service);
1617
+ cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
1618
+ if (project) {
1619
+ cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.PROJECT, project);
1620
+ }
1621
+ // Extract forwarder function from stack outputs
1622
+ this.forwarderFunction = lambda__namespace.Function.fromFunctionArn(this, "Function", this.cfnStack.getAtt("Outputs.DatadogForwarderArn").toString());
1623
+ // Extend Datadog role with custom permissions if enabled
1624
+ if (enableRoleExtension) {
1625
+ extendDatadogRole(this, { project, service });
1626
+ }
1627
+ // Create CloudFormation events rule if enabled
1628
+ if (enableCloudFormationEvents) {
1629
+ this.eventsRule = new awsEvents.Rule(this, "CloudFormationEventsRule", {
1630
+ eventPattern: {
1631
+ source: ["aws.cloudformation"],
1632
+ },
1633
+ targets: [
1634
+ new awsEventsTargets.LambdaFunction(this.forwarderFunction, {
1635
+ event: awsEvents.RuleTargetInput.fromEventPath("$"),
1636
+ }),
1637
+ ],
1638
+ });
1639
+ // Add tags to events rule
1640
+ cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1641
+ cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.SERVICE, service);
1642
+ cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
1643
+ if (project) {
1644
+ cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.PROJECT, project);
1645
+ }
1646
+ }
1647
+ // Create CloudFormation output if enabled
1648
+ if (createOutput) {
1649
+ new cdk__namespace.CfnOutput(this, "ForwarderArnOutput", {
1650
+ description: "Datadog Log Forwarder Lambda ARN",
1651
+ exportName,
1652
+ value: this.cfnStack.getAtt("Outputs.DatadogForwarderArn").toString(),
1653
+ });
1654
+ }
1655
+ }
1656
+ }
1657
+
1131
1658
  // It is a consumer if the environment is ephemeral
1132
1659
  function checkEnvIsConsumer(env = process.env) {
1133
- return (env.PROJECT_ENV === cdk.CDK.ENV.PERSONAL ||
1660
+ return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
1134
1661
  !!env.CDK_ENV_PERSONAL ||
1135
1662
  /** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
1136
1663
  /** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
1137
1664
  }
1138
1665
  function checkEnvIsProvider(env = process.env) {
1139
- return env.PROJECT_ENV === cdk.CDK.ENV.SANDBOX;
1666
+ return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
1140
1667
  }
1141
1668
  function cleanName(name) {
1142
1669
  return name.replace(/[^a-zA-Z0-9:-]/g, "");
@@ -1150,7 +1677,7 @@ function exportEnvName(name, env = process.env) {
1150
1677
  }
1151
1678
  else {
1152
1679
  if (checkEnvIsConsumer(env)) {
1153
- rawName = `env-${cdk.CDK.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
1680
+ rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
1154
1681
  }
1155
1682
  else {
1156
1683
  rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
@@ -1171,10 +1698,10 @@ class JaypieEnvSecret extends constructs.Construct {
1171
1698
  exportName = cleanName(exportParam);
1172
1699
  }
1173
1700
  if (consumer) {
1174
- const secretName = cdk$1.Fn.importValue(exportName);
1701
+ const secretName = cdk.Fn.importValue(exportName);
1175
1702
  this._secret = secretsmanager__namespace.Secret.fromSecretNameV2(this, id, secretName);
1176
1703
  // Add CfnOutput for consumer secrets
1177
- new cdk$1.CfnOutput(this, `ConsumedName`, {
1704
+ new cdk.CfnOutput(this, `ConsumedName`, {
1178
1705
  value: this._secret.secretName,
1179
1706
  });
1180
1707
  }
@@ -1183,24 +1710,24 @@ class JaypieEnvSecret extends constructs.Construct {
1183
1710
  const secretProps = {
1184
1711
  generateSecretString,
1185
1712
  secretStringValue: !generateSecretString && secretValue
1186
- ? cdk$1.SecretValue.unsafePlainText(secretValue)
1713
+ ? cdk.SecretValue.unsafePlainText(secretValue)
1187
1714
  : undefined,
1188
1715
  };
1189
1716
  this._secret = new secretsmanager__namespace.Secret(this, id, secretProps);
1190
1717
  if (roleTag) {
1191
- cdk$1.Tags.of(this._secret).add(cdk.CDK.TAG.ROLE, roleTag);
1718
+ cdk.Tags.of(this._secret).add(CDK$2.TAG.ROLE, roleTag);
1192
1719
  }
1193
1720
  if (vendorTag) {
1194
- cdk$1.Tags.of(this._secret).add(cdk.CDK.TAG.VENDOR, vendorTag);
1721
+ cdk.Tags.of(this._secret).add(CDK$2.TAG.VENDOR, vendorTag);
1195
1722
  }
1196
1723
  if (provider) {
1197
- new cdk$1.CfnOutput(this, `ProvidedName`, {
1724
+ new cdk.CfnOutput(this, `ProvidedName`, {
1198
1725
  value: this._secret.secretName,
1199
1726
  exportName,
1200
1727
  });
1201
1728
  }
1202
1729
  else {
1203
- new cdk$1.CfnOutput(this, `CreatedName`, {
1730
+ new cdk.CfnOutput(this, `CreatedName`, {
1204
1731
  value: this._secret.secretName,
1205
1732
  });
1206
1733
  }
@@ -1208,12 +1735,12 @@ class JaypieEnvSecret extends constructs.Construct {
1208
1735
  }
1209
1736
  // IResource implementation
1210
1737
  get stack() {
1211
- return cdk$1.Stack.of(this);
1738
+ return cdk.Stack.of(this);
1212
1739
  }
1213
1740
  get env() {
1214
1741
  return {
1215
- account: cdk$1.Stack.of(this).account,
1216
- region: cdk$1.Stack.of(this).region,
1742
+ account: cdk.Stack.of(this).account,
1743
+ region: cdk.Stack.of(this).region,
1217
1744
  };
1218
1745
  }
1219
1746
  applyRemovalPolicy(policy) {
@@ -1265,8 +1792,8 @@ class JaypieDatadogSecret extends JaypieEnvSecret {
1265
1792
  constructor(scope, id = "MongoConnectionString", props) {
1266
1793
  const defaultProps = {
1267
1794
  envKey: "DATADOG_API_KEY",
1268
- roleTag: cdk.CDK.ROLE.MONITORING,
1269
- vendorTag: cdk.CDK.VENDOR.DATADOG,
1795
+ roleTag: CDK$2.ROLE.MONITORING,
1796
+ vendorTag: CDK$2.VENDOR.DATADOG,
1270
1797
  ...props,
1271
1798
  };
1272
1799
  super(scope, id, defaultProps);
@@ -1277,7 +1804,7 @@ class JaypieDnsRecord extends constructs.Construct {
1277
1804
  constructor(scope, id, props) {
1278
1805
  super(scope, id);
1279
1806
  const { comment, recordName, type, values } = props;
1280
- const ttl = props.ttl || cdk__namespace.Duration.seconds(cdk.CDK.DNS.CONFIG.TTL);
1807
+ const ttl = props.ttl || cdk__namespace.Duration.seconds(CDK$2.DNS.CONFIG.TTL);
1281
1808
  // Resolve the hosted zone (supports both string and IHostedZone)
1282
1809
  const zone = resolveHostedZone(scope, {
1283
1810
  name: `${id}HostedZone`,
@@ -1292,9 +1819,9 @@ class JaypieDnsRecord extends constructs.Construct {
1292
1819
  };
1293
1820
  // Create the appropriate record based on type
1294
1821
  switch (type) {
1295
- case cdk.CDK.DNS.RECORD.A: {
1822
+ case CDK$2.DNS.RECORD.A: {
1296
1823
  if (!Array.isArray(values) || values.length === 0) {
1297
- throw new cdk.ConfigurationError("A record requires at least one IP address");
1824
+ throw new errors.ConfigurationError("A record requires at least one IP address");
1298
1825
  }
1299
1826
  this.record = new route53.ARecord(this, "Record", {
1300
1827
  ...baseProps,
@@ -1302,9 +1829,9 @@ class JaypieDnsRecord extends constructs.Construct {
1302
1829
  });
1303
1830
  break;
1304
1831
  }
1305
- case cdk.CDK.DNS.RECORD.CNAME: {
1832
+ case CDK$2.DNS.RECORD.CNAME: {
1306
1833
  if (!Array.isArray(values) || values.length === 0) {
1307
- throw new cdk.ConfigurationError("CNAME record requires a domain name");
1834
+ throw new errors.ConfigurationError("CNAME record requires a domain name");
1308
1835
  }
1309
1836
  this.record = new route53.CnameRecord(this, "Record", {
1310
1837
  ...baseProps,
@@ -1312,9 +1839,9 @@ class JaypieDnsRecord extends constructs.Construct {
1312
1839
  });
1313
1840
  break;
1314
1841
  }
1315
- case cdk.CDK.DNS.RECORD.MX: {
1842
+ case CDK$2.DNS.RECORD.MX: {
1316
1843
  if (!Array.isArray(values) || values.length === 0) {
1317
- throw new cdk.ConfigurationError("MX record requires at least one mail server");
1844
+ throw new errors.ConfigurationError("MX record requires at least one mail server");
1318
1845
  }
1319
1846
  this.record = new route53.MxRecord(this, "Record", {
1320
1847
  ...baseProps,
@@ -1322,9 +1849,9 @@ class JaypieDnsRecord extends constructs.Construct {
1322
1849
  });
1323
1850
  break;
1324
1851
  }
1325
- case cdk.CDK.DNS.RECORD.NS: {
1852
+ case CDK$2.DNS.RECORD.NS: {
1326
1853
  if (!Array.isArray(values) || values.length === 0) {
1327
- throw new cdk.ConfigurationError("NS record requires at least one name server");
1854
+ throw new errors.ConfigurationError("NS record requires at least one name server");
1328
1855
  }
1329
1856
  this.record = new route53.NsRecord(this, "Record", {
1330
1857
  ...baseProps,
@@ -1332,9 +1859,9 @@ class JaypieDnsRecord extends constructs.Construct {
1332
1859
  });
1333
1860
  break;
1334
1861
  }
1335
- case cdk.CDK.DNS.RECORD.TXT: {
1862
+ case CDK$2.DNS.RECORD.TXT: {
1336
1863
  if (!Array.isArray(values) || values.length === 0) {
1337
- throw new cdk.ConfigurationError("TXT record requires at least one value");
1864
+ throw new errors.ConfigurationError("TXT record requires at least one value");
1338
1865
  }
1339
1866
  this.record = new route53.TxtRecord(this, "Record", {
1340
1867
  ...baseProps,
@@ -1343,26 +1870,106 @@ class JaypieDnsRecord extends constructs.Construct {
1343
1870
  break;
1344
1871
  }
1345
1872
  default:
1346
- throw new cdk.ConfigurationError(`Unsupported DNS record type: ${type}. Supported types: A, CNAME, MX, NS, TXT`);
1873
+ throw new errors.ConfigurationError(`Unsupported DNS record type: ${type}. Supported types: A, CNAME, MX, NS, TXT`);
1347
1874
  }
1348
1875
  // Add standard tags to the DNS record
1349
- cdk__namespace.Tags.of(this.record).add(cdk.CDK.TAG.SERVICE, cdk.CDK.SERVICE.INFRASTRUCTURE);
1350
- cdk__namespace.Tags.of(this.record).add(cdk.CDK.TAG.ROLE, cdk.CDK.ROLE.NETWORKING);
1876
+ cdk__namespace.Tags.of(this.record).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.INFRASTRUCTURE);
1877
+ cdk__namespace.Tags.of(this.record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
1878
+ }
1879
+ }
1880
+
1881
+ class JaypieEventsRule extends constructs.Construct {
1882
+ /**
1883
+ * Create a new EventBridge rule that targets a Lambda function
1884
+ */
1885
+ constructor(scope, idOrSourceOrProps, propsOrUndefined) {
1886
+ // Handle overloaded constructor signatures
1887
+ let props;
1888
+ let id;
1889
+ if (typeof idOrSourceOrProps === "string") {
1890
+ // Check if it looks like an AWS source (starts with "aws.")
1891
+ if (idOrSourceOrProps.startsWith("aws.")) {
1892
+ // First param is source, second is props
1893
+ props = propsOrUndefined || {};
1894
+ props.source = idOrSourceOrProps;
1895
+ // Generate ID from source
1896
+ const sourceName = idOrSourceOrProps
1897
+ .replace("aws.", "")
1898
+ .split(".")
1899
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
1900
+ .join("");
1901
+ id = props.id || `${sourceName}EventsRule`;
1902
+ }
1903
+ else {
1904
+ // First param is ID, second is props
1905
+ props = propsOrUndefined || {};
1906
+ id = idOrSourceOrProps;
1907
+ }
1908
+ }
1909
+ else {
1910
+ // First param is props
1911
+ props = idOrSourceOrProps || {};
1912
+ if (props.source) {
1913
+ const sourceName = typeof props.source === "string"
1914
+ ? props.source
1915
+ .replace("aws.", "")
1916
+ .split(".")
1917
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
1918
+ .join("")
1919
+ : "Events";
1920
+ id = props.id || `${sourceName}EventsRule`;
1921
+ }
1922
+ else {
1923
+ id = props.id || "EventsRule";
1924
+ }
1925
+ }
1926
+ super(scope, id);
1927
+ // Extract Jaypie-specific options
1928
+ const { id: _id, project, service = CDK$2.SERVICE.DATADOG, source, targetFunction, vendor = CDK$2.VENDOR.DATADOG, ...ruleProps } = props;
1929
+ // Resolve target function
1930
+ this.targetFunction =
1931
+ targetFunction || resolveDatadogForwarderFunction(scope);
1932
+ // Build event pattern if source is specified
1933
+ const eventPattern = source
1934
+ ? {
1935
+ ...ruleProps.eventPattern,
1936
+ source: Array.isArray(source) ? source : [source],
1937
+ }
1938
+ : ruleProps.eventPattern;
1939
+ // Build rule props
1940
+ const finalRuleProps = {
1941
+ ...ruleProps,
1942
+ eventPattern,
1943
+ targets: [
1944
+ new awsEventsTargets.LambdaFunction(this.targetFunction, {
1945
+ event: awsEvents.RuleTargetInput.fromEventPath("$"),
1946
+ }),
1947
+ ],
1948
+ };
1949
+ // Create the rule
1950
+ this.rule = new awsEvents.Rule(this, "Rule", finalRuleProps);
1951
+ // Add tags
1952
+ cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1953
+ cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.SERVICE, service);
1954
+ cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.VENDOR, vendor);
1955
+ if (project) {
1956
+ cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.PROJECT, project);
1957
+ }
1351
1958
  }
1352
1959
  }
1353
1960
 
1354
1961
  class JaypieGitHubDeployRole extends constructs.Construct {
1355
1962
  constructor(scope, id = "GitHubDeployRole", props = {}) {
1356
1963
  super(scope, id);
1357
- const { oidcProviderArn = cdk$1.Fn.importValue(cdk.CDK.IMPORT.OIDC_PROVIDER), output = true, repoRestriction: propsRepoRestriction, } = props;
1964
+ const { oidcProviderArn = cdk.Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), output = true, repoRestriction: propsRepoRestriction, } = props;
1358
1965
  // Extract account ID from the scope
1359
- const accountId = cdk$1.Stack.of(this).account;
1966
+ const accountId = cdk.Stack.of(this).account;
1360
1967
  // Resolve repoRestriction from props or environment variables
1361
1968
  let repoRestriction = propsRepoRestriction;
1362
1969
  if (!repoRestriction) {
1363
1970
  const envRepo = process.env.CDK_ENV_REPO || process.env.PROJECT_REPO;
1364
1971
  if (!envRepo) {
1365
- throw new cdk.ConfigurationError("No repoRestriction provided. Set repoRestriction prop, CDK_ENV_REPO, or PROJECT_REPO environment variable");
1972
+ throw new errors.ConfigurationError("No repoRestriction provided. Set repoRestriction prop, CDK_ENV_REPO, or PROJECT_REPO environment variable");
1366
1973
  }
1367
1974
  // Extract organization from owner/repo format and create org-wide restriction
1368
1975
  const organization = envRepo.split("/")[0];
@@ -1375,10 +1982,10 @@ class JaypieGitHubDeployRole extends constructs.Construct {
1375
1982
  "token.actions.githubusercontent.com:sub": repoRestriction,
1376
1983
  },
1377
1984
  }, "sts:AssumeRoleWithWebIdentity"),
1378
- maxSessionDuration: cdk$1.Duration.hours(1),
1985
+ maxSessionDuration: cdk.Duration.hours(1),
1379
1986
  path: "/",
1380
1987
  });
1381
- cdk$1.Tags.of(this._role).add(cdk.CDK.TAG.ROLE, cdk.CDK.ROLE.DEPLOY);
1988
+ cdk.Tags.of(this._role).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
1382
1989
  // Allow the role to access the GitHub OIDC provider
1383
1990
  this._role.addToPolicy(new awsIam.PolicyStatement({
1384
1991
  actions: ["sts:AssumeRoleWithWebIdentity"],
@@ -1417,7 +2024,7 @@ class JaypieGitHubDeployRole extends constructs.Construct {
1417
2024
  // Export the ARN of the role
1418
2025
  if (output !== false) {
1419
2026
  const outputId = typeof output === "string" ? output : "GitHubActionsRoleArn";
1420
- new cdk$1.CfnOutput(this, outputId, {
2027
+ new cdk.CfnOutput(this, outputId, {
1421
2028
  value: this._role.roleArn,
1422
2029
  });
1423
2030
  }
@@ -1436,8 +2043,8 @@ class JaypieGitHubDeployRole extends constructs.Construct {
1436
2043
  class JaypieExpressLambda extends JaypieLambda {
1437
2044
  constructor(scope, id, props) {
1438
2045
  super(scope, id, {
1439
- timeout: cdk$1.Duration.seconds(cdk.CDK.DURATION.EXPRESS_API),
1440
- roleTag: cdk.CDK.ROLE.API,
2046
+ timeout: cdk.Duration.seconds(CDK$2.DURATION.EXPRESS_API),
2047
+ roleTag: CDK$2.ROLE.API,
1441
2048
  ...props,
1442
2049
  });
1443
2050
  }
@@ -1497,7 +2104,7 @@ class JaypieHostedZone extends constructs.Construct {
1497
2104
  super(scope, id);
1498
2105
  const { zoneName, project } = props;
1499
2106
  const destination = props.destination ?? true;
1500
- const service = props.service || cdk.CDK.SERVICE.INFRASTRUCTURE;
2107
+ const service = props.service || CDK$2.SERVICE.INFRASTRUCTURE;
1501
2108
  // Create the log group
1502
2109
  this.logGroup = new awsLogs.LogGroup(this, "LogGroup", {
1503
2110
  logGroupName: process.env.PROJECT_NONCE
@@ -1506,10 +2113,10 @@ class JaypieHostedZone extends constructs.Construct {
1506
2113
  retention: awsLogs.RetentionDays.ONE_WEEK,
1507
2114
  });
1508
2115
  // Add tags
1509
- cdk__namespace.Tags.of(this.logGroup).add(cdk.CDK.TAG.SERVICE, service);
1510
- cdk__namespace.Tags.of(this.logGroup).add(cdk.CDK.TAG.ROLE, cdk.CDK.ROLE.NETWORKING);
2116
+ cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.SERVICE, service);
2117
+ cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
1511
2118
  if (project) {
1512
- cdk__namespace.Tags.of(this.logGroup).add(cdk.CDK.TAG.PROJECT, project);
2119
+ cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.PROJECT, project);
1513
2120
  }
1514
2121
  // Grant Route 53 permissions to write to the log group
1515
2122
  this.logGroup.grantWrite(new awsIam.ServicePrincipal(SERVICE.ROUTE53));
@@ -1529,10 +2136,10 @@ class JaypieHostedZone extends constructs.Construct {
1529
2136
  zoneName,
1530
2137
  });
1531
2138
  // Add tags
1532
- cdk__namespace.Tags.of(this.hostedZone).add(cdk.CDK.TAG.SERVICE, service);
1533
- cdk__namespace.Tags.of(this.hostedZone).add(cdk.CDK.TAG.ROLE, cdk.CDK.ROLE.NETWORKING);
2139
+ cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.SERVICE, service);
2140
+ cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
1534
2141
  if (project) {
1535
- cdk__namespace.Tags.of(this.hostedZone).add(cdk.CDK.TAG.PROJECT, project);
2142
+ cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.PROJECT, project);
1536
2143
  }
1537
2144
  // Create DNS records if provided
1538
2145
  this.dnsRecords = [];
@@ -1567,7 +2174,7 @@ class JaypieInfrastructureStack extends JaypieStack {
1567
2174
  super(scope, id, { key, ...stackProps });
1568
2175
  // Add infrastructure-specific tag
1569
2176
  if (process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA) {
1570
- cdk$1.Tags.of(this).add(CDK.TAG.STACK_SHA, process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA);
2177
+ cdk.Tags.of(this).add(CDK.TAG.STACK_SHA, process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA);
1571
2178
  }
1572
2179
  }
1573
2180
  }
@@ -1576,8 +2183,8 @@ class JaypieMongoDbSecret extends JaypieEnvSecret {
1576
2183
  constructor(scope, id = "MongoConnectionString", props) {
1577
2184
  const defaultProps = {
1578
2185
  envKey: "MONGODB_URI",
1579
- roleTag: cdk.CDK.ROLE.STORAGE,
1580
- vendorTag: cdk.CDK.VENDOR.MONGODB,
2186
+ roleTag: CDK$2.ROLE.STORAGE,
2187
+ vendorTag: CDK$2.VENDOR.MONGODB,
1581
2188
  ...props,
1582
2189
  };
1583
2190
  super(scope, id, defaultProps);
@@ -1588,14 +2195,108 @@ class JaypieOpenAiSecret extends JaypieEnvSecret {
1588
2195
  constructor(scope, id = "OpenAiApiKey", props) {
1589
2196
  const defaultProps = {
1590
2197
  envKey: "OPENAI_API_KEY",
1591
- roleTag: cdk.CDK.ROLE.PROCESSING,
1592
- vendorTag: cdk.CDK.VENDOR.OPENAI,
2198
+ roleTag: CDK$2.ROLE.PROCESSING,
2199
+ vendorTag: CDK$2.VENDOR.OPENAI,
1593
2200
  ...props,
1594
2201
  };
1595
2202
  super(scope, id, defaultProps);
1596
2203
  }
1597
2204
  }
1598
2205
 
2206
+ class JaypieOrganizationTrail extends constructs.Construct {
2207
+ /**
2208
+ * Create a new organization CloudTrail with S3 bucket and lifecycle policies
2209
+ */
2210
+ constructor(scope, idOrProps, propsOrUndefined) {
2211
+ // Handle overloaded constructor signatures
2212
+ let props;
2213
+ let id;
2214
+ if (typeof idOrProps === "string") {
2215
+ // First param is ID, second is props
2216
+ props = propsOrUndefined || {};
2217
+ id = idOrProps;
2218
+ }
2219
+ else {
2220
+ // First param is props
2221
+ props = idOrProps || {};
2222
+ const defaultName = process.env.PROJECT_NONCE
2223
+ ? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
2224
+ : "organization-cloudtrail";
2225
+ id = props.id || `${props.trailName || defaultName}-Trail`;
2226
+ }
2227
+ super(scope, id);
2228
+ // Resolve options with defaults
2229
+ const { bucketName = process.env.PROJECT_NONCE
2230
+ ? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
2231
+ : "organization-cloudtrail", enableDatadogNotifications = true, enableFileValidation = false, expirationDays = 365, glacierTransitionDays = 180, infrequentAccessTransitionDays = 30, project, service = CDK$2.SERVICE.INFRASTRUCTURE, trailName = process.env.PROJECT_NONCE
2232
+ ? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
2233
+ : "organization-cloudtrail", } = props;
2234
+ // Create the S3 bucket for CloudTrail logs
2235
+ this.bucket = new s3.Bucket(this, "Bucket", {
2236
+ accessControl: s3.BucketAccessControl.LOG_DELIVERY_WRITE,
2237
+ bucketName,
2238
+ lifecycleRules: [
2239
+ {
2240
+ expiration: cdk__namespace.Duration.days(expirationDays),
2241
+ transitions: [
2242
+ {
2243
+ storageClass: s3.StorageClass.INFREQUENT_ACCESS,
2244
+ transitionAfter: cdk__namespace.Duration.days(infrequentAccessTransitionDays),
2245
+ },
2246
+ {
2247
+ storageClass: s3.StorageClass.GLACIER,
2248
+ transitionAfter: cdk__namespace.Duration.days(glacierTransitionDays),
2249
+ },
2250
+ ],
2251
+ },
2252
+ ],
2253
+ });
2254
+ // Add CloudTrail bucket policies
2255
+ this.bucket.addToResourcePolicy(new awsIam.PolicyStatement({
2256
+ actions: ["s3:GetBucketAcl"],
2257
+ effect: awsIam.Effect.ALLOW,
2258
+ principals: [new awsIam.ServicePrincipal("cloudtrail.amazonaws.com")],
2259
+ resources: [this.bucket.bucketArn],
2260
+ }));
2261
+ this.bucket.addToResourcePolicy(new awsIam.PolicyStatement({
2262
+ actions: ["s3:PutObject"],
2263
+ conditions: {
2264
+ StringEquals: {
2265
+ "s3:x-amz-acl": "bucket-owner-full-control",
2266
+ },
2267
+ },
2268
+ effect: awsIam.Effect.ALLOW,
2269
+ principals: [new awsIam.ServicePrincipal("cloudtrail.amazonaws.com")],
2270
+ resources: [`${this.bucket.bucketArn}/*`],
2271
+ }));
2272
+ // Add tags to bucket
2273
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
2274
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
2275
+ if (project) {
2276
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
2277
+ }
2278
+ // Add Datadog notifications if enabled
2279
+ if (enableDatadogNotifications) {
2280
+ const datadogForwarderFunction = resolveDatadogForwarderFunction(scope);
2281
+ this.bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.LambdaDestination(datadogForwarderFunction));
2282
+ }
2283
+ // Create the organization trail
2284
+ this.trail = new awsCloudtrail.Trail(this, "Trail", {
2285
+ bucket: this.bucket,
2286
+ enableFileValidation,
2287
+ isOrganizationTrail: true,
2288
+ managementEvents: awsCloudtrail.ReadWriteType.ALL,
2289
+ trailName,
2290
+ });
2291
+ // Add tags to trail
2292
+ cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.SERVICE, service);
2293
+ cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
2294
+ if (project) {
2295
+ cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.PROJECT, project);
2296
+ }
2297
+ }
2298
+ }
2299
+
1599
2300
  /**
1600
2301
  * JaypieSsoPermissions Construct
1601
2302
  *
@@ -1662,15 +2363,15 @@ class JaypieSsoPermissions extends constructs.Construct {
1662
2363
  .managedPolicyArn,
1663
2364
  awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
1664
2365
  ],
1665
- sessionDuration: cdk$1.Duration.hours(1).toIsoString(),
2366
+ sessionDuration: cdk.Duration.hours(1).toIsoString(),
1666
2367
  tags: [
1667
2368
  {
1668
- key: cdk.CDK.TAG.SERVICE,
1669
- value: cdk.CDK.SERVICE.SSO,
2369
+ key: CDK$2.TAG.SERVICE,
2370
+ value: CDK$2.SERVICE.SSO,
1670
2371
  },
1671
2372
  {
1672
- key: cdk.CDK.TAG.ROLE,
1673
- value: cdk.CDK.ROLE.SECURITY,
2373
+ key: CDK$2.TAG.ROLE,
2374
+ value: CDK$2.ROLE.SECURITY,
1674
2375
  },
1675
2376
  ],
1676
2377
  });
@@ -1744,15 +2445,15 @@ class JaypieSsoPermissions extends constructs.Construct {
1744
2445
  awsIam.ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
1745
2446
  .managedPolicyArn,
1746
2447
  ],
1747
- sessionDuration: cdk$1.Duration.hours(12).toIsoString(),
2448
+ sessionDuration: cdk.Duration.hours(12).toIsoString(),
1748
2449
  tags: [
1749
2450
  {
1750
- key: cdk.CDK.TAG.SERVICE,
1751
- value: cdk.CDK.SERVICE.SSO,
2451
+ key: CDK$2.TAG.SERVICE,
2452
+ value: CDK$2.SERVICE.SSO,
1752
2453
  },
1753
2454
  {
1754
- key: cdk.CDK.TAG.ROLE,
1755
- value: cdk.CDK.ROLE.SECURITY,
2455
+ key: CDK$2.TAG.ROLE,
2456
+ value: CDK$2.ROLE.SECURITY,
1756
2457
  },
1757
2458
  ],
1758
2459
  });
@@ -1802,15 +2503,15 @@ class JaypieSsoPermissions extends constructs.Construct {
1802
2503
  .managedPolicyArn,
1803
2504
  awsIam.ManagedPolicy.fromAwsManagedPolicyName("job-function/SystemAdministrator").managedPolicyArn,
1804
2505
  ],
1805
- sessionDuration: cdk$1.Duration.hours(4).toIsoString(),
2506
+ sessionDuration: cdk.Duration.hours(4).toIsoString(),
1806
2507
  tags: [
1807
2508
  {
1808
- key: cdk.CDK.TAG.SERVICE,
1809
- value: cdk.CDK.SERVICE.SSO,
2509
+ key: CDK$2.TAG.SERVICE,
2510
+ value: CDK$2.SERVICE.SSO,
1810
2511
  },
1811
2512
  {
1812
- key: cdk.CDK.TAG.ROLE,
1813
- value: cdk.CDK.ROLE.SECURITY,
2513
+ key: CDK$2.TAG.ROLE,
2514
+ value: CDK$2.ROLE.SECURITY,
1814
2515
  },
1815
2516
  ],
1816
2517
  });
@@ -1842,19 +2543,19 @@ class JaypieSsoPermissions extends constructs.Construct {
1842
2543
  permissionSetNames.forEach((permissionSetName) => {
1843
2544
  const permissionSet = permissionSetMap[permissionSetName];
1844
2545
  if (!permissionSet) {
1845
- throw new cdk.ConfigurationError(`Unknown permission set: ${permissionSetName}. Valid options: ${Object.keys(permissionSetMap).join(", ")}`);
2546
+ throw new errors.ConfigurationError(`Unknown permission set: ${permissionSetName}. Valid options: ${Object.keys(permissionSetMap).join(", ")}`);
1846
2547
  }
1847
2548
  const accountAssignment = new awsSso.CfnAssignment(this, `AccountAssignment-${accountId}-${permissionSet.label}Role-${groupId}Group`, {
1848
2549
  // Required
1849
2550
  instanceArn: iamIdentityCenterArn,
1850
2551
  permissionSetArn: permissionSet.arn,
1851
2552
  principalId: groupId,
1852
- principalType: cdk.CDK.PRINCIPAL_TYPE.GROUP,
2553
+ principalType: CDK$2.PRINCIPAL_TYPE.GROUP,
1853
2554
  targetId: accountId,
1854
- targetType: cdk.CDK.TARGET_TYPE.AWS_ACCOUNT,
2555
+ targetType: CDK$2.TARGET_TYPE.AWS_ACCOUNT,
1855
2556
  });
1856
- cdk$1.Tags.of(accountAssignment).add(cdk.CDK.TAG.SERVICE, cdk.CDK.SERVICE.SSO);
1857
- cdk$1.Tags.of(accountAssignment).add(cdk.CDK.TAG.ROLE, cdk.CDK.ROLE.SECURITY);
2557
+ cdk.Tags.of(accountAssignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
2558
+ cdk.Tags.of(accountAssignment).add(CDK$2.TAG.ROLE, CDK$2.ROLE.SECURITY);
1858
2559
  });
1859
2560
  });
1860
2561
  };
@@ -1924,18 +2625,18 @@ class JaypieSsoSyncApplication extends constructs.Construct {
1924
2625
  GoogleCredentials: resolvedGoogleCredentials,
1925
2626
  GoogleGroupMatch: resolvedGoogleGroupMatch,
1926
2627
  IdentityStoreID: resolvedIdentityStoreId,
1927
- Region: cdk$1.Stack.of(this).region,
2628
+ Region: cdk.Stack.of(this).region,
1928
2629
  SCIMEndpointAccessToken: resolvedScimEndpointAccessToken,
1929
2630
  SCIMEndpointUrl: resolvedScimEndpointUrl,
1930
2631
  },
1931
2632
  });
1932
2633
  // Add tags
1933
2634
  const defaultTags = {
1934
- [cdk.CDK.TAG.ROLE]: cdk.CDK.ROLE.SECURITY,
2635
+ [CDK$2.TAG.ROLE]: CDK$2.ROLE.SECURITY,
1935
2636
  };
1936
2637
  const allTags = { ...defaultTags, ...tags };
1937
2638
  Object.entries(allTags).forEach(([key, value]) => {
1938
- cdk$1.Tags.of(this._application).add(key, value);
2639
+ cdk.Tags.of(this._application).add(key, value);
1939
2640
  });
1940
2641
  }
1941
2642
  get application() {
@@ -1947,8 +2648,8 @@ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
1947
2648
  constructor(scope, id = "TraceSigningKey", props) {
1948
2649
  const defaultProps = {
1949
2650
  envKey: "TRACE_SIGNING_KEY",
1950
- roleTag: cdk.CDK.ROLE.API,
1951
- vendorTag: cdk.CDK.VENDOR.KNOWTRACE,
2651
+ roleTag: CDK$2.ROLE.API,
2652
+ vendorTag: CDK$2.VENDOR.KNOWTRACE,
1952
2653
  ...props,
1953
2654
  };
1954
2655
  super(scope, id, defaultProps);
@@ -1958,19 +2659,19 @@ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
1958
2659
  class JaypieWebDeploymentBucket extends constructs.Construct {
1959
2660
  constructor(scope, id, props = {}) {
1960
2661
  super(scope, id);
1961
- const roleTag = props.roleTag || cdk.CDK.ROLE.HOSTING;
2662
+ const roleTag = props.roleTag || CDK$2.ROLE.HOSTING;
1962
2663
  // Environment variable validation
1963
2664
  if (process.env.CDK_ENV_WEB_SUBDOMAIN &&
1964
- !cdk.isValidSubdomain(process.env.CDK_ENV_WEB_SUBDOMAIN)) {
1965
- throw new cdk.ConfigurationError("CDK_ENV_WEB_SUBDOMAIN is not a valid subdomain");
2665
+ !isValidSubdomain(process.env.CDK_ENV_WEB_SUBDOMAIN)) {
2666
+ throw new errors.ConfigurationError("CDK_ENV_WEB_SUBDOMAIN is not a valid subdomain");
1966
2667
  }
1967
2668
  if (process.env.CDK_ENV_WEB_HOSTED_ZONE &&
1968
- !cdk.isValidHostname(process.env.CDK_ENV_WEB_HOSTED_ZONE)) {
1969
- throw new cdk.ConfigurationError("CDK_ENV_WEB_HOSTED_ZONE is not a valid hostname");
2669
+ !isValidHostname$1(process.env.CDK_ENV_WEB_HOSTED_ZONE)) {
2670
+ throw new errors.ConfigurationError("CDK_ENV_WEB_HOSTED_ZONE is not a valid hostname");
1970
2671
  }
1971
2672
  if (process.env.CDK_ENV_HOSTED_ZONE &&
1972
- !cdk.isValidHostname(process.env.CDK_ENV_HOSTED_ZONE)) {
1973
- throw new cdk.ConfigurationError("CDK_ENV_HOSTED_ZONE is not a valid hostname");
2673
+ !isValidHostname$1(process.env.CDK_ENV_HOSTED_ZONE)) {
2674
+ throw new errors.ConfigurationError("CDK_ENV_HOSTED_ZONE is not a valid hostname");
1974
2675
  }
1975
2676
  // Determine host from props or environment
1976
2677
  let host = props.host;
@@ -1978,7 +2679,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
1978
2679
  try {
1979
2680
  host =
1980
2681
  process.env.CDK_ENV_WEB_HOST ||
1981
- cdk.mergeDomain(process.env.CDK_ENV_WEB_SUBDOMAIN || "", process.env.CDK_ENV_WEB_HOSTED_ZONE ||
2682
+ mergeDomain(process.env.CDK_ENV_WEB_SUBDOMAIN || "", process.env.CDK_ENV_WEB_HOSTED_ZONE ||
1982
2683
  process.env.CDK_ENV_HOSTED_ZONE ||
1983
2684
  "");
1984
2685
  }
@@ -1986,8 +2687,8 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
1986
2687
  host = undefined;
1987
2688
  }
1988
2689
  }
1989
- if (host && !cdk.isValidHostname(host)) {
1990
- throw new cdk.ConfigurationError("Host is not a valid hostname");
2690
+ if (host && !isValidHostname$1(host)) {
2691
+ throw new errors.ConfigurationError("Host is not a valid hostname");
1991
2692
  }
1992
2693
  // Determine zone from props or environment
1993
2694
  const zone = props.zone ||
@@ -2000,7 +2701,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2000
2701
  blockPublicAccess: s3__namespace.BlockPublicAccess.BLOCK_ACLS,
2001
2702
  bucketName: props.name || constructEnvName("web"),
2002
2703
  publicReadAccess: true,
2003
- removalPolicy: cdk$1.RemovalPolicy.DESTROY,
2704
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
2004
2705
  versioned: false,
2005
2706
  websiteErrorDocument: "index.html",
2006
2707
  websiteIndexDocument: "index.html",
@@ -2018,7 +2719,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2018
2719
  this.isWebsite = this.bucket.isWebsite;
2019
2720
  this.notificationsHandlerRole = undefined;
2020
2721
  this.policy = this.bucket.policy;
2021
- cdk$1.Tags.of(this.bucket).add(cdk.CDK.TAG.ROLE, roleTag);
2722
+ cdk.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, roleTag);
2022
2723
  // Create deployment role if repository is configured
2023
2724
  let repo;
2024
2725
  if (process.env.CDK_ENV_REPO) {
@@ -2026,14 +2727,14 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2026
2727
  }
2027
2728
  if (repo) {
2028
2729
  const bucketDeployRole = new awsIam.Role(this, "DestinationBucketDeployRole", {
2029
- assumedBy: new awsIam.FederatedPrincipal(cdk$1.Fn.importValue(cdk.CDK.IMPORT.OIDC_PROVIDER), {
2730
+ assumedBy: new awsIam.FederatedPrincipal(cdk.Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), {
2030
2731
  StringLike: {
2031
2732
  "token.actions.githubusercontent.com:sub": repo,
2032
2733
  },
2033
2734
  }, "sts:AssumeRoleWithWebIdentity"),
2034
- maxSessionDuration: cdk$1.Duration.hours(1),
2735
+ maxSessionDuration: cdk.Duration.hours(1),
2035
2736
  });
2036
- cdk$1.Tags.of(bucketDeployRole).add(cdk.CDK.TAG.ROLE, cdk.CDK.ROLE.DEPLOY);
2737
+ cdk.Tags.of(bucketDeployRole).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
2037
2738
  // Allow the role to write to the bucket
2038
2739
  bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
2039
2740
  effect: awsIam.Effect.ALLOW,
@@ -2058,7 +2759,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2058
2759
  }));
2059
2760
  this.deployRoleArn = bucketDeployRole.roleArn;
2060
2761
  // Output the deploy role ARN
2061
- new cdk$1.CfnOutput(this, "DestinationBucketDeployRoleArn", {
2762
+ new cdk.CfnOutput(this, "DestinationBucketDeployRoleArn", {
2062
2763
  value: bucketDeployRole.roleArn,
2063
2764
  });
2064
2765
  }
@@ -2078,10 +2779,10 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2078
2779
  domainName: host,
2079
2780
  validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
2080
2781
  });
2081
- new cdk$1.CfnOutput(this, "CertificateArn", {
2782
+ new cdk.CfnOutput(this, "CertificateArn", {
2082
2783
  value: this.certificate.certificateArn,
2083
2784
  });
2084
- cdk$1.Tags.of(this.certificate).add(cdk.CDK.TAG.ROLE, roleTag);
2785
+ cdk.Tags.of(this.certificate).add(CDK$2.TAG.ROLE, roleTag);
2085
2786
  }
2086
2787
  // Create CloudFront distribution
2087
2788
  this.distribution = new cloudfront__namespace.Distribution(this, "Distribution", {
@@ -2093,7 +2794,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2093
2794
  certificate: this.certificate,
2094
2795
  domainNames: [host],
2095
2796
  });
2096
- cdk$1.Tags.of(this.distribution).add(cdk.CDK.TAG.ROLE, roleTag);
2797
+ cdk.Tags.of(this.distribution).add(CDK$2.TAG.ROLE, roleTag);
2097
2798
  // If this is production, enable caching on everything but index.html
2098
2799
  if (isProductionEnv()) {
2099
2800
  this.distribution.addBehavior("/*", new origins__namespace.S3Origin(this.bucket), {
@@ -2107,7 +2808,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2107
2808
  target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
2108
2809
  zone: hostedZone,
2109
2810
  });
2110
- cdk$1.Tags.of(record).add(cdk.CDK.TAG.ROLE, cdk.CDK.ROLE.NETWORKING);
2811
+ cdk.Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
2111
2812
  this.distributionDomainName = this.distribution.distributionDomainName;
2112
2813
  }
2113
2814
  }
@@ -2209,16 +2910,17 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2209
2910
  }
2210
2911
  }
2211
2912
 
2212
- Object.defineProperty(exports, "CDK", {
2213
- enumerable: true,
2214
- get: function () { return cdk.CDK; }
2215
- });
2913
+ exports.CDK = CDK$2;
2914
+ exports.JaypieAccountLoggingBucket = JaypieAccountLoggingBucket;
2216
2915
  exports.JaypieApiGateway = JaypieApiGateway;
2217
2916
  exports.JaypieAppStack = JaypieAppStack;
2218
2917
  exports.JaypieBucketQueuedLambda = JaypieBucketQueuedLambda;
2918
+ exports.JaypieDatadogBucket = JaypieDatadogBucket;
2919
+ exports.JaypieDatadogForwarder = JaypieDatadogForwarder;
2219
2920
  exports.JaypieDatadogSecret = JaypieDatadogSecret;
2220
2921
  exports.JaypieDnsRecord = JaypieDnsRecord;
2221
2922
  exports.JaypieEnvSecret = JaypieEnvSecret;
2923
+ exports.JaypieEventsRule = JaypieEventsRule;
2222
2924
  exports.JaypieExpressLambda = JaypieExpressLambda;
2223
2925
  exports.JaypieGitHubDeployRole = JaypieGitHubDeployRole;
2224
2926
  exports.JaypieHostedZone = JaypieHostedZone;
@@ -2226,6 +2928,7 @@ exports.JaypieInfrastructureStack = JaypieInfrastructureStack;
2226
2928
  exports.JaypieLambda = JaypieLambda;
2227
2929
  exports.JaypieMongoDbSecret = JaypieMongoDbSecret;
2228
2930
  exports.JaypieOpenAiSecret = JaypieOpenAiSecret;
2931
+ exports.JaypieOrganizationTrail = JaypieOrganizationTrail;
2229
2932
  exports.JaypieQueuedLambda = JaypieQueuedLambda;
2230
2933
  exports.JaypieSsoPermissions = JaypieSsoPermissions;
2231
2934
  exports.JaypieSsoSyncApplication = JaypieSsoSyncApplication;
@@ -2237,10 +2940,14 @@ exports.constructEnvName = constructEnvName;
2237
2940
  exports.constructStackName = constructStackName;
2238
2941
  exports.constructTagger = constructTagger;
2239
2942
  exports.envHostname = envHostname;
2943
+ exports.extendDatadogRole = extendDatadogRole;
2240
2944
  exports.isEnv = isEnv;
2241
2945
  exports.isProductionEnv = isProductionEnv;
2242
2946
  exports.isSandboxEnv = isSandboxEnv;
2947
+ exports.isValidHostname = isValidHostname$1;
2948
+ exports.isValidSubdomain = isValidSubdomain;
2243
2949
  exports.jaypieLambdaEnv = jaypieLambdaEnv;
2950
+ exports.mergeDomain = mergeDomain;
2244
2951
  exports.resolveDatadogForwarderFunction = resolveDatadogForwarderFunction;
2245
2952
  exports.resolveDatadogLayers = resolveDatadogLayers;
2246
2953
  exports.resolveDatadogLoggingDestination = resolveDatadogLoggingDestination;