@jaypie/constructs 1.1.53 → 1.1.55

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,22 +1,25 @@
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
25
  var cloudfront = require('aws-cdk-lib/aws-cloudfront');
@@ -39,7 +42,8 @@ function _interopNamespaceDefault(e) {
39
42
  return Object.freeze(n);
40
43
  }
41
44
 
42
- var cdk__namespace = /*#__PURE__*/_interopNamespaceDefault(cdk$1);
45
+ var cdk__namespace = /*#__PURE__*/_interopNamespaceDefault(cdk);
46
+ var s3__namespace = /*#__PURE__*/_interopNamespaceDefault(s3);
43
47
  var acm__namespace = /*#__PURE__*/_interopNamespaceDefault(acm);
44
48
  var apiGateway__namespace = /*#__PURE__*/_interopNamespaceDefault(apiGateway);
45
49
  var route53__namespace = /*#__PURE__*/_interopNamespaceDefault(route53);
@@ -47,13 +51,231 @@ var route53Targets__namespace = /*#__PURE__*/_interopNamespaceDefault(route53Tar
47
51
  var secretsmanager__namespace = /*#__PURE__*/_interopNamespaceDefault(secretsmanager);
48
52
  var lambda__namespace = /*#__PURE__*/_interopNamespaceDefault(lambda);
49
53
  var logDestinations__namespace = /*#__PURE__*/_interopNamespaceDefault(logDestinations);
50
- var s3__namespace = /*#__PURE__*/_interopNamespaceDefault(s3);
51
54
  var s3n__namespace = /*#__PURE__*/_interopNamespaceDefault(s3n);
52
55
  var sqs__namespace = /*#__PURE__*/_interopNamespaceDefault(sqs);
53
56
  var lambdaEventSources__namespace = /*#__PURE__*/_interopNamespaceDefault(lambdaEventSources);
54
57
  var cloudfront__namespace = /*#__PURE__*/_interopNamespaceDefault(cloudfront);
55
58
  var origins__namespace = /*#__PURE__*/_interopNamespaceDefault(origins);
56
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
+
57
279
  function addDatadogLayers(lambdaFunction, options = {}) {
58
280
  const datadogApiKeyArn = options?.datadogApiKeyArn;
59
281
  const resolvedDatadogApiKeyArn = datadogApiKeyArn ||
@@ -70,8 +292,8 @@ function addDatadogLayers(lambdaFunction, options = {}) {
70
292
  DD_PROFILING_ENABLED: "false",
71
293
  DD_SERVERLESS_APPSEC_ENABLED: "false",
72
294
  DD_SERVICE: process.env.PROJECT_SERVICE || "",
73
- DD_SITE: cdk.CDK.DATADOG.SITE,
74
- 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 || ""}`,
75
297
  DD_TRACE_OTEL_ENABLED: "false",
76
298
  };
77
299
  // Add environment variables only if they don't already exist
@@ -81,8 +303,8 @@ function addDatadogLayers(lambdaFunction, options = {}) {
81
303
  const datadogApiKeySecret = secretsmanager__namespace.Secret.fromSecretCompleteArn(lambdaFunction, "DatadogApiKey", resolvedDatadogApiKeyArn);
82
304
  const datadogLambda = new datadogCdkConstructsV2.DatadogLambda(lambdaFunction, "DatadogLambda", {
83
305
  apiKeySecret: datadogApiKeySecret, // apiKeySecret auto-grants secret access to the added lambdas
84
- nodeLayerVersion: cdk.CDK.DATADOG.LAYER.NODE,
85
- extensionLayerVersion: cdk.CDK.DATADOG.LAYER.EXTENSION,
306
+ nodeLayerVersion: CDK$2.DATADOG.LAYER.NODE,
307
+ extensionLayerVersion: CDK$2.DATADOG.LAYER.EXTENSION,
86
308
  env: process.env.PROJECT_ENV,
87
309
  service: process.env.PROJECT_SERVICE,
88
310
  version: process.env.PROJECT_VERSION,
@@ -134,35 +356,35 @@ function constructTagger(construct, { name } = {}) {
134
356
  const stackName = name || constructStackName();
135
357
  const version = process.env.npm_package_version || process.env.PROJECT_VERSION || null;
136
358
  if (process.env.PROJECT_COMMIT && process.env.PROJECT_COMMIT.length > 8) {
137
- 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));
138
360
  }
139
- cdk$1.Tags.of(construct).add(CDK$1.TAG.BUILD_DATE, new Date().toISOString());
140
- 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());
141
363
  if (process.env.PROJECT_COMMIT)
142
- cdk$1.Tags.of(construct).add(CDK$1.TAG.COMMIT, process.env.PROJECT_COMMIT);
143
- 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);
144
366
  if (process.env.PROJECT_ENV)
145
- 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);
146
368
  if (process.env.PROJECT_NONCE)
147
- 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);
148
370
  if (process.env.PROJECT_KEY)
149
- cdk$1.Tags.of(construct).add(CDK$1.TAG.PROJECT, process.env.PROJECT_KEY);
150
- 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);
151
373
  if (process.env.PROJECT_SERVICE)
152
- 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);
153
375
  if (process.env.PROJECT_SPONSOR)
154
- 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);
155
377
  if (stackName)
156
- cdk$1.Tags.of(construct).add(CDK$1.TAG.STACK, stackName);
378
+ cdk.Tags.of(construct).add(CDK$1.TAG.STACK, stackName);
157
379
  if (version)
158
- cdk$1.Tags.of(construct).add(CDK$1.TAG.VERSION, version);
380
+ cdk.Tags.of(construct).add(CDK$1.TAG.VERSION, version);
159
381
  return true;
160
382
  }
161
383
 
162
384
  function envHostname({ component, domain, env, subdomain, } = {}) {
163
385
  const resolvedDomain = domain || process.env.CDK_ENV_DOMAIN || process.env.CDK_ENV_HOSTED_ZONE;
164
386
  if (!resolvedDomain) {
165
- 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");
166
388
  }
167
389
  const resolvedComponent = component === "@" || component === "" ? undefined : component;
168
390
  const resolvedSubdomain = subdomain || process.env.CDK_ENV_SUBDOMAIN;
@@ -176,6 +398,55 @@ function envHostname({ component, domain, env, subdomain, } = {}) {
176
398
  return parts.join(".");
177
399
  }
178
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
+
179
450
  /**
180
451
  * Check if the current environment matches the given environment
181
452
  */
@@ -186,13 +457,86 @@ function isEnv(env) {
186
457
  * Check if the current environment is production
187
458
  */
188
459
  function isProductionEnv() {
189
- return isEnv(cdk.CDK.ENV.PRODUCTION);
460
+ return isEnv(CDK$2.ENV.PRODUCTION);
190
461
  }
191
462
  /**
192
463
  * Check if the current environment is sandbox
193
464
  */
194
465
  function isSandboxEnv() {
195
- 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;
196
540
  }
197
541
 
198
542
  function jaypieLambdaEnv(options = {}) {
@@ -249,6 +593,21 @@ function jaypieLambdaEnv(options = {}) {
249
593
  return environment;
250
594
  }
251
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
+
252
611
  const DEFAULT_FUNCTION_NAME$1 = "DatadogForwarderFunction";
253
612
  // Cache to store resolved functions
254
613
  // Using nested structure to support multiple functions per scope with automatic GC
@@ -256,7 +615,7 @@ const functionCache = new WeakMap();
256
615
  function resolveDatadogForwarderFunction(scope, options) {
257
616
  const { import: importValue, name } = options || {};
258
617
  const functionName = name || DEFAULT_FUNCTION_NAME$1;
259
- const importKey = importValue || cdk.CDK.IMPORT.DATADOG_LOG_FORWARDER;
618
+ const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
260
619
  // Create a cache key based on name and import
261
620
  const cacheKey = `${functionName}:${importKey}`;
262
621
  // Get or create scope cache
@@ -278,7 +637,7 @@ function resolveDatadogForwarderFunction(scope, options) {
278
637
 
279
638
  function resolveDatadogLayers(scope, options = {}) {
280
639
  const { datadogApiKeyArn, uniqueId } = options;
281
- let resolvedRegion = cdk$1.Stack.of(scope).region || "us-east-1";
640
+ let resolvedRegion = cdk.Stack.of(scope).region || "us-east-1";
282
641
  // Resolve the Datadog API key ARN from multiple sources
283
642
  const resolvedDatadogApiKeyArn = datadogApiKeyArn ||
284
643
  process.env.DATADOG_API_KEY_ARN ||
@@ -289,9 +648,9 @@ function resolveDatadogLayers(scope, options = {}) {
289
648
  }
290
649
  const layerIdSuffix = uniqueId || process.env.PROJECT_NONCE || Date.now().toString();
291
650
  // Create Datadog Node.js layer
292
- 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}`);
293
652
  // Create Datadog Extension layer
294
- 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}`);
295
654
  return [datadogNodeLayer, datadogExtensionLayer];
296
655
  }
297
656
 
@@ -303,7 +662,7 @@ function resolveDatadogLoggingDestination(scope, options) {
303
662
  const { import: importValue, name } = options || {};
304
663
  // Create a cache key based on name and import (same as forwarder function)
305
664
  const functionName = name || DEFAULT_FUNCTION_NAME;
306
- const importKey = importValue || cdk.CDK.IMPORT.DATADOG_LOG_FORWARDER;
665
+ const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
307
666
  const cacheKey = `${functionName}:${importKey}`;
308
667
  // Get or create scope cache
309
668
  let scopeCache = destinationCache.get(scope);
@@ -326,7 +685,7 @@ function resolveDatadogLoggingDestination(scope, options) {
326
685
 
327
686
  function resolveHostedZone(scope, { name = "HostedZone", zone = process.env.CDK_ENV_HOSTED_ZONE, } = {}) {
328
687
  if (!zone) {
329
- 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");
330
689
  }
331
690
  if (typeof zone === "string") {
332
691
  return route53__namespace.HostedZone.fromLookup(scope, name, {
@@ -359,7 +718,7 @@ const resolveParamsAndSecrets = ({ paramsAndSecrets, options, }) => {
359
718
  class JaypieApiGateway extends constructs.Construct {
360
719
  constructor(scope, id, props) {
361
720
  super(scope, id);
362
- 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;
363
722
  // Determine zone from props or environment
364
723
  let zone = propsZone;
365
724
  if (!zone && process.env.CDK_ENV_API_HOSTED_ZONE) {
@@ -373,7 +732,7 @@ class JaypieApiGateway extends constructs.Construct {
373
732
  }
374
733
  else if (process.env.CDK_ENV_API_SUBDOMAIN &&
375
734
  process.env.CDK_ENV_API_HOSTED_ZONE) {
376
- 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);
377
736
  }
378
737
  }
379
738
  const apiGatewayName = name || constructEnvName("ApiGateway");
@@ -388,7 +747,7 @@ class JaypieApiGateway extends constructs.Construct {
388
747
  domainName: host,
389
748
  validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
390
749
  });
391
- 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);
392
751
  }
393
752
  else if (typeof certificate === "object") {
394
753
  certificateToUse = certificate;
@@ -407,19 +766,19 @@ class JaypieApiGateway extends constructs.Construct {
407
766
  handler,
408
767
  ...lambdaRestApiProps,
409
768
  });
410
- cdk$1.Tags.of(this._api).add(cdk.CDK.TAG.ROLE, roleTag);
769
+ cdk.Tags.of(this._api).add(CDK$2.TAG.ROLE, roleTag);
411
770
  if (host && certificateToUse && hostedZone) {
412
771
  this._domainName = this._api.addDomainName(apiDomainName, {
413
772
  domainName: host,
414
773
  certificate: certificateToUse,
415
774
  });
416
- cdk$1.Tags.of(this._domainName).add(cdk.CDK.TAG.ROLE, roleTag);
775
+ cdk.Tags.of(this._domainName).add(CDK$2.TAG.ROLE, roleTag);
417
776
  const record = new route53__namespace.ARecord(this, "AliasRecord", {
418
777
  recordName: host,
419
778
  target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.ApiGatewayDomain(this._domainName)),
420
779
  zone: hostedZone,
421
780
  });
422
- 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);
423
782
  }
424
783
  }
425
784
  get api() {
@@ -460,8 +819,8 @@ class JaypieApiGateway extends constructs.Construct {
460
819
  }
461
820
  get env() {
462
821
  return {
463
- account: cdk$1.Stack.of(this).account,
464
- region: cdk$1.Stack.of(this).region,
822
+ account: cdk.Stack.of(this).account,
823
+ region: cdk.Stack.of(this).region,
465
824
  };
466
825
  }
467
826
  get stack() {
@@ -504,7 +863,7 @@ class JaypieApiGateway extends constructs.Construct {
504
863
  }
505
864
  }
506
865
 
507
- class JaypieStack extends cdk$1.Stack {
866
+ class JaypieStack extends cdk.Stack {
508
867
  constructor(scope, id, props = {}) {
509
868
  const { key, ...stackProps } = props;
510
869
  // Handle stackName
@@ -537,7 +896,7 @@ class JaypieAppStack extends JaypieStack {
537
896
  class JaypieLambda extends constructs.Construct {
538
897
  constructor(scope, id, props) {
539
898
  super(scope, id);
540
- 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;
541
900
  // Get base environment with defaults
542
901
  const environment = jaypieLambdaEnv({ initialEnvironment });
543
902
  const codeAsset = typeof code === "string" ? lambda__namespace.Code.fromAsset(code) : code;
@@ -596,14 +955,14 @@ class JaypieLambda extends constructs.Construct {
596
955
  runtime,
597
956
  runtimeManagementMode,
598
957
  securityGroups,
599
- timeout: typeof timeout === "number" ? cdk$1.Duration.seconds(timeout) : timeout,
958
+ timeout: typeof timeout === "number" ? cdk.Duration.seconds(timeout) : timeout,
600
959
  tracing,
601
960
  vpc,
602
961
  vpcSubnets,
603
962
  // Enable auto-publishing of versions when using provisioned concurrency
604
963
  currentVersionOptions: provisionedConcurrentExecutions !== undefined
605
964
  ? {
606
- removalPolicy: cdk$1.RemovalPolicy.RETAIN,
965
+ removalPolicy: cdk.RemovalPolicy.RETAIN,
607
966
  description: "Auto-published version for provisioned concurrency",
608
967
  // Don't set provisioned concurrency here - it will be set on the alias
609
968
  }
@@ -632,10 +991,10 @@ class JaypieLambda extends constructs.Construct {
632
991
  this._provisioned.node.addDependency(version);
633
992
  }
634
993
  if (roleTag) {
635
- cdk$1.Tags.of(this._lambda).add(cdk.CDK.TAG.ROLE, roleTag);
994
+ cdk.Tags.of(this._lambda).add(CDK$2.TAG.ROLE, roleTag);
636
995
  }
637
996
  if (vendorTag) {
638
- cdk$1.Tags.of(this._lambda).add(cdk.CDK.TAG.VENDOR, vendorTag);
997
+ cdk.Tags.of(this._lambda).add(CDK$2.TAG.VENDOR, vendorTag);
639
998
  }
640
999
  // Assign _reference based on provisioned state
641
1000
  this._reference =
@@ -738,8 +1097,8 @@ class JaypieLambda extends constructs.Construct {
738
1097
  }
739
1098
  get env() {
740
1099
  return {
741
- account: cdk$1.Stack.of(this).account,
742
- region: cdk$1.Stack.of(this).region,
1100
+ account: cdk.Stack.of(this).account,
1101
+ region: cdk.Stack.of(this).region,
743
1102
  };
744
1103
  }
745
1104
  get stack() {
@@ -756,19 +1115,19 @@ class JaypieLambda extends constructs.Construct {
756
1115
  class JaypieQueuedLambda extends constructs.Construct {
757
1116
  constructor(scope, id, props) {
758
1117
  super(scope, id);
759
- 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;
760
1119
  // Create SQS Queue
761
1120
  this._queue = new sqs__namespace.Queue(this, "Queue", {
762
1121
  fifo,
763
1122
  visibilityTimeout: typeof visibilityTimeout === "number"
764
- ? cdk$1.Duration.seconds(visibilityTimeout)
1123
+ ? cdk.Duration.seconds(visibilityTimeout)
765
1124
  : visibilityTimeout,
766
1125
  });
767
1126
  if (roleTag) {
768
- cdk$1.Tags.of(this._queue).add(cdk.CDK.TAG.ROLE, roleTag);
1127
+ cdk.Tags.of(this._queue).add(CDK$2.TAG.ROLE, roleTag);
769
1128
  }
770
1129
  if (vendorTag) {
771
- cdk$1.Tags.of(this._queue).add(cdk.CDK.TAG.VENDOR, vendorTag);
1130
+ cdk.Tags.of(this._queue).add(CDK$2.TAG.VENDOR, vendorTag);
772
1131
  }
773
1132
  // Create Lambda with JaypieLambda
774
1133
  this._lambdaConstruct = new JaypieLambda(this, "Function", {
@@ -913,12 +1272,12 @@ class JaypieQueuedLambda extends constructs.Construct {
913
1272
  }
914
1273
  get env() {
915
1274
  return {
916
- account: cdk$1.Stack.of(this).account,
917
- region: cdk$1.Stack.of(this).region,
1275
+ account: cdk.Stack.of(this).account,
1276
+ region: cdk.Stack.of(this).region,
918
1277
  };
919
1278
  }
920
1279
  get stack() {
921
- return cdk$1.Stack.of(this);
1280
+ return cdk.Stack.of(this);
922
1281
  }
923
1282
  applyRemovalPolicy(policy) {
924
1283
  this._lambdaConstruct.applyRemovalPolicy(policy);
@@ -996,15 +1355,15 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
996
1355
  // Create S3 Bucket
997
1356
  this._bucket = new s3__namespace.Bucket(this, "Bucket", {
998
1357
  bucketName: bucketOptions.bucketName || bucketName,
999
- removalPolicy: bucketOptions.removalPolicy || cdk$1.RemovalPolicy.RETAIN,
1358
+ removalPolicy: bucketOptions.removalPolicy || cdk.RemovalPolicy.RETAIN,
1000
1359
  ...bucketOptions,
1001
1360
  });
1002
1361
  // Add tags to bucket
1003
1362
  if (roleTag) {
1004
- cdk$1.Tags.of(this._bucket).add(cdk.CDK.TAG.ROLE, roleTag);
1363
+ cdk.Tags.of(this._bucket).add(CDK$2.TAG.ROLE, roleTag);
1005
1364
  }
1006
1365
  if (vendorTag) {
1007
- cdk$1.Tags.of(this._bucket).add(cdk.CDK.TAG.VENDOR, vendorTag);
1366
+ cdk.Tags.of(this._bucket).add(CDK$2.TAG.VENDOR, vendorTag);
1008
1367
  }
1009
1368
  // Add an event notification from the bucket to the queue
1010
1369
  this._bucket.addEventNotification(s3__namespace.EventType.OBJECT_CREATED, new s3n__namespace.SqsDestination(this.queue));
@@ -1127,15 +1486,184 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
1127
1486
  }
1128
1487
  }
1129
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
+
1130
1658
  // It is a consumer if the environment is ephemeral
1131
1659
  function checkEnvIsConsumer(env = process.env) {
1132
- return (env.PROJECT_ENV === cdk.CDK.ENV.PERSONAL ||
1660
+ return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
1133
1661
  !!env.CDK_ENV_PERSONAL ||
1134
1662
  /** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
1135
1663
  /** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
1136
1664
  }
1137
1665
  function checkEnvIsProvider(env = process.env) {
1138
- return env.PROJECT_ENV === cdk.CDK.ENV.SANDBOX;
1666
+ return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
1139
1667
  }
1140
1668
  function cleanName(name) {
1141
1669
  return name.replace(/[^a-zA-Z0-9:-]/g, "");
@@ -1149,7 +1677,7 @@ function exportEnvName(name, env = process.env) {
1149
1677
  }
1150
1678
  else {
1151
1679
  if (checkEnvIsConsumer(env)) {
1152
- rawName = `env-${cdk.CDK.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
1680
+ rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
1153
1681
  }
1154
1682
  else {
1155
1683
  rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
@@ -1170,10 +1698,10 @@ class JaypieEnvSecret extends constructs.Construct {
1170
1698
  exportName = cleanName(exportParam);
1171
1699
  }
1172
1700
  if (consumer) {
1173
- const secretName = cdk$1.Fn.importValue(exportName);
1701
+ const secretName = cdk.Fn.importValue(exportName);
1174
1702
  this._secret = secretsmanager__namespace.Secret.fromSecretNameV2(this, id, secretName);
1175
1703
  // Add CfnOutput for consumer secrets
1176
- new cdk$1.CfnOutput(this, `ConsumedName`, {
1704
+ new cdk.CfnOutput(this, `ConsumedName`, {
1177
1705
  value: this._secret.secretName,
1178
1706
  });
1179
1707
  }
@@ -1182,24 +1710,24 @@ class JaypieEnvSecret extends constructs.Construct {
1182
1710
  const secretProps = {
1183
1711
  generateSecretString,
1184
1712
  secretStringValue: !generateSecretString && secretValue
1185
- ? cdk$1.SecretValue.unsafePlainText(secretValue)
1713
+ ? cdk.SecretValue.unsafePlainText(secretValue)
1186
1714
  : undefined,
1187
1715
  };
1188
1716
  this._secret = new secretsmanager__namespace.Secret(this, id, secretProps);
1189
1717
  if (roleTag) {
1190
- cdk$1.Tags.of(this._secret).add(cdk.CDK.TAG.ROLE, roleTag);
1718
+ cdk.Tags.of(this._secret).add(CDK$2.TAG.ROLE, roleTag);
1191
1719
  }
1192
1720
  if (vendorTag) {
1193
- cdk$1.Tags.of(this._secret).add(cdk.CDK.TAG.VENDOR, vendorTag);
1721
+ cdk.Tags.of(this._secret).add(CDK$2.TAG.VENDOR, vendorTag);
1194
1722
  }
1195
1723
  if (provider) {
1196
- new cdk$1.CfnOutput(this, `ProvidedName`, {
1724
+ new cdk.CfnOutput(this, `ProvidedName`, {
1197
1725
  value: this._secret.secretName,
1198
1726
  exportName,
1199
1727
  });
1200
1728
  }
1201
1729
  else {
1202
- new cdk$1.CfnOutput(this, `CreatedName`, {
1730
+ new cdk.CfnOutput(this, `CreatedName`, {
1203
1731
  value: this._secret.secretName,
1204
1732
  });
1205
1733
  }
@@ -1207,12 +1735,12 @@ class JaypieEnvSecret extends constructs.Construct {
1207
1735
  }
1208
1736
  // IResource implementation
1209
1737
  get stack() {
1210
- return cdk$1.Stack.of(this);
1738
+ return cdk.Stack.of(this);
1211
1739
  }
1212
1740
  get env() {
1213
1741
  return {
1214
- account: cdk$1.Stack.of(this).account,
1215
- region: cdk$1.Stack.of(this).region,
1742
+ account: cdk.Stack.of(this).account,
1743
+ region: cdk.Stack.of(this).region,
1216
1744
  };
1217
1745
  }
1218
1746
  applyRemovalPolicy(policy) {
@@ -1264,8 +1792,8 @@ class JaypieDatadogSecret extends JaypieEnvSecret {
1264
1792
  constructor(scope, id = "MongoConnectionString", props) {
1265
1793
  const defaultProps = {
1266
1794
  envKey: "DATADOG_API_KEY",
1267
- roleTag: cdk.CDK.ROLE.MONITORING,
1268
- vendorTag: cdk.CDK.VENDOR.DATADOG,
1795
+ roleTag: CDK$2.ROLE.MONITORING,
1796
+ vendorTag: CDK$2.VENDOR.DATADOG,
1269
1797
  ...props,
1270
1798
  };
1271
1799
  super(scope, id, defaultProps);
@@ -1276,7 +1804,7 @@ class JaypieDnsRecord extends constructs.Construct {
1276
1804
  constructor(scope, id, props) {
1277
1805
  super(scope, id);
1278
1806
  const { comment, recordName, type, values } = props;
1279
- 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);
1280
1808
  // Resolve the hosted zone (supports both string and IHostedZone)
1281
1809
  const zone = resolveHostedZone(scope, {
1282
1810
  name: `${id}HostedZone`,
@@ -1291,9 +1819,9 @@ class JaypieDnsRecord extends constructs.Construct {
1291
1819
  };
1292
1820
  // Create the appropriate record based on type
1293
1821
  switch (type) {
1294
- case cdk.CDK.DNS.RECORD.A: {
1822
+ case CDK$2.DNS.RECORD.A: {
1295
1823
  if (!Array.isArray(values) || values.length === 0) {
1296
- 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");
1297
1825
  }
1298
1826
  this.record = new route53.ARecord(this, "Record", {
1299
1827
  ...baseProps,
@@ -1301,9 +1829,9 @@ class JaypieDnsRecord extends constructs.Construct {
1301
1829
  });
1302
1830
  break;
1303
1831
  }
1304
- case cdk.CDK.DNS.RECORD.CNAME: {
1832
+ case CDK$2.DNS.RECORD.CNAME: {
1305
1833
  if (!Array.isArray(values) || values.length === 0) {
1306
- throw new cdk.ConfigurationError("CNAME record requires a domain name");
1834
+ throw new errors.ConfigurationError("CNAME record requires a domain name");
1307
1835
  }
1308
1836
  this.record = new route53.CnameRecord(this, "Record", {
1309
1837
  ...baseProps,
@@ -1311,9 +1839,9 @@ class JaypieDnsRecord extends constructs.Construct {
1311
1839
  });
1312
1840
  break;
1313
1841
  }
1314
- case cdk.CDK.DNS.RECORD.MX: {
1842
+ case CDK$2.DNS.RECORD.MX: {
1315
1843
  if (!Array.isArray(values) || values.length === 0) {
1316
- 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");
1317
1845
  }
1318
1846
  this.record = new route53.MxRecord(this, "Record", {
1319
1847
  ...baseProps,
@@ -1321,9 +1849,9 @@ class JaypieDnsRecord extends constructs.Construct {
1321
1849
  });
1322
1850
  break;
1323
1851
  }
1324
- case cdk.CDK.DNS.RECORD.NS: {
1852
+ case CDK$2.DNS.RECORD.NS: {
1325
1853
  if (!Array.isArray(values) || values.length === 0) {
1326
- 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");
1327
1855
  }
1328
1856
  this.record = new route53.NsRecord(this, "Record", {
1329
1857
  ...baseProps,
@@ -1331,9 +1859,9 @@ class JaypieDnsRecord extends constructs.Construct {
1331
1859
  });
1332
1860
  break;
1333
1861
  }
1334
- case cdk.CDK.DNS.RECORD.TXT: {
1862
+ case CDK$2.DNS.RECORD.TXT: {
1335
1863
  if (!Array.isArray(values) || values.length === 0) {
1336
- throw new cdk.ConfigurationError("TXT record requires at least one value");
1864
+ throw new errors.ConfigurationError("TXT record requires at least one value");
1337
1865
  }
1338
1866
  this.record = new route53.TxtRecord(this, "Record", {
1339
1867
  ...baseProps,
@@ -1342,26 +1870,106 @@ class JaypieDnsRecord extends constructs.Construct {
1342
1870
  break;
1343
1871
  }
1344
1872
  default:
1345
- 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`);
1346
1874
  }
1347
1875
  // Add standard tags to the DNS record
1348
- cdk__namespace.Tags.of(this.record).add(cdk.CDK.TAG.SERVICE, cdk.CDK.SERVICE.INFRASTRUCTURE);
1349
- 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
+ }
1350
1958
  }
1351
1959
  }
1352
1960
 
1353
1961
  class JaypieGitHubDeployRole extends constructs.Construct {
1354
1962
  constructor(scope, id = "GitHubDeployRole", props = {}) {
1355
1963
  super(scope, id);
1356
- 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;
1357
1965
  // Extract account ID from the scope
1358
- const accountId = cdk$1.Stack.of(this).account;
1966
+ const accountId = cdk.Stack.of(this).account;
1359
1967
  // Resolve repoRestriction from props or environment variables
1360
1968
  let repoRestriction = propsRepoRestriction;
1361
1969
  if (!repoRestriction) {
1362
1970
  const envRepo = process.env.CDK_ENV_REPO || process.env.PROJECT_REPO;
1363
1971
  if (!envRepo) {
1364
- 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");
1365
1973
  }
1366
1974
  // Extract organization from owner/repo format and create org-wide restriction
1367
1975
  const organization = envRepo.split("/")[0];
@@ -1374,10 +1982,10 @@ class JaypieGitHubDeployRole extends constructs.Construct {
1374
1982
  "token.actions.githubusercontent.com:sub": repoRestriction,
1375
1983
  },
1376
1984
  }, "sts:AssumeRoleWithWebIdentity"),
1377
- maxSessionDuration: cdk$1.Duration.hours(1),
1985
+ maxSessionDuration: cdk.Duration.hours(1),
1378
1986
  path: "/",
1379
1987
  });
1380
- 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);
1381
1989
  // Allow the role to access the GitHub OIDC provider
1382
1990
  this._role.addToPolicy(new awsIam.PolicyStatement({
1383
1991
  actions: ["sts:AssumeRoleWithWebIdentity"],
@@ -1416,7 +2024,7 @@ class JaypieGitHubDeployRole extends constructs.Construct {
1416
2024
  // Export the ARN of the role
1417
2025
  if (output !== false) {
1418
2026
  const outputId = typeof output === "string" ? output : "GitHubActionsRoleArn";
1419
- new cdk$1.CfnOutput(this, outputId, {
2027
+ new cdk.CfnOutput(this, outputId, {
1420
2028
  value: this._role.roleArn,
1421
2029
  });
1422
2030
  }
@@ -1435,8 +2043,8 @@ class JaypieGitHubDeployRole extends constructs.Construct {
1435
2043
  class JaypieExpressLambda extends JaypieLambda {
1436
2044
  constructor(scope, id, props) {
1437
2045
  super(scope, id, {
1438
- timeout: cdk$1.Duration.seconds(cdk.CDK.DURATION.EXPRESS_API),
1439
- roleTag: cdk.CDK.ROLE.API,
2046
+ timeout: cdk.Duration.seconds(CDK$2.DURATION.EXPRESS_API),
2047
+ roleTag: CDK$2.ROLE.API,
1440
2048
  ...props,
1441
2049
  });
1442
2050
  }
@@ -1496,7 +2104,7 @@ class JaypieHostedZone extends constructs.Construct {
1496
2104
  super(scope, id);
1497
2105
  const { zoneName, project } = props;
1498
2106
  const destination = props.destination ?? true;
1499
- const service = props.service || cdk.CDK.SERVICE.INFRASTRUCTURE;
2107
+ const service = props.service || CDK$2.SERVICE.INFRASTRUCTURE;
1500
2108
  // Create the log group
1501
2109
  this.logGroup = new awsLogs.LogGroup(this, "LogGroup", {
1502
2110
  logGroupName: process.env.PROJECT_NONCE
@@ -1505,10 +2113,10 @@ class JaypieHostedZone extends constructs.Construct {
1505
2113
  retention: awsLogs.RetentionDays.ONE_WEEK,
1506
2114
  });
1507
2115
  // Add tags
1508
- cdk__namespace.Tags.of(this.logGroup).add(cdk.CDK.TAG.SERVICE, service);
1509
- 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);
1510
2118
  if (project) {
1511
- 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);
1512
2120
  }
1513
2121
  // Grant Route 53 permissions to write to the log group
1514
2122
  this.logGroup.grantWrite(new awsIam.ServicePrincipal(SERVICE.ROUTE53));
@@ -1528,10 +2136,10 @@ class JaypieHostedZone extends constructs.Construct {
1528
2136
  zoneName,
1529
2137
  });
1530
2138
  // Add tags
1531
- cdk__namespace.Tags.of(this.hostedZone).add(cdk.CDK.TAG.SERVICE, service);
1532
- 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);
1533
2141
  if (project) {
1534
- 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);
1535
2143
  }
1536
2144
  // Create DNS records if provided
1537
2145
  this.dnsRecords = [];
@@ -1566,7 +2174,7 @@ class JaypieInfrastructureStack extends JaypieStack {
1566
2174
  super(scope, id, { key, ...stackProps });
1567
2175
  // Add infrastructure-specific tag
1568
2176
  if (process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA) {
1569
- 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);
1570
2178
  }
1571
2179
  }
1572
2180
  }
@@ -1575,8 +2183,8 @@ class JaypieMongoDbSecret extends JaypieEnvSecret {
1575
2183
  constructor(scope, id = "MongoConnectionString", props) {
1576
2184
  const defaultProps = {
1577
2185
  envKey: "MONGODB_URI",
1578
- roleTag: cdk.CDK.ROLE.STORAGE,
1579
- vendorTag: cdk.CDK.VENDOR.MONGODB,
2186
+ roleTag: CDK$2.ROLE.STORAGE,
2187
+ vendorTag: CDK$2.VENDOR.MONGODB,
1580
2188
  ...props,
1581
2189
  };
1582
2190
  super(scope, id, defaultProps);
@@ -1587,14 +2195,108 @@ class JaypieOpenAiSecret extends JaypieEnvSecret {
1587
2195
  constructor(scope, id = "OpenAiApiKey", props) {
1588
2196
  const defaultProps = {
1589
2197
  envKey: "OPENAI_API_KEY",
1590
- roleTag: cdk.CDK.ROLE.PROCESSING,
1591
- vendorTag: cdk.CDK.VENDOR.OPENAI,
2198
+ roleTag: CDK$2.ROLE.PROCESSING,
2199
+ vendorTag: CDK$2.VENDOR.OPENAI,
1592
2200
  ...props,
1593
2201
  };
1594
2202
  super(scope, id, defaultProps);
1595
2203
  }
1596
2204
  }
1597
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
+
1598
2300
  /**
1599
2301
  * JaypieSsoPermissions Construct
1600
2302
  *
@@ -1661,15 +2363,15 @@ class JaypieSsoPermissions extends constructs.Construct {
1661
2363
  .managedPolicyArn,
1662
2364
  awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
1663
2365
  ],
1664
- sessionDuration: cdk$1.Duration.hours(1).toIsoString(),
2366
+ sessionDuration: cdk.Duration.hours(1).toIsoString(),
1665
2367
  tags: [
1666
2368
  {
1667
- key: cdk.CDK.TAG.SERVICE,
1668
- value: cdk.CDK.SERVICE.SSO,
2369
+ key: CDK$2.TAG.SERVICE,
2370
+ value: CDK$2.SERVICE.SSO,
1669
2371
  },
1670
2372
  {
1671
- key: cdk.CDK.TAG.ROLE,
1672
- value: cdk.CDK.ROLE.SECURITY,
2373
+ key: CDK$2.TAG.ROLE,
2374
+ value: CDK$2.ROLE.SECURITY,
1673
2375
  },
1674
2376
  ],
1675
2377
  });
@@ -1743,15 +2445,15 @@ class JaypieSsoPermissions extends constructs.Construct {
1743
2445
  awsIam.ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
1744
2446
  .managedPolicyArn,
1745
2447
  ],
1746
- sessionDuration: cdk$1.Duration.hours(12).toIsoString(),
2448
+ sessionDuration: cdk.Duration.hours(12).toIsoString(),
1747
2449
  tags: [
1748
2450
  {
1749
- key: cdk.CDK.TAG.SERVICE,
1750
- value: cdk.CDK.SERVICE.SSO,
2451
+ key: CDK$2.TAG.SERVICE,
2452
+ value: CDK$2.SERVICE.SSO,
1751
2453
  },
1752
2454
  {
1753
- key: cdk.CDK.TAG.ROLE,
1754
- value: cdk.CDK.ROLE.SECURITY,
2455
+ key: CDK$2.TAG.ROLE,
2456
+ value: CDK$2.ROLE.SECURITY,
1755
2457
  },
1756
2458
  ],
1757
2459
  });
@@ -1801,15 +2503,15 @@ class JaypieSsoPermissions extends constructs.Construct {
1801
2503
  .managedPolicyArn,
1802
2504
  awsIam.ManagedPolicy.fromAwsManagedPolicyName("job-function/SystemAdministrator").managedPolicyArn,
1803
2505
  ],
1804
- sessionDuration: cdk$1.Duration.hours(4).toIsoString(),
2506
+ sessionDuration: cdk.Duration.hours(4).toIsoString(),
1805
2507
  tags: [
1806
2508
  {
1807
- key: cdk.CDK.TAG.SERVICE,
1808
- value: cdk.CDK.SERVICE.SSO,
2509
+ key: CDK$2.TAG.SERVICE,
2510
+ value: CDK$2.SERVICE.SSO,
1809
2511
  },
1810
2512
  {
1811
- key: cdk.CDK.TAG.ROLE,
1812
- value: cdk.CDK.ROLE.SECURITY,
2513
+ key: CDK$2.TAG.ROLE,
2514
+ value: CDK$2.ROLE.SECURITY,
1813
2515
  },
1814
2516
  ],
1815
2517
  });
@@ -1841,19 +2543,19 @@ class JaypieSsoPermissions extends constructs.Construct {
1841
2543
  permissionSetNames.forEach((permissionSetName) => {
1842
2544
  const permissionSet = permissionSetMap[permissionSetName];
1843
2545
  if (!permissionSet) {
1844
- 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(", ")}`);
1845
2547
  }
1846
2548
  const accountAssignment = new awsSso.CfnAssignment(this, `AccountAssignment-${accountId}-${permissionSet.label}Role-${groupId}Group`, {
1847
2549
  // Required
1848
2550
  instanceArn: iamIdentityCenterArn,
1849
2551
  permissionSetArn: permissionSet.arn,
1850
2552
  principalId: groupId,
1851
- principalType: cdk.CDK.PRINCIPAL_TYPE.GROUP,
2553
+ principalType: CDK$2.PRINCIPAL_TYPE.GROUP,
1852
2554
  targetId: accountId,
1853
- targetType: cdk.CDK.TARGET_TYPE.AWS_ACCOUNT,
2555
+ targetType: CDK$2.TARGET_TYPE.AWS_ACCOUNT,
1854
2556
  });
1855
- cdk$1.Tags.of(accountAssignment).add(cdk.CDK.TAG.SERVICE, cdk.CDK.SERVICE.SSO);
1856
- 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);
1857
2559
  });
1858
2560
  });
1859
2561
  };
@@ -1909,7 +2611,7 @@ class JaypieSsoSyncApplication extends constructs.Construct {
1909
2611
  missingParams.push(`scimEndpointUrl or ${scimEndpointUrlEnvKey} environment variable`);
1910
2612
  }
1911
2613
  if (missingParams.length > 0) {
1912
- throw new cdk.ConfigurationError(`JaypieSsoSyncApplication missing required configuration: ${missingParams.join(", ")}`);
2614
+ throw new errors.ConfigurationError(`JaypieSsoSyncApplication missing required configuration: ${missingParams.join(", ")}`);
1913
2615
  }
1914
2616
  // Create the SSO Sync Application
1915
2617
  // Type assertion is safe because we validated all required values above
@@ -1923,18 +2625,18 @@ class JaypieSsoSyncApplication extends constructs.Construct {
1923
2625
  GoogleCredentials: resolvedGoogleCredentials,
1924
2626
  GoogleGroupMatch: resolvedGoogleGroupMatch,
1925
2627
  IdentityStoreID: resolvedIdentityStoreId,
1926
- Region: cdk$1.Stack.of(this).region,
2628
+ Region: cdk.Stack.of(this).region,
1927
2629
  SCIMEndpointAccessToken: resolvedScimEndpointAccessToken,
1928
2630
  SCIMEndpointUrl: resolvedScimEndpointUrl,
1929
2631
  },
1930
2632
  });
1931
2633
  // Add tags
1932
2634
  const defaultTags = {
1933
- [cdk.CDK.TAG.ROLE]: cdk.CDK.ROLE.SECURITY,
2635
+ [CDK$2.TAG.ROLE]: CDK$2.ROLE.SECURITY,
1934
2636
  };
1935
2637
  const allTags = { ...defaultTags, ...tags };
1936
2638
  Object.entries(allTags).forEach(([key, value]) => {
1937
- cdk$1.Tags.of(this._application).add(key, value);
2639
+ cdk.Tags.of(this._application).add(key, value);
1938
2640
  });
1939
2641
  }
1940
2642
  get application() {
@@ -1946,8 +2648,8 @@ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
1946
2648
  constructor(scope, id = "TraceSigningKey", props) {
1947
2649
  const defaultProps = {
1948
2650
  envKey: "TRACE_SIGNING_KEY",
1949
- roleTag: cdk.CDK.ROLE.API,
1950
- vendorTag: cdk.CDK.VENDOR.KNOWTRACE,
2651
+ roleTag: CDK$2.ROLE.API,
2652
+ vendorTag: CDK$2.VENDOR.KNOWTRACE,
1951
2653
  ...props,
1952
2654
  };
1953
2655
  super(scope, id, defaultProps);
@@ -1957,19 +2659,19 @@ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
1957
2659
  class JaypieWebDeploymentBucket extends constructs.Construct {
1958
2660
  constructor(scope, id, props = {}) {
1959
2661
  super(scope, id);
1960
- const roleTag = props.roleTag || cdk.CDK.ROLE.HOSTING;
2662
+ const roleTag = props.roleTag || CDK$2.ROLE.HOSTING;
1961
2663
  // Environment variable validation
1962
2664
  if (process.env.CDK_ENV_WEB_SUBDOMAIN &&
1963
- !cdk.isValidSubdomain(process.env.CDK_ENV_WEB_SUBDOMAIN)) {
1964
- 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");
1965
2667
  }
1966
2668
  if (process.env.CDK_ENV_WEB_HOSTED_ZONE &&
1967
- !cdk.isValidHostname(process.env.CDK_ENV_WEB_HOSTED_ZONE)) {
1968
- 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");
1969
2671
  }
1970
2672
  if (process.env.CDK_ENV_HOSTED_ZONE &&
1971
- !cdk.isValidHostname(process.env.CDK_ENV_HOSTED_ZONE)) {
1972
- 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");
1973
2675
  }
1974
2676
  // Determine host from props or environment
1975
2677
  let host = props.host;
@@ -1977,7 +2679,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
1977
2679
  try {
1978
2680
  host =
1979
2681
  process.env.CDK_ENV_WEB_HOST ||
1980
- 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 ||
1981
2683
  process.env.CDK_ENV_HOSTED_ZONE ||
1982
2684
  "");
1983
2685
  }
@@ -1985,8 +2687,8 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
1985
2687
  host = undefined;
1986
2688
  }
1987
2689
  }
1988
- if (host && !cdk.isValidHostname(host)) {
1989
- 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");
1990
2692
  }
1991
2693
  // Determine zone from props or environment
1992
2694
  const zone = props.zone ||
@@ -1999,7 +2701,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
1999
2701
  blockPublicAccess: s3__namespace.BlockPublicAccess.BLOCK_ACLS,
2000
2702
  bucketName: props.name || constructEnvName("web"),
2001
2703
  publicReadAccess: true,
2002
- removalPolicy: cdk$1.RemovalPolicy.DESTROY,
2704
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
2003
2705
  versioned: false,
2004
2706
  websiteErrorDocument: "index.html",
2005
2707
  websiteIndexDocument: "index.html",
@@ -2017,7 +2719,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2017
2719
  this.isWebsite = this.bucket.isWebsite;
2018
2720
  this.notificationsHandlerRole = undefined;
2019
2721
  this.policy = this.bucket.policy;
2020
- cdk$1.Tags.of(this.bucket).add(cdk.CDK.TAG.ROLE, roleTag);
2722
+ cdk.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, roleTag);
2021
2723
  // Create deployment role if repository is configured
2022
2724
  let repo;
2023
2725
  if (process.env.CDK_ENV_REPO) {
@@ -2025,14 +2727,14 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2025
2727
  }
2026
2728
  if (repo) {
2027
2729
  const bucketDeployRole = new awsIam.Role(this, "DestinationBucketDeployRole", {
2028
- 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), {
2029
2731
  StringLike: {
2030
2732
  "token.actions.githubusercontent.com:sub": repo,
2031
2733
  },
2032
2734
  }, "sts:AssumeRoleWithWebIdentity"),
2033
- maxSessionDuration: cdk$1.Duration.hours(1),
2735
+ maxSessionDuration: cdk.Duration.hours(1),
2034
2736
  });
2035
- 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);
2036
2738
  // Allow the role to write to the bucket
2037
2739
  bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
2038
2740
  effect: awsIam.Effect.ALLOW,
@@ -2057,7 +2759,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2057
2759
  }));
2058
2760
  this.deployRoleArn = bucketDeployRole.roleArn;
2059
2761
  // Output the deploy role ARN
2060
- new cdk$1.CfnOutput(this, "DestinationBucketDeployRoleArn", {
2762
+ new cdk.CfnOutput(this, "DestinationBucketDeployRoleArn", {
2061
2763
  value: bucketDeployRole.roleArn,
2062
2764
  });
2063
2765
  }
@@ -2077,10 +2779,10 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2077
2779
  domainName: host,
2078
2780
  validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
2079
2781
  });
2080
- new cdk$1.CfnOutput(this, "CertificateArn", {
2782
+ new cdk.CfnOutput(this, "CertificateArn", {
2081
2783
  value: this.certificate.certificateArn,
2082
2784
  });
2083
- cdk$1.Tags.of(this.certificate).add(cdk.CDK.TAG.ROLE, roleTag);
2785
+ cdk.Tags.of(this.certificate).add(CDK$2.TAG.ROLE, roleTag);
2084
2786
  }
2085
2787
  // Create CloudFront distribution
2086
2788
  this.distribution = new cloudfront__namespace.Distribution(this, "Distribution", {
@@ -2092,7 +2794,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2092
2794
  certificate: this.certificate,
2093
2795
  domainNames: [host],
2094
2796
  });
2095
- cdk$1.Tags.of(this.distribution).add(cdk.CDK.TAG.ROLE, roleTag);
2797
+ cdk.Tags.of(this.distribution).add(CDK$2.TAG.ROLE, roleTag);
2096
2798
  // If this is production, enable caching on everything but index.html
2097
2799
  if (isProductionEnv()) {
2098
2800
  this.distribution.addBehavior("/*", new origins__namespace.S3Origin(this.bucket), {
@@ -2106,7 +2808,7 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2106
2808
  target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
2107
2809
  zone: hostedZone,
2108
2810
  });
2109
- 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);
2110
2812
  this.distributionDomainName = this.distribution.distributionDomainName;
2111
2813
  }
2112
2814
  }
@@ -2208,16 +2910,17 @@ class JaypieWebDeploymentBucket extends constructs.Construct {
2208
2910
  }
2209
2911
  }
2210
2912
 
2211
- Object.defineProperty(exports, "CDK", {
2212
- enumerable: true,
2213
- get: function () { return cdk.CDK; }
2214
- });
2913
+ exports.CDK = CDK$2;
2914
+ exports.JaypieAccountLoggingBucket = JaypieAccountLoggingBucket;
2215
2915
  exports.JaypieApiGateway = JaypieApiGateway;
2216
2916
  exports.JaypieAppStack = JaypieAppStack;
2217
2917
  exports.JaypieBucketQueuedLambda = JaypieBucketQueuedLambda;
2918
+ exports.JaypieDatadogBucket = JaypieDatadogBucket;
2919
+ exports.JaypieDatadogForwarder = JaypieDatadogForwarder;
2218
2920
  exports.JaypieDatadogSecret = JaypieDatadogSecret;
2219
2921
  exports.JaypieDnsRecord = JaypieDnsRecord;
2220
2922
  exports.JaypieEnvSecret = JaypieEnvSecret;
2923
+ exports.JaypieEventsRule = JaypieEventsRule;
2221
2924
  exports.JaypieExpressLambda = JaypieExpressLambda;
2222
2925
  exports.JaypieGitHubDeployRole = JaypieGitHubDeployRole;
2223
2926
  exports.JaypieHostedZone = JaypieHostedZone;
@@ -2225,6 +2928,7 @@ exports.JaypieInfrastructureStack = JaypieInfrastructureStack;
2225
2928
  exports.JaypieLambda = JaypieLambda;
2226
2929
  exports.JaypieMongoDbSecret = JaypieMongoDbSecret;
2227
2930
  exports.JaypieOpenAiSecret = JaypieOpenAiSecret;
2931
+ exports.JaypieOrganizationTrail = JaypieOrganizationTrail;
2228
2932
  exports.JaypieQueuedLambda = JaypieQueuedLambda;
2229
2933
  exports.JaypieSsoPermissions = JaypieSsoPermissions;
2230
2934
  exports.JaypieSsoSyncApplication = JaypieSsoSyncApplication;
@@ -2236,10 +2940,14 @@ exports.constructEnvName = constructEnvName;
2236
2940
  exports.constructStackName = constructStackName;
2237
2941
  exports.constructTagger = constructTagger;
2238
2942
  exports.envHostname = envHostname;
2943
+ exports.extendDatadogRole = extendDatadogRole;
2239
2944
  exports.isEnv = isEnv;
2240
2945
  exports.isProductionEnv = isProductionEnv;
2241
2946
  exports.isSandboxEnv = isSandboxEnv;
2947
+ exports.isValidHostname = isValidHostname$1;
2948
+ exports.isValidSubdomain = isValidSubdomain;
2242
2949
  exports.jaypieLambdaEnv = jaypieLambdaEnv;
2950
+ exports.mergeDomain = mergeDomain;
2243
2951
  exports.resolveDatadogForwarderFunction = resolveDatadogForwarderFunction;
2244
2952
  exports.resolveDatadogLayers = resolveDatadogLayers;
2245
2953
  exports.resolveDatadogLoggingDestination = resolveDatadogLoggingDestination;