@go-to-k/cdkd 0.284.25 → 0.284.26

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 isInterruptedWaitError, $n as LocalMigrateError, $t as escapeRegExp, A as isStatefulRecreateTargetSync, An as CFN_TEMPLATE_BODY_LIMIT, At as s3BucketArn, B as collectPublishedOutputNames, Bn as processStackMessages, Bt as DagBuilder, C as createPreDeleteFinalSnapshot, Cn as resolveCaptureObservedState, Ct as TEMPLATE_SOURCED_RULES, D as makeCanonicalizePropertiesFn, Dn as resolveUseCdkBootstrapAssets, Dt as maskSecretsInText, E as unsupportedFinalSnapshotError, En as resolveStateBucketWithDefaultAndSource, Et as isSingleDynamicReferenceToken, F as gray, Fn as expectedOwnerParam, Ft as applyRoleArnIfSet, G as IAMRoleProvider, Gn as resetAwsClients, Gt as shouldRetainResource, H as isExportAliasCollision, Hn as resolveBucketRegion, Ht as LockManager, I as green, In as PARTITION_TABLE, It as DiffCalculator, J as ProviderRegistry, Jn as CdkdError, Jt as WorkGraph, K as collectInlinePolicyNamesManagedBySiblings, Kn as setAwsClients, Kt as AssetPublisher, L as red, Ln as canonicalizeRegion, Lt as INTRINSIC_KEYS, M as formatResourceLine, Mn as MIGRATE_TMP_PREFIX, Mt as s3BucketDualStackDomainName, N as bold, Nn as findLargeInlineResources, Nt as s3BucketRegionalDomainName, O as extractDeploymentEventError, On as stateBucketExistenceConfirmed, Ot as redactSecretsForState, P as cyan, Pn as uploadCfnTemplate, Pt as s3BucketWebsiteUrl, Q as endCommandInterruptScope, Qn as LocalInvokeBuildError$1, Qt as rewriteTemplateAssetReferences, R as yellow, Rn as derivePartitionAndUrlSuffix, Rt as describeTypeWithThrottleRetry, S as ccRoutedFinalSnapshotError, Sn as resolveAutoAssetStorage, St as STATE_SOURCED_READBACK_RULES, T as refusesFinalSnapshot, Tn as resolveStateBucketWithDefault, Tt as dynamicReferenceTokens, U as secretBearingStateKeyWarning, Un as AwsClients, Ut as S3StateBackend, V as exportAliasCollisionScrubWarning, Vt as TemplateParser, W as stateKeySecretExposure, Wn as getAwsClients, Wt as rebuildClientForBucketRegion, X as findSilentDropProperties, Xt as createAssetRedirectResolver, Y as findActionableSilentDrops, Yt as buildAssetRedirectMap, Z as beginCommandInterruptScope, Zn as DeployCancelledError, Zt as loadPublishableAssetManifest, _ as IMPLICIT_DELETE_DEPENDENCIES, _n as Synthesizer, _r as isRetryableTransientError, _t as replayWarn, a as DeploymentEventsStore, an as parseBootstrapMarker, ar as ProvisioningError, at as IntrinsicFunctionResolver, b as PRE_DELETE_SNAPSHOT_TYPES, bn as getLegacyStateBucketName, bt as requireConfigString, c as producerRegionsFromState, cn as validateContainerRepoName, cr as StackHasActiveImportsError, ct as refStateLookupFromResource, d as updatePartialMessage, dn as formatDockerLoginError, dt as resolveExplicitPhysicalId, en as stripControlChars, er as LocalStartServiceError, et as startInterruptWatch, f as updatePartialReason, fn as getDockerCmd, ft as assertRegionMatch, g as maskingRetryLogger, gn as getDockerImageBySourceHash, gr as isMarkedNonRetryable, gt as readConfigString, h as withResourceDeadline, hn as AssetManifestLoader, hr as withErrorHandling, ht as configStringRefusal, i as DeploymentEventsReader, in as getBootstrapMarkerKey, ir as PartialFailureError, it as isTerminationProtectionPropagationError, j as renderStatefulReason, jn as CFN_TEMPLATE_URL_LIMIT, jt as s3BucketDomainName, k as MULTI_REGION_RECREATE_BLOCKED_TYPES, kn as warnDeprecatedNoPrefixCliFlag, kt as scrubResourceRecord, l as replayFailedOperations, ln as buildDenyExternalAccessPolicy, lr as StackTerminationProtectionError, lt as WAFv2WebACLProvider, m as deleteSkipReason, mn as runDockerStreaming, mr as normalizeAwsError, mt as configBooleanRefusal, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as BOOTSTRAP_MARKER_PREFIX, nr as MissingCdkCliError, nt as slowCcOperationTimeoutMs, o as planFailedOps, on as readBootstrapMarkerBody, or as ResourceTimeoutError, ot as cfnRefValueFromPhysicalId, p as UNSPECIFIED_SKIP_REASON, pn as runDockerForeground, pt as coerceCfnBoolean, q as clearOnUpdateRemoval, qt as stringifyValue, r as DeployEngine, rn as ensureAssetStorage, rr as NestedStackChildDirectDestroyError, rt as disableInstanceApiTermination, s as planRollback, sn as validateAssetBucketName, sr as ResourceUpdateNotSupportedError, st as getAccountInfo, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as AssetModeResolver, tt as CloudControlProvider, u as replayRollback, un as buildDockerImage, ut as normalizeAwsTagsToCfn, v as computeImplicitDeleteEdges, vn as synthesisStatusMessage, vr as isThrottlingError, vt as requireConfigArray, w as isFinalSnapshotError, wn as resolveSkipPrefix, wt as createSecretMasker, x as buildFinalSnapshotIdentifier, xn as resolveApp, xt as STATE_SOURCED_CROSS_GENERATION_RULES, y as ATOMIC_FINAL_SNAPSHOT_TYPES, yn as getDefaultStateBucketName, yr as markNonRetryable, yt as requireConfigObject, z as collectDeclaredOutputNames, zn as AssemblyReader, zt as withRetry } from "./deploy-engine-BpZWPiaL.js";
2
+ import { $ as isInterruptedWaitError, $n as LocalMigrateError, $t as escapeRegExp, A as isStatefulRecreateTargetSync, An as CFN_TEMPLATE_BODY_LIMIT, At as s3BucketArn, B as collectPublishedOutputNames, Bn as processStackMessages, Bt as DagBuilder, C as createPreDeleteFinalSnapshot, Cn as resolveCaptureObservedState, Ct as TEMPLATE_SOURCED_RULES, D as makeCanonicalizePropertiesFn, Dn as resolveUseCdkBootstrapAssets, Dt as maskSecretsInText, E as unsupportedFinalSnapshotError, En as resolveStateBucketWithDefaultAndSource, Et as isSingleDynamicReferenceToken, F as gray, Fn as expectedOwnerParam, Ft as applyRoleArnIfSet, G as IAMRoleProvider, Gn as resetAwsClients, Gt as shouldRetainResource, H as isExportAliasCollision, Hn as resolveBucketRegion, Ht as LockManager, I as green, In as PARTITION_TABLE, It as DiffCalculator, J as ProviderRegistry, Jn as CdkdError, Jt as WorkGraph, K as collectInlinePolicyNamesManagedBySiblings, Kn as setAwsClients, Kt as AssetPublisher, L as red, Ln as canonicalizeRegion, Lt as INTRINSIC_KEYS, M as formatResourceLine, Mn as MIGRATE_TMP_PREFIX, Mt as s3BucketDualStackDomainName, N as bold, Nn as findLargeInlineResources, Nt as s3BucketRegionalDomainName, O as extractDeploymentEventError, On as stateBucketExistenceConfirmed, Ot as redactSecretsForState, P as cyan, Pn as uploadCfnTemplate, Pt as s3BucketWebsiteUrl, Q as endCommandInterruptScope, Qn as LocalInvokeBuildError$1, Qt as rewriteTemplateAssetReferences, R as yellow, Rn as derivePartitionAndUrlSuffix, Rt as describeTypeWithThrottleRetry, S as ccRoutedFinalSnapshotError, Sn as resolveAutoAssetStorage, St as STATE_SOURCED_READBACK_RULES, T as refusesFinalSnapshot, Tn as resolveStateBucketWithDefault, Tt as dynamicReferenceTokens, U as secretBearingStateKeyWarning, Un as AwsClients, Ut as S3StateBackend, V as exportAliasCollisionScrubWarning, Vt as TemplateParser, W as stateKeySecretExposure, Wn as getAwsClients, Wt as rebuildClientForBucketRegion, X as findSilentDropProperties, Xt as createAssetRedirectResolver, Y as findActionableSilentDrops, Yt as buildAssetRedirectMap, Z as beginCommandInterruptScope, Zn as DeployCancelledError, Zt as loadPublishableAssetManifest, _ as IMPLICIT_DELETE_DEPENDENCIES, _n as Synthesizer, _r as isRetryableTransientError, _t as replayWarn, a as DeploymentEventsStore, an as parseBootstrapMarker, ar as ProvisioningError, at as IntrinsicFunctionResolver, b as PRE_DELETE_SNAPSHOT_TYPES, bn as getLegacyStateBucketName, bt as requireConfigString, c as producerRegionsFromState, cn as validateContainerRepoName, cr as StackHasActiveImportsError, ct as refStateLookupFromResource, d as updatePartialMessage, dn as formatDockerLoginError, dt as resolveExplicitPhysicalId, en as stripControlChars, er as LocalStartServiceError, et as startInterruptWatch, f as updatePartialReason, fn as getDockerCmd, ft as assertRegionMatch, g as maskingRetryLogger, gn as getDockerImageBySourceHash, gr as isMarkedNonRetryable, gt as readConfigString, h as withResourceDeadline, hn as AssetManifestLoader, hr as withErrorHandling, ht as configStringRefusal, i as DeploymentEventsReader, in as getBootstrapMarkerKey, ir as PartialFailureError, it as isTerminationProtectionPropagationError, j as renderStatefulReason, jn as CFN_TEMPLATE_URL_LIMIT, jt as s3BucketDomainName, k as MULTI_REGION_RECREATE_BLOCKED_TYPES, kn as warnDeprecatedNoPrefixCliFlag, kt as scrubResourceRecord, l as replayFailedOperations, ln as buildDenyExternalAccessPolicy, lr as StackTerminationProtectionError, lt as WAFv2WebACLProvider, m as deleteSkipReason, mn as runDockerStreaming, mr as normalizeAwsError, mt as configBooleanRefusal, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as BOOTSTRAP_MARKER_PREFIX, nr as MissingCdkCliError, nt as slowCcOperationTimeoutMs, o as planFailedOps, on as readBootstrapMarkerBody, or as ResourceTimeoutError, ot as cfnRefValueFromPhysicalId, p as UNSPECIFIED_SKIP_REASON, pn as runDockerForeground, pt as coerceCfnBoolean, q as clearOnUpdateRemoval, qt as stringifyValue, r as DeployEngine, rn as ensureAssetStorage, rr as NestedStackChildDirectDestroyError, rt as disableInstanceApiTermination, s as planRollback, sn as validateAssetBucketName, sr as ResourceUpdateNotSupportedError, st as getAccountInfo, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as AssetModeResolver, tt as CloudControlProvider, u as replayRollback, un as buildDockerImage, ut as normalizeAwsTagsToCfn, v as computeImplicitDeleteEdges, vn as synthesisStatusMessage, vr as isThrottlingError, vt as requireConfigArray, w as isFinalSnapshotError, wn as resolveSkipPrefix, wt as createSecretMasker, x as buildFinalSnapshotIdentifier, xn as resolveApp, xt as STATE_SOURCED_CROSS_GENERATION_RULES, y as ATOMIC_FINAL_SNAPSHOT_TYPES, yn as getDefaultStateBucketName, yr as markNonRetryable, yt as requireConfigObject, z as collectDeclaredOutputNames, zn as AssemblyReader, zt as withRetry } from "./deploy-engine-DNCs229s.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as getCurrentStackName, 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-B2lPfJBN.js";
4
+ import { t as ASGProvider } from "./asg-provider-CS5j-5z6.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";
@@ -865,6 +865,165 @@ function namedCliRegion(optionRegion) {
865
865
  function rawCliRegion(optionRegion) {
866
866
  return optionRegion || process.env["AWS_REGION"] || void 0;
867
867
  }
868
+ /**
869
+ * The region a command OPERATES on.
870
+ *
871
+ * Order: `--region` -> `AWS_REGION` -> `AWS_DEFAULT_REGION` -> the region the
872
+ * AWS SDK's own chain resolves (i.e. the selected profile's `region =` line)
873
+ * -> `us-east-1`.
874
+ *
875
+ * The profile step is what issue
876
+ * [#2029](https://github.com/go-to-k/cdkd/issues/2029) adds, and it removes an
877
+ * incoherence rather than adding a preference. cdkd ALREADY consulted the
878
+ * profile: every command builds its pre-flight bag as
879
+ * `new AwsClients({ ...(options.region && { region }) })`, so with no flag the
880
+ * bag is region-less and the SDK resolves the profile. Only the region VALUE -
881
+ * the one that keys the state file, keys the bootstrap marker and answers
882
+ * `AWS::Region` in every user template - fell back to the literal. Measured on
883
+ * an env-agnostic stack with `region = ap-northeast-1` in the profile and no
884
+ * flag or env var:
885
+ *
886
+ * ```text
887
+ * State bucket '...' is in 'us-east-1' (client was 'ap-northeast-1'); rebuilding S3 client.
888
+ * Getting state for stack: CdkdBasicExample (us-east-1)
889
+ * Resolved Ref to pseudo parameter: AWS::Region -> us-east-1
890
+ * ```
891
+ *
892
+ * One command, two regions: the pre-flight talked to the profile's region
893
+ * while the resources, their ARNs and their state landed in us-east-1. Only an
894
+ * ENV-AGNOSTIC stack is affected - a stack with `env: { region }` pinned in CDK
895
+ * carries its own region through the Cloud Assembly and never reaches this
896
+ * fallback (`assembly-reader.ts` maps CDK's `unknown-region` to `undefined`).
897
+ *
898
+ * `AWS_DEFAULT_REGION` sits between `AWS_REGION` and the profile because the
899
+ * AWS CLI honours it and the JS SDK does NOT (measured: with only that variable
900
+ * set, `STSClient({}).config.region()` returns the PROFILE's region, not the
901
+ * variable's). Reading it here is what stops cdkd disagreeing with the CLI a
902
+ * user just ran.
903
+ *
904
+ * Returning the SOURCE, not just the region, is what lets a caller tell "the
905
+ * user asked for this region" from "we inferred it" - which is the difference
906
+ * that decides whether the reconciliation in
907
+ * {@link reconcileRegionWithLegacyDefault} may move the answer back.
908
+ */
909
+ async function resolveEffectiveRegion(options) {
910
+ const flag = canonicalizeRegion(options.region);
911
+ if (flag) return {
912
+ region: flag,
913
+ source: "flag"
914
+ };
915
+ const awsRegion = canonicalizeRegion(process.env["AWS_REGION"]);
916
+ if (awsRegion) return {
917
+ region: awsRegion,
918
+ source: "env"
919
+ };
920
+ const defaultRegionEnv = canonicalizeRegion(process.env["AWS_DEFAULT_REGION"]);
921
+ if (defaultRegionEnv) return {
922
+ region: defaultRegionEnv,
923
+ source: "default-env"
924
+ };
925
+ const fromProfile = canonicalizeRegion(await resolveSdkDefaultRegion(options.profile));
926
+ if (fromProfile) return {
927
+ region: fromProfile,
928
+ source: "profile"
929
+ };
930
+ return {
931
+ region: LEGACY_DEFAULT_REGION,
932
+ source: "default"
933
+ };
934
+ }
935
+ /**
936
+ * The region cdkd used to fall back to before issue #2029, and still falls
937
+ * back to when nothing at all answers.
938
+ *
939
+ * Named rather than inlined because the reconciliation below has to ask
940
+ * "is there state under the OLD default?", and that question only means
941
+ * something while the two are the same constant.
942
+ */
943
+ const LEGACY_DEFAULT_REGION = "us-east-1";
944
+ /**
945
+ * Ask the AWS SDK's own chain which region it would use.
946
+ *
947
+ * A throwaway client, sampled once and destroyed: `src/utils/aws-clients.ts`
948
+ * is explicit that a region-less bag's lazy members resolve independently and
949
+ * need not agree with each other, so nothing here is reused for real work.
950
+ * Returns `undefined` rather than throwing when the chain answers nothing -
951
+ * a client with no resolvable region REJECTS, and that rejection is the normal
952
+ * "no region configured" signal rather than an error worth surfacing.
953
+ */
954
+ async function resolveSdkDefaultRegion(profile) {
955
+ const { STSClient } = await import("@aws-sdk/client-sts");
956
+ let client;
957
+ try {
958
+ client = new STSClient({ ...profile && { profile } });
959
+ return await client.config.region() || void 0;
960
+ } catch {
961
+ return;
962
+ } finally {
963
+ client?.destroy?.();
964
+ }
965
+ }
966
+ /**
967
+ * The marker-key twin of {@link reconcileRegionWithLegacyDefault}.
968
+ *
969
+ * `cdkd bootstrap` WRITES `cdkd-bootstrap/{region}.json`; `cdkd gc` and
970
+ * `cdkd bootstrap --destroy` READ it. Issue
971
+ * [#2029](https://github.com/go-to-k/cdkd/issues/2029) moves what `{region}`
972
+ * resolves to when the user names none, and a previous attempt at it moved the
973
+ * READ side alone — which silently broke that pairing: for a user with a
974
+ * non-us-east-1 profile, `cdkd gc` reported "not opted in" and the teardown
975
+ * "nothing to delete" while the asset bucket and ECR repo stayed alive and
976
+ * billing. So all three commands resolve through THIS function, and the pairing
977
+ * holds by construction rather than by three call sites agreeing.
978
+ *
979
+ * The reconciliation itself is the same rule as the state-key one: an inferred
980
+ * region yields to an existing marker under the old default, a named region
981
+ * never does, and a first bootstrap goes to the profile's region because there
982
+ * is nothing to strand.
983
+ *
984
+ * Note this is deliberately NOT `readBootstrapMarkerBody`: that helper answers
985
+ * "read the marker for THIS region, trying both spellings", which is a
986
+ * different question and would conflate a case difference with a region
987
+ * difference. Here both probes use the canonical key, because the question is
988
+ * only which REGION owns a marker.
989
+ */
990
+ async function reconcileMarkerRegionWithLegacyDefault(input) {
991
+ const { effective, probe, markerKeyFor, logger } = input;
992
+ if (!regionNeedsReconciliation(effective)) return effective.region;
993
+ const markerExists = async (region) => {
994
+ try {
995
+ return await probe.getRawObject(markerKeyFor(region)) !== null;
996
+ } catch (error) {
997
+ if (error.name !== "NoSuchBucket") throw error;
998
+ logger.debug(`[region] no state bucket yet, so no bootstrap marker for ${region} (first bootstrap).`);
999
+ return false;
1000
+ }
1001
+ };
1002
+ if (await markerExists(effective.region)) {
1003
+ logger.debug(`[region] asset storage exists in ${effective.region} (from your AWS profile); using it.`);
1004
+ return effective.region;
1005
+ }
1006
+ if (!await markerExists("us-east-1")) return effective.region;
1007
+ logger.info(`cdkd asset storage exists in ${LEGACY_DEFAULT_REGION}, but your AWS profile resolves ${effective.region}. Continuing to use ${LEGACY_DEFAULT_REGION} so the existing storage is not orphaned. Pass '--region ${effective.region}' to target your profile's region, or '--region ${LEGACY_DEFAULT_REGION}' to silence this message.`);
1008
+ return LEGACY_DEFAULT_REGION;
1009
+ }
1010
+ /**
1011
+ * Whether {@link resolveEffectiveRegion}'s answer is subject to the
1012
+ * reconciliation — i.e. whether cdkd INFERRED it rather than being told.
1013
+ *
1014
+ * `flag` and `env` (`AWS_REGION`) are the user speaking, and are obeyed as
1015
+ * given. `default-env` (`AWS_DEFAULT_REGION`) is NOT: cdkd never read that
1016
+ * variable before, so acting on it is a new inference. `default` is already
1017
+ * the legacy region, so there is nothing to reconcile against.
1018
+ *
1019
+ * Exported so a caller can skip CONSTRUCTING a probe client it will not use —
1020
+ * the reconciliation functions return early on the same condition, but by then
1021
+ * the caller has already paid for an S3 client and a state backend.
1022
+ */
1023
+ function regionNeedsReconciliation(effective) {
1024
+ if (effective.source === "flag" || effective.source === "env") return false;
1025
+ return effective.region !== LEGACY_DEFAULT_REGION;
1026
+ }
868
1027
 
869
1028
  //#endregion
870
1029
  //#region src/cli/commands/state-file-keys.ts
@@ -1159,15 +1318,43 @@ async function bootstrapDestroyCommand(options) {
1159
1318
  roleArn: options.roleArn,
1160
1319
  region: namedCliRegion(options.region)
1161
1320
  });
1162
- const region = namedCliRegion(options.region) ?? "us-east-1";
1321
+ const effective = await resolveEffectiveRegion(options);
1322
+ const accountClients = new AwsClients({
1323
+ region: effective.region,
1324
+ ...options.profile && { profile: options.profile }
1325
+ });
1326
+ const accountId = (await accountClients.sts.send(new GetCallerIdentityCommand({}))).Account;
1327
+ const bucketName = options.stateBucket ?? getDefaultStateBucketName(accountId);
1328
+ let region = effective.region;
1329
+ if (regionNeedsReconciliation(effective)) {
1330
+ const probeBackend = new S3StateBackend(new S3Client({
1331
+ region: effective.region,
1332
+ ...options.profile && { profile: options.profile }
1333
+ }), {
1334
+ bucket: bucketName,
1335
+ prefix: STATE_PREFIX
1336
+ }, {
1337
+ region: effective.region,
1338
+ ...options.profile && { profile: options.profile }
1339
+ });
1340
+ try {
1341
+ region = await reconcileMarkerRegionWithLegacyDefault({
1342
+ effective,
1343
+ probe: probeBackend,
1344
+ markerKeyFor: getBootstrapMarkerKey,
1345
+ logger
1346
+ });
1347
+ } finally {
1348
+ probeBackend.destroyClient();
1349
+ }
1350
+ }
1163
1351
  const rawRegion = rawCliRegion(options.region) ?? region;
1164
- const awsClients = new AwsClients({
1352
+ const awsClients = region === effective.region ? accountClients : new AwsClients({
1165
1353
  region,
1166
1354
  ...options.profile && { profile: options.profile }
1167
1355
  });
1356
+ if (awsClients !== accountClients) accountClients.destroy();
1168
1357
  setAwsClients(awsClients);
1169
- const accountId = (await awsClients.sts.send(new GetCallerIdentityCommand({}))).Account;
1170
- const bucketName = options.stateBucket ?? getDefaultStateBucketName(accountId);
1171
1358
  const markerS3Client = new S3Client({
1172
1359
  region,
1173
1360
  ...options.profile && { profile: options.profile }
@@ -1291,19 +1478,19 @@ async function bootstrapCommand(options) {
1291
1478
  if (options.verbose) logger.setLevel("debug");
1292
1479
  logger.info("Starting cdkd bootstrap...");
1293
1480
  logger.debug("Options:", options);
1294
- const rawRegion = rawCliRegion(options.region) ?? "us-east-1";
1481
+ const rawRegion = rawCliRegion(options.region);
1295
1482
  foldRegionOption(options);
1296
1483
  await applyRoleArnIfSet({
1297
1484
  roleArn: options.roleArn,
1298
1485
  region: options.region
1299
1486
  });
1300
- const awsClients = new AwsClients({
1301
- ...options.region && { region: options.region },
1487
+ const effective = await resolveEffectiveRegion(options);
1488
+ let awsClients = new AwsClients({
1489
+ region: effective.region,
1302
1490
  ...options.profile && { profile: options.profile }
1303
1491
  });
1304
1492
  setAwsClients(awsClients);
1305
- const s3Client = awsClients.s3;
1306
- const region = namedCliRegion(options.region) ?? "us-east-1";
1493
+ let s3Client = awsClients.s3;
1307
1494
  let bucketName;
1308
1495
  let accountId;
1309
1496
  if (options.stateBucket) {
@@ -1315,6 +1502,39 @@ async function bootstrapCommand(options) {
1315
1502
  bucketName = getDefaultStateBucketName(accountId);
1316
1503
  logger.info(`Using default state bucket: ${bucketName}`);
1317
1504
  }
1505
+ let region = effective.region;
1506
+ if (regionNeedsReconciliation(effective)) {
1507
+ const probeBackend = new S3StateBackend(new S3Client({
1508
+ region: effective.region,
1509
+ ...options.profile && { profile: options.profile }
1510
+ }), {
1511
+ bucket: bucketName,
1512
+ prefix: "cdkd"
1513
+ }, {
1514
+ region: effective.region,
1515
+ ...options.profile && { profile: options.profile }
1516
+ });
1517
+ try {
1518
+ region = await reconcileMarkerRegionWithLegacyDefault({
1519
+ effective,
1520
+ probe: probeBackend,
1521
+ markerKeyFor: getBootstrapMarkerKey,
1522
+ logger
1523
+ });
1524
+ } finally {
1525
+ probeBackend.destroyClient();
1526
+ }
1527
+ }
1528
+ if (region !== effective.region) {
1529
+ const held = new AwsClients({
1530
+ region,
1531
+ ...options.profile && { profile: options.profile }
1532
+ });
1533
+ awsClients.destroy();
1534
+ awsClients = held;
1535
+ setAwsClients(awsClients);
1536
+ s3Client = awsClients.s3;
1537
+ }
1318
1538
  const rebuiltStateBucketClient = await rebuildClientForBucketRegion(s3Client, bucketName, { ...options.profile && { profile: options.profile } });
1319
1539
  const stateBucketS3 = rebuiltStateBucketClient ?? s3Client;
1320
1540
  try {
@@ -1401,7 +1621,7 @@ async function bootstrapCommand(options) {
1401
1621
  ecrClient,
1402
1622
  stateBackend,
1403
1623
  accountId,
1404
- region: rawRegion,
1624
+ region: rawRegion ?? region,
1405
1625
  force: options.force,
1406
1626
  ...options.assetBucket && { assetBucketName: options.assetBucket },
1407
1627
  ...options.containerRepo && { containerRepoName: options.containerRepo }
@@ -72155,26 +72375,37 @@ async function gcCommand(options) {
72155
72375
  roleArn: options.roleArn,
72156
72376
  region: namedCliRegion(options.region)
72157
72377
  });
72158
- const region = namedCliRegion(options.region) ?? "us-east-1";
72159
- const rawRegion = rawCliRegion(options.region) ?? region;
72160
- const awsClients = new AwsClients({
72161
- region,
72378
+ const effective = await resolveEffectiveRegion(options);
72379
+ const accountClients = new AwsClients({
72380
+ region: effective.region,
72162
72381
  ...options.profile && { profile: options.profile }
72163
72382
  });
72164
- setAwsClients(awsClients);
72165
- const accountId = (await awsClients.sts.send(new GetCallerIdentityCommand({}))).Account;
72383
+ const accountId = (await accountClients.sts.send(new GetCallerIdentityCommand({}))).Account;
72166
72384
  const bucketName = options.stateBucket ?? getDefaultStateBucketName(accountId);
72167
72385
  const markerS3Client = new S3Client({
72168
- region,
72386
+ region: effective.region,
72169
72387
  ...options.profile && { profile: options.profile }
72170
72388
  });
72171
72389
  const stateBackend = new S3StateBackend(markerS3Client, {
72172
72390
  bucket: bucketName,
72173
72391
  prefix: "cdkd"
72174
72392
  }, {
72393
+ region: effective.region,
72394
+ ...options.profile && { profile: options.profile }
72395
+ });
72396
+ const region = regionNeedsReconciliation(effective) ? await reconcileMarkerRegionWithLegacyDefault({
72397
+ effective,
72398
+ probe: stateBackend,
72399
+ markerKeyFor: getBootstrapMarkerKey,
72400
+ logger
72401
+ }) : effective.region;
72402
+ const rawRegion = rawCliRegion(options.region) ?? region;
72403
+ const awsClients = region === effective.region ? accountClients : new AwsClients({
72175
72404
  region,
72176
72405
  ...options.profile && { profile: options.profile }
72177
72406
  });
72407
+ if (awsClients !== accountClients) accountClients.destroy();
72408
+ setAwsClients(awsClients);
72178
72409
  const ecrClient = new ECRClient({
72179
72410
  region,
72180
72411
  ...options.profile && { profile: options.profile }
@@ -72253,7 +72484,7 @@ async function gcCommand(options) {
72253
72484
  * Create the `cdkd gc` command (upstream `cdk gc` parity naming).
72254
72485
  */
72255
72486
  function createGcCommand() {
72256
- const cmd = new Command("gc").description("Garbage-collect unreferenced objects / images from ONE region's cdkd-owned asset storage (asset bucket + container-asset ECR repo). References are collected from every cdkd state file; CDK bootstrap storage is never touched (use cdk gc for that).").option("--state-bucket <bucket>", "S3 bucket holding cdkd state (default: cdkd-state-{accountId})").addOption(new Option("--older-than <duration>", "Never delete an object / image newer than this age, even when unreferenced (protects in-flight publishes and recent rollback targets). Accepts <n>d / <n>h.").default(DEFAULT_OLDER_THAN_MS, "30d").argParser(parseOlderThan)).option("--dry-run", "Print the reclaim plan (per-item list + totals) without deleting", false).addOption(new Option("--region <region>", "Region whose cdkd asset storage to garbage-collect (defaults to AWS_REGION / AWS_DEFAULT_REGION, else the profile region; refuses if none is configured)")).action(withErrorHandling(async (options) => {
72487
+ const cmd = new Command("gc").description("Garbage-collect unreferenced objects / images from ONE region's cdkd-owned asset storage (asset bucket + container-asset ECR repo). References are collected from every cdkd state file; CDK bootstrap storage is never touched (use cdk gc for that).").option("--state-bucket <bucket>", "S3 bucket holding cdkd state (default: cdkd-state-{accountId})").addOption(new Option("--older-than <duration>", "Never delete an object / image newer than this age, even when unreferenced (protects in-flight publishes and recent rollback targets). Accepts <n>d / <n>h.").default(DEFAULT_OLDER_THAN_MS, "30d").argParser(parseOlderThan)).option("--dry-run", "Print the reclaim plan (per-item list + totals) without deleting", false).addOption(new Option("--region <region>", "Region whose cdkd asset storage to garbage-collect (defaults to AWS_REGION, then AWS_DEFAULT_REGION, then your AWS profile's region, then us-east-1)")).action(withErrorHandling(async (options) => {
72257
72488
  await gcCommand(options);
72258
72489
  }));
72259
72490
  commonOptions.forEach((opt) => cmd.addOption(opt));
@@ -84028,7 +84259,7 @@ function createMigrateCommand() {
84028
84259
  */
84029
84260
  function buildProgram() {
84030
84261
  const program = new Command();
84031
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.25");
84262
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.26");
84032
84263
  program.hook("preAction", (_thisCommand, actionCommand) => {
84033
84264
  const { profile } = actionCommand.optsWithGlobals();
84034
84265
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;