@go-to-k/cdkd 0.283.0 → 0.283.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as configStringRefusal, $t as resolveStateBucketWithDefaultAndSource, A as green, An as NestedStackChildDirectDestroyError, At as BOOTSTRAP_MARKER_PREFIX, B as slowCcOperationTimeoutMs, Bt as runDockerForeground, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ct as stringifyValue, D as bold, Dn as LocalStartServiceError, Dt as loadPublishableAssetManifest, E as formatResourceLine, En as LocalMigrateError, Et as createAssetRedirectResolver, F as clearOnUpdateRemoval, Fn as StackHasActiveImportsError, Ft as validateContainerRepoName, G as getAccountInfo, Gt as synthesisStatusMessage, H as isTerminationProtectionPropagationError, Hn as withErrorHandling, Ht as AssetManifestLoader, I as ProviderRegistry, In as StackTerminationProtectionError, It as buildDenyExternalAccessPolicy, J as normalizeAwsTagsToCfn, Jt as resolveApp, K as refStateLookupFromResource, Kt as getDefaultStateBucketName, L as findActionableSilentDrops, Lt as buildDockerImage, M as yellow, Mn as ProvisioningError, Mt as getBootstrapMarkerKey, N as IAMRoleProvider, Nn as ResourceTimeoutError, Nt as parseBootstrapMarker, O as cyan, Ot as rewriteTemplateAssetReferences, P as collectInlinePolicyNamesManagedBySiblings, Pn as ResourceUpdateNotSupportedError, Pt as validateAssetBucketName, Q as configBooleanRefusal, Qt as resolveStateBucketWithDefault, R as findSilentDropProperties, Rt as formatDockerLoginError, S as extractDeploymentEventError, St as AssetPublisher, T as renderStatefulReason, Tn as LocalInvokeBuildError$1, Tt as buildAssetRedirectMap, U as IntrinsicFunctionResolver, Ut as getDockerImageBySourceHash, V as disableInstanceApiTermination, Vn as normalizeAwsError, Vt as runDockerStreaming, W as cfnRefValueFromPhysicalId, Wt as Synthesizer, X as assertRegionMatch, Xt as resolveCaptureObservedState, Y as resolveExplicitPhysicalId, Yt as resolveAutoAssetStorage, Z as coerceCfnBoolean, Zt as resolveSkipPrefix, _ as createPreDeleteFinalSnapshot, _n as getAwsClients, _t as TemplateParser, a as DeploymentEventsStore, an as MIGRATE_TMP_PREFIX, at as s3BucketArn, b as unsupportedFinalSnapshotError, bt as rebuildClientForBucketRegion, c as replayFailedOperations, cn as expectedOwnerParam, ct as s3BucketRegionalDomainName, d as IMPLICIT_DELETE_DEPENDENCIES, dn as derivePartitionAndUrlSuffix, dt as DiffCalculator, en as resolveUseCdkBootstrapAssets, et as readConfigString, f as computeImplicitDeleteEdges, fn as AssemblyReader, ft as describeTypeWithThrottleRetry, g as ccRoutedFinalSnapshotError, gn as AwsClients, gt as DagBuilder, h as buildFinalSnapshotIdentifier, hn as resolveBucketRegion, ht as isThrottlingError, i as DeploymentEventsReader, in as CFN_TEMPLATE_URL_LIMIT, it as requireConfigString, j as red, jn as PartialFailureError, jt as ensureAssetStorage, k as gray, kn as MissingCdkCliError, kt as AssetModeResolver, l as replayRollback, ln as PARTITION_TABLE, lt as s3BucketWebsiteUrl, m as PRE_DELETE_SNAPSHOT_TYPES, mt as isRetryableTransientError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as warnDeprecatedNoPrefixCliFlag, nt as requireConfigArray, o as planFailedOps, on as findLargeInlineResources, ot as s3BucketDomainName, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as processStackMessages, pt as withRetry, q as WAFv2WebACLProvider, qt as getLegacyStateBucketName, r as DeployEngine, rn as CFN_TEMPLATE_BODY_LIMIT, rt as requireConfigObject, s as planRollback, sn as uploadCfnTemplate, st as s3BucketDualStackDomainName, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as stateBucketExistenceConfirmed, tt as replayWarn, u as withResourceDeadline, un as canonicalizeRegion, ut as applyRoleArnIfSet, v as isFinalSnapshotError, vn as resetAwsClients, vt as LockManager, w as isStatefulRecreateTargetSync, wn as DeployCancelledError, wt as WorkGraph, x as makeCanonicalizePropertiesFn, xn as CdkdError, xt as shouldRetainResource, y as refusesFinalSnapshot, yn as setAwsClients, yt as S3StateBackend, z as CloudControlProvider, zt as getDockerCmd } from "./deploy-engine-CEzMNZTA.js";
2
+ import { $ as configStringRefusal, $t as resolveStateBucketWithDefaultAndSource, A as green, An as NestedStackChildDirectDestroyError, At as BOOTSTRAP_MARKER_PREFIX, B as slowCcOperationTimeoutMs, Bt as runDockerForeground, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ct as stringifyValue, D as bold, Dn as LocalStartServiceError, Dt as loadPublishableAssetManifest, E as formatResourceLine, En as LocalMigrateError, Et as createAssetRedirectResolver, F as clearOnUpdateRemoval, Fn as StackHasActiveImportsError, Ft as validateContainerRepoName, G as getAccountInfo, Gt as synthesisStatusMessage, H as isTerminationProtectionPropagationError, Hn as withErrorHandling, Ht as AssetManifestLoader, I as ProviderRegistry, In as StackTerminationProtectionError, It as buildDenyExternalAccessPolicy, J as normalizeAwsTagsToCfn, Jt as resolveApp, K as refStateLookupFromResource, Kt as getDefaultStateBucketName, L as findActionableSilentDrops, Lt as buildDockerImage, M as yellow, Mn as ProvisioningError, Mt as getBootstrapMarkerKey, N as IAMRoleProvider, Nn as ResourceTimeoutError, Nt as parseBootstrapMarker, O as cyan, Ot as rewriteTemplateAssetReferences, P as collectInlinePolicyNamesManagedBySiblings, Pn as ResourceUpdateNotSupportedError, Pt as validateAssetBucketName, Q as configBooleanRefusal, Qt as resolveStateBucketWithDefault, R as findSilentDropProperties, Rt as formatDockerLoginError, S as extractDeploymentEventError, St as AssetPublisher, T as renderStatefulReason, Tn as LocalInvokeBuildError$1, Tt as buildAssetRedirectMap, U as IntrinsicFunctionResolver, Ut as getDockerImageBySourceHash, V as disableInstanceApiTermination, Vn as normalizeAwsError, Vt as runDockerStreaming, W as cfnRefValueFromPhysicalId, Wt as Synthesizer, X as assertRegionMatch, Xt as resolveCaptureObservedState, Y as resolveExplicitPhysicalId, Yt as resolveAutoAssetStorage, Z as coerceCfnBoolean, Zt as resolveSkipPrefix, _ as createPreDeleteFinalSnapshot, _n as getAwsClients, _t as TemplateParser, a as DeploymentEventsStore, an as MIGRATE_TMP_PREFIX, at as s3BucketArn, b as unsupportedFinalSnapshotError, bt as rebuildClientForBucketRegion, c as replayFailedOperations, cn as expectedOwnerParam, ct as s3BucketRegionalDomainName, d as IMPLICIT_DELETE_DEPENDENCIES, dn as derivePartitionAndUrlSuffix, dt as DiffCalculator, en as resolveUseCdkBootstrapAssets, et as readConfigString, f as computeImplicitDeleteEdges, fn as AssemblyReader, ft as describeTypeWithThrottleRetry, g as ccRoutedFinalSnapshotError, gn as AwsClients, gt as DagBuilder, h as buildFinalSnapshotIdentifier, hn as resolveBucketRegion, ht as isThrottlingError, i as DeploymentEventsReader, in as CFN_TEMPLATE_URL_LIMIT, it as requireConfigString, j as red, jn as PartialFailureError, jt as ensureAssetStorage, k as gray, kn as MissingCdkCliError, kt as AssetModeResolver, l as replayRollback, ln as PARTITION_TABLE, lt as s3BucketWebsiteUrl, m as PRE_DELETE_SNAPSHOT_TYPES, mt as isRetryableTransientError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as warnDeprecatedNoPrefixCliFlag, nt as requireConfigArray, o as planFailedOps, on as findLargeInlineResources, ot as s3BucketDomainName, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as processStackMessages, pt as withRetry, q as WAFv2WebACLProvider, qt as getLegacyStateBucketName, r as DeployEngine, rn as CFN_TEMPLATE_BODY_LIMIT, rt as requireConfigObject, s as planRollback, sn as uploadCfnTemplate, st as s3BucketDualStackDomainName, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as stateBucketExistenceConfirmed, tt as replayWarn, u as withResourceDeadline, un as canonicalizeRegion, ut as applyRoleArnIfSet, v as isFinalSnapshotError, vn as resetAwsClients, vt as LockManager, w as isStatefulRecreateTargetSync, wn as DeployCancelledError, wt as WorkGraph, x as makeCanonicalizePropertiesFn, xn as CdkdError, xt as shouldRetainResource, y as refusesFinalSnapshot, yn as setAwsClients, yt as S3StateBackend, z as CloudControlProvider, zt as getDockerCmd } from "./deploy-engine-DuYBir2W.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
4
- import { t as ASGProvider } from "./asg-provider-BNuwJ11q.js";
4
+ import { t as ASGProvider } from "./asg-provider-Cl4ATw1l.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";
@@ -9139,15 +9139,24 @@ var SQSQueueProvider = class {
9139
9139
  }
9140
9140
  }
9141
9141
  /**
9142
- * Construct SQS queue ARN from account/region/queue name
9142
+ * Construct SQS queue ARN from account/region/queue name.
9143
+ *
9144
+ * The partition is derived from the region through the same closed mapping
9145
+ * `${AWS::Partition}` uses (issue #1815) rather than hardcoded to `aws` —
9146
+ * this ARN is recorded into state and served as the queue's `Arn`
9147
+ * attribute, so a commercial literal is structurally valid but wrong in
9148
+ * `aws-cn` / `aws-us-gov` and nothing downstream rejects it.
9143
9149
  */
9144
9150
  async constructArn(queueName) {
9145
9151
  try {
9146
9152
  const accountId = (await this.stsClient.send(new GetCallerIdentityCommand({}))).Account;
9147
- return `arn:aws:sqs:${await this.sqsClient.config.region()}:${accountId}:${queueName}`;
9153
+ const region = await this.sqsClient.config.region();
9154
+ const { partition } = derivePartitionAndUrlSuffix(region);
9155
+ return `arn:${partition}:sqs:${region}:${accountId}:${queueName}`;
9148
9156
  } catch {
9149
9157
  this.logger.warn("Failed to construct SQS ARN from STS, using placeholder");
9150
- return `arn:aws:sqs:unknown:unknown:${queueName}`;
9158
+ const { partition } = derivePartitionAndUrlSuffix(process.env["AWS_REGION"] ?? "");
9159
+ return `arn:${partition}:sqs:unknown:unknown:${queueName}`;
9151
9160
  }
9152
9161
  }
9153
9162
  /**
@@ -15484,10 +15493,16 @@ var DynamoDBGlobalTableProvider = class {
15484
15493
  };
15485
15494
  if (tableArn) await this.applyTagDiff(tableArn, extractLocalTags(previousProperties), extractLocalTags(properties));
15486
15495
  const recordedOldBilling = previousProperties["BillingMode"];
15487
- const recordedOldBillingUsable = recordedOldBilling === void 0 || typeof recordedOldBilling === "string" && recordedOldBilling.trim() !== "";
15488
- const liveBillingMode = describeResp.Table?.BillingModeSummary?.BillingMode ?? "PROVISIONED";
15489
- const oldBilling = recordedOldBillingUsable ? recordedOldBilling ?? "PAY_PER_REQUEST" : liveBillingMode;
15490
- 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.`);
15496
+ const recordedOldBillingAbsent = recordedOldBilling === void 0;
15497
+ const recordedOldBillingUsable = typeof recordedOldBilling === "string" && recordedOldBilling.trim() !== "";
15498
+ const rawLiveBillingMode = describeResp.Table?.BillingModeSummary?.BillingMode;
15499
+ const reportedBillingMode = rawLiveBillingMode === "PROVISIONED" || rawLiveBillingMode === "PAY_PER_REQUEST" ? rawLiveBillingMode : void 0;
15500
+ const liveBillingMode = reportedBillingMode ?? "PROVISIONED";
15501
+ const desiredDeclaresBillingMode = properties["BillingMode"] !== void 0;
15502
+ const seedAbsentFromLive = recordedOldBillingAbsent && desiredDeclaresBillingMode;
15503
+ const oldBilling = recordedOldBillingUsable ? recordedOldBilling : recordedOldBillingAbsent ? seedAbsentFromLive ? liveBillingMode : "PAY_PER_REQUEST" : liveBillingMode;
15504
+ if (!recordedOldBillingUsable && !recordedOldBillingAbsent) 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.`);
15505
+ if (seedAbsentFromLive && oldBilling !== "PAY_PER_REQUEST") this.logger.warn(`AWS::DynamoDB::GlobalTable ${logicalId}: the cdkd state record declares no BillingMode — using ${oldBilling} as the comparison baseline for this update (${reportedBillingMode !== void 0 ? "the mode DescribeTable reports" : "inferred: DescribeTable reports no BillingModeSummary, which means the table was created without an explicit mode"}), so a template declaring a different mode still applies the flip instead of comparing equal to the create-path default.`);
15491
15506
  let billingUnusable = false;
15492
15507
  const requestedBilling = requireConfigString(properties["BillingMode"], "PAY_PER_REQUEST", "AWS::DynamoDB::GlobalTable BillingMode", { onUnusable: (message) => {
15493
15508
  billingUnusable = true;
@@ -15575,6 +15590,7 @@ var DynamoDBGlobalTableProvider = class {
15575
15590
  effectiveProperties = { ...effectiveProperties ?? properties };
15576
15591
  if (recordedOldBilling === void 0) delete effectiveProperties["BillingMode"];
15577
15592
  else effectiveProperties["BillingMode"] = oldBilling;
15593
+ effectiveProperties = retainUnsendableCapacityMembers(effectiveProperties, previousProperties, newBilling);
15578
15594
  }
15579
15595
  const awsOnDemand = describeResp.Table?.OnDemandThroughput;
15580
15596
  const onDemandPayload = {};
@@ -17533,6 +17549,185 @@ function stripProvisionedCapacityKeys(properties) {
17533
17549
  return result;
17534
17550
  }
17535
17551
  /**
17552
+ * The capacity member keys that can only reach AWS while the table is
17553
+ * PROVISIONED, per container level (issue #1738).
17554
+ *
17555
+ * Identical to the set {@link stripProvisionedCapacityKeys} removes, and that
17556
+ * is not a coincidence: both answer "which members are unsendable once the mode
17557
+ * is PAY_PER_REQUEST". The two spellings a pre-#1387 cdkd wrote into state
17558
+ * (`ProvisionedThroughput` / `ProvisionedThroughputOverride`) are included for
17559
+ * the same reason — a state replay is exactly that population.
17560
+ */
17561
+ const PROVISIONED_ONLY_CAPACITY_KEYS = {
17562
+ table: ["WriteProvisionedThroughputSettings"],
17563
+ index: [
17564
+ "WriteProvisionedThroughputSettings",
17565
+ "ReadProvisionedThroughputSettings",
17566
+ "ProvisionedThroughput"
17567
+ ],
17568
+ replica: ["ReadProvisionedThroughputSettings", "ProvisionedThroughputOverride"],
17569
+ replicaIndex: ["ReadProvisionedThroughputSettings", "ProvisionedThroughputOverride"]
17570
+ };
17571
+ /**
17572
+ * The mirror set: capacity members that can only reach AWS while the table is
17573
+ * PAY_PER_REQUEST (issue #1738).
17574
+ *
17575
+ * `stripProvisionedCapacityKeys` has no equivalent because the create arm it
17576
+ * serves always substitutes PAY_PER_REQUEST — the mode under which these ARE
17577
+ * sent, which is why its header says they are deliberately not stripped. On the
17578
+ * UPDATE path the kept mode can be PROVISIONED, and then it is this half that
17579
+ * never leaves the process.
17580
+ */
17581
+ const ON_DEMAND_ONLY_CAPACITY_KEYS = {
17582
+ table: ["WriteOnDemandThroughputSettings"],
17583
+ index: [
17584
+ "WriteOnDemandThroughputSettings",
17585
+ "ReadOnDemandThroughputSettings",
17586
+ "OnDemandThroughput"
17587
+ ],
17588
+ replica: ["ReadOnDemandThroughputSettings", "OnDemandThroughputOverride"],
17589
+ replicaIndex: ["ReadOnDemandThroughputSettings", "OnDemandThroughputOverride"]
17590
+ };
17591
+ /**
17592
+ * Replace every capacity member the KEPT billing mode cannot send with the
17593
+ * PREVIOUS record's value (issue #1738).
17594
+ *
17595
+ * Called from `update()`'s `BillingMode` warn-and-KEEP arm, where an unusable
17596
+ * desired mode suppresses the flip: AWS is left holding the mode it already
17597
+ * had, so every block belonging to the OTHER mode was silently dropped on the
17598
+ * way out while the engine recorded what the template declared.
17599
+ *
17600
+ * **The split is per MEMBER against the KEPT mode**, and each side was read off
17601
+ * the gates in this file rather than off the member's name-shape (the
17602
+ * name-shape trap #1726 records — an on-demand ceiling looks like capacity and
17603
+ * goes on the wire under precisely the on-demand mode):
17604
+ *
17605
+ * - KEPT `PROVISIONED` — the on-demand half is unsendable. Step 3's table-level
17606
+ * `OnDemandThroughput` is gated on `newBilling !== 'PROVISIONED'`;
17607
+ * `toSdkGlobalSecondaryIndexes` and `toSdkReplicaThroughputOverrides` both
17608
+ * drop their on-demand branch under PROVISIONED. The provisioned half is NOT
17609
+ * retained: step 4b and step 6b's auto-scaling reconcile are live whenever
17610
+ * either side is PROVISIONED, and step 6's GSI diff carries per-index
17611
+ * `ProvisionedThroughput` — those blocks really are applied.
17612
+ * - KEPT `PAY_PER_REQUEST` — the provisioned half is unsendable. The table-level
17613
+ * `ProvisionedThroughput` rides step 4's flip and nothing else, the two
17614
+ * auto-scaling reconciles are gated on either side being PROVISIONED, and both
17615
+ * translators drop their provisioned branch. The on-demand half is NOT
17616
+ * retained: step 3 sends the table-level ceilings and step 6 the per-index
17617
+ * ones under exactly this mode.
17618
+ *
17619
+ * One residual is deliberately NOT covered, because the suppression did not
17620
+ * create it: the TABLE-level provisioned capacity NUMBER rides step 4's flip
17621
+ * and nothing else, so on ANY non-flip PROVISIONED -> PROVISIONED update it is
17622
+ * unsent while its `*CapacityAutoScalingSettings` sibling in the same block IS
17623
+ * reconciled. Retaining the block wholesale under a kept PROVISIONED mode would
17624
+ * therefore erase a real auto-scaling update to hide a pre-existing gap — so
17625
+ * the block stays as declared and the gap stays where it already lives.
17626
+ *
17627
+ * Retaining rather than DROPPING is the UPDATE row of
17628
+ * `.claude/rules/providers.md`: the call never ran, so AWS still holds the
17629
+ * previously-applied block and that IS what state should describe. Dropping
17630
+ * would leave a later template that REMOVES the block deriving no removal, so
17631
+ * the live setting would survive forever.
17632
+ *
17633
+ * **The previous side is VALIDATED before it is retained** (the #1653-review
17634
+ * rule) through `asRecord` — the SAME predicate every wire read of these blocks
17635
+ * applies, so the two cannot disagree about what a bare string / an array / a
17636
+ * number means. Be honest about what that buys: it refuses exactly the
17637
+ * not-a-plain-object shapes, and a block whose NUMERIC member is an unresolved
17638
+ * intrinsic passes it, because the wire accepts that block too (and then sends
17639
+ * nothing for the member, which is the same "AWS keeps what it had" outcome).
17640
+ * When the previous side is unusable OR absent the key is DROPPED — there is no
17641
+ * value cdkd can vouch for either way.
17642
+ *
17643
+ * COPIES each retained block one level rather than aliasing it: the rollback
17644
+ * executor spreads the returned bag shallowly, so handing back the previous
17645
+ * record's own object would leave two state records sharing one mutable value.
17646
+ * Scope that claim honestly — it covers the retained blocks only; every other
17647
+ * key of the input bag stays aliased, exactly as the sibling arms leave it.
17648
+ *
17649
+ * Deliberately NOT paired with a `canonicalizeDesiredProperties` twin: this is
17650
+ * a SKIP, not a narrowing, so there is no pure function of the desired bag to
17651
+ * fold both comparison sides with, and folding one would derive a REMOVAL of a
17652
+ * live capacity block from a template whose only fault is `BillingMode`.
17653
+ *
17654
+ * Pure and non-mutating at every level.
17655
+ */
17656
+ function retainUnsendableCapacityMembers(desired, previous, keptBillingMode) {
17657
+ const unsendable = keptBillingMode === "PROVISIONED" ? ON_DEMAND_ONLY_CAPACITY_KEYS : PROVISIONED_ONLY_CAPACITY_KEYS;
17658
+ const result = retainCapacityKeys({ ...desired }, previous, unsendable.table);
17659
+ const indexes = result["GlobalSecondaryIndexes"];
17660
+ if (Array.isArray(indexes)) {
17661
+ const previousIndexes = indexCapacityContainers(previous["GlobalSecondaryIndexes"], "IndexName");
17662
+ result["GlobalSecondaryIndexes"] = indexes.map((entry) => {
17663
+ const gsi = asRecord$1(entry);
17664
+ if (!gsi) return entry;
17665
+ const previousGsi = previousIndexes.get(gsi["IndexName"]);
17666
+ return retainCapacityKeys({ ...gsi }, previousGsi, unsendable.index);
17667
+ });
17668
+ }
17669
+ const replicas = result["Replicas"];
17670
+ if (Array.isArray(replicas)) {
17671
+ const previousReplicas = indexCapacityContainers(previous["Replicas"], "Region");
17672
+ result["Replicas"] = replicas.map((entry) => {
17673
+ const replica = asRecord$1(entry);
17674
+ if (!replica) return entry;
17675
+ const previousReplica = previousReplicas.get(replica["Region"]);
17676
+ const copy = retainCapacityKeys({ ...replica }, previousReplica, unsendable.replica);
17677
+ const replicaIndexes = copy["GlobalSecondaryIndexes"];
17678
+ if (Array.isArray(replicaIndexes)) {
17679
+ const previousReplicaIndexes = indexCapacityContainers(previousReplica?.["GlobalSecondaryIndexes"], "IndexName");
17680
+ const kept = replicaIndexes.map((indexEntry) => {
17681
+ const gsi = asRecord$1(indexEntry);
17682
+ if (!gsi) return indexEntry;
17683
+ const previousGsi = previousReplicaIndexes.get(gsi["IndexName"]);
17684
+ return retainCapacityKeys({ ...gsi }, previousGsi, unsendable.replicaIndex);
17685
+ }).filter((entry) => {
17686
+ const gsi = asRecord$1(entry);
17687
+ if (!gsi) return true;
17688
+ return Object.keys(gsi).some((k) => k !== "IndexName");
17689
+ });
17690
+ if (kept.length > 0) copy["GlobalSecondaryIndexes"] = kept;
17691
+ else delete copy["GlobalSecondaryIndexes"];
17692
+ }
17693
+ return copy;
17694
+ });
17695
+ }
17696
+ return result;
17697
+ }
17698
+ /**
17699
+ * Retain (or drop) one container's unsendable capacity members, in place on a
17700
+ * bag the caller already copied.
17701
+ *
17702
+ * The counterpart container being `undefined` — no matching index / replica in
17703
+ * the previous record — is the ABSENT case, not a special one: there is nothing
17704
+ * to vouch for, so the key is dropped exactly as it is for an unusable value.
17705
+ */
17706
+ function retainCapacityKeys(target, previousContainer, keys) {
17707
+ for (const key of keys) {
17708
+ const previousValue = asRecord$1(previousContainer?.[key]);
17709
+ if (previousValue) target[key] = { ...previousValue };
17710
+ else delete target[key];
17711
+ }
17712
+ return target;
17713
+ }
17714
+ /**
17715
+ * Index a previous-side array by its identity member, so a per-index /
17716
+ * per-replica retain reads the counterpart entry rather than the positional
17717
+ * one — AWS does not guarantee readback order and a template edit can reorder
17718
+ * either list.
17719
+ */
17720
+ function indexCapacityContainers(value, identityKey) {
17721
+ const byIdentity = /* @__PURE__ */ new Map();
17722
+ if (!Array.isArray(value)) return byIdentity;
17723
+ for (const entry of value) {
17724
+ const record = asRecord$1(entry);
17725
+ const identity = record?.[identityKey];
17726
+ if (record && typeof identity === "string") byIdentity.set(identity, record);
17727
+ }
17728
+ return byIdentity;
17729
+ }
17730
+ /**
17536
17731
  * True when a CFn sub-object is nothing but an unresolved intrinsic
17537
17732
  * (`{"Fn::If": […]}` / `{"Ref": …}`), i.e. the template DID declare the block
17538
17733
  * but its value could not be resolved to a shape with members.
@@ -18239,14 +18434,23 @@ var LogsLogGroupProvider = class {
18239
18434
  }
18240
18435
  }
18241
18436
  /**
18242
- * Build log group ARN from name
18437
+ * Build log group ARN from name.
18438
+ *
18439
+ * The partition is derived from the region through the same closed mapping
18440
+ * `${AWS::Partition}` uses (issue #1815) rather than hardcoded to `aws` —
18441
+ * this ARN is recorded into state and served as the log group's `Arn`
18442
+ * attribute, so a commercial literal is structurally valid but wrong in
18443
+ * `aws-cn` / `aws-us-gov` and nothing downstream rejects it.
18243
18444
  */
18244
18445
  async buildArn(logGroupName) {
18245
18446
  try {
18246
18447
  const accountId = (await this.stsClient.send(new GetCallerIdentityCommand({}))).Account;
18247
- return `arn:aws:logs:${await this.logsClient.config.region() || process.env["AWS_REGION"] || "us-east-1"}:${accountId}:log-group:${logGroupName}:*`;
18448
+ const region = await this.logsClient.config.region() || process.env["AWS_REGION"] || "us-east-1";
18449
+ const { partition } = derivePartitionAndUrlSuffix(region);
18450
+ return `arn:${partition}:logs:${region}:${accountId}:log-group:${logGroupName}:*`;
18248
18451
  } catch {
18249
- return `arn:aws:logs:unknown:unknown:log-group:${logGroupName}:*`;
18452
+ const { partition } = derivePartitionAndUrlSuffix(process.env["AWS_REGION"] ?? "");
18453
+ return `arn:${partition}:logs:unknown:unknown:log-group:${logGroupName}:*`;
18250
18454
  }
18251
18455
  }
18252
18456
  /**
@@ -18507,9 +18711,12 @@ var CloudWatchAlarmProvider = class {
18507
18711
  this.logger.debug(`Failed to describe alarm ${alarmName}, constructing ARN from config: ${error instanceof Error ? error.message : String(error)}`);
18508
18712
  }
18509
18713
  try {
18510
- return `arn:aws:cloudwatch:${await this.cloudWatchClient.config.region() || process.env["AWS_REGION"] || "us-east-1"}:*:alarm:${alarmName}`;
18714
+ const region = await this.cloudWatchClient.config.region() || process.env["AWS_REGION"] || "us-east-1";
18715
+ const { partition } = derivePartitionAndUrlSuffix(region);
18716
+ return `arn:${partition}:cloudwatch:${region}:*:alarm:${alarmName}`;
18511
18717
  } catch {
18512
- return `arn:aws:cloudwatch:*:*:alarm:${alarmName}`;
18718
+ const { partition } = derivePartitionAndUrlSuffix(process.env["AWS_REGION"] ?? "");
18719
+ return `arn:${partition}:cloudwatch:*:*:alarm:${alarmName}`;
18513
18720
  }
18514
18721
  }
18515
18722
  /**
@@ -24797,13 +25004,23 @@ var ApiGatewayProvider = class ApiGatewayProvider {
24797
25004
  /**
24798
25005
  * Build the ARN for an API Gateway Stage, used for tag mutations.
24799
25006
  *
24800
- * Format: `arn:aws:apigateway:{region}::/restapis/{restApiId}/stages/{stageName}`.
25007
+ * Format:
25008
+ * `arn:{partition}:apigateway:{region}::/restapis/{restApiId}/stages/{stageName}`.
24801
25009
  * The double colon (`::`) is intentional: API Gateway tagging uses an
24802
25010
  * account-id-less ARN.
25011
+ *
25012
+ * The partition is derived from the region through the same closed mapping
25013
+ * `${AWS::Partition}` uses (issue #1815) rather than hardcoded to `aws`.
25014
+ * Unlike the pure attribute builders this one is passed straight to
25015
+ * `TagResource` / `UntagResource` as `resourceArn`, so in `aws-cn` /
25016
+ * `aws-us-gov` a commercial literal does not just record a wrong string —
25017
+ * it aims the tag mutation at an ARN that names no stage.
24803
25018
  */
24804
25019
  async buildStageArn(restApiId, stageName) {
24805
25020
  try {
24806
- return `arn:aws:apigateway:${await this.apiGatewayClient.config.region()}::/restapis/${restApiId}/stages/${stageName}`;
25021
+ const region = await this.apiGatewayClient.config.region();
25022
+ const { partition } = derivePartitionAndUrlSuffix(region);
25023
+ return `arn:${partition}:apigateway:${region}::/restapis/${restApiId}/stages/${stageName}`;
24807
25024
  } catch {
24808
25025
  return;
24809
25026
  }
@@ -38556,11 +38773,19 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
38556
38773
  }
38557
38774
  /**
38558
38775
  * Build a namespace ARN from namespace ID.
38559
- * Format: arn:aws:servicediscovery:{region}:{account}:namespace/{namespaceId}
38776
+ * Format: `arn:{partition}:servicediscovery:{region}:{account}:namespace/{namespaceId}`
38777
+ *
38778
+ * The partition is derived from the region through the same closed mapping
38779
+ * `${AWS::Partition}` uses (issue #1815) rather than hardcoded to `aws` —
38780
+ * this ARN is recorded into state and served as the namespace's `Arn`
38781
+ * attribute, so a commercial-partition literal is silently wrong (but
38782
+ * structurally valid, hence unrejected) in `aws-cn` / `aws-us-gov`.
38560
38783
  */
38561
38784
  async buildNamespaceArn(namespaceId) {
38562
38785
  const accountId = (await this.getStsClient().send(new GetCallerIdentityCommand({}))).Account || "";
38563
- return `arn:aws:servicediscovery:${await this.getClient().config.region()}:${accountId}:namespace/${namespaceId}`;
38786
+ const region = await this.getClient().config.region();
38787
+ const { partition } = derivePartitionAndUrlSuffix(region);
38788
+ return `arn:${partition}:servicediscovery:${region}:${accountId}:namespace/${namespaceId}`;
38564
38789
  }
38565
38790
  };
38566
38791
 
@@ -43208,12 +43433,35 @@ var BudgetsBudgetProvider = class {
43208
43433
  }
43209
43434
  /**
43210
43435
  * Budget ARN for the tagging APIs (`ListTagsForResource` / `TagResource` /
43211
- * `UntagResource`). Budgets ARNs carry no region component. The `aws`
43212
- * partition is assumed — consistent with other providers that construct
43213
- * ARNs; non-`aws` partitions are not supported by cdkd today.
43214
- */
43215
- budgetArn(accountId, budgetName) {
43216
- return `arn:aws:budgets::${accountId}:budget/${budgetName}`;
43436
+ * `UntagResource`), and the `Arn` attribute recorded into state.
43437
+ *
43438
+ * Budgets ARNs carry no region component, but they DO carry a partition, so
43439
+ * a region is still needed to name it. It is read from the RESOLVED region
43440
+ * of the very client that receives this ARN — the same
43441
+ * `getClient().config.region()` the `delete()` path already consults for
43442
+ * its `assertRegionMatch` guard — and routed through the closed mapping
43443
+ * `${AWS::Partition}` uses (issue #1815).
43444
+ *
43445
+ * Asking the CLIENT rather than `providerRegion` is what makes the ARN and
43446
+ * its consumer agree. `providerRegion` is `process.env['AWS_REGION']`, and
43447
+ * when that is unset `getClient()` builds `new BudgetsClient({})`, leaving
43448
+ * the SDK to resolve the region from its OWN chain (`AWS_DEFAULT_REGION`,
43449
+ * the `~/.aws/config` profile). So a profile-configured `cn-north-1` /
43450
+ * `us-gov-*` caller would have derived the COMMERCIAL partition from an
43451
+ * empty string while the client itself talked to a non-commercial endpoint
43452
+ * — on the PRIMARY path, not a fallback.
43453
+ *
43454
+ * The `aws` partition USED to be hardcoded here on the stated assumption
43455
+ * that non-`aws` partitions were unsupported. That was wrong in the quiet
43456
+ * direction: the literal is structurally valid everywhere, so in `aws-cn` /
43457
+ * `aws-us-gov` nothing rejected it — the ARN was simply recorded into state
43458
+ * and handed to `TagResource` naming no budget. A client whose region is
43459
+ * unset or unrecognized still derives to `aws`, so commercial output is
43460
+ * unchanged byte for byte.
43461
+ */
43462
+ async budgetArn(accountId, budgetName) {
43463
+ const { partition } = derivePartitionAndUrlSuffix(await this.getClient().config.region() ?? "");
43464
+ return `arn:${partition}:budgets::${accountId}:budget/${budgetName}`;
43217
43465
  }
43218
43466
  /**
43219
43467
  * Convert a CFn `Spend` shape to the SDK shape. CFn templates carry
@@ -43369,7 +43617,7 @@ var BudgetsBudgetProvider = class {
43369
43617
  this.logger.debug(`Successfully created budget ${logicalId}: ${name}`);
43370
43618
  return {
43371
43619
  physicalId: name,
43372
- attributes: { Arn: this.budgetArn(accountId, name) }
43620
+ attributes: { Arn: await this.budgetArn(accountId, name) }
43373
43621
  };
43374
43622
  } catch (error) {
43375
43623
  if (error instanceof ProvisioningError) throw error;
@@ -43396,7 +43644,7 @@ var BudgetsBudgetProvider = class {
43396
43644
  return {
43397
43645
  physicalId,
43398
43646
  wasReplaced: false,
43399
- attributes: { Arn: this.budgetArn(accountId, physicalId) }
43647
+ attributes: { Arn: await this.budgetArn(accountId, physicalId) }
43400
43648
  };
43401
43649
  } catch (error) {
43402
43650
  if (error instanceof ProvisioningError) throw error;
@@ -43512,7 +43760,7 @@ var BudgetsBudgetProvider = class {
43512
43760
  async reconcileResourceTags(accountId, budgetName, oldTags, newTags) {
43513
43761
  const sortedJson = (tags) => JSON.stringify([...tags].sort((a, b) => (a.Key ?? "").localeCompare(b.Key ?? "")));
43514
43762
  if (sortedJson(oldTags) === sortedJson(newTags)) return;
43515
- const arn = this.budgetArn(accountId, budgetName);
43763
+ const arn = await this.budgetArn(accountId, budgetName);
43516
43764
  const newKeys = new Set(newTags.map((t) => t.Key));
43517
43765
  const removedKeys = oldTags.map((t) => t.Key).filter((k) => {
43518
43766
  return typeof k === "string" && !newKeys.has(k);
@@ -43555,7 +43803,7 @@ var BudgetsBudgetProvider = class {
43555
43803
  /**
43556
43804
  * `Fn::GetAtt` support. CloudFormation documents no attributes for
43557
43805
  * `AWS::Budgets::Budget`; `Arn` is served as a cdkd convenience (computed
43558
- * — Budgets ARNs are `arn:aws:budgets::{account}:budget/{name}`, verified
43806
+ * — Budgets ARNs are `arn:{partition}:budgets::{account}:budget/{name}`, verified
43559
43807
  * to exist via `DescribeBudget` first).
43560
43808
  */
43561
43809
  async getAttribute(physicalId, resourceType, attributeName) {
@@ -43566,7 +43814,7 @@ var BudgetsBudgetProvider = class {
43566
43814
  AccountId: accountId,
43567
43815
  BudgetName: physicalId
43568
43816
  }));
43569
- return this.budgetArn(accountId, physicalId);
43817
+ return await this.budgetArn(accountId, physicalId);
43570
43818
  } catch (error) {
43571
43819
  const cause = error instanceof Error ? error : void 0;
43572
43820
  throw new ProvisioningError(`Failed to resolve Arn for budget ${physicalId}: ${cause?.message ?? String(error)}`, resourceType, physicalId, physicalId, cause);
@@ -43591,7 +43839,7 @@ var BudgetsBudgetProvider = class {
43591
43839
  }));
43592
43840
  return {
43593
43841
  physicalId: explicit,
43594
- attributes: { Arn: this.budgetArn(accountId, explicit) }
43842
+ attributes: { Arn: await this.budgetArn(accountId, explicit) }
43595
43843
  };
43596
43844
  } catch (error) {
43597
43845
  if (error instanceof NotFoundException$5) return null;
@@ -51387,13 +51635,33 @@ var S3DirectoryBucketProvider = class {
51387
51635
  return { Arn: await this.buildBucketArn(bucketName) };
51388
51636
  }
51389
51637
  /**
51390
- * Directory bucket ARN: `arn:aws:s3express:{region}:{account}:bucket/{name}`.
51638
+ * Directory bucket ARN:
51639
+ * `arn:{partition}:s3express:{region}:{account}:bucket/{name}`.
51391
51640
  * Also the `ResourceArn` for the S3 Control tag operations. Callers that
51392
51641
  * already resolved the account (for the S3 Control `AccountId` parameter)
51393
51642
  * pass it in so the STS lookup runs once per operation.
51643
+ *
51644
+ * The partition is derived from the region through the same closed mapping
51645
+ * `${AWS::Partition}` uses (issue #1815) rather than hardcoded to `aws`.
51646
+ * Here it is load-bearing TWICE over: the value is served as the bucket's
51647
+ * `Arn` attribute AND passed to S3 Control as the `ResourceArn` of a tag
51648
+ * mutation, so outside the commercial partition the wrong literal does not
51649
+ * merely record a bad string — it aims the tag call at an ARN that does not
51650
+ * name this bucket.
51651
+ *
51652
+ * KNOWN BOUND (pre-existing, not introduced by #1815): the region — and now
51653
+ * the partition derived from it — comes from `s3Client`, while the tag
51654
+ * calls that consume this ARN go through `s3ControlClient`, built from
51655
+ * `providerRegion` (`process.env['AWS_REGION']`). Those two can disagree,
51656
+ * so the ARN can name a different region than the client receiving it. The
51657
+ * region SEGMENT already had this exposure before the partition rode along
51658
+ * with it; unifying the two clients' region resolution is a separate change.
51394
51659
  */
51395
51660
  async buildBucketArn(bucketName, accountId) {
51396
- return `arn:aws:s3express:${await this.getRegion()}:${accountId ?? await this.getAccountId()}:bucket/${bucketName}`;
51661
+ const region = await this.getRegion();
51662
+ const account = accountId ?? await this.getAccountId();
51663
+ const { partition } = derivePartitionAndUrlSuffix(region);
51664
+ return `arn:${partition}:s3express:${region}:${account}:bucket/${bucketName}`;
51397
51665
  }
51398
51666
  /**
51399
51667
  * Create an S3 Express Directory Bucket
@@ -76252,7 +76520,7 @@ function createMigrateCommand() {
76252
76520
  */
76253
76521
  function buildProgram() {
76254
76522
  const program = new Command();
76255
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.0");
76523
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.2");
76256
76524
  program.hook("preAction", (_thisCommand, actionCommand) => {
76257
76525
  const { profile } = actionCommand.optsWithGlobals();
76258
76526
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;