@go-to-k/cdkd 0.291.16 → 0.291.18

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 displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, 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-GASr9bAF.js";
1
+ import { C as displayAwsMessage, D as isPasteableIdent, E as displayStackName, O as truncateCodePoints, S as UNRENDERABLE, T as displaySafe, d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS, y as withoutGeneratedFallbackName } from "./logger-Bt0RGDfb.js";
2
2
  import { a as setAssumedRoleCredentials, c as currentStackAwsScope, i as getPreAssumeEnvCredentials, l as runInStackAwsScope, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, s as ambientRegion, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-jISU4PHo.js";
3
- import { t as getCdkdVersion } from "./version-Qigb_6I0.js";
3
+ import { t as getCdkdVersion } from "./version-CJkb2usU.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";
@@ -35,10 +35,9 @@ import { GetHostedZoneCommand, ListHostedZonesByNameCommand, Route53Client } fro
35
35
  import { DescribeListenersCommand, DescribeLoadBalancersCommand, ElasticLoadBalancingV2Client } from "@aws-sdk/client-elastic-load-balancing-v2";
36
36
  import { KMSClient, ListAliasesCommand } from "@aws-sdk/client-kms";
37
37
  import { readFile } from "fs/promises";
38
- import { isAbsolute as isAbsolute$1, relative, resolve as resolve$1, sep } from "path";
38
+ import { isAbsolute as isAbsolute$1, resolve as resolve$1 } from "path";
39
39
  import { CreateRepositoryCommand, DescribeImagesCommand as DescribeImagesCommand$1, DescribeRepositoriesCommand, ECRClient, GetAuthorizationTokenCommand, PutImageTagMutabilityCommand } from "@aws-sdk/client-ecr";
40
40
  import { inspect } from "node:util";
41
- import { realpathSync as realpathSync$1 } from "fs";
42
41
  import { hostname } from "os";
43
42
  import graphlib from "graphlib";
44
43
  import { DescribeDBClustersCommand, DescribeDBInstancesCommand, RDSClient } from "@aws-sdk/client-rds";
@@ -3365,7 +3364,9 @@ function assemblyPathEscape(base, bound, candidate) {
3365
3364
  * Modifier Letters block (U+02BA reads as `"`, U+02BC as `'`) plus the ones
3366
3365
  * outside it (U+0374, U+0559, U+07F4-U+07F5, U+A78B-U+A78C, and the halfwidth
3367
3366
  * sound marks U+FF9E-U+FF9F). Not all of `\p{Lm}`: U+30FC is in it, and it is
3368
- * an ordinary character of a Japanese directory name.
3367
+ * an ordinary character of a Japanese directory name. `classify` tests
3368
+ * {@link DEFAULT_IGNORABLE} first, so that carve-out is a backstop here, kept
3369
+ * so the class matches cdk-local's `displayUntrustedValue`.
3369
3370
  */
3370
3371
  const VISIBLE_LETTER = new RegExp(String.raw`^(?![\p{Default_Ignorable_Code_Point}\u02b0-\u02ff\u0374\u0559\u07f4\u07f5\ua78b\ua78c\uff9e\uff9f])[\p{L}\p{N}]$`, "u");
3371
3372
  /**
@@ -3375,6 +3376,14 @@ const VISIBLE_LETTER = new RegExp(String.raw`^(?![\p{Default_Ignorable_Code_Poin
3375
3376
  */
3376
3377
  const COMBINING_MARK = /^\p{M}$/u;
3377
3378
  /**
3379
+ * A default-ignorable code point, which draws as NOTHING. Tested before the
3380
+ * mark rule, because some are combining marks (U+034F, U+17B4, U+180B, the
3381
+ * variation selectors U+FE00-U+FE0F and U+E0100-U+E01EF): after a letter they
3382
+ * would otherwise pass as bare, and `/home/me/.ss\u034fh` would print exactly
3383
+ * like `.ssh` while naming a different path (go-to-k/cdkd#3656).
3384
+ */
3385
+ const DEFAULT_IGNORABLE = /^\p{Default_Ignorable_Code_Point}$/u;
3386
+ /**
3378
3387
  * The ASCII a bare path may carry besides letters and digits. Everything else —
3379
3388
  * any whitespace, any quote, any other symbol — takes the boundary, which
3380
3389
  * costs a legitimate path nothing but a pair of quotes.
@@ -3394,6 +3403,10 @@ const BARE_PUNCTUATION = /^[/\\._~+@:=-]$/;
3394
3403
  function classify(chars) {
3395
3404
  let afterLetter = false;
3396
3405
  return chars.map((ch) => {
3406
+ if (DEFAULT_IGNORABLE.test(ch)) {
3407
+ afterLetter = false;
3408
+ return "escaped";
3409
+ }
3397
3410
  if (COMBINING_MARK.test(ch)) return afterLetter ? "bare" : "escaped";
3398
3411
  afterLetter = VISIBLE_LETTER.test(ch);
3399
3412
  if (afterLetter || BARE_PUNCTUATION.test(ch)) return "bare";
@@ -3944,7 +3957,7 @@ var AssemblyReader = class {
3944
3957
  getStack(assemblyDir, manifest, stackName) {
3945
3958
  const { stacks, failedStages } = this.readAssembly(assemblyDir, manifest);
3946
3959
  const stack = stacks.find((s) => s.stackName === stackName);
3947
- if (!stack) throw new SynthesisError(`Stack '${displaySafe(stackName)}' not found in assembly. ` + (stacks.length > 0 ? `Available: ${stacks.map((s) => displayIdent(s.stackName, { maxCodePoints: STACK_REF_MAX_CODE_POINTS })).join(", ")}` : "The assembly has no stacks") + failedStageNote([stackName], failedStages));
3960
+ if (!stack) throw new SynthesisError(`Stack ${displayStackName(stackName)} not found in assembly. ` + (stacks.length > 0 ? `Available: ${stacks.map((s) => displayIdent(s.stackName, { maxCodePoints: STACK_REF_MAX_CODE_POINTS })).join(", ")}` : "The assembly has no stacks") + failedStageNote([stackName], failedStages));
3948
3961
  return stack;
3949
3962
  }
3950
3963
  /**
@@ -3964,7 +3977,7 @@ var AssemblyReader = class {
3964
3977
  const props = artifact.properties;
3965
3978
  if (props?.file) {
3966
3979
  const resolved = resolveAssemblyPath(assemblyDir, props.file);
3967
- if (!resolved.contained) throw new SynthesisError(`Asset manifest artifact '${displaySafe(artifactId)}' has file=${displayAssemblyPath(props.file)} which ${renderAssemblyPathEscape(resolved, assemblyDir)}`);
3980
+ if (!resolved.contained) throw new SynthesisError(`Asset manifest artifact ${displayStackName(artifactId)} has file=${displayAssemblyPath(props.file)} which ${renderAssemblyPathEscape(resolved, assemblyDir)}`);
3968
3981
  map.set(artifactId, resolved.path);
3969
3982
  }
3970
3983
  }
@@ -3977,16 +3990,16 @@ var AssemblyReader = class {
3977
3990
  const props = artifact.properties;
3978
3991
  const stackName = props?.stackName || artifactId;
3979
3992
  const templateFile = props?.templateFile;
3980
- if (!templateFile) throw new SynthesisError(`Stack '${displaySafe(stackName)}' has no templateFile property`);
3993
+ if (!templateFile) throw new SynthesisError(`Stack ${displayStackName(stackName)} has no templateFile property`);
3981
3994
  const resolvedTemplate = resolveAssemblyPath(assemblyDir, templateFile);
3982
- if (!resolvedTemplate.contained) throw new SynthesisError(`Stack '${displaySafe(stackName)}' has templateFile=${displayAssemblyPath(templateFile)} which ${renderAssemblyPathEscape(resolvedTemplate, assemblyDir)}`);
3995
+ if (!resolvedTemplate.contained) throw new SynthesisError(`Stack ${displayStackName(stackName)} has templateFile=${displayAssemblyPath(templateFile)} which ${renderAssemblyPathEscape(resolvedTemplate, assemblyDir)}`);
3983
3996
  const templatePath = resolvedTemplate.path;
3984
3997
  let template;
3985
3998
  try {
3986
3999
  const content = readFileSync(templatePath, "utf-8");
3987
4000
  template = JSON.parse(content);
3988
4001
  } catch (error) {
3989
- throw new SynthesisError(`Failed to read template for stack '${displaySafe(stackName)}': ${displaySafe(error instanceof Error ? error.message : String(error))}`, error instanceof Error ? error : void 0);
4002
+ throw new SynthesisError(`Failed to read template for stack ${displayStackName(stackName)}: ${displaySafe(error instanceof Error ? error.message : String(error))}`, error instanceof Error ? error : void 0);
3990
4003
  }
3991
4004
  this.logger.debug(`Stack: ${displaySafe(stackName)}, Resources: ${Object.keys(template.Resources ?? {}).length}`);
3992
4005
  let assetManifestPath;
@@ -4005,7 +4018,7 @@ var AssemblyReader = class {
4005
4018
  if (depName !== stackName) dependencyNames.push(depName);
4006
4019
  }
4007
4020
  }
4008
- if (dependencyNames.length > 0) this.logger.debug(`Stack '${displaySafe(stackName)}' depends on: [${dependencyNames.map((n) => displaySafe(n)).join(", ")}]`);
4021
+ if (dependencyNames.length > 0) this.logger.debug(`Stack ${displayStackName(stackName)} depends on: [${dependencyNames.map((n) => displaySafe(n)).join(", ")}]`);
4009
4022
  let env;
4010
4023
  if (artifact.environment) env = parseEnvironment(artifact.environment);
4011
4024
  const nestedTemplates = nullPrototypeRecord();
@@ -4013,9 +4026,9 @@ var AssemblyReader = class {
4013
4026
  if (resource?.Type !== "AWS::CloudFormation::Stack") continue;
4014
4027
  const assetPath = resource.Metadata?.["aws:asset:path"];
4015
4028
  if (typeof assetPath !== "string" || assetPath.length === 0) continue;
4016
- if (isAbsolute(assetPath) || /^[a-zA-Z]:[\\/]/.test(assetPath) || assetPath.startsWith("\\\\")) throw new SynthesisError(`Stack '${displaySafe(stackName)}' nested-stack '${displaySafe(logicalId)}' has Metadata['aws:asset:path']=${displayAssemblyPath(assetPath)} which is absolute. CDK emits relative asset paths for nested templates; an absolute path indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to load.`);
4029
+ if (isAbsolute(assetPath) || /^[a-zA-Z]:[\\/]/.test(assetPath) || assetPath.startsWith("\\\\")) throw new SynthesisError(`Stack ${displayStackName(stackName)} nested-stack ${displayIdent(logicalId)} has Metadata['aws:asset:path']=${displayAssemblyPath(assetPath)} which is absolute. CDK emits relative asset paths for nested templates; an absolute path indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to load.`);
4017
4030
  const resolvedNested = resolveAssemblyPath(assemblyDir, assetPath);
4018
- if (!resolvedNested.contained) throw new SynthesisError(`Stack '${displaySafe(stackName)}' nested-stack '${displaySafe(logicalId)}' has Metadata['aws:asset:path']=${displayAssemblyPath(assetPath)} which ${renderAssemblyPathEscape(resolvedNested, assemblyDir)}`);
4031
+ if (!resolvedNested.contained) throw new SynthesisError(`Stack ${displayStackName(stackName)} nested-stack ${displayIdent(logicalId)} has Metadata['aws:asset:path']=${displayAssemblyPath(assetPath)} which ${renderAssemblyPathEscape(resolvedNested, assemblyDir)}`);
4019
4032
  nestedTemplates[logicalId] = resolvedNested.path;
4020
4033
  }
4021
4034
  return {
@@ -5115,6 +5128,10 @@ function canonicalizeRegion(region) {
5115
5128
  * the COMMERCIAL suffix, so `parseEcrRegistryHost` (`src/utils/ecr-uri.ts`)
5116
5129
  * rejected it under the strict host check issue #1758 added and the image was
5117
5130
  * classified `public` — anonymous pull, no `docker login`, opaque failure.
5131
+ * `parseEcrRegistryHost` is stricter than this fallback (issue #3670): a
5132
+ * region whose PREFIX no partition row knows (`nz-north-1`) is refused there,
5133
+ * and reported by `looksLikeEcrHostWithForeignSuffix`, until its partition's
5134
+ * `regionRegex` is added to `AWS_REGION_ID` in `src/utils/ecr-uri.ts`.
5118
5135
  *
5119
5136
  * The region is CANONICALIZED through {@link canonicalizeRegion} before the
5120
5137
  * prefix tests (issue [#1795](https://github.com/go-to-k/cdkd/issues/1795)),
@@ -7378,12 +7395,12 @@ var Synthesizer = class {
7378
7395
  else if (accountId) stateBucket = `cdkd-state-${accountId}`;
7379
7396
  else {
7380
7397
  const oversize = stacksWithMacros.find((s) => JSON.stringify(s.template).length > 51200);
7381
- if (oversize) throw new SynthesisError(`Stack '${displaySafe(oversize.stackName)}' uses CloudFormation macros AND its serialized template exceeds the 51,200-byte inline TemplateBody limit, so cdkd must upload the template to S3 for the transient expansion changeset. cdkd could not resolve a state bucket: STS GetCallerIdentity failed AND --state-bucket was not provided. Pass --state-bucket <name> (cdkd uses the same bucket as cdkd deploy state storage; typically 'cdkd-state-<accountId>').`);
7398
+ if (oversize) throw new SynthesisError(`Stack ${displayStackName(oversize.stackName)} uses CloudFormation macros AND its serialized template exceeds the 51,200-byte inline TemplateBody limit, so cdkd must upload the template to S3 for the transient expansion changeset. cdkd could not resolve a state bucket: STS GetCallerIdentity failed AND --state-bucket was not provided. Pass --state-bucket <name> (cdkd uses the same bucket as cdkd deploy state storage; typically 'cdkd-state-<accountId>').`);
7382
7399
  stateBucket = void 0;
7383
7400
  }
7384
7401
  for (const stack of stacksWithMacros) {
7385
7402
  const macros = enumerateMacros(stack.template);
7386
- this.logger.info(`[macros] Expanding CloudFormation macros for stack '${displaySafe(stack.stackName)}' via CFn round-trip (transforms: ${macros.map((m) => displaySafe(m)).join(", ")}; may take 30-60s)...`);
7403
+ this.logger.info(`[macros] Expanding CloudFormation macros for stack ${displayStackName(stack.stackName)} via CFn round-trip (transforms: ${macros.map((m) => displaySafe(m)).join(", ")}; may take 30-60s)...`);
7387
7404
  const before = Date.now();
7388
7405
  const expanded = await expandMacros(stack.template, {
7389
7406
  region,
@@ -7536,14 +7553,14 @@ function resolveFileAssetSourcePath(manifestDir, asset, opts) {
7536
7553
  const absolute = resolve$1(asset.source.path);
7537
7554
  const escape = absoluteAssemblyPathEscape(assetOutdir, absolute);
7538
7555
  if (escape !== void 0) warnAbsoluteAssetPath({
7539
- subject: `File asset '${displaySafe(asset.displayName)}'`,
7556
+ subject: `File asset ${displayAssemblyPath(asset.displayName)}`,
7540
7557
  field: "source.path",
7541
7558
  absolute,
7542
7559
  escape,
7543
7560
  sink
7544
7561
  });
7545
7562
  else if (namesTheSameDirectory(assetOutdir, absolute)) warnWholeAssemblyAsSource({
7546
- subject: `File asset '${displaySafe(asset.displayName)}'`,
7563
+ subject: `File asset ${displayAssemblyPath(asset.displayName)}`,
7547
7564
  field: "source.path",
7548
7565
  outdir: absolute,
7549
7566
  sink
@@ -7553,14 +7570,14 @@ function resolveFileAssetSourcePath(manifestDir, asset, opts) {
7553
7570
  const resolved = resolveAssemblyPath(manifestDir, asset.source.path, { containWithin: assetOutdir });
7554
7571
  if (!resolved.contained && namesTheSameDirectory(assetOutdir, resolved.path)) {
7555
7572
  warnWholeAssemblyAsSource({
7556
- subject: `File asset '${displaySafe(asset.displayName)}'`,
7573
+ subject: `File asset ${displayAssemblyPath(asset.displayName)}`,
7557
7574
  field: "source.path",
7558
7575
  outdir: resolved.path,
7559
7576
  sink
7560
7577
  });
7561
7578
  return resolved.path;
7562
7579
  }
7563
- if (!resolved.contained) throw new Error(`File asset '${displaySafe(asset.displayName)}' has source.path=${displayAssemblyPath(asset.source.path)} which ${renderAssemblyPathEscape(resolved, assetOutdir, "publish it")}`);
7580
+ if (!resolved.contained) throw new Error(`File asset ${displayAssemblyPath(asset.displayName)} has source.path=${displayAssemblyPath(asset.source.path)} which ${renderAssemblyPathEscape(resolved, assetOutdir, "publish it")}`);
7564
7581
  return resolved.path;
7565
7582
  }
7566
7583
  /**
@@ -7596,7 +7613,7 @@ var AssetManifestLoader = class {
7596
7613
  */
7597
7614
  async loadManifest(cdkOutputDir, stackName) {
7598
7615
  const resolved = resolveAssemblyPath(cdkOutputDir, `${stackName}.assets.json`);
7599
- if (!resolved.contained) throw new Error(`Asset manifest for stack '${displaySafe(stackName)}' ${renderAssemblyPathEscape(resolved, cdkOutputDir)}`);
7616
+ if (!resolved.contained) throw new Error(`Asset manifest for stack ${displayStackName(stackName)} ${renderAssemblyPathEscape(resolved, cdkOutputDir)}`);
7600
7617
  const manifestPath = resolved.path;
7601
7618
  try {
7602
7619
  this.logger.debug(`Loading asset manifest from: ${manifestPath}`);
@@ -9559,63 +9576,7 @@ function resolveDockerContextDirectory(opts) {
9559
9576
  }
9560
9577
  /** One spelling of the warning subject, so the two arms cannot drift. */
9561
9578
  function dockerSubject(assetId) {
9562
- return assetId === void 0 ? "A Docker asset" : `Docker asset '${displaySafe(assetId)}'`;
9563
- }
9564
- /**
9565
- * Refuse a Docker asset whose build context the bundled `cdk-local` engine
9566
- * would take from outside the assembly (issue
9567
- * [#3503](https://github.com/go-to-k/cdkd/issues/3503)).
9568
- *
9569
- * `cdk-local`'s own `buildDockerImage` spells the context (and the
9570
- * `executable` arm's cwd) as `${cdkOutDir}/${source.directory}` with no
9571
- * containment, and cdkd cannot hand it a pre-resolved path without changing
9572
- * the image tag it derives from `source`. So this judges the path the engine
9573
- * WILL open, before the engine runs, through the same
9574
- * {@link resolveDockerContextDirectory} cdkd's own build uses.
9575
- *
9576
- * An ABSOLUTE value is judged by the engine's spelling, not honoured: the
9577
- * concatenation folds it UNDER the manifest directory, so its leading
9578
- * separators are stripped and the remainder takes the relative arm. That
9579
- * keeps a symlink inside the assembly from becoming an escape through an
9580
- * absolute spelling, and keeps the honour-and-warn arm, which describes a
9581
- * build this engine never performs, out of these paths.
9582
- */
9583
- function assertCdkLocalDockerContextContained(opts) {
9584
- const { manifestDir, source, assetOutdir, wrapError } = opts;
9585
- const directory = source.directory;
9586
- if (!directory) return;
9587
- const asEngineJoinsIt = isAbsolute$1(directory) ? directory.replace(/^[/\\]+/, "") : directory;
9588
- resolveDockerContextDirectory({
9589
- manifestDir,
9590
- directory: asEngineJoinsIt,
9591
- wrapError,
9592
- assetOutdir,
9593
- sink: source.executable && source.executable.length > 0 ? "run this asset's source.executable with that directory as its working directory" : "send that directory to docker build as the context of an image cdkd then runs locally"
9594
- });
9595
- const engineSpelled = `${manifestDir}/${asEngineJoinsIt}`;
9596
- const physical = tryRealpathNative(engineSpelled);
9597
- if (physical === void 0) return;
9598
- const bound = tryRealpathNative(assetOutdir) ?? resolve$1(assetOutdir);
9599
- const rel = relative(bound, physical);
9600
- if (rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute$1(rel)) return;
9601
- throw wrapError(`asset source.directory=${displayAssemblyPath(directory)} which ` + renderAssemblyPathEscape({
9602
- contained: false,
9603
- escape: "symlink",
9604
- path: engineSpelled,
9605
- realPath: physical
9606
- }, assetOutdir, "build it"));
9607
- }
9608
- /**
9609
- * `realpath(3)`, or `undefined` when the path does not resolve. `.native` is
9610
- * load-bearing: the JavaScript `realpathSync` folds `..` lexically first,
9611
- * which is the very reading the check above exists to avoid.
9612
- */
9613
- function tryRealpathNative(p) {
9614
- try {
9615
- return realpathSync$1.native(p);
9616
- } catch {
9617
- return;
9618
- }
9579
+ return assetId === void 0 ? "A Docker asset" : `Docker asset ${displayIdent(assetId)}`;
9619
9580
  }
9620
9581
  /**
9621
9582
  * Build a Docker image from a CDK asset source. Returns the local image
@@ -24505,9 +24466,9 @@ function inspectCommand(stackName, region) {
24505
24466
  * entries FIRST ({@link repairMalformedResourceEntriesForReadOnly}), so the
24506
24467
  * row is reported once, as unreadable, rather than warned about as a preview
24507
24468
  * and then dropped. The object test stays here rather than
24508
- * {@link isReadableResourceEntry} because the deploy path takes this
24509
- * predicate WITHOUT the entry one, and narrowing it would let a typeless
24510
- * object with a torn map reach the comparison unrefused. Likewise a `resources` bag that is not readable at all
24469
+ * {@link isReadableResourceEntry} so this verdict does not depend on the entry
24470
+ * guard running first: narrowing it would let a typeless object with a torn
24471
+ * map through any caller that takes this predicate alone. Likewise a `resources` bag that is not readable at all
24511
24472
  * yields `[]` here rather than ids invented from a string's characters — that
24512
24473
  * class is {@link hasReadableResources}'s to report. What a caller must not do
24513
24474
  * is take only this one.
@@ -25165,13 +25126,10 @@ function malformedResourceEntriesWarning(rawStackName, rawRegion, logicalIds) {
25165
25126
  * `logicalId` is not a string arrives as `''` and renders as the
25166
25127
  * `UNRENDERABLE` stand-in.
25167
25128
  */
25168
- function malformedOrphanRecordsWarning(rawStackName, rawRegion, logicalIds) {
25129
+ function malformedOrphanRecordsWarning(rawStackName, rawRegion, logicalIds, alsoRejectsTornMaps) {
25169
25130
  const stackName = absentIfEmpty(rawStackName);
25170
25131
  const region = absentIfEmpty(rawRegion);
25171
- const named = logicalIds.slice(0, NAMED_UNREADABLE_ENTRIES).map((id) => displayLogicalId(id)).join(", ");
25172
- const rest = logicalIds.length - NAMED_UNREADABLE_ENTRIES;
25173
- const more = rest > 0 ? ` and ${rest} more` : "";
25174
- return `${stackClause(stackName, region)} holds ${logicalIds.length} rollback-orphan record(s) in 'orphans' that cannot be read as resources — ${named}${more} — because they are not objects, or carry no resource type. Continuing WITHOUT them: they are not previewed for adoption. These ids were read from 'orphans'; the 'resources' map carries its own warning when it is damaged too. See the stored values with: ` + inspectCommand(stackName, region);
25132
+ return `${stackClause(stackName, region)} holds ${logicalIds.length} rollback-orphan record(s) in 'orphans' that cannot be read as resources — ${namedOrphanRows(logicalIds)} — because they are not objects, have no string 'logicalId' or share it with another row, or their 'state' is not an object, names no resource type${alsoRejectsTornMaps ? ", has no non-empty string 'physicalId', or holds a 'properties' or 'attributes' map that is not an object" : ` or has no non-empty string 'physicalId'`}. Continuing WITHOUT them: ${alsoRejectsTornMaps ? `they are excluded from the secret scan, so nothing this run does can certify them` : "they are not previewed for adoption, and 'cdkd deploy' refuses the record over them rather than dropping them as this command does"}. These ids were read from 'orphans'; the 'resources' map carries its own warning when it is damaged too. See the stored values with: ` + inspectCommand(stackName, region);
25175
25133
  }
25176
25134
  /**
25177
25135
  * The half the refusal and the warning share: what is wrong and which entries.
@@ -25210,6 +25168,46 @@ function malformedResourceEntriesRefusalMessage(rawStackName, rawRegion, logical
25210
25168
  return `${namedEntriesClause(stackName, region, logicalIds)} This command can WRITE state, so it refuses rather than skipping them: saving over the record would report a clean run for entries nothing could read, and would leave the next command to fail on them with no more to go on. Nothing was locked, read from AWS or written FOR THIS STACK. Inspect it with: ` + inspectCommand(stackName, region);
25211
25169
  }
25212
25170
  /**
25171
+ * For `cdkd deploy`: refuse a record whose `resources` map holds a ROW that is
25172
+ * not a readable resource record (go-to-k/cdkd#3314). TWO call sites, one
25173
+ * verdict. `DeployEngine`'s state load is the one a deploy hits: two walks run
25174
+ * between the load and the diff (the CLI's prefix-migration gate and the
25175
+ * observed-state auto-refresh) and each died on a `null` row with a bare
25176
+ * `TypeError`. `DiffCalculator.calculateDiff` also calls it, beside
25177
+ * {@link refuseMalformedResourceProperties}, because that is where the verdict
25178
+ * is made. Any other caller of the diff that forgets gets the refusal.
25179
+ *
25180
+ * The harm is the diff's verdict, not a save. The diff looks up each template
25181
+ * resource by logical id, so a `null` (or any non-object) row reads as "not in
25182
+ * state" and is planned as a CREATE. That either collides with the live
25183
+ * resource's name or creates a SECOND copy and orphans the first. A row with no
25184
+ * `resourceType` compares unequal to the template's type, so it is planned as a
25185
+ * TYPE CHANGE, which is a replacement.
25186
+ *
25187
+ * A third entry point on the ENTRY class rather than a call to
25188
+ * {@link refuseMalformedResourceEntries}, whose text is false here. That text
25189
+ * says "Nothing was locked", but the deploy holds its lock at both sites. Same
25190
+ * predicate, so the verdict cannot diverge.
25191
+ *
25192
+ * The engine passes its own resolved stack and region. `calculateDiff` holds no
25193
+ * trusted pair and passes neither (see {@link stackClause}).
25194
+ */
25195
+ function refuseMalformedResourceEntriesForDeploy(state, stackName, region) {
25196
+ const unreadable = unreadableResourceEntries(state);
25197
+ if (unreadable.length === 0) return;
25198
+ throw markNonRetryable(new CdkdError(malformedDeployResourceEntriesRefusalMessage(stackName, region, unreadable), STATE_RESOURCES_MALFORMED));
25199
+ }
25200
+ /**
25201
+ * The text {@link refuseMalformedResourceEntriesForDeploy} raises. Like
25202
+ * {@link malformedResourcePropertiesRefusalMessage}, it must be true under
25203
+ * `--dry-run` too, since the dry-run return comes after the diff.
25204
+ */
25205
+ function malformedDeployResourceEntriesRefusalMessage(rawStackName, rawRegion, logicalIds) {
25206
+ const stackName = absentIfEmpty(rawStackName);
25207
+ const region = absentIfEmpty(rawRegion);
25208
+ return `${namedEntriesClause(stackName, region, logicalIds)} 'cdkd deploy' can WRITE state and AWS resources, so it refuses rather than continuing — under '--dry-run' too, because the plan a dry run would print is the wrong one: a row that is not an object reads as a resource cdkd does not manage, so the template's resource of that id is planned as a CREATE (a second copy of a live resource, or a name collision), and a row with no resource type is planned as a TYPE CHANGE, which replaces the live resource. Nothing was provisioned and no state was written FOR THIS STACK. Repair or remove the record first; 'cdkd diff' previews the rest of the stack without those rows and warns that it did. Inspect the record with: ` + inspectCommand(stackName, region);
25209
+ }
25210
+ /**
25213
25211
  * For `cdkd orphan`: refuse a record whose SURVIVING `resources` entries are
25214
25212
  * not readable resource records (go-to-k/cdkd#3350).
25215
25213
  *
@@ -25303,12 +25301,177 @@ function malformedOrphanResourceAttributesRefusalMessage(rawStackName, rawRegion
25303
25301
  return orphanRefusal(stackName, region, recovery, `${stackClause(stackName, region)} holds ${logicalIds.length} resource record(s) whose 'attributes' map cannot be read — ${named}${more} — because it is null or not an object. The record is malformed or truncated. 'cdkd orphan' REWRITES and SAVES every record it keeps, so it refuses rather than continuing — under '--dry-run' too, so a dry run never prints a plan the real run refuses. The map is carried into the save VERBATIM, and a list is walked, so its rewrites are recorded into a container that is still not a map. It is the cache a later 'cdkd deploy' reads another resource's 'Fn::GetAtt' of this one from, so continuing would report success over the one map that read depends on.`, SURVIVOR_THIRD_WAY_OUT);
25304
25302
  }
25305
25303
  /**
25304
+ * Is ONE `orphans[]` record PREVIEWABLE — the narrow half of
25305
+ * {@link isReadableOrphanRecord}, for `cdkd diff` alone (go-to-k/cdkd#3500).
25306
+ *
25307
+ * An object, a string `logicalId`, and a `state` that is a readable resource
25308
+ * entry with a non-empty string `physicalId`: everything the adoption preview
25309
+ * dereferences, and nothing more. The `physicalId` half is NOT inherited from
25310
+ * {@link isReadableResourceEntry} — see {@link hasStringPhysicalId} for why that
25311
+ * one stops at `resourceType` and why a ROW cannot. It
25312
+ * deliberately does not ask about that entry's `properties`, because `cdkd diff`
25313
+ * has a LATER answer for that one — `computeStackDiff` runs the `properties`
25314
+ * repair a second time over the adopted records and names what it emptied, which
25315
+ * `docs/cli-diff.md` and `.claude/rules/state-malformed-properties.md` both
25316
+ * describe. Dropping such a row here instead would make that repair unreachable
25317
+ * and silently retire a documented report. A torn `attributes` map rides through
25318
+ * for the same reason and gets NO later answer: stated rather than solved, and
25319
+ * the same residual `cdkd diff` had before this predicate existed.
25320
+ *
25321
+ * It CANNOT see rows sharing a `logicalId` (go-to-k/cdkd#3643), a LIST-level
25322
+ * defect — never filter a list with it alone; take
25323
+ * {@link previewableOrphanRecords} / {@link unpreviewableOrphanRecords}.
25324
+ *
25325
+ * Every WRITER takes the full predicate: none of them has a second answer, and
25326
+ * a torn `properties` map reaching a save is go-to-k/cdkd#3344's defect.
25327
+ */
25328
+ function isPreviewableOrphanRecord(record) {
25329
+ if (!isReadableBag(record)) return false;
25330
+ if (typeof record.logicalId !== "string") return false;
25331
+ const entry = record.state;
25332
+ return isReadableResourceEntry(entry) && hasStringPhysicalId(entry);
25333
+ }
25334
+ /**
25335
+ * Does this resource entry carry a NON-EMPTY string `physicalId`?
25336
+ *
25337
+ * Its own helper rather than a clause spelled in each row predicate, so the two
25338
+ * cannot drift on it, and deliberately NOT folded into
25339
+ * {@link isReadableResourceEntry}: that one answers for a `resources` ENTRY,
25340
+ * where stopping at `resourceType` is a recorded decision — a row with a type and
25341
+ * no physical id fails per-resource where it is used and is reported by that
25342
+ * command in its own terms.
25343
+ *
25344
+ * An `orphans` ROW has no such second report, which is why it asks for more
25345
+ * (go-to-k/cdkd#3641 review). The record names a resource cdkd deliberately LEFT
25346
+ * LIVE in AWS, so the physical id is the only handle on it: `cdkd destroy`'s
25347
+ * pre-confirmation listing is the operator's only notice before the record is
25348
+ * deleted, and it rendered an absent id as an EMPTY field in the row they
25349
+ * approve. `planOrphanAdoption` dereferences it eight times over — including
25350
+ * `knownPhysicalId` on the provider call and `claims().has(...)` for a
25351
+ * cross-stack collision — so a row without one decides an adoption from
25352
+ * `undefined`.
25353
+ */
25354
+ function hasStringPhysicalId(entry) {
25355
+ const physicalId = entry.physicalId;
25356
+ return typeof physicalId === "string" && physicalId !== "";
25357
+ }
25358
+ /**
25359
+ * The `orphans[]` records {@link isPreviewableOrphanRecord} rejects, PLUS every
25360
+ * row sharing its string `logicalId` with another row (go-to-k/cdkd#3643),
25361
+ * named the way {@link unreadableOrphanRecords} names its own.
25362
+ */
25363
+ function unpreviewableOrphanRecords(state) {
25364
+ if (!Array.isArray(state.orphans)) return [];
25365
+ const rows = state.orphans;
25366
+ const shared = sharedOrphanLogicalIds(rows);
25367
+ return rows.filter((record) => !isPreviewableOrphanRecord(record) || sharesLogicalId(record, shared)).map(orphanRowName);
25368
+ }
25369
+ /**
25370
+ * The rows `cdkd diff` KEEPS for the adoption preview: the complement of
25371
+ * {@link unpreviewableOrphanRecords}, homed beside it so the two cannot
25372
+ * disagree about a row (go-to-k/cdkd#3643). `[]` for a container that is not a
25373
+ * list, which {@link hasReadableOrphans} reports.
25374
+ *
25375
+ * Why not `orphans.filter(isPreviewableOrphanRecord)` at the call site, which is
25376
+ * what `cdkd diff` did: that predicate decides ONE record, and a shared
25377
+ * `logicalId` is a property of the LIST. Two rows carrying one id both pass it,
25378
+ * and `planOrphanAdoption` then keys its adoption map on that id, so the preview
25379
+ * shows ONE adoption for two rows — the collapse the writers now refuse.
25380
+ */
25381
+ function previewableOrphanRecords(state) {
25382
+ if (!Array.isArray(state.orphans)) return [];
25383
+ const rows = state.orphans;
25384
+ const shared = sharedOrphanLogicalIds(rows);
25385
+ return rows.filter((record) => isPreviewableOrphanRecord(record) && !sharesLogicalId(record, shared));
25386
+ }
25387
+ /**
25388
+ * The string `logicalId`s that TWO OR MORE rows of `orphans` carry
25389
+ * (go-to-k/cdkd#3643) — `''` included, since an empty string is still a key.
25390
+ *
25391
+ * Such a record is MALFORMED, not merely unusual: no supported writer produces
25392
+ * one, because `orphansAfterRollback` merges by `logicalId` and keeps one row per
25393
+ * id. What a reader does with it is the loss the row predicate exists to refuse
25394
+ * for a MISSING id — `orphansAfterRollback` keeps only the LAST of the rows, and
25395
+ * the deploy's adoption pass writes `adopted[logicalId]` for each, so the first
25396
+ * row's resource stays live in AWS with nothing tracking it.
25397
+ *
25398
+ * Counted over EVERY row that is an object carrying a string id, not only the
25399
+ * rows the per-record predicate accepts: a torn row and a healthy one sharing an
25400
+ * id still say the record was edited or damaged, and keeping the healthy one
25401
+ * would have a read-only command preview, as the stack's only row of that id, a
25402
+ * row whose twin the writers refuse over. A `Map` rather than a plain object, so
25403
+ * an id spelled `__proto__` or `constructor` is counted rather than resolved
25404
+ * through the prototype.
25405
+ */
25406
+ function sharedOrphanLogicalIds(rows) {
25407
+ const seen = /* @__PURE__ */ new Map();
25408
+ for (const record of rows) {
25409
+ if (!isReadableBag(record)) continue;
25410
+ const id = record.logicalId;
25411
+ if (typeof id !== "string") continue;
25412
+ seen.set(id, (seen.get(id) ?? 0) + 1);
25413
+ }
25414
+ const shared = /* @__PURE__ */ new Set();
25415
+ for (const [id, count] of seen) if (count > 1) shared.add(id);
25416
+ return shared;
25417
+ }
25418
+ /** Does this row carry one of the {@link sharedOrphanLogicalIds}? */
25419
+ function sharesLogicalId(record, shared) {
25420
+ if (shared.size === 0 || !isReadableBag(record)) return false;
25421
+ const id = record.logicalId;
25422
+ return typeof id === "string" && shared.has(id);
25423
+ }
25424
+ /**
25425
+ * How every `orphans` ROW helper names a row: its `logicalId`, or `''`
25426
+ * (rendered as the `UNRENDERABLE` stand-in) when it has no string one.
25427
+ */
25428
+ function orphanRowName(record) {
25429
+ const id = isReadableBag(record) ? record.logicalId : void 0;
25430
+ return typeof id === "string" ? id : "";
25431
+ }
25432
+ /**
25433
+ * Is ONE `orphans[]` record usable by a reader (go-to-k/cdkd#3500)?
25434
+ *
25435
+ * Split out of {@link unreadableOrphanRecords} rather than written a second
25436
+ * time, so the five WRITE-capable readers that take a row's fields off the cast
25437
+ * and the `cdkd orphan` refusal that predates them cannot disagree about which
25438
+ * rows are usable. `cdkd diff` deliberately disagrees for one class — see
25439
+ * {@link isPreviewableOrphanRecord}, which is the narrower half it takes. The two shapes answer different questions and share this verdict:
25440
+ * this one decides ONE record, that one names every record a state fails on.
25441
+ *
25442
+ * A reader needs all of it to hold, because it reaches the record as a WHOLE:
25443
+ * the record is an object, its `logicalId` is a string (a row with none keys
25444
+ * `orphansAfterRollback`'s merge map on `undefined`, so several of them collapse
25445
+ * into one), its `state` is a readable resource entry carrying a NON-EMPTY string
25446
+ * `physicalId` ({@link hasStringPhysicalId} carries why that is asked here and
25447
+ * not for a `resources` entry), and that entry's `properties` and `attributes`
25448
+ * maps are readable.
25449
+ *
25450
+ * It CANNOT see the one LIST-level defect: two rows sharing a string `logicalId`
25451
+ * collapse in the same merge map, and each of them passes this test
25452
+ * (go-to-k/cdkd#3643). So never filter a list with it alone — take
25453
+ * {@link unreadableOrphanRecords} (or, for `cdkd diff`,
25454
+ * {@link previewableOrphanRecords}), which add that check.
25455
+ */
25456
+ function isReadableOrphanRecord(record) {
25457
+ if (!isPreviewableOrphanRecord(record)) return false;
25458
+ const entry = record.state;
25459
+ return isReadableBag(entry.properties) && hasReadableAttributes(entry);
25460
+ }
25461
+ /**
25306
25462
  * The `orphans[]` records a reader cannot use: a record that is not an object
25307
- * or has no string `logicalId`, whose `state` is not a readable resource entry,
25308
- * or whose `state` carries an unreadable `properties` or `attributes` map
25463
+ * or has no string `logicalId`, or whose `state` is not a readable resource entry,
25464
+ * carries no non-empty string `physicalId`, or carries an unreadable `properties`
25465
+ * or `attributes` map
25309
25466
  * (go-to-k/cdkd#3344). The `logicalId` half is go-to-k/cdkd#3500's row
25310
- * predicate: rows with none all key one map entry, and `orphansAfterRollback`
25311
- * collapses them into one.
25467
+ * predicate: rows MISSING one all key the same map entry, and
25468
+ * `orphansAfterRollback` collapses those into one. EVERY row whose string
25469
+ * `logicalId` another row also carries is named too — each of them, since
25470
+ * nothing says which is the live resource (go-to-k/cdkd#3643,
25471
+ * {@link sharedOrphanLogicalIds}): those collapse in the same map by the same
25472
+ * mechanism, and it is the check {@link isReadableOrphanRecord} cannot make.
25473
+ * Folded in HERE so every refusal, the read-only repair and `cdkd orphan`
25474
+ * inherit it and no caller re-spells it.
25312
25475
  *
25313
25476
  * Every `ResourceState` question the `resources` side answers in separate
25314
25477
  * predicates, asked in ONE here: a rollback-orphan record is reached only as a
@@ -25321,15 +25484,100 @@ function malformedOrphanResourceAttributesRefusalMessage(rawStackName, rawRegion
25321
25484
  */
25322
25485
  function unreadableOrphanRecords(state) {
25323
25486
  if (!Array.isArray(state.orphans)) return [];
25324
- return state.orphans.filter((record) => {
25325
- if (!isReadableBag(record)) return true;
25326
- if (typeof record.logicalId !== "string") return true;
25327
- const entry = record.state;
25328
- return !isReadableResourceEntry(entry) || !isReadableBag(entry.properties) || !hasReadableAttributes(entry);
25329
- }).map((record) => {
25330
- const id = isReadableBag(record) ? record.logicalId : void 0;
25331
- return typeof id === "string" ? id : "";
25332
- });
25487
+ const rows = state.orphans;
25488
+ const shared = sharedOrphanLogicalIds(rows);
25489
+ return rows.filter((record) => isUnusableOrphanRow(record, shared)).map(orphanRowName);
25490
+ }
25491
+ /**
25492
+ * The writers' per-row verdict with the list-level half applied: the ONE test
25493
+ * {@link unreadableOrphanRecords} names by and
25494
+ * {@link repairMalformedOrphanRecordsForReadOnly} drops by, so the rows a
25495
+ * dry run drops are exactly the rows the real run refuses over.
25496
+ */
25497
+ function isUnusableOrphanRow(record, shared) {
25498
+ return !isReadableOrphanRecord(record) || sharesLogicalId(record, shared);
25499
+ }
25500
+ /**
25501
+ * Refuse a readable `orphans` list holding a record no reader can use, on a
25502
+ * path that can WRITE state (go-to-k/cdkd#3500).
25503
+ *
25504
+ * The CONTAINER pair beside this one ({@link refuseMalformedOrphans} and its
25505
+ * destroy twin) answers "is the field a list"; this answers "is every row in it
25506
+ * usable", and a list can fail the second while passing the first. One
25507
+ * predicate for both questions would be wrong in the direction that loses
25508
+ * evidence: `cdkd diff` repairs the container and must DROP a row, which is not
25509
+ * the same verdict.
25510
+ *
25511
+ * Marked non-retryable for {@link refuseMalformedOrphans}'s reason: a retry
25512
+ * cannot change a persisted record, and a nested child's deploy runs inside a
25513
+ * `withRetry`.
25514
+ *
25515
+ * It takes NO `LockRecoveryContext`, matching its container twin
25516
+ * {@link refuseMalformedOrphans}: no caller on these paths holds one, and a
25517
+ * parameter every call site omits renders nothing. The message builder keeps its
25518
+ * own `recovery` parameter, and its arms are pinned by calling it directly —
25519
+ * `refuseMalformedOrphansForOrphan` is NOT that route, since it raises a
25520
+ * different text ({@link malformedOrphansForOrphanRefusalMessage}).
25521
+ */
25522
+ function refuseMalformedOrphanRecords(state, stackName, region) {
25523
+ const unreadable = unreadableOrphanRecords(state);
25524
+ if (unreadable.length === 0) return;
25525
+ throw markNonRetryable(new CdkdError(malformedOrphanRecordsRefusalMessage(stackName, region, unreadable), STATE_RESOURCES_MALFORMED));
25526
+ }
25527
+ /**
25528
+ * The DESTROY twin of {@link refuseMalformedOrphanRecords}, carrying its own
25529
+ * text for the reason the container pair splits: a destroy neither walks the
25530
+ * record back into `resources` nor writes a reshaped one, so the writer text's
25531
+ * mechanism would be false here. What it does instead is REPORT the rows — the
25532
+ * operator's only notice that those resources stop being tracked — from fields
25533
+ * it never validates, and then remove the record.
25534
+ */
25535
+ function refuseMalformedOrphanRecordsForDestroy(state, stackName, region) {
25536
+ const unreadable = unreadableOrphanRecords(state);
25537
+ if (unreadable.length === 0) return;
25538
+ throw markNonRetryable(new CdkdError(malformedOrphanRecordsForDestroyRefusalMessage(stackName, region, unreadable), STATE_RESOURCES_MALFORMED));
25539
+ }
25540
+ /**
25541
+ * The WRITER text {@link refuseMalformedOrphanRecords} raises.
25542
+ *
25543
+ * Its mechanism clause names THREE outcomes rather than one, because a reader
25544
+ * meets a different one per shape: a row MISSING its `logicalId` keys
25545
+ * `orphansAfterRollback`'s merge map on `undefined`, so rows missing it collapse
25546
+ * to one saved survivor (distinct NUMERIC ids do not — they stay distinct keys),
25547
+ * and rows SHARING a string one collapse the same way (go-to-k/cdkd#3643);
25548
+ * a row whose `state` is absent or null aborts wherever it is first
25549
+ * dereferenced; and a `state` that is a primitive or names no resource type
25550
+ * reads as `undefined` and is carried or reported as though it were a record.
25551
+ */
25552
+ function malformedOrphanRecordsRefusalMessage(rawStackName, rawRegion, logicalIds, recovery) {
25553
+ const stackName = absentIfEmpty(rawStackName);
25554
+ const region = absentIfEmpty(rawRegion);
25555
+ return orphanRefusal(stackName, region, recovery, `${stackClause(stackName, region)} holds ${logicalIds.length} rollback-orphan record(s) in 'orphans' that cannot be read — ${namedOrphanRows(logicalIds)} — because the record has no string 'logicalId' or shares it with another row, or its 'state' is not an object, names no resource type, has no non-empty string 'physicalId', or holds a 'properties' or 'attributes' map that is not an object. This command can WRITE state, and what it would do with such a row is not one thing: 'cdkd rollback' merges by 'logicalId', so rows MISSING one, or SHARING one, key the same entry and only one of them survives into the record it saves, the others' resources left live in AWS with nothing tracking them; a row whose 'state' is absent or null aborts wherever it is first dereferenced, with an error naming neither the field nor the stack; and the quietest shape is neither — a 'state' that is a primitive, or an object naming no resource type, reads as 'undefined' and is carried or reported as if it were a record. It refuses rather than pick one of those outcomes.`, "Repair the record by hand rather than deleting it — it is the only record that an earlier failed deploy left its resource live in AWS.", ORPHANS_DROP_CAVEAT);
25556
+ }
25557
+ /** The DESTROY text {@link refuseMalformedOrphanRecordsForDestroy} raises. */
25558
+ function malformedOrphanRecordsForDestroyRefusalMessage(rawStackName, rawRegion, logicalIds, recovery) {
25559
+ const stackName = absentIfEmpty(rawStackName);
25560
+ const region = absentIfEmpty(rawRegion);
25561
+ return orphanRefusal(stackName, region, recovery, `${stackClause(stackName, region)} holds ${logicalIds.length} rollback-orphan record(s) in 'orphans' that cannot be read — ${namedOrphanRows(logicalIds)} — because the record has no string 'logicalId' or shares it with another row, or its 'state' is not an object, names no resource type, has no non-empty string 'physicalId', or holds a 'properties' or 'attributes' map that is not an object. This command DELETES state, and the record is the only evidence that those resources are live in AWS — so it refuses over any part of a row it cannot read rather than removing evidence it could not fully report. What the DAMAGE costs the report depends on which part is torn: the listing that tells you which resources stop being tracked prints the row's own 'logicalId', resource type and physical id, from fields nothing else validates, so it can abort before the confirmation naming nothing, or print a row with a field missing and let you approve the delete. A torn 'properties' or 'attributes' map the listing never reads is refused too, because what is discarded is the whole record. So are rows sharing one 'logicalId', though the listing would print each: no cdkd command writes such a record — the rollback save merges by id and every other save carries the list unchanged — so it was damaged or edited by hand, and deleting it would discard it before anyone decides which of those resources the stack still owns.`, "Repair the record by hand rather than deleting it — it is the only record that an earlier failed deploy left its resource live in AWS.", ORPHANS_DROP_CAVEAT);
25562
+ }
25563
+ /**
25564
+ * The READ-ONLY disposition for the same rows: DROP them and return their
25565
+ * names, for a command that never writes the record back — `cdkd scrub
25566
+ * --dry-run` today.
25567
+ *
25568
+ * Returns the dropped names rather than a boolean because its caller reports
25569
+ * them: `cdkd scrub --dry-run` warns with them by name, and the stack joins the
25570
+ * audited-record refusal's list. `cdkd diff` does NOT call this — it needs the
25571
+ * narrower {@link isPreviewableOrphanRecord} and keeps the survivors itself. An
25572
+ * empty return means nothing was dropped and the list is untouched.
25573
+ */
25574
+ function repairMalformedOrphanRecordsForReadOnly(state) {
25575
+ const unreadable = unreadableOrphanRecords(state);
25576
+ if (unreadable.length === 0) return [];
25577
+ const rows = state.orphans ?? [];
25578
+ const shared = sharedOrphanLogicalIds(rows);
25579
+ state.orphans = rows.filter((record) => !isUnusableOrphanRow(record, shared));
25580
+ return unreadable;
25333
25581
  }
25334
25582
  /**
25335
25583
  * For `cdkd orphan`: refuse a record whose `orphans` list, or any record in
@@ -25353,6 +25601,87 @@ function refuseMalformedOrphansForOrphan(state, stackName, region, recovery) {
25353
25601
  if (unreadable.length === 0) return;
25354
25602
  throw new CdkdError(malformedOrphansForOrphanRefusalMessage(stackName, region, unreadable, recovery), STATE_RESOURCES_MALFORMED);
25355
25603
  }
25604
+ /**
25605
+ * The `--fail` / exit-3 reason `cdkd diff` reports for an `orphans` row it KEEPS
25606
+ * in the preview and `cdkd deploy` REFUSES (go-to-k/cdkd#3641 maintainer review,
25607
+ * M1).
25608
+ *
25609
+ * The two predicates disagree on exactly one class — a row whose `properties` or
25610
+ * `attributes` map is torn — and that disagreement is deliberate: `cdkd diff`
25611
+ * keeps such a row so the second `properties` repair can name it. What was
25612
+ * missing is the OTHER half of keeping it: the deploy refuses the whole record
25613
+ * over that same row, so a preview that says nothing lets `cdkd diff --fail`
25614
+ * exit 0 and the deploy the operator runs next refuse. That is the contract
25615
+ * `cdkd diff` states for the adoption preview, and this PR is what made it
25616
+ * false. Precisely which shapes: a torn `attributes` map, and a torn `properties`
25617
+ * map on a row the adoption did NOT take — an ADOPTED torn-`properties` row was
25618
+ * already refused at deploy through `calculateDiff` before this lane
25619
+ * (go-to-k/cdkd#3641 item o5), and it is the one the sibling arm reports.
25620
+ *
25621
+ * Homed HERE rather than in `diff-recursive.ts` for the reason every text in
25622
+ * this module is: it renders untrusted logical ids, so it needs
25623
+ * {@link namedOrphanRows} — the same cap, the same `displayLogicalId`, the same
25624
+ * `UNRENDERABLE` stand-in. It NAMES the rows as well as counting them, unlike
25625
+ * {@link deployRefusesPropertiesReason}'s count-only line in that file. It keeps
25626
+ * the names even though {@link malformedOrphanRowsKeptWarning} now names them
25627
+ * too: this reason ships in the `--json` payload, where "see the warning above"
25628
+ * has no referent — the same argument that sibling's doc makes for its own
25629
+ * shape.
25630
+ */
25631
+ function deployRefusesOrphanRowsReason(logicalIds) {
25632
+ return `${logicalIds.length} rollback-orphan record(s) in 'orphans' — ${namedOrphanRows(logicalIds)} — carry a 'properties' or 'attributes' map that cannot be read. This preview KEEPS them, so any adoption shown for them is one 'cdkd deploy' will not perform: it refuses the record over the same rows, and the deploy this previews will not start.`;
25633
+ }
25634
+ /**
25635
+ * The WARNING `cdkd diff` prints for an `orphans` row it KEEPS and a writer
25636
+ * refuses (go-to-k/cdkd#3641 maintainer round 2, M1 continued).
25637
+ *
25638
+ * At every node the RUN REACHES with an adoption preview, which is narrower than
25639
+ * "every node" and the difference is not pedantry (round 4): a non-recursive run —
25640
+ * the default — returns before visiting any child, a state-only child being
25641
+ * DELETED is built with no `previewOrphanAdoption` so the whole row pass is
25642
+ * skipped, and any other caller omitting that option skips it too.
25643
+ *
25644
+ * The exit-3 reason beside this one is TOP-LEVEL only, which is
25645
+ * go-to-k/cdkd#3335's scope decision and right: the deploy skips an unchanged
25646
+ * nested-stack row, so a reason there would report a refusal over a deploy that
25647
+ * succeeds. What made that split SAFE for every other class is that each one
25648
+ * still warns at every node — and this class had no warning at all, because the
25649
+ * row is KEPT and {@link malformedOrphanRecordsWarning} speaks only for rows that
25650
+ * were DROPPED. So a changed nested child holding such a row printed nothing and
25651
+ * its deploy refused.
25652
+ *
25653
+ * It says what the row IS and what the deploy will do, and makes no claim about
25654
+ * what else happens to the row — which is where the first two spellings went
25655
+ * wrong (go-to-k/cdkd#3641 rounds 3 and 4). One said the map "is repaired and
25656
+ * named again over the records an adoption takes", false for every row that
25657
+ * survives the caller's subtraction; the next said nothing else repairs or
25658
+ * previews these rows, false for an ADOPTED row whose `properties` are healthy and
25659
+ * whose `attributes` are torn — the subtraction removes adopted torn-`properties`
25660
+ * names, not every adopted row, and `planOrphanAdoption` rebuilds that row's
25661
+ * attributes and previews the adopted resource. The row's disposition beyond
25662
+ * "kept" is the caller's business, not this sentence's.
25663
+ *
25664
+ * It says the deploy of THIS stack refuses, not that "the deploy this previews"
25665
+ * will not start (round 4 again): under `--recursive` this warning can name a row
25666
+ * in an UNCHANGED nested child, whose parent deploy skips that child and
25667
+ * succeeds. The stack the message already names is the one whose deploy refuses.
25668
+ */
25669
+ function malformedOrphanRowsKeptWarning(rawStackName, rawRegion, logicalIds) {
25670
+ const stackName = absentIfEmpty(rawStackName);
25671
+ const region = absentIfEmpty(rawRegion);
25672
+ return `${stackClause(stackName, region)} holds ${logicalIds.length} rollback-orphan record(s) in 'orphans' — ${namedOrphanRows(logicalIds)} — whose 'properties' or 'attributes' map is not an object. This preview KEEPS them. 'cdkd deploy' of THIS stack does NOT: it refuses the whole record over these rows and will not start until they are repaired. See the stored values with: ` + inspectCommand(stackName, region);
25673
+ }
25674
+ /**
25675
+ * The `<ids> and N more` fragment every `orphans` ROW text shares — the three
25676
+ * refusals and the warning — so a fifth cannot spell the cap differently
25677
+ * (go-to-k/cdkd#3500). Derive the list with
25678
+ * `grep -n "namedOrphanRows(" src/state/malformed-resources-bag.ts`.
25679
+ */
25680
+ function namedOrphanRows(logicalIds) {
25681
+ const named = logicalIds.slice(0, NAMED_UNREADABLE_ENTRIES).map((id) => displayLogicalId(id)).join(", ");
25682
+ const rest = logicalIds.length - NAMED_UNREADABLE_ENTRIES;
25683
+ return `${named}${rest > 0 ? ` and ${rest} more` : ""}`;
25684
+ }
25356
25685
  /** See {@link orphanRefusal}'s `dropCaveat`. */
25357
25686
  const ORPHANS_DROP_CAVEAT = "; it also discards the 'orphans' list, the only record of resources an earlier failed deploy left live in AWS";
25358
25687
  /**
@@ -25369,10 +25698,7 @@ function malformedOrphansForOrphanRefusalMessage(rawStackName, rawRegion, logica
25369
25698
  const region = absentIfEmpty(rawRegion);
25370
25699
  const verbatim = "'cdkd orphan' carries it into the record it saves VERBATIM, without reading it, so it refuses rather than continuing — under '--dry-run' too, so a dry run never prints a plan the real run refuses.";
25371
25700
  if (logicalIds === void 0) return orphanRefusal(stackName, region, recovery, `${stackClause(stackName, region)} has no readable 'orphans' list — the record is malformed or truncated. ${verbatim} The next 'cdkd deploy' refuses the same record, so continuing would only report success over it and leave that refusal one command later.`, "'cdkd orphan' cannot address this list at all, and rewriting it to [] by hand discards the only record of resources an earlier failed deploy left live in AWS.", ORPHANS_DROP_CAVEAT);
25372
- const named = logicalIds.slice(0, NAMED_UNREADABLE_ENTRIES).map((id) => displayLogicalId(id)).join(", ");
25373
- const rest = logicalIds.length - NAMED_UNREADABLE_ENTRIES;
25374
- const more = rest > 0 ? ` and ${rest} more` : "";
25375
- return orphanRefusal(stackName, region, recovery, `${stackClause(stackName, region)} holds ${logicalIds.length} rollback-orphan record(s) in 'orphans' that cannot be read — ${named}${more} — because the record has no string 'logicalId', its 'state' is not an object or names no resource type, or that state's 'properties' or 'attributes' map is not one. The record is malformed or truncated. ${verbatim} Those records are not inert: they are what the next 'cdkd deploy' may re-adopt into 'resources' and what 'cdkd diff' previews for adoption, so continuing would report success over damage the next command meets.`, "'cdkd orphan' cannot remove such a record itself: it is not in 'resources', so it has no construct path. Repair the record by hand rather than deleting it — it is the only record that an earlier failed deploy left its resource live in AWS.", ORPHANS_DROP_CAVEAT);
25701
+ return orphanRefusal(stackName, region, recovery, `${stackClause(stackName, region)} holds ${logicalIds.length} rollback-orphan record(s) in 'orphans' that cannot be read — ${namedOrphanRows(logicalIds)} — because the record has no string 'logicalId' or shares it with another row, or its 'state' is not an object, names no resource type, has no non-empty string 'physicalId', or holds a 'properties' or 'attributes' map that is not an object. The record is malformed or truncated. ${verbatim} Those records are not inert: they are what the next 'cdkd deploy' may re-adopt into 'resources' and what 'cdkd diff' previews for adoption, so continuing would report success over damage the next command meets.`, "'cdkd orphan' cannot remove such a record itself: it is not in 'resources', so it has no construct path. Repair the record by hand rather than deleting it — it is the only record that an earlier failed deploy left its resource live in AWS.", ORPHANS_DROP_CAVEAT);
25376
25702
  }
25377
25703
 
25378
25704
  //#endregion
@@ -25427,6 +25753,21 @@ const INTRINSIC_KEYS = /* @__PURE__ */ new Set([
25427
25753
  */
25428
25754
  const IN_PLACE_UPDATE_DERIVED_ATTRS = Object.freeze({ "AWS::EC2::LaunchTemplate": /* @__PURE__ */ new Set(["LatestVersionNumber", "DefaultVersionNumber"]) });
25429
25755
  /**
25756
+ * The PREFIX twin of {@link IN_PLACE_UPDATE_DERIVED_ATTRS}, for a type whose
25757
+ * derived attributes are an open family rather than a fixed list (issue
25758
+ * [#3631](https://github.com/go-to-k/cdkd/issues/3631)).
25759
+ *
25760
+ * A nested stack's `Outputs.<Key>` attributes are the child's outputs, and a
25761
+ * child-only change moves them while the parent's `AWS::CloudFormation::Stack`
25762
+ * row changes only `TemplateURL` / `Parameters`. The parent's diff runs BEFORE
25763
+ * the child deploys, so a reader of `Fn::GetAtt [Child, 'Outputs.<Key>']`
25764
+ * resolves against the PREVIOUS value and diffs NO_CHANGE. Which outputs move
25765
+ * is decided by the child's own deploy, so it is not knowable here: every
25766
+ * reader is promoted, and the deploy engine's re-resolve-and-skip drops the
25767
+ * ones whose output did not move.
25768
+ */
25769
+ const IN_PLACE_UPDATE_DERIVED_ATTR_PREFIXES = Object.freeze({ "AWS::CloudFormation::Stack": Object.freeze(["Outputs."]) });
25770
+ /**
25430
25771
  * Diff calculator for comparing desired state (template) with current state
25431
25772
  */
25432
25773
  var DiffCalculator = class DiffCalculator {
@@ -25462,6 +25803,7 @@ var DiffCalculator = class DiffCalculator {
25462
25803
  */
25463
25804
  async calculateDiff(currentState, desiredTemplate, resolveFn, canonicalizeProperties, allowedUnsupportedProperties) {
25464
25805
  const changes = /* @__PURE__ */ new Map();
25806
+ refuseMalformedResourceEntriesForDeploy(currentState, void 0, void 0);
25465
25807
  refuseMalformedResourceProperties(currentState, void 0, void 0);
25466
25808
  const currentResources = currentState.resources;
25467
25809
  const desiredResources = desiredTemplate.Resources;
@@ -25556,8 +25898,11 @@ var DiffCalculator = class DiffCalculator {
25556
25898
  });
25557
25899
  this.logger.debug(`DELETE: ${logicalId} (${currentResource.resourceType})`);
25558
25900
  }
25559
- this.promoteReplacementDependents(changes, desiredTemplate);
25560
- this.promoteInPlaceAttributeDependents(changes, desiredTemplate, rawGetAttRefs);
25901
+ let round = 0;
25902
+ do {
25903
+ round++;
25904
+ this.promoteReplacementDependents(changes, desiredTemplate);
25905
+ } while (this.promoteInPlaceAttributeDependents(changes, desiredTemplate, rawGetAttRefs, currentResources, round));
25561
25906
  const summary = this.getSummary(changes);
25562
25907
  this.logger.debug(`Diff calculated: ${summary.create} CREATE, ${summary.update} UPDATE, ${summary.delete} DELETE, ${summary.noChange} NO_CHANGE`);
25563
25908
  return changes;
@@ -25669,7 +26014,11 @@ var DiffCalculator = class DiffCalculator {
25669
26014
  * update side-effects even though it is not a template property — the
25670
26015
  * per-type {@link IN_PLACE_UPDATE_DERIVED_ATTRS} allow list (issue #985;
25671
26016
  * e.g. `AWS::EC2::LaunchTemplate.LatestVersionNumber`, which an
25672
- * `autoscaling.AutoScalingGroup` reads for its `LaunchTemplate.Version`).
26017
+ * `autoscaling.AutoScalingGroup` reads for its `LaunchTemplate.Version`);
26018
+ * or
26019
+ * 3. `Attr` starts with a derived-attribute PREFIX of `Up`'s type — the
26020
+ * {@link IN_PLACE_UPDATE_DERIVED_ATTR_PREFIXES} table (issue #3631; a
26021
+ * nested stack's `Outputs.<Key>`).
25673
26022
  * (`Ref` resolves to the physical id, which an in-place update never moves, so
25674
26023
  * Ref-only dependents are left NO_CHANGE; likewise a GetAtt of a computed
25675
26024
  * attribute NOT in the allow list, e.g. a Lambda `Arn` on a Description edit.)
@@ -25678,14 +26027,25 @@ var DiffCalculator = class DiffCalculator {
25678
26027
  * promoted resource against the in-flight state and skips the provider call
25679
26028
  * when nothing actually changed.
25680
26029
  *
25681
- * Single-hop by design (unlike {@link promoteReplacementDependents}, which is
25682
- * fully transitive via a worklist): `changedPropsByUpstream` is frozen at entry,
25683
- * so a chain `A(in-place) -> B(reads A's changed attr) -> C(reads B's now-changed
25684
- * attr)` promotes B but not C. Deep GetAtt-of-a-changed-attr chains are rare in
25685
- * practice; if one ever needs full transitivity, promote to a worklist that
25686
- * re-derives changed props as dependents are promoted.
25687
- */
25688
- promoteInPlaceAttributeDependents(changes, desiredTemplate, rawGetAttRefs) {
26030
+ * One pass is single-hop: `changedPropsByUpstream` is frozen at entry, so a
26031
+ * chain `A(in-place) -> B(reads A's changed attr) -> C(reads B's now-changed
26032
+ * attr)` promotes B but not C. `calculateDiff` re-runs it to a fixpoint, and
26033
+ * the pass after B's promotion sees B as an in-place UPDATE.
26034
+ *
26035
+ * A SPECULATIVE promotion — arm 3 in any round, and every arm after the first
26036
+ * round, whose upstreams exist only because an earlier promotion guessed —
26037
+ * skips a dependent that reads a MASKED attribute anywhere
26038
+ * ({@link readsMaskedAttribute}). The engine resolves the dependent's whole
26039
+ * bag and refuses a read of `***` BEFORE its no-change skip, so such a guess
26040
+ * would fail a deploy that left the dependent alone before this pass could
26041
+ * reach it (go-to-k/cdkd#3662). The first round's arms 1 and 2 keep their
26042
+ * behaviour: a property they match really changed.
26043
+ *
26044
+ * @param round 1-based fixpoint round; see the speculative-promotion note.
26045
+ * @returns whether any synthetic PropertyChange was added (the caller's
26046
+ * fixpoint signal).
26047
+ */
26048
+ promoteInPlaceAttributeDependents(changes, desiredTemplate, rawGetAttRefs, currentResources, round) {
25689
26049
  const changedPropsByUpstream = /* @__PURE__ */ new Map();
25690
26050
  const inPlaceUpdateTypeByUpstream = /* @__PURE__ */ new Map();
25691
26051
  for (const [logicalId, change] of changes) {
@@ -25695,7 +26055,8 @@ var DiffCalculator = class DiffCalculator {
25695
26055
  const props = propertyChanges.map((pc) => pc.path).filter((p) => typeof p === "string");
25696
26056
  if (props.length > 0) changedPropsByUpstream.set(logicalId, new Set(props));
25697
26057
  }
25698
- if (changedPropsByUpstream.size === 0 && inPlaceUpdateTypeByUpstream.size === 0) return;
26058
+ if (changedPropsByUpstream.size === 0 && inPlaceUpdateTypeByUpstream.size === 0) return false;
26059
+ let added = false;
25699
26060
  for (const [dependentId, perProp] of rawGetAttRefs) {
25700
26061
  const resource = desiredTemplate.Resources[dependentId];
25701
26062
  if (!resource || resource.Type === "AWS::CDK::Metadata") continue;
@@ -25704,22 +26065,29 @@ var DiffCalculator = class DiffCalculator {
25704
26065
  if (change.changeType !== "NO_CHANGE" && change.changeType !== "UPDATE") continue;
25705
26066
  const existingPaths = new Set((change.propertyChanges ?? []).map((pc) => pc.path));
25706
26067
  const syntheticChanges = [];
26068
+ const speculationAllowed = !DiffCalculator.readsMaskedAttribute(perProp, currentResources);
25707
26069
  for (const [propKey, getAttRefs] of perProp) {
25708
26070
  if (existingPaths.has(propKey)) continue;
25709
26071
  let matched = false;
25710
26072
  for (const [upstreamId, attrs] of getAttRefs) {
25711
26073
  if (upstreamId === dependentId) continue;
26074
+ if (round > 1 && !speculationAllowed) break;
25712
26075
  const changedProps = changedPropsByUpstream.get(upstreamId);
25713
26076
  if (changedProps && [...attrs].some((attr) => changedProps.has(attr))) {
25714
26077
  matched = true;
25715
26078
  break;
25716
26079
  }
25717
26080
  const upstreamType = inPlaceUpdateTypeByUpstream.get(upstreamId);
25718
- const derivedAttrs = upstreamType ? IN_PLACE_UPDATE_DERIVED_ATTRS[upstreamType] : void 0;
26081
+ const derivedAttrs = upstreamType !== void 0 && Object.hasOwn(IN_PLACE_UPDATE_DERIVED_ATTRS, upstreamType) ? IN_PLACE_UPDATE_DERIVED_ATTRS[upstreamType] : void 0;
25719
26082
  if (derivedAttrs && [...attrs].some((attr) => derivedAttrs.has(attr))) {
25720
26083
  matched = true;
25721
26084
  break;
25722
26085
  }
26086
+ const derivedPrefixes = upstreamType !== void 0 && Object.hasOwn(IN_PLACE_UPDATE_DERIVED_ATTR_PREFIXES, upstreamType) ? IN_PLACE_UPDATE_DERIVED_ATTR_PREFIXES[upstreamType] : void 0;
26087
+ if (speculationAllowed && derivedPrefixes && [...attrs].some((attr) => derivedPrefixes.some((prefix) => attr.startsWith(prefix)))) {
26088
+ matched = true;
26089
+ break;
26090
+ }
25723
26091
  }
25724
26092
  if (!matched) continue;
25725
26093
  syntheticChanges.push({
@@ -25730,12 +26098,51 @@ var DiffCalculator = class DiffCalculator {
25730
26098
  });
25731
26099
  }
25732
26100
  if (syntheticChanges.length === 0) continue;
26101
+ added = true;
25733
26102
  if (change.changeType === "NO_CHANGE") {
25734
26103
  change.changeType = "UPDATE";
25735
26104
  change.propertyChanges = syntheticChanges;
25736
26105
  this.logger.debug(`UPDATE (in-place attr propagated): ${dependentId} reads a changed attribute of an updated resource`);
25737
26106
  } else change.propertyChanges = [...change.propertyChanges ?? [], ...syntheticChanges];
25738
26107
  }
26108
+ return added;
26109
+ }
26110
+ /**
26111
+ * Whether any `Fn::GetAtt` / `Fn::Sub` read of a dependent (its
26112
+ * {@link extractGetAttRefs} map, every property and every upstream) lands on
26113
+ * a PERSISTED attribute carrying the redaction mask. The attribute is found
26114
+ * the way the resolver finds it: the flat key, else the dot-separated path
26115
+ * walked through nested objects (a Cloud Control record's
26116
+ * `Endpoint.Address`).
26117
+ *
26118
+ * What it cannot see, and the engine still refuses on a speculative
26119
+ * promotion: a masked cross-stack read (`Fn::ImportValue` /
26120
+ * `Fn::GetStackOutput`), whose producer record is another stack's, and a
26121
+ * `Ref` whose state-key lookup lands on a masked property, since `Ref`
26122
+ * reads are not extracted.
26123
+ */
26124
+ static readsMaskedAttribute(perProp, currentResources) {
26125
+ for (const getAttRefs of perProp.values()) for (const [upstreamId, attrs] of getAttRefs) {
26126
+ const attributes = Object.hasOwn(currentResources, upstreamId) ? currentResources[upstreamId]?.attributes : void 0;
26127
+ if (!attributes) continue;
26128
+ for (const attr of attrs) if (carriesSecretMask(DiffCalculator.persistedAttribute(attributes, attr))) return true;
26129
+ }
26130
+ return false;
26131
+ }
26132
+ /**
26133
+ * A persisted attribute by the resolver's lookup rule: the own flat key, else
26134
+ * the dot-separated path through own keys of nested objects; `undefined`
26135
+ * when neither exists.
26136
+ */
26137
+ static persistedAttribute(attributes, attr) {
26138
+ if (Object.hasOwn(attributes, attr)) return attributes[attr];
26139
+ if (!attr.includes(".")) return void 0;
26140
+ let cursor = attributes;
26141
+ for (const part of attr.split(".")) {
26142
+ if (cursor === null || typeof cursor !== "object" || !Object.hasOwn(cursor, part)) return;
26143
+ cursor = cursor[part];
26144
+ }
26145
+ return cursor;
25739
26146
  }
25740
26147
  /**
25741
26148
  * Extract `Fn::GetAtt` / `Fn::Sub`-`${X.Attr}` references from a property value
@@ -25765,6 +26172,10 @@ var DiffCalculator = class DiffCalculator {
25765
26172
  if ("Fn::GetAtt" in obj) {
25766
26173
  const ga = obj["Fn::GetAtt"];
25767
26174
  if (Array.isArray(ga) && typeof ga[0] === "string" && typeof ga[1] === "string") add(ga[0], ga[1]);
26175
+ else if (typeof ga === "string") {
26176
+ const split = splitGetAttStringForm(ga);
26177
+ if (split) add(split.logicalId, split.attributeName);
26178
+ }
25768
26179
  return;
25769
26180
  }
25770
26181
  if ("Fn::Sub" in obj) {
@@ -31104,12 +31515,15 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
31104
31515
  }
31105
31516
  if (resource.resourceType === "AWS::Route53::HostedZone" && attributeName === "NameServers" && typeof flatValue === "string") {
31106
31517
  const nameServers = flatValue === "" ? [] : flatValue.split(",");
31518
+ const notedNameServers = this.noteAttributeSecrecy(logicalId, attributeName, nameServers, context);
31107
31519
  this.logger.debug(`Normalized legacy Fn::GetAtt attribute: ${this.displayMasked(logicalId, context)}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(nameServers, context)), context)}`);
31108
- return this.noteAttributeSecrecy(logicalId, attributeName, nameServers, context);
31520
+ return notedNameServers;
31109
31521
  }
31522
+ const reresolvesNestedReference = resource.resourceType === NESTED_STACK_RESOURCE_TYPE$2 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX) && carriesDynamicReference(flatValue);
31523
+ const notedFlatValue = reresolvesNestedReference ? flatValue : this.noteAttributeSecrecy(logicalId, attributeName, flatValue, context);
31110
31524
  this.logger.debug(`Resolved Fn::GetAtt from attributes: ${this.displayMasked(logicalId, context)}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(flatValue, context)), context)}`);
31111
- if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$2 && 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 }));
31112
- return this.noteAttributeSecrecy(logicalId, attributeName, flatValue, context);
31525
+ if (reresolvesNestedReference) return await this.reresolveCrossStackValue(flatValue, nestedStackChildRegionFromLocalArn(resource.physicalId), context, `nested stack ${logicalId} ${this.displayLeaf(attributeName, context)}`, crossStackSourceKey({ "Fn::GetAtt": getAtt }));
31526
+ return notedFlatValue;
31113
31527
  }
31114
31528
  if (attributeName.includes(".")) {
31115
31529
  const parts = attributeName.split(".");
@@ -31120,8 +31534,9 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
31120
31534
  break;
31121
31535
  }
31122
31536
  if (cursor !== void 0) {
31537
+ const notedCursor = this.noteAttributeSecrecy(logicalId, attributeName, cursor, context);
31123
31538
  this.logger.debug(`Resolved Fn::GetAtt from nested attributes: ${this.displayMasked(logicalId, context)}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(cursor, context)), context)}`);
31124
- return this.noteAttributeSecrecy(logicalId, attributeName, cursor, context);
31539
+ return notedCursor;
31125
31540
  }
31126
31541
  }
31127
31542
  }
@@ -31180,6 +31595,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
31180
31595
  *
31181
31596
  * A context supplying NEITHER field — the diff / no-op resolver, `cdkd
31182
31597
  * scrub`, `cdkd import` — pays two undefined checks and gets its value back.
31598
+ *
31599
+ * ORDERING: call it BEFORE any log line that prints the value
31600
+ * (go-to-k/cdkd#3659). Note 1 is what puts a `NoEcho` value into the bag
31601
+ * `displayMasked` masks against, so a line logged first prints the plaintext
31602
+ * at `--verbose`.
31183
31603
  */
31184
31604
  noteAttributeSecrecy(logicalId, attributeName, value, context) {
31185
31605
  const declared = context.noEchoAttributeResources?.get(logicalId);
@@ -31314,8 +31734,9 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
31314
31734
  }
31315
31735
  /** Serve a value the #1852 heal just read from AWS, logging it like a cached read. */
31316
31736
  serveHealedAttribute(logicalId, attributeName, value, context) {
31737
+ const noted = this.noteAttributeSecrecy(logicalId, attributeName, value, context);
31317
31738
  this.logger.debug(`Resolved Fn::GetAtt from a re-read of AWS (the state record lacked it): ${this.displayMasked(logicalId, context)}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(value, context)), context)}`);
31318
- return this.noteAttributeSecrecy(logicalId, attributeName, value, context);
31739
+ return noted;
31319
31740
  }
31320
31741
  /**
31321
31742
  * {@link constructGuardedAttribute}, healing a STALE state record first when
@@ -35270,6 +35691,15 @@ const CC_PROTECTION_PROPERTIES = {
35270
35691
  function ccProtectionProperty(resourceType) {
35271
35692
  return CC_PROTECTION_PROPERTIES[resourceType];
35272
35693
  }
35694
+ /**
35695
+ * Every resource type registered in the CC protection registry. Consumed by
35696
+ * the cross-site consistency test that keeps this registry, the destroy
35697
+ * confirm-prompt count map, the `--remove-protection` help strings, and the
35698
+ * docs tables from drifting apart.
35699
+ */
35700
+ function ccProtectionRegistryTypes() {
35701
+ return Object.keys(CC_PROTECTION_PROPERTIES);
35702
+ }
35273
35703
 
35274
35704
  //#endregion
35275
35705
  //#region src/provisioning/unsupported-types.generated.ts
@@ -36326,7 +36756,7 @@ var CloudControlProvider = class {
36326
36756
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
36327
36757
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
36328
36758
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
36329
- const { ASGProvider } = await import("./asg-provider-DAvRcbyS.js").then((n) => n.n);
36759
+ const { ASGProvider } = await import("./asg-provider-DR5gOGbi.js").then((n) => n.n);
36330
36760
  const asgProvider = new ASGProvider();
36331
36761
  return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
36332
36762
  }
@@ -41574,7 +42004,8 @@ var ProviderRegistry = class {
41574
42004
  provisionedBy: "sdk"
41575
42005
  };
41576
42006
  }
41577
- if (isNonProvisionable(resourceType) || specificProvider.disableCcApiFallback === true) throw new Error(this.buildUnroutableSilentDropMessage(resourceType, actionableDrops));
42007
+ const unroutable = this.ccRouteUnavailableReason(resourceType);
42008
+ if (unroutable !== void 0) throw new Error(this.buildUnroutableSilentDropMessage(resourceType, actionableDrops, unroutable));
41578
42009
  this.logger.debug(`Auto-routing ${resourceType} via Cloud Control (silent-drop properties: ${actionableDrops.map((d) => d.property).join(", ")})`);
41579
42010
  return {
41580
42011
  provider: this.cloudControlProvider,
@@ -41599,19 +42030,29 @@ var ProviderRegistry = class {
41599
42030
  throw new Error(`No provider available for resource type: ${resourceType}. This resource type is not supported by Cloud Control API and no SDK provider is registered.`);
41600
42031
  }
41601
42032
  /**
42033
+ * Why the Cloud Control auto-route cannot serve `resourceType`, or
42034
+ * `undefined` when it can. The ONE predicate both refusal sites
42035
+ * (`getProviderFor` and `reportSilentDropDecisions`) and the accepted-drop
42036
+ * warn read, so the warn that predicts what a flag-less deploy does cannot
42037
+ * disagree with the refusal that deploy then hits (issue #2792).
42038
+ */
42039
+ ccRouteUnavailableReason(resourceType) {
42040
+ if (isNonProvisionable(resourceType)) return "ProvisioningType: NON_PROVISIONABLE — Cloud Control has no handlers for it";
42041
+ if (this.providers.get(resourceType)?.disableCcApiFallback === true) return "the type's SDK provider opts out of the Cloud Control fallback (disableCcApiFallback)";
42042
+ }
42043
+ /**
41602
42044
  * Error message for a resource whose template uses SDK-provider-unhandled
41603
42045
  * properties on a type where the Cloud Control auto-route (issue #614) is
41604
42046
  * NOT viable — `ProvisioningType: NON_PROVISIONABLE` (no CC handlers) or a
41605
- * provider-level `disableCcApiFallback` opt-out. Includes each property's
42047
+ * provider-level `disableCcApiFallback` opt-out; `reason` is
42048
+ * {@link ccRouteUnavailableReason}'s answer. Includes each property's
41606
42049
  * `unhandledByDesign` rationale so the user sees WHY it is unhandled, plus
41607
42050
  * the `--allow-unsupported-properties` escape hatch (which forces the SDK
41608
42051
  * path and accepts the drop — the provider may still reject the resource
41609
42052
  * if the property is load-bearing, e.g. a non-Lustre FSx variant config).
41610
42053
  */
41611
- buildUnroutableSilentDropMessage(resourceType, drops) {
41612
- const details = drops.map((d) => ` - ${d.property}: ${d.rationale}`).join("\n");
41613
- const overrideHint = drops.map((d) => `${resourceType}:${d.property}`).join(",");
41614
- return `${resourceType} uses properties cdkd's SDK Provider does not handle, and this type cannot fall back to Cloud Control API (${isNonProvisionable(resourceType) ? "ProvisioningType: NON_PROVISIONABLE — Cloud Control has no handlers for it" : "the type's SDK provider opts out of the Cloud Control fallback (disableCcApiFallback)"}):\n${details}\nRemove the properties, or force the SDK provider path and accept the drop via --prefer-sdk-route ${overrideHint} (the provider may still reject the resource if the property is required).`;
42054
+ buildUnroutableSilentDropMessage(resourceType, drops, reason) {
42055
+ return `${resourceType} uses properties cdkd's SDK Provider does not handle, and this type cannot fall back to Cloud Control API (${reason}):\n${drops.map((d) => ` - ${d.property}: ${d.rationale}`).join("\n")}\nRemove the properties, or force the SDK provider path and accept the drop via --prefer-sdk-route ${drops.map((d) => `${resourceType}:${d.property}`).join(",")} (the provider may still reject the resource if the property is required).`;
41615
42056
  }
41616
42057
  /**
41617
42058
  * Legacy entry point that returns just the provider. Delegates to
@@ -41793,8 +42234,8 @@ var ProviderRegistry = class {
41793
42234
  const overridden = stickyCc ? [] : findAcceptedSilentDrops(resourceType, properties, this.allowedUnsupportedProperties);
41794
42235
  const autoRouted = drops.map(({ property }) => property).filter((property) => !this.allowedUnsupportedProperties.has(`${resourceType}:${property}`));
41795
42236
  if (autoRouted.length > 0) {
41796
- const provider = this.providers.get(resourceType);
41797
- if (isNonProvisionable(resourceType) || provider?.disableCcApiFallback === true) throw new Error(`${logicalId}: ${this.buildUnroutableSilentDropMessage(resourceType, drops.filter((d) => autoRouted.includes(d.property)))}`);
42237
+ const unroutable = this.ccRouteUnavailableReason(resourceType);
42238
+ if (unroutable !== void 0) throw new Error(`${logicalId}: ${this.buildUnroutableSilentDropMessage(resourceType, drops.filter((d) => autoRouted.includes(d.property)), unroutable)}`);
41798
42239
  const message = `${logicalId} (${resourceType}): routing via Cloud Control API (cdkd's SDK Provider does not yet wire ${autoRouted.join(", ")} — CC API will forward the full property map. Override via --prefer-sdk-route ${autoRouted.map((p) => `${resourceType}:${p}`).join(",")}.)`;
41799
42240
  if (provisionedBy === "cc-api") this.logger.debug(message);
41800
42241
  else this.logger.info(message);
@@ -41808,9 +42249,11 @@ var ProviderRegistry = class {
41808
42249
  }
41809
42250
  if (overridden.length > 0) {
41810
42251
  const createOnly = getPropertyCoverage(resourceType)?.createOnlyDrops;
41811
- const reroutable = overridden.filter((p) => createOnly?.has(p) !== true);
41812
- const needsRecreate = overridden.filter((p) => createOnly?.has(p) === true);
42252
+ const unroutable = this.ccRouteUnavailableReason(resourceType);
42253
+ const reroutable = unroutable === void 0 ? overridden.filter((p) => createOnly?.has(p) !== true) : [];
42254
+ const needsRecreate = unroutable === void 0 ? overridden.filter((p) => createOnly?.has(p) === true) : [];
41813
42255
  const remedies = [];
42256
+ if (unroutable !== void 0) remedies.push(`This type cannot be routed via Cloud Control API (${unroutable}), so removing the override makes the deploy refuse at pre-flight instead of rerouting; cdkd has no route that applies ${overridden.length === 1 ? "it" : "them"} to this type.`);
41814
42257
  if (reroutable.length > 0) remedies.push(`Remove the override for ${reroutable.join(", ")} to route this resource via Cloud Control API instead.`);
41815
42258
  if (needsRecreate.length > 0) {
41816
42259
  const one = needsRecreate.length === 1;
@@ -49760,7 +50203,9 @@ var DeployEngine = class DeployEngine {
49760
50203
  this.healBaseline = currentState.resources ?? {};
49761
50204
  refuseMalformedOutputs(currentState, stackName, this.stackRegion);
49762
50205
  refuseMalformedResourcesForDeploy(currentState, stackName, this.stackRegion);
50206
+ refuseMalformedResourceEntriesForDeploy(currentState, stackName, this.stackRegion);
49763
50207
  refuseMalformedOrphans(currentState, stackName, this.stackRegion);
50208
+ refuseMalformedOrphanRecords(currentState, stackName, this.stackRegion);
49764
50209
  const migrationPending = currentStateData?.migrationPending ?? false;
49765
50210
  this.logger.debug(`Loaded current state: ${Object.keys(currentState.resources).length} resources`);
49766
50211
  if (this.options.onCurrentStateLoaded) await this.options.onCurrentStateLoaded(stackName, currentStateData?.state);
@@ -51807,5 +52252,5 @@ var DeployEngine = class DeployEngine {
51807
52252
  };
51808
52253
 
51809
52254
  //#endregion
51810
- export { yellow as $, formatError as $a, MIGRATE_TMP_PREFIX as $i, repairMalformedOutputsForReadOnly as $n, WorkGraph as $r, replayWarn as $t, createPreDeleteFinalSnapshot as A, runWithStackAwsClients as Aa, runDockerForeground as Ai, malformedOrphansRefusalMessage as An, scrubResourceRecord as Ar, isTerminationProtectionPropagationError as At, isStatefulRecreateTargetSync as B, LocalInvokeBuildError as Ba, getLegacyStateBucketName as Bi, refuseMalformedNestedChildOutputs as Bn, DEFAULT_STATE_PREFIX as Br, parameterTypeMayLoseSecretIdentity as Bt, namesAnyDeclaredParameter as C, renderAssemblyPathEscape as Ca, finchSecretArgvRefusal as Ci, hasReadableOutputs as Cn, maskSecretsInError as Cr, CloudControlProvider as Ct, PRE_DELETE_SNAPSHOT_TYPES as D, AwsClients as Da, isMalformedEnvKey as Di, malformedExportSourceWarning as Dn, recordMaskOnlyValuesIn as Dr, deleteSkipReason as Dt, ATOMIC_FINAL_SNAPSHOT_TYPES as E, resolveBucketRegion as Ea, isDockerClientEnvKey as Ei, malformedExportNamesWarning as En, recordMaskOnlyValue as Er, deleteIndeterminateGuards as Et, extractDeploymentEventError as F, CrossAccountSecretRefusalError as Fa, AssetManifestLoader as Fi, malformedResourceEntriesWarning as Fn, buildForceUnlockCommand as Fr, coerceParameterTypedValue as Ft, toCfnInteger as G, PartialFailureError as Ga, resolveStateBucketWithDefault as Gi, refuseMalformedOutputsForDestroy as Gn, importableOutputKeys as Gr, resolveExplicitPhysicalId as Gt, WARM_THROUGHPUT_MEMBERS as H, LockError as Ha, resolveAutoAssetStorage as Hi, refuseMalformedOrphansForDestroy as Hn, hasReadableExportSet as Hr, isSettledInstanceState as Ht, makeSiblingClaimReader as I, DependencyError as Ia, getDockerImageBySourceHash as Ii, malformedResourcePropertiesWarning as In, buildLockContentionMessage as Ir, getAccountInfo as It, bold as J, ResourceUpdateNotSupportedError as Ja, stateBucketExistenceConfirmed as Ji, refuseMalformedResourceEntriesForOrphan as Jn, orphansAfterRollback as Jr, coerceCfnInteger as Jt, toFiniteNumber as K, ProvisioningError as Ka, resolveStateBucketWithDefaultAndSource as Ki, refuseMalformedResourceAttributesForOrphan as Kn, importableOutputs as Kr, assertRegionMatch as Kt, planOrphanAdoption as L, DeployCancelledError as La, Synthesizer as Li, malformedResourcesWarning as Ln, forceQuitRecoveryClause as Lr, isImpossibleEmptyStoredAttribute as Lt, refusesFinalSnapshot as M, AssetError as Ma, warnFinchArgvExposure as Mi, malformedOutputsRefusalMessage as Mn, S3StateBackend as Mr, carriesDynamicReference as Mt, unsupportedFinalSnapshotError as N, CdkdError as Na, escapeRegExp$1 as Ni, malformedOutputsWarning as Nn, rebuildClientForBucketRegion as Nr, carriesFetchableDynamicReference as Nt, buildFinalSnapshotIdentifier as O, getAwsClients as Oa, partitionSensitiveEnv as Oi, malformedLocalOutputsWarning as On, recoverMaskedOutput as Or, isWaitAbandonedError as Ot, makeCanonicalizePropertiesFn as P, ConfigError as Pa, stripControlChars as Pi, malformedRenderedContainersWarning as Pn, UNREPRODUCIBLE_LOCK_CLAUSE as Pr, cfnRefValueFromPhysicalId as Pt, red as Q, SynthesisError as Qa, CFN_TEMPLATE_URL_LIMIT as Qi, repairMalformedOrphansForReadOnly as Qn, stringifyValue as Qr, readConfigString as Qt, MULTI_REGION_RECREATE_BLOCKED_TYPES as R, DynamicReferenceRegionAmbiguousError as Ra, synthesisStatusMessage as Ri, malformedStateRefusalMessage as Rn, formatLockExpiry as Rr, isNamelessDynamicReferenceError as Rt, computeParameterTaint as S, displayAssemblyPath as Sa, dockerSpawnEnvWithSensitive as Si, hasReadableOrphans as Sn, markSameGenerationBag as Sr, startInterruptWatch as St, resourcesNamingDeclaredParameter as T, clearBucketRegionCache as Ta, getDockerCmd as Ti, isReadableResourceEntry as Tn, pathCrossesDottedKey as Tr, UNSPECIFIED_SKIP_REASON as Tt, coerceWarmThroughput as U, MalformedProducerRecordRefusalError as Ua, resolveCaptureObservedState as Ui, refuseMalformedOrphansForOrphan as Un, hasReasonlessBaselineRefusal as Ur, WAFv2WebACLProvider as Ut, renderStatefulReason as V, LocalStartServiceError as Va, resolveApp as Vi, refuseMalformedOrphans as Vn, exportNamesCarriedFrom as Vr, refStateLookupFromResource as Vt, isWarmThroughputDecrease as W, NestedStackChildDirectDestroyError as Wa, resolveSkipPrefix as Wi, refuseMalformedOutputs as Wn, hasUnverifiableParameterRefusal as Wr, normalizeAwsTagsToCfn as Wt, gray as X, StackTerminationProtectionError as Xa, ssmResolvedValueType as Xi, refuseMalformedResourcesForDestroy as Xn, shouldRetainResource as Xr, configIntegerRefusal as Xt, cyan as Y, StackHasActiveImportsError as Ya, warnDeprecatedNoPrefixCliFlag as Yi, refuseMalformedResourcePropertiesForOrphan as Yn, orphansCarriedFrom as Yr, configBooleanRefusal as Yt, green as Z, StateError as Za, CFN_TEMPLATE_BODY_LIMIT as Zi, refuseMalformedState as Zn, AssetPublisher as Zr, configStringRefusal as Zt, bagHoldsSecretExpression as _, hasOwnKey as _a, resolveDockerContextDirectory as _i, INTRINSIC_KEYS as _n, dynamicReferenceTokens as _r, maskerOrIdentity as _t, DeploymentEventsStore as a, producerCoordinateKey as aa, BOOTSTRAP_MARKER_PREFIX as ai, withSharedDrainBudget as an, isMarkedNonRetryable as ao, findActionableSilentDrops as ar, secretBearingStateKeyWarning as at, mergeNoChangeOutputs as b, ownValue as ba, describeDockerExecFailure as bi, UNREADABLE_RESOURCES_MAP_ROW as bn, isSingleDynamicReferenceToken as br, interruptWatchListenerCount as bt, replayFailedOperations as c, expectedOwnerParam as ca, getBootstrapMarkerKey as ci, s3BucketDualStackDomainName as cn, isTransientServerError as co, withRetry as cr, IAMRoleProvider as ct, updatePartialMessage as d, derivePartitionAndUrlSuffix as da, readBootstrapMarkerBody as di, applyRoleArnIfSet as dn, retryClassificationText as do, STATE_SOURCED_BASELINE_RULES as dr, definedAttributes as dt, findLargeInlineResources as ea, buildAssetRedirectMap as ei, requireConfigArray as en, isCdkdError as eo, repairMalformedResourceEntriesForReadOnly as er, collectDeclaredOutputNames as et, updatePartialReason as f, AssemblyReader as fa, validateAssetBucketName as fi, AWS_CREDENTIAL_ENV_KEYS as fn, describeAwsFailure as fo, STATE_SOURCED_CROSS_GENERATION_RULES as fr, stringifyIfAssigned as ft, computeImplicitDeleteEdges as g, defineOwnKey as ga, buildDockerImage as gi, DiffCalculator as gn, createSecretMasker as gr, maskDeep as gt, IMPLICIT_DELETE_DEPENDENCIES as h, processStackMessages as ha, assertCdkLocalDockerContextContained as hi, envCredentialsAreAssumedRole as hn, carriesSecretMask as hr, createMaskedRetryLogger as ht, DeploymentEventsReader as i, injectiveKeyPrefix as ia, AssetModeResolver as ii, producerRegionsFromState as in, shellQuote as io, safeStackName as ir, secretBearing as it, isFinalSnapshotError as j, setAwsClients as ja, runDockerStreaming as ji, malformedOrphansWarning as jn, LockManager as jr, IntrinsicFunctionResolver as jt, ccRoutedFinalSnapshotError as k, resetAwsClients as ka, redactDockerArgvValues as ki, malformedOrphanRecordsWarning as kn, redactSecretsForState as kr, disableInstanceApiTermination as kt, replayRollback as l, PARTITION_TABLE as la, isCrossRegionRedirect as li, s3BucketRegionalDomainName as ln, markNonRetryable as lo, DagBuilder as lr, collectInlinePolicyNamesManagedBySiblings as lt, maskingRetryLogger as m, globMatches as ma, buildDenyExternalAccessPolicy as mi, callerEnvCredentials as mn, __exportAll as mo, TEMPLATE_SOURCED_RULES as mr, wouldReturnToSdkProvider as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as na, loadPublishableAssetManifest as ni, requireConfigString as nn, withErrorHandling as no, repairMalformedResourcesForReadOnly as nr, exportAliasCollisionScrubWarning as nt, planFailedOps as o, producerRecordKey as oa, assertAssetBucketRegion as oi, s3BucketArn as on, isRetryableTransientError as oo, findSilentDropProperties as or, secretSafeKeyDisplay as ot, withResourceDeadline as p, failedStageNote as pa, validateContainerRepoName as pi, applyCallerIdentityCredentials as pn, safeStringify$1 as po, STATE_SOURCED_READBACK_RULES as pr, ProviderRegistry as pt, formatResourceLine as q, ResourceTimeoutError as qa, resolveUseCdkBootstrapAssets as qi, refuseMalformedResourceEntries as qn, isReadableBag as qr, coerceCfnBoolean as qt, DeployEngine as r, injectiveKey as ra, rewriteTemplateAssetReferences as ri, classifyReplaySecretRegion as rn, pasteableCommand as ro, safeRegion as rr, isExportAliasCollision as rt, planRollback as s, decodeListingKey as sa, ensureAssetStorage as si, s3BucketDomainName as sn, isThrottlingError as so, describeTypeWithThrottleRetry as sr, getCurrentResourceSecrets as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, uploadCfnTemplate as ta, createAssetRedirectResolver as ti, requireConfigObject as tn, normalizeAwsError as to, repairMalformedResourcePropertiesForReadOnly as tr, collectPublishedOutputNames as tt, resolveReplacementOldType as u, canonicalizeRegion as ua, parseBootstrapMarker as ui, s3BucketWebsiteUrl as un, markRedactedCause as uo, TemplateParser as ur, clearOnUpdateRemoval as ut, isSecretBearingReferenceString as v, hasPlainPrototype$1 as va, warnManifestExecutable as vi, STATE_RESOURCES_MALFORMED as vn, errorCauseChain as vr, beginCommandInterruptScope as vt, reachableDivergentParameters as w, resolveAssemblyPath as wa, formatDockerLoginError as wi, hasReadableResources as wn, maskSecretsInText as wr, slowCcOperationTimeoutMs as wt, bindingSkippedOutputs as x, absoluteAssemblyPathEscape as xa, describeDockerFailure as xi, displayLogicalId as xn, isUncertifiedBaselineMaskPosition as xr, isInterruptedWaitError as xt, keptWholeReasonText as y, nullPrototypeRecord as ya, describeDockerCapturedOutput as yi, UNREADABLE_ORPHANS_CONTAINER_ROW as yn, identityKeyFor as yr, endCommandInterruptScope as yt, isStatefulRecreateTargetForReplace as z, IntrinsicResolutionRefusalError as za, getDefaultStateBucketName as zi, refuseDivergentRecordRegionForDestroy as zn, CUSTOM_RESOURCE_RESPONSE_PREFIX as zr, isUnboundTemplateParameter as zt };
51811
- //# sourceMappingURL=deploy-engine-yukC8N_f.js.map
52255
+ export { yellow as $, NestedStackChildDirectDestroyError as $a, resolveSkipPrefix as $i, refuseMalformedOutputsForDestroy as $n, hasReadableExportSet as $r, readConfigString as $t, createPreDeleteFinalSnapshot as A, absoluteAssemblyPathEscape as Aa, describeDockerFailure as Ai, malformedExportSourceWarning as An, identityKeyFor as Ar, disableInstanceApiTermination as At, isStatefulRecreateTargetSync as B, setAwsClients as Ba, runDockerStreaming as Bi, malformedResourceEntriesWarning as Bn, redactSecretsForState as Br, isUnboundTemplateParameter as Bt, namesAnyDeclaredParameter as C, globMatches as Ca, validateAssetBucketName as Ci, displayLogicalId as Cn, __exportAll as Co, STATE_SOURCED_CROSS_GENERATION_RULES as Cr, CloudControlProvider as Ct, PRE_DELETE_SNAPSHOT_TYPES as D, hasPlainPrototype$1 as Da, resolveDockerContextDirectory as Di, isReadableOrphanRecord as Dn, createSecretMasker as Dr, deleteIndeterminateGuards as Dt, ATOMIC_FINAL_SNAPSHOT_TYPES as E, hasOwnKey as Ea, buildDockerImage as Ei, hasReadableResources as En, carriesSecretMask as Er, UNSPECIFIED_SKIP_REASON as Et, extractDeploymentEventError as F, resolveBucketRegion as Fa, isDockerClientEnvKey as Fi, malformedOrphansRefusalMessage as Fn, maskSecretsInText as Fr, cfnRefValueFromPhysicalId as Ft, toCfnInteger as G, DependencyError as Ga, getDockerImageBySourceHash as Gi, refuseDivergentRecordRegionForDestroy as Gn, UNREPRODUCIBLE_LOCK_CLAUSE as Gr, normalizeAwsTagsToCfn as Gt, WARM_THROUGHPUT_MEMBERS as H, CdkdError as Ha, escapeRegExp$1 as Hi, malformedResourcesWarning as Hn, LockManager as Hr, refStateLookupFromResource as Ht, makeSiblingClaimReader as I, AwsClients as Ia, isMalformedEnvKey as Ii, malformedOrphansWarning as In, pathCrossesDottedKey as Ir, coerceParameterTypedValue as It, bold as J, IntrinsicResolutionRefusalError as Ja, getDefaultStateBucketName as Ji, refuseMalformedOrphanRecordsForDestroy as Jn, forceQuitRecoveryClause as Jr, coerceCfnBoolean as Jt, toFiniteNumber as K, DeployCancelledError as Ka, Synthesizer as Ki, refuseMalformedNestedChildOutputs as Kn, buildForceUnlockCommand as Kr, resolveExplicitPhysicalId as Kt, planOrphanAdoption as L, getAwsClients as La, partitionSensitiveEnv as Li, malformedOutputsRefusalMessage as Ln, recordMaskOnlyValue as Lr, getAccountInfo as Lt, refusesFinalSnapshot as M, renderAssemblyPathEscape as Ma, finchSecretArgvRefusal as Mi, malformedOrphanRecordsRefusalMessage as Mn, isUncertifiedBaselineMaskPosition as Mr, IntrinsicFunctionResolver as Mt, unsupportedFinalSnapshotError as N, resolveAssemblyPath as Na, formatDockerLoginError as Ni, malformedOrphanRecordsWarning as Nn, markSameGenerationBag as Nr, carriesDynamicReference as Nt, buildFinalSnapshotIdentifier as O, nullPrototypeRecord as Oa, describeDockerCapturedOutput as Oi, isReadableResourceEntry as On, dynamicReferenceTokens as Or, deleteSkipReason as Ot, makeCanonicalizePropertiesFn as P, clearBucketRegionCache as Pa, getDockerCmd as Pi, malformedOrphanRowsKeptWarning as Pn, maskSecretsInError as Pr, carriesFetchableDynamicReference as Pt, red as Q, MalformedProducerRecordRefusalError as Qa, resolveCaptureObservedState as Qi, refuseMalformedOutputs as Qn, exportNamesCarriedFrom as Qr, configStringRefusal as Qt, MULTI_REGION_RECREATE_BLOCKED_TYPES as R, resetAwsClients as Ra, redactDockerArgvValues as Ri, malformedOutputsWarning as Rn, recordMaskOnlyValuesIn as Rr, isImpossibleEmptyStoredAttribute as Rt, computeParameterTaint as S, failedStageNote as Sa, readBootstrapMarkerBody as Si, deployRefusesOrphanRowsReason as Sn, safeStringify$1 as So, STATE_SOURCED_BASELINE_RULES as Sr, startInterruptWatch as St, resourcesNamingDeclaredParameter as T, defineOwnKey as Ta, buildDenyExternalAccessPolicy as Ti, hasReadableOutputs as Tn, TEMPLATE_SOURCED_RULES as Tr, ccProtectionRegistryTypes as Tt, coerceWarmThroughput as U, ConfigError as Ua, stripControlChars as Ui, malformedStateRefusalMessage as Un, S3StateBackend as Ur, isSettledInstanceState as Ut, renderStatefulReason as V, AssetError as Va, warnFinchArgvExposure as Vi, malformedResourcePropertiesWarning as Vn, scrubResourceRecord as Vr, parameterTypeMayLoseSecretIdentity as Vt, isWarmThroughputDecrease as W, CrossAccountSecretRefusalError as Wa, AssetManifestLoader as Wi, previewableOrphanRecords as Wn, rebuildClientForBucketRegion as Wr, WAFv2WebACLProvider as Wt, gray as X, LocalStartServiceError as Xa, resolveApp as Xi, refuseMalformedOrphansForDestroy as Xn, CUSTOM_RESOURCE_RESPONSE_PREFIX as Xr, configBooleanRefusal as Xt, cyan as Y, LocalInvokeBuildError as Ya, getLegacyStateBucketName as Yi, refuseMalformedOrphans as Yn, formatLockExpiry as Yr, coerceCfnInteger as Yt, green as Z, LockError as Za, resolveAutoAssetStorage as Zi, refuseMalformedOrphansForOrphan as Zn, DEFAULT_STATE_PREFIX as Zr, configIntegerRefusal as Zt, bagHoldsSecretExpression as _, expectedOwnerParam as _a, assertAssetBucketRegion as _i, DiffCalculator as _n, isTransientServerError as _o, findSilentDropProperties as _r, maskerOrIdentity as _t, DeploymentEventsStore as a, ssmResolvedValueType as aa, orphansAfterRollback as ai, producerRegionsFromState as an, StackTerminationProtectionError as ao, refuseMalformedState as ar, secretBearingStateKeyWarning as at, mergeNoChangeOutputs as b, derivePartitionAndUrlSuffix as ba, isCrossRegionRedirect as bi, UNREADABLE_ORPHANS_CONTAINER_ROW as bn, retryClassificationText as bo, DagBuilder as br, interruptWatchListenerCount as bt, replayFailedOperations as c, MIGRATE_TMP_PREFIX as ca, AssetPublisher as ci, s3BucketDomainName as cn, formatError as co, repairMalformedOutputsForReadOnly as cr, IAMRoleProvider as ct, updatePartialMessage as d, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as da, buildAssetRedirectMap as di, s3BucketWebsiteUrl as dn, withErrorHandling as do, repairMalformedResourcesForReadOnly as dr, definedAttributes as dt, resolveStateBucketWithDefault as ea, hasReasonlessBaselineRefusal as ei, replayWarn as en, PartialFailureError as eo, refuseMalformedResourceAttributesForOrphan as er, collectDeclaredOutputNames as et, updatePartialReason as f, injectiveKey as fa, createAssetRedirectResolver as fi, applyRoleArnIfSet as fn, pasteableCommand as fo, safeRegion as fr, stringifyIfAssigned as ft, computeImplicitDeleteEdges as g, decodeListingKey as ga, BOOTSTRAP_MARKER_PREFIX as gi, envCredentialsAreAssumedRole as gn, isThrottlingError as go, findActionableSilentDrops as gr, maskDeep as gt, IMPLICIT_DELETE_DEPENDENCIES as h, producerRecordKey as ha, AssetModeResolver as hi, callerEnvCredentials as hn, isRetryableTransientError as ho, unreadableOrphanRecords as hr, createMaskedRetryLogger as ht, DeploymentEventsReader as i, warnDeprecatedNoPrefixCliFlag as ia, isReadableBag as ii, classifyReplaySecretRegion as in, StackHasActiveImportsError as io, refuseMalformedResourcesForDestroy as ir, secretBearing as it, isFinalSnapshotError as j, displayAssemblyPath as ja, dockerSpawnEnvWithSensitive as ji, malformedLocalOutputsWarning as jn, isSingleDynamicReferenceToken as jr, isTerminationProtectionPropagationError as jt, ccRoutedFinalSnapshotError as k, ownValue as ka, describeDockerExecFailure as ki, malformedExportNamesWarning as kn, errorCauseChain as kr, isWaitAbandonedError as kt, replayRollback as l, findLargeInlineResources as la, stringifyValue as li, s3BucketDualStackDomainName as ln, isCdkdError as lo, repairMalformedResourceEntriesForReadOnly as lr, collectInlinePolicyNamesManagedBySiblings as lt, maskingRetryLogger as m, producerCoordinateKey as ma, rewriteTemplateAssetReferences as mi, applyCallerIdentityCredentials as mn, isMarkedNonRetryable as mo, unpreviewableOrphanRecords as mr, wouldReturnToSdkProvider as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, resolveUseCdkBootstrapAssets as na, importableOutputKeys as ni, requireConfigObject as nn, ResourceTimeoutError as no, refuseMalformedResourceEntriesForOrphan as nr, exportAliasCollisionScrubWarning as nt, planFailedOps as o, CFN_TEMPLATE_BODY_LIMIT as oa, orphansCarriedFrom as oi, withSharedDrainBudget as on, StateError as oo, repairMalformedOrphanRecordsForReadOnly as or, secretSafeKeyDisplay as ot, withResourceDeadline as p, injectiveKeyPrefix as pa, loadPublishableAssetManifest as pi, AWS_CREDENTIAL_ENV_KEYS as pn, shellQuote as po, safeStackName as pr, ProviderRegistry as pt, formatResourceLine as q, DynamicReferenceRegionAmbiguousError as qa, synthesisStatusMessage as qi, refuseMalformedOrphanRecords as qn, buildLockContentionMessage as qr, assertRegionMatch as qt, DeployEngine as r, stateBucketExistenceConfirmed as ra, importableOutputs as ri, requireConfigString as rn, ResourceUpdateNotSupportedError as ro, refuseMalformedResourcePropertiesForOrphan as rr, isExportAliasCollision as rt, planRollback as s, CFN_TEMPLATE_URL_LIMIT as sa, shouldRetainResource as si, s3BucketArn as sn, SynthesisError as so, repairMalformedOrphansForReadOnly as sr, getCurrentResourceSecrets as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, resolveStateBucketWithDefaultAndSource as ta, hasUnverifiableParameterRefusal as ti, requireConfigArray as tn, ProvisioningError as to, refuseMalformedResourceEntries as tr, collectPublishedOutputNames as tt, resolveReplacementOldType as u, uploadCfnTemplate as ua, WorkGraph as ui, s3BucketRegionalDomainName as un, normalizeAwsError as uo, repairMalformedResourcePropertiesForReadOnly as ur, clearOnUpdateRemoval as ut, isSecretBearingReferenceString as v, PARTITION_TABLE as va, ensureAssetStorage as vi, INTRINSIC_KEYS as vn, markNonRetryable as vo, describeTypeWithThrottleRetry as vr, beginCommandInterruptScope as vt, reachableDivergentParameters as w, processStackMessages as wa, validateContainerRepoName as wi, hasReadableOrphans as wn, STATE_SOURCED_READBACK_RULES as wr, slowCcOperationTimeoutMs as wt, bindingSkippedOutputs as x, AssemblyReader as xa, parseBootstrapMarker as xi, UNREADABLE_RESOURCES_MAP_ROW as xn, describeAwsFailure as xo, TemplateParser as xr, isInterruptedWaitError as xt, keptWholeReasonText as y, canonicalizeRegion as ya, getBootstrapMarkerKey as yi, STATE_RESOURCES_MALFORMED as yn, markRedactedCause as yo, withRetry as yr, endCommandInterruptScope as yt, isStatefulRecreateTargetForReplace as z, runWithStackAwsClients as za, runDockerForeground as zi, malformedRenderedContainersWarning as zn, recoverMaskedOutput as zr, isNamelessDynamicReferenceError as zt };
52256
+ //# sourceMappingURL=deploy-engine-BwDlgFRs.js.map