@go-to-k/cdkd 0.290.45 → 0.290.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- import { C as displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, S as UNRENDERABLE, T as displaySafe, d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS, y as withoutGeneratedFallbackName } from "./logger-DjBU2Ap3.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-GASr9bAF.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-DMaX1bq2.js";
3
+ import { t as getCdkdVersion } from "./version-DpaXQZoG.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";
@@ -1887,7 +1887,7 @@ function formatError(error) {
1887
1887
  if (isCdkdError(error)) {
1888
1888
  let message = `${error.name}: ${error.message}`;
1889
1889
  if (error.cause) {
1890
- const cause = displaySafe(error.cause.message);
1890
+ const cause = displayAwsMessage(error.cause.message);
1891
1891
  if (cause) message += `\nCaused by: ${cause}`;
1892
1892
  }
1893
1893
  return message;
@@ -4331,22 +4331,24 @@ var ContextProviderRegistry = class {
4331
4331
  const results = {};
4332
4332
  for (const entry of missing) {
4333
4333
  const provider = this.providers.get(entry.provider);
4334
+ const shownProvider = displaySafe(entry.provider);
4334
4335
  if (!provider) {
4335
- this.logger.warn(`No context provider registered for: ${entry.provider}`);
4336
+ this.logger.warn(`No context provider registered for: ${shownProvider}`);
4336
4337
  results[entry.key] = {
4337
- [PROVIDER_ERROR_KEY]: `Unknown context provider: ${entry.provider}`,
4338
+ [PROVIDER_ERROR_KEY]: `Unknown context provider: ${shownProvider}`,
4338
4339
  [TRANSIENT_CONTEXT_KEY]: true
4339
4340
  };
4340
4341
  continue;
4341
4342
  }
4343
+ const shownKey = displaySafe(entry.key);
4342
4344
  try {
4343
- this.logger.debug(`Resolving context: ${entry.provider} (key: ${entry.key})`);
4345
+ this.logger.debug(`Resolving context: ${shownProvider} (key: ${shownKey})`);
4344
4346
  const value = await provider.resolve(entry.props);
4345
4347
  results[entry.key] = value;
4346
- this.logger.debug(`Resolved context: ${entry.key}`);
4348
+ this.logger.debug(`Resolved context: ${shownKey}`);
4347
4349
  } catch (error) {
4348
- const message = error instanceof Error ? error.message : String(error);
4349
- this.logger.error(`Context provider '${entry.provider}' failed: ${message}`);
4350
+ const message = displayAwsMessage(error instanceof Error ? error.message : String(error));
4351
+ this.logger.error(`Context provider '${shownProvider}' failed: ${message}`);
4350
4352
  results[entry.key] = {
4351
4353
  [PROVIDER_ERROR_KEY]: message,
4352
4354
  [TRANSIENT_CONTEXT_KEY]: true
@@ -5961,6 +5963,21 @@ function ssmResolvedValueType(type) {
5961
5963
 
5962
5964
  //#endregion
5963
5965
  //#region src/synthesis/macro-expander.ts
5966
+ /**
5967
+ * A transform name comes from the TEMPLATE's own `Transform` / `Fn::Transform`
5968
+ * node, so every render of one goes through `displaySafe`, in thrown messages
5969
+ * and in the `debug` line alike
5970
+ * ([#3479](https://github.com/go-to-k/cdkd/issues/3479)). `displaySafe` neither
5971
+ * quotes nor truncates, so a legitimate `AWS::Serverless-2016-10-31` renders
5972
+ * byte-identically.
5973
+ *
5974
+ * A JOINED list sanitizes per ELEMENT so the separator stays
5975
+ * byte-exact; `displaySafe`'s own doc carries why that is a formatting rule
5976
+ * rather than a safety one.
5977
+ */
5978
+ function displaySafeTransforms(names) {
5979
+ return names.map((name) => displaySafe(name)).join(", ");
5980
+ }
5964
5981
  /** 600 seconds = 10 minutes. SDK waiter's `maxWaitTime` is in seconds. */
5965
5982
  const WAITER_MAX_WAIT_SECONDS = 600;
5966
5983
  const PARAMETER_PLACEHOLDER = "cdkd-macro-expand-placeholder";
@@ -5978,8 +5995,51 @@ const PARAMETER_PLACEHOLDER = "cdkd-macro-expand-placeholder";
5978
5995
  const EARLY_VALIDATION_MAX_ATTEMPTS = 3;
5979
5996
  const EARLY_VALIDATION_RETRY_BASE_DELAY_MS = 2e3;
5980
5997
  /** Matches the changeset FAILED StatusReason emitted by the hook family. */
5998
+ const EARLY_VALIDATION_MARKER = /AWS::EarlyValidation::/;
5999
+ /**
6000
+ * Where the retry verdict is CARRIED, rather than re-derived from the rendered
6001
+ * message.
6002
+ *
6003
+ * The verdict used to be `EARLY_VALIDATION_MARKER.test(err.message)`, and that
6004
+ * became wrong the moment AWS's reply started rendering through
6005
+ * `displayAwsMessage` ([#3479](https://github.com/go-to-k/cdkd/issues/3479)):
6006
+ * the cap CUTS at `AWS_MESSAGE_MAX_CODE_POINTS`, so a `StatusReason` carrying
6007
+ * the hook marker past that point would no longer match, and `cdkd deploy` of a
6008
+ * macro stack would hard-fail on attempt 1 where issue
6009
+ * [#1151](https://github.com/go-to-k/cdkd/issues/1151) says it must retry. The
6010
+ * sanitizer's CHARSET half was never the risk — the marker is plain ASCII —
6011
+ * but a decision re-derived from display text inherits every display rule,
6012
+ * including ones added later for unrelated reasons.
6013
+ *
6014
+ * So each throw site tests the RAW text once and records the answer here. A
6015
+ * symbol rather than a field on `MacroExpansionError`: the verdict is this
6016
+ * module's business, not the shared error class's.
6017
+ *
6018
+ * IT ALSO CLOSES A PATH THAT PRE-DATES THE CAP, which is the stronger argument
6019
+ * for the shape. The old predicate matched the WHOLE message, and two of cdkd's
6020
+ * OWN refusals below interpolate template-supplied transform names — so an
6021
+ * assembly declaring `Transform: AWS::EarlyValidation::x` made the multi-stage
6022
+ * and no-TemplateBody refusals test positive, and a template value steered the
6023
+ * retry loop into three CFn changeset round-trips plus the backoff between
6024
+ * them. A verdict read from a symbol set out of AWS's own text cannot be
6025
+ * steered that way at all.
6026
+ *
6027
+ * IT RIDES THE INSTANCE, which is the one way a future edit can lose it: a site
6028
+ * that catches one of these and wraps it in a NEW `MacroExpansionError` drops
6029
+ * the verdict silently — the same shape as the bug above, one indirection over.
6030
+ * Re-tag at any such wrap.
6031
+ */
6032
+ const EARLY_VALIDATION = Symbol("cdkd.macroExpansion.earlyValidationRejection");
6033
+ /**
6034
+ * Tag `error` when `rawReason` — AWS's text BEFORE any display helper touches
6035
+ * it — names a validation hook.
6036
+ */
6037
+ function withEarlyValidationVerdict(error, rawReason) {
6038
+ if (EARLY_VALIDATION_MARKER.test(rawReason)) error[EARLY_VALIDATION] = true;
6039
+ return error;
6040
+ }
5981
6041
  function isEarlyValidationRejection(err) {
5982
- return err instanceof MacroExpansionError && /AWS::EarlyValidation::/.test(err.message);
6042
+ return err instanceof MacroExpansionError && err[EARLY_VALIDATION] === true;
5983
6043
  }
5984
6044
  /** Test seam: overridable sleep so retry tests don't wait wall-clock. */
5985
6045
  const retryDelays = { sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)) };
@@ -6093,7 +6153,7 @@ async function expandMacros(template, opts) {
6093
6153
  }
6094
6154
  async function expandMacrosAttempt(template, opts, logger) {
6095
6155
  const macros = enumerateMacros(template);
6096
- logger.debug(`Macro expansion: detected transforms [${macros.join(", ")}], starting CFn round-trip...`);
6156
+ logger.debug(`Macro expansion: detected transforms [${displaySafeTransforms(macros)}], starting CFn round-trip...`);
6097
6157
  const transientStackName = `cdkd-macro-expand-${randomUUID().slice(0, 16)}`;
6098
6158
  const changeSetName = `${transientStackName}-changeset`;
6099
6159
  const region = opts.region;
@@ -6136,7 +6196,8 @@ async function expandMacrosAttempt(template, opts, logger) {
6136
6196
  ...parameters.length > 0 && { Parameters: parameters }
6137
6197
  }));
6138
6198
  } catch (err) {
6139
- throw new MacroExpansionError(`CloudFormation rejected the macro-expansion changeset: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : void 0);
6199
+ const rawMessage = err instanceof Error ? err.message : String(err);
6200
+ throw withEarlyValidationVerdict(new MacroExpansionError(`CloudFormation rejected the macro-expansion changeset: ${displayAwsMessage(rawMessage)}`, err instanceof Error ? err : void 0), rawMessage);
6140
6201
  }
6141
6202
  let waiterFailed = false;
6142
6203
  let waiterError;
@@ -6159,20 +6220,21 @@ async function expandMacrosAttempt(template, opts, logger) {
6159
6220
  StackName: transientStackName,
6160
6221
  ChangeSetName: changeSetName
6161
6222
  })).catch(() => void 0);
6162
- const reason = desc?.StatusReason ?? "unknown (DescribeChangeSet failed)";
6163
- const status = desc?.Status ?? "UNKNOWN";
6164
- throw new MacroExpansionError(`CloudFormation macro expansion failed (status=${status}): ${reason}`, waiterError instanceof Error ? waiterError : void 0);
6223
+ const rawReason = desc?.StatusReason ?? "unknown (DescribeChangeSet failed)";
6224
+ const reason = displayAwsMessage(rawReason);
6225
+ const status = displaySafe(desc?.Status ?? "UNKNOWN");
6226
+ throw withEarlyValidationVerdict(new MacroExpansionError(`CloudFormation macro expansion failed (status=${status}): ${reason}`, waiterError instanceof Error ? waiterError : void 0), rawReason);
6165
6227
  }
6166
6228
  const tpl = await cfn.send(new GetTemplateCommand({
6167
6229
  StackName: transientStackName,
6168
6230
  ChangeSetName: changeSetName,
6169
6231
  TemplateStage: "Processed"
6170
6232
  }));
6171
- if (tpl.TemplateBody === void 0 || tpl.TemplateBody === null) throw new MacroExpansionError(`CloudFormation returned no Processed-stage template body for the macro-expansion changeset. This typically indicates a CFn-side regression — re-run, and if the failure persists open an issue with the transforms involved: [${macros.join(", ")}].`);
6233
+ if (tpl.TemplateBody === void 0 || tpl.TemplateBody === null) throw new MacroExpansionError(`CloudFormation returned no Processed-stage template body for the macro-expansion changeset. This typically indicates a CFn-side regression — re-run, and if the failure persists open an issue with the transforms involved: [${displaySafeTransforms(macros)}].`);
6172
6234
  const expanded = parseTemplateBody(tpl.TemplateBody);
6173
6235
  if (containsMacro(expanded)) {
6174
6236
  const inner = enumerateMacros(expanded);
6175
- throw new MacroExpansionError(`Macro expansion produced a template that still contains macros [${inner.join(", ")}]. Multi-stage macros (a macro whose expansion emits another macro reference) are intentionally out of scope in cdkd v1 — see https://github.com/go-to-k/cdkd/issues/463. If you need this pattern, manually pre-expand the template and deploy the result.`);
6237
+ throw new MacroExpansionError(`Macro expansion produced a template that still contains macros [${displaySafeTransforms(inner)}]. Multi-stage macros (a macro whose expansion emits another macro reference) are intentionally out of scope in cdkd v1 — see https://github.com/go-to-k/cdkd/issues/463. If you need this pattern, manually pre-expand the template and deploy the result.`);
6176
6238
  }
6177
6239
  logger.debug(`Macro expansion: success — ${Object.keys(expanded.Resources ?? {}).length} resources after expansion.`);
6178
6240
  return expanded;
@@ -6243,7 +6305,7 @@ function stringifyParamDefault(value, type, paramKey, logger) {
6243
6305
  if (inner !== void 0 && isListParameterType(inner)) return "placeholder,placeholder";
6244
6306
  return "placeholder";
6245
6307
  }
6246
- logger.warn(`Parameter '${paramKey}' has unrecognized CFn Type '${type}'; using a generic string placeholder for the transient macro-expansion changeset. If CFn rejects the changeset with a type error, file an issue with the offending Type.`);
6308
+ logger.warn(`Parameter '${displaySafe(paramKey)}' has unrecognized CFn Type '${displaySafe(type)}'; using a generic string placeholder for the transient macro-expansion changeset. If CFn rejects the changeset with a type error, file an issue with the offending Type.`);
6247
6309
  return PARAMETER_PLACEHOLDER;
6248
6310
  }
6249
6311
  return PARAMETER_PLACEHOLDER;
@@ -6260,7 +6322,7 @@ function parseTemplateBody(body) {
6260
6322
  if (typeof body === "string") try {
6261
6323
  parsed = JSON.parse(body);
6262
6324
  } catch (err) {
6263
- throw new MacroExpansionError(`CloudFormation returned a non-JSON Processed-stage template body. cdkd's macro-expansion path only supports JSON-shaped synth templates (CDK apps emit JSON by default). Cause: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : void 0);
6325
+ throw new MacroExpansionError(`CloudFormation returned a non-JSON Processed-stage template body. cdkd's macro-expansion path only supports JSON-shaped synth templates (CDK apps emit JSON by default). Cause: ${displayAwsMessage(err instanceof Error ? err.message : String(err))}`, err instanceof Error ? err : void 0);
6264
6326
  }
6265
6327
  else if (body && typeof body === "object" && !Array.isArray(body)) parsed = body;
6266
6328
  else throw new MacroExpansionError(`CloudFormation returned an unexpected TemplateBody shape (${typeof body}). Expected a JSON string or parsed object.`);
@@ -6270,7 +6332,7 @@ function parseTemplateBody(body) {
6270
6332
  return parsed;
6271
6333
  }
6272
6334
  function formatErr(err) {
6273
- return err instanceof Error ? err.message : String(err);
6335
+ return displayAwsMessage(err instanceof Error ? err.message : String(err));
6274
6336
  }
6275
6337
 
6276
6338
  //#endregion
@@ -6644,6 +6706,26 @@ async function probeBucket(client, bucketName, deps) {
6644
6706
  //#endregion
6645
6707
  //#region src/synthesis/synthesizer.ts
6646
6708
  /**
6709
+ * Every manifest- or template-derived value this module RENDERS goes through
6710
+ * `displaySafe` — thrown messages and ordinary `logger.info` lines alike
6711
+ * ([#3479](https://github.com/go-to-k/cdkd/issues/3479)). Two reasons it is the
6712
+ * DEFAULT here rather than a judgement per message:
6713
+ *
6714
+ * - The "only where the input is untrusted" boundary keeps being drawn wrong. The
6715
+ * macro-expansion line below is a DEFAULT-verbosity `logger.info` on a normal
6716
+ * run, and a `stackName` carrying `ESC [ 2 K` plus a carriage return erases
6717
+ * cdkd's own line while a trailing line terminator forges another. Nothing
6718
+ * about the message being "operational" protects it, and `formatError`
6719
+ * sanitizes only an error's `cause`, never its own `message`.
6720
+ * - The cost is in the helper, not the scope: `displaySafe` neither quotes nor
6721
+ * truncates, so it is the identity on every legitimate stack name, context key
6722
+ * and transform name.
6723
+ *
6724
+ * A JOINED list sanitizes per ELEMENT so the separator stays
6725
+ * byte-exact; `displaySafe`'s own doc carries why that is a formatting rule
6726
+ * rather than a safety one.
6727
+ */
6728
+ /**
6647
6729
  * CDK CLI compatibility: a `--app` value pointing at an existing directory is
6648
6730
  * treated as a pre-synthesized cloud assembly — synthesis (the subprocess
6649
6731
  * execution) is skipped and the manifest is read directly. A `--app` value that
@@ -6771,7 +6853,7 @@ var Synthesizer = class {
6771
6853
  }
6772
6854
  const missingKeys = new Set(manifest.missing.map((m) => m.key));
6773
6855
  this.logger.debug(`Missing context: ${manifest.missing.length} value(s)`);
6774
- if (previousMissingKeys && setsEqual(missingKeys, previousMissingKeys)) throw new SynthesisError(`Context resolution made no progress. Missing context keys: ${[...missingKeys].join(", ")}. Ensure cdk.context.json is correctly configured or required AWS permissions are granted.`);
6856
+ if (previousMissingKeys && setsEqual(missingKeys, previousMissingKeys)) throw new SynthesisError(`Context resolution made no progress. Missing context keys: ${[...missingKeys].map((k) => displaySafe(k)).join(", ")}. Ensure cdk.context.json is correctly configured or required AWS permissions are granted.`);
6775
6857
  previousMissingKeys = missingKeys;
6776
6858
  this.logger.info("Resolving missing context...");
6777
6859
  const resolved = await contextProviderRegistry.resolve(manifest.missing);
@@ -6816,7 +6898,7 @@ var Synthesizer = class {
6816
6898
  const stacksWithMacros = stacks.filter((s) => containsMacro(s.template));
6817
6899
  if (stacksWithMacros.length === 0) return;
6818
6900
  const region = resolved?.region || options.region || process.env["AWS_REGION"] || process.env["AWS_DEFAULT_REGION"] || stacksWithMacros[0]?.region || await resolveSdkDefaultRegion(options.profile);
6819
- if (!region) throw new SynthesisError(`Stack(s) [${stacksWithMacros.map((s) => s.stackName).join(", ")}] use CloudFormation macros (Transform / Fn::Transform) but cdkd could not resolve an AWS region for the expansion round-trip. Set AWS_REGION, pass --region <r>, or set env: { region: '<r>' } in your CDK Stack constructor.`);
6901
+ if (!region) throw new SynthesisError(`Stack(s) [${stacksWithMacros.map((s) => displaySafe(s.stackName)).join(", ")}] use CloudFormation macros (Transform / Fn::Transform) but cdkd could not resolve an AWS region for the expansion round-trip. Set AWS_REGION, pass --region <r>, or set env: { region: '<r>' } in your CDK Stack constructor.`);
6820
6902
  let accountId = resolved?.accountId;
6821
6903
  if (resolved === void 0 && !options.stateBucket) try {
6822
6904
  const stsClient = new STSClient({
@@ -6833,12 +6915,12 @@ var Synthesizer = class {
6833
6915
  else if (accountId) stateBucket = `cdkd-state-${accountId}`;
6834
6916
  else {
6835
6917
  const oversize = stacksWithMacros.find((s) => JSON.stringify(s.template).length > 51200);
6836
- if (oversize) throw new SynthesisError(`Stack '${oversize.stackName}' uses CloudFormation macros AND its serialized template exceeds the 51,200-byte inline TemplateBody limit, so cdkd must upload the template to S3 for the transient expansion changeset. cdkd could not resolve a state bucket: STS GetCallerIdentity failed AND --state-bucket was not provided. Pass --state-bucket <name> (cdkd uses the same bucket as cdkd deploy state storage; typically 'cdkd-state-<accountId>').`);
6918
+ if (oversize) throw new SynthesisError(`Stack '${displaySafe(oversize.stackName)}' uses CloudFormation macros AND its serialized template exceeds the 51,200-byte inline TemplateBody limit, so cdkd must upload the template to S3 for the transient expansion changeset. cdkd could not resolve a state bucket: STS GetCallerIdentity failed AND --state-bucket was not provided. Pass --state-bucket <name> (cdkd uses the same bucket as cdkd deploy state storage; typically 'cdkd-state-<accountId>').`);
6837
6919
  stateBucket = void 0;
6838
6920
  }
6839
6921
  for (const stack of stacksWithMacros) {
6840
6922
  const macros = enumerateMacros(stack.template);
6841
- this.logger.info(`[macros] Expanding CloudFormation macros for stack '${stack.stackName}' via CFn round-trip (transforms: ${macros.join(", ")}; may take 30-60s)...`);
6923
+ this.logger.info(`[macros] Expanding CloudFormation macros for stack '${displaySafe(stack.stackName)}' via CFn round-trip (transforms: ${macros.map((m) => displaySafe(m)).join(", ")}; may take 30-60s)...`);
6842
6924
  const before = Date.now();
6843
6925
  const expanded = await expandMacros(stack.template, {
6844
6926
  region,
@@ -22060,7 +22142,9 @@ function repairMalformedResourcesForReadOnly(state) {
22060
22142
  * documents `2` as "PARTIAL — journal kept, idempotent re-run", so a `2` here
22061
22143
  * would tell an operator to re-run a command that attempted nothing.
22062
22144
  */
22063
- function malformedStateRefusalMessage(stackName, region) {
22145
+ function malformedStateRefusalMessage(rawStackName, rawRegion) {
22146
+ const stackName = absentIfEmpty(rawStackName);
22147
+ const region = absentIfEmpty(rawRegion);
22064
22148
  return `${malformedStateDiagnosis(stackName, region)} This command can WRITE state, so it refuses rather than continuing: saving over a record whose resource map could not be read would replace the evidence with a well-formed empty one and lose it permanently. Repair or remove the record first. Inspect it with: ${inspectCommand(stackName, region)}`;
22065
22149
  }
22066
22150
  /**
@@ -22370,7 +22454,9 @@ function refuseDivergentRecordRegionForDestroy(state, stackName, keyRegion, dive
22370
22454
  * Identifiers are sanitized and THEN shell-quoted and the command is emitted
22371
22455
  * LAST, for the reasons {@link safeIdentifier}'s note gives.
22372
22456
  */
22373
- function malformedDeployResourcesRefusalMessage(stackName, region) {
22457
+ function malformedDeployResourcesRefusalMessage(rawStackName, rawRegion) {
22458
+ const stackName = absentIfEmpty(rawStackName);
22459
+ const region = absentIfEmpty(rawRegion);
22374
22460
  return `${malformedStateDiagnosis(stackName, region)} 'cdkd deploy' can WRITE state and AWS resources, so it refuses rather than continuing — under '--dry-run' too, because the plan a dry run would print is the wrong one: an unreadable map reads as ZERO recorded resources, so every resource the template declares is planned as a CREATE and the deploy re-provisions a stack that already exists, colliding on each deterministic name and duplicating the rest, then saves a well-formed record over the only evidence anything was wrong. Reading the bag as EMPTY produces that same plan rather than avoiding it. Nothing was provisioned and no state was written FOR THIS STACK. Repair or remove the record first; 'cdkd diff' previews the stack with this map read as EMPTY and warns that it did. Inspect it with: ${inspectCommand(stackName, region)}`;
22375
22461
  }
22376
22462
  /**
@@ -22454,14 +22540,61 @@ function refuseMalformedResourcesForDeploy(state, stackName, region) {
22454
22540
  * would mean JSON-quoting a name in the one place the text is meant to read as
22455
22541
  * English (review of go-to-k/cdkd#3190).
22456
22542
  */
22457
- function malformedRenderedContainersWarning(stackName, region, containers) {
22458
- const stack = safeStackName(stackName);
22459
- const reg = safeRegion(region);
22543
+ function malformedRenderedContainersWarning(rawStackName, rawRegion, containers) {
22544
+ const stackName = absentIfEmpty(rawStackName);
22545
+ const region = absentIfEmpty(rawRegion);
22460
22546
  const names = containers.map((name) => `'${safeIdentifier(name, SHORT_NAME_MAX_CODE_POINTS)}'`).join(", ");
22461
- return `State for ${shellQuote(stack)} (${shellQuote(reg)}) has a non-object ${names} — the record is malformed or truncated. 'Object.entries' walks a string or a list as readily as a map, so rendering one INVENTS a row per character or element. Continuing with it EMPTY: this view shows no rows there, which is not the same as the record holding none. A per-resource container is named once however many resources hold one. See the stored values with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
22547
+ return `${stackClause(stackName, region)} has a non-object ${names} — the record is malformed or truncated. 'Object.entries' walks a string or a list as readily as a map, so rendering one INVENTS a row per character or element. Continuing with it EMPTY: this view shows no rows there, which is not the same as the record holding none. A per-resource container is named once however many resources hold one. See the stored values with: ` + inspectCommand(stackName, region);
22548
+ }
22549
+ /**
22550
+ * `''` is NOT an identity, and every renderer below treats it as one
22551
+ * (go-to-k/cdkd#3520). `safeStackName('')` and `safeRegion('')` are both
22552
+ * `UNRENDERABLE`, so an empty region rendered `('<unrenderable>')` in the
22553
+ * clause AND `--stack-region '<unrenderable>'` in the command the reader
22554
+ * pastes — a flag that selects no record at all, on a message whose whole job
22555
+ * is to send them to the damaged one. `undefined` already means "absent" to
22556
+ * {@link stackClause} and {@link inspectCommand}, which drop the clause and
22557
+ * the flag, and that is the right answer for both identifiers.
22558
+ *
22559
+ * Every message that renders an identity calls this and then
22560
+ * {@link stackClause} / {@link inspectCommand} rather than spelling the clause
22561
+ * and the command again (go-to-k/cdkd#3526): for a present identity those two
22562
+ * render byte-identically to what each builder used to build by hand, and for
22563
+ * an absent one they give the no-identity form and the hole template instead
22564
+ * of `<unrenderable>`. That is why the per-site note is one line — the reason
22565
+ * lives here.
22566
+ *
22567
+ * NORMALISE AT THE BOUNDARY. `dropRecordCommand`, `identityWithheld` and
22568
+ * `orphanInspectClause` each carry their own `=== ''` arm on purpose, so this
22569
+ * is not the module's only floor — but a floor in the SHARED helpers was tried
22570
+ * here and removed, and the reason is NOT that nothing reaches them: three
22571
+ * unboundaried builders do. It is that each passes `exact ? x : undefined`,
22572
+ * and `''` is never exact — `safeIdentifier('')` is the placeholder, so
22573
+ * `safeStackName('') !== ''` — so those helpers already receive `undefined`
22574
+ * and a floor there reddened no case. The boundary is what covers every
22575
+ * helper a builder reaches at once, which is the property
22576
+ * {@link malformedOrphanResourcePropertiesRefusalMessage} measured when
22577
+ * guarding inside ONE helper fixed only part of its message.
22578
+ *
22579
+ * Not reachable from today's callers: `cdkd state`'s sites sit below an
22580
+ * `if (!ref.region) throw`, which is falsy and so rejects `''` too, and
22581
+ * `cdkd export` normalises at its own call. What was missing is the CONTRACT —
22582
+ * a caller that hands `''` got the placeholder silently, and the guard that
22583
+ * would have caught it lived in another file.
22584
+ *
22585
+ * One cost, stated rather than discovered later: an empty STACK name with a
22586
+ * real region now drops that region from the clause and the command too, since
22587
+ * {@link inspectCommand} answers a missing stack with the two-hole template.
22588
+ * That is the existing `undefined` behaviour rather than a new rule — a record
22589
+ * cdkd cannot name is one it cannot build a selecting command for either.
22590
+ */
22591
+ function absentIfEmpty(value) {
22592
+ return value === "" ? void 0 : value;
22462
22593
  }
22463
22594
  /** The warning a caller of {@link repairMalformedResourcesForReadOnly} emits. */
22464
- function malformedResourcesWarning(stackName, region) {
22595
+ function malformedResourcesWarning(rawStackName, rawRegion) {
22596
+ const stackName = absentIfEmpty(rawStackName);
22597
+ const region = absentIfEmpty(rawRegion);
22465
22598
  return `${malformedStateDiagnosis(stackName, region)} Continuing with an EMPTY resource set: this command's output describes zero resources, which is not the same as the stack having none. Inspect it with: ${inspectCommand(stackName, region)}`;
22466
22599
  }
22467
22600
  /**
@@ -22537,10 +22670,10 @@ function repairMalformedOutputsForReadOnly(state) {
22537
22670
  * Identifiers are sanitized and then shell-quoted and the command is emitted
22538
22671
  * LAST and UNWRAPPED, for the reasons {@link safeIdentifier}'s note gives.
22539
22672
  */
22540
- function malformedOutputsWarning(stackName, region) {
22541
- const stack = safeStackName(stackName);
22542
- const reg = safeRegion(region);
22543
- return `State for ${shellQuote(stack)} (${shellQuote(reg)}) has no readable 'outputs' map — the record is malformed or truncated. Where the stored value is a string or a list, 'Object.entries' walks it as readily as a map, so diffing it INVENTS a REMOVE row per character or element carrying the record's own characters; where it is a number, a boolean or null, it yields no comparison at all. Continuing with it EMPTY: every output this diff resolves is reported as an ADD and no stored key is reported as a REMOVE, which is not the same as the record holding none. See the stored value with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
22673
+ function malformedOutputsWarning(rawStackName, rawRegion) {
22674
+ const stackName = absentIfEmpty(rawStackName);
22675
+ const region = absentIfEmpty(rawRegion);
22676
+ return `${stackClause(stackName, region)} has no readable 'outputs' map — the record is malformed or truncated. Where the stored value is a string or a list, 'Object.entries' walks it as readily as a map, so diffing it INVENTS a REMOVE row per character or element carrying the record's own characters; where it is a number, a boolean or null, it yields no comparison at all. Continuing with it EMPTY: every output this diff resolves is reported as an ADD and no stored key is reported as a REMOVE, which is not the same as the record holding none. See the stored value with: ` + inspectCommand(stackName, region);
22544
22677
  }
22545
22678
  /**
22546
22679
  * For a command that can WRITE state: refuse the record by name instead of
@@ -22584,10 +22717,10 @@ function refuseMalformedState(state, stackName, region) {
22584
22717
  * Identifiers are sanitized and THEN shell-quoted and the command is emitted
22585
22718
  * LAST and UNWRAPPED, for the reasons {@link safeIdentifier}'s note gives.
22586
22719
  */
22587
- function malformedOutputsRefusalMessage(stackName, region) {
22588
- const stack = safeStackName(stackName);
22589
- const reg = safeRegion(region);
22590
- return `State for ${shellQuote(stack)} (${shellQuote(reg)}) has no readable 'outputs' map — the record is malformed or truncated. This command can WRITE state, so it refuses rather than continuing: it REBUILDS the bag before saving, and 'Object.entries' walks a string or a list as readily as a map, so a six-character value would be saved back as a well-formed six-key map (a null one as an empty map). That replaces the only signal anything is wrong with a legitimate-looking record, permanently — and the next deploy republishes it into the shared exports index every other stack's Fn::ImportValue resolves against. Repair or remove the record first. Inspect it with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
22720
+ function malformedOutputsRefusalMessage(rawStackName, rawRegion) {
22721
+ const stackName = absentIfEmpty(rawStackName);
22722
+ const region = absentIfEmpty(rawRegion);
22723
+ return `${stackClause(stackName, region)} has no readable 'outputs' map — the record is malformed or truncated. This command can WRITE state, so it refuses rather than continuing: it REBUILDS the bag before saving, and 'Object.entries' walks a string or a list as readily as a map, so a six-character value would be saved back as a well-formed six-key map (a null one as an empty map). That replaces the only signal anything is wrong with a legitimate-looking record, permanently — and the next deploy republishes it into the shared exports index every other stack's Fn::ImportValue resolves against. Repair or remove the record first. Inspect it with: ` + inspectCommand(stackName, region);
22591
22724
  }
22592
22725
  /**
22593
22726
  * The line `ExportIndexStore`'s rebuild emits for a producer record whose
@@ -22610,10 +22743,10 @@ function malformedOutputsRefusalMessage(stackName, region) {
22610
22743
  * Identifiers are sanitized and THEN shell-quoted and the command is emitted
22611
22744
  * LAST and UNWRAPPED, for the reasons {@link safeIdentifier}'s note gives.
22612
22745
  */
22613
- function malformedExportSourceWarning(stackName, region) {
22614
- const stack = safeStackName(stackName);
22615
- const reg = safeRegion(region);
22616
- return `State for ${shellQuote(stack)} (${shellQuote(reg)}) has no readable 'outputs' map or 'exportNames' list — the record is malformed or truncated. It contributes NO exports to this region's index, which is not the same as the stack exporting none: an Fn::ImportValue of a name this stack really publishes will fail in the CONSUMER stack, naming that stack rather than this record. Continuing with the other producers — enumerating a string or a list here would instead publish one FABRICATED export per character or element. See the stored values with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
22746
+ function malformedExportSourceWarning(rawStackName, rawRegion) {
22747
+ const stackName = absentIfEmpty(rawStackName);
22748
+ const region = absentIfEmpty(rawRegion);
22749
+ return `${stackClause(stackName, region)} has no readable 'outputs' map or 'exportNames' list — the record is malformed or truncated. It contributes NO exports to this region's index, which is not the same as the stack exporting none: an Fn::ImportValue of a name this stack really publishes will fail in the CONSUMER stack, naming that stack rather than this record. Continuing with the other producers — enumerating a string or a list here would instead publish one FABRICATED export per character or element. See the stored values with: ` + inspectCommand(stackName, region);
22617
22750
  }
22618
22751
  /**
22619
22752
  * The line a READ-ONLY command emits when a record's `exportNames` FIELD could
@@ -22639,10 +22772,10 @@ function malformedExportSourceWarning(stackName, region) {
22639
22772
  * Identifiers are sanitized and THEN shell-quoted and the command is emitted
22640
22773
  * LAST and UNWRAPPED, for the reasons {@link safeIdentifier}'s note gives.
22641
22774
  */
22642
- function malformedExportNamesWarning(stackName, region) {
22643
- const stack = safeStackName(stackName);
22644
- const reg = safeRegion(region);
22645
- return `State for ${shellQuote(stack)} (${shellQuote(reg)}) has an unusable 'exportNames' list — the record is malformed or truncated. It is read as an EMPTY export set, which is not the same as the record holding one: no stored key is reported as an export, so a row that should carry an '[export]' tag renders without it. Reading it as UNKNOWN instead would be worse — that falls back to the pre-v9 rule where every output name is importable. See the stored value with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
22775
+ function malformedExportNamesWarning(rawStackName, rawRegion) {
22776
+ const stackName = absentIfEmpty(rawStackName);
22777
+ const region = absentIfEmpty(rawRegion);
22778
+ return `${stackClause(stackName, region)} has an unusable 'exportNames' list — the record is malformed or truncated. It is read as an EMPTY export set, which is not the same as the record holding one: no stored key is reported as an export, so a row that should carry an '[export]' tag renders without it. Reading it as UNKNOWN instead would be worse — that falls back to the pre-v9 rule where every output name is importable. See the stored value with: ` + inspectCommand(stackName, region);
22646
22779
  }
22647
22780
  /**
22648
22781
  * For a command that can WRITE state: refuse a record whose `outputs` bag
@@ -22748,10 +22881,10 @@ function repairMalformedOrphansForReadOnly(state) {
22748
22881
  * Identifiers are sanitized and THEN shell-quoted and the command is emitted
22749
22882
  * LAST and UNWRAPPED, for the reasons {@link safeIdentifier}'s note gives.
22750
22883
  */
22751
- function malformedOrphansWarning(stackName, region) {
22752
- const stack = safeStackName(stackName);
22753
- const reg = safeRegion(region);
22754
- return `State for ${shellQuote(stack)} (${shellQuote(reg)}) has no readable 'orphans' list — the record is malformed or truncated. Readers reach it on a bare '?? []' or '?.length', which admits a string, a number, a plain object and null alike: a string is WALKED, one garbage orphan per character, and the others read as no orphans at all. Continuing with it EMPTY: this view previews no adoption and names no orphan, which is NOT the same as the record holding none — resources from an earlier failed deploy may still be live in AWS. See the stored value with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
22884
+ function malformedOrphansWarning(rawStackName, rawRegion) {
22885
+ const stackName = absentIfEmpty(rawStackName);
22886
+ const region = absentIfEmpty(rawRegion);
22887
+ return `${stackClause(stackName, region)} has no readable 'orphans' list — the record is malformed or truncated. Readers reach it on a bare '?? []' or '?.length', which admits a string, a number, a plain object and null alike: a string is WALKED, one garbage orphan per character, and the others read as no orphans at all. Continuing with it EMPTY: this view previews no adoption and names no orphan, which is NOT the same as the record holding none — resources from an earlier failed deploy may still be live in AWS. See the stored value with: ` + inspectCommand(stackName, region);
22755
22888
  }
22756
22889
  /**
22757
22890
  * The refusal text for a command that can WRITE the record.
@@ -22768,10 +22901,10 @@ function malformedOrphansWarning(stackName, region) {
22768
22901
  * Identifiers are sanitized and THEN shell-quoted and the command is emitted
22769
22902
  * LAST and UNWRAPPED, for the reasons {@link safeIdentifier}'s note gives.
22770
22903
  */
22771
- function malformedOrphansRefusalMessage(stackName, region) {
22772
- const stack = safeStackName(stackName);
22773
- const reg = safeRegion(region);
22774
- return `State for ${shellQuote(stack)} (${shellQuote(reg)}) has no readable 'orphans' list — the record is malformed or truncated. This command can WRITE state, so it refuses rather than continuing: a string container is WALKED one character at a time and written back as a list of character-shaped orphan records, and every other unreadable shape reads as no orphans at all — so a run would delete or adopt against a record whose evidence of resources left live in AWS by an earlier failed deploy it never read. Repair or remove the record first, and no cdkd command repairs this container: rewriting it to [] by hand discards the very evidence this refusal is protecting. Inspect the record with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
22904
+ function malformedOrphansRefusalMessage(rawStackName, rawRegion) {
22905
+ const stackName = absentIfEmpty(rawStackName);
22906
+ const region = absentIfEmpty(rawRegion);
22907
+ return `${stackClause(stackName, region)} has no readable 'orphans' list — the record is malformed or truncated. This command can WRITE state, so it refuses rather than continuing: a string container is WALKED one character at a time and written back as a list of character-shaped orphan records, and every other unreadable shape reads as no orphans at all — so a run would delete or adopt against a record whose evidence of resources left live in AWS by an earlier failed deploy it never read. Repair or remove the record first, and no cdkd command repairs this container: rewriting it to [] by hand discards the very evidence this refusal is protecting. Inspect the record with: ` + inspectCommand(stackName, region);
22775
22908
  }
22776
22909
  /**
22777
22910
  * The DESTROY refusal text for this container (go-to-k/cdkd#3379 review).
@@ -22854,10 +22987,10 @@ function refuseMalformedOrphans(state, stackName, region) {
22854
22987
  * Identifiers are sanitized and THEN shell-quoted and the command is emitted
22855
22988
  * LAST and UNWRAPPED, for the reasons {@link safeIdentifier}'s note gives.
22856
22989
  */
22857
- function malformedDestroyOutputsRefusalMessage(stackName, region) {
22858
- const stack = safeStackName(stackName);
22859
- const reg = safeRegion(region);
22860
- return `State for ${shellQuote(stack)} (${shellQuote(reg)}) has no readable 'outputs' map — the record is malformed or truncated. This command DELETES state, so it refuses rather than continuing: it reads this bag to decide whether the stack might export anything, and that decision gates the cross-stack check that refuses to delete a producer another stack still imports from. A string or a list invents one export name per character or element; a null, a number or a boolean reads as 'exports nothing' and SKIPS the check entirely, deleting the record while consumers still resolve against it. Repair or remove the record first. Inspect it with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
22990
+ function malformedDestroyOutputsRefusalMessage(rawStackName, rawRegion) {
22991
+ const stackName = absentIfEmpty(rawStackName);
22992
+ const region = absentIfEmpty(rawRegion);
22993
+ return `${stackClause(stackName, region)} has no readable 'outputs' map — the record is malformed or truncated. This command DELETES state, so it refuses rather than continuing: it reads this bag to decide whether the stack might export anything, and that decision gates the cross-stack check that refuses to delete a producer another stack still imports from. A string or a list invents one export name per character or element; a null, a number or a boolean reads as 'exports nothing' and SKIPS the check entirely, deleting the record while consumers still resolve against it. Repair or remove the record first. Inspect it with: ` + inspectCommand(stackName, region);
22861
22994
  }
22862
22995
  /**
22863
22996
  * For `cdkd destroy` / `cdkd state destroy`: refuse a record whose `outputs`
@@ -22896,10 +23029,10 @@ function refuseMalformedOutputsForDestroy(state, stackName, region) {
22896
23029
  * Identifiers are sanitized and THEN shell-quoted and the command is emitted
22897
23030
  * LAST and UNWRAPPED, for the reasons {@link safeIdentifier}'s note gives.
22898
23031
  */
22899
- function malformedNestedChildOutputsRefusalMessage(childStackName, region) {
22900
- const stack = safeStackName(childStackName);
22901
- const reg = safeRegion(region);
22902
- return `State for nested stack child ${shellQuote(stack)} (${shellQuote(reg)}) has no readable 'outputs' map — the record is malformed or truncated. The parent's 'Outputs.<Key>' attributes are REBUILT from this bag and persisted into the PARENT's record, and 'Object.entries' walks a string or a list as readily as a map — so a six-character value would become six fabricated parent attributes that every Fn::GetAtt against this nested stack then resolves into live AWS calls. The deploy refuses rather than fabricating them. Repair or remove the child's record first. Inspect it with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
23032
+ function malformedNestedChildOutputsRefusalMessage(rawChildStackName, rawRegion) {
23033
+ const childStackName = absentIfEmpty(rawChildStackName);
23034
+ const region = absentIfEmpty(rawRegion);
23035
+ return `${stackClause(childStackName, region, "nested stack child")} has no readable 'outputs' map — the record is malformed or truncated. The parent's 'Outputs.<Key>' attributes are REBUILT from this bag and persisted into the PARENT's record, and 'Object.entries' walks a string or a list as readily as a map — so a six-character value would become six fabricated parent attributes that every Fn::GetAtt against this nested stack then resolves into live AWS calls. The deploy refuses rather than fabricating them. Repair or remove the child's record first. Inspect it with: ` + inspectCommand(childStackName, region);
22903
23036
  }
22904
23037
  /**
22905
23038
  * For the nested-stack provider: refuse a CHILD record whose `outputs` bag
@@ -22947,10 +23080,10 @@ function refuseMalformedNestedChildOutputs(state, childStackName, region) {
22947
23080
  * Identifiers are sanitized and THEN shell-quoted and the command is emitted
22948
23081
  * LAST and UNWRAPPED, for the reasons {@link safeIdentifier}'s note gives.
22949
23082
  */
22950
- function malformedLocalOutputsWarning(stackName, region) {
22951
- const stack = safeStackName(stackName);
22952
- const reg = safeRegion(region);
22953
- return `State for ${shellQuote(stack)} (${shellQuote(reg)}) has no readable 'outputs' map — the record is malformed or truncated. 'Object.entries' walks a string or a list as readily as a map, so reading it would hand this local run one FABRICATED output per character or element. Continuing with it EMPTY: every reference to an output of this record resolves to nothing and is dropped, which is not the same as the record holding none. See the stored value with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
23083
+ function malformedLocalOutputsWarning(rawStackName, rawRegion) {
23084
+ const stackName = absentIfEmpty(rawStackName);
23085
+ const region = absentIfEmpty(rawRegion);
23086
+ return `${stackClause(stackName, region)} has no readable 'outputs' map — the record is malformed or truncated. 'Object.entries' walks a string or a list as readily as a map, so reading it would hand this local run one FABRICATED output per character or element. Continuing with it EMPTY: every reference to an output of this record resolves to nothing and is dropped, which is not the same as the record holding none. See the stored value with: ` + inspectCommand(stackName, region);
22954
23087
  }
22955
23088
  /** How many logical ids a `properties` message names before it says "and N more". */
22956
23089
  const NAMED_UNREADABLE_PROPERTY_BAGS = 5;
@@ -22982,10 +23115,10 @@ const NAMED_UNREADABLE_PROPERTY_BAGS = 5;
22982
23115
  * a pasted command that selects no record at all, so it is dropped for the
22983
23116
  * same reason — the call go-to-k/cdkd#3226 makes for `inspectCommand`.
22984
23117
  */
22985
- function stackClause(stackName, region) {
23118
+ function stackClause(stackName, region, kind = "") {
22986
23119
  if (stackName === void 0) return "The state record this command loaded";
22987
23120
  const where = region === void 0 ? "" : ` (${shellQuote(safeRegion(region))})`;
22988
- return `State for ${shellQuote(safeStackName(stackName))}${where}`;
23121
+ return `State for ${kind ? `${kind} ` : ""}${shellQuote(safeStackName(stackName))}${where}`;
22989
23122
  }
22990
23123
  /**
22991
23124
  * The DESTRUCTIVE remedy, built the way {@link inspectCommand} builds the
@@ -23310,7 +23443,9 @@ function namedPropertyBagsClause(stackName, region, logicalIds) {
23310
23443
  * by a refusal the moment the flag comes off. So the message points at
23311
23444
  * `cdkd diff` instead of weakening the guard.
23312
23445
  */
23313
- function malformedResourcePropertiesRefusalMessage(stackName, region, logicalIds) {
23446
+ function malformedResourcePropertiesRefusalMessage(rawStackName, rawRegion, logicalIds) {
23447
+ const stackName = absentIfEmpty(rawStackName);
23448
+ const region = absentIfEmpty(rawRegion);
23314
23449
  return `${namedPropertyBagsClause(stackName, region, logicalIds)} 'cdkd deploy' can WRITE state and AWS resources, so it refuses rather than continuing — under '--dry-run' too, because the plan a dry run would print is the wrong one: a DELETE and re-create of resources the template did not change, and reading the bag as empty produces that same verdict rather than avoiding it. Nothing was provisioned and no state was written FOR THIS STACK. Repair or remove the record first; 'cdkd diff' previews the rest of the stack with those maps read as EMPTY and warns that it did. Inspect the record with: ${inspectCommand(stackName, region)}`;
23315
23450
  }
23316
23451
  /**
@@ -23401,8 +23536,8 @@ function malformedResourcePropertiesRefusalMessage(stackName, region, logicalIds
23401
23536
  * validated either — go-to-k/cdkd#3360). See {@link stackClause}.
23402
23537
  */
23403
23538
  function malformedOrphanResourcePropertiesRefusalMessage(rawStackName, rawRegion, logicalIds, recovery) {
23404
- const stackName = rawStackName === "" ? void 0 : rawStackName;
23405
- const region = rawRegion === "" ? void 0 : rawRegion;
23539
+ const stackName = absentIfEmpty(rawStackName);
23540
+ const region = absentIfEmpty(rawRegion);
23406
23541
  const inspect = orphanInspectClause(stackName, region, recovery);
23407
23542
  const listCommand = withheldIdentityListCommand(stackName, region, recovery);
23408
23543
  const commands = [
@@ -23601,7 +23736,9 @@ function refuseMalformedResourcePropertiesForOrphan(state, removedLogicalIds, st
23601
23736
  * The warning a caller of {@link repairMalformedResourcePropertiesForReadOnly}
23602
23737
  * emits.
23603
23738
  */
23604
- function malformedResourcePropertiesWarning(stackName, region, logicalIds) {
23739
+ function malformedResourcePropertiesWarning(rawStackName, rawRegion, logicalIds) {
23740
+ const stackName = absentIfEmpty(rawStackName);
23741
+ const region = absentIfEmpty(rawRegion);
23605
23742
  return `${namedPropertyBagsClause(stackName, region, logicalIds)} Continuing with those maps EMPTY: what these records are stored as holding is NOT what this preview compares against. Where the template still declares the resource, every property it declares previews as an addition and a create-only one as a replacement; where it no longer declares it, the DELETE row shows an empty previous side instead of the stored one. Do NOT run 'cdkd deploy' against this record — it REFUSES on the same defect rather than acting on this preview. See the stored values with: ${inspectCommand(stackName, region)}`;
23606
23743
  }
23607
23744
  /**
@@ -23733,10 +23870,10 @@ function repairMalformedResourceEntriesForReadOnly(state) {
23733
23870
  return unreadable;
23734
23871
  }
23735
23872
  /** The warning a caller of {@link repairMalformedResourceEntriesForReadOnly} emits. */
23736
- function malformedResourceEntriesWarning(stackName, region, logicalIds) {
23737
- const stack = safeStackName(stackName);
23738
- const reg = safeRegion(region);
23739
- return `${namedEntriesClause(stackName, region, logicalIds)} Continuing WITHOUT them: this command's output describes the remaining resources only, which is not the same as the stack holding none of these. See the stored values with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
23873
+ function malformedResourceEntriesWarning(rawStackName, rawRegion, logicalIds) {
23874
+ const stackName = absentIfEmpty(rawStackName);
23875
+ const region = absentIfEmpty(rawRegion);
23876
+ return `${namedEntriesClause(stackName, region, logicalIds)} Continuing WITHOUT them: this command's output describes the remaining resources only, which is not the same as the stack holding none of these. See the stored values with: ` + inspectCommand(stackName, region);
23740
23877
  }
23741
23878
  /**
23742
23879
  * The `orphans[]` twin of {@link malformedResourceEntriesWarning}, and a
@@ -23766,13 +23903,13 @@ function malformedResourceEntriesWarning(stackName, region, logicalIds) {
23766
23903
  * `logicalId` is not a string arrives as `''` and renders as the
23767
23904
  * `UNRENDERABLE` stand-in.
23768
23905
  */
23769
- function malformedOrphanRecordsWarning(stackName, region, logicalIds) {
23770
- const stack = safeStackName(stackName);
23771
- const reg = safeRegion(region);
23906
+ function malformedOrphanRecordsWarning(rawStackName, rawRegion, logicalIds) {
23907
+ const stackName = absentIfEmpty(rawStackName);
23908
+ const region = absentIfEmpty(rawRegion);
23772
23909
  const named = logicalIds.slice(0, NAMED_UNREADABLE_ENTRIES).map((id) => displayLogicalId(id)).join(", ");
23773
23910
  const rest = logicalIds.length - NAMED_UNREADABLE_ENTRIES;
23774
23911
  const more = rest > 0 ? ` and ${rest} more` : "";
23775
- return `State for ${shellQuote(stack)} (${shellQuote(reg)}) holds ${logicalIds.length} rollback-orphan record(s) in 'orphans' that cannot be read as resources — ${named}${more} — because they are not objects, or carry no resource type. Continuing WITHOUT them: they are not previewed for adoption. These ids were read from 'orphans'; the 'resources' map carries its own warning when it is damaged too. See the stored values with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
23912
+ return `${stackClause(stackName, region)} holds ${logicalIds.length} rollback-orphan record(s) in 'orphans' that cannot be read as resources — ${named}${more} — because they are not objects, or carry no resource type. Continuing WITHOUT them: they are not previewed for adoption. These ids were read from 'orphans'; the 'resources' map carries its own warning when it is damaged too. See the stored values with: ` + inspectCommand(stackName, region);
23776
23913
  }
23777
23914
  /**
23778
23915
  * The half the refusal and the warning share: what is wrong and which entries.
@@ -23781,12 +23918,10 @@ function malformedOrphanRecordsWarning(stackName, region, logicalIds) {
23781
23918
  * happens NEXT, and a second copy of the diagnosis is what drifts.
23782
23919
  */
23783
23920
  function namedEntriesClause(stackName, region, logicalIds) {
23784
- const stack = safeStackName(stackName);
23785
- const reg = safeRegion(region);
23786
23921
  const named = logicalIds.slice(0, NAMED_UNREADABLE_ENTRIES).map((id) => displayLogicalId(id)).join(", ");
23787
23922
  const rest = logicalIds.length - NAMED_UNREADABLE_ENTRIES;
23788
23923
  const more = rest > 0 ? ` and ${rest} more` : "";
23789
- return `State for ${shellQuote(stack)} (${shellQuote(reg)}) holds ${logicalIds.length} resource record(s) that cannot be read as resources — ${named}${more} — because they are not objects, or carry no resource type, so nothing can tell what AWS resource they name. The record is malformed or truncated.`;
23924
+ return `${stackClause(stackName, region)} holds ${logicalIds.length} resource record(s) that cannot be read as resources — ${named}${more} — because they are not objects, or carry no resource type, so nothing can tell what AWS resource they name. The record is malformed or truncated.`;
23790
23925
  }
23791
23926
  /**
23792
23927
  * The text {@link refuseMalformedResourceEntries} raises.
@@ -23807,10 +23942,10 @@ function namedEntriesClause(stackName, region, logicalIds) {
23807
23942
  * Exported so a test can pin the wording against the producer rather than
23808
23943
  * re-spelling it, the way the other messages here are consumed.
23809
23944
  */
23810
- function malformedResourceEntriesRefusalMessage(stackName, region, logicalIds) {
23811
- const stack = safeStackName(stackName);
23812
- const reg = safeRegion(region);
23813
- return `${namedEntriesClause(stackName, region, logicalIds)} This command can WRITE state, so it refuses rather than skipping them: saving over the record would report a clean run for entries nothing could read, and would leave the next command to fail on them with no more to go on. Nothing was locked, read from AWS or written FOR THIS STACK. Inspect it with: cdkd state show ${shellQuote(stack)} --stack-region ${shellQuote(reg)} --json`;
23945
+ function malformedResourceEntriesRefusalMessage(rawStackName, rawRegion, logicalIds) {
23946
+ const stackName = absentIfEmpty(rawStackName);
23947
+ const region = absentIfEmpty(rawRegion);
23948
+ return `${namedEntriesClause(stackName, region, logicalIds)} This command can WRITE state, so it refuses rather than skipping them: saving over the record would report a clean run for entries nothing could read, and would leave the next command to fail on them with no more to go on. Nothing was locked, read from AWS or written FOR THIS STACK. Inspect it with: ` + inspectCommand(stackName, region);
23814
23949
  }
23815
23950
 
23816
23951
  //#endregion
@@ -34484,7 +34619,7 @@ var CloudControlProvider = class {
34484
34619
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
34485
34620
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
34486
34621
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
34487
- const { ASGProvider } = await import("./asg-provider-CVB0Ky06.js").then((n) => n.n);
34622
+ const { ASGProvider } = await import("./asg-provider-LZQSJyN2.js").then((n) => n.n);
34488
34623
  const asgProvider = new ASGProvider();
34489
34624
  return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
34490
34625
  }
@@ -48194,4 +48329,4 @@ var DeployEngine = class DeployEngine {
48194
48329
 
48195
48330
  //#endregion
48196
48331
  export { yellow as $, AssemblyReader as $i, safeStackName as $n, assertAssetBucketRegion as $r, requireConfigObject as $t, createPreDeleteFinalSnapshot as A, ResourceTimeoutError as Aa, resolveCaptureObservedState as Ai, malformedOutputsRefusalMessage as An, formatLockExpiry as Ar, carriesDynamicReference as At, isStatefulRecreateTargetSync as B, isMarkedNonRetryable as Ba, MIGRATE_TMP_PREFIX as Bi, refuseMalformedOrphansForDestroy as Bn, isReadableBag as Br, isSettledInstanceState as Bt, namesAnyDeclaredParameter as C, LocalInvokeBuildError as Ca, getDockerImageBySourceHash as Ci, isReadableResourceEntry as Cn, LockManager as Cr, slowCcOperationTimeoutMs as Ct, PRE_DELETE_SNAPSHOT_TYPES as D, NestedStackChildDirectDestroyError as Da, getLegacyStateBucketName as Di, malformedOrphanRecordsWarning as Dn, buildForceUnlockCommand as Dr, disableInstanceApiTermination as Dt, ATOMIC_FINAL_SNAPSHOT_TYPES as E, MalformedProducerRecordRefusalError as Ea, getDefaultStateBucketName as Ei, malformedLocalOutputsWarning as En, UNREPRODUCIBLE_LOCK_CLAUSE as Er, deleteSkipReason as Et, extractDeploymentEventError as F, SynthesisError as Fa, stateBucketExistenceConfirmed as Fi, malformedResourcesWarning as Fn, hasReadableExportSet as Fr, isImpossibleEmptyStoredAttribute as Ft, toCfnInteger as G, markRedactedCause as Ga, injectiveKeyPrefix as Gi, refuseMalformedResourcesForDestroy as Gn, stringifyValue as Gr, coerceCfnBoolean as Gt, WARM_THROUGHPUT_MEMBERS as H, isThrottlingError as Ha, uploadCfnTemplate as Hi, refuseMalformedOutputsForDestroy as Hn, orphansCarriedFrom as Hr, normalizeAwsTagsToCfn as Ht, makeSiblingClaimReader as I, formatError as Ia, warnDeprecatedNoPrefixCliFlag as Ii, malformedStateRefusalMessage as In, hasReasonlessBaselineRefusal as Ir, isNamelessDynamicReferenceError as It, bold as J, safeStringify$1 as Ja, decodeListingKey as Ji, repairMalformedOutputsForReadOnly as Jn, createAssetRedirectResolver as Jr, configIntegerRefusal as Jt, toFiniteNumber as K, retryClassificationText as Ka, producerCoordinateKey as Ki, refuseMalformedState as Kn, WorkGraph as Kr, coerceCfnInteger as Kt, planOrphanAdoption as L, isCdkdError as La, ssmResolvedValueType as Li, refuseDivergentRecordRegionForDestroy as Ln, hasUnverifiableParameterRefusal as Lr, isUnboundTemplateParameter as Lt, refusesFinalSnapshot as M, StackHasActiveImportsError as Ma, resolveStateBucketWithDefault as Mi, malformedRenderedContainersWarning as Mn, CUSTOM_RESOURCE_RESPONSE_PREFIX as Mr, cfnRefValueFromPhysicalId as Mt, unsupportedFinalSnapshotError as N, StackTerminationProtectionError as Na, resolveStateBucketWithDefaultAndSource as Ni, malformedResourceEntriesWarning as Nn, DEFAULT_STATE_PREFIX as Nr, coerceParameterTypedValue as Nt, buildFinalSnapshotIdentifier as O, PartialFailureError as Oa, resolveApp as Oi, malformedOrphansRefusalMessage as On, buildLockContentionMessage as Or, isTerminationProtectionPropagationError as Ot, makeCanonicalizePropertiesFn as P, StateError as Pa, resolveUseCdkBootstrapAssets as Pi, malformedResourcePropertiesWarning as Pn, exportNamesCarriedFrom as Pr, getAccountInfo as Pt, red as Q, derivePartitionAndUrlSuffix as Qi, safeRegion as Qn, BOOTSTRAP_MARKER_PREFIX as Qr, requireConfigArray as Qt, MULTI_REGION_RECREATE_BLOCKED_TYPES as R, normalizeAwsError as Ra, CFN_TEMPLATE_BODY_LIMIT as Ri, refuseMalformedNestedChildOutputs as Rn, importableOutputKeys as Rr, parameterTypeMayLoseSecretIdentity as Rt, computeParameterTaint as S, IntrinsicResolutionRefusalError as Sa, AssetManifestLoader as Si, hasReadableResources as Sn, scrubResourceRecord as Sr, isWaitAbandonedError as St, resourcesNamingDeclaredParameter as T, LockError as Ta, synthesisStatusMessage as Ti, malformedExportSourceWarning as Tn, rebuildClientForBucketRegion as Tr, deleteIndeterminateGuards as Tt, coerceWarmThroughput as U, isTransientServerError as Ua, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Ui, refuseMalformedResourceEntries as Un, shouldRetainResource as Ur, resolveExplicitPhysicalId as Ut, renderStatefulReason as V, isRetryableTransientError as Va, findLargeInlineResources as Vi, refuseMalformedOutputs as Vn, orphansAfterRollback as Vr, WAFv2WebACLProvider as Vt, isWarmThroughputDecrease as W, markNonRetryable as Wa, injectiveKey as Wi, refuseMalformedResourcePropertiesForOrphan as Wn, AssetPublisher as Wr, assertRegionMatch as Wt, gray as X, PARTITION_TABLE as Xi, repairMalformedResourcePropertiesForReadOnly as Xn, rewriteTemplateAssetReferences as Xr, readConfigString as Xt, cyan as Y, __exportAll as Ya, expectedOwnerParam as Yi, repairMalformedResourceEntriesForReadOnly as Yn, loadPublishableAssetManifest as Yr, configStringRefusal as Yt, green as Z, canonicalizeRegion as Zi, repairMalformedResourcesForReadOnly as Zn, AssetModeResolver as Zr, replayWarn as Zt, bagHoldsSecretExpression as _, ConfigError as _a, redactDockerArgvValues as _i, UNREADABLE_ORPHANS_CONTAINER_ROW as _n, maskSecretsInError as _r, endCommandInterruptScope as _t, DeploymentEventsStore as a, nullPrototypeRecord as aa, validateAssetBucketName as ai, s3BucketDomainName as an, TemplateParser as ar, secretBearingStateKeyWarning as at, mergeNoChangeOutputs as b, DeployCancelledError as ba, escapeRegExp$1 as bi, hasReadableOrphans as bn, recoverMaskedOutput as br, startInterruptWatch as bt, replayFailedOperations as c, resolveAssemblyPath as ca, buildDockerImage as ci, s3BucketWebsiteUrl as cn, STATE_SOURCED_READBACK_RULES as cr, IAMRoleProvider as ct, updatePartialMessage as d, AwsClients as da, describeDockerExecFailure as di, applyCallerIdentityCredentials as dn, createSecretMasker as dr, ProviderRegistry as dt, failedStageNote as ea, ensureAssetStorage as ei, requireConfigString as en, findActionableSilentDrops as er, collectDeclaredOutputNames as et, updatePartialReason as f, getAwsClients as fa, describeDockerFailure as fi, callerEnvCredentials as fn, dynamicReferenceTokens as fr, wouldReturnToSdkProvider as ft, computeImplicitDeleteEdges as g, CdkdError as ga, partitionSensitiveEnv as gi, STATE_RESOURCES_MALFORMED as gn, markSameGenerationBag as gr, beginCommandInterruptScope as gt, IMPLICIT_DELETE_DEPENDENCIES as h, AssetError as ha, getDockerCmd as hi, INTRINSIC_KEYS as hn, isSingleDynamicReferenceToken as hr, maskerOrIdentity as ht, DeploymentEventsReader as i, hasPlainPrototype$1 as ia, readBootstrapMarkerBody as ii, s3BucketArn as in, DagBuilder as ir, secretBearing as it, isFinalSnapshotError as j, ResourceUpdateNotSupportedError as ja, resolveSkipPrefix as ji, malformedOutputsWarning as jn, shellQuote as jr, carriesFetchableDynamicReference as jt, ccRoutedFinalSnapshotError as k, ProvisioningError as ka, resolveAutoAssetStorage as ki, malformedOrphansWarning as kn, forceQuitRecoveryClause as kr, IntrinsicFunctionResolver as kt, replayRollback as l, clearBucketRegionCache as la, resolveDockerContextDirectory as li, applyRoleArnIfSet as ln, TEMPLATE_SOURCED_RULES as lr, collectInlinePolicyNamesManagedBySiblings as lt, maskingRetryLogger as m, setAwsClients as ma, formatDockerLoginError as mi, DiffCalculator as mn, identityKeyFor as mr, maskDeep as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, defineOwnKey as na, isCrossRegionRedirect as ni, producerRegionsFromState as nn, describeTypeWithThrottleRetry as nr, exportAliasCollisionScrubWarning as nt, planFailedOps as o, ownValue as oa, validateContainerRepoName as oi, s3BucketDualStackDomainName as on, STATE_SOURCED_BASELINE_RULES as or, secretSafeKeyDisplay as ot, withResourceDeadline as p, resetAwsClients as pa, dockerSpawnEnvWithSensitive as pi, envCredentialsAreAssumedRole as pn, errorCauseChain as pr, createMaskedRetryLogger as pt, formatResourceLine as q, describeAwsFailure as qa, producerRecordKey as qi, repairMalformedOrphansForReadOnly as qn, buildAssetRedirectMap as qr, configBooleanRefusal as qt, DeployEngine as r, hasOwnKey as ra, parseBootstrapMarker as ri, withSharedDrainBudget as rn, withRetry as rr, isExportAliasCollision as rt, planRollback as s, renderAssemblyPathEscape as sa, buildDenyExternalAccessPolicy as si, s3BucketRegionalDomainName as sn, STATE_SOURCED_CROSS_GENERATION_RULES as sr, getCurrentResourceSecrets as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, processStackMessages as ta, getBootstrapMarkerKey as ti, classifyReplaySecretRegion as tn, findSilentDropProperties as tr, collectPublishedOutputNames as tt, resolveReplacementOldType as u, resolveBucketRegion as ua, describeDockerCapturedOutput as ui, AWS_CREDENTIAL_ENV_KEYS as un, carriesSecretMask as ur, clearOnUpdateRemoval as ut, isSecretBearingReferenceString as v, CrossAccountSecretRefusalError as va, runDockerForeground as vi, UNREADABLE_RESOURCES_MAP_ROW as vn, maskSecretsInText as vr, interruptWatchListenerCount as vt, reachableDivergentParameters as w, LocalStartServiceError as wa, Synthesizer as wi, malformedExportNamesWarning as wn, S3StateBackend as wr, UNSPECIFIED_SKIP_REASON as wt, bindingSkippedOutputs as x, DynamicReferenceRegionAmbiguousError as xa, stripControlChars as xi, hasReadableOutputs as xn, redactSecretsForState as xr, CloudControlProvider as xt, keptWholeReasonText as y, DependencyError as ya, runDockerStreaming as yi, displayLogicalId as yn, recordMaskOnlyValue as yr, isInterruptedWaitError as yt, isStatefulRecreateTargetForReplace as z, withErrorHandling as za, CFN_TEMPLATE_URL_LIMIT as zi, refuseMalformedOrphans as zn, importableOutputs as zr, refStateLookupFromResource as zt };
48197
- //# sourceMappingURL=deploy-engine-DhZW4if5.js.map
48332
+ //# sourceMappingURL=deploy-engine-DzSrzD0b.js.map