@go-to-k/cdkd 0.290.24 → 0.290.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { C as displaySafe, S as displayIdent, b as STACK_REF_MAX_CODE_POINTS, d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName, w as truncateCodePoints, x as UNRENDERABLE, y as withoutGeneratedFallbackName } from "./logger-CkHVgzLx.js";
2
2
  import { a as setAssumedRoleCredentials, i as getPreAssumeEnvCredentials, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-BoIxyCir.js";
3
- import { t as getCdkdVersion } from "./version-PsB8OjhM.js";
3
+ import { t as getCdkdVersion } from "./version-yD49aCCv.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";
@@ -59,6 +59,191 @@ var __exportAll = (all, no_symbols) => {
59
59
  return target;
60
60
  };
61
61
 
62
+ //#endregion
63
+ //#region src/utils/aws-failure-text.ts
64
+ /**
65
+ * Appended to a redacted summary so the withheld half is still reachable.
66
+ *
67
+ * EXPORTED for the TESTS, and that is now its only reason. It was exported
68
+ * because `cloud-control-provider.ts`'s `abandonWait` reduced on its own wider
69
+ * predicate and had to compose this sentence by hand; issue
70
+ * [#3297](https://github.com/go-to-k/cdkd/issues/3297) removed that predicate
71
+ * and the hand-copy with it, so no `src/` file spells this literal twice. The
72
+ * remaining importer is `cloud-control-wait-abandoned.test.ts`, which asserts
73
+ * the ABSENCE of the pointer and would silently stop discriminating if it
74
+ * hard-coded the words instead.
75
+ */
76
+ const VERBOSE_POINTER = "Re-run with --verbose for AWS's own message.";
77
+ /**
78
+ * The summary {@link describeAwsFailure} builds for an `Error` it is reducing.
79
+ *
80
+ * UN-exported by issue [#3297](https://github.com/go-to-k/cdkd/issues/3297).
81
+ * It was exported for one caller — `cloud-control-provider.ts`, whose own
82
+ * authorship predicate was WIDER than this module's, so the helper handed it
83
+ * raw text for a shape it wanted reduced and it had to build this string
84
+ * itself. That predicate is gone and so is the divergence, leaving the export
85
+ * with no callers and a doc citing one that no longer exists. Keep it private:
86
+ * a re-export is the signal that a second predicate has appeared.
87
+ */
88
+ function redactedAwsFailureSummary(error) {
89
+ return `${error.name || "Error"}. ${VERBOSE_POINTER}`;
90
+ }
91
+ /**
92
+ * Whether AWS wrote this failure's message.
93
+ *
94
+ * Keyed on a real SERVICE signal: `$fault`, which every modeled
95
+ * `@smithy/smithy-client` `ServiceException` carries, or a NUMERIC
96
+ * `$metadata.httpStatusCode`, which only a deserialized RESPONSE has. Nothing
97
+ * under `src/` sets either, so a match cannot be a cdkd-authored error.
98
+ *
99
+ * **`$metadata` PRESENCE is deliberately NOT the signal, and an earlier
100
+ * revision of this function used it** (issue
101
+ * [#3297](https://github.com/go-to-k/cdkd/issues/3297)). `@smithy/core`'s retry
102
+ * middleware stamps `$metadata = {attempts, totalRetryDelay}` onto EVERY error
103
+ * it gives up on — socket errors included — and creates the object when it is
104
+ * absent. Measured against a real `CloudControlClient` pointed at a closed
105
+ * port:
106
+ *
107
+ * name 'Error', code 'ECONNREFUSED', $fault undefined,
108
+ * message 'connect ECONNREFUSED 127.0.0.1:1',
109
+ * $metadata { attempts: 3, totalRetryDelay: 58 }
110
+ *
111
+ * So the old predicate classified a plain transport failure as AWS-authored and
112
+ * reduced it to its `name` — which for a socket error is the bare token
113
+ * `Error`, carrying no diagnosis at all. That deleted `connect ECONNREFUSED
114
+ * <ip>:443`, the exact wording issue
115
+ * [#3236](https://github.com/go-to-k/cdkd/issues/3236) was REPORTED with, from
116
+ * the only durable record of the outage.
117
+ *
118
+ * The `CredentialsProviderError` name is the third arm because credential
119
+ * resolution carries NEITHER signal — identity is resolved by
120
+ * `httpAuthSchemeMiddleware` at step `serialize`, which WRAPS retry's
121
+ * `finalizeRequest`, so an escaping credential error never enters retry's catch
122
+ * (measured, and the mechanism is why: retry CREATES `$metadata` when absent,
123
+ * so an error arriving without one proves it bypassed retry). It is also the
124
+ * shape whose message carries the most: `@aws-sdk/credential-provider-process`
125
+ * wraps EVERY exec failure in it, so the text interpolates the helper's ARGV
126
+ * and its stderr.
127
+ *
128
+ * **Its siblings are deliberately NOT matched, and the reasons differ per class
129
+ * rather than being one rule.** `@smithy/property-provider` exports a family of
130
+ * three — `ProviderError`, and its subclasses `CredentialsProviderError` and
131
+ * `TokenProviderError` — plus `@smithy/credential-provider-imds`'s
132
+ * `InstanceMetadataV1FallbackError`, the only subclass OF
133
+ * `CredentialsProviderError` in the installed tree. (All four set `name`; an
134
+ * earlier revision read "the only subclass that overrides `name`", which is
135
+ * false of the three siblings.) Each was read rather than reasoned about,
136
+ * because the answers go BOTH ways:
137
+ *
138
+ * - `TokenProviderError` must NOT be reduced. Its message IS the remedy —
139
+ * `Token is expired. To refresh this SSO session run 'aws sso login' with
140
+ * the corresponding profile.` — so reducing it to a wire name would delete
141
+ * the fix instruction. A revision matching the whole `*ProviderError` suffix
142
+ * did exactly that. The benefit is not realized on the Cloud Control poll
143
+ * path today, and claiming otherwise was measured false:
144
+ * `@aws-sdk/credential-provider-sso` catches every token failure and
145
+ * rethrows it as a `CredentialsProviderError`, so the shape cannot escape a
146
+ * SigV4 client there. The carve-out is a rule about the FAMILY, kept because
147
+ * the rewrap is upstream behaviour that can change.
148
+ * - `ProviderError` (the base) and `InstanceMetadataV1FallbackError` carry
149
+ * connectivity and CONFIG wording respectively — the IMDS one interpolates
150
+ * three fixed literals naming config keys, with no argv, stderr, profile
151
+ * value or identity in it. Both are more useful raw.
152
+ *
153
+ * So string EQUALITY is right here, and right by enumeration rather than by
154
+ * assumption. Re-check the list when the SDK major moves; do not widen it to a
155
+ * suffix. The specific mechanism to re-check is `ProviderError.from()`, which
156
+ * does `Object.assign(new this(...), error)` — that copies a SOURCE error's own
157
+ * `name` over the class field and would defeat string equality outright. It has
158
+ * ZERO call sites anywhere in `node_modules` today, which is the only reason
159
+ * equality is safe rather than merely correct-looking.
160
+ *
161
+ * `$response` is gone with the `$metadata` disjunct, and the reason is NOT the
162
+ * obvious one -- an earlier revision of this comment said it "is only ever set
163
+ * on a `ServiceException`, which carries `$fault` anyway", and review measured
164
+ * that false. `@smithy/core`'s `deserializerMiddleware` stamps `$response` on
165
+ * ANY error escaping the deserializer, and the very next line reads
166
+ * `if (!("$metadata" in error))`, i.e. the library explicitly handles the
167
+ * `$response`-without-`$metadata` shape (a `JSON.parse` `SyntaxError` out of
168
+ * `parseJsonBody`). What actually preserves the verdict is the SIBLING line in
169
+ * that same catch: `error.$metadata = { httpStatusCode: response.statusCode,
170
+ * ... }`, so the `httpStatusCode` disjunct subsumes `$response` for every
171
+ * response the middleware can read. The residual is a `response` failing
172
+ * `HttpResponse.isInstance`, or a throw inside that block's own
173
+ * `catch (ignored)` -- shapes with no HTTP status at all, which are not service
174
+ * rejections and should not be reduced.
175
+ *
176
+ * A transport-level failure (a socket timeout, a DNS error) reaches a caller
177
+ * with neither signal, and that is the correct answer rather than a gap: those
178
+ * messages are written by the HTTP layer and name a host, never a caller.
179
+ */
180
+ function isAwsAuthoredFailure(error) {
181
+ const candidate = error;
182
+ return candidate.$fault !== void 0 || typeof candidate.$metadata?.httpStatusCode === "number" || error.name === "CredentialsProviderError";
183
+ }
184
+ /**
185
+ * Exactly what `String(value)` produces, without the throw.
186
+ *
187
+ * `String(value)` is the only spelling that carries an `Error`'s NAME as well
188
+ * as its message (`"AccessDenied: User ... is not authorized"`), and several
189
+ * call sites depend on that: four build a persisted `outcome: 'partial'`
190
+ * reason where the wire code is the discriminator an operator reads first.
191
+ * {@link describeAwsFailure}'s `detail` is the MESSAGE only, so it is NOT a
192
+ * drop-in for the bare form -- substituting it silently deletes the code.
193
+ * Measured, after a sweep did exactly that at nine sites.
194
+ *
195
+ * So this is the 1:1 replacement: same output for every value `String` can
196
+ * convert, and a stable sentence for the ones it cannot. A null-prototype
197
+ * object throws `TypeError: Cannot convert object to primitive value`, and an
198
+ * object with a hostile or `null` `toString` throws whatever it likes -- inside
199
+ * a catch, that replaces the failure being reported.
200
+ *
201
+ * Use `describeAwsFailure(x).detail` where the site previously read
202
+ * `x instanceof Error ? x.message : String(x)`; use THIS where it read a bare
203
+ * `String(x)`.
204
+ *
205
+ * NOT a substitute for `displaySafe` in `display-safe.ts`, which guards the
206
+ * same coercion and answers differently on purpose -- it falls back to
207
+ * `Object.prototype.toString` and, more importantly, SANITISES the result for a
208
+ * terminal. Anything about to be rendered or logged goes through that one; this
209
+ * one is for the text a catch is about to record.
210
+ */
211
+ function safeStringify$1(value) {
212
+ try {
213
+ return String(value);
214
+ } catch {
215
+ return "a value that could not be converted to text";
216
+ }
217
+ }
218
+ /**
219
+ * Describe a caught failure for a thrown message. See {@link AwsFailureText}.
220
+ */
221
+ function describeAwsFailure(error) {
222
+ if (error instanceof Error) {
223
+ if (!isAwsAuthoredFailure(error)) return {
224
+ summary: error.message,
225
+ detail: error.message,
226
+ redacted: false
227
+ };
228
+ return {
229
+ summary: redactedAwsFailureSummary(error),
230
+ detail: error.message,
231
+ redacted: true
232
+ };
233
+ }
234
+ let detail;
235
+ try {
236
+ detail = String(error);
237
+ } catch {
238
+ detail = "a value that could not be converted to text";
239
+ }
240
+ return {
241
+ summary: `a non-Error value of type ${typeof error}. ${VERBOSE_POINTER}`,
242
+ detail,
243
+ redacted: true
244
+ };
245
+ }
246
+
62
247
  //#endregion
63
248
  //#region src/deployment/retryable-errors.ts
64
249
  /**
@@ -722,7 +907,7 @@ function isRetryableTransientError(error, message) {
722
907
  * classify.
723
908
  */
724
909
  function retryClassificationText(error) {
725
- const top = error instanceof Error ? error.message : String(error);
910
+ const top = describeAwsFailure(error).detail;
726
911
  if (!hasRedactedCause(error)) return top;
727
912
  const parts = [];
728
913
  const seen = /* @__PURE__ */ new Set();
@@ -7360,7 +7545,7 @@ function repairSpawnRefusal(text, args, maskedArgs) {
7360
7545
  * error would only say "Command failed with exit code N".
7361
7546
  */
7362
7547
  function describeDockerExecFailure(error, args) {
7363
- const message = thrownMessageText(error) || safeStringify$1(error);
7548
+ const message = thrownMessageText(error) || safeStringify(error);
7364
7549
  const stderrText = capturedStreamText(error, "stderr");
7365
7550
  return redactDockerArgvInText(stderrText ? `${message}\n${stderrText}` : message, args);
7366
7551
  }
@@ -7403,7 +7588,7 @@ function thrownMessageText(error) {
7403
7588
  * two of these call sites are in `cleanupEcsRun`, where an exception aborts
7404
7589
  * the remaining volume / network teardown and leaks real Docker resources.
7405
7590
  */
7406
- function safeStringify$1(error) {
7591
+ function safeStringify(error) {
7407
7592
  try {
7408
7593
  return String(error);
7409
7594
  } catch {
@@ -7429,7 +7614,7 @@ function safeStringify$1(error) {
7429
7614
  * wrapper text needs the status as well.
7430
7615
  */
7431
7616
  function describeDockerFailure(error, args) {
7432
- return redactDockerArgvInText(capturedStreamText(error, "stderr") || thrownMessageText(error) || safeStringify$1(error), args);
7617
+ return redactDockerArgvInText(capturedStreamText(error, "stderr") || thrownMessageText(error) || safeStringify(error), args);
7433
7618
  }
7434
7619
  /**
7435
7620
  * A `cause` that is safe to CHAIN onto a wrapper error, for a caller that
@@ -7896,191 +8081,6 @@ var DockerAssetPublisher = class {
7896
8081
  }
7897
8082
  };
7898
8083
 
7899
- //#endregion
7900
- //#region src/utils/aws-failure-text.ts
7901
- /**
7902
- * Appended to a redacted summary so the withheld half is still reachable.
7903
- *
7904
- * EXPORTED for the TESTS, and that is now its only reason. It was exported
7905
- * because `cloud-control-provider.ts`'s `abandonWait` reduced on its own wider
7906
- * predicate and had to compose this sentence by hand; issue
7907
- * [#3297](https://github.com/go-to-k/cdkd/issues/3297) removed that predicate
7908
- * and the hand-copy with it, so no `src/` file spells this literal twice. The
7909
- * remaining importer is `cloud-control-wait-abandoned.test.ts`, which asserts
7910
- * the ABSENCE of the pointer and would silently stop discriminating if it
7911
- * hard-coded the words instead.
7912
- */
7913
- const VERBOSE_POINTER = "Re-run with --verbose for AWS's own message.";
7914
- /**
7915
- * The summary {@link describeAwsFailure} builds for an `Error` it is reducing.
7916
- *
7917
- * UN-exported by issue [#3297](https://github.com/go-to-k/cdkd/issues/3297).
7918
- * It was exported for one caller — `cloud-control-provider.ts`, whose own
7919
- * authorship predicate was WIDER than this module's, so the helper handed it
7920
- * raw text for a shape it wanted reduced and it had to build this string
7921
- * itself. That predicate is gone and so is the divergence, leaving the export
7922
- * with no callers and a doc citing one that no longer exists. Keep it private:
7923
- * a re-export is the signal that a second predicate has appeared.
7924
- */
7925
- function redactedAwsFailureSummary(error) {
7926
- return `${error.name || "Error"}. ${VERBOSE_POINTER}`;
7927
- }
7928
- /**
7929
- * Whether AWS wrote this failure's message.
7930
- *
7931
- * Keyed on a real SERVICE signal: `$fault`, which every modeled
7932
- * `@smithy/smithy-client` `ServiceException` carries, or a NUMERIC
7933
- * `$metadata.httpStatusCode`, which only a deserialized RESPONSE has. Nothing
7934
- * under `src/` sets either, so a match cannot be a cdkd-authored error.
7935
- *
7936
- * **`$metadata` PRESENCE is deliberately NOT the signal, and an earlier
7937
- * revision of this function used it** (issue
7938
- * [#3297](https://github.com/go-to-k/cdkd/issues/3297)). `@smithy/core`'s retry
7939
- * middleware stamps `$metadata = {attempts, totalRetryDelay}` onto EVERY error
7940
- * it gives up on — socket errors included — and creates the object when it is
7941
- * absent. Measured against a real `CloudControlClient` pointed at a closed
7942
- * port:
7943
- *
7944
- * name 'Error', code 'ECONNREFUSED', $fault undefined,
7945
- * message 'connect ECONNREFUSED 127.0.0.1:1',
7946
- * $metadata { attempts: 3, totalRetryDelay: 58 }
7947
- *
7948
- * So the old predicate classified a plain transport failure as AWS-authored and
7949
- * reduced it to its `name` — which for a socket error is the bare token
7950
- * `Error`, carrying no diagnosis at all. That deleted `connect ECONNREFUSED
7951
- * <ip>:443`, the exact wording issue
7952
- * [#3236](https://github.com/go-to-k/cdkd/issues/3236) was REPORTED with, from
7953
- * the only durable record of the outage.
7954
- *
7955
- * The `CredentialsProviderError` name is the third arm because credential
7956
- * resolution carries NEITHER signal — identity is resolved by
7957
- * `httpAuthSchemeMiddleware` at step `serialize`, which WRAPS retry's
7958
- * `finalizeRequest`, so an escaping credential error never enters retry's catch
7959
- * (measured, and the mechanism is why: retry CREATES `$metadata` when absent,
7960
- * so an error arriving without one proves it bypassed retry). It is also the
7961
- * shape whose message carries the most: `@aws-sdk/credential-provider-process`
7962
- * wraps EVERY exec failure in it, so the text interpolates the helper's ARGV
7963
- * and its stderr.
7964
- *
7965
- * **Its siblings are deliberately NOT matched, and the reasons differ per class
7966
- * rather than being one rule.** `@smithy/property-provider` exports a family of
7967
- * three — `ProviderError`, and its subclasses `CredentialsProviderError` and
7968
- * `TokenProviderError` — plus `@smithy/credential-provider-imds`'s
7969
- * `InstanceMetadataV1FallbackError`, the only subclass OF
7970
- * `CredentialsProviderError` in the installed tree. (All four set `name`; an
7971
- * earlier revision read "the only subclass that overrides `name`", which is
7972
- * false of the three siblings.) Each was read rather than reasoned about,
7973
- * because the answers go BOTH ways:
7974
- *
7975
- * - `TokenProviderError` must NOT be reduced. Its message IS the remedy —
7976
- * `Token is expired. To refresh this SSO session run 'aws sso login' with
7977
- * the corresponding profile.` — so reducing it to a wire name would delete
7978
- * the fix instruction. A revision matching the whole `*ProviderError` suffix
7979
- * did exactly that. The benefit is not realized on the Cloud Control poll
7980
- * path today, and claiming otherwise was measured false:
7981
- * `@aws-sdk/credential-provider-sso` catches every token failure and
7982
- * rethrows it as a `CredentialsProviderError`, so the shape cannot escape a
7983
- * SigV4 client there. The carve-out is a rule about the FAMILY, kept because
7984
- * the rewrap is upstream behaviour that can change.
7985
- * - `ProviderError` (the base) and `InstanceMetadataV1FallbackError` carry
7986
- * connectivity and CONFIG wording respectively — the IMDS one interpolates
7987
- * three fixed literals naming config keys, with no argv, stderr, profile
7988
- * value or identity in it. Both are more useful raw.
7989
- *
7990
- * So string EQUALITY is right here, and right by enumeration rather than by
7991
- * assumption. Re-check the list when the SDK major moves; do not widen it to a
7992
- * suffix. The specific mechanism to re-check is `ProviderError.from()`, which
7993
- * does `Object.assign(new this(...), error)` — that copies a SOURCE error's own
7994
- * `name` over the class field and would defeat string equality outright. It has
7995
- * ZERO call sites anywhere in `node_modules` today, which is the only reason
7996
- * equality is safe rather than merely correct-looking.
7997
- *
7998
- * `$response` is gone with the `$metadata` disjunct, and the reason is NOT the
7999
- * obvious one -- an earlier revision of this comment said it "is only ever set
8000
- * on a `ServiceException`, which carries `$fault` anyway", and review measured
8001
- * that false. `@smithy/core`'s `deserializerMiddleware` stamps `$response` on
8002
- * ANY error escaping the deserializer, and the very next line reads
8003
- * `if (!("$metadata" in error))`, i.e. the library explicitly handles the
8004
- * `$response`-without-`$metadata` shape (a `JSON.parse` `SyntaxError` out of
8005
- * `parseJsonBody`). What actually preserves the verdict is the SIBLING line in
8006
- * that same catch: `error.$metadata = { httpStatusCode: response.statusCode,
8007
- * ... }`, so the `httpStatusCode` disjunct subsumes `$response` for every
8008
- * response the middleware can read. The residual is a `response` failing
8009
- * `HttpResponse.isInstance`, or a throw inside that block's own
8010
- * `catch (ignored)` -- shapes with no HTTP status at all, which are not service
8011
- * rejections and should not be reduced.
8012
- *
8013
- * A transport-level failure (a socket timeout, a DNS error) reaches a caller
8014
- * with neither signal, and that is the correct answer rather than a gap: those
8015
- * messages are written by the HTTP layer and name a host, never a caller.
8016
- */
8017
- function isAwsAuthoredFailure(error) {
8018
- const candidate = error;
8019
- return candidate.$fault !== void 0 || typeof candidate.$metadata?.httpStatusCode === "number" || error.name === "CredentialsProviderError";
8020
- }
8021
- /**
8022
- * Exactly what `String(value)` produces, without the throw.
8023
- *
8024
- * `String(value)` is the only spelling that carries an `Error`'s NAME as well
8025
- * as its message (`"AccessDenied: User ... is not authorized"`), and several
8026
- * call sites depend on that: four build a persisted `outcome: 'partial'`
8027
- * reason where the wire code is the discriminator an operator reads first.
8028
- * {@link describeAwsFailure}'s `detail` is the MESSAGE only, so it is NOT a
8029
- * drop-in for the bare form -- substituting it silently deletes the code.
8030
- * Measured, after a sweep did exactly that at nine sites.
8031
- *
8032
- * So this is the 1:1 replacement: same output for every value `String` can
8033
- * convert, and a stable sentence for the ones it cannot. A null-prototype
8034
- * object throws `TypeError: Cannot convert object to primitive value`, and an
8035
- * object with a hostile or `null` `toString` throws whatever it likes -- inside
8036
- * a catch, that replaces the failure being reported.
8037
- *
8038
- * Use `describeAwsFailure(x).detail` where the site previously read
8039
- * `x instanceof Error ? x.message : String(x)`; use THIS where it read a bare
8040
- * `String(x)`.
8041
- *
8042
- * NOT a substitute for `displaySafe` in `display-safe.ts`, which guards the
8043
- * same coercion and answers differently on purpose -- it falls back to
8044
- * `Object.prototype.toString` and, more importantly, SANITISES the result for a
8045
- * terminal. Anything about to be rendered or logged goes through that one; this
8046
- * one is for the text a catch is about to record.
8047
- */
8048
- function safeStringify(value) {
8049
- try {
8050
- return String(value);
8051
- } catch {
8052
- return "a value that could not be converted to text";
8053
- }
8054
- }
8055
- /**
8056
- * Describe a caught failure for a thrown message. See {@link AwsFailureText}.
8057
- */
8058
- function describeAwsFailure(error) {
8059
- if (error instanceof Error) {
8060
- if (!isAwsAuthoredFailure(error)) return {
8061
- summary: error.message,
8062
- detail: error.message,
8063
- redacted: false
8064
- };
8065
- return {
8066
- summary: redactedAwsFailureSummary(error),
8067
- detail: error.message,
8068
- redacted: true
8069
- };
8070
- }
8071
- let detail;
8072
- try {
8073
- detail = String(error);
8074
- } catch {
8075
- detail = "a value that could not be converted to text";
8076
- }
8077
- return {
8078
- summary: `a non-Error value of type ${typeof error}. ${VERBOSE_POINTER}`,
8079
- detail,
8080
- redacted: true
8081
- };
8082
- }
8083
-
8084
8084
  //#endregion
8085
8085
  //#region src/utils/deny-external-access-policy.ts
8086
8086
  /**
@@ -9534,6 +9534,44 @@ function isReadableBag(container) {
9534
9534
  return typeof container === "object" && container !== null && !Array.isArray(container);
9535
9535
  }
9536
9536
  /**
9537
+ * What KIND of value a record's `region` field holds, as a bounded token safe
9538
+ * to print at default verbosity (issue
9539
+ * [#3328](https://github.com/go-to-k/cdkd/issues/3328)).
9540
+ *
9541
+ * The VALUE is never safe there: a body region is chosen by anyone able to
9542
+ * write that key, and a line reading `body region 'eu-west-1'` hands an
9543
+ * operator a region to aim a destructive re-run at — the misdirection half of
9544
+ * that issue. Its kind is the answer to "what is wrong with this record"
9545
+ * without being the answer to "which region should I use", and the result is
9546
+ * one of TEN fixed strings (`null`, `an array`, and an article plus each of
9547
+ * `typeof`'s eight), so no input can forge a line through it.
9548
+ *
9549
+ * `probeLegacyState` reaches the same JUDGEMENT one field over and does NOT
9550
+ * call this — it renders a bare `typeof` into a sentence a unit case pins
9551
+ * verbatim (`'region' is number, not a string`). Two spellings of a bounded
9552
+ * token, deliberately not collapsed: the two are not interchangeable strings,
9553
+ * and changing that one is a message change with its own fence.
9554
+ *
9555
+ * `null` and arrays are named rather than left as `object`, the way
9556
+ * `parseStateBody` names its own refused roots — `typeof null` is `'object'`,
9557
+ * which would report the two most likely hand-edit shapes as the same thing.
9558
+ * The article is computed because `a object` reads as a defect in the very
9559
+ * diagnostic the reader is being asked to trust.
9560
+ *
9561
+ * HOME: this module rather than beside either caller, for the reason
9562
+ * {@link isReadableBag}'s note gives — `src/state/s3-state-backend.ts`
9563
+ * (`adoptKeyRegion`, which produces the divergence) and
9564
+ * `src/state/malformed-resources-bag.ts` (the destroy refusal that renders it)
9565
+ * both need it, and the second is imported by modules that `vi.mock` the
9566
+ * first wholesale.
9567
+ */
9568
+ function describeRegionValueKind(value) {
9569
+ if (value === null) return "null";
9570
+ if (Array.isArray(value)) return "an array";
9571
+ const type = typeof value;
9572
+ return `${/^[aeiou]/.test(type) ? "an" : "a"} ${type}`;
9573
+ }
9574
+ /**
9537
9575
  * The keys of `state.outputs` an `Fn::ImportValue` may bind to (issue
9538
9576
  * [#2193](https://github.com/go-to-k/cdkd/issues/2193)) — THE predicate
9539
9577
  * behind "what does this stack export". Four readers used to answer that
@@ -10287,6 +10325,11 @@ var S3StateBackend = class {
10287
10325
  *
10288
10326
  * Note: S3 returns ETag with surrounding quotes (e.g., `"abc123"`). We
10289
10327
  * preserve the quotes — they are required for `IfMatch` conditions.
10328
+ *
10329
+ * On the region-scoped path the returned `state.region` is ALWAYS the
10330
+ * region of the key it was read from, whatever the body said — see
10331
+ * {@link adoptKeyRegion}, which also reports a body that disagreed via
10332
+ * `divergentBodyRegion`.
10290
10333
  */
10291
10334
  async getState(stackName, region) {
10292
10335
  await this.ensureClientForBucket();
@@ -10303,11 +10346,13 @@ var S3StateBackend = class {
10303
10346
  if (!response.Body) throw new StateError(`State file for stack '${shownStackName}' (${shownRegionName}) has no body`);
10304
10347
  if (!response.ETag) throw new StateError(`State file for stack '${shownStackName}' (${shownRegionName}) has no ETag`);
10305
10348
  const bodyString = await response.Body.transformToString();
10306
- const state = this.parseStateBody(bodyString, stackName);
10349
+ const parsed = this.parseStateBody(bodyString, stackName);
10350
+ const { state, divergentBodyRegion } = this.adoptKeyRegion(parsed, region, shownStackName, shownRegionName);
10307
10351
  this.logger.debug(`Retrieved state: ${shownStackName} (${shownRegionName}), ETag: ${response.ETag}`);
10308
10352
  return {
10309
10353
  state,
10310
- etag: response.ETag
10354
+ etag: response.ETag,
10355
+ ...divergentBodyRegion !== void 0 && { divergentBodyRegion }
10311
10356
  };
10312
10357
  } catch (error) {
10313
10358
  if (!isNoSuchKey(error)) {
@@ -10869,6 +10914,83 @@ var S3StateBackend = class {
10869
10914
  displayName(stackName) {
10870
10915
  return displaySafe(stackName, { asciiOnly: true }) || "<unrenderable>";
10871
10916
  }
10917
+ /**
10918
+ * Make the S3 KEY's region the region of the record read from it, and report
10919
+ * a body that named a different one.
10920
+ *
10921
+ * The key is the authority, and that is not a new rule: `saveState`
10922
+ * normalizes the body it writes against the same `region` argument
10923
+ * `getStateKey` builds the key from, and has done since `83e5ddb66` — the
10924
+ * commit that introduced the region-scoped layout (schema v2, PR
10925
+ * [#57](https://github.com/go-to-k/cdkd/pull/57)). Key and body therefore
10926
+ * agree by construction for every record any cdkd version has ever written,
10927
+ * so this makes the READ symmetric with a write rule that already held
10928
+ * rather than inventing a second one — and a divergence means the record was
10929
+ * written by something that is not cdkd.
10930
+ *
10931
+ * It is not cosmetic. `region` is what `destroy-runner.ts` locks, saves and
10932
+ * `deleteState`s against (`state.region ?? ctx.baseRegion`), and what
10933
+ * `DeleteContext.expectedRegion` carries so a provider can tell "the
10934
+ * resource is genuinely gone" from "I looked in the wrong region"
10935
+ * (`src/provisioning/region-check.ts`). A divergent body aimed all of that
10936
+ * at a region the record does not live in, and `cdkd state destroy` reported
10937
+ * `✓ State deleted` having deleted nothing (issue
10938
+ * [#3328](https://github.com/go-to-k/cdkd/issues/3328)). Normalizing HERE
10939
+ * fixes every consumer at once, with no region threaded through any of them.
10940
+ *
10941
+ * REFUSING instead was rejected deliberately: this is the shared read path
10942
+ * for deploy, destroy, rollback AND the recovery commands (`cdkd state
10943
+ * orphan`, `cdkd state destroy`), which is the same reason `parseStateBody`
10944
+ * validates nothing inside a well-formed root. A refusal would strand the
10945
+ * record it was asked to clean up, leaving `aws s3 rm` as the only remedy.
10946
+ *
10947
+ * It REPLACES rather than defaults, which is the half `??` cannot do:
10948
+ * `state.region ?? ctx.baseRegion` falls through only for `null` /
10949
+ * `undefined`, so a `"region": ""` built the key `cdkd/<stack>//state.json`
10950
+ * and a `"region": 123` reached `acquireLock` / `deleteState` as a region.
10951
+ *
10952
+ * **An ABSENT region is normalized too, and silently.** A body carrying no
10953
+ * `region` makes no claim to contradict, and the key it was read from names
10954
+ * one — which is the answer `legacyProbeBelongsTo` already gives one layout
10955
+ * over, where a body naming no region belongs to ANY region (issue
10956
+ * [#2550](https://github.com/go-to-k/cdkd/issues/2550)): "any region" plus
10957
+ * "read under this key" is this key's region. It is also strictly better than
10958
+ * what absence used to reach, which was `ctx.baseRegion` at the destroy —
10959
+ * the CLI's region, which need not be the key's at all.
10960
+ *
10961
+ * **`null` and `''` are NOT folded in with absent here, unlike in
10962
+ * `legacyProbeBelongsTo`**, and the difference is the key: the legacy key
10963
+ * names no region, so a falsy field there has to mean "any". This key names
10964
+ * one, so nothing needs inferring and the only question left is whether the
10965
+ * body AGREES — which `null` and `''` do not. They are also two of the shapes
10966
+ * `export.ts`'s nested-child refusal keys on, and folding them in would drop
10967
+ * that refusal silently.
10968
+ *
10969
+ * The divergent value is REPORTED to the caller rather than only logged
10970
+ * because one caller refuses on it: `walkCdkdStateStackTree` (`export.ts`)
10971
+ * fails a nested child whose recorded region is not its parent's, and
10972
+ * normalizing the state without reporting would have made that refusal
10973
+ * unreachable rather than fixed.
10974
+ */
10975
+ adoptKeyRegion(state, region, shownStackName, shownRegionName) {
10976
+ const raw = state.region;
10977
+ if (raw === region) return { state };
10978
+ if (raw === void 0) return { state: {
10979
+ ...state,
10980
+ region
10981
+ } };
10982
+ const kind = describeRegionValueKind(raw);
10983
+ this.logger.warn(`State record for stack '${shownStackName}' carries a 'region' of its own (${kind}) that is not the region of the S3 key it was read from ('${shownRegionName}'). cdkd stamps the key's region into every record it writes, so this record was not written by cdkd. Continuing against '${shownRegionName}' — the key is authoritative. Re-run with --verbose to see the recorded value, where it has one to show.`);
10984
+ const shownBodyRegion = truncateCodePoints(displaySafe(raw, { asciiOnly: true }), 255);
10985
+ this.logger.debug(`State record for stack '${shownStackName}' carries body region ` + (shownBodyRegion.text === "" ? `${kind}, which renders as nothing — its kind above is the whole of it.` : `'${shownBodyRegion.text}'${shownBodyRegion.truncated ? " [cut]" : ""}.`));
10986
+ return {
10987
+ state: {
10988
+ ...state,
10989
+ region
10990
+ },
10991
+ divergentBodyRegion: raw
10992
+ };
10993
+ }
10872
10994
  async probeLegacyState(stackName) {
10873
10995
  try {
10874
10996
  const response = await this.s3Client.send(new GetObjectCommand({
@@ -10957,6 +11079,15 @@ var S3StateBackend = class {
10957
11079
  this.logger.debug(`Legacy state for stack '${this.displayName(stackName)}' has region '${displaySafe(state.region, { asciiOnly: true }) || "<unrenderable>"}', not '${displaySafe(region, { asciiOnly: true }) || "<unrenderable>"}' — skipping legacy fallback.`);
10958
11080
  return null;
10959
11081
  }
11082
+ const legacyRegion = state.region;
11083
+ if (legacyRegion !== void 0 && (typeof legacyRegion !== "string" || legacyRegion === "")) {
11084
+ this.logger.debug(`Legacy state for stack '${this.displayName(stackName)}' names no usable region (${describeRegionValueKind(legacyRegion)}); reading it as region-less.`);
11085
+ const { region: _dropped, ...withoutRegion } = state;
11086
+ return {
11087
+ state: withoutRegion,
11088
+ etag: response.ETag
11089
+ };
11090
+ }
10960
11091
  return {
10961
11092
  state,
10962
11093
  etag: response.ETag
@@ -17847,7 +17978,7 @@ async function withRetry(operation, logicalId, opts = {}) {
17847
17978
  return await operation();
17848
17979
  } catch (error) {
17849
17980
  lastError = error;
17850
- const message = error instanceof Error ? error.message : String(error);
17981
+ const message = describeAwsFailure(error).detail;
17851
17982
  const classifyText = retryClassificationText(error);
17852
17983
  if (isMarkedNonRetryable(error)) throw error;
17853
17984
  const retryable = opts.isRetryable ? opts.isRetryable(classifyText, error) : isRetryableTransientError(error, classifyText);
@@ -20971,18 +21102,26 @@ function malformedStateRefusalMessage(stackName, region) {
20971
21102
  * **That remedy is a TEMPLATE, not a substituted command, and the asymmetry
20972
21103
  * with the `cdkd state show` line above it is the point.** `state orphan`
20973
21104
  * DELETES a record; `state show` reads one. The `region` this function is
20974
- * handed comes from `destroy-runner.ts`'s
20975
- * `state.region ?? ctx.baseRegion`, and `state.region` is RECORD-BODY content
20976
- * that `getState` does not check against the key it loaded from — measured
20977
- * 2026-09-17: a record planted at `.../us-east-1/state.json` carrying
20978
- * `"region": "eu-west-1"` rendered a pasteable
20979
- * `cdkd state orphan <stack> --stack-region eu-west-1`, aiming a destructive
20980
- * command at a DIFFERENT region's record for the same stack. That is the
20981
- * misdirection class {@link stackClause} records for stack names, one field
20982
- * over. Substituting into the read-only `state show` line is the pre-existing
20983
- * behaviour of {@link malformedStateDetail} and is left alone; what this lane
20984
- * must not add is a destructive one. The divergence itself — the runner also
20985
- * LOCKS and DELETES against the body region — is go-to-k/cdkd#3328.
21105
+ * handed comes from `destroy-runner.ts`'s `state.region ?? ctx.baseRegion` —
21106
+ * which WAS record-body content `getState` did not check against the key it
21107
+ * loaded from, measured 2026-09-17: a record planted at
21108
+ * `.../us-east-1/state.json` carrying `"region": "eu-west-1"` rendered a
21109
+ * pasteable `cdkd state orphan <stack> --stack-region eu-west-1`, aiming a
21110
+ * destructive command at a DIFFERENT region's record for the same stack. That
21111
+ * is the misdirection class {@link stackClause} records for stack names, one
21112
+ * field over. Substituting into the read-only `state show` line is the
21113
+ * pre-existing behaviour of {@link malformedStateDetail} and is left alone;
21114
+ * what this lane must not add is a destructive one.
21115
+ *
21116
+ * **go-to-k/cdkd#3328 closed the BODY half of that, and the template stays
21117
+ * anyway** — the measurement above is now history, not a live repro.
21118
+ * `S3StateBackend.getState` normalizes a region-scoped record's `region` to
21119
+ * its KEY's and warns on a body that disagreed, so `state.region` reaching
21120
+ * this function is the key's region. But the key is an S3 KEY SEGMENT, which
21121
+ * is bucket-plantable in its own right (`cdkd/<stack>/<anything>/state.json`
21122
+ * lists as a region), and a legacy record still falls through to
21123
+ * `ctx.baseRegion`. So the value is still not this process's own, and a
21124
+ * DELETING command built from it is still the thing not to hand over.
20986
21125
  *
20987
21126
  * **A template is not enough on its own, because the name the reader would
20988
21127
  * type into it comes from the clause ABOVE.** `safeIdentifier` composes
@@ -21007,9 +21146,16 @@ function malformedStateRefusalMessage(stackName, region) {
21007
21146
  * "with the stack and region this message NAMES", which is the attacker's
21008
21147
  * region (review round 2 of go-to-k/cdkd#3332: templating had removed the
21009
21148
  * paste, not the aim). The arm therefore points at the record's S3 KEY, which
21010
- * `getState` resolved and a record body cannot forge, and says outright that
21011
- * the region printed above is body-derived and need not match it. The
21012
- * divergence is go-to-k/cdkd#3328.
21149
+ * `getState` resolved and a record body cannot forge.
21150
+ *
21151
+ * Since go-to-k/cdkd#3328 the printed region IS that key's — `getState`
21152
+ * normalizes a region-scoped record's `region` to the key's region and warns
21153
+ * on a body that disagreed — so the arm no longer has to warn the reader that
21154
+ * the two may differ, and its text no longer does. It still says WHERE the
21155
+ * region comes from and still sends the reader to `cdkd state list --long` to
21156
+ * confirm the key, because a key segment is itself bucket-plantable and
21157
+ * because a LEGACY record has no key region at all, in which case the value
21158
+ * printed is the CLI's own and `--stack-region` must be omitted.
21013
21159
  *
21014
21160
  * **The cap this arm measures against is `STACK_REF_MAX_CODE_POINTS`, not the
21015
21161
  * 128 every other text in this module uses**, and it is threaded rather than
@@ -21027,7 +21173,7 @@ function malformedStateRefusalMessage(stackName, region) {
21027
21173
  function malformedDestroyResourcesRefusalMessage(stackName, region) {
21028
21174
  const cap = STACK_REF_MAX_CODE_POINTS;
21029
21175
  const exact = safeIdentifier(stackName, cap) === stackName && safeIdentifier(region, cap) === region;
21030
- return `${exact ? malformedStateDetail(stackName, region, cap) : `${stackClause(void 0, void 0)} ${MALFORMED_RESOURCES_DIAGNOSIS}`} This command DELETES state, so it refuses rather than continuing: the resource map IS the list of what to delete, so an unreadable one counts as ZERO resources and the run takes the empty-stack fast path, which removes state.json and reports success while every resource the record named is still live in AWS and no longer referenced by anything. Reading the bag as EMPTY is that same outcome rather than an alternative to it, so there is no repair available here. Repair or remove the record first. ${exact ? "To drop the record deliberately and leave the live resources standing, run 'cdkd state orphan' against the stack and the region THE RECORD'S S3 KEY holds — spelled out rather than pasteable, because that command DELETES a record and the region printed above is read from the record's own BODY, which need not match its key. Confirm the key with 'cdkd state list --long' — a legacy record shows none, and for one of those the flag must be OMITTED or it selects nothing — then: cdkd state orphan <stack> --stack-region <region>" : `This record's stack name or region does NOT render exactly — what is printed above is a sanitized form, and another record may render identically — so this message names no target and offers no command against one. List the records as stored with 'cdkd state list --long', which prints a name needing sanitizing in quoted form, and act on the one whose key matches. Inspect it with: ${inspectCommand(void 0, void 0)}`}`;
21176
+ return `${exact ? malformedStateDetail(stackName, region, cap) : `${stackClause(void 0, void 0)} ${MALFORMED_RESOURCES_DIAGNOSIS}`} This command DELETES state, so it refuses rather than continuing: the resource map IS the list of what to delete, so an unreadable one counts as ZERO resources and the run takes the empty-stack fast path, which removes state.json and reports success while every resource the record named is still live in AWS and no longer referenced by anything. Reading the bag as EMPTY is that same outcome rather than an alternative to it, so there is no repair available here. Repair or remove the record first. ${exact ? "To drop the record deliberately and leave the live resources standing, run 'cdkd state orphan' against the stack and the region THE RECORD'S S3 KEY holds — spelled out rather than pasteable, because that command DELETES a record and the region printed above is the one cdkd was pointed at, not a value cdkd owns. Confirm the key with 'cdkd state list --long' — a legacy record shows none, and for one of those the flag must be OMITTED or it selects nothing — then: cdkd state orphan <stack> --stack-region <region>" : `This record's stack name or region does NOT render exactly — what is printed above is a sanitized form, and another record may render identically — so this message names no target and offers no command against one. List the records as stored with 'cdkd state list --long', which prints a name needing sanitizing in quoted form, and act on the one whose key matches. Inspect it with: ${inspectCommand(void 0, void 0)}`}`;
21031
21177
  }
21032
21178
  /**
21033
21179
  * For `cdkd destroy` / `cdkd state destroy`: refuse a record whose `resources`
@@ -21049,6 +21195,89 @@ function refuseMalformedResourcesForDestroy(state, stackName, region) {
21049
21195
  if (hasReadableResources(state)) return;
21050
21196
  throw markNonRetryable(new CdkdError(malformedDestroyResourcesRefusalMessage(stackName, region), STATE_RESOURCES_MALFORMED));
21051
21197
  }
21198
+ /** The code a destroy refusal over a body/key region divergence carries. */
21199
+ const STATE_REGION_DIVERGED = "STATE_REGION_DIVERGED";
21200
+ /**
21201
+ * The DESTROY refusal text for a record whose BODY names a different region
21202
+ * than the KEY it was read from, while it still lists resources (issue
21203
+ * [#3328](https://github.com/go-to-k/cdkd/issues/3328), review round 1).
21204
+ *
21205
+ * **This is the one case where adopting the key is not enough, and it is a
21206
+ * consequence of adopting it rather than an argument against.**
21207
+ * `S3StateBackend.getState` replaces the body's `region` with the key's, which
21208
+ * is right for the key math, the lock and the record's own identity — cdkd
21209
+ * stamps the key's region into every body it writes, so a divergence means
21210
+ * another writer. But WHERE THE RESOURCES ARE is a different question, and a
21211
+ * divergent record is the one shape that gives two answers to it. cdkd cannot
21212
+ * tell which half is honest:
21213
+ *
21214
+ * - the BODY lies (a planted field, the misdirection this issue is about) —
21215
+ * the resources are in the key's region and acting there is correct;
21216
+ * - the KEY lies (an `aws s3 cp` between region prefixes, a restore, a
21217
+ * third-party writer) — the resources are in the body's region, and acting
21218
+ * in the key's finds NOTHING.
21219
+ *
21220
+ * The second one is not a wash, it is silent data loss: `destroy-runner.ts`
21221
+ * reads a `*NotFound` from a provider as ALREADY DELETED (`deletedCount++`,
21222
+ * the row dropped from the record), so a whole stack of live resources would
21223
+ * be reported destroyed and `deleteState` would then remove the only record
21224
+ * naming them. `assertRegionMatch` — the guard that exists to stop exactly
21225
+ * that inference — cannot fire, because after normalization
21226
+ * `DeleteContext.expectedRegion` and the client region are the same value by
21227
+ * construction.
21228
+ *
21229
+ * So the refusal is narrow by design and its trigger is the CONJUNCTION:
21230
+ * divergent AND at least one resource. **A resource-less record is
21231
+ * deliberately NOT refused** — there is nothing to orphan, and that is the
21232
+ * issue's own measured repro plus the `cdkd state destroy` recovery path the
21233
+ * read-side decision exists to protect. The read path still refuses NOTHING:
21234
+ * `cdkd state show`, `cdkd state list` and `cdkd state orphan` all keep
21235
+ * working on the very record this refuses to DELETE FROM, which is what keeps
21236
+ * it fixable.
21237
+ *
21238
+ * The remedy is a TEMPLATE, never a substituted command, for the reason
21239
+ * {@link malformedDestroyResourcesRefusalMessage} records — the region reaching
21240
+ * this builder is an S3 key SEGMENT, which anyone able to write the bucket
21241
+ * chooses. (NOT "because cdkd does not know which region belongs in the flag",
21242
+ * which an earlier revision said and which is false for this flag:
21243
+ * `--stack-region` on `state orphan` selects the RECORD, and the key's region
21244
+ * is exactly that.) The body's region is not printed at all — its KIND is, the
21245
+ * bounded token {@link describeRegionValueKind} produces, for the reason
21246
+ * `getState`'s warn gives.
21247
+ *
21248
+ * And the target-naming half is GATED on both identifiers rendering EXACTLY,
21249
+ * the second half of that sibling's rule: a template is not enough on its own,
21250
+ * because the name the reader types into it comes from the clause ABOVE.
21251
+ */
21252
+ function divergentRecordRegionRefusalMessage(stackName, keyRegion, divergentBodyRegion, resourceCount) {
21253
+ const cap = STACK_REF_MAX_CODE_POINTS;
21254
+ const exact = safeIdentifier(stackName, cap) === stackName && safeIdentifier(keyRegion, cap) === keyRegion;
21255
+ const lists = resourceCount === void 0 ? "its resources map cannot be read" : `it still lists ${resourceCount} resource${resourceCount === 1 ? "" : "s"}`;
21256
+ const kind = describeRegionValueKind(divergentBodyRegion);
21257
+ const opening = exact ? `cdkd will not destroy stack ${shellQuote(safeIdentifier(stackName, cap))} (${shellQuote(safeIdentifier(keyRegion, cap))}): the state record read from that region's key carries a 'region' of its own (${kind}) that is not the key's, and ${lists}` : `cdkd will not destroy the state record this command loaded: it carries a 'region' of its own (${kind}) that is not the region of the key it was read from, and ${lists}`;
21258
+ const remedy = exact ? "Re-run with --verbose to see what the record's region field holds, then either destroy against the region the resources are really in, or repair that field to match the key it is stored under and re-run. To drop the record and leave the live resources standing, spelled out rather than pasteable because that command DELETES a record: cdkd state orphan <stack> --stack-region <region>" : `This record's stack name or region does NOT render exactly — what any surrounding output shows is a sanitized form, and another record may render identically — so this message names no target and offers no command against one. List the records as stored with 'cdkd state list --long', which prints a name needing sanitizing in quoted form, and act on the one whose key matches. Inspect it with: ${inspectCommand(void 0, void 0)}`;
21259
+ return `${opening} ${resourceCount === void 0 ? `— so cdkd can neither count what it would delete nor tell which region it is in` : `— so cdkd cannot tell which region those resources are in`}. cdkd stamps the key's region into every record it writes, so this record was not written by cdkd. Destroying against the key's region would issue every delete there; if the record's own region is the honest half, each delete comes back not-found, which this command reads as ALREADY DELETED — it would report success, remove the record, and leave every resource standing in the other region with nothing naming it. ${remedy}`;
21260
+ }
21261
+ /**
21262
+ * Refuse a destroy over a record whose body region diverged from its key's,
21263
+ * while it still lists resources — see
21264
+ * {@link divergentRecordRegionRefusalMessage} for why the conjunction is the
21265
+ * trigger and why a resource-less record is not refused.
21266
+ *
21267
+ * CALL IT AT THE TOP OF THE DESTROY, **above the `resourceCount` fast path**,
21268
+ * for the placement reason {@link refuseMalformedResourcesForDestroy} gives —
21269
+ * and BELOW that call, because this one reads the bag's SIZE and only that
21270
+ * guard proves the bag can be read at all.
21271
+ *
21272
+ * `divergentBodyRegion` is `S3StateBackend.getState`'s report, `undefined` on
21273
+ * every record that agreed with its key — which is every record cdkd wrote.
21274
+ */
21275
+ function refuseDivergentRecordRegionForDestroy(state, stackName, keyRegion, divergentBodyRegion) {
21276
+ if (divergentBodyRegion === void 0) return;
21277
+ const resourceCount = isReadableBag(state.resources) ? Object.keys(state.resources).length : void 0;
21278
+ if (resourceCount === 0) return;
21279
+ throw markNonRetryable(new CdkdError(divergentRecordRegionRefusalMessage(stackName, keyRegion, divergentBodyRegion, resourceCount), STATE_REGION_DIVERGED));
21280
+ }
21052
21281
  /**
21053
21282
  * The DEPLOY refusal text for the `resources` bag (issue
21054
21283
  * [#3161](https://github.com/go-to-k/cdkd/issues/3161)).
@@ -21560,10 +21789,12 @@ function stackClause(stackName, region) {
21560
21789
  *
21561
21790
  * **It diverges from {@link malformedDestroyResourcesRefusalMessage}, which
21562
21791
  * keeps `cdkd state orphan` a template on purpose, and the difference is where
21563
- * the identity comes from.** That builder is reached with a record-derived
21564
- * REGION (`state.region ?? ctx.baseRegion` in `destroy-runner.ts`) -- its stack
21565
- * name is the caller's, so the region alone is what would aim a destructive
21566
- * command using a value the record supplied. This one is reached only with the caller's
21792
+ * the identity comes from.** That builder is reached with a REGION cdkd read
21793
+ * off the state bucket (`state.region ?? ctx.baseRegion` in
21794
+ * `destroy-runner.ts`; since go-to-k/cdkd#3328 the first operand is the S3
21795
+ * KEY's region rather than the record body's, which is a key SEGMENT and so
21796
+ * still plantable) -- its stack name is the caller's, so the region alone is
21797
+ * what would aim a destructive command using a value cdkd does not own. This one is reached only with the caller's
21567
21798
  * synthesized stack and `pickStackRegion`'s answer, so substituting is safe and
21568
21799
  * omitting the region is the wider action. Two opposite precedents in one
21569
21800
  * module; neither is the general rule.
@@ -22639,6 +22870,21 @@ var DiffCalculator = class DiffCalculator {
22639
22870
  * The three environment variables that carry a static AWS identity into a child
22640
22871
  * process or container. Spelled once here so a forwarding site and the fence
22641
22872
  * that watches it cannot disagree about the population.
22873
+ *
22874
+ * That claim is only true while the population is IMPORTED, and for one release
22875
+ * it was not: every `forwardAwsEnv` body hardcoded its own array and
22876
+ * `local-surface-env-identity.test.ts` hardcoded the quoted literal, so the
22877
+ * constant guaranteed nothing and a fourth key added here would have reached
22878
+ * neither (issue [#3250](https://github.com/go-to-k/cdkd/issues/3250) item 4).
22879
+ * Every forwarding site now spreads this array and the fence treats the
22880
+ * constant's NAME as one of its site shapes, with a floor per spelling — so a
22881
+ * site quietly going back to a local copy drops out of one shape's count while
22882
+ * the other stays green, which is the failure that floor exists to report.
22883
+ *
22884
+ * Derive the readers with
22885
+ * `grep -rn AWS_CREDENTIAL_ENV_KEYS src tests`, never from a list written here:
22886
+ * a list of call sites in a doc comment is the shape that went stale in the
22887
+ * first place.
22642
22888
  */
22643
22889
  const AWS_CREDENTIAL_ENV_KEYS = [
22644
22890
  "AWS_ACCESS_KEY_ID",
@@ -22686,6 +22932,25 @@ function callerEnvCredentials() {
22686
22932
  return getPreAssumeEnvCredentials();
22687
22933
  }
22688
22934
  /**
22935
+ * Is the credential triple currently in `process.env` cdkd's OWN `--role-arn`
22936
+ * assumed role rather than the caller's?
22937
+ *
22938
+ * For a site that has to EXPLAIN a `callerEnvCredentials()` miss, not for one
22939
+ * that has to act on it. `undefined` from that function is ambiguous to a USER
22940
+ * in exactly one direction: with a role assumed, `AWS_ACCESS_KEY_ID` is visibly
22941
+ * set — cdkd set it — so an error telling them to set it is advice they have
22942
+ * already followed, and the real reason is that cdkd refuses to act as the role
22943
+ * here (issue [#3250](https://github.com/go-to-k/cdkd/issues/3250) item 5).
22944
+ *
22945
+ * Do not branch BEHAVIOUR on this. The three-way decision belongs to
22946
+ * `callerEnvCredentials` / `applyCallerIdentityCredentials`, which distinguish
22947
+ * "no role assumed" from "a role assumed over a caller who had nothing" — a
22948
+ * distinction this boolean deliberately collapses.
22949
+ */
22950
+ function envCredentialsAreAssumedRole() {
22951
+ return getAssumedRoleCredentials() !== void 0;
22952
+ }
22953
+ /**
22689
22954
  * Correct an environment bag that was populated by copying `process.env`, so it
22690
22955
  * carries the CALLER's identity rather than a `--role-arn` assumed role's.
22691
22956
  *
@@ -32160,7 +32425,7 @@ var CloudControlProvider = class {
32160
32425
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
32161
32426
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
32162
32427
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
32163
- const { ASGProvider } = await import("./asg-provider-CHeJf5zw.js").then((n) => n.n);
32428
+ const { ASGProvider } = await import("./asg-provider-7KM2pV3Q.js").then((n) => n.n);
32164
32429
  const asgProvider = new ASGProvider();
32165
32430
  return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
32166
32431
  }
@@ -36308,8 +36573,8 @@ var IAMRoleProvider = class {
36308
36573
  this.logger.warn(`Skipped deleting old role ${physicalId} during replacement: ${deleteResult.reason}. The old role may be orphaned and require manual cleanup.`);
36309
36574
  }
36310
36575
  } catch (error) {
36311
- orphanReason = `old role ${physicalId} could not be deleted: ${safeStringify(error)}`;
36312
- this.logger.warn(`Failed to delete old role ${physicalId} during replacement: ${safeStringify(error)}. The old role may be orphaned and require manual cleanup.`);
36576
+ orphanReason = `old role ${physicalId} could not be deleted: ${safeStringify$1(error)}`;
36577
+ this.logger.warn(`Failed to delete old role ${physicalId} during replacement: ${safeStringify$1(error)}. The old role may be orphaned and require manual cleanup.`);
36313
36578
  }
36314
36579
  const base = {
36315
36580
  physicalId: createResult.physicalId,
@@ -38526,6 +38791,23 @@ function makeSiblingClaimReader(params) {
38526
38791
  //#endregion
38527
38792
  //#region src/types/deployment-events.ts
38528
38793
  /**
38794
+ * Structured deployment-event types (issue #808).
38795
+ *
38796
+ * cdkd's CloudFormation `DescribeStackEvents` equivalent: every deploy /
38797
+ * destroy run appends one JSONL line per lifecycle event to
38798
+ * `s3://{bucket}/{prefix}/{stackName}/{region}/deployments/{runId}.jsonl`,
38799
+ * plus a small `deployments/index.json` listing the last N runs.
38800
+ *
38801
+ * Deliberately a SEPARATE key family from `state.json` — no state schema
38802
+ * bump is involved (state stays at its current version), and event files
38803
+ * survive `cdkd destroy` (state deletion does not touch `deployments/`),
38804
+ * so post-mortem context is available even after the stack is gone.
38805
+ *
38806
+ * SECURITY: events carry error + metadata ONLY. Resource properties are
38807
+ * never recorded here (they may contain secrets); properties already live
38808
+ * in state.json.
38809
+ */
38810
+ /**
38529
38811
  * Walk an error's `.cause` chain (bounded) and extract metadata for a
38530
38812
  * deployment event. The outermost error supplies `name` / `message`
38531
38813
  * (matching what the user sees in the log); the innermost AWS-SDK-shaped
@@ -38535,7 +38817,7 @@ function makeSiblingClaimReader(params) {
38535
38817
  function extractDeploymentEventError(err) {
38536
38818
  if (!(err instanceof Error)) return {
38537
38819
  name: "UnknownError",
38538
- message: String(err)
38820
+ message: safeStringify$1(err)
38539
38821
  };
38540
38822
  const result = {
38541
38823
  name: err.name || "Error",
@@ -38707,7 +38989,7 @@ const PRE_DELETE_SNAPSHOT_TIMEOUT_MS = 36e5;
38707
38989
  /** Post-snapshot settle budget for a Redshift cluster before its delete. */
38708
38990
  const REDSHIFT_CLUSTER_SETTLE_TIMEOUT_MS = 12e5;
38709
38991
  function errMsg(error) {
38710
- return error instanceof Error ? error.message : String(error);
38992
+ return describeAwsFailure(error).detail;
38711
38993
  }
38712
38994
  function isVolumeNotFound(error) {
38713
38995
  return (error instanceof Error ? error.name : "") === "InvalidVolume.NotFound" || errMsg(error).includes("InvalidVolume.NotFound");
@@ -45154,5 +45436,5 @@ var DeployEngine = class DeployEngine {
45154
45436
  };
45155
45437
 
45156
45438
  //#endregion
45157
- export { secretBearing as $, isCdkdError as $i, scrubResourceRecord as $n, Synthesizer as $r, s3BucketArn as $t, extractDeploymentEventError as A, setAwsClients as Ai, repairMalformedResourcesForReadOnly as An, isCrossRegionRedirect as Ar, isImpossibleEmptyStoredAttribute as At, toCfnInteger as B, LocalStartServiceError as Bi, TEMPLATE_SOURCED_RULES as Bn, describeDockerExecFailure as Br, coerceCfnBoolean as Bt, buildFinalSnapshotIdentifier as C, AssemblyReader as Ci, refuseMalformedOutputs as Cn, loadPublishableAssetManifest as Cr, isTerminationProtectionPropagationError as Ct, refusesFinalSnapshot as D, AwsClients as Di, refuseMalformedState as Dn, assertAssetBucketRegion as Dr, cfnRefValueFromPhysicalId as Dt, isFinalSnapshotError as E, resolveBucketRegion as Ei, refuseMalformedResourcesForDestroy as En, BOOTSTRAP_MARKER_PREFIX as Er, carriesFetchableDynamicReference as Et, isStatefulRecreateTargetSync as F, DependencyError as Fi, DagBuilder as Fn, buildDenyExternalAccessPolicy as Fr, isSettledInstanceState as Ft, gray as G, ProvisioningError as Gi, identityKeyFor as Gn, partitionSensitiveEnv as Gr, readConfigString as Gt, formatResourceLine as H, MalformedProducerRecordRefusalError as Hi, createSecretMasker as Hn, dockerSpawnEnvWithSensitive as Hr, configBooleanRefusal as Ht, renderStatefulReason as I, DeployCancelledError as Ii, TemplateParser as In, describeAwsFailure as Ir, WAFv2WebACLProvider as It, yellow as J, StackHasActiveImportsError as Ji, maskSecretsInError as Jn, runDockerStreaming as Jr, requireConfigObject as Jt, green as K, ResourceTimeoutError as Ki, isSingleDynamicReferenceToken as Kn, redactDockerArgvValues as Kr, replayWarn as Kt, WARM_THROUGHPUT_MEMBERS as L, DynamicReferenceRegionAmbiguousError as Li, STATE_SOURCED_BASELINE_RULES as Ln, safeStringify as Lr, normalizeAwsTagsToCfn as Lt, planOrphanAdoption as M, CdkdError as Mi, findSilentDropProperties as Mn, readBootstrapMarkerBody as Mr, isUnboundTemplateParameter as Mt, MULTI_REGION_RECREATE_BLOCKED_TYPES as N, ConfigError as Ni, describeTypeWithThrottleRetry as Nn, validateAssetBucketName as Nr, parameterTypeMayLoseSecretIdentity as Nt, unsupportedFinalSnapshotError as O, getAwsClients as Oi, repairMalformedOutputsForReadOnly as On, ensureAssetStorage as Or, coerceParameterTypedValue as Ot, isStatefulRecreateTargetForReplace as P, CrossAccountSecretRefusalError as Pi, withRetry as Pn, validateContainerRepoName as Pr, refStateLookupFromResource as Pt, isExportAliasCollision as Q, formatError as Qi, redactSecretsForState as Qn, getDockerImageBySourceHash as Qr, withSharedDrainBudget as Qt, coerceWarmThroughput as R, IntrinsicResolutionRefusalError as Ri, STATE_SOURCED_CROSS_GENERATION_RULES as Rn, buildDockerImage as Rr, resolveExplicitPhysicalId as Rt, PRE_DELETE_SNAPSHOT_TYPES as S, derivePartitionAndUrlSuffix as Si, refuseMalformedNestedChildOutputs as Sn, createAssetRedirectResolver as Sr, disableInstanceApiTermination as St, createPreDeleteFinalSnapshot as T, clearBucketRegionCache as Ti, refuseMalformedResourcePropertiesForOrphan as Tn, AssetModeResolver as Tr, carriesDynamicReference as Tt, bold as U, NestedStackChildDirectDestroyError as Ui, dynamicReferenceTokens as Un, formatDockerLoginError as Ur, configIntegerRefusal as Ut, toFiniteNumber as V, LockError as Vi, carriesSecretMask as Vn, describeDockerFailure as Vr, coerceCfnInteger as Vt, cyan as W, PartialFailureError as Wi, errorCauseChain as Wn, getDockerCmd as Wr, configStringRefusal as Wt, collectPublishedOutputNames as X, StateError as Xi, recordMaskOnlyValue as Xn, stripControlChars as Xr, classifyReplaySecretRegion as Xt, collectDeclaredOutputNames as Y, StackTerminationProtectionError as Yi, maskSecretsInText as Yn, escapeRegExp$1 as Yr, requireConfigString as Yt, exportAliasCollisionScrubWarning as Z, SynthesisError as Zi, recoverMaskedOutput as Zn, AssetManifestLoader as Zr, producerRegionsFromState as Zt, isSecretBearingReferenceString as _, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as _i, malformedRenderedContainersWarning as _n, shouldRetainResource as _r, isWaitAbandonedError as _t, DeploymentEventsStore as a, isTransientServerError as aa, resolveCaptureObservedState as ai, applyCallerIdentityCredentials as an, forceQuitRecoveryClause as ar, clearOnUpdateRemoval as at, bindingSkippedOutputs as b, PARTITION_TABLE as bi, malformedStateRefusalMessage as bn, WorkGraph as br, deleteIndeterminateGuards as bt, replayFailedOperations as c, retryClassificationText as ca, resolveStateBucketWithDefaultAndSource as ci, INTRINSIC_KEYS as cn, CUSTOM_RESOURCE_RESPONSE_PREFIX as cr, createMaskedRetryLogger as ct, updatePartialReason as d, warnDeprecatedNoPrefixCliFlag as di, hasReadableResources as dn, hasReadableExportSet as dr, beginCommandInterruptScope as dt, normalizeAwsError as ea, synthesisStatusMessage as ei, s3BucketDomainName as en, LockManager as er, secretBearingStateKeyWarning as et, withResourceDeadline as f, CFN_TEMPLATE_BODY_LIMIT as fi, malformedExportNamesWarning as fn, importableOutputKeys as fr, endCommandInterruptScope as ft, bagHoldsSecretExpression as g, uploadCfnTemplate as gi, malformedOutputsWarning as gn, orphansCarriedFrom as gr, CloudControlProvider as gt, computeImplicitDeleteEdges as h, findLargeInlineResources as hi, malformedOutputsRefusalMessage as hn, orphansAfterRollback as hr, startInterruptWatch as ht, DeploymentEventsReader as i, isThrottlingError as ia, resolveAutoAssetStorage as ii, applyRoleArnIfSet as in, buildLockContentionMessage as ir, collectInlinePolicyNamesManagedBySiblings as it, makeSiblingClaimReader as j, AssetError as ji, findActionableSilentDrops as jn, parseBootstrapMarker as jr, isNamelessDynamicReferenceError as jt, makeCanonicalizePropertiesFn as k, resetAwsClients as ki, repairMalformedResourcePropertiesForReadOnly as kn, getBootstrapMarkerKey as kr, getAccountInfo as kt, replayRollback as l, __exportAll as la, resolveUseCdkBootstrapAssets as li, STATE_RESOURCES_MALFORMED as ln, DEFAULT_STATE_PREFIX as lr, maskDeep as lt, IMPLICIT_DELETE_DEPENDENCIES as m, MIGRATE_TMP_PREFIX as mi, malformedLocalOutputsWarning as mn, isReadableBag as mr, isInterruptedWaitError as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, isMarkedNonRetryable as na, getLegacyStateBucketName as ni, s3BucketRegionalDomainName as nn, rebuildClientForBucketRegion as nr, getCurrentResourceSecrets as nt, planFailedOps as o, markNonRetryable as oa, resolveSkipPrefix as oi, callerEnvCredentials as on, formatLockExpiry as or, ProviderRegistry as ot, maskingRetryLogger as p, CFN_TEMPLATE_URL_LIMIT as pi, malformedExportSourceWarning as pn, importableOutputs as pr, interruptWatchListenerCount as pt, red as q, ResourceUpdateNotSupportedError as qi, markSameGenerationBag as qn, runDockerForeground as qr, requireConfigArray as qt, DeployEngine as r, isRetryableTransientError as ra, resolveApp as ri, s3BucketWebsiteUrl as rn, buildForceUnlockCommand as rr, IAMRoleProvider as rt, planRollback as s, markRedactedCause as sa, resolveStateBucketWithDefault as si, DiffCalculator as sn, shellQuote as sr, wouldReturnToSdkProvider as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, withErrorHandling as ta, getDefaultStateBucketName as ti, s3BucketDualStackDomainName as tn, S3StateBackend as tr, secretSafeKeyDisplay as tt, updatePartialMessage as u, stateBucketExistenceConfirmed as ui, hasReadableOutputs as un, exportNamesCarriedFrom as ur, maskerOrIdentity as ut, keptWholeReasonText as v, decodeListingKey as vi, malformedResourcePropertiesWarning as vn, AssetPublisher as vr, slowCcOperationTimeoutMs as vt, ccRoutedFinalSnapshotError as w, processStackMessages as wi, refuseMalformedOutputsForDestroy as wn, rewriteTemplateAssetReferences as wr, IntrinsicFunctionResolver as wt, ATOMIC_FINAL_SNAPSHOT_TYPES as x, canonicalizeRegion as xi, producerRecordKey as xn, buildAssetRedirectMap as xr, deleteSkipReason as xt, mergeNoChangeOutputs as y, expectedOwnerParam as yi, malformedResourcesWarning as yn, stringifyValue as yr, UNSPECIFIED_SKIP_REASON as yt, isWarmThroughputDecrease as z, LocalInvokeBuildError as zi, STATE_SOURCED_READBACK_RULES as zn, describeDockerCapturedOutput as zr, assertRegionMatch as zt };
45158
- //# sourceMappingURL=deploy-engine-BcNBidfy.js.map
45439
+ export { secretBearing as $, formatError as $i, recordMaskOnlyValue as $n, getDockerImageBySourceHash as $r, s3BucketArn as $t, extractDeploymentEventError as A, resetAwsClients as Ai, refuseMalformedState as An, assertAssetBucketRegion as Ar, isImpossibleEmptyStoredAttribute as At, toCfnInteger as B, LocalInvokeBuildError as Bi, STATE_SOURCED_BASELINE_RULES as Bn, describeDockerCapturedOutput as Br, coerceCfnBoolean as Bt, buildFinalSnapshotIdentifier as C, derivePartitionAndUrlSuffix as Ci, producerRecordKey as Cn, WorkGraph as Cr, isTerminationProtectionPropagationError as Ct, refusesFinalSnapshot as D, resolveBucketRegion as Di, refuseMalformedOutputsForDestroy as Dn, rewriteTemplateAssetReferences as Dr, cfnRefValueFromPhysicalId as Dt, isFinalSnapshotError as E, clearBucketRegionCache as Ei, refuseMalformedOutputs as En, loadPublishableAssetManifest as Er, carriesFetchableDynamicReference as Et, isStatefulRecreateTargetSync as F, CrossAccountSecretRefusalError as Fi, findSilentDropProperties as Fn, readBootstrapMarkerBody as Fr, isSettledInstanceState as Ft, gray as G, PartialFailureError as Gi, createSecretMasker as Gn, getDockerCmd as Gr, readConfigString as Gt, formatResourceLine as H, LockError as Hi, STATE_SOURCED_READBACK_RULES as Hn, describeDockerFailure as Hr, configBooleanRefusal as Ht, renderStatefulReason as I, DependencyError as Ii, describeTypeWithThrottleRetry as In, validateAssetBucketName as Ir, WAFv2WebACLProvider as It, yellow as J, ResourceUpdateNotSupportedError as Ji, identityKeyFor as Jn, runDockerForeground as Jr, requireConfigObject as Jt, green as K, ProvisioningError as Ki, dynamicReferenceTokens as Kn, partitionSensitiveEnv as Kr, replayWarn as Kt, WARM_THROUGHPUT_MEMBERS as L, DeployCancelledError as Li, withRetry as Ln, validateContainerRepoName as Lr, normalizeAwsTagsToCfn as Lt, planOrphanAdoption as M, AssetError as Mi, repairMalformedResourcePropertiesForReadOnly as Mn, getBootstrapMarkerKey as Mr, isUnboundTemplateParameter as Mt, MULTI_REGION_RECREATE_BLOCKED_TYPES as N, CdkdError as Ni, repairMalformedResourcesForReadOnly as Nn, isCrossRegionRedirect as Nr, parameterTypeMayLoseSecretIdentity as Nt, unsupportedFinalSnapshotError as O, AwsClients as Oi, refuseMalformedResourcePropertiesForOrphan as On, AssetModeResolver as Or, coerceParameterTypedValue as Ot, isStatefulRecreateTargetForReplace as P, ConfigError as Pi, findActionableSilentDrops as Pn, parseBootstrapMarker as Pr, refStateLookupFromResource as Pt, isExportAliasCollision as Q, SynthesisError as Qi, maskSecretsInText as Qn, AssetManifestLoader as Qr, withSharedDrainBudget as Qt, coerceWarmThroughput as R, DynamicReferenceRegionAmbiguousError as Ri, DagBuilder as Rn, buildDenyExternalAccessPolicy as Rr, resolveExplicitPhysicalId as Rt, PRE_DELETE_SNAPSHOT_TYPES as S, canonicalizeRegion as Si, malformedStateRefusalMessage as Sn, stringifyValue as Sr, disableInstanceApiTermination as St, createPreDeleteFinalSnapshot as T, processStackMessages as Ti, refuseMalformedNestedChildOutputs as Tn, createAssetRedirectResolver as Tr, carriesDynamicReference as Tt, bold as U, MalformedProducerRecordRefusalError as Ui, TEMPLATE_SOURCED_RULES as Un, dockerSpawnEnvWithSensitive as Ur, configIntegerRefusal as Ut, toFiniteNumber as V, LocalStartServiceError as Vi, STATE_SOURCED_CROSS_GENERATION_RULES as Vn, describeDockerExecFailure as Vr, coerceCfnInteger as Vt, cyan as W, NestedStackChildDirectDestroyError as Wi, carriesSecretMask as Wn, formatDockerLoginError as Wr, configStringRefusal as Wt, collectPublishedOutputNames as X, StackTerminationProtectionError as Xi, markSameGenerationBag as Xn, escapeRegExp$1 as Xr, classifyReplaySecretRegion as Xt, collectDeclaredOutputNames as Y, StackHasActiveImportsError as Yi, isSingleDynamicReferenceToken as Yn, runDockerStreaming as Yr, requireConfigString as Yt, exportAliasCollisionScrubWarning as Z, StateError as Zi, maskSecretsInError as Zn, stripControlChars as Zr, producerRegionsFromState as Zt, isSecretBearingReferenceString as _, uploadCfnTemplate as _i, malformedOutputsRefusalMessage as _n, isReadableBag as _r, isWaitAbandonedError as _t, DeploymentEventsStore as a, isThrottlingError as aa, resolveAutoAssetStorage as ai, AWS_CREDENTIAL_ENV_KEYS as an, rebuildClientForBucketRegion as ar, clearOnUpdateRemoval as at, bindingSkippedOutputs as b, expectedOwnerParam as bi, malformedResourcePropertiesWarning as bn, shouldRetainResource as br, deleteIndeterminateGuards as bt, replayFailedOperations as c, markRedactedCause as ca, resolveStateBucketWithDefault as ci, envCredentialsAreAssumedRole as cn, forceQuitRecoveryClause as cr, createMaskedRetryLogger as ct, updatePartialReason as d, safeStringify$1 as da, stateBucketExistenceConfirmed as di, STATE_RESOURCES_MALFORMED as dn, CUSTOM_RESOURCE_RESPONSE_PREFIX as dr, beginCommandInterruptScope as dt, isCdkdError as ea, Synthesizer as ei, s3BucketDomainName as en, recoverMaskedOutput as er, secretBearingStateKeyWarning as et, withResourceDeadline as f, __exportAll as fa, warnDeprecatedNoPrefixCliFlag as fi, hasReadableOutputs as fn, DEFAULT_STATE_PREFIX as fr, endCommandInterruptScope as ft, bagHoldsSecretExpression as g, findLargeInlineResources as gi, malformedLocalOutputsWarning as gn, importableOutputs as gr, CloudControlProvider as gt, computeImplicitDeleteEdges as h, MIGRATE_TMP_PREFIX as hi, malformedExportSourceWarning as hn, importableOutputKeys as hr, startInterruptWatch as ht, DeploymentEventsReader as i, isRetryableTransientError as ia, resolveApp as ii, applyRoleArnIfSet as in, S3StateBackend as ir, collectInlinePolicyNamesManagedBySiblings as it, makeSiblingClaimReader as j, setAwsClients as ji, repairMalformedOutputsForReadOnly as jn, ensureAssetStorage as jr, isNamelessDynamicReferenceError as jt, makeCanonicalizePropertiesFn as k, getAwsClients as ki, refuseMalformedResourcesForDestroy as kn, BOOTSTRAP_MARKER_PREFIX as kr, getAccountInfo as kt, replayRollback as l, retryClassificationText as la, resolveStateBucketWithDefaultAndSource as li, DiffCalculator as ln, formatLockExpiry as lr, maskDeep as lt, IMPLICIT_DELETE_DEPENDENCIES as m, CFN_TEMPLATE_URL_LIMIT as mi, malformedExportNamesWarning as mn, hasReadableExportSet as mr, isInterruptedWaitError as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, withErrorHandling as na, getDefaultStateBucketName as ni, s3BucketRegionalDomainName as nn, scrubResourceRecord as nr, getCurrentResourceSecrets as nt, planFailedOps as o, isTransientServerError as oa, resolveCaptureObservedState as oi, applyCallerIdentityCredentials as on, buildForceUnlockCommand as or, ProviderRegistry as ot, maskingRetryLogger as p, CFN_TEMPLATE_BODY_LIMIT as pi, hasReadableResources as pn, exportNamesCarriedFrom as pr, interruptWatchListenerCount as pt, red as q, ResourceTimeoutError as qi, errorCauseChain as qn, redactDockerArgvValues as qr, requireConfigArray as qt, DeployEngine as r, isMarkedNonRetryable as ra, getLegacyStateBucketName as ri, s3BucketWebsiteUrl as rn, LockManager as rr, IAMRoleProvider as rt, planRollback as s, markNonRetryable as sa, resolveSkipPrefix as si, callerEnvCredentials as sn, buildLockContentionMessage as sr, wouldReturnToSdkProvider as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, normalizeAwsError as ta, synthesisStatusMessage as ti, s3BucketDualStackDomainName as tn, redactSecretsForState as tr, secretSafeKeyDisplay as tt, updatePartialMessage as u, describeAwsFailure as ua, resolveUseCdkBootstrapAssets as ui, INTRINSIC_KEYS as un, shellQuote as ur, maskerOrIdentity as ut, keptWholeReasonText as v, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as vi, malformedOutputsWarning as vn, orphansAfterRollback as vr, slowCcOperationTimeoutMs as vt, ccRoutedFinalSnapshotError as w, AssemblyReader as wi, refuseDivergentRecordRegionForDestroy as wn, buildAssetRedirectMap as wr, IntrinsicFunctionResolver as wt, ATOMIC_FINAL_SNAPSHOT_TYPES as x, PARTITION_TABLE as xi, malformedResourcesWarning as xn, AssetPublisher as xr, deleteSkipReason as xt, mergeNoChangeOutputs as y, decodeListingKey as yi, malformedRenderedContainersWarning as yn, orphansCarriedFrom as yr, UNSPECIFIED_SKIP_REASON as yt, isWarmThroughputDecrease as z, IntrinsicResolutionRefusalError as zi, TemplateParser as zn, buildDockerImage as zr, assertRegionMatch as zt };
45440
+ //# sourceMappingURL=deploy-engine-CbIc5Y73.js.map