@go-to-k/cdkd 0.284.24 → 0.284.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as disableInstanceApiTermination, $n as NestedStackChildDirectDestroyError, $t as ensureAssetStorage, A as isStatefulRecreateTargetSync, An as uploadCfnTemplate, At as s3BucketWebsiteUrl, B as collectPublishedOutputNames, Bn as getAwsClients, Bt as rebuildClientForBucketRegion, C as createPreDeleteFinalSnapshot, Cn as resolveUseCdkBootstrapAssets, Ct as maskSecretsInText, D as makeCanonicalizePropertiesFn, Dn as CFN_TEMPLATE_URL_LIMIT, Dt as s3BucketDomainName, E as unsupportedFinalSnapshotError, En as CFN_TEMPLATE_BODY_LIMIT, Et as s3BucketArn, F as gray, Fn as AssemblyReader, Ft as withRetry, G as IAMRoleProvider, Gt as buildAssetRedirectMap, H as isExportAliasCollision, Hn as setAwsClients, Ht as AssetPublisher, I as green, In as processStackMessages, It as DagBuilder, J as ProviderRegistry, Jn as LocalInvokeBuildError$1, Jt as rewriteTemplateAssetReferences, K as collectInlinePolicyNamesManagedBySiblings, Kt as createAssetRedirectResolver, L as red, Lt as TemplateParser, M as formatResourceLine, Mn as PARTITION_TABLE, Mt as DiffCalculator, N as bold, Nn as canonicalizeRegion, Nt as INTRINSIC_KEYS, O as extractDeploymentEventError, On as MIGRATE_TMP_PREFIX, Ot as s3BucketDualStackDomainName, P as cyan, Pn as derivePartitionAndUrlSuffix, Pt as describeTypeWithThrottleRetry, Q as slowCcOperationTimeoutMs, Qn as MissingCdkCliError, Qt as BOOTSTRAP_MARKER_PREFIX, R as yellow, Rn as resolveBucketRegion, Rt as LockManager, S as ccRoutedFinalSnapshotError, Sn as resolveStateBucketWithDefaultAndSource, St as isSingleDynamicReferenceToken, T as refusesFinalSnapshot, Tn as warnDeprecatedNoPrefixCliFlag, Tt as scrubResourceRecord, U as secretBearingStateKeyWarning, Ut as stringifyValue, V as exportAliasCollisionScrubWarning, Vn as resetAwsClients, Vt as shouldRetainResource, W as stateKeySecretExposure, Wn as CdkdError, Wt as WorkGraph, X as findSilentDropProperties, Xn as LocalStartServiceError, Xt as stripControlChars, Y as findActionableSilentDrops, Yn as LocalMigrateError, Yt as escapeRegExp, Z as CloudControlProvider, Zt as AssetModeResolver, _ as IMPLICIT_DELETE_DEPENDENCIES, _n as resolveApp, _t as STATE_SOURCED_CROSS_GENERATION_RULES, a as DeploymentEventsStore, an as buildDenyExternalAccessPolicy, ar as StackTerminationProtectionError, at as WAFv2WebACLProvider, b as PRE_DELETE_SNAPSHOT_TYPES, bn as resolveSkipPrefix, bt as createSecretMasker, c as producerRegionsFromState, cn as getDockerCmd, ct as assertRegionMatch, d as updatePartialMessage, dn as AssetManifestLoader, dr as withErrorHandling, dt as configStringRefusal, en as getBootstrapMarkerKey, er as PartialFailureError, et as isTerminationProtectionPropagationError, f as updatePartialReason, fn as getDockerImageBySourceHash, fr as isMarkedNonRetryable, ft as readConfigString, g as maskingRetryLogger, gn as getLegacyStateBucketName, gt as requireConfigString, h as withResourceDeadline, hn as getDefaultStateBucketName, hr as markNonRetryable, ht as requireConfigObject, i as DeploymentEventsReader, in as validateContainerRepoName, ir as StackHasActiveImportsError, it as refStateLookupFromResource, j as renderStatefulReason, jn as expectedOwnerParam, jt as applyRoleArnIfSet, k as MULTI_REGION_RECREATE_BLOCKED_TYPES, kn as findLargeInlineResources, kt as s3BucketRegionalDomainName, l as replayFailedOperations, ln as runDockerForeground, lt as coerceCfnBoolean, m as deleteSkipReason, mn as synthesisStatusMessage, mr as isThrottlingError, mt as requireConfigArray, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as readBootstrapMarkerBody, nr as ResourceTimeoutError, nt as cfnRefValueFromPhysicalId, o as planFailedOps, on as buildDockerImage, ot as normalizeAwsTagsToCfn, p as UNSPECIFIED_SKIP_REASON, pn as Synthesizer, pr as isRetryableTransientError, pt as replayWarn, q as clearOnUpdateRemoval, qn as DeployCancelledError, qt as loadPublishableAssetManifest, r as DeployEngine, rn as validateAssetBucketName, rr as ResourceUpdateNotSupportedError, rt as getAccountInfo, s as planRollback, sn as formatDockerLoginError, st as resolveExplicitPhysicalId, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as parseBootstrapMarker, tr as ProvisioningError, tt as IntrinsicFunctionResolver, u as replayRollback, un as runDockerStreaming, ur as normalizeAwsError, ut as configBooleanRefusal, v as computeImplicitDeleteEdges, vn as resolveAutoAssetStorage, vt as STATE_SOURCED_READBACK_RULES, w as isFinalSnapshotError, wn as stateBucketExistenceConfirmed, wt as redactSecretsForState, x as buildFinalSnapshotIdentifier, xn as resolveStateBucketWithDefault, xt as dynamicReferenceTokens, y as ATOMIC_FINAL_SNAPSHOT_TYPES, yn as resolveCaptureObservedState, yt as TEMPLATE_SOURCED_RULES, z as collectDeclaredOutputNames, zn as AwsClients, zt as S3StateBackend } from "./deploy-engine-BJQbni1s.js";
2
+ import { $ as isInterruptedWaitError, $n as LocalMigrateError, $t as escapeRegExp, A as isStatefulRecreateTargetSync, An as CFN_TEMPLATE_BODY_LIMIT, At as s3BucketArn, B as collectPublishedOutputNames, Bn as processStackMessages, Bt as DagBuilder, C as createPreDeleteFinalSnapshot, Cn as resolveCaptureObservedState, Ct as TEMPLATE_SOURCED_RULES, D as makeCanonicalizePropertiesFn, Dn as resolveUseCdkBootstrapAssets, Dt as maskSecretsInText, E as unsupportedFinalSnapshotError, En as resolveStateBucketWithDefaultAndSource, Et as isSingleDynamicReferenceToken, F as gray, Fn as expectedOwnerParam, Ft as applyRoleArnIfSet, G as IAMRoleProvider, Gn as resetAwsClients, Gt as shouldRetainResource, H as isExportAliasCollision, Hn as resolveBucketRegion, Ht as LockManager, I as green, In as PARTITION_TABLE, It as DiffCalculator, J as ProviderRegistry, Jn as CdkdError, Jt as WorkGraph, K as collectInlinePolicyNamesManagedBySiblings, Kn as setAwsClients, Kt as AssetPublisher, L as red, Ln as canonicalizeRegion, Lt as INTRINSIC_KEYS, M as formatResourceLine, Mn as MIGRATE_TMP_PREFIX, Mt as s3BucketDualStackDomainName, N as bold, Nn as findLargeInlineResources, Nt as s3BucketRegionalDomainName, O as extractDeploymentEventError, On as stateBucketExistenceConfirmed, Ot as redactSecretsForState, P as cyan, Pn as uploadCfnTemplate, Pt as s3BucketWebsiteUrl, Q as endCommandInterruptScope, Qn as LocalInvokeBuildError$1, Qt as rewriteTemplateAssetReferences, R as yellow, Rn as derivePartitionAndUrlSuffix, Rt as describeTypeWithThrottleRetry, S as ccRoutedFinalSnapshotError, Sn as resolveAutoAssetStorage, St as STATE_SOURCED_READBACK_RULES, T as refusesFinalSnapshot, Tn as resolveStateBucketWithDefault, Tt as dynamicReferenceTokens, U as secretBearingStateKeyWarning, Un as AwsClients, Ut as S3StateBackend, V as exportAliasCollisionScrubWarning, Vt as TemplateParser, W as stateKeySecretExposure, Wn as getAwsClients, Wt as rebuildClientForBucketRegion, X as findSilentDropProperties, Xt as createAssetRedirectResolver, Y as findActionableSilentDrops, Yt as buildAssetRedirectMap, Z as beginCommandInterruptScope, Zn as DeployCancelledError, Zt as loadPublishableAssetManifest, _ as IMPLICIT_DELETE_DEPENDENCIES, _n as Synthesizer, _r as isRetryableTransientError, _t as replayWarn, a as DeploymentEventsStore, an as parseBootstrapMarker, ar as ProvisioningError, at as IntrinsicFunctionResolver, b as PRE_DELETE_SNAPSHOT_TYPES, bn as getLegacyStateBucketName, bt as requireConfigString, c as producerRegionsFromState, cn as validateContainerRepoName, cr as StackHasActiveImportsError, ct as refStateLookupFromResource, d as updatePartialMessage, dn as formatDockerLoginError, dt as resolveExplicitPhysicalId, en as stripControlChars, er as LocalStartServiceError, et as startInterruptWatch, f as updatePartialReason, fn as getDockerCmd, ft as assertRegionMatch, g as maskingRetryLogger, gn as getDockerImageBySourceHash, gr as isMarkedNonRetryable, gt as readConfigString, h as withResourceDeadline, hn as AssetManifestLoader, hr as withErrorHandling, ht as configStringRefusal, i as DeploymentEventsReader, in as getBootstrapMarkerKey, ir as PartialFailureError, it as isTerminationProtectionPropagationError, j as renderStatefulReason, jn as CFN_TEMPLATE_URL_LIMIT, jt as s3BucketDomainName, k as MULTI_REGION_RECREATE_BLOCKED_TYPES, kn as warnDeprecatedNoPrefixCliFlag, kt as scrubResourceRecord, l as replayFailedOperations, ln as buildDenyExternalAccessPolicy, lr as StackTerminationProtectionError, lt as WAFv2WebACLProvider, m as deleteSkipReason, mn as runDockerStreaming, mr as normalizeAwsError, mt as configBooleanRefusal, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as BOOTSTRAP_MARKER_PREFIX, nr as MissingCdkCliError, nt as slowCcOperationTimeoutMs, o as planFailedOps, on as readBootstrapMarkerBody, or as ResourceTimeoutError, ot as cfnRefValueFromPhysicalId, p as UNSPECIFIED_SKIP_REASON, pn as runDockerForeground, pt as coerceCfnBoolean, q as clearOnUpdateRemoval, qt as stringifyValue, r as DeployEngine, rn as ensureAssetStorage, rr as NestedStackChildDirectDestroyError, rt as disableInstanceApiTermination, s as planRollback, sn as validateAssetBucketName, sr as ResourceUpdateNotSupportedError, st as getAccountInfo, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as AssetModeResolver, tt as CloudControlProvider, u as replayRollback, un as buildDockerImage, ut as normalizeAwsTagsToCfn, v as computeImplicitDeleteEdges, vn as synthesisStatusMessage, vr as isThrottlingError, vt as requireConfigArray, w as isFinalSnapshotError, wn as resolveSkipPrefix, wt as createSecretMasker, x as buildFinalSnapshotIdentifier, xn as resolveApp, xt as STATE_SOURCED_CROSS_GENERATION_RULES, y as ATOMIC_FINAL_SNAPSHOT_TYPES, yn as getDefaultStateBucketName, yr as markNonRetryable, yt as requireConfigObject, z as collectDeclaredOutputNames, zn as AssemblyReader, zt as withRetry } from "./deploy-engine-BpZWPiaL.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as getCurrentStackName, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
4
- import { t as ASGProvider } from "./asg-provider-Bh0odlSk.js";
4
+ import { t as ASGProvider } from "./asg-provider-B2lPfJBN.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
7
7
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, 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";
@@ -1774,6 +1774,7 @@ function createListCommand() {
1774
1774
  * handler of its own).
1775
1775
  */
1776
1776
  function forwardSigtermToSigint() {
1777
+ beginCommandInterruptScope();
1777
1778
  const handler = () => {
1778
1779
  if (process.listenerCount("SIGINT") === 0) process.exit(143);
1779
1780
  process.emit("SIGINT", "SIGINT");
@@ -1781,6 +1782,7 @@ function forwardSigtermToSigint() {
1781
1782
  process.on("SIGTERM", handler);
1782
1783
  return () => {
1783
1784
  process.removeListener("SIGTERM", handler);
1785
+ endCommandInterruptScope();
1784
1786
  };
1785
1787
  }
1786
1788
 
@@ -14839,28 +14841,37 @@ const DELETE_INDEX_WAIT_PROCEED_NOTE = `the DeleteTable goes ahead anyway and AW
14839
14841
  async function waitForIndexesSettled(opts) {
14840
14842
  const { tableName, logicalId, logger, describeTable, maxAttempts, proceedNote } = opts;
14841
14843
  const budgetWait = opts.budgetWait;
14844
+ const watch = startInterruptWatch(`index settle wait on ${tableName}`);
14842
14845
  let attempt = 0;
14843
- while (budgetWait ? budgetWait.nextPoll() : attempt < maxAttempts) {
14844
- attempt += 1;
14845
- try {
14846
- if (!hasTransitionalIndex((await describeTable()).Table?.GlobalSecondaryIndexes ?? [])) return;
14847
- } catch (err) {
14848
- const message = err instanceof Error ? err.message : String(err);
14849
- if (isThrottlingError(err)) {
14850
- logger.debug(`DescribeTable throttled while waiting for indexes on ${tableName} (attempt ${attempt}/${maxAttempts}); still waiting: ${message}`);
14851
- await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
14852
- continue;
14846
+ try {
14847
+ while (budgetWait ? budgetWait.nextPoll() : attempt < maxAttempts) {
14848
+ attempt += 1;
14849
+ if (watch.isInterrupted()) {
14850
+ logger.debug(`Interrupted while waiting for indexes on ${tableName} (${logicalId}) after ${attempt - 1} DescribeTable polls; ${proceedNote}`);
14851
+ return;
14853
14852
  }
14854
- if (err instanceof ResourceNotFoundException$1) {
14855
- logger.debug(`Table ${tableName} (${logicalId}) no longer exists while waiting for indexes`);
14853
+ try {
14854
+ if (!hasTransitionalIndex((await describeTable()).Table?.GlobalSecondaryIndexes ?? [])) return;
14855
+ } catch (err) {
14856
+ const message = err instanceof Error ? err.message : String(err);
14857
+ if (isThrottlingError(err)) {
14858
+ logger.debug(`DescribeTable throttled while waiting for indexes on ${tableName} (attempt ${attempt}/${maxAttempts}); still waiting: ${message}`);
14859
+ await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
14860
+ continue;
14861
+ }
14862
+ if (err instanceof ResourceNotFoundException$1) {
14863
+ logger.debug(`Table ${tableName} (${logicalId}) no longer exists while waiting for indexes`);
14864
+ return;
14865
+ }
14866
+ const errorName = err instanceof Error ? err.name : typeof err;
14867
+ logger.debug(`DescribeTable failed while waiting for indexes on ${tableName} (${logicalId}): ${message}`);
14868
+ logger.warn(`DescribeTable failed (${errorName}) while waiting for indexes on ${tableName} (${logicalId}), so cdkd stopped waiting; ${proceedNote} Re-run with --verbose for AWS's own message.`);
14856
14869
  return;
14857
14870
  }
14858
- const errorName = err instanceof Error ? err.name : typeof err;
14859
- logger.debug(`DescribeTable failed while waiting for indexes on ${tableName} (${logicalId}): ${message}`);
14860
- logger.warn(`DescribeTable failed (${errorName}) while waiting for indexes on ${tableName} (${logicalId}), so cdkd stopped waiting; ${proceedNote} Re-run with --verbose for AWS's own message.`);
14861
- return;
14871
+ await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
14862
14872
  }
14863
- await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
14873
+ } finally {
14874
+ watch.dispose();
14864
14875
  }
14865
14876
  const pollSeconds = INDEX_SETTLE_POLL_INTERVAL_MS / 1e3;
14866
14877
  const pollsRun = budgetWait ? budgetWait.pollsRun : maxAttempts;
@@ -14965,24 +14976,31 @@ function indexBusyRetryWarning(opts) {
14965
14976
  */
14966
14977
  async function deleteTableWithIndexBusyRetry(opts) {
14967
14978
  let deleteAttempts = 0;
14968
- await withRetry(async () => {
14969
- if (deleteAttempts++ > 0) {
14970
- if (deleteAttempts === 2) opts.logger.warn(indexBusyRetryWarning({
14971
- typeLabel: opts.typeLabel,
14972
- logicalId: opts.logicalId,
14973
- physicalId: opts.physicalId,
14974
- attemptNumber: deleteAttempts,
14975
- maxRetries: opts.maxRetries
14976
- }));
14977
- await opts.reArm();
14978
- }
14979
- await opts.deleteTable();
14980
- }, opts.logicalId, {
14981
- maxRetries: opts.maxRetries,
14982
- isRetryable: (message) => isIndexBusyDeleteError(message) && (opts.shouldKeepRetrying?.() ?? true),
14983
- logger: opts.logger,
14984
- sleep: (ms) => opts.sleepSeam.sleep ? opts.sleepSeam.sleep(ms) : new Promise((resolve) => setTimeout(resolve, ms))
14985
- });
14979
+ const watch = startInterruptWatch(`DynamoDB ${opts.typeLabel} ${opts.logicalId} delete`);
14980
+ try {
14981
+ await withRetry(async () => {
14982
+ if (deleteAttempts++ > 0) {
14983
+ if (deleteAttempts === 2) opts.logger.warn(indexBusyRetryWarning({
14984
+ typeLabel: opts.typeLabel,
14985
+ logicalId: opts.logicalId,
14986
+ physicalId: opts.physicalId,
14987
+ attemptNumber: deleteAttempts,
14988
+ maxRetries: opts.maxRetries
14989
+ }));
14990
+ await opts.reArm();
14991
+ }
14992
+ await opts.deleteTable();
14993
+ }, opts.logicalId, {
14994
+ maxRetries: opts.maxRetries,
14995
+ isRetryable: (message) => isIndexBusyDeleteError(message) && (opts.shouldKeepRetrying?.() ?? true),
14996
+ logger: opts.logger,
14997
+ isInterrupted: watch.isInterrupted,
14998
+ onInterrupted: watch.onInterrupted,
14999
+ sleep: (ms) => opts.sleepSeam.sleep ? opts.sleepSeam.sleep(ms) : new Promise((resolve) => setTimeout(resolve, ms))
15000
+ });
15001
+ } finally {
15002
+ watch.dispose();
15003
+ }
14986
15004
  }
14987
15005
 
14988
15006
  //#endregion
@@ -18658,94 +18676,111 @@ var DynamoDBGlobalTableProvider = class {
18658
18676
  const oldEnabled = oldSettings !== void 0 && oldSettings !== null;
18659
18677
  const newEnabled = newSettings !== void 0 && newSettings !== null;
18660
18678
  if (!oldEnabled && !newEnabled) return;
18661
- if (newEnabled) {
18662
- const minCapacity = Number(newSettings["MinCapacity"] ?? 0);
18663
- const maxCapacity = Number(newSettings["MaxCapacity"] ?? 0);
18664
- if (!Number.isFinite(minCapacity) || !Number.isFinite(maxCapacity)) {
18665
- warn(`Cannot apply auto-scaling diff on ${tableName} (${dimension}): MinCapacity / MaxCapacity must be numbers, got ${maskSecrets(String(newSettings["MinCapacity"]))} / ${maskSecrets(String(newSettings["MaxCapacity"]))}`);
18679
+ const watch = startInterruptWatch(`DynamoDB auto-scaling for ${tableName}`);
18680
+ try {
18681
+ if (newEnabled) {
18682
+ const minCapacity = Number(newSettings["MinCapacity"] ?? 0);
18683
+ const maxCapacity = Number(newSettings["MaxCapacity"] ?? 0);
18684
+ if (!Number.isFinite(minCapacity) || !Number.isFinite(maxCapacity)) {
18685
+ warn(`Cannot apply auto-scaling diff on ${tableName} (${dimension}): MinCapacity / MaxCapacity must be numbers, got ${maskSecrets(String(newSettings["MinCapacity"]))} / ${maskSecrets(String(newSettings["MaxCapacity"]))}`);
18686
+ return;
18687
+ }
18688
+ try {
18689
+ await withRetry(() => asClient.send(new RegisterScalableTargetCommand({
18690
+ ServiceNamespace: "dynamodb",
18691
+ ResourceId: resourceId,
18692
+ ScalableDimension: dimension,
18693
+ MinCapacity: minCapacity,
18694
+ MaxCapacity: maxCapacity
18695
+ })), `${resourceId} (${dimension})`, {
18696
+ isRetryable: (_message, error) => isThrottlingError(error),
18697
+ maxRetries: asMaxRetries,
18698
+ isInterrupted: watch.isInterrupted,
18699
+ onInterrupted: watch.onInterrupted,
18700
+ ...autoScalingRetryDelays.sleep ? { sleep: autoScalingRetryDelays.sleep } : {}
18701
+ });
18702
+ } catch (err) {
18703
+ if (isInterruptedWaitError(err)) throw err;
18704
+ 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}`);
18705
+ return;
18706
+ }
18707
+ const tttCfg = newSettings["TargetTrackingScalingPolicyConfiguration"] ?? {};
18708
+ const targetValue = Number(tttCfg["TargetValue"]);
18709
+ if (!Number.isFinite(targetValue)) {
18710
+ warn(`Auto-scaling target registered on ${tableName} (${dimension}) but TargetValue is missing or non-numeric — skipping PutScalingPolicy. Provide TargetTrackingScalingPolicyConfiguration.TargetValue in the template.`);
18711
+ return;
18712
+ }
18713
+ const targetTrackingConfig = {
18714
+ PredefinedMetricSpecification: { PredefinedMetricType: metricType },
18715
+ TargetValue: targetValue
18716
+ };
18717
+ if (tttCfg["ScaleInCooldown"] !== void 0) targetTrackingConfig["ScaleInCooldown"] = Number(tttCfg["ScaleInCooldown"]);
18718
+ if (tttCfg["ScaleOutCooldown"] !== void 0) targetTrackingConfig["ScaleOutCooldown"] = Number(tttCfg["ScaleOutCooldown"]);
18719
+ if (tttCfg["DisableScaleIn"] !== void 0) targetTrackingConfig["DisableScaleIn"] = Boolean(tttCfg["DisableScaleIn"]);
18720
+ try {
18721
+ await withRetry(() => asClient.send(new PutScalingPolicyCommand({
18722
+ PolicyName: policyName,
18723
+ ServiceNamespace: "dynamodb",
18724
+ ResourceId: resourceId,
18725
+ ScalableDimension: dimension,
18726
+ PolicyType: "TargetTrackingScaling",
18727
+ TargetTrackingScalingPolicyConfiguration: targetTrackingConfig
18728
+ })), `${policyName}`, {
18729
+ isRetryable: (_message, error) => isThrottlingError(error),
18730
+ maxRetries: asMaxRetries,
18731
+ isInterrupted: watch.isInterrupted,
18732
+ onInterrupted: watch.onInterrupted,
18733
+ ...autoScalingRetryDelays.sleep ? { sleep: autoScalingRetryDelays.sleep } : {}
18734
+ });
18735
+ this.logger.debug(`Upserted auto-scaling policy ${policyName} on ${tableName} (${dimension})`);
18736
+ } catch (err) {
18737
+ if (isInterruptedWaitError(err)) throw err;
18738
+ 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`);
18739
+ }
18666
18740
  return;
18667
18741
  }
18742
+ const isObjectNotFound = (err) => {
18743
+ if (!(err instanceof Error)) return false;
18744
+ const name = err.name ?? "";
18745
+ const msg = err.message ?? "";
18746
+ return name === "ObjectNotFoundException" || msg.includes("No scaling policy found") || msg.includes("No scalable target found");
18747
+ };
18668
18748
  try {
18669
- await withRetry(() => asClient.send(new RegisterScalableTargetCommand({
18749
+ await withRetry(() => asClient.send(new DeleteScalingPolicyCommand({
18750
+ PolicyName: policyName,
18670
18751
  ServiceNamespace: "dynamodb",
18671
18752
  ResourceId: resourceId,
18672
- ScalableDimension: dimension,
18673
- MinCapacity: minCapacity,
18674
- MaxCapacity: maxCapacity
18753
+ ScalableDimension: dimension
18675
18754
  })), `${resourceId} (${dimension})`, {
18676
18755
  isRetryable: (_message, error) => isThrottlingError(error),
18677
18756
  maxRetries: asMaxRetries,
18757
+ isInterrupted: watch.isInterrupted,
18758
+ onInterrupted: watch.onInterrupted,
18678
18759
  ...autoScalingRetryDelays.sleep ? { sleep: autoScalingRetryDelays.sleep } : {}
18679
18760
  });
18680
18761
  } catch (err) {
18681
- 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}`);
18682
- return;
18683
- }
18684
- const tttCfg = newSettings["TargetTrackingScalingPolicyConfiguration"] ?? {};
18685
- const targetValue = Number(tttCfg["TargetValue"]);
18686
- if (!Number.isFinite(targetValue)) {
18687
- warn(`Auto-scaling target registered on ${tableName} (${dimension}) but TargetValue is missing or non-numeric — skipping PutScalingPolicy. Provide TargetTrackingScalingPolicyConfiguration.TargetValue in the template.`);
18688
- return;
18762
+ if (isInterruptedWaitError(err)) throw err;
18763
+ if (!isObjectNotFound(err)) 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}`);
18689
18764
  }
18690
- const targetTrackingConfig = {
18691
- PredefinedMetricSpecification: { PredefinedMetricType: metricType },
18692
- TargetValue: targetValue
18693
- };
18694
- if (tttCfg["ScaleInCooldown"] !== void 0) targetTrackingConfig["ScaleInCooldown"] = Number(tttCfg["ScaleInCooldown"]);
18695
- if (tttCfg["ScaleOutCooldown"] !== void 0) targetTrackingConfig["ScaleOutCooldown"] = Number(tttCfg["ScaleOutCooldown"]);
18696
- if (tttCfg["DisableScaleIn"] !== void 0) targetTrackingConfig["DisableScaleIn"] = Boolean(tttCfg["DisableScaleIn"]);
18697
18765
  try {
18698
- await withRetry(() => asClient.send(new PutScalingPolicyCommand({
18699
- PolicyName: policyName,
18766
+ await withRetry(() => asClient.send(new DeregisterScalableTargetCommand({
18700
18767
  ServiceNamespace: "dynamodb",
18701
18768
  ResourceId: resourceId,
18702
- ScalableDimension: dimension,
18703
- PolicyType: "TargetTrackingScaling",
18704
- TargetTrackingScalingPolicyConfiguration: targetTrackingConfig
18705
- })), `${policyName}`, {
18769
+ ScalableDimension: dimension
18770
+ })), `${resourceId} (${dimension})`, {
18706
18771
  isRetryable: (_message, error) => isThrottlingError(error),
18707
18772
  maxRetries: asMaxRetries,
18773
+ isInterrupted: watch.isInterrupted,
18774
+ onInterrupted: watch.onInterrupted,
18708
18775
  ...autoScalingRetryDelays.sleep ? { sleep: autoScalingRetryDelays.sleep } : {}
18709
18776
  });
18710
- this.logger.debug(`Upserted auto-scaling policy ${policyName} on ${tableName} (${dimension})`);
18777
+ this.logger.debug(`Deregistered auto-scaling target ${resourceId} (${dimension})`);
18711
18778
  } catch (err) {
18712
- 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`);
18779
+ if (isInterruptedWaitError(err)) throw err;
18780
+ if (!isObjectNotFound(err)) 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}`);
18713
18781
  }
18714
- return;
18715
- }
18716
- const isObjectNotFound = (err) => {
18717
- if (!(err instanceof Error)) return false;
18718
- const name = err.name ?? "";
18719
- const msg = err.message ?? "";
18720
- return name === "ObjectNotFoundException" || msg.includes("No scaling policy found") || msg.includes("No scalable target found");
18721
- };
18722
- try {
18723
- await withRetry(() => asClient.send(new DeleteScalingPolicyCommand({
18724
- PolicyName: policyName,
18725
- ServiceNamespace: "dynamodb",
18726
- ResourceId: resourceId,
18727
- ScalableDimension: dimension
18728
- })), `${resourceId} (${dimension})`, {
18729
- isRetryable: (_message, error) => isThrottlingError(error),
18730
- maxRetries: asMaxRetries,
18731
- ...autoScalingRetryDelays.sleep ? { sleep: autoScalingRetryDelays.sleep } : {}
18732
- });
18733
- } catch (err) {
18734
- if (!isObjectNotFound(err)) 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}`);
18735
- }
18736
- try {
18737
- await withRetry(() => asClient.send(new DeregisterScalableTargetCommand({
18738
- ServiceNamespace: "dynamodb",
18739
- ResourceId: resourceId,
18740
- ScalableDimension: dimension
18741
- })), `${resourceId} (${dimension})`, {
18742
- isRetryable: (_message, error) => isThrottlingError(error),
18743
- maxRetries: asMaxRetries,
18744
- ...autoScalingRetryDelays.sleep ? { sleep: autoScalingRetryDelays.sleep } : {}
18745
- });
18746
- this.logger.debug(`Deregistered auto-scaling target ${resourceId} (${dimension})`);
18747
- } catch (err) {
18748
- if (!isObjectNotFound(err)) 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}`);
18782
+ } finally {
18783
+ watch.dispose();
18749
18784
  }
18750
18785
  }
18751
18786
  /**
@@ -18943,6 +18978,7 @@ var DynamoDBGlobalTableProvider = class {
18943
18978
  }
18944
18979
  } catch (describeErr) {
18945
18980
  if (describeErr instanceof ProvisioningError) throw describeErr;
18981
+ if (isInterruptedWaitError(describeErr)) throw describeErr;
18946
18982
  if (!(describeErr instanceof ResourceNotFoundException$1)) {
18947
18983
  const cause = describeErr instanceof Error ? describeErr : void 0;
18948
18984
  throw new ProvisioningError(`Failed to describe DynamoDB GlobalTable ${logicalId} before delete: ${describeErr instanceof Error ? describeErr.message : String(describeErr)}`, resourceType, logicalId, physicalId, cause);
@@ -19654,12 +19690,20 @@ var DynamoDBGlobalTableProvider = class {
19654
19690
  */
19655
19691
  async waitForReplicaGone(tableName, region, logicalId, maxAttempts = REPLICA_GONE_WAIT_ATTEMPTS, budget) {
19656
19692
  const wait = beginDeleteWait(maxAttempts, budget);
19657
- while (wait.nextPoll()) try {
19658
- if (!(await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.Replicas?.find((r) => r.RegionName === region)) return;
19659
- await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
19660
- } catch (err) {
19661
- if (err instanceof ResourceNotFoundException$1) return;
19662
- throw err;
19693
+ const watch = startInterruptWatch(`DynamoDB replica ${region} teardown on ${tableName}`);
19694
+ try {
19695
+ while (wait.nextPoll()) {
19696
+ if (watch.isInterrupted()) throw watch.onInterrupted();
19697
+ try {
19698
+ if (!(await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.Replicas?.find((r) => r.RegionName === region)) return;
19699
+ await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
19700
+ } catch (err) {
19701
+ if (err instanceof ResourceNotFoundException$1) return;
19702
+ throw err;
19703
+ }
19704
+ }
19705
+ } finally {
19706
+ watch.dispose();
19663
19707
  }
19664
19708
  throw new ProvisioningError(`Replica ${region} for table ${tableName} did not disappear within ${wait.pollsRun}s${wait.note()}`, "AWS::DynamoDB::GlobalTable", logicalId, tableName);
19665
19709
  }
@@ -19679,12 +19723,23 @@ var DynamoDBGlobalTableProvider = class {
19679
19723
  */
19680
19724
  async waitForTableGone(tableName, logicalId, maxAttempts = 600, budget) {
19681
19725
  const wait = beginDeleteWait(maxAttempts, budget);
19682
- while (wait.nextPoll()) try {
19683
- await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }));
19684
- await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
19685
- } catch (err) {
19686
- if (err instanceof ResourceNotFoundException$1) return;
19687
- throw err;
19726
+ const watch = startInterruptWatch(`DynamoDB table ${tableName} deletion wait`);
19727
+ try {
19728
+ while (wait.nextPoll()) {
19729
+ if (watch.isInterrupted()) {
19730
+ this.logger.warn(`DynamoDB GlobalTable ${logicalId}: interrupted while waiting for ${tableName} to disappear. AWS ACCEPTED the DeleteTable and will finish it; cdkd stopped watching.`);
19731
+ return;
19732
+ }
19733
+ try {
19734
+ await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }));
19735
+ await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
19736
+ } catch (err) {
19737
+ if (err instanceof ResourceNotFoundException$1) return;
19738
+ throw err;
19739
+ }
19740
+ }
19741
+ } finally {
19742
+ watch.dispose();
19688
19743
  }
19689
19744
  if (wait.cutShort) {
19690
19745
  this.logger.warn(`DynamoDB GlobalTable ${logicalId}: DeleteTable on ${tableName} was ACCEPTED by AWS but cdkd stopped waiting for the table to disappear${wait.note()} The delete completes on AWS's side; a later describe (or a re-run of this destroy) sees it gone.`);
@@ -35399,13 +35454,23 @@ var ELBv2Provider = class {
35399
35454
  try {
35400
35455
  const listenerAttributes = this.normalizeAttributes(properties["ListenerAttributes"]);
35401
35456
  if (listenerAttributes.length > 0) {
35402
- await withRetry(() => this.getClient().send(new ModifyListenerAttributesCommand({
35403
- ListenerArn: listenerArn,
35404
- Attributes: listenerAttributes
35405
- })), logicalId, { logger: this.maskedRetryLogger(maskSecrets) });
35457
+ const watch = startInterruptWatch(`ELBv2 Listener ${logicalId} attributes`);
35458
+ try {
35459
+ await withRetry(() => this.getClient().send(new ModifyListenerAttributesCommand({
35460
+ ListenerArn: listenerArn,
35461
+ Attributes: listenerAttributes
35462
+ })), logicalId, {
35463
+ logger: this.maskedRetryLogger(maskSecrets),
35464
+ isInterrupted: watch.isInterrupted,
35465
+ onInterrupted: watch.onInterrupted
35466
+ });
35467
+ } finally {
35468
+ watch.dispose();
35469
+ }
35406
35470
  this.logger.debug(`Applied ${listenerAttributes.length} ListenerAttribute(s) for ${logicalId}`);
35407
35471
  }
35408
35472
  } catch (innerError) {
35473
+ if (isInterruptedWaitError(innerError)) this.logger.warn(maskerOrIdentity(maskSecrets)(`Interrupted after creating Listener ${logicalId} (${listenerArn}) but before its attributes were applied. Nothing in cdkd state refers to it, so cdkd is deleting it now — left behind it would fail the next deploy with DuplicateListener and neither rollback nor destroy could reach it. If that delete does not complete, remove it manually: aws elbv2 delete-listener --listener-arn ${listenerArn}`));
35409
35474
  try {
35410
35475
  await this.getClient().send(new DeleteListenerCommand({ ListenerArn: listenerArn }));
35411
35476
  this.logger.debug(`Cleaned up partially-created Listener ${logicalId} (${listenerArn}) after attributes-wiring failure`);
@@ -35442,10 +35507,19 @@ var ELBv2Provider = class {
35442
35507
  }));
35443
35508
  const submittedAttrs = this.diffAttributes(this.normalizeAttributes(properties["ListenerAttributes"]), this.normalizeAttributes(previousProperties["ListenerAttributes"]), this.attributeRemovalResolver(LISTENER_ATTRIBUTE_DEFAULTS));
35444
35509
  if (submittedAttrs.length > 0) {
35445
- await withRetry(() => this.getClient().send(new ModifyListenerAttributesCommand({
35446
- ListenerArn: physicalId,
35447
- Attributes: submittedAttrs
35448
- })), logicalId, { logger: this.maskedRetryLogger(maskSecrets) });
35510
+ const watch = startInterruptWatch(`ELBv2 Listener ${logicalId} attributes`);
35511
+ try {
35512
+ await withRetry(() => this.getClient().send(new ModifyListenerAttributesCommand({
35513
+ ListenerArn: physicalId,
35514
+ Attributes: submittedAttrs
35515
+ })), logicalId, {
35516
+ logger: this.maskedRetryLogger(maskSecrets),
35517
+ isInterrupted: watch.isInterrupted,
35518
+ onInterrupted: watch.onInterrupted
35519
+ });
35520
+ } finally {
35521
+ watch.dispose();
35522
+ }
35449
35523
  this.logger.debug(`Applied ${submittedAttrs.length} ListenerAttributes change(s) for ${logicalId}`);
35450
35524
  }
35451
35525
  await this.applyTagDiff(physicalId, previousProperties["Tags"], properties["Tags"]);
@@ -42539,16 +42613,23 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
42539
42613
  this.logger.debug(`Deleting Cloud Map namespace ${logicalId}: ${physicalId}`);
42540
42614
  const client = this.getClient();
42541
42615
  try {
42542
- await withRetry(async () => {
42543
- const operationId = (await client.send(new DeleteNamespaceCommand({ Id: physicalId }))).OperationId;
42544
- if (operationId) await this.pollOperation(operationId, logicalId, resourceType);
42545
- }, logicalId, {
42546
- maxRetries: 24,
42547
- initialDelayMs: 3e3,
42548
- maxDelayMs: 15e3,
42549
- isRetryable: (message, error) => error instanceof ResourceInUse || /has associated services/i.test(message),
42550
- logger: this.logger
42551
- });
42616
+ const watch = startInterruptWatch(`Cloud Map namespace ${logicalId}`);
42617
+ try {
42618
+ await withRetry(async () => {
42619
+ const operationId = (await client.send(new DeleteNamespaceCommand({ Id: physicalId }))).OperationId;
42620
+ if (operationId) await this.pollOperation(operationId, logicalId, resourceType);
42621
+ }, logicalId, {
42622
+ maxRetries: 24,
42623
+ initialDelayMs: 3e3,
42624
+ maxDelayMs: 15e3,
42625
+ isRetryable: (message, error) => error instanceof ResourceInUse || /has associated services/i.test(message),
42626
+ logger: this.logger,
42627
+ isInterrupted: watch.isInterrupted,
42628
+ onInterrupted: watch.onInterrupted
42629
+ });
42630
+ } finally {
42631
+ watch.dispose();
42632
+ }
42552
42633
  this.logger.debug(`Successfully deleted Cloud Map namespace ${logicalId}`);
42553
42634
  } catch (error) {
42554
42635
  if (error instanceof NamespaceNotFound) {
@@ -42748,13 +42829,23 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
42748
42829
  try {
42749
42830
  const attrs = this.normalizeServiceAttributes(properties["ServiceAttributes"]);
42750
42831
  if (Object.keys(attrs).length > 0) {
42751
- await withRetry(() => client.send(new UpdateServiceAttributesCommand({
42752
- ServiceId: serviceId,
42753
- Attributes: attrs
42754
- })), logicalId, { logger: this.maskedRetryLogger(maskSecrets) });
42832
+ const watch = startInterruptWatch(`Cloud Map service ${logicalId} attributes`);
42833
+ try {
42834
+ await withRetry(() => client.send(new UpdateServiceAttributesCommand({
42835
+ ServiceId: serviceId,
42836
+ Attributes: attrs
42837
+ })), logicalId, {
42838
+ logger: this.maskedRetryLogger(maskSecrets),
42839
+ isInterrupted: watch.isInterrupted,
42840
+ onInterrupted: watch.onInterrupted
42841
+ });
42842
+ } finally {
42843
+ watch.dispose();
42844
+ }
42755
42845
  this.logger.debug(`Applied ${Object.keys(attrs).length} ServiceAttribute(s) for ${logicalId}`);
42756
42846
  }
42757
42847
  } catch (innerError) {
42848
+ if (isInterruptedWaitError(innerError)) this.logger.warn(maskerOrIdentity(maskSecrets)(`Interrupted after creating ServiceDiscovery Service ${logicalId} (${serviceId}) but before its ServiceAttributes were applied. Nothing in cdkd state refers to it, so cdkd is deleting it now — left behind it would fail the next deploy on a name collision AND block deletion of its namespace with ResourceInUse. If that delete does not complete, remove it manually: aws servicediscovery delete-service --id ${serviceId}`));
42758
42849
  try {
42759
42850
  await client.send(new DeleteServiceCommand$1({ Id: serviceId }));
42760
42851
  this.logger.debug(`Cleaned up partially-created ServiceDiscovery Service ${logicalId} (${serviceId}) after ServiceAttributes wiring failure`);
@@ -42824,19 +42915,34 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
42824
42915
  }))).OperationId;
42825
42916
  if (operationId) await this.pollOperation(operationId, logicalId, resourceType, maskSecrets);
42826
42917
  }
42827
- if (hasAttrUpsert) {
42828
- await withRetry(() => client.send(new UpdateServiceAttributesCommand({
42829
- ServiceId: physicalId,
42830
- Attributes: upsertAttrs
42831
- })), logicalId, { logger: this.maskedRetryLogger(maskSecrets) });
42832
- this.logger.debug(`Applied ${Object.keys(upsertAttrs).length} ServiceAttribute change(s) for ${logicalId}`);
42833
- }
42834
- if (hasAttrRemove) {
42835
- await withRetry(() => client.send(new DeleteServiceAttributesCommand({
42836
- ServiceId: physicalId,
42837
- Attributes: removedAttrKeys
42838
- })), logicalId, { logger: this.maskedRetryLogger(maskSecrets) });
42839
- this.logger.debug(`Removed ${removedAttrKeys.length} ServiceAttribute(s) for ${logicalId}`);
42918
+ if (hasAttrUpsert || hasAttrRemove) {
42919
+ const attrWatch = startInterruptWatch(`Cloud Map service ${logicalId} attributes`);
42920
+ try {
42921
+ if (hasAttrUpsert) {
42922
+ await withRetry(() => client.send(new UpdateServiceAttributesCommand({
42923
+ ServiceId: physicalId,
42924
+ Attributes: upsertAttrs
42925
+ })), logicalId, {
42926
+ logger: this.maskedRetryLogger(maskSecrets),
42927
+ isInterrupted: attrWatch.isInterrupted,
42928
+ onInterrupted: attrWatch.onInterrupted
42929
+ });
42930
+ this.logger.debug(`Applied ${Object.keys(upsertAttrs).length} ServiceAttribute change(s) for ${logicalId}`);
42931
+ }
42932
+ if (hasAttrRemove) {
42933
+ await withRetry(() => client.send(new DeleteServiceAttributesCommand({
42934
+ ServiceId: physicalId,
42935
+ Attributes: removedAttrKeys
42936
+ })), logicalId, {
42937
+ logger: this.maskedRetryLogger(maskSecrets),
42938
+ isInterrupted: attrWatch.isInterrupted,
42939
+ onInterrupted: attrWatch.onInterrupted
42940
+ });
42941
+ this.logger.debug(`Removed ${removedAttrKeys.length} ServiceAttribute(s) for ${logicalId}`);
42942
+ }
42943
+ } finally {
42944
+ attrWatch.dispose();
42945
+ }
42840
42946
  }
42841
42947
  this.logger.debug(`Successfully updated service discovery service ${logicalId}`);
42842
42948
  return {
@@ -58198,7 +58304,7 @@ async function runDestroyForStack(stackName, state, ctx) {
58198
58304
  } catch (error) {
58199
58305
  renderer.removeTask(logicalId);
58200
58306
  const msg = error instanceof Error ? error.message : String(error);
58201
- if (!isFinalSnapshotError(error) && (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException"))) {
58307
+ if (!isInterruptedWaitError(error) && !isFinalSnapshotError(error) && (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException"))) {
58202
58308
  logger.debug(` ${logicalId} already deleted, removing from state`);
58203
58309
  result.deletedCount++;
58204
58310
  ctx.eventRecorder?.record({
@@ -58282,11 +58388,19 @@ async function runDestroyForStack(stackName, state, ctx) {
58282
58388
  logger.warn(`\n${yellow("⚠")} ${bold(`Stack ${stackName} partially destroyed`)} (${green(result.deletedCount)} deleted${retainedSuffix}${skippedSuffix}, ${red(result.errorCount)} errors). State preserved — re-run 'cdkd destroy' / 'cdkd state destroy' to clean up. If the same resource keeps failing, ${orphanHint} is the last resort: it removes the state record without deleting AWS resources.` + skippedClause);
58283
58389
  }
58284
58390
  } finally {
58285
- process.removeListener("SIGINT", sigintHandler);
58286
- renderer.stop();
58287
- await saveChain;
58288
- logger.debug("Releasing lock...");
58289
- await ctx.lockManager.releaseLock(stackName, regionForState);
58391
+ try {
58392
+ try {
58393
+ renderer.stop();
58394
+ } catch (rendererError) {
58395
+ logger.debug(`Live renderer teardown failed (continuing to release the lock): ${rendererError instanceof Error ? rendererError.message : String(rendererError)}`);
58396
+ }
58397
+ await saveChain;
58398
+ logger.debug("Releasing lock...");
58399
+ await ctx.lockManager.releaseLock(stackName, regionForState);
58400
+ } finally {
58401
+ process.removeListener("SIGINT", sigintHandler);
58402
+ }
58403
+ result.interrupted ||= draining;
58290
58404
  if (destroyAwsClients) {
58291
58405
  destroyAwsClients.destroy();
58292
58406
  process.env["AWS_REGION"] = ctx.baseRegion;
@@ -83914,7 +84028,7 @@ function createMigrateCommand() {
83914
84028
  */
83915
84029
  function buildProgram() {
83916
84030
  const program = new Command();
83917
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.24");
84031
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.25");
83918
84032
  program.hook("preAction", (_thisCommand, actionCommand) => {
83919
84033
  const { profile } = actionCommand.optsWithGlobals();
83920
84034
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;