@go-to-k/cdkd 0.290.12 → 0.290.14

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
1
  import { C as displaySafe, S as displayIdent, 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, x as UNRENDERABLE, y as withoutGeneratedFallbackName } from "./logger-C17H06QA.js";
2
2
  import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
3
- import { t as getCdkdVersion } from "./version-CEPJTaHl.js";
3
+ import { t as getCdkdVersion } from "./version-CqnAWzcc.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";
@@ -804,6 +804,96 @@ function isNameCollisionError(message) {
804
804
  return /(?<!\b(?:must|not|should|may|cannot)\s)already exists?\b/i.test(message) || message.includes("AlreadyExists");
805
805
  }
806
806
  /**
807
+ * SDK exception NAMES that mean, on their own, "a resource with this NAME
808
+ * already exists" — nothing else (issue
809
+ * [#3208](https://github.com/go-to-k/cdkd/issues/3208)).
810
+ *
811
+ * This list is the NARROW exception to the paragraph above, not a reversal of
812
+ * it. That paragraph refuses name-based classification because a name like
813
+ * `ResourceConflictException` is AMBIGUOUS — Lambda raises it for a function in
814
+ * a PENDING state too, so crediting it would delete a live function under
815
+ * `--replace`. Every name here is the opposite: the service declares it for the
816
+ * duplicate-name condition and for nothing else, so there is no second reading
817
+ * to be wrong about. A name may join this list only on that test.
818
+ *
819
+ * It exists because ELBv2 states the condition in prose that the message
820
+ * matcher above cannot see, and must not be widened to see. MEASURED against
821
+ * the real API (us-east-1, 2026-09-16, `@aws-sdk/client-elastic-load-balancing-v2`
822
+ * 3.1126.0), creating a second target group under a live name:
823
+ *
824
+ * name = 'DuplicateTargetGroupNameException'
825
+ * message = "A target group with the same name 'x' exists, but with
826
+ * different settings"
827
+ * cause = none
828
+ *
829
+ * The message carries no code and never says "already exists", so
830
+ * `isNameCollisionError` misses it — and the consequence is not cosmetic: the
831
+ * `--replace` delete-first fallback and the rollback executor's
832
+ * delete-new-first arm both gate on that predicate, so BOTH recovery paths went
833
+ * inert and a create-only change to a cdkd-named target group could not be
834
+ * deployed at all. Widening the prose matcher to a bare `exists` was rejected:
835
+ * it is substring-matched against every service's text, and the direction of a
836
+ * false positive here is a DELETE.
837
+ *
838
+ * The SET is complete rather than illustrative: `Duplicate*NameException` over
839
+ * every installed `@aws-sdk/client-*` model yields exactly these three, all
840
+ * ELBv2. Two ELBv2 siblings are deliberately EXCLUDED, and the reasons are the
841
+ * membership test in action — `DuplicateListenerException` reports a listener
842
+ * already bound to that PORT, which is not a name, and
843
+ * `DuplicateTagKeysException` reports repeated keys WITHIN one request, which
844
+ * is not an existence condition at all. Crediting either would hand a
845
+ * destructive path a collision that no delete can clear.
846
+ */
847
+ const NAME_COLLISION_ERROR_NAMES = /* @__PURE__ */ new Set([
848
+ "DuplicateTargetGroupNameException",
849
+ "DuplicateLoadBalancerNameException",
850
+ "DuplicateTrustStoreNameException"
851
+ ]);
852
+ /**
853
+ * {@link isNameCollisionError}, but reading the ERROR rather than a rendered
854
+ * message — which is the only way to see an exception NAME (issue #3208).
855
+ *
856
+ * Providers wrap an AWS failure as `Failed to create X: ${err.message}`, so the
857
+ * name is dropped before any caller could match it. This walks the bounded
858
+ * `cause` chain (the same {@link MAX_CAUSE_CHAIN_DEPTH} as every other
859
+ * classifier in this file), checking each link's `name` against
860
+ * {@link NAME_COLLISION_ERROR_NAMES} and each link's `message` through
861
+ * {@link isNameCollisionError}. A provider is required to thread the caught
862
+ * value as `cause` (`scripts/check-provider-error-cause.ts` enforces it across
863
+ * `src/provisioning/providers/**`), which is what makes the walk reach the SDK
864
+ * error at all.
865
+ *
866
+ * Prefer this at any site holding the error object. The string form stays for
867
+ * callers that genuinely have only text, and its behaviour is unchanged — this
868
+ * is strictly additive, so nothing that matched before stops matching.
869
+ */
870
+ /**
871
+ * `String(value)` that cannot itself throw — a thrown `Object.create(null)` has
872
+ * no `toString`. Preserves the call sites' pre-#3208 `String(createError)` arm
873
+ * for a non-`Error` throw, which reading `.message` alone would have dropped.
874
+ */
875
+ function stringifyForMatch(value) {
876
+ try {
877
+ return String(value);
878
+ } catch {
879
+ return "";
880
+ }
881
+ }
882
+ function isNameCollisionErrorFrom(error, logicalId) {
883
+ if (typeof error === "string") return isNameCollisionError(error);
884
+ let current = error;
885
+ for (let depth = 0; depth < MAX_CAUSE_CHAIN_DEPTH && current != null; depth++) {
886
+ const link = current;
887
+ if (typeof link.logicalId === "string" && link.logicalId !== logicalId) return false;
888
+ if (typeof link.name === "string" && NAME_COLLISION_ERROR_NAMES.has(link.name)) return true;
889
+ if (depth === 0) {
890
+ if (isNameCollisionError(error instanceof Error && typeof link.message === "string" ? link.message : stringifyForMatch(error))) return true;
891
+ }
892
+ current = link.cause;
893
+ }
894
+ return false;
895
+ }
896
+ /**
807
897
  * Match a same-name re-creation cooldown — an AWS service still holding a
808
898
  * resource's name while its asynchronous delete finishes. Every recognised
809
899
  * spelling lives in {@link NAME_COOLDOWN_ERROR_MESSAGE_PATTERNS}; add new ones
@@ -9133,6 +9223,53 @@ function shouldRetainResource(deletionPolicy) {
9133
9223
  return deletionPolicy === "Retain" || deletionPolicy === "RetainExceptOnCreate";
9134
9224
  }
9135
9225
  /**
9226
+ * Can this container be read as the record map its type claims it is?
9227
+ *
9228
+ * THE single plain-object test behind every malformed-bag guard in the repo.
9229
+ * `parseStateBody` validates the root object and the schema version and
9230
+ * nothing inside, so a hand-edited or truncated record reaches a consumer with
9231
+ * `resources` / `outputs` / `attributes` / `properties` holding a string, a
9232
+ * list, a number or `null`. `Object.entries` walks a string or a list as
9233
+ * readily as a map, so either FABRICATES entries. `in` and `Object.hasOwn`
9234
+ * behave differently per shape and neither is a guard: `in` THROWS on a
9235
+ * string, a number and `null`, but ANSWERS on a list (`0 in [1,2]` and
9236
+ * `'length' in [1,2]` are both `true`), while `Object.hasOwn` ANSWERS on a
9237
+ * string and a list alike (`Object.hasOwn('abcdef', '0')` is `true`) and
9238
+ * throws ONLY on `null` / `undefined` — the one pair a `?? {}` upstream has
9239
+ * usually already absorbed, which is what makes it look like a guard. Do not
9240
+ * read a nearby `in` as already catching the list case. Widened past
9241
+ * `null` / `undefined` for
9242
+ * exactly that reason: `[]`, `5` and `"ab"` all survive `Object.entries` and
9243
+ * yield a bag that is empty or, for the string, absurd.
9244
+ *
9245
+ * WHY IT LIVES HERE rather than beside the guards that grew out of it. Its
9246
+ * first home was `src/state/malformed-resources-bag.ts`, which still re-exports
9247
+ * it so no importer moved; it came down to this module when
9248
+ * {@link importableOutputKeys} needed it (go-to-k/cdkd#3192). That helper sits
9249
+ * in `src/types/**`, the layer every other one imports and which imports
9250
+ * nothing itself, so the alternative — `src/types/state.ts` importing from
9251
+ * `src/state/**` — inverts the layering AND pulls that module's
9252
+ * `error-handler` / `display-safe` / `lock-contention-message` chain into the
9253
+ * one module the whole codebase depends on. Same move, same reason, as
9254
+ * `DEFAULT_STATE_PREFIX` in `src/state/state-prefix.ts`: home the leaf in the
9255
+ * layer that needs it, re-export from where it was.
9256
+ *
9257
+ * Spelling this test a second time at any call site is what the single export
9258
+ * exists to prevent: the copies could then drift, and the one that drifted
9259
+ * would fabricate again while looking guarded. Enumerate the consumers with
9260
+ * `grep -rn "isReadableBag" src/` rather than from a list here — three
9261
+ * successive enumerations of them, written by reasoning, came out incomplete.
9262
+ *
9263
+ * What it does NOT decide is whether a given container is malformed. That is a
9264
+ * per-container call, because ABSENCE means different things: an absent
9265
+ * `resources` bag is a defect, while an absent `outputs` bag is an ordinary
9266
+ * record `cdkd scrub` round-trips on purpose. Each caller pairs this predicate
9267
+ * with its own absence rule.
9268
+ */
9269
+ function isReadableBag(container) {
9270
+ return typeof container === "object" && container !== null && !Array.isArray(container);
9271
+ }
9272
+ /**
9136
9273
  * The keys of `state.outputs` an `Fn::ImportValue` may bind to (issue
9137
9274
  * [#2193](https://github.com/go-to-k/cdkd/issues/2193)) — THE predicate
9138
9275
  * behind "what does this stack export". Four readers used to answer that
@@ -9150,15 +9287,79 @@ function shouldRetainResource(deletionPolicy) {
9150
9287
  *
9151
9288
  * `outputs` is typed required but every consumer treats it as optional (a
9152
9289
  * state file may simply have none), so it is read defensively here too.
9290
+ *
9291
+ * ## Two FAIL-CLOSED arms for a malformed record (go-to-k/cdkd#3192)
9292
+ *
9293
+ * Both answer `[]` — "this record exports nothing" — and neither repairs the
9294
+ * record or throws. The reasoning is the same for both and is a property of
9295
+ * WHERE this function sits: it is a pure predicate with no stack name, no
9296
+ * region and no writer of its own, reached from the exports-index rebuild, the
9297
+ * deploy-time resolver's state scan, `cdkd diff`'s no-change merge and the
9298
+ * local-command loader. Throwing here would be the bare `TypeError`
9299
+ * go-to-k/cdkd#3018 set out to remove, renamed; and refusing the whole
9300
+ * operation over ONE foreign record would take every OTHER stack's
9301
+ * `Fn::ImportValue` down with it. Publishing nothing from a record whose
9302
+ * export set cannot be read is the only answer that fabricates nothing and
9303
+ * destroys nothing — the damaged record is left exactly as it is, for the
9304
+ * write-capable commands (`cdkd orphan` / `import` / `scrub`) to REFUSE by
9305
+ * name.
9306
+ *
9307
+ * 1. `outputs` is not a readable bag. `Object.keys('abcdef')` is
9308
+ * `['0'...'5']`, so the pre-fix code published one fabricated export per
9309
+ * CHARACTER into `cdkd/_index/<region>/exports.json` — the shared index
9310
+ * every consumer's `Fn::ImportValue` binds against. {@link isReadableBag}
9311
+ * is the test, so an ABSENT bag keeps answering `[]` exactly as the old
9312
+ * `?? {}` did; no verdict moves on a healthy or an absent record.
9313
+ * 2. `exportNames` is present but not an array. `state.exportNames.filter(...)`
9314
+ * threw `TypeError: state.exportNames.filter is not a function` on a
9315
+ * hand-edited non-array or `null`, from `cdkd diff` among others. Falling
9316
+ * back to the `undefined` arm instead would be fail-OPEN in the one
9317
+ * direction #2193 exists to close — every plain Output name published as an
9318
+ * export — so a corrupt set is read as an EMPTY set, not as an unknown one.
9319
+ * A non-string element is dropped for the same reason: `Object.hasOwn` does
9320
+ * not throw on one, it COERCES, so `exportNames: [0]` would otherwise
9321
+ * publish the bag's `"0"` key. The filter is the identity on every healthy
9322
+ * `string[]`.
9153
9323
  */
9154
9324
  function importableOutputKeys(state) {
9155
- const outputs = state.outputs ?? {};
9325
+ if (!hasReadableExportSet(state)) return [];
9326
+ const outputs = state.outputs;
9156
9327
  if (state.exportNames === void 0) return Object.keys(outputs);
9157
- return state.exportNames.filter((name) => Object.hasOwn(outputs, name));
9328
+ return state.exportNames.filter((name) => typeof name === "string" && Object.hasOwn(outputs, name));
9329
+ }
9330
+ /**
9331
+ * Whether {@link importableOutputKeys} can answer from this record at all —
9332
+ * the two fail-closed arms of its own doc, as ONE predicate a caller can ask
9333
+ * (issue go-to-k/cdkd#3192).
9334
+ *
9335
+ * It exists because `[]` is a real answer: a stack that exports nothing and a
9336
+ * record whose export set could not be read produce the identical empty list,
9337
+ * and a caller that must SAY which one it got cannot re-derive the difference
9338
+ * from the list. `ExportIndexStore`'s rebuild is that caller — a silent
9339
+ * fail-closed there would leave the next `Fn::ImportValue` miss naming the
9340
+ * consumer rather than the damaged producer record.
9341
+ *
9342
+ * ONE spelling shared with {@link importableOutputKeys}, which calls this
9343
+ * rather than repeating the two tests: a second copy could drift, and the copy
9344
+ * that drifted would report a record readable while the key list stayed empty,
9345
+ * or the reverse.
9346
+ *
9347
+ * `outputs` absent answers FALSE here, unlike
9348
+ * `malformed-resources-bag.ts`'s `hasReadableOutputs`, and the difference is
9349
+ * the QUESTION rather than a disagreement: that one asks "is this record
9350
+ * damaged" (an absent bag is an ordinary record), this one asks "can an export
9351
+ * set be read off it" (it cannot). A caller that must tell absent from damaged
9352
+ * tests for absence first, as the exports-index rebuild does.
9353
+ */
9354
+ function hasReadableExportSet(state) {
9355
+ if (!isReadableBag(state.outputs)) return false;
9356
+ if (state.exportNames === void 0) return true;
9357
+ if (!Array.isArray(state.exportNames)) return false;
9358
+ return state.exportNames.length === 0 || state.exportNames.some((n) => typeof n === "string");
9158
9359
  }
9159
9360
  /** `state.outputs` narrowed to its {@link importableOutputKeys}. */
9160
9361
  function importableOutputs(state) {
9161
- const outputs = state.outputs ?? {};
9362
+ const outputs = isReadableBag(state.outputs) ? state.outputs : {};
9162
9363
  const picked = Object.create(null);
9163
9364
  for (const name of importableOutputKeys(state)) picked[name] = outputs[name];
9164
9365
  return picked;
@@ -24506,6 +24707,16 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
24506
24707
  * one.
24507
24708
  */
24508
24709
  producerRegionGuest = false;
24710
+ /**
24711
+ * How a producer-region guest PRINTS its own `explicitRegion` (issue
24712
+ * [#3150](https://github.com/go-to-k/cdkd/issues/3150)): the masked log text
24713
+ * its creator held for that region, which a template can assemble around a
24714
+ * short secret, or `***` once a later spelling of the same region masks
24715
+ * differently. Display only; `undefined` on an ordinary resolver, whose
24716
+ * region its command built (the stack's synthesized or recorded region,
24717
+ * `--region`, or a region read out of a literal token).
24718
+ */
24719
+ explicitRegionLogText;
24509
24720
  strictGetAtt;
24510
24721
  cfnFallback;
24511
24722
  /**
@@ -24757,18 +24968,19 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
24757
24968
  * uppercase spelling would build a second client for the same physical
24758
24969
  * region — benign, but wasteful and confusing in a debug log.
24759
24970
  */
24760
- clientsForRegion(targetRegion) {
24971
+ clientsForRegion(targetRegion, targetLogText) {
24761
24972
  const ambient = getAwsClients();
24762
24973
  if (!targetRegion) return ambient;
24763
24974
  const target = canonicalizeRegion(targetRegion);
24764
- if (!isClientSafeRegion(target)) throw new Error(`Refusing to build AWS clients for the region '${stripControlChars(target).slice(0, 64)}': it is not a valid AWS region name, and a region is substituted into the AWS service hostname.`);
24975
+ const loggedTarget = targetLogText ?? (targetRegion === this.explicitRegion ? this.explicitRegionLogText : void 0) ?? target;
24976
+ if (!isClientSafeRegion(target)) throw new Error(`Refusing to build AWS clients for the region '${stripControlChars(loggedTarget).slice(0, 64)}': it is not a valid AWS region name, and a region is substituted into the AWS service hostname.`);
24765
24977
  if (typeof ambient.withRegion !== "function") return ambient;
24766
24978
  const cached = this.regionScopedClients.get(target);
24767
24979
  if (cached) return cached;
24768
24980
  if (canonicalizeRegion(ambient.configuredRegion) === target) return ambient;
24769
24981
  const scoped = ambient.withRegion(target);
24770
24982
  this.regionScopedClients.set(target, scoped);
24771
- this.logger.debug(`Using region-scoped AWS clients for ${target}`);
24983
+ this.logger.debug(`Using region-scoped AWS clients for ${loggedTarget}`);
24772
24984
  return scoped;
24773
24985
  }
24774
24986
  /**
@@ -25358,7 +25570,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
25358
25570
  return this.noteAttributeSecrecy(logicalId, attributeName, nameServers, context);
25359
25571
  }
25360
25572
  this.logger.debug(`Resolved Fn::GetAtt from attributes: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, this.maskValueLeaves(flatValue, context)), context)}`);
25361
- if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX) && carriesDynamicReference(flatValue)) return await this.reresolveCrossStackValue(flatValue, nestedStackChildRegionFromLocalArn(resource.physicalId), context, `nested stack ${logicalId} ${attributeName}`, crossStackSourceKey({ "Fn::GetAtt": getAtt }));
25573
+ if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX) && carriesDynamicReference(flatValue)) return await this.reresolveCrossStackValue(flatValue, nestedStackChildRegionFromLocalArn(resource.physicalId), context, `nested stack ${logicalId} ${this.logTextOfLeaf(attributeName, context)}`, crossStackSourceKey({ "Fn::GetAtt": getAtt }));
25362
25574
  return this.noteAttributeSecrecy(logicalId, attributeName, flatValue, context);
25363
25575
  }
25364
25576
  if (attributeName.includes(".")) {
@@ -25376,8 +25588,9 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
25376
25588
  }
25377
25589
  }
25378
25590
  if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX)) {
25379
- const declared = Object.keys(resource.attributes ?? {}).filter((k) => k.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX)).map((k) => k.slice(8)).sort();
25380
- throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.maskSecretsForLog(attributeName, context)}]: the nested stack '${logicalId}' declares no output named '${this.maskSecretsForLog(attributeName.slice(8), context)}'. Its outputs are ${declared.length > 0 ? declared.join(", ") : "(none)"}. Check the output name in the nested stack's template, and deploy the child stack again if you have just added it.`));
25591
+ const declared = Object.keys(resource.attributes ?? {}).filter((k) => k.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX)).sort().map((k) => this.maskSecretsForLog(this.outputNameLogText(k, context), context));
25592
+ const declaredText = declared.length > 0 ? declared.join(", ") : "(none)";
25593
+ throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.maskSecretsForLog(attributeName, context)}]: the nested stack '${logicalId}' declares no output named '${this.maskSecretsForLog(this.outputNameLogText(attributeName, context), context)}'. Its outputs are ${this.maskSecretsForLog(declaredText, context)}. Check the output name in the nested stack's template, and deploy the child stack again if you have just added it.`));
25381
25594
  }
25382
25595
  const value = await this.constructGuardedAttribute(resource, attributeName, context, logicalId);
25383
25596
  this.logger.debug(`Resolved Fn::GetAtt: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, this.maskValueLeaves(value, context)), context)}`);
@@ -26664,7 +26877,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
26664
26877
  * the producer's rather than the caller's — which is exactly what issue #2057
26665
26878
  * says the other two copies get wrong.
26666
26879
  */
26667
- async reresolveCrossStackValue(value, producerRegion, context, origin, sourceKey, producerOutput) {
26880
+ async reresolveCrossStackValue(value, producerRegion, context, origin, sourceKey, producerOutput, producerRegionLogText) {
26668
26881
  if (carriesSecretMask(value)) {
26669
26882
  const recovered = producerOutput === void 0 || producerOutput.crossAccount === true ? void 0 : recoverMaskedOutput(producerOutput.stackName, producerOutput.region, producerOutput.outputKey);
26670
26883
  if (recovered !== void 0) {
@@ -26680,7 +26893,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
26680
26893
  }
26681
26894
  }
26682
26895
  if (!carriesDynamicReference(value)) return value;
26683
- const resolver = this.resolverForProducerRegion(producerRegion);
26896
+ const resolver = this.resolverForProducerRegion(producerRegion, context, producerRegionLogText);
26684
26897
  const pinnedContext = producerRegion ? withoutProducerRegions(context) : context;
26685
26898
  const walk = async (v) => {
26686
26899
  if (typeof v === "string") return v.includes("{{resolve:") ? await resolver.resolveDynamicReferences(v, pinnedContext) : v;
@@ -26761,19 +26974,25 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
26761
26974
  if (secret) recordedSecretExpressions.add(expression);
26762
26975
  else recordedSecretExpressions.delete(expression);
26763
26976
  }
26764
- resolverForProducerRegion(producerRegion) {
26977
+ resolverForProducerRegion(producerRegion, context, producerRegionLogText) {
26765
26978
  if (!producerRegion) return this;
26766
26979
  const target = canonicalizeRegion(producerRegion);
26767
26980
  if (target === canonicalizeRegion(this.explicitRegion)) return this;
26981
+ const regionText = producerRegionLogText !== void 0 ? canonicalizeRegion(producerRegionLogText) : this.regionLogText(producerRegion, context);
26982
+ const guestRegionText = this.maskThenStripThenMask(regionText, context);
26768
26983
  const cached = this.producerRegionResolvers.get(target);
26769
- if (cached) return cached;
26984
+ if (cached) {
26985
+ if (cached.explicitRegionLogText !== guestRegionText) cached.explicitRegionLogText = "***";
26986
+ return cached;
26987
+ }
26770
26988
  const scoped = new IntrinsicFunctionResolver(target, {
26771
26989
  strictGetAtt: this.strictGetAtt,
26772
26990
  cfnFallback: this.cfnFallback
26773
26991
  });
26774
26992
  scoped.producerRegionGuest = true;
26993
+ scoped.explicitRegionLogText = guestRegionText;
26775
26994
  this.producerRegionResolvers.set(target, scoped);
26776
- this.logger.debug(`Using a producer-region resolver for ${target}`);
26995
+ this.logger.debug(`Using a producer-region resolver for ${guestRegionText}`);
26777
26996
  return scoped;
26778
26997
  }
26779
26998
  /**
@@ -26798,8 +27017,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
26798
27017
  }
26799
27018
  if (entry && (!context.stackName || entry.producerStack !== context.stackName)) {
26800
27019
  this.recordImport(context, exportName, entry.producerStack, entry.producerRegion);
26801
- this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from index: ${this.maskSecretsForLog(entry.producerStack, context)} / ${entry.producerRegion}; ${carriesDynamicReference(entry.value) ? "redacted dynamic reference" : "literal value"})`);
26802
- return await this.reresolveCrossStackValue(entry.value, entry.producerRegion, context, `Fn::ImportValue '${exportName}' (producer ${entry.producerStack} / ${entry.producerRegion})`, sourceKey, {
27020
+ this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from index: ${this.maskSecretsForLog(entry.producerStack, context)} / ${this.maskSecretsForLog(entry.producerRegion, context)}; ${carriesDynamicReference(entry.value) ? "redacted dynamic reference" : "literal value"})`);
27021
+ return await this.reresolveCrossStackValue(entry.value, entry.producerRegion, context, `Fn::ImportValue '${this.logTextOfLeaf(exportName, context)}' (producer ${this.logTextOfLeaf(entry.producerStack, context)} / ${this.logTextOfLeaf(entry.producerRegion, context)})`, sourceKey, {
26803
27022
  stackName: entry.producerStack,
26804
27023
  region: entry.producerRegion,
26805
27024
  outputKey: exportName
@@ -26823,13 +27042,13 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
26823
27042
  }
26824
27043
  const stateData = await context.stateBackend.getState(refStack, lookupRegion);
26825
27044
  if (!stateData) {
26826
- this.logger.debug(`No state found for stack: ${this.maskSecretsForLog(refStack, context)} (${lookupRegion})`);
27045
+ this.logger.debug(`No state found for stack: ${this.maskSecretsForLog(refStack, context)} (${this.maskSecretsForLog(lookupRegion, context)})`);
26827
27046
  continue;
26828
27047
  }
26829
27048
  const { state } = stateData;
26830
27049
  if (importableOutputKeys(state).includes(exportName)) {
26831
27050
  const value = state.outputs[exportName];
26832
- this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from stack: ${this.maskSecretsForLog(refStack, context)} / ${lookupRegion}; ${carriesDynamicReference(value) ? "redacted dynamic reference" : "literal value"})`);
27051
+ this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from stack: ${this.maskSecretsForLog(refStack, context)} / ${this.maskSecretsForLog(lookupRegion, context)}; ${carriesDynamicReference(value) ? "redacted dynamic reference" : "literal value"})`);
26833
27052
  if (context.exportIndex) context.exportIndex.patchEntry(exportName, {
26834
27053
  value,
26835
27054
  producerStack: refStack,
@@ -26850,7 +27069,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
26850
27069
  continue;
26851
27070
  }
26852
27071
  }
26853
- if (found) return await this.reresolveCrossStackValue(found.value, found.lookupRegion, context, `Fn::ImportValue '${exportName}' (producer ${found.refStack} / ${found.lookupRegion})`, sourceKey, {
27072
+ if (found) return await this.reresolveCrossStackValue(found.value, found.lookupRegion, context, `Fn::ImportValue '${this.logTextOfLeaf(exportName, context)}' (producer ${this.logTextOfLeaf(found.refStack, context)} / ${this.logTextOfLeaf(found.lookupRegion, context)})`, sourceKey, {
26854
27073
  stackName: found.refStack,
26855
27074
  region: found.lookupRegion,
26856
27075
  outputKey: exportName
@@ -26915,9 +27134,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
26915
27134
  * `OutputName` is the overwhelmingly common cause, and the list is what makes
26916
27135
  * the error actionable.
26917
27136
  *
26918
- * Residual, stated rather than hidden: the needles belong to the CONSUMER's
26919
- * resolution, so a plaintext sitting in a PRODUCER key that this consumer
26920
- * never resolved is not maskable from here. The cap is what bounds that case;
27137
+ * `maskSecretsForLog` reads the consumer pass's log twin first (issue #3150),
27138
+ * so a key equal to a name this pass assembled around a short secret is
27139
+ * masked. Residual, stated rather than hidden: the needles and twins belong to the
27140
+ * CONSUMER's resolution, so a plaintext sitting in a PRODUCER key that this
27141
+ * consumer never resolved is not maskable from here. The cap is what bounds that case;
26921
27142
  * `cdkd scrub` reporting the producer's own `secretBearingKeys` is the remedy.
26922
27143
  */
26923
27144
  describeAvailableOutputs(keys, context) {
@@ -26949,7 +27170,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
26949
27170
  * logged for non-not-found failures). Same graceful-degradation
26950
27171
  * contract as {@link lookupCfnExport}.
26951
27172
  */
26952
- async lookupCfnStackOutputs(stackName, region, context) {
27173
+ async lookupCfnStackOutputs(stackName, region, context, loggedRegionText) {
26953
27174
  const cacheKey = `${region}\0${stackName}`;
26954
27175
  let fetch = this.cfnStackOutputsCache.get(cacheKey);
26955
27176
  if (!fetch) {
@@ -26963,7 +27184,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
26963
27184
  return await fetch;
26964
27185
  } catch (error) {
26965
27186
  const message = error instanceof Error ? error.message : String(error);
26966
- this.logger.warn(`Fn::GetStackOutput: CloudFormation DescribeStacks fallback failed for stack '${this.maskSecretsForLog(stackName, context)}' (${this.maskSecretsForLog(region, context)}): ${this.maskSecretsForLog(message, context)}. Grant cloudformation:DescribeStacks to resolve outputs from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
27187
+ this.logger.warn(`Fn::GetStackOutput: CloudFormation DescribeStacks fallback failed for stack '${this.maskSecretsForLog(stackName, context)}' (${this.maskSecretsForLog(loggedRegionText, context)}): ${this.maskSecretsForLog(message, context)}. Grant cloudformation:DescribeStacks to resolve outputs from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
26967
27188
  return;
26968
27189
  }
26969
27190
  }
@@ -27068,12 +27289,14 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27068
27289
  const outputName = await this.resolveValue(args["OutputName"], context);
27069
27290
  if (typeof outputName !== "string" || outputName === "") throw new Error(`Fn::GetStackOutput: OutputName must resolve to a non-empty string, got ${typeof outputName}`);
27070
27291
  let region = this.resolverRegion;
27292
+ let loggedRegionText = this.logTextOfLeaf(region, context);
27071
27293
  if ("Region" in args && args["Region"] !== void 0 && args["Region"] !== null) {
27072
27294
  const resolvedRegion = await this.resolveValue(args["Region"], context);
27073
27295
  if (typeof resolvedRegion !== "string" || resolvedRegion === "") throw new Error(`Fn::GetStackOutput: Region must resolve to a non-empty string, got ${typeof resolvedRegion}`);
27074
27296
  const requestedRegion = canonicalizeRegion(resolvedRegion);
27075
- if (!isClientSafeRegion(requestedRegion)) throw new Error(`Fn::GetStackOutput: '${this.maskThenStripThenMask(resolvedRegion, context).slice(0, 64)}' is not a valid AWS region name. The region selects both the AWS endpoint and the state-file key, so cdkd will not use it.`);
27297
+ if (!isClientSafeRegion(requestedRegion)) throw new Error(`Fn::GetStackOutput: '${this.maskThenStripThenMask(this.logTextOfLeaf(resolvedRegion, context) !== resolvedRegion ? "***" : resolvedRegion, context).slice(0, 64)}' is not a valid AWS region name. The region selects both the AWS endpoint and the state-file key, so cdkd will not use it.`);
27076
27298
  region = requestedRegion;
27299
+ loggedRegionText = this.regionLogText(resolvedRegion, context);
27077
27300
  }
27078
27301
  let roleArn;
27079
27302
  if ("RoleArn" in args && args["RoleArn"] !== void 0 && args["RoleArn"] !== null) {
@@ -27081,15 +27304,15 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27081
27304
  if (typeof raw !== "string" || raw === "") throw new Error(`Fn::GetStackOutput: RoleArn must be a literal string in the template (no Ref / Fn::GetAtt / Fn::Sub allowed for cross-account references). Got ${raw === null ? "null" : Array.isArray(raw) ? "array" : typeof raw}${typeof raw === "object" ? ` (intrinsic shape: ${JSON.stringify(raw).slice(0, 80)})` : ""}.`);
27082
27305
  roleArn = raw;
27083
27306
  }
27084
- if (!roleArn && context.stackName && context.stackName === stackName && canonicalizeRegion(region) === canonicalizeRegion(this.resolverRegion)) throw new Error(`Fn::GetStackOutput: cannot reference own stack '${this.maskSecretsForLog(stackName, context)}' in the same region '${this.maskSecretsForLog(region, context)}'`);
27307
+ if (!roleArn && context.stackName && context.stackName === stackName && canonicalizeRegion(region) === canonicalizeRegion(this.resolverRegion)) throw new Error(`Fn::GetStackOutput: cannot reference own stack '${this.maskSecretsForLog(stackName, context)}' in the same region '${this.maskSecretsForLog(loggedRegionText, context)}'`);
27085
27308
  const loggedStackName = this.maskSecretsForLog(stackName, context);
27086
27309
  const loggedOutputName = this.maskSecretsForLog(outputName, context);
27087
- const loggedRegion = this.maskSecretsForLog(region, context);
27310
+ const loggedRegion = this.maskSecretsForLog(loggedRegionText, context);
27088
27311
  this.logger.debug(`Resolving Fn::GetStackOutput: StackName=${loggedStackName}, Region=${loggedRegion}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${roleArn}` : ""}`);
27089
27312
  const stateData = roleArn ? await this.getCrossAccountStackState(roleArn, stackName, region, context) : await this.getSameAccountStackState(stackName, region, context);
27090
27313
  if (!stateData) {
27091
27314
  if (!roleArn && this.cfnFallback) {
27092
- const cfnOutputs = await this.lookupCfnStackOutputs(stackName, region, context);
27315
+ const cfnOutputs = await this.lookupCfnStackOutputs(stackName, region, context, loggedRegionText);
27093
27316
  if (cfnOutputs) {
27094
27317
  if (!Object.hasOwn(cfnOutputs, outputName)) {
27095
27318
  const available = this.describeAvailableOutputs(Object.keys(cfnOutputs), context);
@@ -27111,12 +27334,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27111
27334
  this.logger.info(`Resolved Fn::GetStackOutput: StackName=${loggedStackName}, Region=${loggedRegion}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${roleArn}` : ""} (${carriesDynamicReference(value) ? "redacted dynamic reference" : "literal value"})`);
27112
27335
  if (!roleArn) this.recordOutputRead(context, stackName, region, outputName);
27113
27336
  if (roleArn && !context.skipDynamicReferences && carriesDynamicReference(value)) throw markNonRetryable(new CrossAccountSecretRefusalError(`Fn::GetStackOutput: output '${loggedOutputName}' of stack '${loggedStackName}' (${loggedRegion}) is a redacted dynamic reference, and this is a CROSS-ACCOUNT reference (RoleArn ${roleArn}). cdkd will not resolve a producer account's secret with the consumer's credentials — a same-named secret in the consumer account would answer instead. Export a non-secret value (e.g. the secret's ARN) and resolve it in the consumer stack, or reference the producer stack from within its own account.`));
27114
- return await this.reresolveCrossStackValue(value, region, context, `Fn::GetStackOutput '${outputName}' (producer ${stackName} / ${region})`, sourceKey, {
27337
+ return await this.reresolveCrossStackValue(value, region, context, `Fn::GetStackOutput '${this.logTextOfLeaf(outputName, context)}' (producer ${this.logTextOfLeaf(stackName, context)} / ${loggedRegionText})`, sourceKey, {
27115
27338
  stackName,
27116
27339
  region,
27117
27340
  outputKey: outputName,
27118
27341
  ...roleArn ? { crossAccount: true } : {}
27119
- });
27342
+ }, loggedRegionText);
27120
27343
  }
27121
27344
  /**
27122
27345
  * Push a resolved `Fn::GetStackOutput` into the consumer's
@@ -27254,7 +27477,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27254
27477
  if (typeof resolvedValue !== "string") throw new Error(`Fn::Base64: value must resolve to a string, got ${typeof resolvedValue}`);
27255
27478
  const result = Buffer.from(resolvedValue).toString("base64");
27256
27479
  const inputLogText = this.logTextOfLeaf(resolvedValue, context);
27257
- if (context.recordedSecretValues && (inputLogText !== resolvedValue || this.maskSecretsForLog(resolvedValue, context) !== resolvedValue)) recordMaskOnlyValue(context.recordedSecretValues, result);
27480
+ if (context.recordedSecretValues && (inputLogText !== resolvedValue || this.maskNeedlesForLog(resolvedValue, context) !== resolvedValue)) recordMaskOnlyValue(context.recordedSecretValues, result);
27258
27481
  this.logger.debug(`Resolved Fn::Base64: ${this.maskSecretsForLog(inputLogText, context)} -> ${this.maskSecretsForLog(inputLogText !== resolvedValue ? "***" : result, context)}`);
27259
27482
  return result;
27260
27483
  }
@@ -27284,21 +27507,23 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27284
27507
  * given explicitly (see {@link explicitRegion}).
27285
27508
  */
27286
27509
  let clientRegion;
27510
+ let loggedRegionText;
27287
27511
  if (typeof resolvedValue === "string" && resolvedValue !== "") {
27288
27512
  const requested = canonicalizeRegion(resolvedValue);
27289
- if (!isClientSafeRegion(requested)) throw new Error(`Fn::GetAZs: '${this.maskThenStripThenMask(resolvedValue, context).slice(0, 64)}' is not a valid AWS region name. A region is substituted into the AWS service hostname, so cdkd will not build a client from it.`);
27513
+ if (!isClientSafeRegion(requested)) throw new Error(`Fn::GetAZs: '${this.maskThenStripThenMask(this.logTextOfLeaf(resolvedValue, context) !== resolvedValue ? "***" : resolvedValue, context).slice(0, 64)}' is not a valid AWS region name. A region is substituted into the AWS service hostname, so cdkd will not build a client from it.`);
27290
27514
  region = requested;
27291
27515
  clientRegion = requested;
27516
+ loggedRegionText = this.regionLogText(resolvedValue, context);
27292
27517
  } else {
27293
27518
  region = (await getAccountInfo(this.resolverRegion)).region;
27294
27519
  clientRegion = this.explicitRegion;
27295
27520
  }
27296
27521
  const cached = cachedAvailabilityZones[region];
27297
27522
  if (cached) {
27298
- this.logger.debug(`Resolved Fn::GetAZs from cache: ${this.maskSecretsForLog(region, context)} -> ${JSON.stringify(this.maskValueLeaves(cached, context))}`);
27523
+ this.logger.debug(`Resolved Fn::GetAZs from cache: ${this.maskSecretsForLog(loggedRegionText ?? region, context)} -> ${JSON.stringify(this.maskValueLeaves(cached, context))}`);
27299
27524
  return cached;
27300
27525
  }
27301
- const ec2Client = this.clientsForRegion(clientRegion).ec2;
27526
+ const ec2Client = this.clientsForRegion(clientRegion, loggedRegionText === void 0 ? void 0 : this.maskSecretsForLog(loggedRegionText, context)).ec2;
27302
27527
  let azNames;
27303
27528
  try {
27304
27529
  azNames = ((await ec2Client.send(new DescribeAvailabilityZonesCommand({ Filters: [{
@@ -27309,11 +27534,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27309
27534
  Values: ["available"]
27310
27535
  }] }))).AvailabilityZones || []).map((az) => az.ZoneName).filter((name) => name !== void 0).sort();
27311
27536
  } catch (error) {
27312
- throw new Error(`Fn::GetAZs: failed to describe availability zones for region '${this.maskSecretsForLog(region, context)}': ${this.maskSecretsForLog(error instanceof Error ? error.message : String(error), context)}`);
27537
+ throw new Error(`Fn::GetAZs: failed to describe availability zones for region '${this.maskSecretsForLog(loggedRegionText ?? region, context)}': ${this.maskSecretsForLog(error instanceof Error ? error.message : String(error), context)}`);
27313
27538
  }
27314
- if (azNames.length === 0) throw new Error(`Fn::GetAZs: no availability zones returned for region '${this.maskSecretsForLog(region, context)}'. Either the region is not enabled on this account (opt-in regions must be enabled before use), or the request was answered by a different region's endpoint.`);
27539
+ if (azNames.length === 0) throw new Error(`Fn::GetAZs: no availability zones returned for region '${this.maskSecretsForLog(loggedRegionText ?? region, context)}'. Either the region is not enabled on this account (opt-in regions must be enabled before use), or the request was answered by a different region's endpoint.`);
27315
27540
  cachedAvailabilityZones[region] = azNames;
27316
- this.logger.debug(`Resolved Fn::GetAZs: ${this.maskSecretsForLog(region, context)} -> ${JSON.stringify(this.maskValueLeaves(azNames, context))}`);
27541
+ this.logger.debug(`Resolved Fn::GetAZs: ${this.maskSecretsForLog(loggedRegionText ?? region, context)} -> ${JSON.stringify(this.maskValueLeaves(azNames, context))}`);
27317
27542
  return azNames;
27318
27543
  }
27319
27544
  /**
@@ -27350,8 +27575,37 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27350
27575
  * Mask any resolved secret value out of a string bound for a log line, using
27351
27576
  * the secrets recorded on the resolution pass (GHSA fix). No-op when the pass
27352
27577
  * recorded no secrets.
27578
+ *
27579
+ * A value with a registered LOG TWIN prints as its twin first (issue
27580
+ * [#3150](https://github.com/go-to-k/cdkd/issues/3150)). The needle mask
27581
+ * matches a secret under {@link MIN_NEEDLE_LENGTH} only as the whole text,
27582
+ * so a name an `Fn::Sub` assembled around a 1-3 character secret (a map key,
27583
+ * a stack or output name, an attribute name, the secret id of a dynamic
27584
+ * reference) printed in the clear at every site that handed its RAW value
27585
+ * here, although the pass had registered where the secret sits. Looking the
27586
+ * twin up HERE, rather than at each such site, is what makes the class
27587
+ * closed for raw values: every one of those sites already calls this method.
27588
+ * The twin prints as it is: it is the raw text with masked spans, so it
27589
+ * holds no recorded needle the raw text lacks. When the needle mask ALSO
27590
+ * changes the raw text the whole text is masked instead, the rule
27591
+ * `logTwinText` applies to a Join / Sub line, since the two masks' spans
27592
+ * cannot be merged. What this cannot see is a value TRANSFORMED before it
27593
+ * arrives (a lowercased region, a sliced output name, an assembled
27594
+ * sentence); those sites derive their text from the twin themselves.
27353
27595
  */
27354
27596
  maskSecretsForLog(text, context) {
27597
+ const registered = this.registeredLogTwin(text, context);
27598
+ if (registered === void 0) return this.maskNeedlesForLog(text, context);
27599
+ return this.maskNeedlesForLog(text, context) !== text ? "***" : registered;
27600
+ }
27601
+ /**
27602
+ * The needle mask alone: {@link maskSecretsForLog} without the log-twin
27603
+ * lookup. For the two DETECTORS that must ask the needle mask apart from the
27604
+ * position mask: `logTwinText`, which asks it of a value that already has a
27605
+ * twin, and `resolveBase64`, whose other operand is the position mask. A
27606
+ * message masks through `maskSecretsForLog`.
27607
+ */
27608
+ maskNeedlesForLog(text, context) {
27355
27609
  let masked = text;
27356
27610
  const inherited = context?.inheritedSecrets;
27357
27611
  if (inherited && inherited.size > 0) masked = maskSecretsInText(masked, inherited);
@@ -27381,7 +27635,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27381
27635
  * resolver's mask-coverage checker sees a masker at the site.
27382
27636
  */
27383
27637
  logTwinText(result, twin, context) {
27384
- return twin !== result && this.maskSecretsForLog(result, context) !== result ? "***" : twin;
27638
+ return twin !== result && this.maskNeedlesForLog(result, context) !== result ? "***" : twin;
27385
27639
  }
27386
27640
  /**
27387
27641
  * The log twins of `Fn::Split`'s pieces (issue #3100), each registered for
@@ -27429,16 +27683,15 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27429
27683
  * the leaf treated as a resolution product — masked whole when it is a
27430
27684
  * recorded secret, otherwise the masked twin this pass registered for it —
27431
27685
  * and then `logTwinText`'s whole-line guard. The caller still wraps the
27432
- * result in `maskSecretsForLog`. Shared by every caller of `maskValueLeaves`
27433
- * (the `Fn::Select`, `Fn::Split`, `Fn::Equals`, `Fn::FindInMap`,
27434
- * `Fn::GetAZs` and `Fn::Cidr` lines and the VPC `Ipv6CidrBlocks` line among
27435
- * them) and by the `Fn::Base64` line, so a string one
27436
- * write already masked by position is not printed in the clear by the next
27437
- * intrinsic that logs it. `maskValueLeaves` also masks values interpolated
27438
- * into THROWN messages (the `Fn::Cidr` argument refusals, for one), so those
27439
- * messages take the same position mask: a short secret an earlier write put
27440
- * into the value is `***` there too, the direction the mask at the throw
27441
- * already takes for a whole secret.
27686
+ * result in `maskSecretsForLog`. Used where the text is needed BEFORE that
27687
+ * masker, or apart from it: the `Fn::Base64` line and its detector, and the
27688
+ * names issue [#3150](https://github.com/go-to-k/cdkd/issues/3150)
27689
+ * transforms or composes before printing (`regionLogText`,
27690
+ * `outputNameLogText`, the invalid-region refusals, the cross-stack `origin`
27691
+ * strings). A leaf printed as it is needs none of it: `maskSecretsForLog`
27692
+ * looks the twin up itself, which is how `maskValueLeaves` gives its lines
27693
+ * and the THROWN messages it masks (the `Fn::Cidr` argument refusals, for
27694
+ * one) the same position mask.
27442
27695
  */
27443
27696
  logTextOfLeaf(value, context) {
27444
27697
  return this.logTwinText(value, this.logTwinOfProduct({
@@ -27446,6 +27699,85 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27446
27699
  twin: value
27447
27700
  }, context).twin, context);
27448
27701
  }
27702
+ /**
27703
+ * The log text of a nested stack's OUTPUT name, the part of `attributeName`
27704
+ * after `Outputs.` (issue [#3150](https://github.com/go-to-k/cdkd/issues/3150)).
27705
+ * The twin registry is keyed by the exact string, so the suffix has no twin
27706
+ * of its own: it is sliced from the twin of the WHOLE attribute name. A twin
27707
+ * that no longer starts with the literal prefix has a mask over part of it
27708
+ * (a secret was written into the prefix), so the slice offset no longer
27709
+ * lines up and the suffix prints as `***`.
27710
+ */
27711
+ outputNameLogText(attributeName, context) {
27712
+ const twin = this.logTextOfLeaf(attributeName, context);
27713
+ return twin.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX) ? twin.slice(8) : "***";
27714
+ }
27715
+ /**
27716
+ * The log text of a template-supplied REGION as the resolver uses it, i.e.
27717
+ * after `canonicalizeRegion` (issue #3150). The twin is taken from the RAW
27718
+ * value, the string a write registered, and lowercased the same way: a
27719
+ * lookup of the canonical form finds no twin once the raw value had an
27720
+ * upper-case part, and lowercasing leaves every `***` span where it was.
27721
+ */
27722
+ regionLogText(rawRegion, context) {
27723
+ const canonical = canonicalizeRegion(rawRegion);
27724
+ const text = canonicalizeRegion(this.logTextOfLeaf(rawRegion, context));
27725
+ return text !== canonical && this.maskNeedlesForLog(canonical, context) !== canonical ? "***" : text;
27726
+ }
27727
+ /**
27728
+ * The log text of a name or token whose `twin` masks spans of `raw` (issue
27729
+ * [#3150](https://github.com/go-to-k/cdkd/issues/3150)). The twin prints
27730
+ * unless the needle mask ALSO changes `raw`; then the two must agree. When
27731
+ * the needle-masked `raw` IS the twin, both masked the same spans and the
27732
+ * twin is safe (`/probe/***` for a 4+ character secret the name holds
27733
+ * whole). When they differ, a recorded secret straddles a masked span
27734
+ * (`q7ab` across `id-***ab`), no text can honour both, and the name prints
27735
+ * as `***`. Coarser `logTwinText` gives up whenever both masks fire.
27736
+ */
27737
+ straddleSafeTwin(raw, twin, context) {
27738
+ if (twin === raw) return twin;
27739
+ const needled = this.maskNeedlesForLog(raw, context);
27740
+ if (needled === raw) return twin;
27741
+ return needled === twin ? twin : "***";
27742
+ }
27743
+ /**
27744
+ * The log text of a name parsed out of a dynamic-reference token (issue
27745
+ * [#3150](https://github.com/go-to-k/cdkd/issues/3150)). `resolveSub` /
27746
+ * `resolveJoin` re-enter the dynamic-reference loop with the ASSEMBLED string
27747
+ * and its twin, so a secret id, JSON key, version stage or id, SSM parameter
27748
+ * name, service or ARN region assembled around a short secret has its masked
27749
+ * spelling in `tokenTwin` only. Every such name is a run of the token's
27750
+ * `:`-separated pieces, so its log text is the twin's run over the same
27751
+ * pieces. Nothing is registered: the pass's log-twin registry also decides
27752
+ * what `Fn::Base64` persists, so the mapping lives in the returned function.
27753
+ *
27754
+ * `tokenTwin` is the twin's own `{{resolve:...}}` token paired with this one.
27755
+ * Each run's text goes through `straddleSafeTwin`, so a name whose needle
27756
+ * mask disagrees with its twin prints as `***`. A name whose runs carry different
27757
+ * twins, or a token whose pieces do not pair with the twin's (a mask
27758
+ * covering a `:`), prints as `***` too.
27759
+ *
27760
+ * A name that is no run of the token is a default the caller synthesized
27761
+ * (`AWSCURRENT` for an empty version stage, `''` for an absent version id),
27762
+ * printed as it is. Should the token spell such a non-empty name inside a longer
27763
+ * piece, it prints as `***`: the run rule says nothing about that text.
27764
+ */
27765
+ dynamicReferenceNameLogText(inner, tokenTwin, context) {
27766
+ const twinPieces = tokenTwin.slice(10, -2).split(":");
27767
+ const pieces = inner.split(":");
27768
+ if (twinPieces.length !== pieces.length) return () => "***";
27769
+ return (name) => {
27770
+ let text;
27771
+ for (let start = 0; start < pieces.length; start++) for (let end = start + 1; end <= pieces.length; end++) {
27772
+ if (pieces.slice(start, end).join(":") !== name) continue;
27773
+ const twin = this.straddleSafeTwin(name, twinPieces.slice(start, end).join(":"), context);
27774
+ if (text !== void 0 && text !== twin) return "***";
27775
+ text = twin;
27776
+ break;
27777
+ }
27778
+ return text ?? (name !== "" && inner.includes(name) ? "***" : name);
27779
+ };
27780
+ }
27449
27781
  /** The bag a pass's log twins are keyed by (issue #3100): the recorded one, else the inherited one. */
27450
27782
  logTwinBag(context) {
27451
27783
  return context?.recordedSecretValues ?? context?.inheritedSecrets;
@@ -27581,7 +27913,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27581
27913
  maskValueLeaves(value, context) {
27582
27914
  const done = /* @__PURE__ */ new Map();
27583
27915
  const walk = (node) => {
27584
- if (typeof node === "string") return this.maskSecretsForLog(this.logTextOfLeaf(node, context), context);
27916
+ if (typeof node === "string") return this.maskSecretsForLog(node, context);
27585
27917
  if (node === null || typeof node !== "object") return node;
27586
27918
  const memo = done.get(node);
27587
27919
  if (memo !== void 0) return memo;
@@ -27593,7 +27925,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27593
27925
  }
27594
27926
  const out = Object.create(null);
27595
27927
  done.set(node, out);
27596
- for (const [key, child] of Object.entries(node)) out[this.maskSecretsForLog(this.logTextOfLeaf(key, context), context)] = walk(child);
27928
+ for (const [key, child] of Object.entries(node)) out[this.maskSecretsForLog(key, context)] = walk(child);
27597
27929
  return out;
27598
27930
  };
27599
27931
  return walk(value);
@@ -27620,14 +27952,14 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27620
27952
  * visible. The FIRST mask is demonstrated by a test: deleting it (masking
27621
27953
  * only after the strip) reds the split-needle case, because the recorded
27622
27954
  * needle is then the split form and the strip has destroyed it. The SECOND
27623
- * mask is NOT reached by any test here and is defence in depth: it earns its
27624
- * place only when the bag holds a needle that is the STRIPPED form of the
27625
- * value in hand, and every route through THIS resolver records the value it
27626
- * actually resolved — so the split copy is itself a needle and the first
27627
- * mask already catches it. The shape was measured against a hand-built bag
27628
- * during review, not produced by the resolver. Kept anyway: it is one
27629
- * idempotent call, and the alternative is re-deciding per future caller
27630
- * whether the bag and the value can disagree.
27955
+ * mask earns its place when the bag holds a needle that is the STRIPPED form
27956
+ * of the text in hand. A value this resolver resolved cannot be that text
27957
+ * (the split copy is itself recorded, so the first mask catches it), but a
27958
+ * template LITERAL can: issue #3150's region-scoped clients refusal prints
27959
+ * a producer-region guest's region through this helper, and a literal ARN
27960
+ * region spelling a recorded `st-1` as `s` + U+0001 + `t-1` is masked only
27961
+ * by the second pass (`intrinsic-resolver-name-argument-log-twin.test.ts`
27962
+ * pins both halves on that route).
27631
27963
  *
27632
27964
  * THE BOUND, since this file's job is to state them: this covers a needle
27633
27965
  * split by a character `stripControlChars` removes. A needle split by
@@ -27661,7 +27993,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27661
27993
  * NEEDLE match that straddles a masked span, which is why the log lines go
27662
27994
  * through `logTwinText` rather than masking the twin alone.
27663
27995
  */
27664
- async resolveDynamicReferencesWithLogTwin(value, logTwin, context) {
27996
+ async resolveDynamicReferencesWithLogTwin(value, logTwin, context, inherited) {
27665
27997
  const pattern = /\{\{resolve:([^}]+)\}\}/g;
27666
27998
  let result = value;
27667
27999
  let twin = logTwin;
@@ -27671,14 +28003,22 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27671
28003
  fullMatch: match[0],
27672
28004
  inner: match[1]
27673
28005
  });
27674
- for (const { fullMatch, inner } of matches) {
28006
+ const twinTokens = dynamicReferenceTokens(logTwin);
28007
+ const tokensAligned = twinTokens.length === matches.length;
28008
+ for (const [index, { fullMatch, inner }] of matches.entries()) {
27675
28009
  const service = inner.split(":")[0];
28010
+ const pairedTwin = tokensAligned ? twinTokens[index] : void 0;
28011
+ const tokenLogText = inherited?.tokenLogText ?? (pairedTwin === void 0 ? "***" : this.straddleSafeTwin(fullMatch, pairedTwin, context));
28012
+ const nameLogText = inherited?.nameLogText ?? (pairedTwin === void 0 ? () => "***" : this.dynamicReferenceNameLogText(inner, pairedTwin, context));
27676
28013
  const isKnownSecret = service === "secretsmanager" || service === "ssm-secure" || recordedSecretExpressions.has(fullMatch);
27677
28014
  if (isKnownSecret && context?.skipDynamicReferences) continue;
27678
28015
  const regionVerdict = classifyReplaySecretRegion(fullMatch, this.explicitRegion ?? this.resolverRegion, context?.producerRegions);
27679
- if (regionVerdict.kind === "ambiguous") throw markNonRetryable(new DynamicReferenceRegionAmbiguousError(`Refusing to resolve the secret reference ${this.maskSecretsForLog(fullMatch, context)}: it names '${this.maskSecretsForLog(regionVerdict.secretName, context)}' without a region, and this stack reads from ${regionVerdict.foreignProducerRegions.join(", ")} as well as its own region. cdkd cannot tell which one must answer, and resolving against the wrong one yields a different secret. Spell the reference as a full ARN to say which region owns it.`));
28016
+ if (regionVerdict.kind === "ambiguous") throw markNonRetryable(new DynamicReferenceRegionAmbiguousError(`Refusing to resolve the secret reference ${this.maskSecretsForLog(tokenLogText, context)}: it names '${this.maskSecretsForLog(nameLogText(regionVerdict.secretName), context)}' without a region, and this stack reads from ${this.maskSecretsForLog(regionVerdict.foreignProducerRegions.map((r) => this.maskSecretsForLog(r, context)).join(", "), context)} as well as its own region. cdkd cannot tell which one must answer, and resolving against the wrong one yields a different secret. Spell the reference as a full ARN to say which region owns it.`));
27680
28017
  if (regionVerdict.kind === "named-region") {
27681
- const foreign = await this.resolverForProducerRegion(regionVerdict.region).resolveDynamicReferencesWithLogTwin(fullMatch, fullMatch, withoutProducerRegions(context));
28018
+ const foreign = await this.resolverForProducerRegion(regionVerdict.region, context, nameLogText(regionVerdict.region)).resolveDynamicReferencesWithLogTwin(fullMatch, fullMatch, withoutProducerRegions(context), {
28019
+ tokenLogText,
28020
+ nameLogText
28021
+ });
27682
28022
  result = result.replace(fullMatch, () => foreign.result);
27683
28023
  twin = twin.replace(fullMatch, () => foreign.twin);
27684
28024
  continue;
@@ -27709,10 +28049,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27709
28049
  * which caches nothing (issue #1933).
27710
28050
  */
27711
28051
  let cacheable = true;
27712
- if (service === "secretsmanager") resolved = await this.resolveSecretsManagerReference(inner, context);
28052
+ if (service === "secretsmanager") resolved = await this.resolveSecretsManagerReference(inner, context, nameLogText);
27713
28053
  else if (service === "ssm") {
27714
28054
  const decrypt = context?.skipDynamicReferences !== true;
27715
- const param = await this.resolveSSMReference(parts, decrypt, "ssm", context);
28055
+ const param = await this.resolveSSMReference(parts, decrypt, "ssm", context, nameLogText);
27716
28056
  if (param.type === "SecureString") this.pinSecretVerdict(fullMatch, true);
27717
28057
  else if (!param.secure) this.pinSecretVerdict(fullMatch, false);
27718
28058
  else cacheable = false;
@@ -27722,12 +28062,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27722
28062
  }
27723
28063
  resolved = param.value;
27724
28064
  } else if (service === "ssm-secure") {
27725
- const param = await this.resolveSSMReference(parts, true, "ssm-secure", context);
27726
- if (!param.secure) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to resolve ${this.maskSecretsForLog(fullMatch, context)}: the parameter is a ${param.type} parameter, and the ssm-secure spelling is defined for SecureString parameters only. Reference it as {{resolve:ssm:...}} if it is public configuration.`));
28065
+ const param = await this.resolveSSMReference(parts, true, "ssm-secure", context, nameLogText);
28066
+ if (!param.secure) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to resolve ${this.maskSecretsForLog(tokenLogText, context)}: the parameter is a ${param.type} parameter, and the ssm-secure spelling is defined for SecureString parameters only. Reference it as {{resolve:ssm:...}} if it is public configuration.`));
27727
28067
  isSecret = true;
27728
28068
  resolved = param.value;
27729
28069
  } else {
27730
- this.logger.warn(this.maskSecretsForLog(`Unsupported dynamic reference service: ${service}`, context));
28070
+ this.logger.warn(this.maskSecretsForLog(`Unsupported dynamic reference service: ${this.maskSecretsForLog(nameLogText(String(service)), context)}`, context));
27731
28071
  continue;
27732
28072
  }
27733
28073
  if (cacheable) this.cachedDynamicReferences.set(fullMatch, {
@@ -27763,7 +28103,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27763
28103
  * runs when no mid-string ":SecretString:" delimiter is present, so the json-key / version
27764
28104
  * forms are unaffected.)
27765
28105
  */
27766
- async resolveSecretsManagerReference(inner, context) {
28106
+ async resolveSecretsManagerReference(inner, context, nameLogText) {
27767
28107
  const afterService = inner.substring(15);
27768
28108
  let secretId;
27769
28109
  let jsonKey = "";
@@ -27792,9 +28132,9 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27792
28132
  } else secretId = afterService;
27793
28133
  if (!versionStage) versionStage = "AWSCURRENT";
27794
28134
  if (!secretId) throw new Error("Dynamic reference: secretsmanager SECRET_ID is required");
27795
- const loggedSecretId = this.maskSecretsForLog(secretId, context);
27796
- const loggedJsonKey = this.maskSecretsForLog(jsonKey, context);
27797
- this.logger.debug(`Resolving dynamic reference: secretsmanager:${loggedSecretId}:SecretString:${loggedJsonKey}:${this.maskSecretsForLog(versionStage, context)}:${this.maskSecretsForLog(versionId, context)}`);
28135
+ const loggedSecretId = this.maskSecretsForLog(nameLogText(secretId), context);
28136
+ const loggedJsonKey = this.maskSecretsForLog(nameLogText(jsonKey), context);
28137
+ this.logger.debug(`Resolving dynamic reference: secretsmanager:${loggedSecretId}:SecretString:${loggedJsonKey}:${this.maskSecretsForLog(nameLogText(versionStage), context)}:${this.maskSecretsForLog(nameLogText(versionId), context)}`);
27798
28138
  const client = this.clientsForRegion(this.explicitRegion).secretsManager;
27799
28139
  const command = new GetSecretValueCommand({
27800
28140
  SecretId: secretId,
@@ -27955,10 +28295,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
27955
28295
  * discard the value when `secure` is set — it is ciphertext, not the resolved
27956
28296
  * reference.
27957
28297
  */
27958
- async resolveSSMReference(parts, decrypt = true, service = "ssm", context) {
28298
+ async resolveSSMReference(parts, decrypt, service, context, nameLogText) {
27959
28299
  const parameterName = parts.slice(1).join(":");
27960
28300
  if (!parameterName) throw new Error(`Dynamic reference: ${service} PARAMETER_NAME is required`);
27961
- const loggedParameterName = this.maskSecretsForLog(parameterName, context);
28301
+ const loggedParameterName = this.maskSecretsForLog(nameLogText(parameterName), context);
27962
28302
  this.logger.debug(`Resolving dynamic reference: ${service}:${loggedParameterName}`);
27963
28303
  const client = this.clientsForRegion(this.explicitRegion).ssm;
27964
28304
  const command = new GetParameterCommand({
@@ -29310,7 +29650,7 @@ var CloudControlProvider = class {
29310
29650
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
29311
29651
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
29312
29652
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
29313
- const { ASGProvider } = await import("./asg-provider-Daqr0Ygq.js").then((n) => n.n);
29653
+ const { ASGProvider } = await import("./asg-provider-BAYjqZuM.js").then((n) => n.n);
29314
29654
  const asgProvider = new ASGProvider();
29315
29655
  return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
29316
29656
  }
@@ -38011,7 +38351,7 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
38011
38351
  });
38012
38352
  } catch (createError) {
38013
38353
  const msg = createError instanceof Error ? createError.message : String(createError);
38014
- if (!isNameCollisionError(msg)) throw createError;
38354
+ if (!isNameCollisionErrorFrom(createError, op.logicalId)) throw createError;
38015
38355
  if (rollbackRetainsNewResource(current)) throw markNonRetryable(new CdkdError(maskSecretsInText(`Cannot reverse the replacement of ${safe(op.logicalId)} (${safe(op.resourceType)}): the re-create of the old resource (${safe(prev.physicalId)}) collided with the name still held by the new one (${safe(current.physicalId)}), and UpdateReplacePolicy: Retain pins that new resource in place, so cdkd will not delete it to free the name. Delete the new resource yourself, or remove UpdateReplacePolicy: Retain, then re-run \`cdkd rollback\` — the journal is kept, so the revert resumes from here. To leave THIS resource alone and let the rest of the rollback proceed, re-run with ` + (typeof op.logicalId === "string" && PASTEABLE_LOGICAL_ID.test(op.logicalId) ? `\`cdkd rollback --orphan ${op.logicalId}\`: one op failure stops the ` : "`cdkd rollback --orphan <id>` (the id is withheld: it is not a plain CloudFormation logical id, so a pasted command could be reshaped by the shell or name a different resource -- read it from `cdkd events`): one op failure stops the ") + `segment loop, so a single pinned resource otherwise halts every OLDER segment too. Underlying collision: ${displaySafe(msg)}`, secrets), "NAMED_REPLACEMENT_COLLISION", maskSecretsInError(createError instanceof Error ? createError : void 0, secrets)));
38016
38356
  logger.info(` Rollback: re-create collided with the new resource's name — deleting the new resource (${displaySafe(current.physicalId)}) first...`);
38017
38357
  {
@@ -41148,7 +41488,7 @@ var DeployEngine = class DeployEngine {
41148
41488
  createResult = await this.withRetry(() => withCurrentResourceSecrets(updateSecrets, () => replaceProvider.create(logicalId, resourceType, replaceProps, { maskSecrets: createSecretMasker(updateSecrets) })), logicalId, void 0, void 0, replaceProvider);
41149
41489
  } catch (createError) {
41150
41490
  const createMsg = createError instanceof Error ? createError.message : String(createError);
41151
- if (!isNameCollisionError(createMsg)) throw createError;
41491
+ if (!isNameCollisionErrorFrom(createError, logicalId)) throw createError;
41152
41492
  const nameOrigin = this.replacementNameOrigin(logicalId, currentResource.physicalId);
41153
41493
  if (updateReplacePolicy === "Retain") throw new CdkdError(`${logicalId} (${resourceType}) requires replacement, but its physical name is still held by the existing resource AND UpdateReplacePolicy: Retain pins that resource in place. ${nameOrigin.descriptor}. ${nameOrigin.remedy} — with Retain, the old resource keeps the name, so a same-name replacement can never proceed.`, "NAMED_REPLACEMENT_COLLISION");
41154
41494
  if (this.options.replace !== true) throw new CdkdError(`${logicalId} (${resourceType}) requires replacement, but the create-first attempt collided with the existing resource: ${createMsg}. ${nameOrigin.descriptor}, so the CloudFormation-style safe replacement order (create the new resource before deleting the old) cannot reuse the occupied name — CloudFormation refuses this shape with "cannot update a stack when a custom-named resource requires replacing". ${nameOrigin.remedy}, or re-run with \`cdkd deploy --replace\` to delete the old resource FIRST and recreate it under the same name (the resource is briefly unavailable while it is recreated).`, "NAMED_REPLACEMENT_COLLISION");
@@ -41284,8 +41624,7 @@ var DeployEngine = class DeployEngine {
41284
41624
  createResult = await this.withRetry(() => withCurrentResourceSecrets(updateSecrets, () => replProvider.create(logicalId, resourceType, replProps, { maskSecrets: createSecretMasker(updateSecrets) })), logicalId, void 0, void 0, replProvider);
41285
41625
  } catch (createError) {
41286
41626
  if (!retainOldOnReplace) throw new Error(maskSecretsInText(`Failed to create ${logicalId} after the UPDATE-not-supported replacement: the old resource (${currentResource.physicalId}) is now gone. Cause: ${createError instanceof Error ? createError.message : String(createError)}. Re-run the deploy to create it fresh.`, updateSecrets), { cause: createError instanceof Error ? createError : void 0 });
41287
- const createMsg = createError instanceof Error ? createError.message : String(createError);
41288
- if (!isNameCollisionError(createMsg)) throw createError;
41627
+ if (!isNameCollisionErrorFrom(createError, logicalId)) throw createError;
41289
41628
  const nameOrigin = this.replacementNameOrigin(logicalId, currentResource.physicalId);
41290
41629
  throw markNonRetryable(new CdkdError(`${logicalId} (${resourceType}) requires replacement because the provisioning layer cannot update it in place — but its physical name is still held by the existing resource AND UpdateReplacePolicy: Retain pins that resource in place. ${nameOrigin.descriptor}. ${nameOrigin.remedy} — with Retain, the old resource keeps the name, so a same-name replacement can never proceed. Removing UpdateReplacePolicy: Retain lets cdkd delete the old resource first, which destroys it and any data it holds.`, "NAMED_REPLACEMENT_COLLISION", createError instanceof Error ? createError : void 0));
41291
41630
  }
@@ -41569,7 +41908,7 @@ var DeployEngine = class DeployEngine {
41569
41908
  if (!(error instanceof ProvisioningError)) return void 0;
41570
41909
  const physicalId = error.physicalId;
41571
41910
  if (!physicalId) return void 0;
41572
- if (!isNameCollisionError(error.message)) return void 0;
41911
+ if (!isNameCollisionErrorFrom(error, logicalId)) return void 0;
41573
41912
  const stackName = getCurrentStackName();
41574
41913
  if (!looksLikeCdkdGeneratedName(physicalId, logicalId, stackName)) return void 0;
41575
41914
  if (!stackName) return void 0;
@@ -42039,5 +42378,5 @@ var DeployEngine = class DeployEngine {
42039
42378
  };
42040
42379
 
42041
42380
  //#endregion
42042
- export { secretBearing as $, ensureAssetStorage as $n, setAwsClients as $r, s3BucketRegionalDomainName as $t, extractDeploymentEventError as A, retryClassificationText as Ai, buildForceUnlockCommand as An, resolveSkipPrefix as Ar, parameterTypeMayLoseSecretIdentity as At, toCfnInteger as B, orphansAfterRollback as Bn, uploadCfnTemplate as Br, configIntegerRefusal as Bt, buildFinalSnapshotIdentifier as C, withErrorHandling as Ci, recordMaskOnlyValue as Cn, Synthesizer as Cr, IntrinsicFunctionResolver as Ct, refusesFinalSnapshot as D, isTransientServerError as Di, LockManager as Dn, resolveApp as Dr, getAccountInfo as Dt, isFinalSnapshotError as E, isThrottlingError as Ei, scrubResourceRecord as En, getLegacyStateBucketName as Er, coerceParameterTypedValue as Et, isStatefulRecreateTargetSync as F, CUSTOM_RESOURCE_RESPONSE_PREFIX as Fn, warnDeprecatedNoPrefixCliFlag as Fr, resolveExplicitPhysicalId as Ft, gray as G, WorkGraph as Gn, derivePartitionAndUrlSuffix as Gr, requireConfigObject as Gt, formatResourceLine as H, shouldRetainResource as Hn, expectedOwnerParam as Hr, readConfigString as Ht, renderStatefulReason as I, DEFAULT_STATE_PREFIX as In, CFN_TEMPLATE_BODY_LIMIT as Ir, assertRegionMatch as It, yellow as J, loadPublishableAssetManifest as Jn, clearBucketRegionCache as Jr, producerRegionsFromState as Jt, green as K, buildAssetRedirectMap as Kn, AssemblyReader as Kr, requireConfigString as Kt, WARM_THROUGHPUT_MEMBERS as L, exportNamesCarriedFrom as Ln, CFN_TEMPLATE_URL_LIMIT as Lr, coerceCfnBoolean as Lt, planOrphanAdoption as M, forceQuitRecoveryClause as Mn, resolveStateBucketWithDefaultAndSource as Mr, isSettledInstanceState as Mt, MULTI_REGION_RECREATE_BLOCKED_TYPES as N, formatLockExpiry as Nn, resolveUseCdkBootstrapAssets as Nr, WAFv2WebACLProvider as Nt, unsupportedFinalSnapshotError as O, markNonRetryable as Oi, S3StateBackend as On, resolveAutoAssetStorage as Or, isImpossibleEmptyStoredAttribute as Ot, isStatefulRecreateTargetForReplace as P, shellQuote as Pn, stateBucketExistenceConfirmed as Pr, normalizeAwsTagsToCfn as Pt, isExportAliasCollision as Q, assertAssetBucketRegion as Qn, resetAwsClients as Qr, s3BucketDualStackDomainName as Qt, coerceWarmThroughput as R, importableOutputKeys as Rn, MIGRATE_TMP_PREFIX as Rr, coerceCfnInteger as Rt, PRE_DELETE_SNAPSHOT_TYPES as S, normalizeAwsError as Si, maskSecretsInText as Sn, getDockerImageBySourceHash as Sr, isTerminationProtectionPropagationError as St, createPreDeleteFinalSnapshot as T, isRetryableTransientError as Ti, redactSecretsForState as Tn, getDefaultStateBucketName as Tr, cfnRefValueFromPhysicalId as Tt, bold as U, AssetPublisher as Un, PARTITION_TABLE as Ur, replayWarn as Ut, toFiniteNumber as V, orphansCarriedFrom as Vn, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Vr, configStringRefusal as Vt, cyan as W, stringifyValue as Wn, canonicalizeRegion as Wr, requireConfigArray as Wt, collectPublishedOutputNames as X, AssetModeResolver as Xn, AwsClients as Xr, s3BucketArn as Xt, collectDeclaredOutputNames as Y, rewriteTemplateAssetReferences as Yn, resolveBucketRegion as Yr, withSharedDrainBudget as Yt, exportAliasCollisionScrubWarning as Z, BOOTSTRAP_MARKER_PREFIX as Zn, getAwsClients as Zr, s3BucketDomainName as Zt, isSecretBearingReferenceString as _, StackTerminationProtectionError as _i, errorCauseChain as _n, runDockerForeground as _r, slowCcOperationTimeoutMs as _t, DeploymentEventsStore as a, DeployCancelledError as ai, findSilentDropProperties as an, validateContainerRepoName as ar, clearOnUpdateRemoval as at, bindingSkippedOutputs as b, formatError as bi, markSameGenerationBag as bn, stripControlChars as br, deleteSkipReason as bt, replayFailedOperations as c, LocalInvokeBuildError as ci, DagBuilder as cn, buildDockerImage as cr, createMaskedRetryLogger as ct, updatePartialReason as d, NestedStackChildDirectDestroyError as di, STATE_SOURCED_CROSS_GENERATION_RULES as dn, describeDockerFailure as dr, beginCommandInterruptScope as dt, AssetError as ei, s3BucketWebsiteUrl as en, getBootstrapMarkerKey as er, secretBearingStateKeyWarning as et, withResourceDeadline as f, PartialFailureError as fi, STATE_SOURCED_READBACK_RULES as fn, dockerSpawnEnvWithSensitive as fr, endCommandInterruptScope as ft, bagHoldsSecretExpression as g, StackHasActiveImportsError as gi, dynamicReferenceTokens as gn, redactDockerArgvValues as gr, CloudControlProvider as gt, computeImplicitDeleteEdges as h, ResourceUpdateNotSupportedError as hi, createSecretMasker as hn, partitionSensitiveEnv as hr, startInterruptWatch as ht, DeploymentEventsReader as i, DependencyError as ii, findActionableSilentDrops as in, validateAssetBucketName as ir, collectInlinePolicyNamesManagedBySiblings as it, makeSiblingClaimReader as j, __exportAll as ji, buildLockContentionMessage as jn, resolveStateBucketWithDefault as jr, refStateLookupFromResource as jt, makeCanonicalizePropertiesFn as k, markRedactedCause as ki, rebuildClientForBucketRegion as kn, resolveCaptureObservedState as kr, isUnboundTemplateParameter as kt, replayRollback as l, LocalStartServiceError as li, TemplateParser as ln, describeDockerCapturedOutput as lr, maskDeep as lt, IMPLICIT_DELETE_DEPENDENCIES as m, ResourceTimeoutError as mi, carriesSecretMask as mn, getDockerCmd as mr, isInterruptedWaitError as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, ConfigError as ni, DiffCalculator as nn, parseBootstrapMarker as nr, getCurrentResourceSecrets as nt, planFailedOps as o, DynamicReferenceRegionAmbiguousError as oi, describeTypeWithThrottleRetry as on, buildDenyExternalAccessPolicy as or, ProviderRegistry as ot, maskingRetryLogger as p, ProvisioningError as pi, TEMPLATE_SOURCED_RULES as pn, formatDockerLoginError as pr, interruptWatchListenerCount as pt, red as q, createAssetRedirectResolver as qn, processStackMessages as qr, classifyReplaySecretRegion as qt, DeployEngine as r, CrossAccountSecretRefusalError as ri, INTRINSIC_KEYS as rn, readBootstrapMarkerBody as rr, IAMRoleProvider as rt, planRollback as s, IntrinsicResolutionRefusalError as si, withRetry as sn, describeAwsFailure as sr, wouldReturnToSdkProvider as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, CdkdError as ti, applyRoleArnIfSet as tn, isCrossRegionRedirect as tr, secretSafeKeyDisplay as tt, updatePartialMessage as u, LockError as ui, STATE_SOURCED_BASELINE_RULES as un, describeDockerExecFailure as ur, maskerOrIdentity as ut, keptWholeReasonText as v, StateError as vi, identityKeyFor as vn, runDockerStreaming as vr, UNSPECIFIED_SKIP_REASON as vt, ccRoutedFinalSnapshotError as w, isMarkedNonRetryable as wi, recoverMaskedOutput as wn, synthesisStatusMessage as wr, carriesDynamicReference as wt, ATOMIC_FINAL_SNAPSHOT_TYPES as x, isCdkdError as xi, maskSecretsInError as xn, AssetManifestLoader as xr, disableInstanceApiTermination as xt, mergeNoChangeOutputs as y, SynthesisError as yi, isSingleDynamicReferenceToken as yn, escapeRegExp$1 as yr, deleteIndeterminateGuards as yt, isWarmThroughputDecrease as z, importableOutputs as zn, findLargeInlineResources as zr, configBooleanRefusal as zt };
42043
- //# sourceMappingURL=deploy-engine-e6ZxOr56.js.map
42381
+ export { secretBearing as $, BOOTSTRAP_MARKER_PREFIX as $n, getAwsClients as $r, s3BucketRegionalDomainName as $t, extractDeploymentEventError as A, markNonRetryable as Ai, buildForceUnlockCommand as An, resolveAutoAssetStorage as Ar, parameterTypeMayLoseSecretIdentity as At, toCfnInteger as B, importableOutputs as Bn, MIGRATE_TMP_PREFIX as Br, configIntegerRefusal as Bt, buildFinalSnapshotIdentifier as C, isCdkdError as Ci, recordMaskOnlyValue as Cn, AssetManifestLoader as Cr, IntrinsicFunctionResolver as Ct, refusesFinalSnapshot as D, isRetryableTransientError as Di, LockManager as Dn, getDefaultStateBucketName as Dr, getAccountInfo as Dt, isFinalSnapshotError as E, isMarkedNonRetryable as Ei, scrubResourceRecord as En, synthesisStatusMessage as Er, coerceParameterTypedValue as Et, isStatefulRecreateTargetSync as F, CUSTOM_RESOURCE_RESPONSE_PREFIX as Fn, resolveUseCdkBootstrapAssets as Fr, resolveExplicitPhysicalId as Ft, gray as G, AssetPublisher as Gn, PARTITION_TABLE as Gr, requireConfigObject as Gt, formatResourceLine as H, orphansAfterRollback as Hn, uploadCfnTemplate as Hr, readConfigString as Ht, renderStatefulReason as I, DEFAULT_STATE_PREFIX as In, stateBucketExistenceConfirmed as Ir, assertRegionMatch as It, yellow as J, buildAssetRedirectMap as Jn, AssemblyReader as Jr, producerRegionsFromState as Jt, green as K, stringifyValue as Kn, canonicalizeRegion as Kr, requireConfigString as Kt, WARM_THROUGHPUT_MEMBERS as L, exportNamesCarriedFrom as Ln, warnDeprecatedNoPrefixCliFlag as Lr, coerceCfnBoolean as Lt, planOrphanAdoption as M, retryClassificationText as Mi, forceQuitRecoveryClause as Mn, resolveSkipPrefix as Mr, isSettledInstanceState as Mt, MULTI_REGION_RECREATE_BLOCKED_TYPES as N, __exportAll as Ni, formatLockExpiry as Nn, resolveStateBucketWithDefault as Nr, WAFv2WebACLProvider as Nt, unsupportedFinalSnapshotError as O, isThrottlingError as Oi, S3StateBackend as On, getLegacyStateBucketName as Or, isImpossibleEmptyStoredAttribute as Ot, isStatefulRecreateTargetForReplace as P, shellQuote as Pn, resolveStateBucketWithDefaultAndSource as Pr, normalizeAwsTagsToCfn as Pt, isExportAliasCollision as Q, AssetModeResolver as Qn, AwsClients as Qr, s3BucketDualStackDomainName as Qt, coerceWarmThroughput as R, hasReadableExportSet as Rn, CFN_TEMPLATE_BODY_LIMIT as Rr, coerceCfnInteger as Rt, PRE_DELETE_SNAPSHOT_TYPES as S, formatError as Si, maskSecretsInText as Sn, stripControlChars as Sr, isTerminationProtectionPropagationError as St, createPreDeleteFinalSnapshot as T, withErrorHandling as Ti, redactSecretsForState as Tn, Synthesizer as Tr, cfnRefValueFromPhysicalId as Tt, bold as U, orphansCarriedFrom as Un, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Ur, replayWarn as Ut, toFiniteNumber as V, isReadableBag as Vn, findLargeInlineResources as Vr, configStringRefusal as Vt, cyan as W, shouldRetainResource as Wn, expectedOwnerParam as Wr, requireConfigArray as Wt, collectPublishedOutputNames as X, loadPublishableAssetManifest as Xn, clearBucketRegionCache as Xr, s3BucketArn as Xt, collectDeclaredOutputNames as Y, createAssetRedirectResolver as Yn, processStackMessages as Yr, withSharedDrainBudget as Yt, exportAliasCollisionScrubWarning as Z, rewriteTemplateAssetReferences as Zn, resolveBucketRegion as Zr, s3BucketDomainName as Zt, isSecretBearingReferenceString as _, ResourceUpdateNotSupportedError as _i, errorCauseChain as _n, partitionSensitiveEnv as _r, slowCcOperationTimeoutMs as _t, DeploymentEventsStore as a, CrossAccountSecretRefusalError as ai, findSilentDropProperties as an, readBootstrapMarkerBody as ar, clearOnUpdateRemoval as at, bindingSkippedOutputs as b, StateError as bi, markSameGenerationBag as bn, runDockerStreaming as br, deleteSkipReason as bt, replayFailedOperations as c, DynamicReferenceRegionAmbiguousError as ci, DagBuilder as cn, buildDenyExternalAccessPolicy as cr, createMaskedRetryLogger as ct, updatePartialReason as d, LocalStartServiceError as di, STATE_SOURCED_CROSS_GENERATION_RULES as dn, describeDockerCapturedOutput as dr, beginCommandInterruptScope as dt, resetAwsClients as ei, s3BucketWebsiteUrl as en, assertAssetBucketRegion as er, secretBearingStateKeyWarning as et, withResourceDeadline as f, LockError as fi, STATE_SOURCED_READBACK_RULES as fn, describeDockerExecFailure as fr, endCommandInterruptScope as ft, bagHoldsSecretExpression as g, ResourceTimeoutError as gi, dynamicReferenceTokens as gn, getDockerCmd as gr, CloudControlProvider as gt, computeImplicitDeleteEdges as h, ProvisioningError as hi, createSecretMasker as hn, formatDockerLoginError as hr, startInterruptWatch as ht, DeploymentEventsReader as i, ConfigError as ii, findActionableSilentDrops as in, parseBootstrapMarker as ir, collectInlinePolicyNamesManagedBySiblings as it, makeSiblingClaimReader as j, markRedactedCause as ji, buildLockContentionMessage as jn, resolveCaptureObservedState as jr, refStateLookupFromResource as jt, makeCanonicalizePropertiesFn as k, isTransientServerError as ki, rebuildClientForBucketRegion as kn, resolveApp as kr, isUnboundTemplateParameter as kt, replayRollback as l, IntrinsicResolutionRefusalError as li, TemplateParser as ln, describeAwsFailure as lr, maskDeep as lt, IMPLICIT_DELETE_DEPENDENCIES as m, PartialFailureError as mi, carriesSecretMask as mn, dockerSpawnEnvWithSensitive as mr, isInterruptedWaitError as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, AssetError as ni, DiffCalculator as nn, getBootstrapMarkerKey as nr, getCurrentResourceSecrets as nt, planFailedOps as o, DependencyError as oi, describeTypeWithThrottleRetry as on, validateAssetBucketName as or, ProviderRegistry as ot, maskingRetryLogger as p, NestedStackChildDirectDestroyError as pi, TEMPLATE_SOURCED_RULES as pn, describeDockerFailure as pr, interruptWatchListenerCount as pt, red as q, WorkGraph as qn, derivePartitionAndUrlSuffix as qr, classifyReplaySecretRegion as qt, DeployEngine as r, CdkdError as ri, INTRINSIC_KEYS as rn, isCrossRegionRedirect as rr, IAMRoleProvider as rt, planRollback as s, DeployCancelledError as si, withRetry as sn, validateContainerRepoName as sr, wouldReturnToSdkProvider as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, setAwsClients as ti, applyRoleArnIfSet as tn, ensureAssetStorage as tr, secretSafeKeyDisplay as tt, updatePartialMessage as u, LocalInvokeBuildError as ui, STATE_SOURCED_BASELINE_RULES as un, buildDockerImage as ur, maskerOrIdentity as ut, keptWholeReasonText as v, StackHasActiveImportsError as vi, identityKeyFor as vn, redactDockerArgvValues as vr, UNSPECIFIED_SKIP_REASON as vt, ccRoutedFinalSnapshotError as w, normalizeAwsError as wi, recoverMaskedOutput as wn, getDockerImageBySourceHash as wr, carriesDynamicReference as wt, ATOMIC_FINAL_SNAPSHOT_TYPES as x, SynthesisError as xi, maskSecretsInError as xn, escapeRegExp$1 as xr, disableInstanceApiTermination as xt, mergeNoChangeOutputs as y, StackTerminationProtectionError as yi, isSingleDynamicReferenceToken as yn, runDockerForeground as yr, deleteIndeterminateGuards as yt, isWarmThroughputDecrease as z, importableOutputKeys as zn, CFN_TEMPLATE_URL_LIMIT as zr, configBooleanRefusal as zt };
42382
+ //# sourceMappingURL=deploy-engine-v7h1H_Ac.js.map