@go-to-k/cdkd 0.289.3 → 0.289.5

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 { 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 } from "./logger-D2Md1kTq.js";
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";
2
2
  import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
3
- import { t as getCdkdVersion } from "./version-DPykgdNN.js";
3
+ import { t as getCdkdVersion } from "./version-DMsY_Ble.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";
@@ -1554,7 +1554,10 @@ function isCdkdError(error) {
1554
1554
  function formatError(error) {
1555
1555
  if (isCdkdError(error)) {
1556
1556
  let message = `${error.name}: ${error.message}`;
1557
- if (error.cause) message += `\nCaused by: ${error.cause.message}`;
1557
+ if (error.cause) {
1558
+ const cause = displaySafe(error.cause.message);
1559
+ if (cause) message += `\nCaused by: ${cause}`;
1560
+ }
1558
1561
  return message;
1559
1562
  }
1560
1563
  if (error instanceof Error) return `${error.name}: ${error.message}`;
@@ -3987,56 +3990,6 @@ async function expectedOwnerParam(client) {
3987
3990
  return owner ? { ExpectedBucketOwner: owner } : {};
3988
3991
  }
3989
3992
 
3990
- //#endregion
3991
- //#region src/utils/display-safe.ts
3992
- /**
3993
- * Make an untrusted value safe to render in a terminal or persist into a log.
3994
- *
3995
- * A LEAF module with no imports, and deliberately in `src/utils/` rather than
3996
- * beside its first caller: issue [#2170](https://github.com/go-to-k/cdkd/issues/2170)'s
3997
- * review found the same rule being widened BY HAND one module at a time and
3998
- * missing an instance every round — the change sanitized 1 of 5 readers of
3999
- * `LockInfo.owner`. One shared definition, imported by everything that renders
4000
- * such a value, is what stops the next reader from inheriting nothing.
4001
- *
4002
- * The stripped class is wider than C0 + DEL, which a first cut used and which
4003
- * misses every mechanism that actually forges a line:
4004
- *
4005
- * - `U+0085` (NEL) and the C1 range — xterm reads `U+009B` as CSI in UTF-8;
4006
- * - `U+2028` / `U+2029` — this text is PERSISTED and re-rendered by JSON and
4007
- * web log viewers, where both are line terminators;
4008
- * - `U+202A`-`U+202E` / `U+2066`-`U+2069` — the Trojan-Source bidi overrides
4009
- * and isolates, which visually REORDER the command being pasted.
4010
- *
4011
- * Known residual, recorded rather than implied away: the invisible formatters
4012
- * (`U+200B`-`U+200D`, `U+FEFF`) and the bidi MARKS (`U+200E` / `U+200F` /
4013
- * `U+061C`) survive, as do bare RTL letters, which no denylist can reach. All
4014
- * of them can only make a rendered name differ visually from its bytes — the
4015
- * command a user pastes still acts on exactly what is shown, and the blast
4016
- * radius stays the attacker's own stack name.
4017
- *
4018
- * A caller whose value has a KNOWN ASCII charset (a stack name, an AWS region)
4019
- * should pass `asciiOnly`, which is a positive allowlist and therefore has no
4020
- * such residual at all.
4021
- *
4022
- * ONE CALLER DELIBERATELY GOES WIDER, and it is recorded here so an editor of
4023
- * the residual note above knows a second module now disagrees with it.
4024
- * `src/deployment/outputs-export-alias.ts` deletes a class derived from
4025
- * `\p{Cc}` / `\p{Cf}` / `\p{Zl}` / `\p{Zp}` /
4026
- * `\p{Default_Ignorable_Code_Point}`, because on THAT path the subject is a possibly
4027
- * secret-bearing name in an operator's log: a plaintext split by a zero-width
4028
- * character is READ as if it were contiguous, so it is disclosed without any
4029
- * paste, and the command-forgery reasoning above does not transfer (issue
4030
- * [#2874](https://github.com/go-to-k/cdkd/issues/2874)). Nothing here changes
4031
- * -- widening this helper would alter every caller that merely wants a
4032
- * terminal-safe string.
4033
- */
4034
- function displaySafe(value, opts) {
4035
- if (value === void 0 || value === null) return "";
4036
- const text = String(value);
4037
- return (opts?.asciiOnly ? text.replace(/[^ -~]/g, " ") : text.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029\u202a-\u202e\u2066-\u2069]/g, " ")).trim();
4038
- }
4039
-
4040
3993
  //#endregion
4041
3994
  //#region src/state/s3-replication-purge-gap.ts
4042
3995
  /**
@@ -9656,18 +9609,20 @@ var S3StateBackend = class {
9656
9609
  async getState(stackName, region) {
9657
9610
  await this.ensureClientForBucket();
9658
9611
  const newKey = this.getStateKey(stackName, region);
9612
+ const shownStackName = this.displayName(stackName);
9613
+ const shownRegionName = displaySafe(region, { asciiOnly: true }) || "<unrenderable>";
9659
9614
  try {
9660
- this.logger.debug(`Getting state for stack: ${stackName} (${region})`);
9615
+ this.logger.debug(`Getting state for stack: ${shownStackName} (${shownRegionName})`);
9661
9616
  const response = await this.s3Client.send(new GetObjectCommand({
9662
9617
  Bucket: this.config.bucket,
9663
9618
  ...await this.ownerParam(),
9664
9619
  Key: newKey
9665
9620
  }));
9666
- if (!response.Body) throw new StateError(`State file for stack '${stackName}' (${region}) has no body`);
9667
- if (!response.ETag) throw new StateError(`State file for stack '${stackName}' (${region}) has no ETag`);
9621
+ if (!response.Body) throw new StateError(`State file for stack '${shownStackName}' (${shownRegionName}) has no body`);
9622
+ if (!response.ETag) throw new StateError(`State file for stack '${shownStackName}' (${shownRegionName}) has no ETag`);
9668
9623
  const bodyString = await response.Body.transformToString();
9669
9624
  const state = this.parseStateBody(bodyString, stackName);
9670
- this.logger.debug(`Retrieved state: ${stackName} (${region}), ETag: ${response.ETag}`);
9625
+ this.logger.debug(`Retrieved state: ${shownStackName} (${shownRegionName}), ETag: ${response.ETag}`);
9671
9626
  return {
9672
9627
  state,
9673
9628
  etag: response.ETag
@@ -9675,13 +9630,15 @@ var S3StateBackend = class {
9675
9630
  } catch (error) {
9676
9631
  if (!isNoSuchKey(error)) {
9677
9632
  if (error instanceof StateError) throw error;
9678
- throw new StateError(`Failed to get state for stack '${stackName}' (${region}): ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : void 0);
9633
+ const detail = displaySafe(error instanceof Error ? error.message : String(error), { asciiOnly: true }) || "<unrenderable>";
9634
+ throw new StateError(`Failed to get state for stack '${shownStackName}' (${shownRegionName}): ${detail}`, error instanceof Error ? error : void 0);
9679
9635
  }
9680
- this.logger.debug(`No state at new key for stack: ${stackName} (${region})`);
9636
+ this.logger.debug(`No state at new key for stack: ${shownStackName} (${shownRegionName})`);
9681
9637
  }
9682
9638
  const legacy = await this.tryGetLegacy(stackName, region);
9683
9639
  if (legacy) {
9684
- this.logger.warn(`Loaded legacy state for stack '${stackName}' from '${this.getLegacyStateKey(stackName)}'. It will be migrated to the region-scoped layout on next save.`);
9640
+ const shownKey = displaySafe(this.getLegacyStateKey(stackName), { asciiOnly: true }) || "<unrenderable>";
9641
+ this.logger.warn(`Loaded legacy state for stack '${shownStackName}' from '${shownKey}'. It will be migrated to the region-scoped layout on next save.`);
9685
9642
  return {
9686
9643
  ...legacy,
9687
9644
  migrationPending: true
@@ -10309,7 +10266,7 @@ var S3StateBackend = class {
10309
10266
  const bodyString = await response.Body.transformToString();
10310
10267
  const state = this.parseStateBody(bodyString, stackName);
10311
10268
  if (state.region && state.region !== region) {
10312
- this.logger.debug(`Legacy state for stack '${stackName}' has region '${state.region}', not '${region}' — skipping legacy fallback.`);
10269
+ this.logger.debug(`Legacy state for stack '${this.displayName(stackName)}' has region '${displaySafe(state.region, { asciiOnly: true }) || "<unrenderable>"}', not '${displaySafe(region, { asciiOnly: true }) || "<unrenderable>"}' — skipping legacy fallback.`);
10313
10270
  return null;
10314
10271
  }
10315
10272
  return {
@@ -10318,7 +10275,8 @@ var S3StateBackend = class {
10318
10275
  };
10319
10276
  } catch (error) {
10320
10277
  if (isNoSuchKey(error)) return null;
10321
- throw new StateError(`Failed to get legacy state for stack '${stackName}': ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : void 0);
10278
+ const detail = displaySafe(error instanceof Error ? error.message : String(error), { asciiOnly: true }) || "<unrenderable>";
10279
+ throw new StateError(`Failed to get legacy state for stack '${this.displayName(stackName)}': ${detail}`, error instanceof Error ? error : void 0);
10322
10280
  }
10323
10281
  }
10324
10282
  /**
@@ -10332,10 +10290,15 @@ var S3StateBackend = class {
10332
10290
  try {
10333
10291
  parsed = JSON.parse(bodyString);
10334
10292
  } catch (error) {
10335
- throw new StateError(`State file for stack '${stackName}' is not valid JSON: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : void 0);
10293
+ const raw = error instanceof Error ? error.message : String(error);
10294
+ const detail = displaySafe(raw, { asciiOnly: true }) || "<unrenderable>";
10295
+ throw new StateError(`State file for stack '${this.displayName(stackName)}' is not valid JSON: ${detail}`, error instanceof Error ? error : void 0);
10336
10296
  }
10337
10297
  const v = parsed.version;
10338
- if (v !== void 0 && !STATE_SCHEMA_VERSIONS_READABLE.includes(v)) throw new StateError(`Unsupported state schema version ${String(v)} for stack '${stackName}'. This cdkd binary supports versions ${STATE_SCHEMA_VERSIONS_READABLE.join(", ")}. Upgrade cdkd to a version that supports schema ${String(v)}.`);
10298
+ if (v !== void 0 && !STATE_SCHEMA_VERSIONS_READABLE.includes(v)) {
10299
+ const shown = displaySafe(String(v), { asciiOnly: true }) || "<unrenderable>";
10300
+ 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
+ }
10339
10302
  return parsed;
10340
10303
  }
10341
10304
  };
@@ -10381,6 +10344,20 @@ const MIN_RENEWAL_INTERVAL_MS = 1e3;
10381
10344
  */
10382
10345
  const LOCK_OBJECT_DESCRIPTION = "a stack lock heartbeat, which records the lock owner, its acquisition timestamp, its deadline and the operation name — no secret, but one row per renewal";
10383
10346
  /**
10347
+ * One spelling of "this value came from an S3 key or a lock record, and is
10348
+ * about to be interpolated into a message a terminal will render". A stack
10349
+ * name and an AWS region both have a known charset, so the ASCII allowlist is
10350
+ * a no-op on every legitimate input while an S3 key admits any UTF-8.
10351
+ *
10352
+ * Call it for a stack name or a region. Not every value in this file goes
10353
+ * through it, and this comment does not say which do: three revisions of that
10354
+ * sentence carried a count or a scope claim and each was wrong.
10355
+ * `grep safeSegment` answers it exactly.
10356
+ */
10357
+ function safeSegment(value) {
10358
+ return displaySafe(value, { asciiOnly: true }) || "<unrenderable>";
10359
+ }
10360
+ /**
10384
10361
  * S3-based lock manager using conditional writes (If-None-Match)
10385
10362
  *
10386
10363
  * Implements distributed locking using S3's If-None-Match: "*" condition
@@ -10585,7 +10562,7 @@ var LockManager = class {
10585
10562
  }
10586
10563
  return false;
10587
10564
  }
10588
- throw new LockError(`Failed to acquire lock for stack '${displaySafe(stackName, { asciiOnly: true }) || "<unrenderable>"}' (${displaySafe(region, { asciiOnly: true }) || "<unrenderable>"}): ${displaySafe(error instanceof Error ? error.message : String(error))}`, error instanceof Error ? error : void 0);
10565
+ throw new LockError(`Failed to acquire lock for stack '${safeSegment(stackName)}' (${safeSegment(region)}): ${displaySafe(error instanceof Error ? error.message : String(error), { asciiOnly: true }) || "<unrenderable>"}`, error instanceof Error ? error : void 0);
10589
10566
  }
10590
10567
  }
10591
10568
  /**
@@ -10628,18 +10605,19 @@ var LockManager = class {
10628
10605
  async getLockRecord(stackName, region) {
10629
10606
  await this.ensureClientForBucket();
10630
10607
  const key = this.getLockKey(stackName, region);
10608
+ const shownStack = safeSegment(stackName);
10631
10609
  try {
10632
- this.logger.debug(`Getting lock info for stack: ${stackName}`);
10610
+ this.logger.debug(`Getting lock info for stack: ${shownStack}`);
10633
10611
  const response = await this.s3Client.send(new GetObjectCommand({
10634
10612
  Bucket: this.config.bucket,
10635
10613
  ...await this.ownerParam(),
10636
10614
  Key: key
10637
10615
  }));
10638
- if (!response.Body) throw new LockError(`Lock file for stack '${stackName}' has no body`);
10616
+ if (!response.Body) throw new LockError(`Lock file for stack '${shownStack}' has no body`);
10639
10617
  const bodyString = await response.Body.transformToString();
10640
10618
  const raw = JSON.parse(bodyString);
10641
10619
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
10642
- this.logger.debug(`Lock file for stack ${stackName} is not an object; treating as absent`);
10620
+ this.logger.debug(`Lock file for stack ${shownStack} is not an object; treating as absent`);
10643
10621
  return null;
10644
10622
  }
10645
10623
  const parsed = raw;
@@ -10648,18 +10626,19 @@ var LockManager = class {
10648
10626
  owner: displaySafe(parsed.owner),
10649
10627
  ...parsed.operation !== void 0 && { operation: displaySafe(parsed.operation) }
10650
10628
  };
10651
- this.logger.debug(`Lock info for stack: ${stackName}:`, lockInfo);
10629
+ this.logger.debug(`Lock info for stack: ${shownStack}:`, lockInfo);
10652
10630
  return {
10653
10631
  info: lockInfo,
10654
10632
  etag: response.ETag
10655
10633
  };
10656
10634
  } catch (error) {
10657
10635
  if (error instanceof NoSuchKey) {
10658
- this.logger.debug(`No lock exists for stack: ${stackName}`);
10636
+ this.logger.debug(`No lock exists for stack: ${shownStack}`);
10659
10637
  return null;
10660
10638
  }
10661
10639
  if (error instanceof LockError) throw error;
10662
- throw new LockError(`Failed to get lock info for stack '${stackName}': ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : void 0);
10640
+ const detail = displaySafe(error instanceof Error ? error.message : String(error), { asciiOnly: true }) || "<unrenderable>";
10641
+ throw new LockError(`Failed to get lock info for stack '${shownStack}': ${detail}`, error instanceof Error ? error : void 0);
10663
10642
  }
10664
10643
  }
10665
10644
  /**
@@ -11156,7 +11135,7 @@ var LockManager = class {
11156
11135
  if (lockInfo) {
11157
11136
  const remainingMs = lockInfo.expiresAt - Date.now();
11158
11137
  if (attempt < maxRetries) {
11159
- this.logger.info(`Stack '${displaySafe(stackName, { asciiOnly: true }) || "<unrenderable>"}' (${displaySafe(region, { asciiOnly: true }) || "<unrenderable>"}) 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})`);
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})`);
11160
11139
  await new Promise((resolve) => setTimeout(resolve, retryDelay));
11161
11140
  continue;
11162
11141
  }
@@ -11166,8 +11145,8 @@ var LockManager = class {
11166
11145
  const expiresIn = lockInfo ? this.formatDuration(lockInfo.expiresAt - Date.now()) : "unknown";
11167
11146
  const forceUnlockCommand = buildForceUnlockCommand(stackName, region);
11168
11147
  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)}`;
11169
- const safeStack = displaySafe(stackName, { asciiOnly: true }) || "<unrenderable>";
11170
- const safeRegion = displaySafe(region, { asciiOnly: true }) || "<unrenderable>";
11148
+ const safeStack = safeSegment(stackName);
11149
+ const safeRegion = safeSegment(region);
11171
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}`));
11172
11151
  }
11173
11152
  };
@@ -11300,7 +11279,8 @@ function resolvedExpressionsOf(secrets) {
11300
11279
  * for the object it is handed. A copy of the bag, a derived needle map, a
11301
11280
  * previous generation's record, a scrub / drift walk and a bag nobody marked
11302
11281
  * all answer `false` and keep the fall-through. `cdkd import` marks the one
11303
- * bag its own resolver produced (the sixth site below, issue #2745).
11282
+ * bag its own resolver produced (the sixth site below, issue #2745), and the
11283
+ * nested-stack recorder marks the COPY it positions (the seventh, same issue).
11304
11284
  *
11305
11285
  * A `WeakSet` for the reason {@link resolvedPairsOf} is a `WeakMap`: the mark
11306
11286
  * dies with the object it is on, and nothing has to clear it.
@@ -11335,9 +11315,9 @@ const sameGenerationBags = /* @__PURE__ */ new WeakSet();
11335
11315
  * and may skip it entirely. "Every leaf this pass produced" is false for the
11336
11316
  * auto-refresh readback of an UNCHANGED resource, where nothing was resolved
11337
11317
  * at all — there the safety comes from the empty secrets map, not from the
11338
- * mark. Read the per-site list below rather than a rule over all six.
11318
+ * mark. Read the per-site list below rather than a rule over all seven.
11339
11319
  *
11340
- * The six call sites, and which of them the record HOLDS, because the earlier
11320
+ * The seven call sites, and which of them the record HOLDS, because the earlier
11341
11321
  * "two never-installed copies" reading of this paragraph was false once the
11342
11322
  * third copy arrived:
11343
11323
  * - `propertiesToRecord` — the record's `properties`, installed. The resolved
@@ -11382,6 +11362,20 @@ const sameGenerationBags = /* @__PURE__ */ new WeakSet();
11382
11362
  * it does NOT claim is that AWS holds the value: `observedProperties` is
11383
11363
  * captured separately, against the redacted record, and is never marked
11384
11364
  * here.
11365
+ * - `recordNestedStackParameterExpressions` (issue #2745) — a shallow COPY of
11366
+ * a nested-stack row's resolved `Parameters` sub-bag, marked as the
11367
+ * position pass's input so a sub-floor middle a literal frame embeds is
11368
+ * written as its token there, which is what the recorder's sub-floor
11369
+ * carry reads. NOT installed: the caller's own object is untouched, and
11370
+ * whether THAT gets marked is decided at the caller (the deploy engine's
11371
+ * two sites mark it later, in `propertiesToRecord`; the replay's three
11372
+ * never do). Pair provenance holds at all five callers: the engine's two
11373
+ * resolve the row in the pass that recorded the pairs, and the replay's
11374
+ * three hand it the bag `resolveReplayProps` filled through
11375
+ * `recordResolvedPair` from the journal rows it resolved just before -- the
11376
+ * desired row, and on the update and failed-op arms the current or
11377
+ * attempted row as well -- all within one replay, so every pair in it is
11378
+ * that pass's own.
11385
11379
  */
11386
11380
  function markSameGenerationBag(bag) {
11387
11381
  sameGenerationBags.add(bag);
@@ -11712,6 +11706,21 @@ function substringNeedlesOf(secrets) {
11712
11706
  * documented as "the expressions this pass recorded" must not silently contain
11713
11707
  * something that is not one: the day a candidate test stops requiring token
11714
11708
  * SHAPE, the sentinel would be live in it.
11709
+ *
11710
+ * A SECOND non-token class passes the filter, and is meant to: the FRAMED
11711
+ * whole-value entry `recordNestedStackParameterExpressions` writes for a
11712
+ * sub-floor carry (issue #2745) has a `port:{{resolve:...}}` value. It
11713
+ * enters the skeleton and frame candidate unions. The frame pattern is
11714
+ * anchored to the source's token and cannot match it; the SKELETON pattern of
11715
+ * an `Fn::Join` / `Fn::Sub` spelling the same frame around the same token
11716
+ * CAN, and then names that leaf's own frame around this pass's token -- the
11717
+ * correct expression for it, and one `isKnownSecretExpression` reads as
11718
+ * secret anyway, since the value is a map entry. What it DOES cost is the
11719
+ * length cap -- a framed value longer than
11720
+ * `MAX_SKELETON_CANDIDATE_LENGTH` makes both intrinsic arms refuse every
11721
+ * INTRINSIC-sourced leaf of the resource whose bag holds it; a literal-source
11722
+ * leaf keeps the span arm, which reads no candidate list (the recorder's doc
11723
+ * lists it).
11715
11724
  */
11716
11725
  function recordedExpressionsOf(secrets) {
11717
11726
  const expressions = /* @__PURE__ */ new Set();
@@ -12081,6 +12090,14 @@ const NESTED_STACK_RESOURCE_TYPE$2 = "AWS::CloudFormation::Stack";
12081
12090
  */
12082
12091
  const nestedStackParameterExpressions = /* @__PURE__ */ new WeakMap();
12083
12092
  /**
12093
+ * Condition (iv)'s key (see {@link recordNestedStackParameterExpressions}) for
12094
+ * a `Parameters` spelling that is not a single-span literal frame of its
12095
+ * resolved value carrying this pass's pair for its token: an object source, a
12096
+ * plain literal, a two-span literal, a bag equal to its source, and a frame
12097
+ * whose token this pass never resolved to the middle (a PUBLIC sibling).
12098
+ */
12099
+ const UNFRAMED_SPELLING = Symbol("cdkd.nested-parameter.unframed-spelling");
12100
+ /**
12084
12101
  * Record, for the pass that owns `secrets`, which `{{resolve:...}}` expression
12085
12102
  * each `Parameters` entry of a nested-stack row was resolved FROM (issue
12086
12103
  * #2291). No-op for every other resource type.
@@ -12154,6 +12171,127 @@ const nestedStackParameterExpressions = /* @__PURE__ */ new WeakMap();
12154
12171
  * `return source`, which is the same string again), so both callers reach it.
12155
12172
  * Fenced by the self-referential case in
12156
12173
  * `secret-redaction-nested-parameter-source.test.ts`.
12174
+ *
12175
+ * THE SUB-FLOOR CARRY (issue #2745, its nested-stack site). A parameter the
12176
+ * parent spelled as a LITERAL frame around one token -- `Pin:
12177
+ * 'port:{{resolve:secretsmanager:S:SecretString:pin::}}'` -- resolves to
12178
+ * `port:q7`: not a key of the map (refusal 1), with a middle below
12179
+ * `MIN_NEEDLE_LENGTH`, so the child's carry (`inheritedSecretsCarriedBy`:
12180
+ * whole value at any length, substring at or above the floor) misses it both
12181
+ * ways and the child persisted `port:q7`. This recorder is the only point
12182
+ * holding the literal frame beside the pair, so the carry is written HERE:
12183
+ * the position pass runs over a MARKED shallow copy, on which
12184
+ * {@link positionByEmbeddedSpan} writes the frame on pair evidence, and a
12185
+ * second walk records `resolvedValue -> sourceLeaf`
12186
+ * (`'port:q7' -> 'port:{{resolve:...}}'`) as an ordinary WHOLE-VALUE entry of
12187
+ * the parent's own bag, under five conditions:
12188
+ *
12189
+ * (i) `positioned[name] === sourceLeaf` -- the pass CERTIFIED the leaf.
12190
+ * On a literal source the span arm returns the source verbatim, so a
12191
+ * mismatch proves it did not fire -- refusal 2b's test, applied to
12192
+ * a literal source; pinned by the "map NO RESOLVER populated" case,
12193
+ * where (ii) and (iii) both pass and only the missing pair refuses.
12194
+ * (ii) `redactSecretsForState(resolvedValue, secrets) === resolvedValue`
12195
+ * -- the value scan is SILENT on it: the sub-floor gate, byte-for-byte
12196
+ * the bound the span arm accepted. What it refuses is a middle at or
12197
+ * above the floor, whose substring carry the child already has (the
12198
+ * only refusal a test can see: the others coincide with (i) or with
12199
+ * the map's own entry, as a whole-token source's would).
12200
+ * (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.
12209
+ * (iv) ONE FRAME per VALUE, across the whole row. Once the entry exists
12210
+ * the scan is no longer silent on the value, so on the parent's
12211
+ * record every leaf holding it answers against the entry through the
12212
+ * span arm's bound (`scanned === prefix + survivor + suffix`). The
12213
+ * SAME frame around another token -- the (iii) shape, `port:` + A
12214
+ * beside `port:` + B -- passes that bound and keeps its own token. A
12215
+ * DIFFERENT frame (`port:` + `q7` beside `port` + `:q7`, each passing
12216
+ * (i)-(iii) on its own), a leaf (iii) refused under a different
12217
+ * frame, an object spelling and a plain literal equal to the value
12218
+ * all fail it and would take the entry's frame -- and so would a
12219
+ * same-frame sibling whose token this pass never resolved to the
12220
+ * middle: a PUBLIC `ssm` reference in the same `port:` frame holding
12221
+ * the same two characters is kept RESOLVED on the record, and the
12222
+ * entry would rewrite it to the secret sibling's expression. So the
12223
+ * frames (prefix + suffix) of every spelling of every string value in
12224
+ * the row are gathered BEFORE the conditions run, a spelling that is
12225
+ * not a single-span literal frame with this pass's pair for its token
12226
+ * (`resolvedPlaintextOf`) counting as its own, and a value is
12227
+ * recorded only when its certified frame is the row's only one.
12228
+ * (v) No OTHER string leaf of the ROW would be rewritten by the entry,
12229
+ * read over every leaf of the resolved row since the bag is per
12230
+ * resource, not per `Parameters`. Two arms of the persist walk read
12231
+ * a map entry. The WHOLE-VALUE arm is floorless, so a leaf EQUAL to
12232
+ * the value OUTSIDE `Parameters` (a `TemplateURL` that happens to
12233
+ * equal it has no frame of its own and escapes (iv)) or inside a
12234
+ * LIST-valued sibling parameter (an array here; `extractParameters`
12235
+ * joins it back for the wire) refuses at any length; equality
12236
+ * between string parameters is (iv)'s frame identity. The SUBSTRING
12237
+ * arm is reached only by a value at or above `MIN_NEEDLE_LENGTH`
12238
+ * (`port:q7` clears the floor its bare middle sits under), so a
12239
+ * sibling merely CONTAINING the value -- `x-port` + `:q7` from
12240
+ * another token, a plain literal `literal-port:q7-end` -- refuses
12241
+ * only then, or it would be spliced with this frame on the parent's
12242
+ * record, exactly the #2087 splice the child side is scoped against;
12243
+ * a 3-character `pq7` beside such a sibling is carried, since no
12244
+ * needle exists for it.
12245
+ *
12246
+ * Written AFTER the walk, so no iteration reads the recorder's own write --
12247
+ * a statement of intent rather than a pinned behaviour: under (iv) and (v)
12248
+ * an in-loop write is EQUIVALENT (a later value the entry would rewrite is
12249
+ * refused by (v), an equal one by (iv) or as the same entry), so no test can
12250
+ * red on the order, and it is kept so (ii)'s reading of the map cannot come
12251
+ * to depend on iteration order. No pair and no pin are recorded for the
12252
+ * entry: it is not a token
12253
+ * ({@link recordedExpressionsOf} names the class), and no consumer needs one
12254
+ * -- the child's carry, the child's persist (the floorless whole-value arm),
12255
+ * the diff side (`redactParametersForDiff`'s fallback) and the parent's
12256
+ * rollback record all read the map by whole value. Refusal 3's
12257
+ * self-referential shape cannot reach the write: {@link singleSpanFrame}
12258
+ * refuses a middle that is itself a token, which is what a bag equal to its
12259
+ * source has.
12260
+ *
12261
+ * WHAT STAYS OPEN, weighed against the three live consumers named on
12262
+ * {@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
12274
+ * value is a SUBSTRING needle (7 characters here) in every child resource
12275
+ * that consumed the parameter, so an unrelated literal there containing it
12276
+ * is spliced -- the #2087 class, bounded to resources whose own resolution
12277
+ * consumed the parameter; on the parent's own row (v) refuses the entry
12278
+ * instead, over the leaves visible at record time -- a readback leaf AWS
12279
+ * rewrote to contain the value is spliced like any 4+ character needle's.
12280
+ * (c) A frame longer than
12281
+ * `MAX_SKELETON_CANDIDATE_LENGTH` makes both intrinsic arms refuse every
12282
+ * INTRINSIC-sourced leaf of that child resource, which then falls to the
12283
+ * value scan (a literal-source leaf keeps the span arm). (d) A child
12284
+ * OUTPUT carrying the framed value re-resolves in the parent to `port:q7`,
12285
+ * where the cross-stack seam refuses a non-token and the consumer's leaf
12286
+ * persists the plaintext -- the same class as the nonliteral-frame deferral.
12287
+ * (e) An OBJECT-spelled parameter source (`Fn::Join` / `Fn::Sub` in the
12288
+ * parent's own `Parameters` block) is out of this arm's reach -- (i) cannot
12289
+ * hold for one. #2745 scopes this site to the literal spelling; the object
12290
+ * spelling is issue #3062. (f) A child record persisted BEFORE this carry
12291
+ * keeps `port:q7` until the child is next redeployed: the parent's own row
12292
+ * already held the frame (the literal arm), so a parent deploy whose child
12293
+ * row is unchanged never re-runs the child, and `cdkd scrub` cannot repair
12294
+ * it either -- it walks the child's stored bag with no inherited bag.
12157
12295
  */
12158
12296
  function recordNestedStackParameterExpressions(secrets, resourceType, resolvedProperties, sourceProperties, rules = TEMPLATE_DERIVED_RULES) {
12159
12297
  if (resourceType !== NESTED_STACK_RESOURCE_TYPE$2) return;
@@ -12164,7 +12302,7 @@ function recordNestedStackParameterExpressions(secrets, resourceType, resolvedPr
12164
12302
  const resolvedParameters = resolvedProperties["Parameters"];
12165
12303
  const sourceParameters = sourceProperties["Parameters"];
12166
12304
  if (!isPlainObject$2(resolvedParameters) || !isPlainObject$2(sourceParameters)) return;
12167
- const positioned = redactSecretsForState(resolvedParameters, secrets, sourceParameters, rules);
12305
+ const positioned = redactSecretsForState(markSameGenerationBag({ ...resolvedParameters }), secrets, sourceParameters, rules);
12168
12306
  let table = nestedStackParameterExpressions.get(secrets);
12169
12307
  for (const [name, resolvedValue] of Object.entries(resolvedParameters)) {
12170
12308
  if (typeof resolvedValue !== "string" || !secrets.has(resolvedValue)) continue;
@@ -12182,6 +12320,49 @@ function recordNestedStackParameterExpressions(secrets, resourceType, resolvedPr
12182
12320
  }
12183
12321
  storeAssociation(table, name, expression, resolvedValue);
12184
12322
  }
12323
+ const framesByValue = /* @__PURE__ */ new Map();
12324
+ for (const [name, resolvedValue] of Object.entries(resolvedParameters)) {
12325
+ if (typeof resolvedValue !== "string") continue;
12326
+ const sourceLeaf = sourceParameters[name];
12327
+ const frame = typeof sourceLeaf === "string" ? singleSpanFrame(resolvedValue, sourceLeaf) : void 0;
12328
+ 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}`);
12330
+ framesByValue.set(resolvedValue, frames);
12331
+ }
12332
+ const rowWithoutParameters = {};
12333
+ for (const [key, value] of Object.entries(resolvedProperties)) if (key !== "Parameters") rowWithoutParameters[key] = value;
12334
+ const outsideLeaves = wholeStringLeavesOf(rowWithoutParameters);
12335
+ const framed = /* @__PURE__ */ new Map();
12336
+ for (const [name, resolvedValue] of Object.entries(resolvedParameters)) {
12337
+ const sourceLeaf = sourceParameters[name];
12338
+ if (typeof resolvedValue !== "string" || typeof sourceLeaf !== "string") continue;
12339
+ if (positioned[name] !== sourceLeaf) continue;
12340
+ if (redactSecretsForState(resolvedValue, secrets) !== resolvedValue) continue;
12341
+ const frame = singleSpanFrame(resolvedValue, sourceLeaf);
12342
+ if (frame === void 0 || secrets.get(frame.middle) !== frame.token) continue;
12343
+ if (framesByValue.get(resolvedValue)?.size !== 1) continue;
12344
+ const isNeedle = resolvedValue.length >= 4;
12345
+ const rewrites = (leaf) => leaf === resolvedValue || isNeedle && leaf.includes(resolvedValue);
12346
+ let embedded = false;
12347
+ for (const leaf of outsideLeaves) if (rewrites(leaf)) {
12348
+ embedded = true;
12349
+ break;
12350
+ }
12351
+ for (const other of Object.values(resolvedParameters)) {
12352
+ if (embedded) break;
12353
+ if (typeof other === "string") {
12354
+ if (other !== resolvedValue && rewrites(other)) embedded = true;
12355
+ continue;
12356
+ }
12357
+ for (const leaf of wholeStringLeavesOf(other)) if (rewrites(leaf)) {
12358
+ embedded = true;
12359
+ break;
12360
+ }
12361
+ }
12362
+ if (embedded) continue;
12363
+ framed.set(resolvedValue, sourceLeaf);
12364
+ }
12365
+ for (const [plaintext, expression] of framed) secrets.set(plaintext, expression);
12185
12366
  }
12186
12367
  /**
12187
12368
  * Copy a parent pass's per-PARAMETER associations onto a nested-stack CHILD
@@ -13047,10 +13228,14 @@ function positionByCrossStackSource(bag, source, secrets) {
13047
13228
  * template text, which genuinely can be a PUBLIC ssm reference that must stay
13048
13229
  * resolved in state (issue #1901), so it has to be tested. Here the candidates
13049
13230
  * come only from {@link recordedSecretExpressions} and from the values of a
13050
- * {@link RecordedSecretValues} map, both of which the resolver populates ONLY
13051
- * on a proven-secret verdict — so the test could never answer `false`, and an
13052
- * unfalsifiable guard reads as protection while fencing nothing. Widening
13053
- * either candidate source is what would make it necessary again.
13231
+ * {@link RecordedSecretValues} map, which the resolver populates ONLY on a
13232
+ * proven-secret verdict — and, since issue #2745, the nested-stack recorder's
13233
+ * framed carry entries ({@link recordedExpressionsOf}), which are not
13234
+ * references but ARE map values, so the same membership test reads them as
13235
+ * secret too. So the test could never answer `false`, and an
13236
+ * unfalsifiable guard reads as protection while fencing nothing. Widening the
13237
+ * candidate sources to a reference that can be public is what would make it
13238
+ * necessary again.
13054
13239
  */
13055
13240
  function positionByIntrinsicSkeleton(bag, source, secrets, secretExpressions) {
13056
13241
  if (bag === "" || !secrets.has(bag)) return void 0;
@@ -20533,11 +20718,15 @@ const drainDeadlines = new AsyncLocalStorage();
20533
20718
  *
20534
20719
  * THE TRADE, stated because it is real: iterations that actually WAIT spend
20535
20720
  * the shared budget, so a later one can find it exhausted and its drain get
20536
- * no grace. That is the same exposure issue
20721
+ * no grace. That is the exposure issue
20537
20722
  * [#2814](https://github.com/go-to-k/cdkd/issues/2814) records for nesting,
20538
- * now reachable across a loop as well -- and on the `Export.Name` leg the
20539
- * cost is a DROPPED write rather than a late one, since that block's
20540
- * `nameSecrets` is a per-iteration local. What bounds it is that only real
20723
+ * reachable across a loop as well: a part such a drain stops waiting for
20724
+ * records LATE. Late, not lost -- the engine's `Export.Name` block writes each
20725
+ * recording through to the pass map (it used to copy a per-iteration local,
20726
+ * which DROPPED a late one), and the outputs readers after the pass redact
20727
+ * against the pass map at the moment they read -- but a reader that already
20728
+ * took its copy does not see it, and the resolver warns when a drain stops
20729
+ * waiting. What bounds it is that only real
20541
20730
  * waiting spends the budget: reaching zero takes a full cap of drain wait
20542
20731
  * inside one pass, not merely a slow pass. It is taken deliberately -- an
20543
20732
  * unbounded hold on a deploy's state save costs a first deploy every
@@ -21297,6 +21486,61 @@ function configBooleanRefusal(container, key, containerPath) {
21297
21486
  if (coerceCfnBoolean(value) !== void 0) return void 0;
21298
21487
  return `${containerPath}.${key} must be a boolean ${malformedShapeDetail(value)}`;
21299
21488
  }
21489
+ /**
21490
+ * Coerce a CFn integer, which may arrive as the string `"32"`.
21491
+ *
21492
+ * Same reason as {@link coerceCfnBoolean}: CloudFormation is stringly typed, so
21493
+ * an imported or hand-written template legitimately spells `PasswordLength:
21494
+ * "32"`. A safe whole number, or a string that is a DECIMAL INTEGER LITERAL
21495
+ * (optional sign, then digits, nothing else), is the value; anything else —
21496
+ * `null`, `''`, `'abc'`, `3.5`, `'1e2'`, `'0x10'`, `' 32 '`, an object,
21497
+ * an unresolved intrinsic — is `undefined`, so a caller that indexed
21498
+ * `(x as number) || 32` cannot read a malformed member as its default. The
21499
+ * string grammar is exactly what CloudFormation's own validator accepts,
21500
+ * MEASURED (us-east-1, 2026-09-13, `AWS::SecretsManager::Secret
21501
+ * GenerateSecretString.PasswordLength`): `"+12"` and `"007"` pass, `" 12 "` fails
21502
+ * `expected type: Integer, found: String`, `"1e1"` fails `found: Float` —
21503
+ * so a padded string is refused HERE rather than minted from a template the
21504
+ * service rejects, and a signed one is accepted. Both branches use
21505
+ * `Number.isSafeInteger`, so one value gets one verdict whatever its
21506
+ * spelling.
21507
+ *
21508
+ * @returns the integer, or `undefined` when the value is not one.
21509
+ */
21510
+ function coerceCfnInteger(value) {
21511
+ if (typeof value === "number") return Number.isSafeInteger(value) ? value : void 0;
21512
+ if (typeof value !== "string" || !/^[+-]?\d+$/.test(value)) return void 0;
21513
+ const n = Number(value);
21514
+ return Number.isSafeInteger(n) ? n : void 0;
21515
+ }
21516
+ /**
21517
+ * The integer twin of {@link configBooleanRefusal} (issue #3056): the same
21518
+ * CONTAINER-then-FIELD halves, the FIELD half being {@link coerceCfnInteger} —
21519
+ * the function the wire read calls. Exists for the member shape neither
21520
+ * sibling sees: `(config['PasswordLength'] as number) || 32` read a
21521
+ * malformed length as the default, and a non-numeric STRING (truthy) reached
21522
+ * `new Uint8Array('abc')` and minted an EMPTY password with no warning.
21523
+ *
21524
+ * @param min the smallest integer that is usable (a generated-password length
21525
+ * of `0` is a value, not a refusal, to `Number.isInteger`, and it mints an
21526
+ * empty secret); pass `1` for a count, `0` where zero is legitimate.
21527
+ * @param max the largest, when the API documents one — a value the service
21528
+ * would reject is refused HERE, before a local consumer spends the work
21529
+ * (`new Uint8Array(1e9)` allocates and spins before AWS ever sees the
21530
+ * length). Omit when the API states no cap.
21531
+ * @returns The refusal sentence (`<path> must be …`), or `undefined` when the
21532
+ * value is usable — including the ABSENT container / ABSENT key cases, which
21533
+ * legitimately take the caller's default.
21534
+ */
21535
+ function configIntegerRefusal(container, key, containerPath, min, max) {
21536
+ if (container === void 0 || container === null) return void 0;
21537
+ if (!isPlainObject$1(container) || isUnresolvedIntrinsicContainer(container)) return `${containerPath} must be an object ${malformedShapeDetail(container)}`;
21538
+ const value = container[key];
21539
+ if (value === void 0) return void 0;
21540
+ const n = coerceCfnInteger(value);
21541
+ if (n !== void 0 && n >= min && (max === void 0 || n <= max)) return void 0;
21542
+ return `${containerPath}.${key} must be an integer ${max === void 0 ? `>= ${min}` : `between ${min} and ${max}`} ${malformedShapeDetail(value)}`;
21543
+ }
21300
21544
  /** The FIELD half of {@link configStringRefusal}, shared with {@link requireConfigString}. */
21301
21545
  function configValueRefusal(value, fallback, path, options) {
21302
21546
  if (value === void 0) return void 0;
@@ -21373,9 +21617,13 @@ const NAMEABLE_INTRINSIC_KEYS = /* @__PURE__ */ new Set([
21373
21617
  * now throw on an intrinsic — `route53-provider.ts` (`HostedZoneConfig`),
21374
21618
  * `cloudfront-oai-provider.ts` (`CloudFrontOriginAccessIdentityConfig`),
21375
21619
  * `kinesis-provider.ts` (`StreamModeDetails`), `ecs-provider.ts`
21376
- * (`DeploymentController`), `secretsmanager-secret-provider.ts`
21377
- * (`GenerateSecretString`, via `generateSecretString`) and `codebuild-provider.ts`
21378
- * (reached from `update()` through `mapProperties`). Not a count, for the same
21620
+ * (`DeploymentController`) and `codebuild-provider.ts` (reached from `update()`
21621
+ * through `mapProperties`). `secretsmanager-secret-provider.ts` was on this
21622
+ * list until issue #3048 moved its update path to a SKIP (the container is
21623
+ * refused by `requireConfigObject` before `generateSecretString` reads it,
21624
+ * and since issue #3056 every MEMBER read inside it takes the same skip
21625
+ * through an `onUnusable` callback; `create()` still throws on both). Not a
21626
+ * count, for the same
21379
21627
  * reason as the near-copy list below: two successive reviews each found
21380
21628
  * another. Re-derive with
21381
21629
  * `grep -rn 'readConfigString(' src/provisioning/providers/`.
@@ -23033,6 +23281,13 @@ const CAP_EXPIRED = Symbol("drain-cap-expired");
23033
23281
  */
23034
23282
  const concurrentDrainCap = {};
23035
23283
  /**
23284
+ * The budgets whose drain has already reported abandoned inputs (issue
23285
+ * [#2814](https://github.com/go-to-k/cdkd/issues/2814)). A `WeakSet` so a
23286
+ * budget's entry dies with the budget, as the budget itself does with the
23287
+ * async context that opened it.
23288
+ */
23289
+ const abandonReported = /* @__PURE__ */ new WeakSet();
23290
+ /**
23036
23291
  * `Promise.all`'s RESULT and its choice of error, with `Promise.allSettled`'s
23037
23292
  * TIMING: every promise started here has settled before this returns, and
23038
23293
  * before it throws unless the cap below expires first (issue
@@ -23110,8 +23365,25 @@ const concurrentDrainCap = {};
23110
23365
  * require the "healthy work slower than 60 s" shape the sizing paragraph
23111
23366
  * describes; that shape is the cheapest way to reach it with no nesting and
23112
23367
  * no wrapped loop, not the floor.
23113
- * Residual: issue
23114
- * [#2814](https://github.com/go-to-k/cdkd/issues/2814).
23368
+ *
23369
+ * WHAT HAPPENS TO A PART THE CAP STOPS WAITING FOR (issue
23370
+ * [#2814](https://github.com/go-to-k/cdkd/issues/2814)). It keeps running, and
23371
+ * its recording still lands in the context's map whenever it arrives -- the
23372
+ * cap costs it ORDER, not the write. So the answer sits with the READERS: one
23373
+ * that runs after the recording arrives must see it, which `DeployEngine`'s
23374
+ * outputs pass arranges for its `Export.Name` block (a map that writes each
23375
+ * recording through to the pass map at once, where a local copied in a
23376
+ * `finally` used to drop it) and for the persisted outputs, the exports index
23377
+ * and the deploy summary (each redacting against the pass map as it stands
23378
+ * at the moment it is written). A reader that took its
23379
+ * copy BEFORE the recording arrived -- a failure message already printed, a
23380
+ * state save already sent -- cannot be helped without waiting, and the wait
23381
+ * is what the cap bounds. Cancelling the
23382
+ * part would not close that either: a cancelled part never records, so its
23383
+ * plaintext would be missing for EVERY reader rather than for the early ones.
23384
+ * What this helper adds is the report: releasing a rejection with inputs
23385
+ * still running calls `onAbandoned` with how many, at most once per budget,
23386
+ * and the resolver turns that into a warning.
23115
23387
  *
23116
23388
  * ONE REMAINING CONSEQUENCE, deliberate and pinned by a case rather than only
23117
23389
  * described: the earliest-in-time rule holds PER INVOCATION, not across
@@ -23129,11 +23401,16 @@ const concurrentDrainCap = {};
23129
23401
  * failure where the old race was arbitrary. Residual: issue
23130
23402
  * [#2805](https://github.com/go-to-k/cdkd/issues/2805).
23131
23403
  */
23132
- async function allSettledKeepingFirstRejection(promises) {
23404
+ async function allSettledKeepingFirstRejection(promises, onAbandoned) {
23133
23405
  let rejection;
23134
23406
  const shared = drainDeadlines.getStore();
23135
23407
  let armCap;
23136
- const guarded = promises.map((promise) => promise.catch((error) => {
23408
+ let settled = 0;
23409
+ const guarded = promises.map((promise) => promise.then((value) => {
23410
+ settled += 1;
23411
+ return value;
23412
+ }, (error) => {
23413
+ settled += 1;
23137
23414
  if (rejection === void 0) {
23138
23415
  rejection = { error };
23139
23416
  armCap?.();
@@ -23158,7 +23435,17 @@ async function allSettledKeepingFirstRejection(promises) {
23158
23435
  });
23159
23436
  try {
23160
23437
  const outcome = await Promise.race([Promise.all(guarded), capped]);
23161
- if (rejection !== void 0) throw rejection.error;
23438
+ if (rejection !== void 0) {
23439
+ const pending = promises.length - settled;
23440
+ if (pending > 0) {
23441
+ if (shared === void 0) onAbandoned(pending);
23442
+ else if (!abandonReported.has(shared)) {
23443
+ abandonReported.add(shared);
23444
+ onAbandoned(pending);
23445
+ }
23446
+ }
23447
+ throw rejection.error;
23448
+ }
23162
23449
  if (outcome === CAP_EXPIRED) throw markNonRetryable(/* @__PURE__ */ new Error("drain cap expired with no rejection recorded"));
23163
23450
  return outcome;
23164
23451
  } finally {
@@ -24126,6 +24413,17 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
24126
24413
  return conditions;
24127
24414
  }
24128
24415
  /**
24416
+ * Warn that a drain stopped waiting for `pending` inputs of a failed
24417
+ * resolution (issue [#2814](https://github.com/go-to-k/cdkd/issues/2814)).
24418
+ * The helper calls this at most once per budget. The text carries a count
24419
+ * and the cap, nothing the template or AWS supplied, so it needs no masking.
24420
+ */
24421
+ warnAbandonedParts(pending) {
24422
+ const capSeconds = (concurrentDrainCap.ms ?? DRAIN_AFTER_REJECTION_MS) / 1e3;
24423
+ const parts = pending === 1 ? "1 concurrent part was" : `${pending} concurrent parts were`;
24424
+ this.logger.warn(`A resolution failed while ${parts} still running, and cdkd stopped waiting because the wait after a failure (capped at ${capSeconds}s) was used up. A secret such a part resolves from now on is still recorded, but only what runs after it arrives can mask it: output already printed or saved is not revisited.`);
24425
+ }
24426
+ /**
24129
24427
  * Recursively resolve a value
24130
24428
  */
24131
24429
  async resolveValue(value, context) {
@@ -24133,7 +24431,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
24133
24431
  if (typeof value === "string" && value.includes("{{resolve:")) return await this.resolveDynamicReferences(value, context);
24134
24432
  return value;
24135
24433
  }
24136
- if (Array.isArray(value)) return (await allSettledKeepingFirstRejection(value.map((v) => this.resolveValue(v, context)))).filter((v) => v !== AWS_NO_VALUE);
24434
+ if (Array.isArray(value)) return (await allSettledKeepingFirstRejection(value.map((v) => this.resolveValue(v, context)), (pending) => this.warnAbandonedParts(pending))).filter((v) => v !== AWS_NO_VALUE);
24137
24435
  const obj = value;
24138
24436
  if ("Ref" in obj) return await this.resolveRef(obj["Ref"], context);
24139
24437
  if ("Fn::GetAtt" in obj) return await this.resolveGetAtt(obj["Fn::GetAtt"], context);
@@ -25040,7 +25338,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
25040
25338
  let result = (await allSettledKeepingFirstRejection(values.map(async (v) => {
25041
25339
  const resolved = await this.resolveValue(v, context);
25042
25340
  return String(resolved);
25043
- }))).join(delimiter);
25341
+ }), (pending) => this.warnAbandonedParts(pending))).join(delimiter);
25044
25342
  if (result.includes("{{resolve:")) result = await this.resolveDynamicReferences(result, context);
25045
25343
  this.logger.debug(`Resolved Fn::Join: ${this.maskSecretsForLog(result, context)}`);
25046
25344
  return result;
@@ -28005,7 +28303,7 @@ var CloudControlProvider = class {
28005
28303
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
28006
28304
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
28007
28305
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
28008
- const { ASGProvider } = await import("./asg-provider-rQ1BW281.js").then((n) => n.n);
28306
+ const { ASGProvider } = await import("./asg-provider-DD_xuWE3.js").then((n) => n.n);
28009
28307
  const asgProvider = new ASGProvider();
28010
28308
  return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
28011
28309
  }
@@ -37316,6 +37614,47 @@ var DeploymentEventsReader = class {
37316
37614
  */
37317
37615
  const EMPTY_SECRETS = /* @__PURE__ */ new Map();
37318
37616
  /**
37617
+ * A secrets map that keeps its OWN entries and also writes each one through
37618
+ * to `target` the moment it is recorded (issue
37619
+ * [#2814](https://github.com/go-to-k/cdkd/issues/2814)).
37620
+ *
37621
+ * The outputs pass resolves each intrinsic `Export.Name` through one of these.
37622
+ * Its own entries are what `exportNameSecretExposure` reads as "substituted
37623
+ * into THIS name", which must stay exact -- a view of the whole pass map
37624
+ * (`cdkd scrub`'s `SharedEntriesSecrets`) would report every secret the pass
37625
+ * has recorded as one the name holds, and refuse every alias. The write-through
37626
+ * is what a copy could not give: a part the drain cap stopped waiting for
37627
+ * records AFTER the name's block has ended, and when this was a local copied
37628
+ * into the pass map in a `finally`, such a record reached nothing.
37629
+ *
37630
+ * Only `set` writes through: it is the one operation the resolver performs on
37631
+ * a recording map (grepped -- no resolver path calls `delete` or `clear` on
37632
+ * one). `cdkd scrub`'s `SharedEntriesSecrets` overrides those too, and the
37633
+ * asymmetry is deliberate: that one is a VIEW holding no entries of its own,
37634
+ * while a `delete` here would leave the pass map carrying an entry this map
37635
+ * dropped -- one needle too many, which over-redacts rather than leaks.
37636
+ * The ENTRIES only, never the resolved pairs beside them
37637
+ * (issue #2485): those are keyed by map INSTANCE (`recordResolvedPair`), so
37638
+ * they stay on this one. A name never positions a leaf, and a value re-using
37639
+ * the same token records its own pair at the seam, so carrying them could add
37640
+ * nothing -- what it COULD do is mark a pair conflicting (an `ssm` reference
37641
+ * whose `Type` came back unclassifiable is never cached, so a name resolving
37642
+ * it re-asks AWS and can see a value that moved since the value pass) and
37643
+ * destroy the positioning the value pass had earned.
37644
+ */
37645
+ var ForwardingSecrets = class extends Map {
37646
+ target;
37647
+ constructor(target) {
37648
+ super();
37649
+ this.target = target;
37650
+ }
37651
+ set(plaintext, expression) {
37652
+ super.set(plaintext, expression);
37653
+ this.target?.set(plaintext, expression);
37654
+ return this;
37655
+ }
37656
+ };
37657
+ /**
37319
37658
  * Default per-resource warn threshold: warn the user when a single
37320
37659
  * resource has been in flight for 5 minutes. Most CC API resources
37321
37660
  * complete in under a minute; 5m is the agreed elbow.
@@ -37590,6 +37929,16 @@ var DeployEngine = class DeployEngine {
37590
37929
  */
37591
37930
  outputSecrets = /* @__PURE__ */ new Map();
37592
37931
  /**
37932
+ * The outputs pass's own recording map(s), one per `resolveOutputs` call
37933
+ * this deploy, so every redaction of the outputs bag re-reads them rather
37934
+ * than trusting the copy taken when the pass ended (issue
37935
+ * [#2814](https://github.com/go-to-k/cdkd/issues/2814)): a part the drain
37936
+ * cap stopped waiting for records into its pass map LATE, and the final
37937
+ * save, the exports index and the deploy summary all run after that copy.
37938
+ * Reset per `deploy()`.
37939
+ */
37940
+ outputsPassSecretMaps = [];
37941
+ /**
37593
37942
  * UNRESOLVED template `Outputs` values, keyed by output name (issue #1910) —
37594
37943
  * the outputs' POSITION source, the sibling of `perResourceTemplateProps` for
37595
37944
  * the bag `resolveOutputs` produces. Without it two outputs resolving one
@@ -37718,6 +38067,7 @@ var DeployEngine = class DeployEngine {
37718
38067
  this.perResourceResolvedType = /* @__PURE__ */ new Map();
37719
38068
  this.attemptedResolvedProps = /* @__PURE__ */ new Map();
37720
38069
  this.outputSecrets = /* @__PURE__ */ new Map();
38070
+ this.outputsPassSecretMaps = [];
37721
38071
  this.outputsTemplateSource = Object.create(null);
37722
38072
  this.outputsSourceUsable = true;
37723
38073
  this.skippedOutputs = void 0;
@@ -37789,12 +38139,16 @@ var DeployEngine = class DeployEngine {
37789
38139
  * Redact the resolved stack OUTPUTS bag, positioned by the unresolved
37790
38140
  * template `Outputs` values (issue #1910).
37791
38141
  *
37792
- * A single entry point because THREE call sites redact this same bag — the
37793
- * state-persist choke point, the no-change re-check, and the post-deploy
37794
- * publish that feeds the exports index / deploy summary — and before this
37795
- * they each spelled the value-only redaction separately. Two outputs
37796
- * resolving one secret collapsed onto whichever expression was recorded last
37797
- * at all three.
38142
+ * A single entry point because SEVEN call sites redact this same bag — the
38143
+ * state-persist choke point, the no-change re-check, that path's exports
38144
+ * index and deploy summary, the changes path's index and summary, and the
38145
+ * outputs pass itself. THREE of those are the ones issue #1910 unified —
38146
+ * the persist choke point, the re-check, and the outputs pass, which the
38147
+ * list above ends on rather than opens with: before it they each spelled
38148
+ * the value-only redaction separately, and two outputs resolving one secret
38149
+ * collapsed onto whichever expression was recorded last at all three. The
38150
+ * other FOUR are issue #2814's, both paths' index and summary: each read
38151
+ * the bag unredacted until then, and now redacts at the moment it reads.
37798
38152
  */
37799
38153
  /**
37800
38154
  * Record the plaintext behind every output {@link redactOutputs} just masked,
@@ -37807,9 +38161,18 @@ var DeployEngine = class DeployEngine {
37807
38161
  * value read back out of a previous run's state — is skipped, because there
37808
38162
  * is no plaintext behind it to remember.
37809
38163
  *
37810
- * Called only from the REAL-DEPLOY outputs pass. The other two `redactOutputs`
37811
- * callers hand it a bag from a previous generation, where a mask is already
37812
- * unrecoverable and pretending otherwise would serve a stale value.
38164
+ * Called only from the REAL-DEPLOY outputs pass. Of the six other
38165
+ * `redactOutputs` callers, three CAN hand it a bag from a previous
38166
+ * generation — the persist walk always may, and the no-change path's
38167
+ * exports index and summary do on the arms where that path keeps the
38168
+ * previous bag — and there a mask is already unrecoverable, so pretending
38169
+ * otherwise would
38170
+ * serve a stale value. The other three are not this pass either: the
38171
+ * no-change path performs the FIRST redaction of its own resolution there,
38172
+ * and the changes path's index and summary re-redact the bag this pass
38173
+ * already produced. (The "other two" this replaces counted the base's three
38174
+ * callers correctly; what was wrong was calling both of the others
38175
+ * previous-generation, when only the persist walk was one.)
37813
38176
  */
37814
38177
  rememberRecoverableMaskedOutputs(stackName, resolved, redacted) {
37815
38178
  if (resolved === redacted) return;
@@ -37820,7 +38183,51 @@ var DeployEngine = class DeployEngine {
37820
38183
  recordRecoverableMaskedOutput(stackName, this.stackRegion, key, plaintext);
37821
38184
  }
37822
38185
  }
38186
+ /**
38187
+ * Fold every outputs-pass recording map into `outputSecrets` — the entries,
38188
+ * and the uncollapsed evidence beside them (issue #2485: without it a
38189
+ * literal Output embedding one of two same-plaintext references would lose
38190
+ * its span positioning and persist the sibling's expression). Only what the
38191
+ * outputs pass itself resolved, so the outputs redaction (GHSA fix) uses
38192
+ * only outputs-substituted references: a literal output equal to a secret
38193
+ * nothing resolved is not recorded, and is not touched.
38194
+ *
38195
+ * Run by every {@link redactOutputs} (issue
38196
+ * [#2814](https://github.com/go-to-k/cdkd/issues/2814)), because a part the
38197
+ * drain cap stopped waiting for records into its pass map LATE: a secret
38198
+ * that arrives between the pass and the final save is then still a needle
38199
+ * for the save, the exports index and the deploy summary. Repeating it is
38200
+ * safe for the ENTRIES — re-setting one the bag holds changes nothing, and
38201
+ * `Map.set` on an existing key keeps its insertion order, so last-wins
38202
+ * cannot be reordered by a refold.
38203
+ *
38204
+ * The PAIRS are where repeating is not merely a no-op, stated because the
38205
+ * end-of-pass copy could not see it: a part the cap stopped waiting for can
38206
+ * record a pair LATE, and a second value for one expression marks it
38207
+ * `CONFLICTING_PLAINTEXT` (a non-cacheable `{{resolve:ssm:X}}` re-resolved
38208
+ * to a moved value). A later refold then carries that conflict into
38209
+ * `outputSecrets`, the leaf loses its positioning and falls to the value
38210
+ * scan, which can persist a sibling's expression (the issue #2485 class).
38211
+ * The plaintext stays MASKED either way, so the cost is which expression
38212
+ * is stored, not a disclosure — and the alternative, dropping the late
38213
+ * pair, would keep a positioning the pass itself no longer vouches for.
38214
+ */
38215
+ absorbOutputsPassSecrets() {
38216
+ for (const passSecrets of this.outputsPassSecretMaps) {
38217
+ for (const [value, expr] of passSecrets) this.outputSecrets.set(value, expr);
38218
+ mergeResolvedPairs(passSecrets, this.outputSecrets);
38219
+ }
38220
+ }
38221
+ /**
38222
+ * Redact the outputs bag. NOT a pure transform: it folds the outputs pass
38223
+ * map into `outputSecrets` first (issue #2814), so every call can grow that
38224
+ * bag. Its KEY set only grows, and the bag has no other reader, which is
38225
+ * what makes calling this on every save, index write and summary safe. The
38226
+ * resolved PAIRS beside those keys are not equally free to refold — see
38227
+ * {@link absorbOutputsPassSecrets}, which states what a late one costs.
38228
+ */
37823
38229
  redactOutputs(outputs) {
38230
+ this.absorbOutputsPassSecrets();
37824
38231
  if (this.outputSecrets.size === 0) return outputs;
37825
38232
  return redactSecretsForState(outputs, this.outputSecrets, this.outputsSourceUsable ? this.outputsTemplateSource : void 0, TEMPLATE_SOURCED_RULES);
37826
38233
  }
@@ -38453,7 +38860,10 @@ var DeployEngine = class DeployEngine {
38453
38860
  persistedOutputs = refreshedState.outputs;
38454
38861
  if (outputsChanged) this.logger.info("Persisted Outputs-only change (no resource diff).");
38455
38862
  else this.logger.debug("Persisted export-set change (no outputs-value diff, no-change path, #2193)");
38456
- if (this.exportIndexStore) await this.exportIndexStore.updateForStack(stackName, this.stackRegion, importableOutputs(refreshedState));
38863
+ if (this.exportIndexStore) await this.exportIndexStore.updateForStack(stackName, this.stackRegion, importableOutputs({
38864
+ ...refreshedState,
38865
+ outputs: this.redactOutputs(refreshedState.outputs)
38866
+ }));
38457
38867
  } else if (observedRefresh) this.logger.debug("Persisted refreshed observedProperties (no-change path)");
38458
38868
  else this.logger.debug("Persisted skipped-outputs record (no outputs-value diff, no-change path, #2740)");
38459
38869
  } catch (saveError) {
@@ -38470,7 +38880,7 @@ var DeployEngine = class DeployEngine {
38470
38880
  updatePartial: 0,
38471
38881
  unchanged: Object.keys(currentState.resources).length,
38472
38882
  durationMs: Date.now() - startTime,
38473
- outputs: this.buildDisplayOutputs(template, persistedOutputs),
38883
+ outputs: this.buildDisplayOutputs(template, this.redactOutputs(persistedOutputs)),
38474
38884
  attributeFallbackCount: this.resolver.getPhysicalIdFallbackCount()
38475
38885
  };
38476
38886
  }
@@ -38501,7 +38911,10 @@ var DeployEngine = class DeployEngine {
38501
38911
  await this.drainObservedCaptures(newState.resources);
38502
38912
  const newEtag = await this.stateBackend.saveState(stackName, this.stackRegion, this.withParentInfo(newState));
38503
38913
  this.logger.debug(`State saved (ETag: ${newEtag})`);
38504
- await Promise.all([this.deleteRollbackJournalBestEffort(stackName), this.exportIndexStore ? this.exportIndexStore.updateForStack(stackName, this.stackRegion, importableOutputs(newState)) : Promise.resolve()]);
38914
+ await Promise.all([this.deleteRollbackJournalBestEffort(stackName), this.exportIndexStore ? this.exportIndexStore.updateForStack(stackName, this.stackRegion, importableOutputs({
38915
+ ...newState,
38916
+ outputs: this.redactOutputs(newState.outputs)
38917
+ })) : Promise.resolve()]);
38505
38918
  const durationMs = Date.now() - startTime;
38506
38919
  const unchangedCount = this.diffCalculator.filterByType(changes, "NO_CHANGE").length + actualCounts.skipped;
38507
38920
  return {
@@ -38513,7 +38926,7 @@ var DeployEngine = class DeployEngine {
38513
38926
  updatePartial: actualCounts.updatePartial,
38514
38927
  unchanged: unchangedCount,
38515
38928
  durationMs,
38516
- outputs: this.buildDisplayOutputs(template, newState.outputs ?? {}),
38929
+ outputs: this.buildDisplayOutputs(template, this.redactOutputs(newState.outputs ?? {})),
38517
38930
  attributeFallbackCount: this.resolver.getPhysicalIdFallbackCount()
38518
38931
  };
38519
38932
  } finally {
@@ -40125,8 +40538,10 @@ var DeployEngine = class DeployEngine {
40125
40538
  * defense in depth here — but two masking sites in one flow must not argue
40126
40539
  * opposite sides of the same question. `secrets` is the outputs pass's own
40127
40540
  * map: everything recorded before this handler runs, an `Export.Name`
40128
- * resolution's entries included (its `finally` merges them back before the
40129
- * `catch` reaches here). Since issue #2563 a still-pending concurrent part
40541
+ * resolution's entries included (its map writes each one through to the
40542
+ * pass map as the resolver records it, so they are here before the `catch`
40543
+ * is; issue #2814 replaced the `finally` that copied them at the end of
40544
+ * the block). Since issue #2563 a still-pending concurrent part
40130
40545
  * is in the PASS bag before this handler runs: the resolver drains every
40131
40546
  * part it started before a rejection reaches a caller. Not
40132
40547
  * unconditionally, and the weaker claim is the true one -- the drain is
@@ -40261,6 +40676,7 @@ var DeployEngine = class DeployEngine {
40261
40676
  };
40262
40677
  const outputsPassSecrets = context.recordedSecretValues ?? EMPTY_SECRETS;
40263
40678
  const outputsPassInherited = context.inheritedSecrets ?? EMPTY_SECRETS;
40679
+ if (context.recordedSecretValues) this.outputsPassSecretMaps.push(context.recordedSecretValues);
40264
40680
  const publishedOutputNames = collectPublishedOutputNames(template.Outputs, conditions);
40265
40681
  let outputsPassCompleted = false;
40266
40682
  try {
@@ -40286,20 +40702,16 @@ var DeployEngine = class DeployEngine {
40286
40702
  if (!output.Export?.Name) continue;
40287
40703
  const value = outputs[outputKey];
40288
40704
  if (value === void 0) continue;
40289
- const nameSecrets = /* @__PURE__ */ new Map();
40705
+ const nameSecrets = new ForwardingSecrets(context.recordedSecretValues);
40290
40706
  const nameReads = [];
40291
40707
  let exportName;
40292
40708
  try {
40293
- try {
40294
- exportName = typeof output.Export.Name === "string" ? output.Export.Name : await this.resolver.resolve(output.Export.Name, {
40295
- ...context,
40296
- recordedSecretValues: nameSecrets,
40297
- redactedAttributeReads: nameReads
40298
- });
40299
- refuseMaskedOutputReads(outputKey, nameReads);
40300
- } finally {
40301
- for (const [plaintext, expression] of nameSecrets) context.recordedSecretValues?.set(plaintext, expression);
40302
- }
40709
+ exportName = typeof output.Export.Name === "string" ? output.Export.Name : await this.resolver.resolve(output.Export.Name, {
40710
+ ...context,
40711
+ recordedSecretValues: nameSecrets,
40712
+ redactedAttributeReads: nameReads
40713
+ });
40714
+ refuseMaskedOutputReads(outputKey, nameReads);
40303
40715
  } catch (error) {
40304
40716
  this.handleOutputResolutionFailure(error, outputKey, outputs, outputsPassSecrets, outputsPassInherited);
40305
40717
  continue;
@@ -40316,10 +40728,6 @@ var DeployEngine = class DeployEngine {
40316
40728
  }
40317
40729
  outputsPassCompleted = true;
40318
40730
  } finally {
40319
- if (context.recordedSecretValues) {
40320
- for (const [value, expr] of context.recordedSecretValues) this.outputSecrets.set(value, expr);
40321
- mergeResolvedPairs(context.recordedSecretValues, this.outputSecrets);
40322
- }
40323
40731
  if (!outputsPassCompleted) this.outputsSourceUsable = false;
40324
40732
  }
40325
40733
  for (const [outputKey, output] of Object.entries(template.Outputs)) {
@@ -40341,5 +40749,5 @@ var DeployEngine = class DeployEngine {
40341
40749
  };
40342
40750
 
40343
40751
  //#endregion
40344
- export { collectInlinePolicyNamesManagedBySiblings as $, buildDockerImage as $n, IntrinsicResolutionRefusalError as $r, DagBuilder as $t, isStatefulRecreateTargetForReplace as A, importableOutputs as An, findLargeInlineResources as Ar, coerceCfnBoolean as At, gray as B, rewriteTemplateAssetReferences as Bn, clearBucketRegionCache as Br, withSharedDrainBudget as Bt, refusesFinalSnapshot as C, __exportAll as Ci, buildLockContentionMessage as Cn, resolveStateBucketWithDefaultAndSource as Cr, isUnboundTemplateParameter as Ct, makeSiblingClaimReader as D, DEFAULT_STATE_PREFIX as Dn, CFN_TEMPLATE_BODY_LIMIT as Dr, normalizeAwsTagsToCfn as Dt, extractDeploymentEventError as E, CUSTOM_RESOURCE_RESPONSE_PREFIX as En, warnDeprecatedNoPrefixCliFlag as Er, WAFv2WebACLProvider as Et, isWarmThroughputDecrease as F, stringifyValue as Fn, PARTITION_TABLE as Fr, requireConfigArray as Ft, collectPublishedOutputNames as G, getBootstrapMarkerKey as Gn, setAwsClients as Gr, s3BucketWebsiteUrl as Gt, red as H, BOOTSTRAP_MARKER_PREFIX as Hn, AwsClients as Hr, s3BucketDomainName as Ht, toFiniteNumber as I, WorkGraph as In, canonicalizeRegion as Ir, requireConfigObject as It, secretBearing as J, readBootstrapMarkerBody as Jn, ConfigError as Jr, INTRINSIC_KEYS as Jt, exportAliasCollisionScrubWarning as K, isCrossRegionRedirect as Kn, AssetError as Kr, applyRoleArnIfSet as Kt, formatResourceLine as L, buildAssetRedirectMap as Ln, derivePartitionAndUrlSuffix as Lr, requireConfigString as Lt, renderStatefulReason as M, orphansCarriedFrom as Mn, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Mr, configStringRefusal as Mt, WARM_THROUGHPUT_MEMBERS as N, shouldRetainResource as Nn, displaySafe as Nr, readConfigString as Nt, planOrphanAdoption as O, exportNamesCarriedFrom as On, CFN_TEMPLATE_URL_LIMIT as Or, resolveExplicitPhysicalId as Ot, coerceWarmThroughput as P, AssetPublisher as Pn, expectedOwnerParam as Pr, replayWarn as Pt, IAMRoleProvider as Q, describeAwsFailure as Qn, DynamicReferenceRegionAmbiguousError as Qr, withRetry as Qt, bold as R, createAssetRedirectResolver as Rn, AssemblyReader as Rr, classifyReplaySecretRegion as Rt, isFinalSnapshotError as S, retryClassificationText as Si, buildForceUnlockCommand as Sn, resolveStateBucketWithDefault as Sr, getAccountInfo as St, makeCanonicalizePropertiesFn as T, shellQuote as Tn, stateBucketExistenceConfirmed as Tr, refStateLookupFromResource as Tt, yellow as U, assertAssetBucketRegion as Un, getAwsClients as Ur, s3BucketDualStackDomainName as Ut, green as V, AssetModeResolver as Vn, resolveBucketRegion as Vr, s3BucketArn as Vt, collectDeclaredOutputNames as W, ensureAssetStorage as Wn, resetAwsClients as Wr, s3BucketRegionalDomainName as Wt, secretSafeKeyDisplay as X, validateContainerRepoName as Xn, DependencyError as Xr, findSilentDropProperties as Xt, secretBearingStateKeyWarning as Y, validateAssetBucketName as Yn, CrossAccountSecretRefusalError as Yr, findActionableSilentDrops as Yt, getCurrentResourceSecrets as Z, buildDenyExternalAccessPolicy as Zn, DeployCancelledError as Zr, describeTypeWithThrottleRetry as Zt, ATOMIC_FINAL_SNAPSHOT_TYPES as _, isRetryableTransientError as _i, scrubResourceRecord as _n, getLegacyStateBucketName as _r, isTerminationProtectionPropagationError as _t, DeploymentEventsStore as a, ProvisioningError as ai, carriesSecretMask as an, getDockerCmd as ar, maskerOrIdentity as at, ccRoutedFinalSnapshotError as b, markNonRetryable as bi, rebuildClientForBucketRegion as bn, resolveCaptureObservedState as br, cfnRefValueFromPhysicalId as bt, replayFailedOperations as c, StackHasActiveImportsError as ci, errorCauseChain as cn, runDockerForeground as cr, interruptWatchListenerCount as ct, updatePartialReason as d, SynthesisError as di, markSameGenerationBag as dn, stripControlChars as dr, CloudControlProvider as dt, LocalInvokeBuildError as ei, TemplateParser as en, describeDockerCapturedOutput as er, clearOnUpdateRemoval as et, withResourceDeadline as f, formatError as fi, maskSecretsInError as fn, AssetManifestLoader as fr, slowCcOperationTimeoutMs as ft, bindingSkippedOutputs as g, isMarkedNonRetryable as gi, redactSecretsForState as gn, getDefaultStateBucketName as gr, disableInstanceApiTermination as gt, computeImplicitDeleteEdges as h, withErrorHandling as hi, recoverMaskedOutput as hn, synthesisStatusMessage as hr, deleteSkipReason as ht, DeploymentEventsReader as i, PartialFailureError as ii, TEMPLATE_SOURCED_RULES as in, formatDockerLoginError as ir, maskDeep as it, isStatefulRecreateTargetSync as j, orphansAfterRollback as jn, uploadCfnTemplate as jr, configBooleanRefusal as jt, MULTI_REGION_RECREATE_BLOCKED_TYPES as k, importableOutputKeys as kn, MIGRATE_TMP_PREFIX as kr, assertRegionMatch as kt, replayRollback as l, StackTerminationProtectionError as li, identityKeyFor as ln, runDockerStreaming as lr, isInterruptedWaitError as lt, IMPLICIT_DELETE_DEPENDENCIES as m, normalizeAwsError as mi, recordMaskOnlyValue as mn, Synthesizer as mr, deleteIndeterminateGuards as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, LockError as ni, STATE_SOURCED_CROSS_GENERATION_RULES as nn, describeDockerFailure as nr, wouldReturnToSdkProvider as nt, planFailedOps as o, ResourceTimeoutError as oi, createSecretMasker as on, partitionSensitiveEnv as or, beginCommandInterruptScope as ot, maskingRetryLogger as p, isCdkdError as pi, maskSecretsInText as pn, getDockerImageBySourceHash as pr, UNSPECIFIED_SKIP_REASON as pt, isExportAliasCollision as q, parseBootstrapMarker as qn, CdkdError as qr, DiffCalculator as qt, DeployEngine as r, NestedStackChildDirectDestroyError as ri, STATE_SOURCED_READBACK_RULES as rn, dockerSpawnEnvWithSensitive as rr, createMaskedRetryLogger as rt, planRollback as s, ResourceUpdateNotSupportedError as si, dynamicReferenceTokens as sn, redactDockerArgvValues as sr, endCommandInterruptScope as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, LocalStartServiceError as ti, STATE_SOURCED_BASELINE_RULES as tn, describeDockerExecFailure as tr, ProviderRegistry as tt, updatePartialMessage as u, StateError as ui, isSingleDynamicReferenceToken as un, escapeRegExp$1 as ur, startInterruptWatch as ut, PRE_DELETE_SNAPSHOT_TYPES as v, isThrottlingError as vi, LockManager as vn, resolveApp as vr, IntrinsicFunctionResolver as vt, unsupportedFinalSnapshotError as w, forceQuitRecoveryClause as wn, resolveUseCdkBootstrapAssets as wr, parameterTypeMayLoseSecretIdentity as wt, createPreDeleteFinalSnapshot as x, markRedactedCause as xi, UNRENDERABLE as xn, resolveSkipPrefix as xr, coerceParameterTypedValue as xt, buildFinalSnapshotIdentifier as y, isTransientServerError as yi, S3StateBackend as yn, resolveAutoAssetStorage as yr, carriesDynamicReference as yt, cyan as z, loadPublishableAssetManifest as zn, processStackMessages as zr, producerRegionsFromState as zt };
40345
- //# sourceMappingURL=deploy-engine-DAykhfpV.js.map
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