@go-to-k/cdkd 0.278.36 → 0.278.37

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 describeTypeWithThrottleRetry, $t as AwsClients, A as red, At as Synthesizer, B as isTerminationProtectionPropagationError, Bt as resolveUseCdkBootstrapAssets, C as isStatefulRecreateTargetSync, Ct as buildDockerImage, D as cyan, Dt as runDockerStreaming, E as bold, Et as runDockerForeground, F as ProviderRegistry, Ft as resolveAutoAssetStorage, G as normalizeAwsTagsToCfn, Gt as MIGRATE_TMP_PREFIX, H as cfnRefValueFromPhysicalId, Ht as warnDeprecatedNoPrefixCliFlag, I as findActionableSilentDrops, It as resolveCaptureObservedState, J as readConfigString, Jt as expectedOwnerParam, K as resolveExplicitPhysicalId, Kt as findLargeInlineResources, L as CloudControlProvider, Lt as resolveSkipPrefix, M as IAMRoleProvider, Mt as getDefaultStateBucketName, N as collectInlinePolicyNamesManagedBySiblings, Nt as getLegacyStateBucketName, O as gray, Ot as AssetManifestLoader, P as clearOnUpdateRemoval, Pt as resolveApp, Q as DiffCalculator, Qt as resolveBucketRegion, R as slowCcOperationTimeoutMs, Rt as resolveStateBucketWithDefault, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, St as validateContainerRepoName, T as formatResourceLine, Tn as withErrorHandling, Tt as getDockerCmd, U as refStateLookupFromResource, Ut as CFN_TEMPLATE_BODY_LIMIT, V as IntrinsicFunctionResolver, Vt as stateBucketExistenceConfirmed, W as WAFv2WebACLProvider, Wt as CFN_TEMPLATE_URL_LIMIT, X as requireConfigString, Xt as processStackMessages, Y as requireConfigArray, Yt as AssemblyReader, Z as applyRoleArnIfSet, _ as createPreDeleteFinalSnapshot, _n as ResourceUpdateNotSupportedError, _t as BOOTSTRAP_MARKER_PREFIX, a as DeploymentEventsStore, at as LockManager, b as unsupportedFinalSnapshotError, bt as parseBootstrapMarker, c as replayFailedOperations, cn as LocalInvokeBuildError$1, ct as shouldRetainResource, d as IMPLICIT_DELETE_DEPENDENCIES, dt as WorkGraph, en as getAwsClients, et as withRetry, f as computeImplicitDeleteEdges, fn as MissingCdkCliError, ft as buildAssetRedirectMap, g as ccRoutedFinalSnapshotError, gn as ResourceTimeoutError, gt as AssetModeResolver, h as buildFinalSnapshotIdentifier, hn as ProvisioningError, ht as rewriteTemplateAssetReferences, i as DeploymentEventsReader, in as CdkdError, it as TemplateParser, j as yellow, jt as synthesisStatusMessage, k as green, kt as getDockerImageBySourceHash, l as replayRollback, ln as LocalMigrateError, lt as AssetPublisher, m as PRE_DELETE_SNAPSHOT_TYPES, mn as PartialFailureError, mt as loadPublishableAssetManifest, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as setAwsClients, nt as isThrottlingError, o as planFailedOps, ot as S3StateBackend, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as NestedStackChildDirectDestroyError, pt as createAssetRedirectResolver, q as assertRegionMatch, qt as uploadCfnTemplate, r as DeployEngine, rt as DagBuilder, s as planRollback, sn as DeployCancelledError, st as rebuildClientForBucketRegion, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resetAwsClients, tt as isRetryableTransientError, u as withResourceDeadline, un as LocalStartServiceError, ut as stringifyValue, v as isFinalSnapshotError, vn as StackHasActiveImportsError, vt as ensureAssetStorage, w as renderStatefulReason, wn as normalizeAwsError, wt as formatDockerLoginError, x as extractDeploymentEventError, xt as validateAssetBucketName, y as refusesFinalSnapshot, yn as StackTerminationProtectionError, yt as getBootstrapMarkerKey, z as disableInstanceApiTermination, zt as resolveStateBucketWithDefaultAndSource } from "./deploy-engine-iHGY-Q_U.js";
2
+ import { $ as describeTypeWithThrottleRetry, $t as AwsClients, A as red, At as Synthesizer, B as isTerminationProtectionPropagationError, Bt as resolveUseCdkBootstrapAssets, C as isStatefulRecreateTargetSync, Ct as buildDockerImage, D as cyan, Dt as runDockerStreaming, E as bold, Et as runDockerForeground, F as ProviderRegistry, Ft as resolveAutoAssetStorage, G as normalizeAwsTagsToCfn, Gt as MIGRATE_TMP_PREFIX, H as cfnRefValueFromPhysicalId, Ht as warnDeprecatedNoPrefixCliFlag, I as findActionableSilentDrops, It as resolveCaptureObservedState, J as readConfigString, Jt as expectedOwnerParam, K as resolveExplicitPhysicalId, Kt as findLargeInlineResources, L as CloudControlProvider, Lt as resolveSkipPrefix, M as IAMRoleProvider, Mt as getDefaultStateBucketName, N as collectInlinePolicyNamesManagedBySiblings, Nt as getLegacyStateBucketName, O as gray, Ot as AssetManifestLoader, P as clearOnUpdateRemoval, Pt as resolveApp, Q as DiffCalculator, Qt as resolveBucketRegion, R as slowCcOperationTimeoutMs, Rt as resolveStateBucketWithDefault, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, St as validateContainerRepoName, T as formatResourceLine, Tn as withErrorHandling, Tt as getDockerCmd, U as refStateLookupFromResource, Ut as CFN_TEMPLATE_BODY_LIMIT, V as IntrinsicFunctionResolver, Vt as stateBucketExistenceConfirmed, W as WAFv2WebACLProvider, Wt as CFN_TEMPLATE_URL_LIMIT, X as requireConfigString, Xt as processStackMessages, Y as requireConfigArray, Yt as AssemblyReader, Z as applyRoleArnIfSet, _ as createPreDeleteFinalSnapshot, _n as ResourceUpdateNotSupportedError, _t as BOOTSTRAP_MARKER_PREFIX, a as DeploymentEventsStore, at as LockManager, b as unsupportedFinalSnapshotError, bt as parseBootstrapMarker, c as replayFailedOperations, cn as LocalInvokeBuildError$1, ct as shouldRetainResource, d as IMPLICIT_DELETE_DEPENDENCIES, dt as WorkGraph, en as getAwsClients, et as withRetry, f as computeImplicitDeleteEdges, fn as MissingCdkCliError, ft as buildAssetRedirectMap, g as ccRoutedFinalSnapshotError, gn as ResourceTimeoutError, gt as AssetModeResolver, h as buildFinalSnapshotIdentifier, hn as ProvisioningError, ht as rewriteTemplateAssetReferences, i as DeploymentEventsReader, in as CdkdError, it as TemplateParser, j as yellow, jt as synthesisStatusMessage, k as green, kt as getDockerImageBySourceHash, l as replayRollback, ln as LocalMigrateError, lt as AssetPublisher, m as PRE_DELETE_SNAPSHOT_TYPES, mn as PartialFailureError, mt as loadPublishableAssetManifest, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as setAwsClients, nt as isThrottlingError, o as planFailedOps, ot as S3StateBackend, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as NestedStackChildDirectDestroyError, pt as createAssetRedirectResolver, q as assertRegionMatch, qt as uploadCfnTemplate, r as DeployEngine, rt as DagBuilder, s as planRollback, sn as DeployCancelledError, st as rebuildClientForBucketRegion, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resetAwsClients, tt as isRetryableTransientError, u as withResourceDeadline, un as LocalStartServiceError, ut as stringifyValue, v as isFinalSnapshotError, vn as StackHasActiveImportsError, vt as ensureAssetStorage, w as renderStatefulReason, wn as normalizeAwsError, wt as formatDockerLoginError, x as extractDeploymentEventError, xt as validateAssetBucketName, y as refusesFinalSnapshot, yn as StackTerminationProtectionError, yt as getBootstrapMarkerKey, z as disableInstanceApiTermination, zt as resolveStateBucketWithDefaultAndSource } from "./deploy-engine-bEhHOGVR.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-5hYG8VHd.js";
4
+ import { t as ASGProvider } from "./asg-provider-BTeSlton.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";
@@ -12315,7 +12315,8 @@ var DynamoDBGlobalTableProvider = class {
12315
12315
  AttributeDefinitions: attributeDefinitions,
12316
12316
  BillingMode: billingMode
12317
12317
  };
12318
- if (billingMode === "PROVISIONED") createParams.ProvisionedThroughput = derivePerCallProvisionedThroughput(properties, currentRegion);
12318
+ const diagnostics = [];
12319
+ if (billingMode === "PROVISIONED") createParams.ProvisionedThroughput = derivePerCallProvisionedThroughput(properties, currentRegion, "min", diagnostics);
12319
12320
  const streamSpecInput = properties["StreamSpecification"];
12320
12321
  const needsStream = replicas.some((r) => r["Region"] !== currentRegion) || replicas.length > 1;
12321
12322
  if (streamSpecInput != null) {
@@ -12336,7 +12337,6 @@ var DynamoDBGlobalTableProvider = class {
12336
12337
  StreamViewType: "NEW_AND_OLD_IMAGES"
12337
12338
  };
12338
12339
  }
12339
- const diagnostics = [];
12340
12340
  let sdkIndexes;
12341
12341
  try {
12342
12342
  sdkIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, billingMode, "min", diagnostics);
@@ -12552,7 +12552,10 @@ var DynamoDBGlobalTableProvider = class {
12552
12552
  const onDemandCeilingLive = oldBilling === "PAY_PER_REQUEST" && newBilling === "PAY_PER_REQUEST";
12553
12553
  const diagnostics = [];
12554
12554
  const desiredCeilings = collectTableOnDemandCeilings(properties, currentRegion, diagnostics);
12555
- const desiredSdkIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling, "min", diagnostics);
12555
+ const flippingToProvisioned = oldBilling !== newBilling && newBilling === "PROVISIONED";
12556
+ if (flippingToProvisioned) derivePerCallProvisionedThroughput(properties, currentRegion, "seed", diagnostics);
12557
+ const desiredSdkIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling, "min", flippingToProvisioned ? void 0 : diagnostics);
12558
+ if (flippingToProvisioned) toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling, "seed", diagnostics);
12556
12559
  for (const replica of properties["Replicas"] ?? []) {
12557
12560
  const replicaRegion = asRecord$1(replica)?.["Region"];
12558
12561
  if (typeof replicaRegion !== "string" || replicaRegion === currentRegion) continue;
@@ -13232,9 +13235,11 @@ var DynamoDBGlobalTableProvider = class {
13232
13235
  const cause = regionErr instanceof Error ? regionErr : void 0;
13233
13236
  throw new ProvisioningError(`Could not resolve client region for DynamoDB GlobalTable ${logicalId} delete — would risk dropping the local replica`, resourceType, logicalId, physicalId, cause);
13234
13237
  }
13238
+ let preDeleteIndexes;
13235
13239
  try {
13236
13240
  const describe = await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: physicalId }));
13237
13241
  const replicas = describe.Table?.Replicas ?? [];
13242
+ preDeleteIndexes = describe.Table?.GlobalSecondaryIndexes;
13238
13243
  const tableIndexNames = (describe.Table?.GlobalSecondaryIndexes ?? []).map((gsi) => gsi.IndexName).filter((name) => typeof name === "string");
13239
13244
  for (const replica of replicas) {
13240
13245
  const region = replica.RegionName;
@@ -13265,6 +13270,10 @@ var DynamoDBGlobalTableProvider = class {
13265
13270
  }
13266
13271
  }
13267
13272
  try {
13273
+ if (hasTransitionalIndex(preDeleteIndexes)) {
13274
+ this.logger.debug(`Waiting for indexes on ${physicalId} to settle before DeleteTable (issue #1521)`);
13275
+ await this.waitForIndexesActive(physicalId, logicalId);
13276
+ }
13268
13277
  await this.dynamoDBClient.send(new DeleteTableCommand({ TableName: physicalId }));
13269
13278
  await this.waitForTableGone(physicalId, logicalId);
13270
13279
  this.logger.debug(`Successfully deleted DynamoDB GlobalTable ${logicalId}`);
@@ -13679,34 +13688,78 @@ var DynamoDBGlobalTableProvider = class {
13679
13688
  * `waitForTableActive`, this tolerates any non-terminal status — the
13680
13689
  * table may already be ACTIVE on the no-op path (already-disabled
13681
13690
  * protection) or transition through UPDATING.
13691
+ *
13692
+ * **A table is ACTIVE while one of its INDEXES is still transitioning**, and
13693
+ * the next `UpdateTable` in the serialized sequence is then rejected with
13694
+ * `Attempt to change a resource which is still in use: Index is being
13695
+ * updated` (issue #1521). That is not hypothetical: it broke the
13696
+ * `PAY_PER_REQUEST -> PROVISIONED` flip that also drops a GSI — AWS applies
13697
+ * the flip to every index, and cdkd issued the index DELETE while the index
13698
+ * was still absorbing it. Reproduced on pristine `main`, three runs out of
13699
+ * four on 2026-08-10, and the failed deploy's own `cdkd destroy` then
13700
+ * ORPHANED the table for the same reason.
13701
+ *
13702
+ * So the wait has TWO conditions read off the SAME response (an extra
13703
+ * `DescribeTable` per wait would double this path's API traffic for
13704
+ * something the response already carries) — but they keep DIFFERENT failure
13705
+ * semantics, and conflating them is a real regression rather than a
13706
+ * simplification. A table that never reaches ACTIVE is a hard error: nothing
13707
+ * can proceed. A still-transitioning INDEX is best-effort: `IndexStatus`
13708
+ * stays `CREATING` for the whole BACKFILL of a newly added GSI, which on a
13709
+ * populated table routinely outlives any cap — so throwing there would fail
13710
+ * a deploy whose resources are all correct, and would do it on an unrelated
13711
+ * tag-only edit that merely ran while an earlier index was still building.
13712
+ * Past the cap the index half warns and proceeds, leaving AWS's own
13713
+ * rejection as the backstop, which is the behavior it had before #1521 for
13714
+ * every case except the one the wait exists to fix.
13682
13715
  */
13683
13716
  async waitForTableActiveAfterUpdate(tableName, logicalId, maxAttempts = 600) {
13717
+ let tableReachedActive = false;
13684
13718
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
13685
- if ((await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.TableStatus === "ACTIVE") return;
13719
+ const response = await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }));
13720
+ if (response.Table?.TableStatus === "ACTIVE") {
13721
+ tableReachedActive = true;
13722
+ if (!hasTransitionalIndex(response.Table?.GlobalSecondaryIndexes)) return;
13723
+ }
13686
13724
  await new Promise((resolve) => setTimeout(resolve, 1e3));
13687
13725
  }
13726
+ if (tableReachedActive) {
13727
+ this.logger.warn(`Indexes on ${tableName} (${logicalId}) were still transitioning after ${maxAttempts}s — a large index backfill can outlive this wait. Proceeding; AWS rejects the next call if it is still too early.`);
13728
+ return;
13729
+ }
13688
13730
  throw new ProvisioningError(`Table ${tableName} did not reach ACTIVE within ${maxAttempts}s after UpdateTable`, "AWS::DynamoDB::GlobalTable", logicalId, tableName);
13689
13731
  }
13690
13732
  /**
13691
13733
  * Wait until every GSI reports `IndexStatus: ACTIVE` (Issue #1419).
13692
13734
  *
13693
- * `waitForTableActiveAfterUpdate` only checks `TableStatus`, and a GSI
13694
- * added by `UpdateTable` leaves the TABLE ACTIVE while the index itself
13695
- * is still `CREATING` — application-autoscaling rejects a target whose
13696
- * resource is not ready, so registering right after the GSI-add loop
13735
+ * A GSI added by `UpdateTable` leaves the TABLE ACTIVE while the index
13736
+ * itself is still `CREATING` — application-autoscaling rejects a target
13737
+ * whose resource is not ready, so registering right after the GSI-add loop
13697
13738
  * would skip the very index the deploy just introduced.
13698
13739
  *
13740
+ * Since issue #1521 the same distinction is enforced INSIDE
13741
+ * {@link waitForTableActiveAfterUpdate} (which now reads both conditions off
13742
+ * its own response), so this helper is no longer the only place that knows
13743
+ * it. It remains the explicit wait for the two callers that have no
13744
+ * table-status wait to piggyback on: the autoscaling registration above and
13745
+ * the pre-`DeleteTable` gate, which AWS rejects while an index is
13746
+ * transitioning. An index that is `DELETING` still reports, so the shared
13747
+ * {@link hasTransitionalIndex} predicate covers `CREATING` / `UPDATING` /
13748
+ * `DELETING` alike.
13749
+ *
13699
13750
  * **Best-effort by design**: on timeout it warns and returns rather than
13700
13751
  * throwing. A missed registration self-heals on the next deploy (the
13701
13752
  * reconcile re-asserts anything not already present), whereas throwing
13702
13753
  * here would fail a deploy whose actual resources are all correct. Index
13703
13754
  * backfill on a large table can take a long time, hence the 15-minute cap.
13755
+ * The caller that follows it with a mutating call keeps AWS's own error as
13756
+ * the backstop for the case where even that cap is not enough.
13704
13757
  */
13705
13758
  async waitForIndexesActive(tableName, logicalId) {
13706
13759
  const maxAttempts = 900;
13707
13760
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
13708
13761
  try {
13709
- if (((await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.GlobalSecondaryIndexes ?? []).every((gsi) => gsi.IndexStatus === "ACTIVE")) return;
13762
+ if (!hasTransitionalIndex((await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.GlobalSecondaryIndexes ?? [])) return;
13710
13763
  } catch (err) {
13711
13764
  this.logger.debug(`DescribeTable while waiting for indexes on ${tableName}: ${err instanceof Error ? err.message : String(err)}`);
13712
13765
  return;
@@ -13810,10 +13863,17 @@ function extractLocalDeletionProtection(props, region) {
13810
13863
  * literal `ReadCapacityUnits`, which is taken before its auto-scaling
13811
13864
  * members; 5 if absent.
13812
13865
  */
13813
- function derivePerCallProvisionedThroughput(properties, region, source = "min") {
13866
+ function derivePerCallProvisionedThroughput(properties, region, source = "min", diagnostics) {
13867
+ const rawReplicas = properties["Replicas"];
13868
+ const rawRead = (Array.isArray(rawReplicas) ? rawReplicas : []).find((r) => asRecord$1(r)?.["Region"] === region)?.["ReadProvisionedThroughputSettings"];
13869
+ const rawWrite = properties["WriteProvisionedThroughputSettings"];
13870
+ const read = deriveReadCapacityUnits(asRecord$1(rawRead), source);
13871
+ const write = deriveWriteCapacityUnits(asRecord$1(rawWrite), source);
13872
+ if (read === void 0) reportUnresolvedProvisionedCapacity(rawRead, "Read", source, { appliedUnits: DEFAULT_CAPACITY_UNITS }, diagnostics, { blockName: "Replicas[local].ReadProvisionedThroughputSettings" });
13873
+ if (write === void 0) reportUnresolvedProvisionedCapacity(rawWrite, "Write", source, { appliedUnits: DEFAULT_CAPACITY_UNITS }, diagnostics, { blockName: "WriteProvisionedThroughputSettings" });
13814
13874
  return {
13815
- ReadCapacityUnits: deriveReadCapacityUnits(asRecord$1((properties["Replicas"] ?? []).find((r) => r["Region"] === region)?.["ReadProvisionedThroughputSettings"]), source) ?? DEFAULT_CAPACITY_UNITS,
13816
- WriteCapacityUnits: deriveWriteCapacityUnits(asRecord$1(properties["WriteProvisionedThroughputSettings"]), source) ?? DEFAULT_CAPACITY_UNITS
13875
+ ReadCapacityUnits: read ?? DEFAULT_CAPACITY_UNITS,
13876
+ WriteCapacityUnits: write ?? DEFAULT_CAPACITY_UNITS
13817
13877
  };
13818
13878
  }
13819
13879
  /**
@@ -13968,6 +14028,26 @@ function asRecord$1(value) {
13968
14028
  * read-back comparison must therefore expect ABSENCE, not this value.
13969
14029
  */
13970
14030
  const ON_DEMAND_LIMIT_RESET = -1;
14031
+ /**
14032
+ * The GSI states during which AWS refuses another `UpdateTable` or a
14033
+ * `DeleteTable` on the table (issue #1521) — a table is ACTIVE while one of
14034
+ * its indexes is still transitioning, which is what made the serialized
14035
+ * update sequence race and what left a table ORPHANED when the follow-up
14036
+ * destroy hit the same rule.
14037
+ *
14038
+ * `DELETING` is included because a dropped index keeps reporting until it is
14039
+ * actually gone. The test is on the TRANSITIONAL values rather than on
14040
+ * `!== 'ACTIVE'`: an absent or unrecognized status must not park the caller
14041
+ * for its whole cap on a table that is fine.
14042
+ */
14043
+ const INDEX_TRANSITIONAL_STATUSES = /* @__PURE__ */ new Set([
14044
+ "CREATING",
14045
+ "UPDATING",
14046
+ "DELETING"
14047
+ ]);
14048
+ function hasTransitionalIndex(indexes) {
14049
+ return (indexes ?? []).some((gsi) => INDEX_TRANSITIONAL_STATUSES.has(gsi.IndexStatus ?? ""));
14050
+ }
13971
14051
  /** Coerce a CFn numeric (CFn is stringly-typed) to a finite number. */
13972
14052
  function toFiniteNumber(value) {
13973
14053
  if (value === void 0 || value === null || value === "") return void 0;
@@ -14058,6 +14138,94 @@ function reportUnresolvedRawMember(rawValue, diagnostics, context) {
14058
14138
  });
14059
14139
  }
14060
14140
  /**
14141
+ * Which member of a `*ProvisionedThroughputSettings` block to blame when the
14142
+ * derivation came back `undefined` (issue #1511).
14143
+ *
14144
+ * The derivation reports only "no number", so the member is re-derived here in
14145
+ * the SAME order {@link deriveWriteCapacityUnits} / {@link deriveReadCapacityUnits}
14146
+ * / {@link pickAutoScalingCapacity} consult members — the literal first, then
14147
+ * the auto-scaling block's `source`-selected member. Reaching this function at
14148
+ * all means every one of them failed, so the first DECLARED one is the value
14149
+ * the user was trying to set.
14150
+ *
14151
+ * `relPath` is relative to the block, `''` meaning the block ITSELF is the
14152
+ * unusable value (a string / array / bare unresolved intrinsic, or an object
14153
+ * carrying no capacity member at all).
14154
+ *
14155
+ * Returns `undefined` for an ABSENT block: defaulting there is the documented
14156
+ * pre-existing behavior for a template that never asked for a capacity, and
14157
+ * warning about it would shout on every deploy of a table that is fine.
14158
+ */
14159
+ function blameUnresolvedProvisionedMember(rawSettings, side, source) {
14160
+ if (rawSettings === void 0) return void 0;
14161
+ const settings = asRecord$1(rawSettings);
14162
+ if (!settings || isUnresolvedIntrinsicBlock(settings)) return {
14163
+ relPath: "",
14164
+ value: rawSettings
14165
+ };
14166
+ const literal = settings[`${side}CapacityUnits`];
14167
+ if (literal !== void 0) return {
14168
+ relPath: `${side}CapacityUnits`,
14169
+ value: literal
14170
+ };
14171
+ const autoKey = `${side}CapacityAutoScalingSettings`;
14172
+ const rawAutoScaling = settings[autoKey];
14173
+ if (rawAutoScaling === void 0) return {
14174
+ relPath: "",
14175
+ value: rawSettings
14176
+ };
14177
+ const autoScaling = asRecord$1(rawAutoScaling);
14178
+ if (!autoScaling || isUnresolvedIntrinsicBlock(autoScaling)) return {
14179
+ relPath: autoKey,
14180
+ value: rawAutoScaling
14181
+ };
14182
+ const order = source === "seed" ? ["SeedCapacity", "MinCapacity"] : ["MinCapacity", "SeedCapacity"];
14183
+ for (const member of order) {
14184
+ const value = autoScaling[member];
14185
+ if (value !== void 0) return {
14186
+ relPath: `${autoKey}.${member}`,
14187
+ value
14188
+ };
14189
+ }
14190
+ return {
14191
+ relPath: autoKey,
14192
+ value: rawAutoScaling
14193
+ };
14194
+ }
14195
+ /**
14196
+ * Report a DECLARED provisioned capacity the template could not resolve — the
14197
+ * PROVISIONED twin of {@link reportUnresolvedRawMember} (issue #1511).
14198
+ *
14199
+ * The two sides need different wording because they have different outcomes,
14200
+ * which is exactly why #1503 deferred this half. An unreadable ON-DEMAND
14201
+ * ceiling is SUPPRESSED (nothing is sent, the live ceiling stands), so its
14202
+ * diagnostic says "nothing was sent". A provisioned table cannot suppress —
14203
+ * AWS requires `ProvisionedThroughput` on the table and on every GSI of it —
14204
+ * so the derivation falls through to {@link DEFAULT_CAPACITY_UNITS} and a
14205
+ * table the template explicitly sized is deployed at 5/5. That is a throttling
14206
+ * symptom days later rather than an error, which is what makes naming it here
14207
+ * worth a warning at all. The one provisioned site that CAN suppress is a
14208
+ * replica's `ProvisionedThroughputOverride` (absent = inherit the source
14209
+ * table), hence the `suppressed` outcome arm.
14210
+ *
14211
+ * Caller decides WHEN: only when the derivation returned `undefined` AND no
14212
+ * explicit SDK-shaped member is standing in for it, so the warning always
14213
+ * describes what actually reached AWS.
14214
+ */
14215
+ function reportUnresolvedProvisionedCapacity(rawSettings, side, source, outcome, diagnostics, context) {
14216
+ if (!diagnostics) return;
14217
+ const blame = blameUnresolvedProvisionedMember(rawSettings, side, source);
14218
+ if (!blame) return;
14219
+ const path = blame.relPath ? `${context.blockName}.${blame.relPath}` : context.blockName;
14220
+ const consequence = "appliedUnits" in outcome ? `so cdkd sent ${outcome.appliedUnits} capacity units instead (its default — AWS requires a concrete provisioned capacity here, so there was nothing to suppress)` : outcome.suppressed;
14221
+ diagnostics.push({
14222
+ kind: "unresolved-member",
14223
+ ...context.indexName !== void 0 && { indexName: context.indexName },
14224
+ member: blame.relPath ? blame.relPath.slice(blame.relPath.lastIndexOf(".") + 1) : context.blockName.slice(context.blockName.lastIndexOf(".") + 1),
14225
+ message: `${path} is declared but did not resolve to a number (${JSON.stringify(blame.value)?.slice(0, 80)}), ${consequence}. This is usually an unresolved intrinsic; resolve it to a literal to apply the capacity you declared.`
14226
+ });
14227
+ }
14228
+ /**
14061
14229
  * Derive a single WRITE capacity number from a CFn
14062
14230
  * `WriteProvisionedThroughputSettings` block.
14063
14231
  *
@@ -14188,14 +14356,32 @@ function toSdkGlobalSecondaryIndexes(properties, region, billingMode, source = "
14188
14356
  member: "OnDemandThroughput",
14189
14357
  message: "an explicit OnDemandThroughput block was dropped because the table's BillingMode is PROVISIONED; AWS rejects on-demand ceilings on a provisioned table. Remove the block, or set BillingMode: PAY_PER_REQUEST."
14190
14358
  });
14191
- if (billingMode === "PROVISIONED") sdk.ProvisionedThroughput = mergeExplicitThroughputBlock(explicitProvisioned, {
14192
- ReadCapacityUnits: deriveReadCapacityUnits(asRecord$1(localEntry?.["ReadProvisionedThroughputSettings"]), source) ?? deriveReadCapacityUnits(asRecord$1(gsi["ReadProvisionedThroughputSettings"]), source) ?? DEFAULT_CAPACITY_UNITS,
14193
- WriteCapacityUnits: deriveWriteCapacityUnits(asRecord$1(gsi["WriteProvisionedThroughputSettings"]), source) ?? DEFAULT_CAPACITY_UNITS
14194
- }, ["ReadCapacityUnits", "WriteCapacityUnits"], diagnostics, {
14195
- ...typeof indexName === "string" && { indexName },
14196
- blockName: "ProvisionedThroughput"
14197
- });
14198
- else {
14359
+ if (billingMode === "PROVISIONED") {
14360
+ const rawLocalRead = localEntry?.["ReadProvisionedThroughputSettings"];
14361
+ const rawGsiRead = gsi["ReadProvisionedThroughputSettings"];
14362
+ const rawWrite = gsi["WriteProvisionedThroughputSettings"];
14363
+ const derivedRead = deriveReadCapacityUnits(asRecord$1(rawLocalRead), source) ?? deriveReadCapacityUnits(asRecord$1(rawGsiRead), source);
14364
+ const derivedWrite = deriveWriteCapacityUnits(asRecord$1(rawWrite), source);
14365
+ const explicitCovers = (member) => explicitProvisioned !== void 0 && toFiniteNumber(explicitProvisioned[member]) !== void 0;
14366
+ if (derivedRead === void 0 && !explicitCovers("ReadCapacityUnits")) {
14367
+ const fromReplica = rawLocalRead !== void 0;
14368
+ reportUnresolvedProvisionedCapacity(fromReplica ? rawLocalRead : rawGsiRead, "Read", source, { appliedUnits: DEFAULT_CAPACITY_UNITS }, diagnostics, {
14369
+ ...typeof indexName === "string" && { indexName },
14370
+ blockName: fromReplica ? "Replicas[local].GlobalSecondaryIndexes[].ReadProvisionedThroughputSettings" : "ReadProvisionedThroughputSettings"
14371
+ });
14372
+ }
14373
+ if (derivedWrite === void 0 && !explicitCovers("WriteCapacityUnits")) reportUnresolvedProvisionedCapacity(rawWrite, "Write", source, { appliedUnits: DEFAULT_CAPACITY_UNITS }, diagnostics, {
14374
+ ...typeof indexName === "string" && { indexName },
14375
+ blockName: "WriteProvisionedThroughputSettings"
14376
+ });
14377
+ sdk.ProvisionedThroughput = mergeExplicitThroughputBlock(explicitProvisioned, {
14378
+ ReadCapacityUnits: derivedRead ?? DEFAULT_CAPACITY_UNITS,
14379
+ WriteCapacityUnits: derivedWrite ?? DEFAULT_CAPACITY_UNITS
14380
+ }, ["ReadCapacityUnits", "WriteCapacityUnits"], diagnostics, {
14381
+ ...typeof indexName === "string" && { indexName },
14382
+ blockName: "ProvisionedThroughput"
14383
+ });
14384
+ } else {
14199
14385
  const rawWrite = asRecord$1(gsi["WriteOnDemandThroughputSettings"])?.["MaxWriteRequestUnits"];
14200
14386
  const rawReadLocal = asRecord$1(localEntry?.["ReadOnDemandThroughputSettings"])?.["MaxReadRequestUnits"];
14201
14387
  const rawReadGsi = asRecord$1(gsi["ReadOnDemandThroughputSettings"])?.["MaxReadRequestUnits"];
@@ -14364,7 +14550,12 @@ function toSdkReplicaThroughputOverrides(cfn, billingMode, diagnostics, context)
14364
14550
  message: "an explicit OnDemandThroughputOverride was dropped because the table's BillingMode is PROVISIONED; AWS rejects on-demand ceilings on a provisioned table."
14365
14551
  });
14366
14552
  if (billingMode === "PROVISIONED") {
14367
- const readCapacity = deriveReadCapacityUnits(asRecord$1(cfn["ReadProvisionedThroughputSettings"]));
14553
+ const rawRead = cfn["ReadProvisionedThroughputSettings"];
14554
+ const readCapacity = deriveReadCapacityUnits(asRecord$1(rawRead));
14555
+ if (readCapacity === void 0 && toFiniteNumber(explicitProvisioned?.["ReadCapacityUnits"]) === void 0) reportUnresolvedProvisionedCapacity(rawRead, "Read", "min", { suppressed: "so no ProvisionedThroughputOverride was sent and the replica keeps the source table's read capacity" }, diagnostics, {
14556
+ ...context,
14557
+ blockName: "ReadProvisionedThroughputSettings"
14558
+ });
14368
14559
  const merged = mergeExplicitThroughputBlock(explicitProvisioned, readCapacity !== void 0 ? { ReadCapacityUnits: readCapacity } : void 0, ["ReadCapacityUnits"], diagnostics, {
14369
14560
  ...context,
14370
14561
  blockName: "ProvisionedThroughputOverride"
@@ -66706,7 +66897,7 @@ function createMigrateCommand() {
66706
66897
  */
66707
66898
  function buildProgram() {
66708
66899
  const program = new Command();
66709
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.36");
66900
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.37");
66710
66901
  program.hook("preAction", (_thisCommand, actionCommand) => {
66711
66902
  const { profile } = actionCommand.optsWithGlobals();
66712
66903
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;