@go-to-k/cdkd 0.290.3 → 0.290.5

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 secretSafeKeyDisplay, $n as parseBootstrapMarker, $t as DiffCalculator, A as planOrphanAdoption, An as formatLockExpiry, Ar as resolveUseCdkBootstrapAssets, At as WAFv2WebACLProvider, B as formatResourceLine, Bn as AssetPublisher, Br as PARTITION_TABLE, Bt as replayWarn, C as createPreDeleteFinalSnapshot, Ci as isThrottlingError, Cn as scrubResourceRecord, Cr as getLegacyStateBucketName, Ct as cfnRefValueFromPhysicalId, D as makeCanonicalizePropertiesFn, Di as retryClassificationText, Dn as buildForceUnlockCommand, Dr as resolveSkipPrefix, Dt as parameterTypeMayLoseSecretIdentity, E as unsupportedFinalSnapshotError, Ei as markRedactedCause, En as rebuildClientForBucketRegion, Er as resolveCaptureObservedState, Et as isUnboundTemplateParameter, F as WARM_THROUGHPUT_MEMBERS, Fn as importableOutputKeys, Fr as MIGRATE_TMP_PREFIX, Ft as coerceCfnInteger, G as red, Gn as loadPublishableAssetManifest, Gt as producerRegionsFromState, H as cyan, Hn as WorkGraph, Hr as derivePartitionAndUrlSuffix, Ht as requireConfigObject, I as coerceWarmThroughput$1, In as importableOutputs, Ir as findLargeInlineResources, It as configBooleanRefusal, J as collectPublishedOutputNames, Jn as BOOTSTRAP_MARKER_PREFIX, Jr as getAwsClients, Jt as s3BucketDomainName, K as yellow, Kn as rewriteTemplateAssetReferences, Kr as resolveBucketRegion, Kt as withSharedDrainBudget, L as isWarmThroughputDecrease, Ln as orphansAfterRollback, Lr as uploadCfnTemplate, Lt as configIntegerRefusal, M as isStatefulRecreateTargetForReplace, Mn as CUSTOM_RESOURCE_RESPONSE_PREFIX, Mr as warnDeprecatedNoPrefixCliFlag, Mt as resolveExplicitPhysicalId, N as isStatefulRecreateTargetSync, Nn as DEFAULT_STATE_PREFIX, Nr as CFN_TEMPLATE_BODY_LIMIT, Nt as assertRegionMatch, O as extractDeploymentEventError, On as buildLockContentionMessage, Or as resolveStateBucketWithDefault, Ot as refStateLookupFromResource, P as renderStatefulReason, Pn as exportNamesCarriedFrom, Pr as CFN_TEMPLATE_URL_LIMIT, Pt as coerceCfnBoolean, Q as secretBearingStateKeyWarning, Qn as isCrossRegionRedirect, Qr as CdkdError, Qt as applyRoleArnIfSet, R as toCfnInteger, Rn as orphansCarriedFrom, Rr as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, Rt as configStringRefusal, S as ccRoutedFinalSnapshotError, Si as isRetryableTransientError, Sn as redactSecretsForState, Sr as getDefaultStateBucketName, St as carriesDynamicReference, T as refusesFinalSnapshot, Ti as markNonRetryable, Tn as S3StateBackend, Tr as resolveAutoAssetStorage, Tt as getAccountInfo, U as gray, Un as buildAssetRedirectMap, Ur as AssemblyReader, Ut as requireConfigString, V as bold, Vn as stringifyValue, Vr as canonicalizeRegion, Vt as requireConfigArray, W as green, Wn as createAssetRedirectResolver, Wr as processStackMessages, Wt as classifyReplaySecretRegion, X as isExportAliasCollision, Xn as ensureAssetStorage, Xr as setAwsClients, Xt as s3BucketRegionalDomainName, Y as exportAliasCollisionScrubWarning, Yn as assertAssetBucketRegion, Yr as resetAwsClients, Yt as s3BucketDualStackDomainName, Z as secretBearing, Zn as getBootstrapMarkerKey, Zt as s3BucketWebsiteUrl, _ as isSecretBearingReferenceString, _n as markSameGenerationBag, _r as stripControlChars, _t as deleteIndeterminateGuards, a as DeploymentEventsStore, ai as LocalInvokeBuildError$1, an as DagBuilder, ar as buildDockerImage, at as wouldReturnToSdkProvider, b as PRE_DELETE_SNAPSHOT_TYPES, bi as withErrorHandling, bn as recordMaskOnlyValue, br as Synthesizer, bt as isTerminationProtectionPropagationError, c as replayFailedOperations, ci as NestedStackChildDirectDestroyError, cn as STATE_SOURCED_CROSS_GENERATION_RULES, cr as describeDockerFailure, ct as maskerOrIdentity, d as updatePartialReason, di as ResourceTimeoutError, dn as carriesSecretMask, dr as getDockerCmd, dt as interruptWatchListenerCount, ei as CrossAccountSecretRefusalError, en as INTRINSIC_KEYS, er as readBootstrapMarkerBody, et as getCurrentResourceSecrets, f as withResourceDeadline, fi as ResourceUpdateNotSupportedError, fn as createSecretMasker, fr as partitionSensitiveEnv, ft as isInterruptedWaitError, g as bagHoldsSecretExpression, gn as isSingleDynamicReferenceToken, gr as escapeRegExp, gt as UNSPECIFIED_SKIP_REASON, h as computeImplicitDeleteEdges, hn as identityKeyFor, hr as runDockerStreaming, ht as slowCcOperationTimeoutMs, i as DeploymentEventsReader, ii as IntrinsicResolutionRefusalError, in as withRetry, ir as describeAwsFailure, it as ProviderRegistry, j as MULTI_REGION_RECREATE_BLOCKED_TYPES, jn as shellQuote, jr as stateBucketExistenceConfirmed, jt as normalizeAwsTagsToCfn, k as makeSiblingClaimReader, kn as forceQuitRecoveryClause, kr as resolveStateBucketWithDefaultAndSource, kt as isSettledInstanceState, l as replayRollback, li as PartialFailureError, ln as STATE_SOURCED_READBACK_RULES, lr as dockerSpawnEnvWithSensitive, lt as beginCommandInterruptScope, m as IMPLICIT_DELETE_DEPENDENCIES, mi as StackTerminationProtectionError, mn as errorCauseChain, mr as runDockerForeground, mt as CloudControlProvider, n as DEFAULT_RESOURCE_WARN_AFTER_MS, ni as DeployCancelledError, nn as findSilentDropProperties, nr as validateContainerRepoName, nt as collectInlinePolicyNamesManagedBySiblings, o as planFailedOps, oi as LocalStartServiceError, on as TemplateParser, or as describeDockerCapturedOutput, ot as createMaskedRetryLogger, p as maskingRetryLogger, pi as StackHasActiveImportsError, pn as dynamicReferenceTokens, pr as redactDockerArgvValues, pt as startInterruptWatch, q as collectDeclaredOutputNames, qn as AssetModeResolver, qr as AwsClients, qt as s3BucketArn, r as DeployEngine, ri as DynamicReferenceRegionAmbiguousError, rn as describeTypeWithThrottleRetry, rr as buildDenyExternalAccessPolicy, rt as clearOnUpdateRemoval, s as planRollback, sn as STATE_SOURCED_BASELINE_RULES, sr as describeDockerExecFailure, st as maskDeep, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as findActionableSilentDrops, tr as validateAssetBucketName, tt as IAMRoleProvider, u as updatePartialMessage, ui as ProvisioningError, un as TEMPLATE_SOURCED_RULES, ur as formatDockerLoginError, ut as endCommandInterruptScope, v as bindingSkippedOutputs, vn as maskSecretsInError, vr as AssetManifestLoader, vt as deleteSkipReason, w as isFinalSnapshotError, wi as isTransientServerError, wn as LockManager, wr as resolveApp, wt as coerceParameterTypedValue, x as buildFinalSnapshotIdentifier, xi as isMarkedNonRetryable, xn as recoverMaskedOutput, xr as synthesisStatusMessage, xt as IntrinsicFunctionResolver, y as ATOMIC_FINAL_SNAPSHOT_TYPES, yi as normalizeAwsError, yn as maskSecretsInText, yr as getDockerImageBySourceHash, yt as disableInstanceApiTermination, z as toFiniteNumber, zn as shouldRetainResource, zr as expectedOwnerParam, zt as readConfigString } from "./deploy-engine-0Wg4MwP2.js";
1
+ import { $ as secretSafeKeyDisplay, $n as isCrossRegionRedirect, $r as CdkdError, $t as applyRoleArnIfSet, A as planOrphanAdoption, An as forceQuitRecoveryClause, Ar as resolveStateBucketWithDefaultAndSource, At as isSettledInstanceState, B as formatResourceLine, Bn as shouldRetainResource, Br as expectedOwnerParam, Bt as readConfigString, C as createPreDeleteFinalSnapshot, Ci as isRetryableTransientError, Cn as redactSecretsForState, Cr as getDefaultStateBucketName, Ct as cfnRefValueFromPhysicalId, D as makeCanonicalizePropertiesFn, Di as markRedactedCause, Dn as rebuildClientForBucketRegion, Dr as resolveCaptureObservedState, Dt as isUnboundTemplateParameter, E as unsupportedFinalSnapshotError, Ei as markNonRetryable, En as S3StateBackend, Er as resolveAutoAssetStorage, Et as isImpossibleEmptyStoredAttribute, F as WARM_THROUGHPUT_MEMBERS, Fn as exportNamesCarriedFrom, Fr as CFN_TEMPLATE_URL_LIMIT, Ft as coerceCfnBoolean, G as red, Gn as createAssetRedirectResolver, Gr as processStackMessages, Gt as classifyReplaySecretRegion, H as cyan, Hn as stringifyValue, Hr as canonicalizeRegion, Ht as requireConfigArray, I as coerceWarmThroughput$1, In as importableOutputKeys, Ir as MIGRATE_TMP_PREFIX, It as coerceCfnInteger, J as collectPublishedOutputNames, Jn as AssetModeResolver, Jr as AwsClients, Jt as s3BucketArn, K as yellow, Kn as loadPublishableAssetManifest, Kt as producerRegionsFromState, L as isWarmThroughputDecrease, Ln as importableOutputs, Lr as findLargeInlineResources, Lt as configBooleanRefusal, M as isStatefulRecreateTargetForReplace, Mn as shellQuote, Mr as stateBucketExistenceConfirmed, Mt as normalizeAwsTagsToCfn, N as isStatefulRecreateTargetSync, Nn as CUSTOM_RESOURCE_RESPONSE_PREFIX, Nr as warnDeprecatedNoPrefixCliFlag, Nt as resolveExplicitPhysicalId, O as extractDeploymentEventError, Oi as retryClassificationText, On as buildForceUnlockCommand, Or as resolveSkipPrefix, Ot as parameterTypeMayLoseSecretIdentity, P as renderStatefulReason, Pn as DEFAULT_STATE_PREFIX, Pr as CFN_TEMPLATE_BODY_LIMIT, Pt as assertRegionMatch, Q as secretBearingStateKeyWarning, Qn as getBootstrapMarkerKey, Qt as s3BucketWebsiteUrl, R as toCfnInteger, Rn as orphansAfterRollback, Rr as uploadCfnTemplate, Rt as configIntegerRefusal, S as ccRoutedFinalSnapshotError, Si as isMarkedNonRetryable, Sn as recoverMaskedOutput, Sr as synthesisStatusMessage, St as carriesDynamicReference, T as refusesFinalSnapshot, Ti as isTransientServerError, Tn as LockManager, Tr as resolveApp, Tt as getAccountInfo, U as gray, Un as WorkGraph, Ur as derivePartitionAndUrlSuffix, Ut as requireConfigObject, V as bold, Vn as AssetPublisher, Vr as PARTITION_TABLE, Vt as replayWarn, W as green, Wn as buildAssetRedirectMap, Wr as AssemblyReader, Wt as requireConfigString, X as isExportAliasCollision, Xn as assertAssetBucketRegion, Xr as resetAwsClients, Xt as s3BucketDualStackDomainName, Y as exportAliasCollisionScrubWarning, Yn as BOOTSTRAP_MARKER_PREFIX, Yr as getAwsClients, Yt as s3BucketDomainName, Z as secretBearing, Zn as ensureAssetStorage, Zr as setAwsClients, Zt as s3BucketRegionalDomainName, _ as isSecretBearingReferenceString, _n as isSingleDynamicReferenceToken, _r as escapeRegExp, _t as deleteIndeterminateGuards, a as DeploymentEventsStore, ai as IntrinsicResolutionRefusalError, an as withRetry, ar as describeAwsFailure, at as wouldReturnToSdkProvider, b as PRE_DELETE_SNAPSHOT_TYPES, bi as normalizeAwsError, bn as maskSecretsInText, br as getDockerImageBySourceHash, bt as isTerminationProtectionPropagationError, c as replayFailedOperations, cn as STATE_SOURCED_BASELINE_RULES, cr as describeDockerExecFailure, ct as maskerOrIdentity, d as updatePartialReason, di as ProvisioningError, dn as TEMPLATE_SOURCED_RULES, dr as formatDockerLoginError, dt as interruptWatchListenerCount, en as DiffCalculator, er as parseBootstrapMarker, et as getCurrentResourceSecrets, f as withResourceDeadline, fi as ResourceTimeoutError, fn as carriesSecretMask, fr as getDockerCmd, ft as isInterruptedWaitError, g as bagHoldsSecretExpression, gn as identityKeyFor, gr as runDockerStreaming, gt as UNSPECIFIED_SKIP_REASON, h as computeImplicitDeleteEdges, hi as StackTerminationProtectionError, hn as errorCauseChain, hr as runDockerForeground, ht as slowCcOperationTimeoutMs, i as DeploymentEventsReader, ii as DynamicReferenceRegionAmbiguousError, in as describeTypeWithThrottleRetry, ir as buildDenyExternalAccessPolicy, it as ProviderRegistry, j as MULTI_REGION_RECREATE_BLOCKED_TYPES, jn as formatLockExpiry, jr as resolveUseCdkBootstrapAssets, jt as WAFv2WebACLProvider, k as makeSiblingClaimReader, kn as buildLockContentionMessage, kr as resolveStateBucketWithDefault, kt as refStateLookupFromResource, l as replayRollback, li as NestedStackChildDirectDestroyError, ln as STATE_SOURCED_CROSS_GENERATION_RULES, lr as describeDockerFailure, lt as beginCommandInterruptScope, m as IMPLICIT_DELETE_DEPENDENCIES, mi as StackHasActiveImportsError, mn as dynamicReferenceTokens, mr as redactDockerArgvValues, mt as CloudControlProvider, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as findActionableSilentDrops, nr as validateAssetBucketName, nt as collectInlinePolicyNamesManagedBySiblings, o as planFailedOps, oi as LocalInvokeBuildError$1, on as DagBuilder, or as buildDockerImage, ot as createMaskedRetryLogger, p as maskingRetryLogger, pi as ResourceUpdateNotSupportedError, pn as createSecretMasker, pr as partitionSensitiveEnv, pt as startInterruptWatch, q as collectDeclaredOutputNames, qn as rewriteTemplateAssetReferences, qr as resolveBucketRegion, qt as withSharedDrainBudget, r as DeployEngine, ri as DeployCancelledError, rn as findSilentDropProperties, rr as validateContainerRepoName, rt as clearOnUpdateRemoval, s as planRollback, si as LocalStartServiceError, sn as TemplateParser, sr as describeDockerCapturedOutput, st as maskDeep, t as DEFAULT_RESOURCE_TIMEOUT_MS, ti as CrossAccountSecretRefusalError, tn as INTRINSIC_KEYS, tr as readBootstrapMarkerBody, tt as IAMRoleProvider, u as updatePartialMessage, ui as PartialFailureError, un as STATE_SOURCED_READBACK_RULES, ur as dockerSpawnEnvWithSensitive, ut as endCommandInterruptScope, v as bindingSkippedOutputs, vn as markSameGenerationBag, vr as stripControlChars, vt as deleteSkipReason, w as isFinalSnapshotError, wi as isThrottlingError, wn as scrubResourceRecord, wr as getLegacyStateBucketName, wt as coerceParameterTypedValue, x as buildFinalSnapshotIdentifier, xi as withErrorHandling, xn as recordMaskOnlyValue, xr as Synthesizer, xt as IntrinsicFunctionResolver, y as ATOMIC_FINAL_SNAPSHOT_TYPES, yn as maskSecretsInError, yr as AssetManifestLoader, yt as disableInstanceApiTermination, z as toFiniteNumber, zn as orphansCarriedFrom, zr as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, zt as configStringRefusal } from "./deploy-engine-WFO9o9M0.js";
2
2
  import { S as truncateCodePoints, _ as withSkipPrefix, b as displayIdent, 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, x as displaySafe, y as UNRENDERABLE } from "./logger-th1myfgh.js";
3
3
  import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
4
- import { t as getCdkdVersion } from "./version-BTao4oxo.js";
5
- import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-RLnpBIKj.js";
4
+ import { t as getCdkdVersion } from "./version-BBIkp8ja.js";
5
+ import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-DU7mN-cS.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";
@@ -18953,7 +18953,7 @@ var DynamoDBTableProvider = class {
18953
18953
  const warn = (message) => this.logger.warn(maskSecrets(message));
18954
18954
  const coerced = coerceWarmThroughput(value);
18955
18955
  if (coerced === void 0) return void 0;
18956
- if (coerced.dropped.length > 0) warn(`${scope}: WarmThroughput member(s) ${coerced.dropped.join(", ")} in ${JSON.stringify(maskLeafValue$1(value, maskSecrets))} are not a number DynamoDB accepts, so they were dropped from the request, which leaves ${JSON.stringify(maskLeafValue$1(coerced.spec, maskSecrets))}. Check for an unresolved intrinsic or a non-numeric value.`);
18956
+ if (coerced.dropped.length > 0) warn(`${scope}: WarmThroughput member(s) ${coerced.dropped.join(", ")} in ${JSON.stringify(maskLeafValue$1(value, maskSecrets))} are not a number DynamoDB accepts, so they were dropped from the request, which leaves ${JSON.stringify(maskLeafValue$1(coerced.spec, maskSecrets))}. Check for an unresolved intrinsic or a spelling CloudFormation would reject: an Integer here is an optional sign and decimal digits only (no surrounding whitespace, hex, exponent or decimal point).`);
18957
18957
  return coerced.spec;
18958
18958
  }
18959
18959
  /**
@@ -20726,10 +20726,10 @@ var DynamoDBGlobalTableProvider = class {
20726
20726
  const watch = startInterruptWatch(`DynamoDB auto-scaling for ${tableName}`);
20727
20727
  try {
20728
20728
  if (newEnabled) {
20729
- const minCapacity = Number(newSettings["MinCapacity"] ?? 0);
20730
- const maxCapacity = Number(newSettings["MaxCapacity"] ?? 0);
20731
- if (!Number.isFinite(minCapacity) || !Number.isFinite(maxCapacity)) {
20732
- warn(`Cannot apply auto-scaling diff on ${tableName} (${dimension}): MinCapacity / MaxCapacity must be numbers, got ${maskSecrets(String(newSettings["MinCapacity"]))} / ${maskSecrets(String(newSettings["MaxCapacity"]))}`);
20729
+ const minCapacity = coerceCfnInteger(newSettings["MinCapacity"] ?? 0);
20730
+ const maxCapacity = coerceCfnInteger(newSettings["MaxCapacity"] ?? 0);
20731
+ if (minCapacity === void 0 || maxCapacity === void 0) {
20732
+ warn(`Cannot apply auto-scaling diff on ${tableName} (${dimension}): MinCapacity / MaxCapacity must be integers CloudFormation accepts (an optional sign and decimal digits; no surrounding whitespace, hex, exponent or decimal point), got ${maskSecrets(String(newSettings["MinCapacity"]))} / ${maskSecrets(String(newSettings["MaxCapacity"]))}`);
20733
20733
  return;
20734
20734
  }
20735
20735
  try {
@@ -20761,8 +20761,16 @@ var DynamoDBGlobalTableProvider = class {
20761
20761
  PredefinedMetricSpecification: { PredefinedMetricType: metricType },
20762
20762
  TargetValue: targetValue
20763
20763
  };
20764
- if (tttCfg["ScaleInCooldown"] !== void 0) targetTrackingConfig["ScaleInCooldown"] = Number(tttCfg["ScaleInCooldown"]);
20765
- if (tttCfg["ScaleOutCooldown"] !== void 0) targetTrackingConfig["ScaleOutCooldown"] = Number(tttCfg["ScaleOutCooldown"]);
20764
+ for (const cooldown of ["ScaleInCooldown", "ScaleOutCooldown"]) {
20765
+ const raw = tttCfg[cooldown];
20766
+ if (raw === void 0) continue;
20767
+ const seconds = coerceCfnInteger(raw);
20768
+ if (seconds === void 0) {
20769
+ warn(`Auto-scaling policy on ${tableName} (${dimension}): TargetTrackingScalingPolicyConfiguration.${cooldown} is not an integer CloudFormation accepts (got ${JSON.stringify(maskDeep(raw, maskSecrets))?.slice(0, 80)}), so it is omitted and the service default applies.`);
20770
+ continue;
20771
+ }
20772
+ targetTrackingConfig[cooldown] = seconds;
20773
+ }
20766
20774
  if (tttCfg["DisableScaleIn"] !== void 0) targetTrackingConfig["DisableScaleIn"] = Boolean(tttCfg["DisableScaleIn"]);
20767
20775
  try {
20768
20776
  await withRetry(() => asClient.send(new PutScalingPolicyCommand({
@@ -22145,13 +22153,13 @@ function warmThroughputDiagnostic(raw, coercion, indexName) {
22145
22153
  return {
22146
22154
  ...scope,
22147
22155
  ...indexName !== void 0 && { indexName },
22148
- message: `WarmThroughput.${dropped} did not resolve to a number and was DROPPED; ${kept} is still sent. AWS types these as numbers, so a quoted "12000" is fine but an unresolved intrinsic or an object is not. The dropped member keeps its current AWS value until the template supplies a usable one.`
22156
+ message: `WarmThroughput.${dropped} did not resolve to a number and was DROPPED; ${kept} is still sent. AWS types these as numbers, so a quoted "12000" is fine but an unresolved intrinsic or an object is not. The dropped member keeps its current AWS value until the template supplies a usable one. CloudFormation types this member as an Integer and accepts only an optional sign and decimal digits (no surrounding whitespace, hex, exponent or decimal point), so a spelling it would reject is treated the same way.`
22149
22157
  };
22150
22158
  }
22151
22159
  return {
22152
22160
  ...scope,
22153
22161
  ...indexName !== void 0 && { indexName },
22154
- message: `WarmThroughput is declared but no member resolved to a number${dropped ? ` (${dropped})` : ""}, so the whole block is REFUSED and not sent. Forwarding it would fail as an opaque AWS validation error naming neither cdkd nor the property. Set WarmThroughput.ReadUnitsPerSecond / WriteUnitsPerSecond to numbers, or remove the block. Nothing else about the index is affected.`
22162
+ message: `WarmThroughput is declared but no member resolved to a number${dropped ? ` (${dropped})` : ""}, so the whole block is REFUSED and not sent. Forwarding it would fail as an opaque AWS validation error naming neither cdkd nor the property. Set WarmThroughput.ReadUnitsPerSecond / WriteUnitsPerSecond to numbers, or remove the block. Nothing else about the index is affected. CloudFormation types this member as an Integer and accepts only an optional sign and decimal digits (no surrounding whitespace, hex, exponent or decimal point), so a spelling it would reject is treated the same way.`
22155
22163
  };
22156
22164
  }
22157
22165
  /**
@@ -22165,9 +22173,10 @@ function warmThroughputDiagnostic(raw, coercion, indexName) {
22165
22173
  * the pre-fix code forwarded such a block VERBATIM. Three defects followed, and fixing any subset leaves a
22166
22174
  * live one, which is why this helper does all three at once:
22167
22175
  *
22168
- * 1. **Coercion.** The verbatim forward was the ONE path that skipped
22169
- * {@link toFiniteNumber}, so a stringly-typed CFn `"5"` reached the SDK
22170
- * unnormalized while every derived value was a number.
22176
+ * 1. **Coercion.** The verbatim forward was the ONE path that skipped the
22177
+ * numeric reader (`coerceCfnInteger` since issue #3135), so a
22178
+ * stringly-typed CFn `"5"` reached the SDK unnormalized while every
22179
+ * derived value was a number.
22171
22180
  * 2. **Per-member merge.** Whole-block replacement meant a PARTIAL explicit
22172
22181
  * block SUPPRESSED valid derived siblings — an explicit
22173
22182
  * `{MaxReadRequestUnits: 41}` discarded a perfectly good
@@ -22194,16 +22203,17 @@ function mergeExplicitThroughputBlock(explicit, derived, members, diagnostics, c
22194
22203
  const out = {};
22195
22204
  for (const member of members) {
22196
22205
  if (explicit && explicit[member] !== void 0) {
22197
- const coerced = toFiniteNumber(explicit[member]);
22206
+ const coerced = coerceCfnInteger(explicit[member]);
22198
22207
  if (coerced !== void 0) {
22199
22208
  out[member] = coerced;
22200
22209
  continue;
22201
22210
  }
22211
+ const standIn = derived?.[member];
22202
22212
  diagnostics?.push({
22203
22213
  kind: "unresolved-member",
22204
22214
  ...context.indexName !== void 0 && { indexName: context.indexName },
22205
22215
  member,
22206
- message: `${context.blockName}.${member} is present but did not resolve to a number (${JSON.stringify(maskDeep(explicit[member], maskSecrets))?.slice(0, 80)}), so it was ignored. This is usually an unresolved intrinsic; resolve it to a literal.`
22216
+ message: `${context.blockName}.${member} is present but did not resolve to a number (${JSON.stringify(maskDeep(explicit[member], maskSecrets))?.slice(0, 80)}), so ` + (typeof standIn === "number" ? `it was ignored and ${standIn} was sent instead. ` : `it was ignored. `) + "This is usually an unresolved intrinsic; resolve it to a literal. CloudFormation types this member as an Integer and accepts only an optional sign and decimal digits (no surrounding whitespace, hex, exponent or decimal point), so a spelling it would reject is treated the same way."
22207
22217
  });
22208
22218
  }
22209
22219
  const derivedValue = derived?.[member];
@@ -22227,12 +22237,12 @@ function mergeExplicitThroughputBlock(explicit, derived, members, diagnostics, c
22227
22237
  function reportUnresolvedRawMember(rawValue, diagnostics, context, maskSecrets = (text) => text) {
22228
22238
  if (!diagnostics) return;
22229
22239
  if (rawValue === void 0) return;
22230
- if (toFiniteNumber(rawValue) !== void 0) return;
22240
+ if (coerceCfnInteger(rawValue) !== void 0) return;
22231
22241
  diagnostics.push({
22232
22242
  kind: "unresolved-member",
22233
22243
  ...context.indexName !== void 0 && { indexName: context.indexName },
22234
22244
  member: context.member,
22235
- message: `${context.blockName}.${context.member} is declared but did not resolve to a number (${JSON.stringify(maskDeep(rawValue, maskSecrets))?.slice(0, 80)}), so no throughput value was sent for it and the live AWS setting was left unchanged. This is usually an unresolved intrinsic; resolve it to a literal to apply the limit, or remove the property entirely to clear it.`
22245
+ message: `${context.blockName}.${context.member} is declared but did not resolve to a number (${JSON.stringify(maskDeep(rawValue, maskSecrets))?.slice(0, 80)}), so no throughput value was sent for it and the live AWS setting was left unchanged. This is usually an unresolved intrinsic; resolve it to a literal to apply the limit, or remove the property entirely to clear it. CloudFormation types this member as an Integer and accepts only an optional sign and decimal digits (no surrounding whitespace, hex, exponent or decimal point), so a spelling it would reject is treated the same way.`
22236
22246
  });
22237
22247
  }
22238
22248
  /**
@@ -22320,7 +22330,7 @@ function reportUnresolvedProvisionedCapacity(rawSettings, side, source, outcome,
22320
22330
  kind: "unresolved-member",
22321
22331
  ...context.indexName !== void 0 && { indexName: context.indexName },
22322
22332
  member: blame.relPath ? blame.relPath.slice(blame.relPath.lastIndexOf(".") + 1) : context.blockName.slice(context.blockName.lastIndexOf(".") + 1),
22323
- message: `${path} is declared but did not resolve to a number (${JSON.stringify(maskDeep(blame.value, maskSecrets))?.slice(0, 80)}), ${consequence}. This is usually an unresolved intrinsic; resolve it to a literal to apply the capacity you declared.`
22333
+ message: `${path} is declared but did not resolve to a number (${JSON.stringify(maskDeep(blame.value, maskSecrets))?.slice(0, 80)}), ${consequence}. This is usually an unresolved intrinsic; resolve it to a literal to apply the capacity you declared. CloudFormation types this member as an Integer and accepts only an optional sign and decimal digits (no surrounding whitespace, hex, exponent or decimal point), so a spelling it would reject is treated the same way.`
22324
22334
  });
22325
22335
  }
22326
22336
  /**
@@ -22347,7 +22357,7 @@ function reportUnresolvedProvisionedCapacity(rawSettings, side, source, outcome,
22347
22357
  */
22348
22358
  function deriveWriteCapacityUnits(settings, source = "min") {
22349
22359
  if (!settings) return void 0;
22350
- const literal = toFiniteNumber(settings["WriteCapacityUnits"]);
22360
+ const literal = coerceCfnInteger(settings["WriteCapacityUnits"]);
22351
22361
  if (literal !== void 0) return literal;
22352
22362
  const autoScaling = asRecord$1(settings["WriteCapacityAutoScalingSettings"]);
22353
22363
  if (!autoScaling) return void 0;
@@ -22363,7 +22373,7 @@ function deriveWriteCapacityUnits(settings, source = "min") {
22363
22373
  */
22364
22374
  function deriveReadCapacityUnits(settings, source = "min") {
22365
22375
  if (!settings) return void 0;
22366
- const literal = toFiniteNumber(settings["ReadCapacityUnits"]);
22376
+ const literal = coerceCfnInteger(settings["ReadCapacityUnits"]);
22367
22377
  if (literal !== void 0) return literal;
22368
22378
  const autoScaling = asRecord$1(settings["ReadCapacityAutoScalingSettings"]);
22369
22379
  if (!autoScaling) return void 0;
@@ -22371,9 +22381,9 @@ function deriveReadCapacityUnits(settings, source = "min") {
22371
22381
  }
22372
22382
  /** Resolve a `*CapacityAutoScalingSettings` block to one number. */
22373
22383
  function pickAutoScalingCapacity(autoScaling, source) {
22374
- const min = toFiniteNumber(autoScaling["MinCapacity"]);
22375
- if (source === "min") return min ?? toFiniteNumber(autoScaling["SeedCapacity"]);
22376
- return toFiniteNumber(autoScaling["SeedCapacity"]) ?? min;
22384
+ const min = coerceCfnInteger(autoScaling["MinCapacity"]);
22385
+ if (source === "min") return min ?? coerceCfnInteger(autoScaling["SeedCapacity"]);
22386
+ return coerceCfnInteger(autoScaling["SeedCapacity"]) ?? min;
22377
22387
  }
22378
22388
  /**
22379
22389
  * Translate the CFn `AWS::DynamoDB::GlobalTable` `GlobalSecondaryIndexes[]`
@@ -22470,7 +22480,7 @@ function toSdkGlobalSecondaryIndexes(properties, region, billingMode, source = "
22470
22480
  const rawWrite = gsi["WriteProvisionedThroughputSettings"];
22471
22481
  const derivedRead = deriveReadCapacityUnits(asRecord$1(rawLocalRead), source) ?? deriveReadCapacityUnits(asRecord$1(rawGsiRead), source);
22472
22482
  const derivedWrite = deriveWriteCapacityUnits(asRecord$1(rawWrite), source);
22473
- const explicitCovers = (member) => explicitProvisioned !== void 0 && toFiniteNumber(explicitProvisioned[member]) !== void 0;
22483
+ const explicitCovers = (member) => explicitProvisioned !== void 0 && coerceCfnInteger(explicitProvisioned[member]) !== void 0;
22474
22484
  if (derivedRead === void 0 && !explicitCovers("ReadCapacityUnits")) {
22475
22485
  const fromReplica = rawLocalRead !== void 0;
22476
22486
  reportUnresolvedProvisionedCapacity(fromReplica ? rawLocalRead : rawGsiRead, "Read", source, { appliedUnits: DEFAULT_CAPACITY_UNITS }, diagnostics, {
@@ -22503,8 +22513,8 @@ function toSdkGlobalSecondaryIndexes(properties, region, billingMode, source = "
22503
22513
  blockName: rawReadLocal !== void 0 ? "Replicas[local].GlobalSecondaryIndexes[].ReadOnDemandThroughputSettings" : "ReadOnDemandThroughputSettings",
22504
22514
  member: "MaxReadRequestUnits"
22505
22515
  }, maskSecrets);
22506
- const maxWrite = toFiniteNumber(rawWrite);
22507
- const maxRead = toFiniteNumber(rawReadLocal) ?? toFiniteNumber(rawReadGsi);
22516
+ const maxWrite = coerceCfnInteger(rawWrite);
22517
+ const maxRead = coerceCfnInteger(rawReadLocal) ?? coerceCfnInteger(rawReadGsi);
22508
22518
  const merged = mergeExplicitThroughputBlock(explicitOnDemand, {
22509
22519
  ...maxRead !== void 0 && { MaxReadRequestUnits: maxRead },
22510
22520
  ...maxWrite !== void 0 && { MaxWriteRequestUnits: maxWrite }
@@ -22797,7 +22807,7 @@ function isUnresolvedIntrinsicBlock(value) {
22797
22807
  * The per-GSI reset needs to know "did the template DROP this member", and the
22798
22808
  * only faithful answer is whether the CFn key is THERE — not whether its value
22799
22809
  * coerced. `toSdkGlobalSecondaryIndexes` runs every value through
22800
- * {@link toFiniteNumber}, which returns `undefined` for a present-but-
22810
+ * `coerceCfnInteger`, which returns `undefined` for a present-but-
22801
22811
  * unparseable value (an unresolved `{Ref: …}`, `''`, an object). Deciding
22802
22812
  * "dropped" from that collapsed view sent the `-1` reset for a member the
22803
22813
  * template was actively trying to SET, silently clearing the ceiling — the same
@@ -22914,8 +22924,8 @@ function collectUncomparableCapacityGsiNames(properties, region) {
22914
22924
  continue;
22915
22925
  }
22916
22926
  const explicitProvisioned = asRecord$1(gsi["ProvisionedThroughput"]);
22917
- const derivedRead = toFiniteNumber(explicitProvisioned?.["ReadCapacityUnits"]) ?? deriveReadCapacityUnits(asRecord$1(readBlocks[0])) ?? deriveReadCapacityUnits(asRecord$1(readBlocks[1]));
22918
- const derivedWrite = toFiniteNumber(explicitProvisioned?.["WriteCapacityUnits"]) ?? deriveWriteCapacityUnits(asRecord$1(writeBlock));
22927
+ const derivedRead = coerceCfnInteger(explicitProvisioned?.["ReadCapacityUnits"]) ?? deriveReadCapacityUnits(asRecord$1(readBlocks[0])) ?? deriveReadCapacityUnits(asRecord$1(readBlocks[1]));
22928
+ const derivedWrite = coerceCfnInteger(explicitProvisioned?.["WriteCapacityUnits"]) ?? deriveWriteCapacityUnits(asRecord$1(writeBlock));
22919
22929
  if (derivedRead === void 0 || derivedWrite === void 0) out.add(name);
22920
22930
  }
22921
22931
  return out;
@@ -23068,7 +23078,7 @@ function toSdkReplicaThroughputOverrides(cfn, billingMode, diagnostics, context,
23068
23078
  if (billingMode === "PROVISIONED") {
23069
23079
  const rawRead = cfn["ReadProvisionedThroughputSettings"];
23070
23080
  const readCapacity = deriveReadCapacityUnits(asRecord$1(rawRead));
23071
- if (readCapacity === void 0 && toFiniteNumber(explicitProvisioned?.["ReadCapacityUnits"]) === void 0) reportUnresolvedProvisionedCapacity(rawRead, "Read", "min", { suppressed: "so no ProvisionedThroughputOverride was sent and the replica keeps the source table's read capacity" }, diagnostics, {
23081
+ if (readCapacity === void 0 && coerceCfnInteger(explicitProvisioned?.["ReadCapacityUnits"]) === void 0) reportUnresolvedProvisionedCapacity(rawRead, "Read", "min", { suppressed: "so no ProvisionedThroughputOverride was sent and the replica keeps the source table's read capacity" }, diagnostics, {
23072
23082
  ...context,
23073
23083
  blockName: "ReadProvisionedThroughputSettings"
23074
23084
  }, maskSecrets);
@@ -23084,7 +23094,7 @@ function toSdkReplicaThroughputOverrides(cfn, billingMode, diagnostics, context,
23084
23094
  blockName: "ReadOnDemandThroughputSettings",
23085
23095
  member: "MaxReadRequestUnits"
23086
23096
  }, maskSecrets);
23087
- const maxReadRequestUnits = toFiniteNumber(rawMaxRead);
23097
+ const maxReadRequestUnits = coerceCfnInteger(rawMaxRead);
23088
23098
  const merged = mergeExplicitThroughputBlock(explicitOnDemand, maxReadRequestUnits !== void 0 ? { MaxReadRequestUnits: maxReadRequestUnits } : void 0, ["MaxReadRequestUnits"], diagnostics, {
23089
23099
  ...context,
23090
23100
  blockName: "OnDemandThroughputOverride"
@@ -23127,11 +23137,11 @@ function collectTableOnDemandCeilings(properties, region, diagnostics, maskSecre
23127
23137
  return {
23128
23138
  read: {
23129
23139
  declared: rawRead !== void 0 || isUnresolvedIntrinsicBlock(readBlock),
23130
- value: toFiniteNumber(rawRead)
23140
+ value: coerceCfnInteger(rawRead)
23131
23141
  },
23132
23142
  write: {
23133
23143
  declared: rawWrite !== void 0 || isUnresolvedIntrinsicBlock(writeBlock),
23134
- value: toFiniteNumber(rawWrite)
23144
+ value: coerceCfnInteger(rawWrite)
23135
23145
  }
23136
23146
  };
23137
23147
  }
@@ -26555,6 +26565,19 @@ const SG_INGRESS_IP_PROTOCOL_DEFAULT = "-1";
26555
26565
  /** The refusal path both the create guard and the canonicalizer report under. */
26556
26566
  const SG_INGRESS_IP_PROTOCOL_PATH = "AWS::EC2::SecurityGroupIngress IpProtocol";
26557
26567
  /**
26568
+ * Bounded eventual-consistency retry for the `DescribeSecurityGroups`
26569
+ * read-back (`readSecurityGroupVpcId`, issue #3097): an answer
26570
+ * `isNotFoundError` classifies as not-found is retried, everything else omits
26571
+ * the attribute at once. Five ATTEMPTS (four retries) a second apart cover the
26572
+ * propagation window EC2 documents for its read APIs;
26573
+ * `securityGroupReadbackDelays.sleep` is the unit-test seam (the pattern
26574
+ * `deleteTableRetryDelays` uses), and the fallback is the provider's own
26575
+ * `sleep`, so the class has one timer to fake.
26576
+ */
26577
+ const SG_READBACK_NOT_FOUND_ATTEMPTS = 5;
26578
+ const SG_READBACK_RETRY_DELAY_MS = 1e3;
26579
+ const securityGroupReadbackDelays = {};
26580
+ /**
26558
26581
  * Page ceiling for {@link EC2Provider.lookupIngressRuleId}'s
26559
26582
  * `DescribeSecurityGroupRules` walk (issue #1761).
26560
26583
  *
@@ -27808,10 +27831,10 @@ var EC2Provider = class {
27808
27831
  this.logger.debug(`Successfully created SecurityGroup ${logicalId}: ${groupId}`);
27809
27832
  return {
27810
27833
  physicalId: groupId,
27811
- attributes: {
27834
+ attributes: definedAttributes({
27812
27835
  GroupId: groupId,
27813
- VpcId: properties["VpcId"] ?? ""
27814
- }
27836
+ VpcId: await this.readSecurityGroupVpcId(groupId)
27837
+ })
27815
27838
  };
27816
27839
  } catch (error) {
27817
27840
  const cause = error instanceof Error ? error : void 0;
@@ -27901,10 +27924,10 @@ var EC2Provider = class {
27901
27924
  return {
27902
27925
  physicalId,
27903
27926
  wasReplaced: false,
27904
- attributes: {
27927
+ attributes: definedAttributes({
27905
27928
  GroupId: physicalId,
27906
- VpcId: properties["VpcId"] ?? ""
27907
- }
27929
+ VpcId: await this.readSecurityGroupVpcId(physicalId)
27930
+ })
27908
27931
  };
27909
27932
  } catch (error) {
27910
27933
  const cause = error instanceof Error ? error : void 0;
@@ -27968,17 +27991,72 @@ var EC2Provider = class {
27968
27991
  }
27969
27992
  }));
27970
27993
  }
27971
- async getSecurityGroupAttribute(physicalId, attributeName) {
27972
- if (attributeName === "GroupId") return physicalId;
27973
- try {
27974
- const sg = (await this.ec2Client.send(new DescribeSecurityGroupsCommand({ GroupIds: [physicalId] }))).SecurityGroups?.[0];
27975
- if (!sg) return void 0;
27976
- if (attributeName === "VpcId") return sg.VpcId;
27977
- return;
27978
- } catch {
27994
+ /**
27995
+ * The `VpcId` a security group's `attributes` map records, read back from
27996
+ * `DescribeSecurityGroups` on the group id (issue #3097).
27997
+ *
27998
+ * The two `AWS::EC2::SecurityGroup` result maps used to copy the TEMPLATE's
27999
+ * own `VpcId` property with a `?? ''` fallback — the last two sites issue
28000
+ * #3077 left on that shape. A `CfnSecurityGroup` declared without `VpcId`
28001
+ * lands in the account's default VPC, so the template has nothing to copy
28002
+ * and the record held `''` where CloudFormation answers the default VPC's
28003
+ * id. The read-back answers both shapes the same way: `CreateSecurityGroup`
28004
+ * returns only the group id, and the describe reports `VpcId` for an
28005
+ * explicit VPC and for the default one alike.
28006
+ *
28007
+ * Best-effort by design: the group is already created (or updated) and
28008
+ * wired when this runs, so a failed or empty read must not fail the
28009
+ * resource. It answers `undefined` then, which `definedAttributes` turns
28010
+ * into an ABSENT key — never `''` — and the resolver's
28011
+ * `AWS::EC2::SecurityGroup` `VpcId` arm re-reads it live on the next
28012
+ * `Fn::GetAtt`. The error CLASS (and HTTP status when the SDK attached one)
28013
+ * is what the debug line names at its head; AWS's own text follows it on the
28014
+ * same debug line, since a denied describe quotes the caller's account, role
28015
+ * and session (`.claude/rules/provider-resource-identity.md`).
28016
+ *
28017
+ * EC2's read API is eventually consistent: a `DescribeSecurityGroups` issued
28018
+ * right after `CreateSecurityGroup` can answer `InvalidGroup.NotFound` for a
28019
+ * group the write path already accepted tags and rules for (#3139 review).
28020
+ * An answer `isNotFoundError` classifies as not-found (a substring
28021
+ * predicate — on this API that is `InvalidGroup.NotFound`) is therefore
28022
+ * retried a bounded number of times (`SG_READBACK_NOT_FOUND_ATTEMPTS`,
28023
+ * `SG_READBACK_RETRY_DELAY_MS` apart) before the key is omitted; every
28024
+ * other failure, and an EMPTY answer, omits at once. On the update path a
28025
+ * group deleted out of band takes the same bounded wait and then omits —
28026
+ * its own rule / tag calls MAY already have failed, but an update whose
28027
+ * bags agree issues none, and the rollback replay / `drift --revert` reach
28028
+ * `updateSecurityGroup` too, so this read can be the first call to touch
28029
+ * it. `getSecurityGroupAttribute` (the orphan rewriter's live fetch) shares
28030
+ * the wait. The sleep itself is inside the best-effort boundary: a
28031
+ * rejecting sleep (only a test seam can reject) omits like a failed read.
28032
+ */
28033
+ async readSecurityGroupVpcId(groupId) {
28034
+ for (let attempt = 1;; attempt++) try {
28035
+ const vpcId = (await this.ec2Client.send(new DescribeSecurityGroupsCommand({ GroupIds: [groupId] })))?.SecurityGroups?.[0]?.VpcId;
28036
+ if (vpcId === void 0) this.logger.debug(`DescribeSecurityGroups reported no VpcId for SecurityGroup ${groupId}; omitting the attribute`);
28037
+ return vpcId;
28038
+ } catch (error) {
28039
+ const name = error instanceof Error && error.name ? error.name : "Error";
28040
+ const status = error?.$metadata?.httpStatusCode;
28041
+ const errorClass = typeof status === "number" ? `${name}, HTTP ${status}` : name;
28042
+ if (this.isNotFoundError(error) && attempt < 5) {
28043
+ this.logger.debug(`DescribeSecurityGroups for SecurityGroup ${groupId} answered not-found (${errorClass}) on attempt ${attempt}/${5}; retrying in ${SG_READBACK_RETRY_DELAY_MS}ms (eventual consistency)`);
28044
+ try {
28045
+ await (securityGroupReadbackDelays.sleep ?? ((ms) => this.sleep(ms)))(SG_READBACK_RETRY_DELAY_MS);
28046
+ } catch {
28047
+ this.logger.debug(`DescribeSecurityGroups retry sleep for SecurityGroup ${groupId} rejected; omitting the VpcId attribute`);
28048
+ return;
28049
+ }
28050
+ continue;
28051
+ }
28052
+ this.logger.debug(`DescribeSecurityGroups for SecurityGroup ${groupId} failed (${errorClass}); omitting the VpcId attribute: ${error instanceof Error ? error.message : String(error)}`);
27979
28053
  return;
27980
28054
  }
27981
28055
  }
28056
+ async getSecurityGroupAttribute(physicalId, attributeName) {
28057
+ if (attributeName === "GroupId") return physicalId;
28058
+ if (attributeName === "VpcId") return this.readSecurityGroupVpcId(physicalId);
28059
+ }
27982
28060
  /**
27983
28061
  * @param onUnusableProtocol When supplied, a malformed `IpProtocol` WARNS and
27984
28062
  * defaults instead of throwing. Passed by `updateSecurityGroupIngress`,
@@ -29671,10 +29749,17 @@ var EC2Provider = class {
29671
29749
  physicalId,
29672
29750
  attributes: {}
29673
29751
  } : null;
29674
- case "AWS::EC2::SecurityGroup": return (await this.ec2Client.send(new DescribeSecurityGroupsCommand({ GroupIds: [physicalId] }))).SecurityGroups?.[0] ? {
29675
- physicalId,
29676
- attributes: {}
29677
- } : null;
29752
+ case "AWS::EC2::SecurityGroup": {
29753
+ const group = (await this.ec2Client.send(new DescribeSecurityGroupsCommand({ GroupIds: [physicalId] }))).SecurityGroups?.[0];
29754
+ if (!group) return null;
29755
+ return {
29756
+ physicalId,
29757
+ attributes: definedAttributes({
29758
+ GroupId: physicalId,
29759
+ VpcId: group.VpcId
29760
+ })
29761
+ };
29762
+ }
29678
29763
  case "AWS::EC2::SecurityGroupIngress": {
29679
29764
  if (!SG_RULE_ID_PATTERN$1.test(physicalId)) return null;
29680
29765
  const rule = (await this.ec2Client.send(new DescribeSecurityGroupRulesCommand({ SecurityGroupRuleIds: [physicalId] }))).SecurityGroupRules?.[0];
@@ -63959,8 +64044,9 @@ const UNSUBSTITUTED_SUB_PLACEHOLDER = /\$\{[^}]*\}/;
63959
64044
  *
63960
64045
  * 1. `undefined` — the SAME signal the deploy side keys on. `resolve` returns it
63961
64046
  * WITHOUT throwing for a constructible-but-unknown attribute
63962
- * (`AWS::DynamoDB::Table.StreamArn`, `AWS::IAM::Policy.PolicyId`,
63963
- * `AWS::EC2::SecurityGroup.VpcId`, ...). `JSON.stringify` drops an
64047
+ * (`AWS::DynamoDB::Table.StreamArn`, `AWS::IAM::Policy.PolicyId`, ...;
64048
+ * `AWS::EC2::SecurityGroup.VpcId` left this list in #3097 — it now reads
64049
+ * the group live and REFUSES, which the catch below records). `JSON.stringify` drops an
63964
64050
  * `undefined`-valued key, so state can never hold one and such an output
63965
64051
  * would otherwise report a PERMANENT phantom `ADD` printing `new: undefined`.
63966
64052
  * 2. A symbol — `Ref: AWS::NoValue` resolves to a sentinel symbol, which
@@ -68661,6 +68747,87 @@ function createScrubCommand() {
68661
68747
  return cmd;
68662
68748
  }
68663
68749
 
68750
+ //#endregion
68751
+ //#region src/utils/own-keys.ts
68752
+ /**
68753
+ * Own-key helpers for the bags that reach cdkd through `JSON.parse` — a
68754
+ * `state.json` record, a Cloud Control `Properties` document, a template — and
68755
+ * for the raw SDK readbacks compared against them.
68756
+ *
68757
+ * `JSON.parse` yields a key literally named `__proto__` as an ORDINARY own
68758
+ * data property, and every comparator / rebuild walk over such a bag has to
68759
+ * keep treating it as one. Two idioms silently do not:
68760
+ *
68761
+ * - **Rebuilding onto a `{}` literal.** `out[k] = v` with `k === '__proto__'`
68762
+ * runs `Object.prototype`'s accessor, SETS the node's prototype and DROPS
68763
+ * the key — on BOTH comparison sides when the walk is a canonicalizer, so a
68764
+ * drift at that key is invisible (issue
68765
+ * [#3121](https://github.com/go-to-k/cdkd/issues/3121)), and on the WRITE
68766
+ * path the member vanishes from the payload (issue
68767
+ * [#2899](https://github.com/go-to-k/cdkd/issues/2899)). Rebuild onto
68768
+ * {@link nullPrototypeRecord} (no accessor there, so plain assignment
68769
+ * defines an own key) or, onto an existing plain object, define the key
68770
+ * with {@link defineOwnKey}.
68771
+ * - **`key in bag` membership.** `in` reads the prototype chain, so a key
68772
+ * named `constructor` / `toString` answers `true` for every plain object
68773
+ * and the recursion then walks `Object`'s function. {@link hasOwnKey} asks
68774
+ * the bag alone; {@link ownValue} is the matching read.
68775
+ *
68776
+ * And ONE shape test every such walk needs FIRST: {@link hasPlainPrototype}.
68777
+ * A non-plain object (`Date`, `Uint8Array`, `Map`, a class instance) has no
68778
+ * own enumerable keys, so a key-walk over one FABRICATES `{}` (or an index
68779
+ * map) and any two of them compare equal. Such a value is returned by
68780
+ * IDENTITY and compared as itself.
68781
+ *
68782
+ * Extracted from `src/cli/commands/drift.ts` (issue #2899, swept by PR #3124)
68783
+ * so the analyzer-side canonicalizers (#3121) share ONE spelling rather than a
68784
+ * fourth copy. A LEAF module, deliberately, and it must stay one: a value
68785
+ * import from a module other suites `vi.mock` wholesale reds those suites
68786
+ * with a missing-export failure (which is why `secret-redaction.ts` keeps its
68787
+ * own `hasPlainPrototype` — several drift suites mock that module).
68788
+ */
68789
+ /** Own-key membership: `in` reads the prototype chain, which a `JSON.parse`d bag never means. */
68790
+ function hasOwnKey(target, key) {
68791
+ return Object.prototype.hasOwnProperty.call(target, key);
68792
+ }
68793
+ /** An own-key read: `undefined` for an inherited name (`__proto__`, `constructor`, ...). */
68794
+ function ownValue(target, key) {
68795
+ return hasOwnKey(target, key) ? target[key] : void 0;
68796
+ }
68797
+ /** An ordinary (writable, enumerable, configurable) own data property — never the prototype. */
68798
+ function defineOwnKey(target, key, value) {
68799
+ Object.defineProperty(target, key, {
68800
+ value,
68801
+ writable: true,
68802
+ enumerable: true,
68803
+ configurable: true
68804
+ });
68805
+ }
68806
+ /**
68807
+ * Does this value carry an ORDINARY object prototype?
68808
+ *
68809
+ * `typeof v === 'object'` admits `Date` / `Map` / `Set` / `Uint8Array` / class
68810
+ * instances, whose own enumerable keys are `[]` — so a key-walk over one
68811
+ * reports "no contradiction" between two values that actually differ, and a
68812
+ * rebuild over one fabricates `{}`. A NULL prototype counts as plain: it is
68813
+ * what {@link nullPrototypeRecord} yields, and a walk must be able to re-walk
68814
+ * its own output.
68815
+ */
68816
+ function hasPlainPrototype(value) {
68817
+ const proto = Object.getPrototypeOf(value);
68818
+ return proto === Object.prototype || proto === null;
68819
+ }
68820
+ /**
68821
+ * A fresh rebuild target with NO prototype, so `out['__proto__'] = v` defines
68822
+ * an own key instead of setting the prototype. `JSON.stringify`,
68823
+ * `Object.keys` / `entries` and `structuredClone` all read it like a plain
68824
+ * object; only `instanceof Object` and a direct `.hasOwnProperty()` method call
68825
+ * do not, and no consumer on the drift path uses either.
68826
+ */
68827
+ function nullPrototypeRecord() {
68828
+ return Object.create(null);
68829
+ }
68830
+
68664
68831
  //#endregion
68665
68832
  //#region src/analyzer/drift-normalize.ts
68666
68833
  /**
@@ -68693,6 +68860,24 @@ function createScrubCommand() {
68693
68860
  * `properties`-fallback baseline (a resource deployed before observed-capture,
68694
68861
  * whose baseline is the user's TEMPLATE order). Sorting only the AWS read side
68695
68862
  * would manufacture drift on exactly that path.
68863
+ *
68864
+ * Every object arm below returns a NON-PLAIN object (a `Date` the raw SDK
68865
+ * readback carries, a `Uint8Array`) BY IDENTITY and rebuilds a plain one onto
68866
+ * a NULL-prototype record (issue
68867
+ * [#3121](https://github.com/go-to-k/cdkd/issues/3121), the analyzer-side
68868
+ * population of the class PR #3124 closed in `drift.ts`). A `{}` literal
68869
+ * rebuild had two consequences, both on the COMPARISON copies: an own
68870
+ * `__proto__` key (what `JSON.parse` yields from `state.json`) became the
68871
+ * rebuilt node's prototype and was dropped on BOTH sides, so a drift at that
68872
+ * key was invisible; and `Object.entries(new Date())` is `[]`, so a `Date`
68873
+ * member was flattened to `{}` before `calculateResourceDrift` could see it
68874
+ * (phantom drift against an ISO-string baseline, and `--accept` persisted the
68875
+ * `{}`). The identity return is what makes the second visible to the
68876
+ * comparator; the null-prototype target is what keeps the first an own key.
68877
+ * One layer LATER the same flattening survives in `secret-redaction.ts`'s
68878
+ * value walk, which `--accept` runs over a resource that RECORDS a secret
68879
+ * (issue #2427) -- so the ISO string reaches the baseline only where that
68880
+ * walk does not run.
68696
68881
  */
68697
68882
  function canonicalizeTagListsDeep(v) {
68698
68883
  if (Array.isArray(v)) {
@@ -68706,7 +68891,8 @@ function canonicalizeTagListsDeep(v) {
68706
68891
  return mapped;
68707
68892
  }
68708
68893
  if (v && typeof v === "object") {
68709
- const out = {};
68894
+ if (!hasPlainPrototype(v)) return v;
68895
+ const out = nullPrototypeRecord();
68710
68896
  for (const [k, val] of Object.entries(v)) out[k] = canonicalizeTagListsDeep(val);
68711
68897
  return out;
68712
68898
  }
@@ -68721,7 +68907,8 @@ function canonicalizeIdArraysDeep(v) {
68721
68907
  return mapped;
68722
68908
  }
68723
68909
  if (v && typeof v === "object") {
68724
- const out = {};
68910
+ if (!hasPlainPrototype(v)) return v;
68911
+ const out = nullPrototypeRecord();
68725
68912
  for (const [k, val] of Object.entries(v)) out[k] = canonicalizeIdArraysDeep(val);
68726
68913
  return out;
68727
68914
  }
@@ -68878,7 +69065,8 @@ function walkUnordered(v, path, paths) {
68878
69065
  return mapped;
68879
69066
  }
68880
69067
  if (v && typeof v === "object") {
68881
- const out = {};
69068
+ if (!hasPlainPrototype(v)) return v;
69069
+ const out = nullPrototypeRecord();
68882
69070
  for (const [k, val] of Object.entries(v)) out[k] = walkUnordered(val, path === "" ? k : `${path}.${k}`, paths);
68883
69071
  return out;
68884
69072
  }
@@ -69021,14 +69209,16 @@ function isIgnoredPath(path, ignorePaths) {
69021
69209
  * common case of "no drift" allocates nothing.
69022
69210
  */
69023
69211
  function diffAt(path, stateValue, awsValue, out, ignorePaths, unionWalkObjects) {
69024
- if (deepEqual(stateValue, awsValue)) return;
69212
+ const stateForm = jsonForm(stateValue);
69213
+ const awsForm = jsonForm(awsValue);
69214
+ if (deepEqual(stateForm, awsForm)) return;
69025
69215
  if (typeof stateValue === "string" && stateValue.includes("{{resolve:")) return;
69026
- if (isPlainObject(stateValue) && isPlainObject(awsValue) && !Array.isArray(stateValue) && !Array.isArray(awsValue)) {
69027
- const keys = unionWalkObjects ? /* @__PURE__ */ new Set([...Object.keys(stateValue), ...Object.keys(awsValue)]) : Object.keys(stateValue);
69216
+ if (isPlainObject(stateForm) && isPlainObject(awsForm) && !Array.isArray(stateForm) && !Array.isArray(awsForm)) {
69217
+ const keys = unionWalkObjects ? /* @__PURE__ */ new Set([...Object.keys(stateForm), ...Object.keys(awsForm)]) : Object.keys(stateForm);
69028
69218
  for (const key of keys) {
69029
69219
  const childPath = `${path}.${key}`;
69030
69220
  if (isIgnoredPath(childPath, ignorePaths)) continue;
69031
- diffAt(childPath, stateValue[key], awsValue[key], out, ignorePaths, unionWalkObjects);
69221
+ diffAt(childPath, stateForm[key], awsForm[key], out, ignorePaths, unionWalkObjects);
69032
69222
  }
69033
69223
  return;
69034
69224
  }
@@ -69039,11 +69229,39 @@ function diffAt(path, stateValue, awsValue, out, ignorePaths, unionWalkObjects)
69039
69229
  });
69040
69230
  }
69041
69231
  /**
69232
+ * The form `JSON.stringify` would serialize a value as, for the ONE shape the
69233
+ * comparator's "JSON-roundtrip equality" contract did not cover: a NON-PLAIN
69234
+ * object carrying `toJSON` (a `Date`, which serializes as its ISO string; a
69235
+ * `Buffer`, whose prototype `toJSON` yields `{type, data}`). Every other value
69236
+ * is returned untouched. What the comparator then does with an untouched
69237
+ * non-plain object is what it does with a plain one -- it walks the OWN keys
69238
+ * (`deepEqual` below, and `diffAt`'s descend gate, test `typeof === 'object'`
69239
+ * and nothing about the prototype) -- so a `Uint8Array` compares by its
69240
+ * index keys (the persisted `{"0":1,"1":2}` equals the readback typed array,
69241
+ * the convergence this fold exists for) and a keyless `Map` / class instance
69242
+ * compares equal to `{}` and to any other keyless one. That last case is
69243
+ * PRE-EXISTING and is not what the canonicalizers' identity return protects:
69244
+ * they keep a non-plain node from being REBUILT as `{}`, and this fold keeps
69245
+ * the JSON-carrying ones from drifting forever against their persisted form;
69246
+ * neither adds a prototype guard here, because a bare `hasPlainPrototype`
69247
+ * gate would break the typed-array convergence (PR #3148 delta review).
69248
+ */
69249
+ function jsonForm(value) {
69250
+ if (value === null || typeof value !== "object" || hasPlainPrototype(value)) return value;
69251
+ const toJSON = value.toJSON;
69252
+ return typeof toJSON === "function" ? toJSON.call(value) : value;
69253
+ }
69254
+ /**
69042
69255
  * Structural equality used by the drift comparator. Identical to a
69043
69256
  * plain `JSON.stringify`-roundtrip equality except it tolerates
69044
69257
  * undefined-vs-missing-key gaps the same way (both serialize away).
69258
+ * `Date`s (and anything else non-plain with `toJSON`) are compared in their
69259
+ * JSON form via {@link jsonForm}, so the persisted ISO string of an accepted
69260
+ * readback equals the `Date` the next readback returns.
69045
69261
  */
69046
69262
  function deepEqual(a, b) {
69263
+ a = jsonForm(a);
69264
+ b = jsonForm(b);
69047
69265
  if (a === b) return true;
69048
69266
  if (a === null || b === null || a === void 0 || b === void 0) return a === b;
69049
69267
  if (typeof a !== typeof b) return false;
@@ -69126,6 +69344,12 @@ function isPlainObject(value) {
69126
69344
  * and manufacturing a different phantom drift, so the pass is scoped to the
69127
69345
  * object shape — which is what `AWS::S3::BucketPolicy` and the other
69128
69346
  * `PolicyDocument`-typed properties carry in cdkd state.
69347
+ *
69348
+ * The two rewrite walks rebuild onto a NULL-prototype record and return a
69349
+ * non-plain object by identity, for the reason `drift-normalize.ts`'s header
69350
+ * gives (issue #3121): they run on the comparison copies of BOTH sides, so a
69351
+ * `{}` literal rebuild dropped an own `__proto__` key symmetrically and
69352
+ * flattened a `Date` to `{}` before the comparator saw it.
69129
69353
  */
69130
69354
  /**
69131
69355
  * An IAM unique id as it appears in a rendered policy. The prefix set is
@@ -69215,7 +69439,8 @@ function collectPrincipalForms(value) {
69215
69439
  function rewritePrincipalUniqueIds(value, arnByUniqueId) {
69216
69440
  if (Array.isArray(value)) return value.map((el) => rewritePrincipalUniqueIds(el, arnByUniqueId));
69217
69441
  if (!value || typeof value !== "object") return value;
69218
- const out = {};
69442
+ if (!hasPlainPrototype(value)) return value;
69443
+ const out = nullPrototypeRecord();
69219
69444
  for (const [key, member] of Object.entries(value)) out[key] = key === "Principal" || key === "NotPrincipal" ? rewritePrincipalValue(member, arnByUniqueId) : rewritePrincipalUniqueIds(member, arnByUniqueId);
69220
69445
  return out;
69221
69446
  }
@@ -69224,7 +69449,8 @@ function rewritePrincipalValue(value, arnByUniqueId) {
69224
69449
  if (typeof value === "string") return swap(value);
69225
69450
  if (Array.isArray(value)) return value.map(swap);
69226
69451
  if (!value || typeof value !== "object") return value;
69227
- const out = {};
69452
+ if (!hasPlainPrototype(value)) return value;
69453
+ const out = nullPrototypeRecord();
69228
69454
  for (const [key, member] of Object.entries(value)) {
69229
69455
  if (key !== "AWS") {
69230
69456
  out[key] = rewritePrincipalUniqueIds(member, arnByUniqueId);
@@ -69482,7 +69708,12 @@ const CC_API_FALLBACK_DENY_LIST = {
69482
69708
  *
69483
69709
  * Strips are shallow only — fields are removed if they appear at any
69484
69710
  * depth (recursive walk) but the values themselves are not modified.
69485
- * Arrays are walked element-wise.
69711
+ * Arrays are walked element-wise. The rebuild target is a NULL-prototype
69712
+ * record and a non-plain object is returned by identity, for the reason
69713
+ * `drift-normalize.ts`'s header gives (issue #3121): the walk feeds the
69714
+ * comparison copy, so a `{}` literal rebuild dropped an own `__proto__` key
69715
+ * (which the `JSON.parse`d `Properties` document can carry) and flattened a
69716
+ * non-plain member to `{}`.
69486
69717
  */
69487
69718
  /**
69488
69719
  * Field names AWS attaches to nearly every resource. These are NOT user
@@ -69538,7 +69769,8 @@ function stripWalk(value) {
69538
69769
  if (value === null || value === void 0) return value;
69539
69770
  if (Array.isArray(value)) return value.map(stripWalk);
69540
69771
  if (typeof value === "object") {
69541
- const out = {};
69772
+ if (!hasPlainPrototype(value)) return value;
69773
+ const out = nullPrototypeRecord();
69542
69774
  for (const [key, child] of Object.entries(value)) {
69543
69775
  if (ALWAYS_STRIPPED_FIELDS.has(key)) continue;
69544
69776
  out[key] = stripWalk(child);
@@ -70949,10 +71181,10 @@ function buildReadCurrentStateContext(state, excludedLogicalId) {
70949
71181
  * below would then compare against the accepted one. `undefined` is the answer
70950
71182
  * "the bag has nothing here", which is what an absent segment means.
70951
71183
  *
70952
- * Exported, like `setAtPath`, as a test seam only: a `__proto__` path cannot
70953
- * reach either through the command today (the comparison chain's normalisers
70954
- * drop the key on both sides — issue #3121), so the own-key rule is pinned on
70955
- * the helpers directly.
71184
+ * Exported, like `setAtPath`, as a test seam: the own-key rule is pinned on
71185
+ * the helpers directly, and since issue #3121 (the comparison chain's
71186
+ * normalisers keep the key as an own key on both sides) a top-level
71187
+ * `__proto__` drift also reaches both through the command.
70956
71188
  */
70957
71189
  function getAtPath(source, path) {
70958
71190
  if (path.length === 0) return source;
@@ -70991,23 +71223,6 @@ function setAtPath(target, path, value) {
70991
71223
  }
70992
71224
  defineOwnKey(cursor, segments[segments.length - 1], value);
70993
71225
  }
70994
- /** Own-key membership: `in` reads the prototype chain, which a `JSON.parse`d bag never means. */
70995
- function hasOwnKey(target, key) {
70996
- return Object.prototype.hasOwnProperty.call(target, key);
70997
- }
70998
- /** An own-key read: `undefined` for an inherited name (`__proto__`, `constructor`, ...). */
70999
- function ownValue(target, key) {
71000
- return hasOwnKey(target, key) ? target[key] : void 0;
71001
- }
71002
- /** An ordinary (writable, enumerable, configurable) own data property — never the prototype. */
71003
- function defineOwnKey(target, key, value) {
71004
- Object.defineProperty(target, key, {
71005
- value,
71006
- writable: true,
71007
- enumerable: true,
71008
- configurable: true
71009
- });
71010
- }
71011
71226
  /**
71012
71227
  * `--accept`: state ← AWS.
71013
71228
  *
@@ -71365,22 +71580,6 @@ function isPlainRecord(value) {
71365
71580
  return typeof value === "object" && value !== null && !Array.isArray(value);
71366
71581
  }
71367
71582
  /**
71368
- * Does this value carry an ORDINARY object prototype?
71369
- *
71370
- * {@link isPlainRecord} admits `Date` / `Map` / `Set` / class instances, whose
71371
- * own enumerable keys are `[]` — so a key-walk over one reports "no
71372
- * contradiction" between two values that actually differ. The redaction walk's
71373
- * `deepEqualJsonValue` carries the same guard for the same reason
71374
- * (`secret-redaction.ts`, issue #2869): a `Date` anchor corroborates NOTHING.
71375
- * Kept file-local rather than imported because `secret-redaction.js` is
71376
- * `vi.mock`ed by several drift suites, and a value import from it reds them
71377
- * with a missing-export failure.
71378
- */
71379
- function hasPlainPrototype(value) {
71380
- const proto = Object.getPrototypeOf(value);
71381
- return proto === Object.prototype || proto === null;
71382
- }
71383
- /**
71384
71583
  * Merge the AWS-current value with the revert baseline, KEEPING every path the
71385
71584
  * baseline does not declare (issue
71386
71585
  * [#1626](https://github.com/go-to-k/cdkd/issues/1626) items 2 + 3).
@@ -80886,7 +81085,9 @@ var AttributeFetcher = class {
80886
81085
  ok: false,
80887
81086
  reason
80888
81087
  };
80889
- const cached = this.orphans[orphanLogicalId].attributes?.[attribute];
81088
+ const orphan = this.orphans[orphanLogicalId];
81089
+ const stored = orphan.attributes?.[attribute];
81090
+ const cached = isImpossibleEmptyStoredAttribute(orphan.resourceType, attribute, stored) ? void 0 : stored;
80890
81091
  if (cached === void 0) {
80891
81092
  this.logger.warn(`--force: state.attributes also lacks '${orphanLogicalId}.${attribute}'; leaving the original intrinsic in place.`);
80892
81093
  return {
@@ -92457,4 +92658,4 @@ function buildProgram() {
92457
92658
 
92458
92659
  //#endregion
92459
92660
  export { buildProgram };
92460
- //# sourceMappingURL=program-CoRRfEe_.js.map
92661
+ //# sourceMappingURL=program-D30EPjOn.js.map