@go-to-k/cdkd 0.291.27 → 0.291.28

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.
@@ -1,8 +1,8 @@
1
- import { $ as yellow, $a as CdkdError, $i as synthesisStatusMessage, $n as refuseMalformedOrphanRecordsForDestroy, $r as forceQuitRecoveryClause, $t as configIntegerRefusal, A as createPreDeleteFinalSnapshot, Aa as credentialFingerprint, Ai as validateContainerRepoName, An as hasReadableOutputs, Ao as isTransientServerError, Ar as TEMPLATE_SOURCED_RULES, At as deleteSkipReason, B as isStatefulRecreateTargetSync, Ba as absoluteAssemblyPathEscape, Bi as getDockerCmd, Bn as malformedOrphansRefusalMessage, Br as maskSecretsInText, Bt as isImpossibleEmptyStoredAttribute, C as namesAnyDeclaredParameter, Ca as expectedOwnerParam, Ci as assertAssetBucketRegion, Cn as STATE_REGION_DIVERGED, Co as commandHole, Cr as describeTypeWithThrottleRetry, Ct as compositeIdSkipResult, D as PRE_DELETE_SNAPSHOT_TYPES, Da as ambientClientDefaults, Di as parseBootstrapMarker, Dn as deployRefusesOrphanRowsReason, Do as isMarkedNonRetryable, Dr as STATE_SOURCED_BASELINE_RULES, Dt as maskerOrIdentity, E as ATOMIC_FINAL_SNAPSHOT_TYPES, Ea as derivePartitionAndUrlSuffix, Ei as isCrossRegionRedirect, En as UNREADABLE_RESOURCES_MAP_ROW, Eo as withheldTargetClause, Er as TemplateParser, Et as maskDeep, F as extractDeploymentEventError, Fa as defineOwnKey, Fi as describeDockerExecFailure, Fn as malformedExportSourceWarning, Fo as safeStringify, Fr as identityKeyFor, Ft as carriesDynamicReference, G as toCfnInteger, Gi as runDockerForeground, Gn as malformedResourceEntriesWarning, Gr as redactSecretsForState, Gt as isSettledInstanceState, H as WARM_THROUGHPUT_MEMBERS, Ha as displayAssemblyPath, Hi as isMalformedEnvKey, Hn as malformedOutputsRefusalMessage, Hr as recordMaskOnlyValue, Ht as isUnboundTemplateParameter, I as makeSiblingClaimReader, Ia as hasOwnKey, Ii as describeDockerFailure, In as malformedLocalOutputsWarning, Ir as isSingleDynamicReferenceToken, It as carriesFetchableDynamicReference, J as bold, Ja as getAwsClients, Ji as escapeRegExp, Jn as malformedStateRefusalMessage, Jr as S3StateBackend, Jt as resolveExplicitPhysicalId, K as toFiniteNumber, Ka as resolveBucketRegion, Ki as runDockerStreaming, Kn as malformedResourcePropertiesWarning, Kr as scrubResourceRecord, Kt as WAFv2WebACLProvider, L as planOrphanAdoption, La as hasPlainPrototype, Li as dockerSpawnEnvWithSensitive, Ln as malformedOrphanRecordsRefusalMessage, Lr as isUncertifiedBaselineMaskPosition, Lt as cfnRefValueFromPhysicalId, M as refusesFinalSnapshot, Ma as failedStageNote, Mi as buildDockerImage, Mn as isReadableOrphanRecord, Mo as markRedactedCause, Mr as createSecretMasker, Mt as disableInstanceApiTermination, N as unsupportedFinalSnapshotError, Na as globMatches, Ni as resolveDockerContextDirectory, Nn as isReadableResourceEntry, No as retryClassificationText, Nr as dynamicReferenceTokens, Nt as isTerminationProtectionPropagationError, O as buildFinalSnapshotIdentifier, Oa as ambientCredentialConfig, Oi as readBootstrapMarkerBody, On as displayLogicalId, Oo as isRetryableTransientError, Or as STATE_SOURCED_CROSS_GENERATION_RULES, Ot as UNSPECIFIED_SKIP_REASON, P as makeCanonicalizePropertiesFn, Pa as processStackMessages, Pi as describeDockerCapturedOutput, Pn as malformedExportNamesWarning, Po as describeAwsFailure, Pr as errorCauseChain, Pt as IntrinsicFunctionResolver, Q as red, Qi as Synthesizer, Qn as refuseMalformedOrphanRecords, Qr as buildLockContentionMessage, Qt as configBooleanRefusal, R as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ra as nullPrototypeRecord, Ri as finchSecretArgvRefusal, Rn as malformedOrphanRecordsWarning, Rr as markSameGenerationBag, Rt as coerceParameterTypedValue, S as computeParameterTaint, Sa as decodeListingKey, Si as BOOTSTRAP_MARKER_PREFIX, Sn as INTRINSIC_KEYS, So as withErrorHandling, Sr as findSilentDropProperties, St as compositeIdSeparatorRefusal, T as resourcesNamingDeclaredParameter, Ta as canonicalizeRegion, Ti as getBootstrapMarkerKey, Tn as UNREADABLE_ORPHANS_CONTAINER_ROW, To as shellQuote, Tr as DagBuilder, Tt as createMaskedRetryLogger, U as coerceWarmThroughput$1, Ua as renderAssemblyPathEscape, Ui as partitionSensitiveEnv, Un as malformedOutputsWarning, Ur as recordMaskOnlyValuesIn, Ut as parameterTypeMayLoseSecretIdentity, V as renderStatefulReason, Va as describeFileReadFailure, Vi as isDockerClientEnvKey, Vn as malformedOrphansWarning, Vr as pathCrossesDottedKey, Vt as isNamelessDynamicReferenceError, W as isWarmThroughputDecrease, Wa as resolveAssemblyPath, Wi as redactDockerArgvValues, Wn as malformedRenderedContainersWarning, Wr as recoverMaskedOutput, Wt as refStateLookupFromResource, X as gray, Xa as runWithStackAwsClients, Xi as AssetManifestLoader, Xn as refuseDivergentRecordRegionForDestroy, Xr as UNREPRODUCIBLE_LOCK_CLAUSE, Xt as coerceCfnBoolean, Y as cyan, Ya as resetAwsClients, Yi as stripControlChars, Yn as previewableOrphanRecords, Yr as rebuildClientForBucketRegion, Yt as assertRegionMatch, Z as green, Za as setAwsClients, Zi as getDockerImageBySourceHash, Zn as refuseMalformedNestedChildOutputs, Zr as buildForceUnlockCommand, Zt as coerceCfnInteger, _ as bagHoldsSecretExpression, _a as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, _i as buildAssetRedirectMap, _n as AWS_CREDENTIAL_ENV_KEYS, _r as safeRegion, _t as startInterruptWatch, a as DeploymentEventsStore, aa as resolveSkipPrefix, ai as hasReadableExportSet, an as requireConfigString, ao as IntrinsicResolutionRefusalError, ar as refuseMalformedResourceAttributesForOrphan, at as secretBearingStateKeyWarning, b as mergeNoChangeOutputs, ba as producerCoordinateKey, bi as rewriteTemplateAssetReferences, bn as envCredentialsAreAssumedRole, br as unreadableOrphanRecords, bt as ccProtectionRegistryTypes, c as replayFailedOperations, ca as resolveUseCdkBootstrapAssets, ci as importableOutputKeys, cn as classifyReplaySecretRegion, cr as refuseMalformedResourcePropertiesForOrphan, ct as IAMRoleProvider, d as updatePartialMessage, da as ssmResolvedValueType, di as orphansAfterRollback, dn as s3BucketArn, do as PartialFailureError, dr as repairMalformedOrphanRecordsForReadOnly, dt as ProviderRegistry, ea as getDefaultStateBucketName, ei as formatLockExpiry, en as configStringRefusal, er as refuseMalformedOrphans, et as collectDeclaredOutputNames, f as updatePartialReason, fa as CFN_TEMPLATE_BODY_LIMIT, fi as orphansCarriedFrom, fn as s3BucketDomainName, fo as ProvisioningError, fr as repairMalformedOrphansForReadOnly, ft as wouldReturnToSdkProvider, g as computeImplicitDeleteEdges, ga as uploadCfnTemplate, gi as WorkGraph, gn as applyRoleArnIfSet, go as StackTerminationProtectionError, gr as repairMalformedResourcesForReadOnly, gt as isInterruptedWaitError, h as IMPLICIT_DELETE_DEPENDENCIES, ha as findLargeInlineResources, hi as stringifyValue, hn as s3BucketWebsiteUrl, ho as StackHasActiveImportsError, hr as repairMalformedResourcePropertiesForReadOnly, ht as interruptWatchListenerCount, i as DeploymentEventsReader, ia as resolveCaptureObservedState, ii as exportNamesCarriedFrom, in as requireConfigObject, io as DynamicReferenceRegionAmbiguousError, ir as refuseMalformedOutputsForDestroy, it as secretBearing, j as isFinalSnapshotError, ja as AssemblyReader, ji as buildDenyExternalAccessPolicy, jn as hasReadableResources, jo as markNonRetryable, jr as carriesSecretMask, jt as isWaitAbandonedError, k as ccRoutedFinalSnapshotError, ka as clientDefaultsFor, ki as validateAssetBucketName, kn as hasReadableOrphans, ko as isThrottlingError, kr as STATE_SOURCED_READBACK_RULES, kt as deleteIndeterminateGuards, l as replayRollback, la as stateBucketExistenceConfirmed, li as importableOutputs, ln as producerRegionsFromState, lo as MalformedProducerRecordRefusalError, lr as refuseMalformedResourcesForDestroy, lt as collectInlinePolicyNamesManagedBySiblings, m as maskingRetryLogger, ma as MIGRATE_TMP_PREFIX, mi as AssetPublisher, mn as s3BucketRegionalDomainName, mo as ResourceUpdateNotSupportedError, mr as repairMalformedResourceEntriesForReadOnly, mt as endCommandInterruptScope, n as DEFAULT_RESOURCE_WARN_AFTER_MS, na as resolveApp, ni as DEFAULT_STATE_PREFIX, nn as replayWarn, nr as refuseMalformedOrphansForOrphan, nt as exportAliasCollisionScrubWarning, o as planFailedOps, oa as resolveStateBucketWithDefault, oi as hasReasonlessBaselineRefusal, on as definedAttributes, oo as LocalInvokeBuildError$1, or as refuseMalformedResourceEntries, ot as secretSafeKeyDisplay, p as withResourceDeadline, pa as CFN_TEMPLATE_URL_LIMIT, pi as shouldRetainResource, pn as s3BucketDualStackDomainName, po as ResourceTimeoutError, pr as repairMalformedOutputsForReadOnly, pt as beginCommandInterruptScope, q as formatResourceLine, qa as AwsClients, qi as warnFinchArgvExposure, qn as malformedResourcesWarning, qr as LockManager, qt as normalizeAwsTagsToCfn, r as DeployEngine, ra as resolveAutoAssetStorage, ri as describeRegionValueKind, rn as requireConfigArray, ro as DeployCancelledError, rr as refuseMalformedOutputs, rt as isExportAliasCollision, s as planRollback, sa as resolveStateBucketWithDefaultAndSource, si as hasUnverifiableParameterRefusal, sn as stringifyIfAssigned, so as LocalStartServiceError, sr as refuseMalformedResourceEntriesForOrphan, st as getCurrentResourceSecrets, t as DEFAULT_RESOURCE_TIMEOUT_MS, ta as getLegacyStateBucketName, ti as CUSTOM_RESOURCE_RESPONSE_PREFIX, tn as readConfigString, to as CrossAccountSecretRefusalError, tr as refuseMalformedOrphansForDestroy, tt as collectPublishedOutputNames, u as resolveReplacementOldType, ua as warnDeprecatedNoPrefixCliFlag, ui as isReadableBag, un as withSharedDrainBudget, uo as NestedStackChildDirectDestroyError, ur as refuseMalformedState, ut as clearOnUpdateRemoval, v as isSecretBearingReferenceString, va as injectiveKey, vi as createAssetRedirectResolver, vn as applyCallerIdentityCredentials, vo as SynthesisError, vr as safeStackName, vt as CloudControlProvider, w as reachableDivergentParameters, wa as PARTITION_TABLE, wi as ensureAssetStorage, wn as STATE_RESOURCES_MALFORMED, wo as pasteableCommand, wr as withRetry, wt as packCompositeId, x as bindingSkippedOutputs, xa as producerRecordKey, xi as AssetModeResolver, xn as DiffCalculator, xo as normalizeAwsError, xr as findActionableSilentDrops, xt as compositeIdFormatMessage, y as keptWholeReasonText, ya as injectiveKeyPrefix, yi as loadPublishableAssetManifest, yn as callerEnvCredentials, yr as unpreviewableOrphanRecords, yt as slowCcOperationTimeoutMs, z as isStatefulRecreateTargetForReplace, za as ownValue, zi as formatDockerLoginError, zn as malformedOrphanRowsKeptWarning, zr as maskSecretsInError, zt as getAccountInfo } from "./deploy-engine-DIZztJy5.js";
1
+ import { $ as yellow, $a as setAwsClients, $i as getDockerImageBySourceHash, $n as refuseMalformedOrphanRecordsForDestroy, $r as buildForceUnlockCommand, $t as configIntegerRefusal, A as createPreDeleteFinalSnapshot, Aa as ambientCredentialConfig, Ai as readBootstrapMarkerBody, An as hasReadableOutputs, Ao as withheldTargetClause, Ar as STATE_SOURCED_CROSS_GENERATION_RULES, At as deleteSkipReason, B as isStatefulRecreateTargetSync, Ba as nullPrototypeRecord, Bi as finchSecretArgvRefusal, Bn as malformedOrphansRefusalMessage, Br as markSameGenerationBag, Bt as isImpossibleEmptyStoredAttribute, C as namesAnyDeclaredParameter, Ca as producerRecordKey, Ci as AssetModeResolver, Cn as STATE_REGION_DIVERGED, Co as normalizeAwsError, Cr as prefetchCreateOnlyPropertyPaths, Ct as compositeIdSkipResult, D as PRE_DELETE_SNAPSHOT_TYPES, Da as canonicalizeRegion, Di as getBootstrapMarkerKey, Dn as deployRefusesOrphanRowsReason, Do as plainOrDescribed, Dr as DagBuilder, Dt as maskerOrIdentity, E as ATOMIC_FINAL_SNAPSHOT_TYPES, Ea as PARTITION_TABLE, Ei as ensureAssetStorage, En as UNREADABLE_RESOURCES_MAP_ROW, Eo as pasteableCommand, Er as withRetry, Et as maskDeep, F as extractDeploymentEventError, Fa as globMatches, Fi as resolveDockerContextDirectory, Fn as malformedExportSourceWarning, Fo as markNonRetryable, Fr as dynamicReferenceTokens, Ft as carriesDynamicReference, G as toCfnInteger, Ga as renderAssemblyPathEscape, Gi as partitionSensitiveEnv, Gn as malformedResourceEntriesWarning, Gr as recordMaskOnlyValuesIn, Gt as isSettledInstanceState, H as WARM_THROUGHPUT_MEMBERS, Ha as absoluteAssemblyPathEscape, Hi as getDockerCmd, Hn as malformedOutputsRefusalMessage, Hr as maskSecretsInText, Ht as isUnboundTemplateParameter, I as makeSiblingClaimReader, Ia as processStackMessages, Ii as describeDockerCapturedOutput, In as malformedLocalOutputsWarning, Io as markRedactedCause, Ir as errorCauseChain, It as carriesFetchableDynamicReference, J as bold, Ja as resolveBucketRegion, Ji as runDockerStreaming, Jn as malformedStateRefusalMessage, Jr as scrubResourceRecord, Jt as resolveExplicitPhysicalId, K as toFiniteNumber, Ka as resolveAssemblyPath, Ki as redactDockerArgvValues, Kn as malformedResourcePropertiesWarning, Kr as recoverMaskedOutput, Kt as WAFv2WebACLProvider, L as planOrphanAdoption, La as defineOwnKey, Li as describeDockerExecFailure, Ln as malformedOrphanRecordsRefusalMessage, Lo as retryClassificationText, Lr as identityKeyFor, Lt as cfnRefValueFromPhysicalId, M as refusesFinalSnapshot, Ma as credentialFingerprint, Mi as validateContainerRepoName, Mn as isReadableOrphanRecord, Mo as isRetryableTransientError, Mr as TEMPLATE_SOURCED_RULES, Mt as disableInstanceApiTermination, N as unsupportedFinalSnapshotError, Na as AssemblyReader, Ni as buildDenyExternalAccessPolicy, Nn as isReadableResourceEntry, No as isThrottlingError, Nr as carriesSecretMask, Nt as isTerminationProtectionPropagationError, O as buildFinalSnapshotIdentifier, Oa as derivePartitionAndUrlSuffix, Oi as isCrossRegionRedirect, On as displayLogicalId, Oo as quotedOrDescribed, Or as TemplateParser, Ot as UNSPECIFIED_SKIP_REASON, P as makeCanonicalizePropertiesFn, Pa as failedStageNote, Pi as buildDockerImage, Pn as malformedExportNamesWarning, Po as isTransientServerError, Pr as createSecretMasker, Pt as IntrinsicFunctionResolver, Q as red, Qa as runWithStackAwsClients, Qi as AssetManifestLoader, Qn as refuseMalformedOrphanRecords, Qr as UNREPRODUCIBLE_LOCK_CLAUSE, Qt as configBooleanRefusal, R as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ra as hasOwnKey, Ri as describeDockerFailure, Rn as malformedOrphanRecordsWarning, Ro as describeAwsFailure, Rr as isSingleDynamicReferenceToken, Rt as coerceParameterTypedValue, S as computeParameterTaint, Sa as producerCoordinateKey, Si as rewriteTemplateAssetReferences, Sn as INTRINSIC_KEYS, Sr as findSilentDropProperties, St as compositeIdSeparatorRefusal, T as resourcesNamingDeclaredParameter, Ta as expectedOwnerParam, Ti as assertAssetBucketRegion, Tn as UNREADABLE_ORPHANS_CONTAINER_ROW, To as commandHole, Tr as describeTypeWithThrottleRetry, Tt as createMaskedRetryLogger, U as coerceWarmThroughput$1, Ua as describeFileReadFailure, Ui as isDockerClientEnvKey, Un as malformedOutputsWarning, Ur as pathCrossesDottedKey, Ut as parameterTypeMayLoseSecretIdentity, V as renderStatefulReason, Va as ownValue, Vi as formatDockerLoginError, Vn as malformedOrphansWarning, Vr as maskSecretsInError, Vt as isNamelessDynamicReferenceError, W as isWarmThroughputDecrease, Wa as displayAssemblyPath, Wi as isMalformedEnvKey, Wn as malformedRenderedContainersWarning, Wr as recordMaskOnlyValue, Wt as refStateLookupFromResource, X as gray, Xa as getAwsClients, Xi as escapeRegExp, Xn as refuseDivergentRecordRegionForDestroy, Xr as S3StateBackend, Xt as coerceCfnBoolean, Y as cyan, Ya as AwsClients, Yi as warnFinchArgvExposure, Yn as previewableOrphanRecords, Yr as LockManager, Yt as assertRegionMatch, Z as green, Za as resetAwsClients, Zi as stripControlChars, Zn as refuseMalformedNestedChildOutputs, Zr as rebuildClientForBucketRegion, Zt as coerceCfnInteger, _ as bagHoldsSecretExpression, _a as findLargeInlineResources, _i as stringifyValue, _n as AWS_CREDENTIAL_ENV_KEYS, _o as StackHasActiveImportsError, _r as safeRegion, _t as startInterruptWatch, a as DeploymentEventsStore, aa as resolveAutoAssetStorage, ai as describeRegionValueKind, an as requireConfigString, ao as DeployCancelledError, ar as refuseMalformedResourceAttributesForOrphan, at as secretBearingStateKeyWarning, b as mergeNoChangeOutputs, ba as injectiveKey, bi as createAssetRedirectResolver, bn as envCredentialsAreAssumedRole, bo as SynthesisError, br as unreadableOrphanRecords, bt as ccProtectionRegistryTypes, c as replayFailedOperations, ca as resolveStateBucketWithDefault, ci as hasReasonlessBaselineRefusal, cn as classifyReplaySecretRegion, co as LocalInvokeBuildError$1, cr as refuseMalformedResourcePropertiesForOrphan, ct as IAMRoleProvider, d as updatePartialMessage, da as stateBucketExistenceConfirmed, di as importableOutputs, dn as s3BucketArn, do as MalformedProducerRecordRefusalError, dr as repairMalformedOrphanRecordsForReadOnly, dt as ProviderRegistry, ea as Synthesizer, ei as buildLockContentionMessage, en as configStringRefusal, er as refuseMalformedOrphans, et as collectDeclaredOutputNames, f as updatePartialReason, fa as warnDeprecatedNoPrefixCliFlag, fi as isReadableBag, fn as s3BucketDomainName, fo as NestedStackChildDirectDestroyError, fr as repairMalformedOrphansForReadOnly, ft as wouldReturnToSdkProvider, g as computeImplicitDeleteEdges, ga as MIGRATE_TMP_PREFIX, gi as AssetPublisher, gn as applyRoleArnIfSet, go as ResourceUpdateNotSupportedError, gr as repairMalformedResourcesForReadOnly, gt as isInterruptedWaitError, h as IMPLICIT_DELETE_DEPENDENCIES, ha as CFN_TEMPLATE_URL_LIMIT, hi as shouldRetainResource, hn as s3BucketWebsiteUrl, ho as ResourceTimeoutError, hr as repairMalformedResourcePropertiesForReadOnly, ht as interruptWatchListenerCount, i as DeploymentEventsReader, ia as resolveApp, ii as DEFAULT_STATE_PREFIX, in as requireConfigObject, ir as refuseMalformedOutputsForDestroy, it as secretBearing, j as isFinalSnapshotError, ja as clientDefaultsFor, ji as validateAssetBucketName, jn as hasReadableResources, jo as isMarkedNonRetryable, jr as STATE_SOURCED_READBACK_RULES, jt as isWaitAbandonedError, k as ccRoutedFinalSnapshotError, ka as ambientClientDefaults, ki as parseBootstrapMarker, kn as hasReadableOrphans, ko as shellQuote, kr as STATE_SOURCED_BASELINE_RULES, kt as deleteIndeterminateGuards, l as replayRollback, la as resolveStateBucketWithDefaultAndSource, li as hasUnverifiableParameterRefusal, ln as producerRegionsFromState, lo as LocalStartServiceError, lr as refuseMalformedResourcesForDestroy, lt as collectInlinePolicyNamesManagedBySiblings, m as maskingRetryLogger, ma as CFN_TEMPLATE_BODY_LIMIT, mi as orphansCarriedFrom, mn as s3BucketRegionalDomainName, mo as ProvisioningError, mr as repairMalformedResourceEntriesForReadOnly, mt as endCommandInterruptScope, n as DEFAULT_RESOURCE_WARN_AFTER_MS, na as getDefaultStateBucketName, ni as formatLockExpiry, nn as replayWarn, nr as refuseMalformedOrphansForOrphan, nt as exportAliasCollisionScrubWarning, o as planFailedOps, oa as resolveCaptureObservedState, oi as exportNamesCarriedFrom, on as definedAttributes, oo as DynamicReferenceRegionAmbiguousError, or as refuseMalformedResourceEntries, ot as secretSafeKeyDisplay, p as withResourceDeadline, pa as ssmResolvedValueType, pi as orphansAfterRollback, pn as s3BucketDualStackDomainName, po as PartialFailureError, pr as repairMalformedOutputsForReadOnly, pt as beginCommandInterruptScope, q as formatResourceLine, qi as runDockerForeground, qn as malformedResourcesWarning, qr as redactSecretsForState, qt as normalizeAwsTagsToCfn, r as DeployEngine, ra as getLegacyStateBucketName, ri as CUSTOM_RESOURCE_RESPONSE_PREFIX, rn as requireConfigArray, ro as CrossAccountSecretRefusalError, rr as refuseMalformedOutputs, rt as isExportAliasCollision, s as planRollback, sa as resolveSkipPrefix, si as hasReadableExportSet, sn as stringifyIfAssigned, so as IntrinsicResolutionRefusalError, sr as refuseMalformedResourceEntriesForOrphan, st as getCurrentResourceSecrets, t as DEFAULT_RESOURCE_TIMEOUT_MS, ta as synthesisStatusMessage, ti as forceQuitRecoveryClause, tn as readConfigString, to as CdkdError, tr as refuseMalformedOrphansForDestroy, tt as collectPublishedOutputNames, u as resolveReplacementOldType, ua as resolveUseCdkBootstrapAssets, ui as importableOutputKeys, un as withSharedDrainBudget, ur as refuseMalformedState, ut as clearOnUpdateRemoval, v as isSecretBearingReferenceString, va as uploadCfnTemplate, vi as WorkGraph, vn as applyCallerIdentityCredentials, vo as StackTerminationProtectionError, vr as safeStackName, vt as CloudControlProvider, w as reachableDivergentParameters, wa as decodeListingKey, wi as BOOTSTRAP_MARKER_PREFIX, wn as STATE_RESOURCES_MALFORMED, wo as withErrorHandling, wr as templateResourceTypes, wt as packCompositeId, x as bindingSkippedOutputs, xa as injectiveKeyPrefix, xi as loadPublishableAssetManifest, xn as DiffCalculator, xr as findActionableSilentDrops, xt as compositeIdFormatMessage, y as keptWholeReasonText, ya as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, yi as buildAssetRedirectMap, yn as callerEnvCredentials, yr as unpreviewableOrphanRecords, yt as slowCcOperationTimeoutMs, z as isStatefulRecreateTargetForReplace, za as hasPlainPrototype, zi as dockerSpawnEnvWithSensitive, zn as malformedOrphanRowsKeptWarning, zo as safeStringify, zr as isUncertifiedBaselineMaskPosition, zt as getAccountInfo } from "./deploy-engine-CidtgaFr.js";
2
2
  import { C as displayAwsMessage, D as isPasteableIdent, E as displayStackName, O as truncateCodePoints, S as UNRENDERABLE, T as displaySafe, _ as withSkipPrefix, b as SECRET_REF_MAX_CODE_POINTS, c as PATTERN_B_NAME_OPTIONS, f as explicitNamePropertyFor, h as getCurrentStackName, i as reserveStdoutForPayload, l as PATTERN_B_NAME_PROPERTIES, m as generateResourceNameWithFallback, n as getLogger, o as runStackBuffered, p as generateResourceName, s as getLiveRenderer, u as PATTERN_B_RESOURCE_TYPES, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS } from "./logger-Bt0RGDfb.js";
3
3
  import { n as awsClientDefaults, s as ambientRegion, u as formatAuthority } from "./aws-client-defaults-qSDe1fA_.js";
4
- import { t as getCdkdVersion } from "./version-CuKFdkKV.js";
5
- import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-CWCv370K.js";
4
+ import { t as getCdkdVersion } from "./version-CyFoAN6V.js";
5
+ import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-DaYT8UPi.js";
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
7
  import { createHash, randomBytes, randomUUID } from "node:crypto";
8
8
  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, NotFound, 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";
@@ -9810,6 +9810,34 @@ var S3BucketProvider = class S3BucketProvider {
9810
9810
  return Array.isArray(list) && list.length === 0;
9811
9811
  }
9812
9812
  /**
9813
+ * The refusal a TEMPLATE-path `update()` raises for a malformed
9814
+ * `VersioningConfiguration.Status` or `LoggingConfiguration` member, or
9815
+ * `undefined` (issue [#3728](https://github.com/go-to-k/cdkd/issues/3728)).
9816
+ *
9817
+ * It asks exactly what the two warn-and-skip arms in
9818
+ * {@link applySubConfigDiffs} ask, with the same predicate, key, fallback and
9819
+ * path, and only where those arms would ACT:
9820
+ *
9821
+ * - versioning: the block CHANGED (nullish on both sides, the arm's own
9822
+ * `versioningChanged`), and `configStringRefusal` refuses `Status`;
9823
+ * - logging: the block CHANGED, and `DestinationBucketName` or
9824
+ * `LogFilePrefix` refuses, in the applier's order. An ABSENT block (the
9825
+ * legitimate removal, `diffSubConfig`'s clear arm) needs no gate of its
9826
+ * own: `configStringRefusal` answers `undefined` for a nullish container.
9827
+ *
9828
+ * An unchanged malformed value is not a pending operation, so it is not
9829
+ * refused either — the same gate the arms' own warnings take.
9830
+ */
9831
+ static versioningOrLoggingRefusal(properties, previousProperties) {
9832
+ const changed = (key) => JSON.stringify(previousProperties[key] ?? null) !== JSON.stringify(properties[key] ?? null);
9833
+ if (changed("VersioningConfiguration")) {
9834
+ const refusal = configStringRefusal(properties["VersioningConfiguration"], "Status", "Suspended", "AWS::S3::Bucket VersioningConfiguration");
9835
+ if (refusal !== void 0) return refusal;
9836
+ }
9837
+ const logging = properties["LoggingConfiguration"];
9838
+ if (changed("LoggingConfiguration")) return configStringRefusal(logging, "DestinationBucketName", "", "AWS::S3::Bucket LoggingConfiguration") ?? configStringRefusal(logging, "LogFilePrefix", "", "AWS::S3::Bucket LoggingConfiguration");
9839
+ }
9840
+ /**
9813
9841
  * Turn a set of per-property overrides into the `effectiveProperties` bag the
9814
9842
  * deploy engine records in place of the desired one (issues #1612 / #1670).
9815
9843
  *
@@ -10696,6 +10724,10 @@ var S3BucketProvider = class S3BucketProvider {
10696
10724
  wasReplaced: true
10697
10725
  };
10698
10726
  }
10727
+ if (context?.replayingState !== true && context?.desiredFromAwsReadback !== true) {
10728
+ const refusal = S3BucketProvider.versioningOrLoggingRefusal(properties, previousProperties);
10729
+ if (refusal !== void 0) throw new ProvisioningError(`${refusal}. Nothing was applied to bucket ${displaySafe(physicalId)}; fix the template value`, resourceType, logicalId, physicalId);
10730
+ }
10699
10731
  await this.assertStateBucketRegion("update", logicalId, resourceType, physicalId, await this.getRegion());
10700
10732
  try {
10701
10733
  const effectiveOverrides = await this.applyConfiguration(physicalId, properties, {
@@ -18687,6 +18719,53 @@ function liveCeilingAlreadyMatches(live, requested) {
18687
18719
  return compared > 0;
18688
18720
  }
18689
18721
  /**
18722
+ * Is this per-index capacity AWS's on-demand `{0, 0}` placeholder — BOTH
18723
+ * members resolving to 0 through {@link toFiniteNumber}, so a string-spelled
18724
+ * `{'0', '0'}` counts and a half-zero `{0, 7}` does not?
18725
+ *
18726
+ * The ONE predicate behind both answers to that value on the UPDATE path: the
18727
+ * template-path pre-flight refusal ({@link zeroCapacityIndexRefusal}) and the
18728
+ * warn-skip the two non-template callers still take
18729
+ * (`DynamoDBTableProvider.skipZeroCapacityIndexUpdate`). Sharing it is what
18730
+ * keeps the refusal from being narrower or wider than the skip it replaces on
18731
+ * the template path.
18732
+ */
18733
+ function isZeroCapacityPlaceholder(requested) {
18734
+ if (requested === void 0 || requested === null || typeof requested !== "object") return false;
18735
+ return toFiniteNumber(requested.ReadCapacityUnits) === 0 && toFiniteNumber(requested.WriteCapacityUnits) === 0;
18736
+ }
18737
+ /**
18738
+ * The template-path pre-flight refusal for a per-index `ProvisionedThroughput`
18739
+ * of `{0, 0}` (issue [#3728](https://github.com/go-to-k/cdkd/issues/3728)), or
18740
+ * `undefined` when no index would reach `applyGsiUpdates`' capacity arms
18741
+ * carrying one.
18742
+ *
18743
+ * SCOPE, matched to what those arms act on rather than to every entry that
18744
+ * holds the value. An index is named only when its capacity is a PENDING
18745
+ * operation: it is absent from the recorded previous side (a new index, or the
18746
+ * adopted-index arm), or its `ProvisionedThroughput` differs from the recorded
18747
+ * one (the same-name arm's own change test, `JSON.stringify` against
18748
+ * `JSON.stringify`). A steady-state template that has always carried the
18749
+ * placeholder emits no capacity op, so refusing it would fail a deploy that
18750
+ * sends nothing — the same gate the `StreamSpecification` refusal takes.
18751
+ *
18752
+ * Wider than the skip in ONE direction, deliberately: a brand-new index, whose
18753
+ * `Create` action never consulted the skip and sent `{0, 0}` for AWS to reject
18754
+ * AFTER the tag diff and any billing flip had landed. Refusing it here trades
18755
+ * that half-applied failure for one that applied nothing.
18756
+ */
18757
+ function zeroCapacityIndexRefusal(properties, previousProperties, logicalId, maskSecrets) {
18758
+ const asEntries = (value) => (Array.isArray(value) ? value : []).filter((entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry));
18759
+ const previousByName = /* @__PURE__ */ new Map();
18760
+ for (const entry of asEntries(previousProperties["GlobalSecondaryIndexes"])) if (entry["IndexName"]) previousByName.set(entry["IndexName"], entry);
18761
+ const offending = asEntries(properties["GlobalSecondaryIndexes"]).filter((entry) => Boolean(entry["IndexName"])).filter((entry) => isZeroCapacityPlaceholder(entry["ProvisionedThroughput"])).filter((entry) => {
18762
+ const previous = previousByName.get(entry["IndexName"]);
18763
+ return previous === void 0 || JSON.stringify(previous["ProvisionedThroughput"]) !== JSON.stringify(entry["ProvisionedThroughput"]);
18764
+ }).map((entry) => typeof entry["IndexName"] === "string" ? maskSecrets(entry["IndexName"]) : "<unnamed>");
18765
+ if (offending.length === 0) return void 0;
18766
+ return `AWS::DynamoDB::Table ${logicalId}: GlobalSecondaryIndexes ${offending.join(", ")} declare ProvisionedThroughput {ReadCapacityUnits: 0, WriteCapacityUnits: 0}, which is AWS's on-demand placeholder rather than a capacity DynamoDB accepts (the minimum is 1). Declare a capacity of at least 1, or remove the per-index ProvisionedThroughput on a PAY_PER_REQUEST table. Nothing was applied to the table`;
18767
+ }
18768
+ /**
18690
18769
  * The sentinel DynamoDB accepts to CLEAR an on-demand request-unit maximum
18691
18770
  * (issue [#3373](https://github.com/go-to-k/cdkd/issues/3373)).
18692
18771
  *
@@ -19590,6 +19669,10 @@ var DynamoDBTableProvider = class {
19590
19669
  const [streamRefusal] = streamMemberRefusals(properties["StreamSpecification"]);
19591
19670
  if (streamRefusal !== void 0) throw new ProvisioningError(`${streamRefusal}. Nothing was applied to DynamoDB table ${logicalId}; fix the template value`, resourceType, logicalId, physicalId);
19592
19671
  }
19672
+ if (context?.replayingState !== true && context?.desiredFromAwsReadback !== true) {
19673
+ const zeroCapacityRefusal = zeroCapacityIndexRefusal(properties, previousProperties, logicalId, maskSecrets);
19674
+ if (zeroCapacityRefusal !== void 0) throw new ProvisioningError(zeroCapacityRefusal, resourceType, logicalId, physicalId);
19675
+ }
19593
19676
  const table = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }))).Table;
19594
19677
  let latestStreamArn = table?.LatestStreamArn;
19595
19678
  if (table?.TableArn) await this.applyTagDiff(table.TableArn, previousProperties["Tags"], properties["Tags"]);
@@ -21143,43 +21226,35 @@ var DynamoDBTableProvider = class {
21143
21226
  * capacity — including a half-declared or malformed one — still goes out and
21144
21227
  * is answered by AWS, the fail-OPEN direction this file uses everywhere.
21145
21228
  *
21146
- * CALLER-BLIND, decided rather than overlooked (PR review). A TEMPLATE-borne
21147
- * `{0, 0}` warn-skips here while `create()` fails loudly on the same value,
21148
- * which reads like the "loud failure for a quiet lie" trade the adopted-index
21149
- * arm below forbids. It was decided for three reasons; the first no longer
21150
- * holds as written (see its own bullet), the other two still do:
21151
- *
21152
- * - When this was decided, no discriminator separated the callers that
21153
- * matter. `ResourceProvider.update` takes an optional `UpdateContext`, and
21154
- * `desiredFromAwsReadback` on it is set ONLY by `cdkd drift --revert`
21155
- * (`src/cli/commands/drift.ts`). The rollback executor's revert arms DO
21156
- * pass a context (a `maskSecrets` capability, issue #1932) but never set
21157
- * that flag: their desired bag is `previousState.properties`, a TEMPLATE
21158
- * recorded earlier. At the time they were therefore indistinguishable
21159
- * from an ordinary template deploy — and THAT is the pair this decision
21160
- * turns on. (An earlier revision said the revert arms "pass NO context",
21161
- * which stopped being true with #1932 — issue #1999.) Since issue #3141
21162
- * they ALSO set `UpdateContext.replayingState`, so the caller is now
21163
- * knowable: the per-index `OnDemandThroughput` refusal in `update()` gates
21164
- * on exactly that pair of flags (the go-to-k/cdkd#3401 review). This arm
21165
- * was not re-decided when that became possible; splitting it the same way
21166
- * would turn a template-path warn-skip into a refusal, a behaviour change
21167
- * that needs its own review rather than a comment edit (issue #3728).
21168
- * - The repo's general rule for the UPDATE path is WARN-never-throw (issues
21169
- * #1545 / #1552): the desired bag here can BE a historical cdkd state
21170
- * record, and a refusal would make the table un-updatable and
21171
- * un-rollbackable with no template-side remedy.
21172
- * - The lie is bounded and self-surfacing. AWS never accepted `{0, 0}` at
21173
- * create either, so no live table can be holding it; and where the value
21174
- * would actually matter — a PROVISIONED table, whose indexes hold a real
21175
- * capacity — recording `{0, 0}` diverges from the readback and `cdkd drift`
21176
- * REPORTS it. On a PAY_PER_REQUEST table the recorded `{0, 0}` matches what
21177
- * `DescribeTable` reports for every index anyway, so there is no divergence
21178
- * to hide.
21229
+ * NOT caller-blind any more (issue #3728). A TEMPLATE-borne `{0, 0}` never
21230
+ * reaches this skip: `update()` REFUSES it at pre-flight, before any call —
21231
+ * see {@link zeroCapacityIndexRefusal} — exactly as `create()` fails loudly
21232
+ * on the same value. What still arrives here is the two NON-template desired
21233
+ * bags, and for them the warn-skip stands:
21234
+ *
21235
+ * - `cdkd drift --revert` (`UpdateContext.desiredFromAwsReadback`), the
21236
+ * caller this skip was written for: its desired bag is a pre-#1767
21237
+ * `observedProperties` blob, which the user cannot edit from the template.
21238
+ * - The rollback executor's two revert arms (`UpdateContext.replayingState`,
21239
+ * since issue #3141): their desired bag is `previousState.properties`, a
21240
+ * cdkd STATE record, so a refusal would make the table un-rollbackable with
21241
+ * no template-side remedy (issues #1545 / #1552).
21242
+ *
21243
+ * The lie the skip tells on those paths is bounded and self-surfacing. AWS
21244
+ * never accepted `{0, 0}` at create either, so no live table can be holding
21245
+ * it; and where the value would actually matter — a PROVISIONED table, whose
21246
+ * indexes hold a real capacity — recording `{0, 0}` diverges from the readback
21247
+ * and `cdkd drift` REPORTS it. On a PAY_PER_REQUEST table the recorded
21248
+ * `{0, 0}` matches what `DescribeTable` reports for every index anyway, so
21249
+ * there is no divergence to hide.
21250
+ *
21251
+ * Until issue #3728 this skip was caller-blind: when it was written no
21252
+ * discriminator separated a template deploy from the rollback revert arms
21253
+ * (they set neither flag), and the template path warn-skipped too. That
21254
+ * reason expired with #3141, and the template path now takes the refusal.
21179
21255
  */
21180
21256
  skipZeroCapacityIndexUpdate(indexName, physicalId, requested, maskSecrets) {
21181
- if (requested === void 0) return false;
21182
- if (toFiniteNumber(requested.ReadCapacityUnits) !== 0 || toFiniteNumber(requested.WriteCapacityUnits) !== 0) return false;
21257
+ if (!isZeroCapacityPlaceholder(requested)) return false;
21183
21258
  const warn = (message) => this.logger.warn(maskSecrets(message));
21184
21259
  warn(`${this.indexScopeAt(indexName, physicalId, maskSecrets)}: the requested ProvisionedThroughput is {ReadCapacityUnits: 0, WriteCapacityUnits: 0}, which is AWS's on-demand placeholder rather than a capacity DynamoDB accepts (the minimum is 1), so no per-index throughput update was sent. This usually means the value came from a pre-#1767 cdkd state record — a \`cdkd drift --revert\` of such a record, or a rollback replaying it. Re-run \`cdkd deploy\` (or \`cdkd drift --accept\`) to refresh the record.`);
21185
21260
  return true;
@@ -28896,6 +28971,14 @@ function narrowRouteDestinations(properties) {
28896
28971
  narrowed
28897
28972
  };
28898
28973
  }
28974
+ /**
28975
+ * The multi-destination refusal sentence, shared by `createRoute`'s guard and
28976
+ * `updateRoute`'s template-path pre-flight (issue #3728) so the two cannot
28977
+ * word the same fault differently.
28978
+ */
28979
+ function routeMultipleDestinationsMessage(logicalId, declared) {
28980
+ return `Route ${logicalId} declares more than one destination (${declared.join(", ")}). CloudFormation and EC2 accept exactly one of DestinationCidrBlock/DestinationIpv6CidrBlock/DestinationPrefixListId; remove the extra keys from the template.`;
28981
+ }
28899
28982
  /** The `AuthorizeSecurityGroupIngress` default protocol: "all protocols". */
28900
28983
  const SG_INGRESS_IP_PROTOCOL_DEFAULT = "-1";
28901
28984
  /** The refusal path both the create guard and the canonicalizer report under. */
@@ -29280,7 +29363,7 @@ var EC2Provider = class {
29280
29363
  case "AWS::EC2::VPCGatewayAttachment": return this.updateVpcGatewayAttachment(logicalId, physicalId);
29281
29364
  case "AWS::EC2::NatGateway": return this.updateNatGateway(logicalId, physicalId);
29282
29365
  case "AWS::EC2::RouteTable": return this.updateRouteTable(logicalId, physicalId);
29283
- case "AWS::EC2::Route": return this.updateRoute(logicalId, physicalId, resourceType, properties, previousProperties, context?.maskSecrets);
29366
+ case "AWS::EC2::Route": return this.updateRoute(logicalId, physicalId, resourceType, properties, previousProperties, context);
29284
29367
  case "AWS::EC2::SubnetRouteTableAssociation": return this.updateSubnetRouteTableAssociation(logicalId, physicalId);
29285
29368
  case "AWS::EC2::SecurityGroup": return this.updateSecurityGroup(logicalId, physicalId, resourceType, properties, previousProperties);
29286
29369
  case "AWS::EC2::SecurityGroupIngress": return this.updateSecurityGroupIngress(logicalId, physicalId, resourceType, properties, previousProperties, context?.maskSecrets);
@@ -30009,7 +30092,7 @@ var EC2Provider = class {
30009
30092
  const { declared: declaredDestinations, narrowed: narrowedProperties } = narrowRouteDestinations(properties);
30010
30093
  let effectiveProperties;
30011
30094
  if (declaredDestinations.length > 1) {
30012
- const message = `Route ${logicalId} declares more than one destination (${declaredDestinations.join(", ")}). CloudFormation and EC2 accept exactly one of DestinationCidrBlock/DestinationIpv6CidrBlock/DestinationPrefixListId; remove the extra keys from the template.`;
30095
+ const message = routeMultipleDestinationsMessage(logicalId, declaredDestinations);
30013
30096
  if (onMultipleDestinations) {
30014
30097
  const usedKey = declaredDestinations[0];
30015
30098
  effectiveProperties = narrowedProperties;
@@ -30053,12 +30136,17 @@ var EC2Provider = class {
30053
30136
  throw new ProvisioningError(`Failed to create Route ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
30054
30137
  }
30055
30138
  }
30056
- async updateRoute(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
30139
+ async updateRoute(logicalId, physicalId, resourceType, properties, previousProperties, context) {
30057
30140
  this.logger.debug(`Updating Route ${logicalId}: ${physicalId}`);
30141
+ const maskSecrets = context?.maskSecrets;
30058
30142
  if (JSON.stringify(properties) === JSON.stringify(previousProperties)) return {
30059
30143
  physicalId,
30060
30144
  wasReplaced: false
30061
30145
  };
30146
+ if (context?.replayingState !== true && context?.desiredFromAwsReadback !== true) {
30147
+ const { declared } = narrowRouteDestinations(properties);
30148
+ if (declared.length > 1) throw new ProvisioningError(`${routeMultipleDestinationsMessage(logicalId, declared)} The route was not changed.`, resourceType, logicalId, physicalId);
30149
+ }
30062
30150
  try {
30063
30151
  await this.deleteRoute(logicalId, physicalId, resourceType);
30064
30152
  const createResult = await this.createRoute(logicalId, resourceType, properties, (message) => this.logger.warn(message), maskSecrets);
@@ -33262,7 +33350,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
33262
33350
  path: `/canarySettings/stageVariableOverrides/${key}`,
33263
33351
  value
33264
33352
  });
33265
- for (const key of Object.keys(prevOverrides)) if (!(key in overrides)) patchOperations.push({
33353
+ for (const key of Object.keys(prevOverrides)) if (!Object.hasOwn(overrides, key)) patchOperations.push({
33266
33354
  op: "remove",
33267
33355
  path: `/canarySettings/stageVariableOverrides/${key}`
33268
33356
  });
@@ -33274,7 +33362,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
33274
33362
  path: `/variables/${key}`,
33275
33363
  value
33276
33364
  });
33277
- for (const key of Object.keys(prevVariables)) if (!(key in variables)) patchOperations.push({
33365
+ for (const key of Object.keys(prevVariables)) if (!Object.hasOwn(variables, key)) patchOperations.push({
33278
33366
  op: "remove",
33279
33367
  path: `/variables/${key}`
33280
33368
  });
@@ -44984,7 +45072,7 @@ var Route53Provider = class {
44984
45072
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
44985
45073
  switch (resourceType) {
44986
45074
  case "AWS::Route53::HostedZone": return this.updateHostedZone(logicalId, physicalId, resourceType, properties, previousProperties);
44987
- case "AWS::Route53::RecordSet": return this.updateRecordSet(logicalId, physicalId, resourceType, properties, context);
45075
+ case "AWS::Route53::RecordSet": return this.updateRecordSet(logicalId, physicalId, resourceType, properties, previousProperties, context);
44988
45076
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
44989
45077
  }
44990
45078
  }
@@ -45331,12 +45419,17 @@ var Route53Provider = class {
45331
45419
  throw new ProvisioningError(`Failed to create record set ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
45332
45420
  }
45333
45421
  }
45334
- async updateRecordSet(logicalId, physicalId, resourceType, properties, context) {
45422
+ async updateRecordSet(logicalId, physicalId, resourceType, properties, previousProperties, context) {
45335
45423
  this.logger.debug(`Updating Route 53 record set ${logicalId}: ${physicalId}`);
45336
45424
  const hostedZoneId = await this.resolveHostedZoneId(properties, logicalId, resourceType, physicalId);
45337
45425
  const recordName = properties["Name"];
45338
45426
  const recordType = properties["Type"];
45339
- const compositeId = packCompositeId(resourceType, logicalId, [
45427
+ const stateBorneDesired = context?.replayingState === true || context?.desiredFromAwsReadback === true;
45428
+ const recordedSegmentValues = {
45429
+ recordName: previousProperties["Name"],
45430
+ recordType: previousProperties["Type"]
45431
+ };
45432
+ const segments = [
45340
45433
  {
45341
45434
  name: "hostedZoneId",
45342
45435
  value: hostedZoneId
@@ -45349,8 +45442,15 @@ var Route53Provider = class {
45349
45442
  name: "recordType",
45350
45443
  value: recordType
45351
45444
  }
45352
- ], {
45353
- onRefusal: (message) => this.logger.warn(message),
45445
+ ];
45446
+ const sameRecordedSegment = (value, recorded) => {
45447
+ if (typeof recorded !== "string" || recorded.includes("{{resolve:")) return true;
45448
+ const normalize = (text) => text.replace(/\.$/, "").toLowerCase();
45449
+ return normalize(String(value)) === normalize(recorded);
45450
+ };
45451
+ const introducesSeparator = !stateBorneDesired && segments.some((segment) => String(segment.value).includes("|") && !sameRecordedSegment(segment.value, recordedSegmentValues[segment.name]));
45452
+ const compositeId = packCompositeId(resourceType, logicalId, segments, {
45453
+ ...!introducesSeparator && { onRefusal: (message) => this.logger.warn(message) },
45354
45454
  maskSecrets: context?.maskSecrets
45355
45455
  });
45356
45456
  try {
@@ -47607,19 +47707,25 @@ var CognitoUserPoolProvider = class {
47607
47707
  * modify or remove an existing attribute — those changes require replacement
47608
47708
  * and are rejected with ResourceUpdateNotSupportedError.
47609
47709
  *
47610
- * The `context` parameter is read for ONE thing today: `maskSecrets` (issue
47710
+ * The `context` parameter is read for two things: `maskSecrets` (issue
47611
47711
  * #1932 item 3), forwarded to `applyMfaConfig` so the MFA warnings mask a
47612
- * resolved secret the same way they do on the create path. It deliberately
47613
- * does NOT consult `desiredFromAwsReadback` -- this provider has no
47614
- * empty-collection-means-delete shape, so reading that flag would be a
47615
- * behavior change with no motivating case, and the `MfaConfiguration`
47616
- * refusal below stays UNCONDITIONALLY downgraded for the reason its own
47617
- * comment gives (a refusal against a state-borne bag leaves the resource
47618
- * un-rollbackable). Accepting the context does not change that.
47712
+ * resolved secret the same way they do on the create path; and the ORIGIN
47713
+ * of the desired bag (`replayingState` / `desiredFromAwsReadback`), which
47714
+ * decides whether the `MfaConfiguration` shape guard below refuses (a
47715
+ * template-path update) or warns (a rollback revert or `cdkd drift
47716
+ * --revert`) — issue #3728. It reads `desiredFromAwsReadback` for that
47717
+ * origin question ONLY: this provider has no empty-collection-means-delete
47718
+ * shape, so nothing here treats a readback value as a removal.
47619
47719
  */
47620
47720
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
47621
47721
  this.logger.debug(`Updating Cognito User Pool ${logicalId}: ${physicalId}`);
47622
- const mfaConfiguration = readDeclaredMfaConfiguration(properties["MfaConfiguration"], { onUnusable: (message) => this.logger.warn(message) });
47722
+ const stateBorneDesired = context?.replayingState === true || context?.desiredFromAwsReadback === true;
47723
+ let mfaConfiguration;
47724
+ try {
47725
+ mfaConfiguration = readDeclaredMfaConfiguration(properties["MfaConfiguration"], stateBorneDesired ? { onUnusable: (message) => this.logger.warn(message) } : {});
47726
+ } catch (error) {
47727
+ throw new ProvisioningError(`${error instanceof Error ? error.message : String(error)}. Nothing was applied to user pool ${physicalId}`, resourceType, logicalId, physicalId, error instanceof Error ? error : void 0);
47728
+ }
47623
47729
  this.warnOnBlankMfaConfiguration(properties["MfaConfiguration"]);
47624
47730
  const liveFirstAuthFactors = mfaPreflightNeedsLiveSignInPolicy(properties, mfaConfiguration) ? await this.readLiveAllowedFirstAuthFactors(physicalId) : void 0;
47625
47731
  this.assertMfaCombinationApplicable(logicalId, resourceType, physicalId, properties, mfaConfiguration, liveFirstAuthFactors);
@@ -51473,6 +51579,163 @@ function resolveTableIdentity(input) {
51473
51579
  };
51474
51580
  }
51475
51581
  /**
51582
+ * Describe an unresolved name by SHAPE only. Its leaves can be resolved
51583
+ * secrets, and the engine's masker matches a secret's full plaintext, which a
51584
+ * JSON rendering (escaping) or a cut (a fragment) would defeat.
51585
+ */
51586
+ function describeUnresolvedName(value) {
51587
+ if (Array.isArray(value)) return "an array";
51588
+ if (typeof value === "object" && value !== null) {
51589
+ const keys = Object.keys(value).slice(0, 5);
51590
+ return keys.length === 0 ? "an empty object" : `an object with keys [${keys.join(", ")}]`;
51591
+ }
51592
+ if (typeof value === "number") return "a number that is not a safe integer";
51593
+ return `a ${typeof value}`;
51594
+ }
51595
+ /**
51596
+ * Refuse an UPDATE whose nested `Name` renames the recorded Glue entity
51597
+ * (issue #3724).
51598
+ *
51599
+ * CDK emits the NESTED name (`TableInput.Name`, `DatabaseInput.Name`,
51600
+ * `ConnectionInput.Name`), and none of the registry schemas marks it
51601
+ * createOnly (Table marks its top-level `Name`, Database its `DatabaseName`,
51602
+ * Connection only `CatalogId`), so a rename diffs as an in-place UPDATE. For
51603
+ * a table that update is a wrong-target write: `UpdateTable` addresses the
51604
+ * table BY `TableInput.Name`, so it would rewrite whichever table holds the new
51605
+ * name — possibly one cdkd does not manage — while the state record kept the
51606
+ * old id. `UpdateDatabase` / `UpdateConnection` address the recorded name, but
51607
+ * whatever AWS does with a different name inside the definition, cdkd keeps
51608
+ * recording the old one. No arm is licensed to downgrade this on a replay: the
51609
+ * alternative is a write to a resource the record does not name.
51610
+ *
51611
+ * `ResourceUpdateNotSupportedError` is what the deploy engine's `--replace`
51612
+ * fallback reacts to, so the remedy it names is a real path. An ABSENT name
51613
+ * passes: the builders then send the recorded one. Any other non-string value
51614
+ * still reaches the wire uncoerced, so a safe integer is compared by its string
51615
+ * form (the one spelling JS and Glue cannot disagree on) and anything else is
51616
+ * refused with an untyped error, since a `--replace` CREATE would carry the
51617
+ * same value — cdkd cannot tell which entity it addresses.
51618
+ * `foldCase` is set for tables and databases, whose names Glue folds to
51619
+ * lowercase: a case-only difference addresses the same entity, and a
51620
+ * `drift --revert` bag read back from AWS carries the folded spelling. Only
51621
+ * ASCII is folded, so a guess wrong about Glue's folding over-refuses rather
51622
+ * than letting a different entity through.
51623
+ */
51624
+ function refuseNestedRename(args) {
51625
+ const { desiredName: raw, recordedName } = args;
51626
+ if (raw == null) return;
51627
+ if (!(typeof raw === "string" || typeof raw === "number" && Number.isSafeInteger(raw))) throw markNonRetryable(new ProvisioningError(`${args.field} is not a resolved name (${describeUnresolvedName(raw)}), so cdkd cannot tell which Glue ${args.noun} the update of ${args.logicalId} would address and will not send it. Declare ${args.field} as a string ('${recordedName}' keeps the recorded ${args.noun})`, args.resourceType, args.logicalId));
51628
+ const desiredName = String(raw);
51629
+ const fold = (name) => args.foldCase ? name.replace(/[A-Z]/g, (c) => c.toLowerCase()) : name;
51630
+ if (fold(desiredName) === fold(recordedName)) return;
51631
+ const remedyFlags = args.stateful ? "--replace --force-stateful-recreation" : "--replace";
51632
+ throw new ResourceUpdateNotSupportedError(args.resourceType, args.logicalId, `${args.field} changed from '${recordedName}' to '${desiredName}', and cdkd cannot rename a Glue ${args.noun} in place — ${args.consequence(desiredName)}. To rename it, re-deploy with ${remedyFlags}, which DELETEs the ${args.noun} '${recordedName}' and then CREATEs '${desiredName}' (under UpdateReplacePolicy: Retain the old one is kept instead); the CREATE fails if a ${args.noun} named '${desiredName}' already exists, after the DELETE. If the template did not change this name, the state record was left by an earlier deploy that wrote to '${desiredName}' (issue #3724): check that ${args.noun} before re-deploying. Otherwise keep the name '${recordedName}'`);
51633
+ }
51634
+ /**
51635
+ * The recorded `CatalogId` a `readCurrentState` snapshot carries back, so a
51636
+ * `drift --revert` built from that snapshot addresses the catalog the entity
51637
+ * was READ from (issue #3756). `GetDatabase` / `GetTable` / `GetConnection` do
51638
+ * not echo it, and a snapshot without it made the revert's update target this
51639
+ * account's DEFAULT catalog. Echoed only when usable: the raw value keeps drift
51640
+ * comparing like with like, and an unusable one (the account-id pseudo
51641
+ * parameter, another intrinsic) stays absent, which is what the reads
51642
+ * addressed for it.
51643
+ */
51644
+ function withRecordedCatalogId(snapshot, properties) {
51645
+ const raw = properties?.["CatalogId"];
51646
+ return catalogIdForApi(raw) === void 0 ? snapshot : {
51647
+ ...snapshot,
51648
+ CatalogId: raw
51649
+ };
51650
+ }
51651
+ /**
51652
+ * The `CatalogId` an UPDATE sends. A finite number (a YAML-numeric account id,
51653
+ * which {@link withRecordedCatalogId} can now carry into a `drift --revert`
51654
+ * bag) is stringified the way the readers and deletes do; anything else keeps
51655
+ * the pre-existing cast, so an unresolved intrinsic still reaches AWS and is
51656
+ * rejected there rather than silently defaulting to this account's catalog.
51657
+ */
51658
+ function updateCatalogId(value) {
51659
+ return typeof value === "number" && Number.isFinite(value) ? String(value) : value;
51660
+ }
51661
+ /**
51662
+ * Classify a `CatalogId`. Absent and the account-id pseudo parameter both mean
51663
+ * the caller's own catalog ({@link isAccountIdPseudoParameter}); a usable
51664
+ * literal is itself; anything else (another intrinsic, a malformed value)
51665
+ * cannot be placed.
51666
+ */
51667
+ function classifyCatalog(value) {
51668
+ if (value == null || isAccountIdPseudoParameter(value)) return { kind: "default" };
51669
+ const id = catalogIdForApi(value);
51670
+ return id === void 0 ? { kind: "unusable" } : {
51671
+ kind: "literal",
51672
+ id
51673
+ };
51674
+ }
51675
+ /**
51676
+ * Refuse an UPDATE whose `CatalogId` moves the entity to another Data Catalog
51677
+ * (issue #3756).
51678
+ *
51679
+ * Every Glue update sends the DESIRED `CatalogId` while the state record names
51680
+ * only the entity, so a changed catalog addresses the same-named entity in
51681
+ * ANOTHER catalog — rewriting it while the recorded one is left alone.
51682
+ * `AWS::Glue::Database` reaches this on every such edit (its registry schema
51683
+ * marks only `DatabaseName` createOnly); Table and Connection mark `CatalogId`
51684
+ * createOnly and reach it only when the createOnly lookup degrades.
51685
+ *
51686
+ * Equivalence is decided, not string-compared: absent and a literal equal to
51687
+ * the caller's own account are the SAME catalog, so the mixed case asks STS
51688
+ * (`callerAccountId`), which a CDK stack's `catalogId: Stack.of(this).account`
51689
+ * hits whenever the recorded side holds the pseudo parameter an import left.
51690
+ * An unusable DESIRED side is left to the wire as before, and an unusable
51691
+ * RECORDED side (an imported raw intrinsic) cannot be placed, so it warns and
51692
+ * proceeds: refusing would wedge every later deploy on a record the user
51693
+ * cannot edit.
51694
+ */
51695
+ async function refuseCatalogMove(args) {
51696
+ const recorded = classifyCatalog(args.recorded);
51697
+ const desired = classifyCatalog(args.desired);
51698
+ if (desired.kind === "unusable") return;
51699
+ if (recorded.kind === "unusable") {
51700
+ args.warn(`Glue ${args.noun} ${args.logicalId}: the recorded CatalogId is not a usable value (likely an unresolved intrinsic an import recorded), so cdkd cannot confirm the update addresses the Data Catalog the ${args.noun} was deployed to; it proceeds against ${desired.kind === "literal" ? `Data Catalog ${desired.id}` : "this account's default Data Catalog"}.`);
51701
+ return;
51702
+ }
51703
+ if (recorded.kind === "default" && desired.kind === "default") return;
51704
+ if (recorded.kind === "literal" && desired.kind === "literal") {
51705
+ if (recorded.id === desired.id) return;
51706
+ } else {
51707
+ const literal = recorded.kind === "literal" ? recorded.id : desired.id;
51708
+ let callerAccount;
51709
+ try {
51710
+ callerAccount = await args.callerAccountId();
51711
+ } catch (error) {
51712
+ throw new ProvisioningError(`Could not resolve the caller's account id (sts:GetCallerIdentity) to tell whether CatalogId ${literal} is this account's default Data Catalog, so cdkd did not update Glue ${args.noun} ${args.logicalId}: ${error instanceof Error ? error.message : String(error)}`, args.resourceType, args.logicalId, void 0, error instanceof Error ? error : void 0);
51713
+ }
51714
+ if (literal === callerAccount) return;
51715
+ }
51716
+ const describe = (side) => side.kind === "literal" ? `Data Catalog ${side.id}` : "this account's default Data Catalog";
51717
+ const remedyFlags = args.stateful ? "--replace --force-stateful-recreation" : "--replace";
51718
+ throw new ResourceUpdateNotSupportedError(args.resourceType, args.logicalId, `CatalogId moves the ${args.noun} '${args.entityName}' from ${describe(recorded)} to ${describe(desired)}, and an in-place update would address the same-named ${args.noun} in the NEW catalog while the state record kept the old one. To move it, re-deploy with ${remedyFlags}, which DELETEs the ${args.noun} from ${describe(recorded)} and then CREATEs it in ${describe(desired)} (under UpdateReplacePolicy: Retain the old one is kept instead); or keep the recorded CatalogId`);
51719
+ }
51720
+ /**
51721
+ * A memoized "which account am I" for {@link refuseCatalogMove}'s mixed case,
51722
+ * built per provider instance so the STS call happens at most once and only
51723
+ * when a catalog comparison needs it. A failure is not cached.
51724
+ */
51725
+ function makeCallerAccountResolver(region) {
51726
+ let stsClient;
51727
+ let cached;
51728
+ return async () => {
51729
+ if (cached) return cached;
51730
+ stsClient ??= new STSClient({
51731
+ ...ambientClientDefaults(),
51732
+ ...region ? { region } : {}
51733
+ });
51734
+ cached = await resolveAccountId(stsClient);
51735
+ return cached;
51736
+ };
51737
+ }
51738
+ /**
51476
51739
  * SDK Provider for AWS Glue resources
51477
51740
  *
51478
51741
  * Supports:
@@ -51485,6 +51748,7 @@ function resolveTableIdentity(input) {
51485
51748
  var GlueProvider = class {
51486
51749
  client;
51487
51750
  providerRegion = ambientRegion();
51751
+ callerAccountId = makeCallerAccountResolver(this.providerRegion);
51488
51752
  logger = getLogger().child("GlueProvider");
51489
51753
  handledProperties = /* @__PURE__ */ new Map([["AWS::Glue::Database", /* @__PURE__ */ new Set([
51490
51754
  "DatabaseInput",
@@ -51551,7 +51815,29 @@ var GlueProvider = class {
51551
51815
  this.logger.debug(`Updating Glue Database ${logicalId}: ${physicalId}`);
51552
51816
  const databaseInput = properties["DatabaseInput"];
51553
51817
  if (!databaseInput) throw new ProvisioningError(`DatabaseInput is required for Glue Database update ${logicalId}`, resourceType, logicalId, physicalId);
51554
- const catalogId = properties["CatalogId"];
51818
+ refuseNestedRename({
51819
+ resourceType,
51820
+ logicalId,
51821
+ field: "DatabaseInput.Name",
51822
+ noun: "database",
51823
+ recordedName: physicalId,
51824
+ desiredName: databaseInput["Name"],
51825
+ foldCase: true,
51826
+ consequence: () => `UpdateDatabase would address '${physicalId}' with a different name inside its definition, and the state record would keep '${physicalId}' whatever AWS did with it`,
51827
+ stateful: true
51828
+ });
51829
+ await refuseCatalogMove({
51830
+ resourceType,
51831
+ logicalId,
51832
+ noun: "database",
51833
+ entityName: physicalId,
51834
+ recorded: previousProperties?.["CatalogId"],
51835
+ desired: properties["CatalogId"],
51836
+ stateful: true,
51837
+ callerAccountId: this.callerAccountId,
51838
+ warn: (message) => this.logger.warn(message)
51839
+ });
51840
+ const catalogId = updateCatalogId(properties["CatalogId"]);
51555
51841
  const liveParameters = await this.readLiveDatabaseParameters(logicalId, resourceType, physicalId, catalogId);
51556
51842
  const previousDatabaseInput = asRecord(previousProperties?.["DatabaseInput"]);
51557
51843
  try {
@@ -51644,7 +51930,29 @@ var GlueProvider = class {
51644
51930
  const { databaseName, tableName } = decoded;
51645
51931
  const tableInput = properties["TableInput"];
51646
51932
  if (!tableInput) throw new ProvisioningError(`TableInput is required for Glue Table update ${logicalId}`, resourceType, logicalId, physicalId);
51647
- const catalogId = properties["CatalogId"];
51933
+ refuseNestedRename({
51934
+ resourceType,
51935
+ logicalId,
51936
+ field: "TableInput.Name",
51937
+ noun: "table",
51938
+ recordedName: tableName,
51939
+ desiredName: tableInput["Name"],
51940
+ foldCase: true,
51941
+ consequence: (desired) => `UpdateTable addresses the table by TableInput.Name, so it would write to the table named '${desired}' in database '${databaseName}' (possibly one cdkd does not manage) while the state record kept '${tableName}'`,
51942
+ stateful: true
51943
+ });
51944
+ await refuseCatalogMove({
51945
+ resourceType,
51946
+ logicalId,
51947
+ noun: "table",
51948
+ entityName: `${databaseName}.${tableName}`,
51949
+ recorded: previousProperties?.["CatalogId"],
51950
+ desired: properties["CatalogId"],
51951
+ stateful: true,
51952
+ callerAccountId: this.callerAccountId,
51953
+ warn: (message) => this.logger.warn(message)
51954
+ });
51955
+ const catalogId = updateCatalogId(properties["CatalogId"]);
51648
51956
  const updateIcebergKey = findIcebergTableInputKey(properties);
51649
51957
  if (updateIcebergKey !== void 0) this.logger.warn(icebergTableInputRefusalMessage(logicalId, updateIcebergKey, "update"));
51650
51958
  const live = await this.readLiveTableState(logicalId, resourceType, physicalId, databaseName, tableName, catalogId);
@@ -52155,11 +52463,10 @@ var GlueProvider = class {
52155
52463
  * `ViewExpandedText`, `TargetTable`). The table physicalId is
52156
52464
  * `databaseName|tableName`; we recover both from the split.
52157
52465
  *
52158
- * `CatalogId` is intentionally not surfaced — `GetDatabase` /
52159
- * `GetTable` do not echo it back, and cdkd state's `CatalogId` is
52160
- * usually the AWS account id (defaulted by the API). Comparator only
52161
- * descends into keys present in state, so an absent surface key cannot
52162
- * fire false drift here.
52466
+ * `CatalogId` is not READ — `GetDatabase` / `GetTable` do not echo it —
52467
+ * but the recorded usable value is carried back
52468
+ * ({@link withRecordedCatalogId}), so a `drift --revert` built from this
52469
+ * snapshot addresses the catalog it was read from.
52163
52470
  *
52164
52471
  * Returns `undefined` when the resource is gone (`EntityNotFoundException`).
52165
52472
  * Other Glue resource types (`Job`, `Crawler`, `Connection`, `Trigger`,
@@ -52170,8 +52477,14 @@ var GlueProvider = class {
52170
52477
  async readCurrentState(physicalId, _logicalId, resourceType, properties) {
52171
52478
  const catalogId = catalogIdForApi(properties?.["CatalogId"]);
52172
52479
  switch (resourceType) {
52173
- case "AWS::Glue::Database": return this.readDatabase(physicalId, catalogId);
52174
- case "AWS::Glue::Table": return this.readTable(physicalId, catalogId, properties);
52480
+ case "AWS::Glue::Database": {
52481
+ const snapshot = await this.readDatabase(physicalId, catalogId);
52482
+ return snapshot && withRecordedCatalogId(snapshot, properties);
52483
+ }
52484
+ case "AWS::Glue::Table": {
52485
+ const snapshot = await this.readTable(physicalId, catalogId, properties);
52486
+ return snapshot && withRecordedCatalogId(snapshot, properties);
52487
+ }
52175
52488
  default: return;
52176
52489
  }
52177
52490
  }
@@ -53605,6 +53918,7 @@ function toCfnCrawlerTargets(targets) {
53605
53918
  var GlueConnectionProvider = class {
53606
53919
  client;
53607
53920
  providerRegion = ambientRegion();
53921
+ callerAccountId = makeCallerAccountResolver(this.providerRegion);
53608
53922
  logger = getLogger().child("GlueConnectionProvider");
53609
53923
  handledProperties = /* @__PURE__ */ new Map([["AWS::Glue::Connection", /* @__PURE__ */ new Set(["ConnectionInput", "CatalogId"])]]);
53610
53924
  getClient() {
@@ -53635,14 +53949,36 @@ var GlueConnectionProvider = class {
53635
53949
  throw new ProvisioningError(`Failed to create Glue Connection ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
53636
53950
  }
53637
53951
  }
53638
- async update(logicalId, physicalId, resourceType, properties, _previousProperties) {
53952
+ async update(logicalId, physicalId, resourceType, properties, previousProperties) {
53639
53953
  this.logger.debug(`Updating Glue Connection ${logicalId}: ${physicalId}`);
53640
53954
  const connectionInput = properties["ConnectionInput"];
53641
53955
  if (!connectionInput) throw new ProvisioningError(`ConnectionInput is required for Glue Connection update ${logicalId}`, resourceType, logicalId, physicalId);
53642
- const catalogId = properties["CatalogId"];
53956
+ refuseNestedRename({
53957
+ resourceType,
53958
+ logicalId,
53959
+ field: "ConnectionInput.Name",
53960
+ noun: "connection",
53961
+ recordedName: physicalId,
53962
+ desiredName: connectionInput["Name"],
53963
+ foldCase: false,
53964
+ consequence: () => `UpdateConnection would address '${physicalId}' with a different name inside its definition, and the state record would keep '${physicalId}' whatever AWS did with it`,
53965
+ stateful: false
53966
+ });
53967
+ await refuseCatalogMove({
53968
+ resourceType,
53969
+ logicalId,
53970
+ noun: "connection",
53971
+ entityName: physicalId,
53972
+ recorded: previousProperties?.["CatalogId"],
53973
+ desired: properties["CatalogId"],
53974
+ stateful: false,
53975
+ callerAccountId: this.callerAccountId,
53976
+ warn: (message) => this.logger.warn(message)
53977
+ });
53978
+ const catalogId = updateCatalogId(properties["CatalogId"]);
53643
53979
  try {
53644
53980
  await this.getClient().send(new UpdateConnectionCommand({
53645
- ...catalogId && { CatalogId: catalogId },
53981
+ ...catalogId !== void 0 && { CatalogId: catalogId },
53646
53982
  Name: physicalId,
53647
53983
  ConnectionInput: buildConnectionInput(connectionInput, physicalId)
53648
53984
  }));
@@ -53691,7 +54027,7 @@ var GlueConnectionProvider = class {
53691
54027
  throw err;
53692
54028
  }
53693
54029
  if (!conn) return void 0;
53694
- return { ConnectionInput: {
54030
+ return withRecordedCatalogId({ ConnectionInput: {
53695
54031
  Name: conn.Name ?? physicalId,
53696
54032
  ConnectionType: conn.ConnectionType ?? "",
53697
54033
  Description: conn.Description ?? "",
@@ -53701,7 +54037,7 @@ var GlueConnectionProvider = class {
53701
54037
  AthenaProperties: conn.AthenaProperties ?? {},
53702
54038
  PythonProperties: conn.PythonProperties ?? {},
53703
54039
  PhysicalConnectionRequirements: conn.PhysicalConnectionRequirements ? pickDefined(conn.PhysicalConnectionRequirements) : {}
53704
- } };
54040
+ } }, properties);
53705
54041
  }
53706
54042
  async import(input) {
53707
54043
  const explicitName = input.knownPhysicalId ?? input.properties["ConnectionInput"]?.["Name"];
@@ -64868,14 +65204,24 @@ async function runDestroyForStack(stackName, state, ctx) {
64868
65204
  * as the other. The ` / ` join that first replaced it was its own defect:
64869
65205
  * pasted, it handed the second command to the first as arguments.
64870
65206
  */
64871
- const hintFor = (command, targets, label) => [...new Set(targets.map((t) => `\n${label}: ${pasteableCommand(command, [{
65207
+ const hintCommand = (command, t) => pasteableCommand(command, [{
64872
65208
  value: t,
64873
- hole: "stack"
65209
+ hole: "stack",
65210
+ opts: { plainIdent: true }
64874
65211
  }, {
64875
65212
  flag: "--stack-region",
64876
65213
  value: regionForState,
64877
- hole: "region"
64878
- }]).command}`))].join("");
65214
+ hole: "region",
65215
+ opts: { plainIdent: true }
65216
+ }]);
65217
+ const hintFor = (command, targets, label) => [...new Set(targets.map((t) => `\n${label}: ${hintCommand(command, t).command}`))].join("");
65218
+ /**
65219
+ * The ONE sentence explaining `hintFor`'s holes, for the prose BEFORE its
65220
+ * lines (go-to-k/cdkd#3759). `hintFor` prints one command per target, so a
65221
+ * per-hole `withheldTargetClause` would repeat once per line; this states the
65222
+ * rule once, and only when the gate actually withheld a value on some line.
65223
+ */
65224
+ const hintHolesClause = (targets) => targets.every((t) => hintCommand("cdkd state show", t).withheld.length === 0) ? "" : " A target that is not a plain identifier is printed as a quoted '<stack>' or '<region>' placeholder; list the records as stored with 'cdkd state list --long'.";
64879
65225
  const buildDestroySnapshot = () => {
64880
65226
  const { imports: _imports, outputReads: _outputReads, ...rest } = state;
64881
65227
  return {
@@ -65163,26 +65509,29 @@ async function runDestroyForStack(stackName, state, ctx) {
65163
65509
  const skippedSuffix = result.skippedCount > 0 ? `, ${yellow(result.skippedCount)} skipped` : "";
65164
65510
  const guardSuffix = result.guardIndeterminateCount > 0 ? `, ${yellow(result.guardIndeterminateCount)} unverified` : "";
65165
65511
  if (result.guardIndeterminateCount > 0) {
65166
- const durablePointer = ctx.eventRecorder === void 0 ? "This summary is the only record: this run wrote no deployment events, either because it is a 'cdkd state destroy' (go-to-k/cdkd#2423) or because it is a nested-stack child, neither of which threads an event recorder." : `The RESOURCE_GUARD_INDETERMINATE entries name the check and the reason and survive the run.\nRead them with: ${pasteableCommand("cdkd events", [{
65512
+ const eventsCommand = pasteableCommand("cdkd events", [{
65167
65513
  value: stackName,
65168
- hole: "stack"
65169
- }]).command}`;
65170
- logger.warn(`\n${yellow("⚠")} ${result.guardIndeterminateCount} pre-flight safety check(s) could NOT be completed during this destroy and cdkd proceeded anyway: ${[...guardIndeterminateTargets].join(", ")}. A check can be suppressed by DENYING the permission it needs, so treat this as unconfirmed rather than benign. ${durablePointer}`);
65514
+ hole: "stack",
65515
+ opts: { plainIdent: true }
65516
+ }]);
65517
+ const durablePointer = ctx.eventRecorder === void 0 ? "This summary is the only record: this run wrote no deployment events, either because it is a 'cdkd state destroy' (go-to-k/cdkd#2423) or because it is a nested-stack child, neither of which threads an event recorder." : "The RESOURCE_GUARD_INDETERMINATE entries name the check and the reason and survive the run." + withheldTargetClause(eventsCommand, "stack", "cdkd events", "This stack's name") + `\nRead them with: ${eventsCommand.command}`;
65518
+ logger.warn(`\n${yellow("⚠")} ${result.guardIndeterminateCount} pre-flight safety check(s) could NOT be completed during this destroy and cdkd proceeded anyway: ${[...guardIndeterminateTargets].map((t) => plainOrDescribed(t, "logical id")).join(", ")}. A check can be suppressed by DENYING the permission it needs, so treat this as unconfirmed rather than benign. ${durablePointer}`);
65171
65519
  }
65172
- if (!preserveState) logger.info(`\n${green("✓")} ${bold(`Stack ${stackName} destroyed`)} (${green(result.deletedCount)} deleted${retainedSuffix}${guardSuffix}, ${result.errorCount} errors)`);
65173
- else if (result.interrupted && result.errorCount === 0) logger.warn(`\n${yellow("⚠")} ${bold(`Stack ${stackName} destroy interrupted`)} (${green(result.deletedCount)} deleted${retainedSuffix}${skippedSuffix}${guardSuffix}, ${result.errorCount} errors). State preserved — re-run 'cdkd destroy' / 'cdkd state destroy' to finish.`);
65520
+ if (!preserveState) logger.info(`\n${green("✓")} ${bold(`Stack ${plainOrDescribed(stackName, "stack name")} destroyed`)} (${green(result.deletedCount)} deleted${retainedSuffix}${guardSuffix}, ${result.errorCount} errors)`);
65521
+ else if (result.interrupted && result.errorCount === 0) logger.warn(`\n${yellow("⚠")} ${bold(`Stack ${plainOrDescribed(stackName, "stack name")} destroy interrupted`)} (${green(result.deletedCount)} deleted${retainedSuffix}${skippedSuffix}${guardSuffix}, ${result.errorCount} errors). State preserved — re-run 'cdkd destroy' / 'cdkd state destroy' to finish.`);
65174
65522
  else if (result.errorCount === 0) {
65175
65523
  const targets = [...skippedStateTargets];
65176
65524
  const showHint = hintFor("cdkd state show", targets, "Inspect it with");
65177
65525
  const orphanHint = hintFor("cdkd state orphan", targets, "Drop the record with");
65178
- logger.warn(`\n${yellow("⚠")} ${bold(`Stack ${stackName} partially destroyed`)} (${green(result.deletedCount)} deleted${retainedSuffix}${skippedSuffix}${guardSuffix}, ${result.errorCount} errors). cdkd could not address the skipped resource(s), so they may still exist in AWS. Fix the physicalId in state.json and re-run, or delete them by hand and drop the records.` + showHint + orphanHint);
65526
+ logger.warn(`\n${yellow("⚠")} ${bold(`Stack ${plainOrDescribed(stackName, "stack name")} partially destroyed`)} (${green(result.deletedCount)} deleted${retainedSuffix}${skippedSuffix}${guardSuffix}, ${result.errorCount} errors). cdkd could not address the skipped resource(s), so they may still exist in AWS. Fix the physicalId in state.json and re-run, or delete them by hand and drop the records.` + hintHolesClause(targets) + showHint + orphanHint);
65179
65527
  } else {
65180
- const orphanHint = hintFor("cdkd state orphan", failedStateTargets.size > 0 ? [...failedStateTargets] : [stackName], "Drop the record with");
65528
+ const failedTargets = failedStateTargets.size > 0 ? [...failedStateTargets] : [stackName];
65529
+ const orphanHint = hintFor("cdkd state orphan", failedTargets, "Drop the record with");
65181
65530
  const skippedTargets = [...skippedStateTargets];
65182
65531
  const skippedClause = skippedTargets.length > 0 ? ` Separately, ${result.skippedCount} resource(s) were SKIPPED — cdkd could not address them, so no delete was issued and they may still exist in AWS. Fix the physicalId in state.json and re-run, or delete them by hand and drop their records.` : "";
65183
65532
  const skippedCommands = hintFor("cdkd state show", skippedTargets, "Inspect it with") + hintFor("cdkd state orphan", skippedTargets, "Drop the record with");
65184
65533
  const dedupedCommands = (lines) => [...new Set(lines.split("\n").filter((l) => l !== ""))].map((l) => `\n${l}`).join("");
65185
- logger.warn(`\n${yellow("⚠")} ${bold(`Stack ${stackName} partially destroyed`)} (${green(result.deletedCount)} deleted${retainedSuffix}${skippedSuffix}${guardSuffix}, ${red(result.errorCount)} errors). State preserved — re-run 'cdkd destroy' / 'cdkd state destroy' to clean up. If the same resource keeps failing, dropping the state record is the last resort: it removes the record without deleting AWS resources.` + skippedClause + dedupedCommands(orphanHint + skippedCommands));
65534
+ logger.warn(`\n${yellow("⚠")} ${bold(`Stack ${plainOrDescribed(stackName, "stack name")} partially destroyed`)} (${green(result.deletedCount)} deleted${retainedSuffix}${skippedSuffix}${guardSuffix}, ${red(result.errorCount)} errors). State preserved — re-run 'cdkd destroy' / 'cdkd state destroy' to clean up. If the same resource keeps failing, dropping the state record is the last resort: it removes the record without deleting AWS resources.` + skippedClause + hintHolesClause([...failedTargets, ...skippedTargets]) + dedupedCommands(orphanHint + skippedCommands));
65186
65535
  }
65187
65536
  } finally {
65188
65537
  try {
@@ -65297,16 +65646,18 @@ function getCurrentNestedStackContext() {
65297
65646
  * prevent. `tests/unit/provisioning/nested-stack-provider.test.ts` pins that
65298
65647
  * property against the union of both phrase sets.
65299
65648
  *
65300
- * @param childStackName the child's `<parent>~<childLogicalId>` state key
65649
+ * @param childStackName the child's `<parent>~<childLogicalId>` state key as
65650
+ * the PROSE may print it — the caller passes it through `plainOrDescribed`,
65651
+ * since this leaf cannot import it (go-to-k/cdkd#3759)
65301
65652
  * @param errorCount resources in the child that failed to delete
65302
65653
  * @param alsoSkippedCount resources the child SKIPPED (no AWS call issued)
65303
65654
  * @param alsoInterrupted whether the child's destroy was also interrupted
65304
65655
  */
65305
- function nestedStackChildFailureMessage(childStackName, errorCount, alsoSkippedCount, alsoInterrupted, inspectCommand) {
65656
+ function nestedStackChildFailureMessage(childStackName, errorCount, alsoSkippedCount, alsoInterrupted, inspectCommand, holeNote = "") {
65306
65657
  const extra = [];
65307
65658
  if (alsoSkippedCount > 0) extra.push(`${alsoSkippedCount} resource(s) were also skipped`);
65308
65659
  if (alsoInterrupted) extra.push("the child destroy was also interrupted");
65309
- return `Nested stack ${childStackName} failed to destroy: ${errorCount} resource(s) failed to delete` + (extra.length > 0 ? ` (${extra.join("; ")})` : "") + `. The child's state is PRESERVED and still lists them — inspect it, resolve the failure, and re-run the destroy. The parent's record of this nested stack is kept so the child stays reachable.\nInspect it with: ${inspectCommand}`;
65660
+ return `Nested stack ${childStackName} failed to destroy: ${errorCount} resource(s) failed to delete` + (extra.length > 0 ? ` (${extra.join("; ")})` : "") + ". The child's state is PRESERVED and still lists them — inspect it, resolve the failure, and re-run the destroy. The parent's record of this nested stack is kept so the child stays reachable." + holeNote + `\nInspect it with: ${inspectCommand}`;
65310
65661
  }
65311
65662
 
65312
65663
  //#endregion
@@ -65483,10 +65834,12 @@ var NestedStackProvider = class {
65483
65834
  ...ctx.destroyOptions?.resourceTimeoutByType && { resourceTimeoutByType: ctx.destroyOptions.resourceTimeoutByType }
65484
65835
  }));
65485
65836
  if (childResult.errorCount > 0) {
65486
- const failure = new Error(nestedStackChildFailureMessage(childStackName, childResult.errorCount, childResult.skippedCount, childResult.interrupted, pasteableCommand("cdkd state show", [{
65837
+ const inspect = pasteableCommand("cdkd state show", [{
65487
65838
  value: childStackName,
65488
- hole: "stack"
65489
- }]).command));
65839
+ hole: "stack",
65840
+ opts: { plainIdent: true }
65841
+ }]);
65842
+ const failure = new Error(nestedStackChildFailureMessage(plainOrDescribed(childStackName, "stack name"), childResult.errorCount, childResult.skippedCount, childResult.interrupted, inspect.command, withheldTargetClause(inspect, "stack", "cdkd state show", "The child stack's name")));
65490
65843
  throw childResult.interrupted ? markNonRetryable(failure) : failure;
65491
65844
  }
65492
65845
  if (childResult.skippedCount > 0 || childResult.interrupted) {
@@ -68983,17 +69336,38 @@ async function resolveChildStackParameters(parentStackRow, parentTemplate, paren
68983
69336
  * and the user should re-synth, exactly as `NestedStackProvider` would
68984
69337
  * fail at deploy time.
68985
69338
  */
69339
+ /**
69340
+ * The template's `Resources` entries whose logical id the state already
69341
+ * records — the only rows that can diff as an UPDATE. Own keys only, and
69342
+ * tolerant of a malformed section, since it feeds a prefetch that must never
69343
+ * throw.
69344
+ */
69345
+ function recordedTemplateResources(template, state) {
69346
+ const recorded = nullPrototypeRecord();
69347
+ const resources = template.Resources;
69348
+ const stateResources = state.resources;
69349
+ if (resources === null || typeof resources !== "object" || stateResources === null || typeof stateResources !== "object") return recorded;
69350
+ for (const [logicalId, resource] of Object.entries(resources)) if (Object.prototype.hasOwnProperty.call(stateResources, logicalId)) recorded[logicalId] = resource;
69351
+ return recorded;
69352
+ }
68986
69353
  async function buildDiffTree(args) {
68987
69354
  const { stackName, displayName, region, template, nestedTemplates, recursive, stateBackend, diffCalculator, parentHasSecretReference, parameters, canonicalizeProperties, assetRedirect, cfnFallback, previewOrphanAdoption, ancestorTemplatePaths, isNestedChild } = args;
68988
69355
  const { state, unreadable, deployRefusals } = await loadStateOrEmpty(stackName, region, stateBackend);
69356
+ const createOnlyPrefetch = prefetchCreateOnlyPropertyPaths(templateResourceTypes(recordedTemplateResources(template, state)));
68989
69357
  const secretBearingAbove = parentHasSecretReference === true || templateHasSecretDynamicReference(template);
68990
- const { changes, outputChanges, adoptedOrphans, adoptedRecords, blocking, unreadableOrphans, deployRefusals: adoptedDeployRefusals } = await computeStackDiff(state, template, region, stackName, stateBackend, diffCalculator, {
68991
- ...parameters && { parameters },
68992
- ...canonicalizeProperties && { canonicalizeProperties },
68993
- ...cfnFallback !== void 0 && { cfnFallback },
68994
- ...previewOrphanAdoption && { previewOrphanAdoption },
68995
- inheritSecretBearingTemplate: false
68996
- });
69358
+ let stackDiff;
69359
+ try {
69360
+ stackDiff = await computeStackDiff(state, template, region, stackName, stateBackend, diffCalculator, {
69361
+ ...parameters && { parameters },
69362
+ ...canonicalizeProperties && { canonicalizeProperties },
69363
+ ...cfnFallback !== void 0 && { cfnFallback },
69364
+ ...previewOrphanAdoption && { previewOrphanAdoption },
69365
+ inheritSecretBearingTemplate: false
69366
+ });
69367
+ } finally {
69368
+ createOnlyPrefetch.cancel();
69369
+ }
69370
+ const { changes, outputChanges, adoptedOrphans, adoptedRecords, blocking, unreadableOrphans, deployRefusals: adoptedDeployRefusals } = stackDiff;
68997
69371
  const stateAfterAdoption = Object.keys(adoptedRecords).length > 0 ? {
68998
69372
  ...state,
68999
69373
  resources: {
@@ -77522,14 +77896,16 @@ async function destroyCommand(stackArgs, options) {
77522
77896
  else stackTargetRegion = onlyRegion;
77523
77897
  } else if (synthRegion && refs.some((r) => r.region === synthRegion)) stackTargetRegion = synthRegion;
77524
77898
  else {
77525
- const regions = refs.map((r) => r.region ?? "(legacy)").join(", ");
77526
- throw new Error(`Stack '${stackName}' has state in multiple regions: ${regions}. Remove cdkd's record for ONE region with the command below (fill in the region), or run destroy from a CDK app whose env.region matches one of them.\nRemove one record with: ${pasteableCommand("cdkd state orphan", [{
77899
+ const regions = refs.map((r) => r.region === void 0 ? "(legacy)" : plainOrDescribed(r.region, "region")).join(", ");
77900
+ const orphanOne = pasteableCommand("cdkd state orphan", [{
77527
77901
  value: stackName,
77528
- hole: "stack"
77902
+ hole: "stack",
77903
+ opts: { plainIdent: true }
77529
77904
  }, {
77530
77905
  flag: "--stack-region",
77531
77906
  hole: "region"
77532
- }]).command}`);
77907
+ }]);
77908
+ throw new Error(`Stack ${quotedOrDescribed(stackName, "stack name")} has state in multiple regions: ${regions}. Remove cdkd's record for ONE region with the command below (fill in the region), or run destroy from a CDK app whose env.region matches one of them.` + withheldTargetClause(orphanOne, "stack", "cdkd state orphan", "This stack's name") + `\nRemove one record with: ${orphanOne.command}`);
77533
77909
  }
77534
77910
  const stateResult = await stateBackend.getState(stackName, stackTargetRegion);
77535
77911
  if (!stateResult) {
@@ -79152,7 +79528,7 @@ async function resolveIdentifierValue(entry, ctx, deps) {
79152
79528
  return entry.resolve(ctx);
79153
79529
  } catch (stateOnlyError) {
79154
79530
  if (entry.backfill && deps) return await entry.backfill(ctx, deps);
79155
- if (carriesSecretMask(ctx.attributes[entry.field])) throw new Error(maskedIdentifierAttributeReason(entry.field, ctx.logicalId));
79531
+ if (carriesSecretMask(ctx.attributes[entry.field])) throw new RepairableRefusal(maskedIdentifierAttributeReason(entry.field, ctx.logicalId), importRepairCommand(ctx.logicalId));
79156
79532
  throw stateOnlyError;
79157
79533
  }
79158
79534
  }
@@ -79167,25 +79543,19 @@ async function resolveIdentifierValue(entry, ctx, deps) {
79167
79543
  * today and would never stop being blocked by a re-import.
79168
79544
  */
79169
79545
  function maskedIdentifierAttributeReason(field, logicalId) {
79170
- return `cdkd state holds only the redaction mask ('***') at attributes.${field} for ${isPasteableIdent(logicalId) ? logicalId : `this resource (its logical id is not a plain identifier; read it with 'cdkd state show')`}, and that attribute is the value cdkd export reads as this resource type's CloudFormation import identifier (${field}); nothing masked may reach the exported template, since CloudFormation would either refuse it at IMPORT or write it onto the live resource at the next update. The mask is what 'cdkd import' writes for a Cloud Control model key it could not certify as read-only — every key when cloudformation:DescribeType was unavailable. Re-deploying does NOT clear it. Repair the record with the command below, after granting cloudformation:DescribeType, or export the stack without this resource and adopt it into CloudFormation by hand. See https://github.com/go-to-k/cdkd/issues/2932.\nRepair with: ${importRepairCommand(logicalId)}`;
79546
+ return `cdkd state holds only the redaction mask ('***') at attributes.${field} for ${isPasteableIdent(logicalId) ? logicalId : `this resource (its logical id is not a plain identifier; read it with 'cdkd state show')`}, and that attribute is the value cdkd export reads as this resource type's CloudFormation import identifier (${field}); nothing masked may reach the exported template, since CloudFormation would either refuse it at IMPORT or write it onto the live resource at the next update. The mask is what 'cdkd import' writes for a Cloud Control model key it could not certify as read-only — every key when cloudformation:DescribeType was unavailable. Re-deploying does NOT clear it. Repair the record with the command below, after granting cloudformation:DescribeType, or export the stack without this resource and adopt it into CloudFormation by hand. See https://github.com/go-to-k/cdkd/issues/2932.`;
79171
79547
  }
79172
79548
  /**
79173
- * The `cdkd import` line `maskedIdentifierAttributeReason` tells the operator
79174
- * to run.
79549
+ * The `cdkd import` command a redaction-mask refusal carries in
79550
+ * {@link BlockedResource.repair}, which `groupBlockedReasons` prints on the
79551
+ * row's one `Repair with:` line.
79175
79552
  *
79176
- * It is a named helper for ONE caller on purpose. A SECOND site prints the same
79177
- * remedy — `buildImportPlan`'s resolved-identifier refusal — written
79178
- * independently, and it still carries go-to-k/cdkd#3363's shape: a prose
79179
- * `'...'` span holding a `cdkd` verb AND an interpolation, its opening quote in
79180
- * one concatenated literal and its interpolation in the next. The source fence
79181
- * found it only once its `+` runs were folded; a grep for the gate's name never
79182
- * would have.
79183
- *
79184
- * That site is NOT routed here, deliberately: `export.ts` is one of the
79185
- * go-to-k/cdkd#3436 own-copy gates the maintainer asked to land in a follow-up
79186
- * PR rather than widen go-to-k/cdkd#3613. The fence carries an EXEMPTION naming
79187
- * it, so this helper gains its second caller in the PR that closes the site —
79188
- * and the exemption goes stale in the same commit.
79553
+ * Two refusals carry this remedy, and both build it here so there is one
79554
+ * spelling of its gate: `resolveIdentifierValue`'s masked-attribute refusal
79555
+ * (a `RepairableRefusal` beside `maskedIdentifierAttributeReason`'s prose) and
79556
+ * `buildImportPlan`'s resolved-identifier refusal (go-to-k/cdkd#3736). The
79557
+ * second used to spell it independently inside a prose `'...'` span with the
79558
+ * logical id interpolated (go-to-k/cdkd#3363's shape).
79189
79559
  *
79190
79560
  * The POSITIONAL is a hole on purpose, and the load-bearing half is what it is
79191
79561
  * NOT: an earlier cut passed the LOGICAL ID there, producing a command that
@@ -79193,11 +79563,10 @@ function maskedIdentifierAttributeReason(field, logicalId) {
79193
79563
  * check can see that — both spellings are equally well-formed to one — and only
79194
79564
  * reading the command can.
79195
79565
  *
79196
- * It is a hole rather than a name because its caller,
79197
- * {@link maskedIdentifierAttributeReason}, has no stack name in scope at all.
79198
- * (`buildImportPlan` does have one, so when that site joins this helper it
79199
- * could fill it — a separate change with its own gating question, and not a
79200
- * reason to grow two spellings here.)
79566
+ * It is a hole rather than a name because one caller,
79567
+ * `resolveIdentifierValue`, has no stack name in scope at all.
79568
+ * (`buildImportPlan` does have one, but filling it is a separate gating
79569
+ * question, and a second spelling here is what this helper exists to avoid.)
79201
79570
  */
79202
79571
  function importRepairCommand(logicalId) {
79203
79572
  const named = isPasteableIdent(logicalId);
@@ -79677,6 +80046,19 @@ async function assertCfnStackAbsent(cfnClient, stackName) {
79677
80046
  }
79678
80047
  }
79679
80048
  /**
80049
+ * A resolver refusal that carries a repair command, so the `buildImportPlan`
80050
+ * catch can move it into {@link BlockedResource.repair} instead of the
80051
+ * message text.
80052
+ */
80053
+ var RepairableRefusal = class extends Error {
80054
+ repair;
80055
+ constructor(message, repair) {
80056
+ super(message);
80057
+ this.name = "RepairableRefusal";
80058
+ this.repair = repair;
80059
+ }
80060
+ };
80061
+ /**
79680
80062
  * Returns true when a resource type is non-importable BUT can be handled
79681
80063
  * by the phase-2 CREATE path. Today this is exactly `Custom::*` and the
79682
80064
  * untyped CDK form `AWS::CloudFormation::CustomResource` (both are
@@ -80183,7 +80565,8 @@ async function buildImportPlan(state, template, cfnClient, parentStackName, opti
80183
80565
  blocked.push({
80184
80566
  logicalId,
80185
80567
  resourceType,
80186
- reason: "could not resolve resource identifier: " + (err instanceof Error ? err.message : String(err))
80568
+ reason: "could not resolve resource identifier: " + (err instanceof Error ? err.message : String(err)),
80569
+ ...err instanceof RepairableRefusal ? { repair: err.repair } : {}
80187
80570
  });
80188
80571
  continue;
80189
80572
  }
@@ -80192,7 +80575,8 @@ async function buildImportPlan(state, template, cfnClient, parentStackName, opti
80192
80575
  blocked.push({
80193
80576
  logicalId,
80194
80577
  resourceType,
80195
- reason: `the CloudFormation import identifier cdkd resolved for this resource is the redaction mask ('***'), so the exported template would declare the mask as the resource's identity — CloudFormation would either refuse it at IMPORT or write it onto the live resource at the next update. cdkd state holds only the mask where the identifier should be (a masked attribute, or a masked physical id). Repair the record ('cdkd import <stack> --resource ${logicalId}=<physicalId> --force', granting cloudformation:DescribeType first if the import warned that it could not read the schema), or export the stack without this resource and adopt it into CloudFormation by hand. See https://github.com/go-to-k/cdkd/issues/2932.`
80578
+ reason: "the CloudFormation import identifier cdkd resolved for this resource is the redaction mask ('***'), so the exported template would declare the mask as the resource's identity — CloudFormation would either refuse it at IMPORT or write it onto the live resource at the next update. cdkd state holds only the mask where the identifier should be (a masked attribute, or a masked physical id). Repair the record with the command below, granting cloudformation:DescribeType first if the import warned that it could not read the schema, or export the stack without this resource and adopt it into CloudFormation by hand. See https://github.com/go-to-k/cdkd/issues/2932.",
80579
+ repair: importRepairCommand(logicalId)
80196
80580
  });
80197
80581
  continue;
80198
80582
  }
@@ -80241,10 +80625,29 @@ function groupBlockedReasons(blocked) {
80241
80625
  resourceType: b.resourceType,
80242
80626
  reasons: []
80243
80627
  };
80244
- entry.reasons.push(b.reason);
80628
+ const text = displaySafe(b.reason);
80629
+ entry.reasons.push(b.repair === void 0 ? text : `${text}\nRepair with: ${b.repair}`);
80245
80630
  byResource.set(b.logicalId, entry);
80246
80631
  }
80247
- return [...byResource].map(([logicalId, { resourceType, reasons }]) => reasons.length === 1 ? ` - ${logicalId} (${resourceType}): ${reasons[0]}` : ` - ${logicalId} (${resourceType}):\n${reasons.map((r) => ` - ${r}`).join("\n")}`);
80632
+ return [...byResource].map(([logicalId, { resourceType, reasons }]) => {
80633
+ const header = `${blockedRowId(logicalId)} (${blockedRowType(resourceType)})`;
80634
+ return reasons.length === 1 ? ` - ${header}: ${reasons[0]}` : ` - ${header}:\n${reasons.map((r) => ` - ${r}`).join("\n")}`;
80635
+ });
80636
+ }
80637
+ /**
80638
+ * A blocked row's identifiers come from the synthesized template's keys and
80639
+ * print beside reasons that end in a labelled `Repair with:` line, so the row
80640
+ * names them only in a shape that cannot forge that line — by a newline, or by
80641
+ * interior padding that wraps on screen (go-to-k/cdkd#3736). A CloudFormation
80642
+ * logical id is always a plain identifier, and a type always matches
80643
+ * `BLOCKED_ROW_TYPE`, so a real template loses nothing.
80644
+ */
80645
+ function blockedRowId(logicalId) {
80646
+ return isPasteableIdent(logicalId) ? logicalId : "a resource whose logical id is not a plain identifier";
80647
+ }
80648
+ const BLOCKED_ROW_TYPE = /^[A-Za-z0-9]+(?:::[A-Za-z0-9_@-]+)+$/;
80649
+ function blockedRowType(resourceType) {
80650
+ return BLOCKED_ROW_TYPE.test(resourceType) ? resourceType : "an unrecognized type";
80248
80651
  }
80249
80652
  /**
80250
80653
  * Build the `ResourceIdentifier` map CloudFormation IMPORT expects in
@@ -99291,4 +99694,4 @@ function buildProgram() {
99291
99694
 
99292
99695
  //#endregion
99293
99696
  export { buildProgram };
99294
- //# sourceMappingURL=program-WCBWtvb0.js.map
99697
+ //# sourceMappingURL=program-DP27UiVh.js.map