@cdkd/cli 0.0.1 → 0.324.0

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.
Files changed (35) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +179 -2
  3. package/dist/asg-provider-D8-GEE_f.js +1277 -0
  4. package/dist/asg-provider-D8-GEE_f.js.map +1 -0
  5. package/dist/aws-client-defaults-BLXFsfMa.js +275 -0
  6. package/dist/aws-client-defaults-BLXFsfMa.js.map +1 -0
  7. package/dist/aws-clients-DWpHZk2l.js +440 -0
  8. package/dist/aws-clients-DWpHZk2l.js.map +1 -0
  9. package/dist/aws-failure-text-DiHodi76.js +1158 -0
  10. package/dist/aws-failure-text-DiHodi76.js.map +1 -0
  11. package/dist/cli.d.ts +1 -0
  12. package/dist/cli.js +135 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/created-resource-sink-CmrPwGge.js +2343 -0
  15. package/dist/created-resource-sink-CmrPwGge.js.map +1 -0
  16. package/dist/db-instance-modify-retry-DCLA_p9I.js +43 -0
  17. package/dist/db-instance-modify-retry-DCLA_p9I.js.map +1 -0
  18. package/dist/deploy-engine-B32kQ3SO.js +55858 -0
  19. package/dist/deploy-engine-B32kQ3SO.js.map +1 -0
  20. package/dist/iam-role-provider-D3th06lJ.js +698 -0
  21. package/dist/iam-role-provider-D3th06lJ.js.map +1 -0
  22. package/dist/index.d.ts +1630 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +7 -0
  25. package/dist/neptune-provider-CN7ptfAK.js +1470 -0
  26. package/dist/neptune-provider-CN7ptfAK.js.map +1 -0
  27. package/dist/program-CcdJAeR0.js +31554 -0
  28. package/dist/program-CcdJAeR0.js.map +1 -0
  29. package/dist/provider-classes-CZnJcera.js +55542 -0
  30. package/dist/provider-classes-CZnJcera.js.map +1 -0
  31. package/dist/rds-provider-YcTAhlgr.js +1749 -0
  32. package/dist/rds-provider-YcTAhlgr.js.map +1 -0
  33. package/dist/version-h1THYe6R.js +11 -0
  34. package/dist/version-h1THYe6R.js.map +1 -0
  35. package/package.json +139 -3
@@ -0,0 +1,2343 @@
1
+ import { r as getAwsClients } from "./aws-clients-DWpHZk2l.js";
2
+ import { $ as safeMsg, H as STACK_REF_MAX_CODE_POINTS, J as displaySafe, K as displayAwsMessage, Q as plainIdentOr, U as UNRENDERABLE, V as SECRET_REF_MAX_CODE_POINTS, X as isPasteableIdent, Y as displayStackName, Z as isPlainIdent, a as safeStringify, i as isAwsAuthoredFailure, k as getCurrentStackName, n as describeAwsFailure, nt as truncateCodePoints, q as displayIdent, s as getLogger, t as VERBOSE_POINTER } from "./aws-failure-text-DiHodi76.js";
3
+ import { c as ambientRegion, n as awsClientDefaults, r as getAssumedRoleCredentials } from "./aws-client-defaults-BLXFsfMa.js";
4
+ import { AsyncLocalStorage } from "node:async_hooks";
5
+
6
+ // src/deployment/retryable-errors/patterns.ts
7
+ const IAM_PROPAGATION_ERROR_MESSAGE_PATTERNS = [
8
+ "cannot be assumed",
9
+ "Firehose is unable to assume role",
10
+ "is unable to assume provided role",
11
+ "is unable to assume the role",
12
+ "security token included in the request is invalid. (Service:",
13
+ "Received Exception while reading from provided stream. The security token included in the request is invalid",
14
+ "role defined for the function",
15
+ "not authorized to perform",
16
+ "execution role",
17
+ "trust policy",
18
+ "Role validation failed",
19
+ "does not have required permissions",
20
+ "Trusted Entity",
21
+ "Invalid principal in policy",
22
+ "Verify in IAM that the role has adequate trust relationships",
23
+ "The user with name",
24
+ "Policy Error: PrincipalNotFound",
25
+ "Invalid value for the parameter Policy",
26
+ "required permissions for: ENHANCED_MONITORING",
27
+ "Caught ServiceAccessDeniedException",
28
+ "The operator role is invalid or doesn't have sufficient permissions",
29
+ "doesn't have permission to perform ec2:DescribeSecurityGroups",
30
+ "One or more security group IDs are invalid",
31
+ "permissions required to assume the role",
32
+ "does not have a trust relationship allowing",
33
+ "authorized to assume the provided role",
34
+ "not authorized to access the Log Destination",
35
+ "Cannot access stream",
36
+ "Please ensure the role can perform",
37
+ "KMS key is invalid for CreateGrant",
38
+ "Policy contains a statement with one or more invalid principals",
39
+ "Invalid IAM Instance Profile",
40
+ "Invalid InstanceProfile",
41
+ "Failed to authorize instance profile",
42
+ "is not a valid role to allow SNS",
43
+ "Could not assume the infrastructure role",
44
+ "AWS was not able to validate the provided access credentials (Service: Ecs,"
45
+ ];
46
+ const OTHER_TRANSIENT_ERROR_MESSAGE_PATTERNS = [
47
+ "currently in the following state: Pending",
48
+ "has dependencies and cannot be deleted",
49
+ "can't be deleted since it has",
50
+ "DependencyViolation",
51
+ "does not exist",
52
+ "Schema is currently being altered",
53
+ "scheduled for deletion",
54
+ "Could not deliver test message",
55
+ "concurrent update operation",
56
+ "because it is in use",
57
+ "Rate exceeded",
58
+ "is marked disabled for mutation",
59
+ "There is an operation running on the Cluster",
60
+ "Unable to complete operation due to concurrent modification"
61
+ ];
62
+ const NAME_COOLDOWN_ERROR_MESSAGE_PATTERNS = [
63
+ "QueueDeletedRecently",
64
+ "wait 60 seconds",
65
+ "StateMachineDeleting",
66
+ "State Machine is being deleted",
67
+ "conflicting conditional operation"
68
+ ];
69
+ const RETRYABLE_ERROR_MESSAGE_PATTERNS = [
70
+ ...IAM_PROPAGATION_ERROR_MESSAGE_PATTERNS,
71
+ ...OTHER_TRANSIENT_ERROR_MESSAGE_PATTERNS,
72
+ ...NAME_COOLDOWN_ERROR_MESSAGE_PATTERNS
73
+ ];
74
+ const RETRYABLE_HTTP_STATUS_CODES = /* @__PURE__ */ new Set([429, 503]);
75
+
76
+ // src/deployment/retryable-errors/marks.ts
77
+ const THROTTLING_ERROR_NAMES = /* @__PURE__ */ new Set([
78
+ "BandwidthLimitExceeded",
79
+ "EC2ThrottledException",
80
+ "LimitExceededException",
81
+ "PriorRequestNotComplete",
82
+ "ProvisionedThroughputExceededException",
83
+ "RequestLimitExceeded",
84
+ "RequestThrottled",
85
+ "RequestThrottledException",
86
+ "SlowDown",
87
+ "ThrottledException",
88
+ "Throttling",
89
+ "ThrottlingException",
90
+ "TooManyRequestsException",
91
+ "TransactionInProgressException"
92
+ ]);
93
+ const NON_RETRYABLE_MARKER = Symbol.for("cdkd.nonRetryable");
94
+ const MAX_CAUSE_CHAIN_DEPTH = 5;
95
+ const REDACTED_CAUSE_MARKER = Symbol.for("cdkd.redactedCause");
96
+ function markRedactedCause(error) {
97
+ if (!Object.isExtensible(error)) return error;
98
+ Object.defineProperty(error, REDACTED_CAUSE_MARKER, {
99
+ value: true,
100
+ enumerable: false,
101
+ configurable: true,
102
+ writable: false
103
+ });
104
+ return error;
105
+ }
106
+ function wrapMaskedAwsError(mask, error, build) {
107
+ const raw = describeAwsFailure(error).detail;
108
+ const masked = mask(raw);
109
+ const wrapped = build(masked);
110
+ return masked === raw ? wrapped : markRedactedCause(wrapped);
111
+ }
112
+ function hasRedactedCause(error) {
113
+ let current = error;
114
+ for (let depth = 0; depth < 5 && current != null; depth++) {
115
+ if (typeof current === "object" || typeof current === "function") {
116
+ if (current[REDACTED_CAUSE_MARKER] === true) return true;
117
+ }
118
+ current = current.cause;
119
+ }
120
+ return false;
121
+ }
122
+ function markNonRetryable(error) {
123
+ if (!Object.isExtensible(error)) return error;
124
+ Object.defineProperty(error, NON_RETRYABLE_MARKER, {
125
+ value: true,
126
+ enumerable: false,
127
+ configurable: true,
128
+ writable: false
129
+ });
130
+ return error;
131
+ }
132
+ function isMarkedNonRetryable(error) {
133
+ let current = error;
134
+ for (let depth = 0; depth < 5 && current != null; depth++) {
135
+ if (typeof current === "object" || typeof current === "function") {
136
+ if (current[NON_RETRYABLE_MARKER] === true) return true;
137
+ }
138
+ current = current.cause;
139
+ }
140
+ return false;
141
+ }
142
+ function isThrottlingError(error) {
143
+ let current = error;
144
+ for (let depth = 0; depth < 5 && current != null; depth++) {
145
+ const name = current.name;
146
+ if (typeof name === "string" && THROTTLING_ERROR_NAMES.has(name)) return true;
147
+ const status = current.$metadata?.httpStatusCode;
148
+ if (status !== void 0 && RETRYABLE_HTTP_STATUS_CODES.has(status)) return true;
149
+ current = current.cause;
150
+ }
151
+ return false;
152
+ }
153
+
154
+ // src/deployment/retryable-errors/transient.ts
155
+ const TRANSIENT_SERVER_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([
156
+ 500,
157
+ 502,
158
+ 503,
159
+ 504
160
+ ]);
161
+ function isTransientServerError(error) {
162
+ let current = error;
163
+ for (let depth = 0; depth < 5 && current != null; depth++) {
164
+ const status = current.$metadata?.httpStatusCode;
165
+ if (status !== void 0 && TRANSIENT_SERVER_ERROR_STATUS_CODES.has(status)) return true;
166
+ current = current.cause;
167
+ }
168
+ return false;
169
+ }
170
+ const AMBIGUOUS_SOCKET_ERROR_CODES = /* @__PURE__ */ new Set([
171
+ "ECONNRESET",
172
+ "EPIPE",
173
+ "ETIMEDOUT",
174
+ "EADDRNOTAVAIL"
175
+ ]);
176
+ const AMBIGUOUS_CC_HANDLER_ERROR_CODES = /* @__PURE__ */ new Set([
177
+ "InternalFailure",
178
+ "HandlerInternalFailure",
179
+ "ServiceInternalError",
180
+ "NetworkFailure",
181
+ "NotStabilized",
182
+ "ServiceTimeout",
183
+ "GeneralServiceException",
184
+ "Throttling"
185
+ ]);
186
+ function isAmbiguousCcHandlerErrorCode(code) {
187
+ return code !== void 0 && AMBIGUOUS_CC_HANDLER_ERROR_CODES.has(code);
188
+ }
189
+ function isAmbiguousOutcomeError(error) {
190
+ try {
191
+ let current = error;
192
+ for (let depth = 0; depth < 5 && typeof current === "object" && current !== null; depth++) {
193
+ const link = current;
194
+ if (typeof link.name === "string" && THROTTLING_ERROR_NAMES.has(link.name)) return false;
195
+ if (link.$retryable?.throttling === true) return false;
196
+ const status = link.$metadata?.httpStatusCode;
197
+ if (typeof status === "number" && TRANSIENT_SERVER_ERROR_STATUS_CODES.has(status)) return true;
198
+ if (typeof link.code === "string" && AMBIGUOUS_SOCKET_ERROR_CODES.has(link.code)) return true;
199
+ if (link.name === "TimeoutError") return true;
200
+ if (typeof link.ccErrorCode === "string" && AMBIGUOUS_CC_HANDLER_ERROR_CODES.has(link.ccErrorCode)) return true;
201
+ current = link.cause;
202
+ }
203
+ } catch {}
204
+ return false;
205
+ }
206
+ function describeRetryClassificationSignals(error) {
207
+ let current = error;
208
+ let deepestName;
209
+ let sawMetadata = false;
210
+ let metadataName;
211
+ let metadataRequestId;
212
+ for (let depth = 0; depth < 5 && current != null; depth++) {
213
+ const name = current.name;
214
+ if (depth > 0 && typeof name === "string" && name !== "") deepestName = name;
215
+ const metadata = current.$metadata;
216
+ if (metadata != null && typeof metadata === "object" && !Array.isArray(metadata)) {
217
+ const { httpStatusCode, requestId } = metadata;
218
+ const linkName = typeof name === "string" && name !== "" ? name : void 0;
219
+ if (!sawMetadata) {
220
+ sawMetadata = true;
221
+ metadataName = linkName;
222
+ metadataRequestId = typeof requestId === "string" && requestId !== "" ? requestId : void 0;
223
+ }
224
+ if (typeof httpStatusCode === "number") return {
225
+ name: linkName ?? deepestName,
226
+ httpStatusCode,
227
+ requestId: (typeof requestId === "string" && requestId !== "" ? requestId : void 0) ?? metadataRequestId,
228
+ noMetadata: false
229
+ };
230
+ }
231
+ current = current.cause;
232
+ }
233
+ return {
234
+ name: metadataName ?? deepestName,
235
+ requestId: metadataRequestId,
236
+ noMetadata: !sawMetadata
237
+ };
238
+ }
239
+ function formatRetryClassificationSignals(error) {
240
+ const signals = describeRetryClassificationSignals(error);
241
+ if (signals.name === void 0 && signals.httpStatusCode === void 0 && signals.requestId === void 0) return "";
242
+ const parts = [];
243
+ if (signals.name !== void 0) parts.push(`name=${signals.name}`);
244
+ if (signals.httpStatusCode !== void 0) parts.push(`http=${signals.httpStatusCode}`);
245
+ if (signals.requestId !== void 0) parts.push(`requestId=${signals.requestId}`);
246
+ if (signals.noMetadata) parts.push("no-$metadata");
247
+ return ` [${parts.join(" ")}]`;
248
+ }
249
+ function isRetryableTransientError(error, message) {
250
+ if (isMarkedNonRetryable(error)) return false;
251
+ if (isExplicitDenyError(message)) return false;
252
+ if (isThrottlingError(error)) return true;
253
+ if (isTransientServerError(error)) return true;
254
+ return RETRYABLE_ERROR_MESSAGE_PATTERNS.some((p) => message.includes(p));
255
+ }
256
+ function retryClassificationText(error) {
257
+ const top = describeAwsFailure(error).detail;
258
+ if (!hasRedactedCause(error)) return top;
259
+ const parts = [];
260
+ const seen = /* @__PURE__ */ new Set();
261
+ let current = error;
262
+ for (let depth = 0; depth < 5 && current != null && !seen.has(current); depth++) {
263
+ seen.add(current);
264
+ if (current instanceof Error) {
265
+ if (current.message) parts.push(current.message);
266
+ } else try {
267
+ parts.push(String(current));
268
+ } catch {}
269
+ current = current?.cause;
270
+ }
271
+ return parts.join("\n");
272
+ }
273
+ function isIamPropagationError(message) {
274
+ if (isExplicitDenyError(message)) return false;
275
+ return IAM_PROPAGATION_ERROR_MESSAGE_PATTERNS.some((p) => message.includes(p));
276
+ }
277
+ const EXPLICIT_DENY_PHRASE = "with an explicit deny";
278
+ const EXPLICIT_DENY_POLICY_KINDS = [
279
+ "service control policy",
280
+ "resource control policy",
281
+ "identity-based policy",
282
+ "resource-based policy",
283
+ "permissions boundary",
284
+ "session policy",
285
+ "VPC endpoint policy",
286
+ "role trust policy"
287
+ ];
288
+ function isExplicitDenyError(message) {
289
+ return message.includes(EXPLICIT_DENY_PHRASE);
290
+ }
291
+ function explicitDenyPolicyKind(message) {
292
+ const at = message.indexOf(EXPLICIT_DENY_PHRASE);
293
+ if (at < 0) return void 0;
294
+ const rest = message.slice(at + 21);
295
+ const article = /^ in (?:a|an|the) /.exec(rest);
296
+ if (article === null) return void 0;
297
+ const tail = rest.slice(article[0].length);
298
+ return EXPLICIT_DENY_POLICY_KINDS.find((kind) => tail.startsWith(kind));
299
+ }
300
+
301
+ // src/utils/regexp.ts
302
+ function escapeRegExp(value) {
303
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
304
+ }
305
+ function stripControlChars(value) {
306
+ return value.replace(/[\u0000-\u001f\u007f-\u009f\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "");
307
+ }
308
+ function stripForDisplay(value) {
309
+ return displaySafe(stripControlChars(value));
310
+ }
311
+
312
+ // src/deployment/retryable-errors/name-collision.ts
313
+ const ALREADY_EXISTS_CODE = /(?<![-:/])\b[A-Za-z0-9]*AlreadyExists(?:Exception|Fault)?\b(?!-)/;
314
+ function isNameCollisionError(message) {
315
+ return /(?<!\b(?:must|not|should|may|cannot)\s)already exists?\b/i.test(message) || ALREADY_EXISTS_CODE.test(message);
316
+ }
317
+ function relayedNameCollision(withheldText) {
318
+ return isNameCollisionError(withheldText) ? " AWS reports that it already exists." : "";
319
+ }
320
+ const NAME_COLLISION_ERROR_NAMES = /* @__PURE__ */ new Set([
321
+ "DuplicateTargetGroupNameException",
322
+ "DuplicateLoadBalancerNameException",
323
+ "DuplicateTrustStoreNameException"
324
+ ]);
325
+ const NAME_COLLISION_MARKER = Symbol.for("cdkd.nameCollision");
326
+ function markNameCollision(error) {
327
+ if (!Object.isExtensible(error)) return error;
328
+ Object.defineProperty(error, NAME_COLLISION_MARKER, {
329
+ value: true,
330
+ enumerable: false,
331
+ configurable: true
332
+ });
333
+ return error;
334
+ }
335
+ const REPLAY_MAY_COLLIDE_MARKER = Symbol.for("cdkd.replayMayCollide");
336
+ function markReplayMayCollide(error) {
337
+ try {
338
+ if (typeof error !== "object" && typeof error !== "function" || error === null || !Object.isExtensible(error)) return error;
339
+ Object.defineProperty(error, REPLAY_MAY_COLLIDE_MARKER, {
340
+ value: true,
341
+ enumerable: false,
342
+ configurable: true,
343
+ writable: false
344
+ });
345
+ } catch {}
346
+ return error;
347
+ }
348
+ function hasReplayMayCollide(error) {
349
+ try {
350
+ let current = error;
351
+ for (let depth = 0; depth < 5 && (typeof current === "object" || typeof current === "function") && current !== null; depth++) {
352
+ if (Object.getOwnPropertyDescriptor(current, REPLAY_MAY_COLLIDE_MARKER)?.value === true) return true;
353
+ current = current.cause;
354
+ }
355
+ } catch {}
356
+ return false;
357
+ }
358
+ function isNameCollisionErrorFrom(error, logicalId) {
359
+ if (hasReplayMayCollide(error)) return false;
360
+ return relaysNameCollision(error, logicalId, () => false);
361
+ }
362
+ function isReplayedNameCollisionFrom(error, logicalId, isRetryAnchor) {
363
+ if (!hasReplayMayCollide(error)) return false;
364
+ return relaysNameCollision(error, logicalId, isRetryAnchor);
365
+ }
366
+ function relaysNameCollision(error, logicalId, isRetryAnchor) {
367
+ const topRelaysIt = error instanceof Error && typeof error.message === "string" && isNameCollisionError(error.message);
368
+ let current = error;
369
+ for (let depth = 0; depth < 5 && current != null; depth++) {
370
+ const link = current;
371
+ if (typeof link.logicalId === "string" && link.logicalId !== logicalId && !isRetryAnchor(current)) return false;
372
+ if (typeof link.name === "string" && NAME_COLLISION_ERROR_NAMES.has(link.name)) return true;
373
+ if (link.ccErrorCode === "AlreadyExists") return true;
374
+ if (link[NAME_COLLISION_MARKER] === true) return true;
375
+ if (topRelaysIt && current instanceof Error && isAwsAuthoredFailure(current) && typeof link.message === "string" && isNameCollisionError(link.message)) return true;
376
+ current = link.cause;
377
+ }
378
+ return false;
379
+ }
380
+ function isNameCooldownError(message) {
381
+ return NAME_COOLDOWN_ERROR_MESSAGE_PATTERNS.some((p) => message.includes(p));
382
+ }
383
+ const REDACTED_ALREADY_EXISTS_CLASS = new RegExp(`\\b[A-Za-z0-9]*AlreadyExists(?:Exception|Fault)?(?=\\. ${escapeRegExp(VERBOSE_POINTER)})`, "g");
384
+ function isRecreateRetryableError(message, error) {
385
+ return isNameCollisionError(error !== void 0 && hasRedactedCause(error) ? message.replace(REDACTED_ALREADY_EXISTS_CLASS, "") : message) || isNameCooldownError(message);
386
+ }
387
+
388
+ // src/deployment/retryable-errors/update-unsupported.ts
389
+ const CC_UNSUPPORTED_ACTION_ERROR_NAME = "UnsupportedActionException";
390
+ function isUpdateUnsupportedError(error, logicalId) {
391
+ if (error?.name === "ResourceUpdateNotSupportedError") return false;
392
+ let current = error;
393
+ for (let depth = 0; current !== null && current !== void 0 && depth < 5; depth++) {
394
+ const link = current;
395
+ if (typeof link.logicalId === "string" && link.logicalId !== logicalId) return false;
396
+ if (link.name === "UnsupportedActionException") return true;
397
+ if (link.ccErrorCode === "UnsupportedActionException" && link.ccOperation === "UPDATE") return true;
398
+ current = link.cause;
399
+ }
400
+ return false;
401
+ }
402
+
403
+ // src/utils/pasteable-run-context.ts
404
+ let runFlags = {};
405
+ let verbFlags;
406
+ function setPasteableRunFlags(flags) {
407
+ runFlags = { ...flags };
408
+ }
409
+ function pasteableRunFlags() {
410
+ return runFlags;
411
+ }
412
+ function setPasteableVerbFlags(map) {
413
+ verbFlags = map;
414
+ }
415
+ function pasteableVerbFlags(verb) {
416
+ return verbFlags?.get(verb);
417
+ }
418
+
419
+ // src/utils/pasteable-command.ts
420
+ const SHELL_ACTIVE = /[\s'"`$;&|<>()*?[\]{}!#~\\^=%,]/;
421
+ const PASTE_ARG_UNSAFE = /[\s'"`$;&|<>()\\*?[\]{}!]|^[#=]|(?:^|[=:])~/;
422
+ function shellQuote(value) {
423
+ return /^[A-Za-z0-9._/@:+-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
424
+ }
425
+ const CLAUSE_BREAK = /: | -- | — |[.!?]\s/;
426
+ function hasClauseBreak(text) {
427
+ return CLAUSE_BREAK.test(text);
428
+ }
429
+ const UNSHOWABLE_VALUE = "a value that cannot be shown safely here";
430
+ function shellBoundedDisplay(shown) {
431
+ if (hasClauseBreak(shown)) return UNSHOWABLE_VALUE;
432
+ return shown.startsWith("\"") || shown === "<unrenderable>" ? shellQuote(shown) : shown;
433
+ }
434
+ function commandHole(name) {
435
+ return `'<${name}>'`;
436
+ }
437
+ const SHELL_ACTIVE_WHY = "holds whitespace or a character a shell treats specially, and only a value that stays inert without its quotes is named in a command here, since text pasted before the command can undo the quotes and leave it to run as shell";
438
+ function plainOrDescribed(value, what) {
439
+ return isPasteableIdent(value) ? value : `a ${what} that is not a plain identifier`;
440
+ }
441
+ function quotedOrDescribed(value, what) {
442
+ return isPasteableIdent(value) ? `'${value}'` : plainOrDescribed(value, what);
443
+ }
444
+ function physicalIdShownBesideCommand(value, opts) {
445
+ if (typeof value !== "string" || value.startsWith("-")) return void 0;
446
+ const plain = (text) => isInertUnquoted(text) && displayIdent(text, { maxCodePoints: 613 }) === text;
447
+ if (plain(value)) return value;
448
+ const token = opts?.maskToken;
449
+ if (token === void 0 || token === "" || !value.includes(token)) return void 0;
450
+ const rendered = displayIdent(value, { maxCodePoints: 613 });
451
+ return value.split(token).every((part) => part === "" || plain(part)) && rendered === JSON.stringify(value) ? rendered : void 0;
452
+ }
453
+ function isInertUnquoted(value) {
454
+ return !PASTE_ARG_UNSAFE.test(value);
455
+ }
456
+ function withholdReason(value, opts) {
457
+ if (value === "") return "empty";
458
+ const safe = displaySafe(value, { asciiOnly: true });
459
+ if (safe !== value) return "altered";
460
+ const cap = opts?.maxCodePoints !== void 0 && Number.isFinite(opts.maxCodePoints) ? Math.max(1, opts.maxCodePoints) : STACK_REF_MAX_CODE_POINTS;
461
+ if (truncateCodePoints(safe, cap).truncated) return "too-long";
462
+ if (value.startsWith("-")) return "option-shaped";
463
+ if (opts?.patternMatched === true && (value.includes("*") || value.includes("/"))) return "pattern-shaped";
464
+ if (opts?.plainIdent === true) return isPasteableIdent(value) ? void 0 : "not-plain";
465
+ if (!isInertUnquoted(value)) return "shell-active";
466
+ }
467
+ function pasteableCommand(verb, args = [], extraFlags = []) {
468
+ let exact = true;
469
+ const withheld = [];
470
+ const parts = [verb];
471
+ for (const arg of args) {
472
+ if ("literal" in arg) {
473
+ parts.push(arg.literal);
474
+ continue;
475
+ }
476
+ if (!("value" in arg)) {
477
+ if ("flag" in arg) parts.push(arg.flag);
478
+ parts.push(commandHole(arg.hole));
479
+ exact = false;
480
+ continue;
481
+ }
482
+ const reason = withholdReason(arg.value, arg.opts);
483
+ let rendered;
484
+ if (reason === void 0) rendered = shellQuote(arg.value);
485
+ else {
486
+ rendered = commandHole(arg.hole);
487
+ exact = false;
488
+ withheld.push({
489
+ hole: arg.hole,
490
+ reason
491
+ });
492
+ }
493
+ if ("flag" in arg) parts.push(arg.flag);
494
+ parts.push(rendered);
495
+ }
496
+ parts.push(...extraFlags);
497
+ parts.push(...runFlagWords(verb, args, extraFlags));
498
+ return {
499
+ command: parts.join(" "),
500
+ exact,
501
+ withheld
502
+ };
503
+ }
504
+ function runFlagWords(verb, args, extraFlags) {
505
+ const accepted = pasteableVerbFlags(verb);
506
+ if (accepted === void 0) return [];
507
+ const present = /* @__PURE__ */ new Set();
508
+ for (const arg of args) if ("flag" in arg) present.add(arg.flag);
509
+ else if ("literal" in arg) present.add(arg.literal.split(" ")[0]);
510
+ for (const f of extraFlags) present.add(f.split(" ")[0]);
511
+ const run = pasteableRunFlags();
512
+ const out = [];
513
+ const add = (flag, value, hole, allowEmpty) => {
514
+ if (value === void 0 || value === "" && !allowEmpty) return;
515
+ if (!accepted.has(flag) || present.has(flag)) return;
516
+ const inert = displaySafe(value) === value && !value.startsWith("-") && !SHELL_ACTIVE.test(value) && !/\p{Cf}/u.test(value) && !truncateCodePoints(value, 1152).truncated;
517
+ out.push(`${flag} ${value === "" || inert ? shellQuote(value) : commandHole(hole)}`);
518
+ };
519
+ add("--profile", run.profile, "profile", false);
520
+ add("--state-bucket", run.stateBucket, "bucket", false);
521
+ add("--state-prefix", run.statePrefix, "prefix", true);
522
+ if (run.roleArn === true && accepted.has("--role-arn") && !present.has("--role-arn")) out.push(`--role-arn ${commandHole("role-arn")}`);
523
+ return out;
524
+ }
525
+ function withheldTargetClause(built, hole, verb, subject = "This record's name") {
526
+ const reason = built.withheld.find((w) => w.hole === hole)?.reason;
527
+ if (reason === void 0) return "";
528
+ let why;
529
+ switch (reason) {
530
+ case "altered":
531
+ why = `does NOT render exactly, so another record may render identically`;
532
+ break;
533
+ case "empty":
534
+ why = `is empty`;
535
+ break;
536
+ case "too-long":
537
+ why = `is too long to print`;
538
+ break;
539
+ case "option-shaped":
540
+ why = `begins with a '-', so it is not safe to print as an argument to '${verb}' — a name like '--all' is parsed as the FLAG and targets every stack`;
541
+ break;
542
+ case "pattern-shaped":
543
+ why = `would be read as a PATTERN by '${verb}', which can match other stacks`;
544
+ break;
545
+ case "shell-active":
546
+ why = SHELL_ACTIVE_WHY;
547
+ break;
548
+ case "not-plain":
549
+ why = "is not a plain identifier (a letter or digit, then letters, digits, '~', '_', '.' or '-'), the only shape named in a command here, since a name outside it can run as shell or read as a line of this message once the terminal wraps";
550
+ break;
551
+ default: throw new Error(`withheldTargetClause: unhandled WithholdReason ${String(reason)}`);
552
+ }
553
+ return ` ${subject} ${why} — so it is not named in the command below; list the records as stored with 'cdkd state list --long' and act on the one whose key matches.`;
554
+ }
555
+
556
+ // src/utils/resource-type-shape.ts
557
+ const TIMEOUT_FLAG_RESOURCE_TYPE = /^[A-Z][A-Za-z0-9]+::[A-Z][A-Za-z0-9]+::[A-Z][A-Za-z0-9]+$/;
558
+ const RESOURCE_TYPE_MAX_LENGTH = 204;
559
+ const RESOURCE_TYPE_SHAPE$1 = /^(?:[A-Z][A-Za-z0-9]+(?:::[A-Z][A-Za-z0-9]+)+|Custom::[A-Za-z0-9_@-]+)$/;
560
+ function hasResourceTypeShape(value) {
561
+ return value.length <= 204 && RESOURCE_TYPE_SHAPE$1.test(value);
562
+ }
563
+
564
+ // src/utils/error-handler.ts
565
+ var CdkdError = class CdkdError extends Error {
566
+ code;
567
+ cause;
568
+ constructor(message, code, cause) {
569
+ super(message);
570
+ this.code = code;
571
+ this.cause = cause;
572
+ this.name = "CdkdError";
573
+ Object.setPrototypeOf(this, CdkdError.prototype);
574
+ }
575
+ };
576
+ var AccountIdUnavailableError = class AccountIdUnavailableError extends CdkdError {
577
+ constructor(message) {
578
+ super(message, "ACCOUNT_ID_UNAVAILABLE");
579
+ this.name = "AccountIdUnavailableError";
580
+ Object.setPrototypeOf(this, AccountIdUnavailableError.prototype);
581
+ }
582
+ };
583
+ var StateError = class StateError extends CdkdError {
584
+ constructor(message, cause) {
585
+ super(message, "STATE_ERROR", cause);
586
+ this.name = "StateError";
587
+ Object.setPrototypeOf(this, StateError.prototype);
588
+ }
589
+ };
590
+ var LockError = class LockError extends CdkdError {
591
+ constructor(message, cause) {
592
+ super(message, "LOCK_ERROR", cause);
593
+ this.name = "LockError";
594
+ Object.setPrototypeOf(this, LockError.prototype);
595
+ }
596
+ };
597
+ var SynthesisError = class SynthesisError extends CdkdError {
598
+ constructor(message, cause) {
599
+ super(message, "SYNTHESIS_ERROR", cause);
600
+ this.name = "SynthesisError";
601
+ Object.setPrototypeOf(this, SynthesisError.prototype);
602
+ }
603
+ };
604
+ var DeployCancelledError = class DeployCancelledError extends CdkdError {
605
+ constructor(message = "Deployment cancelled by user") {
606
+ super(message, "DEPLOY_CANCELLED");
607
+ this.name = "DeployCancelledError";
608
+ Object.setPrototypeOf(this, DeployCancelledError.prototype);
609
+ }
610
+ };
611
+ var DeploymentNotApprovedError = class DeploymentNotApprovedError extends CdkdError {
612
+ softAbort = true;
613
+ constructor(message) {
614
+ super(message, "DEPLOY_NOT_APPROVED");
615
+ this.name = "DeploymentNotApprovedError";
616
+ Object.setPrototypeOf(this, DeploymentNotApprovedError.prototype);
617
+ }
618
+ };
619
+ function isSoftAbort(error) {
620
+ return error instanceof Error && error.softAbort === true;
621
+ }
622
+ var AssetError = class AssetError extends CdkdError {
623
+ constructor(message, cause) {
624
+ super(message, "ASSET_ERROR", cause);
625
+ this.name = "AssetError";
626
+ Object.setPrototypeOf(this, AssetError.prototype);
627
+ }
628
+ };
629
+ var LocalInvokeBuildError = class LocalInvokeBuildError extends CdkdError {
630
+ constructor(message, cause) {
631
+ super(message, "LOCAL_INVOKE_BUILD_ERROR", cause);
632
+ this.name = "LocalInvokeBuildError";
633
+ Object.setPrototypeOf(this, LocalInvokeBuildError.prototype);
634
+ }
635
+ };
636
+ var ProvisioningError = class ProvisioningError extends CdkdError {
637
+ resourceType;
638
+ logicalId;
639
+ physicalId;
640
+ constructor(message, resourceType, logicalId, physicalId, cause) {
641
+ super(message, "PROVISIONING_ERROR", cause);
642
+ this.resourceType = resourceType;
643
+ this.logicalId = logicalId;
644
+ this.physicalId = physicalId;
645
+ this.name = "ProvisioningError";
646
+ Object.setPrototypeOf(this, ProvisioningError.prototype);
647
+ }
648
+ };
649
+ var ResourceTimeoutError = class ResourceTimeoutError extends CdkdError {
650
+ logicalId;
651
+ resourceType;
652
+ region;
653
+ elapsedMs;
654
+ operation;
655
+ timeoutMs;
656
+ constructor(logicalId, resourceType, region, elapsedMs, operation, timeoutMs) {
657
+ const elapsedLabel = formatDuration$1(elapsedMs);
658
+ const timeoutLabel = formatDuration$1(timeoutMs);
659
+ const shownId = plainOrDescribed(logicalId, "logical id");
660
+ const shownType = hasResourceTypeShape(resourceType) ? resourceType : "a resource type that is not printable as typed";
661
+ const remedy = hasResourceTypeShape(resourceType) && TIMEOUT_FLAG_RESOURCE_TYPE.test(resourceType) ? safeMsg`slow ENI provisioning. Re-run with --resource-timeout ${resourceType}='<DURATION>'\n` + "to bump the budget for this resource type only, or --verbose to see the\n" : "slow ENI provisioning. Re-run with a larger --resource-timeout '<DURATION>',\nor --verbose to see the\n";
662
+ super(safeMsg`Resource ${shownId} (${shownType}) in ${region} timed out after ${timeoutLabel} during ${operation} (elapsed ${elapsedLabel}).\n` + "This may indicate a stuck Cloud Control polling loop, hung Custom Resource, or\n" + remedy + "underlying provider activity.", "RESOURCE_TIMEOUT");
663
+ this.logicalId = logicalId;
664
+ this.resourceType = resourceType;
665
+ this.region = region;
666
+ this.elapsedMs = elapsedMs;
667
+ this.operation = operation;
668
+ this.timeoutMs = timeoutMs;
669
+ this.name = "ResourceTimeoutError";
670
+ Object.setPrototypeOf(this, ResourceTimeoutError.prototype);
671
+ }
672
+ };
673
+ function formatDuration$1(ms) {
674
+ if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
675
+ const totalMinutes = Math.round(ms / 6e4);
676
+ if (totalMinutes < 60) return `${totalMinutes}m`;
677
+ const hours = Math.floor(totalMinutes / 60);
678
+ const minutes = totalMinutes % 60;
679
+ return minutes === 0 ? `${hours}h` : `${hours}h${minutes}m`;
680
+ }
681
+ var IntrinsicResolutionRefusalError = class IntrinsicResolutionRefusalError extends CdkdError {
682
+ constructor(message, cause, code = "INTRINSIC_RESOLUTION_REFUSAL") {
683
+ super(message, code, cause);
684
+ this.name = "IntrinsicResolutionRefusalError";
685
+ Object.setPrototypeOf(this, IntrinsicResolutionRefusalError.prototype);
686
+ }
687
+ };
688
+ var CrossAccountSecretRefusalError = class CrossAccountSecretRefusalError extends IntrinsicResolutionRefusalError {
689
+ constructor(message, cause) {
690
+ super(message, cause, "INTRINSIC_RESOLUTION_REFUSAL_CROSS_ACCOUNT_SECRET");
691
+ this.name = "CrossAccountSecretRefusalError";
692
+ Object.setPrototypeOf(this, CrossAccountSecretRefusalError.prototype);
693
+ }
694
+ };
695
+ var MalformedProducerRecordRefusalError = class MalformedProducerRecordRefusalError extends IntrinsicResolutionRefusalError {
696
+ constructor(message, cause) {
697
+ super(message, cause, "INTRINSIC_RESOLUTION_REFUSAL_MALFORMED_PRODUCER_RECORD");
698
+ this.name = "MalformedProducerRecordRefusalError";
699
+ Object.setPrototypeOf(this, MalformedProducerRecordRefusalError.prototype);
700
+ }
701
+ };
702
+ var DynamicReferenceRegionAmbiguousError = class DynamicReferenceRegionAmbiguousError extends IntrinsicResolutionRefusalError {
703
+ constructor(message, cause) {
704
+ super(message, cause, "DYNAMIC_REFERENCE_REGION_AMBIGUOUS");
705
+ this.name = "DynamicReferenceRegionAmbiguousError";
706
+ Object.setPrototypeOf(this, DynamicReferenceRegionAmbiguousError.prototype);
707
+ }
708
+ };
709
+ var DependencyError = class DependencyError extends CdkdError {
710
+ constructor(message, cause) {
711
+ super(message, "DEPENDENCY_ERROR", cause);
712
+ this.name = "DependencyError";
713
+ Object.setPrototypeOf(this, DependencyError.prototype);
714
+ }
715
+ };
716
+ var ConfigError = class ConfigError extends CdkdError {
717
+ constructor(message, cause) {
718
+ super(message, "CONFIG_ERROR", cause);
719
+ this.name = "ConfigError";
720
+ Object.setPrototypeOf(this, ConfigError.prototype);
721
+ }
722
+ };
723
+ var PartialFailureError = class PartialFailureError extends CdkdError {
724
+ exitCode = 2;
725
+ constructor(message, cause) {
726
+ super(message, "PARTIAL_FAILURE", cause);
727
+ this.name = "PartialFailureError";
728
+ Object.setPrototypeOf(this, PartialFailureError.prototype);
729
+ }
730
+ };
731
+ var ResourceUpdateNotSupportedError = class ResourceUpdateNotSupportedError extends CdkdError {
732
+ exitCode = 2;
733
+ resourceType;
734
+ logicalId;
735
+ suggestion;
736
+ constructor(resourceType, logicalId, suggestion, cause) {
737
+ super(`${resourceType} (${logicalId}) cannot be updated in place: ${suggestion ? suggestion : "use cdkd deploy with --replace, or change the resource definition to create a new version"}.`, "RESOURCE_UPDATE_NOT_SUPPORTED", cause);
738
+ this.resourceType = resourceType;
739
+ this.logicalId = logicalId;
740
+ this.suggestion = suggestion;
741
+ this.name = "ResourceUpdateNotSupportedError";
742
+ Object.setPrototypeOf(this, ResourceUpdateNotSupportedError.prototype);
743
+ markNonRetryable(this);
744
+ }
745
+ };
746
+ var StackTerminationProtectionError = class StackTerminationProtectionError extends CdkdError {
747
+ stackName;
748
+ constructor(stackName, cause, account, recorded) {
749
+ if (recorded !== void 0) {
750
+ const { command, root, stackRegion } = recorded;
751
+ const bypass = pasteableCommand(command, [
752
+ {
753
+ value: stackName,
754
+ hole: "stack",
755
+ opts: {
756
+ plainIdent: true,
757
+ ...command === "cdkd destroy" && { patternMatched: true }
758
+ }
759
+ },
760
+ ...stackRegion !== void 0 && command === "cdkd state destroy" ? [{
761
+ flag: "--stack-region",
762
+ value: stackRegion,
763
+ hole: "region",
764
+ opts: { plainIdent: true }
765
+ }] : [],
766
+ { literal: "--remove-protection" },
767
+ ...account?.args ?? []
768
+ ]);
769
+ super((root === void 0 ? `Stack ${quotedOrDescribed(stackName, "stack name")} was last deployed with terminationProtection: true and cannot be destroyed.` : `Stack ${quotedOrDescribed(stackName, "stack name")} is nested under ${quotedOrDescribed(root, "stack name")}, which was last deployed with terminationProtection: true, so it cannot be destroyed.`) + (command === "cdkd destroy" ? ` Redeploy it with the protection off, or pass --remove-protection to destroy it anyway;` : ` To destroy it anyway, pass --remove-protection;`) + ` the flag also turns off the deletion protection of each resource it deletes.` + withheldTargetClause(bypass, "stack", command, "This stack's name") + (account?.clause ? ` ${account.clause.trimEnd()}` : "") + `\nDestroy it with: ${bypass.command}`, "STACK_TERMINATION_PROTECTION", cause);
770
+ this.stackName = stackName;
771
+ this.name = "StackTerminationProtectionError";
772
+ Object.setPrototypeOf(this, StackTerminationProtectionError.prototype);
773
+ return;
774
+ }
775
+ const retry = pasteableCommand("cdkd destroy", [{
776
+ value: stackName,
777
+ hole: "stack",
778
+ opts: {
779
+ patternMatched: true,
780
+ plainIdent: true
781
+ }
782
+ }, ...account?.args ?? []]);
783
+ super(`Stack ${quotedOrDescribed(stackName, "stack name")} has terminationProtection: true and cannot be destroyed. Set terminationProtection: false in the CDK code, redeploy, then retry the destroy.` + withheldTargetClause(retry, "stack", "cdkd destroy", "This stack's name") + (account?.clause ? ` ${account.clause.trimEnd()}` : "") + `\nRetry with: ${retry.command}`, "STACK_TERMINATION_PROTECTION", cause);
784
+ this.stackName = stackName;
785
+ this.name = "StackTerminationProtectionError";
786
+ Object.setPrototypeOf(this, StackTerminationProtectionError.prototype);
787
+ }
788
+ };
789
+ var NestedStackChildDirectDestroyError = class NestedStackChildDirectDestroyError extends CdkdError {
790
+ stackName;
791
+ parentStack;
792
+ parentLogicalId;
793
+ constructor(stackName, parentStack, parentLogicalId, cause, account) {
794
+ const accountArgs = account?.args ?? [];
795
+ const logicalIdSuffix = parentLogicalId ? ` (parent's logical id: ${plainOrDescribed(parentLogicalId, "logical id")})` : "";
796
+ const cascade = pasteableCommand("cdkd destroy", [{
797
+ value: parentStack,
798
+ hole: "parent",
799
+ opts: {
800
+ patternMatched: true,
801
+ plainIdent: true
802
+ }
803
+ }, ...accountArgs]);
804
+ const childDestroy = pasteableCommand("cdkd state destroy", [{
805
+ value: stackName,
806
+ hole: "stack",
807
+ opts: { plainIdent: true }
808
+ }, ...accountArgs]);
809
+ const withheld = withheldTargetClause(cascade, "parent", "cdkd destroy", "The parent stack's name") + withheldTargetClause(childDestroy, "stack", "cdkd state destroy", "The child stack's name") + (account?.clause ? ` ${account.clause.trimEnd()}` : "");
810
+ const child = quotedOrDescribed(stackName, "stack name");
811
+ const parent = quotedOrDescribed(parentStack, "stack name");
812
+ super(`Stack ${child} is a nested child of ${parent}${logicalIdSuffix}; directly destroying a nested stack is not supported. Either cascade-delete this child along with its parent, or destroy the child on its own — which deletes its AWS resources and its record, and leaves the parent's reference dangling (the synth-free escape hatch).${withheld}\nCascade-delete with: ${cascade.command}\nDestroy the child alone with: ${childDestroy.command}`, "NESTED_STACK_CHILD_DIRECT_DESTROY", cause);
813
+ this.stackName = stackName;
814
+ this.parentStack = parentStack;
815
+ if (parentLogicalId !== void 0) this.parentLogicalId = parentLogicalId;
816
+ this.name = "NestedStackChildDirectDestroyError";
817
+ Object.setPrototypeOf(this, NestedStackChildDirectDestroyError.prototype);
818
+ }
819
+ };
820
+ function exportNamePhrase(exportName) {
821
+ const shown = plainIdentOr(exportName, "");
822
+ return shown ? `export '${shown}'` : "an export whose name is not a plain identifier";
823
+ }
824
+ var StackHasActiveImportsError = class StackHasActiveImportsError extends CdkdError {
825
+ exitCode = 2;
826
+ producerStack;
827
+ producerRegion;
828
+ consumers;
829
+ constructor(producerStack, producerRegion, consumers, cause) {
830
+ const lines = consumers.map((c) => ` - ${plainOrDescribed(c.consumerStack, "stack name")} (${plainOrDescribed(c.consumerRegion, "region")}): imports ${exportNamePhrase(c.exportName)}`);
831
+ super(`Cannot destroy stack ${quotedOrDescribed(producerStack, "stack name")} (${plainOrDescribed(producerRegion, "region")}): the following stacks still import its outputs via Fn::ImportValue:\n${lines.join("\n")}\n\nThis matches CloudFormation's strong-reference semantics — exports are\nprotected as long as a consumer references them.\n\nTo proceed:\n 1. Destroy the consumer first: cdkd destroy <consumer-stack>\n 2. Or remove the Fn::ImportValue from the consumer's template\n (e.g. inline the value, or refactor) and re-deploy the consumer,\n then retry this destroy.\n\nNote: cdkd's Fn::GetStackOutput intrinsic is a weak alternative that\ndoes NOT protect the producer — use it when you intentionally want\nthe producer to be deletable independently of consumers.`, "STACK_HAS_ACTIVE_IMPORTS", cause);
832
+ this.producerStack = producerStack;
833
+ this.producerRegion = producerRegion;
834
+ this.consumers = consumers;
835
+ this.name = "StackHasActiveImportsError";
836
+ Object.setPrototypeOf(this, StackHasActiveImportsError.prototype);
837
+ }
838
+ };
839
+ var LocalStartServiceError = class LocalStartServiceError extends CdkdError {
840
+ constructor(message, cause) {
841
+ super(message, "LOCAL_START_SERVICE_ERROR", cause);
842
+ this.name = "LocalStartServiceError";
843
+ Object.setPrototypeOf(this, LocalStartServiceError.prototype);
844
+ }
845
+ };
846
+ var MacroExpansionError = class MacroExpansionError extends CdkdError {
847
+ exitCode = 2;
848
+ constructor(message, cause) {
849
+ super(message, "MACRO_EXPANSION_ERROR", cause);
850
+ this.name = "MacroExpansionError";
851
+ Object.setPrototypeOf(this, MacroExpansionError.prototype);
852
+ }
853
+ };
854
+ function isCdkdError(error) {
855
+ return error instanceof CdkdError;
856
+ }
857
+ function formatError(error) {
858
+ if (isCdkdError(error)) {
859
+ let message = `${error.name}: ${error.message}`;
860
+ if (error.cause) {
861
+ const cause = displayAwsMessage(error.cause.message);
862
+ if (cause) message += `\nCaused by: ${cause}`;
863
+ }
864
+ return message;
865
+ }
866
+ if (error instanceof Error) return `${error.name}: ${error.message}`;
867
+ return safeStringify(error);
868
+ }
869
+ function handleError(error) {
870
+ const logger = getLogger();
871
+ const silent = error instanceof CdkdError && error.silent;
872
+ if (isSoftAbort(error)) logger.warn(error.message);
873
+ else if (!silent) logger.error(formatError(error));
874
+ if (error instanceof Error && error.stack) logger.debug("Stack trace:", error.stack);
875
+ const customExitCode = error instanceof CdkdError ? error.exitCode : void 0;
876
+ const exitCode = typeof customExitCode === "number" ? customExitCode : 1;
877
+ process.exit(exitCode);
878
+ }
879
+ function withErrorHandling(fn) {
880
+ return async (...args) => {
881
+ try {
882
+ await fn(...args);
883
+ } catch (error) {
884
+ handleError(error);
885
+ }
886
+ };
887
+ }
888
+ function bucketNames(bucket) {
889
+ if (bucket === void 0) return {
890
+ subject: "Bucket '<unknown bucket>'",
891
+ object: "bucket '<unknown bucket>'",
892
+ quoted: "'<unknown bucket>'"
893
+ };
894
+ if (isPasteableIdent(bucket)) return {
895
+ subject: `Bucket '${bucket}'`,
896
+ object: `bucket '${bucket}'`,
897
+ quoted: `'${bucket}'`
898
+ };
899
+ const described = "a bucket whose name is not a plain identifier";
900
+ return {
901
+ subject: "A bucket whose name is not a plain identifier",
902
+ object: described,
903
+ quoted: described
904
+ };
905
+ }
906
+ function normalizeAwsError(err, context = {}) {
907
+ if (!(err instanceof Error)) return new Error(safeStringify(err), { cause: err });
908
+ if (!(err.name === "Unknown" || err.message === "UnknownError")) return err;
909
+ const status = err.$metadata?.httpStatusCode;
910
+ const bucket = bucketNames(context.bucket);
911
+ const operation = context.operation ?? "operation";
912
+ switch (status) {
913
+ case 301: {
914
+ const responseHeaders = err.$response?.headers;
915
+ const region = responseHeaders?.["x-amz-bucket-region"] ?? responseHeaders?.["X-Amz-Bucket-Region"];
916
+ const where = region ? ` (in ${region})` : "";
917
+ return new Error(`${bucket.subject}${where} is in a different region than the client. cdkd resolves this automatically; if you see this message, please report it.`, { cause: err });
918
+ }
919
+ case 403: return new Error(`Access denied to ${bucket.object}. Verify credentials and bucket policy.`, { cause: err });
920
+ case 404: return new Error(`${bucket.subject} does not exist.`, { cause: err });
921
+ default: {
922
+ const statusStr = status !== void 0 ? `HTTP ${status}` : "unknown HTTP status";
923
+ return new Error(`S3 error during ${operation} on ${bucket.quoted} (${statusStr}). See CloudTrail for details.`, { cause: err });
924
+ }
925
+ }
926
+ }
927
+
928
+ // src/utils/ambient-client-defaults.ts
929
+ function ambientCredentialConfig() {
930
+ return getAwsClients()?.credentialConfig ?? {};
931
+ }
932
+ function clientDefaultsFor(credentialConfig, ignoreAssumedRole) {
933
+ return {
934
+ ...awsClientDefaults({
935
+ profile: credentialConfig.profile,
936
+ ...ignoreAssumedRole !== void 0 && { ignoreAssumedRole }
937
+ }),
938
+ ...credentialConfig.profile && { profile: credentialConfig.profile },
939
+ ...credentialConfig.credentials && { credentials: { ...credentialConfig.credentials } }
940
+ };
941
+ }
942
+ function ambientClientDefaults() {
943
+ return clientDefaultsFor(ambientCredentialConfig());
944
+ }
945
+ function credentialFingerprint(credentialConfig) {
946
+ return JSON.stringify([credentialConfig.profile ?? null, credentialConfig.credentials?.accessKeyId ?? null]);
947
+ }
948
+
949
+ // src/utils/pasteable-aws-profile.ts
950
+ let explicitProfile;
951
+ function setPasteableAwsProfile(profile) {
952
+ explicitProfile = profile;
953
+ }
954
+ function pasteableAwsProfileFlag() {
955
+ if (getAssumedRoleCredentials() !== void 0) return `--profile ${commandHole("role-profile")}`;
956
+ const profile = explicitProfile;
957
+ if (profile === void 0 || profile === "") return "";
958
+ if (displaySafe(profile) !== profile || profile.startsWith("-") || SHELL_ACTIVE.test(profile)) return `--profile ${commandHole("profile")}`;
959
+ return `--profile ${shellQuote(profile)}`;
960
+ }
961
+ function withPasteableAwsProfile(literal) {
962
+ const flag = pasteableAwsProfileFlag();
963
+ if (flag === "") return literal;
964
+ return literal.replace(/(^|[\s;&|(])aws (?=[a-z]|$)/g, (_m, pre) => `${pre}aws ${flag} `);
965
+ }
966
+
967
+ // src/state/record-keys.ts
968
+ function injectivePairKey(first, second) {
969
+ return JSON.stringify([first, second]);
970
+ }
971
+ function injectiveKey(...parts) {
972
+ return JSON.stringify(parts);
973
+ }
974
+ function injectiveKeyPrefix(first) {
975
+ return JSON.stringify([first]).slice(0, -1) + ",";
976
+ }
977
+ function producerRecordKey(stackName, region) {
978
+ return injectivePairKey(stackName, region);
979
+ }
980
+ function producerCoordinateKey(stackName, exportOrOutputName) {
981
+ return injectivePairKey(stackName, exportOrOutputName);
982
+ }
983
+
984
+ // src/state/state-prefix.ts
985
+ const DEFAULT_STATE_PREFIX = "cdkd";
986
+ const CUSTOM_RESOURCE_RESPONSE_PREFIX = "custom-resource-responses";
987
+
988
+ // src/state/lock-contention-message.ts
989
+ function formatLockExpiry(expiresAt) {
990
+ if (!Number.isFinite(expiresAt)) return "expires at an unknown time";
991
+ const remainingMs = expiresAt - Date.now();
992
+ return remainingMs > 0 ? `expires in ${formatDuration(remainingMs)}` : `expired ${formatDuration(-remainingMs)} ago`;
993
+ }
994
+ const LOCK_OWNER_DESCRIBED = "a lock owner that is not a plain identifier";
995
+ const LOCK_OPERATION_DESCRIBED = "a lock operation that is not a plain identifier";
996
+ function formatLockOwner(owner) {
997
+ if (owner === "") return "an unnamed holder";
998
+ return owner.includes(",") ? LOCK_OWNER_DESCRIBED : plainIdentOr(owner, LOCK_OWNER_DESCRIBED);
999
+ }
1000
+ function formatLockOperation(operation) {
1001
+ return operation.includes(",") ? LOCK_OPERATION_DESCRIBED : plainIdentOr(operation, LOCK_OPERATION_DESCRIBED);
1002
+ }
1003
+ function formatDuration(ms) {
1004
+ const seconds = Math.floor(ms / 1e3);
1005
+ if (seconds < 60) return `${seconds}s`;
1006
+ return `${Math.floor(seconds / 60)}m${seconds % 60}s`;
1007
+ }
1008
+ function buildForceUnlockCommand(stackName, region, recovery) {
1009
+ const recoveryFlags = recoveryCommandFlags(recovery);
1010
+ const built = pasteableCommand("cdkd force-unlock", region === void 0 ? [{
1011
+ value: stackName,
1012
+ hole: "stack",
1013
+ opts: { plainIdent: true }
1014
+ }] : [{
1015
+ value: stackName,
1016
+ hole: "stack",
1017
+ opts: { plainIdent: true }
1018
+ }, {
1019
+ flag: "--stack-region",
1020
+ value: region,
1021
+ hole: "region",
1022
+ opts: { plainIdent: true }
1023
+ }], recoveryFlags.flags);
1024
+ if (!built.exact || !recoveryFlags.exact) return "";
1025
+ return built.command;
1026
+ }
1027
+ function recoveryCommandFlags(recovery) {
1028
+ const gated = (value) => {
1029
+ const v = sanitizeRecoveryValue(value);
1030
+ return {
1031
+ text: v.text,
1032
+ exact: v.exact && isInertUnquoted(v.text)
1033
+ };
1034
+ };
1035
+ const profile = gated(recovery?.profile);
1036
+ const stateBucket = gated(recovery?.stateBucket);
1037
+ const statePrefix = gated(recovery?.statePrefix);
1038
+ const flags = [];
1039
+ if (profile.text || !profile.exact) flags.push(profile.exact ? `--profile ${shellQuote(profile.text)}` : `--profile ${commandHole("profile")}`);
1040
+ if (stateBucket.text || !stateBucket.exact) flags.push(stateBucket.exact ? `--state-bucket ${shellQuote(stateBucket.text)}` : `--state-bucket ${commandHole("bucket")}`);
1041
+ const prefix = recovery?.statePrefix;
1042
+ if (prefix !== void 0 && prefix !== "cdkd") flags.push(statePrefix.exact ? `--state-prefix ${shellQuote(statePrefix.text)}` : `--state-prefix ${commandHole("prefix")}`);
1043
+ return {
1044
+ flags,
1045
+ exact: profile.exact && stateBucket.exact && statePrefix.exact
1046
+ };
1047
+ }
1048
+ function sanitizeRecoveryValue(value) {
1049
+ if (value === void 0) return {
1050
+ text: "",
1051
+ exact: true
1052
+ };
1053
+ const text = displaySafe(value);
1054
+ return {
1055
+ text,
1056
+ exact: text === value
1057
+ };
1058
+ }
1059
+ const UNREPRODUCIBLE_LOCK_VALUES = "the name, region, profile, state bucket or state prefix";
1060
+ const UNREPRODUCIBLE_LOCK_CLAUSE = `Inspect the lock object directly: ${UNREPRODUCIBLE_LOCK_VALUES} recorded for this stack cannot be reproduced safely on a command line (changed by sanitizing, unrenderable, empty, too long, not a plain identifier, beginning with '-', which cdkd refuses rather than risk it parsing as an option, or holding whitespace or a character a shell treats specially), so no command is shown: one built from it could address a different lock or run part of it as shell.`;
1061
+ function forceQuitRecoveryClause(stackName, region, recovery) {
1062
+ const command = buildForceUnlockCommand(stackName, region, recovery);
1063
+ return command ? ` If the next run reports a lock, run: ${command}` : ` ${UNREPRODUCIBLE_LOCK_CLAUSE}`;
1064
+ }
1065
+ async function buildLockContentionMessage(args) {
1066
+ const { lockManager, stackName, region, subject = "stack", recovery, heldClause, suffix } = args;
1067
+ let held = heldClause ?? "another cdkd process holds it";
1068
+ let sawHolder = false;
1069
+ try {
1070
+ const info = await lockManager.getLockInfo(stackName, region);
1071
+ if (info) {
1072
+ const operation = info.operation ? `, operation: ${formatLockOperation(info.operation)}` : "";
1073
+ const expires = formatLockExpiry(info.expiresAt);
1074
+ const owner = displaySafe(info.owner);
1075
+ const holder = owner ? `held by ${formatLockOwner(owner)}${operation}` : `held by an unnamed holder`;
1076
+ held = `${heldClause ? `${heldClause} — ` : ""}${holder}, ${expires}`;
1077
+ if (owner) sawHolder = true;
1078
+ }
1079
+ } catch {}
1080
+ const advice = sawHolder ? `That process is still running — wait for it to finish. Only if you are certain it is gone` : `Wait for it to finish, or if you are certain no other process is active`;
1081
+ const recoveryCommand = buildForceUnlockCommand(stackName, region, recovery);
1082
+ const safeRegion = displaySafe(region, { asciiOnly: true }) || "<unrenderable>";
1083
+ const head = `Could not acquire lock for ${subject} ${displayStackName(stackName)} (${safeRegion}) — ${held}.` + (suffix ? ` ${suffix}` : "");
1084
+ if (!recoveryCommand) return `${head} ${advice}. No recovery command can be shown: ${UNREPRODUCIBLE_LOCK_VALUES} recorded for this lock cannot be reproduced safely on a command line (changed by sanitizing, unrenderable, empty, too long, not a plain identifier, beginning with '-', which cdkd refuses rather than risk it parsing as an option, or holding whitespace or a character a shell treats specially), so no command is shown: one built from it could address a different lock or run part of it as shell — inspect the lock object directly.`;
1085
+ return `${head} ${advice}, run: ${recoveryCommand}`;
1086
+ }
1087
+
1088
+ // src/provisioning/auxiliary-failure.ts
1089
+ const AUXILIARY_LOGICAL_ID_SUFFIX = "/auxiliary";
1090
+ const MAX_DEPTH$1 = 5;
1091
+ function auxiliaryLogicalId(ownerLogicalId) {
1092
+ return `${ownerLogicalId}${AUXILIARY_LOGICAL_ID_SUFFIX}`;
1093
+ }
1094
+ const RETRY_AUXILIARY_OWNER = "withRetry";
1095
+ function isAuxiliaryMarkOf(link, ownerLogicalId) {
1096
+ try {
1097
+ if (typeof link !== "object" && typeof link !== "function" || link === null) return false;
1098
+ const own = Object.getOwnPropertyDescriptor(link, "logicalId");
1099
+ return isAuxiliaryMark(own) && own?.value === auxiliaryLogicalId(ownerLogicalId);
1100
+ } catch {
1101
+ return false;
1102
+ }
1103
+ }
1104
+ function isAuxiliaryMark(descriptor) {
1105
+ return descriptor !== void 0 && descriptor.enumerable === false && descriptor.writable === false && typeof descriptor.value === "string" && descriptor.value.endsWith(AUXILIARY_LOGICAL_ID_SUFFIX);
1106
+ }
1107
+ function isAuxiliaryFailure(error) {
1108
+ try {
1109
+ let current = error;
1110
+ for (let depth = 0; depth < MAX_DEPTH$1 && typeof current === "object" && current !== null; depth++) {
1111
+ if (isAuxiliaryMark(Object.getOwnPropertyDescriptor(current, "logicalId"))) return true;
1112
+ current = current.cause;
1113
+ }
1114
+ } catch {}
1115
+ return false;
1116
+ }
1117
+ const CREATED_BEFORE_FAILURE = Symbol.for("cdkd.createdBeforeFailure");
1118
+ function markCreatedBeforeFailure(error, ownerLogicalId, resourceType, physicalId, createdResourceIdentity) {
1119
+ try {
1120
+ if (typeof error !== "object" || error === null || !Object.isExtensible(error)) return error;
1121
+ if (physicalId === "") return error;
1122
+ Object.defineProperty(error, CREATED_BEFORE_FAILURE, {
1123
+ value: Object.freeze({
1124
+ logicalId: ownerLogicalId,
1125
+ resourceType,
1126
+ physicalId,
1127
+ ...typeof createdResourceIdentity === "string" && createdResourceIdentity !== "" && { createdResourceIdentity }
1128
+ }),
1129
+ enumerable: false,
1130
+ writable: false,
1131
+ configurable: true
1132
+ });
1133
+ } catch {}
1134
+ return error;
1135
+ }
1136
+ function createdMarkOn(link) {
1137
+ const value = link[CREATED_BEFORE_FAILURE];
1138
+ if (typeof value !== "object" || value === null) return void 0;
1139
+ const { logicalId, resourceType, physicalId, createdResourceIdentity } = value;
1140
+ if (typeof logicalId !== "string" || typeof resourceType !== "string" || typeof physicalId !== "string" || physicalId === "") return;
1141
+ return typeof createdResourceIdentity === "string" && createdResourceIdentity !== "" ? {
1142
+ logicalId,
1143
+ resourceType,
1144
+ physicalId,
1145
+ createdResourceIdentity
1146
+ } : {
1147
+ logicalId,
1148
+ resourceType,
1149
+ physicalId
1150
+ };
1151
+ }
1152
+ function createdBeforeFailure(error, logicalId, resourceType) {
1153
+ return anchoredCreatedMark(error, logicalId, resourceType)?.physicalId;
1154
+ }
1155
+ function createdResourceIdentityBeforeFailure(error, logicalId, resourceType) {
1156
+ return anchoredCreatedMark(error, logicalId, resourceType)?.createdResourceIdentity;
1157
+ }
1158
+ function anchoredCreatedMark(error, logicalId, resourceType) {
1159
+ try {
1160
+ let current = error;
1161
+ for (let depth = 0; depth < 10 && typeof current === "object" && current !== null; depth++) {
1162
+ const mark = createdMarkOn(current);
1163
+ if (mark) return mark.logicalId === logicalId && mark.resourceType === resourceType ? mark : void 0;
1164
+ const own = Object.getOwnPropertyDescriptor(current, "logicalId");
1165
+ if (typeof own?.value === "string" && own.value !== logicalId && !isAuxiliaryMarkOf(current, logicalId)) return;
1166
+ current = current.cause;
1167
+ }
1168
+ } catch {}
1169
+ }
1170
+ function clearCreatedBeforeFailure(error) {
1171
+ try {
1172
+ let current = error;
1173
+ for (let depth = 0; depth < 10 && typeof current === "object" && current !== null; depth++) {
1174
+ if (Object.getOwnPropertyDescriptor(current, CREATED_BEFORE_FAILURE)?.configurable) delete current[CREATED_BEFORE_FAILURE];
1175
+ current = current.cause;
1176
+ }
1177
+ } catch {}
1178
+ return error;
1179
+ }
1180
+ function hasCreatedBeforeFailure(error) {
1181
+ try {
1182
+ let current = error;
1183
+ for (let depth = 0; depth < 10 && typeof current === "object" && current !== null; depth++) {
1184
+ if (createdMarkOn(current)) return true;
1185
+ current = current.cause;
1186
+ }
1187
+ } catch {}
1188
+ return false;
1189
+ }
1190
+ function firstCreatedMark(error) {
1191
+ let current = error;
1192
+ for (let depth = 0; depth < 10 && typeof current === "object" && current !== null; depth++) {
1193
+ const mark = createdMarkOn(current);
1194
+ if (mark) return mark;
1195
+ current = current.cause;
1196
+ }
1197
+ }
1198
+ const SUPERSEDES_EARLIER_CREATE = Symbol.for("cdkd.supersedesEarlierCreate");
1199
+ function markSupersedesEarlierCreate(error) {
1200
+ try {
1201
+ if (typeof error !== "object" || error === null || !Object.isExtensible(error)) return error;
1202
+ Object.defineProperty(error, SUPERSEDES_EARLIER_CREATE, {
1203
+ value: true,
1204
+ enumerable: false,
1205
+ writable: false,
1206
+ configurable: true
1207
+ });
1208
+ } catch {}
1209
+ return error;
1210
+ }
1211
+ function supersedesEarlierCreate(error) {
1212
+ let current = error;
1213
+ for (let depth = 0; depth < 10 && typeof current === "object" && current !== null; depth++) {
1214
+ if (current[SUPERSEDES_EARLIER_CREATE] === true) return true;
1215
+ current = current.cause;
1216
+ }
1217
+ return false;
1218
+ }
1219
+ function replayMadeAnotherResource(earlier, later) {
1220
+ try {
1221
+ const before = firstCreatedMark(earlier);
1222
+ const after = firstCreatedMark(later);
1223
+ if (before === void 0 || after === void 0 || before.logicalId !== after.logicalId || before.resourceType !== after.resourceType || supersedesEarlierCreate(later)) return false;
1224
+ const members = (id) => id.split(",").filter((member) => member !== "");
1225
+ const earlierMembers = new Set(members(before.physicalId));
1226
+ return !members(after.physicalId).some((member) => earlierMembers.has(member));
1227
+ } catch {
1228
+ return false;
1229
+ }
1230
+ }
1231
+ function carryCreatedBeforeFailure(from, to) {
1232
+ try {
1233
+ if (typeof to !== "object" || to === null) return to;
1234
+ let current = from;
1235
+ let mark;
1236
+ for (let depth = 0; depth < 10 && typeof current === "object" && current !== null && !mark; depth++) {
1237
+ mark = createdMarkOn(current);
1238
+ current = current.cause;
1239
+ }
1240
+ if (!mark) return to;
1241
+ let probe = to;
1242
+ for (let depth = 0; depth < 10 && typeof probe === "object" && probe !== null; depth++) {
1243
+ if (createdMarkOn(probe)) return to;
1244
+ probe = probe.cause;
1245
+ }
1246
+ return markCreatedBeforeFailure(to, mark.logicalId, mark.resourceType, mark.physicalId, mark.createdResourceIdentity);
1247
+ } catch {
1248
+ return to;
1249
+ }
1250
+ }
1251
+ function markAuxiliaryFailure(error, ownerLogicalId) {
1252
+ try {
1253
+ let current = error;
1254
+ for (let depth = 0; depth < MAX_DEPTH$1 && typeof current === "object" && current !== null; depth++) {
1255
+ const own = Object.getOwnPropertyDescriptor(current, "logicalId");
1256
+ if (typeof own?.value === "string") {
1257
+ if (isAuxiliaryMark(own)) return error;
1258
+ } else {
1259
+ if (Object.isExtensible(current) && own?.configurable !== false) Object.defineProperty(current, "logicalId", {
1260
+ value: auxiliaryLogicalId(ownerLogicalId),
1261
+ enumerable: false,
1262
+ writable: false,
1263
+ configurable: true
1264
+ });
1265
+ return error;
1266
+ }
1267
+ current = current.cause;
1268
+ }
1269
+ } catch {}
1270
+ return error;
1271
+ }
1272
+
1273
+ // src/provisioning/masked-retry-logger.ts
1274
+ function maskerOrIdentity(maskSecrets) {
1275
+ return maskSecrets ?? ((text) => text);
1276
+ }
1277
+ function createMaskedRetryLogger(logger, maskSecrets) {
1278
+ const mask = maskerOrIdentity(maskSecrets);
1279
+ return {
1280
+ debug: (message) => logger.debug(mask(message)),
1281
+ warn: (message) => logger.warn(mask(message))
1282
+ };
1283
+ }
1284
+ function createMaskedLogSinks(logger, maskSecrets) {
1285
+ const mask = maskerOrIdentity(maskSecrets);
1286
+ return {
1287
+ mask,
1288
+ value: (value) => mask(String(value)),
1289
+ debug: (message) => logger.debug(mask(message)),
1290
+ warn: (message) => logger.warn(mask(message))
1291
+ };
1292
+ }
1293
+ function isSecretDerivedValue(raw, mask) {
1294
+ return typeof raw === "string" && raw !== "" && (mask(raw) !== raw || raw.includes("{{resolve:") || raw === "***");
1295
+ }
1296
+ function withDerivedNameMasks(logger, sinks, pairs) {
1297
+ const base = sinks.mask;
1298
+ const derivedNames = pairs.filter((pair) => typeof pair[0] === "string" && pair[0] !== "" && typeof pair[1] === "string" && pair[1] !== "" && isSecretDerivedValue(pair[0], base)).map(([, derived]) => derived);
1299
+ if (derivedNames.length === 0) return sinks;
1300
+ const mask = (text) => {
1301
+ const masked = base(text);
1302
+ const needles = [];
1303
+ for (const derived of new Set(derivedNames)) {
1304
+ if (derived.length < 4) {
1305
+ needles.push(derived);
1306
+ continue;
1307
+ }
1308
+ const rendered = base(derived);
1309
+ if (rendered === "***") continue;
1310
+ for (let i = text.indexOf(derived); i !== -1;) {
1311
+ const j = i + derived.length;
1312
+ if (masked !== base(text.slice(0, i)) + rendered + base(text.slice(j))) return "***";
1313
+ i = text.indexOf(derived, i + 1);
1314
+ }
1315
+ needles.push(rendered);
1316
+ }
1317
+ let out = masked;
1318
+ for (const needle of needles.sort((a, b) => b.length - a.length)) out = out.split(needle).join("***");
1319
+ return out;
1320
+ };
1321
+ return {
1322
+ mask,
1323
+ value: (value) => mask(String(value)),
1324
+ debug: (message) => logger.debug(mask(message)),
1325
+ warn: (message) => logger.warn(mask(message))
1326
+ };
1327
+ }
1328
+ const MASK_WALK_MAX_DEPTH = 8;
1329
+ const MASK_WALK_DEPTH_CAP_MARKER = "***";
1330
+ const BASE_MASKER_SUBSTRING_FLOOR = 4;
1331
+ function maskDeep(value, mask, depth = 0) {
1332
+ if (typeof value === "string") return mask(value);
1333
+ if (depth >= 8) return "***";
1334
+ if (Array.isArray(value)) return value.map((entry) => maskDeep(entry, mask, depth + 1));
1335
+ if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [mask(k), maskDeep(v, mask, depth + 1)]));
1336
+ return value;
1337
+ }
1338
+
1339
+ // src/provisioning/state-orphan-remedy.ts
1340
+ function stateOrphanRecordRemedy(context, logicalId) {
1341
+ const resourceArg = `--resource ${isPasteableIdent(logicalId) ? logicalId : "<logicalId>"}`;
1342
+ if (context?.stackDestroy === true) return `'cdkd state orphan <stack> --stack-region <region>', which drops every record the stack still has in that region, not just this one (add '${resourceArg}' to drop only this one)`;
1343
+ return `'cdkd state orphan <stack> --stack-region <region> ${resourceArg}', which drops only this record — never run it without --resource on a stack that is still deployed, where that drops every resource's record and the next deploy re-creates or collides with all of them`;
1344
+ }
1345
+
1346
+ // src/provisioning/composite-id.ts
1347
+ const COMPOSITE_ID_SEPARATOR = "|";
1348
+ function canonicalizeRoute53QueryName(name) {
1349
+ const lowered = name.toLowerCase().replaceAll("*", "\\052").replaceAll("|", "\\174");
1350
+ return lowered.endsWith(".") ? lowered : `${lowered}.`;
1351
+ }
1352
+ function segmentAfterAnchor(physicalId, anchor) {
1353
+ if (typeof anchor !== "string" || anchor === "") return void 0;
1354
+ const prefix = `${anchor}${"|"}`;
1355
+ if (!physicalId.startsWith(prefix)) return void 0;
1356
+ const rest = physicalId.slice(prefix.length);
1357
+ return rest === "" ? void 0 : rest;
1358
+ }
1359
+ const QUOTABLE_VALUE = /^(?:[A-Za-z0-9:_@./+=,~|*-]+| *)$/;
1360
+ function quotedOr(value, quote, description) {
1361
+ return QUOTABLE_VALUE.test(value) ? `${quote}${value}${quote}` : description;
1362
+ }
1363
+ function logicalIdShown(logicalId) {
1364
+ return plainOrDescribed(logicalId, "logical id");
1365
+ }
1366
+ const RESOURCE_TYPE_SHAPE = /^[A-Za-z0-9]+(?:::[A-Za-z0-9_@-]+)+$/;
1367
+ function resourceTypeShown(resourceType) {
1368
+ return RESOURCE_TYPE_SHAPE.test(resourceType) && plainIdentOr(resourceType, "") === resourceType ? resourceType : "a resource type that is not a plain identifier";
1369
+ }
1370
+ function idShape(segments) {
1371
+ return segments.map((segment) => `<${segment.name}>`).join("|");
1372
+ }
1373
+ function compositeIdSeparatorRefusal(resourceType, logicalId, segments, maskSecrets) {
1374
+ const offending = segments.filter((segment) => String(segment.value).includes("|"));
1375
+ if (offending.length === 0) return void 0;
1376
+ const mask = maskerOrIdentity(maskSecrets);
1377
+ const named = offending.map((segment) => `${segment.name} ${quotedOr(mask(String(segment.value)), "'", "(its value is not shown: it is not a plain identifier)")}`).join(" and ");
1378
+ return `${resourceTypeShown(resourceType)} ${logicalIdShown(logicalId)}: ${named} contains '${"|"}', which cdkd uses as the separator in this type's physical id (${idShape(segments)}). Recording it would produce an id that decodes back to a DIFFERENT resource, so a later cdkd destroy / drift / update would target the wrong one — or silently skip it while the real resource stays alive. cdkd cannot record a value containing '${"|"}' in this position. Change the named value so it does not contain '${"|"}', or manage the resource outside cdkd.`;
1379
+ }
1380
+ function packCompositeId(resourceType, logicalId, segments, options) {
1381
+ const refusal = compositeIdSeparatorRefusal(resourceType, logicalId, segments, options?.maskSecrets);
1382
+ if (refusal !== void 0) {
1383
+ if (options?.onRefusal) options.onRefusal(`${refusal} Continuing with the ambiguous id because the value comes from a cdkd state record, which no template edit can repair; the same value is REFUSED on a template-path create.`);
1384
+ else throw new ProvisioningError(refusal, resourceType, logicalId);
1385
+ }
1386
+ return segments.map((segment) => String(segment.value)).join("|");
1387
+ }
1388
+ function formatShape(segments) {
1389
+ return segments.map((segment) => `<${segment}>`).join("|");
1390
+ }
1391
+ function compositeIdFormatMessage(format, logicalId, physicalId, options) {
1392
+ const accepted = format.alsoAccepts ? `"${formatShape(format.segments)}" or ${format.alsoAccepts}` : `"${formatShape(format.segments)}"`;
1393
+ const head = `Invalid physicalId format for ${format.label} ${logicalIdShown(logicalId)}: expected ${accepted}, got ${quotedOr(physicalId, "\"", "an id that is not a plain identifier")}`;
1394
+ if (!options?.skipping) return head;
1395
+ return `${head}. Skipping the delete — no AWS call is issued, so the resource is LEFT IN PLACE. The state record is KEPT and the run reports the skip: on 'cdkd destroy' / 'cdkd state destroy' (which exits non-zero) and, since issue 1762, on the plain DELETE of a resource removed from the template during cdkd deploy (which re-attempts it on the next deploy). So repair the id in state.json and re-run, or delete the resource by hand and drop the record with ${stateOrphanRecordRemedy(options.context, logicalId)}. NOTE a deploy-side REPLACEMENT or rollback delete instead FAILS the resource (https://github.com/go-to-k/cdkd/issues/1762) — the old resource is left untracked there, so delete it by hand.`;
1396
+ }
1397
+ const COMPOSITE_ID_SKIP_REASON = "malformed physicalId in state — no delete issued";
1398
+ function compositeIdSkipResult() {
1399
+ return {
1400
+ outcome: "skipped",
1401
+ reason: COMPOSITE_ID_SKIP_REASON
1402
+ };
1403
+ }
1404
+
1405
+ // src/provisioning/interrupt-watch.ts
1406
+ var InterruptedWaitError = class InterruptedWaitError extends Error {
1407
+ constructor(what) {
1408
+ super(`${what} interrupted by user (SIGINT)`);
1409
+ this.name = "InterruptedWaitError";
1410
+ Object.setPrototypeOf(this, InterruptedWaitError.prototype);
1411
+ }
1412
+ };
1413
+ function isInterruptedWaitError(error) {
1414
+ const seen = /* @__PURE__ */ new Set();
1415
+ let current = error;
1416
+ while (current !== void 0 && current !== null) {
1417
+ if (current instanceof InterruptedWaitError) return true;
1418
+ if (!(current instanceof Error)) return false;
1419
+ if (seen.has(current)) return false;
1420
+ seen.add(current);
1421
+ current = current.cause;
1422
+ }
1423
+ return false;
1424
+ }
1425
+ const liveWatches = /* @__PURE__ */ new Set();
1426
+ let sharedSigintHandler;
1427
+ let sigintLatched = false;
1428
+ const interruptWatchTestSeam = {};
1429
+ let commandInterruptScopeDepth = 0;
1430
+ function commandOwnsInterrupts() {
1431
+ const override = interruptWatchTestSeam.commandOwnsInterrupts;
1432
+ if (override !== void 0) return override();
1433
+ return commandInterruptScopeDepth > 0;
1434
+ }
1435
+ function armSharedSigintHandler() {
1436
+ if (sharedSigintHandler !== void 0) return;
1437
+ if (!commandOwnsInterrupts()) return;
1438
+ const handler = () => {
1439
+ sigintLatched = true;
1440
+ for (const live of liveWatches) live.interrupted = true;
1441
+ if (process.listeners("SIGINT").filter((l) => l !== handler).length === 0) {
1442
+ process.stderr.write("\nInterrupted.\nIf the next run reports a stack lock, release it with: cdkd force-unlock <stack-name>\n");
1443
+ (interruptWatchTestSeam.forceQuit ?? ((code) => process.exit(code)))(130);
1444
+ }
1445
+ };
1446
+ sharedSigintHandler = handler;
1447
+ process.on("SIGINT", handler);
1448
+ }
1449
+ function startInterruptWatch(what) {
1450
+ armSharedSigintHandler();
1451
+ const state = { interrupted: sigintLatched };
1452
+ liveWatches.add(state);
1453
+ let disposed = false;
1454
+ return {
1455
+ isInterrupted: () => state.interrupted,
1456
+ onInterrupted: () => new InterruptedWaitError(what),
1457
+ dispose: () => {
1458
+ if (disposed) return;
1459
+ disposed = true;
1460
+ liveWatches.delete(state);
1461
+ }
1462
+ };
1463
+ }
1464
+ function resetInterruptWatchLatch() {
1465
+ sigintLatched = false;
1466
+ for (const live of liveWatches) live.interrupted = false;
1467
+ }
1468
+ function beginCommandInterruptScope() {
1469
+ commandInterruptScopeDepth += 1;
1470
+ if (commandInterruptScopeDepth === 1) resetInterruptWatchLatch();
1471
+ }
1472
+ function endCommandInterruptScope() {
1473
+ commandInterruptScopeDepth = Math.max(0, commandInterruptScopeDepth - 1);
1474
+ if (commandInterruptScopeDepth > 0) return;
1475
+ if (sharedSigintHandler !== void 0) {
1476
+ process.removeListener("SIGINT", sharedSigintHandler);
1477
+ sharedSigintHandler = void 0;
1478
+ }
1479
+ resetInterruptWatchLatch();
1480
+ }
1481
+ function interruptWatchListenerCount() {
1482
+ return sharedSigintHandler === void 0 ? 0 : 1;
1483
+ }
1484
+
1485
+ // src/utils/elapsed-budget.ts
1486
+ const monotonicNowMs = typeof globalThis.performance?.now === "function" ? () => globalThis.performance.now() : () => Date.now();
1487
+ var ElapsedBudget = class {
1488
+ totalMs;
1489
+ clock;
1490
+ startedAt;
1491
+ constructor(totalMs, clock = monotonicNowMs) {
1492
+ if (!Number.isFinite(totalMs) || totalMs <= 0) throw new RangeError(`ElapsedBudget: totalMs must be a positive finite number (got ${totalMs})`);
1493
+ this.totalMs = totalMs;
1494
+ this.clock = clock;
1495
+ this.startedAt = clock();
1496
+ }
1497
+ elapsedMs() {
1498
+ return Math.max(0, this.clock() - this.startedAt);
1499
+ }
1500
+ remainingMs() {
1501
+ return Math.max(0, this.totalMs - this.elapsedMs());
1502
+ }
1503
+ isExhausted() {
1504
+ return this.remainingMs() <= 0;
1505
+ }
1506
+ attemptsWithin(cap, perAttemptMs, minAttempts = 1) {
1507
+ if (!Number.isFinite(cap) || cap <= 0) return 0;
1508
+ if (!Number.isFinite(perAttemptMs) || perAttemptMs <= 0) return Math.min(minAttempts, cap);
1509
+ const affordable = Math.floor(this.remainingMs() / perAttemptMs);
1510
+ return Math.max(Math.min(minAttempts, cap), Math.min(cap, affordable));
1511
+ }
1512
+ };
1513
+ var ElapsedBudgetRegistry = class {
1514
+ budgets = /* @__PURE__ */ new Map();
1515
+ acquire(key, totalMs, clock = monotonicNowMs, reuseWithinMs) {
1516
+ const existing = this.budgets.get(key);
1517
+ if (existing) {
1518
+ if (!(reuseWithinMs !== void 0 && existing.elapsedMs() > reuseWithinMs)) return existing;
1519
+ this.budgets.delete(key);
1520
+ }
1521
+ const created = new ElapsedBudget(totalMs, clock);
1522
+ this.budgets.set(key, created);
1523
+ return created;
1524
+ }
1525
+ release(key) {
1526
+ this.budgets.delete(key);
1527
+ }
1528
+ get size() {
1529
+ return this.budgets.size;
1530
+ }
1531
+ clear() {
1532
+ this.budgets.clear();
1533
+ }
1534
+ };
1535
+
1536
+ // src/provisioning/replacement-protection-advice.ts
1537
+ const DELETION_PROTECTION_DOC_POINTER = "\"Deletion protection blocks a replacement, and deploy cannot clear it\" at https://cdkd.dev/cli-deploy-safety/#deletion-protection-blocks-a-replacement-and-deploy-cannot-clear-it";
1538
+ const UNNAMEABLE_ID_CLAUSE = "Then disable protection out of band, via the console: the physical id cdkd recorded for this resource cannot be reproduced safely on a command line (sanitizing would change it, so a command would act on a different resource, or it holds a character or prefix a pasted shell line, or the AWS CLI itself, would act on, or it is derived from a secret, which a pasted command would print).";
1539
+ const AWS_CLI_ACTIVE_ARG = /^-|^(?:fileb?|https?):\/\/|@=/i;
1540
+ function isAwsCliLiteral(value) {
1541
+ return !AWS_CLI_ACTIVE_ARG.test(value);
1542
+ }
1543
+ function renderDisableCommand(disable) {
1544
+ const resolved = disable;
1545
+ const quoted = pasteableArg(resolved.identifier, resolved.maskSecrets);
1546
+ if (quoted === void 0) return "";
1547
+ const tail = resolved.after ? ` ${resolved.after}` : "";
1548
+ return `${withPasteableAwsProfile(resolved.before)} ${quoted}${tail}`;
1549
+ }
1550
+ function pasteableArg(value, maskSecrets) {
1551
+ if (typeof value !== "string") return void 0;
1552
+ const safe = displaySafe(value, { asciiOnly: true });
1553
+ if (!safe || safe !== value) return void 0;
1554
+ if (maskSecrets && maskSecrets(safe) !== safe) return void 0;
1555
+ if (!isInertUnquoted(safe)) return void 0;
1556
+ if (!isAwsCliLiteral(safe)) return void 0;
1557
+ return shellQuote(safe);
1558
+ }
1559
+ var PasteableAwsCommand = class {
1560
+ text;
1561
+ #origin;
1562
+ constructor(text, origin) {
1563
+ this.text = text;
1564
+ this.#origin = origin;
1565
+ }
1566
+ builtBy(tag) {
1567
+ return this.#origin === tag;
1568
+ }
1569
+ render() {
1570
+ return this.text ?? "[command withheld: a name or id it would carry cannot be printed safely on a command line (inexact, so a pasted copy could act on a different resource, or holding a character or prefix a pasted shell line, or the AWS CLI itself, would act on); use the console]";
1571
+ }
1572
+ };
1573
+ const WITHHELD_AWS_COMMAND = "[command withheld: a name or id it would carry cannot be printed safely on a command line (inexact, so a pasted copy could act on a different resource, or holding a character or prefix a pasted shell line, or the AWS CLI itself, would act on); use the console]";
1574
+ function pasteableAwsCommand(maskSecrets) {
1575
+ const tag = (strings, ...values) => {
1576
+ let text = withPasteableAwsProfile(strings[0] ?? "");
1577
+ for (let i = 0; i < values.length; i++) {
1578
+ const value = values[i];
1579
+ const rendered = value instanceof PasteableAwsCommand ? value.builtBy(tag) ? value.text : void 0 : pasteableArg(value, maskSecrets);
1580
+ if (rendered === void 0) return new PasteableAwsCommand(void 0, tag);
1581
+ text += rendered + withPasteableAwsProfile(strings[i + 1] ?? "");
1582
+ }
1583
+ return new PasteableAwsCommand(text, tag);
1584
+ };
1585
+ return tag;
1586
+ }
1587
+ function protectedReplacementAdvice(args) {
1588
+ const { evidence, replaceFlags } = args;
1589
+ const command = renderDisableCommand(args.disable);
1590
+ const disable = args.disable;
1591
+ const head = `${evidence}, so ${replaceFlags} alone will NOT succeed while AWS still has protection on: the replacement DELETES the resource, AWS refuses that delete while protection is on, and cdkd deploy has no --remove-protection flag to clear it (only cdkd destroy and cdkd state destroy act on one). Read ${DELETION_PROTECTION_DOC_POINTER} BEFORE you disable anything: whether disabling helps at all, and what the flag ends up as, depend on your UpdateReplacePolicy and on whether the deploy completes — neither of which this refusal can see.`;
1592
+ if (!command) return `${head} ${UNNAMEABLE_ID_CLAUSE} Then re-run with ${replaceFlags}.`;
1593
+ return `${head} Then disable protection out of band — \`${command}\`, or via the console.${disable.caveat ? ` ${disable.caveat}` : ""} Then re-run with ${replaceFlags}.`;
1594
+ }
1595
+
1596
+ // src/provisioning/providers/deletion-protection-compensation.ts
1597
+ var ProtectionFlipRegistry = class {
1598
+ records = /* @__PURE__ */ new Map();
1599
+ acquire(key, reuseWithinMs, clock = monotonicNowMs) {
1600
+ const existing = this.records.get(key);
1601
+ if (existing) {
1602
+ if (existing.idle.elapsedMs() <= reuseWithinMs) {
1603
+ existing.idle = new ElapsedBudget(reuseWithinMs, clock);
1604
+ return existing.record;
1605
+ }
1606
+ this.records.delete(key);
1607
+ }
1608
+ const created = {
1609
+ record: {
1610
+ flippedOffByThisRun: false,
1611
+ deleteAccepted: false
1612
+ },
1613
+ idle: new ElapsedBudget(reuseWithinMs, clock)
1614
+ };
1615
+ this.records.set(key, created);
1616
+ return created.record;
1617
+ }
1618
+ release(key) {
1619
+ this.records.delete(key);
1620
+ }
1621
+ get size() {
1622
+ return this.records.size;
1623
+ }
1624
+ clear() {
1625
+ this.records.clear();
1626
+ }
1627
+ };
1628
+ function protectionFlipKey(resourceType, physicalId, region) {
1629
+ return injectiveKey(resourceType, region ?? "", physicalId);
1630
+ }
1631
+ const PROTECTION_FLIP_REUSE_WINDOW_MS = 18e5;
1632
+ const finalDeleteAttempt = new AsyncLocalStorage();
1633
+ function runDeleteAttempt(isFinal, attempt) {
1634
+ return finalDeleteAttempt.run(isFinal, attempt);
1635
+ }
1636
+ function isTerminalDeleteFailure(error) {
1637
+ if (isInterruptedWaitError(error)) return true;
1638
+ if (finalDeleteAttempt.getStore() === true) return true;
1639
+ if (isMarkedNonRetryable(error)) return true;
1640
+ const classify = retryClassificationText(error);
1641
+ return !(isRetryableTransientError(error, classify) || classify.includes("Too Many Requests"));
1642
+ }
1643
+ function rdsFamilyProtectionSite(opts) {
1644
+ return {
1645
+ subject: `${opts.serviceLabel} ${opts.kind === "cluster" ? "DBCluster" : "DBInstance"}`,
1646
+ guardName: "DeletionProtection",
1647
+ noun: opts.kind,
1648
+ isNotFound: opts.isNotFound,
1649
+ notFoundMeaning: `${opts.serviceLabel} answered ${opts.notFoundFault}. That most commonly means the ${opts.kind} is gone, and it can also mean it is not in this region or account.`,
1650
+ commands: () => {
1651
+ const aws = pasteableAwsCommand();
1652
+ const regionArg = opts.region ? aws` --region ${opts.region}` : aws``;
1653
+ const id = opts.physicalId;
1654
+ const svc = opts.cliService;
1655
+ const service = svc === "rds" ? aws`rds` : svc === "docdb" ? aws`docdb` : aws`neptune`;
1656
+ const check = opts.kind === "cluster" ? aws`aws ${service} describe-db-clusters --db-cluster-identifier ${id}${regionArg}` : aws`aws ${service} describe-db-instances --db-instance-identifier ${id}${regionArg}`;
1657
+ const restore = opts.kind === "cluster" ? aws`aws ${service} modify-db-cluster --db-cluster-identifier ${id}${regionArg} --deletion-protection --apply-immediately` : aws`aws ${service} modify-db-instance --db-instance-identifier ${id}${regionArg} --deletion-protection --apply-immediately`;
1658
+ return {
1659
+ check: check.render(),
1660
+ restoreAfterNotFound: restore.render(),
1661
+ restoreLive: restore.render()
1662
+ };
1663
+ }
1664
+ };
1665
+ }
1666
+ function isResourceNotFoundException(error) {
1667
+ return typeof error === "object" && error !== null && error.name === "ResourceNotFoundException";
1668
+ }
1669
+ function logGroupProtectionSite(physicalId, region) {
1670
+ return {
1671
+ subject: "Log group",
1672
+ guardName: "DeletionProtectionEnabled",
1673
+ noun: "log group",
1674
+ isNotFound: isResourceNotFoundException,
1675
+ notFoundMeaning: "CloudWatch Logs answered ResourceNotFoundException. That most commonly means the log group is gone, and it can also mean it is not in this region or account.",
1676
+ commands: () => {
1677
+ const aws = pasteableAwsCommand();
1678
+ const regionArg = region ? aws` --region ${region}` : aws``;
1679
+ const restore = aws`aws logs put-log-group-deletion-protection --log-group-identifier ${physicalId}${regionArg} --deletion-protection-enabled`;
1680
+ return {
1681
+ check: aws`aws logs describe-log-groups --log-group-identifiers ${physicalId}${regionArg}`.render(),
1682
+ restoreAfterNotFound: restore.render(),
1683
+ restoreLive: restore.render()
1684
+ };
1685
+ }
1686
+ };
1687
+ }
1688
+ function userPoolProtectionSite(physicalId, region) {
1689
+ return {
1690
+ subject: "Cognito User Pool",
1691
+ guardName: "DeletionProtection",
1692
+ noun: "user pool",
1693
+ isNotFound: isResourceNotFoundException,
1694
+ notFoundMeaning: "Cognito answered ResourceNotFoundException. That most commonly means the user pool is gone, and it can also mean it is not in this region or account.",
1695
+ commands: () => {
1696
+ const aws = pasteableAwsCommand();
1697
+ const regionArg = region ? aws` --region ${region}` : aws``;
1698
+ const restore = aws`aws cognito-idp update-user-pool --user-pool-id ${physicalId}${regionArg} --deletion-protection ACTIVE`;
1699
+ return {
1700
+ check: aws`aws cognito-idp describe-user-pool --user-pool-id ${physicalId}${regionArg}`.render(),
1701
+ restoreAfterNotFound: restore.render(),
1702
+ restoreLive: restore.render()
1703
+ };
1704
+ },
1705
+ restoreCaveat: "Note UpdateUserPool resets some members a call omits (self sign-up, Lambda triggers, advanced security, AutoVerifiedAttributes and a customer-managed KeyConfiguration among them), so send your complete pool configuration alongside that flag rather than the flag alone."
1706
+ };
1707
+ }
1708
+ async function compensateProtectionFlip(opts) {
1709
+ if (!opts.flip.flippedOffByThisRun) return "not-applicable";
1710
+ if (opts.flip.deleteAccepted) return "not-applicable";
1711
+ if (!isTerminalDeleteFailure(opts.error)) return "not-applicable";
1712
+ const { site } = opts;
1713
+ try {
1714
+ const result = await opts.reEnable();
1715
+ if (result === "read-on-write-failed") {
1716
+ opts.logger.warn(safeMsg`${site.subject} ${opts.logicalId}: the delete failed after ` + safeMsg`--remove-protection tried to turn ${site.guardName} off; ${opts.physicalId} ` + safeMsg`read it on afterwards, but writing it back failed, so cdkd cannot confirm ` + safeMsg`it. Check with: ${site.commands().check}. The delete failure below is the outcome.`);
1717
+ return "failed";
1718
+ }
1719
+ opts.logger.warn(result === "already-on" ? safeMsg`${site.subject} ${opts.logicalId}: the delete failed after ` + safeMsg`--remove-protection tried to turn ${site.guardName} off; ${opts.physicalId} ` + safeMsg`already read it on, and cdkd wrote it back (AWS accepted the write). The ` + safeMsg`delete failure below is the outcome.` : safeMsg`${site.subject} ${opts.logicalId}: the delete failed after ` + safeMsg`--remove-protection had turned ${site.guardName} off, so it was ` + safeMsg`re-enabled on ${opts.physicalId}. The delete failure below is the outcome.`);
1720
+ return "restored";
1721
+ } catch (reEnableError) {
1722
+ const mask = maskerOrIdentity(opts.mask);
1723
+ const failure = describeAwsFailure(reEnableError);
1724
+ const detail = mask(failure.summary);
1725
+ if (failure.redacted) opts.logger.debug(mask(safeMsg`${site.subject} ${opts.logicalId}: re-enabling ${site.guardName} on ` + safeMsg`${opts.physicalId} failed: ${mask(failure.detail)}`));
1726
+ const commands = site.commands();
1727
+ const caveat = site.restoreCaveat ? ` ${site.restoreCaveat}` : "";
1728
+ if (site.isNotFound(reEnableError)) {
1729
+ opts.logger.warn(safeMsg`${site.subject} ${opts.logicalId}: could not re-enable ` + safeMsg`${site.guardName} on ${opts.physicalId} after the delete failed — ` + safeMsg`${site.notFoundMeaning} If it still exists, its deletion protection is OFF. ` + safeMsg`Check with: ${commands.check} and if it is there, restore it with: ` + safeMsg`${commands.restoreAfterNotFound}.${caveat} (${detail})`);
1730
+ return "failed";
1731
+ }
1732
+ opts.logger.error(safeMsg`${site.subject} ${opts.logicalId}: could NOT re-enable ` + safeMsg`${site.guardName} on ${opts.physicalId} after the delete failed — ` + safeMsg`that ${site.noun} is LIVE with its deletion protection still off. Restore it with: ` + safeMsg`${commands.restoreLive}.${caveat} (${detail})`);
1733
+ return "failed";
1734
+ }
1735
+ }
1736
+ async function observeThenDisableProtection(opts) {
1737
+ let observedOn = false;
1738
+ try {
1739
+ observedOn = await opts.observe();
1740
+ } catch (observeError) {
1741
+ const mask = maskerOrIdentity(opts.mask);
1742
+ opts.logger.debug(mask(safeMsg`Could not read ${opts.guardName} on ${mask(opts.physicalId)} before disabling it: ${mask(describeAwsFailure(observeError).detail)}`));
1743
+ }
1744
+ await opts.disable();
1745
+ if (observedOn) opts.flip.flippedOffByThisRun = true;
1746
+ }
1747
+ async function deleteWithProtectionCompensation(opts) {
1748
+ const flip = opts.registry.acquire(opts.key, opts.reuseWithinMs ?? 18e5, opts.clock);
1749
+ try {
1750
+ await opts.run(flip);
1751
+ } catch (error) {
1752
+ await settleProtectionFlip({
1753
+ registry: opts.registry,
1754
+ key: opts.key,
1755
+ flip,
1756
+ error,
1757
+ compensation: opts.compensation
1758
+ });
1759
+ throw error;
1760
+ }
1761
+ opts.registry.release(opts.key);
1762
+ }
1763
+ async function settleProtectionFlip(opts) {
1764
+ let outcome = "failed";
1765
+ try {
1766
+ outcome = await compensateProtectionFlip({
1767
+ ...opts.compensation,
1768
+ flip: opts.flip,
1769
+ error: opts.error
1770
+ });
1771
+ } catch {}
1772
+ if (isTerminalDeleteFailure(opts.error) && outcome !== "failed") {
1773
+ opts.registry.release(opts.key);
1774
+ return true;
1775
+ }
1776
+ return false;
1777
+ }
1778
+ const FINAL_SNAPSHOT_SETTLE_WAIT_MS = 9e5;
1779
+ const FINAL_SNAPSHOT_COMPENSATION_MARGIN_MS = 6e4;
1780
+ const FLIP_TRANSITION_GRACE_MS = 3e4;
1781
+ function finalSnapshotWaitBudgetMs(opts) {
1782
+ const budgetMs = Math.min(opts.maxWaitMs ?? 9e5, opts.remainingMs - FINAL_SNAPSHOT_COMPENSATION_MARGIN_MS);
1783
+ if (budgetMs <= 0) throw new Error(`--remove-protection turned DeletionProtection off, but the resource timeout leaves no time to wait for the ${opts.noun} to return to 'available', which the final snapshot its DeletionPolicy asks for needs. The delete was not sent and the guard is put back; re-run the destroy with a longer --resource-timeout`);
1784
+ return budgetMs;
1785
+ }
1786
+ const TERMINAL_DB_STATUSES = /* @__PURE__ */ new Set([
1787
+ "deleting",
1788
+ "failed",
1789
+ "stopped",
1790
+ "storage-full",
1791
+ "insufficient-capacity",
1792
+ "restore-error"
1793
+ ]);
1794
+ const PROCEED_DB_STATUSES = /* @__PURE__ */ new Set(["storage-optimization", "storage-initialization"]);
1795
+ function isProceedDbStatus(status) {
1796
+ return status === "available" || status !== void 0 && PROCEED_DB_STATUSES.has(status);
1797
+ }
1798
+ function isTerminalDbStatus(status) {
1799
+ if (status === void 0) return false;
1800
+ return TERMINAL_DB_STATUSES.has(status) || status.startsWith("inaccessible-") || status.startsWith("incompatible-") || status.endsWith("-failed");
1801
+ }
1802
+ function isTerminalDbWaitStatus(status) {
1803
+ return status !== "inaccessible-encryption-credentials-recoverable" && isTerminalDbStatus(status);
1804
+ }
1805
+ async function settleBeforeFinalSnapshot(opts, startTime, budgetMs) {
1806
+ const { noun, label, logicalId } = opts;
1807
+ let delay = 5e3;
1808
+ let graceSleptMs = 0;
1809
+ let sawOff = false;
1810
+ let sawTransitional = false;
1811
+ for (;;) {
1812
+ const current = await opts.read();
1813
+ if (current === void 0) return;
1814
+ const terminal = isTerminalDbStatus(current.status);
1815
+ const operational = current.status !== "available" && isProceedDbStatus(current.status);
1816
+ if (!isProceedDbStatus(current.status) && !terminal) sawTransitional = true;
1817
+ if (current.deletionProtection === false) sawOff = true;
1818
+ if (current.deletionProtection === false && (terminal || operational)) return;
1819
+ if (current.deletionProtection === true && terminal) throw new Error(`--remove-protection turned DeletionProtection off, but the ${noun} still reads it ON in status '${current.status ?? "unknown"}', which does not change on its own, and the final snapshot its DeletionPolicy asks for needs it 'available'. The delete was not sent and the guard is put back; re-run the destroy once the ${noun} is 'available'`);
1820
+ const settled = current.deletionProtection === false && current.status === "available";
1821
+ const inGrace = !sawTransitional && graceSleptMs < 3e4;
1822
+ if (settled && !inGrace) return;
1823
+ const step = inGrace ? Math.min(delay, FLIP_TRANSITION_GRACE_MS - graceSleptMs) : delay;
1824
+ if (Date.now() - startTime + step >= budgetMs) {
1825
+ if (settled) return;
1826
+ if (current.deletionProtection === false && current.status !== void 0 && current.status !== "modifying") return;
1827
+ break;
1828
+ }
1829
+ opts.logger.debug(safeMsg`${label} ${logicalId}: waiting for the DeletionProtection change to settle before the final snapshot (status ${current.status ?? "unknown"})`);
1830
+ await opts.sleep(step);
1831
+ if (sawOff) graceSleptMs += step;
1832
+ delay = Math.min(delay * 2, 1e4);
1833
+ }
1834
+ throw new Error(`--remove-protection turned DeletionProtection off, but the ${noun} did not return to 'available' within ${Math.max(1, Math.round(budgetMs / 6e4))} minutes, and the final snapshot its DeletionPolicy asks for needs it. The delete was not sent and the guard is put back; re-run the destroy once the ${noun} is 'available'`);
1835
+ }
1836
+ function isFinalSnapshotNotAvailableRefusal(error, stateFault) {
1837
+ if (!(error instanceof Error) || error.name !== stateFault) return false;
1838
+ return /(?:isn't|is not|isn’t) (?:currently )?in (?:the )?available state/i.test(error.message);
1839
+ }
1840
+ async function deleteAfterProtectionFlipWithFinalSnapshot(opts) {
1841
+ const budgetMs = finalSnapshotWaitBudgetMs(opts);
1842
+ const startTime = Date.now();
1843
+ await settleBeforeFinalSnapshot(opts, startTime, budgetMs);
1844
+ let delay = 5e3;
1845
+ let refusals = 0;
1846
+ for (;;) try {
1847
+ await opts.send();
1848
+ return;
1849
+ } catch (error) {
1850
+ if (!isFinalSnapshotNotAvailableRefusal(error, opts.stateFault)) throw error;
1851
+ refusals += 1;
1852
+ opts.logger.debug(safeMsg`${opts.label} ${opts.logicalId}: final-snapshot delete refused as not 'available' (refusal ${String(refusals)}); waiting for it to return to 'available' before retrying`);
1853
+ for (;;) {
1854
+ if (Date.now() - startTime + delay >= budgetMs) throw error;
1855
+ await opts.sleep(delay);
1856
+ delay = Math.min(delay * 2, 1e4);
1857
+ const current = await opts.read();
1858
+ if (current === void 0) break;
1859
+ if (current.deletionProtection === false && isProceedDbStatus(current.status)) break;
1860
+ if (current.deletionProtection === true || isTerminalDbStatus(current.status)) throw error;
1861
+ }
1862
+ }
1863
+ }
1864
+
1865
+ // src/provisioning/import-helpers.ts
1866
+ function remedyLogicalId(logicalId) {
1867
+ return isPasteableIdent(logicalId) ? logicalId : commandHole("logicalId");
1868
+ }
1869
+ function isPlainImportValue(value) {
1870
+ return isPlainIdent(value, { maxCodePoints: SECRET_REF_MAX_CODE_POINTS });
1871
+ }
1872
+ function isQuotableImportValue(value) {
1873
+ return value.split("|").every((segment) => segment === "" || isPlainImportValue(segment));
1874
+ }
1875
+ function quotedRemedyLogicalId(logicalId) {
1876
+ return isPasteableIdent(logicalId) ? logicalId : "<logicalId>";
1877
+ }
1878
+ function refusalTypeShown(resourceType) {
1879
+ return resourceTypeShown(resourceType) === resourceType ? resourceType : "(resource type not shown: it is not a plain identifier)";
1880
+ }
1881
+ const VALUE_NOT_SHOWN = "(not shown: it is not a plain identifier)";
1882
+ const PLAIN_JSON = /^[A-Za-z0-9"{}[\]:,._@/+=~-]+$/;
1883
+ function isPlainImportJson(value) {
1884
+ const json = JSON.stringify(value);
1885
+ return typeof json === "string" && json.length <= 2048 && PLAIN_JSON.test(json);
1886
+ }
1887
+ function readNameProperty(input, propertyName) {
1888
+ const value = input.properties?.[propertyName];
1889
+ return typeof value === "string" && value.length > 0 ? value : void 0;
1890
+ }
1891
+ function resolveExplicitPhysicalId(input, nameProperty) {
1892
+ if (input.knownPhysicalId) return input.knownPhysicalId;
1893
+ if (nameProperty) {
1894
+ const name = readNameProperty(input, nameProperty);
1895
+ if (name) return name;
1896
+ }
1897
+ }
1898
+ function normalizeAwsTagsToCfn(tags) {
1899
+ if (!tags) return [];
1900
+ const out = [];
1901
+ if (Array.isArray(tags)) for (const t of tags) {
1902
+ const obj = t;
1903
+ const k = (typeof obj["Key"] === "string" ? obj["Key"] : void 0) ?? (typeof obj["TagKey"] === "string" ? obj["TagKey"] : void 0) ?? (typeof obj["key"] === "string" ? obj["key"] : void 0);
1904
+ const v = (typeof obj["Value"] === "string" ? obj["Value"] : void 0) ?? (typeof obj["TagValue"] === "string" ? obj["TagValue"] : void 0) ?? (typeof obj["value"] === "string" ? obj["value"] : void 0);
1905
+ if (typeof k !== "string" || k.length === 0) continue;
1906
+ if (k.startsWith("aws:")) continue;
1907
+ out.push({
1908
+ Key: k,
1909
+ Value: typeof v === "string" ? v : ""
1910
+ });
1911
+ }
1912
+ else for (const [k, v] of Object.entries(tags)) {
1913
+ if (!k || k.startsWith("aws:")) continue;
1914
+ out.push({
1915
+ Key: k,
1916
+ Value: typeof v === "string" ? v : ""
1917
+ });
1918
+ }
1919
+ out.sort((a, b) => a.Key < b.Key ? -1 : a.Key > b.Key ? 1 : 0);
1920
+ return out;
1921
+ }
1922
+
1923
+ // src/provisioning/region-check.ts
1924
+ function assertRegionMatch(clientRegion, expectedRegion, resourceType, logicalId, physicalId, phase = "not-found", endpoint = "regional") {
1925
+ if (!expectedRegion) return;
1926
+ if (!clientRegion) throw new ProvisioningError(phase === "not-found" ? `Refusing to treat NotFound as idempotent delete success for ${logicalId} (${resourceType}): AWS client region is unknown but stack state expects ${expectedRegion}. The resource may exist in ${expectedRegion} and would be silently removed from state if this NotFound were trusted.` : `Refusing to ${phaseVerb(phase)} ${logicalId} (${resourceType}): AWS client region is unknown but stack state records the resource in ${expectedRegion}. ` + (endpoint === "global" ? `cdkd cannot confirm that this client was built for the stack this record belongs to, so the ${phaseVerb(phase)} is not issued. Point the ` : `cdkd cannot confirm that the physical id recorded in state names the resource this client would act on, so the ${phaseVerb(phase)} is not issued. Point the `) + `AWS client at ${expectedRegion} (AWS_REGION or your AWS profile) and re-run.`, resourceType, logicalId, physicalId);
1927
+ if (clientRegion !== expectedRegion) throw new ProvisioningError(phase === "not-found" ? `Refusing to treat NotFound as idempotent delete success for ${logicalId} (${resourceType}): AWS client region ${clientRegion} does not match stack state region ${expectedRegion}. The resource likely still exists in ${expectedRegion}; rerun the destroy with the correct region (e.g. --region ${expectedRegion}).` : `Refusing to ${phaseVerb(phase)} ${logicalId} (${resourceType}): AWS client region ${clientRegion} does not match stack state region ${expectedRegion}. ` + (endpoint === "global" ? `This service has one global endpoint, so the call would reach the same resource, but a client in ${clientRegion} was not built for the stack this ${expectedRegion} record belongs to, so the ${phaseVerb(phase)} is not issued. ` : `The physical id recorded in cdkd state names a resource in ${expectedRegion}, so this ${phaseVerb(phase)} would act on whatever carries that id in ${clientRegion} instead — which for a name-shaped physical id is a different resource that usually exists. `) + `Point the AWS client at ${expectedRegion} (AWS_REGION or your AWS profile) and re-run; when the run spans several regions at once (cdkd drift --all), select the stacks in one region per run, because no single client region is correct for all of them. If the recorded region is the wrong one, correct the state record (cdkd state show).`, resourceType, logicalId, physicalId);
1928
+ }
1929
+ function phaseVerb(phase) {
1930
+ return phase === "pre-update" ? "update" : "delete";
1931
+ }
1932
+
1933
+ // src/provisioning/not-gone.ts
1934
+ const NOT_GONE = Symbol.for("cdkd.notGone");
1935
+ const MAX_DEPTH = 10;
1936
+ function markNotGone(error) {
1937
+ try {
1938
+ if (typeof error !== "object" || error === null || !Object.isExtensible(error)) return error;
1939
+ Object.defineProperty(error, NOT_GONE, {
1940
+ value: true,
1941
+ enumerable: false,
1942
+ writable: false,
1943
+ configurable: true
1944
+ });
1945
+ } catch {}
1946
+ return error;
1947
+ }
1948
+ function isMarkedNotGone(error) {
1949
+ try {
1950
+ let current = error;
1951
+ for (let depth = 0; depth < MAX_DEPTH && typeof current === "object" && current !== null; depth++) {
1952
+ if (Object.prototype.hasOwnProperty.call(current, NOT_GONE)) return true;
1953
+ current = current.cause;
1954
+ }
1955
+ } catch {}
1956
+ return false;
1957
+ }
1958
+
1959
+ // src/types/resource.ts
1960
+ const RESOURCE_NOT_FOUND = Symbol("cdkd.resourceNotFound");
1961
+
1962
+ // src/provisioning/providers/ambiguous-create.ts
1963
+ const CREATION_DATE_SKEW_MARGIN_MS = 5e3;
1964
+ const AMBIGUOUS_LATCH_TTL_MS = 18e5;
1965
+ const MAX_TRACKED = 1e4;
1966
+ const createAttemptKey = (scope, logicalId) => injectiveKey(scope, ambientRegion() ?? "", getCurrentStackName() ?? "", logicalId);
1967
+ const setBounded = (map, key, value) => {
1968
+ if (!map.has(key) && map.size >= MAX_TRACKED) {
1969
+ const oldest = map.keys().next();
1970
+ if (!oldest.done) map.delete(oldest.value);
1971
+ }
1972
+ map.set(key, value);
1973
+ };
1974
+ const replayedInSend = (output) => {
1975
+ const attempts = output?.$metadata?.attempts;
1976
+ return typeof attempts === "number" && attempts > 1;
1977
+ };
1978
+ const isObject = (value) => typeof value === "object" && value !== null;
1979
+ const sendsAfterAmbiguous = /* @__PURE__ */ new WeakSet();
1980
+ const outputsAfterAmbiguous = /* @__PURE__ */ new WeakSet();
1981
+ const markReplayFollowedAmbiguous = (output) => {
1982
+ if (isObject(output)) outputsAfterAmbiguous.add(output);
1983
+ };
1984
+ const replayFollowedAmbiguousAttempt = (output) => isObject(output) && outputsAfterAmbiguous.has(output);
1985
+ const AMBIGUOUS_REPLAY_MIDDLEWARE = "cdkdAmbiguousReplayMiddleware";
1986
+ const trackAmbiguousReplays = (client) => {
1987
+ const stack = client.middlewareStack;
1988
+ if (typeof stack?.addRelativeTo !== "function") return;
1989
+ stack.addRelativeTo((next) => async (args) => {
1990
+ try {
1991
+ const result = await next(args);
1992
+ if (sendsAfterAmbiguous.has(args)) markReplayFollowedAmbiguous(result.output);
1993
+ return result;
1994
+ } catch (error) {
1995
+ if (isAmbiguousOutcomeError(error)) sendsAfterAmbiguous.add(args);
1996
+ throw error;
1997
+ }
1998
+ }, {
1999
+ name: AMBIGUOUS_REPLAY_MIDDLEWARE,
2000
+ relation: "after",
2001
+ toMiddleware: "retryMiddleware",
2002
+ override: true
2003
+ });
2004
+ };
2005
+ const replayedSendWindow = (output, attemptStartMs) => replayedInSend(output) ? {
2006
+ floorMs: attemptStartMs - CREATION_DATE_SKEW_MARGIN_MS,
2007
+ ceilingMs: Date.now() + CREATION_DATE_SKEW_MARGIN_MS,
2008
+ replayedInSend: true
2009
+ } : void 0;
2010
+ const ambiguousAttemptLead = (action, logicalId, window) => window.replayedInSend === true ? `The ${action} call for ${logicalId} succeeded only after the AWS SDK sent it again, following an attempt that failed without a definite answer` : `An earlier ${action} attempt for ${logicalId} failed without a definite answer`;
2011
+ const mergeWindow = (existing, next, armedAtMs) => existing === void 0 ? {
2012
+ floorMs: next.floorMs,
2013
+ ceilingMs: next.ceilingMs,
2014
+ armedAtMs
2015
+ } : {
2016
+ floorMs: Math.min(existing.floorMs, next.floorMs),
2017
+ ceilingMs: Math.max(existing.ceilingMs, next.ceilingMs),
2018
+ armedAtMs: Math.max(existing.armedAtMs, armedAtMs)
2019
+ };
2020
+ var AmbiguousCreateLatch = class {
2021
+ entries = /* @__PURE__ */ new Map();
2022
+ scope;
2023
+ constructor(scope) {
2024
+ this.scope = scope;
2025
+ }
2026
+ noteFailure(logicalId, error, attemptStartMs, carried) {
2027
+ if (!isAmbiguousOutcomeError(error) && !hasReplayMayCollide(error)) return;
2028
+ const key = createAttemptKey(this.scope, logicalId);
2029
+ const now = Date.now();
2030
+ let entry = mergeWindow(this.entries.get(key), {
2031
+ floorMs: attemptStartMs - CREATION_DATE_SKEW_MARGIN_MS,
2032
+ ceilingMs: now + CREATION_DATE_SKEW_MARGIN_MS
2033
+ }, now);
2034
+ if (carried !== void 0) entry = mergeWindow(entry, carried, now);
2035
+ setBounded(this.entries, key, entry);
2036
+ }
2037
+ take(logicalId) {
2038
+ const key = createAttemptKey(this.scope, logicalId);
2039
+ const entry = this.entries.get(key);
2040
+ this.entries.delete(key);
2041
+ if (entry === void 0 || Date.now() - entry.armedAtMs > 18e5) return;
2042
+ return {
2043
+ floorMs: entry.floorMs,
2044
+ ceilingMs: entry.ceilingMs
2045
+ };
2046
+ }
2047
+ resetForTests() {
2048
+ this.entries.clear();
2049
+ }
2050
+ };
2051
+ const isInsideWindow = (date, window) => date !== void 0 && date.getTime() >= window.floorMs && date.getTime() <= window.ceilingMs;
2052
+ var RecentIdSet = class {
2053
+ ids = /* @__PURE__ */ new Map();
2054
+ add(id) {
2055
+ setBounded(this.ids, id, true);
2056
+ }
2057
+ has(id) {
2058
+ return this.ids.has(id);
2059
+ }
2060
+ resetForTests() {
2061
+ this.ids.clear();
2062
+ }
2063
+ };
2064
+ const isRetryStrategyV2 = (value) => typeof value === "object" && value !== null && typeof value.acquireInitialRetryToken === "function" && typeof value.refreshRetryTokenForRetry === "function" && typeof value.recordSuccess === "function";
2065
+ function withoutServerErrorRetries(client) {
2066
+ const config = client.config;
2067
+ const base = config?.retryStrategy;
2068
+ if (config === void 0 || typeof base !== "function") return client;
2069
+ trackAmbiguousReplays(client);
2070
+ const resolveBase = base;
2071
+ const afterAmbiguous = /* @__PURE__ */ new WeakSet();
2072
+ config.retryStrategy = async () => {
2073
+ const strategy = await resolveBase();
2074
+ if (!isRetryStrategyV2(strategy)) return strategy;
2075
+ return {
2076
+ acquireInitialRetryToken: (scope) => strategy.acquireInitialRetryToken(scope),
2077
+ refreshRetryTokenForRetry: async (token, errorInfo) => {
2078
+ const ambiguous = isAmbiguousOutcomeError(errorInfo.error);
2079
+ const replayed = isObject(token) && afterAmbiguous.has(token);
2080
+ if (replayed) markReplayMayCollide(errorInfo.error);
2081
+ if (ambiguous && isTransientServerError(errorInfo.error)) throw new Error("cdkd: no SDK retry of a 5xx on a tokenless create");
2082
+ const next = await strategy.refreshRetryTokenForRetry(token, errorInfo);
2083
+ if ((ambiguous || replayed) && isObject(next)) afterAmbiguous.add(next);
2084
+ return next;
2085
+ },
2086
+ recordSuccess: (token) => strategy.recordSuccess(token)
2087
+ };
2088
+ };
2089
+ return client;
2090
+ }
2091
+
2092
+ // src/provisioning/update-removal.ts
2093
+ function clearOnUpdateRemoval(newValue, previousValue, clearValue) {
2094
+ if (newValue !== void 0) return newValue;
2095
+ if (previousValue !== void 0) return clearValue;
2096
+ }
2097
+ const NONE = Object.freeze([]);
2098
+ const NONE_REMOVED = /* @__PURE__ */ new Set();
2099
+ function removedTemplateKeys(baseline, desired) {
2100
+ if (typeof baseline !== "object" || baseline === null || Array.isArray(baseline)) return NONE;
2101
+ let removed;
2102
+ for (const key of Object.keys(baseline)) if (baseline[key] !== void 0 && desired[key] === void 0) (removed ??= []).push(key);
2103
+ return removed ?? NONE;
2104
+ }
2105
+ function copyDeclared(value) {
2106
+ if (Array.isArray(value)) return value.map(copyDeclared);
2107
+ if (typeof value !== "object" || value === null) return value;
2108
+ const out = {};
2109
+ for (const [key, member] of Object.entries(value)) out[key] = copyDeclared(member);
2110
+ return out;
2111
+ }
2112
+ function injectDeclared(declared, removed, properties) {
2113
+ if (declared === void 0) return properties;
2114
+ let out;
2115
+ for (const key of removed) {
2116
+ if (!declared.has(key)) continue;
2117
+ out ??= { ...properties };
2118
+ out[key] = copyDeclared(declared.get(key));
2119
+ }
2120
+ return out ?? properties;
2121
+ }
2122
+ function withoutInjectedRemovals(result, injected) {
2123
+ const effective = result.effectiveProperties;
2124
+ if (effective === void 0 || !injected.some((key) => Object.hasOwn(effective, key))) return result;
2125
+ const recorded = { ...effective };
2126
+ for (const key of injected) delete recorded[key];
2127
+ return {
2128
+ ...result,
2129
+ effectiveProperties: recorded
2130
+ };
2131
+ }
2132
+ function prepareRemovalForUpdate(provider, resourceType, desired, baseline) {
2133
+ const removed = removedTemplateKeys(baseline, desired);
2134
+ const context = { removedProperties: removed.length === 0 ? NONE_REMOVED : new Set(removed) };
2135
+ if (removed.length === 0) return {
2136
+ properties: desired,
2137
+ injected: NONE,
2138
+ context,
2139
+ unhandled: NONE
2140
+ };
2141
+ const declared = provider.removalDefaults?.get(resourceType);
2142
+ const handled = provider.removalHandledInUpdate?.get(resourceType);
2143
+ const properties = injectDeclared(declared, removed, desired);
2144
+ const injected = declared === void 0 ? NONE : removed.filter((key) => declared.has(key));
2145
+ if (handled === void 0) return {
2146
+ properties,
2147
+ injected,
2148
+ context,
2149
+ unhandled: NONE
2150
+ };
2151
+ return {
2152
+ properties,
2153
+ injected,
2154
+ context,
2155
+ unhandled: removed.filter((key) => !declared?.has(key) && !handled.has(key))
2156
+ };
2157
+ }
2158
+ function withRemovalDefaults(declarations, resourceType, properties, previousProperties, context) {
2159
+ if (context?.removedProperties !== void 0) return properties;
2160
+ const declared = declarations?.get(resourceType);
2161
+ if (declared === void 0) return properties;
2162
+ return injectDeclared(declared, removedTemplateKeys(previousProperties, properties), properties);
2163
+ }
2164
+ function removalWarning(logicalId, resourceType, unhandled, caller = "deploy") {
2165
+ const one = unhandled.length === 1;
2166
+ const names = `${one ? "property" : "properties"} ${unhandled.join(", ")}`;
2167
+ return caller === "rollback" ? safeMsg`${logicalId} (${resourceType}): ${names} ${one ? "is" : "are"} absent from the state being restored; the rollback leaves the value the failed deploy applied in place (CloudFormation would reset it to its default).` : safeMsg`${logicalId} (${resourceType}): ${names} ${one ? "was" : "were"} removed from the template; cdkd leaves the current AWS value in place (CloudFormation would reset it to its default).`;
2168
+ }
2169
+
2170
+ // src/provisioning/iam-policy-targets.ts
2171
+ const IAM_PRINCIPAL_NAME = /^[\w+=,.@-]{1,128}$/;
2172
+ function readRecordedPrincipals(value) {
2173
+ if (value === void 0 || value === null) return { kind: "absent" };
2174
+ if (!Array.isArray(value)) return {
2175
+ kind: "malformed",
2176
+ detail: `found ${typeof value}`
2177
+ };
2178
+ if (!value.every((v) => typeof v === "string" && IAM_PRINCIPAL_NAME.test(v))) return {
2179
+ kind: "malformed",
2180
+ detail: `a ${value.length}-element list holding a non-name entry`
2181
+ };
2182
+ return {
2183
+ kind: "names",
2184
+ names: value
2185
+ };
2186
+ }
2187
+ function readPrincipalLists(values) {
2188
+ const lists = {};
2189
+ const malformed = [];
2190
+ const secretDerived = [];
2191
+ for (const key of Object.keys(values)) {
2192
+ const value = values[key];
2193
+ const recorded = readRecordedPrincipals(value);
2194
+ if (recorded.kind === "malformed") {
2195
+ malformed.push(key);
2196
+ if (holdsSecretDerivedEntry(value)) secretDerived.push(key);
2197
+ } else lists[key] = recorded.kind === "names" ? recorded.names : void 0;
2198
+ }
2199
+ return malformed.length > 0 ? {
2200
+ lists,
2201
+ malformed,
2202
+ secretDerived
2203
+ } : { lists };
2204
+ }
2205
+ const SECRET_MASK = "***";
2206
+ function holdsSecretDerivedEntry(value) {
2207
+ const seen = /* @__PURE__ */ new Set();
2208
+ const walk = (v, depth) => {
2209
+ if (typeof v === "string") return v === SECRET_MASK || v.includes("{{resolve:");
2210
+ if (typeof v !== "object" || v === null || depth > 64 || seen.has(v)) return false;
2211
+ seen.add(v);
2212
+ if (Array.isArray(v)) return v.some((e) => walk(e, depth + 1));
2213
+ return Object.entries(v).some(([k, e]) => walk(k, depth + 1) || walk(e, depth + 1));
2214
+ };
2215
+ return walk(value, 0);
2216
+ }
2217
+ function onlySecretDerived(read) {
2218
+ return "malformed" in read && read.malformed.every((k) => read.secretDerived.includes(k));
2219
+ }
2220
+ function recordedPrincipalsRepair(malformed, secretDerived, what, secretDerivedRepair) {
2221
+ const plain = malformed.filter((k) => !secretDerived.includes(k));
2222
+ const parts = [];
2223
+ if (plain.length > 0) parts.push(`repair the recorded ${plain.join(" / ")} in state.json to a list of ${what} and re-run`);
2224
+ if (secretDerived.length > 0) parts.push(`the recorded ${secretDerived.join(" / ")} is secret-derived (cdkd keeps the dynamic reference or its mask in state), so do not write the name into state.json; ` + secretDerivedRepair);
2225
+ return parts.join("; ");
2226
+ }
2227
+ const SECRET_DERIVED_READ_LIVE = "cdkd reads it from IAM instead once every other list is well-formed";
2228
+ const RESOLVED_PREVIOUS_PRINCIPAL_KEYS = {
2229
+ "AWS::IAM::Policy": [
2230
+ "Roles",
2231
+ "Groups",
2232
+ "Users"
2233
+ ],
2234
+ "AWS::IAM::UserToGroupAddition": ["Users"]
2235
+ };
2236
+ const ATTACHMENT_TARGET_KEY = {
2237
+ "AWS::IAM::Policy": "PolicyName",
2238
+ "AWS::IAM::UserToGroupAddition": "GroupName"
2239
+ };
2240
+ function holdsMask(value) {
2241
+ if (typeof value === "string") return value === SECRET_MASK;
2242
+ if (Array.isArray(value)) return value.some(holdsMask);
2243
+ if (typeof value === "object" && value !== null) return Object.values(value).some(holdsMask);
2244
+ return false;
2245
+ }
2246
+ function providerRecordedTarget(resourceType, physicalId) {
2247
+ if (resourceType !== "AWS::IAM::Policy") return void 0;
2248
+ return physicalId.includes(":") ? physicalId.split(":")[0] : physicalId;
2249
+ }
2250
+ function withUnchangedSecretPrincipalLists(resourceType, physicalId, previous, desiredRedacted) {
2251
+ const untouched = {
2252
+ previous,
2253
+ dropped: []
2254
+ };
2255
+ const keys = Object.prototype.hasOwnProperty.call(RESOLVED_PREVIOUS_PRINCIPAL_KEYS, resourceType) ? RESOLVED_PREVIOUS_PRINCIPAL_KEYS[resourceType] : [];
2256
+ if (keys.length === 0) return untouched;
2257
+ const target = ATTACHMENT_TARGET_KEY[resourceType];
2258
+ const recordedTarget = previous[target];
2259
+ const derivedTarget = providerRecordedTarget(resourceType, physicalId);
2260
+ if (holdsSecretDerivedEntry(recordedTarget) || JSON.stringify(recordedTarget) !== JSON.stringify(desiredRedacted[target]) || derivedTarget !== void 0 && recordedTarget !== derivedTarget) return untouched;
2261
+ const rewritten = [];
2262
+ for (const key of keys) {
2263
+ if (!Object.prototype.hasOwnProperty.call(previous, key)) continue;
2264
+ const recorded = previous[key];
2265
+ if (!holdsSecretDerivedEntry(recorded)) continue;
2266
+ const redacted = desiredRedacted[key];
2267
+ if (!Array.isArray(recorded) || !Array.isArray(redacted) || holdsMask(recorded)) return untouched;
2268
+ const kept = [];
2269
+ for (const entry of recorded) {
2270
+ if (!holdsSecretDerivedEntry(entry)) {
2271
+ kept.push(entry);
2272
+ continue;
2273
+ }
2274
+ if (typeof entry !== "string" || !redacted.includes(entry)) return untouched;
2275
+ }
2276
+ rewritten.push([key, kept]);
2277
+ }
2278
+ if (rewritten.length === 0) return untouched;
2279
+ const out = { ...previous };
2280
+ for (const [key, kept] of rewritten) out[key] = kept;
2281
+ return {
2282
+ previous: out,
2283
+ dropped: rewritten.map(([key]) => key)
2284
+ };
2285
+ }
2286
+
2287
+ // src/provisioning/created-resource-sink.ts
2288
+ var CreatedResourceSink = class {
2289
+ logicalId;
2290
+ resourceType;
2291
+ reports = [];
2292
+ constructor(logicalId, resourceType) {
2293
+ this.logicalId = logicalId;
2294
+ this.resourceType = resourceType;
2295
+ }
2296
+ get report() {
2297
+ return this.reports.at(-1);
2298
+ }
2299
+ record(report) {
2300
+ this.reports.push(report);
2301
+ }
2302
+ clear() {
2303
+ this.reports.length = 0;
2304
+ }
2305
+ retract(physicalId) {
2306
+ for (let i = this.reports.length - 1; i >= 0; i--) if (this.reports[i].physicalId === physicalId) this.reports.splice(i, 1);
2307
+ }
2308
+ };
2309
+ const storage = new AsyncLocalStorage();
2310
+ function collectCreatedResources(sink, fn) {
2311
+ return storage.run(sink, fn);
2312
+ }
2313
+ function clearCreatedReport() {
2314
+ storage.getStore()?.clear();
2315
+ }
2316
+ function anchoredSink(ownerLogicalId, resourceType) {
2317
+ const sink = storage.getStore();
2318
+ return sink !== void 0 && sink.logicalId === ownerLogicalId && sink.resourceType === resourceType ? sink : void 0;
2319
+ }
2320
+ function reportCreatedResource(ownerLogicalId, resourceType, physicalId, createdResourceIdentity) {
2321
+ try {
2322
+ if (typeof physicalId !== "string" || physicalId === "") return;
2323
+ anchoredSink(ownerLogicalId, resourceType)?.record({
2324
+ physicalId,
2325
+ ...typeof createdResourceIdentity === "string" && createdResourceIdentity !== "" && { createdResourceIdentity }
2326
+ });
2327
+ } catch {}
2328
+ }
2329
+ function reportedCreatedResource(ownerLogicalId, resourceType) {
2330
+ try {
2331
+ return anchoredSink(ownerLogicalId, resourceType)?.report;
2332
+ } catch {
2333
+ return;
2334
+ }
2335
+ }
2336
+ function retractCreatedResource(ownerLogicalId, resourceType, physicalId) {
2337
+ try {
2338
+ anchoredSink(ownerLogicalId, resourceType)?.retract(physicalId);
2339
+ } catch {}
2340
+ }
2341
+
2342
+ export { isTerminalDeleteFailure as $, physicalIdShownBesideCommand as $n, formatLockExpiry as $t, setBounded as A, MalformedProducerRecordRefusalError as An, isThrottlingError as Ar, stateOrphanRecordRemedy as At, normalizeAwsTagsToCfn as B, formatError as Bn, createdBeforeFailure as Bt, AmbiguousCreateLatch as C, DeploymentNotApprovedError as Cn, isExplicitDenyError as Cr, compositeIdFormatMessage as Ct, isInsideWindow as D, LocalStartServiceError as Dn, retryClassificationText as Dr, packCompositeId as Dt, createAttemptKey as E, LocalInvokeBuildError as En, isTransientServerError as Er, logicalIdShown as Et, assertRegionMatch as F, ResourceUpdateNotSupportedError as Fn, maskerOrIdentity as Ft, PROTECTION_FLIP_REUSE_WINDOW_MS as G, TIMEOUT_FLAG_RESOURCE_TYPE as Gn, markAuxiliaryFailure as Gt, refusalTypeShown as H, isSoftAbort as Hn, hasCreatedBeforeFailure as Ht, VALUE_NOT_SHOWN as I, StackHasActiveImportsError as In, withDerivedNameMasks as It, deleteAfterProtectionFlipWithFinalSnapshot as J, UNSHOWABLE_VALUE as Jn, replayMadeAnotherResource as Jt, ProtectionFlipRegistry as K, SHELL_ACTIVE as Kn, markCreatedBeforeFailure as Kt, isPlainImportJson as L, StackTerminationProtectionError as Ln, RETRY_AUXILIARY_OWNER as Lt, RESOURCE_NOT_FOUND as M, PartialFailureError as Mn, markRedactedCause as Mr, createMaskedRetryLogger as Mt, isMarkedNotGone as N, ProvisioningError as Nn, wrapMaskedAwsError as Nr, isSecretDerivedValue as Nt, replayFollowedAmbiguousAttempt as O, LockError as On, THROTTLING_ERROR_NAMES as Or, resourceTypeShown as Ot, markNotGone as P, ResourceTimeoutError as Pn, maskDeep as Pt, isTerminalDbWaitStatus as Q, pasteableCommand as Qn, forceQuitRecoveryClause as Qt, isPlainImportValue as R, StateError as Rn, carryCreatedBeforeFailure as Rt, AMBIGUOUS_LATCH_TTL_MS as S, DeployCancelledError as Sn, isAmbiguousOutcomeError as Sr, canonicalizeRoute53QueryName as St, ambiguousAttemptLead as T, IntrinsicResolutionRefusalError as Tn, isRetryableTransientError as Tr, compositeIdSkipResult as Tt, remedyLogicalId as U, normalizeAwsError as Un, isAuxiliaryFailure as Ut, quotedRemedyLogicalId as V, isCdkdError as Vn, createdResourceIdentityBeforeFailure as Vt, resolveExplicitPhysicalId as W, withErrorHandling as Wn, isAuxiliaryMarkOf as Wt, isFinalSnapshotNotAvailableRefusal as X, hasClauseBreak as Xn, buildForceUnlockCommand as Xt, deleteWithProtectionCompensation as Y, commandHole as Yn, UNREPRODUCIBLE_LOCK_CLAUSE as Yt, isProceedDbStatus as Z, isInertUnquoted as Zn, buildLockContentionMessage as Zt, prepareRemovalForUpdate as _, AssetError as _n, stripControlChars as _r, beginCommandInterruptScope as _t, reportedCreatedResource as a, DEFAULT_STATE_PREFIX as an, setPasteableRunFlags as ar, settleProtectionFlip as at, withRemovalDefaults as b, CrossAccountSecretRefusalError as bn, formatRetryClassificationSignals as br, isInterruptedWaitError as bt, RESOLVED_PREVIOUS_PRINCIPAL_KEYS as c, producerCoordinateKey as cn, hasReplayMayCollide as cr, UNNAMEABLE_ID_CLAUSE as ct, onlySecretDerived as d, withPasteableAwsProfile as dn, isRecreateRetryableError as dr, pasteableAwsCommand as dt, formatLockOperation as en, plainOrDescribed as er, logGroupProtectionSite as et, readPrincipalLists as f, ambientClientDefaults as fn, isReplayedNameCollisionFrom as fr, protectedReplacementAdvice as ft, clearOnUpdateRemoval as g, AccountIdUnavailableError as gn, escapeRegExp as gr, monotonicNowMs as gt, withUnchangedSecretPrincipalLists as h, credentialFingerprint as hn, relayedNameCollision as hr, ElapsedBudgetRegistry as ht, reportCreatedResource as i, CUSTOM_RESOURCE_RESPONSE_PREFIX as in, withheldTargetClause as ir, runDeleteAttempt as it, withoutServerErrorRetries as j, NestedStackChildDirectDestroyError as jn, markNonRetryable as jr, createMaskedLogSinks as jt, replayedSendWindow as k, MacroExpansionError as kn, isMarkedNonRetryable as kr, segmentAfterAnchor as kt, SECRET_DERIVED_READ_LIVE as l, producerRecordKey as ln, isNameCollisionErrorFrom as lr, WITHHELD_AWS_COMMAND as lt, recordedPrincipalsRepair as m, clientDefaultsFor as mn, markReplayMayCollide as mr, ElapsedBudget as mt, clearCreatedReport as n, recoveryCommandFlags as nn, shellBoundedDisplay as nr, protectionFlipKey as nt, retractCreatedResource as o, injectiveKey as on, setPasteableVerbFlags as or, userPoolProtectionSite as ot, readRecordedPrincipals as p, ambientCredentialConfig as pn, markNameCollision as pr, renderDisableCommand as pt, compensateProtectionFlip as q, SHELL_ACTIVE_WHY as qn, markSupersedesEarlierCreate as qt, collectCreatedResources as r, sanitizeRecoveryValue as rn, shellQuote as rr, rdsFamilyProtectionSite as rt, IAM_PRINCIPAL_NAME as s, injectiveKeyPrefix as sn, isUpdateUnsupportedError as sr, DELETION_PROTECTION_DOC_POINTER as st, CreatedResourceSink as t, formatLockOwner as tn, quotedOrDescribed as tr, observeThenDisableProtection as tt, holdsSecretDerivedEntry as u, setPasteableAwsProfile as un, isNameCooldownError as ur, isAwsCliLiteral as ut, removalWarning as v, CdkdError as vn, stripForDisplay as vr, endCommandInterruptScope as vt, RecentIdSet as w, DynamicReferenceRegionAmbiguousError as wn, isIamPropagationError as wr, compositeIdSeparatorRefusal as wt, withoutInjectedRemovals as x, DependencyError as xn, isAmbiguousCcHandlerErrorCode as xr, startInterruptWatch as xt, removedTemplateKeys as y, ConfigError as yn, explicitDenyPolicyKind as yr, interruptWatchListenerCount as yt, isQuotableImportValue as z, SynthesisError as zn, clearCreatedBeforeFailure as zt };
2343
+ //# sourceMappingURL=created-resource-sink-CmrPwGge.js.map