@jaypie/constructs 1.2.11 → 1.2.13

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.
@@ -3,9 +3,26 @@ import { RemovalPolicy, Stack } from "aws-cdk-lib";
3
3
  import * as acm from "aws-cdk-lib/aws-certificatemanager";
4
4
  import * as apiGateway from "aws-cdk-lib/aws-apigateway";
5
5
  import * as route53 from "aws-cdk-lib/aws-route53";
6
+ import { HostConfig } from "./helpers";
6
7
  export interface JaypieApiGatewayProps extends apiGateway.LambdaRestApiProps {
7
8
  certificate?: boolean | acm.ICertificate;
8
- host?: string;
9
+ /**
10
+ * The domain name for the API Gateway.
11
+ *
12
+ * Supports both string and config object:
13
+ * - String: used directly as the domain name (e.g., "api.example.com")
14
+ * - Object: passed to envHostname() to construct the domain name
15
+ * - { subdomain, domain, env, component }
16
+ *
17
+ * @example
18
+ * // Direct string
19
+ * host: "api.example.com"
20
+ *
21
+ * @example
22
+ * // Config object - resolves using envHostname()
23
+ * host: { subdomain: "api" }
24
+ */
25
+ host?: string | HostConfig;
9
26
  name?: string;
10
27
  roleTag?: string;
11
28
  zone?: string | route53.IHostedZone;
@@ -6,7 +6,8 @@ import * as route53 from "aws-cdk-lib/aws-route53";
6
6
  import * as s3 from "aws-cdk-lib/aws-s3";
7
7
  import { LambdaDestination } from "aws-cdk-lib/aws-s3-notifications";
8
8
  import { Construct } from "constructs";
9
- export interface JaypieDistributionProps extends Omit<cloudfront.DistributionProps, "certificate" | "defaultBehavior"> {
9
+ import { HostConfig } from "./helpers";
10
+ export interface JaypieDistributionProps extends Omit<cloudfront.DistributionProps, "certificate" | "defaultBehavior" | "logBucket"> {
10
11
  /**
11
12
  * SSL certificate for the CloudFront distribution
12
13
  * @default true (creates a new certificate)
@@ -20,20 +21,45 @@ export interface JaypieDistributionProps extends Omit<cloudfront.DistributionPro
20
21
  * Log destination configuration for CloudFront access logs
21
22
  * - LambdaDestination: Use a specific Lambda destination for S3 notifications
22
23
  * - true: Use Datadog forwarder for S3 notifications (default)
23
- * - false: Disable logging entirely
24
+ * - false: Disable S3 notifications (logging still occurs if logBucket is set)
24
25
  * @default true
25
26
  */
26
27
  destination?: LambdaDestination | boolean;
28
+ /**
29
+ * External log bucket for CloudFront access logs.
30
+ * - IBucket: Use existing bucket directly
31
+ * - string: Bucket name to import
32
+ * - { exportName: string }: CloudFormation export name to import
33
+ * - true: Use account logging bucket (CDK.IMPORT.LOG_BUCKET)
34
+ * @default undefined (creates new bucket if destination !== false)
35
+ */
36
+ logBucket?: s3.IBucket | string | {
37
+ exportName: string;
38
+ } | true;
27
39
  /**
28
40
  * The origin handler - can be an IOrigin, IFunctionUrl, or IFunction
29
41
  * If IFunction, a FunctionUrl will be created with auth NONE
30
42
  */
31
43
  handler?: cloudfront.IOrigin | lambda.IFunctionUrl | lambda.IFunction;
32
44
  /**
33
- * The domain name for the distribution
45
+ * The domain name for the distribution.
46
+ *
47
+ * Supports both string and config object:
48
+ * - String: used directly as the domain name (e.g., "api.example.com")
49
+ * - Object: passed to envHostname() to construct the domain name
50
+ * - { subdomain, domain, env, component }
51
+ *
34
52
  * @default mergeDomain(CDK_ENV_API_SUBDOMAIN, CDK_ENV_API_HOSTED_ZONE || CDK_ENV_HOSTED_ZONE)
53
+ *
54
+ * @example
55
+ * // Direct string
56
+ * host: "api.example.com"
57
+ *
58
+ * @example
59
+ * // Config object - resolves using envHostname()
60
+ * host: { subdomain: "api" }
35
61
  */
36
- host?: string;
62
+ host?: string | HostConfig;
37
63
  /**
38
64
  * Invoke mode for Lambda Function URLs.
39
65
  * If not provided, auto-detects from handler if it has an invokeMode property
@@ -74,6 +100,8 @@ export declare class JaypieDistribution extends Construct implements cloudfront.
74
100
  private isIFunctionUrl;
75
101
  private isIFunction;
76
102
  private hasInvokeMode;
103
+ private isExportNameObject;
104
+ private resolveLogBucket;
77
105
  get env(): {
78
106
  account: string;
79
107
  region: string;
@@ -4,7 +4,7 @@ import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
4
4
  export interface JaypieDynamoDbProps extends Omit<dynamodb.TablePropsV2, "globalSecondaryIndexes" | "partitionKey" | "sortKey"> {
5
5
  /**
6
6
  * Configure GSIs for the table.
7
- * - `undefined` or `true`: Creates all five Jaypie GSIs (Alias, Class, Ou, Type, Xid)
7
+ * - `undefined` or `true`: Creates all five Jaypie GSIs (Alias, Class, Scope, Type, Xid)
8
8
  * - `false`: No GSIs
9
9
  * - Array: Use the specified GSIs
10
10
  */
@@ -61,7 +61,7 @@ export interface JaypieDynamoDbProps extends Omit<dynamodb.TablePropsV2, "global
61
61
  * // Use only specific GSIs
62
62
  * const table = new JaypieDynamoDb(this, "MyTable", {
63
63
  * globalSecondaryIndexes: [
64
- * JaypieDynamoDb.GlobalSecondaryIndex.Ou,
64
+ * JaypieDynamoDb.GlobalSecondaryIndex.Scope,
65
65
  * JaypieDynamoDb.GlobalSecondaryIndex.Type,
66
66
  * ],
67
67
  * });
@@ -73,7 +73,7 @@ export declare class JaypieDynamoDb extends Construct implements dynamodb.ITable
73
73
  static readonly GlobalSecondaryIndex: {
74
74
  readonly Alias: dynamodb.GlobalSecondaryIndexPropsV2;
75
75
  readonly Class: dynamodb.GlobalSecondaryIndexPropsV2;
76
- readonly Ou: dynamodb.GlobalSecondaryIndexPropsV2;
76
+ readonly Scope: dynamodb.GlobalSecondaryIndexPropsV2;
77
77
  readonly Type: dynamodb.GlobalSecondaryIndexPropsV2;
78
78
  readonly Xid: dynamodb.GlobalSecondaryIndexPropsV2;
79
79
  };
@@ -2,13 +2,11 @@ import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
2
2
  import { IHostedZone } from "aws-cdk-lib/aws-route53";
3
3
  import * as secretsmanager from "aws-cdk-lib/aws-secretsmanager";
4
4
  import { Construct } from "constructs";
5
- import { EnvironmentInput, SecretsArrayItem } from "./helpers";
6
- export interface DomainNameConfig {
7
- component?: string;
8
- domain?: string;
9
- env?: string;
10
- subdomain?: string;
11
- }
5
+ import { EnvironmentInput, HostConfig, SecretsArrayItem } from "./helpers";
6
+ /**
7
+ * @deprecated Use HostConfig instead. This alias is kept for backwards compatibility.
8
+ */
9
+ export type DomainNameConfig = HostConfig;
12
10
  export interface JaypieNextjsProps {
13
11
  datadogApiKeyArn?: string;
14
12
  /**
@@ -1,6 +1,11 @@
1
- export declare function envHostname({ component, domain, env, subdomain, }?: {
1
+ /**
2
+ * Configuration for resolving a hostname from parts.
3
+ * Used by envHostname() to construct domain names from environment and config.
4
+ */
5
+ export interface HostConfig {
2
6
  component?: string;
3
7
  domain?: string;
4
8
  env?: string;
5
9
  subdomain?: string;
6
- }): string;
10
+ }
11
+ export declare function envHostname({ component, domain, env, subdomain, }?: HostConfig): string;
@@ -2,7 +2,7 @@ export { addDatadogLayers } from "./addDatadogLayers";
2
2
  export { constructEnvName } from "./constructEnvName";
3
3
  export { constructStackName } from "./constructStackName";
4
4
  export { constructTagger } from "./constructTagger";
5
- export { envHostname } from "./envHostname";
5
+ export { envHostname, HostConfig } from "./envHostname";
6
6
  export { extendDatadogRole, ExtendDatadogRoleOptions, } from "./extendDatadogRole";
7
7
  export { clearAllCertificateCaches, clearCertificateCache, resolveCertificate, ResolveCertificateOptions, } from "./resolveCertificate";
8
8
  export { isEnv, isProductionEnv, isSandboxEnv } from "./isEnv";
@@ -1159,15 +1159,20 @@ class JaypieApiGateway extends constructs.Construct {
1159
1159
  zone = process.env.CDK_ENV_API_HOSTED_ZONE;
1160
1160
  }
1161
1161
  // Determine host from props or environment
1162
- let host = propsHost;
1163
- if (!host) {
1164
- if (process.env.CDK_ENV_API_HOST_NAME) {
1165
- host = process.env.CDK_ENV_API_HOST_NAME;
1166
- }
1167
- else if (process.env.CDK_ENV_API_SUBDOMAIN &&
1168
- process.env.CDK_ENV_API_HOSTED_ZONE) {
1169
- host = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE);
1170
- }
1162
+ let host;
1163
+ if (typeof propsHost === "string") {
1164
+ host = propsHost;
1165
+ }
1166
+ else if (typeof propsHost === "object") {
1167
+ // Resolve host from HostConfig using envHostname()
1168
+ host = envHostname(propsHost);
1169
+ }
1170
+ else if (process.env.CDK_ENV_API_HOST_NAME) {
1171
+ host = process.env.CDK_ENV_API_HOST_NAME;
1172
+ }
1173
+ else if (process.env.CDK_ENV_API_SUBDOMAIN &&
1174
+ process.env.CDK_ENV_API_HOSTED_ZONE) {
1175
+ host = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE);
1171
1176
  }
1172
1177
  const apiGatewayName = name || constructEnvName("ApiGateway");
1173
1178
  const apiDomainName = constructEnvName("ApiDomainName");
@@ -2357,7 +2362,7 @@ class JaypieDatadogSecret extends JaypieEnvSecret {
2357
2362
  class JaypieDistribution extends constructs.Construct {
2358
2363
  constructor(scope, id, props) {
2359
2364
  super(scope, id);
2360
- const { certificate: certificateProp = true, defaultBehavior: propsDefaultBehavior, destination: destinationProp = true, handler, host: propsHost, invokeMode = lambda__namespace.InvokeMode.BUFFERED, originReadTimeout = cdk.Duration.seconds(CDK$2.DURATION.CLOUDFRONT_API), roleTag = CDK$2.ROLE.API, zone: propsZone, ...distributionProps } = props;
2365
+ const { certificate: certificateProp = true, defaultBehavior: propsDefaultBehavior, destination: destinationProp = true, handler, host: propsHost, invokeMode = lambda__namespace.InvokeMode.BUFFERED, logBucket: logBucketProp, originReadTimeout = cdk.Duration.seconds(CDK$2.DURATION.CLOUDFRONT_API), roleTag = CDK$2.ROLE.API, zone: propsZone, ...distributionProps } = props;
2361
2366
  // Validate environment variables
2362
2367
  if (process.env.CDK_ENV_API_SUBDOMAIN &&
2363
2368
  !isValidSubdomain(process.env.CDK_ENV_API_SUBDOMAIN)) {
@@ -2372,8 +2377,20 @@ class JaypieDistribution extends constructs.Construct {
2372
2377
  throw new Error("CDK_ENV_HOSTED_ZONE is not a valid hostname");
2373
2378
  }
2374
2379
  // Determine host from props or environment
2375
- let host = propsHost;
2376
- if (!host) {
2380
+ let host;
2381
+ if (typeof propsHost === "string") {
2382
+ host = propsHost;
2383
+ }
2384
+ else if (typeof propsHost === "object") {
2385
+ // Resolve host from HostConfig using envHostname()
2386
+ try {
2387
+ host = envHostname(propsHost);
2388
+ }
2389
+ catch {
2390
+ host = undefined;
2391
+ }
2392
+ }
2393
+ else {
2377
2394
  try {
2378
2395
  if (process.env.CDK_ENV_API_HOST_NAME) {
2379
2396
  host = process.env.CDK_ENV_API_HOST_NAME;
@@ -2459,12 +2476,16 @@ class JaypieDistribution extends constructs.Construct {
2459
2476
  });
2460
2477
  this.certificate = certificateToUse;
2461
2478
  }
2462
- // Create log bucket if logging is enabled
2479
+ // Resolve or create log bucket
2463
2480
  let logBucket;
2464
- if (destinationProp !== false) {
2465
- logBucket = new s3__namespace.Bucket(this, constructEnvName("LogBucket"), {
2466
- objectOwnership: s3__namespace.ObjectOwnership.OBJECT_WRITER,
2467
- removalPolicy: cdk.RemovalPolicy.DESTROY,
2481
+ const isExternalBucket = logBucketProp !== undefined;
2482
+ if (logBucketProp !== undefined) {
2483
+ // Use external bucket
2484
+ logBucket = this.resolveLogBucket(logBucketProp);
2485
+ }
2486
+ else if (destinationProp !== false) {
2487
+ // Create new bucket (original behavior)
2488
+ const createdBucket = new s3__namespace.Bucket(this, constructEnvName("LogBucket"), {
2468
2489
  autoDeleteObjects: true,
2469
2490
  lifecycleRules: [
2470
2491
  {
@@ -2477,15 +2498,21 @@ class JaypieDistribution extends constructs.Construct {
2477
2498
  ],
2478
2499
  },
2479
2500
  ],
2501
+ objectOwnership: s3__namespace.ObjectOwnership.OBJECT_WRITER,
2502
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
2480
2503
  });
2481
- cdk.Tags.of(logBucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.STORAGE);
2482
- // Add S3 notification to Datadog forwarder
2504
+ cdk.Tags.of(createdBucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.STORAGE);
2505
+ logBucket = createdBucket;
2506
+ }
2507
+ // Add S3 notifications if we have a bucket and destination is not false
2508
+ if (logBucket && destinationProp !== false && !isExternalBucket) {
2509
+ // Only add notifications to buckets we created (not external buckets)
2483
2510
  const lambdaDestination = destinationProp === true
2484
2511
  ? new s3n.LambdaDestination(resolveDatadogForwarderFunction(this))
2485
2512
  : destinationProp;
2486
2513
  logBucket.addEventNotification(s3__namespace.EventType.OBJECT_CREATED, lambdaDestination);
2487
- this.logBucket = logBucket;
2488
2514
  }
2515
+ this.logBucket = logBucket;
2489
2516
  // Create the CloudFront distribution
2490
2517
  this.distribution = new cloudfront__namespace.Distribution(this, constructEnvName("Distribution"), {
2491
2518
  defaultBehavior,
@@ -2556,6 +2583,30 @@ class JaypieDistribution extends constructs.Construct {
2556
2583
  "invokeMode" in handler &&
2557
2584
  typeof handler.invokeMode === "string");
2558
2585
  }
2586
+ isExportNameObject(value) {
2587
+ return (typeof value === "object" &&
2588
+ value !== null &&
2589
+ "exportName" in value &&
2590
+ typeof value.exportName === "string");
2591
+ }
2592
+ resolveLogBucket(logBucketProp) {
2593
+ // true = use account logging bucket
2594
+ if (logBucketProp === true) {
2595
+ const bucketName = cdk.Fn.importValue(CDK$2.IMPORT.LOG_BUCKET);
2596
+ return s3__namespace.Bucket.fromBucketName(this, "ImportedLogBucket", bucketName);
2597
+ }
2598
+ // { exportName: string } = import from CloudFormation export
2599
+ if (this.isExportNameObject(logBucketProp)) {
2600
+ const bucketName = cdk.Fn.importValue(logBucketProp.exportName);
2601
+ return s3__namespace.Bucket.fromBucketName(this, "ImportedLogBucket", bucketName);
2602
+ }
2603
+ // string = bucket name
2604
+ if (typeof logBucketProp === "string") {
2605
+ return s3__namespace.Bucket.fromBucketName(this, "ImportedLogBucket", logBucketProp);
2606
+ }
2607
+ // IBucket = use directly
2608
+ return logBucketProp;
2609
+ }
2559
2610
  // Implement IDistribution interface
2560
2611
  get env() {
2561
2612
  return {
@@ -2670,7 +2721,7 @@ class JaypieDnsRecord extends constructs.Construct {
2670
2721
  const GSI_NAMES = {
2671
2722
  ALIAS: "indexAlias",
2672
2723
  CLASS: "indexClass",
2673
- OU: "indexOu",
2724
+ SCOPE: "indexScope",
2674
2725
  TYPE: "indexType",
2675
2726
  XID: "indexXid",
2676
2727
  };
@@ -2697,9 +2748,9 @@ const GlobalSecondaryIndex = {
2697
2748
  projectionType: dynamodb__namespace.ProjectionType.ALL,
2698
2749
  sortKey: { name: "sequence", type: dynamodb__namespace.AttributeType.NUMBER },
2699
2750
  },
2700
- Ou: {
2701
- indexName: GSI_NAMES.OU,
2702
- partitionKey: { name: GSI_NAMES.OU, type: dynamodb__namespace.AttributeType.STRING },
2751
+ Scope: {
2752
+ indexName: GSI_NAMES.SCOPE,
2753
+ partitionKey: { name: GSI_NAMES.SCOPE, type: dynamodb__namespace.AttributeType.STRING },
2703
2754
  projectionType: dynamodb__namespace.ProjectionType.ALL,
2704
2755
  sortKey: { name: "sequence", type: dynamodb__namespace.AttributeType.NUMBER },
2705
2756
  },
@@ -2723,7 +2774,7 @@ const GlobalSecondaryIndex = {
2723
2774
  const GlobalSecondaryIndexes = [
2724
2775
  GlobalSecondaryIndex.Alias,
2725
2776
  GlobalSecondaryIndex.Class,
2726
- GlobalSecondaryIndex.Ou,
2777
+ GlobalSecondaryIndex.Scope,
2727
2778
  GlobalSecondaryIndex.Type,
2728
2779
  GlobalSecondaryIndex.Xid,
2729
2780
  ];
@@ -2756,7 +2807,7 @@ const GlobalSecondaryIndexes = [
2756
2807
  * // Use only specific GSIs
2757
2808
  * const table = new JaypieDynamoDb(this, "MyTable", {
2758
2809
  * globalSecondaryIndexes: [
2759
- * JaypieDynamoDb.GlobalSecondaryIndex.Ou,
2810
+ * JaypieDynamoDb.GlobalSecondaryIndex.Scope,
2760
2811
  * JaypieDynamoDb.GlobalSecondaryIndex.Type,
2761
2812
  * ],
2762
2813
  * });