@go-to-k/cdkd 0.289.5 → 0.289.7

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.
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as isVersionOnlyInvocation, t as getCdkdVersion } from "./version-DMsY_Ble.js";
2
+ import { n as isVersionOnlyInvocation, t as getCdkdVersion } from "./version-DJGTB4Rm.js";
3
3
 
4
4
  //#region src/cli/pipe-close-handler.ts
5
5
  /**
@@ -74,7 +74,7 @@ async function main() {
74
74
  console.log(getCdkdVersion());
75
75
  return;
76
76
  }
77
- const { buildProgram } = await import("./program-CgSZ7WgF.js");
77
+ const { buildProgram } = await import("./program-Ca0PiFL-.js");
78
78
  const program = buildProgram();
79
79
  const args = reorderArgs(process.argv);
80
80
  await program.parseAsync(args);
@@ -1,6 +1,6 @@
1
- import { 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, y as displaySafe } from "./logger-CecsQ1UT.js";
1
+ import { b 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, y as UNRENDERABLE } from "./logger-DTEUzYP_.js";
2
2
  import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
3
- import { t as getCdkdVersion } from "./version-DMsY_Ble.js";
3
+ import { t as getCdkdVersion } from "./version-DJGTB4Rm.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";
@@ -9134,29 +9134,44 @@ var UnknownRollbackJournalVersionError = class extends Error {
9134
9134
  foundVersion;
9135
9135
  stackName;
9136
9136
  constructor(foundVersion, stackName) {
9137
- super(`Rollback journal for '${stackName}' has journalVersion ${foundVersion}, but this cdkd only understands up to ${1}. Upgrade cdkd to roll this stack back.`);
9137
+ super(`Rollback journal for '${safeJournalText(stackName)}' has journalVersion ${foundVersion}, but this cdkd only understands up to ${1}. Upgrade cdkd to roll this stack back.`);
9138
9138
  this.name = "UnknownRollbackJournalVersionError";
9139
9139
  this.foundVersion = foundVersion;
9140
9140
  this.stackName = stackName;
9141
9141
  }
9142
9142
  };
9143
9143
  /**
9144
+ * One spelling of "this value came from an S3 key or a rollback-journal record,
9145
+ * and is about to be interpolated into a message a terminal will render"
9146
+ * (issue #3064). Call it for a stack name, a parse detail, or a journal field.
9147
+ * `grep safeJournalText` answers the scope; this comment does not.
9148
+ */
9149
+ function safeJournalText(value) {
9150
+ return displaySafe(value, { asciiOnly: true }) || "<unrenderable>";
9151
+ }
9152
+ /**
9144
9153
  * Parse + validate a journal body. Throws
9145
9154
  * {@link UnknownRollbackJournalVersionError} on a newer version, and a plain
9146
9155
  * Error on a structurally-invalid body.
9147
9156
  */
9148
9157
  function parseRollbackJournal(bodyString, stackName) {
9158
+ const shownStack = safeJournalText(stackName);
9149
9159
  let parsed;
9150
9160
  try {
9151
9161
  parsed = JSON.parse(bodyString);
9152
9162
  } catch (err) {
9153
- throw new Error(`Rollback journal for '${stackName}' is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
9163
+ const detail = safeJournalText(err instanceof Error ? err.message : String(err));
9164
+ throw new Error(`Rollback journal for '${shownStack}' is not valid JSON: ${detail}`);
9154
9165
  }
9155
- if (typeof parsed !== "object" || parsed === null) throw new Error(`Rollback journal for '${stackName}' is malformed (not an object).`);
9166
+ if (typeof parsed !== "object" || parsed === null) throw new Error(`Rollback journal for '${shownStack}' is malformed (not an object).`);
9156
9167
  const j = parsed;
9157
- if (typeof j.journalVersion !== "number" || j.journalVersion < 1) throw new Error(`Rollback journal for '${stackName}' has an invalid 'journalVersion' (${String(j.journalVersion)}).`);
9168
+ if (typeof j.journalVersion !== "number" || j.journalVersion < 1) {
9169
+ const raw = j.journalVersion;
9170
+ const shown = safeJournalText(raw === null ? "null" : raw === void 0 ? "undefined" : raw);
9171
+ throw new Error(`Rollback journal for '${shownStack}' has an invalid 'journalVersion' (${shown}).`);
9172
+ }
9158
9173
  if (j.journalVersion > 1) throw new UnknownRollbackJournalVersionError(j.journalVersion, stackName);
9159
- if (!Array.isArray(j.segments)) throw new Error(`Rollback journal for '${stackName}' is missing a 'segments' array.`);
9174
+ if (!Array.isArray(j.segments)) throw new Error(`Rollback journal for '${shownStack}' is missing a 'segments' array.`);
9160
9175
  return {
9161
9176
  journalVersion: j.journalVersion,
9162
9177
  stackName: j.stackName ?? stackName,
@@ -9224,12 +9239,6 @@ const CUSTOM_RESOURCE_RESPONSE_PREFIX = "custom-resource-responses";
9224
9239
  * `for nested-stack child`), so a user grepping CI logs for one of them found
9225
9240
  * two of three. `subject` now varies only the noun.
9226
9241
  */
9227
- /**
9228
- * Stand-in for a value with nothing renderable left after sanitization. Named
9229
- * rather than inlined so the message and the command-suppression branch cannot
9230
- * disagree about what "unrenderable" looks like.
9231
- */
9232
- const UNRENDERABLE = "<unrenderable>";
9233
9242
  /** Render `expiresIn` without implying more precision than a clock skew allows. */
9234
9243
  function formatRemaining(ms) {
9235
9244
  if (!Number.isFinite(ms)) return "at an unknown time";
@@ -10286,17 +10295,23 @@ var S3StateBackend = class {
10286
10295
  * silently lose data on the next save.
10287
10296
  */
10288
10297
  parseStateBody(bodyString, stackName) {
10289
- let parsed;
10298
+ let root;
10290
10299
  try {
10291
- parsed = JSON.parse(bodyString);
10300
+ root = JSON.parse(bodyString);
10292
10301
  } catch (error) {
10293
10302
  const raw = error instanceof Error ? error.message : String(error);
10294
10303
  const detail = displaySafe(raw, { asciiOnly: true }) || "<unrenderable>";
10295
10304
  throw new StateError(`State file for stack '${this.displayName(stackName)}' is not valid JSON: ${detail}`, error instanceof Error ? error : void 0);
10296
10305
  }
10306
+ if (root === null || typeof root !== "object" || Array.isArray(root)) {
10307
+ const got = root === null ? "null" : Array.isArray(root) ? "an array" : `a ${typeof root}`;
10308
+ throw new StateError(`State file for stack '${this.displayName(stackName)}' is not a JSON object (it parses to ${got}), so cdkd cannot read it as a state record.`);
10309
+ }
10310
+ const parsed = root;
10297
10311
  const v = parsed.version;
10298
10312
  if (v !== void 0 && !STATE_SCHEMA_VERSIONS_READABLE.includes(v)) {
10299
- const shown = displaySafe(String(v), { asciiOnly: true }) || "<unrenderable>";
10313
+ const vShown = v;
10314
+ const shown = displaySafe(vShown === null ? "null" : vShown, { asciiOnly: true }) || "<unrenderable>";
10300
10315
  throw new StateError(`Unsupported state schema version ${shown} for stack '${this.displayName(stackName)}'. This cdkd binary supports versions ${STATE_SCHEMA_VERSIONS_READABLE.join(", ")}. Upgrade cdkd to a version that supports schema ${shown}.`);
10301
10316
  }
10302
10317
  return parsed;
@@ -10485,6 +10500,25 @@ var LockManager = class {
10485
10500
  return `${Math.floor(seconds / 60)}m${seconds % 60}s`;
10486
10501
  }
10487
10502
  /**
10503
+ * `expires in 1m23s` / `expired 45s ago` for a finite deadline, `expires at
10504
+ * an unknown time` otherwise — the ONE spelling for a lock's expiry in this
10505
+ * module's messages, so the three sites that render one (the expired-lock
10506
+ * takeover warning, the retry line, the final refusal) cannot disagree.
10507
+ *
10508
+ * The non-finite arm is what stops `expired NaNmNaNs ago` (issue #3083):
10509
+ * `expiresAt` is an unchecked cast, and `getLockRecord` deliberately hands
10510
+ * a coercion that THROWS through as `NaN` (go-to-k/cdkd#2947) rather than
10511
+ * inventing a deadline. The wording matches `formatRemaining` in
10512
+ * `lock-contention-message.ts`, which answered the same input this way
10513
+ * first. `isLockExpired` decides what such a value MEANS (expired); this
10514
+ * only decides what it SAYS.
10515
+ */
10516
+ formatExpiry(expiresAt) {
10517
+ if (!Number.isFinite(expiresAt)) return "expires at an unknown time";
10518
+ const remainingMs = expiresAt - Date.now();
10519
+ return remainingMs > 0 ? `expires in ${this.formatDuration(remainingMs)}` : `expired ${this.formatDuration(-remainingMs)} ago`;
10520
+ }
10521
+ /**
10488
10522
  * Try to acquire a lock for a stack
10489
10523
  *
10490
10524
  * Uses If-None-Match: "*" to ensure atomic lock acquisition.
@@ -10538,7 +10572,8 @@ var LockManager = class {
10538
10572
  return false;
10539
10573
  } else throw deleteError;
10540
10574
  }
10541
- this.logger.warn(`Took over an EXPIRED lock for stack: ${stackName} (${region}, owner: ${existing.info.owner}, expired ${this.formatDuration(now - existing.info.expiresAt)} ago). A live cdkd process renews its lock well inside the TTL, so this normally means the previous owner crashed or was suspended -- if it is in fact still running, both processes are now writing to the same stack.`);
10575
+ const why = Number.isFinite(existing.info.expiresAt) ? "A live cdkd process renews its lock well inside the TTL, so this normally means the previous owner crashed or was suspended" : `Its expiresAt is not a finite number, which cdkd treats as already expired`;
10576
+ this.logger.warn(`Took over an EXPIRED lock for stack: ${stackName} (${region}, owner: ${existing.info.owner}, ${this.formatExpiry(existing.info.expiresAt)}). ${why} -- if it is in fact still running, both processes are now writing to the same stack.`);
10542
10577
  try {
10543
10578
  const retryEtag = await this.putLockObject(key, lockInfo);
10544
10579
  this.logger.debug(`Lock acquired for stack: ${stackName} (${region}) after expired lock cleanup, owner: ${lockOwner}`);
@@ -10624,7 +10659,8 @@ var LockManager = class {
10624
10659
  const lockInfo = {
10625
10660
  ...parsed,
10626
10661
  owner: displaySafe(parsed.owner),
10627
- ...parsed.operation !== void 0 && { operation: displaySafe(parsed.operation) }
10662
+ ...parsed.operation !== void 0 && { operation: displaySafe(parsed.operation) },
10663
+ expiresAt: nanIfUncoercible(parsed.expiresAt)
10628
10664
  };
10629
10665
  this.logger.debug(`Lock info for stack: ${shownStack}:`, lockInfo);
10630
10666
  return {
@@ -11133,23 +11169,37 @@ var LockManager = class {
11133
11169
  if (await this.acquireLock(stackName, region, owner, operation)) return;
11134
11170
  const lockInfo = await this.getLockInfo(stackName, region);
11135
11171
  if (lockInfo) {
11136
- const remainingMs = lockInfo.expiresAt - Date.now();
11137
11172
  if (attempt < maxRetries) {
11138
- this.logger.info(`Stack '${safeSegment(stackName)}' (${safeSegment(region)}) is locked by ${lockInfo.owner}${lockInfo.operation ? ` (operation: ${lockInfo.operation})` : ""}. Lock expires in ${this.formatDuration(remainingMs)}. Retrying in ${this.formatDuration(retryDelay)}... (attempt ${attempt + 1}/${maxRetries})`);
11173
+ this.logger.info(`Stack '${safeSegment(stackName)}' (${safeSegment(region)}) is locked by ${lockInfo.owner}${lockInfo.operation ? ` (operation: ${lockInfo.operation})` : ""}. Lock ${this.formatExpiry(lockInfo.expiresAt)}. Retrying in ${this.formatDuration(retryDelay)}... (attempt ${attempt + 1}/${maxRetries})`);
11139
11174
  await new Promise((resolve) => setTimeout(resolve, retryDelay));
11140
11175
  continue;
11141
11176
  }
11142
11177
  }
11143
11178
  }
11144
11179
  const lockInfo = await this.getLockInfo(stackName, region);
11145
- const expiresIn = lockInfo ? this.formatDuration(lockInfo.expiresAt - Date.now()) : "unknown";
11180
+ const expiry = lockInfo ? this.formatExpiry(lockInfo.expiresAt) : void 0;
11146
11181
  const forceUnlockCommand = buildForceUnlockCommand(stackName, region);
11147
11182
  const recovery = forceUnlockCommand ? `If you are certain no other process is active, run: ${forceUnlockCommand}` : `If you are certain no other process is active, ${UNREPRODUCIBLE_LOCK_CLAUSE.charAt(0).toLowerCase()}${UNREPRODUCIBLE_LOCK_CLAUSE.slice(1)}`;
11148
11183
  const safeStack = safeSegment(stackName);
11149
11184
  const safeRegion = safeSegment(region);
11150
- throw new LockError(`Failed to acquire lock for stack '${safeStack}' (${safeRegion}) after ${maxRetries + 1} attempts. ` + (lockInfo ? `Locked by: ${lockInfo.owner}${lockInfo.operation ? `, operation: ${lockInfo.operation}` : ""}, expires in: ${expiresIn}. ` + recovery : `Lock exists but could not read lock info. ${recovery}`));
11185
+ throw new LockError(`Failed to acquire lock for stack '${safeStack}' (${safeRegion}) after ${maxRetries + 1} attempts. ` + (lockInfo ? `Locked by: ${lockInfo.owner}${lockInfo.operation ? `, operation: ${lockInfo.operation}` : ""}, ${expiry}. ` + recovery : `Lock exists but could not read lock info. ${recovery}`));
11151
11186
  }
11152
11187
  };
11188
+ /**
11189
+ * `value` itself, unless coercing it to a number THROWS — then `NaN`.
11190
+ *
11191
+ * Deliberately not a conversion: see the `expiresAt` note in `getLockRecord`.
11192
+ * The call is the probe — `Number` throws on exactly the values the readers'
11193
+ * arithmetic would throw on, and on nothing else.
11194
+ */
11195
+ function nanIfUncoercible(value) {
11196
+ try {
11197
+ Number(value);
11198
+ return value;
11199
+ } catch {
11200
+ return NaN;
11201
+ }
11202
+ }
11153
11203
 
11154
11204
  //#endregion
11155
11205
  //#region src/deployment/secret-redaction.ts
@@ -12030,7 +12080,11 @@ function recordCrossStackExpression(secrets, key, expression, plaintext) {
12030
12080
  *
12031
12081
  * The `isSingleDynamicReferenceToken` shape invariant is deliberately NOT here:
12032
12082
  * it belongs to each writer, which is where the parameters are named and where
12033
- * a swap could be introduced.
12083
+ * a swap could be introduced. One writer stores a NON-token on purpose:
12084
+ * {@link recordNestedStackParameterExpressions}'s sub-floor carry records a
12085
+ * parameter's literal FRAME (`port:{{resolve:...}}`, issue #3079), the same
12086
+ * string its whole-value entry writes, so a reader returning it persists what
12087
+ * the entry would have.
12034
12088
  */
12035
12089
  function storeAssociation(associations, key, expression, plaintext) {
12036
12090
  const seen = associations.get(key);
@@ -12198,14 +12252,21 @@ const UNFRAMED_SPELLING = Symbol("cdkd.nested-parameter.unframed-spelling");
12198
12252
  * only refusal a test can see: the others coincide with (i) or with
12199
12253
  * the map's own entry, as a whole-token source's would).
12200
12254
  * (iii) `secrets.get(middle) === token` -- the token IS the map's survivor
12201
- * for the middle. Load-bearing: two framed parameters over ONE middle
12202
- * (`pin::` and `pin:AWSCURRENT:`) keep their own tokens on this row's
12203
- * record today because each reaches the span arm with the scan
12204
- * silent; once a `'port:q7'` entry exists the scan answers for the
12205
- * whole leaf and the arm's bound (`scanned === prefix + survivor +
12206
- * suffix`) decides. Recording only the frame whose token is the
12207
- * middle's survivor makes every parent leaf keep its own token by
12208
- * construction rather than by recording order.
12255
+ * for the middle -- OR every framed spelling of the value in the row
12256
+ * carries this token (issue #3079). Load-bearing: two framed
12257
+ * parameters over ONE middle (`pin::` and `pin:AWSCURRENT:`) keep
12258
+ * their own tokens on this row's record today because each reaches
12259
+ * the span arm with the scan silent; once a `'port:q7'` entry exists
12260
+ * the scan answers for the whole leaf and the arm's bound (`scanned
12261
+ * === prefix + survivor + suffix`) decides. With the survivor's
12262
+ * frame as the entry every same-frame leaf passes the bound and
12263
+ * writes its own token; with a loser's, a same-frame sibling around
12264
+ * another token would fail it and take the loser's frame -- so a
12265
+ * loser's frame is the entry only when no such sibling exists, and
12266
+ * then its own leaf reads the entry from the value scan after the
12267
+ * bound refuses, which is its frame. Either way every parent leaf
12268
+ * keeps its own token by construction rather than by recording
12269
+ * order.
12209
12270
  * (iv) ONE FRAME per VALUE, across the whole row. Once the entry exists
12210
12271
  * the scan is no longer silent on the value, so on the parent's
12211
12272
  * record every leaf holding it answers against the entry through the
@@ -12258,19 +12319,41 @@ const UNFRAMED_SPELLING = Symbol("cdkd.nested-parameter.unframed-spelling");
12258
12319
  * refuses a middle that is itself a token, which is what a bag equal to its
12259
12320
  * source has.
12260
12321
  *
12322
+ * THE PER-NAME ASSOCIATION (issue #3079). The entry is keyed by VALUE, so over
12323
+ * one middle with TWO tokens in one frame it names one of them, and every
12324
+ * child leaf holding the value would take that one -- the loser's `{Ref}`
12325
+ * persisting the survivor's frame, which is the wrong reference the three
12326
+ * consumers below re-resolve after the sibling rotates. So beside the entry,
12327
+ * every leaf passing (i), (ii), the pair gate between (ii) and (iv), (iv)
12328
+ * and (v) -- the survivor's and the loser's alike, and whether or not (iii)
12329
+ * let its entry through -- is recorded BY NAME into {@link nestedStackParameterExpressions}, the table
12330
+ * the whole-token walk above fills, as `name -> (its own frame, the value)`.
12331
+ * The child inherits it ({@link inheritNestedStackParameterAssociations})
12332
+ * and reads it through {@link certifiedExpressionForLeaf} at all three
12333
+ * sites: the carry (`recordInheritedParameterSecrets` asks per plaintext),
12334
+ * the persist walk (`positionByCrossStackSource` on the `{Ref}` source) and
12335
+ * the diff side (`redactParametersForDiff`). Each requires the bag to HOLD
12336
+ * the value, which the entry provides -- or the child's OWN resolution of
12337
+ * the same plaintext, in which case the association answers with the leaf's
12338
+ * own frame, the right reference for it. So a FRAMED association whose value
12339
+ * (iii) refused is inert or correct, never wrong -- a claim about this
12340
+ * route's pair gate, not about the whole-token walk above, whose refusal 4
12341
+ * cannot see a public token (issue #3090). The association's expression is
12342
+ * the FRAME, not a token -- the one writer into that table that stores a
12343
+ * non-token, said so on {@link storeAssociation}; its readers return it to be
12344
+ * persisted, which is exactly what the entry would have written.
12345
+ *
12261
12346
  * WHAT STAYS OPEN, weighed against the three live consumers named on
12262
12347
  * {@link positionByIntrinsicFrame} (`cdkd rollback`, `drift --revert`, a
12263
- * consumer's cross-stack read). (a) Over one middle with TWO tokens, (iii)
12264
- * records the survivor's frames only, never a loser's, so the loser's child
12265
- * leaf takes the survivor's frame when the two frames MATCH (`port:` + either
12266
- * token: the survivor's entry is the loser's whole value too) and stays
12267
- * PLAINTEXT when they differ (`port:` + A beside `url:` + B: no entry names
12268
- * `port:q7`). Either is the pre-#2291 answer for this shape; on a match it is
12269
- * the wrong reference those consumers re-resolve -- after the sibling
12270
- * rotates, a rollback or `--revert` of the child hands the sibling's
12271
- * plaintext to the loser's live property. The PARENT's record stays per leaf
12272
- * by (iii) and (iv). One token framed two ways (`port:` + T and `url:` + T)
12273
- * is two values and two entries, not this shape. (b) The framed
12348
+ * consumer's cross-stack read). (a) CLOSED by the association above (was:
12349
+ * the loser's child leaf took the survivor's frame, or stayed plaintext
12350
+ * under a different frame -- the second half closed by (iii)'s second arm).
12351
+ * What remains of it: one child RESOURCE consuming BOTH parameters holds
12352
+ * ONE plaintext-keyed slot for the value, and a leaf of it the association
12353
+ * cannot reach by name -- an `Fn::Sub` EMBEDDING the `{Ref}`, positioned by
12354
+ * the value scan -- reads that slot's frame, whichever `{Ref}` resolved
12355
+ * last; a bare `{Ref}` leaf of the same resource is positioned by name and
12356
+ * is unaffected (pinned). (b) The framed
12274
12357
  * value is a SUBSTRING needle (7 characters here) in every child resource
12275
12358
  * that consumed the parameter, so an unrelated literal there containing it
12276
12359
  * is spliced -- the #2087 class, bounded to resources whose own resolution
@@ -12321,25 +12404,33 @@ function recordNestedStackParameterExpressions(secrets, resourceType, resolvedPr
12321
12404
  storeAssociation(table, name, expression, resolvedValue);
12322
12405
  }
12323
12406
  const framesByValue = /* @__PURE__ */ new Map();
12407
+ const tokensOf = /* @__PURE__ */ new Map();
12324
12408
  for (const [name, resolvedValue] of Object.entries(resolvedParameters)) {
12325
12409
  if (typeof resolvedValue !== "string") continue;
12326
12410
  const sourceLeaf = sourceParameters[name];
12327
12411
  const frame = typeof sourceLeaf === "string" ? singleSpanFrame(resolvedValue, sourceLeaf) : void 0;
12328
12412
  const frames = framesByValue.get(resolvedValue) ?? /* @__PURE__ */ new Set();
12329
- frames.add(frame === void 0 || resolvedPlaintextOf(secrets, frame.token) !== frame.middle ? UNFRAMED_SPELLING : `${frame.prefix.length}:${frame.prefix}${frame.suffix}`);
12413
+ const unframed = frame === void 0 || resolvedPlaintextOf(secrets, frame.token) !== frame.middle;
12414
+ frames.add(unframed ? UNFRAMED_SPELLING : `${frame.prefix.length}:${frame.prefix}${frame.suffix}`);
12330
12415
  framesByValue.set(resolvedValue, frames);
12416
+ if (!unframed) {
12417
+ const tokens = tokensOf.get(resolvedValue) ?? /* @__PURE__ */ new Set();
12418
+ tokens.add(frame.token);
12419
+ tokensOf.set(resolvedValue, tokens);
12420
+ }
12331
12421
  }
12332
12422
  const rowWithoutParameters = {};
12333
12423
  for (const [key, value] of Object.entries(resolvedProperties)) if (key !== "Parameters") rowWithoutParameters[key] = value;
12334
12424
  const outsideLeaves = wholeStringLeavesOf(rowWithoutParameters);
12335
12425
  const framed = /* @__PURE__ */ new Map();
12426
+ const framedByName = [];
12336
12427
  for (const [name, resolvedValue] of Object.entries(resolvedParameters)) {
12337
12428
  const sourceLeaf = sourceParameters[name];
12338
12429
  if (typeof resolvedValue !== "string" || typeof sourceLeaf !== "string") continue;
12339
12430
  if (positioned[name] !== sourceLeaf) continue;
12340
12431
  if (redactSecretsForState(resolvedValue, secrets) !== resolvedValue) continue;
12341
12432
  const frame = singleSpanFrame(resolvedValue, sourceLeaf);
12342
- if (frame === void 0 || secrets.get(frame.middle) !== frame.token) continue;
12433
+ if (frame === void 0 || resolvedPlaintextOf(secrets, frame.token) !== frame.middle) continue;
12343
12434
  if (framesByValue.get(resolvedValue)?.size !== 1) continue;
12344
12435
  const isNeedle = resolvedValue.length >= 4;
12345
12436
  const rewrites = (leaf) => leaf === resolvedValue || isNeedle && leaf.includes(resolvedValue);
@@ -12360,9 +12451,22 @@ function recordNestedStackParameterExpressions(secrets, resourceType, resolvedPr
12360
12451
  }
12361
12452
  }
12362
12453
  if (embedded) continue;
12454
+ framedByName.push([
12455
+ name,
12456
+ sourceLeaf,
12457
+ resolvedValue
12458
+ ]);
12459
+ if (secrets.get(frame.middle) !== frame.token && tokensOf.get(resolvedValue)?.size !== 1) continue;
12363
12460
  framed.set(resolvedValue, sourceLeaf);
12364
12461
  }
12365
12462
  for (const [plaintext, expression] of framed) secrets.set(plaintext, expression);
12463
+ for (const [name, expression, plaintext] of framedByName) {
12464
+ if (table === void 0) {
12465
+ table = /* @__PURE__ */ new Map();
12466
+ nestedStackParameterExpressions.set(secrets, table);
12467
+ }
12468
+ storeAssociation(table, name, expression, plaintext);
12469
+ }
12366
12470
  }
12367
12471
  /**
12368
12472
  * Copy a parent pass's per-PARAMETER associations onto a nested-stack CHILD
@@ -28303,7 +28407,7 @@ var CloudControlProvider = class {
28303
28407
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
28304
28408
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
28305
28409
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
28306
- const { ASGProvider } = await import("./asg-provider-DD_xuWE3.js").then((n) => n.n);
28410
+ const { ASGProvider } = await import("./asg-provider-D6Zyjgrr.js").then((n) => n.n);
28307
28411
  const asgProvider = new ASGProvider();
28308
28412
  return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
28309
28413
  }
@@ -40749,5 +40853,5 @@ var DeployEngine = class DeployEngine {
40749
40853
  };
40750
40854
 
40751
40855
  //#endregion
40752
- export { collectInlinePolicyNamesManagedBySiblings as $, buildDenyExternalAccessPolicy as $n, DynamicReferenceRegionAmbiguousError as $r, describeTypeWithThrottleRetry as $t, isStatefulRecreateTargetForReplace as A, exportNamesCarriedFrom as An, CFN_TEMPLATE_URL_LIMIT as Ar, coerceCfnBoolean as At, gray as B, createAssetRedirectResolver as Bn, processStackMessages as Br, classifyReplaySecretRegion as Bt, refusesFinalSnapshot as C, retryClassificationText as Ci, UNRENDERABLE as Cn, resolveSkipPrefix as Cr, isUnboundTemplateParameter as Ct, makeSiblingClaimReader as D, shellQuote as Dn, stateBucketExistenceConfirmed as Dr, normalizeAwsTagsToCfn as Dt, extractDeploymentEventError as E, forceQuitRecoveryClause as En, resolveUseCdkBootstrapAssets as Er, WAFv2WebACLProvider as Et, isWarmThroughputDecrease as F, shouldRetainResource as Fn, expectedOwnerParam as Fr, readConfigString as Ft, collectPublishedOutputNames as G, assertAssetBucketRegion as Gn, resetAwsClients as Gr, s3BucketDualStackDomainName as Gt, red as H, rewriteTemplateAssetReferences as Hn, resolveBucketRegion as Hr, withSharedDrainBudget as Ht, toFiniteNumber as I, AssetPublisher as In, PARTITION_TABLE as Ir, replayWarn as It, secretBearing as J, isCrossRegionRedirect as Jn, CdkdError as Jr, applyRoleArnIfSet as Jt, exportAliasCollisionScrubWarning as K, ensureAssetStorage as Kn, setAwsClients as Kr, s3BucketRegionalDomainName as Kt, formatResourceLine as L, stringifyValue as Ln, canonicalizeRegion as Lr, requireConfigArray as Lt, renderStatefulReason as M, importableOutputs as Mn, findLargeInlineResources as Mr, configBooleanRefusal as Mt, WARM_THROUGHPUT_MEMBERS as N, orphansAfterRollback as Nn, uploadCfnTemplate as Nr, configIntegerRefusal as Nt, planOrphanAdoption as O, CUSTOM_RESOURCE_RESPONSE_PREFIX as On, warnDeprecatedNoPrefixCliFlag as Or, resolveExplicitPhysicalId as Ot, coerceWarmThroughput as P, orphansCarriedFrom as Pn, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Pr, configStringRefusal as Pt, IAMRoleProvider as Q, validateContainerRepoName as Qn, DeployCancelledError as Qr, findSilentDropProperties as Qt, bold as R, WorkGraph as Rn, derivePartitionAndUrlSuffix as Rr, requireConfigObject as Rt, isFinalSnapshotError as S, markRedactedCause as Si, rebuildClientForBucketRegion as Sn, resolveCaptureObservedState as Sr, getAccountInfo as St, makeCanonicalizePropertiesFn as T, buildLockContentionMessage as Tn, resolveStateBucketWithDefaultAndSource as Tr, refStateLookupFromResource as Tt, yellow as U, AssetModeResolver as Un, AwsClients as Ur, s3BucketArn as Ut, green as V, loadPublishableAssetManifest as Vn, clearBucketRegionCache as Vr, producerRegionsFromState as Vt, collectDeclaredOutputNames as W, BOOTSTRAP_MARKER_PREFIX as Wn, getAwsClients as Wr, s3BucketDomainName as Wt, secretSafeKeyDisplay as X, readBootstrapMarkerBody as Xn, CrossAccountSecretRefusalError as Xr, INTRINSIC_KEYS as Xt, secretBearingStateKeyWarning as Y, parseBootstrapMarker as Yn, ConfigError as Yr, DiffCalculator as Yt, getCurrentResourceSecrets as Z, validateAssetBucketName as Zn, DependencyError as Zr, findActionableSilentDrops as Zt, ATOMIC_FINAL_SNAPSHOT_TYPES as _, isMarkedNonRetryable as _i, recoverMaskedOutput as _n, synthesisStatusMessage as _r, isTerminationProtectionPropagationError as _t, DeploymentEventsStore as a, PartialFailureError as ai, STATE_SOURCED_READBACK_RULES as an, dockerSpawnEnvWithSensitive as ar, maskerOrIdentity as at, ccRoutedFinalSnapshotError as b, isTransientServerError as bi, LockManager as bn, resolveApp as br, cfnRefValueFromPhysicalId as bt, replayFailedOperations as c, ResourceUpdateNotSupportedError as ci, createSecretMasker as cn, partitionSensitiveEnv as cr, interruptWatchListenerCount as ct, updatePartialReason as d, StateError as di, identityKeyFor as dn, runDockerStreaming as dr, CloudControlProvider as dt, IntrinsicResolutionRefusalError as ei, withRetry as en, describeAwsFailure as er, clearOnUpdateRemoval as et, withResourceDeadline as f, SynthesisError as fi, isSingleDynamicReferenceToken as fn, escapeRegExp$1 as fr, slowCcOperationTimeoutMs as ft, bindingSkippedOutputs as g, withErrorHandling as gi, recordMaskOnlyValue as gn, Synthesizer as gr, disableInstanceApiTermination as gt, computeImplicitDeleteEdges as h, normalizeAwsError as hi, maskSecretsInText as hn, getDockerImageBySourceHash as hr, deleteSkipReason as ht, DeploymentEventsReader as i, NestedStackChildDirectDestroyError as ii, STATE_SOURCED_CROSS_GENERATION_RULES as in, describeDockerFailure as ir, maskDeep as it, isStatefulRecreateTargetSync as j, importableOutputKeys as jn, MIGRATE_TMP_PREFIX as jr, coerceCfnInteger as jt, MULTI_REGION_RECREATE_BLOCKED_TYPES as k, DEFAULT_STATE_PREFIX as kn, CFN_TEMPLATE_BODY_LIMIT as kr, assertRegionMatch as kt, replayRollback as l, StackHasActiveImportsError as li, dynamicReferenceTokens as ln, redactDockerArgvValues as lr, isInterruptedWaitError as lt, IMPLICIT_DELETE_DEPENDENCIES as m, isCdkdError as mi, maskSecretsInError as mn, AssetManifestLoader as mr, deleteIndeterminateGuards as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, LocalStartServiceError as ni, TemplateParser as nn, describeDockerCapturedOutput as nr, wouldReturnToSdkProvider as nt, planFailedOps as o, ProvisioningError as oi, TEMPLATE_SOURCED_RULES as on, formatDockerLoginError as or, beginCommandInterruptScope as ot, maskingRetryLogger as p, formatError as pi, markSameGenerationBag as pn, stripControlChars as pr, UNSPECIFIED_SKIP_REASON as pt, isExportAliasCollision as q, getBootstrapMarkerKey as qn, AssetError as qr, s3BucketWebsiteUrl as qt, DeployEngine as r, LockError as ri, STATE_SOURCED_BASELINE_RULES as rn, describeDockerExecFailure as rr, createMaskedRetryLogger as rt, planRollback as s, ResourceTimeoutError as si, carriesSecretMask as sn, getDockerCmd as sr, endCommandInterruptScope as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, LocalInvokeBuildError as ti, DagBuilder as tn, buildDockerImage as tr, ProviderRegistry as tt, updatePartialMessage as u, StackTerminationProtectionError as ui, errorCauseChain as un, runDockerForeground as ur, startInterruptWatch as ut, PRE_DELETE_SNAPSHOT_TYPES as v, isRetryableTransientError as vi, redactSecretsForState as vn, getDefaultStateBucketName as vr, IntrinsicFunctionResolver as vt, unsupportedFinalSnapshotError as w, __exportAll as wi, buildForceUnlockCommand as wn, resolveStateBucketWithDefault as wr, parameterTypeMayLoseSecretIdentity as wt, createPreDeleteFinalSnapshot as x, markNonRetryable as xi, S3StateBackend as xn, resolveAutoAssetStorage as xr, coerceParameterTypedValue as xt, buildFinalSnapshotIdentifier as y, isThrottlingError as yi, scrubResourceRecord as yn, getLegacyStateBucketName as yr, carriesDynamicReference as yt, cyan as z, buildAssetRedirectMap as zn, AssemblyReader as zr, requireConfigString as zt };
40753
- //# sourceMappingURL=deploy-engine-Dhgm0r8o.js.map
40856
+ export { collectInlinePolicyNamesManagedBySiblings as $, describeAwsFailure as $n, IntrinsicResolutionRefusalError as $r, describeTypeWithThrottleRetry as $t, isStatefulRecreateTargetForReplace as A, importableOutputKeys as An, MIGRATE_TMP_PREFIX as Ar, coerceCfnBoolean as At, gray as B, loadPublishableAssetManifest as Bn, clearBucketRegionCache as Br, classifyReplaySecretRegion as Bt, refusesFinalSnapshot as C, __exportAll as Ci, buildForceUnlockCommand as Cn, resolveStateBucketWithDefault as Cr, isUnboundTemplateParameter as Ct, makeSiblingClaimReader as D, CUSTOM_RESOURCE_RESPONSE_PREFIX as Dn, warnDeprecatedNoPrefixCliFlag as Dr, normalizeAwsTagsToCfn as Dt, extractDeploymentEventError as E, shellQuote as En, stateBucketExistenceConfirmed as Er, WAFv2WebACLProvider as Et, isWarmThroughputDecrease as F, AssetPublisher as Fn, PARTITION_TABLE as Fr, readConfigString as Ft, collectPublishedOutputNames as G, ensureAssetStorage as Gn, setAwsClients as Gr, s3BucketDualStackDomainName as Gt, red as H, AssetModeResolver as Hn, AwsClients as Hr, withSharedDrainBudget as Ht, toFiniteNumber as I, stringifyValue as In, canonicalizeRegion as Ir, replayWarn as It, secretBearing as J, parseBootstrapMarker as Jn, ConfigError as Jr, applyRoleArnIfSet as Jt, exportAliasCollisionScrubWarning as K, getBootstrapMarkerKey as Kn, AssetError as Kr, s3BucketRegionalDomainName as Kt, formatResourceLine as L, WorkGraph as Ln, derivePartitionAndUrlSuffix as Lr, requireConfigArray as Lt, renderStatefulReason as M, orphansAfterRollback as Mn, uploadCfnTemplate as Mr, configBooleanRefusal as Mt, WARM_THROUGHPUT_MEMBERS as N, orphansCarriedFrom as Nn, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Nr, configIntegerRefusal as Nt, planOrphanAdoption as O, DEFAULT_STATE_PREFIX as On, CFN_TEMPLATE_BODY_LIMIT as Or, resolveExplicitPhysicalId as Ot, coerceWarmThroughput as P, shouldRetainResource as Pn, expectedOwnerParam as Pr, configStringRefusal as Pt, IAMRoleProvider as Q, buildDenyExternalAccessPolicy as Qn, DynamicReferenceRegionAmbiguousError as Qr, findSilentDropProperties as Qt, bold as R, buildAssetRedirectMap as Rn, AssemblyReader as Rr, requireConfigObject as Rt, isFinalSnapshotError as S, retryClassificationText as Si, rebuildClientForBucketRegion as Sn, resolveSkipPrefix as Sr, getAccountInfo as St, makeCanonicalizePropertiesFn as T, forceQuitRecoveryClause as Tn, resolveUseCdkBootstrapAssets as Tr, refStateLookupFromResource as Tt, yellow as U, BOOTSTRAP_MARKER_PREFIX as Un, getAwsClients as Ur, s3BucketArn as Ut, green as V, rewriteTemplateAssetReferences as Vn, resolveBucketRegion as Vr, producerRegionsFromState as Vt, collectDeclaredOutputNames as W, assertAssetBucketRegion as Wn, resetAwsClients as Wr, s3BucketDomainName as Wt, secretSafeKeyDisplay as X, validateAssetBucketName as Xn, DependencyError as Xr, INTRINSIC_KEYS as Xt, secretBearingStateKeyWarning as Y, readBootstrapMarkerBody as Yn, CrossAccountSecretRefusalError as Yr, DiffCalculator as Yt, getCurrentResourceSecrets as Z, validateContainerRepoName as Zn, DeployCancelledError as Zr, findActionableSilentDrops as Zt, ATOMIC_FINAL_SNAPSHOT_TYPES as _, isRetryableTransientError as _i, recoverMaskedOutput as _n, getDefaultStateBucketName as _r, isTerminationProtectionPropagationError as _t, DeploymentEventsStore as a, ProvisioningError as ai, STATE_SOURCED_READBACK_RULES as an, formatDockerLoginError as ar, maskerOrIdentity as at, ccRoutedFinalSnapshotError as b, markNonRetryable as bi, LockManager as bn, resolveAutoAssetStorage as br, cfnRefValueFromPhysicalId as bt, replayFailedOperations as c, StackHasActiveImportsError as ci, createSecretMasker as cn, redactDockerArgvValues as cr, interruptWatchListenerCount as ct, updatePartialReason as d, SynthesisError as di, identityKeyFor as dn, escapeRegExp$1 as dr, CloudControlProvider as dt, LocalInvokeBuildError as ei, withRetry as en, buildDockerImage as er, clearOnUpdateRemoval as et, withResourceDeadline as f, formatError as fi, isSingleDynamicReferenceToken as fn, stripControlChars as fr, slowCcOperationTimeoutMs as ft, bindingSkippedOutputs as g, isMarkedNonRetryable as gi, recordMaskOnlyValue as gn, synthesisStatusMessage as gr, disableInstanceApiTermination as gt, computeImplicitDeleteEdges as h, withErrorHandling as hi, maskSecretsInText as hn, Synthesizer as hr, deleteSkipReason as ht, DeploymentEventsReader as i, PartialFailureError as ii, STATE_SOURCED_CROSS_GENERATION_RULES as in, dockerSpawnEnvWithSensitive as ir, maskDeep as it, isStatefulRecreateTargetSync as j, importableOutputs as jn, findLargeInlineResources as jr, coerceCfnInteger as jt, MULTI_REGION_RECREATE_BLOCKED_TYPES as k, exportNamesCarriedFrom as kn, CFN_TEMPLATE_URL_LIMIT as kr, assertRegionMatch as kt, replayRollback as l, StackTerminationProtectionError as li, dynamicReferenceTokens as ln, runDockerForeground as lr, isInterruptedWaitError as lt, IMPLICIT_DELETE_DEPENDENCIES as m, normalizeAwsError as mi, maskSecretsInError as mn, getDockerImageBySourceHash as mr, deleteIndeterminateGuards as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, LockError as ni, TemplateParser as nn, describeDockerExecFailure as nr, wouldReturnToSdkProvider as nt, planFailedOps as o, ResourceTimeoutError as oi, TEMPLATE_SOURCED_RULES as on, getDockerCmd as or, beginCommandInterruptScope as ot, maskingRetryLogger as p, isCdkdError as pi, markSameGenerationBag as pn, AssetManifestLoader as pr, UNSPECIFIED_SKIP_REASON as pt, isExportAliasCollision as q, isCrossRegionRedirect as qn, CdkdError as qr, s3BucketWebsiteUrl as qt, DeployEngine as r, NestedStackChildDirectDestroyError as ri, STATE_SOURCED_BASELINE_RULES as rn, describeDockerFailure as rr, createMaskedRetryLogger as rt, planRollback as s, ResourceUpdateNotSupportedError as si, carriesSecretMask as sn, partitionSensitiveEnv as sr, endCommandInterruptScope as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, LocalStartServiceError as ti, DagBuilder as tn, describeDockerCapturedOutput as tr, ProviderRegistry as tt, updatePartialMessage as u, StateError as ui, errorCauseChain as un, runDockerStreaming as ur, startInterruptWatch as ut, PRE_DELETE_SNAPSHOT_TYPES as v, isThrottlingError as vi, redactSecretsForState as vn, getLegacyStateBucketName as vr, IntrinsicFunctionResolver as vt, unsupportedFinalSnapshotError as w, buildLockContentionMessage as wn, resolveStateBucketWithDefaultAndSource as wr, parameterTypeMayLoseSecretIdentity as wt, createPreDeleteFinalSnapshot as x, markRedactedCause as xi, S3StateBackend as xn, resolveCaptureObservedState as xr, coerceParameterTypedValue as xt, buildFinalSnapshotIdentifier as y, isTransientServerError as yi, scrubResourceRecord as yn, resolveApp as yr, carriesDynamicReference as yt, cyan as z, createAssetRedirectResolver as zn, processStackMessages as zr, requireConfigString as zt };
40857
+ //# sourceMappingURL=deploy-engine-DrTOY3f-.js.map