@go-to-k/cdkd 0.283.30 → 0.283.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5560,6 +5560,27 @@ var AssetModeResolver = class {
5560
5560
  function escapeRegExp$1(value) {
5561
5561
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5562
5562
  }
5563
+ /**
5564
+ * Remove control characters from a value about to be PRINTED.
5565
+ *
5566
+ * Lives beside {@link escapeRegExp} for the reason that helper's own note
5567
+ * gives: this was spelled twice — in `src/cli/commands/diff-recursive.ts` and
5568
+ * in `src/deployment/outputs-export-alias.ts` — and copies of a
5569
+ * security-shaped helper drift. Both print the same class of string: a
5570
+ * resolved CloudFormation Output / `Export.Name`, which passed no CFn
5571
+ * validator, so it can carry ANSI escapes or bidi overrides straight into a
5572
+ * terminal or a CI log.
5573
+ *
5574
+ * C0 + DEL + C1 + the bidi marks and isolates. Apply on HUMAN-render paths
5575
+ * only: a `--json` payload is a machine interface where an export name is data
5576
+ * a consumer may match on, and mutating it there trades a correctness
5577
+ * regression for a display concern (see `stripDisplayOnlyChars`, which is the
5578
+ * narrower guard for an already-serialized payload and stays local to its
5579
+ * caller because its C0 exclusion is specific to that path).
5580
+ */
5581
+ function stripControlChars(value) {
5582
+ return value.replace(/[\u0000-\u001f\u007f-\u009f\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "");
5583
+ }
5563
5584
 
5564
5585
  //#endregion
5565
5586
  //#region src/assets/asset-redirect.ts
@@ -9925,40 +9946,88 @@ function buildNeedleRegex(values) {
9925
9946
  if (needles.length === 0) return void 0;
9926
9947
  return new RegExp(needles.map(escapeRegExp).join("|"), "g");
9927
9948
  }
9928
- /** The bag was produced by resolving the source: same shape, template source. */
9949
+ /**
9950
+ * The bag was produced by resolving the source: same shape, template source.
9951
+ *
9952
+ * `sourceIsSameGeneration` is FALSE despite the name, and that is the whole
9953
+ * lesson of issue #1917's review: the RULES describe the source, but the deploy
9954
+ * engine's persist choke point applies them to every record in the state map,
9955
+ * including ones this pass never rewrote. A template can never certify the
9956
+ * generation of the bag it is walked against.
9957
+ */
9929
9958
  const TEMPLATE_DERIVED_RULES = {
9930
9959
  descendArrays: true,
9931
- trustAnyExpression: false
9960
+ trustAnyExpression: false,
9961
+ sourceIsSameGeneration: false
9962
+ };
9963
+ /**
9964
+ * An AWS readback, or a persisted STATE bag, projected from the TEMPLATE: no
9965
+ * relaxation applies.
9966
+ *
9967
+ * One constant covers both bags because the template is what decides all three
9968
+ * rules here, and it decides them identically: shapes may diverge (no
9969
+ * positional descent), the template carries PUBLIC ssm expressions (no blanket
9970
+ * trust), and it is a different generation from anything it is walked against
9971
+ * (no source-takes-precedence on an expression-shaped leaf). An earlier draft
9972
+ * split this into a second `TEMPLATE_SOURCED_STATE_BAG_RULES` for `cdkd scrub`;
9973
+ * the two ended up byte-identical once the axis moved from CALLER to
9974
+ * GENERATION, and two identical constants are a drift hazard, not a
9975
+ * distinction.
9976
+ */
9977
+ const TEMPLATE_SOURCED_RULES = {
9978
+ descendArrays: false,
9979
+ trustAnyExpression: false,
9980
+ sourceIsSameGeneration: false
9932
9981
  };
9933
- /** An AWS readback projected from a persisted STATE bag. */
9982
+ /** An AWS readback projected from THIS record's own persisted STATE bag. */
9934
9983
  const STATE_SOURCED_READBACK_RULES = {
9935
9984
  descendArrays: false,
9936
- trustAnyExpression: true
9985
+ trustAnyExpression: true,
9986
+ sourceIsSameGeneration: true
9937
9987
  };
9938
- /** An AWS readback projected from the TEMPLATE: neither relaxation applies. */
9939
- const TEMPLATE_SOURCED_READBACK_RULES = {
9988
+ /**
9989
+ * A STATE source that is no longer this bag's own generation — `cdkd scrub`'s
9990
+ * `observedProperties` walk (issue #1917 review).
9991
+ *
9992
+ * It differs from {@link STATE_SOURCED_READBACK_RULES} on one flag, and the
9993
+ * difference is not cosmetic. Scrub positions `properties` against TODAY's
9994
+ * template BEFORE the observed bag is walked, so the "record's own properties"
9995
+ * that serve as the observed source may already carry an expression the stack
9996
+ * has never deployed. Taking that source for an observed leaf that ALREADY
9997
+ * holds an expression rewrites the drift baseline onto an undeployed
9998
+ * reference — which `cdkd drift --revert` then pushes to AWS.
9999
+ *
10000
+ * `trustAnyExpression` stays TRUE: the source is still a STATE bag, holding no
10001
+ * public expressions, and that relaxation is what lets scrub clean a legacy
10002
+ * PLAINTEXT observed leaf (issue #1900) rather than falling back to a value
10003
+ * scan that an old state file gives no needles for.
10004
+ */
10005
+ const STATE_SOURCED_CROSS_GENERATION_RULES = {
9940
10006
  descendArrays: false,
9941
- trustAnyExpression: false
10007
+ trustAnyExpression: true,
10008
+ sourceIsSameGeneration: false
9942
10009
  };
9943
10010
  /**
9944
- * The bag was produced by resolving a STATE source — both relaxations apply.
10011
+ * The bag was produced by resolving a STATE source — every relaxation applies.
9945
10012
  *
9946
10013
  * The rollback replay is the case (issue #1910): `resolveReplayProps` resolves
9947
10014
  * the JOURNALED bag and the provider's `effectiveProperties` come back from
9948
10015
  * that, so the two have identical structure and positional array descent is
9949
- * sound exactly as it is for a template-derived bag. And the source is a
9950
- * persisted record, which holds no PUBLIC expressions — a `String` ssm
9951
- * reference is stored resolved — so any `{{resolve:...}}` in it is by
9952
- * construction a secret.
10016
+ * sound exactly as it is for a template-derived bag. The source is a persisted
10017
+ * record, which holds no PUBLIC expressions — a `String` ssm reference is
10018
+ * stored resolved — so any `{{resolve:...}}` in it is by construction a secret.
10019
+ * And it is the SAME generation the bag was resolved from, one statement
10020
+ * earlier in the same call, which is as strong as that claim ever gets.
9953
10021
  *
9954
- * Using `STATE_SOURCED_READBACK_RULES` here instead would be wrong in the
9955
- * quiet direction: it turns array descent OFF, so a secret nested in a
9956
- * `Tags[]` / ECS `secrets[]` element falls to the value scan and a colliding
9957
- * pair still collapses there.
10022
+ * Using a `*_SOURCED_*` constant here instead would be wrong in the quiet
10023
+ * direction: it turns positional array descent OFF for a bag that genuinely
10024
+ * does correspond positionally, and drops the generation claim for the one
10025
+ * writer that can actually make it.
9958
10026
  */
9959
10027
  const STATE_DERIVED_RULES = {
9960
10028
  descendArrays: true,
9961
- trustAnyExpression: true
10029
+ trustAnyExpression: true,
10030
+ sourceIsSameGeneration: true
9962
10031
  };
9963
10032
  function isDynamicReferenceString(value) {
9964
10033
  return typeof value === "string" && value.includes("{{resolve:");
@@ -10252,6 +10321,103 @@ function positionByIntrinsicSkeleton(bag, source, secrets, secretExpressions) {
10252
10321
  return matched;
10253
10322
  }
10254
10323
  /**
10324
+ * Keys tried, in order, when pairing two arrays whose ORDER cannot be trusted
10325
+ * (issue #1915).
10326
+ *
10327
+ * Both are the identity field of an unordered CloudFormation list that really
10328
+ * does carry secrets: `Name` is the ECS `ContainerDefinitions[]` /
10329
+ * `Environment[]` / `Secrets[]` and CodeBuild `EnvironmentVariables[]` shape,
10330
+ * `Key` is the `Tags[]` shape the drift normalizer already keys on
10331
+ * (`canonicalizeTagListsDeep` in `src/analyzer/drift-normalize.ts`). The list
10332
+ * is deliberately SHORT: an entry is only useful when it is a genuine identity,
10333
+ * and a wrong entry costs a refused pairing rather than a wrong one (see
10334
+ * {@link identityKeyFor}), so widening it buys little and has to be justified
10335
+ * per shape.
10336
+ *
10337
+ * Worth naming rather than leaving to be rediscovered: `drift-normalize.ts`
10338
+ * deliberately moved its GENERAL case OFF heuristics like this one, onto
10339
+ * provider-DECLARED paths (`getDriftUnorderedPaths` + `matchesPathPrefix`),
10340
+ * after issue #1783 showed a heuristic claiming order-independence for a list
10341
+ * that was order-SIGNIFICANT (`KeySchema`). Only its `Tags[]` pass is still a
10342
+ * heuristic. This module diverges on purpose, because the two failure modes are
10343
+ * not comparable: there, a wrong claim SUPPRESSES real drift; here, the worst a
10344
+ * wrong pairing key can do is fail the uniqueness test and refuse, leaving the
10345
+ * leaf exactly where the value scan already had it. If that ever stops being
10346
+ * true — if a pairing gains the power to DELETE or REORDER rather than only to
10347
+ * rewrite a matched leaf — this list has to move to a declared seam too.
10348
+ */
10349
+ const ARRAY_IDENTITY_KEYS = ["Name", "Key"];
10350
+ /**
10351
+ * Is every element of `items` a plain object carrying a NON-EMPTY OWN string at
10352
+ * `key`, with no two elements sharing a value?
10353
+ *
10354
+ * `Object.hasOwn` for the same reason the object walk below uses it: without
10355
+ * it the prototype chain answers for a key like `constructor`, and while state
10356
+ * that came from `JSON.parse` cannot carry one, this module is called with
10357
+ * caller-constructed bags too and the two walks disagreeing is the kind of gap
10358
+ * that only shows up once something else changes.
10359
+ *
10360
+ * The empty string is excluded because it is not a distinguishing identity —
10361
+ * the same reason the value scan refuses it as a needle. Two elements both
10362
+ * carrying `Name: ''` would otherwise pair on "equality" that means nothing.
10363
+ */
10364
+ function isUniquelyKeyedBy(items, key) {
10365
+ if (items.length === 0) return false;
10366
+ const seen = /* @__PURE__ */ new Set();
10367
+ for (const item of items) {
10368
+ if (!isPlainObject$2(item) || !Object.hasOwn(item, key)) return false;
10369
+ const id = item[key];
10370
+ if (typeof id !== "string" || id === "") return false;
10371
+ if (seen.has(id)) return false;
10372
+ seen.add(id);
10373
+ }
10374
+ return true;
10375
+ }
10376
+ /**
10377
+ * Pick a key that identifies the elements of BOTH arrays, or `undefined` when
10378
+ * none does (issue #1915).
10379
+ *
10380
+ * This is what lets a leaf nested in an ARRAY be positioned on a path where
10381
+ * positional descent is refused. `descendArrays: false` is not a claim that the
10382
+ * bag and source elements are unrelated — it is a claim that their ORDER does
10383
+ * not correspond, because AWS does not preserve list order (the reason
10384
+ * `src/analyzer/drift-normalize.ts` exists). Matching by an identity FIELD
10385
+ * answers the order objection directly instead of working around it, so the
10386
+ * relaxation is sound on every rules constant and is deliberately NOT gated on
10387
+ * one.
10388
+ *
10389
+ * Requiring uniqueness WITHIN each array is what makes a pairing impossible to
10390
+ * get wrong: pairing is by string EQUALITY, so a bag element can only ever meet
10391
+ * the source element carrying the same identity, and uniqueness means there is
10392
+ * at most one of those. A key that is not really an identity (a repeated enum
10393
+ * value) fails the uniqueness test and refuses the whole pairing rather than
10394
+ * producing a plausible-looking wrong one. An element with no partner is not
10395
+ * guessed at either — it falls to the value scan, exactly as the whole array
10396
+ * did before.
10397
+ *
10398
+ * Keys are tried in {@link ARRAY_IDENTITY_KEYS} order and the FIRST one that
10399
+ * qualifies on both sides wins; a key that fails does not veto the next one.
10400
+ *
10401
+ * Two shapes this deliberately does NOT reach, stated so they are bounds rather
10402
+ * than surprises — both fail closed, leaving the leaf to the value scan:
10403
+ *
10404
+ * - **The identity field itself holds a secret.** The source element carries
10405
+ * `{Name: '{{resolve:...}}'}` and the bag element the resolved plaintext, so
10406
+ * the two never pair and the element's other leaves are not reached either.
10407
+ * Closing it would mean pairing on a field whose two sides are KNOWN to
10408
+ * differ, i.e. exactly the guessing the uniqueness rule exists to forbid. On
10409
+ * the unchanged-resource path the value scan is also a no-op, so such a leaf
10410
+ * keeps its plaintext — narrow (an identity field is a name, not a
10411
+ * credential) but real.
10412
+ * - **Arrays of arrays.** `M: [[{Name, Value}]]` pairs nothing, because the
10413
+ * OUTER elements are arrays rather than plain objects and have no identity
10414
+ * field to key on. Positional descent into the outer list would reintroduce
10415
+ * the order assumption this function exists to avoid.
10416
+ */
10417
+ function identityKeyFor(bag, source) {
10418
+ for (const key of ARRAY_IDENTITY_KEYS) if (isUniquelyKeyedBy(bag, key) && isUniquelyKeyedBy(source, key)) return key;
10419
+ }
10420
+ /**
10255
10421
  * PATH-based redaction: walk `bag` alongside a SOURCE bag that still carries the
10256
10422
  * unresolved `{{resolve:...}}` expressions, and wherever the source leaf is such
10257
10423
  * a string, persist THAT string verbatim.
@@ -10284,16 +10450,41 @@ function positionByIntrinsicSkeleton(bag, source, secrets, secretExpressions) {
10284
10450
  * where neither is.
10285
10451
  */
10286
10452
  function redactByPath(bag, source, secrets, rules, secretExpressions) {
10287
- if (typeof bag === "string" && isSingleDynamicReferenceToken(bag)) return bag;
10288
10453
  if (isDynamicReferenceString(source) && typeof bag === "string") {
10289
- if (isSingleDynamicReferenceToken(source) && (rules.trustAnyExpression || isKnownSecretExpression(source, secretExpressions))) return source;
10454
+ if (isSingleDynamicReferenceToken(source) && (rules.trustAnyExpression || isKnownSecretExpression(source, secretExpressions))) {
10455
+ if (!rules.sourceIsSameGeneration && isSingleDynamicReferenceToken(bag)) return secrets.get(bag) ?? bag;
10456
+ return source;
10457
+ }
10290
10458
  return redactSecretsForState(bag, secrets);
10291
10459
  }
10292
10460
  if (typeof bag === "string" && isPlainObject$2(source)) {
10293
10461
  const positioned = positionByIntrinsicSkeleton(bag, source, secrets, secretExpressions);
10294
10462
  if (positioned !== void 0) return positioned;
10295
10463
  }
10296
- if (rules.descendArrays && Array.isArray(bag) && Array.isArray(source) && bag.length === source.length) return bag.map((item, i) => redactByPath(item, source[i], secrets, rules, secretExpressions));
10464
+ if (Array.isArray(bag) && Array.isArray(source)) {
10465
+ const key = identityKeyFor(bag, source);
10466
+ if (key !== void 0) {
10467
+ const sourceIndexById = /* @__PURE__ */ new Map();
10468
+ source.forEach((item, i) => {
10469
+ sourceIndexById.set(item[key], i);
10470
+ });
10471
+ let orderPreserved = true;
10472
+ const partnerIndex = bag.map((item, i) => {
10473
+ const j = sourceIndexById.get(item[key]);
10474
+ if (j === void 0) return -1;
10475
+ if (j !== i) orderPreserved = false;
10476
+ return j;
10477
+ });
10478
+ const positionalIsExact = rules.descendArrays && bag.length === source.length && orderPreserved;
10479
+ return bag.map((item, i) => {
10480
+ const j = partnerIndex[i];
10481
+ if (j >= 0) return redactByPath(item, source[j], secrets, rules, secretExpressions);
10482
+ if (positionalIsExact) return redactByPath(item, source[i], secrets, rules, secretExpressions);
10483
+ return redactSecretsForState(item, secrets);
10484
+ });
10485
+ }
10486
+ if (rules.descendArrays && bag.length === source.length) return bag.map((item, i) => redactByPath(item, source[i], secrets, rules, secretExpressions));
10487
+ }
10297
10488
  if (isPlainObject$2(bag) && isPlainObject$2(source)) {
10298
10489
  const out = {};
10299
10490
  for (const [k, v] of Object.entries(bag)) out[k] = Object.hasOwn(source, k) ? redactByPath(v, source[k], secrets, rules, secretExpressions) : redactSecretsForState(v, secrets);
@@ -10319,6 +10510,7 @@ function redactSecretsForState(bag, secrets, source, rules = TEMPLATE_DERIVED_RU
10319
10510
  if (typeof value === "string") {
10320
10511
  const whole = wholeValueExpr(value);
10321
10512
  if (whole !== void 0) return whole;
10513
+ if (isSingleDynamicReferenceToken(value)) return value;
10322
10514
  if (!regex) return value;
10323
10515
  regex.lastIndex = 0;
10324
10516
  if (!regex.test(value)) return value;
@@ -10342,13 +10534,23 @@ function redactSecretsForState(bag, secrets, source, rules = TEMPLATE_DERIVED_RU
10342
10534
  * changed, or the input by identity when there are no secrets — so callers can
10343
10535
  * detect a no-op cheaply. Shared by the deploy engine's save choke point and
10344
10536
  * the `cdkd scrub` command so both scrub the same three fields identically.
10345
- */
10346
- function scrubResourceRecord(record, secrets, sourceProperties) {
10537
+ *
10538
+ * `observedRules` overrides the rules the `observedProperties` walk would
10539
+ * otherwise DERIVE from whether a `sourceProperties` bag was supplied. Exactly
10540
+ * one caller needs it and the reason is not obvious, so it is a parameter
10541
+ * rather than another derivation: `cdkd scrub` has already repositioned
10542
+ * `properties` against TODAY's template by the time it calls this, so the
10543
+ * "record's own properties" this walk falls back to are no longer the same
10544
+ * GENERATION as the observed bag beside them (issue #1917 review). Left
10545
+ * unspecified, the derivation below is right for every other caller, whose
10546
+ * `properties` reach this function untouched.
10547
+ */
10548
+ function scrubResourceRecord(record, secrets, sourceProperties, observedRules) {
10347
10549
  if (secrets.size === 0 && sourceProperties === void 0 && !record.observedProperties) return record;
10348
10550
  const next = { ...record };
10349
10551
  next.properties = redactSecretsForState(record.properties, secrets, sourceProperties);
10350
10552
  if (record.attributes) next.attributes = redactSecretsForState(record.attributes, secrets);
10351
- if (record.observedProperties) next.observedProperties = redactSecretsForState(record.observedProperties, secrets, sourceProperties ?? next.properties, sourceProperties === void 0 ? STATE_SOURCED_READBACK_RULES : TEMPLATE_SOURCED_READBACK_RULES);
10553
+ if (record.observedProperties) next.observedProperties = redactSecretsForState(record.observedProperties, secrets, sourceProperties ?? next.properties, observedRules ?? (sourceProperties === void 0 ? STATE_SOURCED_READBACK_RULES : TEMPLATE_SOURCED_RULES));
10352
10554
  return next;
10353
10555
  }
10354
10556
  /**
@@ -14895,7 +15097,7 @@ var CloudControlProvider = class {
14895
15097
  if (context?.finalSnapshotIdentifier !== void 0) throw new ProvisioningError(`${logicalId} (${resourceType}) requires a final snapshot (DeletionPolicy: Snapshot), but the Cloud Control API delete route has no final-snapshot parameter. Re-run with --skip-final-snapshot after snapshotting manually, or retain the resource.`, resourceType, logicalId, physicalId);
14896
15098
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
14897
15099
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
14898
- const { ASGProvider } = await import("./asg-provider-BpVWOeU3.js").then((n) => n.n);
15100
+ const { ASGProvider } = await import("./asg-provider-CVoisQ0L.js").then((n) => n.n);
14899
15101
  return await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
14900
15102
  }
14901
15103
  const isProtectedEc2Instance = context?.removeProtection === true && resourceType === "AWS::EC2::Instance";
@@ -19950,6 +20152,262 @@ function collectInlinePolicyNamesManagedBySiblings(targetPhysicalId, context, at
19950
20152
  return result;
19951
20153
  }
19952
20154
 
20155
+ //#endregion
20156
+ //#region src/deployment/outputs-export-alias.ts
20157
+ /**
20158
+ * Does CloudFormation suppress this output on this deploy?
20159
+ *
20160
+ * CFn does not create an output whose `Condition` evaluates false, and
20161
+ * `resolveOutputs` mirrors that (issue #1028). Unknown condition names are
20162
+ * KEPT, matching `filterResourcesByCondition` on the resource side — a
20163
+ * condition cdkd could not evaluate must not silently delete an output.
20164
+ *
20165
+ * DEPLOY-SIDE ONLY. `cdkd scrub` deliberately does not use this: see
20166
+ * {@link collectDeclaredOutputNames}.
20167
+ */
20168
+ function isOutputSuppressedByCondition(output, conditions) {
20169
+ return output.Condition !== void 0 && conditions?.[output.Condition] === false;
20170
+ }
20171
+ /**
20172
+ * The output NAMES this deploy actually publishes — every declared output minus
20173
+ * the condition-suppressed ones.
20174
+ *
20175
+ * This is the set that owns keys in BOTH bags, and the reason the answer is
20176
+ * "published" rather than "declared": a suppressed output writes no value, so
20177
+ * it must not write a position source either, and its name is free for an
20178
+ * export alias to use. Reserving names for suppressed outputs would drop a
20179
+ * WORKING export the moment an unrelated condition went false.
20180
+ *
20181
+ * Sound at deploy time because these are the SAME condition values the deploy
20182
+ * itself acted on. Not sound in scrub — see {@link collectDeclaredOutputNames}.
20183
+ */
20184
+ function collectPublishedOutputNames(outputs, conditions) {
20185
+ const names = /* @__PURE__ */ new Set();
20186
+ for (const [name, output] of Object.entries(outputs)) if (!isOutputSuppressedByCondition(output, conditions)) names.add(name);
20187
+ return names;
20188
+ }
20189
+ /**
20190
+ * Every DECLARED output name, conditions ignored — the set `cdkd scrub` tests
20191
+ * collisions against.
20192
+ *
20193
+ * Scrub must be a SUPERSET here, and the asymmetry with the deploy engine is
20194
+ * forced by what scrub can know. Its condition values are re-evaluated
20195
+ * best-effort, from template defaults only (the command takes no
20196
+ * `--parameters`), and `evaluateConditions` assumes FALSE on any evaluation
20197
+ * failure. So "suppressed" is both easy to hit spuriously and impossible to
20198
+ * confirm against the deploy that actually wrote the state.
20199
+ *
20200
+ * The two error directions are not symmetric, which is what settles the rule:
20201
+ *
20202
+ * - Judging a colliding output suppressed when the DEPLOY published it (the
20203
+ * spurious-false case above) makes scrub miss the collision, write the
20204
+ * exporting output's expression over the colliding key, and persist a
20205
+ * reference naming a DIFFERENT secret — the #1919 corruption, produced by
20206
+ * the remediation command itself.
20207
+ * - Judging it published when the deploy suppressed it costs one spurious
20208
+ * warning and one key redacted by VALUE match instead of by position. State
20209
+ * exactly what that costs, since an earlier revision understated it: the
20210
+ * value map is keyed by PLAINTEXT, so when two DISTINCT secrets resolve to
20211
+ * one value it keeps only the last, and that key can be persisted holding a
20212
+ * reference naming the OTHER secret. It is a smaller blast radius than the
20213
+ * first case (one key, and only when two secrets coincide, versus every
20214
+ * collision) but it is the same KIND of error, not a mere loss of precision.
20215
+ *
20216
+ * A wrong reference beats a lost precision bound, so scrub over-approximates.
20217
+ */
20218
+ function collectDeclaredOutputNames(outputs) {
20219
+ return new Set(Object.keys(outputs));
20220
+ }
20221
+ /**
20222
+ * Would aliasing `exportName` land on a key another output owns?
20223
+ *
20224
+ * An output exporting under its OWN name is not a collision: the alias rewrites
20225
+ * the identical key with the identical value, and both bags then carry the same
20226
+ * source.
20227
+ *
20228
+ * Deliberately NOT extended to two outputs sharing one `Export.Name` with no
20229
+ * output of that name. Both bags stay consistent there (one iteration writes
20230
+ * both the value and its source), so it is not this issue's class — see
20231
+ * `docs/cross-stack-references.md`.
20232
+ */
20233
+ function isExportAliasCollision(exportName, outputKey, ownedOutputNames) {
20234
+ return exportName !== outputKey && ownedOutputNames.has(exportName);
20235
+ }
20236
+ /**
20237
+ * Mirrors `secret-redaction`'s own `MIN_NEEDLE_LENGTH`. Duplicated rather than
20238
+ * imported because the two bounds answer different questions and should be free
20239
+ * to diverge: that one bounds what may be REWRITTEN, this one what may be
20240
+ * REFUSED or REPORTED.
20241
+ */
20242
+ const MIN_SECRET_NEEDLE = 4;
20243
+ /**
20244
+ * Which recorded secrets are visible in `text`, or `undefined` when none is.
20245
+ *
20246
+ * ONE rule for both callers below, because they were inconsistent and the
20247
+ * inconsistency was a hole: the export-name check refused only an exact
20248
+ * whole-name match while the state-KEY scan did bounded containment over the
20249
+ * same kind of map — so `prod-<secret>-endpoint` was written as a state key by
20250
+ * the deploy and then reported by `cdkd scrub` as an UNREPAIRABLE leak. The tool
20251
+ * was creating the exact state it tells the user it cannot fix.
20252
+ *
20253
+ * A WHOLE-value match counts at any length; an EMBEDDED one only at or above
20254
+ * {@link MIN_SECRET_NEEDLE}. The bound is what makes containment safe to use for
20255
+ * a refusal at all — an unbounded scan over a degenerate one-character secret
20256
+ * matches almost every name and silently drops working exports — and its cost
20257
+ * is stated rather than hidden: a secret of three characters or fewer embedded
20258
+ * in a longer string is not seen. That value is indistinguishable from
20259
+ * coincidence, and the same bound already governs every substring redaction
20260
+ * cdkd performs.
20261
+ *
20262
+ * No empty-string case is needed at either caller: the resolver never records an
20263
+ * empty secret (`secret-redaction` says so — an empty needle would match every
20264
+ * leaf), and the length bound excludes it from the containment arm regardless.
20265
+ */
20266
+ function secretsPresentIn(text, secrets) {
20267
+ if (!secrets || secrets.size === 0) return void 0;
20268
+ const exposure = /* @__PURE__ */ new Map();
20269
+ for (const [plaintext, expression] of secrets) if (text === plaintext || plaintext.length >= MIN_SECRET_NEEDLE && text.includes(plaintext)) exposure.set(plaintext, expression);
20270
+ return exposure.size > 0 ? exposure : void 0;
20271
+ }
20272
+ /**
20273
+ * The secrets present in a resolved `Export.Name`, or `undefined` when it
20274
+ * carries none.
20275
+ *
20276
+ * An `Export.Name` may be an intrinsic (`Fn::Sub` / `Fn::Join`), and those
20277
+ * substitute dynamic references — so the resolved name can contain a resolved
20278
+ * secret. That name would become a state KEY, and every redaction pass walks
20279
+ * VALUES only, so the plaintext would land in `state.json` and be republished
20280
+ * into the exports index.
20281
+ *
20282
+ * TWO signals, and neither subsumes the other:
20283
+ *
20284
+ * - `substitutedIntoName` — the caller resolves the name with its OWN
20285
+ * `recordedSecretValues` map, so a non-empty map means the resolver
20286
+ * substituted a secret INTO THIS NAME. Exact, and the only arm that can see a
20287
+ * substituted secret SHORTER than the containment bound below.
20288
+ * - a bounded containment scan of `recordedThisPass` ({@link secretsPresentIn}),
20289
+ * which catches plaintext that arrived by any other route — a literal name, a
20290
+ * cache hit, an `Fn::Sub` variable echoing the value.
20291
+ *
20292
+ * An earlier revision had only the first, calling the scan unpromising because
20293
+ * an UNBOUNDED one is: a degenerate one-character recorded secret would make
20294
+ * every name containing that character "secret" and silently drop working
20295
+ * exports. The bound is what makes the scan safe, and without the scan the
20296
+ * deploy wrote `prod-<secret>-endpoint` as a state key that `cdkd scrub` then
20297
+ * reported as unrepairable.
20298
+ *
20299
+ * The containment arm applies with no order caveat any more: the deploy engine
20300
+ * resolves EVERY output value
20301
+ * before deciding any alias, so this arm sees the complete map whatever the
20302
+ * declaration order. (An earlier revision of this paragraph said catching that
20303
+ * required "resolving the whole template before deciding anything" and called
20304
+ * it impractical — the value pass already does exactly that, at no extra cost.
20305
+ * The residual is now only a secret first substituted by ANOTHER output's
20306
+ * `Export.Name`, since those resolve in the second pass, in declaration order.)
20307
+ *
20308
+ * No empty-string special case, in either map: the resolver never records an
20309
+ * empty secret (`secret-redaction.ts` states it — an empty needle would match
20310
+ * every leaf), so an `''` key cannot reach here, and a guard against it would
20311
+ * be a branch no test could ever distinguish.
20312
+ */
20313
+ function exportNameSecretExposure(exportName, substitutedIntoName, recordedThisPass) {
20314
+ const exposure = new Map(substitutedIntoName);
20315
+ for (const [plaintext, expression] of secretsPresentIn(exportName, recordedThisPass) ?? []) exposure.set(plaintext, expression);
20316
+ return exposure.size > 0 ? exposure : void 0;
20317
+ }
20318
+ /**
20319
+ * Secrets visible in a state KEY, or `undefined` when there are none.
20320
+ *
20321
+ * Unlike {@link exportNameSecretExposure} there is no resolution to attribute
20322
+ * this to: the key was written by an EARLIER binary, so containment is the only
20323
+ * available signal. It is therefore bounded the same way `secret-redaction`
20324
+ * bounds its own substring scan — a value shorter than {@link MIN_SECRET_NEEDLE}
20325
+ * is matched only as the WHOLE key — because an unbounded containment scan over
20326
+ * a degenerate short secret flags every key in the state and fails the
20327
+ * `--dry-run --fail` CI gate repo-wide, which is the availability failure the
20328
+ * export-name check was redesigned to avoid.
20329
+ *
20330
+ * Bound and cost are {@link secretsPresentIn}'s; see there.
20331
+ */
20332
+ function stateKeySecretExposure(key, secrets) {
20333
+ return secretsPresentIn(key, secrets);
20334
+ }
20335
+ /**
20336
+ * Replace EVERY occurrence of every exposed secret with the mask.
20337
+ *
20338
+ * Deliberately not `maskSecretsInText`: that helper skips needles shorter than
20339
+ * its minimum length, which is right when scanning arbitrary bags for
20340
+ * coincidental matches but wrong here, where the values are known to be IN this
20341
+ * string. Feeding a detected-but-unmaskable name to it printed the secret under
20342
+ * a "masked:" label — a message asserting a protection it had not performed.
20343
+ * Longest-first so a secret containing another is masked whole.
20344
+ */
20345
+ function maskEveryOccurrence(text, exposure) {
20346
+ let out = text;
20347
+ for (const value of Array.from(exposure.keys()).sort((a, b) => b.length - a.length)) out = out.split(value).join("***");
20348
+ return out;
20349
+ }
20350
+ /**
20351
+ * Warning for an `Export.Name` that resolved to something containing secret
20352
+ * plaintext. Refused rather than published: the name would be a state KEY, and
20353
+ * keys are never redacted.
20354
+ *
20355
+ * The name is shown MASKED, and omitted entirely if masking somehow left it
20356
+ * unchanged. stderr is a reader like any other, so the invariant is absolute: a
20357
+ * message must never claim a masking it did not perform.
20358
+ */
20359
+ function secretBearingExportNameWarning(outputKey, exportName, exposure) {
20360
+ const masked = stripControlChars(maskEveryOccurrence(exportName, exposure));
20361
+ const shown = masked === stripControlChars(exportName) ? "" : `(masked: "${masked}") `;
20362
+ return `Output ${stripControlChars(outputKey)} has an Export.Name that resolves to a value containing a secret ${shown}— skipping the export alias. An export name becomes a key in state.json and in the exports index, and redaction rewrites VALUES only, so publishing it would persist the secret in plaintext. Use a non-secret Export.Name.`;
20363
+ }
20364
+ /**
20365
+ * Warning for a state KEY that already holds secret plaintext — the residue an
20366
+ * EARLIER binary left when it published an export name that resolved to one.
20367
+ *
20368
+ * `cdkd scrub` cannot repair this. Every redaction pass rewrites VALUES; a key
20369
+ * is the export's identity, so renaming it here would silently retire an export
20370
+ * consumers resolve by name, and dropping it would delete a live export. The
20371
+ * remedy is in the template: give the output a non-secret `Export.Name` and
20372
+ * redeploy, which rewrites `state.outputs` wholesale and republishes the index.
20373
+ * Reported so the `--dry-run --fail` CI gate stops calling such a state clean.
20374
+ */
20375
+ function secretBearingStateKeyWarning(stackName, key, exposure) {
20376
+ return `State for ${stripControlChars(stackName)} holds an output KEY containing a secret (masked: "${stripControlChars(maskEveryOccurrence(key, exposure))}") — cdkd scrub cannot rewrite a key, only a value, because the key IS the export name consumers resolve by. Give that output a non-secret Export.Name and redeploy: the next deploy replaces state.outputs and the exports index entirely. ROTATE the exposed secret.`;
20377
+ }
20378
+ /**
20379
+ * Warning for an `Export.Name` colliding with an output NAME it does not own.
20380
+ *
20381
+ * Names both outputs because the two are equally likely to be the mistake, and
20382
+ * says which value survives — the export is skipped, so the key keeps the
20383
+ * output's own value.
20384
+ *
20385
+ * No masking here, and none is needed: the name printed is one that MATCHED a
20386
+ * declared output name, i.e. template text, and a name carrying a secret is
20387
+ * refused by {@link secretBearingExportNameWarning} before this is reached.
20388
+ */
20389
+ function exportAliasCollisionWarning(outputKey, exportName) {
20390
+ const shown = stripControlChars(exportName);
20391
+ const from = stripControlChars(outputKey);
20392
+ return `Output ${from} exports as "${shown}", which is also the name of another output in this stack — skipping the export alias, so output ${shown} keeps its own value and the export is not published. A consumer's Fn::ImportValue on "${shown}" therefore resolves to output ${shown}, NOT to ${from} (CloudFormation would publish both). Rename the export, or the colliding output.`;
20393
+ }
20394
+ /**
20395
+ * Warning for the same collision seen by `cdkd scrub`, whose remedy differs.
20396
+ *
20397
+ * Scrub does not resolve outputs — it redacts state written by an EARLIER
20398
+ * binary, where the alias may have won the colliding key — so it cannot claim
20399
+ * the key belongs to either output. It drops the position source for that key
20400
+ * and lets the value scan decide from the plaintext actually stored, which is
20401
+ * why this message promises something weaker than the deploy-time one. It can
20402
+ * also fire on a template the deploy handled cleanly, per
20403
+ * {@link collectDeclaredOutputNames}.
20404
+ */
20405
+ function exportAliasCollisionScrubWarning(outputKey, exportName, secrets) {
20406
+ const exposure = secretsPresentIn(exportName, secrets);
20407
+ const shown = stripControlChars(exposure ? maskEveryOccurrence(exportName, exposure) : exportName);
20408
+ return `Output ${stripControlChars(outputKey)} exports as "${shown}", which is also the name of another output in this stack — state cannot say which of the two the stored value under "${shown}" came from, so that key is redacted by value match instead of by template position, and two references resolving to the same value could still collapse there. Rename the export, or the colliding output, and redeploy.`;
20409
+ }
20410
+
19953
20411
  //#endregion
19954
20412
  //#region src/utils/colors.ts
19955
20413
  /**
@@ -21526,16 +21984,22 @@ async function resolveReplayProps(props, resolver, secrets) {
21526
21984
  * deploy reports a change that never converges — the same defect the four
21527
21985
  * deploy-side writers had, arriving here through the replay instead.
21528
21986
  *
21529
- * It takes `STATE_DERIVED_RULES`, which is BOTH relaxations. The source is a
21987
+ * It takes `STATE_DERIVED_RULES`, which is every relaxation. The source is a
21530
21988
  * persisted record, so it holds no PUBLIC expressions (a `String` ssm reference
21531
21989
  * is stored resolved) and any `{{resolve:...}}` in it is by construction a
21532
21990
  * secret — that is `trustAnyExpression`. And the bag WAS produced by resolving
21533
21991
  * that source (`resolveReplayProps` -> the provider's `effectiveProperties`),
21534
21992
  * so the two have identical structure and positional array descent is sound —
21535
- * that is `descendArrays`. Using `STATE_SOURCED_READBACK_RULES` here reads
21536
- * plausible and is wrong in the quiet direction: it turns array descent off, so
21537
- * a secret nested in a `Tags[]` / ECS `secrets[]` element would still collapse
21538
- * on the replay path.
21993
+ * that is `descendArrays`. It is also the SAME generation, resolved one
21994
+ * statement earlier in this call — that is `sourceIsSameGeneration`, and this
21995
+ * writer is one of only two that can honestly claim it.
21996
+ *
21997
+ * Using `STATE_SOURCED_READBACK_RULES` here reads plausible and is wrong in the
21998
+ * quiet direction: it turns positional array descent off. Since issue #1915 a
21999
+ * `Tags[]` / ECS `Environment[]` element is reached by KEYED descent either
22000
+ * way, so the concrete loss is narrower than it was — it is a list whose
22001
+ * elements carry no `Name` / `Key` identity, which only positional descent can
22002
+ * walk.
21539
22003
  *
21540
22004
  * No-op when the op resolved no secret.
21541
22005
  */
@@ -22107,7 +22571,7 @@ const FLUSH_INTERVAL_MS = 2e3;
22107
22571
  const FLUSH_EVENT_THRESHOLD = 50;
22108
22572
  /** Build-time cdkd version, with a dev fallback for non-built contexts. */
22109
22573
  function getCdkdVersion() {
22110
- return "0.283.30";
22574
+ return "0.283.32";
22111
22575
  }
22112
22576
  /**
22113
22577
  * Generate a time-sortable unique run id, e.g.
@@ -22748,6 +23212,14 @@ var DeployEngine = class {
22748
23212
  */
22749
23213
  outputsTemplateSource = {};
22750
23214
  /**
23215
+ * Whether {@link outputsTemplateSource} may be used to POSITION the outputs
23216
+ * redaction. False once an outputs pass threw partway: the post-loop
23217
+ * name pass never ran, so the bag holds only the alias keys written before
23218
+ * the throw — a partial source built from THIS template, while the bag the
23219
+ * failure path then redacts is the PREVIOUS deploy's. Reset per `deploy()`.
23220
+ */
23221
+ outputsSourceUsable = true;
23222
+ /**
22751
23223
  * Per-logical-id snapshot of the intrinsic-RESOLVED desired properties
22752
23224
  * each CREATE / UPDATE attempted (issue #1198). Written just before the
22753
23225
  * provider call; read only when the op FAILS, to journal the failed op's
@@ -22792,6 +23264,7 @@ var DeployEngine = class {
22792
23264
  this.perResourceTemplateProps = /* @__PURE__ */ new Map();
22793
23265
  this.outputSecrets = /* @__PURE__ */ new Map();
22794
23266
  this.outputsTemplateSource = {};
23267
+ this.outputsSourceUsable = true;
22795
23268
  this.resolver.resetPhysicalIdFallbackCount();
22796
23269
  return withStackName(stackName, () => this.doDeploy(stackName, template));
22797
23270
  }
@@ -22835,7 +23308,7 @@ var DeployEngine = class {
22835
23308
  */
22836
23309
  redactOutputs(outputs) {
22837
23310
  if (this.outputSecrets.size === 0) return outputs;
22838
- return redactSecretsForState(outputs, this.outputSecrets, this.outputsTemplateSource);
23311
+ return redactSecretsForState(outputs, this.outputSecrets, this.outputsSourceUsable ? this.outputsTemplateSource : void 0);
22839
23312
  }
22840
23313
  /**
22841
23314
  * Redact resolved secret plaintext out of rollback-journal operations (GHSA
@@ -24439,9 +24912,30 @@ var DeployEngine = class {
24439
24912
  });
24440
24913
  }
24441
24914
  /**
24442
- * Resolve stack outputs from template and resource attributes
24915
+ * What a failed Output resolution does, shared by both passes of
24916
+ * {@link resolveOutputs} so they cannot drift — the alias pass reports the
24917
+ * SAME failure for a name it could not resolve as the value pass does for a
24918
+ * value, which is what the single-pass shape did when both lived in one
24919
+ * `try`.
24920
+ *
24921
+ * Issue #1111 item 2: under `--strict-getatt` an unresolvable Output fails
24922
+ * the deploy instead of silently publishing nothing (which breaks downstream
24923
+ * `Fn::ImportValue` consumers with "export not found" long after this deploy
24924
+ * exits 0).
24925
+ */
24926
+ handleOutputResolutionFailure(error, outputKey, outputs) {
24927
+ if (this.options.strictGetAtt) throw new Error(`Failed to resolve output ${outputKey}: ${error instanceof Error ? error.message : String(error)} (--strict-getatt promotes output resolution failures to deploy errors; drop the flag to warn and skip the output instead)`, { cause: error });
24928
+ this.logger.warn(`Failed to resolve output ${outputKey}: ${String(error)}`);
24929
+ outputs[outputKey] = void 0;
24930
+ }
24931
+ /**
24932
+ * Resolve stack outputs from template and resource attributes.
24443
24933
  *
24444
- * Uses IntrinsicFunctionResolver for full CloudFormation intrinsic function support.
24934
+ * Uses `IntrinsicFunctionResolver` for full CloudFormation intrinsic function
24935
+ * support, and runs in TWO passes — every value, then every export alias —
24936
+ * so an alias decision sees the complete set of secrets this pass resolved
24937
+ * rather than whatever the declaration order happened to have recorded by
24938
+ * then (issue #1919).
24445
24939
  */
24446
24940
  async resolveOutputs(template, resources, stackName, parameterValues, conditions) {
24447
24941
  if (!template.Outputs) return {};
@@ -24452,29 +24946,58 @@ var DeployEngine = class {
24452
24946
  ...parameterValues && { parameters: parameterValues },
24453
24947
  ...conditions && { conditions }
24454
24948
  }, stackName);
24455
- for (const [outputKey, output] of Object.entries(template.Outputs)) {
24456
- if (output.Condition !== void 0 && conditions?.[output.Condition] === false) {
24457
- this.logger.debug(`Skipping output ${outputKey} — condition ${output.Condition} is false`);
24458
- continue;
24949
+ const publishedOutputNames = collectPublishedOutputNames(template.Outputs, conditions);
24950
+ let outputsPassCompleted = false;
24951
+ try {
24952
+ for (const [outputKey, output] of Object.entries(template.Outputs)) {
24953
+ if (isOutputSuppressedByCondition(output, conditions)) {
24954
+ this.logger.debug(`Skipping output ${outputKey} — condition ${output.Condition} is false`);
24955
+ continue;
24956
+ }
24957
+ try {
24958
+ outputs[outputKey] = await this.resolver.resolve(output.Value, context);
24959
+ } catch (error) {
24960
+ this.handleOutputResolutionFailure(error, outputKey, outputs);
24961
+ }
24459
24962
  }
24460
- try {
24461
- const value = await this.resolver.resolve(output.Value, context);
24462
- outputs[outputKey] = value;
24463
- if (output.Export?.Name) {
24464
- const exportName = typeof output.Export.Name === "string" ? output.Export.Name : await this.resolver.resolve(output.Export.Name, context);
24465
- if (typeof exportName === "string") {
24466
- outputs[exportName] = value;
24467
- this.outputsTemplateSource[exportName] = output.Value;
24963
+ for (const [outputKey, output] of Object.entries(template.Outputs)) {
24964
+ if (isOutputSuppressedByCondition(output, conditions)) continue;
24965
+ if (!output.Export?.Name) continue;
24966
+ const value = outputs[outputKey];
24967
+ if (value === void 0) continue;
24968
+ const nameSecrets = /* @__PURE__ */ new Map();
24969
+ let exportName;
24970
+ try {
24971
+ try {
24972
+ exportName = typeof output.Export.Name === "string" ? output.Export.Name : await this.resolver.resolve(output.Export.Name, {
24973
+ ...context,
24974
+ recordedSecretValues: nameSecrets
24975
+ });
24976
+ } finally {
24977
+ for (const [plaintext, expression] of nameSecrets) context.recordedSecretValues?.set(plaintext, expression);
24468
24978
  }
24979
+ } catch (error) {
24980
+ this.handleOutputResolutionFailure(error, outputKey, outputs);
24981
+ continue;
24982
+ }
24983
+ if (typeof exportName !== "string") continue;
24984
+ const exposure = exportNameSecretExposure(exportName, nameSecrets, context.recordedSecretValues);
24985
+ if (exposure) this.logger.warn(secretBearingExportNameWarning(outputKey, exportName, exposure));
24986
+ else if (isExportAliasCollision(exportName, outputKey, publishedOutputNames)) this.logger.warn(exportAliasCollisionWarning(outputKey, exportName));
24987
+ else {
24988
+ outputs[exportName] = value;
24989
+ this.outputsTemplateSource[exportName] = output.Value;
24469
24990
  }
24470
- } catch (error) {
24471
- if (this.options.strictGetAtt) throw new Error(`Failed to resolve output ${outputKey}: ${error instanceof Error ? error.message : String(error)} (--strict-getatt promotes output resolution failures to deploy errors; drop the flag to warn and skip the output instead)`, { cause: error });
24472
- this.logger.warn(`Failed to resolve output ${outputKey}: ${String(error)}`);
24473
- outputs[outputKey] = void 0;
24474
24991
  }
24992
+ outputsPassCompleted = true;
24993
+ } finally {
24994
+ if (context.recordedSecretValues) for (const [value, expr] of context.recordedSecretValues) this.outputSecrets.set(value, expr);
24995
+ if (!outputsPassCompleted) this.outputsSourceUsable = false;
24996
+ }
24997
+ for (const [outputKey, output] of Object.entries(template.Outputs)) {
24998
+ if (!publishedOutputNames.has(outputKey)) continue;
24999
+ this.outputsTemplateSource[outputKey] = output.Value;
24475
25000
  }
24476
- if (context.recordedSecretValues) for (const [value, expr] of context.recordedSecretValues) this.outputSecrets.set(value, expr);
24477
- for (const [outputKey, output] of Object.entries(template.Outputs)) this.outputsTemplateSource[outputKey] = output.Value;
24478
25001
  return outputs;
24479
25002
  }
24480
25003
  buildDisplayOutputs(template, resolvedOutputs) {
@@ -24489,5 +25012,5 @@ var DeployEngine = class {
24489
25012
  };
24490
25013
 
24491
25014
  //#endregion
24492
- export { coerceCfnBoolean as $, resolveApp as $t, cyan as A, LocalInvokeBuildError as An, createAssetRedirectResolver as At, findSilentDropProperties as B, StackHasActiveImportsError as Bn, validateContainerRepoName as Bt, makeCanonicalizePropertiesFn as C, resetAwsClients as Cn, S3StateBackend as Ct, renderStatefulReason as D, ConfigError as Dn, stringifyValue as Dt, isStatefulRecreateTargetSync as E, CdkdError as En, AssetPublisher as Et, IAMRoleProvider as F, NestedStackChildDirectDestroyError as Fn, BOOTSTRAP_MARKER_PREFIX as Ft, IntrinsicFunctionResolver as G, isCdkdError as Gn, runDockerForeground as Gt, slowCcOperationTimeoutMs as H, StateError as Hn, buildDockerImage as Ht, collectInlinePolicyNamesManagedBySiblings as I, PartialFailureError as In, ensureAssetStorage as It, refStateLookupFromResource as J, isMarkedNonRetryable as Jn, getDockerImageBySourceHash as Jt, cfnRefValueFromPhysicalId as K, normalizeAwsError as Kn, runDockerStreaming as Kt, clearOnUpdateRemoval as L, ProvisioningError as Ln, getBootstrapMarkerKey as Lt, green as M, LocalStartServiceError as Mn, rewriteTemplateAssetReferences as Mt, red as N, LockError as Nn, escapeRegExp$1 as Nt, formatResourceLine as O, DependencyError as On, WorkGraph as Ot, yellow as P, MissingCdkCliError as Pn, AssetModeResolver as Pt, assertRegionMatch as Q, __exportAll as Qn, getLegacyStateBucketName as Qt, ProviderRegistry as R, ResourceTimeoutError as Rn, parseBootstrapMarker as Rt, unsupportedFinalSnapshotError as S, getAwsClients as Sn, LockManager as St, MULTI_REGION_RECREATE_BLOCKED_TYPES as T, AssetError as Tn, shouldRetainResource as Tt, disableInstanceApiTermination as U, SynthesisError as Un, formatDockerLoginError as Ut, CloudControlProvider as V, StackTerminationProtectionError as Vn, buildDenyExternalAccessPolicy as Vt, isTerminationProtectionPropagationError as W, formatError as Wn, getDockerCmd as Wt, normalizeAwsTagsToCfn as X, isThrottlingError as Xn, synthesisStatusMessage as Xt, WAFv2WebACLProvider as Y, isRetryableTransientError as Yn, Synthesizer as Yt, resolveExplicitPhysicalId as Z, markNonRetryable as Zn, getDefaultStateBucketName as Zt, buildFinalSnapshotIdentifier as _, AssemblyReader as _n, INTRINSIC_KEYS as _t, DeploymentEventsStore as a, resolveUseCdkBootstrapAssets as an, requireConfigObject as at, isFinalSnapshotError as b, resolveBucketRegion as bn, DagBuilder as bt, replayFailedOperations as c, CFN_TEMPLATE_BODY_LIMIT as cn, redactSecretsForState as ct, deleteSkipReason as d, findLargeInlineResources as dn, s3BucketDomainName as dt, resolveAutoAssetStorage as en, configBooleanRefusal as et, withResourceDeadline as f, uploadCfnTemplate as fn, s3BucketDualStackDomainName as ft, PRE_DELETE_SNAPSHOT_TYPES as g, derivePartitionAndUrlSuffix as gn, DiffCalculator as gt, ATOMIC_FINAL_SNAPSHOT_TYPES as h, canonicalizeRegion as hn, applyRoleArnIfSet as ht, DeploymentEventsReader as i, resolveStateBucketWithDefaultAndSource as in, requireConfigArray as it, gray as j, LocalMigrateError as jn, loadPublishableAssetManifest as jt, bold as k, DeployCancelledError as kn, buildAssetRedirectMap as kt, replayRollback as l, CFN_TEMPLATE_URL_LIMIT as ln, scrubResourceRecord as lt, computeImplicitDeleteEdges as m, PARTITION_TABLE as mn, s3BucketWebsiteUrl as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, resolveSkipPrefix as nn, readConfigString as nt, planFailedOps as o, stateBucketExistenceConfirmed as on, requireConfigString as ot, IMPLICIT_DELETE_DEPENDENCIES as p, expectedOwnerParam as pn, s3BucketRegionalDomainName as pt, getAccountInfo as q, withErrorHandling as qn, AssetManifestLoader as qt, DeployEngine as r, resolveStateBucketWithDefault as rn, replayWarn as rt, planRollback as s, warnDeprecatedNoPrefixCliFlag as sn, TEMPLATE_SOURCED_READBACK_RULES as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, resolveCaptureObservedState as tn, configStringRefusal as tt, UNSPECIFIED_SKIP_REASON as u, MIGRATE_TMP_PREFIX as un, s3BucketArn as ut, ccRoutedFinalSnapshotError as v, processStackMessages as vn, describeTypeWithThrottleRetry as vt, extractDeploymentEventError as w, setAwsClients as wn, rebuildClientForBucketRegion as wt, refusesFinalSnapshot as x, AwsClients as xn, TemplateParser as xt, createPreDeleteFinalSnapshot as y, clearBucketRegionCache as yn, withRetry as yt, findActionableSilentDrops as z, ResourceUpdateNotSupportedError as zn, validateAssetBucketName as zt };
24493
- //# sourceMappingURL=deploy-engine-Bb4ECtz3.js.map
25015
+ export { getAccountInfo as $, isCdkdError as $n, runDockerForeground as $t, cyan as A, getAwsClients as An, S3StateBackend as At, stateKeySecretExposure as B, LocalStartServiceError as Bn, escapeRegExp$1 as Bt, makeCanonicalizePropertiesFn as C, canonicalizeRegion as Cn, DiffCalculator as Ct, renderStatefulReason as D, clearBucketRegionCache as Dn, DagBuilder as Dt, isStatefulRecreateTargetSync as E, processStackMessages as En, withRetry as Et, collectDeclaredOutputNames as F, ConfigError as Fn, WorkGraph as Ft, findActionableSilentDrops as G, ProvisioningError as Gn, getBootstrapMarkerKey as Gt, collectInlinePolicyNamesManagedBySiblings as H, MissingCdkCliError as Hn, AssetModeResolver as Ht, collectPublishedOutputNames as I, DependencyError as In, buildAssetRedirectMap as It, slowCcOperationTimeoutMs as J, StackHasActiveImportsError as Jn, validateContainerRepoName as Jt, findSilentDropProperties as K, ResourceTimeoutError as Kn, parseBootstrapMarker as Kt, exportAliasCollisionScrubWarning as L, DeployCancelledError as Ln, createAssetRedirectResolver as Lt, green as M, setAwsClients as Mn, shouldRetainResource as Mt, red as N, AssetError as Nn, AssetPublisher as Nt, formatResourceLine as O, resolveBucketRegion as On, TemplateParser as Ot, yellow as P, CdkdError as Pn, stringifyValue as Pt, cfnRefValueFromPhysicalId as Q, formatError as Qn, getDockerCmd as Qt, isExportAliasCollision as R, LocalInvokeBuildError as Rn, loadPublishableAssetManifest as Rt, unsupportedFinalSnapshotError as S, PARTITION_TABLE as Sn, applyRoleArnIfSet as St, MULTI_REGION_RECREATE_BLOCKED_TYPES as T, AssemblyReader as Tn, describeTypeWithThrottleRetry as Tt, clearOnUpdateRemoval as U, NestedStackChildDirectDestroyError as Un, BOOTSTRAP_MARKER_PREFIX as Ut, IAMRoleProvider as V, LockError as Vn, stripControlChars as Vt, ProviderRegistry as W, PartialFailureError as Wn, ensureAssetStorage as Wt, isTerminationProtectionPropagationError as X, StateError as Xn, buildDockerImage as Xt, disableInstanceApiTermination as Y, StackTerminationProtectionError as Yn, buildDenyExternalAccessPolicy as Yt, IntrinsicFunctionResolver as Z, SynthesisError as Zn, formatDockerLoginError as Zt, buildFinalSnapshotIdentifier as _, CFN_TEMPLATE_URL_LIMIT as _n, s3BucketArn as _t, DeploymentEventsStore as a, getDefaultStateBucketName as an, markNonRetryable as ar, coerceCfnBoolean as at, isFinalSnapshotError as b, uploadCfnTemplate as bn, s3BucketRegionalDomainName as bt, replayFailedOperations as c, resolveAutoAssetStorage as cn, readConfigString as ct, deleteSkipReason as d, resolveStateBucketWithDefault as dn, requireConfigObject as dt, runDockerStreaming as en, normalizeAwsError as er, refStateLookupFromResource as et, withResourceDeadline as f, resolveStateBucketWithDefaultAndSource as fn, requireConfigString as ft, PRE_DELETE_SNAPSHOT_TYPES as g, CFN_TEMPLATE_BODY_LIMIT as gn, scrubResourceRecord as gt, ATOMIC_FINAL_SNAPSHOT_TYPES as h, warnDeprecatedNoPrefixCliFlag as hn, redactSecretsForState as ht, DeploymentEventsReader as i, synthesisStatusMessage as in, isThrottlingError as ir, assertRegionMatch as it, gray as j, resetAwsClients as jn, rebuildClientForBucketRegion as jt, bold as k, AwsClients as kn, LockManager as kt, replayRollback as l, resolveCaptureObservedState as ln, replayWarn as lt, computeImplicitDeleteEdges as m, stateBucketExistenceConfirmed as mn, TEMPLATE_SOURCED_RULES as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, getDockerImageBySourceHash as nn, isMarkedNonRetryable as nr, normalizeAwsTagsToCfn as nt, planFailedOps as o, getLegacyStateBucketName as on, __exportAll as or, configBooleanRefusal as ot, IMPLICIT_DELETE_DEPENDENCIES as p, resolveUseCdkBootstrapAssets as pn, STATE_SOURCED_CROSS_GENERATION_RULES as pt, CloudControlProvider as q, ResourceUpdateNotSupportedError as qn, validateAssetBucketName as qt, DeployEngine as r, Synthesizer as rn, isRetryableTransientError as rr, resolveExplicitPhysicalId as rt, planRollback as s, resolveApp as sn, configStringRefusal as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, AssetManifestLoader as tn, withErrorHandling as tr, WAFv2WebACLProvider as tt, UNSPECIFIED_SKIP_REASON as u, resolveSkipPrefix as un, requireConfigArray as ut, ccRoutedFinalSnapshotError as v, MIGRATE_TMP_PREFIX as vn, s3BucketDomainName as vt, extractDeploymentEventError as w, derivePartitionAndUrlSuffix as wn, INTRINSIC_KEYS as wt, refusesFinalSnapshot as x, expectedOwnerParam as xn, s3BucketWebsiteUrl as xt, createPreDeleteFinalSnapshot as y, findLargeInlineResources as yn, s3BucketDualStackDomainName as yt, secretBearingStateKeyWarning as z, LocalMigrateError as zn, rewriteTemplateAssetReferences as zt };
25016
+ //# sourceMappingURL=deploy-engine-rYTNzst0.js.map