@go-to-k/cdkd 0.290.26 → 0.290.28
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-7KM2pV3Q.js → asg-provider-DiZsq-4k.js} +3 -3
- package/dist/{asg-provider-7KM2pV3Q.js.map → asg-provider-DiZsq-4k.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-CbIc5Y73.js → deploy-engine-CcFbq2q8.js} +62 -11
- package/dist/deploy-engine-CcFbq2q8.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/{logger-CkHVgzLx.js → logger-6LP1UiFA.js} +18 -2
- package/dist/logger-6LP1UiFA.js.map +1 -0
- package/dist/{program-BgDlhE-S.js → program-C1iVGcxJ.js} +722 -126
- package/dist/{program-BgDlhE-S.js.map → program-C1iVGcxJ.js.map} +1 -1
- package/dist/{version-yD49aCCv.js → version-B2-UuEte.js} +2 -2
- package/dist/{version-yD49aCCv.js.map → version-B2-UuEte.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-CbIc5Y73.js.map +0 -1
- package/dist/logger-CkHVgzLx.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $i as
|
|
2
|
-
import { a as setLogger, n as getLogger, t as ConsoleLogger } from "./logger-
|
|
1
|
+
import { $i as SynthesisError, Ai as getAwsClients, Di as clearBucketRegionCache, Fi as ConfigError, Li as DependencyError, Mi as setAwsClients, Ni as AssetError, Oi as resolveBucketRegion, Pi as CdkdError, Qi as StateError, Rn as DagBuilder, Sr as AssetPublisher, Ti as AssemblyReader, Ui as LockError, ea as formatError, gt as CloudControlProvider, ir as S3StateBackend, ji as resetAwsClients, ki as AwsClients, ln as DiffCalculator, na as normalizeAwsError, ot as ProviderRegistry, qi as ProvisioningError, r as DeployEngine, rr as LockManager, rt as IAMRoleProvider, ta as isCdkdError, ti as Synthesizer, zn as TemplateParser } from "./deploy-engine-CcFbq2q8.js";
|
|
2
|
+
import { a as setLogger, n as getLogger, t as ConsoleLogger } from "./logger-6LP1UiFA.js";
|
|
3
3
|
|
|
4
4
|
export { AssemblyReader, AssetError, AssetPublisher, AwsClients, CdkdError, CloudControlProvider, ConfigError, ConsoleLogger, DagBuilder, DependencyError, DeployEngine, DiffCalculator, IAMRoleProvider, LockError, LockManager, ProviderRegistry, ProvisioningError, S3StateBackend, StateError, SynthesisError, Synthesizer, TemplateParser, clearBucketRegionCache, formatError, getAwsClients, getLogger, isCdkdError, normalizeAwsError, resetAwsClients, resolveBucketRegion, setAwsClients, setLogger };
|
|
@@ -193,6 +193,22 @@ const IDENT_MAX_CODE_POINTS = 255;
|
|
|
193
193
|
*/
|
|
194
194
|
const STACK_REF_MAX_CODE_POINTS = 1152;
|
|
195
195
|
/**
|
|
196
|
+
* The cap for a SECRET REFERENCE -- an ECS task definition's `ValueFrom`, the
|
|
197
|
+
* Secrets Manager ARN it classifies to, or the SSM parameter name.
|
|
198
|
+
*
|
|
199
|
+
* A separate constant from {@link ROLE_ARN_MAX_CODE_POINTS} because the grammar
|
|
200
|
+
* is genuinely different and the role figure is too small: an SSM parameter NAME
|
|
201
|
+
* runs to 1011 characters on its own, and AWS caps an ARN at 2048. Rendering
|
|
202
|
+
* `[cut: N more characters withheld]` in the message that says WHICH secret
|
|
203
|
+
* failed to resolve is the failure both constants exist to avoid
|
|
204
|
+
* (go-to-k/cdkd#3390 round 4).
|
|
205
|
+
*
|
|
206
|
+
* 2048 is AWS's documented ARN ceiling, which bounds every shape this cap
|
|
207
|
+
* serves -- the ARN forms by definition, and the bare parameter name by being
|
|
208
|
+
* shorter than the ARN containing it.
|
|
209
|
+
*/
|
|
210
|
+
const SECRET_REF_MAX_CODE_POINTS = 2048;
|
|
211
|
+
/**
|
|
196
212
|
* The shape of a value that renders WITHOUT a visible boundary: the characters
|
|
197
213
|
* a CloudFormation logical id, a resource type (`AWS::S3::Bucket`,
|
|
198
214
|
* `Custom::my-thing_v2@x`), a change type, a stack name -- including the
|
|
@@ -1408,5 +1424,5 @@ function setLogger(logger) {
|
|
|
1408
1424
|
}
|
|
1409
1425
|
|
|
1410
1426
|
//#endregion
|
|
1411
|
-
export {
|
|
1412
|
-
//# sourceMappingURL=logger-
|
|
1427
|
+
export { displayIdent as C, UNRENDERABLE as S, truncateCodePoints as T, withSkipPrefix as _, setLogger as a, SECRET_REF_MAX_CODE_POINTS as b, PATTERN_B_NAME_OPTIONS as c, applyDefaultNameForFallback as d, explicitNamePropertyFor as f, looksLikeCdkdGeneratedName as g, getCurrentStackName as h, reserveStdoutForPayload as i, PATTERN_B_NAME_PROPERTIES as l, generateResourceNameWithFallback as m, getLogger as n, runStackBuffered as o, generateResourceName as p, isStdoutReservedForPayload as r, getLiveRenderer as s, ConsoleLogger as t, PATTERN_B_RESOURCE_TYPES as u, withStackName as v, displaySafe as w, STACK_REF_MAX_CODE_POINTS as x, withoutGeneratedFallbackName as y };
|
|
1428
|
+
//# sourceMappingURL=logger-6LP1UiFA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger-6LP1UiFA.js","names":[],"sources":["../src/utils/display-safe.ts","../src/provisioning/resource-name.ts","../src/utils/live-renderer.ts","../src/utils/stack-context.ts","../src/utils/logger.ts"],"sourcesContent":["/**\n * Make an untrusted value safe to render in a terminal or persist into a log.\n *\n * A LEAF module with no imports, and deliberately in `src/utils/` rather than\n * beside its first caller: issue [#2170](https://github.com/go-to-k/cdkd/issues/2170)'s\n * review found the same rule being widened BY HAND one module at a time and\n * missing an instance every round — the change sanitized 1 of 5 readers of\n * `LockInfo.owner`. One shared definition, imported by everything that renders\n * such a value, is what stops the next reader from inheriting nothing.\n *\n * The stripped class is wider than C0 + DEL, which a first cut used and which\n * misses every mechanism that actually forges a line:\n *\n * - `U+0085` (NEL) and the C1 range — xterm reads `U+009B` as CSI in UTF-8;\n * - `U+2028` / `U+2029` — this text is PERSISTED and re-rendered by JSON and\n * web log viewers, where both are line terminators;\n * - `U+202A`-`U+202E` / `U+2066`-`U+2069` — the Trojan-Source bidi overrides\n * and isolates, which visually REORDER the command being pasted.\n *\n * Known residual, recorded rather than implied away: the invisible formatters\n * (`U+200B`-`U+200D`, `U+FEFF`) and the bidi MARKS (`U+200E` / `U+200F` /\n * `U+061C`) survive, as do bare RTL letters, which no denylist can reach. All\n * of them can only make a rendered name differ visually from its bytes — the\n * command a user pastes still acts on exactly what is shown, and the blast\n * radius stays the attacker's own stack name.\n *\n * A caller whose value has a KNOWN ASCII charset (a stack name, an AWS region)\n * should pass `asciiOnly`, which is a positive allowlist and therefore has no\n * such residual at all.\n *\n * ONE CALLER DELIBERATELY GOES WIDER, and it is recorded here so an editor of\n * the residual note above knows a second module now disagrees with it.\n * `src/deployment/outputs-export-alias.ts` deletes a class derived from\n * `\\p{Cc}` / `\\p{Cf}` / `\\p{Zl}` / `\\p{Zp}` /\n * `\\p{Default_Ignorable_Code_Point}`, because on THAT path the subject is a possibly\n * secret-bearing name in an operator's log: a plaintext split by a zero-width\n * character is READ as if it were contiguous, so it is disclosed without any\n * paste, and the command-forgery reasoning above does not transfer (issue\n * [#2874](https://github.com/go-to-k/cdkd/issues/2874)). Nothing here changes\n * -- widening this helper would alter every caller that merely wants a\n * terminal-safe string.\n */\n\n/**\n * Stand-in for a value with nothing renderable left after sanitization. Named\n * rather than inlined so two messages cannot disagree about what\n * \"unrenderable\" looks like.\n *\n * Homed HERE, in the leaf, since issue #3064: it used to live in\n * `src/state/lock-contention-message.ts`, which meant `src/utils/` and\n * `src/types/` could not use it without inverting the layering -- so\n * `formatError` dropped a clause instead, and a `src/types/` parser rendered\n * an unrenderable field as EMPTY, which reads as absent. That file re-exports\n * it, so its existing importers are unchanged.\n */\nexport const UNRENDERABLE = '<unrenderable>';\n\n/**\n * The RAW text a value would render as, before any sanitization.\n *\n * Shared by `displaySafe` and `displayIdent` so the two agree on what the input\n * WAS. `displayIdent` needs that to answer \"did sanitization change anything?\",\n * and answering it by comparing against the caller's `unknown` would be wrong\n * for every non-string: the comparison must be against the STRINGIFIED form,\n * which is what `displaySafe` actually sanitizes.\n *\n * ABSENT means nothing to display, not the WORD -- `displaySafe`'s own comment\n * below carries why, and this is the function that implements it.\n *\n * `String(value)` is NOT total: an object whose `toString` is not callable —\n * `{\"toString\": null}`, reachable through `JSON.parse` of a hand-edited record\n * (issue #2947) — makes it throw, and a display helper that throws takes the\n * whole render with it. The fallback is `Object.prototype.toString`, which\n * calls none of the object's own methods — it reads only\n * `Symbol.toStringTag`, a key `JSON.parse` cannot produce — so it cannot\n * throw for any JSON-derived value. Every value `String` already handled\n * renders exactly as before; only the throwing ones change.\n *\n * `safeStringify` in `aws-failure-text.ts` guards the SAME coercion and is not\n * the same helper: it answers a failed conversion with a sentence rather than\n * `[object Object]`, because its output is a persisted failure reason an\n * operator reads rather than terminal output, and it sanitises nothing. This\n * one stays the answer on any path that RENDERS or LOGS the value.\n */\nfunction toDisplayText(value: unknown): string {\n if (value === undefined || value === null) return '';\n try {\n return String(value);\n } catch {\n // The fallback can throw TOO -- a throwing `Symbol.toStringTag` getter, or\n // a Proxy whose `get` trap throws -- and an escaping exception from a\n // DISPLAY helper takes the whole render with it, which is the failure mode\n // the first fallback was added to prevent. A second `catch` ends the\n // regress: there is no third expression to evaluate.\n try {\n return Object.prototype.toString.call(value);\n } catch {\n return UNRENDERABLE;\n }\n }\n}\n\n/**\n * The ONE sanitizing step, shared so `displaySafe` and `displayIdent` cannot\n * drift: `displayIdent` must sanitize the SAME text it compares against, and it\n * can only do that by holding the raw text itself (see its rule 3).\n */\nfunction sanitizeAsciiOnly(text: string): string {\n // Printable ASCII only. Correct for a stack name or an AWS region, both of\n // which have a known charset.\n return text.replace(/[^ -~]/g, ' ').trim();\n}\n\nexport function displaySafe(value: unknown, opts?: { asciiOnly?: boolean }): string {\n // ABSENT means nothing to display, not the WORD. `String(undefined)` is\n // `'undefined'` — a truthy string — so a caller keying its\n // \"is there anything here?\" decision on the result was silently answered\n // \"yes\" for a lock.json with no `owner`, printing `held by undefined` while\n // certifying that the holder was live. The callers that key a decision on\n // emptiness — the lock summary, and every refusal that falls back to\n // `UNRENDERABLE` — would each need this same rule, so it lives here rather\n // than at each of them. Not all of them do: `ConsoleLogger` concatenates the\n // result and `sameLockIdentity` only compares two of them, and neither is\n // harmed by it.\n const text = toDisplayText(value);\n if (text === '') return '';\n if (opts?.asciiOnly) return sanitizeAsciiOnly(text);\n return text\n .replace(\n // eslint-disable-next-line no-control-regex\n /[\\u0000-\\u001f\\u007f-\\u009f\\u2028\\u2029\\u202a-\\u202e\\u2066-\\u2069]/g,\n ' '\n )\n .trim();\n}\n\n/**\n * Cut `text` to at most `maxCodePoints` CODE POINTS, never splitting a\n * surrogate pair.\n *\n * `String.prototype.slice` counts UTF-16 code units, so a cut landing between\n * the two halves of an astral character leaves a lone high surrogate at the\n * end — rendered as a replacement character or dropped, depending on the\n * terminal (issue #2947). One helper rather than a fix at each call site: a\n * truncation site adopts the rule by calling it, and go-to-k/cdkd#3018 tracks\n * the one known site that does not yet.\n *\n * `truncated` reports whether anything was actually cut, so a caller marking\n * the cut (`…`) does not mark a value that was exactly the window's length.\n */\nexport function truncateCodePoints(\n text: string,\n maxCodePoints: number\n): { text: string; truncated: boolean } {\n const codePoints = Array.from(text);\n if (codePoints.length <= maxCodePoints) return { text, truncated: false };\n return { text: codePoints.slice(0, maxCodePoints).join(''), truncated: true };\n}\n\n/**\n * The DEFAULT longest an identifier this module renders can legitimately be.\n * 255 is CloudFormation's cap on a logical id, the longest of the identifier\n * shapes `displayIdent` sees by default (a resource type is at most\n * `64::64::64::MODULE`, a CloudFormation stack name 128, a region 25, a run id\n * ~40); a longer value is not one of those identifiers, whatever else it is.\n *\n * It is a DEFAULT and not a universal because one caller's identifier is\n * legitimately longer -- see `STACK_REF_MAX_CODE_POINTS`. A caller whose value\n * has a different grammar passes its own `maxCodePoints` rather than widening\n * this one, so the shapes above keep the tightest cap their grammar allows.\n */\nexport const IDENT_MAX_CODE_POINTS = 255;\n\n/**\n * The cap for a cdkd STATE-RECORD stack name, which is NOT bounded by\n * CloudFormation's 128-character stack-name limit.\n *\n * `NestedStackProvider.deriveChildStackName` mints a child record's name as\n * `${parentStackName}~${nestedLogicalId}` and applies that RECURSIVELY, one `~`\n * segment per nesting level, so the length grows with nesting depth:\n *\n * 128 + 4 * (1 + 255) = 1152\n *\n * -- a 128-character CloudFormation root name plus four `~` + 255-character\n * logical-id segments, reading CloudFormation's five-level nesting quota as\n * root + 4.\n *\n * Why it needs to be past 255 at all: CDK's generated nested-stack logical ids\n * run ~60 characters (`XNestedStackXNestedStackResource<hash>`), so even a\n * 20-character root passes 255 at the fourth level -- and a name cut there\n * prints `[cut: N more characters withheld]` in the middle of a row that\n * `cdkd state list | while read -r ref` consumes. Every LEGITIMATE value under\n * this cap renders byte-identically (a spoofing value still gains quotes --\n * that is rule 3's whole job), and byte-identity on legitimate rows is what\n * makes the boundary rendering safe to adopt at all.\n *\n * TWO THINGS THIS IS NOT, both stated because the first revision of this\n * comment claimed them:\n *\n * 1. It is not an ENFORCED bound. cdkd never calls CloudFormation, and nothing\n * in `src/` validates stack-name length or nesting depth, so the cap is a\n * budget rather than a guarantee. It is NOT reachable by nesting deeper: at\n * CDK's ~60-character generated logical ids even a SIX-level chain is ~494\n * code points, so depth alone does not approach 1152. What reaches it is\n * length -- HAND-WRITTEN logical ids near CloudFormation's own 255-character\n * ceiling, stacked several levels deep. Reading the nesting quota as five\n * levels BELOW the root would put the figure at 1408. Treat 1152 as CDK\n * practice with a wide margin, not a proof.\n * 2. It does not keep the genuine trailing annotation on screen. At 255 that\n * was arguable; at 1152 the `(region)` is many wrapped lines away, and a\n * terminal WRAPPING a long quoted name can put a visual line that reads\n * exactly like a genuine row on screen with both quotes scrolled out of\n * view -- reachable well under either cap, so the raise does not create the\n * class, but the cap does not close it either. Tracked on\n * go-to-k/cdkd#3179. What the cap still does is bound the PAYLOAD.\n */\nexport const STACK_REF_MAX_CODE_POINTS = 128 + 4 * (1 + IDENT_MAX_CODE_POINTS);\n\n/**\n * The cap for an IAM ROLE ARN, which the 255 default TRUNCATES while still\n * legal.\n *\n * AWS bounds a role PATH at 512 characters and a role NAME at 64, on top of the\n * `arn:<partition>:iam::<12-digit account>:role` prefix -- so a perfectly valid\n * ARN reaches ~612 and `displayIdent`'s default would render\n * `[cut: N more characters withheld]` inside the very message that says WHICH\n * role failed to assume (go-to-k/cdkd#3390 round 3). Cutting the identifier out\n * of the sentence whose only job is to identify it is the failure this constant\n * exists to avoid, and it is the same argument `STACK_REF_MAX_CODE_POINTS`\n * makes for a nested-stack child's name.\n *\n * Derived rather than rounded, so the arithmetic is auditable: the longest\n * partition cdkd knows is `aws-iso-e` (9), and the fixed segments are\n * `arn:` + `:iam::` + 12 + `:role` = 27.\n *\n * It bounds the PAYLOAD, which is all a cap can do -- the wrapping caveat on\n * `STACK_REF_MAX_CODE_POINTS` applies here unchanged.\n */\nexport const ROLE_ARN_MAX_CODE_POINTS = 27 + 9 + 512 + 64;\n\n/**\n * The cap for a SECRET REFERENCE -- an ECS task definition's `ValueFrom`, the\n * Secrets Manager ARN it classifies to, or the SSM parameter name.\n *\n * A separate constant from {@link ROLE_ARN_MAX_CODE_POINTS} because the grammar\n * is genuinely different and the role figure is too small: an SSM parameter NAME\n * runs to 1011 characters on its own, and AWS caps an ARN at 2048. Rendering\n * `[cut: N more characters withheld]` in the message that says WHICH secret\n * failed to resolve is the failure both constants exist to avoid\n * (go-to-k/cdkd#3390 round 4).\n *\n * 2048 is AWS's documented ARN ceiling, which bounds every shape this cap\n * serves -- the ARN forms by definition, and the bare parameter name by being\n * shorter than the ARN containing it.\n */\nexport const SECRET_REF_MAX_CODE_POINTS = 2048;\n\n/**\n * The shape of a value that renders WITHOUT a visible boundary: the characters\n * a CloudFormation logical id, a resource type (`AWS::S3::Bucket`,\n * `Custom::my-thing_v2@x`), a change type, a stack name -- including the\n * `Parent~Child` name cdkd mints for a nested-stack child -- a region, a\n * `deployments/` run id, an S3 key or a role ARN may contain. No space, no\n * bracket, no quote -- so a value matching it cannot plant a `(type)` /\n * `-- reason` annotation of the surrounding line inside itself. Known\n * residual, cosmetic: an IAM path may legally carry `!#$%&'()*`, so a role ARN\n * with one renders quoted; those characters are exactly the boundary-forging\n * set, so they stay out.\n *\n * `,` STAYS IN THIS SET, and issue #3164's review measured the cost of that.\n * Two callers join rendered values with `', '`, and the separator is split\n * across the value and the formatter -- a name ending in a bare `,` is followed\n * by the formatter's own ` (region)`, so `ProdStack,` renders\n * `ProdStack, (us-east-1)` and a two-target list reads as THREE entries. Only\n * `state refresh-observed` prints a count beside its list; `state orphan`\n * prints none, which is the worse of the two. Removing `,` would close that, and was tried:\n * it regresses a LEGITIMATE value class, because an IAM role name allows\n * `[\\w+=,.@-]`, so `arn:aws:iam::…:role/cdkd-deploy+role,x=y` is a real role\n * ARN this module renders and `display-safe.test.ts` pins as an identity shape.\n * Quoting every such ARN to disambiguate a list that does not contain ARNs is\n * the wrong trade, so the joined-list ambiguity is recorded on\n * go-to-k/cdkd#3179 rather than paid for here.\n */\nconst PLAIN_IDENT = /^[A-Za-z0-9:_@./+=,~-]+$/;\n\n/**\n * Render an untrusted IDENTIFIER -- a journal or S3-key field with a known\n * ASCII charset -- into a message a terminal will show (issues\n * [#3064](https://github.com/go-to-k/cdkd/issues/3064) /\n * [#3092](https://github.com/go-to-k/cdkd/issues/3092)). Three rules, applied\n * in this order:\n *\n * 1. `displaySafe(value, { asciiOnly: true })`, then `UNRENDERABLE` for a value\n * with nothing renderable left -- the same allowlist + fallback every\n * caller used to spell for itself.\n * 2. A value longer than `opts.maxCodePoints` (default `IDENT_MAX_CODE_POINTS`)\n * is CUT there and the count of withheld characters appended, bounding the\n * PAYLOAD a planted id can put on the line. It does not bound what a\n * terminal then WRAPS: a long quoted value can still wrap so that a visual\n * line reads like a genuine row with the quotes off-screen, at either cap.\n * A caller whose identifier has a LONGER legitimate grammar passes its own\n * cap -- `STACK_REF_MAX_CODE_POINTS` is the one such caller today -- because\n * a cut that fires on a LEGITIMATE value breaks the byte-identity that makes\n * rule 3 safe to adopt at all.\n * 3. A value is rendered as a JSON string literal -- so its BOUNDARY is\n * visible -- unless BOTH of these hold: sanitization was the IDENTITY on it,\n * and what is left is a `PLAIN_IDENT`. The allowlist alone cannot stop an\n * all-ASCII `X (AWS::RDS::DBInstance) -- already reverted` from reading as\n * cdkd's own annotation inside a real row; quoting it makes the row read\n * `\"X (AWS::RDS::DBInstance) -- already reverted\" (AWS::S3::Bucket)`, and\n * JSON escaping keeps an embedded `\"` from faking the closing quote.\n * Conditional on purpose: every legitimate value survives sanitization\n * unchanged AND is a plain identifier, so it renders exactly as it always\n * did -- no fixture, no unit pin and no operator's grep changes, and only a\n * value that could spoof gains quotes.\n *\n * THE IDENTITY HALF IS NOT REDUNDANT WITH THE ALLOWLIST, and omitting it was\n * a live spoof (issue #3164 review): `displaySafe` maps every\n * non-printable-ASCII character to a space and then TRIMS, so padding is\n * ERASED before the `PLAIN_IDENT` test ever runs. A planted\n * `cdkd/ProdStack /us-east-1/state.json` -- one trailing space -- therefore\n * tested as plain, rendered UNQUOTED, and printed byte-identical to the\n * genuine `ProdStack` in `us-east-1`; `listStacks` keys its dedupe on\n * `stackName\\0region`, so both refs survive to the output and a\n * `sort -u`-ing consumer collapses them. Leading space, a tab, a NUL, an ESC\n * and a padded REGION all did the same with a one-character input. Comparing\n * against the raw text closes the class at its root rather than per padding\n * character. `src/state/lock-contention-message.ts` reached the same rule\n * first, for the same reason.\n *\n * A caller comparing the result against the input (the `--orphan <id>` remedy\n * prints its id only when this function is the identity on it) inherits all\n * three: a quoted, cut or fallback rendering is never pasted as a command\n * argument.\n *\n * NOT for a value that is USED rather than shown (a lookup key, a provider\n * argument), and NOT for free-form text (an SDK error message legitimately\n * carries spaces and non-ASCII; it takes `displaySafe()` directly).\n */\nexport function displayIdent(value: unknown, opts?: { maxCodePoints?: number }): string {\n // Evaluate the input ONCE. Two calls would read `value.toString()` twice, and\n // a non-deterministic one then re-opens the very spoof rule 3 exists to\n // close: `{ toString: () => n++ === 0 ? 'ProdStack ' : 'ProdStack' }` renders\n // the PADDED text and compares it against the UNPADDED second reading, so\n // `altered` is false and the value goes out bare. Unreachable from today's\n // callers, which pass S3-key strings and `JSON.parse` output -- but a control\n // that a hostile `toString` can switch off is not a control, and this\n // function IS the control.\n const raw = toDisplayText(value);\n if (raw === '') return UNRENDERABLE;\n const clean = sanitizeAsciiOnly(raw);\n if (!clean) return UNRENDERABLE;\n // Floor the caller's cap at 1, and fall back to the default for a non-finite\n // one. Stated precisely, because the first revision of this comment guessed:\n // `truncateCodePoints` slices, so a cap at or below `-length` yields the\n // EMPTY string and the value collapses to `\"\"` (the withheld count stays\n // accurate throughout -- that part was never wrong). `Math.floor` matters\n // only for `0 < cap < 1`, where an unfloored `slice(0, 0.5)` is also empty.\n //\n // `Infinity` deliberately takes the DEFAULT rather than meaning \"no cap\": a\n // caller asking for no cap gets 255 and a cut, which is the opposite of the\n // intent -- recorded rather than silently reinterpreted, since no caller\n // passes one and guessing which way they meant it is how a display helper\n // acquires a second contract.\n const requested = opts?.maxCodePoints ?? IDENT_MAX_CODE_POINTS;\n const cap = Number.isFinite(requested)\n ? Math.max(1, Math.floor(requested))\n : IDENT_MAX_CODE_POINTS;\n const { text, truncated } = truncateCodePoints(clean, cap);\n // Rule 3. `altered` is the half the allowlist cannot supply: sanitization\n // trims, so padding is gone from `clean` and `PLAIN_IDENT` would pass a value\n // that did NOT arrive plain. Compared against `raw` -- the SAME text that was\n // sanitized, read once above -- so neither a non-string nor a\n // non-deterministic `toString` can make the two operands disagree.\n const altered = clean !== raw;\n const shown = !altered && PLAIN_IDENT.test(text) ? text : JSON.stringify(text);\n // `clean` is ASCII here, so `.length` counts characters.\n return truncated\n ? `${shown} [cut: ${clean.length - text.length} more characters withheld]`\n : shown;\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport { createHash } from 'node:crypto';\n\n/**\n * Per-async-context stack name. Resource-name generation reads this so that\n * concurrent deploys (`cdkd deploy --all` runs stacks in parallel up to\n * `--stack-concurrency`) don't fight over a single shared variable.\n *\n * History: this was `let currentStackName: string | undefined` until\n * 2026-05-01. Two parallel `deploy()` calls would each call\n * `setCurrentStackName(...)` and the second would overwrite the first;\n * any IAM Role / SQS Queue / etc. created by the first stack while the\n * second was active would get the second stack's prefix in its physical\n * name, then the second stack's own create attempt for the same logical\n * id would collide (\"Role with name X already exists\"). Switching to\n * `AsyncLocalStorage` scopes the value to each deploy's async chain.\n */\nconst stackNameStore = new AsyncLocalStorage<string>();\n\n/**\n * Run `fn` with `stackName` set as the stack name visible to\n * `generateResourceName` for the duration of the callback (and any\n * `await`s inside). Concurrent invocations each get an independent scope\n * — this is the safe API for parallel deploys.\n */\nexport function withStackName<T>(stackName: string, fn: () => Promise<T>): Promise<T>;\nexport function withStackName<T>(stackName: string, fn: () => T): T;\nexport function withStackName<T>(stackName: string, fn: () => T | Promise<T>): T | Promise<T> {\n return stackNameStore.run(stackName, fn);\n}\n\n/**\n * Read the current async context's stack name, if any.\n *\n * Returns `undefined` outside any `withStackName` / `setCurrentStackName`\n * scope. Used by the live renderer to scope per-stack in-flight task\n * entries so concurrent deploys don't clobber each other's tasks (same\n * `logicalId` in two stacks would collide on the singleton renderer's\n * task Map without this).\n */\nexport function getCurrentStackName(): string | undefined {\n return stackNameStore.getStore();\n}\n\n/**\n * Set the current async context's stack name.\n *\n * @deprecated Use {@link withStackName} for new code — it makes the scope\n * obvious at the call site. This setter now uses\n * `AsyncLocalStorage.enterWith` so it remains safe under\n * `--stack-concurrency > 1` (each `deploy()` call has its own async\n * resource, so the value does NOT leak across siblings), but\n * `withStackName` is structurally clearer.\n */\nexport function setCurrentStackName(stackName: string): void {\n stackNameStore.enterWith(stackName);\n}\n\n/**\n * Per-async-context \"skip the stack-name prefix on user-supplied physical\n * names\" flag. Read by `generateResourceName` when its caller passes\n * `userSupplied: true`; auto-generated-name paths\n * (`generateResourceName(logicalId, ...)`) ignore this flag.\n *\n * Scoped via AsyncLocalStorage so that `--stack-concurrency > 1` runs\n * cannot cross-contaminate — each deploy's body is wrapped in its own\n * `withSkipPrefix(...)` scope (the deploy CLI plumbs the resolved\n * `--no-prefix-user-supplied-names` value through here). Default\n * `false` preserves pre-PR behavior when the flag is not set.\n */\nconst skipPrefixStore = new AsyncLocalStorage<boolean>();\n\n/**\n * Run `fn` with the \"skip prefix on user-supplied names\" flag set to\n * `skip`. Mirrors {@link withStackName} — concurrent invocations each\n * get an independent scope so parallel deploys do not fight over a\n * single shared variable.\n *\n * Wrap this around `withStackName(...)` (innermost is `fn`) in the\n * deploy CLI: `withSkipPrefix(flag, () => withStackName(name, body))`.\n * Order does not matter — the two stores are independent — but\n * consistent ordering keeps the call sites readable.\n */\nexport function withSkipPrefix<T>(skip: boolean, fn: () => Promise<T>): Promise<T>;\nexport function withSkipPrefix<T>(skip: boolean, fn: () => T): T;\nexport function withSkipPrefix<T>(skip: boolean, fn: () => T | Promise<T>): T | Promise<T> {\n return skipPrefixStore.run(skip, fn);\n}\n\n/**\n * Read the current async context's skip-prefix flag. Defaults to\n * `false` when no `withSkipPrefix` scope is active.\n *\n * Public for unit tests; `generateResourceName` consumes this\n * internally.\n */\nexport function getCurrentSkipPrefix(): boolean {\n return skipPrefixStore.getStore() ?? false;\n}\n\n/**\n * Resource types whose pre-PR #297 code path ran user-supplied\n * physical names through `generateResourceName` (= got the stack-name\n * prefix). These are the only types affected by\n * `--no-prefix-user-supplied-names`; flipping the flag against an\n * existing stack proposes REPLACEMENT on every state resource of\n * one of these types whose `physicalId` is still prefixed.\n *\n * Pattern A providers (Lambda, S3, SNS, SQS, DynamoDB, Logs LogGroup,\n * Events Rule, etc.) historically short-circuited user-supplied names\n * **out** of `generateResourceName` entirely — those types never got\n * the prefix regardless of the flag, so they are NOT included here.\n *\n * Used by the deploy-time pre-flight migration check in\n * `src/cli/commands/prefix-migration-check.ts`. Keep in sync with the\n * Pattern B provider call sites that consume\n * `generateResourceNameWithFallback(...)`.\n */\nexport const PATTERN_B_RESOURCE_TYPES: readonly string[] = [\n 'AWS::IAM::Role',\n 'AWS::IAM::User',\n 'AWS::IAM::Group',\n 'AWS::IAM::InstanceProfile',\n 'AWS::IAM::ManagedPolicy',\n 'AWS::ElasticLoadBalancingV2::LoadBalancer',\n 'AWS::ElasticLoadBalancingV2::TargetGroup',\n] as const;\n\n/**\n * For each Pattern B resource type, the CFn template `Properties` field\n * the user sets to supply a physical name (`new iam.Role(this, 'X',\n * { roleName: 'my-role' })` → `Properties.RoleName: 'my-role'`).\n *\n * Used by the prefix-migration check to distinguish user-supplied\n * physical names (which the v0.94.0 default flip would actually\n * REPLACE on next deploy) from auto-generated logical-id-fallback\n * names (which keep the prefix in BOTH old and new default — no\n * REPLACE pending). Without this discriminator, the migration\n * check naively flags every prefix-style physicalId regardless of\n * its origin, surfacing a false-positive WARNING on auto-generated\n * names that won't actually be touched.\n *\n * Keep in sync with `PATTERN_B_RESOURCE_TYPES` and with each\n * provider's `Properties[<NameField>]` lookup in\n * `src/provisioning/providers/`.\n */\nexport const PATTERN_B_NAME_PROPERTIES: Readonly<Record<string, string>> = {\n 'AWS::IAM::Role': 'RoleName',\n 'AWS::IAM::User': 'UserName',\n 'AWS::IAM::Group': 'GroupName',\n 'AWS::IAM::InstanceProfile': 'InstanceProfileName',\n 'AWS::IAM::ManagedPolicy': 'ManagedPolicyName',\n 'AWS::ElasticLoadBalancingV2::LoadBalancer': 'Name',\n 'AWS::ElasticLoadBalancingV2::TargetGroup': 'Name',\n};\n\n/**\n * For each Pattern B resource type, the `generateResourceName` options each\n * provider passes at its name call site (maxLength + any lowercase /\n * allowedPattern). Keep in sync with the provider call sites\n * (`generateResourceNameWithFallback(...)` in\n * `src/provisioning/providers/`).\n *\n * Used by the prefix-migration check to recompute BOTH the legacy\n * (prefixed) and the post-v0.94 (unprefixed) physical name for a recorded\n * user-supplied name through the SAME sanitize / truncate pipeline the\n * provider used — so a name that needed sanitization (`my_role` ->\n * `my-role`) or hash-suffix truncation (> maxLength) is compared exactly,\n * not via a naive prefix concat / strip.\n */\nexport const PATTERN_B_NAME_OPTIONS: Readonly<Record<string, ResourceNameOptions>> = {\n 'AWS::IAM::Role': { maxLength: 64 },\n 'AWS::IAM::User': { maxLength: 64 },\n 'AWS::IAM::Group': { maxLength: 128 },\n 'AWS::IAM::InstanceProfile': { maxLength: 128 },\n 'AWS::IAM::ManagedPolicy': { maxLength: 128 },\n 'AWS::ElasticLoadBalancingV2::LoadBalancer': { maxLength: 32 },\n 'AWS::ElasticLoadBalancingV2::TargetGroup': { maxLength: 32 },\n};\n\n/**\n * Options for generating a resource name.\n */\nexport interface ResourceNameOptions {\n /** Maximum length for the name (e.g., 32 for ALB/TG, 64 for IAM, 63 for S3) */\n maxLength: number;\n /** Whether to force lowercase (e.g., S3 buckets) */\n lowercase?: boolean;\n /** Allowed character regex pattern. Characters not matching will be removed.\n * Default: /[^a-zA-Z0-9-]/ (alphanumeric + hyphen) */\n allowedPattern?: RegExp;\n /**\n * `true` when the caller is passing a name the user explicitly\n * declared in their CDK code (e.g. `new iam.Role(this, 'X', {\n * roleName: 'my-role' })`). `false` (default) when the caller is\n * passing the logical-id fallback or any other cdkd-generated value.\n *\n * Combined with the per-deploy `withSkipPrefix(true)` flag, a\n * `userSupplied: true` call skips the stack-name prefix and returns\n * the user's declared name verbatim (after the same sanitize /\n * truncate pipeline). When `userSupplied` is `false` OR\n * `withSkipPrefix` is unset / `false`, the stack-name prefix is\n * applied (pre-PR behavior).\n *\n * This split is load-bearing: cdkd's stack-scoping concern (prefix\n * for cross-stack uniqueness on auto-generated names) must stay\n * coupled to the auto-generated path, NOT to user-declared names —\n * those belong to the user.\n */\n userSupplied?: boolean;\n}\n\n/**\n * Generate a unique resource name from the logical ID.\n *\n * Generates names in CloudFormation-compatible format:\n * `{StackName}-{LogicalId}-{Hash}` (truncated to maxLength).\n *\n * @param name The raw name (from properties or logicalId fallback)\n * @param options Length and character constraints\n * @returns A sanitized, truncated name that fits the constraints\n */\nexport function generateResourceName(name: string, options: ResourceNameOptions): string {\n const {\n maxLength,\n lowercase = false,\n allowedPattern = /[^a-zA-Z0-9-]/g,\n userSupplied = false,\n } = options;\n\n // Include stack name for uniqueness (like CloudFormation does).\n //\n // The prefix is suppressed when the caller marked the name as\n // user-supplied AND the per-deploy `withSkipPrefix(true)` flag is\n // active — the user owns that name and cdkd should not rewrite it.\n // Every other path (logical-id fallback, no withSkipPrefix scope,\n // flag set to false) keeps the prefix for cross-stack uniqueness.\n const currentStackName = stackNameStore.getStore();\n const shouldPrefix = currentStackName && !(userSupplied && getCurrentSkipPrefix());\n const fullName = shouldPrefix ? `${currentStackName}-${name}` : name;\n\n // Apply lowercase BEFORE pattern matching (so A-Z aren't removed by /[^a-z0-9.-]/)\n let sanitized = lowercase ? fullName.toLowerCase() : fullName;\n sanitized = sanitized.replace(allowedPattern, '-');\n\n // Collapse consecutive hyphens and remove leading/trailing\n sanitized = sanitized.replace(/-{2,}/g, '-').replace(/^-+|-+$/g, '');\n\n if (sanitized.length <= maxLength) {\n return sanitized;\n }\n\n // Truncate with hash suffix for uniqueness\n const hash = createHash('sha256').update(fullName).digest('hex').substring(0, 8);\n const maxPrefixLength = maxLength - hash.length - 1; // -1 for separator\n const prefix = sanitized.substring(0, maxPrefixLength).replace(/-+$/, '');\n\n return `${prefix}-${hash}`;\n}\n\n/**\n * Best-effort: was this physical name GENERATED by cdkd from the logical id,\n * rather than declared in the user's template? (issue #1636)\n *\n * The replacement-collision refusals in `deploy-engine.ts` used to assert\n * \"the resource has a user-supplied physical name\" for EVERY colliding\n * resource, and then prescribe \"rename the resource in your CDK code\". For a\n * resource the template never named that is false in both halves: the name is\n * cdkd's own `{stackName}-{logicalId}` — which has no random component, so the\n * create-first attempt lands on the name the old resource still holds exactly\n * as it would for a user-named one — and \"rename\" would mean renaming the\n * CONSTRUCT (changing the logical id), a far more disruptive action than the\n * message implies. A user who reads \"user-supplied physical name\" and finds\n * none in their template cannot connect the message to their code at all.\n *\n * The test is the naming scheme run backwards: {@link generateResourceName}\n * with `userSupplied: false` always builds from `{stackName}-{logicalId}`,\n * while a template-supplied name builds from `{stackName}-{userName}` (or the\n * bare user name under `--no-prefix-user-supplied-names`). So the two differ\n * unless the declared name happens to EQUAL the logical id.\n *\n * **Deliberately approximate, and biased toward the safe answer.** The\n * per-type `maxLength` / `lowercase` / `allowedPattern` options live in the\n * providers, and the engine does not know which type maps to which — so this\n * compares case-insensitively on the alphanumeric skeleton and accepts the\n * truncated `…-<8 hex>` form by prefix. Every unresolved case answers `false`:\n * a `false` merely keeps the pre-#1636 wording, which is correct for the\n * template-named resource that is the likelier reading of an unrecognized\n * name, whereas a wrong `true` would tell a user who DID name their resource\n * to go looking for a name they never wrote.\n *\n * @param physicalId the name AWS currently holds for the existing resource\n * @param logicalId the template logical id of the resource being replaced\n * @param stackName the deploying stack's name (absent = unresolvable, `false`)\n */\nexport function looksLikeCdkdGeneratedName(\n physicalId: string,\n logicalId: string,\n stackName: string | undefined\n): boolean {\n if (!physicalId || !logicalId || !stackName) return false;\n // The skeleton drops every separator the per-type `allowedPattern` could\n // have rewritten (a hyphen, an underscore, a dot) so an S3-style\n // lowercase-and-dot name still matches its own derivation.\n const skeleton = (value: string): string => value.toLowerCase().replace(/[^a-z0-9]/g, '');\n const derived = skeleton(`${stackName}-${logicalId}`);\n const actual = skeleton(physicalId);\n if (actual === derived) return true;\n // Truncated form: `{prefix}-{8 hex}` where prefix is a cut of the same\n // derivation. Require BOTH halves — a bare prefix match would accept a\n // template-supplied name that merely starts the same way.\n const truncated = /^(.*)-([0-9a-f]{8})$/.exec(physicalId.toLowerCase());\n if (!truncated) return false;\n const prefix = skeleton(truncated[1]!);\n return prefix.length > 0 && derived.startsWith(prefix);\n}\n\n/**\n * Generate a resource name from a user-declared physical name OR\n * fall back to the logical id.\n *\n * Wraps {@link generateResourceName} to express the Pattern B call-site\n * shape (`generateResourceName((properties['Name'] as string | undefined)\n * || logicalId, opts)`) as a single typed helper. The user-supplied\n * branch passes `userSupplied: true`, which makes the per-deploy\n * `withSkipPrefix(true)` flag drop the stack-name prefix on that name.\n * The fallback (logical-id) branch is `userSupplied: false` and keeps\n * the prefix regardless of the flag — auto-generated names rely on\n * the prefix for cross-stack uniqueness.\n *\n * Use at every Pattern B provider call site (currently IAM Role, IAM\n * User, IAM Group, IAM InstanceProfile, ELBv2 LoadBalancer, ELBv2\n * TargetGroup) so the `--no-prefix-user-supplied-names` flag controls\n * those types consistently. Pattern A providers (Lambda, S3, SNS,\n * SQS, DynamoDB, etc.) do NOT need this helper — they already\n * short-circuit the user-supplied name out of the\n * `generateResourceName` call entirely, so the prefix is never\n * applied to user-supplied names regardless of the flag.\n */\nexport function generateResourceNameWithFallback(\n userSuppliedName: string | undefined,\n logicalId: string,\n options: Omit<ResourceNameOptions, 'userSupplied'>\n): string {\n if (userSuppliedName !== undefined && userSuppliedName !== '') {\n return generateResourceName(userSuppliedName, { ...options, userSupplied: true });\n }\n return generateResourceName(logicalId, { ...options, userSupplied: false });\n}\n\n/**\n * Default name generation rules for a Cloud Control CREATE.\n *\n * When an SDK provider falls back to CC API, the resource may need a\n * default name that the SDK provider would have generated. A type with NO\n * SDK provider can need one too, when its Cloud Control create handler fails\n * without a name its schema leaves optional (`AWS::Lambda::CapacityProvider`,\n * issue #3174). This map defines the name property and generation options for\n * each resource type. The name is filled on a create only; the update path\n * takes it back out (`withoutGeneratedFallbackName`).\n *\n * Format: resourceType → { nameProperty, options, postProcess? }\n */\nconst FALLBACK_NAME_RULES: Record<\n string,\n {\n nameProperty: string;\n options: ResourceNameOptions;\n }\n> = {\n 'AWS::S3::Bucket': { nameProperty: 'BucketName', options: { maxLength: 63, lowercase: true } },\n 'AWS::SQS::Queue': { nameProperty: 'QueueName', options: { maxLength: 80 } },\n 'AWS::SNS::Topic': { nameProperty: 'TopicName', options: { maxLength: 256 } },\n 'AWS::Lambda::Function': { nameProperty: 'FunctionName', options: { maxLength: 64 } },\n 'AWS::Lambda::LayerVersion': { nameProperty: 'LayerName', options: { maxLength: 64 } },\n // No SDK provider — Cloud Control only. The schema does not require the\n // name, yet a create without one fails with `Resource Handler Internal\n // Failure` (issue #3174). Schema: maxLength 140, `[a-zA-Z0-9-_]+`; the\n // default `allowedPattern` emits a subset of that.\n 'AWS::Lambda::CapacityProvider': {\n nameProperty: 'CapacityProviderName',\n options: { maxLength: 140 },\n },\n 'AWS::IAM::Role': { nameProperty: 'RoleName', options: { maxLength: 64 } },\n 'AWS::IAM::Policy': { nameProperty: 'PolicyName', options: { maxLength: 64 } },\n 'AWS::IAM::ManagedPolicy': {\n nameProperty: 'ManagedPolicyName',\n options: { maxLength: 128 },\n },\n 'AWS::IAM::User': { nameProperty: 'UserName', options: { maxLength: 64 } },\n 'AWS::IAM::Group': { nameProperty: 'GroupName', options: { maxLength: 128 } },\n 'AWS::IAM::InstanceProfile': {\n nameProperty: 'InstanceProfileName',\n options: { maxLength: 128 },\n },\n 'AWS::DynamoDB::Table': { nameProperty: 'TableName', options: { maxLength: 255 } },\n 'AWS::ECR::Repository': {\n nameProperty: 'RepositoryName',\n options: { maxLength: 256, lowercase: true },\n },\n 'AWS::ECS::Cluster': { nameProperty: 'ClusterName', options: { maxLength: 255 } },\n 'AWS::ECS::Service': { nameProperty: 'ServiceName', options: { maxLength: 255 } },\n 'AWS::Logs::LogGroup': { nameProperty: 'LogGroupName', options: { maxLength: 512 } },\n 'AWS::CloudWatch::Alarm': { nameProperty: 'AlarmName', options: { maxLength: 256 } },\n 'AWS::Events::Rule': { nameProperty: 'Name', options: { maxLength: 64 } },\n 'AWS::Events::EventBus': { nameProperty: 'Name', options: { maxLength: 256 } },\n 'AWS::Kinesis::Stream': { nameProperty: 'Name', options: { maxLength: 128 } },\n 'AWS::StepFunctions::StateMachine': {\n nameProperty: 'StateMachineName',\n options: { maxLength: 80 },\n },\n 'AWS::SecretsManager::Secret': {\n nameProperty: 'Name',\n options: { maxLength: 512, allowedPattern: /[^a-zA-Z0-9-/_]/g },\n },\n 'AWS::SSM::Parameter': { nameProperty: 'Name', options: { maxLength: 2048 } },\n 'AWS::Cognito::UserPool': { nameProperty: 'UserPoolName', options: { maxLength: 128 } },\n 'AWS::ElastiCache::SubnetGroup': {\n nameProperty: 'CacheSubnetGroupName',\n options: { maxLength: 255, lowercase: true },\n },\n 'AWS::ElastiCache::CacheCluster': {\n nameProperty: 'ClusterName',\n options: { maxLength: 40, lowercase: true },\n },\n 'AWS::RDS::DBSubnetGroup': {\n nameProperty: 'DBSubnetGroupName',\n options: { maxLength: 255, lowercase: true },\n },\n 'AWS::RDS::DBCluster': {\n nameProperty: 'DBClusterIdentifier',\n options: { maxLength: 63, lowercase: true },\n },\n 'AWS::RDS::DBInstance': {\n nameProperty: 'DBInstanceIdentifier',\n options: { maxLength: 63, lowercase: true },\n },\n // DocumentDB — RDS-shaped API; same name constraints.\n 'AWS::DocDB::DBSubnetGroup': {\n nameProperty: 'DBSubnetGroupName',\n options: { maxLength: 255, lowercase: true },\n },\n 'AWS::DocDB::DBCluster': {\n nameProperty: 'DBClusterIdentifier',\n options: { maxLength: 63, lowercase: true },\n },\n 'AWS::DocDB::DBInstance': {\n nameProperty: 'DBInstanceIdentifier',\n options: { maxLength: 63, lowercase: true },\n },\n // Neptune — RDS-shaped API; same name constraints.\n 'AWS::Neptune::DBSubnetGroup': {\n nameProperty: 'DBSubnetGroupName',\n options: { maxLength: 255, lowercase: true },\n },\n 'AWS::Neptune::DBCluster': {\n nameProperty: 'DBClusterIdentifier',\n options: { maxLength: 63, lowercase: true },\n },\n 'AWS::Neptune::DBInstance': {\n nameProperty: 'DBInstanceIdentifier',\n options: { maxLength: 63, lowercase: true },\n },\n 'AWS::ElasticLoadBalancingV2::LoadBalancer': {\n nameProperty: 'Name',\n options: { maxLength: 32 },\n },\n 'AWS::ElasticLoadBalancingV2::TargetGroup': {\n nameProperty: 'Name',\n options: { maxLength: 32 },\n },\n 'AWS::WAFv2::WebACL': { nameProperty: 'Name', options: { maxLength: 128 } },\n 'AWS::CodeBuild::Project': { nameProperty: 'Name', options: { maxLength: 255 } },\n 'AWS::S3Express::DirectoryBucket': {\n nameProperty: 'BucketName',\n options: { maxLength: 63, lowercase: true },\n },\n};\n\n/**\n * Apply default name generation for CC API fallback.\n *\n * When a resource doesn't have an explicit name property set,\n * generates the same default name that the SDK provider would have created.\n * This ensures consistent naming regardless of whether SDK or CC API handles the resource.\n *\n * @param logicalId Logical ID from the template\n * @param resourceType CloudFormation resource type\n * @param properties Resource properties (will not be mutated)\n * @returns Properties with default name applied if needed, or original properties if no rule exists\n */\nexport function applyDefaultNameForFallback(\n logicalId: string,\n resourceType: string,\n properties: Record<string, unknown>\n): Record<string, unknown> {\n const rule = FALLBACK_NAME_RULES[resourceType];\n if (!rule) return properties;\n\n // If the name property is already set, no need to generate\n if (properties[rule.nameProperty]) return properties;\n\n const generatedName = generateResourceName(logicalId, rule.options);\n\n return {\n ...properties,\n [rule.nameProperty]: generatedName,\n };\n}\n\n/**\n * The name property {@link applyDefaultNameForFallback} fills for\n * `resourceType`, or `undefined` when it fills none.\n *\n * Read by its two siblings in this file, never by a caller outside it:\n * {@link withoutGeneratedFallbackName}, which the deploy engine calls from its\n * Cloud Control UPDATE path, and {@link explicitNamePropertyFor}, which layers\n * the adoption-only table on top. The UPDATE path must NOT send the generated\n * name (issue #3174). The name only matters to a CREATE: an\n * update is a JSON Patch against the recorded bag, which holds the template's\n * resolved properties and so never carries a generated name. Injecting one\n * there makes every update patch `add` the name — harmless while the live name\n * equals the generated one. For a resource imported under any other name it is\n * a real change: refused where the name is create-only (as it is for\n * `AWS::Lambda::CapacityProvider`), and a RENAME where the type accepts the\n * update (`AWS::Cognito::UserPool`'s `UserPoolName`, for one).\n */\nexport function fallbackNamePropertyFor(resourceType: string): string | undefined {\n return FALLBACK_NAME_RULES[resourceType]?.nameProperty;\n}\n\n/**\n * `prepared` with the name {@link applyDefaultNameForFallback} GENERATED into\n * it taken back out — the bag the deploy engine hands a Cloud Control UPDATE\n * (issue #3174; why an update must not carry it is on\n * {@link fallbackNamePropertyFor}).\n *\n * The name counts as generated exactly when the fill would have produced it:\n * the type has a rule, `resolvedProps` leaves the property falsy (the fill's\n * own test, so an empty string is generated over too), and `prepared` carries\n * it. The property then goes back to what `resolvedProps` holds: deleted when\n * the template has no such key, the template's own falsy value when it does,\n * so the update's desired side agrees with the template on this key.\n * Otherwise `prepared` is returned by identity. Neither input is mutated.\n */\nexport function withoutGeneratedFallbackName(\n resourceType: string,\n resolvedProps: Record<string, unknown>,\n prepared: Record<string, unknown>\n): Record<string, unknown> {\n const nameProperty = fallbackNamePropertyFor(resourceType);\n if (nameProperty === undefined || resolvedProps[nameProperty] || !(nameProperty in prepared)) {\n return prepared;\n }\n const restored = { ...prepared };\n if (nameProperty in resolvedProps) {\n restored[nameProperty] = resolvedProps[nameProperty];\n } else {\n delete restored[nameProperty];\n }\n return restored;\n}\n\n/**\n * The template property that supplies an explicit physical name for\n * `resourceType`, or `undefined` when cdkd knows of none (issue #2934).\n *\n * Read by the orphan-adoption pre-pass, where it decides TWO things. It\n * answers whether THIS template hands the resource a name or lets cdkd generate\n * one — and, because an unknown type answers `undefined`, it is also the\n * pre-pass's ALLOW-LIST: a type absent from both tables is NOT adopted at all.\n *\n * That second role inverts what an earlier version of this doc described. It\n * said an unknown type reads as \"cdkd names this\" and adoption proceeds, whose\n * failure mode is a REPLACEMENT that destroys a resource `DeletionPolicy:\n * Retain` preserved — three review rounds each found one more type reaching it.\n * Now an absent type costs a missed adoption (go-to-k/cdkd#2916's diagnosis\n * still prints) rather than a deletion, so the tables may be incomplete in the\n * safe direction only.\n *\n * **An entry here ADMITS a type to adoption.** Before adding one, check the\n * three tests in `ADOPTION_REFUSED_TYPES` (`src/deployment/orphan-adoption.ts`)\n * — create mints a new resource instead of colliding, `update()` refuses, the\n * resulting replacement destroys the retained copy. A type meeting all three\n * belongs in THAT set even if it is named here; `AWS::Lambda::LayerVersion` is\n * exactly that shape and `AWS::ECS::TaskDefinition` is kept there as a belt\n * against a future `Family` entry.\n */\nexport function explicitNamePropertyFor(resourceType: string): string | undefined {\n return fallbackNamePropertyFor(resourceType) ?? ADOPTION_ONLY_NAME_PROPERTIES[resourceType];\n}\n\n/**\n * Name properties known to the ADOPTION guard but not to\n * {@link FALLBACK_NAME_RULES} (issue #2934).\n *\n * A separate table on purpose: `FALLBACK_NAME_RULES` decides what cdkd GENERATES\n * for a Cloud Control create, so adding an entry there changes the name every\n * later create of that type uses; an existing resource keeps its name, because\n * the Cloud Control update path does not send a generated one (issue #3174),\n * and an SDK provider's update never had one to send. This table\n * answers a strictly narrower question, \"does the template hand this resource\n * a name\", and generates nothing. An entry here still ADMITS its type to\n * adoption exactly as one there does, so it needs the same three-test check.\n *\n * Found by review: the SDK providers for the entries below generate a name\n * themselves. What that costs changed with the inversion described above, so\n * both halves are worth stating. An ABSENT type is refused outright — the\n * empty-`nameProperties` arm in `orphan-adoption.ts` runs BEFORE\n * `templateStillDeclares` — costing a missed adoption, with\n * go-to-k/cdkd#2916's diagnosis still printing. A WRONG entry is the live\n * hazard: a non-empty list clears the empty-list arm, `templateStillDeclares` then\n * looks for a property the template does not use, so an explicit name reads as\n * \"no explicit name\", the record is adopted, and the diff turns the create-only\n * name into a REPLACE that deletes the resource being rescued. Supplying a name\n * is the natural user move after that diagnosis, so this is a reachable path,\n * not a theoretical one.\n *\n * `AWS::Budgets::Budget` is deliberately absent rather than forgotten: its name\n * is NESTED (`Budget.BudgetName`, create-only), which neither this flat table\n * nor `templateStillDeclares`' top-level property read can express, so adoption\n * of that type is unguarded today — go-to-k/cdkd#2943 carries it.\n *\n * `AWS::ECS::TaskDefinition` was added here and REMOVED again, which is worth\n * recording: its physical id is the revision ARN rather than a\n * `generateResourceName` output, so the by-construction premise this whole\n * guard rests on is false for it and an entry here could never match. It is\n * refused by the adoption pre-pass instead — and the ALLOW-LIST gate is what\n * refuses it: absent from both tables here, it never reaches adoption at all.\n * `ADOPTION_REFUSED_TYPES` in `src/deployment/orphan-adoption.ts` carries it as\n * a BELT against a future entry here (named rather than `{@link}`-ed: this\n * module does not import it, so the link would render as dead text).\n */\nconst ADOPTION_ONLY_NAME_PROPERTIES: Record<string, string> = {\n 'AWS::AutoScaling::AutoScalingGroup': 'AutoScalingGroupName',\n 'AWS::CodeCommit::Repository': 'RepositoryName',\n 'AWS::DynamoDB::GlobalTable': 'TableName',\n 'AWS::RDS::DBProxy': 'DBProxyName',\n 'AWS::RDS::DBProxyEndpoint': 'DBProxyEndpointName',\n 'AWS::Scheduler::Schedule': 'Name',\n};\n","/**\n * Live multi-line progress renderer for the bottom of the terminal.\n *\n * Maintains a \"live area\" listing in-flight tasks (Creating MyBucket...),\n * redrawn on a spinner timer. Other log output is routed through\n * {@link LiveRenderer.printAbove} so it appears above the live area without\n * disturbing the currently-displayed in-flight tasks.\n *\n * Design notes:\n * - Multiple resources can be in flight concurrently (cdkd uses parallel DAG\n * dispatch), so a single in-place line overwrite is not enough — each\n * in-flight resource is its own line in the live area.\n * - On non-TTY (CI/log-collection), the renderer stays inactive and\n * {@link LiveRenderer.printAbove} falls through to a direct write, so output\n * matches the previous append-only behavior.\n * - In verbose mode (debug level) the caller should not start the renderer:\n * debug logs would interleave too aggressively with the live area.\n */\n\nimport { getCurrentStackName } from '../provisioning/resource-name.js';\n\nconst SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\nconst FRAME_INTERVAL_MS = 80;\nconst ESC = '\\x1b[';\n\ninterface Task {\n label: string;\n startedAt: number;\n /** Stack the task belongs to (from `withStackName` AsyncLocalStorage),\n * or `undefined` when addTask was called outside any stack scope.\n * Captured at addTask time because the spinner re-draw runs on a\n * setInterval timer with no inherited async context. */\n stackName: string | undefined;\n}\n\n/**\n * Scope a task `id` to its calling stack so two stacks running in\n * parallel — `cdkd deploy --all` with `--stack-concurrency > 1` — don't\n * collide on the same `logicalId` in the renderer's task Map. Without\n * this, stack B's `addTask('MyQueue', ...)` would overwrite stack A's\n * entry, and stack A's later `removeTask('MyQueue')` would erase\n * stack B's.\n */\nfunction scopedKey(id: string, stackName: string | undefined): string {\n return stackName ? `${stackName}:${id}` : id;\n}\n\nexport class LiveRenderer {\n private tasks = new Map<string, Task>();\n private active = false;\n private spinnerIndex = 0;\n private interval: NodeJS.Timeout | null = null;\n private linesDrawn = 0;\n private cursorHidden = false;\n private exitListener: (() => void) | null = null;\n private readonly stream: NodeJS.WriteStream;\n\n constructor(stream: NodeJS.WriteStream = process.stdout) {\n this.stream = stream;\n }\n\n isActive(): boolean {\n return this.active;\n }\n\n /**\n * Enable the live renderer. No-op if stdout is not a TTY or if\n * `CDKD_NO_LIVE=1`. Returns true if successfully enabled.\n */\n start(): boolean {\n if (this.active) return true;\n if (!this.stream.isTTY) return false;\n if (process.env['CDKD_NO_LIVE'] === '1') return false;\n\n this.active = true;\n this.hideCursor();\n // Restore the cursor on abrupt process exit (e.g., uncaught exception\n // before stop() runs). Removed in stop() to avoid leaking listeners\n // across renderer instances.\n if (!this.exitListener) {\n this.exitListener = () => this.showCursor();\n process.on('exit', this.exitListener);\n }\n this.interval = setInterval(() => this.draw(), FRAME_INTERVAL_MS);\n if (typeof this.interval.unref === 'function') this.interval.unref();\n return true;\n }\n\n stop(): void {\n if (!this.active) return;\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = null;\n }\n this.clear();\n this.showCursor();\n if (this.exitListener) {\n process.removeListener('exit', this.exitListener);\n this.exitListener = null;\n }\n this.tasks.clear();\n this.active = false;\n }\n\n addTask(id: string, label: string): void {\n const stackName = getCurrentStackName();\n this.tasks.set(scopedKey(id, stackName), { label, startedAt: Date.now(), stackName });\n if (this.active) this.draw();\n }\n\n removeTask(id: string): void {\n const stackName = getCurrentStackName();\n if (!this.tasks.delete(scopedKey(id, stackName))) return;\n if (this.active) this.draw();\n }\n\n /**\n * Replace the label of a previously-added task in place. No-op if the\n * task is not currently tracked (e.g. it already finished). Used by the\n * per-resource deadline wrapper to surface a \"[taking longer than\n * expected, Nm+]\" suffix without disturbing the elapsed-time counter\n * the renderer tracks via `startedAt`.\n */\n updateTaskLabel(id: string, label: string): void {\n const stackName = getCurrentStackName();\n const task = this.tasks.get(scopedKey(id, stackName));\n if (!task) return;\n task.label = label;\n if (this.active) this.draw();\n }\n\n /**\n * Print content above the live area. Clears the live area, runs the writer,\n * then redraws the live area. When the renderer is inactive, the writer\n * runs directly so callers can use this unconditionally.\n */\n printAbove(write: () => void): void {\n if (!this.active) {\n write();\n return;\n }\n this.clear();\n write();\n this.draw();\n }\n\n private clear(): void {\n if (this.linesDrawn === 0) return;\n this.stream.write('\\r');\n for (let i = 0; i < this.linesDrawn; i++) {\n this.stream.write(`${ESC}1A${ESC}2K`);\n }\n this.linesDrawn = 0;\n }\n\n private draw(): void {\n if (!this.active) return;\n this.clear();\n if (this.tasks.size === 0) return;\n\n const frame = SPINNER_FRAMES[this.spinnerIndex % SPINNER_FRAMES.length]!;\n this.spinnerIndex++;\n\n // When tasks from more than one stack are in flight at the same time,\n // prefix each line with the stack name so the user can tell them\n // apart. In single-stack runs, omit the prefix to keep the area\n // visually clean. (Detection is per-frame: as soon as a second\n // stack adds its first task, the prefix appears on every row;\n // when the second stack's tasks all complete and only one stack\n // remains, the prefix disappears.)\n const distinctStacks = new Set<string | undefined>();\n for (const task of this.tasks.values()) distinctStacks.add(task.stackName);\n const showStackPrefix = distinctStacks.size > 1;\n\n // Truncate to terminal width so a long label does not wrap and confuse\n // the line-up clear logic. Default to 80 if columns is unavailable.\n const cols = this.stream.columns ?? 80;\n const lines: string[] = [];\n for (const task of this.tasks.values()) {\n const elapsed = ((Date.now() - task.startedAt) / 1000).toFixed(1);\n const prefix = showStackPrefix && task.stackName ? `[${task.stackName}] ` : '';\n const raw = ` ${frame} ${prefix}${task.label} (${elapsed}s)`;\n lines.push(this.truncate(raw, cols));\n }\n this.stream.write(lines.join('\\n') + '\\n');\n this.linesDrawn = lines.length;\n }\n\n private truncate(s: string, maxLen: number): string {\n if (s.length <= maxLen) return s;\n if (maxLen <= 1) return '…';\n return s.substring(0, maxLen - 1) + '…';\n }\n\n private hideCursor(): void {\n if (this.cursorHidden) return;\n this.stream.write(`${ESC}?25l`);\n this.cursorHidden = true;\n }\n\n private showCursor(): void {\n if (!this.cursorHidden) return;\n this.stream.write(`${ESC}?25h`);\n this.cursorHidden = false;\n }\n}\n\nlet globalRenderer: LiveRenderer | null = null;\n\nexport function getLiveRenderer(): LiveRenderer {\n if (!globalRenderer) globalRenderer = new LiveRenderer();\n return globalRenderer;\n}\n\n/**\n * Reset the singleton (for tests).\n */\nexport function resetLiveRenderer(): void {\n if (globalRenderer) globalRenderer.stop();\n globalRenderer = null;\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\n/**\n * Per-stack log buffer for parallel multi-stack deploys.\n *\n * Without buffering, two concurrent `deploy()` calls interleave their\n * `logger.info(...)` lines: stack A's \"Changes: 4 to create\" appears in\n * the middle of stack B's `[N/N] ✅ ...` progress, and stack B's\n * \"Deployment completed\" lands after stack A's late progress lines. With\n * buffering, each stack's log output is captured into its own buffer\n * for the duration of the deploy and flushed atomically when the deploy\n * finishes — so the user sees one clean per-stack block.\n *\n * Single-stack deploys do NOT enable buffering (the caller checks\n * `stacks.length > 1`); real-time output is preferred when there is no\n * interleaving risk.\n */\nexport interface StackOutputBuffer {\n lines: string[];\n}\n\nconst outputBufferStore = new AsyncLocalStorage<StackOutputBuffer>();\n\n/**\n * Run `fn` with a fresh log buffer scoped to its async chain. Any\n * `logger.info / debug / warn / error` calls inside `fn` (and any\n * `await`s) push into the buffer instead of writing to stdout/stderr.\n * Returns the buffered lines (and either `result` or `error`) so the\n * caller can flush them in one block.\n */\nexport async function runStackBuffered<T>(\n fn: () => Promise<T>\n): Promise<{ lines: string[] } & ({ ok: true; result: T } | { ok: false; error: unknown })> {\n const buffer: StackOutputBuffer = { lines: [] };\n return outputBufferStore.run(buffer, async () => {\n try {\n const result = await fn();\n return { ok: true, result, lines: buffer.lines };\n } catch (error) {\n return { ok: false, error, lines: buffer.lines };\n }\n });\n}\n\n/**\n * Get the current async context's stack output buffer, or `undefined`\n * if no `runStackBuffered` is active. The logger consults this on every\n * call: present → push to buffer; absent → fall through to live\n * renderer / console.\n */\nexport function getCurrentStackOutputBuffer(): StackOutputBuffer | undefined {\n return outputBufferStore.getStore();\n}\n","import type { Logger, LogLevel } from '../types/config.js';\nimport { displaySafe } from './display-safe.js';\nimport { getLiveRenderer } from './live-renderer.js';\nimport { getCurrentStackOutputBuffer } from './stack-context.js';\n\n/**\n * ANSI color codes\n *\n * Kept internal — `ConsoleLogger.formatMessage` references these for the\n * verbose/compact mode level prefixes. For inline color wrapping in\n * production code, import from `./colors.js` instead (which lives in a\n * separate module so unit tests that mock `logger.ts` don't strip color\n * helpers as a side effect).\n */\nconst colors = {\n reset: '\\x1b[0m',\n bright: '\\x1b[1m',\n dim: '\\x1b[2m',\n red: '\\x1b[31m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n blue: '\\x1b[34m',\n cyan: '\\x1b[36m',\n gray: '\\x1b[90m',\n} as const;\n\n/**\n * Whether stdout has been claimed by a machine-readable payload.\n *\n * Issue [#2230](https://github.com/go-to-k/cdkd/issues/2230). `info` and\n * `debug` resolve to `console.info` / `console.debug`, which write to\n * STDOUT — the same stream a `--json` command prints its payload on. A\n * command whose remediation path logs a status line therefore hands a pipe\n * consumer prose wrapped around the document, and the failure is silent in\n * the worst direction: the human sees correct-looking output while the\n * parser sees a syntax error, or succeeds against a truncated read.\n *\n * MODULE-LEVEL rather than an instance field on purpose. The decision is a\n * property of the PROCESS's stdout, not of one logger object: `child()`\n * hands out fresh {@link ChildLogger} instances (`role-arn`, the state\n * backend, ...) that would each need the flag set individually, and those\n * are exactly the loggers a command cannot reach from its own file.\n *\n * OPT-IN, and deliberately not derived from a `--json` flag inside the\n * logger: nothing changes for a command that does not call\n * {@link reserveStdoutForPayload}. Every `--json` surface keeps its payload\n * stream clean one way or the other, but NOT all by this mechanism, and the\n * count is easy to state one short: SEVEN call this — `cdkd drift`'s original\n * call, plus the six issue\n * [#2280](https://github.com/go-to-k/cdkd/issues/2280) added to it (`cdkd\n * list`, `cdkd events`, and the four `cdkd state {list,resources,show,info}`\n * subcommands) — while `cdkd diff` predates the mechanism and demotes the\n * logger to `warn` instead, which SUPPRESSES its info-level lines rather than\n * moving them. Do not read \"every `--json` surface\" as \"every `--json`\n * surface calls this\". Of those seven, TWO no longer consult `--json` at all\n * — see the next paragraph, which owns them.\n *\n * A `--json` flag is NOT what makes a stream a payload stream, though, and\n * issue [#2410](https://github.com/go-to-k/cdkd/issues/2410) plus issue\n * [#2435](https://github.com/go-to-k/cdkd/issues/2435) are the FIVE commands\n * where it is not involved at all: `cdkd synth` (the template), `cdkd list`\n * in EVERY mode (the YAML and the one-id-per-line spellings, not just\n * `--json`), `cdkd state list` in EVERY mode (the one-`Stack (region)`-\n * per-line spelling as much as `--json`; `--long` / `--tree` are formatted\n * views swept along, since the reservation is taken before the mode is\n * known), `cdkd local invoke` (the function response) and `cdkd local\n * invoke-agentcore` (the agent response). NOTE `cdkd list` AND `cdkd state\n * list` each appear in BOTH paragraphs and are ONE call site apiece, not two:\n * go-to-k/cdkd#2280 added both under `--json`, and go-to-k/cdkd#2410 /\n * go-to-k/cdkd#2435 respectively made those same calls unconditional. Ten\n * commands call this in total, not twelve. Those five call it\n * UNCONDITIONALLY at command entry, so for them the DEFAULT human output\n * contract is the one that moved — deliberately, and documented per command\n * in `docs/cli-reference.md`. The OTHER three `cdkd state` subcommands keep\n * the `--json` gate on the discriminator that page states: their flagless\n * output is a formatted human VIEW with no record-set mode behind it, not a\n * line-oriented RECORD SET. What has NOT changed is that the decision stays\n * with the command: a command whose stdout is a human surface (`cdkd deploy`,\n * `cdkd local start-api`, `cdkd local run-task`) still never calls this.\n */\nlet stdoutReservedForPayload = false;\n\n/**\n * Claim stdout for a machine-readable payload: `info` / `debug` lines route\n * to stderr for the rest of the process.\n *\n * The lines are MOVED, not dropped. An operator watching a terminal still\n * sees every status line; only the byte stream a pipe reads changes.\n */\nexport function reserveStdoutForPayload(): void {\n stdoutReservedForPayload = true;\n}\n\n/**\n * Hand stdout back. Exists for tests — the CLI runs one command per process\n * and never releases the reservation.\n */\nexport function releaseStdoutForPayload(): void {\n stdoutReservedForPayload = false;\n}\n\n/**\n * Whether {@link reserveStdoutForPayload} is in effect.\n *\n * Production callers exist, and the carve-out is narrow enough to state: a\n * writer that does NOT go through {@link ConsoleLogger} and therefore cannot\n * be routed by {@link ConsoleLogger.emit}. Today both live in\n * `src/utils/docker-cmd.ts` — `spawnStreaming`, which mirrors a child's\n * stdout live under `--verbose`, and `spawnForeground`, which hands the\n * child our fd 1 outright. Issue\n * [#2410](https://github.com/go-to-k/cdkd/issues/2410) routed both to stderr\n * while a command holds the reservation.\n *\n * The class is NOT closed, and saying so is the point: `streamLogs` in\n * `src/local/docker-runner.ts` is a third member — it pipes the CONTAINER's\n * stdout into ours on `cdkd local invoke` — and is deliberately NOT\n * converted, because unlike docker's own diagnostics a container's stdout\n * may BE what the user wants on that stream. That needs a per-caller\n * contract decision and a real-Docker integ round, tracked as\n * [#2419](https://github.com/go-to-k/cdkd/issues/2419).\n *\n * That is the ONLY shape this predicate is for. A COMMAND must never branch\n * on it — a command's own prose already flows through the logger, so\n * consulting this would be a second copy of a decision `emit` already makes,\n * and the two would drift. If you are reaching for it from a `commands/`\n * file, the answer is `reserveStdoutForPayload()` instead.\n *\n * Also used by tests, alongside {@link releaseStdoutForPayload}.\n */\nexport function isStdoutReservedForPayload(): boolean {\n return stdoutReservedForPayload;\n}\n\n/**\n * Format timestamp\n */\nfunction formatTimestamp(): string {\n const now = new Date();\n return now.toISOString();\n}\n\n/**\n * Console logger implementation\n *\n * Supports two output modes:\n * - verbose (debug level): timestamps, module prefixes, all details\n * - compact (info level): clean output without timestamps or prefixes\n */\nexport class ConsoleLogger implements Logger {\n private level: LogLevel;\n private useColors: boolean;\n\n constructor(level: LogLevel = 'info', useColors: boolean = true) {\n this.level = level;\n this.useColors = useColors;\n }\n\n private shouldLog(level: LogLevel): boolean {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const currentLevelIndex = levels.indexOf(this.level);\n const messageLevelIndex = levels.indexOf(level);\n return messageLevelIndex >= currentLevelIndex;\n }\n\n private formatMessage(level: LogLevel, message: string, ...args: unknown[]): string {\n // Sanitized because `JSON.stringify` is NOT a terminal-safety boundary.\n // It escapes `\\n` and the rest of C0, so an extra arg cannot forge a LINE\n // -- but `U+0085` (NEL), the C1 range xterm reads as CSI, and\n // `U+2028`/`U+2029` all survive it verbatim (measured on Node 24). Every\n // caller passing a parsed record as an extra arg therefore put raw\n // attacker bytes on the terminal: `getLockRecord` logs the whole\n // `lock.json`, of which only `owner` and `operation` are sanitized, so any\n // other key carried them through. Fixing it HERE rather than at that call\n // site is the same argument `display-safe.ts` makes in its own header --\n // widening the rule by hand, one reader at a time, is what missed four of\n // five readers in issue #2170 (issue #3003).\n const formattedArgs =\n args.length > 0 ? ' ' + displaySafe(args.map((a) => JSON.stringify(a)).join(' ')) : '';\n\n // Verbose mode: full timestamps and level\n if (this.level === 'debug') {\n const timestamp = formatTimestamp();\n const levelStr = level.toUpperCase().padEnd(5);\n\n if (this.useColors) {\n const levelColor = {\n debug: colors.gray,\n info: colors.blue,\n warn: colors.yellow,\n error: colors.red,\n }[level];\n\n return `${colors.dim}${timestamp}${colors.reset} ${levelColor}${levelStr}${colors.reset} ${message}${formattedArgs}`;\n }\n\n return `${timestamp} ${levelStr} ${message}${formattedArgs}`;\n }\n\n // Compact mode: clean output\n if (this.useColors) {\n if (level === 'error') {\n return `${colors.red}${message}${formattedArgs}${colors.reset}`;\n }\n if (level === 'warn') {\n return `${colors.yellow}${message}${formattedArgs}${colors.reset}`;\n }\n return `${message}${formattedArgs}`;\n }\n\n return `${message}${formattedArgs}`;\n }\n\n /**\n * Route a formatted log line. When a per-stack output buffer is active in\n * the current async context (parallel multi-stack deploy), capture the\n * line into the buffer so it can be flushed as one atomic block when the\n * stack finishes. Otherwise fall through to the live renderer / console\n * as before.\n *\n * `warn` / `error` already go to stderr via `console.warn` / `console.error`.\n * `info` / `debug` go to stdout — and JOIN them on stderr while\n * {@link stdoutReservedForPayload} is set, so the reserving command's payload\n * is the only thing on stdout.\n *\n * KNOWN GAP, latent and deliberately not restructured (issues\n * [#2230](https://github.com/go-to-k/cdkd/issues/2230) and\n * [#2410](https://github.com/go-to-k/cdkd/issues/2410)): the stack-output\n * buffer short-circuits ABOVE the reservation check, so a line captured under\n * `runStackBuffered` is replayed by whatever flushes the buffer and never\n * consults the reservation at all. Unreachable today — `deploy.ts`'s\n * `runStackBuffered` call is the only thing that opens a buffer, and `deploy`\n * reserves nothing (its stdout is a human surface). A bare line number here\n * would go stale on the next edit to that file with nothing noticing, so it\n * is named by symbol instead. Because the path is unreachable, a fix would\n * be untestable and would have to guess where the flush should route.\n *\n * That rationale survives #2410 unchanged, and it is worth restating WHY,\n * because #2410 widened the reserving population from a `--json`-only set to\n * one that includes commands with no flag at all. The four it added —\n * `cdkd synth`, `cdkd list`, `cdkd local invoke`,\n * `cdkd local invoke-agentcore` — and the fifth issue\n * [#2435](https://github.com/go-to-k/cdkd/issues/2435) then added,\n * `cdkd state list`, run NO work inside `runStackBuffered`:\n * buffering exists for the parallel multi-stack deploy path, which none of\n * them enters. So the gap stays latent, and the condition for it becoming\n * REAL is unchanged in substance but wider in reach: it is no longer \"a\n * `--json` command runs work inside `runStackBuffered`\" but \"ANY reserving\n * command does\". Whoever does that must route the flush, not just call\n * {@link reserveStdoutForPayload}.\n */\n private emit(level: LogLevel, formatted: string): void {\n const buffer = getCurrentStackOutputBuffer();\n if (buffer) {\n buffer.lines.push(formatted);\n return;\n }\n getLiveRenderer().printAbove(() => {\n if (level === 'error') console.error(formatted);\n else if (level === 'warn') console.warn(formatted);\n else if (stdoutReservedForPayload) console.error(formatted);\n else if (level === 'info') console.info(formatted);\n else console.debug(formatted);\n });\n }\n\n debug(message: string, ...args: unknown[]): void {\n if (this.shouldLog('debug')) {\n this.emit('debug', this.formatMessage('debug', message, ...args));\n }\n }\n\n info(message: string, ...args: unknown[]): void {\n if (this.shouldLog('info')) {\n this.emit('info', this.formatMessage('info', message, ...args));\n }\n }\n\n warn(message: string, ...args: unknown[]): void {\n if (this.shouldLog('warn')) {\n this.emit('warn', this.formatMessage('warn', message, ...args));\n }\n }\n\n error(message: string, ...args: unknown[]): void {\n if (this.shouldLog('error')) {\n this.emit('error', this.formatMessage('error', message, ...args));\n }\n }\n\n /**\n * Set log level\n */\n setLevel(level: LogLevel): void {\n this.level = level;\n }\n\n getLevel(): LogLevel {\n return this.level;\n }\n\n /**\n * Create a child logger with a prefix\n *\n * In verbose mode, prefix is shown as [Prefix]. In compact mode, prefix is hidden.\n */\n child(prefix: string): ChildLogger {\n return new ChildLogger(prefix, this.useColors);\n }\n}\n\n/**\n * Child logger that always syncs level from global logger\n */\nclass ChildLogger extends ConsoleLogger {\n private readonly prefix: string;\n\n constructor(prefix: string, useColors: boolean) {\n super('info', useColors);\n this.prefix = prefix;\n }\n\n private syncLevel(): void {\n if (globalLogger) {\n this.setLevel(globalLogger.getLevel());\n }\n }\n\n override debug(message: string, ...args: unknown[]): void {\n this.syncLevel();\n super.debug(`[${this.prefix}] ${message}`, ...args);\n }\n\n override info(message: string, ...args: unknown[]): void {\n this.syncLevel();\n const msg = this.getLevel() === 'debug' ? `[${this.prefix}] ${message}` : message;\n super.info(msg, ...args);\n }\n\n override warn(message: string, ...args: unknown[]): void {\n this.syncLevel();\n const msg = this.getLevel() === 'debug' ? `[${this.prefix}] ${message}` : message;\n super.warn(msg, ...args);\n }\n\n override error(message: string, ...args: unknown[]): void {\n this.syncLevel();\n const msg = this.getLevel() === 'debug' ? `[${this.prefix}] ${message}` : message;\n super.error(msg, ...args);\n }\n}\n\n/**\n * Global logger instance\n */\nlet globalLogger: ConsoleLogger | null = null;\n\n/**\n * Get or create global logger\n */\nexport function getLogger(): ConsoleLogger {\n if (!globalLogger) {\n globalLogger = new ConsoleLogger();\n }\n return globalLogger;\n}\n\n/**\n * Set global logger instance\n */\nexport function setLogger(logger: ConsoleLogger): void {\n globalLogger = logger;\n}\n"],"mappings":"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"}
|