@go-to-k/cdkd 0.285.12 → 0.285.14

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 { _ as withStackName, d as applyDefaultNameForFallback, f as generateResourceName, h as looksLikeCdkdGeneratedName, m as getCurrentStackName, n as getLogger, p as generateResourceNameWithFallback, r as isStdoutReservedForPayload, s as getLiveRenderer } from "./logger-Dw-3y48G.js";
2
2
  import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
3
- import { t as getCdkdVersion } from "./version-CS-eKBJA.js";
3
+ import { t as getCdkdVersion } from "./version-DP5kGlzj.js";
4
4
  import { AsyncLocalStorage } from "node:async_hooks";
5
5
  import { randomUUID } from "node:crypto";
6
6
  import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -2705,7 +2705,10 @@ var AZContextProvider = class {
2705
2705
  async resolve(props) {
2706
2706
  const region = props["region"] || this.awsConfig?.region;
2707
2707
  this.logger.debug(`Fetching availability zones for region: ${region}`);
2708
- const client = new EC2Client({ ...region && { region } });
2708
+ const client = new EC2Client({
2709
+ ...awsClientDefaults(),
2710
+ ...region && { region }
2711
+ });
2709
2712
  try {
2710
2713
  const azs = ((await client.send(new DescribeAvailabilityZonesCommand({}))).AvailabilityZones ?? []).filter((az) => az.State === "available").map((az) => az.ZoneName).filter(Boolean).sort();
2711
2714
  this.logger.debug(`Found ${azs.length} availability zones: ${azs.join(", ")}`);
@@ -2735,7 +2738,10 @@ var SSMContextProvider = class {
2735
2738
  const parameterName = props["parameterName"];
2736
2739
  if (!parameterName) throw new Error("SSM context provider requires parameterName property");
2737
2740
  this.logger.debug(`Reading SSM parameter: ${parameterName} (region: ${region})`);
2738
- const client = new SSMClient({ ...region && { region } });
2741
+ const client = new SSMClient({
2742
+ ...awsClientDefaults(),
2743
+ ...region && { region }
2744
+ });
2739
2745
  try {
2740
2746
  const response = await client.send(new GetParameterCommand({ Name: parameterName }));
2741
2747
  if (!response.Parameter || response.Parameter.Value === void 0) {
@@ -2774,7 +2780,10 @@ var HostedZoneContextProvider = class {
2774
2780
  const vpcId = props["vpcId"];
2775
2781
  if (!domainName) throw new Error("Hosted zone context provider requires domainName property");
2776
2782
  this.logger.debug(`Looking up hosted zone: ${domainName} (private: ${privateZone})`);
2777
- const client = new Route53Client({ ...region && { region } });
2783
+ const client = new Route53Client({
2784
+ ...awsClientDefaults(),
2785
+ ...region && { region }
2786
+ });
2778
2787
  try {
2779
2788
  const zones = (await client.send(new ListHostedZonesByNameCommand({
2780
2789
  DNSName: domainName,
@@ -2825,7 +2834,10 @@ var VpcContextProvider = class {
2825
2834
  const subnetGroupNameTag = props["subnetGroupNameTag"] || "aws-cdk:subnet-name";
2826
2835
  const returnVpnGateways = props["returnVpnGateways"];
2827
2836
  this.logger.debug(`Looking up VPC (region: ${region}, filter: ${JSON.stringify(filter)})`);
2828
- const client = new EC2Client({ ...region && { region } });
2837
+ const client = new EC2Client({
2838
+ ...awsClientDefaults(),
2839
+ ...region && { region }
2840
+ });
2829
2841
  try {
2830
2842
  const vpcFilters = filter ? Object.entries(filter).map(([name, value]) => ({
2831
2843
  Name: name,
@@ -2988,7 +3000,10 @@ var CcApiContextProvider = class {
2988
3000
  const ignoreErrorOnMissingContext = props["ignoreErrorOnMissingContext"];
2989
3001
  if (!typeName) throw new Error("CC API context provider requires typeName property");
2990
3002
  this.logger.debug(`CC API lookup: ${typeName}${exactIdentifier ? ` (id: ${exactIdentifier})` : ""} (region: ${region})`);
2991
- const client = new CloudControlClient({ ...region && { region } });
3003
+ const client = new CloudControlClient({
3004
+ ...awsClientDefaults(),
3005
+ ...region && { region }
3006
+ });
2992
3007
  try {
2993
3008
  let resources;
2994
3009
  if (exactIdentifier) {
@@ -3115,7 +3130,10 @@ var AmiContextProvider = class {
3115
3130
  const owners = props["owners"];
3116
3131
  const filters = props["filters"];
3117
3132
  this.logger.debug(`Looking up AMI (region: ${region})`);
3118
- const client = new EC2Client({ ...region && { region } });
3133
+ const client = new EC2Client({
3134
+ ...awsClientDefaults(),
3135
+ ...region && { region }
3136
+ });
3119
3137
  try {
3120
3138
  const ec2Filters = filters ? Object.entries(filters).map(([name, values]) => ({
3121
3139
  Name: name,
@@ -3155,7 +3173,10 @@ var SecurityGroupContextProvider = class {
3155
3173
  const securityGroupName = props["securityGroupName"];
3156
3174
  const vpcId = props["vpcId"];
3157
3175
  this.logger.debug(`Looking up security group (id: ${securityGroupId}, name: ${securityGroupName}, region: ${region})`);
3158
- const client = new EC2Client({ ...region && { region } });
3176
+ const client = new EC2Client({
3177
+ ...awsClientDefaults(),
3178
+ ...region && { region }
3179
+ });
3159
3180
  try {
3160
3181
  const filters = [];
3161
3182
  if (securityGroupId) filters.push({
@@ -3206,7 +3227,10 @@ var LoadBalancerContextProvider = class {
3206
3227
  const loadBalancerArn = props["loadBalancerArn"];
3207
3228
  const loadBalancerType = props["loadBalancerType"];
3208
3229
  this.logger.debug(`Looking up load balancer (arn: ${loadBalancerArn}, region: ${region})`);
3209
- const client = new ElasticLoadBalancingV2Client({ ...region && { region } });
3230
+ const client = new ElasticLoadBalancingV2Client({
3231
+ ...awsClientDefaults(),
3232
+ ...region && { region }
3233
+ });
3210
3234
  try {
3211
3235
  let lbs = (await client.send(new DescribeLoadBalancersCommand({ ...loadBalancerArn && { LoadBalancerArns: [loadBalancerArn] } }))).LoadBalancers ?? [];
3212
3236
  if (loadBalancerType) lbs = lbs.filter((lb) => lb.Type === loadBalancerType);
@@ -3245,7 +3269,10 @@ var LoadBalancerListenerContextProvider = class {
3245
3269
  const listenerPort = props["listenerPort"];
3246
3270
  const listenerProtocol = props["listenerProtocol"];
3247
3271
  this.logger.debug(`Looking up load balancer listener (arn: ${listenerArn}, lb: ${loadBalancerArn}, region: ${region})`);
3248
- const client = new ElasticLoadBalancingV2Client({ ...region && { region } });
3272
+ const client = new ElasticLoadBalancingV2Client({
3273
+ ...awsClientDefaults(),
3274
+ ...region && { region }
3275
+ });
3249
3276
  try {
3250
3277
  let listeners = (await client.send(new DescribeListenersCommand({
3251
3278
  ...listenerArn && { ListenerArns: [listenerArn] },
@@ -3286,7 +3313,10 @@ var KeyContextProvider = class {
3286
3313
  const aliasName = props["aliasName"];
3287
3314
  if (!aliasName) throw new Error("Key context provider requires aliasName property");
3288
3315
  this.logger.debug(`Looking up KMS key by alias: ${aliasName} (region: ${region})`);
3289
- const client = new KMSClient({ ...region && { region } });
3316
+ const client = new KMSClient({
3317
+ ...awsClientDefaults(),
3318
+ ...region && { region }
3319
+ });
3290
3320
  try {
3291
3321
  const normalizedAlias = aliasName.startsWith("alias/") ? aliasName : `alias/${aliasName}`;
3292
3322
  let nextMarker;
@@ -4138,6 +4168,7 @@ async function uploadCfnTemplate(args) {
4138
4168
  ...s3ClientOpts?.credentials && { credentials: s3ClientOpts.credentials }
4139
4169
  });
4140
4170
  const s3 = new S3Client({
4171
+ ...awsClientDefaults({ profile: s3ClientOpts?.profile }),
4141
4172
  region,
4142
4173
  ...s3ClientOpts?.profile && { profile: s3ClientOpts.profile },
4143
4174
  ...s3ClientOpts?.credentials && { credentials: s3ClientOpts.credentials }
@@ -4535,7 +4566,10 @@ async function expandMacrosAttempt(template, opts, logger) {
4535
4566
  const serialized = JSON.stringify(template);
4536
4567
  const parameters = buildParameterValues(template, logger);
4537
4568
  ownsClient = opts.cfnClient === void 0;
4538
- cfn = opts.cfnClient ?? new CloudFormationClient({ region });
4569
+ cfn = opts.cfnClient ?? new CloudFormationClient({
4570
+ ...awsClientDefaults(),
4571
+ region
4572
+ });
4539
4573
  let templateInput;
4540
4574
  if (serialized.length > 1048576) throw new MacroExpansionError(`Template is ${serialized.length} bytes, which exceeds CloudFormation's ${CFN_TEMPLATE_URL_LIMIT}-byte TemplateURL ceiling for macro expansion. Shrink inline payloads (move inline lambda.Code.ZipFile to lambda.Code.fromAsset, etc.) or split the stack before retrying.`);
4541
4575
  if (serialized.length <= 51200) templateInput = { TemplateBody: serialized };
@@ -5143,7 +5177,10 @@ var Synthesizer = class {
5143
5177
  const region = explicitRegion || await resolveSdkDefaultRegion(options.profile);
5144
5178
  let accountId;
5145
5179
  try {
5146
- const stsClient = new STSClient({ ...region && { region } });
5180
+ const stsClient = new STSClient({
5181
+ ...awsClientDefaults(),
5182
+ ...region && { region }
5183
+ });
5147
5184
  accountId = (await stsClient.send(new GetCallerIdentityCommand({}))).Account;
5148
5185
  stsClient.destroy();
5149
5186
  } catch {
@@ -5235,7 +5272,10 @@ var Synthesizer = class {
5235
5272
  if (!region) throw new SynthesisError(`Stack(s) [${stacksWithMacros.map((s) => s.stackName).join(", ")}] use CloudFormation macros (Transform / Fn::Transform) but cdkd could not resolve an AWS region for the expansion round-trip. Set AWS_REGION, pass --region <r>, or set env: { region: '<r>' } in your CDK Stack constructor.`);
5236
5273
  let accountId = resolved?.accountId;
5237
5274
  if (resolved === void 0 && !options.stateBucket) try {
5238
- const stsClient = new STSClient({ region });
5275
+ const stsClient = new STSClient({
5276
+ ...awsClientDefaults(),
5277
+ region
5278
+ });
5239
5279
  accountId = (await stsClient.send(new GetCallerIdentityCommand({}))).Account;
5240
5280
  stsClient.destroy();
5241
5281
  } catch {
@@ -5281,7 +5321,10 @@ var Synthesizer = class {
5281
5321
  async function resolveSdkDefaultRegion(profile) {
5282
5322
  let client;
5283
5323
  try {
5284
- client = new STSClient({ ...profile && { profile } });
5324
+ client = new STSClient({
5325
+ ...awsClientDefaults({ profile }),
5326
+ ...profile && { profile }
5327
+ });
5285
5328
  return await client.config.region() || void 0;
5286
5329
  } catch {
5287
5330
  return;
@@ -5471,7 +5514,10 @@ var FileAssetPublisher = class {
5471
5514
  const objectKey = this.resolvePlaceholders(dest.objectKey, accountId, region);
5472
5515
  const destRegion = dest.region ? this.resolvePlaceholders(dest.region, accountId, region) : region;
5473
5516
  this.logger.debug(`Publishing file asset ${asset.displayName || assetHash} → s3://${bucketName}/${objectKey}`);
5474
- const client = new S3Client({ region: destRegion });
5517
+ const client = new S3Client({
5518
+ ...awsClientDefaults(),
5519
+ region: destRegion
5520
+ });
5475
5521
  try {
5476
5522
  if (await this.objectExists(client, bucketName, objectKey)) {
5477
5523
  this.logger.debug(`Asset already exists, skipping: s3://${bucketName}/${objectKey}`);
@@ -6095,7 +6141,10 @@ var DockerAssetPublisher = class {
6095
6141
  const destRegion = dest.region ? this.resolvePlaceholders(dest.region, accountId, region) : region;
6096
6142
  const ecrUri = `${accountId}.dkr.ecr.${destRegion}.${ecrUrlSuffix(destRegion)}/${repositoryName}:${imageTag}`;
6097
6143
  this.logger.debug(`Publishing Docker image ${asset.displayName || assetHash} → ${ecrUri}`);
6098
- const client = new ECRClient({ region: destRegion });
6144
+ const client = new ECRClient({
6145
+ ...awsClientDefaults(),
6146
+ region: destRegion
6147
+ });
6099
6148
  try {
6100
6149
  if (await this.imageExists(client, repositoryName, imageTag)) {
6101
6150
  this.logger.debug(`Image already exists, skipping: ${ecrUri}`);
@@ -6131,7 +6180,10 @@ var DockerAssetPublisher = class {
6131
6180
  const imageTag = this.resolvePlaceholders(dest.imageTag, accountId, region);
6132
6181
  const destRegion = dest.region ? this.resolvePlaceholders(dest.region, accountId, region) : region;
6133
6182
  const ecrUri = `${accountId}.dkr.ecr.${destRegion}.${ecrUrlSuffix(destRegion)}/${repositoryName}:${imageTag}`;
6134
- const client = new ECRClient({ region: destRegion });
6183
+ const client = new ECRClient({
6184
+ ...awsClientDefaults(),
6185
+ region: destRegion
6186
+ });
6135
6187
  try {
6136
6188
  if (await this.imageExists(client, repositoryName, imageTag)) {
6137
6189
  this.logger.debug(`Image already exists, skipping: ${ecrUri}`);
@@ -7427,6 +7479,7 @@ function createAssetRedirectResolver(opts) {
7427
7479
  accountIdPromise ??= (async () => {
7428
7480
  const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
7429
7481
  const stsClient = new STSClient({
7482
+ ...awsClientDefaults({ profile: opts.profile }),
7430
7483
  region: opts.stsRegion,
7431
7484
  ...opts.profile && { profile: opts.profile }
7432
7485
  });
@@ -7706,7 +7759,10 @@ var AssetPublisher = class {
7706
7759
  let accountId = options.accountId;
7707
7760
  if (!accountId) {
7708
7761
  const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
7709
- const stsClient = new STSClient({ region });
7762
+ const stsClient = new STSClient({
7763
+ ...awsClientDefaults(),
7764
+ region
7765
+ });
7710
7766
  accountId = (await stsClient.send(new GetCallerIdentityCommand({}))).Account;
7711
7767
  stsClient.destroy();
7712
7768
  }
@@ -9552,6 +9608,99 @@ var LockManager = class {
9552
9608
  /** Fixed marker substituted for a secret value in log / error output. */
9553
9609
  const SECRET_MASK = "***";
9554
9610
  /**
9611
+ * The UNCOLLAPSED companion of a {@link RecordedSecretValues} map: for each map
9612
+ * instance, every `expression -> plaintext` pair the resolver recorded INTO IT,
9613
+ * keyed by EXPRESSION (issue [#2485](https://github.com/go-to-k/cdkd/issues/2485)).
9614
+ *
9615
+ * WHY IT EXISTS. The map is keyed by PLAINTEXT, so two expressions resolving to
9616
+ * one value keep ONE entry — whichever the resolver recorded last. A WHOLE-token
9617
+ * leaf is immune (the position pass copies its own source), but a leaf that
9618
+ * EMBEDS a token in a literal string is redacted by the value scan, which can
9619
+ * only write the map's surviving expression: the versioned sibling's, for a
9620
+ * template that spells the un-versioned one, and the next deploy diffs that
9621
+ * leaf forever. Recovering the losing expression needs evidence the map has
9622
+ * discarded, and it has to be PASS-LOCAL: `recordedSecretExpressions` is
9623
+ * process-wide and says only that an expression IS secret, never what it
9624
+ * resolved to in THIS resource — so it cannot tell "the source token lost the
9625
+ * map slot to its sibling" from "the source token was never resolved here"
9626
+ * (a previous generation's bag, where writing today's expression over the
9627
+ * framed value would record something that was never deployed).
9628
+ *
9629
+ * Keyed by the map INSTANCE, so the evidence is exactly as pass-local as the
9630
+ * map itself: a map the resolver populated (the deploy's `perResourceSecrets`
9631
+ * entry, and equally the map drift / scrub / import hand their own resolution)
9632
+ * carries the pairs of THAT resolution, while a map the resolver did not
9633
+ * populate — a derived needle map, a nested-stack inheritance copy, a
9634
+ * `new Map(secrets)` copy — starts with no entries here and takes the
9635
+ * pre-#2485 fall-through, the safe direction. A copy loses the evidence
9636
+ * deliberately: a copy is not the pass that resolved anything.
9637
+ *
9638
+ * `CONFLICTING_PLAINTEXT` marks an expression this map saw resolve to TWO
9639
+ * values (a region-pinned re-resolution of one spelling, say); it then vouches
9640
+ * for nothing, which is the same "answer nothing you cannot prove" rule
9641
+ * {@link plaintextIndexOf} applies to the collapsed map's reverse index.
9642
+ */
9643
+ const resolvedPairsOf = /* @__PURE__ */ new WeakMap();
9644
+ /**
9645
+ * Record that `expression` resolved to `plaintext` in the pass that owns
9646
+ * `secrets` — the resolver's recording seam calls this beside its
9647
+ * `secrets.set(plaintext, expression)`, so the two never disagree about which
9648
+ * pass the evidence belongs to. Mask-only map entries (value `SECRET_MASK`)
9649
+ * never pass through that seam — they came from no `{{resolve:...}}` token —
9650
+ * so nothing here special-cases the mask string: a secret whose plaintext
9651
+ * happens to BE `***` is a secret like any other.
9652
+ */
9653
+ function recordResolvedPair(secrets, expression, plaintext) {
9654
+ let pairs = resolvedPairsOf.get(secrets);
9655
+ if (pairs === void 0) {
9656
+ pairs = /* @__PURE__ */ new Map();
9657
+ resolvedPairsOf.set(secrets, pairs);
9658
+ }
9659
+ const previous = pairs.get(expression);
9660
+ if (previous === void 0) pairs.set(expression, plaintext);
9661
+ else if (previous !== plaintext) pairs.set(expression, CONFLICTING_PLAINTEXT);
9662
+ }
9663
+ /**
9664
+ * Carry the resolved pairs of `from` into `to`, for the one copy of a
9665
+ * resolver-populated map that POSITIONS anything: the deploy engine accumulates
9666
+ * each stack's output resolution into its `outputSecrets` bag entry by entry,
9667
+ * and without this the copy would keep the collapsed entries while dropping the
9668
+ * evidence — so a literal `Output` embedding one of two same-plaintext
9669
+ * references would fall back to the value scan and persist the sibling's
9670
+ * expression. The engine's other entry-by-entry copy — an `Export.Name`'s
9671
+ * secrets into the pass map — deliberately does NOT call this: a name never
9672
+ * positions a leaf, a value re-using the same token records its own pair at
9673
+ * the seam, and the only thing the merge could add is a CONFLICT (a
9674
+ * non-cacheable `{{resolve:ssm:X}}` whose value moved between the value pass
9675
+ * and the name's resolution), which would destroy positioning the value pass
9676
+ * had earned. A pair that conflicts across the two maps is marked conflicting
9677
+ * in `to`, the same rule {@link recordResolvedPair} applies within one map.
9678
+ *
9679
+ * Deliberately NOT a general "copy the map" helper: every other new map is a
9680
+ * different PASS, and starting it without evidence is the safe direction.
9681
+ */
9682
+ function mergeResolvedPairs(from, to) {
9683
+ const pairs = resolvedPairsOf.get(from);
9684
+ if (pairs === void 0) return;
9685
+ for (const [expression, plaintext] of pairs) if (typeof plaintext === "string") recordResolvedPair(to, expression, plaintext);
9686
+ else {
9687
+ let target = resolvedPairsOf.get(to);
9688
+ if (target === void 0) {
9689
+ target = /* @__PURE__ */ new Map();
9690
+ resolvedPairsOf.set(to, target);
9691
+ }
9692
+ target.set(expression, CONFLICTING_PLAINTEXT);
9693
+ }
9694
+ }
9695
+ /**
9696
+ * The plaintext `expression` resolved to in the pass that owns `secrets`, or
9697
+ * `undefined` when that pass recorded nothing for it (or two different values).
9698
+ */
9699
+ function resolvedPlaintextOf(secrets, expression) {
9700
+ const recorded = resolvedPairsOf.get(secrets)?.get(expression);
9701
+ return typeof recorded === "string" ? recorded : void 0;
9702
+ }
9703
+ /**
9555
9704
  * Every `{{resolve:...}}` expression this process has PROVEN resolves to a
9556
9705
  * secret, as a SET — uncollapsed by resolved value (issue #1910).
9557
9706
  *
@@ -10562,8 +10711,9 @@ function isKnownSecretExpression(expression, secretExpressions) {
10562
10711
  return isSecretExpressionByVerdictOrSpelling(expression) || secretExpressions.has(expression);
10563
10712
  }
10564
10713
  /**
10565
- * The two arms of {@link isKnownSecretExpression} that need NO pass-local set:
10566
- * `secretsmanager` by SPELLING, and anything this process PROVED secret.
10714
+ * The arms of {@link isKnownSecretExpression} that need NO pass-local set:
10715
+ * `secretsmanager` / `ssm-secure` by SPELLING, and anything this process
10716
+ * PROVED secret.
10567
10717
  *
10568
10718
  * Split out so the resolver can ask the same question at the issue #2059
10569
10719
  * recording seam, where no `secretExpressions` set is in hand. It must not
@@ -10588,7 +10738,7 @@ function isKnownSecretExpression(expression, secretExpressions) {
10588
10738
  * change to a store this function only reads.
10589
10739
  */
10590
10740
  function isSecretExpressionByVerdictOrSpelling(expression) {
10591
- return expression.startsWith("{{resolve:secretsmanager:") || isRecordedSecretExpression(expression);
10741
+ return expression.startsWith("{{resolve:secretsmanager:") || expression.startsWith("{{resolve:ssm-secure:") || isRecordedSecretExpression(expression);
10592
10742
  }
10593
10743
  /**
10594
10744
  * The character class a `{{resolve:...}}` reference's INNER text is built from,
@@ -11132,6 +11282,124 @@ function positionByIntrinsicSkeleton(bag, source, secrets, secretExpressions) {
11132
11282
  return matched;
11133
11283
  }
11134
11284
  /**
11285
+ * The ONE-span frame shared by {@link positionByEmbeddedSpan} and
11286
+ * {@link learnMixedLeafNeedle}: a source holding exactly one `{{resolve:...}}`
11287
+ * token, and a bag that starts with the source's prefix and ends with its
11288
+ * suffix with something non-empty between them that is NOT itself a complete
11289
+ * token (an already-redacted record is a persisted answer, not a plaintext).
11290
+ * `undefined` for any other shape. One helper rather than two copies so the
11291
+ * two refusals cannot drift apart.
11292
+ */
11293
+ function singleSpanFrame(bag, source) {
11294
+ const spans = dynamicReferenceSpans(source);
11295
+ if (spans.length !== 1) return void 0;
11296
+ const [span] = spans;
11297
+ const token = source.slice(span.start, span.end);
11298
+ const prefix = source.slice(0, span.start);
11299
+ const suffix = source.slice(span.end);
11300
+ if (bag.length <= prefix.length + suffix.length) return void 0;
11301
+ if (!bag.startsWith(prefix) || !bag.endsWith(suffix)) return void 0;
11302
+ const middle = bag.slice(prefix.length, bag.length - suffix.length);
11303
+ if (isSingleDynamicReferenceToken(middle)) return void 0;
11304
+ return {
11305
+ token,
11306
+ prefix,
11307
+ suffix,
11308
+ middle
11309
+ };
11310
+ }
11311
+ /**
11312
+ * Position a literal source leaf that EMBEDS exactly one `{{resolve:...}}`
11313
+ * token — `postgres://app-svc:{{resolve:ssm-secure:NAME}}@db/app` — by the
11314
+ * span the source states, writing `prefix + token + suffix` (issue
11315
+ * [#2485](https://github.com/go-to-k/cdkd/issues/2485)).
11316
+ *
11317
+ * WHY THE VALUE SCAN IS NOT ENOUGH HERE. The scan writes the map's surviving
11318
+ * expression for a plaintext, and the map keeps one expression per plaintext:
11319
+ * a whole-value `NAME:1` sibling that resolved LAST leaves `NAME:1` as the only
11320
+ * expression for the value, so the embedded leaf persists the versioned
11321
+ * spelling for a template that spells `NAME`, and the deploy diff — expression
11322
+ * against expression — reports that leaf on every run. The whole-token arm of
11323
+ * {@link redactByPath} is immune because it copies its own source; this arm
11324
+ * gives the one-span literal leaf the same immunity.
11325
+ *
11326
+ * THE EVIDENCE, and why the shape of the frame is not enough on its own: the
11327
+ * frame check (`bag` starts with the source's prefix and ends with its suffix,
11328
+ * with something between) is what {@link learnMixedLeafNeedle} already uses to
11329
+ * LEARN a needle, and it proves only that the bag has the source's shape. The
11330
+ * bag can also be a PREVIOUS generation's (`cdkd scrub`, a state-sourced walk)
11331
+ * with an earlier plaintext framed exactly like this, and writing today's
11332
+ * token over it would record an expression that was never deployed at that
11333
+ * position — the hazard `sourceIsSameGeneration` exists for on the whole-token
11334
+ * arm. So the middle must EQUAL what THIS pass recorded the source token
11335
+ * resolving to ({@link recordResolvedPair}, per map instance): that is evidence
11336
+ * of this resolution, not of shape, and it is absent by construction for every
11337
+ * bag this pass did not produce. It is also what keeps a PUBLIC `ssm` token
11338
+ * resolved (issue #1901) — the resolver records only secret verdicts — and what
11339
+ * keeps a mask-only `NoEcho` value out (never recorded).
11340
+ *
11341
+ * WHAT THIS EVIDENCE DOES NOT CLAIM, stated because a reviewer asked: it does
11342
+ * not prove the bag was produced FROM this source. A previous generation's bag
11343
+ * whose framed middle happens to EQUAL a plaintext this pass resolved the
11344
+ * source token to (`cdkd scrub` walking an old record against today's template,
11345
+ * or a failed deploy persisting an old bag) takes this arm and persists TODAY's
11346
+ * expression at that position. That is not a new claim: the value scan the
11347
+ * arm replaces rewrites that same plaintext onto one of THIS pass's expressions
11348
+ * regardless of generation — the map holds no other — so the class of answer
11349
+ * is unchanged and only the choice within it improves (the source's own
11350
+ * token rather than the map's survivor). The generation hazard this arm must
11351
+ * not create is the whole-token arm's: a middle that is ALREADY an expression
11352
+ * (a persisted answer from another generation), which the token refusal below
11353
+ * keeps out — and, by the same argument, any leaf the value scan would NOT
11354
+ * rewrite to exactly `prefix + survivor + suffix`: a middle shorter than the
11355
+ * scan's needle floor (an embedded 1-3 character secret stays the scan's
11356
+ * documented residual — issue #2516 tracks closing it with a bound that
11357
+ * proves the bag's generation, which this evidence does not), a whole leaf
11358
+ * that is itself another recorded plaintext, a needle starting in the prefix
11359
+ * and overlapping the middle. The
11360
+ * arm checks that equivalence against the scan's own answer rather than
11361
+ * re-deriving the scan's rules. Pinned by the cross-generation cases in
11362
+ * `secret-redaction-embedded-span.test.ts`.
11363
+ *
11364
+ * One shape reaches this arm that a reader may not expect: a WHOLE-token
11365
+ * source that FAILED the whole-token arm's `isKnownSecretExpression` gate (an
11366
+ * `ssm` token whose type came back unclassifiable and which lost the map slot
11367
+ * to a sibling). Its "frame" is empty, and if this pass recorded it resolving
11368
+ * to the bag it is written back as itself — an expression, and the leaf's own,
11369
+ * where the scan wrote the survivor. Stated so it is not mistaken for a leak.
11370
+ *
11371
+ * Everything else keeps the pre-#2485 fall-through: two or more spans (which
11372
+ * span produced which value is genuinely ambiguous when they share one), an
11373
+ * `Fn::Sub` / `Fn::Join` source (an object, not this arm at all — issue #2320's
11374
+ * placeholder primitive), a frame mismatch, a middle that is itself a complete
11375
+ * token (an already-redacted record, per the same refusal
11376
+ * {@link learnMixedLeafNeedle} makes), and a middle this pass cannot vouch for.
11377
+ *
11378
+ * The frame is copied from the SOURCE, not scanned. A needle occurring in the
11379
+ * literal frame would be a reference the template never had at that offset —
11380
+ * the fabricated-baseline direction {@link preferPositionDecisions} refuses —
11381
+ * and the whole-token arm returns its source unscanned for the same reason.
11382
+ *
11383
+ * RETURNS THE VALUE SCAN'S ANSWER ON EVERY REFUSAL, not `undefined`: the scan
11384
+ * is computed once, here, for `(bag, secrets)` — the arm's bound below compares
11385
+ * against it, and every fall-through IS it — so the compared value provably
11386
+ * comes from the same bag and map the arm positions. An earlier revision took
11387
+ * the scan as a parameter, which left the bound one wrong caller away from
11388
+ * comparing against a scan of some other bag with no type error.
11389
+ */
11390
+ function positionByEmbeddedSpan(bag, source, secrets) {
11391
+ const scanned = redactSecretsForState(bag, secrets);
11392
+ const frame = singleSpanFrame(bag, source);
11393
+ if (frame === void 0) return scanned;
11394
+ const { token, prefix, suffix, middle } = frame;
11395
+ const recorded = resolvedPlaintextOf(secrets, token);
11396
+ if (recorded === void 0 || recorded !== middle) return scanned;
11397
+ const survivor = secrets.get(middle);
11398
+ if (survivor === void 0) return scanned;
11399
+ if (scanned !== prefix + survivor + suffix) return scanned;
11400
+ return prefix + token + suffix;
11401
+ }
11402
+ /**
11135
11403
  * Keys tried, in order, when pairing two arrays whose ORDER cannot be trusted
11136
11404
  * (issue #1915).
11137
11405
  *
@@ -11331,7 +11599,7 @@ function redactByPath(bag, source, secrets, rules, secretExpressions) {
11331
11599
  if (!rules.sourceIsSameGeneration && isSingleDynamicReferenceToken(bag)) return secrets.get(bag) ?? bag;
11332
11600
  return source;
11333
11601
  }
11334
- return redactSecretsForState(bag, secrets);
11602
+ return positionByEmbeddedSpan(bag, source, secrets);
11335
11603
  }
11336
11604
  if (typeof bag === "string" && isPlainObject$2(source)) {
11337
11605
  const certified = positionByCrossStackSource(bag, source, secrets);
@@ -11449,12 +11717,13 @@ function subtreeHasDynamicReference(value) {
11449
11717
  * Widening it changes one answer, in the SAFE direction for BOTH readers.
11450
11718
  *
11451
11719
  * `drift.ts`'s `survivingDynamicReferences` is the reader that is easy to
11452
- * forget, because it lives in another file — it feeds `isSecretBySpelling`,
11453
- * so seeing MORE tokens can only mask more, never less. Do not shorten this
11454
- * to "the only reader": that sentence is what a later editor uses to bound
11455
- * the blast radius of touching the class, and getting it wrong points them
11456
- * away from the report / `--json` / `--accept` path where an unmasked
11457
- * `ssm-secure` survivor would surface.
11720
+ * forget, because it lives in another file — it feeds the survivor REPORT
11721
+ * (`onUnresolved`, and through it the `unresolvedToken` cause), so seeing MORE
11722
+ * tokens can only report more, never less. Do not shorten this to "the only
11723
+ * reader": that sentence is what a later editor uses to bound the blast
11724
+ * radius of touching the class, and getting it wrong points them away from
11725
+ * the report / `--json` / `--accept` path where an unreported survivor would
11726
+ * surface.
11458
11727
  *
11459
11728
  * The other reader is the DECLARED direction for issue #1901:
11460
11729
  * {@link mixedLeafMayCarryPublicReference}, which asks whether a MIXED leaf
@@ -11860,9 +12129,9 @@ function unkeyedArrayPairsByAnchors(bag, source) {
11860
12129
  * from one the pass decided IN FAVOUR of the value already there. Two shapes
11861
12130
  * hit it, both fabricating a baseline `cdkd drift --revert` then pushes:
11862
12131
  *
11863
- * - the resolver's unsupported-service arm leaves an `{{resolve:ssm-secure:`
11864
- * token LITERAL, so AWS echoes it back and the source leaf EQUALS the bag
11865
- * leaf. The string arm returns `source` — a decision — and the equality made
12132
+ * - the resolver's unsupported-service arm leaves a `{{resolve:...}}` token it
12133
+ * has no arm for LITERAL (`ssm-secure:` was one until issue #2482), so AWS
12134
+ * echoes it back and the source leaf EQUALS the bag leaf. The string arm returns `source` — a decision — and the equality made
11866
12135
  * it look like no decision at all. (A BARE such token takes the whole-token
11867
12136
  * arm and one embedded in text takes the mixed-leaf arm; both decide, and
11868
12137
  * both were misread.)
@@ -12046,7 +12315,8 @@ function learnWholeTokenNeedle(collector, bag, source) {
12046
12315
  * would then contain a whole `{{resolve:...}}` token and a resolved readback
12047
12316
  * cannot end with one. The shape it genuinely decides is a second reference
12048
12317
  * that survives LITERALLY in the readback — the resolver's
12049
- * unsupported-service arm (`ssm-secure:`) produces exactly that — where the
12318
+ * unsupported-service arm produces exactly that (`ssm-secure:` did until
12319
+ * issue #2482; a spelling with no arm still does) — where the
12050
12320
  * extraction would in fact be right and is declined anyway. Measured: a
12051
12321
  * both-resolved fixture leaves this line unfenced.
12052
12322
  * - the source's literal PREFIX and SUFFIX must both be present at the ends of
@@ -12060,17 +12330,10 @@ function learnWholeTokenNeedle(collector, bag, source) {
12060
12330
  * correctly, while an `indexOf` scan would cut it short.
12061
12331
  */
12062
12332
  function learnMixedLeafNeedle(collector, bag, source) {
12063
- const spans = dynamicReferenceSpans(source);
12064
- if (spans.length !== 1) return;
12065
- const [span] = spans;
12066
- const token = source.slice(span.start, span.end);
12333
+ const frame = singleSpanFrame(bag, source);
12334
+ if (frame === void 0) return;
12335
+ const { token, middle: plaintext } = frame;
12067
12336
  if (!expressionMaySeedANeedle(token)) return;
12068
- const prefix = source.slice(0, span.start);
12069
- const suffix = source.slice(span.end);
12070
- if (bag.length <= prefix.length + suffix.length) return;
12071
- if (!bag.startsWith(prefix) || !bag.endsWith(suffix)) return;
12072
- const plaintext = bag.slice(prefix.length, bag.length - suffix.length);
12073
- if (isSingleDynamicReferenceToken(plaintext)) return;
12074
12337
  learnNeedle(collector, plaintext, token);
12075
12338
  }
12076
12339
  /**
@@ -15100,7 +15363,7 @@ async function assumeRoleForCrossAccountStateRead(roleArn) {
15100
15363
  const promise = (async () => {
15101
15364
  const logger = getLogger().child("role-arn");
15102
15365
  logger.debug(`Assuming role for cross-account state read: ${roleArn}`);
15103
- const sts = new STSClient({});
15366
+ const sts = new STSClient({ ...awsClientDefaults() });
15104
15367
  try {
15105
15368
  let response;
15106
15369
  try {
@@ -15169,7 +15432,10 @@ async function applyRoleArnIfSet(opts) {
15169
15432
  if (!roleArn) return;
15170
15433
  const logger = getLogger().child("role-arn");
15171
15434
  logger.debug(`Assuming role ${roleArn}...`);
15172
- const sts = new STSClient({ ...opts.region && { region: opts.region } });
15435
+ const sts = new STSClient({
15436
+ ...awsClientDefaults(),
15437
+ ...opts.region && { region: opts.region }
15438
+ });
15173
15439
  try {
15174
15440
  const response = await sts.send(new AssumeRoleCommand({
15175
15441
  RoleArn: roleArn,
@@ -15323,17 +15589,22 @@ function s3BucketWebsiteUrl(bucketName, region) {
15323
15589
  /**
15324
15590
  * The `{{resolve:<service>:...}}` families whose value can be a SECRET, and
15325
15591
  * therefore the only ones the region question below is asked about: every
15326
- * `secretsmanager` reference by spelling, and every `ssm` one, which is secret
15327
- * exactly when its parameter is a `SecureString` (issue #1901).
15328
- *
15329
- * Every OTHER service is `local` because cdkd cannot resolve it at all, NOT
15330
- * because it is public. `ssm-secure` is the live example and is emphatically
15331
- * not public: `resolveDynamicReferences` has no arm for it, so the literal
15332
- * token is passed through to AWS and CloudFormation resolves it SERVER-side.
15333
- * cdkd never holds its value, so there is no region for cdkd to get wrong
15334
- * which is the only reason it can be waved through here.
15335
- */
15336
- const REPLAY_SECRET_SERVICES = /* @__PURE__ */ new Set(["secretsmanager", "ssm"]);
15592
+ * `secretsmanager` reference by spelling, every `ssm-secure` one by spelling
15593
+ * (issue #2482 it is resolved through the same `GetParameter` as `ssm`, so
15594
+ * the wrong region answers it in exactly the same way), and every `ssm` one,
15595
+ * which is secret exactly when its parameter is a `SecureString` (issue #1901).
15596
+ *
15597
+ * Every OTHER service is `local` because cdkd cannot resolve it at all — the
15598
+ * resolver's unsupported-service arm leaves such a token in place, so there
15599
+ * is no lookup for a region to get wrong. None of CloudFormation's three
15600
+ * services is in that position any more; the arm exists for a spelling that
15601
+ * is not a dynamic reference at all, or one AWS adds later.
15602
+ */
15603
+ const REPLAY_SECRET_SERVICES = /* @__PURE__ */ new Set([
15604
+ "secretsmanager",
15605
+ "ssm",
15606
+ "ssm-secure"
15607
+ ]);
15337
15608
  /**
15338
15609
  * Split a `{{resolve:secretsmanager:...}}` inner body into its SECRET_ID.
15339
15610
  *
@@ -15369,7 +15640,7 @@ function secretsManagerSecretId(inner) {
15369
15640
  * a different thing in the refusal message than the one that would be read.
15370
15641
  */
15371
15642
  function ssmParameterName(inner) {
15372
- return inner.substring(4);
15643
+ return inner.substring(inner.indexOf(":") + 1);
15373
15644
  }
15374
15645
  /**
15375
15646
  * The region an ARN names, or `undefined` for anything that is not an ARN with
@@ -16470,7 +16741,10 @@ var WAFv2WebACLProvider = class {
16470
16741
  "AssociationConfig"
16471
16742
  ])]]);
16472
16743
  getClient() {
16473
- if (!this.wafv2Client) this.wafv2Client = new WAFV2Client(this.providerRegion ? { region: this.providerRegion } : {});
16744
+ if (!this.wafv2Client) this.wafv2Client = new WAFV2Client({
16745
+ ...awsClientDefaults(),
16746
+ ...this.providerRegion ? { region: this.providerRegion } : {}
16747
+ });
16474
16748
  return this.wafv2Client;
16475
16749
  }
16476
16750
  /**
@@ -18240,6 +18514,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
18240
18514
  const building = (async () => {
18241
18515
  const { ServiceDiscoveryClient } = await import("@aws-sdk/client-servicediscovery");
18242
18516
  return new ServiceDiscoveryClient({
18517
+ ...awsClientDefaults({ profile: scoped.credentialConfig?.profile }),
18243
18518
  ...scoped.credentialConfig ?? {},
18244
18519
  ...region ? { region } : {}
18245
18520
  });
@@ -20047,7 +20322,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
20047
20322
  getCfnClient(region) {
20048
20323
  let client = this.cfnClients[region];
20049
20324
  if (!client) {
20050
- client = new CloudFormationClient({ region });
20325
+ client = new CloudFormationClient({
20326
+ ...awsClientDefaults(),
20327
+ region
20328
+ });
20051
20329
  this.cfnClients[region] = client;
20052
20330
  }
20053
20331
  return client;
@@ -20231,6 +20509,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
20231
20509
  const { bucket, region: bucketRegion } = await resolveCrossAccountStateBucket(parsed.accountId, credentials);
20232
20510
  const prefix = context.stateBackend?.prefix ?? "cdkd";
20233
20511
  return new S3StateBackend(new S3Client({
20512
+ ...awsClientDefaults(),
20234
20513
  region: bucketRegion,
20235
20514
  credentials: {
20236
20515
  accessKeyId: credentials.accessKeyId,
@@ -20421,7 +20700,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
20421
20700
  });
20422
20701
  for (const { fullMatch, inner } of matches) {
20423
20702
  const service = inner.split(":")[0];
20424
- const isKnownSecret = service === "secretsmanager" || recordedSecretExpressions.has(fullMatch);
20703
+ const isKnownSecret = service === "secretsmanager" || service === "ssm-secure" || recordedSecretExpressions.has(fullMatch);
20425
20704
  if (isKnownSecret && context?.skipDynamicReferences) continue;
20426
20705
  const regionVerdict = classifyReplaySecretRegion(fullMatch, this.explicitRegion ?? this.resolverRegion, context?.producerRegions);
20427
20706
  if (regionVerdict.kind === "ambiguous") throw markNonRetryable(new DynamicReferenceRegionAmbiguousError(`Refusing to resolve the secret reference ${fullMatch}: it names '${regionVerdict.secretName}' without a region, and this stack reads from ${regionVerdict.foreignProducerRegions.join(", ")} as well as its own region. cdkd cannot tell which one must answer, and resolving against the wrong one yields a different secret. Spell the reference as a full ARN to say which region owns it.`));
@@ -20432,7 +20711,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
20432
20711
  }
20433
20712
  const cached = this.cachedDynamicReferences.get(fullMatch);
20434
20713
  if (cached) {
20435
- if (cached.secret && cached.value) context?.recordedSecretValues?.set(cached.value, fullMatch);
20714
+ if (cached.secret && cached.value) {
20715
+ context?.recordedSecretValues?.set(cached.value, fullMatch);
20716
+ if (context?.recordedSecretValues) recordResolvedPair(context.recordedSecretValues, fullMatch, cached.value);
20717
+ }
20436
20718
  result = result.replace(fullMatch, () => cached.value);
20437
20719
  continue;
20438
20720
  }
@@ -20464,6 +20746,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
20464
20746
  if (!decrypt) continue;
20465
20747
  }
20466
20748
  resolved = param.value;
20749
+ } else if (service === "ssm-secure") {
20750
+ const param = await this.resolveSSMReference(parts, true, "ssm-secure");
20751
+ if (!param.secure) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to resolve ${fullMatch}: the parameter is a ${param.type} parameter, and the ssm-secure spelling is defined for SecureString parameters only. Reference it as {{resolve:ssm:...}} if it is public configuration.`));
20752
+ isSecret = true;
20753
+ resolved = param.value;
20467
20754
  } else {
20468
20755
  this.logger.warn(`Unsupported dynamic reference service: ${service}`);
20469
20756
  continue;
@@ -20474,7 +20761,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
20474
20761
  });
20475
20762
  if (isSecret && resolved) {
20476
20763
  context?.recordedSecretValues?.set(resolved, fullMatch);
20477
- if (service === "secretsmanager") this.pinSecretVerdict(fullMatch, true);
20764
+ if (context?.recordedSecretValues) recordResolvedPair(context.recordedSecretValues, fullMatch, resolved);
20765
+ if (service === "secretsmanager" || service === "ssm-secure") this.pinSecretVerdict(fullMatch, true);
20478
20766
  }
20479
20767
  result = result.replace(fullMatch, () => resolved);
20480
20768
  }
@@ -20684,16 +20972,16 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
20684
20972
  * discard the value when `secure` is set — it is ciphertext, not the resolved
20685
20973
  * reference.
20686
20974
  */
20687
- async resolveSSMReference(parts, decrypt = true) {
20975
+ async resolveSSMReference(parts, decrypt = true, service = "ssm") {
20688
20976
  const parameterName = parts.slice(1).join(":");
20689
- if (!parameterName) throw new Error("Dynamic reference: ssm PARAMETER_NAME is required");
20690
- this.logger.debug(`Resolving dynamic reference: ssm:${parameterName}`);
20977
+ if (!parameterName) throw new Error(`Dynamic reference: ${service} PARAMETER_NAME is required`);
20978
+ this.logger.debug(`Resolving dynamic reference: ${service}:${parameterName}`);
20691
20979
  const client = this.clientsForRegion(this.explicitRegion).ssm;
20692
20980
  const command = new GetParameterCommand({
20693
20981
  Name: parameterName,
20694
20982
  WithDecryption: decrypt
20695
20983
  });
20696
- const response = await this.sendWithThrottleRetry(() => client.send(command), `ssm:${parameterName}`);
20984
+ const response = await this.sendWithThrottleRetry(() => client.send(command), `${service}:${parameterName}`);
20697
20985
  const paramValue = response.Parameter?.Value;
20698
20986
  if (paramValue === void 0 || paramValue === null) throw new Error(`Dynamic reference: SSM parameter '${parameterName}' not found or has no value`);
20699
20987
  const paramType = response.Parameter?.Type;
@@ -20701,7 +20989,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
20701
20989
  if (secure && paramType !== "SecureString" && !this.warnedUnrecognizedSsmTypes.has(`${parameterName}\u0000${String(paramType)}`)) {
20702
20990
  this.warnedUnrecognizedSsmTypes.add(`${parameterName}\u0000${String(paramType)}`);
20703
20991
  const reported = paramType === void 0 ? "(absent)" : `'${String(paramType)}'`;
20704
- this.logger.warn(`SSM parameter '${parameterName}' reported an unrecognized Type ${reported} — treating its value as a secret, so cdkd will persist the {{resolve:ssm:...}} expression rather than the resolved value. Declare the parameter as String / StringList if it is public config.`);
20992
+ this.logger.warn(`SSM parameter '${parameterName}' reported an unrecognized Type ${reported} — treating its value as a secret, so cdkd will persist the {{resolve:${service}:...}} expression rather than the resolved value. Declare the parameter as String / StringList if it is public config.`);
20705
20993
  }
20706
20994
  return {
20707
20995
  value: paramValue,
@@ -21836,7 +22124,7 @@ var CloudControlProvider = class {
21836
22124
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
21837
22125
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
21838
22126
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
21839
- const { ASGProvider } = await import("./asg-provider-CXkE2U3z.js").then((n) => n.n);
22127
+ const { ASGProvider } = await import("./asg-provider-BvIH9Ivw.js").then((n) => n.n);
21840
22128
  return withIndeterminateGuard(await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
21841
22129
  }
21842
22130
  const isProtectedEc2Instance = context?.removeProtection === true && resourceType === "AWS::EC2::Instance";
@@ -22209,7 +22497,7 @@ var CloudControlProvider = class {
22209
22497
  break;
22210
22498
  case "AWS::RDS::DBCluster":
22211
22499
  try {
22212
- const cluster = (await new RDSClient({}).send(new DescribeDBClustersCommand({ DBClusterIdentifier: physicalId }))).DBClusters?.[0];
22500
+ const cluster = (await new RDSClient({ ...awsClientDefaults() }).send(new DescribeDBClustersCommand({ DBClusterIdentifier: physicalId }))).DBClusters?.[0];
22213
22501
  if (cluster) {
22214
22502
  if (cluster.Endpoint) enriched["Endpoint.Address"] = cluster.Endpoint;
22215
22503
  if (cluster.Port !== void 0) enriched["Endpoint.Port"] = String(cluster.Port);
@@ -22224,7 +22512,7 @@ var CloudControlProvider = class {
22224
22512
  break;
22225
22513
  case "AWS::RDS::DBInstance":
22226
22514
  try {
22227
- const inst = (await new RDSClient({}).send(new DescribeDBInstancesCommand({ DBInstanceIdentifier: physicalId }))).DBInstances?.[0];
22515
+ const inst = (await new RDSClient({ ...awsClientDefaults() }).send(new DescribeDBInstancesCommand({ DBInstanceIdentifier: physicalId }))).DBInstances?.[0];
22228
22516
  if (inst) {
22229
22517
  if (inst.Endpoint?.Address) enriched["Endpoint.Address"] = inst.Endpoint.Address;
22230
22518
  if (inst.Endpoint?.Port !== void 0) enriched["Endpoint.Port"] = String(inst.Endpoint.Port);
@@ -22378,7 +22666,7 @@ var CloudControlProvider = class {
22378
22666
  break;
22379
22667
  case "AWS::ElastiCache::ReplicationGroup":
22380
22668
  try {
22381
- const rg = (await new ElastiCacheClient({}).send(new DescribeReplicationGroupsCommand({ ReplicationGroupId: physicalId }))).ReplicationGroups?.[0];
22669
+ const rg = (await new ElastiCacheClient({ ...awsClientDefaults() }).send(new DescribeReplicationGroupsCommand({ ReplicationGroupId: physicalId }))).ReplicationGroups?.[0];
22382
22670
  if (rg) {
22383
22671
  const primaryNode = rg.NodeGroups?.[0];
22384
22672
  if (primaryNode?.PrimaryEndpoint?.Address) enriched["PrimaryEndPoint.Address"] = primaryNode.PrimaryEndpoint.Address;
@@ -22400,7 +22688,7 @@ var CloudControlProvider = class {
22400
22688
  break;
22401
22689
  case "AWS::Redshift::Cluster":
22402
22690
  try {
22403
- const cluster = (await new RedshiftClient({}).send(new DescribeClustersCommand({ ClusterIdentifier: physicalId }))).Clusters?.[0];
22691
+ const cluster = (await new RedshiftClient({ ...awsClientDefaults() }).send(new DescribeClustersCommand({ ClusterIdentifier: physicalId }))).Clusters?.[0];
22404
22692
  if (cluster?.Endpoint) {
22405
22693
  if (cluster.Endpoint.Address) enriched["Endpoint.Address"] = cluster.Endpoint.Address;
22406
22694
  if (cluster.Endpoint.Port !== void 0) enriched["Endpoint.Port"] = String(cluster.Endpoint.Port);
@@ -22412,7 +22700,7 @@ var CloudControlProvider = class {
22412
22700
  break;
22413
22701
  case "AWS::OpenSearchService::Domain":
22414
22702
  try {
22415
- const domain = (await new OpenSearchClient({}).send(new DescribeDomainCommand({ DomainName: physicalId }))).DomainStatus;
22703
+ const domain = (await new OpenSearchClient({ ...awsClientDefaults() }).send(new DescribeDomainCommand({ DomainName: physicalId }))).DomainStatus;
22416
22704
  if (domain) {
22417
22705
  const endpoint = domain.Endpoint ?? domain.Endpoints?.["vpc"];
22418
22706
  if (endpoint) enriched["DomainEndpoint"] = endpoint;
@@ -33940,7 +34228,10 @@ var DeployEngine = class {
33940
34228
  }
33941
34229
  outputsPassCompleted = true;
33942
34230
  } finally {
33943
- if (context.recordedSecretValues) for (const [value, expr] of context.recordedSecretValues) this.outputSecrets.set(value, expr);
34231
+ if (context.recordedSecretValues) {
34232
+ for (const [value, expr] of context.recordedSecretValues) this.outputSecrets.set(value, expr);
34233
+ mergeResolvedPairs(context.recordedSecretValues, this.outputSecrets);
34234
+ }
33944
34235
  if (!outputsPassCompleted) this.outputsSourceUsable = false;
33945
34236
  }
33946
34237
  for (const [outputKey, output] of Object.entries(template.Outputs)) {
@@ -33961,5 +34252,5 @@ var DeployEngine = class {
33961
34252
  };
33962
34253
 
33963
34254
  //#endregion
33964
- export { DEFAULT_STATE_PREFIX as $, resolveUseCdkBootstrapAssets as $n, __exportAll as $r, isSingleDynamicReferenceToken as $t, bold as A, validateAssetBucketName as An, LocalStartServiceError as Ar, requireConfigObject as At, secretBearingStateKeyWarning as B, runDockerStreaming as Bn, StateError as Br, DiffCalculator as Bt, unsupportedFinalSnapshotError as C, BOOTSTRAP_MARKER_PREFIX as Cn, ConfigError as Cr, assertRegionMatch as Ct, isStatefulRecreateTargetSync as D, isCrossRegionRedirect as Dn, DynamicReferenceRegionAmbiguousError as Dr, readConfigString as Dt, MULTI_REGION_RECREATE_BLOCKED_TYPES as E, getBootstrapMarkerKey as En, DeployCancelledError as Er, configStringRefusal as Et, yellow as F, dockerSpawnEnvWithSensitive as Fn, ProvisioningError as Fr, s3BucketDomainName as Ft, clearOnUpdateRemoval as G, getDefaultStateBucketName as Gn, withErrorHandling as Gr, TemplateParser as Gt, getCurrentResourceSecrets as H, getDockerImageBySourceHash as Hn, formatError as Hr, describeTypeWithThrottleRetry as Ht, collectDeclaredOutputNames as I, formatDockerLoginError as In, ResourceTimeoutError as Ir, s3BucketDualStackDomainName as It, findSilentDropProperties as J, resolveAutoAssetStorage as Jn, isThrottlingError as Jr, TEMPLATE_SOURCED_RULES as Jt, ProviderRegistry as K, getLegacyStateBucketName as Kn, isMarkedNonRetryable as Kr, STATE_SOURCED_CROSS_GENERATION_RULES as Kt, collectPublishedOutputNames as L, getDockerCmd as Ln, ResourceUpdateNotSupportedError as Lr, s3BucketRegionalDomainName as Lt, gray as M, buildDenyExternalAccessPolicy as Mn, MissingCdkCliError as Mr, classifyReplaySecretRegion as Mt, green as N, describeAwsFailure as Nn, NestedStackChildDirectDestroyError as Nr, producerRegionsFromState as Nt, renderStatefulReason as O, parseBootstrapMarker as On, LocalInvokeBuildError as Or, replayWarn as Ot, red as P, buildDockerImage as Pn, PartialFailureError as Pr, s3BucketArn as Pt, CUSTOM_RESOURCE_RESPONSE_PREFIX as Q, resolveStateBucketWithDefaultAndSource as Qn, retryClassificationText as Qr, errorCauseChain as Qt, exportAliasCollisionScrubWarning as R, partitionSensitiveEnv as Rn, StackHasActiveImportsError as Rr, s3BucketWebsiteUrl as Rt, refusesFinalSnapshot as S, AssetModeResolver as Sn, CdkdError as Sr, resolveExplicitPhysicalId as St, extractDeploymentEventError as T, ensureAssetStorage as Tn, DependencyError as Tr, configBooleanRefusal as Tt, IAMRoleProvider as U, Synthesizer as Un, isCdkdError as Ur, withRetry as Ut, stateKeySecretExposure as V, AssetManifestLoader as Vn, SynthesisError as Vr, INTRINSIC_KEYS as Vt, collectInlinePolicyNamesManagedBySiblings as W, synthesisStatusMessage as Wn, normalizeAwsError as Wr, DagBuilder as Wt, maskDeep as X, resolveSkipPrefix as Xn, markNonRetryable as Xr, createSecretMasker as Xt, createMaskedRetryLogger as Y, resolveCaptureObservedState as Yn, isTransientServerError as Yr, carriesSecretMask as Yt, maskerOrIdentity as Z, resolveStateBucketWithDefault as Zn, markRedactedCause as Zr, dynamicReferenceTokens as Zt, PRE_DELETE_SNAPSHOT_TYPES as _, createAssetRedirectResolver as _n, AwsClients as _r, isUnboundTemplateParameter as _t, DeploymentEventsStore as a, scrubResourceRecord as an, findLargeInlineResources as ar, CloudControlProvider as at, createPreDeleteFinalSnapshot as b, escapeRegExp$1 as bn, setAwsClients as br, WAFv2WebACLProvider as bt, replayFailedOperations as c, rebuildClientForBucketRegion as cn, displaySafe as cr, deleteIndeterminateGuards as ct, updatePartialReason as d, importableOutputs as dn, canonicalizeRegion as dr, isTerminationProtectionPropagationError as dt, maskSecretsInError as en, stateBucketExistenceConfirmed as er, beginCommandInterruptScope as et, withResourceDeadline as f, shouldRetainResource as fn, derivePartitionAndUrlSuffix as fr, IntrinsicFunctionResolver as ft, ATOMIC_FINAL_SNAPSHOT_TYPES as g, buildAssetRedirectMap as gn, resolveBucketRegion as gr, getAccountInfo as gt, computeImplicitDeleteEdges as h, WorkGraph as hn, clearBucketRegionCache as hr, coerceParameterTypedValue as ht, DeploymentEventsReader as i, redactSecretsForState as in, MIGRATE_TMP_PREFIX as ir, startInterruptWatch as it, cyan as j, validateContainerRepoName as jn, LockError as jr, requireConfigString as jt, formatResourceLine as k, readBootstrapMarkerBody as kn, LocalMigrateError as kr, requireConfigArray as kt, replayRollback as l, exportNamesCarriedFrom as ln, expectedOwnerParam as lr, deleteSkipReason as lt, IMPLICIT_DELETE_DEPENDENCIES as m, stringifyValue as mn, processStackMessages as mr, cfnRefValueFromPhysicalId as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, recordMaskOnlyValue as nn, CFN_TEMPLATE_BODY_LIMIT as nr, interruptWatchListenerCount as nt, planFailedOps as o, LockManager as on, uploadCfnTemplate as or, slowCcOperationTimeoutMs as ot, maskingRetryLogger as p, AssetPublisher as pn, AssemblyReader as pr, carriesDynamicReference as pt, findActionableSilentDrops as q, resolveApp as qn, isRetryableTransientError as qr, STATE_SOURCED_READBACK_RULES as qt, DeployEngine as r, recoverMaskedOutput as rn, CFN_TEMPLATE_URL_LIMIT as rr, isInterruptedWaitError as rt, planRollback as s, S3StateBackend as sn, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as sr, UNSPECIFIED_SKIP_REASON as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, maskSecretsInText as tn, warnDeprecatedNoPrefixCliFlag as tr, endCommandInterruptScope as tt, updatePartialMessage as u, importableOutputKeys as un, PARTITION_TABLE as ur, disableInstanceApiTermination as ut, buildFinalSnapshotIdentifier as v, loadPublishableAssetManifest as vn, getAwsClients as vr, parameterTypeMayLoseSecretIdentity as vt, makeCanonicalizePropertiesFn as w, assertAssetBucketRegion as wn, CrossAccountSecretRefusalError as wr, coerceCfnBoolean as wt, isFinalSnapshotError as x, stripControlChars as xn, AssetError as xr, normalizeAwsTagsToCfn as xt, ccRoutedFinalSnapshotError as y, rewriteTemplateAssetReferences as yn, resetAwsClients as yr, refStateLookupFromResource as yt, isExportAliasCollision as z, runDockerForeground as zn, StackTerminationProtectionError as zr, applyRoleArnIfSet as zt };
33965
- //# sourceMappingURL=deploy-engine-C3epVkTj.js.map
34255
+ export { DEFAULT_STATE_PREFIX as $, resolveUseCdkBootstrapAssets as $n, retryClassificationText as $r, isSingleDynamicReferenceToken as $t, bold as A, validateAssetBucketName as An, LocalMigrateError as Ar, requireConfigObject as At, secretBearingStateKeyWarning as B, runDockerStreaming as Bn, StackTerminationProtectionError as Br, DiffCalculator as Bt, unsupportedFinalSnapshotError as C, BOOTSTRAP_MARKER_PREFIX as Cn, ConfigError as Cr, assertRegionMatch as Ct, isStatefulRecreateTargetSync as D, isCrossRegionRedirect as Dn, DynamicReferenceRegionAmbiguousError as Dr, readConfigString as Dt, MULTI_REGION_RECREATE_BLOCKED_TYPES as E, getBootstrapMarkerKey as En, DeployCancelledError as Er, configStringRefusal as Et, yellow as F, dockerSpawnEnvWithSensitive as Fn, PartialFailureError as Fr, s3BucketDomainName as Ft, clearOnUpdateRemoval as G, getDefaultStateBucketName as Gn, normalizeAwsError as Gr, TemplateParser as Gt, getCurrentResourceSecrets as H, getDockerImageBySourceHash as Hn, SynthesisError as Hr, describeTypeWithThrottleRetry as Ht, collectDeclaredOutputNames as I, formatDockerLoginError as In, ProvisioningError as Ir, s3BucketDualStackDomainName as It, findSilentDropProperties as J, resolveAutoAssetStorage as Jn, isRetryableTransientError as Jr, TEMPLATE_SOURCED_RULES as Jt, ProviderRegistry as K, getLegacyStateBucketName as Kn, withErrorHandling as Kr, STATE_SOURCED_CROSS_GENERATION_RULES as Kt, collectPublishedOutputNames as L, getDockerCmd as Ln, ResourceTimeoutError as Lr, s3BucketRegionalDomainName as Lt, gray as M, buildDenyExternalAccessPolicy as Mn, LockError as Mr, classifyReplaySecretRegion as Mt, green as N, describeAwsFailure as Nn, MissingCdkCliError as Nr, producerRegionsFromState as Nt, renderStatefulReason as O, parseBootstrapMarker as On, IntrinsicResolutionRefusalError as Or, replayWarn as Ot, red as P, buildDockerImage as Pn, NestedStackChildDirectDestroyError as Pr, s3BucketArn as Pt, CUSTOM_RESOURCE_RESPONSE_PREFIX as Q, resolveStateBucketWithDefaultAndSource as Qn, markRedactedCause as Qr, errorCauseChain as Qt, exportAliasCollisionScrubWarning as R, partitionSensitiveEnv as Rn, ResourceUpdateNotSupportedError as Rr, s3BucketWebsiteUrl as Rt, refusesFinalSnapshot as S, AssetModeResolver as Sn, CdkdError as Sr, resolveExplicitPhysicalId as St, extractDeploymentEventError as T, ensureAssetStorage as Tn, DependencyError as Tr, configBooleanRefusal as Tt, IAMRoleProvider as U, Synthesizer as Un, formatError as Ur, withRetry as Ut, stateKeySecretExposure as V, AssetManifestLoader as Vn, StateError as Vr, INTRINSIC_KEYS as Vt, collectInlinePolicyNamesManagedBySiblings as W, synthesisStatusMessage as Wn, isCdkdError as Wr, DagBuilder as Wt, maskDeep as X, resolveSkipPrefix as Xn, isTransientServerError as Xr, createSecretMasker as Xt, createMaskedRetryLogger as Y, resolveCaptureObservedState as Yn, isThrottlingError as Yr, carriesSecretMask as Yt, maskerOrIdentity as Z, resolveStateBucketWithDefault as Zn, markNonRetryable as Zr, dynamicReferenceTokens as Zt, PRE_DELETE_SNAPSHOT_TYPES as _, createAssetRedirectResolver as _n, AwsClients as _r, isUnboundTemplateParameter as _t, DeploymentEventsStore as a, scrubResourceRecord as an, findLargeInlineResources as ar, CloudControlProvider as at, createPreDeleteFinalSnapshot as b, escapeRegExp$1 as bn, setAwsClients as br, WAFv2WebACLProvider as bt, replayFailedOperations as c, rebuildClientForBucketRegion as cn, displaySafe as cr, deleteIndeterminateGuards as ct, updatePartialReason as d, importableOutputs as dn, canonicalizeRegion as dr, isTerminationProtectionPropagationError as dt, __exportAll as ei, maskSecretsInError as en, stateBucketExistenceConfirmed as er, beginCommandInterruptScope as et, withResourceDeadline as f, shouldRetainResource as fn, derivePartitionAndUrlSuffix as fr, IntrinsicFunctionResolver as ft, ATOMIC_FINAL_SNAPSHOT_TYPES as g, buildAssetRedirectMap as gn, resolveBucketRegion as gr, getAccountInfo as gt, computeImplicitDeleteEdges as h, WorkGraph as hn, clearBucketRegionCache as hr, coerceParameterTypedValue as ht, DeploymentEventsReader as i, redactSecretsForState as in, MIGRATE_TMP_PREFIX as ir, startInterruptWatch as it, cyan as j, validateContainerRepoName as jn, LocalStartServiceError as jr, requireConfigString as jt, formatResourceLine as k, readBootstrapMarkerBody as kn, LocalInvokeBuildError as kr, requireConfigArray as kt, replayRollback as l, exportNamesCarriedFrom as ln, expectedOwnerParam as lr, deleteSkipReason as lt, IMPLICIT_DELETE_DEPENDENCIES as m, stringifyValue as mn, processStackMessages as mr, cfnRefValueFromPhysicalId as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, recordMaskOnlyValue as nn, CFN_TEMPLATE_BODY_LIMIT as nr, interruptWatchListenerCount as nt, planFailedOps as o, LockManager as on, uploadCfnTemplate as or, slowCcOperationTimeoutMs as ot, maskingRetryLogger as p, AssetPublisher as pn, AssemblyReader as pr, carriesDynamicReference as pt, findActionableSilentDrops as q, resolveApp as qn, isMarkedNonRetryable as qr, STATE_SOURCED_READBACK_RULES as qt, DeployEngine as r, recoverMaskedOutput as rn, CFN_TEMPLATE_URL_LIMIT as rr, isInterruptedWaitError as rt, planRollback as s, S3StateBackend as sn, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as sr, UNSPECIFIED_SKIP_REASON as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, maskSecretsInText as tn, warnDeprecatedNoPrefixCliFlag as tr, endCommandInterruptScope as tt, updatePartialMessage as u, importableOutputKeys as un, PARTITION_TABLE as ur, disableInstanceApiTermination as ut, buildFinalSnapshotIdentifier as v, loadPublishableAssetManifest as vn, getAwsClients as vr, parameterTypeMayLoseSecretIdentity as vt, makeCanonicalizePropertiesFn as w, assertAssetBucketRegion as wn, CrossAccountSecretRefusalError as wr, coerceCfnBoolean as wt, isFinalSnapshotError as x, stripControlChars as xn, AssetError as xr, normalizeAwsTagsToCfn as xt, ccRoutedFinalSnapshotError as y, rewriteTemplateAssetReferences as yn, resetAwsClients as yr, refStateLookupFromResource as yt, isExportAliasCollision as z, runDockerForeground as zn, StackHasActiveImportsError as zr, applyRoleArnIfSet as zt };
34256
+ //# sourceMappingURL=deploy-engine-CA50haPO.js.map