@go-to-k/cdkd 0.281.19 → 0.281.21

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.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as configStringRefusal, $t as CFN_TEMPLATE_URL_LIMIT, A as green, At as validateContainerRepoName, B as slowCcOperationTimeoutMs, Bt as synthesisStatusMessage, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as PartialFailureError, Ct as rewriteTemplateAssetReferences, D as bold, Dn as StackHasActiveImportsError, Dt as getBootstrapMarkerKey, E as formatResourceLine, En as ResourceUpdateNotSupportedError, Et as ensureAssetStorage, F as clearOnUpdateRemoval, Ft as runDockerForeground, G as getAccountInfo, Gt as resolveCaptureObservedState, H as isTerminationProtectionPropagationError, Ht as getLegacyStateBucketName, I as ProviderRegistry, It as runDockerStreaming, J as normalizeAwsTagsToCfn, Jt as resolveStateBucketWithDefaultAndSource, K as refStateLookupFromResource, Kt as resolveSkipPrefix, L as findActionableSilentDrops, Lt as AssetManifestLoader, M as yellow, Mt as buildDockerImage, N as IAMRoleProvider, Nn as normalizeAwsError, Nt as formatDockerLoginError, O as cyan, On as StackTerminationProtectionError, Ot as parseBootstrapMarker, P as collectInlinePolicyNamesManagedBySiblings, Pn as withErrorHandling, Pt as getDockerCmd, Q as configBooleanRefusal, Qt as CFN_TEMPLATE_BODY_LIMIT, R as findSilentDropProperties, Rt as getDockerImageBySourceHash, S as extractDeploymentEventError, Sn as NestedStackChildDirectDestroyError, St as loadPublishableAssetManifest, T as renderStatefulReason, Tn as ResourceTimeoutError, Tt as BOOTSTRAP_MARKER_PREFIX, U as IntrinsicFunctionResolver, Ut as resolveApp, V as disableInstanceApiTermination, Vt as getDefaultStateBucketName, W as cfnRefValueFromPhysicalId, Wt as resolveAutoAssetStorage, X as assertRegionMatch, Xt as stateBucketExistenceConfirmed, Y as resolveExplicitPhysicalId, Yt as resolveUseCdkBootstrapAssets, Z as coerceCfnBoolean, Zt as warnDeprecatedNoPrefixCliFlag, _ as createPreDeleteFinalSnapshot, _n as LocalInvokeBuildError$1, _t as AssetPublisher, a as DeploymentEventsStore, an as processStackMessages, at as applyRoleArnIfSet, b as unsupportedFinalSnapshotError, bt as buildAssetRedirectMap, c as replayFailedOperations, cn as AwsClients, ct as withRetry, d as IMPLICIT_DELETE_DEPENDENCIES, dn as setAwsClients, dt as DagBuilder, en as MIGRATE_TMP_PREFIX, et as readConfigString, f as computeImplicitDeleteEdges, ft as TemplateParser, g as ccRoutedFinalSnapshotError, gn as DeployCancelledError, gt as shouldRetainResource, h as buildFinalSnapshotIdentifier, ht as rebuildClientForBucketRegion, i as DeploymentEventsReader, in as AssemblyReader, it as requireConfigString, j as red, jt as derivePartitionAndUrlSuffix, k as gray, kt as validateAssetBucketName, l as replayRollback, ln as getAwsClients, lt as isRetryableTransientError, m as PRE_DELETE_SNAPSHOT_TYPES, mt as S3StateBackend, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as uploadCfnTemplate, nt as requireConfigArray, o as planFailedOps, ot as DiffCalculator, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as CdkdError, pt as LockManager, q as WAFv2WebACLProvider, qt as resolveStateBucketWithDefault, r as DeployEngine, rn as expectedOwnerParam, rt as requireConfigObject, s as planRollback, sn as resolveBucketRegion, st as describeTypeWithThrottleRetry, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as findLargeInlineResources, tt as replayWarn, u as withResourceDeadline, un as resetAwsClients, ut as isThrottlingError, v as isFinalSnapshotError, vn as LocalMigrateError, vt as stringifyValue, w as isStatefulRecreateTargetSync, wn as ProvisioningError, wt as AssetModeResolver, x as makeCanonicalizePropertiesFn, xn as MissingCdkCliError, xt as createAssetRedirectResolver, y as refusesFinalSnapshot, yn as LocalStartServiceError, yt as WorkGraph, z as CloudControlProvider, zt as Synthesizer } from "./deploy-engine-DPnxkjWi.js";
2
+ import { $ as configStringRefusal, $t as CFN_TEMPLATE_URL_LIMIT, A as green, At as validateContainerRepoName, B as slowCcOperationTimeoutMs, Bt as synthesisStatusMessage, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as PartialFailureError, Ct as rewriteTemplateAssetReferences, D as bold, Dn as StackHasActiveImportsError, Dt as getBootstrapMarkerKey, E as formatResourceLine, En as ResourceUpdateNotSupportedError, Et as ensureAssetStorage, F as clearOnUpdateRemoval, Ft as runDockerForeground, G as getAccountInfo, Gt as resolveCaptureObservedState, H as isTerminationProtectionPropagationError, Ht as getLegacyStateBucketName, I as ProviderRegistry, It as runDockerStreaming, J as normalizeAwsTagsToCfn, Jt as resolveStateBucketWithDefaultAndSource, K as refStateLookupFromResource, Kt as resolveSkipPrefix, L as findActionableSilentDrops, Lt as AssetManifestLoader, M as yellow, Mt as buildDockerImage, N as IAMRoleProvider, Nn as normalizeAwsError, Nt as formatDockerLoginError, O as cyan, On as StackTerminationProtectionError, Ot as parseBootstrapMarker, P as collectInlinePolicyNamesManagedBySiblings, Pn as withErrorHandling, Pt as getDockerCmd, Q as configBooleanRefusal, Qt as CFN_TEMPLATE_BODY_LIMIT, R as findSilentDropProperties, Rt as getDockerImageBySourceHash, S as extractDeploymentEventError, Sn as NestedStackChildDirectDestroyError, St as loadPublishableAssetManifest, T as renderStatefulReason, Tn as ResourceTimeoutError, Tt as BOOTSTRAP_MARKER_PREFIX, U as IntrinsicFunctionResolver, Ut as resolveApp, V as disableInstanceApiTermination, Vt as getDefaultStateBucketName, W as cfnRefValueFromPhysicalId, Wt as resolveAutoAssetStorage, X as assertRegionMatch, Xt as stateBucketExistenceConfirmed, Y as resolveExplicitPhysicalId, Yt as resolveUseCdkBootstrapAssets, Z as coerceCfnBoolean, Zt as warnDeprecatedNoPrefixCliFlag, _ as createPreDeleteFinalSnapshot, _n as LocalInvokeBuildError$1, _t as AssetPublisher, a as DeploymentEventsStore, an as processStackMessages, at as applyRoleArnIfSet, b as unsupportedFinalSnapshotError, bt as buildAssetRedirectMap, c as replayFailedOperations, cn as AwsClients, ct as withRetry, d as IMPLICIT_DELETE_DEPENDENCIES, dn as setAwsClients, dt as DagBuilder, en as MIGRATE_TMP_PREFIX, et as readConfigString, f as computeImplicitDeleteEdges, ft as TemplateParser, g as ccRoutedFinalSnapshotError, gn as DeployCancelledError, gt as shouldRetainResource, h as buildFinalSnapshotIdentifier, ht as rebuildClientForBucketRegion, i as DeploymentEventsReader, in as AssemblyReader, it as requireConfigString, j as red, jt as derivePartitionAndUrlSuffix, k as gray, kt as validateAssetBucketName, l as replayRollback, ln as getAwsClients, lt as isRetryableTransientError, m as PRE_DELETE_SNAPSHOT_TYPES, mt as S3StateBackend, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as uploadCfnTemplate, nt as requireConfigArray, o as planFailedOps, ot as DiffCalculator, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as CdkdError, pt as LockManager, q as WAFv2WebACLProvider, qt as resolveStateBucketWithDefault, r as DeployEngine, rn as expectedOwnerParam, rt as requireConfigObject, s as planRollback, sn as resolveBucketRegion, st as describeTypeWithThrottleRetry, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as findLargeInlineResources, tt as replayWarn, u as withResourceDeadline, un as resetAwsClients, ut as isThrottlingError, v as isFinalSnapshotError, vn as LocalMigrateError, vt as stringifyValue, w as isStatefulRecreateTargetSync, wn as ProvisioningError, wt as AssetModeResolver, x as makeCanonicalizePropertiesFn, xn as MissingCdkCliError, xt as createAssetRedirectResolver, y as refusesFinalSnapshot, yn as LocalStartServiceError, yt as WorkGraph, z as CloudControlProvider, zt as Synthesizer } from "./deploy-engine-Cx48pqGQ.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
4
- import { t as ASGProvider } from "./asg-provider-CE-lwlgZ.js";
4
+ import { t as ASGProvider } from "./asg-provider-CH0YfbOn.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
7
7
  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, 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";
@@ -13320,6 +13320,51 @@ const TABLE_ACTIVE_WAIT_ATTEMPTS = 60;
13320
13320
  * genuinely wedged capacity edit still fails in a minute rather than ten.
13321
13321
  */
13322
13322
  const BILLING_FLIP_ACTIVE_WAIT_ATTEMPTS = 600;
13323
+ /**
13324
+ * Is this `WarmThroughput` VALUE one cdkd will actually put on the wire
13325
+ * (issue #1760)?
13326
+ *
13327
+ * The single definition of the send rule. `create()` and `update()` call it
13328
+ * where they used to inline `if (properties['WarmThroughput'])`, and the
13329
+ * bag-level {@link declaresWarmThroughput} below calls it too — so the drift
13330
+ * side cannot answer "the template declared one" for a value the write side
13331
+ * silently skips. That divergence is not hypothetical: with the two sites
13332
+ * spelled independently, respelling either as `!== undefined` leaves every
13333
+ * test green while the pair starts disagreeing on `WarmThroughput: null`, and
13334
+ * the disagreement re-creates the exact phantom drift this issue removed —
13335
+ * cdkd sends nothing, AWS still computes 12000/4000, the readback emits it as
13336
+ * though the template had asked, and `--revert` can never clear it because the
13337
+ * write gate skips the falsy value. Keep this the ONLY spelling of the rule.
13338
+ *
13339
+ * Deliberately TRUTHINESS, because that is what the wire has always done.
13340
+ */
13341
+ function isSendableWarmThroughput(value) {
13342
+ return Boolean(value);
13343
+ }
13344
+ /**
13345
+ * Does this desired-property bag DECLARE a table-level `WarmThroughput`?
13346
+ *
13347
+ * The drift-side question ("did cdkd send one for this resource"), answered by
13348
+ * {@link isSendableWarmThroughput} so it cannot drift from the write path.
13349
+ *
13350
+ * An ABSENT or EMPTY bag answers TRUE: the caller supplied nothing to decide
13351
+ * with, so both drift consumers keep the pre-#1760 behavior (emit it, compare
13352
+ * it) rather than dropping a key on the strength of a bag that was never
13353
+ * passed. That fallback is exactly why the write sites take the VALUE-level
13354
+ * predicate instead of this one — handing `create()` an absent bag must send
13355
+ * nothing, not send `undefined`.
13356
+ *
13357
+ * Note the empty-bag arm carries the same residual as the declared arm
13358
+ * (issue #1768): a `properties: {}` record keeps the AWS-computed value in its
13359
+ * snapshot, so a `--revert` on such a resource can re-send it. Left as-is
13360
+ * rather than dropped, because a bag that was never populated is not evidence
13361
+ * the template declared nothing — and a wrong DROP here is unrecoverable
13362
+ * phantom drift, while the residual is a loud, per-resource revert failure.
13363
+ */
13364
+ function declaresWarmThroughput(properties) {
13365
+ if (properties === void 0 || Object.keys(properties).length === 0) return true;
13366
+ return isSendableWarmThroughput(properties["WarmThroughput"]);
13367
+ }
13323
13368
  var DynamoDBTableProvider = class {
13324
13369
  dynamoDBClient;
13325
13370
  logger = getLogger().child("DynamoDBTableProvider");
@@ -13376,7 +13421,7 @@ var DynamoDBTableProvider = class {
13376
13421
  };
13377
13422
  }
13378
13423
  if (properties["OnDemandThroughput"]) createParams.OnDemandThroughput = properties["OnDemandThroughput"];
13379
- if (properties["WarmThroughput"]) createParams.WarmThroughput = properties["WarmThroughput"];
13424
+ if (isSendableWarmThroughput(properties["WarmThroughput"])) createParams.WarmThroughput = properties["WarmThroughput"];
13380
13425
  if (properties["StreamSpecification"]) createParams.StreamSpecification = {
13381
13426
  StreamEnabled: true,
13382
13427
  StreamViewType: properties["StreamSpecification"]["StreamViewType"]
@@ -13539,7 +13584,7 @@ var DynamoDBTableProvider = class {
13539
13584
  }
13540
13585
  }
13541
13586
  if (JSON.stringify(properties["WarmThroughput"]) !== JSON.stringify(previousProperties["WarmThroughput"])) {
13542
- if (properties["WarmThroughput"]) {
13587
+ if (isSendableWarmThroughput(properties["WarmThroughput"])) {
13543
13588
  await this.dynamoDBClient.send(new UpdateTableCommand({
13544
13589
  TableName: physicalId,
13545
13590
  WarmThroughput: properties["WarmThroughput"]
@@ -14131,6 +14176,75 @@ var DynamoDBTableProvider = class {
14131
14176
  }
14132
14177
  }
14133
14178
  /**
14179
+ * `AttributeDefinitions` is a SET, keyed by `AttributeName`: CFn accepts the
14180
+ * members in any order and `DescribeTable` returns them in an order matching
14181
+ * neither the request nor the template. Measured live (us-east-1,
14182
+ * 2026-08-13): a `CreateTable` whose input declared
14183
+ * `[{pk,S}, {gsipk,S}]` read back as `[{gsipk,S}, {pk,S}]` on the create
14184
+ * response AND on every later `DescribeTable`. The comparator compares
14185
+ * arrays positionally, so without this declaration the reorder is permanent
14186
+ * phantom drift on a table nobody touched — reachable whenever the drift
14187
+ * baseline is the template `properties` rather than `observedProperties`
14188
+ * (after a reverse-replacement rollback, which strips `observedProperties`,
14189
+ * or on a resource deployed before observed-capture existed; on the ordinary
14190
+ * path both sides come from the same readback and already agree, which is
14191
+ * why routine runs never surfaced it). Issue #1760, the
14192
+ * `AWS::DynamoDB::Table` twin of issue #1742's `AWS::DynamoDB::GlobalTable`
14193
+ * finding.
14194
+ *
14195
+ * `KeySchema` is deliberately NOT declared, at the table level or the index
14196
+ * level: it is order-SIGNIFICANT (HASH before RANGE), so sorting it would
14197
+ * HIDE a real key change rather than remove a phantom one.
14198
+ */
14199
+ getDriftUnorderedPaths(resourceType) {
14200
+ if (resourceType !== "AWS::DynamoDB::Table") return [];
14201
+ return ["AttributeDefinitions"];
14202
+ }
14203
+ /**
14204
+ * `WarmThroughput` is reported by AWS for EVERY table, whether or not the
14205
+ * template ever asked for one. Measured live (us-east-1, 2026-08-13) on a
14206
+ * `CreateTable` whose input declared NO `WarmThroughput`:
14207
+ *
14208
+ * ```
14209
+ * "WarmThroughput": { "ReadUnitsPerSecond": 12000,
14210
+ * "WriteUnitsPerSecond": 4000, "Status": "ACTIVE" }
14211
+ * ```
14212
+ *
14213
+ * so `readCurrentState`'s emit-when-present guard could never do what its
14214
+ * comment claimed ("only on tables that set warm throughput"). That value is
14215
+ * AWS-computed and AWS-owned — it only ever increases as the table's traffic
14216
+ * grows, and it cannot be lowered — so once it is frozen into an
14217
+ * `observedProperties` baseline, a later AWS-side increase surfaces as drift
14218
+ * on a property the user never declared and `--revert` would issue a
14219
+ * decrease AWS rejects. `readCurrentState` therefore emits it only when the
14220
+ * DESIRED bag declares it (the fix issue #1742 PROPOSES for the per-index
14221
+ * sibling — still open and unlanded at the time of writing, so this is the
14222
+ * first site to carry the shape), never as a blanket drop:
14223
+ * `AWS::DynamoDB::Table` accepts an explicit `WarmThroughput` and a real
14224
+ * change to one must stay visible. Known residual on that declared arm
14225
+ * (issue #1768): AWS may GROW a declared value, which is reported as drift —
14226
+ * correctly, cdkd holds a value AWS no longer has — but `--revert` then
14227
+ * issues a decrease AWS rejects, surfacing as a per-resource revert failure
14228
+ * rather than as a silent wrong answer.
14229
+ *
14230
+ * This declaration is the OTHER half: a state record written by an earlier
14231
+ * binary already carries the computed value in `observedProperties`, so the
14232
+ * gate alone would flip every such table to a one-sided `WarmThroughput`
14233
+ * drift (baseline has it, the AWS side no longer emits it) until the next
14234
+ * deploy refreshed the capture. Ignoring the path on BOTH sides when the
14235
+ * template declares nothing covers the transition and the steady state
14236
+ * alike; a table that DOES declare `WarmThroughput` is compared normally.
14237
+ * Same per-resource scoping seam as `AWS::ApiGatewayV2::Integration`'s
14238
+ * `TlsConfig` (issue #1602): an absent / empty bag falls back to the
14239
+ * type-level answer of COMPARING, since hiding a real drift is the worse
14240
+ * failure.
14241
+ */
14242
+ getDriftUnknownPaths(resourceType, properties) {
14243
+ if (resourceType !== "AWS::DynamoDB::Table") return [];
14244
+ if (declaresWarmThroughput(properties)) return [];
14245
+ return ["WarmThroughput"];
14246
+ }
14247
+ /**
14134
14248
  * Read the AWS-current DynamoDB table configuration in CFn-property shape.
14135
14249
  *
14136
14250
  * `DescribeTable` returns every field cdkd manages in one call. AWS uses
@@ -14142,8 +14256,18 @@ var DynamoDBTableProvider = class {
14142
14256
  * - StreamSpecification's CFn shape includes only `StreamViewType`; the
14143
14257
  * API response carries `StreamEnabled` too. We surface both since the
14144
14258
  * drift comparator only descends into keys present in state.
14145
- * - GSI / LSI in the API response include `IndexStatus`, `ItemCount` and
14146
- * sizing fields that cdkd never sets; the comparator filters them.
14259
+ * - GSI / LSI in the API response include `IndexStatus`, `ItemCount`,
14260
+ * `IndexArn` and sizing fields cdkd never sets, and they are forwarded
14261
+ * VERBATIM. This used to claim "the comparator filters them" — it does
14262
+ * NOT, and issue #1767 quotes that sentence as the bug: the filter is
14263
+ * `calculateResourceDrift`'s state-keys-only walk, which only reaches an
14264
+ * absent TOP-LEVEL key. These are members of an array under a key the
14265
+ * baseline DOES carry, and arrays compare positionally, so every
14266
+ * AWS-managed member participates — `ItemCount` MOVES as data lands, so
14267
+ * an in-use table with an index drifts against its own
14268
+ * `observedProperties` on the ordinary path. Fixing it needs a full
14269
+ * reverse-mapper plus the same migration companion this issue's
14270
+ * `WarmThroughput` gate needed, hence a separate issue.
14147
14271
  *
14148
14272
  * Returns `undefined` when the table is gone (`ResourceNotFoundException`).
14149
14273
  *
@@ -14153,8 +14277,12 @@ var DynamoDBTableProvider = class {
14153
14277
  * drift, and the result key is omitted entirely when AWS reports no user
14154
14278
  * tags (matches `create()`'s behavior of only sending Tags when the
14155
14279
  * template carries them).
14280
+ *
14281
+ * `properties` is the DESIRED bag (the state-recorded template intent every
14282
+ * caller passes) and is consulted for exactly one field, `WarmThroughput` —
14283
+ * see {@link getDriftUnknownPaths} for the measurement and the reasoning.
14156
14284
  */
14157
- async readCurrentState(physicalId, _logicalId, _resourceType) {
14285
+ async readCurrentState(physicalId, _logicalId, _resourceType, properties) {
14158
14286
  try {
14159
14287
  const table = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }))).Table;
14160
14288
  if (!table) return void 0;
@@ -14173,7 +14301,7 @@ var DynamoDBTableProvider = class {
14173
14301
  if (table.OnDemandThroughput.MaxWriteRequestUnits !== void 0) odt["MaxWriteRequestUnits"] = table.OnDemandThroughput.MaxWriteRequestUnits;
14174
14302
  if (Object.keys(odt).length > 0) result["OnDemandThroughput"] = odt;
14175
14303
  }
14176
- if (table.WarmThroughput) {
14304
+ if (table.WarmThroughput && declaresWarmThroughput(properties)) {
14177
14305
  const wt = {};
14178
14306
  if (table.WarmThroughput.ReadUnitsPerSecond !== void 0) wt["ReadUnitsPerSecond"] = table.WarmThroughput.ReadUnitsPerSecond;
14179
14307
  if (table.WarmThroughput.WriteUnitsPerSecond !== void 0) wt["WriteUnitsPerSecond"] = table.WarmThroughput.WriteUnitsPerSecond;
@@ -14572,6 +14700,18 @@ var DynamoDBGlobalTableProvider = class {
14572
14700
  delete copy["GlobalSecondaryIndexes"];
14573
14701
  return copy;
14574
14702
  });
14703
+ const keptKeyAttributes = collectDesiredKeyAttributeNames(properties, { indexListKeys: ["LocalSecondaryIndexes"] });
14704
+ if (keptKeyAttributes.size > 0 && Array.isArray(attributeDefinitions)) {
14705
+ const prunedAttributeDefinitions = attributeDefinitions.filter((definition) => {
14706
+ const name = asRecord$1(definition)?.["AttributeName"];
14707
+ if (typeof name !== "string") return true;
14708
+ return keptKeyAttributes.has(name);
14709
+ });
14710
+ if (prunedAttributeDefinitions.length !== attributeDefinitions.length) {
14711
+ createParams.AttributeDefinitions = prunedAttributeDefinitions;
14712
+ effectiveProperties["AttributeDefinitions"] = [...prunedAttributeDefinitions];
14713
+ }
14714
+ }
14575
14715
  }
14576
14716
  if (sdkIndexes.length > 0) createParams.GlobalSecondaryIndexes = sdkIndexes;
14577
14717
  if (properties["LocalSecondaryIndexes"]) createParams.LocalSecondaryIndexes = properties["LocalSecondaryIndexes"];
@@ -15945,6 +16085,31 @@ var DynamoDBGlobalTableProvider = class {
15945
16085
  return [];
15946
16086
  }
15947
16087
  /**
16088
+ * `AttributeDefinitions` is an unordered SET, and the comparator compares
16089
+ * arrays positionally (issue #1742).
16090
+ *
16091
+ * `DescribeTable` returns the definitions in an order matching neither the
16092
+ * request nor alphabetical — measured us-east-1 2026-08-13 on the
16093
+ * `rollback-replay-effective-props` fixture, where a table declaring
16094
+ * `[pk, gsipk]` read back as `[gsipk, pk]` — so an UNTOUCHED table drifted
16095
+ * forever. Sorting cannot hide a real change here because the list is a
16096
+ * genuine set: members are keyed by `AttributeName`, so adding, removing or
16097
+ * retyping one still changes the canonicalized form.
16098
+ *
16099
+ * Reachable whenever the drift baseline is `properties` rather than
16100
+ * `observedProperties` — i.e. after any reverse-replacement rollback, which
16101
+ * `rollback-executor.ts` strips `observedProperties` on. That is also why it
16102
+ * went unseen: on the ordinary path both sides come from the same readback.
16103
+ *
16104
+ * `KeySchema` is deliberately NOT declared, at the table level or per index:
16105
+ * it is order-SIGNIFICANT (HASH before RANGE), so sorting it would silently
16106
+ * hide a real key change — the failure direction this file's sibling rules
16107
+ * call the worse one.
16108
+ */
16109
+ getDriftUnorderedPaths(_resourceType) {
16110
+ return ["AttributeDefinitions"];
16111
+ }
16112
+ /**
15948
16113
  * Adopt an existing DynamoDB GlobalTable into cdkd state.
15949
16114
  *
15950
16115
  * Lookup order:
@@ -16293,12 +16458,21 @@ function collectAutoScalingTargets(properties, localRegion) {
16293
16458
  * redefine-in-place (replacement required); one outside it is the normal
16294
16459
  * bookkeeping of a GSI deletion and must NOT force a replacement.
16295
16460
  *
16296
- * Malformed / intrinsic-valued entries contribute nothing, which errs on the
16297
- * PERMISSIVE side of the guard — correct here, because the guard's failure
16298
- * mode is a forced replacement of a live table, and AWS itself still rejects
16299
- * a genuinely-bad `UpdateTable` loudly.
16300
- */
16301
- function collectDesiredKeyAttributeNames(properties) {
16461
+ * Malformed / intrinsic-valued entries contribute nothing. For the `update()`
16462
+ * guard that errs on the PERMISSIVE side — correct there, because that guard's
16463
+ * failure mode is a forced replacement of a live table, and AWS still rejects a
16464
+ * genuinely-bad `UpdateTable` loudly. For the create-path prune below it errs
16465
+ * the other way (an unreadable LSI key schema prunes MORE), which is only
16466
+ * reachable on a bag AWS would reject anyway.
16467
+ *
16468
+ * `options.indexListKeys` narrows which index lists contribute, for the ONE
16469
+ * caller whose question is not "what does the template reference" but "what
16470
+ * will this call actually key on" — `create()`'s replay-CREATE GSI omit arm
16471
+ * (issue #1741), which sends NO indexes and therefore must not count the
16472
+ * omitted block's key attributes as still-referenced. It defaults to BOTH
16473
+ * lists, so the `update()` guard above reads exactly as it did before.
16474
+ */
16475
+ function collectDesiredKeyAttributeNames(properties, options) {
16302
16476
  const names = /* @__PURE__ */ new Set();
16303
16477
  const addKeySchema = (value) => {
16304
16478
  if (!Array.isArray(value)) return;
@@ -16308,7 +16482,8 @@ function collectDesiredKeyAttributeNames(properties) {
16308
16482
  }
16309
16483
  };
16310
16484
  addKeySchema(properties["KeySchema"]);
16311
- for (const indexListKey of ["GlobalSecondaryIndexes", "LocalSecondaryIndexes"]) {
16485
+ const indexListKeys = options?.indexListKeys ?? ["GlobalSecondaryIndexes", "LocalSecondaryIndexes"];
16486
+ for (const indexListKey of indexListKeys) {
16312
16487
  const indexes = properties[indexListKey];
16313
16488
  if (!Array.isArray(indexes)) continue;
16314
16489
  for (const index of indexes) addKeySchema(asRecord$1(index)?.["KeySchema"]);
@@ -74299,7 +74474,7 @@ function createMigrateCommand() {
74299
74474
  */
74300
74475
  function buildProgram() {
74301
74476
  const program = new Command();
74302
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.281.19");
74477
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.281.21");
74303
74478
  program.hook("preAction", (_thisCommand, actionCommand) => {
74304
74479
  const { profile } = actionCommand.optsWithGlobals();
74305
74480
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;