@go-to-k/cdkd 0.295.0 → 0.296.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 (33) hide show
  1. package/dist/{asg-provider-DuAHOXvT.js → asg-provider-AVCcoXnQ.js} +8 -6
  2. package/dist/asg-provider-AVCcoXnQ.js.map +1 -0
  3. package/dist/cli.js +2 -2
  4. package/dist/{deploy-engine-CuI_h_Ml.js → deploy-engine-D7204v7Z.js} +986 -183
  5. package/dist/deploy-engine-D7204v7Z.js.map +1 -0
  6. package/dist/{iam-policy-targets-CMSQGQnm.js → iam-policy-targets-DSDSdxXz.js} +145 -4
  7. package/dist/iam-policy-targets-DSDSdxXz.js.map +1 -0
  8. package/dist/{iam-role-provider-CyM-wxAp.js → iam-role-provider-CCPaZ3U4.js} +11 -5
  9. package/dist/iam-role-provider-CCPaZ3U4.js.map +1 -0
  10. package/dist/index.d.ts +37 -3
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -4
  13. package/dist/{logger-B46YMG0h.js → logger-Dvc7ddKe.js} +19 -3
  14. package/dist/{logger-B46YMG0h.js.map → logger-Dvc7ddKe.js.map} +1 -1
  15. package/dist/{neptune-provider-BGVccWhg.js → neptune-provider-D1t9NgT-.js} +20 -14
  16. package/dist/neptune-provider-D1t9NgT-.js.map +1 -0
  17. package/dist/{program-Cbxe8TgI.js → program-B0sp_SHG.js} +6219 -5942
  18. package/dist/program-B0sp_SHG.js.map +1 -0
  19. package/dist/{provider-classes-DiWFkAOH.js → provider-classes-DMOnX5s-.js} +989 -238
  20. package/dist/provider-classes-DMOnX5s-.js.map +1 -0
  21. package/dist/{rds-provider-D6_Rcnub.js → rds-provider-DltlcDiQ.js} +22 -15
  22. package/dist/rds-provider-DltlcDiQ.js.map +1 -0
  23. package/dist/{version-CMMITJVg.js → version-DUHDmEhz.js} +2 -2
  24. package/dist/{version-CMMITJVg.js.map → version-DUHDmEhz.js.map} +1 -1
  25. package/package.json +4 -4
  26. package/dist/asg-provider-DuAHOXvT.js.map +0 -1
  27. package/dist/deploy-engine-CuI_h_Ml.js.map +0 -1
  28. package/dist/iam-policy-targets-CMSQGQnm.js.map +0 -1
  29. package/dist/iam-role-provider-CyM-wxAp.js.map +0 -1
  30. package/dist/neptune-provider-BGVccWhg.js.map +0 -1
  31. package/dist/program-Cbxe8TgI.js.map +0 -1
  32. package/dist/provider-classes-DiWFkAOH.js.map +0 -1
  33. package/dist/rds-provider-D6_Rcnub.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { r as getAwsClients, s as __exportAll } from "./aws-clients-CnmtbrxD.js";
2
- import { A as displaySafe, E as UNRENDERABLE, F as safeMsg, L as truncateCodePoints, M as isPasteableIdent, O as displayAwsMessage, P as plainIdentOr, S as withoutGeneratedFallbackName, T as STACK_REF_MAX_CODE_POINTS, _ as getCurrentSkipPrefix, b as withSkipPrefix, c as getLiveRenderer, f as applyDefaultNameForFallback, g as generateResourceNameWithFallback, h as generateResourceName, j as displayStackName, k as displayIdent, m as explicitNamePropertyFor, n as getLogger, r as isStdoutReservedForPayload, s as installLogLineMaskerSource, v as getCurrentStackName, w as SECRET_REF_MAX_CODE_POINTS, x as withStackName, y as looksLikeCdkdGeneratedName } from "./logger-B46YMG0h.js";
3
- import { $t as StateError, A as isTerminalDeleteFailure, An as isAmbiguousCcHandlerErrorCode, At as clientDefaultsFor, Bn as isMarkedNonRetryable, Bt as IntrinsicResolutionRefusalError, C as refusalTypeShown, Cn as isNameCollisionErrorFrom, D as ProtectionFlipRegistry, Dt as withPasteableAwsProfile, En as isReplayedNameCollisionFrom, Fn as retryClassificationText, Gt as MalformedProducerRecordRefusalError, H as pasteableAwsCommand, Hn as markNonRetryable, In as describeAwsFailure, It as CrossAccountSecretRefusalError, J as isInterruptedWaitError, Jt as ProvisioningError, Lt as DependencyError, M as observeThenDisableProtection, Mn as isIamPropagationError, Mt as AccountIdUnavailableError, N as protectionFlipKey, Nn as isRetryableTransientError, Nt as AssetError, O as compensateProtectionFlip, On as markReplayMayCollide, Ot as ambientClientDefaults, Pn as isTransientServerError, Pt as CdkdError, Q as canonicalizeRoute53QueryName, R as DELETION_PROTECTION_DOC_POINTER, Rn as safeStringify$1, S as quotedRemedyLogicalId, Sn as hasReplayMayCollide, St as injectiveKey, T as resolveExplicitPhysicalId, Tn as isRecreateRetryableError, Tt as producerRecordKey, U as protectedReplacementAdvice, Un as markRedactedCause, Ut as LockError, Vn as isThrottlingError, Wn as wrapMaskedAwsError, Wt as MacroExpansionError, X as ElapsedBudgetRegistry, Xt as ResourceUpdateNotSupportedError, Y as startInterruptWatch, Yt as ResourceTimeoutError, Z as monotonicNowMs, _n as shellQuote, _t as formatLockOperation, at as segmentAfterAnchor, b as isQuotableImportValue, bt as CUSTOM_RESOURCE_RESPONSE_PREFIX, cn as UNSHOWABLE_VALUE, ct as isSecretDerivedValue, d as prepareRemovalForUpdate, dn as isInertUnquoted, dt as withDerivedNameMasks, en as SynthesisError, et as compositeIdSeparatorRefusal, f as removalWarning, fn as pasteableCommand, ft as UNREPRODUCIBLE_LOCK_CLAUSE, g as assertRegionMatch, gn as shellBoundedDisplay, gt as formatLockExpiry, h as RESOURCE_NOT_FOUND, hn as quotedOrDescribed, i as holdsSecretDerivedEntry, it as resourceTypeShown, jn as isAmbiguousOutcomeError, jt as credentialFingerprint, k as deleteWithProtectionCompensation, kn as formatRetryClassificationSignals, kt as ambientCredentialConfig, l as withUnchangedSecretPrincipalLists, ln as commandHole, lt as maskDeep, m as withoutInjectedRemovals, mn as plainOrDescribed, nt as logicalIdShown, on as SHELL_ACTIVE, ot as createMaskedLogSinks, pn as physicalIdShownBesideCommand, pt as buildForceUnlockCommand, rn as normalizeAwsError, sn as SHELL_ACTIVE_WHY, st as createMaskedRetryLogger, ut as maskerOrIdentity, vn as withheldTargetClause, vt as formatLockOwner, wn as isNameCooldownError, x as normalizeAwsTagsToCfn, xn as isUpdateUnsupportedError, xt as DEFAULT_STATE_PREFIX, yt as sanitizeRecoveryValue, zt as DynamicReferenceRegionAmbiguousError } from "./iam-policy-targets-CMSQGQnm.js";
2
+ import { A as displaySafe, E as UNRENDERABLE, F as safeMsg, L as truncateCodePoints, M as isPasteableIdent, O as displayAwsMessage, P as plainIdentOr, S as withoutGeneratedFallbackName, T as STACK_REF_MAX_CODE_POINTS, _ as getCurrentSkipPrefix, b as withSkipPrefix, c as getLiveRenderer, f as applyDefaultNameForFallback, g as generateResourceNameWithFallback, h as generateResourceName, j as displayStackName, k as displayIdent, m as explicitNamePropertyFor, n as getLogger, r as isStdoutReservedForPayload, s as installLogLineMaskerSource, v as getCurrentStackName, w as SECRET_REF_MAX_CODE_POINTS, x as withStackName, y as looksLikeCdkdGeneratedName } from "./logger-Dvc7ddKe.js";
3
+ import { $n as markRedactedCause, $t as LockError, A as isTerminalDeleteFailure, At as CUSTOM_RESOURCE_RESPONSE_PREFIX, Bn as isAmbiguousCcHandlerErrorCode, Bt as clientDefaultsFor, C as refusalTypeShown, Cn as physicalIdShownBesideCommand, Ct as buildForceUnlockCommand, D as ProtectionFlipRegistry, Dn as shellQuote, Dt as formatLockOperation, En as shellBoundedDisplay, Et as formatLockExpiry, Fn as isRecreateRetryableError, Ft as producerRecordKey, Gn as retryClassificationText, H as pasteableAwsCommand, Hn as isIamPropagationError, Ht as AccountIdUnavailableError, In as isReplayedNameCollisionFrom, J as isInterruptedWaitError, Jn as safeStringify$1, Kn as describeAwsFailure, Kt as CrossAccountSecretRefusalError, Lt as withPasteableAwsProfile, M as observeThenDisableProtection, Mn as hasReplayMayCollide, Mt as injectiveKey, N as protectionFlipKey, Nn as isNameCollisionErrorFrom, O as compensateProtectionFlip, On as withheldTargetClause, Ot as formatLockOwner, Pn as isNameCooldownError, Q as canonicalizeRoute53QueryName, Qn as markNonRetryable, R as DELETION_PROTECTION_DOC_POINTER, Rn as markReplayMayCollide, Rt as ambientClientDefaults, S as quotedRemedyLogicalId, Sn as pasteableCommand, St as UNREPRODUCIBLE_LOCK_CLAUSE, T as resolveExplicitPhysicalId, Tn as quotedOrDescribed, U as protectedReplacementAdvice, Un as isRetryableTransientError, Ut as AssetError, Vn as isAmbiguousOutcomeError, Vt as credentialFingerprint, Wn as isTransientServerError, Wt as CdkdError, X as ElapsedBudgetRegistry, Xn as isMarkedNonRetryable, Xt as IntrinsicResolutionRefusalError, Y as startInterruptWatch, Yt as DynamicReferenceRegionAmbiguousError, Z as monotonicNowMs, Zn as isThrottlingError, _n as SHELL_ACTIVE_WHY, _t as hasCreatedBeforeFailure, an as ResourceTimeoutError, at as segmentAfterAnchor, b as isQuotableImportValue, bt as markAuxiliaryFailure, ct as createMaskedRetryLogger, d as prepareRemovalForUpdate, dt as maskerOrIdentity, en as MacroExpansionError, er as wrapMaskedAwsError, et as compositeIdSeparatorRefusal, f as removalWarning, ft as withDerivedNameMasks, g as assertRegionMatch, gn as SHELL_ACTIVE, gt as createdBeforeFailure, h as RESOURCE_NOT_FOUND, i as holdsSecretDerivedEntry, in as ProvisioningError, it as resourceTypeShown, jn as isUpdateUnsupportedError, jt as DEFAULT_STATE_PREFIX, k as deleteWithProtectionCompensation, kt as sanitizeRecoveryValue, l as withUnchangedSecretPrincipalLists, ln as StateError, lt as isSecretDerivedValue, m as withoutInjectedRemovals, mt as carryCreatedBeforeFailure, nt as logicalIdShown, on as ResourceUpdateNotSupportedError, ot as stateOrphanRecordRemedy, pn as normalizeAwsError, pt as RETRY_AUXILIARY_OWNER, qt as DependencyError, st as createMaskedLogSinks, tn as MalformedProducerRecordRefusalError, un as SynthesisError, ut as maskDeep, vn as UNSHOWABLE_VALUE, vt as isAuxiliaryFailure, wn as plainOrDescribed, x as normalizeAwsTagsToCfn, xn as isInertUnquoted, xt as markCreatedBeforeFailure, yn as commandHole, yt as isAuxiliaryMarkOf, zn as formatRetryClassificationSignals, zt as ambientCredentialConfig } from "./iam-policy-targets-DSDSdxXz.js";
4
4
  import { a as setAssumedRoleCredentials, i as getPreAssumeEnvCredentials, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, s as ambientRegion, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-D0q0z4dG.js";
5
- import { t as getCdkdVersion } from "./version-CMMITJVg.js";
5
+ import { t as getCdkdVersion } from "./version-DUHDmEhz.js";
6
6
  import { createHash, randomUUID } from "node:crypto";
7
7
  import { AsyncLocalStorage } from "node:async_hooks";
8
8
  import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -2705,7 +2705,7 @@ var FileAssetPublisher = class {
2705
2705
 
2706
2706
  //#endregion
2707
2707
  //#region src/utils/regexp.ts
2708
- function escapeRegExp$2(value) {
2708
+ function escapeRegExp$3(value) {
2709
2709
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2710
2710
  }
2711
2711
  function stripControlChars(value) {
@@ -3123,7 +3123,7 @@ function trailingShortFlagOfCluster(arg) {
3123
3123
  for (const flag of ARGV_VALUE_BEARING_FLAGS) if (flag.length === 2 && flag.startsWith("-") && flag[1] !== "-" && flag[1] === last) return flag;
3124
3124
  }
3125
3125
  const ARGV_VALUE_TOKEN_RE = (() => {
3126
- const alternation = [...ARGV_VALUE_BEARING_FLAGS].sort((a, b) => b.length - a.length || a.localeCompare(b)).map(escapeRegExp$2).join("|");
3126
+ const alternation = [...ARGV_VALUE_BEARING_FLAGS].sort((a, b) => b.length - a.length || a.localeCompare(b)).map(escapeRegExp$3).join("|");
3127
3127
  if (alternation.length === 0 || [...ARGV_VALUE_BEARING_FLAGS].some((f) => f.trim() === "")) throw new Error("ARGV_VALUE_BEARING_FLAGS is empty or holds an empty flag: the token scan would match every k=v");
3128
3128
  return new RegExp(`(^|\\s)(${alternation})(\\s+|=)([^\\s=]*)=\\S*`, "g");
3129
3129
  })();
@@ -4133,10 +4133,10 @@ function flattenAssetPlaceholders(value, accountId, region, partition = "aws") {
4133
4133
  return value.replace(/\$\{AWS::AccountId\}/g, accountId).replace(/\$\{AWS::Region\}/g, region).replace(/\$\{AWS::Partition\}/g, partition);
4134
4134
  }
4135
4135
  function isDefaultBootstrapBucketName(name, accountId, region) {
4136
- return new RegExp(`^cdk-[a-z0-9]+-assets-${accountId}-${escapeRegExp$2(region)}$`).test(name);
4136
+ return new RegExp(`^cdk-[a-z0-9]+-assets-${accountId}-${escapeRegExp$3(region)}$`).test(name);
4137
4137
  }
4138
4138
  function isDefaultBootstrapRepoName(name, accountId, region) {
4139
- return new RegExp(`^cdk-[a-z0-9]+-container-assets-${accountId}-${escapeRegExp$2(region)}$`).test(name);
4139
+ return new RegExp(`^cdk-[a-z0-9]+-container-assets-${accountId}-${escapeRegExp$3(region)}$`).test(name);
4140
4140
  }
4141
4141
  function buildAssetRedirectMap(manifest, marker, accountId, region, partition = "aws") {
4142
4142
  const buckets = /* @__PURE__ */ new Map();
@@ -4145,7 +4145,7 @@ function buildAssetRedirectMap(manifest, marker, accountId, region, partition =
4145
4145
  const addForms = (rawName, flattened, target) => {
4146
4146
  sources.set(flattened, target);
4147
4147
  if (rawName !== flattened) sources.set(rawName, target);
4148
- const suffixRe = new RegExp(`-${accountId}-${escapeRegExp$2(region)}$`);
4148
+ const suffixRe = new RegExp(`-${accountId}-${escapeRegExp$3(region)}$`);
4149
4149
  for (const suffix of [
4150
4150
  "-${AWS::AccountId}-${AWS::Region}",
4151
4151
  `-\${AWS::AccountId}-${region}`,
@@ -4186,7 +4186,7 @@ function buildAssetRedirectMap(manifest, marker, accountId, region, partition =
4186
4186
  };
4187
4187
  }
4188
4188
  function buildBoundaryRegex(source) {
4189
- return new RegExp(`(?<![A-Za-z0-9_.-])${escapeRegExp$2(source)}(?![A-Za-z0-9_-])`, "g");
4189
+ return new RegExp(`(?<![A-Za-z0-9_.-])${escapeRegExp$3(source)}(?![A-Za-z0-9_-])`, "g");
4190
4190
  }
4191
4191
  function rewriteString(value, map, counter) {
4192
4192
  let result = value;
@@ -4803,6 +4803,14 @@ function refuseMalformedOperation(shownStack, where, op) {
4803
4803
  const attempted = o["attemptedProperties"];
4804
4804
  if (attempted !== void 0 && (typeof attempted !== "object" || attempted === null || Array.isArray(attempted))) fail("attemptedProperties", attempted, "an object when present");
4805
4805
  if (o["oldResourceRetained"] !== void 0 && typeof o["oldResourceRetained"] !== "boolean") fail("oldResourceRetained", o["oldResourceRetained"], "a boolean when present");
4806
+ if (o["physicalIdRecoveredFromError"] !== void 0 && typeof o["physicalIdRecoveredFromError"] !== "boolean") fail("physicalIdRecoveredFromError", o["physicalIdRecoveredFromError"], "a boolean when present");
4807
+ if (o["physicalIdRecoveredFromError"] !== void 0 && o["changeType"] !== "CREATE") refuseMalformed(shownStack, `${where}.physicalIdRecoveredFromError is only valid on a CREATE (got changeType ${kind(o["changeType"])}).`);
4808
+ if (o["deletionPolicy"] !== void 0 && ![
4809
+ "Delete",
4810
+ "Retain",
4811
+ "Snapshot",
4812
+ "RetainExceptOnCreate"
4813
+ ].includes(o["deletionPolicy"])) fail("deletionPolicy", o["deletionPolicy"], "one of Delete, Retain, Snapshot, RetainExceptOnCreate when present");
4806
4814
  if (o["previousResourceType"] !== void 0 && typeof o["previousResourceType"] !== "string") fail("previousResourceType", o["previousResourceType"], "a string when present");
4807
4815
  }
4808
4816
  function kind(v) {
@@ -4968,7 +4976,7 @@ async function rebuildClientForBucketRegion(client, bucket, opts = {}) {
4968
4976
 
4969
4977
  //#endregion
4970
4978
  //#region src/state/s3-state-backend.ts
4971
- function errorDetail$1(error) {
4979
+ function errorDetail$2(error) {
4972
4980
  return displaySafe(error instanceof Error ? error.message : error, { asciiOnly: true }) || "<unrenderable>";
4973
4981
  }
4974
4982
  const LEGACY_KEY_DEPTH = 2;
@@ -5088,7 +5096,7 @@ var S3StateBackend = class {
5088
5096
  } catch (error) {
5089
5097
  if (!isNoSuchKey(error)) {
5090
5098
  if (error instanceof StateError) throw error;
5091
- throw new StateError(`Failed to get state for stack ${shownStackName} (${shownRegionName}): ${errorDetail$1(error)}`, error instanceof Error ? error : void 0);
5099
+ throw new StateError(`Failed to get state for stack ${shownStackName} (${shownRegionName}): ${errorDetail$2(error)}`, error instanceof Error ? error : void 0);
5092
5100
  }
5093
5101
  this.logger.debug(`No state at new key for stack: ${shownStackName} (${shownRegionName})`);
5094
5102
  }
@@ -5135,7 +5143,7 @@ var S3StateBackend = class {
5135
5143
  }));
5136
5144
  this.logger.info(`Migrated state for stack ${this.displayName(stackName)} to region-scoped layout (${displayIdent(region)})`);
5137
5145
  } catch (deleteError) {
5138
- this.logger.warn(`Migrated stack ${this.displayName(stackName)} to new key, but failed to delete legacy key: ${errorDetail$1(deleteError)}`);
5146
+ this.logger.warn(`Migrated stack ${this.displayName(stackName)} to new key, but failed to delete legacy key: ${errorDetail$2(deleteError)}`);
5139
5147
  }
5140
5148
  await notifyStateSaved(stackName, region);
5141
5149
  return response.ETag;
@@ -5145,7 +5153,7 @@ var S3StateBackend = class {
5145
5153
  bucket: this.config.bucket,
5146
5154
  operation: "PutObject"
5147
5155
  });
5148
- throw new StateError(`Failed to save state for stack ${this.stackRef(stackName, region)}: ${errorDetail$1(normalized)}`, normalized);
5156
+ throw new StateError(`Failed to save state for stack ${this.stackRef(stackName, region)}: ${errorDetail$2(normalized)}`, normalized);
5149
5157
  }
5150
5158
  }
5151
5159
  async deleteState(stackName, region) {
@@ -5177,7 +5185,7 @@ var S3StateBackend = class {
5177
5185
  bucket: this.config.bucket,
5178
5186
  operation: "DeleteObject"
5179
5187
  });
5180
- throw new StateError(`Failed to delete state for stack ${this.stackRef(stackName, region)}: ${errorDetail$1(normalized)}`, normalized);
5188
+ throw new StateError(`Failed to delete state for stack ${this.stackRef(stackName, region)}: ${errorDetail$2(normalized)}`, normalized);
5181
5189
  } finally {
5182
5190
  await this.deleteRollbackJournal(stackName, region);
5183
5191
  }
@@ -5199,7 +5207,7 @@ var S3StateBackend = class {
5199
5207
  bucket: this.config.bucket,
5200
5208
  operation: "DeleteObject"
5201
5209
  });
5202
- throw new StateError(`Failed to delete legacy state for stack ${displayStackName(stackName)}: ${errorDetail$1(normalized)}`, normalized);
5210
+ throw new StateError(`Failed to delete legacy state for stack ${displayStackName(stackName)}: ${errorDetail$2(normalized)}`, normalized);
5203
5211
  }
5204
5212
  }
5205
5213
  async listStacks() {
@@ -5370,7 +5378,7 @@ var S3StateBackend = class {
5370
5378
  });
5371
5379
  } catch (error) {
5372
5380
  const describing = options.objectDescription ? ` (${options.objectDescription})` : "";
5373
- this.logger.warn(`Could not purge noncurrent versions of ${keys.length} key(s) in bucket '${this.config.bucket}': the purge could not be started. Their previous versions survive and remain readable via GetObject with a VersionId${describing}. Grant s3:ListBucketVersions and s3:DeleteObjectVersion on the state bucket, or purge the key(s) by hand. Underlying error: ${errorDetail$1(error)}`);
5381
+ this.logger.warn(`Could not purge noncurrent versions of ${keys.length} key(s) in bucket '${this.config.bucket}': the purge could not be started. Their previous versions survive and remain readable via GetObject with a VersionId${describing}. Grant s3:ListBucketVersions and s3:DeleteObjectVersion on the state bucket, or purge the key(s) by hand. Underlying error: ${errorDetail$2(error)}`);
5374
5382
  }
5375
5383
  }
5376
5384
  async purgeNoncurrentVersionsUnderPrefix(keyPrefix, options = {}) {
@@ -5384,7 +5392,7 @@ var S3StateBackend = class {
5384
5392
  } catch (error) {
5385
5393
  const prefixShown = displayIdent(keyPrefix, { maxCodePoints: STACK_REF_MAX_CODE_POINTS });
5386
5394
  const describing = options.objectDescription ? ` (${options.objectDescription})` : "";
5387
- this.logger.warn(safeMsg`Could not purge noncurrent versions under the prefix ${prefixShown} in bucket ${this.config.bucket}: the purge could not be started. Their previous versions survive and remain readable via GetObject with a VersionId${describing}. Grant s3:ListBucketVersions and s3:DeleteObjectVersion on the state bucket, or purge the prefix by hand. Underlying error: ${errorDetail$1(error)}`);
5395
+ this.logger.warn(safeMsg`Could not purge noncurrent versions under the prefix ${prefixShown} in bucket ${this.config.bucket}: the purge could not be started. Their previous versions survive and remain readable via GetObject with a VersionId${describing}. Grant s3:ListBucketVersions and s3:DeleteObjectVersion on the state bucket, or purge the prefix by hand. Underlying error: ${errorDetail$2(error)}`);
5388
5396
  return {
5389
5397
  deletedBodies: 0,
5390
5398
  complete: false
@@ -5410,7 +5418,7 @@ var S3StateBackend = class {
5410
5418
  await this.saveCreateTokenLedger(stackName, region, ledger);
5411
5419
  } catch (error) {
5412
5420
  const normalized = normalizeAwsError(error, { bucket: this.config.bucket });
5413
- throw new StateError(`Failed to renew the create-token ledger of stack ${this.stackRef(stackName, region)}: ${errorDetail$1(normalized)}. The state record was left in place; re-run once the ledger can be written.`, normalized);
5421
+ throw new StateError(`Failed to renew the create-token ledger of stack ${this.stackRef(stackName, region)}: ${errorDetail$2(normalized)}. The state record was left in place; re-run once the ledger can be written.`, normalized);
5414
5422
  }
5415
5423
  }
5416
5424
  async deleteCreateTokenLedger(stackName, region) {
@@ -5427,7 +5435,7 @@ var S3StateBackend = class {
5427
5435
  bucket: this.config.bucket,
5428
5436
  operation: "DeleteObject"
5429
5437
  });
5430
- throw new StateError(`Failed to delete the create-token ledger for stack ${this.stackRef(stackName, region)}: ${errorDetail$1(normalized)}. The state record was left in place; re-run once the ledger can be deleted.`, normalized);
5438
+ throw new StateError(`Failed to delete the create-token ledger for stack ${this.stackRef(stackName, region)}: ${errorDetail$2(normalized)}. The state record was left in place; re-run once the ledger can be deleted.`, normalized);
5431
5439
  }
5432
5440
  }
5433
5441
  async loadRollbackJournal(stackName, region) {
@@ -5453,6 +5461,58 @@ var S3StateBackend = class {
5453
5461
  else newest.failedOperations = remaining;
5454
5462
  await this.putRawObject(this.getRollbackJournalKey(stackName, region), JSON.stringify(journal, null, 2));
5455
5463
  }
5464
+ async dropRollbackJournalFailedOperations(stackName, region, drop) {
5465
+ const journal = await this.loadRollbackJournal(stackName, region);
5466
+ if (!journal) return 0;
5467
+ let removed = 0;
5468
+ for (const segment of journal.segments) {
5469
+ if (!segment.failedOperations) continue;
5470
+ const kept = segment.failedOperations.filter((op) => !drop(op, segment));
5471
+ removed += segment.failedOperations.length - kept.length;
5472
+ if (kept.length === 0) delete segment.failedOperations;
5473
+ else segment.failedOperations = kept;
5474
+ }
5475
+ if (removed === 0) return 0;
5476
+ await this.putRawObject(this.getRollbackJournalKey(stackName, region), JSON.stringify(journal, null, 2));
5477
+ return removed;
5478
+ }
5479
+ async reduceRollbackJournalToFailedOperations(stackName, region, keep, supersededLogicalIds = [], demote = () => false) {
5480
+ const journal = await this.loadRollbackJournal(stackName, region);
5481
+ if (!journal) return 0;
5482
+ const carried = new Set(supersededLogicalIds);
5483
+ const kept = [];
5484
+ let count = 0;
5485
+ const keptBySegment = journal.segments.map((segment) => (segment.failedOperations ?? []).filter((op) => keep(op, segment)).map((op) => op.physicalIdRecoveredFromError === true && demote(op, segment) ? {
5486
+ ...op,
5487
+ physicalIdRecoveredFromError: false
5488
+ } : op));
5489
+ const keptIds = new Set(keptBySegment.flat().map((op) => op.logicalId));
5490
+ for (let s = journal.segments.length - 1; s >= 0; s--) {
5491
+ const segment = journal.segments[s];
5492
+ for (const op of segment.operations) carried.add(op.logicalId);
5493
+ for (const id of segment.supersededLogicalIds ?? []) carried.add(id);
5494
+ const failed = keptBySegment[s];
5495
+ if (failed.length === 0) continue;
5496
+ const reduced = {
5497
+ ...segment,
5498
+ initialDeploy: false,
5499
+ operations: [],
5500
+ failedOperations: failed
5501
+ };
5502
+ delete reduced.previousOutputs;
5503
+ delete reduced.previousCrossStackReads;
5504
+ addSupersededIds(reduced, [...carried].filter((id) => !keptIds.has(id) || segment.supersededLogicalIds?.includes(id)));
5505
+ kept.unshift(reduced);
5506
+ count += failed.length;
5507
+ }
5508
+ if (kept.length === 0) {
5509
+ await this.deleteRollbackJournal(stackName, region);
5510
+ return 0;
5511
+ }
5512
+ journal.segments = kept;
5513
+ await this.putRawObject(this.getRollbackJournalKey(stackName, region), JSON.stringify(journal, null, 2));
5514
+ return count;
5515
+ }
5456
5516
  async popRollbackJournalSegment(stackName, region) {
5457
5517
  const journal = await this.loadRollbackJournal(stackName, region);
5458
5518
  if (!journal || journal.segments.length === 0) {
@@ -5521,7 +5581,7 @@ var S3StateBackend = class {
5521
5581
  } catch (error) {
5522
5582
  if (!isNoSuchKey(error) && error.name !== "NotFound") {
5523
5583
  gone = false;
5524
- this.logger.warn(`Failed to delete rollback journal for ${this.stackRef(stackName, region)}: ${errorDetail$1(error)}`);
5584
+ this.logger.warn(`Failed to delete rollback journal for ${this.stackRef(stackName, region)}: ${errorDetail$2(error)}`);
5525
5585
  }
5526
5586
  }
5527
5587
  await this.purgeNoncurrentVersions([key], { objectDescription: "the rollback journal, whose `failedOperations[].attemptedProperties` records the properties of the failed write verbatim" });
@@ -5639,7 +5699,7 @@ var S3StateBackend = class {
5639
5699
  };
5640
5700
  } catch (error) {
5641
5701
  if (isNoSuchKey(error)) return null;
5642
- throw new StateError(`Failed to get legacy state for stack ${this.displayName(stackName)}: ${errorDetail$1(error)}`, error instanceof Error ? error : void 0);
5702
+ throw new StateError(`Failed to get legacy state for stack ${this.displayName(stackName)}: ${errorDetail$2(error)}`, error instanceof Error ? error : void 0);
5643
5703
  }
5644
5704
  }
5645
5705
  parseStateBody(bodyString, stackName) {
@@ -5691,7 +5751,7 @@ function safeSegment(value) {
5691
5751
  function stackRef(stackName, region) {
5692
5752
  return region === void 0 ? safeSegment(stackName) : `${safeSegment(stackName)} (${safeSegment(region)})`;
5693
5753
  }
5694
- function errorDetail(error) {
5754
+ function errorDetail$1(error) {
5695
5755
  return displaySafe(error instanceof Error ? error.message : error, { asciiOnly: true }) || "<unrenderable>";
5696
5756
  }
5697
5757
  function isLockInfoExpired(lockInfo) {
@@ -5827,7 +5887,7 @@ var LockManager = class {
5827
5887
  }
5828
5888
  return false;
5829
5889
  }
5830
- throw new LockError(`Failed to acquire lock for stack ${safeSegment(stackName)} (${safeSegment(region)}): ` + errorDetail(error), error instanceof Error ? error : void 0);
5890
+ throw new LockError(`Failed to acquire lock for stack ${safeSegment(stackName)} (${safeSegment(region)}): ` + errorDetail$1(error), error instanceof Error ? error : void 0);
5831
5891
  }
5832
5892
  }
5833
5893
  async putLockObject(key, lockInfo, condition = { IfNoneMatch: "*" }) {
@@ -5882,7 +5942,7 @@ var LockManager = class {
5882
5942
  return null;
5883
5943
  }
5884
5944
  if (error instanceof LockError) throw error;
5885
- throw new LockError(`Failed to get lock info for stack ${shownStack}: ${errorDetail(error)}`, error instanceof Error ? error : void 0);
5945
+ throw new LockError(`Failed to get lock info for stack ${shownStack}: ${errorDetail$1(error)}`, error instanceof Error ? error : void 0);
5886
5946
  }
5887
5947
  }
5888
5948
  async isLocked(stackName, region) {
@@ -5930,16 +5990,16 @@ var LockManager = class {
5930
5990
  this.logger.warn(`Not releasing the lock for stack ${stackRef(stackName, region)}: the conditional delete could not be evaluated here, and this process could not confirm the lock is still its own. Leaving it in place rather than risk deleting another process's lock. It expires on its own, or clear it with cdkd force-unlock. If this repeats, grant s3:GetObject on the lock key -- a delete conditioned on an ETag requires it.`);
5931
5991
  return;
5932
5992
  }
5933
- this.logger.debug(`Conditional lock release for stack ${stackRef(stackName, region)} is not supported here (${errorDetail(error)}); retrying unconditionally`);
5993
+ this.logger.debug(`Conditional lock release for stack ${stackRef(stackName, region)} is not supported here (${errorDetail$1(error)}); retrying unconditionally`);
5934
5994
  try {
5935
5995
  await this.deleteLock(stackName, region);
5936
5996
  this.logger.debug(`Lock released for stack: ${stackRef(stackName, region)}`);
5937
5997
  return;
5938
5998
  } catch (fallbackError) {
5939
- throw new LockError(`Failed to release lock for stack ${stackRef(stackName, region)}: ${errorDetail(fallbackError)}`, fallbackError instanceof Error ? fallbackError : void 0);
5999
+ throw new LockError(`Failed to release lock for stack ${stackRef(stackName, region)}: ${errorDetail$1(fallbackError)}`, fallbackError instanceof Error ? fallbackError : void 0);
5940
6000
  }
5941
6001
  }
5942
- throw new LockError(`Failed to release lock for stack ${stackRef(stackName, region)}: ${errorDetail(error)}`, error instanceof Error ? error : void 0);
6002
+ throw new LockError(`Failed to release lock for stack ${stackRef(stackName, region)}: ${errorDetail$1(error)}`, error instanceof Error ? error : void 0);
5943
6003
  } finally {
5944
6004
  await this.purgeLockVersions(this.getLockKey(stackName, region), "release");
5945
6005
  }
@@ -5998,7 +6058,7 @@ var LockManager = class {
5998
6058
  objectDescription: LOCK_OBJECT_DESCRIPTION
5999
6059
  });
6000
6060
  } catch (error) {
6001
- report(`Could not purge noncurrent versions of the lock key ${displayIdent(key, { maxCodePoints: STACK_REF_MAX_CODE_POINTS })} in bucket '${this.config.bucket}': the purge could not be started. Their previous versions survive and remain readable via GetObject with a VersionId (${LOCK_OBJECT_DESCRIPTION}). Grant s3:ListBucketVersions and s3:DeleteObjectVersion on the state bucket, or purge the key by hand. Underlying error: ${errorDetail(error)}`);
6061
+ report(`Could not purge noncurrent versions of the lock key ${displayIdent(key, { maxCodePoints: STACK_REF_MAX_CODE_POINTS })} in bucket '${this.config.bucket}': the purge could not be started. Their previous versions survive and remain readable via GetObject with a VersionId (${LOCK_OBJECT_DESCRIPTION}). Grant s3:ListBucketVersions and s3:DeleteObjectVersion on the state bucket, or purge the key by hand. Underlying error: ${errorDetail$1(error)}`);
6002
6062
  }
6003
6063
  }
6004
6064
  trackHeldLock(args) {
@@ -6078,7 +6138,7 @@ var LockManager = class {
6078
6138
  this.logger.warn(`Lost the lock for stack ${stackRef(held.stackName, held.region)} while the operation was still running: the lock object has been replaced or removed. Another cdkd process may now be writing to this stack concurrently. This process will not delete the current lock when it finishes.`);
6079
6139
  return;
6080
6140
  }
6081
- this.logger.debug(`Lock renewal for stack ${stackRef(held.stackName, held.region)} failed, will retry: ${errorDetail(error)}`);
6141
+ this.logger.debug(`Lock renewal for stack ${stackRef(held.stackName, held.region)} failed, will retry: ${errorDetail$1(error)}`);
6082
6142
  if (!held.warnedPastExpiry && Date.now() >= held.info.expiresAt) {
6083
6143
  held.warnedPastExpiry = true;
6084
6144
  this.logger.warn(`Lock renewal for stack ${stackRef(held.stackName, held.region)} has been failing long enough that the lock expired at ${new Date(held.info.expiresAt).toISOString()}. Another cdkd process can now take it while this operation is still running. Renewal keeps retrying.`);
@@ -6151,13 +6211,13 @@ function nanIfUncoercible(value) {
6151
6211
  //#endregion
6152
6212
  //#region src/deployment/secret-redaction/rules.ts
6153
6213
  const MIN_NEEDLE_LENGTH = 4;
6154
- function escapeRegExp$1(value) {
6214
+ function escapeRegExp$2(value) {
6155
6215
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6156
6216
  }
6157
6217
  function buildNeedleRegex(values) {
6158
6218
  const needles = Array.from(new Set(values)).filter((v) => v.length >= 4).sort((a, b) => b.length - a.length);
6159
6219
  if (needles.length === 0) return void 0;
6160
- return new RegExp(needles.map(escapeRegExp$1).join("|"), "g");
6220
+ return new RegExp(needles.map(escapeRegExp$2).join("|"), "g");
6161
6221
  }
6162
6222
  const TEMPLATE_DERIVED_RULES = {
6163
6223
  descendArrays: true,
@@ -7323,7 +7383,7 @@ function intrinsicSkeletonPattern(source) {
7323
7383
  return anchoredSkeletonPattern(segments);
7324
7384
  }
7325
7385
  function anchoredSkeletonPattern(segments) {
7326
- const body = joinSkeletonSegments(segments.map((segment) => segment === UNKNOWN_PART ? SKELETON_WILDCARD : escapeRegExp$1(segment)));
7386
+ const body = joinSkeletonSegments(segments.map((segment) => segment === UNKNOWN_PART ? SKELETON_WILDCARD : escapeRegExp$2(segment)));
7327
7387
  return body === void 0 ? void 0 : new RegExp(`^${body}$`);
7328
7388
  }
7329
7389
  const UNKNOWN_PART = Symbol("unknown intrinsic part");
@@ -9099,58 +9159,6 @@ var ReplacementRulesRegistry = class {
9099
9159
  }
9100
9160
  };
9101
9161
 
9102
- //#endregion
9103
- //#region src/provisioning/auxiliary-failure.ts
9104
- const AUXILIARY_LOGICAL_ID_SUFFIX = "/auxiliary";
9105
- const MAX_DEPTH = 5;
9106
- function auxiliaryLogicalId(ownerLogicalId) {
9107
- return `${ownerLogicalId}${AUXILIARY_LOGICAL_ID_SUFFIX}`;
9108
- }
9109
- const RETRY_AUXILIARY_OWNER = "withRetry";
9110
- function isAuxiliaryMarkOf(link, ownerLogicalId) {
9111
- try {
9112
- if (typeof link !== "object" && typeof link !== "function" || link === null) return false;
9113
- const own = Object.getOwnPropertyDescriptor(link, "logicalId");
9114
- return isAuxiliaryMark(own) && own?.value === auxiliaryLogicalId(ownerLogicalId);
9115
- } catch {
9116
- return false;
9117
- }
9118
- }
9119
- function isAuxiliaryMark(descriptor) {
9120
- return descriptor !== void 0 && descriptor.enumerable === false && descriptor.writable === false && typeof descriptor.value === "string" && descriptor.value.endsWith(AUXILIARY_LOGICAL_ID_SUFFIX);
9121
- }
9122
- function isAuxiliaryFailure(error) {
9123
- try {
9124
- let current = error;
9125
- for (let depth = 0; depth < MAX_DEPTH && typeof current === "object" && current !== null; depth++) {
9126
- if (isAuxiliaryMark(Object.getOwnPropertyDescriptor(current, "logicalId"))) return true;
9127
- current = current.cause;
9128
- }
9129
- } catch {}
9130
- return false;
9131
- }
9132
- function markAuxiliaryFailure(error, ownerLogicalId) {
9133
- try {
9134
- let current = error;
9135
- for (let depth = 0; depth < MAX_DEPTH && typeof current === "object" && current !== null; depth++) {
9136
- const own = Object.getOwnPropertyDescriptor(current, "logicalId");
9137
- if (typeof own?.value === "string") {
9138
- if (isAuxiliaryMark(own)) return error;
9139
- } else {
9140
- if (Object.isExtensible(current) && own?.configurable !== false) Object.defineProperty(current, "logicalId", {
9141
- value: auxiliaryLogicalId(ownerLogicalId),
9142
- enumerable: false,
9143
- writable: false,
9144
- configurable: true
9145
- });
9146
- return error;
9147
- }
9148
- current = current.cause;
9149
- }
9150
- } catch {}
9151
- return error;
9152
- }
9153
-
9154
9162
  //#endregion
9155
9163
  //#region src/deployment/retry.ts
9156
9164
  const IAM_PROPAGATION_INITIAL_DELAY_MS = 250;
@@ -9175,11 +9183,14 @@ async function withRetry(operation, logicalId, opts = {}) {
9175
9183
  let nameCooldownSleptMs = 0;
9176
9184
  let serverErrorRetries = 0;
9177
9185
  let replayMayCollide = false;
9186
+ let previousError;
9178
9187
  const settle = (error) => replayMayCollide ? markReplayMayCollide(markAuxiliaryFailure(error, RETRY_AUXILIARY_OWNER)) : error;
9179
9188
  for (let attempt = 0; attempt <= attemptCeiling; attempt++) try {
9180
9189
  return await operation();
9181
9190
  } catch (error) {
9182
9191
  lastError = error;
9192
+ const carried = carryCreatedBeforeFailure(previousError, error);
9193
+ if (hasCreatedBeforeFailure(carried) || !hasCreatedBeforeFailure(previousError)) previousError = carried;
9183
9194
  if (isAuxiliaryFailure(error) || hasReplayMayCollide(error) || isAmbiguousOutcomeError(error)) replayMayCollide = true;
9184
9195
  const message = describeAwsFailure(error).detail;
9185
9196
  const classifyText = retryClassificationText(error);
@@ -15209,10 +15220,10 @@ function refuseMalformedResourcesForDestroy(state, stackName, region, recovery)
15209
15220
  throw markNonRetryable(new CdkdError(malformedDestroyResourcesRefusalMessage(stackName, region, recovery), STATE_RESOURCES_MALFORMED));
15210
15221
  }
15211
15222
  const STATE_REGION_DIVERGED = "STATE_REGION_DIVERGED";
15212
- function divergentRecordRegionRefusalMessage(stackName, keyRegion, divergentBodyRegion, resourceCount, recovery) {
15223
+ function divergentRecordRegionRefusalMessage(stackName, keyRegion, divergentBodyRegion, resourceCount, recovery, provenOrphans = 0) {
15213
15224
  const cap = STACK_REF_MAX_CODE_POINTS;
15214
15225
  const exact = rendersExactly(stackName, cap) && rendersExactly(keyRegion, cap) && isPasteableIdent(stackName) && isPasteableIdent(keyRegion);
15215
- const lists = resourceCount === void 0 ? "its resources map cannot be read" : `it still lists ${resourceCount} resource${resourceCount === 1 ? "" : "s"}`;
15226
+ const lists = resourceCount === void 0 ? "its resources map cannot be read" : resourceCount === 0 && provenOrphans > 0 ? `its rollback journal holds ${provenOrphans} failed create${provenOrphans === 1 ? "" : "s"} whose resource the destroy would delete` : `it still lists ${resourceCount} resource${resourceCount === 1 ? "" : "s"}`;
15216
15227
  const kind = describeRegionValueKind(divergentBodyRegion);
15217
15228
  const opening = exact ? `cdkd will not destroy stack ${shellQuote(safeIdentifier(stackName, cap))} (${shellQuote(safeIdentifier(keyRegion, cap))}): the state record read from that region's key carries a 'region' of its own (${kind}) that is not the key's, and ${lists}` : `cdkd will not destroy the state record this command loaded: it carries a 'region' of its own (${kind}) that is not the region of the key it was read from, and ${lists}`;
15218
15229
  const listing = withheldListingPointer(recovery);
@@ -15220,11 +15231,11 @@ function divergentRecordRegionRefusalMessage(stackName, keyRegion, divergentBody
15220
15231
  const remedy = exact ? "Re-run with --verbose to see what the record's region field holds, then either destroy against the region the resources are really in, or repair that field to match the key it is stored under and re-run. " + withheldAccountClause(withheldAccounts, "the command at the end of this message prints") + "To drop the record and leave the live resources standing, spelled out rather than pasteable because that command DELETES a record: " + dropRecordTemplate(recovery) : "This record's stack name or region does NOT render exactly — what any surrounding output shows is a sanitized form, and another record may render identically — so this message names no target and offers no command against one. List the records with " + (listing.line === void 0 ? `${WITHHELD_LISTING_POINTER}. Inspect it with: ${inspectCommand(void 0, void 0)}` : `${listing.pointer}.` + destroyAccountClause(recovery) + `\n${listing.line}\nInspect it with: ${inspectCommand(void 0, void 0, recovery)}`);
15221
15232
  return `${opening} ${resourceCount === void 0 ? `— so cdkd can neither count what it would delete nor tell which region it is in` : `— so cdkd cannot tell which region those resources are in`}. cdkd stamps the key's region into every record it writes, so this record was not written by cdkd. Destroying against the key's region would issue every delete there; if the record's own region is the honest half, each delete comes back not-found, which this command reads as ALREADY DELETED — it would report success, remove the record, and leave every resource standing in the other region with nothing naming it. ${remedy}`;
15222
15233
  }
15223
- function refuseDivergentRecordRegionForDestroy(state, stackName, keyRegion, divergentBodyRegion, recovery) {
15234
+ function refuseDivergentRecordRegionForDestroy(state, stackName, keyRegion, divergentBodyRegion, recovery, provenOrphans = 0) {
15224
15235
  if (divergentBodyRegion === void 0) return;
15225
15236
  const resourceCount = isReadableBag(state.resources) ? Object.keys(state.resources).length : void 0;
15226
- if (resourceCount === 0) return;
15227
- throw markNonRetryable(new CdkdError(divergentRecordRegionRefusalMessage(stackName, keyRegion, divergentBodyRegion, resourceCount, recovery), STATE_REGION_DIVERGED));
15237
+ if (resourceCount === 0 && provenOrphans === 0) return;
15238
+ throw markNonRetryable(new CdkdError(divergentRecordRegionRefusalMessage(stackName, keyRegion, divergentBodyRegion, resourceCount, recovery, provenOrphans), STATE_REGION_DIVERGED));
15228
15239
  }
15229
15240
  function malformedDeployResourcesRefusalMessage(rawStackName, rawRegion) {
15230
15241
  const stackName = absentIfEmpty(rawStackName);
@@ -15663,6 +15674,16 @@ function malformedDestroyResourceEntriesRefusalMessage(rawStackName, rawRegion,
15663
15674
  const region = absentIfEmpty(rawRegion);
15664
15675
  return `${namedEntriesClause(stackName, region, logicalIds)} 'cdkd destroy' DELETES what this map names, so it refuses rather than continuing: a null row stops the run on a bare TypeError in the listing above the prompt; a false, 0 or empty-string row is SKIPPED as 'not found in state' and the record removed with its resource still live in AWS; and any other row it cannot read, unless its own deletion policy retains it, is routed to a provider on whatever its type field holds — nothing, or a non-string — with a physical id nothing checked, so its delete either fails on a type-lookup error that names the row but not what is wrong with its record, after every readable row not retained was deleted, or is counted done, the record removed and success reported with its resource still live. Nothing was deleted or written FOR THIS STACK. Repair the row first; 'cdkd state orphan' drops the whole record with every resource left standing, which is more than this row asks for. ${inspectClause(stackName, region, recovery)}Inspect the record with: ` + inspectCommand(stackName, region, recovery);
15665
15676
  }
15677
+ function refuseMalformedResourcePropertiesForDestroy(state, stackName, region, recovery) {
15678
+ const unreadable = unreadableResourcePropertyBags(state);
15679
+ if (unreadable.length === 0) return;
15680
+ throw markNonRetryable(new CdkdError(malformedDestroyResourcePropertiesRefusalMessage(stackName, region, unreadable, recovery), STATE_RESOURCES_MALFORMED));
15681
+ }
15682
+ function malformedDestroyResourcePropertiesRefusalMessage(rawStackName, rawRegion, logicalIds, recovery) {
15683
+ const stackName = absentIfEmpty(rawStackName);
15684
+ const region = absentIfEmpty(rawRegion);
15685
+ return `${namedPropertyBagsDiagnosis(stackName, region, logicalIds)} 'cdkd destroy' hands that map to the resource's delete, which reads keys off it — whether an RDS DB instance takes a final snapshot, whether an ECR repository is emptied first — so it refuses rather than continuing: the provider would get the torn value as it stands, and reading the map as EMPTY answers each of those keys as absent, a delete that fails or takes the wrong branch after every resource ordered before it was deleted. Nothing was deleted or written FOR THIS STACK. Repair the map first; 'cdkd state orphan' drops the whole record with every resource left standing, which is more than this asks for. ${inspectClause(stackName, region, recovery)}Inspect the record with: ` + inspectCommand(stackName, region, recovery);
15686
+ }
15666
15687
  function refuseMalformedResourceEntriesForImport(state, reimportedLogicalIds, stackName, region, recovery) {
15667
15688
  const reimported = new Set(reimportedLogicalIds);
15668
15689
  const unreadable = unreadableResourceEntries(state).filter((id) => !reimported.has(id));
@@ -15902,7 +15923,7 @@ function isPlainObject$2(value) {
15902
15923
  return value !== null && typeof value === "object" && !Array.isArray(value);
15903
15924
  }
15904
15925
  const DYNAMIC_REFERENCE_OPEN = "{{resolve:";
15905
- const NESTED_STACK_TYPE$2 = "AWS::CloudFormation::Stack";
15926
+ const NESTED_STACK_TYPE$3 = "AWS::CloudFormation::Stack";
15906
15927
  const NESTED_OUTPUT_PREFIX = "Outputs.";
15907
15928
  function carriesSecretValue(value, corpora) {
15908
15929
  if (carriesSecretMask(value)) return true;
@@ -15977,7 +15998,7 @@ function intrinsicKey(value) {
15977
15998
  const key = keys[0];
15978
15999
  return key === "Ref" || key.startsWith("Fn::") ? key : void 0;
15979
16000
  }
15980
- const OPAQUE_ATTRIBUTE_TYPES = /* @__PURE__ */ new Set(["AWS::CloudFormation::CustomResource", NESTED_STACK_TYPE$2]);
16001
+ const OPAQUE_ATTRIBUTE_TYPES = /* @__PURE__ */ new Set(["AWS::CloudFormation::CustomResource", NESTED_STACK_TYPE$3]);
15981
16002
  function hasOpaqueAttributes(logicalId, walk) {
15982
16003
  const definition = walk.resources[logicalId];
15983
16004
  const type = isPlainObject$2(definition) ? definition["Type"] : void 0;
@@ -16334,7 +16355,7 @@ function passedKey(passed) {
16334
16355
  function isNestedStackRow(logicalId, walk) {
16335
16356
  if (!Object.hasOwn(walk.resources, logicalId)) return false;
16336
16357
  const definition = walk.resources[logicalId];
16337
- return isPlainObject$2(definition) && definition["Type"] === NESTED_STACK_TYPE$2;
16358
+ return isPlainObject$2(definition) && definition["Type"] === NESTED_STACK_TYPE$3;
16338
16359
  }
16339
16360
  function getAttAttribute(operand, target) {
16340
16361
  if (Array.isArray(operand)) return operand.length === 2 && typeof operand[1] === "string" ? operand[1] : void 0;
@@ -21381,13 +21402,13 @@ function resolvesReference(desired, previous, mask) {
21381
21402
  rest = rest.slice(at + token.length);
21382
21403
  }
21383
21404
  literals.push(rest);
21384
- const pattern = literals.map(escapeRegExp).join("[\\s\\S]*");
21405
+ const pattern = literals.map(escapeRegExp$1).join("[\\s\\S]*");
21385
21406
  return new RegExp(`^${pattern}$`).test(desired);
21386
21407
  }
21387
21408
  function isPlainObject$1(value) {
21388
21409
  return typeof value === "object" && value !== null && !Array.isArray(value);
21389
21410
  }
21390
- function escapeRegExp(text) {
21411
+ function escapeRegExp$1(text) {
21391
21412
  return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
21392
21413
  }
21393
21414
 
@@ -21795,7 +21816,10 @@ function isCcConflict(error) {
21795
21816
  }
21796
21817
  var CloudControlProvider = class {
21797
21818
  cloudControlClient;
21798
- logger = getLogger().child("CloudControlProvider");
21819
+ baseLogger = getLogger().child("CloudControlProvider");
21820
+ get logger() {
21821
+ return idLogScope.getStore() ?? this.baseLogger;
21822
+ }
21799
21823
  patchGenerator = new JsonPatchGenerator();
21800
21824
  warnedUnresolvableSchemaTypes = /* @__PURE__ */ new Set();
21801
21825
  protectionFlips = new ProtectionFlipRegistry();
@@ -21810,7 +21834,12 @@ var CloudControlProvider = class {
21810
21834
  const awsClients = getAwsClients();
21811
21835
  this.cloudControlClient = awsClients.cloudControl;
21812
21836
  }
21813
- async create(logicalId, resourceType, properties) {
21837
+ async create(logicalId, resourceType, properties, context) {
21838
+ const mask = maskerOrIdentity(context?.maskSecrets);
21839
+ const idLog = new IdScrubLog(this.logger, mask, holdsSecretDerivedLeaf(properties, mask));
21840
+ return inIdLogScope(idLog, () => this.createInIdLogScope(logicalId, resourceType, properties, idLog));
21841
+ }
21842
+ async createInIdLogScope(logicalId, resourceType, properties, idLog) {
21814
21843
  this.logger.debug(`Creating resource ${logicalId} (${resourceType})`);
21815
21844
  try {
21816
21845
  const ccProperties = stringifyJsonProperties(resourceType, stripNullValues(properties));
@@ -21824,6 +21853,7 @@ var CloudControlProvider = class {
21824
21853
  this.logger.debug(`Create request submitted for ${logicalId}, token: ${createResponse.ProgressEvent.RequestToken}`);
21825
21854
  const progressEvent = await this.waitForOperation(createResponse.ProgressEvent.RequestToken, logicalId, "CREATE", resourceType);
21826
21855
  if (!progressEvent.Identifier) throw new ProvisioningError(`Failed to create resource ${logicalId}: No physical ID returned`, resourceType, logicalId);
21856
+ idLog.addId(progressEvent.Identifier);
21827
21857
  this.logger.debug(`Created resource ${logicalId}, physical ID: ${progressEvent.Identifier}`);
21828
21858
  const result = { physicalId: progressEvent.Identifier };
21829
21859
  if (progressEvent.ResourceModel) result.attributes = this.parseResourceModel(progressEvent.ResourceModel);
@@ -21831,6 +21861,7 @@ var CloudControlProvider = class {
21831
21861
  result.attributes = await this.enrichResourceAttributes(resourceType, progressEvent.Identifier, result.attributes);
21832
21862
  return result;
21833
21863
  } catch (error) {
21864
+ idLog.addId(identifierCarriedBy(error));
21834
21865
  await this.cleanupFailedCreateRemnant(error, resourceType, logicalId);
21835
21866
  this.handleError(error, "CREATE", resourceType, logicalId);
21836
21867
  }
@@ -21851,7 +21882,10 @@ var CloudControlProvider = class {
21851
21882
  } catch (cleanupError) {
21852
21883
  const message = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);
21853
21884
  if (isWaitAbandonedError(cleanupError)) {
21854
- this.logger.warn(`Could not confirm whether the remnant ${error.physicalId} left by the failed CREATE of ${logicalId} was removed (a retry may fail with AlreadyExists until it is removed manually): ${displaySafe(message)}`);
21885
+ const line = `Could not confirm whether the remnant ${error.physicalId} left by the failed CREATE of ${logicalId} was removed (a retry may fail with AlreadyExists until it is removed manually): ${displaySafe(message)}`;
21886
+ const log = this.logger;
21887
+ if (log instanceof IdScrubLog) log.warnKeepingResume(line, abandonedRequestTokens(cleanupError));
21888
+ else log.warn(line);
21855
21889
  return;
21856
21890
  }
21857
21891
  if (isNotFoundMessage(message)) {
@@ -21862,7 +21896,12 @@ var CloudControlProvider = class {
21862
21896
  }
21863
21897
  }
21864
21898
  async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
21865
- const idLog = updateIdLogSink(this.logger, physicalId, properties, previousProperties, context?.maskSecrets);
21899
+ const mask = maskerOrIdentity(context?.maskSecrets);
21900
+ const idLog = new IdScrubLog(this.logger, mask, holdsSecretDerivedLeaf(properties, mask) || holdsSecretDerivedLeaf(previousProperties, mask));
21901
+ idLog.addId(physicalId);
21902
+ return inIdLogScope(idLog, () => this.updateInIdLogScope(logicalId, physicalId, resourceType, properties, previousProperties, context, idLog));
21903
+ }
21904
+ async updateInIdLogScope(logicalId, physicalId, resourceType, properties, previousProperties, context, idLog) {
21866
21905
  idLog.debug(`Updating resource ${logicalId} (${resourceType}), physical ID: ${physicalId}`);
21867
21906
  await this.assertRecordedRegionAgainstClient("pre-update", context?.expectedRegion, resourceType, logicalId, physicalId);
21868
21907
  try {
@@ -21936,26 +21975,41 @@ var CloudControlProvider = class {
21936
21975
  this.handleError(error, "UPDATE", resourceType, logicalId, physicalId);
21937
21976
  }
21938
21977
  }
21939
- async delete(logicalId, physicalId, resourceType, _properties, context) {
21978
+ async delete(logicalId, physicalId, resourceType, properties, context) {
21979
+ const mask = maskerOrIdentity(void 0);
21980
+ const idLog = new IdScrubLog(this.logger, mask, holdsSecretDerivedLeaf(properties, mask));
21981
+ idLog.addId(physicalId);
21982
+ const result = await inIdLogScope(idLog, () => this.deleteInIdLogScope(logicalId, physicalId, resourceType, properties, context));
21983
+ if (!idLog.withheld || !result) return result;
21984
+ return {
21985
+ ...result,
21986
+ ...result.outcome === "skipped" && { reason: idLog.scrub(result.reason) },
21987
+ ...Array.isArray(result.indeterminateGuards) && { indeterminateGuards: result.indeterminateGuards.map((guard) => ({
21988
+ ...guard,
21989
+ reason: idLog.scrub(guard.reason)
21990
+ })) }
21991
+ };
21992
+ }
21993
+ async deleteInIdLogScope(logicalId, physicalId, resourceType, properties, context) {
21940
21994
  this.logger.debug(`Deleting resource ${logicalId} (${resourceType}), physical ID: ${physicalId}`);
21941
21995
  if (context?.finalSnapshotIdentifier !== void 0) throw new ProvisioningError(`${logicalId} (${resourceType}) requires a final snapshot (DeletionPolicy: Snapshot), but the Cloud Control API delete route has no final-snapshot parameter. Re-run with --skip-final-snapshot after snapshotting manually, or retain the resource.`, resourceType, logicalId, physicalId);
21942
21996
  await this.assertRecordedRegionAgainstClient("pre-delete", context?.expectedRegion, resourceType, logicalId, physicalId);
21943
21997
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
21944
21998
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
21945
21999
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
21946
- const { ASGProvider } = await import("./asg-provider-DuAHOXvT.js");
22000
+ const { ASGProvider } = await import("./asg-provider-AVCcoXnQ.js");
21947
22001
  const delegateRegion = ambientRegion();
21948
22002
  let asgProvider = this.asgDeleteDelegates.get(delegateRegion);
21949
22003
  if (asgProvider === void 0) {
21950
22004
  asgProvider = new ASGProvider();
21951
22005
  this.asgDeleteDelegates.set(delegateRegion, asgProvider);
21952
22006
  }
21953
- return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
22007
+ return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, properties, context), indeterminateGuard);
21954
22008
  }
21955
22009
  if ((context?.deletionPolicy === "Delete" || context?.skipFinalSnapshot === true) && SDK_DELETE_FAMILIES.has(resourceType)) {
21956
- if (deletesThroughSdkProvider(resourceType, _properties)) {
22010
+ if (deletesThroughSdkProvider(resourceType, properties)) {
21957
22011
  const delegate = await this.sdkDeleteDelegateInCcRegion(resourceType, logicalId, physicalId);
21958
- return withIndeterminateGuard(await delegate.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
22012
+ return withIndeterminateGuard(await delegate.delete(logicalId, physicalId, resourceType, properties, context), indeterminateGuard);
21959
22013
  }
21960
22014
  this.logger.warn(safeMsg`${logicalId} (${resourceType}) records a GlobalClusterIdentifier cdkd cannot detach it from, so it is deleted through ` + `Cloud Control, which handles the global-cluster membership. Its handler ` + safeMsg`can take a manual final snapshot of ${physicalId}, which cdkd does not record; ` + `delete it yourself if you do not want it.`);
21961
22015
  }
@@ -22049,7 +22103,7 @@ var CloudControlProvider = class {
22049
22103
  const family = SDK_DELETE_FAMILIES.get(resourceType);
22050
22104
  const cached = this.sdkDeleteDelegates.get(family);
22051
22105
  if (cached !== void 0 && cached.region === ccRegion) return cached.provider;
22052
- const provider = family === "rds" ? new (await (import("./rds-provider-D6_Rcnub.js"))).RDSProvider({ region: ccRegion }) : new (await (import("./neptune-provider-BGVccWhg.js"))).NeptuneProvider({ region: ccRegion });
22106
+ const provider = family === "rds" ? new (await (import("./rds-provider-DltlcDiQ.js"))).RDSProvider({ region: ccRegion }) : new (await (import("./neptune-provider-D1t9NgT-.js"))).NeptuneProvider({ region: ccRegion });
22053
22107
  this.sdkDeleteDelegates.set(family, {
22054
22108
  region: ccRegion,
22055
22109
  provider
@@ -22752,20 +22806,94 @@ var CloudControlProvider = class {
22752
22806
  return masked;
22753
22807
  }
22754
22808
  };
22755
- function updateIdLogSink(logger, physicalId, properties, previousProperties, maskSecrets) {
22756
- const mask = maskerOrIdentity(maskSecrets);
22757
- const raw = updateIdWithheld(properties, previousProperties, mask) ? [physicalId, ...physicalId.split("|")].filter((needle) => needle !== "") : [];
22758
- const masked = raw.map((needle) => mask(needle));
22759
- const needles = [.../* @__PURE__ */ new Set([...raw, ...masked.filter((m, i) => m !== "" && m !== raw[i] && m !== "***")])].sort((a, b) => b.length - a.length);
22760
- const scrub = (message) => needles.reduce((text, needle) => text.split(needle).join("***"), mask(message));
22761
- return {
22762
- debug: (message) => logger.debug(scrub(message)),
22763
- warn: (message) => logger.warn(scrub(message)),
22764
- withheld: needles.length > 0
22765
- };
22809
+ var IdScrubLog = class {
22810
+ needles = [];
22811
+ base;
22812
+ mask;
22813
+ evidence;
22814
+ constructor(base, mask, evidence) {
22815
+ this.base = base;
22816
+ this.mask = mask;
22817
+ this.evidence = evidence;
22818
+ }
22819
+ get withheld() {
22820
+ return this.needles.length > 0;
22821
+ }
22822
+ addId(id) {
22823
+ if (!this.evidence || id === void 0 || id === "") return;
22824
+ const parts = [id, ...id.split("|")].filter((needle) => needle !== "");
22825
+ const raw = [...new Set(parts.flatMap((part) => [
22826
+ part,
22827
+ displaySafe(part),
22828
+ displaySafe(part, { asciiOnly: true })
22829
+ ]))].filter((needle) => needle !== "" && needle !== "***");
22830
+ const masked = raw.map((needle) => this.mask(needle));
22831
+ this.needles = [.../* @__PURE__ */ new Set([
22832
+ ...this.needles,
22833
+ ...raw,
22834
+ ...masked.filter((m, i) => m !== "" && m !== raw[i] && m !== "***")
22835
+ ])].sort((a, b) => b.length - a.length);
22836
+ }
22837
+ scrub = (text) => this.needles.reduce((out, needle) => out.split(needle).join("***"), this.mask(text));
22838
+ scrubKeepingResume(text, requestTokens) {
22839
+ const tokens = requestTokens.filter((token) => token !== "");
22840
+ if (tokens.length === 0) return this.scrub(text);
22841
+ const keep = new RegExp(`(${[RESUME_COMMAND.source, ...tokens.map(escapeRegExp)].join("|")})`);
22842
+ return text.split(keep).map((segment, i) => i % 2 === 1 ? this.mask(segment) : this.scrub(segment)).join("");
22843
+ }
22844
+ scrubError(error) {
22845
+ if (this.needles.length === 0 || !(error instanceof Error)) return error;
22846
+ const original = errorCauseChain(error);
22847
+ const tokens = abandonedRequestTokens(error);
22848
+ const scrubbed = maskSecretsInError(error, /* @__PURE__ */ new Map(), (text) => this.scrubKeepingResume(text, tokens));
22849
+ const clones = errorCauseChain(scrubbed);
22850
+ original.forEach((link, i) => {
22851
+ const clone = clones[i];
22852
+ if (clone === void 0 || clone === link) return;
22853
+ for (const key of VERBATIM_ERROR_FIELDS) {
22854
+ const descriptor = Object.getOwnPropertyDescriptor(link, key);
22855
+ if (descriptor === void 0 || typeof descriptor.value !== "string") continue;
22856
+ try {
22857
+ Object.defineProperty(clone, key, descriptor);
22858
+ } catch {}
22859
+ }
22860
+ });
22861
+ return scrubbed;
22862
+ }
22863
+ warnKeepingResume(message, requestTokens) {
22864
+ this.base.warn(this.scrubKeepingResume(message, requestTokens));
22865
+ }
22866
+ debug(message) {
22867
+ this.base.debug(this.scrub(message));
22868
+ }
22869
+ info(message) {
22870
+ this.base.info(this.scrub(message));
22871
+ }
22872
+ warn(message) {
22873
+ this.base.warn(this.scrub(message));
22874
+ }
22875
+ error(message) {
22876
+ this.base.error(this.scrub(message));
22877
+ }
22878
+ };
22879
+ const VERBATIM_ERROR_FIELDS = ["logicalId", "resourceType"];
22880
+ function escapeRegExp(text) {
22881
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
22882
+ }
22883
+ const RESUME_COMMAND = /aws(?: --profile (?:'(?:[^'\n]|'\\'')*'|\S+))? cloudcontrol get-resource-request-status --request-token \S+(?: --region \S+)?/;
22884
+ function abandonedRequestTokens(error) {
22885
+ return error instanceof Error ? errorCauseChain(error).flatMap((link) => link instanceof CloudControlWaitAbandonedError ? [link.requestToken] : []) : [];
22886
+ }
22887
+ const idLogScope = new AsyncLocalStorage();
22888
+ async function inIdLogScope(idLog, fn) {
22889
+ try {
22890
+ return await idLogScope.run(idLog, fn);
22891
+ } catch (error) {
22892
+ throw idLog.scrubError(error);
22893
+ }
22766
22894
  }
22767
- function updateIdWithheld(properties, previousProperties, mask) {
22768
- return holdsSecretDerivedLeaf(properties, mask) || holdsSecretDerivedLeaf(previousProperties, mask);
22895
+ function identifierCarriedBy(error) {
22896
+ return error instanceof ProvisioningError ? error.physicalId : void 0;
22769
22897
  }
22770
22898
  function holdsSecretDerivedLeaf(value, mask, depth = 0) {
22771
22899
  if (typeof value === "string") return isSecretDerivedValue(value, mask);
@@ -22874,8 +23002,34 @@ const CR_MASKED_SERVICE_TOKEN_SKIP_REASON = "masked ServiceToken in state — De
22874
23002
  const CR_REFERENCE_SERVICE_TOKEN_SKIP_REASON = "dynamic reference as ServiceToken in state — handler not invoked";
22875
23003
  const CR_DELETE_INVOKE_FAILED_SKIP_REASON = "Delete request to the handler did not complete — resource unproven";
22876
23004
  const CR_DELETE_HANDLER_FAILED_SKIP_REASON = "Delete handler reported FAILED — resource unproven";
22877
- const CR_SKIP_NOT_A_RETRY_CAVEAT = "NOTE this record is a POINTER, not a retry: the same destroy run deletes the backing Lambda, so the next 'cdkd destroy' finds the handler gone and DROPS this record (issue 804 pre-check). Tear the resource down by hand, then clear the stack's records with 'cdkd state orphan <stack> --stack-region <region>' — that command drops EVERY record for the stack in that region, not just this one.";
23005
+ const CR_BACKING_LAMBDA_GONE_SKIP_REASON = "backing Lambda function is gone — Delete handler not invoked";
23006
+ function crSkipNotARetryCaveat(context, logicalId) {
23007
+ return `NOTE this record is a POINTER, not a retry: the same destroy run deletes the backing Lambda, so the next 'cdkd destroy' cannot reach the handler either: it skips this resource again and keeps the record (issue 2115). Tear the resource down by hand, then drop the record with ${stateOrphanRecordRemedy(context, logicalId)}.`;
23008
+ }
22878
23009
  const DEPLOY_SKIP_CAVEAT = "NOTE this arm is ALSO reached from cdkd deploy. Since issue 1762 the DELETE of a resource removed from the template behaves like destroy — the record is KEPT and the next deploy re-attempts it — but a REPLACEMENT / rollback delete FAILS the resource instead (https://github.com/go-to-k/cdkd/issues/1762), leaving the old one untracked; there, tear the resource down by hand.";
23010
+ function crDeployNoStateOrphan(logicalId) {
23011
+ return `For a resource removed from the template, to give up on the delete and stop tracking it, drop the record with ${stateOrphanRecordRemedy(void 0, logicalId)}.`;
23012
+ }
23013
+ function crDeploySkipTail(logicalId) {
23014
+ return "Meanwhile 'cdkd deploy --allow-unaddressed' exits 0 (a deploy-only flag; 'cdkd rollback' has none); the record stays and each deploy re-attempts the delete. A delete-first REPLACEMENT or a rollback delete usually FAILS the resource and keeps the record (https://github.com/go-to-k/cdkd/issues/1762). Where the other copy already exists (a replacement that created the new resource first, or a rollback that re-created the old one first), the skip only warns and the surviving copy is no longer tracked, so tear it down by hand. " + crDeployNoStateOrphan(logicalId);
23015
+ }
23016
+ const CR_DEPLOY_HANDLER_SKIP_HEAD = "This delete is not part of a stack destroy (a 'cdkd deploy' template removal, replacement or rollback, or a 'cdkd rollback'). For a resource removed from the template the record is KEPT and the next 'cdkd deploy' re-sends the Delete: fix what made it fail (the handler's Delete logic, or what stopped the invoke) and re-deploy. That reaches the handler only while its function exists: if this deploy also deleted the backing Lambda, the next deploy finds it gone and DROPS the record with a warning, so tear down what the handler manages by hand. ";
23017
+ const CR_DEPLOY_UNADDRESSABLE_HEAD = "This delete is not part of a stack destroy (a 'cdkd deploy' template removal, replacement or rollback, or a 'cdkd rollback'). For a resource removed from the template the record is KEPT and every 'cdkd deploy' skips it again until ServiceToken is restored: put the provider's Lambda function or SNS topic ARN back in state.json and re-deploy, which sends the Delete while that handler exists. If its Lambda function is gone, that deploy DROPS the record with a warning instead, so tear down what the handler manages by hand. ";
23018
+ function crDestroyUnaddressableRemedy(context, logicalId) {
23019
+ return `Tear the resource down by hand, then drop the record with ${stateOrphanRecordRemedy(context, logicalId)}. Restoring ServiceToken (the provider's Lambda function or SNS topic ARN) in state.json and re-running helps only while that handler still exists: a destroy goes on to delete its backing Lambda.`;
23020
+ }
23021
+ function handlerSkipRemedy(context, logicalId) {
23022
+ return context?.stackDestroy === true ? ` — cdkd is KEEPING the state record and the run exits non-zero. ${crSkipNotARetryCaveat(context, logicalId)}` : `. ${CR_DEPLOY_HANDLER_SKIP_HEAD}${crDeploySkipTail(logicalId)}`;
23023
+ }
23024
+ function crUnresolvedTokenUpdateRemedy() {
23025
+ return "This indicates an unresolved value in the template, or, on a rollback, in the resource's recorded state. Check that the template resolves ServiceToken to the provider's Lambda function or SNS topic ARN and re-deploy; a rollback replays the recorded value, so restore the template and re-deploy instead. Do not drop this record with 'cdkd state orphan', with or without --resource: the template still declares the resource, so the next deploy would send the handler a fresh Create and leave what it already manages untracked.";
23026
+ }
23027
+ function unresolvedTokenDeleteRemedy(context, logicalId) {
23028
+ return context?.stackDestroy === true ? `re-run 'cdkd import', or drop the record with ${stateOrphanRecordRemedy(context, logicalId)}, to recover.` : `put the provider's Lambda function or SNS topic ARN back as ServiceToken in state.json and re-run to recover. ${crDeployNoStateOrphan(logicalId)}`;
23029
+ }
23030
+ function unaddressableServiceTokenRemedy(context, logicalId) {
23031
+ return context?.stackDestroy === true ? crDestroyUnaddressableRemedy(context, logicalId) : `${CR_DEPLOY_UNADDRESSABLE_HEAD}${crDeploySkipTail(logicalId)}`;
23032
+ }
22879
23033
  function isCustomResourceResponsePayload(value) {
22880
23034
  if (typeof value !== "object" || value === null) return false;
22881
23035
  const payload = value;
@@ -23218,7 +23372,7 @@ var CustomResourceProvider = class CustomResourceProvider {
23218
23372
  this.logger.debug(`Creating custom resource ${logicalId} (${resourceType})`);
23219
23373
  const serviceToken = properties["ServiceToken"];
23220
23374
  if (!serviceToken) throw new ProvisioningError(`ServiceToken is required for custom resource ${logicalId}`, resourceType, logicalId);
23221
- if (typeof serviceToken !== "string") throw new ProvisioningError(`Custom Resource ${logicalId}: ServiceToken is not a resolved string ARN (got ${typeof serviceToken}). This usually indicates state was written by a pre-fix cdkd import; re-run 'cdkd import' or 'cdkd state orphan <stack> --stack-region <region>' to recover.`, resourceType, logicalId);
23375
+ if (typeof serviceToken !== "string") throw new ProvisioningError(`Custom Resource ${logicalId}: ServiceToken is not a resolved string ARN (got ${typeof serviceToken}). This indicates an unresolved value in the template, or, on a rollback, in the resource's recorded state. Check that the template resolves ServiceToken to the provider's Lambda function or SNS topic ARN; a rollback replays the recorded value, so restore the template and re-deploy instead.`, resourceType, logicalId);
23222
23376
  this.refuseResolvedSecret(logicalId, resourceType, properties);
23223
23377
  try {
23224
23378
  const cfnResponse = await this.invokeCustomResourceWithRetry(serviceToken, logicalId, "Create", (invocation) => ({
@@ -23249,7 +23403,7 @@ var CustomResourceProvider = class CustomResourceProvider {
23249
23403
  this.logger.debug(`Updating custom resource ${logicalId}: ${physicalId} (${resourceType})`);
23250
23404
  const serviceToken = properties["ServiceToken"];
23251
23405
  if (!serviceToken) throw new ProvisioningError(`ServiceToken is required for custom resource ${logicalId}`, resourceType, logicalId, physicalId);
23252
- if (typeof serviceToken !== "string") throw new ProvisioningError(`Custom Resource ${logicalId}: ServiceToken is not a resolved string ARN (got ${typeof serviceToken}). This usually indicates state was written by a pre-fix cdkd import; re-run 'cdkd import' or 'cdkd state orphan <stack> --stack-region <region>' to recover.`, resourceType, logicalId, physicalId);
23406
+ if (typeof serviceToken !== "string") throw new ProvisioningError(`Custom Resource ${logicalId}: ServiceToken is not a resolved string ARN (got ${typeof serviceToken}). ` + crUnresolvedTokenUpdateRemedy(), resourceType, logicalId, physicalId);
23253
23407
  this.refuseResolvedSecret(logicalId, resourceType, properties, physicalId, previousProperties);
23254
23408
  try {
23255
23409
  const cfnResponse = await this.invokeCustomResourceWithRetry(serviceToken, logicalId, "Update", (invocation) => ({
@@ -23279,7 +23433,7 @@ var CustomResourceProvider = class CustomResourceProvider {
23279
23433
  throw new ProvisioningError(`Failed to update custom resource ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
23280
23434
  }
23281
23435
  }
23282
- async delete(logicalId, physicalId, resourceType, properties, _context) {
23436
+ async delete(logicalId, physicalId, resourceType, properties, context) {
23283
23437
  this.logger.debug(`Deleting custom resource ${logicalId}: ${physicalId} (${resourceType})`);
23284
23438
  if (!properties) {
23285
23439
  this.logger.warn(`No properties available for custom resource ${logicalId}, skipping deletion — the handler is never invoked, so anything this custom resource manages is LEFT IN PLACE. Restore the record's properties in state.json and re-run, or tear the resource down by hand. ${DEPLOY_SKIP_CAVEAT}`);
@@ -23296,23 +23450,30 @@ var CustomResourceProvider = class CustomResourceProvider {
23296
23450
  reason: CR_NO_SERVICE_TOKEN_SKIP_REASON
23297
23451
  };
23298
23452
  }
23299
- if (typeof serviceToken !== "string") throw new ProvisioningError(`Custom Resource ${logicalId}: ServiceToken is not a resolved string ARN (got ${typeof serviceToken}). This usually indicates state was written by a pre-fix cdkd import; re-run 'cdkd import' or 'cdkd state orphan <stack> --stack-region <region>' to recover.`, resourceType, logicalId, physicalId);
23453
+ if (typeof serviceToken !== "string") throw new ProvisioningError(`Custom Resource ${logicalId}: ServiceToken is not a resolved string ARN (got ${typeof serviceToken}). This usually indicates state was written by a pre-fix cdkd import; ` + unresolvedTokenDeleteRemedy(context, logicalId), resourceType, logicalId, physicalId);
23300
23454
  if (carriesSecretMask(serviceToken)) {
23301
- this.logger.warn(safeMsg`ServiceToken for custom resource ${logicalId} is recorded in state as the redaction ` + safeMsg`mask '${"***"}' (a NoEcho value this resource reads equals, or is contained ` + "in, its ServiceToken), so cdkd cannot address the handler; skipping deletion — anything this custom resource manages is LEFT IN PLACE. Re-deploying does not repair the record while that attribute still carries the value. Tear the resource down by hand, then clear the stack's records with 'cdkd state orphan <stack> --stack-region <region>' — that command drops EVERY record for the stack in that region, not just this one. Restoring ServiceToken (the provider's Lambda function or SNS topic ARN) in state.json and re-running helps only while that handler still exists: a destroy goes on to delete its backing Lambda. " + safeMsg`${DEPLOY_SKIP_CAVEAT}`);
23455
+ this.logger.warn(safeMsg`ServiceToken for custom resource ${logicalId} is recorded in state as the redaction ` + safeMsg`mask '${"***"}' (a NoEcho value this resource reads equals, or is contained ` + "in, its ServiceToken), so cdkd cannot address the handler; skipping deletion — anything this custom resource manages is LEFT IN PLACE. Re-deploying does not repair the record while that attribute still carries the value. " + safeMsg`${unaddressableServiceTokenRemedy(context, logicalId)}`);
23302
23456
  return {
23303
23457
  outcome: "skipped",
23304
23458
  reason: CR_MASKED_SERVICE_TOKEN_SKIP_REASON
23305
23459
  };
23306
23460
  }
23307
23461
  if (dynamicReferenceTokens(serviceToken).length > 0) {
23308
- this.logger.warn(safeMsg`ServiceToken for custom resource ${logicalId} is recorded in state as a ` + "'{{resolve:...}}' dynamic reference, which cdkd does not resolve on delete, so it cannot address the handler; skipping deletion — anything this custom resource manages is LEFT IN PLACE. CloudFormation does not support secure (secretsmanager / ssm-secure) dynamic references in custom resources, and cdkd now refuses such a template at deploy time. Tear the resource down by hand, then clear the stack's records with 'cdkd state orphan <stack> --stack-region <region>' — that command drops EVERY record for the stack in that region, not just this one. Restoring ServiceToken (the provider's Lambda function or SNS topic ARN) in state.json and re-running helps only while that handler still exists: a destroy goes on to delete its backing Lambda. " + safeMsg`${DEPLOY_SKIP_CAVEAT}`);
23462
+ this.logger.warn(safeMsg`ServiceToken for custom resource ${logicalId} is recorded in state as a ` + "'{{resolve:...}}' dynamic reference, which cdkd does not resolve on delete, so it cannot address the handler; skipping deletion — anything this custom resource manages is LEFT IN PLACE. CloudFormation does not support secure (secretsmanager / ssm-secure) dynamic references in custom resources, and cdkd now refuses such a template at deploy time. " + safeMsg`${unaddressableServiceTokenRemedy(context, logicalId)}`);
23309
23463
  return {
23310
23464
  outcome: "skipped",
23311
23465
  reason: CR_REFERENCE_SERVICE_TOKEN_SKIP_REASON
23312
23466
  };
23313
23467
  }
23314
23468
  if (!this.isSnsServiceToken(serviceToken) && await this.isBackingLambdaGone(serviceToken)) {
23315
- this.logger.warn(`Backing Lambda for custom resource ${logicalId} no longer exists (${serviceToken}); treating the custom resource as already deleted and DROPPING its state record. The handler can never run again, so if its teardown was never PROVEN — e.g. an earlier run reported this resource as skipped (issue 2054) — whatever it manages is still LIVE and is now untracked by cdkd. Check for leftovers before treating the stack as gone.`);
23469
+ if (context?.stackDestroy === true) {
23470
+ this.logger.warn(safeMsg`Backing Lambda for custom resource ${logicalId} no longer exists (${serviceToken}), so ` + "its Delete handler cannot be invoked and cdkd cannot confirm the resource was deleted; skipping deletion — anything this custom resource manages may still be LIVE. cdkd is KEEPING the state record and the run exits non-zero, as CloudFormation leaves such a stack DELETE_FAILED. A re-run skips it again unless a function with that exact ARN exists again (e.g. a shared provider stack destroyed before this one, then redeployed). Otherwise confirm by hand that whatever it manages is gone (or tear it down), then drop the record with " + safeMsg`${stateOrphanRecordRemedy(context, logicalId)}.`);
23471
+ return {
23472
+ outcome: "skipped",
23473
+ reason: CR_BACKING_LAMBDA_GONE_SKIP_REASON
23474
+ };
23475
+ }
23476
+ this.logger.warn(safeMsg`Backing Lambda for custom resource ${logicalId} no longer exists (${serviceToken}); ` + "treating the custom resource as already deleted and DROPPING its state record, as CloudFormation ignores a delete failure in an update's cleanup phase. The handler can never run again, so if its teardown was never PROVEN, whatever it manages may still be LIVE and is now untracked by cdkd. Check for leftovers by hand.");
23316
23477
  return;
23317
23478
  }
23318
23479
  try {
@@ -23327,7 +23488,7 @@ var CustomResourceProvider = class CustomResourceProvider {
23327
23488
  ResourceProperties: this.stringifyProperties(properties)
23328
23489
  }), DELETE_PATH_UNMASKED);
23329
23490
  if (cfnResponse.Status === "FAILED") {
23330
- this.logger.warn(`Custom resource delete handler returned FAILED for ${logicalId}: ${cfnResponse.Reason || "Unknown reason"}. The handler reported that it did NOT delete, so anything this custom resource manages is LEFT IN PLACE — cdkd is KEEPING the state record and the run exits non-zero. ${CR_SKIP_NOT_A_RETRY_CAVEAT} ('cdkd deploy' also accepts --allow-unaddressed, which forces exit 0; 'cdkd destroy' has no such flag.) ${DEPLOY_SKIP_CAVEAT}`);
23491
+ this.logger.warn(`Custom resource delete handler returned FAILED for ${logicalId}: ${cfnResponse.Reason || "Unknown reason"}. The handler reported that it did NOT delete, so anything this custom resource manages is LEFT IN PLACE${handlerSkipRemedy(context, logicalId)}`);
23331
23492
  return {
23332
23493
  outcome: "skipped",
23333
23494
  reason: CR_DELETE_HANDLER_FAILED_SKIP_REASON
@@ -23335,7 +23496,7 @@ var CustomResourceProvider = class CustomResourceProvider {
23335
23496
  }
23336
23497
  this.logger.debug(`Successfully deleted custom resource ${logicalId}`);
23337
23498
  } catch (error) {
23338
- this.logger.warn(`Failed to delete custom resource ${logicalId}, but continuing: ${describeAwsFailure(error).detail}. The Delete handler did not complete, so anything this custom resource manages may still be LIVE — cdkd is KEEPING the state record and the run exits non-zero. ${CR_SKIP_NOT_A_RETRY_CAVEAT} ${DEPLOY_SKIP_CAVEAT}`);
23499
+ this.logger.warn(`Failed to delete custom resource ${logicalId}, but continuing: ${describeAwsFailure(error).detail}. The Delete handler did not complete, so anything this custom resource manages may still be LIVE${handlerSkipRemedy(context, logicalId)}`);
23339
23500
  return {
23340
23501
  outcome: "skipped",
23341
23502
  reason: CR_DELETE_INVOKE_FAILED_SKIP_REASON
@@ -26283,6 +26444,81 @@ function ownPoliciesChange(change) {
26283
26444
  return (change?.propertyChanges ?? []).some((c) => c.path === "Policies" && c.inPlacePropagated !== true && c.replacementPropagated !== true);
26284
26445
  }
26285
26446
 
26447
+ //#endregion
26448
+ //#region src/analyzer/destructive-changes.ts
26449
+ const IGNORED_RESOURCE_TYPES = /* @__PURE__ */ new Set(["AWS::CDK::Metadata"]);
26450
+ const PATH_METADATA_KEY = "aws:cdk:path";
26451
+ function findDestructiveChanges(stackName, changes, records, template, recreateTargets = /* @__PURE__ */ new Set()) {
26452
+ const found = [];
26453
+ for (const change of changes) {
26454
+ const record = Object.hasOwn(records, change.logicalId) ? records[change.logicalId] : void 0;
26455
+ const resourceType = record?.resourceType ?? change.resourceType;
26456
+ if (IGNORED_RESOURCE_TYPES.has(resourceType) || IGNORED_RESOURCE_TYPES.has(change.resourceType)) continue;
26457
+ const impact = change.changeType === "UPDATE" && recreateTargets.has(change.logicalId) ? "WILL_REPLACE" : destructiveImpactOf(change, record);
26458
+ if (impact === void 0) continue;
26459
+ const recorded = typeof record?.constructPath === "string" ? record.constructPath : void 0;
26460
+ const constructPath = constructPathOf(template, change.logicalId) ?? recorded;
26461
+ found.push({
26462
+ stackName,
26463
+ logicalId: change.logicalId,
26464
+ resourceType,
26465
+ ...constructPath !== void 0 && { constructPath },
26466
+ impact
26467
+ });
26468
+ }
26469
+ return found;
26470
+ }
26471
+ function destructiveImpactOf(change, record) {
26472
+ switch (change.changeType) {
26473
+ case "DELETE": return shouldRetainResource(record?.deletionPolicy) ? "WILL_ORPHAN" : "WILL_DESTROY";
26474
+ case "UPDATE": {
26475
+ if (record !== void 0 && record.resourceType !== change.resourceType) return "WILL_REPLACE";
26476
+ let mayReplace = false;
26477
+ for (const pc of change.propertyChanges ?? []) {
26478
+ if (!pc.requiresReplacement) continue;
26479
+ if (pc.inPlacePropagated === true || pc.replacementPropagated === true) mayReplace = true;
26480
+ else return "WILL_REPLACE";
26481
+ }
26482
+ return mayReplace ? "MAY_REPLACE" : void 0;
26483
+ }
26484
+ default: return;
26485
+ }
26486
+ }
26487
+ function constructPathOf(template, logicalId) {
26488
+ const resources = template?.Resources;
26489
+ if (resources === void 0 || !Object.hasOwn(resources, logicalId)) return void 0;
26490
+ const path = resources[logicalId]?.Metadata?.[PATH_METADATA_KEY];
26491
+ return typeof path === "string" && path !== "" ? path : void 0;
26492
+ }
26493
+ function describeDestructiveImpact(impact) {
26494
+ switch (impact) {
26495
+ case "WILL_REPLACE": return "will be replaced";
26496
+ case "MAY_REPLACE": return "may be replaced";
26497
+ case "WILL_DESTROY": return "will be destroyed";
26498
+ case "WILL_ORPHAN": return "will be orphaned";
26499
+ }
26500
+ }
26501
+ function formatDestructiveChange(change) {
26502
+ const path = change.constructPath !== void 0 ? `${displaySafe(displayPath(change.constructPath))} ` : "";
26503
+ return `${displayIdent(change.stackName)}: ${displaySafe(change.resourceType)} ${path}${displayIdent(change.logicalId)} ${describeDestructiveImpact(change.impact)}`;
26504
+ }
26505
+ function displayPath(constructPath) {
26506
+ let parts = constructPath.replace(/^\//, "").split("/");
26507
+ if (parts.length > 1) {
26508
+ parts = parts.slice(1);
26509
+ const last = parts[parts.length - 1];
26510
+ if (parts.length > 1 && (last === "Resource" || last === "Default")) parts = parts.slice(0, -1);
26511
+ }
26512
+ return parts.join("/");
26513
+ }
26514
+ function withConstructPath(record, path) {
26515
+ if (path === void 0 || record.constructPath === path) return record;
26516
+ return {
26517
+ ...record,
26518
+ constructPath: path
26519
+ };
26520
+ }
26521
+
26286
26522
  //#endregion
26287
26523
  //#region src/deployment/masking-retry-logger.ts
26288
26524
  function maskingRetryLogger(logger, secrets) {
@@ -27178,9 +27414,9 @@ const REDACTED_DELETE_ADDRESS_SKIP_REASON = "redacted address property in state
27178
27414
  function redactedDeleteAddressFields(fields) {
27179
27415
  return Object.keys(fields).filter((key) => isRedactedRecordedValue(fields[key]));
27180
27416
  }
27181
- function redactedDeleteAddressSkip(logger, logicalId, what, redactedFields) {
27417
+ function redactedDeleteAddressSkip(logger, logicalId, what, redactedFields, context) {
27182
27418
  if (redactedFields.length === 0) return void 0;
27183
- logger.warn(safeMsg`${what} ${logicalId} is recorded in state with ${redactedFields.join(", ")} ` + "redacted (the '***' mask of a NoEcho value it read, or a secret '{{resolve:...}}' reference), which names nothing in AWS; skipping deletion — no AWS call is issued, so the resource is LEFT IN PLACE unless a parent resource this run also deletes removes it (then only the record is stale). Re-deploying records the same redaction again. 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. Remove the resource by hand, then clear the stack's records with 'cdkd state orphan <stack>' — that command drops EVERY record for the stack, not just this one. 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 remove it by hand.");
27419
+ logger.warn(safeMsg`${what} ${logicalId} is recorded in state with ${redactedFields.join(", ")} ` + "redacted (the '***' mask of a NoEcho value it read, or a secret '{{resolve:...}}' reference), which names nothing in AWS; skipping deletion — no AWS call is issued, so the resource is LEFT IN PLACE unless a parent resource this run also deletes removes it (then only the record is stale). Re-deploying records the same redaction again. 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. Remove the resource by hand, " + safeMsg`then drop the record with ${stateOrphanRecordRemedy(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 remove it by hand.");
27184
27420
  return {
27185
27421
  outcome: "skipped",
27186
27422
  reason: REDACTED_DELETE_ADDRESS_SKIP_REASON
@@ -28154,6 +28390,15 @@ function classifyFailedOp(op, stateResources) {
28154
28390
  const current = stateResources[op.logicalId];
28155
28391
  if (op.changeType === "CREATE") {
28156
28392
  if (!op.physicalId) return "skip-failed-unknown";
28393
+ if (op.physicalIdRecoveredFromError === false) return "skip-failed-superseded";
28394
+ if (op.physicalIdRecoveredFromError === true) {
28395
+ if (current) return current.physicalId === op.physicalId ? "skip-failed-noop" : "skip-failed-mismatch";
28396
+ if (stateHoldsPhysicalId(stateResources, op.resourceType, op.physicalId)) return "skip-failed-noop";
28397
+ const orphanPolicy = effectiveDeletionPolicy(op.resourceType, op.deletionPolicy, op.attemptedProperties);
28398
+ if (orphanPolicy === "Retain") return "orphan-failed-create-retain";
28399
+ if (orphanPolicy === "Snapshot") return "delete-failed-create-with-final-snapshot";
28400
+ return "delete-failed-create";
28401
+ }
28157
28402
  if (!current) return "skip-failed-noop";
28158
28403
  if (current.physicalId !== op.physicalId) return "skip-failed-mismatch";
28159
28404
  const policy = effectiveDeletionPolicy(current.resourceType, current.deletionPolicy, current.properties);
@@ -28165,6 +28410,31 @@ function classifyFailedOp(op, stateResources) {
28165
28410
  if (isTypeChangeOp(op)) return "skip-failed-type-change";
28166
28411
  return "revert-failed-update";
28167
28412
  }
28413
+ function stateHoldsPhysicalId(stateResources, resourceType, physicalId) {
28414
+ return Object.values(stateResources).some((r) => r?.resourceType === resourceType && r.physicalId === physicalId);
28415
+ }
28416
+ function demoteSupersededOrphans(segments, orphans = []) {
28417
+ let demoted = 0;
28418
+ segments.forEach((segment, s) => {
28419
+ for (const op of segment.failedOperations ?? []) {
28420
+ if (op.changeType !== "CREATE" || op.physicalIdRecoveredFromError !== true) continue;
28421
+ if (segments.slice(s + 1).some((t) => (t.operations ?? []).some((o) => mayOwn(o, op, true)) || (t.failedOperations ?? []).some((o) => mayOwn(o, op, false))) || segments.slice(s).some((t) => t.supersededLogicalIds?.includes(op.logicalId) === true) || orphans.some((o) => o?.logicalId === op.logicalId || o?.state?.resourceType === op.resourceType && o.state?.physicalId === op.physicalId)) {
28422
+ op.physicalIdRecoveredFromError = false;
28423
+ demoted++;
28424
+ }
28425
+ }
28426
+ });
28427
+ return demoted;
28428
+ }
28429
+ function mayOwn(o, orphan, completed) {
28430
+ if (o?.resourceType !== orphan.resourceType) return false;
28431
+ if (completed && o.changeType === "CREATE") return true;
28432
+ if (o.physicalId === orphan.physicalId) return true;
28433
+ return o.previousState?.physicalId === orphan.physicalId;
28434
+ }
28435
+ function isJournaledOrphan(op) {
28436
+ return op.changeType === "CREATE" && typeof op.physicalIdRecoveredFromError === "boolean";
28437
+ }
28168
28438
  function planFailedOps(failedOps, stateResources) {
28169
28439
  return failedOps.map((op) => ({
28170
28440
  op,
@@ -28997,6 +29267,10 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
28997
29267
  logger.warn(safeMsg` Rollback: Skipping failed CREATE of ${shownLogicalId(op.logicalId)} (${refusalResourceType(op.resourceType)}) — it recorded ${mask(String(op.physicalId))}, which is not the resource state tracks under this id; manual attention may be required`);
28998
29268
  recordRollbackSkip(skipScope, op, "The failed CREATE recorded a physical id other than the one state now tracks under this logical id, so the rollback left it as it is; manual attention may be required.");
28999
29269
  break;
29270
+ case "skip-failed-superseded":
29271
+ logger.warn(safeMsg` Rollback: Skipping failed CREATE of ${shownLogicalId(op.logicalId)} (${refusalResourceType(op.resourceType)}) — it created ${mask(String(op.physicalId))} before failing, and a later deploy or rollback may own a resource under that id now; if it is not in use, delete it manually`);
29272
+ recordRollbackSkip(skipScope, op, "The failed CREATE created its resource before failing, but a later deploy or rollback may own a resource under that id now, so the rollback left it as it is; manual attention may be required.");
29273
+ break;
29000
29274
  case "skip-failed-unknown":
29001
29275
  logger.warn(` Rollback: failed CREATE of ${safe(op.logicalId)} (${safe(op.resourceType)}) recorded no physical id — if it was partially created in AWS, delete it manually`);
29002
29276
  recordRollbackSkip(skipScope, op, "The failed CREATE recorded no physical id, so the rollback cannot address it; if it was partially created in AWS, delete it manually.");
@@ -29024,7 +29298,7 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
29024
29298
  }
29025
29299
  delete stateResources[op.logicalId];
29026
29300
  await noteRetainedResource(op.resourceType, op.logicalId);
29027
- logger.info(` Rollback: leaving partially-created ${safe(op.logicalId)} (${safe(op.resourceType)}) in AWS (DeletionPolicy: Retain) — removed from state`);
29301
+ logger.info(` Rollback: leaving partially-created ${safe(op.logicalId)} (${safe(op.resourceType)}) in AWS (DeletionPolicy: Retain)` + (failedCreateRecord ? " — removed from state" : " — it was never in state"));
29028
29302
  await options.afterOp?.(op.logicalId);
29029
29303
  ctx.recordEvent?.({
29030
29304
  eventType: "ROLLBACK_RESOURCE_SUCCEEDED",
@@ -29044,7 +29318,7 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
29044
29318
  const takeFinalSnapshot = snapshotPolicy && ctx.skipFinalSnapshot !== true;
29045
29319
  let finalSnapshotIdentifier;
29046
29320
  if (takeFinalSnapshot) finalSnapshotIdentifier = await prepareCreateRollbackFinalSnapshot(op, deleteProvisionedBy, ctx, mask);
29047
- logger.info(` Rollback: deleting partially-created ${safe(op.logicalId)} (${safe(op.resourceType)}) (--revert-failed)` + (takeFinalSnapshot ? " — DeletionPolicy: Snapshot" : "") + (snapshotPolicy && !takeFinalSnapshot ? " — DeletionPolicy: Snapshot NOT taken (--skip-final-snapshot)" : ""));
29321
+ logger.info(` Rollback: deleting partially-created ${safe(op.logicalId)} (${safe(op.resourceType)})` + (takeFinalSnapshot ? " — DeletionPolicy: Snapshot" : "") + (snapshotPolicy && !takeFinalSnapshot ? " — DeletionPolicy: Snapshot NOT taken (--skip-final-snapshot)" : ""));
29048
29322
  const { provider, provisionedBy: deleteRoutedVia } = ctx.providerRegistry.getProviderFor({
29049
29323
  resourceType: op.resourceType,
29050
29324
  provisionedBy: deleteProvisionedBy
@@ -29058,7 +29332,7 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
29058
29332
  deletionPolicy: snapshotPolicy ? "Snapshot" : "Delete",
29059
29333
  recordedAttributes: stateResources[op.logicalId]?.attributes
29060
29334
  });
29061
- throwIfDeleteSkipped(failedCreateDelete, op.logicalId, op.physicalId, "while deleting the partially-created resource (--revert-failed)", {
29335
+ throwIfDeleteSkipped(failedCreateDelete, op.logicalId, op.physicalId, "while deleting the partially-created resource", {
29062
29336
  ctx,
29063
29337
  stackName,
29064
29338
  resourceType: op.resourceType,
@@ -29165,7 +29439,7 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
29165
29439
  error: maskedRollbackEventError(revertError, mask)
29166
29440
  });
29167
29441
  }
29168
- inlinePolicyWriters.noteOutcome(op, action === "skip-failed-noop" || action === "skip-failed-mismatch" || ownRecord(stateResources, op.logicalId) !== recordBefore);
29442
+ inlinePolicyWriters.noteOutcome(op, action === "skip-failed-noop" || action === "skip-failed-mismatch" || action === "skip-failed-superseded" || ownRecord(stateResources, op.logicalId) !== recordBefore);
29169
29443
  }
29170
29444
  if (result.interrupted) await restoreHeldInlinePolicies(inlinePolicyWriters, stateResources, stackName, ctx, result, { refuseAll: true });
29171
29445
  if (emitEnvelope) ctx.recordEvent?.({
@@ -29210,7 +29484,7 @@ async function collectNestedChildUnaddressed(fn) {
29210
29484
  //#endregion
29211
29485
  //#region src/deployment/nested-child-journal.ts
29212
29486
  const NESTED_PENDING_PARENT_REASON = "nested-pending-parent";
29213
- const NESTED_STACK_TYPE$1 = "AWS::CloudFormation::Stack";
29487
+ const NESTED_STACK_TYPE$2 = "AWS::CloudFormation::Stack";
29214
29488
  const MAX_NESTED_WALK_DEPTH = 32;
29215
29489
  function nestedChildStackName(parentStackName, logicalId) {
29216
29490
  return `${parentStackName}~${logicalId}`;
@@ -29266,7 +29540,7 @@ function recordDisplacedSkips(recordEvent, stackName, ops) {
29266
29540
  });
29267
29541
  }
29268
29542
  function revertedNestedRowIds(ops) {
29269
- return ops.filter((op) => op.resourceType === NESTED_STACK_TYPE$1 && op.changeType === "UPDATE").map((op) => op.logicalId);
29543
+ return ops.filter((op) => op.resourceType === NESTED_STACK_TYPE$2 && op.changeType === "UPDATE").map((op) => op.logicalId);
29270
29544
  }
29271
29545
  const revertRunStorage = new AsyncLocalStorage();
29272
29546
  function withNestedRevertRun(runId, fn) {
@@ -29292,7 +29566,7 @@ async function dropNestedChildJournals(args) {
29292
29566
  if (!isPlainRecord(resources)) return;
29293
29567
  if (depthExceeded(depth, parentStackName, logger)) return;
29294
29568
  for (const [logicalId, record] of Object.entries(resources)) {
29295
- if (!isPlainRecord(record) || record["resourceType"] !== NESTED_STACK_TYPE$1) continue;
29569
+ if (!isPlainRecord(record) || record["resourceType"] !== NESTED_STACK_TYPE$2) continue;
29296
29570
  const child = nestedChildStackName(parentStackName, logicalId);
29297
29571
  try {
29298
29572
  const data = await stateBackend.getState(child, region);
@@ -29306,13 +29580,34 @@ async function dropNestedChildJournals(args) {
29306
29580
  warnUncleared(logger, child, error);
29307
29581
  }
29308
29582
  try {
29309
- await withChildLock(args.lockManager, child, region, logger, () => stateBackend.deleteRollbackJournal(child, region));
29310
- logger.debug(safeMsg`Deleted the rollback journal of nested stack ${displaySafe(child)}`);
29583
+ if (await withChildLock(args.lockManager, child, region, logger, async () => {
29584
+ if (args.beforeDelete) {
29585
+ if (!await args.beforeDelete(child, await readOwnRecord(stateBackend, child, region))) return false;
29586
+ }
29587
+ let gone;
29588
+ try {
29589
+ gone = await stateBackend.deleteRollbackJournal(child, region);
29590
+ } catch (error) {
29591
+ await args.onDeleteFailed?.(child);
29592
+ throw error;
29593
+ }
29594
+ if (gone === false) {
29595
+ await args.onDeleteFailed?.(child);
29596
+ return false;
29597
+ }
29598
+ return true;
29599
+ })) logger.debug(safeMsg`Deleted the rollback journal of nested stack ${displaySafe(child)}`);
29311
29600
  } catch (error) {
29312
29601
  warnUncleared(logger, child, error);
29313
29602
  }
29314
29603
  }
29315
29604
  }
29605
+ async function readOwnRecord(stateBackend, child, region) {
29606
+ try {
29607
+ const data = await stateBackend.getState(child, region);
29608
+ if (data && isPlainRecord(data.state) && isPlainRecord(data.state.resources) && (data.state.stackName === void 0 || data.state.stackName === child)) return data.state;
29609
+ } catch {}
29610
+ }
29316
29611
  async function dropSettledNestedJournals(args) {
29317
29612
  const { stateBackend, parentStackName, region, runId, logger } = args;
29318
29613
  const depth = args.depth ?? 0;
@@ -29622,6 +29917,306 @@ function makeSiblingClaimReader(params) {
29622
29917
  };
29623
29918
  }
29624
29919
 
29920
+ //#endregion
29921
+ //#region src/deployment/rollback-executor/journaled-orphans.ts
29922
+ const NONE = {
29923
+ segments: [],
29924
+ count: 0
29925
+ };
29926
+ async function loadJournaledOrphans(stateBackend, stackName, region, rollbackOrphans, logger, options = {}) {
29927
+ let segments;
29928
+ try {
29929
+ const journal = await stateBackend.loadRollbackJournal(stackName, region);
29930
+ if (!journal) return NONE;
29931
+ segments = journal.segments;
29932
+ } catch (err) {
29933
+ const detail = err instanceof Error ? err.message : String(err);
29934
+ logger.warn(safeMsg`Could not read the stack's rollback journal (${displaySafe(detail)}); ` + safeMsg`no resource it records for a failed CREATE is deleted, and ${options.unreadableOutcome ?? "a destroy that proceeds removes the journal with the state"}.`);
29935
+ return {
29936
+ ...NONE,
29937
+ unreadable: true
29938
+ };
29939
+ }
29940
+ const physicalIdOnly = (ops) => ops.filter((op) => op.changeType !== "CREATE");
29941
+ const evidence = segments.map((segment) => options.deployRunId !== void 0 && segment.reason === "nested-pending-parent" && segment.runId === options.deployRunId ? {
29942
+ ...segment,
29943
+ operations: physicalIdOnly(segment.operations)
29944
+ } : segment);
29945
+ const newer = options.newerOperations ?? [];
29946
+ if (newer.length > 0) evidence.push({ operations: physicalIdOnly(newer) });
29947
+ demoteSupersededOrphans(evidence, Array.isArray(rollbackOrphans) ? rollbackOrphans : []);
29948
+ const deployLogicalIds = new Set(newer.map((op) => op.logicalId));
29949
+ for (const segment of segments) if (options.deployRunId !== void 0 && segment.reason === "nested-pending-parent" && segment.runId === options.deployRunId) for (const op of segment.operations) deployLogicalIds.add(op.logicalId);
29950
+ const out = [];
29951
+ let count = 0;
29952
+ for (let s = segments.length - 1; s >= 0; s--) {
29953
+ const segment = segments[s];
29954
+ if (segment.reason === "nested-pending-parent") continue;
29955
+ const ops = splitImportedOps(segment.failedOperations ?? [], segment).replay.filter(isJournaledOrphan);
29956
+ if (ops.length === 0) continue;
29957
+ out.push({
29958
+ segment,
29959
+ ops
29960
+ });
29961
+ count += ops.length;
29962
+ }
29963
+ return {
29964
+ segments: out,
29965
+ count,
29966
+ deployLogicalIds
29967
+ };
29968
+ }
29969
+ function journaledOrphanLines(orphans, logger) {
29970
+ return orphans.segments.flatMap(({ ops }) => ops.map((op) => ` - ${logicalIdShown(op.logicalId)} (${resourceTypeShown(op.resourceType)}) ${displacedPhysicalIdShown(op, logger) ?? "a physical id"}`));
29971
+ }
29972
+ async function deleteJournaledOrphans(orphans, stateResources, stackName, ctx, options = {}) {
29973
+ const total = {
29974
+ failures: 0,
29975
+ warnings: 0,
29976
+ interrupted: false,
29977
+ handled: []
29978
+ };
29979
+ const inlinePolicyWriters = new RollbackInlinePolicyWriters();
29980
+ for (const { segment, ops } of orphans.segments) {
29981
+ if (options.isInterrupted?.()) {
29982
+ total.interrupted = true;
29983
+ break;
29984
+ }
29985
+ const replay = () => withStackName(stackName, () => replayFailedOperations(ops, stateResources, stackName, ctx, {
29986
+ emitEnvelope: false,
29987
+ inlinePolicyWriters,
29988
+ ...options.isInterrupted && { isInterrupted: options.isInterrupted }
29989
+ }));
29990
+ const result = segment.skipPrefix === void 0 ? await replay() : await withSkipPrefix(segment.skipPrefix, replay);
29991
+ total.failures += result.failures;
29992
+ total.warnings += result.warnings;
29993
+ const pending = new Set(result.remainingFailedOps);
29994
+ for (const op of ops) if (!pending.has(op)) total.handled.push({
29995
+ segment,
29996
+ op
29997
+ });
29998
+ if (result.interrupted) {
29999
+ total.interrupted = true;
30000
+ break;
30001
+ }
30002
+ }
30003
+ return total;
30004
+ }
30005
+ async function settleJournaledOrphansOnSuccess(args) {
30006
+ const { stateBackend, stackName, region, stateResources, newerOperations, ctx, logger } = args;
30007
+ const stack = displayIdent(stackName);
30008
+ const newerIds = newerOperations.map((op) => op.logicalId);
30009
+ let orphans;
30010
+ try {
30011
+ orphans = await loadJournaledOrphans(stateBackend, stackName, region, args.rollbackOrphans, logger, {
30012
+ newerOperations,
30013
+ ...args.deployRunId !== void 0 && { deployRunId: args.deployRunId },
30014
+ unreadableOutcome: "the successful deploy removes the journal"
30015
+ });
30016
+ } catch (err) {
30017
+ logger.warn(safeMsg`Could not act on the rollback journal of stack ${stack}: ${errorDetail(err)}. It is kept.`);
30018
+ await markSuperseded(stateBackend, stackName, region, newerIds);
30019
+ return {
30020
+ unaddressed: 1,
30021
+ keepJournal: true
30022
+ };
30023
+ }
30024
+ if (orphans.count === 0) return {
30025
+ unaddressed: 0,
30026
+ keepJournal: false
30027
+ };
30028
+ const all = orphans.segments.flatMap(({ segment, ops }) => ops.map((op) => ({
30029
+ segment,
30030
+ op
30031
+ })));
30032
+ let kept = all;
30033
+ if (stateResources === void 0) logger.warn(safeMsg`The rollback journal of stack ${stack} records ${all.length} resource(s) a failed deploy ` + `created, but the stack's state cannot be read, so whether a record now owns them is unknown: none is deleted.`);
30034
+ else {
30035
+ logger.info(safeMsg`The rollback journal of stack ${stack} records ${all.length} resource(s) a failed deploy ` + `created that no state record holds; acting on them per their DeletionPolicy before the journal is removed:`);
30036
+ for (const line of journaledOrphanLines(orphans, logger)) logger.info(line);
30037
+ try {
30038
+ const { unreadable, tracked } = await applySuccessRule(orphans, stateResources, orphans.deployLogicalIds ?? /* @__PURE__ */ new Set(), args.foreignHolder, stack, logger, args.isInterrupted);
30039
+ const outcome = await deleteJournaledOrphans({
30040
+ segments: orphans.segments.map(({ segment, ops }) => ({
30041
+ segment,
30042
+ ops: ops.filter((op) => !unreadable.has(op) && !tracked.has(op))
30043
+ })).filter(({ ops }) => ops.length > 0),
30044
+ count: orphans.count - unreadable.size - tracked.size
30045
+ }, { ...stateResources }, stackName, ctx, args.isInterrupted ? { isInterrupted: args.isInterrupted } : {});
30046
+ kept = all.filter(({ segment, op }) => unreadable.has(op) || !tracked.has(op) && !isHandledOrphan(outcome.handled, segment, op));
30047
+ } catch (err) {
30048
+ logger.warn(safeMsg`Acting on the journaled resources of stack ${stack} failed: ${errorDetail(err)}`);
30049
+ }
30050
+ }
30051
+ const keptOps = new Set(kept.map(({ op }) => op));
30052
+ const skipped = all.filter(({ op }) => !keptOps.has(op) && op.physicalIdRecoveredFromError === false).length;
30053
+ const cleared = all.filter(({ op }) => !keptOps.has(op));
30054
+ const stripCleared = cleared.length === 0 ? void 0 : async () => {
30055
+ try {
30056
+ await stateBackend.dropRollbackJournalFailedOperations(stackName, region, (op, segment) => isHandledOrphan(cleared, segment, op));
30057
+ } catch (err) {
30058
+ logger.warn(safeMsg`Failed to remove the settled entries from the rollback journal of stack ${stack}: ` + safeMsg`${errorDetail(err)}. A cdkd rollback or cdkd destroy could act on them again; run a ` + "successful cdkd deploy of the stack first, which settles them again.");
30059
+ }
30060
+ };
30061
+ if (kept.length === 0) return {
30062
+ unaddressed: skipped,
30063
+ keepJournal: false,
30064
+ ...stripCleared && { stripCleared }
30065
+ };
30066
+ const keptIds = new Set(kept.map(({ op }) => op.logicalId));
30067
+ const supersededIds = newerIds.filter((id) => !keptIds.has(id));
30068
+ const demoted = kept.filter(({ op }) => op.physicalIdRecoveredFromError === false);
30069
+ const reduce = () => stateBackend.reduceRollbackJournalToFailedOperations(stackName, region, (op, segment) => isHandledOrphan(kept, segment, op), supersededIds, (op, segment) => isHandledOrphan(demoted, segment, op));
30070
+ let reduced = false;
30071
+ for (let attempt = 0; attempt < 2 && !reduced; attempt++) try {
30072
+ await reduce();
30073
+ reduced = true;
30074
+ } catch (err) {
30075
+ if (attempt === 1) logger.warn(safeMsg`Failed to reduce the rollback journal of stack ${stack} to its undeleted resources: ` + safeMsg`${errorDetail(err)}. The whole journal is kept.`);
30076
+ }
30077
+ if (!reduced) {
30078
+ await markSuperseded(stateBackend, stackName, region, supersededIds);
30079
+ await stripCleared?.();
30080
+ }
30081
+ logger.warn(safeMsg`${kept.length} resource(s) a failed deploy of stack ${stack} created were not deleted ` + (reduced ? "(see above). The rollback journal, their only record, is kept with just them; the next " : "(see above). The rollback journal, their only record, is kept; the next ") + "successful deploy retries.");
30082
+ return {
30083
+ unaddressed: kept.length + skipped,
30084
+ keepJournal: true
30085
+ };
30086
+ }
30087
+ async function applySuccessRule(orphans, stateResources, deployLogicalIds, foreignHolder, stack, logger, isInterrupted) {
30088
+ const unreadable = /* @__PURE__ */ new Set();
30089
+ const tracked = /* @__PURE__ */ new Set();
30090
+ let interrupted = false;
30091
+ for (const { ops } of orphans.segments) for (const op of ops) {
30092
+ if (op.physicalIdRecoveredFromError !== true || !op.physicalId) continue;
30093
+ if (interrupted || isInterrupted?.()) {
30094
+ interrupted = true;
30095
+ unreadable.add(op);
30096
+ continue;
30097
+ }
30098
+ if (Object.values(stateResources).some((r) => r?.resourceType === op.resourceType && r.physicalId === op.physicalId)) {
30099
+ tracked.add(op);
30100
+ continue;
30101
+ }
30102
+ if (Object.prototype.hasOwnProperty.call(stateResources, op.logicalId) || deployLogicalIds.has(op.logicalId)) {
30103
+ op.physicalIdRecoveredFromError = false;
30104
+ continue;
30105
+ }
30106
+ const holding = await foreignHolder(op.resourceType, op.physicalId);
30107
+ if (holding === void 0) continue;
30108
+ if (holding.kind === "held") {
30109
+ logger.warn(safeMsg`${logicalIdShown(op.logicalId)} (${resourceTypeShown(op.resourceType)}), which a failed deploy ` + safeMsg`of stack ${stack} created, is not deleted: ${holding.by} holds a resource of that type under ` + "the same physical id.");
30110
+ op.physicalIdRecoveredFromError = false;
30111
+ continue;
30112
+ }
30113
+ logger.warn(safeMsg`${logicalIdShown(op.logicalId)} (${resourceTypeShown(op.resourceType)}), which a failed deploy ` + safeMsg`of stack ${stack} created, is not deleted: ${holding.what}, so whether another stack holds it is unknown.`);
30114
+ unreadable.add(op);
30115
+ }
30116
+ return {
30117
+ unreadable,
30118
+ tracked
30119
+ };
30120
+ }
30121
+ function makeForeignHolderScan(stateBackend) {
30122
+ let scan;
30123
+ const key = (type, id) => JSON.stringify([type, id]);
30124
+ const run = async () => {
30125
+ const holders = /* @__PURE__ */ new Map();
30126
+ let refs;
30127
+ try {
30128
+ refs = await stateBackend.listStacks();
30129
+ } catch {
30130
+ return {
30131
+ holders,
30132
+ unreadable: "the state bucket listing (it could not be read)"
30133
+ };
30134
+ }
30135
+ let unreadable;
30136
+ await Promise.all(refs.map(async (ref) => {
30137
+ const named = (why) => safeMsg`the state record of stack ${displayIdent(ref.stackName)} (${why})`;
30138
+ if (ref.region === void 0) {
30139
+ unreadable ??= named("its region cannot be read");
30140
+ return;
30141
+ }
30142
+ const region = ref.region;
30143
+ let state;
30144
+ try {
30145
+ state = (await stateBackend.getState(ref.stackName, region))?.state;
30146
+ } catch {
30147
+ unreadable ??= named("it could not be read");
30148
+ return;
30149
+ }
30150
+ if (state === void 0) return;
30151
+ const resources = state.resources;
30152
+ if (resources !== void 0 && (typeof resources !== "object" || resources === null || Array.isArray(resources))) {
30153
+ unreadable ??= named("its resources cannot be read");
30154
+ return;
30155
+ }
30156
+ if (!hasReadableOrphans(state) || unreadableOrphanRecords(state).length > 0) {
30157
+ unreadable ??= named("its rollback-orphan records cannot be read");
30158
+ return;
30159
+ }
30160
+ const held = [...Object.values(resources ?? {}), ...(state.orphans ?? []).map((record) => record.state)];
30161
+ for (const record of held) {
30162
+ const r = record;
30163
+ if (typeof r?.resourceType !== "string" || typeof r.physicalId !== "string") continue;
30164
+ const k = key(r.resourceType, r.physicalId);
30165
+ const list = holders.get(k) ?? [];
30166
+ list.push({
30167
+ stackName: ref.stackName,
30168
+ region
30169
+ });
30170
+ holders.set(k, list);
30171
+ }
30172
+ }));
30173
+ return {
30174
+ holders,
30175
+ ...unreadable !== void 0 && { unreadable }
30176
+ };
30177
+ };
30178
+ return (self) => async (resourceType, physicalId) => {
30179
+ scan ??= run();
30180
+ const { holders, unreadable } = await scan;
30181
+ const other = (holders.get(key(resourceType, physicalId)) ?? []).find((h) => !(h.stackName === self.stackName && h.region === self.region));
30182
+ if (other) return {
30183
+ kind: "held",
30184
+ by: safeMsg`the state record of stack ${displayIdent(other.stackName)} (${displaySafe(other.region)})`
30185
+ };
30186
+ return unreadable === void 0 ? void 0 : {
30187
+ kind: "unreadable",
30188
+ what: unreadable
30189
+ };
30190
+ };
30191
+ }
30192
+ async function markSuperseded(stateBackend, stackName, region, ids) {
30193
+ try {
30194
+ await stateBackend.markRollbackJournalSuperseded(stackName, region, ids);
30195
+ } catch {}
30196
+ }
30197
+ function errorDetail(err) {
30198
+ return displaySafe(err instanceof Error ? err.message : String(err));
30199
+ }
30200
+ function isHandledOrphan(handled, segment, op) {
30201
+ return handled.some((h) => h.segment.timestamp === segment.timestamp && h.segment.runId === segment.runId && h.op.logicalId === op.logicalId && h.op.changeType === op.changeType && h.op.resourceType === op.resourceType && h.op.physicalId === op.physicalId);
30202
+ }
30203
+ function orphanKey(segment, op) {
30204
+ return JSON.stringify([
30205
+ segment.timestamp,
30206
+ segment.runId ?? null,
30207
+ op.logicalId,
30208
+ op.changeType,
30209
+ op.resourceType,
30210
+ op.physicalId ?? null
30211
+ ]);
30212
+ }
30213
+ function sameJournaledOrphans(a, b) {
30214
+ const keys = (o) => o.segments.flatMap(({ segment, ops }) => ops.map((op) => orphanKey(segment, op))).sort();
30215
+ const ka = keys(a);
30216
+ const kb = keys(b);
30217
+ return ka.length === kb.length && ka.every((k, i) => k === kb[i]);
30218
+ }
30219
+
29625
30220
  //#endregion
29626
30221
  //#region src/deployment/deploy-engine/rollback.ts
29627
30222
  function redactOperationsForJournal(operations) {
@@ -29670,33 +30265,109 @@ async function adoptRollbackOrphans(currentState, effectiveTemplate) {
29670
30265
  if (records.length > 0) currentState.orphans = plan.remaining;
29671
30266
  return plan;
29672
30267
  }
29673
- async function performRollback(completedOperations, stateResources, stackName, previousState) {
30268
+ async function performRollback(completedOperations, stateResources, stackName, previousState, failedOperations = [], priorOrphans = []) {
30269
+ const orphanIndexes = [];
30270
+ const orphanOps = [];
30271
+ failedOperations.forEach((op, i) => {
30272
+ if (!isJournaledOrphan(op)) return;
30273
+ orphanIndexes.push(i);
30274
+ orphanOps.push({ ...op });
30275
+ });
30276
+ demoteSupersededOrphans([{
30277
+ operations: completedOperations,
30278
+ failedOperations: orphanOps
30279
+ }], priorOrphans);
30280
+ const ctx = this.rollbackExecutorContext(previousState, stackName);
30281
+ const inlinePolicyWriters = new RollbackInlinePolicyWriters();
29674
30282
  const runId = this.options.eventRecorder?.runId;
29675
- const { result, run } = await withNestedRevertRun(runId, async (scope) => ({
29676
- result: await replayRollback(completedOperations, stateResources, stackName, this.rollbackExecutorContext(previousState, stackName)),
29677
- run: scope
29678
- }));
30283
+ const { result, failed, run } = await withNestedRevertRun(runId, async (scope) => {
30284
+ return {
30285
+ failed: orphanOps.length > 0 ? await replayFailedOperations(orphanOps, stateResources, stackName, ctx, {
30286
+ emitEnvelope: completedOperations.length === 0,
30287
+ inlinePolicyWriters
30288
+ }) : void 0,
30289
+ result: await replayRollback(completedOperations, stateResources, stackName, ctx, { inlinePolicyWriters }),
30290
+ run: scope
30291
+ };
30292
+ });
30293
+ const pendingOrphans = new Set(failed?.remainingFailedOps ?? []);
30294
+ const handled = new Set(orphanIndexes.filter((_, k) => !pendingOrphans.has(orphanOps[k])));
29679
30295
  return {
29680
- failures: result.failures,
29681
- warnings: result.warnings + run.warnings,
29682
- skipped: result.skipped,
29683
- orphaned: result.orphaned,
29684
- settledNested: run.settled
30296
+ failures: result.failures + (failed?.failures ?? 0),
30297
+ warnings: result.warnings + run.warnings + (failed?.warnings ?? 0),
30298
+ skipped: result.skipped + (failed?.skipped ?? 0),
30299
+ orphaned: [...failed?.orphaned ?? [], ...result.orphaned],
30300
+ settledNested: run.settled,
30301
+ remainingFailedOps: failedOperations.filter((_, i) => !handled.has(i))
29685
30302
  };
29686
30303
  }
29687
30304
  async function settleJournalAfterSuccess(stackName, completedOperations, previousState, finalResources, initialDeploy) {
29688
30305
  if (this.options.parentStackInfo) {
29689
30306
  await this.writeRollbackJournalSegment(stackName, completedOperations, [], NESTED_PENDING_PARENT_REASON, initialDeploy, nestedPendingSnapshot(previousState));
29690
- return;
29691
- }
29692
- await Promise.all([this.deleteRollbackJournalBestEffort(stackName, completedOperations.map((op) => op.logicalId)), dropNestedChildJournals({
30307
+ return 0;
30308
+ }
30309
+ let nestedLeft = 0;
30310
+ const foreignHolderFor = makeForeignHolderScan(this.stateBackend);
30311
+ const deployRunId = this.options.eventRecorder?.runId;
30312
+ const stripOnFailure = /* @__PURE__ */ new Map();
30313
+ const [ownLeft] = await Promise.all([(async () => {
30314
+ const own = await settleJournaledOrphansOnSuccess({
30315
+ stateBackend: this.stateBackend,
30316
+ stackName,
30317
+ region: this.stackRegion,
30318
+ stateResources: finalResources,
30319
+ rollbackOrphans: previousState.orphans,
30320
+ newerOperations: completedOperations,
30321
+ ...deployRunId !== void 0 && { deployRunId },
30322
+ foreignHolder: foreignHolderFor({
30323
+ stackName,
30324
+ region: this.stackRegion
30325
+ }),
30326
+ ctx: this.rollbackExecutorContext(previousState, stackName),
30327
+ isInterrupted: () => this.interrupted,
30328
+ logger: this.logger
30329
+ });
30330
+ if (own.keepJournal) return own.unaddressed;
30331
+ if (!await this.deleteRollbackJournalBestEffort(stackName, completedOperations.map((op) => op.logicalId))) await own.stripCleared?.();
30332
+ return own.unaddressed;
30333
+ })(), dropNestedChildJournals({
29693
30334
  stateBackend: this.stateBackend,
29694
30335
  lockManager: this.lockManager,
29695
30336
  parentStackName: stackName,
29696
30337
  region: this.stackRegion,
29697
30338
  resources: finalResources,
29698
- logger: this.logger
30339
+ logger: this.logger,
30340
+ beforeDelete: async (child, childRecord) => {
30341
+ const childState = childRecord !== void 0 && hasReadableOrphans(childRecord) ? childRecord : void 0;
30342
+ const settled = await settleJournaledOrphansOnSuccess({
30343
+ stateBackend: this.stateBackend,
30344
+ stackName: child,
30345
+ region: this.stackRegion,
30346
+ stateResources: childState?.resources,
30347
+ rollbackOrphans: childState?.orphans,
30348
+ newerOperations: [],
30349
+ ...deployRunId !== void 0 && { deployRunId },
30350
+ foreignHolder: foreignHolderFor({
30351
+ stackName: child,
30352
+ region: this.stackRegion
30353
+ }),
30354
+ isInterrupted: () => this.interrupted,
30355
+ ctx: {
30356
+ ...this.rollbackExecutorContext(childState ?? previousState, child),
30357
+ producerRegionsIncomplete: true,
30358
+ nestedChildStack: child
30359
+ },
30360
+ logger: this.logger
30361
+ });
30362
+ nestedLeft += settled.unaddressed;
30363
+ if (settled.stripCleared) stripOnFailure.set(child, settled.stripCleared);
30364
+ return !settled.keepJournal;
30365
+ },
30366
+ onDeleteFailed: async (child) => {
30367
+ await stripOnFailure.get(child)?.();
30368
+ }
29699
30369
  })]);
30370
+ return ownLeft + nestedLeft;
29700
30371
  }
29701
30372
  async function deleteRollbackJournalBestEffort(stackName, supersededLogicalIds = []) {
29702
30373
  let deleted;
@@ -29706,12 +30377,14 @@ async function deleteRollbackJournalBestEffort(stackName, supersededLogicalIds =
29706
30377
  this.logger.debug(safeMsg`Failed to delete rollback journal for ${stackName}: ${err instanceof Error ? err.message : String(err)}`);
29707
30378
  deleted = false;
29708
30379
  }
29709
- if (deleted !== false || supersededLogicalIds.length === 0) return;
30380
+ if (deleted !== false) return true;
30381
+ if (supersededLogicalIds.length === 0) return false;
29710
30382
  try {
29711
30383
  await this.stateBackend.markRollbackJournalSuperseded(stackName, this.stackRegion, supersededLogicalIds);
29712
30384
  } catch (markErr) {
29713
30385
  this.logger.debug(safeMsg`Failed to record superseded logical ids on the rollback journal for ${stackName}: ${markErr instanceof Error ? markErr.message : String(markErr)}`);
29714
30386
  }
30387
+ return false;
29715
30388
  }
29716
30389
  async function settleJournalAfterCleanRollback(stackName, failedOperations, initialDeploy) {
29717
30390
  if (failedOperations.length === 0) {
@@ -30850,6 +31523,7 @@ function priorAttemptsInJournal(journal, logicalId, resourceType) {
30850
31523
  }
30851
31524
  function recordedAsNewResource(op) {
30852
31525
  if (typeof op.physicalId !== "string" || op.physicalId === "") return false;
31526
+ if (op.physicalIdRecoveredFromError === true) return false;
30853
31527
  if (op.changeType === "CREATE") return true;
30854
31528
  const previous = op.previousState?.physicalId;
30855
31529
  return typeof previous === "string" && previous !== op.physicalId;
@@ -31502,6 +32176,7 @@ var DynamoDBGlobalTableProvider = class {
31502
32176
  };
31503
32177
  } catch (wiringError) {
31504
32178
  warn(`Wiring failed after CreateTable for ${tableName}; attempting best-effort cleanup`);
32179
+ let tableLeftBehind = false;
31505
32180
  try {
31506
32181
  const replicasForCleanup = (await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.Replicas ?? [];
31507
32182
  for (const replica of replicasForCleanup) {
@@ -31520,12 +32195,15 @@ var DynamoDBGlobalTableProvider = class {
31520
32195
  }
31521
32196
  await this.dynamoDBClient.send(new DeleteTableCommand({ TableName: tableName }));
31522
32197
  } catch (cleanupErr) {
32198
+ tableLeftBehind = true;
31523
32199
  const cleanupMsg = describeAwsFailure(cleanupErr).detail;
31524
32200
  warn(`Partial-create cleanup failed for ${tableName}: ${cleanupMsg}. To remove the orphaned AWS-side table, run: ` + pasteableAwsCommand(maskSecrets)`aws dynamodb delete-table --table-name ${tableName}`.render());
31525
32201
  }
31526
32202
  markAuxiliaryFailure(wiringError, logicalId);
31527
32203
  const cause = wiringError instanceof Error ? wiringError : void 0;
31528
- throw this.wrapMaskedError(log, wiringError, (text) => new ProvisioningError(`Failed to create DynamoDB GlobalTable ${logicalId}: ${text}`, resourceType, logicalId, tableName, cause));
32204
+ const thrown = this.wrapMaskedError(log, wiringError, (text) => new ProvisioningError(`Failed to create DynamoDB GlobalTable ${logicalId}: ${text}`, resourceType, logicalId, tableName, cause));
32205
+ if (tableLeftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, tableName);
32206
+ throw thrown;
31529
32207
  }
31530
32208
  }
31531
32209
  reportThroughputDiagnostics(diagnostics, logicalId, physicalId, maskSecrets = (text) => text) {
@@ -34547,31 +35225,61 @@ function validateOptions(opts) {
34547
35225
  }
34548
35226
  async function withResourceDeadline(operation, opts) {
34549
35227
  validateOptions(opts);
34550
- const startedAt = Date.now();
34551
35228
  return new Promise((resolve, reject) => {
34552
35229
  let settled = false;
35230
+ let timedOut = false;
35231
+ let warned = false;
34553
35232
  let warnTimer;
34554
35233
  let timeoutTimer;
35234
+ let elapsedBeforePause = 0;
35235
+ let runningSince = Date.now();
35236
+ let pauses = 0;
35237
+ const elapsed = () => elapsedBeforePause + (pauses > 0 ? 0 : Date.now() - runningSince);
34555
35238
  const cleanup = () => {
34556
35239
  if (warnTimer !== void 0) clearTimeout(warnTimer);
34557
35240
  if (timeoutTimer !== void 0) clearTimeout(timeoutTimer);
34558
35241
  warnTimer = void 0;
34559
35242
  timeoutTimer = void 0;
34560
35243
  };
34561
- if (opts.onWarn) warnTimer = setTimeout(() => {
34562
- if (settled) return;
34563
- try {
34564
- opts.onWarn(Date.now() - startedAt);
34565
- } catch {}
34566
- }, opts.warnAfterMs);
34567
- timeoutTimer = setTimeout(() => {
34568
- if (settled) return;
34569
- settled = true;
34570
- cleanup();
34571
- const elapsed = Date.now() - startedAt;
34572
- reject(opts.onTimeout(elapsed));
34573
- }, opts.timeoutMs);
34574
- Promise.resolve().then(() => operation()).then((value) => {
35244
+ const arm = () => {
35245
+ if (opts.onWarn && !warned) warnTimer = setTimeout(() => {
35246
+ if (settled) return;
35247
+ warned = true;
35248
+ try {
35249
+ opts.onWarn(elapsed());
35250
+ } catch {}
35251
+ }, Math.max(0, opts.warnAfterMs - elapsed()));
35252
+ timeoutTimer = setTimeout(() => {
35253
+ if (settled) return;
35254
+ settled = true;
35255
+ timedOut = true;
35256
+ cleanup();
35257
+ reject(opts.onTimeout(elapsed()));
35258
+ }, Math.max(0, opts.timeoutMs - elapsed()));
35259
+ };
35260
+ arm();
35261
+ const outer = deadlineScope.getStore();
35262
+ const control = {
35263
+ pause: () => {
35264
+ pauses += 1;
35265
+ if (pauses === 1) {
35266
+ elapsedBeforePause += Date.now() - runningSince;
35267
+ cleanup();
35268
+ }
35269
+ outer?.pause();
35270
+ },
35271
+ resume: () => {
35272
+ if (pauses === 0) return;
35273
+ pauses -= 1;
35274
+ if (pauses === 0) {
35275
+ runningSince = Date.now();
35276
+ if (!settled) arm();
35277
+ }
35278
+ outer?.resume();
35279
+ },
35280
+ expired: () => timedOut || (outer?.expired() ?? false)
35281
+ };
35282
+ Promise.resolve().then(() => deadlineScope.run(control, operation)).then((value) => {
34575
35283
  if (settled) return;
34576
35284
  settled = true;
34577
35285
  cleanup();
@@ -34584,6 +35292,20 @@ async function withResourceDeadline(operation, opts) {
34584
35292
  });
34585
35293
  });
34586
35294
  }
35295
+ const deadlineScope = new AsyncLocalStorage();
35296
+ async function whileEnclosingDeadlinesPaused(fn) {
35297
+ const control = deadlineScope.getStore();
35298
+ if (control === void 0) return fn();
35299
+ control.pause();
35300
+ try {
35301
+ return await fn();
35302
+ } finally {
35303
+ control.resume();
35304
+ }
35305
+ }
35306
+ function enclosingDeadlineExpired() {
35307
+ return deadlineScope.getStore()?.expired() ?? false;
35308
+ }
34587
35309
 
34588
35310
  //#endregion
34589
35311
  //#region src/deployment/deploy-engine/provision.ts
@@ -34979,15 +35701,20 @@ async function executeDeployment(template, currentState, changes, dag, execution
34979
35701
  this.interrupted = true;
34980
35702
  this.interruptCause ??= "sibling-failure";
34981
35703
  const refused = isRefusedBeforeApplying(provisionError, logicalId);
34982
- const physicalId = newResources[logicalId]?.physicalId ?? previousState?.physicalId;
34983
- if (refused && change.changeType === "CREATE" && physicalId === void 0) throw provisionError;
35704
+ const statePhysicalId = newResources[logicalId]?.physicalId ?? previousState?.physicalId;
35705
+ if (refused && change.changeType === "CREATE" && statePhysicalId === void 0) throw provisionError;
35706
+ const createdId = change.changeType === "CREATE" && statePhysicalId === void 0 ? createdBeforeFailure(provisionError, logicalId, change.resourceType) : void 0;
34984
35707
  failedOperations.push({
34985
35708
  logicalId,
34986
35709
  changeType: change.changeType,
34987
35710
  resourceType: change.resourceType,
34988
- provisionedBy: newResources[logicalId]?.provisionedBy ?? previousState?.provisionedBy,
35711
+ provisionedBy: createdId !== void 0 ? "sdk" : newResources[logicalId]?.provisionedBy ?? previousState?.provisionedBy,
34989
35712
  ...previousState && { previousState },
34990
- physicalId,
35713
+ physicalId: createdId ?? statePhysicalId,
35714
+ ...createdId !== void 0 && {
35715
+ physicalIdRecoveredFromError: true,
35716
+ deletionPolicy: journaledOrphanPolicy(this.extractTemplateAttributes(template, logicalId).deletionPolicy)
35717
+ },
34991
35718
  ...!refused && { attemptedProperties: this.attemptedResolvedProps.get(logicalId) }
34992
35719
  });
34993
35720
  throw provisionError;
@@ -35141,6 +35868,7 @@ async function executeDeployment(template, currentState, changes, dag, execution
35141
35868
  let autoRollbackJournaled = false;
35142
35869
  let rollbackOrphans = [];
35143
35870
  let rollbackSettledNested = /* @__PURE__ */ new Map();
35871
+ let rollbackRemainingFailedOps = failedOperations;
35144
35872
  if (error instanceof InterruptedError || isInterruptedWaitError(error) || this.interruptCause === "user") {
35145
35873
  await this.writeRollbackJournalSegment(stackName, completedOperations, failedOperations, "interrupted", initialDeploy);
35146
35874
  this.logger.info(`Partial state saved (${Object.keys(newResources).length} resources). Run deploy again to resume, 'cdkd rollback' to revert, or destroy to clean up.`);
@@ -35152,7 +35880,7 @@ async function executeDeployment(template, currentState, changes, dag, execution
35152
35880
  this.logger.warn(safeMsg`Partial state has been saved. ${this.recoveryHint("Run 'cdkd deploy' to resume, 'cdkd rollback' to revert, or destroy to clean up.")}`);
35153
35881
  } else {
35154
35882
  autoRollbackJournaled = await this.writeRollbackJournalSegment(stackName, completedOperations, failedOperations, "auto-rollback-started", initialDeploy);
35155
- const rollbackResult = await this.performRollback(completedOperations, newResources, stackName, currentState);
35883
+ const rollbackResult = await this.performRollback(completedOperations, newResources, stackName, currentState, failedOperations, currentState.orphans ?? []);
35156
35884
  autoRollbackClean = autoRollbackJournaled && rollbackResult.failures === 0 && rollbackResult.skipped === 0;
35157
35885
  if (rollbackResult.failures === 0 && rollbackResult.skipped > 0) {
35158
35886
  this.logger.warn(safeMsg`The automatic rollback could not revert ${rollbackResult.skipped} operation(s) ` + `(see the warnings above; each is recorded as a ROLLBACK_RESOURCE_SKIPPED event).` + (autoRollbackJournaled ? ` The rollback journal keeps them.` : ""));
@@ -35163,6 +35891,7 @@ async function executeDeployment(template, currentState, changes, dag, execution
35163
35891
  }
35164
35892
  rollbackOrphans = rollbackResult.orphaned;
35165
35893
  rollbackSettledNested = rollbackResult.settledNested;
35894
+ rollbackRemainingFailedOps = rollbackResult.remainingFailedOps;
35166
35895
  }
35167
35896
  try {
35168
35897
  const postRollbackState = {
@@ -35179,7 +35908,7 @@ async function executeDeployment(template, currentState, changes, dag, execution
35179
35908
  };
35180
35909
  await this.stateBackend.saveState(stackName, this.stackRegion, this.withParentInfo(postRollbackState), { ...currentEtag !== void 0 && { expectedEtag: currentEtag } });
35181
35910
  this.logger.debug("State saved after deployment failure");
35182
- if (autoRollbackClean) await this.settleNestedChildrenAfterCleanRollback(stackName, rollbackSettledNested, await this.settleJournalAfterCleanRollback(stackName, failedOperations, initialDeploy));
35911
+ if (autoRollbackClean) await this.settleNestedChildrenAfterCleanRollback(stackName, rollbackSettledNested, await this.settleJournalAfterCleanRollback(stackName, rollbackRemainingFailedOps, initialDeploy));
35183
35912
  } catch (saveError) {
35184
35913
  this.logger.debug(`Retrying state save after rollback (ETag mismatch): ${saveError instanceof Error ? saveError.message : String(saveError)}`);
35185
35914
  try {
@@ -35198,7 +35927,7 @@ async function executeDeployment(template, currentState, changes, dag, execution
35198
35927
  };
35199
35928
  await this.stateBackend.saveState(stackName, this.stackRegion, this.withParentInfo(postRollbackState), { ...freshEtag !== void 0 && { expectedEtag: freshEtag } });
35200
35929
  this.logger.debug("State saved after deployment failure (retry succeeded)");
35201
- if (autoRollbackClean) await this.settleNestedChildrenAfterCleanRollback(stackName, rollbackSettledNested, await this.settleJournalAfterCleanRollback(stackName, failedOperations, initialDeploy));
35930
+ if (autoRollbackClean) await this.settleNestedChildrenAfterCleanRollback(stackName, rollbackSettledNested, await this.settleJournalAfterCleanRollback(stackName, rollbackRemainingFailedOps, initialDeploy));
35202
35931
  } catch (retryError) {
35203
35932
  this.logger.warn(`Failed to save state after rollback: ${retryError instanceof Error ? retryError.message : String(retryError)}`);
35204
35933
  }
@@ -35265,6 +35994,10 @@ async function persistStateAfterOutputFailure(stackName, currentState, newResour
35265
35994
  }
35266
35995
  }
35267
35996
  }
35997
+ function journaledOrphanPolicy(policy) {
35998
+ if (policy === void 0) return void 0;
35999
+ return policy === "Delete" || policy === "Retain" || policy === "Snapshot" || policy === "RetainExceptOnCreate" ? policy : "Retain";
36000
+ }
35268
36001
 
35269
36002
  //#endregion
35270
36003
  //#region src/provisioning/canonicalize-properties.ts
@@ -35366,6 +36099,62 @@ function refuseNoValueOutputs(outputs, conditions) {
35366
36099
  throw markNonRetryable(/* @__PURE__ */ new Error(`Output ${offending.join(", ")} evaluates to AWS::NoValue, and CloudFormation rejects the template for it ("The Value field of every Outputs member must evaluate to a String") before creating anything. To publish an Output only sometimes, give it a Condition instead of selecting AWS::NoValue in its Value.`));
35367
36100
  }
35368
36101
 
36102
+ //#endregion
36103
+ //#region src/deployment/deployment-approval.ts
36104
+ const NESTED_STACK_TYPE$1 = "AWS::CloudFormation::Stack";
36105
+ async function requireDeploymentApproval(args) {
36106
+ const level = args.options.requireApproval ?? "never";
36107
+ const approve = args.options.approveDeployment;
36108
+ if (level === "never" || approve === void 0) return;
36109
+ const changes = [...args.changes].filter((c) => c.changeType !== "NO_CHANGE");
36110
+ const destructiveChanges = findDestructiveChanges(args.stackName, changes, args.records, args.template, new Set(args.recreateTargetIds ?? []));
36111
+ const onlyNestedUpdates = changes.every((c) => c.changeType === "UPDATE" && c.resourceType === NESTED_STACK_TYPE$1);
36112
+ if (!(level === "destructive" ? destructiveChanges.length > 0 : changes.length > 0 && !onlyNestedUpdates)) return;
36113
+ const count = (type) => changes.filter((c) => c.changeType === type).length;
36114
+ if (enclosingDeadlineExpired()) throw approvalAfterTimeout(args.stackName);
36115
+ let approved;
36116
+ try {
36117
+ approved = await whileEnclosingDeadlinesPaused(() => approve({
36118
+ stackName: args.stackName,
36119
+ level,
36120
+ counts: {
36121
+ create: count("CREATE"),
36122
+ update: count("UPDATE"),
36123
+ delete: count("DELETE")
36124
+ },
36125
+ destructiveChanges
36126
+ }));
36127
+ } catch (error) {
36128
+ throw error instanceof Error ? markNonRetryable(error) : error;
36129
+ }
36130
+ if (!approved) throw markNonRetryable(new CdkdError(safeMsg`Deployment of stack ${args.stackName} was not approved (--require-approval=${level}). Nothing was changed.`, "DEPLOY_NOT_APPROVED"));
36131
+ }
36132
+ function approvalAfterTimeout(stackName) {
36133
+ return markNonRetryable(new CdkdError(safeMsg`Deployment of stack ${stackName} was not started: its parent's nested-stack row already timed out (--resource-timeout). Nothing was changed.`, "DEPLOY_APPROVAL_AFTER_TIMEOUT"));
36134
+ }
36135
+ async function requireOutputsOnlyApproval(args) {
36136
+ const approve = args.options.approveDeployment;
36137
+ if (args.options.requireApproval !== "any-change" || approve === void 0) return;
36138
+ if (enclosingDeadlineExpired()) throw approvalAfterTimeout(args.stackName);
36139
+ let approved;
36140
+ try {
36141
+ approved = await whileEnclosingDeadlinesPaused(() => approve({
36142
+ stackName: args.stackName,
36143
+ level: "any-change",
36144
+ counts: {
36145
+ create: 0,
36146
+ update: 0,
36147
+ delete: 0
36148
+ },
36149
+ destructiveChanges: [],
36150
+ outputsOnly: true
36151
+ }));
36152
+ } catch (error) {
36153
+ throw error instanceof Error ? markNonRetryable(error) : error;
36154
+ }
36155
+ if (!approved) throw markNonRetryable(new CdkdError(safeMsg`Deployment of stack ${args.stackName} was not approved (--require-approval=any-change). Nothing was changed.`, "DEPLOY_NOT_APPROVED"));
36156
+ }
36157
+
35369
36158
  //#endregion
35370
36159
  //#region src/deployment/condition-verdicts.ts
35371
36160
  function conditionInputsFrom(args) {
@@ -35893,6 +36682,7 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
35893
36682
  }));
35894
36683
  }
35895
36684
  const effectiveTemplate = this.templateParser.filterResourcesByCondition(template, conditions);
36685
+ this.constructPathTemplate = effectiveTemplate;
35896
36686
  const orphanCountBeforeAdoption = (currentState.orphans ?? []).length;
35897
36687
  const orphanPlan = await this.adoptRollbackOrphans(currentState, effectiveTemplate);
35898
36688
  if (Object.keys(orphanPlan.adopted).length > 0) {
@@ -35986,6 +36776,10 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
35986
36776
  const skippedOutputsChanged = !skippedOutputsEqual(currentState.skippedOutputs, this.skippedOutputs);
35987
36777
  const healedAttributesPending = this.hasUnpersistedHeals(currentState.resources);
35988
36778
  const conditionVerdictsChanged = !conditionVerdictRecordsEqual(readRecordedConditionVerdicts(currentState), conditionVerdicts);
36779
+ if (outputsChanged || exportSetChanged && currentState.exportNames !== void 0) await requireOutputsOnlyApproval({
36780
+ options: this.options,
36781
+ stackName
36782
+ });
35989
36783
  if (observedRefresh || maskedFingerprintsBackfilled || conditionVerdictsChanged || outputsChanged || exportSetChanged || skippedOutputsChanged || orphansChanged || healedAttributesPending) try {
35990
36784
  const refreshedState = {
35991
36785
  version: 10,
@@ -36019,13 +36813,12 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
36019
36813
  this.logger.warn(`Failed to persist no-change state update: ${saveError instanceof Error ? saveError.message : String(saveError)} — drift baseline / outputs will be re-resolved on next deploy.`);
36020
36814
  }
36021
36815
  }
36022
- if (!this.options.dryRun) await this.settleJournalAfterSuccess(stackName, [], currentState, currentState.resources, currentEtag === void 0);
36023
36816
  return {
36024
36817
  stackName,
36025
36818
  created: 0,
36026
36819
  updated: 0,
36027
36820
  deleted: 0,
36028
- deleteSkipped: 0,
36821
+ deleteSkipped: this.options.dryRun ? 0 : await this.settleJournalAfterSuccess(stackName, [], currentState, currentState.resources, currentEtag === void 0),
36029
36822
  updatePartial: 0,
36030
36823
  unchanged: Object.keys(currentState.resources).length,
36031
36824
  durationMs: Date.now() - startTime,
@@ -36051,6 +36844,14 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
36051
36844
  attributeFallbackCount: this.resolver.getPhysicalIdFallbackCount()
36052
36845
  };
36053
36846
  }
36847
+ await requireDeploymentApproval({
36848
+ options: this.options,
36849
+ stackName,
36850
+ changes: changes.values(),
36851
+ records: currentState.resources,
36852
+ template: effectiveTemplate,
36853
+ recreateTargetIds: recreateTargetIdsFor(this.options.recreateTargets, stackName)
36854
+ });
36054
36855
  this.resolver.resetPhysicalIdFallbackCount();
36055
36856
  const progress = {
36056
36857
  current: 0,
@@ -36064,7 +36865,7 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
36064
36865
  } : newState));
36065
36866
  this.logger.debug(`State saved (ETag: ${newEtag})`);
36066
36867
  await forgetRecordedCreateTokens(Object.keys(newState.resources));
36067
- await Promise.all([this.settleJournalAfterSuccess(stackName, completedOperations, currentState, newState.resources, currentEtag === void 0), this.exportIndexStore ? this.exportIndexStore.updateForStack(stackName, this.stackRegion, importableOutputs({
36868
+ const [journaledOrphansLeft] = await Promise.all([this.settleJournalAfterSuccess(stackName, completedOperations, currentState, newState.resources, currentEtag === void 0), this.exportIndexStore ? this.exportIndexStore.updateForStack(stackName, this.stackRegion, importableOutputs({
36068
36869
  ...newState,
36069
36870
  outputs: this.redactOutputs(newState.outputs)
36070
36871
  })) : Promise.resolve()]);
@@ -36075,7 +36876,7 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
36075
36876
  created: actualCounts.created,
36076
36877
  updated: actualCounts.updated,
36077
36878
  deleted: actualCounts.deleted,
36078
- deleteSkipped: actualCounts.deleteSkipped,
36879
+ deleteSkipped: actualCounts.deleteSkipped + journaledOrphansLeft,
36079
36880
  updatePartial: actualCounts.updatePartial,
36080
36881
  nestedUpdatePartial: actualCounts.nestedUpdatePartial,
36081
36882
  unchanged: unchangedCount,
@@ -36400,6 +37201,7 @@ var DeployEngine = class {
36400
37201
  outputsTemplateSource = Object.create(null);
36401
37202
  resolvedExportNames = [];
36402
37203
  skippedOutputs;
37204
+ constructPathTemplate;
36403
37205
  outputsSourceUsable = true;
36404
37206
  attemptedResolvedProps = /* @__PURE__ */ new Map();
36405
37207
  liveTaskLabels = /* @__PURE__ */ new Map();
@@ -36441,6 +37243,7 @@ var DeployEngine = class {
36441
37243
  this.perResourceSecrets = /* @__PURE__ */ new Map();
36442
37244
  this.noEchoAttributeResources = /* @__PURE__ */ new Map();
36443
37245
  this.perResourceTemplateProps = /* @__PURE__ */ new Map();
37246
+ this.constructPathTemplate = void 0;
36444
37247
  this.perResourceResolvedType = /* @__PURE__ */ new Map();
36445
37248
  this.fingerprintNoEchoValues = void 0;
36446
37249
  this.fingerprintParameters = void 0;
@@ -36473,7 +37276,7 @@ var DeployEngine = class {
36473
37276
  const secrets = this.perResourceSecrets.get(logicalId);
36474
37277
  const templateProps = this.perResourceTemplateProps.get(logicalId);
36475
37278
  const scrubbed = scrubResourceRecord(this.withHealedAttributes(logicalId, record), secrets ?? /* @__PURE__ */ new Map(), templateProps);
36476
- resources[logicalId] = withMaskedPropertyFingerprints(scrubbed, record.properties, templateProps, secrets, this.fingerprintNoEchoValues, this.perResourceInputFingerprints.get(logicalId));
37279
+ resources[logicalId] = withConstructPath(withMaskedPropertyFingerprints(scrubbed, record.properties, templateProps, secrets, this.fingerprintNoEchoValues, this.perResourceInputFingerprints.get(logicalId)), constructPathOf(this.constructPathTemplate, logicalId));
36477
37280
  }
36478
37281
  const orphans = state.orphans?.map((entry) => {
36479
37282
  const sameResource = this.perResourceResolvedType.get(entry.logicalId) === entry.state.resourceType;
@@ -36654,5 +37457,5 @@ DeployEngine.prototype.replacementNameOrigin = replacementNameOrigin;
36654
37457
  DeployEngine.prototype.orphanedNameCollisionAdvice = orphanedNameCollisionAdvice;
36655
37458
 
36656
37459
  //#endregion
36657
- export { resourcesNamingDeclaredParameter as $, wholeStringLeavesOf as $a, isWarmThroughputDecrease as $i, explicitRoleArnOrThrow as $n, partitionSensitiveEnv as $o, malformedOutputsWarning as $r, noEchoParameterValueSeed as $t, resolveDynamoDbDeleteBudgetClock as A, printingCorpusOf as Aa, repairMalformedOrphanRecordsForReadOnly as Ai, ccProtectionRegistryTypes as An, rewriteTemplateAssetReferences as Ao, STATE_REGION_DIVERGED as Ar, expectedOwnerParam as As, ccRoutedFinalSnapshotError as At, getPriorAttempts as B, scrubResourceRecord as Ba, findSilentDropProperties as Bi, isTerminationProtectionPropagationError as Bn, validateContainerRepoName as Bo, isReadableResourceEntry as Br, nullPrototypeRecord as Bs, updatePartialMessage as Bt, perType as C, inheritNestedStackParameterAssociations as Ca, refuseMalformedResourcePropertiesForDrift as Ci, ProviderRegistry as Cn, AssetPublisher as Co, isStalePlaceholderArnAttribute as Cr, CFN_TEMPLATE_BODY_LIMIT as Cs, resolveReplacementOldType as Ct, beginDeleteWait as D, hasMaskableValues as Da, refuseMalformedResourcesForDestroy as Di, getCurrentResourceSecrets as Dn, buildAssetRedirectMap as Do, parameterInputsFor as Dr, uploadCfnTemplate as Ds, ATOMIC_FINAL_SNAPSHOT_TYPES as Dt, DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS as E, createUnionSecretMasker as Ea, refuseMalformedResourcesForDeploy as Ei, isCustomResourceType as En, WorkGraph as Eo, withSharedDrainBudget as Er, findLargeInlineResources as Es, inheritProducerRegions as Et, waitForIndexesSettled as F, errorCauseChain as Fa, repairMalformedResourcesForReadOnly as Fi, withIndeterminateGuard as Fn, getBootstrapMarkerKey as Fo, displayLogicalId as Fr, StageLoadError as Fs, unsupportedFinalSnapshotError as Ft, cyan as G, pathCrossesDottedKey as Ga, findReplacedReadersOfRecreateTargets as Gi, INTRINSIC_KEYS as Gn, describeDockerExecFailure as Go, malformedLocalResourceEntriesWarning as Gr, renderAssemblyPathEscape as Gs, WITHHELD_NAME_DISPLAY as Gt, formatResourceLine as H, DYNAMIC_REFERENCE_TOKEN_SCAN as Ha, withoutSilentDropProperties as Hi, reDisableInstanceApiTermination as Hn, buildDockerImage as Ho, malformedExportNamesWarning as Hr, absoluteAssemblyPathEscape as Hs, DeploymentEventsReader as Ht, ECR_AUTO_DELETE_IMAGES_TAG as I, maskSecretsInError as Ia, unpreviewableOrphanRecords as Ii, isWaitAbandonedError as In, isCrossRegionRedirect as Io, hasReadableOrphans as Ir, processStackMessages as Is, REDACTED_DELETE_ADDRESS_SKIP_REASON as It, red as J, provenPublicValue as Ja, isStatefulRecreateTargetForReplace as Ji, producerRegionsFromState as Jn, finchSecretArgvRefusal as Jo, malformedOrphanRecordsWarning as Jr, resolveBucketRegion as Js, secretBearing as Jt, gray as K, identityKeyFor as Ka, shownType as Ki, IntrinsicFunctionResolver as Kn, describeDockerFailure as Ko, malformedLocalResourcesWarning as Kr, resolveAssemblyPath as Ks, displayTextOrWithheld as Kt, S3_AUTO_DELETE_OBJECTS_TAG as L, maskSecretsInText as La, unreadableOrphanRecords as Li, markWaitAbandoned as Ln, parseBootstrapMarker as Lo, hasReadableOutputs as Lr, defineOwnKey as Ls, isRedactedRecordedValue as Lt, DELETE_INDEX_WAIT_PROCEED_NOTE as M, recordLogOnlyValue as Ma, repairMalformedOutputsForReadOnly as Mi, UNSPECIFIED_SKIP_REASON as Mn, BOOTSTRAP_MARKER_PREFIX as Mo, UNREADABLE_ORPHANS_CONTAINER_ROW as Mr, canonicalizeRegion as Ms, effectiveDeletionPolicy as Mt, INDEX_SETTLE_POLL_INTERVAL_MS as N, shareLogOnlyValues as Na, repairMalformedResourceEntriesForReadOnly as Ni, deleteIndeterminateGuards as Nn, assertAssetBucketRegion as No, UNREADABLE_RESOURCES_MAP_ROW as Nr, derivePartitionAndUrlSuffix as Ns, isFinalSnapshotError as Nt, compensateRemovedDeletionProtection as O, literalSplitDelimitersOf as Oa, refuseMalformedResourcesForExport as Oi, CloudControlProvider as On, createAssetRedirectResolver as Oo, passedParameterClassesOf as Or, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Os, PRE_DELETE_SNAPSHOT_TYPES as Ot, deleteTableWithIndexBusyRetry as P, createSecretMasker as Pa, repairMalformedResourcePropertiesForReadOnly as Pi, deleteSkipReason as Pn, ensureAssetStorage as Po, deployRefusesOrphanRowsReason as Pr, AssemblyReader as Ps, refusesFinalSnapshot as Pt, reachableDivergentParameters as Q, recordProvenPublicExpression as Qa, coerceWarmThroughput as Qi, assertSendableRoleArn as Qn, isMalformedEnvKey as Qo, malformedOutputsRefusalMessage as Qr, isWholeDynamicReferenceValue as Qt, hasCdkAutoDeleteTag as R, markSameGenerationBag as Ra, unreadableResourceEntries as Ri, disableInstanceApiTermination as Rn, readBootstrapMarkerBody as Ro, hasReadableResources as Rr, hasOwnKey as Rs, redactedDeleteAddressFields as Rt, isProtectionValueActive as S, liveMatchesUnresolvedTokenFrame as Sa, refuseMalformedResourceEntriesForOrphan as Si, CC_BROKEN_REASON as Sn, shouldRetainResource as So, cfnRefValueFromPhysicalId as Sr, ssmResolvedValueType as Ss, planRollback as St, DynamoDBGlobalTableProvider as T, carryLogOnlyValues as Ta, refuseMalformedResourcePropertiesForOrphan as Ti, wouldReturnToSdkProvider as Tn, stringifyValue as To, parseWebACLArn as Tr, MIGRATE_TMP_PREFIX as Ts, getCurrentProducerRegions as Tt, createReadOnlyAttributeHealerFactory as U, dynamicReferenceTokens as Ua, NO_CC_HANDLERS_REASON as Ui, reEnableInstanceApiTermination as Un, resolveDockerContextDirectory as Uo, malformedExportSourceWarning as Ur, describeFileReadFailure as Us, DeploymentEventsStore as Ut, markRefusedBeforeApplying as V, recoverMaskedOutput as Va, unwrittenCreateOnlyReplacement as Vi, observeThenDisableInstanceApiTermination as Vn, buildDenyExternalAccessPolicy as Vo, malformedDriftResourcePropertiesWarning as Vr, ownValue as Vs, updatePartialReason as Vt, bold as W, isUncertifiedBaselineMaskPosition as Wa, hasNoCloudControlHandlers as Wi, DiffCalculator as Wn, describeDockerCapturedOutput as Wo, malformedLocalOutputsWarning as Wr, displayAssemblyPath as Ws, extractDeploymentEventError as Wt, computeParameterTaint as X, recordMaskOnlyValuesIn as Xa, renderStatefulReason as Xi, applyRoleArnIfSet as Xn, getDockerCmd as Xo, malformedOrphansRefusalMessage as Xr, secretSafeKeyDisplay as Xt, yellow as Y, recordMaskOnlyValue as Ya, isStatefulRecreateTargetSync as Yi, regionLessSecretName as Yn, formatDockerLoginError as Yo, malformedOrphanRowsKeptWarning as Yr, secretBearingStateKeyWarning as Yt, namesAnyDeclaredParameter as Z, recordNoEchoAttributeValues as Za, WARM_THROUGHPUT_MEMBERS as Zi, assertFlagRoleArn as Zn, isDockerClientEnvKey as Zo, malformedOrphansWarning as Zr, exportNameSecretExposure as Zt, makeCanonicalizePropertiesFn as _, canonicalLambdaFunctionName as _a, refuseMalformedResourceEntries as _i, refuseMalformedDesiredTags as _n, importableOutputKeys as _o, carriesDynamicReference as _r, resolveStateBucketWithDefault as _s, destroyStackRecordsView as _t, findNestedTemplateTreeDefect as a, configIntegerRefusal as aa, malformedStateRefusalMessage as ai, DEFAULT_RESOURCE_WARN_AFTER_MS as an, isSingleDynamicReferenceToken as ao, envCredentialsAreAssumedRole as ar, escapeRegExp$2 as as, dropSettledNestedJournals as at, recordedProtectionNote as b, DagBuilder as ba, refuseMalformedResourceEntriesForImport as bi, definedAttributes as bn, orphansAfterRollback as bo, keepsSecretReferenceToken as br, stateBucketExistenceConfirmed as bs, recordAfterRollbackUpdate as bt, renderNestedTemplateTreeDefect as c, replayWarn as ca, refuseMalformedNestedChildOutputs as ci, earliestDefined as cn, S3StateBackend as co, s3BucketDomainName as cr, getDockerImageBySourceHash as cs, recordDisplacedSkips as ct, conditionInputsFrom as d, requireConfigString as da, refuseMalformedOrphans as di, withCreateTokenLedger as dn, acceptedCreateOnlyDropsOf as do, s3BucketWebsiteUrl as dr, getDefaultStateBucketName as ds, withNestedRevertRun as dt, toCfnInteger as ea, malformedRenderedContainersWarning as ei, collectDeclaredOutputNames as en, STATE_SOURCED_BASELINE_RULES as eo, isIamRoleArn as er, redactDockerArgvValues as es, makeSiblingClaimReader as et, parentSuppliedValues as f, prefetchCreateOnlyPropertyPaths as fa, refuseMalformedOrphansForDestroy as fi, acquireIdempotencyToken as fn, describeRegionValueKind as fo, coerceParameterTypedValue as fr, getLegacyStateBucketName as fs, getCurrentNestedStackContext as ft, mergeNoChangeOutputs as g, markAuxiliaryFailure as ga, refuseMalformedResourceAttributesForOrphan as gi, readTagList as gn, hasUnverifiableParameterRefusal as go, isImpossibleEmptyStoredAttribute as gr, resolveSkipPrefix as gs, awaitInFlightSibling as gt, keptWholeReasonText as h, withRetry as ha, refuseMalformedOutputsForDestroy as hi, planTagDiff as hn, hasReasonlessBaselineRefusal as ho, getAccountInfo as hr, resolveCaptureObservedState as hs, replayRollback as ht, childTemplateLoader as i, configBooleanRefusal as ia, malformedScrubResourceEntriesRefusalMessage as ii, DEFAULT_RESOURCE_TIMEOUT_MS as in, WHOLE_DYNAMIC_REFERENCE_PATTERN as io, callerEnvCredentials as ir, warnFinchArgvExposure as is, displacedPhysicalIdShown as it, resolveDynamoDbDeleteBudgetMs as j, recordLogOnlyParameterValue as ja, repairMalformedOrphansForReadOnly as ji, unchangedBehindSecretReference as jn, AssetModeResolver as jo, STATE_RESOURCES_MALFORMED as jr, PARTITION_TABLE as js, createPreDeleteFinalSnapshot as jt, deleteBudgetKey as k, logOnlyValueCount as ka, refuseMalformedState as ki, slowCcOperationTimeoutMs as kn, loadPublishableAssetManifest as ko, JSON_LISTING_HOLE_VALUE as kr, decodeListingKey as ks, buildFinalSnapshotIdentifier as kt, templateIdentity as l, requireConfigArray as la, refuseMalformedOrphanRecords as li, ledgerForStack as ln, rebuildClientForBucketRegion as lo, s3BucketDualStackDomainName as lr, Synthesizer as ls, revertNestedChildFromJournal as lt, isSecretBearingReferenceString as m, describeTypeWithThrottleRetry as ma, refuseMalformedOutputs as mi, DBPROXY_TAG_OPTIONS as mn, hasReadableExportSet as mo, parameterTypeMayLoseSecretIdentity as mr, resolveAutoAssetStorage as ms, replayFailedOperations as mt, IMPLICIT_DELETE_DEPENDENCIES as n, coerceCfnBoolean as na, malformedResourcePropertiesWarning as ni, isExportAliasCollision as nn, STATE_SOURCED_READBACK_RULES as no, AWS_CREDENTIAL_ENV_KEYS as nr, runDockerForeground as ns, NESTED_PENDING_PARENT_REASON as nt, isAbsoluteAssetPath as o, configStringRefusal as oa, previewableOrphanRecords as oi, maskingRetryLogger as on, LockManager as oo, isSettledInstanceState as or, stripControlChars as os, getNestedRevertRun as ot, readRecordedConditionVerdicts as p, templateResourceTypes as pa, refuseMalformedOrphansForOrphan as pi, DBPROXY_TAGS_WHAT as pn, exportNamesCarriedFrom as po, isUnboundTemplateParameter as pr, resolveApp as ps, withNestedStackContext as pt, green as q, carriesSecretMask as qa, MULTI_REGION_RECREATE_BLOCKED_TYPES as qi, classifyReplaySecretRegion as qn, dockerSpawnEnvWithSensitive as qo, malformedOrphanRecordsRefusalMessage as qr, clearBucketRegionCache as qs, exportAliasCollisionScrubWarning as qt, computeImplicitDeleteEdges as r, coerceCfnInteger as ra, malformedResourcesWarning as ri, bindingSkippedOutputs as rn, TEMPLATE_SOURCED_RULES as ro, applyCallerIdentityCredentials as rr, runDockerStreaming as rs, displacedOpClause as rt, listNestedTemplateRows as s, readConfigString as sa, refuseDivergentRecordRegionForDestroy as si, RollbackInlinePolicyWriters as sn, isLockInfoExpired as so, s3BucketArn as sr, AssetManifestLoader as ss, nestedChildStackName as st, DeployEngine as t, toFiniteNumber as ta, malformedResourceEntriesWarning as ti, collectPublishedOutputNames as tn, STATE_SOURCED_CROSS_GENERATION_RULES as to, sendableAssumeRoleCommand as tr, redactedDockerCause as ts, planOrphanAdoption as tt, conditionFingerprint as u, requireConfigObject as ua, refuseMalformedOrphanRecordsForDestroy as ui, reserveStackCreateToken as un, splitImportedOps as uo, s3BucketRegionalDomainName as ur, synthesisStatusMessage as us, revertedNestedRowIds as ut, withResourceDeadline as v, lambdaFunctionNameForMask as va, refuseMalformedResourceEntriesForDeploy as vi, tagMapAsList as vn, importableOutputs as vo, carriesFetchableDynamicReference as vr, resolveStateBucketWithDefaultAndSource as vs, getStackRecords as vt, readProtection as w, recordNestedStackParameterExpressions as wa, refuseMalformedResourcePropertiesForExport as wi, ccBrokenReason as wn, isSensitiveAttributeName as wo, refStateLookupFromResource as wr, CFN_TEMPLATE_URL_LIMIT as ws, findNestedStackTypeChanges as wt, PROTECTION_PROPERTY_BY_TYPE as x, TemplateParser as xa, refuseMalformedResourceEntriesForImportSave as xi, stringifyIfAssigned as xn, orphansCarriedFrom as xo, AWS_NO_VALUE as xr, warnDeprecatedNoPrefixCliFlag as xs, planFailedOps as xt, recordedProtectionEvidence as y, sameLambdaFunctionAddress as ya, refuseMalformedResourceEntriesForDestroy as yi, tagPlanWarning as yn, isReadableBag as yo, isNamelessDynamicReferenceError as yr, resolveUseCdkBootstrapAssets as ys, withStackRecords as yt, isTruthyCfnBoolean as z, redactSecretsForState as za, findActionableSilentDrops as zi, ec2InstanceProtectionSite as zn, validateAssetBucketName as zo, isReadableOrphanRecord as zr, hasPlainPrototype$1 as zs, redactedDeleteAddressSkip as zt };
36658
- //# sourceMappingURL=deploy-engine-CuI_h_Ml.js.map
37460
+ export { resourcesNamingDeclaredParameter as $, dynamicReferenceTokens as $a, withoutSilentDropProperties as $i, reEnableInstanceApiTermination as $n, resolveDockerContextDirectory as $o, malformedExportSourceWarning as $r, absoluteAssemblyPathEscape as $s, displayTextOrWithheld as $t, resolveDynamoDbDeleteBudgetClock as A, DagBuilder as Aa, refuseMalformedResourceEntriesForImport as Ai, definedAttributes as An, orphansAfterRollback as Ao, keepsSecretReferenceToken as Ar, resolveUseCdkBootstrapAssets as As, resolveReplacementOldType as At, getPriorAttempts as B, printingCorpusOf as Ba, refuseMalformedState as Bi, ccProtectionRegistryTypes as Bn, rewriteTemplateAssetReferences as Bo, STATE_REGION_DIVERGED as Br, decodeListingKey as Bs, isFinalSnapshotError as Bt, perType as C, prefetchCreateOnlyPropertyPaths as Ca, refuseMalformedOrphansForOrphan as Ci, DBPROXY_TAGS_WHAT as Cn, exportNamesCarriedFrom as Co, isUnboundTemplateParameter as Cr, loadCdkJson as Cs, getStackRecords as Ct, beginDeleteWait as D, canonicalLambdaFunctionName as Da, refuseMalformedResourceEntries as Di, refuseMalformedDesiredTags as Dn, importableOutputKeys as Do, carriesDynamicReference as Dr, resolveSkipPrefix as Ds, isJournaledOrphan as Dt, DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS as E, withRetry as Ea, refuseMalformedResourceAttributesForOrphan as Ei, readTagList as En, hasUnverifiableParameterRefusal as Eo, isImpossibleEmptyStoredAttribute as Er, resolveCaptureObservedState as Es, demoteSupersededOrphans as Et, waitForIndexesSettled as F, carryLogOnlyValues as Fa, refuseMalformedResourcePropertiesForExport as Fi, wouldReturnToSdkProvider as Fn, stringifyValue as Fo, parseWebACLArn as Fr, CFN_TEMPLATE_URL_LIMIT as Fs, PRE_DELETE_SNAPSHOT_TYPES as Ft, cyan as G, errorCauseChain as Ga, repairMalformedResourcePropertiesForReadOnly as Gi, withIndeterminateGuard as Gn, getBootstrapMarkerKey as Go, displayLogicalId as Gr, AssemblyReader as Gs, redactedDeleteAddressFields as Gt, formatResourceLine as H, recordLogOnlyValue as Ha, repairMalformedOrphansForReadOnly as Hi, UNSPECIFIED_SKIP_REASON as Hn, BOOTSTRAP_MARKER_PREFIX as Ho, UNREADABLE_ORPHANS_CONTAINER_ROW as Hr, PARTITION_TABLE as Hs, unsupportedFinalSnapshotError as Ht, ECR_AUTO_DELETE_IMAGES_TAG as I, createUnionSecretMasker as Ia, refuseMalformedResourcePropertiesForOrphan as Ii, isCustomResourceType as In, WorkGraph as Io, withSharedDrainBudget as Ir, MIGRATE_TMP_PREFIX as Is, buildFinalSnapshotIdentifier as It, red as J, markSameGenerationBag as Ja, unreadableOrphanRecords as Ji, disableInstanceApiTermination as Jn, readBootstrapMarkerBody as Jo, hasReadableResources as Jr, defineOwnKey as Js, updatePartialReason as Jt, gray as K, maskSecretsInError as Ka, repairMalformedResourcesForReadOnly as Ki, isWaitAbandonedError as Kn, isCrossRegionRedirect as Ko, hasReadableOrphans as Kr, StageLoadError as Ks, redactedDeleteAddressSkip as Kt, S3_AUTO_DELETE_OBJECTS_TAG as L, hasMaskableValues as La, refuseMalformedResourcesForDeploy as Li, getCurrentResourceSecrets as Ln, buildAssetRedirectMap as Lo, parameterInputsFor as Lr, findLargeInlineResources as Ls, ccRoutedFinalSnapshotError as Lt, DELETE_INDEX_WAIT_PROCEED_NOTE as M, liveMatchesUnresolvedTokenFrame as Ma, refuseMalformedResourceEntriesForOrphan as Mi, CC_BROKEN_REASON as Mn, shouldRetainResource as Mo, cfnRefValueFromPhysicalId as Mr, warnDeprecatedNoPrefixCliFlag as Ms, getCurrentProducerRegions as Mt, INDEX_SETTLE_POLL_INTERVAL_MS as N, inheritNestedStackParameterAssociations as Na, refuseMalformedResourcePropertiesForDestroy as Ni, ProviderRegistry as Nn, AssetPublisher as No, isStalePlaceholderArnAttribute as Nr, ssmResolvedValueType as Ns, inheritProducerRegions as Nt, compensateRemovedDeletionProtection as O, lambdaFunctionNameForMask as Oa, refuseMalformedResourceEntriesForDeploy as Oi, tagMapAsList as On, importableOutputs as Oo, carriesFetchableDynamicReference as Or, resolveStateBucketWithDefault as Os, planFailedOps as Ot, deleteTableWithIndexBusyRetry as P, recordNestedStackParameterExpressions as Pa, refuseMalformedResourcePropertiesForDrift as Pi, ccBrokenReason as Pn, isSensitiveAttributeName as Po, refStateLookupFromResource as Pr, CFN_TEMPLATE_BODY_LIMIT as Ps, ATOMIC_FINAL_SNAPSHOT_TYPES as Pt, reachableDivergentParameters as Q, DYNAMIC_REFERENCE_TOKEN_SCAN as Qa, unwrittenCreateOnlyReplacement as Qi, reDisableInstanceApiTermination as Qn, buildDockerImage as Qo, malformedExportNamesWarning as Qr, ownValue as Qs, WITHHELD_NAME_DISPLAY as Qt, hasCdkAutoDeleteTag as R, literalSplitDelimitersOf as Ra, refuseMalformedResourcesForDestroy as Ri, CloudControlProvider as Rn, createAssetRedirectResolver as Ro, passedParameterClassesOf as Rr, uploadCfnTemplate as Rs, createPreDeleteFinalSnapshot as Rt, isProtectionValueActive as S, requireConfigString as Sa, refuseMalformedOrphansForDestroy as Si, acquireIdempotencyToken as Sn, describeRegionValueKind as So, coerceParameterTypedValue as Sr, getLegacyStateBucketName as Ss, destroyStackRecordsView as St, DynamoDBGlobalTableProvider as T, describeTypeWithThrottleRetry as Ta, refuseMalformedOutputsForDestroy as Ti, planTagDiff as Tn, hasReasonlessBaselineRefusal as To, getAccountInfo as Tr, resolveAutoAssetStorage as Ts, recordAfterRollbackUpdate as Tt, createReadOnlyAttributeHealerFactory as U, shareLogOnlyValues as Ua, repairMalformedOutputsForReadOnly as Ui, deleteIndeterminateGuards as Un, assertAssetBucketRegion as Uo, UNREADABLE_RESOURCES_MAP_ROW as Ur, canonicalizeRegion as Us, REDACTED_DELETE_ADDRESS_SKIP_REASON as Ut, markRefusedBeforeApplying as V, recordLogOnlyParameterValue as Va, repairMalformedOrphanRecordsForReadOnly as Vi, unchangedBehindSecretReference as Vn, AssetModeResolver as Vo, STATE_RESOURCES_MALFORMED as Vr, expectedOwnerParam as Vs, refusesFinalSnapshot as Vt, bold as W, createSecretMasker as Wa, repairMalformedResourceEntriesForReadOnly as Wi, deleteSkipReason as Wn, ensureAssetStorage as Wo, deployRefusesOrphanRowsReason as Wr, derivePartitionAndUrlSuffix as Ws, isRedactedRecordedValue as Wt, computeParameterTaint as X, scrubResourceRecord as Xa, findActionableSilentDrops as Xi, isTerminationProtectionPropagationError as Xn, validateContainerRepoName as Xo, isReadableResourceEntry as Xr, hasPlainPrototype$1 as Xs, DeploymentEventsStore as Xt, yellow as Y, redactSecretsForState as Ya, unreadableResourceEntries as Yi, ec2InstanceProtectionSite as Yn, validateAssetBucketName as Yo, isReadableOrphanRecord as Yr, hasOwnKey as Ys, DeploymentEventsReader as Yt, namesAnyDeclaredParameter as Z, recoverMaskedOutput as Za, findSilentDropProperties as Zi, observeThenDisableInstanceApiTermination as Zn, buildDenyExternalAccessPolicy as Zo, malformedDriftResourcePropertiesWarning as Zr, nullPrototypeRecord as Zs, extractDeploymentEventError as Zt, makeCanonicalizePropertiesFn as _, configStringRefusal as _a, refuseDivergentRecordRegionForDestroy as _i, RollbackInlinePolicyWriters as _n, isLockInfoExpired as _o, s3BucketArn as _r, AssetManifestLoader as _s, getCurrentNestedStackContext as _t, findNestedTemplateTreeDefect as a, isStatefulRecreateTargetForReplace as aa, resolveBucketRegion as ac, malformedOrphanRowsKeptWarning as ai, isWholeDynamicReferenceValue as an, recordMaskOnlyValue as ao, regionLessSecretName as ar, formatDockerLoginError as as, makeSiblingClaimReader as at, recordedProtectionNote as b, requireConfigArray as ba, refuseMalformedOrphanRecordsForDestroy as bi, reserveStackCreateToken as bn, splitImportedOps as bo, s3BucketRegionalDomainName as br, synthesisStatusMessage as bs, replayRollback as bt, renderNestedTemplateTreeDefect as c, WARM_THROUGHPUT_MEMBERS as ca, malformedOutputsRefusalMessage as ci, collectPublishedOutputNames as cn, recordProvenPublicExpression as co, assertSendableRoleArn as cr, isMalformedEnvKey as cs, displacedOpClause as ct, conditionInputsFrom as d, toCfnInteger as da, malformedResourceEntriesWarning as di, DEFAULT_RESOURCE_TIMEOUT_MS as dn, STATE_SOURCED_CROSS_GENERATION_RULES as do, sendableAssumeRoleCommand as dr, redactedDockerCause as ds, getNestedRevertRun as dt, NO_CC_HANDLERS_REASON as ea, describeFileReadFailure as ec, malformedLocalOutputsWarning as ei, exportAliasCollisionScrubWarning as en, isUncertifiedBaselineMaskPosition as eo, DiffCalculator as er, describeDockerCapturedOutput as es, deleteJournaledOrphans as et, parentSuppliedValues as f, toFiniteNumber as fa, malformedResourcePropertiesWarning as fi, DEFAULT_RESOURCE_WARN_AFTER_MS as fn, STATE_SOURCED_READBACK_RULES as fo, AWS_CREDENTIAL_ENV_KEYS as fr, runDockerForeground as fs, nestedChildStackName as ft, mergeNoChangeOutputs as g, configIntegerRefusal as ga, previewableOrphanRecords as gi, formatDestructiveChange as gn, LockManager as go, isSettledInstanceState as gr, stripControlChars as gs, withNestedRevertRun as gt, keptWholeReasonText as h, configBooleanRefusal as ha, malformedStateRefusalMessage as hi, findDestructiveChanges as hn, isSingleDynamicReferenceToken as ho, envCredentialsAreAssumedRole as hr, escapeRegExp$3 as hs, revertedNestedRowIds as ht, childTemplateLoader as i, MULTI_REGION_RECREATE_BLOCKED_TYPES as ia, clearBucketRegionCache as ic, malformedOrphanRecordsWarning as ii, exportNameSecretExposure as in, provenPublicValue as io, producerRegionsFromState as ir, finchSecretArgvRefusal as is, sameJournaledOrphans as it, resolveDynamoDbDeleteBudgetMs as j, TemplateParser as ja, refuseMalformedResourceEntriesForImportSave as ji, stringifyIfAssigned as jn, orphansCarriedFrom as jo, AWS_NO_VALUE as jr, stateBucketExistenceConfirmed as js, findNestedStackTypeChanges as jt, deleteBudgetKey as k, sameLambdaFunctionAddress as ka, refuseMalformedResourceEntriesForDestroy as ki, tagPlanWarning as kn, isReadableBag as ko, isNamelessDynamicReferenceError as kr, resolveStateBucketWithDefaultAndSource as ks, planRollback as kt, templateIdentity as l, coerceWarmThroughput as la, malformedOutputsWarning as li, isExportAliasCollision as ln, wholeStringLeavesOf as lo, explicitRoleArnOrThrow as lr, partitionSensitiveEnv as ls, displacedPhysicalIdShown as lt, isSecretBearingReferenceString as m, coerceCfnInteger as ma, malformedScrubResourceEntriesRefusalMessage as mi, constructPathOf as mn, WHOLE_DYNAMIC_REFERENCE_PATTERN as mo, callerEnvCredentials as mr, warnFinchArgvExposure as ms, revertNestedChildFromJournal as mt, IMPLICIT_DELETE_DEPENDENCIES as n, findReplacedReadersOfRecreateTargets as na, renderAssemblyPathEscape as nc, malformedLocalResourcesWarning as ni, secretBearingStateKeyWarning as nn, identityKeyFor as no, IntrinsicFunctionResolver as nr, describeDockerFailure as ns, journaledOrphanLines as nt, isAbsoluteAssetPath as o, isStatefulRecreateTargetSync as oa, malformedOrphansRefusalMessage as oi, noEchoParameterValueSeed as on, recordMaskOnlyValuesIn as oo, applyRoleArnIfSet as or, getDockerCmd as os, planOrphanAdoption as ot, readRecordedConditionVerdicts as p, coerceCfnBoolean as pa, malformedResourcesWarning as pi, maskingRetryLogger as pn, TEMPLATE_SOURCED_RULES as po, applyCallerIdentityCredentials as pr, runDockerStreaming as ps, recordDisplacedSkips as pt, green as q, maskSecretsInText as qa, unpreviewableOrphanRecords as qi, markWaitAbandoned as qn, parseBootstrapMarker as qo, hasReadableOutputs as qr, processStackMessages as qs, updatePartialMessage as qt, computeImplicitDeleteEdges as r, shownType as ra, resolveAssemblyPath as rc, malformedOrphanRecordsRefusalMessage as ri, secretSafeKeyDisplay as rn, carriesSecretMask as ro, classifyReplaySecretRegion as rr, dockerSpawnEnvWithSensitive as rs, loadJournaledOrphans as rt, listNestedTemplateRows as s, renderStatefulReason as sa, malformedOrphansWarning as si, collectDeclaredOutputNames as sn, recordNoEchoAttributeValues as so, assertFlagRoleArn as sr, isDockerClientEnvKey as ss, NESTED_PENDING_PARENT_REASON as st, DeployEngine as t, hasNoCloudControlHandlers as ta, displayAssemblyPath as tc, malformedLocalResourceEntriesWarning as ti, secretBearing as tn, pathCrossesDottedKey as to, INTRINSIC_KEYS as tr, describeDockerExecFailure as ts, isHandledOrphan as tt, conditionFingerprint as u, isWarmThroughputDecrease as ua, malformedRenderedContainersWarning as ui, bindingSkippedOutputs as un, STATE_SOURCED_BASELINE_RULES as uo, isIamRoleArn as ur, redactDockerArgvValues as us, dropSettledNestedJournals as ut, withResourceDeadline as v, readConfigString as va, refuseMalformedNestedChildOutputs as vi, earliestDefined as vn, S3StateBackend as vo, s3BucketDomainName as vr, getDockerImageBySourceHash as vs, withNestedStackContext as vt, readProtection as w, templateResourceTypes as wa, refuseMalformedOutputs as wi, DBPROXY_TAG_OPTIONS as wn, hasReadableExportSet as wo, parameterTypeMayLoseSecretIdentity as wr, resolveApp as ws, withStackRecords as wt, PROTECTION_PROPERTY_BY_TYPE as x, requireConfigObject as xa, refuseMalformedOrphans as xi, withCreateTokenLedger as xn, acceptedCreateOnlyDropsOf as xo, s3BucketWebsiteUrl as xr, getDefaultStateBucketName as xs, awaitInFlightSibling as xt, recordedProtectionEvidence as y, replayWarn as ya, refuseMalformedOrphanRecords as yi, ledgerForStack as yn, rebuildClientForBucketRegion as yo, s3BucketDualStackDomainName as yr, Synthesizer as ys, replayFailedOperations as yt, isTruthyCfnBoolean as z, logOnlyValueCount as za, refuseMalformedResourcesForExport as zi, slowCcOperationTimeoutMs as zn, loadPublishableAssetManifest as zo, JSON_LISTING_HOLE_VALUE as zr, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as zs, effectiveDeletionPolicy as zt };
37461
+ //# sourceMappingURL=deploy-engine-D7204v7Z.js.map