@go-to-k/cdkd 0.278.11 → 0.278.13

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-BpbIYUuG.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-QQOSO3LR.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";
@@ -4813,6 +4813,52 @@ function isTruthyCfnBoolean(value) {
4813
4813
  //#endregion
4814
4814
  //#region src/provisioning/providers/s3-bucket-provider.ts
4815
4815
  /**
4816
+ * A plain (non-array) object. `typeof x === 'object'` alone accepts arrays and
4817
+ * `null`; a `Transition: []` or an unresolved intrinsic pushed through as a
4818
+ * single transition entry makes S3 answer `MalformedXML` for the WHOLE
4819
+ * lifecycle configuration, so the legacy singular readers screen with this.
4820
+ */
4821
+ function isPlainObject$1(value) {
4822
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4823
+ }
4824
+ /**
4825
+ * Coerce a CFn numeric to a finite number. CloudFormation is stringly typed —
4826
+ * a hand-written or imported template (the audience the legacy lifecycle
4827
+ * branches exist for) can carry `"365"` where the schema says number.
4828
+ */
4829
+ function coerceCfnNumber(value) {
4830
+ if (typeof value !== "number" && typeof value !== "string") return void 0;
4831
+ if (value === "") return void 0;
4832
+ const n = Number(value);
4833
+ return Number.isFinite(n) ? n : void 0;
4834
+ }
4835
+ /**
4836
+ * Merge a legacy SINGULAR action object into its modern plural array.
4837
+ *
4838
+ * S3 rejects two transitions with the same `StorageClass` and fails the whole
4839
+ * `PutBucketLifecycleConfiguration`, so a blind concatenation is unsafe. But
4840
+ * dropping the singular wholesale loses a legitimate template: a plural
4841
+ * `[{GLACIER, 30}]` alongside a singular `{DEEP_ARCHIVE, 90}` is two different
4842
+ * classes, which S3 accepts. So: keep both, let the PLURAL win on a
4843
+ * StorageClass collision, and say so rather than dropping in silence.
4844
+ */
4845
+ function mergeLegacySingular(plural, singular, onCollision) {
4846
+ const out = (Array.isArray(plural) ? plural : []).filter(isPlainObject$1);
4847
+ if (!isPlainObject$1(singular)) return out;
4848
+ const sc = singular["StorageClass"];
4849
+ if (typeof sc === "string" && out.some((e) => e["StorageClass"] === sc)) {
4850
+ onCollision(sc);
4851
+ return out;
4852
+ }
4853
+ return [...out, singular];
4854
+ }
4855
+ /** Coerce a CFn boolean, which may arrive as the string `"true"` / `"false"`. */
4856
+ function coerceCfnBoolean(value) {
4857
+ if (typeof value === "boolean") return value;
4858
+ if (value === "true") return true;
4859
+ if (value === "false") return false;
4860
+ }
4861
+ /**
4816
4862
  * SDK Provider for AWS::S3::Bucket
4817
4863
  *
4818
4864
  * Uses S3 SDK directly instead of CC API for synchronous bucket creation.
@@ -4982,7 +5028,7 @@ var S3BucketProvider = class {
4982
5028
  const filter = rule["Filter"];
4983
5029
  return {
4984
5030
  prefix: filter?.["Prefix"] ?? rule["Prefix"],
4985
- tagFilters: filter?.["TagFilters"],
5031
+ tagFilters: filter?.["TagFilters"] ?? rule["TagFilters"],
4986
5032
  sizeGt: filter?.["ObjectSizeGreaterThan"] ?? rule["ObjectSizeGreaterThan"],
4987
5033
  sizeLt: filter?.["ObjectSizeLessThan"] ?? rule["ObjectSizeLessThan"]
4988
5034
  };
@@ -5010,24 +5056,37 @@ var S3BucketProvider = class {
5010
5056
  ExpiredObjectDeleteMarker: exp["ExpiredObjectDeleteMarker"]
5011
5057
  };
5012
5058
  }
5013
- const nve = rule["NoncurrentVersionExpiration"];
5059
+ if (coerceCfnBoolean(rule["ExpiredObjectDeleteMarker"]) === true) {
5060
+ const exp = sdkRule.Expiration;
5061
+ if (!(exp?.["Days"] !== void 0 || exp?.["Date"] !== void 0)) sdkRule.Expiration = { ExpiredObjectDeleteMarker: true };
5062
+ else this.logger.warn(`Lifecycle rule '${rule["Id"] ?? "<unnamed>"}' on ${bucketName} sets ExpiredObjectDeleteMarker alongside an expiration Days/Date; S3 forbids combining them, so the delete-marker cleanup was not applied.`);
5063
+ }
5064
+ const nve = isPlainObject$1(rule["NoncurrentVersionExpiration"]) ? rule["NoncurrentVersionExpiration"] : void 0;
5065
+ const legacyNveDays = rule["NoncurrentVersionExpirationInDays"];
5014
5066
  if (nve) sdkRule.NoncurrentVersionExpiration = {
5015
5067
  NoncurrentDays: nve["NoncurrentDays"],
5016
5068
  NewerNoncurrentVersions: nve["NewerNoncurrentVersions"]
5017
5069
  };
5018
- const nvts = rule["NoncurrentVersionTransitions"];
5019
- if (nvts && Array.isArray(nvts)) sdkRule.NoncurrentVersionTransitions = nvts.map((nvt) => ({
5020
- NoncurrentDays: nvt["NoncurrentDays"],
5070
+ else if (coerceCfnNumber(legacyNveDays) !== void 0) sdkRule.NoncurrentVersionExpiration = { NoncurrentDays: coerceCfnNumber(legacyNveDays) };
5071
+ const toSdkNvt = (nvt) => ({
5072
+ NoncurrentDays: nvt["TransitionInDays"] ?? nvt["NoncurrentDays"],
5021
5073
  StorageClass: nvt["StorageClass"],
5022
5074
  NewerNoncurrentVersions: nvt["NewerNoncurrentVersions"]
5023
- }));
5024
- const transitions = rule["Transitions"];
5025
- if (transitions && Array.isArray(transitions)) sdkRule.Transitions = transitions.map((t) => ({
5075
+ });
5076
+ const nvts = rule["NoncurrentVersionTransitions"];
5077
+ const singularNvt = rule["NoncurrentVersionTransition"];
5078
+ const allNvts = mergeLegacySingular(nvts, singularNvt, (sc) => this.logger.warn(`Lifecycle rule '${rule["Id"] ?? "<unnamed>"}' on ${bucketName} declares both NoncurrentVersionTransitions and the legacy NoncurrentVersionTransition for storage class ${sc}; S3 rejects duplicates, so the legacy singular was ignored.`));
5079
+ if (allNvts.length > 0) sdkRule.NoncurrentVersionTransitions = allNvts.map(toSdkNvt);
5080
+ const toSdkTransition = (t) => ({
5026
5081
  Days: t["TransitionInDays"] ?? t["Days"],
5027
5082
  Date: t["TransitionDate"] ?? t["Date"] ? new Date(t["TransitionDate"] ?? t["Date"]) : void 0,
5028
5083
  StorageClass: t["StorageClass"]
5029
- }));
5030
- const abort = rule["AbortIncompleteMultipartUpload"];
5084
+ });
5085
+ const transitions = rule["Transitions"];
5086
+ const singularTransition = rule["Transition"];
5087
+ const allTransitions = mergeLegacySingular(transitions, singularTransition, (sc) => this.logger.warn(`Lifecycle rule '${rule["Id"] ?? "<unnamed>"}' on ${bucketName} declares both Transitions and the legacy Transition for storage class ${sc}; S3 rejects duplicates, so the legacy singular was ignored.`));
5088
+ if (allTransitions.length > 0) sdkRule.Transitions = allTransitions.map(toSdkTransition);
5089
+ const abort = isPlainObject$1(rule["AbortIncompleteMultipartUpload"]) ? rule["AbortIncompleteMultipartUpload"] : void 0;
5031
5090
  if (abort) sdkRule.AbortIncompleteMultipartUpload = { DaysAfterInitiation: abort["DaysAfterInitiation"] };
5032
5091
  const { prefix, tagFilters, sizeGt, sizeLt } = gatherScope(rule);
5033
5092
  const hasTags = tagFilters !== void 0 && tagFilters.length > 0;
@@ -5037,6 +5096,7 @@ var S3BucketProvider = class {
5037
5096
  ObjectSizeGreaterThan: sizeGt,
5038
5097
  ObjectSizeLessThan: sizeLt
5039
5098
  } };
5099
+ else if (hasTags && tagFilters.length > 1) sdkRule.Filter = { And: { Tags: tagFilters } };
5040
5100
  else if (hasTags) sdkRule.Filter = { Tag: tagFilters[0] };
5041
5101
  else if (sizeGt !== void 0) sdkRule.Filter = { ObjectSizeGreaterThan: sizeGt };
5042
5102
  else if (sizeLt !== void 0) sdkRule.Filter = { ObjectSizeLessThan: sizeLt };
@@ -5872,7 +5932,7 @@ var S3BucketProvider = class {
5872
5932
  }
5873
5933
  if (r.NoncurrentVersionTransitions && r.NoncurrentVersionTransitions.length > 0) out["NoncurrentVersionTransitions"] = r.NoncurrentVersionTransitions.map((nvt) => {
5874
5934
  const item = {};
5875
- if (nvt.NoncurrentDays !== void 0) item["NoncurrentDays"] = nvt.NoncurrentDays;
5935
+ if (nvt.NoncurrentDays !== void 0) item["TransitionInDays"] = nvt.NoncurrentDays;
5876
5936
  if (nvt.StorageClass !== void 0) item["StorageClass"] = nvt.StorageClass;
5877
5937
  if (nvt.NewerNoncurrentVersions !== void 0) item["NewerNoncurrentVersions"] = nvt.NewerNoncurrentVersions;
5878
5938
  return item;
@@ -11948,10 +12008,15 @@ var DynamoDBTableProvider = class {
11948
12008
  * - `update()` covers every mutable surface — Tags, DeletionProtection,
11949
12009
  * TableClass, SSE, StreamSpec, OnDemand throughput, BillingMode flip,
11950
12010
  * 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,
12011
+ * - The serialization is load-bearing: AWS's `UpdateTable` does not accept
12012
+ * `ReplicaUpdates` alongside `BillingMode` / `GlobalSecondaryIndexUpdates`,
12013
+ * so each category is its own SDK round-trip with a wait-for-ACTIVE in
12014
+ * between. `BillingMode` and `GlobalSecondaryIndexUpdates` DO combine, and
12015
+ * only in the `PAY_PER_REQUEST -> PROVISIONED` flip, where AWS REQUIRES
12016
+ * per-GSI `ProvisionedThroughput` in the SAME call as `BillingMode` ("you
12017
+ * must specify read and write capacity unit values for the table and for
12018
+ * each global secondary index") — so that path deliberately sends both
12019
+ * together (Issue #1387). Immutable property changes (TableName, KeySchema,
11955
12020
  * AttributeDefinitions removal, LocalSecondaryIndexes) throw
11956
12021
  * `ProvisioningError` naming the offending field — the deploy engine's
11957
12022
  * diff classification should catch these as REPLACEMENT before ever
@@ -12146,7 +12211,13 @@ var DynamoDBGlobalTableProvider = class {
12146
12211
  StreamViewType: "NEW_AND_OLD_IMAGES"
12147
12212
  };
12148
12213
  }
12149
- if (properties["GlobalSecondaryIndexes"]) createParams.GlobalSecondaryIndexes = properties["GlobalSecondaryIndexes"];
12214
+ let sdkIndexes;
12215
+ try {
12216
+ sdkIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, billingMode);
12217
+ } catch (error) {
12218
+ throw new ProvisioningError(error instanceof Error ? error.message : String(error), resourceType, logicalId, void 0, error instanceof Error ? error : void 0);
12219
+ }
12220
+ if (sdkIndexes.length > 0) createParams.GlobalSecondaryIndexes = sdkIndexes;
12150
12221
  if (properties["LocalSecondaryIndexes"]) createParams.LocalSecondaryIndexes = properties["LocalSecondaryIndexes"];
12151
12222
  if (properties["SSESpecification"]) {
12152
12223
  const sse = properties["SSESpecification"];
@@ -12237,11 +12308,26 @@ var DynamoDBGlobalTableProvider = class {
12237
12308
  * `DescribeTable` until the replica's `ReplicaStatus` flips to ACTIVE.
12238
12309
  * Capped at 10 minutes per replica (AWS Replica provisioning typically
12239
12310
  * takes 1–5 min).
12311
+ *
12312
+ * DELIBERATELY UNMAPPED replica sub-keys (Issue #1387 — recorded rather
12313
+ * than silently dropped; `CreateReplicationGroupMemberAction` simply has
12314
+ * no member for them, so they need their own follow-up API calls):
12315
+ * - `ReplicaStreamSpecification.ResourcePolicy` — the DynamoDB Streams
12316
+ * resource policy is set by `PutResourcePolicy` against the replica's
12317
+ * STREAM ARN, not by any `UpdateTable` field.
12318
+ * - `ResourcePolicy` — the table-level resource policy, likewise a
12319
+ * separate `PutResourcePolicy` call.
12320
+ * - `GlobalSecondaryIndexes[].ContributorInsightsSpecification` — a
12321
+ * per-index `UpdateContributorInsights` call.
12322
+ * `Replicas` is declared in `handledProperties` at the TOP level, which
12323
+ * is the granularity cdkd's pre-flight property-coverage check works at;
12324
+ * these nested keys are tracked as a known gap, not a supported surface.
12240
12325
  */
12241
12326
  async addReplica(tableName, replica, region, logicalId) {
12242
12327
  const create = { RegionName: region };
12243
12328
  if (replica["KMSMasterKeyId"]) create.KMSMasterKeyId = replica["KMSMasterKeyId"];
12244
- if (replica["GlobalSecondaryIndexes"]) create.GlobalSecondaryIndexes = replica["GlobalSecondaryIndexes"];
12329
+ const replicaIndexes = toSdkReplicaGlobalSecondaryIndexes(replica["GlobalSecondaryIndexes"]);
12330
+ if (replicaIndexes) create.GlobalSecondaryIndexes = replicaIndexes;
12245
12331
  if (replica["TableClassOverride"]) create.TableClassOverride = replica["TableClassOverride"];
12246
12332
  const replicaUpdates = [{ Create: create }];
12247
12333
  await this.dynamoDBClient.send(new UpdateTableCommand({
@@ -12253,10 +12339,14 @@ var DynamoDBGlobalTableProvider = class {
12253
12339
  /**
12254
12340
  * Update a DynamoDB Global Table in place.
12255
12341
  *
12256
- * AWS-side state-machine constraint: `UpdateTable` accepts only ONE of
12257
- * `{BillingMode, ReplicaUpdates, GlobalSecondaryIndexUpdates}` per call,
12342
+ * AWS-side state-machine constraint: `UpdateTable` does not accept
12343
+ * `ReplicaUpdates` alongside `BillingMode` / `GlobalSecondaryIndexUpdates`,
12258
12344
  * so each category must serialize into its own SDK round-trip with a
12259
- * `waitForTableActiveAfterUpdate` between every step. Order:
12345
+ * `waitForTableActiveAfterUpdate` between every step. `BillingMode` and
12346
+ * `GlobalSecondaryIndexUpdates` DO combine, and only in step 4's
12347
+ * `PAY_PER_REQUEST -> PROVISIONED` flip, which AWS requires to carry
12348
+ * per-GSI `ProvisionedThroughput` in the same call (Issue #1387).
12349
+ * Order:
12260
12350
  * 1. Wait for current ACTIVE (defensive).
12261
12351
  * 2. Tags diff (TagResource / UntagResource — no wait needed).
12262
12352
  * 3. Non-conflicting flat fields (DeletionProtectionEnabled / TableClass
@@ -12335,12 +12425,41 @@ var DynamoDBGlobalTableProvider = class {
12335
12425
  }
12336
12426
  const oldBilling = previousProperties["BillingMode"] ?? "PAY_PER_REQUEST";
12337
12427
  const newBilling = properties["BillingMode"] ?? "PAY_PER_REQUEST";
12428
+ const gsiHandledByBillingFlip = /* @__PURE__ */ new Set();
12338
12429
  if (oldBilling !== newBilling) {
12339
12430
  const billingUpdate = {
12340
12431
  TableName: physicalId,
12341
12432
  BillingMode: newBilling
12342
12433
  };
12343
- if (newBilling === "PROVISIONED") billingUpdate.ProvisionedThroughput = derivePerCallProvisionedThroughput(properties, currentRegion);
12434
+ if (newBilling === "PROVISIONED") {
12435
+ billingUpdate.ProvisionedThroughput = derivePerCallProvisionedThroughput(properties, currentRegion);
12436
+ const provisionedIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling);
12437
+ const previousCfnIndexes = previousProperties["GlobalSecondaryIndexes"];
12438
+ const existingIndexNames = new Set((Array.isArray(previousCfnIndexes) ? previousCfnIndexes : []).map((entry) => entry?.["IndexName"]).filter((name) => typeof name === "string"));
12439
+ const previousByIndexName = /* @__PURE__ */ new Map();
12440
+ for (const gsi of toSdkGlobalSecondaryIndexes(previousProperties, currentRegion, newBilling)) if (gsi.IndexName) previousByIndexName.set(gsi.IndexName, gsi);
12441
+ const byIndexName = new Map(previousByIndexName);
12442
+ const survivingIndexNames = /* @__PURE__ */ new Set();
12443
+ for (const gsi of provisionedIndexes) {
12444
+ if (!gsi.IndexName) continue;
12445
+ survivingIndexNames.add(gsi.IndexName);
12446
+ byIndexName.set(gsi.IndexName, gsi);
12447
+ }
12448
+ const indexUpdates = [];
12449
+ for (const indexName of existingIndexNames) {
12450
+ const gsi = byIndexName.get(indexName);
12451
+ if (!gsi?.ProvisionedThroughput) continue;
12452
+ const previousWarm = previousByIndexName.get(indexName)?.WarmThroughput;
12453
+ const warmChanged = gsi.WarmThroughput !== void 0 && !deepEqual$1(gsi.WarmThroughput, previousWarm);
12454
+ indexUpdates.push({ Update: {
12455
+ IndexName: indexName,
12456
+ ProvisionedThroughput: gsi.ProvisionedThroughput,
12457
+ ...warmChanged && { WarmThroughput: gsi.WarmThroughput }
12458
+ } });
12459
+ if (survivingIndexNames.has(indexName)) gsiHandledByBillingFlip.add(indexName);
12460
+ }
12461
+ if (indexUpdates.length > 0) billingUpdate.GlobalSecondaryIndexUpdates = indexUpdates;
12462
+ }
12344
12463
  await this.dynamoDBClient.send(new UpdateTableCommand(billingUpdate));
12345
12464
  await this.waitForTableActiveAfterUpdate(physicalId, logicalId);
12346
12465
  }
@@ -12391,7 +12510,8 @@ var DynamoDBGlobalTableProvider = class {
12391
12510
  }
12392
12511
  const updateAction = { RegionName: region };
12393
12512
  if (replica["KMSMasterKeyId"] !== void 0) updateAction.KMSMasterKeyId = replica["KMSMasterKeyId"];
12394
- if (replica["GlobalSecondaryIndexes"]) updateAction.GlobalSecondaryIndexes = replica["GlobalSecondaryIndexes"];
12513
+ const replicaIndexes = toSdkReplicaGlobalSecondaryIndexes(replica["GlobalSecondaryIndexes"]);
12514
+ if (replicaIndexes) updateAction.GlobalSecondaryIndexes = replicaIndexes;
12395
12515
  if (replica["TableClassOverride"]) updateAction.TableClassOverride = replica["TableClassOverride"];
12396
12516
  if (!(updateAction.KMSMasterKeyId !== void 0 || updateAction.GlobalSecondaryIndexes !== void 0 || updateAction.TableClassOverride !== void 0)) {
12397
12517
  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 +12523,10 @@ var DynamoDBGlobalTableProvider = class {
12403
12523
  }));
12404
12524
  await this.waitForReplicaActive(physicalId, region, logicalId);
12405
12525
  }
12406
- const gsiDiff = diffGlobalSecondaryIndexes(previousProperties["GlobalSecondaryIndexes"] ?? [], properties["GlobalSecondaryIndexes"] ?? []);
12526
+ const previousSdkIndexes = toSdkGlobalSecondaryIndexes(previousProperties, currentRegion, oldBilling);
12527
+ const gsiDiff = diffGlobalSecondaryIndexes(previousSdkIndexes, toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling));
12528
+ const previousSdkByName = /* @__PURE__ */ new Map();
12529
+ for (const prev of previousSdkIndexes) if (prev.IndexName) previousSdkByName.set(prev.IndexName, prev);
12407
12530
  for (const gsi of gsiDiff.removed) {
12408
12531
  if (!gsi.IndexName) continue;
12409
12532
  const gsiUpdate = { Delete: { IndexName: gsi.IndexName } };
@@ -12420,7 +12543,8 @@ var DynamoDBGlobalTableProvider = class {
12420
12543
  KeySchema: gsi.KeySchema,
12421
12544
  Projection: gsi.Projection,
12422
12545
  ...gsi.ProvisionedThroughput && { ProvisionedThroughput: gsi.ProvisionedThroughput },
12423
- ...gsi.OnDemandThroughput && { OnDemandThroughput: gsi.OnDemandThroughput }
12546
+ ...gsi.OnDemandThroughput && { OnDemandThroughput: gsi.OnDemandThroughput },
12547
+ ...gsi.WarmThroughput && { WarmThroughput: gsi.WarmThroughput }
12424
12548
  } };
12425
12549
  await this.dynamoDBClient.send(new UpdateTableCommand({
12426
12550
  TableName: physicalId,
@@ -12431,11 +12555,18 @@ var DynamoDBGlobalTableProvider = class {
12431
12555
  }
12432
12556
  for (const gsi of gsiDiff.modified) {
12433
12557
  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
- } };
12558
+ if (gsiHandledByBillingFlip.has(gsi.IndexName)) continue;
12559
+ const billingFlipped = oldBilling !== newBilling;
12560
+ const update = { IndexName: gsi.IndexName };
12561
+ if (gsi.OnDemandThroughput) update.OnDemandThroughput = gsi.OnDemandThroughput;
12562
+ if (!billingFlipped && gsi.ProvisionedThroughput) update.ProvisionedThroughput = gsi.ProvisionedThroughput;
12563
+ const previousSdk = previousSdkByName.get(gsi.IndexName);
12564
+ if (gsi.WarmThroughput !== void 0 && !deepEqual$1(gsi.WarmThroughput, previousSdk?.WarmThroughput)) update.WarmThroughput = gsi.WarmThroughput;
12565
+ if (!update.ProvisionedThroughput && !update.OnDemandThroughput && !update.WarmThroughput) {
12566
+ 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.`);
12567
+ continue;
12568
+ }
12569
+ const gsiUpdate = { Update: update };
12439
12570
  await this.dynamoDBClient.send(new UpdateTableCommand({
12440
12571
  TableName: physicalId,
12441
12572
  GlobalSecondaryIndexUpdates: [gsiUpdate]
@@ -13200,11 +13331,16 @@ var DynamoDBGlobalTableProvider = class {
13200
13331
  *
13201
13332
  * Source of truth (CFn `AWS::DynamoDB::GlobalTable` shape):
13202
13333
  * - WriteCapacityUnits → `properties.WriteProvisionedThroughputSettings`
13203
- * (top-level on the table). Literal `WriteCapacityUnits` wins over
13204
- * auto-scaling `MinCapacity`; both default to 5 if absent.
13334
+ * (top-level on the table). That block's ONLY member is
13335
+ * `WriteCapacityAutoScalingSettings` write capacity on a GlobalTable is
13336
+ * always auto-scaled, so there is no literal `WriteCapacityUnits` to
13337
+ * prefer. `CreateTable` needs one concrete number, so the auto-scaling
13338
+ * block's `SeedCapacity` (documented as the value used until the scaling
13339
+ * policies exist) is taken before `MinCapacity`; 5 if the block is absent.
13205
13340
  * - ReadCapacityUnits → `Replicas[?Region==<region>].ReadProvisionedThroughputSettings`
13206
- * (per-replica, the deploy region's setting). Same literal-vs-auto-
13207
- * scaling-vs-default-5 precedence.
13341
+ * (per-replica, the deploy region's setting). That block DOES carry a
13342
+ * literal `ReadCapacityUnits`, which is taken before its auto-scaling
13343
+ * `SeedCapacity` / `MinCapacity`; 5 if absent.
13208
13344
  */
13209
13345
  /**
13210
13346
  * Extract the local replica's `DeletionProtectionEnabled` from a CFn
@@ -13227,16 +13363,190 @@ function extractLocalDeletionProtection(props, region) {
13227
13363
  return typeof topLevel === "boolean" ? topLevel : void 0;
13228
13364
  }
13229
13365
  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
13366
  return {
13236
- ReadCapacityUnits: Number(localReadSettings?.["ReadCapacityUnits"] ?? readAutoScaling?.["MinCapacity"] ?? 5),
13237
- WriteCapacityUnits: writeCapacity
13367
+ ReadCapacityUnits: deriveReadCapacityUnits(asRecord((properties["Replicas"] ?? []).find((r) => r["Region"] === region)?.["ReadProvisionedThroughputSettings"])) ?? DEFAULT_CAPACITY_UNITS,
13368
+ WriteCapacityUnits: deriveWriteCapacityUnits(asRecord(properties["WriteProvisionedThroughputSettings"])) ?? DEFAULT_CAPACITY_UNITS
13238
13369
  };
13239
13370
  }
13371
+ /**
13372
+ * Capacity units applied when a PROVISIONED template carries no usable
13373
+ * capacity value at all. Matches the pre-existing table-level default.
13374
+ */
13375
+ const DEFAULT_CAPACITY_UNITS = 5;
13376
+ /** Narrow an unknown CFn sub-object to a record (or `undefined`). */
13377
+ function asRecord(value) {
13378
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
13379
+ }
13380
+ /** Coerce a CFn numeric (CFn is stringly-typed) to a finite number. */
13381
+ function toFiniteNumber(value) {
13382
+ if (value === void 0 || value === null || value === "") return void 0;
13383
+ const n = Number(value);
13384
+ return Number.isFinite(n) ? n : void 0;
13385
+ }
13386
+ /**
13387
+ * Derive a single WRITE capacity number from a CFn
13388
+ * `WriteProvisionedThroughputSettings` block.
13389
+ *
13390
+ * The `AWS::DynamoDB::GlobalTable` registry schema (verified 2026-08-09 via
13391
+ * `aws cloudformation describe-type`) declares this block with exactly ONE
13392
+ * member — `WriteCapacityAutoScalingSettings` — i.e. write capacity on a
13393
+ * GlobalTable is ALWAYS auto-scaled; there is no literal `WriteCapacityUnits`
13394
+ * (that is why CDK 2.244's `TableV2` rejects `Capacity.fixed()` for write).
13395
+ * `CreateTable` / `UpdateTable` still require a concrete starting number, so
13396
+ * we take `SeedCapacity` ("the initial provisioned capacity units") when the
13397
+ * template supplies it, else `MinCapacity`.
13398
+ *
13399
+ * A literal `WriteCapacityUnits` is still honored first: the CFn schema
13400
+ * forbids it, but hand-authored / imported templates and cdkd state written
13401
+ * before this helper existed can carry it, and honoring it is strictly more
13402
+ * faithful than ignoring it.
13403
+ */
13404
+ function deriveWriteCapacityUnits(settings) {
13405
+ if (!settings) return void 0;
13406
+ const literal = toFiniteNumber(settings["WriteCapacityUnits"]);
13407
+ if (literal !== void 0) return literal;
13408
+ const autoScaling = asRecord(settings["WriteCapacityAutoScalingSettings"]);
13409
+ if (!autoScaling) return void 0;
13410
+ return toFiniteNumber(autoScaling["SeedCapacity"]) ?? toFiniteNumber(autoScaling["MinCapacity"]);
13411
+ }
13412
+ /**
13413
+ * Derive a single READ capacity number from a CFn
13414
+ * `ReadProvisionedThroughputSettings` block (both the per-replica shape,
13415
+ * which carries `ReadCapacityUnits` + `ReadCapacityAutoScalingSettings`, and
13416
+ * the GSI/table-level `GlobalReadProvisionedThroughputSettings` shape, which
13417
+ * carries only `ReadCapacityUnits`). Same Seed-before-Min precedence as the
13418
+ * write side.
13419
+ */
13420
+ function deriveReadCapacityUnits(settings) {
13421
+ if (!settings) return void 0;
13422
+ const literal = toFiniteNumber(settings["ReadCapacityUnits"]);
13423
+ if (literal !== void 0) return literal;
13424
+ const autoScaling = asRecord(settings["ReadCapacityAutoScalingSettings"]);
13425
+ if (!autoScaling) return void 0;
13426
+ return toFiniteNumber(autoScaling["SeedCapacity"]) ?? toFiniteNumber(autoScaling["MinCapacity"]);
13427
+ }
13428
+ /**
13429
+ * Translate the CFn `AWS::DynamoDB::GlobalTable` `GlobalSecondaryIndexes[]`
13430
+ * blob into the SDK's `GlobalSecondaryIndex[]` shape (Issue #1387).
13431
+ *
13432
+ * The two schemas model per-GSI throughput COMPLETELY differently, and the
13433
+ * AWS SDK v3 serializer silently drops unknown members — so the pre-fix raw
13434
+ * cast meant a PROVISIONED GlobalTable with a GSI failed `CreateTable`
13435
+ * outright (AWS requires `ProvisionedThroughput` on every GSI), and every
13436
+ * `TableV2` per-GSI on-demand limit vanished without a trace.
13437
+ *
13438
+ * Verified mapping (CFn registry schema `AWS::DynamoDB::GlobalTable` +
13439
+ * `@aws-sdk/client-dynamodb` `models_0.d.ts`, both read 2026-08-09; the CFn
13440
+ * side additionally confirmed against a real `cdk synth` of `TableV2` with a
13441
+ * GSI under both billing modes):
13442
+ *
13443
+ * | CFn | SDK |
13444
+ * | ---------------------------------------------------------------------- | --------------------------------------- |
13445
+ * | `GSI.WriteProvisionedThroughputSettings.WriteCapacityAutoScalingSettings` | `ProvisionedThroughput.WriteCapacityUnits` |
13446
+ * | `Replicas[local].GlobalSecondaryIndexes[].ReadProvisionedThroughputSettings.ReadCapacityUnits` | `ProvisionedThroughput.ReadCapacityUnits` |
13447
+ * | `GSI.WriteOnDemandThroughputSettings.MaxWriteRequestUnits` | `OnDemandThroughput.MaxWriteRequestUnits` |
13448
+ * | `Replicas[local].GlobalSecondaryIndexes[].ReadOnDemandThroughputSettings.MaxReadRequestUnits` | `OnDemandThroughput.MaxReadRequestUnits` |
13449
+ * | `GSI.WarmThroughput` | `WarmThroughput` (same spelling) |
13450
+ *
13451
+ * READ capacity really does live on the REPLICA's GSI entry, not on the
13452
+ * top-level GSI — CDK's `globalSecondaryIndexes[].readCapacity` synthesizes
13453
+ * to `Replicas[?Region==<deploy region>].GlobalSecondaryIndexes[].
13454
+ * ReadProvisionedThroughputSettings`. `CreateTable` only ever provisions the
13455
+ * LOCAL replica, so the local entry is the right source. The GSI-level
13456
+ * `ReadProvisionedThroughputSettings` / `ReadOnDemandThroughputSettings`
13457
+ * (which the schema also permits) are honored as a fallback for
13458
+ * hand-authored templates.
13459
+ *
13460
+ * Already-SDK-shaped `ProvisionedThroughput` / `OnDemandThroughput` members
13461
+ * win over the derivation: the CFn schema forbids them, but cdkd state
13462
+ * written before this fix (and hand-authored templates) can carry them, and
13463
+ * silently re-deriving over an explicit value would be a regression.
13464
+ */
13465
+ function toSdkGlobalSecondaryIndexes(properties, region, billingMode) {
13466
+ const rawIndexes = properties["GlobalSecondaryIndexes"];
13467
+ 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.`);
13468
+ const cfnIndexes = rawIndexes ?? [];
13469
+ if (cfnIndexes.length === 0) return [];
13470
+ const replicas = properties["Replicas"] ?? [];
13471
+ const localReplicaIndexes = (Array.isArray(replicas) ? replicas.find((r) => asRecord(r)?.["Region"] === region) : void 0)?.["GlobalSecondaryIndexes"] ?? [];
13472
+ const localByName = /* @__PURE__ */ new Map();
13473
+ if (Array.isArray(localReplicaIndexes)) for (const entry of localReplicaIndexes) {
13474
+ const record = asRecord(entry);
13475
+ const name = record?.["IndexName"];
13476
+ if (record && typeof name === "string") localByName.set(name, record);
13477
+ }
13478
+ const result = [];
13479
+ for (const entry of cfnIndexes) {
13480
+ const gsi = asRecord(entry);
13481
+ if (!gsi) {
13482
+ result.push(entry);
13483
+ continue;
13484
+ }
13485
+ const indexName = gsi["IndexName"];
13486
+ const localEntry = typeof indexName === "string" ? localByName.get(indexName) : void 0;
13487
+ const sdk = {
13488
+ IndexName: indexName,
13489
+ KeySchema: gsi["KeySchema"],
13490
+ Projection: gsi["Projection"]
13491
+ };
13492
+ if (gsi["WarmThroughput"] !== void 0) sdk.WarmThroughput = gsi["WarmThroughput"];
13493
+ const explicitProvisioned = asRecord(gsi["ProvisionedThroughput"]);
13494
+ const explicitOnDemand = asRecord(gsi["OnDemandThroughput"]);
13495
+ if (explicitProvisioned) sdk.ProvisionedThroughput = explicitProvisioned;
13496
+ else if (billingMode === "PROVISIONED") sdk.ProvisionedThroughput = {
13497
+ ReadCapacityUnits: deriveReadCapacityUnits(asRecord(localEntry?.["ReadProvisionedThroughputSettings"])) ?? deriveReadCapacityUnits(asRecord(gsi["ReadProvisionedThroughputSettings"])) ?? DEFAULT_CAPACITY_UNITS,
13498
+ WriteCapacityUnits: deriveWriteCapacityUnits(asRecord(gsi["WriteProvisionedThroughputSettings"])) ?? DEFAULT_CAPACITY_UNITS
13499
+ };
13500
+ if (explicitOnDemand) sdk.OnDemandThroughput = explicitOnDemand;
13501
+ else if (billingMode !== "PROVISIONED") {
13502
+ const maxWrite = toFiniteNumber(asRecord(gsi["WriteOnDemandThroughputSettings"])?.["MaxWriteRequestUnits"]);
13503
+ const maxRead = toFiniteNumber(asRecord(localEntry?.["ReadOnDemandThroughputSettings"])?.["MaxReadRequestUnits"]) ?? toFiniteNumber(asRecord(gsi["ReadOnDemandThroughputSettings"])?.["MaxReadRequestUnits"]);
13504
+ if (maxWrite !== void 0 || maxRead !== void 0) sdk.OnDemandThroughput = {
13505
+ ...maxRead !== void 0 && { MaxReadRequestUnits: maxRead },
13506
+ ...maxWrite !== void 0 && { MaxWriteRequestUnits: maxWrite }
13507
+ };
13508
+ }
13509
+ result.push(sdk);
13510
+ }
13511
+ return result;
13512
+ }
13513
+ /**
13514
+ * Translate a CFn `Replicas[].GlobalSecondaryIndexes[]` blob (the
13515
+ * `ReplicaGlobalSecondaryIndexSpecification` shape) into the SDK's
13516
+ * `ReplicaGlobalSecondaryIndex[]` used by `CreateReplicationGroupMemberAction`
13517
+ * / `UpdateReplicationGroupMemberAction` (Issue #1387).
13518
+ *
13519
+ * Verified mapping:
13520
+ *
13521
+ * | CFn | SDK |
13522
+ * | ---------------------------------------------------------- | ---------------------------------------------- |
13523
+ * | `ReadProvisionedThroughputSettings.ReadCapacityUnits` | `ProvisionedThroughputOverride.ReadCapacityUnits` |
13524
+ * | `ReadOnDemandThroughputSettings.MaxReadRequestUnits` | `OnDemandThroughputOverride.MaxReadRequestUnits` |
13525
+ *
13526
+ * DELIBERATELY UNMAPPED: `ContributorInsightsSpecification`. The replica-GSI
13527
+ * SDK shape has no member for it — per-index contributor insights are toggled
13528
+ * by a separate `UpdateContributorInsights(TableName, IndexName)` call, which
13529
+ * this provider does not issue for any index (it only reads the TABLE-level
13530
+ * status back in `readCurrentState`). Recorded here rather than silently
13531
+ * dropped; tracked with the other GlobalTable nested-key gaps.
13532
+ */
13533
+ function toSdkReplicaGlobalSecondaryIndexes(replicaIndexes) {
13534
+ if (!Array.isArray(replicaIndexes) || replicaIndexes.length === 0) return void 0;
13535
+ return replicaIndexes.map((entry) => {
13536
+ const cfn = asRecord(entry);
13537
+ if (!cfn) return entry;
13538
+ const sdk = { IndexName: cfn["IndexName"] };
13539
+ const explicitProvisioned = asRecord(cfn["ProvisionedThroughputOverride"]);
13540
+ const explicitOnDemand = asRecord(cfn["OnDemandThroughputOverride"]);
13541
+ const readCapacity = deriveReadCapacityUnits(asRecord(cfn["ReadProvisionedThroughputSettings"]));
13542
+ const maxReadRequestUnits = toFiniteNumber(asRecord(cfn["ReadOnDemandThroughputSettings"])?.["MaxReadRequestUnits"]);
13543
+ if (explicitProvisioned) sdk.ProvisionedThroughputOverride = explicitProvisioned;
13544
+ else if (readCapacity !== void 0) sdk.ProvisionedThroughputOverride = { ReadCapacityUnits: readCapacity };
13545
+ if (explicitOnDemand) sdk.OnDemandThroughputOverride = explicitOnDemand;
13546
+ else if (maxReadRequestUnits !== void 0) sdk.OnDemandThroughputOverride = { MaxReadRequestUnits: maxReadRequestUnits };
13547
+ return sdk;
13548
+ });
13549
+ }
13240
13550
  function diffReplicas(oldReplicas, newReplicas) {
13241
13551
  const oldByRegion = /* @__PURE__ */ new Map();
13242
13552
  const newByRegion = /* @__PURE__ */ new Map();
@@ -64596,7 +64906,7 @@ function createMigrateCommand() {
64596
64906
  */
64597
64907
  function buildProgram() {
64598
64908
  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");
64909
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.13");
64600
64910
  program.hook("preAction", (_thisCommand, actionCommand) => {
64601
64911
  const { profile } = actionCommand.optsWithGlobals();
64602
64912
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;