@go-to-k/cdkd 0.290.25 → 0.290.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
- import { $ as secretBearing, $i as normalizeAwsError, $n as redactSecretsForState, $r as synthesisStatusMessage, $t as s3BucketArn, A as extractDeploymentEventError, An as repairMalformedResourcePropertiesForReadOnly, Ar as getBootstrapMarkerKey, At as isImpossibleEmptyStoredAttribute, B as toCfnInteger, Bn as STATE_SOURCED_READBACK_RULES, Br as describeDockerFailure, Bt as coerceCfnBoolean, C as buildFinalSnapshotIdentifier, Ci as processStackMessages, Cn as refuseMalformedNestedChildOutputs, Cr as createAssetRedirectResolver, Ct as isTerminationProtectionPropagationError, D as refusesFinalSnapshot, Di as getAwsClients, Dn as refuseMalformedResourcesForDestroy, Dr as BOOTSTRAP_MARKER_PREFIX, Dt as cfnRefValueFromPhysicalId, E as isFinalSnapshotError, Ei as AwsClients, En as refuseMalformedResourcePropertiesForOrphan, Er as AssetModeResolver, Et as carriesFetchableDynamicReference, F as isStatefulRecreateTargetSync, Fi as DeployCancelledError, Fn as withRetry, Fr as validateContainerRepoName, Ft as isSettledInstanceState, G as gray, Gi as ResourceTimeoutError, Gn as errorCauseChain, Gr as redactDockerArgvValues, Gt as readConfigString, H as formatResourceLine, Hi as NestedStackChildDirectDestroyError, Hn as carriesSecretMask, Hr as formatDockerLoginError, Ht as configBooleanRefusal, I as renderStatefulReason, Ii as DynamicReferenceRegionAmbiguousError, In as DagBuilder, Ir as buildDenyExternalAccessPolicy, It as WAFv2WebACLProvider, J as yellow, Ji as StackTerminationProtectionError, Jn as markSameGenerationBag, Jr as escapeRegExp, Jt as requireConfigObject, K as green, Ki as ResourceUpdateNotSupportedError, Kn as identityKeyFor, Kr as runDockerForeground, Kt as replayWarn, L as WARM_THROUGHPUT_MEMBERS, Li as IntrinsicResolutionRefusalError, Ln as TemplateParser, Lr as buildDockerImage, Lt as normalizeAwsTagsToCfn, M as planOrphanAdoption, Mn as findActionableSilentDrops, Mr as parseBootstrapMarker, Mt as isUnboundTemplateParameter, N as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ni as CrossAccountSecretRefusalError, Nn as findSilentDropProperties, Nr as readBootstrapMarkerBody, Nt as parameterTypeMayLoseSecretIdentity, O as unsupportedFinalSnapshotError, Oi as resetAwsClients, On as refuseMalformedState, Or as assertAssetBucketRegion, Ot as coerceParameterTypedValue, P as isStatefulRecreateTargetForReplace, Pn as describeTypeWithThrottleRetry, Pr as validateAssetBucketName, Pt as refStateLookupFromResource, Q as isExportAliasCollision, Qn as recoverMaskedOutput, Qr as Synthesizer, Qt as withSharedDrainBudget, R as coerceWarmThroughput$1, Ri as LocalInvokeBuildError$1, Rn as STATE_SOURCED_BASELINE_RULES, Rr as describeDockerCapturedOutput, Rt as resolveExplicitPhysicalId, S as PRE_DELETE_SNAPSHOT_TYPES, Si as AssemblyReader, Sn as refuseDivergentRecordRegionForDestroy, Sr as buildAssetRedirectMap, St as disableInstanceApiTermination, T as createPreDeleteFinalSnapshot, Ti as resolveBucketRegion, Tn as refuseMalformedOutputsForDestroy, Tr as rewriteTemplateAssetReferences, Tt as carriesDynamicReference, U as bold, Ui as PartialFailureError, Un as createSecretMasker, Ur as getDockerCmd, Ut as configIntegerRefusal, V as toFiniteNumber, Vi as MalformedProducerRecordRefusalError, Vn as TEMPLATE_SOURCED_RULES, Vr as dockerSpawnEnvWithSensitive, Vt as coerceCfnInteger, W as cyan, Wi as ProvisioningError, Wn as dynamicReferenceTokens, Wr as partitionSensitiveEnv, Wt as configStringRefusal, X as collectPublishedOutputNames, Xn as maskSecretsInText, Xr as AssetManifestLoader, Xt as classifyReplaySecretRegion, Y as collectDeclaredOutputNames, Yn as maskSecretsInError, Yr as stripControlChars, Yt as requireConfigString, Z as exportAliasCollisionScrubWarning, Zn as recordMaskOnlyValue, Zr as getDockerImageBySourceHash, Zt as producerRegionsFromState, _ as isSecretBearingReferenceString, _i as decodeListingKey, _n as malformedRenderedContainersWarning, _r as orphansCarriedFrom, _t as isWaitAbandonedError, a as DeploymentEventsStore, aa as markNonRetryable, ai as resolveSkipPrefix, an as applyCallerIdentityCredentials, ar as buildLockContentionMessage, at as clearOnUpdateRemoval, b as bindingSkippedOutputs, bi as canonicalizeRegion, bn as malformedStateRefusalMessage, br as stringifyValue, bt as deleteIndeterminateGuards, c as replayFailedOperations, ca as describeAwsFailure, ci as resolveUseCdkBootstrapAssets, cn as INTRINSIC_KEYS, cr as shellQuote, ct as createMaskedRetryLogger, d as updatePartialReason, di as CFN_TEMPLATE_BODY_LIMIT, dn as hasReadableResources, dr as exportNamesCarriedFrom, dt as beginCommandInterruptScope, ea as withErrorHandling, ei as getDefaultStateBucketName, en as s3BucketDomainName, er as scrubResourceRecord, et as secretBearingStateKeyWarning, f as withResourceDeadline, fi as CFN_TEMPLATE_URL_LIMIT, fn as malformedExportNamesWarning, fr as hasReadableExportSet, ft as endCommandInterruptScope, g as bagHoldsSecretExpression, gi as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, gn as malformedOutputsWarning, gr as orphansAfterRollback, gt as CloudControlProvider, h as computeImplicitDeleteEdges, hi as uploadCfnTemplate, hn as malformedOutputsRefusalMessage, hr as isReadableBag, ht as startInterruptWatch, i as DeploymentEventsReader, ia as isTransientServerError, ii as resolveCaptureObservedState, in as applyRoleArnIfSet, ir as buildForceUnlockCommand, it as collectInlinePolicyNamesManagedBySiblings, j as makeSiblingClaimReader, ji as CdkdError, jn as repairMalformedResourcesForReadOnly, jr as isCrossRegionRedirect, jt as isNamelessDynamicReferenceError, k as makeCanonicalizePropertiesFn, ki as setAwsClients, kn as repairMalformedOutputsForReadOnly, kr as ensureAssetStorage, kt as getAccountInfo, l as replayRollback, la as safeStringify, li as stateBucketExistenceConfirmed, ln as STATE_RESOURCES_MALFORMED, lr as CUSTOM_RESOURCE_RESPONSE_PREFIX, lt as maskDeep, m as IMPLICIT_DELETE_DEPENDENCIES, mi as findLargeInlineResources, mn as malformedLocalOutputsWarning, mr as importableOutputs, mt as isInterruptedWaitError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, na as isRetryableTransientError, ni as resolveApp, nn as s3BucketRegionalDomainName, nr as S3StateBackend, nt as getCurrentResourceSecrets, o as planFailedOps, oa as markRedactedCause, oi as resolveStateBucketWithDefault, on as callerEnvCredentials, or as forceQuitRecoveryClause, ot as ProviderRegistry, p as maskingRetryLogger, pi as MIGRATE_TMP_PREFIX, pn as malformedExportSourceWarning, pr as importableOutputKeys, pt as interruptWatchListenerCount, q as red, qi as StackHasActiveImportsError, qn as isSingleDynamicReferenceToken, qr as runDockerStreaming, qt as requireConfigArray, r as DeployEngine, ra as isThrottlingError, ri as resolveAutoAssetStorage, rn as s3BucketWebsiteUrl, rr as rebuildClientForBucketRegion, rt as IAMRoleProvider, s as planRollback, sa as retryClassificationText, si as resolveStateBucketWithDefaultAndSource, sn as DiffCalculator, sr as formatLockExpiry, st as wouldReturnToSdkProvider, t as DEFAULT_RESOURCE_TIMEOUT_MS, ta as isMarkedNonRetryable, ti as getLegacyStateBucketName, tn as s3BucketDualStackDomainName, tr as LockManager, tt as secretSafeKeyDisplay, u as updatePartialMessage, ui as warnDeprecatedNoPrefixCliFlag, un as hasReadableOutputs, ur as DEFAULT_STATE_PREFIX, ut as maskerOrIdentity, v as keptWholeReasonText, vi as expectedOwnerParam, vn as malformedResourcePropertiesWarning, vr as shouldRetainResource, vt as slowCcOperationTimeoutMs, w as ccRoutedFinalSnapshotError, wn as refuseMalformedOutputs, wr as loadPublishableAssetManifest, wt as IntrinsicFunctionResolver, x as ATOMIC_FINAL_SNAPSHOT_TYPES, xi as derivePartitionAndUrlSuffix, xn as producerRecordKey, xr as WorkGraph, xt as deleteSkipReason, y as mergeNoChangeOutputs, yi as PARTITION_TABLE, yn as malformedResourcesWarning, yr as AssetPublisher, yt as UNSPECIFIED_SKIP_REASON, z as isWarmThroughputDecrease, zi as LocalStartServiceError, zn as STATE_SOURCED_CROSS_GENERATION_RULES, zr as describeDockerExecFailure, zt as assertRegionMatch } from "./deploy-engine-DtFiFMxh.js";
1
+ import { $ as secretBearing, $n as recordMaskOnlyValue, $r as getDockerImageBySourceHash, $t as s3BucketArn, A as extractDeploymentEventError, Ai as resetAwsClients, An as refuseMalformedState, Ar as assertAssetBucketRegion, At as isImpossibleEmptyStoredAttribute, B as toCfnInteger, Bi as LocalInvokeBuildError$1, Bn as STATE_SOURCED_BASELINE_RULES, Br as describeDockerCapturedOutput, Bt as coerceCfnBoolean, C as buildFinalSnapshotIdentifier, Ci as derivePartitionAndUrlSuffix, Cn as producerRecordKey, Cr as WorkGraph, Ct as isTerminationProtectionPropagationError, D as refusesFinalSnapshot, Di as resolveBucketRegion, Dn as refuseMalformedOutputsForDestroy, Dr as rewriteTemplateAssetReferences, Dt as cfnRefValueFromPhysicalId, E as isFinalSnapshotError, En as refuseMalformedOutputs, Er as loadPublishableAssetManifest, Et as carriesFetchableDynamicReference, F as isStatefulRecreateTargetSync, Fi as CrossAccountSecretRefusalError, Fn as findSilentDropProperties, Fr as readBootstrapMarkerBody, Ft as isSettledInstanceState, G as gray, Gi as PartialFailureError, Gn as createSecretMasker, Gr as getDockerCmd, Gt as readConfigString, H as formatResourceLine, Hn as STATE_SOURCED_READBACK_RULES, Hr as describeDockerFailure, Ht as configBooleanRefusal, I as renderStatefulReason, In as describeTypeWithThrottleRetry, Ir as validateAssetBucketName, It as WAFv2WebACLProvider, J as yellow, Ji as ResourceUpdateNotSupportedError, Jn as identityKeyFor, Jr as runDockerForeground, Jt as requireConfigObject, K as green, Ki as ProvisioningError, Kn as dynamicReferenceTokens, Kr as partitionSensitiveEnv, Kt as replayWarn, L as WARM_THROUGHPUT_MEMBERS, Li as DeployCancelledError, Ln as withRetry, Lr as validateContainerRepoName, Lt as normalizeAwsTagsToCfn, M as planOrphanAdoption, Mn as repairMalformedResourcePropertiesForReadOnly, Mr as getBootstrapMarkerKey, Mt as isUnboundTemplateParameter, N as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ni as CdkdError, Nn as repairMalformedResourcesForReadOnly, Nr as isCrossRegionRedirect, Nt as parameterTypeMayLoseSecretIdentity, O as unsupportedFinalSnapshotError, Oi as AwsClients, On as refuseMalformedResourcePropertiesForOrphan, Or as AssetModeResolver, Ot as coerceParameterTypedValue, P as isStatefulRecreateTargetForReplace, Pn as findActionableSilentDrops, Pr as parseBootstrapMarker, Pt as refStateLookupFromResource, Q as isExportAliasCollision, Qn as maskSecretsInText, Qr as AssetManifestLoader, Qt as withSharedDrainBudget, R as coerceWarmThroughput$1, Ri as DynamicReferenceRegionAmbiguousError, Rn as DagBuilder, Rr as buildDenyExternalAccessPolicy, Rt as resolveExplicitPhysicalId, S as PRE_DELETE_SNAPSHOT_TYPES, Si as canonicalizeRegion, Sn as malformedStateRefusalMessage, Sr as stringifyValue, St as disableInstanceApiTermination, T as createPreDeleteFinalSnapshot, Ti as processStackMessages, Tn as refuseMalformedNestedChildOutputs, Tr as createAssetRedirectResolver, Tt as carriesDynamicReference, U as bold, Ui as MalformedProducerRecordRefusalError, Un as TEMPLATE_SOURCED_RULES, Ur as dockerSpawnEnvWithSensitive, Ut as configIntegerRefusal, V as toFiniteNumber, Vi as LocalStartServiceError, Vn as STATE_SOURCED_CROSS_GENERATION_RULES, Vr as describeDockerExecFailure, Vt as coerceCfnInteger, W as cyan, Wi as NestedStackChildDirectDestroyError, Wn as carriesSecretMask, Wr as formatDockerLoginError, Wt as configStringRefusal, X as collectPublishedOutputNames, Xi as StackTerminationProtectionError, Xn as markSameGenerationBag, Xr as escapeRegExp, Xt as classifyReplaySecretRegion, Y as collectDeclaredOutputNames, Yi as StackHasActiveImportsError, Yn as isSingleDynamicReferenceToken, Yr as runDockerStreaming, Yt as requireConfigString, Z as exportAliasCollisionScrubWarning, Zn as maskSecretsInError, Zr as stripControlChars, Zt as producerRegionsFromState, _ as isSecretBearingReferenceString, _i as uploadCfnTemplate, _n as malformedOutputsRefusalMessage, _r as isReadableBag, _t as isWaitAbandonedError, a as DeploymentEventsStore, aa as isThrottlingError, ai as resolveAutoAssetStorage, an as AWS_CREDENTIAL_ENV_KEYS, ar as rebuildClientForBucketRegion, at as clearOnUpdateRemoval, b as bindingSkippedOutputs, bi as expectedOwnerParam, bn as malformedResourcePropertiesWarning, br as shouldRetainResource, bt as deleteIndeterminateGuards, c as replayFailedOperations, ca as markRedactedCause, ci as resolveStateBucketWithDefault, cn as envCredentialsAreAssumedRole, cr as forceQuitRecoveryClause, ct as createMaskedRetryLogger, d as updatePartialReason, da as safeStringify, di as stateBucketExistenceConfirmed, dn as STATE_RESOURCES_MALFORMED, dr as CUSTOM_RESOURCE_RESPONSE_PREFIX, dt as beginCommandInterruptScope, ei as Synthesizer, en as s3BucketDomainName, er as recoverMaskedOutput, et as secretBearingStateKeyWarning, f as withResourceDeadline, fi as warnDeprecatedNoPrefixCliFlag, fn as hasReadableOutputs, fr as DEFAULT_STATE_PREFIX, ft as endCommandInterruptScope, g as bagHoldsSecretExpression, gi as findLargeInlineResources, gn as malformedLocalOutputsWarning, gr as importableOutputs, gt as CloudControlProvider, h as computeImplicitDeleteEdges, hi as MIGRATE_TMP_PREFIX, hn as malformedExportSourceWarning, hr as importableOutputKeys, ht as startInterruptWatch, i as DeploymentEventsReader, ia as isRetryableTransientError, ii as resolveApp, in as applyRoleArnIfSet, ir as S3StateBackend, it as collectInlinePolicyNamesManagedBySiblings, j as makeSiblingClaimReader, ji as setAwsClients, jn as repairMalformedOutputsForReadOnly, jr as ensureAssetStorage, jt as isNamelessDynamicReferenceError, k as makeCanonicalizePropertiesFn, ki as getAwsClients, kn as refuseMalformedResourcesForDestroy, kr as BOOTSTRAP_MARKER_PREFIX, kt as getAccountInfo, l as replayRollback, la as retryClassificationText, li as resolveStateBucketWithDefaultAndSource, ln as DiffCalculator, lr as formatLockExpiry, lt as maskDeep, m as IMPLICIT_DELETE_DEPENDENCIES, mi as CFN_TEMPLATE_URL_LIMIT, mn as malformedExportNamesWarning, mr as hasReadableExportSet, mt as isInterruptedWaitError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, na as withErrorHandling, ni as getDefaultStateBucketName, nn as s3BucketRegionalDomainName, nr as scrubResourceRecord, nt as getCurrentResourceSecrets, o as planFailedOps, oa as isTransientServerError, oi as resolveCaptureObservedState, on as applyCallerIdentityCredentials, or as buildForceUnlockCommand, ot as ProviderRegistry, p as maskingRetryLogger, pi as CFN_TEMPLATE_BODY_LIMIT, pn as hasReadableResources, pr as exportNamesCarriedFrom, pt as interruptWatchListenerCount, q as red, qi as ResourceTimeoutError, qn as errorCauseChain, qr as redactDockerArgvValues, qt as requireConfigArray, r as DeployEngine, ra as isMarkedNonRetryable, ri as getLegacyStateBucketName, rn as s3BucketWebsiteUrl, rr as LockManager, rt as IAMRoleProvider, s as planRollback, sa as markNonRetryable, si as resolveSkipPrefix, sn as callerEnvCredentials, sr as buildLockContentionMessage, st as wouldReturnToSdkProvider, t as DEFAULT_RESOURCE_TIMEOUT_MS, ta as normalizeAwsError, ti as synthesisStatusMessage, tn as s3BucketDualStackDomainName, tr as redactSecretsForState, tt as secretSafeKeyDisplay, u as updatePartialMessage, ua as describeAwsFailure, ui as resolveUseCdkBootstrapAssets, un as INTRINSIC_KEYS, ur as shellQuote, ut as maskerOrIdentity, v as keptWholeReasonText, vi as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, vn as malformedOutputsWarning, vr as orphansAfterRollback, vt as slowCcOperationTimeoutMs, w as ccRoutedFinalSnapshotError, wi as AssemblyReader, wn as refuseDivergentRecordRegionForDestroy, wr as buildAssetRedirectMap, wt as IntrinsicFunctionResolver, x as ATOMIC_FINAL_SNAPSHOT_TYPES, xi as PARTITION_TABLE, xn as malformedResourcesWarning, xr as AssetPublisher, xt as deleteSkipReason, y as mergeNoChangeOutputs, yi as decodeListingKey, yn as malformedRenderedContainersWarning, yr as orphansCarriedFrom, yt as UNSPECIFIED_SKIP_REASON, z as isWarmThroughputDecrease, zi as IntrinsicResolutionRefusalError, zn as TemplateParser, zr as buildDockerImage, zt as assertRegionMatch } from "./deploy-engine-CA70_Rji.js";
2
2
  import { C as displaySafe, S as displayIdent, _ as withSkipPrefix, b as STACK_REF_MAX_CODE_POINTS, c as PATTERN_B_NAME_OPTIONS, f as explicitNamePropertyFor, h as getCurrentStackName, i as reserveStdoutForPayload, l as PATTERN_B_NAME_PROPERTIES, m as generateResourceNameWithFallback, n as getLogger, o as runStackBuffered, p as generateResourceName, s as getLiveRenderer, u as PATTERN_B_RESOURCE_TYPES, v as withStackName, w as truncateCodePoints, x as UNRENDERABLE } from "./logger-CkHVgzLx.js";
3
3
  import { n as awsClientDefaults } from "./aws-client-defaults-BoIxyCir.js";
4
- import { t as getCdkdVersion } from "./version-CduXA6UL.js";
5
- import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-DL62n7uG.js";
4
+ import { t as getCdkdVersion } from "./version-CWpPiHCn.js";
5
+ import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-Dn1f0SJi.js";
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
7
  import { createHash, randomBytes, randomUUID } from "node:crypto";
8
8
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, NotFound, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -17849,6 +17849,105 @@ const PROVISIONED_CAPACITY_MEMBERS = ["ReadCapacityUnits", "WriteCapacityUnits"]
17849
17849
  */
17850
17850
  const ON_DEMAND_CEILING_MEMBERS = ["MaxReadRequestUnits", "MaxWriteRequestUnits"];
17851
17851
  /**
17852
+ * Narrow ONE `OnDemandThroughput` block to exactly what cdkd puts on the wire,
17853
+ * and report the members that were DROPPED (issue
17854
+ * [#3287](https://github.com/go-to-k/cdkd/issues/3287)).
17855
+ *
17856
+ * The ONE spelling of that transform, and that is the whole reason it is a
17857
+ * module-level PURE function rather than a method: two callers need the same
17858
+ * answer and one of them may not log.
17859
+ *
17860
+ * - {@link DynamoDBTableProvider.coerceOnDemandCeilingsForSend} — the WIRE, at
17861
+ * all six send sites, which announces each `dropped` member;
17862
+ * - {@link DynamoDBTableProvider.indexCeilingForSend}'s per-index change
17863
+ * detector, which compares the desired and recorded blocks and must not warn
17864
+ * (the warning is the send's, one line further down, and a detector that
17865
+ * announced would fire on a comparison that emits no op at all).
17866
+ *
17867
+ * Behaviour is BYTE-identical to what `coerceOnDemandCeilingsForSend` did
17868
+ * inline before issue [#3287](https://github.com/go-to-k/cdkd/issues/3287),
17869
+ * including the two identity properties its callers
17870
+ * depend on: a block that is not a plain object comes back AS ITSELF (AWS names
17871
+ * the shape), and a block needing no rewrite comes back by IDENTITY, so
17872
+ * {@link DynamoDBTableProvider.coerceIndexThroughputForCreate} can still tell
17873
+ * "unchanged" from "rewritten" without a structural compare. A member name the
17874
+ * grammar does not know is preserved untouched, the same fail-open direction.
17875
+ *
17876
+ * `dropped` carries the RAW declared value beside each member name because the
17877
+ * warning interpolates it, and only the masking caller may do that.
17878
+ */
17879
+ function narrowOnDemandCeilings(declared) {
17880
+ if (!isPlainCapacityBlock(declared)) return {
17881
+ block: declared,
17882
+ dropped: []
17883
+ };
17884
+ const block = declared;
17885
+ let rewritten;
17886
+ const dropped = [];
17887
+ for (const member of ON_DEMAND_CEILING_MEMBERS) {
17888
+ if (!(member in block)) continue;
17889
+ const coerced = coerceCfnInteger(block[member]);
17890
+ if (coerced === block[member] && coerced !== void 0) continue;
17891
+ rewritten ??= { ...block };
17892
+ if (coerced === void 0) {
17893
+ dropped.push({
17894
+ member,
17895
+ raw: block[member]
17896
+ });
17897
+ delete rewritten[member];
17898
+ } else rewritten[member] = coerced;
17899
+ }
17900
+ return {
17901
+ block: rewritten ?? block,
17902
+ dropped
17903
+ };
17904
+ }
17905
+ /**
17906
+ * Does the ceiling AWS currently holds for a GSI already cover every member
17907
+ * cdkd is about to send? (issue
17908
+ * [#3287](https://github.com/go-to-k/cdkd/issues/3287))
17909
+ *
17910
+ * The `OnDemandThroughput` twin of {@link liveCapacityAlreadyMatches}, and it
17911
+ * exists for the same #1630 reason: cdkd writes state only after `update()`
17912
+ * RETURNS, so a ceiling op that already LANDED before a later step threw is
17913
+ * unrecorded and the next deploy re-emits it. Consulting what AWS holds is what
17914
+ * lets the deploy converge instead.
17915
+ *
17916
+ * Compared PER MEMBER over the members being SENT, deliberately, and it differs
17917
+ * from the capacity sibling in exactly that: omitting a ceiling member KEEPS
17918
+ * whatever maximum the table already carries (only an explicit `-1` removes
17919
+ * one), so a member cdkd is not sending says nothing about whether the call is
17920
+ * redundant. A live member the request does not carry is therefore ignored.
17921
+ *
17922
+ * Anything unreadable on EITHER side fails OPEN (the op is still emitted): an
17923
+ * absent live block — which is also what `DescribeTable` reports for a
17924
+ * PROVISIONED table, where the numbers do not exist — an absent live member, a
17925
+ * value that is not a number. That is the correct direction: the worst case is
17926
+ * the pre-fix behaviour of emitting the op, whereas a false MATCH would
17927
+ * silently drop a ceiling change the user asked for.
17928
+ *
17929
+ * **What AWS does with a repeated identical ceiling is UNMEASURED**, and this
17930
+ * guard is written so that it does not matter: it withholds only a call that
17931
+ * would change nothing. If DynamoDB rejects the repeat the way it rejects a
17932
+ * repeated provisioned capacity ("The provisioned throughput for the index X
17933
+ * will not change"), the guard is what keeps the deploy from wedging; if it
17934
+ * accepts it, the guard saves a call and a full index-ACTIVE wait. Measuring it
17935
+ * would sharpen the doc, not the code.
17936
+ */
17937
+ function liveCeilingAlreadyMatches(live, requested) {
17938
+ if (live === void 0) return false;
17939
+ const liveBag = live;
17940
+ let compared = 0;
17941
+ for (const member of ON_DEMAND_CEILING_MEMBERS) {
17942
+ if (!(member in requested)) continue;
17943
+ const want = toFiniteNumber(requested[member]);
17944
+ const have = toFiniteNumber(liveBag[member]);
17945
+ if (want === void 0 || have === void 0 || want !== have) return false;
17946
+ compared += 1;
17947
+ }
17948
+ return compared > 0;
17949
+ }
17950
+ /**
17852
17951
  * The capacity both TABLE-level forwarders substitute for a member the
17853
17952
  * template does not declare at all — `create()`'s `CreateTable` and the
17854
17953
  * BillingMode flip's `UpdateTable`. It is the pre-issue-#3147 `?? 5`, kept
@@ -18313,15 +18412,27 @@ function reverseMapSecondaryIndex(live, desired, bagInformative) {
18313
18412
  * [#3255](https://github.com/go-to-k/cdkd/issues/3255), across ALL FOUR
18314
18413
  * per-index send sites (`create()`'s mapper plus `applyGsiUpdates`'
18315
18414
  * adopted-repair, `Create` and same-name `Update` actions);
18316
- * `OnDemandThroughput`'s from issue
18317
- * [#3265](https://github.com/go-to-k/cdkd/issues/3265), across its own four
18318
- * (the two TABLE-level forwards plus `create()`'s mapper and
18319
- * `applyGsiUpdates`' `Create` action) — ever SUPPRESSES the block: each
18415
+ * `OnDemandThroughput`'s from issues
18416
+ * [#3265](https://github.com/go-to-k/cdkd/issues/3265) and
18417
+ * [#3287](https://github.com/go-to-k/cdkd/issues/3287), across its own SIX
18418
+ * (the two TABLE-level forwards, `create()`'s mapper, and all three
18419
+ * `applyGsiUpdates` actions) — ever SUPPRESSES the block: each
18320
18420
  * rewrites members inside a block that is sent either way, so "would cdkd
18321
18421
  * send this block" is unchanged by them, even where both members were
18322
18422
  * dropped and an empty block goes out. That is the reason
18323
18423
  * {@link DynamoDBTableProvider.coerceOnDemandCeilingsForSend} drops a
18324
- * MEMBER and never the block. Widening
18424
+ * MEMBER and never the block.
18425
+ *
18426
+ * **The two per-index UPDATE arms added by #3287 are the ONE qualification,
18427
+ * and it does not reach this predicate.**
18428
+ * {@link DynamoDBTableProvider.indexCeilingForSend} withholds the MEMBER
18429
+ * from an `UpdateGlobalSecondaryIndexAction` when nothing survived, because
18430
+ * an action carrying only `IndexName` is rejected outright — where the
18431
+ * TABLE-level arm sends the empty block and lets DynamoDB answer. That is a
18432
+ * statement about one OP, not about whether the template declared a block
18433
+ * cdkd forwards: the same bag still reaches `CreateTable` / `UpdateTable`
18434
+ * wherever those arms run, so the predicate's answer is unchanged.
18435
+ * Widening
18325
18436
  * `isSendableWarmThroughput` over them would be wrong twice over — it reads
18326
18437
  * members those blocks do not have (`ReadUnitsPerSecond` vs
18327
18438
  * `ReadCapacityUnits`), so every declared capacity would read as undeclared.
@@ -18632,8 +18743,8 @@ var DynamoDBTableProvider = class {
18632
18743
  if (tableCapacityUnusable) reasons.push(`the template declares no usable table-level ProvisionedThroughput`);
18633
18744
  warn(`AWS::DynamoDB::Table ${logicalId}: this deploy removes global secondary index(es) ${removable.map((n) => maskSecrets(n)).join(", ")} and flips BillingMode to PROVISIONED. The removal would normally be applied BEFORE the flip, but ${reasons.join(" and ")}, so AWS rejects the flip either way. Nothing was removed.`);
18634
18745
  } else if (removable.length > 0) {
18635
- this.logger.debug(`Deleting GSI(s) ${removable.join(", ")} on DynamoDB table ${physicalId} before the BillingMode flip to PROVISIONED`);
18636
- await this.runGsiOps(physicalId, removable.map((name) => ({ Delete: { IndexName: name } })), void 0);
18746
+ debug(`Deleting GSI(s) ${removable.join(", ")} on DynamoDB table ${physicalId} before the BillingMode flip to PROVISIONED`);
18747
+ await this.runGsiOps(physicalId, removable.map((name) => ({ Delete: { IndexName: name } })), void 0, maskSecrets);
18637
18748
  for (const name of removable) preFlipDeletedIndexNames.add(name);
18638
18749
  }
18639
18750
  }
@@ -19318,6 +19429,7 @@ var DynamoDBTableProvider = class {
19318
19429
  */
19319
19430
  async applyGsiUpdates(physicalId, resourceType, logicalId, previousGsis, desiredGsis, desiredAttributeDefinitions, handledByBillingFlip = /* @__PURE__ */ new Set(), liveIndexNames, liveIndexByName, liveCapacityComparable = false, maskSecrets = (text) => text) {
19320
19431
  const warn = (message) => this.logger.warn(maskSecrets(message));
19432
+ const debug = (message) => this.logger.debug(maskSecrets(message));
19321
19433
  const prev = previousGsis ?? [];
19322
19434
  const desired = desiredGsis ?? [];
19323
19435
  const prevByName = new Map(prev.filter((g) => g.IndexName).map((g) => [g.IndexName, g]));
@@ -19326,7 +19438,7 @@ var DynamoDBTableProvider = class {
19326
19438
  for (const name of prevByName.keys()) {
19327
19439
  if (desiredByName.has(name)) continue;
19328
19440
  if (liveIndexNames !== void 0 && !liveIndexNames.has(name)) {
19329
- this.logger.debug(`GSI ${name} is recorded in cdkd state but not live on DynamoDB table ${physicalId}; skipping its Delete (already removed)`);
19441
+ debug(`${this.indexScopeAt(name, physicalId, maskSecrets)} is recorded in cdkd state but not live; skipping its Delete (already removed)`);
19330
19442
  continue;
19331
19443
  }
19332
19444
  ops.push({ Delete: { IndexName: name } });
@@ -19335,7 +19447,7 @@ var DynamoDBTableProvider = class {
19335
19447
  const live = liveIndexByName?.get(name);
19336
19448
  if (!gsi.KeySchema) throw new ProvisioningError(`GlobalSecondaryIndex ${name} on DynamoDB table ${logicalId} is missing KeySchema`, resourceType, logicalId, physicalId);
19337
19449
  if (live !== void 0 && live.IndexStatus !== "DELETING") {
19338
- warn(`GSI ${maskSecrets(name)} on DynamoDB table ${physicalId} already exists in AWS 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.`);
19450
+ warn(`${this.indexScopeAt(name, physicalId, maskSecrets)} already exists in AWS 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.`);
19339
19451
  const adopted = { IndexName: name };
19340
19452
  let adoptedHasMember = false;
19341
19453
  if (liveCapacityComparable && gsi.ProvisionedThroughput && !handledByBillingFlip.has(name) && !liveCapacityAlreadyMatches(live.ProvisionedThroughput, gsi.ProvisionedThroughput) && !this.skipZeroCapacityIndexUpdate(name, physicalId, gsi.ProvisionedThroughput, maskSecrets)) {
@@ -19347,8 +19459,13 @@ var DynamoDBTableProvider = class {
19347
19459
  adopted.WarmThroughput = adoptedWarm;
19348
19460
  adoptedHasMember = true;
19349
19461
  }
19462
+ const adoptedCeiling = this.indexCeilingForSend(name, physicalId, gsi, void 0, live, maskSecrets);
19463
+ if (adoptedCeiling) {
19464
+ adopted.OnDemandThroughput = adoptedCeiling;
19465
+ adoptedHasMember = true;
19466
+ }
19350
19467
  if (adoptedHasMember) ops.push({ Update: adopted });
19351
- warn(`GSI ${maskSecrets(name)} was adopted from AWS rather than created, so cdkd could not verify its KeySchema / Projection match the template. Run \`cdkd drift ${physicalId}\` to confirm, and \`cdkd drift --accept\` if AWS is the source of truth.`);
19468
+ warn(`${this.indexScopeAt(name, physicalId, maskSecrets)} was adopted from AWS rather than created, so cdkd could not verify its KeySchema / Projection match the template. Run \`cdkd drift ${physicalId}\` to confirm, and \`cdkd drift --accept\` if AWS is the source of truth.`);
19352
19469
  continue;
19353
19470
  }
19354
19471
  this.warnRefusedWarmThroughput(this.indexScopeAt(name, physicalId, maskSecrets), gsi.WarmThroughput, maskSecrets);
@@ -19367,7 +19484,7 @@ var DynamoDBTableProvider = class {
19367
19484
  const update = { IndexName: name };
19368
19485
  let updateHasMember = false;
19369
19486
  if (gsi.ProvisionedThroughput && !handledByBillingFlip.has(name) && JSON.stringify(before.ProvisionedThroughput) !== JSON.stringify(gsi.ProvisionedThroughput)) {
19370
- if (liveCapacityComparable && liveCapacityAlreadyMatches(liveIndexByName?.get(name)?.ProvisionedThroughput, gsi.ProvisionedThroughput)) this.logger.debug(`GSI ${name} on DynamoDB table ${physicalId} already carries the requested capacity in AWS; skipping its throughput Update`);
19487
+ if (liveCapacityComparable && liveCapacityAlreadyMatches(liveIndexByName?.get(name)?.ProvisionedThroughput, gsi.ProvisionedThroughput)) debug(`${this.indexScopeAt(name, physicalId, maskSecrets)} already carries the requested capacity in AWS; skipping its throughput Update`);
19371
19488
  else if (!this.skipZeroCapacityIndexUpdate(name, physicalId, gsi.ProvisionedThroughput, maskSecrets)) {
19372
19489
  update.ProvisionedThroughput = this.indexCapacityForSend(this.indexScopeAt(name, physicalId, maskSecrets), gsi.ProvisionedThroughput, maskSecrets);
19373
19490
  updateHasMember = true;
@@ -19378,9 +19495,14 @@ var DynamoDBTableProvider = class {
19378
19495
  update.WarmThroughput = warm;
19379
19496
  updateHasMember = true;
19380
19497
  }
19498
+ const ceiling = this.indexCeilingForSend(name, physicalId, gsi, before, liveIndexByName?.get(name), maskSecrets);
19499
+ if (ceiling) {
19500
+ update.OnDemandThroughput = ceiling;
19501
+ updateHasMember = true;
19502
+ }
19381
19503
  if (updateHasMember) ops.push({ Update: update });
19382
19504
  }
19383
- await this.runGsiOps(physicalId, ops, desiredAttributeDefinitions);
19505
+ await this.runGsiOps(physicalId, ops, desiredAttributeDefinitions, maskSecrets);
19384
19506
  }
19385
19507
  /**
19386
19508
  * The NUMERIC `WarmThroughput` spec to put on the wire, announcing any member
@@ -19467,6 +19589,136 @@ var DynamoDBTableProvider = class {
19467
19589
  return this.coerceIndexCapacityBlock(scope, declared, maskSecrets) ?? declared;
19468
19590
  }
19469
19591
  /**
19592
+ * The per-index `OnDemandThroughput` an `UpdateGlobalSecondaryIndexAction`
19593
+ * should carry, or `undefined` when no op is warranted (issue
19594
+ * [#3287](https://github.com/go-to-k/cdkd/issues/3287)).
19595
+ *
19596
+ * The whole of that issue: `UpdateGlobalSecondaryIndexAction` declares the
19597
+ * member, the SDK has always accepted it, and NEITHER update arm ever set it
19598
+ * — while the `Create` action arm has carried it since issue #3265. So one
19599
+ * template applied a per-index ceiling when the index was ADDED and silently
19600
+ * discarded a later edit to it, recording the new value as applied so no
19601
+ * later deploy converged. Routed through
19602
+ * {@link coerceOnDemandCeilingsForSend} like the other five sites, so one
19603
+ * template value cannot be answered differently at two of them.
19604
+ *
19605
+ * FIVE decisions, in the order the op is decided, plus one deliberate NON-decision:
19606
+ *
19607
+ * - **The declaration gate is TRUTHINESS**, matching `indexDeclares`' gate
19608
+ * for this block and the `Create` arm's `gsi.OnDemandThroughput` test. A
19609
+ * tighter gate here would make the #1767 drift-side predicate answer
19610
+ * "declared" for a block this arm skips.
19611
+ * - **The BILLING MODE is deliberately NOT gated here**, and that is a
19612
+ * decision with a filed issue rather than an oversight. DynamoDB accepts
19613
+ * `OnDemandThroughput` only on a PAY_PER_REQUEST table, and the ceiling
19614
+ * rides the SAME action as the capacity — so on a PROVISIONED one the op
19615
+ * is rejected and takes an otherwise-valid capacity edit with it. A gate
19616
+ * WITHHOLDING the member was written for the go-to-k/cdkd#3380 review's M1
19617
+ * and REVERTED on the next round: a mid-update warn-and-skip lets the
19618
+ * deploy succeed, so the engine records the un-sent ceiling as applied and
19619
+ * the deploy that FIXES the billing mode then compares equal and never
19620
+ * sends it — issue #3287's own defect, reintroduced by its own guard. The
19621
+ * file's table-level twin states that reasoning at its own refusal. The
19622
+ * correct shape is a PRE-FLIGHT refusal covering all THREE per-index send
19623
+ * sites (the `Create` arm has carried the same defect since #3265), which
19624
+ * is issue [#3392](https://github.com/go-to-k/cdkd/issues/3392). Until
19625
+ * then AWS validates the constraint, exactly as it does for the
19626
+ * TABLE-level arm and for the `Create` action.
19627
+ *
19628
+ * **That IS a behaviour change on this shape, stated rather than glossed**
19629
+ * (round 3's C4). Before #3287 neither `Update` arm sent the member, so a
19630
+ * PROVISIONED table declaring a per-index ceiling deployed GREEN with the
19631
+ * ceiling silently ignored. Now the same-name arm sends it once the
19632
+ * ceiling CHANGES, AWS rejects the `UpdateTable`, and the co-resident
19633
+ * capacity / warm edits fail with it; the adopted-index arm has no
19634
+ * recorded previous side to gate on, so it re-fails every deploy until the
19635
+ * template is edited. Loud and recording nothing beats silent and lost,
19636
+ * which is why it ships this way rather than behind the withdrawn skip —
19637
+ * but it is a change, and #3392 is what makes it a cdkd-worded refusal
19638
+ * before any call instead of an AWS rejection after one.
19639
+ * - **The change detector compares the NARROWED blocks WHEN NOTHING WAS
19640
+ * DROPPED, and the RAW ones otherwise** — the one place it differs from its
19641
+ * `ProvisionedThroughput` sibling two arms up. `'200'` and `200` are the
19642
+ * SAME ceiling once coerced, so a purely narrowed detector saves an
19643
+ * `UpdateGlobalSecondaryIndexAction` — and a full index-ACTIVE wait — on a
19644
+ * re-spelling. But narrowing ALONE conceals a case the raw compare reports
19645
+ * (the go-to-k/cdkd#3380 spec review's Drift 2): two DIFFERENT rejected
19646
+ * spellings (`' 25 '` against a recorded `' 30 '`) narrow to the same
19647
+ * block, so the arm would return before {@link
19648
+ * DynamoDBTableProvider.coerceOnDemandCeilingsForSend} ever warns and the
19649
+ * user would stop being told what to fix — exactly what
19650
+ * `.claude/rules/provider-diff-record-folds.md` refuses and what keeps the
19651
+ * TABLE-level detector raw. So the discriminator is
19652
+ * {@link narrowOnDemandCeilings}'s OWN `dropped` report: any drop on either
19653
+ * side falls back to the RAW compare, which keeps the announcement alive
19654
+ * while the narrowed compare still absorbs a lossless re-spelling.
19655
+ * - **A block with no SURVIVING GRAMMAR MEMBER emits NO op, and WARNS.** The TABLE-level
19656
+ * arm sends the empty block and lets DynamoDB answer; here the action can
19657
+ * legitimately carry nothing but `IndexName`, which AWS rejects outright,
19658
+ * so an empty ceiling would take a capacity edit down with it or fire a
19659
+ * doomed call on its own. The test is per MEMBER and not
19660
+ * `Object.keys(...).length === 0` (the go-to-k/cdkd#3380 review's m2):
19661
+ * {@link narrowOnDemandCeilings} preserves an unknown member name by
19662
+ * design, so a typo'd `MaxReadRequestUnit` leaves a non-empty object that
19663
+ * the SDK serializes to `{}` — the same rejected action, reached past a
19664
+ * key-count gate. The fail-open "let AWS name it" reasoning does NOT reach
19665
+ * that shape here, because AWS never sees the name to name. The warning is
19666
+ * this arm's OWN, not {@link warnUnusableOnDemandCeiling}'s (round 2's C3):
19667
+ * an unknown member is never DROPPED by the narrowing — its name is
19668
+ * preserved — so the per-member announcement says nothing about it, and
19669
+ * withholding silently would lose the ceiling where the pre-gate code at
19670
+ * least failed loudly at AWS.
19671
+ * - **A block that is not a plain object is forwarded VERBATIM**, the
19672
+ * fail-open direction every forwarder in this file takes: a STRING, a
19673
+ * NUMBER or an ARRAY is AWS's to reject by name — and unlike the unknown
19674
+ * member above, AWS really does see it and rejects it by shape.
19675
+ * - **The live ceiling is consulted for idempotency**
19676
+ * ({@link liveCeilingAlreadyMatches}), the #1630 arm the capacity sibling
19677
+ * already has, and it needs NO billing-mode gate: `DescribeTable` reports
19678
+ * `OnDemandThroughput` only on a PAY_PER_REQUEST table, so on a
19679
+ * PROVISIONED one the live block is simply absent and the guard fails
19680
+ * OPEN — the correct direction, since the worst case is the op AWS then
19681
+ * rejects by name (issue #3392) rather than a silently dropped change.
19682
+ * There is no `{0, 0}` analogue of the #1571 capacity trap.
19683
+ *
19684
+ * What it deliberately does NOT do is REMOVE a ceiling. A desired side that
19685
+ * declares none takes the truthiness gate and emits nothing, so the live
19686
+ * maximum SURVIVES — `-1` is AWS's documented removal sentinel and an absent
19687
+ * member keeps the maximum. That is byte-identical to what `update()`'s
19688
+ * TABLE-level arm does with the same edit (its own comment states it), and
19689
+ * implementing the reset on ONE of the two would make the same property
19690
+ * behave differently at the table and at an index of the same table. The
19691
+ * removal direction is filed for BOTH sites together as issue
19692
+ * [#3373](https://github.com/go-to-k/cdkd/issues/3373), which also records
19693
+ * why it is not an extrapolation from the GlobalTable measurement: `-1` is
19694
+ * LIVE-VERIFIED at `UpdateTable.OnDemandThroughput` and UNMEASURED at this
19695
+ * action's, and sending it DESTROYS a live ceiling.
19696
+ */
19697
+ indexCeilingForSend(name, physicalId, gsi, before, live, maskSecrets) {
19698
+ if (!gsi.OnDemandThroughput) return void 0;
19699
+ if (before !== void 0) {
19700
+ const desired = narrowOnDemandCeilings(gsi.OnDemandThroughput);
19701
+ const previous = narrowOnDemandCeilings(before.OnDemandThroughput);
19702
+ const [desiredSide, previousSide] = desired.dropped.length > 0 || previous.dropped.length > 0 ? [gsi.OnDemandThroughput, before.OnDemandThroughput] : [desired.block, previous.block];
19703
+ if (JSON.stringify(desiredSide) === JSON.stringify(previousSide)) return void 0;
19704
+ }
19705
+ const scope = this.indexScopeAt(name, physicalId, maskSecrets);
19706
+ const warn = (message) => this.logger.warn(maskSecrets(message));
19707
+ const debug = (message) => this.logger.debug(maskSecrets(message));
19708
+ const send = this.coerceOnDemandCeilingsForSend(scope, gsi.OnDemandThroughput, maskSecrets);
19709
+ if (!isPlainCapacityBlock(send)) return send;
19710
+ if (!ON_DEMAND_CEILING_MEMBERS.some((member) => member in send)) {
19711
+ if (narrowOnDemandCeilings(gsi.OnDemandThroughput).dropped.length > 0) debug(`${scope}: every declared OnDemandThroughput member was unusable, so no ceiling op was sent`);
19712
+ else warn(`${scope}: declares an OnDemandThroughput with no member DynamoDB accepts (${ON_DEMAND_CEILING_MEMBERS.join(" / ")}), so no ceiling op was sent. Check the member names.`);
19713
+ return;
19714
+ }
19715
+ if (liveCeilingAlreadyMatches(live?.OnDemandThroughput, send)) {
19716
+ debug(`${scope}: already carries the requested OnDemandThroughput in AWS; skipping its ceiling Update`);
19717
+ return;
19718
+ }
19719
+ return send;
19720
+ }
19721
+ /**
19470
19722
  * One `CreateTable` index entry with ALL THREE of its throughput blocks
19471
19723
  * coerced — `WarmThroughput` (PR review round 6), `ProvisionedThroughput`
19472
19724
  * (issue [#3255](https://github.com/go-to-k/cdkd/issues/3255)) and
@@ -19606,7 +19858,7 @@ var DynamoDBTableProvider = class {
19606
19858
  * through CloudFormation's DynamoDB Integer grammar (issue
19607
19859
  * [#3265](https://github.com/go-to-k/cdkd/issues/3265)).
19608
19860
  *
19609
- * Called from ALL FOUR send sites, which is the whole point — the block was
19861
+ * Called from ALL SIX send sites, which is the whole point — the block was
19610
19862
  * forwarded VERBATIM at every one of them, so a CFn-legal
19611
19863
  * `MaxReadRequestUnits: '100'` reached AWS as the STRING `"100"` in a `Long`
19612
19864
  * field and the request failed, while a `' 100 '` / `'0x64'` / `'1e2'`
@@ -19620,15 +19872,19 @@ var DynamoDBTableProvider = class {
19620
19872
  * same blind spot that hid this forwarder from issue #3147's sweep and
19621
19873
  * from #3255's round 6, one property over);
19622
19874
  * 3. `update()`'s TABLE-level `UpdateTable.OnDemandThroughput`;
19623
- * 4. `applyGsiUpdates`' per-index `Create` action.
19624
- *
19625
- * FOUR and not six: `UpdateGlobalSecondaryIndexAction` declares
19626
- * `OnDemandThroughput` too, and neither the adopted-index repair nor the
19627
- * same-name `Update` arm ever sets it — so a ceiling EDIT on a live index is
19628
- * silently discarded today. That is a MISSING SEND rather than a grammar
19629
- * defect, so it is not routed here; issue
19630
- * [#3287](https://github.com/go-to-k/cdkd/issues/3287) holds it, and it will
19631
- * read through this function when it lands.
19875
+ * 4. `applyGsiUpdates`' per-index `Create` action;
19876
+ * 5. `applyGsiUpdates`' same-name `Update` action, and
19877
+ * 6. its adopted-index repair `Update` — both through
19878
+ * {@link indexCeilingForSend}, added by issue
19879
+ * [#3287](https://github.com/go-to-k/cdkd/issues/3287).
19880
+ *
19881
+ * Sites 5 and 6 were the MISSING SEND rather than a grammar defect: until
19882
+ * #3287 `UpdateGlobalSecondaryIndexAction.OnDemandThroughput` was declared by
19883
+ * the SDK and set by neither arm, and `updateHasMember` was set only by the
19884
+ * capacity and warm arms — so a template whose only edit was a live index's
19885
+ * ceiling fired no op at all, the engine recorded the new value, and the next
19886
+ * deploy compared EQUAL. The edit was lost permanently, with nothing refused
19887
+ * and nothing dropped to warn about.
19632
19888
  *
19633
19889
  * THREE decisions, each with a plausible alternative:
19634
19890
  *
@@ -19684,9 +19940,16 @@ var DynamoDBTableProvider = class {
19684
19940
  * fail. Closing it needs `effectiveProperties` AND its
19685
19941
  * `canonicalizeDesiredProperties` twin (the rule requires the pair; shipping
19686
19942
  * the first alone re-issues a no-op `UpdateTable` on every later deploy), and
19687
- * this provider implements neither today. Tracked as issue
19688
- * [#3286](https://github.com/go-to-k/cdkd/issues/3286); the warning names the
19689
- * residual so a user is not left to discover it from a `cdkd drift` line. The
19943
+ * this provider implements neither today. That pair was WRITTEN and
19944
+ * WITHDRAWN: `observedProperties` is captured from the RAW template bag in
19945
+ * `deploy-engine.ts`, so a folded record leaves the observed side carrying a
19946
+ * per-index ceiling the record omits, and `drift-calculator.ts` compares
19947
+ * `GlobalSecondaryIndexes` as one positional deep-equal array — the fold
19948
+ * MANUFACTURES the phantom drift it exists to remove. Agreeing the two is a
19949
+ * deploy-engine change across three call sites. Tracked as issue
19950
+ * [#3286](https://github.com/go-to-k/cdkd/issues/3286), which carries the
19951
+ * evidence; the warning names the residual so a user is not left to discover
19952
+ * it from a `cdkd drift` line. The
19690
19953
  * `AWS::DynamoDB::GlobalTable` sibling answers the SAME way for the same
19691
19954
  * spelling — `collectTableOnDemandCeilings` coerces through
19692
19955
  * `coerceCfnInteger` and a rejected ceiling is reported and LEFT UNSENT, with
@@ -19694,20 +19957,9 @@ var DynamoDBTableProvider = class {
19694
19957
  * capacity answer, not its ceiling one) — so the two types do not diverge.
19695
19958
  */
19696
19959
  coerceOnDemandCeilingsForSend(scope, declared, maskSecrets) {
19697
- if (!isPlainCapacityBlock(declared)) return declared;
19698
- const block = declared;
19699
- let rewritten;
19700
- for (const member of ON_DEMAND_CEILING_MEMBERS) {
19701
- if (!(member in block)) continue;
19702
- const coerced = coerceCfnInteger(block[member]);
19703
- if (coerced === block[member]) continue;
19704
- rewritten ??= { ...block };
19705
- if (coerced === void 0) {
19706
- this.warnUnusableOnDemandCeiling(scope, member, block[member], maskSecrets);
19707
- delete rewritten[member];
19708
- } else rewritten[member] = coerced;
19709
- }
19710
- return rewritten ?? block;
19960
+ const { block, dropped } = narrowOnDemandCeilings(declared);
19961
+ for (const { member, raw } of dropped) this.warnUnusableOnDemandCeiling(scope, member, raw, maskSecrets);
19962
+ return block;
19711
19963
  }
19712
19964
  /**
19713
19965
  * Announce an `OnDemandThroughput` member a forwarder DROPPED (issue
@@ -19871,15 +20123,15 @@ var DynamoDBTableProvider = class {
19871
20123
  if (requested === void 0) return false;
19872
20124
  if (toFiniteNumber(requested.ReadCapacityUnits) !== 0 || toFiniteNumber(requested.WriteCapacityUnits) !== 0) return false;
19873
20125
  const warn = (message) => this.logger.warn(maskSecrets(message));
19874
- warn(`GSI ${maskSecrets(indexName)} on DynamoDB table ${physicalId}: the requested ProvisionedThroughput is {ReadCapacityUnits: 0, WriteCapacityUnits: 0}, which is AWS's on-demand placeholder rather than a capacity DynamoDB accepts (the minimum is 1), so no per-index throughput update was sent. This usually means the value came from a pre-#1767 cdkd state record — a \`cdkd drift --revert\` of such a record, or a rollback replaying it. Re-run \`cdkd deploy\` (or \`cdkd drift --accept\`) to refresh the record.`);
20126
+ warn(`${this.indexScopeAt(indexName, physicalId, maskSecrets)}: the requested ProvisionedThroughput is {ReadCapacityUnits: 0, WriteCapacityUnits: 0}, which is AWS's on-demand placeholder rather than a capacity DynamoDB accepts (the minimum is 1), so no per-index throughput update was sent. This usually means the value came from a pre-#1767 cdkd state record — a \`cdkd drift --revert\` of such a record, or a rollback replaying it. Re-run \`cdkd deploy\` (or \`cdkd drift --accept\`) to refresh the record.`);
19875
20127
  return true;
19876
20128
  }
19877
20129
  /**
19878
20130
  * The per-index `WarmThroughput` this update should send, or `undefined`
19879
20131
  * (issue #1768, one nesting level down from the table-level branch).
19880
20132
  *
19881
- * `applyGsiUpdates` used to send only `ProvisionedThroughput` /
19882
- * `OnDemandThroughput` on both of its arms, while `readCurrentState` emits a
20133
+ * `applyGsiUpdates` used to send only `ProvisionedThroughput` on both of its
20134
+ * arms, while `readCurrentState` emits a
19883
20135
  * declared per-index `WarmThroughput` — so a template declaring one had it
19884
20136
  * silently dropped on every index add / change, `cdkd drift` reported the
19885
20137
  * difference forever, and `--revert` emitted no op at all and exited 0
@@ -19903,20 +20155,20 @@ var DynamoDBTableProvider = class {
19903
20155
  warmThroughputOpFor(indexName, physicalId, desired, previous, live, maskSecrets = (text) => text) {
19904
20156
  const warn = (message) => this.logger.warn(maskSecrets(message));
19905
20157
  const debug = (message) => this.logger.debug(maskSecrets(message));
19906
- const safeIndexName = maskSecrets(indexName);
20158
+ const scope = this.indexScopeAt(indexName, physicalId, maskSecrets);
19907
20159
  const requested = desired.WarmThroughput;
19908
20160
  if (previous !== void 0 && JSON.stringify(previous.WarmThroughput) === JSON.stringify(requested)) return;
19909
- const sendable = this.coerceWarmThroughputForSend(`GSI ${safeIndexName} on DynamoDB table ${physicalId}`, requested, maskSecrets);
20161
+ const sendable = this.coerceWarmThroughputForSend(scope, requested, maskSecrets);
19910
20162
  if (sendable === void 0) {
19911
- this.warnRefusedWarmThroughput(`GSI ${safeIndexName} on DynamoDB table ${physicalId}`, requested, maskSecrets);
20163
+ this.warnRefusedWarmThroughput(scope, requested, maskSecrets);
19912
20164
  return;
19913
20165
  }
19914
20166
  if (warmThroughputAlreadyMatches(sendable, live?.WarmThroughput)) {
19915
- debug(`GSI ${safeIndexName} on DynamoDB table ${physicalId} already carries the requested warm throughput in AWS; skipping its WarmThroughput Update`);
20167
+ debug(`${scope} already carries the requested warm throughput in AWS; skipping its WarmThroughput Update`);
19916
20168
  return;
19917
20169
  }
19918
20170
  if (isWarmThroughputDecrease(sendable, live?.WarmThroughput)) {
19919
- warn(`GSI ${safeIndexName} on DynamoDB table ${physicalId}: the requested WarmThroughput ${JSON.stringify(maskDeep(sendable, maskSecrets))} is lower than the ${JSON.stringify(maskDeep({
20171
+ warn(`${scope}: the requested WarmThroughput ${JSON.stringify(maskDeep(sendable, maskSecrets))} is lower than the ${JSON.stringify(maskDeep({
19920
20172
  ReadUnitsPerSecond: live?.WarmThroughput?.ReadUnitsPerSecond,
19921
20173
  WriteUnitsPerSecond: live?.WarmThroughput?.WriteUnitsPerSecond
19922
20174
  }, maskSecrets))} DynamoDB currently holds for that index. Warm throughput only ever RISES with an index's traffic and cannot be lowered — AWS rejects the UpdateTable with "decreasing WarmThroughput is not supported" — so it was NOT sent and every other change on this table still applied. On a deploy this warning may be the only signal you get: a successful deploy re-captures AWS's current value as the drift baseline. If your template declares this index's WarmThroughput, set it to the value AWS holds (or remove it, which AWS treats as keeping the last-set value); if it does not, the value came from a cdkd state record and 'cdkd deploy' (or 'cdkd drift --accept') refreshes it.`);
@@ -19933,7 +20185,8 @@ var DynamoDBTableProvider = class {
19933
20185
  * of it — the wait is what makes the next op (there, the BillingMode flip)
19934
20186
  * legal.
19935
20187
  */
19936
- async runGsiOps(physicalId, ops, desiredAttributeDefinitions) {
20188
+ async runGsiOps(physicalId, ops, desiredAttributeDefinitions, maskSecrets = (text) => text) {
20189
+ const debug = (message) => this.logger.debug(maskSecrets(message));
19937
20190
  for (const op of ops) {
19938
20191
  const input = {
19939
20192
  TableName: physicalId,
@@ -19942,8 +20195,7 @@ var DynamoDBTableProvider = class {
19942
20195
  if (op.Create && desiredAttributeDefinitions) input.AttributeDefinitions = desiredAttributeDefinitions;
19943
20196
  await this.dynamoDBClient.send(new UpdateTableCommand(input));
19944
20197
  await this.waitForTableAndIndexesActive(physicalId);
19945
- const verb = op.Create ? "created" : op.Delete ? "deleted" : "updated";
19946
- this.logger.debug(`${verb} GSI ${op.Create?.IndexName ?? op.Delete?.IndexName ?? op.Update?.IndexName} on DynamoDB table ${physicalId}`);
20198
+ debug(`${op.Create ? "created" : op.Delete ? "deleted" : "updated"} ${this.indexScopeAt(op.Create?.IndexName ?? op.Delete?.IndexName ?? op.Update?.IndexName, physicalId, maskSecrets)}`);
19947
20199
  }
19948
20200
  }
19949
20201
  /**
@@ -70106,6 +70358,28 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
70106
70358
  state: scrubbed
70107
70359
  };
70108
70360
  });
70361
+ const crossStackNeedles = allRecordedSecrets(outputSecrets, perResourceSecrets, orphanSecrets);
70362
+ const redactCrossStackName = (name) => redactSecretsForState(name, crossStackNeedles);
70363
+ const newImports = state.imports?.map((entry) => {
70364
+ const exportName = redactCrossStackName(entry.exportName);
70365
+ if (exportName === entry.exportName) return entry;
70366
+ recordsChanged++;
70367
+ return {
70368
+ ...entry,
70369
+ exportName
70370
+ };
70371
+ });
70372
+ const newOutputReads = state.outputReads?.map((entry) => {
70373
+ const sourceStack = redactCrossStackName(entry.sourceStack);
70374
+ const outputName = redactCrossStackName(entry.outputName);
70375
+ if (sourceStack === entry.sourceStack && outputName === entry.outputName) return entry;
70376
+ recordsChanged++;
70377
+ return {
70378
+ ...entry,
70379
+ sourceStack,
70380
+ outputName
70381
+ };
70382
+ });
70109
70383
  const newOutputs = redactUnaccountedOutputs(outputSecrets.size > 0 ? redactSecretsForState(state.outputs, outputSecrets, outputsSourceUntrusted ? void 0 : outputsTemplateSource, TEMPLATE_SOURCED_RULES) : state.outputs, state.outputs, accountedOutputKeys, allRecordedSecrets(outputSecrets, perResourceSecrets, orphanSecrets));
70110
70384
  emitAbandonedScanNotes();
70111
70385
  if (JSON.stringify(newOutputs) !== JSON.stringify(state.outputs)) recordsChanged++;
@@ -70115,6 +70389,8 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
70115
70389
  ...carriedState,
70116
70390
  resources: newResources,
70117
70391
  ...state.orphans !== void 0 && { orphans: newOrphans },
70392
+ ...newImports !== void 0 && { imports: newImports },
70393
+ ...newOutputReads !== void 0 && { outputReads: newOutputReads },
70118
70394
  outputs: newOutputs,
70119
70395
  lastModified: Date.now()
70120
70396
  };
@@ -90314,13 +90590,12 @@ function readEnvOverridesFile$3(filePath) {
90314
90590
  * which drives the `--role-arn` cases without booting the API server.
90315
90591
  */
90316
90592
  function forwardAwsEnv$2(env) {
90317
- for (const key of [
90318
- "AWS_ACCESS_KEY_ID",
90319
- "AWS_SECRET_ACCESS_KEY",
90320
- "AWS_SESSION_TOKEN",
90593
+ const passThrough = [
90594
+ ...AWS_CREDENTIAL_ENV_KEYS,
90321
90595
  "AWS_REGION",
90322
90596
  "AWS_DEFAULT_REGION"
90323
- ]) {
90597
+ ];
90598
+ for (const key of passThrough) {
90324
90599
  const value = process.env[key];
90325
90600
  if (value !== void 0) env[key] = value;
90326
90601
  }
@@ -92795,6 +93070,36 @@ async function buildSigV4HeadersIfRequested(options, resolved, loaded, host, por
92795
93070
  return headers;
92796
93071
  }
92797
93072
  /**
93073
+ * The `--sigv4` refusal, as a pure function of the two facts that decide its
93074
+ * wording (issue [#3250](https://github.com/go-to-k/cdkd/issues/3250) item 5).
93075
+ *
93076
+ * NAMING THE EXCLUSION. On a `--role-arn` run over a caller with no static
93077
+ * triple, `AWS_ACCESS_KEY_ID` IS set in this process — cdkd set it, to the
93078
+ * role — so "set AWS_ACCESS_KEY_ID" alone reads as a bug report about cdkd
93079
+ * rather than as the refusal it is. The message has to say which value it
93080
+ * declines and why.
93081
+ *
93082
+ * THE REMEDY ORDER DIFFERS PER BRANCH, deliberately. The assumed-role branch
93083
+ * fires precisely when the caller has no static pair BY DESIGN — SSO, an
93084
+ * instance role, a container role — so leading with "mint an access key pair"
93085
+ * tells the one user who should not do that to do it first. The role-less
93086
+ * branch is the opposite: there the pair genuinely is the simplest answer.
93087
+ * `--profile` is dropped from either list when one was already passed and did
93088
+ * not produce a usable pair — the same already-followed-advice defect, one arm
93089
+ * over.
93090
+ *
93091
+ * PURE, and separate from its caller, for a reason the caller cannot give: the
93092
+ * `--profile` arm of {@link resolveHostCredentialsForSigV4} transacts with AWS,
93093
+ * so the unit suite's network fence refuses every route that reaches this throw
93094
+ * with `options.profile` set. As a helper, all four combinations are testable
93095
+ * with no network at all.
93096
+ */
93097
+ function sigv4NoCredentialsRefusal(args) {
93098
+ const exportPair = `set AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY in your shell before running cdkd (the snapshot cdkd puts back is taken before it assumes the role, so exporting them afterwards is too late)`;
93099
+ const profileRemedy = args.profileAlreadyPassed ? "" : `pass --profile <name>, `;
93100
+ return new CdkdError(args.assumedRolePublished ? `--sigv4: no AWS credentials available to sign the request as YOU. AWS_ACCESS_KEY_ID is set in this process, but cdkd set it: it holds the --role-arn assumed role, and this signature is what the emulated agent sees as its INVOKER, so cdkd deliberately will not sign as that role. Your own credentials carried no static triple before the assume — an SSO / IAM Identity Center, EC2 instance or ECS container chain, or a profile you exported rather than passed — so there is nothing to put back. To fix: ${profileRemedy}pass --assume-role <arn>, or ${exportPair}.` : `--sigv4: no AWS credentials available to sign the request. To fix: ${exportPair}, ${profileRemedy}or pass --assume-role <arn>.`, "LOCAL_INVOKE_AGENTCORE_SIGV4_NO_CREDENTIALS");
93101
+ }
93102
+ /**
92798
93103
  * Resolve credentials for host-side SigV4 signing. Precedence:
92799
93104
  * 1. `--assume-role` → STS temp creds (warn + fall through on STS failure);
92800
93105
  * 2. `--profile` → profile creds (sessionToken when the profile carries one);
@@ -92837,7 +93142,10 @@ async function resolveHostCredentialsForSigV4(options, resolved, loaded, region)
92837
93142
  secretAccessKey: shellCredentials.secretAccessKey,
92838
93143
  ...shellCredentials.sessionToken && { sessionToken: shellCredentials.sessionToken }
92839
93144
  };
92840
- throw new CdkdError("--sigv4: no AWS credentials available to sign the request. Set AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY, pass --profile <name>, or pass --assume-role <arn>.", "LOCAL_INVOKE_AGENTCORE_SIGV4_NO_CREDENTIALS");
93145
+ throw sigv4NoCredentialsRefusal({
93146
+ assumedRolePublished: envCredentialsAreAssumedRole(),
93147
+ profileAlreadyPassed: options.profile !== void 0
93148
+ });
92841
93149
  }
92842
93150
  /**
92843
93151
  * Acquire the agent image. A CODE artifact (managed runtime) is built from
@@ -93278,9 +93586,7 @@ function platformToArchitecture(platform) {
93278
93586
  */
93279
93587
  function forwardAwsEnv$1(env) {
93280
93588
  const passThrough = [
93281
- "AWS_ACCESS_KEY_ID",
93282
- "AWS_SECRET_ACCESS_KEY",
93283
- "AWS_SESSION_TOKEN",
93589
+ ...AWS_CREDENTIAL_ENV_KEYS,
93284
93590
  "AWS_REGION",
93285
93591
  "AWS_DEFAULT_REGION"
93286
93592
  ];
@@ -94456,9 +94762,7 @@ async function assumeLambdaExecutionRole(roleArn, region) {
94456
94762
  */
94457
94763
  function forwardAwsEnv(env) {
94458
94764
  const passThrough = [
94459
- "AWS_ACCESS_KEY_ID",
94460
- "AWS_SECRET_ACCESS_KEY",
94461
- "AWS_SESSION_TOKEN",
94765
+ ...AWS_CREDENTIAL_ENV_KEYS,
94462
94766
  "AWS_REGION",
94463
94767
  "AWS_DEFAULT_REGION"
94464
94768
  ];
@@ -94706,4 +95010,4 @@ function buildProgram() {
94706
95010
 
94707
95011
  //#endregion
94708
95012
  export { buildProgram };
94709
- //# sourceMappingURL=program-babtsurS.js.map
95013
+ //# sourceMappingURL=program-dYPgGoOv.js.map