@go-to-k/cdkd 0.290.28 → 0.290.30

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
- import { C as displayIdent, S as UNRENDERABLE, T as truncateCodePoints, 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 displaySafe, x as STACK_REF_MAX_CODE_POINTS, y as withoutGeneratedFallbackName } from "./logger-6LP1UiFA.js";
1
+ import { C as displayAwsMessage, E 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-BjWM-UVj.js";
2
2
  import { a as setAssumedRoleCredentials, i as getPreAssumeEnvCredentials, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-BoIxyCir.js";
3
- import { t as getCdkdVersion } from "./version-B2-UuEte.js";
3
+ import { t as getCdkdVersion } from "./version-C15BftsD.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";
@@ -20580,6 +20580,7 @@ const PROPERTY_COVERAGE_BY_TYPE = /* @__PURE__ */ new Map([
20580
20580
  "FifoThroughputScope",
20581
20581
  "FifoTopic",
20582
20582
  "KmsMasterKeyId",
20583
+ "MaximumMessageSize",
20583
20584
  "SignatureVersion",
20584
20585
  "Subscription",
20585
20586
  "Tags",
@@ -23136,7 +23137,8 @@ async function assumeRoleForCrossAccountStateRead(roleArn) {
23136
23137
  }
23137
23138
  const promise = (async () => {
23138
23139
  const logger = getLogger().child("role-arn");
23139
- logger.debug(`Assuming role for cross-account state read: ${roleArn}`);
23140
+ const displayRoleArn = displayIdent(roleArn, { maxCodePoints: 613 });
23141
+ logger.debug(`Assuming role for cross-account state read: ${displayRoleArn}`);
23140
23142
  const sts = new STSClient({ ...awsClientDefaults() });
23141
23143
  try {
23142
23144
  let response;
@@ -23147,13 +23149,13 @@ async function assumeRoleForCrossAccountStateRead(roleArn) {
23147
23149
  DurationSeconds: 3600
23148
23150
  }));
23149
23151
  } catch (err) {
23150
- const message = err instanceof Error ? err.message : String(err);
23151
- throw new Error(`AssumeRole into ${roleArn} failed: ${message}. If this is a trust-policy issue, the producer's role must allow sts:AssumeRole from the consumer's principal. See https://github.com/go-to-k/cdkd/blob/main/docs/cross-stack-references.md for the trust-policy template.`, { cause: err instanceof Error ? err : void 0 });
23152
+ const message = displayAwsMessage(err instanceof Error ? err.message : String(err));
23153
+ throw new Error(`AssumeRole into ${displayRoleArn} failed: ${message}. If this is a trust-policy issue, the producer's role must allow sts:AssumeRole from the consumer's principal. See https://github.com/go-to-k/cdkd/blob/main/docs/cross-stack-references.md for the trust-policy template.`, { cause: err instanceof Error ? err : void 0 });
23152
23154
  }
23153
- if (!response.Credentials) throw new Error(`AssumeRole for cross-account Fn::GetStackOutput returned no credentials (RoleArn=${roleArn})`);
23155
+ if (!response.Credentials) throw new Error(`AssumeRole for cross-account Fn::GetStackOutput returned no credentials (RoleArn=${displayRoleArn})`);
23154
23156
  const { AccessKeyId, SecretAccessKey, SessionToken, Expiration } = response.Credentials;
23155
- if (!AccessKeyId || !SecretAccessKey || !SessionToken) throw new Error(`AssumeRole response missing required credentials fields for cross-account state read (RoleArn=${roleArn})`);
23156
- logger.info(`Assumed role for cross-account state read: ${roleArn} (session expires ${Expiration?.toISOString() ?? "unknown"})`);
23157
+ if (!AccessKeyId || !SecretAccessKey || !SessionToken) throw new Error(`AssumeRole response missing required credentials fields for cross-account state read (RoleArn=${displayRoleArn})`);
23158
+ logger.info(`Assumed role for cross-account state read: ${displayRoleArn} (session expires ${Expiration?.toISOString() ?? "unknown"})`);
23157
23159
  return {
23158
23160
  accessKeyId: AccessKeyId,
23159
23161
  secretAccessKey: SecretAccessKey,
@@ -23261,18 +23263,23 @@ async function applyRoleArnIfSet(opts) {
23261
23263
  if (!roleArn) return;
23262
23264
  if (getAssumedRoleCredentials() !== void 0) throw new Error(SECOND_ROLE_PUBLISH_REFUSAL);
23263
23265
  const logger = getLogger().child("role-arn");
23264
- const displayRoleArn = displaySafe(roleArn, { asciiOnly: true });
23266
+ const displayRoleArn = displayIdent(roleArn, { maxCodePoints: 613 });
23265
23267
  logger.debug(`Assuming role ${displayRoleArn}...`);
23266
23268
  const sts = new STSClient({
23267
23269
  ...awsClientDefaults({ ignoreAssumedRole: true }),
23268
23270
  ...opts.region && { region: opts.region }
23269
23271
  });
23270
23272
  try {
23271
- const response = await sts.send(new AssumeRoleCommand({
23272
- RoleArn: roleArn,
23273
- RoleSessionName: `cdkd-${Date.now()}`,
23274
- DurationSeconds: 3600
23275
- }));
23273
+ let response;
23274
+ try {
23275
+ response = await sts.send(new AssumeRoleCommand({
23276
+ RoleArn: roleArn,
23277
+ RoleSessionName: `cdkd-${Date.now()}`,
23278
+ DurationSeconds: 3600
23279
+ }));
23280
+ } catch (err) {
23281
+ throw new Error(`AssumeRole for ${displayRoleArn} failed: ${displayAwsMessage(err instanceof Error ? err.message : String(err))}`, { cause: err instanceof Error ? err : void 0 });
23282
+ }
23276
23283
  if (!response.Credentials) throw new Error(`AssumeRole returned no credentials for role ${displayRoleArn}`);
23277
23284
  const { AccessKeyId, SecretAccessKey, SessionToken, Expiration } = response.Credentials;
23278
23285
  if (!AccessKeyId || !SecretAccessKey || !SessionToken) throw new Error(`AssumeRole response missing credentials fields for role ${displayRoleArn}`);
@@ -27647,7 +27654,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27647
27654
  const value = context.parameters[logicalId];
27648
27655
  const declared = context.template.Parameters;
27649
27656
  const paramDef = declared != null && Object.hasOwn(declared, logicalId) ? declared[logicalId] : void 0;
27650
- this.logger.debug(`Resolved Ref to parameter: ${logicalId} -> ${this.maskSecretsForLog(stringifyParameterForLog(paramDef, this.maskValueLeaves(value, context)), context)}`);
27657
+ this.logger.debug(`Resolved Ref to parameter: ${logicalId} -> ${this.displayMasked(stringifyParameterForLog(paramDef, this.maskValueLeaves(value, context)), context)}`);
27651
27658
  this.recordInheritedParameterSecrets(logicalId, value, context);
27652
27659
  return value;
27653
27660
  }
@@ -27820,11 +27827,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27820
27827
  this.rejectPlaceholderArnAttribute(resource, attributeName, flatValue, logicalId, context);
27821
27828
  if (resource.resourceType === "AWS::Route53::HostedZone" && attributeName === "NameServers" && typeof flatValue === "string") {
27822
27829
  const nameServers = flatValue === "" ? [] : flatValue.split(",");
27823
- this.logger.debug(`Normalized legacy Fn::GetAtt attribute: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, this.maskValueLeaves(nameServers, context)), context)}`);
27830
+ this.logger.debug(`Normalized legacy Fn::GetAtt attribute: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(nameServers, context)), context)}`);
27824
27831
  return this.noteAttributeSecrecy(logicalId, attributeName, nameServers, context);
27825
27832
  }
27826
- this.logger.debug(`Resolved Fn::GetAtt from attributes: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, this.maskValueLeaves(flatValue, context)), context)}`);
27827
- if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX) && carriesDynamicReference(flatValue)) return await this.reresolveCrossStackValue(flatValue, nestedStackChildRegionFromLocalArn(resource.physicalId), context, `nested stack ${logicalId} ${this.logTextOfLeaf(attributeName, context)}`, crossStackSourceKey({ "Fn::GetAtt": getAtt }));
27833
+ this.logger.debug(`Resolved Fn::GetAtt from attributes: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(flatValue, context)), context)}`);
27834
+ if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX) && carriesDynamicReference(flatValue)) return await this.reresolveCrossStackValue(flatValue, nestedStackChildRegionFromLocalArn(resource.physicalId), context, `nested stack ${logicalId} ${this.displayLeaf(attributeName, context)}`, crossStackSourceKey({ "Fn::GetAtt": getAtt }));
27828
27835
  return this.noteAttributeSecrecy(logicalId, attributeName, flatValue, context);
27829
27836
  }
27830
27837
  if (attributeName.includes(".")) {
@@ -27836,7 +27843,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27836
27843
  break;
27837
27844
  }
27838
27845
  if (cursor !== void 0) {
27839
- this.logger.debug(`Resolved Fn::GetAtt from nested attributes: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, this.maskValueLeaves(cursor, context)), context)}`);
27846
+ this.logger.debug(`Resolved Fn::GetAtt from nested attributes: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(cursor, context)), context)}`);
27840
27847
  return this.noteAttributeSecrecy(logicalId, attributeName, cursor, context);
27841
27848
  }
27842
27849
  }
@@ -27844,10 +27851,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27844
27851
  if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX)) {
27845
27852
  const declared = Object.keys(resource.attributes ?? {}).filter((k) => k.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX)).sort().map((k) => this.maskSecretsForLog(this.outputNameLogText(k, context), context));
27846
27853
  const declaredText = declared.length > 0 ? declared.join(", ") : "(none)";
27847
- throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.maskSecretsForLog(attributeName, context)}]: the nested stack '${logicalId}' declares no output named '${this.maskSecretsForLog(this.outputNameLogText(attributeName, context), context)}'. Its outputs are ${this.maskSecretsForLog(declaredText, context)}. Check the output name in the nested stack's template, and deploy the child stack again if you have just added it.`));
27854
+ throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}]: the nested stack '${logicalId}' declares no output named '${this.displayMasked(this.outputNameLogText(attributeName, context), context)}'. Its outputs are ${this.displayMasked(declaredText, context)}. Check the output name in the nested stack's template, and deploy the child stack again if you have just added it.`));
27848
27855
  }
27849
27856
  const value = await this.constructGuardedAttribute(resource, attributeName, context, logicalId);
27850
- this.logger.debug(`Resolved Fn::GetAtt: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, this.maskValueLeaves(value, context)), context)}`);
27857
+ this.logger.debug(`Resolved Fn::GetAtt: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(value, context)), context)}`);
27851
27858
  return value;
27852
27859
  }
27853
27860
  /**
@@ -27940,7 +27947,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27940
27947
  rejectPlaceholderArnAttribute(resource, attributeName, value, logicalId, context) {
27941
27948
  if (!REF_RETURNS_ARN_FROM_STATE.get(resource.resourceType)?.includes(attributeName)) return;
27942
27949
  if (typeof value !== "string" || !isPlaceholderArn(value)) return;
27943
- throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.maskSecretsForLog(attributeName, context)}] for ${resource.resourceType}: the recorded value "${stringifyValue(this.maskValueLeaves(value, context))}" is a placeholder written by a cdkd version older than issue #1681 — its region and account fields are literal wildcards, so it is not a usable ARN. Deploy the stack again so the resource's next update heals the record (cdkd now records the real ARN), or re-import the resource.`));
27950
+ throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resource.resourceType}: the recorded value "${stringifyValue(this.maskValueLeaves(value, context))}" is a placeholder written by a cdkd version older than issue #1681 — its region and account fields are literal wildcards, so it is not a usable ARN. Deploy the stack again so the resource's next update heals the record (cdkd now records the real ARN), or re-import the resource.`));
27944
27951
  }
27945
27952
  /**
27946
27953
  * Construct resource attribute value based on resource type, refusing to
@@ -27985,7 +27992,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27985
27992
  async constructGuardedAttribute(resource, attributeName, context, logicalId) {
27986
27993
  const accountInfo = await getAccountInfo(this.resolverRegion);
27987
27994
  const value = await this.constructAttribute(resource, attributeName, context, logicalId, accountInfo);
27988
- if (accountInfo.fabricated && embedsAccountId(value, accountInfo.accountId)) throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.maskSecretsForLog(attributeName, context)}] for ${resource.resourceType}: STS did not report this deploy's account id, so cdkd would build the value from the placeholder account ${accountInfo.accountId} — structurally valid, naming a different account, and indistinguishable downstream from a real one. Fix the AWS credentials (or set AWS_ACCOUNT_ID to this deploy's account) and deploy again.`);
27995
+ if (accountInfo.fabricated && embedsAccountId(value, accountInfo.accountId)) throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resource.resourceType}: STS did not report this deploy's account id, so cdkd would build the value from the placeholder account ${accountInfo.accountId} — structurally valid, naming a different account, and indistinguishable downstream from a real one. Fix the AWS credentials (or set AWS_ACCOUNT_ID to this deploy's account) and deploy again.`);
27989
27996
  return value;
27990
27997
  }
27991
27998
  /**
@@ -28030,7 +28037,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28030
28037
  const associations = (await ec2.send(new DescribeVpcsCommand({ VpcIds: [physicalId] }))).Vpcs?.[0]?.Ipv6CidrBlockAssociationSet || [];
28031
28038
  const blocks = associations.filter((a) => a.Ipv6CidrBlockState?.State === "associated").map((a) => a.Ipv6CidrBlock);
28032
28039
  if (blocks.length > 0) {
28033
- this.logger.debug(`Resolved VPC Ipv6CidrBlocks for ${physicalId}: ${this.maskSecretsForLog(JSON.stringify(this.maskValueLeaves(blocks, context)), context)}`);
28040
+ this.logger.debug(`Resolved VPC Ipv6CidrBlocks for ${physicalId}: ${this.displayMasked(JSON.stringify(this.maskValueLeaves(blocks, context)), context)}`);
28034
28041
  return blocks;
28035
28042
  }
28036
28043
  if (associations.filter((a) => a.Ipv6CidrBlockState?.State === "associating").length === 0) {
@@ -28047,7 +28054,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28047
28054
  return [];
28048
28055
  }
28049
28056
  case "DefaultSecurityGroup": {
28050
- if (!/^vpc-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, DefaultSecurityGroup] for AWS::EC2::VPC: the state record's physical id "${displaySafe(this.maskThenStripThenMask(physicalId, context)).slice(0, 64)}" is not a VPC id (vpc-<hex>), so cdkd will not use it as an EC2 filter value. Repair the record (cdkd import, or re-create the VPC) and deploy again.`));
28057
+ if (!/^vpc-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, DefaultSecurityGroup] for AWS::EC2::VPC: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a VPC id (vpc-<hex>), so cdkd will not use it as an EC2 filter value. Repair the record (cdkd import, or re-create the VPC) and deploy again.`));
28051
28058
  const cachedGroupId = cachedVpcDefaultSecurityGroups[physicalId];
28052
28059
  if (cachedGroupId !== void 0) return cachedGroupId;
28053
28060
  let groupId;
@@ -28262,7 +28269,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28262
28269
  if (resourceType === "AWS::EC2::SecurityGroup") switch (attributeName) {
28263
28270
  case "GroupId": return physicalId;
28264
28271
  case "VpcId": {
28265
- if (!/^sg-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, VpcId] for AWS::EC2::SecurityGroup: the state record's physical id "${displaySafe(this.maskThenStripThenMask(physicalId, context)).slice(0, 64)}" is not a security group id (sg-<hex>), so cdkd will not look it up. Repair the record (cdkd import, or re-create the security group) and deploy again.`));
28272
+ if (!/^sg-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, VpcId] for AWS::EC2::SecurityGroup: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a security group id (sg-<hex>), so cdkd will not look it up. Repair the record (cdkd import, or re-create the security group) and deploy again.`));
28266
28273
  const cachedVpcId = cachedSecurityGroupVpcIds[physicalId];
28267
28274
  if (cachedVpcId !== void 0) return cachedVpcId;
28268
28275
  let vpcId;
@@ -28356,7 +28363,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28356
28363
  resourceType,
28357
28364
  physicalId,
28358
28365
  context,
28359
- observed: `DescribeInstances reports no ${this.maskSecretsForLog(attributeName, context)} yet (${observedState})`,
28366
+ observed: `DescribeInstances reports no ${this.displayMasked(attributeName, context)} yet (${observedState})`,
28360
28367
  remedy: settled ? "Check the instance in the console; a terminated or stopped instance has no such attribute to serve." : "Deploy without --no-wait so the instance is running before its attributes are read, or reference the attribute from a later deploy once it is."
28361
28368
  });
28362
28369
  }
@@ -28369,7 +28376,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28369
28376
  const value = attributeName === "LatestVersionNumber" ? lt?.LatestVersionNumber : lt?.DefaultVersionNumber;
28370
28377
  if (value !== void 0 && value !== null) return String(value);
28371
28378
  } catch (err) {
28372
- this.logger.warn(`DescribeLaunchTemplates(${physicalId}) failed for ${this.maskSecretsForLog(attributeName, context)}: ${this.maskSecretsForLog(err instanceof Error ? err.message : String(err), context)}`);
28379
+ this.logger.warn(`DescribeLaunchTemplates(${physicalId}) failed for ${this.displayMasked(attributeName, context)}: ${this.displayMasked(err instanceof Error ? err.message : String(err), context)}`);
28373
28380
  }
28374
28381
  return attributeName === "LatestVersionNumber" ? "$Latest" : "$Default";
28375
28382
  }
@@ -28379,7 +28386,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28379
28386
  if (resourceType === "AWS::CloudFront::Distribution") switch (attributeName) {
28380
28387
  case "Id": return physicalId;
28381
28388
  case "DomainName": {
28382
- if (!/^[A-Z0-9]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, DomainName] for AWS::CloudFront::Distribution: the state record's physical id "${displaySafe(this.maskThenStripThenMask(physicalId, context)).slice(0, 64)}" is not a distribution id (upper-case alphanumerics), so cdkd will not look it up. Repair the record (cdkd import, or re-create the distribution) and deploy again.`));
28389
+ if (!/^[A-Z0-9]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, DomainName] for AWS::CloudFront::Distribution: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a distribution id (upper-case alphanumerics), so cdkd will not look it up. Repair the record (cdkd import, or re-create the distribution) and deploy again.`));
28383
28390
  const cachedDomainName = cachedCloudFrontDomainNames[physicalId];
28384
28391
  if (cachedDomainName !== void 0) return cachedDomainName;
28385
28392
  let domainName;
@@ -28413,7 +28420,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28413
28420
  default: return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
28414
28421
  }
28415
28422
  if (resourceType === "AWS::RDS::DBProxy" || resourceType === "AWS::RDS::DBProxyEndpoint") {
28416
- if (attributeName === "VpcId") throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.maskSecretsForLog(attributeName, context)}] for ${resourceType}: the state record holds no VpcId for it (the read-back that would have recorded it reported none), and the physical id "${displaySafe(this.maskThenStripThenMask(physicalId, context))}" is a name, not a VPC id, so cdkd refuses to substitute it. Update the resource so its next deploy records the value, or reference the VPC directly.`));
28423
+ if (attributeName === "VpcId") throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: the state record holds no VpcId for it (the read-back that would have recorded it reported none), and the physical id "${this.displayMasked(physicalId, context)}" is a name, not a VPC id, so cdkd refuses to substitute it. Update the resource so its next deploy records the value, or reference the VPC directly.`));
28417
28424
  return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
28418
28425
  }
28419
28426
  return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
@@ -28472,7 +28479,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28472
28479
  */
28473
28480
  refuseUnservedAttribute(site) {
28474
28481
  const { logicalId, attributeName, resourceType, physicalId, context, observed, remedy } = site;
28475
- throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.maskSecretsForLog(attributeName, context)}] for ${resourceType}: ${observed}. The physical id "${displaySafe(this.maskThenStripThenMask(physicalId, context))}" is not a usable ${this.maskSecretsForLog(attributeName, context)}, so cdkd refuses to substitute it. ${remedy}`);
28482
+ throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: ${observed}. The physical id "${this.displayMasked(physicalId, context)}" is not a usable ${this.displayMasked(attributeName, context)}, so cdkd refuses to substitute it. ${remedy}`);
28476
28483
  }
28477
28484
  /**
28478
28485
  * The `observed` clause of {@link refuseUnservedAttribute} for a live read
@@ -28484,7 +28491,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28484
28491
  const name = displaySafe(err instanceof Error && err.name ? err.name : "Error", { asciiOnly: true }).slice(0, 64).trim() || "Error";
28485
28492
  const status = err?.$metadata?.httpStatusCode;
28486
28493
  const errorClass = typeof status === "number" && Number.isInteger(status) ? `${name}, HTTP ${status}` : name;
28487
- this.logger.debug(`${read} failed (${errorClass}): ${this.maskSecretsForLog(err instanceof Error ? err.message : String(err), context)}`);
28494
+ this.logger.debug(`${read} failed (${errorClass}): ${this.displayMasked(err instanceof Error ? err.message : String(err), context)}`);
28488
28495
  return `${read} failed (${errorClass}); re-run with --verbose for the AWS error text`;
28489
28496
  }
28490
28497
  /**
@@ -28519,11 +28526,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28519
28526
  const expectsUrlShape = attributeName.endsWith("Url") && !/^https?:\/\//.test(physicalId);
28520
28527
  if (expectsArnShape || expectsUrlShape) {
28521
28528
  const expectedShape = expectsArnShape ? "an ARN (arn:...)" : "a URL (http(s)://...)";
28522
- throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.maskSecretsForLog(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and the physical ID fallback "${displaySafe(this.maskThenStripThenMask(physicalId, context))}" is not ${expectedShape}. CloudFormation would return a different value here, so falling back to the physical ID would silently produce a wrong value (e.g. in stack Outputs). Avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.maskSecretsForLog(attributeName, context)}.`));
28529
+ throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and the physical ID fallback "${this.displayMasked(physicalId, context)}" is not ${expectedShape}. CloudFormation would return a different value here, so falling back to the physical ID would silently produce a wrong value (e.g. in stack Outputs). Avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.displayMasked(attributeName, context)}.`));
28523
28530
  }
28524
- if (this.strictGetAtt) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.maskSecretsForLog(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and --strict-getatt rejects the physical ID fallback "${displaySafe(this.maskThenStripThenMask(physicalId, context))}" (which may not be the value CloudFormation would return). Drop --strict-getatt to fall back with a warning, avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.maskSecretsForLog(attributeName, context)}.`));
28531
+ if (this.strictGetAtt) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and --strict-getatt rejects the physical ID fallback "${this.displayMasked(physicalId, context)}" (which may not be the value CloudFormation would return). Drop --strict-getatt to fall back with a warning, avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.displayMasked(attributeName, context)}.`));
28525
28532
  this.physicalIdFallbackCount++;
28526
- this.logger.warn(`Unknown attribute ${this.maskSecretsForLog(attributeName, context)} for resource type ${resourceType}, returning physical ID`);
28533
+ this.logger.warn(`Unknown attribute ${this.displayMasked(attributeName, context)} for resource type ${resourceType}, returning physical ID`);
28527
28534
  return physicalId;
28528
28535
  }
28529
28536
  /**
@@ -28594,7 +28601,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28594
28601
  complete
28595
28602
  });
28596
28603
  this.rememberLogTwin(context, result, twin);
28597
- this.logger.debug(`Resolved Fn::Join: ${this.maskSecretsForLog(this.logTwinText(result, twin, context), context)}`);
28604
+ this.logger.debug(`Resolved Fn::Join: ${this.displayMasked(this.logTwinText(result, twin, context), context)}`);
28598
28605
  return result;
28599
28606
  }
28600
28607
  /**
@@ -28843,7 +28850,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28843
28850
  complete
28844
28851
  });
28845
28852
  this.rememberLogTwin(context, result, twin);
28846
- this.logger.debug(`Resolved Fn::Sub: ${this.maskSecretsForLog(this.logTwinText(result, twin, context), context)}`);
28853
+ this.logger.debug(`Resolved Fn::Sub: ${this.displayMasked(this.logTwinText(result, twin, context), context)}`);
28847
28854
  return result;
28848
28855
  }
28849
28856
  /**
@@ -29210,7 +29217,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29210
29217
  }
29211
29218
  return v;
29212
29219
  };
29213
- this.logger.debug(`Re-resolving dynamic reference(s) in ${this.maskSecretsForLog(origin, context)}`);
29220
+ this.logger.debug(`Re-resolving dynamic reference(s) in ${this.displayMasked(origin, context)}`);
29214
29221
  const reresolved = await walk(value);
29215
29222
  if (sourceKey !== void 0 && typeof value === "string" && isSingleDynamicReferenceToken(value) && typeof reresolved === "string" && context.recordedSecretValues?.has(reresolved) === true && isSecretExpressionByVerdictOrSpelling(value)) recordCrossStackExpression(context.recordedSecretValues, sourceKey, value, reresolved);
29216
29223
  return reresolved;
@@ -29313,13 +29320,13 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29313
29320
  try {
29314
29321
  entry = await context.exportIndex.lookup(exportName);
29315
29322
  } catch (err) {
29316
- this.logger.warn(`Exports index lookup failed for '${loggedExportName}': ${this.maskSecretsForLog(err instanceof Error ? err.message : String(err), context)}; falling back to state.json scan`);
29323
+ this.logger.warn(`Exports index lookup failed for '${loggedExportName}': ${this.displayMasked(err instanceof Error ? err.message : String(err), context)}; falling back to state.json scan`);
29317
29324
  entry = void 0;
29318
29325
  }
29319
29326
  if (entry && (!context.stackName || entry.producerStack !== context.stackName)) {
29320
29327
  this.recordImport(context, exportName, entry.producerStack, entry.producerRegion);
29321
- this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from index: ${this.maskSecretsForLog(entry.producerStack, context)} / ${this.maskSecretsForLog(entry.producerRegion, context)}; ${carriesDynamicReference(entry.value) ? "redacted dynamic reference" : "literal value"})`);
29322
- return await this.reresolveCrossStackValue(entry.value, entry.producerRegion, context, `Fn::ImportValue '${this.logTextOfLeaf(exportName, context)}' (producer ${this.logTextOfLeaf(entry.producerStack, context)} / ${this.logTextOfLeaf(entry.producerRegion, context)})`, sourceKey, {
29328
+ this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from index: ${this.displayMasked(entry.producerStack, context)} / ${this.displayMasked(entry.producerRegion, context)}; ${carriesDynamicReference(entry.value) ? "redacted dynamic reference" : "literal value"})`);
29329
+ return await this.reresolveCrossStackValue(entry.value, entry.producerRegion, context, `Fn::ImportValue '${this.displayLeaf(exportName, context)}' (producer ${this.displayLeaf(entry.producerStack, context)} / ${this.displayLeaf(entry.producerRegion, context)})`, sourceKey, {
29323
29330
  stackName: entry.producerStack,
29324
29331
  region: entry.producerRegion,
29325
29332
  outputKey: exportName
@@ -29332,30 +29339,30 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29332
29339
  for (const ref of allStacks) {
29333
29340
  const { stackName: refStack, region: refRegion } = ref;
29334
29341
  if (context.stackName && refStack === context.stackName) {
29335
- this.logger.debug(`Skipping current stack: ${this.maskSecretsForLog(refStack, context)}`);
29342
+ this.logger.debug(`Skipping current stack: ${this.displayMasked(refStack, context)}`);
29336
29343
  continue;
29337
29344
  }
29338
29345
  try {
29339
29346
  const lookupRegion = refRegion ?? this.resolverRegion ?? "";
29340
29347
  if (!lookupRegion) {
29341
- this.logger.debug(`No region available for stack '${this.maskSecretsForLog(refStack, context)}' — skipping (cdkd cannot read state without a region)`);
29348
+ this.logger.debug(`No region available for stack '${this.displayMasked(refStack, context)}' — skipping (cdkd cannot read state without a region)`);
29342
29349
  continue;
29343
29350
  }
29344
29351
  const stateData = await context.stateBackend.getState(refStack, lookupRegion);
29345
29352
  if (!stateData) {
29346
- this.logger.debug(`No state found for stack: ${this.maskSecretsForLog(refStack, context)} (${this.maskSecretsForLog(lookupRegion, context)})`);
29353
+ this.logger.debug(`No state found for stack: ${this.displayMasked(refStack, context)} (${this.displayMasked(lookupRegion, context)})`);
29347
29354
  continue;
29348
29355
  }
29349
29356
  const { state } = stateData;
29350
29357
  if (importableOutputKeys(state).includes(exportName)) {
29351
29358
  const value = state.outputs[exportName];
29352
- this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from stack: ${this.maskSecretsForLog(refStack, context)} / ${this.maskSecretsForLog(lookupRegion, context)}; ${carriesDynamicReference(value) ? "redacted dynamic reference" : "literal value"})`);
29359
+ this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from stack: ${this.displayMasked(refStack, context)} / ${this.displayMasked(lookupRegion, context)}; ${carriesDynamicReference(value) ? "redacted dynamic reference" : "literal value"})`);
29353
29360
  if (context.exportIndex) context.exportIndex.patchEntry(exportName, {
29354
29361
  value,
29355
29362
  producerStack: refStack,
29356
29363
  producerRegion: lookupRegion
29357
29364
  }).catch((err) => {
29358
- this.logger.debug(`Failed to patch exports index for '${this.maskSecretsForLog(exportName, context)}': ${this.maskSecretsForLog(err instanceof Error ? err.message : String(err), context)}`);
29365
+ this.logger.debug(`Failed to patch exports index for '${this.displayMasked(exportName, context)}': ${this.displayMasked(err instanceof Error ? err.message : String(err), context)}`);
29359
29366
  });
29360
29367
  this.recordImport(context, exportName, refStack, lookupRegion);
29361
29368
  found = {
@@ -29366,11 +29373,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29366
29373
  break;
29367
29374
  }
29368
29375
  } catch (error) {
29369
- this.logger.warn(`Failed to read state for stack ${this.maskSecretsForLog(refStack, context)}: ${this.maskSecretsForLog(error instanceof Error ? error.message : String(error), context)}`);
29376
+ this.logger.warn(`Failed to read state for stack ${this.displayMasked(refStack, context)}: ${this.displayMasked(error instanceof Error ? error.message : String(error), context)}`);
29370
29377
  continue;
29371
29378
  }
29372
29379
  }
29373
- if (found) return await this.reresolveCrossStackValue(found.value, found.lookupRegion, context, `Fn::ImportValue '${this.logTextOfLeaf(exportName, context)}' (producer ${this.logTextOfLeaf(found.refStack, context)} / ${this.logTextOfLeaf(found.lookupRegion, context)})`, sourceKey, {
29380
+ if (found) return await this.reresolveCrossStackValue(found.value, found.lookupRegion, context, `Fn::ImportValue '${this.displayLeaf(exportName, context)}' (producer ${this.displayLeaf(found.refStack, context)} / ${this.displayLeaf(found.lookupRegion, context)})`, sourceKey, {
29374
29381
  stackName: found.refStack,
29375
29382
  region: found.lookupRegion,
29376
29383
  outputKey: exportName
@@ -29413,7 +29420,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29413
29420
  };
29414
29421
  return;
29415
29422
  } catch (error) {
29416
- this.logger.warn(`Fn::ImportValue: CloudFormation ListExports fallback failed for export '${this.maskSecretsForLog(exportName, context)}' (region ${this.resolverRegion}): ${this.maskSecretsForLog(error instanceof Error ? error.message : String(error), context)}. Grant cloudformation:ListExports to resolve exports from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
29423
+ this.logger.warn(`Fn::ImportValue: CloudFormation ListExports fallback failed for export '${this.displayMasked(exportName, context)}' (region ${this.resolverRegion}): ${this.displayMasked(error instanceof Error ? error.message : String(error), context)}. Grant cloudformation:ListExports to resolve exports from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
29417
29424
  return;
29418
29425
  }
29419
29426
  }
@@ -29445,7 +29452,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29445
29452
  describeAvailableOutputs(keys, context) {
29446
29453
  if (keys.length === 0) return "(none)";
29447
29454
  const shown = keys.slice(0, MAX_LISTED_AVAILABLE_OUTPUTS);
29448
- const rendered = shown.map((k) => this.maskSecretsForLog(k, context)).join(", ");
29455
+ const rendered = shown.map((k) => this.displayMasked(k, context)).join(", ");
29449
29456
  const hidden = keys.length - shown.length;
29450
29457
  return hidden > 0 ? `${rendered} (+${hidden} more)` : rendered;
29451
29458
  }
@@ -29485,8 +29492,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29485
29492
  return await fetch;
29486
29493
  } catch (error) {
29487
29494
  const message = error instanceof Error ? error.message : String(error);
29488
- const cfnNameMask = this.positionalNameMask([[stackName, this.maskSecretsForLog(stackName, context)], [region, this.maskSecretsForLog(loggedRegionText, context)]]);
29489
- this.logger.warn(`Fn::GetStackOutput: CloudFormation DescribeStacks fallback failed for stack '${this.maskSecretsForLog(stackName, context)}' (${this.maskSecretsForLog(loggedRegionText, context)}): ${this.maskSecretsForLog(cfnNameMask ? cfnNameMask(message) : message, context)}. Grant cloudformation:DescribeStacks to resolve outputs from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
29495
+ const cfnNameMask = this.positionalNameMask([[stackName, this.displayMasked(stackName, context)], [region, this.displayMasked(loggedRegionText, context)]]);
29496
+ this.logger.warn(`Fn::GetStackOutput: CloudFormation DescribeStacks fallback failed for stack '${this.displayMasked(stackName, context)}' (${this.displayMasked(loggedRegionText, context)}): ${displayAwsMessage(this.maskSecretsForLog(cfnNameMask ? cfnNameMask(message) : message, context))}. Grant cloudformation:DescribeStacks to resolve outputs from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
29490
29497
  return;
29491
29498
  }
29492
29499
  }
@@ -29596,7 +29603,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29596
29603
  const resolvedRegion = await this.resolveValue(args["Region"], context);
29597
29604
  if (typeof resolvedRegion !== "string" || resolvedRegion === "") throw new Error(`Fn::GetStackOutput: Region must resolve to a non-empty string, got ${typeof resolvedRegion}`);
29598
29605
  const requestedRegion = canonicalizeRegion(resolvedRegion);
29599
- if (!isClientSafeRegion(requestedRegion)) throw new Error(`Fn::GetStackOutput: '${this.maskThenStripThenMask(this.logTextOfLeaf(resolvedRegion, context) !== resolvedRegion ? "***" : resolvedRegion, context).slice(0, 64)}' is not a valid AWS region name. The region selects both the AWS endpoint and the state-file key, so cdkd will not use it.`);
29606
+ if (!isClientSafeRegion(requestedRegion)) throw new Error(`Fn::GetStackOutput: '${this.displayMasked(this.logTextOfLeaf(resolvedRegion, context) !== resolvedRegion ? "***" : resolvedRegion, context).slice(0, 64)}' is not a valid AWS region name. The region selects both the AWS endpoint and the state-file key, so cdkd will not use it.`);
29600
29607
  region = requestedRegion;
29601
29608
  loggedRegionText = this.regionLogText(resolvedRegion, context);
29602
29609
  }
@@ -29606,11 +29613,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29606
29613
  if (typeof raw !== "string" || raw === "") throw new Error(`Fn::GetStackOutput: RoleArn must be a literal string in the template (no Ref / Fn::GetAtt / Fn::Sub allowed for cross-account references). Got ${raw === null ? "null" : Array.isArray(raw) ? "array" : typeof raw}${typeof raw === "object" ? ` (intrinsic shape: ${JSON.stringify(raw).slice(0, 80)})` : ""}.`);
29607
29614
  roleArn = raw;
29608
29615
  }
29609
- if (!roleArn && context.stackName && context.stackName === stackName && canonicalizeRegion(region) === canonicalizeRegion(this.resolverRegion)) throw new Error(`Fn::GetStackOutput: cannot reference own stack '${this.maskSecretsForLog(stackName, context)}' in the same region '${this.maskSecretsForLog(loggedRegionText, context)}'`);
29610
- const loggedStackName = this.maskSecretsForLog(stackName, context);
29611
- const loggedOutputName = this.maskSecretsForLog(outputName, context);
29616
+ if (!roleArn && context.stackName && context.stackName === stackName && canonicalizeRegion(region) === canonicalizeRegion(this.resolverRegion)) throw new Error(`Fn::GetStackOutput: cannot reference own stack '${this.displayMasked(stackName, context)}' in the same region '${this.displayMasked(loggedRegionText, context)}'`);
29617
+ const loggedStackName = this.displayMasked(stackName, context);
29618
+ const loggedOutputName = this.displayMasked(outputName, context);
29612
29619
  const loggedRegion = this.maskSecretsForLog(loggedRegionText, context);
29613
- this.logger.debug(`Resolving Fn::GetStackOutput: StackName=${loggedStackName}, Region=${loggedRegion}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${roleArn}` : ""}`);
29620
+ const shownRoleArn = roleArn ? displayIdent(roleArn, { maxCodePoints: 613 }) : "";
29621
+ this.logger.debug(`Resolving Fn::GetStackOutput: StackName=${loggedStackName}, Region=${loggedRegion}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${shownRoleArn}` : ""}`);
29614
29622
  let stateData;
29615
29623
  try {
29616
29624
  stateData = roleArn ? await this.getCrossAccountStackState(roleArn, stackName, region, context) : await this.getSameAccountStackState(stackName, region, context);
@@ -29630,7 +29638,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29630
29638
  return value;
29631
29639
  }
29632
29640
  }
29633
- throw new Error(`Fn::GetStackOutput: stack '${loggedStackName}' not found in region '${loggedRegion}'${roleArn ? ` (cross-account via ${roleArn})` : ""}. ${!roleArn && this.cfnFallback ? "Searched cdkd state and CloudFormation stacks. Make sure the producer stack has been deployed (via cdkd or CloudFormation)." : `Make sure the producer stack has been deployed via cdkd.`}`);
29641
+ throw new Error(`Fn::GetStackOutput: stack '${loggedStackName}' not found in region '${loggedRegion}'${roleArn ? ` (cross-account via ${shownRoleArn})` : ""}. ${!roleArn && this.cfnFallback ? "Searched cdkd state and CloudFormation stacks. Make sure the producer stack has been deployed (via cdkd or CloudFormation)." : `Make sure the producer stack has been deployed via cdkd.`}`);
29634
29642
  }
29635
29643
  if (!hasReadableOutputs(stateData.state)) throw markNonRetryable(new MalformedProducerRecordRefusalError(`Fn::GetStackOutput: the state record of producer stack '${loggedStackName}' (${loggedRegion}) has no readable 'outputs' map — the record is malformed or truncated. cdkd refuses to resolve from it rather than reading it as a map: 'Object.hasOwn' answers TRUE for '0' on a string and for an index on a list, so continuing would resolve ONE CHARACTER or element of the record as this reference's value and this deploy would send it to AWS. Inspect the producer's record with 'cdkd state show' --json, repair or remove it, then re-run.`));
29636
29644
  const outputs = stateData.state.outputs ?? {};
@@ -29639,10 +29647,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29639
29647
  throw new Error(`Fn::GetStackOutput: output '${loggedOutputName}' not found in stack '${loggedStackName}' (${loggedRegion}). Available outputs: ${available}`);
29640
29648
  }
29641
29649
  const value = outputs[outputName];
29642
- this.logger.info(`Resolved Fn::GetStackOutput: StackName=${loggedStackName}, Region=${loggedRegion}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${roleArn}` : ""} (${carriesDynamicReference(value) ? "redacted dynamic reference" : "literal value"})`);
29650
+ this.logger.info(`Resolved Fn::GetStackOutput: StackName=${loggedStackName}, Region=${loggedRegion}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${shownRoleArn}` : ""} (${carriesDynamicReference(value) ? "redacted dynamic reference" : "literal value"})`);
29643
29651
  if (!roleArn) this.recordOutputRead(context, stackName, region, outputName);
29644
- if (roleArn && !context.skipDynamicReferences && carriesDynamicReference(value)) throw markNonRetryable(new CrossAccountSecretRefusalError(`Fn::GetStackOutput: output '${loggedOutputName}' of stack '${loggedStackName}' (${loggedRegion}) is a redacted dynamic reference, and this is a CROSS-ACCOUNT reference (RoleArn ${roleArn}). cdkd will not resolve a producer account's secret with the consumer's credentials — a same-named secret in the consumer account would answer instead. Export a non-secret value (e.g. the secret's ARN) and resolve it in the consumer stack, or reference the producer stack from within its own account.`));
29645
- return await this.reresolveCrossStackValue(value, region, context, `Fn::GetStackOutput '${this.logTextOfLeaf(outputName, context)}' (producer ${this.logTextOfLeaf(stackName, context)} / ${loggedRegionText})`, sourceKey, {
29652
+ if (roleArn && !context.skipDynamicReferences && carriesDynamicReference(value)) throw markNonRetryable(new CrossAccountSecretRefusalError(`Fn::GetStackOutput: output '${loggedOutputName}' of stack '${loggedStackName}' (${loggedRegion}) is a redacted dynamic reference, and this is a CROSS-ACCOUNT reference (RoleArn ${shownRoleArn}). cdkd will not resolve a producer account's secret with the consumer's credentials — a same-named secret in the consumer account would answer instead. Export a non-secret value (e.g. the secret's ARN) and resolve it in the consumer stack, or reference the producer stack from within its own account.`));
29653
+ return await this.reresolveCrossStackValue(value, region, context, `Fn::GetStackOutput '${this.displayLeaf(outputName, context)}' (producer ${this.displayLeaf(stackName, context)} / ${this.displayLeaf(loggedRegionText, context)})`, sourceKey, {
29646
29654
  stackName,
29647
29655
  region,
29648
29656
  outputKey: outputName,
@@ -29794,7 +29802,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29794
29802
  */
29795
29803
  async getCrossAccountStackState(roleArn, stackName, region, context) {
29796
29804
  const parsed = parseIamRoleArn(roleArn);
29797
- if (!parsed) throw new Error(`Fn::GetStackOutput: RoleArn '${roleArn}' is not a valid IAM role ARN. Expected shape: arn:<partition>:iam::<12-digit-account-id>:role/<role-name> (e.g. arn:aws:iam::123456789012:role/MyRole, arn:aws-us-gov:iam::...).`);
29805
+ if (!parsed) throw new Error(`Fn::GetStackOutput: RoleArn '${displayIdent(roleArn, { maxCodePoints: 613 })}' is not a valid IAM role ARN. Expected shape: arn:<partition>:iam::<12-digit-account-id>:role/<role-name> (e.g. arn:aws:iam::123456789012:role/MyRole, arn:aws-us-gov:iam::...).`);
29798
29806
  const credentials = await assumeRoleForCrossAccountStateRead(roleArn);
29799
29807
  const { bucket, region: bucketRegion } = await resolveCrossAccountStateBucket(parsed.accountId, credentials);
29800
29808
  const prefix = context.stateBackend?.prefix ?? "cdkd";
@@ -29851,19 +29859,19 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29851
29859
  }
29852
29860
  if (!map) {
29853
29861
  if (hasDefaultValue) return await resolveDefault();
29854
- throw new Error(`Fn::FindInMap: mapping '${this.maskSecretsForLog(mapName, context)}' not found in Mappings section`);
29862
+ throw new Error(`Fn::FindInMap: mapping '${this.displayMasked(mapName, context)}' not found in Mappings section`);
29855
29863
  }
29856
29864
  const topLevel = Object.hasOwn(map, topLevelKey) ? map[topLevelKey] : void 0;
29857
29865
  if (!topLevel || typeof topLevel !== "object") {
29858
29866
  if (hasDefaultValue) return await resolveDefault();
29859
- throw new Error(`Fn::FindInMap: top-level key '${this.maskSecretsForLog(topLevelKey, context)}' not found in mapping '${this.maskSecretsForLog(mapName, context)}'`);
29867
+ throw new Error(`Fn::FindInMap: top-level key '${this.displayMasked(topLevelKey, context)}' not found in mapping '${this.displayMasked(mapName, context)}'`);
29860
29868
  }
29861
29869
  if (!Object.hasOwn(topLevel, secondLevelKey)) {
29862
29870
  if (hasDefaultValue) return await resolveDefault();
29863
- throw new Error(`Fn::FindInMap: second-level key '${this.maskSecretsForLog(secondLevelKey, context)}' not found in mapping '${this.maskSecretsForLog(mapName, context)}' -> '${this.maskSecretsForLog(topLevelKey, context)}'`);
29871
+ throw new Error(`Fn::FindInMap: second-level key '${this.displayMasked(secondLevelKey, context)}' not found in mapping '${this.displayMasked(mapName, context)}' -> '${this.displayMasked(topLevelKey, context)}'`);
29864
29872
  }
29865
29873
  const result = topLevel[secondLevelKey];
29866
- this.logger.debug(`Resolved Fn::FindInMap: ${this.maskSecretsForLog(mapName, context)}.${this.maskSecretsForLog(topLevelKey, context)}.${this.maskSecretsForLog(secondLevelKey, context)} -> ${this.maskSecretsForLog(JSON.stringify(this.maskValueLeaves(result, context)), context)}`);
29874
+ this.logger.debug(`Resolved Fn::FindInMap: ${this.displayMasked(mapName, context)}.${this.displayMasked(topLevelKey, context)}.${this.displayMasked(secondLevelKey, context)} -> ${this.displayMasked(JSON.stringify(this.maskValueLeaves(result, context)), context)}`);
29867
29875
  return result;
29868
29876
  }
29869
29877
  /**
@@ -29878,7 +29886,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29878
29886
  const result = Buffer.from(resolvedValue).toString("base64");
29879
29887
  const inputLogText = this.logTextOfLeaf(resolvedValue, context);
29880
29888
  if (context.recordedSecretValues && (inputLogText !== resolvedValue || this.maskNeedlesForLog(resolvedValue, context) !== resolvedValue)) recordMaskOnlyValue(context.recordedSecretValues, result);
29881
- this.logger.debug(`Resolved Fn::Base64: ${this.maskSecretsForLog(inputLogText, context)} -> ${this.maskSecretsForLog(inputLogText !== resolvedValue ? "***" : result, context)}`);
29889
+ this.logger.debug(`Resolved Fn::Base64: ${this.displayMasked(inputLogText, context)} -> ${this.displayMasked(inputLogText !== resolvedValue ? "***" : result, context)}`);
29882
29890
  return result;
29883
29891
  }
29884
29892
  /**
@@ -29910,7 +29918,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29910
29918
  let loggedRegionText;
29911
29919
  if (typeof resolvedValue === "string" && resolvedValue !== "") {
29912
29920
  const requested = canonicalizeRegion(resolvedValue);
29913
- if (!isClientSafeRegion(requested)) throw new Error(`Fn::GetAZs: '${this.maskThenStripThenMask(this.logTextOfLeaf(resolvedValue, context) !== resolvedValue ? "***" : resolvedValue, context).slice(0, 64)}' is not a valid AWS region name. A region is substituted into the AWS service hostname, so cdkd will not build a client from it.`);
29921
+ if (!isClientSafeRegion(requested)) throw new Error(`Fn::GetAZs: '${this.displayMasked(this.logTextOfLeaf(resolvedValue, context) !== resolvedValue ? "***" : resolvedValue, context).slice(0, 64)}' is not a valid AWS region name. A region is substituted into the AWS service hostname, so cdkd will not build a client from it.`);
29914
29922
  region = requested;
29915
29923
  clientRegion = requested;
29916
29924
  loggedRegionText = this.regionLogText(resolvedValue, context);
@@ -29920,7 +29928,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29920
29928
  }
29921
29929
  const cached = cachedAvailabilityZones[region];
29922
29930
  if (cached) {
29923
- this.logger.debug(`Resolved Fn::GetAZs from cache: ${this.maskSecretsForLog(loggedRegionText ?? region, context)} -> ${JSON.stringify(this.maskValueLeaves(cached, context))}`);
29931
+ this.logger.debug(`Resolved Fn::GetAZs from cache: ${this.displayMasked(loggedRegionText ?? region, context)} -> ${JSON.stringify(this.maskValueLeaves(cached, context))}`);
29924
29932
  return cached;
29925
29933
  }
29926
29934
  const ec2Client = this.clientsForRegion(clientRegion, loggedRegionText === void 0 ? void 0 : this.maskSecretsForLog(loggedRegionText, context)).ec2;
@@ -29934,11 +29942,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29934
29942
  Values: ["available"]
29935
29943
  }] }))).AvailabilityZones || []).map((az) => az.ZoneName).filter((name) => name !== void 0).sort();
29936
29944
  } catch (error) {
29937
- throw new Error(`Fn::GetAZs: failed to describe availability zones for region '${this.maskSecretsForLog(loggedRegionText ?? region, context)}': ${this.maskSecretsForLog(error instanceof Error ? error.message : String(error), context)}`);
29945
+ throw new Error(`Fn::GetAZs: failed to describe availability zones for region '${this.displayMasked(loggedRegionText ?? region, context)}': ${this.displayMasked(error instanceof Error ? error.message : String(error), context)}`);
29938
29946
  }
29939
- if (azNames.length === 0) throw new Error(`Fn::GetAZs: no availability zones returned for region '${this.maskSecretsForLog(loggedRegionText ?? region, context)}'. Either the region is not enabled on this account (opt-in regions must be enabled before use), or the request was answered by a different region's endpoint.`);
29947
+ if (azNames.length === 0) throw new Error(`Fn::GetAZs: no availability zones returned for region '${this.displayMasked(loggedRegionText ?? region, context)}'. Either the region is not enabled on this account (opt-in regions must be enabled before use), or the request was answered by a different region's endpoint.`);
29940
29948
  cachedAvailabilityZones[region] = azNames;
29941
- this.logger.debug(`Resolved Fn::GetAZs: ${this.maskSecretsForLog(loggedRegionText ?? region, context)} -> ${JSON.stringify(this.maskValueLeaves(azNames, context))}`);
29949
+ this.logger.debug(`Resolved Fn::GetAZs: ${this.displayMasked(loggedRegionText ?? region, context)} -> ${JSON.stringify(this.maskValueLeaves(azNames, context))}`);
29942
29950
  return azNames;
29943
29951
  }
29944
29952
  /**
@@ -30406,6 +30414,100 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30406
30414
  maskThenStripThenMask(value, context) {
30407
30415
  return this.maskSecretsForLog(stripControlChars(this.maskSecretsForLog(value, context)), context);
30408
30416
  }
30417
+ /**
30418
+ * The ONE way a masked value reaches a message in this file.
30419
+ *
30420
+ * ## Why this exists rather than a rule about call sites
30421
+ *
30422
+ * `maskSecretsForLog` answers "does this text contain a recorded secret". It
30423
+ * makes NO claim about control characters, and it cannot be taught to: its
30424
+ * result is also used as a COMPARISON (`maskNeedlesForLog(text) !== text`
30425
+ * decides whether a twin was registered), so stripping inside it would change
30426
+ * what that comparison means at every call site.
30427
+ *
30428
+ * That split produced a four-round treadmill on go-to-k/cdkd#3408. Round 1
30429
+ * found the four `Fn::GetStackOutput` throws rendering a masked name raw and
30430
+ * fixed them. Round 2 found the CloudFormation-fallback warn — the DEFAULT
30431
+ * path, at DEFAULT verbosity — and the self-reference throw. Round 3 found
30432
+ * the `reresolveCrossStackValue` origin builder and
30433
+ * `describeAvailableOutputs`. Each round fixed what it found and the next
30434
+ * round found more, because the population was never enumerated. Measured
30435
+ * 2026-09-19 with a comment-stripped scan: SEVENTY-ONE interpolations
30436
+ * rendered a `maskSecretsForLog` result directly, plus thirteen hand-spelled
30437
+ * `displaySafe(maskThenStripThenMask(...))` compositions and two bare
30438
+ * `maskThenStripThenMask` interpolations — and three reviewers between them
30439
+ * reached eight of the eighty-six.
30440
+ *
30441
+ * So the repair is not another site. Interpolating the MASKER is now itself
30442
+ * the defect, and this is the only sanctioned way to render one — which turns
30443
+ * "did someone remember to strip here" into a property one scanner checks
30444
+ * over the whole file at once.
30445
+ * `tests/unit/deployment/resolver-display-masked-population.test.ts` is that
30446
+ * scanner: it fails on any `${this.maskSecretsForLog(...)}`, so a new raw
30447
+ * render in THAT SHAPE cannot be added silently.
30448
+ *
30449
+ * ## KNOWN RESIDUAL — the class is NARROWED, not closed
30450
+ *
30451
+ * Stated here rather than implied away, because an earlier revision of this
30452
+ * comment claimed the class was "closed by CONSTRUCTION" and that is FALSE.
30453
+ * The scanner is line-shaped and forbids only the `${this.<masker>(` spelling.
30454
+ * TEN local bindings take a raw masker result and interpolate it later, which
30455
+ * it cannot see — and at least one is a live exposure:
30456
+ * `loggedExportName` (bound from `maskSecretsForLog`) reaches an
30457
+ * `Fn::ImportValue` throw carrying an unstripped ESC and CR, measured on this
30458
+ * tree. `loggedSecretId`, `loggedJsonKey`, `loggedParameterName`,
30459
+ * `maskedAttributeName` and `loggedOrigin` are the same shape.
30460
+ *
30461
+ * Do NOT close those by hand at the ten sites: that is the fifth round of the
30462
+ * enumeration this builder exists to end, and the mechanism that resolves an
30463
+ * identifier to its initializer ALREADY EXISTS in this repo —
30464
+ * `sanitizedLocals` in
30465
+ * `tests/unit/cli/local-profile-display-population.test.ts`.
30466
+ * go-to-k/cdkd#3426 owns binding the scanner to it; go-to-k/cdkd#3405 and
30467
+ * go-to-k/cdkd#3411 own the surrounding scope and parser work.
30468
+ *
30469
+ * ## What it does, in order
30470
+ *
30471
+ * `maskThenStripThenMask` first — mask, strip, mask — because
30472
+ * `stripControlChars` DELETES, so a plaintext split by an invisible would be
30473
+ * reconstituted contiguous by a strip applied after a single mask. Then
30474
+ * `displaySafe`, which covers the class `stripControlChars` does not:
30475
+ * `U+2028` / `U+2029` (line terminators to JSON and web log viewers) and the
30476
+ * Trojan-Source bidi overrides. That composition is what the `physicalId`
30477
+ * renders here already spelled by hand; this names it, and they now share it.
30478
+ *
30479
+ * It is listed in `MASKERS` in `scripts/check-resolver-mask-coverage.ts`,
30480
+ * which is a SECURITY decision and a sound one: every path through it passes
30481
+ * `maskSecretsForLog`, so it is strictly stronger than the entry it replaces
30482
+ * and cannot be weaker at any input.
30483
+ */
30484
+ displayMasked(value, context) {
30485
+ return displaySafe(this.maskThenStripThenMask(value, context));
30486
+ }
30487
+ /**
30488
+ * The display form of a LOG-TWIN leaf — the second render route, named for
30489
+ * the same reason as {@link displayMasked} and closed by the same scanner.
30490
+ *
30491
+ * `logTextOfLeaf` resolves a value to its registered twin (or `SECRET_MASK`),
30492
+ * which is a MASKING answer and, like `maskSecretsForLog`'s, says nothing
30493
+ * about control characters. Four `origin` builders interpolated it raw — the
30494
+ * `Fn::ImportValue` pair, the `Fn::GetStackOutput` one and the nested-stack
30495
+ * attribute one — and those strings flow into `redactedAttributeReads[].display`
30496
+ * and out through a `ProvisioningError` message.
30497
+ *
30498
+ * This route is why fixing the masker alone did not close the class:
30499
+ * `displayMasked` covers every site that interpolates `maskSecretsForLog`,
30500
+ * and these interpolate something else. Two routes, two names, one scanner
30501
+ * forbidding the raw form of both.
30502
+ *
30503
+ * Composed rather than re-derived. `maskSecretsInText` is idempotent (see
30504
+ * {@link maskThenStripThenMask}), so masking twin text again costs nothing
30505
+ * and cannot change the twin — which keeps this a STRICT addition of the
30506
+ * strip and the `displaySafe` pass, with the twin resolution untouched.
30507
+ */
30508
+ displayLeaf(value, context) {
30509
+ return this.displayMasked(this.logTextOfLeaf(value, context), context);
30510
+ }
30409
30511
  async resolveDynamicReferences(value, context) {
30410
30512
  return (await this.resolveDynamicReferencesWithLogTwin(value, value, context)).result;
30411
30513
  }
@@ -30455,7 +30557,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30455
30557
  continue;
30456
30558
  }
30457
30559
  const regionVerdict = classifyReplaySecretRegion(fullMatch, this.explicitRegion ?? this.resolverRegion, context?.producerRegions);
30458
- if (regionVerdict.kind === "ambiguous") throw markNonRetryable(new DynamicReferenceRegionAmbiguousError(`Refusing to resolve the secret reference ${this.maskSecretsForLog(tokenLogText, context)}: it names '${this.maskSecretsForLog(nameLogText(regionVerdict.secretName), context)}' without a region, and this stack reads from ${this.maskSecretsForLog(regionVerdict.foreignProducerRegions.map((r) => this.maskSecretsForLog(r, context)).join(", "), context)} 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.`));
30560
+ if (regionVerdict.kind === "ambiguous") throw markNonRetryable(new DynamicReferenceRegionAmbiguousError(`Refusing to resolve the secret reference ${this.displayMasked(tokenLogText, context)}: it names '${this.displayMasked(nameLogText(regionVerdict.secretName), context)}' without a region, and this stack reads from ${this.displayMasked(regionVerdict.foreignProducerRegions.map((r) => this.maskSecretsForLog(r, context)).join(", "), context)} 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.`));
30459
30561
  if (regionVerdict.kind === "named-region") {
30460
30562
  const foreign = await this.resolverForProducerRegion(regionVerdict.region, context, nameLogText(regionVerdict.region)).resolveDynamicReferencesWithLogTwin(fullMatch, fullMatch, withoutProducerRegions(context), {
30461
30563
  tokenLogText,
@@ -30520,11 +30622,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30520
30622
  resolved = param.value;
30521
30623
  } else if (service === "ssm-secure") {
30522
30624
  const param = await this.resolveSSMReference(parts, true, "ssm-secure", context, nameLogText);
30523
- if (!param.secure) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to resolve ${this.maskSecretsForLog(tokenLogText, context)}: 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.`));
30625
+ if (!param.secure) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to resolve ${this.displayMasked(tokenLogText, context)}: 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.`));
30524
30626
  isSecret = true;
30525
30627
  resolved = param.value;
30526
30628
  } else {
30527
- this.logger.warn(this.maskSecretsForLog(`Unsupported dynamic reference service: ${this.maskSecretsForLog(nameLogText(String(service)), context)}`, context));
30629
+ this.logger.warn(this.maskSecretsForLog(`Unsupported dynamic reference service: ${this.displayMasked(nameLogText(String(service)), context)}`, context));
30528
30630
  complete = false;
30529
30631
  continue;
30530
30632
  }
@@ -30614,7 +30716,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30614
30716
  if (!secretId) throw new Error("Dynamic reference: secretsmanager SECRET_ID is required");
30615
30717
  const loggedSecretId = this.maskSecretsForLog(nameLogText(secretId), context);
30616
30718
  const loggedJsonKey = this.maskSecretsForLog(nameLogText(jsonKey), context);
30617
- this.logger.debug(`Resolving dynamic reference: secretsmanager:${loggedSecretId}:SecretString:${loggedJsonKey}:${this.maskSecretsForLog(nameLogText(versionStage), context)}:${this.maskSecretsForLog(nameLogText(versionId), context)}`);
30719
+ this.logger.debug(`Resolving dynamic reference: secretsmanager:${loggedSecretId}:SecretString:${loggedJsonKey}:${this.displayMasked(nameLogText(versionStage), context)}:${this.displayMasked(nameLogText(versionId), context)}`);
30618
30720
  const client = this.clientsForRegion(this.explicitRegion).secretsManager;
30619
30721
  const command = new GetSecretValueCommand({
30620
30722
  SecretId: secretId,
@@ -30656,7 +30758,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30656
30758
  const count = Number(await this.resolveValue(rawCount, context));
30657
30759
  const cidrBits = Number(await this.resolveValue(rawCidrBits, context));
30658
30760
  if (!ipBlock || typeof ipBlock !== "string") throw new Error(`Fn::Cidr: ipBlock must be a string, got ${typeof ipBlock}: ${JSON.stringify(this.maskValueLeaves(ipBlock, context))}`);
30659
- this.logger.debug(`Resolving Fn::Cidr: ipBlock=${this.maskSecretsForLog(JSON.stringify(this.maskValueLeaves(ipBlock, context)), context)}, count=${count}, cidrBits=${cidrBits}`);
30761
+ this.logger.debug(`Resolving Fn::Cidr: ipBlock=${this.displayMasked(JSON.stringify(this.maskValueLeaves(ipBlock, context)), context)}, count=${count}, cidrBits=${cidrBits}`);
30660
30762
  const isIpv6 = ipBlock.includes(":");
30661
30763
  const results = [];
30662
30764
  if (isIpv6) {
@@ -30688,7 +30790,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30688
30790
  results.push(`${a}.${b}.${c}.${d}/${subnetPrefix}`);
30689
30791
  }
30690
30792
  }
30691
- this.logger.debug(`Fn::Cidr result: ${this.maskSecretsForLog(JSON.stringify(this.maskValueLeaves(results, context)), context)}`);
30793
+ this.logger.debug(`Fn::Cidr result: ${this.displayMasked(JSON.stringify(this.maskValueLeaves(results, context)), context)}`);
30692
30794
  return results;
30693
30795
  }
30694
30796
  /** Expand IPv6 address to full 8-group form */
@@ -32476,7 +32578,7 @@ var CloudControlProvider = class {
32476
32578
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
32477
32579
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
32478
32580
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
32479
- const { ASGProvider } = await import("./asg-provider-DiZsq-4k.js").then((n) => n.n);
32581
+ const { ASGProvider } = await import("./asg-provider-De5KONKr.js").then((n) => n.n);
32480
32582
  const asgProvider = new ASGProvider();
32481
32583
  return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
32482
32584
  }
@@ -40443,6 +40545,34 @@ function retainedSurvivorMessages(logicalId, resourceType, survivorPhysicalId, s
40443
40545
  reason: `UpdateReplacePolicy: Retain kept the replacement's new ${resourceType} (${survivorPhysicalId}); it is live, still billing, and no longer tracked by cdkd. ${stateClause}`
40444
40546
  };
40445
40547
  }
40548
+ function requireRestorableBaseline(bag, logger, { logicalId, consequence, remedy, retry }) {
40549
+ if (isRestorableBag(bag)) return true;
40550
+ if (bag === void 0) {
40551
+ logger.warn(` Rollback: Cannot restore ${safe(logicalId)} \u2014 its recorded previous state has no \`properties\` bag, so there is nothing to restore it to. An empty desired state would ${consequence}. The resource is therefore left exactly as it is. ${remedy}`);
40552
+ return false;
40553
+ }
40554
+ const shape = bag === null ? "null" : Array.isArray(bag) ? "an array" : `a ${typeof bag}`;
40555
+ throw new CdkdError(`Cannot roll ${safe(logicalId)} back: its recorded previous state has a \`properties\` field that is not a property bag (${shape}), so cdkd cannot tell what to restore it to. Replaying it would do one of two things, and cdkd does neither: send the malformed value to the provider as-is, or send an empty desired state (which would ${consequence}). The rollback JOURNAL record is kept: once that record holds a property bag again, ${retry}. Otherwise fix forward with \`cdkd deploy\`, or remove the stack with \`cdkd destroy\`.`, "ROLLBACK_UNUSABLE_BASELINE");
40556
+ }
40557
+ /**
40558
+ * Is this a desired bag a replay can actually restore TO?
40559
+ *
40560
+ * Absent is the shape issue #3203 started from, but `=== undefined` is the
40561
+ * WRONG test and a 0-red mutation row is what said so: `null ?? {}` is `{}`,
40562
+ * so a `null` bag reaches the provider as an empty desired state exactly as an
40563
+ * absent one does. A non-object bag (`"abc"`, `[]`) is worse still -- it goes
40564
+ * to the provider VERBATIM. go-to-k/cdkd#3149 refuses those shapes for a
40565
+ * JOURNAL-sourced record at the parser, but `previousState` is equally often
40566
+ * STATE-sourced, and `parseStateBody` deliberately validates no inner shape
40567
+ * (go-to-k/cdkd#2947's placement decision), so this is the only boundary that
40568
+ * sees them on that path.
40569
+ *
40570
+ * A PRESENT but empty `{}` is restorable and must pass: the operator recorded
40571
+ * a resource that really has no properties. Only "no usable bag" is refused.
40572
+ */
40573
+ function isRestorableBag(bag) {
40574
+ return typeof bag === "object" && bag !== null && !Array.isArray(bag);
40575
+ }
40446
40576
  /**
40447
40577
  * `DeletionPolicy: Snapshot` on a rolled-back CREATE (issue #1358) — the
40448
40578
  * executor's copy of the deploy engine's `prepareFinalSnapshotForDelete`
@@ -41331,6 +41461,15 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
41331
41461
  case "reverse-replacement": {
41332
41462
  const current = stateResources[op.logicalId];
41333
41463
  const prev = op.previousState;
41464
+ if (!requireRestorableBaseline(prev.properties, logger, {
41465
+ logicalId: op.logicalId,
41466
+ consequence: "create a default-configured resource and then delete the live one",
41467
+ remedy: "Re-run `cdkd deploy` to re-converge it.",
41468
+ retry: "re-running `cdkd rollback` retries this op"
41469
+ })) {
41470
+ result.warnings++;
41471
+ return;
41472
+ }
41334
41473
  const resolvedPrevProps = await resolveReplayProps(prev.properties, resolver, secrets, ctx, op.logicalId) ?? {};
41335
41474
  refuseMaskedReplayBaseline(resolvedPrevProps, op.logicalId);
41336
41475
  recordNestedStackParameterExpressions(secrets, op.resourceType, resolvedPrevProps, prev.properties, STATE_DERIVED_RULES);
@@ -41436,6 +41575,15 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
41436
41575
  result.warnings++;
41437
41576
  return;
41438
41577
  }
41578
+ if (!requireRestorableBaseline(previousState.properties, logger, {
41579
+ logicalId: op.logicalId,
41580
+ consequence: "be applied as a complete desired state: a patch provider removes every property, and an SDK provider may reset a subset or replace the resource",
41581
+ remedy: "Re-run `cdkd deploy` to re-converge it.",
41582
+ retry: "re-running `cdkd rollback` retries this op"
41583
+ })) {
41584
+ result.warnings++;
41585
+ return;
41586
+ }
41439
41587
  logger.info(` Rollback: Restoring ${safe(op.logicalId)} (${safe(op.resourceType)}) to previous state`);
41440
41588
  const { provider } = ctx.providerRegistry.getProviderFor({
41441
41589
  resourceType: op.resourceType,
@@ -41605,6 +41753,15 @@ async function replayFailedOperations(failedOps, stateResources, stackName, ctx,
41605
41753
  case "revert-failed-update": {
41606
41754
  const current = stateResources[op.logicalId];
41607
41755
  const prev = op.previousState;
41756
+ if (!requireRestorableBaseline(prev.properties, logger, {
41757
+ logicalId: op.logicalId,
41758
+ consequence: "be applied as a complete desired state: a patch provider removes every property, and an SDK provider may reset a subset or replace the resource",
41759
+ remedy: "Inspect it with `cdkd drift` (this op died mid-flight, so its remote state is unknown) and re-converge with `cdkd drift --revert` or `cdkd deploy`.",
41760
+ retry: "re-running `cdkd rollback --revert-failed` retries this op (a plain `cdkd rollback` replays only the COMPLETED ops and then pops the whole segment, discarding this record)"
41761
+ })) {
41762
+ result.warnings++;
41763
+ break;
41764
+ }
41608
41765
  logger.info(` Rollback: force-reverting failed UPDATE of ${safe(op.logicalId)} (${safe(op.resourceType)}) to its pre-deploy properties (--revert-failed; remote state is unknown)`);
41609
41766
  const { provider } = ctx.providerRegistry.getProviderFor({
41610
41767
  resourceType: op.resourceType,
@@ -45488,4 +45645,4 @@ var DeployEngine = class DeployEngine {
45488
45645
 
45489
45646
  //#endregion
45490
45647
  export { secretBearing as $, SynthesisError as $i, recordMaskOnlyValue as $n, AssetManifestLoader as $r, s3BucketArn as $t, extractDeploymentEventError as A, getAwsClients as Ai, refuseMalformedState as An, BOOTSTRAP_MARKER_PREFIX as Ar, isImpossibleEmptyStoredAttribute as At, toCfnInteger as B, IntrinsicResolutionRefusalError as Bi, STATE_SOURCED_BASELINE_RULES as Bn, buildDockerImage as Br, coerceCfnBoolean as Bt, buildFinalSnapshotIdentifier as C, canonicalizeRegion as Ci, producerRecordKey as Cn, stringifyValue as Cr, isTerminationProtectionPropagationError as Ct, refusesFinalSnapshot as D, clearBucketRegionCache as Di, refuseMalformedOutputsForDestroy as Dn, loadPublishableAssetManifest as Dr, cfnRefValueFromPhysicalId as Dt, isFinalSnapshotError as E, processStackMessages as Ei, refuseMalformedOutputs as En, createAssetRedirectResolver as Er, carriesFetchableDynamicReference as Et, isStatefulRecreateTargetSync as F, ConfigError as Fi, findSilentDropProperties as Fn, parseBootstrapMarker as Fr, isSettledInstanceState as Ft, gray as G, NestedStackChildDirectDestroyError as Gi, createSecretMasker as Gn, formatDockerLoginError as Gr, readConfigString as Gt, formatResourceLine as H, LocalStartServiceError as Hi, STATE_SOURCED_READBACK_RULES as Hn, describeDockerExecFailure as Hr, configBooleanRefusal as Ht, renderStatefulReason as I, CrossAccountSecretRefusalError as Ii, describeTypeWithThrottleRetry as In, readBootstrapMarkerBody as Ir, WAFv2WebACLProvider as It, yellow as J, ResourceTimeoutError as Ji, identityKeyFor as Jn, redactDockerArgvValues as Jr, requireConfigObject as Jt, green as K, PartialFailureError as Ki, dynamicReferenceTokens as Kn, getDockerCmd as Kr, replayWarn as Kt, WARM_THROUGHPUT_MEMBERS as L, DependencyError as Li, withRetry as Ln, validateAssetBucketName as Lr, normalizeAwsTagsToCfn as Lt, planOrphanAdoption as M, setAwsClients as Mi, repairMalformedResourcePropertiesForReadOnly as Mn, ensureAssetStorage as Mr, isUnboundTemplateParameter as Mt, MULTI_REGION_RECREATE_BLOCKED_TYPES as N, AssetError as Ni, repairMalformedResourcesForReadOnly as Nn, getBootstrapMarkerKey as Nr, parameterTypeMayLoseSecretIdentity as Nt, unsupportedFinalSnapshotError as O, resolveBucketRegion as Oi, refuseMalformedResourcePropertiesForOrphan as On, rewriteTemplateAssetReferences as Or, coerceParameterTypedValue as Ot, isStatefulRecreateTargetForReplace as P, CdkdError as Pi, findActionableSilentDrops as Pn, isCrossRegionRedirect as Pr, refStateLookupFromResource as Pt, isExportAliasCollision as Q, StateError as Qi, maskSecretsInText as Qn, stripControlChars as Qr, withSharedDrainBudget as Qt, coerceWarmThroughput as R, DeployCancelledError as Ri, DagBuilder as Rn, validateContainerRepoName as Rr, resolveExplicitPhysicalId as Rt, PRE_DELETE_SNAPSHOT_TYPES as S, PARTITION_TABLE as Si, malformedStateRefusalMessage as Sn, AssetPublisher as Sr, disableInstanceApiTermination as St, createPreDeleteFinalSnapshot as T, AssemblyReader as Ti, refuseMalformedNestedChildOutputs as Tn, buildAssetRedirectMap as Tr, carriesDynamicReference as Tt, bold as U, LockError as Ui, TEMPLATE_SOURCED_RULES as Un, describeDockerFailure as Ur, configIntegerRefusal as Ut, toFiniteNumber as V, LocalInvokeBuildError as Vi, STATE_SOURCED_CROSS_GENERATION_RULES as Vn, describeDockerCapturedOutput as Vr, coerceCfnInteger as Vt, cyan as W, MalformedProducerRecordRefusalError as Wi, carriesSecretMask as Wn, dockerSpawnEnvWithSensitive as Wr, configStringRefusal as Wt, collectPublishedOutputNames as X, StackHasActiveImportsError as Xi, markSameGenerationBag as Xn, runDockerStreaming as Xr, classifyReplaySecretRegion as Xt, collectDeclaredOutputNames as Y, ResourceUpdateNotSupportedError as Yi, isSingleDynamicReferenceToken as Yn, runDockerForeground as Yr, requireConfigString as Yt, exportAliasCollisionScrubWarning as Z, StackTerminationProtectionError as Zi, maskSecretsInError as Zn, escapeRegExp$1 as Zr, producerRegionsFromState as Zt, isSecretBearingReferenceString as _, findLargeInlineResources as _i, malformedOutputsRefusalMessage as _n, importableOutputs as _r, isWaitAbandonedError as _t, DeploymentEventsStore as a, isRetryableTransientError as aa, resolveApp as ai, AWS_CREDENTIAL_ENV_KEYS as an, rebuildClientForBucketRegion as ar, clearOnUpdateRemoval as at, bindingSkippedOutputs as b, decodeListingKey as bi, malformedResourcePropertiesWarning as bn, orphansCarriedFrom as br, deleteIndeterminateGuards as bt, replayFailedOperations as c, markNonRetryable as ca, resolveSkipPrefix as ci, envCredentialsAreAssumedRole as cn, buildLockContentionMessage as cr, createMaskedRetryLogger as ct, updatePartialReason as d, describeAwsFailure as da, resolveUseCdkBootstrapAssets as di, STATE_RESOURCES_MALFORMED as dn, shellQuote as dr, beginCommandInterruptScope as dt, formatError as ea, getDockerImageBySourceHash as ei, s3BucketDomainName as en, recoverMaskedOutput as er, secretBearingStateKeyWarning as et, withResourceDeadline as f, safeStringify$1 as fa, stateBucketExistenceConfirmed as fi, hasReadableOutputs as fn, CUSTOM_RESOURCE_RESPONSE_PREFIX as fr, endCommandInterruptScope as ft, bagHoldsSecretExpression as g, MIGRATE_TMP_PREFIX as gi, malformedLocalOutputsWarning as gn, importableOutputKeys as gr, CloudControlProvider as gt, computeImplicitDeleteEdges as h, CFN_TEMPLATE_URL_LIMIT as hi, malformedExportSourceWarning as hn, hasReadableExportSet as hr, startInterruptWatch as ht, DeploymentEventsReader as i, isMarkedNonRetryable as ia, getLegacyStateBucketName as ii, applyRoleArnIfSet as in, S3StateBackend as ir, collectInlinePolicyNamesManagedBySiblings as it, makeSiblingClaimReader as j, resetAwsClients as ji, repairMalformedOutputsForReadOnly as jn, assertAssetBucketRegion as jr, isNamelessDynamicReferenceError as jt, makeCanonicalizePropertiesFn as k, AwsClients as ki, refuseMalformedResourcesForDestroy as kn, AssetModeResolver as kr, getAccountInfo as kt, replayRollback as l, markRedactedCause as la, resolveStateBucketWithDefault as li, DiffCalculator as ln, forceQuitRecoveryClause as lr, maskDeep as lt, IMPLICIT_DELETE_DEPENDENCIES as m, CFN_TEMPLATE_BODY_LIMIT as mi, malformedExportNamesWarning as mn, exportNamesCarriedFrom as mr, isInterruptedWaitError as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, normalizeAwsError as na, synthesisStatusMessage as ni, s3BucketRegionalDomainName as nn, scrubResourceRecord as nr, getCurrentResourceSecrets as nt, planFailedOps as o, isThrottlingError as oa, resolveAutoAssetStorage as oi, applyCallerIdentityCredentials as on, UNREPRODUCIBLE_LOCK_CLAUSE as or, ProviderRegistry as ot, maskingRetryLogger as p, __exportAll as pa, warnDeprecatedNoPrefixCliFlag as pi, hasReadableResources as pn, DEFAULT_STATE_PREFIX as pr, interruptWatchListenerCount as pt, red as q, ProvisioningError as qi, errorCauseChain as qn, partitionSensitiveEnv as qr, requireConfigArray as qt, DeployEngine as r, withErrorHandling as ra, getDefaultStateBucketName as ri, s3BucketWebsiteUrl as rn, LockManager as rr, IAMRoleProvider as rt, planRollback as s, isTransientServerError as sa, resolveCaptureObservedState as si, callerEnvCredentials as sn, buildForceUnlockCommand as sr, wouldReturnToSdkProvider as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, isCdkdError as ta, Synthesizer as ti, s3BucketDualStackDomainName as tn, redactSecretsForState as tr, secretSafeKeyDisplay as tt, updatePartialMessage as u, retryClassificationText as ua, resolveStateBucketWithDefaultAndSource as ui, INTRINSIC_KEYS as un, formatLockExpiry as ur, maskerOrIdentity as ut, keptWholeReasonText as v, uploadCfnTemplate as vi, malformedOutputsWarning as vn, isReadableBag as vr, slowCcOperationTimeoutMs as vt, ccRoutedFinalSnapshotError as w, derivePartitionAndUrlSuffix as wi, refuseDivergentRecordRegionForDestroy as wn, WorkGraph as wr, IntrinsicFunctionResolver as wt, ATOMIC_FINAL_SNAPSHOT_TYPES as x, expectedOwnerParam as xi, malformedResourcesWarning as xn, shouldRetainResource as xr, deleteSkipReason as xt, mergeNoChangeOutputs as y, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as yi, malformedRenderedContainersWarning as yn, orphansAfterRollback as yr, UNSPECIFIED_SKIP_REASON as yt, isWarmThroughputDecrease as z, DynamicReferenceRegionAmbiguousError as zi, TemplateParser as zn, buildDenyExternalAccessPolicy as zr, assertRegionMatch as zt };
45491
- //# sourceMappingURL=deploy-engine-CcFbq2q8.js.map
45648
+ //# sourceMappingURL=deploy-engine-mI1Xv1oi.js.map