@go-to-k/cdkd 0.278.11 → 0.278.12

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 DagBuilder, A as red, At as resolveApp, B as isTerminationProtectionPropagationError, Bt as CFN_TEMPLATE_URL_LIMIT, C as isStatefulRecreateTargetSync, Ct as runDockerStreaming, D as cyan, Dt as synthesisStatusMessage, E as bold, Et as Synthesizer, F as ProviderRegistry, Ft as resolveStateBucketWithDefaultAndSource, G as normalizeAwsTagsToCfn, Gt as AssemblyReader, H as cfnRefValueFromPhysicalId, Ht as findLargeInlineResources, I as findActionableSilentDrops, It as resolveUseCdkBootstrapAssets, J as applyRoleArnIfSet, Jt as resolveBucketRegion, K as resolveExplicitPhysicalId, Kt as processStackMessages, L as CloudControlProvider, Lt as stateBucketExistenceConfirmed, M as IAMRoleProvider, Mt as resolveCaptureObservedState, N as collectInlinePolicyNamesManagedBySiblings, Nt as resolveSkipPrefix, O as gray, Ot as getDefaultStateBucketName, P as clearOnUpdateRemoval, Pt as resolveStateBucketWithDefault, Q as isRetryableTransientError, Qt as setAwsClients, R as slowCcOperationTimeoutMs, Rt as warnDeprecatedNoPrefixCliFlag, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, St as runDockerForeground, T as formatResourceLine, Tt as getDockerImageBySourceHash, U as refStateLookupFromResource, Ut as uploadCfnTemplate, V as IntrinsicFunctionResolver, Vt as MIGRATE_TMP_PREFIX, W as WAFv2WebACLProvider, Wt as expectedOwnerParam, X as describeTypeWithThrottleRetry, Xt as getAwsClients, Y as DiffCalculator, Yt as AwsClients, Z as withRetry, Zt as resetAwsClients, _ as createPreDeleteFinalSnapshot, _t as validateAssetBucketName, a as DeploymentEventsStore, an as LocalMigrateError, at as AssetPublisher, b as unsupportedFinalSnapshotError, bn as normalizeAwsError, bt as formatDockerLoginError, c as replayFailedOperations, cn as MissingCdkCliError, ct as buildAssetRedirectMap, d as IMPLICIT_DELETE_DEPENDENCIES, dn as ProvisioningError, dt as rewriteTemplateAssetReferences, en as CdkdError, et as TemplateParser, f as computeImplicitDeleteEdges, fn as ResourceTimeoutError, ft as AssetModeResolver, g as ccRoutedFinalSnapshotError, gt as parseBootstrapMarker, h as buildFinalSnapshotIdentifier, hn as StackTerminationProtectionError, ht as getBootstrapMarkerKey, i as DeploymentEventsReader, in as LocalInvokeBuildError$1, it as shouldRetainResource, j as yellow, jt as resolveAutoAssetStorage, k as green, kt as getLegacyStateBucketName, l as replayRollback, ln as NestedStackChildDirectDestroyError, lt as createAssetRedirectResolver, m as PRE_DELETE_SNAPSHOT_TYPES, mn as StackHasActiveImportsError, mt as ensureAssetStorage, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as S3StateBackend, o as planFailedOps, on as LocalStartServiceError, ot as stringifyValue, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as ResourceUpdateNotSupportedError, pt as BOOTSTRAP_MARKER_PREFIX, q as assertRegionMatch, r as DeployEngine, rn as DeployCancelledError, rt as rebuildClientForBucketRegion, s as planRollback, st as WorkGraph, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as LockManager, u as withResourceDeadline, un as PartialFailureError, ut as loadPublishableAssetManifest, v as isFinalSnapshotError, vt as validateContainerRepoName, w as renderStatefulReason, wt as AssetManifestLoader, x as extractDeploymentEventError, xn as withErrorHandling, xt as getDockerCmd, y as refusesFinalSnapshot, yt as buildDockerImage, z as disableInstanceApiTermination, zt as CFN_TEMPLATE_BODY_LIMIT } from "./deploy-engine-DrlCNItN.js";
2
+ import { $ as DagBuilder, A as red, At as resolveApp, B as isTerminationProtectionPropagationError, Bt as CFN_TEMPLATE_URL_LIMIT, C as isStatefulRecreateTargetSync, Ct as runDockerStreaming, D as cyan, Dt as synthesisStatusMessage, E as bold, Et as Synthesizer, F as ProviderRegistry, Ft as resolveStateBucketWithDefaultAndSource, G as normalizeAwsTagsToCfn, Gt as AssemblyReader, H as cfnRefValueFromPhysicalId, Ht as findLargeInlineResources, I as findActionableSilentDrops, It as resolveUseCdkBootstrapAssets, J as applyRoleArnIfSet, Jt as resolveBucketRegion, K as resolveExplicitPhysicalId, Kt as processStackMessages, L as CloudControlProvider, Lt as stateBucketExistenceConfirmed, M as IAMRoleProvider, Mt as resolveCaptureObservedState, N as collectInlinePolicyNamesManagedBySiblings, Nt as resolveSkipPrefix, O as gray, Ot as getDefaultStateBucketName, P as clearOnUpdateRemoval, Pt as resolveStateBucketWithDefault, Q as isRetryableTransientError, Qt as setAwsClients, R as slowCcOperationTimeoutMs, Rt as warnDeprecatedNoPrefixCliFlag, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, St as runDockerForeground, T as formatResourceLine, Tt as getDockerImageBySourceHash, U as refStateLookupFromResource, Ut as uploadCfnTemplate, V as IntrinsicFunctionResolver, Vt as MIGRATE_TMP_PREFIX, W as WAFv2WebACLProvider, Wt as expectedOwnerParam, X as describeTypeWithThrottleRetry, Xt as getAwsClients, Y as DiffCalculator, Yt as AwsClients, Z as withRetry, Zt as resetAwsClients, _ as createPreDeleteFinalSnapshot, _t as validateAssetBucketName, a as DeploymentEventsStore, an as LocalMigrateError, at as AssetPublisher, b as unsupportedFinalSnapshotError, bn as normalizeAwsError, bt as formatDockerLoginError, c as replayFailedOperations, cn as MissingCdkCliError, ct as buildAssetRedirectMap, d as IMPLICIT_DELETE_DEPENDENCIES, dn as ProvisioningError, dt as rewriteTemplateAssetReferences, en as CdkdError, et as TemplateParser, f as computeImplicitDeleteEdges, fn as ResourceTimeoutError, ft as AssetModeResolver, g as ccRoutedFinalSnapshotError, gt as parseBootstrapMarker, h as buildFinalSnapshotIdentifier, hn as StackTerminationProtectionError, ht as getBootstrapMarkerKey, i as DeploymentEventsReader, in as LocalInvokeBuildError$1, it as shouldRetainResource, j as yellow, jt as resolveAutoAssetStorage, k as green, kt as getLegacyStateBucketName, l as replayRollback, ln as NestedStackChildDirectDestroyError, lt as createAssetRedirectResolver, m as PRE_DELETE_SNAPSHOT_TYPES, mn as StackHasActiveImportsError, mt as ensureAssetStorage, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as S3StateBackend, o as planFailedOps, on as LocalStartServiceError, ot as stringifyValue, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as ResourceUpdateNotSupportedError, pt as BOOTSTRAP_MARKER_PREFIX, q as assertRegionMatch, r as DeployEngine, rn as DeployCancelledError, rt as rebuildClientForBucketRegion, s as planRollback, st as WorkGraph, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as LockManager, u as withResourceDeadline, un as PartialFailureError, ut as loadPublishableAssetManifest, v as isFinalSnapshotError, vt as validateContainerRepoName, w as renderStatefulReason, wt as AssetManifestLoader, x as extractDeploymentEventError, xn as withErrorHandling, xt as getDockerCmd, y as refusesFinalSnapshot, yt as buildDockerImage, z as disableInstanceApiTermination, zt as CFN_TEMPLATE_BODY_LIMIT } from "./deploy-engine-BKfqbjsz.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BYMEE-BS.js";
4
- import { t as ASGProvider } from "./asg-provider-DW0eC_5t.js";
4
+ import { t as ASGProvider } from "./asg-provider-BAnnBKci.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, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, 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";
@@ -11948,10 +11948,15 @@ var DynamoDBTableProvider = class {
11948
11948
  * - `update()` covers every mutable surface — Tags, DeletionProtection,
11949
11949
  * TableClass, SSE, StreamSpec, OnDemand throughput, BillingMode flip,
11950
11950
  * Replica add / remove / modify, GSI add / remove / modify, TTL toggle.
11951
- * - The serialization is load-bearing: AWS's `UpdateTable` accepts only
11952
- * ONE of `{BillingMode, ReplicaUpdates, GlobalSecondaryIndexUpdates}`
11953
- * per call, so each category is its own SDK round-trip with a wait-for
11954
- * -ACTIVE in between. Immutable property changes (TableName, KeySchema,
11951
+ * - The serialization is load-bearing: AWS's `UpdateTable` does not accept
11952
+ * `ReplicaUpdates` alongside `BillingMode` / `GlobalSecondaryIndexUpdates`,
11953
+ * so each category is its own SDK round-trip with a wait-for-ACTIVE in
11954
+ * between. `BillingMode` and `GlobalSecondaryIndexUpdates` DO combine, and
11955
+ * only in the `PAY_PER_REQUEST -> PROVISIONED` flip, where AWS REQUIRES
11956
+ * per-GSI `ProvisionedThroughput` in the SAME call as `BillingMode` ("you
11957
+ * must specify read and write capacity unit values for the table and for
11958
+ * each global secondary index") — so that path deliberately sends both
11959
+ * together (Issue #1387). Immutable property changes (TableName, KeySchema,
11955
11960
  * AttributeDefinitions removal, LocalSecondaryIndexes) throw
11956
11961
  * `ProvisioningError` naming the offending field — the deploy engine's
11957
11962
  * diff classification should catch these as REPLACEMENT before ever
@@ -12146,7 +12151,13 @@ var DynamoDBGlobalTableProvider = class {
12146
12151
  StreamViewType: "NEW_AND_OLD_IMAGES"
12147
12152
  };
12148
12153
  }
12149
- if (properties["GlobalSecondaryIndexes"]) createParams.GlobalSecondaryIndexes = properties["GlobalSecondaryIndexes"];
12154
+ let sdkIndexes;
12155
+ try {
12156
+ sdkIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, billingMode);
12157
+ } catch (error) {
12158
+ throw new ProvisioningError(error instanceof Error ? error.message : String(error), resourceType, logicalId, void 0, error instanceof Error ? error : void 0);
12159
+ }
12160
+ if (sdkIndexes.length > 0) createParams.GlobalSecondaryIndexes = sdkIndexes;
12150
12161
  if (properties["LocalSecondaryIndexes"]) createParams.LocalSecondaryIndexes = properties["LocalSecondaryIndexes"];
12151
12162
  if (properties["SSESpecification"]) {
12152
12163
  const sse = properties["SSESpecification"];
@@ -12237,11 +12248,26 @@ var DynamoDBGlobalTableProvider = class {
12237
12248
  * `DescribeTable` until the replica's `ReplicaStatus` flips to ACTIVE.
12238
12249
  * Capped at 10 minutes per replica (AWS Replica provisioning typically
12239
12250
  * takes 1–5 min).
12251
+ *
12252
+ * DELIBERATELY UNMAPPED replica sub-keys (Issue #1387 — recorded rather
12253
+ * than silently dropped; `CreateReplicationGroupMemberAction` simply has
12254
+ * no member for them, so they need their own follow-up API calls):
12255
+ * - `ReplicaStreamSpecification.ResourcePolicy` — the DynamoDB Streams
12256
+ * resource policy is set by `PutResourcePolicy` against the replica's
12257
+ * STREAM ARN, not by any `UpdateTable` field.
12258
+ * - `ResourcePolicy` — the table-level resource policy, likewise a
12259
+ * separate `PutResourcePolicy` call.
12260
+ * - `GlobalSecondaryIndexes[].ContributorInsightsSpecification` — a
12261
+ * per-index `UpdateContributorInsights` call.
12262
+ * `Replicas` is declared in `handledProperties` at the TOP level, which
12263
+ * is the granularity cdkd's pre-flight property-coverage check works at;
12264
+ * these nested keys are tracked as a known gap, not a supported surface.
12240
12265
  */
12241
12266
  async addReplica(tableName, replica, region, logicalId) {
12242
12267
  const create = { RegionName: region };
12243
12268
  if (replica["KMSMasterKeyId"]) create.KMSMasterKeyId = replica["KMSMasterKeyId"];
12244
- if (replica["GlobalSecondaryIndexes"]) create.GlobalSecondaryIndexes = replica["GlobalSecondaryIndexes"];
12269
+ const replicaIndexes = toSdkReplicaGlobalSecondaryIndexes(replica["GlobalSecondaryIndexes"]);
12270
+ if (replicaIndexes) create.GlobalSecondaryIndexes = replicaIndexes;
12245
12271
  if (replica["TableClassOverride"]) create.TableClassOverride = replica["TableClassOverride"];
12246
12272
  const replicaUpdates = [{ Create: create }];
12247
12273
  await this.dynamoDBClient.send(new UpdateTableCommand({
@@ -12253,10 +12279,14 @@ var DynamoDBGlobalTableProvider = class {
12253
12279
  /**
12254
12280
  * Update a DynamoDB Global Table in place.
12255
12281
  *
12256
- * AWS-side state-machine constraint: `UpdateTable` accepts only ONE of
12257
- * `{BillingMode, ReplicaUpdates, GlobalSecondaryIndexUpdates}` per call,
12282
+ * AWS-side state-machine constraint: `UpdateTable` does not accept
12283
+ * `ReplicaUpdates` alongside `BillingMode` / `GlobalSecondaryIndexUpdates`,
12258
12284
  * so each category must serialize into its own SDK round-trip with a
12259
- * `waitForTableActiveAfterUpdate` between every step. Order:
12285
+ * `waitForTableActiveAfterUpdate` between every step. `BillingMode` and
12286
+ * `GlobalSecondaryIndexUpdates` DO combine, and only in step 4's
12287
+ * `PAY_PER_REQUEST -> PROVISIONED` flip, which AWS requires to carry
12288
+ * per-GSI `ProvisionedThroughput` in the same call (Issue #1387).
12289
+ * Order:
12260
12290
  * 1. Wait for current ACTIVE (defensive).
12261
12291
  * 2. Tags diff (TagResource / UntagResource — no wait needed).
12262
12292
  * 3. Non-conflicting flat fields (DeletionProtectionEnabled / TableClass
@@ -12335,12 +12365,41 @@ var DynamoDBGlobalTableProvider = class {
12335
12365
  }
12336
12366
  const oldBilling = previousProperties["BillingMode"] ?? "PAY_PER_REQUEST";
12337
12367
  const newBilling = properties["BillingMode"] ?? "PAY_PER_REQUEST";
12368
+ const gsiHandledByBillingFlip = /* @__PURE__ */ new Set();
12338
12369
  if (oldBilling !== newBilling) {
12339
12370
  const billingUpdate = {
12340
12371
  TableName: physicalId,
12341
12372
  BillingMode: newBilling
12342
12373
  };
12343
- if (newBilling === "PROVISIONED") billingUpdate.ProvisionedThroughput = derivePerCallProvisionedThroughput(properties, currentRegion);
12374
+ if (newBilling === "PROVISIONED") {
12375
+ billingUpdate.ProvisionedThroughput = derivePerCallProvisionedThroughput(properties, currentRegion);
12376
+ const provisionedIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling);
12377
+ const previousCfnIndexes = previousProperties["GlobalSecondaryIndexes"];
12378
+ const existingIndexNames = new Set((Array.isArray(previousCfnIndexes) ? previousCfnIndexes : []).map((entry) => entry?.["IndexName"]).filter((name) => typeof name === "string"));
12379
+ const previousByIndexName = /* @__PURE__ */ new Map();
12380
+ for (const gsi of toSdkGlobalSecondaryIndexes(previousProperties, currentRegion, newBilling)) if (gsi.IndexName) previousByIndexName.set(gsi.IndexName, gsi);
12381
+ const byIndexName = new Map(previousByIndexName);
12382
+ const survivingIndexNames = /* @__PURE__ */ new Set();
12383
+ for (const gsi of provisionedIndexes) {
12384
+ if (!gsi.IndexName) continue;
12385
+ survivingIndexNames.add(gsi.IndexName);
12386
+ byIndexName.set(gsi.IndexName, gsi);
12387
+ }
12388
+ const indexUpdates = [];
12389
+ for (const indexName of existingIndexNames) {
12390
+ const gsi = byIndexName.get(indexName);
12391
+ if (!gsi?.ProvisionedThroughput) continue;
12392
+ const previousWarm = previousByIndexName.get(indexName)?.WarmThroughput;
12393
+ const warmChanged = gsi.WarmThroughput !== void 0 && !deepEqual$1(gsi.WarmThroughput, previousWarm);
12394
+ indexUpdates.push({ Update: {
12395
+ IndexName: indexName,
12396
+ ProvisionedThroughput: gsi.ProvisionedThroughput,
12397
+ ...warmChanged && { WarmThroughput: gsi.WarmThroughput }
12398
+ } });
12399
+ if (survivingIndexNames.has(indexName)) gsiHandledByBillingFlip.add(indexName);
12400
+ }
12401
+ if (indexUpdates.length > 0) billingUpdate.GlobalSecondaryIndexUpdates = indexUpdates;
12402
+ }
12344
12403
  await this.dynamoDBClient.send(new UpdateTableCommand(billingUpdate));
12345
12404
  await this.waitForTableActiveAfterUpdate(physicalId, logicalId);
12346
12405
  }
@@ -12391,7 +12450,8 @@ var DynamoDBGlobalTableProvider = class {
12391
12450
  }
12392
12451
  const updateAction = { RegionName: region };
12393
12452
  if (replica["KMSMasterKeyId"] !== void 0) updateAction.KMSMasterKeyId = replica["KMSMasterKeyId"];
12394
- if (replica["GlobalSecondaryIndexes"]) updateAction.GlobalSecondaryIndexes = replica["GlobalSecondaryIndexes"];
12453
+ const replicaIndexes = toSdkReplicaGlobalSecondaryIndexes(replica["GlobalSecondaryIndexes"]);
12454
+ if (replicaIndexes) updateAction.GlobalSecondaryIndexes = replicaIndexes;
12395
12455
  if (replica["TableClassOverride"]) updateAction.TableClassOverride = replica["TableClassOverride"];
12396
12456
  if (!(updateAction.KMSMasterKeyId !== void 0 || updateAction.GlobalSecondaryIndexes !== void 0 || updateAction.TableClassOverride !== void 0)) {
12397
12457
  this.logger.debug(`Cross-region replica ${region} of ${physicalId}: only Tags-style changes detected; UpdateReplica skipped (AWS rejects empty Update actions). Tags propagation handled above via per-region client.`);
@@ -12403,7 +12463,10 @@ var DynamoDBGlobalTableProvider = class {
12403
12463
  }));
12404
12464
  await this.waitForReplicaActive(physicalId, region, logicalId);
12405
12465
  }
12406
- const gsiDiff = diffGlobalSecondaryIndexes(previousProperties["GlobalSecondaryIndexes"] ?? [], properties["GlobalSecondaryIndexes"] ?? []);
12466
+ const previousSdkIndexes = toSdkGlobalSecondaryIndexes(previousProperties, currentRegion, oldBilling);
12467
+ const gsiDiff = diffGlobalSecondaryIndexes(previousSdkIndexes, toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling));
12468
+ const previousSdkByName = /* @__PURE__ */ new Map();
12469
+ for (const prev of previousSdkIndexes) if (prev.IndexName) previousSdkByName.set(prev.IndexName, prev);
12407
12470
  for (const gsi of gsiDiff.removed) {
12408
12471
  if (!gsi.IndexName) continue;
12409
12472
  const gsiUpdate = { Delete: { IndexName: gsi.IndexName } };
@@ -12420,7 +12483,8 @@ var DynamoDBGlobalTableProvider = class {
12420
12483
  KeySchema: gsi.KeySchema,
12421
12484
  Projection: gsi.Projection,
12422
12485
  ...gsi.ProvisionedThroughput && { ProvisionedThroughput: gsi.ProvisionedThroughput },
12423
- ...gsi.OnDemandThroughput && { OnDemandThroughput: gsi.OnDemandThroughput }
12486
+ ...gsi.OnDemandThroughput && { OnDemandThroughput: gsi.OnDemandThroughput },
12487
+ ...gsi.WarmThroughput && { WarmThroughput: gsi.WarmThroughput }
12424
12488
  } };
12425
12489
  await this.dynamoDBClient.send(new UpdateTableCommand({
12426
12490
  TableName: physicalId,
@@ -12431,11 +12495,18 @@ var DynamoDBGlobalTableProvider = class {
12431
12495
  }
12432
12496
  for (const gsi of gsiDiff.modified) {
12433
12497
  if (!gsi.IndexName) continue;
12434
- const gsiUpdate = { Update: {
12435
- IndexName: gsi.IndexName,
12436
- ...gsi.ProvisionedThroughput && { ProvisionedThroughput: gsi.ProvisionedThroughput },
12437
- ...gsi.OnDemandThroughput && { OnDemandThroughput: gsi.OnDemandThroughput }
12438
- } };
12498
+ if (gsiHandledByBillingFlip.has(gsi.IndexName)) continue;
12499
+ const billingFlipped = oldBilling !== newBilling;
12500
+ const update = { IndexName: gsi.IndexName };
12501
+ if (gsi.OnDemandThroughput) update.OnDemandThroughput = gsi.OnDemandThroughput;
12502
+ if (!billingFlipped && gsi.ProvisionedThroughput) update.ProvisionedThroughput = gsi.ProvisionedThroughput;
12503
+ const previousSdk = previousSdkByName.get(gsi.IndexName);
12504
+ if (gsi.WarmThroughput !== void 0 && !deepEqual$1(gsi.WarmThroughput, previousSdk?.WarmThroughput)) update.WarmThroughput = gsi.WarmThroughput;
12505
+ if (!update.ProvisionedThroughput && !update.OnDemandThroughput && !update.WarmThroughput) {
12506
+ if (!billingFlipped) this.logger.warn(`GSI '${gsi.IndexName}' on ${physicalId} changed in a way DynamoDB's UpdateTable cannot express (KeySchema / Projection are immutable on an existing index). Recreate the index under a new name to apply the change.`);
12507
+ continue;
12508
+ }
12509
+ const gsiUpdate = { Update: update };
12439
12510
  await this.dynamoDBClient.send(new UpdateTableCommand({
12440
12511
  TableName: physicalId,
12441
12512
  GlobalSecondaryIndexUpdates: [gsiUpdate]
@@ -13200,11 +13271,16 @@ var DynamoDBGlobalTableProvider = class {
13200
13271
  *
13201
13272
  * Source of truth (CFn `AWS::DynamoDB::GlobalTable` shape):
13202
13273
  * - WriteCapacityUnits → `properties.WriteProvisionedThroughputSettings`
13203
- * (top-level on the table). Literal `WriteCapacityUnits` wins over
13204
- * auto-scaling `MinCapacity`; both default to 5 if absent.
13274
+ * (top-level on the table). That block's ONLY member is
13275
+ * `WriteCapacityAutoScalingSettings` — write capacity on a GlobalTable is
13276
+ * always auto-scaled, so there is no literal `WriteCapacityUnits` to
13277
+ * prefer. `CreateTable` needs one concrete number, so the auto-scaling
13278
+ * block's `SeedCapacity` (documented as the value used until the scaling
13279
+ * policies exist) is taken before `MinCapacity`; 5 if the block is absent.
13205
13280
  * - ReadCapacityUnits → `Replicas[?Region==<region>].ReadProvisionedThroughputSettings`
13206
- * (per-replica, the deploy region's setting). Same literal-vs-auto-
13207
- * scaling-vs-default-5 precedence.
13281
+ * (per-replica, the deploy region's setting). That block DOES carry a
13282
+ * literal `ReadCapacityUnits`, which is taken before its auto-scaling
13283
+ * `SeedCapacity` / `MinCapacity`; 5 if absent.
13208
13284
  */
13209
13285
  /**
13210
13286
  * Extract the local replica's `DeletionProtectionEnabled` from a CFn
@@ -13227,16 +13303,190 @@ function extractLocalDeletionProtection(props, region) {
13227
13303
  return typeof topLevel === "boolean" ? topLevel : void 0;
13228
13304
  }
13229
13305
  function derivePerCallProvisionedThroughput(properties, region) {
13230
- const wps = properties["WriteProvisionedThroughputSettings"];
13231
- const writeAutoScaling = wps?.["WriteCapacityAutoScalingSettings"];
13232
- const writeCapacity = Number(wps?.["WriteCapacityUnits"] ?? writeAutoScaling?.["MinCapacity"] ?? 5);
13233
- const localReadSettings = (properties["Replicas"] ?? []).find((r) => r["Region"] === region)?.["ReadProvisionedThroughputSettings"];
13234
- const readAutoScaling = localReadSettings?.["ReadCapacityAutoScalingSettings"];
13235
13306
  return {
13236
- ReadCapacityUnits: Number(localReadSettings?.["ReadCapacityUnits"] ?? readAutoScaling?.["MinCapacity"] ?? 5),
13237
- WriteCapacityUnits: writeCapacity
13307
+ ReadCapacityUnits: deriveReadCapacityUnits(asRecord((properties["Replicas"] ?? []).find((r) => r["Region"] === region)?.["ReadProvisionedThroughputSettings"])) ?? DEFAULT_CAPACITY_UNITS,
13308
+ WriteCapacityUnits: deriveWriteCapacityUnits(asRecord(properties["WriteProvisionedThroughputSettings"])) ?? DEFAULT_CAPACITY_UNITS
13238
13309
  };
13239
13310
  }
13311
+ /**
13312
+ * Capacity units applied when a PROVISIONED template carries no usable
13313
+ * capacity value at all. Matches the pre-existing table-level default.
13314
+ */
13315
+ const DEFAULT_CAPACITY_UNITS = 5;
13316
+ /** Narrow an unknown CFn sub-object to a record (or `undefined`). */
13317
+ function asRecord(value) {
13318
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
13319
+ }
13320
+ /** Coerce a CFn numeric (CFn is stringly-typed) to a finite number. */
13321
+ function toFiniteNumber(value) {
13322
+ if (value === void 0 || value === null || value === "") return void 0;
13323
+ const n = Number(value);
13324
+ return Number.isFinite(n) ? n : void 0;
13325
+ }
13326
+ /**
13327
+ * Derive a single WRITE capacity number from a CFn
13328
+ * `WriteProvisionedThroughputSettings` block.
13329
+ *
13330
+ * The `AWS::DynamoDB::GlobalTable` registry schema (verified 2026-08-09 via
13331
+ * `aws cloudformation describe-type`) declares this block with exactly ONE
13332
+ * member — `WriteCapacityAutoScalingSettings` — i.e. write capacity on a
13333
+ * GlobalTable is ALWAYS auto-scaled; there is no literal `WriteCapacityUnits`
13334
+ * (that is why CDK 2.244's `TableV2` rejects `Capacity.fixed()` for write).
13335
+ * `CreateTable` / `UpdateTable` still require a concrete starting number, so
13336
+ * we take `SeedCapacity` ("the initial provisioned capacity units") when the
13337
+ * template supplies it, else `MinCapacity`.
13338
+ *
13339
+ * A literal `WriteCapacityUnits` is still honored first: the CFn schema
13340
+ * forbids it, but hand-authored / imported templates and cdkd state written
13341
+ * before this helper existed can carry it, and honoring it is strictly more
13342
+ * faithful than ignoring it.
13343
+ */
13344
+ function deriveWriteCapacityUnits(settings) {
13345
+ if (!settings) return void 0;
13346
+ const literal = toFiniteNumber(settings["WriteCapacityUnits"]);
13347
+ if (literal !== void 0) return literal;
13348
+ const autoScaling = asRecord(settings["WriteCapacityAutoScalingSettings"]);
13349
+ if (!autoScaling) return void 0;
13350
+ return toFiniteNumber(autoScaling["SeedCapacity"]) ?? toFiniteNumber(autoScaling["MinCapacity"]);
13351
+ }
13352
+ /**
13353
+ * Derive a single READ capacity number from a CFn
13354
+ * `ReadProvisionedThroughputSettings` block (both the per-replica shape,
13355
+ * which carries `ReadCapacityUnits` + `ReadCapacityAutoScalingSettings`, and
13356
+ * the GSI/table-level `GlobalReadProvisionedThroughputSettings` shape, which
13357
+ * carries only `ReadCapacityUnits`). Same Seed-before-Min precedence as the
13358
+ * write side.
13359
+ */
13360
+ function deriveReadCapacityUnits(settings) {
13361
+ if (!settings) return void 0;
13362
+ const literal = toFiniteNumber(settings["ReadCapacityUnits"]);
13363
+ if (literal !== void 0) return literal;
13364
+ const autoScaling = asRecord(settings["ReadCapacityAutoScalingSettings"]);
13365
+ if (!autoScaling) return void 0;
13366
+ return toFiniteNumber(autoScaling["SeedCapacity"]) ?? toFiniteNumber(autoScaling["MinCapacity"]);
13367
+ }
13368
+ /**
13369
+ * Translate the CFn `AWS::DynamoDB::GlobalTable` `GlobalSecondaryIndexes[]`
13370
+ * blob into the SDK's `GlobalSecondaryIndex[]` shape (Issue #1387).
13371
+ *
13372
+ * The two schemas model per-GSI throughput COMPLETELY differently, and the
13373
+ * AWS SDK v3 serializer silently drops unknown members — so the pre-fix raw
13374
+ * cast meant a PROVISIONED GlobalTable with a GSI failed `CreateTable`
13375
+ * outright (AWS requires `ProvisionedThroughput` on every GSI), and every
13376
+ * `TableV2` per-GSI on-demand limit vanished without a trace.
13377
+ *
13378
+ * Verified mapping (CFn registry schema `AWS::DynamoDB::GlobalTable` +
13379
+ * `@aws-sdk/client-dynamodb` `models_0.d.ts`, both read 2026-08-09; the CFn
13380
+ * side additionally confirmed against a real `cdk synth` of `TableV2` with a
13381
+ * GSI under both billing modes):
13382
+ *
13383
+ * | CFn | SDK |
13384
+ * | ---------------------------------------------------------------------- | --------------------------------------- |
13385
+ * | `GSI.WriteProvisionedThroughputSettings.WriteCapacityAutoScalingSettings` | `ProvisionedThroughput.WriteCapacityUnits` |
13386
+ * | `Replicas[local].GlobalSecondaryIndexes[].ReadProvisionedThroughputSettings.ReadCapacityUnits` | `ProvisionedThroughput.ReadCapacityUnits` |
13387
+ * | `GSI.WriteOnDemandThroughputSettings.MaxWriteRequestUnits` | `OnDemandThroughput.MaxWriteRequestUnits` |
13388
+ * | `Replicas[local].GlobalSecondaryIndexes[].ReadOnDemandThroughputSettings.MaxReadRequestUnits` | `OnDemandThroughput.MaxReadRequestUnits` |
13389
+ * | `GSI.WarmThroughput` | `WarmThroughput` (same spelling) |
13390
+ *
13391
+ * READ capacity really does live on the REPLICA's GSI entry, not on the
13392
+ * top-level GSI — CDK's `globalSecondaryIndexes[].readCapacity` synthesizes
13393
+ * to `Replicas[?Region==<deploy region>].GlobalSecondaryIndexes[].
13394
+ * ReadProvisionedThroughputSettings`. `CreateTable` only ever provisions the
13395
+ * LOCAL replica, so the local entry is the right source. The GSI-level
13396
+ * `ReadProvisionedThroughputSettings` / `ReadOnDemandThroughputSettings`
13397
+ * (which the schema also permits) are honored as a fallback for
13398
+ * hand-authored templates.
13399
+ *
13400
+ * Already-SDK-shaped `ProvisionedThroughput` / `OnDemandThroughput` members
13401
+ * win over the derivation: the CFn schema forbids them, but cdkd state
13402
+ * written before this fix (and hand-authored templates) can carry them, and
13403
+ * silently re-deriving over an explicit value would be a regression.
13404
+ */
13405
+ function toSdkGlobalSecondaryIndexes(properties, region, billingMode) {
13406
+ const rawIndexes = properties["GlobalSecondaryIndexes"];
13407
+ if (rawIndexes !== void 0 && !Array.isArray(rawIndexes)) throw new Error(`AWS::DynamoDB::GlobalTable GlobalSecondaryIndexes must be an array, got ${typeof rawIndexes} (${JSON.stringify(rawIndexes)?.slice(0, 200)}). An unresolved intrinsic here would otherwise deploy a table with no indexes.`);
13408
+ const cfnIndexes = rawIndexes ?? [];
13409
+ if (cfnIndexes.length === 0) return [];
13410
+ const replicas = properties["Replicas"] ?? [];
13411
+ const localReplicaIndexes = (Array.isArray(replicas) ? replicas.find((r) => asRecord(r)?.["Region"] === region) : void 0)?.["GlobalSecondaryIndexes"] ?? [];
13412
+ const localByName = /* @__PURE__ */ new Map();
13413
+ if (Array.isArray(localReplicaIndexes)) for (const entry of localReplicaIndexes) {
13414
+ const record = asRecord(entry);
13415
+ const name = record?.["IndexName"];
13416
+ if (record && typeof name === "string") localByName.set(name, record);
13417
+ }
13418
+ const result = [];
13419
+ for (const entry of cfnIndexes) {
13420
+ const gsi = asRecord(entry);
13421
+ if (!gsi) {
13422
+ result.push(entry);
13423
+ continue;
13424
+ }
13425
+ const indexName = gsi["IndexName"];
13426
+ const localEntry = typeof indexName === "string" ? localByName.get(indexName) : void 0;
13427
+ const sdk = {
13428
+ IndexName: indexName,
13429
+ KeySchema: gsi["KeySchema"],
13430
+ Projection: gsi["Projection"]
13431
+ };
13432
+ if (gsi["WarmThroughput"] !== void 0) sdk.WarmThroughput = gsi["WarmThroughput"];
13433
+ const explicitProvisioned = asRecord(gsi["ProvisionedThroughput"]);
13434
+ const explicitOnDemand = asRecord(gsi["OnDemandThroughput"]);
13435
+ if (explicitProvisioned) sdk.ProvisionedThroughput = explicitProvisioned;
13436
+ else if (billingMode === "PROVISIONED") sdk.ProvisionedThroughput = {
13437
+ ReadCapacityUnits: deriveReadCapacityUnits(asRecord(localEntry?.["ReadProvisionedThroughputSettings"])) ?? deriveReadCapacityUnits(asRecord(gsi["ReadProvisionedThroughputSettings"])) ?? DEFAULT_CAPACITY_UNITS,
13438
+ WriteCapacityUnits: deriveWriteCapacityUnits(asRecord(gsi["WriteProvisionedThroughputSettings"])) ?? DEFAULT_CAPACITY_UNITS
13439
+ };
13440
+ if (explicitOnDemand) sdk.OnDemandThroughput = explicitOnDemand;
13441
+ else if (billingMode !== "PROVISIONED") {
13442
+ const maxWrite = toFiniteNumber(asRecord(gsi["WriteOnDemandThroughputSettings"])?.["MaxWriteRequestUnits"]);
13443
+ const maxRead = toFiniteNumber(asRecord(localEntry?.["ReadOnDemandThroughputSettings"])?.["MaxReadRequestUnits"]) ?? toFiniteNumber(asRecord(gsi["ReadOnDemandThroughputSettings"])?.["MaxReadRequestUnits"]);
13444
+ if (maxWrite !== void 0 || maxRead !== void 0) sdk.OnDemandThroughput = {
13445
+ ...maxRead !== void 0 && { MaxReadRequestUnits: maxRead },
13446
+ ...maxWrite !== void 0 && { MaxWriteRequestUnits: maxWrite }
13447
+ };
13448
+ }
13449
+ result.push(sdk);
13450
+ }
13451
+ return result;
13452
+ }
13453
+ /**
13454
+ * Translate a CFn `Replicas[].GlobalSecondaryIndexes[]` blob (the
13455
+ * `ReplicaGlobalSecondaryIndexSpecification` shape) into the SDK's
13456
+ * `ReplicaGlobalSecondaryIndex[]` used by `CreateReplicationGroupMemberAction`
13457
+ * / `UpdateReplicationGroupMemberAction` (Issue #1387).
13458
+ *
13459
+ * Verified mapping:
13460
+ *
13461
+ * | CFn | SDK |
13462
+ * | ---------------------------------------------------------- | ---------------------------------------------- |
13463
+ * | `ReadProvisionedThroughputSettings.ReadCapacityUnits` | `ProvisionedThroughputOverride.ReadCapacityUnits` |
13464
+ * | `ReadOnDemandThroughputSettings.MaxReadRequestUnits` | `OnDemandThroughputOverride.MaxReadRequestUnits` |
13465
+ *
13466
+ * DELIBERATELY UNMAPPED: `ContributorInsightsSpecification`. The replica-GSI
13467
+ * SDK shape has no member for it — per-index contributor insights are toggled
13468
+ * by a separate `UpdateContributorInsights(TableName, IndexName)` call, which
13469
+ * this provider does not issue for any index (it only reads the TABLE-level
13470
+ * status back in `readCurrentState`). Recorded here rather than silently
13471
+ * dropped; tracked with the other GlobalTable nested-key gaps.
13472
+ */
13473
+ function toSdkReplicaGlobalSecondaryIndexes(replicaIndexes) {
13474
+ if (!Array.isArray(replicaIndexes) || replicaIndexes.length === 0) return void 0;
13475
+ return replicaIndexes.map((entry) => {
13476
+ const cfn = asRecord(entry);
13477
+ if (!cfn) return entry;
13478
+ const sdk = { IndexName: cfn["IndexName"] };
13479
+ const explicitProvisioned = asRecord(cfn["ProvisionedThroughputOverride"]);
13480
+ const explicitOnDemand = asRecord(cfn["OnDemandThroughputOverride"]);
13481
+ const readCapacity = deriveReadCapacityUnits(asRecord(cfn["ReadProvisionedThroughputSettings"]));
13482
+ const maxReadRequestUnits = toFiniteNumber(asRecord(cfn["ReadOnDemandThroughputSettings"])?.["MaxReadRequestUnits"]);
13483
+ if (explicitProvisioned) sdk.ProvisionedThroughputOverride = explicitProvisioned;
13484
+ else if (readCapacity !== void 0) sdk.ProvisionedThroughputOverride = { ReadCapacityUnits: readCapacity };
13485
+ if (explicitOnDemand) sdk.OnDemandThroughputOverride = explicitOnDemand;
13486
+ else if (maxReadRequestUnits !== void 0) sdk.OnDemandThroughputOverride = { MaxReadRequestUnits: maxReadRequestUnits };
13487
+ return sdk;
13488
+ });
13489
+ }
13240
13490
  function diffReplicas(oldReplicas, newReplicas) {
13241
13491
  const oldByRegion = /* @__PURE__ */ new Map();
13242
13492
  const newByRegion = /* @__PURE__ */ new Map();
@@ -64596,7 +64846,7 @@ function createMigrateCommand() {
64596
64846
  */
64597
64847
  function buildProgram() {
64598
64848
  const program = new Command();
64599
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.11");
64849
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.12");
64600
64850
  program.hook("preAction", (_thisCommand, actionCommand) => {
64601
64851
  const { profile } = actionCommand.optsWithGlobals();
64602
64852
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;