@go-to-k/cdkd 0.291.7 → 0.291.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { C as displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, S as UNRENDERABLE, T as displaySafe, 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 displayIdent, x as STACK_REF_MAX_CODE_POINTS, y as withoutGeneratedFallbackName } from "./logger-GASr9bAF.js";
2
2
  import { a as setAssumedRoleCredentials, c as currentStackAwsScope, i as getPreAssumeEnvCredentials, l as runInStackAwsScope, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, s as ambientRegion, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-jISU4PHo.js";
3
- import { t as getCdkdVersion } from "./version-oHfbqdBD.js";
3
+ import { t as getCdkdVersion } from "./version-RzKz4JAW.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";
@@ -1230,6 +1230,182 @@ function isUpdateUnsupportedError(error, logicalId) {
1230
1230
  return false;
1231
1231
  }
1232
1232
 
1233
+ //#endregion
1234
+ //#region src/utils/pasteable-command.ts
1235
+ /**
1236
+ * The way cdkd builds a `cdkd ...` command it tells an operator to PASTE.
1237
+ *
1238
+ * Thirteen message modules route through it today. The CATEGORIES it does not
1239
+ * cover yet are at the end of this header — categories, not a census: the
1240
+ * population is what go-to-k/cdkd#3436's greps return against the tree of the
1241
+ * day, and a list here would be read as complete and go stale.
1242
+ *
1243
+ * go-to-k/cdkd#3363 measured the class this module closes, and
1244
+ * [#3436](https://github.com/go-to-k/cdkd/issues/3436) records it repo-wide.
1245
+ * Three shapes, each of which a per-site discipline kept re-introducing:
1246
+ *
1247
+ * 1. **A command inside prose quotes.** `drop it whole with '<command>'` puts
1248
+ * the command in a `'...'` span, so an interpolated value carrying `'`
1249
+ * CLOSES that wrapper when the span is pasted WITH its quotes and the rest
1250
+ * runs as shell: `'cdkd state orphan S --state-bucket 'b; printf X; #''`
1251
+ * printed `X`. Quoting the value does not help — the wrapper is what
1252
+ * inverts. So a command is printed UNWRAPPED on its own labelled line, and
1253
+ * this module returns the text for exactly that.
1254
+ *
1255
+ * **UNWRAPPED always; LAST only when the message prints ONE command.** A
1256
+ * message offering a READ and a DESTRUCTIVE template prints one command per
1257
+ * LINE, and there the read ends a line while the TEMPLATE is last — the
1258
+ * rule `.claude/rules/state-malformed-containers.md` states for
1259
+ * go-to-k/cdkd#3516, and a shape this module already serves twice:
1260
+ * `deploy-engine.ts`'s `Inspect it with:` / `Drop the record with:` pair,
1261
+ * and `destroy-runner.ts`'s through `hintFor`. In both, the read command is
1262
+ * correctly NOT last. Collapsing such a pair onto one line is the defect
1263
+ * that rule names: the read must end a line, and the template must be last
1264
+ * with no substituted value after it.
1265
+ * 2. **A bare `<placeholder>`.** `<name>` is two redirections, not a word:
1266
+ * `<name` reads stdin from a file and `>` truncates whatever word follows.
1267
+ * {@link commandHole} quotes it, so it pastes as one literal argument.
1268
+ * 3. **A value whose quote context the PROSE already flipped.** An English
1269
+ * apostrophe earlier in the sentence (`this stack's name`) opens a shell
1270
+ * quote that closes at the value's own opening quote, leaving the value
1271
+ * bare. This module cannot reach that one — a value in prose is not a
1272
+ * command — which is why the rule it belongs to is "a shell-quoted value
1273
+ * goes on a labelled trailing line, never inside a sentence", and why
1274
+ * deleting apostrophes is NOT the remedy (the next sentence re-opens it).
1275
+ *
1276
+ * What this module owns is the ARGUMENT side of shape 1 and shape 2: every
1277
+ * user-controlled value is sanitized, gated on rendering EXACTLY, and either
1278
+ * shell-quoted or replaced by a quoted hole. A value sanitizing would ALTER is
1279
+ * never printed in its altered spelling — that addresses a DIFFERENT record
1280
+ * than the message means — and never silently dropped, which would resolve the
1281
+ * ambient default instead.
1282
+ *
1283
+ * {@link shellQuote} and {@link commandHole} live here rather than in
1284
+ * `../state/lock-contention-message.js`, which still re-exports them for its
1285
+ * own callers: `src/utils/**` imports nothing from `src/state/**`, so the
1286
+ * shared helper could not reach them the other way round.
1287
+ *
1288
+ * ## The CATEGORIES not covered yet, as of go-to-k/cdkd#3436's first half
1289
+ *
1290
+ * Re-derive the members with that issue's greps rather than trusting a list:
1291
+ * these are kinds of site, and each kind has more members than the examples.
1292
+ *
1293
+ * - **Its own copy of the gate.** `buildForceUnlockCommand`
1294
+ * (`state/lock-contention-message.ts`), the `cdkd orphan` properties refusal
1295
+ * (`state/malformed-resources-bag.ts`), `orphanCommandFor` (`cli/commands/
1296
+ * export.ts`), and others in `deployment/deploy-engine.ts`,
1297
+ * `deployment/rollback-executor.ts` and `cli/commands/gc.ts`. They behave the
1298
+ * same way; they are not this function, so a rule change reaches them only by
1299
+ * hand.
1300
+ * - **A command in prose quotes with a RAW value**, outside the modules
1301
+ * migrated here — `provisioning/providers/**` (S3 Tables, Route 53, DynamoDB),
1302
+ * `cli/config-loader.ts` and `cli/commands/orphan.ts` are where the greps land
1303
+ * today.
1304
+ * - **The `cdkd drift` sites of
1305
+ * [#3307](https://github.com/go-to-k/cdkd/issues/3307)**, which take their own
1306
+ * gate in that PR. Note a `cdkd drift` command also appears OUTSIDE that file.
1307
+ * - **A spelled-out template or usage synopsis printing a BARE `<hole>`**, and
1308
+ * the source fence that would find them.
1309
+ */
1310
+ /**
1311
+ * Quote a value for a pasteable shell command.
1312
+ *
1313
+ * EXPORTED since issue [#2610]: `src/provisioning/replacement-protection-advice.ts`
1314
+ * prints `aws <service> ...` recovery commands naming a resource's physical id,
1315
+ * which is the same hazard one directory over. A second spelling of this
1316
+ * predicate is how the two would come to disagree about which values need
1317
+ * quoting -- the reason `display-safe.ts`'s header gives for not widening a
1318
+ * rule by hand. It is a pure function of its argument and imports nothing.
1319
+ */
1320
+ function shellQuote(value) {
1321
+ return /^[A-Za-z0-9._/@:+-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
1322
+ }
1323
+ /**
1324
+ * A placeholder for a value a pasteable command could not name, QUOTED.
1325
+ *
1326
+ * A bare `<profile>` is two shell redirections, not a word: pasted, `<profile`
1327
+ * reads stdin from a file named `profile` and `>` sends stdout to whatever
1328
+ * word follows. While the hole was the command's LAST word that was only a
1329
+ * syntax error; with flags appended after it (`--profile <profile>
1330
+ * --state-bucket b`) it ran the command with `--state-bucket` swallowed as a
1331
+ * redirect target and `b` as the profile — a delete against the ambient
1332
+ * bucket, with nothing printed (measured by the maintainer, M4 of the
1333
+ * go-to-k/cdkd#3363 review). Quoted, it pastes as one literal argument and
1334
+ * nothing else — no redirection, no swallowed flag. It is not refused: an
1335
+ * UNFILLED hole runs as that literal value (a stack named `<stack>`, a prefix
1336
+ * `<prefix>`).
1337
+ */
1338
+ function commandHole(name) {
1339
+ return `'<${name}>'`;
1340
+ }
1341
+ /**
1342
+ * WHY a value must become a hole, or `undefined` when it may be NAMED.
1343
+ *
1344
+ * FIRST MATCH WINS, in the order written: `empty`, `altered`, `too-long`,
1345
+ * `option-shaped`, `pattern-shaped` (m21 of the go-to-k/cdkd#3499 review). A
1346
+ * value can satisfy several — `-\u001b[x` is both option-shaped and altered —
1347
+ * and the caller renders ONE sentence, so the order decides which. It runs
1348
+ * cheapest-and-most-fundamental first: a value that does not survive rendering
1349
+ * cannot be reasoned about as a command argument at all, so saying "this would
1350
+ * be read as an option" about a spelling that is not what is stored would be
1351
+ * the more misleading of the two true sentences. The order is also the order
1352
+ * the two functions this replaced already applied, which is what keeps the
1353
+ * refactor behaviour-preserving.
1354
+ */
1355
+ function withholdReason(value, opts) {
1356
+ if (value === "") return "empty";
1357
+ const safe = displaySafe(value, { asciiOnly: true });
1358
+ if (safe !== value) return "altered";
1359
+ if (truncateCodePoints(safe, 1152).truncated) return "too-long";
1360
+ if (value.startsWith("-")) return "option-shaped";
1361
+ if (opts?.patternMatched === true && (value.includes("*") || value.includes("/"))) return "pattern-shaped";
1362
+ }
1363
+ /**
1364
+ * Build a pasteable `cdkd` command: every user-controlled value shell-quoted
1365
+ * behind an exactness gate, every placeholder quoted, nothing wrapped.
1366
+ *
1367
+ * `extraFlags` is where a caller appends the flags that pin the command to its
1368
+ * own account and key space — in practice `recoveryCommandFlags(recovery)`,
1369
+ * whose `exact` the caller folds into its own decision. They are appended
1370
+ * verbatim, LAST, because they are built by the same rules one layer up.
1371
+ */
1372
+ function pasteableCommand(verb, args = [], extraFlags = []) {
1373
+ let exact = true;
1374
+ const withheld = [];
1375
+ const parts = [verb];
1376
+ for (const arg of args) {
1377
+ if ("literal" in arg) {
1378
+ parts.push(arg.literal);
1379
+ continue;
1380
+ }
1381
+ if (!("value" in arg)) {
1382
+ if ("flag" in arg) parts.push(arg.flag);
1383
+ parts.push(commandHole(arg.hole));
1384
+ exact = false;
1385
+ continue;
1386
+ }
1387
+ const reason = withholdReason(arg.value, arg.opts);
1388
+ let rendered;
1389
+ if (reason === void 0) rendered = shellQuote(arg.value);
1390
+ else {
1391
+ rendered = commandHole(arg.hole);
1392
+ exact = false;
1393
+ withheld.push({
1394
+ hole: arg.hole,
1395
+ reason
1396
+ });
1397
+ }
1398
+ if ("flag" in arg) parts.push(arg.flag);
1399
+ parts.push(rendered);
1400
+ }
1401
+ parts.push(...extraFlags);
1402
+ return {
1403
+ command: parts.join(" "),
1404
+ exact,
1405
+ withheld
1406
+ };
1407
+ }
1408
+
1233
1409
  //#endregion
1234
1410
  //#region src/utils/error-handler.ts
1235
1411
  /**
@@ -1736,14 +1912,18 @@ var ResourceUpdateNotSupportedError = class ResourceUpdateNotSupportedError exte
1736
1912
  * redeploy, then retry the destroy. A future `--remove-protection`
1737
1913
  * flag (separate scope) will provide an explicit one-shot bypass.
1738
1914
  *
1739
- * Note: `cdkd state destroy` (state-only, no synth) does NOT honor
1915
+ * Note: `cdkd state destroy` (the synth-free destroy) does NOT honor
1740
1916
  * `terminationProtection` — the flag is a CDK property not persisted
1741
1917
  * in cdkd's state.json. Use `cdkd destroy` when synth is available.
1742
1918
  */
1743
1919
  var StackTerminationProtectionError = class StackTerminationProtectionError extends CdkdError {
1744
1920
  stackName;
1745
1921
  constructor(stackName, cause) {
1746
- super(`Stack '${stackName}' has terminationProtection: true and cannot be destroyed. Set terminationProtection: false in the CDK code, redeploy, then retry 'cdkd destroy ${stackName}'.`, "STACK_TERMINATION_PROTECTION", cause);
1922
+ super(`Stack '${stackName}' has terminationProtection: true and cannot be destroyed. Set terminationProtection: false in the CDK code, redeploy, then retry the destroy.\nRetry with: ${pasteableCommand("cdkd destroy", [{
1923
+ value: stackName,
1924
+ hole: "stack",
1925
+ opts: { patternMatched: true }
1926
+ }]).command}`, "STACK_TERMINATION_PROTECTION", cause);
1747
1927
  this.stackName = stackName;
1748
1928
  this.name = "StackTerminationProtectionError";
1749
1929
  Object.setPrototypeOf(this, StackTerminationProtectionError.prototype);
@@ -1773,8 +1953,11 @@ var StackTerminationProtectionError = class StackTerminationProtectionError exte
1773
1953
  * 1. `cdkd destroy <parent>` — the normal cascading-destroy path; the
1774
1954
  * parent's reverse-DAG walks into the child via
1775
1955
  * `NestedStackProvider.delete` and removes both layers atomically.
1776
- * 2. `cdkd state destroy <child>` — state-only destroy with no parent
1777
- * coupling check. The state-driven entry point intentionally
1956
+ * 2. `cdkd state destroy <child>` — the SYNTH-FREE destroy, with no parent
1957
+ * coupling check. It deletes the child's AWS resources and then its
1958
+ * record; "state-only" named the wrong thing, and this message's own text
1959
+ * was corrected away from that reading (go-to-k/cdkd#3499 review nits).
1960
+ * `cdkd state orphan` is the record-only command. The state-driven entry point intentionally
1778
1961
  * bypasses this guard for the same reason `cdkd state destroy`
1779
1962
  * bypasses `terminationProtection`: it's the "I know what I'm
1780
1963
  * doing" path for cleaning up state when synth is unavailable or
@@ -1786,7 +1969,16 @@ var NestedStackChildDirectDestroyError = class NestedStackChildDirectDestroyErro
1786
1969
  parentLogicalId;
1787
1970
  constructor(stackName, parentStack, parentLogicalId, cause) {
1788
1971
  const logicalIdSuffix = parentLogicalId ? ` (parent's logical id: ${parentLogicalId})` : "";
1789
- super(`Stack '${stackName}' is a nested child of '${parentStack}'${logicalIdSuffix}; directly destroying a nested stack is not supported. Either run 'cdkd destroy ${parentStack}' to cascade-delete this child along with its parent, or run 'cdkd state destroy ${stackName}' if you intentionally want to leave the parent's reference dangling (the state-only escape hatch).`, "NESTED_STACK_CHILD_DIRECT_DESTROY", cause);
1972
+ const cascadeCommand = pasteableCommand("cdkd destroy", [{
1973
+ value: parentStack,
1974
+ hole: "parent",
1975
+ opts: { patternMatched: true }
1976
+ }]).command;
1977
+ const childDestroyCommand = pasteableCommand("cdkd state destroy", [{
1978
+ value: stackName,
1979
+ hole: "stack"
1980
+ }]).command;
1981
+ super(`Stack '${stackName}' is a nested child of '${parentStack}'${logicalIdSuffix}; directly destroying a nested stack is not supported. Either cascade-delete this child along with its parent, or destroy the child on its own — which deletes its AWS resources and its record, and leaves the parent's reference dangling (the synth-free escape hatch).\nCascade-delete with: ${cascadeCommand}\nDestroy the child alone with: ${childDestroyCommand}`, "NESTED_STACK_CHILD_DIRECT_DESTROY", cause);
1790
1982
  this.stackName = stackName;
1791
1983
  this.parentStack = parentStack;
1792
1984
  if (parentLogicalId !== void 0) this.parentLogicalId = parentLogicalId;
@@ -3165,6 +3357,86 @@ function assemblyPathEscape(base, bound, candidate) {
3165
3357
  return resolved;
3166
3358
  }
3167
3359
  /**
3360
+ * A letter or digit that neither draws as a blank nor reads as a quote.
3361
+ * `\p{L}` alone admits both, so two classes are carved out: the
3362
+ * default-ignorables, which hold letters that draw as a blank (the Hangul
3363
+ * fillers U+3164 and U+FFA0), and the quote-shaped letters — the Spacing
3364
+ * Modifier Letters block (U+02BA reads as `"`, U+02BC as `'`) plus the ones
3365
+ * outside it (U+0374, U+0559, U+07F4-U+07F5, U+A78B-U+A78C, and the halfwidth
3366
+ * sound marks U+FF9E-U+FF9F). Not all of `\p{Lm}`: U+30FC is in it, and it is
3367
+ * an ordinary character of a Japanese directory name.
3368
+ */
3369
+ const VISIBLE_LETTER = new RegExp(String.raw`^(?![\p{Default_Ignorable_Code_Point}\u02b0-\u02ff\u0374\u0559\u07f4\u07f5\ua78b\ua78c\uff9e\uff9f])[\p{L}\p{N}]$`, "u");
3370
+ /**
3371
+ * A combining mark, which counts only DIRECTLY after a visible letter (or a
3372
+ * mark that itself counted): on a space or on punctuation it draws on its own,
3373
+ * and U+030B / U+030E there look like a quote.
3374
+ */
3375
+ const COMBINING_MARK = /^\p{M}$/u;
3376
+ /**
3377
+ * The ASCII a bare path may carry besides letters and digits. Everything else —
3378
+ * any whitespace, any quote, any other symbol — takes the boundary, which
3379
+ * costs a legitimate path nothing but a pair of quotes.
3380
+ */
3381
+ const BARE_PUNCTUATION = /^[/\\._~+@:=-]$/;
3382
+ /**
3383
+ * Classify each code point: `bare` (allowed in a bare path), `shown` (shown as
3384
+ * itself inside the boundary), or `escaped`. An ALLOWLIST, because a denylist
3385
+ * has to enumerate every character that draws as a blank or reads as a quote,
3386
+ * and JS `\s` alone already misses U+2800 and U+3164.
3387
+ *
3388
+ * Each test is ONE character and this walks the value — never `^(...)+$` over
3389
+ * the whole of it: alternatives that overlap (ASCII letters are in both
3390
+ * classes) under a quantifier backtrack exponentially on a long path that
3391
+ * fails near its end, which hangs the process uncatchably.
3392
+ */
3393
+ function classify(chars) {
3394
+ let afterLetter = false;
3395
+ return chars.map((ch) => {
3396
+ if (COMBINING_MARK.test(ch)) return afterLetter ? "bare" : "escaped";
3397
+ afterLetter = VISIBLE_LETTER.test(ch);
3398
+ if (afterLetter || BARE_PUNCTUATION.test(ch)) return "bare";
3399
+ return ch >= " " && ch <= "~" ? "shown" : "escaped";
3400
+ });
3401
+ }
3402
+ /**
3403
+ * Render a filesystem path that an assembly chose, or that embeds a value it
3404
+ * chose, into cdkd's own prose (go-to-k/cdkd#3509). The caller writes NO
3405
+ * quotes around the result.
3406
+ *
3407
+ * `displaySafe` alone is not enough inside quotes of cdkd's: it is a denylist
3408
+ * of control characters and passes `'`, so a value carrying one closed cdkd's
3409
+ * quote and wrote a clause of its own into the refusal. This keeps a plain path
3410
+ * bare and puts any other one inside a JSON string literal. Inside it, `"` and
3411
+ * `\` are escaped as JSON escapes them, and so is every character that is
3412
+ * neither printable ASCII nor a visible letter — a curly or fullwidth quote
3413
+ * that could pass for the boundary's own closing `"`, and a blank that could
3414
+ * pass for a space. The result stays valid JSON: `JSON.parse` returns the
3415
+ * sanitized value.
3416
+ *
3417
+ * Deliberately NOT `displayIdent`, which go-to-k/cdkd#3506 used for a Stage
3418
+ * path: that one is ASCII-only and capped at 255 code points, and a legitimate
3419
+ * path is neither — a non-ASCII directory name would render as spaces, naming
3420
+ * a path that does not exist. Here nothing is truncated and a non-ASCII letter
3421
+ * is shown as itself. A legitimate path with a space or any symbol outside
3422
+ * `/ \ . _ ~ + @ : = -` (`/Users/me/My Project/cdk.out`,
3423
+ * `C:\Program Files (x86)\app`) renders quoted, and a non-letter non-ASCII
3424
+ * character in it (an emoji, `©`) is shown as its `\u` escape.
3425
+ */
3426
+ function displayAssemblyPath(value) {
3427
+ const clean = displaySafe(value);
3428
+ const chars = Array.from(clean);
3429
+ const kinds = classify(chars);
3430
+ if (clean === value && chars.length > 0 && kinds.every((k) => k === "bare")) return clean;
3431
+ let body = "";
3432
+ chars.forEach((ch, i) => {
3433
+ if (ch === "\"" || ch === "\\") body += `\\${ch}`;
3434
+ else if (kinds[i] !== "escaped") body += ch;
3435
+ else for (let u = 0; u < ch.length; u++) body += `\\u${ch.charCodeAt(u).toString(16).padStart(4, "0")}`;
3436
+ });
3437
+ return `"${body}"`;
3438
+ }
3439
+ /**
3168
3440
  * The shared tail of every containment refusal: what the value resolved to,
3169
3441
  * what it escaped, and why that means the assembly is not CDK-generated. Each
3170
3442
  * call site supplies its own subject ("Stack 'X' has templateFile='...' which
@@ -3173,23 +3445,25 @@ function assemblyPathEscape(base, bound, candidate) {
3173
3445
  * other than loading the file — `renderNestedTemplateTreeDefect` says "deploy"
3174
3446
  * or "diff", matching its own sibling refusals.
3175
3447
  *
3176
- * Every interpolation goes through `displaySafe` for the reason
3177
- * `AssemblyReader`'s own refusals give (go-to-k/cdkd#3277): this text exists
3178
- * FOR a hand-modified assembly, so the candidate, the resolved path and even
3179
- * `dir` (below a Stage it derives from the manifest's `directoryName`) are all
3180
- * attacker-chosen, and `formatError` sanitizes only an error's `cause`, never
3181
- * its own `message`.
3448
+ * Every path goes through {@link displayAssemblyPath}, with no quotes of this
3449
+ * function's own, for the reason `AssemblyReader`'s own refusals give
3450
+ * (go-to-k/cdkd#3277): this text exists FOR a hand-modified assembly, so the
3451
+ * candidate, the resolved path and even `dir` (below a Stage it derives from
3452
+ * the manifest's `directoryName`) are all attacker-chosen, and `formatError`
3453
+ * sanitizes only an error's `cause`, never its own `message`.
3182
3454
  */
3183
3455
  function renderAssemblyPathEscape(escape, dir, action = "load", provenanceOverride) {
3184
3456
  const provenance = provenanceOverride ?? `CDK emits assembly paths that stay inside the assembly directory; one that leaves it indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to ${action}.`;
3185
3457
  const base = path.resolve(dir);
3186
3458
  const realBase = resolveThroughLinks(base) ?? base;
3459
+ const shownPath = displayAssemblyPath(escape.path);
3460
+ const shownBase = displayAssemblyPath(base);
3187
3461
  if (escape.escape === "symlink") {
3188
- if (escape.realPath === realBase) return `resolves to '${displaySafe(escape.path)}', a symbolic link to the directory '${displaySafe(base)}' itself rather than to a file inside it. ${provenance}`;
3189
- return `resolves to '${displaySafe(escape.path)}', which leads through a symbolic link to '${displaySafe(escape.realPath)}', outside '${displaySafe(base)}'. ${provenance}`;
3462
+ if (escape.realPath === realBase) return `resolves to ${shownPath}, a symbolic link to the directory ${shownBase} itself rather than to a file inside it. ${provenance}`;
3463
+ return `resolves to ${shownPath}, which leads through a symbolic link to ${displayAssemblyPath(escape.realPath)}, outside ${shownBase}. ${provenance}`;
3190
3464
  }
3191
- if (escape.path === base) return `names the directory '${displaySafe(base)}' itself rather than a file inside it. ${provenance}`;
3192
- return `resolves to '${displaySafe(escape.path)}', outside '${displaySafe(base)}'. ${provenance}`;
3465
+ if (escape.path === base) return `names the directory ${shownBase} itself rather than a file inside it. ${provenance}`;
3466
+ return `resolves to ${shownPath}, outside ${shownBase}. ${provenance}`;
3193
3467
  }
3194
3468
 
3195
3469
  //#endregion
@@ -7817,12 +8091,25 @@ function formatDockerLoginError(stderr, endpoint) {
7817
8091
  return trimmed;
7818
8092
  }
7819
8093
  /**
7820
- * Env vars the docker CLI itself reads to decide how / where to run. A resolved
7821
- * ECS secret (or SecureString) whose NAME collides with one of these must NOT
7822
- * override it in the docker client's own process environment: a secret named
7823
- * `DOCKER_HOST` would redirect the client to a different daemon, and `PATH`
7824
- * would break locating the docker binary. See issue
7825
- * https://github.com/go-to-k/cdkd/issues/2183.
8094
+ * Env vars the container CLI itself reads to decide how / where to run — the
8095
+ * binary {@link getDockerCmd} resolves, i.e. `docker` or whatever `CDK_DOCKER`
8096
+ * names (podman / nerdctl / finch). A resolved ECS secret (or SecureString)
8097
+ * whose NAME collides with one of these must NOT override it in the client's
8098
+ * own process environment: a secret named `DOCKER_HOST` (or podman's
8099
+ * `CONTAINER_HOST`) would redirect the client to a different daemon, and `PATH`
8100
+ * would break locating the binary. See issues
8101
+ * https://github.com/go-to-k/cdkd/issues/2183 and
8102
+ * https://github.com/go-to-k/cdkd/issues/2188.
8103
+ *
8104
+ * RULE for additions: anything a supported container CLIENT (or a credential /
8105
+ * connection helper it execs) reads to decide WHAT CODE IT LOADS, WHAT IT
8106
+ * TRUSTS, or WHERE / HOW IT CONNECTS. The docker CLI's documented set is kept
8107
+ * whole, behaviour toggles included; beyond it, a var that only tunes
8108
+ * behaviour (a storage driver, a snapshotter, a temp dir, an experimental
8109
+ * toggle) is OUT, because dropping a colliding secret costs the user that
8110
+ * secret. The operator's OWN value is never touched — the spawn
8111
+ * env starts from `process.env` — so adding a key costs only a secret of that
8112
+ * name.
7826
8113
  */
7827
8114
  const DOCKER_CLIENT_ENV_KEYS = /* @__PURE__ */ new Set([
7828
8115
  "PATH",
@@ -7864,6 +8151,40 @@ const DOCKER_CLIENT_ENV_KEYS = /* @__PURE__ */ new Set([
7864
8151
  "AWS_CONTAINER_CREDENTIALS_FULL_URI",
7865
8152
  "AWS_ROLE_ARN",
7866
8153
  "AWS_EC2_METADATA_SERVICE_ENDPOINT",
8154
+ "AWS_ECR_CACHE_DIR",
8155
+ "CONTAINER_HOST",
8156
+ "CONTAINER_CONNECTION",
8157
+ "CONTAINER_SSHKEY",
8158
+ "PODMAN_CONNECTIONS_CONF",
8159
+ "CONTAINER_PROXY",
8160
+ "CONTAINERS_SSH_CONF",
8161
+ "CONTAINERS_CONF",
8162
+ "CONTAINERS_CONF_OVERRIDE",
8163
+ "CONTAINERS_REGISTRIES_CONF",
8164
+ "CONTAINERS_REGISTRIES_CONF_OVERRIDE",
8165
+ "REGISTRIES_CONFIG_PATH",
8166
+ "CONTAINERS_STORAGE_CONF",
8167
+ "CONTAINERS_STORAGE_CONF_OVERRIDE",
8168
+ "CONTAINERS_POLICY_JSON",
8169
+ "STORAGE_OPTS",
8170
+ "CONTAINERS_HELPER_BINARY_DIR",
8171
+ "REGISTRY_AUTH_FILE",
8172
+ "DBUS_SESSION_BUS_ADDRESS",
8173
+ "NOTIFY_SOCKET",
8174
+ "XDG_CONFIG_HOME",
8175
+ "XDG_RUNTIME_DIR",
8176
+ "APPDATA",
8177
+ "PROGRAMDATA",
8178
+ "PROGRAMFILES",
8179
+ "LOCALAPPDATA",
8180
+ "SSH",
8181
+ "CONTAINERD_ADDRESS",
8182
+ "CONTAINERD_NAMESPACE",
8183
+ "NERDCTL_TOML",
8184
+ "CNI_PATH",
8185
+ "NETCONFPATH",
8186
+ "ROOTLESSKIT_STATE_DIR",
8187
+ "NERDCTL_LOG_FILE",
7867
8188
  "SSL_CERT_FILE",
7868
8189
  "SSL_CERT_DIR",
7869
8190
  "GODEBUG",
@@ -9667,7 +9988,14 @@ function isCrossRegionRedirect(error) {
9667
9988
  */
9668
9989
  async function assertAssetBucketRegion(s3Client, bucketName, expectedRegion, accountId, cause) {
9669
9990
  const want = canonicalizeRegion(expectedRegion);
9670
- const remedy = `Either bootstrap ${want} with an asset-bucket name unique to it ('cdkd bootstrap --region ${want} --asset-bucket <name>'), or run this against the bucket's own region.`;
9991
+ const remedy = `Either bootstrap ${want} with an asset-bucket name unique to it, or run this against the bucket's own region.\nBootstrap with: ${pasteableCommand("cdkd bootstrap", [{
9992
+ flag: "--region",
9993
+ value: want,
9994
+ hole: "region"
9995
+ }, {
9996
+ flag: "--asset-bucket",
9997
+ hole: "name"
9998
+ }]).command}`;
9671
9999
  let actual;
9672
10000
  const fromHeader = readBucketRegionHeader(cause);
9673
10001
  if (fromHeader) actual = canonicalizeRegion(fromHeader);
@@ -9831,7 +10159,11 @@ function parseBootstrapMarker(body, markerKey) {
9831
10159
  * (bucket-squatting defense, design §5).
9832
10160
  */
9833
10161
  async function verifyAssetStorageExists(marker, accountId, region, opts = {}) {
9834
- const rebootstrapHint = `Run 'cdkd bootstrap --region ${region}' to recreate it. cdkd never silently falls back to CDK bootstrap asset storage once a region is opted in.`;
10162
+ const rebootstrapHint = `cdkd never silently falls back to CDK bootstrap asset storage once a region is opted in.\nRecreate it with: ${pasteableCommand("cdkd bootstrap", [{
10163
+ flag: "--region",
10164
+ value: region,
10165
+ hole: "region"
10166
+ }]).command}`;
9835
10167
  const clientOpts = {
9836
10168
  region,
9837
10169
  ...opts.profile && { profile: opts.profile }
@@ -9915,7 +10247,11 @@ async function ensureAssetStorage(options) {
9915
10247
  const conflicts = [];
9916
10248
  if (options.assetBucketName && options.assetBucketName !== existingMarker.assetBucket) conflicts.push(`asset bucket '${existingMarker.assetBucket}' (requested '${options.assetBucketName}')`);
9917
10249
  if (options.containerRepoName && options.containerRepoName !== existingMarker.containerRepo) conflicts.push(`container repo '${existingMarker.containerRepo}' (requested '${options.containerRepoName}')`);
9918
- if (conflicts.length > 0) throw new CdkdError(`Region '${region}' is already bootstrapped with ${conflicts.join(" and ")}. Changing asset storage names would strand the existing storage and every published asset in it — run 'cdkd bootstrap --destroy --region ${region}' to tear the region's asset storage down first, then re-run bootstrap with the new names.`, "ASSET_STORAGE_NAME_CONFLICT");
10250
+ if (conflicts.length > 0) throw new CdkdError(`Region '${region}' is already bootstrapped with ${conflicts.join(" and ")}. Changing asset storage names would strand the existing storage and every published asset in it — tear the region's asset storage down first, then re-run bootstrap with the new names.\nTear it down with: ${pasteableCommand("cdkd bootstrap", [{ literal: "--destroy" }, {
10251
+ flag: "--region",
10252
+ value: region,
10253
+ hole: "region"
10254
+ }]).command}`, "ASSET_STORAGE_NAME_CONFLICT");
9919
10255
  }
9920
10256
  const assetBucket = options.assetBucketName ?? existingMarker?.assetBucket ?? getCdkdAssetBucketName(accountId, region);
9921
10257
  const containerRepo = options.containerRepoName ?? existingMarker?.containerRepo ?? getCdkdContainerRepoName(accountId, region);
@@ -10105,7 +10441,11 @@ var AssetModeResolver = class {
10105
10441
  }
10106
10442
  if (!this.legacyNoticeShownRegions.has(region) && !this.suppressLegacyNotice) {
10107
10443
  this.legacyNoticeShownRegions.add(region);
10108
- this.logger.info(`Assets for region '${region}' are published to the CDK bootstrap bucket/repo, which 'cdk gc' may garbage-collect (cdkd-deployed stacks have no CloudFormation stack for gc to scan). Run 'cdkd bootstrap --region ${region}' to create cdkd-owned asset storage that 'cdk gc' never touches.`);
10444
+ this.logger.info(`Assets for region '${region}' are published to the CDK bootstrap bucket/repo, which 'cdk gc' may garbage-collect (cdkd-deployed stacks have no CloudFormation stack for gc to scan). Create cdkd-owned asset storage that 'cdk gc' never touches:\nBootstrap with: ${pasteableCommand("cdkd bootstrap", [{
10445
+ flag: "--region",
10446
+ value: region,
10447
+ hole: "region"
10448
+ }]).command}`);
10109
10449
  }
10110
10450
  return { mode: "legacy" };
10111
10451
  }
@@ -10165,7 +10505,11 @@ var AssetModeResolver = class {
10165
10505
  };
10166
10506
  } catch (error) {
10167
10507
  const message = error instanceof Error ? error.message : String(error);
10168
- this.logger.warn(`Failed to auto-create cdkd asset storage for region '${region}': ${message} Falling back to the CDK bootstrap destinations for this run — run 'cdkd bootstrap --region ${region}' (with S3/ECR create permissions) to opt the region in.`);
10508
+ this.logger.warn(`Failed to auto-create cdkd asset storage for region '${region}': ${message} Falling back to the CDK bootstrap destinations for this run — opt the region in with S3/ECR create permissions.\nBootstrap with: ${pasteableCommand("cdkd bootstrap", [{
10509
+ flag: "--region",
10510
+ value: region,
10511
+ hole: "region"
10512
+ }]).command}`);
10169
10513
  return null;
10170
10514
  } finally {
10171
10515
  s3Client?.destroy();
@@ -11406,19 +11750,6 @@ function formatDuration(ms) {
11406
11750
  return `${Math.floor(seconds / 60)}m${seconds % 60}s`;
11407
11751
  }
11408
11752
  /**
11409
- * Quote a value for a pasteable shell command.
11410
- *
11411
- * EXPORTED since issue [#2610]: `src/provisioning/replacement-protection-advice.ts`
11412
- * prints `aws <service> ...` recovery commands naming a resource's physical id,
11413
- * which is the same hazard one directory over. A second spelling of this
11414
- * predicate is how the two would come to disagree about which values need
11415
- * quoting -- the reason `display-safe.ts`'s header gives for not widening a
11416
- * rule by hand. It is a pure function of its argument and imports nothing.
11417
- */
11418
- function shellQuote(value) {
11419
- return /^[A-Za-z0-9._/@:+-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
11420
- }
11421
- /**
11422
11753
  * The `cdkd force-unlock ...` line, carrying every flag that decides which
11423
11754
  * lock object it resolves to.
11424
11755
  */
@@ -11432,24 +11763,6 @@ function buildForceUnlockCommand(stackName, region, recovery) {
11432
11763
  return [safeRegion === void 0 ? `cdkd force-unlock ${shellQuote(safeStack)}` : `cdkd force-unlock ${shellQuote(safeStack)} --stack-region ${shellQuote(safeRegion)}`, ...recoveryFlags.flags].join(" ");
11433
11764
  }
11434
11765
  /**
11435
- * A placeholder for a value a pasteable command could not name, QUOTED.
11436
- *
11437
- * A bare `<profile>` is two shell redirections, not a word: pasted, `<profile`
11438
- * reads stdin from a file named `profile` and `>` sends stdout to whatever
11439
- * word follows. While the hole was the command's LAST word that was only a
11440
- * syntax error; with flags appended after it (`--profile <profile>
11441
- * --state-bucket b`) it ran the command with `--state-bucket` swallowed as a
11442
- * redirect target and `b` as the profile — a delete against the ambient
11443
- * bucket, with nothing printed (measured by the maintainer, M4 of the
11444
- * go-to-k/cdkd#3363 review). Quoted, it pastes as one literal argument and
11445
- * nothing else — no redirection, no swallowed flag. It is not refused: an
11446
- * UNFILLED hole runs as that literal value (a stack named `<stack>`, a prefix
11447
- * `<prefix>`).
11448
- */
11449
- function commandHole(name) {
11450
- return `'<${name}>'`;
11451
- }
11452
- /**
11453
11766
  * The flags that pin a pasteable state command to the caller's account and
11454
11767
  * bucket: `--profile`, the resolved `--state-bucket`, and a NON-default
11455
11768
  * `--state-prefix` (see {@link LockRecoveryContext} for why each is emitted
@@ -16779,6 +17092,40 @@ function hasPlainPrototype(value) {
16779
17092
  return proto === Object.prototype || proto === null;
16780
17093
  }
16781
17094
  /**
17095
+ * Is this a `Date` the value scan may return BY IDENTITY (issue
17096
+ * [#2427](https://github.com/go-to-k/cdkd/issues/2427))?
17097
+ *
17098
+ * The scan redacts what PERSISTS, and `JSON.stringify` persists an unmodified
17099
+ * `Date` as `Date.prototype.toJSON`'s ISO timestamp (or `null`) — never its own
17100
+ * entries. So the only thing to scan is that timestamp — the walk runs it
17101
+ * through the string arms and keeps the `Date` only when they leave it alone —
17102
+ * and rebuilding it just threw the timestamp away. "Unmodified" is three
17103
+ * conjuncts, each closing a shape whose persisted form is NOT that timestamp:
17104
+ * the EXACT prototype, a real `[[DateValue]]` (the `getTime` brand check, which
17105
+ * a `Proxy` or an `Object.create(Date.prototype)` fails), and NO own keys — an
17106
+ * own `toJSON` (enumerable or not) is what `JSON.stringify` would call instead.
17107
+ * No SDK readback or `JSON.parse` produces any of those; they are refused
17108
+ * because the identity return must mean exactly "persists as a timestamp".
17109
+ *
17110
+ * Deliberately NOT "any non-plain object". `JSON.stringify` persists an
17111
+ * arbitrary class instance's OWN ENUMERABLE fields, which is exactly what the
17112
+ * rebuild scans, so returning one by identity would persist a plaintext held in
17113
+ * such a field — a disclosure, traded for fidelity. A `Uint8Array` persists as
17114
+ * the same `{"0":...}` either way. The EXACT prototype (not `instanceof`) keeps
17115
+ * a `Date` subclass on the rebuild: its prototype can override `toJSON`, which
17116
+ * the rebuild drops and identity would honour. A cross-realm `Date` fails the
17117
+ * test too, which is the old flattening rather than a leak.
17118
+ */
17119
+ function isOrdinaryDate(value) {
17120
+ if (Object.getPrototypeOf(value) !== Date.prototype) return false;
17121
+ try {
17122
+ Date.prototype.getTime.call(value);
17123
+ } catch {
17124
+ return false;
17125
+ }
17126
+ return Reflect.ownKeys(value).length === 0;
17127
+ }
17128
+ /**
16782
17129
  * Structural equality over the JSON shapes this module walks (issue #2012).
16783
17130
  *
16784
17131
  * Own ENUMERABLE keys only, and a key count on both sides, so an inherited
@@ -17379,21 +17726,22 @@ function asIndex(marks, index) {
17379
17726
  *
17380
17727
  * KEEPING A NON-PLAIN LEAF INTACT takes the prototype guard on the object arm,
17381
17728
  * NOT that leaf rule, and an earlier revision of this comment claimed the
17382
- * opposite — measured wrong. The scan's own walk rebuilds objects and turns a
17729
+ * opposite — measured wrong. The scan's own walk rebuilt objects and turned a
17383
17730
  * `Date` the provider readback carries (`LastModified`) into `{}`; that
17384
- * flattening predates this module's derived needles on the POPULATED-map path
17385
- * (issue #2427). The object arm runs FIRST here and `isPlainObject` admits a
17386
- * `Date`, so without `hasPlainPrototype` this walk did the flattening ITSELF —
17387
- * newly extending #2427 to the EMPTY-map path, where the unchanged-resource
17388
- * `drainObservedCaptures` baseline lives and where `cdkd drift --revert` pushes
17389
- * the result to the live resource. With the guard a non-plain leaf falls
17390
- * through to `refused`. That is the position passes' own answer, which is the
17391
- * bag by identity: their object arm carried no prototype guard of its own until
17392
- * issue [#2869](https://github.com/go-to-k/cdkd/issues/2869), so a non-plain
17393
- * leaf whose source subtree carries a reference WAS already flattened one
17394
- * function earlier and this guard could only keep a `{}` intact. Both halves
17395
- * are guarded now; the remaining copy of the defect is the VALUE scan's own
17396
- * walk, which is issue #2427 and a different pass.
17731
+ * flattening predated this module's derived needles on the POPULATED-map path
17732
+ * (issue #2427, since fixed in the scan). The object arm runs FIRST here and
17733
+ * `isPlainObject` admits a `Date`, so without `hasPlainPrototype` this walk did
17734
+ * the flattening ITSELF — newly extending #2427 to the EMPTY-map path, where
17735
+ * the unchanged-resource `drainObservedCaptures` baseline lives and where
17736
+ * `cdkd drift --revert` pushes the result to the live resource. With the guard
17737
+ * a non-plain leaf falls through to `refused`. That is the position passes' own
17738
+ * answer, which is the bag by identity: their object arm carried no prototype
17739
+ * guard of its own until issue
17740
+ * [#2869](https://github.com/go-to-k/cdkd/issues/2869), so a non-plain leaf
17741
+ * whose source subtree carries a reference WAS already flattened one function
17742
+ * earlier and this guard could only keep a `{}` intact. Both halves are guarded
17743
+ * now, and the VALUE scan's own walk keeps a `Date` by identity since issue
17744
+ * #2427, so `scanned` holds the same instance.
17397
17745
  *
17398
17746
  * The net effect is byte-identical to the FIRST ordering on every input where
17399
17747
  * the un-certification did not fire — which is the whole point: it keeps that
@@ -17947,6 +18295,12 @@ function redactSecretsForState(bag, secrets, source, rules = TEMPLATE_DERIVED_RU
17947
18295
  }
17948
18296
  if (Array.isArray(value)) return value.map(walk);
17949
18297
  if (value !== null && typeof value === "object") {
18298
+ if (isOrdinaryDate(value)) {
18299
+ const persisted = value.toJSON();
18300
+ if (typeof persisted !== "string") return value;
18301
+ const scanned = walk(persisted);
18302
+ return scanned === persisted ? value : scanned;
18303
+ }
17950
18304
  const out = Object.create(null);
17951
18305
  for (const [k, v] of Object.entries(value)) out[k] = walk(v);
17952
18306
  return out;
@@ -32368,12 +32722,32 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
32368
32722
  throw error;
32369
32723
  }
32370
32724
  }
32371
- /** Lazily-constructed per-region CloudFormation client (issue #1697). */
32725
+ /**
32726
+ * Lazily-constructed per-region CloudFormation client (issue #1697).
32727
+ *
32728
+ * Built with the ambient clients' {@link AwsClients.credentialConfig}, the
32729
+ * way {@link serviceDiscoveryClient} is (issue
32730
+ * [#1983](https://github.com/go-to-k/cdkd/issues/1983)). An explicit
32731
+ * `AwsClientConfig.credentials` has no environment path — only a LIBRARY
32732
+ * caller passes one, and it never runs the CLI's `AWS_PROFILE` mirror — so a
32733
+ * client built from `awsClientDefaults()` alone ran the CFn fallback reads
32734
+ * under the default chain's identity instead. The explicit `credentials`
32735
+ * spread AFTER the defaults, so they outrank an assumed `--role-arn` role,
32736
+ * matching {@link AwsClients}' own spread order.
32737
+ *
32738
+ * Not routed through {@link clientsForRegion}: the region here is always
32739
+ * the one the caller named, never the ambient's, and a test double without
32740
+ * `credentialConfig` degrades to the default chain rather than throwing.
32741
+ * Keyed by region alone for the reason {@link regionScopedClients} gives:
32742
+ * the credential half is process-wide.
32743
+ */
32372
32744
  getCfnClient(region) {
32373
32745
  let client = this.cfnClients[region];
32374
32746
  if (!client) {
32747
+ const credentialConfig = getAwsClients().credentialConfig ?? {};
32375
32748
  client = new CloudFormationClient({
32376
- ...awsClientDefaults(),
32749
+ ...awsClientDefaults({ profile: credentialConfig.profile }),
32750
+ ...credentialConfig,
32377
32751
  region
32378
32752
  });
32379
32753
  this.cfnClients[region] = client;
@@ -35587,7 +35961,7 @@ var CloudControlProvider = class {
35587
35961
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
35588
35962
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
35589
35963
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
35590
- const { ASGProvider } = await import("./asg-provider-DqAx9B6g.js").then((n) => n.n);
35964
+ const { ASGProvider } = await import("./asg-provider-D9EJApN9.js").then((n) => n.n);
35591
35965
  const asgProvider = new ASGProvider();
35592
35966
  return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
35593
35967
  }
@@ -45153,6 +45527,53 @@ function updatePartialMessage(reason) {
45153
45527
  //#endregion
45154
45528
  //#region src/deployment/rollback-executor.ts
45155
45529
  /**
45530
+ * Rollback executor (issue #1183).
45531
+ *
45532
+ * The reusable engine that reverts a list of {@link CompletedOperation}s —
45533
+ * extracted from `DeployEngine` so BOTH callers drive identical semantics:
45534
+ *
45535
+ * - `DeployEngine` runs it in-process after a failed deploy (automatic
45536
+ * rollback, unchanged behavior except the two fixes below).
45537
+ * - The standalone `cdkd rollback` command runs it against a persisted
45538
+ * rollback journal (issue #1183 §journal), so a `--no-rollback` /
45539
+ * interrupted / partially-failed-auto-rollback deploy can be reverted
45540
+ * later.
45541
+ *
45542
+ * The executor deliberately depends only on `ProviderRegistry`, the stack
45543
+ * region, a logger, and an optional event recorder + per-op state-save
45544
+ * hook. It does NOT touch `DagBuilder` / `DiffCalculator` / the synthesizer
45545
+ * / `ExportIndexStore`, so the command can construct it without any of the
45546
+ * engine's synth-side collaborators (rollback never publishes
45547
+ * outputs/exports).
45548
+ *
45549
+ * Two deliberate behavior fixes vs. the pre-extraction in-process path
45550
+ * (both are pre-existing gaps; fixing them once benefits both callers):
45551
+ *
45552
+ * 1. **DeletionPolicy on CREATE rollback** — rolling a CREATE back IS a
45553
+ * delete as far as the policy is concerned (CloudFormation semantics), so
45554
+ * the CURRENT state record's `DeletionPolicy` decides what happens:
45555
+ * - `Retain` → ORPHANED (removed from state, left in AWS). The policy
45556
+ * says KEEP the resource, so cdkd does.
45557
+ * - `Snapshot` → final snapshot, THEN delete (issue #1358), through the
45558
+ * same mechanism matrix as the deploy engine's
45559
+ * `prepareFinalSnapshotForDelete`: atomic delete parameter for the
45560
+ * SDK-routed `ATOMIC_FINAL_SNAPSHOT_TYPES`, an explicit pre-delete
45561
+ * snapshot for `PRE_DELETE_SNAPSHOT_TYPES`, refusal for every other
45562
+ * Snapshot shape (cc-api routing included) unless
45563
+ * `--skip-final-snapshot` opts into the data loss. This arm ORPHANED
45564
+ * alongside `Retain` until #1358: when this file was written cdkd could
45565
+ * not create a final snapshot at all, so leaving the resource behind
45566
+ * was the only non-destructive option — but it silently handed the user
45567
+ * an untracked, billing resource that state no longer knew about.
45568
+ * `src/provisioning/final-snapshot.ts` (#1352 / #1353) removed the
45569
+ * constraint, so the policy is now honored literally.
45570
+ * - `RetainExceptOnCreate` (which exists precisely to allow cleanup of
45571
+ * failed creates) and absent / `Delete` → DELETE.
45572
+ * 2. **Idempotent replay skip rules** — so a partially-failed rollback can
45573
+ * be re-run safely (also harmless for the in-process caller, which
45574
+ * replays each op exactly once).
45575
+ */
45576
+ /**
45156
45577
  * Issue [#1762](https://github.com/go-to-k/cdkd/issues/1762): turn a
45157
45578
  * `{ outcome: 'skipped' }` delete into a thrown error at every rollback delete
45158
45579
  * arm.
@@ -46479,7 +46900,14 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
46479
46900
  createLayer: createProvisionedBy
46480
46901
  }) && !deletedNewFirst && createResult.physicalId === current.physicalId;
46481
46902
  if (adoptedLiveNewResource) {
46482
- logger.warn(` ⚠ ${safe(op.logicalId)} (${safe(op.resourceType)}): the re-create returned the LIVE new resource (${displaySafe(current.physicalId)}) instead of re-creating the old one — its Create API is name-idempotent and the new resource still holds the same user-supplied name. Skipping the delete-new step (it would delete that very resource). The old resource's ORIGINAL properties may NOT have been re-applied; state now records the pre-replacement properties, so run 'cdkd drift ${stackName}' to inspect and 'cdkd deploy' to reconcile, or rename the resource to make the replacement reversible.`);
46903
+ logger.warn(` ⚠ ${safe(op.logicalId)} (${safe(op.resourceType)}): the re-create returned the LIVE new resource (${displaySafe(current.physicalId)}) instead of re-creating the old one — its Create API is name-idempotent and the new resource still holds the same user-supplied name. Skipping the delete-new step (it would delete that very resource). The old resource's ORIGINAL properties may NOT have been re-applied; state now records the pre-replacement properties, so inspect the drift and run 'cdkd deploy' to reconcile, or rename the resource to make the replacement reversible.\nInspect it with: ${pasteableCommand("cdkd drift", [{
46904
+ value: stackName,
46905
+ hole: "stack"
46906
+ }, {
46907
+ flag: "--stack-region",
46908
+ value: ctx.region,
46909
+ hole: "region"
46910
+ }]).command}`);
46483
46911
  result.warnings++;
46484
46912
  }
46485
46913
  const { observedProperties: _staleObserved, ...prevRecord } = prev;
@@ -48207,27 +48635,6 @@ var DeployEngine = class DeployEngine {
48207
48635
  const foreignReads = reads.filter((read) => !isLocal(read));
48208
48636
  const hasRefStateRead = reads.some((read) => read.kind === "ref-state-key" && isLocal(read));
48209
48637
  /**
48210
- * A logical id rendered inside the single-quoted command below.
48211
- *
48212
- * cdkd validates no logical-id charset, so the id can contain a `'` — and
48213
- * the line it lands in is meant to be COPY-PASTED into a shell, where an
48214
- * unescaped one closes the quoting early and the rest of the command
48215
- * reparses as something else. POSIX single-quote escaping (`'` becomes
48216
- * `'\\''`) is the fix: close, emit an escaped quote, reopen.
48217
- *
48218
- * cdkd never EXECUTES this string — it is advice in an error message — so
48219
- * this is about the pasted command being correct, not about injection into
48220
- * cdkd itself (issue #2847 round-5 review).
48221
- *
48222
- * SINCE go-to-k/cdkd#3435 no id reaching this arm can contain a `'`:
48223
- * `isPasteableIdent` admits `[A-Za-z0-9][A-Za-z0-9~_.-]*` only, and the
48224
- * command is not emitted for anything else. Kept anyway rather than
48225
- * deleted — dropping a guard on the strength of a NEW guard one line up is
48226
- * how the pair later comes apart, and this one costs a `replaceAll` on a
48227
- * short string.
48228
- */
48229
- const quoteSafe = (id) => id.replaceAll("'", `'\\''`);
48230
- /**
48231
48638
  * A logical id rendered as a NAME rather than as a command argument
48232
48639
  * (go-to-k/cdkd#3435 security round).
48233
48640
  *
@@ -48242,11 +48649,24 @@ var DeployEngine = class DeployEngine {
48242
48649
  /** CloudFormation's own logical-id length bound. */
48243
48650
  const CFN_LOGICAL_ID_MAX_LENGTH = 255;
48244
48651
  const parts = [];
48652
+ /** Pasteable commands, emitted LAST and one per line (go-to-k/cdkd#3436). */
48653
+ const commandLines = [];
48245
48654
  if (localTargets.length > 0) {
48246
48655
  const pasteableHere = (id) => isPasteableIdent(id) && id.length <= CFN_LOGICAL_ID_MAX_LENGTH;
48247
48656
  const pasteable = localTargets.filter(pasteableHere);
48248
48657
  const withheld = localTargets.filter((id) => !pasteableHere(id));
48249
- if (pasteable.length > 0) parts.push(`Re-import the record that HOLDS the mask: ` + pasteable.map((id) => `'cdkd import <stack> --resource ${quoteSafe(id)}=<physicalId> --force'`).join(", ") + `.`);
48658
+ if (pasteable.length > 0) {
48659
+ parts.push(`Re-import the record that HOLDS the mask (command(s) below).`);
48660
+ for (const id of pasteable) commandLines.push(`Re-import with: ${pasteableCommand("cdkd import", [
48661
+ { hole: "stack" },
48662
+ {
48663
+ flag: "--resource",
48664
+ value: `${id}=<physicalId>`,
48665
+ hole: "resource"
48666
+ },
48667
+ { literal: "--force" }
48668
+ ]).command}`);
48669
+ }
48250
48670
  if (withheld.length > 0) parts.push(`Re-import the record that HOLDS the mask for ${withheld.map(shown).join(", ")}, but the command is withheld: that is not a plain CloudFormation logical id, so a pasted 'cdkd import <stack> --resource <id>=<physicalId> --force' could be reshaped by the shell or name a different resource. Read the id from 'cdkd state show' and quote it yourself.`);
48251
48671
  }
48252
48672
  if (unclearableTargets.length > 0) parts.push(`Do NOT re-import ${unclearableTargets.map(shown).join(", ")}: a custom resource's import records no attributes, so the masked bag is carried forward unchanged and the refusal repeats. Cause (1) above is the one that applies to it.`);
@@ -48255,7 +48675,7 @@ var DeployEngine = class DeployEngine {
48255
48675
  parts.push(`${subject} through ANOTHER stack (an Fn::ImportValue, an Fn::GetStackOutput, or a nested stack's Outputs), whose masked record lives in that stack's state — re-importing anything in this stack cannot clear it; act on the producer stack instead.`);
48256
48676
  }
48257
48677
  if (hasRefStateRead) parts.push("A 'Ref <LogicalId> (state key <Key>)' entry above is CDKD's own read, not one the template can stop making: CloudFormation defines that resource type's Ref value as that state key rather than the physical id, so the record must be repaired (re-import it, or let a deploy create or update the resource) — the \"stop reading it\" remedy does not apply to such an entry.");
48258
- return parts.join(" ");
48678
+ return [parts.join(" "), ...commandLines].join("\n");
48259
48679
  }
48260
48680
  /**
48261
48681
  * Refuse to provision a resource whose resolution served a REDACTED attribute
@@ -48298,7 +48718,7 @@ var DeployEngine = class DeployEngine {
48298
48718
  refuseRedactedAttributeReads(logicalId, resourceType, context) {
48299
48719
  const reads = context.redactedAttributeReads;
48300
48720
  if (reads === void 0 || reads.length === 0) return;
48301
- throw new ProvisioningError(`Cannot resolve ${reads.map((read) => read.display).join(", ")} for ${logicalId}: cdkd's recorded state holds only the redaction mask there, and the value is not recoverable from state. There are two ways a record comes to hold the mask. (1) A custom resource handler declared its response NoEcho: true — the value is generated by the handler, so cdkd has nothing to re-derive it from and must not write the literal mask to AWS. Remedies: force that custom resource to update (change one of its properties, e.g. a nonce / version property) so its handler runs again and supplies the value in this same run; or stop setting NoEcho on that response. If the value comes from ANOTHER stack, the producer and this stack must deploy in ONE run (cdkd deploy --all) with the producer's custom resource actually running — re-deploying the producer by itself does not help, because it re-masks the value on the way into its own state. (2) The resource was adopted by 'cdkd import' through the Cloud Control fallback, which records only the attributes the type's CloudFormation schema declares read-only and masks the rest. Either the attribute named above is not one of them — CloudFormation would reject an Fn::GetAtt naming it too, so stop reading it — or cdkd could not read that schema and masked the whole model, which the import warned about when it happened. ${DeployEngine.maskedRecordRemedyFor(reads, context.resources)} If that warning named a missing cloudformation:DescribeType permission, grant it first. See https://github.com/go-to-k/cdkd/issues/2449.`, resourceType, logicalId);
48721
+ throw new ProvisioningError(`Cannot resolve ${reads.map((read) => read.display).join(", ")} for ${logicalId}: cdkd's recorded state holds only the redaction mask there, and the value is not recoverable from state. There are two ways a record comes to hold the mask. (1) A custom resource handler declared its response NoEcho: true — the value is generated by the handler, so cdkd has nothing to re-derive it from and must not write the literal mask to AWS. Remedies: force that custom resource to update (change one of its properties, e.g. a nonce / version property) so its handler runs again and supplies the value in this same run; or stop setting NoEcho on that response. If the value comes from ANOTHER stack, the producer and this stack must deploy in ONE run (cdkd deploy --all) with the producer's custom resource actually running — re-deploying the producer by itself does not help, because it re-masks the value on the way into its own state. (2) The resource was adopted by 'cdkd import' through the Cloud Control fallback, which records only the attributes the type's CloudFormation schema declares read-only and masks the rest. Either the attribute named above is not one of them — CloudFormation would reject an Fn::GetAtt naming it too, so stop reading it — or cdkd could not read that schema and masked the whole model, which the import warned about when it happened. If that warning named a missing cloudformation:DescribeType permission, grant it first. See https://github.com/go-to-k/cdkd/issues/2449. ${DeployEngine.maskedRecordRemedyFor(reads, context.resources)}`, resourceType, logicalId);
48302
48722
  }
48303
48723
  redactOperationsForJournal(operations) {
48304
48724
  return operations.map((op) => {
@@ -48821,7 +49241,13 @@ var DeployEngine = class DeployEngine {
48821
49241
  const journal = await this.stateBackend.loadRollbackJournal(stackName, this.stackRegion);
48822
49242
  if (journal && journal.segments.length > 0) {
48823
49243
  const failedOnly = journal.segments.every((s) => s.operations.length === 0) && journal.segments.some((s) => (s.failedOperations?.length ?? 0) > 0);
48824
- this.logger.info(failedOnly ? `A previous deploy of '${stackName}' failed and was automatically rolled back. The failed resource may be partially applied — run 'cdkd rollback ${stackName} --revert-failed' to revert it, or continue deploying to fix forward (a successful deploy clears this note).` : `A previous deploy of '${stackName}' failed or was interrupted. Run 'cdkd rollback ${stackName}' to revert it, or continue deploying to fix forward.`);
49244
+ this.logger.info(failedOnly ? `A previous deploy of '${stackName}' failed and was automatically rolled back. The failed resource may be partially applied — revert it, or continue deploying to fix forward (a successful deploy clears this note).\nRevert it with: ${pasteableCommand("cdkd rollback", [{
49245
+ value: stackName,
49246
+ hole: "stack"
49247
+ }, { literal: "--revert-failed" }]).command}` : `A previous deploy of '${stackName}' failed or was interrupted. Revert it, or continue deploying to fix forward.\nRevert it with: ${pasteableCommand("cdkd rollback", [{
49248
+ value: stackName,
49249
+ hole: "stack"
49250
+ }]).command}`);
48825
49251
  }
48826
49252
  } catch {}
48827
49253
  this.stampReasonlessParameterRefusals(currentState.resources, template);
@@ -49450,7 +49876,10 @@ var DeployEngine = class DeployEngine {
49450
49876
  return;
49451
49877
  }
49452
49878
  await this.writeRollbackJournalSegment(stackName, [], failedOperations, "auto-rollback-clean", initialDeploy);
49453
- this.logger.info(`The automatic rollback restored the pre-deploy state. The failed resource's pre-failure record was kept — if it was left partially applied, run 'cdkd rollback ${stackName} --revert-failed' to revert it.`);
49879
+ this.logger.info(`The automatic rollback restored the pre-deploy state. The failed resource's pre-failure record was kept — if it was left partially applied, revert it.\nRevert it with: ${pasteableCommand("cdkd rollback", [{
49880
+ value: stackName,
49881
+ hole: "stack"
49882
+ }, { literal: "--revert-failed" }]).command}`);
49454
49883
  }
49455
49884
  /** Build the {@link RollbackExecutorContext} from the engine's fields. */
49456
49885
  rollbackExecutorContext(previousState) {
@@ -50179,7 +50608,21 @@ var DeployEngine = class DeployEngine {
50179
50608
  const skipPrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
50180
50609
  renderer.removeTask(logicalId);
50181
50610
  this.logger.info(`${skipPrefix}${formatResourceLine("skipped", logicalId, resourceType, `skipped (${deleteSkipped})`)}`);
50182
- this.logger.warn(deleteSkippedMessage(logicalId, currentResource.physicalId, deleteSkipped, "while removing it from the template") + `. Its cdkd state record was KEPT, so the next 'cdkd deploy' re-attempts the delete. Repair the record first ('cdkd state show ${stackName}' to inspect; for a nested stack it is the CHILD's own state, whose other resources may already be gone), or delete the resource by hand and drop the record with 'cdkd state orphan ${stackName}'.`);
50611
+ this.logger.warn(deleteSkippedMessage(logicalId, currentResource.physicalId, deleteSkipped, "while removing it from the template") + `. Its cdkd state record was KEPT, so the next 'cdkd deploy' re-attempts the delete. Repair the record first (for a nested stack it is the CHILD's own state, whose other resources may already be gone), or delete the resource by hand and drop the record.\nInspect it with: ${pasteableCommand("cdkd state show", [{
50612
+ value: stackName,
50613
+ hole: "stack"
50614
+ }, {
50615
+ flag: "--stack-region",
50616
+ value: this.stackRegion,
50617
+ hole: "region"
50618
+ }]).command}\nDrop the record with: ${pasteableCommand("cdkd state orphan", [{
50619
+ value: stackName,
50620
+ hole: "stack"
50621
+ }, {
50622
+ flag: "--stack-region",
50623
+ value: this.stackRegion,
50624
+ hole: "region"
50625
+ }]).command}`);
50183
50626
  if (counts) counts.deleteSkipped++;
50184
50627
  return { deleteSkipped };
50185
50628
  }
@@ -50837,5 +51280,5 @@ var DeployEngine = class DeployEngine {
50837
51280
  };
50838
51281
 
50839
51282
  //#endregion
50840
- export { yellow as $, retryClassificationText as $a, expectedOwnerParam as $i, repairMalformedResourcePropertiesForReadOnly as $n, rewriteTemplateAssetReferences as $r, requireConfigObject as $t, createPreDeleteFinalSnapshot as A, LocalInvokeBuildError as Aa, getDefaultStateBucketName as Ai, malformedOutputsRefusalMessage as An, buildForceUnlockCommand as Ar, carriesDynamicReference as At, isStatefulRecreateTargetSync as B, StackTerminationProtectionError as Ba, warnDeprecatedNoPrefixCliFlag as Bi, refuseMalformedOrphansForDestroy as Bn, hasUnverifiableParameterRefusal as Br, isSettledInstanceState as Bt, namesAnyDeclaredParameter as C, CdkdError as Ca, runDockerStreaming as Ci, isReadableResourceEntry as Cn, recoverMaskedOutput as Cr, slowCcOperationTimeoutMs as Ct, PRE_DELETE_SNAPSHOT_TYPES as D, DeployCancelledError as Da, getDockerImageBySourceHash as Di, malformedOrphanRecordsWarning as Dn, S3StateBackend as Dr, disableInstanceApiTermination as Dt, ATOMIC_FINAL_SNAPSHOT_TYPES as E, DependencyError as Ea, AssetManifestLoader as Ei, malformedLocalOutputsWarning as En, LockManager as Er, deleteSkipReason as Et, extractDeploymentEventError as F, PartialFailureError as Fa, resolveSkipPrefix as Fi, malformedResourcesWarning as Fn, CUSTOM_RESOURCE_RESPONSE_PREFIX as Fr, isImpossibleEmptyStoredAttribute as Ft, toCfnInteger as G, normalizeAwsError as Ga, findLargeInlineResources as Gi, refuseMalformedResourceEntries as Gn, orphansCarriedFrom as Gr, coerceCfnBoolean as Gt, WARM_THROUGHPUT_MEMBERS as H, SynthesisError as Ha, CFN_TEMPLATE_BODY_LIMIT as Hi, refuseMalformedOutputs as Hn, importableOutputs as Hr, normalizeAwsTagsToCfn as Ht, makeSiblingClaimReader as I, ProvisioningError as Ia, resolveStateBucketWithDefault as Ii, malformedStateRefusalMessage as In, DEFAULT_STATE_PREFIX as Ir, isNamelessDynamicReferenceError as It, bold as J, isRetryableTransientError as Ja, injectiveKey as Ji, refuseMalformedResourcesForDestroy as Jn, stringifyValue as Jr, configIntegerRefusal as Jt, toFiniteNumber as K, withErrorHandling as Ka, uploadCfnTemplate as Ki, refuseMalformedResourceEntriesForOrphan as Kn, shouldRetainResource as Kr, coerceCfnInteger as Kt, planOrphanAdoption as L, ResourceTimeoutError as La, resolveStateBucketWithDefaultAndSource as Li, refuseDivergentRecordRegionForDestroy as Ln, exportNamesCarriedFrom as Lr, isUnboundTemplateParameter as Lt, refusesFinalSnapshot as M, LockError as Ma, resolveApp as Mi, malformedRenderedContainersWarning as Mn, forceQuitRecoveryClause as Mr, cfnRefValueFromPhysicalId as Mt, unsupportedFinalSnapshotError as N, MalformedProducerRecordRefusalError as Na, resolveAutoAssetStorage as Ni, malformedResourceEntriesWarning as Nn, formatLockExpiry as Nr, coerceParameterTypedValue as Nt, buildFinalSnapshotIdentifier as O, DynamicReferenceRegionAmbiguousError as Oa, Synthesizer as Oi, malformedOrphansRefusalMessage as On, rebuildClientForBucketRegion as Or, isTerminationProtectionPropagationError as Ot, makeCanonicalizePropertiesFn as P, NestedStackChildDirectDestroyError as Pa, resolveCaptureObservedState as Pi, malformedResourcePropertiesWarning as Pn, shellQuote as Pr, getAccountInfo as Pt, red as Q, markRedactedCause as Qa, decodeListingKey as Qi, repairMalformedResourceEntriesForReadOnly as Qn, loadPublishableAssetManifest as Qr, requireConfigArray as Qt, MULTI_REGION_RECREATE_BLOCKED_TYPES as R, ResourceUpdateNotSupportedError as Ra, resolveUseCdkBootstrapAssets as Ri, refuseMalformedNestedChildOutputs as Rn, hasReadableExportSet as Rr, parameterTypeMayLoseSecretIdentity as Rt, computeParameterTaint as S, AssetError as Sa, runDockerForeground as Si, hasReadableResources as Sn, recordMaskOnlyValue as Sr, isWaitAbandonedError as St, resourcesNamingDeclaredParameter as T, CrossAccountSecretRefusalError as Ta, stripControlChars as Ti, malformedExportSourceWarning as Tn, scrubResourceRecord as Tr, deleteIndeterminateGuards as Tt, coerceWarmThroughput as U, formatError as Ua, CFN_TEMPLATE_URL_LIMIT as Ui, refuseMalformedOutputsForDestroy as Un, isReadableBag as Ur, resolveExplicitPhysicalId as Ut, renderStatefulReason as V, StateError as Va, ssmResolvedValueType as Vi, refuseMalformedOrphansForOrphan as Vn, importableOutputKeys as Vr, WAFv2WebACLProvider as Vt, isWarmThroughputDecrease as W, isCdkdError as Wa, MIGRATE_TMP_PREFIX as Wi, refuseMalformedResourceAttributesForOrphan as Wn, orphansAfterRollback as Wr, assertRegionMatch as Wt, gray as X, isTransientServerError as Xa, producerCoordinateKey as Xi, repairMalformedOrphansForReadOnly as Xn, buildAssetRedirectMap as Xr, readConfigString as Xt, cyan as Y, isThrottlingError as Ya, injectiveKeyPrefix as Yi, refuseMalformedState as Yn, WorkGraph as Yr, configStringRefusal as Yt, green as Z, markNonRetryable as Za, producerRecordKey as Zi, repairMalformedOutputsForReadOnly as Zn, createAssetRedirectResolver as Zr, replayWarn as Zt, bagHoldsSecretExpression as _, AwsClients as _a, dockerSpawnEnvWithSensitive as _i, UNREADABLE_ORPHANS_CONTAINER_ROW as _n, identityKeyFor as _r, endCommandInterruptScope as _t, DeploymentEventsStore as a, globMatches as aa, isCrossRegionRedirect as ai, s3BucketDomainName as an, describeTypeWithThrottleRetry as ar, secretBearingStateKeyWarning as at, mergeNoChangeOutputs as b, runWithStackAwsClients as ba, partitionSensitiveEnv as bi, hasReadableOrphans as bn, maskSecretsInError as br, startInterruptWatch as bt, replayFailedOperations as c, hasOwnKey as ca, validateAssetBucketName as ci, s3BucketWebsiteUrl as cn, TemplateParser as cr, IAMRoleProvider as ct, updatePartialMessage as d, ownValue as da, buildDockerImage as di, applyCallerIdentityCredentials as dn, STATE_SOURCED_READBACK_RULES as dr, ProviderRegistry as dt, PARTITION_TABLE as ea, AssetModeResolver as ei, requireConfigString as en, describeAwsFailure as eo, repairMalformedResourcesForReadOnly as er, collectDeclaredOutputNames as et, updatePartialReason as f, absoluteAssemblyPathEscape as fa, resolveDockerContextDirectory as fi, callerEnvCredentials as fn, TEMPLATE_SOURCED_RULES as fr, wouldReturnToSdkProvider as ft, computeImplicitDeleteEdges as g, resolveBucketRegion as ga, describeDockerFailure as gi, STATE_RESOURCES_MALFORMED as gn, errorCauseChain as gr, beginCommandInterruptScope as gt, IMPLICIT_DELETE_DEPENDENCIES as h, clearBucketRegionCache as ha, describeDockerExecFailure as hi, INTRINSIC_KEYS as hn, dynamicReferenceTokens as hr, maskerOrIdentity as ht, DeploymentEventsReader as i, failedStageNote as ia, getBootstrapMarkerKey as ii, s3BucketArn as in, findSilentDropProperties as ir, secretBearing as it, isFinalSnapshotError as j, LocalStartServiceError as ja, getLegacyStateBucketName as ji, malformedOutputsWarning as jn, buildLockContentionMessage as jr, carriesFetchableDynamicReference as jt, ccRoutedFinalSnapshotError as k, IntrinsicResolutionRefusalError as ka, synthesisStatusMessage as ki, malformedOrphansWarning as kn, UNREPRODUCIBLE_LOCK_CLAUSE as kr, IntrinsicFunctionResolver as kt, replayRollback as l, hasPlainPrototype$1 as la, validateContainerRepoName as li, applyRoleArnIfSet as ln, STATE_SOURCED_BASELINE_RULES as lr, collectInlinePolicyNamesManagedBySiblings as lt, maskingRetryLogger as m, resolveAssemblyPath as ma, describeDockerCapturedOutput as mi, DiffCalculator as mn, createSecretMasker as mr, maskDeep as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, derivePartitionAndUrlSuffix as na, assertAssetBucketRegion as ni, producerRegionsFromState as nn, __exportAll as no, safeStackName as nr, exportAliasCollisionScrubWarning as nt, planFailedOps as o, processStackMessages as oa, parseBootstrapMarker as oi, s3BucketDualStackDomainName as on, withRetry as or, secretSafeKeyDisplay as ot, withResourceDeadline as p, renderAssemblyPathEscape as pa, warnManifestExecutable as pi, envCredentialsAreAssumedRole as pn, carriesSecretMask as pr, createMaskedRetryLogger as pt, formatResourceLine as q, isMarkedNonRetryable as qa, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as qi, refuseMalformedResourcePropertiesForOrphan as qn, AssetPublisher as qr, configBooleanRefusal as qt, DeployEngine as r, AssemblyReader as ra, ensureAssetStorage as ri, withSharedDrainBudget as rn, findActionableSilentDrops as rr, isExportAliasCollision as rt, planRollback as s, defineOwnKey as sa, readBootstrapMarkerBody as si, s3BucketRegionalDomainName as sn, DagBuilder as sr, getCurrentResourceSecrets as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, canonicalizeRegion as ta, BOOTSTRAP_MARKER_PREFIX as ti, classifyReplaySecretRegion as tn, safeStringify$1 as to, safeRegion as tr, collectPublishedOutputNames as tt, resolveReplacementOldType as u, nullPrototypeRecord as ua, buildDenyExternalAccessPolicy as ui, AWS_CREDENTIAL_ENV_KEYS as un, STATE_SOURCED_CROSS_GENERATION_RULES as ur, clearOnUpdateRemoval as ut, isSecretBearingReferenceString as v, getAwsClients as va, formatDockerLoginError as vi, UNREADABLE_RESOURCES_MAP_ROW as vn, isSingleDynamicReferenceToken as vr, interruptWatchListenerCount as vt, reachableDivergentParameters as w, ConfigError as wa, escapeRegExp$1 as wi, malformedExportNamesWarning as wn, redactSecretsForState as wr, UNSPECIFIED_SKIP_REASON as wt, bindingSkippedOutputs as x, setAwsClients as xa, redactDockerArgvValues as xi, hasReadableOutputs as xn, maskSecretsInText as xr, CloudControlProvider as xt, keptWholeReasonText as y, resetAwsClients as ya, getDockerCmd as yi, displayLogicalId as yn, markSameGenerationBag as yr, isInterruptedWaitError as yt, isStatefulRecreateTargetForReplace as z, StackHasActiveImportsError as za, stateBucketExistenceConfirmed as zi, refuseMalformedOrphans as zn, hasReasonlessBaselineRefusal as zr, refStateLookupFromResource as zt };
50841
- //# sourceMappingURL=deploy-engine-Gl-b_bNn.js.map
51283
+ export { yellow as $, markRedactedCause as $a, PARTITION_TABLE as $i, repairMalformedResourcePropertiesForReadOnly as $n, AssetModeResolver as $r, requireConfigObject as $t, createPreDeleteFinalSnapshot as A, LocalStartServiceError as Aa, getLegacyStateBucketName as Ai, malformedOutputsRefusalMessage as An, buildForceUnlockCommand as Ar, carriesDynamicReference as At, isStatefulRecreateTargetSync as B, StateError as Ba, ssmResolvedValueType as Bi, refuseMalformedOrphansForDestroy as Bn, importableOutputKeys as Br, isSettledInstanceState as Bt, namesAnyDeclaredParameter as C, ConfigError as Ca, escapeRegExp$1 as Ci, isReadableResourceEntry as Cn, recoverMaskedOutput as Cr, slowCcOperationTimeoutMs as Ct, PRE_DELETE_SNAPSHOT_TYPES as D, DynamicReferenceRegionAmbiguousError as Da, Synthesizer as Di, malformedOrphanRecordsWarning as Dn, S3StateBackend as Dr, disableInstanceApiTermination as Dt, ATOMIC_FINAL_SNAPSHOT_TYPES as E, DeployCancelledError as Ea, getDockerImageBySourceHash as Ei, malformedLocalOutputsWarning as En, LockManager as Er, deleteSkipReason as Et, extractDeploymentEventError as F, ProvisioningError as Fa, resolveStateBucketWithDefault as Fi, malformedResourcesWarning as Fn, DEFAULT_STATE_PREFIX as Fr, isImpossibleEmptyStoredAttribute as Ft, toCfnInteger as G, withErrorHandling as Ga, uploadCfnTemplate as Gi, refuseMalformedResourceEntries as Gn, shouldRetainResource as Gr, coerceCfnBoolean as Gt, WARM_THROUGHPUT_MEMBERS as H, formatError as Ha, CFN_TEMPLATE_URL_LIMIT as Hi, refuseMalformedOutputs as Hn, isReadableBag as Hr, normalizeAwsTagsToCfn as Ht, makeSiblingClaimReader as I, ResourceTimeoutError as Ia, resolveStateBucketWithDefaultAndSource as Ii, malformedStateRefusalMessage as In, exportNamesCarriedFrom as Ir, isNamelessDynamicReferenceError as It, bold as J, isMarkedNonRetryable as Ja, injectiveKeyPrefix as Ji, refuseMalformedResourcesForDestroy as Jn, WorkGraph as Jr, configIntegerRefusal as Jt, toFiniteNumber as K, pasteableCommand as Ka, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Ki, refuseMalformedResourceEntriesForOrphan as Kn, AssetPublisher as Kr, coerceCfnInteger as Kt, planOrphanAdoption as L, ResourceUpdateNotSupportedError as La, resolveUseCdkBootstrapAssets as Li, refuseDivergentRecordRegionForDestroy as Ln, hasReadableExportSet as Lr, isUnboundTemplateParameter as Lt, refusesFinalSnapshot as M, MalformedProducerRecordRefusalError as Ma, resolveAutoAssetStorage as Mi, malformedRenderedContainersWarning as Mn, forceQuitRecoveryClause as Mr, cfnRefValueFromPhysicalId as Mt, unsupportedFinalSnapshotError as N, NestedStackChildDirectDestroyError as Na, resolveCaptureObservedState as Ni, malformedResourceEntriesWarning as Nn, formatLockExpiry as Nr, coerceParameterTypedValue as Nt, buildFinalSnapshotIdentifier as O, IntrinsicResolutionRefusalError as Oa, synthesisStatusMessage as Oi, malformedOrphansRefusalMessage as On, rebuildClientForBucketRegion as Or, isTerminationProtectionPropagationError as Ot, makeCanonicalizePropertiesFn as P, PartialFailureError as Pa, resolveSkipPrefix as Pi, malformedResourcePropertiesWarning as Pn, CUSTOM_RESOURCE_RESPONSE_PREFIX as Pr, getAccountInfo as Pt, red as Q, markNonRetryable as Qa, expectedOwnerParam as Qi, repairMalformedResourceEntriesForReadOnly as Qn, rewriteTemplateAssetReferences as Qr, requireConfigArray as Qt, MULTI_REGION_RECREATE_BLOCKED_TYPES as R, StackHasActiveImportsError as Ra, stateBucketExistenceConfirmed as Ri, refuseMalformedNestedChildOutputs as Rn, hasReasonlessBaselineRefusal as Rr, parameterTypeMayLoseSecretIdentity as Rt, computeParameterTaint as S, CdkdError as Sa, runDockerStreaming as Si, hasReadableResources as Sn, recordMaskOnlyValue as Sr, isWaitAbandonedError as St, resourcesNamingDeclaredParameter as T, DependencyError as Ta, AssetManifestLoader as Ti, malformedExportSourceWarning as Tn, scrubResourceRecord as Tr, deleteIndeterminateGuards as Tt, coerceWarmThroughput as U, isCdkdError as Ua, MIGRATE_TMP_PREFIX as Ui, refuseMalformedOutputsForDestroy as Un, orphansAfterRollback as Ur, resolveExplicitPhysicalId as Ut, renderStatefulReason as V, SynthesisError as Va, CFN_TEMPLATE_BODY_LIMIT as Vi, refuseMalformedOrphansForOrphan as Vn, importableOutputs as Vr, WAFv2WebACLProvider as Vt, isWarmThroughputDecrease as W, normalizeAwsError as Wa, findLargeInlineResources as Wi, refuseMalformedResourceAttributesForOrphan as Wn, orphansCarriedFrom as Wr, assertRegionMatch as Wt, gray as X, isThrottlingError as Xa, producerRecordKey as Xi, repairMalformedOrphansForReadOnly as Xn, createAssetRedirectResolver as Xr, readConfigString as Xt, cyan as Y, isRetryableTransientError as Ya, producerCoordinateKey as Yi, refuseMalformedState as Yn, buildAssetRedirectMap as Yr, configStringRefusal as Yt, green as Z, isTransientServerError as Za, decodeListingKey as Zi, repairMalformedOutputsForReadOnly as Zn, loadPublishableAssetManifest as Zr, replayWarn as Zt, bagHoldsSecretExpression as _, getAwsClients as _a, formatDockerLoginError as _i, UNREADABLE_ORPHANS_CONTAINER_ROW as _n, identityKeyFor as _r, endCommandInterruptScope as _t, DeploymentEventsStore as a, processStackMessages as aa, parseBootstrapMarker as ai, s3BucketDomainName as an, describeTypeWithThrottleRetry as ar, secretBearingStateKeyWarning as at, mergeNoChangeOutputs as b, setAwsClients as ba, redactDockerArgvValues as bi, hasReadableOrphans as bn, maskSecretsInError as br, startInterruptWatch as bt, replayFailedOperations as c, hasPlainPrototype$1 as ca, validateContainerRepoName as ci, s3BucketWebsiteUrl as cn, TemplateParser as cr, IAMRoleProvider as ct, updatePartialMessage as d, absoluteAssemblyPathEscape as da, resolveDockerContextDirectory as di, applyCallerIdentityCredentials as dn, STATE_SOURCED_READBACK_RULES as dr, ProviderRegistry as dt, canonicalizeRegion as ea, BOOTSTRAP_MARKER_PREFIX as ei, requireConfigString as en, retryClassificationText as eo, repairMalformedResourcesForReadOnly as er, collectDeclaredOutputNames as et, updatePartialReason as f, renderAssemblyPathEscape as fa, warnManifestExecutable as fi, callerEnvCredentials as fn, TEMPLATE_SOURCED_RULES as fr, wouldReturnToSdkProvider as ft, computeImplicitDeleteEdges as g, AwsClients as ga, dockerSpawnEnvWithSensitive as gi, STATE_RESOURCES_MALFORMED as gn, errorCauseChain as gr, beginCommandInterruptScope as gt, IMPLICIT_DELETE_DEPENDENCIES as h, resolveBucketRegion as ha, describeDockerFailure as hi, INTRINSIC_KEYS as hn, dynamicReferenceTokens as hr, maskerOrIdentity as ht, DeploymentEventsReader as i, globMatches as ia, isCrossRegionRedirect as ii, s3BucketArn as in, findSilentDropProperties as ir, secretBearing as it, isFinalSnapshotError as j, LockError as ja, resolveApp as ji, malformedOutputsWarning as jn, buildLockContentionMessage as jr, carriesFetchableDynamicReference as jt, ccRoutedFinalSnapshotError as k, LocalInvokeBuildError as ka, getDefaultStateBucketName as ki, malformedOrphansWarning as kn, UNREPRODUCIBLE_LOCK_CLAUSE as kr, IntrinsicFunctionResolver as kt, replayRollback as l, nullPrototypeRecord as la, buildDenyExternalAccessPolicy as li, applyRoleArnIfSet as ln, STATE_SOURCED_BASELINE_RULES as lr, collectInlinePolicyNamesManagedBySiblings as lt, maskingRetryLogger as m, clearBucketRegionCache as ma, describeDockerExecFailure as mi, DiffCalculator as mn, createSecretMasker as mr, maskDeep as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, AssemblyReader as na, ensureAssetStorage as ni, producerRegionsFromState as nn, safeStringify$1 as no, safeStackName as nr, exportAliasCollisionScrubWarning as nt, planFailedOps as o, defineOwnKey as oa, readBootstrapMarkerBody as oi, s3BucketDualStackDomainName as on, withRetry as or, secretSafeKeyDisplay as ot, withResourceDeadline as p, resolveAssemblyPath as pa, describeDockerCapturedOutput as pi, envCredentialsAreAssumedRole as pn, carriesSecretMask as pr, createMaskedRetryLogger as pt, formatResourceLine as q, shellQuote as qa, injectiveKey as qi, refuseMalformedResourcePropertiesForOrphan as qn, stringifyValue as qr, configBooleanRefusal as qt, DeployEngine as r, failedStageNote as ra, getBootstrapMarkerKey as ri, withSharedDrainBudget as rn, __exportAll as ro, findActionableSilentDrops as rr, isExportAliasCollision as rt, planRollback as s, hasOwnKey as sa, validateAssetBucketName as si, s3BucketRegionalDomainName as sn, DagBuilder as sr, getCurrentResourceSecrets as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, derivePartitionAndUrlSuffix as ta, assertAssetBucketRegion as ti, classifyReplaySecretRegion as tn, describeAwsFailure as to, safeRegion as tr, collectPublishedOutputNames as tt, resolveReplacementOldType as u, ownValue as ua, buildDockerImage as ui, AWS_CREDENTIAL_ENV_KEYS as un, STATE_SOURCED_CROSS_GENERATION_RULES as ur, clearOnUpdateRemoval as ut, isSecretBearingReferenceString as v, resetAwsClients as va, getDockerCmd as vi, UNREADABLE_RESOURCES_MAP_ROW as vn, isSingleDynamicReferenceToken as vr, interruptWatchListenerCount as vt, reachableDivergentParameters as w, CrossAccountSecretRefusalError as wa, stripControlChars as wi, malformedExportNamesWarning as wn, redactSecretsForState as wr, UNSPECIFIED_SKIP_REASON as wt, bindingSkippedOutputs as x, AssetError as xa, runDockerForeground as xi, hasReadableOutputs as xn, maskSecretsInText as xr, CloudControlProvider as xt, keptWholeReasonText as y, runWithStackAwsClients as ya, partitionSensitiveEnv as yi, displayLogicalId as yn, markSameGenerationBag as yr, isInterruptedWaitError as yt, isStatefulRecreateTargetForReplace as z, StackTerminationProtectionError as za, warnDeprecatedNoPrefixCliFlag as zi, refuseMalformedOrphans as zn, hasUnverifiableParameterRefusal as zr, refStateLookupFromResource as zt };
51284
+ //# sourceMappingURL=deploy-engine-TkL9nyq8.js.map