@go-to-k/cdkd 0.278.53 → 0.278.54

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 DiffCalculator, $t as resolveBucketRegion, A as red, At as getDockerImageBySourceHash, B as isTerminationProtectionPropagationError, Bt as resolveStateBucketWithDefaultAndSource, C as isStatefulRecreateTargetSync, Ct as validateContainerRepoName, D as cyan, Dt as runDockerForeground, E as bold, En as withErrorHandling, Et as getDockerCmd, F as ProviderRegistry, Ft as resolveApp, G as normalizeAwsTagsToCfn, Gt as CFN_TEMPLATE_URL_LIMIT, H as cfnRefValueFromPhysicalId, Ht as stateBucketExistenceConfirmed, I as findActionableSilentDrops, It as resolveAutoAssetStorage, J as readConfigString, Jt as uploadCfnTemplate, K as resolveExplicitPhysicalId, Kt as MIGRATE_TMP_PREFIX, L as CloudControlProvider, Lt as resolveCaptureObservedState, M as IAMRoleProvider, Mt as synthesisStatusMessage, N as collectInlinePolicyNamesManagedBySiblings, Nt as getDefaultStateBucketName, O as gray, Ot as runDockerStreaming, P as clearOnUpdateRemoval, Pt as getLegacyStateBucketName, Q as applyRoleArnIfSet, R as slowCcOperationTimeoutMs, Rt as resolveSkipPrefix, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, St as validateAssetBucketName, T as formatResourceLine, Tn as normalizeAwsError, Tt as formatDockerLoginError, U as refStateLookupFromResource, Ut as warnDeprecatedNoPrefixCliFlag, V as IntrinsicFunctionResolver, Vt as resolveUseCdkBootstrapAssets, W as WAFv2WebACLProvider, Wt as CFN_TEMPLATE_BODY_LIMIT, X as requireConfigArray, Xt as AssemblyReader, Y as replayWarn, Yt as expectedOwnerParam, Z as requireConfigString, Zt as processStackMessages, _ as createPreDeleteFinalSnapshot, _n as ResourceTimeoutError, _t as AssetModeResolver, a as DeploymentEventsStore, an as CdkdError, at as TemplateParser, b as unsupportedFinalSnapshotError, bn as StackTerminationProtectionError, bt as getBootstrapMarkerKey, c as replayFailedOperations, cn as DeployCancelledError, ct as rebuildClientForBucketRegion, d as IMPLICIT_DELETE_DEPENDENCIES, dn as LocalStartServiceError, dt as stringifyValue, en as AwsClients, et as describeTypeWithThrottleRetry, f as computeImplicitDeleteEdges, ft as WorkGraph, g as ccRoutedFinalSnapshotError, gn as ProvisioningError, gt as rewriteTemplateAssetReferences, h as buildFinalSnapshotIdentifier, hn as PartialFailureError, ht as loadPublishableAssetManifest, i as DeploymentEventsReader, it as DagBuilder, j as yellow, jt as Synthesizer, k as green, kt as AssetManifestLoader, l as replayRollback, ln as LocalInvokeBuildError$1, lt as shouldRetainResource, m as PRE_DELETE_SNAPSHOT_TYPES, mn as NestedStackChildDirectDestroyError, mt as createAssetRedirectResolver, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as resetAwsClients, nt as isRetryableTransientError, o as planFailedOps, ot as LockManager, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as MissingCdkCliError, pt as buildAssetRedirectMap, q as assertRegionMatch, qt as findLargeInlineResources, r as DeployEngine, rn as setAwsClients, rt as isThrottlingError, s as planRollback, st as S3StateBackend, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as getAwsClients, tt as withRetry, u as withResourceDeadline, un as LocalMigrateError, ut as AssetPublisher, v as isFinalSnapshotError, vn as ResourceUpdateNotSupportedError, vt as BOOTSTRAP_MARKER_PREFIX, w as renderStatefulReason, wt as buildDockerImage, x as extractDeploymentEventError, xt as parseBootstrapMarker, y as refusesFinalSnapshot, yn as StackHasActiveImportsError, yt as ensureAssetStorage, z as disableInstanceApiTermination, zt as resolveStateBucketWithDefault } from "./deploy-engine-CeEJqaaT.js";
2
+ import { $ as DiffCalculator, $t as resolveBucketRegion, A as red, At as getDockerImageBySourceHash, B as isTerminationProtectionPropagationError, Bt as resolveStateBucketWithDefaultAndSource, C as isStatefulRecreateTargetSync, Ct as validateContainerRepoName, D as cyan, Dt as runDockerForeground, E as bold, En as withErrorHandling, Et as getDockerCmd, F as ProviderRegistry, Ft as resolveApp, G as normalizeAwsTagsToCfn, Gt as CFN_TEMPLATE_URL_LIMIT, H as cfnRefValueFromPhysicalId, Ht as stateBucketExistenceConfirmed, I as findActionableSilentDrops, It as resolveAutoAssetStorage, J as readConfigString, Jt as uploadCfnTemplate, K as resolveExplicitPhysicalId, Kt as MIGRATE_TMP_PREFIX, L as CloudControlProvider, Lt as resolveCaptureObservedState, M as IAMRoleProvider, Mt as synthesisStatusMessage, N as collectInlinePolicyNamesManagedBySiblings, Nt as getDefaultStateBucketName, O as gray, Ot as runDockerStreaming, P as clearOnUpdateRemoval, Pt as getLegacyStateBucketName, Q as applyRoleArnIfSet, R as slowCcOperationTimeoutMs, Rt as resolveSkipPrefix, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, St as validateAssetBucketName, T as formatResourceLine, Tn as normalizeAwsError, Tt as formatDockerLoginError, U as refStateLookupFromResource, Ut as warnDeprecatedNoPrefixCliFlag, V as IntrinsicFunctionResolver, Vt as resolveUseCdkBootstrapAssets, W as WAFv2WebACLProvider, Wt as CFN_TEMPLATE_BODY_LIMIT, X as requireConfigArray, Xt as AssemblyReader, Y as replayWarn, Yt as expectedOwnerParam, Z as requireConfigString, Zt as processStackMessages, _ as createPreDeleteFinalSnapshot, _n as ResourceTimeoutError, _t as AssetModeResolver, a as DeploymentEventsStore, an as CdkdError, at as TemplateParser, b as unsupportedFinalSnapshotError, bn as StackTerminationProtectionError, bt as getBootstrapMarkerKey, c as replayFailedOperations, cn as DeployCancelledError, ct as rebuildClientForBucketRegion, d as IMPLICIT_DELETE_DEPENDENCIES, dn as LocalStartServiceError, dt as stringifyValue, en as AwsClients, et as describeTypeWithThrottleRetry, f as computeImplicitDeleteEdges, ft as WorkGraph, g as ccRoutedFinalSnapshotError, gn as ProvisioningError, gt as rewriteTemplateAssetReferences, h as buildFinalSnapshotIdentifier, hn as PartialFailureError, ht as loadPublishableAssetManifest, i as DeploymentEventsReader, it as DagBuilder, j as yellow, jt as Synthesizer, k as green, kt as AssetManifestLoader, l as replayRollback, ln as LocalInvokeBuildError$1, lt as shouldRetainResource, m as PRE_DELETE_SNAPSHOT_TYPES, mn as NestedStackChildDirectDestroyError, mt as createAssetRedirectResolver, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as resetAwsClients, nt as isRetryableTransientError, o as planFailedOps, ot as LockManager, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as MissingCdkCliError, pt as buildAssetRedirectMap, q as assertRegionMatch, qt as findLargeInlineResources, r as DeployEngine, rn as setAwsClients, rt as isThrottlingError, s as planRollback, st as S3StateBackend, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as getAwsClients, tt as withRetry, u as withResourceDeadline, un as LocalMigrateError, ut as AssetPublisher, v as isFinalSnapshotError, vn as ResourceUpdateNotSupportedError, vt as BOOTSTRAP_MARKER_PREFIX, w as renderStatefulReason, wt as buildDockerImage, x as extractDeploymentEventError, xt as parseBootstrapMarker, y as refusesFinalSnapshot, yn as StackHasActiveImportsError, yt as ensureAssetStorage, z as disableInstanceApiTermination, zt as resolveStateBucketWithDefault } from "./deploy-engine-CEoy1_C7.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-Camwh-s6.js";
4
+ import { t as ASGProvider } from "./asg-provider-Ro9B_1Sy.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";
@@ -7775,8 +7775,20 @@ var SNSTopicProvider = class {
7775
7775
  }
7776
7776
  /**
7777
7777
  * Create an SNS topic
7778
+ *
7779
+ * `context.replayingState` (issue #1551) downgrades this method's one
7780
+ * template-shape refusal — `buildDeliveryStatusAttributeMap`'s `'throw'`
7781
+ * mode — to a warning. The rollback executor's
7782
+ * reverse-replacement arm revives the OLD topic with
7783
+ * `create(..., previousState.properties, REPLAYING_STATE_CREATE_CONTEXT)`,
7784
+ * so the properties bag can be a historical cdkd STATE record carrying a
7785
+ * shape this provider now refuses — and the user has no template-side
7786
+ * remedy for a state record. The parameter was previously absent, so that
7787
+ * 4th argument was silently ignored and `buildDeliveryStatusAttributeMap`'s
7788
+ * `'throw'` mode stranded the replay; issue #1538 fixed the update side
7789
+ * only.
7778
7790
  */
7779
- async create(logicalId, resourceType, properties) {
7791
+ async create(logicalId, resourceType, properties, context) {
7780
7792
  this.logger.debug(`Creating SNS topic ${logicalId}`);
7781
7793
  const topicName = properties["TopicName"] || generateResourceName(logicalId, { maxLength: 256 });
7782
7794
  try {
@@ -7814,7 +7826,7 @@ var SNSTopicProvider = class {
7814
7826
  AttributeValue: dataProtectionPolicy
7815
7827
  }));
7816
7828
  }
7817
- const deliveryStatusAttributes = buildDeliveryStatusAttributeMap(properties["DeliveryStatusLogging"], logicalId, "throw");
7829
+ const deliveryStatusAttributes = buildDeliveryStatusAttributeMap(properties["DeliveryStatusLogging"], logicalId, context?.replayingState === true ? "warn" : "throw");
7818
7830
  for (const [attributeName, attributeValue] of deliveryStatusAttributes) await this.snsClient.send(new SetTopicAttributesCommand({
7819
7831
  TopicArn: topicArn,
7820
7832
  AttributeName: attributeName,
@@ -8311,9 +8323,15 @@ const SNS_DELIVERY_STATUS_ATTRIBUTE_SUFFIXES = [
8311
8323
  * / unknown protocol, and encodes the create-vs-update distinction
8312
8324
  * (issue #1538):
8313
8325
  *
8314
- * - `'throw'` — the CREATE desired side, which is always template-borne:
8315
- * a template error must fail fast, naming the offending value.
8316
- * - `'warn'` — the UPDATE desired side, which is NOT always template-borne:
8326
+ * - `'throw'` — a TEMPLATE-borne desired side: an ordinary `create()`, where a
8327
+ * template error must fail fast, naming the offending value.
8328
+ * - `'warn'` — a desired side that is NOT template-borne. That is every
8329
+ * `update()`, and — since issue #1551 — a `create()` whose
8330
+ * `CreateContext.replayingState` is set (the rollback executor's
8331
+ * reverse-replacement arm, which revives the old topic from a state record).
8332
+ * The wording below is therefore operation-neutral: on a create there is no
8333
+ * previously-set attribute to reset, only one that is never applied. Same
8334
+ * reason as before:
8317
8335
  * the rollback executor's revert arm and `cdkd drift --revert` both call
8318
8336
  * `update()` with a state-derived bag as the DESIRED properties, so a
8319
8337
  * state record carrying an unsupported protocol / malformed shape (a
@@ -8346,8 +8364,9 @@ const SNS_DELIVERY_STATUS_ATTRIBUTE_SUFFIXES = [
8346
8364
  * protocol, non-array container, non-object entry) used to throw on that
8347
8365
  * side too, which made such a state record permanently unreplayable —
8348
8366
  * fixed by issue #1538, which moved `update()`'s desired side to `'warn'`
8349
- * while `create()` keeps `'throw'` (a template create must still fail
8350
- * fast; #1529 / #1536 deliberately left that contract unchanged).
8367
+ * while `create()` kept `'throw'`; issue #1551 then extended the downgrade to
8368
+ * a create that DECLARES a state replay, leaving the template-path create
8369
+ * strict (#1529 / #1536 deliberately left that contract unchanged).
8351
8370
  */
8352
8371
  function buildDeliveryStatusAttributeMap(logging, logicalId, onMalformed) {
8353
8372
  const map = /* @__PURE__ */ new Map();
@@ -8358,7 +8377,7 @@ function buildDeliveryStatusAttributeMap(logging, logicalId, onMalformed) {
8358
8377
  const seenProtocols = /* @__PURE__ */ new Set();
8359
8378
  if (!Array.isArray(logging)) {
8360
8379
  if (onMalformed === "throw") throw new Error(`SNS topic ${logicalId}: DeliveryStatusLogging must be an array of {Protocol, ...} objects, got ${JSON.stringify(logging)}`);
8361
- if (onMalformed === "warn") warn(`SNS topic ${logicalId}: DeliveryStatusLogging must be an array of {Protocol, ...} objects, got ${JSON.stringify(logging)} — treating it as empty for this update, so previously set delivery-status attributes are reset. The desired properties can come from a cdkd state record (rollback replay / drift --revert), so this does not fail the update; fix the template (or the state record on a replay) to clear this warning.`);
8380
+ if (onMalformed === "warn") warn(`SNS topic ${logicalId}: DeliveryStatusLogging must be an array of {Protocol, ...} objects, got ${JSON.stringify(logging)} — treating it as empty, so no delivery-status attribute is applied (on an update, any previously set one is reset). The desired properties can come from a cdkd state record (rollback replay / drift --revert), so this does not fail the operation; fix the template (or the state record on a replay) to clear this warning.`);
8362
8381
  return map;
8363
8382
  }
8364
8383
  for (const entry of logging) {
@@ -10083,9 +10102,16 @@ var LambdaUrlProvider = class {
10083
10102
  wasReplaced: false,
10084
10103
  attributes: {}
10085
10104
  };
10105
+ let authTypeUnusable = false;
10106
+ const requestedAuthType = requireConfigString(properties["AuthType"], "NONE", "AWS::Lambda::Url AuthType", { onUnusable: (message) => {
10107
+ authTypeUnusable = true;
10108
+ this.logger.warn(`${message} The function URL's existing auth type is kept for this update rather than reset to the default (NONE), which would make the URL public.`);
10109
+ } });
10110
+ const previousAuthType = previousProperties["AuthType"];
10111
+ const authType = authTypeUnusable ? typeof previousAuthType === "string" && previousAuthType.trim() !== "" ? previousAuthType : void 0 : requestedAuthType;
10086
10112
  const updateParams = {
10087
10113
  FunctionName: physicalId,
10088
- AuthType: requireConfigString(properties["AuthType"], "NONE", "AWS::Lambda::Url AuthType")
10114
+ ...authType !== void 0 && { AuthType: authType }
10089
10115
  };
10090
10116
  const invokeMode = clearOnUpdateRemoval(properties["InvokeMode"], previousProperties["InvokeMode"], "BUFFERED");
10091
10117
  if (invokeMode !== void 0) updateParams.InvokeMode = invokeMode;
@@ -11729,7 +11755,10 @@ var DynamoDBTableProvider = class {
11729
11755
  if (table?.TableArn) await this.applyTagDiff(table.TableArn, previousProperties["Tags"], properties["Tags"]);
11730
11756
  const normalizeTableClass = (v) => typeof v === "string" && v.length > 0 ? v : "STANDARD";
11731
11757
  const tableClassChanged = normalizeTableClass(properties["TableClass"]) !== normalizeTableClass(previousProperties["TableClass"]);
11732
- const prevBillingMode = previousProperties["BillingMode"];
11758
+ const recordedPrevBillingMode = previousProperties["BillingMode"];
11759
+ const recordedPrevBillingModeUsable = recordedPrevBillingMode === void 0 || typeof recordedPrevBillingMode === "string" && recordedPrevBillingMode.trim() !== "";
11760
+ const prevBillingMode = recordedPrevBillingModeUsable ? recordedPrevBillingMode : table?.BillingModeSummary?.BillingMode ?? "PROVISIONED";
11761
+ if (!recordedPrevBillingModeUsable) this.logger.warn(`AWS::DynamoDB::Table ${logicalId}: the recorded previous BillingMode is unusable (${JSON.stringify(recordedPrevBillingMode)}) — using the table's actual billing mode (${prevBillingMode}) as the comparison baseline for this update so a corrected template does not issue a same-mode UpdateTable.`);
11733
11762
  let billingModeUnusable = false;
11734
11763
  const requestedBillingMode = properties["BillingMode"] === void 0 ? void 0 : requireConfigString(properties["BillingMode"], "PROVISIONED", "AWS::DynamoDB::Table BillingMode", { onUnusable: (message) => {
11735
11764
  billingModeUnusable = true;
@@ -12707,7 +12736,8 @@ var DynamoDBGlobalTableProvider = class {
12707
12736
  }
12708
12737
  let sdkIndexes;
12709
12738
  try {
12710
- sdkIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, billingMode, "min", diagnostics, replayWarn(this.logger, context).onUnusable);
12739
+ const onUnusableCreateIndexes = replayWarn(this.logger, context).onUnusable;
12740
+ sdkIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, billingMode, "min", diagnostics, onUnusableCreateIndexes && ((message) => onUnusableCreateIndexes(`${message} Omitting the malformed block, so this create carries NO indexes; the same value is REFUSED on a template-path create`)));
12711
12741
  } catch (error) {
12712
12742
  throw new ProvisioningError(error instanceof Error ? error.message : String(error), resourceType, logicalId, void 0, error instanceof Error ? error : void 0);
12713
12743
  }
@@ -12915,7 +12945,10 @@ var DynamoDBGlobalTableProvider = class {
12915
12945
  return (props["Replicas"] ?? []).find((r) => r["Region"] === currentRegion)?.["Tags"];
12916
12946
  };
12917
12947
  if (tableArn) await this.applyTagDiff(tableArn, extractLocalTags(previousProperties), extractLocalTags(properties));
12918
- const oldBilling = previousProperties["BillingMode"] ?? "PAY_PER_REQUEST";
12948
+ const recordedOldBilling = previousProperties["BillingMode"];
12949
+ const recordedOldBillingUsable = recordedOldBilling === void 0 || typeof recordedOldBilling === "string" && recordedOldBilling.trim() !== "";
12950
+ const oldBilling = recordedOldBillingUsable ? recordedOldBilling ?? "PAY_PER_REQUEST" : describeResp.Table?.BillingModeSummary?.BillingMode ?? "PROVISIONED";
12951
+ if (!recordedOldBillingUsable) this.logger.warn(`AWS::DynamoDB::GlobalTable ${logicalId}: the recorded previous BillingMode is unusable (${JSON.stringify(recordedOldBilling)}) — using the table's actual billing mode (${oldBilling}) as the comparison baseline for this update so a corrected template does not issue a same-mode UpdateTable.`);
12919
12952
  let billingUnusable = false;
12920
12953
  const requestedBilling = requireConfigString(properties["BillingMode"], "PAY_PER_REQUEST", "AWS::DynamoDB::GlobalTable BillingMode", { onUnusable: (message) => {
12921
12954
  billingUnusable = true;
@@ -12927,8 +12960,19 @@ var DynamoDBGlobalTableProvider = class {
12927
12960
  const desiredCeilings = collectTableOnDemandCeilings(properties, currentRegion, diagnostics);
12928
12961
  const flippingToProvisioned = oldBilling !== newBilling && newBilling === "PROVISIONED";
12929
12962
  if (flippingToProvisioned) derivePerCallProvisionedThroughput(properties, currentRegion, "seed", diagnostics);
12930
- const desiredSdkIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling, "min", flippingToProvisioned ? void 0 : diagnostics);
12931
- if (flippingToProvisioned) toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling, "seed", diagnostics);
12963
+ let desiredGsiUnusable = false;
12964
+ const onUnusableGsiBlock = (message) => {
12965
+ desiredGsiUnusable = true;
12966
+ this.logger.warn(`${message} No GlobalSecondaryIndexes change is applied by this update; the table's existing indexes are left untouched.`);
12967
+ };
12968
+ let previousGsiUnusable = false;
12969
+ const onUnusablePreviousGsiBlock = (message) => {
12970
+ previousGsiUnusable = true;
12971
+ this.logger.warn(`${message} (recorded in cdkd state, not in the template) — using the table's LIVE indexes as the comparison baseline for this update, so a corrected template still applies.`);
12972
+ };
12973
+ const liveIndexNames = new Set((describeResp.Table?.GlobalSecondaryIndexes ?? []).map((live) => live.IndexName).filter((name) => typeof name === "string"));
12974
+ const desiredSdkIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling, "min", flippingToProvisioned ? void 0 : diagnostics, onUnusableGsiBlock);
12975
+ if (flippingToProvisioned) toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling, "seed", diagnostics, onUnusableGsiBlock);
12932
12976
  for (const replica of properties["Replicas"] ?? []) {
12933
12977
  const replicaRegion = asRecord$1(replica)?.["Region"];
12934
12978
  if (typeof replicaRegion !== "string" || replicaRegion === currentRegion) continue;
@@ -12959,11 +13003,18 @@ var DynamoDBGlobalTableProvider = class {
12959
13003
  flatChanged = true;
12960
13004
  }
12961
13005
  if (properties["StreamSpecification"] !== void 0 && !deepEqual$1(properties["StreamSpecification"], previousProperties["StreamSpecification"])) {
12962
- flatUpdate.StreamSpecification = {
12963
- StreamEnabled: true,
12964
- StreamViewType: readConfigString(properties["StreamSpecification"], "StreamViewType", "NEW_AND_OLD_IMAGES", "AWS::DynamoDB::GlobalTable StreamSpecification")
12965
- };
12966
- flatChanged = true;
13006
+ let streamSpecUnusable = false;
13007
+ const streamViewType = readConfigString(properties["StreamSpecification"], "StreamViewType", "NEW_AND_OLD_IMAGES", "AWS::DynamoDB::GlobalTable StreamSpecification", { onUnusable: (message) => {
13008
+ streamSpecUnusable = true;
13009
+ this.logger.warn(`${message} The table's existing stream configuration is left untouched for this update rather than re-pointed at the default.`);
13010
+ } });
13011
+ if (!streamSpecUnusable) {
13012
+ flatUpdate.StreamSpecification = {
13013
+ StreamEnabled: true,
13014
+ StreamViewType: streamViewType
13015
+ };
13016
+ flatChanged = true;
13017
+ }
12967
13018
  }
12968
13019
  const awsOnDemand = describeResp.Table?.OnDemandThroughput;
12969
13020
  const onDemandPayload = {};
@@ -12995,11 +13046,13 @@ var DynamoDBGlobalTableProvider = class {
12995
13046
  };
12996
13047
  if (newBilling === "PROVISIONED") {
12997
13048
  billingUpdate.ProvisionedThroughput = derivePerCallProvisionedThroughput(properties, currentRegion, "seed");
12998
- const provisionedIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling, "seed");
13049
+ const provisionedIndexes = toSdkGlobalSecondaryIndexes(properties, currentRegion, newBilling, "seed", void 0, onUnusableGsiBlock);
13050
+ const previousFlipIndexes = toSdkGlobalSecondaryIndexes(previousProperties, currentRegion, newBilling, "seed", void 0, onUnusablePreviousGsiBlock);
13051
+ if (desiredGsiUnusable || previousGsiUnusable) throw new ProvisioningError(`Cannot flip AWS::DynamoDB::GlobalTable ${logicalId} to PROVISIONED while its GlobalSecondaryIndexes block is unusable: AWS requires per-index provisioned capacity in the same UpdateTable, and it cannot be derived from a malformed block (nor read from a table that is still on-demand). Fix the GlobalSecondaryIndexes value — in the template, or in the cdkd state record on a rollback / drift --revert replay — and re-deploy. The table is unchanged.`, resourceType, logicalId, physicalId);
13052
+ const previousByIndexName = /* @__PURE__ */ new Map();
13053
+ for (const gsi of previousFlipIndexes) if (gsi.IndexName) previousByIndexName.set(gsi.IndexName, gsi);
12999
13054
  const previousCfnIndexes = previousProperties["GlobalSecondaryIndexes"];
13000
13055
  const existingIndexNames = new Set((Array.isArray(previousCfnIndexes) ? previousCfnIndexes : []).map((entry) => entry?.["IndexName"]).filter((name) => typeof name === "string"));
13001
- const previousByIndexName = /* @__PURE__ */ new Map();
13002
- for (const gsi of toSdkGlobalSecondaryIndexes(previousProperties, currentRegion, newBilling, "seed")) if (gsi.IndexName) previousByIndexName.set(gsi.IndexName, gsi);
13003
13056
  const byIndexName = new Map(previousByIndexName);
13004
13057
  const survivingIndexNames = /* @__PURE__ */ new Set();
13005
13058
  for (const gsi of provisionedIndexes) {
@@ -13121,8 +13174,18 @@ var DynamoDBGlobalTableProvider = class {
13121
13174
  }));
13122
13175
  await this.waitForReplicaActive(physicalId, region, logicalId);
13123
13176
  }
13124
- const previousSdkIndexes = toSdkGlobalSecondaryIndexes(previousProperties, currentRegion, oldBilling);
13125
- const gsiDiff = diffGlobalSecondaryIndexes(previousSdkIndexes, desiredSdkIndexes);
13177
+ const previousTranslatedIndexes = toSdkGlobalSecondaryIndexes(previousProperties, currentRegion, oldBilling, "min", void 0, onUnusablePreviousGsiBlock);
13178
+ const previousSdkIndexes = previousGsiUnusable ? desiredSdkIndexes.filter((gsi) => typeof gsi.IndexName === "string" && liveIndexNames.has(gsi.IndexName)) : previousTranslatedIndexes;
13179
+ if (previousGsiUnusable) {
13180
+ const desiredNames = new Set(desiredSdkIndexes.map((gsi) => gsi.IndexName).filter((name) => typeof name === "string"));
13181
+ const liveOnly = [...liveIndexNames].filter((name) => !desiredNames.has(name));
13182
+ if (liveOnly.length > 0) this.logger.warn(`AWS::DynamoDB::GlobalTable ${logicalId}: ${liveOnly.join(", ")} exist(s) on the table but not in the template. No Delete is issued while the recorded GlobalSecondaryIndexes are unusable — a junk state record cannot prove cdkd created them. Re-deploy once state carries a valid block to remove them.`);
13183
+ }
13184
+ const gsiDiff = desiredGsiUnusable ? {
13185
+ added: [],
13186
+ removed: [],
13187
+ modified: []
13188
+ } : diffGlobalSecondaryIndexes(previousSdkIndexes, desiredSdkIndexes);
13126
13189
  const rawOnDemandDeclarations = collectRawOnDemandDeclarations(properties, currentRegion);
13127
13190
  const previousSdkByName = /* @__PURE__ */ new Map();
13128
13191
  for (const prev of previousSdkIndexes) if (prev.IndexName) previousSdkByName.set(prev.IndexName, prev);
@@ -13182,7 +13245,10 @@ var DynamoDBGlobalTableProvider = class {
13182
13245
  }));
13183
13246
  await this.waitForTableActiveAfterUpdate(physicalId, logicalId);
13184
13247
  }
13185
- if (newBilling === "PROVISIONED" || oldBilling === "PROVISIONED") {
13248
+ const skipAutoScalingForUnusableGsi = desiredGsiUnusable && newBilling !== "PAY_PER_REQUEST";
13249
+ const autoScalingMeaningfulForIndexes = (newBilling === "PROVISIONED" || oldBilling === "PROVISIONED") && !skipAutoScalingForUnusableGsi;
13250
+ if (skipAutoScalingForUnusableGsi && oldBilling === "PROVISIONED") this.logger.warn(`AWS::DynamoDB::GlobalTable ${logicalId}: skipping the auto-scaling target reconcile because the GlobalSecondaryIndexes block is unusable — the per-index targets are derived from it, so reconciling would deregister the live indexes' scalable targets. Existing targets are left untouched.`);
13251
+ if (autoScalingMeaningfulForIndexes) {
13186
13252
  if (gsiDiff.added.length > 0) await this.waitForIndexesActive(physicalId, logicalId);
13187
13253
  await this.reconcileAutoScalingTargets(physicalId, collectAutoScalingTargets(previousProperties, currentRegion), newBilling === "PAY_PER_REQUEST" ? [] : collectAutoScalingTargets(properties, currentRegion), currentRegion, autoScalingApplied);
13188
13254
  }
@@ -14692,7 +14758,7 @@ function toSdkGlobalSecondaryIndexes(properties, region, billingMode, source = "
14692
14758
  if (rawIndexes !== void 0 && !Array.isArray(rawIndexes)) {
14693
14759
  const shapeDetail = `AWS::DynamoDB::GlobalTable GlobalSecondaryIndexes must be an array, got ${typeof rawIndexes} (${JSON.stringify(rawIndexes)?.slice(0, 200)}).`;
14694
14760
  if (onUnusableIndexes) {
14695
- onUnusableIndexes(`${shapeDetail} Omitting the malformed block, so this create carries NO indexes; the same value is REFUSED on a template-path create`);
14761
+ onUnusableIndexes(shapeDetail);
14696
14762
  return [];
14697
14763
  }
14698
14764
  throw new Error(`${shapeDetail} An unresolved intrinsic here would otherwise deploy a table with no indexes.`);
@@ -68036,7 +68102,7 @@ function createMigrateCommand() {
68036
68102
  */
68037
68103
  function buildProgram() {
68038
68104
  const program = new Command();
68039
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.53");
68105
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.54");
68040
68106
  program.hook("preAction", (_thisCommand, actionCommand) => {
68041
68107
  const { profile } = actionCommand.optsWithGlobals();
68042
68108
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;