@go-to-k/cdkd 0.310.0 → 0.312.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +2 -2
  2. package/dist/{asg-provider-I1bFHVsO.js → asg-provider-CVLTQtQr.js} +5 -3
  3. package/dist/asg-provider-CVLTQtQr.js.map +1 -0
  4. package/dist/cli.js +2 -2
  5. package/dist/{iam-policy-targets-D94wh05t.js → created-resource-sink-BT2Dfnpt.js} +51 -2
  6. package/dist/created-resource-sink-BT2Dfnpt.js.map +1 -0
  7. package/dist/{db-instance-modify-retry-BUG7NMWS.js → db-instance-modify-retry-D7wXWxVo.js} +2 -2
  8. package/dist/{db-instance-modify-retry-BUG7NMWS.js.map → db-instance-modify-retry-D7wXWxVo.js.map} +1 -1
  9. package/dist/{deploy-engine-DZO2SOQJ.js → deploy-engine-CVNzK__j.js} +914 -185
  10. package/dist/deploy-engine-CVNzK__j.js.map +1 -0
  11. package/dist/{iam-role-provider-BWw7I-SZ.js → iam-role-provider-DM7bN9m1.js} +5 -3
  12. package/dist/iam-role-provider-DM7bN9m1.js.map +1 -0
  13. package/dist/index.d.ts +9 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +3 -3
  16. package/dist/{neptune-provider-8kjFmR1l.js → neptune-provider-qdoLunCh.js} +55 -6
  17. package/dist/neptune-provider-qdoLunCh.js.map +1 -0
  18. package/dist/{program-CS420Kx9.js → program-Nk_95KKk.js} +37 -19
  19. package/dist/program-Nk_95KKk.js.map +1 -0
  20. package/dist/{provider-classes-KsrvwO_r.js → provider-classes-Dz0pYmlJ.js} +2099 -217
  21. package/dist/provider-classes-Dz0pYmlJ.js.map +1 -0
  22. package/dist/{rds-provider-BqwTQeS-.js → rds-provider-CXSZq1c8.js} +28 -14
  23. package/dist/rds-provider-CXSZq1c8.js.map +1 -0
  24. package/dist/{version-QOtLxcEo.js → version-BqCEt5Wu.js} +2 -2
  25. package/dist/{version-QOtLxcEo.js.map → version-BqCEt5Wu.js.map} +1 -1
  26. package/package.json +2 -2
  27. package/dist/asg-provider-I1bFHVsO.js.map +0 -1
  28. package/dist/deploy-engine-DZO2SOQJ.js.map +0 -1
  29. package/dist/iam-policy-targets-D94wh05t.js.map +0 -1
  30. package/dist/iam-role-provider-BWw7I-SZ.js.map +0 -1
  31. package/dist/neptune-provider-8kjFmR1l.js.map +0 -1
  32. package/dist/program-CS420Kx9.js.map +0 -1
  33. package/dist/provider-classes-KsrvwO_r.js.map +0 -1
  34. package/dist/rds-provider-BqwTQeS-.js.map +0 -1
@@ -1,14 +1,14 @@
1
1
  import { r as getAwsClients, s as withApiGatewayPacing } from "./aws-clients-Bu9EMILF.js";
2
2
  import { B as SECRET_REF_MAX_CODE_POINTS, C as generateResourceName, D as getCurrentStackInstanceId, J as displayStackName, K as displayIdent, M as recordedGeneratedNameKept, Q as safeMsg, T as generatedNameFor, Y as isPasteableIdent, Z as plainIdentOr, a as safeStringify, i as isAwsAuthoredFailure, k as isBeforeStateRead, n as describeAwsFailure, q as displaySafe, r as describeAwsStatusReason, s as getLogger, t as VERBOSE_POINTER, w as generateResourceNameWithFallback } from "./aws-failure-text-NSBdKPYK.js";
3
- import { $ as userPoolProtectionSite, $t as injectiveKeyPrefix, A as isPlainImportJson, C as setBounded, Cr as markNonRetryable, Ct as createMaskedLogSinks, D as markNotGone, Dn as ResourceUpdateNotSupportedError, Dt as maskerOrIdentity, Et as maskDeep, F as refusalTypeShown, Ft as isAuxiliaryFailure, G as isTerminalDbWaitStatus, H as deleteWithProtectionCompensation, Hn as hasClauseBreak, I as remedyLogicalId, J as observeThenDisableProtection, K as isTerminalDeleteFailure, Kn as plainOrDescribed, L as resolveExplicitPhysicalId, Lt as markAuxiliaryFailure, M as isQuotableImportValue, N as normalizeAwsTagsToCfn, O as assertRegionMatch, Ot as withDerivedNameMasks, P as quotedRemedyLogicalId, Qt as injectiveKey, Rt as markCreatedBeforeFailure, S as replayedSendWindow, Sr as isThrottlingError, St as stateOrphanRecordRemedy, T as RESOURCE_NOT_FOUND, Tn as ProvisioningError, Tr as wrapMaskedAwsError, Tt as isSecretDerivedValue, Un as isInertUnquoted, V as deleteAfterProtectionFlipWithFinalSnapshot, Vn as commandHole, Wn as pasteableCommand, X as rdsFamilyProtectionSite, Xn as withheldTargetClause, Y as protectionFlipKey, Yn as shellQuote, _ as RecentIdSet, _r as isRetryableTransientError, _t as compositeIdSkipResult, a as onlySecretDerived, an as ambientCredentialConfig, ar as markNameCollision, at as protectedReplacementAdvice, b as isInsideWindow, br as THROTTLING_ERROR_NAMES, c as recordedPrincipalsRepair, cn as AccountIdUnavailableError, dr as explicitDenyPolicyKind, ft as isInterruptedWaitError, g as AmbiguousCreateLatch, gt as compositeIdSeparatorRefusal, h as AMBIGUOUS_LATCH_TTL_MS, hr as isExplicitDenyError, ht as compositeIdFormatMessage, i as holdsSecretDerivedEntry, in as ambientClientDefaults, it as pasteableAwsCommand, j as isPlainImportValue, jt as clearCreatedBeforeFailure, k as VALUE_NOT_SHOWN, mr as isAmbiguousOutcomeError, mt as canonicalizeRoute53QueryName, nt as WITHHELD_AWS_COMMAND, o as readPrincipalLists, on as clientDefaultsFor, or as markReplayMayCollide, ot as renderDisableCommand, p as withRemovalDefaults, pt as startInterruptWatch, q as logGroupProtectionSite, r as SECRET_DERIVED_READ_LIVE, rn as withPasteableAwsProfile, rt as isAwsCliLiteral, sn as credentialFingerprint, sr as relayedNameCollision, st as ElapsedBudgetRegistry, tt as UNNAMEABLE_ID_CLAUSE, u as clearOnUpdateRemoval, un as CdkdError, v as ambiguousAttemptLead, vr as isTransientServerError, vt as logicalIdShown, w as withoutServerErrorRetries, wr as markRedactedCause, wt as createMaskedRetryLogger, x as replayFollowedAmbiguousAttempt, xr as isMarkedNonRetryable, xt as segmentAfterAnchor, y as createAttemptKey, yt as packCompositeId, z as ProtectionFlipRegistry, zt as markSupersedesEarlierCreate } from "./iam-policy-targets-D94wh05t.js";
3
+ import { $ as logGroupProtectionSite, $n as plainOrDescribed, A as withoutServerErrorRetries, Ar as markNonRetryable, At as createMaskedLogSinks, B as quotedRemedyLogicalId, C as RecentIdSet, Ct as compositeIdSeparatorRefusal, D as replayFollowedAmbiguousAttempt, Dr as THROTTLING_ERROR_NAMES, E as isInsideWindow, Et as packCompositeId, F as VALUE_NOT_SHOWN, Ft as withDerivedNameMasks, G as ProtectionFlipRegistry, Gt as markCreatedBeforeFailure, H as remedyLogicalId, Ht as isAuxiliaryFailure, I as isPlainImportJson, J as deleteWithProtectionCompensation, Jn as commandHole, Kt as markSupersedesEarlierCreate, L as isPlainImportValue, Mn as ProvisioningError, Mr as wrapMaskedAwsError, Mt as isSecretDerivedValue, N as markNotGone, Nt as maskDeep, O as replayedSendWindow, Or as isMarkedNonRetryable, Ot as segmentAfterAnchor, P as assertRegionMatch, Pn as ResourceUpdateNotSupportedError, Pt as maskerOrIdentity, Q as isTerminalDeleteFailure, R as isQuotableImportValue, Rt as clearCreatedBeforeFailure, S as AmbiguousCreateLatch, Sr as isExplicitDenyError, St as compositeIdFormatMessage, T as createAttemptKey, Tr as isTransientServerError, Tt as logicalIdShown, U as resolveExplicitPhysicalId, V as refusalTypeShown, Wt as markAuxiliaryFailure, Xn as isInertUnquoted, Yn as hasClauseBreak, Z as isTerminalDbWaitStatus, Zn as pasteableCommand, _n as CdkdError, a as retractCreatedResource, an as injectiveKey, at as userPoolProtectionSite, bt as startInterruptWatch, c as SECRET_DERIVED_READ_LIVE, ct as WITHHELD_AWS_COMMAND, d as readPrincipalLists, dn as ambientClientDefaults, dt as protectedReplacementAdvice, et as observeThenDisableProtection, fn as ambientCredentialConfig, fr as markNameCollision, ft as renderDisableCommand, h as clearOnUpdateRemoval, hn as AccountIdUnavailableError, i as reportCreatedResource, j as RESOURCE_NOT_FOUND, jr as markRedactedCause, jt as createMaskedRetryLogger, k as setBounded, kr as isThrottlingError, kt as stateOrphanRecordRemedy, l as holdsSecretDerivedEntry, lt as isAwsCliLiteral, mn as credentialFingerprint, mr as relayedNameCollision, mt as ElapsedBudgetRegistry, nr as shellQuote, nt as rdsFamilyProtectionSite, on as injectiveKeyPrefix, p as recordedPrincipalsRepair, pn as clientDefaultsFor, pr as markReplayMayCollide, pt as ElapsedBudget, q as deleteAfterProtectionFlipWithFinalSnapshot, rr as withheldTargetClause, st as UNNAMEABLE_ID_CLAUSE, tt as protectionFlipKey, u as onlySecretDerived, un as withPasteableAwsProfile, ut as pasteableAwsCommand, v as removedTemplateKeys, vr as explicitDenyPolicyKind, w as ambiguousAttemptLead, wr as isRetryableTransientError, wt as compositeIdSkipResult, x as AMBIGUOUS_LATCH_TTL_MS, xr as isAmbiguousOutcomeError, xt as canonicalizeRoute53QueryName, y as withRemovalDefaults, yt as isInterruptedWaitError, z as normalizeAwsTagsToCfn } from "./created-resource-sink-BT2Dfnpt.js";
4
4
  import { c as ambientRegion } from "./aws-client-defaults-BX9Y_fKp.js";
5
- import { $l as stringifyValue, $s as readConfigString, $t as lookupEachName, A as beginDeleteWait, Aa as s3BucketRegionalDomainName, Ai as isRefusedByName, Ar as awaitInFlightSibling, As as withheldAccountClause, B as waitForIndexesSettled, Bi as stringifyIfAssigned, Bt as getCurrentNestedStackContext, C as planContributorInsightsOp, Cd as resolveAssemblyPath, Co as accountArgs, D as reverseMapTableContributorInsights, Da as s3BucketArn, E as reverseMapIndexContributorInsights, Ea as isSettledInstanceState, Ec as passedNoEchoParametersOf, Ei as acquireIdempotencyToken, F as DELETE_INDEX_WAIT_PROCEED_NOTE, Fc as snapshotPassProvenance, Fi as readTagList, G as sendTtlWaitingOutRateLimit, Gs as isWarmThroughputDecrease, H as isTtlRateLimited, I as INDEX_SETTLE_POLL_INTERVAL_MS, Ia as outputReferenceProvenanceOf, Ii as refuseMalformedDesiredTags, In as REDACTED_DELETE_ADDRESS_SKIP_REASON, It as revertNestedChildFromJournal, J as hasCdkAutoDeleteTag, Js as coerceCfnBoolean, K as ECR_AUTO_DELETE_IMAGES_TAG, Ki as waitForLambdaFunctionActive, Ks as toCfnInteger, L as deleteTableWithIndexBusyRetry, Li as tagMapAsList, Ln as isRedactedRecordedValue, M as deleteBudgetKey, Mc as markSameGenerationBag, Mi as DBPROXY_TAGS_WHAT, Mr as getStackRecords, Ms as findSilentDropProperties, N as resolveDynamoDbDeleteBudgetClock, Nc as redactForState, Ni as DBPROXY_TAG_OPTIONS, Nt as getNestedRevertRun, O as stripIndexContributorInsights, Oa as s3BucketDomainName, P as resolveDynamoDbDeleteBudgetMs, Pc as recordReferenceProvenanceEntry, Pi as planTagDiff, Pn as getCurrentProducerRegions, Pr as replacedDeleteFailureVerdict, Ps as withoutSilentDropProperties, Qc as withoutPhysicalNameCopies, Qo as refuseMalformedNestedChildOutputs, Qs as configStringRefusal, Qt as isAccessDeniedError, R as maskedLogger, Rc as hasMaskableValues, Ri as tagPlanWarning, Rn as redactedDeleteAddressFields, S as indexDeclaresContributorInsights, Sd as renderAssemblyPathEscape, Si as earliestDefined, T as readContributorInsightsSpec, U as planTimeToLive, Us as WARM_THROUGHPUT_MEMBERS, V as isTtlAlreadyDisabled, Vt as withNestedStackContext, W as readTtlSpec, Ws as coerceWarmThroughput$1, X as getPriorAttempts, Xi as getCurrentResourceSecrets, Xl as timePhase, Xs as configBooleanRefusal, Y as isTruthyCfnBoolean, Ys as coerceCfnInteger, Z as markRefusedBeforeApplying, Zs as configIntegerRefusal, Zt as chunks, _ as streamMemberRefusals, _a as reDisableInstanceApiTermination, b as CONTRIBUTOR_INSIGHTS_KEY, bd as describeFileReadFailure, c as DynamoDBGlobalTableProvider, cc as blockDeviceMappingsDifferOnlyInDeleteOnTermination, cl as physicalNameCopiesOf, ct as listNestedTemplateRows, d as declaredStreamPolicyMember, da as isWaitAbandonedError, dc as emrClusterInstancesReplaced, ec as replayWarn, el as dynamicReferenceTokens, en as withApiLimit, f as declaredStreamTagsMember, fa as markWaitAbandoned, fc as canonicalLambdaFunctionName, fl as carriesSecretMask, g as streamDeclaresTags, ga as observeThenDisableInstanceApiTermination, go as passedParameterClassesOf, gt as enclosingDeadlineRemainingMs, h as streamDeclaresPolicy, ha as isTerminationProtectionPropagationError, ht as enclosingDeadlineExpired, ia as unchangedBehindSecretReference, iu as rewriteTemplateAssetReferences, j as compensateRemovedDeletionProtection, ja as s3BucketWebsiteUrl, ji as markDeleteStarted, k as DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS, ka as s3BucketDualStackDomainName, l as STREAM_POLICY_KEY, la as deleteSkipReason, li as secretNamedAttributeNamesOf, ll as recordAttributeIdCopy, lt as renderNestedTemplateTreeDefect, m as reverseMapStreamPolicy, ma as ec2InstanceProtectionSite, mc as sameLambdaFunctionAddress, na as slowCcOperationTimeoutMs, nc as requireConfigObject, no as getAccountInfo, od as canonicalizeRegion, ot as findNestedTemplateTreeDefect, p as planStreamMemberOps, pc as lambdaFunctionNameForMask, po as parseWebACLArn, q as S3_AUTO_DELETE_OBJECTS_TAG, qs as toFiniteNumber, ra as onlySecretReferencesDiffer, rc as requireConfigString, rd as decodeListingKey, sc as withRetry, sd as derivePartitionAndUrlSuffix, sl as recoverMaskedOutput, st as isAbsoluteAssetPath, t as DeployEngine, tc as requireConfigArray, u as STREAM_TAGS_KEY, ua as withIndeterminateGuard, uc as ecsServiceLaunchTypeSetOrChanged, v as streamPolicyMatchesDeclared, va as reEnableInstanceApiTermination, vs as refuseMalformedResourcesForDestroy, w as planIndexContributorInsightsOps, wi as reserveStackCreateToken, x as contributorInsightsRefusals, xd as displayAssemblyPath, y as streamTagsMatchDeclared, z as relayedIndexBusyDelete, zi as definedAttributes, zn as redactedDeleteAddressSkip } from "./deploy-engine-DZO2SOQJ.js";
6
- import { n as collectInlinePolicyNamesManagedBySiblings, r as IamCreateClientCache, t as IAMRoleProvider } from "./iam-role-provider-BWw7I-SZ.js";
7
- import { a as resolveSecretDerivedPrincipals, i as isResolvableSecretPrincipalList, n as runDestroyForStack, o as canonicalizeIpProtocolValue, r as holdsUnprovenSecretPrincipalToken, s as resolvedResourceTimeoutMs, t as canonicalizeIpv4Cidr } from "./program-CS420Kx9.js";
8
- import { t as modifyDbInstanceWhenAvailable } from "./db-instance-modify-retry-BUG7NMWS.js";
9
- import { RDSProvider } from "./rds-provider-BqwTQeS-.js";
10
- import { NeptuneProvider } from "./neptune-provider-8kjFmR1l.js";
11
- import { ASGProvider } from "./asg-provider-I1bFHVsO.js";
5
+ import { $ as getPriorAttempts, $s as isWarmThroughputDecrease, A as DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS, Ad as renderAssemblyPathEscape, Ai as acquireIdempotencyToken, Ao as accountArgs, B as deleteTableWithIndexBusyRetry, Bc as markSameGenerationBag, Bi as refuseMalformedDesiredTags, Bn as isRedactedRecordedValue, Bs as findSilentDropProperties, C as planContributorInsightsOp, Co as passedParameterClassesOf, D as reverseMapTableContributorInsights, E as reverseMapIndexContributorInsights, Ei as earliestDefined, F as resolveDynamoDbDeleteBudgetClock, Fa as s3BucketDomainName, Fi as markDeleteStarted, Fr as getStackRecords, G as isTtlRateLimited, H as relayedIndexBusyDelete, Hc as recordReferenceProvenanceEntry, Hi as tagPlanWarning, Hn as redactedDeleteAddressSkip, Hs as withoutSilentDropProperties, I as resolveDynamoDbDeleteBudgetMs, Ia as s3BucketDualStackDomainName, Ii as DBPROXY_TAGS_WHAT, It as getNestedRevertRun, J as sendTtlWaitingOutRateLimit, K as planTimeToLive, Kc as hasMaskableValues, L as DELETE_INDEX_WAIT_PROCEED_NOTE, La as s3BucketRegionalDomainName, Li as DBPROXY_TAG_OPTIONS, Ln as getCurrentProducerRegions, Lr as replacedDeleteFailureVerdict, Ls as withheldAccountClause, M as beginDeleteWait, N as compensateRemovedDeletionProtection, Na as isSettledInstanceState, Nr as awaitInFlightSibling, O as stripIndexContributorInsights, Od as describeFileReadFailure, Oi as reserveStackCreateToken, P as deleteBudgetKey, Pa as s3BucketArn, Pc as passedNoEchoParametersOf, Pi as isRefusedByName, Q as isTruthyCfnBoolean, Qs as coerceWarmThroughput$1, R as INDEX_SETTLE_POLL_INTERVAL_MS, Ra as s3BucketWebsiteUrl, Ri as planTagDiff, S as indexDeclaresContributorInsights, Sa as reEnableInstanceApiTermination, Sc as sameLambdaFunctionAddress, T as readContributorInsightsSpec, Ta as isUsableAttemptedName, Ts as refuseMalformedResourcesForDestroy, U as waitForIndexesSettled, Ua as outputReferenceProvenanceOf, Uc as snapshotPassProvenance, Ui as definedAttributes, Ut as getCurrentNestedStackContext, V as maskedLogger, Vc as redactForState, Vi as tagMapAsList, Vn as redactedDeleteAddressFields, W as isTtlAlreadyDisabled, Wi as stringifyIfAssigned, Wt as withNestedStackContext, X as S3_AUTO_DELETE_OBJECTS_TAG, Xi as waitForLambdaFunctionActive, Y as ECR_AUTO_DELETE_IMAGES_TAG, Z as hasCdkAutoDeleteTag, Zs as WARM_THROUGHPUT_MEMBERS, _ as streamMemberRefusals, _l as recordAttributeIdCopy, ac as configIntegerRefusal, as as refuseMalformedNestedChildOutputs, b as CONTRIBUTOR_INSIGHTS_KEY, ba as observeThenDisableInstanceApiTermination, bc as canonicalLambdaFunctionName, bl as carriesSecretMask, bo as parseWebACLArn, c as DynamoDBGlobalTableProvider, ca as unchangedBehindSecretReference, cc as replayWarn, cl as dynamicReferenceTokens, d as declaredStreamPolicyMember, dc as requireConfigString, dd as decodeListingKey, dt as listNestedTemplateRows, ea as getCurrentResourceSecrets, ec as toCfnInteger, en as chunks, et as markRefusedBeforeApplying, f as declaredStreamTagsMember, ft as renderNestedTemplateTreeDefect, fu as rewriteTemplateAssetReferences, g as streamDeclaresTags, ga as markWaitAbandoned, gc as blockDeviceMappingsDifferOnlyInDeleteOnTermination, gl as physicalNameCopiesOf, h as streamDeclaresPolicy, ha as isWaitAbandonedError, hc as withRetry, hd as derivePartitionAndUrlSuffix, hl as recoverMaskedOutput, ic as configBooleanRefusal, iu as timePhase, j as DYNAMODB_DELETE_POLL_COST_MS, jd as resolveAssemblyPath, k as DYNAMODB_DELETE_DEADLINE_MARGIN_MS, kd as displayAssemblyPath, l as STREAM_POLICY_KEY, lc as requireConfigArray, lo as getAccountInfo, lt as findNestedTemplateTreeDefect, m as reverseMapStreamPolicy, ma as withIndeterminateGuard, md as canonicalizeRegion, nc as coerceCfnBoolean, nn as lookupEachName, oa as slowCcOperationTimeoutMs, oc as configStringRefusal, ol as withoutPhysicalNameCopies, p as planStreamMemberOps, pa as deleteSkipReason, pi as secretNamedAttributeNamesOf, q as readTtlSpec, rc as coerceCfnInteger, rn as withApiLimit, sa as onlySecretReferencesDiffer, sc as readConfigString, su as stringifyValue, t as DeployEngine, tc as toFiniteNumber, tn as isAccessDeniedError, u as STREAM_TAGS_KEY, uc as requireConfigObject, ut as isAbsoluteAssetPath, v as streamPolicyMatchesDeclared, va as ec2InstanceProtectionSite, vc as ecsServiceLaunchTypeSetOrChanged, vt as enclosingDeadlineExpired, w as planIndexContributorInsightsOps, x as contributorInsightsRefusals, xa as reDisableInstanceApiTermination, xc as lambdaFunctionNameForMask, y as streamTagsMatchDeclared, ya as isTerminationProtectionPropagationError, yc as emrClusterInstancesReplaced, yt as enclosingDeadlineRemainingMs, z as TABLE_INDEX_NOUN, zi as readTagList, zn as REDACTED_DELETE_ADDRESS_SKIP_REASON, zt as revertNestedChildFromJournal } from "./deploy-engine-CVNzK__j.js";
6
+ import { n as collectInlinePolicyNamesManagedBySiblings, r as IamCreateClientCache, t as IAMRoleProvider } from "./iam-role-provider-DM7bN9m1.js";
7
+ import { a as resolveSecretDerivedPrincipals, i as isResolvableSecretPrincipalList, n as runDestroyForStack, o as canonicalizeIpProtocolValue, r as holdsUnprovenSecretPrincipalToken, s as resolvedResourceTimeoutMs, t as canonicalizeIpv4Cidr } from "./program-Nk_95KKk.js";
8
+ import { t as modifyDbInstanceWhenAvailable } from "./db-instance-modify-retry-D7wXWxVo.js";
9
+ import { RDSProvider } from "./rds-provider-CXSZq1c8.js";
10
+ import { NeptuneProvider } from "./neptune-provider-qdoLunCh.js";
11
+ import { ASGProvider } from "./asg-provider-CVLTQtQr.js";
12
12
  import { createHash, randomUUID } from "node:crypto";
13
13
  import { 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, ListBucketsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client } from "@aws-sdk/client-s3";
14
14
  import { AddRoleToInstanceProfileCommand, AddUserToGroupCommand, AttachGroupPolicyCommand, AttachRolePolicyCommand, AttachUserPolicyCommand, CreateAccessKeyCommand, CreateGroupCommand, CreateInstanceProfileCommand, CreateLoginProfileCommand, CreatePolicyCommand, CreatePolicyVersionCommand, CreateUserCommand, DeleteAccessKeyCommand, DeleteGroupCommand, DeleteGroupPolicyCommand, DeleteInstanceProfileCommand, DeleteLoginProfileCommand, DeletePolicyCommand, DeletePolicyVersionCommand, DeleteRolePolicyCommand, DeleteUserCommand, DeleteUserPermissionsBoundaryCommand, DeleteUserPolicyCommand, DetachGroupPolicyCommand, DetachRolePolicyCommand, DetachUserPolicyCommand, GetAccessKeyLastUsedCommand, GetGroupCommand, GetGroupPolicyCommand, GetInstanceProfileCommand, GetPolicyCommand, GetPolicyVersionCommand, GetRolePolicyCommand, GetUserCommand, GetUserPolicyCommand, IAMClient, ListAccessKeysCommand, ListAttachedGroupPoliciesCommand, ListAttachedUserPoliciesCommand, ListEntitiesForPolicyCommand, ListGroupPoliciesCommand, ListGroupsForUserCommand, ListPoliciesCommand, ListPolicyTagsCommand, ListPolicyVersionsCommand, ListUserPoliciesCommand, NoSuchEntityException, PutGroupPolicyCommand, PutRolePolicyCommand, PutUserPermissionsBoundaryCommand, PutUserPolicyCommand, RemoveRoleFromInstanceProfileCommand, RemoveUserFromGroupCommand, TagPolicyCommand, TagUserCommand, UntagPolicyCommand, UntagUserCommand, UpdateAccessKeyCommand, UpdateGroupCommand, UpdateLoginProfileCommand, UpdateUserCommand } from "@aws-sdk/client-iam";
@@ -63,7 +63,7 @@ import { CodeCommitClient, CreateCommitCommand, CreateRepositoryCommand as Creat
63
63
  import { CreateLifecyclePolicyCommand, DLMClient, DeleteLifecyclePolicyCommand as DeleteLifecyclePolicyCommand$1, GetLifecyclePoliciesCommand, GetLifecyclePolicyCommand as GetLifecyclePolicyCommand$1, ResourceNotFoundException as ResourceNotFoundException$16, TagResourceCommand as TagResourceCommand$26, UntagResourceCommand as UntagResourceCommand$27, UpdateLifecyclePolicyCommand } from "@aws-sdk/client-dlm";
64
64
  import { CreateVectorBucketCommand, DeleteIndexCommand, DeleteVectorBucketCommand, GetVectorBucketCommand, ListIndexesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$18, S3VectorsClient, TagResourceCommand as TagResourceCommand$27, UntagResourceCommand as UntagResourceCommand$28 } from "@aws-sdk/client-s3vectors";
65
65
  import { ListTagsForResourceCommand as ListTagsForResourceCommand$19, S3ControlClient, TagResourceCommand as TagResourceCommand$28, UntagResourceCommand as UntagResourceCommand$29 } from "@aws-sdk/client-s3-control";
66
- import { CreateNamespaceCommand, CreateTableBucketCommand, CreateTableCommand as CreateTableCommand$2, DeleteNamespaceCommand as DeleteNamespaceCommand$1, DeleteTableBucketCommand, DeleteTableCommand as DeleteTableCommand$2, GetNamespaceCommand as GetNamespaceCommand$1, GetTableBucketCommand, GetTableCommand as GetTableCommand$1, ListNamespacesCommand as ListNamespacesCommand$1, ListTableBucketsCommand, ListTablesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$20, NotFoundException as NotFoundException$7, RenameTableCommand, S3TablesClient, TagResourceCommand as TagResourceCommand$29, UntagResourceCommand as UntagResourceCommand$30 } from "@aws-sdk/client-s3tables";
66
+ import { CreateNamespaceCommand, CreateTableBucketCommand, CreateTableCommand as CreateTableCommand$2, DeleteNamespaceCommand as DeleteNamespaceCommand$1, DeleteTableBucketCommand, DeleteTableBucketMetricsConfigurationCommand, DeleteTableBucketReplicationCommand, DeleteTableCommand as DeleteTableCommand$2, GetNamespaceCommand as GetNamespaceCommand$1, GetTableBucketCommand, GetTableBucketEncryptionCommand, GetTableBucketMaintenanceConfigurationCommand, GetTableBucketMetricsConfigurationCommand, GetTableBucketReplicationCommand, GetTableBucketStorageClassCommand, GetTableCommand as GetTableCommand$1, GetTableMaintenanceConfigurationCommand, ListNamespacesCommand as ListNamespacesCommand$1, ListTableBucketsCommand, ListTablesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$20, NotFoundException as NotFoundException$7, PutTableBucketEncryptionCommand, PutTableBucketMaintenanceConfigurationCommand, PutTableBucketMetricsConfigurationCommand, PutTableBucketReplicationCommand, PutTableBucketStorageClassCommand, PutTableMaintenanceConfigurationCommand, RenameTableCommand, S3TablesClient, TagResourceCommand as TagResourceCommand$29, UntagResourceCommand as UntagResourceCommand$30 } from "@aws-sdk/client-s3tables";
67
67
 
68
68
  //#region src/provisioning/providers/iam-policy-provider.ts
69
69
  const POLICY_NAME_SKIP_REASON = "no policy name in state — no delete issued";
@@ -656,6 +656,7 @@ var IAMManagedPolicyProvider = class {
656
656
  if (!policyArn) throw new ProvisioningError(`CreatePolicy succeeded but no Arn returned for ${logicalId}`, resourceType, logicalId, policyName);
657
657
  leftBehindArn = policyArn;
658
658
  createdPolicyIdentity = policyIdentityToken(response.Policy?.PolicyId);
659
+ reportCreatedResource(logicalId, resourceType, policyArn, createdPolicyIdentity);
659
660
  log.debug(`Created IAM managed policy: ${v(policyArn)}`);
660
661
  try {
661
662
  await this.attachToPrincipals(policyArn, principals.lists.Groups, principals.lists.Roles, principals.lists.Users, log);
@@ -664,6 +665,7 @@ var IAMManagedPolicyProvider = class {
664
665
  await this.detachAllPrincipals(policyArn);
665
666
  await this.deleteAllNonDefaultVersions(policyArn);
666
667
  await this.iamClient.send(new DeletePolicyCommand({ PolicyArn: policyArn }));
668
+ retractCreatedResource(logicalId, resourceType, policyArn);
667
669
  leftBehindArn = void 0;
668
670
  log.debug(`Cleaned up partially-created managed policy ${logicalId} (${v(policyArn)}) after attachment failure`);
669
671
  } catch (cleanupError) {
@@ -1263,6 +1265,7 @@ var IAMInstanceProfileProvider = class {
1263
1265
  }));
1264
1266
  leftBehind = true;
1265
1267
  createdProfileIdentity = instanceProfileIdentityToken(response.InstanceProfile?.InstanceProfileId);
1268
+ reportCreatedResource(logicalId, resourceType, instanceProfileName, createdProfileIdentity);
1266
1269
  log.debug(`Created IAM instance profile: ${v(instanceProfileName)}`);
1267
1270
  const attachedRoles = [];
1268
1271
  try {
@@ -1285,6 +1288,7 @@ var IAMInstanceProfileProvider = class {
1285
1288
  if (!(err instanceof NoSuchEntityException)) throw err;
1286
1289
  }
1287
1290
  await this.iamClient.send(new DeleteInstanceProfileCommand({ InstanceProfileName: instanceProfileName }));
1291
+ retractCreatedResource(logicalId, resourceType, instanceProfileName);
1288
1292
  leftBehind = false;
1289
1293
  log.debug(`Cleaned up partially-created IAM instance profile ${logicalId} (${v(instanceProfileName)}) after wiring failure`);
1290
1294
  } catch (cleanupError) {
@@ -1534,12 +1538,14 @@ var IAMAccessKeyProvider = class {
1534
1538
  const accessKeyId = response.AccessKey?.AccessKeyId;
1535
1539
  const secretAccessKey = response.AccessKey?.SecretAccessKey;
1536
1540
  leftBehindKeyId = accessKeyId;
1541
+ reportCreatedResource(logicalId, resourceType, accessKeyId ?? "");
1537
1542
  if (!accessKeyId || !secretAccessKey) {
1538
1543
  if (accessKeyId) try {
1539
1544
  await this.iamClient.send(new DeleteAccessKeyCommand({
1540
1545
  UserName: userName,
1541
1546
  AccessKeyId: accessKeyId
1542
1547
  }));
1548
+ retractCreatedResource(logicalId, resourceType, accessKeyId);
1543
1549
  leftBehindKeyId = void 0;
1544
1550
  } catch (cleanupError) {
1545
1551
  const lead = `Failed to clean up IAM access key ${logicalId} (${v(accessKeyId)}) minted by a partial CreateAccessKey response`;
@@ -1559,6 +1565,7 @@ var IAMAccessKeyProvider = class {
1559
1565
  UserName: userName,
1560
1566
  AccessKeyId: accessKeyId
1561
1567
  }));
1568
+ retractCreatedResource(logicalId, resourceType, accessKeyId);
1562
1569
  leftBehindKeyId = void 0;
1563
1570
  log.debug(`Cleaned up partially-created IAM access key ${logicalId} (${v(accessKeyId)}) after status wiring failure`);
1564
1571
  } catch (cleanupError) {
@@ -1584,7 +1591,10 @@ var IAMAccessKeyProvider = class {
1584
1591
  };
1585
1592
  } catch (error) {
1586
1593
  const reconciled = await this.deleteOrphanFromFailedAttempt(userName, logicalId, baseline, attemptStartMs, log);
1587
- if (leftBehindKeyId !== void 0 && reconciled.has(leftBehindKeyId)) leftBehindKeyId = void 0;
1594
+ if (leftBehindKeyId !== void 0 && reconciled.has(leftBehindKeyId)) {
1595
+ retractCreatedResource(logicalId, resourceType, leftBehindKeyId);
1596
+ leftBehindKeyId = void 0;
1597
+ }
1588
1598
  const cause = error instanceof Error ? error : void 0;
1589
1599
  const thrown = this.wrapMaskedError(log, error, (text) => new ProvisioningError(`Failed to create IAM access key ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
1590
1600
  if (leftBehindKeyId !== void 0) {
@@ -1935,6 +1945,7 @@ var IAMUserGroupProvider = class {
1935
1945
  const response = await (await this.createClient.get()).send(new CreateUserCommand(createParams));
1936
1946
  leftBehind = true;
1937
1947
  createdUserIdentity = userOrGroupIdentityToken(response.User?.UserId);
1948
+ reportCreatedResource(logicalId, resourceType, userName, createdUserIdentity);
1938
1949
  try {
1939
1950
  const permissionsBoundary = properties["PermissionsBoundary"];
1940
1951
  if (permissionsBoundary) {
@@ -1995,6 +2006,7 @@ var IAMUserGroupProvider = class {
1995
2006
  if (!(err instanceof NoSuchEntityException)) throw err;
1996
2007
  }
1997
2008
  await this.iamClient.send(new DeleteUserCommand({ UserName: userName }));
2009
+ retractCreatedResource(logicalId, resourceType, userName);
1998
2010
  leftBehind = false;
1999
2011
  log.debug(`Cleaned up partially-created IAM user ${logicalId} (${v(userName)}) after wiring failure`);
2000
2012
  } catch (cleanupError) {
@@ -2337,6 +2349,7 @@ var IAMUserGroupProvider = class {
2337
2349
  const response = await (await this.createClient.get()).send(new CreateGroupCommand(createParams));
2338
2350
  leftBehind = true;
2339
2351
  createdGroupIdentity = userOrGroupIdentityToken(response.Group?.GroupId);
2352
+ reportCreatedResource(logicalId, resourceType, groupName, createdGroupIdentity);
2340
2353
  try {
2341
2354
  const managedPolicyArns = properties["ManagedPolicyArns"];
2342
2355
  if (managedPolicyArns && Array.isArray(managedPolicyArns)) for (const policyArn of managedPolicyArns) {
@@ -2361,6 +2374,7 @@ var IAMUserGroupProvider = class {
2361
2374
  await this.detachAllGroupPolicies(groupName, log);
2362
2375
  await this.deleteAllGroupInlinePolicies(groupName, log);
2363
2376
  await this.iamClient.send(new DeleteGroupCommand({ GroupName: groupName }));
2377
+ retractCreatedResource(logicalId, resourceType, groupName);
2364
2378
  leftBehind = false;
2365
2379
  log.debug(`Cleaned up partially-created IAM group ${logicalId} (${v(groupName)}) after wiring failure`);
2366
2380
  } catch (cleanupError) {
@@ -4803,6 +4817,7 @@ var S3BucketProvider = class S3BucketProvider {
4803
4817
  }
4804
4818
  createdNewBucket = adoptedOwnLeftover || preflight.kind === "absent";
4805
4819
  bucketLeftBehind = createdNewBucket;
4820
+ if (createdNewBucket) reportCreatedResource(logicalId, resourceType, bucketName);
4806
4821
  if (createdNewBucket) windowSpent = true;
4807
4822
  if (preflight.kind === "region" && preflight.region !== "us-east-1") this.refuseForeignRegionAdopt(logicalId, resourceType, bucketName, "us-east-1", preflight.region);
4808
4823
  if (preflight.kind === "region" && !adoptedOwnLeftover) this.logger.warn(`S3 bucket ${this.shown(bucketName)} for ${displaySafe(logicalId)} (${resourceType}) already existed in ${preflight.region} and was ADOPTED, not created. In us-east-1 S3 answers a re-create of a bucket you already own with 200 OK and RESETS that bucket's access control lists, so any ACL previously set on ${this.shown(bucketName)} is now the default. cdkd will not delete this bucket if the rest of this create fails.`);
@@ -4832,6 +4847,7 @@ var S3BucketProvider = class S3BucketProvider {
4832
4847
  if (createdNewBucket) try {
4833
4848
  await this.s3Client.send(new DeleteBucketCommand({ Bucket: bucketName }));
4834
4849
  bucketLeftBehind = false;
4850
+ retractCreatedResource(logicalId, resourceType, bucketName);
4835
4851
  this.logger.debug(`Cleaned up partially-created S3 bucket ${displaySafe(logicalId)} (${this.shown(bucketName)}) after wiring failure`);
4836
4852
  } catch (cleanupError) {
4837
4853
  this.logger.debug(`DeleteBucket cleanup failed for S3 bucket ${displaySafe(logicalId)} (${this.shown(bucketName)}): ${describeAwsFailure(cleanupError).detail}`);
@@ -6263,7 +6279,7 @@ function normalizeStatement(statement) {
6263
6279
  for (const key of PRINCIPAL_KEYS) if (Object.prototype.hasOwnProperty.call(out, key)) out[key] = normalizePrincipal(out[key]);
6264
6280
  return out;
6265
6281
  }
6266
- function canonicalJson$2(value) {
6282
+ function canonicalJson$3(value) {
6267
6283
  const sortKeys = (v) => {
6268
6284
  if (Array.isArray(v)) return v.map(sortKeys);
6269
6285
  if (v !== null && typeof v === "object") {
@@ -6287,7 +6303,7 @@ function policyContentKey(document) {
6287
6303
  if (Object.prototype.hasOwnProperty.call(doc, "Statement")) doc["Statement"] = (Array.isArray(doc["Statement"]) ? doc["Statement"] : [doc["Statement"]]).map(normalizeStatement);
6288
6304
  value = doc;
6289
6305
  }
6290
- return canonicalJson$2(value);
6306
+ return canonicalJson$3(value);
6291
6307
  }
6292
6308
 
6293
6309
  //#endregion
@@ -6565,7 +6581,7 @@ function listedQueues(bag) {
6565
6581
  function isListedQueueUrl(value) {
6566
6582
  return isSqsQueueUrl(value) && !value.includes(",") && !value.includes("*");
6567
6583
  }
6568
- function canonicalJson$1(value) {
6584
+ function canonicalJson$2(value) {
6569
6585
  const sortKeys = (v) => {
6570
6586
  if (Array.isArray(v)) return v.map(sortKeys);
6571
6587
  if (v !== null && typeof v === "object") {
@@ -6580,14 +6596,14 @@ function canonicalJson$1(value) {
6580
6596
  }
6581
6597
  function canonicalPolicy$1(text) {
6582
6598
  try {
6583
- return canonicalJson$1(JSON.parse(text));
6599
+ return canonicalJson$2(JSON.parse(text));
6584
6600
  } catch {
6585
6601
  return `raw:${text}`;
6586
6602
  }
6587
6603
  }
6588
6604
  function policyReference(document) {
6589
6605
  if (typeof document === "string" && document.length > 0) return canonicalPolicy$1(document);
6590
- if (document !== null && typeof document === "object") return canonicalJson$1(document);
6606
+ if (document !== null && typeof document === "object") return canonicalJson$2(document);
6591
6607
  }
6592
6608
  function splitQueueUrls(physicalId) {
6593
6609
  return physicalId.split(",").filter((url) => url.length > 0);
@@ -6683,7 +6699,8 @@ var SNSTopicProvider = class {
6683
6699
  if (properties["MaximumMessageSize"] != null) topicAttributes["MaximumMessageSize"] = stringifyValue(properties["MaximumMessageSize"]);
6684
6700
  let tags;
6685
6701
  if (properties["Tags"] != null) tags = desiredTags;
6686
- const heldBefore = Boolean(properties["ArchivePolicy"]) || Boolean(properties["DataProtectionPolicy"]) || properties["DeliveryStatusLogging"] != null || Array.isArray(properties["Subscription"]) ? await this.topicHeldBefore(topicName) : "free";
6702
+ const wiringDeclared = Boolean(properties["ArchivePolicy"]) || Boolean(properties["DataProtectionPolicy"]) || properties["DeliveryStatusLogging"] != null || Array.isArray(properties["Subscription"]);
6703
+ const heldBefore = wiringDeclared ? await this.topicHeldBefore(topicName) : "free";
6687
6704
  const createParams = {
6688
6705
  Name: topicName,
6689
6706
  ...Object.keys(topicAttributes).length > 0 && { Attributes: topicAttributes },
@@ -6691,6 +6708,7 @@ var SNSTopicProvider = class {
6691
6708
  };
6692
6709
  const topicArn = (await this.snsClient.send(new CreateTopicCommand(createParams))).TopicArn;
6693
6710
  if (!topicArn) throw new Error("CreateTopic did not return TopicArn");
6711
+ if (wiringDeclared && heldBefore === "free") reportCreatedResource(logicalId, resourceType, topicArn);
6694
6712
  try {
6695
6713
  if (properties["ArchivePolicy"]) {
6696
6714
  const archivePolicy = typeof properties["ArchivePolicy"] === "string" ? properties["ArchivePolicy"] : JSON.stringify(properties["ArchivePolicy"]);
@@ -6722,6 +6740,7 @@ var SNSTopicProvider = class {
6722
6740
  if (heldBefore !== "free") warn(skippedCleanupText(heldBefore, `SNS topic ${logicalId} (${topicArn})`, pasteableAwsCommand(maskSecrets)`aws sns delete-topic --topic-arn ${topicArn}`.render()));
6723
6741
  else try {
6724
6742
  await this.snsClient.send(new DeleteTopicCommand({ TopicArn: topicArn }));
6743
+ retractCreatedResource(logicalId, resourceType, topicArn);
6725
6744
  this.logger.debug(`Cleaned up partially-created SNS topic ${logicalId} (${topicArn}) after wiring failure`);
6726
6745
  } catch (cleanupError) {
6727
6746
  leftBehindArn = topicArn;
@@ -7609,7 +7628,7 @@ var SNSTopicPolicyProvider = class {
7609
7628
  if (candidates.length === 0) return [];
7610
7629
  let reference;
7611
7630
  if (typeof document === "string" && document.length > 0) reference = canonicalPolicy(document);
7612
- else if (document !== null && typeof document === "object") reference = canonicalJson(document);
7631
+ else if (document !== null && typeof document === "object") reference = canonicalJson$1(document);
7613
7632
  if (reference === void 0) {
7614
7633
  this.logger.warn(mask(safeMsg`The topics ${candidates.join(", ")} listed by ${logicalId} were not checked (no policy document recorded to compare with) and may still carry its policy.`));
7615
7634
  return [];
@@ -7726,7 +7745,7 @@ function defaultTopicPolicy(topicArn) {
7726
7745
  }]
7727
7746
  });
7728
7747
  }
7729
- function canonicalJson(value) {
7748
+ function canonicalJson$1(value) {
7730
7749
  const sortKeys = (v) => {
7731
7750
  if (Array.isArray(v)) return v.map(sortKeys);
7732
7751
  if (v !== null && typeof v === "object") {
@@ -7740,7 +7759,7 @@ function canonicalJson(value) {
7740
7759
  }
7741
7760
  function canonicalPolicy(text) {
7742
7761
  try {
7743
- return canonicalJson(JSON.parse(text));
7762
+ return canonicalJson$1(JSON.parse(text));
7744
7763
  } catch {
7745
7764
  return `raw:${text}`;
7746
7765
  }
@@ -8059,6 +8078,7 @@ var LambdaFunctionProvider = class {
8059
8078
  functionCreated = true;
8060
8079
  createdFunctionName = response.FunctionName || functionName;
8061
8080
  createdFunctionIdentity = functionIdentityToken(response.FunctionArn, response.LastModified);
8081
+ reportCreatedResource(logicalId, resourceType, createdFunctionName, createdFunctionIdentity);
8062
8082
  const recursiveLoop = properties["RecursiveLoop"];
8063
8083
  if (recursiveLoop !== void 0) await this.applyPostCreateConfig(() => this.lambdaClient.send(new PutFunctionRecursionConfigCommand({
8064
8084
  FunctionName: functionName,
@@ -8093,6 +8113,7 @@ var LambdaFunctionProvider = class {
8093
8113
  minDelay: 1,
8094
8114
  maxDelay: 5
8095
8115
  }, { FunctionName: functionName });
8116
+ reportCreatedResource(logicalId, resourceType, createdFunctionName);
8096
8117
  await this.putRuntimeManagementConfig(functionName, runtimeManagementConfig);
8097
8118
  createdFunctionIdentity = void 0;
8098
8119
  }, {
@@ -8117,6 +8138,7 @@ var LambdaFunctionProvider = class {
8117
8138
  const cause = error instanceof Error ? error : void 0;
8118
8139
  const thrown = this.wrapMaskedError(log, error, (text) => new ProvisioningError(`Failed to create Lambda function ${logicalId}: ${text}`, resourceType, logicalId, functionName, cause ?? new Error(safeStringify(error))));
8119
8140
  if (createdFunctionName !== void 0 && !postCreateCleanup.deleted) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdFunctionName, createdFunctionIdentity);
8141
+ else if (createdFunctionName !== void 0) retractCreatedResource(logicalId, resourceType, createdFunctionName);
8120
8142
  throw thrown;
8121
8143
  }
8122
8144
  }
@@ -10398,6 +10420,7 @@ var LambdaMicrovmImageProvider = class {
10398
10420
  const imageArn = response.imageArn;
10399
10421
  createdArn = imageArn;
10400
10422
  if (!imageArn) throw new ProvisioningError(`CreateMicrovmImage succeeded but no imageArn returned for ${logicalId}`, resourceType, logicalId);
10423
+ reportCreatedResource(logicalId, resourceType, imageArn);
10401
10424
  this.logger.debug(`Created MicroVM image ${logicalId}: ${imageArn} (state=${response.state})`);
10402
10425
  const noWait = process.env["CDKD_NO_WAIT"] === "true";
10403
10426
  let latest = {
@@ -10656,15 +10679,15 @@ function buildCommonImageInput(properties) {
10656
10679
  return input;
10657
10680
  }
10658
10681
  function mapCodeArtifact(value) {
10659
- if (!isRecord$1(value)) return void 0;
10682
+ if (!isRecord$2(value)) return void 0;
10660
10683
  const uri = value["Uri"];
10661
10684
  return typeof uri === "string" ? { uri } : void 0;
10662
10685
  }
10663
10686
  function mapLogging(value) {
10664
- if (!isRecord$1(value)) return void 0;
10687
+ if (!isRecord$2(value)) return void 0;
10665
10688
  if (value["Disabled"] === true) return { disabled: {} };
10666
10689
  const cloudWatch = value["CloudWatch"];
10667
- if (isRecord$1(cloudWatch)) {
10690
+ if (isRecord$2(cloudWatch)) {
10668
10691
  const cw = {};
10669
10692
  if (typeof cloudWatch["LogGroup"] === "string") cw.logGroup = cloudWatch["LogGroup"];
10670
10693
  if (typeof cloudWatch["LogStream"] === "string") cw.logStream = cloudWatch["LogStream"];
@@ -10673,32 +10696,32 @@ function mapLogging(value) {
10673
10696
  }
10674
10697
  function mapCpuConfigurations(value) {
10675
10698
  if (!Array.isArray(value)) return void 0;
10676
- return value.filter(isRecord$1).map((c) => ({ architecture: c["Architecture"] }));
10699
+ return value.filter(isRecord$2).map((c) => ({ architecture: c["Architecture"] }));
10677
10700
  }
10678
10701
  function mapResources(value) {
10679
10702
  if (!Array.isArray(value)) return void 0;
10680
- return value.filter(isRecord$1).map((r) => Number(r["MinimumMemoryInMiB"])).filter((n) => Number.isFinite(n)).map((minimumMemoryInMiB) => ({ minimumMemoryInMiB }));
10703
+ return value.filter(isRecord$2).map((r) => Number(r["MinimumMemoryInMiB"])).filter((n) => Number.isFinite(n)).map((minimumMemoryInMiB) => ({ minimumMemoryInMiB }));
10681
10704
  }
10682
10705
  function mapHooks(value) {
10683
- if (!isRecord$1(value)) return void 0;
10706
+ if (!isRecord$2(value)) return void 0;
10684
10707
  return lowerFirstKeysDeep(value);
10685
10708
  }
10686
10709
  function mapKeyValueListToRecord(value) {
10687
10710
  if (!Array.isArray(value)) return void 0;
10688
10711
  const out = {};
10689
- for (const entry of value) if (isRecord$1(entry) && typeof entry["Key"] === "string") out[entry["Key"]] = typeof entry["Value"] === "string" ? entry["Value"] : "";
10712
+ for (const entry of value) if (isRecord$2(entry) && typeof entry["Key"] === "string") out[entry["Key"]] = typeof entry["Value"] === "string" ? entry["Value"] : "";
10690
10713
  return Object.keys(out).length > 0 ? out : void 0;
10691
10714
  }
10692
10715
  function lowerFirstKeysDeep(value) {
10693
10716
  if (Array.isArray(value)) return value.map(lowerFirstKeysDeep);
10694
- if (isRecord$1(value)) {
10717
+ if (isRecord$2(value)) {
10695
10718
  const out = {};
10696
10719
  for (const [k, v] of Object.entries(value)) out[k.charAt(0).toLowerCase() + k.slice(1)] = lowerFirstKeysDeep(v);
10697
10720
  return out;
10698
10721
  }
10699
10722
  return value;
10700
10723
  }
10701
- function isRecord$1(value) {
10724
+ function isRecord$2(value) {
10702
10725
  return typeof value === "object" && value !== null && !Array.isArray(value);
10703
10726
  }
10704
10727
 
@@ -10784,7 +10807,10 @@ var LambdaWebFunctionRevisionProvider = class {
10784
10807
  throw error;
10785
10808
  }
10786
10809
  createdArn = response.revisionArn;
10787
- if (createdArn) revisionsCreatedByThisProcess.add(createdArn);
10810
+ if (createdArn) {
10811
+ revisionsCreatedByThisProcess.add(createdArn);
10812
+ reportCreatedResource(logicalId, resourceType, createdArn);
10813
+ }
10788
10814
  const replayWindow = replayedSendWindow(response, attemptStartMs);
10789
10815
  if (replayWindow !== void 0) await this.reportPossibleRevisionOrphans(logicalId, replayWindow, log, input.functionName);
10790
10816
  if (!createdArn || !response.revisionId) throw new ProvisioningError(`CreateWebFunctionRevision succeeded but returned no revision ARN for ${logicalIdShown(logicalId)}`, resourceType, logicalId);
@@ -10798,6 +10824,7 @@ var LambdaWebFunctionRevisionProvider = class {
10798
10824
  let leftBehindArn = createdArn;
10799
10825
  if (leftBehindArn) try {
10800
10826
  await this.delete(logicalId, leftBehindArn, resourceType, void 0, context?.maskSecrets ? { maskSecrets: context.maskSecrets } : void 0);
10827
+ retractCreatedResource(logicalId, resourceType, leftBehindArn);
10801
10828
  leftBehindArn = void 0;
10802
10829
  } catch (cleanupError) {
10803
10830
  log.warn(safeMsg`Failed to delete partially-created Lambda web function revision ${logicalId}: ${describeAwsFailure(cleanupError).detail}. The failed deploy records it for \`cdkd rollback --revert-failed\`, or delete it by hand`);
@@ -11167,6 +11194,7 @@ var LambdaWebFunctionEndpointProvider = class {
11167
11194
  try {
11168
11195
  createdArn = (await this.client.send(new CreateWebFunctionEndpointCommand(input))).endpointArn;
11169
11196
  if (!createdArn) throw new ProvisioningError(`CreateWebFunctionEndpoint succeeded but returned no endpoint ARN for ${logicalIdShown(logicalId)}`, resourceType, logicalId);
11197
+ reportCreatedResource(logicalId, resourceType, createdArn);
11170
11198
  const settled = await this.waitForSettled(input.functionName, input.endpointName, logicalId, resourceType, "create", void 0, context?.maskSecrets);
11171
11199
  return {
11172
11200
  physicalId: createdArn,
@@ -11489,6 +11517,11 @@ var LambdaNetworkConnectorProvider = class {
11489
11517
  const tagList = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, void 0, "Tags", DBPROXY_TAGS_WHAT, DBPROXY_TAG_OPTIONS);
11490
11518
  const name = typeof properties["Name"] === "string" && properties["Name"] !== "" ? properties["Name"] : generatedNameFor("AWS::Lambda::NetworkConnector", logicalId);
11491
11519
  const vpc = this.readVpcEgress(properties, logicalId, resourceType, void 0);
11520
+ if (declaredElasticIp(properties) !== void 0) {
11521
+ const message = elasticIpRefusalMessage(logicalId, "create");
11522
+ if (context?.replayingState !== true) throw new ProvisioningError(message, resourceType, logicalId);
11523
+ this.logger.warn(safeMsg`${message} Proceeding without it: this create replays a cdkd state record.`);
11524
+ }
11492
11525
  const createToken = acquireIdempotencyToken({
11493
11526
  scope: "CreateNetworkConnector",
11494
11527
  logicalId
@@ -11510,6 +11543,7 @@ var LambdaNetworkConnectorProvider = class {
11510
11543
  try {
11511
11544
  createdArn = (await this.client.send(new CreateNetworkConnectorCommand(input))).Arn;
11512
11545
  if (!createdArn) throw new ProvisioningError(`CreateNetworkConnector succeeded but returned no ARN for ${logicalIdShown(logicalId)}`, resourceType, logicalId);
11546
+ reportCreatedResource(logicalId, resourceType, createdArn);
11513
11547
  const settled = await this.waitForSettled(createdArn, logicalId, "create");
11514
11548
  createToken.release();
11515
11549
  return {
@@ -11527,6 +11561,12 @@ var LambdaNetworkConnectorProvider = class {
11527
11561
  assertRegionMatch(context?.expectedRegion ? await this.client.config.region() : void 0, context?.expectedRegion, resourceType, logicalId, physicalId, "pre-update");
11528
11562
  refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId, "Tags", DBPROXY_TAGS_WHAT, DBPROXY_TAG_OPTIONS);
11529
11563
  const vpc = this.readVpcEgress(properties, logicalId, resourceType, physicalId);
11564
+ const desiredElasticIp = declaredElasticIp(properties);
11565
+ if (desiredElasticIp !== void 0 && !isDeepStrictEqual(desiredElasticIp, declaredElasticIp(previousProperties))) {
11566
+ const message = elasticIpRefusalMessage(logicalId, "update");
11567
+ if (context?.replayingState !== true && context?.desiredFromAwsReadback !== true) throw new ProvisioningError(message, resourceType, logicalId, physicalId);
11568
+ this.logger.warn(safeMsg`${message} Proceeding without it: this update restores a recorded or read-back configuration.`);
11569
+ }
11530
11570
  const updateRole = this.readOperatorRole(properties, logicalId, resourceType, physicalId, context);
11531
11571
  if (typeof previousProperties["OperatorRole"] === "string" && properties["OperatorRole"] === void 0 && context?.replayingState !== true && context?.desiredFromAwsReadback !== true) throw new ProvisioningError(`OperatorRole cannot be removed in place for Lambda network connector ${logicalIdShown(logicalId)}: cdkd sends no role when the template declares none, so the current one would stay attached. Set it to another role, or replace the connector (change its Name).`, resourceType, logicalId, physicalId);
11532
11572
  try {
@@ -11725,6 +11765,15 @@ var LambdaNetworkConnectorProvider = class {
11725
11765
  return markRedactedCause(build(`${failure.summary}${relayedNameCollision(failure.detail)}`));
11726
11766
  }
11727
11767
  };
11768
+ const ELASTIC_IP_KEY = "ElasticIpConfiguration";
11769
+ function declaredElasticIp(properties) {
11770
+ const config = properties["Configuration"];
11771
+ const vpc = isPlainRecord$3(config) ? config["VpcEgressConfiguration"] : void 0;
11772
+ return isPlainRecord$3(vpc) ? vpc[ELASTIC_IP_KEY] : void 0;
11773
+ }
11774
+ function elasticIpRefusalMessage(logicalId, mode) {
11775
+ return `AWS::Lambda::NetworkConnector ${logicalIdShown(logicalId)}: Configuration.VpcEgressConfiguration.${ELASTIC_IP_KEY} cannot be sent by cdkd yet — the AWS SDK for JavaScript (@aws-sdk/client-lambda-core) does not model the member, so it would be dropped from the ${mode === "create" ? "CreateNetworkConnector" : "UpdateNetworkConnector"} request without an error. Remove ${ELASTIC_IP_KEY} from Configuration.VpcEgressConfiguration to deploy the connector without it.`;
11776
+ }
11728
11777
  var TerminalConnectorStateError = class extends Error {};
11729
11778
  function buildAttributes(resp) {
11730
11779
  return definedAttributes({
@@ -11739,6 +11788,172 @@ function versionOfArn(arn) {
11739
11788
  return match ? Number(match[1]) : void 0;
11740
11789
  }
11741
11790
 
11791
+ //#endregion
11792
+ //#region src/provisioning/delete-gone-wait.ts
11793
+ async function waitForGoneAfterDelete(opts) {
11794
+ const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
11795
+ const now = opts.now ?? Date.now;
11796
+ const userTimeoutMs = resolvedResourceTimeoutMs(opts.resourceType);
11797
+ const maxWaitMs = userTimeoutMs === void 0 ? opts.maxWaitMs : Math.min(opts.maxWaitMs, userTimeoutMs / 2);
11798
+ const startedAt = now();
11799
+ const stopped = `${opts.what}: the delete was ACCEPTED by AWS, but cdkd stopped waiting for it to disappear`;
11800
+ const consequence = "AWS finishes the delete on its side; until it does, a create of the same name is refused with \"already exists\".";
11801
+ const watch = startInterruptWatch(`${opts.what} deletion wait`);
11802
+ try {
11803
+ let lastStatus;
11804
+ let sawNonFailedStatus = false;
11805
+ for (;;) {
11806
+ if (watch.isInterrupted()) {
11807
+ opts.logger.warn(safeMsg`${stopped} (interrupted). ${consequence}`);
11808
+ return;
11809
+ }
11810
+ try {
11811
+ const status = await opts.describe();
11812
+ if (status === void 0) {
11813
+ opts.logger.debug(safeMsg`${opts.what} is gone`);
11814
+ return;
11815
+ }
11816
+ const failure = opts.failedStatus?.(status);
11817
+ if (failure !== void 0) {
11818
+ if (sawNonFailedStatus || now() - startedAt >= opts.pollIntervalMs) throw markWaitAbandoned(failure);
11819
+ } else sawNonFailedStatus = true;
11820
+ lastStatus = status;
11821
+ opts.logger.debug(safeMsg`${opts.what} status: ${status}, waiting for it to disappear`);
11822
+ } catch (error) {
11823
+ if (isWaitAbandonedError(error)) throw error;
11824
+ if (!isThrottlingError(error) && !isTransientServerError(error)) {
11825
+ opts.logger.debug(safeMsg`${opts.what}: status read failed: ${describeAwsFailure(error).detail}`);
11826
+ opts.logger.warn(safeMsg`${stopped}: its status could not be read (${describeAwsFailure(error).summary}). ${consequence}`);
11827
+ return;
11828
+ }
11829
+ opts.logger.debug(safeMsg`${opts.what}: status read throttled or failed transiently, re-polling`);
11830
+ }
11831
+ const remainingMs = maxWaitMs - (now() - startedAt);
11832
+ if (remainingMs <= 0) {
11833
+ opts.logger.warn(safeMsg`${stopped} after ${Math.round(maxWaitMs / 1e3)}s` + safeMsg`${lastStatus !== void 0 ? ` (last status: ${lastStatus})` : ""}. ${consequence}`);
11834
+ return;
11835
+ }
11836
+ await sleep(Math.min(opts.pollIntervalMs, remainingMs));
11837
+ }
11838
+ } finally {
11839
+ watch.dispose();
11840
+ }
11841
+ }
11842
+
11843
+ //#endregion
11844
+ //#region src/provisioning/dynamodb-vector-indexes.ts
11845
+ function vectorIndexesByName(value) {
11846
+ const out = /* @__PURE__ */ new Map();
11847
+ if (!Array.isArray(value)) return out;
11848
+ for (const entry of value) {
11849
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
11850
+ const name = entry["IndexName"];
11851
+ if (typeof name === "string") out.set(name, entry);
11852
+ }
11853
+ return out;
11854
+ }
11855
+ function vectorIndexForSend(entry) {
11856
+ const out = {};
11857
+ for (const key of [
11858
+ "IndexName",
11859
+ "VectorAttribute",
11860
+ "Projection",
11861
+ "DistanceFunction"
11862
+ ]) if (entry[key] !== void 0) out[key] = entry[key];
11863
+ if (entry["Dimensions"] !== void 0) out["Dimensions"] = coerceCfnInteger(entry["Dimensions"]) ?? entry["Dimensions"];
11864
+ if (entry["SearchSchema"] !== void 0) out["SearchSchema"] = entry["SearchSchema"];
11865
+ return out;
11866
+ }
11867
+ function vectorIndexesForCreate(value) {
11868
+ if (value === void 0 || value === null) return void 0;
11869
+ if (!Array.isArray(value)) return value;
11870
+ return value.map((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry) ? vectorIndexForSend(entry) : entry);
11871
+ }
11872
+ function sortedByJson(list) {
11873
+ if (!Array.isArray(list)) return list;
11874
+ return [...list].sort((a, b) => {
11875
+ const x = JSON.stringify(a) ?? "";
11876
+ const y = JSON.stringify(b) ?? "";
11877
+ return x < y ? -1 : x > y ? 1 : 0;
11878
+ });
11879
+ }
11880
+ function vectorIndexKey(entry) {
11881
+ const e = entry;
11882
+ const projection = e["Projection"];
11883
+ const normalizedProjection = typeof projection === "object" && projection !== null && !Array.isArray(projection) ? {
11884
+ ProjectionType: projection["ProjectionType"],
11885
+ NonKeyAttributes: sortedByJson(nonEmpty(projection["NonKeyAttributes"]))
11886
+ } : projection;
11887
+ const attribute = e["VectorAttribute"];
11888
+ return JSON.stringify([
11889
+ e["IndexName"],
11890
+ typeof attribute === "object" && attribute !== null ? attribute["AttributeName"] : attribute,
11891
+ coerceCfnInteger(e["Dimensions"]) ?? e["Dimensions"],
11892
+ e["DistanceFunction"],
11893
+ normalizedProjection,
11894
+ sortedByJson(Array.isArray(e["SearchSchema"]) && e["SearchSchema"].length > 0 ? e["SearchSchema"].map((s) => typeof s === "object" && s !== null ? {
11895
+ AttributeName: s["AttributeName"],
11896
+ SearchSchemaElementType: s["SearchSchemaElementType"]
11897
+ } : s) : nonEmpty(e["SearchSchema"]))
11898
+ ]);
11899
+ }
11900
+ function nonEmpty(value) {
11901
+ return Array.isArray(value) && value.length === 0 ? void 0 : value;
11902
+ }
11903
+ function planVectorIndexUpdates(previous, desired, live) {
11904
+ const prevByName = vectorIndexesByName(previous);
11905
+ const desiredByName = vectorIndexesByName(desired);
11906
+ const liveByName = live === void 0 ? void 0 : new Map(live.filter((d) => typeof d.IndexName === "string").map((d) => [d.IndexName, d]));
11907
+ const plan = {
11908
+ ops: [],
11909
+ changed: [],
11910
+ adopted: [],
11911
+ alreadyGone: []
11912
+ };
11913
+ const liveHolds = (name, entry) => {
11914
+ const l = liveByName?.get(name);
11915
+ return l !== void 0 && l.IndexStatus !== "DELETING" && vectorIndexKey(l) === vectorIndexKey(entry);
11916
+ };
11917
+ for (const name of prevByName.keys()) {
11918
+ if (desiredByName.has(name)) continue;
11919
+ const l = liveByName?.get(name);
11920
+ if (liveByName !== void 0 && (l === void 0 || l.IndexStatus === "DELETING")) {
11921
+ plan.alreadyGone.push(name);
11922
+ continue;
11923
+ }
11924
+ plan.ops.push({ Delete: { IndexName: name } });
11925
+ }
11926
+ for (const [name, entry] of desiredByName) {
11927
+ const before = prevByName.get(name);
11928
+ if (before === void 0) {
11929
+ const l = liveByName?.get(name);
11930
+ if (l === void 0 || l.IndexStatus === "DELETING") plan.ops.push({ Create: vectorIndexForSend(entry) });
11931
+ else if (liveHolds(name, entry)) plan.adopted.push(name);
11932
+ else plan.changed.push(name);
11933
+ continue;
11934
+ }
11935
+ if (vectorIndexKey(before) === vectorIndexKey(entry)) continue;
11936
+ if (!liveHolds(name, entry)) plan.changed.push(name);
11937
+ }
11938
+ return plan;
11939
+ }
11940
+ function readbackVectorIndexes(live) {
11941
+ if (live === void 0 || live.length === 0) return void 0;
11942
+ return live.map((d) => {
11943
+ const out = {};
11944
+ if (d.IndexName !== void 0) out["IndexName"] = d.IndexName;
11945
+ if (d.VectorAttribute !== void 0) out["VectorAttribute"] = d.VectorAttribute;
11946
+ if (d.Dimensions !== void 0) out["Dimensions"] = d.Dimensions;
11947
+ if (d.DistanceFunction !== void 0) out["DistanceFunction"] = d.DistanceFunction;
11948
+ if (d.Projection !== void 0) out["Projection"] = d.Projection;
11949
+ if (d.SearchSchema !== void 0 && d.SearchSchema.length > 0) out["SearchSchema"] = d.SearchSchema;
11950
+ return out;
11951
+ });
11952
+ }
11953
+ function vectorIndexesSettled(live) {
11954
+ return (live ?? []).every((d) => d.IndexStatus === void 0 || d.IndexStatus === "ACTIVE");
11955
+ }
11956
+
11742
11957
  //#endregion
11743
11958
  //#region src/provisioning/providers/dynamodb-table-provider.ts
11744
11959
  function isTableName(id) {
@@ -12150,6 +12365,10 @@ const tableNameReleaseWait = {
12150
12365
  maxPolls: 600,
12151
12366
  intervalMs: INDEX_SETTLE_POLL_INTERVAL_MS
12152
12367
  };
12368
+ const tableDeleteGoneWait = {
12369
+ maxWaitMs: 600 * DYNAMODB_DELETE_POLL_COST_MS,
12370
+ pollIntervalMs: INDEX_SETTLE_POLL_INTERVAL_MS
12371
+ };
12153
12372
  var DynamoDBTableProvider = class {
12154
12373
  dynamoDBClient;
12155
12374
  createClient;
@@ -12161,7 +12380,7 @@ var DynamoDBTableProvider = class {
12161
12380
  if (!failure.redacted) return wrapMaskedAwsError(m, error, build);
12162
12381
  this.logger.debug(m(build(m(failure.detail)).message));
12163
12382
  const relay = isAuxiliaryFailure(error) ? "" : relayedNameCollision(failure.detail);
12164
- const wrapped = build(`${m(failure.summary)}${relay}${relayedIndexBusyDelete(failure.detail)}`);
12383
+ const wrapped = build(`${m(failure.summary)}${relay}${relayedIndexBusyDelete(failure.detail, TABLE_INDEX_NOUN)}`);
12165
12384
  return error instanceof Error ? markRedactedCause(wrapped) : wrapped;
12166
12385
  }
12167
12386
  handledProperties = /* @__PURE__ */ new Map([["AWS::DynamoDB::Table", /* @__PURE__ */ new Set([
@@ -12183,7 +12402,8 @@ var DynamoDBTableProvider = class {
12183
12402
  "TimeToLiveSpecification",
12184
12403
  "ResourcePolicy",
12185
12404
  "KinesisStreamSpecification",
12186
- "ContributorInsightsSpecification"
12405
+ "ContributorInsightsSpecification",
12406
+ "VectorIndexes"
12187
12407
  ])]]);
12188
12408
  unhandledByDesign = /* @__PURE__ */ new Map([["AWS::DynamoDB::Table", /* @__PURE__ */ new Map([["ImportSourceSpecification", "S3 import uses the separate ImportTable API (not CreateTable) and is create-only with no readback; deferred to a dedicated import-from-S3 PR"]])]]);
12189
12409
  deleteBudgets = new ElapsedBudgetRegistry();
@@ -12257,6 +12477,8 @@ var DynamoDBTableProvider = class {
12257
12477
  const declaredGsis = properties["GlobalSecondaryIndexes"];
12258
12478
  createParams.GlobalSecondaryIndexes = Array.isArray(declaredGsis) ? declaredGsis.map((entry) => this.coerceIndexThroughputForCreate(logicalId, stripIndexContributorInsights(entry), maskSecrets)) : declaredGsis;
12259
12479
  }
12480
+ const createVectorIndexes = vectorIndexesForCreate(properties["VectorIndexes"]);
12481
+ if (createVectorIndexes !== void 0) createParams.VectorIndexes = createVectorIndexes;
12260
12482
  if (properties["LocalSecondaryIndexes"]) createParams.LocalSecondaryIndexes = properties["LocalSecondaryIndexes"];
12261
12483
  const sseCreateRefusal = sseSpecificationRefusal(properties["SSESpecification"], logicalId);
12262
12484
  if (sseCreateRefusal !== void 0 && context?.replayingState !== true) throw new ProvisioningError(`${maskSecrets(sseCreateRefusal)}. Nothing was created; fix the template value`, resourceType, logicalId);
@@ -12280,8 +12502,13 @@ var DynamoDBTableProvider = class {
12280
12502
  const createOut = await (await this.getCreateClient()).send(new CreateTableCommand(createParams));
12281
12503
  tableCreated = true;
12282
12504
  createdTableId = createOut?.TableDescription?.TableId;
12283
- this.logger.debug(`CreateTable initiated for ${tableName}, waiting for ACTIVE status`);
12284
- const tableInfo = await timePhase("provider waits", "DynamoDB table: wait for ACTIVE", () => this.waitForTableActive(tableName), logicalId);
12505
+ reportCreatedResource(logicalId, resourceType, tableName, createdTableId);
12506
+ debug(`CreateTable initiated for ${tableName}, waiting for ACTIVE status`);
12507
+ const tableInfo = await timePhase("provider waits", "DynamoDB table: wait for ACTIVE", () => this.waitForTableActive(tableName, maskSecrets), logicalId);
12508
+ if (createVectorIndexes !== void 0) await timePhase("provider waits", "DynamoDB table: wait for vector indexes", () => this.waitForTableAndIndexesActive(tableName, void 0, {
12509
+ after: "CreateTable",
12510
+ maskSecrets
12511
+ }), logicalId);
12285
12512
  await this.applyPointInTimeRecovery(tableName, properties["PointInTimeRecoverySpecification"], void 0, maskSecrets);
12286
12513
  await this.applyTimeToLive(tableName, properties["TimeToLiveSpecification"], void 0, "create", {
12287
12514
  logicalId,
@@ -12292,7 +12519,7 @@ var DynamoDBTableProvider = class {
12292
12519
  await this.applyContributorInsights(tableName, properties["ContributorInsightsSpecification"], void 0, maskSecrets);
12293
12520
  await this.applyIndexContributorInsights(tableName, properties["GlobalSecondaryIndexes"], void 0, maskSecrets);
12294
12521
  await this.applyStreamMembers(logicalId, resourceType, tableName, tableInfo.streamArn, properties["StreamSpecification"], void 0, true, maskSecrets);
12295
- this.logger.debug(`Successfully created DynamoDB table ${logicalId}: ${tableName}`);
12522
+ debug(`Successfully created DynamoDB table ${logicalId}: ${tableName}`);
12296
12523
  return {
12297
12524
  physicalId: tableName,
12298
12525
  attributes: {
@@ -12306,7 +12533,8 @@ var DynamoDBTableProvider = class {
12306
12533
  let tableLeftBehind = false;
12307
12534
  if (tableCreated) {
12308
12535
  try {
12309
- await this.dynamoDBClient.send(new DeleteTableCommand({ TableName: tableName }));
12536
+ await this.rollBackCreatedTable(logicalId, tableName, maskSecrets);
12537
+ retractCreatedResource(logicalId, resourceType, tableName);
12310
12538
  debug(`Rolled back partially-created DynamoDB table ${tableName}`);
12311
12539
  } catch (cleanupError) {
12312
12540
  tableLeftBehind = true;
@@ -12323,6 +12551,38 @@ var DynamoDBTableProvider = class {
12323
12551
  throw thrown;
12324
12552
  }
12325
12553
  }
12554
+ async rollBackCreatedTable(logicalId, tableName, maskSecrets) {
12555
+ const logger = maskedLogger(this.logger, maskSecrets, tableName);
12556
+ const allowanceMs = Math.min(resolveDynamoDbDeleteBudgetMs(), enclosingDeadlineRemainingMs() - DYNAMODB_DELETE_DEADLINE_MARGIN_MS);
12557
+ const budget = allowanceMs > 0 ? new ElapsedBudget(allowanceMs, resolveDynamoDbDeleteBudgetClock()) : void 0;
12558
+ await deleteTableWithIndexBusyRetry({
12559
+ logicalId,
12560
+ physicalId: tableName,
12561
+ typeLabel: "table",
12562
+ indexNoun: TABLE_INDEX_NOUN,
12563
+ maxRetries: 14,
12564
+ logger,
12565
+ deleteTable: async () => {
12566
+ await this.dynamoDBClient.send(new DeleteTableCommand({ TableName: tableName }));
12567
+ },
12568
+ reArm: () => {
12569
+ const wait = beginDeleteWait(900, budget);
12570
+ return waitForIndexesSettled({
12571
+ tableName,
12572
+ logicalId,
12573
+ logger,
12574
+ describeTable: () => this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName })),
12575
+ maxAttempts: wait.grantedPolls,
12576
+ budgetWait: wait,
12577
+ shortCapUnlessVectorBusy: 60,
12578
+ proceedNote: DELETE_INDEX_WAIT_PROCEED_NOTE
12579
+ });
12580
+ },
12581
+ shouldKeepRetrying: () => budget !== void 0 && !budget.isExhausted(),
12582
+ retryWarning: () => `DynamoDB table ${logicalId}: AWS refused the DeleteTable rolling back the partially-created table ${tableName} because ${TABLE_INDEX_NOUN} is still being created, updated or deleted. Waiting for the indexes to settle and retrying (up to ${14} retries in all, within ~${Math.max(1, Math.round((budget?.totalMs ?? 0) / 6e4))} minutes).`,
12583
+ sleepSeam: deleteTableRetryDelays
12584
+ });
12585
+ }
12326
12586
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
12327
12587
  const markUnsettled = (key) => context?.markUnsettled?.(key);
12328
12588
  const maskSecrets = context?.maskSecrets ?? ((text) => text);
@@ -12367,10 +12627,19 @@ var DynamoDBTableProvider = class {
12367
12627
  if (context?.replayingState !== true && context?.desiredFromAwsReadback !== true) throw new ProvisioningError(`${lsiMessage}. Nothing was applied to the table; revert the LocalSecondaryIndexes change, or create a new table under a new logical ID (a new table starts empty)`, resourceType, logicalId, physicalId);
12368
12628
  warn(`${lsiMessage}; the LocalSecondaryIndexes change was not applied`);
12369
12629
  }
12630
+ const vectorIndexPlan = JSON.stringify(properties["VectorIndexes"]) !== JSON.stringify(previousProperties["VectorIndexes"]) ? planVectorIndexUpdates(previousProperties["VectorIndexes"], properties["VectorIndexes"], table?.VectorIndexes ?? (table === void 0 ? void 0 : [])) : void 0;
12631
+ if (vectorIndexPlan !== void 0 && vectorIndexPlan.changed.length > 0) {
12632
+ const vectorMessage = `AWS::DynamoDB::Table ${logicalId}: VectorIndexes ${vectorIndexPlan.changed.map((n) => maskSecrets(n)).join(", ")} changed definition, which DynamoDB cannot modify in place`;
12633
+ if (context?.replayingState !== true && context?.desiredFromAwsReadback !== true) throw new ProvisioningError(`${maskSecrets(vectorMessage)}. Nothing was applied to the table; rename the index (so it is dropped and re-created) or revert the change`, resourceType, logicalId, physicalId);
12634
+ warn(`${vectorMessage}; that change was not applied`);
12635
+ }
12370
12636
  let latestStreamArn = table?.LatestStreamArn;
12371
12637
  if (table?.TableArn) await this.applyTagDiff(table.TableArn, resourceType, logicalId, previousProperties["Tags"], properties["Tags"], {
12372
12638
  forceUpsert: context?.unsettledProperties?.has("Tags") === true,
12373
- beforeWrite: () => markUnsettled("Tags")
12639
+ beforeWrite: () => markUnsettled("Tags"),
12640
+ afterAdded: (keys) => {
12641
+ if (keys.length > 0) context?.markAttempted?.("Tags", keys);
12642
+ }
12374
12643
  });
12375
12644
  const normalizeTableClass = (v) => typeof v === "string" && v.length > 0 ? v : "STANDARD";
12376
12645
  const tableClassChanged = normalizeTableClass(properties["TableClass"]) !== normalizeTableClass(previousProperties["TableClass"]);
@@ -12482,7 +12751,7 @@ var DynamoDBTableProvider = class {
12482
12751
  if (tableClassChanged) markUnsettled("TableClass");
12483
12752
  if (updateInput.GlobalSecondaryIndexUpdates !== void 0) markUnsettled("GlobalSecondaryIndexes");
12484
12753
  await this.dynamoDBClient.send(new UpdateTableCommand(updateInput));
12485
- if (billingMode !== liveBillingMode) await this.waitForTableAndIndexesActive(physicalId, BILLING_FLIP_ACTIVE_WAIT_ATTEMPTS);
12754
+ if (billingMode !== liveBillingMode) await this.waitForTableAndIndexesActive(physicalId, BILLING_FLIP_ACTIVE_WAIT_ATTEMPTS, { maskSecrets });
12486
12755
  else await this.waitForTableActiveAfterUpdate(physicalId, 60);
12487
12756
  this.logger.debug(`Updated BillingMode/ProvisionedThroughput/TableClass on DynamoDB table ${physicalId}`);
12488
12757
  }
@@ -12595,8 +12864,9 @@ var DynamoDBTableProvider = class {
12595
12864
  if (context?.unsettledProperties?.has("GlobalSecondaryIndexes") === true && currentLiveIndexNames !== void 0) this.warnUndeclaredLiveIndexes(physicalId, currentLiveIndexNames, [previousProperties["GlobalSecondaryIndexes"], properties["GlobalSecondaryIndexes"]], maskSecrets);
12596
12865
  if (JSON.stringify(properties["GlobalSecondaryIndexes"]) !== JSON.stringify(previousProperties["GlobalSecondaryIndexes"])) {
12597
12866
  markUnsettled("GlobalSecondaryIndexes");
12598
- await this.applyGsiUpdates(physicalId, resourceType, logicalId, previousProperties["GlobalSecondaryIndexes"], properties["GlobalSecondaryIndexes"], properties["AttributeDefinitions"], gsiHandledByBillingFlip, currentLiveIndexNames, currentLiveIndexByName, liveCapacityComparable, effectiveBillingMode === "PAY_PER_REQUEST", maskSecrets);
12867
+ await this.applyGsiUpdates(physicalId, resourceType, logicalId, previousProperties["GlobalSecondaryIndexes"], properties["GlobalSecondaryIndexes"], properties["AttributeDefinitions"], gsiHandledByBillingFlip, currentLiveIndexNames, currentLiveIndexByName, liveCapacityComparable, effectiveBillingMode === "PAY_PER_REQUEST", maskSecrets, (indexName) => context?.markAttempted?.("GlobalSecondaryIndexes", [indexName]));
12599
12868
  }
12869
+ if (vectorIndexPlan !== void 0) await this.applyVectorIndexUpdates(physicalId, vectorIndexPlan, properties["AttributeDefinitions"], maskSecrets, () => markUnsettled("VectorIndexes"), (indexName) => context?.markAttempted?.("VectorIndexes", [indexName]));
12600
12870
  if (JSON.stringify(properties["PointInTimeRecoverySpecification"]) !== JSON.stringify(previousProperties["PointInTimeRecoverySpecification"])) {
12601
12871
  markUnsettled("PointInTimeRecoverySpecification");
12602
12872
  await this.applyPointInTimeRecovery(physicalId, properties["PointInTimeRecoverySpecification"], previousProperties["PointInTimeRecoverySpecification"], maskSecrets);
@@ -12712,16 +12982,18 @@ var DynamoDBTableProvider = class {
12712
12982
  }
12713
12983
  try {
12714
12984
  const reArmPolls = () => {
12715
- const wait = beginDeleteWait(60, budget);
12985
+ const wait = beginDeleteWait(900, budget);
12716
12986
  return {
12717
12987
  maxAttempts: wait.grantedPolls,
12718
- budgetWait: wait
12988
+ budgetWait: wait,
12989
+ shortCapUnlessVectorBusy: 60
12719
12990
  };
12720
12991
  };
12721
12992
  await deleteTableWithIndexBusyRetry({
12722
12993
  logicalId,
12723
12994
  physicalId,
12724
12995
  typeLabel: "table",
12996
+ indexNoun: TABLE_INDEX_NOUN,
12725
12997
  maxRetries: 14,
12726
12998
  logger: loopLogger,
12727
12999
  deleteTable: async () => {
@@ -12743,6 +13015,22 @@ var DynamoDBTableProvider = class {
12743
13015
  },
12744
13016
  sleepSeam: deleteTableRetryDelays
12745
13017
  });
13018
+ const mask = maskerOrIdentity(context?.maskSecrets);
13019
+ await waitForGoneAfterDelete({
13020
+ what: mask(`DynamoDB table ${physicalId}`),
13021
+ resourceType,
13022
+ describe: async () => {
13023
+ try {
13024
+ return (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }))).Table?.TableStatus ?? "UNKNOWN";
13025
+ } catch (describeError) {
13026
+ if (describeError instanceof ResourceNotFoundException$1) return void 0;
13027
+ throw describeError;
13028
+ }
13029
+ },
13030
+ logger: loopLogger,
13031
+ pollIntervalMs: tableDeleteGoneWait.pollIntervalMs,
13032
+ maxWaitMs: Math.min(tableDeleteGoneWait.maxWaitMs, budget.remainingMs())
13033
+ });
12746
13034
  this.logger.debug(`Successfully deleted DynamoDB table ${logicalId}`);
12747
13035
  this.deleteBudgets.release(deleteBudgetKey(physicalId, context?.expectedRegion));
12748
13036
  this.protectionFlips.release(deleteBudgetKey(physicalId, context?.expectedRegion));
@@ -12786,6 +13074,13 @@ var DynamoDBTableProvider = class {
12786
13074
  ResourceArn: tableArn,
12787
13075
  Tags: tagsToAdd
12788
13076
  }));
13077
+ let recordedKeys;
13078
+ try {
13079
+ recordedKeys = /* @__PURE__ */ new Set([...planTagDiff(void 0, oldTagsRaw).set.keys()]);
13080
+ } catch {
13081
+ recordedKeys = void 0;
13082
+ }
13083
+ unsettled.afterAdded?.(tagsToAdd.map((t) => t.Key).filter((key) => recordedKeys !== void 0 && !recordedKeys.has(key)));
12789
13084
  this.logger.debug(`Added/updated ${tagsToAdd.length} tag(s) on DynamoDB table ${tableArn}`);
12790
13085
  }
12791
13086
  }
@@ -13090,7 +13385,7 @@ var DynamoDBTableProvider = class {
13090
13385
  const warn = (message) => this.logger.warn(maskSecrets(message));
13091
13386
  const ops = planIndexContributorInsightsOps(desiredIndexes, previousIndexes, (indexName, reason) => warn(`${this.indexScopeAt(indexName, tableName, maskSecrets)}: ${reason}, so its Contributor Insights was left as it is. Fix the template value and re-deploy.`));
13092
13387
  if (ops.length === 0) return;
13093
- await this.waitForTableAndIndexesActive(tableName);
13388
+ await this.waitForTableAndIndexesActive(tableName, void 0, { maskSecrets });
13094
13389
  for (const op of ops) {
13095
13390
  beforeSend();
13096
13391
  await this.sendContributorInsightsOp(tableName, op, maskSecrets);
@@ -13107,11 +13402,11 @@ var DynamoDBTableProvider = class {
13107
13402
  })), `set ContributorInsights on ${target}`);
13108
13403
  debug(`Set ContributorInsightsAction=${op.action}${op.mode !== void 0 ? ` Mode=${op.mode}` : ""} on ${target}`);
13109
13404
  }
13110
- async waitForTableActive(tableName, maxAttempts = 60) {
13405
+ async waitForTableActive(tableName, maskSecrets, maxAttempts = 60) {
13111
13406
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
13112
13407
  const response = await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }));
13113
13408
  const status = response.Table?.TableStatus;
13114
- this.logger.debug(`Table ${tableName} status: ${status} (attempt ${attempt}/${maxAttempts})`);
13409
+ this.logger.debug(maskSecrets(`Table ${tableName} status: ${status} (attempt ${attempt}/${maxAttempts})`));
13115
13410
  if (status === "ACTIVE") return {
13116
13411
  tableArn: response.Table?.TableArn,
13117
13412
  tableId: response.Table?.TableId,
@@ -13149,17 +13444,38 @@ var DynamoDBTableProvider = class {
13149
13444
  }
13150
13445
  throw new Error(maskSecrets(`DynamoDB table ${tableName} was still ${last ?? "unread"} ${KINESIS_DESTINATION_SETTLE_POLLS * KINESIS_DESTINATION_SETTLE_POLL_MS / 1e3} seconds after its encryption change`));
13151
13446
  }
13152
- async waitForTableAndIndexesActive(tableName, maxAttempts = 1800) {
13447
+ async waitForTableAndIndexesActive(tableName, maxAttempts = 1800, opts = {}) {
13153
13448
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
13154
13449
  const table = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table;
13155
13450
  const tableActive = table?.TableStatus === "ACTIVE";
13156
13451
  const indexesActive = (table?.GlobalSecondaryIndexes ?? []).every((gsi) => gsi.IndexStatus === "ACTIVE");
13157
- if (tableActive && indexesActive) return;
13452
+ if (tableActive && indexesActive && vectorIndexesSettled(table?.VectorIndexes)) return;
13158
13453
  await new Promise((resolve) => setTimeout(resolve, 1e3));
13159
13454
  }
13160
- throw new Error(`Table ${tableName} and its global secondary indexes did not reach ACTIVE within ${maxAttempts} seconds after UpdateTable`);
13455
+ const m = maskerOrIdentity(opts.maskSecrets);
13456
+ throw new Error(m(`Table ${m(tableName)} and its indexes did not reach ACTIVE within ${maxAttempts} seconds after ${opts.after ?? "UpdateTable"}`));
13457
+ }
13458
+ async applyVectorIndexUpdates(physicalId, plan, desiredAttributeDefinitions, maskSecrets, beforeFirstOp, onCreated) {
13459
+ const debug = (message) => this.logger.debug(maskSecrets(message));
13460
+ const warn = (message) => this.logger.warn(maskSecrets(message));
13461
+ for (const name of plan.alreadyGone) debug(`${this.vectorIndexScopeAt(name, physicalId, maskSecrets)} is recorded in cdkd state but not live; skipping its Delete (already removed)`);
13462
+ for (const name of plan.adopted) warn(`${this.vectorIndexScopeAt(name, physicalId, maskSecrets)} already exists in AWS with the declared definition but is absent from cdkd's recorded previous state, so its Create is being skipped. This usually means an earlier deploy created the index and then failed before state was written.`);
13463
+ if (plan.ops.length === 0) return;
13464
+ await this.waitForTableAndIndexesActive(physicalId, void 0, { maskSecrets });
13465
+ beforeFirstOp();
13466
+ for (const op of plan.ops) {
13467
+ const input = {
13468
+ TableName: physicalId,
13469
+ VectorIndexUpdates: [op]
13470
+ };
13471
+ if (op.Create && desiredAttributeDefinitions) input.AttributeDefinitions = desiredAttributeDefinitions;
13472
+ await this.dynamoDBClient.send(new UpdateTableCommand(input));
13473
+ if (op.Create?.IndexName !== void 0) onCreated(op.Create.IndexName);
13474
+ await this.waitForTableAndIndexesActive(physicalId, void 0, { maskSecrets });
13475
+ debug(`${op.Create ? "created" : "deleted"} ` + this.vectorIndexScopeAt(op.Create?.IndexName ?? op.Delete?.IndexName, physicalId, maskSecrets));
13476
+ }
13161
13477
  }
13162
- async applyGsiUpdates(physicalId, resourceType, logicalId, previousGsis, desiredGsis, desiredAttributeDefinitions, handledByBillingFlip, liveIndexNames, liveIndexByName, liveCapacityComparable, onDemandCeilingsSendable, maskSecrets) {
13478
+ async applyGsiUpdates(physicalId, resourceType, logicalId, previousGsis, desiredGsis, desiredAttributeDefinitions, handledByBillingFlip, liveIndexNames, liveIndexByName, liveCapacityComparable, onDemandCeilingsSendable, maskSecrets, onCreated = () => void 0) {
13163
13479
  const warn = (message) => this.logger.warn(maskSecrets(message));
13164
13480
  const debug = (message) => this.logger.debug(maskSecrets(message));
13165
13481
  const prev = previousGsis ?? [];
@@ -13234,7 +13550,7 @@ var DynamoDBTableProvider = class {
13234
13550
  }
13235
13551
  if (updateHasMember) ops.push({ Update: update });
13236
13552
  }
13237
- await this.runGsiOps(physicalId, ops, desiredAttributeDefinitions, maskSecrets);
13553
+ await this.runGsiOps(physicalId, ops, desiredAttributeDefinitions, maskSecrets, onCreated);
13238
13554
  }
13239
13555
  coerceWarmThroughputForSend(scope, value, maskSecrets) {
13240
13556
  const warn = (message) => this.logger.warn(maskSecrets(message));
@@ -13261,6 +13577,9 @@ var DynamoDBTableProvider = class {
13261
13577
  this.logger.warn(maskSecrets(`${this.indexScopeAt(name, physicalId, maskSecrets)} is live, but neither cdkd's record nor the template declares it, so cdkd left it in place while converging the unsettled GlobalSecondaryIndexes (CloudFormation never deletes an index it did not record). It may belong to another tool, or an earlier failed cdkd update may have created it; cdkd cannot tell which. If it should not exist, delete it ` + (secretDerived ? `with an UpdateTable GlobalSecondaryIndexUpdates Delete for the index.` : `with: aws dynamodb update-table --table-name ${physicalId} --global-secondary-index-updates '[{"Delete":{"IndexName":"${name}"}}]' (in the table's region)`)));
13262
13578
  }
13263
13579
  }
13580
+ vectorIndexScopeAt(indexName, physicalId, maskSecrets) {
13581
+ return `vector index ${typeof indexName === "string" ? maskSecrets(indexName) : "<unnamed>"} on DynamoDB table ${maskSecrets(physicalId)}`;
13582
+ }
13264
13583
  indexScopeAt(indexName, physicalId, maskSecrets) {
13265
13584
  return `GSI ${typeof indexName === "string" ? maskSecrets(indexName) : "<unnamed>"} on DynamoDB table ${physicalId}`;
13266
13585
  }
@@ -13398,7 +13717,7 @@ var DynamoDBTableProvider = class {
13398
13717
  }
13399
13718
  return sendable;
13400
13719
  }
13401
- async runGsiOps(physicalId, ops, desiredAttributeDefinitions, maskSecrets) {
13720
+ async runGsiOps(physicalId, ops, desiredAttributeDefinitions, maskSecrets, onCreated = () => void 0) {
13402
13721
  const debug = (message) => this.logger.debug(maskSecrets(message));
13403
13722
  for (const op of ops) {
13404
13723
  const input = {
@@ -13407,7 +13726,8 @@ var DynamoDBTableProvider = class {
13407
13726
  };
13408
13727
  if (op.Create && desiredAttributeDefinitions) input.AttributeDefinitions = desiredAttributeDefinitions;
13409
13728
  await this.dynamoDBClient.send(new UpdateTableCommand(input));
13410
- await this.waitForTableAndIndexesActive(physicalId);
13729
+ if (op.Create?.IndexName !== void 0) onCreated(op.Create.IndexName);
13730
+ await this.waitForTableAndIndexesActive(physicalId, void 0, { maskSecrets });
13411
13731
  debug(`${op.Create ? "created" : op.Delete ? "deleted" : "updated"} ${this.indexScopeAt(op.Create?.IndexName ?? op.Delete?.IndexName ?? op.Update?.IndexName, physicalId, maskSecrets)}`);
13412
13732
  }
13413
13733
  }
@@ -13469,7 +13789,8 @@ var DynamoDBTableProvider = class {
13469
13789
  return [
13470
13790
  "AttributeDefinitions",
13471
13791
  "GlobalSecondaryIndexes[]",
13472
- "LocalSecondaryIndexes[]"
13792
+ "LocalSecondaryIndexes[]",
13793
+ "VectorIndexes"
13473
13794
  ];
13474
13795
  }
13475
13796
  getDriftUnknownPaths(resourceType, properties) {
@@ -13547,15 +13868,19 @@ var DynamoDBTableProvider = class {
13547
13868
  aws
13548
13869
  };
13549
13870
  }
13550
- async readUnsettled(physicalId, logicalId, resourceType, names, recordedProperties, desiredProperties) {
13871
+ async readUnsettled(physicalId, logicalId, resourceType, names, recordedProperties, desiredProperties, attemptedItems) {
13551
13872
  const live = await this.readCurrentState(physicalId, logicalId, resourceType, recordedProperties);
13552
13873
  if (typeof live !== "object" || live === null) return live;
13553
13874
  const out = { ...live };
13554
13875
  if (out["BillingMode"] === "PAY_PER_REQUEST") delete out["ProvisionedThroughput"];
13555
13876
  if (names.has("GlobalSecondaryIndexes")) {
13556
- out["GlobalSecondaryIndexes"] = projectIndexesOnto(out["GlobalSecondaryIndexes"], recordedProperties?.["GlobalSecondaryIndexes"], desiredProperties?.["GlobalSecondaryIndexes"]);
13877
+ out["GlobalSecondaryIndexes"] = projectIndexesOnto(out["GlobalSecondaryIndexes"], recordedProperties?.["GlobalSecondaryIndexes"], desiredProperties?.["GlobalSecondaryIndexes"], attemptedItems?.get("GlobalSecondaryIndexes"));
13557
13878
  if (out["GlobalSecondaryIndexes"] === void 0) delete out["GlobalSecondaryIndexes"];
13558
13879
  }
13880
+ if (names.has("VectorIndexes")) {
13881
+ out["VectorIndexes"] = projectIndexesOnto(out["VectorIndexes"], recordedProperties?.["VectorIndexes"], desiredProperties?.["VectorIndexes"], attemptedItems?.get("VectorIndexes"));
13882
+ if (out["VectorIndexes"] === void 0) delete out["VectorIndexes"];
13883
+ }
13559
13884
  const strict = async (key, read) => {
13560
13885
  if (!names.has(key)) return;
13561
13886
  const value = await read();
@@ -13644,6 +13969,8 @@ var DynamoDBTableProvider = class {
13644
13969
  const desiredByName = desiredIndexEntriesByName(properties?.["LocalSecondaryIndexes"]);
13645
13970
  result["LocalSecondaryIndexes"] = table.LocalSecondaryIndexes.map((live) => reverseMapSecondaryIndex(live, live.IndexName === void 0 ? void 0 : desiredByName.get(live.IndexName), bagInformative));
13646
13971
  }
13972
+ const liveVectorIndexes = readbackVectorIndexes(table.VectorIndexes);
13973
+ if (liveVectorIndexes !== void 0) result["VectorIndexes"] = liveVectorIndexes;
13647
13974
  if (table.SSEDescription?.Status === "ENABLED") {
13648
13975
  const sse = { SSEEnabled: true };
13649
13976
  if (table.SSEDescription.KMSMasterKeyArn !== void 0) sse["KMSMasterKeyId"] = table.SSEDescription.KMSMasterKeyArn;
@@ -13870,9 +14197,9 @@ var DynamoDBTableProvider = class {
13870
14197
  function isTransientStreamReadFailure(err) {
13871
14198
  return isThrottlingError(err) || isTransientServerError(err) || isAmbiguousOutcomeError(err) || err instanceof ResourceNotFoundException$1;
13872
14199
  }
13873
- function projectIndexesOnto(live, recorded, desired) {
14200
+ function projectIndexesOnto(live, recorded, desired, attempted = /* @__PURE__ */ new Set()) {
13874
14201
  if (!Array.isArray(live)) return void 0;
13875
- const named = /* @__PURE__ */ new Set();
14202
+ const named = new Set([...attempted].filter(isUsableAttemptedName));
13876
14203
  for (const list of [recorded, desired]) {
13877
14204
  if (!Array.isArray(list)) continue;
13878
14205
  for (const entry of list) {
@@ -13928,7 +14255,7 @@ var LogsLogGroupProvider = class {
13928
14255
  "DeletionProtectionEnabled",
13929
14256
  "BearerTokenAuthenticationEnabled"
13930
14257
  ])]]);
13931
- unhandledByDesign = /* @__PURE__ */ new Map([["AWS::Logs::LogGroup", /* @__PURE__ */ new Map([["ResourcePolicyDocument", "covered by the separate account-wide AWS::Logs::ResourcePolicy resource type; logs:PutResourcePolicy is account-scoped, not per-log-group, so it has no CreateLogGroup / PutRetentionPolicy counterpart — routed via Cloud Control instead"]])]]);
14258
+ unhandledByDesign = /* @__PURE__ */ new Map([["AWS::Logs::LogGroup", /* @__PURE__ */ new Map([["ResourcePolicyDocument", "not driven by this provider by decision (issue #1412): no CreateLogGroup / PutRetentionPolicy member carries it, and both policy lifecycles — the account-scoped AWS::Logs::ResourcePolicy type and the log-group-scoped logs:PutResourcePolicy (resourceArn + policyScope RESOURCE) — are ones this provider does not take on — routed via Cloud Control, whose handler applies it"]])]]);
13932
14259
  constructor() {
13933
14260
  const awsClients = getAwsClients();
13934
14261
  this.logsClient = awsClients.cloudWatchLogs;
@@ -13991,6 +14318,7 @@ var LogsLogGroupProvider = class {
13991
14318
  await this.logsClient.send(new CreateLogGroupCommand(createParams));
13992
14319
  createdNewLogGroup = true;
13993
14320
  leftBehind = true;
14321
+ reportCreatedResource(logicalId, resourceType, logGroupName);
13994
14322
  } catch (createError) {
13995
14323
  if (createError instanceof ResourceAlreadyExistsException) this.logger.debug(`Log group ${logGroupName} already exists, using existing`);
13996
14324
  else throw createError;
@@ -14037,6 +14365,7 @@ var LogsLogGroupProvider = class {
14037
14365
  if (createdNewLogGroup) try {
14038
14366
  await this.logsClient.send(new DeleteLogGroupCommand({ logGroupName }));
14039
14367
  leftBehind = false;
14368
+ retractCreatedResource(logicalId, resourceType, logGroupName);
14040
14369
  this.logger.debug(`Cleaned up partially-created log group ${displaySafe(logicalId)} (${displaySafe(logGroupName)}) after wiring failure`);
14041
14370
  } catch (cleanupError) {
14042
14371
  const mask = maskerOrIdentity(context?.maskSecrets);
@@ -14387,6 +14716,29 @@ var LogsLogGroupProvider = class {
14387
14716
 
14388
14717
  //#endregion
14389
14718
  //#region src/provisioning/providers/cloudwatch-alarm-provider.ts
14719
+ function isDefaultEvaluationWindow(value) {
14720
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
14721
+ const keys = Object.keys(value);
14722
+ if (keys.length !== 1 || keys[0] !== "SlidingWindow") return false;
14723
+ const sliding = value["SlidingWindow"];
14724
+ return sliding !== null && typeof sliding === "object" && !Array.isArray(sliding) && Object.keys(sliding).length === 0;
14725
+ }
14726
+ const PROMQL_EXCLUDED_MEMBERS = [
14727
+ "Namespace",
14728
+ "MetricName",
14729
+ "Dimensions",
14730
+ "Period",
14731
+ "Unit",
14732
+ "Statistic",
14733
+ "ExtendedStatistic",
14734
+ "Metrics",
14735
+ "Threshold",
14736
+ "ComparisonOperator",
14737
+ "ThresholdMetricId",
14738
+ "EvaluationPeriods",
14739
+ "DatapointsToAlarm",
14740
+ "EvaluationWindow"
14741
+ ];
14390
14742
  function isAlarmWriteThrottle(error) {
14391
14743
  return isThrottlingError(error) || error instanceof Error && error.message.includes("Rate exceeded");
14392
14744
  }
@@ -14422,9 +14774,13 @@ var CloudWatchAlarmProvider = class {
14422
14774
  "Tags",
14423
14775
  "ExtendedStatistic",
14424
14776
  "EvaluateLowSampleCountPercentile",
14425
- "ThresholdMetricId"
14777
+ "ThresholdMetricId",
14778
+ "ActionConfiguration",
14779
+ "WarmUpConfiguration",
14780
+ "EvaluationWindow",
14781
+ "EvaluationCriteria",
14782
+ "EvaluationInterval"
14426
14783
  ])]]);
14427
- unhandledByDesign = /* @__PURE__ */ new Map([["AWS::CloudWatch::Alarm", /* @__PURE__ */ new Map([["EvaluationCriteria", "Absent from both the SDK PutMetricAlarm input and the aws-cdk-lib CfnAlarm L1 (a newer CFn-schema-only property ahead of SDK/CDK support); no wire path to forward it and no CDK app can emit it."], ["EvaluationInterval", "Absent from both the SDK PutMetricAlarm input and the aws-cdk-lib CfnAlarm L1 (a newer CFn-schema-only property ahead of SDK/CDK support); no wire path to forward it and no CDK app can emit it."]])]]);
14428
14784
  constructor() {
14429
14785
  const awsClients = getAwsClients();
14430
14786
  this.cloudWatchClient = awsClients.cloudWatch;
@@ -14573,7 +14929,12 @@ var CloudWatchAlarmProvider = class {
14573
14929
  Unit: emptyToUndefined(properties["Unit"]),
14574
14930
  ExtendedStatistic: emptyToUndefined(properties["ExtendedStatistic"]),
14575
14931
  EvaluateLowSampleCountPercentile: emptyToUndefined(properties["EvaluateLowSampleCountPercentile"]),
14576
- ThresholdMetricId: emptyToUndefined(properties["ThresholdMetricId"])
14932
+ ThresholdMetricId: emptyToUndefined(properties["ThresholdMetricId"]),
14933
+ ActionConfiguration: properties["ActionConfiguration"],
14934
+ WarmUpConfiguration: properties["WarmUpConfiguration"],
14935
+ EvaluationWindow: properties["EvaluationWindow"],
14936
+ EvaluationCriteria: properties["EvaluationCriteria"],
14937
+ EvaluationInterval: properties["EvaluationInterval"]
14577
14938
  };
14578
14939
  const metricsValue = properties["Metrics"];
14579
14940
  if (Array.isArray(metricsValue) && metricsValue.length > 0) params["Metrics"] = metricsValue.map((m) => {
@@ -14605,6 +14966,7 @@ var CloudWatchAlarmProvider = class {
14605
14966
  params["Period"] = properties["Period"];
14606
14967
  params["Statistic"] = emptyToUndefined(properties["Statistic"]);
14607
14968
  params["Dimensions"] = properties["Dimensions"];
14969
+ if (properties["EvaluationCriteria"] !== void 0 && Array.isArray(params["Dimensions"]) && params["Dimensions"].length === 0) delete params["Dimensions"];
14608
14970
  }
14609
14971
  return params;
14610
14972
  }
@@ -14615,30 +14977,40 @@ var CloudWatchAlarmProvider = class {
14615
14977
  }))).MetricAlarms?.[0];
14616
14978
  if (!alarm) return RESOURCE_NOT_FOUND;
14617
14979
  const result = {};
14980
+ const promql = alarm.EvaluationCriteria !== void 0;
14618
14981
  if (alarm.AlarmName !== void 0) result["AlarmName"] = alarm.AlarmName;
14619
14982
  result["AlarmDescription"] = alarm.AlarmDescription ?? "";
14620
- result["MetricName"] = alarm.MetricName ?? "";
14621
- result["Namespace"] = alarm.Namespace ?? "";
14622
- result["Statistic"] = alarm.Statistic ?? "";
14623
- if (alarm.ComparisonOperator !== void 0) result["ComparisonOperator"] = alarm.ComparisonOperator;
14624
- if (alarm.Threshold !== void 0) result["Threshold"] = alarm.Threshold;
14625
- if (alarm.EvaluationPeriods !== void 0) result["EvaluationPeriods"] = alarm.EvaluationPeriods;
14626
- if (alarm.Period !== void 0) result["Period"] = alarm.Period;
14627
- if (alarm.DatapointsToAlarm !== void 0) result["DatapointsToAlarm"] = alarm.DatapointsToAlarm;
14983
+ if (!promql) {
14984
+ result["MetricName"] = alarm.MetricName ?? "";
14985
+ result["Namespace"] = alarm.Namespace ?? "";
14986
+ result["Statistic"] = alarm.Statistic ?? "";
14987
+ if (alarm.ComparisonOperator !== void 0) result["ComparisonOperator"] = alarm.ComparisonOperator;
14988
+ if (alarm.Threshold !== void 0) result["Threshold"] = alarm.Threshold;
14989
+ if (alarm.EvaluationPeriods !== void 0) result["EvaluationPeriods"] = alarm.EvaluationPeriods;
14990
+ if (alarm.Period !== void 0) result["Period"] = alarm.Period;
14991
+ if (alarm.DatapointsToAlarm !== void 0) result["DatapointsToAlarm"] = alarm.DatapointsToAlarm;
14992
+ }
14628
14993
  result["ActionsEnabled"] = alarm.ActionsEnabled ?? true;
14629
14994
  result["AlarmActions"] = alarm.AlarmActions ? [...alarm.AlarmActions] : [];
14630
14995
  result["OKActions"] = alarm.OKActions ? [...alarm.OKActions] : [];
14631
14996
  result["InsufficientDataActions"] = alarm.InsufficientDataActions ? [...alarm.InsufficientDataActions] : [];
14632
14997
  result["TreatMissingData"] = alarm.TreatMissingData ?? "";
14633
- result["Unit"] = alarm.Unit ?? "";
14634
- result["Dimensions"] = (alarm.Dimensions ?? []).map((d) => ({
14635
- ...d.Name !== void 0 ? { Name: d.Name } : {},
14636
- ...d.Value !== void 0 ? { Value: d.Value } : {}
14637
- }));
14638
- result["Metrics"] = (alarm.Metrics ?? []).map((m) => m);
14639
- if (alarm.ExtendedStatistic !== void 0) result["ExtendedStatistic"] = alarm.ExtendedStatistic;
14998
+ if (!promql) {
14999
+ result["Unit"] = alarm.Unit ?? "";
15000
+ result["Dimensions"] = (alarm.Dimensions ?? []).map((d) => ({
15001
+ ...d.Name !== void 0 ? { Name: d.Name } : {},
15002
+ ...d.Value !== void 0 ? { Value: d.Value } : {}
15003
+ }));
15004
+ result["Metrics"] = (alarm.Metrics ?? []).map((m) => m);
15005
+ }
15006
+ if (!promql && alarm.ExtendedStatistic !== void 0) result["ExtendedStatistic"] = alarm.ExtendedStatistic;
14640
15007
  if (alarm.EvaluateLowSampleCountPercentile !== void 0) result["EvaluateLowSampleCountPercentile"] = alarm.EvaluateLowSampleCountPercentile;
14641
- if (alarm.ThresholdMetricId !== void 0) result["ThresholdMetricId"] = alarm.ThresholdMetricId;
15008
+ if (!promql && alarm.ThresholdMetricId !== void 0) result["ThresholdMetricId"] = alarm.ThresholdMetricId;
15009
+ if (alarm.ActionConfiguration !== void 0) result["ActionConfiguration"] = alarm.ActionConfiguration;
15010
+ if (alarm.WarmUpConfiguration !== void 0) result["WarmUpConfiguration"] = alarm.WarmUpConfiguration;
15011
+ if (!promql && alarm.EvaluationWindow !== void 0 && !isDefaultEvaluationWindow(alarm.EvaluationWindow)) result["EvaluationWindow"] = alarm.EvaluationWindow;
15012
+ if (alarm.EvaluationCriteria !== void 0) result["EvaluationCriteria"] = alarm.EvaluationCriteria;
15013
+ if (promql && alarm.EvaluationInterval !== void 0) result["EvaluationInterval"] = alarm.EvaluationInterval;
14642
15014
  if (alarm.AlarmArn) try {
14643
15015
  const tagsResp = await this.cloudWatchClient.send(new ListTagsForResourceCommand$4({ ResourceARN: alarm.AlarmArn }));
14644
15016
  result["Tags"] = normalizeAwsTagsToCfn(tagsResp.Tags);
@@ -14647,6 +15019,26 @@ var CloudWatchAlarmProvider = class {
14647
15019
  }
14648
15020
  return result;
14649
15021
  }
15022
+ async canonicalizeDriftPair(resourceType, baseline, aws) {
15023
+ if (resourceType !== "AWS::CloudWatch::Alarm" || !isDefaultEvaluationWindow(baseline["EvaluationWindow"]) || aws["EvaluationWindow"] !== void 0) return {
15024
+ baseline,
15025
+ aws
15026
+ };
15027
+ const trimmed = { ...baseline };
15028
+ delete trimmed["EvaluationWindow"];
15029
+ return {
15030
+ baseline: trimmed,
15031
+ aws
15032
+ };
15033
+ }
15034
+ getDriftAbsentMeansDefaultKeys(resourceType, properties) {
15035
+ if (resourceType !== "AWS::CloudWatch::Alarm") return [];
15036
+ if (properties["EvaluationCriteria"] !== void 0) return [...PROMQL_EXCLUDED_MEMBERS].filter((key) => properties[key] === void 0);
15037
+ const keys = ["EvaluationCriteria", "EvaluationInterval"];
15038
+ const declared = properties["EvaluationWindow"];
15039
+ if (declared === void 0 || isDefaultEvaluationWindow(declared)) keys.push("EvaluationWindow");
15040
+ return keys;
15041
+ }
14650
15042
  generatedCreateName(_resourceType, logicalId, properties) {
14651
15043
  if (properties["AlarmName"]) return void 0;
14652
15044
  return generatedNameFor("AWS::CloudWatch::Alarm", logicalId);
@@ -15721,6 +16113,7 @@ var EventBridgeRuleProvider = class {
15721
16113
  if (properties["Tags"]) putRuleParams["Tags"] = desiredTags;
15722
16114
  const ruleArn = (await this.eventBridgeClient.send(new PutRuleCommand(putRuleParams))).RuleArn;
15723
16115
  this.logger.debug(`Created EventBridge rule: ${ruleName} (${ruleArn})`);
16116
+ if (heldBefore === "free") reportCreatedResource(logicalId, resourceType, ruleArn);
15724
16117
  const eventBusName = properties["EventBusName"];
15725
16118
  try {
15726
16119
  if (targets && targets.length > 0) {
@@ -15750,6 +16143,7 @@ var EventBridgeRuleProvider = class {
15750
16143
  Name: ruleName,
15751
16144
  EventBusName: eventBusName
15752
16145
  }));
16146
+ retractCreatedResource(logicalId, resourceType, ruleArn);
15753
16147
  this.logger.debug(`Cleaned up partially-created EventBridge rule ${logicalId} (${ruleName}) after wiring failure`);
15754
16148
  } catch (cleanupError) {
15755
16149
  leftLiveArn = ruleArn;
@@ -16122,6 +16516,7 @@ var EventBridgeBusProvider = class {
16122
16516
  if (properties["LogConfig"] !== void 0) createParams.LogConfig = properties["LogConfig"];
16123
16517
  const response = await (await this.getCreateClient()).send(new CreateEventBusCommand(createParams));
16124
16518
  busCreated = true;
16519
+ reportCreatedResource(logicalId, resourceType, name);
16125
16520
  const policy = policyDocument(properties["Policy"]);
16126
16521
  if (policy !== void 0) await this.eventBridgeClient.send(new PutPermissionCommand({
16127
16522
  EventBusName: name,
@@ -17151,6 +17546,7 @@ var EC2Provider = class {
17151
17546
  }
17152
17547
  const vpcId = response.Vpc.VpcId;
17153
17548
  ec2IdsCreatedByThisProcess.add(vpcId);
17549
+ reportCreatedResource(logicalId, resourceType, vpcId);
17154
17550
  const vpcReplayWindow = replayedSendWindow(response, attemptStartMs);
17155
17551
  if (vpcReplayWindow !== void 0) await this.reportPossibleOrphanVpcs(logicalId, cidrBlock, desiredTags, vpcReplayWindow, context);
17156
17552
  let defaultSgId;
@@ -17179,6 +17575,7 @@ var EC2Provider = class {
17179
17575
  } catch (innerError) {
17180
17576
  try {
17181
17577
  await this.ec2Client.send(new DeleteVpcCommand({ VpcId: vpcId }));
17578
+ retractCreatedResource(logicalId, resourceType, vpcId);
17182
17579
  this.logger.debug(`Cleaned up partially-created VPC ${logicalId} (${vpcId}) after wiring failure`);
17183
17580
  } catch (cleanupError) {
17184
17581
  leftBehindId = vpcId;
@@ -17334,6 +17731,7 @@ var EC2Provider = class {
17334
17731
  }
17335
17732
  const subnetId = response.Subnet.SubnetId;
17336
17733
  ec2IdsCreatedByThisProcess.add(subnetId);
17734
+ reportCreatedResource(logicalId, resourceType, subnetId);
17337
17735
  const subnetReplayWindow = replayedSendWindow(response, attemptStartMs);
17338
17736
  if (subnetReplayWindow !== void 0) await this.reportPossibleOrphanSubnets(logicalId, vpcId, cidrBlock, desiredTags, subnetReplayWindow, context);
17339
17737
  const availabilityZone = response.Subnet.AvailabilityZone;
@@ -17350,6 +17748,7 @@ var EC2Provider = class {
17350
17748
  } catch (innerError) {
17351
17749
  try {
17352
17750
  await this.ec2Client.send(new DeleteSubnetCommand({ SubnetId: subnetId }));
17751
+ retractCreatedResource(logicalId, resourceType, subnetId);
17353
17752
  this.logger.debug(`Cleaned up partially-created Subnet ${logicalId} (${subnetId}) after wiring failure`);
17354
17753
  } catch (cleanupError) {
17355
17754
  leftBehindId = subnetId;
@@ -17604,6 +18003,7 @@ var EC2Provider = class {
17604
18003
  } catch {
17605
18004
  createdId = allocationId;
17606
18005
  }
18006
+ reportCreatedResource(logicalId, resourceType, createdId);
17607
18007
  ec2IdsCreatedByThisProcess.add(allocationId);
17608
18008
  const eipReplayWindow = replayedSendWindow(response, attemptStartMs);
17609
18009
  if (eipReplayWindow !== void 0) await this.reportPossibleOrphanAddresses(logicalId, input, desiredTags, eipReplayWindow, context);
@@ -17636,6 +18036,7 @@ var EC2Provider = class {
17636
18036
  } finally {
17637
18037
  watch.dispose();
17638
18038
  }
18039
+ retractCreatedResource(logicalId, resourceType, createdId);
17639
18040
  createdId = void 0;
17640
18041
  this.logger.debug(safeMsg`Released partially-created EIP ${logicalId} (${allocatedId})`);
17641
18042
  } catch (cleanupError) {
@@ -17772,6 +18173,7 @@ var EC2Provider = class {
17772
18173
  VpnGatewayId: gatewayId
17773
18174
  }));
17774
18175
  attachSent = true;
18176
+ reportCreatedResource(logicalId, resourceType, physicalId);
17775
18177
  await this.waitForVpnGatewayAttachment(gatewayId, vpcId, "attached", logicalId);
17776
18178
  }
17777
18179
  this.logger.debug(`Successfully created VPCGatewayAttachment ${logicalId}: ${physicalId}`);
@@ -18068,6 +18470,7 @@ var EC2Provider = class {
18068
18470
  })));
18069
18471
  const natGatewayId = response.NatGateway.NatGatewayId;
18070
18472
  createdId = natGatewayId;
18473
+ reportCreatedResource(logicalId, resourceType, natGatewayId);
18071
18474
  if (!tagsApplied) await this.applyTags(natGatewayId, desiredTags, logicalId, maskerOrIdentity(context?.maskSecrets));
18072
18475
  let described;
18073
18476
  if (process.env["CDKD_NO_WAIT"] !== "true") {
@@ -18465,6 +18868,7 @@ var EC2Provider = class {
18465
18868
  ...TagSpecifications && { TagSpecifications }
18466
18869
  })));
18467
18870
  const groupId = response.GroupId;
18871
+ reportCreatedResource(logicalId, resourceType, groupId);
18468
18872
  try {
18469
18873
  const ingressRules = properties["SecurityGroupIngress"];
18470
18874
  const egressRules = properties["SecurityGroupEgress"];
@@ -18481,6 +18885,7 @@ var EC2Provider = class {
18481
18885
  } catch (innerError) {
18482
18886
  try {
18483
18887
  await this.ec2Client.send(new DeleteSecurityGroupCommand({ GroupId: groupId }));
18888
+ retractCreatedResource(logicalId, resourceType, groupId);
18484
18889
  this.logger.debug(`Cleaned up partially-created SecurityGroup ${logicalId} (${groupId}) after wiring failure`);
18485
18890
  } catch (cleanupError) {
18486
18891
  leftBehindId = groupId;
@@ -19027,6 +19432,7 @@ var EC2Provider = class {
19027
19432
  const instance = response.Instances?.[0];
19028
19433
  if (!instance?.InstanceId) throw new Error("No instance ID returned from RunInstances");
19029
19434
  const instanceId = instance.InstanceId;
19435
+ reportCreatedResource(logicalId, resourceType, instanceId);
19030
19436
  try {
19031
19437
  if (!tagsApplied) await this.applyTags(instanceId, desiredTags, logicalId, maskerOrIdentity(context?.maskSecrets));
19032
19438
  if (process.env["CDKD_NO_WAIT"] !== "true") {
@@ -19056,6 +19462,7 @@ var EC2Provider = class {
19056
19462
  } catch (innerError) {
19057
19463
  try {
19058
19464
  await this.ec2Client.send(new TerminateInstancesCommand({ InstanceIds: [instanceId] }));
19465
+ retractCreatedResource(logicalId, resourceType, instanceId);
19059
19466
  runInstancesToken.release();
19060
19467
  this.logger.debug(`Terminate requested for partially-created EC2 Instance ${logicalId} (${instanceId}) after wiring failure (not waiting for terminated state)`);
19061
19468
  } catch (cleanupError) {
@@ -21655,6 +22062,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
21655
22062
  documentationVersion: properties["DocumentationVersion"],
21656
22063
  canarySettings: toSdkCanarySettings(properties["CanarySetting"])
21657
22064
  }));
22065
+ reportCreatedResource(logicalId, resourceType, stageName);
21658
22066
  const methodSettings = properties["MethodSettings"];
21659
22067
  const postCreateOps = buildMethodSettingsPatchOps(void 0, methodSettings);
21660
22068
  const accessLogSetting = properties["AccessLogSetting"];
@@ -21688,6 +22096,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
21688
22096
  restApiId,
21689
22097
  stageName
21690
22098
  }));
22099
+ retractCreatedResource(logicalId, resourceType, stageName);
21691
22100
  } catch (cleanupError) {
21692
22101
  stageLeftBehind = true;
21693
22102
  const cleanupFailure = describeAwsFailure(cleanupError);
@@ -21945,6 +22354,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
21945
22354
  }
21946
22355
  await putMethod();
21947
22356
  }
22357
+ reportCreatedResource(logicalId, resourceType, physicalId);
21948
22358
  try {
21949
22359
  const integration = properties["Integration"];
21950
22360
  if (integration) await this.apiGatewayClient.send(new PutIntegrationCommand(putIntegrationInput({
@@ -21973,6 +22383,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
21973
22383
  resourceId,
21974
22384
  httpMethod
21975
22385
  }));
22386
+ retractCreatedResource(logicalId, resourceType, physicalId);
21976
22387
  log.debug(`Cleaned up partially-created API Gateway Method ${logicalId} (${methodPath}) after wiring failure`);
21977
22388
  } catch (cleanupError) {
21978
22389
  methodLeftBehind = true;
@@ -22719,6 +23130,20 @@ var ApiGatewayV2Provider = class ApiGatewayV2Provider {
22719
23130
  ])]
22720
23131
  ]);
22721
23132
  removalDefaults = /* @__PURE__ */ new Map([["AWS::ApiGatewayV2::Authorizer", /* @__PURE__ */ new Map([["IdentitySource", []]])]]);
23133
+ removalHandledInUpdate = /* @__PURE__ */ new Map([["AWS::ApiGatewayV2::Route", /* @__PURE__ */ new Set([
23134
+ "ApiId",
23135
+ "RouteKey",
23136
+ "AuthorizationScopes",
23137
+ "Target",
23138
+ "AuthorizationType",
23139
+ "AuthorizerId",
23140
+ "OperationName",
23141
+ "ApiKeyRequired",
23142
+ "RequestModels",
23143
+ "RequestParameters",
23144
+ "ModelSelectionExpression",
23145
+ "RouteResponseSelectionExpression"
23146
+ ])]]);
22722
23147
  unhandledByDesign = /* @__PURE__ */ new Map([["AWS::ApiGatewayV2::Api", /* @__PURE__ */ new Map([
22723
23148
  ["Body", "OpenAPI/Swagger inline spec; routed through ImportApi, not the field-by-field CreateApi path."],
22724
23149
  ["BodyS3Location", "OpenAPI/Swagger spec on S3; routed through ImportApi, not the field-by-field CreateApi path."],
@@ -23756,12 +24181,15 @@ var ApiGatewayV2Provider = class ApiGatewayV2Provider {
23756
24181
  input.OperationName = properties["OperationName"];
23757
24182
  changed = true;
23758
24183
  }
23759
- {
24184
+ if (restoreFromAws) {
23760
24185
  const r = this.clearableUpdate(properties["ApiKeyRequired"], previousProperties["ApiKeyRequired"], false);
23761
24186
  if (r.changed) {
23762
24187
  input.ApiKeyRequired = r.value;
23763
24188
  changed = true;
23764
24189
  }
24190
+ } else if (properties["ApiKeyRequired"] !== void 0 && properties["ApiKeyRequired"] !== previousProperties["ApiKeyRequired"]) {
24191
+ input.ApiKeyRequired = properties["ApiKeyRequired"];
24192
+ changed = true;
23765
24193
  }
23766
24194
  if (restoreFromAws) {
23767
24195
  const merged = this.mapWithRemovals(properties["RequestModels"], previousProperties["RequestModels"]);
@@ -25099,14 +25527,57 @@ function camelToPascalCaseKeys(value, preserveKeys) {
25099
25527
  //#region src/provisioning/providers/agentcore-runtime-provider.ts
25100
25528
  const CFN_MAP_KEYS = /* @__PURE__ */ new Set(["Tags", "AdvertisedScopeMapping"]);
25101
25529
  const SDK_MAP_KEYS = /* @__PURE__ */ new Set(["tags", "advertisedScopeMapping"]);
25530
+ const RUNTIME_TAGS_WHAT = "a map of tag keys to scalar values or a list of tags with a non-empty string Key and a scalar Value";
25531
+ function isUserTagKey(key) {
25532
+ return !key.startsWith("aws:");
25533
+ }
25534
+ function desiredRuntimeTags(raw, resourceType, logicalId, physicalId) {
25535
+ const tags = refuseMalformedDesiredTags(tagMapAsList(raw), resourceType, logicalId, physicalId, "Tags", RUNTIME_TAGS_WHAT);
25536
+ if (!tags.every((t) => isUserTagKey(t.Key))) throw markNonRetryable(new ProvisioningError(`${physicalId !== void 0 ? "desired " : ""}Tags of ${resourceType} ${logicalId} holds a reserved aws: key, which AWS does not let a user set — the resource was not ${physicalId !== void 0 ? "updated" : "created"}`, resourceType, logicalId, physicalId));
25537
+ return tags;
25538
+ }
25539
+ const CREATE_TAG_RETRY_DELAYS_MS = [
25540
+ 500,
25541
+ 1e3,
25542
+ 2e3,
25543
+ 4e3
25544
+ ];
25545
+ function isTransientTagFailure(error) {
25546
+ return isThrottlingError(error) || error instanceof ResourceNotFoundException$6 || error instanceof Error && error.name === "ResourceNotFoundException";
25547
+ }
25548
+ function isNonEmptyObject(value) {
25549
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).length > 0;
25550
+ }
25551
+ function writeSharedRuntimeMembers(input, properties) {
25552
+ const requestHeaders = properties["RequestHeaderConfiguration"];
25553
+ if (isNonEmptyObject(requestHeaders)) input["requestHeaderConfiguration"] = pascalToCamelCaseKeys(requestHeaders, CFN_MAP_KEYS);
25554
+ const filesystems = properties["FilesystemConfigurations"];
25555
+ if (Array.isArray(filesystems) && filesystems.length > 0) input["filesystemConfigurations"] = pascalToCamelCaseKeys(filesystems, CFN_MAP_KEYS);
25556
+ const capacityProvider = properties["CapacityProviderConfiguration"];
25557
+ if (isNonEmptyObject(capacityProvider)) input["capacityProviderConfiguration"] = pascalToCamelCaseKeys(capacityProvider, CFN_MAP_KEYS);
25558
+ const platformVersion = properties["PlatformVersion"];
25559
+ if (platformVersion !== void 0 && platformVersion !== null && platformVersion !== "") input["platformVersion"] = platformVersion;
25560
+ }
25102
25561
  var AgentCoreRuntimeProvider = class {
25103
25562
  client;
25104
25563
  logger = getLogger().child("AgentCoreRuntimeProvider");
25564
+ sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
25565
+ async sendCreateTagCall(send) {
25566
+ for (let attempt = 0;; attempt++) try {
25567
+ return await send();
25568
+ } catch (error) {
25569
+ const delay = CREATE_TAG_RETRY_DELAYS_MS[attempt];
25570
+ if (delay === void 0 || !isTransientTagFailure(error)) throw error;
25571
+ this.logger.debug(safeMsg`Post-create tag call failed transiently (${describeAwsFailure(error).summary}); retrying in ${delay}ms`);
25572
+ await this.sleep(delay);
25573
+ }
25574
+ }
25105
25575
  wrapCaughtError(mask, error, build) {
25106
25576
  const failure = describeAwsFailure(error);
25107
25577
  if (!failure.redacted) return wrapMaskedAwsError(mask, error, build);
25108
25578
  this.logger.debug(mask(build(mask(failure.detail)).message));
25109
- const wrapped = build(`${mask(failure.summary)}${relayedNameCollision(failure.detail)}`);
25579
+ const relay = isAuxiliaryFailure(error) ? "" : relayedNameCollision(failure.detail);
25580
+ const wrapped = build(`${mask(failure.summary)}${relay}`);
25110
25581
  return error instanceof Error ? markRedactedCause(wrapped) : wrapped;
25111
25582
  }
25112
25583
  handledProperties = /* @__PURE__ */ new Map([["AWS::BedrockAgentCore::Runtime", /* @__PURE__ */ new Set([
@@ -25119,7 +25590,12 @@ var AgentCoreRuntimeProvider = class {
25119
25590
  "ProtocolConfiguration",
25120
25591
  "LifecycleConfiguration",
25121
25592
  "EnvironmentVariables",
25122
- "ClientToken"
25593
+ "ClientToken",
25594
+ "RequestHeaderConfiguration",
25595
+ "FilesystemConfigurations",
25596
+ "CapacityProviderConfiguration",
25597
+ "PlatformVersion",
25598
+ "Tags"
25123
25599
  ])]]);
25124
25600
  constructor() {
25125
25601
  const awsClients = getAwsClients();
@@ -25131,6 +25607,8 @@ var AgentCoreRuntimeProvider = class {
25131
25607
  if (!agentRuntimeName) throw new ProvisioningError(`AgentRuntimeName is required for ${logicalId}`, resourceType, logicalId);
25132
25608
  const roleArn = properties["RoleArn"];
25133
25609
  if (!roleArn) throw new ProvisioningError(`RoleArn is required for ${logicalId}`, resourceType, logicalId);
25610
+ const desiredTags = desiredRuntimeTags(properties["Tags"], resourceType, logicalId);
25611
+ let createdRuntimeId;
25134
25612
  try {
25135
25613
  const input = {
25136
25614
  agentRuntimeName,
@@ -25148,24 +25626,34 @@ var AgentCoreRuntimeProvider = class {
25148
25626
  if (properties["LifecycleConfiguration"] !== void 0 && typeof properties["LifecycleConfiguration"] === "object" && properties["LifecycleConfiguration"] !== null && Object.keys(properties["LifecycleConfiguration"]).length > 0) input["lifecycleConfiguration"] = pascalToCamelCaseKeys(properties["LifecycleConfiguration"], CFN_MAP_KEYS);
25149
25627
  if (properties["EnvironmentVariables"] !== void 0) input["environmentVariables"] = properties["EnvironmentVariables"];
25150
25628
  if (properties["ClientToken"] !== void 0) input["clientToken"] = properties["ClientToken"];
25629
+ writeSharedRuntimeMembers(input, properties);
25630
+ if (desiredTags.length > 0) input["tags"] = Object.fromEntries(desiredTags.map((t) => [t.Key, t.Value]));
25151
25631
  const response = await this.client.send(new CreateAgentRuntimeCommand(input));
25152
25632
  const agentRuntimeId = response.agentRuntimeId;
25153
25633
  const agentRuntimeArn = response.agentRuntimeArn;
25634
+ createdRuntimeId = agentRuntimeId;
25635
+ reportCreatedResource(logicalId, resourceType, agentRuntimeId);
25154
25636
  this.logger.debug(`Created BedrockAgentCore Runtime: ${agentRuntimeId} (${agentRuntimeArn})`);
25637
+ if (desiredTags.length > 0) await this.ensureCreateTags(agentRuntimeId, agentRuntimeArn, desiredTags);
25155
25638
  return {
25156
25639
  physicalId: agentRuntimeId,
25157
25640
  attributes: this.buildAttributes(response, agentRuntimeName)
25158
25641
  };
25159
25642
  } catch (error) {
25643
+ if (createdRuntimeId !== void 0) markAuxiliaryFailure(error, logicalId);
25160
25644
  const cause = error instanceof Error ? error : void 0;
25161
- throw this.wrapCaughtError(maskerOrIdentity(context?.maskSecrets), error, (text) => new ProvisioningError(`Failed to create BedrockAgentCore Runtime ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
25645
+ const thrown = this.wrapCaughtError(maskerOrIdentity(context?.maskSecrets), error, (text) => new ProvisioningError(`Failed to create BedrockAgentCore Runtime ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
25646
+ if (createdRuntimeId !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdRuntimeId);
25647
+ throw thrown;
25162
25648
  }
25163
25649
  }
25164
- async update(logicalId, physicalId, resourceType, properties, _previousProperties, context) {
25650
+ async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
25165
25651
  this.logger.debug(`Updating BedrockAgentCore Runtime ${logicalId}: ${physicalId}`);
25166
25652
  assertRegionMatch(context?.expectedRegion ? await this.client.config.region() : void 0, context?.expectedRegion, resourceType, logicalId, physicalId, "pre-update");
25167
25653
  const roleArn = properties["RoleArn"];
25168
25654
  if (!roleArn) throw new ProvisioningError(`RoleArn is required for ${logicalId}`, resourceType, logicalId, physicalId);
25655
+ desiredRuntimeTags(properties["Tags"], resourceType, logicalId, physicalId);
25656
+ const mask = maskerOrIdentity(context?.maskSecrets);
25169
25657
  try {
25170
25658
  const input = {
25171
25659
  agentRuntimeId: physicalId,
@@ -25183,8 +25671,10 @@ var AgentCoreRuntimeProvider = class {
25183
25671
  if (properties["LifecycleConfiguration"] !== void 0 && typeof properties["LifecycleConfiguration"] === "object" && properties["LifecycleConfiguration"] !== null && Object.keys(properties["LifecycleConfiguration"]).length > 0) input["lifecycleConfiguration"] = pascalToCamelCaseKeys(properties["LifecycleConfiguration"], CFN_MAP_KEYS);
25184
25672
  if (properties["EnvironmentVariables"] !== void 0) input["environmentVariables"] = properties["EnvironmentVariables"];
25185
25673
  if (properties["ClientToken"] !== void 0) input["clientToken"] = properties["ClientToken"];
25674
+ writeSharedRuntimeMembers(input, properties);
25186
25675
  const response = await this.client.send(new UpdateAgentRuntimeCommand(input));
25187
25676
  const agentRuntimeId = response.agentRuntimeId;
25677
+ await this.syncTags(logicalId, resourceType, physicalId, response.agentRuntimeArn, previousProperties["Tags"], properties["Tags"], mask);
25188
25678
  this.logger.debug(`Successfully updated BedrockAgentCore Runtime ${logicalId}`);
25189
25679
  return {
25190
25680
  physicalId: agentRuntimeId,
@@ -25192,10 +25682,44 @@ var AgentCoreRuntimeProvider = class {
25192
25682
  attributes: this.buildAttributes(response, properties["AgentRuntimeName"])
25193
25683
  };
25194
25684
  } catch (error) {
25685
+ if (error instanceof CdkdError) throw error;
25195
25686
  const cause = error instanceof Error ? error : void 0;
25196
- throw this.wrapCaughtError(maskerOrIdentity(context?.maskSecrets), error, (text) => new ProvisioningError(`Failed to update BedrockAgentCore Runtime ${logicalId}: ${text}`, resourceType, logicalId, physicalId, cause));
25687
+ throw this.wrapCaughtError(mask, error, (text) => new ProvisioningError(`Failed to update BedrockAgentCore Runtime ${logicalId}: ${text}`, resourceType, logicalId, physicalId, cause));
25197
25688
  }
25198
25689
  }
25690
+ async ensureCreateTags(agentRuntimeId, resourceArn, desired) {
25691
+ let live;
25692
+ try {
25693
+ live = (await this.sendCreateTagCall(() => this.client.send(new ListTagsForResourceCommand$6({ resourceArn })))).tags ?? {};
25694
+ } catch (err) {
25695
+ this.logger.debug(safeMsg`Could not read the tags of new runtime ${agentRuntimeId} (${describeAwsFailure(err).detail}); setting every declared tag`);
25696
+ }
25697
+ const missing = Object.fromEntries(desired.filter((t) => live?.[t.Key] !== t.Value).map((t) => [t.Key, t.Value]));
25698
+ if (Object.keys(missing).length === 0) return;
25699
+ if (live !== void 0) this.logger.debug(safeMsg`New runtime ${agentRuntimeId} lacks ${Object.keys(missing).length} of the tag(s) CreateAgentRuntime was sent; setting them with TagResource`);
25700
+ await this.sendCreateTagCall(() => this.client.send(new TagResourceCommand$9({
25701
+ resourceArn,
25702
+ tags: missing
25703
+ })));
25704
+ }
25705
+ async syncTags(logicalId, resourceType, physicalId, responseArn, previousTagsRaw, nextTagsRaw, mask) {
25706
+ const plan = planTagDiff(tagMapAsList(previousTagsRaw), tagMapAsList(nextTagsRaw));
25707
+ const warning = tagPlanWarning(plan, resourceType, logicalId);
25708
+ if (warning !== void 0) this.logger.warn(mask(warning));
25709
+ const remove = plan.remove.filter(isUserTagKey);
25710
+ const set = Object.fromEntries([...plan.set].filter(([k]) => isUserTagKey(k)));
25711
+ if (remove.length === 0 && Object.keys(set).length === 0) return;
25712
+ const resourceArn = responseArn ?? (await this.client.send(new GetAgentRuntimeCommand({ agentRuntimeId: physicalId }))).agentRuntimeArn;
25713
+ if (!resourceArn) throw new ProvisioningError(`Cannot apply the Tags change to ${logicalId}: AWS returned no runtime ARN`, resourceType, logicalId, physicalId);
25714
+ if (remove.length > 0) await this.client.send(new UntagResourceCommand$9({
25715
+ resourceArn,
25716
+ tagKeys: remove
25717
+ }));
25718
+ if (Object.keys(set).length > 0) await this.client.send(new TagResourceCommand$9({
25719
+ resourceArn,
25720
+ tags: set
25721
+ }));
25722
+ }
25199
25723
  async delete(logicalId, physicalId, resourceType, _properties, context) {
25200
25724
  this.logger.debug(`Deleting BedrockAgentCore Runtime ${logicalId}: ${physicalId}`);
25201
25725
  try {
@@ -25263,6 +25787,16 @@ var AgentCoreRuntimeProvider = class {
25263
25787
  }
25264
25788
  if (resp.lifecycleConfiguration !== void 0) result["LifecycleConfiguration"] = camelToPascalCaseKeys(resp.lifecycleConfiguration, SDK_MAP_KEYS);
25265
25789
  if (resp.environmentVariables !== void 0) result["EnvironmentVariables"] = resp.environmentVariables;
25790
+ result["RequestHeaderConfiguration"] = resp.requestHeaderConfiguration !== void 0 ? camelToPascalCaseKeys(resp.requestHeaderConfiguration, SDK_MAP_KEYS) : {};
25791
+ result["FilesystemConfigurations"] = resp.filesystemConfigurations !== void 0 ? camelToPascalCaseKeys(resp.filesystemConfigurations, SDK_MAP_KEYS) : [];
25792
+ result["CapacityProviderConfiguration"] = resp.capacityProviderConfiguration !== void 0 ? camelToPascalCaseKeys(resp.capacityProviderConfiguration, SDK_MAP_KEYS) : {};
25793
+ result["PlatformVersion"] = resp.platformVersion ?? "";
25794
+ if (resp.agentRuntimeArn) try {
25795
+ const tagsResp = await this.client.send(new ListTagsForResourceCommand$6({ resourceArn: resp.agentRuntimeArn }));
25796
+ result["Tags"] = Object.fromEntries(Object.entries(tagsResp.tags ?? {}).filter(([k]) => isUserTagKey(k)));
25797
+ } catch (err) {
25798
+ this.logger.debug(safeMsg`Failed to read tags for runtime ${physicalId}: ${describeAwsFailure(err).detail}`);
25799
+ }
25266
25800
  return result;
25267
25801
  }
25268
25802
  async import(input) {
@@ -26108,6 +26642,60 @@ function isEcsErrorNamed(error, names) {
26108
26642
  return typeof name === "string" && names.includes(name);
26109
26643
  }
26110
26644
  const settleRolloutDelays = { sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)) };
26645
+ function requestedRolloutOf(service) {
26646
+ if (!service) return void 0;
26647
+ const primary = service.deployments?.find((d) => d.status === "PRIMARY");
26648
+ const deploymentId = typeof primary?.id === "string" && primary.id ? primary.id : void 0;
26649
+ const taskDefinitionRaw = primary?.taskDefinition ?? service.taskDefinition;
26650
+ const taskDefinition = typeof taskDefinitionRaw === "string" && taskDefinitionRaw ? taskDefinitionRaw : void 0;
26651
+ if (deploymentId === void 0 && taskDefinition === void 0) return void 0;
26652
+ return {
26653
+ deploymentId,
26654
+ taskDefinition
26655
+ };
26656
+ }
26657
+ function failedRequestedDeployment(requested, deployments) {
26658
+ if (!Array.isArray(deployments)) return void 0;
26659
+ const target = requested?.deploymentId !== void 0 ? deployments.find((d) => d?.id === requested.deploymentId) : deployments.find((d) => d?.status === "PRIMARY");
26660
+ return target?.rolloutState === "FAILED" ? target : void 0;
26661
+ }
26662
+ function taskDefinitionRevision(arn) {
26663
+ const match = typeof arn === "string" ? /:task-definition\/[^:]+:(\d+)$/.exec(arn) : null;
26664
+ return match ? match[1] : void 0;
26665
+ }
26666
+ function describeRollout(rollout) {
26667
+ const revision = taskDefinitionRevision(rollout.taskDefinition);
26668
+ const id = typeof rollout.id === "string" && /^ecs-svc\/\d+$/.test(rollout.id) ? rollout.id : void 0;
26669
+ if (id && revision) return `deployment ${id} (task definition revision ${revision})`;
26670
+ if (id) return `deployment ${id}`;
26671
+ if (revision) return `task definition revision ${revision}`;
26672
+ return "another deployment";
26673
+ }
26674
+ var EcsCircuitBreakerVerdict = class extends Error {
26675
+ constructor(message) {
26676
+ super(message);
26677
+ this.name = "EcsCircuitBreakerVerdict";
26678
+ }
26679
+ };
26680
+ function wrapBreakerFailedError(failed) {
26681
+ return new EcsCircuitBreakerVerdict(`its deployment circuit breaker failed the rollout of ${describeRollout(failed)} (deployment FAILED). The new tasks failed to start or failed their health checks`);
26682
+ }
26683
+ function controllerSkipsWait(properties) {
26684
+ let type;
26685
+ try {
26686
+ type = readConfigString(properties["DeploymentController"], "Type", "ECS", "DeploymentController");
26687
+ } catch {
26688
+ return "its DeploymentController could not be read";
26689
+ }
26690
+ if (type === "ECS") return void 0;
26691
+ if (type === "CODE_DEPLOY" || type === "EXTERNAL") return `its DeploymentController is ${type}, which ECS does not settle`;
26692
+ return "its DeploymentController type is not one cdkd can settle";
26693
+ }
26694
+ function rolloutSupersededBy(requested, settled) {
26695
+ if (!requested || !settled) return void 0;
26696
+ if (requested.deploymentId !== void 0 && typeof settled.id === "string" && settled.id) return settled.id === requested.deploymentId ? void 0 : describeRollout(settled);
26697
+ if (requested.taskDefinition !== void 0 && typeof settled.taskDefinition === "string" && settled.taskDefinition) return settled.taskDefinition === requested.taskDefinition ? void 0 : describeRollout(settled);
26698
+ }
26111
26699
  function markNotGoneOnNotFoundClass$1(error, wrapped) {
26112
26700
  return error instanceof Error && error.name.includes("NotFound") ? markNotGone(wrapped) : wrapped;
26113
26701
  }
@@ -26257,7 +26845,7 @@ var ECSProvider = class {
26257
26845
  switch (resourceType) {
26258
26846
  case "AWS::ECS::Cluster": return this.createCluster(logicalId, resourceType, properties, this.operationSinks(context, [[properties["ClusterName"], properties["ClusterName"]]]));
26259
26847
  case "AWS::ECS::TaskDefinition": return this.createTaskDefinition(logicalId, resourceType, properties, this.operationSinks(context, [[properties["Family"], properties["Family"]]]));
26260
- case "AWS::ECS::Service": return this.createService(logicalId, resourceType, properties, this.operationSinks(context, [[properties["ServiceName"], properties["ServiceName"]], [properties["Cluster"], clusterNameOfRef(properties["Cluster"])]]));
26848
+ case "AWS::ECS::Service": return this.createService(logicalId, resourceType, properties, this.operationSinks(context, [[properties["ServiceName"], properties["ServiceName"]], [properties["Cluster"], clusterNameOfRef(properties["Cluster"])]]), context?.replayingState === true);
26261
26849
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId);
26262
26850
  }
26263
26851
  }
@@ -26274,7 +26862,7 @@ var ECSProvider = class {
26274
26862
  [properties["Cluster"], recordedCluster],
26275
26863
  [previousProperties["Cluster"], recordedCluster],
26276
26864
  [properties["Cluster"], clusterNameOfRef(properties["Cluster"])]
26277
- ]), context?.desiredFromAwsReadback === true);
26865
+ ]), context?.desiredFromAwsReadback === true, context?.replayingState === true);
26278
26866
  }
26279
26867
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
26280
26868
  }
@@ -26494,14 +27082,15 @@ var ECSProvider = class {
26494
27082
  default: return;
26495
27083
  }
26496
27084
  }
26497
- async createService(logicalId, resourceType, properties, log) {
27085
+ async createService(logicalId, resourceType, properties, log, replayingState) {
26498
27086
  log.debug(`Creating ECS service ${logicalId}`);
26499
27087
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
26500
27088
  const client = this.getClient();
26501
27089
  const serviceName = properties["ServiceName"] || generatedNameFor("AWS::ECS::Service", logicalId);
26502
27090
  let survivorArn;
26503
27091
  try {
26504
- const heldBefore = process.env["CDKD_FULL_WAIT"] === "true" ? await nameHeldBefore(() => this.serviceNameHeld(properties["Cluster"], serviceName), (error) => hasErrorName(error, ["ClusterNotFoundException"])) : "free";
27092
+ const skipReason = controllerSkipsWait(properties);
27093
+ const heldBefore = process.env["CDKD_NO_WAIT"] !== "true" && skipReason === void 0 && !replayingState ? await nameHeldBefore(() => this.serviceNameHeld(properties["Cluster"], serviceName), (error) => hasErrorName(error, ["ClusterNotFoundException"])) : "free";
26505
27094
  const service = (await client.send(new CreateServiceCommand({
26506
27095
  cluster: properties["Cluster"],
26507
27096
  serviceName,
@@ -26532,9 +27121,20 @@ var ECSProvider = class {
26532
27121
  }))).service;
26533
27122
  if (!service || !service.serviceArn) throw new Error("CreateService did not return service ARN");
26534
27123
  log.debug(`Successfully created ECS service ${logicalId}: ${service.serviceArn}`);
27124
+ if (heldBefore === "free") reportCreatedResource(logicalId, resourceType, service.serviceArn);
26535
27125
  try {
26536
- await this.settleService(logicalId, service.serviceName ?? service.serviceArn, properties["Cluster"], log);
27126
+ await this.settleService(logicalId, service.serviceName ?? service.serviceArn, properties["Cluster"], log, requestedRolloutOf(service), skipReason);
26537
27127
  } catch (waitError) {
27128
+ if (replayingState) {
27129
+ log.warn(`${this.wrapSteadyStateError(logicalId, waitError, properties["Cluster"], log, false).message} Continuing the rollback: the restored service stays recorded.`);
27130
+ return {
27131
+ physicalId: service.serviceArn,
27132
+ attributes: {
27133
+ ServiceArn: service.serviceArn,
27134
+ Name: service.serviceName
27135
+ }
27136
+ };
27137
+ }
26538
27138
  const aws = pasteableAwsCommand(log.mask);
26539
27139
  const cleanupCluster = properties["Cluster"];
26540
27140
  const clusterArg = cleanupCluster ? aws` --cluster ${cleanupCluster}` : aws``;
@@ -26547,6 +27147,7 @@ var ECSProvider = class {
26547
27147
  service: service.serviceArn,
26548
27148
  force: true
26549
27149
  }));
27150
+ retractCreatedResource(logicalId, resourceType, service.serviceArn);
26550
27151
  log.warn(`Deleted partially-created ECS service ${logicalId} (${service.serviceArn}) after the steady-state wait failed, so the next deploy's CreateService does not collide on the name. Its stopped tasks remain inspectable for about an hour: ${listStopped.render()}`);
26551
27152
  } catch (cleanupError) {
26552
27153
  survivorArn = service.serviceArn;
@@ -26555,10 +27156,7 @@ var ECSProvider = class {
26555
27156
  if (cleanupFailure.redacted) log.debug(`${cleanupHead}${cleanupFailure.detail}`);
26556
27157
  log.warn(`${cleanupHead}${cleanupFailure.redacted ? `${cleanupFailure.summary} ` : `${cleanupFailure.detail}. `}Manual deletion may be required before the next deploy: ${deleteCommand.render()}`);
26557
27158
  }
26558
- const waitFailure = describeAwsFailure(waitError);
26559
- if (waitFailure.redacted) log.debug(`ECS service ${logicalId} steady-state wait failed: ${waitFailure.detail}`);
26560
- const steadyStateError = new Error(`ECS service ${logicalIdShown(logicalId)} did not reach steady state under --full-wait: ${waitFailure.summary.replace(/\.$/, "")}. Inspect why its tasks stopped (stopped tasks stay visible for about an hour): ${listStopped.render()}, then ${aws`aws ecs describe-tasks${clusterArg} --tasks '<task-arn>' --query 'tasks[].[stoppedReason,containers[].reason]'`.render()}`, { cause: waitError });
26561
- throw waitFailure.redacted ? markRedactedCause(steadyStateError) : steadyStateError;
27159
+ throw this.wrapSteadyStateError(logicalId, waitError, properties["Cluster"], log);
26562
27160
  }
26563
27161
  return {
26564
27162
  physicalId: service.serviceArn,
@@ -26601,30 +27199,68 @@ var ECSProvider = class {
26601
27199
  reduced.name = error.name;
26602
27200
  return markRedactedCause(reduced);
26603
27201
  }
26604
- async settleService(logicalId, serviceRef, cluster, log) {
26605
- if (process.env["CDKD_FULL_WAIT"] === "true") {
27202
+ wrapSteadyStateError(logicalId, waitError, cluster, log, offerNoWait = true) {
27203
+ const aws = pasteableAwsCommand(log.mask);
27204
+ const clusterArg = cluster ? aws` --cluster ${cluster}` : aws``;
27205
+ const listStopped = aws`aws ecs list-tasks${clusterArg} --desired-status STOPPED`;
27206
+ const waitFailure = describeAwsFailure(waitError);
27207
+ if (waitFailure.redacted) log.debug(`ECS service ${logicalId} steady-state wait failed: ${waitFailure.detail}`);
27208
+ const flagsAvailable = offerNoWait && process.env["CDKD_WAIT_FLAGS_AVAILABLE"] === "true";
27209
+ const timeoutHint = flagsAvailable && !(waitError instanceof EcsCircuitBreakerVerdict) ? ` To wait longer, raise the limit with --resource-timeout AWS::ECS::Service=${commandHole("duration")}.` : "";
27210
+ const noWaitHint = flagsAvailable ? " To deploy without waiting for steady state, pass --no-wait." : "";
27211
+ const steadyStateError = new Error(`ECS service ${logicalIdShown(logicalId)} did not reach steady state: ${waitFailure.summary.replace(/\.$/, "")}. Inspect why its tasks stopped (stopped tasks stay visible for about an hour): ${listStopped.render()}, then ${aws`aws ecs describe-tasks${clusterArg} --tasks '<task-arn>' --query 'tasks[].[stoppedReason,containers[].reason]'`.render()}.${timeoutHint}${noWaitHint}`, { cause: waitError });
27212
+ return waitFailure.redacted ? markRedactedCause(steadyStateError) : steadyStateError;
27213
+ }
27214
+ async settleService(logicalId, serviceRef, cluster, log, requested, skipReason) {
27215
+ if (skipReason !== void 0) {
27216
+ this.logger.info(log.mask(`ECS service ${logicalIdShown(logicalId)} accepted (not waiting for steady state: ${skipReason}).`));
27217
+ return;
27218
+ }
27219
+ if (process.env["CDKD_NO_WAIT"] !== "true") {
26606
27220
  log.debug(`Waiting for ECS service ${logicalId} to reach steady state...`);
26607
27221
  const maxWaitTime = Math.max(600, (resolvedResourceTimeoutMs("AWS::ECS::Service") ?? 0) / 1e3);
27222
+ const client = this.getClient();
27223
+ const breakerVerdict = new AbortController();
27224
+ let failedRollout;
27225
+ const watchedClient = { send: async (command) => {
27226
+ const output = await client.send(command);
27227
+ if (failedRollout === void 0) {
27228
+ failedRollout = failedRequestedDeployment(requested, output?.services?.[0]?.deployments);
27229
+ if (failedRollout !== void 0) breakerVerdict.abort();
27230
+ }
27231
+ return output;
27232
+ } };
26608
27233
  try {
26609
27234
  await waitUntilServicesStable({
26610
- client: this.getClient(),
27235
+ client: watchedClient,
26611
27236
  maxWaitTime,
26612
27237
  minDelay: 5,
26613
- maxDelay: 10
27238
+ maxDelay: 10,
27239
+ abortSignal: breakerVerdict.signal
26614
27240
  }, {
26615
27241
  cluster,
26616
27242
  services: [serviceRef]
26617
27243
  });
26618
27244
  } catch (waiterError) {
27245
+ if (failedRollout !== void 0) {
27246
+ log.debug(`ECS service ${logicalId} deployment ${failedRollout.id ?? "(no id)"} FAILED: ${failedRollout.rolloutStateReason ?? "(no rollout reason)"}`);
27247
+ throw wrapBreakerFailedError(failedRollout);
27248
+ }
26619
27249
  throw this.wrapWaiterResultError(waiterError, logicalId, log);
26620
27250
  }
27251
+ if (failedRollout !== void 0) throw wrapBreakerFailedError(failedRollout);
26621
27252
  const rolloutDeadline = Date.now() + 12e4;
27253
+ let settledDeployment;
26622
27254
  for (;;) {
26623
27255
  const deployments = (await this.getClient().send(new DescribeServicesCommand({
26624
27256
  cluster,
26625
27257
  services: [serviceRef]
26626
27258
  })))?.services?.[0]?.deployments;
26627
- const rolloutState = deployments && deployments.length === 1 ? deployments[0]?.rolloutState : void 0;
27259
+ const failedNow = failedRequestedDeployment(requested, deployments);
27260
+ if (failedNow !== void 0) throw wrapBreakerFailedError(failedNow);
27261
+ settledDeployment = deployments && deployments.length === 1 ? deployments[0] : void 0;
27262
+ const rolloutState = settledDeployment?.rolloutState;
27263
+ if (settledDeployment !== void 0 && rolloutState === "FAILED") throw wrapBreakerFailedError(settledDeployment);
26628
27264
  if (rolloutState === void 0 || rolloutState === "COMPLETED") break;
26629
27265
  if (Date.now() >= rolloutDeadline) {
26630
27266
  log.warn(`ECS service ${logicalId} is steady but its rollout is still ${rolloutState} after the post-stability grace window; continuing`);
@@ -26632,15 +27268,22 @@ var ECSProvider = class {
26632
27268
  }
26633
27269
  await settleRolloutDelays.sleep(3e3);
26634
27270
  }
27271
+ const supersededBy = rolloutSupersededBy(requested, settledDeployment);
27272
+ if (supersededBy !== void 0) {
27273
+ log.debug(`ECS service ${logicalId} settled deployment ${settledDeployment?.id ?? "(no id)"}: ${settledDeployment?.rolloutStateReason ?? "(no rollout reason)"}`);
27274
+ throw new EcsCircuitBreakerVerdict(`its deployment circuit breaker rolled the rollout back: the service settled on ${supersededBy}, not on the deployment this update started (${describeRollout({
27275
+ id: requested.deploymentId,
27276
+ taskDefinition: requested.taskDefinition
27277
+ })}). The new tasks failed to start or failed their health checks`);
27278
+ }
26635
27279
  log.debug(`ECS service ${logicalId} reached steady state`);
26636
27280
  return;
26637
27281
  }
26638
27282
  const aws = pasteableAwsCommand(log.mask);
26639
27283
  const clusterArg = cluster ? aws` --cluster ${cluster}` : aws``;
26640
- const fullWaitHint = process.env["CDKD_WAIT_FLAGS_AVAILABLE"] === "true" ? "; pass --full-wait to wait" : "";
26641
- this.logger.info(log.mask(`ECS service ${logicalIdShown(logicalId)} accepted (not waiting for steady state${fullWaitHint}). To wait manually: ${aws`aws ecs wait services-stable${clusterArg} --services ${serviceRef}`.render()}`));
27284
+ this.logger.info(log.mask(`ECS service ${logicalIdShown(logicalId)} accepted (not waiting for steady state under --no-wait). To wait manually: ${aws`aws ecs wait services-stable${clusterArg} --services ${serviceRef}`.render()}`));
26642
27285
  }
26643
- async updateService(logicalId, physicalId, resourceType, properties, previousProperties, log, desiredFromAwsReadback) {
27286
+ async updateService(logicalId, physicalId, resourceType, properties, previousProperties, log, desiredFromAwsReadback, replayingState) {
26644
27287
  log.debug(`Updating ECS service ${logicalId}: ${physicalId}`);
26645
27288
  refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId);
26646
27289
  const client = this.getClient();
@@ -26710,7 +27353,12 @@ var ECSProvider = class {
26710
27353
  forceNewDeployment: forceNewDeploymentInput
26711
27354
  }))).service;
26712
27355
  if (service?.serviceArn) await this.applyTagDiff(service.serviceArn, resourceType, logicalId, previousProperties["Tags"], properties["Tags"], log);
26713
- await this.settleService(logicalId, service?.serviceName ?? physicalId, properties["Cluster"], log);
27356
+ try {
27357
+ await this.settleService(logicalId, service?.serviceName ?? physicalId, properties["Cluster"], log, requestedRolloutOf(service), desiredFromAwsReadback ? "a drift revert does not wait for it" : controllerSkipsWait(properties));
27358
+ } catch (waitError) {
27359
+ if (replayingState) log.warn(`${this.wrapSteadyStateError(logicalId, waitError, properties["Cluster"], log, false).message} The rollback update was applied; continuing.`);
27360
+ else throw this.wrapSteadyStateError(logicalId, waitError, properties["Cluster"], log);
27361
+ }
26714
27362
  return {
26715
27363
  physicalId,
26716
27364
  wasReplaced: false,
@@ -27982,6 +28630,7 @@ var ELBv2Provider = class {
27982
28630
  if (!lb || !lb.LoadBalancerArn) throw new Error("CreateLoadBalancer did not return LoadBalancer ARN");
27983
28631
  const lbArn = lb.LoadBalancerArn;
27984
28632
  this.logger.debug(`Successfully created LoadBalancer ${logicalId}: ${lbArn}`);
28633
+ if (heldBefore === "free") reportCreatedResource(logicalId, resourceType, lbArn);
27985
28634
  try {
27986
28635
  if (waitForActive) {
27987
28636
  this.logger.debug(`Waiting for LoadBalancer ${logicalId} to reach active state...`);
@@ -28020,6 +28669,7 @@ var ELBv2Provider = class {
28020
28669
  if (heldBefore !== "free") this.logger.warn(maskerOrIdentity(maskSecrets)(skippedCleanupText(heldBefore, `LoadBalancer ${logicalId} (${lbArn})`, pasteableAwsCommand(maskSecrets)`aws elbv2 delete-load-balancer --load-balancer-arn ${lbArn}`.render())));
28021
28670
  else try {
28022
28671
  await this.getClient().send(new DeleteLoadBalancerCommand({ LoadBalancerArn: lbArn }));
28672
+ retractCreatedResource(logicalId, resourceType, lbArn);
28023
28673
  this.logger.debug(`Cleaned up partially-created LoadBalancer ${logicalId} (${lbArn}) after wiring failure`);
28024
28674
  } catch (cleanupError) {
28025
28675
  leftBehindArn = lbArn;
@@ -28288,6 +28938,7 @@ var ELBv2Provider = class {
28288
28938
  if (!tg || !tg.TargetGroupArn) throw new Error("CreateTargetGroup did not return TargetGroup ARN");
28289
28939
  const tgArn = tg.TargetGroupArn;
28290
28940
  this.logger.debug(`Successfully created TargetGroup ${logicalId}: ${tgArn}`);
28941
+ if (heldBefore === "free") reportCreatedResource(logicalId, resourceType, tgArn);
28291
28942
  try {
28292
28943
  if (tgAttributes.length > 0) {
28293
28944
  await this.getClient().send(new ModifyTargetGroupAttributesCommand({
@@ -28310,6 +28961,7 @@ var ELBv2Provider = class {
28310
28961
  if (isInterruptedWaitError(innerError)) this.logger.warn(maskerOrIdentity(maskSecrets)(`Interrupted after creating TargetGroup ${logicalId} (${tgArn}) but before its targets were registered. Nothing in cdkd state refers to it, so cdkd is deleting it now. If that delete fails, remove it yourself: ${pasteableAwsCommand(maskSecrets)`aws elbv2 delete-target-group --target-group-arn ${tgArn}`.render()}`));
28311
28962
  try {
28312
28963
  await this.getClient().send(new DeleteTargetGroupCommand({ TargetGroupArn: tgArn }));
28964
+ retractCreatedResource(logicalId, resourceType, tgArn);
28313
28965
  this.logger.debug(`Cleaned up partially-created TargetGroup ${logicalId} (${tgArn}) after wiring failure`);
28314
28966
  } catch (cleanupError) {
28315
28967
  leftBehindArn = tgArn;
@@ -28483,6 +29135,7 @@ var ELBv2Provider = class {
28483
29135
  if (!listener || !listener.ListenerArn) throw new Error("CreateListener did not return Listener ARN");
28484
29136
  const listenerArn = listener.ListenerArn;
28485
29137
  this.logger.debug(`Successfully created Listener ${logicalId}: ${listenerArn}`);
29138
+ if (heldBefore === "free") reportCreatedResource(logicalId, resourceType, listenerArn);
28486
29139
  try {
28487
29140
  if (listenerAttributes.length > 0) {
28488
29141
  const watch = startInterruptWatch(`ELBv2 Listener ${logicalId} attributes`);
@@ -28508,6 +29161,7 @@ var ELBv2Provider = class {
28508
29161
  if (isInterruptedWaitError(innerError)) this.logger.warn(maskerOrIdentity(maskSecrets)(`Interrupted after creating Listener ${logicalId} (${listenerArn}) but before its attributes were applied. Nothing in cdkd state refers to it, so cdkd is deleting it now — left behind it would fail the next deploy with DuplicateListener and destroy could not reach it. If that delete fails, outside a rollback's re-create, the failed deploy's rollback journal records it for \`cdkd rollback --revert-failed\`; otherwise remove it yourself: ${pasteableAwsCommand(maskSecrets)`aws elbv2 delete-listener --listener-arn ${listenerArn}`.render()}`));
28509
29162
  try {
28510
29163
  await this.getClient().send(new DeleteListenerCommand({ ListenerArn: listenerArn }));
29164
+ retractCreatedResource(logicalId, resourceType, listenerArn);
28511
29165
  this.logger.debug(`Cleaned up partially-created Listener ${logicalId} (${listenerArn}) after attributes-wiring failure`);
28512
29166
  } catch (cleanupError) {
28513
29167
  leftBehindArn = listenerArn;
@@ -29048,6 +29702,7 @@ const POLL_INTERVAL_MS$1 = 5e3;
29048
29702
  const POLL_TIMEOUT_MS$1 = 18e5;
29049
29703
  const RETAINED_ON_REMOVAL = [
29050
29704
  "Auth",
29705
+ "DefaultAuthScheme",
29051
29706
  "RequireTLS",
29052
29707
  "IdleClientTimeout",
29053
29708
  "DebugLogging",
@@ -29059,7 +29714,14 @@ const CFN_KEEPS_ON_REMOVAL$1 = /* @__PURE__ */ new Set([
29059
29714
  "DebugLogging",
29060
29715
  "VpcSecurityGroupIds"
29061
29716
  ]);
29062
- const authRemovalRefused = (properties) => {
29717
+ const authRemovalRefused = (properties, previousProperties) => authRequired({ DefaultAuthScheme: properties["DefaultAuthScheme"] !== void 0 ? properties["DefaultAuthScheme"] : previousProperties["DefaultAuthScheme"] });
29718
+ const leavesProxyWithoutAuth = (properties, previousProperties) => {
29719
+ const desired = properties["Auth"];
29720
+ const declaredEmpty = desired === null || Array.isArray(desired) && desired.length === 0;
29721
+ if (properties["DefaultAuthScheme"] === "NONE" && previousProperties["DefaultAuthScheme"] !== "NONE" && (desired === void 0 || declaredEmpty)) return true;
29722
+ return declaredEmpty && JSON.stringify(desired) !== JSON.stringify(previousProperties["Auth"]) && authRemovalRefused(properties, previousProperties);
29723
+ };
29724
+ const authRequired = (properties) => {
29063
29725
  const scheme = properties["DefaultAuthScheme"];
29064
29726
  return scheme === void 0 || scheme === "NONE";
29065
29727
  };
@@ -29079,6 +29741,9 @@ var RDSDBProxyProvider = class {
29079
29741
  "RequireTLS",
29080
29742
  "IdleClientTimeout",
29081
29743
  "DebugLogging",
29744
+ "DefaultAuthScheme",
29745
+ "EndpointNetworkType",
29746
+ "TargetConnectionNetworkType",
29082
29747
  "Tags"
29083
29748
  ])]]);
29084
29749
  getClient() {
@@ -29105,18 +29770,21 @@ var RDSDBProxyProvider = class {
29105
29770
  const engineFamily = properties["EngineFamily"];
29106
29771
  if (!engineFamily) throw new ProvisioningError(`EngineFamily is required for AWS::RDS::DBProxy ${logicalId}`, resourceType, logicalId);
29107
29772
  const auth = properties["Auth"];
29108
- if (!auth || auth.length === 0) throw new ProvisioningError(`Auth (at least one entry) is required for AWS::RDS::DBProxy ${logicalId}`, resourceType, logicalId);
29773
+ if ((!auth || auth.length === 0) && authRequired(properties)) throw new ProvisioningError(`Auth (at least one entry) is required for AWS::RDS::DBProxy ${logicalId} while DefaultAuthScheme is NONE or unset`, resourceType, logicalId);
29109
29774
  const roleArn = properties["RoleArn"];
29110
29775
  if (!roleArn) throw new ProvisioningError(`RoleArn is required for AWS::RDS::DBProxy ${logicalId}`, resourceType, logicalId);
29111
29776
  const vpcSubnetIds = properties["VpcSubnetIds"];
29112
29777
  if (!vpcSubnetIds || vpcSubnetIds.length === 0) throw new ProvisioningError(`VpcSubnetIds (at least one) is required for AWS::RDS::DBProxy ${logicalId}`, resourceType, logicalId);
29113
29778
  const client = this.getClient();
29114
- this.logger.debug(`Creating DBProxy ${dbProxyName} (${engineFamily})`);
29779
+ this.logger.debug(mask(`Creating DBProxy ${dbProxyName} (${engineFamily})`));
29115
29780
  try {
29116
29781
  await this.getCreateClient().send(new CreateDBProxyCommand({
29117
29782
  DBProxyName: dbProxyName,
29118
29783
  EngineFamily: engineFamily,
29119
- Auth: auth,
29784
+ Auth: auth && auth.length > 0 ? auth : void 0,
29785
+ DefaultAuthScheme: properties["DefaultAuthScheme"],
29786
+ EndpointNetworkType: properties["EndpointNetworkType"],
29787
+ TargetConnectionNetworkType: properties["TargetConnectionNetworkType"],
29120
29788
  RoleArn: roleArn,
29121
29789
  VpcSubnetIds: vpcSubnetIds,
29122
29790
  VpcSecurityGroupIds: properties["VpcSecurityGroupIds"],
@@ -29128,6 +29796,7 @@ var RDSDBProxyProvider = class {
29128
29796
  } catch (error) {
29129
29797
  throw this.wrapCaughtError(error, "CREATE", resourceType, logicalId, void 0, mask);
29130
29798
  }
29799
+ reportCreatedResource(logicalId, resourceType, dbProxyName);
29131
29800
  let endpoint;
29132
29801
  let dbProxyArn;
29133
29802
  let vpcId;
@@ -29167,13 +29836,16 @@ var RDSDBProxyProvider = class {
29167
29836
  }
29168
29837
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
29169
29838
  refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId, "Tags", DBPROXY_TAGS_WHAT, DBPROXY_TAG_OPTIONS);
29170
- if (context?.replayingState !== true && context?.desiredFromAwsReadback !== true && this.removedRetained(properties, previousProperties, context).includes("Auth") && authRemovalRefused(properties)) throw markNonRetryable(new ProvisioningError(safeMsg`${logicalId} (${resourceType}): Auth was removed from the template, but AWS requires Auth while DefaultAuthScheme is NONE or unset, and CloudFormation's update fails on the same change. Declare Auth again; nothing was changed.`, resourceType, logicalId, physicalId));
29839
+ if (context?.replayingState !== true && context?.desiredFromAwsReadback !== true && this.removedRetained(properties, previousProperties, context).includes("Auth") && authRemovalRefused(properties, previousProperties)) throw markNonRetryable(new ProvisioningError(safeMsg`${logicalId} (${resourceType}): Auth was removed from the template, but AWS requires Auth while DefaultAuthScheme is NONE or unset, and CloudFormation's update fails on the same change. Declare Auth again; nothing was changed.`, resourceType, logicalId, physicalId));
29840
+ if (context?.replayingState !== true && context?.desiredFromAwsReadback !== true && leavesProxyWithoutAuth(properties, previousProperties)) throw markNonRetryable(new ProvisioningError(safeMsg`${logicalId} (${resourceType}): the update leaves the proxy with no Auth while DefaultAuthScheme is NONE or unset, and AWS requires Auth then. Declare Auth; nothing was changed.`, resourceType, logicalId, physicalId));
29171
29841
  const client = this.getClient();
29172
29842
  const { mask } = withDerivedNameMasks(this.logger, createMaskedLogSinks(this.logger, context?.maskSecrets), [[previousProperties["DBProxyName"], physicalId]]);
29173
29843
  for (const field of [
29174
29844
  "DBProxyName",
29175
29845
  "EngineFamily",
29176
- "VpcSubnetIds"
29846
+ "VpcSubnetIds",
29847
+ "EndpointNetworkType",
29848
+ "TargetConnectionNetworkType"
29177
29849
  ]) if (JSON.stringify(properties[field]) !== JSON.stringify(previousProperties[field]) && !await unchangedBehindSecretReference({
29178
29850
  resourceType,
29179
29851
  key: field,
@@ -29186,6 +29858,7 @@ var RDSDBProxyProvider = class {
29186
29858
  let hasModify = false;
29187
29859
  for (const key of [
29188
29860
  "Auth",
29861
+ "DefaultAuthScheme",
29189
29862
  "RequireTLS",
29190
29863
  "IdleClientTimeout",
29191
29864
  "DebugLogging",
@@ -29313,7 +29986,10 @@ var RDSDBProxyProvider = class {
29313
29986
  SecretArn: a.SecretArn,
29314
29987
  IAMAuth: a.IAMAuth,
29315
29988
  ClientPasswordAuthType: a.ClientPasswordAuthType
29316
- }))
29989
+ })),
29990
+ ...p.DefaultAuthScheme !== void 0 && { DefaultAuthScheme: p.DefaultAuthScheme },
29991
+ ...p.EndpointNetworkType !== void 0 && { EndpointNetworkType: p.EndpointNetworkType },
29992
+ ...p.TargetConnectionNetworkType !== void 0 && { TargetConnectionNetworkType: p.TargetConnectionNetworkType }
29317
29993
  };
29318
29994
  if (p.DBProxyArn) try {
29319
29995
  const tagResp = await client.send(new ListTagsForResourceCommand$10({ ResourceName: p.DBProxyArn }));
@@ -29416,6 +30092,7 @@ var RDSDBProxyEndpointProvider = class {
29416
30092
  "VpcSubnetIds",
29417
30093
  "VpcSecurityGroupIds",
29418
30094
  "TargetRole",
30095
+ "EndpointNetworkType",
29419
30096
  "Tags"
29420
30097
  ])]]);
29421
30098
  getClient() {
@@ -29452,11 +30129,13 @@ var RDSDBProxyEndpointProvider = class {
29452
30129
  VpcSubnetIds: vpcSubnetIds,
29453
30130
  VpcSecurityGroupIds: properties["VpcSecurityGroupIds"],
29454
30131
  TargetRole: properties["TargetRole"],
30132
+ EndpointNetworkType: properties["EndpointNetworkType"],
29455
30133
  Tags: tags.length > 0 ? tags : void 0
29456
30134
  }));
29457
30135
  } catch (error) {
29458
30136
  throw this.wrapCaughtError(error, "CREATE", resourceType, logicalId, void 0, mask);
29459
30137
  }
30138
+ reportCreatedResource(logicalId, resourceType, dbProxyEndpointName);
29460
30139
  let endpoint;
29461
30140
  let arn;
29462
30141
  let isDefault;
@@ -29506,7 +30185,8 @@ var RDSDBProxyEndpointProvider = class {
29506
30185
  "DBProxyName",
29507
30186
  "DBProxyEndpointName",
29508
30187
  "VpcSubnetIds",
29509
- "TargetRole"
30188
+ "TargetRole",
30189
+ "EndpointNetworkType"
29510
30190
  ]) if (JSON.stringify(properties[field]) !== JSON.stringify(previousProperties[field]) && !await unchangedBehindSecretReference({
29511
30191
  resourceType,
29512
30192
  key: field,
@@ -29610,7 +30290,8 @@ var RDSDBProxyEndpointProvider = class {
29610
30290
  DBProxyName: e.DBProxyName,
29611
30291
  VpcSubnetIds: e.VpcSubnetIds ?? [],
29612
30292
  VpcSecurityGroupIds: e.VpcSecurityGroupIds ?? [],
29613
- TargetRole: e.TargetRole ?? "READ_WRITE"
30293
+ TargetRole: e.TargetRole ?? "READ_WRITE",
30294
+ ...e.EndpointNetworkType !== void 0 && { EndpointNetworkType: e.EndpointNetworkType }
29614
30295
  };
29615
30296
  if (e.DBProxyEndpointArn) try {
29616
30297
  const tagResp = await client.send(new ListTagsForResourceCommand$10({ ResourceName: e.DBProxyEndpointArn }));
@@ -29816,6 +30497,7 @@ var RDSDBProxyTargetGroupProvider = class {
29816
30497
  throw await this.retireRegistrationAfterFailedCreate(this.wrapError(error, "CREATE (describe)", resourceType, logicalId, void 0, mask), dbProxyName, targetGroupName, dbClusterIdentifiers, dbInstanceIdentifiers, context?.maskSecrets);
29817
30498
  }
29818
30499
  if (!targetGroupArn) throw await this.retireRegistrationAfterFailedCreate(new ProvisioningError(`Failed to recover TargetGroupArn for ${dbProxyName}/${targetGroupName} after create`, resourceType, logicalId), dbProxyName, targetGroupName, dbClusterIdentifiers, dbInstanceIdentifiers, context?.maskSecrets);
30500
+ if (dbClusterIdentifiers.length > 0 || dbInstanceIdentifiers.length > 0) reportCreatedResource(logicalId, resourceType, targetGroupArn);
29819
30501
  if (tags.length > 0) try {
29820
30502
  await client.send(new AddTagsToResourceCommand$2({
29821
30503
  ResourceName: targetGroupArn,
@@ -30135,6 +30817,7 @@ var RDSDBProxyTargetGroupProvider = class {
30135
30817
  DBClusterIdentifiers: clusters,
30136
30818
  DBInstanceIdentifiers: instances
30137
30819
  }));
30820
+ if (leftBehind) retractCreatedResource(leftBehind.logicalId, leftBehind.resourceType, leftBehind.physicalId);
30138
30821
  return original;
30139
30822
  } catch (cleanupError) {
30140
30823
  const aws = pasteableAwsCommand(maskSecrets);
@@ -30271,6 +30954,25 @@ function masterUserPasswordToSend(properties, previousProperties, context) {
30271
30954
  return typeof desired === "string" && typeof recorded === "string" && recorded === desired && dynamicReferenceTokens(recorded).length === 0 && recorded !== "***" ? void 0 : desired;
30272
30955
  }
30273
30956
  const DOCDB_DEFAULT_PORT = 27017;
30957
+ const DOCDB_CLUSTER_KEPT_ON_REMOVAL = [
30958
+ "DeletionProtection",
30959
+ "BackupRetentionPeriod",
30960
+ "VpcSecurityGroupIds",
30961
+ "EngineVersion",
30962
+ "PreferredBackupWindow",
30963
+ "PreferredMaintenanceWindow",
30964
+ "DBClusterParameterGroupName"
30965
+ ];
30966
+ const DOCDB_INSTANCE_KEPT_ON_REMOVAL = ["PreferredMaintenanceWindow"];
30967
+ function keptOnRemovalWarning$1(keys, properties, previousProperties, context, logicalId, resourceType) {
30968
+ const removedSet = context?.removedProperties ?? new Set(removedTemplateKeys(previousProperties, properties));
30969
+ const removed = keys.filter((key) => removedSet.has(key));
30970
+ if (removed.length === 0) return void 0;
30971
+ const one = removed.length === 1;
30972
+ const names = `${one ? "property" : "properties"} ${removed.join(", ")}`;
30973
+ const modify = resourceType === "AWS::DocDB::DBInstance" ? "ModifyDBInstance" : "ModifyDBCluster";
30974
+ return context?.replayingState ? safeMsg`${logicalId} (${resourceType}): ${names} ${one ? "is" : "are"} absent from the state being restored; ${modify} keeps a setting it is not sent, so the rollback leaves any value the failed deploy applied in place.` : safeMsg`${logicalId} (${resourceType}): ${names} ${one ? "was" : "were"} removed from the template; cdkd leaves the current AWS ${one ? "value" : "values"} in place, as CloudFormation does. Declare ${one ? "it" : "them"} to change ${one ? "it" : "them"}.`;
30975
+ }
30274
30976
  const SECURITY_GROUP_CHANGE_MEMBER_STATUSES = /* @__PURE__ */ new Set(["modifying"]);
30275
30977
  const PORT_CHANGE_MEMBER_STATUSES = /* @__PURE__ */ new Set(["modifying", "rebooting"]);
30276
30978
  function numberOrUndefined(value) {
@@ -30312,6 +31014,25 @@ var DocDBProvider = class {
30312
31014
  logger = getLogger().child("DocDBProvider");
30313
31015
  protectionFlips = new ProtectionFlipRegistry();
30314
31016
  disableCcApiFallback = true;
31017
+ removalHandledInUpdate = /* @__PURE__ */ new Map([["AWS::DocDB::DBCluster", /* @__PURE__ */ new Set([
31018
+ "DBClusterIdentifier",
31019
+ "DBSubnetGroupName",
31020
+ "KmsKeyId",
31021
+ "MasterUsername",
31022
+ "StorageEncrypted",
31023
+ "Port",
31024
+ "Tags",
31025
+ "MasterUserPassword",
31026
+ ...DOCDB_CLUSTER_KEPT_ON_REMOVAL
31027
+ ])], ["AWS::DocDB::DBInstance", /* @__PURE__ */ new Set([
31028
+ "DBInstanceIdentifier",
31029
+ "DBClusterIdentifier",
31030
+ "AvailabilityZone",
31031
+ "DBInstanceClass",
31032
+ "Tags",
31033
+ "AutoMinorVersionUpgrade",
31034
+ ...DOCDB_INSTANCE_KEPT_ON_REMOVAL
31035
+ ])]]);
30315
31036
  handledProperties = /* @__PURE__ */ new Map([["AWS::DocDB::DBCluster", /* @__PURE__ */ new Set([
30316
31037
  "DBClusterIdentifier",
30317
31038
  "EngineVersion",
@@ -30365,7 +31086,7 @@ var DocDBProvider = class {
30365
31086
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
30366
31087
  switch (resourceType) {
30367
31088
  case "AWS::DocDB::DBCluster": return this.updateDBCluster(logicalId, physicalId, resourceType, properties, previousProperties, context);
30368
- case "AWS::DocDB::DBInstance": return this.updateDBInstance(logicalId, physicalId, resourceType, properties, previousProperties, context?.maskSecrets);
31089
+ case "AWS::DocDB::DBInstance": return this.updateDBInstance(logicalId, physicalId, resourceType, properties, previousProperties, context);
30369
31090
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
30370
31091
  }
30371
31092
  }
@@ -30410,6 +31131,7 @@ var DocDBProvider = class {
30410
31131
  }));
30411
31132
  clusterCreated = true;
30412
31133
  createdResourceId = response.DBCluster?.DbClusterResourceId;
31134
+ reportCreatedResource(logicalId, resourceType, dbClusterIdentifier, createdResourceId);
30413
31135
  if (!response.DBCluster) throw new Error("CreateDBCluster did not return DBCluster");
30414
31136
  this.logger.debug(`Successfully created DocDB DBCluster ${logicalId}: ${dbClusterIdentifier}`);
30415
31137
  if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForClusterAvailable(logicalId, dbClusterIdentifier);
@@ -30456,6 +31178,8 @@ var DocDBProvider = class {
30456
31178
  for (const member of described?.DBClusterMembers ?? []) if (member.DBInstanceIdentifier) await this.waitForMemberAfterClusterChange(member.DBInstanceIdentifier, logicalId, SECURITY_GROUP_CHANGE_MEMBER_STATUSES, "VPC security groups");
30457
31179
  }
30458
31180
  if (described?.DBClusterArn) await applyDocDBTagDiff(this.getClient(), this.logger, described.DBClusterArn, resourceType, logicalId, previousProperties["Tags"], properties["Tags"]);
31181
+ const keptWarning = keptOnRemovalWarning$1(DOCDB_CLUSTER_KEPT_ON_REMOVAL, properties, previousProperties, context, logicalId, resourceType);
31182
+ if (keptWarning !== void 0) this.logger.warn(keptWarning);
30459
31183
  return {
30460
31184
  physicalId,
30461
31185
  wasReplaced: false,
@@ -30565,6 +31289,7 @@ var DocDBProvider = class {
30565
31289
  }));
30566
31290
  instanceCreated = true;
30567
31291
  createdResourceId = response.DBInstance?.DbiResourceId;
31292
+ reportCreatedResource(logicalId, resourceType, dbInstanceIdentifier, createdResourceId);
30568
31293
  if (!response.DBInstance) throw new Error("CreateDBInstance did not return DBInstance");
30569
31294
  this.logger.debug(`Successfully created DocDB DBInstance ${logicalId}: ${dbInstanceIdentifier}`);
30570
31295
  if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForInstanceAvailable(logicalId, dbInstanceIdentifier);
@@ -30578,8 +31303,9 @@ var DocDBProvider = class {
30578
31303
  throw thrown;
30579
31304
  }
30580
31305
  }
30581
- async updateDBInstance(logicalId, physicalId, resourceType, properties, previousProperties, mask) {
31306
+ async updateDBInstance(logicalId, physicalId, resourceType, properties, previousProperties, context) {
30582
31307
  this.logger.debug(`Updating DocDB DBInstance ${logicalId}: ${physicalId}`);
31308
+ const mask = context?.maskSecrets;
30583
31309
  refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId);
30584
31310
  try {
30585
31311
  await modifyDbInstanceWhenAvailable({
@@ -30600,6 +31326,8 @@ var DocDBProvider = class {
30600
31326
  this.logger.debug(`Successfully updated DocDB DBInstance ${logicalId}`);
30601
31327
  const described = await this.describeDBInstance(physicalId);
30602
31328
  if (described?.DBInstanceArn) await applyDocDBTagDiff(this.getClient(), this.logger, described.DBInstanceArn, resourceType, logicalId, previousProperties["Tags"], properties["Tags"]);
31329
+ const keptWarning = keptOnRemovalWarning$1(DOCDB_INSTANCE_KEPT_ON_REMOVAL, properties, previousProperties, context, logicalId, resourceType);
31330
+ if (keptWarning !== void 0) this.logger.warn(keptWarning);
30603
31331
  return {
30604
31332
  physicalId,
30605
31333
  wasReplaced: false,
@@ -31268,6 +31996,7 @@ var Route53Provider = class {
31268
31996
  if (!hostedZone?.Id) throw new Error("CreateHostedZone did not return HostedZone.Id");
31269
31997
  const zoneId = hostedZone.Id.replace("/hostedzone/", "");
31270
31998
  createdZoneId = zoneId;
31999
+ reportCreatedResource(logicalId, resourceType, zoneId);
31271
32000
  if (vpcs && vpcs.length > 1) for (let i = 1; i < vpcs.length; i++) {
31272
32001
  const additionalVpc = vpcs[i];
31273
32002
  this.logger.debug(`Associating additional VPC ${String(additionalVpc["VPCId"])} with hosted zone ${zoneId}`);
@@ -31293,6 +32022,7 @@ var Route53Provider = class {
31293
32022
  await this.deleteQueryLoggingConfigForZone(zoneId, logicalId);
31294
32023
  await this.getClient().send(new DeleteHostedZoneCommand({ Id: zoneId }));
31295
32024
  zoneRolledBack = true;
32025
+ retractCreatedResource(logicalId, resourceType, zoneId);
31296
32026
  callerReferenceToken.release();
31297
32027
  } catch (deleteError) {
31298
32028
  const head = `Best-effort rollback DeleteHostedZone for ${zoneId} also failed: `;
@@ -32213,6 +32943,27 @@ function sanitizeDescription(value) {
32213
32943
  function isPlainObject$2(value) {
32214
32944
  return typeof value === "object" && value !== null && !Array.isArray(value);
32215
32945
  }
32946
+ const DATA_PROTECTION_FALSE_DEFAULTS = ["ExcludeRuleMatchDetails", "ExcludeRateBasedDetails"];
32947
+ function toCfnDataProtectionConfig(config, baseline) {
32948
+ const entries = config["DataProtections"];
32949
+ if (!Array.isArray(entries)) return config;
32950
+ const baseEntries = isPlainObject$2(baseline) && Array.isArray(baseline["DataProtections"]) ? baseline["DataProtections"] : [];
32951
+ return {
32952
+ ...config,
32953
+ DataProtections: entries.map((entry, i) => {
32954
+ if (!isPlainObject$2(entry)) return entry;
32955
+ const base = baseEntries[i];
32956
+ const converted = { ...entry };
32957
+ for (const key of DATA_PROTECTION_FALSE_DEFAULTS) if (converted[key] === false && !(isPlainObject$2(base) && key in base)) delete converted[key];
32958
+ return converted;
32959
+ })
32960
+ };
32961
+ }
32962
+ function applicationConfigForUpdate(next, previous) {
32963
+ if (next === void 0 || next === null) return void 0;
32964
+ if (isDeepStrictEqual(next, previous)) return void 0;
32965
+ return next;
32966
+ }
32216
32967
  function toSdkByteMatchStatement(byteMatchStatement) {
32217
32968
  const encoded = byteMatchStatement["SearchStringBase64"];
32218
32969
  if (typeof encoded !== "string") return byteMatchStatement;
@@ -32391,7 +33142,11 @@ var WAFv2WebACLProvider = class {
32391
33142
  "CaptchaConfig",
32392
33143
  "ChallengeConfig",
32393
33144
  "TokenDomains",
32394
- "AssociationConfig"
33145
+ "AssociationConfig",
33146
+ "DataProtectionConfig",
33147
+ "OnSourceDDoSProtectionConfig",
33148
+ "ApplicationConfig",
33149
+ "MonetizationConfig"
32395
33150
  ])]]);
32396
33151
  getClient() {
32397
33152
  if (!this.wafv2Client) {
@@ -32432,7 +33187,11 @@ var WAFv2WebACLProvider = class {
32432
33187
  CaptchaConfig: properties["CaptchaConfig"],
32433
33188
  ChallengeConfig: properties["ChallengeConfig"],
32434
33189
  TokenDomains: properties["TokenDomains"],
32435
- AssociationConfig: properties["AssociationConfig"]
33190
+ AssociationConfig: properties["AssociationConfig"],
33191
+ DataProtectionConfig: properties["DataProtectionConfig"],
33192
+ OnSourceDDoSProtectionConfig: properties["OnSourceDDoSProtectionConfig"],
33193
+ ApplicationConfig: properties["ApplicationConfig"],
33194
+ MonetizationConfig: properties["MonetizationConfig"]
32436
33195
  }))).Summary;
32437
33196
  if (!summary?.ARN || !summary?.Id) throw new Error("CreateWebACL did not return Summary with ARN and Id");
32438
33197
  this.logger.debug(`Successfully created WAFv2 WebACL ${logicalId}: ${summary.ARN}`);
@@ -32486,7 +33245,11 @@ var WAFv2WebACLProvider = class {
32486
33245
  CaptchaConfig: properties["CaptchaConfig"],
32487
33246
  ChallengeConfig: properties["ChallengeConfig"],
32488
33247
  TokenDomains: properties["TokenDomains"],
32489
- AssociationConfig: properties["AssociationConfig"]
33248
+ AssociationConfig: properties["AssociationConfig"],
33249
+ DataProtectionConfig: properties["DataProtectionConfig"],
33250
+ OnSourceDDoSProtectionConfig: properties["OnSourceDDoSProtectionConfig"],
33251
+ ApplicationConfig: applicationConfigForUpdate(properties["ApplicationConfig"], previousProperties["ApplicationConfig"]),
33252
+ MonetizationConfig: properties["MonetizationConfig"]
32490
33253
  }));
32491
33254
  await this.applyTagDiff(physicalId, resourceType, logicalId, previousProperties["Tags"], properties["Tags"]);
32492
33255
  this.logger.debug(`Successfully updated WAFv2 WebACL ${logicalId}`);
@@ -32593,6 +33356,10 @@ var WAFv2WebACLProvider = class {
32593
33356
  if (webACL.ChallengeConfig) result["ChallengeConfig"] = webACL.ChallengeConfig;
32594
33357
  result["TokenDomains"] = webACL.TokenDomains ? [...webACL.TokenDomains] : [];
32595
33358
  if (webACL.AssociationConfig) result["AssociationConfig"] = webACL.AssociationConfig;
33359
+ if (webACL.DataProtectionConfig) result["DataProtectionConfig"] = toCfnDataProtectionConfig(webACL.DataProtectionConfig, properties?.["DataProtectionConfig"]);
33360
+ if (webACL.OnSourceDDoSProtectionConfig) result["OnSourceDDoSProtectionConfig"] = webACL.OnSourceDDoSProtectionConfig;
33361
+ if (webACL.ApplicationConfig) result["ApplicationConfig"] = webACL.ApplicationConfig;
33362
+ if (webACL.MonetizationConfig) result["MonetizationConfig"] = webACL.MonetizationConfig;
32596
33363
  try {
32597
33364
  const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$13({ ResourceARN: physicalId }));
32598
33365
  result["Tags"] = normalizeAwsTagsToCfn(tagsResp.TagInfoForResource?.TagList);
@@ -33068,6 +33835,7 @@ var CognitoUserPoolProvider = class {
33068
33835
  if (!userPool?.Id) throw new Error("CreateUserPool did not return UserPool.Id");
33069
33836
  const userPoolId = userPool.Id;
33070
33837
  createdUserPoolId = userPoolId;
33838
+ reportCreatedResource(logicalId, resourceType, userPoolId);
33071
33839
  const replayWindow = replayedSendWindow(created, attemptStartMs);
33072
33840
  if (replayWindow !== void 0) await this.reportPossibleOrphanPools(logicalId, poolName, replayWindow, createMaskedLogSinks(this.logger, context?.maskSecrets), userPoolId);
33073
33841
  const userPoolArn = userPool.Arn;
@@ -33095,6 +33863,7 @@ var CognitoUserPoolProvider = class {
33095
33863
  markAuxiliaryFailure(error, logicalId);
33096
33864
  try {
33097
33865
  await this.getClient().send(new DeleteUserPoolCommand({ UserPoolId: createdUserPoolId }));
33866
+ retractCreatedResource(logicalId, resourceType, createdUserPoolId);
33098
33867
  this.logger.debug(`Rolled back partially-created Cognito User Pool ${createdUserPoolId}`);
33099
33868
  } catch (rollbackError) {
33100
33869
  poolLeftBehind = true;
@@ -33791,6 +34560,36 @@ function nodesToRemove(nodes, desiredCount, desiredZones) {
33791
34560
  if (keep.size + unnamed < desiredCount) return void 0;
33792
34561
  return named.map((n) => n.id).filter((id) => !keep.has(id)).sort();
33793
34562
  }
34563
+ const KEPT_ON_REMOVAL$1 = [
34564
+ "EngineVersion",
34565
+ "CacheParameterGroupName",
34566
+ "PreferredMaintenanceWindow",
34567
+ "SnapshotWindow",
34568
+ "IpDiscovery",
34569
+ "SnapshotRetentionLimit",
34570
+ "AutoMinorVersionUpgrade",
34571
+ "NotificationTopicArn",
34572
+ "VpcSecurityGroupIds",
34573
+ "TransitEncryptionEnabled",
34574
+ "PreferredAvailabilityZone"
34575
+ ];
34576
+ const NOT_MODIFIABLE = /* @__PURE__ */ new Set(["TransitEncryptionEnabled", "PreferredAvailabilityZone"]);
34577
+ const PLACEMENT_ON_REMOVAL = ["AZMode", "PreferredAvailabilityZones"];
34578
+ const removalWarnings = (logicalId, kept, placement, caller) => {
34579
+ const lines = [];
34580
+ const head = (names) => safeMsg`${logicalId} (AWS::ElastiCache::CacheCluster): ${names.length === 1 ? "property" : "properties"} ${names.join(", ")}`;
34581
+ if (kept.length > 0) {
34582
+ const one = kept.length === 1;
34583
+ const fixed = kept.filter((name) => NOT_MODIFIABLE.has(name));
34584
+ const replaceNote = fixed.length === 0 ? "" : safeMsg` (${fixed.join(", ")} ${fixed.length === 1 ? "changes" : "change"} only by replacing the cluster)`;
34585
+ lines.push(caller === "rollback" ? `${head(kept)} ${one ? "is" : "are"} absent from the state being restored; the rollback sends nothing for ${one ? "it" : "them"}, so the cluster keeps the value it holds now.` : `${head(kept)} ${one ? "was" : "were"} removed from the template and ${one ? "keeps its" : "keep their"} current AWS value, as CloudFormation leaves ${one ? "it" : "them"}; cdkd sends nothing for a removed property. To change a value, declare it explicitly${replaceNote}.`);
34586
+ }
34587
+ if (placement.length > 0) {
34588
+ const one = placement.length === 1;
34589
+ lines.push(caller === "rollback" ? `${head(placement)} ${one ? "is" : "are"} absent from the state being restored; the rollback leaves the cluster unchanged.` : `${head(placement)} ${one ? "was" : "were"} removed from the template; cdkd sends nothing for a removed property, so the nodes stay in their zones. CloudFormation updates a cross-az cluster, or a single-az one that keeps its zone list, in place. It replaces a memcached single-az cluster left with no zone, and cdkd plans that replacement too (go-to-k/cdkd#5558). To move the nodes, replace the cluster, for example by changing its logical ID.`);
34590
+ }
34591
+ return lines;
34592
+ };
33794
34593
  var ElastiCacheProvider = class {
33795
34594
  client;
33796
34595
  createClient;
@@ -33840,6 +34639,22 @@ var ElastiCacheProvider = class {
33840
34639
  "TransitEncryptionEnabled"
33841
34640
  ])]]);
33842
34641
  unhandledByDesign = /* @__PURE__ */ new Map([["AWS::ElastiCache::CacheCluster", /* @__PURE__ */ new Map([["CacheSecurityGroupNames", "EC2-Classic-only — use VpcSecurityGroupIds for VPC-deployed clusters (EC2-Classic retired 2022-08-15)"]])]]);
34642
+ removalHandledInUpdate = /* @__PURE__ */ new Map([["AWS::ElastiCache::CacheCluster", /* @__PURE__ */ new Set([
34643
+ "CacheSubnetGroupName",
34644
+ "ClusterName",
34645
+ "Engine",
34646
+ "NetworkType",
34647
+ "Port",
34648
+ "SnapshotName",
34649
+ "SnapshotArns",
34650
+ "CacheSecurityGroupNames",
34651
+ "CacheNodeType",
34652
+ "NumCacheNodes",
34653
+ "Tags",
34654
+ "LogDeliveryConfigurations",
34655
+ ...KEPT_ON_REMOVAL$1,
34656
+ ...PLACEMENT_ON_REMOVAL
34657
+ ])]]);
33843
34658
  getClient() {
33844
34659
  if (!this.client) {
33845
34660
  this.client = new ElastiCacheClient({
@@ -33869,7 +34684,7 @@ var ElastiCacheProvider = class {
33869
34684
  const mask = context?.maskSecrets;
33870
34685
  switch (resourceType) {
33871
34686
  case "AWS::ElastiCache::SubnetGroup": return this.updateSubnetGroup(logicalId, physicalId, resourceType, properties, previousProperties, mask);
33872
- case "AWS::ElastiCache::CacheCluster": return this.updateCacheCluster(logicalId, physicalId, resourceType, properties, previousProperties, mask);
34687
+ case "AWS::ElastiCache::CacheCluster": return this.updateCacheCluster(logicalId, physicalId, resourceType, properties, previousProperties, mask, context);
33873
34688
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
33874
34689
  }
33875
34690
  }
@@ -33976,6 +34791,7 @@ var ElastiCacheProvider = class {
33976
34791
  ...tags.length > 0 && { Tags: tags }
33977
34792
  }));
33978
34793
  clusterCreated = true;
34794
+ reportCreatedResource(logicalId, resourceType, cacheClusterId);
33979
34795
  this.logger.debug(`Successfully created CacheCluster ${logicalId}: ${cacheClusterId}`);
33980
34796
  if (process.env["CDKD_NO_WAIT"] !== "true") await this.waitForClusterAvailable(cacheClusterId);
33981
34797
  const described = await this.describeCacheCluster(cacheClusterId);
@@ -34000,7 +34816,7 @@ var ElastiCacheProvider = class {
34000
34816
  throw thrown;
34001
34817
  }
34002
34818
  }
34003
- async updateCacheCluster(logicalId, physicalId, resourceType, properties, previousProperties, mask) {
34819
+ async updateCacheCluster(logicalId, physicalId, resourceType, properties, previousProperties, mask, context) {
34004
34820
  this.logger.debug(`Updating CacheCluster ${logicalId}: ${physicalId}`);
34005
34821
  refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId);
34006
34822
  try {
@@ -34039,6 +34855,7 @@ var ElastiCacheProvider = class {
34039
34855
  const described = await this.describeCacheCluster(physicalId);
34040
34856
  this.warnTransitEncryptionNotApplied(logicalId, properties, previousProperties, described?.TransitEncryptionEnabled);
34041
34857
  if (described?.ARN) await this.applyTagDiff(described.ARN, resourceType, logicalId, previousProperties["Tags"], properties["Tags"]);
34858
+ this.warnRemovalsLeftInPlace(logicalId, properties, previousProperties, context);
34042
34859
  const attributes = {};
34043
34860
  if (described?.CacheNodes?.[0]?.Endpoint) {
34044
34861
  const endpoint = described.CacheNodes[0].Endpoint;
@@ -34139,6 +34956,13 @@ var ElastiCacheProvider = class {
34139
34956
  if (notApplied.length === 0) return;
34140
34957
  this.logger.warn(safeMsg`CacheCluster ${logicalId}: the ${notApplied.join(", ")} change is not applied to the ` + "existing cluster (ModifyCacheCluster cannot change it, and CloudFormation leaves it unchanged too). To apply it, replace the cluster, for example by changing its logical ID.");
34141
34958
  }
34959
+ warnRemovalsLeftInPlace(logicalId, properties, previousProperties, context) {
34960
+ if (context?.desiredFromAwsReadback) return;
34961
+ const removed = context?.removedProperties;
34962
+ const wasRemoved = (key) => removed !== void 0 ? removed.has(key) : properties[key] === void 0 && previousProperties[key] !== void 0;
34963
+ const lines = removalWarnings(logicalId, KEPT_ON_REMOVAL$1.filter(wasRemoved), PLACEMENT_ON_REMOVAL.filter(wasRemoved), context?.replayingState ? "rollback" : "deploy");
34964
+ for (const line of lines) this.logger.warn(line);
34965
+ }
34142
34966
  buildLogDeliveryConfigurationsForUpdate(newConfigs, previousConfigs) {
34143
34967
  const kept = newConfigs ?? [];
34144
34968
  const keptLogTypes = new Set(kept.map((c) => c.LogType).filter((t) => t !== void 0));
@@ -34786,6 +35610,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
34786
35610
  if (!service || !service.Id) throw new Error("CreateService did not return Service ID");
34787
35611
  const serviceId = service.Id;
34788
35612
  leftBehindServiceId = serviceId;
35613
+ reportCreatedResource(logicalId, resourceType, serviceId);
34789
35614
  this.logger.debug(`Successfully created service discovery service ${logicalId}: ${serviceId}`);
34790
35615
  try {
34791
35616
  const attrs = this.normalizeServiceAttributes(properties["ServiceAttributes"]);
@@ -34810,6 +35635,7 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
34810
35635
  try {
34811
35636
  await client.send(new DeleteServiceCommand$1({ Id: serviceId }));
34812
35637
  leftBehindServiceId = void 0;
35638
+ retractCreatedResource(logicalId, resourceType, serviceId);
34813
35639
  this.logger.debug(`Cleaned up partially-created ServiceDiscovery Service ${logicalId} (${serviceId}) after ServiceAttributes wiring failure`);
34814
35640
  } catch (cleanupError) {
34815
35641
  const cleanupFailure = describeAwsFailure(cleanupError);
@@ -35303,6 +36129,25 @@ const APPSYNC_APIKEY_ID_FORMAT = {
35303
36129
  label: "AppSync ApiKey",
35304
36130
  segments: ["apiId", "apiKeyId"]
35305
36131
  };
36132
+ const CFN_APPSYNC_ARN_RESOURCE_PATHS = {
36133
+ "AWS::AppSync::GraphQLApi": /^apis\/([^/|]+)$/,
36134
+ "AWS::AppSync::DataSource": /^apis\/([^/|]+)\/datasources\/([^/|]+)$/,
36135
+ "AWS::AppSync::Resolver": /^apis\/([^/|]+)\/types\/([^/|]+)\/resolvers\/([^/|]+)$/,
36136
+ "AWS::AppSync::ApiKey": /^apis\/([^/|]+)\/apikeys?\/([^/|]+)$/
36137
+ };
36138
+ function graphQLSchemaApiId(physicalId) {
36139
+ const schema = /^([a-z0-9]+)GraphQLSchema$/.exec(physicalId);
36140
+ return schema ? schema[1] : physicalId;
36141
+ }
36142
+ function appSyncPhysicalIdFromCfn(resourceType, logicalId, physicalId, region) {
36143
+ if (resourceType === "AWS::AppSync::GraphQLSchema") return graphQLSchemaApiId(physicalId);
36144
+ const pathPattern = CFN_APPSYNC_ARN_RESOURCE_PATHS[resourceType];
36145
+ const arn = /^arn:[^:]+:appsync:([^:]*):[^:]*:(.+)$/.exec(physicalId);
36146
+ const segments = pathPattern && arn ? pathPattern.exec(arn[2]) : null;
36147
+ if (!arn || !segments) return physicalId;
36148
+ if (canonicalizeRegion(arn[1]) !== canonicalizeRegion(region)) throw markNonRetryable(new ProvisioningError(safeMsg`${resourceType} ${physicalId} is in region ${arn[1]}, not the import region ${region}`, resourceType, logicalId, physicalId));
36149
+ return segments.slice(1).join("|");
36150
+ }
35306
36151
  const MUTABLE_GRAPHQL_API_CONFIG_PROPERTIES = [
35307
36152
  "UserPoolConfig",
35308
36153
  "OpenIDConnectConfig",
@@ -35442,7 +36287,7 @@ var AppSyncProvider = class {
35442
36287
  }
35443
36288
  if (resourceType === "AWS::AppSync::ApiKey" && parts.length === 2) {
35444
36289
  const [apiId, apiKeyId] = parts;
35445
- const apiKeyArn = await arn(`apis/${apiId}/apikey/${apiKeyId}`);
36290
+ const apiKeyArn = await arn(`apis/${apiId}/apikeys/${apiKeyId}`);
35446
36291
  return {
35447
36292
  ApiKey: apiKeyId,
35448
36293
  ...apiKeyArn && { Arn: apiKeyArn }
@@ -35591,12 +36436,13 @@ var AppSyncProvider = class {
35591
36436
  };
35592
36437
  }
35593
36438
  async updateGraphQLSchema(logicalId, physicalId, resourceType, properties, previousProperties, mask = maskerOrIdentity(void 0)) {
36439
+ const recordedApiId = graphQLSchemaApiId(physicalId);
35594
36440
  if (properties["ApiId"] !== void 0 && previousProperties["ApiId"] !== void 0 && properties["ApiId"] !== previousProperties["ApiId"] && !await unchangedBehindSecretReference({
35595
36441
  resourceType,
35596
36442
  key: "ApiId",
35597
36443
  desired: properties["ApiId"],
35598
36444
  previous: previousProperties["ApiId"],
35599
- physicalName: physicalId,
36445
+ physicalName: recordedApiId,
35600
36446
  maskSecrets: mask
35601
36447
  })) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS AppSync GraphQLSchema.ApiId cannot move a schema to another API in place (CloudFormation fails a template change of it too) — deploy with --replace, or give the schema a new logical ID, to create it on the new API");
35602
36448
  const newDef = properties["Definition"];
@@ -35621,7 +36467,7 @@ var AppSyncProvider = class {
35621
36467
  physicalId,
35622
36468
  wasReplaced: false
35623
36469
  };
35624
- const apiId = properties["ApiId"] ?? physicalId;
36470
+ const apiId = properties["ApiId"] ?? recordedApiId;
35625
36471
  try {
35626
36472
  await this.getClient().send(new StartSchemaCreationCommand({
35627
36473
  apiId,
@@ -36234,6 +37080,7 @@ var AppSyncProvider = class {
36234
37080
  throw error;
36235
37081
  }
36236
37082
  createdApiId = response.graphqlApi?.apiId;
37083
+ if (createdApiId) reportCreatedResource(logicalId, resourceType, createdApiId);
36237
37084
  const apiId = response.graphqlApi.apiId;
36238
37085
  const replayWindow = replayedSendWindow(response, attemptStartMs);
36239
37086
  if (replayWindow !== void 0) await this.reportPossibleOrphanApis(logicalId, input, replayWindow, maskerOrIdentity(context?.maskSecrets), apiId);
@@ -36257,6 +37104,7 @@ var AppSyncProvider = class {
36257
37104
  markAuxiliaryFailure(error, logicalId);
36258
37105
  try {
36259
37106
  await this.getClient().send(new DeleteGraphqlApiCommand({ apiId: createdApiId }));
37107
+ retractCreatedResource(logicalId, resourceType, createdApiId);
36260
37108
  this.logger.debug(`Rolled back partially-created GraphQL API ${createdApiId}`);
36261
37109
  } catch (rollbackError) {
36262
37110
  apiLeftBehind = true;
@@ -36543,7 +37391,7 @@ var AppSyncProvider = class {
36543
37391
  maskSecrets: context?.maskSecrets,
36544
37392
  ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
36545
37393
  });
36546
- const apiKeyArn = await this.buildAppSyncArnOrWarn(`apis/${apiId}/apikey/${apiKeyId}`, logicalId, physicalId);
37394
+ const apiKeyArn = await this.buildAppSyncArnOrWarn(`apis/${apiId}/apikeys/${apiKeyId}`, logicalId, physicalId);
36547
37395
  return {
36548
37396
  physicalId,
36549
37397
  attributes: {
@@ -36628,10 +37476,11 @@ var AppSyncProvider = class {
36628
37476
  }
36629
37477
  }
36630
37478
  async readGraphQLSchema(physicalId, properties) {
37479
+ const apiId = graphQLSchemaApiId(physicalId);
36631
37480
  let resp;
36632
37481
  try {
36633
37482
  resp = await this.getClient().send(new GetIntrospectionSchemaCommand({
36634
- apiId: physicalId,
37483
+ apiId,
36635
37484
  format: "SDL"
36636
37485
  }));
36637
37486
  } catch (err) {
@@ -36648,7 +37497,7 @@ var AppSyncProvider = class {
36648
37497
  if (await this.canonicalizeSdl(stateDefinition, "state") === canonicalAws) definitionToReturn = stateDefinition;
36649
37498
  }
36650
37499
  return {
36651
- ApiId: physicalId,
37500
+ ApiId: apiId,
36652
37501
  Definition: definitionToReturn
36653
37502
  };
36654
37503
  }
@@ -36889,14 +37738,18 @@ var AppSyncProvider = class {
36889
37738
  return RESOURCE_NOT_FOUND;
36890
37739
  }
36891
37740
  async import(input) {
37741
+ const known = input.knownPhysicalId && appSyncPhysicalIdFromCfn(input.resourceType, input.logicalId, input.knownPhysicalId, input.region);
36892
37742
  if (input.resourceType !== "AWS::AppSync::GraphQLApi") {
36893
- if (input.knownPhysicalId) return {
36894
- physicalId: input.knownPhysicalId,
36895
- attributes: await this.childImportAttributes(input.resourceType, input.knownPhysicalId, input.region)
37743
+ if (known) return {
37744
+ physicalId: known,
37745
+ attributes: await this.childImportAttributes(input.resourceType, known, input.region)
36896
37746
  };
36897
37747
  return null;
36898
37748
  }
36899
- const explicit = resolveExplicitPhysicalId(input, null);
37749
+ const explicit = resolveExplicitPhysicalId(known ? {
37750
+ ...input,
37751
+ knownPhysicalId: known
37752
+ } : input, null);
36900
37753
  if (explicit) try {
36901
37754
  const response = await this.getClient().send(new GetGraphqlApiCommand({ apiId: explicit }));
36902
37755
  return {
@@ -39391,6 +40244,7 @@ var KMSProvider = class {
39391
40244
  heldAtMs: Date.now()
39392
40245
  });
39393
40246
  createdKeyId = keyId;
40247
+ reportCreatedResource(logicalId, resourceType, keyId);
39394
40248
  const declinedAtMs = supersededAttempts.get(attemptKey);
39395
40249
  supersededAttempts.delete(attemptKey);
39396
40250
  supersededPending = declinedAtMs !== void 0 && Date.now() - declinedAtMs <= 18e5;
@@ -39398,6 +40252,7 @@ var KMSProvider = class {
39398
40252
  if (replayWindow !== void 0) await this.reportPossibleOrphanKeys(logicalId, input, replayWindow);
39399
40253
  }
39400
40254
  createdKeyId = keyId;
40255
+ reportCreatedResource(logicalId, resourceType, keyId);
39401
40256
  if (enableKeyRotation === true) {
39402
40257
  const rotationPeriodInDays = properties["RotationPeriodInDays"];
39403
40258
  this.logger.debug(`Enabling key rotation for KMS Key ${logicalId}`);
@@ -40623,58 +41478,6 @@ var BudgetsBudgetProvider = class {
40623
41478
  }
40624
41479
  };
40625
41480
 
40626
- //#endregion
40627
- //#region src/provisioning/delete-gone-wait.ts
40628
- async function waitForGoneAfterDelete(opts) {
40629
- const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
40630
- const now = opts.now ?? Date.now;
40631
- const userTimeoutMs = resolvedResourceTimeoutMs(opts.resourceType);
40632
- const maxWaitMs = userTimeoutMs === void 0 ? opts.maxWaitMs : Math.min(opts.maxWaitMs, userTimeoutMs / 2);
40633
- const startedAt = now();
40634
- const stopped = `${opts.what}: the delete was ACCEPTED by AWS, but cdkd stopped waiting for it to disappear`;
40635
- const consequence = "AWS finishes the delete on its side; until it does, a create of the same name is refused with \"already exists\".";
40636
- const watch = startInterruptWatch(`${opts.what} deletion wait`);
40637
- try {
40638
- let lastStatus;
40639
- let sawNonFailedStatus = false;
40640
- for (;;) {
40641
- if (watch.isInterrupted()) {
40642
- opts.logger.warn(safeMsg`${stopped} (interrupted). ${consequence}`);
40643
- return;
40644
- }
40645
- try {
40646
- const status = await opts.describe();
40647
- if (status === void 0) {
40648
- opts.logger.debug(safeMsg`${opts.what} is gone`);
40649
- return;
40650
- }
40651
- const failure = opts.failedStatus?.(status);
40652
- if (failure !== void 0) {
40653
- if (sawNonFailedStatus || now() - startedAt >= opts.pollIntervalMs) throw markWaitAbandoned(failure);
40654
- } else sawNonFailedStatus = true;
40655
- lastStatus = status;
40656
- opts.logger.debug(safeMsg`${opts.what} status: ${status}, waiting for it to disappear`);
40657
- } catch (error) {
40658
- if (isWaitAbandonedError(error)) throw error;
40659
- if (!isThrottlingError(error) && !isTransientServerError(error)) {
40660
- opts.logger.debug(safeMsg`${opts.what}: status read failed: ${describeAwsFailure(error).detail}`);
40661
- opts.logger.warn(safeMsg`${stopped}: its status could not be read (${describeAwsFailure(error).summary}). ${consequence}`);
40662
- return;
40663
- }
40664
- opts.logger.debug(safeMsg`${opts.what}: status read throttled or failed transiently, re-polling`);
40665
- }
40666
- const remainingMs = maxWaitMs - (now() - startedAt);
40667
- if (remainingMs <= 0) {
40668
- opts.logger.warn(safeMsg`${stopped} after ${Math.round(maxWaitMs / 1e3)}s` + safeMsg`${lastStatus !== void 0 ? ` (last status: ${lastStatus})` : ""}. ${consequence}`);
40669
- return;
40670
- }
40671
- await sleep(Math.min(opts.pollIntervalMs, remainingMs));
40672
- }
40673
- } finally {
40674
- watch.dispose();
40675
- }
40676
- }
40677
-
40678
41481
  //#endregion
40679
41482
  //#region src/provisioning/providers/kinesis-provider.ts
40680
41483
  function isKmsEncryption(value) {
@@ -40864,6 +41667,7 @@ var KinesisStreamProvider = class {
40864
41667
  ...Object.keys(tags).length > 0 && { Tags: tags }
40865
41668
  }));
40866
41669
  streamCreated = true;
41670
+ reportCreatedResource(logicalId, resourceType, streamName);
40867
41671
  this.logger.debug(`CreateStream initiated for ${streamName}, waiting for ACTIVE status`);
40868
41672
  const streamInfo = await this.waitForStreamActive(streamName);
40869
41673
  const retentionPeriodHours = retentionRead.kind === "usable" ? retentionRead.hours : void 0;
@@ -40969,7 +41773,10 @@ var KinesisStreamProvider = class {
40969
41773
  }
40970
41774
  await this.applyTagDiff(physicalId, resourceType, logicalId, previousProperties["Tags"], properties["Tags"], {
40971
41775
  forceUpsert: context?.unsettledProperties?.has("Tags") === true,
40972
- beforeWrite: () => markUnsettled("Tags")
41776
+ beforeWrite: () => markUnsettled("Tags"),
41777
+ afterAdded: (keys) => {
41778
+ if (keys.length > 0) context?.markAttempted?.("Tags", keys);
41779
+ }
40973
41780
  });
40974
41781
  const newEncryption = properties["StreamEncryption"];
40975
41782
  const oldEncryption = previousProperties["StreamEncryption"];
@@ -41031,10 +41838,13 @@ var KinesisStreamProvider = class {
41031
41838
  }
41032
41839
  if (toEnable.length > 0) {
41033
41840
  this.logger.debug(`Enabling ${toEnable.length} shard-level metric(s) on ${physicalId}`);
41034
- await this.getClient().send(new EnableEnhancedMonitoringCommand({
41841
+ const current = expandShardLevelMetrics((await this.getClient().send(new EnableEnhancedMonitoringCommand({
41035
41842
  StreamName: physicalId,
41036
41843
  ShardLevelMetrics: toEnable
41037
- }));
41844
+ })))?.CurrentShardLevelMetrics ?? []);
41845
+ const before = new Set(Array.isArray(current) ? current : []);
41846
+ const added = toEnable.filter((metric) => !before.has(metric));
41847
+ if (added.length > 0) context?.markAttempted?.("DesiredShardLevelMetrics", added);
41038
41848
  await this.waitForStreamActive(physicalId);
41039
41849
  }
41040
41850
  }
@@ -41201,6 +42011,13 @@ var KinesisStreamProvider = class {
41201
42011
  StreamName: streamName,
41202
42012
  Tags: tagsToAdd
41203
42013
  }));
42014
+ let recordedKeys;
42015
+ try {
42016
+ recordedKeys = /* @__PURE__ */ new Set([...planTagDiff(void 0, oldTagsRaw).set.keys()]);
42017
+ } catch {
42018
+ recordedKeys = void 0;
42019
+ }
42020
+ unsettled.afterAdded?.(Object.keys(tagsToAdd).filter((key) => recordedKeys !== void 0 && !recordedKeys.has(key)));
41204
42021
  this.logger.debug(`Added/updated ${Object.keys(tagsToAdd).length} tag(s) on Kinesis stream ${streamName}`);
41205
42022
  }
41206
42023
  }
@@ -41217,7 +42034,7 @@ var KinesisStreamProvider = class {
41217
42034
  const secretDerived = mask(physicalId) !== physicalId;
41218
42035
  warn(`Kinesis stream ${physicalId} has shard-level metrics enabled that neither cdkd's record nor the template declares (${undeclared.join(", ")}), so cdkd left them enabled while converging the unsettled DesiredShardLevelMetrics (CloudFormation never disables a metric it did not record). Another tool may have enabled them, or an earlier failed cdkd update may have; cdkd cannot tell which. If they should be off, disable them ` + (secretDerived ? `with DisableEnhancedMonitoring for those metrics.` : `with: aws kinesis disable-enhanced-monitoring --stream-name ${physicalId} --shard-level-metrics ${undeclared.join(" ")} (in the stream's region)`));
41219
42036
  }
41220
- async readUnsettled(physicalId, logicalId, resourceType, names, recordedProperties, desiredProperties) {
42037
+ async readUnsettled(physicalId, logicalId, resourceType, names, recordedProperties, desiredProperties, attemptedItems) {
41221
42038
  const live = await this.readCurrentState(physicalId, logicalId, resourceType);
41222
42039
  if (typeof live !== "object" || live === null) return live;
41223
42040
  const out = { ...live };
@@ -41226,6 +42043,7 @@ var KinesisStreamProvider = class {
41226
42043
  const expanded = expandShardLevelMetrics(bag?.["DesiredShardLevelMetrics"]);
41227
42044
  return isUsableMetricsList(expanded) ? expanded : [];
41228
42045
  }));
42046
+ for (const metric of attemptedItems?.get("DesiredShardLevelMetrics") ?? []) if (metric !== "ALL" && isUsableAttemptedName(metric)) named.add(metric);
41229
42047
  const liveMetrics = expandShardLevelMetrics(out["DesiredShardLevelMetrics"]);
41230
42048
  out["DesiredShardLevelMetrics"] = (Array.isArray(liveMetrics) ? liveMetrics : []).filter((metric) => typeof metric === "string" && named.has(metric));
41231
42049
  }
@@ -41386,6 +42204,7 @@ var KinesisStreamConsumerProvider = class {
41386
42204
  if (!consumer?.ConsumerARN || !consumer.ConsumerName) throw new ProvisioningError("RegisterStreamConsumer did not return ConsumerARN/ConsumerName", resourceType, logicalId);
41387
42205
  const consumerArn = consumer.ConsumerARN;
41388
42206
  createdArn = consumerArn;
42207
+ reportCreatedResource(logicalId, resourceType, consumerArn);
41389
42208
  await this.waitForConsumerActive(consumerArn);
41390
42209
  this.logger.debug(`Successfully registered Kinesis stream consumer ${logicalId}`);
41391
42210
  return {
@@ -42259,6 +43078,7 @@ var PipesPipeProvider = class {
42259
43078
  ...Object.keys(tags).length > 0 && { Tags: tags }
42260
43079
  }));
42261
43080
  createdIdentity = pipeIdentityToken(name, created?.Arn, created?.CreationTime);
43081
+ reportCreatedResource(logicalId, resourceType, name, createdIdentity);
42262
43082
  } catch (error) {
42263
43083
  const cause = error instanceof Error ? error : void 0;
42264
43084
  throw this.wrapCaughtError(mask, error, (text) => new ProvisioningError(`Failed to create Pipe ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
@@ -42566,7 +43386,10 @@ var PipesPipeProvider = class {
42566
43386
  try {
42567
43387
  await this.getClient().send(new DeletePipeCommand({ Name: name }));
42568
43388
  } catch (cleanupError) {
42569
- if (cleanupError instanceof NotFoundException$6) return original;
43389
+ if (cleanupError instanceof NotFoundException$6) {
43390
+ retractCreatedResource(logicalId, resourceType, name);
43391
+ return original;
43392
+ }
42570
43393
  return orphaned(describeAwsFailure(cleanupError).summary);
42571
43394
  }
42572
43395
  let gone = false;
@@ -42592,10 +43415,12 @@ var PipesPipeProvider = class {
42592
43415
  return orphaned("its delete did not complete (AWS reports a failed or rolled-back delete)");
42593
43416
  }
42594
43417
  if (gone) {
43418
+ retractCreatedResource(logicalId, resourceType, name);
42595
43419
  original.message += " (cdkd deleted the pipe it had created, so nothing is left behind)";
42596
43420
  return original;
42597
43421
  }
42598
43422
  if (sawDeleting) {
43423
+ retractCreatedResource(logicalId, resourceType, name);
42599
43424
  original.message += " (cdkd started deleting the pipe it had created; a re-deploy may report the name in use until AWS finishes)";
42600
43425
  return original;
42601
43426
  }
@@ -42741,7 +43566,7 @@ var EFSProvider = class {
42741
43566
  const log = createMaskedLogSinks(this.logger, context?.maskSecrets);
42742
43567
  properties = withRemovalDefaults(this.removalDefaults, resourceType, properties, previousProperties, context);
42743
43568
  switch (resourceType) {
42744
- case "AWS::EFS::FileSystem": return this.updateFileSystem(logicalId, physicalId, resourceType, properties, previousProperties, log);
43569
+ case "AWS::EFS::FileSystem": return this.updateFileSystem(logicalId, physicalId, resourceType, properties, previousProperties, log, context?.replayingState === true || context?.desiredFromAwsReadback === true);
42745
43570
  case "AWS::EFS::MountTarget": return this.updateMountTarget(logicalId, physicalId, resourceType, properties, log);
42746
43571
  case "AWS::EFS::AccessPoint": return this.updateAccessPoint(logicalId, physicalId, resourceType, properties, previousProperties, log);
42747
43572
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
@@ -42789,7 +43614,7 @@ var EFSProvider = class {
42789
43614
  throw this.wrapMaskedError(log, error, (text) => new ProvisioningError(`Failed to update EFS AccessPoint ${logicalId}: ${text}`, resourceType, logicalId, physicalId, cause));
42790
43615
  }
42791
43616
  }
42792
- async updateFileSystem(logicalId, physicalId, resourceType, properties, previousProperties, log) {
43617
+ async updateFileSystem(logicalId, physicalId, resourceType, properties, previousProperties, log, stateBorneDesired) {
42793
43618
  for (const key of [
42794
43619
  "Encrypted",
42795
43620
  "KmsKeyId",
@@ -42804,8 +43629,13 @@ var EFSProvider = class {
42804
43629
  const newProvisioned = properties["ProvisionedThroughputInMibps"];
42805
43630
  const oldThroughputMode = previousProperties["ThroughputMode"];
42806
43631
  const oldProvisioned = previousProperties["ProvisionedThroughputInMibps"];
43632
+ const throughputValueMissing = newThroughputMode === "provisioned" && newProvisioned === void 0 && (oldProvisioned !== void 0 || oldThroughputMode !== "provisioned");
43633
+ if (throughputValueMissing) {
43634
+ if (!stateBorneDesired) throw new ProvisioningError(`Failed to update EFS FileSystem ${logicalId}: Throughput value must be specified for provisioned throughput mode. The template sets ThroughputMode: provisioned without ProvisionedThroughputInMibps, which CloudFormation rejects too. Set ProvisionedThroughputInMibps, or change ThroughputMode (e.g. to bursting or elastic).`, resourceType, logicalId, physicalId);
43635
+ log.warn(`EFS FileSystem ${logicalId}: the desired properties (from cdkd state or an AWS readback, not the template) set ThroughputMode: provisioned without ProvisionedThroughputInMibps, which AWS rejects; leaving the file system's throughput as it is on AWS and applying the rest of the update. Its throughput may now differ from the record: run cdkd drift to see it.`);
43636
+ }
42807
43637
  const effectiveThroughputMode = newThroughputMode === void 0 && newProvisioned === void 0 && oldThroughputMode === "provisioned" ? "bursting" : newThroughputMode;
42808
- const throughputModeChanged = effectiveThroughputMode !== void 0 && effectiveThroughputMode !== oldThroughputMode;
43638
+ const throughputModeChanged = !throughputValueMissing && effectiveThroughputMode !== void 0 && effectiveThroughputMode !== oldThroughputMode;
42809
43639
  const provisionedChanged = newProvisioned !== void 0 && newProvisioned !== oldProvisioned;
42810
43640
  const changed = (key) => JSON.stringify(properties[key]) !== JSON.stringify(previousProperties[key]);
42811
43641
  const lifecycleChanged = changed("LifecyclePolicies");
@@ -42926,6 +43756,7 @@ var EFSProvider = class {
42926
43756
  }))
42927
43757
  });
42928
43758
  fileSystemId = created.fileSystemId;
43759
+ reportCreatedResource(logicalId, resourceType, fileSystemId);
42929
43760
  const available = await this.waitForFileSystemAvailable(fileSystemId, logicalId, resourceType, log);
42930
43761
  const arn = created.arn ?? available.FileSystemArn;
42931
43762
  await this.applyLifecyclePolicies(fileSystemId, properties["LifecyclePolicies"], log);
@@ -42947,6 +43778,7 @@ var EFSProvider = class {
42947
43778
  try {
42948
43779
  await this.getClient().send(new DeleteFileSystemCommand({ FileSystemId: fileSystemId }));
42949
43780
  this.unconfirmedCreationTokens.delete(creationToken);
43781
+ retractCreatedResource(logicalId, resourceType, fileSystemId);
42950
43782
  log.debug(`Rolled back partially-created EFS FileSystem ${fileSystemId}`);
42951
43783
  } catch (cleanupError) {
42952
43784
  fileSystemLeftBehind = true;
@@ -43124,6 +43956,7 @@ var EFSProvider = class {
43124
43956
  }));
43125
43957
  const mountTargetId = response.MountTargetId;
43126
43958
  createdMountTargetId = mountTargetId;
43959
+ reportCreatedResource(logicalId, resourceType, mountTargetId);
43127
43960
  log.debug(`Created EFS MountTarget ${logicalId}: ${mountTargetId}, waiting for available state`);
43128
43961
  await this.waitForMountTargetAvailable(mountTargetId, logicalId, resourceType, log);
43129
43962
  log.debug(`Successfully created EFS MountTarget ${logicalId}: ${mountTargetId}`);
@@ -43775,6 +44608,7 @@ var FSxFileSystemProvider = class {
43775
44608
  throw markNonRetryable(new ProvisioningError(safeMsg`FSx answered the create of ${logicalId} with file system ${returnedId}, which ${createdAt === void 0 ? "carries no creation time" : "predates this create"}: the create token cdkd derives for this stack's file system is held by it, and it is NOT recorded in cdkd state. It is not one this deploy, or an interrupted earlier deploy of this stack, made: it was created under this token by an earlier cdkd version, or a completed deploy recorded it and cdkd later let go of it, and it may hold data, so cdkd neither records nor deletes it. Inspect it with: ${inspect} --file-system-ids ${returnedId} -- then import it into this stack, or delete it once you are sure its data is not needed, and deploy again.`, resourceType, logicalId));
43776
44609
  }
43777
44610
  fileSystemId = returnedId;
44611
+ reportCreatedResource(logicalId, resourceType, returnedId);
43778
44612
  const fs = await this.waitForFileSystemAvailable(fileSystemId, logicalId, resourceType, context?.maskSecrets);
43779
44613
  this.logger.debug(`Successfully created FSx FileSystem ${logicalId}: ${fileSystemId}`);
43780
44614
  this.createTokenFirstSentAt.delete(clientRequestToken);
@@ -43786,6 +44620,7 @@ var FSxFileSystemProvider = class {
43786
44620
  let survivorId;
43787
44621
  if (fileSystemId !== void 0) try {
43788
44622
  await this.getClient().send(new DeleteFileSystemCommand$1({ FileSystemId: fileSystemId }));
44623
+ retractCreatedResource(logicalId, resourceType, fileSystemId);
43789
44624
  this.createTokenFirstSentAt.delete(clientRequestToken);
43790
44625
  this.logger.warn(`Rolled back partially-created FSx FileSystem ${fileSystemId}`);
43791
44626
  } catch (cleanupError) {
@@ -44712,11 +45547,11 @@ var FSxFileSystemProvider = class {
44712
45547
 
44713
45548
  //#endregion
44714
45549
  //#region src/provisioning/emr-configuration.ts
44715
- function isRecord(value) {
45550
+ function isRecord$1(value) {
44716
45551
  return typeof value === "object" && value !== null && !Array.isArray(value);
44717
45552
  }
44718
45553
  function toSdkConfiguration(raw) {
44719
- if (!isRecord(raw)) return raw;
45554
+ if (!isRecord$1(raw)) return raw;
44720
45555
  const { ConfigurationProperties, Configurations, ...rest } = raw;
44721
45556
  return {
44722
45557
  ...rest,
@@ -44733,9 +45568,9 @@ function toSdkStepConfigs(value) {
44733
45568
  if (value === void 0) return void 0;
44734
45569
  if (!Array.isArray(value)) return value;
44735
45570
  return value.map((step) => {
44736
- if (!isRecord(step)) return step;
45571
+ if (!isRecord$1(step)) return step;
44737
45572
  const hadoopJarStep = step["HadoopJarStep"];
44738
- if (!isRecord(hadoopJarStep) || hadoopJarStep["StepProperties"] === void 0) return step;
45573
+ if (!isRecord$1(hadoopJarStep) || hadoopJarStep["StepProperties"] === void 0) return step;
44739
45574
  const { StepProperties, ...restHadoopJarStep } = hadoopJarStep;
44740
45575
  return {
44741
45576
  ...step,
@@ -44750,7 +45585,7 @@ function toSdkInstanceTypeConfigs(value) {
44750
45585
  if (value === void 0) return void 0;
44751
45586
  if (!Array.isArray(value)) return value;
44752
45587
  return value.map((instanceTypeConfig) => {
44753
- if (!isRecord(instanceTypeConfig) || instanceTypeConfig["Configurations"] === void 0) return instanceTypeConfig;
45588
+ if (!isRecord$1(instanceTypeConfig) || instanceTypeConfig["Configurations"] === void 0) return instanceTypeConfig;
44754
45589
  return {
44755
45590
  ...instanceTypeConfig,
44756
45591
  Configurations: toSdkConfigurations(instanceTypeConfig["Configurations"])
@@ -44897,6 +45732,7 @@ var EMRInstanceGroupConfigProvider = class {
44897
45732
  const groupId = response.InstanceGroupIds?.[0];
44898
45733
  if (groupId) groupsCreatedByThisProcess.add(groupId);
44899
45734
  createdGroupId = groupId;
45735
+ reportCreatedResource(logicalId, resourceType, groupId ?? "");
44900
45736
  const replayWindow = replayedSendWindow(response, attemptStartMs);
44901
45737
  if (replayWindow !== void 0 && group.InstanceRole === "TASK") await reportOrphans(replayWindow);
44902
45738
  if (!groupId) throw new ProvisioningError(`EMR AddInstanceGroups for ${logicalId} returned no instance group id`, resourceType, logicalId);
@@ -44923,6 +45759,7 @@ var EMRInstanceGroupConfigProvider = class {
44923
45759
  }]
44924
45760
  }));
44925
45761
  log.warn(safeMsg`Scaled partially-created EMR TASK instance group ${logicalId} (${leftBehindGroupId}) to 0; EMR has no delete API, so the empty group stays on cluster ${log.value(jobFlowId)} until it terminates`);
45762
+ retractCreatedResource(logicalId, resourceType, leftBehindGroupId);
44926
45763
  leftBehindGroupId = void 0;
44927
45764
  } catch (cleanupError) {
44928
45765
  const cleanupHead = safeMsg`Failed to scale partially-created EMR TASK instance group ${logicalId} (${leftBehindGroupId}) to 0: `;
@@ -45272,6 +46109,7 @@ var EMRInstanceFleetConfigProvider = class {
45272
46109
  const fleetId = response.InstanceFleetId;
45273
46110
  if (fleetId) fleetsCreatedByThisProcess.add(fleetId);
45274
46111
  createdFleetId = fleetId;
46112
+ reportCreatedResource(logicalId, resourceType, fleetId ?? "");
45275
46113
  const replayWindow = replayedSendWindow(response, attemptStartMs);
45276
46114
  if (replayWindow !== void 0 && fleet.InstanceFleetType === "TASK") await reportOrphans(replayWindow);
45277
46115
  if (!fleetId) throw new ProvisioningError(`EMR AddInstanceFleet for ${logicalId} returned no instance fleet id`, resourceType, logicalId);
@@ -45763,6 +46601,7 @@ var EMRClusterProvider = class {
45763
46601
  }
45764
46602
  clusterId = response.JobFlowId;
45765
46603
  if (clusterId) clustersCreatedByThisProcess.add(clusterId);
46604
+ reportCreatedResource(logicalId, resourceType, clusterId ?? "");
45766
46605
  const replayWindow = replayedSendWindow(response, attemptStartMs);
45767
46606
  if (replayWindow !== void 0) await reportOrphans(replayWindow);
45768
46607
  if (!clusterId) throw new ProvisioningError(`EMR RunJobFlow for ${logicalId} returned no JobFlowId`, resourceType, logicalId);
@@ -45780,6 +46619,7 @@ var EMRClusterProvider = class {
45780
46619
  TerminationProtected: false
45781
46620
  }));
45782
46621
  await this.getClient().send(new TerminateJobFlowsCommand({ JobFlowIds: [clusterId] }));
46622
+ retractCreatedResource(logicalId, resourceType, clusterId);
45783
46623
  this.logger.warn(`Rolled back partially-created EMR Cluster ${clusterId}`);
45784
46624
  } catch (cleanupError) {
45785
46625
  survivorId = clusterId;
@@ -46739,6 +47579,7 @@ var FirehoseProvider = class {
46739
47579
  const response = await this.getCreateClient().send(new CreateDeliveryStreamCommand(input));
46740
47580
  const physicalId = deliveryStreamName || input.DeliveryStreamName || response.DeliveryStreamARN?.split("/").pop() || "";
46741
47581
  createdStreamName = physicalId;
47582
+ reportCreatedResource(logicalId, resourceType, physicalId);
46742
47583
  const arn = response.DeliveryStreamARN;
46743
47584
  this.logger.debug(`Successfully created Firehose delivery stream ${logicalId}: ${physicalId}`);
46744
47585
  await this.waitForActive(physicalId, logicalId, resourceType, maskerOrIdentity(context?.maskSecrets));
@@ -47634,7 +48475,7 @@ function readListBlock(raw, path) {
47634
48475
  };
47635
48476
  }
47636
48477
  }
47637
- function listOf(read) {
48478
+ function listOf$1(read) {
47638
48479
  return read.kind === "list" ? read.value : void 0;
47639
48480
  }
47640
48481
  function retainPreviousLists(desired, previous, keys, live) {
@@ -47805,6 +48646,7 @@ var CloudTrailProvider = class {
47805
48646
  trailCreated = true;
47806
48647
  const trailArn = result.TrailARN;
47807
48648
  createdTrailArn = result.TrailARN;
48649
+ if (createdTrailArn) reportCreatedResource(logicalId, resourceType, createdTrailArn);
47808
48650
  const basicForCreate = eventSelectors && eventSelectors.length > 0 ? eventSelectors : void 0;
47809
48651
  if (basicForCreate || advancedEventSelectors && advancedEventSelectors.length > 0) {
47810
48652
  this.logger.debug(`Setting event selectors for CloudTrail Trail ${logicalId}`);
@@ -47882,14 +48724,14 @@ var CloudTrailProvider = class {
47882
48724
  const advancedRead = readListBlock(properties["AdvancedEventSelectors"], "AWS::CloudTrail::Trail AdvancedEventSelectors");
47883
48725
  for (const read of [basicRead, advancedRead]) if (read.kind === "unusable") this.logger.warn(safeMsg`${read.detail} The trail's existing event selectors are left untouched for this update rather than reset to the AWS default.`);
47884
48726
  const selectorsUnusable = basicRead.kind === "unusable" || advancedRead.kind === "unusable";
47885
- const newEventSelectors = listOf(basicRead);
47886
- const newAdvancedSelectors = listOf(advancedRead);
48727
+ const newEventSelectors = listOf$1(basicRead);
48728
+ const newAdvancedSelectors = listOf$1(advancedRead);
47887
48729
  const oldEventSelectors = previousProperties["EventSelectors"];
47888
48730
  const oldAdvancedSelectors = previousProperties["AdvancedEventSelectors"];
47889
48731
  const hadAdvancedSelectors = Array.isArray(oldAdvancedSelectors) && oldAdvancedSelectors.length > 0;
47890
48732
  const aggregationRead = readListBlock(properties["AggregationConfigurations"], "AWS::CloudTrail::Trail AggregationConfigurations");
47891
48733
  if (aggregationRead.kind === "unusable") this.logger.warn(safeMsg`${aggregationRead.detail} The trail's existing aggregation configurations are left untouched for this update rather than cleared.`);
47892
- const newAggregation = listOf(aggregationRead);
48734
+ const newAggregation = listOf$1(aggregationRead);
47893
48735
  const aggregationChanged = aggregationRead.kind !== "unusable" && setKey(newAggregation) !== setKey(previousProperties["AggregationConfigurations"]);
47894
48736
  const aggregationCleared = aggregationChanged && nonEmptyListOf(aggregationRead) === void 0;
47895
48737
  const putAggregation = async () => {
@@ -48294,7 +49136,8 @@ var CodeBuildProvider = class {
48294
49136
  batchReportMode: cfnBuildBatchConfig["BatchReportMode"],
48295
49137
  restrictions: restrictions ? {
48296
49138
  maximumBuildsAllowed: restrictions["MaximumBuildsAllowed"],
48297
- computeTypesAllowed: restrictions["ComputeTypesAllowed"]
49139
+ computeTypesAllowed: restrictions["ComputeTypesAllowed"],
49140
+ fleetsAllowed: restrictions["FleetsAllowed"]
48298
49141
  } : void 0
48299
49142
  };
48300
49143
  }
@@ -48363,6 +49206,7 @@ var CodeBuildProvider = class {
48363
49206
  const projectName = result.project.name;
48364
49207
  const projectArn = result.project.arn;
48365
49208
  createdName = projectName;
49209
+ reportCreatedResource(logicalId, resourceType, createdName);
48366
49210
  if (visibilityRead.kind === "usable" && (visibilityRead.visibility !== DEFAULT_PROJECT_VISIBILITY || visibilityRead.resourceAccessRole !== void 0)) await this.applyVisibility(projectArn, visibilityRead);
48367
49211
  this.logger.debug(`Successfully created CodeBuild Project ${logicalId}: ${projectName}`);
48368
49212
  return {
@@ -48602,6 +49446,7 @@ var CodeBuildProvider = class {
48602
49446
  const r = {};
48603
49447
  if (project.buildBatchConfig.restrictions.maximumBuildsAllowed !== void 0) r["MaximumBuildsAllowed"] = project.buildBatchConfig.restrictions.maximumBuildsAllowed;
48604
49448
  if (project.buildBatchConfig.restrictions.computeTypesAllowed !== void 0) r["ComputeTypesAllowed"] = project.buildBatchConfig.restrictions.computeTypesAllowed;
49449
+ if (project.buildBatchConfig.restrictions.fleetsAllowed !== void 0) r["FleetsAllowed"] = project.buildBatchConfig.restrictions.fleetsAllowed;
48605
49450
  bbc["Restrictions"] = r;
48606
49451
  }
48607
49452
  if (project.buildBatchConfig.timeoutInMins !== void 0) bbc["TimeoutInMins"] = project.buildBatchConfig.timeoutInMins;
@@ -48784,6 +49629,7 @@ var CodeCommitRepositoryProvider = class {
48784
49629
  if (!metadata?.repositoryName) throw new Error("CreateRepository did not return repository metadata");
48785
49630
  const createdName = metadata.repositoryName;
48786
49631
  leftLiveName = createdName;
49632
+ reportCreatedResource(logicalId, resourceType, createdName);
48787
49633
  try {
48788
49634
  const code = properties["Code"];
48789
49635
  if (code) await this.seedInitialCommit(createdName, code);
@@ -48797,7 +49643,10 @@ var CodeCommitRepositoryProvider = class {
48797
49643
  }
48798
49644
  } catch (postCreateError) {
48799
49645
  this.logger.warn(`Post-create step failed for CodeCommit Repository ${logicalId}; deleting the just-created repository ${createdName} to avoid an orphan`);
48800
- if (await this.bestEffortDelete(createdName, maskerOrIdentity(context?.maskSecrets))) leftLiveName = void 0;
49646
+ if (await this.bestEffortDelete(createdName, maskerOrIdentity(context?.maskSecrets))) {
49647
+ retractCreatedResource(logicalId, resourceType, leftLiveName);
49648
+ leftLiveName = void 0;
49649
+ }
48801
49650
  throw markAuxiliaryFailure(postCreateError, logicalId);
48802
49651
  }
48803
49652
  this.logger.debug(`Successfully created CodeCommit Repository ${logicalId}: ${createdName}`);
@@ -49166,6 +50015,28 @@ var CodeCommitRepositoryProvider = class {
49166
50015
 
49167
50016
  //#endregion
49168
50017
  //#region src/provisioning/providers/dlm-lifecycle-policy-provider.ts
50018
+ const KEPT_ON_REMOVAL = [
50019
+ "Description",
50020
+ "ExecutionRoleArn",
50021
+ "State",
50022
+ "PolicyDetails",
50023
+ "CreateInterval",
50024
+ "RetainInterval",
50025
+ "CopyTags",
50026
+ "ExtendDeletion",
50027
+ "CrossRegionCopyTargets",
50028
+ "Exclusions"
50029
+ ];
50030
+ function keptRemovedNames(properties, previousProperties, context) {
50031
+ if (context?.desiredFromAwsReadback) return [];
50032
+ const removed = context?.removedProperties;
50033
+ return KEPT_ON_REMOVAL.filter((key) => previousProperties[key] != null && properties[key] == null && (removed === void 0 || removed.has(key) || properties[key] === null));
50034
+ }
50035
+ const keptOnRemovalWarning = (logicalId, names, caller) => {
50036
+ const one = names.length === 1;
50037
+ const subject = one ? "property" : "properties";
50038
+ return caller === "rollback" ? safeMsg`${logicalId} (AWS::DLM::LifecyclePolicy): ${subject} ${names.join(", ")} ${one ? "is" : "are"} absent from the state being restored; UpdateLifecyclePolicy keeps a field it is not sent, so the rollback leaves the value the failed deploy applied in place.` : safeMsg`${logicalId} (AWS::DLM::LifecyclePolicy): ${subject} ${names.join(", ")} ${one ? "was" : "were"} removed from the template and ${one ? "keeps its" : "keep their"} current AWS value, as CloudFormation leaves ${one ? "it" : "them"}; UpdateLifecyclePolicy keeps a field it is not sent. Declare the intended value explicitly to change it.`;
50039
+ };
49169
50040
  const createLifecyclePolicyLatch = new AmbiguousCreateLatch("dlm:CreateLifecyclePolicy");
49170
50041
  const policiesCreatedByThisProcess = new RecentIdSet();
49171
50042
  var DLMLifecyclePolicyProvider = class {
@@ -49195,6 +50066,11 @@ var DLMLifecyclePolicyProvider = class {
49195
50066
  "CrossRegionCopyTargets",
49196
50067
  "Exclusions"
49197
50068
  ])]]);
50069
+ removalHandledInUpdate = /* @__PURE__ */ new Map([["AWS::DLM::LifecyclePolicy", /* @__PURE__ */ new Set([
50070
+ ...KEPT_ON_REMOVAL,
50071
+ "Tags",
50072
+ "DefaultPolicy"
50073
+ ])]]);
49198
50074
  getClient() {
49199
50075
  if (!this.client) this.client = new DLMClient({
49200
50076
  ...ambientClientDefaults(),
@@ -49319,6 +50195,8 @@ var DLMLifecyclePolicyProvider = class {
49319
50195
  ...properties["CreateInterval"] != null && { CreateInterval: Number(properties["CreateInterval"]) },
49320
50196
  ...properties["RetainInterval"] != null && { RetainInterval: Number(properties["RetainInterval"]) }
49321
50197
  }));
50198
+ const keptNames = keptRemovedNames(properties, previousProperties, context);
50199
+ if (keptNames.length > 0) this.logger.warn(keptOnRemovalWarning(logicalId, keptNames, context?.replayingState ? "rollback" : "deploy"));
49322
50200
  const plan = planTagDiff(previousProperties["Tags"], properties["Tags"]);
49323
50201
  const tagWarning = tagPlanWarning(plan, resourceType, logicalId);
49324
50202
  if (tagWarning !== void 0) this.logger.warn(tagWarning);
@@ -49657,6 +50535,370 @@ function notEmptyRefusalMessage(bucketName, maskSecrets) {
49657
50535
  const remedy = command ? `Delete all objects first (e.g. ${command}) and destroy again.` : secretDerived ? "Delete all objects first, via the console, and destroy again: the bucket name is derived from a secret, so cdkd does not print it." : "Delete all objects first, via the console, and destroy again: the bucket name cdkd recorded cannot be reproduced safely on a command line, so any command shown here could act on a different bucket or run part of the name as shell.";
49658
50536
  return mask(`bucket ${displayIdent(mask(bucketName))} is not empty. Matching CloudFormation, cdkd does not delete a non-empty directory bucket without an explicit opt-in. ${remedy}`);
49659
50537
  }
50538
+ function isRecord(value) {
50539
+ return typeof value === "object" && value !== null && !Array.isArray(value);
50540
+ }
50541
+ function itemId(item) {
50542
+ if (!isRecord(item)) return void 0;
50543
+ const id = item["Id"];
50544
+ return typeof id === "string" && id !== "" ? id : void 0;
50545
+ }
50546
+ function canonicalJson(value) {
50547
+ const sorted = (v) => {
50548
+ if (Array.isArray(v)) return v.map(sorted);
50549
+ if (isRecord(v)) return Object.fromEntries(Object.keys(v).sort().map((k) => [k, sorted(v[k])]));
50550
+ return v;
50551
+ };
50552
+ return JSON.stringify(sorted(value)) ?? "undefined";
50553
+ }
50554
+ function sameJson(a, b) {
50555
+ return canonicalJson(a) === canonicalJson(b);
50556
+ }
50557
+ function optionalString(value, path, refuse) {
50558
+ if (value === void 0 || value === null) return void 0;
50559
+ if (typeof value !== "string") refuse(path, "a string");
50560
+ return value;
50561
+ }
50562
+ function optionalBoolean(value, path, refuse) {
50563
+ if (value === void 0 || value === null) return void 0;
50564
+ if (typeof value === "boolean") return value;
50565
+ if (value === "true") return true;
50566
+ if (value === "false") return false;
50567
+ return refuse(path, "a boolean");
50568
+ }
50569
+ function optionalInteger(value, path, refuse) {
50570
+ if (value === void 0 || value === null) return void 0;
50571
+ if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) return value;
50572
+ if (typeof value === "string" && /^[0-9]{1,15}$/.test(value)) return Number(value);
50573
+ return refuse(path, "a non-negative integer");
50574
+ }
50575
+ function listOf(value, path, refuse) {
50576
+ if (!Array.isArray(value)) refuse(path, "a list");
50577
+ return value;
50578
+ }
50579
+ function encryptionToSdk(value, refuse) {
50580
+ if (value === void 0 || value === null) return void 0;
50581
+ if (!isRecord(value)) refuse("BucketEncryption", "an object");
50582
+ const rules = listOf(value["ServerSideEncryptionConfiguration"], "BucketEncryption.ServerSideEncryptionConfiguration", refuse);
50583
+ if (rules.length === 0) return void 0;
50584
+ return { Rules: rules.map((rule, i) => {
50585
+ const path = `BucketEncryption.ServerSideEncryptionConfiguration[${i}]`;
50586
+ if (!isRecord(rule)) refuse(path, "an object");
50587
+ const out = {};
50588
+ const byDefault = rule["ServerSideEncryptionByDefault"];
50589
+ if (byDefault !== void 0 && byDefault !== null) {
50590
+ if (!isRecord(byDefault)) refuse(`${path}.ServerSideEncryptionByDefault`, "an object");
50591
+ const algorithm = optionalString(byDefault["SSEAlgorithm"], `${path}.ServerSideEncryptionByDefault.SSEAlgorithm`, refuse);
50592
+ const keyId = optionalString(byDefault["KMSMasterKeyID"], `${path}.ServerSideEncryptionByDefault.KMSMasterKeyID`, refuse);
50593
+ out.ApplyServerSideEncryptionByDefault = {
50594
+ SSEAlgorithm: algorithm,
50595
+ ...keyId !== void 0 ? { KMSMasterKeyID: keyId } : {}
50596
+ };
50597
+ }
50598
+ const bucketKey = optionalBoolean(rule["BucketKeyEnabled"], `${path}.BucketKeyEnabled`, refuse);
50599
+ if (bucketKey !== void 0) out.BucketKeyEnabled = bucketKey;
50600
+ return out;
50601
+ }) };
50602
+ }
50603
+ function lifecycleFilterToSdk(rule, path, refuse) {
50604
+ const prefix = optionalString(rule["Prefix"], `${path}.Prefix`, refuse);
50605
+ const greater = optionalInteger(rule["ObjectSizeGreaterThan"], `${path}.ObjectSizeGreaterThan`, refuse);
50606
+ const less = optionalInteger(rule["ObjectSizeLessThan"], `${path}.ObjectSizeLessThan`, refuse);
50607
+ const members = {
50608
+ ...prefix !== void 0 ? { Prefix: prefix } : {},
50609
+ ...greater !== void 0 ? { ObjectSizeGreaterThan: greater } : {},
50610
+ ...less !== void 0 ? { ObjectSizeLessThan: less } : {}
50611
+ };
50612
+ const count = Object.keys(members).length;
50613
+ if (count === 0) return { Prefix: "" };
50614
+ if (count === 1) return members;
50615
+ return { And: members };
50616
+ }
50617
+ function lifecycleToSdk(value, refuse) {
50618
+ if (value === void 0 || value === null) return void 0;
50619
+ if (!isRecord(value)) refuse("LifecycleConfiguration", "an object");
50620
+ const rules = listOf(value["Rules"], "LifecycleConfiguration.Rules", refuse);
50621
+ if (rules.length === 0) return void 0;
50622
+ return { Rules: rules.map((rule, i) => {
50623
+ const path = `LifecycleConfiguration.Rules[${i}]`;
50624
+ if (!isRecord(rule)) refuse(path, "an object");
50625
+ const id = optionalString(rule["Id"], `${path}.Id`, refuse);
50626
+ const status = optionalString(rule["Status"], `${path}.Status`, refuse);
50627
+ const days = optionalInteger(rule["ExpirationInDays"], `${path}.ExpirationInDays`, refuse);
50628
+ const out = {
50629
+ ...id !== void 0 ? { ID: id } : {},
50630
+ Status: status,
50631
+ Filter: lifecycleFilterToSdk(rule, path, refuse)
50632
+ };
50633
+ if (days !== void 0) out.Expiration = { Days: days };
50634
+ const abort = rule["AbortIncompleteMultipartUpload"];
50635
+ if (abort !== void 0 && abort !== null) {
50636
+ if (!isRecord(abort)) refuse(`${path}.AbortIncompleteMultipartUpload`, "an object");
50637
+ const after = optionalInteger(abort["DaysAfterInitiation"], `${path}.AbortIncompleteMultipartUpload.DaysAfterInitiation`, refuse);
50638
+ out.AbortIncompleteMultipartUpload = after !== void 0 ? { DaysAfterInitiation: after } : {};
50639
+ }
50640
+ return out;
50641
+ }) };
50642
+ }
50643
+ function keyedItems(value, property, refuse) {
50644
+ const out = /* @__PURE__ */ new Map();
50645
+ if (value === void 0 || value === null) return out;
50646
+ listOf(value, property, refuse).forEach((item, i) => {
50647
+ if (!isRecord(item)) refuse(`${property}[${i}]`, "an object");
50648
+ const id = itemId(item);
50649
+ if (id === void 0) refuse(`${property}[${i}].Id`, "a non-empty string");
50650
+ if (out.has(id)) refuse(`${property}[${i}].Id`, "an Id no other item of the list carries");
50651
+ out.set(id, item);
50652
+ });
50653
+ return out;
50654
+ }
50655
+ function recordedItems(value) {
50656
+ const out = /* @__PURE__ */ new Map();
50657
+ if (!Array.isArray(value)) return out;
50658
+ for (const item of value) {
50659
+ const id = itemId(item);
50660
+ if (id !== void 0 && !out.has(id)) out.set(id, item);
50661
+ }
50662
+ return out;
50663
+ }
50664
+ function metricsItemToSdk(id, item, path, refuse) {
50665
+ const prefix = optionalString(item["Prefix"], `${path}.Prefix`, refuse);
50666
+ const accessPoint = optionalString(item["AccessPointArn"], `${path}.AccessPointArn`, refuse);
50667
+ if (prefix !== void 0 && accessPoint !== void 0) return {
50668
+ Id: id,
50669
+ Filter: { And: {
50670
+ Prefix: prefix,
50671
+ AccessPointArn: accessPoint
50672
+ } }
50673
+ };
50674
+ if (prefix !== void 0) return {
50675
+ Id: id,
50676
+ Filter: { Prefix: prefix }
50677
+ };
50678
+ if (accessPoint !== void 0) return {
50679
+ Id: id,
50680
+ Filter: { AccessPointArn: accessPoint }
50681
+ };
50682
+ return { Id: id };
50683
+ }
50684
+ function inventoryItemToSdk(id, item, path, refuse) {
50685
+ const destination = item["Destination"];
50686
+ if (!isRecord(destination)) refuse(`${path}.Destination`, "an object");
50687
+ const bucketArn = optionalString(destination["BucketArn"], `${path}.Destination.BucketArn`, refuse);
50688
+ const accountId = optionalString(destination["BucketAccountId"], `${path}.Destination.BucketAccountId`, refuse);
50689
+ const format = optionalString(destination["Format"], `${path}.Destination.Format`, refuse);
50690
+ const destPrefix = optionalString(destination["Prefix"], `${path}.Destination.Prefix`, refuse);
50691
+ const enabled = optionalBoolean(item["Enabled"], `${path}.Enabled`, refuse);
50692
+ const versions = optionalString(item["IncludedObjectVersions"], `${path}.IncludedObjectVersions`, refuse);
50693
+ const prefix = optionalString(item["Prefix"], `${path}.Prefix`, refuse);
50694
+ const frequency = optionalString(item["ScheduleFrequency"], `${path}.ScheduleFrequency`, refuse);
50695
+ const fields = item["OptionalFields"];
50696
+ let optionalFields;
50697
+ if (fields !== void 0 && fields !== null) optionalFields = listOf(fields, `${path}.OptionalFields`, refuse).map((f, j) => {
50698
+ if (typeof f !== "string") refuse(`${path}.OptionalFields[${j}]`, "a string");
50699
+ return f;
50700
+ });
50701
+ return {
50702
+ Id: id,
50703
+ Destination: { S3BucketDestination: {
50704
+ Bucket: bucketArn,
50705
+ Format: format,
50706
+ ...accountId !== void 0 ? { AccountId: accountId } : {},
50707
+ ...destPrefix !== void 0 ? { Prefix: destPrefix } : {}
50708
+ } },
50709
+ IsEnabled: enabled,
50710
+ IncludedObjectVersions: versions,
50711
+ Schedule: { Frequency: frequency },
50712
+ ...prefix !== void 0 ? { Filter: { Prefix: prefix } } : {},
50713
+ ...optionalFields !== void 0 ? { OptionalFields: optionalFields } : {}
50714
+ };
50715
+ }
50716
+ function desiredConfigurations(properties, refuse, onUnusable) {
50717
+ const warnOut = (error, what) => onUnusable?.(`${error.message.replace(/ — the resource was not created$/, "")}; replaying a recorded state, so the bucket is created without ${what}`);
50718
+ const build = (fallback, what, run) => {
50719
+ if (!onUnusable) return run();
50720
+ try {
50721
+ return run();
50722
+ } catch (error) {
50723
+ if (!(error instanceof ProvisioningError)) throw error;
50724
+ warnOut(error, what);
50725
+ return fallback;
50726
+ }
50727
+ };
50728
+ const buildList = (property, toSdk) => {
50729
+ if (!onUnusable) {
50730
+ const out = /* @__PURE__ */ new Map();
50731
+ let i = 0;
50732
+ for (const [id, item] of keyedItems(properties[property], property, refuse)) out.set(id, toSdk(id, item, `${property}[${i++}]`));
50733
+ return out;
50734
+ }
50735
+ const out = /* @__PURE__ */ new Map();
50736
+ const value = properties[property];
50737
+ if (value === void 0 || value === null) return out;
50738
+ build(void 0, `any ${property} item`, () => listOf(value, property, refuse))?.forEach((item, i) => {
50739
+ const path = `${property}[${i}]`;
50740
+ build(void 0, `${path}`, () => {
50741
+ if (!isRecord(item)) refuse(path, "an object");
50742
+ const id = itemId(item);
50743
+ if (id === void 0) refuse(`${path}.Id`, "a non-empty string");
50744
+ if (out.has(id)) refuse(`${path}.Id`, "an Id no other item of the list carries");
50745
+ out.set(id, toSdk(id, item, path));
50746
+ });
50747
+ });
50748
+ return out;
50749
+ };
50750
+ const metrics = buildList("MetricsConfigurations", (id, item, path) => metricsItemToSdk(id, item, path, refuse));
50751
+ const inventory = buildList("InventoryConfigurations", (id, item, path) => inventoryItemToSdk(id, item, path, refuse));
50752
+ return {
50753
+ encryption: build(void 0, "its BucketEncryption", () => encryptionToSdk(properties["BucketEncryption"], refuse)),
50754
+ lifecycle: build(void 0, "its LifecycleConfiguration", () => lifecycleToSdk(properties["LifecycleConfiguration"], refuse)),
50755
+ metrics,
50756
+ inventory
50757
+ };
50758
+ }
50759
+ function configurationRefuser(resourceType, logicalId, physicalId) {
50760
+ const updating = physicalId !== void 0;
50761
+ return (path, expected) => {
50762
+ throw markNonRetryable(new ProvisioningError(`${updating ? "desired " : ""}${path} of ${resourceType} ${logicalId} is not ${expected} — the resource was not ${updating ? "updated" : "created"}`, resourceType, logicalId, physicalId));
50763
+ };
50764
+ }
50765
+ function encryptionFromSdk(rules) {
50766
+ return { ServerSideEncryptionConfiguration: rules.map((rule) => {
50767
+ const out = {};
50768
+ const byDefault = rule.ApplyServerSideEncryptionByDefault;
50769
+ if (byDefault) out["ServerSideEncryptionByDefault"] = {
50770
+ ...byDefault.SSEAlgorithm !== void 0 ? { SSEAlgorithm: byDefault.SSEAlgorithm } : {},
50771
+ ...byDefault.KMSMasterKeyID !== void 0 ? { KMSMasterKeyID: byDefault.KMSMasterKeyID } : {}
50772
+ };
50773
+ if (rule.BucketKeyEnabled !== void 0) out["BucketKeyEnabled"] = rule.BucketKeyEnabled;
50774
+ return out;
50775
+ }) };
50776
+ }
50777
+ function lifecycleFromSdk(rules) {
50778
+ return { Rules: rules.map((rule) => {
50779
+ const out = {};
50780
+ if (rule.ID !== void 0) out["Id"] = rule.ID;
50781
+ if (rule.Status !== void 0) out["Status"] = rule.Status;
50782
+ const scope = {
50783
+ ...rule.Filter?.And ?? {},
50784
+ ...rule.Filter ?? {}
50785
+ };
50786
+ const prefix = scope["Prefix"] ?? rule.Prefix;
50787
+ if (prefix !== void 0 && prefix !== "") out["Prefix"] = prefix;
50788
+ if (typeof scope["ObjectSizeGreaterThan"] === "number") out["ObjectSizeGreaterThan"] = String(scope["ObjectSizeGreaterThan"]);
50789
+ if (typeof scope["ObjectSizeLessThan"] === "number") out["ObjectSizeLessThan"] = String(scope["ObjectSizeLessThan"]);
50790
+ if (rule.Expiration?.Days !== void 0) out["ExpirationInDays"] = rule.Expiration.Days;
50791
+ if (rule.AbortIncompleteMultipartUpload) {
50792
+ const after = rule.AbortIncompleteMultipartUpload.DaysAfterInitiation;
50793
+ out["AbortIncompleteMultipartUpload"] = after !== void 0 ? { DaysAfterInitiation: after } : {};
50794
+ }
50795
+ return out;
50796
+ }) };
50797
+ }
50798
+ function metricsFromSdk(item) {
50799
+ const filter = item.Filter;
50800
+ const scope = filter?.And ?? filter ?? {};
50801
+ return {
50802
+ Id: item.Id,
50803
+ ...scope.Prefix !== void 0 ? { Prefix: scope.Prefix } : {},
50804
+ ...scope.AccessPointArn !== void 0 ? { AccessPointArn: scope.AccessPointArn } : {}
50805
+ };
50806
+ }
50807
+ function inventoryFromSdk(item) {
50808
+ const dest = item.Destination?.S3BucketDestination;
50809
+ return {
50810
+ Id: item.Id,
50811
+ Destination: {
50812
+ ...dest?.Bucket !== void 0 ? { BucketArn: dest.Bucket } : {},
50813
+ ...dest?.AccountId !== void 0 ? { BucketAccountId: dest.AccountId } : {},
50814
+ ...dest?.Format !== void 0 ? { Format: dest.Format } : {},
50815
+ ...dest?.Prefix !== void 0 ? { Prefix: dest.Prefix } : {}
50816
+ },
50817
+ ...item.IsEnabled !== void 0 ? { Enabled: item.IsEnabled } : {},
50818
+ ...item.IncludedObjectVersions !== void 0 ? { IncludedObjectVersions: item.IncludedObjectVersions } : {},
50819
+ ...item.Filter?.Prefix !== void 0 ? { Prefix: item.Filter.Prefix } : {},
50820
+ ...item.Schedule?.Frequency !== void 0 ? { ScheduleFrequency: item.Schedule.Frequency } : {},
50821
+ ...item.OptionalFields !== void 0 && item.OptionalFields.length > 0 ? { OptionalFields: [...item.OptionalFields] } : {}
50822
+ };
50823
+ }
50824
+ function impliedBucketKey(algorithm) {
50825
+ if (algorithm === "AES256") return false;
50826
+ if (algorithm === "aws:kms") return true;
50827
+ }
50828
+ function canonicalizeConfigurationsForDrift(properties) {
50829
+ let out = properties;
50830
+ const encryption = properties["BucketEncryption"];
50831
+ if (isRecord(encryption) && Array.isArray(encryption["ServerSideEncryptionConfiguration"])) {
50832
+ let changed = false;
50833
+ const rules = encryption["ServerSideEncryptionConfiguration"].map((rule) => {
50834
+ if (!isRecord(rule) || !("BucketKeyEnabled" in rule)) return rule;
50835
+ const byDefault = rule["ServerSideEncryptionByDefault"];
50836
+ const implied = impliedBucketKey(isRecord(byDefault) ? byDefault["SSEAlgorithm"] : void 0);
50837
+ const declared = rule["BucketKeyEnabled"];
50838
+ if (implied === void 0 || (declared === "true" ? true : declared === "false" ? false : declared) !== implied) return rule;
50839
+ changed = true;
50840
+ const { BucketKeyEnabled: _dropped, ...rest } = rule;
50841
+ return rest;
50842
+ });
50843
+ if (changed) out = {
50844
+ ...out,
50845
+ BucketEncryption: {
50846
+ ...encryption,
50847
+ ServerSideEncryptionConfiguration: rules
50848
+ }
50849
+ };
50850
+ }
50851
+ const lifecycle = properties["LifecycleConfiguration"];
50852
+ if (isRecord(lifecycle) && Array.isArray(lifecycle["Rules"])) {
50853
+ let changed = false;
50854
+ const rules = lifecycle["Rules"].map((rule) => {
50855
+ if (!isRecord(rule) || rule["Prefix"] !== "") return rule;
50856
+ changed = true;
50857
+ const { Prefix: _dropped, ...rest } = rule;
50858
+ return rest;
50859
+ });
50860
+ if (changed) out = {
50861
+ ...out,
50862
+ LifecycleConfiguration: {
50863
+ ...lifecycle,
50864
+ Rules: rules
50865
+ }
50866
+ };
50867
+ }
50868
+ return out;
50869
+ }
50870
+ function liveMatchesDesired(name, desired, live) {
50871
+ const d = canonicalizeConfigurationsForDrift({ [name]: desired[name] })[name];
50872
+ let l = canonicalizeConfigurationsForDrift({ [name]: live[name] })[name];
50873
+ if (name === "LifecycleConfiguration" && isRecord(d) && isRecord(l)) {
50874
+ const dRules = Array.isArray(d["Rules"]) ? d["Rules"] : [];
50875
+ const lRules = Array.isArray(l["Rules"]) ? l["Rules"] : [];
50876
+ l = {
50877
+ ...l,
50878
+ Rules: lRules.map((rule, i) => {
50879
+ const declared = dRules[i];
50880
+ if (!isRecord(rule) || !isRecord(declared) || "Id" in declared) return rule;
50881
+ const { Id: _assigned, ...rest } = rule;
50882
+ return rest;
50883
+ })
50884
+ };
50885
+ }
50886
+ if (name === "MetricsConfigurations" || name === "InventoryConfigurations") {
50887
+ const withSortedFields = (item) => isRecord(item) && Array.isArray(item["OptionalFields"]) ? {
50888
+ ...item,
50889
+ OptionalFields: [...item["OptionalFields"]].map(String).sort()
50890
+ } : item;
50891
+ const asSet = (v) => Array.isArray(v) ? v.map((item) => canonicalJson(withSortedFields(item))).sort() : [canonicalJson(v)];
50892
+ return sameJson(asSet(d), asSet(l));
50893
+ }
50894
+ return sameJson(d, l);
50895
+ }
50896
+ const CONFIGURATION_READ_ACTIONS = {
50897
+ BucketEncryption: "s3express:GetEncryptionConfiguration",
50898
+ LifecycleConfiguration: "s3express:GetLifecycleConfiguration",
50899
+ MetricsConfigurations: "s3express:GetMetricsConfiguration",
50900
+ InventoryConfigurations: "s3express:GetInventoryConfiguration"
50901
+ };
49660
50902
  var S3DirectoryBucketProvider = class {
49661
50903
  s3Client;
49662
50904
  createClient;
@@ -49674,8 +50916,44 @@ var S3DirectoryBucketProvider = class {
49674
50916
  "DataRedundancy",
49675
50917
  "LocationName",
49676
50918
  "BucketName",
49677
- "Tags"
50919
+ "Tags",
50920
+ "BucketEncryption",
50921
+ "LifecycleConfiguration",
50922
+ "MetricsConfigurations",
50923
+ "InventoryConfigurations"
49678
50924
  ])]]);
50925
+ async readUnsettled(physicalId, _logicalId, _resourceType, names, recordedProperties, desiredProperties) {
50926
+ const live = await this.readState(physicalId);
50927
+ if (live === RESOURCE_NOT_FOUND) return live;
50928
+ const desired = desiredProperties ?? recordedProperties ?? {};
50929
+ const out = {};
50930
+ for (const name of names) {
50931
+ if (!Object.hasOwn(live, name)) continue;
50932
+ if (desired[name] !== void 0 && liveMatchesDesired(name, desired, live)) {
50933
+ out[name] = desired[name];
50934
+ continue;
50935
+ }
50936
+ if (name === "MetricsConfigurations" || name === "InventoryConfigurations") {
50937
+ const named = /* @__PURE__ */ new Set([...recordedItems(recordedProperties?.[name]).keys(), ...recordedItems(desiredProperties?.[name]).keys()]);
50938
+ const items = live[name];
50939
+ out[name] = Array.isArray(items) ? items.filter((item) => {
50940
+ const id = itemId(item);
50941
+ return id !== void 0 && named.has(id);
50942
+ }) : items;
50943
+ continue;
50944
+ }
50945
+ out[name] = live[name];
50946
+ }
50947
+ return out;
50948
+ }
50949
+ getDriftUnorderedPaths(resourceType) {
50950
+ if (resourceType !== "AWS::S3Express::DirectoryBucket") return [];
50951
+ return ["MetricsConfigurations", "InventoryConfigurations"];
50952
+ }
50953
+ canonicalizeDriftProperties(resourceType, properties) {
50954
+ if (resourceType !== "AWS::S3Express::DirectoryBucket") return properties;
50955
+ return canonicalizeConfigurationsForDrift(properties);
50956
+ }
49679
50957
  ec2Client;
49680
50958
  s3ControlClient;
49681
50959
  providerRegion = ambientRegion();
@@ -49739,6 +51017,7 @@ var S3DirectoryBucketProvider = class {
49739
51017
  async create(logicalId, resourceType, properties, context) {
49740
51018
  this.logger.debug(`Creating S3 Express Directory Bucket ${logicalId}`);
49741
51019
  const desiredTags = refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId);
51020
+ const configs = desiredConfigurations(properties, configurationRefuser(resourceType, logicalId), replayWarn(this.logger, context).onUnusable);
49742
51021
  const dataRedundancy = requireConfigString(properties["DataRedundancy"], "SingleAvailabilityZone", "AWS::S3Express::DirectoryBucket DataRedundancy", replayWarn(this.logger, context));
49743
51022
  const azName = properties["LocationName"]?.replace(/--x-s3$/, "") || "us-east-1a";
49744
51023
  const azId = await this.getAzId(azName);
@@ -49746,6 +51025,7 @@ var S3DirectoryBucketProvider = class {
49746
51025
  if (!bucketName) bucketName = generatedNameFor("AWS::S3Express::DirectoryBucket", logicalId, { zoneId: azId });
49747
51026
  const tags = desiredTags;
49748
51027
  const includeTags = tags.length > 0;
51028
+ let bucketCreated = false;
49749
51029
  try {
49750
51030
  const attributes = await this.buildAttributes(bucketName);
49751
51031
  await (await this.getCreateClient()).send(new CreateBucketCommand({
@@ -49762,19 +51042,112 @@ var S3DirectoryBucketProvider = class {
49762
51042
  ...includeTags ? { Tags: tags } : {}
49763
51043
  }
49764
51044
  }));
51045
+ bucketCreated = true;
49765
51046
  this.logger.debug(`Created S3 Express Directory Bucket: ${bucketName}`);
51047
+ await this.applyConfigurations(bucketName, configs, properties, {}, true);
49766
51048
  return {
49767
51049
  physicalId: bucketName,
49768
51050
  attributes
49769
51051
  };
49770
51052
  } catch (error) {
49771
51053
  const cause = error instanceof Error ? error : void 0;
49772
- throw this.wrapCaughtError(maskerOrIdentity(context?.maskSecrets), error, (text) => new ProvisioningError(`Failed to create S3 Express Directory Bucket ${logicalId}: ${text}`, resourceType, logicalId, bucketName, cause));
51054
+ const thrown = this.wrapCaughtError(maskerOrIdentity(context?.maskSecrets), error, (text) => new ProvisioningError(`Failed to create S3 Express Directory Bucket ${logicalId}: ${text}`, resourceType, logicalId, bucketName, cause));
51055
+ if (bucketCreated) markCreatedBeforeFailure(thrown, logicalId, resourceType, bucketName);
51056
+ throw thrown;
51057
+ }
51058
+ }
51059
+ async applyConfigurations(bucketName, desired, desiredRaw, previousRaw, creating, markUnsettled = () => {}) {
51060
+ const changed = (desiredValue, previousValue) => creating || !sameJson(desiredValue, previousValue);
51061
+ const holds = (value, toSdk) => {
51062
+ try {
51063
+ return toSdk(value, () => {
51064
+ throw new Error("malformed");
51065
+ }) !== void 0;
51066
+ } catch {
51067
+ return true;
51068
+ }
51069
+ };
51070
+ if (changed(desiredRaw["BucketEncryption"], previousRaw["BucketEncryption"])) {
51071
+ if (desired.encryption) {
51072
+ markUnsettled("BucketEncryption");
51073
+ await this.s3Client.send(new PutBucketEncryptionCommand({
51074
+ Bucket: bucketName,
51075
+ ServerSideEncryptionConfiguration: desired.encryption
51076
+ }));
51077
+ } else if (!creating && holds(previousRaw["BucketEncryption"], encryptionToSdk)) {
51078
+ markUnsettled("BucketEncryption");
51079
+ await this.s3Client.send(new DeleteBucketEncryptionCommand({ Bucket: bucketName }));
51080
+ this.logger.debug("Sent DeleteBucketEncryption: the default encryption is back to SSE-S3");
51081
+ }
51082
+ }
51083
+ if (changed(desiredRaw["LifecycleConfiguration"], previousRaw["LifecycleConfiguration"])) {
51084
+ if (desired.lifecycle) {
51085
+ markUnsettled("LifecycleConfiguration");
51086
+ await this.s3Client.send(new PutBucketLifecycleConfigurationCommand({
51087
+ Bucket: bucketName,
51088
+ LifecycleConfiguration: desired.lifecycle
51089
+ }));
51090
+ } else if (!creating && holds(previousRaw["LifecycleConfiguration"], lifecycleToSdk)) {
51091
+ markUnsettled("LifecycleConfiguration");
51092
+ await this.s3Client.send(new DeleteBucketLifecycleCommand({ Bucket: bucketName }));
51093
+ this.logger.debug("Sent DeleteBucketLifecycle: every lifecycle rule is removed");
51094
+ }
51095
+ }
51096
+ const desiredMetrics = recordedItems(desiredRaw["MetricsConfigurations"]);
51097
+ const previousMetrics = creating ? /* @__PURE__ */ new Map() : recordedItems(previousRaw["MetricsConfigurations"]);
51098
+ for (const [id, config] of desired.metrics) {
51099
+ if (previousMetrics.has(id) && sameJson(previousMetrics.get(id), desiredMetrics.get(id))) continue;
51100
+ markUnsettled("MetricsConfigurations");
51101
+ await this.s3Client.send(new PutBucketMetricsConfigurationCommand({
51102
+ Bucket: bucketName,
51103
+ Id: id,
51104
+ MetricsConfiguration: config
51105
+ }));
51106
+ }
51107
+ for (const id of previousMetrics.keys()) {
51108
+ if (desired.metrics.has(id)) continue;
51109
+ markUnsettled("MetricsConfigurations");
51110
+ await this.deleteItemIdempotently(new DeleteBucketMetricsConfigurationCommand({
51111
+ Bucket: bucketName,
51112
+ Id: id
51113
+ }));
51114
+ }
51115
+ const desiredInventory = recordedItems(desiredRaw["InventoryConfigurations"]);
51116
+ const previousInventory = creating ? /* @__PURE__ */ new Map() : recordedItems(previousRaw["InventoryConfigurations"]);
51117
+ for (const [id, config] of desired.inventory) {
51118
+ if (previousInventory.has(id) && sameJson(previousInventory.get(id), desiredInventory.get(id))) continue;
51119
+ markUnsettled("InventoryConfigurations");
51120
+ await this.s3Client.send(new PutBucketInventoryConfigurationCommand({
51121
+ Bucket: bucketName,
51122
+ Id: id,
51123
+ InventoryConfiguration: config
51124
+ }));
51125
+ }
51126
+ for (const id of previousInventory.keys()) {
51127
+ if (desired.inventory.has(id)) continue;
51128
+ markUnsettled("InventoryConfigurations");
51129
+ await this.deleteItemIdempotently(new DeleteBucketInventoryConfigurationCommand({
51130
+ Bucket: bucketName,
51131
+ Id: id
51132
+ }));
51133
+ }
51134
+ }
51135
+ async deleteItemIdempotently(command) {
51136
+ try {
51137
+ if (command instanceof DeleteBucketMetricsConfigurationCommand) await this.s3Client.send(command);
51138
+ else await this.s3Client.send(command);
51139
+ } catch (error) {
51140
+ if (error instanceof Error && error.name === "NoSuchConfiguration") {
51141
+ this.logger.debug("A configuration this update deletes is already gone");
51142
+ return;
51143
+ }
51144
+ throw error;
49773
51145
  }
49774
51146
  }
49775
51147
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
49776
51148
  if (context?.expectedRegion) for (const clientRegion of [await this.s3Client.config.region(), await this.getS3ControlClient().config.region()]) assertRegionMatch(clientRegion, context.expectedRegion, resourceType, logicalId, physicalId, "pre-update");
49777
51149
  refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId);
51150
+ const configs = desiredConfigurations(properties, configurationRefuser(resourceType, logicalId, physicalId));
49778
51151
  try {
49779
51152
  const plan = planTagDiff(previousProperties["Tags"], properties["Tags"]);
49780
51153
  const tagWarning = tagPlanWarning(plan, resourceType, logicalId);
@@ -49787,18 +51160,25 @@ var S3DirectoryBucketProvider = class {
49787
51160
  if (removedKeys.length > 0 || tagsToApply.length > 0) {
49788
51161
  const accountId = await this.getAccountId();
49789
51162
  const resourceArn = await this.buildBucketArn(physicalId, accountId);
49790
- if (removedKeys.length > 0) await this.getS3ControlClient().send(new UntagResourceCommand$29({
49791
- AccountId: accountId,
49792
- ResourceArn: resourceArn,
49793
- TagKeys: removedKeys
49794
- }));
49795
- if (tagsToApply.length > 0) await this.getS3ControlClient().send(new TagResourceCommand$28({
49796
- AccountId: accountId,
49797
- ResourceArn: resourceArn,
49798
- Tags: tagsToApply
49799
- }));
51163
+ if (removedKeys.length > 0) {
51164
+ context?.markUnsettled?.("Tags");
51165
+ await this.getS3ControlClient().send(new UntagResourceCommand$29({
51166
+ AccountId: accountId,
51167
+ ResourceArn: resourceArn,
51168
+ TagKeys: removedKeys
51169
+ }));
51170
+ }
51171
+ if (tagsToApply.length > 0) {
51172
+ context?.markUnsettled?.("Tags");
51173
+ await this.getS3ControlClient().send(new TagResourceCommand$28({
51174
+ AccountId: accountId,
51175
+ ResourceArn: resourceArn,
51176
+ Tags: tagsToApply
51177
+ }));
51178
+ }
49800
51179
  this.logger.debug(`Updated tags for S3 Express Directory Bucket ${physicalId}`);
49801
- } else this.logger.debug(`Update for S3 Express Directory Bucket ${logicalId} is a no-op (no tag changes; other properties are immutable)`);
51180
+ }
51181
+ await this.applyConfigurations(physicalId, configs, properties, previousProperties, false, (key) => context?.markUnsettled?.(key));
49802
51182
  return {
49803
51183
  physicalId,
49804
51184
  wasReplaced: false
@@ -49871,6 +51251,9 @@ var S3DirectoryBucketProvider = class {
49871
51251
  } while (continuationToken);
49872
51252
  }
49873
51253
  async readCurrentState(physicalId, _logicalId, _resourceType) {
51254
+ return this.readState(physicalId);
51255
+ }
51256
+ async readState(physicalId) {
49874
51257
  let locationType;
49875
51258
  try {
49876
51259
  locationType = (await this.s3Client.send(new HeadBucketCommand({ Bucket: physicalId })))?.BucketLocationType;
@@ -49882,8 +51265,63 @@ var S3DirectoryBucketProvider = class {
49882
51265
  const result = { BucketName: physicalId };
49883
51266
  if (physicalId.endsWith("--x-s3")) result["DataRedundancy"] = locationType === "LocalZone" ? "SingleLocalZone" : "SingleAvailabilityZone";
49884
51267
  result["Tags"] = await this.readTags(physicalId);
51268
+ const reads = [
51269
+ ["BucketEncryption", () => this.readEncryption(physicalId)],
51270
+ ["LifecycleConfiguration", () => this.readLifecycle(physicalId)],
51271
+ ["MetricsConfigurations", () => this.readMetrics(physicalId)],
51272
+ ["InventoryConfigurations", () => this.readInventory(physicalId)]
51273
+ ];
51274
+ (await Promise.all(reads.map(([key, read]) => read().catch((error) => {
51275
+ const name = error?.name;
51276
+ if (name === "AccessDenied" || name === "AccessDeniedException") throw new Error(`Reading ${key} of an S3 Express directory bucket needs ${CONFIGURATION_READ_ACTIONS[key]}: grant it to read the bucket back`, { cause: error });
51277
+ throw error;
51278
+ })))).forEach((value, i) => {
51279
+ result[reads[i][0]] = value;
51280
+ });
49885
51281
  return result;
49886
51282
  }
51283
+ async readEncryption(bucket) {
51284
+ try {
51285
+ return encryptionFromSdk((await this.s3Client.send(new GetBucketEncryptionCommand({ Bucket: bucket }))).ServerSideEncryptionConfiguration?.Rules ?? []);
51286
+ } catch (err) {
51287
+ if (err.name === "ServerSideEncryptionConfigurationNotFoundError") return { ServerSideEncryptionConfiguration: [] };
51288
+ throw err;
51289
+ }
51290
+ }
51291
+ async readLifecycle(bucket) {
51292
+ try {
51293
+ return lifecycleFromSdk((await this.s3Client.send(new GetBucketLifecycleConfigurationCommand({ Bucket: bucket }))).Rules ?? []);
51294
+ } catch (err) {
51295
+ if (err.name === "NoSuchLifecycleConfiguration") return { Rules: [] };
51296
+ throw err;
51297
+ }
51298
+ }
51299
+ async readMetrics(bucket) {
51300
+ const out = [];
51301
+ let token;
51302
+ do {
51303
+ const resp = await this.s3Client.send(new ListBucketMetricsConfigurationsCommand({
51304
+ Bucket: bucket,
51305
+ ContinuationToken: token
51306
+ }));
51307
+ for (const item of resp.MetricsConfigurationList ?? []) out.push(metricsFromSdk(item));
51308
+ token = resp.IsTruncated ? resp.NextContinuationToken : void 0;
51309
+ } while (token);
51310
+ return out;
51311
+ }
51312
+ async readInventory(bucket) {
51313
+ const out = [];
51314
+ let token;
51315
+ do {
51316
+ const resp = await this.s3Client.send(new ListBucketInventoryConfigurationsCommand({
51317
+ Bucket: bucket,
51318
+ ContinuationToken: token
51319
+ }));
51320
+ for (const item of resp.InventoryConfigurationList ?? []) out.push(inventoryFromSdk(item));
51321
+ token = resp.IsTruncated ? resp.NextContinuationToken : void 0;
51322
+ } while (token);
51323
+ return out;
51324
+ }
49887
51325
  async readTags(bucketName) {
49888
51326
  const accountId = await this.getAccountId();
49889
51327
  const resourceArn = await this.buildBucketArn(bucketName, accountId);
@@ -49962,6 +51400,173 @@ function tableIdentityFromGetTable(tableARN, namespace, name) {
49962
51400
  name
49963
51401
  };
49964
51402
  }
51403
+ function isCfnObject(value) {
51404
+ return value !== null && typeof value === "object" && !Array.isArray(value);
51405
+ }
51406
+ function desiredObject(properties, key, resourceType, logicalId, physicalId) {
51407
+ const value = properties[key];
51408
+ if (value === void 0) return void 0;
51409
+ if (isCfnObject(value)) return value;
51410
+ throw new ProvisioningError(`${key} for ${resourceType} ${logicalId} must be an object; refusing before any call`, resourceType, logicalId, physicalId);
51411
+ }
51412
+ function recordedObject(properties, key) {
51413
+ const value = properties[key];
51414
+ return isCfnObject(value) ? value : void 0;
51415
+ }
51416
+ var NotAnInteger = class extends Error {
51417
+ member;
51418
+ constructor(member) {
51419
+ super(`${member} must be an integer`);
51420
+ this.member = member;
51421
+ }
51422
+ };
51423
+ function integer(value, member) {
51424
+ if (value === void 0) return void 0;
51425
+ const n = typeof value === "number" ? value : typeof value === "string" && value.trim() !== "" ? Number(value) : NaN;
51426
+ if (Number.isInteger(n)) return n;
51427
+ throw new NotAnInteger(member);
51428
+ }
51429
+ function refuseNonIntegers(convert, key, resourceType, logicalId, physicalId) {
51430
+ try {
51431
+ return convert();
51432
+ } catch (error) {
51433
+ if (!(error instanceof NotAnInteger)) throw error;
51434
+ throw new ProvisioningError(`${key}.${error.member} for ${resourceType} ${logicalId} must be an integer; refusing before any call`, resourceType, logicalId, physicalId, error);
51435
+ }
51436
+ }
51437
+ function list(value) {
51438
+ return Array.isArray(value) ? value : void 0;
51439
+ }
51440
+ function defined(value) {
51441
+ return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0));
51442
+ }
51443
+ function maintenanceStatus(value) {
51444
+ return typeof value === "string" ? value.toLowerCase() : void 0;
51445
+ }
51446
+ function sdkEncryption(cfn) {
51447
+ return defined({
51448
+ sseAlgorithm: cfn["SSEAlgorithm"],
51449
+ kmsKeyArn: cfn["KMSKeyArn"]
51450
+ });
51451
+ }
51452
+ function sdkStorageClass(cfn) {
51453
+ return defined({ storageClass: cfn["StorageClass"] });
51454
+ }
51455
+ function sdkReplication(cfn) {
51456
+ return defined({
51457
+ role: cfn["Role"],
51458
+ rules: list(cfn["Rules"])?.map((rule) => defined({ destinations: list(isCfnObject(rule) ? rule["Destinations"] : void 0)?.map((destination) => defined({ destinationTableBucketARN: isCfnObject(destination) ? destination["DestinationTableBucketARN"] : void 0 })) }))
51459
+ });
51460
+ }
51461
+ function sdkUnreferencedFileRemoval(cfn) {
51462
+ const unreferencedDays = integer(cfn["UnreferencedDays"], "UnreferencedDays");
51463
+ const nonCurrentDays = integer(cfn["NoncurrentDays"], "NoncurrentDays");
51464
+ return defined({
51465
+ status: maintenanceStatus(cfn["Status"]),
51466
+ settings: unreferencedDays === void 0 && nonCurrentDays === void 0 ? void 0 : { icebergUnreferencedFileRemoval: defined({
51467
+ unreferencedDays,
51468
+ nonCurrentDays
51469
+ }) }
51470
+ });
51471
+ }
51472
+ function sdkCompaction(cfn) {
51473
+ const targetFileSizeMB = integer(cfn["TargetFileSizeMB"], "TargetFileSizeMB");
51474
+ return defined({
51475
+ status: maintenanceStatus(cfn["Status"]),
51476
+ settings: targetFileSizeMB === void 0 ? void 0 : { icebergCompaction: { targetFileSizeMB } }
51477
+ });
51478
+ }
51479
+ function sdkSnapshotManagement(cfn) {
51480
+ const minSnapshotsToKeep = integer(cfn["MinSnapshotsToKeep"], "MinSnapshotsToKeep");
51481
+ const maxSnapshotAgeHours = integer(cfn["MaxSnapshotAgeHours"], "MaxSnapshotAgeHours");
51482
+ return defined({
51483
+ status: maintenanceStatus(cfn["Status"]),
51484
+ settings: minSnapshotsToKeep === void 0 && maxSnapshotAgeHours === void 0 ? void 0 : { icebergSnapshotManagement: defined({
51485
+ minSnapshotsToKeep,
51486
+ maxSnapshotAgeHours
51487
+ }) }
51488
+ });
51489
+ }
51490
+ function mapObjects(value, map) {
51491
+ return list(value)?.map((element) => map(isCfnObject(element) ? element : {}));
51492
+ }
51493
+ function sdkIcebergMetadata(cfn) {
51494
+ const schema = isCfnObject(cfn["IcebergSchema"]) ? cfn["IcebergSchema"] : void 0;
51495
+ const schemaV2 = isCfnObject(cfn["IcebergSchemaV2"]) ? cfn["IcebergSchemaV2"] : void 0;
51496
+ const partitionSpec = isCfnObject(cfn["IcebergPartitionSpec"]) ? cfn["IcebergPartitionSpec"] : void 0;
51497
+ const sortOrder = isCfnObject(cfn["IcebergSortOrder"]) ? cfn["IcebergSortOrder"] : void 0;
51498
+ return defined({
51499
+ schema: schema && defined({ fields: mapObjects(schema["SchemaFieldList"], (f) => defined({
51500
+ id: integer(f["Id"], "IcebergSchema.SchemaFieldList.Id"),
51501
+ name: f["Name"],
51502
+ type: f["Type"],
51503
+ required: f["Required"]
51504
+ })) }),
51505
+ schemaV2: schemaV2 && defined({
51506
+ type: schemaV2["SchemaV2FieldType"],
51507
+ fields: mapObjects(schemaV2["SchemaV2FieldList"], (f) => defined({
51508
+ id: integer(f["Id"], "IcebergSchemaV2.SchemaV2FieldList.Id"),
51509
+ name: f["Name"],
51510
+ type: f["Type"],
51511
+ required: f["Required"],
51512
+ doc: f["Doc"]
51513
+ })),
51514
+ schemaId: integer(schemaV2["SchemaId"], "IcebergSchemaV2.SchemaId"),
51515
+ identifierFieldIds: list(schemaV2["IdentifierFieldIds"])?.map((id) => integer(id, "IcebergSchemaV2.IdentifierFieldIds"))
51516
+ }),
51517
+ partitionSpec: partitionSpec && defined({
51518
+ fields: mapObjects(partitionSpec["Fields"], (f) => defined({
51519
+ sourceId: integer(f["SourceId"], "IcebergPartitionSpec.Fields.SourceId"),
51520
+ transform: f["Transform"],
51521
+ name: f["Name"],
51522
+ fieldId: integer(f["FieldId"], "IcebergPartitionSpec.Fields.FieldId")
51523
+ })),
51524
+ specId: integer(partitionSpec["SpecId"], "IcebergPartitionSpec.SpecId")
51525
+ }),
51526
+ writeOrder: sortOrder && defined({
51527
+ orderId: integer(sortOrder["OrderId"], "IcebergSortOrder.OrderId"),
51528
+ fields: mapObjects(sortOrder["Fields"], (f) => defined({
51529
+ sourceId: integer(f["SourceId"], "IcebergSortOrder.Fields.SourceId"),
51530
+ transform: f["Transform"],
51531
+ direction: f["Direction"],
51532
+ nullOrder: f["NullOrder"]
51533
+ }))
51534
+ }),
51535
+ properties: isCfnObject(cfn["TableProperties"]) ? cfn["TableProperties"] : void 0
51536
+ });
51537
+ }
51538
+ const UPDATABLE_CONFIG_KEYS = {
51539
+ "AWS::S3Tables::TableBucket": [
51540
+ "EncryptionConfiguration",
51541
+ "StorageClassConfiguration",
51542
+ "MetricsConfiguration",
51543
+ "UnreferencedFileRemoval",
51544
+ "ReplicationConfiguration"
51545
+ ],
51546
+ "AWS::S3Tables::Table": ["Compaction", "SnapshotManagement"]
51547
+ };
51548
+ function metricsEnabled(metrics, resourceType, logicalId, physicalId) {
51549
+ const status = metrics?.["Status"];
51550
+ if (status === void 0 || status === "Disabled") return false;
51551
+ if (status === "Enabled") return true;
51552
+ throw new ProvisioningError(safeMsg`MetricsConfiguration.Status for ${resourceType} ${logicalId} must be 'Enabled' or 'Disabled'; refusing before any call`, resourceType, logicalId, physicalId);
51553
+ }
51554
+ const TABLE_MAINTENANCE = [[
51555
+ "Compaction",
51556
+ "icebergCompaction",
51557
+ sdkCompaction
51558
+ ], [
51559
+ "SnapshotManagement",
51560
+ "icebergSnapshotManagement",
51561
+ sdkSnapshotManagement
51562
+ ]];
51563
+ const INTEGER_BEARING_CONFIGS = {
51564
+ "AWS::S3Tables::TableBucket": [["UnreferencedFileRemoval", sdkUnreferencedFileRemoval]],
51565
+ "AWS::S3Tables::Table": TABLE_MAINTENANCE.map(([key, , toSdk]) => [key, toSdk])
51566
+ };
51567
+ function cfnBucketStatus(status) {
51568
+ return status === void 0 ? void 0 : `${status.charAt(0).toUpperCase()}${status.slice(1)}`;
51569
+ }
49965
51570
  function sentence(text) {
49966
51571
  return text.endsWith(".") ? text : `${text}.`;
49967
51572
  }
@@ -49978,7 +51583,15 @@ var S3TablesProvider = class {
49978
51583
  return markRedactedCause(build(`${m(failure.summary)}${relayedNameCollision(failure.detail)}`));
49979
51584
  }
49980
51585
  handledProperties = /* @__PURE__ */ new Map([
49981
- ["AWS::S3Tables::TableBucket", /* @__PURE__ */ new Set(["TableBucketName", "Tags"])],
51586
+ ["AWS::S3Tables::TableBucket", /* @__PURE__ */ new Set([
51587
+ "TableBucketName",
51588
+ "Tags",
51589
+ "EncryptionConfiguration",
51590
+ "StorageClassConfiguration",
51591
+ "MetricsConfiguration",
51592
+ "UnreferencedFileRemoval",
51593
+ "ReplicationConfiguration"
51594
+ ])],
49982
51595
  ["AWS::S3Tables::Namespace", /* @__PURE__ */ new Set(["TableBucketARN", "Namespace"])],
49983
51596
  ["AWS::S3Tables::Table", /* @__PURE__ */ new Set([
49984
51597
  "TableBucketARN",
@@ -49987,9 +51600,24 @@ var S3TablesProvider = class {
49987
51600
  "Name",
49988
51601
  "OpenTableFormat",
49989
51602
  "Format",
49990
- "Tags"
51603
+ "Tags",
51604
+ "IcebergMetadata",
51605
+ "StorageClassConfiguration",
51606
+ "Compaction",
51607
+ "SnapshotManagement",
51608
+ "WithoutMetadata"
49991
51609
  ])]
49992
51610
  ]);
51611
+ getDriftUnknownPaths(resourceType) {
51612
+ return resourceType === "AWS::S3Tables::Table" ? [
51613
+ "IcebergMetadata",
51614
+ "StorageClassConfiguration",
51615
+ "WithoutMetadata"
51616
+ ] : [];
51617
+ }
51618
+ getDriftAbsentMeansDefaultKeys(resourceType, properties) {
51619
+ return resourceType === "AWS::S3Tables::TableBucket" && properties["ReplicationConfiguration"] === void 0 ? ["ReplicationConfiguration"] : [];
51620
+ }
49993
51621
  getClient() {
49994
51622
  if (!this.client) {
49995
51623
  this.client = new S3TablesClient({
@@ -50018,18 +51646,25 @@ var S3TablesProvider = class {
50018
51646
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
50019
51647
  assertRegionMatch(context?.expectedRegion ? await this.getClient().config.region() : void 0, context?.expectedRegion, resourceType, logicalId, physicalId, "pre-update");
50020
51648
  if (resourceType === "AWS::S3Tables::Table" || resourceType === "AWS::S3Tables::TableBucket") refuseMalformedDesiredTags(properties["Tags"], resourceType, logicalId, physicalId);
51649
+ for (const key of UPDATABLE_CONFIG_KEYS[resourceType] ?? []) desiredObject(properties, key, resourceType, logicalId, physicalId);
51650
+ for (const [key, convert] of INTEGER_BEARING_CONFIGS[resourceType] ?? []) {
51651
+ const value = desiredObject(properties, key, resourceType, logicalId, physicalId);
51652
+ if (value !== void 0) refuseNonIntegers(() => convert(value), key, resourceType, logicalId, physicalId);
51653
+ }
51654
+ if (resourceType === "AWS::S3Tables::TableBucket") metricsEnabled(recordedObject(properties, "MetricsConfiguration"), resourceType, logicalId, physicalId);
50021
51655
  try {
50022
- return await this.applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties, context?.maskSecrets, context?.recordedAttributes);
51656
+ return await this.applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties, context?.maskSecrets, context?.recordedAttributes, (key) => context?.markUnsettled?.(key));
50023
51657
  } catch (error) {
50024
51658
  if (error instanceof CdkdError) throw error;
50025
51659
  const cause = error instanceof Error ? error : void 0;
50026
51660
  throw this.wrapCaughtError(context?.maskSecrets, error, (text) => new ProvisioningError(`Failed to update S3 Tables resource ${logicalId}: ${text}`, resourceType, logicalId, physicalId, cause));
50027
51661
  }
50028
51662
  }
50029
- async applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties, mask, recordedAttributes) {
51663
+ async applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties, mask, recordedAttributes, markUnsettled) {
50030
51664
  if (resourceType === "AWS::S3Tables::Table") {
50031
51665
  const rename = await this.planTableRename(logicalId, physicalId, resourceType, properties, mask);
50032
51666
  await this.applyTableTagsDiff(logicalId, physicalId, resourceType, previousProperties["Tags"], properties["Tags"], mask);
51667
+ await this.applyTableMaintenanceUpdate(logicalId, physicalId, resourceType, properties, previousProperties, markUnsettled);
50033
51668
  if (rename === void 0) return {
50034
51669
  physicalId,
50035
51670
  wasReplaced: false
@@ -50044,8 +51679,10 @@ var S3TablesProvider = class {
50044
51679
  physicalId: rename.physicalId,
50045
51680
  wasReplaced: false
50046
51681
  };
50047
- } else if (resourceType === "AWS::S3Tables::TableBucket") await this.applyTableBucketTagsDiff(logicalId, physicalId, resourceType, previousProperties["Tags"], properties["Tags"], mask);
50048
- else this.logger.debug(`Update is no-op for ${resourceType} ${logicalId}`);
51682
+ } else if (resourceType === "AWS::S3Tables::TableBucket") {
51683
+ await this.applyTableBucketTagsDiff(logicalId, physicalId, resourceType, previousProperties["Tags"], properties["Tags"], mask);
51684
+ await this.applyTableBucketConfigUpdate(logicalId, physicalId, resourceType, properties, previousProperties, markUnsettled);
51685
+ } else this.logger.debug(`Update is no-op for ${resourceType} ${logicalId}`);
50049
51686
  return {
50050
51687
  physicalId,
50051
51688
  wasReplaced: false
@@ -50066,11 +51703,32 @@ var S3TablesProvider = class {
50066
51703
  const tableBucketName = properties["TableBucketName"];
50067
51704
  if (!tableBucketName) throw new ProvisioningError(`TableBucketName is required for S3 Table Bucket ${logicalId}`, resourceType, logicalId);
50068
51705
  const tags = this.tagListToSdkMap(desiredTags);
51706
+ const encryption = desiredObject(properties, "EncryptionConfiguration", resourceType, logicalId);
51707
+ const storageClass = desiredObject(properties, "StorageClassConfiguration", resourceType, logicalId);
51708
+ const metricsOn = metricsEnabled(desiredObject(properties, "MetricsConfiguration", resourceType, logicalId), resourceType, logicalId);
51709
+ const unreferenced = desiredObject(properties, "UnreferencedFileRemoval", resourceType, logicalId);
51710
+ const replication = desiredObject(properties, "ReplicationConfiguration", resourceType, logicalId);
51711
+ const unreferencedValue = unreferenced && refuseNonIntegers(() => sdkUnreferencedFileRemoval(unreferenced), "UnreferencedFileRemoval", resourceType, logicalId);
51712
+ let createdArn;
50069
51713
  try {
50070
51714
  const tableBucketARN = (await this.getCreateClient().send(new CreateTableBucketCommand({
50071
51715
  name: tableBucketName,
51716
+ ...encryption !== void 0 && { encryptionConfiguration: sdkEncryption(encryption) },
51717
+ ...storageClass !== void 0 && { storageClassConfiguration: sdkStorageClass(storageClass) },
50072
51718
  ...tags !== void 0 && { tags }
50073
51719
  }))).arn;
51720
+ createdArn = tableBucketARN;
51721
+ reportCreatedResource(logicalId, resourceType, tableBucketARN);
51722
+ if (metricsOn) await this.getClient().send(new PutTableBucketMetricsConfigurationCommand({ tableBucketARN }));
51723
+ if (unreferencedValue !== void 0) await this.getClient().send(new PutTableBucketMaintenanceConfigurationCommand({
51724
+ tableBucketARN,
51725
+ type: "icebergUnreferencedFileRemoval",
51726
+ value: unreferencedValue
51727
+ }));
51728
+ if (replication !== void 0) await this.getClient().send(new PutTableBucketReplicationCommand({
51729
+ tableBucketARN,
51730
+ configuration: sdkReplication(replication)
51731
+ }));
50074
51732
  this.logger.debug(`Successfully created S3 Table Bucket ${logicalId}: ${tableBucketARN}`);
50075
51733
  return {
50076
51734
  physicalId: tableBucketARN,
@@ -50078,7 +51736,104 @@ var S3TablesProvider = class {
50078
51736
  };
50079
51737
  } catch (error) {
50080
51738
  const cause = error instanceof Error ? error : void 0;
50081
- throw this.wrapCaughtError(context?.maskSecrets, error, (text) => new ProvisioningError(`Failed to create S3 Table Bucket ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
51739
+ const thrown = this.wrapCaughtError(context?.maskSecrets, error, (text) => new ProvisioningError(`Failed to create S3 Table Bucket ${logicalId}: ${text}`, resourceType, logicalId, void 0, cause));
51740
+ if (createdArn !== void 0) markCreatedBeforeFailure(thrown, logicalId, resourceType, createdArn);
51741
+ throw thrown;
51742
+ }
51743
+ }
51744
+ async applyTableBucketConfigUpdate(logicalId, tableBucketARN, resourceType, properties, previousProperties, markUnsettled) {
51745
+ const changed = (key) => {
51746
+ const next = desiredObject(properties, key, resourceType, logicalId, tableBucketARN);
51747
+ return [next, !isDeepStrictEqual(next, recordedObject(previousProperties, key))];
51748
+ };
51749
+ const client = this.getClient();
51750
+ const [encryption, encryptionChanged] = changed("EncryptionConfiguration");
51751
+ if (encryptionChanged) {
51752
+ if (encryption !== void 0) {
51753
+ markUnsettled("EncryptionConfiguration");
51754
+ await client.send(new PutTableBucketEncryptionCommand({
51755
+ tableBucketARN,
51756
+ encryptionConfiguration: sdkEncryption(encryption)
51757
+ }));
51758
+ } else this.warnRemovalKept(logicalId, "EncryptionConfiguration");
51759
+ }
51760
+ const [storageClass, storageClassChanged] = changed("StorageClassConfiguration");
51761
+ if (storageClassChanged) {
51762
+ if (storageClass !== void 0) {
51763
+ markUnsettled("StorageClassConfiguration");
51764
+ await client.send(new PutTableBucketStorageClassCommand({
51765
+ tableBucketARN,
51766
+ storageClassConfiguration: sdkStorageClass(storageClass)
51767
+ }));
51768
+ } else this.warnRemovalKept(logicalId, "StorageClassConfiguration");
51769
+ }
51770
+ const metricsOn = metricsEnabled(desiredObject(properties, "MetricsConfiguration", resourceType, logicalId, tableBucketARN), resourceType, logicalId);
51771
+ const metricsWasOn = recordedObject(previousProperties, "MetricsConfiguration")?.["Status"] === "Enabled";
51772
+ if (metricsOn && !metricsWasOn) {
51773
+ markUnsettled("MetricsConfiguration");
51774
+ await client.send(new PutTableBucketMetricsConfigurationCommand({ tableBucketARN }));
51775
+ } else if (!metricsOn && metricsWasOn) {
51776
+ markUnsettled("MetricsConfiguration");
51777
+ await this.deleteBucketConfig(tableBucketARN, new DeleteTableBucketMetricsConfigurationCommand({ tableBucketARN }));
51778
+ }
51779
+ const [unreferenced, unreferencedChanged] = changed("UnreferencedFileRemoval");
51780
+ if (unreferencedChanged) {
51781
+ if (unreferenced !== void 0) {
51782
+ markUnsettled("UnreferencedFileRemoval");
51783
+ await client.send(new PutTableBucketMaintenanceConfigurationCommand({
51784
+ tableBucketARN,
51785
+ type: "icebergUnreferencedFileRemoval",
51786
+ value: sdkUnreferencedFileRemoval(unreferenced)
51787
+ }));
51788
+ } else this.warnRemovalKept(logicalId, "UnreferencedFileRemoval");
51789
+ }
51790
+ const [replication, replicationChanged] = changed("ReplicationConfiguration");
51791
+ if (replicationChanged) {
51792
+ if (replication !== void 0) {
51793
+ markUnsettled("ReplicationConfiguration");
51794
+ await client.send(new PutTableBucketReplicationCommand({
51795
+ tableBucketARN,
51796
+ configuration: sdkReplication(replication)
51797
+ }));
51798
+ } else {
51799
+ markUnsettled("ReplicationConfiguration");
51800
+ await this.deleteBucketConfig(tableBucketARN, new DeleteTableBucketReplicationCommand({ tableBucketARN }));
51801
+ }
51802
+ }
51803
+ }
51804
+ async applyTableMaintenanceUpdate(logicalId, physicalId, resourceType, properties, previousProperties, markUnsettled) {
51805
+ const writes = [];
51806
+ for (const [key, type, toSdk] of TABLE_MAINTENANCE) {
51807
+ const next = desiredObject(properties, key, resourceType, logicalId, physicalId);
51808
+ if (isDeepStrictEqual(next, recordedObject(previousProperties, key))) continue;
51809
+ if (next === void 0) this.warnRemovalKept(logicalId, key);
51810
+ else writes.push({
51811
+ key,
51812
+ type,
51813
+ value: toSdk(next)
51814
+ });
51815
+ }
51816
+ if (writes.length === 0) return;
51817
+ const table = await this.resolveTableParts(physicalId);
51818
+ if (table === "not-found" || table === "unparseable") throw new ProvisioningError(table === "not-found" ? safeMsg`S3 Tables Table '${physicalId}' no longer exists (state is out of sync); ` + `refusing to record a maintenance configuration that was not applied` : compositeIdFormatMessage(S3_TABLES_TABLE_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
51819
+ for (const { key, type, value } of writes) {
51820
+ markUnsettled(key);
51821
+ await this.getClient().send(new PutTableMaintenanceConfigurationCommand({
51822
+ ...table,
51823
+ type,
51824
+ value
51825
+ }));
51826
+ }
51827
+ }
51828
+ warnRemovalKept(logicalId, key) {
51829
+ this.logger.warn(safeMsg`${logicalId}: ${key} was removed from the template; cdkd leaves its current AWS value in ` + `place. Declare the value you want (e.g. its default) to change it.`);
51830
+ }
51831
+ async deleteBucketConfig(tableBucketARN, command) {
51832
+ try {
51833
+ await this.getClient().send(command);
51834
+ } catch (error) {
51835
+ if (!(error instanceof NotFoundException$7)) throw error;
51836
+ await this.getClient().send(new GetTableBucketCommand({ tableBucketARN }));
50082
51837
  }
50083
51838
  }
50084
51839
  async deleteTableBucket(logicalId, physicalId, resourceType, context) {
@@ -50213,6 +51968,17 @@ var S3TablesProvider = class {
50213
51968
  const format = properties["OpenTableFormat"] ?? properties["Format"];
50214
51969
  if (!format) throw new ProvisioningError(`OpenTableFormat is required for S3 Tables Table ${logicalId}`, resourceType, logicalId);
50215
51970
  const tags = this.tagListToSdkMap(desiredTags);
51971
+ const icebergCfn = desiredObject(properties, "IcebergMetadata", resourceType, logicalId);
51972
+ const iceberg = icebergCfn && refuseNonIntegers(() => sdkIcebergMetadata(icebergCfn), "IcebergMetadata", resourceType, logicalId);
51973
+ const storageClass = desiredObject(properties, "StorageClassConfiguration", resourceType, logicalId);
51974
+ const maintenance = [];
51975
+ for (const [key, type, toSdk] of TABLE_MAINTENANCE) {
51976
+ const value = desiredObject(properties, key, resourceType, logicalId);
51977
+ if (value !== void 0) maintenance.push({
51978
+ type,
51979
+ value: refuseNonIntegers(() => toSdk(value), key, resourceType, logicalId)
51980
+ });
51981
+ }
50216
51982
  const physicalId = packCompositeId(resourceType, logicalId, [
50217
51983
  {
50218
51984
  name: "tableBucketARN",
@@ -50237,9 +52003,18 @@ var S3TablesProvider = class {
50237
52003
  namespace,
50238
52004
  name,
50239
52005
  format,
52006
+ ...iceberg !== void 0 && { metadata: { iceberg } },
52007
+ ...storageClass !== void 0 && { storageClassConfiguration: sdkStorageClass(storageClass) },
50240
52008
  ...tags !== void 0 && { tags }
50241
52009
  }));
50242
52010
  tableCreated = true;
52011
+ reportCreatedResource(logicalId, resourceType, physicalId);
52012
+ for (const write of maintenance) await this.getClient().send(new PutTableMaintenanceConfigurationCommand({
52013
+ tableBucketARN,
52014
+ namespace,
52015
+ name,
52016
+ ...write
52017
+ }));
50243
52018
  if (!response.tableARN) throw new ProvisioningError(`CreateTable did not return a tableARN for ${logicalId} (${physicalId}) — refusing to record an empty TableARN attribute`, resourceType, logicalId, physicalId);
50244
52019
  const tableARN = response.tableARN;
50245
52020
  this.logger.debug(`Successfully created S3 Tables Table ${logicalId}: ${physicalId}`);
@@ -50275,8 +52050,58 @@ var S3TablesProvider = class {
50275
52050
  const result = {};
50276
52051
  if (bucket.name !== void 0) result["TableBucketName"] = bucket.name;
50277
52052
  result["Tags"] = await this.readTagsBestEffort(physicalId);
52053
+ Object.assign(result, await this.readTableBucketConfigs(physicalId));
50278
52054
  return result;
50279
52055
  }
52056
+ async readTableBucketConfigs(tableBucketARN, strict = false) {
52057
+ const client = this.getClient();
52058
+ const out = {};
52059
+ await this.readConfig(out, "EncryptionConfiguration", async () => {
52060
+ const e = (await client.send(new GetTableBucketEncryptionCommand({ tableBucketARN }))).encryptionConfiguration;
52061
+ return defined({
52062
+ SSEAlgorithm: e?.sseAlgorithm ?? "AES256",
52063
+ KMSKeyArn: e?.kmsKeyArn
52064
+ });
52065
+ }, { SSEAlgorithm: "AES256" }, strict);
52066
+ await this.readConfig(out, "StorageClassConfiguration", async () => {
52067
+ return { StorageClass: (await client.send(new GetTableBucketStorageClassCommand({ tableBucketARN }))).storageClassConfiguration?.storageClass ?? "STANDARD" };
52068
+ }, { StorageClass: "STANDARD" }, strict);
52069
+ await this.readConfig(out, "MetricsConfiguration", async () => {
52070
+ await client.send(new GetTableBucketMetricsConfigurationCommand({ tableBucketARN }));
52071
+ return { Status: "Enabled" };
52072
+ }, { Status: "Disabled" }, strict);
52073
+ await this.readConfig(out, "UnreferencedFileRemoval", async () => {
52074
+ const value = (await client.send(new GetTableBucketMaintenanceConfigurationCommand({ tableBucketARN }))).configuration?.icebergUnreferencedFileRemoval;
52075
+ const settings = value?.settings?.icebergUnreferencedFileRemoval;
52076
+ return defined({
52077
+ Status: cfnBucketStatus(value?.status) ?? "Enabled",
52078
+ UnreferencedDays: settings?.unreferencedDays,
52079
+ NoncurrentDays: settings?.nonCurrentDays
52080
+ });
52081
+ }, { Status: "Enabled" }, strict);
52082
+ await this.readConfig(out, "ReplicationConfiguration", async () => {
52083
+ const c = (await client.send(new GetTableBucketReplicationCommand({ tableBucketARN }))).configuration;
52084
+ if (c === void 0) return void 0;
52085
+ return defined({
52086
+ Role: c.role,
52087
+ Rules: (c.rules ?? []).map((rule) => ({ Destinations: (rule.destinations ?? []).map((d) => ({ DestinationTableBucketARN: d.destinationTableBucketARN })) }))
52088
+ });
52089
+ }, "absent", strict);
52090
+ return out;
52091
+ }
52092
+ async readConfig(out, key, read, notFound, strict = false) {
52093
+ try {
52094
+ const value = await read();
52095
+ if (value !== void 0) out[key] = value;
52096
+ } catch (err) {
52097
+ if (err instanceof NotFoundException$7) {
52098
+ if (notFound !== "absent") out[key] = notFound;
52099
+ return;
52100
+ }
52101
+ if (strict) throw err;
52102
+ this.logger.debug(safeMsg`readCurrentState: reading ${key} failed: ${describeAwsFailure(err).detail} — leaving it out`);
52103
+ }
52104
+ }
50280
52105
  async readNamespaceCurrentState(physicalId) {
50281
52106
  const [tableBucketARN, namespaceName] = physicalId.split("|");
50282
52107
  if (!tableBucketARN || !namespaceName) return void 0;
@@ -50350,8 +52175,62 @@ var S3TablesProvider = class {
50350
52175
  result["Format"] = resp.format;
50351
52176
  }
50352
52177
  result["Tags"] = resp.tableARN ? await this.readTagsBestEffort(resp.tableARN) : [];
52178
+ Object.assign(result, await this.readTableMaintenance(physicalId, {
52179
+ tableBucketARN,
52180
+ namespace,
52181
+ name
52182
+ }));
50353
52183
  return result;
50354
52184
  }
52185
+ async readTableMaintenance(physicalId, table, strict = false) {
52186
+ const result = {};
52187
+ let maintenance;
52188
+ let maintenanceRead = true;
52189
+ try {
52190
+ maintenance = (await this.getClient().send(new GetTableMaintenanceConfigurationCommand(table))).configuration;
52191
+ } catch (err) {
52192
+ maintenanceRead = err instanceof NotFoundException$7;
52193
+ if (!maintenanceRead) {
52194
+ if (strict) throw err;
52195
+ this.logger.debug(safeMsg`readCurrentState: reading the maintenance configuration of ${physicalId} failed: ` + safeMsg`${describeAwsFailure(err).detail} — leaving Compaction / SnapshotManagement out`);
52196
+ }
52197
+ }
52198
+ if (maintenanceRead) {
52199
+ const compaction = maintenance?.icebergCompaction;
52200
+ const compactionSettings = compaction?.settings && "icebergCompaction" in compaction.settings ? compaction.settings.icebergCompaction : void 0;
52201
+ result["Compaction"] = defined({
52202
+ Status: compaction?.status ?? "enabled",
52203
+ TargetFileSizeMB: compactionSettings?.targetFileSizeMB
52204
+ });
52205
+ const snapshots = maintenance?.icebergSnapshotManagement;
52206
+ const snapshotSettings = snapshots?.settings && "icebergSnapshotManagement" in snapshots.settings ? snapshots.settings.icebergSnapshotManagement : void 0;
52207
+ result["SnapshotManagement"] = defined({
52208
+ Status: snapshots?.status ?? "enabled",
52209
+ MinSnapshotsToKeep: snapshotSettings?.minSnapshotsToKeep,
52210
+ MaxSnapshotAgeHours: snapshotSettings?.maxSnapshotAgeHours
52211
+ });
52212
+ }
52213
+ return result;
52214
+ }
52215
+ async readUnsettled(physicalId, logicalId, resourceType, names) {
52216
+ let live;
52217
+ if (resourceType === "AWS::S3Tables::TableBucket") {
52218
+ try {
52219
+ await this.getClient().send(new GetTableBucketCommand({ tableBucketARN: physicalId }));
52220
+ } catch (err) {
52221
+ if (err instanceof NotFoundException$7) return RESOURCE_NOT_FOUND;
52222
+ throw err;
52223
+ }
52224
+ live = await this.readTableBucketConfigs(physicalId, true);
52225
+ } else if (resourceType === "AWS::S3Tables::Table") {
52226
+ const table = await this.resolveTableParts(physicalId);
52227
+ if (table === "not-found") return RESOURCE_NOT_FOUND;
52228
+ if (table === "unparseable") throw new ProvisioningError(compositeIdFormatMessage(S3_TABLES_TABLE_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
52229
+ if (await this.lookupTableArn(table.tableBucketARN, table.namespace, table.name) === null) return RESOURCE_NOT_FOUND;
52230
+ live = await this.readTableMaintenance(physicalId, table, true);
52231
+ } else return;
52232
+ return Object.fromEntries(Object.entries(live).filter(([key]) => names.has(key)));
52233
+ }
50355
52234
  async import(input) {
50356
52235
  switch (input.resourceType) {
50357
52236
  case "AWS::S3Tables::TableBucket": return this.importTableBucket(input);
@@ -50767,6 +52646,7 @@ var ECRProvider = class {
50767
52646
  ...tags ? { tags } : {}
50768
52647
  }));
50769
52648
  repositoryCreated = true;
52649
+ reportCreatedResource(logicalId, resourceType, repositoryName);
50770
52650
  const repo = response.repository;
50771
52651
  if (!repo?.repositoryName) throw new Error("CreateRepository did not return repository name");
50772
52652
  const lifecyclePolicy = properties["LifecyclePolicy"];
@@ -51495,6 +53375,7 @@ var ACMCertificateProvider = class {
51495
53375
  const certificateArn = (await this.acmClient.send(new RequestCertificateCommand(input))).CertificateArn;
51496
53376
  if (!certificateArn) throw new ProvisioningError(`RequestCertificate succeeded but no CertificateArn returned for ${logicalId}`, resourceType, logicalId);
51497
53377
  requestedArn = certificateArn;
53378
+ reportCreatedResource(logicalId, resourceType, certificateArn);
51498
53379
  this.logger.debug(`Requested ACM certificate: ${certificateArn}`);
51499
53380
  if (!(process.env["CDKD_NO_WAIT"] === "true")) await this.waitForCertificateIssued(certificateArn, logicalId, resourceType, dnsValidationZones(properties));
51500
53381
  else this.logger.warn(`Skipping wait for ACM certificate ${logicalId} (CDKD_NO_WAIT=true). Downstream consumers (CloudFront / ALB) will fail until the cert reaches ISSUED.`);
@@ -51508,6 +53389,7 @@ var ACMCertificateProvider = class {
51508
53389
  };
51509
53390
  } catch (error) {
51510
53391
  const survivorNote = requestedArn ? await this.cleanupRequestedCertificate(requestedArn, logicalId, idempotencyToken, context?.maskSecrets) : void 0;
53392
+ if (requestedArn && survivorNote === void 0) retractCreatedResource(logicalId, resourceType, requestedArn);
51511
53393
  if (error instanceof CdkdError) {
51512
53394
  if (survivorNote === void 0) throw error;
51513
53395
  const thrown = new ProvisioningError(`${error.message} ${survivorNote}`, resourceType, logicalId, requestedArn, error);
@@ -51796,4 +53678,4 @@ var ACMCertificateProvider = class {
51796
53678
 
51797
53679
  //#endregion
51798
53680
  export { ACMCertificateProvider, ASGProvider, AgentCoreBrowserProvider, AgentCoreCodeInterpreterProvider, AgentCoreEvaluatorProvider, AgentCoreRuntimeProvider, ApiGatewayProvider, ApiGatewayV2Provider, AppSyncProvider, BudgetsBudgetProvider, CloudFrontDistributionProvider, CloudFrontOACProvider, CloudFrontOAIProvider, CloudTrailProvider, CloudWatchAlarmProvider, CloudWatchAnomalyDetectorProvider, CodeBuildProvider, CodeCommitRepositoryProvider, CognitoUserPoolProvider, DLMLifecyclePolicyProvider, DocDBProvider, DocDBSubnetGroupProvider, DynamoDBGlobalTableProvider, DynamoDBTableProvider, EC2Provider, ECRProvider, ECSProvider, EFSProvider, ELBv2Provider, EMRClusterProvider, EMRInstanceFleetConfigProvider, EMRInstanceGroupConfigProvider, ElastiCacheProvider, EventBridgeBusProvider, EventBridgeRuleProvider, FSxFileSystemProvider, FirehoseProvider, GlueConnectionProvider, GlueCrawlerProvider, GlueJobProvider, GlueProvider, GlueSecurityConfigurationProvider, GlueTriggerProvider, GlueWorkflowProvider, IAMAccessKeyProvider, IAMInstanceProfileProvider, IAMManagedPolicyProvider, IAMPolicyProvider, IAMRoleProvider, IAMUserGroupProvider, KMSProvider, KinesisStreamConsumerProvider, KinesisStreamProvider, LambdaEventInvokeConfigProvider, LambdaEventSourceMappingProvider, LambdaFunctionProvider, LambdaLayerVersionProvider, LambdaMicrovmImageProvider, LambdaNetworkConnectorProvider, LambdaPermissionProvider, LambdaUrlProvider, LambdaWebFunctionEndpointProvider, LambdaWebFunctionRevisionProvider, LogsLogGroupProvider, NeptuneProvider, NestedStackProvider, PipesPipeProvider, RDSDBProxyEndpointProvider, RDSDBProxyProvider, RDSDBProxyTargetGroupProvider, RDSProvider, Route53Provider, S3BucketPolicyProvider, S3BucketProvider, S3DirectoryBucketProvider, S3TablesProvider, S3VectorsProvider, SNSSubscriptionProvider, SNSTopicPolicyProvider, SNSTopicProvider, SQSQueuePolicyProvider, SQSQueueProvider, SSMParameterProvider, SchedulerScheduleProvider, SecretsManagerSecretProvider, ServiceDiscoveryProvider, StepFunctionsProvider, WAFv2WebACLProvider, WaitConditionHandleProvider };
51799
- //# sourceMappingURL=provider-classes-KsrvwO_r.js.map
53681
+ //# sourceMappingURL=provider-classes-Dz0pYmlJ.js.map