@go-to-k/cdkd 0.267.3 → 0.267.4

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.
@@ -7486,6 +7486,266 @@ var ReplacementRulesRegistry = class {
7486
7486
  }
7487
7487
  };
7488
7488
 
7489
+ //#endregion
7490
+ //#region src/deployment/retryable-errors.ts
7491
+ /**
7492
+ * Patterns that mark an AWS error as a transient/retryable failure.
7493
+ * Each entry is a substring match against the error message; all of these
7494
+ * are situations where the same call typically succeeds after a short delay
7495
+ * because of eventual consistency or just-created-dependency propagation.
7496
+ */
7497
+ const RETRYABLE_ERROR_MESSAGE_PATTERNS = [
7498
+ "cannot be assumed",
7499
+ "Firehose is unable to assume role",
7500
+ "is unable to assume provided role",
7501
+ "role defined for the function",
7502
+ "not authorized to perform",
7503
+ "execution role",
7504
+ "trust policy",
7505
+ "Role validation failed",
7506
+ "does not have required permissions",
7507
+ "Trusted Entity",
7508
+ "currently in the following state: Pending",
7509
+ "has dependencies and cannot be deleted",
7510
+ "can't be deleted since it has",
7511
+ "DependencyViolation",
7512
+ "does not exist",
7513
+ "Schema is currently being altered",
7514
+ "Invalid principal in policy",
7515
+ "Policy Error: PrincipalNotFound",
7516
+ "Invalid value for the parameter Policy",
7517
+ "required permissions for: ENHANCED_MONITORING",
7518
+ "Caught ServiceAccessDeniedException",
7519
+ "permissions required to assume the role",
7520
+ "authorized to assume the provided role",
7521
+ "conflicting conditional operation",
7522
+ "scheduled for deletion",
7523
+ "Cannot access stream",
7524
+ "Please ensure the role can perform",
7525
+ "KMS key is invalid for CreateGrant",
7526
+ "Policy contains a statement with one or more invalid principals",
7527
+ "Invalid IAM Instance Profile",
7528
+ "Invalid InstanceProfile",
7529
+ "Failed to authorize instance profile",
7530
+ "Could not deliver test message",
7531
+ "wait 60 seconds",
7532
+ "concurrent update operation",
7533
+ "because it is in use",
7534
+ "Rate exceeded"
7535
+ ];
7536
+ /**
7537
+ * HTTP status codes that always indicate a transient failure worth retrying.
7538
+ * 429 = Too Many Requests (throttle), 503 = Service Unavailable.
7539
+ */
7540
+ const RETRYABLE_HTTP_STATUS_CODES = /* @__PURE__ */ new Set([429, 503]);
7541
+ /**
7542
+ * AWS SDK v3 canonical throttling error names. Mirrors
7543
+ * `@aws-sdk/service-error-classification`'s `THROTTLING_ERROR_CODES` — any
7544
+ * error (or wrapped cause) whose `name` is one of these is a transient rate-
7545
+ * limit rejection worth retrying with backoff. Detecting by NAME is more
7546
+ * robust than by HTTP status because most AWS throttles surface as HTTP 400
7547
+ * (not 429) with the throttling signal carried only in the error code / name
7548
+ * (e.g. SSM `ThrottlingException` for the `Rate exceeded` message).
7549
+ */
7550
+ const THROTTLING_ERROR_NAMES = /* @__PURE__ */ new Set([
7551
+ "BandwidthLimitExceeded",
7552
+ "EC2ThrottledException",
7553
+ "LimitExceededException",
7554
+ "PriorRequestNotComplete",
7555
+ "ProvisionedThroughputExceededException",
7556
+ "RequestLimitExceeded",
7557
+ "RequestThrottled",
7558
+ "RequestThrottledException",
7559
+ "SlowDown",
7560
+ "ThrottledException",
7561
+ "Throttling",
7562
+ "ThrottlingException",
7563
+ "TooManyRequestsException",
7564
+ "TransactionInProgressException"
7565
+ ]);
7566
+ /**
7567
+ * Walk the error + its `.cause` chain (bounded) looking for a rate-limit
7568
+ * signal — either an AWS SDK v3 throttling error `name`
7569
+ * ({@link THROTTLING_ERROR_NAMES}) or a retryable HTTP status
7570
+ * ({@link RETRYABLE_HTTP_STATUS_CODES}) on `$metadata`.
7571
+ *
7572
+ * cdkd wraps the original AWS error in a `ProvisioningError`, so the signal is
7573
+ * typically one cause-link deep; the bounded walk also tolerates SDK errors
7574
+ * that nest a `$response`/cause without exploding on a cyclic chain.
7575
+ *
7576
+ * BOTH signals are checked at EVERY depth. An earlier version checked the name
7577
+ * to depth 5 but the HTTP status only at depths 0 and 1, so a 429 nested two
7578
+ * links deep was missed.
7579
+ */
7580
+ function isThrottlingError(error) {
7581
+ let current = error;
7582
+ for (let depth = 0; depth < 5 && current != null; depth++) {
7583
+ const name = current.name;
7584
+ if (typeof name === "string" && THROTTLING_ERROR_NAMES.has(name)) return true;
7585
+ const status = current.$metadata?.httpStatusCode;
7586
+ if (status !== void 0 && RETRYABLE_HTTP_STATUS_CODES.has(status)) return true;
7587
+ current = current.cause;
7588
+ }
7589
+ return false;
7590
+ }
7591
+ /**
7592
+ * Determine whether an AWS error should be retried.
7593
+ *
7594
+ * Checks (in order):
7595
+ * 1. Rate-limit signal on the error or any wrapped cause — throttling error
7596
+ * `name` or retryable HTTP status (most AWS throttles are HTTP 400, not
7597
+ * 429, so the name check carries most of the weight). See
7598
+ * {@link isThrottlingError}.
7599
+ * 2. Substring match against {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}
7600
+ */
7601
+ function isRetryableTransientError(error, message) {
7602
+ if (isThrottlingError(error)) return true;
7603
+ return RETRYABLE_ERROR_MESSAGE_PATTERNS.some((p) => message.includes(p));
7604
+ }
7605
+ /**
7606
+ * Match the "already exists" name-collision signature raised when a create
7607
+ * targets a physical name still held by another resource (or by the same
7608
+ * name's not-yet-released tombstone after an async delete).
7609
+ *
7610
+ * Deliberately NOT part of {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}: a name
7611
+ * collision is only worth retrying at the specific re-create sites that just
7612
+ * deleted the old holder (the deploy engine's --replace delete-first fallback
7613
+ * and the rollback executor's reverse-replacement) — everywhere else it is a
7614
+ * genuine conflict that must fail fast. Shared by those sites' collision
7615
+ * detection + retry filters so a signature extension lands in one place.
7616
+ */
7617
+ function isNameCollisionError(message) {
7618
+ return /already exists/i.test(message) || message.includes("AlreadyExists");
7619
+ }
7620
+ /**
7621
+ * Match the SQS same-name re-creation cooldown: after `DeleteQueue`, creating
7622
+ * a queue with the SAME name inside ~60s fails with
7623
+ * `AWS.SimpleQueueService.QueueDeletedRecently` ("You must wait 60 seconds
7624
+ * after deleting a queue before you can create another with the same name").
7625
+ *
7626
+ * The generic transient table above already carries 'wait 60 seconds' for
7627
+ * plain CREATEs (rapid destroy → redeploy loops), but the delete-then-re-create
7628
+ * sites (the deploy engine's --replace delete-first fallback and the rollback
7629
+ * executor's reverse-replacement) override the retry filter with
7630
+ * {@link isNameCollisionError}, which this signature does NOT match — so a
7631
+ * replacement revert used to fail fast mid-flight with the resource absent
7632
+ * from both AWS and state (issue #1206). Those sites now OR this matcher into
7633
+ * their retry filter, with a schedule long enough to cover the 60s window.
7634
+ *
7635
+ * Kept separate from {@link isNameCollisionError} on purpose: a cooldown at a
7636
+ * create-first site must NOT be treated as a collision (deleting the new
7637
+ * resource would not release the cooldown on the old name).
7638
+ */
7639
+ function isNameCooldownError(message) {
7640
+ return message.includes("QueueDeletedRecently") || message.includes("wait 60 seconds");
7641
+ }
7642
+ /**
7643
+ * Retry filter for the delete-then-re-create sites: the old name holder was
7644
+ * just deleted, so both the late name release ("already exists" from an async
7645
+ * delete) and the SQS 60s name cooldown are worth waiting out. Pair with a
7646
+ * schedule that covers the full cooldown window (maxRetries 8, delays
7647
+ * 2s/4s/8s then capped at 10s ≈ 64s total sleep).
7648
+ */
7649
+ function isRecreateRetryableError(message) {
7650
+ return isNameCollisionError(message) || isNameCooldownError(message);
7651
+ }
7652
+
7653
+ //#endregion
7654
+ //#region src/deployment/retry.ts
7655
+ /**
7656
+ * Retry helper for resource provisioning operations that hit transient
7657
+ * AWS eventual-consistency errors (IAM propagation, Lambda Pending state,
7658
+ * dependency violations, etc.).
7659
+ *
7660
+ * Extracted from DeployEngine so the backoff schedule can be unit-tested
7661
+ * in isolation. The retryable-error classifier itself lives in
7662
+ * `./retryable-errors.ts`.
7663
+ */
7664
+ const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
7665
+ /**
7666
+ * Run `operation`, retrying transient failures with exponential backoff
7667
+ * capped at `maxDelayMs`.
7668
+ *
7669
+ * Backoff at the defaults (initialDelayMs=1_000, maxDelayMs=8_000, maxRetries=8):
7670
+ * 1s -> 2s -> 4s -> 8s -> 8s -> 8s -> 8s -> 8s (cumulative 47s)
7671
+ *
7672
+ * Non-retryable errors are rethrown immediately. The transient-error
7673
+ * classifier is `isRetryableTransientError` from ./retryable-errors.ts.
7674
+ */
7675
+ async function withRetry(operation, logicalId, opts = {}) {
7676
+ const maxRetries = opts.maxRetries ?? 8;
7677
+ const initialDelayMs = opts.initialDelayMs ?? 1e3;
7678
+ const maxDelayMs = opts.maxDelayMs ?? 8e3;
7679
+ const sleep = opts.sleep ?? defaultSleep;
7680
+ let lastError;
7681
+ for (let attempt = 0; attempt <= maxRetries; attempt++) try {
7682
+ return await operation();
7683
+ } catch (error) {
7684
+ lastError = error;
7685
+ const message = error instanceof Error ? error.message : String(error);
7686
+ if (!(opts.isRetryable ? opts.isRetryable(message, error) : isRetryableTransientError(error, message)) || attempt >= maxRetries) throw error;
7687
+ const delay = Math.min(initialDelayMs * Math.pow(2, attempt), maxDelayMs);
7688
+ opts.logger?.debug(` ⏳ Retrying ${logicalId} in ${delay / 1e3}s (attempt ${attempt + 1}/${maxRetries}) - ${message}`);
7689
+ for (let waited = 0; waited < delay; waited += 1e3) {
7690
+ if (opts.isInterrupted?.()) throw opts.onInterrupted ? opts.onInterrupted() : /* @__PURE__ */ new Error("Interrupted");
7691
+ await sleep(Math.min(1e3, delay - waited));
7692
+ }
7693
+ }
7694
+ throw lastError;
7695
+ }
7696
+
7697
+ //#endregion
7698
+ //#region src/provisioning/describe-type.ts
7699
+ /**
7700
+ * Shared `cloudformation:DescribeType` invocation with throttle-only retry
7701
+ * (issue #1236).
7702
+ *
7703
+ * DescribeType is throttled per-account, and a deploy can issue a burst of
7704
+ * them (the #1182 create-only schema prefetch describes every type in the
7705
+ * template at deploy start). A lookup issued moments later — the write-only
7706
+ * property resolution during a CC-routed UPDATE is the critical case — can
7707
+ * then be throttled past the SDK's own short retry, and the caller's graceful
7708
+ * fallback turns the transient throttle into a real failure: the minimal
7709
+ * update patch drops a load-bearing write-only property and the update
7710
+ * hard-fails (`AWS::ECS::Service.VolumeConfigurations`), or a replacement is
7711
+ * mis-classified from a missing create-only schema.
7712
+ *
7713
+ * The fix is to retry ONLY throttle-shaped failures ({@link isThrottlingError})
7714
+ * with the standard backoff before surfacing the error. Non-throttle failures
7715
+ * (a missing `cloudformation:DescribeType` permission being the important one)
7716
+ * are rethrown immediately so the callers' warn-and-fall-back path stays as
7717
+ * fast as before — a caller permanently without the permission must not pay a
7718
+ * retry sleep on every lookup.
7719
+ */
7720
+ /**
7721
+ * Test seam: overriding `sleep` lets unit tests drive the backoff schedule
7722
+ * without real waits (mirrors macro-expander's `retryDelays`).
7723
+ */
7724
+ const describeTypeRetryDelays = {};
7725
+ /**
7726
+ * Retries after the first attempt, throttle-shaped failures only. At the
7727
+ * default backoff (1s -> 2s -> 4s -> 8s) this adds at most ~15s of sleep —
7728
+ * enough to ride out a prefetch-burst throttle window, small next to the
7729
+ * failed-update + rollback cycle it prevents.
7730
+ */
7731
+ const MAX_THROTTLE_RETRIES = 4;
7732
+ /**
7733
+ * Issue `DescribeType` for a resource type, retrying throttle-shaped failures
7734
+ * with exponential backoff. Any other failure (or a throttle persisting past
7735
+ * the retry budget) is thrown to the caller unchanged.
7736
+ */
7737
+ function describeTypeWithThrottleRetry(resourceType) {
7738
+ return withRetry(() => getAwsClients().cloudFormation.send(new DescribeTypeCommand({
7739
+ Type: "RESOURCE",
7740
+ TypeName: resourceType
7741
+ })), resourceType, {
7742
+ maxRetries: MAX_THROTTLE_RETRIES,
7743
+ isRetryable: (_message, error) => isThrottlingError(error),
7744
+ logger: getLogger().child("DescribeType"),
7745
+ ...describeTypeRetryDelays.sleep ? { sleep: describeTypeRetryDelays.sleep } : {}
7746
+ });
7747
+ }
7748
+
7489
7749
  //#endregion
7490
7750
  //#region src/provisioning/create-only-properties.ts
7491
7751
  /**
@@ -7642,10 +7902,7 @@ function isIntrinsicShaped(value) {
7642
7902
  */
7643
7903
  async function fetchCreateOnlyPropertyPaths(resourceType) {
7644
7904
  const logger = getLogger().child("CreateOnlyProperties");
7645
- const response = await getAwsClients().cloudFormation.send(new DescribeTypeCommand({
7646
- Type: "RESOURCE",
7647
- TypeName: resourceType
7648
- }));
7905
+ const response = await describeTypeWithThrottleRetry(resourceType);
7649
7906
  const result = [];
7650
7907
  if (response.Schema) {
7651
7908
  const createOnly = JSON.parse(response.Schema).createOnlyProperties;
@@ -10826,10 +11083,7 @@ function getTopLevelWriteOnlyProperties(resourceType) {
10826
11083
  */
10827
11084
  async function fetchTopLevelWriteOnlyProperties(resourceType) {
10828
11085
  const logger = getLogger().child("WriteOnlyProperties");
10829
- const response = await getAwsClients().cloudFormation.send(new DescribeTypeCommand({
10830
- Type: "RESOURCE",
10831
- TypeName: resourceType
10832
- }));
11086
+ const response = await describeTypeWithThrottleRetry(resourceType);
10833
11087
  const result = /* @__PURE__ */ new Set();
10834
11088
  if (response.Schema) {
10835
11089
  const writeOnly = JSON.parse(response.Schema).writeOnlyProperties;
@@ -11354,7 +11608,7 @@ var CloudControlProvider = class {
11354
11608
  this.logger.debug(`Deleting resource ${logicalId} (${resourceType}), physical ID: ${physicalId}`);
11355
11609
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
11356
11610
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
11357
- const { ASGProvider } = await import("./asg-provider-Dj8krtJ3.js").then((n) => n.n);
11611
+ const { ASGProvider } = await import("./asg-provider-C8yQ0Br2.js").then((n) => n.n);
11358
11612
  await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
11359
11613
  return;
11360
11614
  }
@@ -16401,214 +16655,6 @@ function computeImplicitDeleteEdges(resources) {
16401
16655
  return edges;
16402
16656
  }
16403
16657
 
16404
- //#endregion
16405
- //#region src/deployment/retryable-errors.ts
16406
- /**
16407
- * Patterns that mark an AWS error as a transient/retryable failure.
16408
- * Each entry is a substring match against the error message; all of these
16409
- * are situations where the same call typically succeeds after a short delay
16410
- * because of eventual consistency or just-created-dependency propagation.
16411
- */
16412
- const RETRYABLE_ERROR_MESSAGE_PATTERNS = [
16413
- "cannot be assumed",
16414
- "Firehose is unable to assume role",
16415
- "is unable to assume provided role",
16416
- "role defined for the function",
16417
- "not authorized to perform",
16418
- "execution role",
16419
- "trust policy",
16420
- "Role validation failed",
16421
- "does not have required permissions",
16422
- "Trusted Entity",
16423
- "currently in the following state: Pending",
16424
- "has dependencies and cannot be deleted",
16425
- "can't be deleted since it has",
16426
- "DependencyViolation",
16427
- "does not exist",
16428
- "Schema is currently being altered",
16429
- "Invalid principal in policy",
16430
- "Policy Error: PrincipalNotFound",
16431
- "Invalid value for the parameter Policy",
16432
- "required permissions for: ENHANCED_MONITORING",
16433
- "Caught ServiceAccessDeniedException",
16434
- "permissions required to assume the role",
16435
- "authorized to assume the provided role",
16436
- "conflicting conditional operation",
16437
- "scheduled for deletion",
16438
- "Cannot access stream",
16439
- "Please ensure the role can perform",
16440
- "KMS key is invalid for CreateGrant",
16441
- "Policy contains a statement with one or more invalid principals",
16442
- "Invalid IAM Instance Profile",
16443
- "Invalid InstanceProfile",
16444
- "Failed to authorize instance profile",
16445
- "Could not deliver test message",
16446
- "wait 60 seconds",
16447
- "concurrent update operation",
16448
- "because it is in use",
16449
- "Rate exceeded"
16450
- ];
16451
- /**
16452
- * HTTP status codes that always indicate a transient failure worth retrying.
16453
- * 429 = Too Many Requests (throttle), 503 = Service Unavailable.
16454
- */
16455
- const RETRYABLE_HTTP_STATUS_CODES = /* @__PURE__ */ new Set([429, 503]);
16456
- /**
16457
- * AWS SDK v3 canonical throttling error names. Mirrors
16458
- * `@aws-sdk/service-error-classification`'s `THROTTLING_ERROR_CODES` — any
16459
- * error (or wrapped cause) whose `name` is one of these is a transient rate-
16460
- * limit rejection worth retrying with backoff. Detecting by NAME is more
16461
- * robust than by HTTP status because most AWS throttles surface as HTTP 400
16462
- * (not 429) with the throttling signal carried only in the error code / name
16463
- * (e.g. SSM `ThrottlingException` for the `Rate exceeded` message).
16464
- */
16465
- const THROTTLING_ERROR_NAMES = /* @__PURE__ */ new Set([
16466
- "BandwidthLimitExceeded",
16467
- "EC2ThrottledException",
16468
- "LimitExceededException",
16469
- "PriorRequestNotComplete",
16470
- "ProvisionedThroughputExceededException",
16471
- "RequestLimitExceeded",
16472
- "RequestThrottled",
16473
- "RequestThrottledException",
16474
- "SlowDown",
16475
- "ThrottledException",
16476
- "Throttling",
16477
- "ThrottlingException",
16478
- "TooManyRequestsException",
16479
- "TransactionInProgressException"
16480
- ]);
16481
- /**
16482
- * Walk the error + its `.cause` chain (bounded) looking for a rate-limit
16483
- * signal — either an AWS SDK v3 throttling error `name`
16484
- * ({@link THROTTLING_ERROR_NAMES}) or a retryable HTTP status
16485
- * ({@link RETRYABLE_HTTP_STATUS_CODES}) on `$metadata`.
16486
- *
16487
- * cdkd wraps the original AWS error in a `ProvisioningError`, so the signal is
16488
- * typically one cause-link deep; the bounded walk also tolerates SDK errors
16489
- * that nest a `$response`/cause without exploding on a cyclic chain.
16490
- *
16491
- * BOTH signals are checked at EVERY depth. An earlier version checked the name
16492
- * to depth 5 but the HTTP status only at depths 0 and 1, so a 429 nested two
16493
- * links deep was missed.
16494
- */
16495
- function isThrottlingError(error) {
16496
- let current = error;
16497
- for (let depth = 0; depth < 5 && current != null; depth++) {
16498
- const name = current.name;
16499
- if (typeof name === "string" && THROTTLING_ERROR_NAMES.has(name)) return true;
16500
- const status = current.$metadata?.httpStatusCode;
16501
- if (status !== void 0 && RETRYABLE_HTTP_STATUS_CODES.has(status)) return true;
16502
- current = current.cause;
16503
- }
16504
- return false;
16505
- }
16506
- /**
16507
- * Determine whether an AWS error should be retried.
16508
- *
16509
- * Checks (in order):
16510
- * 1. Rate-limit signal on the error or any wrapped cause — throttling error
16511
- * `name` or retryable HTTP status (most AWS throttles are HTTP 400, not
16512
- * 429, so the name check carries most of the weight). See
16513
- * {@link isThrottlingError}.
16514
- * 2. Substring match against {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}
16515
- */
16516
- function isRetryableTransientError(error, message) {
16517
- if (isThrottlingError(error)) return true;
16518
- return RETRYABLE_ERROR_MESSAGE_PATTERNS.some((p) => message.includes(p));
16519
- }
16520
- /**
16521
- * Match the "already exists" name-collision signature raised when a create
16522
- * targets a physical name still held by another resource (or by the same
16523
- * name's not-yet-released tombstone after an async delete).
16524
- *
16525
- * Deliberately NOT part of {@link RETRYABLE_ERROR_MESSAGE_PATTERNS}: a name
16526
- * collision is only worth retrying at the specific re-create sites that just
16527
- * deleted the old holder (the deploy engine's --replace delete-first fallback
16528
- * and the rollback executor's reverse-replacement) — everywhere else it is a
16529
- * genuine conflict that must fail fast. Shared by those sites' collision
16530
- * detection + retry filters so a signature extension lands in one place.
16531
- */
16532
- function isNameCollisionError(message) {
16533
- return /already exists/i.test(message) || message.includes("AlreadyExists");
16534
- }
16535
- /**
16536
- * Match the SQS same-name re-creation cooldown: after `DeleteQueue`, creating
16537
- * a queue with the SAME name inside ~60s fails with
16538
- * `AWS.SimpleQueueService.QueueDeletedRecently` ("You must wait 60 seconds
16539
- * after deleting a queue before you can create another with the same name").
16540
- *
16541
- * The generic transient table above already carries 'wait 60 seconds' for
16542
- * plain CREATEs (rapid destroy → redeploy loops), but the delete-then-re-create
16543
- * sites (the deploy engine's --replace delete-first fallback and the rollback
16544
- * executor's reverse-replacement) override the retry filter with
16545
- * {@link isNameCollisionError}, which this signature does NOT match — so a
16546
- * replacement revert used to fail fast mid-flight with the resource absent
16547
- * from both AWS and state (issue #1206). Those sites now OR this matcher into
16548
- * their retry filter, with a schedule long enough to cover the 60s window.
16549
- *
16550
- * Kept separate from {@link isNameCollisionError} on purpose: a cooldown at a
16551
- * create-first site must NOT be treated as a collision (deleting the new
16552
- * resource would not release the cooldown on the old name).
16553
- */
16554
- function isNameCooldownError(message) {
16555
- return message.includes("QueueDeletedRecently") || message.includes("wait 60 seconds");
16556
- }
16557
- /**
16558
- * Retry filter for the delete-then-re-create sites: the old name holder was
16559
- * just deleted, so both the late name release ("already exists" from an async
16560
- * delete) and the SQS 60s name cooldown are worth waiting out. Pair with a
16561
- * schedule that covers the full cooldown window (maxRetries 8, delays
16562
- * 2s/4s/8s then capped at 10s ≈ 64s total sleep).
16563
- */
16564
- function isRecreateRetryableError(message) {
16565
- return isNameCollisionError(message) || isNameCooldownError(message);
16566
- }
16567
-
16568
- //#endregion
16569
- //#region src/deployment/retry.ts
16570
- /**
16571
- * Retry helper for resource provisioning operations that hit transient
16572
- * AWS eventual-consistency errors (IAM propagation, Lambda Pending state,
16573
- * dependency violations, etc.).
16574
- *
16575
- * Extracted from DeployEngine so the backoff schedule can be unit-tested
16576
- * in isolation. The retryable-error classifier itself lives in
16577
- * `./retryable-errors.ts`.
16578
- */
16579
- const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
16580
- /**
16581
- * Run `operation`, retrying transient failures with exponential backoff
16582
- * capped at `maxDelayMs`.
16583
- *
16584
- * Backoff at the defaults (initialDelayMs=1_000, maxDelayMs=8_000, maxRetries=8):
16585
- * 1s -> 2s -> 4s -> 8s -> 8s -> 8s -> 8s -> 8s (cumulative 47s)
16586
- *
16587
- * Non-retryable errors are rethrown immediately. The transient-error
16588
- * classifier is `isRetryableTransientError` from ./retryable-errors.ts.
16589
- */
16590
- async function withRetry(operation, logicalId, opts = {}) {
16591
- const maxRetries = opts.maxRetries ?? 8;
16592
- const initialDelayMs = opts.initialDelayMs ?? 1e3;
16593
- const maxDelayMs = opts.maxDelayMs ?? 8e3;
16594
- const sleep = opts.sleep ?? defaultSleep;
16595
- let lastError;
16596
- for (let attempt = 0; attempt <= maxRetries; attempt++) try {
16597
- return await operation();
16598
- } catch (error) {
16599
- lastError = error;
16600
- const message = error instanceof Error ? error.message : String(error);
16601
- if (!(opts.isRetryable ? opts.isRetryable(message, error) : isRetryableTransientError(error, message)) || attempt >= maxRetries) throw error;
16602
- const delay = Math.min(initialDelayMs * Math.pow(2, attempt), maxDelayMs);
16603
- opts.logger?.debug(` ⏳ Retrying ${logicalId} in ${delay / 1e3}s (attempt ${attempt + 1}/${maxRetries}) - ${message}`);
16604
- for (let waited = 0; waited < delay; waited += 1e3) {
16605
- if (opts.isInterrupted?.()) throw opts.onInterrupted ? opts.onInterrupted() : /* @__PURE__ */ new Error("Interrupted");
16606
- await sleep(Math.min(1e3, delay - waited));
16607
- }
16608
- }
16609
- throw lastError;
16610
- }
16611
-
16612
16658
  //#endregion
16613
16659
  //#region src/deployment/resource-deadline.ts
16614
16660
  /**
@@ -17265,7 +17311,7 @@ const FLUSH_INTERVAL_MS = 2e3;
17265
17311
  const FLUSH_EVENT_THRESHOLD = 50;
17266
17312
  /** Build-time cdkd version, with a dev fallback for non-built contexts. */
17267
17313
  function getCdkdVersion() {
17268
- return "0.267.3";
17314
+ return "0.267.4";
17269
17315
  }
17270
17316
  /**
17271
17317
  * Generate a time-sortable unique run id, e.g.
@@ -19252,5 +19298,5 @@ var DeployEngine = class {
19252
19298
  };
19253
19299
 
19254
19300
  //#endregion
19255
- export { buildAssetRedirectMap as $, PartialFailureError as $t, findActionableSilentDrops as A, CFN_TEMPLATE_URL_LIMIT as At, resolveExplicitPhysicalId as B, getAwsClients as Bt, green as C, resolveCaptureObservedState as Ct, collectInlinePolicyNamesManagedBySiblings as D, resolveUseCdkBootstrapAssets as Dt, IAMRoleProvider as E, resolveStateBucketWithDefaultAndSource as Et, IntrinsicFunctionResolver as F, AssemblyReader as Ft, TemplateParser as G, ConfigError as Gt, applyRoleArnIfSet as H, setAwsClients as Ht, cfnRefValueFromPhysicalId as I, processStackMessages as It, rebuildClientForBucketRegion as J, LocalMigrateError as Jt, LockManager as K, DependencyError as Kt, refStateLookupFromResource as L, clearBucketRegionCache as Lt, slowCcOperationTimeoutMs as M, findLargeInlineResources as Mt, disableInstanceApiTermination as N, uploadCfnTemplate as Nt, clearOnUpdateRemoval as O, warnDeprecatedNoPrefixCliFlag as Ot, isTerminationProtectionPropagationError as P, expectedOwnerParam as Pt, WorkGraph as Q, NestedStackChildDirectDestroyError as Qt, WAFv2WebACLProvider as R, resolveBucketRegion as Rt, gray as S, resolveAutoAssetStorage as St, yellow as T, resolveStateBucketWithDefault as Tt, DiffCalculator as U, AssetError as Ut, assertRegionMatch as V, resetAwsClients as Vt, DagBuilder as W, CdkdError as Wt, AssetPublisher as X, LockError as Xt, shouldRetainResource as Y, LocalStartServiceError as Yt, stringifyValue as Z, MissingCdkCliError as Zt, isStatefulRecreateTargetSync as _, Synthesizer as _t, DeploymentEventsStore as a, StateError as an, ensureAssetStorage as at, bold as b, getLegacyStateBucketName as bt, replayFailedOperations as c, isCdkdError as cn, validateAssetBucketName as ct, withRetry as d, __exportAll as dn, formatDockerLoginError as dt, ProvisioningError as en, createAssetRedirectResolver as et, isRetryableTransientError as f, getDockerCmd as ft, MULTI_REGION_RECREATE_BLOCKED_TYPES as g, getDockerImageBySourceHash as gt, extractDeploymentEventError as h, AssetManifestLoader as ht, DeploymentEventsReader as i, StackTerminationProtectionError as in, BOOTSTRAP_MARKER_PREFIX as it, CloudControlProvider as j, MIGRATE_TMP_PREFIX as jt, ProviderRegistry as k, CFN_TEMPLATE_BODY_LIMIT as kt, replayRollback as l, normalizeAwsError as ln, validateContainerRepoName as lt, computeImplicitDeleteEdges as m, runDockerStreaming as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, ResourceUpdateNotSupportedError as nn, rewriteTemplateAssetReferences as nt, planFailedOps as o, SynthesisError as on, getBootstrapMarkerKey as ot, IMPLICIT_DELETE_DEPENDENCIES as p, runDockerForeground as pt, S3StateBackend as q, LocalInvokeBuildError as qt, DeployEngine as r, StackHasActiveImportsError as rn, AssetModeResolver as rt, planRollback as s, formatError as sn, parseBootstrapMarker as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, ResourceTimeoutError as tn, loadPublishableAssetManifest as tt, withResourceDeadline as u, withErrorHandling as un, buildDockerImage as ut, renderStatefulReason as v, synthesisStatusMessage as vt, red as w, resolveSkipPrefix as wt, cyan as x, resolveApp as xt, formatResourceLine as y, getDefaultStateBucketName as yt, normalizeAwsTagsToCfn as z, AwsClients as zt };
19256
- //# sourceMappingURL=deploy-engine-CXHDJKCt.js.map
19301
+ export { buildAssetRedirectMap as $, PartialFailureError as $t, slowCcOperationTimeoutMs as A, CFN_TEMPLATE_URL_LIMIT as At, applyRoleArnIfSet as B, getAwsClients as Bt, yellow as C, resolveCaptureObservedState as Ct, ProviderRegistry as D, resolveUseCdkBootstrapAssets as Dt, clearOnUpdateRemoval as E, resolveStateBucketWithDefaultAndSource as Et, refStateLookupFromResource as F, AssemblyReader as Ft, TemplateParser as G, ConfigError as Gt, withRetry as H, setAwsClients as Ht, WAFv2WebACLProvider as I, processStackMessages as It, rebuildClientForBucketRegion as J, LocalMigrateError as Jt, LockManager as K, DependencyError as Kt, normalizeAwsTagsToCfn as L, clearBucketRegionCache as Lt, isTerminationProtectionPropagationError as M, findLargeInlineResources as Mt, IntrinsicFunctionResolver as N, uploadCfnTemplate as Nt, findActionableSilentDrops as O, warnDeprecatedNoPrefixCliFlag as Ot, cfnRefValueFromPhysicalId as P, expectedOwnerParam as Pt, WorkGraph as Q, NestedStackChildDirectDestroyError as Qt, resolveExplicitPhysicalId as R, resolveBucketRegion as Rt, red as S, resolveAutoAssetStorage as St, collectInlinePolicyNamesManagedBySiblings as T, resolveStateBucketWithDefault as Tt, isRetryableTransientError as U, AssetError as Ut, DiffCalculator as V, resetAwsClients as Vt, DagBuilder as W, CdkdError as Wt, AssetPublisher as X, LockError as Xt, shouldRetainResource as Y, LocalStartServiceError as Yt, stringifyValue as Z, MissingCdkCliError as Zt, formatResourceLine as _, Synthesizer as _t, DeploymentEventsStore as a, StateError as an, ensureAssetStorage as at, gray as b, getLegacyStateBucketName as bt, replayFailedOperations as c, isCdkdError as cn, validateAssetBucketName as ct, IMPLICIT_DELETE_DEPENDENCIES as d, __exportAll as dn, formatDockerLoginError as dt, ProvisioningError as en, createAssetRedirectResolver as et, computeImplicitDeleteEdges as f, getDockerCmd as ft, renderStatefulReason as g, getDockerImageBySourceHash as gt, isStatefulRecreateTargetSync as h, AssetManifestLoader as ht, DeploymentEventsReader as i, StackTerminationProtectionError as in, BOOTSTRAP_MARKER_PREFIX as it, disableInstanceApiTermination as j, MIGRATE_TMP_PREFIX as jt, CloudControlProvider as k, CFN_TEMPLATE_BODY_LIMIT as kt, replayRollback as l, normalizeAwsError as ln, validateContainerRepoName as lt, MULTI_REGION_RECREATE_BLOCKED_TYPES as m, runDockerStreaming as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, ResourceUpdateNotSupportedError as nn, rewriteTemplateAssetReferences as nt, planFailedOps as o, SynthesisError as on, getBootstrapMarkerKey as ot, extractDeploymentEventError as p, runDockerForeground as pt, S3StateBackend as q, LocalInvokeBuildError as qt, DeployEngine as r, StackHasActiveImportsError as rn, AssetModeResolver as rt, planRollback as s, formatError as sn, parseBootstrapMarker as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, ResourceTimeoutError as tn, loadPublishableAssetManifest as tt, withResourceDeadline as u, withErrorHandling as un, buildDockerImage as ut, bold as v, synthesisStatusMessage as vt, IAMRoleProvider as w, resolveSkipPrefix as wt, green as x, resolveApp as xt, cyan as y, getDefaultStateBucketName as yt, assertRegionMatch as z, AwsClients as zt };
19302
+ //# sourceMappingURL=deploy-engine-DYrhAO-0.js.map