@go-to-k/cdkd 0.301.15 → 0.302.1

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 (34) hide show
  1. package/dist/{asg-provider-ClDYCUia.js → asg-provider-BpRcm_UB.js} +5 -5
  2. package/dist/asg-provider-BpRcm_UB.js.map +1 -0
  3. package/dist/{aws-failure-text-CdgZn80O.js → aws-failure-text-e_ddrDDo.js} +313 -35
  4. package/dist/aws-failure-text-e_ddrDDo.js.map +1 -0
  5. package/dist/cli.js +2 -2
  6. package/dist/{deploy-engine-Dy7N7-Il.js → deploy-engine-CYVt2NQR.js} +2189 -1104
  7. package/dist/deploy-engine-CYVt2NQR.js.map +1 -0
  8. package/dist/{iam-policy-targets-Dap4bLyq.js → iam-policy-targets-CmHNH1DY.js} +17 -4
  9. package/dist/iam-policy-targets-CmHNH1DY.js.map +1 -0
  10. package/dist/{iam-role-provider-C8Icvrf7.js → iam-role-provider-R1GC6KcT.js} +14 -5
  11. package/dist/iam-role-provider-R1GC6KcT.js.map +1 -0
  12. package/dist/index.d.ts +32 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +4 -4
  15. package/dist/{neptune-provider-CtnwSAGM.js → neptune-provider-DzTl6-74.js} +7 -16
  16. package/dist/neptune-provider-DzTl6-74.js.map +1 -0
  17. package/dist/{program-CXvao3wc.js → program-CvRs5pV_.js} +172 -30
  18. package/dist/program-CvRs5pV_.js.map +1 -0
  19. package/dist/{provider-classes-CBbWGsK0.js → provider-classes-B2_u4Fg0.js} +268 -150
  20. package/dist/provider-classes-B2_u4Fg0.js.map +1 -0
  21. package/dist/{rds-provider-BMQvQTTS.js → rds-provider-CmKbYAvr.js} +7 -16
  22. package/dist/rds-provider-CmKbYAvr.js.map +1 -0
  23. package/dist/{version-DadUoJjK.js → version-CaejipV3.js} +2 -2
  24. package/dist/{version-DadUoJjK.js.map → version-CaejipV3.js.map} +1 -1
  25. package/package.json +1 -1
  26. package/dist/asg-provider-ClDYCUia.js.map +0 -1
  27. package/dist/aws-failure-text-CdgZn80O.js.map +0 -1
  28. package/dist/deploy-engine-Dy7N7-Il.js.map +0 -1
  29. package/dist/iam-policy-targets-Dap4bLyq.js.map +0 -1
  30. package/dist/iam-role-provider-C8Icvrf7.js.map +0 -1
  31. package/dist/neptune-provider-CtnwSAGM.js.map +0 -1
  32. package/dist/program-CXvao3wc.js.map +0 -1
  33. package/dist/provider-classes-CBbWGsK0.js.map +0 -1
  34. package/dist/rds-provider-BMQvQTTS.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { r as getAwsClients, s as __exportAll } from "./aws-clients-B_CEQGE1.js";
2
- import { A as SECRET_REF_MAX_CODE_POINTS, C as getCurrentSkipPrefix, D as withStackName, E as withSkipPrefix, F as displayAwsMessage, G as truncateCodePoints, H as safeMsg, I as displayIdent, L as displaySafe, M as UNRENDERABLE, N as asIsMessage, O as withoutGeneratedFallbackName, R as displayStackName, S as generateResourceNameWithFallback, T as looksLikeCdkdGeneratedName, V as plainIdentOr, W as terminalSafe, a as safeStringify$1, b as explicitNamePropertyFor, c as isStdoutReservedForPayload, f as currentLogLineMasker, j as STACK_REF_MAX_CODE_POINTS, k as SAFE_MSG_ALTERED_CHAR, m as getLiveRenderer, n as describeAwsFailure, p as installLogLineMaskerSource, s as getLogger, t as VERBOSE_POINTER, v as applyDefaultNameForFallback, w as getCurrentStackName, x as generateResourceName, z as isPasteableIdent } from "./aws-failure-text-CdgZn80O.js";
3
- import { $ as protectedReplacementAdvice, $n as markReplayMayCollide, $t as ambientCredentialConfig, A as isQuotableImportValue, An as SHELL_ACTIVE, At as isAuxiliaryMarkOf, B as isTerminalDeleteFailure, Bn as shellBoundedDisplay, Bt as formatLockOperation, Cn as StateError, Ct as RETRY_AUXILIARY_OWNER, Dn as normalizeAwsError, Dt as createdResourceIdentityBeforeFailure, E as assertRegionMatch, Et as createdBeforeFailure, F as resolveExplicitPhysicalId, Fn as isInertUnquoted, Ft as UNREPRODUCIBLE_LOCK_CLAUSE, G as runDeleteAttempt, Gn as isUpdateUnsupportedError, Gt as DEFAULT_STATE_PREFIX, H as observeThenDisableProtection, Hn as withheldTargetClause, Ht as recoveryCommandFlags, In as pasteableCommand, It as buildForceUnlockCommand, J as DELETION_PROTECTION_DOC_POINTER, Jn as isNameCollisionErrorFrom, Kn as hasReplayMayCollide, Kt as injectiveKey, L as ProtectionFlipRegistry, Ln as physicalIdShownBesideCommand, M as quotedRemedyLogicalId, Mn as UNSHOWABLE_VALUE, Mt as markCreatedBeforeFailure, N as refusalTypeShown, Nn as commandHole, Ot as hasCreatedBeforeFailure, Pt as replayMadeAnotherResource, Q as pasteableAwsCommand, Qt as ambientClientDefaults, R as compensateProtectionFlip, Rn as plainOrDescribed, St as withDerivedNameMasks, T as RESOURCE_NOT_FOUND, U as protectionFlipKey, Ut as sanitizeRecoveryValue, Vn as shellQuote, Vt as formatLockOwner, Wt as CUSTOM_RESOURCE_RESPONSE_PREFIX, Xn as isRecreateRetryableError, Yn as isNameCooldownError, Yt as producerRecordKey, Zn as isReplayedNameCollisionFrom, Zt as withPasteableAwsProfile, _r as markRedactedCause, _t as createMaskedLogSinks, ar as formatRetryClassificationSignals, at as startInterruptWatch, bn as ResourceUpdateNotSupportedError, bt as maskDeep, ct as canonicalizeRoute53QueryName, d as prepareRemovalForUpdate, dr as isTransientServerError, en as clientDefaultsFor, er as relayedNameCollision, f as removalWarning, fr as retryClassificationText, ft as logicalIdShown, gr as markNonRetryable, gt as stateOrphanRecordRemedy, hn as MalformedProducerRecordRefusalError, hr as isThrottlingError, ht as segmentAfterAnchor, i as holdsSecretDerivedEntry, in as CdkdError, it as isInterruptedWaitError, j as normalizeAwsTagsToCfn, jn as SHELL_ACTIVE_WHY, jt as markAuxiliaryFailure, kt as isAuxiliaryFailure, l as withUnchangedSecretPrincipalLists, ln as DynamicReferenceRegionAmbiguousError, lr as isIamPropagationError, m as withoutInjectedRemovals, mn as MacroExpansionError, mr as isMarkedNonRetryable, mt as resourceTypeShown, nn as AccountIdUnavailableError, nr as stripControlChars, on as CrossAccountSecretRefusalError, or as isAmbiguousCcHandlerErrorCode, ot as ElapsedBudgetRegistry, pn as LockError, rn as AssetError, sn as DependencyError, sr as isAmbiguousOutcomeError, st as monotonicNowMs, tn as credentialFingerprint, tr as escapeRegExp$3, un as IntrinsicResolutionRefusalError, ur as isRetryableTransientError, ut as compositeIdSeparatorRefusal, vn as ProvisioningError, vr as wrapMaskedAwsError, vt as createMaskedRetryLogger, w as withoutServerErrorRetries, wn as SynthesisError, wt as carryCreatedBeforeFailure, xt as maskerOrIdentity, yn as ResourceTimeoutError, yt as isSecretDerivedValue, z as deleteWithProtectionCompensation, zn as quotedOrDescribed, zt as formatLockExpiry } from "./iam-policy-targets-Dap4bLyq.js";
2
+ import { A as isNameGeneration, B as SECRET_REF_MAX_CODE_POINTS, D as getCurrentStackInstanceId, E as getCurrentSkipPrefix, F as withSkipPrefix, G as displayAwsMessage, H as UNRENDERABLE, I as withStackScope, J as displayStackName, K as displayIdent, L as withStackScopeBeforeStateRead, N as stackInstanceNameSuffix, O as getCurrentStackName, P as withNameGeneration, Q as safeMsg, R as withoutGeneratedFallbackName, S as fallbackNamePropertyFor, T as generatedNameFor, U as asIsMessage, V as STACK_REF_MAX_CODE_POINTS, Y as isPasteableIdent, Z as plainIdentOr, a as safeStringify$1, c as isStdoutReservedForPayload, et as terminalSafe, f as currentLogLineMasker, j as looksLikeCdkdGeneratedName, m as getLiveRenderer, n as describeAwsFailure, p as installLogLineMaskerSource, q as displaySafe, s as getLogger, t as VERBOSE_POINTER, tt as truncateCodePoints, v as applyDefaultNameForFallback, w as generateResourceNameWithFallback, x as explicitNamePropertyFor, y as currentNameGeneration, z as SAFE_MSG_ALTERED_CHAR } from "./aws-failure-text-e_ddrDDo.js";
3
+ import { $ as protectedReplacementAdvice, $n as isReplayedNameCollisionFrom, $t as ambientCredentialConfig, A as isQuotableImportValue, At as isAuxiliaryMarkOf, B as isTerminalDeleteFailure, Bn as plainOrDescribed, Bt as formatLockOperation, Ct as RETRY_AUXILIARY_OWNER, Dt as createdResourceIdentityBeforeFailure, E as assertRegionMatch, Et as createdBeforeFailure, F as resolveExplicitPhysicalId, Fn as commandHole, Ft as UNREPRODUCIBLE_LOCK_CLAUSE, G as runDeleteAttempt, Gt as DEFAULT_STATE_PREFIX, H as observeThenDisableProtection, Hn as shellBoundedDisplay, Ht as recoveryCommandFlags, It as buildForceUnlockCommand, J as DELETION_PROTECTION_DOC_POINTER, Jn as hasReplayMayCollide, Kt as injectiveKey, L as ProtectionFlipRegistry, Ln as isInertUnquoted, M as quotedRemedyLogicalId, Mn as SHELL_ACTIVE, Mt as markCreatedBeforeFailure, N as refusalTypeShown, Nn as SHELL_ACTIVE_WHY, On as isSoftAbort, Ot as hasCreatedBeforeFailure, Pn as UNSHOWABLE_VALUE, Pt as replayMadeAnotherResource, Q as pasteableAwsCommand, Qn as isRecreateRetryableError, Qt as ambientClientDefaults, R as compensateProtectionFlip, Rn as pasteableCommand, St as withDerivedNameMasks, T as RESOURCE_NOT_FOUND, Tn as SynthesisError, U as protectionFlipKey, Un as shellQuote, Ut as sanitizeRecoveryValue, Vn as quotedOrDescribed, Vt as formatLockOwner, Wn as withheldTargetClause, Wt as CUSTOM_RESOURCE_RESPONSE_PREFIX, Xn as isNameCollisionErrorFrom, Yt as producerRecordKey, Zn as isNameCooldownError, Zt as withPasteableAwsProfile, _r as isThrottlingError, _t as createMaskedLogSinks, at as startInterruptWatch, bn as ResourceTimeoutError, br as wrapMaskedAwsError, bt as maskDeep, cr as isAmbiguousCcHandlerErrorCode, ct as canonicalizeRoute53QueryName, d as prepareRemovalForUpdate, dn as IntrinsicResolutionRefusalError, dr as isIamPropagationError, en as clientDefaultsFor, f as removalWarning, fr as isRetryableTransientError, ft as logicalIdShown, gn as MalformedProducerRecordRefusalError, gr as isMarkedNonRetryable, gt as stateOrphanRecordRemedy, hn as MacroExpansionError, ht as segmentAfterAnchor, i as holdsSecretDerivedEntry, in as CdkdError, ir as stripControlChars, it as isInterruptedWaitError, j as normalizeAwsTagsToCfn, jt as markAuxiliaryFailure, kn as normalizeAwsError, kt as isAuxiliaryFailure, l as withUnchangedSecretPrincipalLists, ln as DeploymentNotApprovedError, lr as isAmbiguousOutcomeError, m as withoutInjectedRemovals, mn as LockError, mr as retryClassificationText, mt as resourceTypeShown, nn as AccountIdUnavailableError, nr as relayedNameCollision, on as CrossAccountSecretRefusalError, ot as ElapsedBudgetRegistry, pr as isTransientServerError, qn as isUpdateUnsupportedError, rn as AssetError, rr as escapeRegExp$3, sn as DependencyError, sr as formatRetryClassificationSignals, st as monotonicNowMs, tn as credentialFingerprint, tr as markReplayMayCollide, un as DynamicReferenceRegionAmbiguousError, ut as compositeIdSeparatorRefusal, vr as markNonRetryable, vt as createMaskedRetryLogger, w as withoutServerErrorRetries, wn as StateError, wt as carryCreatedBeforeFailure, xn as ResourceUpdateNotSupportedError, xt as maskerOrIdentity, yn as ProvisioningError, yr as markRedactedCause, yt as isSecretDerivedValue, z as deleteWithProtectionCompensation, zn as physicalIdShownBesideCommand, zt as formatLockExpiry } from "./iam-policy-targets-CmHNH1DY.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-DBXBcg0J.js";
5
- import { t as getCdkdVersion } from "./version-DadUoJjK.js";
5
+ import { t as getCdkdVersion } from "./version-CaejipV3.js";
6
6
  import { createHash, randomUUID } from "node:crypto";
7
7
  import { AsyncLocalStorage } from "node:async_hooks";
8
8
  import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketLifecycleConfigurationCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -4577,7 +4577,8 @@ var WorkGraph = class {
4577
4577
  nodeId: node.id,
4578
4578
  error
4579
4579
  });
4580
- this.logger.error(`Failed: ${node.id}: ${describeAwsFailure(error).detail}`);
4580
+ if (isSoftAbort(error)) this.logger.debug(safeMsg`Not approved: ${node.id}`);
4581
+ else this.logger.error(`Failed: ${node.id}: ${describeAwsFailure(error).detail}`);
4581
4582
  }).finally(() => {
4582
4583
  active[node.type]--;
4583
4584
  dispatch();
@@ -4591,6 +4592,12 @@ var WorkGraph = class {
4591
4592
  }
4592
4593
  if (errors.length > 0) {
4593
4594
  const skippedCount = [...this.nodes.values()].filter((n) => n.state === "skipped").length;
4595
+ if (errors.every((e) => isSoftAbort(e.error))) {
4596
+ const lines = errors.map((e) => e.error.message);
4597
+ if (skippedCount > 0) lines.push(`${skippedCount} node(s) depending on a declined stack were skipped.`);
4598
+ reject(errors.length === 1 && skippedCount === 0 ? errors[0].error : new DeploymentNotApprovedError(lines.join("\n")));
4599
+ return;
4600
+ }
4594
4601
  const msg = errors.map((e) => ` - ${e.nodeId}: ${describeAwsFailure(e.error).detail}`).join("\n");
4595
4602
  reject(/* @__PURE__ */ new Error(`${errors.length} node(s) failed${skippedCount > 0 ? `, ${skippedCount} skipped` : ""}:\n${msg}`));
4596
4603
  return;
@@ -4803,7 +4810,8 @@ const STATE_SCHEMA_VERSIONS_READABLE = [
4803
4810
  9,
4804
4811
  10,
4805
4812
  11,
4806
- 12
4813
+ 12,
4814
+ 13
4807
4815
  ];
4808
4816
  function hasUnverifiableParameterRefusal(record) {
4809
4817
  return record?.observedBaselineRefused === true && record.observedBaselineRefusalReason === "unverifiable-parameter";
@@ -4851,6 +4859,13 @@ function importableOutputs(state) {
4851
4859
  function exportNamesCarriedFrom(previous) {
4852
4860
  return previous.exportNames === void 0 ? {} : { exportNames: previous.exportNames };
4853
4861
  }
4862
+ function stackInstanceIdCarriedFrom(previous) {
4863
+ return {
4864
+ ...previous?.stackInstanceId !== void 0 && { stackInstanceId: previous.stackInstanceId },
4865
+ ...previous?.retiredNameGenerations !== void 0 && { retiredNameGenerations: previous.retiredNameGenerations },
4866
+ ...previous?.supersededResources !== void 0 && { supersededResources: previous.supersededResources }
4867
+ };
4868
+ }
4854
4869
  function skippedOutputsCarriedFrom(previous) {
4855
4870
  return previous.skippedOutputs === void 0 ? {} : { skippedOutputs: previous.skippedOutputs };
4856
4871
  }
@@ -4942,6 +4957,7 @@ function refuseMalformedOperation(shownStack, where, op) {
4942
4957
  if (o["changeType"] !== "UPDATE") refuseMalformed(shownStack, `${where}.replacementOrphaned is only valid on an UPDATE.`);
4943
4958
  }
4944
4959
  if (o["wasReplaced"] !== void 0 && typeof o["wasReplaced"] !== "boolean") fail("wasReplaced", o["wasReplaced"], "a boolean when present");
4960
+ if (o["nameGeneration"] !== void 0 && !(typeof o["nameGeneration"] === "number" && Number.isSafeInteger(o["nameGeneration"]) && o["nameGeneration"] >= 0)) fail("nameGeneration", o["nameGeneration"], "a non-negative integer when present");
4945
4961
  if (o["oldDeletedBeforeCreate"] !== void 0 && typeof o["oldDeletedBeforeCreate"] !== "boolean") fail("oldDeletedBeforeCreate", o["oldDeletedBeforeCreate"], "a boolean when present");
4946
4962
  if (o["oldResourceMaySurvive"] !== void 0 && !["kept", "unconfirmed"].includes(o["oldResourceMaySurvive"])) fail("oldResourceMaySurvive", o["oldResourceMaySurvive"], "kept or unconfirmed when present");
4947
4963
  if (o["physicalIdRecoveredFromError"] !== void 0 && typeof o["physicalIdRecoveredFromError"] !== "boolean") fail("physicalIdRecoveredFromError", o["physicalIdRecoveredFromError"], "a boolean when present");
@@ -5099,6 +5115,53 @@ async function notifyStateSaved(stackName, region) {
5099
5115
  await stateSavedObserver.getStore()?.(stackName, region);
5100
5116
  }
5101
5117
 
5118
+ //#endregion
5119
+ //#region src/state/stack-instance-id.ts
5120
+ const STACK_INSTANCE_SIBLING_VERSION = 1;
5121
+ const STACK_INSTANCE_SIBLING_FILE = "stack-instance.json";
5122
+ const STACK_INSTANCE_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
5123
+ function isStackInstanceId(value) {
5124
+ return typeof value === "string" && STACK_INSTANCE_ID_PATTERN.test(value);
5125
+ }
5126
+ function stackInstanceSiblingBody(id) {
5127
+ return JSON.stringify({
5128
+ stackInstanceVersion: 1,
5129
+ stackInstanceId: id
5130
+ }, null, 2);
5131
+ }
5132
+ function parseStackInstanceSibling(body) {
5133
+ let raw;
5134
+ try {
5135
+ raw = JSON.parse(body);
5136
+ } catch {
5137
+ return null;
5138
+ }
5139
+ if (typeof raw !== "object" || raw === null) return null;
5140
+ const doc = raw;
5141
+ if (doc["stackInstanceVersion"] !== 1) return null;
5142
+ return isStackInstanceId(doc["stackInstanceId"]) ? doc["stackInstanceId"] : null;
5143
+ }
5144
+ var MalformedStackInstanceIdError = class extends Error {
5145
+ constructor(stackLabel) {
5146
+ super(`The state record of ${stackLabel} holds a stackInstanceId that is not a well-formed id. cdkd does not guess which lifetime of the stack it names, since names derive from it; restore the field from an earlier version of state.json.`);
5147
+ this.name = "MalformedStackInstanceIdError";
5148
+ }
5149
+ };
5150
+ function stackInstanceIdOf(state, stackLabel) {
5151
+ const value = state?.stackInstanceId;
5152
+ if (value === void 0) return void 0;
5153
+ if (!isStackInstanceId(value)) throw new MalformedStackInstanceIdError(stackLabel);
5154
+ return value;
5155
+ }
5156
+ const STACK_INSTANCE_ID_WITHHELD = "<withheld>";
5157
+ function withStackInstanceIdWithheld(state) {
5158
+ if (state === null || typeof state !== "object" || state.stackInstanceId === void 0) return state;
5159
+ return {
5160
+ ...state,
5161
+ stackInstanceId: STACK_INSTANCE_ID_WITHHELD
5162
+ };
5163
+ }
5164
+
5102
5165
  //#endregion
5103
5166
  //#region src/state/cross-prefix-stack-scan.ts
5104
5167
  function journalCanOwnResources(journal) {
@@ -5110,6 +5173,8 @@ function recordCanOwnResources(record, journal) {
5110
5173
  if (Object.keys(resources).length > 0) return true;
5111
5174
  const orphans = record.orphans;
5112
5175
  if (orphans !== void 0 && (!Array.isArray(orphans) || orphans.length > 0)) return true;
5176
+ const superseded = record.supersededResources;
5177
+ if (superseded !== void 0 && (!Array.isArray(superseded) || superseded.length > 0)) return true;
5113
5178
  if (journal === null) return false;
5114
5179
  const segments = journal.segments;
5115
5180
  if (!Array.isArray(segments)) return true;
@@ -5616,6 +5681,7 @@ function legacyProbeBelongsTo(probe, region) {
5616
5681
  }
5617
5682
  const KEPT_AT_CLOCK_TOLERANCE_MS = 2e3;
5618
5683
  const ROLLBACK_JOURNAL_OBJECT_DESCRIPTION = "the rollback journal, whose `failedOperations[].attemptedProperties` records the properties of the failed write verbatim";
5684
+ const STACK_INSTANCE_OBJECT_DESCRIPTION = "the write-ahead stack instance id, from which a stack's generated resource names derive";
5619
5685
  const STATE_RECORD_OBJECT_DESCRIPTION = "the stack's state record, which carries every resource's recorded properties and attributes";
5620
5686
  var S3StateBackend = class S3StateBackend {
5621
5687
  logger = getLogger().child("S3StateBackend");
@@ -5627,6 +5693,7 @@ var S3StateBackend = class S3StateBackend {
5627
5693
  resolveInFlight = null;
5628
5694
  journalsReadAbsent = /* @__PURE__ */ new Set();
5629
5695
  journalTouches = /* @__PURE__ */ new Map();
5696
+ pendingStackInstanceSiblings = /* @__PURE__ */ new Map();
5630
5697
  constructor(s3Client, config, clientOpts = {}) {
5631
5698
  this.s3Client = s3Client;
5632
5699
  this.config = config;
@@ -5684,6 +5751,9 @@ var S3StateBackend = class S3StateBackend {
5684
5751
  async purgeRollbackJournalVersions(stackName, region) {
5685
5752
  await this.purgeNoncurrentVersions([this.rollbackJournalKey(stackName, region)], { objectDescription: ROLLBACK_JOURNAL_OBJECT_DESCRIPTION });
5686
5753
  }
5754
+ getStackInstanceKey(stackName, region) {
5755
+ return `${this.config.prefix}/${stackName}/${region}/${STACK_INSTANCE_SIBLING_FILE}`;
5756
+ }
5687
5757
  getCreateTokenLedgerKey(stackName, region) {
5688
5758
  return `${this.config.prefix}/${stackName}/${region}/create-tokens.json`;
5689
5759
  }
@@ -5798,7 +5868,8 @@ var S3StateBackend = class S3StateBackend {
5798
5868
  const { expectedEtag, migrateLegacy } = options;
5799
5869
  const body = {
5800
5870
  ...state,
5801
- version: 12,
5871
+ ...stackInstanceIdCarriedFrom(state),
5872
+ version: 13,
5802
5873
  stackName,
5803
5874
  region
5804
5875
  };
@@ -5827,6 +5898,12 @@ var S3StateBackend = class S3StateBackend {
5827
5898
  this.logger.warn(`Migrated stack ${this.displayName(stackName)} to new key, but failed to delete legacy key: ${errorDetail$2(deleteError)}`);
5828
5899
  }
5829
5900
  await notifyStateSaved(stackName, region);
5901
+ const siblingKey = this.getStackInstanceKey(stackName, region);
5902
+ const pendingId = this.pendingStackInstanceSiblings.get(siblingKey);
5903
+ if (pendingId !== void 0 && body.stackInstanceId === pendingId) {
5904
+ this.pendingStackInstanceSiblings.delete(siblingKey);
5905
+ await this.dropStackInstanceSiblingAfterSave(stackName, region);
5906
+ }
5830
5907
  return response.ETag;
5831
5908
  } catch (error) {
5832
5909
  if (error.name === "PreconditionFailed") throw new StateError(`State has been modified by another process. Expected ETag: ${expectedEtag}, but state has changed.`, error instanceof Error ? error : void 0);
@@ -5840,6 +5917,7 @@ var S3StateBackend = class S3StateBackend {
5840
5917
  async deleteState(stackName, region) {
5841
5918
  await this.ensureClientForBucket();
5842
5919
  await this.deleteCreateTokenLedger(stackName, region);
5920
+ await this.deleteStackInstanceSibling(stackName, region);
5843
5921
  try {
5844
5922
  this.logger.debug(`Deleting state: ${this.stackRef(stackName, region)}`);
5845
5923
  await this.s3Client.send(new DeleteObjectCommand({
@@ -5868,7 +5946,7 @@ var S3StateBackend = class S3StateBackend {
5868
5946
  });
5869
5947
  throw new StateError(`Failed to delete state for stack ${this.stackRef(stackName, region)}: ${errorDetail$2(normalized)}`, normalized);
5870
5948
  } finally {
5871
- await this.deleteRollbackJournal(stackName, region);
5949
+ await Promise.all([this.deleteRollbackJournal(stackName, region), this.purgeNoncurrentVersions([this.getStackInstanceKey(stackName, region)], { objectDescription: STACK_INSTANCE_OBJECT_DESCRIPTION })]);
5872
5950
  }
5873
5951
  }
5874
5952
  async purgeDeletedStateHistory(stackName, region) {
@@ -6413,6 +6491,102 @@ var S3StateBackend = class S3StateBackend {
6413
6491
  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);
6414
6492
  }
6415
6493
  }
6494
+ async stackInstanceIdForFirstWrite(stackName, region, options) {
6495
+ const key = this.getStackInstanceKey(stackName, region);
6496
+ const read = async () => this.readSiblingId(key, stackName, region);
6497
+ try {
6498
+ const existing = await read();
6499
+ if (existing !== null) {
6500
+ this.pendingStackInstanceSiblings.set(key, existing);
6501
+ this.logger.debug(safeMsg`Reusing the write-ahead stack instance id of ${displayStackName(stackName)}`);
6502
+ return existing;
6503
+ }
6504
+ const minted = randomUUID();
6505
+ if (!options.writeAhead) return minted;
6506
+ await this.ensureClientForBucket();
6507
+ const body = stackInstanceSiblingBody(minted);
6508
+ const put = async () => this.s3Client.send(new PutObjectCommand({
6509
+ Bucket: this.config.bucket,
6510
+ ...await this.ownerParam(),
6511
+ Key: key,
6512
+ Body: body,
6513
+ ContentLength: Buffer.byteLength(body),
6514
+ ContentType: "application/json",
6515
+ IfNoneMatch: "*"
6516
+ }));
6517
+ try {
6518
+ try {
6519
+ await put();
6520
+ } catch (error) {
6521
+ if (!isConditionalConflict(error)) throw error;
6522
+ await put();
6523
+ }
6524
+ } catch (error) {
6525
+ if (isNotImplemented(error)) {
6526
+ await this.putRawObject(key, body);
6527
+ this.pendingStackInstanceSiblings.set(key, minted);
6528
+ return minted;
6529
+ }
6530
+ if (!isConditionFailure(error)) throw error;
6531
+ const winner = await read();
6532
+ if (winner === null) throw error;
6533
+ this.pendingStackInstanceSiblings.set(key, winner);
6534
+ return winner;
6535
+ }
6536
+ this.pendingStackInstanceSiblings.set(key, minted);
6537
+ return minted;
6538
+ } catch (error) {
6539
+ if (error instanceof StateError) throw error;
6540
+ throw this.stackInstanceSiblingError(stackName, region, error, "write");
6541
+ }
6542
+ }
6543
+ stackInstanceSiblingError(stackName, region, error, access) {
6544
+ const normalized = normalizeAwsError(error, { bucket: this.config.bucket });
6545
+ return new StateError(`Failed to ${access === "write" ? "read or write" : "read"} the write-ahead stack instance id of stack ${this.stackRef(stackName, region)} (${STACK_INSTANCE_SIBLING_FILE} beside its state.json): ${errorDetail$2(normalized)}. ` + (access === "write" ? "Nothing was created; re-run once the state bucket can be written." : "Nothing was changed; re-run once the state bucket can be read."), normalized);
6546
+ }
6547
+ async readSiblingId(key, stackName, region) {
6548
+ const body = await this.getRawObject(key);
6549
+ if (body === null) return null;
6550
+ const id = parseStackInstanceSibling(body);
6551
+ if (id === null) throw new StateError(`The write-ahead stack instance id of stack ${this.stackRef(stackName, region)} (${STACK_INSTANCE_SIBLING_FILE} beside its state.json) is in a format this cdkd version cannot read; upgrade cdkd, or delete that object if it is damaged`);
6552
+ return id;
6553
+ }
6554
+ async readStackInstanceSibling(stackName, region) {
6555
+ try {
6556
+ return await this.readSiblingId(this.getStackInstanceKey(stackName, region), stackName, region) ?? void 0;
6557
+ } catch (error) {
6558
+ if (error instanceof StateError) throw error;
6559
+ throw this.stackInstanceSiblingError(stackName, region, error, "read");
6560
+ }
6561
+ }
6562
+ async deleteStackInstanceSibling(stackName, region) {
6563
+ await this.ensureClientForBucket();
6564
+ const key = this.getStackInstanceKey(stackName, region);
6565
+ this.pendingStackInstanceSiblings.delete(key);
6566
+ try {
6567
+ await this.s3Client.send(new DeleteObjectCommand({
6568
+ Bucket: this.config.bucket,
6569
+ ...await this.ownerParam(),
6570
+ Key: key
6571
+ }));
6572
+ } catch (error) {
6573
+ if (isNoSuchKey(error) || error.name === "NotFound") return;
6574
+ const normalized = normalizeAwsError(error, {
6575
+ bucket: this.config.bucket,
6576
+ operation: "DeleteObject"
6577
+ });
6578
+ throw new StateError(`Failed to delete the write-ahead stack instance id for stack ${this.stackRef(stackName, region)}: ${errorDetail$2(normalized)}. The state record was left in place; re-run once that object can be deleted.`, normalized);
6579
+ }
6580
+ }
6581
+ async dropStackInstanceSiblingAfterSave(stackName, region) {
6582
+ try {
6583
+ await this.deleteStackInstanceSibling(stackName, region);
6584
+ } catch (error) {
6585
+ this.logger.debug(safeMsg`Could not delete the write-ahead stack instance id of ${displayStackName(stackName)}: ${describeAwsFailure(error).summary}`);
6586
+ return;
6587
+ }
6588
+ await this.purgeNoncurrentVersions([this.getStackInstanceKey(stackName, region)], { objectDescription: STACK_INSTANCE_OBJECT_DESCRIPTION });
6589
+ }
6416
6590
  async loadRollbackJournal(stackName, region) {
6417
6591
  const key = this.rollbackJournalKey(stackName, region);
6418
6592
  const touches = this.journalTouches.get(key) ?? 0;
@@ -6611,6 +6785,7 @@ var S3StateBackend = class S3StateBackend {
6611
6785
  if (raw === region) return { state };
6612
6786
  if (raw === void 0) return { state: {
6613
6787
  ...state,
6788
+ ...stackInstanceIdCarriedFrom(state),
6614
6789
  region
6615
6790
  } };
6616
6791
  const kind = describeRegionValueKind(raw);
@@ -6619,6 +6794,7 @@ var S3StateBackend = class S3StateBackend {
6619
6794
  return {
6620
6795
  state: {
6621
6796
  ...state,
6797
+ ...stackInstanceIdCarriedFrom(state),
6622
6798
  region
6623
6799
  },
6624
6800
  divergentBodyRegion: raw
@@ -13577,62 +13753,21 @@ function sentIdentifierIs(requested, recorded, observed, physicalId) {
13577
13753
  return sent !== void 0 && heldAt(recorded, observed, [key]).value === sent;
13578
13754
  });
13579
13755
  }
13756
+ const derivedEntry = (resourceType, file) => ({
13757
+ file,
13758
+ source: `generatedNameFor('${resourceType}', logicalId)`,
13759
+ derive: (id) => generatedNameFor(resourceType, id)
13760
+ });
13580
13761
  const DERIVED_GENERATED_NAMES = {
13581
- "AWS::AutoScaling::AutoScalingGroup": {
13582
- file: "asg-provider.ts",
13583
- source: "generateResourceName(logicalId, { maxLength: 255 })",
13584
- derive: (id) => generateResourceName(id, { maxLength: 255 })
13585
- },
13586
- "AWS::CodeCommit::Repository": {
13587
- file: "codecommit-repository-provider.ts",
13588
- source: "generateResourceName(logicalId, { maxLength: 100 })",
13589
- derive: (id) => generateResourceName(id, { maxLength: 100 })
13590
- },
13591
- "AWS::DynamoDB::GlobalTable": {
13592
- file: "dynamodb-globaltable-provider.ts",
13593
- source: "generateResourceName(logicalId, { maxLength: 255 })",
13594
- derive: (id) => generateResourceName(id, { maxLength: 255 })
13595
- },
13596
- "AWS::Logs::LogGroup": {
13597
- file: "logs-loggroup-provider.ts",
13598
- source: "`/cdkd/${generateResourceName(logicalId, { maxLength: 506, allowedPattern: /[^a-zA-Z0-9-/_]/g })}`",
13599
- derive: (id) => `/cdkd/${generateResourceName(id, {
13600
- maxLength: 506,
13601
- allowedPattern: /[^a-zA-Z0-9-/_]/g
13602
- })}`
13603
- },
13604
- "AWS::RDS::DBProxy": {
13605
- file: "rds-dbproxy-provider.ts",
13606
- source: "generateResourceName(logicalId, { maxLength: 64 })",
13607
- derive: (id) => generateResourceName(id, { maxLength: 64 })
13608
- },
13609
- "AWS::RDS::DBProxyEndpoint": {
13610
- file: "rds-dbproxy-endpoint-provider.ts",
13611
- source: "generateResourceName(logicalId, { maxLength: 64 })",
13612
- derive: (id) => generateResourceName(id, { maxLength: 64 })
13613
- },
13614
- "AWS::S3::Bucket": {
13615
- file: "s3-bucket-provider.ts",
13616
- source: "generateResourceName(logicalId, {\n maxLength: 63,\n lowercase: true,\n allowedPattern: /[^a-z0-9.-]/g,\n })",
13617
- derive: (id) => generateResourceName(id, {
13618
- maxLength: 63,
13619
- lowercase: true,
13620
- allowedPattern: /[^a-z0-9.-]/g
13621
- })
13622
- },
13623
- "AWS::Scheduler::Schedule": {
13624
- file: "scheduler-schedule-provider.ts",
13625
- source: "generateResourceName(logicalId, { maxLength: 64 })",
13626
- derive: (id) => generateResourceName(id, { maxLength: 64 })
13627
- },
13628
- "AWS::SSM::Parameter": {
13629
- file: "ssm-parameter-provider.ts",
13630
- source: "`/${generateResourceName(logicalId, { maxLength: 1023, allowedPattern: /[^a-zA-Z0-9-/_]/g })}`",
13631
- derive: (id) => `/${generateResourceName(id, {
13632
- maxLength: 1023,
13633
- allowedPattern: /[^a-zA-Z0-9-/_]/g
13634
- })}`
13635
- }
13762
+ "AWS::AutoScaling::AutoScalingGroup": derivedEntry("AWS::AutoScaling::AutoScalingGroup", "asg-provider.ts"),
13763
+ "AWS::CodeCommit::Repository": derivedEntry("AWS::CodeCommit::Repository", "codecommit-repository-provider.ts"),
13764
+ "AWS::DynamoDB::GlobalTable": derivedEntry("AWS::DynamoDB::GlobalTable", "dynamodb-globaltable-provider.ts"),
13765
+ "AWS::Logs::LogGroup": derivedEntry("AWS::Logs::LogGroup", "logs-loggroup-provider.ts"),
13766
+ "AWS::RDS::DBProxy": derivedEntry("AWS::RDS::DBProxy", "rds-dbproxy-provider.ts"),
13767
+ "AWS::RDS::DBProxyEndpoint": derivedEntry("AWS::RDS::DBProxyEndpoint", "rds-dbproxy-endpoint-provider.ts"),
13768
+ "AWS::S3::Bucket": derivedEntry("AWS::S3::Bucket", "s3-bucket-provider.ts"),
13769
+ "AWS::Scheduler::Schedule": derivedEntry("AWS::Scheduler::Schedule", "scheduler-schedule-provider.ts"),
13770
+ "AWS::SSM::Parameter": derivedEntry("AWS::SSM::Parameter", "ssm-parameter-provider.ts")
13636
13771
  };
13637
13772
 
13638
13773
  //#endregion
@@ -13720,6 +13855,52 @@ function replacementSentNameMoves(input) {
13720
13855
  physicalId: input.physicalId
13721
13856
  };
13722
13857
  }
13858
+ function generatedReplacementName(resourceType, createdVia, desired, logicalId) {
13859
+ const declared = (property) => {
13860
+ const value = desired?.[property];
13861
+ return value !== void 0 && value !== null && value !== "";
13862
+ };
13863
+ const fallbackProperty = fallbackNamePropertyFor(resourceType);
13864
+ if (createdVia === "cc-api" || GENERATED_NAME_VERBATIM.has(resourceType)) {
13865
+ if (fallbackProperty === void 0 || declared(fallbackProperty)) return void 0;
13866
+ const name = applyDefaultNameForFallback(logicalId, resourceType, desired ?? {}, "omit")[fallbackProperty];
13867
+ return typeof name === "string" ? {
13868
+ property: fallbackProperty,
13869
+ name
13870
+ } : void 0;
13871
+ }
13872
+ const rewrite = ownEntry$1(SENT_NAME_REWRITTEN, resourceType);
13873
+ if (rewrite !== void 0) {
13874
+ if (declared(rewrite.property)) return void 0;
13875
+ return {
13876
+ property: rewrite.property,
13877
+ name: generateResourceNameWithFallback(void 0, logicalId, { maxLength: rewrite.maxLength })
13878
+ };
13879
+ }
13880
+ const derived = ownEntry$1(DERIVED_GENERATED_NAMES, resourceType);
13881
+ const property = explicitNamePropertyFor(resourceType);
13882
+ if (derived === void 0 || property === void 0 || declared(property)) return void 0;
13883
+ return {
13884
+ property,
13885
+ name: derived.derive(logicalId)
13886
+ };
13887
+ }
13888
+ function replacementGeneratedNameMoves(input) {
13889
+ if (getCurrentStackInstanceId() === void 0) return void 0;
13890
+ if (input.oldResourceType !== input.newResourceType) return void 0;
13891
+ const generated = generatedReplacementName(input.newResourceType, input.createdVia, input.desiredProperties, input.logicalId);
13892
+ if (generated === void 0 || generated.name === "") return void 0;
13893
+ const fold = CASE_INSENSITIVE_NAME_TYPES.has(input.newResourceType) ? (value) => value.toLowerCase() : (value) => value;
13894
+ if (input.physicalId === "" || holderIdNames(fold(input.physicalId), fold(generated.name))) return;
13895
+ return {
13896
+ property: generated.property,
13897
+ desiredName: generated.name,
13898
+ heldName: void 0,
13899
+ heldProperty: void 0,
13900
+ physicalId: input.physicalId,
13901
+ generated: true
13902
+ };
13903
+ }
13723
13904
  function maskRewrittenSentName(resourceType, properties, logicalId, base) {
13724
13905
  const sent = sentRewrittenName(resourceType, properties, logicalId);
13725
13906
  if (sent === void 0) return base;
@@ -27247,7 +27428,7 @@ var CloudControlProvider = class {
27247
27428
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
27248
27429
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
27249
27430
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
27250
- const { ASGProvider } = await import("./asg-provider-ClDYCUia.js");
27431
+ const { ASGProvider } = await import("./asg-provider-BpRcm_UB.js");
27251
27432
  const delegateRegion = ambientRegion();
27252
27433
  let asgProvider = this.asgDeleteDelegates.get(delegateRegion);
27253
27434
  if (asgProvider === void 0) {
@@ -27353,7 +27534,7 @@ var CloudControlProvider = class {
27353
27534
  const family = SDK_DELETE_FAMILIES.get(resourceType);
27354
27535
  const cached = this.sdkDeleteDelegates.get(family);
27355
27536
  if (cached !== void 0 && cached.region === ccRegion) return cached.provider;
27356
- const provider = family === "rds" ? new (await (import("./rds-provider-BMQvQTTS.js"))).RDSProvider({ region: ccRegion }) : new (await (import("./neptune-provider-CtnwSAGM.js"))).NeptuneProvider({ region: ccRegion });
27537
+ const provider = family === "rds" ? new (await (import("./rds-provider-CmKbYAvr.js"))).RDSProvider({ region: ccRegion }) : new (await (import("./neptune-provider-DzTl6-74.js"))).NeptuneProvider({ region: ccRegion });
27357
27538
  this.sdkDeleteDelegates.set(family, {
27358
27539
  region: ccRegion,
27359
27540
  provider
@@ -32050,245 +32231,682 @@ function isRefusedByName(error) {
32050
32231
  }
32051
32232
 
32052
32233
  //#endregion
32053
- //#region src/provisioning/providers/idempotency-token.ts
32054
- const PROCESS_NONCE = randomUUID();
32055
- const inFlight$1 = /* @__PURE__ */ new Map();
32056
- const generations = /* @__PURE__ */ new Map();
32057
- const MAX_TRACKED_GENERATIONS = 1e5;
32058
- const tokenKey = (scope, logicalId, maxLength, charset) => injectiveKey(scope, ambientRegion() ?? "", getCurrentStackName() ?? "", logicalId, maxLength, charset);
32059
- const derive = (key, generation, maxLength, charset) => {
32060
- const digest = createHash("sha256").update(`${PROCESS_NONCE}\u0000${key}\u0000${generation}`).digest("hex");
32061
- return `cdkd${charset === "alphanumeric" ? "" : "-"}${digest}`.slice(0, maxLength);
32062
- };
32063
- function acquireIdempotencyToken(options) {
32064
- const { scope, logicalId, maxLength = 64, charset = "default" } = options;
32065
- const key = tokenKey(scope, logicalId, maxLength, charset);
32066
- const existing = inFlight$1.get(key);
32067
- const value = existing ?? derive(key, generations.get(key) ?? 0, maxLength, charset);
32068
- if (existing === void 0) inFlight$1.set(key, value);
32069
- return {
32070
- value,
32071
- release: () => {
32072
- if (!inFlight$1.has(key)) return;
32073
- inFlight$1.delete(key);
32074
- const generation = (generations.get(key) ?? 0) + 1;
32075
- if (!generations.has(key) && generations.size >= MAX_TRACKED_GENERATIONS) {
32076
- const oldest = generations.keys().next();
32077
- if (!oldest.done) generations.delete(oldest.value);
32078
- }
32079
- generations.set(key, generation);
32080
- }
32234
+ //#region src/types/deployment-events.ts
32235
+ function extractDeploymentEventError(err) {
32236
+ if (!(err instanceof Error)) return {
32237
+ name: "UnknownError",
32238
+ message: safeStringify$1(err)
32081
32239
  };
32082
- }
32083
- let unscopedCreateTokensAllowed = false;
32084
- const STACK_SCOPED_DIGEST_LENGTH = 12;
32085
- function stackScopedCreateToken(options) {
32086
- const { logicalId, immutableInputs, maxLength } = options;
32087
- const stackName = getCurrentStackName();
32088
- if (stackName === void 0 && !unscopedCreateTokensAllowed) throw new Error(`cdkd internal error: a create token for ${logicalId} was requested outside a stack scope (withStackName), so it cannot be scoped to its stack`);
32089
- const digest = createHash("sha256").update(injectiveKey(stackName ?? "", ambientRegion() ?? "", logicalId, ...immutableInputs.map((v) => JSON.stringify(v ?? null)))).digest("hex").slice(0, STACK_SCOPED_DIGEST_LENGTH);
32090
- const prefixLength = maxLength - 5 - 1 - STACK_SCOPED_DIGEST_LENGTH;
32091
- return `cdkd-${logicalId.slice(0, prefixLength)}-${digest}`;
32240
+ const result = {
32241
+ name: err.name || "Error",
32242
+ message: err.message
32243
+ };
32244
+ let current = err;
32245
+ for (let depth = 0; depth < 10 && current instanceof Error; depth++) {
32246
+ const maybeAws = current;
32247
+ if (maybeAws.$metadata !== void 0) {
32248
+ const code = maybeAws.Code ?? maybeAws.name;
32249
+ if (code) result.awsErrorCode = code;
32250
+ if (maybeAws.$metadata.requestId) result.requestId = maybeAws.$metadata.requestId;
32251
+ }
32252
+ current = current.cause;
32253
+ }
32254
+ return result;
32092
32255
  }
32093
32256
 
32094
32257
  //#endregion
32095
- //#region src/provisioning/providers/create-token-ledger.ts
32096
- var CreateTokenLedger = class {
32097
- doc;
32098
- loadedAbsent = false;
32099
- stale = false;
32100
- startedThisDeploy = false;
32101
- replaceRecordedLedger = false;
32102
- stateRecorded = false;
32103
- stateRecordedPersisted = false;
32104
- unrecordedLetGo = /* @__PURE__ */ new Map();
32105
- chain = Promise.resolve();
32106
- logger = getLogger().child("CreateTokenLedger");
32107
- store;
32108
- stack;
32109
- constructor(store, stack) {
32110
- this.store = store;
32111
- this.stack = stack;
32258
+ //#region src/state/deployment-events-store.ts
32259
+ const DEPLOYMENT_EVENTS_MAX_INDEX_RUNS = 20;
32260
+ const FLUSH_INTERVAL_MS = 2e3;
32261
+ const FLUSH_EVENT_THRESHOLD = 50;
32262
+ function newDeploymentRunId(now = /* @__PURE__ */ new Date()) {
32263
+ return `${now.toISOString().replace(/[-:.]/g, "")}-${randomUUID().slice(0, 8)}`;
32264
+ }
32265
+ function deploymentEventsKey(prefix, stackName, region, runId) {
32266
+ return `${prefix}/${stackName}/${region}/deployments/${runId}.jsonl`;
32267
+ }
32268
+ function deploymentEventsIndexKey(prefix, stackName, region) {
32269
+ return `${prefix}/${stackName}/${region}/deployments/index.json`;
32270
+ }
32271
+ function deploymentsDirPrefix(prefix, stackName, region) {
32272
+ return `${prefix}/${stackName}/${region}/deployments/`;
32273
+ }
32274
+ const DEPLOYMENT_EVENTS_OBJECT_DESCRIPTION = "deployment-event streams and their index, whose recorded error messages can quote values the secret mask does not catch";
32275
+ async function deleteAndPurgeEventObjects(backend, keys, dirPrefix, scope = "deleted-keys") {
32276
+ if (keys.length === 0) return void 0;
32277
+ let swept;
32278
+ try {
32279
+ await backend.deleteRawObjects(keys);
32280
+ } finally {
32281
+ if (scope === "whole-dir") swept = await backend.purgeNoncurrentVersionsUnderPrefix(dirPrefix, { objectDescription: DEPLOYMENT_EVENTS_OBJECT_DESCRIPTION });
32282
+ else await backend.purgeNoncurrentVersions(keys, {
32283
+ ...keys.length > 1 && { listPrefix: dirPrefix },
32284
+ objectDescription: DEPLOYMENT_EVENTS_OBJECT_DESCRIPTION
32285
+ });
32112
32286
  }
32113
- serialized(fn) {
32114
- const next = this.chain.then(fn, fn);
32115
- this.chain = next.catch(() => void 0);
32116
- return next;
32287
+ return swept;
32288
+ }
32289
+ function runIdTimestampMs(runId) {
32290
+ const m = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(\d{3})Z/.exec(runId);
32291
+ if (!m) return null;
32292
+ const iso = `${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6]}.${m[7]}Z`;
32293
+ const ms = Date.parse(iso);
32294
+ return Number.isNaN(ms) ? null : ms;
32295
+ }
32296
+ function runIdFromJsonlKey(key, dirPrefix) {
32297
+ if (!key.startsWith(dirPrefix) || !key.endsWith(".jsonl")) return null;
32298
+ return key.slice(dirPrefix.length, -6);
32299
+ }
32300
+ var DeploymentEventsStore = class {
32301
+ logger = getLogger().child("DeploymentEvents");
32302
+ backend;
32303
+ stackName;
32304
+ region;
32305
+ command;
32306
+ runId;
32307
+ cdkdVersion;
32308
+ startedAt;
32309
+ events = [];
32310
+ persistedCount = 0;
32311
+ flushTimer;
32312
+ writeChain = Promise.resolve();
32313
+ warnedOnce = false;
32314
+ finalized = false;
32315
+ constructor(backend, options) {
32316
+ this.backend = backend;
32317
+ this.stackName = options.stackName;
32318
+ this.region = options.region;
32319
+ this.command = options.command;
32320
+ this.runId = options.runId ?? newDeploymentRunId();
32321
+ this.cdkdVersion = options.cdkdVersion ?? getCdkdVersion();
32322
+ this.startedAt = (/* @__PURE__ */ new Date()).toISOString();
32117
32323
  }
32118
- beginDeploy(exists) {
32119
- this.stateRecorded = exists;
32120
- this.replaceRecordedLedger = !exists;
32324
+ record(event) {
32325
+ try {
32326
+ if (this.finalized) return;
32327
+ this.events.push({
32328
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
32329
+ ...event
32330
+ });
32331
+ if (this.events.length - this.persistedCount >= FLUSH_EVENT_THRESHOLD) this.scheduleFlush(0);
32332
+ else this.scheduleFlush(FLUSH_INTERVAL_MS);
32333
+ } catch {}
32121
32334
  }
32122
- async current() {
32123
- if (this.doc !== void 0 && !this.stale) return this.doc;
32124
- if (this.loadedAbsent && !this.stale) return null;
32125
- const loaded = await this.store.load();
32126
- this.stale = false;
32127
- this.loadedAbsent = loaded === null;
32128
- if (loaded !== null && this.replaceRecordedLedger && loaded.stateRecorded === true) {
32129
- this.logger.info("This stack has no state record but its create-token ledger outlived an earlier one; starting a fresh ledger, so this deploy does not take over resources that deployment left.");
32130
- this.doc = emptyCreateTokenLedger(randomUUID());
32131
- this.startedThisDeploy = true;
32132
- return this.doc;
32335
+ async finalize(result) {
32336
+ if (this.finalized) return;
32337
+ this.finalized = true;
32338
+ if (this.flushTimer) {
32339
+ clearTimeout(this.flushTimer);
32340
+ this.flushTimer = void 0;
32133
32341
  }
32134
- this.doc = loaded ?? void 0;
32135
- return loaded;
32342
+ if (this.events.length === 0) return;
32343
+ await this.enqueueWrite(async () => {
32344
+ const indexRead = this.readIndexRuns();
32345
+ indexRead.catch(() => {});
32346
+ await this.doFlush();
32347
+ await this.writeIndexAndPrune(result, await indexRead);
32348
+ });
32136
32349
  }
32137
- async persist(doc) {
32138
- if (this.stateRecorded) doc.stateRecorded = true;
32139
- await this.store.save(doc);
32140
- this.replaceRecordedLedger = false;
32141
- if (doc.stateRecorded === true) this.stateRecordedPersisted = true;
32350
+ async writeIndexAndPrune(result, existingRuns) {
32351
+ const { key, file, runs } = this.buildIndexUpdate(result, existingRuns);
32352
+ const keptRunIds = runs.map((r) => r.runId);
32353
+ const willPrune = keptRunIds.length >= 20;
32354
+ const dirPrefix = deploymentsDirPrefix(this.backend.prefix, this.stackName, this.region);
32355
+ const staleKeysRead = willPrune ? this.backend.listRawKeys(dirPrefix) : void 0;
32356
+ staleKeysRead?.catch(() => {});
32357
+ await this.backend.putRawObject(key, JSON.stringify(file, null, 2));
32358
+ if (!staleKeysRead) return;
32359
+ await this.pruneSupersededRunFiles(keptRunIds, dirPrefix, await staleKeysRead);
32142
32360
  }
32143
- reserve(logicalId, base, derive, firstSentAt) {
32144
- return this.serialized(async () => {
32145
- if (this.unrecordedLetGo.has(logicalId)) throw this.wrapLedgerError(logicalId, this.unrecordedLetGo.get(logicalId));
32146
- const earlier = this.doc?.sent[logicalId];
32147
- if (earlier !== void 0 && earlier.base === base) return {
32148
- value: earlier.token,
32149
- earlierFirstSentAt: earlier.firstSentAt,
32150
- ledgerStartedThisDeploy: false
32151
- };
32152
- let doc;
32153
- try {
32154
- const loaded = await this.current();
32155
- if (loaded === null) {
32156
- doc = emptyCreateTokenLedger(randomUUID());
32157
- this.doc = doc;
32158
- this.startedThisDeploy = true;
32159
- } else {
32160
- doc = loaded;
32161
- const recorded = doc.sent[logicalId];
32162
- if (recorded !== void 0 && recorded.base === base) return {
32163
- value: recorded.token,
32164
- earlierFirstSentAt: recorded.firstSentAt,
32165
- ledgerStartedThisDeploy: false
32166
- };
32167
- }
32168
- } catch (error) {
32169
- this.stale = true;
32170
- throw this.wrapLedgerError(logicalId, error);
32171
- }
32172
- const value = derive(doc.nonce);
32173
- const previous = doc.sent[logicalId];
32174
- doc.sent[logicalId] = {
32175
- base,
32176
- token: value,
32177
- firstSentAt
32178
- };
32179
- try {
32180
- await this.persist(doc);
32181
- } catch (error) {
32182
- if (previous === void 0) delete doc.sent[logicalId];
32183
- else doc.sent[logicalId] = previous;
32184
- this.stale = true;
32185
- throw this.wrapLedgerError(logicalId, error);
32186
- }
32187
- return {
32188
- value,
32189
- earlierFirstSentAt: void 0,
32190
- ledgerStartedThisDeploy: this.startedThisDeploy
32191
- };
32192
- });
32361
+ async drain() {
32362
+ await this.writeChain;
32193
32363
  }
32194
- wrapLedgerError(logicalId, error) {
32195
- const stack = this.stack === void 0 ? "this stack" : `stack ${displayStackName(this.stack.stackName)} (${displayIdent(this.stack.region)})`;
32196
- return new Error(safeMsg`Could not read or write the create-token ledger of ${stack} (${describeAwsFailure(error).summary}), so the create of ${logicalId} was not sent: the token it would send without the ledger is not the one a later deploy would send, and the resource could leak. Re-run the deploy once the ledger can be read and written.`, { cause: error });
32364
+ scheduleFlush(delayMs) {
32365
+ if (this.flushTimer) {
32366
+ if (delayMs > 0) return;
32367
+ clearTimeout(this.flushTimer);
32368
+ }
32369
+ this.flushTimer = setTimeout(() => {
32370
+ this.flushTimer = void 0;
32371
+ this.enqueueWrite(() => this.doFlush());
32372
+ }, delayMs);
32373
+ this.flushTimer.unref?.();
32197
32374
  }
32198
- rotate(logicalId) {
32199
- return this.serialized(async () => {
32200
- try {
32201
- const doc = await this.current();
32202
- if (doc === null) return;
32203
- doc.nonce = randomUUID();
32204
- delete doc.sent[logicalId];
32205
- await this.persist(doc);
32206
- this.unrecordedLetGo.delete(logicalId);
32207
- } catch (error) {
32208
- this.stale = true;
32209
- this.unrecordedLetGo.set(logicalId, error);
32210
- this.logger.warn(safeMsg`Could not record in this stack's create-token ledger that ${logicalId} was kept (${describeAwsFailure(error).summary}). A create of ${logicalId} in this deploy is refused; a later deploy's create of it may be handed the kept resource back, so check the resource it records against the one this deploy kept.`);
32211
- }
32375
+ enqueueWrite(op) {
32376
+ const next = this.writeChain.then(op).catch((err) => {
32377
+ this.warnOnce(`Failed to persist deployment events for run ${this.runId}: ${describeAwsFailure(err).detail}`);
32212
32378
  });
32379
+ this.writeChain = next;
32380
+ return next;
32213
32381
  }
32214
- sentEntries() {
32215
- return this.serialized(async () => {
32216
- try {
32217
- return { ...(await this.current())?.sent ?? {} };
32218
- } catch (error) {
32219
- this.stale = true;
32220
- throw error;
32221
- }
32222
- });
32382
+ async doFlush() {
32383
+ if (this.events.length === 0 || this.events.length === this.persistedCount) return;
32384
+ const snapshotCount = this.events.length;
32385
+ const body = this.events.slice(0, snapshotCount).map((e) => JSON.stringify(e)).join("\n");
32386
+ await this.backend.putRawObject(deploymentEventsKey(this.backend.prefix, this.stackName, this.region, this.runId), body + "\n", "application/x-ndjson");
32387
+ this.persistedCount = snapshotCount;
32223
32388
  }
32224
- recordSent(entries, firstSentAt) {
32225
- return this.serialized(async () => {
32226
- try {
32227
- let doc = await this.current();
32228
- if (doc === null) {
32229
- doc = emptyCreateTokenLedger(randomUUID());
32230
- this.doc = doc;
32231
- this.startedThisDeploy = true;
32232
- }
32233
- let changed = false;
32234
- for (const entry of entries) {
32235
- const recorded = doc.sent[entry.logicalId];
32236
- if (recorded?.base === entry.base && recorded.token === entry.token) continue;
32237
- doc.sent[entry.logicalId] = {
32238
- base: entry.base,
32239
- token: entry.token,
32240
- firstSentAt
32241
- };
32242
- changed = true;
32243
- }
32244
- if (changed) await this.persist(doc);
32245
- } catch (error) {
32246
- this.stale = true;
32247
- throw error;
32389
+ async readIndexRuns() {
32390
+ const key = deploymentEventsIndexKey(this.backend.prefix, this.stackName, this.region);
32391
+ try {
32392
+ const raw = await this.backend.getRawObject(key);
32393
+ if (raw !== null) {
32394
+ const parsed = JSON.parse(raw);
32395
+ if (Array.isArray(parsed.runs)) return parsed.runs;
32248
32396
  }
32249
- });
32397
+ } catch (err) {
32398
+ this.logger.debug(`Deployment-events index unreadable, rewriting: ${describeAwsFailure(err).detail}`);
32399
+ }
32400
+ return [];
32250
32401
  }
32251
- abandonedAt() {
32252
- return this.serialized(async () => (await this.current())?.abandonedAt);
32402
+ buildIndexUpdate(result, existingRuns) {
32403
+ const key = deploymentEventsIndexKey(this.backend.prefix, this.stackName, this.region);
32404
+ const runs = [{
32405
+ runId: this.runId,
32406
+ command: this.command,
32407
+ cdkdVersion: this.cdkdVersion,
32408
+ startedAt: this.startedAt,
32409
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
32410
+ result,
32411
+ eventCount: this.persistedCount
32412
+ }, ...existingRuns.filter((r) => r.runId !== this.runId)].slice(0, 20);
32413
+ return {
32414
+ key,
32415
+ file: {
32416
+ indexVersion: 1,
32417
+ stackName: this.stackName,
32418
+ region: this.region,
32419
+ runs,
32420
+ lastModified: Date.now()
32421
+ },
32422
+ runs
32423
+ };
32253
32424
  }
32254
- noteAbandoned(at) {
32255
- return this.serialized(async () => {
32256
- try {
32257
- const doc = await this.current();
32258
- if (doc === null) return;
32259
- if (!Object.values(doc.sent).some((e) => e.base.startsWith("adopt-by-name:")) || doc.abandonedAt !== void 0 && doc.abandonedAt >= at) return;
32260
- doc.abandonedAt = at;
32261
- await this.persist(doc);
32262
- } catch (error) {
32263
- this.stale = true;
32264
- throw error;
32265
- }
32425
+ async pruneSupersededRunFiles(keptRunIds, dirPrefix, keys) {
32426
+ if (keptRunIds.length < 20) return;
32427
+ const cutoff = keptRunIds.reduce((min, id) => id < min ? id : min, keptRunIds[0]);
32428
+ const stale = keys.filter((k) => {
32429
+ const runId = runIdFromJsonlKey(k, dirPrefix);
32430
+ return runId !== null && runId < cutoff;
32266
32431
  });
32432
+ if (stale.length === 0) return;
32433
+ await deleteAndPurgeEventObjects(this.backend, stale, dirPrefix);
32434
+ this.logger.debug(`Pruned ${stale.length} superseded deployment-event stream(s) for ${this.stackName} (${this.region})`);
32267
32435
  }
32268
- settleAdoptingCreates(drop, failedAt) {
32269
- return this.serialized(async () => {
32270
- try {
32271
- const doc = await this.current();
32272
- if (doc === null) return;
32273
- const adopting = (id) => doc.sent[id]?.base.startsWith(ADOPTING_CREATE_BASE) === true;
32274
- let changed = false;
32275
- for (const id of drop) {
32276
- if (!adopting(id)) continue;
32277
- delete doc.sent[id];
32278
- changed = true;
32279
- }
32280
- for (const [id, at] of failedAt) {
32281
- const entry = adopting(id) ? doc.sent[id] : void 0;
32282
- if (entry === void 0 || entry.failedAt !== void 0) continue;
32283
- entry.failedAt = at;
32284
- changed = true;
32285
- }
32286
- if (changed) await this.persist(doc);
32287
- } catch (error) {
32288
- this.stale = true;
32289
- throw error;
32290
- }
32291
- });
32436
+ warnOnce(message) {
32437
+ if (this.warnedOnce) {
32438
+ this.logger.debug(message);
32439
+ return;
32440
+ }
32441
+ this.warnedOnce = true;
32442
+ this.logger.warn(`${message} — continuing; deployment events are best-effort and never block the run.`);
32443
+ }
32444
+ };
32445
+ var DeploymentEventsReader = class {
32446
+ backend;
32447
+ constructor(backend) {
32448
+ this.backend = backend;
32449
+ }
32450
+ async listRegions(stackName) {
32451
+ const prefix = `${this.backend.prefix}/${stackName}/`;
32452
+ const keys = await this.backend.listRawKeys(prefix);
32453
+ const regions = /* @__PURE__ */ new Set();
32454
+ for (const key of keys) {
32455
+ const segments = key.slice(prefix.length).split("/");
32456
+ if (segments.length === 3 && segments[1] === "deployments" && segments[0]) regions.add(segments[0]);
32457
+ }
32458
+ return [...regions].sort();
32459
+ }
32460
+ async listRuns(stackName, region) {
32461
+ const key = deploymentEventsIndexKey(this.backend.prefix, stackName, region);
32462
+ try {
32463
+ const raw = await this.backend.getRawObject(key);
32464
+ if (raw !== null) {
32465
+ const parsed = JSON.parse(raw);
32466
+ if (Array.isArray(parsed.runs)) return parsed.runs;
32467
+ }
32468
+ } catch {}
32469
+ const dirPrefix = `${this.backend.prefix}/${stackName}/${region}/deployments/`;
32470
+ const runIds = (await this.backend.listRawKeys(dirPrefix)).filter((k) => k.endsWith(".jsonl")).map((k) => k.slice(dirPrefix.length, -6)).sort().reverse();
32471
+ return Promise.all(runIds.map((runId) => this.summarizeRunFromJsonl(stackName, region, runId)));
32472
+ }
32473
+ async summarizeRunFromJsonl(stackName, region, runId) {
32474
+ const fallback = {
32475
+ runId,
32476
+ command: "deploy",
32477
+ cdkdVersion: "unknown",
32478
+ startedAt: "",
32479
+ finishedAt: "",
32480
+ result: "UNKNOWN",
32481
+ eventCount: 0
32482
+ };
32483
+ let events = null;
32484
+ try {
32485
+ events = await this.readRunEvents(stackName, region, runId);
32486
+ } catch {
32487
+ return fallback;
32488
+ }
32489
+ if (events === null || events.length === 0) return fallback;
32490
+ const started = events.find((e) => e.eventType === "RUN_STARTED");
32491
+ let finished;
32492
+ for (const e of events) if (e.eventType === "RUN_FINISHED") finished = e;
32493
+ return {
32494
+ runId,
32495
+ command: started?.command ?? fallback.command,
32496
+ cdkdVersion: started?.cdkdVersion ?? fallback.cdkdVersion,
32497
+ startedAt: started?.timestamp ?? fallback.startedAt,
32498
+ finishedAt: finished?.timestamp ?? fallback.finishedAt,
32499
+ result: finished?.result ?? "UNKNOWN",
32500
+ eventCount: events.length
32501
+ };
32502
+ }
32503
+ async readRunEvents(stackName, region, runId) {
32504
+ const key = deploymentEventsKey(this.backend.prefix, stackName, region, runId);
32505
+ const raw = await this.backend.getRawObject(key);
32506
+ if (raw === null) return null;
32507
+ const events = [];
32508
+ for (const line of raw.split("\n")) {
32509
+ const trimmed = line.trim();
32510
+ if (!trimmed) continue;
32511
+ try {
32512
+ events.push(JSON.parse(trimmed));
32513
+ } catch {}
32514
+ }
32515
+ return events;
32516
+ }
32517
+ async pruneRuns(stackName, region, opts = {}) {
32518
+ const dirPrefix = deploymentsDirPrefix(this.backend.prefix, stackName, region);
32519
+ const keys = await this.backend.listRawKeys(dirPrefix);
32520
+ const runIdsDesc = keys.map((k) => runIdFromJsonlKey(k, dirPrefix)).filter((id) => id !== null).sort().reverse();
32521
+ const indexKey = deploymentEventsIndexKey(this.backend.prefix, stackName, region);
32522
+ const indexExisted = keys.includes(indexKey);
32523
+ if (opts.all === true) {
32524
+ const toDelete = runIdsDesc.map((id) => deploymentEventsKey(this.backend.prefix, stackName, region, id));
32525
+ const earlierVersions = await deleteAndPurgeEventObjects(this.backend, [...toDelete, indexKey], dirPrefix, "whole-dir");
32526
+ return {
32527
+ deletedRunIds: runIdsDesc,
32528
+ remainingRunIds: [],
32529
+ indexDeleted: indexExisted,
32530
+ ...earlierVersions !== void 0 && { earlierVersions }
32531
+ };
32532
+ }
32533
+ const noGuards = opts.keep === void 0 && opts.olderThanMs === void 0;
32534
+ const keep = opts.keep ?? (noGuards ? 20 : void 0);
32535
+ const protectedByCount = keep === void 0 ? /* @__PURE__ */ new Set() : new Set(runIdsDesc.slice(0, keep));
32536
+ let candidates = runIdsDesc.filter((id) => !protectedByCount.has(id));
32537
+ if (opts.olderThanMs !== void 0) {
32538
+ const cutoff = (opts.now ?? /* @__PURE__ */ new Date()).getTime() - opts.olderThanMs;
32539
+ candidates = candidates.filter((id) => {
32540
+ const t = runIdTimestampMs(id);
32541
+ return t !== null && t < cutoff;
32542
+ });
32543
+ }
32544
+ if (candidates.length === 0) return {
32545
+ deletedRunIds: [],
32546
+ remainingRunIds: runIdsDesc,
32547
+ indexDeleted: false
32548
+ };
32549
+ const deletedSet = new Set(candidates);
32550
+ const deleteKeys = candidates.map((id) => deploymentEventsKey(this.backend.prefix, stackName, region, id));
32551
+ const remainingRunIds = runIdsDesc.filter((id) => !deletedSet.has(id));
32552
+ if (remainingRunIds.length === 0) {
32553
+ await deleteAndPurgeEventObjects(this.backend, [...deleteKeys, indexKey], dirPrefix);
32554
+ return {
32555
+ deletedRunIds: candidates,
32556
+ remainingRunIds,
32557
+ indexDeleted: indexExisted
32558
+ };
32559
+ }
32560
+ await this.rewriteIndexAfterPrune(indexKey, stackName, region, deletedSet);
32561
+ await deleteAndPurgeEventObjects(this.backend, deleteKeys, dirPrefix);
32562
+ return {
32563
+ deletedRunIds: candidates,
32564
+ remainingRunIds,
32565
+ indexDeleted: false
32566
+ };
32567
+ }
32568
+ async rewriteIndexAfterPrune(indexKey, stackName, region, deletedRunIds) {
32569
+ let raw;
32570
+ try {
32571
+ raw = await this.backend.getRawObject(indexKey);
32572
+ } catch {
32573
+ return;
32574
+ }
32575
+ if (raw === null) return;
32576
+ let parsed;
32577
+ try {
32578
+ parsed = JSON.parse(raw);
32579
+ } catch {
32580
+ return;
32581
+ }
32582
+ if (!Array.isArray(parsed.runs)) return;
32583
+ const remaining = parsed.runs.filter((r) => !deletedRunIds.has(r.runId));
32584
+ if (remaining.length === parsed.runs.length) return;
32585
+ const file = {
32586
+ indexVersion: 1,
32587
+ stackName,
32588
+ region,
32589
+ runs: remaining,
32590
+ lastModified: Date.now()
32591
+ };
32592
+ await this.backend.putRawObject(indexKey, JSON.stringify(file, null, 2));
32593
+ }
32594
+ };
32595
+
32596
+ //#endregion
32597
+ //#region src/deployment/kept-in-history.ts
32598
+ function keptInEarlierRecords(records) {
32599
+ const out = [];
32600
+ for (const record of records) for (const [logicalId, raw] of Object.entries(record.resources)) {
32601
+ if (raw === null || typeof raw !== "object") continue;
32602
+ const r = raw;
32603
+ if (typeof r.resourceType !== "string" || typeof r.physicalId !== "string") continue;
32604
+ if (r.physicalId === "") continue;
32605
+ if (!shouldRetainResource(r.deletionPolicy)) continue;
32606
+ out.push({
32607
+ logicalId,
32608
+ resourceType: r.resourceType,
32609
+ physicalId: r.physicalId,
32610
+ ...record.writtenAt !== void 0 && { keptAt: record.writtenAt }
32611
+ });
32612
+ }
32613
+ return out;
32614
+ }
32615
+ function keptInEventHistory(runs, stackName) {
32616
+ const made = /* @__PURE__ */ new Map();
32617
+ const out = [];
32618
+ for (const events of runs) for (const e of events) {
32619
+ if (e.stackName !== stackName || typeof e.logicalId !== "string") continue;
32620
+ if (typeof e.resourceType !== "string") continue;
32621
+ if (e.eventType === "RESOURCE_SUCCEEDED" && (e.operation === "CREATE" || e.operation === "UPDATE") && typeof e.physicalId === "string" && e.physicalId !== "") made.set(e.logicalId, {
32622
+ resourceType: e.resourceType,
32623
+ physicalId: e.physicalId
32624
+ });
32625
+ else if (e.eventType === "RESOURCE_RETAINED") {
32626
+ const m = made.get(e.logicalId);
32627
+ if (m !== void 0 && m.resourceType === e.resourceType) {
32628
+ const keptAt = Date.parse(e.timestamp);
32629
+ out.push({
32630
+ logicalId: e.logicalId,
32631
+ ...m,
32632
+ ...Number.isFinite(keptAt) && { keptAt }
32633
+ });
32634
+ }
32635
+ }
32636
+ }
32637
+ return out;
32638
+ }
32639
+ async function loadKeptInHistory(sources, stackName, region) {
32640
+ const [records, runs] = await Promise.all([sources.earlierStateResources(stackName, region).catch(() => []), readRecentRuns(sources, stackName, region)]);
32641
+ return [...keptInEarlierRecords(records), ...keptInEventHistory(runs, stackName)];
32642
+ }
32643
+ async function readRecentRuns(sources, stackName, region) {
32644
+ const runsOf = stackName.split("~")[0];
32645
+ return (async () => {
32646
+ const listed = (await sources.listRuns(runsOf, region)).slice(0, 20);
32647
+ return (await Promise.all(listed.map((r) => sources.readRunEvents(runsOf, region, r.runId).catch(() => null)))).filter((e) => e !== null).reverse();
32648
+ })().catch(() => []);
32649
+ }
32650
+ const RETAINED_SURVIVOR_REASON_PREFIX = "UpdateReplacePolicy: Retain kept the replacement's new";
32651
+ function rollbackRetainedInEventHistory(runs, stackName) {
32652
+ const out = [];
32653
+ for (const events of runs) for (const e of events) {
32654
+ if (e.stackName !== stackName || e.eventType !== "ROLLBACK_RESOURCE_SUCCEEDED") continue;
32655
+ if (typeof e.logicalId !== "string" || typeof e.resourceType !== "string") continue;
32656
+ if (typeof e.physicalId !== "string" || e.physicalId === "") continue;
32657
+ if (typeof e.reason !== "string" || !e.reason.startsWith("UpdateReplacePolicy: Retain kept the replacement's new")) continue;
32658
+ out.push({
32659
+ logicalId: e.logicalId,
32660
+ resourceType: e.resourceType,
32661
+ physicalId: e.physicalId
32662
+ });
32663
+ }
32664
+ return out;
32665
+ }
32666
+ async function loadRollbackRetainedCopies(sources, stackName, region) {
32667
+ return rollbackRetainedInEventHistory(await readRecentRuns(sources, stackName, region), stackName);
32668
+ }
32669
+
32670
+ //#endregion
32671
+ //#region src/provisioning/providers/idempotency-token.ts
32672
+ const PROCESS_NONCE = randomUUID();
32673
+ const inFlight$1 = /* @__PURE__ */ new Map();
32674
+ const generations = /* @__PURE__ */ new Map();
32675
+ const MAX_TRACKED_GENERATIONS = 1e5;
32676
+ const tokenKey = (scope, logicalId, maxLength, charset) => injectiveKey(scope, ambientRegion() ?? "", getCurrentStackName() ?? "", logicalId, maxLength, charset);
32677
+ const derive = (key, generation, maxLength, charset) => {
32678
+ const digest = createHash("sha256").update(`${PROCESS_NONCE}\u0000${key}\u0000${generation}`).digest("hex");
32679
+ return `cdkd${charset === "alphanumeric" ? "" : "-"}${digest}`.slice(0, maxLength);
32680
+ };
32681
+ function acquireIdempotencyToken(options) {
32682
+ const { scope, logicalId, maxLength = 64, charset = "default" } = options;
32683
+ const key = tokenKey(scope, logicalId, maxLength, charset);
32684
+ const existing = inFlight$1.get(key);
32685
+ const value = existing ?? derive(key, generations.get(key) ?? 0, maxLength, charset);
32686
+ if (existing === void 0) inFlight$1.set(key, value);
32687
+ return {
32688
+ value,
32689
+ release: () => {
32690
+ if (!inFlight$1.has(key)) return;
32691
+ inFlight$1.delete(key);
32692
+ const generation = (generations.get(key) ?? 0) + 1;
32693
+ if (!generations.has(key) && generations.size >= MAX_TRACKED_GENERATIONS) {
32694
+ const oldest = generations.keys().next();
32695
+ if (!oldest.done) generations.delete(oldest.value);
32696
+ }
32697
+ generations.set(key, generation);
32698
+ }
32699
+ };
32700
+ }
32701
+ let unscopedCreateTokensAllowed = false;
32702
+ const STACK_SCOPED_DIGEST_LENGTH = 12;
32703
+ function stackScopedCreateToken(options) {
32704
+ const { logicalId, immutableInputs, maxLength } = options;
32705
+ const stackName = getCurrentStackName();
32706
+ if (stackName === void 0 && !unscopedCreateTokensAllowed) throw new Error(`cdkd internal error: a create token for ${logicalId} was requested outside a stack scope (withStackScope), so it cannot be scoped to its stack`);
32707
+ const digest = createHash("sha256").update(injectiveKey(stackName ?? "", ambientRegion() ?? "", logicalId, ...immutableInputs.map((v) => JSON.stringify(v ?? null)))).digest("hex").slice(0, STACK_SCOPED_DIGEST_LENGTH);
32708
+ const prefixLength = maxLength - 5 - 1 - STACK_SCOPED_DIGEST_LENGTH;
32709
+ return `cdkd-${logicalId.slice(0, prefixLength)}-${digest}`;
32710
+ }
32711
+
32712
+ //#endregion
32713
+ //#region src/provisioning/providers/create-token-ledger.ts
32714
+ var CreateTokenLedger = class {
32715
+ doc;
32716
+ loadedAbsent = false;
32717
+ stale = false;
32718
+ startedThisDeploy = false;
32719
+ replaceRecordedLedger = false;
32720
+ stateRecorded = false;
32721
+ stateRecordedPersisted = false;
32722
+ unrecordedLetGo = /* @__PURE__ */ new Map();
32723
+ chain = Promise.resolve();
32724
+ logger = getLogger().child("CreateTokenLedger");
32725
+ store;
32726
+ stack;
32727
+ constructor(store, stack) {
32728
+ this.store = store;
32729
+ this.stack = stack;
32730
+ }
32731
+ serialized(fn) {
32732
+ const next = this.chain.then(fn, fn);
32733
+ this.chain = next.catch(() => void 0);
32734
+ return next;
32735
+ }
32736
+ beginDeploy(exists) {
32737
+ this.stateRecorded = exists;
32738
+ this.replaceRecordedLedger = !exists;
32739
+ }
32740
+ async current() {
32741
+ if (this.doc !== void 0 && !this.stale) return this.doc;
32742
+ if (this.loadedAbsent && !this.stale) return null;
32743
+ const loaded = await this.store.load();
32744
+ this.stale = false;
32745
+ this.loadedAbsent = loaded === null;
32746
+ if (loaded !== null && this.replaceRecordedLedger && loaded.stateRecorded === true) {
32747
+ this.logger.info("This stack has no state record but its create-token ledger outlived an earlier one; starting a fresh ledger, so this deploy does not take over resources that deployment left.");
32748
+ this.doc = emptyCreateTokenLedger(randomUUID());
32749
+ this.startedThisDeploy = true;
32750
+ return this.doc;
32751
+ }
32752
+ this.doc = loaded ?? void 0;
32753
+ return loaded;
32754
+ }
32755
+ async persist(doc) {
32756
+ if (this.stateRecorded) doc.stateRecorded = true;
32757
+ await this.store.save(doc);
32758
+ this.replaceRecordedLedger = false;
32759
+ if (doc.stateRecorded === true) this.stateRecordedPersisted = true;
32760
+ }
32761
+ reserve(logicalId, base, derive, firstSentAt) {
32762
+ return this.serialized(async () => {
32763
+ if (this.unrecordedLetGo.has(logicalId)) throw this.wrapLedgerError(logicalId, this.unrecordedLetGo.get(logicalId));
32764
+ const earlier = this.doc?.sent[logicalId];
32765
+ if (earlier !== void 0 && earlier.base === base) return {
32766
+ value: earlier.token,
32767
+ earlierFirstSentAt: earlier.firstSentAt,
32768
+ ledgerStartedThisDeploy: false
32769
+ };
32770
+ let doc;
32771
+ try {
32772
+ const loaded = await this.current();
32773
+ if (loaded === null) {
32774
+ doc = emptyCreateTokenLedger(randomUUID());
32775
+ this.doc = doc;
32776
+ this.startedThisDeploy = true;
32777
+ } else {
32778
+ doc = loaded;
32779
+ const recorded = doc.sent[logicalId];
32780
+ if (recorded !== void 0 && recorded.base === base) return {
32781
+ value: recorded.token,
32782
+ earlierFirstSentAt: recorded.firstSentAt,
32783
+ ledgerStartedThisDeploy: false
32784
+ };
32785
+ }
32786
+ } catch (error) {
32787
+ this.stale = true;
32788
+ throw this.wrapLedgerError(logicalId, error);
32789
+ }
32790
+ const value = derive(doc.nonce);
32791
+ const previous = doc.sent[logicalId];
32792
+ doc.sent[logicalId] = {
32793
+ base,
32794
+ token: value,
32795
+ firstSentAt
32796
+ };
32797
+ try {
32798
+ await this.persist(doc);
32799
+ } catch (error) {
32800
+ if (previous === void 0) delete doc.sent[logicalId];
32801
+ else doc.sent[logicalId] = previous;
32802
+ this.stale = true;
32803
+ throw this.wrapLedgerError(logicalId, error);
32804
+ }
32805
+ return {
32806
+ value,
32807
+ earlierFirstSentAt: void 0,
32808
+ ledgerStartedThisDeploy: this.startedThisDeploy
32809
+ };
32810
+ });
32811
+ }
32812
+ wrapLedgerError(logicalId, error) {
32813
+ const stack = this.stack === void 0 ? "this stack" : `stack ${displayStackName(this.stack.stackName)} (${displayIdent(this.stack.region)})`;
32814
+ return new Error(safeMsg`Could not read or write the create-token ledger of ${stack} (${describeAwsFailure(error).summary}), so the create of ${logicalId} was not sent: the token it would send without the ledger is not the one a later deploy would send, and the resource could leak. Re-run the deploy once the ledger can be read and written.`, { cause: error });
32815
+ }
32816
+ rotate(logicalId) {
32817
+ return this.serialized(async () => {
32818
+ try {
32819
+ const doc = await this.current();
32820
+ if (doc === null) return;
32821
+ doc.nonce = randomUUID();
32822
+ delete doc.sent[logicalId];
32823
+ await this.persist(doc);
32824
+ this.unrecordedLetGo.delete(logicalId);
32825
+ } catch (error) {
32826
+ this.stale = true;
32827
+ this.unrecordedLetGo.set(logicalId, error);
32828
+ this.logger.warn(safeMsg`Could not record in this stack's create-token ledger that ${logicalId} was kept (${describeAwsFailure(error).summary}). A create of ${logicalId} in this deploy is refused; a later deploy's create of it may be handed the kept resource back, so check the resource it records against the one this deploy kept.`);
32829
+ }
32830
+ });
32831
+ }
32832
+ sentEntries() {
32833
+ return this.serialized(async () => {
32834
+ try {
32835
+ return { ...(await this.current())?.sent ?? {} };
32836
+ } catch (error) {
32837
+ this.stale = true;
32838
+ throw error;
32839
+ }
32840
+ });
32841
+ }
32842
+ recordSent(entries, firstSentAt) {
32843
+ return this.serialized(async () => {
32844
+ try {
32845
+ let doc = await this.current();
32846
+ if (doc === null) {
32847
+ doc = emptyCreateTokenLedger(randomUUID());
32848
+ this.doc = doc;
32849
+ this.startedThisDeploy = true;
32850
+ }
32851
+ let changed = false;
32852
+ for (const entry of entries) {
32853
+ const recorded = doc.sent[entry.logicalId];
32854
+ if (recorded?.base === entry.base && recorded.token === entry.token) continue;
32855
+ doc.sent[entry.logicalId] = {
32856
+ base: entry.base,
32857
+ token: entry.token,
32858
+ firstSentAt
32859
+ };
32860
+ changed = true;
32861
+ }
32862
+ if (changed) await this.persist(doc);
32863
+ } catch (error) {
32864
+ this.stale = true;
32865
+ throw error;
32866
+ }
32867
+ });
32868
+ }
32869
+ abandonedAt() {
32870
+ return this.serialized(async () => (await this.current())?.abandonedAt);
32871
+ }
32872
+ noteAbandoned(at) {
32873
+ return this.serialized(async () => {
32874
+ try {
32875
+ const doc = await this.current();
32876
+ if (doc === null) return;
32877
+ if (!Object.values(doc.sent).some((e) => e.base.startsWith("adopt-by-name:")) || doc.abandonedAt !== void 0 && doc.abandonedAt >= at) return;
32878
+ doc.abandonedAt = at;
32879
+ await this.persist(doc);
32880
+ } catch (error) {
32881
+ this.stale = true;
32882
+ throw error;
32883
+ }
32884
+ });
32885
+ }
32886
+ settleAdoptingCreates(drop, failedAt) {
32887
+ return this.serialized(async () => {
32888
+ try {
32889
+ const doc = await this.current();
32890
+ if (doc === null) return;
32891
+ const adopting = (id) => doc.sent[id]?.base.startsWith(ADOPTING_CREATE_BASE) === true;
32892
+ let changed = false;
32893
+ for (const id of drop) {
32894
+ if (!adopting(id)) continue;
32895
+ delete doc.sent[id];
32896
+ changed = true;
32897
+ }
32898
+ for (const [id, at] of failedAt) {
32899
+ const entry = adopting(id) ? doc.sent[id] : void 0;
32900
+ if (entry === void 0 || entry.failedAt !== void 0) continue;
32901
+ entry.failedAt = at;
32902
+ changed = true;
32903
+ }
32904
+ if (changed) await this.persist(doc);
32905
+ } catch (error) {
32906
+ this.stale = true;
32907
+ throw error;
32908
+ }
32909
+ });
32292
32910
  }
32293
32911
  prefetch() {
32294
32912
  return this.serialized(async () => {
@@ -32914,30 +33532,6 @@ async function createReplicationGroupFinalSnapshot(client, replicationGroupId, l
32914
33532
  });
32915
33533
  }
32916
33534
 
32917
- //#endregion
32918
- //#region src/types/deployment-events.ts
32919
- function extractDeploymentEventError(err) {
32920
- if (!(err instanceof Error)) return {
32921
- name: "UnknownError",
32922
- message: safeStringify$1(err)
32923
- };
32924
- const result = {
32925
- name: err.name || "Error",
32926
- message: err.message
32927
- };
32928
- let current = err;
32929
- for (let depth = 0; depth < 10 && current instanceof Error; depth++) {
32930
- const maybeAws = current;
32931
- if (maybeAws.$metadata !== void 0) {
32932
- const code = maybeAws.Code ?? maybeAws.name;
32933
- if (code) result.awsErrorCode = code;
32934
- if (maybeAws.$metadata.requestId) result.requestId = maybeAws.$metadata.requestId;
32935
- }
32936
- current = current.cause;
32937
- }
32938
- return result;
32939
- }
32940
-
32941
33535
  //#endregion
32942
33536
  //#region src/deployment/rollback-executor/types.ts
32943
33537
  const NESTED_STACK_RESOURCE_TYPE$1 = "AWS::CloudFormation::Stack";
@@ -33133,15 +33727,20 @@ function retainedSurvivorMessages(logicalId, resourceType, survivorPhysicalId, s
33133
33727
  const clause = mask(stateClause);
33134
33728
  return {
33135
33729
  warn: ` ⚠ ${safe(logicalId)} (${safe(resourceType)}) has UpdateReplacePolicy: Retain — the replacement's new physical resource (${displaySafe(survivorId)}) is RETAINED by this rollback and is no longer tracked by cdkd: it keeps running and incurring cost, and destroying the stack will not remove it. Delete it yourself once you no longer need it. ${displaySafe(clause)}`,
33136
- reason: `UpdateReplacePolicy: Retain kept the replacement's new ${resourceType} (${survivorId}); it is live, still billing, and no longer tracked by cdkd. ${clause}`
33730
+ reason: `${RETAINED_SURVIVOR_REASON_PREFIX} ${resourceType} (${survivorId}); it is live, still billing, and no longer tracked by cdkd. ${clause}`
33137
33731
  };
33138
33732
  }
33139
33733
 
33140
33734
  //#endregion
33141
33735
  //#region src/deployment/rollback-executor/names.ts
33142
33736
  function replayPrefixScope(input, logger, mask, restoring) {
33737
+ const generation = input.nameGeneration;
33738
+ const inGeneration = (fn) => generation === void 0 ? fn() : withNameGeneration({
33739
+ logicalId: input.logicalId,
33740
+ generation
33741
+ }, fn);
33143
33742
  const choice = replayPrefixChoice(input);
33144
- if (choice.kind === "not-applicable") return (fn) => fn();
33743
+ if (choice.kind === "not-applicable") return (fn) => inGeneration(fn);
33145
33744
  const setting = (skip) => skip ? "SKIPPED" : "KEPT";
33146
33745
  const subject = `${safe(input.logicalId)} (${safe(input.resourceType)})`;
33147
33746
  const declared = choice.declared;
@@ -33161,7 +33760,7 @@ function replayPrefixScope(input, logger, mask, restoring) {
33161
33760
  logger.warn(mask(` ⚠ ${subject}: cdkd cannot tell which user-supplied-name prefix setting created the old resource (` + (withheld ? `its physical id is withheld, as its name is secret-derived or unreadable` : shown(input.physicalId)) + `) — ` + (choice.names === void 0 ? unreadable !== void 0 ? `cdkd cannot read ${unreadable} to compare` : `cdkd cannot derive the names its ${prop} takes to compare` : secretDerived ? `its ${prop} is secret-derived, and neither name it derives matches` : `its ${prop} derives to ${quoted(choice.names.skipped)} (prefix skipped) or ${quoted(choice.names.kept)} (prefix kept), and it is neither`) + `; re-creating it with the stack-name prefix ${setting(choice.skipPrefix)}, as the failed deploy ran, so it may come back under a different physical name.`));
33162
33761
  }
33163
33762
  const skip = choice.skipPrefix;
33164
- return (fn) => withSkipPrefix(skip, fn);
33763
+ return (fn) => withSkipPrefix(skip, () => inGeneration(fn));
33165
33764
  }
33166
33765
  function createOpMasker(logger, secrets, deployBag) {
33167
33766
  const base = createMaskedLogSinks(logger, deployBag === void 0 ? (text) => maskSecretsInText(text, secrets) : createUnionSecretMasker([secrets, deployBag]));
@@ -34257,6 +34856,149 @@ function maskedRecordRemedyFor(reads, resources, causeScoped = false) {
34257
34856
  return [parts.join(" "), ...commandLines].join("\n");
34258
34857
  }
34259
34858
 
34859
+ //#endregion
34860
+ //#region src/deployment/delete-guard-scope.ts
34861
+ const storage$1 = new AsyncLocalStorage();
34862
+ function collectDeleteGuards(sink, fn) {
34863
+ return storage$1.run(sink, fn);
34864
+ }
34865
+ function reportDeleteGuards(result, target) {
34866
+ const sink = storage$1.getStore();
34867
+ if (!sink) return;
34868
+ for (const guard of deleteIndeterminateGuards(result)) sink.push({
34869
+ guard: guard.guard,
34870
+ reason: guard.reason,
34871
+ physicalId: target.physicalId,
34872
+ resourceType: target.resourceType,
34873
+ ...target.provisionedBy && { provisionedBy: target.provisionedBy }
34874
+ });
34875
+ }
34876
+
34877
+ //#endregion
34878
+ //#region src/deployment/superseded-resources.ts
34879
+ function supersededResourcesOf(state) {
34880
+ const rows = state?.supersededResources;
34881
+ if (!Array.isArray(rows)) return [];
34882
+ return rows.filter(isSupersededResourceRecord);
34883
+ }
34884
+ function isSupersededResourceRecord(row) {
34885
+ if (typeof row !== "object" || row === null) return false;
34886
+ const entry = row;
34887
+ const record = entry["state"];
34888
+ if (typeof entry["logicalId"] !== "string") return false;
34889
+ if (typeof record !== "object" || record === null) return false;
34890
+ const { physicalId, resourceType, properties } = record;
34891
+ return typeof physicalId === "string" && physicalId !== "" && typeof resourceType === "string" && resourceType !== "" && typeof properties === "object" && properties !== null && !Array.isArray(properties);
34892
+ }
34893
+ function supersededHolderOf(entry, resources) {
34894
+ const holders = Object.values(resources).filter((record) => typeof record === "object" && record !== null && record.physicalId === entry.state.physicalId);
34895
+ return holders.find((record) => record.resourceType === entry.state.resourceType) ?? holders[0];
34896
+ }
34897
+ function isSupersededHeldBySameType(entry, resources) {
34898
+ return Object.values(resources).some((record) => typeof record === "object" && record !== null && record.physicalId === entry.state.physicalId && record.resourceType === entry.state.resourceType);
34899
+ }
34900
+ function supersededHeldByOtherTypeWarning(entry, holderType) {
34901
+ return `the superseded ${entry.state.resourceType} of ${entry.logicalId} (${entry.state.physicalId}) was not deleted: a ${holderType} record of this stack has the same physical id, and cdkd never deletes an id a record names. If the ${entry.state.resourceType} still exists, delete it by hand; it is no longer tracked in state.`;
34902
+ }
34903
+ function addSuperseded(host, entry) {
34904
+ host.supersededResources = [...(Array.isArray(host.supersededResources) ? host.supersededResources : []).filter((row) => !isSupersededResourceRecord(row) || row.state.physicalId !== entry.state.physicalId || row.state.resourceType !== entry.state.resourceType), entry];
34905
+ }
34906
+ function canRecordSuperseded(record, deleteContext) {
34907
+ if (deleteContext.noEchoDeleteValues !== void 0) return false;
34908
+ if (deleteContext.inlinePolicyClaimed !== void 0) return false;
34909
+ if (Array.isArray(record.noEchoLeaves) && record.noEchoLeaves.length > 0) return false;
34910
+ return !INLINE_POLICY_TYPES.has(record.resourceType);
34911
+ }
34912
+ const INLINE_POLICY_TYPES = /* @__PURE__ */ new Set([
34913
+ "AWS::IAM::Policy",
34914
+ "AWS::IAM::RolePolicy",
34915
+ "AWS::IAM::UserPolicy",
34916
+ "AWS::IAM::GroupPolicy"
34917
+ ]);
34918
+ function removeSuperseded(host, entry) {
34919
+ if (!Array.isArray(host.supersededResources)) return;
34920
+ const remaining = host.supersededResources.filter((row) => row !== entry);
34921
+ if (remaining.length === 0) delete host.supersededResources;
34922
+ else host.supersededResources = remaining;
34923
+ }
34924
+ function supersededAfterRollback(base, pending) {
34925
+ const added = Array.isArray(pending.supersededResources) ? pending.supersededResources : [];
34926
+ if (added.length === 0) return {};
34927
+ return { supersededResources: [...Array.isArray(base.supersededResources) ? base.supersededResources : [], ...added] };
34928
+ }
34929
+ const SUPERSEDED_KEPT_REMEDY = "cdkd keeps it in state: the next `cdkd deploy` of this stack retries the delete, and `cdkd destroy` deletes it";
34930
+ function withoutSuperseded(state) {
34931
+ const { supersededResources: _dropped, ...rest } = state;
34932
+ return rest;
34933
+ }
34934
+ async function deleteSupersededEntry(entry, input) {
34935
+ const record = entry.state;
34936
+ let result;
34937
+ try {
34938
+ const send = () => input.provider.delete(entry.logicalId, record.physicalId, record.resourceType, record.properties, {
34939
+ expectedRegion: input.region,
34940
+ ...entry.forceDataDelete === true && { forceDataDelete: true },
34941
+ ...entry.finalSnapshotIdentifier !== void 0 && { finalSnapshotIdentifier: entry.finalSnapshotIdentifier },
34942
+ deletionPolicy: replacementDeletePolicy(entry.updateReplacePolicy),
34943
+ ...entry.skipFinalSnapshot === true && { skipFinalSnapshot: true },
34944
+ ...input.removeProtection === true && { removeProtection: true },
34945
+ ...input.stackDestroy === true && { stackDestroy: true },
34946
+ recordedAttributes: record.attributes,
34947
+ recordedNoEchoLeaves: record.noEchoLeaves,
34948
+ maskSecrets: input.maskSecrets
34949
+ });
34950
+ result = input.once === true ? await send() : await retryWhileInUse(send, input.stop);
34951
+ } catch (error) {
34952
+ return {
34953
+ kind: "failed",
34954
+ error
34955
+ };
34956
+ }
34957
+ reportDeleteGuards(result, {
34958
+ physicalId: record.physicalId,
34959
+ resourceType: record.resourceType,
34960
+ provisionedBy: record.provisionedBy
34961
+ });
34962
+ const reason = deleteSkipReason(result);
34963
+ return reason === void 0 ? { kind: "deleted" } : {
34964
+ kind: "notDeleted",
34965
+ reason
34966
+ };
34967
+ }
34968
+ const supersededInUseRetry = {
34969
+ attempts: 6,
34970
+ delayMs: 1e4
34971
+ };
34972
+ const IN_USE_ERROR_NAMES = /* @__PURE__ */ new Set([
34973
+ "ResourceInUseException",
34974
+ "ResourceInUse",
34975
+ "DependencyViolation"
34976
+ ]);
34977
+ const IN_USE_CONFLICT = /\bare using it\b/;
34978
+ function isInUseRefusal(error) {
34979
+ for (let current = error, depth = 0; current !== void 0 && depth < 8; depth++) {
34980
+ if (typeof current !== "object" || current === null) return false;
34981
+ const { name, message } = current;
34982
+ if (typeof name === "string" && IN_USE_ERROR_NAMES.has(name)) return true;
34983
+ if (name === "ConflictException" && typeof message === "string" && IN_USE_CONFLICT.test(message)) return true;
34984
+ current = current.cause;
34985
+ }
34986
+ return false;
34987
+ }
34988
+ async function retryWhileInUse(send, stop) {
34989
+ for (let attempt = 1;; attempt++) try {
34990
+ return await send();
34991
+ } catch (error) {
34992
+ if (attempt >= supersededInUseRetry.attempts || stop() || !isInUseRefusal(error)) throw error;
34993
+ await new Promise((resolve) => setTimeout(resolve, supersededInUseRetry.delayMs));
34994
+ if (stop()) throw error;
34995
+ }
34996
+ }
34997
+ function holdsSupersededResources(record) {
34998
+ const rows = record.supersededResources;
34999
+ return rows !== void 0 && (!Array.isArray(rows) || rows.length > 0);
35000
+ }
35001
+
34260
35002
  //#endregion
34261
35003
  //#region src/utils/colors.ts
34262
35004
  const codes = {
@@ -34283,6 +35025,16 @@ function withReplayDeletePrinting(scope, fn) {
34283
35025
  const deployBag = scope.ctx.logOnlyNeedlesFor?.(scope.op.logicalId);
34284
35026
  return withPrintingSecrets(scope.secrets, () => deployBag === void 0 ? fn() : withPrintingSecrets(deployBag, fn));
34285
35027
  }
35028
+ function noteRolledBackCreate(scope, op, record) {
35029
+ if (op.changeType !== "CREATE") return;
35030
+ const create = {
35031
+ logicalId: op.logicalId,
35032
+ record,
35033
+ ...op.nameGeneration !== void 0 && { nameGeneration: op.nameGeneration }
35034
+ };
35035
+ scope.result.rolledBackCreates.push(create);
35036
+ scope.onRolledBackCreate?.(create);
35037
+ }
34286
35038
 
34287
35039
  //#endregion
34288
35040
  //#region src/deployment/rollback-executor/replaced-survivor.ts
@@ -34332,24 +35084,6 @@ async function readSurvivor(s, oldType) {
34332
35084
  }
34333
35085
  }
34334
35086
 
34335
- //#endregion
34336
- //#region src/deployment/delete-guard-scope.ts
34337
- const storage$1 = new AsyncLocalStorage();
34338
- function collectDeleteGuards(sink, fn) {
34339
- return storage$1.run(sink, fn);
34340
- }
34341
- function reportDeleteGuards(result, target) {
34342
- const sink = storage$1.getStore();
34343
- if (!sink) return;
34344
- for (const guard of deleteIndeterminateGuards(result)) sink.push({
34345
- guard: guard.guard,
34346
- reason: guard.reason,
34347
- physicalId: target.physicalId,
34348
- resourceType: target.resourceType,
34349
- ...target.provisionedBy && { provisionedBy: target.provisionedBy }
34350
- });
34351
- }
34352
-
34353
35087
  //#endregion
34354
35088
  //#region src/deployment/stack-records-scope.ts
34355
35089
  async function awaitInFlightSibling(view, self, sibling) {
@@ -34394,11 +35128,92 @@ function replayStackRecordsView(records, orphanLogicalIds = /* @__PURE__ */ new
34394
35128
  };
34395
35129
  }
34396
35130
 
35131
+ //#endregion
35132
+ //#region src/deployment/deploy-engine/name-collision.ts
35133
+ function replacementNameOrigin(logicalId, physicalId) {
35134
+ if (looksLikeCdkdGeneratedName(physicalId, logicalId, getCurrentStackName(), getCurrentStackInstanceId())) return {
35135
+ descriptor: `The physical name (${physicalId}) was GENERATED by cdkd from the construct's logical id rather than declared in your template, and that derivation has no random component — so the new resource asks for exactly the name the existing one still holds`,
35136
+ remedy: "Either give the resource an explicit physical name in your CDK code, or rename the CONSTRUCT (its id feeds the generated name — note that this replaces the resource rather than renaming it in place)"
35137
+ };
35138
+ return {
35139
+ descriptor: `The resource has a user-supplied physical name (${physicalId})`,
35140
+ remedy: "Either rename the resource in your CDK code (a fresh name lets the safe create-first order proceed)"
35141
+ };
35142
+ }
35143
+ function generatedReplacementHolderAdvice(input) {
35144
+ if (input.change?.generated !== true) return "";
35145
+ const name = displaySafe(input.mask(input.change.desiredName), { asciiOnly: true });
35146
+ const old = displaySafe(input.mask(input.oldPhysicalId), { asciiOnly: true });
35147
+ const lead = ` The colliding name ${isInertUnquoted(name) ? name : "the generated name"} is the one this replacement generates for its next name generation, so its holder is most likely the new resource an interrupted earlier attempt of this replacement created. If it is, `;
35148
+ const tail = `, or delete it and re-run the deploy.`;
35149
+ if (input.stackName.includes("~")) return lead + `adopt it through the top-level stack with \`cdkd import\` (then delete ${old} by hand)` + tail;
35150
+ const adopt = pasteableCommand("cdkd import", [{
35151
+ value: input.stackName,
35152
+ hole: "stack"
35153
+ }, {
35154
+ flag: "--resource",
35155
+ value: `${input.logicalId}=${input.change.desiredName}`,
35156
+ hole: "logicalId=physicalId"
35157
+ }]);
35158
+ const command = input.mask(adopt.command) === adopt.command ? adopt.command : void 0;
35159
+ return lead + (command === void 0 ? `adopt it with \`cdkd import\`` : `adopt it with \`${command}\` (using its physical id where the type records another)`) + ` and then delete ${old} by hand` + tail;
35160
+ }
35161
+ function orphanedNameCollisionAdvice(providerRegistry, changeType, logicalId, error) {
35162
+ if (changeType !== "CREATE") return void 0;
35163
+ if (!(error instanceof ProvisioningError)) return void 0;
35164
+ const physicalId = error.physicalId;
35165
+ if (!physicalId) return void 0;
35166
+ const replayed = isReplayedNameCollisionFrom(error, logicalId, (link) => isAuxiliaryMarkOf(link, RETRY_AUXILIARY_OWNER));
35167
+ if (!replayed && !isNameCollisionErrorFrom(error, logicalId)) return void 0;
35168
+ const stackName = getCurrentStackName();
35169
+ if (!looksLikeCdkdGeneratedName(physicalId, logicalId, stackName, getCurrentStackInstanceId())) return;
35170
+ if (!stackName) return void 0;
35171
+ const safeId = displaySafe(physicalId, { asciiOnly: true });
35172
+ const safeStack = displaySafe(stackName, { asciiOnly: true });
35173
+ const safeLogicalId = displaySafe(logicalId, { asciiOnly: true });
35174
+ const idShown = isInertUnquoted(safeId);
35175
+ const shownId = idShown ? safeId : "a name that cannot be shown safely here";
35176
+ const shownLogicalId = isInertUnquoted(safeLogicalId) ? safeLogicalId : "A resource whose logical id cannot be shown safely here";
35177
+ const diagnosis = replayed ? `${shownLogicalId}: the name AWS reports as taken (${shownId}) is most likely held by a resource THIS create made. An earlier attempt of it ended without a clear verdict (a server error, a dropped connection or a timeout, or a failure after the create call itself may have succeeded), and the retry then found the name taken. A dropped connection can also happen before the request reached AWS, so the holder may be another resource: confirm it is yours before deleting or importing it. cdkd records a resource only once its create succeeds, so this one is in no state file: no rollback or cdkd destroy will remove it, and re-running collides with it again.` : `${shownLogicalId}: the name AWS reports as taken (${shownId}) is one cdkd DERIVED from the logical id, and that derivation has no random component — so this is most likely a resource an earlier cdkd run left behind. A rollback leaves a resource carrying DeletionPolicy: Retain in AWS and drops it from state, as CloudFormation does; what differs is the name. CloudFormation would generate a fresh one for an unnamed resource and redeploy clean, whereas cdkd asks again for the name the orphan still holds — so re-running does not clear this.`;
35178
+ const deleteArm = `If it is not a resource you want to keep, delete ${idShown ? safeId : "it"} in AWS — after confirming it holds nothing you need${replayed ? "" : ", since Retain is what kept it"} — and re-deploy.`;
35179
+ let canImport;
35180
+ try {
35181
+ canImport = typeof providerRegistry.getProvider(error.resourceType).import === "function";
35182
+ } catch {
35183
+ canImport = false;
35184
+ }
35185
+ const commandNamesTheRightResource = safeId === physicalId && safeStack === stackName && safeLogicalId === logicalId;
35186
+ const importableTarget = !stackName.includes("~");
35187
+ if (!canImport || !commandNamesTheRightResource || !importableTarget) return `${diagnosis} ${!canImport ? `cdkd cannot adopt ${displaySafe(error.resourceType, { asciiOnly: true })} back into state (its provider implements no import)` : !importableTarget ? `this is a nested-stack child, whose stack name cdkd import cannot resolve` : `cdkd cannot render an import command that provably names this resource`}, so the way forward is to delete it. ${deleteArm}`;
35188
+ const adopt = pasteableCommand("cdkd import", [{
35189
+ value: stackName,
35190
+ hole: "stack"
35191
+ }, {
35192
+ flag: "--resource",
35193
+ value: `${logicalId}=${physicalId}`,
35194
+ hole: "logicalId=physicalId"
35195
+ }]);
35196
+ return `${diagnosis} To recover, adopt it back into state instead of re-creating it, with the command below (a selective import merges into existing state and needs no --force while the resource is absent from it). CONFIRM IT IS YOURS FIRST — a name cdkd derives is predictable, so for a globally-namespaced type it can belong to another account, and the same stack deployed in another region derives the same name. ${deleteArm}${adopt.withheld.length === 0 ? "" : " The command below prints a quoted hole in place of a value cdkd will not name on a command line; replace it whole, quotes included, with your stack name or '<logicalId>=<name>', shell-quoted."}\nAdopt with: ${adopt.command}`;
35197
+ }
35198
+ function isReplacementNameRefusal(error, logicalId) {
35199
+ let current = error;
35200
+ for (let depth = 0; depth < 10 && current != null; depth++) {
35201
+ if (typeof current !== "object") return false;
35202
+ const linkId = current.logicalId;
35203
+ if (typeof linkId === "string" && linkId !== logicalId) return false;
35204
+ if (current instanceof CdkdError && current.code === "NAMED_REPLACEMENT_COLLISION") return true;
35205
+ current = current.cause;
35206
+ }
35207
+ return false;
35208
+ }
35209
+
34397
35210
  //#endregion
34398
35211
  //#region src/deployment/deploy-engine/replacement.ts
34399
35212
  var ReplacementRunState = class {
34400
35213
  replacedDeleteSkips = /* @__PURE__ */ new Map();
34401
35214
  deferredReplacedDeletes = void 0;
35215
+ supersededHost = void 0;
35216
+ supersededEntries = /* @__PURE__ */ new Map();
34402
35217
  retainedOldOnReplacement = /* @__PURE__ */ new Set();
34403
35218
  replacedOldMaySurvive = /* @__PURE__ */ new Map();
34404
35219
  recreatedUnderSameId = /* @__PURE__ */ new Set();
@@ -34407,6 +35222,7 @@ var ReplacementRunState = class {
34407
35222
  updateWasReplaced = /* @__PURE__ */ new Map();
34408
35223
  inPlaceCreateOnlyPaths = /* @__PURE__ */ new Map();
34409
35224
  oldDeletedBeforeCreate = /* @__PURE__ */ new Set();
35225
+ attemptedNameGeneration = /* @__PURE__ */ new Map();
34410
35226
  };
34411
35227
  function replacementDeleteContext(deps, updateReplacePolicy) {
34412
35228
  return {
@@ -34452,15 +35268,19 @@ async function replaceDeleteFirstAndRecreate(run, deps, logicalId, resourceType,
34452
35268
  if (replaceSkipReason !== void 0) throw new Error(deleteSkippedMessage(logicalId, currentResource.physicalId, replaceSkipReason, "during the --replace delete-first fallback"));
34453
35269
  deps.logger.info(safeMsg` ${green("✓")} Old resource deleted`);
34454
35270
  run.oldDeletedBeforeCreate.add(logicalId);
35271
+ await oldDeleteProvider.waitForNameReleased?.(logicalId, currentResource.physicalId, oldResourceType, {
35272
+ isInterrupted: () => deps.userInterrupted(),
35273
+ maskSecrets: (text) => maskSecretsInText(text, secrets)
35274
+ });
34455
35275
  deps.logger.info(safeMsg` Re-creating ${logicalId}...`);
34456
35276
  try {
34457
- return await withRetry(() => deps.withRetry(() => withCurrentResourceSecrets(secrets, () => replaceProvider.create(logicalId, resourceType, replaceProps, createContext)), logicalId, void 0, void 0, replaceProvider), logicalId, {
35277
+ return await withRetry(() => deps.withRetry(() => withCurrentResourceSecrets(secrets, () => replaceProvider.create(logicalId, resourceType, replaceProps, createContext)), logicalId, void 0, void 0, replaceProvider, true), logicalId, {
34458
35278
  maxRetries: 8,
34459
35279
  initialDelayMs: 2e3,
34460
35280
  maxDelayMs: 1e4,
34461
35281
  logger: deps.maskingRetryLoggerFor(secrets),
34462
- isInterrupted: () => deps.interrupted(),
34463
- onInterrupted: () => new InterruptedError(deps.interruptCause() ?? "user"),
35282
+ isInterrupted: () => deps.userInterrupted(),
35283
+ onInterrupted: () => new InterruptedError("user"),
34464
35284
  isRetryable: isRecreateRetryableError
34465
35285
  });
34466
35286
  } catch (recreateError) {
@@ -34494,6 +35314,14 @@ async function checkedReplacementNameChange(deps, input) {
34494
35314
  };
34495
35315
  const adopts = replacementCreateAdoptsName(resourceType, input.createdVia);
34496
35316
  const typeChanged = input.oldResourceType !== resourceType;
35317
+ const generatedMove = replacementGeneratedNameMoves({
35318
+ oldResourceType: input.oldResourceType,
35319
+ newResourceType: resourceType,
35320
+ createdVia: input.createdVia,
35321
+ desiredProperties: input.desiredProperties,
35322
+ physicalId: currentResource.physicalId,
35323
+ logicalId
35324
+ });
34497
35325
  const change = replacementRequestsDifferentName(question) ?? (adopts || replacementOrderIsCaseSensitive(resourceType) ? replacementRequestsDifferentName({
34498
35326
  ...question,
34499
35327
  caseSensitive: true
@@ -34504,8 +35332,9 @@ async function checkedReplacementNameChange(deps, input) {
34504
35332
  desiredProperties: input.desiredProperties,
34505
35333
  physicalId: currentResource.physicalId,
34506
35334
  logicalId
34507
- }) ?? (adopts && typeChanged ? typeChangeNameQuestion(resourceType, input) : void 0);
35335
+ }) ?? (adopts && typeChanged ? typeChangeNameQuestion(resourceType, input) : void 0) ?? generatedMove;
34508
35336
  if (change === void 0) return void 0;
35337
+ if (change === generatedMove && deps.generatedNameGuarded(logicalId)) return change;
34509
35338
  const probe = replacementNameProbe({
34510
35339
  resourceType,
34511
35340
  createdVia: input.createdVia,
@@ -34535,7 +35364,8 @@ async function checkedReplacementNameChange(deps, input) {
34535
35364
  }
34536
35365
  if (found === null) return change;
34537
35366
  if (!typeChanged && probeFoundSameId(resourceType, found.physicalId, currentResource.physicalId)) return;
34538
- throw markNonRetryable(new CdkdError(maskName(`${subject} requires replacement, and another existing resource (${displaySafe(found.physicalId)}) already holds the name it asks for. ${renderReplacementNameChange(change, input.createProps)}. Since ${adoptsText}, creating the replacement would take that resource over and record it as this stack's. Nothing was created or deleted. Choose a name no other resource holds, or delete the resource holding it if it is yours.`), "NAMED_REPLACEMENT_COLLISION"));
35367
+ const remedy = (await deps.rollbackRetainedCopies?.(input.stackName).catch(() => []) ?? []).some((copy) => copy.logicalId === logicalId && copy.resourceType === resourceType && probeFoundSameId(resourceType, copy.physicalId, found.physicalId)) ? `This stack's event history records that a rolled-back deploy kept ${displaySafe(logicalId)}'s replacement under this name because of UpdateReplacePolicy: Retain, as CloudFormation also does, untracked by cdkd. If the holder is that copy and you no longer need it, delete it; otherwise choose another name.` : `Choose a name no other resource holds, or delete the resource holding it if it is yours.`;
35368
+ throw markNonRetryable(new CdkdError(maskName(`${subject} requires replacement, and another existing resource (${displaySafe(found.physicalId)}) already holds the name it asks for. ${renderReplacementNameChange(change, input.createProps)}. Since ${adoptsText}, creating the replacement would take that resource over and record it as this stack's. Nothing was created or deleted. ${remedy}`), "NAMED_REPLACEMENT_COLLISION"));
34539
35369
  }
34540
35370
  async function createFirstThenDeleteOld(run, deps, input) {
34541
35371
  const { logicalId, resourceType, currentResource, secrets } = input;
@@ -34545,11 +35375,23 @@ async function createFirstThenDeleteOld(run, deps, input) {
34545
35375
  const maskName = maskRewrittenSentName(resourceType, input.createProps, logicalId, (text) => maskSecretsInText(text, secrets));
34546
35376
  let createResult;
34547
35377
  try {
34548
- createResult = await deps.withRetry(() => withCurrentResourceSecrets(secrets, () => input.createProvider.create(logicalId, resourceType, input.createProps, { maskSecrets: createSecretMasker(secrets) })), logicalId, void 0, void 0, input.createProvider);
35378
+ createResult = await deps.guardedReplacementCreate({
35379
+ logicalId,
35380
+ resourceType,
35381
+ stackName: input.stackName,
35382
+ createdVia: input.createdVia,
35383
+ resolvedProps: input.resolvedProps
35384
+ }, () => deps.withRetry(() => withCurrentResourceSecrets(secrets, () => input.createProvider.create(logicalId, resourceType, input.createProps, { maskSecrets: createSecretMasker(secrets) })), logicalId, void 0, void 0, input.createProvider));
34549
35385
  } catch (createError) {
34550
35386
  if (!isNameCollisionErrorFrom(createError, logicalId)) throw createError;
34551
35387
  const createMsg = displayAwsMessage(maskName(describeAwsFailure(createError).detail));
34552
- throw markNonRetryable(new CdkdError(maskName(`${displaySafe(logicalId)} (${displaySafe(resourceType)}) requires replacement (${input.trigger}), and cdkd created the new resource first because its name differs, but the create collided: ${createMsg}. If the collision is on the requested name: ${renderReplacementNameChange(input.change, input.createProps)}. Nothing was deleted` + (retainOld ? ` (UpdateReplacePolicy: Retain keeps the old resource in place)` : "") + ". Choose a name no other resource holds, or delete the resource holding it if it is yours."), "NAMED_REPLACEMENT_COLLISION", createError instanceof Error ? createError : void 0));
35388
+ throw markNonRetryable(new CdkdError(maskName(`${displaySafe(logicalId)} (${displaySafe(resourceType)}) requires replacement (${input.trigger}), and cdkd created the new resource first because its name differs, but the create collided: ${createMsg}. If the collision is on the requested name: ${renderReplacementNameChange(input.change, input.createProps)}. Nothing was deleted` + (retainOld ? ` (UpdateReplacePolicy: Retain keeps the old resource in place)` : "") + ". Choose a name no other resource holds, or delete the resource holding it if it is yours." + generatedReplacementHolderAdvice({
35389
+ stackName: input.stackName,
35390
+ logicalId,
35391
+ change: input.change,
35392
+ oldPhysicalId: currentResource.physicalId,
35393
+ mask: (text) => maskName(maskSecretsInText(text, unionOfSecretBags([secrets, printingSecretsFor(deps.secretsRun(), logicalId)])))
35394
+ })), "NAMED_REPLACEMENT_COLLISION", createError instanceof Error ? createError : void 0));
34553
35395
  }
34554
35396
  if (input.equalIdIsSameResource && createResult.physicalId === currentResource.physicalId) throw markNonRetryable(new CdkdError(maskName(`${displaySafe(logicalId)} (${displaySafe(resourceType)}) requires replacement (${input.trigger}) under a new name, but the create returned the resource being replaced (${displaySafe(currentResource.physicalId)}) instead of a new one, so cdkd cannot tell which name it holds. Nothing was deleted. Delete that resource by hand if it is yours, then re-run the deploy.`), "NAMED_REPLACEMENT_IDEMPOTENT_CREATE"));
34555
35397
  if (retainOld) return createResult;
@@ -34562,7 +35404,7 @@ async function deleteReplacedAfterCreate(run, deps, logicalId, oldResourceType,
34562
35404
  let deleteFailed = false;
34563
35405
  const inlinePolicyClaimed = isStandaloneInlinePolicyType(oldResourceType) ? deps.inlinePolicyClaimedFor(oldResourceType, logicalId, deps.deployStateResources()) : void 0;
34564
35406
  try {
34565
- deleteResult = await withPrintingSecrets(secrets, () => deleteProvider.delete(logicalId, currentResource.physicalId, oldResourceType, deleteProperties, {
35407
+ const sendDelete = () => deleteProvider.delete(logicalId, currentResource.physicalId, oldResourceType, deleteProperties, {
34566
35408
  expectedRegion: deps.stackRegion,
34567
35409
  forceDataDelete: deps.options.forceStatefulRecreation === true,
34568
35410
  ...finalSnapshotIdentifier !== void 0 && { finalSnapshotIdentifier },
@@ -34572,7 +35414,8 @@ async function deleteReplacedAfterCreate(run, deps, logicalId, oldResourceType,
34572
35414
  ...noEchoDeleteValues !== void 0 && { noEchoDeleteValues },
34573
35415
  ...inlinePolicyClaimed && { inlinePolicyClaimed },
34574
35416
  maskSecrets: createUnionSecretMasker([secrets, secretNameBagFor(deps.secretsRun(), logicalId)])
34575
- }));
35417
+ });
35418
+ deleteResult = await withPrintingSecrets(secrets, () => isRetry ? retryWhileInUse(sendDelete, () => deps.interrupted()) : sendDelete());
34576
35419
  } catch (deleteError) {
34577
35420
  const deleteMsg = maskSecretsInText(describeAwsFailure(deleteError).detail, secrets);
34578
35421
  deleteFailed = true;
@@ -34580,6 +35423,23 @@ async function deleteReplacedAfterCreate(run, deps, logicalId, oldResourceType,
34580
35423
  const deferred = run.deferredReplacedDeletes;
34581
35424
  if (!isRetry && deferred !== void 0) {
34582
35425
  deps.logger.info(safeMsg` Could not delete old ${logicalId} (${currentResource.physicalId}) yet: ${deleteMsg}. Retrying after the rest of the deploy.`);
35426
+ const host = run.supersededHost;
35427
+ if (host !== void 0 && canRecordSuperseded(currentResource, {
35428
+ noEchoDeleteValues,
35429
+ inlinePolicyClaimed
35430
+ }) && !Object.entries(deps.deployStateResources()).some(([id, record]) => id !== logicalId && record?.physicalId === currentResource.physicalId)) {
35431
+ const entry = {
35432
+ logicalId,
35433
+ supersededAt: Date.now(),
35434
+ state: currentResource,
35435
+ ...updateReplacePolicy !== void 0 && { updateReplacePolicy },
35436
+ ...finalSnapshotIdentifier !== void 0 && { finalSnapshotIdentifier },
35437
+ ...deps.options.forceStatefulRecreation === true && { forceDataDelete: true },
35438
+ ...deps.options.skipFinalSnapshot === true && { skipFinalSnapshot: true }
35439
+ };
35440
+ addSuperseded(host, entry);
35441
+ run.supersededEntries.set(logicalId, entry);
35442
+ }
34583
35443
  deferred.set(logicalId, {
34584
35444
  physicalId: currentResource.physicalId,
34585
35445
  resourceType: oldResourceType,
@@ -34589,7 +35449,13 @@ async function deleteReplacedAfterCreate(run, deps, logicalId, oldResourceType,
34589
35449
  });
34590
35450
  return;
34591
35451
  }
34592
- deps.logger.warn(safeMsg` ⚠ Failed to delete old resource ${logicalId} (${currentResource.physicalId}): ${deleteMsg}`);
35452
+ const kept = isRetry && run.supersededEntries.has(logicalId);
35453
+ deps.logger.warn(kept ? safeMsg` ⚠ Failed to delete old resource ${logicalId} (${currentResource.physicalId}): ${deleteMsg}. ${SUPERSEDED_KEPT_REMEDY}.` : safeMsg` ⚠ Failed to delete old resource ${logicalId} (${currentResource.physicalId}): ${deleteMsg}`);
35454
+ }
35455
+ const recorded = isRetry ? run.supersededEntries.get(logicalId) : void 0;
35456
+ if (recorded !== void 0 && !deleteFailed && run.supersededHost !== void 0) {
35457
+ removeSuperseded(run.supersededHost, recorded);
35458
+ run.supersededEntries.delete(logicalId);
34593
35459
  }
34594
35460
  reportDeleteGuards(deleteResult, {
34595
35461
  physicalId: currentResource.physicalId,
@@ -34662,8 +35528,9 @@ async function retryDeferredReplacedDeletes(run, deps, stackName, counts) {
34662
35528
  function warnUnretriedReplacedDeletes(run, deps, why) {
34663
35529
  const deferred = run.deferredReplacedDeletes;
34664
35530
  run.deferredReplacedDeletes = void 0;
34665
- const note = why === "failed" ? "not retried: the deploy failed first" : "not retried: the deploy was interrupted";
34666
- for (const [logicalId, entry] of deferred ?? []) withPrintingSecrets(secretNameBagFor(deps.secretsRun(), logicalId), () => deps.logger.warn(safeMsg` ⚠ Failed to delete old resource ${logicalId} (${entry.physicalId}): ${entry.failureMessage} (${note})`));
35531
+ const cause = why === "failed" ? "the deploy failed first" : "the deploy was interrupted";
35532
+ const noteFor = (logicalId) => run.supersededEntries.has(logicalId) ? `not retried: ${cause}; cdkd keeps it in state, and the next deploy retries the delete` : `not retried: ${cause}; it is no longer tracked in state, so delete it manually`;
35533
+ for (const [logicalId, entry] of deferred ?? []) withPrintingSecrets(secretNameBagFor(deps.secretsRun(), logicalId), () => deps.logger.warn(safeMsg` ⚠ Failed to delete old resource ${logicalId} (${entry.physicalId}): ${entry.failureMessage} (${noteFor(logicalId)})`));
34667
35534
  }
34668
35535
 
34669
35536
  //#endregion
@@ -35556,6 +36423,136 @@ function recaptureMaskedBaseline(args) {
35556
36423
  return changed ? result : void 0;
35557
36424
  }
35558
36425
 
36426
+ //#endregion
36427
+ //#region src/state/name-generation.ts
36428
+ var MalformedNameGenerationError = class extends Error {
36429
+ constructor(where, reason = "malformed") {
36430
+ super(`The state record holds a name generation for ${displayIdent(where)} that is ` + (reason === "exhausted" ? "too large to derive the next one from (the next is not a safe integer). " : "not a non-negative integer. ") + "cdkd does not guess which name the resource was given, since the next replacement's name derives from it; restore the field from an earlier version of state.json.");
36431
+ this.name = "MalformedNameGenerationError";
36432
+ }
36433
+ };
36434
+ function generationAfter(generation, logicalId) {
36435
+ const next = generation + 1;
36436
+ if (!isNameGeneration(next)) throw new MalformedNameGenerationError(logicalId, "exhausted");
36437
+ return next;
36438
+ }
36439
+ function nameGenerationOf(record, logicalId) {
36440
+ const value = record.nameGeneration;
36441
+ if (value === void 0) return 0;
36442
+ if (!isNameGeneration(value)) throw new MalformedNameGenerationError(logicalId);
36443
+ return value;
36444
+ }
36445
+ function nameGenerationHighWaterOf(record, logicalId) {
36446
+ const generation = nameGenerationOf(record, logicalId);
36447
+ const high = record.nameGenerationHigh;
36448
+ if (high === void 0) return generation;
36449
+ if (!isNameGeneration(high)) throw new MalformedNameGenerationError(logicalId);
36450
+ return Math.max(generation, high);
36451
+ }
36452
+ function retiredNameGenerationsOf(state) {
36453
+ const raw = state.retiredNameGenerations;
36454
+ const out = /* @__PURE__ */ new Map();
36455
+ if (raw === void 0) return out;
36456
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new MalformedNameGenerationError("retiredNameGenerations");
36457
+ for (const [logicalId, value] of Object.entries(raw)) {
36458
+ if (!isNameGeneration(value)) throw new MalformedNameGenerationError(logicalId);
36459
+ out.set(logicalId, value);
36460
+ }
36461
+ return out;
36462
+ }
36463
+ function nameGenerationsOf(state) {
36464
+ const out = /* @__PURE__ */ new Map();
36465
+ for (const [logicalId, retired] of retiredNameGenerationsOf(state)) out.set(logicalId, generationAfter(retired, logicalId));
36466
+ for (const [logicalId, record] of Object.entries(state.resources ?? {})) {
36467
+ if (record === null || typeof record !== "object") continue;
36468
+ out.set(logicalId, nameGenerationOf(record, logicalId));
36469
+ }
36470
+ return out;
36471
+ }
36472
+ function nextNameGeneration(record, logicalId, stackInstanceId) {
36473
+ let next = generationAfter(nameGenerationHighWaterOf(record, logicalId), logicalId);
36474
+ const physicalId = typeof record.physicalId === "string" ? record.physicalId.toLowerCase() : "";
36475
+ while (physicalId.includes(stackInstanceNameSuffix(stackInstanceId, logicalId, next))) next = generationAfter(next, logicalId);
36476
+ return next;
36477
+ }
36478
+ function replacementNameGeneration(record, logicalId, stackInstanceId, keepsName) {
36479
+ if (stackInstanceId === void 0 || keepsName) return {
36480
+ generation: void 0,
36481
+ fields: nameGenerationCarriedFrom(record)
36482
+ };
36483
+ const generation = nextNameGeneration(record, logicalId, stackInstanceId);
36484
+ return {
36485
+ generation,
36486
+ fields: nameGenerationField(generation)
36487
+ };
36488
+ }
36489
+ function nameGenerationField(generation) {
36490
+ return generation === 0 ? {} : { nameGeneration: generation };
36491
+ }
36492
+ function nameGenerationCarriedFrom(previous) {
36493
+ return {
36494
+ ...previous?.nameGeneration !== void 0 && { nameGeneration: previous.nameGeneration },
36495
+ ...previous?.nameGenerationHigh !== void 0 && { nameGenerationHigh: previous.nameGenerationHigh }
36496
+ };
36497
+ }
36498
+ function withNameGenerationReached(record, logicalId, reached) {
36499
+ if (reached === void 0 || !isNameGeneration(reached)) return record;
36500
+ if (reached <= nameGenerationHighWaterOf(record, logicalId)) return record;
36501
+ return {
36502
+ ...record,
36503
+ nameGenerationHigh: reached
36504
+ };
36505
+ }
36506
+ function retiredNameGenerationsAfter(state, leaving) {
36507
+ if (state.stackInstanceId === void 0) return {};
36508
+ const retired = new Map(retiredNameGenerationsOf(state));
36509
+ let changed = false;
36510
+ for (const { logicalId, record } of leaving) {
36511
+ const reached = nameGenerationHighWaterOf(record, logicalId);
36512
+ const before = retired.get(logicalId);
36513
+ if (before !== void 0 && before >= reached) continue;
36514
+ retired.set(logicalId, reached);
36515
+ changed = true;
36516
+ }
36517
+ if (!changed) return state.retiredNameGenerations === void 0 ? {} : { retiredNameGenerations: state.retiredNameGenerations };
36518
+ return { retiredNameGenerations: Object.fromEntries(retired) };
36519
+ }
36520
+ function nameScopeOf(stackName, stackInstanceId, state) {
36521
+ return {
36522
+ stackName,
36523
+ stackInstanceId,
36524
+ nameGenerations: stackInstanceId === void 0 ? void 0 : nameGenerationsOf(state ?? { resources: {} })
36525
+ };
36526
+ }
36527
+ function rolledBackCreateReached(create) {
36528
+ let reached = 0;
36529
+ for (const value of [
36530
+ create.record?.nameGeneration,
36531
+ create.record?.nameGenerationHigh,
36532
+ create.nameGeneration
36533
+ ]) if (isNameGeneration(value)) reached = Math.max(reached, value);
36534
+ return reached;
36535
+ }
36536
+ function retiredAfterRollback(state, orphaned, rolledBackCreates = []) {
36537
+ return retiredNameGenerationsAfter(state, [...orphaned.map((o) => ({
36538
+ logicalId: o.logicalId,
36539
+ record: o.state
36540
+ })), ...rolledBackCreates.map((c) => ({
36541
+ logicalId: c.logicalId,
36542
+ record: { nameGenerationHigh: rolledBackCreateReached(c) }
36543
+ }))]);
36544
+ }
36545
+ function retiredNameGenerationsLeaving(state, remaining) {
36546
+ try {
36547
+ return retiredNameGenerationsAfter(state, Object.entries(state.resources ?? {}).filter(([logicalId, record]) => !Object.hasOwn(remaining, logicalId) && record !== null && typeof record === "object").map(([logicalId, record]) => ({
36548
+ logicalId,
36549
+ record
36550
+ })));
36551
+ } catch {
36552
+ return state.retiredNameGenerations === void 0 ? {} : { retiredNameGenerations: state.retiredNameGenerations };
36553
+ }
36554
+ }
36555
+
35559
36556
  //#endregion
35560
36557
  //#region src/deployment/update-outcome.ts
35561
36558
  function updatePartialReason(result) {
@@ -35816,6 +36813,7 @@ function isPlainRecord$1(value) {
35816
36813
  //#endregion
35817
36814
  //#region src/deployment/type-change-guard.ts
35818
36815
  function equalIdNamesDifferentResources(input) {
36816
+ if (input.resourceType === "AWS::Lambda::Permission") return permissionsOnDifferentFunctions(input.oldProperties, input.newProperties);
35819
36817
  if (input.resourceType !== "AWS::Glue::Table" || input.physicalId === void 0) return false;
35820
36818
  const oldDb = input.oldProperties?.["DatabaseName"];
35821
36819
  const newDb = input.newProperties?.["DatabaseName"];
@@ -35824,6 +36822,15 @@ function equalIdNamesDifferentResources(input) {
35824
36822
  if (segmentAfterAnchor(input.physicalId, oldDb) === void 0 || segmentAfterAnchor(input.physicalId, newDb) === void 0) return false;
35825
36823
  return oldDb !== newDb;
35826
36824
  }
36825
+ function permissionsOnDifferentFunctions(oldProperties, newProperties) {
36826
+ const oldFn = oldProperties?.["FunctionName"];
36827
+ const newFn = newProperties?.["FunctionName"];
36828
+ if (typeof oldFn !== "string" || typeof newFn !== "string") return false;
36829
+ if (carriesSecretMask(oldFn) || carriesSecretMask(newFn)) return false;
36830
+ const oldName = parseLambdaFunctionAddress(oldFn)?.name;
36831
+ const newName = parseLambdaFunctionAddress(newFn)?.name;
36832
+ return oldName !== void 0 && newName !== void 0 && oldName !== newName;
36833
+ }
35827
36834
  function equalIdNamesSameResource(input) {
35828
36835
  if (input.oldType === input.newType) return !equalIdNamesDifferentResources({
35829
36836
  resourceType: input.oldType,
@@ -36243,6 +37250,30 @@ function planRollback(operations, stateResources, orphanLogicalIds = /* @__PURE_
36243
37250
  };
36244
37251
  });
36245
37252
  }
37253
+ function renamedReverseReplacement(op, stateResources, orphanLogicalIds, pendingNewIds = /* @__PURE__ */ new Set()) {
37254
+ if (op.changeType !== "UPDATE") return false;
37255
+ if (op.oldDeletedBeforeCreate === true || op.oldResourceMaySurvive !== void 0) return false;
37256
+ const current = Object.hasOwn(stateResources, op.logicalId) ? stateResources[op.logicalId] : void 0;
37257
+ const oldId = op.previousState?.physicalId;
37258
+ if (typeof oldId !== "string" || typeof current?.physicalId !== "string") return false;
37259
+ if (current.physicalId === oldId) return false;
37260
+ if (pendingNewIds.has(oldId)) return false;
37261
+ const oldType = op.previousState?.resourceType ?? op.resourceType;
37262
+ for (const [logicalId, other] of Object.entries(stateResources)) {
37263
+ if (logicalId === op.logicalId || typeof other?.physicalId !== "string") continue;
37264
+ if (other.physicalId === oldId || other.resourceType === oldType && samePhysicalId(oldType, other.physicalId, oldId)) return false;
37265
+ }
37266
+ return classifyRollbackOp(op, stateResources, orphanLogicalIds) === "reverse-replacement";
37267
+ }
37268
+ function revertReferences(op, stateResources) {
37269
+ const current = Object.hasOwn(stateResources, op.logicalId) ? stateResources[op.logicalId] : void 0;
37270
+ const refs = /* @__PURE__ */ new Set();
37271
+ for (const record of [op.previousState, current]) {
37272
+ if (!Array.isArray(record?.dependencies)) continue;
37273
+ for (const id of record.dependencies) if (typeof id === "string") refs.add(id);
37274
+ }
37275
+ return refs;
37276
+ }
36246
37277
  function partitionOps(operations) {
36247
37278
  const createOps = [];
36248
37279
  const otherOps = [];
@@ -36706,6 +37737,7 @@ async function replayOrphanFlag(s) {
36706
37737
  const orphanFlagProvisionedBy = effectiveProvisionedBy(record, op.provisionedBy);
36707
37738
  s.createRollbackRoute = orphanFlagProvisionedBy;
36708
37739
  delete stateResources[op.logicalId];
37740
+ if (record) noteRolledBackCreate(s, op, record);
36709
37741
  await noteRetainedResource(op.resourceType, op.logicalId);
36710
37742
  logger.info(` Rollback: Orphaning created resource ${safe(op.logicalId)} (--orphan)`);
36711
37743
  await afterOp?.(op.logicalId);
@@ -36738,6 +37770,7 @@ async function replayOrphanRetain(s) {
36738
37770
  onOrphan?.(orphaned);
36739
37771
  }
36740
37772
  delete stateResources[op.logicalId];
37773
+ noteRolledBackCreate(s, op, record);
36741
37774
  await noteRetainedResource(op.resourceType, op.logicalId);
36742
37775
  logger.info(` Rollback: Leaving ${safe(op.logicalId)} (${safe(op.resourceType)}) in AWS (DeletionPolicy: Retain) — removed from state`);
36743
37776
  await afterOp?.(op.logicalId);
@@ -36799,7 +37832,9 @@ async function replayDelete(s) {
36799
37832
  provisionedBy: deleteRoutedVia,
36800
37833
  mask
36801
37834
  });
37835
+ const deletedRecord = stateResources[op.logicalId];
36802
37836
  delete stateResources[op.logicalId];
37837
+ noteRolledBackCreate(s, op, deletedRecord);
36803
37838
  logger.info(` Rollback: ${safe(op.logicalId)} deleted successfully`);
36804
37839
  await afterOp?.(op.logicalId);
36805
37840
  ctx.recordEvent?.({
@@ -36864,7 +37899,7 @@ async function replayReadopt(s, survivor) {
36864
37899
  mask
36865
37900
  });
36866
37901
  }
36867
- stateResources[op.logicalId] = prev;
37902
+ stateResources[op.logicalId] = withNameGenerationReached(prev, op.logicalId, op.nameGeneration);
36868
37903
  logger.info(` Rollback: ${safe(op.logicalId)} restored to the ${oldKind} old resource`);
36869
37904
  await afterOp?.(op.logicalId);
36870
37905
  const survivorProvisionedBy = current.provisionedBy;
@@ -36947,7 +37982,8 @@ async function replayRevert(s) {
36947
37982
  properties: desiredProps,
36948
37983
  logicalId: op.logicalId,
36949
37984
  physicalId: current.physicalId,
36950
- via: revertVia
37985
+ via: revertVia,
37986
+ nameGeneration: nameGenerationOf(current, op.logicalId)
36951
37987
  }, logger, mask, false)(() => updateWithRollbackRetry(provider, [
36952
37988
  op.logicalId,
36953
37989
  current.physicalId,
@@ -37059,7 +38095,8 @@ async function replayReverseReplacement(s) {
37059
38095
  properties: resolvedPrevProps,
37060
38096
  logicalId: op.logicalId,
37061
38097
  physicalId: prev.physicalId,
37062
- via: createProvisionedBy
38098
+ via: createProvisionedBy,
38099
+ nameGeneration: nameGenerationOf(prev, op.logicalId)
37063
38100
  }, logger, mask, true);
37064
38101
  const resolveNewDeleteRoute = () => ctx.providerRegistry.getProviderFor({
37065
38102
  resourceType: op.resourceType,
@@ -37101,6 +38138,10 @@ async function replayReverseReplacement(s) {
37101
38138
  throwIfDeleteSkipped(deleteNewFirst, op.logicalId, current.physicalId, purpose, newDeleteGuardScope(deleteNewFirstRoute.provisionedBy));
37102
38139
  delete stateResources[op.logicalId];
37103
38140
  await afterOp?.(op.logicalId);
38141
+ await deleteNewFirstRoute.provider.waitForNameReleased?.(op.logicalId, current.physicalId, current.resourceType, {
38142
+ isInterrupted: () => isInterrupted?.() === true,
38143
+ maskSecrets: mask
38144
+ });
37104
38145
  };
37105
38146
  const recreateAfterNewDeleted = async () => {
37106
38147
  try {
@@ -37133,7 +38174,7 @@ async function replayReverseReplacement(s) {
37133
38174
  oldResourceType: oldType,
37134
38175
  newResourceType: op.resourceType,
37135
38176
  requested: replayCreateProps(),
37136
- generated: typeof op.logicalId === "string" ? applyDefaultNameForFallback(op.logicalId, oldType, resolvedPrevProps) : void 0,
38177
+ generated: typeof op.logicalId === "string" ? applyDefaultNameForFallback(op.logicalId, oldType, resolvedPrevProps, "omit") : void 0,
37137
38178
  logicalId: op.logicalId,
37138
38179
  createdVia: createProvisionedBy,
37139
38180
  mask,
@@ -37192,56 +38233,125 @@ async function replayReverseReplacement(s) {
37192
38233
  const { observedProperties: _staleObserved, ...prevRecord } = prev;
37193
38234
  recordNoEchoAttributeValues(createResult, secrets, resolvedPrevProps);
37194
38235
  stateResources[op.logicalId] = redactRollbackRecord({
37195
- ...prevRecord,
38236
+ ...withNameGenerationReached(prevRecord, op.logicalId, op.nameGeneration),
37196
38237
  physicalId: createResult.physicalId,
37197
38238
  attributes: createResult.attributes ?? {},
37198
38239
  properties: recordedPropertiesAfterReplayCreate(prevRecord, createResult)
37199
38240
  }, secrets, prevRecord.properties, prevRecord.attributes);
37200
38241
  if (!adoptedLiveNewResource) inlinePolicyWriters.record(op.logicalId, "create", stateResources[op.logicalId], false, createProvisionedBy);
38242
+ const deferral = s.deferral !== void 0 && !deletedNewFirst && !adoptedLiveNewResource && !rollbackRetainsNewResource(current) ? s.deferral : void 0;
38243
+ const supersededFinalSnapshot = rollbackFinalSnapshotId(op.resourceType, current, op.provisionedBy);
38244
+ const supersededPolicy = current.updateReplacePolicy;
38245
+ const superseded = {
38246
+ logicalId: op.logicalId,
38247
+ supersededAt: Date.now(),
38248
+ state: current,
38249
+ ...(supersededPolicy === "Delete" || supersededPolicy === "Snapshot" || supersededPolicy === "RetainExceptOnCreate") && { updateReplacePolicy: supersededPolicy },
38250
+ ...supersededFinalSnapshot !== void 0 && { finalSnapshotIdentifier: supersededFinalSnapshot }
38251
+ };
38252
+ const recordSuperseded = deferral?.onSuperseded !== void 0 && canRecordSuperseded(current, {});
38253
+ if (recordSuperseded) deferral?.onSuperseded?.(superseded);
37201
38254
  await afterOp?.(op.logicalId);
37202
- let survivorReason;
37203
- const survivorId = retainedSurvivorId(current, op);
37204
- if (!deletedNewFirst && !adoptedLiveNewResource && rollbackRetainsNewResource(current)) {
37205
- const survivorMessages = retainedSurvivorMessages(op.logicalId, op.resourceType, survivorId ?? "no recorded id", `State records the re-created old resource (${stateResources[op.logicalId]?.physicalId ?? prev.physicalId}).`, mask);
37206
- logger.warn(survivorMessages.warn);
37207
- survivorReason = survivorMessages.reason;
37208
- result.warnings++;
37209
- await noteRetainedResource(op.resourceType, op.logicalId);
37210
- } else if (!deletedNewFirst && !adoptedLiveNewResource) try {
37211
- const finalSnapshotIdentifier = rollbackFinalSnapshotId(op.resourceType, current, op.provisionedBy);
37212
- const deleteNewAfterRecreateRoute = resolveNewDeleteRoute();
37213
- const deleteNewAfterRecreate = await withReplayDeletePrinting(s, () => deleteNewAfterRecreateRoute.provider.delete(op.logicalId, current.physicalId, op.resourceType, current.properties, {
37214
- expectedRegion: ctx.region,
37215
- ...newCopyClaimed && { inlinePolicyClaimed: newCopyClaimed },
37216
- ...finalSnapshotIdentifier !== void 0 && { finalSnapshotIdentifier },
37217
- deletionPolicy: replacementDeletePolicy(current.updateReplacePolicy),
37218
- recordedAttributes: current.attributes,
37219
- recordedNoEchoLeaves: current.noEchoLeaves,
37220
- maskSecrets: mask
37221
- }));
37222
- throwIfDeleteSkipped(deleteNewAfterRecreate, op.logicalId, current.physicalId, "while deleting the new resource after re-creating the old one", newDeleteGuardScope(deleteNewAfterRecreateRoute.provisionedBy));
37223
- } catch (deleteError) {
37224
- logger.warn(mask(` Rollback: old ${safe(op.logicalId)} re-created, but deleting the new resource (${displaySafe(mask(current.physicalId))}) failed: ${displaySafe(describeAwsFailure(deleteError).detail)}. Delete it manually — it is no longer tracked in state.`));
37225
- survivorReason = `The replacement's new ${op.resourceType} (${current.physicalId}) could not be deleted after the old resource was re-created: ${describeAwsFailure(deleteError).detail}. It is live, still billing, and no longer tracked by cdkd — delete it yourself.`;
38255
+ const finish = async () => {
38256
+ let survivorReason;
38257
+ const survivorId = retainedSurvivorId(current, op);
38258
+ if (!deletedNewFirst && !adoptedLiveNewResource && rollbackRetainsNewResource(current)) {
38259
+ const survivorMessages = retainedSurvivorMessages(op.logicalId, op.resourceType, survivorId ?? "no recorded id", `State records the re-created old resource (${stateResources[op.logicalId]?.physicalId ?? prev.physicalId}).`, mask);
38260
+ logger.warn(survivorMessages.warn);
38261
+ survivorReason = survivorMessages.reason;
38262
+ result.warnings++;
38263
+ await noteRetainedResource(op.resourceType, op.logicalId);
38264
+ } else if (!deletedNewFirst && !adoptedLiveNewResource) try {
38265
+ const finalSnapshotIdentifier = rollbackFinalSnapshotId(op.resourceType, current, op.provisionedBy);
38266
+ const deleteNewAfterRecreateRoute = resolveNewDeleteRoute();
38267
+ const deleteNewAfterRecreate = await withReplayDeletePrinting(s, () => deleteNewAfterRecreateRoute.provider.delete(op.logicalId, current.physicalId, op.resourceType, current.properties, {
38268
+ expectedRegion: ctx.region,
38269
+ ...newCopyClaimed && { inlinePolicyClaimed: newCopyClaimed },
38270
+ ...finalSnapshotIdentifier !== void 0 && { finalSnapshotIdentifier },
38271
+ deletionPolicy: replacementDeletePolicy(current.updateReplacePolicy),
38272
+ recordedAttributes: current.attributes,
38273
+ recordedNoEchoLeaves: current.noEchoLeaves,
38274
+ maskSecrets: mask
38275
+ }));
38276
+ throwIfDeleteSkipped(deleteNewAfterRecreate, op.logicalId, current.physicalId, "while deleting the new resource after re-creating the old one", newDeleteGuardScope(deleteNewAfterRecreateRoute.provisionedBy));
38277
+ } catch (deleteError) {
38278
+ logger.warn(mask(` Rollback: old ${safe(op.logicalId)} re-created, but deleting the new resource (${displaySafe(mask(current.physicalId))}) failed: ${displaySafe(describeAwsFailure(deleteError).detail)}. ` + (recordSuperseded ? `${SUPERSEDED_KEPT_REMEDY}.` : `Delete it manually — it is no longer tracked in state.`)));
38279
+ survivorReason = `The replacement's new ${op.resourceType} (${current.physicalId}) could not be deleted after the old resource was re-created: ${describeAwsFailure(deleteError).detail}. ` + (recordSuperseded ? `It is live and still billing; ${SUPERSEDED_KEPT_REMEDY}.` : `It is live, still billing, and no longer tracked by cdkd — delete it yourself.`);
38280
+ result.warnings++;
38281
+ }
38282
+ const recreatedId = displaySafe(mask(String(createResult.physicalId)));
38283
+ logger.info(mask(adoptedLiveNewResource ? ` Rollback: ${safe(op.logicalId)} adopted the live resource (${recreatedId}) — replacement NOT fully reversed (name-idempotent Create API)` : ` Rollback: ${safe(op.logicalId)} replacement reversed (old resource re-created as ${recreatedId})`));
38284
+ const survivorProvisionedBy = current.provisionedBy;
38285
+ ctx.recordEvent?.({
38286
+ eventType: "ROLLBACK_RESOURCE_SUCCEEDED",
38287
+ stackName,
38288
+ operation: "UPDATE",
38289
+ logicalId: op.logicalId,
38290
+ resourceType: op.resourceType,
38291
+ ...op.provisionedBy && { provisionedBy: op.provisionedBy },
38292
+ ...survivorReason !== void 0 && {
38293
+ ...survivorId !== void 0 && { physicalId: survivorId },
38294
+ reason: mask(survivorReason),
38295
+ ...survivorProvisionedBy && { provisionedBy: survivorProvisionedBy }
38296
+ },
38297
+ ...adoptedLiveNewResource && { reason: mask("The re-create returned the live new resource instead of re-creating the old one (its Create API is name-idempotent), so the replacement was NOT fully reversed: the old resource's original properties may not have been re-applied. Inspect it with `cdkd drift` and run `cdkd deploy` to reconcile.") }
38298
+ });
38299
+ return survivorReason === void 0;
38300
+ };
38301
+ const keep = (reason) => {
38302
+ logger.warn(mask(` Rollback: old ${safe(op.logicalId)} re-created, but the new resource (${displaySafe(mask(current.physicalId))}) was not deleted: ${reason}. ${SUPERSEDED_KEPT_REMEDY}.`));
37226
38303
  result.warnings++;
38304
+ ctx.recordEvent?.({
38305
+ eventType: "ROLLBACK_RESOURCE_SUCCEEDED",
38306
+ stackName,
38307
+ operation: "UPDATE",
38308
+ logicalId: op.logicalId,
38309
+ resourceType: op.resourceType,
38310
+ ...current.provisionedBy && { provisionedBy: current.provisionedBy },
38311
+ physicalId: current.physicalId,
38312
+ reason: mask(`The old resource was re-created; the new one (${current.physicalId}) was not deleted: ${reason}. ${SUPERSEDED_KEPT_REMEDY}.`)
38313
+ });
38314
+ };
38315
+ if (deferral !== void 0) {
38316
+ logger.info(safeMsg` Rollback: old ${safe(op.logicalId)} re-created first; the new resource ` + safeMsg`(${mask(current.physicalId)}) is deleted once the resources that reference it ` + `are reverted`);
38317
+ deferral.defer(async () => {
38318
+ let gone = false;
38319
+ try {
38320
+ if (supersededHolderOf(superseded, stateResources) !== void 0) {
38321
+ logger.warn(safeMsg` Rollback: not deleting the new ${shownLogicalId(op.logicalId)} ` + safeMsg`(${mask(current.physicalId)}): a record of this stack now holds its ` + `physical id. Check it with cdkd drift.`);
38322
+ result.warnings++;
38323
+ ctx.recordEvent?.({
38324
+ eventType: "ROLLBACK_RESOURCE_SUCCEEDED",
38325
+ stackName,
38326
+ operation: "UPDATE",
38327
+ logicalId: op.logicalId,
38328
+ resourceType: op.resourceType,
38329
+ ...op.provisionedBy && { provisionedBy: op.provisionedBy },
38330
+ reason: mask(`The old resource was re-created; the new one (${current.physicalId}) was not deleted because a record of this stack now holds its physical id.`)
38331
+ });
38332
+ gone = true;
38333
+ } else gone = await finish();
38334
+ } catch (finishError) {
38335
+ logger.warn(maskedFailureText(` Rollback failed for ${shownLogicalId(op.logicalId)} (UPDATE): `, finishError, mask));
38336
+ result.failures++;
38337
+ ctx.recordEvent?.({
38338
+ eventType: "ROLLBACK_RESOURCE_FAILED",
38339
+ stackName,
38340
+ operation: op.changeType,
38341
+ logicalId: op.logicalId,
38342
+ resourceType: op.resourceType,
38343
+ ...op.provisionedBy && { provisionedBy: op.provisionedBy },
38344
+ error: maskedRollbackEventError(finishError, mask)
38345
+ });
38346
+ }
38347
+ if (recordSuperseded && gone) {
38348
+ deferral.onSupersededSettled?.(superseded);
38349
+ await afterOp?.(op.logicalId);
38350
+ }
38351
+ }, recordSuperseded ? keep : void 0);
38352
+ return;
37227
38353
  }
37228
- const recreatedId = displaySafe(mask(String(createResult.physicalId)));
37229
- logger.info(mask(adoptedLiveNewResource ? ` Rollback: ${safe(op.logicalId)} adopted the live resource (${recreatedId}) — replacement NOT fully reversed (name-idempotent Create API)` : ` Rollback: ${safe(op.logicalId)} replacement reversed (old resource re-created as ${recreatedId})`));
37230
- const survivorProvisionedBy = current.provisionedBy;
37231
- ctx.recordEvent?.({
37232
- eventType: "ROLLBACK_RESOURCE_SUCCEEDED",
37233
- stackName,
37234
- operation: "UPDATE",
37235
- logicalId: op.logicalId,
37236
- resourceType: op.resourceType,
37237
- ...op.provisionedBy && { provisionedBy: op.provisionedBy },
37238
- ...survivorReason !== void 0 && {
37239
- ...survivorId !== void 0 && { physicalId: survivorId },
37240
- reason: mask(survivorReason),
37241
- ...survivorProvisionedBy && { provisionedBy: survivorProvisionedBy }
37242
- },
37243
- ...adoptedLiveNewResource && { reason: mask("The re-create returned the live new resource instead of re-creating the old one (its Create API is name-idempotent), so the replacement was NOT fully reversed: the old resource's original properties may not have been re-applied. Inspect it with `cdkd drift` and run `cdkd deploy` to reconcile.") }
37244
- });
38354
+ await finish();
37245
38355
  }
37246
38356
  async function deleteMarkedRecreate(error, s) {
37247
38357
  const madeId = createdBeforeFailure(error, s.op.logicalId, s.oldType);
@@ -37318,7 +38428,8 @@ async function replayRollbackUnbound(operations, stateResources, stackName, ctx,
37318
38428
  warnings: 0,
37319
38429
  skipped: 0,
37320
38430
  interrupted: false,
37321
- orphaned: []
38431
+ orphaned: [],
38432
+ rolledBackCreates: []
37322
38433
  };
37323
38434
  if (operations.length === 0) {
37324
38435
  ctx.logger.info("No completed operations to roll back.");
@@ -37335,13 +38446,77 @@ async function replayRollbackUnbound(operations, stateResources, stackName, ctx,
37335
38446
  markDeleteFirstBlocked(operations, options.failedOperations);
37336
38447
  const { createOps, otherOps } = partitionOps(operations);
37337
38448
  const createdInSegment = new Set(createOps.map((o) => o.logicalId));
37338
- for (const op of orderInlinePolicyReversals([...otherOps].reverse(), stateResources)) {
38449
+ const stepOne = orderInlinePolicyReversals([...otherOps].reverse(), stateResources);
38450
+ const stepOneIds = /* @__PURE__ */ new Map();
38451
+ const stepOneIndex = /* @__PURE__ */ new Map();
38452
+ stepOne.forEach((op, at) => {
38453
+ if (!stepOneIds.has(op.logicalId)) stepOneIds.set(op.logicalId, op);
38454
+ stepOneIndex.set(op, at);
38455
+ });
38456
+ const started = /* @__PURE__ */ new Set();
38457
+ const pendingNewDeletes = /* @__PURE__ */ new Map();
38458
+ const failedOps = /* @__PURE__ */ new Set();
38459
+ const usedByFailedRevert = (target) => [...failedOps].some((failed) => failed !== target && revertReferences(failed, stateResources).has(target.logicalId));
38460
+ const pendingNewIds = /* @__PURE__ */ new Set();
38461
+ const runOp = async (op, deferral) => {
38462
+ const failuresBefore = result.failures;
38463
+ const skippedBefore = result.skipped;
38464
+ await replaySingle(op, stateResources, stackName, ctx, resolver, orphanLogicalIds, result, options.onOrphan, inlinePolicyWriters, options.afterOp, options.isInterrupted, createdInSegment, options.onRolledBackCreate, deferral);
38465
+ if (result.failures > failuresBefore || result.skipped > skippedBefore) failedOps.add(op);
38466
+ };
38467
+ const recreateAhead = async (refs) => {
38468
+ for (const target of [...refs].map((id) => stepOneIds.get(id))) {
38469
+ if (target === void 0 || started.has(target)) continue;
38470
+ if (!renamedReverseReplacement(target, stateResources, orphanLogicalIds, pendingNewIds)) continue;
38471
+ const ownRefs = revertReferences(target, stateResources);
38472
+ if ([...ownRefs].some((id) => {
38473
+ const named = stepOneIds.get(id);
38474
+ return named !== void 0 && !started.has(named) && stepOneIndex.get(named) < stepOneIndex.get(target) && !renamedReverseReplacement(named, stateResources, orphanLogicalIds, pendingNewIds);
38475
+ })) continue;
38476
+ if (options.isInterrupted?.()) return;
38477
+ started.add(target);
38478
+ await recreateAhead(ownRefs);
38479
+ if (options.isInterrupted?.()) return;
38480
+ const newId = stateResources[target.logicalId]?.physicalId;
38481
+ await runOp(target, {
38482
+ defer: (finish, keep) => {
38483
+ pendingNewDeletes.set(target, {
38484
+ finish: async () => {
38485
+ await finish();
38486
+ if (typeof newId === "string") pendingNewIds.delete(newId);
38487
+ },
38488
+ keep
38489
+ });
38490
+ if (typeof newId === "string") pendingNewIds.add(newId);
38491
+ },
38492
+ onSuperseded: options.onSuperseded,
38493
+ onSupersededSettled: options.onSupersededSettled
38494
+ });
38495
+ }
38496
+ };
38497
+ for (const op of stepOne) {
37339
38498
  if (options.isInterrupted?.()) {
37340
38499
  result.interrupted = true;
37341
38500
  break;
37342
38501
  }
37343
- await replaySingle(op, stateResources, stackName, ctx, resolver, orphanLogicalIds, result, options.onOrphan, inlinePolicyWriters, options.afterOp, options.isInterrupted, createdInSegment);
38502
+ if (!started.has(op)) await recreateAhead(revertReferences(op, stateResources));
38503
+ if (options.isInterrupted?.()) {
38504
+ result.interrupted = true;
38505
+ break;
38506
+ }
38507
+ if (started.has(op)) {
38508
+ const pending = pendingNewDeletes.get(op);
38509
+ pendingNewDeletes.delete(op);
38510
+ if (pending?.keep !== void 0 && usedByFailedRevert(op)) pending.keep("a resource that references it did not revert");
38511
+ else await pending?.finish();
38512
+ continue;
38513
+ }
38514
+ started.add(op);
38515
+ await runOp(op);
37344
38516
  }
38517
+ for (const pending of pendingNewDeletes.values()) if (pending.keep !== void 0) pending.keep("the rollback was interrupted");
38518
+ else await pending.finish();
38519
+ pendingNewDeletes.clear();
37345
38520
  if (!result.interrupted && createOps.length > 0) {
37346
38521
  const sorted = sortRollbackCreates(createOps, stateResources);
37347
38522
  for (const op of sorted) {
@@ -37349,7 +38524,7 @@ async function replayRollbackUnbound(operations, stateResources, stackName, ctx,
37349
38524
  result.interrupted = true;
37350
38525
  break;
37351
38526
  }
37352
- await replaySingle(op, stateResources, stackName, ctx, resolver, orphanLogicalIds, result, options.onOrphan, inlinePolicyWriters, options.afterOp, options.isInterrupted, createdInSegment);
38527
+ await replaySingle(op, stateResources, stackName, ctx, resolver, orphanLogicalIds, result, options.onOrphan, inlinePolicyWriters, options.afterOp, options.isInterrupted, createdInSegment, options.onRolledBackCreate);
37353
38528
  }
37354
38529
  }
37355
38530
  await restoreHeldInlinePolicies(inlinePolicyWriters, stateResources, stackName, ctx, result);
@@ -37379,7 +38554,7 @@ async function journaledOrphanKeepReason(op, foreignHolder, ctx) {
37379
38554
  return typeof journaled === "string" && journaled !== "" ? { retry: "its live identity could not be read, so nothing proves the resource now under that id is the one it created" } : { keep: "nothing proves the resource now under that id is the one it created (the journal recorded no identity for it; it may have been deleted and its name reused)" };
37380
38555
  }
37381
38556
  }
37382
- async function replaySingle(op, stateResources, stackName, ctx, resolver, orphanLogicalIds, result, onOrphan, inlinePolicyWriters, afterOp, isInterrupted, createdInSegment = /* @__PURE__ */ new Set()) {
38557
+ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphanLogicalIds, result, onOrphan, inlinePolicyWriters, afterOp, isInterrupted, createdInSegment = /* @__PURE__ */ new Set(), onRolledBackCreate, deferral) {
37383
38558
  const action = classifyRollbackOp(op, stateResources, orphanLogicalIds);
37384
38559
  const { logger } = ctx;
37385
38560
  const secrets = /* @__PURE__ */ new Map();
@@ -37396,6 +38571,7 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
37396
38571
  orphanLogicalIds,
37397
38572
  result,
37398
38573
  onOrphan,
38574
+ onRolledBackCreate,
37399
38575
  inlinePolicyWriters,
37400
38576
  afterOp,
37401
38577
  isInterrupted,
@@ -37405,7 +38581,8 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
37405
38581
  opMasker,
37406
38582
  mask,
37407
38583
  createdInSegment,
37408
- createRollbackRoute: void 0
38584
+ createRollbackRoute: void 0,
38585
+ deferral
37409
38586
  };
37410
38587
  const recordBefore = ownRecord(stateResources, op.logicalId);
37411
38588
  try {
@@ -37469,9 +38646,14 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
37469
38646
  remainingFailedOps: [],
37470
38647
  skippedOps: [],
37471
38648
  orphaned: [],
38649
+ rolledBackCreates: [],
37472
38650
  leftInPlace: 0
37473
38651
  };
37474
38652
  const { logger } = ctx;
38653
+ const rolledBack = {
38654
+ result,
38655
+ onRolledBackCreate: options.onRolledBackCreate
38656
+ };
37475
38657
  const resolver = new ReplayResolvers(ctx.region);
37476
38658
  const emitEnvelope = options.emitEnvelope === true && failedOps.length > 0;
37477
38659
  if (emitEnvelope) ctx.recordEvent?.({
@@ -37548,7 +38730,10 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
37548
38730
  result.orphaned.push(orphaned);
37549
38731
  options.onOrphan?.(orphaned);
37550
38732
  }
37551
- if (!recordUnderIdIsNotOwn(op, stateResources)) delete stateResources[op.logicalId];
38733
+ if (!recordUnderIdIsNotOwn(op, stateResources)) {
38734
+ delete stateResources[op.logicalId];
38735
+ noteRolledBackCreate(rolledBack, op, failedCreateRecord);
38736
+ }
37552
38737
  await noteRetainedResource(op.resourceType, op.logicalId);
37553
38738
  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"));
37554
38739
  await options.afterOp?.(op.logicalId);
@@ -37582,7 +38767,10 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
37582
38767
  }, ctx.foreignHolder, ctx);
37583
38768
  if (verdict === "gone") {
37584
38769
  logger.info(safeMsg` Rollback: partially-created ${shownLogicalId(op.logicalId)} (${refusalResourceType(op.resourceType)}) is already gone — nothing to delete`);
37585
- if (!recordUnderIdIsNotOwn(op, stateResources)) delete stateResources[op.logicalId];
38770
+ if (!recordUnderIdIsNotOwn(op, stateResources)) {
38771
+ delete stateResources[op.logicalId];
38772
+ noteRolledBackCreate(rolledBack, op, failedCreateRecord);
38773
+ }
37586
38774
  await options.afterOp?.(op.logicalId);
37587
38775
  ctx.recordEvent?.({
37588
38776
  eventType: "ROLLBACK_RESOURCE_SUCCEEDED",
@@ -37649,7 +38837,10 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
37649
38837
  result.warnings++;
37650
38838
  result.leftInPlace++;
37651
38839
  }
37652
- if (!recordUnderIdIsNotOwn(op, stateResources)) delete stateResources[op.logicalId];
38840
+ if (!recordUnderIdIsNotOwn(op, stateResources)) {
38841
+ delete stateResources[op.logicalId];
38842
+ noteRolledBackCreate(rolledBack, op, failedCreateRecord);
38843
+ }
37653
38844
  await options.afterOp?.(op.logicalId);
37654
38845
  ctx.recordEvent?.({
37655
38846
  eventType: "ROLLBACK_RESOURCE_SUCCEEDED",
@@ -37714,7 +38905,8 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
37714
38905
  properties: desiredProps,
37715
38906
  logicalId: op.logicalId,
37716
38907
  physicalId: current.physicalId,
37717
- via: revertVia
38908
+ via: revertVia,
38909
+ nameGeneration: nameGenerationOf(current, op.logicalId)
37718
38910
  }, logger, mask, false)(() => updateWithRollbackRetry(provider, [
37719
38911
  op.logicalId,
37720
38912
  current.physicalId,
@@ -37730,7 +38922,7 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
37730
38922
  }
37731
38923
  ], op.logicalId, logger, options.isInterrupted, secrets, mask));
37732
38924
  if (revertFailedResult) recordNoEchoAttributeValues(revertFailedResult, secrets, desiredProps);
37733
- stateResources[op.logicalId] = maskRestoredNoEchoRecord(redactRollbackRecord(recordAfterRollbackUpdate(prev, revertFailedResult), secrets, prev.properties, prev.attributes), prev, noEcho.substituted);
38925
+ stateResources[op.logicalId] = maskRestoredNoEchoRecord(redactRollbackRecord(withNameGenerationReached(recordAfterRollbackUpdate(prev, revertFailedResult), op.logicalId, op.nameGeneration), secrets, prev.properties, prev.attributes), prev, noEcho.substituted);
37734
38926
  if (updatePartialReason(revertFailedResult) === void 0) inlinePolicyWriters.record(op.logicalId, "update", stateResources[op.logicalId], !deepEqual$1(prev.properties?.["Policies"], (op.attemptedProperties ?? current.properties)?.["Policies"]), revertVia);
37735
38927
  const revertFailedPartial = updatePartialReason(revertFailedResult);
37736
38928
  if (revertFailedPartial !== void 0) logger.warn(mask(` Rollback: ${safe(op.logicalId)} reverted, ${updatePartialMessage(revertFailedPartial)}`));
@@ -38095,6 +39287,69 @@ function healPrintingBag(baseline, logicalId, resource, namingSecrets) {
38095
39287
  return hit.judge(logicalId);
38096
39288
  }
38097
39289
 
39290
+ //#endregion
39291
+ //#region src/deployment/deploy-engine/superseded-retry.ts
39292
+ async function retryCarriedSupersededResources(engine, host, carried, resources, options = {}) {
39293
+ let changed = false;
39294
+ if (carried.length === 0) return changed;
39295
+ const view = {
39296
+ live: () => Object.entries(resources),
39297
+ survivors: () => Object.entries(resources)
39298
+ };
39299
+ for (const entry of carried) {
39300
+ if (engine.interrupted) break;
39301
+ const { logicalId } = entry;
39302
+ const { physicalId, resourceType } = entry.state;
39303
+ const bag = unionOfSecretBags([secretNameBagFor(engine.secretsRun, logicalId), orphanRecordsPrintingBag([entry])]);
39304
+ const holder = supersededHolderOf(entry, resources);
39305
+ if (holder !== void 0) {
39306
+ removeSuperseded(host, entry);
39307
+ changed = true;
39308
+ if (holder.resourceType !== resourceType) {
39309
+ withPrintingSecrets(bag, () => engine.logger.warn(safeMsg` ⚠ ${supersededHeldByOtherTypeWarning(entry, holder.resourceType)}`));
39310
+ continue;
39311
+ }
39312
+ withPrintingSecrets(bag, () => engine.logger.debug(safeMsg`Superseded ${logicalId} (${physicalId}) is held by a record again; not deleting it`));
39313
+ continue;
39314
+ }
39315
+ await withPrintingSecrets(bag, () => withStackRecords(view, async () => {
39316
+ engine.logger.info(safeMsg` Deleting old ${logicalId} (${physicalId}), superseded by an earlier deploy...`);
39317
+ let outcome;
39318
+ try {
39319
+ const route = engine.providerRegistry.getProviderFor({
39320
+ resourceType,
39321
+ ...entry.state.provisionedBy !== void 0 && { provisionedBy: entry.state.provisionedBy }
39322
+ });
39323
+ outcome = await deleteSupersededEntry(entry, {
39324
+ provider: route.provider,
39325
+ region: engine.stackRegion,
39326
+ maskSecrets: createSecretMasker(bag),
39327
+ stop: () => engine.interrupted,
39328
+ once: options.waitInUse !== true
39329
+ });
39330
+ } catch (error) {
39331
+ outcome = {
39332
+ kind: "failed",
39333
+ error
39334
+ };
39335
+ }
39336
+ if (outcome.kind === "failed") {
39337
+ const detail = maskSecretsInText(describeAwsFailure(outcome.error).detail, bag);
39338
+ engine.logger.warn(safeMsg` ⚠ Failed to delete old resource ${logicalId} (${physicalId}): ${detail}. ${SUPERSEDED_KEPT_REMEDY}.`);
39339
+ return;
39340
+ }
39341
+ removeSuperseded(host, entry);
39342
+ changed = true;
39343
+ if (outcome.kind === "notDeleted") {
39344
+ engine.logger.warn(safeMsg` ⚠ ${deleteSkippedMessage(logicalId, physicalId, outcome.reason, "while cleaning up the superseded resource", { commandFreeLine: true })}. Delete it manually — it is no longer tracked in state.`);
39345
+ return;
39346
+ }
39347
+ engine.logger.info(safeMsg` ${green("✓")} Old ${logicalId} (${physicalId}) deleted`);
39348
+ }));
39349
+ }
39350
+ return changed;
39351
+ }
39352
+
38098
39353
  //#endregion
38099
39354
  //#region src/provisioning/name-lookup.ts
38100
39355
  const inFlight = /* @__PURE__ */ new Map();
@@ -38193,7 +39448,8 @@ var GeneratedNameGuard = class GeneratedNameGuard {
38193
39448
  static start(input) {
38194
39449
  const candidates = [];
38195
39450
  for (const change of input.changes) {
38196
- if (change.changeType !== "CREATE") continue;
39451
+ const generation = change.changeType === "UPDATE" ? input.replacementGeneration?.(change) : void 0;
39452
+ if (change.changeType !== "CREATE" && generation === void 0) continue;
38197
39453
  const properties = change.desiredProperties ?? {};
38198
39454
  let decision;
38199
39455
  let name;
@@ -38204,7 +39460,11 @@ var GeneratedNameGuard = class GeneratedNameGuard {
38204
39460
  properties
38205
39461
  });
38206
39462
  if (!replacementCreateAdoptsName(change.resourceType, decision.provisionedBy)) continue;
38207
- name = decision.provider.generatedCreateName?.(change.resourceType, change.logicalId, properties);
39463
+ const derive = () => decision.provider.generatedCreateName?.(change.resourceType, change.logicalId, properties);
39464
+ name = generation === void 0 ? derive() : withNameGeneration({
39465
+ logicalId: change.logicalId,
39466
+ generation
39467
+ }, derive);
38208
39468
  deferred = decision.provider.lookupNeedsResolvedProperties?.(change.resourceType, properties) === true;
38209
39469
  } catch {
38210
39470
  continue;
@@ -38504,7 +39764,7 @@ var GeneratedNameGuard = class GeneratedNameGuard {
38504
39764
  });
38505
39765
  unlicensed.push(c);
38506
39766
  }
38507
- if (unlicensed.length > 0 && input.loadKeptInHistory !== void 0 && evidence !== void 0 && !("error" in evidence) && evidence.retainedAbsent) {
39767
+ if (unlicensed.length > 0 && input.newScheme !== true && input.loadKeptInHistory !== void 0 && evidence !== void 0 && !("error" in evidence) && evidence.retainedAbsent) {
38508
39768
  let kept;
38509
39769
  try {
38510
39770
  this.historyRead ??= input.loadKeptInHistory();
@@ -38667,14 +39927,15 @@ async function lookupAll(input, candidates) {
38667
39927
  };
38668
39928
  }
38669
39929
  async function loadEvidence(input) {
38670
- const [journal, retained] = await Promise.all([input.loadJournal(), input.loadRetained()]);
39930
+ const kept = input.newScheme !== true;
39931
+ const [journal, retained] = await Promise.all([input.loadJournal(), kept ? input.loadRetained() : Promise.resolve([])]);
38671
39932
  const sources = [];
38672
39933
  for (const record of Object.values(input.records)) if (typeof record?.physicalId === "string") sources.push({
38673
39934
  via: "record",
38674
39935
  type: record.resourceType,
38675
39936
  id: record.physicalId
38676
39937
  });
38677
- for (const orphan of input.orphans ?? []) {
39938
+ for (const orphan of kept ? input.orphans ?? [] : []) {
38678
39939
  const s = orphan.state;
38679
39940
  if (typeof s?.physicalId === "string") sources.push({
38680
39941
  via: "orphan",
@@ -38853,7 +40114,7 @@ function warnDropFailed(logger, stackName, region, logicalIds) {
38853
40114
  logger.warn(safeMsg`${hint.intro}`);
38854
40115
  for (const command of hint.commands) logger.warn(safeMsg`Drop with: ${command}`);
38855
40116
  }
38856
- async function deleteJournaledOrphans(orphans, stateResources, stackName, ctx, options = {}) {
40117
+ async function deleteJournaledOrphans(orphans, stateResources, stackName, stackInstanceId, ctx, options = {}) {
38857
40118
  const total = {
38858
40119
  failures: 0,
38859
40120
  warnings: 0,
@@ -38877,7 +40138,10 @@ async function deleteJournaledOrphans(orphans, stateResources, stackName, ctx, o
38877
40138
  break;
38878
40139
  }
38879
40140
  const ops = [...companions ?? [], ...orphanOps];
38880
- const replay = () => withPrintingSecrets(printing, () => withStackName(stackName, () => replayFailedOperations(ops, stateResources, stackName, maskedCtx, {
40141
+ const replay = () => withPrintingSecrets(printing, () => withStackScope(nameScopeOf(stackName, stackInstanceId, {
40142
+ resources: stateResources,
40143
+ ...options.retiredNameGenerations !== void 0 && { retiredNameGenerations: options.retiredNameGenerations }
40144
+ }), () => replayFailedOperations(ops, stateResources, stackName, maskedCtx, {
38881
40145
  emitEnvelope: false,
38882
40146
  inlinePolicyWriters,
38883
40147
  forDestroy: true,
@@ -38963,14 +40227,15 @@ async function settleJournaledOrphansOnSuccess(args) {
38963
40227
  count: orphans.count - unreadable.size - tracked.size
38964
40228
  };
38965
40229
  const writtenThisRun = [...orphans.deployLogicalIds ?? []].filter((id) => Object.prototype.hasOwnProperty.call(stateResources, id)).map((id) => stateResources[id]).filter((r) => r !== void 0 && r !== null && typeof r === "object");
38966
- const outcome = await deleteJournaledOrphans(acting, { ...stateResources }, stackName, {
40230
+ const outcome = await deleteJournaledOrphans(acting, { ...stateResources }, stackName, args.stackInstanceId, {
38967
40231
  ...ctx,
38968
40232
  writtenThisRun,
38969
40233
  foreignHolder: args.foreignHolder,
38970
40234
  orphanDeleteProven: proven
38971
40235
  }, {
38972
40236
  pointAtDrop: false,
38973
- ...args.isInterrupted && { isInterrupted: args.isInterrupted }
40237
+ ...args.isInterrupted && { isInterrupted: args.isInterrupted },
40238
+ retiredNameGenerations: args.retiredNameGenerations
38974
40239
  });
38975
40240
  leftInPlace = outcome.leftInPlace;
38976
40241
  failedDeletes = outcome.failedLogicalIds;
@@ -39425,10 +40690,13 @@ async function revertNestedChildFromJournal(args) {
39425
40690
  refuseMalformedState(base, childStackName, region, refusalRecovery);
39426
40691
  refuseMalformedOrphans(base, childStackName, region, refusalRecovery);
39427
40692
  refuseMalformedOrphanRecords(base, childStackName, region, refusalRecovery);
40693
+ const childNamingScope = nameScopeOf(childStackName, stackInstanceIdOf(base, displayStackName(childStackName)), base);
39428
40694
  const segments = ((await ctx.stateBackend.loadRollbackJournal(childStackName, region))?.segments ?? []).filter((segment) => segment.runId === runId);
39429
40695
  if (segments.length === 0) refuse("its rollback journal holds no segment for this deploy run, so there is no record of what it held before.");
39430
40696
  const stateResources = { ...base.resources };
39431
40697
  const mintedOrphans = [];
40698
+ const rolledBackCreates = [];
40699
+ const supersededHost = {};
39432
40700
  const restoredOutputs = segments.find((s) => s.previousOutputs)?.previousOutputs;
39433
40701
  const restoredReads = segments.find((s) => s.previousCrossStackReads)?.previousCrossStackReads;
39434
40702
  let currentEtag = stateData.etag;
@@ -39437,10 +40705,13 @@ async function revertNestedChildFromJournal(args) {
39437
40705
  const { skippedOutputs: _dropped, ...carried } = base;
39438
40706
  const next = () => ({
39439
40707
  ...carried,
39440
- version: 12,
40708
+ ...stackInstanceIdCarriedFrom(base),
40709
+ ...supersededAfterRollback(base, supersededHost),
40710
+ version: 13,
39441
40711
  region,
39442
40712
  resources: { ...stateResources },
39443
40713
  ...orphansAfterRollback(base, mintedOrphans),
40714
+ ...retiredAfterRollback(base, mintedOrphans, rolledBackCreates),
39444
40715
  ...restoring && restoredOutputs && { outputs: { ...restoredOutputs.outputs } },
39445
40716
  ...restoring && restoredReads && {
39446
40717
  ...restoredReads.imports && { imports: [...restoredReads.imports] },
@@ -39509,12 +40780,15 @@ async function revertNestedChildFromJournal(args) {
39509
40780
  const segment = segments[s];
39510
40781
  const skipPrefix = segment.skipPrefix ?? getCurrentSkipPrefix();
39511
40782
  const split = splitImportedOps(segment.operations, segment);
39512
- const result = await withNestedStackContext(childCtx, () => withSkipPrefix(skipPrefix, () => withStackName(childStackName, () => withCreateTokenLedger(ledgerForStack(ctx.stateBackend, childStackName, region), () => withNestedRevertRun(runId, async (inner) => {
40783
+ const result = await withNestedStackContext(childCtx, () => withSkipPrefix(skipPrefix, () => withStackScope(childNamingScope, () => withCreateTokenLedger(ledgerForStack(ctx.stateBackend, childStackName, region), () => withNestedRevertRun(runId, async (inner) => {
39513
40784
  const replayed = await withPrintingSecrets(journaledOrphanPrintingBag(completedReplayEntries(split.replay), stateResources), () => replayRollback(split.replay, stateResources, childStackName, execCtx, {
39514
40785
  afterOp: save,
39515
40786
  onOrphan: (record) => mintedOrphans.push(record),
40787
+ onRolledBackCreate: (create) => rolledBackCreates.push(create),
39516
40788
  inlinePolicyWriters,
39517
- failedOperations: segment.failedOperations
40789
+ failedOperations: segment.failedOperations,
40790
+ onSuperseded: (entry) => addSuperseded(supersededHost, entry),
40791
+ onSupersededSettled: (entry) => removeSuperseded(supersededHost, entry)
39518
40792
  }));
39519
40793
  for (const [id, below] of inner.settled) settledBelow.set(id, below);
39520
40794
  return {
@@ -39544,13 +40818,18 @@ async function revertNestedChildFromJournal(args) {
39544
40818
 
39545
40819
  //#endregion
39546
40820
  //#region src/deployment/retained-readoption.ts
39547
- function keptForReadoption(resource) {
39548
- if (!replacementCreateAdoptsName(resource.resourceType, resource.provisionedBy)) return false;
40821
+ function keptUnderGeneratedName(resource) {
39549
40822
  if (typeof resource.physicalId !== "string" || resource.physicalId === "") return false;
39550
40823
  const property = explicitNamePropertyFor(resource.resourceType);
39551
40824
  const properties = resource.properties;
39552
40825
  return property === void 0 || !properties?.[property];
39553
40826
  }
40827
+ function keptForRetainedRecord(resource) {
40828
+ return replacementCreateAdoptsName(resource.resourceType, "sdk") ? keptForReadoption(resource) : keptUnderGeneratedName(resource);
40829
+ }
40830
+ function keptForReadoption(resource) {
40831
+ return replacementCreateAdoptsName(resource.resourceType, resource.provisionedBy) && keptUnderGeneratedName(resource);
40832
+ }
39554
40833
  async function recordRetainedForReadoption(backend, stackName, region, kept, logger, earlier) {
39555
40834
  if (kept.length === 0) {
39556
40835
  try {
@@ -39566,10 +40845,10 @@ async function recordRetainedForReadoption(backend, stackName, region, kept, log
39566
40845
  await backend.saveRetainedResources(stackName, region, [...before.filter((e) => !keptIds.has(e.logicalId)), ...kept]);
39567
40846
  } catch (error) {
39568
40847
  if (error instanceof RetainedTimeUnconfirmedError) {
39569
- logger.warn(safeMsg`Recorded the ${String(kept.length)} kept resource(s) of ${displayStackName(stackName)} ` + safeMsg`a later deploy takes back by name, but their time could not be confirmed from S3 ` + safeMsg`(${describeAwsFailure(error.cause).summary}); they carry this machine's clock instead.`);
40848
+ logger.warn(safeMsg`Recorded the ${String(kept.length)} kept resource(s) of ${displayStackName(stackName)}, ` + safeMsg`but their time could not be confirmed from S3 ` + safeMsg`(${describeAwsFailure(error.cause).summary}); they carry this machine's clock instead.`);
39570
40849
  return;
39571
40850
  }
39572
- logger.warn(safeMsg`Could not record the ${String(kept.length)} kept resource(s) of ${displayStackName(stackName)} ` + safeMsg`a later deploy takes back by name (${describeAwsFailure(error).summary}). That deploy ` + `refuses to create them over the kept ones; adopt them with 'cdkd import' then.`);
40851
+ logger.warn(safeMsg`Could not record the ${String(kept.length)} kept resource(s) of ${displayStackName(stackName)} ` + safeMsg`(${describeAwsFailure(error).summary}). A later deploy may refuse to create one over ` + `its kept copy, or not name them; adopt a kept one with 'cdkd import'.`);
39573
40852
  }
39574
40853
  }
39575
40854
  var KeptForReadoption = class {
@@ -39630,7 +40909,7 @@ async function provisionDelete(deps, logicalId, change, stateResources, stackNam
39630
40909
  deps.logger.info(safeMsg`Retaining ${logicalId} (${resourceType}) - DeletionPolicy: ${deletionPolicy}`);
39631
40910
  delete stateResources[logicalId];
39632
40911
  await noteRetainedResource(resourceType, logicalId);
39633
- if (keptForReadoption(currentResource)) {
40912
+ if (getCurrentStackInstanceId() !== void 0 ? keptForRetainedRecord(currentResource) : keptForReadoption(currentResource)) {
39634
40913
  let kept = keptByEngine.get(deps.owner);
39635
40914
  if (kept === void 0) {
39636
40915
  kept = new KeptForReadoption(deps.stateBackend, stackName, deps.stackRegion, deps.logger);
@@ -39809,6 +41088,9 @@ async function settlePending(params, pending) {
39809
41088
  const reader = params.reader();
39810
41089
  const isUnique = params.identifierUnique ?? ccIdentifierIsUnique;
39811
41090
  const stackShown = displayStackName(stackName);
41091
+ const source = params.stackInstanceId;
41092
+ let resolvedId;
41093
+ const stackInstanceId = () => resolvedId ??= typeof source === "function" ? source() : Promise.resolve(source);
39812
41094
  const settlements = /* @__PURE__ */ new Map();
39813
41095
  const unresolved = [];
39814
41096
  for (const [token, op] of pending) {
@@ -39840,7 +41122,9 @@ async function settlePending(params, pending) {
39840
41122
  identifier: made,
39841
41123
  attemptedProperties: op.attemptedProperties,
39842
41124
  logicalId: op.logicalId,
39843
- stackName
41125
+ stackName,
41126
+ stackInstanceId: await stackInstanceId(),
41127
+ nameGenerationsThrough: generationsThrough(op, held)
39844
41128
  });
39845
41129
  const ended = outcome.status === "succeeded" ? "completed" : outcome.status === "cancelled" ? "was cancelled" : "failed";
39846
41130
  if (outcome.status === "succeeded" && unique && held === void 0 && params.resume?.(op, made)) {
@@ -39894,7 +41178,7 @@ function holdsSecretReference(properties) {
39894
41178
  if (properties === void 0) return false;
39895
41179
  return carriesSecretMask(properties) || JSON.stringify(properties).includes("{{resolve:");
39896
41180
  }
39897
- const ccIdentifierIsUnique = async ({ resourceType, identifier, attemptedProperties, logicalId, stackName }) => {
41181
+ const ccIdentifierIsUnique = async ({ resourceType, identifier, attemptedProperties, logicalId, stackName, stackInstanceId, nameGenerationsThrough }) => {
39898
41182
  if (attemptedProperties === void 0 || holdsSecretReference(attemptedProperties)) return false;
39899
41183
  const [fields, readOnly] = await Promise.all([getPrimaryIdentifierFields(resourceType), getTopLevelReadOnlyProperties(resourceType)]);
39900
41184
  if (fields === void 0 || readOnly === void 0) return false;
@@ -39909,9 +41193,15 @@ const ccIdentifierIsUnique = async ({ resourceType, identifier, attemptedPropert
39909
41193
  collect(attemptedProperties);
39910
41194
  return parts.some((part, i) => {
39911
41195
  if (part === "" || !readOnly.has(fields[i])) return false;
39912
- return !nameSegments(part).some((segment) => sent.has(segment) || looksLikeCdkdGeneratedName(segment, logicalId, stackName));
41196
+ return !nameSegments(part).some((segment) => sent.has(segment) || looksLikeCdkdGeneratedName(segment, logicalId, stackName, stackInstanceId, nameGenerationsThrough === void 0 ? void 0 : { through: nameGenerationsThrough }));
39913
41197
  });
39914
41198
  };
41199
+ function generationsThrough(op, held) {
41200
+ const own = isNameGeneration(op.nameGeneration) ? op.nameGeneration : 0;
41201
+ let recorded = 0;
41202
+ for (const value of [held?.nameGeneration, held?.nameGenerationHigh]) if (isNameGeneration(value)) recorded = Math.max(recorded, value);
41203
+ return Math.max(own, recorded) + 1;
41204
+ }
39915
41205
  function nameSegments(value) {
39916
41206
  const text = value.toLowerCase();
39917
41207
  const arn = /^arn:[^:]*:[^:]*:[^:]*:[^:]*:(.*)$/.exec(text);
@@ -39927,345 +41217,6 @@ function withoutPendingRequest(op) {
39927
41217
  return rest;
39928
41218
  }
39929
41219
 
39930
- //#endregion
39931
- //#region src/state/deployment-events-store.ts
39932
- const DEPLOYMENT_EVENTS_MAX_INDEX_RUNS = 20;
39933
- const FLUSH_INTERVAL_MS = 2e3;
39934
- const FLUSH_EVENT_THRESHOLD = 50;
39935
- function newDeploymentRunId(now = /* @__PURE__ */ new Date()) {
39936
- return `${now.toISOString().replace(/[-:.]/g, "")}-${randomUUID().slice(0, 8)}`;
39937
- }
39938
- function deploymentEventsKey(prefix, stackName, region, runId) {
39939
- return `${prefix}/${stackName}/${region}/deployments/${runId}.jsonl`;
39940
- }
39941
- function deploymentEventsIndexKey(prefix, stackName, region) {
39942
- return `${prefix}/${stackName}/${region}/deployments/index.json`;
39943
- }
39944
- function deploymentsDirPrefix(prefix, stackName, region) {
39945
- return `${prefix}/${stackName}/${region}/deployments/`;
39946
- }
39947
- const DEPLOYMENT_EVENTS_OBJECT_DESCRIPTION = "deployment-event streams and their index, whose recorded error messages can quote values the secret mask does not catch";
39948
- async function deleteAndPurgeEventObjects(backend, keys, dirPrefix, scope = "deleted-keys") {
39949
- if (keys.length === 0) return void 0;
39950
- let swept;
39951
- try {
39952
- await backend.deleteRawObjects(keys);
39953
- } finally {
39954
- if (scope === "whole-dir") swept = await backend.purgeNoncurrentVersionsUnderPrefix(dirPrefix, { objectDescription: DEPLOYMENT_EVENTS_OBJECT_DESCRIPTION });
39955
- else await backend.purgeNoncurrentVersions(keys, {
39956
- ...keys.length > 1 && { listPrefix: dirPrefix },
39957
- objectDescription: DEPLOYMENT_EVENTS_OBJECT_DESCRIPTION
39958
- });
39959
- }
39960
- return swept;
39961
- }
39962
- function runIdTimestampMs(runId) {
39963
- const m = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(\d{3})Z/.exec(runId);
39964
- if (!m) return null;
39965
- const iso = `${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6]}.${m[7]}Z`;
39966
- const ms = Date.parse(iso);
39967
- return Number.isNaN(ms) ? null : ms;
39968
- }
39969
- function runIdFromJsonlKey(key, dirPrefix) {
39970
- if (!key.startsWith(dirPrefix) || !key.endsWith(".jsonl")) return null;
39971
- return key.slice(dirPrefix.length, -6);
39972
- }
39973
- var DeploymentEventsStore = class {
39974
- logger = getLogger().child("DeploymentEvents");
39975
- backend;
39976
- stackName;
39977
- region;
39978
- command;
39979
- runId;
39980
- cdkdVersion;
39981
- startedAt;
39982
- events = [];
39983
- persistedCount = 0;
39984
- flushTimer;
39985
- writeChain = Promise.resolve();
39986
- warnedOnce = false;
39987
- finalized = false;
39988
- constructor(backend, options) {
39989
- this.backend = backend;
39990
- this.stackName = options.stackName;
39991
- this.region = options.region;
39992
- this.command = options.command;
39993
- this.runId = options.runId ?? newDeploymentRunId();
39994
- this.cdkdVersion = options.cdkdVersion ?? getCdkdVersion();
39995
- this.startedAt = (/* @__PURE__ */ new Date()).toISOString();
39996
- }
39997
- record(event) {
39998
- try {
39999
- if (this.finalized) return;
40000
- this.events.push({
40001
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
40002
- ...event
40003
- });
40004
- if (this.events.length - this.persistedCount >= FLUSH_EVENT_THRESHOLD) this.scheduleFlush(0);
40005
- else this.scheduleFlush(FLUSH_INTERVAL_MS);
40006
- } catch {}
40007
- }
40008
- async finalize(result) {
40009
- if (this.finalized) return;
40010
- this.finalized = true;
40011
- if (this.flushTimer) {
40012
- clearTimeout(this.flushTimer);
40013
- this.flushTimer = void 0;
40014
- }
40015
- if (this.events.length === 0) return;
40016
- await this.enqueueWrite(async () => {
40017
- const indexRead = this.readIndexRuns();
40018
- indexRead.catch(() => {});
40019
- await this.doFlush();
40020
- await this.writeIndexAndPrune(result, await indexRead);
40021
- });
40022
- }
40023
- async writeIndexAndPrune(result, existingRuns) {
40024
- const { key, file, runs } = this.buildIndexUpdate(result, existingRuns);
40025
- const keptRunIds = runs.map((r) => r.runId);
40026
- const willPrune = keptRunIds.length >= 20;
40027
- const dirPrefix = deploymentsDirPrefix(this.backend.prefix, this.stackName, this.region);
40028
- const staleKeysRead = willPrune ? this.backend.listRawKeys(dirPrefix) : void 0;
40029
- staleKeysRead?.catch(() => {});
40030
- await this.backend.putRawObject(key, JSON.stringify(file, null, 2));
40031
- if (!staleKeysRead) return;
40032
- await this.pruneSupersededRunFiles(keptRunIds, dirPrefix, await staleKeysRead);
40033
- }
40034
- async drain() {
40035
- await this.writeChain;
40036
- }
40037
- scheduleFlush(delayMs) {
40038
- if (this.flushTimer) {
40039
- if (delayMs > 0) return;
40040
- clearTimeout(this.flushTimer);
40041
- }
40042
- this.flushTimer = setTimeout(() => {
40043
- this.flushTimer = void 0;
40044
- this.enqueueWrite(() => this.doFlush());
40045
- }, delayMs);
40046
- this.flushTimer.unref?.();
40047
- }
40048
- enqueueWrite(op) {
40049
- const next = this.writeChain.then(op).catch((err) => {
40050
- this.warnOnce(`Failed to persist deployment events for run ${this.runId}: ${describeAwsFailure(err).detail}`);
40051
- });
40052
- this.writeChain = next;
40053
- return next;
40054
- }
40055
- async doFlush() {
40056
- if (this.events.length === 0 || this.events.length === this.persistedCount) return;
40057
- const snapshotCount = this.events.length;
40058
- const body = this.events.slice(0, snapshotCount).map((e) => JSON.stringify(e)).join("\n");
40059
- await this.backend.putRawObject(deploymentEventsKey(this.backend.prefix, this.stackName, this.region, this.runId), body + "\n", "application/x-ndjson");
40060
- this.persistedCount = snapshotCount;
40061
- }
40062
- async readIndexRuns() {
40063
- const key = deploymentEventsIndexKey(this.backend.prefix, this.stackName, this.region);
40064
- try {
40065
- const raw = await this.backend.getRawObject(key);
40066
- if (raw !== null) {
40067
- const parsed = JSON.parse(raw);
40068
- if (Array.isArray(parsed.runs)) return parsed.runs;
40069
- }
40070
- } catch (err) {
40071
- this.logger.debug(`Deployment-events index unreadable, rewriting: ${describeAwsFailure(err).detail}`);
40072
- }
40073
- return [];
40074
- }
40075
- buildIndexUpdate(result, existingRuns) {
40076
- const key = deploymentEventsIndexKey(this.backend.prefix, this.stackName, this.region);
40077
- const runs = [{
40078
- runId: this.runId,
40079
- command: this.command,
40080
- cdkdVersion: this.cdkdVersion,
40081
- startedAt: this.startedAt,
40082
- finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
40083
- result,
40084
- eventCount: this.persistedCount
40085
- }, ...existingRuns.filter((r) => r.runId !== this.runId)].slice(0, 20);
40086
- return {
40087
- key,
40088
- file: {
40089
- indexVersion: 1,
40090
- stackName: this.stackName,
40091
- region: this.region,
40092
- runs,
40093
- lastModified: Date.now()
40094
- },
40095
- runs
40096
- };
40097
- }
40098
- async pruneSupersededRunFiles(keptRunIds, dirPrefix, keys) {
40099
- if (keptRunIds.length < 20) return;
40100
- const cutoff = keptRunIds.reduce((min, id) => id < min ? id : min, keptRunIds[0]);
40101
- const stale = keys.filter((k) => {
40102
- const runId = runIdFromJsonlKey(k, dirPrefix);
40103
- return runId !== null && runId < cutoff;
40104
- });
40105
- if (stale.length === 0) return;
40106
- await deleteAndPurgeEventObjects(this.backend, stale, dirPrefix);
40107
- this.logger.debug(`Pruned ${stale.length} superseded deployment-event stream(s) for ${this.stackName} (${this.region})`);
40108
- }
40109
- warnOnce(message) {
40110
- if (this.warnedOnce) {
40111
- this.logger.debug(message);
40112
- return;
40113
- }
40114
- this.warnedOnce = true;
40115
- this.logger.warn(`${message} — continuing; deployment events are best-effort and never block the run.`);
40116
- }
40117
- };
40118
- var DeploymentEventsReader = class {
40119
- backend;
40120
- constructor(backend) {
40121
- this.backend = backend;
40122
- }
40123
- async listRegions(stackName) {
40124
- const prefix = `${this.backend.prefix}/${stackName}/`;
40125
- const keys = await this.backend.listRawKeys(prefix);
40126
- const regions = /* @__PURE__ */ new Set();
40127
- for (const key of keys) {
40128
- const segments = key.slice(prefix.length).split("/");
40129
- if (segments.length === 3 && segments[1] === "deployments" && segments[0]) regions.add(segments[0]);
40130
- }
40131
- return [...regions].sort();
40132
- }
40133
- async listRuns(stackName, region) {
40134
- const key = deploymentEventsIndexKey(this.backend.prefix, stackName, region);
40135
- try {
40136
- const raw = await this.backend.getRawObject(key);
40137
- if (raw !== null) {
40138
- const parsed = JSON.parse(raw);
40139
- if (Array.isArray(parsed.runs)) return parsed.runs;
40140
- }
40141
- } catch {}
40142
- const dirPrefix = `${this.backend.prefix}/${stackName}/${region}/deployments/`;
40143
- const runIds = (await this.backend.listRawKeys(dirPrefix)).filter((k) => k.endsWith(".jsonl")).map((k) => k.slice(dirPrefix.length, -6)).sort().reverse();
40144
- return Promise.all(runIds.map((runId) => this.summarizeRunFromJsonl(stackName, region, runId)));
40145
- }
40146
- async summarizeRunFromJsonl(stackName, region, runId) {
40147
- const fallback = {
40148
- runId,
40149
- command: "deploy",
40150
- cdkdVersion: "unknown",
40151
- startedAt: "",
40152
- finishedAt: "",
40153
- result: "UNKNOWN",
40154
- eventCount: 0
40155
- };
40156
- let events = null;
40157
- try {
40158
- events = await this.readRunEvents(stackName, region, runId);
40159
- } catch {
40160
- return fallback;
40161
- }
40162
- if (events === null || events.length === 0) return fallback;
40163
- const started = events.find((e) => e.eventType === "RUN_STARTED");
40164
- let finished;
40165
- for (const e of events) if (e.eventType === "RUN_FINISHED") finished = e;
40166
- return {
40167
- runId,
40168
- command: started?.command ?? fallback.command,
40169
- cdkdVersion: started?.cdkdVersion ?? fallback.cdkdVersion,
40170
- startedAt: started?.timestamp ?? fallback.startedAt,
40171
- finishedAt: finished?.timestamp ?? fallback.finishedAt,
40172
- result: finished?.result ?? "UNKNOWN",
40173
- eventCount: events.length
40174
- };
40175
- }
40176
- async readRunEvents(stackName, region, runId) {
40177
- const key = deploymentEventsKey(this.backend.prefix, stackName, region, runId);
40178
- const raw = await this.backend.getRawObject(key);
40179
- if (raw === null) return null;
40180
- const events = [];
40181
- for (const line of raw.split("\n")) {
40182
- const trimmed = line.trim();
40183
- if (!trimmed) continue;
40184
- try {
40185
- events.push(JSON.parse(trimmed));
40186
- } catch {}
40187
- }
40188
- return events;
40189
- }
40190
- async pruneRuns(stackName, region, opts = {}) {
40191
- const dirPrefix = deploymentsDirPrefix(this.backend.prefix, stackName, region);
40192
- const keys = await this.backend.listRawKeys(dirPrefix);
40193
- const runIdsDesc = keys.map((k) => runIdFromJsonlKey(k, dirPrefix)).filter((id) => id !== null).sort().reverse();
40194
- const indexKey = deploymentEventsIndexKey(this.backend.prefix, stackName, region);
40195
- const indexExisted = keys.includes(indexKey);
40196
- if (opts.all === true) {
40197
- const toDelete = runIdsDesc.map((id) => deploymentEventsKey(this.backend.prefix, stackName, region, id));
40198
- const earlierVersions = await deleteAndPurgeEventObjects(this.backend, [...toDelete, indexKey], dirPrefix, "whole-dir");
40199
- return {
40200
- deletedRunIds: runIdsDesc,
40201
- remainingRunIds: [],
40202
- indexDeleted: indexExisted,
40203
- ...earlierVersions !== void 0 && { earlierVersions }
40204
- };
40205
- }
40206
- const noGuards = opts.keep === void 0 && opts.olderThanMs === void 0;
40207
- const keep = opts.keep ?? (noGuards ? 20 : void 0);
40208
- const protectedByCount = keep === void 0 ? /* @__PURE__ */ new Set() : new Set(runIdsDesc.slice(0, keep));
40209
- let candidates = runIdsDesc.filter((id) => !protectedByCount.has(id));
40210
- if (opts.olderThanMs !== void 0) {
40211
- const cutoff = (opts.now ?? /* @__PURE__ */ new Date()).getTime() - opts.olderThanMs;
40212
- candidates = candidates.filter((id) => {
40213
- const t = runIdTimestampMs(id);
40214
- return t !== null && t < cutoff;
40215
- });
40216
- }
40217
- if (candidates.length === 0) return {
40218
- deletedRunIds: [],
40219
- remainingRunIds: runIdsDesc,
40220
- indexDeleted: false
40221
- };
40222
- const deletedSet = new Set(candidates);
40223
- const deleteKeys = candidates.map((id) => deploymentEventsKey(this.backend.prefix, stackName, region, id));
40224
- const remainingRunIds = runIdsDesc.filter((id) => !deletedSet.has(id));
40225
- if (remainingRunIds.length === 0) {
40226
- await deleteAndPurgeEventObjects(this.backend, [...deleteKeys, indexKey], dirPrefix);
40227
- return {
40228
- deletedRunIds: candidates,
40229
- remainingRunIds,
40230
- indexDeleted: indexExisted
40231
- };
40232
- }
40233
- await this.rewriteIndexAfterPrune(indexKey, stackName, region, deletedSet);
40234
- await deleteAndPurgeEventObjects(this.backend, deleteKeys, dirPrefix);
40235
- return {
40236
- deletedRunIds: candidates,
40237
- remainingRunIds,
40238
- indexDeleted: false
40239
- };
40240
- }
40241
- async rewriteIndexAfterPrune(indexKey, stackName, region, deletedRunIds) {
40242
- let raw;
40243
- try {
40244
- raw = await this.backend.getRawObject(indexKey);
40245
- } catch {
40246
- return;
40247
- }
40248
- if (raw === null) return;
40249
- let parsed;
40250
- try {
40251
- parsed = JSON.parse(raw);
40252
- } catch {
40253
- return;
40254
- }
40255
- if (!Array.isArray(parsed.runs)) return;
40256
- const remaining = parsed.runs.filter((r) => !deletedRunIds.has(r.runId));
40257
- if (remaining.length === parsed.runs.length) return;
40258
- const file = {
40259
- indexVersion: 1,
40260
- stackName,
40261
- region,
40262
- runs: remaining,
40263
- lastModified: Date.now()
40264
- };
40265
- await this.backend.putRawObject(indexKey, JSON.stringify(file, null, 2));
40266
- }
40267
- };
40268
-
40269
41220
  //#endregion
40270
41221
  //#region src/deployment/orphan-adoption.ts
40271
41222
  function templateStillDeclares(template, record, nameProperties) {
@@ -40281,9 +41232,19 @@ async function planOrphanAdoption(params) {
40281
41232
  adopted: {},
40282
41233
  remaining: [],
40283
41234
  refusals: [],
40284
- notices: []
41235
+ notices: [],
41236
+ warnings: []
40285
41237
  };
40286
41238
  if (params.records.length === 0) return outcome;
41239
+ if (params.newScheme === true) {
41240
+ for (const record of params.records) {
41241
+ if (params.managedLogicalIds.has(record.logicalId)) continue;
41242
+ outcome.remaining.push(record);
41243
+ if ((params.template.Resources?.[record.logicalId])?.Type !== record.state.resourceType) continue;
41244
+ outcome.warnings.push(`${displayIdent(record.logicalId)} (${displayIdent(record.state.resourceType)}) was left in AWS by an earlier rollback as ${displaySafe(record.state.physicalId)} (DeletionPolicy: Retain). This deploy does not take it back: it creates a new resource under a new name, as CloudFormation does. To manage the kept resource again, adopt it with 'cdkd import'.`);
41245
+ }
41246
+ return outcome;
41247
+ }
40287
41248
  let siblingClaims;
40288
41249
  const claims = async () => siblingClaims ??= await params.readSiblingClaims();
40289
41250
  for (const record of params.records) {
@@ -40473,9 +41434,11 @@ async function adoptRollbackOrphans(deps, currentState, effectiveTemplate) {
40473
41434
  selfRegion: deps.stackRegion,
40474
41435
  logger: deps.logger
40475
41436
  }),
40476
- logger: { debug: (m) => deps.logger.debug(m) }
41437
+ logger: { debug: (m) => deps.logger.debug(m) },
41438
+ newScheme: currentState.stackInstanceId !== void 0
40477
41439
  }));
40478
41440
  for (const notice of plan.notices) deps.logger.info(maskSecretsInText(notice, named));
41441
+ for (const warning of plan.warnings) deps.logger.warn(maskSecretsInText(warning, named));
40479
41442
  const adoptedFrom = /* @__PURE__ */ new Map();
40480
41443
  adoptedOrphanRecords.set(deps.owner, adoptedFrom);
40481
41444
  if (plan.refusals.length > 0) throw new Error(safeMsg`Deploy refused — cdkd left ${plan.refusals.length} resource(s) in AWS that it cannot ` + "safely re-adopt:" + plan.refusals.map((r) => safeMsg`\n ${maskSecretsInText(r, named)}`).join(""));
@@ -40534,6 +41497,7 @@ async function performRollback(deps, completedOperations, stateResources, stackN
40534
41497
  createdResourceHolder: crossPrefixHolder === void 0 ? void 0 : () => crossPrefixAnswer ??= crossPrefixHolder(stackName)
40535
41498
  };
40536
41499
  const inlinePolicyWriters = new RollbackInlinePolicyWriters();
41500
+ const supersededHost = {};
40537
41501
  const runId = deps.options.eventRecorder?.runId;
40538
41502
  const { result, failed, run } = await withNestedRevertRun(runId, async (scope) => {
40539
41503
  return {
@@ -40546,7 +41510,10 @@ async function performRollback(deps, completedOperations, stateResources, stackN
40546
41510
  })) : void 0,
40547
41511
  result: await withPrintingSecrets(replayPrintingBag(deps, completedReplayEntries(completedOperations), stateResources), () => replayRollback(completedOperations, stateResources, stackName, ctx, {
40548
41512
  inlinePolicyWriters,
40549
- failedOperations
41513
+ failedOperations,
41514
+ isInterrupted: () => deps.userInterrupted(),
41515
+ onSuperseded: (entry) => addSuperseded(supersededHost, entry),
41516
+ onSupersededSettled: (entry) => removeSuperseded(supersededHost, entry)
40550
41517
  })),
40551
41518
  run: scope
40552
41519
  };
@@ -40557,7 +41524,10 @@ async function performRollback(deps, completedOperations, stateResources, stackN
40557
41524
  failures: result.failures + (failed?.failures ?? 0),
40558
41525
  warnings: result.warnings + run.warnings + (failed?.warnings ?? 0),
40559
41526
  skipped: result.skipped + (failed?.skipped ?? 0),
41527
+ interrupted: result.interrupted,
40560
41528
  orphaned: [...failed?.orphaned ?? [], ...result.orphaned],
41529
+ rolledBackCreates: [...failed?.rolledBackCreates ?? [], ...result.rolledBackCreates],
41530
+ superseded: supersededHost.supersededResources ?? [],
40561
41531
  settledNested: run.settled,
40562
41532
  remainingFailedOps: failedOperations.filter((_, i) => !handled.has(i))
40563
41533
  };
@@ -40600,9 +41570,11 @@ async function settleJournalAfterSuccess(deps, stackName, completedOperations, p
40600
41570
  const own = await settleJournaledOrphansOnSuccess({
40601
41571
  stateBackend: deps.stateBackend,
40602
41572
  stackName,
41573
+ stackInstanceId: stackInstanceIdOf(previousState, displayStackName(stackName)),
40603
41574
  region: deps.stackRegion,
40604
41575
  stateResources: finalResources,
40605
41576
  rollbackOrphans: previousState.orphans,
41577
+ retiredNameGenerations: previousState.retiredNameGenerations,
40606
41578
  newerOperations: completedOperations,
40607
41579
  ...deployRunId !== void 0 && { deployRunId },
40608
41580
  foreignHolder: foreignHolderFor({
@@ -40628,9 +41600,11 @@ async function settleJournalAfterSuccess(deps, stackName, completedOperations, p
40628
41600
  const settled = await settleJournaledOrphansOnSuccess({
40629
41601
  stateBackend: deps.stateBackend,
40630
41602
  stackName: child,
41603
+ stackInstanceId: stackInstanceIdOf(childState, displayStackName(child)),
40631
41604
  region: deps.stackRegion,
40632
41605
  stateResources: childState?.resources,
40633
41606
  rollbackOrphans: childState?.orphans,
41607
+ retiredNameGenerations: childState?.retiredNameGenerations,
40634
41608
  newerOperations: [],
40635
41609
  ...deployRunId !== void 0 && { deployRunId },
40636
41610
  foreignHolder: foreignHolderFor({
@@ -40786,58 +41760,6 @@ async function orphanUntakenResumes(deps, stackName, finalResources) {
40786
41760
  }
40787
41761
  }
40788
41762
 
40789
- //#endregion
40790
- //#region src/deployment/kept-in-history.ts
40791
- function keptInEarlierRecords(records) {
40792
- const out = [];
40793
- for (const record of records) for (const [logicalId, raw] of Object.entries(record.resources)) {
40794
- if (raw === null || typeof raw !== "object") continue;
40795
- const r = raw;
40796
- if (typeof r.resourceType !== "string" || typeof r.physicalId !== "string") continue;
40797
- if (r.physicalId === "") continue;
40798
- if (!shouldRetainResource(r.deletionPolicy)) continue;
40799
- out.push({
40800
- logicalId,
40801
- resourceType: r.resourceType,
40802
- physicalId: r.physicalId,
40803
- ...record.writtenAt !== void 0 && { keptAt: record.writtenAt }
40804
- });
40805
- }
40806
- return out;
40807
- }
40808
- function keptInEventHistory(runs, stackName) {
40809
- const made = /* @__PURE__ */ new Map();
40810
- const out = [];
40811
- for (const events of runs) for (const e of events) {
40812
- if (e.stackName !== stackName || typeof e.logicalId !== "string") continue;
40813
- if (typeof e.resourceType !== "string") continue;
40814
- if (e.eventType === "RESOURCE_SUCCEEDED" && (e.operation === "CREATE" || e.operation === "UPDATE") && typeof e.physicalId === "string" && e.physicalId !== "") made.set(e.logicalId, {
40815
- resourceType: e.resourceType,
40816
- physicalId: e.physicalId
40817
- });
40818
- else if (e.eventType === "RESOURCE_RETAINED") {
40819
- const m = made.get(e.logicalId);
40820
- if (m !== void 0 && m.resourceType === e.resourceType) {
40821
- const keptAt = Date.parse(e.timestamp);
40822
- out.push({
40823
- logicalId: e.logicalId,
40824
- ...m,
40825
- ...Number.isFinite(keptAt) && { keptAt }
40826
- });
40827
- }
40828
- }
40829
- }
40830
- return out;
40831
- }
40832
- async function loadKeptInHistory(sources, stackName, region) {
40833
- const runsOf = stackName.split("~")[0];
40834
- const [records, runs] = await Promise.all([sources.earlierStateResources(stackName, region).catch(() => []), (async () => {
40835
- const listed = (await sources.listRuns(runsOf, region)).slice(0, 20);
40836
- return (await Promise.all(listed.map((r) => sources.readRunEvents(runsOf, region, r.runId).catch(() => null)))).filter((e) => e !== null).reverse();
40837
- })().catch(() => [])]);
40838
- return [...keptInEarlierRecords(records), ...keptInEventHistory(runs, stackName)];
40839
- }
40840
-
40841
41763
  //#endregion
40842
41764
  //#region src/provisioning/canonicalize-properties.ts
40843
41765
  function makeCanonicalizePropertiesFn(registry) {
@@ -42219,7 +43141,7 @@ async function requireDeploymentApproval(args) {
42219
43141
  } catch (error) {
42220
43142
  throw error instanceof Error ? markNonRetryable(error) : error;
42221
43143
  }
42222
- if (!approved) throw markNonRetryable(new CdkdError(safeMsg`Deployment of stack ${args.stackName} was not approved (--require-approval=${level}). Nothing was changed.`, "DEPLOY_NOT_APPROVED"));
43144
+ if (!approved) throw markNonRetryable(new DeploymentNotApprovedError(safeMsg`Deployment of stack ${args.stackName} was not approved (--require-approval=${level}). Nothing was changed.`));
42223
43145
  return approve.autoApproves !== true;
42224
43146
  }
42225
43147
  async function checkDestructivePlan(args) {
@@ -42276,7 +43198,7 @@ async function requireOutputsOnlyApproval(args) {
42276
43198
  } catch (error) {
42277
43199
  throw error instanceof Error ? markNonRetryable(error) : error;
42278
43200
  }
42279
- 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"));
43201
+ if (!approved) throw markNonRetryable(new DeploymentNotApprovedError(safeMsg`Deployment of stack ${args.stackName} was not approved (--require-approval=any-change). Nothing was changed.`));
42280
43202
  }
42281
43203
  function isNoEchoPromotionOnly(change) {
42282
43204
  return change.changeType === "UPDATE" && (change.attributeChanges?.length ?? 0) === 0 && (change.propertyChanges?.length ?? 0) > 0 && change.propertyChanges.every((pc) => pc.noEchoPromoted === true);
@@ -43280,60 +44202,10 @@ function peekRoutingForLabel(providerRegistry, options, change, existingState, s
43280
44202
  }
43281
44203
  return deriveLabelRouting(change, existingState, providerRegistry, isPinnedToCcApi(options, stackName, logicalId));
43282
44204
  }
43283
- function preparePropertiesForCcApi(providerRegistry, resourceType, resolvedProps, logicalId) {
44205
+ function preparePropertiesForCcApi(providerRegistry, resourceType, resolvedProps, logicalId, underivable = "refuse") {
43284
44206
  const sdkProvider = providerRegistry.getRegisteredTypes().includes(resourceType) ? providerRegistry.getProvider(resourceType) : void 0;
43285
44207
  if (sdkProvider?.preparePropertiesForFallback) return sdkProvider.preparePropertiesForFallback(logicalId, resourceType, resolvedProps);
43286
- return applyDefaultNameForFallback(logicalId, resourceType, resolvedProps);
43287
- }
43288
-
43289
- //#endregion
43290
- //#region src/deployment/deploy-engine/name-collision.ts
43291
- function replacementNameOrigin(logicalId, physicalId) {
43292
- if (looksLikeCdkdGeneratedName(physicalId, logicalId, getCurrentStackName())) return {
43293
- descriptor: `The physical name (${physicalId}) was GENERATED by cdkd from the construct's logical id rather than declared in your template, and that derivation has no random component — so the new resource asks for exactly the name the existing one still holds`,
43294
- remedy: "Either give the resource an explicit physical name in your CDK code, or rename the CONSTRUCT (its id feeds the generated name — note that this replaces the resource rather than renaming it in place)"
43295
- };
43296
- return {
43297
- descriptor: `The resource has a user-supplied physical name (${physicalId})`,
43298
- remedy: "Either rename the resource in your CDK code (a fresh name lets the safe create-first order proceed)"
43299
- };
43300
- }
43301
- function orphanedNameCollisionAdvice(providerRegistry, changeType, logicalId, error) {
43302
- if (changeType !== "CREATE") return void 0;
43303
- if (!(error instanceof ProvisioningError)) return void 0;
43304
- const physicalId = error.physicalId;
43305
- if (!physicalId) return void 0;
43306
- const replayed = isReplayedNameCollisionFrom(error, logicalId, (link) => isAuxiliaryMarkOf(link, RETRY_AUXILIARY_OWNER));
43307
- if (!replayed && !isNameCollisionErrorFrom(error, logicalId)) return void 0;
43308
- const stackName = getCurrentStackName();
43309
- if (!looksLikeCdkdGeneratedName(physicalId, logicalId, stackName)) return void 0;
43310
- if (!stackName) return void 0;
43311
- const safeId = displaySafe(physicalId, { asciiOnly: true });
43312
- const safeStack = displaySafe(stackName, { asciiOnly: true });
43313
- const safeLogicalId = displaySafe(logicalId, { asciiOnly: true });
43314
- const idShown = isInertUnquoted(safeId);
43315
- const shownId = idShown ? safeId : "a name that cannot be shown safely here";
43316
- const shownLogicalId = isInertUnquoted(safeLogicalId) ? safeLogicalId : "A resource whose logical id cannot be shown safely here";
43317
- const diagnosis = replayed ? `${shownLogicalId}: the name AWS reports as taken (${shownId}) is most likely held by a resource THIS create made. An earlier attempt of it ended without a clear verdict (a server error, a dropped connection or a timeout, or a failure after the create call itself may have succeeded), and the retry then found the name taken. A dropped connection can also happen before the request reached AWS, so the holder may be another resource: confirm it is yours before deleting or importing it. cdkd records a resource only once its create succeeds, so this one is in no state file: no rollback or cdkd destroy will remove it, and re-running collides with it again.` : `${shownLogicalId}: the name AWS reports as taken (${shownId}) is one cdkd DERIVED from the logical id, and that derivation has no random component — so this is most likely a resource an earlier cdkd run left behind. A rollback leaves a resource carrying DeletionPolicy: Retain in AWS and drops it from state, as CloudFormation does; what differs is the name. CloudFormation would generate a fresh one for an unnamed resource and redeploy clean, whereas cdkd asks again for the name the orphan still holds — so re-running does not clear this.`;
43318
- const deleteArm = `If it is not a resource you want to keep, delete ${idShown ? safeId : "it"} in AWS — after confirming it holds nothing you need${replayed ? "" : ", since Retain is what kept it"} — and re-deploy.`;
43319
- let canImport;
43320
- try {
43321
- canImport = typeof providerRegistry.getProvider(error.resourceType).import === "function";
43322
- } catch {
43323
- canImport = false;
43324
- }
43325
- const commandNamesTheRightResource = safeId === physicalId && safeStack === stackName && safeLogicalId === logicalId;
43326
- const importableTarget = !stackName.includes("~");
43327
- if (!canImport || !commandNamesTheRightResource || !importableTarget) return `${diagnosis} ${!canImport ? `cdkd cannot adopt ${displaySafe(error.resourceType, { asciiOnly: true })} back into state (its provider implements no import)` : !importableTarget ? `this is a nested-stack child, whose stack name cdkd import cannot resolve` : `cdkd cannot render an import command that provably names this resource`}, so the way forward is to delete it. ${deleteArm}`;
43328
- const adopt = pasteableCommand("cdkd import", [{
43329
- value: stackName,
43330
- hole: "stack"
43331
- }, {
43332
- flag: "--resource",
43333
- value: `${logicalId}=${physicalId}`,
43334
- hole: "logicalId=physicalId"
43335
- }]);
43336
- return `${diagnosis} To recover, adopt it back into state instead of re-creating it, with the command below (a selective import merges into existing state and needs no --force while the resource is absent from it). CONFIRM IT IS YOURS FIRST — a name cdkd derives is predictable, so for a globally-namespaced type it can belong to another account, and the same stack deployed in another region derives the same name. ${deleteArm}${adopt.withheld.length === 0 ? "" : " The command below prints a quoted hole in place of a value cdkd will not name on a command line; replace it whole, quotes included, with your stack name or '<logicalId>=<name>', shell-quoted."}\nAdopt with: ${adopt.command}`;
44208
+ return applyDefaultNameForFallback(logicalId, resourceType, resolvedProps, underivable);
43337
44209
  }
43338
44210
 
43339
44211
  //#endregion
@@ -43510,6 +44382,7 @@ async function provisionCreate(engine, logicalId, change, stateResources, stackN
43510
44382
  const recordedProperties = propertiesToRecord(resolvedProps, result, resourceType, createDecision.provisionedBy);
43511
44383
  stateResources[logicalId] = {
43512
44384
  physicalId: result.physicalId,
44385
+ ...getCurrentStackInstanceId() !== void 0 && nameGenerationField(currentNameGeneration(logicalId) ?? 0),
43513
44386
  resourceType,
43514
44387
  properties: recordedProperties,
43515
44388
  ...acceptedCreateOnlyDropsField(recordedProperties, resourceType, createDecision.provisionedBy, "new-resource"),
@@ -43536,6 +44409,21 @@ async function provisionCreate(engine, logicalId, change, stateResources, stackN
43536
44409
  renderer.removeTask(logicalId);
43537
44410
  engine.logger.info(safeMsg`${createPrefix}${formatResourceLine("created", logicalId, resourceType)}`);
43538
44411
  }
44412
+ async function guardedReplacementCreate(engine, input, create) {
44413
+ const guard = engine.generatedNameGuard;
44414
+ if (input.keepsName === true || guard?.candidate(input.logicalId) === void 0) return create();
44415
+ await refuseUnlicensedGeneratedName(engine, input);
44416
+ guard.noteSent(input.logicalId);
44417
+ let result;
44418
+ try {
44419
+ result = await create();
44420
+ } catch (error) {
44421
+ guard.noteFailed(input.logicalId, error);
44422
+ throw error;
44423
+ }
44424
+ guard.noteReturned(input.logicalId);
44425
+ return result;
44426
+ }
43539
44427
  async function refuseUnlicensedGeneratedName(engine, input) {
43540
44428
  const { logicalId, resourceType } = input;
43541
44429
  const asked = engine.generatedNameGuard?.candidate(logicalId);
@@ -44021,7 +44909,7 @@ var DynamoDBGlobalTableProvider = class {
44021
44909
  const warn = log.warn;
44022
44910
  log.debug(`Creating DynamoDB GlobalTable ${logicalId}`);
44023
44911
  const desiredReplicaTags = refuseMalformedReplicaTags(properties, resourceType, logicalId);
44024
- const tableName = properties["TableName"] || generateResourceName(logicalId, { maxLength: 255 });
44912
+ const tableName = properties["TableName"] || generatedNameFor("AWS::DynamoDB::GlobalTable", logicalId);
44025
44913
  const keySchema = properties["KeySchema"];
44026
44914
  const attributeDefinitions = properties["AttributeDefinitions"];
44027
44915
  if (!keySchema) throw new ProvisioningError(`KeySchema is required for DynamoDB GlobalTable ${logicalId}`, resourceType, logicalId);
@@ -46465,7 +47353,7 @@ function recordedProtectionNote(evidence, flags) {
46465
47353
 
46466
47354
  //#endregion
46467
47355
  //#region src/deployment/deploy-engine/update-replace.ts
46468
- async function updateByReplacement(engine, { change, counts, currentProps, currentResource, dependencies, logicalId, oldResourceType, progress, propertyDrivenReplacement, recreateFlagged, recreateViaCcApi, recreateViaSdkProvider, renderer, resolvedProps, resourceType, stackName, stateResources, template, typeChanged, updateReplacePolicy, updateSecrets, lostWithParent }) {
47356
+ async function updateByReplacement(engine, { change, counts, currentProps, currentResource, dependencies, logicalId, oldResourceType, progress, propertyDrivenReplacement, recreateFlagged, recreateViaCcApi, recreateViaSdkProvider, renderer, resolvedProps, resourceType, stackName, stateResources, template, typeChanged, updateReplacePolicy, updateSecrets, lostWithParent, nameGenerationFields }) {
46469
47357
  const noEchoDeleteValues = await noEchoDeleteValuesFromResolved({
46470
47358
  record: currentResource,
46471
47359
  templateResource: template?.Resources?.[logicalId],
@@ -46540,6 +47428,9 @@ async function updateByReplacement(engine, { change, counts, currentProps, curre
46540
47428
  }
46541
47429
  createResult = await createFirstThenDeleteOld(engine.replacementRun, engine.replacementDeps(), {
46542
47430
  retainOld,
47431
+ stackName,
47432
+ createdVia: replaceDecision.provisionedBy,
47433
+ resolvedProps,
46543
47434
  logicalId,
46544
47435
  resourceType,
46545
47436
  oldResourceType,
@@ -46591,17 +47482,27 @@ async function updateByReplacement(engine, { change, counts, currentProps, curre
46591
47482
  if (recreateSkipReason !== void 0) throw new Error(deleteSkippedMessage(logicalId, currentResource.physicalId, recreateSkipReason, `during ${recreateFlagName}`));
46592
47483
  engine.logger.info(safeMsg` ${green("✓")} Old resource deleted`);
46593
47484
  engine.replacementRun.oldDeletedBeforeCreate.add(logicalId);
47485
+ await oldDeleteProvider.waitForNameReleased?.(logicalId, currentResource.physicalId, oldResourceType, {
47486
+ isInterrupted: () => engine.userInterrupted,
47487
+ maskSecrets: createSecretMasker(updateSecrets)
47488
+ });
46594
47489
  }
46595
47490
  engine.logger.info(safeMsg` Creating new ${logicalId}...`);
46596
- createResult = await withRetry(() => engine.withRetry(() => withCurrentResourceSecrets(updateSecrets, () => replaceProvider.create(logicalId, resourceType, replaceProps, { maskSecrets: createSecretMasker(updateSecrets) })), logicalId, void 0, void 0, replaceProvider), logicalId, {
47491
+ createResult = await guardedReplacementCreate(engine, {
47492
+ logicalId,
47493
+ resourceType,
47494
+ stackName,
47495
+ createdVia: replaceDecision.provisionedBy,
47496
+ resolvedProps
47497
+ }, () => withRetry(() => engine.withRetry(() => withCurrentResourceSecrets(updateSecrets, () => replaceProvider.create(logicalId, resourceType, replaceProps, { maskSecrets: createSecretMasker(updateSecrets) })), logicalId, void 0, void 0, replaceProvider, true), logicalId, {
46597
47498
  maxRetries: 8,
46598
47499
  initialDelayMs: 2e3,
46599
47500
  maxDelayMs: 1e4,
46600
47501
  logger: engine.maskingRetryLoggerFor(updateSecrets),
46601
- isInterrupted: () => engine.interrupted,
46602
- onInterrupted: () => new InterruptedError(engine.interruptCause ?? "user"),
47502
+ isInterrupted: () => engine.userInterrupted,
47503
+ onInterrupted: () => new InterruptedError("user"),
46603
47504
  isRetryable: isRecreateRetryableError
46604
- });
47505
+ }));
46605
47506
  if (equalIdIsSameResource && updateReplacePolicy === "Retain" && createResult.physicalId === currentResource.physicalId) throw new CdkdError(safeMsg`${logicalId} (${resourceType}) recreate returned the existing resource ` + safeMsg`(${currentResource.physicalId}) instead of creating a new one — its Create ` + "API is name-idempotent — and UpdateReplacePolicy: Retain means the old resource was never destroyed, so the new properties were not applied. Rename the resource in your CDK code (or remove the explicit physical name) so the recreate can produce a genuinely new resource.", "NAMED_REPLACEMENT_IDEMPOTENT_CREATE");
46606
47507
  } else {
46607
47508
  engine.logger.info(safeMsg` Creating new ${logicalId}...`);
@@ -46611,7 +47512,14 @@ async function updateByReplacement(engine, { change, counts, currentProps, curre
46611
47512
  engine.logger.info(safeMsg` ${logicalId} went with ${lostWithParent}, which was destroyed and re-created under the same id: re-creating it`);
46612
47513
  }
46613
47514
  try {
46614
- createResult = await engine.withRetry(() => withCurrentResourceSecrets(updateSecrets, () => replaceProvider.create(logicalId, resourceType, replaceProps, { maskSecrets: createSecretMasker(updateSecrets) })), logicalId, void 0, void 0, replaceProvider);
47515
+ createResult = await guardedReplacementCreate(engine, {
47516
+ logicalId,
47517
+ resourceType,
47518
+ stackName,
47519
+ createdVia: replaceDecision.provisionedBy,
47520
+ resolvedProps,
47521
+ keepsName: lostWithParent !== void 0
47522
+ }, () => engine.withRetry(() => withCurrentResourceSecrets(updateSecrets, () => replaceProvider.create(logicalId, resourceType, replaceProps, { maskSecrets: createSecretMasker(updateSecrets) })), logicalId, void 0, void 0, replaceProvider, lostWithParent !== void 0));
46615
47523
  } catch (createError) {
46616
47524
  const createMsg = displayAwsMessage(maskSecretsInText(describeAwsFailure(createError).detail, updateSecrets));
46617
47525
  const createSentence = createMsg.endsWith(".") ? createMsg.slice(0, -1) : createMsg;
@@ -46630,7 +47538,7 @@ async function updateByReplacement(engine, { change, counts, currentProps, curre
46630
47538
  createType: resourceType,
46631
47539
  holderType: oldResourceType,
46632
47540
  requested: replaceProps,
46633
- generated: applyDefaultNameForFallback(logicalId, resourceType, resolvedProps),
47541
+ generated: applyDefaultNameForFallback(logicalId, resourceType, resolvedProps, "omit"),
46634
47542
  recorded: currentResource.properties,
46635
47543
  observed: currentResource.observedProperties,
46636
47544
  physicalId: currentResource.physicalId,
@@ -46641,7 +47549,13 @@ async function updateByReplacement(engine, { change, counts, currentProps, curre
46641
47549
  });
46642
47550
  if (!holder.holds) {
46643
47551
  const flagClause = engine.options.replace === true ? ` --replace was NOT applied and nothing was deleted.` : updateReplacePolicy === "Retain" ? " Nothing was deleted. UpdateReplacePolicy: Retain keeps the resource being replaced in place; removing it and re-running with `cdkd deploy --replace` would refuse the same way rather than delete it." : " Nothing was deleted, and `cdkd deploy --replace` would refuse the same way rather than delete it.";
46644
- throw markNonRetryable(new CdkdError(maskSecretsInText(`${displaySafe(logicalId)} (${displaySafe(resourceType)}) requires replacement, but the create-first attempt collided: ${holder.diagnosis} — ` + (holder.known ? "so another resource holds the colliding name (an orphan of an earlier attempt, or one made outside this stack), and deleting the resource being replaced would destroy it and collide again." + flagClause + " Remove or rename the resource holding that name if it is yours, then re-run the deploy." : "so if another resource holds it (an orphan of an earlier attempt, or one made outside this stack), deleting the resource being replaced would destroy it and collide again." + flagClause + " Remove or rename whatever holds that name if it is yours — if that is the resource being replaced itself, delete it by hand — then re-run the deploy.") + ` Underlying collision: ${createMsg}`, updateSecrets), "NAMED_REPLACEMENT_COLLISION", createError instanceof Error ? createError : void 0));
47552
+ throw markNonRetryable(new CdkdError(maskSecretsInText(`${displaySafe(logicalId)} (${displaySafe(resourceType)}) requires replacement, but the create-first attempt collided: ${holder.diagnosis} — ` + (holder.known ? "so another resource holds the colliding name (an orphan of an earlier attempt, or one made outside this stack), and deleting the resource being replaced would destroy it and collide again." + flagClause + " Remove or rename the resource holding that name if it is yours, then re-run the deploy." : "so if another resource holds it (an orphan of an earlier attempt, or one made outside this stack), deleting the resource being replaced would destroy it and collide again." + flagClause + " Remove or rename whatever holds that name if it is yours — if that is the resource being replaced itself, delete it by hand — then re-run the deploy.") + generatedReplacementHolderAdvice({
47553
+ stackName,
47554
+ logicalId,
47555
+ change: nameChange,
47556
+ oldPhysicalId: currentResource.physicalId,
47557
+ mask: (value) => maskSecretsInText(value, unionOfSecretBags([updateSecrets, printingSecretsFor(engine.secretsRun, logicalId)]))
47558
+ }) + ` Underlying collision: ${createMsg}`, updateSecrets), "NAMED_REPLACEMENT_COLLISION", createError instanceof Error ? createError : void 0));
46645
47559
  }
46646
47560
  if (updateReplacePolicy === "Retain") throw new CdkdError(safeMsg`${logicalId} (${resourceType}) requires replacement, but its physical name ` + `is still held by the existing resource AND UpdateReplacePolicy: Retain ` + safeMsg`pins that resource in place. ${nameOrigin.descriptor}. ` + safeMsg`${nameOrigin.remedy} — with Retain, the old resource keeps the name, so a ` + `same-name replacement can never proceed.`, "NAMED_REPLACEMENT_COLLISION");
46647
47561
  if (engine.options.replace !== true) throw new CdkdError(safeMsg`${logicalId} (${resourceType}) requires replacement, but the create-first ` + safeMsg`attempt collided with the existing resource: ${createSentence}. ` + safeMsg`${nameOrigin.descriptor}, so the CloudFormation-style safe replacement ` + "order (create the new resource before deleting the old) cannot reuse the occupied name — CloudFormation refuses this shape with \"cannot update a stack when a custom-named resource requires replacing\". " + safeMsg`${nameOrigin.remedy}, or re-run with \`cdkd deploy --replace\` to delete ` + "the old resource FIRST and recreate it under the same name (the resource is briefly unavailable while it is recreated).", "NAMED_REPLACEMENT_COLLISION");
@@ -46681,6 +47595,7 @@ async function updateByReplacement(engine, { change, counts, currentProps, curre
46681
47595
  const recordedReplacementProperties = propertiesToRecord(resolvedProps, createResult, resourceType, replaceDecision.provisionedBy);
46682
47596
  stateResources[logicalId] = {
46683
47597
  physicalId: createResult.physicalId,
47598
+ ...nameGenerationFields,
46684
47599
  resourceType,
46685
47600
  properties: recordedReplacementProperties,
46686
47601
  ...acceptedCreateOnlyDropsField(recordedReplacementProperties, resourceType, replaceDecision.provisionedBy, "new-resource"),
@@ -46724,7 +47639,7 @@ function ccAutoRouteUpdateFailureRemedy(logicalId, resourceType, autoRoutedPrope
46724
47639
 
46725
47640
  //#endregion
46726
47641
  //#region src/deployment/deploy-engine/update-in-place.ts
46727
- async function updateInPlace(engine, { conditions, counts, currentProps, currentPropsAsWritten, currentResource, dependencies, desiredForSkipCheckAsWritten, logicalId, noEchoHeldPaths, parameterValues, progress, renderer, resolvedProps, resourceType, stackName, stateResources, template, updateReplacePolicy, updateSecrets, reattach }) {
47642
+ async function updateInPlace(engine, { changedProperties, conditions, counts, currentProps, currentPropsAsWritten, currentResource, dependencies, desiredForSkipCheckAsWritten, logicalId, noEchoHeldPaths, parameterValues, progress, renderer, resolvedProps, resourceType, stackName, stateResources, template, updateReplacePolicy, updateSecrets, reattach }) {
46728
47643
  engine.logger.debug(safeMsg`Updating ${logicalId} (${resourceType})`);
46729
47644
  const updateDecision = engine.providerRegistry.getProviderFor({
46730
47645
  resourceType,
@@ -46749,11 +47664,12 @@ async function updateInPlace(engine, { conditions, counts, currentProps, current
46749
47664
  engine.logger.info(message);
46750
47665
  }
46751
47666
  const updateProvider = updateDecision.provider;
46752
- const updateProps = updateDecision.provisionedBy === "cc-api" ? withoutGeneratedFallbackName(resourceType, resolvedProps, preparePropertiesForCcApi(engine.providerRegistry, resourceType, resolvedProps, logicalId)) : resolvedProps;
46753
- const { previous: previousWithSecretsDropped, dropped: droppedPrincipalKinds } = withUnchangedSecretPrincipalLists(resourceType, currentResource.physicalId, noEchoHeldPaths.size === 0 ? currentPropsAsWritten : {
47667
+ const updateProps = updateDecision.provisionedBy === "cc-api" ? withoutGeneratedFallbackName(resourceType, resolvedProps, preparePropertiesForCcApi(engine.providerRegistry, resourceType, resolvedProps, logicalId, "omit")) : resolvedProps;
47668
+ const previousWithNoEchoHeld = noEchoHeldPaths.size === 0 ? currentPropsAsWritten : {
46754
47669
  ...currentPropsAsWritten,
46755
47670
  ...Object.fromEntries([...noEchoHeldPaths].filter((path) => Object.prototype.hasOwnProperty.call(updateProps, path)).map((path) => [path, updateProps[path]]))
46756
- }, desiredForSkipCheckAsWritten);
47671
+ };
47672
+ const { previous: previousWithSecretsDropped, dropped: droppedPrincipalKinds } = withUnchangedSecretPrincipalLists(resourceType, currentResource.physicalId, previousWithNoEchoHeld, desiredForSkipCheckAsWritten);
46757
47673
  const reattached = reattach === true ? withRecreatedAttachmentsDropped({
46758
47674
  resourceType,
46759
47675
  templateProperties: template?.Resources?.[logicalId]?.Properties,
@@ -46763,6 +47679,9 @@ async function updateInPlace(engine, { conditions, counts, currentProps, current
46763
47679
  conditions
46764
47680
  }) : void 0;
46765
47681
  const previousForUpdate = reattached?.previous ?? previousWithSecretsDropped;
47682
+ const changedForProvider = new Set(changedProperties);
47683
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(previousWithNoEchoHeld), ...Object.keys(previousForUpdate)])) if (keyOrderFreeJson(previousForUpdate[key]) !== keyOrderFreeJson(previousWithNoEchoHeld[key])) changedForProvider.add(key);
47684
+ if (changedForProvider.size === 0) for (const key of /* @__PURE__ */ new Set([...Object.keys(updateProps), ...Object.keys(previousForUpdate)])) changedForProvider.add(key);
46766
47685
  if (reattached !== void 0) engine.logger.info(safeMsg` ${logicalId} (${resourceType}) lost its attachment to ${reattached.parents.join(", ")}, which this deploy re-created under the same name: attaching it again`);
46767
47686
  if (droppedPrincipalKinds.length > 0) {
46768
47687
  const kinds = droppedPrincipalKinds.join(" / ");
@@ -46772,6 +47691,7 @@ async function updateInPlace(engine, { conditions, counts, currentProps, current
46772
47691
  let result;
46773
47692
  let resultProvisionedBy = updateDecision.provisionedBy;
46774
47693
  let captureProvider = updateProvider;
47694
+ let replacedAtGeneration;
46775
47695
  const inlinePolicyClaimed = engine.inlinePolicyClaimedFor(resourceType, logicalId, stateResources);
46776
47696
  try {
46777
47697
  result = await engine.withRetry(() => withCurrentResourceSecrets(updateSecrets, () => updateProvider.update(logicalId, currentResource.physicalId, resourceType, removal.properties, previousForUpdate, {
@@ -46779,7 +47699,8 @@ async function updateInPlace(engine, { conditions, counts, currentProps, current
46779
47699
  expectedRegion: engine.stackRegion,
46780
47700
  recordedAttributes: currentResource.attributes,
46781
47701
  ...inlinePolicyClaimed && { inlinePolicyClaimed },
46782
- ...removal.context
47702
+ ...removal.context,
47703
+ changedProperties: changedForProvider
46783
47704
  })), logicalId, void 0, void 0, updateProvider);
46784
47705
  if (result.sentNothing === true) engine.replacementRun.updatesThatSentNothing.add(logicalId);
46785
47706
  if (result.wasReplaced === true && result.outcome === "partial") engine.replacementRun.replacedOldMaySurvive.set(logicalId, result.oldResourceSurvivor === "kept" ? "kept" : "unconfirmed");
@@ -46789,153 +47710,181 @@ async function updateInPlace(engine, { conditions, counts, currentProps, current
46789
47710
  const ccUnsupported = isUpdateUnsupportedError(updateError, logicalId);
46790
47711
  const replaceOptIn = updateError instanceof ResourceUpdateNotSupportedError && engine.options.replace === true;
46791
47712
  if (ccUnsupported || replaceOptIn) {
46792
- const retainOldOnReplace = updateReplacePolicy === "Retain";
46793
- const statefulReason = retainOldOnReplace ? null : isStatefulRecreateTargetForReplace(resourceType, currentProps, currentResource.observedProperties);
46794
- if (statefulReason && engine.options.forceStatefulRecreation !== true) {
46795
- const protection = recordedProtectionEvidence(resourceType, currentProps, currentResource.observedProperties, engine.stackRegion);
46796
- throw markNonRetryable(new CdkdError(replaceOptIn ? safeMsg`--replace would DELETE + CREATE the stateful resource ${logicalId} ` + safeMsg`(${resourceType}) — ${renderStatefulReason(statefulReason)}. ` + (protection ? safeMsg`${recordedProtectionNote(protection, "--replace --force-stateful-recreation")} ` + `Or change the resource's definition to avoid the immutable-property change.` : "Re-run with --force-stateful-recreation to confirm the data loss, or change the resource definition to avoid the immutable-property change.") : safeMsg`${logicalId} (${resourceType}) cannot be updated in place by the ` + "provisioning layer it routes through, so applying this change would DELETE + CREATE it — but it is a stateful resource: " + safeMsg`${renderStatefulReason(statefulReason)}. ` + (protection ? safeMsg`${recordedProtectionNote(protection, "--force-stateful-recreation")} ` + `Or change the resource's definition to avoid the update.` : "Re-run with --force-stateful-recreation to confirm the data loss, or change the resource definition to avoid the update."), "STATEFUL_REPLACE_BLOCKED", updateError instanceof Error ? updateError : void 0));
46797
- }
46798
- const replDecision = engine.providerRegistry.getProviderFor({
46799
- resourceType,
46800
- properties: resolvedProps,
46801
- previousProperties: currentResource.properties
46802
- });
46803
- const replProvider = replDecision.provider;
46804
- const replProps = replDecision.provisionedBy === "cc-api" ? preparePropertiesForCcApi(engine.providerRegistry, resourceType, resolvedProps, logicalId) : resolvedProps;
46805
- const fallbackNameChange = await checkedReplacementNameChange(engine.replacementDeps(), {
46806
- logicalId,
46807
- resourceType,
46808
- oldResourceType: resourceType,
46809
- stackName,
46810
- currentResource,
46811
- desiredProperties: resolvedProps,
46812
- createProvider: replProvider,
46813
- createdVia: replDecision.provisionedBy,
46814
- createProps: replProps,
46815
- secrets: updateSecrets
46816
- });
46817
- const createFirst = !retainOldOnReplace && fallbackNameChange !== void 0;
46818
- const noEchoDeleteValues = await noEchoDeleteValuesFromResolved({
46819
- record: currentResource,
46820
- templateResource: template?.Resources?.[logicalId],
46821
- resolvedProperties: resolvedProps,
46822
- noEchoParameters: noEchoPositionSources(engine.noEchoRun, stateResources, template)?.parameters ?? /* @__PURE__ */ new Set(),
46823
- conditions: engine.noEchoRun.noEchoConditions,
46824
- secrets: updateSecrets,
46825
- inputSources: template && maskedInputSources(engine.resolverContextDeps(), template, stateResources, engine.noEchoRun.noEchoConditions, stackName)
46826
- });
46827
- engine.logger.info(retainOldOnReplace ? safeMsg`UPDATE not supported for ${logicalId} (${resourceType}), replacing ` + `(CREATE only — UpdateReplacePolicy: Retain keeps the old resource)` : createFirst ? safeMsg`UPDATE not supported for ${logicalId} (${resourceType}), replacing (CREATE → DELETE — the new name differs from the old resource's)` : safeMsg`UPDATE not supported for ${logicalId} (${resourceType}), replacing (DELETE → CREATE)`);
46828
- if (retainOldOnReplace) await noteRetainedResource(resourceType, logicalId);
46829
- if (!retainOldOnReplace && !createFirst) {
46830
- const fallbackUpdateReplacePolicy = template?.Resources?.[logicalId]?.UpdateReplacePolicy ?? currentResource.updateReplacePolicy;
46831
- const fallbackFinalSnapshotId = await prepareFinalSnapshotForDelete(engine.replacementDeps(), logicalId, resourceType, currentResource, fallbackUpdateReplacePolicy);
46832
- let fallbackDeleteResult = void 0;
46833
- try {
46834
- fallbackDeleteResult = await withPrintingSecrets(updateSecrets, () => updateProvider.delete(logicalId, currentResource.physicalId, resourceType, currentProps, {
46835
- expectedRegion: engine.stackRegion,
46836
- forceDataDelete: engine.options.forceStatefulRecreation === true,
46837
- ...fallbackFinalSnapshotId !== void 0 && { finalSnapshotIdentifier: fallbackFinalSnapshotId },
46838
- ...replacementDeleteContext(engine.replacementDeps(), fallbackUpdateReplacePolicy),
46839
- recordedAttributes: currentResource.attributes,
46840
- recordedNoEchoLeaves: currentResource.noEchoLeaves,
46841
- ...noEchoDeleteValues !== void 0 && { noEchoDeleteValues },
46842
- maskSecrets: createUnionSecretMasker([updateSecrets, secretNameBagFor(engine.secretsRun, logicalId)])
46843
- }));
46844
- } catch (deleteError) {
46845
- const deleteMsg = describeAwsFailure(deleteError).detail;
46846
- if (!isWaitAbandonedError(deleteError) && !isInterruptedWaitError(deleteError) && !isMarkedNonRetryable(deleteError) && (deleteMsg.includes("does not exist") || deleteMsg.includes("not found") || deleteMsg.includes("NotFound"))) engine.logger.debug(safeMsg`Old resource ${logicalId} already gone, proceeding with CREATE`);
46847
- else throw deleteError;
47713
+ const fallbackStackInstanceId = getCurrentStackInstanceId();
47714
+ const fallbackGeneration = fallbackStackInstanceId === void 0 ? void 0 : nextNameGeneration(currentResource, logicalId, fallbackStackInstanceId);
47715
+ if (fallbackGeneration !== void 0) engine.replacementRun.attemptedNameGeneration.set(logicalId, fallbackGeneration);
47716
+ const replaceFallback = async () => {
47717
+ const retainOldOnReplace = updateReplacePolicy === "Retain";
47718
+ const statefulReason = retainOldOnReplace ? null : isStatefulRecreateTargetForReplace(resourceType, currentProps, currentResource.observedProperties);
47719
+ if (statefulReason && engine.options.forceStatefulRecreation !== true) {
47720
+ const protection = recordedProtectionEvidence(resourceType, currentProps, currentResource.observedProperties, engine.stackRegion);
47721
+ throw markNonRetryable(new CdkdError(replaceOptIn ? safeMsg`--replace would DELETE + CREATE the stateful resource ${logicalId} ` + safeMsg`(${resourceType}) — ${renderStatefulReason(statefulReason)}. ` + (protection ? safeMsg`${recordedProtectionNote(protection, "--replace --force-stateful-recreation")} ` + `Or change the resource's definition to avoid the immutable-property change.` : "Re-run with --force-stateful-recreation to confirm the data loss, or change the resource definition to avoid the immutable-property change.") : safeMsg`${logicalId} (${resourceType}) cannot be updated in place by the ` + "provisioning layer it routes through, so applying this change would DELETE + CREATE it — but it is a stateful resource: " + safeMsg`${renderStatefulReason(statefulReason)}. ` + (protection ? safeMsg`${recordedProtectionNote(protection, "--force-stateful-recreation")} ` + `Or change the resource's definition to avoid the update.` : "Re-run with --force-stateful-recreation to confirm the data loss, or change the resource definition to avoid the update."), "STATEFUL_REPLACE_BLOCKED", updateError instanceof Error ? updateError : void 0));
46848
47722
  }
46849
- reportDeleteGuards(fallbackDeleteResult, {
46850
- physicalId: currentResource.physicalId,
47723
+ const replDecision = engine.providerRegistry.getProviderFor({
46851
47724
  resourceType,
46852
- provisionedBy: updateDecision.provisionedBy
47725
+ properties: resolvedProps,
47726
+ previousProperties: currentResource.properties
46853
47727
  });
46854
- const fallbackSkipReason = deleteSkipReason(fallbackDeleteResult);
46855
- if (fallbackSkipReason !== void 0) throw new Error(deleteSkippedMessage(logicalId, currentResource.physicalId, fallbackSkipReason, "during the UPDATE-not-supported replacement"));
46856
- engine.replacementRun.oldDeletedBeforeCreate.add(logicalId);
46857
- }
46858
- let retainedSurvivorReason;
46859
- let createResult;
46860
- try {
46861
- createResult = createFirst && fallbackNameChange !== void 0 ? await createFirstThenDeleteOld(engine.replacementRun, engine.replacementDeps(), {
47728
+ const replProvider = replDecision.provider;
47729
+ const replProps = replDecision.provisionedBy === "cc-api" ? preparePropertiesForCcApi(engine.providerRegistry, resourceType, resolvedProps, logicalId) : resolvedProps;
47730
+ const fallbackNameChange = await checkedReplacementNameChange(engine.replacementDeps(), {
46862
47731
  logicalId,
46863
47732
  resourceType,
46864
47733
  oldResourceType: resourceType,
47734
+ stackName,
46865
47735
  currentResource,
47736
+ desiredProperties: resolvedProps,
46866
47737
  createProvider: replProvider,
47738
+ createdVia: replDecision.provisionedBy,
46867
47739
  createProps: replProps,
46868
- deleteProvider: updateProvider,
46869
- deleteProvisionedBy: updateDecision.provisionedBy,
46870
- deleteProperties: currentProps,
47740
+ secrets: updateSecrets
47741
+ });
47742
+ const createFirst = !retainOldOnReplace && fallbackNameChange !== void 0;
47743
+ const noEchoDeleteValues = await noEchoDeleteValuesFromResolved({
47744
+ record: currentResource,
47745
+ templateResource: template?.Resources?.[logicalId],
47746
+ resolvedProperties: resolvedProps,
47747
+ noEchoParameters: noEchoPositionSources(engine.noEchoRun, stateResources, template)?.parameters ?? /* @__PURE__ */ new Set(),
47748
+ conditions: engine.noEchoRun.noEchoConditions,
46871
47749
  secrets: updateSecrets,
46872
- change: fallbackNameChange,
46873
- equalIdIsSameResource: !equalIdNamesDifferentResources({
46874
- resourceType,
47750
+ inputSources: template && maskedInputSources(engine.resolverContextDeps(), template, stateResources, engine.noEchoRun.noEchoConditions, stackName)
47751
+ });
47752
+ engine.logger.info(retainOldOnReplace ? safeMsg`UPDATE not supported for ${logicalId} (${resourceType}), replacing ` + `(CREATE only — UpdateReplacePolicy: Retain keeps the old resource)` : createFirst ? safeMsg`UPDATE not supported for ${logicalId} (${resourceType}), replacing (CREATE → DELETE — the new name differs from the old resource's)` : safeMsg`UPDATE not supported for ${logicalId} (${resourceType}), replacing (DELETE → CREATE)`);
47753
+ if (retainOldOnReplace) await noteRetainedResource(resourceType, logicalId);
47754
+ if (!retainOldOnReplace && !createFirst) {
47755
+ const fallbackUpdateReplacePolicy = template?.Resources?.[logicalId]?.UpdateReplacePolicy ?? currentResource.updateReplacePolicy;
47756
+ const fallbackFinalSnapshotId = await prepareFinalSnapshotForDelete(engine.replacementDeps(), logicalId, resourceType, currentResource, fallbackUpdateReplacePolicy);
47757
+ let fallbackDeleteResult = void 0;
47758
+ try {
47759
+ fallbackDeleteResult = await withPrintingSecrets(updateSecrets, () => updateProvider.delete(logicalId, currentResource.physicalId, resourceType, currentProps, {
47760
+ expectedRegion: engine.stackRegion,
47761
+ forceDataDelete: engine.options.forceStatefulRecreation === true,
47762
+ ...fallbackFinalSnapshotId !== void 0 && { finalSnapshotIdentifier: fallbackFinalSnapshotId },
47763
+ ...replacementDeleteContext(engine.replacementDeps(), fallbackUpdateReplacePolicy),
47764
+ recordedAttributes: currentResource.attributes,
47765
+ recordedNoEchoLeaves: currentResource.noEchoLeaves,
47766
+ ...noEchoDeleteValues !== void 0 && { noEchoDeleteValues },
47767
+ maskSecrets: createUnionSecretMasker([updateSecrets, secretNameBagFor(engine.secretsRun, logicalId)])
47768
+ }));
47769
+ } catch (deleteError) {
47770
+ const deleteMsg = describeAwsFailure(deleteError).detail;
47771
+ if (!isWaitAbandonedError(deleteError) && !isInterruptedWaitError(deleteError) && !isMarkedNonRetryable(deleteError) && (deleteMsg.includes("does not exist") || deleteMsg.includes("not found") || deleteMsg.includes("NotFound"))) engine.logger.debug(safeMsg`Old resource ${logicalId} already gone, proceeding with CREATE`);
47772
+ else throw deleteError;
47773
+ }
47774
+ reportDeleteGuards(fallbackDeleteResult, {
46875
47775
  physicalId: currentResource.physicalId,
46876
- oldProperties: currentResource.properties,
46877
- newProperties: resolvedProps
46878
- }),
46879
- snapshotPolicy: template?.Resources?.[logicalId]?.UpdateReplacePolicy ?? currentResource.updateReplacePolicy,
46880
- deletePolicy: template?.Resources?.[logicalId]?.UpdateReplacePolicy ?? currentResource.updateReplacePolicy,
46881
- trigger: "the provisioning layer cannot update it in place",
46882
- noEchoDeleteValues
46883
- }) : await engine.withRetry(() => withCurrentResourceSecrets(updateSecrets, () => replProvider.create(logicalId, resourceType, replProps, { maskSecrets: createSecretMasker(updateSecrets) })), logicalId, void 0, void 0, replProvider);
46884
- } catch (createError) {
46885
- if (createFirst) throw createError;
46886
- if (!retainOldOnReplace) {
46887
- const shownSecrets = unionOfSecretBags([updateSecrets, printingSecretsFor(engine.secretsRun, logicalId)]);
46888
- throw new Error(safeMsg`Failed to create ${maskSecretsInText(logicalId, shownSecrets)} after the ` + safeMsg`UPDATE-not-supported replacement: the old resource (${maskSecretsInText(currentResource.physicalId, shownSecrets)}) ` + safeMsg`is now gone. Re-run the deploy to create it fresh. Cause: ${asIsMessage(maskSecretsInText(describeAwsFailure(createError).detail, shownSecrets))}`, { cause: createError instanceof Error ? createError : void 0 });
47776
+ resourceType,
47777
+ provisionedBy: updateDecision.provisionedBy
47778
+ });
47779
+ const fallbackSkipReason = deleteSkipReason(fallbackDeleteResult);
47780
+ if (fallbackSkipReason !== void 0) throw new Error(deleteSkippedMessage(logicalId, currentResource.physicalId, fallbackSkipReason, "during the UPDATE-not-supported replacement"));
47781
+ engine.replacementRun.oldDeletedBeforeCreate.add(logicalId);
47782
+ await updateProvider.waitForNameReleased?.(logicalId, currentResource.physicalId, resourceType, {
47783
+ isInterrupted: () => engine.userInterrupted,
47784
+ maskSecrets: createSecretMasker(updateSecrets)
47785
+ });
46889
47786
  }
46890
- if (!isNameCollisionErrorFrom(createError, logicalId)) throw createError;
46891
- const nameHeldElsewhere = replacementRequestsDifferentName({
46892
- oldResourceType: resourceType,
46893
- newResourceType: resourceType,
46894
- desiredProperties: resolvedProps,
46895
- recorded: currentResource.properties,
46896
- observed: currentResource.observedProperties,
46897
- physicalId: currentResource.physicalId
46898
- });
46899
- if (nameHeldElsewhere !== void 0) throw markNonRetryable(new CdkdError(safeMsg`${logicalId} (${resourceType}) requires replacement because the ` + `provisioning layer cannot update it in place, but the create collided. ` + safeMsg`${renderNameHeldElsewhere(nameHeldElsewhere)} — so removing ` + "UpdateReplacePolicy: Retain would delete this resource and still collide. Choose a name no other resource holds, or delete the resource holding it if it is yours.", "NAMED_REPLACEMENT_COLLISION", createError instanceof Error ? createError : void 0));
46900
- const nameOrigin = replacementNameOrigin(logicalId, currentResource.physicalId);
46901
- throw markNonRetryable(new CdkdError(safeMsg`${logicalId} (${resourceType}) requires replacement because the ` + "provisioning layer cannot update it in place — but its physical name is still held by the existing resource AND UpdateReplacePolicy: Retain pins that resource in place. " + safeMsg`${nameOrigin.descriptor}. ${nameOrigin.remedy} — with Retain, the old ` + "resource keeps the name, so a same-name replacement can never proceed. Removing UpdateReplacePolicy: Retain lets cdkd delete the old resource first, which destroys it and any data it holds.", "NAMED_REPLACEMENT_COLLISION", createError instanceof Error ? createError : void 0));
46902
- }
46903
- if (!retainOldOnReplace && !createFirst && createResult.physicalId === currentResource.physicalId && !equalIdNamesDifferentResources({
46904
- resourceType,
46905
- physicalId: currentResource.physicalId,
46906
- oldProperties: currentResource.properties,
46907
- newProperties: resolvedProps
46908
- })) engine.replacementRun.recreatedUnderSameId.add(logicalId);
46909
- if (retainOldOnReplace) {
46910
- if (createResult.physicalId === currentResource.physicalId && !equalIdNamesDifferentResources({
47787
+ let retainedSurvivorReason;
47788
+ let createResult;
47789
+ try {
47790
+ createResult = createFirst && fallbackNameChange !== void 0 ? await createFirstThenDeleteOld(engine.replacementRun, engine.replacementDeps(), {
47791
+ stackName,
47792
+ createdVia: replDecision.provisionedBy,
47793
+ resolvedProps,
47794
+ logicalId,
47795
+ resourceType,
47796
+ oldResourceType: resourceType,
47797
+ currentResource,
47798
+ createProvider: replProvider,
47799
+ createProps: replProps,
47800
+ deleteProvider: updateProvider,
47801
+ deleteProvisionedBy: updateDecision.provisionedBy,
47802
+ deleteProperties: currentProps,
47803
+ secrets: updateSecrets,
47804
+ change: fallbackNameChange,
47805
+ equalIdIsSameResource: !equalIdNamesDifferentResources({
47806
+ resourceType,
47807
+ physicalId: currentResource.physicalId,
47808
+ oldProperties: currentResource.properties,
47809
+ newProperties: resolvedProps
47810
+ }),
47811
+ snapshotPolicy: template?.Resources?.[logicalId]?.UpdateReplacePolicy ?? currentResource.updateReplacePolicy,
47812
+ deletePolicy: template?.Resources?.[logicalId]?.UpdateReplacePolicy ?? currentResource.updateReplacePolicy,
47813
+ trigger: "the provisioning layer cannot update it in place",
47814
+ noEchoDeleteValues
47815
+ }) : await guardedReplacementCreate(engine, {
47816
+ logicalId,
47817
+ resourceType,
47818
+ stackName,
47819
+ createdVia: replDecision.provisionedBy,
47820
+ resolvedProps
47821
+ }, () => engine.withRetry(() => withCurrentResourceSecrets(updateSecrets, () => replProvider.create(logicalId, resourceType, replProps, { maskSecrets: createSecretMasker(updateSecrets) })), logicalId, void 0, void 0, replProvider, !retainOldOnReplace));
47822
+ } catch (createError) {
47823
+ if (createFirst) throw createError;
47824
+ if (!retainOldOnReplace) {
47825
+ const shownSecrets = unionOfSecretBags([updateSecrets, printingSecretsFor(engine.secretsRun, logicalId)]);
47826
+ throw new Error(safeMsg`Failed to create ${maskSecretsInText(logicalId, shownSecrets)} after the ` + safeMsg`UPDATE-not-supported replacement: the old resource (${maskSecretsInText(currentResource.physicalId, shownSecrets)}) ` + safeMsg`is now gone. Re-run the deploy to create it fresh. Cause: ${asIsMessage(maskSecretsInText(describeAwsFailure(createError).detail, shownSecrets))}`, { cause: createError instanceof Error ? createError : void 0 });
47827
+ }
47828
+ if (!isNameCollisionErrorFrom(createError, logicalId)) throw createError;
47829
+ const nameHeldElsewhere = replacementRequestsDifferentName({
47830
+ oldResourceType: resourceType,
47831
+ newResourceType: resourceType,
47832
+ desiredProperties: resolvedProps,
47833
+ recorded: currentResource.properties,
47834
+ observed: currentResource.observedProperties,
47835
+ physicalId: currentResource.physicalId
47836
+ });
47837
+ if (nameHeldElsewhere !== void 0) throw markNonRetryable(new CdkdError(safeMsg`${logicalId} (${resourceType}) requires replacement because the ` + `provisioning layer cannot update it in place, but the create collided. ` + safeMsg`${renderNameHeldElsewhere(nameHeldElsewhere)} — so removing ` + "UpdateReplacePolicy: Retain would delete this resource and still collide. Choose a name no other resource holds, or delete the resource holding it if it is yours.", "NAMED_REPLACEMENT_COLLISION", createError instanceof Error ? createError : void 0));
47838
+ const nameOrigin = replacementNameOrigin(logicalId, currentResource.physicalId);
47839
+ throw markNonRetryable(new CdkdError(safeMsg`${logicalId} (${resourceType}) requires replacement because the ` + "provisioning layer cannot update it in place — but its physical name is still held by the existing resource AND UpdateReplacePolicy: Retain pins that resource in place. " + safeMsg`${nameOrigin.descriptor}. ${nameOrigin.remedy} — with Retain, the old ` + "resource keeps the name, so a same-name replacement can never proceed. Removing UpdateReplacePolicy: Retain lets cdkd delete the old resource first, which destroys it and any data it holds.", "NAMED_REPLACEMENT_COLLISION", createError instanceof Error ? createError : void 0));
47840
+ }
47841
+ if (!retainOldOnReplace && !createFirst && createResult.physicalId === currentResource.physicalId && !equalIdNamesDifferentResources({
46911
47842
  resourceType,
46912
47843
  physicalId: currentResource.physicalId,
46913
47844
  oldProperties: currentResource.properties,
46914
47845
  newProperties: resolvedProps
46915
- })) {
46916
- const idempotentNameOrigin = replacementNameOrigin(logicalId, currentResource.physicalId);
46917
- throw markNonRetryable(new CdkdError(safeMsg`${logicalId} (${resourceType}) requires replacement, but its Create ` + `API is name-idempotent: the create returned the existing resource ` + safeMsg`(${currentResource.physicalId}) instead of creating a new one, and ` + `UpdateReplacePolicy: Retain pins that resource in place, so the new ` + safeMsg`properties were not applied. ${idempotentNameOrigin.descriptor}. ` + safeMsg`${idempotentNameOrigin.remedy} — with Retain, the old resource keeps ` + `the name, so a same-name replacement can never proceed.`, "NAMED_REPLACEMENT_IDEMPOTENT_CREATE"));
47846
+ })) engine.replacementRun.recreatedUnderSameId.add(logicalId);
47847
+ if (retainOldOnReplace) {
47848
+ if (createResult.physicalId === currentResource.physicalId && !equalIdNamesDifferentResources({
47849
+ resourceType,
47850
+ physicalId: currentResource.physicalId,
47851
+ oldProperties: currentResource.properties,
47852
+ newProperties: resolvedProps
47853
+ })) {
47854
+ const idempotentNameOrigin = replacementNameOrigin(logicalId, currentResource.physicalId);
47855
+ throw markNonRetryable(new CdkdError(safeMsg`${logicalId} (${resourceType}) requires replacement, but its Create ` + `API is name-idempotent: the create returned the existing resource ` + safeMsg`(${currentResource.physicalId}) instead of creating a new one, and ` + `UpdateReplacePolicy: Retain pins that resource in place, so the new ` + safeMsg`properties were not applied. ${idempotentNameOrigin.descriptor}. ` + safeMsg`${idempotentNameOrigin.remedy} — with Retain, the old resource keeps ` + `the name, so a same-name replacement can never proceed.`, "NAMED_REPLACEMENT_IDEMPOTENT_CREATE"));
47856
+ }
47857
+ engine.replacementRun.retainedOldOnReplacement.add(logicalId);
47858
+ engine.logger.warn(safeMsg` ⚠ ${logicalId} has UpdateReplacePolicy: Retain — the old physical ` + safeMsg`resource (${currentResource.physicalId}) is RETAINED and is no longer ` + "tracked by cdkd: it keeps running and incurring cost, and `cdkd destroy` will not remove it. Delete it yourself once you no longer need its data.");
47859
+ retainedSurvivorReason = `UpdateReplacePolicy: Retain kept the old ${resourceType} (${currentResource.physicalId}), now untracked by cdkd`;
46918
47860
  }
46919
- engine.replacementRun.retainedOldOnReplacement.add(logicalId);
46920
- engine.logger.warn(safeMsg` ⚠ ${logicalId} has UpdateReplacePolicy: Retain — the old physical ` + safeMsg`resource (${currentResource.physicalId}) is RETAINED and is no longer ` + "tracked by cdkd: it keeps running and incurring cost, and `cdkd destroy` will not remove it. Delete it yourself once you no longer need its data.");
46921
- retainedSurvivorReason = `UpdateReplacePolicy: Retain kept the old ${resourceType} (${currentResource.physicalId}), now untracked by cdkd`;
46922
- }
46923
- const replacementResult = {
46924
- physicalId: createResult.physicalId,
46925
- wasReplaced: true,
46926
- ...createResult.attributes && { attributes: createResult.attributes },
46927
- ...createResult.noEchoAttributes === true && { noEchoAttributes: true },
46928
- ...createResult.noEchoAttributeNames && { noEchoAttributeNames: createResult.noEchoAttributeNames },
46929
- ...createResult.secretNamedAttributeNames && { secretNamedAttributeNames: createResult.secretNamedAttributeNames },
46930
- ...retainedSurvivorReason !== void 0 ? {
46931
- outcome: "partial",
46932
- reason: retainedSurvivorReason
46933
- } : { outcome: "updated" }
47861
+ const replacementResult = {
47862
+ physicalId: createResult.physicalId,
47863
+ wasReplaced: true,
47864
+ ...createResult.attributes && { attributes: createResult.attributes },
47865
+ ...createResult.noEchoAttributes === true && { noEchoAttributes: true },
47866
+ ...createResult.noEchoAttributeNames && { noEchoAttributeNames: createResult.noEchoAttributeNames },
47867
+ ...createResult.secretNamedAttributeNames && { secretNamedAttributeNames: createResult.secretNamedAttributeNames },
47868
+ ...retainedSurvivorReason !== void 0 ? {
47869
+ outcome: "partial",
47870
+ reason: retainedSurvivorReason
47871
+ } : { outcome: "updated" }
47872
+ };
47873
+ if (createResult.effectiveProperties) replacementResult.effectiveProperties = createResult.effectiveProperties;
47874
+ return {
47875
+ result: replacementResult,
47876
+ provisionedBy: replDecision.provisionedBy,
47877
+ captureProvider: replProvider
47878
+ };
46934
47879
  };
46935
- if (createResult.effectiveProperties) replacementResult.effectiveProperties = createResult.effectiveProperties;
46936
- result = replacementResult;
46937
- resultProvisionedBy = replDecision.provisionedBy;
46938
- captureProvider = replProvider;
47880
+ const fallbackOutcome = await (fallbackGeneration === void 0 ? replaceFallback() : withNameGeneration({
47881
+ logicalId,
47882
+ generation: fallbackGeneration
47883
+ }, replaceFallback));
47884
+ result = fallbackOutcome.result;
47885
+ resultProvisionedBy = fallbackOutcome.provisionedBy;
47886
+ captureProvider = fallbackOutcome.captureProvider;
47887
+ replacedAtGeneration = fallbackGeneration;
46939
47888
  } else {
46940
47889
  if (currentResource.provisionedBy !== "cc-api" && updateDecision.ccRouteReason !== void 0 && updateError instanceof Error && !isInterruptedWaitError(updateError) && !isWaitAbandonedError(updateError)) updateError.message += ccAutoRouteUpdateFailureRemedy(logicalId, resourceType, updateDecision.ccRouteReason.properties, engine.options.parentStackInfo !== void 0);
46941
47890
  throw updateError;
@@ -46966,6 +47915,7 @@ async function updateInPlace(engine, { conditions, counts, currentProps, current
46966
47915
  if (result.wasReplaced) engine.noEchoRun.noEchoExactEchoes.delete(logicalId);
46967
47916
  stateResources[logicalId] = {
46968
47917
  physicalId: result.physicalId,
47918
+ ...replacedAtGeneration === void 0 ? nameGenerationCarriedFrom(currentResource) : nameGenerationField(replacedAtGeneration),
46969
47919
  resourceType,
46970
47920
  properties: recordedProperties,
46971
47921
  ...acceptedCreateOnlyDropsField(recordedProperties, resourceType, resultProvisionedBy, dischargedByReplacement ? "new-resource" : "in-place", currentResource),
@@ -47010,6 +47960,10 @@ async function updateInPlace(engine, { conditions, counts, currentProps, current
47010
47960
  engine.logger.info(safeMsg`${updatePrefix}${formatResourceLine("updated", logicalId, resourceType)}`);
47011
47961
  }
47012
47962
 
47963
+ //#endregion
47964
+ //#region src/provisioning/changed-properties-consumers.ts
47965
+ const CHANGED_PROPERTIES_CONSUMER_TYPES = /* @__PURE__ */ new Set(["AWS::ServiceDiscovery::Service"]);
47966
+
47013
47967
  //#endregion
47014
47968
  //#region src/deployment/deploy-engine/read-provenance-moved.ts
47015
47969
  function occurrences(value, token) {
@@ -47380,6 +48334,7 @@ async function provisionUpdate(engine, logicalId, change, stateResources, stackN
47380
48334
  for (const path of provenanceMoved.paths) rewrittenPaths.add(path);
47381
48335
  if (provenanceMoved.paths.size > 0) engine.logger.info(safeMsg`${logicalId} is sent: ${[...provenanceMoved.paths].sort().join(", ")} read a secret-derived value at a newer version than ${logicalId} was deployed with (its upstream changed since then)`);
47382
48336
  const recordMatchesDesired = movedMasked.size === 0 && rewrittenPaths.size === 0 && keyOrderFreeJson(desiredForSkipCheckAsWritten) === keyOrderFreeJson(currentPropsAsWritten);
48337
+ const movedAsWritten = (key) => movedMasked.has(key) || rewrittenPaths.has(key) || keyOrderFreeJson(desiredForSkipCheckAsWritten[key]) !== keyOrderFreeJson(currentPropsAsWritten[key]);
47383
48338
  const applyAttributeOnlyUpdate = (attributeChanges) => {
47384
48339
  const attrSummary = attributeChanges.map((a) => `${a.attribute}: ${a.oldValue ?? "(unset)"} → ${a.newValue ?? "(unset)"}`).join(", ");
47385
48340
  engine.logger.info(safeMsg` ↻ ${logicalId} (${resourceType}) attribute update: ${attrSummary}`);
@@ -47484,7 +48439,7 @@ async function provisionUpdate(engine, logicalId, change, stateResources, stackN
47484
48439
  lowered.push(pc);
47485
48440
  continue;
47486
48441
  }
47487
- const moved = movedMasked.has(pc.path) || rewrittenPaths.has(pc.path) || keyOrderFreeJson(desiredForSkipCheckAsWritten[pc.path]) !== keyOrderFreeJson(currentPropsAsWritten[pc.path]);
48442
+ const moved = movedAsWritten(pc.path);
47488
48443
  const conditionalVerdict = moved && !typeChanged && isReplacementCeiling(pc) ? engine.diffCalculator.conditionalReplacementVerdict?.(resourceType, pc.path, currentPropsAsWritten[pc.path], desiredForSkipCheckAsWritten[pc.path]) : void 0;
47489
48444
  if (moved && conditionalVerdict !== false) {
47490
48445
  warnWitnessReplacement(pc.path);
@@ -47530,7 +48485,8 @@ async function provisionUpdate(engine, logicalId, change, stateResources, stackN
47530
48485
  const parameterHeldKeys = /* @__PURE__ */ new Set();
47531
48486
  const parameterUnreadablePaths = [];
47532
48487
  if (pendingParameterLeaves.size > 0 && !typeChanged) {
47533
- const read = parameterCreateOnlyPaths.size > 0 || recordMatchesDesired ? await readOnce() : void 0;
48488
+ const consumerReadsBack = CHANGED_PROPERTIES_CONSUMER_TYPES.has(resourceType) && currentResource.provisionedBy !== "cc-api" && [...pendingParameterLeaves.keys()].some((key) => !parameterCreateOnlyPaths.has(key) && otherFreshAt(key).length === 0);
48489
+ const read = parameterCreateOnlyPaths.size > 0 || recordMatchesDesired || consumerReadsBack ? await readOnce() : void 0;
47534
48490
  const verdictAt = (key) => {
47535
48491
  if (read === void 0) return "not-readable";
47536
48492
  if ("failure" in read) return read.failure;
@@ -47538,14 +48494,13 @@ async function provisionUpdate(engine, logicalId, change, stateResources, stackN
47538
48494
  return liveHoldsFreshLeaves(read.live[key], pendingParameterLeaves.get(key) ?? []) ? "held" : "differs";
47539
48495
  };
47540
48496
  const lowered = [];
47541
- const movedAt = (path) => movedMasked.has(path) || rewrittenPaths.has(path) || keyOrderFreeJson(desiredForSkipCheckAsWritten[path]) !== keyOrderFreeJson(currentPropsAsWritten[path]);
47542
- replacedAnyway = recreateDirectionFor(engine.options, stackName, logicalId) !== void 0 || lostWithParent !== void 0 || (change.propertyChanges ?? []).some((other) => other.requiresReplacement && (!parameterCreateOnlyPaths.has(other.path) || movedAt(other.path)));
48497
+ replacedAnyway = recreateDirectionFor(engine.options, stackName, logicalId) !== void 0 || lostWithParent !== void 0 || (change.propertyChanges ?? []).some((other) => other.requiresReplacement && (!parameterCreateOnlyPaths.has(other.path) || movedAsWritten(other.path)));
47543
48498
  for (const pc of change.propertyChanges ?? []) {
47544
48499
  if (!parameterCreateOnlyPaths.has(pc.path)) {
47545
48500
  lowered.push(pc);
47546
48501
  continue;
47547
48502
  }
47548
- if (movedAt(pc.path)) {
48503
+ if (movedAsWritten(pc.path)) {
47549
48504
  if (pc.requiresReplacement) warnWitnessReplacement(pc.path);
47550
48505
  lowered.push(pc);
47551
48506
  continue;
@@ -47656,30 +48611,39 @@ async function provisionUpdate(engine, logicalId, change, stateResources, stackN
47656
48611
  engine.replacementRun.restoredLostChildren.add(logicalId);
47657
48612
  return outcome;
47658
48613
  };
47659
- if (needsReplacement) return markRestored(await updateByReplacement(engine, {
47660
- change,
47661
- counts,
47662
- currentProps,
47663
- currentResource,
47664
- dependencies,
47665
- logicalId,
47666
- oldResourceType,
47667
- progress,
47668
- propertyDrivenReplacement,
47669
- recreateFlagged,
47670
- recreateViaCcApi,
47671
- recreateViaSdkProvider,
47672
- renderer,
47673
- resolvedProps,
47674
- resourceType,
47675
- stackName,
47676
- stateResources,
47677
- template,
47678
- typeChanged,
47679
- updateReplacePolicy,
47680
- updateSecrets,
47681
- lostWithParent
47682
- }));
48614
+ if (needsReplacement) {
48615
+ const { generation: nextGeneration, fields: nameGenerationFields } = replacementNameGeneration(currentResource, logicalId, getCurrentStackInstanceId(), lostWithParent !== void 0);
48616
+ if (nextGeneration !== void 0) engine.replacementRun.attemptedNameGeneration.set(logicalId, nextGeneration);
48617
+ const replace = () => updateByReplacement(engine, {
48618
+ change,
48619
+ counts,
48620
+ currentProps,
48621
+ currentResource,
48622
+ dependencies,
48623
+ logicalId,
48624
+ oldResourceType,
48625
+ progress,
48626
+ propertyDrivenReplacement,
48627
+ recreateFlagged,
48628
+ recreateViaCcApi,
48629
+ recreateViaSdkProvider,
48630
+ renderer,
48631
+ resolvedProps,
48632
+ resourceType,
48633
+ stackName,
48634
+ stateResources,
48635
+ template,
48636
+ typeChanged,
48637
+ updateReplacePolicy,
48638
+ updateSecrets,
48639
+ lostWithParent,
48640
+ nameGenerationFields
48641
+ });
48642
+ return markRestored(await (nextGeneration === void 0 ? replace() : withNameGeneration({
48643
+ logicalId,
48644
+ generation: nextGeneration
48645
+ }, replace)));
48646
+ }
47683
48647
  const serviceTokenRefusal = serviceTokenUpdateRefusal({
47684
48648
  logicalId,
47685
48649
  resourceType,
@@ -47689,6 +48653,11 @@ async function provisionUpdate(engine, logicalId, change, stateResources, stackN
47689
48653
  diffSawTokenChange: diffMovedServiceToken(change)
47690
48654
  });
47691
48655
  if (serviceTokenRefusal !== void 0) throw markRefusedBeforeApplying(markNonRetryable(new CdkdError(renderServiceTokenRefusal([serviceTokenRefusal], stackName, createSecretMasker(updateSecrets)), SERVICE_TOKEN_CHANGE_REFUSED)));
48656
+ const changedProperties = new Set([.../* @__PURE__ */ new Set([
48657
+ ...Object.keys(desiredForSkipCheckAsWritten),
48658
+ ...Object.keys(currentPropsAsWritten),
48659
+ ...Object.keys(resolvedProps)
48660
+ ])].filter((key) => movedAsWritten(key) || !confirmedPaths.has(key) && (pendingParameterLeaves.has(key) || otherFreshAt(key).length > 0)));
47692
48661
  let previousForProvider = currentPropsAsWritten;
47693
48662
  for (const coordinate of witness?.confirmed ?? []) {
47694
48663
  const value = valueAtCoordinate(resolvedProps, coordinate);
@@ -47699,6 +48668,7 @@ async function provisionUpdate(engine, logicalId, change, stateResources, stackN
47699
48668
  counts,
47700
48669
  currentProps,
47701
48670
  currentPropsAsWritten: previousForProvider,
48671
+ changedProperties,
47702
48672
  currentResource,
47703
48673
  dependencies,
47704
48674
  desiredForSkipCheckAsWritten,
@@ -47963,7 +48933,7 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
47963
48933
  saveChain = saveChain.then(async () => {
47964
48934
  try {
47965
48935
  const partialState = {
47966
- version: 12,
48936
+ version: 13,
47967
48937
  region: engine.stackRegion,
47968
48938
  stackName: currentState.stackName,
47969
48939
  resources: newResources,
@@ -47971,6 +48941,7 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
47971
48941
  ...exportNamesCarriedFrom(currentState),
47972
48942
  ...skippedOutputsCarriedFrom(currentState),
47973
48943
  ...orphansCarriedFrom(currentState),
48944
+ ...stackInstanceIdCarriedFrom(currentState),
47974
48945
  ...crossStackReadsForPartialSave(currentState, engine.recordedImports, engine.recordedOutputReads, engine.crossStackReadKeyNormalizer()),
47975
48946
  lastModified: Date.now()
47976
48947
  };
@@ -47993,6 +48964,12 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
47993
48964
  const inFlightWrites = /* @__PURE__ */ new Map();
47994
48965
  engine.stackRecordsView = deployStackRecordsView(newResources, currentState.resources, deleteChanges, (logicalId) => startedUpdates.has(logicalId) && !settledUpdates.has(logicalId), () => [...inFlightWrites.values()].map(({ write }) => write));
47995
48966
  engine.replacementRun.deferredReplacedDeletes = /* @__PURE__ */ new Map();
48967
+ engine.replacementRun.supersededHost = currentState;
48968
+ const carriedSuperseded = supersededResourcesOf(currentState).filter((entry) => {
48969
+ if (!isSupersededHeldBySameType(entry, currentState.resources)) return true;
48970
+ removeSuperseded(currentState, entry);
48971
+ return false;
48972
+ });
47996
48973
  try {
47997
48974
  const createUpdateIds = [];
47998
48975
  for (const [id, change] of changes.entries()) {
@@ -48086,6 +49063,7 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48086
49063
  engine.interrupted = true;
48087
49064
  engine.interruptCause ??= "sibling-failure";
48088
49065
  const refused = isRefusedBeforeApplying(provisionError, logicalId);
49066
+ const attemptedGeneration = change.changeType === "UPDATE" && !refused && !isReplacementNameRefusal(provisionError, logicalId) ? engine.replacementRun.attemptedNameGeneration.get(logicalId) : void 0;
48089
49067
  const statePhysicalId = newResources[logicalId]?.physicalId ?? previousState?.physicalId;
48090
49068
  if (refused && change.changeType === "CREATE" && statePhysicalId === void 0) throw provisionError;
48091
49069
  const createdId = change.changeType === "CREATE" && statePhysicalId === void 0 ? createdBeforeFailure(provisionError, logicalId, change.resourceType) : void 0;
@@ -48117,10 +49095,14 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48117
49095
  deletionPolicy: journaledOrphanPolicy(extractTemplateAttributes(template, logicalId).deletionPolicy)
48118
49096
  },
48119
49097
  ...change.changeType === "UPDATE" && engine.replacementRun.oldDeletedBeforeCreate.has(logicalId) && { oldDeletedBeforeCreate: true },
49098
+ ...attemptedGeneration !== void 0 && { nameGeneration: attemptedGeneration },
49099
+ ...change.changeType === "CREATE" && getCurrentStackInstanceId() !== void 0 && { nameGeneration: currentNameGeneration(logicalId) ?? 0 },
48120
49100
  ...orphanedBy !== void 0 && { replacementOrphaned: engine.replacementRun.oldDeletedBeforeCreate.has(logicalId) ? "delete-first" : "create-first" },
48121
49101
  ...!refused && { attemptedProperties: engine.attemptedResolvedProps.get(logicalId) }
48122
49102
  };
48123
49103
  failedOperations.push(failedOp);
49104
+ const heldNow = newResources[logicalId];
49105
+ if (attemptedGeneration !== void 0 && heldNow !== void 0) newResources[logicalId] = withNameGenerationReached(heldNow, logicalId, attemptedGeneration);
48124
49106
  let replacementOrphanOp;
48125
49107
  if (orphanedBy !== void 0) {
48126
49108
  replacementOrphanOp = {
@@ -48167,7 +49149,8 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48167
49149
  },
48168
49150
  ...change.changeType === "UPDATE" && engine.replacementRun.replacedOldMaySurvive.has(logicalId) && { oldResourceMaySurvive: engine.replacementRun.replacedOldMaySurvive.get(logicalId) },
48169
49151
  ...change.changeType === "UPDATE" && engine.replacementRun.updateWasReplaced.has(logicalId) && { wasReplaced: engine.replacementRun.updateWasReplaced.get(logicalId) },
48170
- ...change.changeType === "UPDATE" && previousState !== void 0 && { previousResourceType: previousState.resourceType }
49152
+ ...change.changeType === "UPDATE" && previousState !== void 0 && { previousResourceType: previousState.resourceType },
49153
+ ...change.changeType === "UPDATE" && engine.replacementRun.attemptedNameGeneration.has(logicalId) && newResources[logicalId]?.nameGeneration !== void 0 && { nameGeneration: newResources[logicalId].nameGeneration }
48171
49154
  });
48172
49155
  saveStateAfterResource(logicalId);
48173
49156
  if (engine.replacementRun.recreatedUnderSameId.has(logicalId)) enqueueLostChildren(createUpdateExecutor);
@@ -48218,6 +49201,10 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48218
49201
  provisionedBy: previousState?.provisionedBy,
48219
49202
  previousState
48220
49203
  });
49204
+ if (previousState !== void 0 && newResources[logicalId] === void 0) Object.assign(currentState, retiredNameGenerationsAfter(currentState, [{
49205
+ logicalId,
49206
+ record: previousState
49207
+ }]));
48221
49208
  saveStateAfterResource(logicalId);
48222
49209
  }, () => engine.interrupted);
48223
49210
  } finally {
@@ -48226,15 +49213,19 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48226
49213
  if (engine.interrupted && hasPending(deleteExecutor)) throw new InterruptedError(engine.interruptCause ?? "user");
48227
49214
  }
48228
49215
  if (engine.interrupted) warnUnretriedReplacedDeletes(engine.replacementRun, engine.replacementDeps(), "interrupted");
48229
- else try {
48230
- await retryDeferredReplacedDeletes(engine.replacementRun, engine.replacementDeps(), stackName, actualCounts);
48231
- } finally {
48232
- for (const op of completedOperations) {
48233
- if (op.changeType !== "UPDATE") continue;
48234
- const verdict = engine.replacementRun.replacedOldMaySurvive.get(op.logicalId);
48235
- if (verdict !== void 0) op.oldResourceMaySurvive = verdict;
48236
- else delete op.oldResourceMaySurvive;
49216
+ else {
49217
+ try {
49218
+ await retryDeferredReplacedDeletes(engine.replacementRun, engine.replacementDeps(), stackName, actualCounts);
49219
+ } finally {
49220
+ for (const op of completedOperations) {
49221
+ if (op.changeType !== "UPDATE") continue;
49222
+ const verdict = engine.replacementRun.replacedOldMaySurvive.get(op.logicalId);
49223
+ if (verdict !== void 0) op.oldResourceMaySurvive = verdict;
49224
+ else delete op.oldResourceMaySurvive;
49225
+ }
48237
49226
  }
49227
+ const releasedSomething = deleteChanges.size > 0 || completedOperations.some((op) => op.changeType === "UPDATE" && op.previousState !== void 0 && op.physicalId !== void 0 && op.physicalId !== op.previousState.physicalId);
49228
+ await retryCarriedSupersededResources(engine, currentState, carriedSuperseded, newResources, { waitInUse: releasedSomething });
48238
49229
  }
48239
49230
  } catch (error) {
48240
49231
  warnUnretriedReplacedDeletes(engine.replacementRun, engine.replacementDeps(), engine.interruptCause === "user" ? "interrupted" : "failed");
@@ -48293,7 +49284,7 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48293
49284
  }
48294
49285
  try {
48295
49286
  const preRollbackState = {
48296
- version: 12,
49287
+ version: 13,
48297
49288
  region: engine.stackRegion,
48298
49289
  stackName: currentState.stackName,
48299
49290
  resources: newResources,
@@ -48301,6 +49292,7 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48301
49292
  ...exportNamesCarriedFrom(currentState),
48302
49293
  ...skippedOutputsCarriedFrom(currentState),
48303
49294
  ...orphansCarriedFrom(currentState),
49295
+ ...stackInstanceIdCarriedFrom(currentState),
48304
49296
  ...crossStackReadsForPartialSave(currentState, engine.recordedImports, engine.recordedOutputReads, engine.crossStackReadKeyNormalizer()),
48305
49297
  lastModified: Date.now()
48306
49298
  };
@@ -48318,6 +49310,8 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48318
49310
  let autoRollbackClean = false;
48319
49311
  let autoRollbackJournaled = false;
48320
49312
  let rollbackOrphans = [];
49313
+ let rollbackCreates = [];
49314
+ let rollbackSuperseded = [];
48321
49315
  let rollbackSettledNested = /* @__PURE__ */ new Map();
48322
49316
  let rollbackRemainingFailedOps = failedOperations;
48323
49317
  if (error instanceof InterruptedError || isInterruptedWaitError(error) || engine.interruptCause === "user") {
@@ -48332,7 +49326,14 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48332
49326
  } else {
48333
49327
  autoRollbackJournaled = await writeRollbackJournalSegment(engine.rollbackDeps(), stackName, completedOperations, failedOperations, "auto-rollback-started", initialDeploy);
48334
49328
  const rollbackResult = await performRollback(engine.rollbackDeps(), completedOperations, newResources, stackName, currentState, failedOperations, currentState.orphans ?? []);
48335
- autoRollbackClean = autoRollbackJournaled && rollbackResult.failures === 0 && rollbackResult.skipped === 0;
49329
+ autoRollbackClean = autoRollbackJournaled && rollbackResult.failures === 0 && rollbackResult.skipped === 0 && !rollbackResult.interrupted;
49330
+ if (rollbackResult.interrupted && rollbackResult.failures === 0 && rollbackResult.skipped === 0) {
49331
+ const remedy = autoRollbackJournaled ? recoveryHint(engine.rollbackDeps(), "The rollback journal keeps them; finish the rollback with: " + pasteableCommand("cdkd rollback", [{
49332
+ value: stackName,
49333
+ hole: "stack"
49334
+ }]).command) : "";
49335
+ engine.logger.warn(safeMsg`The automatic rollback was interrupted before it reverted every operation. ${remedy}`);
49336
+ }
48336
49337
  if (rollbackResult.failures === 0 && rollbackResult.skipped > 0) {
48337
49338
  engine.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.` : ""));
48338
49339
  if (autoRollbackJournaled && engine.options.parentStackInfo === void 0 && rollbackResult.remainingFailedOps.some((op) => op.changeType !== "DELETE")) engine.logger.warn(safeMsg`The record of the operation that failed is kept too. Revert it with: ${pasteableCommand("cdkd rollback", [{
@@ -48341,12 +49342,14 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48341
49342
  }, { literal: "--revert-failed" }]).command}`);
48342
49343
  }
48343
49344
  rollbackOrphans = rollbackResult.orphaned;
49345
+ rollbackCreates = rollbackResult.rolledBackCreates;
49346
+ rollbackSuperseded = rollbackResult.superseded;
48344
49347
  rollbackSettledNested = rollbackResult.settledNested;
48345
49348
  rollbackRemainingFailedOps = rollbackResult.remainingFailedOps;
48346
49349
  }
48347
49350
  try {
48348
49351
  const postRollbackState = {
48349
- version: 12,
49352
+ version: 13,
48350
49353
  region: engine.stackRegion,
48351
49354
  stackName: currentState.stackName,
48352
49355
  resources: newResources,
@@ -48354,6 +49357,9 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48354
49357
  ...exportNamesCarriedFrom(currentState),
48355
49358
  ...skippedOutputsCarriedFrom(currentState),
48356
49359
  ...orphansAfterRollback(currentState, rollbackOrphans),
49360
+ ...stackInstanceIdCarriedFrom(currentState),
49361
+ ...supersededAfterRollback(currentState, { supersededResources: rollbackSuperseded }),
49362
+ ...retiredAfterRollback(currentState, rollbackOrphans, rollbackCreates),
48357
49363
  ...crossStackReadsForPartialSave(currentState, engine.recordedImports, engine.recordedOutputReads, engine.crossStackReadKeyNormalizer()),
48358
49364
  lastModified: Date.now()
48359
49365
  };
@@ -48365,7 +49371,7 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48365
49371
  try {
48366
49372
  const freshEtag = (await engine.stateBackend.getState(stackName, engine.stackRegion))?.etag;
48367
49373
  const postRollbackState = {
48368
- version: 12,
49374
+ version: 13,
48369
49375
  region: engine.stackRegion,
48370
49376
  stackName: currentState.stackName,
48371
49377
  resources: newResources,
@@ -48373,6 +49379,9 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48373
49379
  ...exportNamesCarriedFrom(currentState),
48374
49380
  ...skippedOutputsCarriedFrom(currentState),
48375
49381
  ...orphansAfterRollback(currentState, rollbackOrphans),
49382
+ ...stackInstanceIdCarriedFrom(currentState),
49383
+ ...supersededAfterRollback(currentState, { supersededResources: rollbackSuperseded }),
49384
+ ...retiredAfterRollback(currentState, rollbackOrphans, rollbackCreates),
48376
49385
  ...crossStackReadsForPartialSave(currentState, engine.recordedImports, engine.recordedOutputReads, engine.crossStackReadKeyNormalizer()),
48377
49386
  lastModified: Date.now()
48378
49387
  };
@@ -48381,6 +49390,7 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48381
49390
  if (autoRollbackClean) await settleNestedChildrenAfterCleanRollback(engine.rollbackDeps(), stackName, rollbackSettledNested, await settleJournalAfterCleanRollback(engine.rollbackDeps(), stackName, rollbackRemainingFailedOps, initialDeploy));
48382
49391
  } catch (retryError) {
48383
49392
  engine.logger.warn(safeMsg`Failed to save state after rollback: ${asIsMessage(describeAwsFailure(retryError).detail)}`);
49393
+ for (const entry of rollbackSuperseded) withPrintingSecrets(unionOfSecretBags([secretNameBagFor(engine.secretsRun, entry.logicalId), orphanRecordsPrintingBag([entry])]), () => engine.logger.warn(safeMsg` ${entry.logicalId}'s new ${entry.state.resourceType} ` + safeMsg`(${entry.state.physicalId}) the rollback left to delete is NOT recorded: ` + "delete it manually."));
48384
49394
  }
48385
49395
  }
48386
49396
  throw error;
@@ -48398,11 +49408,12 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48398
49408
  }
48399
49409
  return {
48400
49410
  state: {
48401
- version: 12,
49411
+ version: 13,
48402
49412
  region: engine.stackRegion,
48403
49413
  stackName: currentState.stackName,
48404
49414
  resources: newResources,
48405
49415
  ...orphansCarriedFrom(currentState),
49416
+ ...stackInstanceIdCarriedFrom(currentState),
48406
49417
  outputs,
48407
49418
  exportNames: [...engine.outputsRun.resolvedExportNames],
48408
49419
  ...engine.outputsRun.skippedOutputs && { skippedOutputs: { ...engine.outputsRun.skippedOutputs } },
@@ -48416,7 +49427,7 @@ async function executeDeployment(engine, template, currentState, changes, dag, e
48416
49427
  }
48417
49428
  async function persistStateAfterOutputFailure(engine, stackName, currentState, newResources, currentEtag, pendingMigration) {
48418
49429
  const buildState = () => ({
48419
- version: 12,
49430
+ version: 13,
48420
49431
  region: engine.stackRegion,
48421
49432
  stackName: currentState.stackName,
48422
49433
  resources: newResources,
@@ -48424,6 +49435,7 @@ async function persistStateAfterOutputFailure(engine, stackName, currentState, n
48424
49435
  ...exportNamesCarriedFrom(currentState),
48425
49436
  ...skippedOutputsCarriedFrom(currentState),
48426
49437
  ...orphansCarriedFrom(currentState),
49438
+ ...stackInstanceIdCarriedFrom(currentState),
48427
49439
  ...crossStackReadsForPartialSave(currentState, engine.recordedImports, engine.recordedOutputReads, engine.crossStackReadKeyNormalizer()),
48428
49440
  lastModified: Date.now()
48429
49441
  });
@@ -48459,12 +49471,14 @@ async function doDeployWithPrefetch(engine, stackName, template, createOnlyPrefe
48459
49471
  const renderer = getLiveRenderer();
48460
49472
  engine.interrupted = false;
48461
49473
  engine.interruptCause = null;
49474
+ engine.userInterrupted = false;
48462
49475
  const sigintHandler = () => {
48463
49476
  renderer.printAbove(() => {
48464
49477
  process.stderr.write("\nInterrupted — saving partial state after current operations complete...\n");
48465
49478
  });
48466
49479
  engine.interrupted = true;
48467
49480
  engine.interruptCause ??= "user";
49481
+ engine.userInterrupted = true;
48468
49482
  };
48469
49483
  process.on("SIGINT", sigintHandler);
48470
49484
  try {
@@ -48482,7 +49496,7 @@ async function doDeployWithPrefetch(engine, stackName, template, createOnlyPrefe
48482
49496
  const currentStateData = await engine.stateBackend.getState(stackName, engine.stackRegion);
48483
49497
  noteDeployStateRecord(currentStateData?.state !== void 0);
48484
49498
  const currentState = currentStateData?.state ?? {
48485
- version: 12,
49499
+ version: 13,
48486
49500
  region: engine.stackRegion,
48487
49501
  stackName,
48488
49502
  resources: {},
@@ -48491,6 +49505,19 @@ async function doDeployWithPrefetch(engine, stackName, template, createOnlyPrefe
48491
49505
  lastModified: Date.now()
48492
49506
  };
48493
49507
  const currentEtag = currentStateData?.etag;
49508
+ stackInstanceIdOf(currentState, displayStackName(stackName));
49509
+ const loadedNameGenerations = nameGenerationsOf(currentState);
49510
+ const nameScope = () => {
49511
+ const stackInstanceId = stackInstanceIdOf(currentState, displayStackName(stackName));
49512
+ return {
49513
+ stackName,
49514
+ stackInstanceId,
49515
+ nameGenerations: stackInstanceId === void 0 ? void 0 : loadedNameGenerations
49516
+ };
49517
+ };
49518
+ const idArrivesLater = (currentStateData === null || currentStateData === void 0) && currentState.stackInstanceId === void 0;
49519
+ const retainedAtFirstDeploy = idArrivesLater && !engine.options.dryRun ? Promise.resolve().then(() => engine.stateBackend.loadRetainedRecord(stackName, engine.stackRegion)) : void 0;
49520
+ const retainedForWarning = retainedAtFirstDeploy?.catch(() => null);
48494
49521
  engine.rebaseline(currentState.resources ?? {});
48495
49522
  engine.runProducerRegionEvidence = () => producerRegionEvidence(engine.rollbackDeps(), currentState);
48496
49523
  refuseMalformedOutputs(currentState, stackName, engine.stackRegion, engine.options.refusalRecovery);
@@ -48534,6 +49561,7 @@ async function doDeployWithPrefetch(engine, stackName, template, createOnlyPrefe
48534
49561
  command: "deploy",
48535
49562
  logger: engine.logger,
48536
49563
  stateResources: currentState.resources,
49564
+ stackInstanceId: currentStateData?.state === void 0 ? () => engine.stateBackend.readStackInstanceSibling(stackName, engine.stackRegion) : nameScope().stackInstanceId,
48537
49565
  resume: (op, identifier) => {
48538
49566
  const attempted = op.attemptedProperties;
48539
49567
  if (template.Resources?.[op.logicalId]?.Type !== op.resourceType || attempted === void 0 || carriesSecretMask(attempted) || op.pendingCloudControlRequest === void 0 || Object.values(currentState.resources).some((r) => r.physicalId === identifier && r.resourceType === op.resourceType)) return false;
@@ -48695,6 +49723,11 @@ async function doDeployWithPrefetch(engine, stackName, template, createOnlyPrefe
48695
49723
  firstDeployStateGate = void 0;
48696
49724
  await gate;
48697
49725
  }
49726
+ const resolveStackInstanceId = async () => {
49727
+ if (currentStateData !== null && currentStateData !== void 0) return;
49728
+ if (engine.options.dryRun || currentState.stackInstanceId !== void 0) return;
49729
+ currentState.stackInstanceId = await engine.stateBackend.stackInstanceIdForFirstWrite(stackName, engine.stackRegion, { writeAhead: true });
49730
+ };
48698
49731
  const startProvenanceCapture = () => {
48699
49732
  if (engine.options.dryRun || isKnownTokenFreeState(currentStateData?.state)) return;
48700
49733
  engine.provenanceCapture.kickOffProvenanceCapture({
@@ -48709,6 +49742,7 @@ async function doDeployWithPrefetch(engine, stackName, template, createOnlyPrefe
48709
49742
  if (!engine.diffCalculator.hasChanges(changes)) {
48710
49743
  engine.logger.info("No changes detected. Stack is up to date.");
48711
49744
  startProvenanceCapture();
49745
+ const supersededChanged = engine.options.dryRun ? false : await retryCarriedSupersededResources(engine, currentState, supersededResourcesOf(currentState), currentState.resources);
48712
49746
  let persistedOutputs = currentState.outputs ?? {};
48713
49747
  if (!engine.options.dryRun) {
48714
49748
  const resolvedOutputs = redactOutputs(engine.maskingDeps(), await withSharedDrainBudget(() => resolveOutputs(engine.outputsRun, engine.outputsDeps(), effectiveTemplate, currentState.resources, stackName, outputsDigestSource, parameterValues, conditions)));
@@ -48747,37 +49781,41 @@ async function doDeployWithPrefetch(engine, stackName, template, createOnlyPrefe
48747
49781
  stackName
48748
49782
  });
48749
49783
  const outputProvenanceBackfill = !outputsChanged && currentState.outputReferenceProvenance === void 0 && engine.provenanceBaseline?.tokenFree === false && engine.outputReferenceProvenanceFor(persistedOutputs, redactOutputs(engine.maskingDeps(), persistedOutputs)) !== void 0;
48750
- if (observedRefresh || outputProvenanceBackfill || maskedFingerprintsBackfilled || conditionVerdictsChanged || outputsChanged || exportSetChanged || skippedOutputsChanged || orphansChanged || healedAttributesPending) try {
48751
- const refreshedState = {
48752
- version: 12,
48753
- region: engine.stackRegion,
48754
- stackName: currentState.stackName,
48755
- resources: currentState.resources,
48756
- ...orphansCarriedFrom(currentState),
48757
- outputs: outputsChanged ? outputsToPersist : persistedOutputs,
48758
- ...exportNamesToPersist === void 0 ? exportNamesCarriedFrom(currentState) : { exportNames: [...exportNamesToPersist] },
48759
- ...engine.outputsRun.skippedOutputs && { skippedOutputs: { ...engine.outputsRun.skippedOutputs } },
48760
- ...crossStackReadsForPartialSave(currentState, engine.recordedImports, engine.recordedOutputReads, engine.crossStackReadKeyNormalizer()),
48761
- ...conditionVerdicts && { conditionVerdicts },
48762
- lastModified: Date.now()
48763
- };
48764
- const saveOptions = {};
48765
- if (currentEtag !== void 0) saveOptions.expectedEtag = currentEtag;
48766
- if (migrationPending) saveOptions.migrateLegacy = true;
48767
- await engine.stateBackend.saveState(stackName, engine.stackRegion, engine.withParentInfo(refreshedState), saveOptions);
48768
- if (outputsChanged || exportSetChanged) {
48769
- persistedOutputs = refreshedState.outputs;
48770
- if (outputsChanged) engine.logger.info("Persisted Outputs-only change (no resource diff).");
48771
- else engine.logger.debug("Persisted export-set change (no outputs-value diff, no-change path)");
48772
- if (engine.exportIndexStore) await engine.exportIndexStore.updateForStack(stackName, engine.stackRegion, importableOutputs({
48773
- ...refreshedState,
48774
- outputs: redactOutputs(engine.maskingDeps(), refreshedState.outputs)
48775
- }));
48776
- } else if (observedRefresh) engine.logger.debug("Persisted refreshed observedProperties (no-change path)");
48777
- else if (healedAttributesPending) engine.logger.debug("Persisted attributes re-read from AWS for a stale record (no-change path)");
48778
- else engine.logger.debug("Persisted skipped-outputs record (no outputs-value diff, no-change path)");
48779
- } catch (saveError) {
48780
- engine.logger.warn(safeMsg`Failed to persist no-change state update: ${asIsMessage(describeAwsFailure(saveError).detail)} — drift baseline / outputs will be re-resolved on next deploy.`);
49784
+ if (observedRefresh || outputProvenanceBackfill || maskedFingerprintsBackfilled || conditionVerdictsChanged || outputsChanged || exportSetChanged || skippedOutputsChanged || orphansChanged || supersededChanged || healedAttributesPending) {
49785
+ await resolveStackInstanceId();
49786
+ try {
49787
+ const refreshedState = {
49788
+ version: 13,
49789
+ region: engine.stackRegion,
49790
+ stackName: currentState.stackName,
49791
+ resources: currentState.resources,
49792
+ ...orphansCarriedFrom(currentState),
49793
+ ...stackInstanceIdCarriedFrom(currentState),
49794
+ outputs: outputsChanged ? outputsToPersist : persistedOutputs,
49795
+ ...exportNamesToPersist === void 0 ? exportNamesCarriedFrom(currentState) : { exportNames: [...exportNamesToPersist] },
49796
+ ...engine.outputsRun.skippedOutputs && { skippedOutputs: { ...engine.outputsRun.skippedOutputs } },
49797
+ ...crossStackReadsForPartialSave(currentState, engine.recordedImports, engine.recordedOutputReads, engine.crossStackReadKeyNormalizer()),
49798
+ ...conditionVerdicts && { conditionVerdicts },
49799
+ lastModified: Date.now()
49800
+ };
49801
+ const saveOptions = {};
49802
+ if (currentEtag !== void 0) saveOptions.expectedEtag = currentEtag;
49803
+ if (migrationPending) saveOptions.migrateLegacy = true;
49804
+ await engine.stateBackend.saveState(stackName, engine.stackRegion, engine.withParentInfo(refreshedState), saveOptions);
49805
+ if (outputsChanged || exportSetChanged) {
49806
+ persistedOutputs = refreshedState.outputs;
49807
+ if (outputsChanged) engine.logger.info("Persisted Outputs-only change (no resource diff).");
49808
+ else engine.logger.debug("Persisted export-set change (no outputs-value diff, no-change path)");
49809
+ if (engine.exportIndexStore) await engine.exportIndexStore.updateForStack(stackName, engine.stackRegion, importableOutputs({
49810
+ ...refreshedState,
49811
+ outputs: redactOutputs(engine.maskingDeps(), refreshedState.outputs)
49812
+ }));
49813
+ } else if (observedRefresh) engine.logger.debug("Persisted refreshed observedProperties (no-change path)");
49814
+ else if (healedAttributesPending) engine.logger.debug("Persisted attributes re-read from AWS for a stale record (no-change path)");
49815
+ else engine.logger.debug("Persisted skipped-outputs record (no outputs-value diff, no-change path)");
49816
+ } catch (saveError) {
49817
+ engine.logger.warn(safeMsg`Failed to persist no-change state update: ${asIsMessage(describeAwsFailure(saveError).detail)} — drift baseline / outputs will be re-resolved on next deploy.`);
49818
+ }
48781
49819
  }
48782
49820
  }
48783
49821
  return {
@@ -48817,7 +49855,7 @@ async function doDeployWithPrefetch(engine, stackName, template, createOnlyPrefe
48817
49855
  }
48818
49856
  const abandonedRunAt = engine.lockManager.abandonedRunEndedBy?.(stackName, engine.stackRegion);
48819
49857
  if (abandonedRunAt !== void 0) await noteAbandonedRun(abandonedRunAt);
48820
- engine.generatedNameGuard = GeneratedNameGuard.start({
49858
+ const generatedNameGuardInput = {
48821
49859
  stackName,
48822
49860
  region: engine.stackRegion,
48823
49861
  changes: changes.values(),
@@ -48828,7 +49866,7 @@ async function doDeployWithPrefetch(engine, stackName, template, createOnlyPrefe
48828
49866
  records: currentState.resources,
48829
49867
  orphans: currentState.orphans,
48830
49868
  loadJournal: () => engine.stateBackend.loadRollbackJournal(stackName, engine.stackRegion),
48831
- loadRetained: () => engine.stateBackend.loadRetainedRecord(stackName, engine.stackRegion),
49869
+ loadRetained: () => retainedAtFirstDeploy ?? engine.stateBackend.loadRetainedRecord(stackName, engine.stackRegion),
48832
49870
  loadKeptInHistory: () => {
48833
49871
  const reader = new DeploymentEventsReader(engine.stateBackend);
48834
49872
  return loadKeptInHistory({
@@ -48840,8 +49878,24 @@ async function doDeployWithPrefetch(engine, stackName, template, createOnlyPrefe
48840
49878
  accountInfo: () => getAccountInfo(engine.stackRegion),
48841
49879
  warn: (message) => engine.logger.warn(message),
48842
49880
  ...abandonedRunAt !== void 0 && { abandonedRunAt }
48843
- });
48844
- if (engine.generatedNameGuard !== void 0) engine.logger.debug(safeMsg`Generated-name check: looking up ${String(engine.generatedNameGuard.size)} planned create(s)`);
49881
+ };
49882
+ const startGeneratedNameGuard = () => {
49883
+ const scope = nameScope();
49884
+ const lifetimeId = scope.stackInstanceId;
49885
+ engine.generatedNameGuard = withStackScope(scope, () => GeneratedNameGuard.start({
49886
+ ...generatedNameGuardInput,
49887
+ ...lifetimeId !== void 0 && {
49888
+ newScheme: true,
49889
+ replacementGeneration: (change) => {
49890
+ const record = currentState.resources[change.logicalId];
49891
+ if (record === void 0 || record.resourceType !== change.resourceType || !((change.propertyChanges ?? []).some((pc) => pc.requiresReplacement) || recreateTargetIdsFor(engine.options.recreateTargets, stackName)?.has(change.logicalId) === true)) return;
49892
+ return nextNameGeneration(record, change.logicalId, lifetimeId);
49893
+ }
49894
+ }
49895
+ }));
49896
+ if (engine.generatedNameGuard !== void 0) engine.logger.debug(safeMsg`Generated-name check: looking up ${String(engine.generatedNameGuard.size)} planned create(s)`);
49897
+ };
49898
+ if (!idArrivesLater) startGeneratedNameGuard();
48845
49899
  await checkDestructivePlan({
48846
49900
  options: engine.options,
48847
49901
  stackName,
@@ -48860,13 +49914,19 @@ async function doDeployWithPrefetch(engine, stackName, template, createOnlyPrefe
48860
49914
  secretMask: () => diffLogMasker(engine.secretsRun, engine.maskingDeps(), diffResolverContext.recordedSecretValues, effectiveTemplate, parameterValues, currentState.resources)
48861
49915
  })) engine.generatedNameGuard?.noteApprovalPrompted();
48862
49916
  startProvenanceCapture();
48863
- engine.generatedNameGuard?.recordPlannedIntents();
48864
49917
  engine.resolver.resetPhysicalIdFallbackCount();
49918
+ await resolveStackInstanceId();
49919
+ if (idArrivesLater) {
49920
+ startGeneratedNameGuard();
49921
+ const kept = await retainedForWarning ?? [];
49922
+ if (kept.length > 0) engine.logger.warn(safeMsg`An earlier deployment of ${displayStackName(stackName)} kept ${String(kept.length)} ` + "resource(s) here (DeletionPolicy: Retain). This deploy does not take them back: it creates new resources under new names, as CloudFormation does. To manage a kept resource again, adopt it with 'cdkd import'.");
49923
+ }
49924
+ engine.generatedNameGuard?.recordPlannedIntents();
48865
49925
  const progress = {
48866
49926
  current: 0,
48867
49927
  total: createChanges.length + updateChanges.length + deleteChanges.length
48868
49928
  };
48869
- const { state: newState, actualCounts, completedOperations } = await withProducerRegions(() => producerRegionEvidence(engine.rollbackDeps(), currentState), () => executeDeployment(engine, effectiveTemplate, currentState, changes, dag, executionLevels, stackName, outputsDigestSource, parameterValues, conditions, currentEtag, progress, migrationPending));
49929
+ const { state: newState, actualCounts, completedOperations } = await withProducerRegions(() => producerRegionEvidence(engine.rollbackDeps(), currentState), () => withStackScope(nameScope(), () => executeDeployment(engine, effectiveTemplate, currentState, changes, dag, executionLevels, stackName, outputsDigestSource, parameterValues, conditions, currentEtag, progress, migrationPending)));
48870
49930
  try {
48871
49931
  engine.stateBackend.warmConnections(stackName, engine.stackRegion, TAIL_WARM_CONNECTIONS);
48872
49932
  } catch {}
@@ -48977,6 +50037,7 @@ var DeployEngine = class {
48977
50037
  resolver;
48978
50038
  interrupted = false;
48979
50039
  interruptCause = null;
50040
+ userInterrupted = false;
48980
50041
  absentJournalVersionPurge;
48981
50042
  noEchoCeilingReadbackTimeoutMs = 3e4;
48982
50043
  observedCapture;
@@ -49109,10 +50170,20 @@ var DeployEngine = class {
49109
50170
  deployStateResources: () => this.deployStateResources,
49110
50171
  interrupted: () => this.interrupted,
49111
50172
  interruptCause: () => this.interruptCause,
50173
+ userInterrupted: () => this.userInterrupted,
49112
50174
  withRetry: (...args) => this.withRetry(...args),
49113
50175
  maskingRetryLoggerFor: (...args) => this.maskingRetryLoggerFor(...args),
49114
50176
  inlinePolicyClaimedFor: (...args) => this.inlinePolicyClaimedFor(...args),
49115
- recordEvent: (...args) => this.recordEvent(...args)
50177
+ recordEvent: (...args) => this.recordEvent(...args),
50178
+ guardedReplacementCreate: (input, create) => guardedReplacementCreate(this, input, create),
50179
+ generatedNameGuarded: (logicalId) => this.generatedNameGuard?.candidate(logicalId) !== void 0,
50180
+ rollbackRetainedCopies: (stackName) => {
50181
+ const reader = new DeploymentEventsReader(this.stateBackend);
50182
+ return loadRollbackRetainedCopies({
50183
+ listRuns: (s, r) => reader.listRuns(s, r),
50184
+ readRunEvents: (s, r, id) => reader.readRunEvents(s, r, id)
50185
+ }, stackName, this.stackRegion);
50186
+ }
49116
50187
  };
49117
50188
  }
49118
50189
  resolverContextDeps() {
@@ -49164,6 +50235,7 @@ var DeployEngine = class {
49164
50235
  },
49165
50236
  absentJournalVersionPurge: () => this.absentJournalVersionPurge,
49166
50237
  interrupted: () => this.interrupted,
50238
+ userInterrupted: () => this.userInterrupted,
49167
50239
  noEchoDeps: () => this.noEchoDeps(),
49168
50240
  recordEvent: (...args) => this.recordEvent(...args),
49169
50241
  crossStackReadKeyNormalizer: (...args) => this.crossStackReadKeyNormalizer(...args)
@@ -49190,7 +50262,7 @@ var DeployEngine = class {
49190
50262
  this.inlinePolicyWriters = /* @__PURE__ */ new Map();
49191
50263
  this.resolver.resetPhysicalIdFallbackCount();
49192
50264
  const ledger = ledgerForStack(this.stateBackend, stackName, this.stackRegion);
49193
- return withStackName(stackName, () => this.options.dryRun ? this.doDeploy(stackName, template) : withCreateTokenLedger(ledger, () => this.doDeploy(stackName, template)));
50265
+ return withStackScopeBeforeStateRead(stackName, () => this.options.dryRun ? this.doDeploy(stackName, template) : withCreateTokenLedger(ledger, () => this.doDeploy(stackName, template)));
49194
50266
  }
49195
50267
  static maskedRecordRemedyFor = maskedRecordRemedyFor;
49196
50268
  redactStateForPersist(state) {
@@ -49203,23 +50275,35 @@ var DeployEngine = class {
49203
50275
  const noEchoScrubbed = applyNoEchoPersist(this.noEchoRun, this.noEchoDeps(), logicalId, record, withNoEchoExactEchoes(this.noEchoRun, logicalId, scrubbed), templateProps, state.resources, secrets);
49204
50276
  resources[logicalId] = withReferenceProvenance(withConstructPath(withMaskedPropertyFingerprints(noEchoScrubbed, record.properties, templateProps, secrets, this.secretsRun.fingerprintNoEchoValues, this.secretsRun.perResourceInputFingerprints.get(logicalId)), constructPathOf(this.noEchoRun.constructPathTemplate, logicalId)), this.referenceProvenanceToPersist(logicalId, record, noEchoScrubbed, secrets));
49205
50277
  }
49206
- const orphans = state.orphans?.map((entry) => {
50278
+ const orphanEntries = state.orphans;
50279
+ const scrubDroppedRecord = (entry) => {
49207
50280
  const sameResource = this.secretsRun.perResourceResolvedType.get(entry.logicalId) === entry.state.resourceType;
49208
50281
  const orphanTemplateProps = sameResource ? this.secretsRun.perResourceTemplateProps.get(entry.logicalId) : void 0;
49209
- return {
50282
+ return withMaskedPropertyFingerprints(applyNoEchoPersist(this.noEchoRun, this.noEchoDeps(), entry.logicalId, entry.state, scrubResourceRecord(entry.state, (sameResource ? this.secretsRun.perResourceSecrets.get(entry.logicalId) : void 0) ?? /* @__PURE__ */ new Map(), orphanTemplateProps, void 0, sameGenerationClaimsOf(entry.state)), orphanTemplateProps, state.resources), entry.state.properties, orphanTemplateProps, sameResource ? this.secretsRun.perResourceSecrets.get(entry.logicalId) : void 0, sameResource ? this.secretsRun.fingerprintNoEchoValues : void 0, sameResource ? this.secretsRun.perResourceInputFingerprints.get(entry.logicalId) : void 0);
50283
+ };
50284
+ const orphans = orphanEntries?.map((entry) => ({
50285
+ ...entry,
50286
+ state: scrubDroppedRecord(entry)
50287
+ }));
50288
+ const superseded = Array.isArray(state.supersededResources) ? state.supersededResources.flatMap((entry) => {
50289
+ if (!isSupersededResourceRecord(entry)) return [entry];
50290
+ if (isSupersededHeldBySameType(entry, state.resources)) return [];
50291
+ return [{
49210
50292
  ...entry,
49211
- state: withMaskedPropertyFingerprints(applyNoEchoPersist(this.noEchoRun, this.noEchoDeps(), entry.logicalId, entry.state, scrubResourceRecord(entry.state, (sameResource ? this.secretsRun.perResourceSecrets.get(entry.logicalId) : void 0) ?? /* @__PURE__ */ new Map(), orphanTemplateProps, void 0, sameGenerationClaimsOf(entry.state)), orphanTemplateProps, state.resources), entry.state.properties, orphanTemplateProps, sameResource ? this.secretsRun.perResourceSecrets.get(entry.logicalId) : void 0, sameResource ? this.secretsRun.fingerprintNoEchoValues : void 0, sameResource ? this.secretsRun.perResourceInputFingerprints.get(entry.logicalId) : void 0)
49212
- };
49213
- });
50293
+ state: scrubDroppedRecord(entry)
50294
+ }];
50295
+ }) : void 0;
49214
50296
  const outputs = redactOutputs(this.maskingDeps(), state.outputs);
49215
50297
  const crossStackReads = this.redactCrossStackReads(state);
49216
- const { outputReferenceProvenance: _incoming, ...rest } = state;
50298
+ const { outputReferenceProvenance: _incoming, supersededResources: _superseded, ...rest } = state;
49217
50299
  const outputReferenceProvenance = this.outputReferenceProvenanceFor(state.outputs, outputs);
49218
50300
  return {
49219
50301
  ...rest,
50302
+ ...stackInstanceIdCarriedFrom(withoutSuperseded(state)),
49220
50303
  resources,
49221
50304
  outputs,
49222
50305
  ...orphans === void 0 ? {} : { orphans },
50306
+ ...superseded === void 0 ? _superseded !== void 0 && { supersededResources: _superseded } : superseded.length > 0 && { supersededResources: superseded },
49223
50307
  ...crossStackReads,
49224
50308
  ...outputReferenceProvenance !== void 0 && { outputReferenceProvenance }
49225
50309
  };
@@ -49292,6 +50376,7 @@ var DeployEngine = class {
49292
50376
  const { parentStack, parentLogicalId, parentRegion } = this.options.parentStackInfo;
49293
50377
  return {
49294
50378
  ...redacted,
50379
+ ...stackInstanceIdCarriedFrom(redacted),
49295
50380
  parentStack,
49296
50381
  parentLogicalId,
49297
50382
  parentRegion
@@ -49339,14 +50424,14 @@ var DeployEngine = class {
49339
50424
  }
49340
50425
  };
49341
50426
  }
49342
- async withRetry(operation, logicalId, maxRetries, initialDelayMs, provider) {
50427
+ async withRetry(operation, logicalId, maxRetries, initialDelayMs, provider, interruptOnlyByUser) {
49343
50428
  if (provider?.disableOuterRetry) return operation();
49344
50429
  return withRetry(operation, logicalId, {
49345
50430
  ...maxRetries !== void 0 && { maxRetries },
49346
50431
  ...initialDelayMs !== void 0 && { initialDelayMs },
49347
50432
  logger: this.maskingRetryLogger(logicalId),
49348
- isInterrupted: () => this.interrupted,
49349
- onInterrupted: () => new InterruptedError(this.interruptCause ?? "user")
50433
+ isInterrupted: () => interruptOnlyByUser === true ? this.userInterrupted : this.interrupted,
50434
+ onInterrupted: () => new InterruptedError(interruptOnlyByUser === true ? "user" : this.interruptCause ?? "user")
49350
50435
  });
49351
50436
  }
49352
50437
  maskingRetryLogger(logicalId) {
@@ -49367,5 +50452,5 @@ function sameGenerationClaimsOf(record) {
49367
50452
  }
49368
50453
 
49369
50454
  //#endregion
49370
- export { DeploymentEventsStore as $, repairMalformedOrphanRecordsForReadOnly as $a, runDockerStreaming as $c, UNREADABLE_ORPHANS_CONTAINER_ROW as $i, ATOMIC_FINAL_SNAPSHOT_TYPES as $n, noEchoOutputsComparison as $o, reDisableInstanceApiTermination as $r, CrossPrefixReadError as $s, updatePartialReason as $t, inheritedProvenanceForChildDiff as A, refuseMalformedNestedChildOutputs as Aa, getBootstrapMarkerKey as Ac, producerRegionsFromState as Ai, StageLoadError as Al, bold as An, configStringRefusal as Ao, TemplateNoEchoReresolver as Ar, pathCrossesDottedKey as As, replayFailedOperations as At, conditionFingerprint as B, refuseMalformedResourceEntriesForDeploy as Ba, describeDockerExecFailure as Bc, keepsSecretReferenceToken as Bi, renderAssemblyPathEscape as Bl, createReadOnlyAttributeHealerFactory as Bn, canonicalLambdaFunctionName as Bo, unchangedBehindSecretReference as Br, provenPublicValue as Bs, recordUnderIdIsNotOwn as Bt, hasCdkAutoDeleteTag as C, malformedResourceEntriesWarning as Ca, createAssetRedirectResolver as Cc, sendableAssumeRoleCommand as Ci, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Cl, findDestructiveChanges as Cn, isWarmThroughputDecrease as Co, stringifyIfAssigned as Cr, maskSecretsInText as Cs, makeForeignHolderScan as Ct, IMPLICIT_DELETE_DEPENDENCIES as D, malformedStateRefusalMessage as Da, BOOTSTRAP_MARKER_PREFIX as Dc, envCredentialsAreAssumedRole as Di, canonicalizeRegion as Dl, getStackRecords as Dn, coerceCfnInteger as Do, isCustomResource as Dr, DYNAMIC_REFERENCE_TOKEN_SCAN as Ds, isAccessDeniedError as Dt, markRefusedBeforeApplying as E, malformedScrubResourceEntriesRefusalMessage as Ea, AssetModeResolver as Ec, callerEnvCredentials as Ei, PARTITION_TABLE as El, destroyStackRecordsView as En, coerceCfnBoolean as Eo, ccBrokenReason as Er, withoutPhysicalNameCopies as Es, chunks as Et, findNestedTemplateTreeDefect as F, refuseMalformedOrphansForOrphan as Fa, validateContainerRepoName as Fc, getAccountInfo as Fi, nullPrototypeRecord as Fl, yellow as Fn, requireConfigString as Fo, CUSTOM_RESOURCE_RESPONSE_LIFECYCLE_RULE_ID as Fr, physicalNameCopiesOf as Fs, isJournaledOrphan as Ft, diffSecurity as G, refuseMalformedResourcePropertiesForDestroy as Ga, getDockerCmd as Gc, parseWebACLArn as Gi, resolveBucketRegion as Gl, orphanRecordsPrintingBag as Gn, canonicalCoordinates as Go, deleteSkipReason as Gr, recordProvenPublicExpression as Gs, findNestedStackTypeChanges as Gt, parentSuppliedValues as H, refuseMalformedResourceEntriesForImport as Ha, dockerSpawnEnvWithSensitive as Hc, cfnRefValueFromPhysicalId as Hi, injectAwsRegion as Hl, journaledOrphanPrintingBag as Hn, sameLambdaFunctionAddress as Ho, UNSPECIFIED_SKIP_REASON as Hr, recordMaskOnlyValuesIn as Hs, resolveReplacementOldType as Ht, isAbsoluteAssetPath as I, refuseMalformedOutputs as Ia, buildDenyExternalAccessPolicy as Ic, isImpossibleEmptyStoredAttribute as Ii, ownValue as Il, noEchoAttributeNamesOf as In, prefetchCreateOnlyPropertyPaths as Io, applyCustomResourceResponseExpiration as Ir, recordAttributeIdCopy as Is, isReplacementOrphan as It, mergeNoChangeOutputs as J, refuseMalformedResourcePropertiesForOrphan as Ja, partitionSensitiveEnv as Jc, passedParameterClassesOf as Ji, secretNamedAttributeNamesOf as Jn, maskReadbackAtCoordinates as Jo, markWaitAbandoned as Jr, isSingleDynamicReferenceToken as Js, REDACTED_DELETE_ADDRESS_SKIP_REASON as Jt, isSecretBearingReferenceString as K, refuseMalformedResourcePropertiesForDrift as Ka, isDockerClientEnvKey as Kc, withSharedDrainBudget as Ki, secretNamePrintingBag as Kn, isMarkedCoordinate as Ko, withIndeterminateGuard as Kr, wholeStringLeavesOf as Ks, getCurrentProducerRegions as Kt, listNestedTemplateRows as L, refuseMalformedOutputsForDestroy as La, buildDockerImage as Lc, carriesDynamicReference as Li, absoluteAssemblyPathEscape as Ll, noEchoComparisonForTemplate as Ln, templateResourceTypes as Lo, customResourceResponseExpirationWarning as Lr, recordPhysicalNameCopy as Ls, planFailedOps as Lt, findServiceTokenRefusals as M, refuseMalformedOrphanRecordsForDestroy as Ma, parseBootstrapMarker as Mc, coerceParameterTypedValue as Mi, defineOwnKey as Ml, gray as Mn, replayWarn as Mo, isCustomResourceType as Mr, inheritNestedStackParameterAssociations as Ms, removeProtectionTypeList as Mt, serviceTokenBlockingReason as N, refuseMalformedOrphans as Na, readBootstrapMarkerBody as Nc, isUnboundTemplateParameter as Ni, hasOwnKey as Nl, green as Nn, requireConfigArray as No, getCurrentResourceSecrets as Nr, recordNestedStackParameterExpressions as Ns, recordAfterRollbackUpdate as Nt, computeImplicitDeleteEdges as O, previewableOrphanRecords as Oa, assertAssetBucketRegion as Oc, classifyRecordedSecretRegion as Oi, derivePartitionAndUrlSuffix as Ol, withStackRecords as On, configBooleanRefusal as Oo, wouldReturnToSdkProvider as Or, dynamicReferenceTokens as Os, lookupEachName as Ot, childTemplateLoader as P, refuseMalformedOrphansForDestroy as Pa, validateAssetBucketName as Pc, parameterTypeMayLoseSecretIdentity as Pi, hasPlainPrototype$1 as Pl, red as Pn, requireConfigObject as Po, withPrintingSecrets as Pr, recoverMaskedOutput as Ps, demoteSupersededOrphans as Pt, DeploymentEventsReader as Q, refuseMalformedState as Qa, runDockerForeground as Qc, STATE_RESOURCES_MALFORMED as Qi, extractDeploymentEventError as Qn, noEchoLeavesOf as Qo, observeThenDisableInstanceApiTermination as Qr, rebuildClientForBucketRegion as Qs, updatePartialMessage as Qt, renderNestedTemplateTreeDefect as R, refuseMalformedResourceAttributesForOrphan as Ra, resolveDockerContextDirectory as Rc, carriesFetchableDynamicReference as Ri, describeFileReadFailure as Rl, noEchoExactEchoLeavesOf as Rn, describeTypeWithThrottleRetry as Ro, CloudControlProvider as Rr, splitGetAttStringForm as Rs, planRollback as Rt, S3_AUTO_DELETE_OBJECTS_TAG as S, malformedRenderedContainersWarning as Sa, buildAssetRedirectMap as Sc, isIamRoleArn as Si, uploadCfnTemplate as Sl, constructPathOf as Sn, coerceWarmThroughput as So, definedAttributes as Sr, maskSecretsInError as Ss, loadJournaledOrphans as St, getPriorAttempts as T, malformedResourcesWarning as Ta, rewriteTemplateAssetReferences as Tc, applyCallerIdentityCredentials as Ti, expectedOwnerParam as Tl, awaitInFlightSibling as Tn, toFiniteNumber as To, ProviderRegistry as Tr, withPhysicalNameCopies as Ts, formatResourceLine as Tt, readRecordedConditionVerdicts as U, refuseMalformedResourceEntriesForImportSave as Ua, finchSecretArgvRefusal as Uc, isStalePlaceholderArnAttribute as Ui, injectedAwsRegionValue as Ul, maskEventTextWithBoundBags as Un, DagBuilder as Uo, deleteIndeterminateGuards as Ur, recordNoEchoAttributeValues as Us, seedsDeleteFirstGuard as Ut, conditionInputsFrom as V, refuseMalformedResourceEntriesForDestroy as Va, describeDockerFailure as Vc, AWS_NO_VALUE as Vi, resolveAssemblyPath as Vl, completedReplayEntries as Vn, lambdaFunctionNameForMask as Vo, RollbackInlinePolicyWriters as Vr, recordMaskOnlyValue as Vs, replacementNeverSwapped as Vt, withResourceDeadline as W, refuseMalformedResourceEntriesForOrphan as Wa, formatDockerLoginError as Wc, refStateLookupFromResource as Wi, clearBucketRegionCache as Wl, namingSecretsWithRecordedNoEcho as Wn, TemplateParser as Wo, deleteLeftInPlace as Wr, recordNoEchoParameterFreshValue as Ws, orphanNeedsIdentity as Wt, makeSiblingClaimReader as X, refuseMalformedResourcesForDestroy as Xa, redactedCause as Xc, JSON_LISTING_HOLE_VALUE as Xi, stateSecretNameNeedles as Xn, maskedLeafCoordinatesOf as Xo, ec2InstanceProtectionSite as Xr, isLockInfoExpired as Xs, redactedDeleteAddressFields as Xt, makeCanonicalizePropertiesFn as Y, refuseMalformedResourcesForDeploy as Ya, redactDockerArgvValues as Yc, declaresNoEcho as Yi, secretNamedRecordNeedles as Yn, maskWholeValue as Yo, disableInstanceApiTermination as Yr, LockManager as Ys, isRedactedRecordedValue as Yt, planOrphanAdoption as Z, refuseMalformedResourcesForExport as Za, redactedDockerCause as Zc, STATE_REGION_DIVERGED as Zi, storedSecretNameNeedles as Zn, noEchoCoordinatesOf as Zo, isTerminationProtectionPropagationError as Zr, S3StateBackend as Zs, redactedDeleteAddressSkip as Zt, deleteTableWithIndexBusyRetry as _, malformedOrphanRowsKeptWarning as _a, shouldRetainResource as _c, recordedReferenceRegions as _i, ssmResolvedValueType as _l, isExportAliasCollision as _n, MULTI_REGION_RECREATE_BLOCKED_TYPES as _o, planTagDiff as _r, recordLogOnlyValue as _s, deleteJournaledOrphans as _t, isProtectionValueActive as a, hasReadableOutputs as aa, splitImportedOps as ac, isSettledInstanceState as ai, getDefaultStateBucketName as al, displayTextOrWithheld as an, unpreviewableOrphanRecords as ao, isFinalSnapshotError as ar, liveMatchesUnresolvedTokenFrame as as, displacedOpClause as at, waitForIndexesSettled as b, malformedOutputsRefusalMessage as ba, stringifyValue as bc, assertSendableRoleArn as bi, MIGRATE_TMP_PREFIX as bl, DEFAULT_RESOURCE_WARN_AFTER_MS as bn, renderStatefulReason as bo, tagMapAsList as br, createSecretMasker as bs, isHandledOrphan as bt, DynamoDBGlobalTableProvider as c, isReadableResourceEntry as ca, exportNamesCarriedFrom as cc, s3BucketDualStackDomainName as ci, resolveApp as cl, secretBearingStateKeyWarning as cn, withheldAccountClause as co, earliestDefined as cr, recordReferenceProvenanceEntry as cs, getNestedRevertRun as ct, compensateRemovedDeletionProtection as d, malformedExportSourceWarning as da, hasUnverifiableParameterRefusal as dc, resolveDynamicReferences as di, resolveSkipPrefix as dl, exportNameNoEchoParameters as dn, unwrittenCreateOnlyReplacement as do, withCreateTokenLedger as dr, createUnionSecretMasker as ds, revertNestedChildFromJournal as dt, UNREADABLE_RESOURCES_MAP_ROW as ea, CrossPrefixScanCache as ec, reEnableInstanceApiTermination as ei, warnFinchArgvExposure as el, computeParameterTaint as en, repairMalformedOrphansForReadOnly as eo, PRE_DELETE_SNAPSHOT_TYPES as er, passedNoEchoParametersOf as es, pendingRequestCount as et, deleteBudgetKey as f, malformedLocalOutputsWarning as fa, importableOutputKeys as fc, producerLookupOver as fi, resolveStateBucketWithDefault as fl, exportNameSecretExposure as fn, withoutSilentDropProperties as fo, acquireIdempotencyToken as fr, hasMaskableValues as fs, revertedNestedRowIds as ft, INDEX_SETTLE_POLL_INTERVAL_MS as g, malformedOrphanRecordsWarning as ga, orphansCarriedFrom as gc, recordedOutputReferenceRegions as gi, warnDeprecatedNoPrefixCliFlag as gl, collectPublishedOutputNames as gn, shownType as go, DBPROXY_TAG_OPTIONS as gr, recordLogOnlyParameterValue as gs, withNestedStackContext as gt, DELETE_INDEX_WAIT_PROCEED_NOTE as h, malformedOrphanRecordsRefusalMessage as ha, orphansAfterRollback as hc, outputReferenceProvenanceOf as hi, stateBucketExistenceConfirmed as hl, collectDeclaredOutputNames as hn, findReplacedReadersOfRecreateTargets as ho, DBPROXY_TAGS_WHAT as hr, printingCorpusOf as hs, getCurrentNestedStackContext as ht, PROTECTION_PROPERTY_BY_TYPE as i, hasReadableOrphans as ia, isNotImplemented as ic, poisonRenderedSpellingsCollidingIn as ii, synthesisStatusMessage as il, WITHHELD_NAME_DISPLAY as in, repairMalformedResourcesForReadOnly as io, effectiveDeletionPolicy as ir, valueAtCoordinate as is, NESTED_PENDING_PARENT_REASON as it, deferredServiceTokenWarning as j, refuseMalformedOrphanRecords as ja, isCrossRegionRedirect as jc, regionLessSecretName as ji, processStackMessages as jl, cyan as jn, readConfigString as jo, readsNoEchoDeleteValues as jr, identityKeyFor as js, replayRollback as jt, crossStackOutputProvenanceSource as k, refuseDivergentRecordRegionForDestroy as ka, ensureAssetStorage as kc, classifyReplaySecretRegion as ki, AssemblyReader as kl, replacedDeleteFailureVerdict as kn, configIntegerRefusal as ko, waitForLambdaFunctionActive as kr, isUncertifiedBaselineMaskPosition as ks, withApiLimit as kt, DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS as l, malformedDriftResourcePropertiesWarning as la, hasReadableExportSet as lc, s3BucketRegionalDomainName as li, resolveAutoAssetStorage as ll, secretSafeKeyDisplay as ln, findActionableSilentDrops as lo, ledgerForStack as lr, snapshotPassProvenance as ls, nestedChildStackName as lt, resolveDynamoDbDeleteBudgetMs as m, malformedLocalResourcesWarning as ma, isReadableBag as mc, keepHeldTokenProvenance as mi, resolveUseCdkBootstrapAssets as ml, noEchoParameterValueSeed as mn, hasNoCloudControlHandlers as mo, markDeleteStarted as mr, logOnlyValueCount as ms, collectNestedChildUnaddressed as mt, recordedProtectionEvidence as n, deployRefusesOrphanRowsReason as na, crossPrefixDeniedWarning as nc, INTRINSIC_KEYS as ni, getDockerImageBySourceHash as nl, reachableDivergentParameters as nn, repairMalformedResourceEntriesForReadOnly as no, ccRoutedFinalSnapshotError as nr, readsNoEchoSource as ns, keptForReadoption as nt, perType as o, hasReadableResources as oa, acceptedCreateOnlyDropsOf as oc, s3BucketArn as oi, getLegacyStateBucketName as ol, exportAliasCollisionScrubWarning as on, unreadableOrphanRecords as oo, refusesFinalSnapshot as or, markSameGenerationBag as os, displacedPhysicalIdShown as ot, resolveDynamoDbDeleteBudgetClock as p, malformedLocalResourceEntriesWarning as pa, importableOutputs as pc, refuseUnreadableCdkCrossAccountRefs as pi, resolveStateBucketWithDefaultAndSource as pl, isWholeDynamicReferenceValue as pn, NO_CC_HANDLERS_REASON as po, isRefusedByName as pr, literalSplitDelimitersOf as ps, withNestedRevertRun as pt, keptWholeReasonText as q, refuseMalformedResourcePropertiesForExport as qa, isMalformedEnvKey as qc, parameterInputsFor as qi, secretNamePrintingBags as qn, maskAtCoordinates as qo, isWaitAbandonedError as qr, WHOLE_DYNAMIC_REFERENCE_PATTERN as qs, inheritProducerRegions as qt, recordedProtectionNote as r, displayLogicalId as ra, isAccessDenied$1 as rc, IntrinsicFunctionResolver as ri, Synthesizer as rl, resourcesNamingDeclaredParameter as rn, repairMalformedResourcePropertiesForReadOnly as ro, createPreDeleteFinalSnapshot as rr, recordedNoEchoValuesOf as rs, recordRetainedForReadoption as rt, readProtection as s, isReadableOrphanRecord as sa, describeRegionValueKind as sc, s3BucketDomainName as si, loadCdkJson as sl, secretBearing as sn, unreadableResourceEntries as so, unsupportedFinalSnapshotError as sr, redactForState as ss, dropSettledNestedJournals as st, DeployEngine as t, accountArgs as ta, applyCrossPrefixScan as tc, DiffCalculator as ti, AssetManifestLoader as tl, namesAnyDeclaredParameter as tn, repairMalformedOutputsForReadOnly as to, buildFinalSnapshotIdentifier as tr, readbackPathFor as ts, resolvePendingCloudControlRequests as tt, beginDeleteWait as u, malformedExportNamesWarning as ua, hasReasonlessBaselineRefusal as uc, s3BucketWebsiteUrl as ui, resolveCaptureObservedState as ul, carriedExportAliasExposure as un, findSilentDropProperties as uo, reserveStackCreateToken as ur, carryLogOnlyValues as us, recordDisplacedSkips as ut, maskedLogger as v, malformedOrphansRefusalMessage as va, AssetPublisher as vc, applyRoleArnIfSet as vi, CFN_TEMPLATE_BODY_LIMIT as vl, bindingSkippedOutputs as vn, isStatefulRecreateTargetForReplace as vo, readTagList as vr, shareLogOnlyValues as vs, dropFailedCommands as vt, isTruthyCfnBoolean as w, malformedResourcePropertiesWarning as wa, loadPublishableAssetManifest as wc, AWS_CREDENTIAL_ENV_KEYS as wi, decodeListingKey as wl, formatDestructiveChange as wn, toCfnInteger as wo, CC_BROKEN_REASON as wr, scrubResourceRecord as ws, sameJournaledOrphans as wt, ECR_AUTO_DELETE_IMAGES_TAG as x, malformedOutputsWarning as xa, WorkGraph as xc, explicitRoleArnOrThrow as xi, findLargeInlineResources as xl, maskingRetryLogger as xn, WARM_THROUGHPUT_MEMBERS as xo, tagPlanWarning as xr, errorCauseChain as xs, journaledOrphanLines as xt, relayedIndexBusyDelete as y, malformedOrphansWarning as ya, isSensitiveAttributeName as yc, assertFlagRoleArn as yi, CFN_TEMPLATE_URL_LIMIT as yl, DEFAULT_RESOURCE_TIMEOUT_MS as yn, isStatefulRecreateTargetSync as yo, refuseMalformedDesiredTags as yr, unionOfSecretBags as ys, dropFailedHint as yt, templateIdentity as z, refuseMalformedResourceEntries as za, describeDockerCapturedOutput as zc, isNamelessDynamicReferenceError as zi, displayAssemblyPath as zl, noEchoParameterNamesOf as zn, withRetry as zo, slowCcOperationTimeoutMs as zr, carriesSecretMask as zs, recheckFailedPlan as zt };
49371
- //# sourceMappingURL=deploy-engine-Dy7N7-Il.js.map
50455
+ export { resolvePendingCloudControlRequests as $, refuseMalformedOutputsForDestroy as $a, validateAssetBucketName as $c, carriesDynamicReference as $i, hasPlainPrototype$1 as $l, noEchoParameterNamesOf as $n, templateResourceTypes as $o, customResourceResponseExpirationWarning as $r, recordPhysicalNameCopy as $s, nameGenerationHighWaterOf as $t, inheritedProvenanceForChildDiff as A, malformedLocalResourcesWarning as Aa, importableOutputKeys as Ac, keepHeldTokenProvenance as Ai, resolveSkipPrefix as Al, destroyStackRecordsView as An, hasNoCloudControlHandlers as Ao, markDeleteStarted as Ar, logOnlyValueCount as As, removeProtectionTypeList as At, conditionFingerprint as B, malformedResourcePropertiesWarning as Ba, WorkGraph as Bc, AWS_CREDENTIAL_ENV_KEYS as Bi, findLargeInlineResources as Bl, addSuperseded as Bn, toCfnInteger as Bo, CC_BROKEN_REASON as Br, scrubResourceRecord as Bs, resolveReplacementOldType as Bt, hasCdkAutoDeleteTag as C, isReadableOrphanRecord as Ca, splitImportedOps as Cc, s3BucketDomainName as Ci, synthesisStatusMessage as Cl, DEFAULT_RESOURCE_TIMEOUT_MS as Cn, unreadableResourceEntries as Co, reserveStackCreateToken as Cr, redactForState as Cs, formatResourceLine as Ct, IMPLICIT_DELETE_DEPENDENCIES as D, malformedExportSourceWarning as Da, hasReadableExportSet as Dc, resolveDynamicReferences as Di, resolveApp as Dl, findDestructiveChanges as Dn, unwrittenCreateOnlyReplacement as Do, DeploymentEventsStore as Dr, createUnionSecretMasker as Ds, withApiLimit as Dt, markRefusedBeforeApplying as E, malformedExportNamesWarning as Ea, exportNamesCarriedFrom as Ec, s3BucketWebsiteUrl as Ei, loadCdkJson as El, constructPathOf as En, findSilentDropProperties as Eo, DeploymentEventsReader as Er, carryLogOnlyValues as Es, lookupEachName as Et, findNestedTemplateTreeDefect as F, malformedOrphansWarning as Fa, shouldRetainResource as Fc, assertFlagRoleArn as Fi, warnDeprecatedNoPrefixCliFlag as Fl, cyan as Fn, isStatefulRecreateTargetSync as Fo, refuseMalformedDesiredTags as Fr, unionOfSecretBags as Fs, planFailedOps as Ft, diffSecurity as G, refuseDivergentRecordRegionForDestroy as Ga, AssetModeResolver as Gc, classifyReplaySecretRegion as Gi, PARTITION_TABLE as Gl, supersededAfterRollback as Gn, configIntegerRefusal as Go, waitForLambdaFunctionActive as Gr, isUncertifiedBaselineMaskPosition as Gs, inheritProducerRegions as Gt, parentSuppliedValues as H, malformedScrubResourceEntriesRefusalMessage as Ha, createAssetRedirectResolver as Hc, callerEnvCredentials as Hi, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Hl, holdsSupersededResources as Hn, coerceCfnBoolean as Ho, ccBrokenReason as Hr, withoutPhysicalNameCopies as Hs, orphanNeedsIdentity as Ht, isAbsoluteAssetPath as I, malformedOutputsRefusalMessage as Ia, stackInstanceIdCarriedFrom as Ic, assertSendableRoleArn as Ii, ssmResolvedValueType as Il, gray as In, renderStatefulReason as Io, tagMapAsList as Ir, createSecretMasker as Is, planRollback as It, mergeNoChangeOutputs as J, refuseMalformedOrphanRecordsForDestroy as Ja, ensureAssetStorage as Jc, coerceParameterTypedValue as Ji, AssemblyReader as Jl, supersededResourcesOf as Jn, replayWarn as Jo, isCustomResourceType as Jr, inheritNestedStackParameterAssociations as Js, redactedDeleteAddressFields as Jt, isSecretBearingReferenceString as K, refuseMalformedNestedChildOutputs as Ka, BOOTSTRAP_MARKER_PREFIX as Kc, producerRegionsFromState as Ki, canonicalizeRegion as Kl, supersededHeldByOtherTypeWarning as Kn, configStringRefusal as Ko, TemplateNoEchoReresolver as Kr, pathCrossesDottedKey as Ks, REDACTED_DELETE_ADDRESS_SKIP_REASON as Kt, listNestedTemplateRows as L, malformedOutputsWarning as La, AssetPublisher as Lc, explicitRoleArnOrThrow as Li, CFN_TEMPLATE_BODY_LIMIT as Ll, green as Ln, WARM_THROUGHPUT_MEMBERS as Lo, tagPlanWarning as Lr, errorCauseChain as Ls, recheckFailedPlan as Lt, findServiceTokenRefusals as M, malformedOrphanRecordsWarning as Ma, isReadableBag as Mc, recordedOutputReferenceRegions as Mi, resolveStateBucketWithDefaultAndSource as Ml, withStackRecords as Mn, shownType as Mo, DBPROXY_TAG_OPTIONS as Mr, recordLogOnlyParameterValue as Ms, demoteSupersededOrphans as Mt, serviceTokenBlockingReason as N, malformedOrphanRowsKeptWarning as Na, orphansAfterRollback as Nc, recordedReferenceRegions as Ni, resolveUseCdkBootstrapAssets as Nl, replacedDeleteFailureVerdict as Nn, MULTI_REGION_RECREATE_BLOCKED_TYPES as No, planTagDiff as Nr, recordLogOnlyValue as Ns, isJournaledOrphan as Nt, computeImplicitDeleteEdges as O, malformedLocalOutputsWarning as Oa, hasReasonlessBaselineRefusal as Oc, producerLookupOver as Oi, resolveAutoAssetStorage as Ol, formatDestructiveChange as On, withoutSilentDropProperties as Oo, extractDeploymentEventError as Or, hasMaskableValues as Os, replayFailedOperations as Ot, childTemplateLoader as P, malformedOrphansRefusalMessage as Pa, orphansCarriedFrom as Pc, applyRoleArnIfSet as Pi, stateBucketExistenceConfirmed as Pl, bold as Pn, isStatefulRecreateTargetForReplace as Po, readTagList as Pr, shareLogOnlyValues as Ps, isReplacementOrphan as Pt, pendingRequestCount as Q, refuseMalformedOutputs as Qa, readBootstrapMarkerBody as Qc, isImpossibleEmptyStoredAttribute as Qi, hasOwnKey as Ql, noEchoExactEchoLeavesOf as Qn, prefetchCreateOnlyPropertyPaths as Qo, applyCustomResourceResponseExpiration as Qr, recordAttributeIdCopy as Qs, nameGenerationCarriedFrom as Qt, renderNestedTemplateTreeDefect as R, malformedRenderedContainersWarning as Ra, isSensitiveAttributeName as Rc, isIamRoleArn as Ri, CFN_TEMPLATE_URL_LIMIT as Rl, red as Rn, coerceWarmThroughput as Ro, definedAttributes as Rr, maskSecretsInError as Rs, recordUnderIdIsNotOwn as Rt, S3_AUTO_DELETE_OBJECTS_TAG as S, hasReadableResources as Sa, withStackInstanceIdWithheld as Sc, s3BucketArn as Si, Synthesizer as Sl, bindingSkippedOutputs as Sn, unreadableOrphanRecords as So, ledgerForStack as Sr, markSameGenerationBag as Ss, sameJournaledOrphans as St, getPriorAttempts as T, malformedDriftResourcePropertiesWarning as Ta, describeRegionValueKind as Tc, s3BucketRegionalDomainName as Ti, getLegacyStateBucketName as Tl, maskingRetryLogger as Tn, findActionableSilentDrops as To, acquireIdempotencyToken as Tr, snapshotPassProvenance as Ts, isAccessDeniedError as Tt, readRecordedConditionVerdicts as U, malformedStateRefusalMessage as Ua, loadPublishableAssetManifest as Uc, envCredentialsAreAssumedRole as Ui, decodeListingKey as Ul, isSupersededResourceRecord as Un, coerceCfnInteger as Uo, isCustomResource as Ur, DYNAMIC_REFERENCE_TOKEN_SCAN as Us, findNestedStackTypeChanges as Ut, conditionInputsFrom as V, malformedResourcesWarning as Va, buildAssetRedirectMap as Vc, applyCallerIdentityCredentials as Vi, uploadCfnTemplate as Vl, deleteSupersededEntry as Vn, toFiniteNumber as Vo, ProviderRegistry as Vr, withPhysicalNameCopies as Vs, seedsDeleteFirstGuard as Vt, withResourceDeadline as W, previewableOrphanRecords as Wa, rewriteTemplateAssetReferences as Wc, classifyRecordedSecretRegion as Wi, expectedOwnerParam as Wl, removeSuperseded as Wn, configBooleanRefusal as Wo, wouldReturnToSdkProvider as Wr, dynamicReferenceTokens as Ws, getCurrentProducerRegions as Wt, makeSiblingClaimReader as X, refuseMalformedOrphansForDestroy as Xa, isCrossRegionRedirect as Xc, parameterTypeMayLoseSecretIdentity as Xi, processStackMessages as Xl, noEchoAttributeNamesOf as Xn, requireConfigObject as Xo, withPrintingSecrets as Xr, recoverMaskedOutput as Xs, updatePartialMessage as Xt, makeCanonicalizePropertiesFn as Y, refuseMalformedOrphans as Ya, getBootstrapMarkerKey as Yc, isUnboundTemplateParameter as Yi, StageLoadError as Yl, withoutSuperseded as Yn, requireConfigArray as Yo, getCurrentResourceSecrets as Yr, recordNestedStackParameterExpressions as Ys, redactedDeleteAddressSkip as Yt, planOrphanAdoption as Z, refuseMalformedOrphansForOrphan as Za, parseBootstrapMarker as Zc, getAccountInfo as Zi, defineOwnKey as Zl, noEchoComparisonForTemplate as Zn, requireConfigString as Zo, CUSTOM_RESOURCE_RESPONSE_LIFECYCLE_RULE_ID as Zr, physicalNameCopiesOf as Zs, updatePartialReason as Zt, deleteTableWithIndexBusyRetry as _, accountArgs as _a, applyCrossPrefixScan as _c, DiffCalculator as _i, runDockerForeground as _l, isWholeDynamicReferenceValue as _n, repairMalformedOutputsForReadOnly as _o, effectiveDeletionPolicy as _r, readbackPathFor as _s, dropFailedHint as _t, isProtectionValueActive as a, isStalePlaceholderArnAttribute as aa, recordNoEchoAttributeValues as ac, deleteIndeterminateGuards as ai, describeDockerExecFailure as al, namesAnyDeclaredParameter as an, refuseMalformedResourceEntriesForImportSave as ao, orphanRecordsPrintingBag as ar, DagBuilder as as, dropSettledNestedJournals as at, renderAssemblyPathEscape as au, waitForIndexesSettled as b, hasReadableOrphans as ba, isNotImplemented as bc, poisonRenderedSpellingsCollidingIn as bi, AssetManifestLoader as bl, collectPublishedOutputNames as bn, repairMalformedResourcesForReadOnly as bo, unsupportedFinalSnapshotError as br, valueAtCoordinate as bs, loadJournaledOrphans as bt, DynamoDBGlobalTableProvider as c, withSharedDrainBudget as ca, wholeStringLeavesOf as cc, withIndeterminateGuard as ci, finchSecretArgvRefusal as cl, WITHHELD_NAME_DISPLAY as cn, refuseMalformedResourcePropertiesForDrift as co, secretNamedAttributeNamesOf as cr, isMarkedCoordinate as cs, recordDisplacedSkips as ct, injectedAwsRegionValue as cu, compensateRemovedDeletionProtection as d, declaresNoEcho as da, LockManager as dc, disableInstanceApiTermination as di, isDockerClientEnvKey as dl, secretBearing as dn, refuseMalformedResourcesForDeploy as do, storedSecretNameNeedles as dr, maskWholeValue as ds, withNestedRevertRun as dt, carriesFetchableDynamicReference as ea, splitGetAttStringForm as ec, CloudControlProvider as ei, validateContainerRepoName as el, nameScopeOf as en, refuseMalformedResourceAttributesForOrphan as eo, createReadOnlyAttributeHealerFactory as er, describeTypeWithThrottleRetry as es, keptForRetainedRecord as et, nullPrototypeRecord as eu, deleteBudgetKey as f, JSON_LISTING_HOLE_VALUE as fa, isLockInfoExpired as fc, ec2InstanceProtectionSite as fi, isMalformedEnvKey as fl, secretBearingStateKeyWarning as fn, refuseMalformedResourcesForDestroy as fo, ATOMIC_FINAL_SNAPSHOT_TYPES as fr, maskedLeafCoordinatesOf as fs, collectNestedChildUnaddressed as ft, INDEX_SETTLE_POLL_INTERVAL_MS as g, UNREADABLE_RESOURCES_MAP_ROW as ga, CrossPrefixScanCache as gc, reEnableInstanceApiTermination as gi, redactedDockerCause as gl, exportNameSecretExposure as gn, repairMalformedOrphansForReadOnly as go, createPreDeleteFinalSnapshot as gr, passedNoEchoParametersOf as gs, dropFailedCommands as gt, DELETE_INDEX_WAIT_PROCEED_NOTE as h, UNREADABLE_ORPHANS_CONTAINER_ROW as ha, CrossPrefixReadError as hc, reDisableInstanceApiTermination as hi, redactedCause as hl, exportNameNoEchoParameters as hn, repairMalformedOrphanRecordsForReadOnly as ho, ccRoutedFinalSnapshotError as hr, noEchoOutputsComparison as hs, deleteJournaledOrphans as ht, PROTECTION_PROPERTY_BY_TYPE as i, cfnRefValueFromPhysicalId as ia, recordMaskOnlyValuesIn as ic, UNSPECIFIED_SKIP_REASON as ii, describeDockerCapturedOutput as il, computeParameterTaint as in, refuseMalformedResourceEntriesForImport as io, namingSecretsWithRecordedNoEcho as ir, sameLambdaFunctionAddress as is, displacedPhysicalIdShown as it, displayAssemblyPath as iu, deferredServiceTokenWarning as j, malformedOrphanRecordsRefusalMessage as ja, importableOutputs as jc, outputReferenceProvenanceOf as ji, resolveStateBucketWithDefault as jl, getStackRecords as jn, findReplacedReadersOfRecreateTargets as jo, DBPROXY_TAGS_WHAT as jr, printingCorpusOf as js, recordAfterRollbackUpdate as jt, crossStackOutputProvenanceSource as k, malformedLocalResourceEntriesWarning as ka, hasUnverifiableParameterRefusal as kc, refuseUnreadableCdkCrossAccountRefs as ki, resolveCaptureObservedState as kl, awaitInFlightSibling as kn, NO_CC_HANDLERS_REASON as ko, isRefusedByName as kr, literalSplitDelimitersOf as ks, replayRollback as kt, DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS as l, parameterInputsFor as la, WHOLE_DYNAMIC_REFERENCE_PATTERN as lc, isWaitAbandonedError as li, formatDockerLoginError as ll, displayTextOrWithheld as ln, refuseMalformedResourcePropertiesForExport as lo, secretNamedRecordNeedles as lr, maskAtCoordinates as ls, revertNestedChildFromJournal as lt, clearBucketRegionCache as lu, resolveDynamoDbDeleteBudgetMs as m, STATE_RESOURCES_MALFORMED as ma, rebuildClientForBucketRegion as mc, observeThenDisableInstanceApiTermination as mi, redactDockerArgvValues as ml, carriedExportAliasExposure as mn, refuseMalformedState as mo, buildFinalSnapshotIdentifier as mr, noEchoLeavesOf as ms, withNestedStackContext as mt, recordedProtectionEvidence as n, keepsSecretReferenceToken as na, provenPublicValue as nc, unchangedBehindSecretReference as ni, buildDockerImage as nl, retiredNameGenerationsAfter as nn, refuseMalformedResourceEntriesForDeploy as no, journaledOrphanPrintingBag as nr, canonicalLambdaFunctionName as ns, NESTED_PENDING_PARENT_REASON as nt, absoluteAssemblyPathEscape as nu, perType as o, refStateLookupFromResource as oa, recordNoEchoParameterFreshValue as oc, deleteLeftInPlace as oi, describeDockerFailure as ol, reachableDivergentParameters as on, refuseMalformedResourceEntriesForOrphan as oo, secretNamePrintingBag as or, TemplateParser as os, getNestedRevertRun as ot, resolveAssemblyPath as ou, resolveDynamoDbDeleteBudgetClock as p, STATE_REGION_DIVERGED as pa, S3StateBackend as pc, isTerminationProtectionPropagationError as pi, partitionSensitiveEnv as pl, secretSafeKeyDisplay as pn, refuseMalformedResourcesForExport as po, PRE_DELETE_SNAPSHOT_TYPES as pr, noEchoCoordinatesOf as ps, getCurrentNestedStackContext as pt, keptWholeReasonText as q, refuseMalformedOrphanRecords as qa, assertAssetBucketRegion as qc, regionLessSecretName as qi, derivePartitionAndUrlSuffix as ql, supersededHolderOf as qn, readConfigString as qo, readsNoEchoDeleteValues as qr, identityKeyFor as qs, isRedactedRecordedValue as qt, recordedProtectionNote as r, AWS_NO_VALUE as ra, recordMaskOnlyValue as rc, RollbackInlinePolicyWriters as ri, resolveDockerContextDirectory as rl, retiredNameGenerationsLeaving as rn, refuseMalformedResourceEntriesForDestroy as ro, maskEventTextWithBoundBags as rr, lambdaFunctionNameForMask as rs, displacedOpClause as rt, describeFileReadFailure as ru, readProtection as s, parseWebACLArn as sa, recordProvenPublicExpression as sc, deleteSkipReason as si, dockerSpawnEnvWithSensitive as sl, resourcesNamingDeclaredParameter as sn, refuseMalformedResourcePropertiesForDestroy as so, secretNamePrintingBags as sr, canonicalCoordinates as ss, nestedChildStackName as st, injectAwsRegion as su, DeployEngine as t, isNamelessDynamicReferenceError as ta, carriesSecretMask as tc, slowCcOperationTimeoutMs as ti, buildDenyExternalAccessPolicy as tl, retiredAfterRollback as tn, refuseMalformedResourceEntries as to, completedReplayEntries as tr, withRetry as ts, recordRetainedForReadoption as tt, ownValue as tu, beginDeleteWait as u, passedParameterClassesOf as ua, isSingleDynamicReferenceToken as uc, markWaitAbandoned as ui, getDockerCmd as ul, exportAliasCollisionScrubWarning as un, refuseMalformedResourcePropertiesForOrphan as uo, stateSecretNameNeedles as ur, maskReadbackAtCoordinates as us, revertedNestedRowIds as ut, resolveBucketRegion as uu, maskedLogger as v, deployRefusesOrphanRowsReason as va, crossPrefixDeniedWarning as vc, INTRINSIC_KEYS as vi, runDockerStreaming as vl, noEchoParameterValueSeed as vn, repairMalformedResourceEntriesForReadOnly as vo, isFinalSnapshotError as vr, readsNoEchoSource as vs, isHandledOrphan as vt, isTruthyCfnBoolean as w, isReadableResourceEntry as wa, acceptedCreateOnlyDropsOf as wc, s3BucketDualStackDomainName as wi, getDefaultStateBucketName as wl, DEFAULT_RESOURCE_WARN_AFTER_MS as wn, withheldAccountClause as wo, withCreateTokenLedger as wr, recordReferenceProvenanceEntry as ws, chunks as wt, ECR_AUTO_DELETE_IMAGES_TAG as x, hasReadableOutputs as xa, stackInstanceIdOf as xc, isSettledInstanceState as xi, getDockerImageBySourceHash as xl, isExportAliasCollision as xn, unpreviewableOrphanRecords as xo, earliestDefined as xr, liveMatchesUnresolvedTokenFrame as xs, makeForeignHolderScan as xt, relayedIndexBusyDelete as y, displayLogicalId as ya, isAccessDenied$1 as yc, IntrinsicFunctionResolver as yi, warnFinchArgvExposure as yl, collectDeclaredOutputNames as yn, repairMalformedResourcePropertiesForReadOnly as yo, refusesFinalSnapshot as yr, recordedNoEchoValuesOf as ys, journaledOrphanLines as yt, templateIdentity as z, malformedResourceEntriesWarning as za, stringifyValue as zc, sendableAssumeRoleCommand as zi, MIGRATE_TMP_PREFIX as zl, yellow as zn, isWarmThroughputDecrease as zo, stringifyIfAssigned as zr, maskSecretsInText as zs, replacementNeverSwapped as zt };
50456
+ //# sourceMappingURL=deploy-engine-CYVt2NQR.js.map