@go-to-k/cdkd 0.102.7 → 0.103.1

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,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-CuHRHcyW.js";
3
- import { A as resolveApp, B as CdkdError, C as AssetPublisher, D as Synthesizer, E as buildDockerImage, F as warnDeprecatedNoPrefixCliFlag, G as PartialFailureError, I as AssemblyReader, J as ResourceUpdateNotSupportedError, K as ProvisioningError, M as resolveSkipPrefix, N as resolveStateBucketWithDefault, O as getDefaultStateBucketName, P as resolveStateBucketWithDefaultAndSource, R as resolveBucketRegion, S as shouldRetainResource, T as WorkGraph, U as LocalInvokeBuildError, X as StackHasActiveImportsError, Y as RouteDiscoveryError, Z as StackTerminationProtectionError, _ as DiffCalculator, a as withRetry, at as getLogger, b as LockManager, c as collectInlinePolicyNamesManagedBySiblings, ct as getLiveRenderer, d as normalizeAwsTagsToCfn, dt as generateResourceName, f as resolveExplicitPhysicalId, ft as generateResourceNameWithFallback, g as IntrinsicFunctionResolver, h as assertRegionMatch, i as withResourceDeadline, j as resolveCaptureObservedState, k as getLegacyStateBucketName, l as CDK_PATH_TAG, lt as PATTERN_B_NAME_PROPERTIES, m as CloudControlProvider, mt as withStackName, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as normalizeAwsError, o as IMPLICIT_DELETE_DEPENDENCIES, p as ProviderRegistry, pt as withSkipPrefix, q as ResourceTimeoutError, r as DeployEngine, rt as withErrorHandling, s as IAMRoleProvider, st as runStackBuffered, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as matchesCdkPath, ut as PATTERN_B_RESOURCE_TYPES, v as DagBuilder, w as stringifyValue, x as S3StateBackend, y as TemplateParser } from "./deploy-engine-DGKtcKF6.js";
3
+ import { A as resolveApp, B as CdkdError, C as AssetPublisher, D as Synthesizer, E as buildDockerImage, F as warnDeprecatedNoPrefixCliFlag, G as PartialFailureError, I as AssemblyReader, J as ResourceUpdateNotSupportedError, K as ProvisioningError, M as resolveSkipPrefix, N as resolveStateBucketWithDefault, O as getDefaultStateBucketName, P as resolveStateBucketWithDefaultAndSource, R as resolveBucketRegion, S as shouldRetainResource, T as WorkGraph, U as LocalInvokeBuildError, X as StackHasActiveImportsError, Y as RouteDiscoveryError, Z as StackTerminationProtectionError, _ as DiffCalculator, a as withRetry, at as getLogger, b as LockManager, c as collectInlinePolicyNamesManagedBySiblings, ct as getLiveRenderer, d as normalizeAwsTagsToCfn, dt as generateResourceName, f as resolveExplicitPhysicalId, ft as generateResourceNameWithFallback, g as IntrinsicFunctionResolver, h as assertRegionMatch, i as withResourceDeadline, j as resolveCaptureObservedState, k as getLegacyStateBucketName, l as CDK_PATH_TAG, lt as PATTERN_B_NAME_PROPERTIES, m as CloudControlProvider, mt as withStackName, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as normalizeAwsError, o as IMPLICIT_DELETE_DEPENDENCIES, p as ProviderRegistry, pt as withSkipPrefix, q as ResourceTimeoutError, r as DeployEngine, rt as withErrorHandling, s as IAMRoleProvider, st as runStackBuffered, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as matchesCdkPath, ut as PATTERN_B_RESOURCE_TYPES, v as DagBuilder, w as stringifyValue, x as S3StateBackend, y as TemplateParser } from "./deploy-engine-D6nbHjNM.js";
4
4
  import { createHash, createPublicKey, createVerify, randomBytes, randomUUID } from "node:crypto";
5
5
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectCommand, 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";
6
6
  import { AddRoleToInstanceProfileCommand, AddUserToGroupCommand, AttachGroupPolicyCommand, AttachUserPolicyCommand, CreateGroupCommand, CreateInstanceProfileCommand, CreateLoginProfileCommand, CreateUserCommand, DeleteAccessKeyCommand, DeleteGroupCommand, DeleteGroupPolicyCommand, DeleteInstanceProfileCommand, DeleteLoginProfileCommand, DeleteRolePolicyCommand, DeleteUserCommand, DeleteUserPermissionsBoundaryCommand, DeleteUserPolicyCommand, DetachGroupPolicyCommand, DetachUserPolicyCommand, GetGroupCommand, GetGroupPolicyCommand, GetInstanceProfileCommand, GetRolePolicyCommand, GetUserCommand, GetUserPolicyCommand, IAMClient, ListAccessKeysCommand, ListAttachedGroupPoliciesCommand, ListAttachedUserPoliciesCommand, ListGroupPoliciesCommand, ListGroupsForUserCommand, ListInstanceProfilesCommand, ListUserPoliciesCommand, ListUserTagsCommand, ListUsersCommand, NoSuchEntityException, PutGroupPolicyCommand, PutRolePolicyCommand, PutUserPermissionsBoundaryCommand, PutUserPolicyCommand, RemoveRoleFromInstanceProfileCommand, RemoveUserFromGroupCommand, TagUserCommand, UntagUserCommand, UpdateLoginProfileCommand } from "@aws-sdk/client-iam";
@@ -9,7 +9,7 @@ import { CreateTopicCommand, DeleteTopicCommand, GetSubscriptionAttributesComman
9
9
  import { AddPermissionCommand, CreateEventSourceMappingCommand, CreateFunctionCommand, CreateFunctionUrlConfigCommand, DeleteEventSourceMappingCommand, DeleteFunctionCommand, DeleteFunctionUrlConfigCommand, DeleteLayerVersionCommand, GetEventSourceMappingCommand, GetFunctionCommand, GetFunctionUrlConfigCommand, GetLayerVersionByArnCommand, GetPolicyCommand, LambdaClient, ListFunctionsCommand, ListLayersCommand, ListTagsCommand, PublishLayerVersionCommand, RemovePermissionCommand, ResourceNotFoundException, TagResourceCommand as TagResourceCommand$1, UntagResourceCommand as UntagResourceCommand$1, UpdateEventSourceMappingCommand, UpdateFunctionCodeCommand, UpdateFunctionConfigurationCommand, UpdateFunctionUrlConfigCommand, waitUntilFunctionUpdatedV2 } from "@aws-sdk/client-lambda";
10
10
  import { AssumeRoleCommand, GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
11
11
  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";
12
- import { CreateTableCommand, DeleteTableCommand, DescribeTableCommand, DynamoDBClient, ListTablesCommand, ListTagsOfResourceCommand, ResourceNotFoundException as ResourceNotFoundException$1, TagResourceCommand as TagResourceCommand$2, UntagResourceCommand as UntagResourceCommand$2, UpdateTableCommand } from "@aws-sdk/client-dynamodb";
12
+ import { CreateTableCommand, DeleteTableCommand, DescribeTableCommand, DynamoDBClient, ListTablesCommand, ListTagsOfResourceCommand, ResourceNotFoundException as ResourceNotFoundException$1, TagResourceCommand as TagResourceCommand$2, UntagResourceCommand as UntagResourceCommand$2, UpdateTableCommand, UpdateTimeToLiveCommand } from "@aws-sdk/client-dynamodb";
13
13
  import { CloudFormationClient, CreateChangeSetCommand, DeleteChangeSetCommand, DeleteStackCommand, DescribeChangeSetCommand, DescribeStackEventsCommand, DescribeStackResourcesCommand, DescribeStacksCommand, DescribeTypeCommand, ExecuteChangeSetCommand, GetTemplateCommand, UpdateStackCommand, waitUntilChangeSetCreateComplete, waitUntilStackDeleteComplete, waitUntilStackImportComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
14
14
  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";
15
15
  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";
@@ -30,14 +30,14 @@ import { CreateAliasCommand, CreateKeyCommand, DeleteAliasCommand, DescribeKeyCo
30
30
  import { promisify } from "node:util";
31
31
  import { CreateRepositoryCommand, DeleteLifecyclePolicyCommand, DeleteRepositoryCommand, DeleteRepositoryPolicyCommand, DescribeRepositoriesCommand, ECRClient, GetAuthorizationTokenCommand, GetLifecyclePolicyCommand, LifecyclePolicyNotFoundException, ListTagsForResourceCommand as ListTagsForResourceCommand$7, PutImageScanningConfigurationCommand, PutImageTagMutabilityCommand, PutLifecyclePolicyCommand, RepositoryNotFoundException, SetRepositoryPolicyCommand, TagResourceCommand as TagResourceCommand$9 } from "@aws-sdk/client-ecr";
32
32
  import graphlib from "graphlib";
33
+ import { AddTagsToResourceCommand as AddTagsToResourceCommand$1, CreateDBClusterCommand, CreateDBInstanceCommand, CreateDBSubnetGroupCommand, DeleteDBClusterCommand, DeleteDBInstanceCommand, DeleteDBSubnetGroupCommand, DescribeDBClustersCommand, DescribeDBInstancesCommand, DescribeDBSubnetGroupsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$8, ModifyDBClusterCommand, ModifyDBInstanceCommand, ModifyDBSubnetGroupCommand, RDSClient, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$1 } from "@aws-sdk/client-rds";
33
34
  import { Command, Option } from "commander";
34
35
  import { writeFileSync as writeFileSync$1 } from "fs";
35
36
  import { join as join$1 } from "path";
36
37
  import * as zlib from "node:zlib";
37
38
  import { ApiGatewayV2Client, CreateApiCommand, CreateAuthorizerCommand as CreateAuthorizerCommand$1, CreateIntegrationCommand, CreateRouteCommand as CreateRouteCommand$1, CreateStageCommand as CreateStageCommand$1, DeleteApiCommand, DeleteAuthorizerCommand as DeleteAuthorizerCommand$1, DeleteIntegrationCommand, DeleteRouteCommand as DeleteRouteCommand$1, DeleteStageCommand as DeleteStageCommand$1, GetApiCommand, GetApisCommand, GetAuthorizerCommand as GetAuthorizerCommand$1, GetIntegrationCommand, GetRouteCommand, GetStageCommand as GetStageCommand$1, NotFoundException as NotFoundException$3, UpdateApiCommand, UpdateAuthorizerCommand as UpdateAuthorizerCommand$1, UpdateIntegrationCommand, UpdateRouteCommand, UpdateStageCommand as UpdateStageCommand$1 } from "@aws-sdk/client-apigatewayv2";
38
- import { CreateStateMachineCommand, DeleteStateMachineCommand, DescribeStateMachineCommand, ListStateMachinesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$8, SFNClient, StateMachineDoesNotExist, TagResourceCommand as TagResourceCommand$10, UntagResourceCommand as UntagResourceCommand$9, UpdateStateMachineCommand } from "@aws-sdk/client-sfn";
39
- import { CreateClusterCommand, CreateServiceCommand, DeleteClusterCommand, DeleteServiceCommand, DeregisterTaskDefinitionCommand, DescribeClustersCommand, DescribeServicesCommand, DescribeTaskDefinitionCommand, ECSClient, ListClustersCommand, ListServicesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$9, PutClusterCapacityProvidersCommand, RegisterTaskDefinitionCommand, TagResourceCommand as TagResourceCommand$11, UntagResourceCommand as UntagResourceCommand$10, UpdateClusterCommand, UpdateServiceCommand } from "@aws-sdk/client-ecs";
40
- import { AddTagsToResourceCommand as AddTagsToResourceCommand$1, CreateDBClusterCommand, CreateDBInstanceCommand, CreateDBSubnetGroupCommand, DeleteDBClusterCommand, DeleteDBInstanceCommand, DeleteDBSubnetGroupCommand, DescribeDBClustersCommand, DescribeDBInstancesCommand, DescribeDBSubnetGroupsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$10, ModifyDBClusterCommand, ModifyDBInstanceCommand, ModifyDBSubnetGroupCommand, RDSClient, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$1 } from "@aws-sdk/client-rds";
39
+ import { CreateStateMachineCommand, DeleteStateMachineCommand, DescribeStateMachineCommand, ListStateMachinesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$9, SFNClient, StateMachineDoesNotExist, TagResourceCommand as TagResourceCommand$10, UntagResourceCommand as UntagResourceCommand$9, UpdateStateMachineCommand } from "@aws-sdk/client-sfn";
40
+ import { CreateClusterCommand, CreateServiceCommand, DeleteClusterCommand, DeleteServiceCommand, DeregisterTaskDefinitionCommand, DescribeClustersCommand, DescribeServicesCommand, DescribeTaskDefinitionCommand, ECSClient, ListClustersCommand, ListServicesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$10, PutClusterCapacityProvidersCommand, RegisterTaskDefinitionCommand, TagResourceCommand as TagResourceCommand$11, UntagResourceCommand as UntagResourceCommand$10, UpdateClusterCommand, UpdateServiceCommand } from "@aws-sdk/client-ecs";
41
41
  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";
42
42
  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";
43
43
  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";
@@ -7839,6 +7839,524 @@ var DynamoDBTableProvider = class {
7839
7839
  }
7840
7840
  };
7841
7841
 
7842
+ //#endregion
7843
+ //#region src/provisioning/providers/dynamodb-globaltable-provider.ts
7844
+ /**
7845
+ * AWS DynamoDB GlobalTable Provider
7846
+ *
7847
+ * Implements resource provisioning for AWS::DynamoDB::GlobalTable using the
7848
+ * standard DynamoDB SDK (2019.11.21 API generation, NOT the legacy
7849
+ * 2017.11.29 endpoints). CDK v2's `dynamodb.TableV2` construct synthesizes
7850
+ * as this type.
7851
+ *
7852
+ * WHY a dedicated SDK provider:
7853
+ * - Pre-PR the type fell through to Cloud Control API which did not pass
7854
+ * a `TableName` field, so AWS auto-generated random names like
7855
+ * `yq2phLewTEUtzr4sy2gYFRU4I-1OGJ0UFLOKOOV` instead of the cdkd
7856
+ * `${stackName}-X<hash>` shape (Issue #383).
7857
+ * - Per memory rule `feedback_dedicated_provider_over_special_case.md`,
7858
+ * the consistent fix is a dedicated SDK Provider rather than adding
7859
+ * the type to `FALLBACK_NAME_RULES`.
7860
+ *
7861
+ * **CRITICAL**: do NOT use the legacy 2017.11.29 endpoints
7862
+ * (`CreateGlobalTableCommand` / `UpdateGlobalTableCommand` /
7863
+ * `DescribeGlobalTableCommand`). The CFn type `AWS::DynamoDB::GlobalTable`
7864
+ * is the 2019.11.21 generation, which uses the regular DynamoDB CRUD API
7865
+ * (`CreateTableCommand` + `UpdateTableCommand` with `ReplicaUpdates`).
7866
+ *
7867
+ * MVP scope:
7868
+ * - `update()` throws `ResourceUpdateNotSupportedError`. In-place GlobalTable
7869
+ * updates (replica add/remove, GSI add/remove, BillingMode flip, throughput
7870
+ * rewrites) are out of scope and a follow-up PR.
7871
+ * - `getDriftUnknownPaths` declares TTL + throughput-settings paths because
7872
+ * cdkd's create/update flows surface them but the read-side reverse
7873
+ * mapping is non-trivial and would fire false drift.
7874
+ * - Per-replica drift (`ContributorInsightsSpecification` /
7875
+ * `PointInTimeRecoverySpecification` / `KinesisStreamSpecification`) is
7876
+ * out of scope for v1.
7877
+ */
7878
+ var DynamoDBGlobalTableProvider = class {
7879
+ dynamoDBClient;
7880
+ logger = getLogger().child("DynamoDBGlobalTableProvider");
7881
+ attributeCache = /* @__PURE__ */ new Map();
7882
+ handledProperties = new Map([["AWS::DynamoDB::GlobalTable", new Set([
7883
+ "TableName",
7884
+ "KeySchema",
7885
+ "AttributeDefinitions",
7886
+ "BillingMode",
7887
+ "StreamSpecification",
7888
+ "GlobalSecondaryIndexes",
7889
+ "LocalSecondaryIndexes",
7890
+ "SSESpecification",
7891
+ "Replicas",
7892
+ "TableClass",
7893
+ "TimeToLiveSpecification",
7894
+ "WriteProvisionedThroughputSettings",
7895
+ "WriteOnDemandThroughputSettings",
7896
+ "DeletionProtectionEnabled",
7897
+ "Tags"
7898
+ ])]]);
7899
+ constructor() {
7900
+ const awsClients = getAwsClients();
7901
+ this.dynamoDBClient = awsClients.dynamoDB;
7902
+ }
7903
+ /**
7904
+ * Create a DynamoDB Global Table (CDK TableV2).
7905
+ *
7906
+ * GlobalTable is built on the regular DynamoDB Table primitive: cdkd issues
7907
+ * `CreateTableCommand` first (which only creates the table in the local
7908
+ * region), waits for `ACTIVE`, then issues one `UpdateTableCommand` per
7909
+ * additional replica region via `ReplicaUpdates: [{ Create: {...} }]`.
7910
+ *
7911
+ * Streams must be enabled with `NEW_AND_OLD_IMAGES` when the table has any
7912
+ * cross-region replicas — AWS rejects the replica-add otherwise. cdkd
7913
+ * auto-enables them with an info log when the template omits it.
7914
+ *
7915
+ * Partial-create cleanup (PR #374-class): if any post-`CreateTableCommand`
7916
+ * wiring (wait ACTIVE → replica adds → TTL → Tags) throws, cdkd issues a
7917
+ * best-effort `DeleteTableCommand` so AWS is not left holding a billing
7918
+ * orphan with no cdkd state record. Cleanup failures escalate to WARN
7919
+ * with the exact `aws dynamodb delete-table --table-name <id>` recovery
7920
+ * command.
7921
+ */
7922
+ async create(logicalId, resourceType, properties) {
7923
+ this.logger.debug(`Creating DynamoDB GlobalTable ${logicalId}`);
7924
+ const tableName = properties["TableName"] || generateResourceName(logicalId, { maxLength: 255 });
7925
+ const keySchema = properties["KeySchema"];
7926
+ const attributeDefinitions = properties["AttributeDefinitions"];
7927
+ if (!keySchema) throw new ProvisioningError(`KeySchema is required for DynamoDB GlobalTable ${logicalId}`, resourceType, logicalId);
7928
+ if (!attributeDefinitions) throw new ProvisioningError(`AttributeDefinitions is required for DynamoDB GlobalTable ${logicalId}`, resourceType, logicalId);
7929
+ const billingMode = properties["BillingMode"] ?? "PAY_PER_REQUEST";
7930
+ const currentRegion = await this.dynamoDBClient.config.region() ?? "";
7931
+ const replicas = properties["Replicas"] ?? [];
7932
+ const createParams = {
7933
+ TableName: tableName,
7934
+ KeySchema: keySchema,
7935
+ AttributeDefinitions: attributeDefinitions,
7936
+ BillingMode: billingMode
7937
+ };
7938
+ if (billingMode === "PROVISIONED") {
7939
+ const writeAutoScaling = properties["WriteProvisionedThroughputSettings"]?.["WriteCapacityAutoScalingSettings"];
7940
+ const writeCapacity = Number(writeAutoScaling?.["MinCapacity"] ?? 5);
7941
+ const readAutoScaling = (replicas.find((r) => r["Region"] === currentRegion)?.["ReadProvisionedThroughputSettings"])?.["ReadCapacityAutoScalingSettings"];
7942
+ createParams.ProvisionedThroughput = {
7943
+ ReadCapacityUnits: Number(readAutoScaling?.["MinCapacity"] ?? 5),
7944
+ WriteCapacityUnits: writeCapacity
7945
+ };
7946
+ }
7947
+ const streamSpecInput = properties["StreamSpecification"];
7948
+ const needsStream = replicas.some((r) => r["Region"] !== currentRegion) || replicas.length > 1;
7949
+ if (streamSpecInput) createParams.StreamSpecification = {
7950
+ StreamEnabled: true,
7951
+ StreamViewType: streamSpecInput["StreamViewType"] ?? "NEW_AND_OLD_IMAGES"
7952
+ };
7953
+ else if (needsStream) {
7954
+ this.logger.info(`Auto-enabling streams (NEW_AND_OLD_IMAGES) on ${logicalId} — required for cross-region replication`);
7955
+ createParams.StreamSpecification = {
7956
+ StreamEnabled: true,
7957
+ StreamViewType: "NEW_AND_OLD_IMAGES"
7958
+ };
7959
+ }
7960
+ if (properties["GlobalSecondaryIndexes"]) createParams.GlobalSecondaryIndexes = properties["GlobalSecondaryIndexes"];
7961
+ if (properties["LocalSecondaryIndexes"]) createParams.LocalSecondaryIndexes = properties["LocalSecondaryIndexes"];
7962
+ if (properties["SSESpecification"]) {
7963
+ const sse = properties["SSESpecification"];
7964
+ const sseInput = { Enabled: sse["SSEEnabled"] !== void 0 ? Boolean(sse["SSEEnabled"]) : true };
7965
+ if (sse["SSEType"]) sseInput.SSEType = sse["SSEType"];
7966
+ createParams.SSESpecification = sseInput;
7967
+ }
7968
+ if (properties["DeletionProtectionEnabled"] !== void 0) createParams.DeletionProtectionEnabled = properties["DeletionProtectionEnabled"];
7969
+ if (properties["TableClass"]) createParams.TableClass = properties["TableClass"];
7970
+ const wodts = properties["WriteOnDemandThroughputSettings"];
7971
+ if (wodts?.["MaxWriteRequestUnits"] !== void 0) createParams.OnDemandThroughput = { MaxWriteRequestUnits: Number(wodts["MaxWriteRequestUnits"]) };
7972
+ if (properties["Tags"]) createParams.Tags = properties["Tags"];
7973
+ try {
7974
+ await this.dynamoDBClient.send(new CreateTableCommand(createParams));
7975
+ this.logger.debug(`CreateTable initiated for ${tableName}, waiting for ACTIVE`);
7976
+ } catch (error) {
7977
+ const cause = error instanceof Error ? error : void 0;
7978
+ throw new ProvisioningError(`Failed to create DynamoDB GlobalTable ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, tableName, cause);
7979
+ }
7980
+ try {
7981
+ const tableInfo = await this.waitForTableActive(tableName);
7982
+ for (const replica of replicas) {
7983
+ const region = replica["Region"];
7984
+ if (!region || region === currentRegion) continue;
7985
+ await this.addReplica(tableName, replica, region);
7986
+ }
7987
+ if (properties["TimeToLiveSpecification"]) {
7988
+ const ttl = properties["TimeToLiveSpecification"];
7989
+ const attributeName = ttl["AttributeName"];
7990
+ const enabled = ttl["Enabled"] !== void 0 ? Boolean(ttl["Enabled"]) : true;
7991
+ if (attributeName) await this.dynamoDBClient.send(new UpdateTimeToLiveCommand({
7992
+ TableName: tableName,
7993
+ TimeToLiveSpecification: {
7994
+ Enabled: enabled,
7995
+ AttributeName: attributeName
7996
+ }
7997
+ }));
7998
+ }
7999
+ this.logger.debug(`Successfully created DynamoDB GlobalTable ${logicalId}: ${tableName}`);
8000
+ return {
8001
+ physicalId: tableName,
8002
+ attributes: {
8003
+ Arn: tableInfo.tableArn,
8004
+ TableId: tableInfo.tableId,
8005
+ StreamArn: tableInfo.streamArn,
8006
+ TableName: tableName
8007
+ }
8008
+ };
8009
+ } catch (wiringError) {
8010
+ this.logger.warn(`Wiring failed after CreateTable for ${tableName}; attempting best-effort cleanup`);
8011
+ try {
8012
+ await this.dynamoDBClient.send(new DeleteTableCommand({ TableName: tableName }));
8013
+ } catch (cleanupErr) {
8014
+ const cleanupMsg = cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr);
8015
+ this.logger.warn(`Partial-create cleanup failed for ${tableName}: ${cleanupMsg}. Run: aws dynamodb delete-table --table-name ${tableName} to remove the orphaned AWS-side table.`);
8016
+ }
8017
+ const cause = wiringError instanceof Error ? wiringError : void 0;
8018
+ throw new ProvisioningError(`Failed to create DynamoDB GlobalTable ${logicalId}: ${wiringError instanceof Error ? wiringError.message : String(wiringError)}`, resourceType, logicalId, tableName, cause);
8019
+ }
8020
+ }
8021
+ /**
8022
+ * Add a single replica region. Issues one `UpdateTableCommand` with
8023
+ * `ReplicaUpdates: [{ Create: { RegionName, ... } }]` and polls
8024
+ * `DescribeTable` until the replica's `ReplicaStatus` flips to ACTIVE.
8025
+ * Capped at 10 minutes per replica (AWS Replica provisioning typically
8026
+ * takes 1–5 min).
8027
+ */
8028
+ async addReplica(tableName, replica, region) {
8029
+ const create = { RegionName: region };
8030
+ if (replica["KMSMasterKeyId"]) create.KMSMasterKeyId = replica["KMSMasterKeyId"];
8031
+ if (replica["GlobalSecondaryIndexes"]) create.GlobalSecondaryIndexes = replica["GlobalSecondaryIndexes"];
8032
+ if (replica["TableClassOverride"]) create.TableClassOverride = replica["TableClassOverride"];
8033
+ const replicaUpdates = [{ Create: create }];
8034
+ await this.dynamoDBClient.send(new UpdateTableCommand({
8035
+ TableName: tableName,
8036
+ ReplicaUpdates: replicaUpdates
8037
+ }));
8038
+ await this.waitForReplicaActive(tableName, region);
8039
+ }
8040
+ /**
8041
+ * Update a DynamoDB Global Table.
8042
+ *
8043
+ * MVP: in-place updates are out of scope — replica add/remove, GSI
8044
+ * add/remove, BillingMode flip, and throughput rewrites each have
8045
+ * distinct UpdateTable shapes and ordering rules. `cdkd drift --revert`
8046
+ * surfaces this as `ResourceUpdateNotSupportedError` (exit code 2);
8047
+ * the user falls back to `cdkd deploy --replace` or destroy + redeploy.
8048
+ */
8049
+ async update(logicalId, _physicalId, resourceType, _properties, _previousProperties) {
8050
+ throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "GlobalTable in-place updates are not yet supported; use 'cdkd deploy --replace' or destroy + redeploy");
8051
+ }
8052
+ /**
8053
+ * Delete a DynamoDB Global Table.
8054
+ *
8055
+ * Order is load-bearing:
8056
+ * 1. Optional `--remove-protection` flip-off (idempotent).
8057
+ * 2. `DescribeTable` → drop every non-local replica via UpdateTable
8058
+ * `ReplicaUpdates: [{ Delete: { RegionName } }]`, one at a time
8059
+ * (AWS rejects multiple replica updates per call). Each delete
8060
+ * polls until the replica disappears from `Replicas[]`.
8061
+ * 3. `DeleteTableCommand` on the local region only.
8062
+ * 4. Wait for `ResourceNotFoundException` on subsequent DescribeTable.
8063
+ *
8064
+ * DELETE idempotency: a `ResourceNotFoundException` is treated as
8065
+ * success ONLY when the client region matches the state region
8066
+ * (`assertRegionMatch`). A mismatched destroy would otherwise silently
8067
+ * strip a still-existing resource from state.
8068
+ */
8069
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
8070
+ this.logger.debug(`Deleting DynamoDB GlobalTable ${logicalId}: ${physicalId}`);
8071
+ if (context?.removeProtection === true) try {
8072
+ await this.dynamoDBClient.send(new UpdateTableCommand({
8073
+ TableName: physicalId,
8074
+ DeletionProtectionEnabled: false
8075
+ }));
8076
+ this.logger.debug(`Disabled DeletionProtectionEnabled on ${logicalId}, waiting for ACTIVE`);
8077
+ try {
8078
+ await this.waitForTableActiveAfterUpdate(physicalId);
8079
+ } catch (waitErr) {
8080
+ this.logger.debug(`Could not wait for table ${physicalId} ACTIVE after protection flip: ${waitErr instanceof Error ? waitErr.message : String(waitErr)}`);
8081
+ }
8082
+ } catch (flipError) {
8083
+ if (!(flipError instanceof ResourceNotFoundException$1)) this.logger.debug(`Could not disable DeletionProtectionEnabled on ${physicalId}: ${flipError instanceof Error ? flipError.message : String(flipError)}`);
8084
+ }
8085
+ let currentRegion;
8086
+ try {
8087
+ currentRegion = await this.dynamoDBClient.config.region();
8088
+ } catch (regionErr) {
8089
+ const cause = regionErr instanceof Error ? regionErr : void 0;
8090
+ throw new ProvisioningError(`Could not resolve client region for DynamoDB GlobalTable ${logicalId} delete — would risk dropping the local replica`, resourceType, logicalId, physicalId, cause);
8091
+ }
8092
+ try {
8093
+ const replicas = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }))).Table?.Replicas ?? [];
8094
+ for (const replica of replicas) {
8095
+ const region = replica.RegionName;
8096
+ if (!region || region === currentRegion) continue;
8097
+ try {
8098
+ await this.dynamoDBClient.send(new UpdateTableCommand({
8099
+ TableName: physicalId,
8100
+ ReplicaUpdates: [{ Delete: { RegionName: region } }]
8101
+ }));
8102
+ await this.waitForReplicaGone(physicalId, region);
8103
+ } catch (replicaErr) {
8104
+ if (!(replicaErr instanceof ResourceNotFoundException$1)) throw replicaErr;
8105
+ }
8106
+ }
8107
+ } catch (describeErr) {
8108
+ if (!(describeErr instanceof ResourceNotFoundException$1)) {
8109
+ const cause = describeErr instanceof Error ? describeErr : void 0;
8110
+ throw new ProvisioningError(`Failed to describe DynamoDB GlobalTable ${logicalId} before delete: ${describeErr instanceof Error ? describeErr.message : String(describeErr)}`, resourceType, logicalId, physicalId, cause);
8111
+ }
8112
+ }
8113
+ try {
8114
+ await this.dynamoDBClient.send(new DeleteTableCommand({ TableName: physicalId }));
8115
+ await this.waitForTableGone(physicalId);
8116
+ this.logger.debug(`Successfully deleted DynamoDB GlobalTable ${logicalId}`);
8117
+ } catch (error) {
8118
+ if (error instanceof ResourceNotFoundException$1) {
8119
+ assertRegionMatch(await this.dynamoDBClient.config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
8120
+ this.logger.debug(`DynamoDB GlobalTable ${physicalId} does not exist, skipping`);
8121
+ return;
8122
+ }
8123
+ const cause = error instanceof Error ? error : void 0;
8124
+ throw new ProvisioningError(`Failed to delete DynamoDB GlobalTable ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
8125
+ }
8126
+ }
8127
+ /**
8128
+ * Resolve a single `Fn::GetAtt` attribute for an existing DynamoDB GlobalTable.
8129
+ *
8130
+ * Cached per `(physicalId, attribute)` for the deploy run to avoid
8131
+ * repeated `DescribeTable` calls.
8132
+ */
8133
+ async getAttribute(physicalId, _resourceType, attributeName) {
8134
+ const cacheKey = `${physicalId}::${attributeName}`;
8135
+ if (this.attributeCache.has(cacheKey)) return this.attributeCache.get(cacheKey);
8136
+ try {
8137
+ const resp = await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }));
8138
+ let value;
8139
+ switch (attributeName) {
8140
+ case "Arn":
8141
+ value = resp.Table?.TableArn;
8142
+ break;
8143
+ case "StreamArn":
8144
+ value = resp.Table?.LatestStreamArn;
8145
+ break;
8146
+ case "TableId":
8147
+ value = resp.Table?.TableId;
8148
+ break;
8149
+ default: value = void 0;
8150
+ }
8151
+ this.attributeCache.set(cacheKey, value);
8152
+ return value;
8153
+ } catch (err) {
8154
+ if (err instanceof ResourceNotFoundException$1) return void 0;
8155
+ throw err;
8156
+ }
8157
+ }
8158
+ /**
8159
+ * Read the AWS-current DynamoDB GlobalTable configuration in CFn-property shape.
8160
+ *
8161
+ * Reverse-maps `DescribeTable` + `ListTagsOfResource` into the
8162
+ * `AWS::DynamoDB::GlobalTable` property set.
8163
+ *
8164
+ * Type-discriminator gating (memory rule
8165
+ * `feedback_always_emit_check_type_discriminator.md`):
8166
+ * - `ProvisionedThroughput`-bearing fields are only surfaced when
8167
+ * `BillingMode === 'PROVISIONED'`. Emitting placeholders on
8168
+ * PAY_PER_REQUEST tables (or vice versa) would fire false drift on
8169
+ * every clean run.
8170
+ * - StreamSpecification / SSESpecification follow the existing
8171
+ * DynamoDB::Table provider's Class 1 guard: only surfaced when AWS
8172
+ * reports the feature actually enabled.
8173
+ *
8174
+ * `getDriftUnknownPaths` declares TTL + write-throughput-settings —
8175
+ * those round-trip in the create path but the reverse-mapping is not
8176
+ * yet implemented and would fire guaranteed false drift.
8177
+ */
8178
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
8179
+ try {
8180
+ const table = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }))).Table;
8181
+ if (!table) return void 0;
8182
+ const result = {};
8183
+ if (table.TableName !== void 0) result["TableName"] = table.TableName;
8184
+ if (table.KeySchema) result["KeySchema"] = table.KeySchema;
8185
+ if (table.AttributeDefinitions) result["AttributeDefinitions"] = table.AttributeDefinitions;
8186
+ const billingMode = table.BillingModeSummary?.BillingMode;
8187
+ if (billingMode) result["BillingMode"] = billingMode;
8188
+ if (table.GlobalSecondaryIndexes && table.GlobalSecondaryIndexes.length > 0) result["GlobalSecondaryIndexes"] = table.GlobalSecondaryIndexes;
8189
+ if (table.LocalSecondaryIndexes && table.LocalSecondaryIndexes.length > 0) result["LocalSecondaryIndexes"] = table.LocalSecondaryIndexes;
8190
+ if (table.StreamSpecification?.StreamEnabled && table.StreamSpecification.StreamViewType) result["StreamSpecification"] = {
8191
+ StreamEnabled: true,
8192
+ StreamViewType: table.StreamSpecification.StreamViewType
8193
+ };
8194
+ if (table.SSEDescription?.Status === "ENABLED") {
8195
+ const sse = { SSEEnabled: true };
8196
+ if (table.SSEDescription.SSEType !== void 0) sse["SSEType"] = table.SSEDescription.SSEType;
8197
+ result["SSESpecification"] = sse;
8198
+ }
8199
+ result["Replicas"] = (table.Replicas ?? []).map((r) => ({
8200
+ Region: r.RegionName,
8201
+ ...r.KMSMasterKeyId !== void 0 && { KMSMasterKeyId: r.KMSMasterKeyId }
8202
+ }));
8203
+ if (table.TableClassSummary?.TableClass) result["TableClass"] = table.TableClassSummary.TableClass;
8204
+ if (table.DeletionProtectionEnabled !== void 0) result["DeletionProtectionEnabled"] = table.DeletionProtectionEnabled;
8205
+ if (table.TableArn) try {
8206
+ result["Tags"] = normalizeAwsTagsToCfn((await this.dynamoDBClient.send(new ListTagsOfResourceCommand({ ResourceArn: table.TableArn }))).Tags);
8207
+ } catch (err) {
8208
+ if (err instanceof ResourceNotFoundException$1) return void 0;
8209
+ result["Tags"] = [];
8210
+ }
8211
+ else result["Tags"] = [];
8212
+ return result;
8213
+ } catch (err) {
8214
+ if (err instanceof ResourceNotFoundException$1) return void 0;
8215
+ throw err;
8216
+ }
8217
+ }
8218
+ /**
8219
+ * State property paths cdkd's GlobalTable readCurrentState cannot (yet)
8220
+ * reverse-map. The drift comparator skips these so a templated value
8221
+ * doesn't fire guaranteed false drift on every clean run.
8222
+ *
8223
+ * - `TimeToLiveSpecification`: cdkd's create() applies it via
8224
+ * UpdateTimeToLive, but the reverse-mapping needs a separate
8225
+ * DescribeTimeToLive call (not yet implemented).
8226
+ * - `WriteProvisionedThroughputSettings` /
8227
+ * `WriteOnDemandThroughputSettings`: CFn's shapes wrap
8228
+ * auto-scaling / on-demand max-RU settings whose reverse-mapping
8229
+ * from `DescribeTable.ProvisionedThroughput` / `OnDemandThroughput`
8230
+ * is non-trivial and would fire false drift in v1.
8231
+ */
8232
+ getDriftUnknownPaths(_resourceType) {
8233
+ return [
8234
+ "TimeToLiveSpecification",
8235
+ "WriteProvisionedThroughputSettings",
8236
+ "WriteOnDemandThroughputSettings"
8237
+ ];
8238
+ }
8239
+ /**
8240
+ * Adopt an existing DynamoDB GlobalTable into cdkd state.
8241
+ *
8242
+ * Lookup order:
8243
+ * 1. `--resource` override or `Properties.TableName` → verify via DescribeTable.
8244
+ * 2. `ListTables` + `ListTagsOfResource`, match `aws:cdk:path` tag.
8245
+ *
8246
+ * Same shape as `DynamoDBTableProvider.import()`. The provider returns
8247
+ * the physical id only; cdkd's import flow does the attribute capture
8248
+ * separately.
8249
+ */
8250
+ async import(input) {
8251
+ const explicit = resolveExplicitPhysicalId(input, "TableName");
8252
+ if (explicit) try {
8253
+ await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: explicit }));
8254
+ return {
8255
+ physicalId: explicit,
8256
+ attributes: {}
8257
+ };
8258
+ } catch (err) {
8259
+ if (err instanceof ResourceNotFoundException$1) return null;
8260
+ throw err;
8261
+ }
8262
+ if (!input.cdkPath) return null;
8263
+ let exclusiveStartTableName;
8264
+ do {
8265
+ const list = await this.dynamoDBClient.send(new ListTablesCommand({ ...exclusiveStartTableName && { ExclusiveStartTableName: exclusiveStartTableName } }));
8266
+ for (const name of list.TableNames ?? []) try {
8267
+ const arn = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: name }))).Table?.TableArn;
8268
+ if (!arn) continue;
8269
+ if (matchesCdkPath((await this.dynamoDBClient.send(new ListTagsOfResourceCommand({ ResourceArn: arn }))).Tags, input.cdkPath)) return {
8270
+ physicalId: name,
8271
+ attributes: {}
8272
+ };
8273
+ } catch (err) {
8274
+ if (err instanceof ResourceNotFoundException$1) continue;
8275
+ throw err;
8276
+ }
8277
+ exclusiveStartTableName = list.LastEvaluatedTableName;
8278
+ } while (exclusiveStartTableName);
8279
+ return null;
8280
+ }
8281
+ async waitForTableActive(tableName, maxAttempts = 120) {
8282
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
8283
+ const response = await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }));
8284
+ const status = response.Table?.TableStatus;
8285
+ this.logger.debug(`Table ${tableName} status: ${status} (attempt ${attempt}/${maxAttempts})`);
8286
+ if (status === "ACTIVE") return {
8287
+ tableArn: response.Table?.TableArn,
8288
+ tableId: response.Table?.TableId,
8289
+ streamArn: response.Table?.LatestStreamArn
8290
+ };
8291
+ if (status !== "CREATING" && status !== "UPDATING") throw new Error(`Unexpected table status: ${status}`);
8292
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
8293
+ }
8294
+ throw new Error(`Table ${tableName} did not reach ACTIVE within ${maxAttempts}s`);
8295
+ }
8296
+ /**
8297
+ * Wait for the table to reach ACTIVE after an UpdateTable call. Unlike
8298
+ * `waitForTableActive`, this tolerates any non-terminal status — the
8299
+ * table may already be ACTIVE on the no-op path (already-disabled
8300
+ * protection) or transition through UPDATING.
8301
+ */
8302
+ async waitForTableActiveAfterUpdate(tableName, maxAttempts = 120) {
8303
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
8304
+ if ((await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.TableStatus === "ACTIVE") return;
8305
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
8306
+ }
8307
+ throw new Error(`Table ${tableName} did not reach ACTIVE within ${maxAttempts}s after UpdateTable`);
8308
+ }
8309
+ /**
8310
+ * Wait until a specific replica's `ReplicaStatus` flips to ACTIVE.
8311
+ * Replica provisioning typically takes 1–5 min; cap at 10 min.
8312
+ */
8313
+ async waitForReplicaActive(tableName, region, maxAttempts = 600) {
8314
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
8315
+ const replica = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.Replicas?.find((r) => r.RegionName === region);
8316
+ if (replica?.ReplicaStatus === "ACTIVE") return;
8317
+ this.logger.debug(`Replica ${region} status: ${replica?.ReplicaStatus} (attempt ${attempt}/${maxAttempts})`);
8318
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
8319
+ }
8320
+ throw new Error(`Replica ${region} for table ${tableName} did not reach ACTIVE within ${maxAttempts}s`);
8321
+ }
8322
+ /**
8323
+ * Wait until a specific replica disappears from `Replicas[]` after a
8324
+ * Delete replica update. Replica deletion typically takes 1–5 min;
8325
+ * cap at 10 min.
8326
+ */
8327
+ async waitForReplicaGone(tableName, region, maxAttempts = 600) {
8328
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
8329
+ if (!(await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.Replicas?.find((r) => r.RegionName === region)) return;
8330
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
8331
+ } catch (err) {
8332
+ if (err instanceof ResourceNotFoundException$1) return;
8333
+ throw err;
8334
+ }
8335
+ throw new Error(`Replica ${region} for table ${tableName} did not disappear within ${maxAttempts}s`);
8336
+ }
8337
+ /**
8338
+ * Wait for `DescribeTable` to return `ResourceNotFoundException`,
8339
+ * confirming the table has actually been removed. `DeleteTable` is
8340
+ * async — the call returns immediately with `TableStatus: DELETING`
8341
+ * and AWS only removes the table some seconds later. Without this
8342
+ * wait, downstream observers (siblings deleted in the same destroy
8343
+ * run, integ scripts that re-check via `aws dynamodb describe-table`)
8344
+ * see "destroy succeeded" but the table is still listed by AWS.
8345
+ * Typical small-table delete completes in 5–30s; cap at 10 min for
8346
+ * worst-case large-table / replica-cascade scenarios.
8347
+ */
8348
+ async waitForTableGone(tableName, maxAttempts = 600) {
8349
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
8350
+ await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }));
8351
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
8352
+ } catch (err) {
8353
+ if (err instanceof ResourceNotFoundException$1) return;
8354
+ throw err;
8355
+ }
8356
+ throw new Error(`Table ${tableName} did not disappear within ${maxAttempts}s`);
8357
+ }
8358
+ };
8359
+
7842
8360
  //#endregion
7843
8361
  //#region src/provisioning/providers/logs-loggroup-provider.ts
7844
8362
  /**
@@ -14812,7 +15330,7 @@ var StepFunctionsProvider = class {
14812
15330
  result["EncryptionConfiguration"] = ec;
14813
15331
  }
14814
15332
  try {
14815
- result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$8({ resourceArn: physicalId }))).tags);
15333
+ result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$9({ resourceArn: physicalId }))).tags);
14816
15334
  } catch (err) {
14817
15335
  if (!(err instanceof StateMachineDoesNotExist)) throw err;
14818
15336
  }
@@ -14850,7 +15368,7 @@ var StepFunctionsProvider = class {
14850
15368
  const list = await this.getClient().send(new ListStateMachinesCommand({ ...nextToken && { nextToken } }));
14851
15369
  for (const sm of list.stateMachines ?? []) {
14852
15370
  if (!sm.stateMachineArn) continue;
14853
- const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$8({ resourceArn: sm.stateMachineArn }));
15371
+ const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$9({ resourceArn: sm.stateMachineArn }));
14854
15372
  if (this.tagsMatchCdkPath(tagsResp.tags, input.cdkPath)) return {
14855
15373
  physicalId: sm.stateMachineArn,
14856
15374
  attributes: {}
@@ -15786,7 +16304,7 @@ var ECSProvider = class {
15786
16304
  do {
15787
16305
  const list = await this.getClient().send(new ListClustersCommand({ ...nextToken && { nextToken } }));
15788
16306
  for (const arn of list.clusterArns ?? []) {
15789
- const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$9({ resourceArn: arn }));
16307
+ const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$10({ resourceArn: arn }));
15790
16308
  if (this.tagsMatchCdkPath(tagsResp.tags, input.cdkPath)) return {
15791
16309
  physicalId: arn.substring(arn.lastIndexOf("/") + 1),
15792
16310
  attributes: {}
@@ -15813,7 +16331,7 @@ var ECSProvider = class {
15813
16331
  ...svcToken && { nextToken: svcToken }
15814
16332
  }));
15815
16333
  for (const svcArn of svcList.serviceArns ?? []) {
15816
- const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$9({ resourceArn: svcArn }));
16334
+ const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$10({ resourceArn: svcArn }));
15817
16335
  if (this.tagsMatchCdkPath(tagsResp.tags, input.cdkPath)) return {
15818
16336
  physicalId: `${clusterArn}|${svcArn.substring(svcArn.lastIndexOf("/") + 1)}`,
15819
16337
  attributes: {}
@@ -17142,7 +17660,7 @@ var RDSProvider = class {
17142
17660
  */
17143
17661
  async attachTags(result, arn) {
17144
17662
  try {
17145
- result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$10({ ResourceName: arn }))).TagList);
17663
+ result["Tags"] = normalizeAwsTagsToCfn((await this.getClient().send(new ListTagsForResourceCommand$8({ ResourceName: arn }))).TagList);
17146
17664
  } catch (err) {
17147
17665
  this.logger.debug(`RDS ListTagsForResource(${arn}) failed: ${err instanceof Error ? err.message : String(err)}`);
17148
17666
  }
@@ -17165,7 +17683,7 @@ var RDSProvider = class {
17165
17683
  const list = await this.getClient().send(new DescribeDBInstancesCommand({ ...marker && { Marker: marker } }));
17166
17684
  for (const inst of list.DBInstances ?? []) {
17167
17685
  if (!inst.DBInstanceIdentifier || !inst.DBInstanceArn) continue;
17168
- if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$10({ ResourceName: inst.DBInstanceArn }))).TagList, input.cdkPath)) return {
17686
+ if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$8({ ResourceName: inst.DBInstanceArn }))).TagList, input.cdkPath)) return {
17169
17687
  physicalId: inst.DBInstanceIdentifier,
17170
17688
  attributes: {}
17171
17689
  };
@@ -17192,7 +17710,7 @@ var RDSProvider = class {
17192
17710
  const list = await this.getClient().send(new DescribeDBClustersCommand({ ...marker && { Marker: marker } }));
17193
17711
  for (const c of list.DBClusters ?? []) {
17194
17712
  if (!c.DBClusterIdentifier || !c.DBClusterArn) continue;
17195
- if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$10({ ResourceName: c.DBClusterArn }))).TagList, input.cdkPath)) return {
17713
+ if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$8({ ResourceName: c.DBClusterArn }))).TagList, input.cdkPath)) return {
17196
17714
  physicalId: c.DBClusterIdentifier,
17197
17715
  attributes: {}
17198
17716
  };
@@ -17219,7 +17737,7 @@ var RDSProvider = class {
17219
17737
  const list = await this.getClient().send(new DescribeDBSubnetGroupsCommand({ ...marker && { Marker: marker } }));
17220
17738
  for (const sg of list.DBSubnetGroups ?? []) {
17221
17739
  if (!sg.DBSubnetGroupName || !sg.DBSubnetGroupArn) continue;
17222
- if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$10({ ResourceName: sg.DBSubnetGroupArn }))).TagList, input.cdkPath)) return {
17740
+ if (matchesCdkPath((await this.getClient().send(new ListTagsForResourceCommand$8({ ResourceName: sg.DBSubnetGroupArn }))).TagList, input.cdkPath)) return {
17223
17741
  physicalId: sg.DBSubnetGroupName,
17224
17742
  attributes: {}
17225
17743
  };
@@ -27905,6 +28423,7 @@ function registerAllProviders(registry) {
27905
28423
  registry.register("AWS::Lambda::EventSourceMapping", new LambdaEventSourceMappingProvider());
27906
28424
  registry.register("AWS::Lambda::LayerVersion", new LambdaLayerVersionProvider());
27907
28425
  registry.register("AWS::DynamoDB::Table", new DynamoDBTableProvider());
28426
+ registry.register("AWS::DynamoDB::GlobalTable", new DynamoDBGlobalTableProvider());
27908
28427
  registry.register("AWS::Logs::LogGroup", new LogsLogGroupProvider());
27909
28428
  registry.register("AWS::CloudWatch::Alarm", new CloudWatchAlarmProvider());
27910
28429
  registry.register("AWS::SecretsManager::Secret", new SecretsManagerSecretProvider());
@@ -29427,6 +29946,7 @@ const PROTECTION_PROPERTY_BY_TYPE = {
29427
29946
  "AWS::Neptune::DBCluster": "DeletionProtection",
29428
29947
  "AWS::Neptune::DBInstance": "DeletionProtection",
29429
29948
  "AWS::DynamoDB::Table": "DeletionProtectionEnabled",
29949
+ "AWS::DynamoDB::GlobalTable": "DeletionProtectionEnabled",
29430
29950
  "AWS::EC2::Instance": "DisableApiTermination",
29431
29951
  "AWS::Cognito::UserPool": "DeletionProtection",
29432
29952
  "AWS::AutoScaling::AutoScalingGroup": "DeletionProtection"
@@ -42932,7 +43452,7 @@ function reorderArgs(argv) {
42932
43452
  */
42933
43453
  async function main() {
42934
43454
  const program = new Command();
42935
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.102.7");
43455
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.103.1");
42936
43456
  program.addCommand(createBootstrapCommand());
42937
43457
  program.addCommand(createSynthCommand());
42938
43458
  program.addCommand(createListCommand());