@go-to-k/cdkd 0.290.30 → 0.290.32

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, E as truncateCodePoints, S as UNRENDERABLE, T as displaySafe, d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS, y as withoutGeneratedFallbackName } from "./logger-BjWM-UVj.js";
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-BX4pJk0T.js";
2
2
  import { a as setAssumedRoleCredentials, i as getPreAssumeEnvCredentials, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-BoIxyCir.js";
3
- import { t as getCdkdVersion } from "./version-C15BftsD.js";
3
+ import { t as getCdkdVersion } from "./version-F41ektvu.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";
@@ -9980,18 +9980,54 @@ function buildForceUnlockCommand(stackName, region, recovery) {
9980
9980
  const stackIsExact = safeStack === stackName;
9981
9981
  const safeRegion = region === void 0 ? void 0 : displaySafe(region, { asciiOnly: true });
9982
9982
  const regionIsExact = region === void 0 || safeRegion === region;
9983
- const safeRecovery = {
9984
- profile: sanitizeRecoveryValue(recovery?.profile),
9985
- stateBucket: sanitizeRecoveryValue(recovery?.stateBucket),
9986
- statePrefix: sanitizeRecoveryValue(recovery?.statePrefix)
9983
+ const recoveryFlags = recoveryCommandFlags(recovery);
9984
+ if (!safeStack || safeRegion === "" || !stackIsExact || !regionIsExact || !recoveryFlags.exact) return "";
9985
+ return [safeRegion === void 0 ? `cdkd force-unlock ${shellQuote(safeStack)}` : `cdkd force-unlock ${shellQuote(safeStack)} --stack-region ${shellQuote(safeRegion)}`, ...recoveryFlags.flags].join(" ");
9986
+ }
9987
+ /**
9988
+ * A placeholder for a value a pasteable command could not name, QUOTED.
9989
+ *
9990
+ * A bare `<profile>` is two shell redirections, not a word: pasted, `<profile`
9991
+ * reads stdin from a file named `profile` and `>` sends stdout to whatever
9992
+ * word follows. While the hole was the command's LAST word that was only a
9993
+ * syntax error; with flags appended after it (`--profile <profile>
9994
+ * --state-bucket b`) it ran the command with `--state-bucket` swallowed as a
9995
+ * redirect target and `b` as the profile — a delete against the ambient
9996
+ * bucket, with nothing printed (measured by the maintainer, M4 of the
9997
+ * go-to-k/cdkd#3363 review). Quoted, it pastes as one literal argument and
9998
+ * nothing else — no redirection, no swallowed flag. It is not refused: an
9999
+ * UNFILLED hole runs as that literal value (a stack named `<stack>`, a prefix
10000
+ * `<prefix>`).
10001
+ */
10002
+ function commandHole(name) {
10003
+ return `'<${name}>'`;
10004
+ }
10005
+ /**
10006
+ * The flags that pin a pasteable state command to the caller's account and
10007
+ * bucket: `--profile`, the resolved `--state-bucket`, and a NON-default
10008
+ * `--state-prefix` (see {@link LockRecoveryContext} for why each is emitted
10009
+ * when it is). ONE spelling, shared by {@link buildForceUnlockCommand} and the
10010
+ * `cdkd orphan` properties refusal `malformed-resources-bag.ts` builds
10011
+ * (go-to-k/cdkd#3363): after `cdkd orphan --profile prod ...` a remedy that
10012
+ * carried none of these resolved the DEFAULT profile's bucket when pasted.
10013
+ * Each fragment takes {@link sanitizeRecoveryValue}'s sanitize + exactness
10014
+ * pair (go-to-k/cdkd#3377); what a caller does with an inexact one is its
10015
+ * own call — `buildForceUnlockCommand` suppresses the whole command, the orphan
10016
+ * refusal prints the hole.
10017
+ */
10018
+ function recoveryCommandFlags(recovery) {
10019
+ const profile = sanitizeRecoveryValue(recovery?.profile);
10020
+ const stateBucket = sanitizeRecoveryValue(recovery?.stateBucket);
10021
+ const statePrefix = sanitizeRecoveryValue(recovery?.statePrefix);
10022
+ const flags = [];
10023
+ if (profile.text || !profile.exact) flags.push(profile.exact ? `--profile ${shellQuote(profile.text)}` : `--profile ${commandHole("profile")}`);
10024
+ if (stateBucket.text || !stateBucket.exact) flags.push(stateBucket.exact ? `--state-bucket ${shellQuote(stateBucket.text)}` : `--state-bucket ${commandHole("bucket")}`);
10025
+ const prefix = recovery?.statePrefix;
10026
+ if (prefix !== void 0 && prefix !== "cdkd") flags.push(statePrefix.exact ? `--state-prefix ${shellQuote(statePrefix.text)}` : `--state-prefix ${commandHole("prefix")}`);
10027
+ return {
10028
+ flags,
10029
+ exact: profile.exact && stateBucket.exact && statePrefix.exact
9987
10030
  };
9988
- const recoveryIsExact = Object.values(safeRecovery).every((v) => v.exact);
9989
- if (!safeStack || safeRegion === "" || !stackIsExact || !regionIsExact || !recoveryIsExact) return "";
9990
- const parts = [safeRegion === void 0 ? `cdkd force-unlock ${shellQuote(safeStack)}` : `cdkd force-unlock ${shellQuote(safeStack)} --stack-region ${shellQuote(safeRegion)}`];
9991
- if (safeRecovery.profile.text) parts.push(`--profile ${shellQuote(safeRecovery.profile.text)}`);
9992
- if (safeRecovery.stateBucket.text) parts.push(`--state-bucket ${shellQuote(safeRecovery.stateBucket.text)}`);
9993
- if (safeRecovery.statePrefix.text && safeRecovery.statePrefix.text !== "cdkd") parts.push(`--state-prefix ${shellQuote(safeRecovery.statePrefix.text)}`);
9994
- return parts.join(" ");
9995
10031
  }
9996
10032
  /**
9997
10033
  * Sanitize ONE {@link LockRecoveryContext} fragment and report whether that
@@ -17109,7 +17145,8 @@ var DagBuilder = class {
17109
17145
  resourceIds.forEach((logicalId) => {
17110
17146
  const resource = this.parser.getResource(template, logicalId);
17111
17147
  graph.setNode(logicalId, resource);
17112
- this.logger.debug(`Added node: ${logicalId} (${resource?.Type})`);
17148
+ const typeText = resource?.Type == null ? "<no Type>" : displayIdent(resource.Type);
17149
+ this.logger.debug(`Added node: ${displayIdent(logicalId)} (${typeText})`);
17113
17150
  });
17114
17151
  this.logger.debug(`Total nodes: ${resourceIds.length}`);
17115
17152
  const parameterNames = new Set(Object.keys(template.Parameters ?? {}));
@@ -17123,15 +17160,15 @@ var DagBuilder = class {
17123
17160
  for (const depId of dependencies) {
17124
17161
  if (skip?.has(depId)) {
17125
17162
  relaxedEdgeCount++;
17126
- this.logger.debug(`Skipped CDK-defensive DependsOn edge: ${depId} -> ${logicalId} (default; opt out with --no-aggressive-vpc-parallel)`);
17163
+ this.logger.debug(`Skipped CDK-defensive DependsOn edge: ${displayIdent(depId)} -> ${displayIdent(logicalId)} (default; opt out with --no-aggressive-vpc-parallel)`);
17127
17164
  continue;
17128
17165
  }
17129
17166
  if (graph.hasNode(depId)) {
17130
17167
  graph.setEdge(depId, logicalId);
17131
17168
  edgeCount++;
17132
- this.logger.debug(`Added edge: ${depId} -> ${logicalId}`);
17133
- } else if (parameterNames.has(depId)) this.logger.debug(`Skipped Parameter reference: ${logicalId} -> ${depId}`);
17134
- else this.logger.warn(`Resource ${logicalId} depends on ${depId}, but ${depId} not found in template`);
17169
+ this.logger.debug(`Added edge: ${displayIdent(depId)} -> ${displayIdent(logicalId)}`);
17170
+ } else if (parameterNames.has(depId)) this.logger.debug(`Skipped Parameter reference: ${displayIdent(logicalId)} -> ${displayIdent(depId)}`);
17171
+ else this.logger.warn(`Resource ${displayIdent(logicalId)} depends on ${displayIdent(depId)}, but ${displayIdent(depId)} not found in template`);
17135
17172
  }
17136
17173
  }
17137
17174
  if (relaxedEdgeCount > 0) this.logger.info(`[DagBuilder] Relaxed ${relaxedEdgeCount} CDK-defensive DependsOn edge(s) (default; opt out with --no-aggressive-vpc-parallel)`);
@@ -17140,7 +17177,7 @@ var DagBuilder = class {
17140
17177
  edgeCount += this.addLambdaVpcEdges(graph, template);
17141
17178
  if (!alg.isAcyclic(graph)) {
17142
17179
  const cycles = this.findCycles(graph);
17143
- throw new DependencyError(`Circular dependency detected in template. Cycles: ${cycles.map((c) => c.join(" -> ")).join("; ")}`);
17180
+ throw new DependencyError(`Circular dependency detected in template. Cycles: ${cycles.map((c) => c.map((n) => displayIdent(n)).join(" -> ")).join("; ")}`);
17144
17181
  }
17145
17182
  return graph;
17146
17183
  }
@@ -17172,9 +17209,9 @@ var DagBuilder = class {
17172
17209
  });
17173
17210
  if (readyNodes.length === 0) {
17174
17211
  const remaining = graphCopy.nodes();
17175
- throw new DependencyError(`Circular dependency detected. Remaining nodes: ${remaining.join(", ")}`);
17212
+ throw new DependencyError(`Circular dependency detected. Remaining nodes: ${remaining.map((n) => displayIdent(n)).join(", ")}`);
17176
17213
  }
17177
- this.logger.debug(`Level ${levelNum}: ${readyNodes.length} resources - ${readyNodes.join(", ")}`);
17214
+ this.logger.debug(`Level ${levelNum}: ${readyNodes.length} resources - ${readyNodes.map((n) => displayIdent(n)).join(", ")}`);
17178
17215
  levels.push(readyNodes);
17179
17216
  readyNodes.forEach((node) => {
17180
17217
  graphCopy.removeNode(node);
@@ -17291,7 +17328,7 @@ var DagBuilder = class {
17291
17328
  if (graph.hasEdge(policyId, logicalId)) continue;
17292
17329
  graph.setEdge(policyId, logicalId);
17293
17330
  added++;
17294
- this.logger.debug(`Added implicit edge (custom resource policy): ${policyId} -> ${logicalId}`);
17331
+ this.logger.debug(`Added implicit edge (custom resource policy): ${displayIdent(policyId)} -> ${displayIdent(logicalId)}`);
17295
17332
  }
17296
17333
  }
17297
17334
  if (added > 0) this.logger.debug(`Added ${added} implicit edges for custom resource policies`);
@@ -17322,7 +17359,7 @@ var DagBuilder = class {
17322
17359
  if (graph.hasEdge(depId, dependentId)) continue;
17323
17360
  graph.setEdge(depId, dependentId);
17324
17361
  added++;
17325
- this.logger.debug(`Added implicit edge (lambda vpc): ${depId} -> ${dependentId}`);
17362
+ this.logger.debug(`Added implicit edge (lambda vpc): ${displayIdent(depId)} -> ${displayIdent(dependentId)}`);
17326
17363
  }
17327
17364
  if (added > 0) this.logger.debug(`Added ${added} implicit edges for Lambda VpcConfig`);
17328
17365
  return added;
@@ -21846,19 +21883,70 @@ function stackClause(stackName, region) {
21846
21883
  * `destroy-runner.ts`; since go-to-k/cdkd#3328 the first operand is the S3
21847
21884
  * KEY's region rather than the record body's, which is a key SEGMENT and so
21848
21885
  * still plantable) -- its stack name is the caller's, so the region alone is
21849
- * what would aim a destructive command using a value cdkd does not own. This one is reached only with the caller's
21850
- * synthesized stack and `pickStackRegion`'s answer, so substituting is safe and
21851
- * omitting the region is the wider action. Two opposite precedents in one
21852
- * module; neither is the general rule.
21886
+ * what would aim a destructive command using a value cdkd does not own. This
21887
+ * one is reached only with the caller's synthesized stack and
21888
+ * `pickStackRegion`'s answer, so omitting the region is the wider action. Two
21889
+ * opposite precedents in one module; neither is the general rule.
21890
+ *
21891
+ * **Substitution is GATED on the identity rendering EXACTLY, at
21892
+ * `STACK_REF_MAX_CODE_POINTS`** (go-to-k/cdkd#3360, and the M0 finding of the
21893
+ * go-to-k/cdkd#3363 review). The synthesized name is not validated: a prebuilt
21894
+ * cloud assembly's manifest reaches `cdkd orphan` unread, so a name `safeIdentifier`
21895
+ * would TRIM (`'prod-api '`) substitutes as a healthy sibling's, and one it would
21896
+ * TRUNCATE (a 166-code-point nested name at the 128 default) becomes a command
21897
+ * `cdkd state orphan` resolves to zero records and reports as a skip at exit 0.
21898
+ * Since go-to-k/cdkd#3359 the legacy shape carries no `--stack-region`, so the
21899
+ * name is the ONLY thing narrowing this DELETE. The template arm is the fallback,
21900
+ * and the caller says where to take the name from instead
21901
+ * ({@link rendersExactly} is the test, shared with the inspect line and the
21902
+ * object path).
21903
+ *
21904
+ * `recovery` qualifies EVERY arm, the no-identity template included, the way
21905
+ * {@link buildForceUnlockCommand} is qualified, through the same `recoveryCommandFlags`: `cdkd state orphan`
21906
+ * re-resolves the bucket from the ambient profile, so a remedy pasted after
21907
+ * `cdkd orphan --profile prod ...` would otherwise address the default
21908
+ * profile's account. The template keeps them too: the identity is the hole,
21909
+ * not the account, and an operator who fills only the hole would otherwise run
21910
+ * an UNQUALIFIED delete — which `cdkd state orphan` reports as a skip at exit 0
21911
+ * when the default bucket has no such record, and offers to delete when it has
21912
+ * one (the M3 finding of the go-to-k/cdkd#3363 review).
21913
+ */
21914
+ function dropRecordCommand(stackName, region, recovery) {
21915
+ if (stackName === void 0 || stackName === "") return [`cdkd state orphan ${commandHole("stack")} --stack-region ${commandHole("region")}`, ...recoveryCommandFlags(recovery).flags].join(" ");
21916
+ const known = region === void 0 || region === "" ? void 0 : region;
21917
+ const regionExact = known === void 0 || rendersExactly(known);
21918
+ if (!rendersExactly(stackName) || !regionExact) {
21919
+ const flag = known === void 0 ? "" : regionExact ? ` --stack-region ${shellQuote(known)}` : ` --stack-region ${commandHole("region")}`;
21920
+ return [`cdkd state orphan ${commandHole("stack")}${flag}`, ...recoveryCommandFlags(recovery).flags].join(" ");
21921
+ }
21922
+ const flag = known === void 0 ? "" : ` --stack-region ${shellQuote(known)}`;
21923
+ return [`cdkd state orphan ${shellQuote(stackName)}${flag}`, ...recoveryCommandFlags(recovery).flags].join(" ");
21924
+ }
21925
+ /**
21926
+ * Whether an identity survives {@link safeIdentifier} UNCHANGED at the
21927
+ * state-record grammar's cap, so a command or a key built from it addresses the
21928
+ * record the message is about. The cap is `STACK_REF_MAX_CODE_POINTS`, not the
21929
+ * 128 the prose uses: `cdkd orphan` reads a PREBUILT assembly's stack name
21930
+ * unvalidated, so a manifest can carry one past 128 that is nonetheless the
21931
+ * record's real key, and truncating it names nothing — the same bound
21932
+ * `malformedDestroyResourcesRefusalMessage` uses, reached from a different
21933
+ * source.
21934
+ *
21935
+ * What it covers: every identity the `cdkd orphan` properties refusal builds
21936
+ * something PASTEABLE from — the drop command ({@link dropRecordCommand}), the
21937
+ * `cdkd state show` line ({@link orphanInspectCommand}) and the object path
21938
+ * ({@link orphanInspectClause}) — so no two of them can disagree about a name
21939
+ * (go-to-k/cdkd#3363 review, M0 and M2). What it does NOT cover yet: the
21940
+ * destroy refusal and the divergent-region refusal above still spell the same
21941
+ * test inline, and the shared {@link inspectCommand} is ungated at 128 for its
21942
+ * other callers.
21853
21943
  */
21854
- function dropRecordCommand(stackName, region) {
21855
- if (stackName === void 0 || stackName === "") return "cdkd state orphan <stack> --stack-region <region>";
21856
- const flag = region === void 0 || region === "" ? "" : ` --stack-region ${shellQuote(safeIdentifier(region))}`;
21857
- return `cdkd state orphan ${shellQuote(safeIdentifier(stackName))}${flag}`;
21944
+ function rendersExactly(value) {
21945
+ return safeIdentifier(value, STACK_REF_MAX_CODE_POINTS) === value;
21858
21946
  }
21859
21947
  /** The remedy command {@link stackClause}'s message ends on. */
21860
21948
  function inspectCommand(stackName, region) {
21861
- if (stackName === void 0) return "cdkd state show <stack> --stack-region <region> --json";
21949
+ if (stackName === void 0) return `cdkd state show ${commandHole("stack")} --stack-region ${commandHole("region")} --json`;
21862
21950
  const flag = region === void 0 ? "" : ` --stack-region ${shellQuote(safeIdentifier(region))}`;
21863
21951
  return `cdkd state show ${shellQuote(safeIdentifier(stackName))}${flag} --json`;
21864
21952
  }
@@ -22163,10 +22251,144 @@ function malformedResourcePropertiesRefusalMessage(stackName, region, logicalIds
22163
22251
  * stack name and the region `pickStackRegion` settled on, never the record's
22164
22252
  * own unvalidated self-report. See {@link stackClause}.
22165
22253
  */
22166
- function malformedOrphanResourcePropertiesRefusalMessage(rawStackName, rawRegion, logicalIds) {
22254
+ function malformedOrphanResourcePropertiesRefusalMessage(rawStackName, rawRegion, logicalIds, recovery) {
22167
22255
  const stackName = rawStackName === "" ? void 0 : rawStackName;
22168
22256
  const region = rawRegion === "" ? void 0 : rawRegion;
22169
- return `${namedPropertyBagsClause(stackName, region, logicalIds)} 'cdkd orphan' REWRITES and SAVES every record it keeps, so it refuses rather than continuing — under '--dry-run' too, because the rewrite audit table a dry run prints is the wrong one. It is not that the save would fabricate a map: an unreadable bag is carried through VERBATIM. It is that this command exists to leave the record deployable by rewriting every reference to an orphaned resource, and a map it cannot read hides whichever references it holds — a string or a number presents none to find, and a list is walked, so its rewrites are recorded into a container that is still not a map. Continuing would rewrite, save, and report success over a record 'cdkd deploy' then REFUSES. No state was written. Two ways out need no CDK app: repair the record by hand, or drop it whole with '${dropRecordCommand(stackName, region)}', which leaves the live AWS resources standing. A third works only while the CDK app STILL DECLARES the named resource — this refusal covers just the records that would SURVIVE the save, so 'cdkd orphan <its construct path>' removes it and repairs the rest; construct paths come from the synthesized template, so a resource the app no longer declares has none and must take one of the first two. Inspect the record with: ${inspectCommand(stackName, region)}`;
22257
+ const inspect = orphanInspectClause(stackName, region, recovery);
22258
+ const listCommand = withheldIdentityListCommand(stackName, region, recovery);
22259
+ const commands = [
22260
+ `Drop the record: ${dropRecordCommand(stackName, region, recovery)}`,
22261
+ ...listCommand === void 0 ? [] : [`Find the exact name: ${listCommand}`],
22262
+ ...inspect.command === void 0 ? [] : [`Inspect the record: ${inspect.command}`]
22263
+ ];
22264
+ return [
22265
+ `${namedPropertyBagsClause(stackName, region, logicalIds)} 'cdkd orphan' REWRITES and SAVES every record it keeps, so it refuses rather than continuing — under '--dry-run' too, because the rewrite audit table a dry run prints is the wrong one. It is not that the save would fabricate a map: an unreadable bag is carried through VERBATIM. It is that this command exists to leave the record deployable by rewriting every reference to an orphaned resource, and a map it cannot read hides whichever references it holds — a string or a number presents none to find, and a list is walked, so its rewrites are recorded into a container that is still not a map. Continuing would rewrite, save, and report success over a record 'cdkd deploy' then REFUSES. No state was written. Two ways out need no CDK app: repair the record by hand, or drop it whole with the 'Drop the record' command below, which leaves the live AWS resources standing${withheldIdentityClause(stackName, region)}${withheldRecoveryClause(recovery)}. A third works only while the CDK app STILL DECLARES the named resource — this refusal covers just the records that would SURVIVE the save, so 'cdkd orphan <its construct path>' removes it and repairs the rest; construct paths come from the synthesized template, so a resource the app no longer declares has none and must take one of the first two.` + (inspect.sentence === void 0 ? "" : ` ${inspect.sentence}`),
22266
+ ...commands,
22267
+ ...inspect.locations ?? []
22268
+ ].join("\n");
22269
+ }
22270
+ /**
22271
+ * The half-sentence that accompanies a TEMPLATE drop remedy when a stack name
22272
+ * WAS known but it, or the region beside it, did not render exactly. The prose
22273
+ * above it prints the sanitized spelling, and an operator filling the template
22274
+ * from that spelling would be aiming at the healthy sibling the gate exists to
22275
+ * protect (`'prod-api '` renders as `prod-api`; a padded region is the same
22276
+ * misdirection one flag over), so the message says where to take them from
22277
+ * instead: `cdkd state list --json`, which writes the raw name
22278
+ * through `JSON.stringify` — `--long` renders through `displayIdent`, which
22279
+ * trims, so it would hand back the same spelling. Empty when the command was
22280
+ * substituted, and when no name was known at all — that arm's template already
22281
+ * reads as a hole to fill.
22282
+ */
22283
+ function withheldIdentityClause(stackName, region) {
22284
+ if (!identityWithheld(stackName, region)) return "";
22285
+ return " — the stack name or region above did not render exactly, so take them from the 'Find the exact name' command below — replace each quoted hole, quotes included, with the shell-quoted value — rather than from this message";
22286
+ }
22287
+ /**
22288
+ * The half-sentence for the OTHER thing that can print as a hole: one of the
22289
+ * account fragments `recoveryCommandFlags` appends.
22290
+ *
22291
+ * Separate from {@link withheldIdentityClause} rather than folded into it,
22292
+ * because the two holes have different SOURCES and therefore different remedies.
22293
+ * An identity hole is filled from `cdkd state list --json`, which is why that
22294
+ * clause points at a command; an account fragment came from the operator's own
22295
+ * argv, so they already hold it and no listing would help. Conflating them would
22296
+ * send someone to a listing that does not carry their `--profile`.
22297
+ *
22298
+ * Without this, a run whose PREFIX or BUCKET was the inexact value printed
22299
+ * `--state-prefix '<prefix>'` with nothing in the message saying the hole was a
22300
+ * hole: `identityWithheld` reads the stack name and the region only, and the
22301
+ * object-path note is suppressed whenever a prefix WAS supplied. That is
22302
+ * {@link withheldIdentityClause}'s own defect class, one fragment over.
22303
+ */
22304
+ function withheldRecoveryClause(recovery) {
22305
+ if (recoveryCommandFlags(recovery).exact) return "";
22306
+ return " — an account fragment ('--profile', '--state-bucket' or '--state-prefix') did not render exactly, so the command lines below print a quoted hole in its place, which you replace whole — quotes included — with the SHELL-QUOTED value you passed this run";
22307
+ }
22308
+ /**
22309
+ * The listing {@link withheldIdentityClause} points at, printed as its own
22310
+ * trailing command line. Qualified like the remedy it stands in for: a bare
22311
+ * listing after `cdkd orphan --profile prod ...` would read the DEFAULT
22312
+ * profile's bucket, and the name it hands back would then be a different
22313
+ * bucket's.
22314
+ */
22315
+ function withheldIdentityListCommand(stackName, region, recovery) {
22316
+ if (!identityWithheld(stackName, region)) return void 0;
22317
+ return ["cdkd state list --json", ...recoveryCommandFlags(recovery).flags].join(" ");
22318
+ }
22319
+ /** A name WAS known, and it or the region beside it did not render exactly. */
22320
+ function identityWithheld(stackName, region) {
22321
+ if (stackName === void 0 || stackName === "") return false;
22322
+ const known = region === "" ? void 0 : region;
22323
+ return !(rendersExactly(stackName) && (known === void 0 || rendersExactly(known)));
22324
+ }
22325
+ /**
22326
+ * The `cdkd state show` command on the orphan properties refusal's trailing
22327
+ * `Inspect the record:` line, for a record listed WITH a region — gated and
22328
+ * qualified exactly as the drop command on the line above it is, because it is
22329
+ * the same identity in the same message.
22330
+ * Gating only the drop command left the message contradicting itself: it said
22331
+ * the name did not render exactly, pointed at `cdkd state list --json`, and then
22332
+ * printed `cdkd state show prod-api ...` with the trimmed spelling of
22333
+ * `'prod-api '`, naming the healthy sibling (the M2 finding of the
22334
+ * go-to-k/cdkd#3363 review). Holes follow {@link dropRecordCommand}'s rule: an
22335
+ * altered region makes both a hole, an altered name only the name.
22336
+ *
22337
+ * Built here rather than by widening {@link inspectCommand}, which serves four
22338
+ * other builders whose identities come from different sources (and this one no
22339
+ * longer calls it at all).
22340
+ */
22341
+ function orphanInspectCommand(stackName, region, recovery) {
22342
+ const regionExact = rendersExactly(region);
22343
+ return [`cdkd state show ${regionExact && rendersExactly(stackName) ? shellQuote(stackName) : commandHole("stack")} --stack-region ${regionExact ? shellQuote(region) : commandHole("region")} --json`, ...recoveryCommandFlags(recovery).flags].join(" ");
22344
+ }
22345
+ /**
22346
+ * How {@link malformedOrphanResourcePropertiesRefusalMessage} tells the operator
22347
+ * to inspect the record: a `command` it prints on its trailing
22348
+ * `Inspect the record:` line, or — for the legacy region-less shape, where no
22349
+ * command can serve — a prose `sentence` naming the S3 object.
22350
+ *
22351
+ * A KNOWN stack with NO region is a legacy record (`<prefix>/<stack>/state.json`)
22352
+ * that `listStacks` lists with no region — `cdkd orphan` hands this module the
22353
+ * region the record is LISTED under since go-to-k/cdkd#3359, and that is
22354
+ * `undefined` only for such a ref (its body names no region, or the probe that
22355
+ * reads it could not). `cdkd state show` refuses such a record with or without
22356
+ * `--stack-region` ("has only a legacy state record without a region"), and the
22357
+ * migration it suggests is a `cdkd deploy`, which refuses this very record
22358
+ * (go-to-k/cdkd#3191). So ending on `cdkd state show` would hand the operator a
22359
+ * command that cannot serve them; the line names the S3 object instead. Teaching
22360
+ * `state show` to read a legacy record was the alternative, declined as a wider
22361
+ * change to a command that is not the one refusing.
22362
+ *
22363
+ * The object path is printed only when the stack name {@link rendersExactly}: a
22364
+ * path with a sanitized or truncated segment names an object that does not
22365
+ * exist. The name comes from the assembly, and `cdkd orphan` accepts a PREBUILT
22366
+ * one whose manifest nothing validates, which is what makes an inexact name
22367
+ * reachable at all (go-to-k/cdkd#3360); an S3 key's `Parent~Child` name, the
22368
+ * case the destroy refusal's gate was written for, cannot appear here.
22369
+ *
22370
+ * With `recovery` the path carries the REAL prefix and names the bucket, the
22371
+ * way the drop remedy above it is qualified; without it the prefix is a
22372
+ * placeholder and the sentence says how to fill it.
22373
+ */
22374
+ function orphanInspectClause(stackName, region, recovery) {
22375
+ if (stackName === void 0 || stackName === "") return { command: [`cdkd state show ${commandHole("stack")} --stack-region ${commandHole("region")} --json`, ...recoveryCommandFlags(recovery).flags].join(" ") };
22376
+ if (region !== void 0) return { command: orphanInspectCommand(stackName, region, recovery) };
22377
+ const lead = "The record is listed with no region — a legacy 'state.json' whose body names none, or one the listing could not read — which 'cdkd state show' cannot read and a 'cdkd deploy' will not migrate while it is torn, so inspect the object directly";
22378
+ const exactOrUndefined = (v) => v !== void 0 && sanitizeRecoveryValue(v).exact ? v : void 0;
22379
+ const bucket = exactOrUndefined(recovery?.stateBucket) || void 0;
22380
+ const locations = bucket === void 0 ? [] : [`State bucket: ${shellQuote(bucket)}`];
22381
+ const where = bucket === void 0 ? `, in the state bucket ('cdkd state info' names it)` : `, in the bucket on the 'State bucket' line below`;
22382
+ if (!rendersExactly(stackName)) return {
22383
+ sentence: `${lead}: it is the legacy 'state.json' under this stack name, which did not render exactly${where}.`,
22384
+ locations
22385
+ };
22386
+ const prefix = exactOrUndefined(recovery?.statePrefix);
22387
+ const key = shellQuote(`${prefix ?? "<prefix>"}/${stackName}/state.json`);
22388
+ return {
22389
+ sentence: `${lead}: the 'Object key' line below names it${where}.${recovery?.statePrefix === void 0 ? ` Its prefix is 'cdkd' unless '--state-prefix' was given.` : prefix === void 0 ? " The prefix you passed did not render exactly, so the key shows the hole '<prefix>' where it belongs; put your own value there, inside the outer quotes." : ""}`,
22390
+ locations: [`Object key: ${key}`, ...locations]
22391
+ };
22170
22392
  }
22171
22393
  /**
22172
22394
  * For `cdkd orphan`: refuse a record whose SURVIVING resource entries carry a
@@ -22220,11 +22442,11 @@ function malformedOrphanResourcePropertiesRefusalMessage(rawStackName, rawRegion
22220
22442
  * The orphan set is resolved from the synthesized template before the state is
22221
22443
  * loaded, so nothing forces this call any lower.
22222
22444
  */
22223
- function refuseMalformedResourcePropertiesForOrphan(state, removedLogicalIds, stackName, region) {
22445
+ function refuseMalformedResourcePropertiesForOrphan(state, removedLogicalIds, stackName, region, recovery) {
22224
22446
  const removed = new Set(removedLogicalIds);
22225
22447
  const unreadable = unreadableResourcePropertyBags(state).filter((id) => !removed.has(id));
22226
22448
  if (unreadable.length === 0) return;
22227
- throw new CdkdError(malformedOrphanResourcePropertiesRefusalMessage(stackName, region, unreadable), STATE_RESOURCES_MALFORMED);
22449
+ throw new CdkdError(malformedOrphanResourcePropertiesRefusalMessage(stackName, region, unreadable, recovery), STATE_RESOURCES_MALFORMED);
22228
22450
  }
22229
22451
  /**
22230
22452
  * The warning a caller of {@link repairMalformedResourcePropertiesForReadOnly}
@@ -25780,7 +26002,7 @@ function detectUnknownIntrinsicKey(obj) {
25780
26002
  function buildUnknownIntrinsicError(key) {
25781
26003
  const title = `Support intrinsic ${key}`;
25782
26004
  const issueUrl = `https://github.com/go-to-k/cdkd/issues/new?title=${encodeURIComponent(title)}&labels=intrinsic-support`;
25783
- return /* @__PURE__ */ new Error(`Unsupported CloudFormation intrinsic function "${key}": cdkd does not support resolving it yet. Deploying this template would produce a broken value. Please request support by opening an issue: ${issueUrl}`);
26005
+ return /* @__PURE__ */ new Error(`Unsupported CloudFormation intrinsic function "${displayIdent(key)}": cdkd does not support resolving it yet. Deploying this template would produce a broken value. Please request support by opening an issue: ${issueUrl}`);
25784
26006
  }
25785
26007
  /**
25786
26008
  * The same context with the `producerRegions` EVIDENCE removed (issue #2134
@@ -27188,14 +27410,14 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27188
27410
  if (!targetRegion) return ambient;
27189
27411
  const target = canonicalizeRegion(targetRegion);
27190
27412
  const loggedTarget = targetLogText ?? (targetRegion === this.explicitRegion ? this.explicitRegionLogText : void 0) ?? target;
27191
- if (!isClientSafeRegion(target)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to build AWS clients for the region '${stripControlChars(loggedTarget).slice(0, 64)}': it is not a valid AWS region name, and a region is substituted into the AWS service hostname.`));
27413
+ if (!isClientSafeRegion(target)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to build AWS clients for the region '${displaySafe(stripControlChars(loggedTarget)).slice(0, 64)}': it is not a valid AWS region name, and a region is substituted into the AWS service hostname.`));
27192
27414
  if (typeof ambient.withRegion !== "function") return ambient;
27193
27415
  const cached = this.regionScopedClients.get(target);
27194
27416
  if (cached) return cached;
27195
27417
  if (canonicalizeRegion(ambient.configuredRegion) === target) return ambient;
27196
27418
  const scoped = ambient.withRegion(target);
27197
27419
  this.regionScopedClients.set(target, scoped);
27198
- this.logger.debug(`Using region-scoped AWS clients for ${loggedTarget}`);
27420
+ this.logger.debug(`Using region-scoped AWS clients for ${displaySafe(loggedTarget)}`);
27199
27421
  return scoped;
27200
27422
  }
27201
27423
  /**
@@ -27261,7 +27483,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27261
27483
  */
27262
27484
  async resolveParameters(template, userParameters, options) {
27263
27485
  const inheritedSecrets = options?.inheritedSecrets;
27264
- const maskInherited = (text) => inheritedSecrets && inheritedSecrets.size > 0 ? maskSecretsInText(text, inheritedSecrets) : text;
27486
+ const maskInherited = (text) => {
27487
+ const mask = (value) => inheritedSecrets && inheritedSecrets.size > 0 ? maskSecretsInText(value, inheritedSecrets) : value;
27488
+ return displaySafe(mask(stripControlChars(mask(text))));
27489
+ };
27265
27490
  const inheritedLogContext = {
27266
27491
  template,
27267
27492
  resources: {},
@@ -27273,13 +27498,13 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27273
27498
  let referencedNames;
27274
27499
  for (const [name, definition] of Object.entries(templateParameters)) {
27275
27500
  const paramDef = definition;
27276
- if (isUnboundTemplateParameter(name, template, userParameters)) throw new Error(`Parameter ${name} is required but no value was provided and no default exists`);
27501
+ if (isUnboundTemplateParameter(name, template, userParameters)) throw new Error(`Parameter ${maskInherited(name)} is required but no value was provided and no default exists`);
27277
27502
  if (userParameters && Object.hasOwn(userParameters, name)) {
27278
27503
  const userValue = userParameters[name];
27279
27504
  if (userValue !== void 0) {
27280
27505
  this.refuseCoercedInheritedSecret(name, paramDef, userValue, inheritedSecrets);
27281
27506
  parameters[name] = this.coerceParameterValue(userValue, paramDef.Type);
27282
- this.logger.debug(`Parameter ${name}: using user-provided value ${maskInherited(stringifyParameterForLog(paramDef, this.maskValueLeaves(userValue, inheritedLogContext)))}`);
27507
+ this.logger.debug(`Parameter ${maskInherited(name)}: using user-provided value ${maskInherited(stringifyParameterForLog(paramDef, this.maskValueLeaves(userValue, inheritedLogContext)))}`);
27283
27508
  continue;
27284
27509
  }
27285
27510
  }
@@ -27287,19 +27512,19 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27287
27512
  if (paramDef.Type.startsWith("AWS::SSM::Parameter::Value")) {
27288
27513
  referencedNames ??= collectReferencedParameterNames(template);
27289
27514
  if (!referencedNames.has(name)) {
27290
- this.logger.debug(`Parameter ${name}: skipping SSM resolution (not referenced by Resources/Outputs/Conditions)`);
27515
+ this.logger.debug(`Parameter ${maskInherited(name)}: skipping SSM resolution (not referenced by Resources/Outputs/Conditions)`);
27291
27516
  continue;
27292
27517
  }
27293
27518
  const ssmPath = String(paramDef.Default);
27294
- this.logger.debug(`Parameter ${name}: resolving SSM parameter path ${ssmPath}`);
27519
+ this.logger.debug(`Parameter ${maskInherited(name)}: resolving SSM parameter path ${maskInherited(ssmPath)}`);
27295
27520
  const resolved = await this.resolveSSMParameter(ssmPath);
27296
27521
  const resolvedType = ssmResolvedValueType(paramDef.Type);
27297
27522
  parameters[name] = resolvedType === void 0 ? resolved : this.coerceParameterValue(resolved, resolvedType);
27298
- this.logger.debug(`Parameter ${name}: resolved SSM value ${maskInherited(stringifyParameterForLog(paramDef, resolved))}`);
27523
+ this.logger.debug(`Parameter ${maskInherited(name)}: resolved SSM value ${maskInherited(stringifyParameterForLog(paramDef, resolved))}`);
27299
27524
  continue;
27300
27525
  }
27301
27526
  parameters[name] = coerceParameterDefault(paramDef.Default, paramDef.Type);
27302
- this.logger.debug(`Parameter ${name}: using default value ${maskInherited(stringifyParameterForLog(paramDef, paramDef.Default))}`);
27527
+ this.logger.debug(`Parameter ${maskInherited(name)}: using default value ${maskInherited(stringifyParameterForLog(paramDef, paramDef.Default))}`);
27303
27528
  continue;
27304
27529
  }
27305
27530
  }
@@ -27341,11 +27566,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27341
27566
  const inProgress = /* @__PURE__ */ new Set();
27342
27567
  const evaluateByName = async (name) => {
27343
27568
  if (Object.hasOwn(conditions, name)) return conditions[name];
27344
- if (inProgress.has(name)) throw new Error(`Circular condition reference detected involving condition "${name}"`);
27569
+ if (inProgress.has(name)) throw new Error(`Circular condition reference detected involving condition "${this.displayMasked(name, maskingContext)}"`);
27345
27570
  const bag = templateConditions;
27346
27571
  const definition = Object.hasOwn(bag, name) ? bag[name] : void 0;
27347
27572
  if (definition === void 0) {
27348
- this.logger.warn(`Condition ${name} not found in template, assuming false`);
27573
+ this.logger.warn(`Condition ${this.displayMasked(name, maskingContext)} not found in template, assuming false`);
27349
27574
  conditions[name] = false;
27350
27575
  return false;
27351
27576
  }
@@ -27357,7 +27582,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27357
27582
  });
27358
27583
  const value = Boolean(result);
27359
27584
  conditions[name] = value;
27360
- this.logger.debug(`Evaluated condition ${name} = ${value}`);
27585
+ this.logger.debug(`Evaluated condition ${this.displayMasked(name, maskingContext)} = ${value}`);
27361
27586
  return value;
27362
27587
  } finally {
27363
27588
  inProgress.delete(name);
@@ -27366,7 +27591,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27366
27591
  for (const name of Object.keys(templateConditions)) try {
27367
27592
  await withSharedDrainBudget(() => evaluateByName(name));
27368
27593
  } catch (error) {
27369
- this.logger.warn(this.maskSecretsForLog(`Failed to evaluate condition ${name}: ${error instanceof Error ? error.message : String(error)}, assuming false`, maskingContext));
27594
+ this.logger.warn(this.displayMasked(`Failed to evaluate condition ${name}: ${error instanceof Error ? error.message : String(error)}, assuming false`, maskingContext));
27370
27595
  conditions[name] = false;
27371
27596
  inProgress.delete(name);
27372
27597
  }
@@ -27470,7 +27695,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27470
27695
  configurable: true
27471
27696
  });
27472
27697
  else resolved[key] = resolvedVal;
27473
- } else this.logger.debug(`Property ${key} resolved to AWS::NoValue, omitting from object`);
27698
+ } else this.logger.debug(`Property ${this.displayMasked(key, context)} resolved to AWS::NoValue, omitting from object`);
27474
27699
  }
27475
27700
  return resolved;
27476
27701
  }
@@ -27633,7 +27858,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27633
27858
  const carriedBefore = inheritedSecretsCarriedBy(userValue, inherited).length;
27634
27859
  if (carriedBefore === 0) return;
27635
27860
  if (inheritedSecretsCarriedBy(this.coerceParameterValue(userValue, paramDef.Type), inherited).length >= carriedBefore) return;
27636
- throw markNonRetryable(new IntrinsicResolutionRefusalError(`Nested-stack parameter '${name}' is declared 'Type: ${paramDef.Type}', but the parent stack resolved a SECRET dynamic reference into it. cdkd keeps a resolved secret out of persisted state by rewriting STRING leaves back to their {{resolve:...}} expression; coercing this value to '${paramDef.Type}' destroys the plaintext cdkd would have matched on, so the DECRYPTED secret would be left in the child stack's state.json with nothing to redact it back to. Declare '${name}' as 'Type: String' in the nested stack's template (CDK does this by default for cross-stack references), or stop passing a secret reference into it.`, void 0, "NESTED_STACK_SECRET_PARAMETER_TYPE"));
27861
+ const loggedName = this.displayMasked(name);
27862
+ throw markNonRetryable(new IntrinsicResolutionRefusalError(`Nested-stack parameter '${loggedName}' is declared 'Type: ${paramDef.Type}', but the parent stack resolved a SECRET dynamic reference into it. cdkd keeps a resolved secret out of persisted state by rewriting STRING leaves back to their {{resolve:...}} expression; coercing this value to '${paramDef.Type}' destroys the plaintext cdkd would have matched on, so the DECRYPTED secret would be left in the child stack's state.json with nothing to redact it back to. Declare '${loggedName}' as 'Type: String' in the nested stack's template (CDK does this by default for cross-stack references), or stop passing a secret reference into it.`, void 0, "NESTED_STACK_SECRET_PARAMETER_TYPE"));
27637
27863
  }
27638
27864
  /**
27639
27865
  * Resolve Ref intrinsic function
@@ -27647,14 +27873,14 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27647
27873
  const resource = Object.hasOwn(context.resources, logicalId) ? context.resources[logicalId] : void 0;
27648
27874
  if (resource) {
27649
27875
  const refValue = this.resolveRefValue(logicalId, resource, context);
27650
- this.logger.debug(`Resolved Ref to resource: ${logicalId} -> ${refValue}`);
27876
+ this.logger.debug(`Resolved Ref to resource: ${this.displayMasked(logicalId, context)} -> ${refValue}`);
27651
27877
  return refValue;
27652
27878
  }
27653
27879
  if (context.parameters && Object.hasOwn(context.parameters, logicalId)) {
27654
27880
  const value = context.parameters[logicalId];
27655
27881
  const declared = context.template.Parameters;
27656
27882
  const paramDef = declared != null && Object.hasOwn(declared, logicalId) ? declared[logicalId] : void 0;
27657
- this.logger.debug(`Resolved Ref to parameter: ${logicalId} -> ${this.displayMasked(stringifyParameterForLog(paramDef, this.maskValueLeaves(value, context)), context)}`);
27883
+ this.logger.debug(`Resolved Ref to parameter: ${this.displayMasked(logicalId, context)} -> ${this.displayMasked(stringifyParameterForLog(paramDef, this.maskValueLeaves(value, context)), context)}`);
27658
27884
  this.recordInheritedParameterSecrets(logicalId, value, context);
27659
27885
  return value;
27660
27886
  }
@@ -27664,10 +27890,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27664
27890
  this.logger.debug(`Resolved Ref to pseudo parameter: ${logicalId} -> ${valueStr}`);
27665
27891
  return pseudoValue;
27666
27892
  }
27667
- const notFoundMsg = `Ref ${logicalId} not found (not a resource, parameter, or pseudo parameter)`;
27893
+ const loggedLogicalId = this.displayMasked(logicalId, context);
27894
+ const notFoundMsg = `Ref ${loggedLogicalId} not found (not a resource, parameter, or pseudo parameter)`;
27668
27895
  if (context.bestEffort) this.logger.debug(notFoundMsg);
27669
27896
  else this.logger.warn(notFoundMsg);
27670
- throw markNonRetryable(/* @__PURE__ */ new Error(`Ref ${logicalId} not found`));
27897
+ throw markNonRetryable(/* @__PURE__ */ new Error(`Ref ${loggedLogicalId} not found`));
27671
27898
  }
27672
27899
  /**
27673
27900
  * Resolve the value a CloudFormation `Ref` returns for a resource.
@@ -27753,10 +27980,29 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27753
27980
  * nothing to do at this site: the value has ALREADY been masked in state, so
27754
27981
  * there is no plaintext to register.
27755
27982
  *
27756
- * The SPELLING (`Ref <LogicalId> (state key <Key>)`) is what
27757
- * `DeployEngine.maskedRecordRemedyFor` partitions on, so it is load-bearing
27758
- * rather than cosmetic — that helper reads the logical id out of it to emit
27759
- * the re-import command, and pins the shape in a test.
27983
+ * THE SPELLING IS NO LONGER PARSED, and this paragraph used to say the
27984
+ * opposite (corrected in go-to-k/cdkd#3432). It read: "`Ref <LogicalId>
27985
+ * (state key <Key>)` is what `DeployEngine.maskedRecordRemedyFor` partitions
27986
+ * on ... that helper reads the logical id out of it". That was true when it
27987
+ * was written and stopped being true at that helper's round-4 review, which
27988
+ * moved the partition onto the `kind` / `logicalId` FIELDS precisely because
27989
+ * two successive regexes over this rendering each shipped a defect — the
27990
+ * second an `[A-Za-z0-9]+` id class that a HYPHENATED logical id falls out
27991
+ * of. `targetOf` reads `read.logicalId`; no regex touches `display`.
27992
+ *
27993
+ * What the shape IS still load-bearing for is a SENTENCE: the refusal's
27994
+ * `hasRefStateRead` arm tells the reader that "a 'Ref <LogicalId> (state key
27995
+ * <Key>)' entry above is CDKD's own read", so the `Ref ` prefix and the
27996
+ * `(state key ...)` clause have to keep appearing. Sanitizing the ID inside
27997
+ * them leaves both intact.
27998
+ *
27999
+ * THE ID IS SANITIZED, the attribute-read sibling's judgement one branch over
28000
+ * (go-to-k/cdkd#3432). `displayMasked` rather than `displayIdent`: the
28001
+ * builder neither quotes nor bounds, so every legitimate id renders exactly
28002
+ * as it did, and the deliberate collision `intrinsic-ref-state-mask.test.ts`
28003
+ * pins between this rendering and `noteAttributeSecrecy`'s is preserved
28004
+ * because BOTH sites take the same builder. The `logicalId` FIELD stays raw —
28005
+ * it is the routing key and the `--resource <id>=` argument.
27760
28006
  *
27761
28007
  * `key` is never masked before interpolation because it is not template text:
27762
28008
  * it comes from the fixed key lists `cfnRefValueFromPhysicalId` passes
@@ -27768,7 +28014,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27768
28014
  kind: "ref-state-key",
27769
28015
  logicalId,
27770
28016
  key,
27771
- display: `Ref ${logicalId} (state key ${key})`
28017
+ display: `Ref ${this.displayMasked(logicalId, context)} (state key ${key})`
27772
28018
  });
27773
28019
  }
27774
28020
  /**
@@ -27810,7 +28056,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27810
28056
  const [rawLogicalId, rawAttributeName] = getAtt;
27811
28057
  logicalId = rawLogicalId;
27812
28058
  const resolvedAttributeName = await this.resolveValue(rawAttributeName, context);
27813
- if (typeof resolvedAttributeName !== "string") throw new Error(`Fn::GetAtt attribute name for ${logicalId} must resolve to a string, got ${typeof resolvedAttributeName}: ${stringifyValue(this.maskValueLeaves(resolvedAttributeName, context))}`);
28059
+ if (typeof resolvedAttributeName !== "string") throw new Error(`Fn::GetAtt attribute name for ${this.displayMasked(logicalId, context)} must resolve to a string, got ${typeof resolvedAttributeName}: ${stringifyValue(this.maskValueLeaves(resolvedAttributeName, context))}`);
27814
28060
  attributeName = resolvedAttributeName;
27815
28061
  } else {
27816
28062
  const split = splitGetAttStringForm(getAtt);
@@ -27819,7 +28065,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27819
28065
  attributeName = split.attributeName;
27820
28066
  }
27821
28067
  const resource = Object.hasOwn(context.resources, logicalId) ? context.resources[logicalId] : void 0;
27822
- if (!resource) throw markNonRetryable(/* @__PURE__ */ new Error(`Resource ${logicalId} not found for Fn::GetAtt`));
28068
+ if (!resource) throw markNonRetryable(/* @__PURE__ */ new Error(`Resource ${this.displayMasked(logicalId, context)} not found for Fn::GetAtt`));
27823
28069
  if (!(resource.resourceType === "AWS::EC2::VPC" && attributeName === "Ipv6CidrBlocks") && resource.attributes !== void 0) {
27824
28070
  const storedValue = Object.hasOwn(resource.attributes, attributeName) ? resource.attributes[attributeName] : void 0;
27825
28071
  const flatValue = isImpossibleEmptyStoredAttribute(resource.resourceType, attributeName, storedValue) ? void 0 : storedValue;
@@ -27827,10 +28073,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27827
28073
  this.rejectPlaceholderArnAttribute(resource, attributeName, flatValue, logicalId, context);
27828
28074
  if (resource.resourceType === "AWS::Route53::HostedZone" && attributeName === "NameServers" && typeof flatValue === "string") {
27829
28075
  const nameServers = flatValue === "" ? [] : flatValue.split(",");
27830
- this.logger.debug(`Normalized legacy Fn::GetAtt attribute: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(nameServers, context)), context)}`);
28076
+ 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)}`);
27831
28077
  return this.noteAttributeSecrecy(logicalId, attributeName, nameServers, context);
27832
28078
  }
27833
- this.logger.debug(`Resolved Fn::GetAtt from attributes: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(flatValue, context)), context)}`);
28079
+ 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)}`);
27834
28080
  if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX) && carriesDynamicReference(flatValue)) return await this.reresolveCrossStackValue(flatValue, nestedStackChildRegionFromLocalArn(resource.physicalId), context, `nested stack ${logicalId} ${this.displayLeaf(attributeName, context)}`, crossStackSourceKey({ "Fn::GetAtt": getAtt }));
27835
28081
  return this.noteAttributeSecrecy(logicalId, attributeName, flatValue, context);
27836
28082
  }
@@ -27843,18 +28089,19 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27843
28089
  break;
27844
28090
  }
27845
28091
  if (cursor !== void 0) {
27846
- this.logger.debug(`Resolved Fn::GetAtt from nested attributes: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(cursor, context)), context)}`);
28092
+ 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)}`);
27847
28093
  return this.noteAttributeSecrecy(logicalId, attributeName, cursor, context);
27848
28094
  }
27849
28095
  }
27850
28096
  }
27851
28097
  if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX)) {
27852
- const declared = Object.keys(resource.attributes ?? {}).filter((k) => k.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX)).sort().map((k) => this.maskSecretsForLog(this.outputNameLogText(k, context), context));
28098
+ const declared = Object.keys(resource.attributes ?? {}).filter((k) => k.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX)).sort().map((k) => this.displayMasked(this.outputNameLogText(k, context), context));
27853
28099
  const declaredText = declared.length > 0 ? declared.join(", ") : "(none)";
27854
- throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}]: the nested stack '${logicalId}' declares no output named '${this.displayMasked(this.outputNameLogText(attributeName, context), context)}'. Its outputs are ${this.displayMasked(declaredText, context)}. Check the output name in the nested stack's template, and deploy the child stack again if you have just added it.`));
28100
+ const loggedLogicalId = this.displayMasked(logicalId, context);
28101
+ throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${loggedLogicalId}, ${this.displayMasked(attributeName, context)}]: the nested stack '${loggedLogicalId}' declares no output named '${this.displayMasked(this.outputNameLogText(attributeName, context), context)}'. Its outputs are ${this.displayMasked(declaredText, context)}. Check the output name in the nested stack's template, and deploy the child stack again if you have just added it.`));
27855
28102
  }
27856
28103
  const value = await this.constructGuardedAttribute(resource, attributeName, context, logicalId);
27857
- this.logger.debug(`Resolved Fn::GetAtt: ${logicalId}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(value, context)), context)}`);
28104
+ this.logger.debug(`Resolved Fn::GetAtt: ${this.displayMasked(logicalId, context)}.${this.displayMasked(attributeName, context)} -> ${this.displayMasked(stringifyAttributeForLog(attributeName, this.maskValueLeaves(value, context)), context)}`);
27858
28105
  return value;
27859
28106
  }
27860
28107
  /**
@@ -27905,12 +28152,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27905
28152
  const declared = context.noEchoAttributeResources?.get(logicalId);
27906
28153
  if ((declared === true || declared !== void 0 && declared.has(attributeName)) && context.recordedSecretValues) recordMaskOnlyValuesIn(value, context.recordedSecretValues);
27907
28154
  if (context.redactedAttributeReads !== void 0 && carriesSecretMask(value)) {
27908
- const maskedAttributeName = this.maskSecretsForLog(attributeName, context);
28155
+ const maskedAttributeName = this.displayMasked(attributeName, context);
27909
28156
  this.pushRedactedAttributeRead(context, {
27910
28157
  kind: "attribute",
27911
28158
  logicalId,
27912
28159
  key: maskedAttributeName,
27913
- display: `${logicalId}.${maskedAttributeName}`
28160
+ display: `${this.displayMasked(logicalId, context)}.${maskedAttributeName}`
27914
28161
  });
27915
28162
  }
27916
28163
  return value;
@@ -27947,7 +28194,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27947
28194
  rejectPlaceholderArnAttribute(resource, attributeName, value, logicalId, context) {
27948
28195
  if (!REF_RETURNS_ARN_FROM_STATE.get(resource.resourceType)?.includes(attributeName)) return;
27949
28196
  if (typeof value !== "string" || !isPlaceholderArn(value)) return;
27950
- throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resource.resourceType}: the recorded value "${stringifyValue(this.maskValueLeaves(value, context))}" is a placeholder written by a cdkd version older than issue #1681 — its region and account fields are literal wildcards, so it is not a usable ARN. Deploy the stack again so the resource's next update heals the record (cdkd now records the real ARN), or re-import the resource.`));
28197
+ throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, ${this.displayMasked(attributeName, context)}] for ${resource.resourceType}: the recorded value "${stringifyValue(this.maskValueLeaves(value, context))}" is a placeholder written by a cdkd version older than issue #1681 — its region and account fields are literal wildcards, so it is not a usable ARN. Deploy the stack again so the resource's next update heals the record (cdkd now records the real ARN), or re-import the resource.`));
27951
28198
  }
27952
28199
  /**
27953
28200
  * Construct resource attribute value based on resource type, refusing to
@@ -27992,7 +28239,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27992
28239
  async constructGuardedAttribute(resource, attributeName, context, logicalId) {
27993
28240
  const accountInfo = await getAccountInfo(this.resolverRegion);
27994
28241
  const value = await this.constructAttribute(resource, attributeName, context, logicalId, accountInfo);
27995
- if (accountInfo.fabricated && embedsAccountId(value, accountInfo.accountId)) throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resource.resourceType}: STS did not report this deploy's account id, so cdkd would build the value from the placeholder account ${accountInfo.accountId} — structurally valid, naming a different account, and indistinguishable downstream from a real one. Fix the AWS credentials (or set AWS_ACCOUNT_ID to this deploy's account) and deploy again.`);
28242
+ if (accountInfo.fabricated && embedsAccountId(value, accountInfo.accountId)) throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, ${this.displayMasked(attributeName, context)}] for ${resource.resourceType}: STS did not report this deploy's account id, so cdkd would build the value from the placeholder account ${accountInfo.accountId} — structurally valid, naming a different account, and indistinguishable downstream from a real one. Fix the AWS credentials (or set AWS_ACCOUNT_ID to this deploy's account) and deploy again.`);
27996
28243
  return value;
27997
28244
  }
27998
28245
  /**
@@ -28054,7 +28301,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28054
28301
  return [];
28055
28302
  }
28056
28303
  case "DefaultSecurityGroup": {
28057
- if (!/^vpc-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, DefaultSecurityGroup] for AWS::EC2::VPC: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a VPC id (vpc-<hex>), so cdkd will not use it as an EC2 filter value. Repair the record (cdkd import, or re-create the VPC) and deploy again.`));
28304
+ if (!/^vpc-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, DefaultSecurityGroup] for AWS::EC2::VPC: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a VPC id (vpc-<hex>), so cdkd will not use it as an EC2 filter value. Repair the record (cdkd import, or re-create the VPC) and deploy again.`));
28058
28305
  const cachedGroupId = cachedVpcDefaultSecurityGroups[physicalId];
28059
28306
  if (cachedGroupId !== void 0) return cachedGroupId;
28060
28307
  let groupId;
@@ -28269,7 +28516,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28269
28516
  if (resourceType === "AWS::EC2::SecurityGroup") switch (attributeName) {
28270
28517
  case "GroupId": return physicalId;
28271
28518
  case "VpcId": {
28272
- if (!/^sg-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, VpcId] for AWS::EC2::SecurityGroup: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a security group id (sg-<hex>), so cdkd will not look it up. Repair the record (cdkd import, or re-create the security group) and deploy again.`));
28519
+ if (!/^sg-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, VpcId] for AWS::EC2::SecurityGroup: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a security group id (sg-<hex>), so cdkd will not look it up. Repair the record (cdkd import, or re-create the security group) and deploy again.`));
28273
28520
  const cachedVpcId = cachedSecurityGroupVpcIds[physicalId];
28274
28521
  if (cachedVpcId !== void 0) return cachedVpcId;
28275
28522
  let vpcId;
@@ -28386,7 +28633,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28386
28633
  if (resourceType === "AWS::CloudFront::Distribution") switch (attributeName) {
28387
28634
  case "Id": return physicalId;
28388
28635
  case "DomainName": {
28389
- if (!/^[A-Z0-9]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, DomainName] for AWS::CloudFront::Distribution: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a distribution id (upper-case alphanumerics), so cdkd will not look it up. Repair the record (cdkd import, or re-create the distribution) and deploy again.`));
28636
+ if (!/^[A-Z0-9]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, DomainName] for AWS::CloudFront::Distribution: the state record's physical id "${this.displayMasked(physicalId, context).slice(0, 64)}" is not a distribution id (upper-case alphanumerics), so cdkd will not look it up. Repair the record (cdkd import, or re-create the distribution) and deploy again.`));
28390
28637
  const cachedDomainName = cachedCloudFrontDomainNames[physicalId];
28391
28638
  if (cachedDomainName !== void 0) return cachedDomainName;
28392
28639
  let domainName;
@@ -28420,7 +28667,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28420
28667
  default: return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
28421
28668
  }
28422
28669
  if (resourceType === "AWS::RDS::DBProxy" || resourceType === "AWS::RDS::DBProxyEndpoint") {
28423
- if (attributeName === "VpcId") throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: the state record holds no VpcId for it (the read-back that would have recorded it reported none), and the physical id "${this.displayMasked(physicalId, context)}" is a name, not a VPC id, so cdkd refuses to substitute it. Update the resource so its next deploy records the value, or reference the VPC directly.`));
28670
+ if (attributeName === "VpcId") throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: the state record holds no VpcId for it (the read-back that would have recorded it reported none), and the physical id "${this.displayMasked(physicalId, context)}" is a name, not a VPC id, so cdkd refuses to substitute it. Update the resource so its next deploy records the value, or reference the VPC directly.`));
28424
28671
  return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
28425
28672
  }
28426
28673
  return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
@@ -28479,7 +28726,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28479
28726
  */
28480
28727
  refuseUnservedAttribute(site) {
28481
28728
  const { logicalId, attributeName, resourceType, physicalId, context, observed, remedy } = site;
28482
- throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: ${observed}. The physical id "${this.displayMasked(physicalId, context)}" is not a usable ${this.displayMasked(attributeName, context)}, so cdkd refuses to substitute it. ${remedy}`);
28729
+ throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: ${observed}. The physical id "${this.displayMasked(physicalId, context)}" is not a usable ${this.displayMasked(attributeName, context)}, so cdkd refuses to substitute it. ${remedy}`);
28483
28730
  }
28484
28731
  /**
28485
28732
  * The `observed` clause of {@link refuseUnservedAttribute} for a live read
@@ -28526,9 +28773,9 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28526
28773
  const expectsUrlShape = attributeName.endsWith("Url") && !/^https?:\/\//.test(physicalId);
28527
28774
  if (expectsArnShape || expectsUrlShape) {
28528
28775
  const expectedShape = expectsArnShape ? "an ARN (arn:...)" : "a URL (http(s)://...)";
28529
- throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and the physical ID fallback "${this.displayMasked(physicalId, context)}" is not ${expectedShape}. CloudFormation would return a different value here, so falling back to the physical ID would silently produce a wrong value (e.g. in stack Outputs). Avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.displayMasked(attributeName, context)}.`));
28776
+ throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and the physical ID fallback "${this.displayMasked(physicalId, context)}" is not ${expectedShape}. CloudFormation would return a different value here, so falling back to the physical ID would silently produce a wrong value (e.g. in stack Outputs). Avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.displayMasked(attributeName, context)}.`));
28530
28777
  }
28531
- if (this.strictGetAtt) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and --strict-getatt rejects the physical ID fallback "${this.displayMasked(physicalId, context)}" (which may not be the value CloudFormation would return). Drop --strict-getatt to fall back with a warning, avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.displayMasked(attributeName, context)}.`));
28778
+ if (this.strictGetAtt) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${this.displayMasked(logicalId, context)}, ${this.displayMasked(attributeName, context)}] for ${resourceType}: attributes are not enriched for this resource type, and --strict-getatt rejects the physical ID fallback "${this.displayMasked(physicalId, context)}" (which may not be the value CloudFormation would return). Drop --strict-getatt to fall back with a warning, avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${this.displayMasked(attributeName, context)}.`));
28532
28779
  this.physicalIdFallbackCount++;
28533
28780
  this.logger.warn(`Unknown attribute ${this.displayMasked(attributeName, context)} for resource type ${resourceType}, returning physical ID`);
28534
28781
  return physicalId;
@@ -28806,13 +29053,13 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
28806
29053
  } catch (getAttError) {
28807
29054
  if (getAttError instanceof IntrinsicResolutionRefusalError) throw getAttError;
28808
29055
  this.rethrowStructuralSubFailure(varNameStr, getAttError, context);
28809
- this.logger.warn(this.maskSecretsForLog(this.subPlaceholderWarning(varNameStr, getAttError), context));
29056
+ this.logger.warn(this.displayMasked(this.subPlaceholderWarning(varNameStr, getAttError), context));
28810
29057
  replacement = match[0];
28811
29058
  }
28812
29059
  else {
28813
29060
  if (refError instanceof IntrinsicResolutionRefusalError) throw refError;
28814
29061
  this.rethrowStructuralSubFailure(varNameStr, refError, context);
28815
- this.logger.warn(this.maskSecretsForLog(this.subPlaceholderWarning(varNameStr, refError), context));
29062
+ this.logger.warn(this.displayMasked(this.subPlaceholderWarning(varNameStr, refError), context));
28816
29063
  replacement = match[0];
28817
29064
  }
28818
29065
  }
@@ -29004,7 +29251,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29004
29251
  const resolvedValue = await this.resolveValue(value, context);
29005
29252
  if (typeof resolvedValue !== "string") {
29006
29253
  const source = this.describeSplitValueSource(value);
29007
- const sourceClause = source ? ` (from ${source.label})` : "";
29254
+ const sourceClause = source ? ` (from ${this.displayMasked(source.label, context)})` : "";
29008
29255
  if (Array.isArray(resolvedValue)) {
29009
29256
  const remedy = source?.kind === "ref" ? "A list-typed parameter — any List<...> type (List<AWS::EC2::Subnet::Id>, List<Number>, …) or CommaDelimitedList — is already a list." : source?.kind === "getatt" ? "A list-valued Fn::GetAtt (for example AWS::Route53::HostedZone.NameServers or AWS::EC2::VPC.Ipv6CidrBlocks) already returns a list. If you wrote the Fn::Split as a workaround for cdkd resolving that attribute to a comma-delimited string, that bug is fixed (PR #1868) and the workaround is no longer needed." : "Several intrinsics already return a list — among them a list-valued Fn::GetAtt, a Ref to a list-typed parameter (any List<...> type or CommaDelimitedList), Fn::GetAZs, Fn::Cidr, and Fn::Split itself.";
29010
29257
  throw markNonRetryable(new IntrinsicResolutionRefusalError(`Fn::Split: the value to split${sourceClause} is ALREADY a list (an array of ${resolvedValue.length} item${resolvedValue.length === 1 ? "" : "s"}), not a string. CloudFormation rejects Fn::Split over a list too, so this template is not valid CloudFormation either. Remove the Fn::Split and use the value directly. ${remedy}`));
@@ -29025,12 +29272,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29025
29272
  async resolveIf(ifArgs, context) {
29026
29273
  const [conditionName, valueIfTrue, valueIfFalse] = ifArgs;
29027
29274
  if (!context.conditions || !Object.hasOwn(context.conditions, conditionName)) {
29028
- this.logger.warn(`Condition ${conditionName} not found in context, assuming false`);
29275
+ this.logger.warn(`Condition ${this.displayMasked(String(conditionName), context)} not found in context, assuming false`);
29029
29276
  return await this.resolveValue(valueIfFalse, context);
29030
29277
  }
29031
29278
  const conditionValue = context.conditions[conditionName];
29032
29279
  const selectedValue = conditionValue ? valueIfTrue : valueIfFalse;
29033
- this.logger.debug(`Resolved Fn::If: condition ${conditionName} = ${conditionValue}, selected ${conditionValue ? "true" : "false"} branch`);
29280
+ this.logger.debug(`Resolved Fn::If: condition ${this.displayMasked(String(conditionName), context)} = ${conditionValue}, selected ${conditionValue ? "true" : "false"} branch`);
29034
29281
  return await this.resolveValue(selectedValue, context);
29035
29282
  }
29036
29283
  /**
@@ -29061,7 +29308,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29061
29308
  async resolveConditionReference(conditionName, context) {
29062
29309
  if (context.conditionResolver) return await context.conditionResolver(conditionName);
29063
29310
  if (context.conditions && Object.hasOwn(context.conditions, conditionName)) return context.conditions[conditionName];
29064
- this.logger.warn(`Condition ${conditionName} not found in context, assuming false`);
29311
+ this.logger.warn(`Condition ${this.displayMasked(conditionName, context)} not found in context, assuming false`);
29065
29312
  return false;
29066
29313
  }
29067
29314
  /**
@@ -29193,7 +29440,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29193
29440
  return recovered;
29194
29441
  }
29195
29442
  if (context.redactedAttributeReads !== void 0) {
29196
- const loggedOrigin = this.maskSecretsForLog(origin, context);
29443
+ const loggedOrigin = this.displayMasked(origin, context);
29197
29444
  this.pushRedactedAttributeRead(context, {
29198
29445
  kind: "cross-stack",
29199
29446
  display: loggedOrigin
@@ -29287,7 +29534,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29287
29534
  const target = canonicalizeRegion(producerRegion);
29288
29535
  if (target === canonicalizeRegion(this.explicitRegion)) return this;
29289
29536
  const regionText = producerRegionLogText !== void 0 ? canonicalizeRegion(producerRegionLogText) : this.regionLogText(producerRegion, context);
29290
- const guestRegionText = this.maskThenStripThenMask(regionText, context);
29537
+ const guestRegionText = this.displayMasked(regionText, context);
29291
29538
  const cached = this.producerRegionResolvers.get(target);
29292
29539
  if (cached) {
29293
29540
  if (cached.explicitRegionLogText !== guestRegionText) cached.explicitRegionLogText = "***";
@@ -29313,7 +29560,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29313
29560
  const exportName = await this.resolveValue(importValueArg, context);
29314
29561
  if (typeof exportName !== "string") throw new Error(`Fn::ImportValue: export name must resolve to a string, got ${typeof exportName}`);
29315
29562
  if (!context.stateBackend) throw new Error("Fn::ImportValue: state backend is required for cross-stack references");
29316
- const loggedExportName = this.maskSecretsForLog(exportName, context);
29563
+ const loggedExportName = this.displayMasked(exportName, context);
29317
29564
  this.logger.debug(`Resolving Fn::ImportValue: ${loggedExportName}`);
29318
29565
  if (context.exportIndex) {
29319
29566
  let entry;
@@ -29442,7 +29689,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29442
29689
  * `OutputName` is the overwhelmingly common cause, and the list is what makes
29443
29690
  * the error actionable.
29444
29691
  *
29445
- * `maskSecretsForLog` reads the consumer pass's log twin first (issue #3150),
29692
+ * `maskSecretsRaw` reads the consumer pass's log twin first (issue #3150),
29446
29693
  * so a key equal to a name this pass assembled around a short secret is
29447
29694
  * masked. Residual, stated rather than hidden: the needles and twins belong to the
29448
29695
  * CONSUMER's resolution, so a plaintext sitting in a PRODUCER key that this
@@ -29493,7 +29740,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29493
29740
  } catch (error) {
29494
29741
  const message = error instanceof Error ? error.message : String(error);
29495
29742
  const cfnNameMask = this.positionalNameMask([[stackName, this.displayMasked(stackName, context)], [region, this.displayMasked(loggedRegionText, context)]]);
29496
- this.logger.warn(`Fn::GetStackOutput: CloudFormation DescribeStacks fallback failed for stack '${this.displayMasked(stackName, context)}' (${this.displayMasked(loggedRegionText, context)}): ${displayAwsMessage(this.maskSecretsForLog(cfnNameMask ? cfnNameMask(message) : message, context))}. Grant cloudformation:DescribeStacks to resolve outputs from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
29743
+ this.logger.warn(`Fn::GetStackOutput: CloudFormation DescribeStacks fallback failed for stack '${this.displayMasked(stackName, context)}' (${this.displayMasked(loggedRegionText, context)}): ${displayAwsMessage(this.displayMasked(cfnNameMask ? cfnNameMask(message) : message, context))}. Grant cloudformation:DescribeStacks to resolve outputs from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
29497
29744
  return;
29498
29745
  }
29499
29746
  }
@@ -29616,7 +29863,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29616
29863
  if (!roleArn && context.stackName && context.stackName === stackName && canonicalizeRegion(region) === canonicalizeRegion(this.resolverRegion)) throw new Error(`Fn::GetStackOutput: cannot reference own stack '${this.displayMasked(stackName, context)}' in the same region '${this.displayMasked(loggedRegionText, context)}'`);
29617
29864
  const loggedStackName = this.displayMasked(stackName, context);
29618
29865
  const loggedOutputName = this.displayMasked(outputName, context);
29619
- const loggedRegion = this.maskSecretsForLog(loggedRegionText, context);
29866
+ const loggedRegion = this.displayMasked(loggedRegionText, context);
29620
29867
  const shownRoleArn = roleArn ? displayIdent(roleArn, { maxCodePoints: 613 }) : "";
29621
29868
  this.logger.debug(`Resolving Fn::GetStackOutput: StackName=${loggedStackName}, Region=${loggedRegion}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${shownRoleArn}` : ""}`);
29622
29869
  let stateData;
@@ -29706,7 +29953,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29706
29953
  * own comment says why. What this clause is not is redundant against
29707
29954
  * `raw !== masked`: `rememberLogTwin` has no empty-key guard and
29708
29955
  * `splitLogTwins` registers every piece it produces, so `registeredLogTwin`
29709
- * can answer `***` for the empty string and make `maskSecretsForLog('')`
29956
+ * can answer `***` for the empty string and make `maskSecretsRaw('')`
29710
29957
  * differ from `''`. Neither call site can reach it — both refuse an empty
29711
29958
  * name upstream — so it fences nothing measured today.
29712
29959
  *
@@ -29931,7 +30178,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29931
30178
  this.logger.debug(`Resolved Fn::GetAZs from cache: ${this.displayMasked(loggedRegionText ?? region, context)} -> ${JSON.stringify(this.maskValueLeaves(cached, context))}`);
29932
30179
  return cached;
29933
30180
  }
29934
- const ec2Client = this.clientsForRegion(clientRegion, loggedRegionText === void 0 ? void 0 : this.maskSecretsForLog(loggedRegionText, context)).ec2;
30181
+ const ec2Client = this.clientsForRegion(clientRegion, loggedRegionText === void 0 ? void 0 : this.displayMasked(loggedRegionText, context)).ec2;
29935
30182
  let azNames;
29936
30183
  try {
29937
30184
  azNames = ((await ec2Client.send(new DescribeAvailabilityZonesCommand({ Filters: [{
@@ -29980,9 +30227,34 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29980
30227
  * Results are cached to avoid repeated API calls.
29981
30228
  */
29982
30229
  /**
29983
- * Mask any resolved secret value out of a string bound for a log line, using
29984
- * the secrets recorded on the resolution pass (GHSA fix). No-op when the pass
29985
- * recorded no secrets.
30230
+ * The SECRET answer alone: mask any resolved secret value out of `text`,
30231
+ * using the secrets recorded on the resolution pass (GHSA fix). No-op when
30232
+ * the pass recorded no secrets.
30233
+ *
30234
+ * ## NOT a display form, and the name says so
30235
+ *
30236
+ * This was `maskSecretsForLog` until go-to-k/cdkd#3426, and the name was the
30237
+ * defect. It answers "does this text contain a recorded secret" and makes no
30238
+ * claim about CONTROL CHARACTERS — but it read as "the spelling a log line
30239
+ * takes", so TEN local bindings took its result and interpolated it later,
30240
+ * one of them reaching an `Fn::ImportValue` warn and throw with a live
30241
+ * `ESC[2K` + CR from a template-supplied export name (measured on this tree,
30242
+ * at DEFAULT verbosity, on the DEFAULT path). Renaming it is what makes the
30243
+ * hazard visible at the BINDING rather than at the render: `const x =
30244
+ * this.maskSecretsRaw(...)` states that more work is owed.
30245
+ *
30246
+ * Reachable from ONE place, {@link maskThenStripThenMask}, which is itself
30247
+ * reachable only from {@link displayMasked}. That containment is the whole
30248
+ * mechanism. It used to be enforced by an AST walk that resolved an
30249
+ * interpolated identifier to its declaration; go-to-k/cdkd#3435 deleted that
30250
+ * checker, and what holds the containment now is
30251
+ * `tests/unit/deployment/resolver-display-masked-population.test.ts`, which
30252
+ * asserts the exact reference COUNTS from the AST (one caller each) and
30253
+ * refuses a direct `${this.<raw masker>(...)}` interpolation by line rule.
30254
+ * Do not call this from a new site: a binding of it interpolated LATER is
30255
+ * the shape neither survivor can see.
30256
+ *
30257
+ * ## What the masking itself does
29986
30258
  *
29987
30259
  * A value with a registered LOG TWIN prints as its twin first (issue
29988
30260
  * [#3150](https://github.com/go-to-k/cdkd/issues/3150)). The needle mask
@@ -29992,7 +30264,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
29992
30264
  * reference) printed in the clear at every site that handed its RAW value
29993
30265
  * here, although the pass had registered where the secret sits. Looking the
29994
30266
  * twin up HERE, rather than at each such site, is what makes the class
29995
- * closed for raw values: every one of those sites already calls this method.
30267
+ * closed for raw values: every one of those sites already reaches this
30268
+ * method through the builder.
29996
30269
  * The twin prints as it is: it is the raw text with masked spans, so it
29997
30270
  * holds no recorded needle the raw text lacks. When the needle mask ALSO
29998
30271
  * changes the raw text the whole text is masked instead, the rule
@@ -30000,8 +30273,21 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30000
30273
  * cannot be merged. What this cannot see is a value TRANSFORMED before it
30001
30274
  * arrives (a lowercased region, a sliced output name, an assembled
30002
30275
  * sentence); those sites derive their text from the twin themselves.
30276
+ *
30277
+ * ## The SELF-COMPARISON, and why it did not block the strip
30278
+ *
30279
+ * The line below compares `maskNeedlesForLog(text) !== text` — the NEEDLE
30280
+ * mask, not this method's own result — so making the RENDER path strip
30281
+ * changes no comparison anywhere. An earlier revision of `displayMasked`'s
30282
+ * comment said stripping could not move inside "the masker" because its
30283
+ * result is also a comparison; that was a claim about this expression, and
30284
+ * the expression's operand is a different function. The measurement that
30285
+ * settled it: every one of this method's 20 escaping call sites rendered
30286
+ * into a log line, a throw, or a `display:` field that becomes one — none
30287
+ * compared, persisted or re-parsed the result — so the render path could
30288
+ * take the strip whole.
30003
30289
  */
30004
- maskSecretsForLog(text, context) {
30290
+ maskSecretsRaw(text, context) {
30005
30291
  const registered = this.registeredLogTwin(text, context);
30006
30292
  if (registered === void 0) return this.maskNeedlesForLog(text, context);
30007
30293
  return this.maskNeedlesForLog(text, context) !== text ? "***" : registered;
@@ -30020,19 +30306,19 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30020
30306
  const raw = error instanceof Error ? error.message : String(error);
30021
30307
  return {
30022
30308
  unit,
30023
- subject: this.maskSecretsForLog(subject, context),
30024
- message: this.maskSecretsForLog(preRedact === void 0 ? raw : preRedact(raw), context),
30309
+ subject: this.displayMasked(subject, context),
30310
+ message: this.displayMasked(preRedact === void 0 ? raw : preRedact(raw), context),
30025
30311
  error,
30026
30312
  carriedDynamicReference: carriesDynamicReference(rawInput),
30027
30313
  carriedFetchableReference: carriesFetchableDynamicReference(rawInput)
30028
30314
  };
30029
30315
  }
30030
30316
  /**
30031
- * The needle mask alone: {@link maskSecretsForLog} without the log-twin
30317
+ * The needle mask alone: {@link maskSecretsRaw} without the log-twin
30032
30318
  * lookup. For the two DETECTORS that must ask the needle mask apart from the
30033
30319
  * position mask: `logTwinText`, which asks it of a value that already has a
30034
30320
  * twin, and `resolveBase64`, whose other operand is the position mask. A
30035
- * message masks through `maskSecretsForLog`.
30321
+ * message masks through `displayMasked`.
30036
30322
  */
30037
30323
  maskNeedlesForLog(text, context) {
30038
30324
  let masked = text;
@@ -30060,8 +30346,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30060
30346
  * part of it outside the span. The value's own needle mask cannot be merged
30061
30347
  * with the twin's positions, so the line gives both up for `***`: never
30062
30348
  * more text than either mask alone would print. Returns the text BEFORE the
30063
- * needle mask; each log line wraps it in `maskSecretsForLog` itself, so the
30064
- * resolver's mask-coverage checker sees a masker at the site.
30349
+ * needle mask; each log line wraps it in `displayMasked` itself, so the
30350
+ * sanitization happens at the render rather than here.
30065
30351
  */
30066
30352
  logTwinText(result, twin, context) {
30067
30353
  return twin !== result && this.maskNeedlesForLog(result, context) !== result ? "***" : twin;
@@ -30112,12 +30398,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30112
30398
  * the leaf treated as a resolution product — masked whole when it is a
30113
30399
  * recorded secret, otherwise the masked twin this pass registered for it —
30114
30400
  * and then `logTwinText`'s whole-line guard. The caller still wraps the
30115
- * result in `maskSecretsForLog`. Used where the text is needed BEFORE that
30401
+ * result in `displayMasked`. Used where the text is needed BEFORE that
30116
30402
  * masker, or apart from it: the `Fn::Base64` line and its detector, and the
30117
30403
  * names issue [#3150](https://github.com/go-to-k/cdkd/issues/3150)
30118
30404
  * transforms or composes before printing (`regionLogText`,
30119
30405
  * `outputNameLogText`, the invalid-region refusals, the cross-stack `origin`
30120
- * strings). A leaf printed as it is needs none of it: `maskSecretsForLog`
30406
+ * strings). A leaf printed as it is needs none of it: `displayMasked`
30121
30407
  * looks the twin up itself, which is how `maskValueLeaves` gives its lines
30122
30408
  * and the THROWN messages it masks (the `Fn::Cidr` argument refusals, for
30123
30409
  * one) the same position mask.
@@ -30357,7 +30643,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30357
30643
  maskValueLeaves(value, context) {
30358
30644
  const done = /* @__PURE__ */ new Map();
30359
30645
  const walk = (node) => {
30360
- if (typeof node === "string") return this.maskSecretsForLog(node, context);
30646
+ if (typeof node === "string") return this.displayMasked(node, context);
30361
30647
  if (node === null || typeof node !== "object") return node;
30362
30648
  const memo = done.get(node);
30363
30649
  if (memo !== void 0) return memo;
@@ -30369,7 +30655,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30369
30655
  }
30370
30656
  const out = Object.create(null);
30371
30657
  done.set(node, out);
30372
- for (const [key, child] of Object.entries(node)) out[this.maskSecretsForLog(key, context)] = walk(child);
30658
+ for (const [key, child] of Object.entries(node)) out[this.displayMasked(key, context)] = walk(child);
30373
30659
  return out;
30374
30660
  };
30375
30661
  return walk(value);
@@ -30400,10 +30686,21 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30400
30686
  * of the text in hand. A value this resolver resolved cannot be that text
30401
30687
  * (the split copy is itself recorded, so the first mask catches it), but a
30402
30688
  * template LITERAL can: issue #3150's region-scoped clients refusal prints
30403
- * a producer-region guest's region through this helper, and a literal ARN
30404
- * region spelling a recorded `st-1` as `s` + U+0001 + `t-1` is masked only
30405
- * by the second pass (`intrinsic-resolver-name-argument-log-twin.test.ts`
30406
- * pins both halves on that route).
30689
+ * a producer-region guest's region through {@link displayMasked}, and a
30690
+ * literal ARN region spelling a recorded `st-1` as `s` + U+0001 + `t-1` is
30691
+ * masked only by the second pass
30692
+ * (`intrinsic-resolver-name-argument-log-twin.test.ts` pins both halves on
30693
+ * that route).
30694
+ *
30695
+ * ONE CALLER, {@link displayMasked}, since go-to-k/cdkd#3426. It is most of
30696
+ * the answer and reads like all of it — it omits `displaySafe`, and
30697
+ * therefore `U+2028` / `U+2029` and the bidi overrides — so a site reaching
30698
+ * for it directly is the near-miss back onto the treadmill. A DIRECT
30699
+ * `${this.maskThenStripThenMask(...)}` interpolation is refused by
30700
+ * `resolver-display-masked-population.test.ts`'s line rule; a BINDING of it
30701
+ * rendered later is not seen by anything since go-to-k/cdkd#3435 deleted the
30702
+ * AST checker, which is why the one-caller containment above is the control
30703
+ * that matters.
30407
30704
  *
30408
30705
  * THE BOUND, since this file's job is to state them: this covers a needle
30409
30706
  * split by a character `stripControlChars` removes. A needle split by
@@ -30412,59 +30709,71 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30412
30709
  * solved here.
30413
30710
  */
30414
30711
  maskThenStripThenMask(value, context) {
30415
- return this.maskSecretsForLog(stripControlChars(this.maskSecretsForLog(value, context)), context);
30712
+ return this.maskSecretsRaw(stripControlChars(this.maskSecretsRaw(value, context)), context);
30416
30713
  }
30417
30714
  /**
30418
- * The ONE way a masked value reaches a message in this file.
30715
+ * The ONE way a masked value reaches a message in this file — and, since
30716
+ * go-to-k/cdkd#3426, the ONLY exit from the masking machinery at all.
30419
30717
  *
30420
30718
  * ## Why this exists rather than a rule about call sites
30421
30719
  *
30422
- * `maskSecretsForLog` answers "does this text contain a recorded secret". It
30423
- * makes NO claim about control characters, and it cannot be taught to: its
30424
- * result is also used as a COMPARISON (`maskNeedlesForLog(text) !== text`
30425
- * decides whether a twin was registered), so stripping inside it would change
30426
- * what that comparison means at every call site.
30427
- *
30428
- * That split produced a four-round treadmill on go-to-k/cdkd#3408. Round 1
30429
- * found the four `Fn::GetStackOutput` throws rendering a masked name raw and
30430
- * fixed them. Round 2 found the CloudFormation-fallback warn — the DEFAULT
30431
- * path, at DEFAULT verbosity — and the self-reference throw. Round 3 found
30432
- * the `reresolveCrossStackValue` origin builder and
30433
- * `describeAvailableOutputs`. Each round fixed what it found and the next
30434
- * round found more, because the population was never enumerated. Measured
30435
- * 2026-09-19 with a comment-stripped scan: SEVENTY-ONE interpolations
30436
- * rendered a `maskSecretsForLog` result directly, plus thirteen hand-spelled
30720
+ * {@link maskSecretsRaw} answers "does this text contain a recorded secret"
30721
+ * and makes NO claim about control characters. That split produced a
30722
+ * four-round treadmill on go-to-k/cdkd#3408. Round 1 found the four
30723
+ * `Fn::GetStackOutput` throws rendering a masked name raw and fixed them.
30724
+ * Round 2 found the CloudFormation-fallback warn — the DEFAULT path, at
30725
+ * DEFAULT verbosity — and the self-reference throw. Round 3 found the
30726
+ * `reresolveCrossStackValue` origin builder and `describeAvailableOutputs`.
30727
+ * Each round fixed what it found and the next round found more, because the
30728
+ * population was never enumerated. Measured 2026-09-19 with a
30729
+ * comment-stripped scan: SEVENTY-ONE interpolations rendered a bare masker
30730
+ * result directly, plus thirteen hand-spelled
30437
30731
  * `displaySafe(maskThenStripThenMask(...))` compositions and two bare
30438
30732
  * `maskThenStripThenMask` interpolations — and three reviewers between them
30439
30733
  * reached eight of the eighty-six.
30440
30734
  *
30441
- * So the repair is not another site. Interpolating the MASKER is now itself
30442
- * the defect, and this is the only sanctioned way to render one — which turns
30443
- * "did someone remember to strip here" into a property one scanner checks
30444
- * over the whole file at once.
30445
- * `tests/unit/deployment/resolver-display-masked-population.test.ts` is that
30446
- * scanner: it fails on any `${this.maskSecretsForLog(...)}`, so a new raw
30447
- * render in THAT SHAPE cannot be added silently.
30448
- *
30449
- * ## KNOWN RESIDUAL — the class is NARROWED, not closed
30450
- *
30451
- * Stated here rather than implied away, because an earlier revision of this
30452
- * comment claimed the class was "closed by CONSTRUCTION" and that is FALSE.
30453
- * The scanner is line-shaped and forbids only the `${this.<masker>(` spelling.
30454
- * TEN local bindings take a raw masker result and interpolate it later, which
30455
- * it cannot see — and at least one is a live exposure:
30456
- * `loggedExportName` (bound from `maskSecretsForLog`) reaches an
30457
- * `Fn::ImportValue` throw carrying an unstripped ESC and CR, measured on this
30458
- * tree. `loggedSecretId`, `loggedJsonKey`, `loggedParameterName`,
30459
- * `maskedAttributeName` and `loggedOrigin` are the same shape.
30460
- *
30461
- * Do NOT close those by hand at the ten sites: that is the fifth round of the
30462
- * enumeration this builder exists to end, and the mechanism that resolves an
30463
- * identifier to its initializer ALREADY EXISTS in this repo —
30464
- * `sanitizedLocals` in
30465
- * `tests/unit/cli/local-profile-display-population.test.ts`.
30466
- * go-to-k/cdkd#3426 owns binding the scanner to it; go-to-k/cdkd#3405 and
30467
- * go-to-k/cdkd#3411 own the surrounding scope and parser work.
30735
+ * ROUND FIVE was the BINDING shape: `const loggedExportName =
30736
+ * this.<masker>(...)` interpolated later, which a line-shaped scanner
30737
+ * forbidding `${this.<masker>(` structurally cannot see. Ten of them existed,
30738
+ * and `loggedExportName` was a LIVE exposure — an `Fn::ImportValue` export
30739
+ * name carrying `ESC[2K` + CR reached a warn AND a throw unstripped, at
30740
+ * default verbosity on the default path.
30741
+ *
30742
+ * Patching those ten sites would have been the fifth round of one
30743
+ * enumeration. What closes the class instead is that there is no longer a
30744
+ * masking answer a site can reach WITHOUT the strip: every escaping call of
30745
+ * the old `maskSecretsForLog` now calls this builder, the raw masker is
30746
+ * reachable only through {@link maskThenStripThenMask}, and that helper is
30747
+ * reachable only from here.
30748
+ *
30749
+ * ## What keeps it closed
30750
+ *
30751
+ * `tests/unit/deployment/resolver-display-masked-population.test.ts`, which
30752
+ * asserts from the AST that `maskSecretsRaw` is referenced only inside
30753
+ * `maskThenStripThenMask` and that helper only inside this builder — exact
30754
+ * reference counts, no inference — and refuses a direct
30755
+ * `${this.<raw masker>(...)}` interpolation by line rule.
30756
+ *
30757
+ * An AST walk that ALSO resolved an interpolated identifier to its
30758
+ * declaration used to cover the BINDING shape. go-to-k/cdkd#3435 deleted it
30759
+ * as high-maintenance tooling (23 commits across it and its suite in nine
30760
+ * days; a widening attempted one PR earlier produced nine defects inside
30761
+ * itself and was withdrawn), so that shape is now held by the containment
30762
+ * above — there is no bare masker to bind, because nothing outside this
30763
+ * builder may call one.
30764
+ *
30765
+ * KNOWN BOUND, stated rather than implied away: a site carrying an
30766
+ * exclusion marker is exempt from that walk, because the marker answers the
30767
+ * SECRET question ("this value cannot carry a plaintext"), which is not the
30768
+ * control-character question. Such a site sanitizes by hand or not at all —
30769
+ * `clientsForRegion`'s two region renders are both markered, and both
30770
+ * sanitize. Widening the walk to judge every interpolated value is
30771
+ * go-to-k/cdkd#3405's mixed-render scope, not this builder's.
30772
+ *
30773
+ * (The marker's literal spelling is deliberately not written in this comment:
30774
+ * the checker scans the raw text for it, and a prose mention parses as a
30775
+ * marker no site consumes — which it then reports STALE. Measured while
30776
+ * writing this paragraph.)
30468
30777
  *
30469
30778
  * ## What it does, in order
30470
30779
  *
@@ -30476,10 +30785,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30476
30785
  * Trojan-Source bidi overrides. That composition is what the `physicalId`
30477
30786
  * renders here already spelled by hand; this names it, and they now share it.
30478
30787
  *
30479
- * It is listed in `MASKERS` in `scripts/check-resolver-mask-coverage.ts`,
30480
- * which is a SECURITY decision and a sound one: every path through it passes
30481
- * `maskSecretsForLog`, so it is strictly stronger than the entry it replaces
30482
- * and cannot be weaker at any input.
30788
+ * It is the ONE sanctioned exit from the masking machinery, which is a
30789
+ * SECURITY decision and a sound one: every path through it passes the
30790
+ * needle-and-twin mask, twice, so it is strictly stronger than the bare
30791
+ * masker it replaced and cannot be weaker at any input.
30483
30792
  */
30484
30793
  displayMasked(value, context) {
30485
30794
  return displaySafe(this.maskThenStripThenMask(value, context));
@@ -30489,16 +30798,16 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30489
30798
  * the same reason as {@link displayMasked} and closed by the same scanner.
30490
30799
  *
30491
30800
  * `logTextOfLeaf` resolves a value to its registered twin (or `SECRET_MASK`),
30492
- * which is a MASKING answer and, like `maskSecretsForLog`'s, says nothing
30801
+ * which is a MASKING answer and, like `maskSecretsRaw`'s, says nothing
30493
30802
  * about control characters. Four `origin` builders interpolated it raw — the
30494
30803
  * `Fn::ImportValue` pair, the `Fn::GetStackOutput` one and the nested-stack
30495
30804
  * attribute one — and those strings flow into `redactedAttributeReads[].display`
30496
30805
  * and out through a `ProvisioningError` message.
30497
30806
  *
30498
- * This route is why fixing the masker alone did not close the class:
30499
- * `displayMasked` covers every site that interpolates `maskSecretsForLog`,
30500
- * and these interpolate something else. Two routes, two names, one scanner
30501
- * forbidding the raw form of both.
30807
+ * This route is why fixing the secret masker alone did not close the class:
30808
+ * `displayMasked` covers every site that renders a needle-masked value, and
30809
+ * these render something else. Two routes, two names, one checker refusing
30810
+ * the raw form of both.
30502
30811
  *
30503
30812
  * Composed rather than re-derived. `maskSecretsInText` is idempotent (see
30504
30813
  * {@link maskThenStripThenMask}), so masking twin text again costs nothing
@@ -30557,7 +30866,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30557
30866
  continue;
30558
30867
  }
30559
30868
  const regionVerdict = classifyReplaySecretRegion(fullMatch, this.explicitRegion ?? this.resolverRegion, context?.producerRegions);
30560
- if (regionVerdict.kind === "ambiguous") throw markNonRetryable(new DynamicReferenceRegionAmbiguousError(`Refusing to resolve the secret reference ${this.displayMasked(tokenLogText, context)}: it names '${this.displayMasked(nameLogText(regionVerdict.secretName), context)}' without a region, and this stack reads from ${this.displayMasked(regionVerdict.foreignProducerRegions.map((r) => this.maskSecretsForLog(r, context)).join(", "), context)} as well as its own region. cdkd cannot tell which one must answer, and resolving against the wrong one yields a different secret. Spell the reference as a full ARN to say which region owns it.`));
30869
+ if (regionVerdict.kind === "ambiguous") throw markNonRetryable(new DynamicReferenceRegionAmbiguousError(`Refusing to resolve the secret reference ${this.displayMasked(tokenLogText, context)}: it names '${this.displayMasked(nameLogText(regionVerdict.secretName), context)}' without a region, and this stack reads from ${this.displayMasked(regionVerdict.foreignProducerRegions.map((r) => this.displayMasked(r, context)).join(", "), context)} as well as its own region. cdkd cannot tell which one must answer, and resolving against the wrong one yields a different secret. Spell the reference as a full ARN to say which region owns it.`));
30561
30870
  if (regionVerdict.kind === "named-region") {
30562
30871
  const foreign = await this.resolverForProducerRegion(regionVerdict.region, context, nameLogText(regionVerdict.region)).resolveDynamicReferencesWithLogTwin(fullMatch, fullMatch, withoutProducerRegions(context), {
30563
30872
  tokenLogText,
@@ -30626,7 +30935,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30626
30935
  isSecret = true;
30627
30936
  resolved = param.value;
30628
30937
  } else {
30629
- this.logger.warn(this.maskSecretsForLog(`Unsupported dynamic reference service: ${this.displayMasked(nameLogText(String(service)), context)}`, context));
30938
+ this.logger.warn(this.displayMasked(`Unsupported dynamic reference service: ${this.displayMasked(nameLogText(String(service)), context)}`, context));
30630
30939
  complete = false;
30631
30940
  continue;
30632
30941
  }
@@ -30714,8 +31023,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30714
31023
  } else secretId = afterService;
30715
31024
  if (!versionStage) versionStage = "AWSCURRENT";
30716
31025
  if (!secretId) throw new Error("Dynamic reference: secretsmanager SECRET_ID is required");
30717
- const loggedSecretId = this.maskSecretsForLog(nameLogText(secretId), context);
30718
- const loggedJsonKey = this.maskSecretsForLog(nameLogText(jsonKey), context);
31026
+ const loggedSecretId = this.displayMasked(nameLogText(secretId), context);
31027
+ const loggedJsonKey = this.displayMasked(nameLogText(jsonKey), context);
30719
31028
  this.logger.debug(`Resolving dynamic reference: secretsmanager:${loggedSecretId}:SecretString:${loggedJsonKey}:${this.displayMasked(nameLogText(versionStage), context)}:${this.displayMasked(nameLogText(versionId), context)}`);
30720
31029
  const client = this.clientsForRegion(this.explicitRegion).secretsManager;
30721
31030
  const command = new GetSecretValueCommand({
@@ -30880,7 +31189,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
30880
31189
  async resolveSSMReference(parts, decrypt, service, context, nameLogText) {
30881
31190
  const parameterName = parts.slice(1).join(":");
30882
31191
  if (!parameterName) throw new Error(`Dynamic reference: ${service} PARAMETER_NAME is required`);
30883
- const loggedParameterName = this.maskSecretsForLog(nameLogText(parameterName), context);
31192
+ const loggedParameterName = this.displayMasked(nameLogText(parameterName), context);
30884
31193
  this.logger.debug(`Resolving dynamic reference: ${service}:${loggedParameterName}`);
30885
31194
  const client = this.clientsForRegion(this.explicitRegion).ssm;
30886
31195
  const command = new GetParameterCommand({
@@ -32578,7 +32887,7 @@ var CloudControlProvider = class {
32578
32887
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
32579
32888
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
32580
32889
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
32581
- const { ASGProvider } = await import("./asg-provider-De5KONKr.js").then((n) => n.n);
32890
+ const { ASGProvider } = await import("./asg-provider-BuEvX2d9.js").then((n) => n.n);
32582
32891
  const asgProvider = new ASGProvider();
32583
32892
  return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
32584
32893
  }
@@ -43214,11 +43523,38 @@ var DeployEngine = class DeployEngine {
43214
43523
  * cdkd never EXECUTES this string — it is advice in an error message — so
43215
43524
  * this is about the pasted command being correct, not about injection into
43216
43525
  * cdkd itself (issue #2847 round-5 review).
43526
+ *
43527
+ * SINCE go-to-k/cdkd#3435 no id reaching this arm can contain a `'`:
43528
+ * `isPasteableIdent` admits `[A-Za-z0-9][A-Za-z0-9~_.-]*` only, and the
43529
+ * command is not emitted for anything else. Kept anyway rather than
43530
+ * deleted — dropping a guard on the strength of a NEW guard one line up is
43531
+ * how the pair later comes apart, and this one costs a `replaceAll` on a
43532
+ * short string.
43217
43533
  */
43218
43534
  const quoteSafe = (id) => id.replaceAll("'", `'\\''`);
43535
+ /**
43536
+ * A logical id rendered as a NAME rather than as a command argument
43537
+ * (go-to-k/cdkd#3435 security round).
43538
+ *
43539
+ * Every sentence below is joined into a `ProvisioningError` message that
43540
+ * `handleError` prints at DEFAULT verbosity, and neither `formatError` nor
43541
+ * the logger sanitizes `error.message` — they cover a `cause` and the extra
43542
+ * ARGS respectively. So a `Resources` KEY carrying `ESC[2K` + CR reached the
43543
+ * terminal raw, which is the same class this PR closed at 18 resolver
43544
+ * renders and missed one module out.
43545
+ */
43546
+ const shown = (id) => displayIdent(id);
43547
+ /** CloudFormation's own logical-id length bound. */
43548
+ const CFN_LOGICAL_ID_MAX_LENGTH = 255;
43219
43549
  const parts = [];
43220
- if (localTargets.length > 0) parts.push(`Re-import the record that HOLDS the mask: ` + localTargets.map((id) => `'cdkd import <stack> --resource ${quoteSafe(id)}=<physicalId> --force'`).join(", ") + `.`);
43221
- if (unclearableTargets.length > 0) parts.push(`Do NOT re-import ${unclearableTargets.join(", ")}: a custom resource's import records no attributes, so the masked bag is carried forward unchanged and the refusal repeats. Cause (1) above is the one that applies to it.`);
43550
+ if (localTargets.length > 0) {
43551
+ const pasteableHere = (id) => isPasteableIdent(id) && id.length <= CFN_LOGICAL_ID_MAX_LENGTH;
43552
+ const pasteable = localTargets.filter(pasteableHere);
43553
+ const withheld = localTargets.filter((id) => !pasteableHere(id));
43554
+ if (pasteable.length > 0) parts.push(`Re-import the record that HOLDS the mask: ` + pasteable.map((id) => `'cdkd import <stack> --resource ${quoteSafe(id)}=<physicalId> --force'`).join(", ") + `.`);
43555
+ if (withheld.length > 0) parts.push(`Re-import the record that HOLDS the mask for ${withheld.map(shown).join(", ")}, but the command is withheld: that is not a plain CloudFormation logical id, so a pasted 'cdkd import <stack> --resource <id>=<physicalId> --force' could be reshaped by the shell or name a different resource. Read the id from 'cdkd state show' and quote it yourself.`);
43556
+ }
43557
+ if (unclearableTargets.length > 0) parts.push(`Do NOT re-import ${unclearableTargets.map(shown).join(", ")}: a custom resource's import records no attributes, so the masked bag is carried forward unchanged and the refusal repeats. Cause (1) above is the one that applies to it.`);
43222
43558
  if (foreignReads.length > 0) {
43223
43559
  const subject = reads.length === 1 ? "The read above resolves" : foreignReads.length === 1 ? "One of the reads above resolves" : "Some of the reads above resolve";
43224
43560
  parts.push(`${subject} through ANOTHER stack (an Fn::ImportValue, an Fn::GetStackOutput, or a nested stack's Outputs), whose masked record lives in that stack's state — re-importing anything in this stack cannot clear it; act on the producer stack instead.`);
@@ -45645,4 +45981,4 @@ var DeployEngine = class DeployEngine {
45645
45981
 
45646
45982
  //#endregion
45647
45983
  export { secretBearing as $, SynthesisError as $i, recordMaskOnlyValue as $n, AssetManifestLoader as $r, s3BucketArn as $t, extractDeploymentEventError as A, getAwsClients as Ai, refuseMalformedState as An, BOOTSTRAP_MARKER_PREFIX as Ar, isImpossibleEmptyStoredAttribute as At, toCfnInteger as B, IntrinsicResolutionRefusalError as Bi, STATE_SOURCED_BASELINE_RULES as Bn, buildDockerImage as Br, coerceCfnBoolean as Bt, buildFinalSnapshotIdentifier as C, canonicalizeRegion as Ci, producerRecordKey as Cn, stringifyValue as Cr, isTerminationProtectionPropagationError as Ct, refusesFinalSnapshot as D, clearBucketRegionCache as Di, refuseMalformedOutputsForDestroy as Dn, loadPublishableAssetManifest as Dr, cfnRefValueFromPhysicalId as Dt, isFinalSnapshotError as E, processStackMessages as Ei, refuseMalformedOutputs as En, createAssetRedirectResolver as Er, carriesFetchableDynamicReference as Et, isStatefulRecreateTargetSync as F, ConfigError as Fi, findSilentDropProperties as Fn, parseBootstrapMarker as Fr, isSettledInstanceState as Ft, gray as G, NestedStackChildDirectDestroyError as Gi, createSecretMasker as Gn, formatDockerLoginError as Gr, readConfigString as Gt, formatResourceLine as H, LocalStartServiceError as Hi, STATE_SOURCED_READBACK_RULES as Hn, describeDockerExecFailure as Hr, configBooleanRefusal as Ht, renderStatefulReason as I, CrossAccountSecretRefusalError as Ii, describeTypeWithThrottleRetry as In, readBootstrapMarkerBody as Ir, WAFv2WebACLProvider as It, yellow as J, ResourceTimeoutError as Ji, identityKeyFor as Jn, redactDockerArgvValues as Jr, requireConfigObject as Jt, green as K, PartialFailureError as Ki, dynamicReferenceTokens as Kn, getDockerCmd as Kr, replayWarn as Kt, WARM_THROUGHPUT_MEMBERS as L, DependencyError as Li, withRetry as Ln, validateAssetBucketName as Lr, normalizeAwsTagsToCfn as Lt, planOrphanAdoption as M, setAwsClients as Mi, repairMalformedResourcePropertiesForReadOnly as Mn, ensureAssetStorage as Mr, isUnboundTemplateParameter as Mt, MULTI_REGION_RECREATE_BLOCKED_TYPES as N, AssetError as Ni, repairMalformedResourcesForReadOnly as Nn, getBootstrapMarkerKey as Nr, parameterTypeMayLoseSecretIdentity as Nt, unsupportedFinalSnapshotError as O, resolveBucketRegion as Oi, refuseMalformedResourcePropertiesForOrphan as On, rewriteTemplateAssetReferences as Or, coerceParameterTypedValue as Ot, isStatefulRecreateTargetForReplace as P, CdkdError as Pi, findActionableSilentDrops as Pn, isCrossRegionRedirect as Pr, refStateLookupFromResource as Pt, isExportAliasCollision as Q, StateError as Qi, maskSecretsInText as Qn, stripControlChars as Qr, withSharedDrainBudget as Qt, coerceWarmThroughput as R, DeployCancelledError as Ri, DagBuilder as Rn, validateContainerRepoName as Rr, resolveExplicitPhysicalId as Rt, PRE_DELETE_SNAPSHOT_TYPES as S, PARTITION_TABLE as Si, malformedStateRefusalMessage as Sn, AssetPublisher as Sr, disableInstanceApiTermination as St, createPreDeleteFinalSnapshot as T, AssemblyReader as Ti, refuseMalformedNestedChildOutputs as Tn, buildAssetRedirectMap as Tr, carriesDynamicReference as Tt, bold as U, LockError as Ui, TEMPLATE_SOURCED_RULES as Un, describeDockerFailure as Ur, configIntegerRefusal as Ut, toFiniteNumber as V, LocalInvokeBuildError as Vi, STATE_SOURCED_CROSS_GENERATION_RULES as Vn, describeDockerCapturedOutput as Vr, coerceCfnInteger as Vt, cyan as W, MalformedProducerRecordRefusalError as Wi, carriesSecretMask as Wn, dockerSpawnEnvWithSensitive as Wr, configStringRefusal as Wt, collectPublishedOutputNames as X, StackHasActiveImportsError as Xi, markSameGenerationBag as Xn, runDockerStreaming as Xr, classifyReplaySecretRegion as Xt, collectDeclaredOutputNames as Y, ResourceUpdateNotSupportedError as Yi, isSingleDynamicReferenceToken as Yn, runDockerForeground as Yr, requireConfigString as Yt, exportAliasCollisionScrubWarning as Z, StackTerminationProtectionError as Zi, maskSecretsInError as Zn, escapeRegExp$1 as Zr, producerRegionsFromState as Zt, isSecretBearingReferenceString as _, findLargeInlineResources as _i, malformedOutputsRefusalMessage as _n, importableOutputs as _r, isWaitAbandonedError as _t, DeploymentEventsStore as a, isRetryableTransientError as aa, resolveApp as ai, AWS_CREDENTIAL_ENV_KEYS as an, rebuildClientForBucketRegion as ar, clearOnUpdateRemoval as at, bindingSkippedOutputs as b, decodeListingKey as bi, malformedResourcePropertiesWarning as bn, orphansCarriedFrom as br, deleteIndeterminateGuards as bt, replayFailedOperations as c, markNonRetryable as ca, resolveSkipPrefix as ci, envCredentialsAreAssumedRole as cn, buildLockContentionMessage as cr, createMaskedRetryLogger as ct, updatePartialReason as d, describeAwsFailure as da, resolveUseCdkBootstrapAssets as di, STATE_RESOURCES_MALFORMED as dn, shellQuote as dr, beginCommandInterruptScope as dt, formatError as ea, getDockerImageBySourceHash as ei, s3BucketDomainName as en, recoverMaskedOutput as er, secretBearingStateKeyWarning as et, withResourceDeadline as f, safeStringify$1 as fa, stateBucketExistenceConfirmed as fi, hasReadableOutputs as fn, CUSTOM_RESOURCE_RESPONSE_PREFIX as fr, endCommandInterruptScope as ft, bagHoldsSecretExpression as g, MIGRATE_TMP_PREFIX as gi, malformedLocalOutputsWarning as gn, importableOutputKeys as gr, CloudControlProvider as gt, computeImplicitDeleteEdges as h, CFN_TEMPLATE_URL_LIMIT as hi, malformedExportSourceWarning as hn, hasReadableExportSet as hr, startInterruptWatch as ht, DeploymentEventsReader as i, isMarkedNonRetryable as ia, getLegacyStateBucketName as ii, applyRoleArnIfSet as in, S3StateBackend as ir, collectInlinePolicyNamesManagedBySiblings as it, makeSiblingClaimReader as j, resetAwsClients as ji, repairMalformedOutputsForReadOnly as jn, assertAssetBucketRegion as jr, isNamelessDynamicReferenceError as jt, makeCanonicalizePropertiesFn as k, AwsClients as ki, refuseMalformedResourcesForDestroy as kn, AssetModeResolver as kr, getAccountInfo as kt, replayRollback as l, markRedactedCause as la, resolveStateBucketWithDefault as li, DiffCalculator as ln, forceQuitRecoveryClause as lr, maskDeep as lt, IMPLICIT_DELETE_DEPENDENCIES as m, CFN_TEMPLATE_BODY_LIMIT as mi, malformedExportNamesWarning as mn, exportNamesCarriedFrom as mr, isInterruptedWaitError as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, normalizeAwsError as na, synthesisStatusMessage as ni, s3BucketRegionalDomainName as nn, scrubResourceRecord as nr, getCurrentResourceSecrets as nt, planFailedOps as o, isThrottlingError as oa, resolveAutoAssetStorage as oi, applyCallerIdentityCredentials as on, UNREPRODUCIBLE_LOCK_CLAUSE as or, ProviderRegistry as ot, maskingRetryLogger as p, __exportAll as pa, warnDeprecatedNoPrefixCliFlag as pi, hasReadableResources as pn, DEFAULT_STATE_PREFIX as pr, interruptWatchListenerCount as pt, red as q, ProvisioningError as qi, errorCauseChain as qn, partitionSensitiveEnv as qr, requireConfigArray as qt, DeployEngine as r, withErrorHandling as ra, getDefaultStateBucketName as ri, s3BucketWebsiteUrl as rn, LockManager as rr, IAMRoleProvider as rt, planRollback as s, isTransientServerError as sa, resolveCaptureObservedState as si, callerEnvCredentials as sn, buildForceUnlockCommand as sr, wouldReturnToSdkProvider as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, isCdkdError as ta, Synthesizer as ti, s3BucketDualStackDomainName as tn, redactSecretsForState as tr, secretSafeKeyDisplay as tt, updatePartialMessage as u, retryClassificationText as ua, resolveStateBucketWithDefaultAndSource as ui, INTRINSIC_KEYS as un, formatLockExpiry as ur, maskerOrIdentity as ut, keptWholeReasonText as v, uploadCfnTemplate as vi, malformedOutputsWarning as vn, isReadableBag as vr, slowCcOperationTimeoutMs as vt, ccRoutedFinalSnapshotError as w, derivePartitionAndUrlSuffix as wi, refuseDivergentRecordRegionForDestroy as wn, WorkGraph as wr, IntrinsicFunctionResolver as wt, ATOMIC_FINAL_SNAPSHOT_TYPES as x, expectedOwnerParam as xi, malformedResourcesWarning as xn, shouldRetainResource as xr, deleteSkipReason as xt, mergeNoChangeOutputs as y, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as yi, malformedRenderedContainersWarning as yn, orphansAfterRollback as yr, UNSPECIFIED_SKIP_REASON as yt, isWarmThroughputDecrease as z, DynamicReferenceRegionAmbiguousError as zi, TemplateParser as zn, buildDenyExternalAccessPolicy as zr, assertRegionMatch as zt };
45648
- //# sourceMappingURL=deploy-engine-mI1Xv1oi.js.map
45984
+ //# sourceMappingURL=deploy-engine-CfOsVRQB.js.map