@jaypie/constructs 1.2.17 → 1.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as cdk from 'aws-cdk-lib';
2
- import { Tags, Stack, Fn, CfnOutput, SecretValue, Duration, RemovalPolicy, CfnStack } from 'aws-cdk-lib';
2
+ import { Tags, Stack, Fn, CfnOutput, SecretValue, Duration, RemovalPolicy, CfnStack, ArnFormat } from 'aws-cdk-lib';
3
3
  import * as s3 from 'aws-cdk-lib/aws-s3';
4
4
  import { Bucket, StorageClass, BucketAccessControl, EventType } from 'aws-cdk-lib/aws-s3';
5
5
  import { Construct } from 'constructs';
@@ -10,6 +10,7 @@ import * as route53Targets from 'aws-cdk-lib/aws-route53-targets';
10
10
  import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
11
11
  import { DatadogLambda } from 'datadog-cdk-constructs-v2';
12
12
  import { ConfigurationError } from '@jaypie/errors';
13
+ import * as iam from 'aws-cdk-lib/aws-iam';
13
14
  import { Role, PolicyStatement, Policy, FederatedPrincipal, Effect, ServicePrincipal, ManagedPolicy } from 'aws-cdk-lib/aws-iam';
14
15
  import * as acm from 'aws-cdk-lib/aws-certificatemanager';
15
16
  import * as lambda from 'aws-cdk-lib/aws-lambda';
@@ -31,6 +32,8 @@ import * as path from 'path';
31
32
  import { Trail, ReadWriteType } from 'aws-cdk-lib/aws-cloudtrail';
32
33
  import { CfnPermissionSet, CfnAssignment } from 'aws-cdk-lib/aws-sso';
33
34
  import { CfnApplication } from 'aws-cdk-lib/aws-sam';
35
+ import * as apigatewayv2 from 'aws-cdk-lib/aws-apigatewayv2';
36
+ import * as apigatewayv2Integrations from 'aws-cdk-lib/aws-apigatewayv2-integrations';
34
37
 
35
38
  const CDK$2 = {
36
39
  ACCOUNT: {
@@ -3185,13 +3188,19 @@ class JaypieMongoDbSecret extends JaypieEnvSecret {
3185
3188
  class JaypieNextJs extends Construct {
3186
3189
  constructor(scope, id, props) {
3187
3190
  super(scope, id);
3188
- const domainName = typeof props?.domainName === "string"
3189
- ? props.domainName
3190
- : envHostname(props?.domainName);
3191
+ // Determine if we should use a custom domain
3192
+ const useDomain = props?.domainProps !== false;
3193
+ // Resolve domain name only if using a custom domain
3194
+ const domainName = useDomain
3195
+ ? typeof props?.domainName === "string"
3196
+ ? props.domainName
3197
+ : envHostname(props?.domainName)
3198
+ : undefined;
3191
3199
  this.domainName = domainName;
3192
- const domainNameSanitized = domainName
3193
- .replace(/\./g, "-")
3194
- .replace(/[^a-zA-Z0-9]/g, "_");
3200
+ // Use domain name or construct ID for cache policy naming
3201
+ const cachePolicyIdentifier = domainName
3202
+ ? domainName.replace(/\./g, "-").replace(/[^a-zA-Z0-9]/g, "_")
3203
+ : id.replace(/[^a-zA-Z0-9]/g, "_");
3195
3204
  // Resolve environment from array or object syntax
3196
3205
  const environment = resolveEnvironment(props?.environment);
3197
3206
  const envSecrets = props?.envSecrets || {};
@@ -3228,27 +3237,32 @@ class JaypieNextJs extends Construct {
3228
3237
  }, {});
3229
3238
  const nextjs = new Nextjs(this, "NextJsApp", {
3230
3239
  nextjsPath,
3231
- domainProps: {
3232
- domainName,
3233
- hostedZone: resolveHostedZone(this, {
3234
- zone: props?.hostedZone,
3235
- }),
3236
- },
3240
+ // Only configure custom domain if useDomain is true
3241
+ ...(useDomain &&
3242
+ domainName && {
3243
+ domainProps: {
3244
+ domainName,
3245
+ hostedZone: resolveHostedZone(this, {
3246
+ zone: props?.hostedZone,
3247
+ }),
3248
+ },
3249
+ }),
3237
3250
  environment: {
3238
3251
  ...jaypieLambdaEnv(),
3239
3252
  ...environment,
3240
3253
  ...secretsEnvironment,
3241
3254
  ...jaypieSecretsEnvironment,
3242
3255
  ...nextPublicEnv,
3243
- NEXT_PUBLIC_SITE_URL: `https://${domainName}`,
3256
+ // NEXT_PUBLIC_SITE_URL will be set after construct creation for CloudFront URL
3257
+ ...(domainName && { NEXT_PUBLIC_SITE_URL: `https://${domainName}` }),
3244
3258
  },
3245
3259
  overrides: {
3246
3260
  nextjsDistribution: {
3247
3261
  imageCachePolicyProps: {
3248
- cachePolicyName: `NextJsImageCachePolicy-${domainNameSanitized}`,
3262
+ cachePolicyName: `NextJsImageCachePolicy-${cachePolicyIdentifier}`,
3249
3263
  },
3250
3264
  serverCachePolicyProps: {
3251
- cachePolicyName: `NextJsServerCachePolicy-${domainNameSanitized}`,
3265
+ cachePolicyName: `NextJsServerCachePolicy-${cachePolicyIdentifier}`,
3252
3266
  },
3253
3267
  },
3254
3268
  nextjsImage: {
@@ -3263,6 +3277,10 @@ class JaypieNextJs extends Construct {
3263
3277
  },
3264
3278
  },
3265
3279
  });
3280
+ // Set NEXT_PUBLIC_SITE_URL to CloudFront URL when no custom domain
3281
+ if (!domainName) {
3282
+ nextjs.serverFunction.lambdaFunction.addEnvironment("NEXT_PUBLIC_SITE_URL", `https://${nextjs.distribution.distributionDomain}`);
3283
+ }
3266
3284
  addDatadogLayers(nextjs.imageOptimizationFunction);
3267
3285
  addDatadogLayers(nextjs.serverFunction.lambdaFunction);
3268
3286
  // Grant secret read permissions
@@ -4103,5 +4121,376 @@ class JaypieTraceSigningKeySecret extends JaypieEnvSecret {
4103
4121
  }
4104
4122
  }
4105
4123
 
4106
- export { CDK$2 as CDK, JaypieAccountLoggingBucket, JaypieApiGateway, JaypieAppStack, JaypieBucketQueuedLambda, JaypieCertificate, JaypieDatadogBucket, JaypieDatadogForwarder, JaypieDatadogSecret, JaypieDistribution, JaypieDnsRecord, JaypieDynamoDb, JaypieEnvSecret, JaypieEventsRule, JaypieExpressLambda, JaypieGitHubDeployRole, JaypieHostedZone, JaypieInfrastructureStack, JaypieLambda, JaypieMongoDbSecret, JaypieNextJs, JaypieOpenAiSecret, JaypieOrganizationTrail, JaypieQueuedLambda, JaypieSsoPermissions, JaypieSsoSyncApplication, JaypieStack, JaypieStaticWebBucket, JaypieTraceSigningKeySecret, JaypieWebDeploymentBucket, addDatadogLayers, clearAllCertificateCaches, clearAllSecretsCaches, clearCertificateCache, clearSecretsCache, constructEnvName, constructStackName, constructTagger, envHostname, extendDatadogRole, isEnv, isProductionEnv, isSandboxEnv, isValidHostname$1 as isValidHostname, isValidSubdomain, jaypieLambdaEnv, mergeDomain, resolveCertificate, resolveDatadogForwarderFunction, resolveDatadogLayers, resolveDatadogLoggingDestination, resolveEnvironment, resolveHostedZone, resolveParamsAndSecrets, resolveSecrets };
4124
+ //
4125
+ //
4126
+ // Main
4127
+ //
4128
+ class JaypieWebSocket extends Construct {
4129
+ constructor(scope, id, props = {}) {
4130
+ super(scope, id);
4131
+ const { certificate = true, connect, default: defaultHandler, disconnect, handler, host: propsHost, logRetention = logs.RetentionDays.THREE_MONTHS, name, roleTag = CDK$2.ROLE.API, routes = {}, stageName = "production", zone: propsZone, } = props;
4132
+ // Validate: either handler OR individual handlers, not both
4133
+ const hasIndividualHandlers = connect || disconnect || defaultHandler;
4134
+ if (handler && hasIndividualHandlers) {
4135
+ throw new Error("Cannot specify both 'handler' and individual route handlers (connect/disconnect/default)");
4136
+ }
4137
+ // Determine zone from props or environment
4138
+ let zone = propsZone;
4139
+ if (!zone && process.env.CDK_ENV_HOSTED_ZONE) {
4140
+ zone = process.env.CDK_ENV_HOSTED_ZONE;
4141
+ }
4142
+ // Determine host from props or environment
4143
+ let host;
4144
+ if (typeof propsHost === "string") {
4145
+ host = propsHost;
4146
+ }
4147
+ else if (typeof propsHost === "object") {
4148
+ // Resolve host from HostConfig using envHostname()
4149
+ host = envHostname(propsHost);
4150
+ }
4151
+ else if (process.env.CDK_ENV_WS_HOST_NAME) {
4152
+ host = process.env.CDK_ENV_WS_HOST_NAME;
4153
+ }
4154
+ else if (process.env.CDK_ENV_WS_SUBDOMAIN &&
4155
+ process.env.CDK_ENV_HOSTED_ZONE) {
4156
+ host = mergeDomain(process.env.CDK_ENV_WS_SUBDOMAIN, process.env.CDK_ENV_HOSTED_ZONE);
4157
+ }
4158
+ const apiName = name || constructEnvName("WebSocket");
4159
+ // Create WebSocket API
4160
+ this._api = new apigatewayv2.WebSocketApi(this, "Api", {
4161
+ apiName,
4162
+ });
4163
+ Tags.of(this._api).add(CDK$2.TAG.ROLE, roleTag);
4164
+ // Add routes with Lambda integrations
4165
+ const connectHandler = handler || connect;
4166
+ const disconnectHandler = handler || disconnect;
4167
+ const defaultRouteHandler = handler || defaultHandler;
4168
+ if (connectHandler) {
4169
+ this._api.addRoute("$connect", {
4170
+ integration: new apigatewayv2Integrations.WebSocketLambdaIntegration("ConnectIntegration", connectHandler),
4171
+ });
4172
+ }
4173
+ if (disconnectHandler) {
4174
+ this._api.addRoute("$disconnect", {
4175
+ integration: new apigatewayv2Integrations.WebSocketLambdaIntegration("DisconnectIntegration", disconnectHandler),
4176
+ });
4177
+ }
4178
+ if (defaultRouteHandler) {
4179
+ this._api.addRoute("$default", {
4180
+ integration: new apigatewayv2Integrations.WebSocketLambdaIntegration("DefaultIntegration", defaultRouteHandler),
4181
+ });
4182
+ }
4183
+ // Add custom routes
4184
+ for (const [routeKey, routeHandler] of Object.entries(routes)) {
4185
+ this._api.addRoute(routeKey, {
4186
+ integration: new apigatewayv2Integrations.WebSocketLambdaIntegration(`${routeKey}Integration`, routeHandler),
4187
+ });
4188
+ }
4189
+ // Create log group for access logs
4190
+ // Note: logGroup is created for future use when API Gateway v2 WebSocket
4191
+ // access logging is fully supported in CDK
4192
+ new logs.LogGroup(this, "AccessLogs", {
4193
+ removalPolicy: RemovalPolicy.DESTROY,
4194
+ retention: logRetention,
4195
+ });
4196
+ // Create stage
4197
+ this._stage = new apigatewayv2.WebSocketStage(this, "Stage", {
4198
+ autoDeploy: true,
4199
+ stageName,
4200
+ webSocketApi: this._api,
4201
+ });
4202
+ Tags.of(this._stage).add(CDK$2.TAG.ROLE, roleTag);
4203
+ // Set up custom domain if host and zone are provided
4204
+ let hostedZone;
4205
+ let certificateToUse;
4206
+ if (host && zone) {
4207
+ hostedZone = resolveHostedZone(this, { zone });
4208
+ // Use resolveCertificate to create certificate at stack level (enables reuse)
4209
+ certificateToUse = resolveCertificate(this, {
4210
+ certificate,
4211
+ domainName: host,
4212
+ roleTag: CDK$2.ROLE.HOSTING,
4213
+ zone: hostedZone,
4214
+ });
4215
+ this._certificate = certificateToUse;
4216
+ this._host = host;
4217
+ if (certificateToUse) {
4218
+ // Create custom domain
4219
+ this._domainName = new apigatewayv2.DomainName(this, "DomainName", {
4220
+ certificate: certificateToUse,
4221
+ domainName: host,
4222
+ });
4223
+ Tags.of(this._domainName).add(CDK$2.TAG.ROLE, roleTag);
4224
+ // Map domain to stage
4225
+ new apigatewayv2.ApiMapping(this, "ApiMapping", {
4226
+ api: this._api,
4227
+ domainName: this._domainName,
4228
+ stage: this._stage,
4229
+ });
4230
+ // Create DNS record
4231
+ new route53.ARecord(this, "AliasRecord", {
4232
+ recordName: host,
4233
+ target: route53.RecordTarget.fromAlias(new route53Targets.ApiGatewayv2DomainProperties(this._domainName.regionalDomainName, this._domainName.regionalHostedZoneId)),
4234
+ zone: hostedZone,
4235
+ });
4236
+ // Also create AAAA record for IPv6
4237
+ new route53.AaaaRecord(this, "AaaaAliasRecord", {
4238
+ recordName: host,
4239
+ target: route53.RecordTarget.fromAlias(new route53Targets.ApiGatewayv2DomainProperties(this._domainName.regionalDomainName, this._domainName.regionalHostedZoneId)),
4240
+ zone: hostedZone,
4241
+ });
4242
+ }
4243
+ }
4244
+ // Grant all handlers permission to manage connections
4245
+ const allHandlers = new Set();
4246
+ if (connectHandler)
4247
+ allHandlers.add(connectHandler);
4248
+ if (disconnectHandler)
4249
+ allHandlers.add(disconnectHandler);
4250
+ if (defaultRouteHandler)
4251
+ allHandlers.add(defaultRouteHandler);
4252
+ Object.values(routes).forEach((h) => allHandlers.add(h));
4253
+ for (const lambdaHandler of allHandlers) {
4254
+ this.grantManageConnections(lambdaHandler);
4255
+ }
4256
+ }
4257
+ //
4258
+ //
4259
+ // Public accessors
4260
+ //
4261
+ get api() {
4262
+ return this._api;
4263
+ }
4264
+ get apiId() {
4265
+ return this._api.apiId;
4266
+ }
4267
+ get certificate() {
4268
+ return this._certificate;
4269
+ }
4270
+ get domainName() {
4271
+ return this._domainName?.name;
4272
+ }
4273
+ /**
4274
+ * The WebSocket endpoint URL.
4275
+ * Uses custom domain if configured, otherwise returns the default stage URL.
4276
+ */
4277
+ get endpoint() {
4278
+ if (this._host) {
4279
+ return `wss://${this._host}`;
4280
+ }
4281
+ return this._stage.url;
4282
+ }
4283
+ get host() {
4284
+ return this._host;
4285
+ }
4286
+ get stage() {
4287
+ return this._stage;
4288
+ }
4289
+ /**
4290
+ * The callback URL for API Gateway Management API.
4291
+ * Use this URL to send messages to connected clients.
4292
+ */
4293
+ get callbackUrl() {
4294
+ if (this._host) {
4295
+ return `https://${this._host}`;
4296
+ }
4297
+ // Extract callback URL from stage URL
4298
+ // Stage URL: wss://abc123.execute-api.us-east-1.amazonaws.com/production
4299
+ // Callback URL: https://abc123.execute-api.us-east-1.amazonaws.com/production
4300
+ return this._stage.url.replace("wss://", "https://");
4301
+ }
4302
+ //
4303
+ //
4304
+ // Public methods
4305
+ //
4306
+ /**
4307
+ * Grant a Lambda function permission to manage WebSocket connections
4308
+ * (post to connections, delete connections).
4309
+ */
4310
+ grantManageConnections(grantee) {
4311
+ return iam.Grant.addToPrincipal({
4312
+ actions: ["execute-api:ManageConnections"],
4313
+ grantee: grantee.grantPrincipal,
4314
+ resourceArns: [
4315
+ Stack.of(this).formatArn({
4316
+ arnFormat: ArnFormat.SLASH_RESOURCE_SLASH_RESOURCE_NAME,
4317
+ resource: this._api.apiId,
4318
+ resourceName: `${this._stage.stageName}/*`,
4319
+ service: "execute-api",
4320
+ }),
4321
+ ],
4322
+ });
4323
+ }
4324
+ }
4325
+
4326
+ /**
4327
+ * JaypieWebSocketLambda - A Lambda function optimized for WebSocket handlers.
4328
+ *
4329
+ * Provides sensible defaults for WebSocket event handling:
4330
+ * - 30 second timeout (same as API handlers)
4331
+ * - API role tag
4332
+ *
4333
+ * @example
4334
+ * ```typescript
4335
+ * const handler = new JaypieWebSocketLambda(this, "ChatHandler", {
4336
+ * code: "dist/handlers",
4337
+ * handler: "chat.handler",
4338
+ * secrets: ["MONGODB_URI"],
4339
+ * });
4340
+ *
4341
+ * new JaypieWebSocket(this, "Chat", {
4342
+ * host: "ws.example.com",
4343
+ * handler,
4344
+ * });
4345
+ * ```
4346
+ */
4347
+ class JaypieWebSocketLambda extends JaypieLambda {
4348
+ constructor(scope, id, props) {
4349
+ super(scope, id, {
4350
+ roleTag: CDK$2.ROLE.API,
4351
+ timeout: Duration.seconds(CDK$2.DURATION.EXPRESS_API),
4352
+ ...props,
4353
+ });
4354
+ }
4355
+ }
4356
+
4357
+ //
4358
+ //
4359
+ // Main
4360
+ //
4361
+ /**
4362
+ * JaypieWebSocketTable - DynamoDB table for storing WebSocket connection IDs.
4363
+ *
4364
+ * Provides a simple table structure for tracking active WebSocket connections:
4365
+ * - Partition key: connectionId (String)
4366
+ * - TTL attribute: expiresAt (for automatic cleanup)
4367
+ * - Optional GSI: userId-index (for looking up connections by user)
4368
+ *
4369
+ * @example
4370
+ * ```typescript
4371
+ * const connectionTable = new JaypieWebSocketTable(this, "Connections");
4372
+ *
4373
+ * const ws = new JaypieWebSocket(this, "Chat", {
4374
+ * host: "ws.example.com",
4375
+ * handler: chatHandler,
4376
+ * });
4377
+ *
4378
+ * // Grant Lambda access to the table
4379
+ * connectionTable.grantReadWriteData(chatHandler);
4380
+ *
4381
+ * // Pass table name to Lambda
4382
+ * chatHandler.addEnvironment("CONNECTION_TABLE", connectionTable.tableName);
4383
+ * ```
4384
+ *
4385
+ * @example
4386
+ * // With user index for looking up all connections for a user
4387
+ * const connectionTable = new JaypieWebSocketTable(this, "Connections", {
4388
+ * userIndex: true,
4389
+ * ttl: Duration.hours(12),
4390
+ * });
4391
+ */
4392
+ class JaypieWebSocketTable extends Construct {
4393
+ constructor(scope, id, props = {}) {
4394
+ super(scope, id);
4395
+ const { roleTag = CDK$2.ROLE.STORAGE, tableName, ttl = Duration.hours(24), userIndex = false, } = props;
4396
+ this._ttlDuration = ttl;
4397
+ // Build global secondary indexes
4398
+ const globalSecondaryIndexes = [];
4399
+ if (userIndex) {
4400
+ globalSecondaryIndexes.push({
4401
+ indexName: "userId-index",
4402
+ partitionKey: { name: "userId", type: dynamodb.AttributeType.STRING },
4403
+ sortKey: { name: "connectedAt", type: dynamodb.AttributeType.STRING },
4404
+ });
4405
+ }
4406
+ // Create the table
4407
+ this._table = new dynamodb.TableV2(this, "Table", {
4408
+ billing: dynamodb.Billing.onDemand(),
4409
+ globalSecondaryIndexes: globalSecondaryIndexes.length > 0 ? globalSecondaryIndexes : undefined,
4410
+ partitionKey: {
4411
+ name: "connectionId",
4412
+ type: dynamodb.AttributeType.STRING,
4413
+ },
4414
+ removalPolicy: RemovalPolicy.DESTROY,
4415
+ tableName: tableName || constructEnvName("WebSocketConnections"),
4416
+ timeToLiveAttribute: "expiresAt",
4417
+ });
4418
+ Tags.of(this._table).add(CDK$2.TAG.ROLE, roleTag);
4419
+ }
4420
+ //
4421
+ //
4422
+ // Public accessors
4423
+ //
4424
+ /**
4425
+ * The underlying DynamoDB TableV2 construct.
4426
+ */
4427
+ get table() {
4428
+ return this._table;
4429
+ }
4430
+ /**
4431
+ * The name of the DynamoDB table.
4432
+ */
4433
+ get tableName() {
4434
+ return this._table.tableName;
4435
+ }
4436
+ /**
4437
+ * The ARN of the DynamoDB table.
4438
+ */
4439
+ get tableArn() {
4440
+ return this._table.tableArn;
4441
+ }
4442
+ /**
4443
+ * TTL duration for connections in seconds.
4444
+ * Use this to calculate expiresAt when storing connections.
4445
+ */
4446
+ get ttlSeconds() {
4447
+ return this._ttlDuration.toSeconds();
4448
+ }
4449
+ //
4450
+ //
4451
+ // Grant methods
4452
+ //
4453
+ /**
4454
+ * Grant read permissions to the table.
4455
+ */
4456
+ grantReadData(grantee) {
4457
+ return this._table.grantReadData(grantee);
4458
+ }
4459
+ /**
4460
+ * Grant write permissions to the table.
4461
+ */
4462
+ grantWriteData(grantee) {
4463
+ return this._table.grantWriteData(grantee);
4464
+ }
4465
+ /**
4466
+ * Grant read and write permissions to the table.
4467
+ */
4468
+ grantReadWriteData(grantee) {
4469
+ return this._table.grantReadWriteData(grantee);
4470
+ }
4471
+ //
4472
+ //
4473
+ // Convenience methods
4474
+ //
4475
+ /**
4476
+ * Add the table name to a Lambda function's environment variables.
4477
+ * Also grants read/write access to the table.
4478
+ */
4479
+ connectLambda(lambdaFunction, options = {}) {
4480
+ const { envKey = "CONNECTION_TABLE", readOnly = false } = options;
4481
+ // Add environment variable
4482
+ if ("addEnvironment" in lambdaFunction) {
4483
+ lambdaFunction.addEnvironment(envKey, this.tableName);
4484
+ }
4485
+ // Grant permissions
4486
+ if (readOnly) {
4487
+ this.grantReadData(lambdaFunction.grantPrincipal);
4488
+ }
4489
+ else {
4490
+ this.grantReadWriteData(lambdaFunction.grantPrincipal);
4491
+ }
4492
+ }
4493
+ }
4494
+
4495
+ export { CDK$2 as CDK, JaypieAccountLoggingBucket, JaypieApiGateway, JaypieAppStack, JaypieBucketQueuedLambda, JaypieCertificate, JaypieDatadogBucket, JaypieDatadogForwarder, JaypieDatadogSecret, JaypieDistribution, JaypieDnsRecord, JaypieDynamoDb, JaypieEnvSecret, JaypieEventsRule, JaypieExpressLambda, JaypieGitHubDeployRole, JaypieHostedZone, JaypieInfrastructureStack, JaypieLambda, JaypieMongoDbSecret, JaypieNextJs, JaypieOpenAiSecret, JaypieOrganizationTrail, JaypieQueuedLambda, JaypieSsoPermissions, JaypieSsoSyncApplication, JaypieStack, JaypieStaticWebBucket, JaypieTraceSigningKeySecret, JaypieWebDeploymentBucket, JaypieWebSocket, JaypieWebSocketLambda, JaypieWebSocketTable, addDatadogLayers, clearAllCertificateCaches, clearAllSecretsCaches, clearCertificateCache, clearSecretsCache, constructEnvName, constructStackName, constructTagger, envHostname, extendDatadogRole, isEnv, isProductionEnv, isSandboxEnv, isValidHostname$1 as isValidHostname, isValidSubdomain, jaypieLambdaEnv, mergeDomain, resolveCertificate, resolveDatadogForwarderFunction, resolveDatadogLayers, resolveDatadogLoggingDestination, resolveEnvironment, resolveHostedZone, resolveParamsAndSecrets, resolveSecrets };
4107
4496
  //# sourceMappingURL=index.js.map