@go-to-k/cdkd 0.290.34 → 0.290.35

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 isExportAliasCollision, $n as maskSecretsInText, $r as stripControlChars, $t as withSharedDrainBudget, A as makeCanonicalizePropertiesFn, Ai as AwsClients, An as refuseMalformedResourcesForDestroy, Ar as AssetModeResolver, At as getAccountInfo, B as isWarmThroughputDecrease, Bi as DynamicReferenceRegionAmbiguousError, Bn as TemplateParser, Br as buildDenyExternalAccessPolicy, Bt as assertRegionMatch, C as PRE_DELETE_SNAPSHOT_TYPES, Ci as PARTITION_TABLE, Cn as malformedStateRefusalMessage, Cr as AssetPublisher, Ct as disableInstanceApiTermination, D as isFinalSnapshotError, Di as processStackMessages, Dn as refuseMalformedOutputs, Dr as createAssetRedirectResolver, Dt as carriesFetchableDynamicReference, E as createPreDeleteFinalSnapshot, Ei as AssemblyReader, En as refuseMalformedNestedChildOutputs, Er as buildAssetRedirectMap, Et as carriesDynamicReference, F as isStatefulRecreateTargetForReplace, Fi as CdkdError, Fn as findActionableSilentDrops, Fr as isCrossRegionRedirect, Ft as refStateLookupFromResource, G as cyan, Gi as MalformedProducerRecordRefusalError, Gn as carriesSecretMask, Gr as dockerSpawnEnvWithSensitive, Gt as configStringRefusal, H as toFiniteNumber, Hi as LocalInvokeBuildError$1, Hn as STATE_SOURCED_CROSS_GENERATION_RULES, Hr as describeDockerCapturedOutput, Ht as coerceCfnInteger, I as isStatefulRecreateTargetSync, In as findSilentDropProperties, Ir as parseBootstrapMarker, It as isSettledInstanceState, J as red, Ji as ProvisioningError, Jn as errorCauseChain, Jr as partitionSensitiveEnv, Jt as requireConfigArray, K as gray, Ki as NestedStackChildDirectDestroyError, Kn as createSecretMasker, Kr as formatDockerLoginError, Kt as readConfigString, L as renderStatefulReason, Li as CrossAccountSecretRefusalError, Ln as describeTypeWithThrottleRetry, Lr as readBootstrapMarkerBody, Lt as WAFv2WebACLProvider, M as makeSiblingClaimReader, Mi as resetAwsClients, Mn as repairMalformedOutputsForReadOnly, Mr as assertAssetBucketRegion, Mt as isNamelessDynamicReferenceError, N as planOrphanAdoption, Ni as setAwsClients, Nn as repairMalformedResourcePropertiesForReadOnly, Nr as ensureAssetStorage, Nt as isUnboundTemplateParameter, O as refusesFinalSnapshot, On as refuseMalformedOutputsForDestroy, Or as loadPublishableAssetManifest, Ot as cfnRefValueFromPhysicalId, P as MULTI_REGION_RECREATE_BLOCKED_TYPES, Pn as repairMalformedResourcesForReadOnly, Pr as getBootstrapMarkerKey, Pt as parameterTypeMayLoseSecretIdentity, Q as exportAliasCollisionScrubWarning, Qi as StackTerminationProtectionError, Qn as maskSecretsInError, Qr as escapeRegExp, Qt as producerRegionsFromState, R as WARM_THROUGHPUT_MEMBERS, Rn as withRetry, Rr as validateAssetBucketName, Rt as normalizeAwsTagsToCfn, S as ATOMIC_FINAL_SNAPSHOT_TYPES, Si as expectedOwnerParam, Sn as malformedResourcesWarning, Sr as shouldRetainResource, St as deleteSkipReason, T as ccRoutedFinalSnapshotError, Ti as derivePartitionAndUrlSuffix, Tn as refuseDivergentRecordRegionForDestroy, Tr as WorkGraph, Tt as IntrinsicFunctionResolver, U as formatResourceLine, Ui as LocalStartServiceError, Un as STATE_SOURCED_READBACK_RULES, Ur as describeDockerExecFailure, Ut as configBooleanRefusal, V as toCfnInteger, Vi as IntrinsicResolutionRefusalError, Vn as STATE_SOURCED_BASELINE_RULES, Vr as buildDockerImage, Vt as coerceCfnBoolean, W as bold, Wn as TEMPLATE_SOURCED_RULES, Wr as describeDockerFailure, Wt as configIntegerRefusal, X as collectDeclaredOutputNames, Xi as ResourceUpdateNotSupportedError, Xn as isSingleDynamicReferenceToken, Xr as runDockerForeground, Xt as requireConfigString, Y as yellow, Yi as ResourceTimeoutError, Yn as identityKeyFor, Yr as redactDockerArgvValues, Yt as requireConfigObject, Z as collectPublishedOutputNames, Zi as StackHasActiveImportsError, Zn as markSameGenerationBag, Zr as runDockerStreaming, Zt as classifyReplaySecretRegion, _ as bagHoldsSecretExpression, _i as MIGRATE_TMP_PREFIX, _n as malformedLocalOutputsWarning, _r as importableOutputKeys, _t as CloudControlProvider, a as DeploymentEventsStore, aa as isMarkedNonRetryable, ai as getLegacyStateBucketName, an as applyRoleArnIfSet, ar as S3StateBackend, at as collectInlinePolicyNamesManagedBySiblings, b as mergeNoChangeOutputs, bi as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, bn as malformedRenderedContainersWarning, br as orphansAfterRollback, bt as UNSPECIFIED_SKIP_REASON, c as replayFailedOperations, ca as isTransientServerError, ci as resolveCaptureObservedState, cn as callerEnvCredentials, cr as buildForceUnlockCommand, ct as wouldReturnToSdkProvider, d as updatePartialMessage, da as retryClassificationText, di as resolveStateBucketWithDefaultAndSource, dn as INTRINSIC_KEYS, dr as formatLockExpiry, dt as maskerOrIdentity, ea as SynthesisError, ei as AssetManifestLoader, en as s3BucketArn, er as recordMaskOnlyValue, et as secretBearing, f as updatePartialReason, fa as describeAwsFailure, fi as resolveUseCdkBootstrapAssets, fn as STATE_RESOURCES_MALFORMED, fr as shellQuote, ft as beginCommandInterruptScope, g as computeImplicitDeleteEdges, gi as CFN_TEMPLATE_URL_LIMIT, gn as malformedExportSourceWarning, gr as hasReadableExportSet, gt as startInterruptWatch, h as IMPLICIT_DELETE_DEPENDENCIES, hi as CFN_TEMPLATE_BODY_LIMIT, hn as malformedExportNamesWarning, hr as exportNamesCarriedFrom, ht as isInterruptedWaitError, i as DeploymentEventsReader, ia as withErrorHandling, ii as getDefaultStateBucketName, in as s3BucketWebsiteUrl, ir as LockManager, it as IAMRoleProvider, j as extractDeploymentEventError, ji as getAwsClients, jn as refuseMalformedState, jr as BOOTSTRAP_MARKER_PREFIX, jt as isImpossibleEmptyStoredAttribute, k as unsupportedFinalSnapshotError, ki as resolveBucketRegion, kn as refuseMalformedResourcePropertiesForOrphan, kr as rewriteTemplateAssetReferences, kt as coerceParameterTypedValue, l as replayRollback, la as markNonRetryable, li as resolveSkipPrefix, ln as envCredentialsAreAssumedRole, lr as buildLockContentionMessage, lt as createMaskedRetryLogger, m as maskingRetryLogger, mi as warnDeprecatedNoPrefixCliFlag, mn as hasReadableResources, mr as DEFAULT_STATE_PREFIX, mt as interruptWatchListenerCount, n as DEFAULT_RESOURCE_WARN_AFTER_MS, ni as Synthesizer, nn as s3BucketDualStackDomainName, nr as redactSecretsForState, nt as secretSafeKeyDisplay, o as planFailedOps, oa as isRetryableTransientError, oi as resolveApp, on as AWS_CREDENTIAL_ENV_KEYS, or as rebuildClientForBucketRegion, ot as clearOnUpdateRemoval, p as withResourceDeadline, pa as safeStringify, pi as stateBucketExistenceConfirmed, pn as hasReadableOutputs, pr as CUSTOM_RESOURCE_RESPONSE_PREFIX, pt as endCommandInterruptScope, q as green, qi as PartialFailureError, qn as dynamicReferenceTokens, qr as getDockerCmd, qt as replayWarn, r as DeployEngine, ra as normalizeAwsError, ri as synthesisStatusMessage, rn as s3BucketRegionalDomainName, rr as scrubResourceRecord, rt as getCurrentResourceSecrets, s as planRollback, sa as isThrottlingError, si as resolveAutoAssetStorage, sn as applyCallerIdentityCredentials, sr as UNREPRODUCIBLE_LOCK_CLAUSE, st as ProviderRegistry, t as DEFAULT_RESOURCE_TIMEOUT_MS, ti as getDockerImageBySourceHash, tn as s3BucketDomainName, tr as recoverMaskedOutput, tt as secretBearingStateKeyWarning, u as resolveReplacementOldType, ua as markRedactedCause, ui as resolveStateBucketWithDefault, un as DiffCalculator, ur as forceQuitRecoveryClause, ut as maskDeep, v as isSecretBearingReferenceString, vi as findLargeInlineResources, vn as malformedOutputsRefusalMessage, vr as importableOutputs, vt as isWaitAbandonedError, w as buildFinalSnapshotIdentifier, wi as canonicalizeRegion, wn as producerRecordKey, wr as stringifyValue, wt as isTerminationProtectionPropagationError, x as bindingSkippedOutputs, xi as decodeListingKey, xn as malformedResourcePropertiesWarning, xr as orphansCarriedFrom, xt as deleteIndeterminateGuards, y as keptWholeReasonText, yi as uploadCfnTemplate, yn as malformedOutputsWarning, yr as isReadableBag, yt as slowCcOperationTimeoutMs, z as coerceWarmThroughput$1, zi as DeployCancelledError, zn as DagBuilder, zr as validateContainerRepoName, zt as resolveExplicitPhysicalId } from "./deploy-engine-efiVJ-rX.js";
1
+ import { $ as isExportAliasCollision, $i as StackTerminationProtectionError, $n as maskSecretsInText, $r as stripControlChars, $t as withSharedDrainBudget, A as makeCanonicalizePropertiesFn, Ai as resolveBucketRegion, An as refuseMalformedResourcesForDestroy, Ar as AssetModeResolver, At as getAccountInfo, B as isWarmThroughputDecrease, Bi as DeployCancelledError, Bn as TemplateParser, Br as buildDenyExternalAccessPolicy, Bt as assertRegionMatch, C as PRE_DELETE_SNAPSHOT_TYPES, Ci as expectedOwnerParam, Cn as malformedStateRefusalMessage, Cr as AssetPublisher, Ct as disableInstanceApiTermination, D as isFinalSnapshotError, Di as AssemblyReader, Dn as refuseMalformedOutputs, Dr as createAssetRedirectResolver, Dt as carriesFetchableDynamicReference, E as createPreDeleteFinalSnapshot, Ei as derivePartitionAndUrlSuffix, En as refuseMalformedNestedChildOutputs, Er as buildAssetRedirectMap, Et as carriesDynamicReference, F as isStatefulRecreateTargetForReplace, Fn as findActionableSilentDrops, Fr as isCrossRegionRedirect, Ft as refStateLookupFromResource, G as cyan, Gn as carriesSecretMask, Gr as dockerSpawnEnvWithSensitive, Gt as configStringRefusal, H as toFiniteNumber, Hi as IntrinsicResolutionRefusalError, Hn as STATE_SOURCED_CROSS_GENERATION_RULES, Hr as describeDockerCapturedOutput, Ht as coerceCfnInteger, I as isStatefulRecreateTargetSync, Ii as CdkdError, In as findSilentDropProperties, Ir as parseBootstrapMarker, It as isSettledInstanceState, J as red, Ji as PartialFailureError, Jn as errorCauseChain, Jr as partitionSensitiveEnv, Jt as requireConfigArray, K as gray, Ki as MalformedProducerRecordRefusalError, Kn as createSecretMasker, Kr as formatDockerLoginError, Kt as readConfigString, L as renderStatefulReason, Ln as describeTypeWithThrottleRetry, Lr as readBootstrapMarkerBody, Lt as WAFv2WebACLProvider, M as makeSiblingClaimReader, Mi as getAwsClients, Mn as repairMalformedOutputsForReadOnly, Mr as assertAssetBucketRegion, Mt as isNamelessDynamicReferenceError, N as planOrphanAdoption, Ni as resetAwsClients, Nn as repairMalformedResourcePropertiesForReadOnly, Nr as ensureAssetStorage, Nt as isUnboundTemplateParameter, O as refusesFinalSnapshot, Oi as processStackMessages, On as refuseMalformedOutputsForDestroy, Or as loadPublishableAssetManifest, Ot as cfnRefValueFromPhysicalId, P as MULTI_REGION_RECREATE_BLOCKED_TYPES, Pi as setAwsClients, Pn as repairMalformedResourcesForReadOnly, Pr as getBootstrapMarkerKey, Pt as parameterTypeMayLoseSecretIdentity, Q as exportAliasCollisionScrubWarning, Qi as StackHasActiveImportsError, Qn as maskSecretsInError, Qr as escapeRegExp, Qt as producerRegionsFromState, R as WARM_THROUGHPUT_MEMBERS, Ri as CrossAccountSecretRefusalError, Rn as withRetry, Rr as validateAssetBucketName, Rt as normalizeAwsTagsToCfn, S as ATOMIC_FINAL_SNAPSHOT_TYPES, Si as decodeListingKey, Sn as malformedResourcesWarning, Sr as shouldRetainResource, St as deleteSkipReason, T as ccRoutedFinalSnapshotError, Ti as canonicalizeRegion, Tn as refuseDivergentRecordRegionForDestroy, Tr as WorkGraph, Tt as IntrinsicFunctionResolver, U as formatResourceLine, Ui as LocalInvokeBuildError$1, Un as STATE_SOURCED_READBACK_RULES, Ur as describeDockerExecFailure, Ut as configBooleanRefusal, V as toCfnInteger, Vi as DynamicReferenceRegionAmbiguousError, Vn as STATE_SOURCED_BASELINE_RULES, Vr as buildDockerImage, Vt as coerceCfnBoolean, W as bold, Wi as LocalStartServiceError, Wn as TEMPLATE_SOURCED_RULES, Wr as describeDockerFailure, Wt as configIntegerRefusal, X as collectDeclaredOutputNames, Xi as ResourceTimeoutError, Xn as isSingleDynamicReferenceToken, Xr as runDockerForeground, Xt as requireConfigString, Y as yellow, Yi as ProvisioningError, Yn as identityKeyFor, Yr as redactDockerArgvValues, Yt as requireConfigObject, Z as collectPublishedOutputNames, Zi as ResourceUpdateNotSupportedError, Zn as markSameGenerationBag, Zr as runDockerStreaming, Zt as classifyReplaySecretRegion, _ as bagHoldsSecretExpression, _i as CFN_TEMPLATE_URL_LIMIT, _n as malformedLocalOutputsWarning, _r as importableOutputKeys, _t as CloudControlProvider, a as DeploymentEventsStore, aa as withErrorHandling, ai as getLegacyStateBucketName, an as applyRoleArnIfSet, ar as S3StateBackend, at as collectInlinePolicyNamesManagedBySiblings, b as mergeNoChangeOutputs, bi as uploadCfnTemplate, bn as malformedRenderedContainersWarning, br as orphansAfterRollback, bt as UNSPECIFIED_SKIP_REASON, c as replayFailedOperations, ca as isThrottlingError, ci as resolveCaptureObservedState, cn as callerEnvCredentials, cr as buildForceUnlockCommand, ct as wouldReturnToSdkProvider, d as updatePartialMessage, da as markRedactedCause, di as resolveStateBucketWithDefaultAndSource, dn as INTRINSIC_KEYS, dr as formatLockExpiry, dt as maskerOrIdentity, ei as AssetManifestLoader, en as s3BucketArn, er as recordMaskOnlyValue, et as secretBearing, f as updatePartialReason, fa as retryClassificationText, fi as resolveUseCdkBootstrapAssets, fn as STATE_RESOURCES_MALFORMED, fr as shellQuote, ft as beginCommandInterruptScope, g as computeImplicitDeleteEdges, gi as CFN_TEMPLATE_BODY_LIMIT, gn as malformedExportSourceWarning, gr as hasReadableExportSet, gt as startInterruptWatch, h as IMPLICIT_DELETE_DEPENDENCIES, hi as ssmResolvedValueType, hn as malformedExportNamesWarning, hr as exportNamesCarriedFrom, ht as isInterruptedWaitError, i as DeploymentEventsReader, ia as normalizeAwsError, ii as getDefaultStateBucketName, in as s3BucketWebsiteUrl, ir as LockManager, it as IAMRoleProvider, j as extractDeploymentEventError, ji as AwsClients, jn as refuseMalformedState, jr as BOOTSTRAP_MARKER_PREFIX, jt as isImpossibleEmptyStoredAttribute, k as unsupportedFinalSnapshotError, kn as refuseMalformedResourcePropertiesForOrphan, kr as rewriteTemplateAssetReferences, kt as coerceParameterTypedValue, l as replayRollback, la as isTransientServerError, li as resolveSkipPrefix, ln as envCredentialsAreAssumedRole, lr as buildLockContentionMessage, lt as createMaskedRetryLogger, m as maskingRetryLogger, ma as safeStringify, mi as warnDeprecatedNoPrefixCliFlag, mn as hasReadableResources, mr as DEFAULT_STATE_PREFIX, mt as interruptWatchListenerCount, n as DEFAULT_RESOURCE_WARN_AFTER_MS, ni as Synthesizer, nn as s3BucketDualStackDomainName, nr as redactSecretsForState, nt as secretSafeKeyDisplay, o as planFailedOps, oa as isMarkedNonRetryable, oi as resolveApp, on as AWS_CREDENTIAL_ENV_KEYS, or as rebuildClientForBucketRegion, ot as clearOnUpdateRemoval, p as withResourceDeadline, pa as describeAwsFailure, pi as stateBucketExistenceConfirmed, pn as hasReadableOutputs, pr as CUSTOM_RESOURCE_RESPONSE_PREFIX, pt as endCommandInterruptScope, q as green, qi as NestedStackChildDirectDestroyError, qn as dynamicReferenceTokens, qr as getDockerCmd, qt as replayWarn, r as DeployEngine, ri as synthesisStatusMessage, rn as s3BucketRegionalDomainName, rr as scrubResourceRecord, rt as getCurrentResourceSecrets, s as planRollback, sa as isRetryableTransientError, si as resolveAutoAssetStorage, sn as applyCallerIdentityCredentials, sr as UNREPRODUCIBLE_LOCK_CLAUSE, st as ProviderRegistry, t as DEFAULT_RESOURCE_TIMEOUT_MS, ta as SynthesisError, ti as getDockerImageBySourceHash, tn as s3BucketDomainName, tr as recoverMaskedOutput, tt as secretBearingStateKeyWarning, u as resolveReplacementOldType, ua as markNonRetryable, ui as resolveStateBucketWithDefault, un as DiffCalculator, ur as forceQuitRecoveryClause, ut as maskDeep, v as isSecretBearingReferenceString, vi as MIGRATE_TMP_PREFIX, vn as malformedOutputsRefusalMessage, vr as importableOutputs, vt as isWaitAbandonedError, w as buildFinalSnapshotIdentifier, wi as PARTITION_TABLE, wn as producerRecordKey, wr as stringifyValue, wt as isTerminationProtectionPropagationError, x as bindingSkippedOutputs, xi as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, xn as malformedResourcePropertiesWarning, xr as orphansCarriedFrom, xt as deleteIndeterminateGuards, y as keptWholeReasonText, yi as findLargeInlineResources, yn as malformedOutputsWarning, yr as isReadableBag, yt as slowCcOperationTimeoutMs, z as coerceWarmThroughput$1, zn as DagBuilder, zr as validateContainerRepoName, zt as resolveExplicitPhysicalId } from "./deploy-engine-DhZtBBq0.js";
2
2
  import { C as displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, S as UNRENDERABLE, T as displaySafe, _ as withSkipPrefix, b as SECRET_REF_MAX_CODE_POINTS, c as PATTERN_B_NAME_OPTIONS, f as explicitNamePropertyFor, h as getCurrentStackName, i as reserveStdoutForPayload, l as PATTERN_B_NAME_PROPERTIES, m as generateResourceNameWithFallback, n as getLogger, o as runStackBuffered, p as generateResourceName, s as getLiveRenderer, u as PATTERN_B_RESOURCE_TYPES, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS } from "./logger-BX4pJk0T.js";
3
3
  import { n as awsClientDefaults } from "./aws-client-defaults-BoIxyCir.js";
4
- import { t as getCdkdVersion } from "./version-CpyoQ3PO.js";
5
- import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-BTxYWlwu.js";
4
+ import { t as getCdkdVersion } from "./version-BIpCWEQx.js";
5
+ import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-BguZAQtF.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";
@@ -16943,6 +16943,216 @@ function isRecord$1(value) {
16943
16943
  return typeof value === "object" && value !== null && !Array.isArray(value);
16944
16944
  }
16945
16945
 
16946
+ //#endregion
16947
+ //#region src/provisioning/dynamodb-contributor-insights.ts
16948
+ /**
16949
+ * The `AWS::DynamoDB::Table` `ContributorInsightsSpecification` rules, in ONE
16950
+ * spelling for the table-level block and the per-index
16951
+ * (`GlobalSecondaryIndexes[].ContributorInsightsSpecification`) one (issue
16952
+ * [#1782](https://github.com/go-to-k/cdkd/issues/1782)).
16953
+ *
16954
+ * Neither block rides `CreateTable` / `UpdateTable`: DynamoDB applies it
16955
+ * through `UpdateContributorInsights` and reports it through
16956
+ * `DescribeContributorInsights`, and both calls address an index by adding
16957
+ * `IndexName`. The SDK's `GlobalSecondaryIndex` shape has NO such member, so a
16958
+ * per-index block left inside the forwarded index entry is discarded by the
16959
+ * serializer — a template asking for per-index Contributor Insights deployed
16960
+ * green with the feature off.
16961
+ *
16962
+ * Pure, so the write plan and the drift read-back gate cannot answer "does
16963
+ * this index declare the block" differently.
16964
+ */
16965
+ /** The CFn property name, shared by the table-level and the per-index block. */
16966
+ const CONTRIBUTOR_INSIGHTS_KEY = "ContributorInsightsSpecification";
16967
+ /**
16968
+ * Read a `ContributorInsightsSpecification` block.
16969
+ *
16970
+ * An ABSENT `Enabled` reads as `false` — the member is required by the CFn
16971
+ * schema, and disabling is the answer that cannot start a billed feature.
16972
+ */
16973
+ function readContributorInsightsSpec(value, path) {
16974
+ if (value === void 0 || value === null) return { kind: "absent" };
16975
+ const refusal = configBooleanRefusal(value, "Enabled", path) ?? configStringRefusal(value, "Mode", "", path);
16976
+ if (refusal !== void 0) return {
16977
+ kind: "unusable",
16978
+ reason: refusal
16979
+ };
16980
+ const block = value;
16981
+ const enabled = coerceCfnBoolean(block["Enabled"]) ?? false;
16982
+ const mode = block["Mode"];
16983
+ return typeof mode === "string" && mode.length > 0 ? {
16984
+ kind: "usable",
16985
+ enabled,
16986
+ mode
16987
+ } : {
16988
+ kind: "usable",
16989
+ enabled
16990
+ };
16991
+ }
16992
+ /**
16993
+ * The call one block needs, given the block the PREVIOUS side recorded.
16994
+ *
16995
+ * - `absent` with a DECLARED previous (usable or not) is a template REMOVAL and
16996
+ * DISABLES — the rule this provider's other post-ACTIVE blocks (PITR, TTL,
16997
+ * Kinesis) and the table-level block already follow. Because the rule is
16998
+ * symmetric in its two sides, a rollback — which replays `update()` with the
16999
+ * sides SWAPPED — restores the old setting through the same arms.
17000
+ * - `unusable` issues NOTHING and reports through `onUnusable`: the live
17001
+ * setting is left alone rather than overwritten with a guess.
17002
+ * - An unchanged usable block issues nothing. `mode` takes part only while
17003
+ * enabled, since it is never sent with `DISABLE`.
17004
+ */
17005
+ function planContributorInsightsOp(desired, previous, onUnusable) {
17006
+ if (desired.kind === "unusable") {
17007
+ onUnusable(desired.reason);
17008
+ return;
17009
+ }
17010
+ if (desired.kind === "absent") return previous.kind === "absent" ? void 0 : { action: "DISABLE" };
17011
+ if (previous.kind === "usable" && previous.enabled === desired.enabled && (!desired.enabled || previous.mode === desired.mode)) return;
17012
+ if (!desired.enabled) return { action: "DISABLE" };
17013
+ return desired.mode === void 0 ? { action: "ENABLE" } : {
17014
+ action: "ENABLE",
17015
+ mode: desired.mode
17016
+ };
17017
+ }
17018
+ /**
17019
+ * The path a per-index refusal names. Deliberately WITHOUT the index name: the
17020
+ * name is a RESOLVED template value, the caller's message already carries it
17021
+ * through a whole-value mask, and a copy embedded here would reach only the
17022
+ * substring mask, which ignores a name shorter than its minimum needle.
17023
+ */
17024
+ const INDEX_CONTRIBUTOR_INSIGHTS_PATH = `GlobalSecondaryIndexes[].${CONTRIBUTOR_INSIGHTS_KEY}`;
17025
+ /** Index a CFn `GlobalSecondaryIndexes` value by `IndexName`. */
17026
+ function indexEntriesByName(value) {
17027
+ const byName = /* @__PURE__ */ new Map();
17028
+ if (!Array.isArray(value)) return byName;
17029
+ for (const entry of value) {
17030
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
17031
+ const name = entry["IndexName"];
17032
+ if (typeof name === "string") byName.set(name, entry);
17033
+ }
17034
+ return byName;
17035
+ }
17036
+ /**
17037
+ * The per-index calls an index list needs, in DESIRED order.
17038
+ *
17039
+ * Only an index the DESIRED list still names can produce a call: an index the
17040
+ * update removed takes its Contributor Insights rules with it, and a call
17041
+ * naming it fails with `ResourceNotFoundException`. An index that is NEW in
17042
+ * the desired list has an `absent` previous side, so its declared block is
17043
+ * always applied.
17044
+ */
17045
+ function planIndexContributorInsightsOps(desiredIndexes, previousIndexes, onUnusable) {
17046
+ const previousByName = indexEntriesByName(previousIndexes);
17047
+ const ops = [];
17048
+ for (const [indexName, entry] of indexEntriesByName(desiredIndexes)) {
17049
+ const path = INDEX_CONTRIBUTOR_INSIGHTS_PATH;
17050
+ const op = planContributorInsightsOp(readContributorInsightsSpec(entry[CONTRIBUTOR_INSIGHTS_KEY], path), readContributorInsightsSpec(previousByName.get(indexName)?.[CONTRIBUTOR_INSIGHTS_KEY], path), (reason) => onUnusable(indexName, reason));
17051
+ if (op !== void 0) ops.push({
17052
+ indexName,
17053
+ ...op
17054
+ });
17055
+ }
17056
+ return ops;
17057
+ }
17058
+ /**
17059
+ * Every refusal a CREATE must raise before `CreateTable` goes out: the
17060
+ * table-level block's and each index's. A create has no live setting to leave
17061
+ * alone, so an unreadable block there is a template error, not a skip.
17062
+ */
17063
+ function contributorInsightsRefusals(tableBlock, indexes) {
17064
+ const refusals = [];
17065
+ const table = readContributorInsightsSpec(tableBlock, CONTRIBUTOR_INSIGHTS_KEY);
17066
+ if (table.kind === "unusable") refusals.push(table.reason);
17067
+ if (!Array.isArray(indexes)) return refusals;
17068
+ indexes.forEach((entry, position) => {
17069
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return;
17070
+ const read = readContributorInsightsSpec(entry[CONTRIBUTOR_INSIGHTS_KEY], `GlobalSecondaryIndexes[${position}].${CONTRIBUTOR_INSIGHTS_KEY}`);
17071
+ if (read.kind === "unusable") refusals.push(read.reason);
17072
+ });
17073
+ return refusals;
17074
+ }
17075
+ /**
17076
+ * One index entry WITHOUT its `ContributorInsightsSpecification`, for the
17077
+ * `CreateTable` forward.
17078
+ *
17079
+ * Explicit so nothing relies on the SDK serializer discarding a member its
17080
+ * `GlobalSecondaryIndex` shape does not have. Returns the entry UNCHANGED —
17081
+ * identity included — when there is nothing to strip, and never mutates it: the
17082
+ * bag is the caller's resolved template, which the engine records into state.
17083
+ */
17084
+ function stripIndexContributorInsights(entry) {
17085
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return entry;
17086
+ if (!("ContributorInsightsSpecification" in entry)) return entry;
17087
+ const { [CONTRIBUTOR_INSIGHTS_KEY]: _stripped, ...rest } = entry;
17088
+ return rest;
17089
+ }
17090
+ /**
17091
+ * Does this DESIRED index entry declare a block cdkd would act on — the gate
17092
+ * for the per-index drift read-back, which costs one API call per index?
17093
+ *
17094
+ * The same reader the write plan runs, so an `unusable` block (never sent) is
17095
+ * never read back either.
17096
+ */
17097
+ function indexDeclaresContributorInsights(desiredEntry) {
17098
+ return readContributorInsightsSpec(desiredEntry?.[CONTRIBUTOR_INSIGHTS_KEY], "").kind === "usable";
17099
+ }
17100
+ /** The setting a `ContributorInsightsStatus` stands for, if any. */
17101
+ function enabledForStatus(status, transientAsTarget) {
17102
+ if (status === "ENABLED" || transientAsTarget && status === "ENABLING") return true;
17103
+ if (status === "DISABLED" || transientAsTarget && status === "DISABLING") return false;
17104
+ }
17105
+ /**
17106
+ * Reverse-map the TABLE-level `DescribeContributorInsights` answer to the CFn
17107
+ * block, or `undefined` when there is nothing stable to report.
17108
+ *
17109
+ * Only the terminal statuses are mapped, and `Mode` is emitted whenever AWS
17110
+ * reports one for an enabled table — the block is a TOP-LEVEL key, which the
17111
+ * comparator walks by the baseline's own keys. `Mode` is never emitted for a
17112
+ * disabled block: it is not sent with `DISABLE`, and a disabled block carrying
17113
+ * one is a CFn-invalid placeholder.
17114
+ */
17115
+ function reverseMapTableContributorInsights(status, mode) {
17116
+ const enabled = enabledForStatus(status, false);
17117
+ if (enabled === void 0) return void 0;
17118
+ const block = { Enabled: enabled };
17119
+ if (enabled && mode !== void 0) block["Mode"] = mode;
17120
+ return block;
17121
+ }
17122
+ /**
17123
+ * Reverse-map one PER-INDEX `DescribeContributorInsights` answer, shaped by
17124
+ * the block the DESIRED entry declares. `undefined` when there is nothing to
17125
+ * report or the declared block is not one cdkd acts on.
17126
+ *
17127
+ * The per-index block sits inside an ARRAY entry, which the comparator matches
17128
+ * as a whole and by exact key set, so everything here exists to make the
17129
+ * read-back equal the declaration whenever AWS holds what was declared:
17130
+ *
17131
+ * - `ENABLING` / `DISABLING` read as their TARGET. The deploy does not wait
17132
+ * for the toggle, so the post-deploy `observedProperties` capture usually
17133
+ * sees `ENABLING`; omitting the block would freeze an entry the settled
17134
+ * index can never equal.
17135
+ * - `Mode` is emitted only when the declared block carries one: AWS reports a
17136
+ * mode for every enabled rule set (its default when none was sent). While
17137
+ * `ENABLING`, a mode AWS does not report yet reads as the declared one —
17138
+ * the one that was just sent.
17139
+ * - `Enabled` keeps the declared SPELLING when the template wrote a string
17140
+ * (`'true'`), since the comparator does not coerce.
17141
+ */
17142
+ function reverseMapIndexContributorInsights(status, mode, declaredBlock) {
17143
+ const declared = readContributorInsightsSpec(declaredBlock, INDEX_CONTRIBUTOR_INSIGHTS_PATH);
17144
+ if (declared.kind !== "usable") return void 0;
17145
+ const enabled = enabledForStatus(status, true);
17146
+ if (enabled === void 0) return void 0;
17147
+ const declaredEnabled = declaredBlock["Enabled"];
17148
+ const block = { Enabled: typeof declaredEnabled !== "string" ? enabled : declared.enabled === enabled ? declaredEnabled : String(enabled) };
17149
+ if (enabled && declared.mode !== void 0) {
17150
+ const liveMode = mode ?? (status === "ENABLING" ? declared.mode : void 0);
17151
+ if (liveMode !== void 0) block["Mode"] = liveMode;
17152
+ }
17153
+ return block;
17154
+ }
17155
+
16946
17156
  //#endregion
16947
17157
  //#region src/provisioning/dynamodb-index-busy-delete.ts
16948
17158
  /**
@@ -18767,16 +18977,10 @@ function warmThroughputAlreadyMatches(desired, live) {
18767
18977
  * to decide with and every block is emitted, cleaned of its AWS-managed
18768
18978
  * members.
18769
18979
  *
18770
- * Known residual, unchanged by this fix: CFn's per-index
18771
- * `ContributorInsightsSpecification` has no `DescribeTable` counterpart (it
18772
- * needs a per-index `DescribeContributorInsights` call), so a template
18773
- * declaring one still cannot converge against a `properties` baseline. It is
18774
- * ALSO dropped on the write side, since `create()` forwards the CFn blob to
18775
- * `CreateTable` and the SDK serializer discards the unknown member — still
18776
- * true after round 6 taught that path to coerce `WarmThroughput`, because the
18777
- * entry is rebuilt with a SPREAD and every other member, known or not, rides
18778
- * along unchanged. Filed as issue
18779
- * [#1782](https://github.com/go-to-k/cdkd/issues/1782).
18980
+ * CFn's per-index `ContributorInsightsSpecification` is NOT mapped here: it
18981
+ * has no `DescribeTable` counterpart, so `readCurrentState` adds it afterwards
18982
+ * from a gated per-index `DescribeContributorInsights` (issue
18983
+ * [#1782](https://github.com/go-to-k/cdkd/issues/1782)).
18780
18984
  */
18781
18985
  function reverseMapSecondaryIndex(live, desired, bagInformative) {
18782
18986
  const out = {};
@@ -19042,6 +19246,10 @@ var DynamoDBTableProvider = class {
19042
19246
  let tableCreated = false;
19043
19247
  try {
19044
19248
  const billingMode = requireConfigString(properties["BillingMode"], "PROVISIONED", "AWS::DynamoDB::Table BillingMode", replayWarn(this.logger, context));
19249
+ if (context?.replayingState !== true) {
19250
+ const [refusal] = contributorInsightsRefusals(properties["ContributorInsightsSpecification"], properties["GlobalSecondaryIndexes"]);
19251
+ if (refusal !== void 0) throw new Error(`${refusal}. Fix the template value`);
19252
+ }
19045
19253
  const createParams = {
19046
19254
  TableName: tableName,
19047
19255
  KeySchema: keySchema,
@@ -19066,7 +19274,7 @@ var DynamoDBTableProvider = class {
19066
19274
  };
19067
19275
  if (properties["GlobalSecondaryIndexes"]) {
19068
19276
  const declaredGsis = properties["GlobalSecondaryIndexes"];
19069
- createParams.GlobalSecondaryIndexes = Array.isArray(declaredGsis) ? declaredGsis.map((entry) => this.coerceIndexThroughputForCreate(logicalId, entry, maskSecrets)) : declaredGsis;
19277
+ createParams.GlobalSecondaryIndexes = Array.isArray(declaredGsis) ? declaredGsis.map((entry) => this.coerceIndexThroughputForCreate(logicalId, stripIndexContributorInsights(entry), maskSecrets)) : declaredGsis;
19070
19278
  }
19071
19279
  if (properties["LocalSecondaryIndexes"]) createParams.LocalSecondaryIndexes = properties["LocalSecondaryIndexes"];
19072
19280
  const sse = mapSSESpecification(properties["SSESpecification"]);
@@ -19083,7 +19291,8 @@ var DynamoDBTableProvider = class {
19083
19291
  await this.applyPointInTimeRecovery(tableName, properties["PointInTimeRecoverySpecification"], void 0, maskSecrets);
19084
19292
  await this.applyTimeToLive(tableName, properties["TimeToLiveSpecification"]);
19085
19293
  await this.applyKinesisStreamingDestination(tableName, properties["KinesisStreamSpecification"], void 0, maskSecrets);
19086
- await this.applyContributorInsights(tableName, properties["ContributorInsightsSpecification"]);
19294
+ await this.applyContributorInsights(tableName, properties["ContributorInsightsSpecification"], void 0, maskSecrets);
19295
+ await this.applyIndexContributorInsights(tableName, properties["GlobalSecondaryIndexes"], void 0, maskSecrets);
19087
19296
  this.logger.debug(`Successfully created DynamoDB table ${logicalId}: ${tableName}`);
19088
19297
  return {
19089
19298
  physicalId: tableName,
@@ -19318,7 +19527,8 @@ var DynamoDBTableProvider = class {
19318
19527
  await this.applyResourcePolicy(table.TableArn, properties["ResourcePolicy"], previousProperties["ResourcePolicy"]);
19319
19528
  }
19320
19529
  if (JSON.stringify(properties["KinesisStreamSpecification"]) !== JSON.stringify(previousProperties["KinesisStreamSpecification"])) await this.applyKinesisStreamingDestination(physicalId, properties["KinesisStreamSpecification"], previousProperties["KinesisStreamSpecification"], maskSecrets);
19321
- if (JSON.stringify(properties["ContributorInsightsSpecification"]) !== JSON.stringify(previousProperties["ContributorInsightsSpecification"])) await this.applyContributorInsights(physicalId, properties["ContributorInsightsSpecification"], previousProperties["ContributorInsightsSpecification"]);
19530
+ if (JSON.stringify(properties["ContributorInsightsSpecification"]) !== JSON.stringify(previousProperties["ContributorInsightsSpecification"])) await this.applyContributorInsights(physicalId, properties["ContributorInsightsSpecification"], previousProperties["ContributorInsightsSpecification"], maskSecrets);
19531
+ await this.applyIndexContributorInsights(physicalId, properties["GlobalSecondaryIndexes"], previousProperties["GlobalSecondaryIndexes"], maskSecrets);
19322
19532
  return {
19323
19533
  physicalId,
19324
19534
  wasReplaced: false,
@@ -19734,24 +19944,53 @@ var DynamoDBTableProvider = class {
19734
19944
  *
19735
19945
  * Called from both `create()` (after the table is ACTIVE) and `update()`
19736
19946
  * (only when the value changed). On `update()`-side removal — template drops
19737
- * the block but it was present before — insights is disabled.
19947
+ * the block but it was present before — insights is disabled. The block is
19948
+ * read by the reader the per-index twin uses
19949
+ * ({@link applyIndexContributorInsights}), so the two cannot disagree about
19950
+ * one shape: an unreadable block is announced and SKIPPED rather than read as
19951
+ * a default (`Boolean('false')` used to ENABLE what the template disabled).
19738
19952
  */
19739
- async applyContributorInsights(tableName, spec, previousSpec) {
19740
- let action;
19741
- let mode;
19742
- if (spec !== void 0 && spec !== null) {
19743
- const s = spec;
19744
- const enabled = Boolean(s["Enabled"]);
19745
- action = enabled ? "ENABLE" : "DISABLE";
19746
- if (enabled && s["Mode"] !== void 0) mode = s["Mode"];
19747
- } else if (previousSpec !== void 0 && previousSpec !== null) action = "DISABLE";
19748
- if (action === void 0) return;
19953
+ async applyContributorInsights(tableName, spec, previousSpec, maskSecrets) {
19954
+ const warn = (message) => this.logger.warn(maskSecrets(message));
19955
+ const op = planContributorInsightsOp(readContributorInsightsSpec(spec, CONTRIBUTOR_INSIGHTS_KEY), readContributorInsightsSpec(previousSpec, CONTRIBUTOR_INSIGHTS_KEY), (reason) => warn(`DynamoDB table ${tableName}: ${reason}, so Contributor Insights was left as it is. Fix the template value and re-deploy.`));
19956
+ if (op === void 0) return;
19957
+ await this.sendContributorInsightsOp(tableName, op, maskSecrets);
19958
+ }
19959
+ /**
19960
+ * Apply every per-index `ContributorInsightsSpecification`
19961
+ * (`GlobalSecondaryIndexes[].ContributorInsightsSpecification`, issue
19962
+ * [#1782](https://github.com/go-to-k/cdkd/issues/1782)) — the table-level
19963
+ * call with `IndexName` added, one call per index whose block changed.
19964
+ *
19965
+ * The block is not a member of the SDK's `GlobalSecondaryIndex`, so until
19966
+ * this existed it was dropped by the serializer on `CreateTable` and never
19967
+ * sent by `applyGsiUpdates`: the deploy was green and the feature was off.
19968
+ *
19969
+ * `UpdateContributorInsights` answers `ResourceNotFoundException` for an
19970
+ * index that is not ACTIVE yet, which {@link retryOnTransientControlPlane}
19971
+ * does not retry, so the index wait runs FIRST — and only when there is a
19972
+ * call to make, so an ordinary update pays no extra `DescribeTable`.
19973
+ * `previousIndexes` is `undefined` on `create()`. Removal and rollback
19974
+ * semantics live on {@link planContributorInsightsOp}.
19975
+ */
19976
+ async applyIndexContributorInsights(tableName, desiredIndexes, previousIndexes, maskSecrets) {
19977
+ const warn = (message) => this.logger.warn(maskSecrets(message));
19978
+ 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.`));
19979
+ if (ops.length === 0) return;
19980
+ await this.waitForTableAndIndexesActive(tableName);
19981
+ for (const op of ops) await this.sendContributorInsightsOp(tableName, op, maskSecrets);
19982
+ }
19983
+ /** Issue ONE `UpdateContributorInsights`, against the table or one index. */
19984
+ async sendContributorInsightsOp(tableName, op, maskSecrets) {
19985
+ const debug = (message) => this.logger.debug(maskSecrets(message));
19986
+ const target = op.indexName === void 0 ? `DynamoDB table ${tableName}` : this.indexScopeAt(op.indexName, tableName, maskSecrets);
19749
19987
  await this.retryOnTransientControlPlane(() => this.dynamoDBClient.send(new UpdateContributorInsightsCommand({
19750
19988
  TableName: tableName,
19751
- ContributorInsightsAction: action,
19752
- ...mode ? { ContributorInsightsMode: mode } : {}
19753
- })), `set ContributorInsights on ${tableName}`);
19754
- this.logger.debug(`Set ContributorInsightsAction=${action}${mode !== void 0 ? ` Mode=${mode}` : ""} on DynamoDB table ${tableName}`);
19989
+ ...op.indexName !== void 0 ? { IndexName: op.indexName } : {},
19990
+ ContributorInsightsAction: op.action,
19991
+ ...op.mode !== void 0 ? { ContributorInsightsMode: op.mode } : {}
19992
+ })), `set ContributorInsights on ${target}`);
19993
+ debug(`Set ContributorInsightsAction=${op.action}${op.mode !== void 0 ? ` Mode=${op.mode}` : ""} on ${target}`);
19755
19994
  }
19756
19995
  /**
19757
19996
  * Poll DescribeTable until the table reaches ACTIVE status
@@ -20732,32 +20971,35 @@ var DynamoDBTableProvider = class {
20732
20971
  * level: it is order-SIGNIFICANT (HASH before RANGE), so sorting it would
20733
20972
  * HIDE a real key change rather than remove a phantom one.
20734
20973
  *
20735
- * `GlobalSecondaryIndexes` / `LocalSecondaryIndexes` are NOT declared either,
20736
- * and that is a MECHANISM limit rather than a judgement about the lists —
20737
- * both really are sets keyed by `IndexName` (issue #1767 proposes declaring
20738
- * them). Every entry here is a SUBTREE declaration, and unlike
20739
- * `getDriftUnknownPaths` this walk DESCENDS INTO ARRAY ELEMENTS giving each
20740
- * the parent's path (`drift-normalize.ts`), so a `'GlobalSecondaryIndexes'`
20741
- * entry also reaches `GlobalSecondaryIndexes.KeySchema` and would sort the
20742
- * per-index key schema — reversing the sentence above at the index level
20743
- * only. Issue #1767 calls the sort and the member reverse-map separable;
20744
- * this change ships the reverse-map, and the ordering half needed a
20745
- * leaf-only form of the declaration first (issue
20746
- * [#1783](https://github.com/go-to-k/cdkd/issues/1783)). **That form now
20747
- * EXISTS** — `LEAF_ONLY_PATH_SUFFIX` (`[]`) landed in
20748
- * [#1799](https://github.com/go-to-k/cdkd/pull/1799), so
20749
- * `'GlobalSecondaryIndexes[]'` would claim the list alone without reaching
20750
- * the per-index `KeySchema`. It is deliberately NOT declared here yet:
20751
- * adopting it is its own change with its own real-AWS verification, tracked
20752
- * as issue [#1812](https://github.com/go-to-k/cdkd/issues/1812). Consequence
20753
- * until then, stated so it is not mistaken for solved: an index list AWS
20754
- * returns in a different ORDER than the template declared is still phantom
20755
- * drift against a `properties` baseline. It is not reachable on the ordinary
20756
- * `observedProperties` path, where both sides come from this same readback.
20974
+ * `GlobalSecondaryIndexes` / `LocalSecondaryIndexes` ARE declared, in the
20975
+ * LEAF-ONLY form (`LEAF_ONLY_PATH_SUFFIX`, `[]`, from
20976
+ * [#1799](https://github.com/go-to-k/cdkd/pull/1799)): both are sets keyed
20977
+ * by `IndexName`, and `DescribeTable` does not return them in the template's
20978
+ * order. A plain SUBTREE entry would be wrong — the unordered walk descends
20979
+ * into array elements giving each the parent's path (`drift-normalize.ts`),
20980
+ * so `'GlobalSecondaryIndexes'` would also sort each index's `KeySchema` and
20981
+ * reverse the sentence above at the index level. The leaf-only form sorts
20982
+ * the list and stops there.
20983
+ *
20984
+ * Measured on real AWS by issue
20985
+ * [#1782](https://github.com/go-to-k/cdkd/issues/1782)'s two-index fixture:
20986
+ * a table created with `[giA, giB]` read back as `[giB, giA]`, so a
20987
+ * `properties` baseline reported whole-list drift although every member
20988
+ * round-tripped. The phantom pre-dates that issue — the #1767 arm only ever
20989
+ * had ONE index — and was not reachable against an `observedProperties`
20990
+ * baseline, where both sides come from the same readback. Both comparison
20991
+ * sides are sorted, by a key-order-independent serialization of the whole
20992
+ * entry, so a baseline captured in AWS's order by an earlier binary keeps
20993
+ * converging; a REAL difference still reports the list, since entries that
20994
+ * differ cannot be made equal by reordering them.
20757
20995
  */
20758
20996
  getDriftUnorderedPaths(resourceType) {
20759
20997
  if (resourceType !== "AWS::DynamoDB::Table") return [];
20760
- return ["AttributeDefinitions"];
20998
+ return [
20999
+ "AttributeDefinitions",
21000
+ "GlobalSecondaryIndexes[]",
21001
+ "LocalSecondaryIndexes[]"
21002
+ ];
20761
21003
  }
20762
21004
  /**
20763
21005
  * `WarmThroughput` is reported by AWS for EVERY table, whether or not the
@@ -20902,7 +21144,9 @@ var DynamoDBTableProvider = class {
20902
21144
  * caller passes). It is consulted for the table-level `WarmThroughput` — see
20903
21145
  * {@link getDriftUnknownPaths} for the measurement and the reasoning — and,
20904
21146
  * since issue #1767, for the per-index throughput blocks of each secondary
20905
- * index, matched by `IndexName`.
21147
+ * index, matched by `IndexName`. Since issue #1782 it also gates the per-index
21148
+ * `ContributorInsightsSpecification` read, which costs one
21149
+ * `DescribeContributorInsights` per declaring index.
20906
21150
  */
20907
21151
  async readCurrentState(physicalId, _logicalId, _resourceType, properties) {
20908
21152
  try {
@@ -21001,15 +21245,31 @@ var DynamoDBTableProvider = class {
21001
21245
  }
21002
21246
  try {
21003
21247
  const ciResp = await this.dynamoDBClient.send(new DescribeContributorInsightsCommand({ TableName: physicalId }));
21004
- const status = ciResp.ContributorInsightsStatus;
21005
- if (status === "ENABLED" || status === "DISABLED") {
21006
- const cspec = { Enabled: status === "ENABLED" };
21007
- if (status === "ENABLED" && ciResp.ContributorInsightsMode !== void 0) cspec["Mode"] = ciResp.ContributorInsightsMode;
21008
- result["ContributorInsightsSpecification"] = cspec;
21009
- }
21248
+ const cspec = reverseMapTableContributorInsights(ciResp.ContributorInsightsStatus, ciResp.ContributorInsightsMode);
21249
+ if (cspec !== void 0) result["ContributorInsightsSpecification"] = cspec;
21010
21250
  } catch (err) {
21011
21251
  this.logger.debug(`Could not read ContributorInsights for ${physicalId}: ${describeAwsFailure(err).detail}`);
21012
21252
  }
21253
+ const emittedGsis = result["GlobalSecondaryIndexes"];
21254
+ if (bagInformative && Array.isArray(emittedGsis)) {
21255
+ const desiredByName = desiredIndexEntriesByName(properties?.["GlobalSecondaryIndexes"]);
21256
+ for (const emitted of emittedGsis) {
21257
+ const indexName = emitted["IndexName"];
21258
+ if (typeof indexName !== "string") continue;
21259
+ const desiredEntry = desiredByName.get(indexName);
21260
+ if (!indexDeclaresContributorInsights(desiredEntry)) continue;
21261
+ try {
21262
+ const indexResp = await this.dynamoDBClient.send(new DescribeContributorInsightsCommand({
21263
+ TableName: physicalId,
21264
+ IndexName: indexName
21265
+ }));
21266
+ const block = reverseMapIndexContributorInsights(indexResp.ContributorInsightsStatus, indexResp.ContributorInsightsMode, desiredEntry?.[CONTRIBUTOR_INSIGHTS_KEY]);
21267
+ if (block !== void 0) emitted[CONTRIBUTOR_INSIGHTS_KEY] = block;
21268
+ } catch (err) {
21269
+ this.logger.debug(`Could not read ContributorInsights for index ${indexName} of ${physicalId}: ${describeAwsFailure(err).detail}`);
21270
+ }
21271
+ }
21272
+ }
21013
21273
  return result;
21014
21274
  } catch (err) {
21015
21275
  if (err instanceof ResourceNotFoundException$1) return void 0;
@@ -84981,6 +85241,368 @@ function createForceUnlockCommand() {
84981
85241
  return cmd;
84982
85242
  }
84983
85243
 
85244
+ //#endregion
85245
+ //#region src/cli/commands/import-deployed-parameters.ts
85246
+ /**
85247
+ * UNVERIFIABLE INPUTS for `cdkd import`'s observed-baseline refusal (issue
85248
+ * [#2854](https://github.com/go-to-k/cdkd/issues/2854)).
85249
+ *
85250
+ * `cdkd import` accepts no parameter values, so every template parameter is
85251
+ * bound to its `Default`. When the resources were deployed by a CloudFormation
85252
+ * stack, the value CloudFormation actually used can differ — and the ordinary
85253
+ * secret-carrying shape is a placeholder `Default` (`CHANGEME`) over a DEPLOYED
85254
+ * value of `{{resolve:secretsmanager:...}}`. The persisted `properties` then
85255
+ * spell no reference anywhere, the position-based redaction of the observed
85256
+ * capture has no evidence, and the decrypted readback would be persisted.
85257
+ *
85258
+ * The remedy here is deliberately PREMISE-INDEPENDENT. It never looks for a
85259
+ * `{{resolve:` opener in what `DescribeStacks` returns (AWS documents
85260
+ * `ParameterValue` as "the input value", which says the reference comes back
85261
+ * literally, but nothing in this module relies on it). It asks one question —
85262
+ * "is the deployed value PROVABLY the value this import bound?" — and treats
85263
+ * every other answer as divergent: a literal reference, the `****` a `NoEcho`
85264
+ * parameter is masked to, a value AWS resolved, a parameter missing from the
85265
+ * response, a `DescribeStacks` that failed. All of those differ from the bound
85266
+ * placeholder, so all of them refuse.
85267
+ *
85268
+ * THE DEPLOYED VALUES ARE COMPARISON-ONLY. They are never bound into the
85269
+ * resolve, never logged, never put in an error message, and never persisted:
85270
+ * binding them is the one design that would leak if AWS ever returned a
85271
+ * resolved value. {@link DeployedParameters} holds them in a `#private` field so
85272
+ * that a stray `JSON.stringify` / `util.inspect` / template literal of the
85273
+ * object renders nothing, and an SDK failure is reported by its error NAME
85274
+ * alone, because an SDK message can echo request input.
85275
+ *
85276
+ * TAINT THROUGH ANOTHER RESOURCE'S ATTRIBUTES IS FOLLOWED within one stack: the
85277
+ * walk records the target of every `Fn::GetAtt` / `${A.Attr}`, and the caller
85278
+ * closes the refused set over those targets, so a resource reading an attribute
85279
+ * of a refused resource (`{Fn::GetAtt: [SsmParam, Value]}` into a Lambda
85280
+ * environment — what CDK's `param.stringValue` emits) is refused with it. A
85281
+ * plain `Ref` of a resource is NOT followed: it yields the physical id, and a
85282
+ * secret used as a resource NAME is already disclosed by the identifier itself.
85283
+ *
85284
+ * KNOWN RESIDUAL, not followed: taint ACROSS the nested-stack boundary. A
85285
+ * parent property reading `Fn::GetAtt: [Child, Outputs.X]`, where the child's
85286
+ * output derives from a divergent CHILD parameter, is judged on the parent's
85287
+ * own parameters only — the root is resolved before any child is read.
85288
+ */
85289
+ /**
85290
+ * What CloudFormation reports a stack was deployed with. Opaque on purpose:
85291
+ * the only question it answers is {@link DeployedParameters.provablyEquals}.
85292
+ */
85293
+ var DeployedParameters = class DeployedParameters {
85294
+ #entries;
85295
+ constructor(entries) {
85296
+ this.#entries = entries;
85297
+ }
85298
+ /** `DescribeStacks` answered; `entries` is its `Parameters` list. */
85299
+ static fromDescribeStacks(parameters) {
85300
+ const entries = /* @__PURE__ */ new Map();
85301
+ for (const parameter of parameters) {
85302
+ if (typeof parameter.ParameterKey !== "string") continue;
85303
+ entries.set(parameter.ParameterKey, {
85304
+ value: parameter.ParameterValue,
85305
+ resolvedValue: parameter.ResolvedValue
85306
+ });
85307
+ }
85308
+ return new DeployedParameters(entries);
85309
+ }
85310
+ /** The deployed values could not be read: NOTHING is provable. */
85311
+ static unavailable() {
85312
+ return new DeployedParameters(void 0);
85313
+ }
85314
+ /**
85315
+ * Whether the deployed value of `name` is provably the value the import
85316
+ * BOUND. The deployed string goes through the SAME coercion the resolver
85317
+ * applied to the bound one (`Number`, `CommaDelimitedList`, `List<...>`), so
85318
+ * `'1'` equals a bound `1` and `'a, b'` equals a bound `['a', 'b']`. For an
85319
+ * SSM-typed parameter the resolver binds the RESOLVED value, so that is the
85320
+ * side compared — `ParameterValue` is only the key there.
85321
+ */
85322
+ provablyEquals(name, type, bound) {
85323
+ const entry = this.#entries?.get(name);
85324
+ if (entry === void 0 || typeof type !== "string") return false;
85325
+ const ssmType = type.startsWith("AWS::SSM::Parameter::Value");
85326
+ const deployed = ssmType ? entry.resolvedValue : entry.value;
85327
+ if (typeof deployed !== "string") return false;
85328
+ const comparedType = ssmType ? ssmResolvedValueType(type) : type;
85329
+ return stableEquals(comparedType === void 0 ? deployed : coerceParameterTypedValue(deployed, comparedType), bound);
85330
+ }
85331
+ };
85332
+ /** Structural equality for the scalar / scalar-array values a parameter binds to. */
85333
+ function stableEquals(a, b) {
85334
+ if (Array.isArray(a) || Array.isArray(b)) {
85335
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
85336
+ return a.every((element, index) => stableEquals(element, b[index]));
85337
+ }
85338
+ if (typeof a === "string" && typeof b === "number") return Number.isFinite(b) && a === String(b);
85339
+ if (typeof a === "number" || typeof b === "number") return typeof a === "number" && typeof b === "number" && Number.isFinite(a) && a === b;
85340
+ if (typeof a === "string" && typeof b === "boolean") return a === String(b);
85341
+ return typeof a === "string" && typeof b === "string" && a === b;
85342
+ }
85343
+ async function readDeployedParameters(cfnStackName, cfnClient, logger, displayName, options) {
85344
+ try {
85345
+ const stack = (await cfnClient.send(new DescribeStacksCommand({ StackName: cfnStackName }))).Stacks?.[0];
85346
+ if (!stack) {
85347
+ logger.warn(unavailableWarning(displayName, "the stack was not returned"));
85348
+ return DeployedParameters.unavailable();
85349
+ }
85350
+ return DeployedParameters.fromDescribeStacks(stack.Parameters ?? []);
85351
+ } catch (err) {
85352
+ if (options?.absentStackIsNoSource === true && err instanceof Error && err.name === "ValidationError" && /does not exist/i.test(err.message)) return;
85353
+ const name = err instanceof Error && /^[A-Za-z0-9_.]{1,80}$/.test(err.name) ? err.name : "error";
85354
+ logger.warn(unavailableWarning(displayName, name));
85355
+ return DeployedParameters.unavailable();
85356
+ }
85357
+ }
85358
+ function unavailableWarning(displayName, reason) {
85359
+ return `Could not read the deployed parameter values of CloudFormation stack '${displayName}' (${reason}). 'cdkd import' needs the cloudformation:DescribeStacks permission to prove that a template parameter was deployed with its 'Default'; without it every resource that depends on a template parameter is imported WITHOUT an observed drift baseline. Review 'cdkd diff' before the next 'cdkd deploy'.`;
85360
+ }
85361
+ /**
85362
+ * The declared parameters whose deployed value is NOT provably the bound one.
85363
+ * A parameter the import could not bind at all (no `Default`, an unreferenced
85364
+ * or failed SSM default, the empty fallback bag) is divergent by definition.
85365
+ */
85366
+ function divergentParameterNames(template, bound, deployed) {
85367
+ const divergent = /* @__PURE__ */ new Set();
85368
+ const declared = template.Parameters;
85369
+ if (declared === void 0 || declared === null || typeof declared !== "object") return divergent;
85370
+ for (const [name, definition] of Object.entries(declared)) {
85371
+ const record = definition !== null && typeof definition === "object" ? definition : void 0;
85372
+ const type = record?.["Type"];
85373
+ const noEcho = record?.["NoEcho"];
85374
+ if (noEcho === true || typeof noEcho === "string" && noEcho.toLowerCase() === "true") {
85375
+ divergent.add(name);
85376
+ continue;
85377
+ }
85378
+ if (!Object.hasOwn(bound, name) || !deployed.provablyEquals(name, type, bound[name])) divergent.add(name);
85379
+ }
85380
+ return divergent;
85381
+ }
85382
+ /**
85383
+ * Every intrinsic the dependence walk knows how to read. An `Fn::`-prefixed
85384
+ * key OUTSIDE this list is unclassifiable: it may name a parameter in a way
85385
+ * the generic recursion cannot see, so the walk fails closed on it.
85386
+ */
85387
+ const KNOWN_INTRINSICS = /* @__PURE__ */ new Set([
85388
+ "Fn::GetAtt",
85389
+ "Fn::Join",
85390
+ "Fn::Sub",
85391
+ "Fn::Select",
85392
+ "Fn::Split",
85393
+ "Fn::If",
85394
+ "Fn::Equals",
85395
+ "Fn::And",
85396
+ "Fn::Or",
85397
+ "Fn::Not",
85398
+ "Fn::ImportValue",
85399
+ "Fn::GetStackOutput",
85400
+ "Fn::FindInMap",
85401
+ "Fn::Base64",
85402
+ "Fn::GetAZs",
85403
+ "Fn::Cidr"
85404
+ ]);
85405
+ /** `${Name}` / `${Name.Attr}` in an `Fn::Sub` string; `${!Literal}` is an escape. */
85406
+ const SUB_VARIABLE = /\$\{([^!}][^}]*)\}/g;
85407
+ /**
85408
+ * Which names (parameters, but also resources and pseudo parameters — the
85409
+ * caller intersects with the divergent set) the RAW `Properties` of a resource
85410
+ * depend on, plus its resource-level `Condition`.
85411
+ *
85412
+ * Generic by design: instead of modelling each intrinsic's argument grammar it
85413
+ * recurses into EVERY value, so a `Ref` nested in an `Fn::Join` / `Fn::Select`
85414
+ * / `Fn::Split` operand, an `Fn::FindInMap` key, or an `Fn::Sub` variable-map
85415
+ * value is found without being named. Only the three forms that name something
85416
+ * by BARE STRING need their own arm: `Ref`, `Fn::Sub`'s template text, and a
85417
+ * condition name (`Fn::If`'s first argument, and `Condition` inside the
85418
+ * `Conditions` section), which is followed transitively through
85419
+ * `template.Conditions`.
85420
+ *
85421
+ * May throw `RangeError` on a bag deeper than the call stack; the caller wraps
85422
+ * it fail-closed, like the discard walk beside it.
85423
+ */
85424
+ function collectParameterDependencies(properties, resourceCondition, template) {
85425
+ const names = /* @__PURE__ */ new Set();
85426
+ const attributeTargets = /* @__PURE__ */ new Set();
85427
+ const visitedConditions = /* @__PURE__ */ new Set();
85428
+ let unclassifiable = false;
85429
+ const conditions = template.Conditions !== null && typeof template.Conditions === "object" ? template.Conditions : {};
85430
+ const followCondition = (name) => {
85431
+ if (typeof name !== "string") {
85432
+ unclassifiable = true;
85433
+ return;
85434
+ }
85435
+ if (visitedConditions.has(name)) return;
85436
+ visitedConditions.add(name);
85437
+ if (!Object.hasOwn(conditions, name)) {
85438
+ unclassifiable = true;
85439
+ return;
85440
+ }
85441
+ walk(conditions[name], true);
85442
+ };
85443
+ const walk = (node, inConditions) => {
85444
+ if (node === null || typeof node !== "object") return;
85445
+ if (Array.isArray(node)) {
85446
+ for (const element of node) walk(element, inConditions);
85447
+ return;
85448
+ }
85449
+ for (const [key, value] of Object.entries(node)) {
85450
+ if (key === "Ref") {
85451
+ if (typeof value === "string") names.add(value);
85452
+ else unclassifiable = true;
85453
+ continue;
85454
+ }
85455
+ if (key === "Fn::Sub") {
85456
+ const text = Array.isArray(value) ? value[0] : value;
85457
+ if (typeof text !== "string" || Array.isArray(value) && value.length !== 2) unclassifiable = true;
85458
+ else for (const match of text.matchAll(SUB_VARIABLE)) {
85459
+ const variable = (match[1] ?? "").trim();
85460
+ names.add(variable);
85461
+ const dot = variable.indexOf(".");
85462
+ if (dot > 0) {
85463
+ names.add(variable.slice(0, dot));
85464
+ attributeTargets.add(variable.slice(0, dot));
85465
+ }
85466
+ }
85467
+ if (Array.isArray(value)) walk(value[1], inConditions);
85468
+ continue;
85469
+ }
85470
+ if (key === "Fn::GetAtt") {
85471
+ const target = Array.isArray(value) ? value[0] : value;
85472
+ if (typeof target === "string") {
85473
+ const dot = target.indexOf(".");
85474
+ const logicalId = Array.isArray(value) || dot <= 0 ? target : target.slice(0, dot);
85475
+ names.add(logicalId);
85476
+ attributeTargets.add(logicalId);
85477
+ } else unclassifiable = true;
85478
+ walk(value, inConditions);
85479
+ continue;
85480
+ }
85481
+ if (key === "Fn::If") {
85482
+ if (Array.isArray(value) && value.length === 3) {
85483
+ followCondition(value[0]);
85484
+ walk(value[1], inConditions);
85485
+ walk(value[2], inConditions);
85486
+ } else unclassifiable = true;
85487
+ continue;
85488
+ }
85489
+ if (key === "Condition" && inConditions) {
85490
+ followCondition(value);
85491
+ continue;
85492
+ }
85493
+ if (key.startsWith("Fn::") && !KNOWN_INTRINSICS.has(key)) unclassifiable = true;
85494
+ walk(value, inConditions);
85495
+ }
85496
+ };
85497
+ walk(properties, false);
85498
+ if (resourceCondition !== void 0) followCondition(resourceCondition);
85499
+ return {
85500
+ names,
85501
+ attributeTargets,
85502
+ unclassifiable
85503
+ };
85504
+ }
85505
+ /**
85506
+ * The divergent parameters that ANYTHING in the template names. A declared
85507
+ * parameter nothing names cannot have shaped a resource — and CDK's
85508
+ * `BootstrapVersion` is exactly that on every default-synthesized stack: SSM
85509
+ * typed, never bound by the resolver because nothing references it, hence
85510
+ * always "divergent". Left in, it would arm the unclassifiable-shape refusal on
85511
+ * stacks whose every real parameter is proven.
85512
+ *
85513
+ * "Names" is the UNION of this module's walk and `TemplateParser`'s reference
85514
+ * scan — the same scan the resolver uses to decide an SSM-typed parameter is
85515
+ * unreferenced — so the two cannot disagree about `BootstrapVersion`. An
85516
+ * unclassifiable shape does NOT keep the whole set here: CloudFormation names a
85517
+ * parameter only through `Ref`, `Fn::Sub` text or a condition, and the generic
85518
+ * recursion reads those inside any intrinsic, known or not. A walk that THROWS
85519
+ * keeps the whole set (fail closed).
85520
+ */
85521
+ function reachableDivergentParameters(template, divergent, rawBags) {
85522
+ if (divergent.size === 0) return /* @__PURE__ */ new Set();
85523
+ try {
85524
+ const roots = [
85525
+ rawBags,
85526
+ template.Resources,
85527
+ template.Outputs,
85528
+ template.Conditions
85529
+ ];
85530
+ const named = new Set(collectParameterDependencies(roots, void 0, template).names);
85531
+ const parser = new TemplateParser();
85532
+ for (const root of roots) {
85533
+ if (root === null || typeof root !== "object") continue;
85534
+ for (const name of parser.extractReferences(root)) named.add(name);
85535
+ }
85536
+ return new Set([...divergent].filter((name) => named.has(name)));
85537
+ } catch {
85538
+ return new Set(divergent);
85539
+ }
85540
+ }
85541
+ /** Whether any DECLARED parameter is named by anything in the template. */
85542
+ function namesAnyDeclaredParameter(template) {
85543
+ const declared = template.Parameters;
85544
+ if (declared === null || typeof declared !== "object") return false;
85545
+ const names = new Set(Object.keys(declared));
85546
+ return names.size > 0 && reachableDivergentParameters(template, names, []).size > 0;
85547
+ }
85548
+ /**
85549
+ * The per-stack ARM 4 verdict. Each resource is judged on its TEMPLATE
85550
+ * definition (`Properties` + `Condition`) AND on the raw bag the state record
85551
+ * carries, unioned: a record preserved by a selective merge holds a previous
85552
+ * run's RESOLVED literal (`Value: 'CHANGEME'`), which names no parameter, so
85553
+ * judging it on the state bag alone would leave nothing to carry to the
85554
+ * freshly imported resource that reads its attribute. Template resources that
85555
+ * are not in state at all are judged too, for the same reason.
85556
+ *
85557
+ * Seeds are direct dependents and unclassifiable bags (a bag that cannot be
85558
+ * read cannot be vouched for, and neither can what reads its attributes); the
85559
+ * refused set is then closed over ATTRIBUTE reads, to a fixpoint.
85560
+ */
85561
+ function computeParameterTaint(template, stateBags, divergent) {
85562
+ const refused = /* @__PURE__ */ new Set();
85563
+ const unclassifiable = /* @__PURE__ */ new Set();
85564
+ const parametersHit = /* @__PURE__ */ new Set();
85565
+ if (divergent.size === 0) return {
85566
+ refused,
85567
+ unclassifiable,
85568
+ parametersHit
85569
+ };
85570
+ const templateResources = template.Resources !== null && typeof template.Resources === "object" ? template.Resources : {};
85571
+ const readers = /* @__PURE__ */ new Map();
85572
+ for (const logicalId of /* @__PURE__ */ new Set([...Object.keys(templateResources), ...stateBags.keys()])) {
85573
+ const definition = Object.hasOwn(templateResources, logicalId) ? templateResources[logicalId] : void 0;
85574
+ const record = definition !== null && typeof definition === "object" ? definition : {};
85575
+ try {
85576
+ const dependencies = collectParameterDependencies([record["Properties"], stateBags.get(logicalId)], record["Condition"], template);
85577
+ readers.set(logicalId, new Set(dependencies.attributeTargets));
85578
+ const hits = [...dependencies.names].filter((name) => divergent.has(name));
85579
+ for (const name of hits) parametersHit.add(name);
85580
+ if (hits.length > 0) refused.add(logicalId);
85581
+ else if (dependencies.unclassifiable) {
85582
+ refused.add(logicalId);
85583
+ unclassifiable.add(logicalId);
85584
+ }
85585
+ } catch {
85586
+ refused.add(logicalId);
85587
+ unclassifiable.add(logicalId);
85588
+ }
85589
+ }
85590
+ for (let grew = refused.size > 0; grew;) {
85591
+ grew = false;
85592
+ for (const [logicalId, targets] of readers) {
85593
+ if (refused.has(logicalId)) continue;
85594
+ if (![...targets].some((target) => refused.has(target))) continue;
85595
+ refused.add(logicalId);
85596
+ grew = true;
85597
+ }
85598
+ }
85599
+ return {
85600
+ refused,
85601
+ unclassifiable,
85602
+ parametersHit
85603
+ };
85604
+ }
85605
+
84984
85606
  //#endregion
84985
85607
  //#region src/cli/commands/import.ts
84986
85608
  async function importCommand(stackArg, options) {
@@ -85183,7 +85805,8 @@ async function importCommand(stackArg, options) {
85183
85805
  }
85184
85806
  }
85185
85807
  const stackState = buildStackState(stackInfo.stackName, targetRegion, rows, templateParser, stateTemplate, existingState, selectiveMode);
85186
- await captureObservedForImportedResources(stackState, providerRegistry, logger, await withSharedDrainBudget(() => resolveImportedProperties(stackState, stateTemplate, targetRegion, stateBackend, logger)), rebuiltLogicalIdsFrom(rows, stateTemplate));
85808
+ const deployedParameters = !namesAnyDeclaredParameter(stateTemplate) ? void 0 : migrationCfnStackName !== void 0 ? await readDeployedParameters(migrationCfnStackName, awsClients.cloudFormation, logger, displaySafe(migrationCfnStackName)) : await readDeployedParameters(stackInfo.stackName, awsClients.cloudFormation, logger, displaySafe(stackInfo.stackName), { absentStackIsNoSource: true });
85809
+ await captureObservedForImportedResources(stackState, providerRegistry, logger, await withSharedDrainBudget(() => resolveImportedProperties(stackState, stateTemplate, targetRegion, stateBackend, logger, deployedParameters)), rebuiltLogicalIdsFrom(rows, stateTemplate));
85187
85810
  const saveOptions = {};
85188
85811
  if (existingEtag) saveOptions.expectedEtag = existingEtag;
85189
85812
  if (migrationPending) saveOptions.migrateLegacy = true;
@@ -85203,7 +85826,8 @@ async function importCommand(stackArg, options) {
85203
85826
  lockOwner: owner,
85204
85827
  accountId: accountIdForNestedSynth,
85205
85828
  logger,
85206
- assetRedirect
85829
+ assetRedirect,
85830
+ cfnClient: awsClients.cloudFormation
85207
85831
  });
85208
85832
  if (migrationCfnStackName) {
85209
85833
  const orphaned = resources.length - importedRows.length;
@@ -85651,7 +86275,7 @@ function defaultOnlyParameterTemplate(template) {
85651
86275
  * the clear.
85652
86276
  *
85653
86277
  * The predicate is deliberately CONSERVATIVE — it over-refuses — and that is a
85654
- * decision a review round paid for. Three arms:
86278
+ * decision a review round paid for. Four arms:
85655
86279
  *
85656
86280
  * - **Resolution THREW** — refuse, unconditionally. A throw means the
85657
86281
  * resolver could not finish, so nothing about the persisted bag is
@@ -85692,6 +86316,15 @@ function defaultOnlyParameterTemplate(template) {
85692
86316
  * opener text and no intrinsic anywhere, because every way a discarded
85693
86317
  * subtree could have SOURCED a reference is an intrinsic.
85694
86318
  *
86319
+ * - **An UNVERIFIABLE INPUT** (ARM 4, issue
86320
+ * [#2854](https://github.com/go-to-k/cdkd/issues/2854)). The raw bag
86321
+ * depends on a template parameter whose DEPLOYED value — read from the
86322
+ * backing CloudFormation stack's `DescribeStacks`, root and every nested
86323
+ * child alike — is not provably the `Default` this import bound. Runs only
86324
+ * when `deployedParameters` is supplied; the comparison, the dependence
86325
+ * walk and the comparison-only rule for the deployed values live in
86326
+ * `import-deployed-parameters.ts`.
86327
+ *
85695
86328
  * The cost of a false refusal is one resource's drift baseline until its next
85696
86329
  * deploy; the cost of a false pass is a plaintext secret in `state.json`. So an
85697
86330
  * unmeasurable bag refuses too.
@@ -85706,8 +86339,21 @@ function defaultOnlyParameterTemplate(template) {
85706
86339
  * issue and none of them proven to be all of them:
85707
86340
  *
85708
86341
  * - a parameter binds to a placeholder `Default` while the DEPLOYED value was
85709
- * the reference, so no opener exists in any template this walk is handed --
85710
- * [#2854](https://github.com/go-to-k/cdkd/issues/2854);
86342
+ * the reference, so no opener exists in any template this walk is handed
86343
+ * ([#2854](https://github.com/go-to-k/cdkd/issues/2854)). ARM 4 below
86344
+ * refuses this when a CloudFormation stack backs the import (its
86345
+ * `DescribeStacks` parameters are the comparison); what REMAINS is an
86346
+ * import with NO CloudFormation stack, where no deployed-parameter source
86347
+ * exists, taint across the nested-stack `Outputs` boundary
86348
+ * (`import-deployed-parameters.ts` states it), and the NEXT DEPLOY: the
86349
+ * refusal marker is cleared by any UPDATE that rebuilds the record, and
86350
+ * `cdkd deploy` binds the same placeholder `Default`, so an UPDATE that does
86351
+ * not rewrite the placeholder-bound property captures the readback against
86352
+ * it ([#3462](https://github.com/go-to-k/cdkd/issues/3462);
86353
+ * `deploy-engine.ts` owns the fix). ARM 4 also WIDENS the population of
86354
+ * [#2872](https://github.com/go-to-k/cdkd/issues/2872): a record preserved
86355
+ * by a selective merge that a pre-#2854 import already gave a plaintext
86356
+ * baseline is now refused, and a refusal leaves that old baseline in place;
85711
86357
  * - `redactSecretsForState` cannot PAIR the readback against the source at a
85712
86358
  * position the source carries NO leaf for -- an observed KEY beside a paired
85713
86359
  * one -- so there is nothing to refuse from and the value scan has no needle
@@ -85739,7 +86385,7 @@ function defaultOnlyParameterTemplate(template) {
85739
86385
  * resolver, since the plaintext reaches the message through the resolver's
85740
86386
  * own throw rather than through anything this function writes.
85741
86387
  */
85742
- async function resolveImportedProperties(stackState, template, region, stateBackend, logger) {
86388
+ async function resolveImportedProperties(stackState, template, region, stateBackend, logger, deployedParameters) {
85743
86389
  const unsafeObservedBaselineLogicalIds = /* @__PURE__ */ new Set();
85744
86390
  const entries = Object.entries(stackState.resources ?? {});
85745
86391
  if (entries.length === 0) return unsafeObservedBaselineLogicalIds;
@@ -85767,6 +86413,24 @@ async function resolveImportedProperties(stackState, template, region, stateBack
85767
86413
  logger.debug(`Template condition evaluation failed during import-time property resolution: ${err instanceof Error ? err.message : String(err)} — continuing without conditions.`);
85768
86414
  }
85769
86415
  const unboundParameterNames = Object.keys(template.Parameters ?? {}).filter((name) => isUnboundTemplateParameter(name, template, parameters));
86416
+ let divergentParameters;
86417
+ if (deployedParameters !== void 0) try {
86418
+ divergentParameters = reachableDivergentParameters(template, divergentParameterNames(template, parameters, deployedParameters), entries.map(([, resource]) => resource.properties));
86419
+ } catch {
86420
+ divergentParameters = new Set(Object.keys(template.Parameters ?? {}));
86421
+ }
86422
+ let parameterTaint;
86423
+ if (divergentParameters !== void 0 && divergentParameters.size > 0) try {
86424
+ parameterTaint = computeParameterTaint(template, new Map(entries.map(([logicalId, resource]) => [logicalId, resource.properties])), divergentParameters);
86425
+ } catch (err) {
86426
+ logger.debug(`observed-baseline parameter-dependence walk failed for '${displaySafe(stackState.stackName)}': ${err instanceof Error ? err.name : typeof err} — refusing every baseline in the stack fail-closed.`);
86427
+ const all = new Set(entries.map(([logicalId]) => logicalId));
86428
+ parameterTaint = {
86429
+ refused: all,
86430
+ unclassifiable: all,
86431
+ parametersHit: /* @__PURE__ */ new Set()
86432
+ };
86433
+ }
85770
86434
  const baseContext = {
85771
86435
  template,
85772
86436
  resources: stackState.resources,
@@ -85790,7 +86454,8 @@ async function resolveImportedProperties(stackState, template, region, stateBack
85790
86454
  threw = true;
85791
86455
  }
85792
86456
  if (recordedSecretValues.size > 0 && resource.attributes !== void 0) resource.attributes = redactSecretsForState(resource.attributes, recordedSecretValues);
85793
- if (threw) {
86457
+ const dependsOnDivergent = parameterTaint?.refused.has(logicalId) === true;
86458
+ if (threw || dependsOnDivergent) {
85794
86459
  unsafeObservedBaselineLogicalIds.add(logicalId);
85795
86460
  continue;
85796
86461
  }
@@ -85805,6 +86470,15 @@ async function resolveImportedProperties(stackState, template, region, stateBack
85805
86470
  }
85806
86471
  if (rawOpeners === void 0 || persistedOpeners === void 0 || persistedOpeners < rawOpeners || discardsNonInertSubtree) unsafeObservedBaselineLogicalIds.add(logicalId);
85807
86472
  }
86473
+ if (parameterTaint !== void 0) {
86474
+ const inState = new Set(entries.map(([logicalId]) => logicalId));
86475
+ const refusedHere = [...parameterTaint.refused].filter((id) => inState.has(id));
86476
+ const unreadable = refusedHere.filter((id) => parameterTaint.unclassifiable.has(id));
86477
+ if (refusedHere.length > 0) {
86478
+ const named = [...parameterTaint.parametersHit].sort().map((name) => displaySafe(name));
86479
+ logger.warn(`${refusedHere.length} resource(s) in '${displaySafe(stackState.stackName)}' depend, directly or through another resource's attributes, on template parameter(s) whose deployed CloudFormation value could not be proven equal to the template 'Default' 'cdkd import' binds` + (named.length > 0 ? ` (${named.join(", ")})` : "") + (unreadable.length > 0 ? ` — ${unreadable.length} of them because their properties could not be checked for such a dependence` : "") + ". Their recorded properties hold the 'Default', NOT the deployed value, and no observed drift baseline was captured for them by this import (the live value may be a secret this import cannot position a redaction for). Review 'cdkd diff' before the next 'cdkd deploy'.");
86480
+ }
86481
+ }
85808
86482
  return unsafeObservedBaselineLogicalIds;
85809
86483
  }
85810
86484
  /** The opener every dynamic reference starts with, secret-bearing or not. */
@@ -86166,7 +86840,7 @@ async function captureObservedForImportedResources(stackState, providerRegistry,
86166
86840
  }
86167
86841
  if (unsafeObservedBaselineLogicalIds.has(logicalId)) {
86168
86842
  resource.observedBaselineRefused = true;
86169
- logger.debug(`observedProperties capture SKIPPED for imported ${logicalId} (${resource.resourceType}): the recorded properties no longer spell the template's dynamic reference, so they cannot position a redaction — capturing an AWS readback against them could persist a resolved secret in plaintext. Drift will compare against the recorded properties for this resource until the next successful deploy.`);
86843
+ logger.debug(`observedProperties capture SKIPPED for imported ${logicalId} (${resource.resourceType}): the recorded properties cannot be shown to spell every dynamic reference the deployed resource was built from, so they cannot position a redaction — capturing an AWS readback against them could persist a resolved secret in plaintext. Drift will compare against the recorded properties for this resource until the next successful deploy.`);
86170
86844
  return;
86171
86845
  }
86172
86846
  delete resource.observedBaselineRefused;
@@ -86349,7 +87023,8 @@ async function importNestedStackChildrenRecursive(args) {
86349
87023
  childStackState.parentStack = parentStackName;
86350
87024
  childStackState.parentLogicalId = childLogicalId;
86351
87025
  childStackState.parentRegion = parentRegion;
86352
- await captureObservedForImportedResources(childStackState, providerRegistry, logger, await withSharedDrainBudget(() => resolveImportedProperties(childStackState, childStateTemplate, childRegion, stateBackend, logger)), rebuiltLogicalIdsFrom(rows, childTemplate));
87026
+ const childDeployedParameters = namesAnyDeclaredParameter(childStateTemplate) ? await readDeployedParameters(childTreeNode.stackName, args.cfnClient, logger, displaySafe(childStackName)) : void 0;
87027
+ await captureObservedForImportedResources(childStackState, providerRegistry, logger, await withSharedDrainBudget(() => resolveImportedProperties(childStackState, childStateTemplate, childRegion, stateBackend, logger, childDeployedParameters)), rebuiltLogicalIdsFrom(rows, childTemplate));
86353
87028
  await stateBackend.saveState(childStackName, childRegion, childStackState);
86354
87029
  logger.info(`✓ Nested stack state written: ${childStackName} (${childRegion}) — ${rows.filter((r) => r.outcome === "imported").length} resource(s) imported.`);
86355
87030
  if (childTreeNode.nested.size > 0) await importNestedStackChildrenRecursive({
@@ -86365,7 +87040,8 @@ async function importNestedStackChildrenRecursive(args) {
86365
87040
  lockOwner,
86366
87041
  accountId,
86367
87042
  logger,
86368
- assetRedirect
87043
+ assetRedirect,
87044
+ cfnClient: args.cfnClient
86369
87045
  });
86370
87046
  } finally {
86371
87047
  await lockManager.releaseLock(childStackName, childRegion).catch((err) => {
@@ -96038,4 +96714,4 @@ function buildProgram() {
96038
96714
 
96039
96715
  //#endregion
96040
96716
  export { buildProgram };
96041
- //# sourceMappingURL=program-Dh1J573K.js.map
96717
+ //# sourceMappingURL=program-COiS7GZJ.js.map