@go-to-k/cdkd 0.291.18 → 0.291.20

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.
@@ -1,6 +1,6 @@
1
1
  import { C as displayAwsMessage, D as isPasteableIdent, E as displayStackName, O as truncateCodePoints, S as UNRENDERABLE, T as displaySafe, d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS, y as withoutGeneratedFallbackName } from "./logger-Bt0RGDfb.js";
2
2
  import { a as setAssumedRoleCredentials, c as currentStackAwsScope, i as getPreAssumeEnvCredentials, l as runInStackAwsScope, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, s as ambientRegion, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-jISU4PHo.js";
3
- import { t as getCdkdVersion } from "./version-CJkb2usU.js";
3
+ import { t as getCdkdVersion } from "./version-BYrVI73L.js";
4
4
  import { AsyncLocalStorage } from "node:async_hooks";
5
5
  import { createHash, randomUUID } from "node:crypto";
6
6
  import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -4120,6 +4120,109 @@ var ContextStore = class {
4120
4120
  }
4121
4121
  };
4122
4122
 
4123
+ //#endregion
4124
+ //#region src/utils/ambient-client-defaults.ts
4125
+ /**
4126
+ * Client config for an SDK client built OUTSIDE `AwsClients` (issue
4127
+ * [#3588](https://github.com/go-to-k/cdkd/issues/3588)).
4128
+ *
4129
+ * `awsClientDefaults()` alone carries the ambient ENVIRONMENT — `AWS_PROFILE`,
4130
+ * a published `--role-arn` role, the proxy handler, a stack scope's region — but
4131
+ * not an explicit `AwsClientConfig.credentials` object, which has no
4132
+ * environment path at all. A LIBRARY caller that installs
4133
+ * `new AwsClients({ credentials })` (`AwsClients` / `setAwsClients` are public
4134
+ * exports) therefore had every client a provider, context provider,
4135
+ * asset publisher or the cross-account STS hop built for itself signing with
4136
+ * the default credential chain: a different identity from the one it
4137
+ * configured. {@link ambientClientDefaults} closes that for every site at once
4138
+ * by reading the ACTIVE clients' {@link AwsClients.credentialConfig}.
4139
+ *
4140
+ * BOUND: a site that caches its client (every SDK provider's lazy
4141
+ * `getClient()`) captures the identity active at its FIRST call. cdkd builds a
4142
+ * provider registry per stack inside that stack's scope, so this is the stack's
4143
+ * identity; a library caller that reuses one registry across `setAwsClients`
4144
+ * switches keeps the first identity's clients.
4145
+ *
4146
+ * SPREAD IT FIRST, exactly where `awsClientDefaults()` was spread:
4147
+ *
4148
+ * `new GlueClient({ ...ambientClientDefaults(), ...(region && { region }) })`
4149
+ *
4150
+ * The site's own keys still win, so a site that names its own `region` or its
4151
+ * own `credentials` keeps them. `scripts/check-aws-client-defaults.ts` accepts
4152
+ * this helper as the defaults spread (it calls `awsClientDefaults` afresh per
4153
+ * call, so each client still gets its own routing agent), and
4154
+ * `tests/unit/utils/ambient-client-defaults-fence.test.ts` refuses a new
4155
+ * `awsClientDefaults()`-only construction under `src/**`.
4156
+ *
4157
+ * Its own module rather than a member of `aws-clients.ts` so that a test which
4158
+ * `vi.mock`s `aws-clients.js` with a `getAwsClients` double still reaches this
4159
+ * code and degrades, instead of losing the export. It is deliberately OUTSIDE
4160
+ * the literal-`.ts` closure `scripts/audit-provider-coverage.ts` resolves
4161
+ * (nothing in that closure imports it), and neither module it imports imports
4162
+ * it back, so there is no cycle.
4163
+ */
4164
+ /**
4165
+ * The ACTIVE clients' credential configuration: the stack scope's
4166
+ * `AwsClients` inside `runWithStackAwsClients`, else the process-global one.
4167
+ *
4168
+ * `{}` when those clients carry none (every CLI run passes no explicit
4169
+ * `credentials`, and `--profile` alone yields `{ profile }`), and also for a
4170
+ * test double that has no `credentialConfig` getter — degrading to the plain
4171
+ * `awsClientDefaults()` answer rather than throwing.
4172
+ *
4173
+ * Read through `getAwsClients()`, which creates an unconfigured global on first
4174
+ * use when nothing installed one. That is side-effect free here: its service
4175
+ * clients are lazy, no production call passes `getAwsClients(config)` (so no
4176
+ * later configuration is shadowed), and `setAwsClients` replaces it.
4177
+ */
4178
+ function ambientCredentialConfig() {
4179
+ return getAwsClients()?.credentialConfig ?? {};
4180
+ }
4181
+ /**
4182
+ * `awsClientDefaults(...)` plus `credentialConfig`, in `AwsClients.clientOptions`'
4183
+ * spread order: the defaults FIRST, so an explicit `credentials` outranks a
4184
+ * published `--role-arn` role, then `profile`, then `credentials`.
4185
+ *
4186
+ * Takes the configuration as an argument for the callers that must key a cache
4187
+ * on the SAME reading they build from ({@link credentialFingerprint}); every
4188
+ * other site calls {@link ambientClientDefaults}.
4189
+ */
4190
+ function clientDefaultsFor(credentialConfig) {
4191
+ return {
4192
+ ...awsClientDefaults({ profile: credentialConfig.profile }),
4193
+ ...credentialConfig.profile && { profile: credentialConfig.profile },
4194
+ ...credentialConfig.credentials && { credentials: { ...credentialConfig.credentials } }
4195
+ };
4196
+ }
4197
+ /**
4198
+ * The config every SDK client built outside `AwsClients` spreads first: the
4199
+ * proxy / role / scope-region defaults plus the ACTIVE clients' profile and
4200
+ * explicit credentials.
4201
+ */
4202
+ function ambientClientDefaults() {
4203
+ return clientDefaultsFor(ambientCredentialConfig());
4204
+ }
4205
+ /**
4206
+ * A cache-key component naming WHICH identity a credential configuration
4207
+ * selects, for a cache that holds a client (or credentials) built from it.
4208
+ *
4209
+ * `profile` plus the explicit credentials' ACCESS KEY ID — never the secret
4210
+ * key or the session token. The access key id is an identifier rather than a
4211
+ * secret (`expected-bucket-owner.ts` already keys its account cache on it), and
4212
+ * STS issues a distinct one per session, so two different explicit bags are
4213
+ * told apart. The one collision is two bags sharing an access key id with
4214
+ * different secrets, at most one of which can be valid.
4215
+ *
4216
+ * What it does NOT carry, because it is PROCESS-WIDE rather than per
4217
+ * configuration: a published `--role-arn` role (set once, refused a second
4218
+ * time), the proxy, and `AWS_PROFILE` / the environment chain. Two readings
4219
+ * with equal fingerprints within one process therefore resolve to the same
4220
+ * identity. Do not log it.
4221
+ */
4222
+ function credentialFingerprint(credentialConfig) {
4223
+ return JSON.stringify([credentialConfig.profile ?? null, credentialConfig.credentials?.accessKeyId ?? null]);
4224
+ }
4225
+
4123
4226
  //#endregion
4124
4227
  //#region src/synthesis/context-providers/az-provider.ts
4125
4228
  /**
@@ -4138,7 +4241,7 @@ var AZContextProvider = class {
4138
4241
  const region = props["region"] || this.awsConfig?.region;
4139
4242
  this.logger.debug(`Fetching availability zones for region: ${region}`);
4140
4243
  const client = new EC2Client({
4141
- ...awsClientDefaults(),
4244
+ ...ambientClientDefaults(),
4142
4245
  ...region && { region }
4143
4246
  });
4144
4247
  try {
@@ -4171,7 +4274,7 @@ var SSMContextProvider = class {
4171
4274
  if (!parameterName) throw new Error("SSM context provider requires parameterName property");
4172
4275
  this.logger.debug(`Reading SSM parameter: ${parameterName} (region: ${region})`);
4173
4276
  const client = new SSMClient({
4174
- ...awsClientDefaults(),
4277
+ ...ambientClientDefaults(),
4175
4278
  ...region && { region }
4176
4279
  });
4177
4280
  try {
@@ -4213,7 +4316,7 @@ var HostedZoneContextProvider = class {
4213
4316
  if (!domainName) throw new Error("Hosted zone context provider requires domainName property");
4214
4317
  this.logger.debug(`Looking up hosted zone: ${domainName} (private: ${privateZone})`);
4215
4318
  const client = new Route53Client({
4216
- ...awsClientDefaults(),
4319
+ ...ambientClientDefaults(),
4217
4320
  ...region && { region }
4218
4321
  });
4219
4322
  try {
@@ -4267,7 +4370,7 @@ var VpcContextProvider = class {
4267
4370
  const returnVpnGateways = props["returnVpnGateways"];
4268
4371
  this.logger.debug(`Looking up VPC (region: ${region}, filter: ${JSON.stringify(filter)})`);
4269
4372
  const client = new EC2Client({
4270
- ...awsClientDefaults(),
4373
+ ...ambientClientDefaults(),
4271
4374
  ...region && { region }
4272
4375
  });
4273
4376
  try {
@@ -4433,7 +4536,7 @@ var CcApiContextProvider = class {
4433
4536
  if (!typeName) throw new Error("CC API context provider requires typeName property");
4434
4537
  this.logger.debug(`CC API lookup: ${typeName}${exactIdentifier ? ` (id: ${exactIdentifier})` : ""} (region: ${region})`);
4435
4538
  const client = new CloudControlClient({
4436
- ...awsClientDefaults(),
4539
+ ...ambientClientDefaults(),
4437
4540
  ...region && { region }
4438
4541
  });
4439
4542
  try {
@@ -4560,7 +4663,7 @@ var AmiContextProvider = class {
4560
4663
  const filters = props["filters"];
4561
4664
  this.logger.debug(`Looking up AMI (region: ${region})`);
4562
4665
  const client = new EC2Client({
4563
- ...awsClientDefaults(),
4666
+ ...ambientClientDefaults(),
4564
4667
  ...region && { region }
4565
4668
  });
4566
4669
  try {
@@ -4603,7 +4706,7 @@ var SecurityGroupContextProvider = class {
4603
4706
  const vpcId = props["vpcId"];
4604
4707
  this.logger.debug(`Looking up security group (id: ${securityGroupId}, name: ${securityGroupName}, region: ${region})`);
4605
4708
  const client = new EC2Client({
4606
- ...awsClientDefaults(),
4709
+ ...ambientClientDefaults(),
4607
4710
  ...region && { region }
4608
4711
  });
4609
4712
  try {
@@ -4657,7 +4760,7 @@ var LoadBalancerContextProvider = class {
4657
4760
  const loadBalancerType = props["loadBalancerType"];
4658
4761
  this.logger.debug(`Looking up load balancer (arn: ${loadBalancerArn}, region: ${region})`);
4659
4762
  const client = new ElasticLoadBalancingV2Client({
4660
- ...awsClientDefaults(),
4763
+ ...ambientClientDefaults(),
4661
4764
  ...region && { region }
4662
4765
  });
4663
4766
  try {
@@ -4699,7 +4802,7 @@ var LoadBalancerListenerContextProvider = class {
4699
4802
  const listenerProtocol = props["listenerProtocol"];
4700
4803
  this.logger.debug(`Looking up load balancer listener (arn: ${listenerArn}, lb: ${loadBalancerArn}, region: ${region})`);
4701
4804
  const client = new ElasticLoadBalancingV2Client({
4702
- ...awsClientDefaults(),
4805
+ ...ambientClientDefaults(),
4703
4806
  ...region && { region }
4704
4807
  });
4705
4808
  try {
@@ -4743,7 +4846,7 @@ var KeyContextProvider = class {
4743
4846
  if (!aliasName) throw new Error("Key context provider requires aliasName property");
4744
4847
  this.logger.debug(`Looking up KMS key by alias: ${aliasName} (region: ${region})`);
4745
4848
  const client = new KMSClient({
4746
- ...awsClientDefaults(),
4849
+ ...ambientClientDefaults(),
4747
4850
  ...region && { region }
4748
4851
  });
4749
4852
  try {
@@ -6647,7 +6750,7 @@ async function expandMacrosAttempt(template, opts, logger) {
6647
6750
  const parameters = buildParameterValues(template, logger);
6648
6751
  ownsClient = opts.cfnClient === void 0;
6649
6752
  cfn = opts.cfnClient ?? new CloudFormationClient({
6650
- ...awsClientDefaults(),
6753
+ ...ambientClientDefaults(),
6651
6754
  region
6652
6755
  });
6653
6756
  let templateInput;
@@ -7778,7 +7881,7 @@ var FileAssetPublisher = class {
7778
7881
  const destRegion = dest.region ? this.resolvePlaceholders(dest.region, accountId, region) : region;
7779
7882
  this.logger.debug(`Publishing file asset ${asset.displayName || assetHash} → s3://${bucketName}/${objectKey}`);
7780
7883
  const client = new S3Client({
7781
- ...awsClientDefaults(),
7884
+ ...ambientClientDefaults(),
7782
7885
  region: destRegion
7783
7886
  });
7784
7887
  try {
@@ -9694,20 +9797,31 @@ function buildDockerBuildCommand(source, tag, platformOverride) {
9694
9797
  function ecrUrlSuffix(region) {
9695
9798
  return derivePartitionAndUrlSuffix(region).urlSuffix;
9696
9799
  }
9800
+ /** An AWS account id: exactly twelve digits. */
9801
+ const ACCOUNT_ID = /^\d{12}$/;
9697
9802
  /**
9698
- * Registries this process has already logged in to, keyed by registry host
9699
- * (`<accountId>.dkr.ecr.<region>.<urlSuffix>`, which uniquely encodes the
9700
- * account + region credential context). ECR authorization tokens are valid
9701
- * for ~12h and a deploy process is short-lived, so a successful login is
9702
- * reused for the process lifetime — mirroring `cdk-assets`, which skips
9703
- * re-login on repeat publishes. Keyed per registry (NOT globally) so that
9704
- * cross-account / cross-region assets each get their own login.
9803
+ * The DNS-label charset a region id is drawn from (a trailing hyphen is not
9804
+ * refused; the host stays under the AWS-owned suffix either way).
9805
+ * `src/utils/ecr-uri.ts` holds the same class for the pull side.
9806
+ */
9807
+ const REGION_LABEL = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
9808
+ /**
9809
+ * The ECR registry host an asset is pushed to. The push URI AND the
9810
+ * `docker login` endpoint are both built from this one value, so the login
9811
+ * always targets the host the push uses (issue #3681).
9705
9812
  *
9706
- * Module-level (not an instance field) because the deploy pipeline may
9707
- * construct more than one `DockerAssetPublisher` per run (e.g. one per
9708
- * WorkGraph asset-publish node), and the login is genuinely process-wide.
9813
+ * The region comes from the asset manifest, and the ECR password is sent to
9814
+ * this host, so both halves are charset-gated HERE: a region like
9815
+ * `x.example.com/` would otherwise name a host AWS does not own. The suffix is
9816
+ * the AWS-owned one `derivePartitionAndUrlSuffix` returns. A known-region
9817
+ * check is deliberately absent, so a region cdkd has not heard of yet still
9818
+ * publishes.
9709
9819
  */
9710
- const loggedInRegistries = /* @__PURE__ */ new Set();
9820
+ function ecrRegistryHost(accountId, region) {
9821
+ if (typeof accountId !== "string" || !ACCOUNT_ID.test(accountId)) throw new AssetError(`Refusing to publish a Docker image asset: ${displayIdent(accountId)} is not a 12-digit AWS account id`);
9822
+ if (typeof region !== "string" || region.length > 63 || !REGION_LABEL.test(region)) throw new AssetError(`Refusing to publish a Docker image asset: the destination region ${displayIdent(region)} is not a valid AWS region id`);
9823
+ return `${accountId}.dkr.ecr.${region}.${ecrUrlSuffix(region)}`;
9824
+ }
9711
9825
  /**
9712
9826
  * Whether a `docker push` failure looks like an ECR authentication failure
9713
9827
  * (missing / stale / expired credential) rather than a network / repo / other
@@ -9737,10 +9851,11 @@ var DockerAssetPublisher = class {
9737
9851
  const repositoryName = this.resolvePlaceholders(dest.repositoryName, accountId, region);
9738
9852
  const imageTag = this.resolvePlaceholders(dest.imageTag, accountId, region);
9739
9853
  const destRegion = dest.region ? this.resolvePlaceholders(dest.region, accountId, region) : region;
9740
- const ecrUri = `${accountId}.dkr.ecr.${destRegion}.${ecrUrlSuffix(destRegion)}/${repositoryName}:${imageTag}`;
9854
+ const registryHost = ecrRegistryHost(accountId, destRegion);
9855
+ const ecrUri = `${registryHost}/${repositoryName}:${imageTag}`;
9741
9856
  this.logger.debug(`Publishing Docker image ${asset.displayName || assetHash} → ${ecrUri}`);
9742
9857
  const client = new ECRClient({
9743
- ...awsClientDefaults(),
9858
+ ...ambientClientDefaults(),
9744
9859
  region: destRegion
9745
9860
  });
9746
9861
  try {
@@ -9750,7 +9865,7 @@ var DockerAssetPublisher = class {
9750
9865
  }
9751
9866
  const localTag = `cdkd-asset-${assetHash}`;
9752
9867
  await this.buildImage(asset, cdkOutputDir, localTag, cdkOutputDir);
9753
- await this.tagAndPushWithLazyLogin(client, localTag, ecrUri, accountId, destRegion);
9868
+ await this.tagAndPushWithLazyLogin(client, localTag, ecrUri, registryHost);
9754
9869
  this.logger.debug(`✅ Published: ${ecrUri}`);
9755
9870
  } finally {
9756
9871
  client.destroy();
@@ -9777,9 +9892,10 @@ var DockerAssetPublisher = class {
9777
9892
  const repositoryName = this.resolvePlaceholders(dest.repositoryName, accountId, region);
9778
9893
  const imageTag = this.resolvePlaceholders(dest.imageTag, accountId, region);
9779
9894
  const destRegion = dest.region ? this.resolvePlaceholders(dest.region, accountId, region) : region;
9780
- const ecrUri = `${accountId}.dkr.ecr.${destRegion}.${ecrUrlSuffix(destRegion)}/${repositoryName}:${imageTag}`;
9895
+ const registryHost = ecrRegistryHost(accountId, destRegion);
9896
+ const ecrUri = `${registryHost}/${repositoryName}:${imageTag}`;
9781
9897
  const client = new ECRClient({
9782
- ...awsClientDefaults(),
9898
+ ...ambientClientDefaults(),
9783
9899
  region: destRegion
9784
9900
  });
9785
9901
  try {
@@ -9787,7 +9903,7 @@ var DockerAssetPublisher = class {
9787
9903
  this.logger.debug(`Image already exists, skipping: ${ecrUri}`);
9788
9904
  continue;
9789
9905
  }
9790
- await this.tagAndPushWithLazyLogin(client, localTag, ecrUri, accountId, destRegion);
9906
+ await this.tagAndPushWithLazyLogin(client, localTag, ecrUri, registryHost);
9791
9907
  this.logger.debug(`✅ Published: ${ecrUri}`);
9792
9908
  } finally {
9793
9909
  client.destroy();
@@ -9854,18 +9970,17 @@ var DockerAssetPublisher = class {
9854
9970
  * round-trip (~3.3s saved). We attempt the push FIRST and only pay the login
9855
9971
  * when it is actually needed:
9856
9972
  *
9857
- * 1. If this PROCESS already logged into the registry (issue #1184 cache),
9858
- * push directly — still self-healing: a mid-process token expiry falls
9859
- * into the same auth-failure retry below.
9860
- * 2. Otherwise push optimistically. On a NON-auth failure (network,
9861
- * repo-not-found, etc.) surface the error unchanged. On an auth failure
9862
- * (no pre-existing cred, OR a stale/expired cred), run a forced ECR
9863
- * login and retry the push ONCE.
9973
+ * 1. Push optimistically. A login earlier in this process (or session)
9974
+ * left a credential in docker's store, so a repeat push to the same
9975
+ * registry needs no new login — docker's store is the only login cache.
9976
+ * 2. On a NON-auth failure (network, repo-not-found, etc.) surface the
9977
+ * error unchanged. On an auth failure (no pre-existing cred, OR a
9978
+ * stale/expired cred), log in to ECR and retry the push ONCE.
9864
9979
  *
9865
9980
  * A stale cred and a missing cred take the SAME branch, so cdkd never
9866
9981
  * fail-hard on an expired credential — it re-logs in and retries.
9867
9982
  */
9868
- async tagAndPushWithLazyLogin(client, localTag, fullUri, accountId, region) {
9983
+ async tagAndPushWithLazyLogin(client, localTag, fullUri, registryHost) {
9869
9984
  await this.tagImage(localTag, fullUri);
9870
9985
  try {
9871
9986
  await this.pushImage(fullUri);
@@ -9875,34 +9990,23 @@ var DockerAssetPublisher = class {
9875
9990
  if (!isDockerAuthFailure(e.stderr || e.message || String(err))) throw err;
9876
9991
  this.logger.debug(`Docker push to ${fullUri} failed with an auth error; logging in to ECR and retrying`);
9877
9992
  }
9878
- await this.ecrLogin(client, accountId, region, { force: true });
9993
+ await this.ecrLogin(client, registryHost);
9879
9994
  await this.pushImage(fullUri);
9880
9995
  }
9881
9996
  /**
9882
- * Authenticate with ECR via `docker login --password-stdin`.
9997
+ * Authenticate with ECR via `docker login --password-stdin`, against
9998
+ * `registryHost` — the host the push targets (`ecrRegistryHost`).
9883
9999
  *
9884
- * The login is cached per registry (`<accountId>.dkr.ecr.<region>.<urlSuffix>`) for the
9885
- * process lifetime: a repeat publish to the same registry returns early
9886
- * without the `GetAuthorizationToken` call or the `docker login` subprocess
9887
- * (mirrors `cdk-assets`). ECR tokens are valid ~12h and a deploy process is
9888
- * short-lived, so this is safe. Keyed per registry so cross-account /
9889
- * cross-region assets each get their own login.
9890
- *
9891
- * `force: true` bypasses the per-process cache short-circuit — used by the
9892
- * lazy-login retry after a push fails auth, where the cached entry may hold a
9893
- * token that AWS has already expired.
10000
+ * Called only after a push failed auth, so it always logs in: a repeat push
10001
+ * to a registry already logged in to succeeds on the credential docker
10002
+ * stored and never reaches here (#1193).
9894
10003
  */
9895
- async ecrLogin(client, accountId, region, options = {}) {
9896
- const registryKey = `${accountId}.dkr.ecr.${region}.${ecrUrlSuffix(region)}`;
9897
- if (!options.force && loggedInRegistries.has(registryKey)) {
9898
- this.logger.debug(`Reusing cached ECR login for ${registryKey}`);
9899
- return;
9900
- }
10004
+ async ecrLogin(client, registryHost) {
9901
10005
  const authData = (await client.send(new GetAuthorizationTokenCommand({}))).authorizationData?.[0];
9902
10006
  if (!authData?.authorizationToken) throw new AssetError("Failed to get ECR authorization token");
9903
10007
  const [username, password] = Buffer.from(authData.authorizationToken, "base64").toString().split(":");
9904
10008
  if (!username || password === void 0) throw new AssetError("ECR authorization token has unexpected shape (missing username/password)");
9905
- const endpoint = authData.proxyEndpoint || `https://${accountId}.dkr.ecr.${region}.${ecrUrlSuffix(region)}`;
10009
+ const endpoint = `https://${registryHost}`;
9906
10010
  const loginArgs = [
9907
10011
  "login",
9908
10012
  "--username",
@@ -9912,7 +10016,6 @@ var DockerAssetPublisher = class {
9912
10016
  ];
9913
10017
  try {
9914
10018
  await runDockerStreaming(loginArgs, { input: password });
9915
- loggedInRegistries.add(registryKey);
9916
10019
  } catch (err) {
9917
10020
  throw new AssetError(`ECR login failed: ${formatDockerLoginError(describeDockerFailure(err, loginArgs), endpoint)}`, redactedDockerCause(err, loginArgs));
9918
10021
  }
@@ -11322,7 +11425,7 @@ var AssetPublisher = class {
11322
11425
  if (!accountId) {
11323
11426
  const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
11324
11427
  const stsClient = new STSClient({
11325
- ...awsClientDefaults(),
11428
+ ...ambientClientDefaults(),
11326
11429
  region
11327
11430
  });
11328
11431
  accountId = (await stsClient.send(new GetCallerIdentityCommand({}))).Account;
@@ -26557,7 +26660,8 @@ function applyCallerIdentityCredentials(env) {
26557
26660
  //#endregion
26558
26661
  //#region src/utils/role-arn.ts
26559
26662
  /**
26560
- * Process-lifetime cache of assumed credentials keyed by RoleArn.
26663
+ * Process-lifetime cache of assumed credentials keyed by RoleArn AND the
26664
+ * SOURCE identity the `sts:AssumeRole` hop runs as.
26561
26665
  *
26562
26666
  * Storing the in-flight `Promise` (rather than the resolved value) collapses
26563
26667
  * concurrent first-time callers into a single `sts:AssumeRole` request. After
@@ -26565,10 +26669,19 @@ function applyCallerIdentityCredentials(env) {
26565
26669
  * stack that references the same producer N times via `Fn::GetStackOutput`
26566
26670
  * only pays the STS hop once.
26567
26671
  *
26568
- * The cache is keyed by RoleArn alone (not RoleArn + region) because STS
26569
- * credentials are global — assumed credentials work against any region's
26570
- * service endpoint. The downstream S3 client built from these credentials
26571
- * picks its own region via `GetBucketLocation`.
26672
+ * Not keyed by region, because STS credentials are global — assumed
26673
+ * credentials work against any region's service endpoint. The downstream S3
26674
+ * client built from these credentials picks its own region via
26675
+ * `GetBucketLocation`.
26676
+ *
26677
+ * KEYED BY THE SOURCE IDENTITY TOO (issue
26678
+ * [#3588](https://github.com/go-to-k/cdkd/issues/3588)): the hop runs as the
26679
+ * ACTIVE `AwsClients`' credential configuration, and a library caller can
26680
+ * install (`setAwsClients`) two different explicit credentials in one process,
26681
+ * as can cdkd's own per-stack scopes. Keyed by RoleArn alone, the second would be handed
26682
+ * credentials the FIRST identity obtained — a role the second one may not even
26683
+ * be trusted to assume. The source half is `credentialFingerprint`'s (profile +
26684
+ * access key id, never secret material).
26572
26685
  *
26573
26686
  * **Expiration handling**: on every cache hit, the cached credentials'
26574
26687
  * `expiration` is compared against `Date.now()` with a 60-second safety
@@ -26628,7 +26741,7 @@ function parseIamRoleArn(roleArn) {
26628
26741
  * the consumer account's normal credentials). Threading the credentials
26629
26742
  * through a fresh `S3Client` via this helper keeps the scope narrow.
26630
26743
  *
26631
- * **Why cache per-RoleArn for the process lifetime.** A multi-resource
26744
+ * **Why cache per RoleArn (and source identity) for the process lifetime.** A multi-resource
26632
26745
  * stack typically references `Fn::GetStackOutput` from many template sites
26633
26746
  * (every IAM policy / Lambda env / ALB listener that pulls a shared VPC ID
26634
26747
  * from a platform stack). Assuming the role once per deploy is sufficient;
@@ -26651,17 +26764,19 @@ function parseIamRoleArn(roleArn) {
26651
26764
  * uniformly), but the next caller after rejection gets a clean slate.
26652
26765
  */
26653
26766
  async function assumeRoleForCrossAccountStateRead(roleArn) {
26654
- const cached = crossAccountCredentialsCache.get(roleArn);
26767
+ const sourceConfig = ambientCredentialConfig();
26768
+ const cacheKey = JSON.stringify([credentialFingerprint(sourceConfig), roleArn]);
26769
+ const cached = crossAccountCredentialsCache.get(cacheKey);
26655
26770
  if (cached) {
26656
26771
  const cachedCreds = await cached;
26657
26772
  if (!cachedCreds.expiration || Date.now() < cachedCreds.expiration.getTime() - CRED_EXPIRY_SAFETY_MS) return cachedCreds;
26658
- crossAccountCredentialsCache.delete(roleArn);
26773
+ crossAccountCredentialsCache.delete(cacheKey);
26659
26774
  }
26660
26775
  const promise = (async () => {
26661
26776
  const logger = getLogger().child("role-arn");
26662
26777
  const displayRoleArn = displayIdent(roleArn, { maxCodePoints: 613 });
26663
26778
  logger.debug(`Assuming role for cross-account state read: ${displayRoleArn}`);
26664
- const sts = new STSClient({ ...awsClientDefaults() });
26779
+ const sts = new STSClient({ ...clientDefaultsFor(sourceConfig) });
26665
26780
  try {
26666
26781
  let response;
26667
26782
  try {
@@ -26688,10 +26803,10 @@ async function assumeRoleForCrossAccountStateRead(roleArn) {
26688
26803
  sts.destroy();
26689
26804
  }
26690
26805
  })().catch((err) => {
26691
- if (crossAccountCredentialsCache.get(roleArn) === promise) crossAccountCredentialsCache.delete(roleArn);
26806
+ if (crossAccountCredentialsCache.get(cacheKey) === promise) crossAccountCredentialsCache.delete(cacheKey);
26692
26807
  throw err;
26693
26808
  });
26694
- crossAccountCredentialsCache.set(roleArn, promise);
26809
+ crossAccountCredentialsCache.set(cacheKey, promise);
26695
26810
  return promise;
26696
26811
  }
26697
26812
  /**
@@ -27485,6 +27600,88 @@ function mergeHealedAttributes(recorded, healed, isStaleValue) {
27485
27600
  return merged ?? recorded;
27486
27601
  }
27487
27602
 
27603
+ //#endregion
27604
+ //#region src/provisioning/attribute-map.ts
27605
+ /**
27606
+ * The ONE spelling of the attribute-map rule an SDK provider's `create()` /
27607
+ * `update()` / `import()` result follows (issue #3077, closing the `?? ''`
27608
+ * shape `docs/provider-development.md` forbids under "Never store an
27609
+ * empty-string placeholder"):
27610
+ *
27611
+ * an attribute cdkd could not read back is ABSENT from the map — never
27612
+ * recorded as the empty string, and never as a present-but-`undefined` key.
27613
+ *
27614
+ * The consumers of `ResourceState.attributes` all key off exactly that
27615
+ * distinction, and a sentinel puts every one of them on the wrong branch:
27616
+ *
27617
+ * - the deploy engine persists the map as-is, so `''` becomes the recorded
27618
+ * value of a field AWS never assigned;
27619
+ * - the intrinsic resolver serves a stored attribute whenever it is not
27620
+ * `undefined`, so `''` shadows `constructAttribute` forever — the
27621
+ * `AWS::EC2::Instance` IP / DNS / AZ arm re-describes the instance from
27622
+ * AWS only on a MISS, the `Arn` arms of the RDS family rebuild the ARN
27623
+ * only on a MISS;
27624
+ * - the Outputs export pass publishes whatever the resolver returned, so a
27625
+ * stored `''` is exported to every `Fn::ImportValue` consumer.
27626
+ *
27627
+ * It exists as a MODULE rather than a per-provider idiom because the rule was
27628
+ * prose and 96 attribute-position `?? ''` / `|| ''` / bare `''` sites across
27629
+ * 18 providers shipped anyway (measured 2026-09-14 with the TypeScript-compiler
27630
+ * walk that is now `tests/unit/provisioning/attribute-map.test.ts`). A helper
27631
+ * that DROPS the key is the one form a provider cannot spell wrong by habit.
27632
+ *
27633
+ * Two values are treated as "not read back":
27634
+ *
27635
+ * - `undefined`: the SDK's spelling of an unassigned optional member.
27636
+ * - `null`: no AWS SDK v3 read-back reports a scalar attribute as `null`, and
27637
+ * a stored `null` takes the resolver's "not `undefined`" hit branch exactly
27638
+ * like `''` does.
27639
+ *
27640
+ * Every other value is kept verbatim — INCLUDING an empty string AWS itself
27641
+ * reported (`false`, `0`, an empty array and an object likewise). The helper
27642
+ * stops cdkd MANUFACTURING `''` for a field it never read; a `''` that came
27643
+ * off the wire is a known value with a meaning the provider owns. The EC2
27644
+ * Instance provider is where that distinction is load-bearing: a `running`
27645
+ * instance with no public address is described with `PublicDnsName: ''` and
27646
+ * no `PublicIpAddress`, and CloudFormation answers `''` for both — a KNOWN
27647
+ * empty, recorded as such so a reference does not degrade to the instance id
27648
+ * on every resolution — while the same `''` on a `pending` instance means
27649
+ * "not yet", which that provider maps to `undefined` BEFORE calling this
27650
+ * helper (its `describedInstanceAttributes`). A caller that knows a `''`
27651
+ * means "unknown" is the one that must say so; this helper cannot tell.
27652
+ *
27653
+ * Deliberately NOT a general "compact this record" utility: it is scoped to
27654
+ * attribute maps. A property bag keeps its `undefined`-valued keys out by other
27655
+ * means (`.claude/rules/provider-property-fidelity.md`, "Remove the key rather
27656
+ * than setting it to `undefined`").
27657
+ *
27658
+ * A LEAF module (imports nothing) so every provider can take it without adding
27659
+ * an edge to the provider import ring.
27660
+ */
27661
+ /**
27662
+ * Build an attribute map from read-back values, omitting every key whose value
27663
+ * is `undefined` or `null`.
27664
+ */
27665
+ function definedAttributes(entries) {
27666
+ const out = {};
27667
+ for (const [key, value] of Object.entries(entries)) {
27668
+ if (value === void 0 || value === null) continue;
27669
+ out[key] = value;
27670
+ }
27671
+ return out;
27672
+ }
27673
+ /**
27674
+ * `String(value)` for a numeric read-back whose CloudFormation attribute is a
27675
+ * string (`Endpoint.Port`), or `undefined` when the field is unassigned — so
27676
+ * `definedAttributes` drops the key instead of recording the literal
27677
+ * `'undefined'`, which is what `String(described?.Port ?? '')` yields once its
27678
+ * `?? ''` is merely deleted.
27679
+ */
27680
+ function stringifyIfAssigned(value) {
27681
+ if (value === void 0 || value === null) return void 0;
27682
+ return String(value);
27683
+ }
27684
+
27488
27685
  //#endregion
27489
27686
  //#region src/provisioning/config-shape.ts
27490
27687
  /**
@@ -28588,7 +28785,7 @@ var WAFv2WebACLProvider = class {
28588
28785
  ])]]);
28589
28786
  getClient() {
28590
28787
  if (!this.wafv2Client) this.wafv2Client = new WAFV2Client({
28591
- ...awsClientDefaults(),
28788
+ ...ambientClientDefaults(),
28592
28789
  ...this.providerRegion ? { region: this.providerRegion } : {}
28593
28790
  });
28594
28791
  return this.wafv2Client;
@@ -28626,13 +28823,23 @@ var WAFv2WebACLProvider = class {
28626
28823
  }))).Summary;
28627
28824
  if (!summary?.ARN || !summary?.Id) throw new Error("CreateWebACL did not return Summary with ARN and Id");
28628
28825
  this.logger.debug(`Successfully created WAFv2 WebACL ${logicalId}: ${summary.ARN}`);
28826
+ let labelNamespace;
28827
+ try {
28828
+ labelNamespace = (await this.getClient().send(new GetWebACLCommand({
28829
+ Id: summary.Id,
28830
+ Name: name,
28831
+ Scope: scope
28832
+ }))).WebACL?.LabelNamespace;
28833
+ } catch (readError) {
28834
+ this.logger.debug(`Could not read the label namespace of ${logicalId} after create: ${describeAwsFailure(readError).detail}`);
28835
+ }
28629
28836
  return {
28630
28837
  physicalId: summary.ARN,
28631
- attributes: {
28838
+ attributes: definedAttributes({
28632
28839
  Arn: summary.ARN,
28633
28840
  Id: summary.Id,
28634
- LabelNamespace: summary["LabelNamespace"]
28635
- }
28841
+ LabelNamespace: labelNamespace
28842
+ })
28636
28843
  };
28637
28844
  } catch (error) {
28638
28845
  if (error instanceof ProvisioningError) throw error;
@@ -28830,19 +29037,33 @@ var WAFv2WebACLProvider = class {
28830
29037
  *
28831
29038
  * Lookup order:
28832
29039
  * 1. `--resource <id>=<arn>` override → verify with `GetWebACL` (parses
28833
- * Name/Id/Scope back out of the ARN).
29040
+ * Name/Id/Scope back out of the ARN). CloudFormation's compound
29041
+ * `name|id|scope` physical id — what `--migrate-from-cloudformation`
29042
+ * passes — is accepted too and recorded under the ARN (issue #3627).
28834
29043
  */
28835
29044
  async import(input) {
28836
29045
  if (input.knownPhysicalId) try {
28837
- const { id, name, scope } = parseWebACLArn(input.knownPhysicalId);
28838
- await this.getClient().send(new GetWebACLCommand({
29046
+ const compound = /^([^|]+)\|([^|]+)\|(REGIONAL|CLOUDFRONT)$/.exec(input.knownPhysicalId);
29047
+ const { id, name, scope } = compound ? {
29048
+ name: compound[1],
29049
+ id: compound[2],
29050
+ scope: compound[3]
29051
+ } : parseWebACLArn(input.knownPhysicalId);
29052
+ if (!id || !name) return null;
29053
+ const resp = await this.getClient().send(new GetWebACLCommand({
28839
29054
  Id: id,
28840
29055
  Name: name,
28841
29056
  Scope: scope
28842
29057
  }));
29058
+ const arn = resp.WebACL?.ARN;
29059
+ if (!arn) return null;
28843
29060
  return {
28844
- physicalId: input.knownPhysicalId,
28845
- attributes: {}
29061
+ physicalId: arn,
29062
+ attributes: definedAttributes({
29063
+ Arn: arn,
29064
+ Id: resp.WebACL?.Id,
29065
+ LabelNamespace: resp.WebACL?.LabelNamespace
29066
+ })
28846
29067
  };
28847
29068
  } catch (err) {
28848
29069
  if (err instanceof WAFNonexistentItemException) return null;
@@ -30012,6 +30233,18 @@ async function allSettledKeepingFirstRejection(promises, onAbandoned) {
30012
30233
  }
30013
30234
  }
30014
30235
  /**
30236
+ * The key for the resolver's per-region SDK client caches ({@link
30237
+ * IntrinsicFunctionResolver}'s `cfnClients`, `regionScopedClients` and
30238
+ * `serviceDiscoveryClients`): the region PLUS the credential fingerprint of the
30239
+ * configuration the cached client was built from (issue
30240
+ * [#3588](https://github.com/go-to-k/cdkd/issues/3588)). Encoded through
30241
+ * {@link injectiveKey}, so no region or profile spelling can forge another
30242
+ * pair's key.
30243
+ */
30244
+ function clientCacheKey(region, credentialConfig) {
30245
+ return injectiveKey(region, credentialFingerprint(credentialConfig));
30246
+ }
30247
+ /**
30015
30248
  * Is `region` safe to build an AWS SDK client from?
30016
30249
  *
30017
30250
  * This is a SECURITY gate, not an AWS region registry, and the distinction
@@ -30493,15 +30726,22 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30493
30726
  * foreign region per resolver, versus the ambient clients which are already
30494
30727
  * created and destroyed per stack by `deploy.ts`.
30495
30728
  *
30496
- * KEYED BY REGION ALONE, which means an entry also pins the CREDENTIAL
30497
- * configuration of whichever ambient instance was current at the first
30498
- * mismatch for that region. That is sound today because the credential half
30499
- * is process-wide rather than per-stack: `--profile` comes from one CLI
30500
- * option and `--role-arn` lands in `process.env`, so every ambient instance
30501
- * a run installs carries the same one. Widen the key to include
30502
- * {@link AwsClients.credentialConfig} the moment that stops being true —
30503
- * per-stack credentials would otherwise let one stack's lookups run under
30504
- * another's identity, which is a worse bug than the one this cache serves.
30729
+ * KEYED BY REGION AND CREDENTIAL FINGERPRINT ({@link clientCacheKey}, issue
30730
+ * [#3588](https://github.com/go-to-k/cdkd/issues/3588)). An entry pins the
30731
+ * credential configuration of the ambient instance it was derived from, and
30732
+ * that half is process-wide only for the CLI (one `--profile`, one
30733
+ * `--role-arn`): a LIBRARY caller can drive one resolver across two
30734
+ * `setAwsClients` installs (or cdkd's per-stack scopes) with different
30735
+ * explicit credentials, and keyed by region alone the second's lookups ran under the first's
30736
+ * identity. {@link cfnClients} and {@link serviceDiscoveryClients} share the
30737
+ * key for the same reason, and the two CloudFormation fallback memos
30738
+ * ({@link cfnExportsPromises}, {@link cfnStackOutputsCache}) carry the
30739
+ * fingerprint too.
30740
+ *
30741
+ * THE CLIENT HALF IS CLOSED, NOT THE WHOLE CLASS: {@link cachedDynamicReferences}
30742
+ * (keyed by the expression alone) and the PROCESS-GLOBAL account identity
30743
+ * behind `getAccountInfo` still assume one identity per resolver and per
30744
+ * process respectively — a value one identity read is served to the next.
30505
30745
  */
30506
30746
  regionScopedClients = /* @__PURE__ */ new Map();
30507
30747
  /**
@@ -30568,9 +30808,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30568
30808
  /**
30569
30809
  * Per-region CloudFormation clients for the cross-stack fallback
30570
30810
  * lookups (issue #1697). Keyed by region because `Fn::GetStackOutput`
30571
- * may target a region different from the consumer's deploy region.
30811
+ * may target a region different from the consumer's deploy region, and by
30812
+ * the credential fingerprint for {@link regionScopedClients}' reason.
30572
30813
  */
30573
- cfnClients = {};
30814
+ cfnClients = /* @__PURE__ */ new Map();
30574
30815
  /**
30575
30816
  * Memoized full `ListExports` listing for the `Fn::ImportValue`
30576
30817
  * fallback (issue #1697 review). Without it, EVERY cdkd-miss import
@@ -30582,14 +30823,18 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30582
30823
  * deploys. FAILED fetches are not cached (the rejection handler
30583
30824
  * clears the slot) so a transient throttle does not poison the rest
30584
30825
  * of the deploy's lookups.
30826
+ *
30827
+ * Keyed by the credential fingerprint (issue #3588): a listing is an answer
30828
+ * one identity was allowed to read, so it is never handed to another.
30585
30829
  */
30586
- cfnExportsPromise;
30830
+ cfnExportsPromises = /* @__PURE__ */ new Map();
30587
30831
  /**
30588
30832
  * Memoized per-(region, stack) `DescribeStacks` outputs for the
30589
30833
  * `Fn::GetStackOutput` fallback (issue #1697 review) — a stack
30590
30834
  * referencing the same CFn producer N times pays one call. Successful
30591
30835
  * lookups (including the definitive "stack does not exist" miss) are
30592
- * cached; lookup FAILURES are evicted so they are retried.
30836
+ * cached; lookup FAILURES are evicted so they are retried. Keyed by the
30837
+ * credential fingerprint as well, for {@link cfnExportsPromises}' reason.
30593
30838
  */
30594
30839
  cfnStackOutputsCache = /* @__PURE__ */ new Map();
30595
30840
  /**
@@ -30821,11 +31066,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30821
31066
  const loggedTarget = targetLogText ?? (targetRegion === this.explicitRegion ? this.explicitRegionLogText : void 0) ?? target;
30822
31067
  if (!isClientSafeRegion(target)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to build AWS clients for the region '${displaySafe(stripControlChars(loggedTarget)).slice(0, 64)}': it is not a valid AWS region name, and a region is substituted into the AWS service hostname.`));
30823
31068
  if (typeof ambient.withRegion !== "function") return ambient;
30824
- const cached = this.regionScopedClients.get(target);
31069
+ const cacheKey = clientCacheKey(target, ambient.credentialConfig ?? {});
31070
+ const cached = this.regionScopedClients.get(cacheKey);
30825
31071
  if (cached) return cached;
30826
31072
  if (canonicalizeRegion(ambient.configuredRegion) === target) return ambient;
30827
31073
  const scoped = ambient.withRegion(target);
30828
- this.regionScopedClients.set(target, scoped);
31074
+ this.regionScopedClients.set(cacheKey, scoped);
30829
31075
  this.logger.debug(`Using region-scoped AWS clients for ${displaySafe(loggedTarget)}`);
30830
31076
  return scoped;
30831
31077
  }
@@ -30856,21 +31102,21 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30856
31102
  * `getAccountInfo`'s in-flight promise and reach here one at a time, which
30857
31103
  * the case says out loud. A REJECTED import is evicted
30858
31104
  * so a transient failure does not poison the rest of the deploy, mirroring
30859
- * `cfnExportsPromise`. Lifetime is the resolver's own, like
31105
+ * `cfnExportsPromises`. Lifetime is the resolver's own, like
30860
31106
  * {@link regionScopedClients} and {@link cfnClients}: at most one per region
30861
31107
  * per resolver, versus the one-per-CALL this replaces.
30862
31108
  */
30863
31109
  async serviceDiscoveryClient() {
30864
31110
  const scoped = this.clientsForRegion(this.explicitRegion);
30865
31111
  const region = scoped.configuredRegion;
30866
- const key = region ?? "";
31112
+ const credentialConfig = scoped.credentialConfig ?? {};
31113
+ const key = clientCacheKey(region ?? "", credentialConfig);
30867
31114
  const cached = this.serviceDiscoveryClients.get(key);
30868
31115
  if (cached) return cached;
30869
31116
  const building = (async () => {
30870
31117
  const { ServiceDiscoveryClient } = await import("@aws-sdk/client-servicediscovery");
30871
31118
  return new ServiceDiscoveryClient({
30872
- ...awsClientDefaults({ profile: scoped.credentialConfig?.profile }),
30873
- ...scoped.credentialConfig ?? {},
31119
+ ...clientDefaultsFor(credentialConfig),
30874
31120
  ...region ? { region } : {}
30875
31121
  });
30876
31122
  })();
@@ -33276,12 +33522,14 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
33276
33522
  * the same not-found error anyway.
33277
33523
  */
33278
33524
  async lookupCfnExport(exportName, context) {
33279
- let listing = this.cfnExportsPromise;
33525
+ const listingKey = credentialFingerprint(ambientCredentialConfig());
33526
+ let listing = this.cfnExportsPromises.get(listingKey);
33280
33527
  if (!listing) {
33281
- listing = this.fetchAllCfnExports();
33282
- this.cfnExportsPromise = listing;
33283
- listing.catch(() => {
33284
- if (this.cfnExportsPromise === listing) this.cfnExportsPromise = void 0;
33528
+ const fetched = this.fetchAllCfnExports();
33529
+ listing = fetched;
33530
+ this.cfnExportsPromises.set(listingKey, fetched);
33531
+ fetched.catch(() => {
33532
+ if (this.cfnExportsPromises.get(listingKey) === fetched) this.cfnExportsPromises.delete(listingKey);
33285
33533
  });
33286
33534
  }
33287
33535
  try {
@@ -33351,7 +33599,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
33351
33599
  * contract as {@link lookupCfnExport}.
33352
33600
  */
33353
33601
  async lookupCfnStackOutputs(stackName, region, context, loggedRegionText) {
33354
- const cacheKey = injectiveKey(region, stackName);
33602
+ const cacheKey = injectiveKey(region, stackName, credentialFingerprint(ambientCredentialConfig()));
33355
33603
  let fetch = this.cfnStackOutputsCache.get(cacheKey);
33356
33604
  if (!fetch) {
33357
33605
  fetch = this.fetchCfnStackOutputs(stackName, region);
@@ -33402,19 +33650,19 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
33402
33650
  * Not routed through {@link clientsForRegion}: the region here is always
33403
33651
  * the one the caller named, never the ambient's, and a test double without
33404
33652
  * `credentialConfig` degrades to the default chain rather than throwing.
33405
- * Keyed by region alone for the reason {@link regionScopedClients} gives:
33406
- * the credential half is process-wide.
33653
+ * Keyed by region AND credential fingerprint for the reason
33654
+ * {@link regionScopedClients} gives (issue #3588).
33407
33655
  */
33408
33656
  getCfnClient(region) {
33409
- let client = this.cfnClients[region];
33657
+ const credentialConfig = ambientCredentialConfig();
33658
+ const key = clientCacheKey(region, credentialConfig);
33659
+ let client = this.cfnClients.get(key);
33410
33660
  if (!client) {
33411
- const credentialConfig = getAwsClients().credentialConfig ?? {};
33412
33661
  client = new CloudFormationClient({
33413
- ...awsClientDefaults({ profile: credentialConfig.profile }),
33414
- ...credentialConfig,
33662
+ ...clientDefaultsFor(credentialConfig),
33415
33663
  region
33416
33664
  });
33417
- this.cfnClients[region] = client;
33665
+ this.cfnClients.set(key, client);
33418
33666
  }
33419
33667
  return client;
33420
33668
  }
@@ -36756,7 +37004,7 @@ var CloudControlProvider = class {
36756
37004
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
36757
37005
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
36758
37006
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
36759
- const { ASGProvider } = await import("./asg-provider-DR5gOGbi.js").then((n) => n.n);
37007
+ const { ASGProvider } = await import("./asg-provider-C0qLsIu5.js").then((n) => n.n);
36760
37008
  const asgProvider = new ASGProvider();
36761
37009
  return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
36762
37010
  }
@@ -37306,7 +37554,7 @@ var CloudControlProvider = class {
37306
37554
  break;
37307
37555
  case "AWS::RDS::DBCluster":
37308
37556
  try {
37309
- const cluster = (await new RDSClient({ ...awsClientDefaults() }).send(new DescribeDBClustersCommand({ DBClusterIdentifier: physicalId }))).DBClusters?.[0];
37557
+ const cluster = (await new RDSClient({ ...ambientClientDefaults() }).send(new DescribeDBClustersCommand({ DBClusterIdentifier: physicalId }))).DBClusters?.[0];
37310
37558
  if (cluster) {
37311
37559
  if (cluster.Endpoint) enriched["Endpoint.Address"] = cluster.Endpoint;
37312
37560
  if (cluster.Port !== void 0) enriched["Endpoint.Port"] = String(cluster.Port);
@@ -37321,7 +37569,7 @@ var CloudControlProvider = class {
37321
37569
  break;
37322
37570
  case "AWS::RDS::DBInstance":
37323
37571
  try {
37324
- const inst = (await new RDSClient({ ...awsClientDefaults() }).send(new DescribeDBInstancesCommand({ DBInstanceIdentifier: physicalId }))).DBInstances?.[0];
37572
+ const inst = (await new RDSClient({ ...ambientClientDefaults() }).send(new DescribeDBInstancesCommand({ DBInstanceIdentifier: physicalId }))).DBInstances?.[0];
37325
37573
  if (inst) {
37326
37574
  if (inst.Endpoint?.Address) enriched["Endpoint.Address"] = inst.Endpoint.Address;
37327
37575
  if (inst.Endpoint?.Port !== void 0) enriched["Endpoint.Port"] = String(inst.Endpoint.Port);
@@ -37475,7 +37723,7 @@ var CloudControlProvider = class {
37475
37723
  break;
37476
37724
  case "AWS::ElastiCache::ReplicationGroup":
37477
37725
  try {
37478
- const rg = (await new ElastiCacheClient({ ...awsClientDefaults() }).send(new DescribeReplicationGroupsCommand({ ReplicationGroupId: physicalId }))).ReplicationGroups?.[0];
37726
+ const rg = (await new ElastiCacheClient({ ...ambientClientDefaults() }).send(new DescribeReplicationGroupsCommand({ ReplicationGroupId: physicalId }))).ReplicationGroups?.[0];
37479
37727
  if (rg) {
37480
37728
  const primaryNode = rg.NodeGroups?.[0];
37481
37729
  if (primaryNode?.PrimaryEndpoint?.Address) enriched["PrimaryEndPoint.Address"] = primaryNode.PrimaryEndpoint.Address;
@@ -37497,7 +37745,7 @@ var CloudControlProvider = class {
37497
37745
  break;
37498
37746
  case "AWS::Redshift::Cluster":
37499
37747
  try {
37500
- const cluster = (await new RedshiftClient({ ...awsClientDefaults() }).send(new DescribeClustersCommand({ ClusterIdentifier: physicalId }))).Clusters?.[0];
37748
+ const cluster = (await new RedshiftClient({ ...ambientClientDefaults() }).send(new DescribeClustersCommand({ ClusterIdentifier: physicalId }))).Clusters?.[0];
37501
37749
  if (cluster?.Endpoint) {
37502
37750
  if (cluster.Endpoint.Address) enriched["Endpoint.Address"] = cluster.Endpoint.Address;
37503
37751
  if (cluster.Endpoint.Port !== void 0) enriched["Endpoint.Port"] = String(cluster.Endpoint.Port);
@@ -37509,7 +37757,7 @@ var CloudControlProvider = class {
37509
37757
  break;
37510
37758
  case "AWS::OpenSearchService::Domain":
37511
37759
  try {
37512
- const domain = (await new OpenSearchClient({ ...awsClientDefaults() }).send(new DescribeDomainCommand({ DomainName: physicalId }))).DomainStatus;
37760
+ const domain = (await new OpenSearchClient({ ...ambientClientDefaults() }).send(new DescribeDomainCommand({ DomainName: physicalId }))).DomainStatus;
37513
37761
  if (domain) {
37514
37762
  const endpoint = domain.Endpoint ?? domain.Endpoints?.["vpc"];
37515
37763
  if (endpoint) enriched["DomainEndpoint"] = endpoint;
@@ -42359,88 +42607,6 @@ function isCustomResource(resourceType) {
42359
42607
  return resourceType.startsWith("Custom::") || resourceType === "AWS::CloudFormation::CustomResource";
42360
42608
  }
42361
42609
 
42362
- //#endregion
42363
- //#region src/provisioning/attribute-map.ts
42364
- /**
42365
- * The ONE spelling of the attribute-map rule an SDK provider's `create()` /
42366
- * `update()` / `import()` result follows (issue #3077, closing the `?? ''`
42367
- * shape `docs/provider-development.md` forbids under "Never store an
42368
- * empty-string placeholder"):
42369
- *
42370
- * an attribute cdkd could not read back is ABSENT from the map — never
42371
- * recorded as the empty string, and never as a present-but-`undefined` key.
42372
- *
42373
- * The consumers of `ResourceState.attributes` all key off exactly that
42374
- * distinction, and a sentinel puts every one of them on the wrong branch:
42375
- *
42376
- * - the deploy engine persists the map as-is, so `''` becomes the recorded
42377
- * value of a field AWS never assigned;
42378
- * - the intrinsic resolver serves a stored attribute whenever it is not
42379
- * `undefined`, so `''` shadows `constructAttribute` forever — the
42380
- * `AWS::EC2::Instance` IP / DNS / AZ arm re-describes the instance from
42381
- * AWS only on a MISS, the `Arn` arms of the RDS family rebuild the ARN
42382
- * only on a MISS;
42383
- * - the Outputs export pass publishes whatever the resolver returned, so a
42384
- * stored `''` is exported to every `Fn::ImportValue` consumer.
42385
- *
42386
- * It exists as a MODULE rather than a per-provider idiom because the rule was
42387
- * prose and 96 attribute-position `?? ''` / `|| ''` / bare `''` sites across
42388
- * 18 providers shipped anyway (measured 2026-09-14 with the TypeScript-compiler
42389
- * walk that is now `tests/unit/provisioning/attribute-map.test.ts`). A helper
42390
- * that DROPS the key is the one form a provider cannot spell wrong by habit.
42391
- *
42392
- * Two values are treated as "not read back":
42393
- *
42394
- * - `undefined`: the SDK's spelling of an unassigned optional member.
42395
- * - `null`: no AWS SDK v3 read-back reports a scalar attribute as `null`, and
42396
- * a stored `null` takes the resolver's "not `undefined`" hit branch exactly
42397
- * like `''` does.
42398
- *
42399
- * Every other value is kept verbatim — INCLUDING an empty string AWS itself
42400
- * reported (`false`, `0`, an empty array and an object likewise). The helper
42401
- * stops cdkd MANUFACTURING `''` for a field it never read; a `''` that came
42402
- * off the wire is a known value with a meaning the provider owns. The EC2
42403
- * Instance provider is where that distinction is load-bearing: a `running`
42404
- * instance with no public address is described with `PublicDnsName: ''` and
42405
- * no `PublicIpAddress`, and CloudFormation answers `''` for both — a KNOWN
42406
- * empty, recorded as such so a reference does not degrade to the instance id
42407
- * on every resolution — while the same `''` on a `pending` instance means
42408
- * "not yet", which that provider maps to `undefined` BEFORE calling this
42409
- * helper (its `describedInstanceAttributes`). A caller that knows a `''`
42410
- * means "unknown" is the one that must say so; this helper cannot tell.
42411
- *
42412
- * Deliberately NOT a general "compact this record" utility: it is scoped to
42413
- * attribute maps. A property bag keeps its `undefined`-valued keys out by other
42414
- * means (`.claude/rules/provider-property-fidelity.md`, "Remove the key rather
42415
- * than setting it to `undefined`").
42416
- *
42417
- * A LEAF module (imports nothing) so every provider can take it without adding
42418
- * an edge to the provider import ring.
42419
- */
42420
- /**
42421
- * Build an attribute map from read-back values, omitting every key whose value
42422
- * is `undefined` or `null`.
42423
- */
42424
- function definedAttributes(entries) {
42425
- const out = {};
42426
- for (const [key, value] of Object.entries(entries)) {
42427
- if (value === void 0 || value === null) continue;
42428
- out[key] = value;
42429
- }
42430
- return out;
42431
- }
42432
- /**
42433
- * `String(value)` for a numeric read-back whose CloudFormation attribute is a
42434
- * string (`Endpoint.Port`), or `undefined` when the field is unassigned — so
42435
- * `definedAttributes` drops the key instead of recording the literal
42436
- * `'undefined'`, which is what `String(described?.Port ?? '')` yields once its
42437
- * `?? ''` is merely deleted.
42438
- */
42439
- function stringifyIfAssigned(value) {
42440
- if (value === void 0 || value === null) return void 0;
42441
- return String(value);
42442
- }
42443
-
42444
42610
  //#endregion
42445
42611
  //#region src/provisioning/update-removal.ts
42446
42612
  /**
@@ -44746,8 +44912,11 @@ async function planOrphanAdoption(params) {
44746
44912
  const claims = async () => siblingClaims ??= await params.readSiblingClaims();
44747
44913
  for (const record of params.records) {
44748
44914
  const { logicalId, state } = record;
44915
+ const shownId = displayIdent(logicalId);
44916
+ const shownType = displayIdent(state.resourceType);
44917
+ const shownPhysicalId = displaySafe(state.physicalId);
44749
44918
  if (params.managedLogicalIds.has(logicalId)) {
44750
- params.logger.debug(`orphan ${logicalId}: already present in state.resources — dropping the stale record`);
44919
+ params.logger.debug(`orphan ${shownId}: already present in state.resources — dropping the stale record`);
44751
44920
  continue;
44752
44921
  }
44753
44922
  let provider;
@@ -44755,12 +44924,12 @@ async function planOrphanAdoption(params) {
44755
44924
  provider = params.getProvider(state.resourceType, state.provisionedBy);
44756
44925
  } catch (error) {
44757
44926
  outcome.remaining.push(record);
44758
- outcome.notices.push(`${logicalId} (${state.resourceType}) is still in AWS as ${state.physicalId} from an earlier rollback, but this build cannot route that type (${error instanceof Error ? error.message : String(error)}) — cdkd is not adopting it.`);
44927
+ outcome.notices.push(`${shownId} (${shownType}) is still in AWS as ${shownPhysicalId} from an earlier rollback, but this build cannot route that type (${displayAwsMessage(error instanceof Error ? error.message : String(error))}) — cdkd is not adopting it.`);
44759
44928
  continue;
44760
44929
  }
44761
44930
  if (!provider.import) {
44762
44931
  outcome.remaining.push(record);
44763
- outcome.notices.push(`${logicalId} (${state.resourceType}) was left in AWS by an earlier rollback as ${state.physicalId}, but its provider cannot verify it — cdkd is not adopting it.`);
44932
+ outcome.notices.push(`${shownId} (${shownType}) was left in AWS by an earlier rollback as ${shownPhysicalId}, but its provider cannot verify it — cdkd is not adopting it.`);
44764
44933
  continue;
44765
44934
  }
44766
44935
  let found;
@@ -44774,32 +44943,32 @@ async function planOrphanAdoption(params) {
44774
44943
  knownPhysicalId: state.physicalId
44775
44944
  });
44776
44945
  } catch (error) {
44777
- outcome.notices.push(`${logicalId} (${state.resourceType}) is recorded as left in AWS as ${state.physicalId}, but cdkd could not confirm it exists (${error instanceof Error ? error.message : String(error)}) — keeping the record and not adopting it this run.`);
44946
+ outcome.notices.push(`${shownId} (${shownType}) is recorded as left in AWS as ${shownPhysicalId}, but cdkd could not confirm it exists (${displayAwsMessage(error instanceof Error ? error.message : String(error))}) — keeping the record and not adopting it this run.`);
44778
44947
  outcome.remaining.push(record);
44779
44948
  continue;
44780
44949
  }
44781
44950
  if (found !== null && found.physicalId !== state.physicalId) {
44782
44951
  outcome.remaining.push(record);
44783
- outcome.notices.push(`${logicalId} (${state.resourceType}): cdkd asked about ${state.physicalId} and its provider answered for ${found.physicalId} — not adopting.`);
44952
+ outcome.notices.push(`${shownId} (${shownType}): cdkd asked about ${shownPhysicalId} and its provider answered for ${displaySafe(found.physicalId)} — not adopting.`);
44784
44953
  continue;
44785
44954
  }
44786
44955
  if (found === null) {
44787
- params.logger.debug(`orphan ${logicalId}: ${state.physicalId} no longer exists in AWS — dropping the record`);
44956
+ params.logger.debug(`orphan ${shownId}: ${shownPhysicalId} no longer exists in AWS — dropping the record`);
44788
44957
  continue;
44789
44958
  }
44790
44959
  if (params.nameProperties(state.resourceType).length === 0 || ADOPTION_REFUSED_TYPES.has(state.resourceType)) {
44791
44960
  outcome.remaining.push(record);
44792
44961
  const why = ADOPTION_REFUSED_TYPES.has(state.resourceType) ? "a new deploy of it mints a new resource rather than colliding, and cdkd cannot update one in place — so adopting it would end in a replacement that destroys what DeletionPolicy: Retain preserved" : "cdkd does not derive that resource's physical name, so a new deploy mints a new resource instead of colliding";
44793
- outcome.notices.push(`${logicalId} (${state.resourceType}) is still in AWS as ${state.physicalId} from an earlier rollback. cdkd does not re-adopt this type: ${why}. Delete it yourself when you no longer need it.`);
44962
+ outcome.notices.push(`${shownId} (${shownType}) is still in AWS as ${shownPhysicalId} from an earlier rollback. cdkd does not re-adopt this type: ${why}. Delete it yourself when you no longer need it.`);
44794
44963
  continue;
44795
44964
  }
44796
44965
  if (!templateStillDeclares(params.template, record, params.nameProperties(state.resourceType))) {
44797
44966
  outcome.remaining.push(record);
44798
- outcome.notices.push(`${logicalId} (${state.resourceType}) is still in AWS as ${state.physicalId} from an earlier rollback. This deploy does not create it under that name, so cdkd is leaving it alone.`);
44967
+ outcome.notices.push(`${shownId} (${shownType}) is still in AWS as ${shownPhysicalId} from an earlier rollback. This deploy does not create it under that name, so cdkd is leaving it alone.`);
44799
44968
  continue;
44800
44969
  }
44801
44970
  if ((await claims()).has(state.physicalId)) {
44802
- outcome.refusals.push(`${logicalId}: ${state.physicalId} is already recorded by another cdkd stack. cdkd will not adopt a resource another stack manages.`);
44971
+ outcome.refusals.push(`${shownId}: ${shownPhysicalId} is already recorded by another cdkd stack. cdkd will not adopt a resource another stack manages.`);
44803
44972
  outcome.remaining.push(record);
44804
44973
  continue;
44805
44974
  }
@@ -44852,7 +45021,7 @@ function makeSiblingClaimReader(params) {
44852
45021
  try {
44853
45022
  refs = await stateBackend.listStacks();
44854
45023
  } catch (error) {
44855
- logger.debug(`orphan adoption: could not list sibling stacks — ${error instanceof Error ? error.message : String(error)}`);
45024
+ logger.debug(`orphan adoption: could not list sibling stacks — ${displayAwsMessage(error instanceof Error ? error.message : String(error))}`);
44856
45025
  return claimed;
44857
45026
  }
44858
45027
  for (const ref of refs) {
@@ -44862,7 +45031,7 @@ function makeSiblingClaimReader(params) {
44862
45031
  const sibling = await stateBackend.getState(ref.stackName, ref.region);
44863
45032
  for (const record of Object.values(sibling?.state.resources ?? {})) claimed.add(record.physicalId);
44864
45033
  } catch (error) {
44865
- logger.debug(`orphan adoption: skipping unreadable state for ${ref.stackName} — ${error instanceof Error ? error.message : String(error)}`);
45034
+ logger.debug(`orphan adoption: skipping unreadable state for ${displayStackName(ref.stackName)} — ${displayAwsMessage(error instanceof Error ? error.message : String(error))}`);
44866
45035
  }
44867
45036
  }
44868
45037
  return claimed;
@@ -50785,7 +50954,7 @@ var DeployEngine = class DeployEngine {
50785
50954
  if (plan.refusals.length > 0) throw new Error(`Deploy refused — cdkd left ${plan.refusals.length} resource(s) in AWS that it cannot safely re-adopt:\n ${plan.refusals.join("\n ")}`);
50786
50955
  for (const [logicalId, record] of Object.entries(plan.adopted)) {
50787
50956
  currentState.resources[logicalId] = record;
50788
- this.logger.info(`Adopting ${logicalId} (${record.resourceType}) left in AWS by an earlier rollback as ${record.physicalId}`);
50957
+ this.logger.info(`Adopting ${displayIdent(logicalId)} (${displayIdent(record.resourceType)}) left in AWS by an earlier rollback as ${displaySafe(record.physicalId)}`);
50789
50958
  }
50790
50959
  if (records.length > 0) currentState.orphans = plan.remaining;
50791
50960
  return plan;
@@ -52252,5 +52421,5 @@ var DeployEngine = class DeployEngine {
52252
52421
  };
52253
52422
 
52254
52423
  //#endregion
52255
- export { yellow as $, NestedStackChildDirectDestroyError as $a, resolveSkipPrefix as $i, refuseMalformedOutputsForDestroy as $n, hasReadableExportSet as $r, readConfigString as $t, createPreDeleteFinalSnapshot as A, absoluteAssemblyPathEscape as Aa, describeDockerFailure as Ai, malformedExportSourceWarning as An, identityKeyFor as Ar, disableInstanceApiTermination as At, isStatefulRecreateTargetSync as B, setAwsClients as Ba, runDockerStreaming as Bi, malformedResourceEntriesWarning as Bn, redactSecretsForState as Br, isUnboundTemplateParameter as Bt, namesAnyDeclaredParameter as C, globMatches as Ca, validateAssetBucketName as Ci, displayLogicalId as Cn, __exportAll as Co, STATE_SOURCED_CROSS_GENERATION_RULES as Cr, CloudControlProvider as Ct, PRE_DELETE_SNAPSHOT_TYPES as D, hasPlainPrototype$1 as Da, resolveDockerContextDirectory as Di, isReadableOrphanRecord as Dn, createSecretMasker as Dr, deleteIndeterminateGuards as Dt, ATOMIC_FINAL_SNAPSHOT_TYPES as E, hasOwnKey as Ea, buildDockerImage as Ei, hasReadableResources as En, carriesSecretMask as Er, UNSPECIFIED_SKIP_REASON as Et, extractDeploymentEventError as F, resolveBucketRegion as Fa, isDockerClientEnvKey as Fi, malformedOrphansRefusalMessage as Fn, maskSecretsInText as Fr, cfnRefValueFromPhysicalId as Ft, toCfnInteger as G, DependencyError as Ga, getDockerImageBySourceHash as Gi, refuseDivergentRecordRegionForDestroy as Gn, UNREPRODUCIBLE_LOCK_CLAUSE as Gr, normalizeAwsTagsToCfn as Gt, WARM_THROUGHPUT_MEMBERS as H, CdkdError as Ha, escapeRegExp$1 as Hi, malformedResourcesWarning as Hn, LockManager as Hr, refStateLookupFromResource as Ht, makeSiblingClaimReader as I, AwsClients as Ia, isMalformedEnvKey as Ii, malformedOrphansWarning as In, pathCrossesDottedKey as Ir, coerceParameterTypedValue as It, bold as J, IntrinsicResolutionRefusalError as Ja, getDefaultStateBucketName as Ji, refuseMalformedOrphanRecordsForDestroy as Jn, forceQuitRecoveryClause as Jr, coerceCfnBoolean as Jt, toFiniteNumber as K, DeployCancelledError as Ka, Synthesizer as Ki, refuseMalformedNestedChildOutputs as Kn, buildForceUnlockCommand as Kr, resolveExplicitPhysicalId as Kt, planOrphanAdoption as L, getAwsClients as La, partitionSensitiveEnv as Li, malformedOutputsRefusalMessage as Ln, recordMaskOnlyValue as Lr, getAccountInfo as Lt, refusesFinalSnapshot as M, renderAssemblyPathEscape as Ma, finchSecretArgvRefusal as Mi, malformedOrphanRecordsRefusalMessage as Mn, isUncertifiedBaselineMaskPosition as Mr, IntrinsicFunctionResolver as Mt, unsupportedFinalSnapshotError as N, resolveAssemblyPath as Na, formatDockerLoginError as Ni, malformedOrphanRecordsWarning as Nn, markSameGenerationBag as Nr, carriesDynamicReference as Nt, buildFinalSnapshotIdentifier as O, nullPrototypeRecord as Oa, describeDockerCapturedOutput as Oi, isReadableResourceEntry as On, dynamicReferenceTokens as Or, deleteSkipReason as Ot, makeCanonicalizePropertiesFn as P, clearBucketRegionCache as Pa, getDockerCmd as Pi, malformedOrphanRowsKeptWarning as Pn, maskSecretsInError as Pr, carriesFetchableDynamicReference as Pt, red as Q, MalformedProducerRecordRefusalError as Qa, resolveCaptureObservedState as Qi, refuseMalformedOutputs as Qn, exportNamesCarriedFrom as Qr, configStringRefusal as Qt, MULTI_REGION_RECREATE_BLOCKED_TYPES as R, resetAwsClients as Ra, redactDockerArgvValues as Ri, malformedOutputsWarning as Rn, recordMaskOnlyValuesIn as Rr, isImpossibleEmptyStoredAttribute as Rt, computeParameterTaint as S, failedStageNote as Sa, readBootstrapMarkerBody as Si, deployRefusesOrphanRowsReason as Sn, safeStringify$1 as So, STATE_SOURCED_BASELINE_RULES as Sr, startInterruptWatch as St, resourcesNamingDeclaredParameter as T, defineOwnKey as Ta, buildDenyExternalAccessPolicy as Ti, hasReadableOutputs as Tn, TEMPLATE_SOURCED_RULES as Tr, ccProtectionRegistryTypes as Tt, coerceWarmThroughput as U, ConfigError as Ua, stripControlChars as Ui, malformedStateRefusalMessage as Un, S3StateBackend as Ur, isSettledInstanceState as Ut, renderStatefulReason as V, AssetError as Va, warnFinchArgvExposure as Vi, malformedResourcePropertiesWarning as Vn, scrubResourceRecord as Vr, parameterTypeMayLoseSecretIdentity as Vt, isWarmThroughputDecrease as W, CrossAccountSecretRefusalError as Wa, AssetManifestLoader as Wi, previewableOrphanRecords as Wn, rebuildClientForBucketRegion as Wr, WAFv2WebACLProvider as Wt, gray as X, LocalStartServiceError as Xa, resolveApp as Xi, refuseMalformedOrphansForDestroy as Xn, CUSTOM_RESOURCE_RESPONSE_PREFIX as Xr, configBooleanRefusal as Xt, cyan as Y, LocalInvokeBuildError as Ya, getLegacyStateBucketName as Yi, refuseMalformedOrphans as Yn, formatLockExpiry as Yr, coerceCfnInteger as Yt, green as Z, LockError as Za, resolveAutoAssetStorage as Zi, refuseMalformedOrphansForOrphan as Zn, DEFAULT_STATE_PREFIX as Zr, configIntegerRefusal as Zt, bagHoldsSecretExpression as _, expectedOwnerParam as _a, assertAssetBucketRegion as _i, DiffCalculator as _n, isTransientServerError as _o, findSilentDropProperties as _r, maskerOrIdentity as _t, DeploymentEventsStore as a, ssmResolvedValueType as aa, orphansAfterRollback as ai, producerRegionsFromState as an, StackTerminationProtectionError as ao, refuseMalformedState as ar, secretBearingStateKeyWarning as at, mergeNoChangeOutputs as b, derivePartitionAndUrlSuffix as ba, isCrossRegionRedirect as bi, UNREADABLE_ORPHANS_CONTAINER_ROW as bn, retryClassificationText as bo, DagBuilder as br, interruptWatchListenerCount as bt, replayFailedOperations as c, MIGRATE_TMP_PREFIX as ca, AssetPublisher as ci, s3BucketDomainName as cn, formatError as co, repairMalformedOutputsForReadOnly as cr, IAMRoleProvider as ct, updatePartialMessage as d, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as da, buildAssetRedirectMap as di, s3BucketWebsiteUrl as dn, withErrorHandling as do, repairMalformedResourcesForReadOnly as dr, definedAttributes as dt, resolveStateBucketWithDefault as ea, hasReasonlessBaselineRefusal as ei, replayWarn as en, PartialFailureError as eo, refuseMalformedResourceAttributesForOrphan as er, collectDeclaredOutputNames as et, updatePartialReason as f, injectiveKey as fa, createAssetRedirectResolver as fi, applyRoleArnIfSet as fn, pasteableCommand as fo, safeRegion as fr, stringifyIfAssigned as ft, computeImplicitDeleteEdges as g, decodeListingKey as ga, BOOTSTRAP_MARKER_PREFIX as gi, envCredentialsAreAssumedRole as gn, isThrottlingError as go, findActionableSilentDrops as gr, maskDeep as gt, IMPLICIT_DELETE_DEPENDENCIES as h, producerRecordKey as ha, AssetModeResolver as hi, callerEnvCredentials as hn, isRetryableTransientError as ho, unreadableOrphanRecords as hr, createMaskedRetryLogger as ht, DeploymentEventsReader as i, warnDeprecatedNoPrefixCliFlag as ia, isReadableBag as ii, classifyReplaySecretRegion as in, StackHasActiveImportsError as io, refuseMalformedResourcesForDestroy as ir, secretBearing as it, isFinalSnapshotError as j, displayAssemblyPath as ja, dockerSpawnEnvWithSensitive as ji, malformedLocalOutputsWarning as jn, isSingleDynamicReferenceToken as jr, isTerminationProtectionPropagationError as jt, ccRoutedFinalSnapshotError as k, ownValue as ka, describeDockerExecFailure as ki, malformedExportNamesWarning as kn, errorCauseChain as kr, isWaitAbandonedError as kt, replayRollback as l, findLargeInlineResources as la, stringifyValue as li, s3BucketDualStackDomainName as ln, isCdkdError as lo, repairMalformedResourceEntriesForReadOnly as lr, collectInlinePolicyNamesManagedBySiblings as lt, maskingRetryLogger as m, producerCoordinateKey as ma, rewriteTemplateAssetReferences as mi, applyCallerIdentityCredentials as mn, isMarkedNonRetryable as mo, unpreviewableOrphanRecords as mr, wouldReturnToSdkProvider as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, resolveUseCdkBootstrapAssets as na, importableOutputKeys as ni, requireConfigObject as nn, ResourceTimeoutError as no, refuseMalformedResourceEntriesForOrphan as nr, exportAliasCollisionScrubWarning as nt, planFailedOps as o, CFN_TEMPLATE_BODY_LIMIT as oa, orphansCarriedFrom as oi, withSharedDrainBudget as on, StateError as oo, repairMalformedOrphanRecordsForReadOnly as or, secretSafeKeyDisplay as ot, withResourceDeadline as p, injectiveKeyPrefix as pa, loadPublishableAssetManifest as pi, AWS_CREDENTIAL_ENV_KEYS as pn, shellQuote as po, safeStackName as pr, ProviderRegistry as pt, formatResourceLine as q, DynamicReferenceRegionAmbiguousError as qa, synthesisStatusMessage as qi, refuseMalformedOrphanRecords as qn, buildLockContentionMessage as qr, assertRegionMatch as qt, DeployEngine as r, stateBucketExistenceConfirmed as ra, importableOutputs as ri, requireConfigString as rn, ResourceUpdateNotSupportedError as ro, refuseMalformedResourcePropertiesForOrphan as rr, isExportAliasCollision as rt, planRollback as s, CFN_TEMPLATE_URL_LIMIT as sa, shouldRetainResource as si, s3BucketArn as sn, SynthesisError as so, repairMalformedOrphansForReadOnly as sr, getCurrentResourceSecrets as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, resolveStateBucketWithDefaultAndSource as ta, hasUnverifiableParameterRefusal as ti, requireConfigArray as tn, ProvisioningError as to, refuseMalformedResourceEntries as tr, collectPublishedOutputNames as tt, resolveReplacementOldType as u, uploadCfnTemplate as ua, WorkGraph as ui, s3BucketRegionalDomainName as un, normalizeAwsError as uo, repairMalformedResourcePropertiesForReadOnly as ur, clearOnUpdateRemoval as ut, isSecretBearingReferenceString as v, PARTITION_TABLE as va, ensureAssetStorage as vi, INTRINSIC_KEYS as vn, markNonRetryable as vo, describeTypeWithThrottleRetry as vr, beginCommandInterruptScope as vt, reachableDivergentParameters as w, processStackMessages as wa, validateContainerRepoName as wi, hasReadableOrphans as wn, STATE_SOURCED_READBACK_RULES as wr, slowCcOperationTimeoutMs as wt, bindingSkippedOutputs as x, AssemblyReader as xa, parseBootstrapMarker as xi, UNREADABLE_RESOURCES_MAP_ROW as xn, describeAwsFailure as xo, TemplateParser as xr, isInterruptedWaitError as xt, keptWholeReasonText as y, canonicalizeRegion as ya, getBootstrapMarkerKey as yi, STATE_RESOURCES_MALFORMED as yn, markRedactedCause as yo, withRetry as yr, endCommandInterruptScope as yt, isStatefulRecreateTargetForReplace as z, runWithStackAwsClients as za, runDockerForeground as zi, malformedRenderedContainersWarning as zn, recoverMaskedOutput as zr, isNamelessDynamicReferenceError as zt };
52256
- //# sourceMappingURL=deploy-engine-BwDlgFRs.js.map
52424
+ export { yellow as $, MalformedProducerRecordRefusalError as $a, resolveSkipPrefix as $i, refuseMalformedOutputsForDestroy as $n, hasReadableExportSet as $r, requireConfigArray as $t, createPreDeleteFinalSnapshot as A, ownValue as Aa, describeDockerFailure as Ai, malformedExportSourceWarning as An, identityKeyFor as Ar, IntrinsicFunctionResolver as At, isStatefulRecreateTargetSync as B, runWithStackAwsClients as Ba, runDockerStreaming as Bi, malformedResourceEntriesWarning as Bn, redactSecretsForState as Br, refStateLookupFromResource as Bt, namesAnyDeclaredParameter as C, failedStageNote as Ca, validateAssetBucketName as Ci, displayLogicalId as Cn, safeStringify$1 as Co, STATE_SOURCED_CROSS_GENERATION_RULES as Cr, ccProtectionRegistryTypes as Ct, PRE_DELETE_SNAPSHOT_TYPES as D, hasOwnKey as Da, resolveDockerContextDirectory as Di, isReadableOrphanRecord as Dn, createSecretMasker as Dr, isWaitAbandonedError as Dt, ATOMIC_FINAL_SNAPSHOT_TYPES as E, defineOwnKey as Ea, buildDockerImage as Ei, hasReadableResources as En, carriesSecretMask as Er, deleteSkipReason as Et, extractDeploymentEventError as F, clearBucketRegionCache as Fa, isDockerClientEnvKey as Fi, malformedOrphansRefusalMessage as Fn, maskSecretsInText as Fr, getAccountInfo as Ft, toCfnInteger as G, CrossAccountSecretRefusalError as Ga, getDockerImageBySourceHash as Gi, refuseDivergentRecordRegionForDestroy as Gn, UNREPRODUCIBLE_LOCK_CLAUSE as Gr, assertRegionMatch as Gt, WARM_THROUGHPUT_MEMBERS as H, AssetError as Ha, escapeRegExp$1 as Hi, malformedResourcesWarning as Hn, LockManager as Hr, WAFv2WebACLProvider as Ht, makeSiblingClaimReader as I, resolveBucketRegion as Ia, isMalformedEnvKey as Ii, malformedOrphansWarning as In, pathCrossesDottedKey as Ir, isImpossibleEmptyStoredAttribute as It, bold as J, DynamicReferenceRegionAmbiguousError as Ja, getDefaultStateBucketName as Ji, refuseMalformedOrphanRecordsForDestroy as Jn, forceQuitRecoveryClause as Jr, configBooleanRefusal as Jt, toFiniteNumber as K, DependencyError as Ka, Synthesizer as Ki, refuseMalformedNestedChildOutputs as Kn, buildForceUnlockCommand as Kr, coerceCfnBoolean as Kt, planOrphanAdoption as L, AwsClients as La, partitionSensitiveEnv as Li, malformedOutputsRefusalMessage as Ln, recordMaskOnlyValue as Lr, isNamelessDynamicReferenceError as Lt, refusesFinalSnapshot as M, displayAssemblyPath as Ma, finchSecretArgvRefusal as Mi, malformedOrphanRecordsRefusalMessage as Mn, isUncertifiedBaselineMaskPosition as Mr, carriesFetchableDynamicReference as Mt, unsupportedFinalSnapshotError as N, renderAssemblyPathEscape as Na, formatDockerLoginError as Ni, malformedOrphanRecordsWarning as Nn, markSameGenerationBag as Nr, cfnRefValueFromPhysicalId as Nt, buildFinalSnapshotIdentifier as O, hasPlainPrototype$1 as Oa, describeDockerCapturedOutput as Oi, isReadableResourceEntry as On, dynamicReferenceTokens as Or, disableInstanceApiTermination as Ot, makeCanonicalizePropertiesFn as P, resolveAssemblyPath as Pa, getDockerCmd as Pi, malformedOrphanRowsKeptWarning as Pn, maskSecretsInError as Pr, coerceParameterTypedValue as Pt, red as Q, LockError as Qa, resolveCaptureObservedState as Qi, refuseMalformedOutputs as Qn, exportNamesCarriedFrom as Qr, replayWarn as Qt, MULTI_REGION_RECREATE_BLOCKED_TYPES as R, getAwsClients as Ra, redactDockerArgvValues as Ri, malformedOutputsWarning as Rn, recordMaskOnlyValuesIn as Rr, isUnboundTemplateParameter as Rt, computeParameterTaint as S, AssemblyReader as Sa, readBootstrapMarkerBody as Si, deployRefusesOrphanRowsReason as Sn, describeAwsFailure as So, STATE_SOURCED_BASELINE_RULES as Sr, slowCcOperationTimeoutMs as St, resourcesNamingDeclaredParameter as T, processStackMessages as Ta, buildDenyExternalAccessPolicy as Ti, hasReadableOutputs as Tn, TEMPLATE_SOURCED_RULES as Tr, deleteIndeterminateGuards as Tt, coerceWarmThroughput as U, CdkdError as Ua, stripControlChars as Ui, malformedStateRefusalMessage as Un, S3StateBackend as Ur, normalizeAwsTagsToCfn as Ut, renderStatefulReason as V, setAwsClients as Va, warnFinchArgvExposure as Vi, malformedResourcePropertiesWarning as Vn, scrubResourceRecord as Vr, isSettledInstanceState as Vt, isWarmThroughputDecrease as W, ConfigError as Wa, AssetManifestLoader as Wi, previewableOrphanRecords as Wn, rebuildClientForBucketRegion as Wr, resolveExplicitPhysicalId as Wt, gray as X, LocalInvokeBuildError as Xa, resolveApp as Xi, refuseMalformedOrphansForDestroy as Xn, CUSTOM_RESOURCE_RESPONSE_PREFIX as Xr, configStringRefusal as Xt, cyan as Y, IntrinsicResolutionRefusalError as Ya, getLegacyStateBucketName as Yi, refuseMalformedOrphans as Yn, formatLockExpiry as Yr, configIntegerRefusal as Yt, green as Z, LocalStartServiceError as Za, resolveAutoAssetStorage as Zi, refuseMalformedOrphansForOrphan as Zn, DEFAULT_STATE_PREFIX as Zr, readConfigString as Zt, bagHoldsSecretExpression as _, expectedOwnerParam as _a, assertAssetBucketRegion as _i, DiffCalculator as _n, isThrottlingError as _o, findSilentDropProperties as _r, endCommandInterruptScope as _t, DeploymentEventsStore as a, ssmResolvedValueType as aa, orphansAfterRollback as ai, producerRegionsFromState as an, StackHasActiveImportsError as ao, refuseMalformedState as ar, secretBearingStateKeyWarning as at, mergeNoChangeOutputs as b, derivePartitionAndUrlSuffix as ba, isCrossRegionRedirect as bi, UNREADABLE_ORPHANS_CONTAINER_ROW as bn, markRedactedCause as bo, DagBuilder as br, startInterruptWatch as bt, replayFailedOperations as c, MIGRATE_TMP_PREFIX as ca, AssetPublisher as ci, s3BucketDomainName as cn, SynthesisError as co, repairMalformedOutputsForReadOnly as cr, IAMRoleProvider as ct, updatePartialMessage as d, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as da, buildAssetRedirectMap as di, s3BucketWebsiteUrl as dn, normalizeAwsError as do, repairMalformedResourcesForReadOnly as dr, ProviderRegistry as dt, resolveStateBucketWithDefault as ea, hasReasonlessBaselineRefusal as ei, requireConfigObject as en, NestedStackChildDirectDestroyError as eo, refuseMalformedResourceAttributesForOrphan as er, collectDeclaredOutputNames as et, updatePartialReason as f, injectiveKey as fa, createAssetRedirectResolver as fi, applyRoleArnIfSet as fn, withErrorHandling as fo, safeRegion as fr, wouldReturnToSdkProvider as ft, computeImplicitDeleteEdges as g, decodeListingKey as ga, BOOTSTRAP_MARKER_PREFIX as gi, envCredentialsAreAssumedRole as gn, isRetryableTransientError as go, findActionableSilentDrops as gr, beginCommandInterruptScope as gt, IMPLICIT_DELETE_DEPENDENCIES as h, producerRecordKey as ha, AssetModeResolver as hi, callerEnvCredentials as hn, isMarkedNonRetryable as ho, unreadableOrphanRecords as hr, maskerOrIdentity as ht, DeploymentEventsReader as i, warnDeprecatedNoPrefixCliFlag as ia, isReadableBag as ii, classifyReplaySecretRegion as in, ResourceUpdateNotSupportedError as io, refuseMalformedResourcesForDestroy as ir, secretBearing as it, isFinalSnapshotError as j, absoluteAssemblyPathEscape as ja, dockerSpawnEnvWithSensitive as ji, malformedLocalOutputsWarning as jn, isSingleDynamicReferenceToken as jr, carriesDynamicReference as jt, ccRoutedFinalSnapshotError as k, nullPrototypeRecord as ka, describeDockerExecFailure as ki, malformedExportNamesWarning as kn, errorCauseChain as kr, isTerminationProtectionPropagationError as kt, replayRollback as l, findLargeInlineResources as la, stringifyValue as li, s3BucketDualStackDomainName as ln, formatError as lo, repairMalformedResourceEntriesForReadOnly as lr, collectInlinePolicyNamesManagedBySiblings as lt, maskingRetryLogger as m, producerCoordinateKey as ma, rewriteTemplateAssetReferences as mi, applyCallerIdentityCredentials as mn, shellQuote as mo, unpreviewableOrphanRecords as mr, maskDeep as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, resolveUseCdkBootstrapAssets as na, importableOutputKeys as ni, definedAttributes as nn, ProvisioningError as no, refuseMalformedResourceEntriesForOrphan as nr, exportAliasCollisionScrubWarning as nt, planFailedOps as o, CFN_TEMPLATE_BODY_LIMIT as oa, orphansCarriedFrom as oi, withSharedDrainBudget as on, StackTerminationProtectionError as oo, repairMalformedOrphanRecordsForReadOnly as or, secretSafeKeyDisplay as ot, withResourceDeadline as p, injectiveKeyPrefix as pa, loadPublishableAssetManifest as pi, AWS_CREDENTIAL_ENV_KEYS as pn, pasteableCommand as po, safeStackName as pr, createMaskedRetryLogger as pt, formatResourceLine as q, DeployCancelledError as qa, synthesisStatusMessage as qi, refuseMalformedOrphanRecords as qn, buildLockContentionMessage as qr, coerceCfnInteger as qt, DeployEngine as r, stateBucketExistenceConfirmed as ra, importableOutputs as ri, stringifyIfAssigned as rn, ResourceTimeoutError as ro, refuseMalformedResourcePropertiesForOrphan as rr, isExportAliasCollision as rt, planRollback as s, CFN_TEMPLATE_URL_LIMIT as sa, shouldRetainResource as si, s3BucketArn as sn, StateError as so, repairMalformedOrphansForReadOnly as sr, getCurrentResourceSecrets as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, resolveStateBucketWithDefaultAndSource as ta, hasUnverifiableParameterRefusal as ti, requireConfigString as tn, PartialFailureError as to, refuseMalformedResourceEntries as tr, collectPublishedOutputNames as tt, resolveReplacementOldType as u, uploadCfnTemplate as ua, WorkGraph as ui, s3BucketRegionalDomainName as un, isCdkdError as uo, repairMalformedResourcePropertiesForReadOnly as ur, clearOnUpdateRemoval as ut, isSecretBearingReferenceString as v, PARTITION_TABLE as va, ensureAssetStorage as vi, INTRINSIC_KEYS as vn, isTransientServerError as vo, describeTypeWithThrottleRetry as vr, interruptWatchListenerCount as vt, reachableDivergentParameters as w, globMatches as wa, validateContainerRepoName as wi, hasReadableOrphans as wn, __exportAll as wo, STATE_SOURCED_READBACK_RULES as wr, UNSPECIFIED_SKIP_REASON as wt, bindingSkippedOutputs as x, ambientClientDefaults as xa, parseBootstrapMarker as xi, UNREADABLE_RESOURCES_MAP_ROW as xn, retryClassificationText as xo, TemplateParser as xr, CloudControlProvider as xt, keptWholeReasonText as y, canonicalizeRegion as ya, getBootstrapMarkerKey as yi, STATE_RESOURCES_MALFORMED as yn, markNonRetryable as yo, withRetry as yr, isInterruptedWaitError as yt, isStatefulRecreateTargetForReplace as z, resetAwsClients as za, runDockerForeground as zi, malformedRenderedContainersWarning as zn, recoverMaskedOutput as zr, parameterTypeMayLoseSecretIdentity as zt };
52425
+ //# sourceMappingURL=deploy-engine-CkF6PyFY.js.map