@jaypie/constructs 1.1.65 → 1.1.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/dist/cjs/JaypieAccountLoggingBucket.d.ts +60 -0
  2. package/dist/cjs/JaypieApiGateway.d.ts +47 -0
  3. package/dist/cjs/JaypieAppStack.d.ts +5 -0
  4. package/dist/cjs/JaypieBucketQueuedLambda.d.ts +48 -0
  5. package/dist/cjs/JaypieDatadogBucket.d.ts +55 -0
  6. package/dist/cjs/JaypieDatadogForwarder.d.ts +76 -0
  7. package/dist/cjs/JaypieDatadogSecret.d.ts +5 -0
  8. package/dist/cjs/JaypieDistribution.d.ts +76 -0
  9. package/dist/cjs/JaypieDnsRecord.d.ts +45 -0
  10. package/dist/cjs/JaypieEnvSecret.d.ts +41 -0
  11. package/dist/cjs/JaypieEventsRule.d.ts +45 -0
  12. package/dist/cjs/JaypieExpressLambda.d.ts +5 -0
  13. package/dist/cjs/JaypieGitHubDeployRole.d.ts +14 -0
  14. package/dist/cjs/JaypieHostedZone.d.ts +59 -0
  15. package/dist/cjs/JaypieInfrastructureStack.d.ts +5 -0
  16. package/dist/cjs/JaypieLambda.d.ts +115 -0
  17. package/dist/cjs/JaypieMongoDbSecret.d.ts +5 -0
  18. package/dist/cjs/JaypieNextJs.d.ts +36 -0
  19. package/dist/cjs/JaypieNextJs.test.d.ts +1 -0
  20. package/dist/cjs/JaypieOpenAiSecret.d.ts +5 -0
  21. package/dist/cjs/JaypieOrganizationTrail.d.ts +62 -0
  22. package/dist/cjs/JaypieQueuedLambda.d.ts +77 -0
  23. package/dist/cjs/JaypieSsoPermissions.d.ts +96 -0
  24. package/dist/cjs/JaypieSsoSyncApplication.d.ts +27 -0
  25. package/dist/cjs/JaypieStack.d.ts +8 -0
  26. package/dist/cjs/JaypieStaticWebBucket.d.ts +22 -0
  27. package/dist/cjs/JaypieTraceSigningKeySecret.d.ts +5 -0
  28. package/dist/cjs/JaypieWebDeploymentBucket.d.ts +84 -0
  29. package/dist/cjs/__tests__/JaypieBucketQueuedLambda.spec.d.ts +1 -0
  30. package/dist/cjs/__tests__/JaypieDistribution.spec.d.ts +1 -0
  31. package/dist/cjs/__tests__/JaypieDnsRecord.spec.d.ts +1 -0
  32. package/dist/cjs/__tests__/JaypieEnvSecret.spec.d.ts +1 -0
  33. package/dist/cjs/__tests__/JaypieExpressLambda.spec.d.ts +1 -0
  34. package/dist/cjs/__tests__/JaypieHostedZone.spec.d.ts +1 -0
  35. package/dist/cjs/__tests__/JaypieLambda.spec.d.ts +1 -0
  36. package/dist/cjs/__tests__/JaypieQueuedLambda.spec.d.ts +1 -0
  37. package/dist/cjs/__tests__/JaypieSsoPermissions.spec.d.ts +1 -0
  38. package/dist/cjs/__tests__/JaypieSsoSyncApplication.spec.d.ts +1 -0
  39. package/dist/cjs/__tests__/JaypieStaticWebBucket.spec.d.ts +1 -0
  40. package/dist/cjs/__tests__/index.spec.d.ts +1 -0
  41. package/dist/cjs/constants.d.ts +151 -0
  42. package/dist/cjs/helpers/__tests__/envHostname.spec.d.ts +1 -0
  43. package/dist/cjs/helpers/__tests__/jaypieLambdaEnv.spec.d.ts +1 -0
  44. package/dist/cjs/helpers/__tests__/resolveDatadogForwarderFunction.spec.d.ts +1 -0
  45. package/dist/cjs/helpers/__tests__/resolveDatadogLoggingDestination.spec.d.ts +1 -0
  46. package/dist/cjs/helpers/__tests__/resolveEnvironment.spec.d.ts +1 -0
  47. package/dist/cjs/helpers/__tests__/resolveSecrets.spec.d.ts +1 -0
  48. package/dist/cjs/helpers/addDatadogLayers.d.ts +5 -0
  49. package/dist/cjs/helpers/constructEnvName.d.ts +5 -0
  50. package/dist/cjs/helpers/constructStackName.d.ts +1 -0
  51. package/dist/cjs/helpers/constructTagger.d.ts +4 -0
  52. package/dist/cjs/helpers/envHostname.d.ts +6 -0
  53. package/dist/cjs/helpers/extendDatadogRole.d.ts +31 -0
  54. package/dist/cjs/helpers/index.d.ts +18 -0
  55. package/dist/cjs/helpers/isEnv.d.ts +12 -0
  56. package/dist/cjs/helpers/isValidHostname.d.ts +1 -0
  57. package/dist/cjs/helpers/isValidSubdomain.d.ts +1 -0
  58. package/dist/cjs/helpers/jaypieLambdaEnv.d.ts +8 -0
  59. package/dist/cjs/helpers/mergeDomain.d.ts +1 -0
  60. package/dist/cjs/helpers/resolveDatadogForwarderFunction.d.ts +7 -0
  61. package/dist/cjs/helpers/resolveDatadogLayers.d.ts +7 -0
  62. package/dist/cjs/helpers/resolveDatadogLoggingDestination.d.ts +4 -0
  63. package/dist/cjs/helpers/resolveEnvironment.d.ts +47 -0
  64. package/dist/cjs/helpers/resolveHostedZone.d.ts +6 -0
  65. package/dist/cjs/helpers/resolveParamsAndSecrets.d.ts +13 -0
  66. package/dist/cjs/helpers/resolveSecrets.d.ts +52 -0
  67. package/dist/cjs/index.cjs +3503 -0
  68. package/dist/cjs/index.cjs.map +1 -0
  69. package/dist/cjs/index.d.ts +29 -0
  70. package/dist/esm/JaypieAccountLoggingBucket.d.ts +60 -0
  71. package/dist/esm/JaypieApiGateway.d.ts +47 -0
  72. package/dist/esm/JaypieAppStack.d.ts +5 -0
  73. package/dist/esm/JaypieBucketQueuedLambda.d.ts +48 -0
  74. package/dist/esm/JaypieDatadogBucket.d.ts +55 -0
  75. package/dist/esm/JaypieDatadogForwarder.d.ts +76 -0
  76. package/dist/esm/JaypieDatadogSecret.d.ts +5 -0
  77. package/dist/esm/JaypieDistribution.d.ts +76 -0
  78. package/dist/esm/JaypieDnsRecord.d.ts +45 -0
  79. package/dist/esm/JaypieEnvSecret.d.ts +41 -0
  80. package/dist/esm/JaypieEventsRule.d.ts +45 -0
  81. package/dist/esm/JaypieExpressLambda.d.ts +5 -0
  82. package/dist/esm/JaypieGitHubDeployRole.d.ts +14 -0
  83. package/dist/esm/JaypieHostedZone.d.ts +59 -0
  84. package/dist/esm/JaypieInfrastructureStack.d.ts +5 -0
  85. package/dist/esm/JaypieLambda.d.ts +115 -0
  86. package/dist/esm/JaypieMongoDbSecret.d.ts +5 -0
  87. package/dist/esm/JaypieNextJs.d.ts +36 -0
  88. package/dist/esm/JaypieNextJs.test.d.ts +1 -0
  89. package/dist/esm/JaypieOpenAiSecret.d.ts +5 -0
  90. package/dist/esm/JaypieOrganizationTrail.d.ts +62 -0
  91. package/dist/esm/JaypieQueuedLambda.d.ts +77 -0
  92. package/dist/esm/JaypieSsoPermissions.d.ts +96 -0
  93. package/dist/esm/JaypieSsoSyncApplication.d.ts +27 -0
  94. package/dist/esm/JaypieStack.d.ts +8 -0
  95. package/dist/esm/JaypieStaticWebBucket.d.ts +22 -0
  96. package/dist/esm/JaypieTraceSigningKeySecret.d.ts +5 -0
  97. package/dist/esm/JaypieWebDeploymentBucket.d.ts +84 -0
  98. package/dist/esm/__tests__/JaypieBucketQueuedLambda.spec.d.ts +1 -0
  99. package/dist/esm/__tests__/JaypieDistribution.spec.d.ts +1 -0
  100. package/dist/esm/__tests__/JaypieDnsRecord.spec.d.ts +1 -0
  101. package/dist/esm/__tests__/JaypieEnvSecret.spec.d.ts +1 -0
  102. package/dist/esm/__tests__/JaypieExpressLambda.spec.d.ts +1 -0
  103. package/dist/esm/__tests__/JaypieHostedZone.spec.d.ts +1 -0
  104. package/dist/esm/__tests__/JaypieLambda.spec.d.ts +1 -0
  105. package/dist/esm/__tests__/JaypieQueuedLambda.spec.d.ts +1 -0
  106. package/dist/esm/__tests__/JaypieSsoPermissions.spec.d.ts +1 -0
  107. package/dist/esm/__tests__/JaypieSsoSyncApplication.spec.d.ts +1 -0
  108. package/dist/esm/__tests__/JaypieStaticWebBucket.spec.d.ts +1 -0
  109. package/dist/esm/__tests__/index.spec.d.ts +1 -0
  110. package/dist/esm/constants.d.ts +151 -0
  111. package/dist/esm/helpers/__tests__/envHostname.spec.d.ts +1 -0
  112. package/dist/esm/helpers/__tests__/jaypieLambdaEnv.spec.d.ts +1 -0
  113. package/dist/esm/helpers/__tests__/resolveDatadogForwarderFunction.spec.d.ts +1 -0
  114. package/dist/esm/helpers/__tests__/resolveDatadogLoggingDestination.spec.d.ts +1 -0
  115. package/dist/esm/helpers/__tests__/resolveEnvironment.spec.d.ts +1 -0
  116. package/dist/esm/helpers/__tests__/resolveSecrets.spec.d.ts +1 -0
  117. package/dist/esm/helpers/addDatadogLayers.d.ts +5 -0
  118. package/dist/esm/helpers/constructEnvName.d.ts +5 -0
  119. package/dist/esm/helpers/constructStackName.d.ts +1 -0
  120. package/dist/esm/helpers/constructTagger.d.ts +4 -0
  121. package/dist/esm/helpers/envHostname.d.ts +6 -0
  122. package/dist/esm/helpers/extendDatadogRole.d.ts +31 -0
  123. package/dist/esm/helpers/index.d.ts +18 -0
  124. package/dist/esm/helpers/isEnv.d.ts +12 -0
  125. package/dist/esm/helpers/isValidHostname.d.ts +1 -0
  126. package/dist/esm/helpers/isValidSubdomain.d.ts +1 -0
  127. package/dist/esm/helpers/jaypieLambdaEnv.d.ts +8 -0
  128. package/dist/esm/helpers/mergeDomain.d.ts +1 -0
  129. package/dist/esm/helpers/resolveDatadogForwarderFunction.d.ts +7 -0
  130. package/dist/esm/helpers/resolveDatadogLayers.d.ts +7 -0
  131. package/dist/esm/helpers/resolveDatadogLoggingDestination.d.ts +4 -0
  132. package/dist/esm/helpers/resolveEnvironment.d.ts +47 -0
  133. package/dist/esm/helpers/resolveHostedZone.d.ts +6 -0
  134. package/dist/esm/helpers/resolveParamsAndSecrets.d.ts +13 -0
  135. package/dist/esm/helpers/resolveSecrets.d.ts +52 -0
  136. package/dist/esm/index.d.ts +29 -0
  137. package/dist/esm/index.js +3423 -0
  138. package/dist/esm/index.js.map +1 -0
  139. package/package.json +2 -2
@@ -0,0 +1,3503 @@
1
+ 'use strict';
2
+
3
+ var cdk = require('aws-cdk-lib');
4
+ var s3 = require('aws-cdk-lib/aws-s3');
5
+ var constructs = require('constructs');
6
+ var acm = require('aws-cdk-lib/aws-certificatemanager');
7
+ var apiGateway = require('aws-cdk-lib/aws-apigateway');
8
+ var route53 = require('aws-cdk-lib/aws-route53');
9
+ var route53Targets = require('aws-cdk-lib/aws-route53-targets');
10
+ var secretsmanager = require('aws-cdk-lib/aws-secretsmanager');
11
+ var datadogCdkConstructsV2 = require('datadog-cdk-constructs-v2');
12
+ var errors = require('@jaypie/errors');
13
+ var awsIam = require('aws-cdk-lib/aws-iam');
14
+ var lambda = require('aws-cdk-lib/aws-lambda');
15
+ var logDestinations = require('aws-cdk-lib/aws-logs-destinations');
16
+ var s3n = require('aws-cdk-lib/aws-s3-notifications');
17
+ var sqs = require('aws-cdk-lib/aws-sqs');
18
+ var lambdaEventSources = require('aws-cdk-lib/aws-lambda-event-sources');
19
+ var logs = require('aws-cdk-lib/aws-logs');
20
+ var awsEvents = require('aws-cdk-lib/aws-events');
21
+ var awsEventsTargets = require('aws-cdk-lib/aws-events-targets');
22
+ var cloudfront = require('aws-cdk-lib/aws-cloudfront');
23
+ var origins = require('aws-cdk-lib/aws-cloudfront-origins');
24
+ var cdkNextjsStandalone = require('cdk-nextjs-standalone');
25
+ var path = require('path');
26
+ var awsCloudtrail = require('aws-cdk-lib/aws-cloudtrail');
27
+ var awsSso = require('aws-cdk-lib/aws-sso');
28
+ var awsSam = require('aws-cdk-lib/aws-sam');
29
+
30
+ function _interopNamespaceDefault(e) {
31
+ var n = Object.create(null);
32
+ if (e) {
33
+ Object.keys(e).forEach(function (k) {
34
+ if (k !== 'default') {
35
+ var d = Object.getOwnPropertyDescriptor(e, k);
36
+ Object.defineProperty(n, k, d.get ? d : {
37
+ enumerable: true,
38
+ get: function () { return e[k]; }
39
+ });
40
+ }
41
+ });
42
+ }
43
+ n.default = e;
44
+ return Object.freeze(n);
45
+ }
46
+
47
+ var cdk__namespace = /*#__PURE__*/_interopNamespaceDefault(cdk);
48
+ var s3__namespace = /*#__PURE__*/_interopNamespaceDefault(s3);
49
+ var acm__namespace = /*#__PURE__*/_interopNamespaceDefault(acm);
50
+ var apiGateway__namespace = /*#__PURE__*/_interopNamespaceDefault(apiGateway);
51
+ var route53__namespace = /*#__PURE__*/_interopNamespaceDefault(route53);
52
+ var route53Targets__namespace = /*#__PURE__*/_interopNamespaceDefault(route53Targets);
53
+ var secretsmanager__namespace = /*#__PURE__*/_interopNamespaceDefault(secretsmanager);
54
+ var lambda__namespace = /*#__PURE__*/_interopNamespaceDefault(lambda);
55
+ var logDestinations__namespace = /*#__PURE__*/_interopNamespaceDefault(logDestinations);
56
+ var s3n__namespace = /*#__PURE__*/_interopNamespaceDefault(s3n);
57
+ var sqs__namespace = /*#__PURE__*/_interopNamespaceDefault(sqs);
58
+ var lambdaEventSources__namespace = /*#__PURE__*/_interopNamespaceDefault(lambdaEventSources);
59
+ var logs__namespace = /*#__PURE__*/_interopNamespaceDefault(logs);
60
+ var cloudfront__namespace = /*#__PURE__*/_interopNamespaceDefault(cloudfront);
61
+ var origins__namespace = /*#__PURE__*/_interopNamespaceDefault(origins);
62
+ var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
63
+
64
+ const CDK$2 = {
65
+ ACCOUNT: {
66
+ DEVELOPMENT: "development",
67
+ MANAGEMENT: "management",
68
+ OPERATIONS: "operations",
69
+ PRODUCTION: "production",
70
+ SANDBOX: "sandbox",
71
+ SECURITY: "security",
72
+ STAGE: "stage",
73
+ },
74
+ BUILD: {
75
+ CONFIG: {
76
+ ALL: "all",
77
+ API: "api",
78
+ INFRASTRUCTURE: "infrastructure",
79
+ NONE: "none",
80
+ WEB: "web",
81
+ },
82
+ PERSONAL: "personal",
83
+ /**
84
+ * @deprecated rename "ephemeral" to "personal" (since 2/24/2025)
85
+ */
86
+ EPHEMERAL: "ephemeral",
87
+ /**
88
+ * @deprecated as even "ephemeral" builds have static assets (since 7/6/2024)
89
+ */
90
+ STATIC: "static",
91
+ },
92
+ CREATION: {
93
+ CDK: "cdk",
94
+ CLOUDFORMATION_TEMPLATE: "template",
95
+ MANUAL: "manual",
96
+ },
97
+ DATADOG: {
98
+ SITE: "datadoghq.com",
99
+ LAYER: {
100
+ // https://docs.datadoghq.com/serverless/aws_lambda/installation/nodejs/?tab=awscdk
101
+ NODE: 127, // 127 on 9/12/2025
102
+ EXTENSION: 86, // 86 on 9/12/2025
103
+ },
104
+ },
105
+ DEFAULT: {
106
+ REGION: "us-east-1",
107
+ },
108
+ DNS: {
109
+ CONFIG: {
110
+ TTL: 300, // 5 minutes in seconds for Route53
111
+ },
112
+ RECORD: {
113
+ A: "A",
114
+ CNAME: "CNAME",
115
+ MX: "MX",
116
+ NS: "NS",
117
+ TXT: "TXT",
118
+ },
119
+ },
120
+ DURATION: {
121
+ EXPRESS_API: 30,
122
+ LAMBDA_MAXIMUM: 900,
123
+ LAMBDA_WORKER: 900,
124
+ },
125
+ ENV: {
126
+ DEMO: "demo", // Mirror of production
127
+ DEVELOPMENT: "development", // Internal most stable development space
128
+ /** @deprecated */ EPHEMERAL: "ephemeral", // Alias for "build"
129
+ LOCAL: "local",
130
+ /** @deprecated */ MAIN: "main", // Alias for development
131
+ META: "meta", // For non-environment/infrastructure stacks
132
+ PERSONAL: "personal", // Personal builds using resources provided by sandbox
133
+ PREVIEW: "preview", // External next thing to be released
134
+ PRODUCTION: "production",
135
+ RELEASE: "release", // Internal next thing to be released
136
+ REVIEW: "review", // Internal place to collaborate on issues
137
+ SANDBOX: "sandbox", // Internal build space with no guaranteed longevity
138
+ TRAINING: "training", // aka "test"; mirror of production for external audiences
139
+ },
140
+ HOST: {
141
+ APEX: "@",
142
+ },
143
+ IMPORT: {
144
+ DATADOG_LOG_FORWARDER: "account-datadog-forwarder",
145
+ DATADOG_ROLE: "account-datadog-role",
146
+ DATADOG_SECRET: "account-datadog-secret",
147
+ LOG_BUCKET: "account-log-bucket",
148
+ OIDC_PROVIDER: "github-oidc-provider",
149
+ },
150
+ LAMBDA: {
151
+ LOG_RETENTION: 90,
152
+ MEMORY_SIZE: 1024,
153
+ },
154
+ PRINCIPAL: {
155
+ ROUTE53: "route53.amazonaws.com",
156
+ },
157
+ PRINCIPAL_TYPE: {
158
+ GROUP: "GROUP",
159
+ USER: "USER",
160
+ },
161
+ PROJECT: {
162
+ INFRASTRUCTURE: "infrastructure",
163
+ },
164
+ ROLE: {
165
+ API: "api",
166
+ DEPLOY: "deploy",
167
+ HOSTING: "hosting",
168
+ MONITORING: "monitoring",
169
+ NETWORKING: "networking",
170
+ PROCESSING: "processing",
171
+ SECURITY: "security",
172
+ STACK: "stack",
173
+ STORAGE: "storage",
174
+ TOY: "toy",
175
+ },
176
+ SERVICE: {
177
+ DATADOG: "datadog",
178
+ INFRASTRUCTURE: "infrastructure",
179
+ LIBRARIES: "libraries",
180
+ NONE: "none",
181
+ SSO: "sso",
182
+ TRACE: "trace",
183
+ },
184
+ TAG: {
185
+ BUILD_DATE: "buildDate",
186
+ BUILD_HEX: "buildHex",
187
+ BUILD_NUMBER: "buildNumber",
188
+ BUILD_TIME: "buildTime",
189
+ BUILD_TYPE: "buildType",
190
+ COMMIT: "commit",
191
+ CREATION: "creation",
192
+ ENV: "env",
193
+ NONCE: "nonce",
194
+ PROJECT: "project",
195
+ ROLE: "role",
196
+ SERVICE: "service",
197
+ SPONSOR: "sponsor",
198
+ STACK: "stack",
199
+ STACK_SHA: "stackSha",
200
+ VENDOR: "vendor",
201
+ VERSION: "version",
202
+ },
203
+ TARGET_TYPE: {
204
+ AWS_ACCOUNT: "AWS_ACCOUNT",
205
+ },
206
+ VENDOR: {
207
+ ANTHROPIC: "anthropic",
208
+ AUTH0: "auth0",
209
+ DATADOG: "datadog",
210
+ KNOWTRACE: "knowtrace",
211
+ MONGODB: "mongodb",
212
+ OPENAI: "openai",
213
+ SPLINTERLANDS: "splinterlands",
214
+ },
215
+ };
216
+
217
+ class JaypieAccountLoggingBucket extends constructs.Construct {
218
+ /**
219
+ * Create a new account-wide logging S3 bucket with lifecycle policies and export
220
+ */
221
+ constructor(scope, idOrProps, propsOrUndefined) {
222
+ // Handle overloaded constructor signatures
223
+ let props;
224
+ let id;
225
+ if (typeof idOrProps === "string") {
226
+ // First param is ID, second is props
227
+ props = propsOrUndefined || {};
228
+ id = idOrProps;
229
+ }
230
+ else {
231
+ // First param is props
232
+ props = idOrProps || {};
233
+ id = props.id || "AccountLoggingBucket";
234
+ }
235
+ super(scope, id);
236
+ // Generate default bucket name with PROJECT_NONCE
237
+ const defaultBucketName = process.env.PROJECT_NONCE
238
+ ? `account-logging-stack-${process.env.PROJECT_NONCE.toLowerCase()}`
239
+ : "account-logging-stack";
240
+ // Extract Jaypie-specific options
241
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
242
+ 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;
243
+ // Create the bucket with lifecycle rules
244
+ this.bucket = new s3.Bucket(this, "Bucket", {
245
+ accessControl: s3.BucketAccessControl.LOG_DELIVERY_WRITE,
246
+ bucketName,
247
+ lifecycleRules: [
248
+ {
249
+ expiration: cdk__namespace.Duration.days(expirationDays),
250
+ transitions: [
251
+ {
252
+ storageClass: s3.StorageClass.INFREQUENT_ACCESS,
253
+ transitionAfter: cdk__namespace.Duration.days(infrequentAccessTransitionDays),
254
+ },
255
+ {
256
+ storageClass: s3.StorageClass.GLACIER,
257
+ transitionAfter: cdk__namespace.Duration.days(glacierTransitionDays),
258
+ },
259
+ ],
260
+ },
261
+ ],
262
+ ...bucketProps,
263
+ });
264
+ // Add tags
265
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
266
+ if (service) {
267
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
268
+ }
269
+ if (project) {
270
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
271
+ }
272
+ // Create CloudFormation output if enabled
273
+ if (createOutput) {
274
+ new cdk__namespace.CfnOutput(this, "BucketNameOutput", {
275
+ description: outputDescription,
276
+ exportName,
277
+ value: this.bucket.bucketName,
278
+ });
279
+ }
280
+ }
281
+ }
282
+
283
+ function addDatadogLayers(lambdaFunction, options = {}) {
284
+ const datadogApiKeyArn = options?.datadogApiKeyArn;
285
+ const resolvedDatadogApiKeyArn = datadogApiKeyArn ||
286
+ process.env.DATADOG_API_KEY_ARN ||
287
+ process.env.CDK_ENV_DATADOG_API_KEY_ARN;
288
+ if (!resolvedDatadogApiKeyArn) {
289
+ return false;
290
+ }
291
+ // Define Datadog environment variables
292
+ const datadogEnvVars = {
293
+ DD_API_KEY_SECRET_ARN: resolvedDatadogApiKeyArn,
294
+ DD_ENHANCED_METRICS: "true",
295
+ DD_ENV: process.env.PROJECT_ENV || "",
296
+ DD_PROFILING_ENABLED: "false",
297
+ DD_SERVERLESS_APPSEC_ENABLED: "false",
298
+ DD_SERVICE: process.env.PROJECT_SERVICE || "",
299
+ DD_SITE: CDK$2.DATADOG.SITE,
300
+ DD_TAGS: `${CDK$2.TAG.SPONSOR}:${process.env.PROJECT_SPONSOR || ""}`,
301
+ DD_TRACE_OTEL_ENABLED: "false",
302
+ };
303
+ // Add environment variables only if they don't already exist
304
+ Object.entries(datadogEnvVars).forEach(([key, value]) => {
305
+ lambdaFunction.addEnvironment(key, value);
306
+ });
307
+ const datadogApiKeySecret = secretsmanager__namespace.Secret.fromSecretCompleteArn(lambdaFunction, "DatadogApiKey", resolvedDatadogApiKeyArn);
308
+ const datadogLambda = new datadogCdkConstructsV2.DatadogLambda(lambdaFunction, "DatadogLambda", {
309
+ apiKeySecret: datadogApiKeySecret, // apiKeySecret auto-grants secret access to the added lambdas
310
+ nodeLayerVersion: CDK$2.DATADOG.LAYER.NODE,
311
+ extensionLayerVersion: CDK$2.DATADOG.LAYER.EXTENSION,
312
+ env: process.env.PROJECT_ENV,
313
+ service: process.env.PROJECT_SERVICE,
314
+ version: process.env.PROJECT_VERSION,
315
+ });
316
+ datadogLambda.addLambdaFunctions([lambdaFunction]);
317
+ return true;
318
+ }
319
+
320
+ function constructEnvName(name, opts) {
321
+ const env = opts?.env ?? process.env.PROJECT_ENV ?? "build";
322
+ const key = opts?.key ?? process.env.PROJECT_KEY ?? "project";
323
+ const nonce = opts?.nonce ?? process.env.PROJECT_NONCE ?? "cfe2"; // This default is intentionally short. It is not a special value but should not be changed.
324
+ return `${env}-${key}-${name}-${nonce}`;
325
+ }
326
+
327
+ function constructStackName(key) {
328
+ if (!key) {
329
+ return `cdk-${process.env.PROJECT_SPONSOR}-${process.env.PROJECT_KEY}-${process.env.PROJECT_ENV}-${process.env.PROJECT_NONCE}`;
330
+ }
331
+ else {
332
+ return `cdk-${process.env.PROJECT_SPONSOR}-${process.env.PROJECT_KEY}-${process.env.PROJECT_ENV}-${process.env.PROJECT_NONCE}-${key}`;
333
+ }
334
+ }
335
+
336
+ const CDK$1 = {
337
+ CREATION: {
338
+ CDK: "cdk",
339
+ },
340
+ ROLE: {
341
+ STACK: "stack",
342
+ },
343
+ TAG: {
344
+ BUILD_DATE: "buildDate",
345
+ BUILD_HEX: "buildHex",
346
+ BUILD_TIME: "buildTime",
347
+ COMMIT: "commit",
348
+ CREATION: "creation",
349
+ ENV: "env",
350
+ NONCE: "nonce",
351
+ PROJECT: "project",
352
+ ROLE: "role",
353
+ SERVICE: "service",
354
+ SPONSOR: "sponsor",
355
+ STACK: "stack",
356
+ VERSION: "version",
357
+ },
358
+ };
359
+ function constructTagger(construct, { name } = {}) {
360
+ const stackName = name || constructStackName();
361
+ const version = process.env.npm_package_version || process.env.PROJECT_VERSION || null;
362
+ if (process.env.PROJECT_COMMIT && process.env.PROJECT_COMMIT.length > 8) {
363
+ cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_HEX, process.env.PROJECT_COMMIT.slice(0, 8));
364
+ }
365
+ cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_DATE, new Date().toISOString());
366
+ cdk.Tags.of(construct).add(CDK$1.TAG.BUILD_TIME, Date.now().toString());
367
+ if (process.env.PROJECT_COMMIT)
368
+ cdk.Tags.of(construct).add(CDK$1.TAG.COMMIT, process.env.PROJECT_COMMIT);
369
+ cdk.Tags.of(construct).add(CDK$1.TAG.CREATION, CDK$1.CREATION.CDK);
370
+ if (process.env.PROJECT_ENV)
371
+ cdk.Tags.of(construct).add(CDK$1.TAG.ENV, process.env.PROJECT_ENV);
372
+ if (process.env.PROJECT_NONCE)
373
+ cdk.Tags.of(construct).add(CDK$1.TAG.NONCE, process.env.PROJECT_NONCE);
374
+ if (process.env.PROJECT_KEY)
375
+ cdk.Tags.of(construct).add(CDK$1.TAG.PROJECT, process.env.PROJECT_KEY);
376
+ cdk.Tags.of(construct).add(CDK$1.TAG.ROLE, CDK$1.ROLE.STACK);
377
+ if (process.env.PROJECT_SERVICE)
378
+ cdk.Tags.of(construct).add(CDK$1.TAG.SERVICE, process.env.PROJECT_SERVICE);
379
+ if (process.env.PROJECT_SPONSOR)
380
+ cdk.Tags.of(construct).add(CDK$1.TAG.SPONSOR, process.env.PROJECT_SPONSOR);
381
+ if (stackName)
382
+ cdk.Tags.of(construct).add(CDK$1.TAG.STACK, stackName);
383
+ if (version)
384
+ cdk.Tags.of(construct).add(CDK$1.TAG.VERSION, version);
385
+ return true;
386
+ }
387
+
388
+ function envHostname({ component, domain, env, subdomain, } = {}) {
389
+ const resolvedDomain = domain || process.env.CDK_ENV_DOMAIN || process.env.CDK_ENV_HOSTED_ZONE;
390
+ if (!resolvedDomain) {
391
+ throw new errors.ConfigurationError("No hostname `domain` provided. Set CDK_ENV_DOMAIN or CDK_ENV_HOSTED_ZONE to use environment domain");
392
+ }
393
+ const resolvedComponent = component === "@" || component === "" ? undefined : component;
394
+ const resolvedSubdomain = subdomain || process.env.CDK_ENV_SUBDOMAIN;
395
+ const resolvedEnv = env || process.env.PROJECT_ENV;
396
+ const filteredEnv = resolvedEnv === CDK$2.ENV.PRODUCTION ? undefined : resolvedEnv;
397
+ const parts = [
398
+ resolvedComponent,
399
+ resolvedSubdomain,
400
+ filteredEnv,
401
+ resolvedDomain,
402
+ ].filter((part) => part);
403
+ return parts.join(".");
404
+ }
405
+
406
+ /**
407
+ * Extends the Datadog IAM role with additional permissions
408
+ *
409
+ * Checks for CDK_ENV_DATADOG_ROLE_ARN environment variable.
410
+ * If found, creates a custom policy with:
411
+ * - budgets:ViewBudget
412
+ * - logs:DescribeLogGroups
413
+ *
414
+ * @param scope - The construct scope
415
+ * @param options - Configuration options
416
+ * @returns The created Policy, or undefined if CDK_ENV_DATADOG_ROLE_ARN is not set
417
+ */
418
+ function extendDatadogRole(scope, options) {
419
+ const datadogRoleArn = process.env.CDK_ENV_DATADOG_ROLE_ARN;
420
+ // Early return if no Datadog role ARN is configured
421
+ if (!datadogRoleArn) {
422
+ return undefined;
423
+ }
424
+ const { id = "DatadogCustomPolicy", project, service = CDK$2.SERVICE.DATADOG, } = options || {};
425
+ // Lookup the Datadog role
426
+ const datadogRole = awsIam.Role.fromRoleArn(scope, "DatadogRole", datadogRoleArn);
427
+ // Build policy statements
428
+ const statements = [
429
+ // Allow view budget
430
+ new awsIam.PolicyStatement({
431
+ actions: ["budgets:ViewBudget"],
432
+ resources: ["*"],
433
+ }),
434
+ // Allow describe log groups
435
+ new awsIam.PolicyStatement({
436
+ actions: ["logs:DescribeLogGroups"],
437
+ resources: ["*"],
438
+ }),
439
+ ];
440
+ // Create the custom policy
441
+ const datadogCustomPolicy = new awsIam.Policy(scope, id, {
442
+ roles: [datadogRole],
443
+ statements,
444
+ });
445
+ // Add tags
446
+ cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.SERVICE, service);
447
+ cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
448
+ cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
449
+ if (project) {
450
+ cdk__namespace.Tags.of(datadogCustomPolicy).add(CDK$2.TAG.PROJECT, project);
451
+ }
452
+ return datadogCustomPolicy;
453
+ }
454
+
455
+ /**
456
+ * Check if the current environment matches the given environment
457
+ */
458
+ function isEnv(env) {
459
+ return process.env.PROJECT_ENV === env;
460
+ }
461
+ /**
462
+ * Check if the current environment is production
463
+ */
464
+ function isProductionEnv() {
465
+ return isEnv(CDK$2.ENV.PRODUCTION);
466
+ }
467
+ /**
468
+ * Check if the current environment is sandbox
469
+ */
470
+ function isSandboxEnv() {
471
+ return isEnv(CDK$2.ENV.SANDBOX);
472
+ }
473
+
474
+ // In short the RFC 1035 standard for valid hostnames is:
475
+ // 1. Must be less than 253 characters
476
+ // 2. Must start with a letter
477
+ // 3. Must end with a letter or number
478
+ // 4. Can only contain letters, numbers, and hyphens
479
+ // 5. Last part of the domain must be at least 2 characters
480
+ function validPart$1(part) {
481
+ if (!part.match(/^[a-z]/))
482
+ return false;
483
+ if (!part.match(/[a-z0-9]$/))
484
+ return false;
485
+ return /^[a-zA-Z0-9-]+$/.test(part);
486
+ }
487
+ function isValidHostname$1(hostname) {
488
+ // Check hostname is a string
489
+ if (typeof hostname !== "string")
490
+ return false;
491
+ // Convert hostname to lowercase
492
+ const check = hostname.toString().toLowerCase();
493
+ // Check hostname is less than 253 characters
494
+ if (check.length > 253)
495
+ return false;
496
+ // Split on dots
497
+ const parts = check.split(".");
498
+ // Check each part is validPart
499
+ const validParts = parts.map(validPart$1);
500
+ // Confirm all parts are valid
501
+ if (!validParts.every((part) => part))
502
+ return false;
503
+ // Confirm last part is at least 2 characters
504
+ const lastPart = parts[parts.length - 1];
505
+ if (lastPart.length < 2)
506
+ return false;
507
+ // Confirm last part is all letters
508
+ if (!lastPart.match(/^[a-z]+$/))
509
+ return false;
510
+ // This is a valid hostname
511
+ return true;
512
+ }
513
+
514
+ function validPart(part) {
515
+ if (!part.match(/^[a-z]/))
516
+ return false;
517
+ if (!part.match(/[a-z0-9]$/))
518
+ return false;
519
+ return /^[a-zA-Z0-9-]+$/.test(part);
520
+ }
521
+ function isValidSubdomain(subdomain) {
522
+ // Check subdomain is a string
523
+ if (typeof subdomain !== "string")
524
+ return false;
525
+ // Special case for apex
526
+ if (subdomain === CDK$2.HOST.APEX)
527
+ return true;
528
+ // Convert subdomain to lowercase
529
+ const check = subdomain.toString().toLowerCase();
530
+ // Check subdomain is less than 250 characters
531
+ // We use 250 instead of 253 because we need to leave room for the dot top-level domain
532
+ if (check.length > 250)
533
+ return false;
534
+ // Split on dots
535
+ const parts = check.split(".");
536
+ // Check each part is validPart
537
+ const validParts = parts.map(validPart);
538
+ // Confirm all parts are valid
539
+ if (!validParts.every((part) => part))
540
+ return false;
541
+ // Do not care if last part is at least 2 characters
542
+ // Do not care if last part is all letters
543
+ // This is a valid subdomain
544
+ return true;
545
+ }
546
+
547
+ function jaypieLambdaEnv(options = {}) {
548
+ const { initialEnvironment = {} } = options;
549
+ // Start with empty environment - we'll only add valid values
550
+ let environment = {};
551
+ // First, add all valid string values from initialEnvironment
552
+ Object.entries(initialEnvironment).forEach(([key, value]) => {
553
+ if (typeof value === "string") {
554
+ environment[key] = value;
555
+ }
556
+ });
557
+ // Default environment values
558
+ const defaultEnvValues = {
559
+ AWS_LAMBDA_NODEJS_DISABLE_CALLBACK_WARNING: "true",
560
+ };
561
+ // Apply default environment values with user overrides
562
+ Object.entries(defaultEnvValues).forEach(([key, defaultValue]) => {
563
+ if (key in initialEnvironment) {
564
+ const userValue = initialEnvironment[key];
565
+ // If user passes a string, it's already added above
566
+ // If user passes non-string falsy value, omit the key
567
+ if (!userValue) {
568
+ delete environment[key];
569
+ }
570
+ // Ignore non-string truthy values (key not added)
571
+ }
572
+ else {
573
+ // No user override, use default value
574
+ environment[key] = defaultValue;
575
+ }
576
+ });
577
+ // Default environment variables from process.env if present
578
+ const defaultEnvVars = [
579
+ "DATADOG_API_KEY_ARN",
580
+ "LOG_LEVEL",
581
+ "MODULE_LOGGER",
582
+ "MODULE_LOG_LEVEL",
583
+ "PROJECT_CHAOS",
584
+ "PROJECT_COMMIT",
585
+ "PROJECT_ENV",
586
+ "PROJECT_KEY",
587
+ "PROJECT_SECRET",
588
+ "PROJECT_SERVICE",
589
+ "PROJECT_SPONSOR",
590
+ "PROJECT_VERSION",
591
+ ];
592
+ // Add default environment variables if they exist in process.env
593
+ defaultEnvVars.forEach((envVar) => {
594
+ if (process.env[envVar] && !environment[envVar]) {
595
+ environment[envVar] = process.env[envVar];
596
+ }
597
+ });
598
+ return environment;
599
+ }
600
+
601
+ function mergeDomain(subDomain, hostedZone) {
602
+ if (!hostedZone) {
603
+ throw new errors.ConfigurationError("hostedZone is required");
604
+ }
605
+ if (!subDomain) {
606
+ // Return hostedZone if subDomain is not passed
607
+ // Pass CDK.HOST.APEX to explicitly indicate apex domain
608
+ return hostedZone;
609
+ }
610
+ if (subDomain === CDK$2.HOST.APEX) {
611
+ return hostedZone;
612
+ }
613
+ return `${subDomain}.${hostedZone}`;
614
+ }
615
+
616
+ const DEFAULT_FUNCTION_NAME$1 = "DatadogForwarderFunction";
617
+ // Cache to store resolved functions
618
+ // Using nested structure to support multiple functions per scope with automatic GC
619
+ const functionCache = new WeakMap();
620
+ function resolveDatadogForwarderFunction(scope, options) {
621
+ const { import: importValue, name } = options || {};
622
+ const functionName = name || DEFAULT_FUNCTION_NAME$1;
623
+ const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
624
+ // Create a cache key based on name and import
625
+ const cacheKey = `${functionName}:${importKey}`;
626
+ // Get or create scope cache
627
+ let scopeCache = functionCache.get(scope);
628
+ if (!scopeCache) {
629
+ scopeCache = new Map();
630
+ functionCache.set(scope, scopeCache);
631
+ }
632
+ // Return cached function if it exists
633
+ const cachedFunction = scopeCache.get(cacheKey);
634
+ if (cachedFunction) {
635
+ return cachedFunction;
636
+ }
637
+ // Create and cache the function
638
+ const func = lambda__namespace.Function.fromFunctionArn(scope, functionName, cdk__namespace.Fn.importValue(importKey));
639
+ scopeCache.set(cacheKey, func);
640
+ return func;
641
+ }
642
+
643
+ function resolveDatadogLayers(scope, options = {}) {
644
+ const { datadogApiKeyArn, uniqueId } = options;
645
+ let resolvedRegion = cdk.Stack.of(scope).region || "us-east-1";
646
+ // Resolve the Datadog API key ARN from multiple sources
647
+ const resolvedDatadogApiKeyArn = datadogApiKeyArn ||
648
+ process.env.DATADOG_API_KEY_ARN ||
649
+ process.env.CDK_ENV_DATADOG_API_KEY_ARN;
650
+ // Return null if no API key is found
651
+ if (!resolvedDatadogApiKeyArn) {
652
+ return undefined;
653
+ }
654
+ const layerIdSuffix = uniqueId || process.env.PROJECT_NONCE || Date.now().toString();
655
+ // Create Datadog Node.js layer
656
+ const datadogNodeLayer = lambda__namespace.LayerVersion.fromLayerVersionArn(scope, `DatadogNodeLayer-${layerIdSuffix}`, `arn:aws:lambda:${resolvedRegion}:464622532012:layer:Datadog-Node20-x:${CDK$2.DATADOG.LAYER.NODE}`);
657
+ // Create Datadog Extension layer
658
+ const datadogExtensionLayer = lambda__namespace.LayerVersion.fromLayerVersionArn(scope, `DatadogExtensionLayer-${layerIdSuffix}`, `arn:aws:lambda:${resolvedRegion}:464622532012:layer:Datadog-Extension:${CDK$2.DATADOG.LAYER.EXTENSION}`);
659
+ return [datadogNodeLayer, datadogExtensionLayer];
660
+ }
661
+
662
+ const DEFAULT_FUNCTION_NAME = "DatadogForwarderFunction";
663
+ // Cache to store resolved logging destinations
664
+ // Using nested structure to support multiple destinations per scope with automatic GC
665
+ const destinationCache = new WeakMap();
666
+ function resolveDatadogLoggingDestination(scope, options) {
667
+ const { import: importValue, name } = options || {};
668
+ // Create a cache key based on name and import (same as forwarder function)
669
+ const functionName = name || DEFAULT_FUNCTION_NAME;
670
+ const importKey = importValue || CDK$2.IMPORT.DATADOG_LOG_FORWARDER;
671
+ const cacheKey = `${functionName}:${importKey}`;
672
+ // Get or create scope cache
673
+ let scopeCache = destinationCache.get(scope);
674
+ if (!scopeCache) {
675
+ scopeCache = new Map();
676
+ destinationCache.set(scope, scopeCache);
677
+ }
678
+ // Return cached destination if it exists
679
+ const cachedDestination = scopeCache.get(cacheKey);
680
+ if (cachedDestination) {
681
+ return cachedDestination;
682
+ }
683
+ // Resolve the Datadog forwarder function
684
+ const datadogForwarderFunction = resolveDatadogForwarderFunction(scope, options);
685
+ // Create and cache the logging destination
686
+ const datadogLoggingDestination = new logDestinations__namespace.LambdaDestination(datadogForwarderFunction);
687
+ scopeCache.set(cacheKey, datadogLoggingDestination);
688
+ return datadogLoggingDestination;
689
+ }
690
+
691
+ /**
692
+ * Resolves environment input to a plain object.
693
+ *
694
+ * When environment is an object (legacy syntax), returns it as-is.
695
+ * When environment is an array:
696
+ * - Strings are treated as keys to lookup in process.env
697
+ * - Objects have their key-value pairs merged in
698
+ *
699
+ * @example
700
+ * // Legacy object syntax
701
+ * resolveEnvironment({ FOO: "bar" })
702
+ * // => { FOO: "bar" }
703
+ *
704
+ * @example
705
+ * // Array syntax with process.env lookup
706
+ * // Given process.env.MY_VAR = "hello"
707
+ * resolveEnvironment(["MY_VAR"])
708
+ * // => { MY_VAR: "hello" }
709
+ *
710
+ * @example
711
+ * // Array syntax with objects
712
+ * resolveEnvironment([{ FOO: "bar", BAZ: "qux" }])
713
+ * // => { FOO: "bar", BAZ: "qux" }
714
+ *
715
+ * @example
716
+ * // Mixed array syntax
717
+ * // Given process.env.MY_VAR = "hello"
718
+ * resolveEnvironment(["MY_VAR", { FOO: "bar" }])
719
+ * // => { MY_VAR: "hello", FOO: "bar" }
720
+ */
721
+ function resolveEnvironment(environment, env = process.env) {
722
+ if (!environment) {
723
+ return {};
724
+ }
725
+ // Legacy object syntax - return as-is
726
+ if (!Array.isArray(environment)) {
727
+ return environment;
728
+ }
729
+ // Array syntax - process each item
730
+ return environment.reduce((acc, item) => {
731
+ if (typeof item === "string") {
732
+ // String: lookup in process.env
733
+ const value = env[item];
734
+ if (value !== undefined) {
735
+ return {
736
+ ...acc,
737
+ [item]: value,
738
+ };
739
+ }
740
+ // Skip if not found in process.env
741
+ return acc;
742
+ }
743
+ // Object: merge key-value pairs
744
+ return {
745
+ ...acc,
746
+ ...item,
747
+ };
748
+ }, {});
749
+ }
750
+
751
+ function resolveHostedZone(scope, { name = "HostedZone", zone = process.env.CDK_ENV_HOSTED_ZONE, } = {}) {
752
+ if (!zone) {
753
+ throw new errors.ConfigurationError("No `zone` provided. Set CDK_ENV_HOSTED_ZONE to use environment zone");
754
+ }
755
+ if (typeof zone === "string") {
756
+ return route53__namespace.HostedZone.fromLookup(scope, name, {
757
+ domainName: zone,
758
+ });
759
+ }
760
+ return zone;
761
+ }
762
+
763
+ const resolveParamsAndSecrets = ({ paramsAndSecrets, options, } = {}) => {
764
+ if (paramsAndSecrets === false) {
765
+ return;
766
+ }
767
+ let resolvedParamsAndSecrets;
768
+ if (paramsAndSecrets instanceof lambda__namespace.ParamsAndSecretsLayerVersion) {
769
+ resolvedParamsAndSecrets = paramsAndSecrets;
770
+ }
771
+ else {
772
+ const resolvedOptions = options || {};
773
+ resolvedParamsAndSecrets = lambda__namespace.ParamsAndSecretsLayerVersion.fromVersion(lambda__namespace.ParamsAndSecretsVersions.V1_0_103, {
774
+ cacheSize: resolvedOptions.cacheSize,
775
+ logLevel: resolvedOptions.logLevel || lambda__namespace.ParamsAndSecretsLogLevel.WARN,
776
+ parameterStoreTtl: resolvedOptions.parameterStoreTtl,
777
+ secretsManagerTtl: resolvedOptions.secretsManagerTtl,
778
+ });
779
+ }
780
+ return resolvedParamsAndSecrets;
781
+ };
782
+
783
+ // It is a consumer if the environment is ephemeral
784
+ function checkEnvIsConsumer(env = process.env) {
785
+ return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
786
+ !!env.CDK_ENV_PERSONAL ||
787
+ /** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
788
+ /** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
789
+ }
790
+ function checkEnvIsProvider(env = process.env) {
791
+ return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
792
+ }
793
+ function cleanName(name) {
794
+ return name.replace(/[^a-zA-Z0-9:-]/g, "");
795
+ }
796
+ function exportEnvName(name, env = process.env) {
797
+ let rawName;
798
+ if (checkEnvIsProvider(env)) {
799
+ rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
800
+ // Clean the entire name to only allow alphanumeric, colons, and hyphens
801
+ return cleanName(rawName);
802
+ }
803
+ else {
804
+ if (checkEnvIsConsumer(env)) {
805
+ rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
806
+ }
807
+ else {
808
+ rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
809
+ }
810
+ }
811
+ return cleanName(rawName);
812
+ }
813
+ class JaypieEnvSecret extends constructs.Construct {
814
+ constructor(scope, idOrEnvKey, props) {
815
+ // Check if idOrEnvKey should be treated as envKey:
816
+ // - No props provided OR props.envKey is not set
817
+ // - AND idOrEnvKey exists as a non-empty string in process.env
818
+ const treatAsEnvKey = (!props || props.envKey === undefined) &&
819
+ typeof process.env[idOrEnvKey] === "string" &&
820
+ process.env[idOrEnvKey] !== "";
821
+ const id = treatAsEnvKey ? `EnvSecret_${idOrEnvKey}` : idOrEnvKey;
822
+ super(scope, id);
823
+ const { consumer = checkEnvIsConsumer(), envKey: envKeyProp, export: exportParam, generateSecretString, provider = checkEnvIsProvider(), roleTag, vendorTag, value, } = props || {};
824
+ const envKey = treatAsEnvKey ? idOrEnvKey : envKeyProp;
825
+ this._envKey = envKey;
826
+ let exportName;
827
+ if (!exportParam) {
828
+ exportName = exportEnvName(id);
829
+ }
830
+ else {
831
+ exportName = cleanName(exportParam);
832
+ }
833
+ if (consumer) {
834
+ const secretName = cdk.Fn.importValue(exportName);
835
+ this._secret = secretsmanager__namespace.Secret.fromSecretNameV2(this, id, secretName);
836
+ // Add CfnOutput for consumer secrets
837
+ new cdk.CfnOutput(this, `ConsumedName`, {
838
+ value: this._secret.secretName,
839
+ });
840
+ }
841
+ else {
842
+ const secretValue = envKey && process.env[envKey] ? process.env[envKey] : value;
843
+ const secretProps = {
844
+ generateSecretString,
845
+ secretStringValue: !generateSecretString && secretValue
846
+ ? cdk.SecretValue.unsafePlainText(secretValue)
847
+ : undefined,
848
+ };
849
+ this._secret = new secretsmanager__namespace.Secret(this, id, secretProps);
850
+ if (roleTag) {
851
+ cdk.Tags.of(this._secret).add(CDK$2.TAG.ROLE, roleTag);
852
+ }
853
+ if (vendorTag) {
854
+ cdk.Tags.of(this._secret).add(CDK$2.TAG.VENDOR, vendorTag);
855
+ }
856
+ if (provider) {
857
+ new cdk.CfnOutput(this, `ProvidedName`, {
858
+ value: this._secret.secretName,
859
+ exportName,
860
+ });
861
+ }
862
+ else {
863
+ new cdk.CfnOutput(this, `CreatedName`, {
864
+ value: this._secret.secretName,
865
+ });
866
+ }
867
+ }
868
+ }
869
+ // IResource implementation
870
+ get stack() {
871
+ return cdk.Stack.of(this);
872
+ }
873
+ get env() {
874
+ return {
875
+ account: cdk.Stack.of(this).account,
876
+ region: cdk.Stack.of(this).region,
877
+ };
878
+ }
879
+ applyRemovalPolicy(policy) {
880
+ this._secret.applyRemovalPolicy(policy);
881
+ }
882
+ // ISecret implementation
883
+ get secretArn() {
884
+ return this._secret.secretArn;
885
+ }
886
+ get secretName() {
887
+ return this._secret.secretName;
888
+ }
889
+ get secretFullArn() {
890
+ return this._secret.secretFullArn;
891
+ }
892
+ get encryptionKey() {
893
+ return this._secret.encryptionKey;
894
+ }
895
+ get secretValue() {
896
+ return this._secret.secretValue;
897
+ }
898
+ secretValueFromJson(key) {
899
+ return this._secret.secretValueFromJson(key);
900
+ }
901
+ grantRead(grantee, versionStages) {
902
+ return this._secret.grantRead(grantee, versionStages);
903
+ }
904
+ grantWrite(grantee) {
905
+ return this._secret.grantWrite(grantee);
906
+ }
907
+ addRotationSchedule(id, options) {
908
+ return this._secret.addRotationSchedule(id, options);
909
+ }
910
+ addToResourcePolicy(statement) {
911
+ return this._secret.addToResourcePolicy(statement);
912
+ }
913
+ denyAccountRootDelete() {
914
+ this._secret.denyAccountRootDelete();
915
+ }
916
+ attach(target) {
917
+ return this._secret.attach(target);
918
+ }
919
+ cfnDynamicReferenceKey(options) {
920
+ return this._secret.cfnDynamicReferenceKey(options);
921
+ }
922
+ get envKey() {
923
+ return this._envKey;
924
+ }
925
+ }
926
+
927
+ /**
928
+ * Cache for secrets by scope to avoid creating duplicates.
929
+ * Uses WeakMap to allow garbage collection when scopes are no longer referenced.
930
+ */
931
+ const secretsByScope = new WeakMap();
932
+ /**
933
+ * Gets or creates the secrets cache for a given scope.
934
+ */
935
+ function getSecretsCache(scope) {
936
+ let cache = secretsByScope.get(scope);
937
+ if (!cache) {
938
+ cache = new Map();
939
+ secretsByScope.set(scope, cache);
940
+ }
941
+ return cache;
942
+ }
943
+ /**
944
+ * Gets an existing secret from the cache or creates a new one.
945
+ * This ensures that multiple constructs within the same scope share secrets.
946
+ */
947
+ function getOrCreateSecret(scope, envKey, props) {
948
+ const cache = getSecretsCache(scope);
949
+ const existingSecret = cache.get(envKey);
950
+ if (existingSecret) {
951
+ return existingSecret;
952
+ }
953
+ // Create new secret - JaypieEnvSecret's smart constructor handles envKey detection
954
+ const secret = new JaypieEnvSecret(scope, envKey, {
955
+ ...props,
956
+ envKey,
957
+ });
958
+ cache.set(envKey, secret);
959
+ return secret;
960
+ }
961
+ /**
962
+ * Resolves secrets input to an array of JaypieEnvSecret instances.
963
+ *
964
+ * When an item is already a JaypieEnvSecret, it's passed through as-is.
965
+ * When an item is a string, a JaypieEnvSecret is created (or reused from cache)
966
+ * with the string as the envKey.
967
+ *
968
+ * Secrets are cached per scope to avoid creating duplicate secrets when
969
+ * multiple constructs in the same scope reference the same secret.
970
+ *
971
+ * @example
972
+ * // JaypieEnvSecret instances pass through
973
+ * const secret = new JaypieEnvSecret(scope, "MySecret", { envKey: "MY_KEY" });
974
+ * resolveSecrets(scope, [secret])
975
+ * // => [secret]
976
+ *
977
+ * @example
978
+ * // Strings create JaypieEnvSecret instances
979
+ * resolveSecrets(scope, ["AUTH0_SECRET", "MONGODB_URI"])
980
+ * // => [JaypieEnvSecret(envKey: "AUTH0_SECRET"), JaypieEnvSecret(envKey: "MONGODB_URI")]
981
+ *
982
+ * @example
983
+ * // Mixed input
984
+ * const existingSecret = new JaypieEnvSecret(scope, "Existing", { envKey: "EXISTING" });
985
+ * resolveSecrets(scope, [existingSecret, "NEW_SECRET"])
986
+ * // => [existingSecret, JaypieEnvSecret(envKey: "NEW_SECRET")]
987
+ *
988
+ * @example
989
+ * // Secrets are shared across calls with the same scope
990
+ * const secrets1 = resolveSecrets(scope, ["SHARED_SECRET"]);
991
+ * const secrets2 = resolveSecrets(scope, ["SHARED_SECRET"]);
992
+ * // secrets1[0] === secrets2[0] (same instance)
993
+ */
994
+ function resolveSecrets(scope, secrets) {
995
+ if (!secrets || secrets.length === 0) {
996
+ return [];
997
+ }
998
+ return secrets.map((item) => {
999
+ if (typeof item === "string") {
1000
+ return getOrCreateSecret(scope, item);
1001
+ }
1002
+ // Already a JaypieEnvSecret instance
1003
+ return item;
1004
+ });
1005
+ }
1006
+ /**
1007
+ * Clears the secrets cache for a given scope.
1008
+ * Primarily useful for testing.
1009
+ */
1010
+ function clearSecretsCache(scope) {
1011
+ secretsByScope.delete(scope);
1012
+ }
1013
+ /**
1014
+ * Clears all secrets caches.
1015
+ * Primarily useful for testing.
1016
+ */
1017
+ function clearAllSecretsCaches() {
1018
+ // WeakMap doesn't have a clear() method, so we create a new one
1019
+ // This relies on the module being reloaded or the function being called
1020
+ // between test runs. For testing, use clearSecretsCache(scope) instead.
1021
+ }
1022
+
1023
+ class JaypieApiGateway extends constructs.Construct {
1024
+ constructor(scope, id, props) {
1025
+ super(scope, id);
1026
+ const { certificate = true, handler, host: propsHost, name, roleTag = CDK$2.ROLE.API, zone: propsZone, } = props;
1027
+ // Determine zone from props or environment
1028
+ let zone = propsZone;
1029
+ if (!zone && process.env.CDK_ENV_API_HOSTED_ZONE) {
1030
+ zone = process.env.CDK_ENV_API_HOSTED_ZONE;
1031
+ }
1032
+ // Determine host from props or environment
1033
+ let host = propsHost;
1034
+ if (!host) {
1035
+ if (process.env.CDK_ENV_API_HOST_NAME) {
1036
+ host = process.env.CDK_ENV_API_HOST_NAME;
1037
+ }
1038
+ else if (process.env.CDK_ENV_API_SUBDOMAIN &&
1039
+ process.env.CDK_ENV_API_HOSTED_ZONE) {
1040
+ host = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE);
1041
+ }
1042
+ }
1043
+ const apiGatewayName = name || constructEnvName("ApiGateway");
1044
+ const certificateName = constructEnvName("Certificate");
1045
+ const apiDomainName = constructEnvName("ApiDomainName");
1046
+ let hostedZone;
1047
+ let certificateToUse;
1048
+ if (host && zone) {
1049
+ hostedZone = resolveHostedZone(this, { zone });
1050
+ if (certificate === true) {
1051
+ certificateToUse = new acm__namespace.Certificate(this, certificateName, {
1052
+ domainName: host,
1053
+ validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
1054
+ });
1055
+ cdk.Tags.of(certificateToUse).add(CDK$2.TAG.ROLE, CDK$2.ROLE.HOSTING);
1056
+ }
1057
+ else if (typeof certificate === "object") {
1058
+ certificateToUse = certificate;
1059
+ }
1060
+ this._certificate = certificateToUse;
1061
+ this._host = host;
1062
+ }
1063
+ const {
1064
+ // * `...lambdaRestApiProps` cannot be moved to the first const destructuring because it needs to exclude the custom properties first.
1065
+ // Ignore the variables we already assigned to other properties
1066
+ /* eslint-disable @typescript-eslint/no-unused-vars */
1067
+ certificate: _certificate, host: _host, name: _name, roleTag: _roleTag, zone: _zone, handler: _handler,
1068
+ /* eslint-enable @typescript-eslint/no-unused-vars */
1069
+ ...lambdaRestApiProps } = props;
1070
+ this._api = new apiGateway__namespace.LambdaRestApi(this, apiGatewayName, {
1071
+ handler,
1072
+ ...lambdaRestApiProps,
1073
+ });
1074
+ cdk.Tags.of(this._api).add(CDK$2.TAG.ROLE, roleTag);
1075
+ if (host && certificateToUse && hostedZone) {
1076
+ this._domainName = this._api.addDomainName(apiDomainName, {
1077
+ domainName: host,
1078
+ certificate: certificateToUse,
1079
+ });
1080
+ cdk.Tags.of(this._domainName).add(CDK$2.TAG.ROLE, roleTag);
1081
+ const record = new route53__namespace.ARecord(this, "AliasRecord", {
1082
+ recordName: host,
1083
+ target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.ApiGatewayDomain(this._domainName)),
1084
+ zone: hostedZone,
1085
+ });
1086
+ cdk.Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
1087
+ }
1088
+ }
1089
+ get api() {
1090
+ return this._api;
1091
+ }
1092
+ get url() {
1093
+ return this._api.url;
1094
+ }
1095
+ get certificateArn() {
1096
+ return this._certificate?.certificateArn;
1097
+ }
1098
+ get domainName() {
1099
+ return this._domainName?.domainName;
1100
+ }
1101
+ get host() {
1102
+ return this._host;
1103
+ }
1104
+ get restApiId() {
1105
+ return this._api.restApiId;
1106
+ }
1107
+ get restApiName() {
1108
+ return this._api.restApiName;
1109
+ }
1110
+ get restApiRootResourceId() {
1111
+ return this._api.restApiRootResourceId;
1112
+ }
1113
+ get deploymentStage() {
1114
+ return this._api.deploymentStage;
1115
+ }
1116
+ get domainNameAliasDomainName() {
1117
+ return this._domainName?.domainNameAliasDomainName;
1118
+ }
1119
+ get domainNameAliasHostedZoneId() {
1120
+ return this._domainName?.domainNameAliasHostedZoneId;
1121
+ }
1122
+ get root() {
1123
+ return this._api.root;
1124
+ }
1125
+ get env() {
1126
+ return {
1127
+ account: cdk.Stack.of(this).account,
1128
+ region: cdk.Stack.of(this).region,
1129
+ };
1130
+ }
1131
+ get stack() {
1132
+ return this._api.stack;
1133
+ }
1134
+ arnForExecuteApi(method, path, stage) {
1135
+ return this._api.arnForExecuteApi(method, path, stage);
1136
+ }
1137
+ metric(metricName, props) {
1138
+ return this._api.metric(metricName, props);
1139
+ }
1140
+ metricCacheHitCount(props) {
1141
+ return this._api.metricCacheHitCount(props);
1142
+ }
1143
+ metricCacheMissCount(props) {
1144
+ return this._api.metricCacheMissCount(props);
1145
+ }
1146
+ metricClientError(props) {
1147
+ return this._api.metricClientError(props);
1148
+ }
1149
+ metricCount(props) {
1150
+ return this._api.metricCount(props);
1151
+ }
1152
+ metricIntegrationLatency(props) {
1153
+ return this._api.metricIntegrationLatency(props);
1154
+ }
1155
+ metricLatency(props) {
1156
+ return this._api.metricLatency(props);
1157
+ }
1158
+ metricServerError(props) {
1159
+ return this._api.metricServerError(props);
1160
+ }
1161
+ applyRemovalPolicy(policy) {
1162
+ this._api.applyRemovalPolicy(policy);
1163
+ }
1164
+ get restApiRef() {
1165
+ return {
1166
+ restApiId: this._api.restApiId,
1167
+ };
1168
+ }
1169
+ }
1170
+
1171
+ class JaypieStack extends cdk.Stack {
1172
+ constructor(scope, id, props = {}) {
1173
+ const { key, ...stackProps } = props;
1174
+ // Handle stackName
1175
+ if (!stackProps.stackName) {
1176
+ stackProps.stackName = constructStackName(key);
1177
+ }
1178
+ // Handle env
1179
+ stackProps.env = {
1180
+ account: process.env.CDK_DEFAULT_ACCOUNT,
1181
+ region: process.env.CDK_DEFAULT_REGION,
1182
+ ...stackProps.env,
1183
+ };
1184
+ super(scope, id, stackProps);
1185
+ // Apply tags
1186
+ constructTagger(this, { name: stackProps.stackName });
1187
+ }
1188
+ }
1189
+
1190
+ class JaypieAppStack extends JaypieStack {
1191
+ constructor(scope, id, props = {}) {
1192
+ const { key = "app", ...stackProps } = props;
1193
+ // Handle stackName
1194
+ if (!stackProps.stackName) {
1195
+ stackProps.stackName = constructStackName(key);
1196
+ }
1197
+ super(scope, id, { key, ...stackProps });
1198
+ }
1199
+ }
1200
+
1201
+ class JaypieLambda extends constructs.Construct {
1202
+ constructor(scope, id, props) {
1203
+ super(scope, id);
1204
+ const { allowAllOutbound, allowPublicSubnet, architecture = lambda__namespace.Architecture.X86_64, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment: environmentInput, envSecrets = {}, ephemeralStorageSize, filesystem, handler = "index.handler", initialPolicy, layers = [], logGroup, logRetention = CDK$2.LAMBDA.LOG_RETENTION, maxEventAge, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag = CDK$2.ROLE.PROCESSING, runtime = new lambda__namespace.Runtime("nodejs24.x", lambda__namespace.RuntimeFamily.NODEJS, {
1205
+ supportsInlineCode: true,
1206
+ }), runtimeManagementMode, secrets: secretsInput = [], securityGroups, timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, vpc, vpcSubnets, } = props;
1207
+ // Resolve environment from array or object syntax
1208
+ const initialEnvironment = resolveEnvironment(environmentInput);
1209
+ // Get base environment with defaults
1210
+ const environment = jaypieLambdaEnv({ initialEnvironment });
1211
+ // Resolve secrets from mixed array (strings and JaypieEnvSecret instances)
1212
+ const secrets = resolveSecrets(scope, secretsInput);
1213
+ const codeAsset = typeof code === "string" ? lambda__namespace.Code.fromAsset(code) : code;
1214
+ // Create a working copy of layers
1215
+ const resolvedLayers = [...layers];
1216
+ // Process secrets environment variables
1217
+ const secretsEnvironment = Object.entries(envSecrets).reduce((acc, [key, secret]) => ({
1218
+ ...acc,
1219
+ [`SECRET_${key}`]: secret.secretName,
1220
+ }), {});
1221
+ // Process JaypieEnvSecret array
1222
+ const jaypieSecretsEnvironment = secrets.reduce((acc, secret) => {
1223
+ if (secret.envKey) {
1224
+ return {
1225
+ ...acc,
1226
+ [`SECRET_${secret.envKey}`]: secret.secretName,
1227
+ };
1228
+ }
1229
+ return acc;
1230
+ }, {});
1231
+ // Add ParamsAndSecrets layer if configured
1232
+ const resolvedParamsAndSecrets = resolveParamsAndSecrets({
1233
+ paramsAndSecrets,
1234
+ options: paramsAndSecretsOptions,
1235
+ });
1236
+ // Create LogGroup if not provided
1237
+ const resolvedLogGroup = logGroup ??
1238
+ new logs__namespace.LogGroup(this, "LogGroup", {
1239
+ retention: logRetention,
1240
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
1241
+ });
1242
+ // Create Lambda Function
1243
+ this._lambda = new lambda__namespace.Function(this, "Function", {
1244
+ allowAllOutbound,
1245
+ allowPublicSubnet,
1246
+ architecture,
1247
+ code: codeAsset,
1248
+ deadLetterQueue,
1249
+ deadLetterQueueEnabled,
1250
+ deadLetterTopic,
1251
+ description,
1252
+ environment: {
1253
+ ...environment,
1254
+ ...secretsEnvironment,
1255
+ ...jaypieSecretsEnvironment,
1256
+ },
1257
+ ephemeralStorageSize,
1258
+ filesystem,
1259
+ handler,
1260
+ initialPolicy,
1261
+ layers: resolvedLayers,
1262
+ logGroup: resolvedLogGroup,
1263
+ maxEventAge,
1264
+ memorySize,
1265
+ paramsAndSecrets: resolvedParamsAndSecrets,
1266
+ profiling,
1267
+ profilingGroup,
1268
+ reservedConcurrentExecutions,
1269
+ retryAttempts,
1270
+ runtime,
1271
+ runtimeManagementMode,
1272
+ securityGroups,
1273
+ timeout: typeof timeout === "number" ? cdk.Duration.seconds(timeout) : timeout,
1274
+ tracing,
1275
+ vpc,
1276
+ vpcSubnets,
1277
+ // Enable auto-publishing of versions when using provisioned concurrency
1278
+ currentVersionOptions: provisionedConcurrentExecutions !== undefined
1279
+ ? {
1280
+ removalPolicy: cdk.RemovalPolicy.RETAIN,
1281
+ description: "Auto-published version for provisioned concurrency",
1282
+ // Don't set provisioned concurrency here - it will be set on the alias
1283
+ }
1284
+ : undefined,
1285
+ });
1286
+ addDatadogLayers(this._lambda, { datadogApiKeyArn });
1287
+ // Grant secret read permissions
1288
+ Object.values(envSecrets).forEach((secret) => {
1289
+ secret.grantRead(this._lambda);
1290
+ });
1291
+ // Grant read permissions for JaypieEnvSecrets
1292
+ secrets.forEach((secret) => {
1293
+ secret.grantRead(this._lambda);
1294
+ });
1295
+ // Configure provisioned concurrency if specified
1296
+ if (provisionedConcurrentExecutions !== undefined) {
1297
+ // Use currentVersion which is auto-published with proper configuration
1298
+ const version = this._lambda.currentVersion;
1299
+ // Create alias for provisioned concurrency
1300
+ this._provisioned = new lambda__namespace.Alias(this, "ProvisionedAlias", {
1301
+ aliasName: "provisioned",
1302
+ version,
1303
+ provisionedConcurrentExecutions,
1304
+ });
1305
+ // Add explicit dependencies to ensure proper creation order
1306
+ this._provisioned.node.addDependency(version);
1307
+ }
1308
+ if (roleTag) {
1309
+ cdk.Tags.of(this._lambda).add(CDK$2.TAG.ROLE, roleTag);
1310
+ }
1311
+ if (vendorTag) {
1312
+ cdk.Tags.of(this._lambda).add(CDK$2.TAG.VENDOR, vendorTag);
1313
+ }
1314
+ // Assign _reference based on provisioned state
1315
+ this._reference =
1316
+ this._provisioned !== undefined ? this._provisioned : this._lambda;
1317
+ }
1318
+ // Public accessors
1319
+ get lambda() {
1320
+ return this._lambda;
1321
+ }
1322
+ get provisioned() {
1323
+ return this._provisioned;
1324
+ }
1325
+ get reference() {
1326
+ return this._reference;
1327
+ }
1328
+ // IFunction implementation
1329
+ get functionArn() {
1330
+ return this._reference.functionArn;
1331
+ }
1332
+ get functionName() {
1333
+ return this._reference.functionName;
1334
+ }
1335
+ get grantPrincipal() {
1336
+ return this._reference.grantPrincipal;
1337
+ }
1338
+ get role() {
1339
+ return this._reference.role;
1340
+ }
1341
+ get architecture() {
1342
+ return this._reference.architecture;
1343
+ }
1344
+ get connections() {
1345
+ return this._reference.connections;
1346
+ }
1347
+ get isBoundToVpc() {
1348
+ return this._reference.isBoundToVpc;
1349
+ }
1350
+ get latestVersion() {
1351
+ return this._reference.latestVersion;
1352
+ }
1353
+ get permissionsNode() {
1354
+ return this._reference.permissionsNode;
1355
+ }
1356
+ get resourceArnsForGrantInvoke() {
1357
+ return this._reference.resourceArnsForGrantInvoke;
1358
+ }
1359
+ get functionRef() {
1360
+ return {
1361
+ functionArn: this._reference.functionArn,
1362
+ functionName: this._reference.functionName,
1363
+ };
1364
+ }
1365
+ addEventSource(source) {
1366
+ this._reference.addEventSource(source);
1367
+ }
1368
+ addEventSourceMapping(id, options) {
1369
+ return this._reference.addEventSourceMapping(id, options);
1370
+ }
1371
+ addFunctionUrl(options) {
1372
+ return this._reference.addFunctionUrl(options);
1373
+ }
1374
+ addPermission(id, permission) {
1375
+ this._reference.addPermission(id, permission);
1376
+ }
1377
+ addToRolePolicy(statement) {
1378
+ this._reference.addToRolePolicy(statement);
1379
+ }
1380
+ configureAsyncInvoke(options) {
1381
+ this._reference.configureAsyncInvoke(options);
1382
+ }
1383
+ grantInvoke(grantee) {
1384
+ return this._reference.grantInvoke(grantee);
1385
+ }
1386
+ grantInvokeCompositePrincipal(compositePrincipal) {
1387
+ return this._reference.grantInvokeCompositePrincipal(compositePrincipal);
1388
+ }
1389
+ grantInvokeUrl(grantee) {
1390
+ return this._reference.grantInvokeUrl(grantee);
1391
+ }
1392
+ grantInvokeLatestVersion(grantee) {
1393
+ return this._reference.grantInvokeLatestVersion(grantee);
1394
+ }
1395
+ grantInvokeVersion(grantee, version) {
1396
+ return this._reference.grantInvokeVersion(grantee, version);
1397
+ }
1398
+ metric(metricName, props) {
1399
+ return this._reference.metric(metricName, props);
1400
+ }
1401
+ metricDuration(props) {
1402
+ return this._reference.metricDuration(props);
1403
+ }
1404
+ metricErrors(props) {
1405
+ return this._reference.metricErrors(props);
1406
+ }
1407
+ metricInvocations(props) {
1408
+ return this._reference.metricInvocations(props);
1409
+ }
1410
+ metricThrottles(props) {
1411
+ return this._reference.metricThrottles(props);
1412
+ }
1413
+ get env() {
1414
+ return {
1415
+ account: cdk.Stack.of(this).account,
1416
+ region: cdk.Stack.of(this).region,
1417
+ };
1418
+ }
1419
+ get stack() {
1420
+ return this._reference.stack;
1421
+ }
1422
+ applyRemovalPolicy(policy) {
1423
+ this._reference.applyRemovalPolicy(policy);
1424
+ }
1425
+ addEnvironment(key, value) {
1426
+ this._lambda.addEnvironment(key, value);
1427
+ }
1428
+ }
1429
+
1430
+ class JaypieQueuedLambda extends constructs.Construct {
1431
+ constructor(scope, id, props) {
1432
+ super(scope, id);
1433
+ const { allowAllOutbound, allowPublicSubnet, architecture, batchSize = 1, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment = {}, envSecrets = {}, ephemeralStorageSize, fifo = true, filesystem, handler = "index.handler", initialPolicy, layers = [], logGroup, logRetention = CDK$2.LAMBDA.LOG_RETENTION, maxEventAge, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag, runtime = new lambda__namespace.Runtime("nodejs24.x", lambda__namespace.RuntimeFamily.NODEJS, {
1434
+ supportsInlineCode: true,
1435
+ }), 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;
1436
+ // Create SQS Queue
1437
+ this._queue = new sqs__namespace.Queue(this, "Queue", {
1438
+ fifo,
1439
+ visibilityTimeout: typeof visibilityTimeout === "number"
1440
+ ? cdk.Duration.seconds(visibilityTimeout)
1441
+ : visibilityTimeout,
1442
+ });
1443
+ if (roleTag) {
1444
+ cdk.Tags.of(this._queue).add(CDK$2.TAG.ROLE, roleTag);
1445
+ }
1446
+ if (vendorTag) {
1447
+ cdk.Tags.of(this._queue).add(CDK$2.TAG.VENDOR, vendorTag);
1448
+ }
1449
+ // Create Lambda with JaypieLambda
1450
+ this._lambdaConstruct = new JaypieLambda(this, "Function", {
1451
+ allowAllOutbound,
1452
+ allowPublicSubnet,
1453
+ architecture,
1454
+ code,
1455
+ datadogApiKeyArn,
1456
+ deadLetterQueue,
1457
+ deadLetterQueueEnabled,
1458
+ deadLetterTopic,
1459
+ description,
1460
+ environment: {
1461
+ ...environment,
1462
+ CDK_ENV_QUEUE_URL: this._queue.queueUrl,
1463
+ },
1464
+ envSecrets,
1465
+ ephemeralStorageSize,
1466
+ filesystem,
1467
+ handler,
1468
+ initialPolicy,
1469
+ layers,
1470
+ logGroup,
1471
+ logRetention,
1472
+ maxEventAge,
1473
+ memorySize,
1474
+ paramsAndSecrets,
1475
+ paramsAndSecretsOptions,
1476
+ profiling,
1477
+ profilingGroup,
1478
+ provisionedConcurrentExecutions,
1479
+ reservedConcurrentExecutions,
1480
+ retryAttempts,
1481
+ roleTag,
1482
+ runtime,
1483
+ runtimeManagementMode,
1484
+ secrets,
1485
+ securityGroups,
1486
+ timeout,
1487
+ tracing,
1488
+ vendorTag,
1489
+ vpc,
1490
+ vpcSubnets,
1491
+ });
1492
+ // Set up queue and lambda integration
1493
+ this._queue.grantConsumeMessages(this._lambdaConstruct);
1494
+ this._queue.grantSendMessages(this._lambdaConstruct);
1495
+ this._lambdaConstruct.addEventSource(new lambdaEventSources__namespace.SqsEventSource(this._queue, {
1496
+ batchSize,
1497
+ }));
1498
+ }
1499
+ // Public accessors
1500
+ get queue() {
1501
+ return this._queue;
1502
+ }
1503
+ get lambda() {
1504
+ return this._lambdaConstruct.lambda;
1505
+ }
1506
+ // IFunction implementation
1507
+ get functionArn() {
1508
+ return this._lambdaConstruct.functionArn;
1509
+ }
1510
+ get functionName() {
1511
+ return this._lambdaConstruct.functionName;
1512
+ }
1513
+ get grantPrincipal() {
1514
+ return this._lambdaConstruct.grantPrincipal;
1515
+ }
1516
+ get role() {
1517
+ return this._lambdaConstruct.role;
1518
+ }
1519
+ get architecture() {
1520
+ return this._lambdaConstruct.architecture;
1521
+ }
1522
+ get connections() {
1523
+ return this._lambdaConstruct.connections;
1524
+ }
1525
+ get isBoundToVpc() {
1526
+ return this._lambdaConstruct.isBoundToVpc;
1527
+ }
1528
+ get latestVersion() {
1529
+ return this._lambdaConstruct.latestVersion;
1530
+ }
1531
+ get permissionsNode() {
1532
+ return this._lambdaConstruct.permissionsNode;
1533
+ }
1534
+ get resourceArnsForGrantInvoke() {
1535
+ return this._lambdaConstruct.resourceArnsForGrantInvoke;
1536
+ }
1537
+ get functionRef() {
1538
+ return this._lambdaConstruct.functionRef;
1539
+ }
1540
+ addEventSource(source) {
1541
+ this._lambdaConstruct.addEventSource(source);
1542
+ }
1543
+ addEventSourceMapping(id, options) {
1544
+ return this._lambdaConstruct.addEventSourceMapping(id, options);
1545
+ }
1546
+ addFunctionUrl(options) {
1547
+ return this._lambdaConstruct.addFunctionUrl(options);
1548
+ }
1549
+ addPermission(id, permission) {
1550
+ this._lambdaConstruct.addPermission(id, permission);
1551
+ }
1552
+ addToRolePolicy(statement) {
1553
+ this._lambdaConstruct.addToRolePolicy(statement);
1554
+ }
1555
+ configureAsyncInvoke(options) {
1556
+ this._lambdaConstruct.configureAsyncInvoke(options);
1557
+ }
1558
+ grantInvoke(grantee) {
1559
+ return this._lambdaConstruct.grantInvoke(grantee);
1560
+ }
1561
+ grantInvokeCompositePrincipal(compositePrincipal) {
1562
+ return this._lambdaConstruct.grantInvokeCompositePrincipal(compositePrincipal);
1563
+ }
1564
+ grantInvokeUrl(grantee) {
1565
+ return this._lambdaConstruct.grantInvokeUrl(grantee);
1566
+ }
1567
+ metric(metricName, props) {
1568
+ return this._lambdaConstruct.metric(metricName, props);
1569
+ }
1570
+ metricDuration(props) {
1571
+ return this._lambdaConstruct.metricDuration(props);
1572
+ }
1573
+ metricErrors(props) {
1574
+ return this._lambdaConstruct.metricErrors(props);
1575
+ }
1576
+ metricInvocations(props) {
1577
+ return this._lambdaConstruct.metricInvocations(props);
1578
+ }
1579
+ metricThrottles(props) {
1580
+ return this._lambdaConstruct.metricThrottles(props);
1581
+ }
1582
+ // Additional IFunction implementation
1583
+ grantInvokeLatestVersion(grantee) {
1584
+ return this._lambdaConstruct.grantInvokeLatestVersion(grantee);
1585
+ }
1586
+ grantInvokeVersion(grantee, version) {
1587
+ return this._lambdaConstruct.grantInvokeVersion(grantee, version);
1588
+ }
1589
+ get env() {
1590
+ return {
1591
+ account: cdk.Stack.of(this).account,
1592
+ region: cdk.Stack.of(this).region,
1593
+ };
1594
+ }
1595
+ get stack() {
1596
+ return cdk.Stack.of(this);
1597
+ }
1598
+ applyRemovalPolicy(policy) {
1599
+ this._lambdaConstruct.applyRemovalPolicy(policy);
1600
+ this._queue.applyRemovalPolicy(policy);
1601
+ }
1602
+ // IQueue implementation
1603
+ get queueRef() {
1604
+ return {
1605
+ queueUrl: this._queue.queueUrl,
1606
+ queueArn: this._queue.queueArn,
1607
+ };
1608
+ }
1609
+ get fifo() {
1610
+ return this._queue.fifo;
1611
+ }
1612
+ get queueArn() {
1613
+ return this._queue.queueArn;
1614
+ }
1615
+ get queueName() {
1616
+ return this._queue.queueName;
1617
+ }
1618
+ get queueUrl() {
1619
+ return this._queue.queueUrl;
1620
+ }
1621
+ get encryptionMasterKey() {
1622
+ return this._queue.encryptionMasterKey;
1623
+ }
1624
+ addToResourcePolicy(statement) {
1625
+ return this._queue.addToResourcePolicy(statement);
1626
+ }
1627
+ grant(grantee, ...actions) {
1628
+ return this._queue.grant(grantee, ...actions);
1629
+ }
1630
+ grantConsumeMessages(grantee) {
1631
+ return this._queue.grantConsumeMessages(grantee);
1632
+ }
1633
+ grantPurge(grantee) {
1634
+ return this._queue.grantPurge(grantee);
1635
+ }
1636
+ grantSendMessages(grantee) {
1637
+ return this._queue.grantSendMessages(grantee);
1638
+ }
1639
+ // Queue metrics
1640
+ metricApproximateAgeOfOldestMessage(props) {
1641
+ return this._queue.metricApproximateAgeOfOldestMessage(props);
1642
+ }
1643
+ metricApproximateNumberOfMessagesDelayed(props) {
1644
+ return this._queue.metricApproximateNumberOfMessagesDelayed(props);
1645
+ }
1646
+ metricApproximateNumberOfMessagesNotVisible(props) {
1647
+ return this._queue.metricApproximateNumberOfMessagesNotVisible(props);
1648
+ }
1649
+ metricApproximateNumberOfMessagesVisible(props) {
1650
+ return this._queue.metricApproximateNumberOfMessagesVisible(props);
1651
+ }
1652
+ metricNumberOfEmptyReceives(props) {
1653
+ return this._queue.metricNumberOfEmptyReceives(props);
1654
+ }
1655
+ metricNumberOfMessagesDeleted(props) {
1656
+ return this._queue.metricNumberOfMessagesDeleted(props);
1657
+ }
1658
+ metricNumberOfMessagesReceived(props) {
1659
+ return this._queue.metricNumberOfMessagesReceived(props);
1660
+ }
1661
+ metricNumberOfMessagesSent(props) {
1662
+ return this._queue.metricNumberOfMessagesSent(props);
1663
+ }
1664
+ metricSentMessageSize(props) {
1665
+ return this._queue.metricSentMessageSize(props);
1666
+ }
1667
+ addEnvironment(key, value) {
1668
+ this._lambdaConstruct.addEnvironment(key, value);
1669
+ }
1670
+ }
1671
+
1672
+ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
1673
+ constructor(scope, id, props) {
1674
+ props.fifo = false; // S3 event notifications are not supported for FIFO queues
1675
+ super(scope, id, props);
1676
+ const { bucketName, roleTag, vendorTag, bucketOptions = {} } = props;
1677
+ // Create S3 Bucket
1678
+ this._bucket = new s3__namespace.Bucket(this, "Bucket", {
1679
+ bucketName: bucketOptions.bucketName || bucketName,
1680
+ removalPolicy: bucketOptions.removalPolicy || cdk.RemovalPolicy.RETAIN,
1681
+ ...bucketOptions,
1682
+ });
1683
+ // Add tags to bucket
1684
+ if (roleTag) {
1685
+ cdk.Tags.of(this._bucket).add(CDK$2.TAG.ROLE, roleTag);
1686
+ }
1687
+ if (vendorTag) {
1688
+ cdk.Tags.of(this._bucket).add(CDK$2.TAG.VENDOR, vendorTag);
1689
+ }
1690
+ // Add an event notification from the bucket to the queue
1691
+ this._bucket.addEventNotification(s3__namespace.EventType.OBJECT_CREATED, new s3n__namespace.SqsDestination(this.queue));
1692
+ // Grant the lambda access to the bucket
1693
+ this._bucket.grantReadWrite(this);
1694
+ // Add environment variable for bucket name
1695
+ this.lambda.addEnvironment("CDK_ENV_BUCKET_NAME", this._bucket.bucketName);
1696
+ }
1697
+ // Public accessors
1698
+ get bucket() {
1699
+ return this._bucket;
1700
+ }
1701
+ // IBucket implementation
1702
+ get bucketArn() {
1703
+ return this._bucket.bucketArn;
1704
+ }
1705
+ get bucketDomainName() {
1706
+ return this._bucket.bucketDomainName;
1707
+ }
1708
+ get bucketDualStackDomainName() {
1709
+ return this._bucket.bucketDualStackDomainName;
1710
+ }
1711
+ get bucketName() {
1712
+ return this._bucket.bucketName;
1713
+ }
1714
+ get bucketRegionalDomainName() {
1715
+ return this._bucket.bucketRegionalDomainName;
1716
+ }
1717
+ get bucketWebsiteDomainName() {
1718
+ return this._bucket.bucketWebsiteDomainName;
1719
+ }
1720
+ get bucketWebsiteUrl() {
1721
+ return this._bucket.bucketWebsiteUrl;
1722
+ }
1723
+ get encryptionKey() {
1724
+ return this._bucket.encryptionKey;
1725
+ }
1726
+ get isWebsite() {
1727
+ return this._bucket.isWebsite || false;
1728
+ }
1729
+ get policy() {
1730
+ return this._bucket.policy;
1731
+ }
1732
+ addEventNotification(event, dest, ...filters) {
1733
+ this._bucket.addEventNotification(event, dest, ...filters);
1734
+ }
1735
+ addObjectCreatedNotification(dest, ...filters) {
1736
+ this._bucket.addObjectCreatedNotification(dest, ...filters);
1737
+ }
1738
+ addObjectRemovedNotification(dest, ...filters) {
1739
+ this._bucket.addObjectRemovedNotification(dest, ...filters);
1740
+ }
1741
+ addToResourcePolicy(permission) {
1742
+ return this._bucket.addToResourcePolicy(permission);
1743
+ }
1744
+ arnForObjects(objectKeyPattern) {
1745
+ return this._bucket.arnForObjects(objectKeyPattern);
1746
+ }
1747
+ enableEventBridgeNotification() {
1748
+ this._bucket.enableEventBridgeNotification();
1749
+ }
1750
+ grantDelete(grantee, objectsKeyPattern) {
1751
+ return this._bucket.grantDelete(grantee, objectsKeyPattern);
1752
+ }
1753
+ grantPublicAccess(keyPrefix, ...allowedActions) {
1754
+ return this._bucket.grantPublicAccess(keyPrefix, ...allowedActions);
1755
+ }
1756
+ grantPut(grantee, objectsKeyPattern) {
1757
+ return this._bucket.grantPut(grantee, objectsKeyPattern);
1758
+ }
1759
+ grantPutAcl(grantee, objectsKeyPattern) {
1760
+ return this._bucket.grantPutAcl(grantee, objectsKeyPattern);
1761
+ }
1762
+ grantRead(grantee, objectsKeyPattern) {
1763
+ return this._bucket.grantRead(grantee, objectsKeyPattern);
1764
+ }
1765
+ grantReadWrite(grantee, objectsKeyPattern) {
1766
+ return this._bucket.grantReadWrite(grantee, objectsKeyPattern);
1767
+ }
1768
+ grantWrite(grantee, objectsKeyPattern) {
1769
+ return this._bucket.grantWrite(grantee, objectsKeyPattern);
1770
+ }
1771
+ onCloudTrailEvent(id, options) {
1772
+ return this._bucket.onCloudTrailEvent(id, options);
1773
+ }
1774
+ onCloudTrailPutObject(id, options) {
1775
+ return this._bucket.onCloudTrailPutObject(id, options);
1776
+ }
1777
+ onCloudTrailWriteObject(id, options) {
1778
+ return this._bucket.onCloudTrailWriteObject(id, options);
1779
+ }
1780
+ s3UrlForObject(key) {
1781
+ return this._bucket.s3UrlForObject(key);
1782
+ }
1783
+ transferAccelerationUrlForObject(key, options) {
1784
+ return this._bucket.transferAccelerationUrlForObject(key, options);
1785
+ }
1786
+ urlForObject(key) {
1787
+ return this._bucket.urlForObject(key);
1788
+ }
1789
+ virtualHostedUrlForObject(key, options) {
1790
+ return this._bucket.virtualHostedUrlForObject(key, options);
1791
+ }
1792
+ grantReplicationPermission(identity, props) {
1793
+ return this._bucket.grantReplicationPermission(identity, props);
1794
+ }
1795
+ addReplicationPolicy(policy) {
1796
+ this._bucket.addReplicationPolicy(policy);
1797
+ }
1798
+ get bucketRef() {
1799
+ return {
1800
+ bucketArn: this._bucket.bucketArn,
1801
+ bucketName: this._bucket.bucketName,
1802
+ };
1803
+ }
1804
+ // Override applyRemovalPolicy to apply to all resources
1805
+ applyRemovalPolicy(policy) {
1806
+ super.applyRemovalPolicy(policy);
1807
+ this._bucket.applyRemovalPolicy(policy);
1808
+ }
1809
+ }
1810
+
1811
+ class JaypieDatadogBucket extends constructs.Construct {
1812
+ /**
1813
+ * Create a new S3 bucket for Datadog log archiving with automatic IAM permissions
1814
+ */
1815
+ constructor(scope, idOrProps, propsOrUndefined) {
1816
+ // Handle overloaded constructor signatures
1817
+ let props;
1818
+ let id;
1819
+ if (typeof idOrProps === "string") {
1820
+ // First param is ID, second is props
1821
+ props = propsOrUndefined || {};
1822
+ id = idOrProps;
1823
+ }
1824
+ else {
1825
+ // First param is props
1826
+ props = idOrProps || {};
1827
+ id = props.id || "JaypieDatadogBucket";
1828
+ }
1829
+ super(scope, id);
1830
+ // Extract Jaypie-specific options
1831
+ const { bucketId = "DatadogArchiveBucket", bucketScope, grantDatadogAccess = true, project, service = CDK$2.SERVICE.DATADOG, ...bucketProps } = props;
1832
+ // Create the bucket using bucketScope (defaults to this) and bucketId
1833
+ const effectiveBucketScope = bucketScope || this;
1834
+ this.bucket = new s3.Bucket(effectiveBucketScope, bucketId, bucketProps);
1835
+ // Add tags to bucket
1836
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
1837
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1838
+ if (project) {
1839
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
1840
+ }
1841
+ // Grant Datadog role access to bucket if enabled
1842
+ if (grantDatadogAccess) {
1843
+ this.policy = this.grantDatadogRoleBucketAccess({ project, service });
1844
+ }
1845
+ }
1846
+ /**
1847
+ * Grants the Datadog IAM role access to this bucket
1848
+ *
1849
+ * Checks for CDK_ENV_DATADOG_ROLE_ARN environment variable.
1850
+ * If found, creates a custom policy with:
1851
+ * - s3:ListBucket on bucket
1852
+ * - s3:GetObject and s3:PutObject on bucket/*
1853
+ *
1854
+ * @param options - Configuration options
1855
+ * @returns The created Policy, or undefined if CDK_ENV_DATADOG_ROLE_ARN is not set
1856
+ */
1857
+ grantDatadogRoleBucketAccess(options) {
1858
+ const datadogRoleArn = process.env.CDK_ENV_DATADOG_ROLE_ARN;
1859
+ // Early return if no Datadog role ARN is configured
1860
+ if (!datadogRoleArn) {
1861
+ return undefined;
1862
+ }
1863
+ const { project, service = CDK$2.SERVICE.DATADOG } = options || {};
1864
+ // Lookup the Datadog role
1865
+ const datadogRole = awsIam.Role.fromRoleArn(this, "DatadogRole", datadogRoleArn);
1866
+ // Build policy statements for bucket access
1867
+ const statements = [
1868
+ // Allow list bucket
1869
+ new awsIam.PolicyStatement({
1870
+ actions: ["s3:ListBucket"],
1871
+ resources: [this.bucket.bucketArn],
1872
+ }),
1873
+ // Allow read and write to the bucket
1874
+ new awsIam.PolicyStatement({
1875
+ actions: ["s3:GetObject", "s3:PutObject"],
1876
+ resources: [`${this.bucket.bucketArn}/*`],
1877
+ }),
1878
+ ];
1879
+ // Create the custom policy
1880
+ const datadogBucketPolicy = new awsIam.Policy(this, "DatadogBucketPolicy", {
1881
+ roles: [datadogRole],
1882
+ statements,
1883
+ });
1884
+ // Add tags
1885
+ cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.SERVICE, service);
1886
+ cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1887
+ cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
1888
+ if (project) {
1889
+ cdk__namespace.Tags.of(datadogBucketPolicy).add(CDK$2.TAG.PROJECT, project);
1890
+ }
1891
+ return datadogBucketPolicy;
1892
+ }
1893
+ }
1894
+
1895
+ const DATADOG_FORWARDER_TEMPLATE_URL = "https://datadog-cloudformation-template.s3.amazonaws.com/aws/forwarder/latest.yaml";
1896
+ const DEFAULT_RESERVED_CONCURRENCY = "10";
1897
+ class JaypieDatadogForwarder extends constructs.Construct {
1898
+ /**
1899
+ * Create a new Datadog forwarder with CloudFormation nested stack
1900
+ */
1901
+ constructor(scope, idOrProps, propsOrUndefined) {
1902
+ // Handle overloaded constructor signatures
1903
+ let props;
1904
+ let id;
1905
+ if (typeof idOrProps === "string") {
1906
+ // First param is ID, second is props
1907
+ props = propsOrUndefined || {};
1908
+ id = idOrProps;
1909
+ }
1910
+ else {
1911
+ // First param is props
1912
+ props = idOrProps || {};
1913
+ id = props.id || "DatadogForwarder";
1914
+ }
1915
+ super(scope, id);
1916
+ // Resolve options with defaults
1917
+ 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;
1918
+ // Validate required parameters
1919
+ if (!datadogApiKey) {
1920
+ throw new Error("Datadog API key is required. Provide via datadogApiKey prop or CDK_ENV_DATADOG_API_KEY environment variable.");
1921
+ }
1922
+ // Build Datadog tags
1923
+ let ddTags = account ? `account:${account}` : "";
1924
+ if (additionalTags) {
1925
+ ddTags = ddTags ? `${ddTags},${additionalTags}` : additionalTags;
1926
+ }
1927
+ // Deploy Datadog CloudFormation stack
1928
+ this.cfnStack = new cdk.CfnStack(this, "Stack", {
1929
+ parameters: {
1930
+ DdApiKey: datadogApiKey,
1931
+ DdTags: ddTags,
1932
+ ReservedConcurrency: reservedConcurrency,
1933
+ },
1934
+ templateUrl,
1935
+ });
1936
+ // Add tags to stack
1937
+ cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1938
+ cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.SERVICE, service);
1939
+ cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
1940
+ if (project) {
1941
+ cdk__namespace.Tags.of(this.cfnStack).add(CDK$2.TAG.PROJECT, project);
1942
+ }
1943
+ // Extract forwarder function from stack outputs
1944
+ this.forwarderFunction = lambda__namespace.Function.fromFunctionArn(this, "Function", this.cfnStack.getAtt("Outputs.DatadogForwarderArn").toString());
1945
+ // Extend Datadog role with custom permissions if enabled
1946
+ if (enableRoleExtension) {
1947
+ extendDatadogRole(this, { project, service });
1948
+ }
1949
+ // Create CloudFormation events rule if enabled
1950
+ if (enableCloudFormationEvents) {
1951
+ this.eventsRule = new awsEvents.Rule(this, "CloudFormationEventsRule", {
1952
+ eventPattern: {
1953
+ source: ["aws.cloudformation"],
1954
+ },
1955
+ targets: [
1956
+ new awsEventsTargets.LambdaFunction(this.forwarderFunction, {
1957
+ event: awsEvents.RuleTargetInput.fromEventPath("$"),
1958
+ }),
1959
+ ],
1960
+ });
1961
+ // Add tags to events rule
1962
+ cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
1963
+ cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.SERVICE, service);
1964
+ cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.VENDOR, CDK$2.VENDOR.DATADOG);
1965
+ if (project) {
1966
+ cdk__namespace.Tags.of(this.eventsRule).add(CDK$2.TAG.PROJECT, project);
1967
+ }
1968
+ }
1969
+ // Create CloudFormation output if enabled
1970
+ if (createOutput) {
1971
+ new cdk__namespace.CfnOutput(this, "ForwarderArnOutput", {
1972
+ description: "Datadog Log Forwarder Lambda ARN",
1973
+ exportName,
1974
+ value: this.cfnStack.getAtt("Outputs.DatadogForwarderArn").toString(),
1975
+ });
1976
+ }
1977
+ }
1978
+ }
1979
+
1980
+ class JaypieDistribution extends constructs.Construct {
1981
+ constructor(scope, id, props) {
1982
+ super(scope, id);
1983
+ const { certificate: certificateProp = true, defaultBehavior: propsDefaultBehavior, destination: destinationProp = true, handler, host: propsHost, invokeMode = lambda__namespace.InvokeMode.BUFFERED, roleTag = CDK$2.ROLE.API, zone: propsZone, ...distributionProps } = props;
1984
+ // Validate environment variables
1985
+ if (process.env.CDK_ENV_API_SUBDOMAIN &&
1986
+ !isValidSubdomain(process.env.CDK_ENV_API_SUBDOMAIN)) {
1987
+ throw new Error("CDK_ENV_API_SUBDOMAIN is not a valid subdomain");
1988
+ }
1989
+ if (process.env.CDK_ENV_API_HOSTED_ZONE &&
1990
+ !isValidHostname$1(process.env.CDK_ENV_API_HOSTED_ZONE)) {
1991
+ throw new Error("CDK_ENV_API_HOSTED_ZONE is not a valid hostname");
1992
+ }
1993
+ if (process.env.CDK_ENV_HOSTED_ZONE &&
1994
+ !isValidHostname$1(process.env.CDK_ENV_HOSTED_ZONE)) {
1995
+ throw new Error("CDK_ENV_HOSTED_ZONE is not a valid hostname");
1996
+ }
1997
+ // Determine host from props or environment
1998
+ let host = propsHost;
1999
+ if (!host) {
2000
+ try {
2001
+ if (process.env.CDK_ENV_API_HOST_NAME) {
2002
+ host = process.env.CDK_ENV_API_HOST_NAME;
2003
+ }
2004
+ else if (process.env.CDK_ENV_API_SUBDOMAIN) {
2005
+ host = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE ||
2006
+ process.env.CDK_ENV_HOSTED_ZONE ||
2007
+ "");
2008
+ }
2009
+ }
2010
+ catch {
2011
+ host = undefined;
2012
+ }
2013
+ }
2014
+ if (host && !isValidHostname$1(host)) {
2015
+ throw new Error("Host is not a valid hostname");
2016
+ }
2017
+ this.host = host;
2018
+ // Determine zone from props or environment
2019
+ const zone = propsZone || process.env.CDK_ENV_HOSTED_ZONE;
2020
+ // Resolve the origin from handler
2021
+ // Check order matters: IFunctionUrl before IOrigin (FunctionUrl also has bind method)
2022
+ // IFunction before IFunctionUrl (IFunction doesn't have functionUrlId)
2023
+ let origin;
2024
+ if (handler) {
2025
+ if (this.isIFunction(handler)) {
2026
+ // Create FunctionUrl for the Lambda function
2027
+ const functionUrl = new lambda__namespace.FunctionUrl(this, "FunctionUrl", {
2028
+ function: handler,
2029
+ authType: lambda__namespace.FunctionUrlAuthType.NONE,
2030
+ invokeMode,
2031
+ });
2032
+ this.functionUrl = functionUrl;
2033
+ origin = new origins__namespace.FunctionUrlOrigin(functionUrl);
2034
+ }
2035
+ else if (this.isIFunctionUrl(handler)) {
2036
+ origin = new origins__namespace.FunctionUrlOrigin(handler);
2037
+ }
2038
+ else if (this.isIOrigin(handler)) {
2039
+ origin = handler;
2040
+ }
2041
+ }
2042
+ // Build default behavior
2043
+ let defaultBehavior;
2044
+ if (propsDefaultBehavior) {
2045
+ defaultBehavior = propsDefaultBehavior;
2046
+ }
2047
+ else if (origin) {
2048
+ defaultBehavior = {
2049
+ allowedMethods: cloudfront__namespace.AllowedMethods.ALLOW_ALL,
2050
+ cachePolicy: cloudfront__namespace.CachePolicy.CACHING_DISABLED,
2051
+ origin,
2052
+ originRequestPolicy: cloudfront__namespace.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
2053
+ viewerProtocolPolicy: cloudfront__namespace.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
2054
+ };
2055
+ }
2056
+ else {
2057
+ throw new Error("Either handler or defaultBehavior must be provided to JaypieDistribution");
2058
+ }
2059
+ // Resolve hosted zone and certificate
2060
+ // Only resolve zone when we need it (for certificate or DNS)
2061
+ let hostedZone;
2062
+ let certificateToUse;
2063
+ if (host && zone && certificateProp !== false) {
2064
+ hostedZone = resolveHostedZone(this, { zone });
2065
+ if (certificateProp === true) {
2066
+ certificateToUse = new acm__namespace.Certificate(this, constructEnvName("Certificate"), {
2067
+ domainName: host,
2068
+ validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
2069
+ });
2070
+ cdk.Tags.of(certificateToUse).add(CDK$2.TAG.ROLE, roleTag);
2071
+ }
2072
+ else if (typeof certificateProp === "object") {
2073
+ certificateToUse = certificateProp;
2074
+ }
2075
+ this.certificate = certificateToUse;
2076
+ }
2077
+ // Create log bucket if logging is enabled
2078
+ let logBucket;
2079
+ if (destinationProp !== false) {
2080
+ logBucket = new s3__namespace.Bucket(this, constructEnvName("LogBucket"), {
2081
+ objectOwnership: s3__namespace.ObjectOwnership.OBJECT_WRITER,
2082
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
2083
+ autoDeleteObjects: true,
2084
+ lifecycleRules: [
2085
+ {
2086
+ expiration: cdk.Duration.days(90),
2087
+ transitions: [
2088
+ {
2089
+ storageClass: s3__namespace.StorageClass.INFREQUENT_ACCESS,
2090
+ transitionAfter: cdk.Duration.days(30),
2091
+ },
2092
+ ],
2093
+ },
2094
+ ],
2095
+ });
2096
+ cdk.Tags.of(logBucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.STORAGE);
2097
+ // Add S3 notification to Datadog forwarder
2098
+ const lambdaDestination = destinationProp === true
2099
+ ? new s3n.LambdaDestination(resolveDatadogForwarderFunction(this))
2100
+ : destinationProp;
2101
+ logBucket.addEventNotification(s3__namespace.EventType.OBJECT_CREATED, lambdaDestination);
2102
+ this.logBucket = logBucket;
2103
+ }
2104
+ // Create the CloudFront distribution
2105
+ this.distribution = new cloudfront__namespace.Distribution(this, constructEnvName("Distribution"), {
2106
+ defaultBehavior,
2107
+ ...(host && certificateToUse
2108
+ ? {
2109
+ certificate: certificateToUse,
2110
+ domainNames: [host],
2111
+ }
2112
+ : {}),
2113
+ ...(logBucket
2114
+ ? {
2115
+ enableLogging: true,
2116
+ logBucket,
2117
+ logFilePrefix: "cloudfront-logs/",
2118
+ }
2119
+ : {}),
2120
+ ...distributionProps,
2121
+ });
2122
+ cdk.Tags.of(this.distribution).add(CDK$2.TAG.ROLE, roleTag);
2123
+ this.distributionArn = `arn:aws:cloudfront::${cdk.Stack.of(this).account}:distribution/${this.distribution.distributionId}`;
2124
+ this.distributionDomainName = this.distribution.distributionDomainName;
2125
+ this.distributionId = this.distribution.distributionId;
2126
+ this.domainName = this.distribution.domainName;
2127
+ // Create DNS records if we have host and zone
2128
+ if (host && hostedZone) {
2129
+ const aRecord = new route53__namespace.ARecord(this, "AliasRecord", {
2130
+ recordName: host,
2131
+ target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
2132
+ zone: hostedZone,
2133
+ });
2134
+ cdk.Tags.of(aRecord).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
2135
+ const aaaaRecord = new route53__namespace.AaaaRecord(this, "AaaaAliasRecord", {
2136
+ recordName: host,
2137
+ target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
2138
+ zone: hostedZone,
2139
+ });
2140
+ cdk.Tags.of(aaaaRecord).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
2141
+ }
2142
+ }
2143
+ // Type guards for handler types
2144
+ isIOrigin(handler) {
2145
+ return (typeof handler === "object" &&
2146
+ handler !== null &&
2147
+ "bind" in handler &&
2148
+ typeof handler.bind === "function");
2149
+ }
2150
+ isIFunctionUrl(handler) {
2151
+ // FunctionUrl has 'url' property which is the function URL string
2152
+ // IFunction does not have 'url' property
2153
+ return (typeof handler === "object" &&
2154
+ handler !== null &&
2155
+ "url" in handler &&
2156
+ "functionArn" in handler);
2157
+ }
2158
+ isIFunction(handler) {
2159
+ // IFunction has functionArn and functionName but NOT 'url'
2160
+ // (FunctionUrl also has functionArn but also has 'url')
2161
+ return (typeof handler === "object" &&
2162
+ handler !== null &&
2163
+ "functionArn" in handler &&
2164
+ "functionName" in handler &&
2165
+ !("url" in handler));
2166
+ }
2167
+ // Implement IDistribution interface
2168
+ get env() {
2169
+ return {
2170
+ account: cdk.Stack.of(this).account,
2171
+ region: cdk.Stack.of(this).region,
2172
+ };
2173
+ }
2174
+ get stack() {
2175
+ return this.distribution.stack;
2176
+ }
2177
+ applyRemovalPolicy(policy) {
2178
+ this.distribution.applyRemovalPolicy(policy);
2179
+ }
2180
+ grant(identity, ...actions) {
2181
+ return this.distribution.grant(identity, ...actions);
2182
+ }
2183
+ grantCreateInvalidation(identity) {
2184
+ return this.distribution.grantCreateInvalidation(identity);
2185
+ }
2186
+ get distributionRef() {
2187
+ return {
2188
+ distributionId: this.distribution.distributionId,
2189
+ };
2190
+ }
2191
+ }
2192
+
2193
+ class JaypieDatadogSecret extends JaypieEnvSecret {
2194
+ constructor(scope, id = "MongoConnectionString", props) {
2195
+ const defaultProps = {
2196
+ envKey: "DATADOG_API_KEY",
2197
+ roleTag: CDK$2.ROLE.MONITORING,
2198
+ vendorTag: CDK$2.VENDOR.DATADOG,
2199
+ ...props,
2200
+ };
2201
+ super(scope, id, defaultProps);
2202
+ }
2203
+ }
2204
+
2205
+ class JaypieDnsRecord extends constructs.Construct {
2206
+ constructor(scope, id, props) {
2207
+ super(scope, id);
2208
+ const { comment, recordName, type, values } = props;
2209
+ const ttl = props.ttl || cdk__namespace.Duration.seconds(CDK$2.DNS.CONFIG.TTL);
2210
+ // Resolve the hosted zone (supports both string and IHostedZone)
2211
+ const zone = resolveHostedZone(scope, {
2212
+ name: `${id}HostedZone`,
2213
+ zone: props.zone,
2214
+ });
2215
+ // Common properties for all record types
2216
+ const baseProps = {
2217
+ comment,
2218
+ recordName,
2219
+ ttl,
2220
+ zone,
2221
+ };
2222
+ // Create the appropriate record based on type
2223
+ switch (type) {
2224
+ case CDK$2.DNS.RECORD.A: {
2225
+ if (!Array.isArray(values) || values.length === 0) {
2226
+ throw new errors.ConfigurationError("A record requires at least one IP address");
2227
+ }
2228
+ this.record = new route53.ARecord(this, "Record", {
2229
+ ...baseProps,
2230
+ target: route53.RecordTarget.fromIpAddresses(...values),
2231
+ });
2232
+ break;
2233
+ }
2234
+ case CDK$2.DNS.RECORD.CNAME: {
2235
+ if (!Array.isArray(values) || values.length === 0) {
2236
+ throw new errors.ConfigurationError("CNAME record requires a domain name");
2237
+ }
2238
+ this.record = new route53.CnameRecord(this, "Record", {
2239
+ ...baseProps,
2240
+ domainName: values[0],
2241
+ });
2242
+ break;
2243
+ }
2244
+ case CDK$2.DNS.RECORD.MX: {
2245
+ if (!Array.isArray(values) || values.length === 0) {
2246
+ throw new errors.ConfigurationError("MX record requires at least one mail server");
2247
+ }
2248
+ this.record = new route53.MxRecord(this, "Record", {
2249
+ ...baseProps,
2250
+ values: values,
2251
+ });
2252
+ break;
2253
+ }
2254
+ case CDK$2.DNS.RECORD.NS: {
2255
+ if (!Array.isArray(values) || values.length === 0) {
2256
+ throw new errors.ConfigurationError("NS record requires at least one name server");
2257
+ }
2258
+ this.record = new route53.NsRecord(this, "Record", {
2259
+ ...baseProps,
2260
+ values: values,
2261
+ });
2262
+ break;
2263
+ }
2264
+ case CDK$2.DNS.RECORD.TXT: {
2265
+ if (!Array.isArray(values) || values.length === 0) {
2266
+ throw new errors.ConfigurationError("TXT record requires at least one value");
2267
+ }
2268
+ this.record = new route53.TxtRecord(this, "Record", {
2269
+ ...baseProps,
2270
+ values: values,
2271
+ });
2272
+ break;
2273
+ }
2274
+ default:
2275
+ throw new errors.ConfigurationError(`Unsupported DNS record type: ${type}. Supported types: A, CNAME, MX, NS, TXT`);
2276
+ }
2277
+ // Add standard tags to the DNS record
2278
+ cdk__namespace.Tags.of(this.record).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.INFRASTRUCTURE);
2279
+ cdk__namespace.Tags.of(this.record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
2280
+ }
2281
+ }
2282
+
2283
+ class JaypieEventsRule extends constructs.Construct {
2284
+ /**
2285
+ * Create a new EventBridge rule that targets a Lambda function
2286
+ */
2287
+ constructor(scope, idOrSourceOrProps, propsOrUndefined) {
2288
+ // Handle overloaded constructor signatures
2289
+ let props;
2290
+ let id;
2291
+ if (typeof idOrSourceOrProps === "string") {
2292
+ // Check if it looks like an AWS source (starts with "aws.")
2293
+ if (idOrSourceOrProps.startsWith("aws.")) {
2294
+ // First param is source, second is props
2295
+ props = propsOrUndefined || {};
2296
+ props.source = idOrSourceOrProps;
2297
+ // Generate ID from source
2298
+ const sourceName = idOrSourceOrProps
2299
+ .replace("aws.", "")
2300
+ .split(".")
2301
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
2302
+ .join("");
2303
+ id = props.id || `${sourceName}EventsRule`;
2304
+ }
2305
+ else {
2306
+ // First param is ID, second is props
2307
+ props = propsOrUndefined || {};
2308
+ id = idOrSourceOrProps;
2309
+ }
2310
+ }
2311
+ else {
2312
+ // First param is props
2313
+ props = idOrSourceOrProps || {};
2314
+ if (props.source) {
2315
+ const sourceName = typeof props.source === "string"
2316
+ ? props.source
2317
+ .replace("aws.", "")
2318
+ .split(".")
2319
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
2320
+ .join("")
2321
+ : "Events";
2322
+ id = props.id || `${sourceName}EventsRule`;
2323
+ }
2324
+ else {
2325
+ id = props.id || "EventsRule";
2326
+ }
2327
+ }
2328
+ super(scope, id);
2329
+ // Extract Jaypie-specific options
2330
+ const { id: _id, project, service = CDK$2.SERVICE.DATADOG, source, targetFunction, vendor = CDK$2.VENDOR.DATADOG, ...ruleProps } = props;
2331
+ // Resolve target function
2332
+ this.targetFunction =
2333
+ targetFunction || resolveDatadogForwarderFunction(scope);
2334
+ // Build event pattern if source is specified
2335
+ const eventPattern = source
2336
+ ? {
2337
+ ...ruleProps.eventPattern,
2338
+ source: Array.isArray(source) ? source : [source],
2339
+ }
2340
+ : ruleProps.eventPattern;
2341
+ // Build rule props
2342
+ const finalRuleProps = {
2343
+ ...ruleProps,
2344
+ eventPattern,
2345
+ targets: [
2346
+ new awsEventsTargets.LambdaFunction(this.targetFunction, {
2347
+ event: awsEvents.RuleTargetInput.fromEventPath("$"),
2348
+ }),
2349
+ ],
2350
+ };
2351
+ // Create the rule
2352
+ this.rule = new awsEvents.Rule(this, "Rule", finalRuleProps);
2353
+ // Add tags
2354
+ cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
2355
+ cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.SERVICE, service);
2356
+ cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.VENDOR, vendor);
2357
+ if (project) {
2358
+ cdk__namespace.Tags.of(this.rule).add(CDK$2.TAG.PROJECT, project);
2359
+ }
2360
+ }
2361
+ }
2362
+
2363
+ class JaypieGitHubDeployRole extends constructs.Construct {
2364
+ constructor(scope, id = "GitHubDeployRole", props = {}) {
2365
+ super(scope, id);
2366
+ const { oidcProviderArn = cdk.Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), output = true, repoRestriction: propsRepoRestriction, } = props;
2367
+ // Extract account ID from the scope
2368
+ const accountId = cdk.Stack.of(this).account;
2369
+ // Resolve repoRestriction from props or environment variables
2370
+ let repoRestriction = propsRepoRestriction;
2371
+ if (!repoRestriction) {
2372
+ const envRepo = process.env.CDK_ENV_REPO || process.env.PROJECT_REPO;
2373
+ if (!envRepo) {
2374
+ throw new errors.ConfigurationError("No repoRestriction provided. Set repoRestriction prop, CDK_ENV_REPO, or PROJECT_REPO environment variable");
2375
+ }
2376
+ // Extract organization from owner/repo format and create org-wide restriction
2377
+ const organization = envRepo.split("/")[0];
2378
+ repoRestriction = `repo:${organization}/*:*`;
2379
+ }
2380
+ // Create the IAM role
2381
+ this._role = new awsIam.Role(this, "GitHubActionsRole", {
2382
+ assumedBy: new awsIam.FederatedPrincipal(oidcProviderArn, {
2383
+ StringLike: {
2384
+ "token.actions.githubusercontent.com:sub": repoRestriction,
2385
+ },
2386
+ }, "sts:AssumeRoleWithWebIdentity"),
2387
+ maxSessionDuration: cdk.Duration.hours(1),
2388
+ path: "/",
2389
+ });
2390
+ cdk.Tags.of(this._role).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
2391
+ // Allow the role to access the GitHub OIDC provider
2392
+ this._role.addToPolicy(new awsIam.PolicyStatement({
2393
+ actions: ["sts:AssumeRoleWithWebIdentity"],
2394
+ resources: [`arn:aws:iam::${accountId}:oidc-provider/*`],
2395
+ }));
2396
+ // Allow the role to deploy CDK apps
2397
+ this._role.addToPolicy(new awsIam.PolicyStatement({
2398
+ actions: [
2399
+ "cloudformation:CreateStack",
2400
+ "cloudformation:DeleteStack",
2401
+ "cloudformation:DescribeStackEvents",
2402
+ "cloudformation:DescribeStackResource",
2403
+ "cloudformation:DescribeStackResources",
2404
+ "cloudformation:DescribeStacks",
2405
+ "cloudformation:GetTemplate",
2406
+ "cloudformation:SetStackPolicy",
2407
+ "cloudformation:UpdateStack",
2408
+ "cloudformation:ValidateTemplate",
2409
+ "iam:PassRole",
2410
+ "route53:ListHostedZones*",
2411
+ "s3:GetObject",
2412
+ "s3:ListBucket",
2413
+ ],
2414
+ effect: awsIam.Effect.ALLOW,
2415
+ resources: ["*"],
2416
+ }));
2417
+ this._role.addToPolicy(new awsIam.PolicyStatement({
2418
+ actions: ["iam:PassRole", "sts:AssumeRole"],
2419
+ effect: awsIam.Effect.ALLOW,
2420
+ resources: [
2421
+ "arn:aws:iam::*:role/cdk-hnb659fds-deploy-role-*",
2422
+ "arn:aws:iam::*:role/cdk-hnb659fds-file-publishing-*",
2423
+ "arn:aws:iam::*:role/cdk-readOnlyRole",
2424
+ ],
2425
+ }));
2426
+ // Export the ARN of the role
2427
+ if (output !== false) {
2428
+ const outputId = typeof output === "string" ? output : "GitHubActionsRoleArn";
2429
+ new cdk.CfnOutput(this, outputId, {
2430
+ value: this._role.roleArn,
2431
+ });
2432
+ }
2433
+ }
2434
+ get role() {
2435
+ return this._role;
2436
+ }
2437
+ get roleArn() {
2438
+ return this._role.roleArn;
2439
+ }
2440
+ get roleName() {
2441
+ return this._role.roleName;
2442
+ }
2443
+ }
2444
+
2445
+ class JaypieExpressLambda extends JaypieLambda {
2446
+ constructor(scope, id, props) {
2447
+ super(scope, id, {
2448
+ timeout: cdk.Duration.seconds(CDK$2.DURATION.EXPRESS_API),
2449
+ roleTag: CDK$2.ROLE.API,
2450
+ ...props,
2451
+ });
2452
+ }
2453
+ }
2454
+
2455
+ const SERVICE = {
2456
+ ROUTE53: "route53.amazonaws.com",
2457
+ };
2458
+ /**
2459
+ * Check if a string is a valid hostname
2460
+ */
2461
+ function isValidHostname(str) {
2462
+ // Check if it contains a dot and matches hostname pattern
2463
+ if (!str.includes("."))
2464
+ return false;
2465
+ // Basic hostname validation: alphanumeric, hyphens, dots
2466
+ // Each label must start and end with alphanumeric
2467
+ const hostnameRegex = /^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/i;
2468
+ return hostnameRegex.test(str);
2469
+ }
2470
+ class JaypieHostedZone extends constructs.Construct {
2471
+ /**
2472
+ * Create a new hosted zone with query logging and optional DNS records
2473
+ */
2474
+ constructor(scope, idOrProps, propsOrRecords) {
2475
+ // Handle overloaded constructor signatures
2476
+ let props;
2477
+ let id;
2478
+ if (typeof idOrProps === "string") {
2479
+ // If it's a valid hostname, treat it as zoneName
2480
+ if (isValidHostname(idOrProps)) {
2481
+ // Third param can be props object or records array
2482
+ if (Array.isArray(propsOrRecords)) {
2483
+ props = { zoneName: idOrProps, records: propsOrRecords };
2484
+ }
2485
+ else {
2486
+ props = propsOrRecords || { zoneName: idOrProps };
2487
+ // Set zoneName if not already set
2488
+ if (!props.zoneName) {
2489
+ props = { ...props, zoneName: idOrProps };
2490
+ }
2491
+ }
2492
+ // Use id from props if provided, otherwise derive from zoneName
2493
+ id = props.id || `${idOrProps}-HostedZone`;
2494
+ }
2495
+ else {
2496
+ // Otherwise treat it as an explicit id
2497
+ props = propsOrRecords;
2498
+ id = idOrProps;
2499
+ }
2500
+ }
2501
+ else {
2502
+ // idOrProps is props
2503
+ props = idOrProps;
2504
+ id = props.id || `${props.zoneName}-HostedZone`;
2505
+ }
2506
+ super(scope, id);
2507
+ const { zoneName, project } = props;
2508
+ const destination = props.destination ?? true;
2509
+ const service = props.service || CDK$2.SERVICE.INFRASTRUCTURE;
2510
+ // Create the log group
2511
+ this.logGroup = new logs.LogGroup(this, "LogGroup", {
2512
+ logGroupName: process.env.PROJECT_NONCE
2513
+ ? `/aws/route53/${zoneName}-${process.env.PROJECT_NONCE}`
2514
+ : `/aws/route53/${zoneName}`,
2515
+ retention: logs.RetentionDays.ONE_WEEK,
2516
+ });
2517
+ // Add tags
2518
+ cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.SERVICE, service);
2519
+ cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
2520
+ if (project) {
2521
+ cdk__namespace.Tags.of(this.logGroup).add(CDK$2.TAG.PROJECT, project);
2522
+ }
2523
+ // Grant Route 53 permissions to write to the log group
2524
+ this.logGroup.grantWrite(new awsIam.ServicePrincipal(SERVICE.ROUTE53));
2525
+ // Add destination based on configuration
2526
+ if (destination !== false) {
2527
+ const lambdaDestination = destination === true
2528
+ ? resolveDatadogLoggingDestination(scope)
2529
+ : destination;
2530
+ this.logGroup.addSubscriptionFilter("DatadogLambdaDestination", {
2531
+ destination: lambdaDestination,
2532
+ filterPattern: logs.FilterPattern.allEvents(),
2533
+ });
2534
+ }
2535
+ // Create the hosted zone
2536
+ this.hostedZone = new route53.HostedZone(this, "HostedZone", {
2537
+ queryLogsLogGroupArn: this.logGroup.logGroupArn,
2538
+ zoneName,
2539
+ });
2540
+ // Add tags
2541
+ cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.SERVICE, service);
2542
+ cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
2543
+ if (project) {
2544
+ cdk__namespace.Tags.of(this.hostedZone).add(CDK$2.TAG.PROJECT, project);
2545
+ }
2546
+ // Create DNS records if provided
2547
+ this.dnsRecords = [];
2548
+ if (props.records) {
2549
+ props.records.forEach((recordConfig, index) => {
2550
+ const { id, ...recordProps } = recordConfig;
2551
+ // Generate a default ID if not provided
2552
+ const recordId = id ||
2553
+ `${recordProps.type}${recordProps.recordName ? `-${recordProps.recordName}` : ""}-${index}`;
2554
+ const dnsRecord = new JaypieDnsRecord(this, recordId, {
2555
+ ...recordProps,
2556
+ zone: this.hostedZone,
2557
+ });
2558
+ this.dnsRecords.push(dnsRecord);
2559
+ });
2560
+ }
2561
+ }
2562
+ }
2563
+
2564
+ const CDK = {
2565
+ TAG: {
2566
+ STACK_SHA: "stackSha",
2567
+ },
2568
+ };
2569
+ class JaypieInfrastructureStack extends JaypieStack {
2570
+ constructor(scope, id, props = {}) {
2571
+ const { key = "infra", ...stackProps } = props;
2572
+ // Handle stackName
2573
+ if (!stackProps.stackName) {
2574
+ stackProps.stackName = constructStackName(key);
2575
+ }
2576
+ super(scope, id, { key, ...stackProps });
2577
+ // Add infrastructure-specific tag
2578
+ if (process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA) {
2579
+ cdk.Tags.of(this).add(CDK.TAG.STACK_SHA, process.env.CDK_ENV_INFRASTRUCTURE_STACK_SHA);
2580
+ }
2581
+ }
2582
+ }
2583
+
2584
+ class JaypieMongoDbSecret extends JaypieEnvSecret {
2585
+ constructor(scope, id = "MongoConnectionString", props) {
2586
+ const defaultProps = {
2587
+ envKey: "MONGODB_URI",
2588
+ roleTag: CDK$2.ROLE.STORAGE,
2589
+ vendorTag: CDK$2.VENDOR.MONGODB,
2590
+ ...props,
2591
+ };
2592
+ super(scope, id, defaultProps);
2593
+ }
2594
+ }
2595
+
2596
+ class JaypieNextJs extends constructs.Construct {
2597
+ constructor(scope, id, props) {
2598
+ super(scope, id);
2599
+ const domainName = props?.domainName || envHostname();
2600
+ this.domainName = domainName;
2601
+ const domainNameSanitized = domainName
2602
+ .replace(/\./g, "-")
2603
+ .replace(/[^a-zA-Z0-9]/g, "_");
2604
+ // Resolve environment from array or object syntax
2605
+ const environment = resolveEnvironment(props?.environment);
2606
+ const envSecrets = props?.envSecrets || {};
2607
+ const nextjsPath = props?.nextjsPath?.startsWith("..")
2608
+ ? path__namespace.join(process.cwd(), props.nextjsPath)
2609
+ : props?.nextjsPath || path__namespace.join(process.cwd(), "..", "nextjs");
2610
+ const paramsAndSecrets = resolveParamsAndSecrets();
2611
+ // Resolve secrets from mixed array (strings and JaypieEnvSecret instances)
2612
+ const secrets = resolveSecrets(scope, props?.secrets);
2613
+ // Process secrets environment variables
2614
+ const secretsEnvironment = Object.entries(envSecrets).reduce((acc, [key, secret]) => ({
2615
+ ...acc,
2616
+ [`SECRET_${key}`]: secret.secretName,
2617
+ }), {});
2618
+ // Process JaypieEnvSecret array
2619
+ const jaypieSecretsEnvironment = secrets.reduce((acc, secret) => {
2620
+ if (secret.envKey) {
2621
+ return {
2622
+ ...acc,
2623
+ [`SECRET_${secret.envKey}`]: secret.secretName,
2624
+ };
2625
+ }
2626
+ return acc;
2627
+ }, {});
2628
+ // Process NEXT_PUBLIC_ environment variables
2629
+ const nextPublicEnv = Object.entries(process.env).reduce((acc, [key, value]) => {
2630
+ if (key.startsWith("NEXT_PUBLIC_") && value) {
2631
+ return {
2632
+ ...acc,
2633
+ [key]: value,
2634
+ };
2635
+ }
2636
+ return acc;
2637
+ }, {});
2638
+ const nextjs = new cdkNextjsStandalone.Nextjs(this, "NextJsApp", {
2639
+ nextjsPath,
2640
+ domainProps: {
2641
+ domainName,
2642
+ hostedZone: resolveHostedZone(this, {
2643
+ zone: props?.hostedZone,
2644
+ }),
2645
+ },
2646
+ environment: {
2647
+ ...jaypieLambdaEnv(),
2648
+ ...environment,
2649
+ ...secretsEnvironment,
2650
+ ...jaypieSecretsEnvironment,
2651
+ ...nextPublicEnv,
2652
+ NEXT_PUBLIC_SITE_URL: `https://${domainName}`,
2653
+ },
2654
+ overrides: {
2655
+ nextjsDistribution: {
2656
+ imageCachePolicyProps: {
2657
+ cachePolicyName: `NextJsImageCachePolicy-${domainNameSanitized}`,
2658
+ },
2659
+ serverCachePolicyProps: {
2660
+ cachePolicyName: `NextJsServerCachePolicy-${domainNameSanitized}`,
2661
+ },
2662
+ },
2663
+ nextjsImage: {
2664
+ functionProps: {
2665
+ paramsAndSecrets,
2666
+ },
2667
+ },
2668
+ nextjsServer: {
2669
+ functionProps: {
2670
+ paramsAndSecrets,
2671
+ },
2672
+ },
2673
+ },
2674
+ });
2675
+ addDatadogLayers(nextjs.imageOptimizationFunction);
2676
+ addDatadogLayers(nextjs.serverFunction.lambdaFunction);
2677
+ // Grant secret read permissions
2678
+ Object.values(envSecrets).forEach((secret) => {
2679
+ secret.grantRead(nextjs.serverFunction.lambdaFunction);
2680
+ });
2681
+ // Grant read permissions for JaypieEnvSecrets
2682
+ secrets.forEach((secret) => {
2683
+ secret.grantRead(nextjs.serverFunction.lambdaFunction);
2684
+ });
2685
+ }
2686
+ }
2687
+
2688
+ class JaypieOpenAiSecret extends JaypieEnvSecret {
2689
+ constructor(scope, id = "OpenAiApiKey", props) {
2690
+ const defaultProps = {
2691
+ envKey: "OPENAI_API_KEY",
2692
+ roleTag: CDK$2.ROLE.PROCESSING,
2693
+ vendorTag: CDK$2.VENDOR.OPENAI,
2694
+ ...props,
2695
+ };
2696
+ super(scope, id, defaultProps);
2697
+ }
2698
+ }
2699
+
2700
+ class JaypieOrganizationTrail extends constructs.Construct {
2701
+ /**
2702
+ * Create a new organization CloudTrail with S3 bucket and lifecycle policies
2703
+ */
2704
+ constructor(scope, idOrProps, propsOrUndefined) {
2705
+ // Handle overloaded constructor signatures
2706
+ let props;
2707
+ let id;
2708
+ if (typeof idOrProps === "string") {
2709
+ // First param is ID, second is props
2710
+ props = propsOrUndefined || {};
2711
+ id = idOrProps;
2712
+ }
2713
+ else {
2714
+ // First param is props
2715
+ props = idOrProps || {};
2716
+ const defaultName = process.env.PROJECT_NONCE
2717
+ ? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
2718
+ : "organization-cloudtrail";
2719
+ id = props.id || `${props.trailName || defaultName}-Trail`;
2720
+ }
2721
+ super(scope, id);
2722
+ // Resolve options with defaults
2723
+ const { bucketName = process.env.PROJECT_NONCE
2724
+ ? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
2725
+ : "organization-cloudtrail", enableDatadogNotifications = true, enableFileValidation = false, expirationDays = 365, glacierTransitionDays = 180, infrequentAccessTransitionDays = 30, project, service = CDK$2.SERVICE.INFRASTRUCTURE, trailName = process.env.PROJECT_NONCE
2726
+ ? `organization-cloudtrail-${process.env.PROJECT_NONCE}`
2727
+ : "organization-cloudtrail", } = props;
2728
+ // Create the S3 bucket for CloudTrail logs
2729
+ this.bucket = new s3.Bucket(this, "Bucket", {
2730
+ accessControl: s3.BucketAccessControl.LOG_DELIVERY_WRITE,
2731
+ bucketName,
2732
+ lifecycleRules: [
2733
+ {
2734
+ expiration: cdk__namespace.Duration.days(expirationDays),
2735
+ transitions: [
2736
+ {
2737
+ storageClass: s3.StorageClass.INFREQUENT_ACCESS,
2738
+ transitionAfter: cdk__namespace.Duration.days(infrequentAccessTransitionDays),
2739
+ },
2740
+ {
2741
+ storageClass: s3.StorageClass.GLACIER,
2742
+ transitionAfter: cdk__namespace.Duration.days(glacierTransitionDays),
2743
+ },
2744
+ ],
2745
+ },
2746
+ ],
2747
+ });
2748
+ // Add CloudTrail bucket policies
2749
+ this.bucket.addToResourcePolicy(new awsIam.PolicyStatement({
2750
+ actions: ["s3:GetBucketAcl"],
2751
+ effect: awsIam.Effect.ALLOW,
2752
+ principals: [new awsIam.ServicePrincipal("cloudtrail.amazonaws.com")],
2753
+ resources: [this.bucket.bucketArn],
2754
+ }));
2755
+ this.bucket.addToResourcePolicy(new awsIam.PolicyStatement({
2756
+ actions: ["s3:PutObject"],
2757
+ conditions: {
2758
+ StringEquals: {
2759
+ "s3:x-amz-acl": "bucket-owner-full-control",
2760
+ },
2761
+ },
2762
+ effect: awsIam.Effect.ALLOW,
2763
+ principals: [new awsIam.ServicePrincipal("cloudtrail.amazonaws.com")],
2764
+ resources: [`${this.bucket.bucketArn}/*`],
2765
+ }));
2766
+ // Add tags to bucket
2767
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.SERVICE, service);
2768
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
2769
+ if (project) {
2770
+ cdk__namespace.Tags.of(this.bucket).add(CDK$2.TAG.PROJECT, project);
2771
+ }
2772
+ // Add Datadog notifications if enabled
2773
+ if (enableDatadogNotifications) {
2774
+ const datadogForwarderFunction = resolveDatadogForwarderFunction(scope);
2775
+ this.bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.LambdaDestination(datadogForwarderFunction));
2776
+ }
2777
+ // Create the organization trail
2778
+ this.trail = new awsCloudtrail.Trail(this, "Trail", {
2779
+ bucket: this.bucket,
2780
+ enableFileValidation,
2781
+ isOrganizationTrail: true,
2782
+ managementEvents: awsCloudtrail.ReadWriteType.ALL,
2783
+ trailName,
2784
+ });
2785
+ // Add tags to trail
2786
+ cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.SERVICE, service);
2787
+ cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.ROLE, CDK$2.ROLE.MONITORING);
2788
+ if (project) {
2789
+ cdk__namespace.Tags.of(this.trail).add(CDK$2.TAG.PROJECT, project);
2790
+ }
2791
+ }
2792
+ }
2793
+
2794
+ /**
2795
+ * JaypieSsoPermissions Construct
2796
+ *
2797
+ * Creates and manages AWS IAM Identity Center (SSO) permission sets and assignments
2798
+ *
2799
+ * @example
2800
+ * const permissionSets = new JaypieSsoPermissions(this, "PermissionSets", {
2801
+ * iamIdentityCenterArn: "arn:aws:sso:::instance/...",
2802
+ * administratorGroupId: "b4c8b438-4031-7000-782d-5046945fb956",
2803
+ * analystGroupId: "2488f4e8-d061-708e-abe1-c315f0e30005",
2804
+ * developerGroupId: "b438a4f8-e0e1-707c-c6e8-21841daf9ad1",
2805
+ * administratorAccountAssignments: {
2806
+ * "211125635435": ["Administrator", "Analyst", "Developer"],
2807
+ * "381492033431": ["Administrator", "Analyst"],
2808
+ * },
2809
+ * analystAccountAssignments: {
2810
+ * "211125635435": ["Analyst", "Developer"],
2811
+ * "381492033431": [],
2812
+ * },
2813
+ * developerAccountAssignments: {
2814
+ * "211125635435": ["Analyst", "Developer"],
2815
+ * "381492033431": [],
2816
+ * },
2817
+ * });
2818
+ */
2819
+ class JaypieSsoPermissions extends constructs.Construct {
2820
+ constructor(scope, id, props) {
2821
+ super(scope, id);
2822
+ const { iamIdentityCenterArn: iamIdentityCenterArnProp, administratorGroupId, analystGroupId, developerGroupId, administratorAccountAssignments, analystAccountAssignments, developerAccountAssignments, } = props;
2823
+ const iamIdentityCenterArn = iamIdentityCenterArnProp || process.env.CDK_ENV_IAM_IDENTITY_CENTER_ARN;
2824
+ if (!iamIdentityCenterArn) {
2825
+ // If no IAM Identity Center ARN provided, skip SSO setup
2826
+ return;
2827
+ }
2828
+ //
2829
+ // Permission Sets
2830
+ //
2831
+ this.administratorPermissionSet = new awsSso.CfnPermissionSet(this, "AdministratorPermissionSet", {
2832
+ // Required
2833
+ instanceArn: iamIdentityCenterArn,
2834
+ name: "Administrator",
2835
+ // Optional
2836
+ description: "Unrestricted access",
2837
+ inlinePolicy: {
2838
+ Version: "2012-10-17",
2839
+ Statement: [
2840
+ {
2841
+ Effect: "Allow",
2842
+ Action: [
2843
+ "aws-portal:ViewUsage",
2844
+ "aws-portal:ViewBilling",
2845
+ "budgets:*",
2846
+ "cur:DescribeReportDefinitions",
2847
+ "cur:PutReportDefinition",
2848
+ "cur:DeleteReportDefinition",
2849
+ "cur:ModifyReportDefinition",
2850
+ ],
2851
+ Resource: "*",
2852
+ },
2853
+ ],
2854
+ },
2855
+ managedPolicies: [
2856
+ awsIam.ManagedPolicy.fromAwsManagedPolicyName("AdministratorAccess")
2857
+ .managedPolicyArn,
2858
+ awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
2859
+ ],
2860
+ sessionDuration: cdk.Duration.hours(1).toIsoString(),
2861
+ tags: [
2862
+ {
2863
+ key: CDK$2.TAG.SERVICE,
2864
+ value: CDK$2.SERVICE.SSO,
2865
+ },
2866
+ {
2867
+ key: CDK$2.TAG.ROLE,
2868
+ value: CDK$2.ROLE.SECURITY,
2869
+ },
2870
+ ],
2871
+ });
2872
+ this.analystPermissionSet = new awsSso.CfnPermissionSet(this, "AnalystPermissionSet", {
2873
+ // Required
2874
+ instanceArn: iamIdentityCenterArn,
2875
+ name: "Analyst",
2876
+ // Optional
2877
+ description: "Read-only access; may expand to limited write access",
2878
+ inlinePolicy: {
2879
+ Version: "2012-10-17",
2880
+ Statement: [
2881
+ {
2882
+ Effect: "Allow",
2883
+ Action: [
2884
+ "aws-portal:ViewUsage",
2885
+ "aws-portal:ViewBilling",
2886
+ "budgets:Describe*",
2887
+ "budgets:View*",
2888
+ "ce:Get*",
2889
+ "ce:List*",
2890
+ "cloudformation:Describe*",
2891
+ "cloudformation:Get*",
2892
+ "cloudformation:List*",
2893
+ "cloudwatch:BatchGet*",
2894
+ "cloudwatch:Get*",
2895
+ "cloudwatch:List*",
2896
+ "cost-optimization-hub:Get*",
2897
+ "cost-optimization-hub:List*",
2898
+ "ec2:Describe*",
2899
+ "ec2:Get*",
2900
+ "ec2:List*",
2901
+ "ec2:Search*",
2902
+ "iam:Get*",
2903
+ "iam:List*",
2904
+ "iam:PassRole",
2905
+ "lambda:Get*",
2906
+ "lambda:List*",
2907
+ "logs:Describe*",
2908
+ "logs:Get*",
2909
+ "logs:List*",
2910
+ "pipes:Describe*",
2911
+ "pipes:List*",
2912
+ "s3:Get*",
2913
+ "s3:List*",
2914
+ "secretsmanager:GetRandomPassword",
2915
+ "secretsmanager:GetResourcePolicy",
2916
+ "secretsmanager:List*",
2917
+ "securityhub:Describe*",
2918
+ "securityhub:Get*",
2919
+ "securityhub:List*",
2920
+ "servicecatalog:Describe*",
2921
+ "sns:Get*",
2922
+ "sns:List*",
2923
+ "sqs:Get*",
2924
+ "sqs:List*",
2925
+ "states:Describe*",
2926
+ "states:Get*",
2927
+ "states:List*",
2928
+ "tag:*",
2929
+ "uxc:*",
2930
+ "xray:*",
2931
+ ],
2932
+ Resource: "*",
2933
+ },
2934
+ ],
2935
+ },
2936
+ managedPolicies: [
2937
+ awsIam.ManagedPolicy.fromAwsManagedPolicyName("AmazonQDeveloperAccess")
2938
+ .managedPolicyArn,
2939
+ awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
2940
+ awsIam.ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
2941
+ .managedPolicyArn,
2942
+ ],
2943
+ sessionDuration: cdk.Duration.hours(12).toIsoString(),
2944
+ tags: [
2945
+ {
2946
+ key: CDK$2.TAG.SERVICE,
2947
+ value: CDK$2.SERVICE.SSO,
2948
+ },
2949
+ {
2950
+ key: CDK$2.TAG.ROLE,
2951
+ value: CDK$2.ROLE.SECURITY,
2952
+ },
2953
+ ],
2954
+ });
2955
+ this.developerPermissionSet = new awsSso.CfnPermissionSet(this, "DeveloperPermissionSet", {
2956
+ // Required
2957
+ instanceArn: iamIdentityCenterArn,
2958
+ name: "Developer",
2959
+ // Optional
2960
+ description: "Administrative access with limited restrictions",
2961
+ inlinePolicy: {
2962
+ Version: "2012-10-17",
2963
+ Statement: [
2964
+ {
2965
+ Effect: "Allow",
2966
+ Action: [
2967
+ "budgets:*",
2968
+ "ce:*",
2969
+ "cloudformation:*",
2970
+ "cloudwatch:*",
2971
+ "cost-optimization-hub:*",
2972
+ "ec2:*",
2973
+ "iam:Get*",
2974
+ "iam:List*",
2975
+ "iam:PassRole",
2976
+ "lambda:*",
2977
+ "logs:*",
2978
+ "pipes:*",
2979
+ "s3:*",
2980
+ "secretsmanager:*",
2981
+ "securityhub:*",
2982
+ "servicecatalog:*",
2983
+ "sns:*",
2984
+ "sqs:*",
2985
+ "states:*",
2986
+ "tag:*",
2987
+ "uxc:*",
2988
+ "xray:*",
2989
+ ],
2990
+ Resource: "*",
2991
+ },
2992
+ ],
2993
+ },
2994
+ managedPolicies: [
2995
+ awsIam.ManagedPolicy.fromAwsManagedPolicyName("AmazonQDeveloperAccess")
2996
+ .managedPolicyArn,
2997
+ awsIam.ManagedPolicy.fromAwsManagedPolicyName("AWSManagementConsoleBasicUserAccess").managedPolicyArn,
2998
+ awsIam.ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
2999
+ .managedPolicyArn,
3000
+ awsIam.ManagedPolicy.fromAwsManagedPolicyName("job-function/SystemAdministrator").managedPolicyArn,
3001
+ ],
3002
+ sessionDuration: cdk.Duration.hours(4).toIsoString(),
3003
+ tags: [
3004
+ {
3005
+ key: CDK$2.TAG.SERVICE,
3006
+ value: CDK$2.SERVICE.SSO,
3007
+ },
3008
+ {
3009
+ key: CDK$2.TAG.ROLE,
3010
+ value: CDK$2.ROLE.SECURITY,
3011
+ },
3012
+ ],
3013
+ });
3014
+ // Map permission set names to their ARNs and labels
3015
+ const permissionSetMap = {
3016
+ Administrator: {
3017
+ arn: this.administratorPermissionSet.attrPermissionSetArn,
3018
+ label: "Administrator",
3019
+ },
3020
+ Analyst: {
3021
+ arn: this.analystPermissionSet.attrPermissionSetArn,
3022
+ label: "Analyst",
3023
+ },
3024
+ Developer: {
3025
+ arn: this.developerPermissionSet.attrPermissionSetArn,
3026
+ label: "Developer",
3027
+ },
3028
+ };
3029
+ //
3030
+ // Assignments
3031
+ //
3032
+ // Helper function to create assignments for a group
3033
+ const createAssignments = (groupId, accountAssignments) => {
3034
+ if (!groupId || !accountAssignments) {
3035
+ return; // Skip if group ID or assignments not provided
3036
+ }
3037
+ Object.keys(accountAssignments).forEach((accountId) => {
3038
+ const permissionSetNames = accountAssignments[accountId];
3039
+ permissionSetNames.forEach((permissionSetName) => {
3040
+ const permissionSet = permissionSetMap[permissionSetName];
3041
+ if (!permissionSet) {
3042
+ throw new errors.ConfigurationError(`Unknown permission set: ${permissionSetName}. Valid options: ${Object.keys(permissionSetMap).join(", ")}`);
3043
+ }
3044
+ const accountAssignment = new awsSso.CfnAssignment(this, `AccountAssignment-${accountId}-${permissionSet.label}Role-${groupId}Group`, {
3045
+ // Required
3046
+ instanceArn: iamIdentityCenterArn,
3047
+ permissionSetArn: permissionSet.arn,
3048
+ principalId: groupId,
3049
+ principalType: CDK$2.PRINCIPAL_TYPE.GROUP,
3050
+ targetId: accountId,
3051
+ targetType: CDK$2.TARGET_TYPE.AWS_ACCOUNT,
3052
+ });
3053
+ cdk.Tags.of(accountAssignment).add(CDK$2.TAG.SERVICE, CDK$2.SERVICE.SSO);
3054
+ cdk.Tags.of(accountAssignment).add(CDK$2.TAG.ROLE, CDK$2.ROLE.SECURITY);
3055
+ });
3056
+ });
3057
+ };
3058
+ // Create assignments for each group
3059
+ createAssignments(administratorGroupId, administratorAccountAssignments);
3060
+ createAssignments(analystGroupId, analystAccountAssignments);
3061
+ createAssignments(developerGroupId, developerAccountAssignments);
3062
+ }
3063
+ }
3064
+
3065
+ //
3066
+ //
3067
+ // Constants
3068
+ //
3069
+ const DEFAULT_APPLICATION_ID = "arn:aws:serverlessrepo:us-east-2:004480582608:applications/SSOSync";
3070
+ const DEFAULT_APPLICATION_VERSION = "2.3.3";
3071
+ const DEFAULT_GOOGLE_GROUP_MATCH = "name:AWS*";
3072
+ //
3073
+ //
3074
+ // Class
3075
+ //
3076
+ class JaypieSsoSyncApplication extends constructs.Construct {
3077
+ constructor(scope, id = "SsoSyncApplication", props = {}) {
3078
+ super(scope, id);
3079
+ const { googleAdminEmail, googleAdminEmailEnvKey = "CDK_ENV_SSOSYNC_GOOGLE_ADMIN_EMAIL", googleCredentials, googleCredentialsEnvKey = "CDK_ENV_SSOSYNC_GOOGLE_CREDENTIALS", googleGroupMatch, googleGroupMatchEnvKey = "CDK_ENV_SSOSYNC_GOOGLE_GROUP_MATCH", identityStoreId, identityStoreIdEnvKey = "CDK_ENV_SSOSYNC_IDENTITY_STORE_ID", scimEndpointAccessToken, scimEndpointAccessTokenEnvKey = "CDK_ENV_SCIM_ENDPOINT_ACCESS_TOKEN", scimEndpointUrl, scimEndpointUrlEnvKey = "CDK_ENV_SSOSYNC_SCIM_ENDPOINT_URL", semanticVersion, semanticVersionEnvKey = "CDK_ENV_SSOSYNC_SEMANTIC_VERSION", ssoSyncApplicationId = DEFAULT_APPLICATION_ID, tags, } = props;
3080
+ // Resolve all values from props or environment variables
3081
+ const resolvedGoogleAdminEmail = googleAdminEmail || process.env[googleAdminEmailEnvKey];
3082
+ const resolvedGoogleCredentials = googleCredentials || process.env[googleCredentialsEnvKey];
3083
+ const resolvedGoogleGroupMatch = googleGroupMatch ||
3084
+ process.env[googleGroupMatchEnvKey] ||
3085
+ DEFAULT_GOOGLE_GROUP_MATCH;
3086
+ const resolvedIdentityStoreId = identityStoreId || process.env[identityStoreIdEnvKey];
3087
+ const resolvedScimEndpointAccessToken = scimEndpointAccessToken || process.env[scimEndpointAccessTokenEnvKey];
3088
+ const resolvedScimEndpointUrl = scimEndpointUrl || process.env[scimEndpointUrlEnvKey];
3089
+ const resolvedSemanticVersion = semanticVersion ||
3090
+ process.env[semanticVersionEnvKey] ||
3091
+ DEFAULT_APPLICATION_VERSION;
3092
+ // Validate required parameters
3093
+ const missingParams = [];
3094
+ if (!resolvedGoogleAdminEmail) {
3095
+ missingParams.push(`googleAdminEmail or ${googleAdminEmailEnvKey} environment variable`);
3096
+ }
3097
+ if (!resolvedGoogleCredentials) {
3098
+ missingParams.push(`googleCredentials or ${googleCredentialsEnvKey} environment variable`);
3099
+ }
3100
+ if (!resolvedIdentityStoreId) {
3101
+ missingParams.push(`identityStoreId or ${identityStoreIdEnvKey} environment variable`);
3102
+ }
3103
+ if (!resolvedScimEndpointAccessToken) {
3104
+ missingParams.push(`scimEndpointAccessToken or ${scimEndpointAccessTokenEnvKey} environment variable`);
3105
+ }
3106
+ if (!resolvedScimEndpointUrl) {
3107
+ missingParams.push(`scimEndpointUrl or ${scimEndpointUrlEnvKey} environment variable`);
3108
+ }
3109
+ if (missingParams.length > 0) {
3110
+ throw new errors.ConfigurationError(`JaypieSsoSyncApplication missing required configuration: ${missingParams.join(", ")}`);
3111
+ }
3112
+ // Create the SSO Sync Application
3113
+ // Type assertion is safe because we validated all required values above
3114
+ this._application = new awsSam.CfnApplication(this, "Application", {
3115
+ location: {
3116
+ applicationId: ssoSyncApplicationId,
3117
+ semanticVersion: resolvedSemanticVersion,
3118
+ },
3119
+ parameters: {
3120
+ GoogleAdminEmail: resolvedGoogleAdminEmail,
3121
+ GoogleCredentials: resolvedGoogleCredentials,
3122
+ GoogleGroupMatch: resolvedGoogleGroupMatch,
3123
+ IdentityStoreID: resolvedIdentityStoreId,
3124
+ Region: cdk.Stack.of(this).region,
3125
+ SCIMEndpointAccessToken: resolvedScimEndpointAccessToken,
3126
+ SCIMEndpointUrl: resolvedScimEndpointUrl,
3127
+ },
3128
+ });
3129
+ // Add tags
3130
+ const defaultTags = {
3131
+ [CDK$2.TAG.ROLE]: CDK$2.ROLE.SECURITY,
3132
+ };
3133
+ const allTags = { ...defaultTags, ...tags };
3134
+ Object.entries(allTags).forEach(([key, value]) => {
3135
+ cdk.Tags.of(this._application).add(key, value);
3136
+ });
3137
+ }
3138
+ get application() {
3139
+ return this._application;
3140
+ }
3141
+ }
3142
+
3143
+ class JaypieWebDeploymentBucket extends constructs.Construct {
3144
+ constructor(scope, id, props = {}) {
3145
+ super(scope, id);
3146
+ const roleTag = props.roleTag || CDK$2.ROLE.HOSTING;
3147
+ // Environment variable validation
3148
+ if (process.env.CDK_ENV_WEB_SUBDOMAIN &&
3149
+ !isValidSubdomain(process.env.CDK_ENV_WEB_SUBDOMAIN)) {
3150
+ throw new errors.ConfigurationError("CDK_ENV_WEB_SUBDOMAIN is not a valid subdomain");
3151
+ }
3152
+ if (process.env.CDK_ENV_WEB_HOSTED_ZONE &&
3153
+ !isValidHostname$1(process.env.CDK_ENV_WEB_HOSTED_ZONE)) {
3154
+ throw new errors.ConfigurationError("CDK_ENV_WEB_HOSTED_ZONE is not a valid hostname");
3155
+ }
3156
+ if (process.env.CDK_ENV_HOSTED_ZONE &&
3157
+ !isValidHostname$1(process.env.CDK_ENV_HOSTED_ZONE)) {
3158
+ throw new errors.ConfigurationError("CDK_ENV_HOSTED_ZONE is not a valid hostname");
3159
+ }
3160
+ // Determine host from props or environment
3161
+ let host = props.host;
3162
+ if (!host) {
3163
+ try {
3164
+ host =
3165
+ process.env.CDK_ENV_WEB_HOST ||
3166
+ mergeDomain(process.env.CDK_ENV_WEB_SUBDOMAIN || "", process.env.CDK_ENV_WEB_HOSTED_ZONE ||
3167
+ process.env.CDK_ENV_HOSTED_ZONE ||
3168
+ "");
3169
+ }
3170
+ catch {
3171
+ host = undefined;
3172
+ }
3173
+ }
3174
+ if (host && !isValidHostname$1(host)) {
3175
+ throw new errors.ConfigurationError("Host is not a valid hostname");
3176
+ }
3177
+ // Determine zone from props or environment
3178
+ const zone = props.zone ||
3179
+ process.env.CDK_ENV_WEB_HOSTED_ZONE ||
3180
+ process.env.CDK_ENV_HOSTED_ZONE;
3181
+ // Create the S3 bucket
3182
+ this.bucket = new s3__namespace.Bucket(this, "DestinationBucket", {
3183
+ accessControl: s3__namespace.BucketAccessControl.BUCKET_OWNER_FULL_CONTROL,
3184
+ autoDeleteObjects: true,
3185
+ blockPublicAccess: s3__namespace.BlockPublicAccess.BLOCK_ACLS_ONLY,
3186
+ bucketName: props.name || constructEnvName("web"),
3187
+ publicReadAccess: true,
3188
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
3189
+ versioned: false,
3190
+ websiteErrorDocument: "index.html",
3191
+ websiteIndexDocument: "index.html",
3192
+ ...props,
3193
+ });
3194
+ // Delegate IBucket properties to the bucket
3195
+ this.bucketArn = this.bucket.bucketArn;
3196
+ this.bucketDomainName = this.bucket.bucketDomainName;
3197
+ this.bucketDualStackDomainName = this.bucket.bucketDualStackDomainName;
3198
+ this.bucketName = this.bucket.bucketName;
3199
+ this.bucketRegionalDomainName = this.bucket.bucketRegionalDomainName;
3200
+ this.bucketWebsiteDomainName = this.bucket.bucketWebsiteDomainName;
3201
+ this.bucketWebsiteUrl = this.bucket.bucketWebsiteUrl;
3202
+ this.encryptionKey = this.bucket.encryptionKey;
3203
+ this.isWebsite = this.bucket.isWebsite;
3204
+ this.notificationsHandlerRole = undefined;
3205
+ this.policy = this.bucket.policy;
3206
+ cdk.Tags.of(this.bucket).add(CDK$2.TAG.ROLE, roleTag);
3207
+ // Create deployment role if repository is configured
3208
+ let repo;
3209
+ if (process.env.CDK_ENV_REPO) {
3210
+ repo = `repo:${process.env.CDK_ENV_REPO}:*`;
3211
+ }
3212
+ if (repo) {
3213
+ const bucketDeployRole = new awsIam.Role(this, "DestinationBucketDeployRole", {
3214
+ assumedBy: new awsIam.FederatedPrincipal(cdk.Fn.importValue(CDK$2.IMPORT.OIDC_PROVIDER), {
3215
+ StringLike: {
3216
+ "token.actions.githubusercontent.com:sub": repo,
3217
+ },
3218
+ }, "sts:AssumeRoleWithWebIdentity"),
3219
+ maxSessionDuration: cdk.Duration.hours(1),
3220
+ });
3221
+ cdk.Tags.of(bucketDeployRole).add(CDK$2.TAG.ROLE, CDK$2.ROLE.DEPLOY);
3222
+ // Allow the role to write to the bucket
3223
+ bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
3224
+ effect: awsIam.Effect.ALLOW,
3225
+ actions: [
3226
+ "s3:DeleteObject",
3227
+ "s3:GetObject",
3228
+ "s3:ListObjectsV2",
3229
+ "s3:PutObject",
3230
+ ],
3231
+ resources: [`${this.bucket.bucketArn}/*`],
3232
+ }));
3233
+ bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
3234
+ effect: awsIam.Effect.ALLOW,
3235
+ actions: ["s3:ListBucket"],
3236
+ resources: [this.bucket.bucketArn],
3237
+ }));
3238
+ // Allow the role to describe the current stack
3239
+ const stack = cdk.Stack.of(this);
3240
+ bucketDeployRole.addToPolicy(new awsIam.PolicyStatement({
3241
+ actions: ["cloudformation:DescribeStacks"],
3242
+ effect: awsIam.Effect.ALLOW,
3243
+ resources: [
3244
+ `arn:aws:cloudformation:${stack.region}:${stack.account}:stack/${stack.stackName}/*`,
3245
+ ],
3246
+ }));
3247
+ this.deployRoleArn = bucketDeployRole.roleArn;
3248
+ // Output the deploy role ARN
3249
+ new cdk.CfnOutput(this, "DestinationBucketDeployRoleArn", {
3250
+ value: bucketDeployRole.roleArn,
3251
+ });
3252
+ }
3253
+ // Create CloudFront distribution and certificate if host and zone are provided
3254
+ if (host && zone) {
3255
+ let hostedZone;
3256
+ if (typeof zone === "string") {
3257
+ hostedZone = route53__namespace.HostedZone.fromLookup(this, "HostedZone", {
3258
+ domainName: zone,
3259
+ });
3260
+ }
3261
+ else if (zone instanceof JaypieHostedZone) {
3262
+ hostedZone = zone.hostedZone;
3263
+ }
3264
+ else {
3265
+ hostedZone = zone;
3266
+ }
3267
+ // Create certificate if not provided
3268
+ if (props.certificate !== false) {
3269
+ this.certificate =
3270
+ typeof props.certificate === "object"
3271
+ ? props.certificate
3272
+ : new acm__namespace.Certificate(this, "Certificate", {
3273
+ domainName: host,
3274
+ validation: acm__namespace.CertificateValidation.fromDns(hostedZone),
3275
+ });
3276
+ new cdk.CfnOutput(this, "CertificateArn", {
3277
+ value: this.certificate.certificateArn,
3278
+ });
3279
+ cdk.Tags.of(this.certificate).add(CDK$2.TAG.ROLE, roleTag);
3280
+ }
3281
+ // Create CloudFront distribution
3282
+ this.distribution = new cloudfront__namespace.Distribution(this, "Distribution", {
3283
+ defaultBehavior: {
3284
+ cachePolicy: cloudfront__namespace.CachePolicy.CACHING_DISABLED,
3285
+ origin: new origins__namespace.S3StaticWebsiteOrigin(this.bucket),
3286
+ viewerProtocolPolicy: cloudfront__namespace.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
3287
+ },
3288
+ certificate: this.certificate,
3289
+ domainNames: [host],
3290
+ });
3291
+ cdk.Tags.of(this.distribution).add(CDK$2.TAG.ROLE, roleTag);
3292
+ // If this is production, enable caching on everything but index.html
3293
+ if (isProductionEnv()) {
3294
+ this.distribution.addBehavior("/*", new origins__namespace.S3StaticWebsiteOrigin(this.bucket), {
3295
+ viewerProtocolPolicy: cloudfront__namespace.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
3296
+ cachePolicy: cloudfront__namespace.CachePolicy.CACHING_OPTIMIZED,
3297
+ });
3298
+ }
3299
+ // Create DNS record
3300
+ const record = new route53__namespace.ARecord(this, "AliasRecord", {
3301
+ recordName: host,
3302
+ target: route53__namespace.RecordTarget.fromAlias(new route53Targets__namespace.CloudFrontTarget(this.distribution)),
3303
+ zone: hostedZone,
3304
+ });
3305
+ cdk.Tags.of(record).add(CDK$2.TAG.ROLE, CDK$2.ROLE.NETWORKING);
3306
+ this.distributionDomainName = this.distribution.distributionDomainName;
3307
+ }
3308
+ }
3309
+ // Implement remaining IBucket methods by delegating to the bucket
3310
+ addEventNotification(event, dest, ...filters) {
3311
+ this.bucket.addEventNotification(event, dest, ...filters);
3312
+ }
3313
+ addObjectCreatedNotification(dest, ...filters) {
3314
+ this.bucket.addObjectCreatedNotification(dest, ...filters);
3315
+ }
3316
+ addObjectRemovedNotification(dest, ...filters) {
3317
+ this.bucket.addObjectRemovedNotification(dest, ...filters);
3318
+ }
3319
+ addToResourcePolicy(permission) {
3320
+ return this.bucket.addToResourcePolicy(permission);
3321
+ }
3322
+ arnForObjects(keyPattern) {
3323
+ return this.bucket.arnForObjects(keyPattern);
3324
+ }
3325
+ grantDelete(identity, objectsKeyPattern) {
3326
+ return this.bucket.grantDelete(identity, objectsKeyPattern);
3327
+ }
3328
+ grantPublicAccess(allowedActions, keyPrefix) {
3329
+ return keyPrefix
3330
+ ? this.bucket.grantPublicAccess(allowedActions, keyPrefix)
3331
+ : this.bucket.grantPublicAccess(allowedActions);
3332
+ }
3333
+ grantPut(identity, objectsKeyPattern) {
3334
+ return this.bucket.grantPut(identity, objectsKeyPattern);
3335
+ }
3336
+ grantPutAcl(identity, objectsKeyPattern) {
3337
+ return this.bucket.grantPutAcl(identity, objectsKeyPattern);
3338
+ }
3339
+ grantRead(identity, objectsKeyPattern) {
3340
+ return this.bucket.grantRead(identity, objectsKeyPattern);
3341
+ }
3342
+ grantReadWrite(identity, objectsKeyPattern) {
3343
+ return this.bucket.grantReadWrite(identity, objectsKeyPattern);
3344
+ }
3345
+ grantWrite(identity, objectsKeyPattern) {
3346
+ return this.bucket.grantWrite(identity, objectsKeyPattern);
3347
+ }
3348
+ grantReplicationPermission(identity, props) {
3349
+ return this.bucket.grantReplicationPermission(identity, props);
3350
+ }
3351
+ s3UrlForObject(key) {
3352
+ return this.bucket.s3UrlForObject(key);
3353
+ }
3354
+ urlForObject(key) {
3355
+ return this.bucket.urlForObject(key);
3356
+ }
3357
+ virtualHostedUrlForObject(key, options) {
3358
+ return this.bucket.virtualHostedUrlForObject(key, options);
3359
+ }
3360
+ transferAccelerationUrlForObject(key) {
3361
+ return this.bucket.transferAccelerationUrlForObject(key);
3362
+ }
3363
+ onCloudTrailEvent(id, options) {
3364
+ return this.bucket.onCloudTrailEvent(id, options);
3365
+ }
3366
+ onCloudTrailPutObject(id, options) {
3367
+ return this.bucket.onCloudTrailPutObject(id, options);
3368
+ }
3369
+ onCloudTrailWriteObject(id, options) {
3370
+ return this.bucket.onCloudTrailWriteObject(id, options);
3371
+ }
3372
+ addCorsRule(rule) {
3373
+ this.bucket.addCorsRule(rule);
3374
+ }
3375
+ addInventory(inventory) {
3376
+ this.bucket.addInventory(inventory);
3377
+ }
3378
+ addLifecycleRule(rule) {
3379
+ this.bucket.addLifecycleRule(rule);
3380
+ }
3381
+ addMetric(metric) {
3382
+ this.bucket.addMetric(metric);
3383
+ }
3384
+ enableEventBridgeNotification() {
3385
+ this.bucket.enableEventBridgeNotification();
3386
+ }
3387
+ addReplicationPolicy(policy) {
3388
+ this.bucket.addReplicationPolicy(policy);
3389
+ }
3390
+ get stack() {
3391
+ return this.bucket.stack;
3392
+ }
3393
+ get env() {
3394
+ return this.bucket.env;
3395
+ }
3396
+ applyRemovalPolicy(policy) {
3397
+ this.bucket.applyRemovalPolicy(policy);
3398
+ }
3399
+ get bucketRef() {
3400
+ return {
3401
+ bucketArn: this.bucket.bucketArn,
3402
+ bucketName: this.bucket.bucketName,
3403
+ };
3404
+ }
3405
+ }
3406
+
3407
+ class JaypieStaticWebBucket extends JaypieWebDeploymentBucket {
3408
+ constructor(scope, id, props = {}) {
3409
+ // Handle overloaded signatures: (scope), (scope, props), (scope, id, props)
3410
+ let resolvedId;
3411
+ let resolvedProps;
3412
+ if (typeof id === "string") {
3413
+ resolvedId = id;
3414
+ resolvedProps = props;
3415
+ }
3416
+ else if (typeof id === "object") {
3417
+ resolvedId = "JaypieStaticWebBucket";
3418
+ resolvedProps = id;
3419
+ }
3420
+ else {
3421
+ resolvedId = "JaypieStaticWebBucket";
3422
+ resolvedProps = props;
3423
+ }
3424
+ const host = resolvedProps.host ?? envHostname({ subdomain: "static" });
3425
+ const name = resolvedProps.name ?? constructEnvName("static");
3426
+ const roleTag = resolvedProps.roleTag ?? CDK$2.ROLE.HOSTING;
3427
+ // Only use default zone if zone is not explicitly provided (including undefined)
3428
+ const zone = "zone" in resolvedProps
3429
+ ? resolvedProps.zone
3430
+ : process.env.CDK_ENV_DOMAIN || process.env.CDK_ENV_HOSTED_ZONE;
3431
+ super(scope, resolvedId, {
3432
+ ...resolvedProps,
3433
+ host,
3434
+ name,
3435
+ roleTag,
3436
+ zone,
3437
+ });
3438
+ }
3439
+ }
3440
+
3441
+ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
3442
+ constructor(scope, id = "TraceSigningKey", props) {
3443
+ const defaultProps = {
3444
+ envKey: "TRACE_SIGNING_KEY",
3445
+ roleTag: CDK$2.ROLE.API,
3446
+ vendorTag: CDK$2.VENDOR.KNOWTRACE,
3447
+ ...props,
3448
+ };
3449
+ super(scope, id, defaultProps);
3450
+ }
3451
+ }
3452
+
3453
+ exports.CDK = CDK$2;
3454
+ exports.JaypieAccountLoggingBucket = JaypieAccountLoggingBucket;
3455
+ exports.JaypieApiGateway = JaypieApiGateway;
3456
+ exports.JaypieAppStack = JaypieAppStack;
3457
+ exports.JaypieBucketQueuedLambda = JaypieBucketQueuedLambda;
3458
+ exports.JaypieDatadogBucket = JaypieDatadogBucket;
3459
+ exports.JaypieDatadogForwarder = JaypieDatadogForwarder;
3460
+ exports.JaypieDatadogSecret = JaypieDatadogSecret;
3461
+ exports.JaypieDistribution = JaypieDistribution;
3462
+ exports.JaypieDnsRecord = JaypieDnsRecord;
3463
+ exports.JaypieEnvSecret = JaypieEnvSecret;
3464
+ exports.JaypieEventsRule = JaypieEventsRule;
3465
+ exports.JaypieExpressLambda = JaypieExpressLambda;
3466
+ exports.JaypieGitHubDeployRole = JaypieGitHubDeployRole;
3467
+ exports.JaypieHostedZone = JaypieHostedZone;
3468
+ exports.JaypieInfrastructureStack = JaypieInfrastructureStack;
3469
+ exports.JaypieLambda = JaypieLambda;
3470
+ exports.JaypieMongoDbSecret = JaypieMongoDbSecret;
3471
+ exports.JaypieNextJs = JaypieNextJs;
3472
+ exports.JaypieOpenAiSecret = JaypieOpenAiSecret;
3473
+ exports.JaypieOrganizationTrail = JaypieOrganizationTrail;
3474
+ exports.JaypieQueuedLambda = JaypieQueuedLambda;
3475
+ exports.JaypieSsoPermissions = JaypieSsoPermissions;
3476
+ exports.JaypieSsoSyncApplication = JaypieSsoSyncApplication;
3477
+ exports.JaypieStack = JaypieStack;
3478
+ exports.JaypieStaticWebBucket = JaypieStaticWebBucket;
3479
+ exports.JaypieTraceSigningKeySecret = JaypieTraceSigningKeySecret;
3480
+ exports.JaypieWebDeploymentBucket = JaypieWebDeploymentBucket;
3481
+ exports.addDatadogLayers = addDatadogLayers;
3482
+ exports.clearAllSecretsCaches = clearAllSecretsCaches;
3483
+ exports.clearSecretsCache = clearSecretsCache;
3484
+ exports.constructEnvName = constructEnvName;
3485
+ exports.constructStackName = constructStackName;
3486
+ exports.constructTagger = constructTagger;
3487
+ exports.envHostname = envHostname;
3488
+ exports.extendDatadogRole = extendDatadogRole;
3489
+ exports.isEnv = isEnv;
3490
+ exports.isProductionEnv = isProductionEnv;
3491
+ exports.isSandboxEnv = isSandboxEnv;
3492
+ exports.isValidHostname = isValidHostname$1;
3493
+ exports.isValidSubdomain = isValidSubdomain;
3494
+ exports.jaypieLambdaEnv = jaypieLambdaEnv;
3495
+ exports.mergeDomain = mergeDomain;
3496
+ exports.resolveDatadogForwarderFunction = resolveDatadogForwarderFunction;
3497
+ exports.resolveDatadogLayers = resolveDatadogLayers;
3498
+ exports.resolveDatadogLoggingDestination = resolveDatadogLoggingDestination;
3499
+ exports.resolveEnvironment = resolveEnvironment;
3500
+ exports.resolveHostedZone = resolveHostedZone;
3501
+ exports.resolveParamsAndSecrets = resolveParamsAndSecrets;
3502
+ exports.resolveSecrets = resolveSecrets;
3503
+ //# sourceMappingURL=index.cjs.map