@go-to-k/cdkd 0.107.0 → 0.109.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,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-D6nbHjNM.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-Br8DvrvB.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";
@@ -30,12 +30,12 @@ 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, CreateDBProxyCommand, CreateDBSubnetGroupCommand, DBProxyNotFoundFault, DBProxyTargetGroupNotFoundFault, DBProxyTargetNotFoundFault, DeleteDBClusterCommand, DeleteDBInstanceCommand, DeleteDBProxyCommand, DeleteDBSubnetGroupCommand, DeregisterDBProxyTargetsCommand, DescribeDBClustersCommand, DescribeDBInstancesCommand, DescribeDBProxiesCommand, DescribeDBProxyTargetGroupsCommand, DescribeDBSubnetGroupsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$8, ModifyDBClusterCommand, ModifyDBInstanceCommand, ModifyDBProxyCommand, ModifyDBProxyTargetGroupCommand, ModifyDBSubnetGroupCommand, RDSClient, RegisterDBProxyTargetsCommand, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$1 } from "@aws-sdk/client-rds";
33
+ import { AddTagsToResourceCommand as AddTagsToResourceCommand$1, CreateDBClusterCommand, CreateDBInstanceCommand, CreateDBProxyCommand, CreateDBProxyEndpointCommand, CreateDBSubnetGroupCommand, DBProxyEndpointNotFoundFault, DBProxyNotFoundFault, DBProxyTargetGroupNotFoundFault, DBProxyTargetNotFoundFault, DeleteDBClusterCommand, DeleteDBInstanceCommand, DeleteDBProxyCommand, DeleteDBProxyEndpointCommand, DeleteDBSubnetGroupCommand, DeregisterDBProxyTargetsCommand, DescribeDBClustersCommand, DescribeDBInstancesCommand, DescribeDBProxiesCommand, DescribeDBProxyEndpointsCommand, DescribeDBProxyTargetGroupsCommand, DescribeDBSubnetGroupsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$8, ModifyDBClusterCommand, ModifyDBInstanceCommand, ModifyDBProxyCommand, ModifyDBProxyEndpointCommand, ModifyDBProxyTargetGroupCommand, ModifyDBSubnetGroupCommand, RDSClient, RegisterDBProxyTargetsCommand, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$1 } from "@aws-sdk/client-rds";
34
34
  import { Command, Option } from "commander";
35
35
  import { writeFileSync as writeFileSync$1 } from "fs";
36
36
  import { join as join$1 } from "path";
37
37
  import * as zlib from "node:zlib";
38
- import { ApplicationAutoScalingClient, DescribeScalableTargetsCommand, DescribeScalingPoliciesCommand } from "@aws-sdk/client-application-auto-scaling";
38
+ import { ApplicationAutoScalingClient, DeleteScalingPolicyCommand, DeregisterScalableTargetCommand, DescribeScalableTargetsCommand, DescribeScalingPoliciesCommand, PutScalingPolicyCommand, RegisterScalableTargetCommand } from "@aws-sdk/client-application-auto-scaling";
39
39
  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";
40
40
  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";
41
41
  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";
@@ -7984,6 +7984,20 @@ var DynamoDBGlobalTableProvider = class {
7984
7984
  return client;
7985
7985
  }
7986
7986
  /**
7987
+ * Lazy-init + cache the local-region `ApplicationAutoScalingClient`.
7988
+ * Previously `applyAutoScalingDiff` constructed a fresh client per
7989
+ * call when no `client` arg was passed, leaking SDK clients (each
7990
+ * holds its own HTTP agent) on multi-stack runs with many
7991
+ * GlobalTables (PR #403 review minor #4).
7992
+ */
7993
+ localAutoScalingClient;
7994
+ async getLocalAutoScalingClient() {
7995
+ if (this.localAutoScalingClient) return this.localAutoScalingClient;
7996
+ const region = await this.dynamoDBClient.config.region() ?? "";
7997
+ this.localAutoScalingClient = new ApplicationAutoScalingClient({ region });
7998
+ return this.localAutoScalingClient;
7999
+ }
8000
+ /**
7987
8001
  * Construct the regional table ARN for a cross-region replica of a
7988
8002
  * GlobalTable. AWS replicates the same `TableName` across every
7989
8003
  * replica region, with each replica's ARN differing only in the
@@ -8237,10 +8251,19 @@ var DynamoDBGlobalTableProvider = class {
8237
8251
  await this.dynamoDBClient.send(new UpdateTableCommand(billingUpdate));
8238
8252
  await this.waitForTableActiveAfterUpdate(physicalId, logicalId);
8239
8253
  }
8254
+ const writeAutoScalingOld = (previousProperties["WriteProvisionedThroughputSettings"] ?? {})["WriteCapacityAutoScalingSettings"];
8255
+ const writeAutoScalingNew = (properties["WriteProvisionedThroughputSettings"] ?? {})["WriteCapacityAutoScalingSettings"];
8256
+ const effectiveNewAutoScaling = oldBilling === "PROVISIONED" && newBilling === "PAY_PER_REQUEST" ? void 0 : writeAutoScalingNew;
8257
+ if ((newBilling === "PROVISIONED" || oldBilling === "PROVISIONED") && !deepEqual$1(writeAutoScalingOld, effectiveNewAutoScaling)) await this.applyAutoScalingDiff(physicalId, "dynamodb:table:WriteCapacityUnits", writeAutoScalingOld, effectiveNewAutoScaling);
8240
8258
  const replicaDiff = diffReplicas(previousProperties["Replicas"] ?? [], properties["Replicas"] ?? []);
8241
8259
  for (const replica of replicaDiff.removed) {
8242
8260
  const region = replica["Region"];
8243
8261
  if (!region || region === currentRegion) continue;
8262
+ const removedReadAutoScaling = (replica["ReadProvisionedThroughputSettings"] ?? {})["ReadCapacityAutoScalingSettings"];
8263
+ if (removedReadAutoScaling) {
8264
+ const regionalAutoScalingClient = this.getRegionalAutoScalingClient(region);
8265
+ await this.applyAutoScalingDiff(physicalId, "dynamodb:table:ReadCapacityUnits", removedReadAutoScaling, void 0, regionalAutoScalingClient);
8266
+ }
8244
8267
  await this.dynamoDBClient.send(new UpdateTableCommand({
8245
8268
  TableName: physicalId,
8246
8269
  ReplicaUpdates: [{ Delete: { RegionName: region } }]
@@ -8251,11 +8274,17 @@ var DynamoDBGlobalTableProvider = class {
8251
8274
  const region = replica["Region"];
8252
8275
  if (!region || region === currentRegion) continue;
8253
8276
  await this.addReplica(physicalId, replica, region, logicalId);
8277
+ const newReadAutoScaling = (replica["ReadProvisionedThroughputSettings"] ?? {})["ReadCapacityAutoScalingSettings"];
8278
+ if (newBilling === "PROVISIONED" && newReadAutoScaling) {
8279
+ const regionalAutoScalingClient = this.getRegionalAutoScalingClient(region);
8280
+ await this.applyAutoScalingDiff(physicalId, "dynamodb:table:ReadCapacityUnits", void 0, newReadAutoScaling, regionalAutoScalingClient);
8281
+ }
8254
8282
  }
8255
8283
  for (const replica of replicaDiff.modified) {
8256
8284
  const region = replica["Region"];
8257
8285
  if (!region || region === currentRegion) continue;
8258
- const oldReplicaTags = (previousProperties["Replicas"] ?? []).find((r) => r["Region"] === region)?.["Tags"];
8286
+ const oldReplica = (previousProperties["Replicas"] ?? []).find((r) => r["Region"] === region);
8287
+ const oldReplicaTags = oldReplica?.["Tags"];
8259
8288
  const newReplicaTags = replica["Tags"];
8260
8289
  if (!deepEqual$1(oldReplicaTags, newReplicaTags)) if (tableArn) {
8261
8290
  const replicaArn = this.replicaArnForRegion(tableArn, region);
@@ -8267,6 +8296,13 @@ var DynamoDBGlobalTableProvider = class {
8267
8296
  }
8268
8297
  else this.logger.warn(`Could not derive replica ARN for region ${region} from ${tableArn} — skipping Tags propagation for ${physicalId}`);
8269
8298
  } else this.logger.warn(`Local DescribeTable returned no TableArn — cannot propagate Tags to cross-region replica ${region} of ${physicalId}`);
8299
+ const oldReadAutoScaling = (oldReplica?.["ReadProvisionedThroughputSettings"] ?? {})["ReadCapacityAutoScalingSettings"];
8300
+ const newReadAutoScaling = (replica["ReadProvisionedThroughputSettings"] ?? {})["ReadCapacityAutoScalingSettings"];
8301
+ const effectiveNewReadAutoScaling = newBilling === "PAY_PER_REQUEST" ? void 0 : newReadAutoScaling;
8302
+ if ((newBilling === "PROVISIONED" || oldBilling === "PROVISIONED") && !deepEqual$1(oldReadAutoScaling, effectiveNewReadAutoScaling)) {
8303
+ const regionalAutoScalingClient = this.getRegionalAutoScalingClient(region);
8304
+ await this.applyAutoScalingDiff(physicalId, "dynamodb:table:ReadCapacityUnits", oldReadAutoScaling, effectiveNewReadAutoScaling, regionalAutoScalingClient);
8305
+ }
8270
8306
  const updateAction = { RegionName: region };
8271
8307
  if (replica["KMSMasterKeyId"] !== void 0) updateAction.KMSMasterKeyId = replica["KMSMasterKeyId"];
8272
8308
  if (replica["GlobalSecondaryIndexes"]) updateAction.GlobalSecondaryIndexes = replica["GlobalSecondaryIndexes"];
@@ -8405,6 +8441,127 @@ var DynamoDBGlobalTableProvider = class {
8405
8441
  }
8406
8442
  }
8407
8443
  /**
8444
+ * Apply an application-autoscaling diff for one (tableName, dimension)
8445
+ * pair (Issue #402 / PR closing Issue #395's deferred items).
8446
+ *
8447
+ * Dimension is either:
8448
+ * - `dynamodb:table:WriteCapacityUnits` (table-level write capacity),
8449
+ * paired with the local-region autoscaling client.
8450
+ * - `dynamodb:table:ReadCapacityUnits` (per-replica read capacity),
8451
+ * paired with a region-scoped autoscaling client returned by
8452
+ * `getRegionalAutoScalingClient`.
8453
+ *
8454
+ * Diff semantics (idempotent upsert, per Issue #402 spec):
8455
+ * - new auto-scaling settings present (Min/Max + TargetValue) →
8456
+ * `RegisterScalableTarget` (upsert) + `PutScalingPolicy` (upsert).
8457
+ * AWS's `RegisterScalableTarget` accepts no-op Min/Max changes
8458
+ * silently, and `PutScalingPolicy` is idempotent on the same
8459
+ * policy name (`DynamoDB{Write,Read}CapacityUtilization:table/<name>`).
8460
+ * - old auto-scaling settings present but new ones absent (= null) →
8461
+ * `DeleteScalingPolicy` + `DeregisterScalableTarget`. AWS rejects
8462
+ * `DeregisterScalableTarget` on a still-policy-attached target,
8463
+ * so the policy must be deleted FIRST.
8464
+ * - neither side has auto-scaling settings → no-op.
8465
+ *
8466
+ * **Best-effort**: any AWS error is logged at WARN with the per-API
8467
+ * recovery command and the deploy continues — auto-scaling drift is
8468
+ * recoverable on the next deploy, and an aborted deploy is much worse
8469
+ * UX than a transient auto-scaling miss. This matches the cross-region
8470
+ * Tags propagation contract (PR #393).
8471
+ *
8472
+ * **Policy naming** matches AWS's CDK / console default:
8473
+ * `DynamoDBWriteCapacityUtilization:table/<table-name>` (write)
8474
+ * `DynamoDBReadCapacityUtilization:table/<table-name>` (read)
8475
+ * so re-imports against a console-created target also match.
8476
+ *
8477
+ * **`SeedCapacity` is intentionally NOT forwarded** — it is a CFn
8478
+ * create-only field with no corresponding `RegisterScalableTarget`
8479
+ * surface; `readAutoScalingSettings` also explicitly skips it.
8480
+ */
8481
+ async applyAutoScalingDiff(tableName, dimension, oldSettings, newSettings, client) {
8482
+ const isWrite = dimension === "dynamodb:table:WriteCapacityUnits";
8483
+ const policyName = isWrite ? `DynamoDBWriteCapacityUtilization:table/${tableName}` : `DynamoDBReadCapacityUtilization:table/${tableName}`;
8484
+ const metricType = isWrite ? "DynamoDBWriteCapacityUtilization" : "DynamoDBReadCapacityUtilization";
8485
+ const resourceId = `table/${tableName}`;
8486
+ const asClient = client ?? await this.getLocalAutoScalingClient();
8487
+ const oldEnabled = oldSettings !== void 0 && oldSettings !== null;
8488
+ const newEnabled = newSettings !== void 0 && newSettings !== null;
8489
+ if (!oldEnabled && !newEnabled) return;
8490
+ if (newEnabled) {
8491
+ const minCapacity = Number(newSettings["MinCapacity"] ?? 0);
8492
+ const maxCapacity = Number(newSettings["MaxCapacity"] ?? 0);
8493
+ if (!Number.isFinite(minCapacity) || !Number.isFinite(maxCapacity)) {
8494
+ this.logger.warn(`Cannot apply auto-scaling diff on ${tableName} (${dimension}): MinCapacity / MaxCapacity must be numbers, got ${String(newSettings["MinCapacity"])} / ${String(newSettings["MaxCapacity"])}`);
8495
+ return;
8496
+ }
8497
+ try {
8498
+ await asClient.send(new RegisterScalableTargetCommand({
8499
+ ServiceNamespace: "dynamodb",
8500
+ ResourceId: resourceId,
8501
+ ScalableDimension: dimension,
8502
+ MinCapacity: minCapacity,
8503
+ MaxCapacity: maxCapacity
8504
+ }));
8505
+ } catch (err) {
8506
+ this.logger.warn(`Could not register auto-scaling target on ${tableName} (${dimension}): ${err instanceof Error ? err.message : String(err)}. Run: aws application-autoscaling register-scalable-target --service-namespace dynamodb --resource-id ${resourceId} --scalable-dimension ${dimension} --min-capacity ${minCapacity} --max-capacity ${maxCapacity}`);
8507
+ return;
8508
+ }
8509
+ const tttCfg = newSettings["TargetTrackingScalingPolicyConfiguration"] ?? {};
8510
+ const targetValue = Number(tttCfg["TargetValue"]);
8511
+ if (!Number.isFinite(targetValue)) {
8512
+ this.logger.warn(`Auto-scaling target registered on ${tableName} (${dimension}) but TargetValue is missing or non-numeric — skipping PutScalingPolicy. Provide TargetTrackingScalingPolicyConfiguration.TargetValue in the template.`);
8513
+ return;
8514
+ }
8515
+ const targetTrackingConfig = {
8516
+ PredefinedMetricSpecification: { PredefinedMetricType: metricType },
8517
+ TargetValue: targetValue
8518
+ };
8519
+ if (tttCfg["ScaleInCooldown"] !== void 0) targetTrackingConfig["ScaleInCooldown"] = Number(tttCfg["ScaleInCooldown"]);
8520
+ if (tttCfg["ScaleOutCooldown"] !== void 0) targetTrackingConfig["ScaleOutCooldown"] = Number(tttCfg["ScaleOutCooldown"]);
8521
+ if (tttCfg["DisableScaleIn"] !== void 0) targetTrackingConfig["DisableScaleIn"] = Boolean(tttCfg["DisableScaleIn"]);
8522
+ try {
8523
+ await asClient.send(new PutScalingPolicyCommand({
8524
+ PolicyName: policyName,
8525
+ ServiceNamespace: "dynamodb",
8526
+ ResourceId: resourceId,
8527
+ ScalableDimension: dimension,
8528
+ PolicyType: "TargetTrackingScaling",
8529
+ TargetTrackingScalingPolicyConfiguration: targetTrackingConfig
8530
+ }));
8531
+ this.logger.debug(`Upserted auto-scaling policy ${policyName} on ${tableName} (${dimension})`);
8532
+ } catch (err) {
8533
+ this.logger.warn(`Could not put auto-scaling policy on ${tableName} (${dimension}): ${err instanceof Error ? err.message : String(err)}. Run: aws application-autoscaling put-scaling-policy --policy-name ${policyName} --service-namespace dynamodb --resource-id ${resourceId} --scalable-dimension ${dimension} --policy-type TargetTrackingScaling`);
8534
+ }
8535
+ return;
8536
+ }
8537
+ const isObjectNotFound = (err) => {
8538
+ if (!(err instanceof Error)) return false;
8539
+ const name = err.name ?? "";
8540
+ const msg = err.message ?? "";
8541
+ return name === "ObjectNotFoundException" || msg.includes("No scaling policy found") || msg.includes("No scalable target found");
8542
+ };
8543
+ try {
8544
+ await asClient.send(new DeleteScalingPolicyCommand({
8545
+ PolicyName: policyName,
8546
+ ServiceNamespace: "dynamodb",
8547
+ ResourceId: resourceId,
8548
+ ScalableDimension: dimension
8549
+ }));
8550
+ } catch (err) {
8551
+ if (!isObjectNotFound(err)) this.logger.warn(`Could not delete auto-scaling policy on ${tableName} (${dimension}): ${err instanceof Error ? err.message : String(err)}. Run: aws application-autoscaling delete-scaling-policy --policy-name ${policyName} --service-namespace dynamodb --resource-id ${resourceId} --scalable-dimension ${dimension}`);
8552
+ }
8553
+ try {
8554
+ await asClient.send(new DeregisterScalableTargetCommand({
8555
+ ServiceNamespace: "dynamodb",
8556
+ ResourceId: resourceId,
8557
+ ScalableDimension: dimension
8558
+ }));
8559
+ this.logger.debug(`Deregistered auto-scaling target ${resourceId} (${dimension})`);
8560
+ } catch (err) {
8561
+ if (!isObjectNotFound(err)) this.logger.warn(`Could not deregister auto-scaling target on ${tableName} (${dimension}): ${err instanceof Error ? err.message : String(err)}. Run: aws application-autoscaling deregister-scalable-target --service-namespace dynamodb --resource-id ${resourceId} --scalable-dimension ${dimension}`);
8562
+ }
8563
+ }
8564
+ /**
8408
8565
  * Delete a DynamoDB Global Table.
8409
8566
  *
8410
8567
  * Order is load-bearing:
@@ -8450,6 +8607,7 @@ var DynamoDBGlobalTableProvider = class {
8450
8607
  const region = replica.RegionName;
8451
8608
  if (!region || region === currentRegion) continue;
8452
8609
  try {
8610
+ await this.applyAutoScalingDiff(physicalId, "dynamodb:table:ReadCapacityUnits", {}, void 0, this.getRegionalAutoScalingClient(region));
8453
8611
  await this.dynamoDBClient.send(new UpdateTableCommand({
8454
8612
  TableName: physicalId,
8455
8613
  ReplicaUpdates: [{ Delete: { RegionName: region } }]
@@ -8459,6 +8617,9 @@ var DynamoDBGlobalTableProvider = class {
8459
8617
  if (!(replicaErr instanceof ResourceNotFoundException$1)) throw replicaErr;
8460
8618
  }
8461
8619
  }
8620
+ const localAsClient = await this.getLocalAutoScalingClient();
8621
+ await this.applyAutoScalingDiff(physicalId, "dynamodb:table:ReadCapacityUnits", {}, void 0, localAsClient);
8622
+ await this.applyAutoScalingDiff(physicalId, "dynamodb:table:WriteCapacityUnits", {}, void 0, localAsClient);
8462
8623
  } catch (describeErr) {
8463
8624
  if (!(describeErr instanceof ResourceNotFoundException$1)) {
8464
8625
  const cause = describeErr instanceof Error ? describeErr : void 0;
@@ -18395,8 +18556,8 @@ var RDSProvider = class {
18395
18556
 
18396
18557
  //#endregion
18397
18558
  //#region src/provisioning/providers/rds-dbproxy-provider.ts
18398
- const POLL_INTERVAL_MS = 5e3;
18399
- const POLL_TIMEOUT_MS = 900 * 1e3;
18559
+ const POLL_INTERVAL_MS$1 = 5e3;
18560
+ const POLL_TIMEOUT_MS$1 = 900 * 1e3;
18400
18561
  /**
18401
18562
  * AWS RDS DBProxy Provider
18402
18563
  *
@@ -18483,7 +18644,7 @@ var RDSDBProxyProvider = class {
18483
18644
  let endpoint;
18484
18645
  let dbProxyArn;
18485
18646
  let vpcId;
18486
- const deadline = Date.now() + POLL_TIMEOUT_MS;
18647
+ const deadline = Date.now() + POLL_TIMEOUT_MS$1;
18487
18648
  let status;
18488
18649
  while (Date.now() < deadline) {
18489
18650
  try {
@@ -18500,7 +18661,7 @@ var RDSDBProxyProvider = class {
18500
18661
  if (error instanceof DBProxyNotFoundFault) {} else if (error instanceof ProvisioningError) throw error;
18501
18662
  else throw this.wrapError(error, "CREATE (poll)", resourceType, logicalId, dbProxyName);
18502
18663
  }
18503
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
18664
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS$1));
18504
18665
  }
18505
18666
  if (!endpoint || !dbProxyArn) throw new ProvisioningError(`Timed out waiting for DBProxy ${dbProxyName} to become available (last status: ${status ?? "unknown"})`, resourceType, logicalId, dbProxyName);
18506
18667
  return {
@@ -18514,6 +18675,11 @@ var RDSDBProxyProvider = class {
18514
18675
  }
18515
18676
  async update(logicalId, physicalId, resourceType, properties, previousProperties) {
18516
18677
  const client = this.getClient();
18678
+ for (const field of [
18679
+ "DBProxyName",
18680
+ "EngineFamily",
18681
+ "VpcSubnetIds"
18682
+ ]) if (JSON.stringify(properties[field]) !== JSON.stringify(previousProperties[field])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `${field} is immutable on AWS::RDS::DBProxy — destroy + redeploy to change it`);
18517
18683
  const input = { DBProxyName: physicalId };
18518
18684
  let hasModify = false;
18519
18685
  for (const key of [
@@ -18556,7 +18722,7 @@ var RDSDBProxyProvider = class {
18556
18722
  }
18557
18723
  throw this.wrapError(error, "DELETE", resourceType, logicalId, physicalId);
18558
18724
  }
18559
- const deadline = Date.now() + POLL_TIMEOUT_MS;
18725
+ const deadline = Date.now() + POLL_TIMEOUT_MS$1;
18560
18726
  while (Date.now() < deadline) {
18561
18727
  try {
18562
18728
  await client.send(new DescribeDBProxiesCommand({ DBProxyName: physicalId }));
@@ -18567,7 +18733,7 @@ var RDSDBProxyProvider = class {
18567
18733
  }
18568
18734
  throw this.wrapError(error, "DELETE (poll)", resourceType, logicalId, physicalId);
18569
18735
  }
18570
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
18736
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS$1));
18571
18737
  }
18572
18738
  throw new ProvisioningError(`Timed out waiting for DBProxy ${physicalId} to fully delete`, resourceType, logicalId, physicalId);
18573
18739
  }
@@ -18742,6 +18908,337 @@ var RDSDBProxyProvider = class {
18742
18908
  }
18743
18909
  };
18744
18910
 
18911
+ //#endregion
18912
+ //#region src/provisioning/providers/rds-dbproxy-endpoint-provider.ts
18913
+ const POLL_INTERVAL_MS = 5e3;
18914
+ const POLL_TIMEOUT_MS = 900 * 1e3;
18915
+ /**
18916
+ * AWS RDS DBProxyEndpoint Provider
18917
+ *
18918
+ * Implements resource provisioning for `AWS::RDS::DBProxyEndpoint` — the
18919
+ * additional read/write or read-only endpoint that can be attached to a
18920
+ * parent DBProxy.
18921
+ *
18922
+ * **Why a dedicated SDK provider** (per `feedback_dedicated_provider_over_special_case.md`):
18923
+ * completes the RDS DBProxy family started in PR #387 (`DBProxyTargetGroup`)
18924
+ * and PR #394 (`DBProxy`). Keeps the whole family on one codebase so create /
18925
+ * update / delete handling stays consistent across the parent + endpoints +
18926
+ * target-group children.
18927
+ *
18928
+ * **Lifecycle**:
18929
+ * - `create`: validates required fields (`DBProxyName` / `VpcSubnetIds`),
18930
+ * issues `CreateDBProxyEndpointCommand`, then polls `DescribeDBProxyEndpoints`
18931
+ * until `Status === 'available'`. Returns `physicalId = DBProxyEndpointName`
18932
+ * plus `Endpoint` / `DBProxyEndpointArn` / `IsDefault` / `VpcId` in
18933
+ * `attributes`.
18934
+ * - `update`: `ModifyDBProxyEndpointCommand` for the mutable fields
18935
+ * (`VpcSecurityGroupIds` → SDK input `VpcSecurityGroupIds`,
18936
+ * `NewDBProxyEndpointName` via rename). Tags diff via separate
18937
+ * `AddTagsToResource` / `RemoveTagsFromResource` calls. DBProxyName /
18938
+ * VpcSubnetIds / TargetRole are immutable on AWS.
18939
+ * - `delete`: `DeleteDBProxyEndpointCommand`, then polls until
18940
+ * `DBProxyEndpointNotFoundFault`. Idempotent on NotFound (region-match
18941
+ * gated). `DBProxyNotFoundFault` also idempotent — if the parent DBProxy
18942
+ * is already gone via CASCADE, the endpoint is too.
18943
+ * - `getAttribute`: `Endpoint` / `DBProxyEndpointArn` / `IsDefault` / `VpcId`
18944
+ * via `DescribeDBProxyEndpoints`, cached per `(physicalId, attribute)`.
18945
+ * - `import`: explicit `--resource <id>=<DBProxyEndpointName>` first; falls
18946
+ * back to paginated auto-lookup via `DescribeDBProxyEndpoints` +
18947
+ * `ListTagsForResource` matching `aws:cdk:path`.
18948
+ *
18949
+ * **physicalId** = DBProxyEndpointName (matches CFn `primaryIdentifier`).
18950
+ */
18951
+ var RDSDBProxyEndpointProvider = class {
18952
+ rdsClient;
18953
+ providerRegion = process.env["AWS_REGION"];
18954
+ logger = getLogger().child("RDSDBProxyEndpointProvider");
18955
+ attributeCache = /* @__PURE__ */ new Map();
18956
+ handledProperties = new Map([["AWS::RDS::DBProxyEndpoint", new Set([
18957
+ "DBProxyEndpointName",
18958
+ "DBProxyName",
18959
+ "VpcSubnetIds",
18960
+ "VpcSecurityGroupIds",
18961
+ "TargetRole",
18962
+ "Tags"
18963
+ ])]]);
18964
+ getClient() {
18965
+ if (!this.rdsClient) this.rdsClient = new RDSClient(this.providerRegion ? { region: this.providerRegion } : {});
18966
+ return this.rdsClient;
18967
+ }
18968
+ async create(logicalId, resourceType, properties) {
18969
+ const dbProxyName = properties["DBProxyName"];
18970
+ if (!dbProxyName) throw new ProvisioningError(`DBProxyName is required for AWS::RDS::DBProxyEndpoint ${logicalId}`, resourceType, logicalId);
18971
+ const dbProxyEndpointName = properties["DBProxyEndpointName"] ?? generateResourceName(logicalId, { maxLength: 64 });
18972
+ const vpcSubnetIds = properties["VpcSubnetIds"];
18973
+ if (!vpcSubnetIds || vpcSubnetIds.length === 0) throw new ProvisioningError(`VpcSubnetIds (at least one) is required for AWS::RDS::DBProxyEndpoint ${logicalId}`, resourceType, logicalId);
18974
+ const client = this.getClient();
18975
+ this.logger.debug(`Creating DBProxyEndpoint ${dbProxyEndpointName} (proxy=${dbProxyName})`);
18976
+ try {
18977
+ await client.send(new CreateDBProxyEndpointCommand({
18978
+ DBProxyName: dbProxyName,
18979
+ DBProxyEndpointName: dbProxyEndpointName,
18980
+ VpcSubnetIds: vpcSubnetIds,
18981
+ VpcSecurityGroupIds: properties["VpcSecurityGroupIds"],
18982
+ TargetRole: properties["TargetRole"],
18983
+ Tags: this.toAwsTags(properties["Tags"])
18984
+ }));
18985
+ } catch (error) {
18986
+ throw this.wrapError(error, "CREATE", resourceType, logicalId, void 0);
18987
+ }
18988
+ let endpoint;
18989
+ let arn;
18990
+ let isDefault;
18991
+ let vpcId;
18992
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
18993
+ let status;
18994
+ while (Date.now() < deadline) {
18995
+ try {
18996
+ const ep = (await client.send(new DescribeDBProxyEndpointsCommand({
18997
+ DBProxyName: dbProxyName,
18998
+ DBProxyEndpointName: dbProxyEndpointName
18999
+ }))).DBProxyEndpoints?.[0];
19000
+ status = ep?.Status;
19001
+ if (status === "available") {
19002
+ endpoint = ep?.Endpoint;
19003
+ arn = ep?.DBProxyEndpointArn;
19004
+ isDefault = ep?.IsDefault;
19005
+ vpcId = ep?.VpcId;
19006
+ break;
19007
+ }
19008
+ if (status === "incompatible-network" || status === "insufficient-resource-limits") throw new ProvisioningError(`DBProxyEndpoint ${dbProxyEndpointName} entered terminal failure state: ${status}`, resourceType, logicalId, dbProxyEndpointName);
19009
+ } catch (error) {
19010
+ if (error instanceof DBProxyEndpointNotFoundFault || error instanceof DBProxyNotFoundFault) {} else if (error instanceof ProvisioningError) throw error;
19011
+ else throw this.wrapError(error, "CREATE (poll)", resourceType, logicalId, dbProxyEndpointName);
19012
+ }
19013
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
19014
+ }
19015
+ if (!endpoint || !arn) throw new ProvisioningError(`Timed out waiting for DBProxyEndpoint ${dbProxyEndpointName} to become available (last status: ${status ?? "unknown"})`, resourceType, logicalId, dbProxyEndpointName);
19016
+ return {
19017
+ physicalId: dbProxyEndpointName,
19018
+ attributes: {
19019
+ Endpoint: endpoint,
19020
+ DBProxyEndpointArn: arn,
19021
+ IsDefault: isDefault ?? false,
19022
+ VpcId: vpcId ?? ""
19023
+ }
19024
+ };
19025
+ }
19026
+ async update(logicalId, physicalId, resourceType, properties, previousProperties) {
19027
+ const client = this.getClient();
19028
+ for (const field of [
19029
+ "DBProxyName",
19030
+ "DBProxyEndpointName",
19031
+ "VpcSubnetIds",
19032
+ "TargetRole"
19033
+ ]) if (JSON.stringify(properties[field]) !== JSON.stringify(previousProperties[field])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `${field} is immutable on AWS::RDS::DBProxyEndpoint — destroy + redeploy to change it`);
19034
+ const oldSG = previousProperties["VpcSecurityGroupIds"] ?? [];
19035
+ const newSG = properties["VpcSecurityGroupIds"] ?? [];
19036
+ if (JSON.stringify(oldSG) !== JSON.stringify(newSG)) {
19037
+ this.logger.debug(`Updating DBProxyEndpoint ${physicalId} security groups`);
19038
+ try {
19039
+ await client.send(new ModifyDBProxyEndpointCommand({
19040
+ DBProxyEndpointName: physicalId,
19041
+ VpcSecurityGroupIds: newSG
19042
+ }));
19043
+ } catch (error) {
19044
+ throw this.wrapError(error, "UPDATE", resourceType, logicalId, physicalId);
19045
+ }
19046
+ }
19047
+ await this.applyTagDiff(physicalId, previousProperties["Tags"], properties["Tags"], resourceType, logicalId);
19048
+ this.invalidateAttributeCache(physicalId);
19049
+ return {
19050
+ physicalId,
19051
+ wasReplaced: false
19052
+ };
19053
+ }
19054
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
19055
+ const client = this.getClient();
19056
+ this.logger.debug(`Deleting DBProxyEndpoint ${physicalId}`);
19057
+ try {
19058
+ await client.send(new DeleteDBProxyEndpointCommand({ DBProxyEndpointName: physicalId }));
19059
+ } catch (error) {
19060
+ if (error instanceof DBProxyEndpointNotFoundFault || error instanceof DBProxyNotFoundFault) {
19061
+ assertRegionMatch(await client.config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
19062
+ this.logger.debug(`DBProxyEndpoint ${physicalId} or parent already gone, treating as success`);
19063
+ return;
19064
+ }
19065
+ throw this.wrapError(error, "DELETE", resourceType, logicalId, physicalId);
19066
+ }
19067
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
19068
+ while (Date.now() < deadline) {
19069
+ try {
19070
+ await client.send(new DescribeDBProxyEndpointsCommand({ DBProxyEndpointName: physicalId }));
19071
+ } catch (error) {
19072
+ if (error instanceof DBProxyEndpointNotFoundFault || error instanceof DBProxyNotFoundFault) {
19073
+ this.logger.debug(`DBProxyEndpoint ${physicalId} fully deleted`);
19074
+ return;
19075
+ }
19076
+ throw this.wrapError(error, "DELETE (poll)", resourceType, logicalId, physicalId);
19077
+ }
19078
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
19079
+ }
19080
+ throw new ProvisioningError(`Timed out waiting for DBProxyEndpoint ${physicalId} to fully delete`, resourceType, logicalId, physicalId);
19081
+ }
19082
+ async getAttribute(physicalId, _resourceType, attributeName) {
19083
+ const cacheKey = `${physicalId}:${attributeName}`;
19084
+ const cached = this.attributeCache.get(cacheKey);
19085
+ if (cached !== void 0) return cached;
19086
+ if (attributeName !== "Endpoint" && attributeName !== "DBProxyEndpointArn" && attributeName !== "IsDefault" && attributeName !== "VpcId") {
19087
+ this.logger.warn(`Unknown attribute ${attributeName} for AWS::RDS::DBProxyEndpoint, returning undefined`);
19088
+ return;
19089
+ }
19090
+ try {
19091
+ const ep = (await this.getClient().send(new DescribeDBProxyEndpointsCommand({ DBProxyEndpointName: physicalId }))).DBProxyEndpoints?.[0];
19092
+ if (!ep) return void 0;
19093
+ const value = {
19094
+ Endpoint: ep.Endpoint,
19095
+ DBProxyEndpointArn: ep.DBProxyEndpointArn,
19096
+ IsDefault: ep.IsDefault ?? false,
19097
+ VpcId: ep.VpcId
19098
+ }[attributeName];
19099
+ if (value !== void 0) this.attributeCache.set(cacheKey, value);
19100
+ return value;
19101
+ } catch (error) {
19102
+ if (error instanceof DBProxyEndpointNotFoundFault || error instanceof DBProxyNotFoundFault) return;
19103
+ throw error;
19104
+ }
19105
+ }
19106
+ async import(input) {
19107
+ const explicit = resolveExplicitPhysicalId(input, "DBProxyEndpointName");
19108
+ if (explicit) return this.buildImportResult(explicit);
19109
+ const client = this.getClient();
19110
+ let marker;
19111
+ do {
19112
+ const describe = await client.send(new DescribeDBProxyEndpointsCommand({
19113
+ Marker: marker,
19114
+ MaxRecords: 100
19115
+ }));
19116
+ for (const ep of describe.DBProxyEndpoints ?? []) {
19117
+ if (!ep.DBProxyEndpointArn) continue;
19118
+ try {
19119
+ if (matchesCdkPath((await client.send(new ListTagsForResourceCommand$8({ ResourceName: ep.DBProxyEndpointArn }))).TagList ?? [], input.cdkPath)) return this.buildImportResult(ep.DBProxyEndpointName ?? "");
19120
+ } catch (error) {
19121
+ this.logger.debug(`ListTagsForResource failed for ${ep.DBProxyEndpointName}: ${error instanceof Error ? error.message : String(error)}`);
19122
+ }
19123
+ }
19124
+ marker = describe.Marker;
19125
+ } while (marker);
19126
+ return null;
19127
+ }
19128
+ async readCurrentState(physicalId) {
19129
+ const client = this.getClient();
19130
+ let ep;
19131
+ try {
19132
+ ep = (await client.send(new DescribeDBProxyEndpointsCommand({ DBProxyEndpointName: physicalId }))).DBProxyEndpoints?.[0];
19133
+ if (!ep) return void 0;
19134
+ } catch (error) {
19135
+ if (error instanceof DBProxyEndpointNotFoundFault || error instanceof DBProxyNotFoundFault) return;
19136
+ throw error;
19137
+ }
19138
+ const e = ep;
19139
+ const result = {
19140
+ DBProxyEndpointName: e.DBProxyEndpointName,
19141
+ DBProxyName: e.DBProxyName,
19142
+ VpcSubnetIds: e.VpcSubnetIds ?? [],
19143
+ VpcSecurityGroupIds: e.VpcSecurityGroupIds ?? [],
19144
+ TargetRole: e.TargetRole ?? "READ_WRITE"
19145
+ };
19146
+ if (e.DBProxyEndpointArn) try {
19147
+ result["Tags"] = normalizeAwsTagsToCfn((await client.send(new ListTagsForResourceCommand$8({ ResourceName: e.DBProxyEndpointArn }))).TagList ?? []);
19148
+ } catch (error) {
19149
+ this.logger.debug(`ListTagsForResource failed for ${physicalId}: ${error instanceof Error ? error.message : String(error)}`);
19150
+ result["Tags"] = [];
19151
+ }
19152
+ else result["Tags"] = [];
19153
+ return result;
19154
+ }
19155
+ async applyTagDiff(physicalId, oldTags, newTags, resourceType, logicalId) {
19156
+ const oldMap = this.toTagMap(oldTags);
19157
+ const newMap = this.toTagMap(newTags);
19158
+ if (oldMap.size === newMap.size && [...oldMap.keys()].every((k) => newMap.has(k)) && [...oldMap.entries()].every(([k, v]) => newMap.get(k) === v)) return;
19159
+ const client = this.getClient();
19160
+ const arnCacheKey = `${physicalId}:DBProxyEndpointArn`;
19161
+ let arn = this.attributeCache.get(arnCacheKey);
19162
+ if (!arn) try {
19163
+ arn = (await client.send(new DescribeDBProxyEndpointsCommand({ DBProxyEndpointName: physicalId }))).DBProxyEndpoints?.[0]?.DBProxyEndpointArn;
19164
+ if (arn) this.attributeCache.set(arnCacheKey, arn);
19165
+ } catch (error) {
19166
+ this.logger.debug(`Skipping tag diff for ${physicalId} (no ARN): ${error instanceof Error ? error.message : String(error)}`);
19167
+ return;
19168
+ }
19169
+ if (!arn) return;
19170
+ const toRemove = [];
19171
+ const toAdd = [];
19172
+ for (const k of oldMap.keys()) if (!newMap.has(k)) toRemove.push(k);
19173
+ for (const [k, v] of newMap.entries()) if (oldMap.get(k) !== v) toAdd.push({
19174
+ Key: k,
19175
+ Value: v
19176
+ });
19177
+ if (toRemove.length > 0) try {
19178
+ await client.send(new RemoveTagsFromResourceCommand$1({
19179
+ ResourceName: arn,
19180
+ TagKeys: toRemove
19181
+ }));
19182
+ } catch (error) {
19183
+ throw this.wrapError(error, "UPDATE (remove tags)", resourceType, logicalId, physicalId);
19184
+ }
19185
+ if (toAdd.length > 0) try {
19186
+ await client.send(new AddTagsToResourceCommand$1({
19187
+ ResourceName: arn,
19188
+ Tags: toAdd
19189
+ }));
19190
+ } catch (error) {
19191
+ throw this.wrapError(error, "UPDATE (add tags)", resourceType, logicalId, physicalId);
19192
+ }
19193
+ }
19194
+ toTagMap(tags) {
19195
+ const map = /* @__PURE__ */ new Map();
19196
+ if (Array.isArray(tags)) {
19197
+ for (const entry of tags) if (entry?.Key !== void 0) map.set(entry.Key, entry.Value ?? "");
19198
+ }
19199
+ return map;
19200
+ }
19201
+ toAwsTags(tags) {
19202
+ if (!Array.isArray(tags) || tags.length === 0) return void 0;
19203
+ return tags.filter((t) => t.Key !== void 0).map((t) => ({
19204
+ Key: t.Key,
19205
+ Value: t.Value ?? ""
19206
+ }));
19207
+ }
19208
+ async buildImportResult(physicalId) {
19209
+ try {
19210
+ const ep = (await this.getClient().send(new DescribeDBProxyEndpointsCommand({ DBProxyEndpointName: physicalId }))).DBProxyEndpoints?.[0];
19211
+ return {
19212
+ physicalId,
19213
+ attributes: {
19214
+ Endpoint: ep?.Endpoint ?? "",
19215
+ DBProxyEndpointArn: ep?.DBProxyEndpointArn ?? "",
19216
+ IsDefault: ep?.IsDefault ?? false,
19217
+ VpcId: ep?.VpcId ?? ""
19218
+ }
19219
+ };
19220
+ } catch {
19221
+ return {
19222
+ physicalId,
19223
+ attributes: {
19224
+ Endpoint: "",
19225
+ DBProxyEndpointArn: "",
19226
+ IsDefault: false,
19227
+ VpcId: ""
19228
+ }
19229
+ };
19230
+ }
19231
+ }
19232
+ invalidateAttributeCache(physicalId) {
19233
+ for (const key of this.attributeCache.keys()) if (key.startsWith(`${physicalId}:`)) this.attributeCache.delete(key);
19234
+ }
19235
+ wrapError(error, op, resourceType, logicalId, physicalId) {
19236
+ const message = error instanceof Error ? error.message : String(error);
19237
+ const cause = error instanceof Error ? error : void 0;
19238
+ return new ProvisioningError(`${op} failed for ${logicalId}: ${message}`, resourceType, logicalId, physicalId, cause);
19239
+ }
19240
+ };
19241
+
18745
19242
  //#endregion
18746
19243
  //#region src/provisioning/providers/rds-dbproxy-targetgroup-provider.ts
18747
19244
  /**
@@ -18861,6 +19358,12 @@ var RDSDBProxyTargetGroupProvider = class {
18861
19358
  const dbProxyName = properties["DBProxyName"];
18862
19359
  if (!dbProxyName) throw new ProvisioningError(`DBProxyName is required for AWS::RDS::DBProxyTargetGroup ${logicalId} update`, resourceType, logicalId, physicalId);
18863
19360
  const targetGroupName = properties["TargetGroupName"] ?? "default";
19361
+ for (const field of ["DBProxyName", "TargetGroupName"]) {
19362
+ const oldVal = previousProperties[field];
19363
+ const newVal = properties[field];
19364
+ const normalize = (v) => field === "TargetGroupName" && (v === void 0 || v === "default") ? "default" : v;
19365
+ if (JSON.stringify(normalize(oldVal)) !== JSON.stringify(normalize(newVal))) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `${field} is immutable on AWS::RDS::DBProxyTargetGroup — destroy + redeploy to change it`);
19366
+ }
18864
19367
  const client = this.getClient();
18865
19368
  const oldPool = previousProperties["ConnectionPoolConfigurationInfo"];
18866
19369
  const newPool = properties["ConnectionPoolConfigurationInfo"];
@@ -29703,6 +30206,7 @@ function registerAllProviders(registry) {
29703
30206
  registry.register("AWS::RDS::DBCluster", rdsProvider);
29704
30207
  registry.register("AWS::RDS::DBInstance", rdsProvider);
29705
30208
  registry.register("AWS::RDS::DBProxy", new RDSDBProxyProvider());
30209
+ registry.register("AWS::RDS::DBProxyEndpoint", new RDSDBProxyEndpointProvider());
29706
30210
  registry.register("AWS::RDS::DBProxyTargetGroup", new RDSDBProxyTargetGroupProvider());
29707
30211
  const docdbProvider = new DocDBProvider();
29708
30212
  registry.register("AWS::DocDB::DBSubnetGroup", docdbProvider);
@@ -44683,7 +45187,7 @@ function reorderArgs(argv) {
44683
45187
  */
44684
45188
  async function main() {
44685
45189
  const program = new Command();
44686
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.107.0");
45190
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.109.0");
44687
45191
  program.addCommand(createBootstrapCommand());
44688
45192
  program.addCommand(createSynthCommand());
44689
45193
  program.addCommand(createListCommand());