@go-to-k/cdkd 0.290.21 → 0.290.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{asg-provider-B8GAVns4.js → asg-provider-BOYln2Tw.js} +10 -10
- package/dist/asg-provider-BOYln2Tw.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-KnCjibgq.js → deploy-engine-CBzq85r6.js} +510 -79
- package/dist/deploy-engine-CBzq85r6.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{logger-C17H06QA.js → logger-CkHVgzLx.js} +7 -1
- package/dist/logger-CkHVgzLx.js.map +1 -0
- package/dist/{program-BLyj1ijG.js → program-BgbPkrcx.js} +192 -189
- package/dist/{program-BLyj1ijG.js.map → program-BgbPkrcx.js.map} +1 -1
- package/dist/{version-CGcB3KHZ.js → version-DxUVmvM5.js} +2 -2
- package/dist/{version-CGcB3KHZ.js.map → version-DxUVmvM5.js.map} +1 -1
- package/package.json +1 -1
- package/dist/asg-provider-B8GAVns4.js.map +0 -1
- package/dist/deploy-engine-KnCjibgq.js.map +0 -1
- package/dist/logger-C17H06QA.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as displaySafe, S as displayIdent, b as STACK_REF_MAX_CODE_POINTS, d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName, w as truncateCodePoints, x as UNRENDERABLE, y as withoutGeneratedFallbackName } from "./logger-
|
|
1
|
+
import { C as displaySafe, S as displayIdent, b as STACK_REF_MAX_CODE_POINTS, d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName, w as truncateCodePoints, x as UNRENDERABLE, y as withoutGeneratedFallbackName } from "./logger-CkHVgzLx.js";
|
|
2
2
|
import { a as setAssumedRoleCredentials, i as getPreAssumeEnvCredentials, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-BoIxyCir.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-DxUVmvM5.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";
|
|
@@ -7360,7 +7360,7 @@ function repairSpawnRefusal(text, args, maskedArgs) {
|
|
|
7360
7360
|
* error would only say "Command failed with exit code N".
|
|
7361
7361
|
*/
|
|
7362
7362
|
function describeDockerExecFailure(error, args) {
|
|
7363
|
-
const message = thrownMessageText(error) || safeStringify(error);
|
|
7363
|
+
const message = thrownMessageText(error) || safeStringify$1(error);
|
|
7364
7364
|
const stderrText = capturedStreamText(error, "stderr");
|
|
7365
7365
|
return redactDockerArgvInText(stderrText ? `${message}\n${stderrText}` : message, args);
|
|
7366
7366
|
}
|
|
@@ -7403,7 +7403,7 @@ function thrownMessageText(error) {
|
|
|
7403
7403
|
* two of these call sites are in `cleanupEcsRun`, where an exception aborts
|
|
7404
7404
|
* the remaining volume / network teardown and leaks real Docker resources.
|
|
7405
7405
|
*/
|
|
7406
|
-
function safeStringify(error) {
|
|
7406
|
+
function safeStringify$1(error) {
|
|
7407
7407
|
try {
|
|
7408
7408
|
return String(error);
|
|
7409
7409
|
} catch {
|
|
@@ -7429,7 +7429,7 @@ function safeStringify(error) {
|
|
|
7429
7429
|
* wrapper text needs the status as well.
|
|
7430
7430
|
*/
|
|
7431
7431
|
function describeDockerFailure(error, args) {
|
|
7432
|
-
return redactDockerArgvInText(capturedStreamText(error, "stderr") || thrownMessageText(error) || safeStringify(error), args);
|
|
7432
|
+
return redactDockerArgvInText(capturedStreamText(error, "stderr") || thrownMessageText(error) || safeStringify$1(error), args);
|
|
7433
7433
|
}
|
|
7434
7434
|
/**
|
|
7435
7435
|
* A `cause` that is safe to CHAIN onto a wrapper error, for a caller that
|
|
@@ -8019,6 +8019,40 @@ function isAwsAuthoredFailure(error) {
|
|
|
8019
8019
|
return candidate.$fault !== void 0 || typeof candidate.$metadata?.httpStatusCode === "number" || error.name === "CredentialsProviderError";
|
|
8020
8020
|
}
|
|
8021
8021
|
/**
|
|
8022
|
+
* Exactly what `String(value)` produces, without the throw.
|
|
8023
|
+
*
|
|
8024
|
+
* `String(value)` is the only spelling that carries an `Error`'s NAME as well
|
|
8025
|
+
* as its message (`"AccessDenied: User ... is not authorized"`), and several
|
|
8026
|
+
* call sites depend on that: four build a persisted `outcome: 'partial'`
|
|
8027
|
+
* reason where the wire code is the discriminator an operator reads first.
|
|
8028
|
+
* {@link describeAwsFailure}'s `detail` is the MESSAGE only, so it is NOT a
|
|
8029
|
+
* drop-in for the bare form -- substituting it silently deletes the code.
|
|
8030
|
+
* Measured, after a sweep did exactly that at nine sites.
|
|
8031
|
+
*
|
|
8032
|
+
* So this is the 1:1 replacement: same output for every value `String` can
|
|
8033
|
+
* convert, and a stable sentence for the ones it cannot. A null-prototype
|
|
8034
|
+
* object throws `TypeError: Cannot convert object to primitive value`, and an
|
|
8035
|
+
* object with a hostile or `null` `toString` throws whatever it likes -- inside
|
|
8036
|
+
* a catch, that replaces the failure being reported.
|
|
8037
|
+
*
|
|
8038
|
+
* Use `describeAwsFailure(x).detail` where the site previously read
|
|
8039
|
+
* `x instanceof Error ? x.message : String(x)`; use THIS where it read a bare
|
|
8040
|
+
* `String(x)`.
|
|
8041
|
+
*
|
|
8042
|
+
* NOT a substitute for `displaySafe` in `display-safe.ts`, which guards the
|
|
8043
|
+
* same coercion and answers differently on purpose -- it falls back to
|
|
8044
|
+
* `Object.prototype.toString` and, more importantly, SANITISES the result for a
|
|
8045
|
+
* terminal. Anything about to be rendered or logged goes through that one; this
|
|
8046
|
+
* one is for the text a catch is about to record.
|
|
8047
|
+
*/
|
|
8048
|
+
function safeStringify(value) {
|
|
8049
|
+
try {
|
|
8050
|
+
return String(value);
|
|
8051
|
+
} catch {
|
|
8052
|
+
return "a value that could not be converted to text";
|
|
8053
|
+
}
|
|
8054
|
+
}
|
|
8055
|
+
/**
|
|
8022
8056
|
* Describe a caught failure for a thrown message. See {@link AwsFailureText}.
|
|
8023
8057
|
*/
|
|
8024
8058
|
function describeAwsFailure(error) {
|
|
@@ -11935,6 +11969,84 @@ function resolvedPlaintextOf(secrets, expression) {
|
|
|
11935
11969
|
function resolvedExpressionsOf(secrets) {
|
|
11936
11970
|
return [...resolvedPairsOf.get(secrets)?.keys() ?? []];
|
|
11937
11971
|
}
|
|
11972
|
+
/** Poison for an intrinsic object one pass resolved two different ways. */
|
|
11973
|
+
const CONFLICTING_LEAF_RESOLUTION = Symbol("conflicting leaf resolution");
|
|
11974
|
+
/**
|
|
11975
|
+
* {@link IntrinsicLeafResolution}s per pass bag, keyed by the template OBJECT
|
|
11976
|
+
* that was resolved (issue [#3156](https://github.com/go-to-k/cdkd/issues/3156)).
|
|
11977
|
+
* The carry in {@link recordNestedStackParameterExpressions} reads it for the
|
|
11978
|
+
* very object the deploy engine handed the resolver, so its evidence is THIS
|
|
11979
|
+
* leaf's own resolution: no other leaf's substitution, however equal its value,
|
|
11980
|
+
* can answer for it, and each substitution carries its own verdict, which the
|
|
11981
|
+
* secret-only pair table cannot (an unclassifiable `ssm` answer recorded as a
|
|
11982
|
+
* pair looks the same as a later public answer with the same plaintext).
|
|
11983
|
+
* Both keys are weak, so a record lives as long as its bag and its template.
|
|
11984
|
+
*/
|
|
11985
|
+
const intrinsicLeafResolutionsOf = /* @__PURE__ */ new WeakMap();
|
|
11986
|
+
/**
|
|
11987
|
+
* Record how the pass that owns `secrets` resolved the intrinsic object
|
|
11988
|
+
* `source`. A second resolution of the same object that differs in any field
|
|
11989
|
+
* poisons it for the pass, and the poison is never lifted; an identical one
|
|
11990
|
+
* changes nothing.
|
|
11991
|
+
*/
|
|
11992
|
+
function recordIntrinsicLeafResolution(secrets, source, resolution) {
|
|
11993
|
+
let leaves = intrinsicLeafResolutionsOf.get(secrets);
|
|
11994
|
+
if (leaves === void 0) {
|
|
11995
|
+
leaves = /* @__PURE__ */ new WeakMap();
|
|
11996
|
+
intrinsicLeafResolutionsOf.set(secrets, leaves);
|
|
11997
|
+
}
|
|
11998
|
+
const copy = {
|
|
11999
|
+
input: resolution.input,
|
|
12000
|
+
output: resolution.output,
|
|
12001
|
+
substitutions: resolution.substitutions.map(({ token, value, secret }) => ({
|
|
12002
|
+
token,
|
|
12003
|
+
value,
|
|
12004
|
+
secret
|
|
12005
|
+
})),
|
|
12006
|
+
complete: resolution.complete
|
|
12007
|
+
};
|
|
12008
|
+
const previous = leaves.get(source);
|
|
12009
|
+
if (previous === void 0) leaves.set(source, copy);
|
|
12010
|
+
else if (previous === CONFLICTING_LEAF_RESOLUTION || !sameLeafResolution(previous, copy)) leaves.set(source, CONFLICTING_LEAF_RESOLUTION);
|
|
12011
|
+
}
|
|
12012
|
+
function sameLeafResolution(a, b) {
|
|
12013
|
+
return a.input === b.input && a.output === b.output && a.complete === b.complete && a.substitutions.length === b.substitutions.length && a.substitutions.every((s, i) => s.token === b.substitutions[i].token && s.value === b.substitutions[i].value && s.secret === b.substitutions[i].secret);
|
|
12014
|
+
}
|
|
12015
|
+
/**
|
|
12016
|
+
* The spelling `source`'s own resolution in this pass proves for
|
|
12017
|
+
* `resolvedValue`, or `undefined` (issue
|
|
12018
|
+
* [#3156](https://github.com/go-to-k/cdkd/issues/3156)). Certified only when
|
|
12019
|
+
* the resolution replaced every token it met, replaced exactly ONE, took a
|
|
12020
|
+
* SECRET verdict for it, and produced `resolvedValue` from `input` by that one
|
|
12021
|
+
* replacement ({@link singleSpanFrame} over `input` with the token and value
|
|
12022
|
+
* equal). That is the observed equation `resolvedValue = input[token := value]`;
|
|
12023
|
+
* it says nothing about what a later lookup of the token returns.
|
|
12024
|
+
*
|
|
12025
|
+
* The `secret` test and the COUNT are independent. The count refuses a
|
|
12026
|
+
* replacement listed ahead of the one the frame names that left its plaintext,
|
|
12027
|
+
* not its token, in `input` -- a used Sub variable holding the SAME token as
|
|
12028
|
+
* the template (`port:T${V}` with `V` = `T`), whose first replacement passes
|
|
12029
|
+
* the token and value tests while `input` keeps the variable's plaintext.
|
|
12030
|
+
* `complete`, `output` and the token and value tests are implied, for every
|
|
12031
|
+
* shape the resolver records, by the count and `singleSpanFrame` over `input`:
|
|
12032
|
+
* a token left unreplaced in `input` is a second span, and a frame that fits
|
|
12033
|
+
* `input` around the one replacement fixes the output it produces. They are
|
|
12034
|
+
* kept so a recorder that is not faithful refuses rather than certifies, and
|
|
12035
|
+
* are pinned by hand-built records only.
|
|
12036
|
+
*/
|
|
12037
|
+
function substitutedSpellingOf(secrets, source, resolvedValue) {
|
|
12038
|
+
const resolution = intrinsicLeafResolutionsOf.get(secrets)?.get(source);
|
|
12039
|
+
if (resolution === void 0 || resolution === CONFLICTING_LEAF_RESOLUTION) return void 0;
|
|
12040
|
+
if (!resolution.complete || resolution.output !== resolvedValue) return void 0;
|
|
12041
|
+
if (resolution.substitutions.length !== 1) return void 0;
|
|
12042
|
+
const [substitution] = resolution.substitutions;
|
|
12043
|
+
if (!substitution.secret) return void 0;
|
|
12044
|
+
const frame = singleSpanFrame(resolvedValue, resolution.input);
|
|
12045
|
+
if (frame === void 0) return void 0;
|
|
12046
|
+
if (frame.token !== substitution.token || frame.middle !== substitution.value) return void 0;
|
|
12047
|
+
for (const plaintext of secrets.keys()) if (plaintext !== "" && (frame.prefix.includes(plaintext) || frame.suffix.includes(plaintext))) return;
|
|
12048
|
+
return resolution.input;
|
|
12049
|
+
}
|
|
11938
12050
|
/**
|
|
11939
12051
|
* The bag OBJECTS a deploy pass produced ITSELF and installed on a success
|
|
11940
12052
|
* path — the fact the resolved-pair evidence above cannot state (issue
|
|
@@ -12886,8 +12998,9 @@ const UNFRAMED_SPELLING = Symbol("cdkd.nested-parameter.unframed-spelling");
|
|
|
12886
12998
|
* literal source; pinned by the "map NO RESOLVER populated" case,
|
|
12887
12999
|
* where (ii) and (iii) both pass and only the missing pair refuses.
|
|
12888
13000
|
* On an OBJECT source (`Fn::Join` / `Fn::Sub`, issue #3062) there is
|
|
12889
|
-
* no source text to compare, so the spelling
|
|
12890
|
-
* admitted only when `positioned[name] !==
|
|
13001
|
+
* no source text to compare, so the spelling-based arm takes what
|
|
13002
|
+
* the pass WROTE, admitted only when `positioned[name] !==
|
|
13003
|
+
* resolvedValue`. Sound
|
|
12891
13004
|
* together with (ii), not alone: on a value the scan is silent on,
|
|
12892
13005
|
* the cross-stack and skeleton arms both refuse (each requires the
|
|
12893
13006
|
* WHOLE leaf to be a key of the map, which would make the scan's
|
|
@@ -12895,16 +13008,23 @@ const UNFRAMED_SPELLING = Symbol("cdkd.nested-parameter.unframed-spelling");
|
|
|
12895
13008
|
* scan leaves the leaf alone -- so the frame arm is the only writer
|
|
12896
13009
|
* that can have rewritten it, on its pair and the engine's mark. A
|
|
12897
13010
|
* frame that is not wholly literal makes that arm refuse, the leaf
|
|
12898
|
-
* comes back unrewritten, and
|
|
12899
|
-
*
|
|
13011
|
+
* comes back unrewritten, and this arm refuses too. It certifies
|
|
13012
|
+
* only a source whose OWN rendered token starts with a
|
|
12900
13013
|
* spelled-secret prefix (`secretsmanager:` / `ssm-secure:`), read from
|
|
12901
13014
|
* the source and never from what the pass wrote: the frame arm
|
|
12902
13015
|
* matches among RECORDED references and a public `ssm` parameter
|
|
12903
13016
|
* records none, so a public leaf whose source spells `ssm:`, or leaves
|
|
12904
13017
|
* the service to an intrinsic part, can match a recorded secret
|
|
12905
|
-
* sibling and be written as the sibling's reference.
|
|
12906
|
-
*
|
|
12907
|
-
*
|
|
13018
|
+
* sibling and be written as the sibling's reference. Where it refuses,
|
|
13019
|
+
* the PROVENANCE arm (issue #3156) certifies on the leaf's own
|
|
13020
|
+
* resolution instead: the resolver records, per `Fn::Join` /
|
|
13021
|
+
* `Fn::Sub` object and pass bag, every token it replaced with the
|
|
13022
|
+
* verdict that replacement took, and one SECRET replacement that turns
|
|
13023
|
+
* the record's input into the value certifies that input as the
|
|
13024
|
+
* spelling -- an `ssm:` or intrinsic-service token, and a frame whose
|
|
13025
|
+
* non-literal part sits OUTSIDE the token, which the frame arm never
|
|
13026
|
+
* writes. `frameSpellingOf` states what it proves and what it does
|
|
13027
|
+
* not. The STATE-derived
|
|
12908
13028
|
* call sites reach this arm as well: `cdkd import` can leave raw
|
|
12909
13029
|
* intrinsics in a record's `properties`, which a rollback replay
|
|
12910
13030
|
* passes as its source, and the frame arm does not read `rules`.
|
|
@@ -12940,10 +13060,9 @@ const UNFRAMED_SPELLING = Symbol("cdkd.nested-parameter.unframed-spelling");
|
|
|
12940
13060
|
* (i)-(iii) on its own), a leaf (iii) refused under a different
|
|
12941
13061
|
* frame, an object spelling the frame arm refused and a plain
|
|
12942
13062
|
* literal equal to the value all fail it and would take the entry's
|
|
12943
|
-
* frame (an object spelling
|
|
12944
|
-
*
|
|
12945
|
-
*
|
|
12946
|
-
* would a
|
|
13063
|
+
* frame (an object spelling (i) certified is a frame like a literal
|
|
13064
|
+
* one, issues #3062 and #3156; one it refused counts as unframed) --
|
|
13065
|
+
* and so would a
|
|
12947
13066
|
* same-frame sibling whose token this pass never resolved to the
|
|
12948
13067
|
* middle: a PUBLIC `ssm` reference in the same `port:` frame holding
|
|
12949
13068
|
* the same two characters is kept RESOLVED on the record, and the
|
|
@@ -12952,7 +13071,13 @@ const UNFRAMED_SPELLING = Symbol("cdkd.nested-parameter.unframed-spelling");
|
|
|
12952
13071
|
* the row are gathered BEFORE the conditions run, a spelling that is
|
|
12953
13072
|
* not a single-span literal frame with this pass's pair for its token
|
|
12954
13073
|
* (`resolvedPlaintextOf`) counting as its own, and a value is
|
|
12955
|
-
* recorded only when its certified frame is the row's only one.
|
|
13074
|
+
* recorded only when its certified frame is the row's only one. A
|
|
13075
|
+
* value one of whose leaves the position pass did NOT write (the
|
|
13076
|
+
* provenance arm's outside-the-token frame) also counts as unframed
|
|
13077
|
+
* unless every framed spelling of it carries ONE token: the frame arm
|
|
13078
|
+
* refuses that leaf on the parent's record, so the entry is written
|
|
13079
|
+
* onto it by the value scan whatever its token, and (iii)'s rescue
|
|
13080
|
+
* for a same-frame sibling does not reach it.
|
|
12956
13081
|
* (v) No OTHER string leaf of the ROW would be rewritten by the entry,
|
|
12957
13082
|
* read over every leaf of the resolved row since the bag is per
|
|
12958
13083
|
* resource, not per `Parameters`. Two arms of the persist walk read
|
|
@@ -13038,16 +13163,29 @@ const UNFRAMED_SPELLING = Symbol("cdkd.nested-parameter.unframed-spelling");
|
|
|
13038
13163
|
* persists the plaintext -- the same class as the nonliteral-frame deferral.
|
|
13039
13164
|
* (e) CLOSED for a wholly literal frame by (i)'s object arm (issue #3062;
|
|
13040
13165
|
* was: an `Fn::Join` / `Fn::Sub` source in the parent's own `Parameters`
|
|
13041
|
-
* block was never carried)
|
|
13042
|
-
*
|
|
13043
|
-
*
|
|
13044
|
-
* the
|
|
13045
|
-
*
|
|
13046
|
-
*
|
|
13047
|
-
*
|
|
13048
|
-
*
|
|
13049
|
-
*
|
|
13050
|
-
*
|
|
13166
|
+
* block was never carried), and for the two spellings that arm refused -- a
|
|
13167
|
+
* token spelling `ssm:` or leaving its service to an intrinsic part, and a
|
|
13168
|
+
* frame whose non-literal part sits outside the token -- by its provenance
|
|
13169
|
+
* arm (issue #3156), which on the second also rewrites the PARENT's record of
|
|
13170
|
+
* the leaf through the entry. What remains of it: a leaf whose own resolution
|
|
13171
|
+
* does not certify -- the token spelled inside a NESTED intrinsic part (the
|
|
13172
|
+
* outer object's own text spells none), the token held by a used `Fn::Sub`
|
|
13173
|
+
* variable (the template spells a placeholder there), a leaf resolved two
|
|
13174
|
+
* different ways in one pass, one whose replacement took a public verdict, one
|
|
13175
|
+
* that replaced more than one token, one whose affix holds a plaintext the bag
|
|
13176
|
+
* holds (another secret a non-literal part resolved), an `Fn::If` around the
|
|
13177
|
+
* object (which the skeleton cannot render, and whose record is under the
|
|
13178
|
+
* selected branch) -- keeps the plaintext in the child, and for the
|
|
13179
|
+
* outside-the-token frame in the parent's record; so does an outside-the-token
|
|
13180
|
+
* frame sharing its value with a leaf of another token, which (iv) refuses. A
|
|
13181
|
+
* rollback replay records nothing here (`resolveReplayProps` resolves strings,
|
|
13182
|
+
* not intrinsic objects): a journal a deploy wrote holds a carried frame's
|
|
13183
|
+
* persisted spelling as a STRING, which the literal arm reads, while a record
|
|
13184
|
+
* `cdkd import` left holding the raw intrinsic stays refused on this arm
|
|
13185
|
+
* (go-to-k/cdkd#3306 tracks the refused shapes). Any other resolved
|
|
13186
|
+
* text a non-literal part contributes is carried verbatim in the spelling, as
|
|
13187
|
+
* the value already carries it.
|
|
13188
|
+
* (f) A child record persisted BEFORE this carry
|
|
13051
13189
|
* keeps `port:q7` until the child is next redeployed: the parent's own row
|
|
13052
13190
|
* already held the frame (the literal or frame arm), so a parent deploy whose child
|
|
13053
13191
|
* row is unchanged never re-runs the child, and `cdkd scrub` cannot repair
|
|
@@ -13083,12 +13221,19 @@ function recordNestedStackParameterExpressions(secrets, resourceType, resolvedPr
|
|
|
13083
13221
|
}
|
|
13084
13222
|
const framesByValue = /* @__PURE__ */ new Map();
|
|
13085
13223
|
const tokensOf = /* @__PURE__ */ new Map();
|
|
13086
|
-
const
|
|
13224
|
+
const frameSpellings = /* @__PURE__ */ new Map();
|
|
13225
|
+
const frameSpellingOf = (name) => {
|
|
13226
|
+
if (!frameSpellings.has(name)) frameSpellings.set(name, computeFrameSpelling(name));
|
|
13227
|
+
return frameSpellings.get(name);
|
|
13228
|
+
};
|
|
13229
|
+
const computeFrameSpelling = (name) => {
|
|
13230
|
+
const resolvedValue = resolvedParameters[name];
|
|
13231
|
+
if (typeof resolvedValue !== "string") return void 0;
|
|
13087
13232
|
const sourceLeaf = sourceParameters[name];
|
|
13088
13233
|
if (typeof sourceLeaf === "string") return sourceLeaf;
|
|
13089
13234
|
if (!isPlainObject$2(sourceLeaf)) return void 0;
|
|
13090
13235
|
const written = positioned[name];
|
|
13091
|
-
if (typeof written !== "string"
|
|
13236
|
+
if (typeof written !== "string") return void 0;
|
|
13092
13237
|
const segments = intrinsicSkeletonSegments(sourceLeaf);
|
|
13093
13238
|
if (segments === void 0) return void 0;
|
|
13094
13239
|
const rendered = segments.map((s) => s === UNKNOWN_PART ? UNKNOWN_PART_PLACEHOLDER : s).join("");
|
|
@@ -13096,16 +13241,20 @@ function recordNestedStackParameterExpressions(secrets, resourceType, resolvedPr
|
|
|
13096
13241
|
if (spans.length !== 1) return void 0;
|
|
13097
13242
|
const [span] = spans;
|
|
13098
13243
|
const ownToken = rendered.slice(span.start, span.end);
|
|
13099
|
-
|
|
13244
|
+
if (written !== resolvedValue && SPELLED_SECRET_REFERENCE_PREFIXES.some((prefix) => ownToken.startsWith(prefix))) return written;
|
|
13245
|
+
const substituted = substitutedSpellingOf(secrets, sourceLeaf, resolvedValue);
|
|
13246
|
+
if (substituted === void 0) return void 0;
|
|
13247
|
+
return written === resolvedValue || written === substituted ? substituted : void 0;
|
|
13100
13248
|
};
|
|
13101
|
-
const certifiedSpellingOf = (name
|
|
13249
|
+
const certifiedSpellingOf = (name) => {
|
|
13102
13250
|
const sourceLeaf = sourceParameters[name];
|
|
13103
13251
|
if (typeof sourceLeaf === "string") return positioned[name] === sourceLeaf ? sourceLeaf : void 0;
|
|
13104
|
-
return frameSpellingOf(name
|
|
13252
|
+
return frameSpellingOf(name);
|
|
13105
13253
|
};
|
|
13254
|
+
const unwrittenValues = /* @__PURE__ */ new Set();
|
|
13106
13255
|
for (const [name, resolvedValue] of Object.entries(resolvedParameters)) {
|
|
13107
13256
|
if (typeof resolvedValue !== "string") continue;
|
|
13108
|
-
const spelling = frameSpellingOf(name
|
|
13257
|
+
const spelling = frameSpellingOf(name);
|
|
13109
13258
|
const frame = spelling === void 0 ? void 0 : singleSpanFrame(resolvedValue, spelling);
|
|
13110
13259
|
const frames = framesByValue.get(resolvedValue) ?? /* @__PURE__ */ new Set();
|
|
13111
13260
|
const unframed = frame === void 0 || resolvedPlaintextOf(secrets, frame.token) !== frame.middle;
|
|
@@ -13115,8 +13264,10 @@ function recordNestedStackParameterExpressions(secrets, resourceType, resolvedPr
|
|
|
13115
13264
|
const tokens = tokensOf.get(resolvedValue) ?? /* @__PURE__ */ new Set();
|
|
13116
13265
|
tokens.add(frame.token);
|
|
13117
13266
|
tokensOf.set(resolvedValue, tokens);
|
|
13267
|
+
if (typeof sourceParameters[name] !== "string" && positioned[name] !== spelling) unwrittenValues.add(resolvedValue);
|
|
13118
13268
|
}
|
|
13119
13269
|
}
|
|
13270
|
+
for (const value of unwrittenValues) if (tokensOf.get(value)?.size !== 1) framesByValue.get(value)?.add(UNFRAMED_SPELLING);
|
|
13120
13271
|
const rowWithoutParameters = {};
|
|
13121
13272
|
for (const [key, value] of Object.entries(resolvedProperties)) if (key !== "Parameters") rowWithoutParameters[key] = value;
|
|
13122
13273
|
const outsideLeaves = wholeStringLeavesOf(rowWithoutParameters);
|
|
@@ -13124,7 +13275,7 @@ function recordNestedStackParameterExpressions(secrets, resourceType, resolvedPr
|
|
|
13124
13275
|
const framedByName = [];
|
|
13125
13276
|
for (const [name, resolvedValue] of Object.entries(resolvedParameters)) {
|
|
13126
13277
|
if (typeof resolvedValue !== "string") continue;
|
|
13127
|
-
const spelling = certifiedSpellingOf(name
|
|
13278
|
+
const spelling = certifiedSpellingOf(name);
|
|
13128
13279
|
if (spelling === void 0) continue;
|
|
13129
13280
|
if (redactSecretsForState(resolvedValue, secrets) !== resolvedValue) continue;
|
|
13130
13281
|
const frame = singleSpanFrame(resolvedValue, spelling);
|
|
@@ -17884,7 +18035,7 @@ function getCreateOnlyPropertyPaths(resourceType) {
|
|
|
17884
18035
|
if (cached) return cached;
|
|
17885
18036
|
const entry = fetchCreateOnlyPropertyPaths(resourceType).catch((error) => {
|
|
17886
18037
|
createOnlyPropertiesCache.delete(resourceType);
|
|
17887
|
-
const message =
|
|
18038
|
+
const message = describeAwsFailure(error).detail;
|
|
17888
18039
|
getLogger().child("CreateOnlyProperties").warn(`Failed to resolve create-only properties for ${resourceType} via cloudformation:DescribeType (${message}). Falling back to the registry-only replacement classification for this resource — an immutable-property change may be mis-classified as an in-place update. Grant cloudformation:DescribeType to enable schema-driven replacement detection.`);
|
|
17889
18040
|
return [];
|
|
17890
18041
|
});
|
|
@@ -21389,6 +21540,39 @@ function stackClause(stackName, region) {
|
|
|
21389
21540
|
const where = region === void 0 ? "" : ` (${shellQuote(safeIdentifier(region))})`;
|
|
21390
21541
|
return `State for ${shellQuote(safeIdentifier(stackName))}${where}`;
|
|
21391
21542
|
}
|
|
21543
|
+
/**
|
|
21544
|
+
* The DESTRUCTIVE remedy, built the way {@link inspectCommand} builds the
|
|
21545
|
+
* read-only one, and the region clause follows the SAME rule for two reasons
|
|
21546
|
+
* that pull in opposite directions.
|
|
21547
|
+
*
|
|
21548
|
+
* With a region in hand the flag is not decoration: `cdkd state orphan <stack>`
|
|
21549
|
+
* without it drops the record for that stack name in EVERY region, so omitting
|
|
21550
|
+
* it hands a stuck operator something wider than the message describes.
|
|
21551
|
+
*
|
|
21552
|
+
* With NO region the flag is worse than useless, and a placeholder is the worst
|
|
21553
|
+
* of the three: an absent region means a v1 record, which predates the
|
|
21554
|
+
* region-prefixed key layout, so `--stack-region <region>` selects no record at
|
|
21555
|
+
* all whatever the operator fills in. The bare command is the correct one
|
|
21556
|
+
* there, because such a record is not region-partitioned.
|
|
21557
|
+
*
|
|
21558
|
+
* With no trusted STACK the whole thing degrades to a template, as the inspect
|
|
21559
|
+
* command does — there is nothing to substitute.
|
|
21560
|
+
*
|
|
21561
|
+
* **It diverges from {@link malformedDestroyResourcesRefusalMessage}, which
|
|
21562
|
+
* keeps `cdkd state orphan` a template on purpose, and the difference is where
|
|
21563
|
+
* the identity comes from.** That builder is reached with a record-derived
|
|
21564
|
+
* REGION (`state.region ?? ctx.baseRegion` in `destroy-runner.ts`) -- its stack
|
|
21565
|
+
* name is the caller's, so the region alone is what would aim a destructive
|
|
21566
|
+
* command using a value the record supplied. This one is reached only with the caller's
|
|
21567
|
+
* synthesized stack and `pickStackRegion`'s answer, so substituting is safe and
|
|
21568
|
+
* omitting the region is the wider action. Two opposite precedents in one
|
|
21569
|
+
* module; neither is the general rule.
|
|
21570
|
+
*/
|
|
21571
|
+
function dropRecordCommand(stackName, region) {
|
|
21572
|
+
if (stackName === void 0 || stackName === "") return "cdkd state orphan <stack> --stack-region <region>";
|
|
21573
|
+
const flag = region === void 0 || region === "" ? "" : ` --stack-region ${shellQuote(safeIdentifier(region))}`;
|
|
21574
|
+
return `cdkd state orphan ${shellQuote(safeIdentifier(stackName))}${flag}`;
|
|
21575
|
+
}
|
|
21392
21576
|
/** The remedy command {@link stackClause}'s message ends on. */
|
|
21393
21577
|
function inspectCommand(stackName, region) {
|
|
21394
21578
|
if (stackName === void 0) return "cdkd state show <stack> --stack-region <region> --json";
|
|
@@ -21463,12 +21647,19 @@ function unreadableResourcePropertyBags(state) {
|
|
|
21463
21647
|
*
|
|
21464
21648
|
* Deliberately NOT folded into {@link refuseMalformedState}, for the reason
|
|
21465
21649
|
* {@link unreadableResourcePropertyBags} gives: that predicate answers a
|
|
21466
|
-
* question about the record ROOT, and its
|
|
21467
|
-
*
|
|
21468
|
-
*
|
|
21469
|
-
*
|
|
21470
|
-
*
|
|
21471
|
-
*
|
|
21650
|
+
* question about the record ROOT, and its callers have each had to make their
|
|
21651
|
+
* own decision about this one. `cdkd rollback` replays a journal rather than
|
|
21652
|
+
* diffing, so an unrelated broken bag would stop the command documented as the
|
|
21653
|
+
* way to UNWIND a stack whose state is already suspect; `cdkd scrub` and
|
|
21654
|
+
* `cdkd import` do not compare properties at all.
|
|
21655
|
+
*
|
|
21656
|
+
* **The rule is OPT-IN PER CALLER, not "whoever COMPARES the bag"**, and an
|
|
21657
|
+
* earlier revision of this sentence said the latter — which go-to-k/cdkd#3318
|
|
21658
|
+
* falsified within two lanes. `cdkd orphan` compares nothing and still opts in
|
|
21659
|
+
* through {@link refuseMalformedResourcePropertiesForOrphan}, because it SAVES
|
|
21660
|
+
* the bag; what each caller owes is an answer to "what would I do with a map I
|
|
21661
|
+
* cannot read", and comparison is only one way to owe one. Do not read this
|
|
21662
|
+
* paragraph as licence to delete a call from a caller that does not diff.
|
|
21472
21663
|
*/
|
|
21473
21664
|
function refuseMalformedResourceProperties(state, stackName, region) {
|
|
21474
21665
|
const unreadable = unreadableResourcePropertyBags(state);
|
|
@@ -21575,7 +21766,7 @@ function namedPropertyBagsClause(stackName, region, logicalIds) {
|
|
|
21575
21766
|
/**
|
|
21576
21767
|
* The text {@link refuseMalformedResourceProperties} raises.
|
|
21577
21768
|
*
|
|
21578
|
-
* `
|
|
21769
|
+
* `rawStackName` / `rawRegion` are the CALLER's resolved identity or nothing at all;
|
|
21579
21770
|
* {@link stackClause} is the authority for why this one may be handed neither.
|
|
21580
21771
|
*
|
|
21581
21772
|
* **It must be true under `cdkd deploy --dry-run` as well**, and the first
|
|
@@ -21604,6 +21795,155 @@ function malformedResourcePropertiesRefusalMessage(stackName, region, logicalIds
|
|
|
21604
21795
|
return `${namedPropertyBagsClause(stackName, region, logicalIds)} 'cdkd deploy' can WRITE state and AWS resources, so it refuses rather than continuing — under '--dry-run' too, because the plan a dry run would print is the wrong one: a DELETE and re-create of resources the template did not change, and reading the bag as empty produces that same verdict rather than avoiding it. Nothing was provisioned and no state was written FOR THIS STACK. Repair or remove the record first; 'cdkd diff' previews the rest of the stack with those maps read as EMPTY and warns that it did. Inspect the record with: ${inspectCommand(stackName, region)}`;
|
|
21605
21796
|
}
|
|
21606
21797
|
/**
|
|
21798
|
+
* The `cdkd orphan` refusal text for the per-entry `properties` container
|
|
21799
|
+
* (issue [#3318](https://github.com/go-to-k/cdkd/issues/3318)).
|
|
21800
|
+
*
|
|
21801
|
+
* A SECOND `properties` refusal beside
|
|
21802
|
+
* {@link malformedResourcePropertiesRefusalMessage}, for the reason every
|
|
21803
|
+
* split in this module is its own: that text describes the DIFF's verdict —
|
|
21804
|
+
* "a DELETE and re-create of resources the template did not change" — which
|
|
21805
|
+
* `cdkd orphan` never computes. It runs no diff at all.
|
|
21806
|
+
*
|
|
21807
|
+
* **And the harm here is NOT the laundering the module's other write-capable
|
|
21808
|
+
* refusals describe.** Measured 2026-09-17 through the real
|
|
21809
|
+
* `rewriteResourceReferences` over an `AWS::S3::Bucket` record: `rewriteValue`
|
|
21810
|
+
* returns a non-object verbatim, so a stored `"abcdef"` comes back as
|
|
21811
|
+
* `"abcdef"`, a `5` as `5`, a `null` as `null`, and an ABSENT bag stays absent
|
|
21812
|
+
* once `JSON.stringify` drops it. Nothing is fabricated and no evidence is
|
|
21813
|
+
* replaced — so {@link malformedOutputsRefusalMessage}'s "saved back as a
|
|
21814
|
+
* well-formed six-key map" sentence, true one container over, would be FALSE
|
|
21815
|
+
* here and must not be borrowed.
|
|
21816
|
+
*
|
|
21817
|
+
* What it DOES share is {@link namedPropertyBagsClause}, which carries a
|
|
21818
|
+
* diff-shaped sentence of its own ("a create-only property among them is a
|
|
21819
|
+
* REPLACEMENT of the live resource"). That is not a contradiction and is not
|
|
21820
|
+
* an oversight: the shared clause describes what the RECORD is, and the reason
|
|
21821
|
+
* an operator must care is precisely that a later `cdkd deploy` / `cdkd diff`
|
|
21822
|
+
* reaches that verdict. What this text must not do — and what its fence pins —
|
|
21823
|
+
* is claim that verdict as its OWN, since `cdkd orphan` computes no diff.
|
|
21824
|
+
*
|
|
21825
|
+
* What IS at stake is the command's own job. `cdkd orphan` exists to leave the
|
|
21826
|
+
* record deployable: it rewrites every surviving sibling's `Ref` /
|
|
21827
|
+
* `Fn::GetAtt` / `Fn::Sub` reference to an orphan so the next deploy neither
|
|
21828
|
+
* re-creates the orphan nor fails on a stale reference. Over a map it cannot
|
|
21829
|
+
* read it cannot do that and cannot say it did not. A scalar bag presents no
|
|
21830
|
+
* reference to find, so the `--force`-less hard fail on unresolvable
|
|
21831
|
+
* references can never fire for one; a LIST bag is the one unreadable shape
|
|
21832
|
+
* `rewriteValue` DOES walk (measured: a stored `[{"Ref":"<orphan>"}]` came
|
|
21833
|
+
* back as `["<physicalId>"]` with one row in the audit table), so its rewrites
|
|
21834
|
+
* are reported into a container that is still not a map. Either way the
|
|
21835
|
+
* command takes a lock, saves, and reports success over a record left in
|
|
21836
|
+
* exactly the state `cdkd deploy` REFUSES
|
|
21837
|
+
* ({@link malformedResourcePropertiesRefusalMessage}) and `cdkd diff` previews
|
|
21838
|
+
* as a replacement — one command after the evidence was last in cdkd's hands.
|
|
21839
|
+
*
|
|
21840
|
+
* **The refusal is SCOPED to the records the save would KEEP, so orphaning the
|
|
21841
|
+
* DAMAGED record itself still works** —
|
|
21842
|
+
* {@link refuseMalformedResourcePropertiesForOrphan} is handed the ids being
|
|
21843
|
+
* removed and never names one. Compare
|
|
21844
|
+
* `malformedDestroyResourcesRefusalMessage`, which has to point at a different
|
|
21845
|
+
* COMMAND for its way out because a destroy keeps every record it reads.
|
|
21846
|
+
*
|
|
21847
|
+
* **But that in-command way out is CONDITIONAL, and the text must say so.**
|
|
21848
|
+
* `cdkd orphan` addresses resources only through the `aws:cdk:path` index of
|
|
21849
|
+
* the SYNTHESIZED template (`resolveConstructPaths` →
|
|
21850
|
+
* `buildCdkPathIndex`), so an id the CDK app no longer declares — a record
|
|
21851
|
+
* left behind when the construct was deleted, or one a hand edit invented —
|
|
21852
|
+
* can never enter the orphan set for ANY invocation. The exemption therefore
|
|
21853
|
+
* cannot reach it, and an earlier revision of this text nonetheless told the
|
|
21854
|
+
* operator to "orphan the damaged record ITSELF" unconditionally: an
|
|
21855
|
+
* instruction that dies on `Construct path '...' not found in template`, which
|
|
21856
|
+
* is the misstating-the-remedy class this module's own notes record (security
|
|
21857
|
+
* review of go-to-k/cdkd#3318). The remedy is now stated with its condition and
|
|
21858
|
+
* the two unconditional ways out beside it — hand repair, and
|
|
21859
|
+
* `cdkd state orphan <stack>`, which needs no CDK app and drops the whole
|
|
21860
|
+
* record with the live resources left standing.
|
|
21861
|
+
*
|
|
21862
|
+
* So the recovery-path objection is answered for the common case by the
|
|
21863
|
+
* exemption and for the rest by naming commands that do not depend on the
|
|
21864
|
+
* template. What this does NOT claim is that `cdkd orphan` stays usable on a
|
|
21865
|
+
* stack holding a template-less torn record: it does not, exactly as
|
|
21866
|
+
* `cdkd deploy` does not (go-to-k/cdkd#3191). A state-keyed escape hatch
|
|
21867
|
+
* (`--orphan-logical-id`, addressing a record by its state key rather than by
|
|
21868
|
+
* a construct path) was considered in the same review and DECLINED: it is a new
|
|
21869
|
+
* mutating CLI surface whose only job is to route around a record the operator
|
|
21870
|
+
* must repair or drop anyway, and `cdkd state orphan <stack>` already removes
|
|
21871
|
+
* such a record with no CDK app and no new flag.
|
|
21872
|
+
*
|
|
21873
|
+
* It must be true under `--dry-run` as well, and it is: the guard sits at the
|
|
21874
|
+
* load, far above the `if (options.dryRun)` return. Refusing there is
|
|
21875
|
+
* {@link malformedResourcePropertiesRefusalMessage}'s call for the same
|
|
21876
|
+
* reason — a plausible rewrite audit table followed by a refusal the moment
|
|
21877
|
+
* the flag comes off is the worst arm of all.
|
|
21878
|
+
*
|
|
21879
|
+
* `rawStackName` / `rawRegion` are the CALLER's resolved identity — the synthesized
|
|
21880
|
+
* stack name and the region `pickStackRegion` settled on, never the record's
|
|
21881
|
+
* own unvalidated self-report. See {@link stackClause}.
|
|
21882
|
+
*/
|
|
21883
|
+
function malformedOrphanResourcePropertiesRefusalMessage(rawStackName, rawRegion, logicalIds) {
|
|
21884
|
+
const stackName = rawStackName === "" ? void 0 : rawStackName;
|
|
21885
|
+
const region = rawRegion === "" ? void 0 : rawRegion;
|
|
21886
|
+
return `${namedPropertyBagsClause(stackName, region, logicalIds)} 'cdkd orphan' REWRITES and SAVES every record it keeps, so it refuses rather than continuing — under '--dry-run' too, because the rewrite audit table a dry run prints is the wrong one. It is not that the save would fabricate a map: an unreadable bag is carried through VERBATIM. It is that this command exists to leave the record deployable by rewriting every reference to an orphaned resource, and a map it cannot read hides whichever references it holds — a string or a number presents none to find, and a list is walked, so its rewrites are recorded into a container that is still not a map. Continuing would rewrite, save, and report success over a record 'cdkd deploy' then REFUSES. No state was written. Two ways out need no CDK app: repair the record by hand, or drop it whole with '${dropRecordCommand(stackName, region)}', which leaves the live AWS resources standing. A third works only while the CDK app STILL DECLARES the named resource — this refusal covers just the records that would SURVIVE the save, so 'cdkd orphan <its construct path>' removes it and repairs the rest; construct paths come from the synthesized template, so a resource the app no longer declares has none and must take one of the first two. Inspect the record with: ${inspectCommand(stackName, region)}`;
|
|
21887
|
+
}
|
|
21888
|
+
/**
|
|
21889
|
+
* For `cdkd orphan`: refuse a record whose SURVIVING resource entries carry a
|
|
21890
|
+
* `properties` bag that cannot be read (issue
|
|
21891
|
+
* [#3318](https://github.com/go-to-k/cdkd/issues/3318)).
|
|
21892
|
+
*
|
|
21893
|
+
* A separate call from {@link refuseMalformedResourceProperties} rather than a
|
|
21894
|
+
* flag on it, for the reason {@link refuseMalformedOutputsForDestroy} is
|
|
21895
|
+
* separate from {@link refuseMalformedOutputs}: the refusal a user sees must
|
|
21896
|
+
* describe what THIS command would have done with the bag, and
|
|
21897
|
+
* {@link malformedOrphanResourcePropertiesRefusalMessage} records how far the
|
|
21898
|
+
* two consequences diverge.
|
|
21899
|
+
*
|
|
21900
|
+
* `removedLogicalIds` is the orphan set — the ids this run is dropping from
|
|
21901
|
+
* `state.resources`. They are EXCLUDED from the verdict because the save
|
|
21902
|
+
* cannot persist a record it is deleting, and because refusing on one would
|
|
21903
|
+
* break the recovery path the refusal is otherwise meant to preserve.
|
|
21904
|
+
*
|
|
21905
|
+
* **It covers `state.resources` ONLY, and the save keeps more than that.**
|
|
21906
|
+
* `rewriteResourceReferences` spreads `carriedState`, so `state.orphans[]` —
|
|
21907
|
+
* rollback-orphaned records each holding a whole `ResourceState`
|
|
21908
|
+
* (go-to-k/cdkd#2934) — rides through untouched, and
|
|
21909
|
+
* {@link unreadableResourcePropertyBags} never walks it. A torn bag parked
|
|
21910
|
+
* there is therefore still saved, and `computeStackDiff` splices those records
|
|
21911
|
+
* into `state.resources` on the next preview. Stated rather than implied,
|
|
21912
|
+
* because "the records the save would KEEP" reads wider than what this scans:
|
|
21913
|
+
* the gap is go-to-k/cdkd#3344, filed rather than folded in because the remedy
|
|
21914
|
+
* differs — an entry in that container has no construct path, so the message's
|
|
21915
|
+
* third way out is meaningless for it.
|
|
21916
|
+
*
|
|
21917
|
+
* **A surviving ENTRY that is not a map is the other gap, and it DOES launder.**
|
|
21918
|
+
* This scan asks whether a resource's `properties` is readable; it never asks
|
|
21919
|
+
* whether the resource RECORD is. `isReadableBag('abcdef')` is false, so a
|
|
21920
|
+
* string entry is skipped here, and then `rewriteResourceReferences`'s
|
|
21921
|
+
* `{ ...resource }` spreads it into per-character keys — the record is SAVED as
|
|
21922
|
+
* `{"0":"a",…,"dependencies":[]}` and the run reports success. A number entry
|
|
21923
|
+
* saves as `{"dependencies":[]}`, a record with no `physicalId`. So the
|
|
21924
|
+
* refusal's own text, which says a torn `properties` map is persisted verbatim
|
|
21925
|
+
* rather than reshaped, is true of the container it scans and NOT of the one
|
|
21926
|
+
* above it.
|
|
21927
|
+
*
|
|
21928
|
+
* That gap is NOT go-to-k/cdkd#3202's as filed — that issue records only the
|
|
21929
|
+
* `null`-entry `TypeError` — and NOT open PR go-to-k/cdkd#3226's, whose file
|
|
21930
|
+
* list carries neither `orphan.ts` nor `orphan-rewriter.ts`. A lane closing
|
|
21931
|
+
* either one leaves this open, which is why it is named here rather than
|
|
21932
|
+
* deferred to them by reference. Filed as go-to-k/cdkd#3350.
|
|
21933
|
+
*
|
|
21934
|
+
* CALL IT AT THE LOAD, beside {@link refuseMalformedState} and
|
|
21935
|
+
* {@link refuseMalformedOutputs}, above `rewriteResourceReferences` — the
|
|
21936
|
+
* placement rule {@link repairMalformedResourcesForReadOnly}'s note records.
|
|
21937
|
+
* The orphan set is resolved from the synthesized template before the state is
|
|
21938
|
+
* loaded, so nothing forces this call any lower.
|
|
21939
|
+
*/
|
|
21940
|
+
function refuseMalformedResourcePropertiesForOrphan(state, removedLogicalIds, stackName, region) {
|
|
21941
|
+
const removed = new Set(removedLogicalIds);
|
|
21942
|
+
const unreadable = unreadableResourcePropertyBags(state).filter((id) => !removed.has(id));
|
|
21943
|
+
if (unreadable.length === 0) return;
|
|
21944
|
+
throw new CdkdError(malformedOrphanResourcePropertiesRefusalMessage(stackName, region, unreadable), STATE_RESOURCES_MALFORMED);
|
|
21945
|
+
}
|
|
21946
|
+
/**
|
|
21607
21947
|
* The warning a caller of {@link repairMalformedResourcePropertiesForReadOnly}
|
|
21608
21948
|
* emits.
|
|
21609
21949
|
*/
|
|
@@ -24554,7 +24894,7 @@ var WAFv2WebACLProvider = class {
|
|
|
24554
24894
|
const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$11({ ResourceARN: physicalId }));
|
|
24555
24895
|
result["Tags"] = normalizeAwsTagsToCfn(tagsResp.TagInfoForResource?.TagList);
|
|
24556
24896
|
} catch (err) {
|
|
24557
|
-
this.logger.debug(`WAFv2 ListTagsForResource(${physicalId}) failed: ${
|
|
24897
|
+
this.logger.debug(`WAFv2 ListTagsForResource(${physicalId}) failed: ${describeAwsFailure(err).detail}`);
|
|
24558
24898
|
}
|
|
24559
24899
|
return result;
|
|
24560
24900
|
}
|
|
@@ -26778,8 +27118,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
26778
27118
|
const obj = value;
|
|
26779
27119
|
if ("Ref" in obj) return await this.resolveRef(obj["Ref"], context);
|
|
26780
27120
|
if ("Fn::GetAtt" in obj) return await this.resolveGetAtt(obj["Fn::GetAtt"], context);
|
|
26781
|
-
if ("Fn::Join" in obj) return await this.resolveJoin(obj["Fn::Join"], context);
|
|
26782
|
-
if ("Fn::Sub" in obj) return await this.resolveSub(obj["Fn::Sub"], context);
|
|
27121
|
+
if ("Fn::Join" in obj) return await this.resolveJoin(obj["Fn::Join"], context, obj);
|
|
27122
|
+
if ("Fn::Sub" in obj) return await this.resolveSub(obj["Fn::Sub"], context, obj);
|
|
26783
27123
|
if ("Fn::Select" in obj) return await this.resolveSelect(obj["Fn::Select"], context);
|
|
26784
27124
|
if ("Fn::Split" in obj) return await this.resolveSplit(obj["Fn::Split"], context);
|
|
26785
27125
|
if ("Fn::If" in obj) return await this.resolveIf(obj["Fn::If"], context);
|
|
@@ -27875,32 +28215,45 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27875
28215
|
*
|
|
27876
28216
|
* Fn::Join: [delimiter, [value1, value2, ...]]
|
|
27877
28217
|
*/
|
|
27878
|
-
async resolveJoin(joinArgs, context) {
|
|
28218
|
+
async resolveJoin(joinArgs, context, source) {
|
|
27879
28219
|
const [delimiter, rawValues] = joinArgs;
|
|
27880
28220
|
let values = rawValues;
|
|
27881
28221
|
if (!Array.isArray(values)) values = await this.resolveValue(values, context);
|
|
27882
28222
|
if (!Array.isArray(values)) throw new Error(`Fn::Join's second argument must be a list (an array literal or a list-returning intrinsic such as Fn::Cidr / Fn::GetAZs / Fn::Split / a Ref to a list-typed parameter — any List<...> type or CommaDelimitedList), but resolved to ${typeof values}`);
|
|
27883
28223
|
const literalList = Array.isArray(rawValues);
|
|
27884
|
-
const
|
|
27885
|
-
if (typeof v === "string")
|
|
27886
|
-
|
|
28224
|
+
const resolvedParts = await allSettledKeepingFirstRejection(values.map(async (v) => {
|
|
28225
|
+
if (typeof v === "string") {
|
|
28226
|
+
const part = v.includes("{{resolve:") ? await this.resolveDynamicReferencesWithLogTwin(v, this.logTwinOfProduct({
|
|
27887
28227
|
result: v,
|
|
27888
28228
|
twin: v
|
|
27889
28229
|
}, context).twin, context) : {
|
|
27890
28230
|
result: v,
|
|
27891
|
-
twin: v
|
|
27892
|
-
|
|
27893
|
-
|
|
27894
|
-
|
|
28231
|
+
twin: v,
|
|
28232
|
+
substitutions: [],
|
|
28233
|
+
complete: true
|
|
28234
|
+
};
|
|
28235
|
+
return {
|
|
28236
|
+
result: part.result,
|
|
28237
|
+
twin: part.twin,
|
|
28238
|
+
product: !literalList,
|
|
28239
|
+
input: v,
|
|
28240
|
+
substitutions: part.substitutions,
|
|
28241
|
+
complete: part.complete
|
|
28242
|
+
};
|
|
28243
|
+
}
|
|
27895
28244
|
const raw = await this.resolveValue(v, context);
|
|
27896
28245
|
const resolved = String(raw);
|
|
27897
28246
|
return {
|
|
27898
28247
|
result: resolved,
|
|
27899
28248
|
twin: resolved,
|
|
27900
28249
|
product: true,
|
|
27901
|
-
raw: { value: raw }
|
|
28250
|
+
raw: { value: raw },
|
|
28251
|
+
input: resolved,
|
|
28252
|
+
substitutions: [],
|
|
28253
|
+
complete: true
|
|
27902
28254
|
};
|
|
27903
|
-
}), (pending) => this.warnAbandonedParts(pending))
|
|
28255
|
+
}), (pending) => this.warnAbandonedParts(pending));
|
|
28256
|
+
const parts = resolvedParts.map((part) => {
|
|
27904
28257
|
if (!part.product) return part;
|
|
27905
28258
|
if (part.raw) return {
|
|
27906
28259
|
result: part.result,
|
|
@@ -27910,7 +28263,20 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27910
28263
|
});
|
|
27911
28264
|
let result = parts.map((part) => part.result).join(delimiter);
|
|
27912
28265
|
let twin = parts.map((part) => part.twin).join(delimiter);
|
|
27913
|
-
|
|
28266
|
+
const substitutions = resolvedParts.flatMap((part) => part.substitutions);
|
|
28267
|
+
let complete = resolvedParts.every((part) => part.complete);
|
|
28268
|
+
if (result.includes("{{resolve:")) {
|
|
28269
|
+
const joined = await this.resolveDynamicReferencesWithLogTwin(result, twin, context);
|
|
28270
|
+
({result, twin} = joined);
|
|
28271
|
+
substitutions.push(...joined.substitutions);
|
|
28272
|
+
complete &&= joined.complete;
|
|
28273
|
+
}
|
|
28274
|
+
this.recordLeafResolution(context, source, {
|
|
28275
|
+
input: resolvedParts.map((part) => part.input).join(delimiter),
|
|
28276
|
+
output: result,
|
|
28277
|
+
substitutions,
|
|
28278
|
+
complete
|
|
28279
|
+
});
|
|
27914
28280
|
this.rememberLogTwin(context, result, twin);
|
|
27915
28281
|
this.logger.debug(`Resolved Fn::Join: ${this.maskSecretsForLog(this.logTwinText(result, twin, context), context)}`);
|
|
27916
28282
|
return result;
|
|
@@ -28052,10 +28418,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28052
28418
|
* inside replace(). For full async support, we'd need to collect all replacements
|
|
28053
28419
|
* first, then do them synchronously.
|
|
28054
28420
|
*/
|
|
28055
|
-
async resolveSub(subArgs, context) {
|
|
28421
|
+
async resolveSub(subArgs, context, source) {
|
|
28056
28422
|
let template;
|
|
28057
28423
|
const variables = Object.create(null);
|
|
28058
28424
|
const variableTwins = Object.create(null);
|
|
28425
|
+
const variablePasses = Object.create(null);
|
|
28059
28426
|
if (Array.isArray(subArgs)) {
|
|
28060
28427
|
const [templateString, variableMap] = subArgs;
|
|
28061
28428
|
template = templateString;
|
|
@@ -28063,10 +28430,13 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28063
28430
|
for (const [key, val] of Object.entries(variableMap)) if (typeof val === "string") {
|
|
28064
28431
|
const resolved = val.includes("{{resolve:") ? await this.resolveDynamicReferencesWithLogTwin(val, val, context) : {
|
|
28065
28432
|
result: val,
|
|
28066
|
-
twin: val
|
|
28433
|
+
twin: val,
|
|
28434
|
+
substitutions: [],
|
|
28435
|
+
complete: true
|
|
28067
28436
|
};
|
|
28068
28437
|
variables[key] = resolved.result;
|
|
28069
28438
|
variableTwins[key] = resolved.twin;
|
|
28439
|
+
variablePasses[key] = resolved;
|
|
28070
28440
|
} else variables[key] = context.abandonedResolutions === void 0 ? await this.resolveValue(val, context) : await this.resolveKeyUnit(key, val, context, context.abandonedResolutions);
|
|
28071
28441
|
} else template = subArgs;
|
|
28072
28442
|
const replacements = [];
|
|
@@ -28093,9 +28463,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28093
28463
|
}
|
|
28094
28464
|
let replacement;
|
|
28095
28465
|
let twinReplacement;
|
|
28466
|
+
let pass;
|
|
28096
28467
|
if (varNameStr in variables) {
|
|
28097
28468
|
replacement = String(variables[varNameStr]);
|
|
28098
28469
|
twinReplacement = varNameStr in variableTwins ? variableTwins[varNameStr] : this.productLogTwin(variables[varNameStr], context);
|
|
28470
|
+
if (varNameStr in variablePasses) pass = variablePasses[varNameStr];
|
|
28099
28471
|
} else {
|
|
28100
28472
|
const pseudoValue = await this.resolvePseudoParameter(varNameStr, context);
|
|
28101
28473
|
if (pseudoValue !== void 0) replacement = String(pseudoValue);
|
|
@@ -28125,7 +28497,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28125
28497
|
replacements.push({
|
|
28126
28498
|
match: match[0],
|
|
28127
28499
|
replacement,
|
|
28128
|
-
twin: twinReplacement ?? replacement
|
|
28500
|
+
twin: twinReplacement ?? replacement,
|
|
28501
|
+
...pass ? { pass } : {}
|
|
28129
28502
|
});
|
|
28130
28503
|
}
|
|
28131
28504
|
let cursor = 0;
|
|
@@ -28138,7 +28511,21 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
28138
28511
|
const entry = replacements[twinCursor++];
|
|
28139
28512
|
return entry ? entry.twin : whole;
|
|
28140
28513
|
});
|
|
28141
|
-
|
|
28514
|
+
const input = result;
|
|
28515
|
+
const substitutions = replacements.flatMap((entry) => entry.pass?.substitutions ?? []);
|
|
28516
|
+
let complete = replacements.every((entry) => entry.pass?.complete ?? true);
|
|
28517
|
+
if (result.includes("{{resolve:")) {
|
|
28518
|
+
const substituted = await this.resolveDynamicReferencesWithLogTwin(result, twin, context);
|
|
28519
|
+
({result, twin} = substituted);
|
|
28520
|
+
substitutions.push(...substituted.substitutions);
|
|
28521
|
+
complete &&= substituted.complete;
|
|
28522
|
+
}
|
|
28523
|
+
this.recordLeafResolution(context, source, {
|
|
28524
|
+
input,
|
|
28525
|
+
output: result,
|
|
28526
|
+
substitutions,
|
|
28527
|
+
complete
|
|
28528
|
+
});
|
|
28142
28529
|
this.rememberLogTwin(context, result, twin);
|
|
28143
28530
|
this.logger.debug(`Resolved Fn::Sub: ${this.maskSecretsForLog(this.logTwinText(result, twin, context), context)}`);
|
|
28144
28531
|
return result;
|
|
@@ -29528,7 +29915,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29528
29915
|
* not the bag holds pairs. Where the middle RE-WRAPS it (an `Fn::Join` around
|
|
29529
29916
|
* the `Ref`) the middle registers its own twin, and the hand-off still drops
|
|
29530
29917
|
* a bag holding no pairs (it passes `inheritedSecrets` only when `size` is
|
|
29531
|
-
* nonzero).
|
|
29918
|
+
* nonzero). What reaches a grandchild instead is the parent's WHOLE-VALUE
|
|
29919
|
+
* entry: the middle's `{ Ref }` copies it into the middle's bag, which is then
|
|
29920
|
+
* non-empty and handed down. Issue #3156 made the carry record that entry for
|
|
29921
|
+
* the intrinsic frames it used to refuse; a frame it still refuses (listed on
|
|
29922
|
+
* `recordNestedStackParameterExpressions`) keeps both gaps.
|
|
29532
29923
|
*
|
|
29533
29924
|
* The registry is keyed by VALUE, and `splitLogTwins`' unaligned arm
|
|
29534
29925
|
* registers every piece, public ones included. So a child `Fn::Base64` over a
|
|
@@ -29578,6 +29969,17 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29578
29969
|
};
|
|
29579
29970
|
}
|
|
29580
29971
|
/**
|
|
29972
|
+
* Record the `Fn::Join` / `Fn::Sub` object `source`'s own resolution under
|
|
29973
|
+
* the pass bag the nested-stack carry reads (issue
|
|
29974
|
+
* [#3156](https://github.com/go-to-k/cdkd/issues/3156)). The key is the
|
|
29975
|
+
* object `resolveValue` dispatched on, and a context with no bag has no pass
|
|
29976
|
+
* to scope it to.
|
|
29977
|
+
*/
|
|
29978
|
+
recordLeafResolution(context, source, resolution) {
|
|
29979
|
+
if (context.recordedSecretValues === void 0) return;
|
|
29980
|
+
recordIntrinsicLeafResolution(context.recordedSecretValues, source, resolution);
|
|
29981
|
+
}
|
|
29982
|
+
/**
|
|
29581
29983
|
* Register a MASKED `twin` as the log twin of `result` for this pass (issue
|
|
29582
29984
|
* #3100). An unmasked twin is not registered, so a later literal that
|
|
29583
29985
|
* resolves to the same string cannot replace an earlier write's mask, and
|
|
@@ -29715,6 +30117,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29715
30117
|
const pattern = /\{\{resolve:([^}]+)\}\}/g;
|
|
29716
30118
|
let result = value;
|
|
29717
30119
|
let twin = logTwin;
|
|
30120
|
+
const substitutions = [];
|
|
30121
|
+
let complete = true;
|
|
29718
30122
|
let match;
|
|
29719
30123
|
const matches = [];
|
|
29720
30124
|
while ((match = pattern.exec(value)) !== null) matches.push({
|
|
@@ -29730,7 +30134,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29730
30134
|
const nameLogText = inherited?.nameLogText ?? (pairedTwin === void 0 ? () => "***" : this.dynamicReferenceNameLogText(inner, pairedTwin, context));
|
|
29731
30135
|
try {
|
|
29732
30136
|
const isKnownSecret = service === "secretsmanager" || service === "ssm-secure" || recordedSecretExpressions.has(fullMatch);
|
|
29733
|
-
if (isKnownSecret && context?.skipDynamicReferences)
|
|
30137
|
+
if (isKnownSecret && context?.skipDynamicReferences) {
|
|
30138
|
+
complete = false;
|
|
30139
|
+
continue;
|
|
30140
|
+
}
|
|
29734
30141
|
const regionVerdict = classifyReplaySecretRegion(fullMatch, this.explicitRegion ?? this.resolverRegion, context?.producerRegions);
|
|
29735
30142
|
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.`));
|
|
29736
30143
|
if (regionVerdict.kind === "named-region") {
|
|
@@ -29740,6 +30147,13 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29740
30147
|
});
|
|
29741
30148
|
result = result.replace(fullMatch, () => foreign.result);
|
|
29742
30149
|
twin = twin.replace(fullMatch, () => foreign.twin);
|
|
30150
|
+
const [delegated] = foreign.substitutions;
|
|
30151
|
+
substitutions.push({
|
|
30152
|
+
token: fullMatch,
|
|
30153
|
+
value: foreign.result,
|
|
30154
|
+
secret: foreign.substitutions.length === 1 && delegated.secret
|
|
30155
|
+
});
|
|
30156
|
+
if (!foreign.complete || foreign.substitutions.length !== 1) complete = false;
|
|
29743
30157
|
continue;
|
|
29744
30158
|
}
|
|
29745
30159
|
const cached = this.cachedDynamicReferences.get(fullMatch);
|
|
@@ -29750,6 +30164,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29750
30164
|
}
|
|
29751
30165
|
result = result.replace(fullMatch, () => cached.value);
|
|
29752
30166
|
twin = twin.replace(fullMatch, () => cached.secret && cached.value ? "***" : cached.value);
|
|
30167
|
+
substitutions.push({
|
|
30168
|
+
token: fullMatch,
|
|
30169
|
+
value: cached.value,
|
|
30170
|
+
secret: cached.secret
|
|
30171
|
+
});
|
|
29753
30172
|
continue;
|
|
29754
30173
|
}
|
|
29755
30174
|
const parts = inner.split(":");
|
|
@@ -29777,7 +30196,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29777
30196
|
else cacheable = false;
|
|
29778
30197
|
isSecret = param.secure;
|
|
29779
30198
|
if (param.secure) {
|
|
29780
|
-
if (!decrypt)
|
|
30199
|
+
if (!decrypt) {
|
|
30200
|
+
complete = false;
|
|
30201
|
+
continue;
|
|
30202
|
+
}
|
|
29781
30203
|
}
|
|
29782
30204
|
resolved = param.value;
|
|
29783
30205
|
} else if (service === "ssm-secure") {
|
|
@@ -29787,6 +30209,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29787
30209
|
resolved = param.value;
|
|
29788
30210
|
} else {
|
|
29789
30211
|
this.logger.warn(this.maskSecretsForLog(`Unsupported dynamic reference service: ${this.maskSecretsForLog(nameLogText(String(service)), context)}`, context));
|
|
30212
|
+
complete = false;
|
|
29790
30213
|
continue;
|
|
29791
30214
|
}
|
|
29792
30215
|
if (cacheable) this.cachedDynamicReferences.set(fullMatch, {
|
|
@@ -29800,6 +30223,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29800
30223
|
}
|
|
29801
30224
|
result = result.replace(fullMatch, () => resolved);
|
|
29802
30225
|
twin = twin.replace(fullMatch, () => isSecret && resolved ? "***" : resolved);
|
|
30226
|
+
substitutions.push({
|
|
30227
|
+
token: fullMatch,
|
|
30228
|
+
value: resolved,
|
|
30229
|
+
secret: isSecret
|
|
30230
|
+
});
|
|
29803
30231
|
} catch (err) {
|
|
29804
30232
|
if (context?.abandonedResolutions === void 0) throw err;
|
|
29805
30233
|
if (isDeliberateResolutionRefusal(err)) throw err;
|
|
@@ -29812,13 +30240,16 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
29812
30240
|
}
|
|
29813
30241
|
return out;
|
|
29814
30242
|
}));
|
|
30243
|
+
complete = false;
|
|
29815
30244
|
continue;
|
|
29816
30245
|
}
|
|
29817
30246
|
}
|
|
29818
30247
|
if (context) this.rememberLogTwin(context, result, twin);
|
|
29819
30248
|
return {
|
|
29820
30249
|
result,
|
|
29821
|
-
twin
|
|
30250
|
+
twin,
|
|
30251
|
+
substitutions,
|
|
30252
|
+
complete
|
|
29822
30253
|
};
|
|
29823
30254
|
}
|
|
29824
30255
|
/**
|
|
@@ -30073,7 +30504,7 @@ async function disableInstanceApiTermination(client, instanceId, logger) {
|
|
|
30073
30504
|
}));
|
|
30074
30505
|
logger.debug(`Disabled DisableApiTermination on EC2 Instance ${instanceId} before deletion`);
|
|
30075
30506
|
} catch (flipError) {
|
|
30076
|
-
logger.debug(`Could not disable DisableApiTermination on ${instanceId}: ${
|
|
30507
|
+
logger.debug(`Could not disable DisableApiTermination on ${instanceId}: ${describeAwsFailure(flipError).detail}`);
|
|
30077
30508
|
}
|
|
30078
30509
|
}
|
|
30079
30510
|
/**
|
|
@@ -30385,7 +30816,7 @@ function getTopLevelWriteOnlyProperties(resourceType) {
|
|
|
30385
30816
|
if (cached) return cached;
|
|
30386
30817
|
const entry = fetchTopLevelWriteOnlyProperties(resourceType).catch((error) => {
|
|
30387
30818
|
writeOnlyPropertiesCache.delete(resourceType);
|
|
30388
|
-
const message =
|
|
30819
|
+
const message = describeAwsFailure(error).detail;
|
|
30389
30820
|
getLogger().child("WriteOnlyProperties").warn(`Failed to resolve write-only properties for ${resourceType} via cloudformation:DescribeType (${message}). Falling back to a minimal update patch — write-only properties (if any) may be dropped by the Cloud Control read-modify-write update. Grant cloudformation:DescribeType to enable write-only property re-inclusion.`);
|
|
30390
30821
|
return /* @__PURE__ */ new Set();
|
|
30391
30822
|
});
|
|
@@ -30511,7 +30942,7 @@ function getTopLevelReadOnlyProperties(resourceType) {
|
|
|
30511
30942
|
if (cached) return cached;
|
|
30512
30943
|
const entry = fetchTopLevelReadOnlyProperties(resourceType).catch((error) => {
|
|
30513
30944
|
readOnlyPropertiesCache.delete(resourceType);
|
|
30514
|
-
const message =
|
|
30945
|
+
const message = describeAwsFailure(error).detail;
|
|
30515
30946
|
getLogger().child("ReadOnlyProperties").debug(`Failed to resolve read-only properties for ${displaySafe(resourceType, { asciiOnly: true })} via cloudformation:DescribeType (${message}).`);
|
|
30516
30947
|
});
|
|
30517
30948
|
readOnlyPropertiesCache.set(resourceType, entry);
|
|
@@ -31729,7 +32160,7 @@ var CloudControlProvider = class {
|
|
|
31729
32160
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
31730
32161
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
31731
32162
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
31732
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
32163
|
+
const { ASGProvider } = await import("./asg-provider-BOYln2Tw.js").then((n) => n.n);
|
|
31733
32164
|
const asgProvider = new ASGProvider();
|
|
31734
32165
|
return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
31735
32166
|
}
|
|
@@ -34396,7 +34827,7 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
34396
34827
|
}
|
|
34397
34828
|
this.logger.debug(`Successfully deleted custom resource ${logicalId}`);
|
|
34398
34829
|
} catch (error) {
|
|
34399
|
-
this.logger.warn(`Failed to delete custom resource ${logicalId}, but continuing: ${
|
|
34830
|
+
this.logger.warn(`Failed to delete custom resource ${logicalId}, but continuing: ${describeAwsFailure(error).detail}. The Delete handler did not complete, so anything this custom resource manages may still be LIVE — cdkd is KEEPING the state record and the run exits non-zero. ${CR_SKIP_NOT_A_RETRY_CAVEAT} ${DEPLOY_SKIP_CAVEAT}`);
|
|
34400
34831
|
return {
|
|
34401
34832
|
outcome: "skipped",
|
|
34402
34833
|
reason: CR_DELETE_INVOKE_FAILED_SKIP_REASON
|
|
@@ -34433,7 +34864,7 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
34433
34864
|
return false;
|
|
34434
34865
|
} catch (error) {
|
|
34435
34866
|
if (error.name === "ResourceNotFoundException") return true;
|
|
34436
|
-
this.logger.debug(`GetFunction pre-check for ${serviceToken} failed inconclusively (${
|
|
34867
|
+
this.logger.debug(`GetFunction pre-check for ${serviceToken} failed inconclusively (${describeAwsFailure(error).detail}); proceeding with the normal delete invoke`);
|
|
34437
34868
|
return false;
|
|
34438
34869
|
}
|
|
34439
34870
|
}
|
|
@@ -34510,7 +34941,7 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
34510
34941
|
} catch (error) {
|
|
34511
34942
|
if (delivered || preDeliveryRetries >= this.preDeliveryAuthzMaxRetries || !this.isTransientAuthzThrow(error)) throw error;
|
|
34512
34943
|
const delayMs = Math.min(250 * Math.pow(2, preDeliveryRetries), IAM_PROPAGATION_MAX_DELAY_MS);
|
|
34513
|
-
this.logger.warn(`Custom resource ${operation} for ${logicalId} hit a transient IAM-authorization error before the request was delivered (attempt ${attempt + 1}/${this.preDeliveryAuthzMaxRetries + 1}): ${this.truncateReason(
|
|
34944
|
+
this.logger.warn(`Custom resource ${operation} for ${logicalId} hit a transient IAM-authorization error before the request was delivered (attempt ${attempt + 1}/${this.preDeliveryAuthzMaxRetries + 1}): ${this.truncateReason(describeAwsFailure(error).detail)}. Retrying in ${delayMs / 1e3}s with a fresh response URL and RequestId.`);
|
|
34514
34945
|
if (invocation !== void 0) await this.cleanupResponseObject(invocation.responseKey);
|
|
34515
34946
|
preDeliveryRetries += 1;
|
|
34516
34947
|
await this.sleepInterruptibly(delayMs, watch);
|
|
@@ -34712,7 +35143,7 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
34712
35143
|
maxDelay: 5
|
|
34713
35144
|
}, { FunctionName: serviceToken });
|
|
34714
35145
|
} catch (error) {
|
|
34715
|
-
this.logger.debug(`Could not recycle backing function for ${logicalId} (${
|
|
35146
|
+
this.logger.debug(`Could not recycle backing function for ${logicalId} (${describeAwsFailure(error).detail}); retrying invoke without a forced cold start`);
|
|
34716
35147
|
}
|
|
34717
35148
|
}
|
|
34718
35149
|
/**
|
|
@@ -35061,7 +35492,7 @@ var CustomResourceProvider = class CustomResourceProvider {
|
|
|
35061
35492
|
Key: responseKey
|
|
35062
35493
|
}));
|
|
35063
35494
|
} catch (error) {
|
|
35064
|
-
this.logger.debug(`Failed to delete custom-resource response object s3://${displaySafe(bucket, { asciiOnly: true }) || "<unrenderable>"}/${displaySafe(responseKey)}; it remains as a current object. Underlying error: ${displaySafe(
|
|
35495
|
+
this.logger.debug(`Failed to delete custom-resource response object s3://${displaySafe(bucket, { asciiOnly: true }) || "<unrenderable>"}/${displaySafe(responseKey)}; it remains as a current object. Underlying error: ${displaySafe(describeAwsFailure(error).detail) || "<unrenderable>"}`);
|
|
35065
35496
|
}
|
|
35066
35497
|
await purgeNoncurrentKeyVersions(this.s3Client, bucket, [responseKey], {
|
|
35067
35498
|
logger: this.logger,
|
|
@@ -35840,7 +36271,7 @@ var IAMRoleProvider = class {
|
|
|
35840
36271
|
await this.iamClient.send(new DeleteRoleCommand({ RoleName: roleName }));
|
|
35841
36272
|
this.logger.debug(`Cleaned up partially-created IAM role ${logicalId} (${roleName}) after wiring failure`);
|
|
35842
36273
|
} catch (cleanupError) {
|
|
35843
|
-
this.logger.warn(`Failed to clean up partially-created IAM role ${logicalId} (${roleName}): ${
|
|
36274
|
+
this.logger.warn(`Failed to clean up partially-created IAM role ${logicalId} (${roleName}): ${describeAwsFailure(cleanupError).detail}. Manual deletion may be required before the next deploy: detach managed policies (aws iam list-attached-role-policies --role-name ${roleName} then aws iam detach-role-policy --role-name ${roleName} --policy-arn <arn>), delete inline policies (aws iam list-role-policies --role-name ${roleName} then aws iam delete-role-policy --role-name ${roleName} --policy-name <name>), then aws iam delete-role --role-name ${roleName}`);
|
|
35844
36275
|
}
|
|
35845
36276
|
throw innerError;
|
|
35846
36277
|
}
|
|
@@ -35877,8 +36308,8 @@ var IAMRoleProvider = class {
|
|
|
35877
36308
|
this.logger.warn(`Skipped deleting old role ${physicalId} during replacement: ${deleteResult.reason}. The old role may be orphaned and require manual cleanup.`);
|
|
35878
36309
|
}
|
|
35879
36310
|
} catch (error) {
|
|
35880
|
-
orphanReason = `old role ${physicalId} could not be deleted: ${
|
|
35881
|
-
this.logger.warn(`Failed to delete old role ${physicalId} during replacement: ${
|
|
36311
|
+
orphanReason = `old role ${physicalId} could not be deleted: ${safeStringify(error)}`;
|
|
36312
|
+
this.logger.warn(`Failed to delete old role ${physicalId} during replacement: ${safeStringify(error)}. The old role may be orphaned and require manual cleanup.`);
|
|
35882
36313
|
}
|
|
35883
36314
|
const base = {
|
|
35884
36315
|
physicalId: createResult.physicalId,
|
|
@@ -38147,7 +38578,7 @@ function makeCanonicalizePropertiesFn(registry) {
|
|
|
38147
38578
|
if (!registry.hasProvider(resourceType)) return properties;
|
|
38148
38579
|
return registry.getProvider(resourceType).canonicalizeDesiredProperties?.(resourceType, properties) ?? properties;
|
|
38149
38580
|
} catch (error) {
|
|
38150
|
-
logger.debug(`canonicalizeDesiredProperties failed for ${resourceType}: ${
|
|
38581
|
+
logger.debug(`canonicalizeDesiredProperties failed for ${resourceType}: ${describeAwsFailure(error).detail}`);
|
|
38151
38582
|
return properties;
|
|
38152
38583
|
}
|
|
38153
38584
|
};
|
|
@@ -44723,5 +45154,5 @@ var DeployEngine = class DeployEngine {
|
|
|
44723
45154
|
};
|
|
44724
45155
|
|
|
44725
45156
|
//#endregion
|
|
44726
|
-
export { secretBearing as $,
|
|
44727
|
-
//# sourceMappingURL=deploy-engine-
|
|
45157
|
+
export { secretBearing as $, isCdkdError as $i, scrubResourceRecord as $n, Synthesizer as $r, s3BucketArn as $t, extractDeploymentEventError as A, setAwsClients as Ai, repairMalformedResourcesForReadOnly as An, isCrossRegionRedirect as Ar, isImpossibleEmptyStoredAttribute as At, toCfnInteger as B, LocalStartServiceError as Bi, TEMPLATE_SOURCED_RULES as Bn, describeDockerExecFailure as Br, coerceCfnBoolean as Bt, buildFinalSnapshotIdentifier as C, AssemblyReader as Ci, refuseMalformedOutputs as Cn, loadPublishableAssetManifest as Cr, isTerminationProtectionPropagationError as Ct, refusesFinalSnapshot as D, AwsClients as Di, refuseMalformedState as Dn, assertAssetBucketRegion as Dr, cfnRefValueFromPhysicalId as Dt, isFinalSnapshotError as E, resolveBucketRegion as Ei, refuseMalformedResourcesForDestroy as En, BOOTSTRAP_MARKER_PREFIX as Er, carriesFetchableDynamicReference as Et, isStatefulRecreateTargetSync as F, DependencyError as Fi, DagBuilder as Fn, buildDenyExternalAccessPolicy as Fr, isSettledInstanceState as Ft, gray as G, ProvisioningError as Gi, identityKeyFor as Gn, partitionSensitiveEnv as Gr, readConfigString as Gt, formatResourceLine as H, MalformedProducerRecordRefusalError as Hi, createSecretMasker as Hn, dockerSpawnEnvWithSensitive as Hr, configBooleanRefusal as Ht, renderStatefulReason as I, DeployCancelledError as Ii, TemplateParser as In, describeAwsFailure as Ir, WAFv2WebACLProvider as It, yellow as J, StackHasActiveImportsError as Ji, maskSecretsInError as Jn, runDockerStreaming as Jr, requireConfigObject as Jt, green as K, ResourceTimeoutError as Ki, isSingleDynamicReferenceToken as Kn, redactDockerArgvValues as Kr, replayWarn as Kt, WARM_THROUGHPUT_MEMBERS as L, DynamicReferenceRegionAmbiguousError as Li, STATE_SOURCED_BASELINE_RULES as Ln, safeStringify as Lr, normalizeAwsTagsToCfn as Lt, planOrphanAdoption as M, CdkdError as Mi, findSilentDropProperties as Mn, readBootstrapMarkerBody as Mr, isUnboundTemplateParameter as Mt, MULTI_REGION_RECREATE_BLOCKED_TYPES as N, ConfigError as Ni, describeTypeWithThrottleRetry as Nn, validateAssetBucketName as Nr, parameterTypeMayLoseSecretIdentity as Nt, unsupportedFinalSnapshotError as O, getAwsClients as Oi, repairMalformedOutputsForReadOnly as On, ensureAssetStorage as Or, coerceParameterTypedValue as Ot, isStatefulRecreateTargetForReplace as P, CrossAccountSecretRefusalError as Pi, withRetry as Pn, validateContainerRepoName as Pr, refStateLookupFromResource as Pt, isExportAliasCollision as Q, formatError as Qi, redactSecretsForState as Qn, getDockerImageBySourceHash as Qr, withSharedDrainBudget as Qt, coerceWarmThroughput as R, IntrinsicResolutionRefusalError as Ri, STATE_SOURCED_CROSS_GENERATION_RULES as Rn, buildDockerImage as Rr, resolveExplicitPhysicalId as Rt, PRE_DELETE_SNAPSHOT_TYPES as S, derivePartitionAndUrlSuffix as Si, refuseMalformedNestedChildOutputs as Sn, createAssetRedirectResolver as Sr, disableInstanceApiTermination as St, createPreDeleteFinalSnapshot as T, clearBucketRegionCache as Ti, refuseMalformedResourcePropertiesForOrphan as Tn, AssetModeResolver as Tr, carriesDynamicReference as Tt, bold as U, NestedStackChildDirectDestroyError as Ui, dynamicReferenceTokens as Un, formatDockerLoginError as Ur, configIntegerRefusal as Ut, toFiniteNumber as V, LockError as Vi, carriesSecretMask as Vn, describeDockerFailure as Vr, coerceCfnInteger as Vt, cyan as W, PartialFailureError as Wi, errorCauseChain as Wn, getDockerCmd as Wr, configStringRefusal as Wt, collectPublishedOutputNames as X, StateError as Xi, recordMaskOnlyValue as Xn, stripControlChars as Xr, classifyReplaySecretRegion as Xt, collectDeclaredOutputNames as Y, StackTerminationProtectionError as Yi, maskSecretsInText as Yn, escapeRegExp$1 as Yr, requireConfigString as Yt, exportAliasCollisionScrubWarning as Z, SynthesisError as Zi, recoverMaskedOutput as Zn, AssetManifestLoader as Zr, producerRegionsFromState as Zt, isSecretBearingReferenceString as _, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as _i, malformedRenderedContainersWarning as _n, shouldRetainResource as _r, isWaitAbandonedError as _t, DeploymentEventsStore as a, isTransientServerError as aa, resolveCaptureObservedState as ai, applyCallerIdentityCredentials as an, forceQuitRecoveryClause as ar, clearOnUpdateRemoval as at, bindingSkippedOutputs as b, PARTITION_TABLE as bi, malformedStateRefusalMessage as bn, WorkGraph as br, deleteIndeterminateGuards as bt, replayFailedOperations as c, retryClassificationText as ca, resolveStateBucketWithDefaultAndSource as ci, INTRINSIC_KEYS as cn, CUSTOM_RESOURCE_RESPONSE_PREFIX as cr, createMaskedRetryLogger as ct, updatePartialReason as d, warnDeprecatedNoPrefixCliFlag as di, hasReadableResources as dn, hasReadableExportSet as dr, beginCommandInterruptScope as dt, normalizeAwsError as ea, synthesisStatusMessage as ei, s3BucketDomainName as en, LockManager as er, secretBearingStateKeyWarning as et, withResourceDeadline as f, CFN_TEMPLATE_BODY_LIMIT as fi, malformedExportNamesWarning as fn, importableOutputKeys as fr, endCommandInterruptScope as ft, bagHoldsSecretExpression as g, uploadCfnTemplate as gi, malformedOutputsWarning as gn, orphansCarriedFrom as gr, CloudControlProvider as gt, computeImplicitDeleteEdges as h, findLargeInlineResources as hi, malformedOutputsRefusalMessage as hn, orphansAfterRollback as hr, startInterruptWatch as ht, DeploymentEventsReader as i, isThrottlingError as ia, resolveAutoAssetStorage as ii, applyRoleArnIfSet as in, buildLockContentionMessage as ir, collectInlinePolicyNamesManagedBySiblings as it, makeSiblingClaimReader as j, AssetError as ji, findActionableSilentDrops as jn, parseBootstrapMarker as jr, isNamelessDynamicReferenceError as jt, makeCanonicalizePropertiesFn as k, resetAwsClients as ki, repairMalformedResourcePropertiesForReadOnly as kn, getBootstrapMarkerKey as kr, getAccountInfo as kt, replayRollback as l, __exportAll as la, resolveUseCdkBootstrapAssets as li, STATE_RESOURCES_MALFORMED as ln, DEFAULT_STATE_PREFIX as lr, maskDeep as lt, IMPLICIT_DELETE_DEPENDENCIES as m, MIGRATE_TMP_PREFIX as mi, malformedLocalOutputsWarning as mn, isReadableBag as mr, isInterruptedWaitError as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, isMarkedNonRetryable as na, getLegacyStateBucketName as ni, s3BucketRegionalDomainName as nn, rebuildClientForBucketRegion as nr, getCurrentResourceSecrets as nt, planFailedOps as o, markNonRetryable as oa, resolveSkipPrefix as oi, callerEnvCredentials as on, formatLockExpiry as or, ProviderRegistry as ot, maskingRetryLogger as p, CFN_TEMPLATE_URL_LIMIT as pi, malformedExportSourceWarning as pn, importableOutputs as pr, interruptWatchListenerCount as pt, red as q, ResourceUpdateNotSupportedError as qi, markSameGenerationBag as qn, runDockerForeground as qr, requireConfigArray as qt, DeployEngine as r, isRetryableTransientError as ra, resolveApp as ri, s3BucketWebsiteUrl as rn, buildForceUnlockCommand as rr, IAMRoleProvider as rt, planRollback as s, markRedactedCause as sa, resolveStateBucketWithDefault as si, DiffCalculator as sn, shellQuote as sr, wouldReturnToSdkProvider as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, withErrorHandling as ta, getDefaultStateBucketName as ti, s3BucketDualStackDomainName as tn, S3StateBackend as tr, secretSafeKeyDisplay as tt, updatePartialMessage as u, stateBucketExistenceConfirmed as ui, hasReadableOutputs as un, exportNamesCarriedFrom as ur, maskerOrIdentity as ut, keptWholeReasonText as v, decodeListingKey as vi, malformedResourcePropertiesWarning as vn, AssetPublisher as vr, slowCcOperationTimeoutMs as vt, ccRoutedFinalSnapshotError as w, processStackMessages as wi, refuseMalformedOutputsForDestroy as wn, rewriteTemplateAssetReferences as wr, IntrinsicFunctionResolver as wt, ATOMIC_FINAL_SNAPSHOT_TYPES as x, canonicalizeRegion as xi, producerRecordKey as xn, buildAssetRedirectMap as xr, deleteSkipReason as xt, mergeNoChangeOutputs as y, expectedOwnerParam as yi, malformedResourcesWarning as yn, stringifyValue as yr, UNSPECIFIED_SKIP_REASON as yt, isWarmThroughputDecrease as z, LocalInvokeBuildError as zi, STATE_SOURCED_READBACK_RULES as zn, describeDockerCapturedOutput as zr, assertRegionMatch as zt };
|
|
45158
|
+
//# sourceMappingURL=deploy-engine-CBzq85r6.js.map
|