@go-to-k/cdkd 0.290.23 → 0.290.25

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-DxUVmvM5.js";
3
+ import { t as getCdkdVersion } from "./version-CduXA6UL.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.
@@ -32160,7 +32391,7 @@ var CloudControlProvider = class {
32160
32391
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
32161
32392
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
32162
32393
  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-BOYln2Tw.js").then((n) => n.n);
32394
+ const { ASGProvider } = await import("./asg-provider-DL62n7uG.js").then((n) => n.n);
32164
32395
  const asgProvider = new ASGProvider();
32165
32396
  return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
32166
32397
  }
@@ -36308,8 +36539,8 @@ var IAMRoleProvider = class {
36308
36539
  this.logger.warn(`Skipped deleting old role ${physicalId} during replacement: ${deleteResult.reason}. The old role may be orphaned and require manual cleanup.`);
36309
36540
  }
36310
36541
  } 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.`);
36542
+ orphanReason = `old role ${physicalId} could not be deleted: ${safeStringify$1(error)}`;
36543
+ 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
36544
  }
36314
36545
  const base = {
36315
36546
  physicalId: createResult.physicalId,
@@ -38526,6 +38757,23 @@ function makeSiblingClaimReader(params) {
38526
38757
  //#endregion
38527
38758
  //#region src/types/deployment-events.ts
38528
38759
  /**
38760
+ * Structured deployment-event types (issue #808).
38761
+ *
38762
+ * cdkd's CloudFormation `DescribeStackEvents` equivalent: every deploy /
38763
+ * destroy run appends one JSONL line per lifecycle event to
38764
+ * `s3://{bucket}/{prefix}/{stackName}/{region}/deployments/{runId}.jsonl`,
38765
+ * plus a small `deployments/index.json` listing the last N runs.
38766
+ *
38767
+ * Deliberately a SEPARATE key family from `state.json` — no state schema
38768
+ * bump is involved (state stays at its current version), and event files
38769
+ * survive `cdkd destroy` (state deletion does not touch `deployments/`),
38770
+ * so post-mortem context is available even after the stack is gone.
38771
+ *
38772
+ * SECURITY: events carry error + metadata ONLY. Resource properties are
38773
+ * never recorded here (they may contain secrets); properties already live
38774
+ * in state.json.
38775
+ */
38776
+ /**
38529
38777
  * Walk an error's `.cause` chain (bounded) and extract metadata for a
38530
38778
  * deployment event. The outermost error supplies `name` / `message`
38531
38779
  * (matching what the user sees in the log); the innermost AWS-SDK-shaped
@@ -38535,7 +38783,7 @@ function makeSiblingClaimReader(params) {
38535
38783
  function extractDeploymentEventError(err) {
38536
38784
  if (!(err instanceof Error)) return {
38537
38785
  name: "UnknownError",
38538
- message: String(err)
38786
+ message: safeStringify$1(err)
38539
38787
  };
38540
38788
  const result = {
38541
38789
  name: err.name || "Error",
@@ -38707,7 +38955,7 @@ const PRE_DELETE_SNAPSHOT_TIMEOUT_MS = 36e5;
38707
38955
  /** Post-snapshot settle budget for a Redshift cluster before its delete. */
38708
38956
  const REDSHIFT_CLUSTER_SETTLE_TIMEOUT_MS = 12e5;
38709
38957
  function errMsg(error) {
38710
- return error instanceof Error ? error.message : String(error);
38958
+ return describeAwsFailure(error).detail;
38711
38959
  }
38712
38960
  function isVolumeNotFound(error) {
38713
38961
  return (error instanceof Error ? error.name : "") === "InvalidVolume.NotFound" || errMsg(error).includes("InvalidVolume.NotFound");
@@ -45154,5 +45402,5 @@ var DeployEngine = class DeployEngine {
45154
45402
  };
45155
45403
 
45156
45404
  //#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-CBzq85r6.js.map
45405
+ export { secretBearing as $, normalizeAwsError as $i, redactSecretsForState as $n, synthesisStatusMessage as $r, s3BucketArn as $t, extractDeploymentEventError as A, AssetError as Ai, repairMalformedResourcePropertiesForReadOnly as An, getBootstrapMarkerKey as Ar, isImpossibleEmptyStoredAttribute as At, toCfnInteger as B, LockError as Bi, STATE_SOURCED_READBACK_RULES as Bn, describeDockerFailure as Br, coerceCfnBoolean as Bt, buildFinalSnapshotIdentifier as C, processStackMessages as Ci, refuseMalformedNestedChildOutputs as Cn, createAssetRedirectResolver as Cr, isTerminationProtectionPropagationError as Ct, refusesFinalSnapshot as D, getAwsClients as Di, refuseMalformedResourcesForDestroy as Dn, BOOTSTRAP_MARKER_PREFIX as Dr, cfnRefValueFromPhysicalId as Dt, isFinalSnapshotError as E, AwsClients as Ei, refuseMalformedResourcePropertiesForOrphan as En, AssetModeResolver as Er, carriesFetchableDynamicReference as Et, isStatefulRecreateTargetSync as F, DeployCancelledError as Fi, withRetry as Fn, validateContainerRepoName as Fr, isSettledInstanceState as Ft, gray as G, ResourceTimeoutError as Gi, errorCauseChain as Gn, redactDockerArgvValues as Gr, readConfigString as Gt, formatResourceLine as H, NestedStackChildDirectDestroyError as Hi, carriesSecretMask as Hn, formatDockerLoginError as Hr, configBooleanRefusal as Ht, renderStatefulReason as I, DynamicReferenceRegionAmbiguousError as Ii, DagBuilder as In, buildDenyExternalAccessPolicy as Ir, WAFv2WebACLProvider as It, yellow as J, StackTerminationProtectionError as Ji, markSameGenerationBag as Jn, escapeRegExp$1 as Jr, requireConfigObject as Jt, green as K, ResourceUpdateNotSupportedError as Ki, identityKeyFor as Kn, runDockerForeground as Kr, replayWarn as Kt, WARM_THROUGHPUT_MEMBERS as L, IntrinsicResolutionRefusalError as Li, TemplateParser as Ln, buildDockerImage as Lr, normalizeAwsTagsToCfn as Lt, planOrphanAdoption as M, ConfigError as Mi, findActionableSilentDrops as Mn, parseBootstrapMarker as Mr, isUnboundTemplateParameter as Mt, MULTI_REGION_RECREATE_BLOCKED_TYPES as N, CrossAccountSecretRefusalError as Ni, findSilentDropProperties as Nn, readBootstrapMarkerBody as Nr, parameterTypeMayLoseSecretIdentity as Nt, unsupportedFinalSnapshotError as O, resetAwsClients as Oi, refuseMalformedState as On, assertAssetBucketRegion as Or, coerceParameterTypedValue as Ot, isStatefulRecreateTargetForReplace as P, DependencyError as Pi, describeTypeWithThrottleRetry as Pn, validateAssetBucketName as Pr, refStateLookupFromResource as Pt, isExportAliasCollision as Q, isCdkdError as Qi, recoverMaskedOutput as Qn, Synthesizer as Qr, withSharedDrainBudget as Qt, coerceWarmThroughput as R, LocalInvokeBuildError as Ri, STATE_SOURCED_BASELINE_RULES as Rn, describeDockerCapturedOutput as Rr, resolveExplicitPhysicalId as Rt, PRE_DELETE_SNAPSHOT_TYPES as S, AssemblyReader as Si, refuseDivergentRecordRegionForDestroy as Sn, buildAssetRedirectMap as Sr, disableInstanceApiTermination as St, createPreDeleteFinalSnapshot as T, resolveBucketRegion as Ti, refuseMalformedOutputsForDestroy as Tn, rewriteTemplateAssetReferences as Tr, carriesDynamicReference as Tt, bold as U, PartialFailureError as Ui, createSecretMasker as Un, getDockerCmd as Ur, configIntegerRefusal as Ut, toFiniteNumber as V, MalformedProducerRecordRefusalError as Vi, TEMPLATE_SOURCED_RULES as Vn, dockerSpawnEnvWithSensitive as Vr, coerceCfnInteger as Vt, cyan as W, ProvisioningError as Wi, dynamicReferenceTokens as Wn, partitionSensitiveEnv as Wr, configStringRefusal as Wt, collectPublishedOutputNames as X, SynthesisError as Xi, maskSecretsInText as Xn, AssetManifestLoader as Xr, classifyReplaySecretRegion as Xt, collectDeclaredOutputNames as Y, StateError as Yi, maskSecretsInError as Yn, stripControlChars as Yr, requireConfigString as Yt, exportAliasCollisionScrubWarning as Z, formatError as Zi, recordMaskOnlyValue as Zn, getDockerImageBySourceHash as Zr, producerRegionsFromState as Zt, isSecretBearingReferenceString as _, decodeListingKey as _i, malformedRenderedContainersWarning as _n, orphansCarriedFrom as _r, isWaitAbandonedError as _t, DeploymentEventsStore as a, markNonRetryable as aa, resolveSkipPrefix as ai, applyCallerIdentityCredentials as an, buildLockContentionMessage as ar, clearOnUpdateRemoval as at, bindingSkippedOutputs as b, canonicalizeRegion as bi, malformedStateRefusalMessage as bn, stringifyValue as br, deleteIndeterminateGuards as bt, replayFailedOperations as c, describeAwsFailure as ca, resolveUseCdkBootstrapAssets as ci, INTRINSIC_KEYS as cn, shellQuote as cr, createMaskedRetryLogger as ct, updatePartialReason as d, CFN_TEMPLATE_BODY_LIMIT as di, hasReadableResources as dn, exportNamesCarriedFrom as dr, beginCommandInterruptScope as dt, withErrorHandling as ea, getDefaultStateBucketName as ei, s3BucketDomainName as en, scrubResourceRecord as er, secretBearingStateKeyWarning as et, withResourceDeadline as f, CFN_TEMPLATE_URL_LIMIT as fi, malformedExportNamesWarning as fn, hasReadableExportSet as fr, endCommandInterruptScope as ft, bagHoldsSecretExpression as g, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as gi, malformedOutputsWarning as gn, orphansAfterRollback as gr, CloudControlProvider as gt, computeImplicitDeleteEdges as h, uploadCfnTemplate as hi, malformedOutputsRefusalMessage as hn, isReadableBag as hr, startInterruptWatch as ht, DeploymentEventsReader as i, isTransientServerError as ia, resolveCaptureObservedState as ii, applyRoleArnIfSet as in, buildForceUnlockCommand as ir, collectInlinePolicyNamesManagedBySiblings as it, makeSiblingClaimReader as j, CdkdError as ji, repairMalformedResourcesForReadOnly as jn, isCrossRegionRedirect as jr, isNamelessDynamicReferenceError as jt, makeCanonicalizePropertiesFn as k, setAwsClients as ki, repairMalformedOutputsForReadOnly as kn, ensureAssetStorage as kr, getAccountInfo as kt, replayRollback as l, safeStringify$1 as la, stateBucketExistenceConfirmed as li, STATE_RESOURCES_MALFORMED as ln, CUSTOM_RESOURCE_RESPONSE_PREFIX as lr, maskDeep as lt, IMPLICIT_DELETE_DEPENDENCIES as m, findLargeInlineResources as mi, malformedLocalOutputsWarning as mn, importableOutputs as mr, isInterruptedWaitError as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, isRetryableTransientError as na, resolveApp as ni, s3BucketRegionalDomainName as nn, S3StateBackend as nr, getCurrentResourceSecrets as nt, planFailedOps as o, markRedactedCause as oa, resolveStateBucketWithDefault as oi, callerEnvCredentials as on, forceQuitRecoveryClause as or, ProviderRegistry as ot, maskingRetryLogger as p, MIGRATE_TMP_PREFIX as pi, malformedExportSourceWarning as pn, importableOutputKeys as pr, interruptWatchListenerCount as pt, red as q, StackHasActiveImportsError as qi, isSingleDynamicReferenceToken as qn, runDockerStreaming as qr, requireConfigArray as qt, DeployEngine as r, isThrottlingError as ra, resolveAutoAssetStorage as ri, s3BucketWebsiteUrl as rn, rebuildClientForBucketRegion as rr, IAMRoleProvider as rt, planRollback as s, retryClassificationText as sa, resolveStateBucketWithDefaultAndSource as si, DiffCalculator as sn, formatLockExpiry as sr, wouldReturnToSdkProvider as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, isMarkedNonRetryable as ta, getLegacyStateBucketName as ti, s3BucketDualStackDomainName as tn, LockManager as tr, secretSafeKeyDisplay as tt, updatePartialMessage as u, __exportAll as ua, warnDeprecatedNoPrefixCliFlag as ui, hasReadableOutputs as un, DEFAULT_STATE_PREFIX as ur, maskerOrIdentity as ut, keptWholeReasonText as v, expectedOwnerParam as vi, malformedResourcePropertiesWarning as vn, shouldRetainResource as vr, slowCcOperationTimeoutMs as vt, ccRoutedFinalSnapshotError as w, clearBucketRegionCache as wi, refuseMalformedOutputs as wn, loadPublishableAssetManifest as wr, IntrinsicFunctionResolver as wt, ATOMIC_FINAL_SNAPSHOT_TYPES as x, derivePartitionAndUrlSuffix as xi, producerRecordKey as xn, WorkGraph as xr, deleteSkipReason as xt, mergeNoChangeOutputs as y, PARTITION_TABLE as yi, malformedResourcesWarning as yn, AssetPublisher as yr, UNSPECIFIED_SKIP_REASON as yt, isWarmThroughputDecrease as z, LocalStartServiceError as zi, STATE_SOURCED_CROSS_GENERATION_RULES as zn, describeDockerExecFailure as zr, assertRegionMatch as zt };
45406
+ //# sourceMappingURL=deploy-engine-DtFiFMxh.js.map