@go-to-k/cdkd 0.156.0 → 0.158.0

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/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { _ as withSkipPrefix, a as runDockerStreaming, c as getLogger, d as getLiveRenderer, f as PATTERN_B_NAME_PROPERTIES, g as generateResourceNameWithFallback, h as generateResourceName, i as runDockerForeground, n as formatDockerLoginError, p as PATTERN_B_RESOURCE_TYPES, r as getDockerCmd, u as runStackBuffered, v as withStackName } from "./docker-cmd-iDMcWcre.js";
3
- import { $ as CdkdError, A as shouldRetainResource, B as resolveSkipPrefix, C as IntrinsicFunctionResolver, D as TemplateParser, E as DagBuilder, F as Synthesizer, G as CFN_TEMPLATE_URL_LIMIT, H as resolveStateBucketWithDefaultAndSource, I as getDefaultStateBucketName, J as uploadCfnTemplate, K as MIGRATE_TMP_PREFIX, L as getLegacyStateBucketName, M as stringifyValue, N as WorkGraph, O as LockManager, P as buildDockerImage, R as resolveApp, S as assertRegionMatch, T as DiffCalculator, U as warnDeprecatedNoPrefixCliFlag, V as resolveStateBucketWithDefault, W as CFN_TEMPLATE_BODY_LIMIT, Y as AssemblyReader, Z as resolveBucketRegion, _ as matchesCdkPath, a as withRetry, b as ProviderRegistry, bt as withErrorHandling, c as bold, ct as PartialFailureError, d as green, dt as ResourceUpdateNotSupportedError, f as red, ft as RouteDiscoveryError, g as CDK_PATH_TAG, h as collectInlinePolicyNamesManagedBySiblings, i as withResourceDeadline, it as LocalStartServiceError, j as AssetPublisher, k as S3StateBackend, l as cyan, lt as ProvisioningError, m as IAMRoleProvider, mt as StackTerminationProtectionError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as LocalInvokeBuildError, o as IMPLICIT_DELETE_DEPENDENCIES, ot as MissingCdkCliError, p as yellow, pt as StackHasActiveImportsError, q as findLargeInlineResources, r as DeployEngine, rt as LocalMigrateError, s as formatResourceLine, st as NestedStackChildDirectDestroyError, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as gray, ut as ResourceTimeoutError, v as normalizeAwsTagsToCfn, w as applyRoleArnIfSet, x as CloudControlProvider, y as resolveExplicitPhysicalId, yt as normalizeAwsError, z as resolveCaptureObservedState } from "./deploy-engine-YQwoPaCE.js";
4
- import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-BF03Alpe.js";
3
+ import { $ as CdkdError, A as shouldRetainResource, B as resolveSkipPrefix, C as IntrinsicFunctionResolver, D as TemplateParser, E as DagBuilder, F as Synthesizer, G as CFN_TEMPLATE_URL_LIMIT, H as resolveStateBucketWithDefaultAndSource, I as getDefaultStateBucketName, J as uploadCfnTemplate, K as MIGRATE_TMP_PREFIX, L as getLegacyStateBucketName, M as stringifyValue, N as WorkGraph, O as LockManager, P as buildDockerImage, R as resolveApp, S as assertRegionMatch, T as DiffCalculator, U as warnDeprecatedNoPrefixCliFlag, V as resolveStateBucketWithDefault, W as CFN_TEMPLATE_BODY_LIMIT, Y as AssemblyReader, Z as resolveBucketRegion, _ as matchesCdkPath, a as withRetry, b as ProviderRegistry, bt as withErrorHandling, c as bold, ct as PartialFailureError, d as green, dt as ResourceUpdateNotSupportedError, f as red, ft as RouteDiscoveryError, g as CDK_PATH_TAG, h as collectInlinePolicyNamesManagedBySiblings, i as withResourceDeadline, it as LocalStartServiceError, j as AssetPublisher, k as S3StateBackend, l as cyan, lt as ProvisioningError, m as IAMRoleProvider, mt as StackTerminationProtectionError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as LocalInvokeBuildError, o as IMPLICIT_DELETE_DEPENDENCIES, ot as MissingCdkCliError, p as yellow, pt as StackHasActiveImportsError, q as findLargeInlineResources, r as DeployEngine, rt as LocalMigrateError, s as formatResourceLine, st as NestedStackChildDirectDestroyError, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as gray, ut as ResourceTimeoutError, v as normalizeAwsTagsToCfn, w as applyRoleArnIfSet, x as CloudControlProvider, y as resolveExplicitPhysicalId, yt as normalizeAwsError, z as resolveCaptureObservedState } from "./deploy-engine-UmoqjtWH.js";
4
+ import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-B15NAPbL.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  import { createHash, createHmac, createPublicKey, createVerify, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
7
7
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListBucketsCommand, ListDirectoryBucketsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -12,7 +12,7 @@ import { AddPermissionCommand, CreateEventSourceMappingCommand, CreateFunctionCo
12
12
  import { AssumeRoleCommand, GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
13
13
  import { AssociateRouteTableCommand, AttachInternetGatewayCommand, AuthorizeSecurityGroupEgressCommand, AuthorizeSecurityGroupIngressCommand, CreateInternetGatewayCommand, CreateNatGatewayCommand, CreateNetworkAclCommand, CreateNetworkAclEntryCommand, CreateRouteCommand, CreateRouteTableCommand, CreateSecurityGroupCommand, CreateSubnetCommand, CreateTagsCommand, CreateVpcCommand, DeleteInternetGatewayCommand, DeleteNatGatewayCommand, DeleteNetworkAclCommand, DeleteNetworkAclEntryCommand, DeleteNetworkInterfaceCommand, DeleteRouteCommand, DeleteRouteTableCommand, DeleteSecurityGroupCommand, DeleteSubnetCommand, DeleteTagsCommand, DeleteVpcCommand, DescribeAvailabilityZonesCommand, DescribeInstanceAttributeCommand, DescribeInstancesCommand, DescribeInternetGatewaysCommand, DescribeNatGatewaysCommand, DescribeNetworkAclsCommand, DescribeNetworkInterfacesCommand, DescribeRouteTablesCommand, DescribeSecurityGroupsCommand, DescribeSubnetsCommand, DescribeVolumesCommand, DescribeVpcAttributeCommand, DescribeVpcsCommand, DetachInternetGatewayCommand, DisassociateRouteTableCommand, EC2Client, ModifyInstanceAttributeCommand, ModifySubnetAttributeCommand, ModifyVpcAttributeCommand, ReplaceNetworkAclAssociationCommand, RevokeSecurityGroupEgressCommand, RevokeSecurityGroupIngressCommand, RunInstancesCommand, TerminateInstancesCommand, waitUntilInstanceRunning, waitUntilInstanceTerminated, waitUntilNatGatewayAvailable, waitUntilNatGatewayDeleted } from "@aws-sdk/client-ec2";
14
14
  import { CreateTableCommand, DeleteTableCommand, DescribeContinuousBackupsCommand, DescribeContributorInsightsCommand, DescribeKinesisStreamingDestinationCommand, DescribeTableCommand, DescribeTimeToLiveCommand, DynamoDBClient, ListTablesCommand, ListTagsOfResourceCommand, ResourceNotFoundException as ResourceNotFoundException$1, TagResourceCommand as TagResourceCommand$2, UntagResourceCommand as UntagResourceCommand$2, UpdateTableCommand, UpdateTimeToLiveCommand } from "@aws-sdk/client-dynamodb";
15
- import { CloudFormationClient, CreateChangeSetCommand, DeleteChangeSetCommand, DeleteStackCommand, DescribeChangeSetCommand, DescribeStackEventsCommand, DescribeStackResourcesCommand, DescribeStacksCommand, DescribeTypeCommand, ExecuteChangeSetCommand, GetTemplateCommand, UpdateStackCommand, waitUntilChangeSetCreateComplete, waitUntilStackDeleteComplete, waitUntilStackImportComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
15
+ import { CloudFormationClient, CreateChangeSetCommand, DeleteChangeSetCommand, DeleteStackCommand, DescribeChangeSetCommand, DescribeStackEventsCommand, DescribeStackResourcesCommand, DescribeStacksCommand, DescribeTypeCommand, ExecuteChangeSetCommand, GetTemplateCommand, ListExportsCommand, UpdateStackCommand, waitUntilChangeSetCreateComplete, waitUntilStackDeleteComplete, waitUntilStackImportComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
16
16
  import { APIGatewayClient, CreateAuthorizerCommand, CreateDeploymentCommand, CreateResourceCommand, CreateStageCommand, DeleteAuthorizerCommand, DeleteDeploymentCommand, DeleteMethodCommand, DeleteResourceCommand, DeleteStageCommand, GetAccountCommand, GetAuthorizerCommand, GetDeploymentCommand, GetMethodCommand, GetResourceCommand, GetStageCommand, NotFoundException as NotFoundException$1, PutIntegrationCommand, PutIntegrationResponseCommand, PutMethodCommand, PutMethodResponseCommand, TagResourceCommand as TagResourceCommand$3, UntagResourceCommand as UntagResourceCommand$3, UpdateAccountCommand, UpdateAuthorizerCommand, UpdateMethodCommand, UpdateStageCommand } from "@aws-sdk/client-api-gateway";
17
17
  import { CreateEventBusCommand, DeleteEventBusCommand, DeleteRuleCommand, DescribeEventBusCommand, DescribeRuleCommand, EventBridgeClient, ListEventBusesCommand, ListRulesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$1, ListTargetsByRuleCommand, PutRuleCommand, PutTargetsCommand, RemoveTargetsCommand, ResourceNotFoundException as ResourceNotFoundException$2, TagResourceCommand as TagResourceCommand$4, UntagResourceCommand as UntagResourceCommand$4, UpdateEventBusCommand } from "@aws-sdk/client-eventbridge";
18
18
  import { CreateSecretCommand, DeleteSecretCommand, DescribeSecretCommand, GetSecretValueCommand, ListSecretsCommand, RemoveRegionsFromReplicationCommand, ReplicateSecretToRegionsCommand, ResourceNotFoundException as ResourceNotFoundException$3, SecretsManagerClient, TagResourceCommand as TagResourceCommand$5, UntagResourceCommand as UntagResourceCommand$5, UpdateSecretCommand } from "@aws-sdk/client-secrets-manager";
@@ -21,6 +21,7 @@ import { CloudFrontClient, CreateCloudFrontOriginAccessIdentityCommand, CreateDi
21
21
  import { CloudWatchClient, DeleteAlarmsCommand, DescribeAlarmsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$4, PutMetricAlarmCommand, TagResourceCommand as TagResourceCommand$6, UntagResourceCommand as UntagResourceCommand$6 } from "@aws-sdk/client-cloudwatch";
22
22
  import { CloudWatchLogsClient, CreateLogGroupCommand, DeleteDataProtectionPolicyCommand, DeleteIndexPolicyCommand, DeleteLogGroupCommand, DeleteRetentionPolicyCommand, DescribeIndexPoliciesCommand, DescribeLogGroupsCommand, GetDataProtectionPolicyCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$5, PutBearerTokenAuthenticationCommand, PutDataProtectionPolicyCommand, PutIndexPolicyCommand, PutLogGroupDeletionProtectionCommand, PutRetentionPolicyCommand, ResourceAlreadyExistsException, ResourceNotFoundException as ResourceNotFoundException$4, TagResourceCommand as TagResourceCommand$7, UntagResourceCommand as UntagResourceCommand$7 } from "@aws-sdk/client-cloudwatch-logs";
23
23
  import { BedrockAgentCoreControlClient, CreateAgentRuntimeCommand, DeleteAgentRuntimeCommand, GetAgentRuntimeCommand, ResourceNotFoundException as ResourceNotFoundException$5, UpdateAgentRuntimeCommand } from "@aws-sdk/client-bedrock-agentcore-control";
24
+ import { ACMClient, AddTagsToCertificateCommand, DeleteCertificateCommand, DescribeCertificateCommand, ListCertificatesCommand, ListTagsForCertificateCommand, RemoveTagsFromCertificateCommand, RequestCertificateCommand, ResourceNotFoundException as ResourceNotFoundException$6, UpdateCertificateOptionsCommand } from "@aws-sdk/client-acm";
24
25
  import * as fs from "node:fs";
25
26
  import { cpSync, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
26
27
  import * as path from "node:path";
@@ -44,17 +45,17 @@ import { CreateClusterCommand, CreateServiceCommand, DeleteClusterCommand, Delet
44
45
  import { AddTagsToResourceCommand as AddTagsToResourceCommand$2, CreateDBClusterCommand as CreateDBClusterCommand$1, CreateDBInstanceCommand as CreateDBInstanceCommand$1, CreateDBSubnetGroupCommand as CreateDBSubnetGroupCommand$1, DeleteDBClusterCommand as DeleteDBClusterCommand$1, DeleteDBInstanceCommand as DeleteDBInstanceCommand$1, DeleteDBSubnetGroupCommand as DeleteDBSubnetGroupCommand$1, DescribeDBClustersCommand as DescribeDBClustersCommand$1, DescribeDBInstancesCommand as DescribeDBInstancesCommand$1, DescribeDBSubnetGroupsCommand as DescribeDBSubnetGroupsCommand$1, DocDBClient, ListTagsForResourceCommand as ListTagsForResourceCommand$11, ModifyDBClusterCommand as ModifyDBClusterCommand$1, ModifyDBInstanceCommand as ModifyDBInstanceCommand$1, ModifyDBSubnetGroupCommand as ModifyDBSubnetGroupCommand$1, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$2 } from "@aws-sdk/client-docdb";
45
46
  import { AddTagsToResourceCommand as AddTagsToResourceCommand$3, CreateDBClusterCommand as CreateDBClusterCommand$2, CreateDBInstanceCommand as CreateDBInstanceCommand$2, CreateDBSubnetGroupCommand as CreateDBSubnetGroupCommand$2, DeleteDBClusterCommand as DeleteDBClusterCommand$2, DeleteDBInstanceCommand as DeleteDBInstanceCommand$2, DeleteDBSubnetGroupCommand as DeleteDBSubnetGroupCommand$2, DescribeDBClustersCommand as DescribeDBClustersCommand$2, DescribeDBInstancesCommand as DescribeDBInstancesCommand$2, DescribeDBSubnetGroupsCommand as DescribeDBSubnetGroupsCommand$2, ListTagsForResourceCommand as ListTagsForResourceCommand$12, ModifyDBClusterCommand as ModifyDBClusterCommand$2, ModifyDBInstanceCommand as ModifyDBInstanceCommand$2, ModifyDBSubnetGroupCommand as ModifyDBSubnetGroupCommand$2, NeptuneClient, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$3 } from "@aws-sdk/client-neptune";
46
47
  import { CreateWebACLCommand, DeleteWebACLCommand, GetWebACLCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$13, ListWebACLsCommand, TagResourceCommand as TagResourceCommand$12, UntagResourceCommand as UntagResourceCommand$11, UpdateWebACLCommand, WAFNonexistentItemException, WAFV2Client } from "@aws-sdk/client-wafv2";
47
- import { CognitoIdentityProviderClient, CreateUserPoolCommand, DeleteUserPoolCommand, DescribeUserPoolCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$14, ListUserPoolsCommand, ResourceNotFoundException as ResourceNotFoundException$6, UpdateUserPoolCommand } from "@aws-sdk/client-cognito-identity-provider";
48
+ import { CognitoIdentityProviderClient, CreateUserPoolCommand, DeleteUserPoolCommand, DescribeUserPoolCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$14, ListUserPoolsCommand, ResourceNotFoundException as ResourceNotFoundException$7, UpdateUserPoolCommand } from "@aws-sdk/client-cognito-identity-provider";
48
49
  import { AddTagsToResourceCommand as AddTagsToResourceCommand$4, CreateCacheClusterCommand, CreateCacheSubnetGroupCommand, DeleteCacheClusterCommand, DeleteCacheSubnetGroupCommand, DescribeCacheClustersCommand, DescribeCacheSubnetGroupsCommand, ElastiCacheClient, ListTagsForResourceCommand as ListTagsForResourceCommand$15, ModifyCacheClusterCommand, ModifyCacheSubnetGroupCommand, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$4 } from "@aws-sdk/client-elasticache";
49
50
  import { CreatePrivateDnsNamespaceCommand, CreateServiceCommand as CreateServiceCommand$1, DeleteNamespaceCommand, DeleteServiceCommand as DeleteServiceCommand$1, GetNamespaceCommand, GetOperationCommand, GetServiceCommand, ListNamespacesCommand, ListServicesCommand as ListServicesCommand$1, ListTagsForResourceCommand as ListTagsForResourceCommand$16, NamespaceNotFound, ServiceDiscoveryClient, ServiceNotFound, UpdatePrivateDnsNamespaceCommand, UpdateServiceCommand as UpdateServiceCommand$1 } from "@aws-sdk/client-servicediscovery";
50
51
  import { AppSyncClient, CreateApiKeyCommand, CreateDataSourceCommand, CreateGraphqlApiCommand, CreateResolverCommand, DeleteApiKeyCommand, DeleteDataSourceCommand, DeleteGraphqlApiCommand, DeleteResolverCommand, GetDataSourceCommand, GetGraphqlApiCommand, GetIntrospectionSchemaCommand, GetResolverCommand, ListApiKeysCommand, ListGraphqlApisCommand, NotFoundException as NotFoundException$4, StartSchemaCreationCommand, TagResourceCommand as TagResourceCommand$13, UntagResourceCommand as UntagResourceCommand$12, UpdateApiKeyCommand, UpdateDataSourceCommand, UpdateGraphqlApiCommand, UpdateResolverCommand } from "@aws-sdk/client-appsync";
51
52
  import { parse, print } from "graphql";
52
53
  import { CreateConnectionCommand, CreateCrawlerCommand, CreateDatabaseCommand, CreateJobCommand, CreateSecurityConfigurationCommand, CreateTableCommand as CreateTableCommand$1, CreateTriggerCommand, CreateWorkflowCommand, DeleteConnectionCommand, DeleteCrawlerCommand, DeleteDatabaseCommand, DeleteJobCommand, DeleteSecurityConfigurationCommand, DeleteTableCommand as DeleteTableCommand$1, DeleteTriggerCommand, DeleteWorkflowCommand, EntityNotFoundException, GetConnectionCommand, GetCrawlerCommand, GetDatabaseCommand, GetDatabasesCommand, GetJobCommand, GetSecurityConfigurationCommand, GetSecurityConfigurationsCommand, GetTableCommand, GetTablesCommand, GetTagsCommand, GetTriggerCommand, GetWorkflowCommand, GlueClient, ListWorkflowsCommand, StartCrawlerScheduleCommand, StartTriggerCommand, StopCrawlerScheduleCommand, StopTriggerCommand, UpdateConnectionCommand, UpdateCrawlerCommand, UpdateDatabaseCommand, UpdateJobCommand, UpdateTableCommand as UpdateTableCommand$1, UpdateTriggerCommand, UpdateWorkflowCommand } from "@aws-sdk/client-glue";
53
- import { AddTagsToStreamCommand, CreateStreamCommand, DecreaseStreamRetentionPeriodCommand, DeleteStreamCommand, DeregisterStreamConsumerCommand, DescribeStreamCommand, DescribeStreamConsumerCommand, IncreaseStreamRetentionPeriodCommand, KinesisClient, ListStreamsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$17, ListTagsForStreamCommand, RegisterStreamConsumerCommand, RemoveTagsFromStreamCommand, ResourceNotFoundException as ResourceNotFoundException$7, StartStreamEncryptionCommand, StopStreamEncryptionCommand, TagResourceCommand as TagResourceCommand$14, UntagResourceCommand as UntagResourceCommand$13, UpdateShardCountCommand } from "@aws-sdk/client-kinesis";
54
+ import { AddTagsToStreamCommand, CreateStreamCommand, DecreaseStreamRetentionPeriodCommand, DeleteStreamCommand, DeregisterStreamConsumerCommand, DescribeStreamCommand, DescribeStreamConsumerCommand, IncreaseStreamRetentionPeriodCommand, KinesisClient, ListStreamsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$17, ListTagsForStreamCommand, RegisterStreamConsumerCommand, RemoveTagsFromStreamCommand, ResourceNotFoundException as ResourceNotFoundException$8, StartStreamEncryptionCommand, StopStreamEncryptionCommand, TagResourceCommand as TagResourceCommand$14, UntagResourceCommand as UntagResourceCommand$13, UpdateShardCountCommand } from "@aws-sdk/client-kinesis";
54
55
  import { AccessPointNotFound, CreateAccessPointCommand, CreateFileSystemCommand, CreateMountTargetCommand, DeleteAccessPointCommand, DeleteFileSystemCommand, DeleteMountTargetCommand, DescribeAccessPointsCommand, DescribeBackupPolicyCommand, DescribeFileSystemsCommand, DescribeLifecycleConfigurationCommand, DescribeMountTargetSecurityGroupsCommand, DescribeMountTargetsCommand, EFSClient, FileSystemNotFound, ModifyMountTargetSecurityGroupsCommand, MountTargetNotFound, UpdateFileSystemCommand } from "@aws-sdk/client-efs";
55
- import { CreateDeliveryStreamCommand, DeleteDeliveryStreamCommand, DescribeDeliveryStreamCommand, FirehoseClient, ListDeliveryStreamsCommand, ListTagsForDeliveryStreamCommand, ResourceNotFoundException as ResourceNotFoundException$8, TagDeliveryStreamCommand, UntagDeliveryStreamCommand, UpdateDestinationCommand } from "@aws-sdk/client-firehose";
56
+ import { CreateDeliveryStreamCommand, DeleteDeliveryStreamCommand, DescribeDeliveryStreamCommand, FirehoseClient, ListDeliveryStreamsCommand, ListTagsForDeliveryStreamCommand, ResourceNotFoundException as ResourceNotFoundException$9, TagDeliveryStreamCommand, UntagDeliveryStreamCommand, UpdateDestinationCommand } from "@aws-sdk/client-firehose";
56
57
  import { AddTagsCommand as AddTagsCommand$1, CloudTrailClient, CreateTrailCommand, DeleteTrailCommand, GetEventSelectorsCommand, GetInsightSelectorsCommand, GetTrailCommand, GetTrailStatusCommand, ListTagsCommand as ListTagsCommand$1, ListTrailsCommand, PutEventSelectorsCommand, PutInsightSelectorsCommand, RemoveTagsCommand as RemoveTagsCommand$1, StartLoggingCommand, StopLoggingCommand, TrailNotFoundException, UpdateTrailCommand } from "@aws-sdk/client-cloudtrail";
57
- import { BatchGetProjectsCommand, CodeBuildClient, CreateProjectCommand, DeleteProjectCommand, ListProjectsCommand, ResourceNotFoundException as ResourceNotFoundException$9, UpdateProjectCommand } from "@aws-sdk/client-codebuild";
58
+ import { BatchGetProjectsCommand, CodeBuildClient, CreateProjectCommand, DeleteProjectCommand, ListProjectsCommand, ResourceNotFoundException as ResourceNotFoundException$10, UpdateProjectCommand } from "@aws-sdk/client-codebuild";
58
59
  import { CreateVectorBucketCommand, DeleteIndexCommand, DeleteVectorBucketCommand, GetVectorBucketCommand, ListIndexesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$18, ListVectorBucketsCommand, S3VectorsClient } from "@aws-sdk/client-s3vectors";
59
60
  import { CreateNamespaceCommand, CreateTableBucketCommand, CreateTableCommand as CreateTableCommand$2, DeleteNamespaceCommand as DeleteNamespaceCommand$1, DeleteTableBucketCommand, DeleteTableCommand as DeleteTableCommand$2, GetTableBucketCommand, GetTableCommand as GetTableCommand$1, ListNamespacesCommand as ListNamespacesCommand$1, ListTableBucketsCommand, ListTablesCommand as ListTablesCommand$1, ListTagsForResourceCommand as ListTagsForResourceCommand$19, NotFoundException as NotFoundException$5, S3TablesClient } from "@aws-sdk/client-s3tables";
60
61
  import { AttachLoadBalancerTargetGroupsCommand, AttachLoadBalancersCommand, AttachTrafficSourcesCommand, AutoScalingClient, CreateAutoScalingGroupCommand, CreateOrUpdateTagsCommand, DeleteAutoScalingGroupCommand, DeleteLifecycleHookCommand, DeleteNotificationConfigurationCommand, DeleteTagsCommand as DeleteTagsCommand$1, DescribeAutoScalingGroupsCommand, DescribeLifecycleHooksCommand, DescribeNotificationConfigurationsCommand, DescribeTrafficSourcesCommand, DetachLoadBalancerTargetGroupsCommand, DetachLoadBalancersCommand, DetachTrafficSourcesCommand, DisableMetricsCollectionCommand, EnableMetricsCollectionCommand, PutLifecycleHookCommand, PutNotificationConfigurationCommand, UpdateAutoScalingGroupCommand } from "@aws-sdk/client-auto-scaling";
@@ -22643,7 +22644,7 @@ var CognitoUserPoolProvider = class {
22643
22644
  if (!templatedActive) try {
22644
22645
  needsFlip = (await this.getClient().send(new DescribeUserPoolCommand({ UserPoolId: physicalId }))).UserPool?.DeletionProtection === "ACTIVE";
22645
22646
  } catch (descError) {
22646
- if (descError instanceof ResourceNotFoundException$6) {
22647
+ if (descError instanceof ResourceNotFoundException$7) {
22647
22648
  assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
22648
22649
  this.logger.debug(`Cognito User Pool ${physicalId} does not exist, skipping deletion`);
22649
22650
  return;
@@ -22666,7 +22667,7 @@ var CognitoUserPoolProvider = class {
22666
22667
  await this.getClient().send(new DeleteUserPoolCommand({ UserPoolId: physicalId }));
22667
22668
  this.logger.debug(`Successfully deleted Cognito User Pool ${logicalId}`);
22668
22669
  } catch (error) {
22669
- if (error instanceof ResourceNotFoundException$6) {
22670
+ if (error instanceof ResourceNotFoundException$7) {
22670
22671
  assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
22671
22672
  this.logger.debug(`Cognito User Pool ${physicalId} does not exist, skipping deletion`);
22672
22673
  return;
@@ -22699,7 +22700,7 @@ var CognitoUserPoolProvider = class {
22699
22700
  try {
22700
22701
  resp = await this.getClient().send(new DescribeUserPoolCommand({ UserPoolId: physicalId }));
22701
22702
  } catch (err) {
22702
- if (err instanceof ResourceNotFoundException$6) return void 0;
22703
+ if (err instanceof ResourceNotFoundException$7) return void 0;
22703
22704
  throw err;
22704
22705
  }
22705
22706
  const pool = resp.UserPool;
@@ -22754,7 +22755,7 @@ var CognitoUserPoolProvider = class {
22754
22755
  attributes: {}
22755
22756
  };
22756
22757
  } catch (err) {
22757
- if (err instanceof ResourceNotFoundException$6) return null;
22758
+ if (err instanceof ResourceNotFoundException$7) return null;
22758
22759
  throw err;
22759
22760
  }
22760
22761
  const desiredName = typeof input.properties?.["UserPoolName"] === "string" ? input.properties["UserPoolName"] : void 0;
@@ -22778,7 +22779,7 @@ var CognitoUserPoolProvider = class {
22778
22779
  attributes: {}
22779
22780
  };
22780
22781
  } catch (err) {
22781
- if (err instanceof ResourceNotFoundException$6) continue;
22782
+ if (err instanceof ResourceNotFoundException$7) continue;
22782
22783
  throw err;
22783
22784
  }
22784
22785
  }
@@ -27008,7 +27009,7 @@ var KinesisStreamProvider = class {
27008
27009
  }));
27009
27010
  this.logger.debug(`Successfully deleted Kinesis stream ${logicalId}`);
27010
27011
  } catch (error) {
27011
- if (error instanceof ResourceNotFoundException$7) {
27012
+ if (error instanceof ResourceNotFoundException$8) {
27012
27013
  assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
27013
27014
  this.logger.debug(`Kinesis stream ${physicalId} does not exist, skipping deletion`);
27014
27015
  return;
@@ -27088,7 +27089,7 @@ var KinesisStreamProvider = class {
27088
27089
  try {
27089
27090
  stream = (await this.getClient().send(new DescribeStreamCommand({ StreamName: physicalId }))).StreamDescription;
27090
27091
  } catch (err) {
27091
- if (err instanceof ResourceNotFoundException$7) return void 0;
27092
+ if (err instanceof ResourceNotFoundException$8) return void 0;
27092
27093
  throw err;
27093
27094
  }
27094
27095
  if (!stream) return void 0;
@@ -27106,7 +27107,7 @@ var KinesisStreamProvider = class {
27106
27107
  try {
27107
27108
  result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForStreamCommand({ StreamName: physicalId }))).Tags);
27108
27109
  } catch (err) {
27109
- if (err instanceof ResourceNotFoundException$7) return void 0;
27110
+ if (err instanceof ResourceNotFoundException$8) return void 0;
27110
27111
  this.logger.debug(`Kinesis ListTagsForStream(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`);
27111
27112
  }
27112
27113
  return result;
@@ -27120,7 +27121,7 @@ var KinesisStreamProvider = class {
27120
27121
  attributes: {}
27121
27122
  };
27122
27123
  } catch (err) {
27123
- if (err instanceof ResourceNotFoundException$7) return null;
27124
+ if (err instanceof ResourceNotFoundException$8) return null;
27124
27125
  throw err;
27125
27126
  }
27126
27127
  if (!input.cdkPath) return null;
@@ -27291,7 +27292,7 @@ var KinesisStreamConsumerProvider = class {
27291
27292
  await this.getClient().send(new DeregisterStreamConsumerCommand({ ConsumerARN: physicalId }));
27292
27293
  this.logger.debug(`Successfully deregistered Kinesis stream consumer ${logicalId}`);
27293
27294
  } catch (error) {
27294
- if (error instanceof ResourceNotFoundException$7) {
27295
+ if (error instanceof ResourceNotFoundException$8) {
27295
27296
  assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
27296
27297
  this.logger.debug(`Kinesis stream consumer ${physicalId} not found, skipping`);
27297
27298
  return;
@@ -27337,7 +27338,7 @@ var KinesisStreamConsumerProvider = class {
27337
27338
  try {
27338
27339
  desc = (await this.getClient().send(new DescribeStreamConsumerCommand({ ConsumerARN: physicalId }))).ConsumerDescription;
27339
27340
  } catch (err) {
27340
- if (err instanceof ResourceNotFoundException$7) return void 0;
27341
+ if (err instanceof ResourceNotFoundException$8) return void 0;
27341
27342
  throw err;
27342
27343
  }
27343
27344
  if (!desc) return void 0;
@@ -27347,7 +27348,7 @@ var KinesisStreamConsumerProvider = class {
27347
27348
  try {
27348
27349
  result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$17({ ResourceARN: physicalId }))).Tags);
27349
27350
  } catch (err) {
27350
- if (err instanceof ResourceNotFoundException$7) return void 0;
27351
+ if (err instanceof ResourceNotFoundException$8) return void 0;
27351
27352
  this.logger.debug(`ListTagsForResource(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`);
27352
27353
  result["Tags"] = [];
27353
27354
  }
@@ -28414,7 +28415,7 @@ var FirehoseProvider = class {
28414
28415
  await this.getClient().send(new DeleteDeliveryStreamCommand({ DeliveryStreamName: physicalId }));
28415
28416
  this.logger.debug(`Successfully deleted Firehose delivery stream ${logicalId}`);
28416
28417
  } catch (error) {
28417
- if (error instanceof ResourceNotFoundException$8) {
28418
+ if (error instanceof ResourceNotFoundException$9) {
28418
28419
  assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
28419
28420
  this.logger.debug(`Firehose delivery stream ${physicalId} does not exist, skipping deletion`);
28420
28421
  return;
@@ -28951,7 +28952,7 @@ var FirehoseProvider = class {
28951
28952
  try {
28952
28953
  desc = (await this.getClient().send(new DescribeDeliveryStreamCommand({ DeliveryStreamName: physicalId }))).DeliveryStreamDescription;
28953
28954
  } catch (err) {
28954
- if (err instanceof ResourceNotFoundException$8) return void 0;
28955
+ if (err instanceof ResourceNotFoundException$9) return void 0;
28955
28956
  throw err;
28956
28957
  }
28957
28958
  if (!desc) return void 0;
@@ -28982,7 +28983,7 @@ var FirehoseProvider = class {
28982
28983
  try {
28983
28984
  result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForDeliveryStreamCommand({ DeliveryStreamName: physicalId }))).Tags);
28984
28985
  } catch (err) {
28985
- if (err instanceof ResourceNotFoundException$8) return void 0;
28986
+ if (err instanceof ResourceNotFoundException$9) return void 0;
28986
28987
  this.logger.debug(`Firehose ListTagsForDeliveryStream(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`);
28987
28988
  result["Tags"] = [];
28988
28989
  }
@@ -29019,7 +29020,7 @@ var FirehoseProvider = class {
29019
29020
  attributes: {}
29020
29021
  };
29021
29022
  } catch (err) {
29022
- if (err instanceof ResourceNotFoundException$8) return null;
29023
+ if (err instanceof ResourceNotFoundException$9) return null;
29023
29024
  throw err;
29024
29025
  }
29025
29026
  if (!input.cdkPath) return null;
@@ -29790,7 +29791,7 @@ var CodeBuildProvider = class {
29790
29791
  await this.getClient().send(new DeleteProjectCommand({ name: physicalId }));
29791
29792
  this.logger.debug(`Successfully deleted CodeBuild Project ${logicalId}`);
29792
29793
  } catch (error) {
29793
- if (error instanceof ResourceNotFoundException$9) {
29794
+ if (error instanceof ResourceNotFoundException$10) {
29794
29795
  assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
29795
29796
  this.logger.debug(`CodeBuild Project ${physicalId} does not exist, skipping deletion`);
29796
29797
  return;
@@ -29845,7 +29846,7 @@ var CodeBuildProvider = class {
29845
29846
  try {
29846
29847
  project = (await this.getClient().send(new BatchGetProjectsCommand({ names: [physicalId] }))).projects?.[0];
29847
29848
  } catch (err) {
29848
- if (err instanceof ResourceNotFoundException$9) return void 0;
29849
+ if (err instanceof ResourceNotFoundException$10) return void 0;
29849
29850
  throw err;
29850
29851
  }
29851
29852
  if (!project) return void 0;
@@ -29989,7 +29990,7 @@ var CodeBuildProvider = class {
29989
29990
  attributes: {}
29990
29991
  } : null;
29991
29992
  } catch (err) {
29992
- if (err instanceof ResourceNotFoundException$9) return null;
29993
+ if (err instanceof ResourceNotFoundException$10) return null;
29993
29994
  throw err;
29994
29995
  }
29995
29996
  if (!input.cdkPath) return null;
@@ -32525,6 +32526,350 @@ var NestedStackProvider = class {
32525
32526
  }
32526
32527
  };
32527
32528
 
32529
+ //#endregion
32530
+ //#region src/provisioning/providers/acm-certificate-provider.ts
32531
+ /**
32532
+ * AWS ACM Certificate Provider
32533
+ *
32534
+ * Implements `AWS::CertificateManager::Certificate` using the ACM SDK.
32535
+ *
32536
+ * **DNS / EMAIL validation is asynchronous.** `RequestCertificate` returns
32537
+ * immediately with status `PENDING_VALIDATION`; the certificate only reaches
32538
+ * `ISSUED` once AWS has confirmed the DNS records (or the email click).
32539
+ *
32540
+ * `create()` polls `DescribeCertificate` until status flips to `ISSUED`. On
32541
+ * the first poll that returns PENDING_VALIDATION, the provider logs the
32542
+ * `DomainValidationOptions` AWS posted so the user knows which CNAME records
32543
+ * to add to their DNS zone. `CDKD_NO_WAIT=true` (or `cdkd deploy --no-wait`)
32544
+ * short-circuits the loop and returns immediately with the ARN — downstream
32545
+ * consumers (CloudFront, ALB) will fail to start if they reach the cert
32546
+ * before it issues, but that's the documented trade-off.
32547
+ *
32548
+ * **CloudFront cross-region note**: ACM certificates referenced by a
32549
+ * CloudFront Distribution MUST live in `us-east-1`. cdkd does not enforce
32550
+ * this — it's the developer's responsibility to deploy the certificate
32551
+ * stack to `us-east-1`. The provider uses the single ACMClient configured
32552
+ * in `aws-clients.ts` (region = stack's region) and does NOT override.
32553
+ *
32554
+ * Physical id is the certificate ARN. CFn exposes only `Ref` (returns the
32555
+ * ARN); `getAttribute('Arn')` / `getAttribute('CertificateArn')` also
32556
+ * return the ARN for any defensive call site.
32557
+ */
32558
+ var ACMCertificateProvider = class {
32559
+ acmClient;
32560
+ logger = getLogger().child("ACMCertificateProvider");
32561
+ maxPollAttempts = Number(process.env["CDKD_ACM_POLL_ATTEMPTS"] ?? 60);
32562
+ pollIntervalMs = Number(process.env["CDKD_ACM_POLL_INTERVAL_MS"] ?? 1e4);
32563
+ constructor() {
32564
+ this.acmClient = getAwsClients().acm;
32565
+ }
32566
+ handledProperties = new Map([["AWS::CertificateManager::Certificate", new Set([
32567
+ "DomainName",
32568
+ "ValidationMethod",
32569
+ "SubjectAlternativeNames",
32570
+ "DomainValidationOptions",
32571
+ "CertificateAuthorityArn",
32572
+ "CertificateTransparencyLoggingPreference",
32573
+ "CertificateExport",
32574
+ "KeyAlgorithm",
32575
+ "Tags"
32576
+ ])]]);
32577
+ async create(logicalId, resourceType, properties) {
32578
+ this.logger.debug(`Requesting ACM certificate ${logicalId}`);
32579
+ const domainName = properties["DomainName"];
32580
+ if (!domainName) throw new ProvisioningError(`DomainName is required for ACM certificate ${logicalId}`, resourceType, logicalId);
32581
+ const input = { DomainName: domainName };
32582
+ if (properties["ValidationMethod"]) input["ValidationMethod"] = properties["ValidationMethod"];
32583
+ if (Array.isArray(properties["SubjectAlternativeNames"])) input["SubjectAlternativeNames"] = properties["SubjectAlternativeNames"];
32584
+ if (Array.isArray(properties["DomainValidationOptions"])) input["DomainValidationOptions"] = properties["DomainValidationOptions"].map((opt) => {
32585
+ const cleaned = { DomainName: opt["DomainName"] };
32586
+ if (opt["ValidationDomain"]) cleaned["ValidationDomain"] = opt["ValidationDomain"];
32587
+ return cleaned;
32588
+ }).filter((opt) => opt["DomainName"]);
32589
+ if (properties["CertificateAuthorityArn"]) input["CertificateAuthorityArn"] = properties["CertificateAuthorityArn"];
32590
+ if (properties["KeyAlgorithm"]) input["KeyAlgorithm"] = properties["KeyAlgorithm"];
32591
+ const options = {};
32592
+ if (properties["CertificateTransparencyLoggingPreference"]) options["CertificateTransparencyLoggingPreference"] = properties["CertificateTransparencyLoggingPreference"];
32593
+ if (properties["CertificateExport"]) options["Export"] = properties["CertificateExport"];
32594
+ if (Object.keys(options).length > 0) input["Options"] = options;
32595
+ const tags = properties["Tags"];
32596
+ if (tags && Array.isArray(tags) && tags.length > 0) input["Tags"] = tags;
32597
+ try {
32598
+ const certificateArn = (await this.acmClient.send(new RequestCertificateCommand(input))).CertificateArn;
32599
+ if (!certificateArn) throw new ProvisioningError(`RequestCertificate succeeded but no CertificateArn returned for ${logicalId}`, resourceType, logicalId);
32600
+ this.logger.debug(`Requested ACM certificate: ${certificateArn}`);
32601
+ if (!(process.env["CDKD_NO_WAIT"] === "true")) await this.waitForCertificateIssued(certificateArn, logicalId);
32602
+ else this.logger.warn(`Skipping wait for ACM certificate ${logicalId} (CDKD_NO_WAIT=true). Downstream consumers (CloudFront / ALB) will fail until the cert reaches ISSUED.`);
32603
+ return {
32604
+ physicalId: certificateArn,
32605
+ attributes: {
32606
+ Arn: certificateArn,
32607
+ CertificateArn: certificateArn
32608
+ }
32609
+ };
32610
+ } catch (error) {
32611
+ const cause = error instanceof Error ? error : void 0;
32612
+ throw new ProvisioningError(`Failed to create ACM certificate ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
32613
+ }
32614
+ }
32615
+ async update(logicalId, physicalId, resourceType, properties, previousProperties) {
32616
+ this.logger.debug(`Updating ACM certificate ${logicalId}: ${physicalId}`);
32617
+ const changedImmutable = [
32618
+ "DomainName",
32619
+ "ValidationMethod",
32620
+ "SubjectAlternativeNames",
32621
+ "DomainValidationOptions",
32622
+ "CertificateAuthorityArn",
32623
+ "KeyAlgorithm"
32624
+ ].find((k) => JSON.stringify(properties[k]) !== JSON.stringify(previousProperties[k]));
32625
+ if (changedImmutable) {
32626
+ this.logger.debug(`${changedImmutable} changed, replacing ACM certificate: ${physicalId}`);
32627
+ const createResult = await this.create(logicalId, resourceType, properties);
32628
+ try {
32629
+ await this.delete(logicalId, physicalId, resourceType, previousProperties);
32630
+ } catch (error) {
32631
+ this.logger.warn(`Failed to delete old ACM certificate ${physicalId} during replacement: ${String(error)}. The old certificate may be orphaned and require manual cleanup.`);
32632
+ }
32633
+ const result = {
32634
+ physicalId: createResult.physicalId,
32635
+ wasReplaced: true
32636
+ };
32637
+ if (createResult.attributes) result.attributes = createResult.attributes;
32638
+ return result;
32639
+ }
32640
+ try {
32641
+ const newCt = properties["CertificateTransparencyLoggingPreference"];
32642
+ const oldCt = previousProperties["CertificateTransparencyLoggingPreference"];
32643
+ const newExport = properties["CertificateExport"];
32644
+ const oldExport = previousProperties["CertificateExport"];
32645
+ if (newCt !== oldCt || newExport !== oldExport) {
32646
+ const options = {};
32647
+ if (newCt) options["CertificateTransparencyLoggingPreference"] = newCt;
32648
+ if (newExport) options["Export"] = newExport;
32649
+ if (Object.keys(options).length > 0) {
32650
+ await this.acmClient.send(new UpdateCertificateOptionsCommand({
32651
+ CertificateArn: physicalId,
32652
+ Options: options
32653
+ }));
32654
+ this.logger.debug(`Updated certificate Options on ${physicalId}`);
32655
+ }
32656
+ }
32657
+ await this.updateTags(physicalId, properties["Tags"], previousProperties["Tags"]);
32658
+ return {
32659
+ physicalId,
32660
+ wasReplaced: false,
32661
+ attributes: {
32662
+ Arn: physicalId,
32663
+ CertificateArn: physicalId
32664
+ }
32665
+ };
32666
+ } catch (error) {
32667
+ const cause = error instanceof Error ? error : void 0;
32668
+ throw new ProvisioningError(`Failed to update ACM certificate ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
32669
+ }
32670
+ }
32671
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
32672
+ this.logger.debug(`Deleting ACM certificate ${logicalId}: ${physicalId}`);
32673
+ try {
32674
+ try {
32675
+ await this.acmClient.send(new DeleteCertificateCommand({ CertificateArn: physicalId }));
32676
+ } catch (error) {
32677
+ if (error instanceof ResourceNotFoundException$6) {
32678
+ assertRegionMatch(await this.acmClient.config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
32679
+ this.logger.debug(`Certificate ${physicalId} does not exist, skipping deletion`);
32680
+ return;
32681
+ }
32682
+ throw error;
32683
+ }
32684
+ this.logger.debug(`Successfully deleted ACM certificate ${logicalId}`);
32685
+ } catch (error) {
32686
+ const cause = error instanceof Error ? error : void 0;
32687
+ throw new ProvisioningError(`Failed to delete ACM certificate ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
32688
+ }
32689
+ }
32690
+ async getAttribute(physicalId, _resourceType, attributeName) {
32691
+ if (attributeName === "Arn" || attributeName === "CertificateArn") return physicalId;
32692
+ }
32693
+ /**
32694
+ * Read the AWS-current certificate properties in CFn-property shape.
32695
+ *
32696
+ * Coverage:
32697
+ * - `DomainName`, `SubjectAlternativeNames`, `KeyAlgorithm` straight from
32698
+ * `DescribeCertificate.Certificate.*`.
32699
+ * - `CertificateTransparencyLoggingPreference` extracted from the nested
32700
+ * `Options` field and flattened to match CFn shape.
32701
+ * - `Tags` via `ListTagsForCertificate`, with the `aws:cdk:path` etc.
32702
+ * auto-tags filtered out by `normalizeAwsTagsToCfn`.
32703
+ * - `ValidationMethod` / `DomainValidationOptions` are intentionally NOT
32704
+ * surfaced — the deployed cert's validation state is observation-only;
32705
+ * cdkd state stores the request-time input, which can legitimately
32706
+ * diverge from the observed state without indicating drift.
32707
+ *
32708
+ * Returns `undefined` when the cert is gone (`ResourceNotFoundException`).
32709
+ */
32710
+ async readCurrentState(physicalId, _logicalId, _resourceType, _properties) {
32711
+ let cert;
32712
+ try {
32713
+ cert = (await this.acmClient.send(new DescribeCertificateCommand({ CertificateArn: physicalId }))).Certificate;
32714
+ } catch (err) {
32715
+ if (err instanceof ResourceNotFoundException$6) return void 0;
32716
+ throw err;
32717
+ }
32718
+ if (!cert) return void 0;
32719
+ const result = {};
32720
+ if (cert.DomainName !== void 0) result["DomainName"] = cert.DomainName;
32721
+ if (Array.isArray(cert.SubjectAlternativeNames)) result["SubjectAlternativeNames"] = cert.SubjectAlternativeNames;
32722
+ if (cert.KeyAlgorithm !== void 0) result["KeyAlgorithm"] = cert.KeyAlgorithm;
32723
+ if (cert.CertificateAuthorityArn !== void 0) result["CertificateAuthorityArn"] = cert.CertificateAuthorityArn;
32724
+ if (cert.Options?.CertificateTransparencyLoggingPreference !== void 0) result["CertificateTransparencyLoggingPreference"] = cert.Options.CertificateTransparencyLoggingPreference;
32725
+ if (cert.Options?.Export !== void 0) result["CertificateExport"] = cert.Options.Export;
32726
+ try {
32727
+ result["Tags"] = normalizeAwsTagsToCfn((await this.acmClient.send(new ListTagsForCertificateCommand({ CertificateArn: physicalId }))).Tags);
32728
+ } catch (err) {
32729
+ if (!(err instanceof ResourceNotFoundException$6)) throw err;
32730
+ }
32731
+ return result;
32732
+ }
32733
+ /**
32734
+ * Path the deploy engine queries to compare drift snapshots — paths
32735
+ * `readCurrentState` deliberately does NOT round-trip.
32736
+ */
32737
+ getDriftUnknownPaths(_resourceType) {
32738
+ return ["ValidationMethod", "DomainValidationOptions"];
32739
+ }
32740
+ /**
32741
+ * Adopt an existing certificate into cdkd state.
32742
+ *
32743
+ * Lookup order:
32744
+ * 1. `--resource` override (must be an ARN — ACM has no other unique id).
32745
+ * 2. Tag-based `aws:cdk:path` match across `ListCertificates` +
32746
+ * `ListTagsForCertificate`. NOTE: ACM has no `Scope: Local` filter —
32747
+ * `ListCertificates` returns customer-managed certs only (AWS-managed
32748
+ * certs are not surfaced via this API), so no extra guard is needed.
32749
+ */
32750
+ async import(input) {
32751
+ if (input.knownPhysicalId) {
32752
+ const arn = input.knownPhysicalId;
32753
+ if (!arn.startsWith("arn:")) throw new Error(`--resource override for ${input.logicalId} must be an ARN (got '${arn}'). ACM certificates have no human-readable physical id.`);
32754
+ try {
32755
+ await this.acmClient.send(new DescribeCertificateCommand({ CertificateArn: arn }));
32756
+ return {
32757
+ physicalId: arn,
32758
+ attributes: {
32759
+ Arn: arn,
32760
+ CertificateArn: arn
32761
+ }
32762
+ };
32763
+ } catch (err) {
32764
+ if (err instanceof ResourceNotFoundException$6) return null;
32765
+ throw err;
32766
+ }
32767
+ }
32768
+ if (!input.cdkPath) return null;
32769
+ let nextToken;
32770
+ do {
32771
+ const list = await this.acmClient.send(new ListCertificatesCommand({ ...nextToken ? { NextToken: nextToken } : {} }));
32772
+ for (const summary of list.CertificateSummaryList ?? []) {
32773
+ if (!summary.CertificateArn) continue;
32774
+ try {
32775
+ if (matchesCdkPath((await this.acmClient.send(new ListTagsForCertificateCommand({ CertificateArn: summary.CertificateArn }))).Tags, input.cdkPath)) return {
32776
+ physicalId: summary.CertificateArn,
32777
+ attributes: {
32778
+ Arn: summary.CertificateArn,
32779
+ CertificateArn: summary.CertificateArn
32780
+ }
32781
+ };
32782
+ } catch (err) {
32783
+ if (err instanceof ResourceNotFoundException$6) continue;
32784
+ throw err;
32785
+ }
32786
+ }
32787
+ nextToken = list.NextToken;
32788
+ } while (nextToken);
32789
+ return null;
32790
+ }
32791
+ /**
32792
+ * Poll `DescribeCertificate` until status === `ISSUED`. On the FIRST poll
32793
+ * that returns PENDING_VALIDATION, log the DomainValidationOptions AWS
32794
+ * posted so the user knows which CNAME records to add to their DNS zone.
32795
+ *
32796
+ * Throws on `VALIDATION_TIMED_OUT` / `FAILED` (terminal failures) and on
32797
+ * polling-cap exhaustion (treated as timeout). SIGINT short-circuits the
32798
+ * loop and returns control to the deploy engine's cleanup path.
32799
+ */
32800
+ async waitForCertificateIssued(certificateArn, logicalId) {
32801
+ this.logger.debug(`Waiting for ACM certificate ${certificateArn} to reach ISSUED status...`);
32802
+ let interrupted = false;
32803
+ let validationOptionsLogged = false;
32804
+ const sigintHandler = () => {
32805
+ interrupted = true;
32806
+ };
32807
+ process.on("SIGINT", sigintHandler);
32808
+ try {
32809
+ for (let attempt = 1; attempt <= this.maxPollAttempts; attempt++) {
32810
+ if (interrupted) {
32811
+ this.logger.debug(`ACM certificate ${certificateArn} wait interrupted by SIGINT, proceeding`);
32812
+ return;
32813
+ }
32814
+ const resp = await this.acmClient.send(new DescribeCertificateCommand({ CertificateArn: certificateArn }));
32815
+ const status = resp.Certificate?.Status;
32816
+ const validations = resp.Certificate?.DomainValidationOptions ?? [];
32817
+ if (status === "ISSUED") {
32818
+ this.logger.debug(`ACM certificate ${certificateArn} is ISSUED`);
32819
+ return;
32820
+ }
32821
+ if (status === "FAILED" || status === "VALIDATION_TIMED_OUT" || status === "INACTIVE" || status === "REVOKED" || status === "EXPIRED") throw new Error(`ACM certificate ${logicalId} (${certificateArn}) entered terminal status ${status} during validation. Check ACM console / DNS records to diagnose.`);
32822
+ if (status === "PENDING_VALIDATION" && !validationOptionsLogged && validations.length > 0) {
32823
+ this.logValidationOptions(validations);
32824
+ validationOptionsLogged = true;
32825
+ }
32826
+ this.logger.debug(`ACM certificate ${certificateArn} status: ${status} (attempt ${attempt}/${this.maxPollAttempts})`);
32827
+ const sleepEnd = Date.now() + this.pollIntervalMs;
32828
+ const tickMs = Math.min(1e3, this.pollIntervalMs);
32829
+ while (Date.now() < sleepEnd && !interrupted) await new Promise((resolve) => setTimeout(resolve, tickMs));
32830
+ }
32831
+ throw new Error(`ACM certificate ${logicalId} (${certificateArn}) did not reach ISSUED status within ${this.maxPollAttempts * this.pollIntervalMs / 1e3}s. If your DNS zone is manually managed, you may need to increase --resource-timeout AWS::CertificateManager::Certificate=<duration> or set CDKD_NO_WAIT=true.`);
32832
+ } finally {
32833
+ process.removeListener("SIGINT", sigintHandler);
32834
+ }
32835
+ }
32836
+ /**
32837
+ * Pretty-print the validation records AWS expects in the DNS zone, so the
32838
+ * user can copy / paste them into Route 53 / Cloudflare / etc. while the
32839
+ * cert is still PENDING_VALIDATION.
32840
+ */
32841
+ logValidationOptions(validations) {
32842
+ const lines = ["ACM certificate is PENDING_VALIDATION. Add the following DNS records to validate:"];
32843
+ for (const v of validations) if (v.ValidationMethod === "DNS" && v.ResourceRecord) {
32844
+ const r = v.ResourceRecord;
32845
+ lines.push(` ${v.DomainName} — ${r.Type} ${r.Name} -> ${r.Value}`);
32846
+ } else if (v.ValidationMethod === "EMAIL") {
32847
+ const emails = (v.ValidationEmails ?? []).join(", ");
32848
+ lines.push(` ${v.DomainName} — confirmation email sent to: ${emails || "<none>"}`);
32849
+ }
32850
+ this.logger.info(lines.join("\n"));
32851
+ }
32852
+ async updateTags(certificateArn, newTags, oldTags) {
32853
+ const newTagMap = new Map((newTags || []).map((t) => [t.Key, t.Value]));
32854
+ const oldTagMap = new Map((oldTags || []).map((t) => [t.Key, t.Value]));
32855
+ const tagsToRemove = [];
32856
+ for (const key of oldTagMap.keys()) if (!newTagMap.has(key)) tagsToRemove.push({ Key: key });
32857
+ const tagsToAdd = [];
32858
+ for (const [key, value] of newTagMap) if (oldTagMap.get(key) !== value) tagsToAdd.push({
32859
+ Key: key,
32860
+ Value: value
32861
+ });
32862
+ if (tagsToRemove.length > 0) await this.acmClient.send(new RemoveTagsFromCertificateCommand({
32863
+ CertificateArn: certificateArn,
32864
+ Tags: tagsToRemove
32865
+ }));
32866
+ if (tagsToAdd.length > 0) await this.acmClient.send(new AddTagsToCertificateCommand({
32867
+ CertificateArn: certificateArn,
32868
+ Tags: tagsToAdd
32869
+ }));
32870
+ }
32871
+ };
32872
+
32528
32873
  //#endregion
32529
32874
  //#region src/provisioning/register-providers.ts
32530
32875
  /**
@@ -32620,6 +32965,7 @@ function registerAllProviders(registry) {
32620
32965
  registry.register("AWS::Route53::RecordSet", route53Provider);
32621
32966
  registry.register("AWS::WAFv2::WebACL", new WAFv2WebACLProvider());
32622
32967
  registry.register("AWS::Cognito::UserPool", new CognitoUserPoolProvider());
32968
+ registry.register("AWS::CertificateManager::Certificate", new ACMCertificateProvider());
32623
32969
  const elasticacheProvider = new ElastiCacheProvider();
32624
32970
  registry.register("AWS::ElastiCache::SubnetGroup", elasticacheProvider);
32625
32971
  registry.register("AWS::ElastiCache::CacheCluster", elasticacheProvider);
@@ -41139,6 +41485,418 @@ async function buildCrossStackResolver(consumerRegion, opts) {
41139
41485
  };
41140
41486
  }
41141
41487
 
41488
+ //#endregion
41489
+ //#region src/local/s3-local-state-provider.ts
41490
+ /**
41491
+ * `S3LocalStateProvider` — implementation of {@link LocalStateProvider}
41492
+ * backed by cdkd's S3 state. Wraps the existing
41493
+ * `loadStateForStack` + `buildCrossStackResolver` helpers in
41494
+ * `src/cli/commands/local-state-loader.ts` so the four `cdkd local *`
41495
+ * commands can route both `--from-state` and `--from-cfn-stack`
41496
+ * through the same provider-shaped interface (issue #606).
41497
+ *
41498
+ * Behavior is identical to the pre-issue-#606 code path — this file
41499
+ * exists ONLY to give the CLI layer a single interface against which
41500
+ * to wire both flags, so adding a third state source (a future
41501
+ * `--from-tf-state`? out of scope for now) doesn't require touching
41502
+ * the four `local-*.ts` command files again.
41503
+ */
41504
+ var S3LocalStateProvider = class {
41505
+ label = "--from-state";
41506
+ opts;
41507
+ disposers = [];
41508
+ constructor(opts) {
41509
+ this.opts = opts;
41510
+ }
41511
+ async load(stackName, synthRegion) {
41512
+ const loaded = await loadStateForStack(stackName, synthRegion, {
41513
+ statePrefix: this.opts.statePrefix,
41514
+ ...this.opts.stackRegion !== void 0 && { stackRegion: this.opts.stackRegion },
41515
+ ...this.opts.stateBucket !== void 0 && { stateBucket: this.opts.stateBucket },
41516
+ ...this.opts.region !== void 0 && { region: this.opts.region },
41517
+ ...this.opts.profile !== void 0 && { profile: this.opts.profile }
41518
+ });
41519
+ if (!loaded) return void 0;
41520
+ const outputs = {};
41521
+ for (const [k, v] of Object.entries(loaded.state.outputs ?? {})) if (typeof v === "string") outputs[k] = v;
41522
+ else if (typeof v === "number" || typeof v === "boolean") outputs[k] = String(v);
41523
+ else outputs[k] = JSON.stringify(v);
41524
+ return {
41525
+ resources: loaded.state.resources,
41526
+ outputs,
41527
+ region: loaded.region
41528
+ };
41529
+ }
41530
+ async buildCrossStackResolver(consumerRegion) {
41531
+ const built = await buildCrossStackResolver(consumerRegion, {
41532
+ statePrefix: this.opts.statePrefix,
41533
+ ...this.opts.stateBucket !== void 0 && { stateBucket: this.opts.stateBucket },
41534
+ ...this.opts.region !== void 0 && { region: this.opts.region },
41535
+ ...this.opts.profile !== void 0 && { profile: this.opts.profile }
41536
+ });
41537
+ if (!built) return void 0;
41538
+ this.disposers.push(built.dispose);
41539
+ return built.resolver;
41540
+ }
41541
+ dispose() {
41542
+ while (this.disposers.length > 0) {
41543
+ const fn = this.disposers.pop();
41544
+ if (fn) try {
41545
+ fn();
41546
+ } catch {}
41547
+ }
41548
+ }
41549
+ };
41550
+
41551
+ //#endregion
41552
+ //#region src/local/cfn-local-state-provider.ts
41553
+ /**
41554
+ * `CfnLocalStateProvider` — implementation of {@link LocalStateProvider}
41555
+ * backed by a deployed CloudFormation stack. Powers `cdkd local *
41556
+ * --from-cfn-stack` (issue #606).
41557
+ *
41558
+ * The shape mirrors the SAM CLI's `sam local invoke --stack-name X`
41559
+ * behavior: reach into a deployed CFn stack via `DescribeStackResources`
41560
+ * to look up physical IDs of every same-stack resource, then make those
41561
+ * IDs available to the existing `state-resolver.ts` substitution engine.
41562
+ * This lets `cdkd local *` substitute env vars / secrets / images that
41563
+ * reference deployed resources in a CDK app deployed via the upstream
41564
+ * CDK CLI (`cdk deploy` → CloudFormation) WITHOUT first migrating the
41565
+ * stack to cdkd.
41566
+ *
41567
+ * Wire-format mapping:
41568
+ *
41569
+ * - `Ref: <LogicalId>` → resolved via the synthetic `ResourceState`
41570
+ * map built from `DescribeStackResources.StackResources[]` (one
41571
+ * entry per `(LogicalResourceId, PhysicalResourceId, ResourceType)`
41572
+ * tuple).
41573
+ * - `Fn::GetAtt: [<LogicalId>, <Attr>]` → **warn-and-drop**. CFn's
41574
+ * `DescribeStackResources` does NOT return per-attribute values
41575
+ * and the v1 policy (issue #606 recommendation (a)) is to surface
41576
+ * a per-key warn instead of pulling in the full provisioning layer
41577
+ * to call provider-specific describe APIs (e.g. `GetQueueAttributes`
41578
+ * for SQS, `GetFunction` for Lambda). Users override the affected
41579
+ * env var via `--env-vars` if the value is critical.
41580
+ * - `Fn::ImportValue: <exportName>` → resolved via `ListExports`
41581
+ * (paginated). Same-region only — CFn exports are region-scoped.
41582
+ * - `Fn::GetStackOutput` → rejected with a clear pointer that the
41583
+ * intrinsic is cdkd-specific (CFn has no equivalent — exports +
41584
+ * outputs are the only cross-stack vocabulary CFn understands).
41585
+ * - Stack outputs (consumed by both `Fn::GetStackOutput` and the
41586
+ * cross-stack-resolver's index-miss fallback) → sourced from
41587
+ * `DescribeStacks.Outputs[]`.
41588
+ *
41589
+ * Region handling: the provider takes a single region at construction
41590
+ * time (the `cdkd local *` commands resolve this from
41591
+ * `--stack-region` > `--region` > `AWS_REGION` > the synth-derived
41592
+ * region per the existing `--from-state` precedence). Cross-region
41593
+ * `Fn::ImportValue` is out of scope for v1 (CFn's `ListExports` is
41594
+ * region-scoped; a future PR can add a multi-region scan if real
41595
+ * usage justifies it).
41596
+ *
41597
+ * AWS API contract notes:
41598
+ *
41599
+ * - `DescribeStackResources` is unpaginated up to 500 resources (CFn's
41600
+ * hard stack cap). One call suffices for the entire stack.
41601
+ * - `DescribeStacks` is unpaginated when called with `StackName`.
41602
+ * - `ListExports` is paginated; the provider walks `NextToken` until
41603
+ * the page set is exhausted.
41604
+ */
41605
+ var CfnLocalStateProvider = class {
41606
+ label = "--from-cfn-stack";
41607
+ cfnStackName;
41608
+ region;
41609
+ client;
41610
+ clientOptions;
41611
+ constructor(opts) {
41612
+ this.cfnStackName = opts.cfnStackName;
41613
+ this.region = opts.region;
41614
+ this.clientOptions = { region: opts.region };
41615
+ if (opts.profile !== void 0) this.clientOptions.profile = opts.profile;
41616
+ }
41617
+ getClient() {
41618
+ if (!this.client) this.client = new CloudFormationClient({ region: this.region });
41619
+ return this.client;
41620
+ }
41621
+ /**
41622
+ * Load the deployed CFn stack's resources + outputs and return them
41623
+ * as a synthetic `LocalStateRecord` (matching the shape the existing
41624
+ * S3-state-driven path produces). `synthRegion` is accepted for
41625
+ * interface parity with the S3 provider but ignored here — the
41626
+ * provider is region-bound at construction time.
41627
+ *
41628
+ * Best-effort: on any CFn API failure (stack not found, access
41629
+ * denied, throttling) the provider logs a warn and returns
41630
+ * `undefined`. The caller then falls back to the PR 1 warn-and-drop
41631
+ * behavior on every intrinsic-valued env var.
41632
+ */
41633
+ async load(_stackName, _synthRegion) {
41634
+ const logger = getLogger();
41635
+ const client = this.getClient();
41636
+ let resourceMap;
41637
+ try {
41638
+ resourceMap = buildResourceStateMap((await client.send(new DescribeStackResourcesCommand({ StackName: this.cfnStackName }))).StackResources ?? []);
41639
+ } catch (err) {
41640
+ logger.warn(`${this.label}: DescribeStackResources(${this.cfnStackName}) failed: ${err instanceof Error ? err.message : String(err)}. Was the stack deployed in region '${this.region}'? Falling back.`);
41641
+ return;
41642
+ }
41643
+ let outputs;
41644
+ try {
41645
+ const stack = (await client.send(new DescribeStacksCommand({ StackName: this.cfnStackName }))).Stacks?.[0];
41646
+ if (!stack) {
41647
+ logger.warn(`${this.label}: DescribeStacks(${this.cfnStackName}) returned no stack; outputs will be empty.`);
41648
+ outputs = {};
41649
+ } else outputs = buildOutputsMap(stack.Outputs ?? []);
41650
+ } catch (err) {
41651
+ logger.warn(`${this.label}: DescribeStacks(${this.cfnStackName}) failed: ${err instanceof Error ? err.message : String(err)}. Outputs will be empty (Fn::GetStackOutput cannot resolve).`);
41652
+ outputs = {};
41653
+ }
41654
+ return {
41655
+ resources: resourceMap,
41656
+ outputs,
41657
+ region: this.region
41658
+ };
41659
+ }
41660
+ /**
41661
+ * Build a `CrossStackResolver` that resolves `Fn::ImportValue` via
41662
+ * `cloudformation:ListExports`. `Fn::GetStackOutput` is rejected here
41663
+ * — it's a cdkd-specific intrinsic with no CFn-side equivalent, and
41664
+ * the user-visible error message names the right intrinsic
41665
+ * (`Fn::ImportValue`) for that use case.
41666
+ *
41667
+ * `consumerRegion` is accepted for interface parity with the S3
41668
+ * provider but the `CfnLocalStateProvider` only resolves exports in
41669
+ * the region the stack lives in (which is the same region the
41670
+ * consumer Lambda runs in for the common single-region use case).
41671
+ * A future PR can extend this to multi-region by walking the SDK's
41672
+ * partition-aware region list.
41673
+ */
41674
+ async buildCrossStackResolver(_consumerRegion) {
41675
+ const logger = getLogger();
41676
+ const client = this.getClient();
41677
+ const label = this.label;
41678
+ const region = this.region;
41679
+ let cachedExports;
41680
+ const ensureExports = async () => {
41681
+ if (cachedExports) return cachedExports;
41682
+ const result = await fetchAllExports(client).catch((err) => {
41683
+ logger.warn(`${label}: ListExports (${region}) failed: ${err instanceof Error ? err.message : String(err)}. Fn::ImportValue intrinsics will warn-and-drop.`);
41684
+ });
41685
+ if (result) cachedExports = result;
41686
+ return result;
41687
+ };
41688
+ return {
41689
+ async resolveImport(exportName) {
41690
+ const map = await ensureExports();
41691
+ if (!map) return void 0;
41692
+ return map.get(exportName);
41693
+ },
41694
+ async resolveGetStackOutput(producerStack, producerRegion, outputName) {
41695
+ logger.warn(`${label}: Fn::GetStackOutput '${producerStack}.${outputName}' (${producerRegion}) is a cdkd-specific intrinsic with no CloudFormation equivalent. Use Fn::ImportValue against an exported output instead, or deploy the producer stack via cdkd deploy and use --from-state.`);
41696
+ }
41697
+ };
41698
+ }
41699
+ dispose() {
41700
+ if (this.client) {
41701
+ this.client.destroy();
41702
+ this.client = void 0;
41703
+ }
41704
+ }
41705
+ };
41706
+ /**
41707
+ * Build the synthetic per-logical-id resource map from
41708
+ * `DescribeStackResources` output. Each `ResourceState` carries the
41709
+ * physical id (covers `Ref`) and the resource type; `attributes` is
41710
+ * left empty per issue #606's (a) recommendation — the warn-and-drop
41711
+ * policy on unresolvable `Fn::GetAtt` is the v1 contract. The other
41712
+ * `ResourceState` fields (`properties`, `dependencies`, etc.) are
41713
+ * also left empty since the substituter doesn't read them.
41714
+ *
41715
+ * Exported for unit testing.
41716
+ */
41717
+ function buildResourceStateMap(stackResources) {
41718
+ const out = {};
41719
+ for (const r of stackResources) {
41720
+ if (!r.LogicalResourceId || !r.PhysicalResourceId || !r.ResourceType) continue;
41721
+ out[r.LogicalResourceId] = {
41722
+ physicalId: r.PhysicalResourceId,
41723
+ resourceType: r.ResourceType,
41724
+ properties: {},
41725
+ attributes: {},
41726
+ dependencies: []
41727
+ };
41728
+ }
41729
+ return out;
41730
+ }
41731
+ /**
41732
+ * Build the outputs map from `DescribeStacks.Outputs[]`. CFn outputs
41733
+ * are stringly typed at the wire level (key + value, with the value
41734
+ * always a string), so the cast is safe.
41735
+ *
41736
+ * Exported for unit testing.
41737
+ */
41738
+ function buildOutputsMap(outputs) {
41739
+ const out = {};
41740
+ for (const o of outputs) {
41741
+ if (o.OutputKey === void 0 || o.OutputValue === void 0) continue;
41742
+ out[o.OutputKey] = o.OutputValue;
41743
+ }
41744
+ return out;
41745
+ }
41746
+ /**
41747
+ * Walk `ListExports` until every page is consumed and return the
41748
+ * `Name -> Value` map. Same-region only (CFn exports are
41749
+ * region-scoped); the caller picks the region at provider
41750
+ * construction time.
41751
+ *
41752
+ * Exported for unit testing.
41753
+ */
41754
+ async function fetchAllExports(client) {
41755
+ const out = /* @__PURE__ */ new Map();
41756
+ let nextToken;
41757
+ let pages = 0;
41758
+ do {
41759
+ const resp = await client.send(new ListExportsCommand({ ...nextToken !== void 0 && { NextToken: nextToken } }));
41760
+ for (const exp of resp.Exports ?? []) {
41761
+ if (exp.Name === void 0 || exp.Value === void 0) continue;
41762
+ out.set(exp.Name, exp.Value);
41763
+ }
41764
+ nextToken = resp.NextToken;
41765
+ pages += 1;
41766
+ if (pages > 50) throw new Error("ListExports pagination exceeded 50 pages — likely a malformed NextToken loop.");
41767
+ } while (nextToken !== void 0);
41768
+ return out;
41769
+ }
41770
+
41771
+ //#endregion
41772
+ //#region src/cli/commands/local-state-source.ts
41773
+ /**
41774
+ * Single-source-of-truth helper that picks a {@link LocalStateProvider}
41775
+ * for the `cdkd local *` family from CLI flags (issue #606).
41776
+ *
41777
+ * The four `cdkd local *` commands all support two mutually-exclusive
41778
+ * state-source flags:
41779
+ *
41780
+ * - `--from-state` (S3-backed; reads cdkd's state for a stack
41781
+ * deployed via `cdkd deploy`).
41782
+ * - `--from-cfn-stack [<cfn-stack-name>]` (CFn-backed; reads a
41783
+ * deployed CloudFormation stack via `DescribeStackResources`).
41784
+ *
41785
+ * This module centralizes:
41786
+ *
41787
+ * - The mutual-exclusion check (rejected at the CLI layer before any
41788
+ * synth / AWS call fires).
41789
+ * - The bare-vs-explicit `--from-cfn-stack` resolution: bare flag uses
41790
+ * the cdkd stack name; explicit value overrides. Matches the
41791
+ * `cdkd import --migrate-from-cloudformation` precedent.
41792
+ * - Region resolution for the CFn client: reuses the existing
41793
+ * `--stack-region` flag (no separate `--cfn-stack-region`) per
41794
+ * issue #606 recommendation.
41795
+ *
41796
+ * Returns `undefined` when neither flag is set — the caller skips the
41797
+ * substitution pass entirely (which is the pre-issue-#606 behavior
41798
+ * when `--from-state` was absent).
41799
+ */
41800
+ /**
41801
+ * Default cdkd stack name → CFn stack name. Matches the
41802
+ * `cdkd import --migrate-from-cloudformation` bare-form precedent:
41803
+ * bare `--from-cfn-stack` uses the cdkd stack name verbatim as the CFn
41804
+ * stack name (typical for CDK apps where the names are the same).
41805
+ * Override by passing `--from-cfn-stack <explicit-name>`.
41806
+ *
41807
+ * Exported for unit testing.
41808
+ */
41809
+ function resolveCfnStackName(fromCfnStack, cdkdStackName) {
41810
+ if (typeof fromCfnStack === "string") return fromCfnStack;
41811
+ return cdkdStackName;
41812
+ }
41813
+ /**
41814
+ * Resolve the region used for the CFn client. The CFn provider is
41815
+ * region-bound at construction time; we apply the precedence chain
41816
+ * `--stack-region` > `--region` > `AWS_REGION` > `AWS_DEFAULT_REGION`
41817
+ * > the synth-derived stack region. Throws `LocalStateSourceError`
41818
+ * when none of these signals is set — the CFn API call needs a
41819
+ * concrete region and silently picking `us-east-1` would query the
41820
+ * wrong stack environment (worst case: succeed against the wrong
41821
+ * stack and return wrong physical IDs). Distinct from
41822
+ * `loadStateForStack`'s behavior: the S3 state bucket name is
41823
+ * account-scoped (not region-scoped) and the bucket's region is
41824
+ * auto-discovered via `GetBucketLocation`, so the S3 provider can
41825
+ * tolerate a missing region. The CFn provider cannot.
41826
+ *
41827
+ * Exported for unit testing.
41828
+ */
41829
+ function resolveCfnRegion(options, synthRegion) {
41830
+ const region = options.stackRegion ?? options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion;
41831
+ if (region === void 0) throw new LocalStateSourceError("--from-cfn-stack requires a region to query CloudFormation. Set one of: --stack-region <region>, --region <region>, AWS_REGION env var, AWS_DEFAULT_REGION env var, or an env.region on the target CDK stack.");
41832
+ return region;
41833
+ }
41834
+ /**
41835
+ * Common error class for the mutual-exclusion check so the CLI layer
41836
+ * can surface a consistent error message from all four commands.
41837
+ */
41838
+ var LocalStateSourceError = class extends Error {
41839
+ constructor(message) {
41840
+ super(message);
41841
+ this.name = "LocalStateSourceError";
41842
+ }
41843
+ };
41844
+ /**
41845
+ * Pre-flight check for `--from-cfn-stack <explicit-name>` when the
41846
+ * caller will construct one provider per routed stack (`local
41847
+ * start-api` / `local start-service`). An explicit value applies to
41848
+ * the SINGLE CFn stack named — when multiple cdkd stacks are routed,
41849
+ * every one of them would query the same CFn stack, yielding silent
41850
+ * wrong-physical-id substitutions for any logical id that happens to
41851
+ * collide between the user's stacks. Reject at the CLI layer instead.
41852
+ *
41853
+ * Bare `--from-cfn-stack` (the cdkdStackName-default) is fine for
41854
+ * multi-stack: each routed stack reads its own CFn counterpart.
41855
+ * `--from-state` is also fine: cdkd's state is per-(stack, region).
41856
+ *
41857
+ * Call this from `start-api` / `start-service` BEFORE the per-stack
41858
+ * `createLocalStateProvider` loop when `routedStackCount > 1`.
41859
+ */
41860
+ function rejectExplicitCfnStackWithMultipleStacks(options, routedStackCount) {
41861
+ if (routedStackCount <= 1) return;
41862
+ if (typeof options.fromCfnStack !== "string") return;
41863
+ throw new LocalStateSourceError(`--from-cfn-stack <name> cannot be used with multiple routed stacks (got ${routedStackCount}). An explicit CFn stack name applies to one stack only and would silently mismap logical IDs across siblings. Use bare --from-cfn-stack (each cdkd stack uses its own name as the CFn stack name) or run one cdkd local invocation per stack.`);
41864
+ }
41865
+ /**
41866
+ * Pick and construct the right `LocalStateProvider` for the supplied
41867
+ * flag set. Returns `undefined` when neither flag is set (caller skips
41868
+ * the substitution pass). Throws `LocalStateSourceError` when both
41869
+ * flags are set (mutually exclusive — different state sources, asking
41870
+ * for both is ambiguous about precedence).
41871
+ *
41872
+ * `cdkdStackName` is the cdkd-side stack name the local command
41873
+ * resolved to its target — needed to apply the bare-`--from-cfn-stack`
41874
+ * default. `synthRegion` is the synth-derived stack region (`env.region`
41875
+ * on the CDK stack) — fallback for the CFn client when no explicit
41876
+ * region override is set.
41877
+ *
41878
+ * For multi-stack callers (`local start-api` / `local start-service`)
41879
+ * also invoke `rejectExplicitCfnStackWithMultipleStacks` BEFORE the
41880
+ * per-stack loop — see that helper's docstring for the rationale.
41881
+ */
41882
+ function createLocalStateProvider(options, cdkdStackName, synthRegion) {
41883
+ const cfnStackOpt = options.fromCfnStack;
41884
+ const cfnFlagPresent = cfnStackOpt !== void 0 && cfnStackOpt !== false;
41885
+ if (options.fromState && cfnFlagPresent) throw new LocalStateSourceError("--from-state and --from-cfn-stack are mutually exclusive. Use --from-state for stacks deployed via `cdkd deploy`; use --from-cfn-stack for stacks deployed via `cdk deploy` (CloudFormation).");
41886
+ if (options.fromState) return new S3LocalStateProvider({
41887
+ statePrefix: options.statePrefix,
41888
+ ...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
41889
+ ...options.region !== void 0 && { region: options.region },
41890
+ ...options.profile !== void 0 && { profile: options.profile },
41891
+ ...options.stackRegion !== void 0 && { stackRegion: options.stackRegion }
41892
+ });
41893
+ if (cfnFlagPresent) return new CfnLocalStateProvider({
41894
+ cfnStackName: resolveCfnStackName(cfnStackOpt, cdkdStackName),
41895
+ region: resolveCfnRegion(options, synthRegion),
41896
+ ...options.profile !== void 0 && { profile: options.profile }
41897
+ });
41898
+ }
41899
+
41142
41900
  //#endregion
41143
41901
  //#region src/local/intrinsic-image.ts
41144
41902
  /**
@@ -53069,7 +53827,7 @@ async function localStartApiCommand(target, options) {
53069
53827
  const m = buildCorsConfigByApiId(stack.template);
53070
53828
  for (const [k, v] of m) corsConfigByApiId.set(k, v);
53071
53829
  }
53072
- const stateByStack = options.fromState ? await loadStateForRoutedStacks(targetStacks, routes, routesWithAuth, options) : /* @__PURE__ */ new Map();
53830
+ const stateByStack = options.fromState || options.fromCfnStack !== void 0 && options.fromCfnStack !== false ? await loadStateForRoutedStacks(targetStacks, routes, routesWithAuth, options) : /* @__PURE__ */ new Map();
53073
53831
  const lambdaIds = uniqueLambdaIds(routes, routesWithAuth, webSocketApis);
53074
53832
  const specs = /* @__PURE__ */ new Map();
53075
53833
  for (let i = 0; i < lambdaIds.length; i++) {
@@ -54109,13 +54867,14 @@ function envHasIntrinsicValue$1(templateEnv) {
54109
54867
  return false;
54110
54868
  }
54111
54869
  /**
54112
- * Load cdkd's S3 state for every stack that owns a routed Lambda. Once
54870
+ * Load deployed state for every stack that owns a routed Lambda. Once
54113
54871
  * per `synthesizeAndBuild` pass (initial boot + every reload), so a
54114
54872
  * Lambda's per-spec build does not pay one round-trip per Lambda. Per-
54115
54873
  * stack failures (no state, ambiguous region, bucket resolution error)
54116
- * degrade to warn-and-fall-back via {@link loadStateForStack} the
54117
- * affected stack's reachable Lambdas behave as if `--from-state` were
54118
- * not set, while sibling stacks with loadable state still substitute.
54874
+ * degrade to warn-and-fall-back via the active `LocalStateProvider`
54875
+ * the affected stack's reachable Lambdas behave as if `--from-state` /
54876
+ * `--from-cfn-stack` were not set, while sibling stacks with loadable
54877
+ * state still substitute.
54119
54878
  *
54120
54879
  * Pseudo parameters are resolved per stack and only when at least one
54121
54880
  * reachable Lambda in that stack has an intrinsic-valued env entry
@@ -54144,24 +54903,31 @@ async function loadStateForRoutedStacks(stacks, routes, routesWithAuth, options)
54144
54903
  }
54145
54904
  return false;
54146
54905
  };
54906
+ rejectExplicitCfnStackWithMultipleStacks(options, reachableStackNames.size);
54147
54907
  for (const stackName of reachableStackNames) {
54148
54908
  const stack = stacks.find((s) => s.stackName === stackName);
54149
54909
  if (!stack) continue;
54150
- const loaded = await loadStateForStack(stack.stackName, stack.region, {
54151
- ...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
54152
- ...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
54153
- statePrefix: options.statePrefix,
54154
- ...options.region !== void 0 && { region: options.region },
54155
- ...options.profile !== void 0 && { profile: options.profile }
54156
- });
54157
- if (!loaded) continue;
54158
- const bundle = { state: loaded.state };
54159
- if (stackHasIntrinsicEnv(stackName)) {
54160
- const pseudo = await resolvePseudoParametersForStartApi(loaded.region, options);
54161
- if (pseudo) bundle.pseudoParameters = pseudo;
54910
+ const provider = createLocalStateProvider(options, stack.stackName, stack.region);
54911
+ if (!provider) continue;
54912
+ try {
54913
+ const loaded = await provider.load(stack.stackName, stack.region);
54914
+ if (!loaded) continue;
54915
+ const bundle = { state: {
54916
+ version: 1,
54917
+ stackName: stack.stackName,
54918
+ resources: loaded.resources,
54919
+ outputs: loaded.outputs,
54920
+ lastModified: 0
54921
+ } };
54922
+ if (stackHasIntrinsicEnv(stackName)) {
54923
+ const pseudo = await resolvePseudoParametersForStartApi(loaded.region, options);
54924
+ if (pseudo) bundle.pseudoParameters = pseudo;
54925
+ }
54926
+ out.set(stackName, bundle);
54927
+ logger.debug(`${provider.label}: loaded state for ${stackName} (${loaded.region})`);
54928
+ } finally {
54929
+ provider.dispose();
54162
54930
  }
54163
- out.set(stackName, bundle);
54164
- logger.debug(`--from-state: loaded state for ${stackName} (${loaded.region})`);
54165
54931
  }
54166
54932
  return out;
54167
54933
  }
@@ -54174,7 +54940,7 @@ async function loadStateForRoutedStacks(stacks, routes, routesWithAuth, options)
54174
54940
  * region takes priority).
54175
54941
  *
54176
54942
  * Region precedence: `--region` > `AWS_REGION` > `AWS_DEFAULT_REGION` >
54177
- * the state record's region (returned by `loadStateForStack`).
54943
+ * the state record's region (returned by the active `LocalStateProvider`).
54178
54944
  */
54179
54945
  async function resolvePseudoParametersForStartApi(stateRegion, options) {
54180
54946
  const logger = getLogger();
@@ -54238,7 +55004,7 @@ function resolveMtlsConfig(options) {
54238
55004
  * Builder for the `start-api` subcommand. Wired up by `local.ts`.
54239
55005
  */
54240
55006
  function createLocalStartApiCommand() {
54241
- const startApi = new Command("start-api").description("Run a long-running local HTTP server that maps API Gateway routes (REST v1, HTTP API, Function URL) to Lambda invocations against the AWS Lambda Runtime Interface Emulator (Docker required). Supports Lambda TOKEN/REQUEST authorizers, Cognito User Pool / HTTP v2 JWT authorizers, and REST v1 AWS_IAM (SigV4 signature verification only — IAM policy evaluation is NOT emulated; see docs/local-emulation.md). When JWKS is unreachable, JWT authorizers fall back to pass-through (every token accepted) with a warn line — local dev fallback. VPC-config Lambdas run locally and surface a warn line at startup; their containers do NOT get attached to the deployed VPC subnets, so calls to private RDS / ElastiCache will fail.").argument("[target]", "Optional API filter. Accepts the bare CDK logical id ('MyHttpApi'; single-stack apps only), stack-qualified logical id ('MyStack:MyHttpApi'), full CDK Construct path ('MyStack/MyHttpApi/Resource'), or an ancestor Construct path that prefix-matches ('MyStack/MyHttpApi'). When omitted, every discovered API gets its own server. Mirrors `cdkd local invoke` / `cdkd local run-task` target syntax.").addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--stack <name>", "Stack to start (single-stack apps auto-detect)")).addOption(new Option("--warm", "Pre-start one container per Lambda at server boot").default(false)).addOption(new Option("--per-lambda-concurrency <n>", "Pool size cap per Lambda (default 2, max 4)").default("2")).addOption(new Option("--no-pull", "Skip docker pull (cached image)")).addOption(new Option("--container-host <host>", "IP the host uses to bind/probe the RIE port (must be a numeric IP — `docker run -p <ip>:<port>:8080` rejects hostnames). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--debug-port-base <port>", "Reserve a contiguous --debug-port range (one per Lambda)")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--assume-role <arn-or-pair>", "Assume the Lambda's execution role and forward STS-issued temp creds. Bare <arn> = global default; <LogicalId>=<arn> = per-Lambda override (repeatable). Per-Lambda > global > unset (developer creds passed through).").argParser((raw, prev) => parseAssumeRoleToken(raw, prev))).addOption(new Option("--watch", "Hot-reload: re-synth + re-discover routes when cdk.out/ or asset directories change. Off by default; the server keeps the previous version serving when synth fails mid-reload.").default(false)).addOption(new Option("--stage <name>", "Select an API Gateway Stage by its 'StageName'. Default: the first Stage attached to each API. Drives event.stageVariables for both REST v1 and HTTP API v2. NOTE: For HTTP API v2 routes, requestContext.stage is always '$default' regardless of this flag (AWS-side limitation — HTTP API only exposes one stage to the integration event); only event.stageVariables is affected for v2 routes. For REST v1 routes the selected StageName is also threaded into requestContext.stage.")).addOption(new Option("--api <id>", "DEPRECATED — use the positional <target> argument instead. Same accepted forms (bare logical id, stack-qualified, Construct path, ancestor prefix). Will be removed in a future major release.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--from-state", "Read cdkd S3 state for every routed stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::Join (and AWS pseudo parameters) in Lambda env vars with the deployed physical IDs / attributes. Off by default — pre-PR warn-and-drop semantics are preserved. Turn on for stacks already deployed via cdkd deploy. Mirrors `cdkd local invoke --from-state` / `cdkd local run-task --from-state`. Re-runs against fresh state on every hot-reload firing (--watch).").default(false)).addOption(new Option("--stack-region <region>", "Region of the cdkd state record to read (used with --from-state when the same stack name has state in multiple regions).")).addOption(new Option("--mtls-truststore <path>", "PEM-encoded CA bundle for client-certificate verification (mutual TLS). When set, the local server switches from HTTP to HTTPS and the TLS handshake rejects clients whose certificate doesn't chain to one of these CAs. Verified certs are surfaced on the Lambda event under requestContext.identity.clientCert (REST v1) / requestContext.authentication.clientCert (HTTP API v2). Must be set together with --mtls-cert + --mtls-key; partial flag sets are rejected. Generate a CA + server + client cert for local dev: openssl req -x509 -newkey rsa:2048 -nodes -keyout ca-key.pem -out ca.pem -subj \"/CN=cdkd-local-ca\" -days 365; openssl req -newkey rsa:2048 -nodes -keyout server-key.pem -out server-csr.pem -subj \"/CN=localhost\"; openssl x509 -req -in server-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365; openssl req -newkey rsa:2048 -nodes -keyout client-key.pem -out client-csr.pem -subj \"/CN=client\"; openssl x509 -req -in client-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365; curl --cacert ca.pem --cert client-cert.pem --key client-key.pem https://localhost:<port>/...")).addOption(new Option("--mtls-cert <path>", "PEM-encoded server certificate for mutual TLS. Self-signed is fine for local dev. Must be set together with --mtls-truststore + --mtls-key.")).addOption(new Option("--mtls-key <path>", "PEM-encoded server private key matching --mtls-cert. Must be set together with --mtls-truststore + --mtls-cert.")).addOption(new Option("--allow-unverified-sigv4", "Opt-in: allow AWS_IAM SigV4 requests that cannot be cryptographically verified (foreign access-key-id, OR no local AWS credentials configured) to pass through with a placeholder principalId. DEFAULT off — fail-closed so unauthenticated bypass is impossible against `event.requestContext.identity.accessKey`-trusting handler code. Use only in dev loops where you understand the risk.").default(false)).action(withErrorHandling(localStartApiCommand));
55007
+ const startApi = new Command("start-api").description("Run a long-running local HTTP server that maps API Gateway routes (REST v1, HTTP API, Function URL) to Lambda invocations against the AWS Lambda Runtime Interface Emulator (Docker required). Supports Lambda TOKEN/REQUEST authorizers, Cognito User Pool / HTTP v2 JWT authorizers, and REST v1 AWS_IAM (SigV4 signature verification only — IAM policy evaluation is NOT emulated; see docs/local-emulation.md). When JWKS is unreachable, JWT authorizers fall back to pass-through (every token accepted) with a warn line — local dev fallback. VPC-config Lambdas run locally and surface a warn line at startup; their containers do NOT get attached to the deployed VPC subnets, so calls to private RDS / ElastiCache will fail.").argument("[target]", "Optional API filter. Accepts the bare CDK logical id ('MyHttpApi'; single-stack apps only), stack-qualified logical id ('MyStack:MyHttpApi'), full CDK Construct path ('MyStack/MyHttpApi/Resource'), or an ancestor Construct path that prefix-matches ('MyStack/MyHttpApi'). When omitted, every discovered API gets its own server. Mirrors `cdkd local invoke` / `cdkd local run-task` target syntax.").addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--stack <name>", "Stack to start (single-stack apps auto-detect)")).addOption(new Option("--warm", "Pre-start one container per Lambda at server boot").default(false)).addOption(new Option("--per-lambda-concurrency <n>", "Pool size cap per Lambda (default 2, max 4)").default("2")).addOption(new Option("--no-pull", "Skip docker pull (cached image)")).addOption(new Option("--container-host <host>", "IP the host uses to bind/probe the RIE port (must be a numeric IP — `docker run -p <ip>:<port>:8080` rejects hostnames). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--debug-port-base <port>", "Reserve a contiguous --debug-port range (one per Lambda)")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--assume-role <arn-or-pair>", "Assume the Lambda's execution role and forward STS-issued temp creds. Bare <arn> = global default; <LogicalId>=<arn> = per-Lambda override (repeatable). Per-Lambda > global > unset (developer creds passed through).").argParser((raw, prev) => parseAssumeRoleToken(raw, prev))).addOption(new Option("--watch", "Hot-reload: re-synth + re-discover routes when cdk.out/ or asset directories change. Off by default; the server keeps the previous version serving when synth fails mid-reload.").default(false)).addOption(new Option("--stage <name>", "Select an API Gateway Stage by its 'StageName'. Default: the first Stage attached to each API. Drives event.stageVariables for both REST v1 and HTTP API v2. NOTE: For HTTP API v2 routes, requestContext.stage is always '$default' regardless of this flag (AWS-side limitation — HTTP API only exposes one stage to the integration event); only event.stageVariables is affected for v2 routes. For REST v1 routes the selected StageName is also threaded into requestContext.stage.")).addOption(new Option("--api <id>", "DEPRECATED — use the positional <target> argument instead. Same accepted forms (bare logical id, stack-qualified, Construct path, ancestor prefix). Will be removed in a future major release.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--from-state", "Read cdkd S3 state for every routed stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::Join (and AWS pseudo parameters) in Lambda env vars with the deployed physical IDs / attributes. Off by default — pre-PR warn-and-drop semantics are preserved. Turn on for stacks already deployed via cdkd deploy. Mirrors `cdkd local invoke --from-state` / `cdkd local run-task --from-state`. Re-runs against fresh state on every hot-reload firing (--watch).").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in Lambda env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name per routed stack; pass an explicit value when a single CFn stack should serve every routed stack. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).addOption(new Option("--mtls-truststore <path>", "PEM-encoded CA bundle for client-certificate verification (mutual TLS). When set, the local server switches from HTTP to HTTPS and the TLS handshake rejects clients whose certificate doesn't chain to one of these CAs. Verified certs are surfaced on the Lambda event under requestContext.identity.clientCert (REST v1) / requestContext.authentication.clientCert (HTTP API v2). Must be set together with --mtls-cert + --mtls-key; partial flag sets are rejected. Generate a CA + server + client cert for local dev: openssl req -x509 -newkey rsa:2048 -nodes -keyout ca-key.pem -out ca.pem -subj \"/CN=cdkd-local-ca\" -days 365; openssl req -newkey rsa:2048 -nodes -keyout server-key.pem -out server-csr.pem -subj \"/CN=localhost\"; openssl x509 -req -in server-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365; openssl req -newkey rsa:2048 -nodes -keyout client-key.pem -out client-csr.pem -subj \"/CN=client\"; openssl x509 -req -in client-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365; curl --cacert ca.pem --cert client-cert.pem --key client-key.pem https://localhost:<port>/...")).addOption(new Option("--mtls-cert <path>", "PEM-encoded server certificate for mutual TLS. Self-signed is fine for local dev. Must be set together with --mtls-truststore + --mtls-key.")).addOption(new Option("--mtls-key <path>", "PEM-encoded server private key matching --mtls-cert. Must be set together with --mtls-truststore + --mtls-cert.")).addOption(new Option("--allow-unverified-sigv4", "Opt-in: allow AWS_IAM SigV4 requests that cannot be cryptographically verified (foreign access-key-id, OR no local AWS credentials configured) to pass through with a placeholder principalId. DEFAULT off — fail-closed so unauthenticated bypass is impossible against `event.requestContext.identity.accessKey`-trusting handler code. Use only in dev loops where you understand the risk.").default(false)).action(withErrorHandling(localStartApiCommand));
54242
55008
  [
54243
55009
  ...commonOptions,
54244
55010
  ...appOptions,
@@ -55132,6 +55898,7 @@ async function localRunTaskCommand(target, options) {
55132
55898
  const state = createEcsRunState();
55133
55899
  let sigintHandler;
55134
55900
  let sigintCount = 0;
55901
+ let stateProvider;
55135
55902
  let cleanupPromise;
55136
55903
  const cleanup = async () => {
55137
55904
  if (!cleanupPromise) cleanupPromise = (async () => {
@@ -55164,34 +55931,22 @@ async function localRunTaskCommand(target, options) {
55164
55931
  ...options.profile && { macroExpandS3ClientOpts: { profile: options.profile } }
55165
55932
  };
55166
55933
  const { stacks } = await synthesizer.synthesize(synthOpts);
55167
- const imageContext = await buildEcsImageResolutionContext$1(target, stacks, options);
55934
+ const candidate = pickCandidateStack$1(parseEcsTarget(target).stackPattern, stacks);
55935
+ stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", candidate?.region);
55936
+ const imageContext = await buildEcsImageResolutionContext$1(candidate, stateProvider, options);
55168
55937
  const task = resolveEcsTaskTarget(target, stacks, imageContext);
55169
55938
  logger.info(`Target: ${task.stack.stackName}/${task.taskDefinitionLogicalId} (family=${task.family}, containers=${task.containers.length})`);
55170
55939
  const taskNeeds = detectEcsImageResolutionNeeds(stacks.find((s) => s.stackName === task.stack.stackName) ?? task.stack);
55171
- let taskCrossStackDispose;
55172
- if (options.fromState && taskNeeds.needsCrossStackResolver) {
55940
+ if (stateProvider && taskNeeds.needsCrossStackResolver) {
55173
55941
  const consumerRegion = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? task.stack.region ?? "us-east-1";
55174
- const built = await buildCrossStackResolver(consumerRegion, {
55175
- ...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
55176
- statePrefix: options.statePrefix,
55177
- ...options.region !== void 0 && { region: options.region },
55178
- ...options.profile !== void 0 && { profile: options.profile }
55942
+ const resolver = await stateProvider.buildCrossStackResolver(consumerRegion);
55943
+ if (resolver) await applyCrossStackResolverToTask(task, {
55944
+ resources: imageContext?.stateResources ?? {},
55945
+ ...imageContext?.pseudoParameters && { pseudoParameters: imageContext.pseudoParameters },
55946
+ consumerRegion,
55947
+ crossStackResolver: resolver
55179
55948
  });
55180
- if (built) {
55181
- taskCrossStackDispose = built.dispose;
55182
- try {
55183
- await applyCrossStackResolverToTask(task, {
55184
- resources: imageContext?.stateResources ?? {},
55185
- ...imageContext?.pseudoParameters && { pseudoParameters: imageContext.pseudoParameters },
55186
- consumerRegion,
55187
- crossStackResolver: built.resolver
55188
- });
55189
- } finally {
55190
- taskCrossStackDispose();
55191
- taskCrossStackDispose = void 0;
55192
- }
55193
- }
55194
- } else if (!options.fromState && taskNeeds.needsCrossStackResolver) logger.warn("Container Environment / Secrets entries contain Fn::ImportValue / Fn::GetStackOutput intrinsics. Pass --from-state to substitute them against deployed cdkd state.");
55949
+ } else if (!stateProvider && taskNeeds.needsCrossStackResolver) logger.warn("Container Environment / Secrets entries contain Fn::ImportValue / Fn::GetStackOutput intrinsics. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute them against deployed state.");
55195
55950
  sigintHandler = () => {
55196
55951
  sigintCount += 1;
55197
55952
  if (sigintCount >= 2) {
@@ -55237,6 +55992,7 @@ async function localRunTaskCommand(target, options) {
55237
55992
  if (result.exitCode !== 0) process.exitCode = result.exitCode;
55238
55993
  } finally {
55239
55994
  if (sigintHandler) process.off("SIGINT", sigintHandler);
55995
+ if (stateProvider) stateProvider.dispose();
55240
55996
  if (!options.detach) await cleanup();
55241
55997
  }
55242
55998
  }
@@ -55289,19 +56045,18 @@ async function assumeTaskRole$1(roleArn, region) {
55289
56045
  *
55290
56046
  * Tier 1 (pseudo parameters) fires `sts:GetCallerIdentity` once for
55291
56047
  * `${AWS::AccountId}`; region / partition / URL suffix come from the CLI
55292
- * (`--region` → env vars → synth-derived stack region). Tier 2
55293
- * (`--from-state`) reuses the shared state-loader to pull cdkd's S3 state
55294
- * for the candidate stack same warn-and-fall-back error policy as
55295
- * `cdkd local invoke --from-state`.
56048
+ * (`--region` → env vars → synth-derived stack region). Tier 2 (state
56049
+ * load) routes through the active {@link LocalStateProvider} so both
56050
+ * `--from-state` and `--from-cfn-stack` produce the same downstream
56051
+ * context shape (issue #606).
55296
56052
  */
55297
- async function buildEcsImageResolutionContext$1(target, stacks, options) {
56053
+ async function buildEcsImageResolutionContext$1(candidate, stateProvider, options) {
55298
56054
  const logger = getLogger();
55299
- const candidate = pickCandidateStack$1(parseEcsTarget(target).stackPattern, stacks);
55300
56055
  if (!candidate) return void 0;
55301
56056
  const needs = detectEcsImageResolutionNeeds(candidate);
55302
56057
  if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution) return;
55303
56058
  const ctx = {};
55304
- const wantsPseudoForEnvOrSecret = options.fromState && needs.needsEnvOrSecretSubstitution;
56059
+ const wantsPseudoForEnvOrSecret = !!stateProvider && needs.needsEnvOrSecretSubstitution;
55305
56060
  if (needs.needsPseudoParameters || wantsPseudoForEnvOrSecret) {
55306
56061
  const region = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? candidate.region;
55307
56062
  if (!region) logger.warn("Resolver references ${AWS::Region} but cdkd could not determine the target region. Pass --region, set AWS_REGION, or declare env.region on the CDK stack.");
@@ -55322,17 +56077,11 @@ async function buildEcsImageResolutionContext$1(target, stacks, options) {
55322
56077
  };
55323
56078
  }
55324
56079
  const wantsState = needs.needsStateResources || needs.needsEnvOrSecretSubstitution;
55325
- if (options.fromState && wantsState) {
55326
- const loaded = await loadStateForStack(candidate.stackName, candidate.region, {
55327
- ...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
55328
- ...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
55329
- statePrefix: options.statePrefix,
55330
- ...options.region !== void 0 && { region: options.region },
55331
- ...options.profile !== void 0 && { profile: options.profile }
55332
- });
55333
- if (loaded) ctx.stateResources = loaded.state.resources;
55334
- } else if (!options.fromState && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass --from-state to substitute the deployed repository URI (requires the stack to have been deployed via cdkd deploy). Otherwise the resolver will surface its existing error.");
55335
- else if (!options.fromState && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics (Ref / Fn::GetAtt / Fn::Sub / Fn::Join). Pass --from-state to substitute them against the deployed cdkd state. Without --from-state these entries are dropped (per-key warnings will follow).");
56080
+ if (stateProvider && wantsState) {
56081
+ const loaded = await stateProvider.load(candidate.stackName, candidate.region);
56082
+ if (loaded) ctx.stateResources = loaded.resources;
56083
+ } else if (!stateProvider && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute the deployed repository URI. Otherwise the resolver will surface its existing error.");
56084
+ else if (!stateProvider && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics (Ref / Fn::GetAtt / Fn::Sub / Fn::Join). Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute them against deployed state. Without a state source these entries are dropped (per-key warnings will follow).");
55336
56085
  return ctx;
55337
56086
  }
55338
56087
  function pickCandidateStack$1(stackPattern, stacks) {
@@ -55375,7 +56124,7 @@ function readEnvOverridesFile$2(filePath) {
55375
56124
  return parsed;
55376
56125
  }
55377
56126
  function createLocalRunTaskCommand() {
55378
- const cmd = new Command("run-task").description("Run an AWS::ECS::TaskDefinition locally — pulls/builds images, sets up a per-task docker network with the AWS-published metadata-endpoints sidecar, and starts every container in dependsOn order. Target accepts a CDK display path (MyStack/MyService/TaskDef) or stack-qualified logical ID (MyStack:MyServiceTaskDefXYZ1234). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the AWS::ECS::TaskDefinition to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed function role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--keep-running", "Don't docker rm -f the user containers on task exit (network + sidecar are still torn down). Use when you want to docker exec into a stopped container for post-mortems.").default(false)).addOption(new Option("--detach", "Start the containers in the background and exit (skip log streaming + auto teardown). Useful in CI smoke tests; caller manages container lifecycle.").default(false)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt references to same-stack AWS::ECR::Repository resources with the deployed URI. Off by default — only the AWS pseudo-parameter tier (${AWS::AccountId} / ${AWS::Region}) is resolved without this flag.").default(false)).addOption(new Option("--stack-region <region>", "Region of the cdkd state record to read (used with --from-state when the same stack name has state in multiple regions).")).action(withErrorHandling(localRunTaskCommand));
56127
+ const cmd = new Command("run-task").description("Run an AWS::ECS::TaskDefinition locally — pulls/builds images, sets up a per-task docker network with the AWS-published metadata-endpoints sidecar, and starts every container in dependsOn order. Target accepts a CDK display path (MyStack/MyService/TaskDef) or stack-qualified logical ID (MyStack:MyServiceTaskDefXYZ1234). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the AWS::ECS::TaskDefinition to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed function role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--keep-running", "Don't docker rm -f the user containers on task exit (network + sidecar are still torn down). Use when you want to docker exec into a stopped container for post-mortems.").default(false)).addOption(new Option("--detach", "Start the containers in the background and exit (skip log streaming + auto teardown). Useful in CI smoke tests; caller manages container lifecycle.").default(false)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt references to same-stack AWS::ECR::Repository resources with the deployed URI. Off by default — only the AWS pseudo-parameter tier (${AWS::AccountId} / ${AWS::Region}) is resolved without this flag.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in container env vars / secrets / image URIs with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name; pass an explicit value when the CFn stack name differs. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).action(withErrorHandling(localRunTaskCommand));
55379
56128
  [
55380
56129
  ...commonOptions,
55381
56130
  ...appOptions,
@@ -56451,6 +57200,7 @@ async function localStartServiceCommand(targets, options) {
56451
57200
  warnIfDeprecatedRegion(options);
56452
57201
  const skipPull = options.pull === false;
56453
57202
  if (!targets || targets.length === 0) throw new LocalStartServiceError("cdkd local start-service requires at least one <target>. Pass one or more service paths like 'Stack/Orders' 'Stack/Frontend'.");
57203
+ rejectExplicitCfnStackWithMultipleStacks(options, targets.length);
56454
57204
  const perTarget = targets.map((t) => ({
56455
57205
  target: t,
56456
57206
  runState: createServiceRunState()
@@ -56547,34 +57297,36 @@ async function localStartServiceCommand(targets, options) {
56547
57297
  * outer code to wait + tear down.
56548
57298
  */
56549
57299
  async function bootOneTarget(target, runState, stacks, options, discovery, skipPull) {
57300
+ const candidate = pickCandidateStack(parseEcsTarget(target).stackPattern, stacks);
57301
+ const stateProvider = createLocalStateProvider(options, candidate?.stackName ?? "", candidate?.region);
57302
+ try {
57303
+ return await runOneTarget(target, runState, stacks, options, discovery, skipPull, stateProvider);
57304
+ } finally {
57305
+ if (stateProvider) stateProvider.dispose();
57306
+ }
57307
+ }
57308
+ async function runOneTarget(target, runState, stacks, options, discovery, skipPull, stateProvider) {
56550
57309
  const logger = getLogger();
56551
- const imageContext = await buildEcsImageResolutionContext(target, stacks, options);
57310
+ const imageContext = await buildEcsImageResolutionContext(target, stacks, options, stateProvider);
56552
57311
  const service = resolveEcsServiceTarget(target, stacks, imageContext);
56553
57312
  logger.info(`Target: ${service.stack.stackName}/${service.serviceLogicalId} (service=${service.serviceName}, desiredCount=${service.desiredCount}, task=${service.task.taskDefinitionLogicalId})`);
56554
57313
  for (const w of service.warnings) logger.warn(w);
56555
57314
  if (service.serviceConnect) logger.info(`Service Connect: namespace='${service.serviceConnect.namespaceName}', ${service.serviceConnect.services.length} service(s) registered for peer discovery.`);
56556
57315
  if (service.serviceRegistries.length > 0) logger.info(`Cloud Map: ${service.serviceRegistries.length} ServiceRegistry binding(s).`);
56557
57316
  const taskNeeds = detectEcsImageResolutionNeeds(stacks.find((s) => s.stackName === service.stack.stackName) ?? service.stack);
56558
- if (options.fromState && taskNeeds.needsCrossStackResolver) {
57317
+ if (stateProvider && taskNeeds.needsCrossStackResolver) {
56559
57318
  const consumerRegion = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? service.stack.region ?? "us-east-1";
56560
- const built = await buildCrossStackResolver(consumerRegion, {
56561
- ...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
56562
- statePrefix: options.statePrefix,
56563
- ...options.region !== void 0 && { region: options.region },
56564
- ...options.profile !== void 0 && { profile: options.profile }
56565
- });
56566
- if (built) try {
57319
+ const resolver = await stateProvider.buildCrossStackResolver(consumerRegion);
57320
+ if (resolver) {
56567
57321
  const subContext = {
56568
57322
  resources: imageContext?.stateResources ?? {},
56569
57323
  ...imageContext?.pseudoParameters && { pseudoParameters: imageContext.pseudoParameters },
56570
57324
  consumerRegion,
56571
- crossStackResolver: built.resolver
57325
+ crossStackResolver: resolver
56572
57326
  };
56573
57327
  await applyCrossStackResolverToTask(service.task, subContext);
56574
- } finally {
56575
- built.dispose();
56576
57328
  }
56577
- } else if (!options.fromState && taskNeeds.needsCrossStackResolver) logger.warn("Container Environment / Secrets entries contain Fn::ImportValue / Fn::GetStackOutput intrinsics. Pass --from-state to substitute them against deployed cdkd state.");
57329
+ } else if (!stateProvider && taskNeeds.needsCrossStackResolver) logger.warn("Container Environment / Secrets entries contain Fn::ImportValue / Fn::GetStackOutput intrinsics. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute them against deployed state.");
56578
57330
  let assumedCredentials;
56579
57331
  let resolvedRoleArn;
56580
57332
  if (options.assumeTaskRole === true) {
@@ -56643,14 +57395,14 @@ async function assumeTaskRole(roleArn, region) {
56643
57395
  * the candidate stack picker differs because services and tasks share
56644
57396
  * the same stack-pattern grammar.
56645
57397
  */
56646
- async function buildEcsImageResolutionContext(target, stacks, options) {
57398
+ async function buildEcsImageResolutionContext(target, stacks, options, stateProvider) {
56647
57399
  const logger = getLogger();
56648
57400
  const candidate = pickCandidateStack(parseEcsTarget(target).stackPattern, stacks);
56649
57401
  if (!candidate) return void 0;
56650
57402
  const needs = detectEcsImageResolutionNeeds(candidate);
56651
57403
  if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution) return;
56652
57404
  const ctx = {};
56653
- const wantsPseudoForEnvOrSecret = options.fromState && needs.needsEnvOrSecretSubstitution;
57405
+ const wantsPseudoForEnvOrSecret = !!stateProvider && needs.needsEnvOrSecretSubstitution;
56654
57406
  if (needs.needsPseudoParameters || wantsPseudoForEnvOrSecret) {
56655
57407
  const region = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? candidate.region;
56656
57408
  if (!region) logger.warn("Resolver references ${AWS::Region} but cdkd could not determine the target region. Pass --region, set AWS_REGION, or declare env.region on the CDK stack.");
@@ -56671,17 +57423,11 @@ async function buildEcsImageResolutionContext(target, stacks, options) {
56671
57423
  };
56672
57424
  }
56673
57425
  const wantsState = needs.needsStateResources || needs.needsEnvOrSecretSubstitution;
56674
- if (options.fromState && wantsState) {
56675
- const loaded = await loadStateForStack(candidate.stackName, candidate.region, {
56676
- ...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
56677
- ...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
56678
- statePrefix: options.statePrefix,
56679
- ...options.region !== void 0 && { region: options.region },
56680
- ...options.profile !== void 0 && { profile: options.profile }
56681
- });
56682
- if (loaded) ctx.stateResources = loaded.state.resources;
56683
- } else if (!options.fromState && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass --from-state to substitute the deployed repository URI.");
56684
- else if (!options.fromState && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics. Pass --from-state to substitute them against the deployed cdkd state.");
57426
+ if (stateProvider && wantsState) {
57427
+ const loaded = await stateProvider.load(candidate.stackName, candidate.region);
57428
+ if (loaded) ctx.stateResources = loaded.resources;
57429
+ } else if (!stateProvider && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute the deployed repository URI.");
57430
+ else if (!stateProvider && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute them against the deployed cdkd state.");
56685
57431
  return ctx;
56686
57432
  }
56687
57433
  function pickCandidateStack(stackPattern, stacks) {
@@ -56752,7 +57498,7 @@ function parseRestartPolicy(raw) {
56752
57498
  throw new LocalStartServiceError(`--restart-policy must be one of 'on-failure', 'always', or 'none' (got '${raw}').`);
56753
57499
  }
56754
57500
  function createLocalStartServiceCommand() {
56755
- const cmd = new Command("start-service").description("Run one or more AWS::ECS::Service resources locally as a long-running emulator. Spins up DesiredCount task replicas per service (clamped by --max-tasks) using the same per-task docker network + metadata sidecar pattern as `cdkd local run-task`, then keeps each replica running and restarts it on exit per --restart-policy. ^C tears every replica + sidecar + network down. Each <target> accepts a CDK display path (MyStack/MyService) or stack-qualified logical ID (MyStack:MyServiceXYZ); single-stack apps may omit the stack prefix. When two or more <target>s are supplied, every service is booted into a shared Cloud Map / Service Connect registry so peer services discover each other via docker --add-host overlay (Issue #460).").argument("<targets...>", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ECS::Service resources to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed task role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--max-tasks <n>", `Hard cap on local replica count. Caps the template DesiredCount so local dev machines don't run an unbounded number of containers. Cannot exceed ${83} due to the per-replica link-local /24 subnet allocator's range.`).default(3).argParser(parseMaxTasks)).addOption(new Option("--restart-policy <policy>", "How to react when an essential container exits. 'on-failure' (default) restarts only on non-zero exit; 'always' restarts on every exit; 'none' shuts the replica down and runs the service degraded.").default("on-failure").argParser(parseRestartPolicy)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt / Fn::ImportValue / Fn::GetStackOutput intrinsics in container images, environment variables, secrets, role ARNs, and volumes.").default(false)).addOption(new Option("--stack-region <region>", "Region of the cdkd state record to read (used with --from-state when the same stack name has state in multiple regions).")).action(withErrorHandling(localStartServiceCommand));
57501
+ const cmd = new Command("start-service").description("Run one or more AWS::ECS::Service resources locally as a long-running emulator. Spins up DesiredCount task replicas per service (clamped by --max-tasks) using the same per-task docker network + metadata sidecar pattern as `cdkd local run-task`, then keeps each replica running and restarts it on exit per --restart-policy. ^C tears every replica + sidecar + network down. Each <target> accepts a CDK display path (MyStack/MyService) or stack-qualified logical ID (MyStack:MyServiceXYZ); single-stack apps may omit the stack prefix. When two or more <target>s are supplied, every service is booted into a shared Cloud Map / Service Connect registry so peer services discover each other via docker --add-host overlay (Issue #460).").argument("<targets...>", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ECS::Service resources to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed task role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--max-tasks <n>", `Hard cap on local replica count. Caps the template DesiredCount so local dev machines don't run an unbounded number of containers. Cannot exceed ${83} due to the per-replica link-local /24 subnet allocator's range.`).default(3).argParser(parseMaxTasks)).addOption(new Option("--restart-policy <policy>", "How to react when an essential container exits. 'on-failure' (default) restarts only on non-zero exit; 'always' restarts on every exit; 'none' shuts the replica down and runs the service degraded.").default("on-failure").argParser(parseRestartPolicy)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt / Fn::ImportValue / Fn::GetStackOutput intrinsics in container images, environment variables, secrets, role ARNs, and volumes.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in container env vars / secrets / image URIs with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name; pass an explicit value when the CFn stack name differs. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).action(withErrorHandling(localStartServiceCommand));
56756
57502
  [
56757
57503
  ...commonOptions,
56758
57504
  ...appOptions,
@@ -56866,19 +57612,19 @@ async function localInvokeCommand(target, options) {
56866
57612
  let stateAudit;
56867
57613
  let templateEnv = getTemplateEnv(lambda.resource);
56868
57614
  let stateForRoleHint;
56869
- let crossStackDispose;
56870
- if (options.fromState) {
56871
- const loaded = await loadStateForStack(lambda.stack.stackName, lambda.stack.region, {
56872
- ...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
56873
- ...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
56874
- statePrefix: options.statePrefix,
56875
- ...options.region !== void 0 && { region: options.region },
56876
- ...options.profile !== void 0 && { profile: options.profile }
56877
- });
57615
+ const stateProvider = createLocalStateProvider(options, lambda.stack.stackName, lambda.stack.region);
57616
+ if (stateProvider) try {
57617
+ const loaded = await stateProvider.load(lambda.stack.stackName, lambda.stack.region);
56878
57618
  if (loaded) {
56879
- stateForRoleHint = loaded.state;
57619
+ stateForRoleHint = {
57620
+ version: 1,
57621
+ stackName: lambda.stack.stackName,
57622
+ resources: loaded.resources,
57623
+ outputs: loaded.outputs,
57624
+ lastModified: 0
57625
+ };
56880
57626
  const subContext = {
56881
- resources: loaded.state.resources,
57627
+ resources: loaded.resources,
56882
57628
  consumerRegion: loaded.region
56883
57629
  };
56884
57630
  if (envHasIntrinsicValue(templateEnv)) {
@@ -56886,36 +57632,24 @@ async function localInvokeCommand(target, options) {
56886
57632
  if (pseudo) subContext.pseudoParameters = pseudo;
56887
57633
  }
56888
57634
  if (envHasCrossStackIntrinsic(templateEnv)) {
56889
- const built = await buildCrossStackResolver(loaded.region, {
56890
- ...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
56891
- statePrefix: options.statePrefix,
56892
- ...options.region !== void 0 && { region: options.region },
56893
- ...options.profile !== void 0 && { profile: options.profile }
56894
- });
56895
- if (built) {
56896
- subContext.crossStackResolver = built.resolver;
56897
- crossStackDispose = built.dispose;
56898
- }
56899
- }
56900
- try {
56901
- const { env, audit } = await substituteEnvVarsFromStateAsync(templateEnv, subContext);
56902
- templateEnv = env;
56903
- stateAudit = audit;
56904
- for (const key of audit.resolvedKeys) logger.debug(`--from-state: substituted env var ${key} from cdkd state`);
56905
- for (const { key, reason } of audit.unresolved) logger.warn(`--from-state: could not substitute env var ${key} (${reason}). Override it via --env-vars or it will be dropped.`);
56906
- } finally {
56907
- if (crossStackDispose) {
56908
- crossStackDispose();
56909
- crossStackDispose = void 0;
56910
- }
57635
+ const resolver = await stateProvider.buildCrossStackResolver(loaded.region);
57636
+ if (resolver) subContext.crossStackResolver = resolver;
56911
57637
  }
57638
+ const { env, audit } = await substituteEnvVarsFromStateAsync(templateEnv, subContext);
57639
+ templateEnv = env;
57640
+ stateAudit = audit;
57641
+ const label = stateProvider.label;
57642
+ for (const key of audit.resolvedKeys) logger.debug(`${label}: substituted env var ${key}`);
57643
+ for (const { key, reason } of audit.unresolved) logger.warn(`${label}: could not substitute env var ${key} (${reason}). Override it via --env-vars or it will be dropped.`);
56912
57644
  }
57645
+ } finally {
57646
+ stateProvider.dispose();
56913
57647
  }
56914
57648
  const overrides = readEnvOverridesFile(options.envVars);
56915
57649
  const envResult = resolveEnvVars(lambda.logicalId, templateEnv, overrides);
56916
57650
  for (const key of envResult.unresolved) {
56917
57651
  if (stateAudit && stateAudit.unresolved.some((u) => u.key === key)) continue;
56918
- logger.warn(`Environment variable ${key} contains a CloudFormation intrinsic and was dropped. Override it with --env-vars (e.g. {"${lambda.logicalId}":{"${key}":"<literal>"}}) or pass --from-state to recover deployed values.`);
57652
+ logger.warn(`Environment variable ${key} contains a CloudFormation intrinsic and was dropped. Override it with --env-vars (e.g. {"${lambda.logicalId}":{"${key}":"<literal>"}}), or pass --from-state (cdkd-deployed) / --from-cfn-stack (cdk-deployed) to recover deployed values.`);
56919
57653
  }
56920
57654
  let resolvedAssumeRoleArn;
56921
57655
  if (typeof options.assumeRole === "string") resolvedAssumeRoleArn = options.assumeRole;
@@ -57485,7 +58219,7 @@ function pickReferencedLogicalId(intrinsic) {
57485
58219
  */
57486
58220
  function createLocalCommand() {
57487
58221
  const local = new Command("local").description("Local execution of Lambda functions (RIE) and ECS task definitions (Docker required)");
57488
- const invoke = new Command("invoke").description("Run a Lambda function locally in a Docker container (RIE-backed). Target accepts a CDK display path (MyStack/MyApi/Handler) or stack-qualified logical ID (MyStack:MyApiHandler1234ABCD). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the Lambda to invoke").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for IMAGE local-build path; `docker build` does not pull base layers by default")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host to bind the RIE port to").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions (closes the \"developer admin / function narrow\" skew). Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) auto-resolves the function's execution role ARN from cdkd state (requires --from-state); (3) `--no-assume-role` explicitly opts out (forces dev creds even with --from-state). Off by default — when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub in env vars with the deployed physical IDs / attributes. Off by default — keep PR 1 warn-and-drop semantics; turn on for stacks already deployed via cdkd deploy.").default(false)).addOption(new Option("--stack-region <region>", "Region of the cdkd state record to read (used with --from-state when the same stack name has state in multiple regions).")).action(withErrorHandling(localInvokeCommand));
58222
+ const invoke = new Command("invoke").description("Run a Lambda function locally in a Docker container (RIE-backed). Target accepts a CDK display path (MyStack/MyApi/Handler) or stack-qualified logical ID (MyStack:MyApiHandler1234ABCD). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the Lambda to invoke").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for IMAGE local-build path; `docker build` does not pull base layers by default")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host to bind the RIE port to").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions (closes the \"developer admin / function narrow\" skew). Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) auto-resolves the function's execution role ARN from cdkd state (requires --from-state); (3) `--no-assume-role` explicitly opts out (forces dev creds even with --from-state). Off by default — when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub in env vars with the deployed physical IDs / attributes. Off by default — keep PR 1 warn-and-drop semantics; turn on for stacks already deployed via cdkd deploy.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name; pass an explicit value when CFn stack name differs. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).action(withErrorHandling(localInvokeCommand));
57489
58223
  [
57490
58224
  ...commonOptions,
57491
58225
  ...appOptions,
@@ -58609,7 +59343,7 @@ function reorderArgs(argv) {
58609
59343
  */
58610
59344
  async function main() {
58611
59345
  const program = new Command();
58612
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.156.0");
59346
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.158.0");
58613
59347
  program.addCommand(createBootstrapCommand());
58614
59348
  program.addCommand(createSynthCommand());
58615
59349
  program.addCommand(createListCommand());