@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.
@@ -1,6 +1,6 @@
1
1
  export { CDK, LAMBDA_WEB_ADAPTER } from "./constants";
2
2
  export { JaypieAccountLoggingBucket, JaypieAccountLoggingBucketProps, } from "./JaypieAccountLoggingBucket";
3
- export { JaypieApiGateway } from "./JaypieApiGateway";
3
+ export { JaypieApiGateway, JaypieApiGatewayProps, } from "./JaypieApiGateway";
4
4
  export { JaypieAppStack } from "./JaypieAppStack";
5
5
  export { JaypieBucketQueuedLambda } from "./JaypieBucketQueuedLambda";
6
6
  export { JaypieCertificate, JaypieCertificateProps, } from "./JaypieCertificate";
@@ -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";
@@ -1,6 +1,6 @@
1
1
  export { CDK, LAMBDA_WEB_ADAPTER } from "./constants";
2
2
  export { JaypieAccountLoggingBucket, JaypieAccountLoggingBucketProps, } from "./JaypieAccountLoggingBucket";
3
- export { JaypieApiGateway } from "./JaypieApiGateway";
3
+ export { JaypieApiGateway, JaypieApiGatewayProps, } from "./JaypieApiGateway";
4
4
  export { JaypieAppStack } from "./JaypieAppStack";
5
5
  export { JaypieBucketQueuedLambda } from "./JaypieBucketQueuedLambda";
6
6
  export { JaypieCertificate, JaypieCertificateProps, } from "./JaypieCertificate";
package/dist/esm/index.js CHANGED
@@ -1127,15 +1127,20 @@ class JaypieApiGateway extends Construct {
1127
1127
  zone = process.env.CDK_ENV_API_HOSTED_ZONE;
1128
1128
  }
1129
1129
  // Determine host from props or environment
1130
- let host = propsHost;
1131
- if (!host) {
1132
- if (process.env.CDK_ENV_API_HOST_NAME) {
1133
- host = process.env.CDK_ENV_API_HOST_NAME;
1134
- }
1135
- else if (process.env.CDK_ENV_API_SUBDOMAIN &&
1136
- process.env.CDK_ENV_API_HOSTED_ZONE) {
1137
- host = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE);
1138
- }
1130
+ let host;
1131
+ if (typeof propsHost === "string") {
1132
+ host = propsHost;
1133
+ }
1134
+ else if (typeof propsHost === "object") {
1135
+ // Resolve host from HostConfig using envHostname()
1136
+ host = envHostname(propsHost);
1137
+ }
1138
+ else if (process.env.CDK_ENV_API_HOST_NAME) {
1139
+ host = process.env.CDK_ENV_API_HOST_NAME;
1140
+ }
1141
+ else if (process.env.CDK_ENV_API_SUBDOMAIN &&
1142
+ process.env.CDK_ENV_API_HOSTED_ZONE) {
1143
+ host = mergeDomain(process.env.CDK_ENV_API_SUBDOMAIN, process.env.CDK_ENV_API_HOSTED_ZONE);
1139
1144
  }
1140
1145
  const apiGatewayName = name || constructEnvName("ApiGateway");
1141
1146
  const apiDomainName = constructEnvName("ApiDomainName");
@@ -2325,7 +2330,7 @@ class JaypieDatadogSecret extends JaypieEnvSecret {
2325
2330
  class JaypieDistribution extends Construct {
2326
2331
  constructor(scope, id, props) {
2327
2332
  super(scope, id);
2328
- const { certificate: certificateProp = true, defaultBehavior: propsDefaultBehavior, destination: destinationProp = true, handler, host: propsHost, invokeMode = lambda.InvokeMode.BUFFERED, originReadTimeout = Duration.seconds(CDK$2.DURATION.CLOUDFRONT_API), roleTag = CDK$2.ROLE.API, zone: propsZone, ...distributionProps } = props;
2333
+ const { certificate: certificateProp = true, defaultBehavior: propsDefaultBehavior, destination: destinationProp = true, handler, host: propsHost, invokeMode = lambda.InvokeMode.BUFFERED, logBucket: logBucketProp, originReadTimeout = Duration.seconds(CDK$2.DURATION.CLOUDFRONT_API), roleTag = CDK$2.ROLE.API, zone: propsZone, ...distributionProps } = props;
2329
2334
  // Validate environment variables
2330
2335
  if (process.env.CDK_ENV_API_SUBDOMAIN &&
2331
2336
  !isValidSubdomain(process.env.CDK_ENV_API_SUBDOMAIN)) {
@@ -2340,8 +2345,20 @@ class JaypieDistribution extends Construct {
2340
2345
  throw new Error("CDK_ENV_HOSTED_ZONE is not a valid hostname");
2341
2346
  }
2342
2347
  // Determine host from props or environment
2343
- let host = propsHost;
2344
- if (!host) {
2348
+ let host;
2349
+ if (typeof propsHost === "string") {
2350
+ host = propsHost;
2351
+ }
2352
+ else if (typeof propsHost === "object") {
2353
+ // Resolve host from HostConfig using envHostname()
2354
+ try {
2355
+ host = envHostname(propsHost);
2356
+ }
2357
+ catch {
2358
+ host = undefined;
2359
+ }
2360
+ }
2361
+ else {
2345
2362
  try {
2346
2363
  if (process.env.CDK_ENV_API_HOST_NAME) {
2347
2364
  host = process.env.CDK_ENV_API_HOST_NAME;
@@ -2427,12 +2444,16 @@ class JaypieDistribution extends Construct {
2427
2444
  });
2428
2445
  this.certificate = certificateToUse;
2429
2446
  }
2430
- // Create log bucket if logging is enabled
2447
+ // Resolve or create log bucket
2431
2448
  let logBucket;
2432
- if (destinationProp !== false) {
2433
- logBucket = new s3.Bucket(this, constructEnvName("LogBucket"), {
2434
- objectOwnership: s3.ObjectOwnership.OBJECT_WRITER,
2435
- removalPolicy: RemovalPolicy.DESTROY,
2449
+ const isExternalBucket = logBucketProp !== undefined;
2450
+ if (logBucketProp !== undefined) {
2451
+ // Use external bucket
2452
+ logBucket = this.resolveLogBucket(logBucketProp);
2453
+ }
2454
+ else if (destinationProp !== false) {
2455
+ // Create new bucket (original behavior)
2456
+ const createdBucket = new s3.Bucket(this, constructEnvName("LogBucket"), {
2436
2457
  autoDeleteObjects: true,
2437
2458
  lifecycleRules: [
2438
2459
  {
@@ -2445,15 +2466,21 @@ class JaypieDistribution extends Construct {
2445
2466
  ],
2446
2467
  },
2447
2468
  ],
2469
+ objectOwnership: s3.ObjectOwnership.OBJECT_WRITER,
2470
+ removalPolicy: RemovalPolicy.DESTROY,
2448
2471
  });
2449
- Tags.of(logBucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.STORAGE);
2450
- // Add S3 notification to Datadog forwarder
2472
+ Tags.of(createdBucket).add(CDK$2.TAG.ROLE, CDK$2.ROLE.STORAGE);
2473
+ logBucket = createdBucket;
2474
+ }
2475
+ // Add S3 notifications if we have a bucket and destination is not false
2476
+ if (logBucket && destinationProp !== false && !isExternalBucket) {
2477
+ // Only add notifications to buckets we created (not external buckets)
2451
2478
  const lambdaDestination = destinationProp === true
2452
2479
  ? new LambdaDestination(resolveDatadogForwarderFunction(this))
2453
2480
  : destinationProp;
2454
2481
  logBucket.addEventNotification(s3.EventType.OBJECT_CREATED, lambdaDestination);
2455
- this.logBucket = logBucket;
2456
2482
  }
2483
+ this.logBucket = logBucket;
2457
2484
  // Create the CloudFront distribution
2458
2485
  this.distribution = new cloudfront.Distribution(this, constructEnvName("Distribution"), {
2459
2486
  defaultBehavior,
@@ -2524,6 +2551,30 @@ class JaypieDistribution extends Construct {
2524
2551
  "invokeMode" in handler &&
2525
2552
  typeof handler.invokeMode === "string");
2526
2553
  }
2554
+ isExportNameObject(value) {
2555
+ return (typeof value === "object" &&
2556
+ value !== null &&
2557
+ "exportName" in value &&
2558
+ typeof value.exportName === "string");
2559
+ }
2560
+ resolveLogBucket(logBucketProp) {
2561
+ // true = use account logging bucket
2562
+ if (logBucketProp === true) {
2563
+ const bucketName = Fn.importValue(CDK$2.IMPORT.LOG_BUCKET);
2564
+ return s3.Bucket.fromBucketName(this, "ImportedLogBucket", bucketName);
2565
+ }
2566
+ // { exportName: string } = import from CloudFormation export
2567
+ if (this.isExportNameObject(logBucketProp)) {
2568
+ const bucketName = Fn.importValue(logBucketProp.exportName);
2569
+ return s3.Bucket.fromBucketName(this, "ImportedLogBucket", bucketName);
2570
+ }
2571
+ // string = bucket name
2572
+ if (typeof logBucketProp === "string") {
2573
+ return s3.Bucket.fromBucketName(this, "ImportedLogBucket", logBucketProp);
2574
+ }
2575
+ // IBucket = use directly
2576
+ return logBucketProp;
2577
+ }
2527
2578
  // Implement IDistribution interface
2528
2579
  get env() {
2529
2580
  return {
@@ -2638,7 +2689,7 @@ class JaypieDnsRecord extends Construct {
2638
2689
  const GSI_NAMES = {
2639
2690
  ALIAS: "indexAlias",
2640
2691
  CLASS: "indexClass",
2641
- OU: "indexOu",
2692
+ SCOPE: "indexScope",
2642
2693
  TYPE: "indexType",
2643
2694
  XID: "indexXid",
2644
2695
  };
@@ -2665,9 +2716,9 @@ const GlobalSecondaryIndex = {
2665
2716
  projectionType: dynamodb.ProjectionType.ALL,
2666
2717
  sortKey: { name: "sequence", type: dynamodb.AttributeType.NUMBER },
2667
2718
  },
2668
- Ou: {
2669
- indexName: GSI_NAMES.OU,
2670
- partitionKey: { name: GSI_NAMES.OU, type: dynamodb.AttributeType.STRING },
2719
+ Scope: {
2720
+ indexName: GSI_NAMES.SCOPE,
2721
+ partitionKey: { name: GSI_NAMES.SCOPE, type: dynamodb.AttributeType.STRING },
2671
2722
  projectionType: dynamodb.ProjectionType.ALL,
2672
2723
  sortKey: { name: "sequence", type: dynamodb.AttributeType.NUMBER },
2673
2724
  },
@@ -2691,7 +2742,7 @@ const GlobalSecondaryIndex = {
2691
2742
  const GlobalSecondaryIndexes = [
2692
2743
  GlobalSecondaryIndex.Alias,
2693
2744
  GlobalSecondaryIndex.Class,
2694
- GlobalSecondaryIndex.Ou,
2745
+ GlobalSecondaryIndex.Scope,
2695
2746
  GlobalSecondaryIndex.Type,
2696
2747
  GlobalSecondaryIndex.Xid,
2697
2748
  ];
@@ -2724,7 +2775,7 @@ const GlobalSecondaryIndexes = [
2724
2775
  * // Use only specific GSIs
2725
2776
  * const table = new JaypieDynamoDb(this, "MyTable", {
2726
2777
  * globalSecondaryIndexes: [
2727
- * JaypieDynamoDb.GlobalSecondaryIndex.Ou,
2778
+ * JaypieDynamoDb.GlobalSecondaryIndex.Scope,
2728
2779
  * JaypieDynamoDb.GlobalSecondaryIndex.Type,
2729
2780
  * ],
2730
2781
  * });