@go-to-k/cdkd 0.284.70 → 0.284.72

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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Cr as LockError, Dr as ProvisioningError, Fr as isCdkdError, G as IAMRoleProvider, Ht as TemplateParser, Ir as normalizeAwsError, J as ProviderRegistry, Ln as Synthesizer, Lt as DiffCalculator, Mr as StateError, Nr as SynthesisError, Pr as formatError, Vt as DagBuilder, _r as DependencyError, ar as AssemblyReader, cn as AssetPublisher, cr as resolveBucketRegion, dr as resetAwsClients, en as LockManager, fr as setAwsClients, hr as ConfigError, lr as AwsClients, mr as CdkdError, nn as S3StateBackend, pr as AssetError, r as DeployEngine, sr as clearBucketRegionCache, st as CloudControlProvider, ur as getAwsClients } from "./deploy-engine-D4VFWSQr.js";
1
+ import { Cr as LockError, Dr as ProvisioningError, Fr as isCdkdError, G as IAMRoleProvider, Ht as TemplateParser, Ir as normalizeAwsError, J as ProviderRegistry, Ln as Synthesizer, Lt as DiffCalculator, Mr as StateError, Nr as SynthesisError, Pr as formatError, Vt as DagBuilder, _r as DependencyError, ar as AssemblyReader, cn as AssetPublisher, cr as resolveBucketRegion, dr as resetAwsClients, en as LockManager, fr as setAwsClients, hr as ConfigError, lr as AwsClients, mr as CdkdError, nn as S3StateBackend, pr as AssetError, r as DeployEngine, sr as clearBucketRegionCache, st as CloudControlProvider, ur as getAwsClients } from "./deploy-engine-Bc0FBOuJ.js";
2
2
  import { i as setLogger, n as getLogger, t as ConsoleLogger } from "./logger-C9-E73FI.js";
3
3
 
4
4
  export { AssemblyReader, AssetError, AssetPublisher, AwsClients, CdkdError, CloudControlProvider, ConfigError, ConsoleLogger, DagBuilder, DependencyError, DeployEngine, DiffCalculator, IAMRoleProvider, LockError, LockManager, ProviderRegistry, ProvisioningError, S3StateBackend, StateError, SynthesisError, Synthesizer, TemplateParser, clearBucketRegionCache, formatError, getAwsClients, getLogger, isCdkdError, normalizeAwsError, resetAwsClients, resolveBucketRegion, setAwsClients, setLogger };
@@ -1,7 +1,7 @@
1
- import { $ as maskerOrIdentity, $n as findLargeInlineResources, $t as scrubResourceRecord, A as renderStatefulReason, An as formatDockerLoginError, Ar as StackHasActiveImportsError, At as producerRegionsFromState, B as exportAliasCollisionScrubWarning, Bn as getLegacyStateBucketName, Br as isThrottlingError, Bt as withRetry, C as isFinalSnapshotError, Cn as parseBootstrapMarker, Ct as configStringRefusal, D as extractDeploymentEventError, Dn as buildDenyExternalAccessPolicy, Dr as ProvisioningError, Dt as requireConfigObject, E as makeCanonicalizePropertiesFn, En as validateContainerRepoName, Er as PartialFailureError, Et as requireConfigArray, F as green, Fn as AssetManifestLoader, Ft as s3BucketWebsiteUrl, G as IAMRoleProvider, Gn as resolveStateBucketWithDefault, Gt as TEMPLATE_SOURCED_RULES, H as secretBearingStateKeyWarning, Hn as resolveAutoAssetStorage, Ht as TemplateParser, I as red, In as getDockerImageBySourceHash, Ir as normalizeAwsError, It as applyRoleArnIfSet, J as ProviderRegistry, Jn as stateBucketExistenceConfirmed, Jt as errorCauseChain, K as collectInlinePolicyNamesManagedBySiblings, Kn as resolveStateBucketWithDefaultAndSource, Kt as createSecretMasker, L as yellow, Ln as Synthesizer, Lr as withErrorHandling, Lt as DiffCalculator, M as bold, Mn as partitionSensitiveEnv, Mt as s3BucketDomainName, N as cyan, Nn as runDockerForeground, Nt as s3BucketDualStackDomainName, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as buildDockerImage, Or as ResourceTimeoutError, Ot as requireConfigString, P as gray, Pn as runDockerStreaming, Pt as s3BucketRegionalDomainName, Q as maskDeep, Qn as MIGRATE_TMP_PREFIX, Qt as redactSecretsForState, R as collectDeclaredOutputNames, Rn as synthesisStatusMessage, Rr as isMarkedNonRetryable, Rt as INTRINSIC_KEYS, S as createPreDeleteFinalSnapshot, Sn as isCrossRegionRedirect, Sr as LocalStartServiceError, St as configBooleanRefusal, T as unsupportedFinalSnapshotError, Tn as validateAssetBucketName, Tr as NestedStackChildDirectDestroyError, Tt as replayWarn, U as stateKeySecretExposure, Un as resolveCaptureObservedState, Ut as STATE_SOURCED_CROSS_GENERATION_RULES, V as isExportAliasCollision, Vn as resolveApp, Vr as markNonRetryable, Vt as DagBuilder, W as getCurrentResourceSecrets, Wn as resolveSkipPrefix, Wt as STATE_SOURCED_READBACK_RULES, X as findSilentDropProperties, Xn as CFN_TEMPLATE_BODY_LIMIT, Xt as maskSecretsInError, Y as findActionableSilentDrops, Yn as warnDeprecatedNoPrefixCliFlag, Yt as isSingleDynamicReferenceToken, Z as createMaskedRetryLogger, Zn as CFN_TEMPLATE_URL_LIMIT, Zt as maskSecretsInText, _ as computeImplicitDeleteEdges, _n as AssetModeResolver, _t as WAFv2WebACLProvider, a as DeploymentEventsStore, an as importableOutputKeys, ar as AssemblyReader, at as isInterruptedWaitError, b as buildFinalSnapshotIdentifier, bn as ensureAssetStorage, br as LocalInvokeBuildError$1, bt as assertRegionMatch, c as replayFailedOperations, cn as AssetPublisher, cr as resolveBucketRegion, ct as slowCcOperationTimeoutMs, d as updatePartialReason, dn as buildAssetRedirectMap, dr as resetAwsClients, dt as IntrinsicFunctionResolver, en as LockManager, er as uploadCfnTemplate, et as CUSTOM_RESOURCE_RESPONSE_PREFIX, f as UNSPECIFIED_SKIP_REASON, fn as createAssetRedirectResolver, fr as setAwsClients, ft as carriesDynamicReference, g as IMPLICIT_DELETE_DEPENDENCIES, gn as stripControlChars, gr as CrossAccountSecretRefusalError, gt as refStateLookupFromResource, h as maskingRetryLogger, hn as escapeRegExp, ht as parameterTypeMayLoseSecretIdentity, i as DeploymentEventsReader, in as exportNamesCarriedFrom, ir as derivePartitionAndUrlSuffix, it as interruptWatchListenerCount, j as formatResourceLine, jn as getDockerCmd, jr as StackTerminationProtectionError, jt as s3BucketArn, k as isStatefulRecreateTargetSync, kn as dockerSpawnEnvWithSensitive, kr as ResourceUpdateNotSupportedError, kt as classifyReplaySecretRegion, l as replayRollback, ln as stringifyValue, lr as AwsClients, lt as disableInstanceApiTermination, m as withResourceDeadline, mn as rewriteTemplateAssetReferences, mr as CdkdError, mt as getAccountInfo, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as S3StateBackend, nr as PARTITION_TABLE, nt as beginCommandInterruptScope, o as planFailedOps, on as importableOutputs, or as processStackMessages, ot as startInterruptWatch, p as deleteSkipReason, pn as loadPublishableAssetManifest, pt as cfnRefValueFromPhysicalId, q as clearOnUpdateRemoval, qn as resolveUseCdkBootstrapAssets, qt as dynamicReferenceTokens, r as DeployEngine, rn as rebuildClientForBucketRegion, rr as canonicalizeRegion, rt as endCommandInterruptScope, s as planRollback, sn as shouldRetainResource, st as CloudControlProvider, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as displaySafe, tr as expectedOwnerParam, tt as DEFAULT_STATE_PREFIX, u as updatePartialMessage, un as WorkGraph, ur as getAwsClients, ut as isTerminationProtectionPropagationError, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as BOOTSTRAP_MARKER_PREFIX, vr as DeployCancelledError, vt as normalizeAwsTagsToCfn, w as refusesFinalSnapshot, wn as readBootstrapMarkerBody, wr as MissingCdkCliError, wt as readConfigString, x as ccRoutedFinalSnapshotError, xn as getBootstrapMarkerKey, xr as LocalMigrateError, xt as coerceCfnBoolean, y as PRE_DELETE_SNAPSHOT_TYPES, yn as assertAssetBucketRegion, yr as DynamicReferenceRegionAmbiguousError, yt as resolveExplicitPhysicalId, z as collectPublishedOutputNames, zn as getDefaultStateBucketName, zr as isRetryableTransientError, zt as describeTypeWithThrottleRetry } from "./deploy-engine-D4VFWSQr.js";
1
+ import { $ as maskerOrIdentity, $n as findLargeInlineResources, $t as scrubResourceRecord, A as renderStatefulReason, An as formatDockerLoginError, Ar as StackHasActiveImportsError, At as producerRegionsFromState, B as exportAliasCollisionScrubWarning, Bn as getLegacyStateBucketName, Br as isThrottlingError, Bt as withRetry, C as isFinalSnapshotError, Cn as parseBootstrapMarker, Ct as configStringRefusal, D as extractDeploymentEventError, Dn as buildDenyExternalAccessPolicy, Dr as ProvisioningError, Dt as requireConfigObject, E as makeCanonicalizePropertiesFn, En as validateContainerRepoName, Er as PartialFailureError, Et as requireConfigArray, F as green, Fn as AssetManifestLoader, Ft as s3BucketWebsiteUrl, G as IAMRoleProvider, Gn as resolveStateBucketWithDefault, Gt as TEMPLATE_SOURCED_RULES, H as secretBearingStateKeyWarning, Hn as resolveAutoAssetStorage, Ht as TemplateParser, I as red, In as getDockerImageBySourceHash, Ir as normalizeAwsError, It as applyRoleArnIfSet, J as ProviderRegistry, Jn as stateBucketExistenceConfirmed, Jt as errorCauseChain, K as collectInlinePolicyNamesManagedBySiblings, Kn as resolveStateBucketWithDefaultAndSource, Kt as createSecretMasker, L as yellow, Ln as Synthesizer, Lr as withErrorHandling, Lt as DiffCalculator, M as bold, Mn as partitionSensitiveEnv, Mt as s3BucketDomainName, N as cyan, Nn as runDockerForeground, Nt as s3BucketDualStackDomainName, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as buildDockerImage, Or as ResourceTimeoutError, Ot as requireConfigString, P as gray, Pn as runDockerStreaming, Pt as s3BucketRegionalDomainName, Q as maskDeep, Qn as MIGRATE_TMP_PREFIX, Qt as redactSecretsForState, R as collectDeclaredOutputNames, Rn as synthesisStatusMessage, Rr as isMarkedNonRetryable, Rt as INTRINSIC_KEYS, S as createPreDeleteFinalSnapshot, Sn as isCrossRegionRedirect, Sr as LocalStartServiceError, St as configBooleanRefusal, T as unsupportedFinalSnapshotError, Tn as validateAssetBucketName, Tr as NestedStackChildDirectDestroyError, Tt as replayWarn, U as stateKeySecretExposure, Un as resolveCaptureObservedState, Ut as STATE_SOURCED_CROSS_GENERATION_RULES, V as isExportAliasCollision, Vn as resolveApp, Vr as markNonRetryable, Vt as DagBuilder, W as getCurrentResourceSecrets, Wn as resolveSkipPrefix, Wt as STATE_SOURCED_READBACK_RULES, X as findSilentDropProperties, Xn as CFN_TEMPLATE_BODY_LIMIT, Xt as maskSecretsInError, Y as findActionableSilentDrops, Yn as warnDeprecatedNoPrefixCliFlag, Yt as isSingleDynamicReferenceToken, Z as createMaskedRetryLogger, Zn as CFN_TEMPLATE_URL_LIMIT, Zt as maskSecretsInText, _ as computeImplicitDeleteEdges, _n as AssetModeResolver, _t as WAFv2WebACLProvider, a as DeploymentEventsStore, an as importableOutputKeys, ar as AssemblyReader, at as isInterruptedWaitError, b as buildFinalSnapshotIdentifier, bn as ensureAssetStorage, br as LocalInvokeBuildError$1, bt as assertRegionMatch, c as replayFailedOperations, cn as AssetPublisher, cr as resolveBucketRegion, ct as slowCcOperationTimeoutMs, d as updatePartialReason, dn as buildAssetRedirectMap, dr as resetAwsClients, dt as IntrinsicFunctionResolver, en as LockManager, er as uploadCfnTemplate, et as CUSTOM_RESOURCE_RESPONSE_PREFIX, f as UNSPECIFIED_SKIP_REASON, fn as createAssetRedirectResolver, fr as setAwsClients, ft as carriesDynamicReference, g as IMPLICIT_DELETE_DEPENDENCIES, gn as stripControlChars, gr as CrossAccountSecretRefusalError, gt as refStateLookupFromResource, h as maskingRetryLogger, hn as escapeRegExp, ht as parameterTypeMayLoseSecretIdentity, i as DeploymentEventsReader, in as exportNamesCarriedFrom, ir as derivePartitionAndUrlSuffix, it as interruptWatchListenerCount, j as formatResourceLine, jn as getDockerCmd, jr as StackTerminationProtectionError, jt as s3BucketArn, k as isStatefulRecreateTargetSync, kn as dockerSpawnEnvWithSensitive, kr as ResourceUpdateNotSupportedError, kt as classifyReplaySecretRegion, l as replayRollback, ln as stringifyValue, lr as AwsClients, lt as disableInstanceApiTermination, m as withResourceDeadline, mn as rewriteTemplateAssetReferences, mr as CdkdError, mt as getAccountInfo, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as S3StateBackend, nr as PARTITION_TABLE, nt as beginCommandInterruptScope, o as planFailedOps, on as importableOutputs, or as processStackMessages, ot as startInterruptWatch, p as deleteSkipReason, pn as loadPublishableAssetManifest, pt as cfnRefValueFromPhysicalId, q as clearOnUpdateRemoval, qn as resolveUseCdkBootstrapAssets, qt as dynamicReferenceTokens, r as DeployEngine, rn as rebuildClientForBucketRegion, rr as canonicalizeRegion, rt as endCommandInterruptScope, s as planRollback, sn as shouldRetainResource, st as CloudControlProvider, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as displaySafe, tr as expectedOwnerParam, tt as DEFAULT_STATE_PREFIX, u as updatePartialMessage, un as WorkGraph, ur as getAwsClients, ut as isTerminationProtectionPropagationError, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as BOOTSTRAP_MARKER_PREFIX, vr as DeployCancelledError, vt as normalizeAwsTagsToCfn, w as refusesFinalSnapshot, wn as readBootstrapMarkerBody, wr as MissingCdkCliError, wt as readConfigString, x as ccRoutedFinalSnapshotError, xn as getBootstrapMarkerKey, xr as LocalMigrateError, xt as coerceCfnBoolean, y as PRE_DELETE_SNAPSHOT_TYPES, yn as assertAssetBucketRegion, yr as DynamicReferenceRegionAmbiguousError, yt as resolveExplicitPhysicalId, z as collectPublishedOutputNames, zn as getDefaultStateBucketName, zr as isRetryableTransientError, zt as describeTypeWithThrottleRetry } from "./deploy-engine-Bc0FBOuJ.js";
2
2
  import { a as runStackBuffered, c as PATTERN_B_NAME_PROPERTIES, d as generateResourceName, f as generateResourceNameWithFallback, g as withStackName, h as withSkipPrefix, l as PATTERN_B_RESOURCE_TYPES, n as getLogger, o as getLiveRenderer, p as getCurrentStackName, r as reserveStdoutForPayload, s as PATTERN_B_NAME_OPTIONS } from "./logger-C9-E73FI.js";
3
- import { t as getCdkdVersion } from "./version-AKrmxFD7.js";
4
- import { t as ASGProvider } from "./asg-provider-Ag8vynRR.js";
3
+ import { t as getCdkdVersion } from "./version-DY_eLvXB.js";
4
+ import { t as ASGProvider } from "./asg-provider-DcAqfBUo.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";
@@ -6969,6 +6969,141 @@ function bucketLocationToRegion(constraint) {
6969
6969
  if (value === "eu") return "eu-west-1";
6970
6970
  return value;
6971
6971
  }
6972
+ /**
6973
+ * Whether an error from `GetBucketLocation` means "no bucket of that name".
6974
+ *
6975
+ * The test is the wire error CODE, which the SDK lifts onto `name`.
6976
+ *
6977
+ * An earlier revision of this comment said `GetBucketLocation` does NOT
6978
+ * deserialize its 404 into the modeled `NoSuchBucket` class "the way
6979
+ * `DeleteBucket`'s is", inferring that from `GetBucketLocationCommand.d.ts`
6980
+ * declaring only `@throws {@link S3ServiceException}`. That inference is
6981
+ * FALSE and was measured false on 2026-08-26 by feeding canned 404 XML through
6982
+ * the real client: both operations yield `ctor=NoSuchBucket`,
6983
+ * `name=NoSuchBucket`, `instanceof NoSuchBucket === true`, `status=404`.
6984
+ * `DeleteBucketCommand.d.ts` declares the same lone `S3ServiceException`, and
6985
+ * the deserializer resolves errors through a per-NAMESPACE schema registry
6986
+ * (`client-s3/dist-cjs/schemas/schemas_0.js`), not a per-operation one — so
6987
+ * the command's declared throw list never decided this. The PREDICATE was
6988
+ * right for the wrong reason; the reason is corrected rather than quietly
6989
+ * dropped, because the wrong one would have justified the wrong fixture in the
6990
+ * next provider that copied it.
6991
+ *
6992
+ * There is deliberately no `instanceof NoSuchBucket` arm. The generated class
6993
+ * assigns `name = 'NoSuchBucket'` on the instance
6994
+ * (`@aws-sdk/client-s3/dist-es/models/errors.js`), so every instance already
6995
+ * satisfies the check below: it would be an arm no case could ever make
6996
+ * decide, and a branch nothing can make decide is a branch nothing protects.
6997
+ *
6998
+ * A BARE HTTP 404 is deliberately NOT accepted, and this is the one place the
6999
+ * predicate is narrower than "the resource is missing". On the create path
7000
+ * `absent` is the answer that ENABLES the partial-create cleanup's
7001
+ * `DeleteBucket`, so anything that can 404 without S3 having said
7002
+ * `NoSuchBucket` — a corporate proxy, an `AWS_ENDPOINT_URL` override, an
7003
+ * S3-compatible gateway — would otherwise license a delete on the strength of
7004
+ * a response S3 never sent. Narrowing costs only that such a 404 becomes
7005
+ * `indeterminate`, and every caller's `indeterminate` branch is the
7006
+ * non-destructive one.
7007
+ *
7008
+ * Everything else — 403 from a bucket owned by another account, a throttle, a
7009
+ * network fault — is likewise NOT absence. Folding those into "absent"
7010
+ * is what would let a probe failure re-authorize the destructive branch each
7011
+ * caller below is guarding.
7012
+ */
7013
+ function isNoSuchBucketError(error) {
7014
+ return error?.name === "NoSuchBucket";
7015
+ }
7016
+ /**
7017
+ * The bucket's region as reported on an error's own `x-amz-bucket-region`
7018
+ * header, canonicalized — the free answer, requiring no second round trip.
7019
+ *
7020
+ * Shared by {@link S3BucketProvider.resolveOwnedBucketRegion} (reading the
7021
+ * `BucketAlreadyOwnedByYou` 409) and {@link S3BucketProvider.probeBucketRegion}
7022
+ * (reading a FAILED probe), so the header-then-canonicalize pair has one
7023
+ * spelling rather than two that can drift apart.
7024
+ */
7025
+ function regionFromErrorHeader(error) {
7026
+ const fromHeader = readBucketRegionHeader(error);
7027
+ return fromHeader ? canonicalizeRegion(fromHeader) : void 0;
7028
+ }
7029
+ /**
7030
+ * Whether this error's `x-amz-bucket-region` describes the BUCKET rather than
7031
+ * the endpoint that answered.
7032
+ *
7033
+ * The distinction is the whole value of the header, and reading it off any
7034
+ * failure throws it away. Only two failure shapes are S3 telling you where the
7035
+ * BUCKET is: the 301 cross-region redirect (`PermanentRedirect`) and
7036
+ * `AuthorizationHeaderMalformed`, which is the SigV4 region mismatch reporting
7037
+ * the region you should have signed for. Every other failure -- a 403 for a
7038
+ * bucket the caller cannot see, a throttle, a 404 -- is answered by the
7039
+ * endpoint that was ASKED, so its header names the region the QUESTION went to.
7040
+ * That is the same property that already orders the absence check first in
7041
+ * {@link S3BucketProvider.probeBucketRegion}; this gate applies it to the rest
7042
+ * of the failure space instead of to `NoSuchBucket` alone.
7043
+ *
7044
+ * Accepting an endpoint-region header as the bucket's answer inverted both
7045
+ * guards built on top of it: on delete a 403 whose header echoed the deploy's
7046
+ * own region compared EQUAL and returned silently, which is exactly the hostile
7047
+ * bucket-policy `Deny` population {@link
7048
+ * S3BucketProvider.announceUnverifiedBucketIdentity} exists for; on create it
7049
+ * promoted an unanswerable probe to "the bucket is already there", producing a
7050
+ * false ADOPTED-and-ACLs-reset warning over a genuinely new bucket and
7051
+ * suppressing the orphan cleanup with no warning at all.
7052
+ *
7053
+ * Matched on the wire error CODE alone, never on an HTTP STATUS. An earlier
7054
+ * revision also accepted a bare `httpStatusCode === 301`, which is the same
7055
+ * defect {@link isNoSuchBucketError} was narrowed to close: a status S3 never
7056
+ * NAMED is not S3 speaking. A proxy, an `AWS_ENDPOINT_URL` override or an
7057
+ * S3-compatible gateway answering 301 with an `x-amz-bucket-region` of the
7058
+ * deploy's own region would have made `probe.region` compare EQUAL, and the
7059
+ * delete guard would have returned silently -- the same silent pass the 403
7060
+ * arm above was removed for, arriving through the status instead of the header.
7061
+ * Unreachable against real S3, since the redirect always carries the code, so
7062
+ * dropping the arm costs nothing and the shape stops being expressible.
7063
+ *
7064
+ * The `BucketAlreadyOwnedByYou` 409 is a THIRD region-bearing shape (measured
7065
+ * for issue #2227) and is deliberately absent here: `GetBucketLocation` cannot
7066
+ * return it, and the create path reads it through
7067
+ * {@link S3BucketProvider.resolveOwnedBucketRegion}'s explicit error argument,
7068
+ * which never routes through this gate.
7069
+ */
7070
+ function carriesBucketRegionHeader(error) {
7071
+ const candidate = error;
7072
+ if (candidate?.name === "PermanentRedirect") return true;
7073
+ return candidate?.name === "AuthorizationHeaderMalformed";
7074
+ }
7075
+ /** The probe was issued and could not answer. */
7076
+ function probeFailedCause(errorName) {
7077
+ return {
7078
+ why: `the GetBucketLocation identity check failed (${errorName})`,
7079
+ remedy: "Re-run with --verbose for AWS's own message. Re-run the destroy once the check can succeed, or grant s3:GetBucketLocation on the bucket (a bucket policy can Deny it), to have the guard verify the target first."
7080
+ };
7081
+ }
7082
+ /**
7083
+ * No probe was issued, because the state record names no region to check
7084
+ * against.
7085
+ *
7086
+ * `DeleteContext.expectedRegion` is absent exactly for a record written before
7087
+ * the region-keyed state layout, and `destroy-runner` omits the field rather
7088
+ * than inventing one. That cohort is the one MOST likely to predate the issue
7089
+ * #2227 create-path guard, so it is the likeliest carrier of a foreign-region
7090
+ * physical id -- and until this cause existed it was the one population that
7091
+ * got no probe, no warning and no debug line, i.e. the highest-risk shape with
7092
+ * the quietest output.
7093
+ *
7094
+ * Warn only. Refusing here would strand every legacy destroy with no IAM
7095
+ * remedy at all -- strictly worse than the stranding the guard already declines
7096
+ * to impose -- and defaulting `expectedRegion` to the client region in
7097
+ * `destroy-runner` would fabricate an expectation the record never made AND
7098
+ * change `assertRegionMatch` on the `NoSuchBucket` path, a far wider blast
7099
+ * radius than this.
7100
+ */
7101
+ function noRecordedRegionCause() {
7102
+ return {
7103
+ why: `this state record carries no region, so there was nothing to check the bucket's location against`,
7104
+ remedy: "This record predates cdkd's region-keyed state layout. Re-deploy the stack to write a record that carries its region, or confirm the bucket's location yourself, to have the guard run on the next destroy."
7105
+ };
7106
+ }
6972
7107
  var S3BucketProvider = class S3BucketProvider {
6973
7108
  s3Client;
6974
7109
  logger = getLogger().child("S3BucketProvider");
@@ -8731,7 +8866,23 @@ var S3BucketProvider = class S3BucketProvider {
8731
8866
  const wantRegion = canonicalizeRegion(expectedRegion);
8732
8867
  const actualRegion = await this.resolveOwnedBucketRegion(bucketName, createError);
8733
8868
  if (actualRegion === wantRegion) return;
8734
- throw markNonRetryable(new ProvisioningError(`Refusing to adopt existing S3 bucket ${bucketName} for ${logicalId} (${resourceType}): it is owned by this account but lives in ${actualRegion}, while this stack deploys to ${wantRegion}. S3 bucket names are globally unique, so 'BucketAlreadyOwnedByYou' does not mean the bucket is in this region. Adopting it would apply this stack's bucket configuration to a bucket in ${actualRegion}, and would record a physical id that denotes no bucket in ${wantRegion}. Either give this stack a bucket name unique to ${wantRegion}, or deploy the stack to ${actualRegion}.`, resourceType, logicalId, bucketName, createError));
8869
+ this.refuseForeignRegionAdopt(logicalId, resourceType, bucketName, wantRegion, actualRegion, createError);
8870
+ }
8871
+ /**
8872
+ * The refusal {@link assertExistingBucketRegion} raises, as its own method so
8873
+ * the `us-east-1` pre-flight can raise the IDENTICAL one.
8874
+ *
8875
+ * Two callers reach the same conclusion by different evidence -- a 409's
8876
+ * `x-amz-bucket-region` on one side, a pre-flight `GetBucketLocation` on the
8877
+ * other -- and the user-facing consequence is the same in both, so the
8878
+ * wording, the `markNonRetryable` marker and the deliberate avoidance of the
8879
+ * literal `does not exist` are stated once rather than paraphrased.
8880
+ *
8881
+ * `never`-returning so a caller can `this.refuseForeignRegionAdopt(...)` as a
8882
+ * statement without the compiler losing that control flow ends there.
8883
+ */
8884
+ refuseForeignRegionAdopt(logicalId, resourceType, bucketName, wantRegion, actualRegion, cause) {
8885
+ throw markNonRetryable(new ProvisioningError(`Refusing to adopt existing S3 bucket ${bucketName} for ${logicalId} (${resourceType}): it is owned by this account but lives in ${actualRegion}, while this stack deploys to ${wantRegion}. S3 bucket names are globally unique, so owning the name (which is what 'BucketAlreadyOwnedByYou' and a successful location lookup each report) never means the bucket is in this region. Adopting it would apply this stack's bucket configuration to a bucket in ${actualRegion}, and would record a physical id that denotes no bucket in ${wantRegion}. Either give this stack a bucket name unique to ${wantRegion}, or deploy the stack to ${actualRegion}.`, resourceType, logicalId, bucketName, cause));
8735
8886
  }
8736
8887
  /**
8737
8888
  * The region an already-owned bucket actually lives in, canonicalized.
@@ -8740,13 +8891,197 @@ var S3BucketProvider = class S3BucketProvider {
8740
8891
  * failure PROPAGATES rather than degrading to a guess: the caller is a
8741
8892
  * fail-closed guard, and a guessed region is exactly what would make it
8742
8893
  * adopt the wrong bucket.
8743
- */
8744
- async resolveOwnedBucketRegion(bucketName, createError) {
8745
- const fromHeader = readBucketRegionHeader(createError);
8746
- if (fromHeader) return canonicalizeRegion(fromHeader);
8894
+ *
8895
+ * The header read here is deliberately NOT behind
8896
+ * {@link carriesBucketRegionHeader}, and that is licensed by the CALL SITE
8897
+ * rather than by the header being safer: the only error ever passed in is the
8898
+ * `BucketAlreadyOwnedByYou` 409 from `CreateBucket`, which is itself a
8899
+ * region-bearing shape (measured for issue #2227 -- a us-west-2 bucket
8900
+ * answered it with `x-amz-bucket-region: us-west-2` to clients in three
8901
+ * regions). The gate exists for {@link probeBucketRegion}, which catches the
8902
+ * WHOLE failure space of `GetBucketLocation` and so must sort the two kinds
8903
+ * apart. If this method ever takes an error from a second call site, it needs
8904
+ * the gate too.
8905
+ *
8906
+ * `awsError` is optional because the two PRE-FLIGHT callers (issues #2241 /
8907
+ * #2245) have no AWS error in hand — they ask the question BEFORE issuing
8908
+ * the call that would produce one, so only the `GetBucketLocation` half
8909
+ * applies. This is deliberately the same function rather than a second one
8910
+ * that "also reads GetBucketLocation": the `EU` and empty-`LocationConstraint`
8911
+ * folds and the `x-amz-bucket-region` header precedence are one predicate,
8912
+ * and a paraphrase of them is a divergence waiting to happen.
8913
+ */
8914
+ async resolveOwnedBucketRegion(bucketName, awsError) {
8915
+ const fromHeader = awsError ? regionFromErrorHeader(awsError) : void 0;
8916
+ if (fromHeader) return fromHeader;
8747
8917
  return bucketLocationToRegion((await this.s3Client.send(new GetBucketLocationCommand({ Bucket: bucketName }))).LocationConstraint);
8748
8918
  }
8749
8919
  /**
8920
+ * Ask S3 where a bucket NAME lives, before doing anything to it.
8921
+ *
8922
+ * The non-throwing pre-flight form of {@link resolveOwnedBucketRegion},
8923
+ * used by the three sites that must confirm a bucket is the one they mean
8924
+ * before acting on it: the `us-east-1` create (issue
8925
+ * [#2241](https://github.com/go-to-k/cdkd/issues/2241)) and the update /
8926
+ * delete paths (issue [#2245](https://github.com/go-to-k/cdkd/issues/2245)).
8927
+ *
8928
+ * Why `GetBucketLocation` and not `HeadBucket`: recorded at length on
8929
+ * {@link S3BucketProvider.assertExistingBucketRegion} and in
8930
+ * `src/utils/aws-region-resolver.ts`. A cross-region `HeadBucket` 301s and
8931
+ * SDK v3's region-redirect middleware mishandles the empty-body HEAD into a
8932
+ * synthetic `name: 'Unknown'`, so it cannot answer this question at all;
8933
+ * `GetBucketLocation` is a GET with an XML body and is answered by ANY
8934
+ * regional endpoint in the same partition for a bucket in any region of
8935
+ * that partition (measured 2026-08-13, see that module).
8936
+ *
8937
+ * Deliberately NOT `resolveBucketRegion` from that module: it never throws
8938
+ * and returns the caller's own `fallbackRegion` on a failed probe, which
8939
+ * would report "same region" for every failure and silently authorize each
8940
+ * branch below. Distinguishing `absent` from `indeterminate` is precisely
8941
+ * what this wrapper adds over it.
8942
+ */
8943
+ async probeBucketRegion(bucketName) {
8944
+ try {
8945
+ return {
8946
+ kind: "region",
8947
+ region: await this.resolveOwnedBucketRegion(bucketName)
8948
+ };
8949
+ } catch (probeError) {
8950
+ if (isNoSuchBucketError(probeError)) return { kind: "absent" };
8951
+ if (carriesBucketRegionHeader(probeError)) {
8952
+ const fromHeader = regionFromErrorHeader(probeError);
8953
+ if (fromHeader) return {
8954
+ kind: "region",
8955
+ region: fromHeader
8956
+ };
8957
+ }
8958
+ return {
8959
+ kind: "indeterminate",
8960
+ reason: probeError instanceof Error ? probeError.message : String(probeError),
8961
+ errorName: probeError instanceof Error ? probeError.name : typeof probeError
8962
+ };
8963
+ }
8964
+ }
8965
+ /**
8966
+ * Refuse to act on a bucket a STATE RECORD names that does not live in
8967
+ * `expectedRegion` (issue [#2245](https://github.com/go-to-k/cdkd/issues/2245)).
8968
+ *
8969
+ * {@link assertExistingBucketRegion} is on the CREATE path only, so it stops
8970
+ * NEW poisoning and does nothing about a record already written by a build
8971
+ * that predates it. Two paths then act on such a record, and neither noticed:
8972
+ * `update()` had no region check at all, and `delete()`'s
8973
+ * {@link assertRegionMatch} fires only from the `NoSuchBucket` branch — which
8974
+ * a cross-region `DeleteBucket` never reaches, because SDK v3's
8975
+ * region-redirect middleware FOLLOWS the 301 for body-bearing operations and
8976
+ * the delete simply succeeds against the other region's bucket.
8977
+ *
8978
+ * ## Why this probes instead of reading state
8979
+ *
8980
+ * The cheap check would be state-vs-client region, and it cannot answer this
8981
+ * question: in the poisoned record the region field is the STACK's region and
8982
+ * MATCHES, while the physical id denotes a bucket somewhere else. The bucket's
8983
+ * own location is knowable only from S3, so one `GetBucketLocation` per
8984
+ * bucket update / delete is the floor. It is a rounding error next to what
8985
+ * those paths already spend — an S3 update issues a dozen or more Put/Get
8986
+ * round trips, and a delete may enumerate and remove every object version.
8987
+ *
8988
+ * ## Refuse rather than heal
8989
+ *
8990
+ * On update, healing would mean rewriting the recorded physical id, i.e. a
8991
+ * state mutation whose blast radius (every `Fn::GetAtt` consumer, the next
8992
+ * diff, the next destroy) is larger than the misconfiguration it repairs.
8993
+ * On delete, refusing strands the destroy and needs a human — a foot-gun, and
8994
+ * a smaller one than deleting a live bucket in another region, which nothing
8995
+ * can undo. Both refusals are `markNonRetryable` for the same reason the
8996
+ * create-side one is: they are deterministic, so letting `withRetry` re-run
8997
+ * the operation for its full budget only delays the same message.
8998
+ *
8999
+ * ## What an unanswered probe does, and the IAM dependency that creates
9000
+ *
9001
+ * `absent` and `indeterminate` both PROCEED, a deliberate asymmetry with the
9002
+ * create-side guard. `absent` is the ordinary already-gone destroy, which the
9003
+ * existing `NoSuchBucket` path handles idempotently one call later.
9004
+ * `indeterminate` is the load-bearing one, and the honest description of it
9005
+ * is NOT that the affected population is unrelated to the defect — an
9006
+ * earlier revision of this comment said exactly that and it is false. It is
9007
+ * the SAME population, merely unprobeable: a principal without
9008
+ * `s3:GetBucketLocation` can hold a poisoned record just as easily as any
9009
+ * other, and for that principal this guard is INERT on every call,
9010
+ * permanently. So this change introduces a new IAM dependency — the guard is
9011
+ * only as good as `s3:GetBucketLocation` on the target bucket, which a bucket
9012
+ * POLICY can also `Deny`.
9013
+ *
9014
+ * Proceeding anyway is still the right trade, for a reason about escape
9015
+ * hatches rather than about who is affected. Failing closed would strand
9016
+ * every update and destroy for a least-privilege role with no way out except
9017
+ * widening IAM — cdkd offers no per-resource override to force one through —
9018
+ * while the reachable hazard needs a bucket that EXISTS and is owned by this
9019
+ * account, for which `GetBucketLocation` answers from any endpoint in the
9020
+ * partition (see {@link probeBucketRegion}). Three fixes that look better and
9021
+ * are worse, recorded so they are not retried: refusing on `indeterminate` is
9022
+ * the stranding above; refusing only on 403 cannot work, because a missing
9023
+ * IAM grant and a hostile `Deny` are the same wire response; and folding 403
9024
+ * into `absent` is the precise collapse {@link BucketRegionProbe}'s third
9025
+ * state exists to prevent.
9026
+ *
9027
+ * What proceeding obliges instead is that it is never SILENT on the
9028
+ * irreversible path — see {@link announceUnverifiedBucketIdentity}.
9029
+ */
9030
+ async assertStateBucketRegion(operation, logicalId, resourceType, physicalId, expectedRegion) {
9031
+ const wantRegion = canonicalizeRegion(expectedRegion);
9032
+ const probe = await this.probeBucketRegion(physicalId);
9033
+ if (probe.kind === "absent") return;
9034
+ if (probe.kind === "indeterminate") {
9035
+ this.logger.debug(`GetBucketLocation failed for S3 bucket ${physicalId} (${logicalId}) before ${operation}: ${probe.reason}`);
9036
+ this.announceUnverifiedBucketIdentity(operation, logicalId, physicalId, probeFailedCause(probe.errorName));
9037
+ return;
9038
+ }
9039
+ if (probe.region === wantRegion) return;
9040
+ const consequence = operation === "update" ? `Applying this stack's bucket configuration would reconfigure a bucket in ${probe.region} that this stack does not own the definition of.` : `Deleting it would destroy a bucket in ${probe.region} that this stack's region does not contain, and that deletion cannot be undone.`;
9041
+ const remedy = operation === "update" ? `Confirm which bucket you mean, then either rerun this stack against ${probe.region}, or — if the recorded id is stale — drop cdkd's record with 'cdkd state orphan <stack>', which removes the record without touching any AWS resource, and deploy again.` : `Confirm which bucket you mean. If ${physicalId} is genuinely yours to delete, delete it deliberately in ${probe.region}; if this record is simply stale, drop it with 'cdkd state orphan <stack>', which removes cdkd's record without touching any AWS resource, and the destroy will then have nothing to do for this resource.`;
9042
+ throw markNonRetryable(new ProvisioningError(`Refusing to ${operation} S3 bucket ${physicalId} for ${logicalId} (${resourceType}): the bucket lives in ${probe.region}, while this stack's state is for ${wantRegion}. S3 bucket names are globally unique, so a state record can name a bucket in another region — records written before the create-path region guard can carry one. ${consequence} ${remedy}`, resourceType, logicalId, physicalId));
9043
+ }
9044
+ /**
9045
+ * Say out loud that the identity guard did not run.
9046
+ *
9047
+ * `delete` gets a WARN and `update` a debug, and the asymmetry is the whole
9048
+ * point rather than a stylistic choice. Both proceed, but only one of them
9049
+ * proceeds into something irreversible, and cdkd's default log level is
9050
+ * `info` -- so a debug line here meant that on the one path where the guard
9051
+ * failing open destroys data, the operator's output was byte-identical to a
9052
+ * normal destroy.
9053
+ *
9054
+ * That is not merely a misconfiguration story. `GetBucketLocation` is
9055
+ * DENIABLE on the target bucket by anyone holding `s3:PutBucketPolicy` on it,
9056
+ * and a `Deny` there is indistinguishable on the wire from a missing IAM
9057
+ * grant. A 403 leaves the probe `indeterminate` -- deliberately, since a 403
9058
+ * is answered by the endpoint that was asked and
9059
+ * {@link carriesBucketRegionHeader} therefore refuses to read its header as
9060
+ * the bucket's region -- so a poisoned record naming a bucket in another
9061
+ * region PROCEEDS: the auto-empty and `DeleteBucket` follow the SDK's 301 and
9062
+ * the bucket is gone. A throttled probe during a wide destroy reaches the
9063
+ * same place by accident, and a state record with no region at all never gets
9064
+ * a probe to begin with. None of the three can be refused -- see the caller's
9065
+ * note on why failing closed is the wrong trade -- so the remaining
9066
+ * obligation is that it never happens QUIETLY.
9067
+ *
9068
+ * Deliberately NOT retried on `isThrottlingError`. It would not touch the
9069
+ * hostile-`Deny` path at all (a 403 carries neither a throttling name nor a
9070
+ * retryable status), the provider has no injected sleep seam here so a
9071
+ * bounded retry either adds real wall-clock to every throttled destroy or
9072
+ * needs a new timer seam plus fixtures to control it, and with this warning
9073
+ * in place a throttled probe is now visible and re-runnable -- which was the
9074
+ * actual defect.
9075
+ */
9076
+ announceUnverifiedBucketIdentity(operation, logicalId, physicalId, cause) {
9077
+ const message = `Could not confirm which bucket ${physicalId} for ${logicalId} is before ${operation}: ${cause.why}, so the cross-region guard did NOT run and cdkd is proceeding on the recorded physical id alone.`;
9078
+ if (operation === "delete") {
9079
+ this.logger.warn(`${message} If this state record names a bucket in another region, this destroy will delete THAT bucket. ${cause.remedy}`);
9080
+ return;
9081
+ }
9082
+ this.logger.debug(`${message} Proceeding.`);
9083
+ }
9084
+ /**
8750
9085
  * Create an S3 bucket
8751
9086
  */
8752
9087
  async create(logicalId, resourceType, properties, context) {
@@ -8761,12 +9096,16 @@ var S3BucketProvider = class S3BucketProvider {
8761
9096
  const region = await this.getRegion();
8762
9097
  if (region !== "us-east-1") createParams.CreateBucketConfiguration = { LocationConstraint: region };
8763
9098
  if (properties["ObjectLockEnabled"] === true || properties["ObjectLockEnabled"] === "true") createParams.ObjectLockEnabledForBucket = true;
9099
+ const preflight = canonicalizeRegion(region) === "us-east-1" ? await this.probeBucketRegion(bucketName) : { kind: "absent" };
8764
9100
  let createdNewBucket = false;
8765
9101
  try {
8766
9102
  await this.s3Client.send(new CreateBucketCommand(createParams));
8767
- createdNewBucket = true;
9103
+ createdNewBucket = preflight.kind === "absent";
9104
+ if (preflight.kind === "region" && preflight.region !== "us-east-1") this.refuseForeignRegionAdopt(logicalId, resourceType, bucketName, "us-east-1", preflight.region);
9105
+ if (preflight.kind === "region") this.logger.warn(`S3 bucket ${bucketName} for ${logicalId} (${resourceType}) already existed in ${preflight.region} and was ADOPTED, not created. In us-east-1 S3 answers a re-create of a bucket you already own with 200 OK and RESETS that bucket's access control lists, so any ACL previously set on ${bucketName} is now the default. cdkd will not delete this bucket if the rest of this create fails.`);
8768
9106
  this.logger.debug(`Created S3 bucket: ${bucketName}`);
8769
9107
  } catch (createError) {
9108
+ if (createError instanceof ProvisioningError) throw createError;
8770
9109
  if (createError instanceof Error && (createError.name === "BucketAlreadyOwnedByYou" || createError.message.includes("you already own it"))) {
8771
9110
  await this.assertExistingBucketRegion(logicalId, resourceType, bucketName, region, createError);
8772
9111
  this.logger.debug(`S3 bucket ${bucketName} already exists and is owned by you`);
@@ -8781,7 +9120,12 @@ var S3BucketProvider = class S3BucketProvider {
8781
9120
  await this.s3Client.send(new DeleteBucketCommand({ Bucket: bucketName }));
8782
9121
  this.logger.debug(`Cleaned up partially-created S3 bucket ${logicalId} (${bucketName}) after wiring failure`);
8783
9122
  } catch (cleanupError) {
8784
- this.logger.warn(`Failed to clean up partially-created S3 bucket ${logicalId} (${bucketName}): ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}. Manual deletion may be required before the next deploy: aws s3api delete-bucket --bucket '${bucketName}'`);
9123
+ this.logger.debug(`DeleteBucket cleanup failed for S3 bucket ${logicalId} (${bucketName}): ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`);
9124
+ this.logger.warn(`Failed to clean up partially-created S3 bucket ${logicalId} (${bucketName}) (${cleanupError instanceof Error ? cleanupError.name : typeof cleanupError}). Re-run with --verbose for AWS's own message. Manual deletion may be required before the next deploy: aws s3api delete-bucket --bucket '${bucketName}'`);
9125
+ }
9126
+ else if (preflight.kind === "indeterminate") {
9127
+ this.logger.debug(`GetBucketLocation failed for S3 bucket ${bucketName} (${logicalId}) during create: ${preflight.reason}`);
9128
+ this.logger.warn(`Not cleaning up S3 bucket ${logicalId} (${bucketName}) after a wiring failure: this deploy could not confirm whether it created the bucket (region probe failed: ${preflight.errorName}), and in us-east-1 a successful CreateBucket does not prove it. Re-run with --verbose for AWS's own message. If cdkd created it, delete it manually before the next deploy: aws s3api delete-bucket --bucket '${bucketName}'`);
8785
9129
  }
8786
9130
  throw innerError;
8787
9131
  }
@@ -8811,6 +9155,7 @@ var S3BucketProvider = class S3BucketProvider {
8811
9155
  wasReplaced: true
8812
9156
  };
8813
9157
  }
9158
+ await this.assertStateBucketRegion("update", logicalId, resourceType, physicalId, await this.getRegion());
8814
9159
  try {
8815
9160
  const effectiveOverrides = await this.applyConfiguration(physicalId, properties, {
8816
9161
  skipTags: true,
@@ -8840,6 +9185,8 @@ var S3BucketProvider = class S3BucketProvider {
8840
9185
  async delete(logicalId, physicalId, resourceType, properties, context) {
8841
9186
  this.logger.debug(`Deleting S3 bucket ${logicalId}: ${physicalId}`);
8842
9187
  const allowAutoEmpty = context?.forceDataDelete === true || hasCdkAutoDeleteTag(properties, "aws-cdk:auto-delete-objects");
9188
+ if (context?.expectedRegion) await this.assertStateBucketRegion("delete", logicalId, resourceType, physicalId, context.expectedRegion);
9189
+ else this.announceUnverifiedBucketIdentity("delete", logicalId, physicalId, noRecordedRegionCause());
8843
9190
  try {
8844
9191
  await this.deleteBucketWithEmptyRetry(logicalId, physicalId, allowAutoEmpty);
8845
9192
  } catch (error) {
@@ -16259,22 +16606,30 @@ function isTerminalDeleteFailure(error) {
16259
16606
  * table with its guard down, which is the exact residue this mechanism
16260
16607
  * exists to remove, so the message carries the physical id and the one
16261
16608
  * command that fixes it.
16609
+ *
16610
+ * It REPORTS what it did rather than only doing it (see
16611
+ * {@link ProtectionCompensationOutcome}), because the caller's decision to drop
16612
+ * the flip record is not the same decision as whether to compensate: dropping
16613
+ * it after a re-enable that FAILED discards the only record that cdkd still
16614
+ * owes this table its guard back.
16262
16615
  */
16263
16616
  async function compensateRemovedDeletionProtection(opts) {
16264
- if (!opts.flip.flippedOffByThisRun) return;
16265
- if (opts.flip.deleteAccepted) return;
16266
- if (!isTerminalDeleteFailure(opts.error)) return;
16617
+ if (!opts.flip.flippedOffByThisRun) return "not-applicable";
16618
+ if (opts.flip.deleteAccepted) return "not-applicable";
16619
+ if (!isTerminalDeleteFailure(opts.error)) return "not-applicable";
16267
16620
  try {
16268
16621
  await opts.reEnable();
16269
16622
  opts.logger.warn(`DynamoDB ${opts.typeLabel} ${opts.logicalId}: the delete failed after --remove-protection had turned DeletionProtectionEnabled off, so it was re-enabled on ${opts.physicalId}. The delete failure below is the outcome.`);
16623
+ return "restored";
16270
16624
  } catch (reEnableError) {
16271
16625
  const detail = reEnableError instanceof Error ? reEnableError.message : String(reEnableError);
16272
16626
  if (isResourceNotFoundError(reEnableError)) {
16273
16627
  const regionArg = opts.region ? ` --region ${opts.region}` : "";
16274
16628
  opts.logger.warn(`DynamoDB ${opts.typeLabel} ${opts.logicalId}: could not re-enable DeletionProtectionEnabled on ${opts.physicalId} after the delete failed — DynamoDB answered ResourceNotFound. That most commonly means the table is gone or its status is not ACTIVE, and it can also mean the table is not in this region or account. If it still exists, its deletion protection is OFF. Check with: aws dynamodb describe-table --table-name ${opts.physicalId}${regionArg} and if it is there, restore it with: aws dynamodb update-table --table-name ${opts.physicalId}${regionArg} --deletion-protection-enabled. (${detail})`);
16275
- return;
16629
+ return "failed";
16276
16630
  }
16277
16631
  opts.logger.error(`DynamoDB ${opts.typeLabel} ${opts.logicalId}: could NOT re-enable DeletionProtectionEnabled on ${opts.physicalId} after the delete failed — that table is LIVE with its deletion protection still off. Restore it with: aws dynamodb update-table --table-name ${opts.physicalId} --deletion-protection-enabled. (${detail})`);
16632
+ return "failed";
16278
16633
  }
16279
16634
  }
16280
16635
  /**
@@ -17245,21 +17600,25 @@ var DynamoDBTableProvider = class {
17245
17600
  try {
17246
17601
  await this.deleteTableResource(logicalId, physicalId, resourceType, context, flip);
17247
17602
  } catch (error) {
17248
- await compensateRemovedDeletionProtection({
17249
- flip,
17250
- error,
17251
- logicalId,
17252
- physicalId,
17253
- typeLabel: "table",
17254
- logger: this.logger,
17255
- ...context?.expectedRegion ? { region: context.expectedRegion } : {},
17256
- reEnable: async () => {
17257
- await this.dynamoDBClient.send(new UpdateTableCommand({
17258
- TableName: physicalId,
17259
- DeletionProtectionEnabled: true
17260
- }));
17261
- }
17262
- });
17603
+ let outcome = "failed";
17604
+ try {
17605
+ outcome = await compensateRemovedDeletionProtection({
17606
+ flip,
17607
+ error,
17608
+ logicalId,
17609
+ physicalId,
17610
+ typeLabel: "table",
17611
+ logger: this.logger,
17612
+ ...context?.expectedRegion ? { region: context.expectedRegion } : {},
17613
+ reEnable: async () => {
17614
+ await this.dynamoDBClient.send(new UpdateTableCommand({
17615
+ TableName: physicalId,
17616
+ DeletionProtectionEnabled: true
17617
+ }));
17618
+ }
17619
+ });
17620
+ } catch {}
17621
+ if (isTerminalDeleteFailure(error) && outcome !== "failed") this.protectionFlips.release(deleteBudgetKey(physicalId, context?.expectedRegion));
17263
17622
  throw error;
17264
17623
  }
17265
17624
  }
@@ -19812,21 +20171,25 @@ var DynamoDBGlobalTableProvider = class {
19812
20171
  try {
19813
20172
  await this.deleteGlobalTableResource(logicalId, physicalId, resourceType, context, flip);
19814
20173
  } catch (error) {
19815
- await compensateRemovedDeletionProtection({
19816
- flip,
19817
- error,
19818
- logicalId,
19819
- physicalId,
19820
- typeLabel: "GlobalTable",
19821
- logger: this.logger,
19822
- ...context?.expectedRegion ? { region: context.expectedRegion } : {},
19823
- reEnable: async () => {
19824
- await this.dynamoDBClient.send(new UpdateTableCommand({
19825
- TableName: physicalId,
19826
- DeletionProtectionEnabled: true
19827
- }));
19828
- }
19829
- });
20174
+ let outcome = "failed";
20175
+ try {
20176
+ outcome = await compensateRemovedDeletionProtection({
20177
+ flip,
20178
+ error,
20179
+ logicalId,
20180
+ physicalId,
20181
+ typeLabel: "GlobalTable",
20182
+ logger: this.logger,
20183
+ ...context?.expectedRegion ? { region: context.expectedRegion } : {},
20184
+ reEnable: async () => {
20185
+ await this.dynamoDBClient.send(new UpdateTableCommand({
20186
+ TableName: physicalId,
20187
+ DeletionProtectionEnabled: true
20188
+ }));
20189
+ }
20190
+ });
20191
+ } catch {}
20192
+ if (isTerminalDeleteFailure(error) && outcome !== "failed") this.protectionFlips.release(deleteBudgetKey(physicalId, context?.expectedRegion));
19830
20193
  throw error;
19831
20194
  }
19832
20195
  }
@@ -88060,4 +88423,4 @@ function buildProgram() {
88060
88423
 
88061
88424
  //#endregion
88062
88425
  export { buildProgram };
88063
- //# sourceMappingURL=program-CP4DgOV1.js.map
88426
+ //# sourceMappingURL=program-B2i8bJUJ.js.map