@go-to-k/cdkd 0.296.28 → 0.296.30

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 (26) hide show
  1. package/dist/{asg-provider-B3zcS87C.js → asg-provider-BaabA371.js} +3 -3
  2. package/dist/{asg-provider-B3zcS87C.js.map → asg-provider-BaabA371.js.map} +1 -1
  3. package/dist/cli.js +2 -2
  4. package/dist/{deploy-engine-D4FdwPCi.js → deploy-engine-BuPc2Dfc.js} +1951 -60
  5. package/dist/deploy-engine-BuPc2Dfc.js.map +1 -0
  6. package/dist/{iam-policy-targets-B61e5_Sk.js → iam-policy-targets-Cz2tPxXk.js} +2 -2
  7. package/dist/{iam-policy-targets-B61e5_Sk.js.map → iam-policy-targets-Cz2tPxXk.js.map} +1 -1
  8. package/dist/{iam-role-provider-0U4kMuzh.js → iam-role-provider-CAiMUBcP.js} +3 -3
  9. package/dist/{iam-role-provider-0U4kMuzh.js.map → iam-role-provider-CAiMUBcP.js.map} +1 -1
  10. package/dist/index.d.ts +121 -55
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +3 -3
  13. package/dist/{neptune-provider-DF6Eh4Tl.js → neptune-provider-5yc_bOJ-.js} +3 -3
  14. package/dist/{neptune-provider-DF6Eh4Tl.js.map → neptune-provider-5yc_bOJ-.js.map} +1 -1
  15. package/dist/{program-v_QNBjtZ.js → program-CIxuz2zX.js} +557 -41
  16. package/dist/program-CIxuz2zX.js.map +1 -0
  17. package/dist/{provider-classes-wbKBBEol.js → provider-classes-DmZ0J68L.js} +216 -26
  18. package/dist/provider-classes-DmZ0J68L.js.map +1 -0
  19. package/dist/{rds-provider-BfQsw4hw.js → rds-provider-IBY6fDvN.js} +3 -3
  20. package/dist/{rds-provider-BfQsw4hw.js.map → rds-provider-IBY6fDvN.js.map} +1 -1
  21. package/dist/{version-B0JEGOLV.js → version-B__YmKBy.js} +2 -2
  22. package/dist/{version-B0JEGOLV.js.map → version-B__YmKBy.js.map} +1 -1
  23. package/package.json +1 -1
  24. package/dist/deploy-engine-D4FdwPCi.js.map +0 -1
  25. package/dist/program-v_QNBjtZ.js.map +0 -1
  26. package/dist/provider-classes-wbKBBEol.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { r as getAwsClients, s as __exportAll } from "./aws-clients-CnmtbrxD.js";
2
2
  import { A as UNRENDERABLE, C as looksLikeCdkdGeneratedName, E as withoutGeneratedFallbackName, F as displayStackName, I as isPasteableIdent, M as displayAwsMessage, N as displayIdent, O as SECRET_REF_MAX_CODE_POINTS, P as displaySafe, R as plainIdentOr, S as getCurrentStackName, T as withStackName, V as truncateCodePoints, a as getLogger, b as generateResourceNameWithFallback, d as installLogLineMaskerSource, f as getLiveRenderer, g as applyDefaultNameForFallback, k as STACK_REF_MAX_CODE_POINTS, o as isStdoutReservedForPayload, r as safeStringify$1, t as describeAwsFailure, u as currentLogLineMasker, v as explicitNamePropertyFor, w as withSkipPrefix, x as getCurrentSkipPrefix, y as generateResourceName, z as safeMsg } from "./aws-failure-text-BaWeCWRz.js";
3
- import { $ as protectedReplacementAdvice, $n as isIamPropagationError, $t as AccountIdUnavailableError, A as isQuotableImportValue, An as commandHole, At as isAuxiliaryMarkOf, B as isTerminalDeleteFailure, Bt as sanitizeRecoveryValue, Ct as RETRY_AUXILIARY_OWNER, Dn as SHELL_ACTIVE, Dt as createdResourceIdentityBeforeFailure, E as assertRegionMatch, Et as createdBeforeFailure, F as resolveExplicitPhysicalId, Fn as plainOrDescribed, G as runDeleteAttempt, Gn as isNameCooldownError, H as observeThenDisableProtection, Hn as isUpdateUnsupportedError, Ht as DEFAULT_STATE_PREFIX, In as quotedOrDescribed, J as DELETION_PROTECTION_DOC_POINTER, Jt as withPasteableAwsProfile, Kn as isRecreateRetryableError, Kt as producerRecordKey, L as ProtectionFlipRegistry, Ln as shellBoundedDisplay, Lt as formatLockExpiry, M as quotedRemedyLogicalId, Mn as isInertUnquoted, Mt as markCreatedBeforeFailure, N as refusalTypeShown, Nn as pasteableCommand, Nt as UNREPRODUCIBLE_LOCK_CLAUSE, On as SHELL_ACTIVE_WHY, Ot as hasCreatedBeforeFailure, Pn as physicalIdShownBesideCommand, Pt as buildForceUnlockCommand, Q as pasteableAwsCommand, Qn as isAmbiguousOutcomeError, Qt as credentialFingerprint, R as compensateProtectionFlip, Rn as shellQuote, Rt as formatLockOperation, St as withDerivedNameMasks, T as RESOURCE_NOT_FOUND, U as protectionFlipKey, Un as hasReplayMayCollide, Ut as injectiveKey, Vt as CUSTOM_RESOURCE_RESPONSE_PREFIX, Wn as isNameCollisionErrorFrom, Xn as formatRetryClassificationSignals, Xt as ambientCredentialConfig, Yn as markReplayMayCollide, Yt as ambientClientDefaults, Zn as isAmbiguousCcHandlerErrorCode, Zt as clientDefaultsFor, _n as ResourceUpdateNotSupportedError, _t as createMaskedLogSinks, ar as isThrottlingError, at as startInterruptWatch, bn as StateError, bt as maskDeep, cr as wrapMaskedAwsError, ct as canonicalizeRoute53QueryName, d as prepareRemovalForUpdate, dn as MacroExpansionError, en as AssetError, er as isRetryableTransientError, f as removalWarning, fn as MalformedProducerRecordRefusalError, ft as logicalIdShown, gn as ResourceTimeoutError, gt as stateOrphanRecordRemedy, hn as ProvisioningError, ht as segmentAfterAnchor, i as holdsSecretDerivedEntry, in as DependencyError, ir as isMarkedNonRetryable, it as isInterruptedWaitError, j as normalizeAwsTagsToCfn, jt as markAuxiliaryFailure, kn as UNSHOWABLE_VALUE, kt as isAuxiliaryFailure, l as withUnchangedSecretPrincipalLists, m as withoutInjectedRemovals, mt as resourceTypeShown, nr as retryClassificationText, on as DynamicReferenceRegionAmbiguousError, or as markNonRetryable, ot as ElapsedBudgetRegistry, qn as isReplayedNameCollisionFrom, rn as CrossAccountSecretRefusalError, sn as IntrinsicResolutionRefusalError, sr as markRedactedCause, st as monotonicNowMs, tn as CdkdError, tr as isTransientServerError, un as LockError, ut as compositeIdSeparatorRefusal, vt as createMaskedRetryLogger, w as withoutServerErrorRetries, wn as normalizeAwsError, wt as carryCreatedBeforeFailure, xn as SynthesisError, xt as maskerOrIdentity, yt as isSecretDerivedValue, z as deleteWithProtectionCompensation, zn as withheldTargetClause, zt as formatLockOwner } from "./iam-policy-targets-B61e5_Sk.js";
3
+ import { $ as protectedReplacementAdvice, $n as isAmbiguousOutcomeError, $t as credentialFingerprint, A as isQuotableImportValue, An as UNSHOWABLE_VALUE, At as isAuxiliaryMarkOf, B as isTerminalDeleteFailure, Bn as withheldTargetClause, Bt as recoveryCommandFlags, Ct as RETRY_AUXILIARY_OWNER, Dt as createdResourceIdentityBeforeFailure, E as assertRegionMatch, Et as createdBeforeFailure, F as resolveExplicitPhysicalId, Fn as physicalIdShownBesideCommand, G as runDeleteAttempt, Gn as isNameCollisionErrorFrom, H as observeThenDisableProtection, Ht as CUSTOM_RESOURCE_RESPONSE_PREFIX, In as plainOrDescribed, J as DELETION_PROTECTION_DOC_POINTER, Jn as isReplayedNameCollisionFrom, Kn as isNameCooldownError, L as ProtectionFlipRegistry, Ln as quotedOrDescribed, Lt as formatLockExpiry, M as quotedRemedyLogicalId, Mt as markCreatedBeforeFailure, N as refusalTypeShown, Nn as isInertUnquoted, Nt as UNREPRODUCIBLE_LOCK_CLAUSE, On as SHELL_ACTIVE, Ot as hasCreatedBeforeFailure, Pn as pasteableCommand, Pt as buildForceUnlockCommand, Q as pasteableAwsCommand, Qn as isAmbiguousCcHandlerErrorCode, Qt as clientDefaultsFor, R as compensateProtectionFlip, Rn as shellBoundedDisplay, Rt as formatLockOperation, Sn as SynthesisError, St as withDerivedNameMasks, T as RESOURCE_NOT_FOUND, Tn as normalizeAwsError, U as protectionFlipKey, Un as isUpdateUnsupportedError, Ut as DEFAULT_STATE_PREFIX, Vt as sanitizeRecoveryValue, Wn as hasReplayMayCollide, Wt as injectiveKey, Xn as markReplayMayCollide, Xt as ambientClientDefaults, Yt as withPasteableAwsProfile, Zn as formatRetryClassificationSignals, Zt as ambientCredentialConfig, _n as ResourceTimeoutError, _t as createMaskedLogSinks, an as DependencyError, ar as isMarkedNonRetryable, at as startInterruptWatch, bt as maskDeep, cn as IntrinsicResolutionRefusalError, cr as markRedactedCause, ct as canonicalizeRoute53QueryName, d as prepareRemovalForUpdate, dn as LockError, en as AccountIdUnavailableError, er as isIamPropagationError, f as removalWarning, fn as MacroExpansionError, ft as logicalIdShown, gn as ProvisioningError, gt as stateOrphanRecordRemedy, ht as segmentAfterAnchor, i as holdsSecretDerivedEntry, in as CrossAccountSecretRefusalError, it as isInterruptedWaitError, j as normalizeAwsTagsToCfn, jn as commandHole, jt as markAuxiliaryFailure, kn as SHELL_ACTIVE_WHY, kt as isAuxiliaryFailure, l as withUnchangedSecretPrincipalLists, lr as wrapMaskedAwsError, m as withoutInjectedRemovals, mt as resourceTypeShown, nn as CdkdError, nr as isTransientServerError, or as isThrottlingError, ot as ElapsedBudgetRegistry, pn as MalformedProducerRecordRefusalError, qn as isRecreateRetryableError, qt as producerRecordKey, rr as retryClassificationText, sn as DynamicReferenceRegionAmbiguousError, sr as markNonRetryable, st as monotonicNowMs, tn as AssetError, tr as isRetryableTransientError, ut as compositeIdSeparatorRefusal, vn as ResourceUpdateNotSupportedError, vt as createMaskedRetryLogger, w as withoutServerErrorRetries, wt as carryCreatedBeforeFailure, xn as StateError, xt as maskerOrIdentity, yt as isSecretDerivedValue, z as deleteWithProtectionCompensation, zn as shellQuote, zt as formatLockOwner } from "./iam-policy-targets-Cz2tPxXk.js";
4
4
  import { a as setAssumedRoleCredentials, i as getPreAssumeEnvCredentials, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, s as ambientRegion, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-D0q0z4dG.js";
5
- import { t as getCdkdVersion } from "./version-B0JEGOLV.js";
5
+ import { t as getCdkdVersion } from "./version-B__YmKBy.js";
6
6
  import { createHash, randomUUID } from "node:crypto";
7
7
  import { AsyncLocalStorage } from "node:async_hooks";
8
8
  import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -4937,14 +4937,16 @@ function parseCreateTokenLedger(body) {
4937
4937
  for (const [logicalId, entry] of Object.entries(rawSent)) if (isSent(entry)) sent[logicalId] = {
4938
4938
  base: entry.base,
4939
4939
  token: entry.token,
4940
- firstSentAt: entry.firstSentAt
4940
+ firstSentAt: entry.firstSentAt,
4941
+ ...typeof entry.failedAt === "number" && Number.isFinite(entry.failedAt) && { failedAt: entry.failedAt }
4941
4942
  };
4942
4943
  }
4943
4944
  return {
4944
4945
  ledgerVersion: 1,
4945
4946
  nonce: doc["nonce"],
4946
4947
  sent,
4947
- ...doc["stateRecorded"] === true && { stateRecorded: true }
4948
+ ...doc["stateRecorded"] === true && { stateRecorded: true },
4949
+ ...typeof doc["abandonedAt"] === "number" && Number.isFinite(doc["abandonedAt"]) && { abandonedAt: doc["abandonedAt"] }
4948
4950
  };
4949
4951
  }
4950
4952
  const stateSavedObserver = new AsyncLocalStorage();
@@ -4955,6 +4957,409 @@ async function notifyStateSaved(stackName, region) {
4955
4957
  await stateSavedObserver.getStore()?.(stackName, region);
4956
4958
  }
4957
4959
 
4960
+ //#endregion
4961
+ //#region src/state/cross-prefix-stack-scan.ts
4962
+ function journalCanOwnResources(journal) {
4963
+ return recordCanOwnResources({ resources: {} }, journal);
4964
+ }
4965
+ function recordCanOwnResources(record, journal) {
4966
+ const resources = record.resources;
4967
+ if (resources === null || typeof resources !== "object" || Array.isArray(resources)) return true;
4968
+ if (Object.keys(resources).length > 0) return true;
4969
+ const orphans = record.orphans;
4970
+ if (orphans !== void 0 && (!Array.isArray(orphans) || orphans.length > 0)) return true;
4971
+ if (journal === null) return false;
4972
+ const segments = journal.segments;
4973
+ if (!Array.isArray(segments)) return true;
4974
+ return segments.some((seg) => {
4975
+ if (seg === null || typeof seg !== "object" || Array.isArray(seg)) return true;
4976
+ const ops = seg.operations;
4977
+ if (ops !== void 0 && (!Array.isArray(ops) || ops.length > 0)) return true;
4978
+ const failed = seg.failedOperations;
4979
+ if (failed === void 0) return false;
4980
+ if (!Array.isArray(failed)) return true;
4981
+ return failed.some((op) => op === null || typeof op !== "object" || op.physicalId !== void 0);
4982
+ });
4983
+ }
4984
+ var CrossPrefixReadError = class extends Error {
4985
+ key;
4986
+ cause;
4987
+ constructor(key, cause) {
4988
+ super(`reading ${displayIdent(key, { maxCodePoints: 1024 })} failed (${errorName$1(cause, "an unknown error")})`);
4989
+ this.name = "CrossPrefixReadError";
4990
+ this.key = key;
4991
+ this.cause = cause;
4992
+ }
4993
+ };
4994
+ const PROBE_CONCURRENCY = 50;
4995
+ function unwrapCause(error) {
4996
+ return error instanceof CrossPrefixReadError ? error.cause : error;
4997
+ }
4998
+ function isNotImplemented(error) {
4999
+ const name = error?.name;
5000
+ const status = error?.$metadata?.httpStatusCode;
5001
+ return name === "NotImplemented" || status === 501;
5002
+ }
5003
+ function isAccessDenied(error) {
5004
+ let current = error;
5005
+ for (let depth = 0; depth < 5 && current !== null && typeof current === "object"; depth++) {
5006
+ const name = current.name;
5007
+ if (name === "AccessDenied" || name === "Forbidden" || name === "AllAccessDisabled") return true;
5008
+ if (current.$metadata?.httpStatusCode === 403) return true;
5009
+ current = current.cause;
5010
+ }
5011
+ return false;
5012
+ }
5013
+ function candidatePrefixPasses(segments, own) {
5014
+ const seen = /* @__PURE__ */ new Set([own]);
5015
+ const pass = (names) => {
5016
+ const out = [];
5017
+ for (const name of names) {
5018
+ if (seen.has(name)) continue;
5019
+ seen.add(name);
5020
+ out.push(name);
5021
+ }
5022
+ return out;
5023
+ };
5024
+ return [pass(segments), pass(segments.map((p) => `${p}/`))];
5025
+ }
5026
+ async function scanOtherPrefixesForStack(target, stackName, region) {
5027
+ let segments;
5028
+ try {
5029
+ segments = await target.listTopLevelPrefixes();
5030
+ } catch (error) {
5031
+ return isAccessDenied(error) ? {
5032
+ kind: "denied",
5033
+ error,
5034
+ stage: "list"
5035
+ } : {
5036
+ kind: "failed",
5037
+ error
5038
+ };
5039
+ }
5040
+ const found = [];
5041
+ const stale = [];
5042
+ let denied;
5043
+ let failed;
5044
+ const probeAll = async (candidates) => {
5045
+ let next = 0;
5046
+ const worker = async () => {
5047
+ while (found.length === 0 && next < candidates.length) {
5048
+ const prefix = candidates[next++];
5049
+ try {
5050
+ const held = await target.recordUnderPrefix(prefix, stackName, region);
5051
+ if (held === "holder") found.push(prefix);
5052
+ else if (held === "empty") stale.push(prefix);
5053
+ } catch (error) {
5054
+ if (isAccessDenied(error)) denied ??= error;
5055
+ else failed ??= error;
5056
+ }
5057
+ }
5058
+ };
5059
+ await Promise.all(Array.from({ length: Math.min(50, candidates.length) }, () => worker()));
5060
+ };
5061
+ const [segmentsThemselves, twins] = candidatePrefixPasses(segments, target.prefix);
5062
+ await probeAll([...segmentsThemselves, ...twins]);
5063
+ const extra = stale.length > 0 ? { stale } : {};
5064
+ if (found.length > 0) return {
5065
+ kind: "found",
5066
+ prefixes: found,
5067
+ ...extra
5068
+ };
5069
+ if (failed !== void 0) return {
5070
+ kind: "failed",
5071
+ error: failed,
5072
+ ...extra
5073
+ };
5074
+ if (denied !== void 0) return {
5075
+ kind: "denied",
5076
+ error: denied,
5077
+ stage: "probe",
5078
+ ...extra
5079
+ };
5080
+ return {
5081
+ kind: "clear",
5082
+ ...extra
5083
+ };
5084
+ }
5085
+ function withSharedListing(target) {
5086
+ let listing;
5087
+ let inFlight = 0;
5088
+ let seq = 0;
5089
+ let prestarted = 0;
5090
+ const keyOf = (stackName, region) => JSON.stringify([stackName, region]);
5091
+ const ranks = /* @__PURE__ */ new Map();
5092
+ const rankOf = (stackName, region) => ranks.get(keyOf(stackName, region)) ?? Number.MAX_SAFE_INTEGER;
5093
+ const heap = [];
5094
+ const before = (a, b) => a.rank < b.rank || a.rank === b.rank && a.seq < b.seq;
5095
+ const swap = (i, j) => {
5096
+ const t = heap[i];
5097
+ heap[i] = heap[j];
5098
+ heap[j] = t;
5099
+ };
5100
+ const siftUp = (i) => {
5101
+ while (i > 0) {
5102
+ const parent = i - 1 >> 1;
5103
+ if (!before(heap[i], heap[parent])) return;
5104
+ swap(i, parent);
5105
+ i = parent;
5106
+ }
5107
+ };
5108
+ const siftDown = (i) => {
5109
+ for (;;) {
5110
+ const l = 2 * i + 1;
5111
+ const r = l + 1;
5112
+ let best = i;
5113
+ if (l < heap.length && before(heap[l], heap[best])) best = l;
5114
+ if (r < heap.length && before(heap[r], heap[best])) best = r;
5115
+ if (best === i) return;
5116
+ swap(i, best);
5117
+ i = best;
5118
+ }
5119
+ };
5120
+ const popBest = () => {
5121
+ const top = heap[0];
5122
+ const last = heap.pop();
5123
+ if (heap.length > 0) {
5124
+ heap[0] = last;
5125
+ siftDown(0);
5126
+ }
5127
+ return top;
5128
+ };
5129
+ const admitBest = () => {
5130
+ while (inFlight < 50 && heap.length > 0) {
5131
+ inFlight++;
5132
+ popBest().resolve();
5133
+ }
5134
+ };
5135
+ const acquire = async (stackName, region) => {
5136
+ const rank = rankOf(stackName, region);
5137
+ if (inFlight < 50 && (heap.length === 0 || heap[0].rank >= rank)) {
5138
+ inFlight++;
5139
+ return;
5140
+ }
5141
+ const key = keyOf(stackName, region);
5142
+ await new Promise((resolve) => {
5143
+ heap.push({
5144
+ key,
5145
+ rank,
5146
+ seq: seq++,
5147
+ resolve
5148
+ });
5149
+ siftUp(heap.length - 1);
5150
+ });
5151
+ };
5152
+ const release = () => {
5153
+ inFlight--;
5154
+ admitBest();
5155
+ };
5156
+ return {
5157
+ prefix: target.prefix,
5158
+ rank: (stackName, region, when) => {
5159
+ const key = keyOf(stackName, region);
5160
+ if (when === "prestart") {
5161
+ if (!ranks.has(key)) ranks.set(key, prestarted++);
5162
+ return;
5163
+ }
5164
+ if (ranks.get(key) === -1) return;
5165
+ ranks.set(key, -1);
5166
+ let moved = false;
5167
+ for (const w of heap) if (w.key === key) {
5168
+ w.rank = -1;
5169
+ moved = true;
5170
+ }
5171
+ if (moved) for (let i = (heap.length >> 1) - 1; i >= 0; i--) siftDown(i);
5172
+ },
5173
+ listTopLevelPrefixes: () => listing ??= target.listTopLevelPrefixes(),
5174
+ recordUnderPrefix: async (prefix, stackName, region) => {
5175
+ await acquire(stackName, region);
5176
+ try {
5177
+ return await target.recordUnderPrefix(prefix, stackName, region);
5178
+ } finally {
5179
+ release();
5180
+ }
5181
+ }
5182
+ };
5183
+ }
5184
+ var CrossPrefixScanCache = class {
5185
+ target;
5186
+ scans = /* @__PURE__ */ new Map();
5187
+ constructor(target) {
5188
+ this.target = withSharedListing(target);
5189
+ }
5190
+ full(stackName, region, when = "now") {
5191
+ this.target.rank(stackName, region, when);
5192
+ const key = JSON.stringify([stackName, region]);
5193
+ let scan = this.scans.get(key);
5194
+ if (scan === void 0) {
5195
+ scan = scanOtherPrefixesForStack(this.target, stackName, region);
5196
+ this.scans.set(key, scan);
5197
+ }
5198
+ return scan;
5199
+ }
5200
+ };
5201
+ const STACK_UNDER_OTHER_PREFIX = "STACK_UNDER_OTHER_STATE_PREFIX";
5202
+ const UNSUPPORTED_SENTENCE = "A stack name is unique per account and region, as in CloudFormation: deploying one stack name under two state prefixes is unsupported, because both deployments use the same cdkd-generated resource names, and a failed deploy, a destroy, or a deploy that deletes or replaces of either one deletes resources the other's record also names.";
5203
+ function subjectText(s) {
5204
+ return `${displayStackName(s.stackName)} (${displayIdent(s.region)})`;
5205
+ }
5206
+ const LISTED_PREFIXES = 5;
5207
+ function prefixesText(prefixes) {
5208
+ const shown = prefixes.slice(0, LISTED_PREFIXES).map((p) => displayIdent(p, { listMember: true })).join(", ");
5209
+ const more = prefixes.length - LISTED_PREFIXES;
5210
+ return more > 0 ? `${shown} and ${more} more` : shown;
5211
+ }
5212
+ function stateCommand(verb, s, prefix) {
5213
+ const account = recoveryCommandFlags({
5214
+ profile: s.recovery?.profile,
5215
+ stateBucket: s.recovery?.stateBucket ?? s.bucket
5216
+ });
5217
+ return pasteableCommand(`cdkd state ${verb}`, [
5218
+ {
5219
+ value: s.stackName,
5220
+ hole: "stack"
5221
+ },
5222
+ {
5223
+ flag: "--stack-region",
5224
+ value: s.region,
5225
+ hole: "region"
5226
+ },
5227
+ {
5228
+ flag: "--state-prefix",
5229
+ value: prefix,
5230
+ hole: "prefix"
5231
+ }
5232
+ ], account.flags).command;
5233
+ }
5234
+ function releaseRemedy(s, prefixes, destroyToo) {
5235
+ const first = prefixes[0];
5236
+ if (prefixes.length === 1 && destroyToo) return `\`${stateCommand("destroy", s, first)}\` deletes its resources and its record, \`${stateCommand("orphan", s, first)}\` drops only its record.`;
5237
+ if (prefixes.length === 1) return `drop the other record with \`cdkd state orphan\` (it removes only the record, never a resource): \`${stateCommand("orphan", s, first)}\`.`;
5238
+ return `release each other record with \`cdkd state orphan\` (it removes only the record, never a resource; a destroy under one of them is refused while another still records the stack, and the check stops at the first ones it finds, so re-run it until none is named), e.g. \`${stateCommand("orphan", s, first)}\`, then the same with each other prefix.`;
5239
+ }
5240
+ const VERB = {
5241
+ deploy: "deploy",
5242
+ destroy: "destroy",
5243
+ rollback: "roll back",
5244
+ "deploy-destructive": "deploy",
5245
+ "deploy-late-replace": "replace a resource of"
5246
+ };
5247
+ const NOTHING = {
5248
+ deploy: "No resource of this stack was created.",
5249
+ destroy: "Nothing was deleted.",
5250
+ rollback: "Nothing was reverted or deleted.",
5251
+ "deploy-destructive": "No resource of this stack was changed.",
5252
+ "deploy-late-replace": "That resource is kept, so the new value is not applied; the rest of the deploy goes on."
5253
+ };
5254
+ function deployUnderOtherPrefixMessage(s, prefixes) {
5255
+ return `Refusing to deploy stack ${subjectText(s)}: it is already recorded under another state prefix of bucket ${displayIdent(s.bucket)} (${prefixesText(prefixes)}). ${UNSUPPORTED_SENTENCE} ${NOTHING.deploy} Either deploy it under that prefix (pass the same --state-prefix it was deployed with), give this stack another name, or, if the other deployment is no longer wanted, ${prefixes.length === 1 ? "remove it first: " : ""}` + releaseRemedy(s, prefixes, true);
5256
+ }
5257
+ function destroyUnderOtherPrefixMessage(s, prefixes, action = "destroy") {
5258
+ return `Refusing to ${VERB[action]} stack ${subjectText(s)}: it is also recorded under another state prefix of bucket ${displayIdent(s.bucket)} (${prefixesText(prefixes)}). ${UNSUPPORTED_SENTENCE} ${NOTHING[action]} Keep one record per stack name and region: once you have confirmed which record describes the deployment you want to keep, ${releaseRemedy(s, prefixes, false)} Then re-run.`;
5259
+ }
5260
+ function destructiveDeployUnderOtherPrefixMessage(s, prefixes, action = "deploy-destructive") {
5261
+ return `${action === "deploy-late-replace" ? `Refusing to replace a resource of stack ${subjectText(s)} (a replacement this deploy found only on reading the resource back)` : `Refusing to deploy stack ${subjectText(s)}: this deploy deletes or replaces resources`}, and the stack is also recorded under another state prefix of bucket ${displayIdent(s.bucket)} (${prefixesText(prefixes)}). ${UNSUPPORTED_SENTENCE} ${NOTHING[action]} Keep one record per stack name and region: once you have confirmed which record describes the deployment you want to keep, ${releaseRemedy(s, prefixes, false)} Then re-run.`;
5262
+ }
5263
+ function staleRecordNotice(s, prefixes) {
5264
+ return `Note: bucket ${displayIdent(s.bucket)} also holds a record of stack ${subjectText(s)} under another state prefix (${prefixesText(prefixes)}) that owns no resource -- the leftover of a failed first deploy. It does not block this command; remove it with \`${stateCommand("orphan", s, prefixes[0])}\`${prefixes.length > 1 ? ", and the same with each other prefix" : ""}.`;
5265
+ }
5266
+ function errorName$1(error, fallback) {
5267
+ const subject = error instanceof CrossPrefixReadError ? error.cause : error;
5268
+ return subject !== null && typeof subject === "object" && typeof subject.name === "string" ? displaySafe(subject.name, { asciiOnly: true }) : fallback;
5269
+ }
5270
+ function failedKeyText(error) {
5271
+ return error instanceof CrossPrefixReadError ? ` reading s3 object ${displayIdent(error.key, { maxCodePoints: 1024 })}` : "";
5272
+ }
5273
+ function crossPrefixDeniedWarning(s, error, stage = "probe") {
5274
+ if (stage === "registry" && isNotImplemented(unwrapCause(error))) return `Could not use the stack registry for stack ${subjectText(s)}: the state bucket's endpoint does not implement a conditional write${failedKeyText(error)} (${errorName$1(error, "NotImplemented")}), so the bucket's other state prefixes were scanned instead, which is slower. Continuing.`;
5275
+ if (stage === "registry") return `Could not use the stack registry for stack ${subjectText(s)}: S3 refused${failedKeyText(error)} (${errorName$1(error, "AccessDenied")}), so the bucket's other state prefixes were scanned instead, which is slower. Grant s3:GetObject, s3:PutObject and s3:DeleteObject on ${displayIdent(s.bucket)}/_cdkd-registry/* to use it. Continuing.`;
5276
+ const what = stage === "list" ? `S3 refused to list bucket ${displayIdent(s.bucket)}` : `S3 refused a read under another state prefix of bucket ${displayIdent(s.bucket)}`;
5277
+ return `Could not check the other state prefixes for stack ${subjectText(s)}: ${what} (${errorName$1(error, "AccessDenied")}). Continuing. Deploying one stack name under two state prefixes in one account and region is unsupported.`;
5278
+ }
5279
+ function inProgressUnderOtherPrefixMessage(s, action, prefix) {
5280
+ const account = recoveryCommandFlags({
5281
+ profile: s.recovery?.profile,
5282
+ stateBucket: s.recovery?.stateBucket ?? s.bucket
5283
+ });
5284
+ const unlock = pasteableCommand("cdkd force-unlock", [
5285
+ {
5286
+ value: s.stackName,
5287
+ hole: "stack"
5288
+ },
5289
+ {
5290
+ flag: "--stack-region",
5291
+ value: s.region,
5292
+ hole: "region"
5293
+ },
5294
+ {
5295
+ flag: "--state-prefix",
5296
+ value: prefix,
5297
+ hole: "prefix"
5298
+ }
5299
+ ], account.flags).command;
5300
+ return `Refusing to ${VERB[action]} stack ${subjectText(s)}: bucket ${displayIdent(s.bucket)} assigns it to state prefix ${displayIdent(prefix)}, which records nothing for it yet but holds its lock, so a deploy there may be in progress. ${UNSUPPORTED_SENTENCE} ${NOTHING[action]} Re-run once that deploy has finished; if none is running, remove its lock with \`${unlock}\` and re-run.`;
5301
+ }
5302
+ function crossPrefixFailedMessage(s, action, error) {
5303
+ return `Refusing to ${VERB[action]} stack ${subjectText(s)}: cdkd could not check whether the bucket ${displayIdent(s.bucket)} records it under another state prefix (${errorName$1(error, "an unknown error")}${failedKeyText(error)}). ${UNSUPPORTED_SENTENCE} ${NOTHING[action]} Re-run once the check can succeed.`;
5304
+ }
5305
+ function applyCrossPrefixScan(result, s, action, warn, info) {
5306
+ if (result.kind !== "in-progress" && result.stale !== void 0 && result.stale.length > 0) info?.(staleRecordNotice(s, result.stale));
5307
+ switch (result.kind) {
5308
+ case "clear": return;
5309
+ case "denied":
5310
+ warn(crossPrefixDeniedWarning(s, result.error, result.stage));
5311
+ return;
5312
+ case "found": {
5313
+ const message = action === "deploy" ? deployUnderOtherPrefixMessage(s, result.prefixes) : action === "deploy-destructive" || action === "deploy-late-replace" ? destructiveDeployUnderOtherPrefixMessage(s, result.prefixes, action) : destroyUnderOtherPrefixMessage(s, result.prefixes, action);
5314
+ throw new CdkdError(message, STACK_UNDER_OTHER_PREFIX);
5315
+ }
5316
+ case "failed": throw new CdkdError(crossPrefixFailedMessage(s, action, result.error), STACK_UNDER_OTHER_PREFIX);
5317
+ case "in-progress": throw new CdkdError(inProgressUnderOtherPrefixMessage(s, action, result.prefix), STACK_UNDER_OTHER_PREFIX);
5318
+ }
5319
+ }
5320
+
5321
+ //#endregion
5322
+ //#region src/state/earlier-state-versions.ts
5323
+ async function readEarlierStateResources(s3, bucket, owner, key, max) {
5324
+ const versions = ((await s3.send(new ListObjectVersionsCommand({
5325
+ Bucket: bucket,
5326
+ ...owner,
5327
+ Prefix: key,
5328
+ EncodingType: "url",
5329
+ MaxKeys: max + 1
5330
+ }))).Versions ?? []).filter((v) => decodeListingKey(v.Key) === key && typeof v.VersionId === "string").sort((a, b) => (b.LastModified?.getTime() ?? 0) - (a.LastModified?.getTime() ?? 0)).slice(0, max);
5331
+ return (await Promise.all(versions.map(async (v) => {
5332
+ const body = await (await s3.send(new GetObjectCommand({
5333
+ Bucket: bucket,
5334
+ ...owner,
5335
+ Key: key,
5336
+ VersionId: v.VersionId
5337
+ }))).Body?.transformToString();
5338
+ if (body === void 0) return void 0;
5339
+ try {
5340
+ const parsed = JSON.parse(body);
5341
+ if (parsed === null || typeof parsed.resources !== "object" || parsed.resources === null) return;
5342
+ return {
5343
+ resources: parsed.resources,
5344
+ ...v.LastModified instanceof Date && { writtenAt: v.LastModified.getTime() }
5345
+ };
5346
+ } catch {
5347
+ return;
5348
+ }
5349
+ }))).filter((r) => r !== void 0);
5350
+ }
5351
+
5352
+ //#endregion
5353
+ //#region src/state/retained-time.ts
5354
+ var RetainedTimeUnconfirmedError = class extends Error {
5355
+ cause;
5356
+ constructor(cause) {
5357
+ super("the kept-resource record was written, but its time could not be confirmed from S3");
5358
+ this.name = "RetainedTimeUnconfirmedError";
5359
+ this.cause = cause;
5360
+ }
5361
+ };
5362
+
4958
5363
  //#endregion
4959
5364
  //#region src/utils/bucket-region-client.ts
4960
5365
  async function rebuildClientForBucketRegion(client, bucket, opts = {}) {
@@ -5014,12 +5419,14 @@ function legacyProbeBelongsTo(probe, region) {
5014
5419
  case "unreadable": return false;
5015
5420
  }
5016
5421
  }
5017
- var S3StateBackend = class {
5422
+ const KEPT_AT_CLOCK_TOLERANCE_MS = 2e3;
5423
+ var S3StateBackend = class S3StateBackend {
5018
5424
  logger = getLogger().child("S3StateBackend");
5019
5425
  s3Client;
5020
5426
  config;
5021
5427
  clientOpts;
5022
5428
  clientResolved = false;
5429
+ scanClient;
5023
5430
  resolveInFlight = null;
5024
5431
  constructor(s3Client, config, clientOpts = {}) {
5025
5432
  this.s3Client = s3Client;
@@ -5028,6 +5435,19 @@ var S3StateBackend = class {
5028
5435
  }
5029
5436
  destroyClient() {
5030
5437
  this.s3Client.destroy();
5438
+ this.scanClient?.destroy();
5439
+ }
5440
+ async clientForScan() {
5441
+ await this.ensureClientForBucket();
5442
+ if (this.scanClient !== void 0) return this.scanClient;
5443
+ if (!(this.s3Client instanceof S3Client)) return this.s3Client;
5444
+ const region = await this.s3Client.config.region();
5445
+ this.scanClient = new S3Client({
5446
+ ...awsClientDefaults(this.clientOpts.profile ? { profile: this.clientOpts.profile } : {}),
5447
+ region,
5448
+ credentials: this.s3Client.config.credentials
5449
+ });
5450
+ return this.scanClient;
5031
5451
  }
5032
5452
  get prefix() {
5033
5453
  return this.config.prefix;
@@ -5295,6 +5715,87 @@ var S3StateBackend = class {
5295
5715
  throw new StateError(`Failed to list stacks: ${normalized.message}`, normalized);
5296
5716
  }
5297
5717
  }
5718
+ async listTopLevelPrefixes() {
5719
+ const client = await this.clientForScan();
5720
+ const out = [];
5721
+ let continuationToken;
5722
+ do {
5723
+ const response = await client.send(new ListObjectsV2Command({
5724
+ Bucket: this.config.bucket,
5725
+ ...await this.ownerParam(),
5726
+ Delimiter: "/",
5727
+ EncodingType: "url",
5728
+ ...continuationToken && { ContinuationToken: continuationToken }
5729
+ }));
5730
+ for (const cp of response.CommonPrefixes ?? []) {
5731
+ let decoded;
5732
+ try {
5733
+ decoded = decodeListingKey(cp.Prefix);
5734
+ } catch {
5735
+ this.logger.debug(safeMsg`Skipping a top-level prefix of bucket ${this.config.bucket} that is not valid URL encoding: ${cp.Prefix ?? ""}`);
5736
+ continue;
5737
+ }
5738
+ if (decoded === void 0 || !decoded.endsWith("/")) continue;
5739
+ if (decoded === `${"_cdkd-registry"}/`) continue;
5740
+ out.push(decoded.slice(0, -1));
5741
+ }
5742
+ continuationToken = response.IsTruncated ? response.NextContinuationToken : void 0;
5743
+ } while (continuationToken);
5744
+ return out;
5745
+ }
5746
+ async recordUnderPrefix(prefix, stackName, region) {
5747
+ const client = await this.clientForScan();
5748
+ const sibling = new S3StateBackend(client, {
5749
+ ...this.config,
5750
+ prefix
5751
+ }, this.clientOpts);
5752
+ sibling.clientResolved = true;
5753
+ const stackDir = `${prefix}/${stackName}/`;
5754
+ let listed;
5755
+ try {
5756
+ listed = await client.send(new ListObjectsV2Command({
5757
+ Bucket: this.config.bucket,
5758
+ ...await this.ownerParam(),
5759
+ Prefix: stackDir,
5760
+ MaxKeys: 1,
5761
+ EncodingType: "url"
5762
+ }));
5763
+ } catch (error) {
5764
+ throw new CrossPrefixReadError(stackDir, error);
5765
+ }
5766
+ if ((listed.KeyCount ?? listed.Contents?.length ?? 0) === 0) return "absent";
5767
+ const read = async (key) => {
5768
+ let body;
5769
+ try {
5770
+ body = await sibling.getRawObject(key);
5771
+ } catch (error) {
5772
+ throw new CrossPrefixReadError(key, error);
5773
+ }
5774
+ if (body === null) return null;
5775
+ if (body.trim() === "") throw new CrossPrefixReadError(key, Object.assign(/* @__PURE__ */ new Error("empty object"), { name: "EmptyObject" }));
5776
+ let value;
5777
+ try {
5778
+ value = JSON.parse(body);
5779
+ } catch (error) {
5780
+ throw new CrossPrefixReadError(key, error);
5781
+ }
5782
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new CrossPrefixReadError(key, /* @__PURE__ */ new TypeError("not a JSON object"));
5783
+ return value;
5784
+ };
5785
+ const [scoped, legacy, journal] = await Promise.all([
5786
+ read(sibling.getStateKey(stackName, region)),
5787
+ read(sibling.getLegacyStateKey(stackName)),
5788
+ read(sibling.getRollbackJournalKey(stackName, region))
5789
+ ]);
5790
+ let record;
5791
+ if (scoped !== null) record = scoped;
5792
+ else if (legacy !== null) {
5793
+ const bodyRegion = legacy["region"];
5794
+ if (!bodyRegion || bodyRegion === region) record = legacy;
5795
+ }
5796
+ if (record === void 0 && journal === null) return "absent";
5797
+ return recordCanOwnResources(record ?? { resources: {} }, journal) ? "holder" : "empty";
5798
+ }
5298
5799
  async putRawObject(key, body, contentType = "application/json") {
5299
5800
  await this.ensureClientForBucket();
5300
5801
  await this.s3Client.send(new PutObjectCommand({
@@ -5306,6 +5807,176 @@ var S3StateBackend = class {
5306
5807
  ContentType: contentType
5307
5808
  }));
5308
5809
  }
5810
+ async earlierStateResources(stackName, region, max = 10) {
5811
+ await this.ensureClientForBucket();
5812
+ return readEarlierStateResources(this.s3Client, this.config.bucket, await this.ownerParam(), this.getStateKey(stackName, region), max);
5813
+ }
5814
+ getRetainedKey(stackName, region) {
5815
+ return `${this.config.prefix}/${stackName}/${region}/retained.json`;
5816
+ }
5817
+ async loadRetainedResources(stackName, region) {
5818
+ return await this.loadRetainedRecord(stackName, region) ?? [];
5819
+ }
5820
+ async loadRetainedRecord(stackName, region) {
5821
+ const key = this.getRetainedKey(stackName, region);
5822
+ let body;
5823
+ try {
5824
+ body = await this.getRawObject(key);
5825
+ } catch (error) {
5826
+ throw new CrossPrefixReadError(key, error);
5827
+ }
5828
+ if (body === null) return null;
5829
+ const entries = parseRetainedResources(body);
5830
+ if (entries === void 0) throw new CrossPrefixReadError(key, Object.assign(/* @__PURE__ */ new Error("not a retained-resource record"), { name: "MalformedRecord" }));
5831
+ return entries;
5832
+ }
5833
+ async ensureRetainedTombstone(stackName, region) {
5834
+ await this.ensureClientForBucket();
5835
+ const key = this.getRetainedKey(stackName, region);
5836
+ const put = async () => this.s3Client.send(new PutObjectCommand({
5837
+ Bucket: this.config.bucket,
5838
+ ...await this.ownerParam(),
5839
+ Key: key,
5840
+ Body: JSON.stringify({
5841
+ retainedVersion: 1,
5842
+ resources: []
5843
+ }),
5844
+ ContentType: "application/json",
5845
+ IfNoneMatch: "*"
5846
+ }));
5847
+ try {
5848
+ try {
5849
+ await put();
5850
+ } catch (error) {
5851
+ if (!isConditionalConflict(error)) throw error;
5852
+ await put();
5853
+ }
5854
+ } catch (error) {
5855
+ if (isConditionFailure(error) && !isConditionalConflict(error)) return;
5856
+ if (!isNotImplemented(error)) throw error;
5857
+ if (await this.loadRetainedRecord(stackName, region) === null) await this.saveRetainedResources(stackName, region, []);
5858
+ }
5859
+ }
5860
+ async saveRetainedResources(stackName, region, entries) {
5861
+ await this.ensureClientForBucket();
5862
+ const key = this.getRetainedKey(stackName, region);
5863
+ const put = (resources) => this.ownerParam().then((owner) => this.s3Client.send(new PutObjectCommand({
5864
+ Bucket: this.config.bucket,
5865
+ ...owner,
5866
+ Key: key,
5867
+ Body: JSON.stringify({
5868
+ retainedVersion: 1,
5869
+ resources
5870
+ }),
5871
+ ContentType: "application/json"
5872
+ })));
5873
+ const fresh = new Set(entries.filter((e) => e.keptAt === void 0));
5874
+ const provisional = Date.now();
5875
+ await put(entries.map((e) => fresh.has(e) ? {
5876
+ ...e,
5877
+ keptAt: provisional
5878
+ } : e));
5879
+ if (fresh.size === 0) return;
5880
+ try {
5881
+ const head = await this.s3Client.send(new HeadObjectCommand({
5882
+ Bucket: this.config.bucket,
5883
+ ...await this.ownerParam(),
5884
+ Key: key
5885
+ }));
5886
+ const keptAt = head.LastModified instanceof Date ? head.LastModified.getTime() : void 0;
5887
+ if (keptAt === void 0) throw new Error("S3 reported no LastModified");
5888
+ if (Math.abs(keptAt - provisional) <= KEPT_AT_CLOCK_TOLERANCE_MS) return;
5889
+ await put(entries.map((e) => fresh.has(e) ? {
5890
+ ...e,
5891
+ keptAt
5892
+ } : e));
5893
+ } catch (error) {
5894
+ throw new RetainedTimeUnconfirmedError(error);
5895
+ }
5896
+ }
5897
+ registryMarkerKey(stackName, region) {
5898
+ return `${REGISTRY_ROOT}/${region}/${stackName}.json`;
5899
+ }
5900
+ async getRegistryMarker(stackName, region) {
5901
+ await this.ensureClientForBucket();
5902
+ const key = this.registryMarkerKey(stackName, region);
5903
+ let body;
5904
+ let etag;
5905
+ try {
5906
+ const response = await this.s3Client.send(new GetObjectCommand({
5907
+ Bucket: this.config.bucket,
5908
+ ...await this.ownerParam(),
5909
+ Key: key
5910
+ }));
5911
+ body = await response.Body?.transformToString();
5912
+ etag = response.ETag;
5913
+ } catch (error) {
5914
+ if (isNoSuchKey(error) || error.name === "NotFound") return null;
5915
+ throw new CrossPrefixReadError(key, error);
5916
+ }
5917
+ const prefix = registryMarkerPrefix(body);
5918
+ if (prefix === void 0 || etag === void 0) throw new CrossPrefixReadError(key, Object.assign(/* @__PURE__ */ new Error("not a registry marker"), { name: "MalformedMarker" }));
5919
+ return {
5920
+ prefix,
5921
+ etag
5922
+ };
5923
+ }
5924
+ async claimRegistryMarker(stackName, region, ifMatch) {
5925
+ await this.ensureClientForBucket();
5926
+ const key = this.registryMarkerKey(stackName, region);
5927
+ try {
5928
+ await this.s3Client.send(new PutObjectCommand({
5929
+ Bucket: this.config.bucket,
5930
+ ...await this.ownerParam(),
5931
+ Key: key,
5932
+ Body: JSON.stringify({ prefix: this.config.prefix }),
5933
+ ContentType: "application/json",
5934
+ ...ifMatch === void 0 ? { IfNoneMatch: "*" } : { IfMatch: ifMatch }
5935
+ }));
5936
+ return "claimed";
5937
+ } catch (error) {
5938
+ if (isConditionFailure(error)) return "conflict";
5939
+ throw new CrossPrefixReadError(key, error);
5940
+ }
5941
+ }
5942
+ async releaseRegistryMarker(stackName, region, known) {
5943
+ const marker = known !== void 0 && (known === null || known.etag !== "") ? known : await this.getRegistryMarker(stackName, region);
5944
+ if (marker === null) return "absent";
5945
+ if (marker.prefix !== this.config.prefix) return "elsewhere";
5946
+ const key = this.registryMarkerKey(stackName, region);
5947
+ try {
5948
+ await this.s3Client.send(new DeleteObjectCommand({
5949
+ Bucket: this.config.bucket,
5950
+ ...await this.ownerParam(),
5951
+ Key: key,
5952
+ IfMatch: marker.etag
5953
+ }));
5954
+ return "released";
5955
+ } catch (error) {
5956
+ if (isConditionFailure(error)) return "elsewhere";
5957
+ if (!isNotImplemented(error)) throw error;
5958
+ }
5959
+ const again = await this.getRegistryMarker(stackName, region);
5960
+ if (again === null) return "absent";
5961
+ if (again.prefix !== this.config.prefix || again.etag !== marker.etag) return "elsewhere";
5962
+ await this.s3Client.send(new DeleteObjectCommand({
5963
+ Bucket: this.config.bucket,
5964
+ ...await this.ownerParam(),
5965
+ Key: key
5966
+ }));
5967
+ return "released";
5968
+ }
5969
+ async lockUnderPrefix(prefix, stackName, region) {
5970
+ await this.ensureClientForBucket();
5971
+ const keys = [`${prefix}/${stackName}/${region}/lock.json`, `${prefix}/${stackName}/lock.json`];
5972
+ return (await Promise.all(keys.map(async (key) => {
5973
+ try {
5974
+ return await this.headObject(key);
5975
+ } catch (error) {
5976
+ throw new CrossPrefixReadError(key, error);
5977
+ }
5978
+ }))).some(Boolean);
5979
+ }
5309
5980
  async getRawObject(key) {
5310
5981
  await this.ensureClientForBucket();
5311
5982
  try {
@@ -5750,6 +6421,53 @@ var S3StateBackend = class {
5750
6421
  return parsed;
5751
6422
  }
5752
6423
  };
6424
+ function parseRetainedResources(body) {
6425
+ let value;
6426
+ try {
6427
+ value = JSON.parse(body);
6428
+ } catch {
6429
+ return;
6430
+ }
6431
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
6432
+ const doc = value;
6433
+ if (doc.retainedVersion !== 1 || !Array.isArray(doc.resources)) return void 0;
6434
+ const out = [];
6435
+ for (const entry of doc.resources) {
6436
+ const e = entry;
6437
+ if (typeof e?.logicalId === "string" && typeof e.resourceType === "string" && typeof e.physicalId === "string" && e.physicalId !== "") out.push({
6438
+ logicalId: e.logicalId,
6439
+ resourceType: e.resourceType,
6440
+ physicalId: e.physicalId,
6441
+ ...typeof e.keptAt === "number" && Number.isFinite(e.keptAt) && { keptAt: e.keptAt }
6442
+ });
6443
+ }
6444
+ return out;
6445
+ }
6446
+ const REGISTRY_ROOT = "_cdkd-registry";
6447
+ function registryMarkerPrefix(body) {
6448
+ if (body === void 0) return void 0;
6449
+ let value;
6450
+ try {
6451
+ value = JSON.parse(body);
6452
+ } catch {
6453
+ return;
6454
+ }
6455
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
6456
+ const prefix = value.prefix;
6457
+ if (typeof prefix !== "string" || prefix.length > 1024) return void 0;
6458
+ if (/[\u0000-\u001f\u007f<>]/.test(prefix)) return void 0;
6459
+ return prefix;
6460
+ }
6461
+ function isConditionalConflict(error) {
6462
+ const name = error?.name;
6463
+ const status = error?.$metadata?.httpStatusCode;
6464
+ return name === "ConditionalRequestConflict" || status === 409 && name !== "PreconditionFailed";
6465
+ }
6466
+ function isConditionFailure(error) {
6467
+ const name = error?.name;
6468
+ const status = error?.$metadata?.httpStatusCode;
6469
+ return name === "PreconditionFailed" || name === "ConditionalRequestConflict" || status === 412 || status === 409;
6470
+ }
5753
6471
  function isNoSuchKey(error) {
5754
6472
  if (error instanceof NoSuchKey) return true;
5755
6473
  return error?.name === "NoSuchKey";
@@ -5768,6 +6486,15 @@ function addSupersededIds(segment, ids) {
5768
6486
  const MAX_RENEWAL_INTERVAL_MS = 12e4;
5769
6487
  const RENEWAL_TTL_FRACTION = 4;
5770
6488
  const MIN_RENEWAL_INTERVAL_MS = 1e3;
6489
+ function renewalIntervalFor(ttlMs) {
6490
+ return Math.max(MIN_RENEWAL_INTERVAL_MS, Math.min(MAX_RENEWAL_INTERVAL_MS, Math.floor(ttlMs / RENEWAL_TTL_FRACTION)));
6491
+ }
6492
+ function abandonedRunHorizon(record) {
6493
+ if (record.lastModified === void 0) return void 0;
6494
+ const ttlMs = Number(record.info.expiresAt) - record.lastModified;
6495
+ const interval = Number.isFinite(ttlMs) && ttlMs > 0 ? renewalIntervalFor(ttlMs) : MAX_RENEWAL_INTERVAL_MS;
6496
+ return record.lastModified + interval;
6497
+ }
5771
6498
  const RELEASED_LOCK_REACQUIRE_LIMIT = 3;
5772
6499
  const LOCK_OBJECT_DESCRIPTION = "a stack lock heartbeat, which records the lock owner, its acquisition timestamp, its deadline and the operation name — no secret, but one row per renewal";
5773
6500
  function safeSegment(value) {
@@ -5791,6 +6518,7 @@ var LockManager = class {
5791
6518
  renewalIntervalMs;
5792
6519
  renewalDisabled;
5793
6520
  heldLocks = /* @__PURE__ */ new Map();
6521
+ abandonedLocks = /* @__PURE__ */ new Map();
5794
6522
  clientResolved = false;
5795
6523
  resolveInFlight = null;
5796
6524
  constructor(s3Client, config, options) {
@@ -5800,7 +6528,7 @@ var LockManager = class {
5800
6528
  if (!(ttlMinutes > 0) || !Number.isFinite(ttlMinutes)) throw new LockError(`Lock TTL must be a positive number of minutes (got ${String(ttlMinutes)}). A non-positive TTL makes every lock born expired, and the renewal interval clamps to its floor, so the heartbeat would spin one request per second for the life of the process.`);
5801
6529
  this.ttlMs = ttlMinutes * 60 * 1e3;
5802
6530
  this.renewalDisabled = options?.disableRenewal === true;
5803
- this.renewalIntervalMs = Math.max(MIN_RENEWAL_INTERVAL_MS, Math.min(MAX_RENEWAL_INTERVAL_MS, Math.floor(this.ttlMs / RENEWAL_TTL_FRACTION)));
6531
+ this.renewalIntervalMs = renewalIntervalFor(this.ttlMs);
5804
6532
  }
5805
6533
  async ownerParam() {
5806
6534
  return expectedOwnerParam(this.s3Client);
@@ -5889,6 +6617,8 @@ var LockManager = class {
5889
6617
  }
5890
6618
  const why = Number.isFinite(existing.info.expiresAt) ? "A live cdkd process renews its lock well inside the TTL, so this normally means the previous owner crashed or was suspended" : `Its expiresAt is not a finite number, which cdkd treats as already expired`;
5891
6619
  this.logger.warn(`Took over an EXPIRED lock for stack: ${safeSegment(stackName)} (${safeSegment(region)}, owner: ${formatLockOwner(existing.info.owner)}, ${formatLockExpiry(existing.info.expiresAt)}). ${why} -- if it is in fact still running, both processes are now writing to the same stack.`);
6620
+ const abandonedAt = abandonedRunHorizon(existing);
6621
+ if (abandonedAt !== void 0) this.abandonedLocks.set(key, abandonedAt);
5892
6622
  try {
5893
6623
  const retryEtag = await this.putLockObject(key, lockInfo);
5894
6624
  this.logger.debug(`Lock acquired for stack: ${stackRef(stackName, region)} after expired lock cleanup, owner: ${displaySafe(lockOwner)}`);
@@ -5959,7 +6689,8 @@ var LockManager = class {
5959
6689
  this.logger.debug(`Lock info for stack: ${shownStack}:`, lockInfo);
5960
6690
  return {
5961
6691
  info: lockInfo,
5962
- etag: response.ETag
6692
+ etag: response.ETag,
6693
+ ...response.LastModified instanceof Date && { lastModified: response.LastModified.getTime() }
5963
6694
  };
5964
6695
  } catch (error) {
5965
6696
  if (error instanceof NoSuchKey) {
@@ -6042,9 +6773,13 @@ var LockManager = class {
6042
6773
  sameLockIdentity(a, b) {
6043
6774
  return displaySafe(a.owner) === displaySafe(b.owner) && a.timestamp === b.timestamp;
6044
6775
  }
6045
- async forceReleaseLock(stackName, region) {
6776
+ abandonedRunEndedBy(stackName, region) {
6777
+ return this.abandonedLocks.get(this.getLockKey(stackName, region));
6778
+ }
6779
+ async forceReleaseLock(stackName, region, beforeDelete) {
6046
6780
  const where = stackRef(stackName, region);
6047
- const lockInfo = await this.getLockInfo(stackName, region).catch(() => null);
6781
+ const record = await this.getLockRecord(stackName, region).catch(() => null);
6782
+ const lockInfo = record?.info ?? null;
6048
6783
  this.logger.warn(lockInfo ? `Force releasing lock for stack: ${where}, owner: ${formatLockOwner(lockInfo.owner)}${lockInfo.operation ? `, operation: ${formatLockOperation(lockInfo.operation)}` : ""}, expired: ${this.isLockExpired(lockInfo)}` : `Force releasing lock for stack: ${where} (no lock body read — absent or unparseable; deleting the object either way, since a lock cdkd cannot read is a lock nothing else can clear)`);
6049
6784
  const key = this.getLockKey(stackName, region);
6050
6785
  const held = this.heldLocks.get(key);
@@ -6052,11 +6787,14 @@ var LockManager = class {
6052
6787
  this.stopRenewal(held);
6053
6788
  held.releasing = Promise.resolve();
6054
6789
  }
6790
+ const horizon = record ? abandonedRunHorizon(record) : void 0;
6791
+ if (horizon !== void 0 && beforeDelete !== void 0) await beforeDelete(horizon).catch(() => void 0);
6055
6792
  try {
6056
6793
  await this.deleteLock(stackName, region);
6057
6794
  } finally {
6058
6795
  await this.purgeLockVersions(key, "reap");
6059
6796
  }
6797
+ return horizon;
6060
6798
  }
6061
6799
  async deleteLock(stackName, region, etag) {
6062
6800
  await this.ensureClientForBucket();
@@ -11311,7 +12049,7 @@ const DERIVED_GENERATED_NAMES = {
11311
12049
  },
11312
12050
  "AWS::S3::Bucket": {
11313
12051
  file: "s3-bucket-provider.ts",
11314
- source: "generateResourceName(logicalId, {\n maxLength: 63,\n lowercase: true,\n allowedPattern: /[^a-z0-9.-]/g,\n })",
12052
+ source: "generateResourceName(logicalId, {\n maxLength: 63,\n lowercase: true,\n allowedPattern: /[^a-z0-9.-]/g,\n })",
11315
12053
  derive: (id) => generateResourceName(id, {
11316
12054
  maxLength: 63,
11317
12055
  lowercase: true,
@@ -22972,7 +23710,7 @@ var CloudControlProvider = class {
22972
23710
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
22973
23711
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
22974
23712
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
22975
- const { ASGProvider } = await import("./asg-provider-B3zcS87C.js");
23713
+ const { ASGProvider } = await import("./asg-provider-BaabA371.js");
22976
23714
  const delegateRegion = ambientRegion();
22977
23715
  let asgProvider = this.asgDeleteDelegates.get(delegateRegion);
22978
23716
  if (asgProvider === void 0) {
@@ -23078,7 +23816,7 @@ var CloudControlProvider = class {
23078
23816
  const family = SDK_DELETE_FAMILIES.get(resourceType);
23079
23817
  const cached = this.sdkDeleteDelegates.get(family);
23080
23818
  if (cached !== void 0 && cached.region === ccRegion) return cached.provider;
23081
- const provider = family === "rds" ? new (await (import("./rds-provider-BfQsw4hw.js"))).RDSProvider({ region: ccRegion }) : new (await (import("./neptune-provider-DF6Eh4Tl.js"))).NeptuneProvider({ region: ccRegion });
23819
+ const provider = family === "rds" ? new (await (import("./rds-provider-IBY6fDvN.js"))).RDSProvider({ region: ccRegion }) : new (await (import("./neptune-provider-5yc_bOJ-.js"))).NeptuneProvider({ region: ccRegion });
23082
23820
  this.sdkDeleteDelegates.set(family, {
23083
23821
  region: ccRegion,
23084
23822
  provider
@@ -25661,7 +26399,7 @@ const NESTED_REQUIRED = /* @__PURE__ */ new Map([
25661
26399
  ["ContainerDefinitions.LinuxParameters.Tmpfs", ["Size"]],
25662
26400
  ["ContainerDefinitions.LogConfiguration", ["LogDriver"]],
25663
26401
  ["ContainerDefinitions.LogConfiguration.SecretOptions", ["Name", "ValueFrom"]],
25664
- ["ContainerDefinitions.ResourceRequirements", ["Type", "Value"]],
26402
+ ["ContainerDefinitions.ResourceRequirements", ["Type"]],
25665
26403
  ["ContainerDefinitions.Secrets", ["Name", "ValueFrom"]],
25666
26404
  ["ContainerDefinitions.Ulimits", [
25667
26405
  "HardLimit",
@@ -27003,9 +27741,12 @@ var ProviderRegistry = class {
27003
27741
  this.providers.delete(resourceType);
27004
27742
  }
27005
27743
  getProviderFor(input) {
27744
+ const debug = (message) => {
27745
+ if (input.quiet !== true) this.logger.debug(message);
27746
+ };
27006
27747
  const { resourceType, properties, provisionedBy } = input;
27007
27748
  if (isCustomResource(resourceType)) {
27008
- this.logger.debug(`Using Custom Resource provider for ${resourceType}`);
27749
+ debug(`Using Custom Resource provider for ${resourceType}`);
27009
27750
  return {
27010
27751
  provider: this.customResourceProvider,
27011
27752
  provisionedBy: "sdk"
@@ -27020,7 +27761,7 @@ var ProviderRegistry = class {
27020
27761
  allowedUnsupportedProperties: this.allowedUnsupportedProperties,
27021
27762
  forceCcApi: input.forceCcApi === true
27022
27763
  })) {
27023
- this.logger.debug(`Routing ${resourceType} via Cloud Control (state-recorded provisionedBy=cc-api)`);
27764
+ debug(`Routing ${resourceType} via Cloud Control (state-recorded provisionedBy=cc-api)`);
27024
27765
  return {
27025
27766
  provider: this.cloudControlProvider,
27026
27767
  provisionedBy: "cc-api"
@@ -27032,9 +27773,9 @@ var ProviderRegistry = class {
27032
27773
  if (specificProvider) {
27033
27774
  const actionableDrops = findActionableSilentDrops(resourceType, properties, this.allowedUnsupportedProperties, input.previousProperties);
27034
27775
  if (actionableDrops.length === 0) {
27035
- this.logger.debug(`Using specific SDK provider for ${resourceType}`);
27776
+ debug(`Using specific SDK provider for ${resourceType}`);
27036
27777
  if (returningToSdk) {
27037
- this.logger.debug(`${resourceType} is returning to its SDK provider from a state-recorded cc-api route; physical id is preserved`);
27778
+ debug(`${resourceType} is returning to its SDK provider from a state-recorded cc-api route; physical id is preserved`);
27038
27779
  return {
27039
27780
  provider: specificProvider,
27040
27781
  provisionedBy: "sdk",
@@ -27048,7 +27789,7 @@ var ProviderRegistry = class {
27048
27789
  }
27049
27790
  const unroutable = this.ccRouteUnavailableReason(resourceType);
27050
27791
  if (unroutable !== void 0) throw new Error(this.buildUnroutableSilentDropMessage(resourceType, actionableDrops, unroutable));
27051
- this.logger.debug(`Auto-routing ${resourceType} via Cloud Control (silent-drop properties: ${actionableDrops.map((d) => d.property).join(", ")})`);
27792
+ debug(`Auto-routing ${resourceType} via Cloud Control (silent-drop properties: ${actionableDrops.map((d) => d.property).join(", ")})`);
27052
27793
  return {
27053
27794
  provider: this.cloudControlProvider,
27054
27795
  provisionedBy: "cc-api",
@@ -27056,14 +27797,14 @@ var ProviderRegistry = class {
27056
27797
  };
27057
27798
  }
27058
27799
  if (CloudControlProvider.isSupportedResourceType(resourceType)) {
27059
- this.logger.debug(`Using Cloud Control API provider for ${resourceType}`);
27800
+ debug(`Using Cloud Control API provider for ${resourceType}`);
27060
27801
  return {
27061
27802
  provider: this.cloudControlProvider,
27062
27803
  provisionedBy: "cc-api"
27063
27804
  };
27064
27805
  }
27065
27806
  if (this.allowedUnsupportedTypes.has(resourceType)) {
27066
- this.logger.debug(`Routing escape-hatch-allowed type ${resourceType} through Cloud Control API`);
27807
+ debug(`Routing escape-hatch-allowed type ${resourceType} through Cloud Control API`);
27067
27808
  return {
27068
27809
  provider: this.cloudControlProvider,
27069
27810
  provisionedBy: "cc-api"
@@ -27336,7 +28077,7 @@ function recordedTagsUnreadableWarning(resourceType, id, applied) {
27336
28077
  //#endregion
27337
28078
  //#region src/provisioning/providers/idempotency-token.ts
27338
28079
  const PROCESS_NONCE = randomUUID();
27339
- const inFlight = /* @__PURE__ */ new Map();
28080
+ const inFlight$1 = /* @__PURE__ */ new Map();
27340
28081
  const generations = /* @__PURE__ */ new Map();
27341
28082
  const MAX_TRACKED_GENERATIONS = 1e5;
27342
28083
  const tokenKey = (scope, logicalId, maxLength, charset) => injectiveKey(scope, ambientRegion() ?? "", getCurrentStackName() ?? "", logicalId, maxLength, charset);
@@ -27347,14 +28088,14 @@ const derive = (key, generation, maxLength, charset) => {
27347
28088
  function acquireIdempotencyToken(options) {
27348
28089
  const { scope, logicalId, maxLength = 64, charset = "default" } = options;
27349
28090
  const key = tokenKey(scope, logicalId, maxLength, charset);
27350
- const existing = inFlight.get(key);
28091
+ const existing = inFlight$1.get(key);
27351
28092
  const value = existing ?? derive(key, generations.get(key) ?? 0, maxLength, charset);
27352
- if (existing === void 0) inFlight.set(key, value);
28093
+ if (existing === void 0) inFlight$1.set(key, value);
27353
28094
  return {
27354
28095
  value,
27355
28096
  release: () => {
27356
- if (!inFlight.has(key)) return;
27357
- inFlight.delete(key);
28097
+ if (!inFlight$1.has(key)) return;
28098
+ inFlight$1.delete(key);
27358
28099
  const generation = (generations.get(key) ?? 0) + 1;
27359
28100
  if (!generations.has(key) && generations.size >= MAX_TRACKED_GENERATIONS) {
27360
28101
  const oldest = generations.keys().next();
@@ -27379,6 +28120,7 @@ function stackScopedCreateToken(options) {
27379
28120
  //#region src/provisioning/providers/create-token-ledger.ts
27380
28121
  var CreateTokenLedger = class {
27381
28122
  doc;
28123
+ loadedAbsent = false;
27382
28124
  stale = false;
27383
28125
  startedThisDeploy = false;
27384
28126
  replaceRecordedLedger = false;
@@ -27404,8 +28146,10 @@ var CreateTokenLedger = class {
27404
28146
  }
27405
28147
  async current() {
27406
28148
  if (this.doc !== void 0 && !this.stale) return this.doc;
28149
+ if (this.loadedAbsent && !this.stale) return null;
27407
28150
  const loaded = await this.store.load();
27408
28151
  this.stale = false;
28152
+ this.loadedAbsent = loaded === null;
27409
28153
  if (loaded !== null && this.replaceRecordedLedger && loaded.stateRecorded === true) {
27410
28154
  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.");
27411
28155
  this.doc = emptyCreateTokenLedger(randomUUID());
@@ -27492,6 +28236,85 @@ var CreateTokenLedger = class {
27492
28236
  }
27493
28237
  });
27494
28238
  }
28239
+ sentEntries() {
28240
+ return this.serialized(async () => {
28241
+ try {
28242
+ return { ...(await this.current())?.sent ?? {} };
28243
+ } catch (error) {
28244
+ this.stale = true;
28245
+ throw error;
28246
+ }
28247
+ });
28248
+ }
28249
+ recordSent(entries, firstSentAt) {
28250
+ return this.serialized(async () => {
28251
+ try {
28252
+ let doc = await this.current();
28253
+ if (doc === null) {
28254
+ doc = emptyCreateTokenLedger(randomUUID());
28255
+ this.doc = doc;
28256
+ this.startedThisDeploy = true;
28257
+ }
28258
+ let changed = false;
28259
+ for (const entry of entries) {
28260
+ const recorded = doc.sent[entry.logicalId];
28261
+ if (recorded?.base === entry.base && recorded.token === entry.token) continue;
28262
+ doc.sent[entry.logicalId] = {
28263
+ base: entry.base,
28264
+ token: entry.token,
28265
+ firstSentAt
28266
+ };
28267
+ changed = true;
28268
+ }
28269
+ if (changed) await this.persist(doc);
28270
+ } catch (error) {
28271
+ this.stale = true;
28272
+ throw error;
28273
+ }
28274
+ });
28275
+ }
28276
+ abandonedAt() {
28277
+ return this.serialized(async () => (await this.current())?.abandonedAt);
28278
+ }
28279
+ noteAbandoned(at) {
28280
+ return this.serialized(async () => {
28281
+ try {
28282
+ const doc = await this.current();
28283
+ if (doc === null) return;
28284
+ if (!Object.values(doc.sent).some((e) => e.base.startsWith("adopt-by-name:")) || doc.abandonedAt !== void 0 && doc.abandonedAt >= at) return;
28285
+ doc.abandonedAt = at;
28286
+ await this.persist(doc);
28287
+ } catch (error) {
28288
+ this.stale = true;
28289
+ throw error;
28290
+ }
28291
+ });
28292
+ }
28293
+ settleAdoptingCreates(drop, failedAt) {
28294
+ return this.serialized(async () => {
28295
+ try {
28296
+ const doc = await this.current();
28297
+ if (doc === null) return;
28298
+ const adopting = (id) => doc.sent[id]?.base.startsWith(ADOPTING_CREATE_BASE) === true;
28299
+ let changed = false;
28300
+ for (const id of drop) {
28301
+ if (!adopting(id)) continue;
28302
+ delete doc.sent[id];
28303
+ changed = true;
28304
+ }
28305
+ for (const [id, at] of failedAt) {
28306
+ const entry = adopting(id) ? doc.sent[id] : void 0;
28307
+ if (entry === void 0 || entry.failedAt !== void 0) continue;
28308
+ entry.failedAt = at;
28309
+ changed = true;
28310
+ }
28311
+ if (changed) await this.persist(doc);
28312
+ } catch (error) {
28313
+ this.stale = true;
28314
+ throw error;
28315
+ }
28316
+ });
28317
+ }
27495
28318
  forget(logicalIds) {
27496
28319
  return this.serialized(async () => {
27497
28320
  try {
@@ -27537,6 +28360,39 @@ function ledgerForStack(backend, stackName, region) {
27537
28360
  });
27538
28361
  }
27539
28362
  const ledgerStore = new AsyncLocalStorage();
28363
+ const ADOPTING_CREATE_BASE = "adopt-by-name:";
28364
+ async function recordedAdoptingCreates() {
28365
+ const ledger = ledgerStore.getStore();
28366
+ if (ledger === void 0) return void 0;
28367
+ const out = /* @__PURE__ */ new Map();
28368
+ for (const [logicalId, entry] of Object.entries(await ledger.sentEntries())) if (entry.base.startsWith("adopt-by-name:")) out.set(logicalId, {
28369
+ resourceType: entry.base.slice(14),
28370
+ name: entry.token,
28371
+ firstSentAt: entry.firstSentAt,
28372
+ ...entry.failedAt !== void 0 && { failedAt: entry.failedAt }
28373
+ });
28374
+ return out;
28375
+ }
28376
+ async function recordAdoptingCreates(creates) {
28377
+ const ledger = ledgerStore.getStore();
28378
+ if (ledger === void 0 || creates.length === 0) return;
28379
+ await ledger.recordSent(creates.map((c) => ({
28380
+ logicalId: c.logicalId,
28381
+ base: `${ADOPTING_CREATE_BASE}${c.resourceType}`,
28382
+ token: c.name
28383
+ })), Date.now());
28384
+ }
28385
+ async function noteAbandonedRun(at) {
28386
+ const ledger = ledgerStore.getStore();
28387
+ if (ledger === void 0) return true;
28388
+ return ledger.noteAbandoned(at).then(() => true, () => false);
28389
+ }
28390
+ async function ledgerAbandonedAt() {
28391
+ return ledgerStore.getStore()?.abandonedAt();
28392
+ }
28393
+ async function settleAdoptingCreates(drop, failedAt = /* @__PURE__ */ new Map()) {
28394
+ await ledgerStore.getStore()?.settleAdoptingCreates(drop, failedAt);
28395
+ }
27540
28396
  function withCreateTokenLedger(ledger, fn) {
27541
28397
  return ledgerStore.run(ledger, () => withStateSavedObserver((stackName, region) => ledger.noteStateSaved(stackName, region), fn));
27542
28398
  }
@@ -28280,6 +29136,25 @@ function skipUnaddressableReplay(scope, logger, op, what, source = "record") {
28280
29136
  logger.warn(safeMsg` Rollback: Cannot ${what} ${shownLogicalId(op.logicalId)} (${refusalResourceType(op.resourceType)}) ` + "— its state record has no non-empty string 'physicalId', so cdkd cannot address it in AWS and sent nothing for it. The resource and its state record are left exactly as they are. Repair the record's 'physicalId' (inspect it with `cdkd state show`), then re-converge it with `cdkd deploy`.");
28281
29137
  recordRollbackSkip(scope, op, UNADDRESSABLE_SKIP_CAUSE);
28282
29138
  }
29139
+ async function keptForAnotherHolder(scope, logger, op, physicalId) {
29140
+ const ask = scope.ctx.createdResourceHolder;
29141
+ if (ask === void 0) return false;
29142
+ let holding;
29143
+ try {
29144
+ holding = await ask(op.resourceType, physicalId);
29145
+ } catch {
29146
+ holding = {
29147
+ kind: "unreadable",
29148
+ what: "the other stacks' state records (the check failed)"
29149
+ };
29150
+ }
29151
+ if (holding === void 0) return false;
29152
+ const why = holding.kind === "held" ? `${holding.by} holds it` : `${holding.what}, so it is not known whether another deployment owns it`;
29153
+ const retry = holding.kind === "unreadable" && holding.retryable === true ? scope.ctx.createdResourceRetryCommand : void 0;
29154
+ logger.warn(safeMsg` Rollback: Keeping created resource ${shownLogicalId(op.logicalId)} (${refusalResourceType(op.resourceType)}) ` + safeMsg`\u2014 ${why}. The failed deploy may have adopted a resource that existed under its name, so the rollback does not delete it; the rollback journal keeps it.` + (retry === void 0 ? "" : safeMsg` Once S3 can be read, finish the rollback with: ${retry}`));
29155
+ recordRollbackSkip(scope, op, "Another state record holds this resource, or it could not be checked, so the rollback did not delete it.");
29156
+ return true;
29157
+ }
28283
29158
  function effectiveProvisionedBy(record, fallbackProvisionedBy) {
28284
29159
  return record?.provisionedBy ?? fallbackProvisionedBy;
28285
29160
  }
@@ -30644,6 +31519,7 @@ async function replayDelete(s) {
30644
31519
  skipUnaddressableReplay(s, logger, op, "delete created resource");
30645
31520
  return;
30646
31521
  }
31522
+ if (await keptForAnotherHolder(s, logger, op, op.physicalId)) return;
30647
31523
  s.createRollbackRoute = deleteProvisionedBy;
30648
31524
  const snapshotPolicy = action === "delete-with-final-snapshot";
30649
31525
  const takeFinalSnapshot = snapshotPolicy && ctx.skipFinalSnapshot !== true;
@@ -31440,6 +32316,10 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
31440
32316
  skipUnaddressableReplay(skipScope, logger, op, "delete partially-created resource", failedCreateRecord ? "record" : "journal");
31441
32317
  break;
31442
32318
  }
32319
+ if (await keptForAnotherHolder(skipScope, logger, op, op.physicalId)) {
32320
+ pending.add(op);
32321
+ break;
32322
+ }
31443
32323
  const orphanDeleteChecked = op.physicalIdRecoveredFromError === true && ctx.foreignHolder !== void 0 && ctx.orphanDeleteProven?.has(op) !== true;
31444
32324
  if (orphanDeleteChecked) {
31445
32325
  const verdict = await journaledOrphanKeepReason({
@@ -32636,7 +33516,16 @@ async function performRollback(completedOperations, stateResources, stackName, p
32636
33516
  operations: completedOperations,
32637
33517
  failedOperations: orphanOps
32638
33518
  }], priorOrphans);
32639
- const ctx = this.rollbackExecutorContext(previousState, stackName);
33519
+ const crossPrefixHolder = this.options.crossPrefixHolder;
33520
+ let crossPrefixAnswer;
33521
+ const ctx = {
33522
+ ...this.rollbackExecutorContext(previousState, stackName),
33523
+ createdResourceRetryCommand: pasteableCommand("cdkd rollback", [{
33524
+ value: (this.options.parentStackInfo?.parentStack ?? stackName).split("~")[0],
33525
+ hole: "stack"
33526
+ }], recoveryCommandFlags(this.options.refusalRecovery).flags).command,
33527
+ createdResourceHolder: crossPrefixHolder === void 0 ? void 0 : () => crossPrefixAnswer ??= crossPrefixHolder(stackName)
33528
+ };
32640
33529
  const inlinePolicyWriters = new RollbackInlinePolicyWriters();
32641
33530
  const runId = this.options.eventRecorder?.runId;
32642
33531
  const { result, failed, run } = await withNestedRevertRun(runId, async (scope) => {
@@ -32666,6 +33555,21 @@ async function performRollback(completedOperations, stateResources, stackName, p
32666
33555
  remainingFailedOps: failedOperations.filter((_, i) => !handled.has(i))
32667
33556
  };
32668
33557
  }
33558
+ function foreignHolderResolver(engine) {
33559
+ const sameBucketHolderFor = makeForeignHolderScan(engine.stateBackend);
33560
+ const crossPrefixHolder = engine.options.crossPrefixHolder;
33561
+ const crossPrefixAnswers = /* @__PURE__ */ new Map();
33562
+ return (self) => async (resourceType, physicalId) => {
33563
+ const held = await sameBucketHolderFor(self)(resourceType, physicalId);
33564
+ if (held !== void 0 || crossPrefixHolder === void 0) return held;
33565
+ let answer = crossPrefixAnswers.get(self.stackName);
33566
+ if (answer === void 0) {
33567
+ answer = crossPrefixHolder(self.stackName);
33568
+ crossPrefixAnswers.set(self.stackName, answer);
33569
+ }
33570
+ return answer;
33571
+ };
33572
+ }
32669
33573
  async function settleJournalAfterSuccess(stackName, completedOperations, previousState, finalResources, initialDeploy) {
32670
33574
  if (this.options.parentStackInfo) {
32671
33575
  await this.writeRollbackJournalSegment(stackName, completedOperations, [], NESTED_PENDING_PARENT_REASON, initialDeploy, nestedPendingSnapshot(previousState, (name) => {
@@ -32677,7 +33581,7 @@ async function settleJournalAfterSuccess(stackName, completedOperations, previou
32677
33581
  return 0;
32678
33582
  }
32679
33583
  let nestedLeft = 0;
32680
- const foreignHolderFor = makeForeignHolderScan(this.stateBackend);
33584
+ const foreignHolderFor = foreignHolderResolver(this);
32681
33585
  const deployRunId = this.options.eventRecorder?.runId;
32682
33586
  const stripOnFailure = /* @__PURE__ */ new Map();
32683
33587
  const [ownLeft] = await Promise.all([(async () => {
@@ -34197,6 +35101,13 @@ async function provisionCreate(logicalId, change, stateResources, stackName, tem
34197
35101
  });
34198
35102
  const createProvider = createDecision.provider;
34199
35103
  const createProps = createDecision.provisionedBy === "cc-api" ? this.preparePropertiesForCcApi(resourceType, resolvedProps, logicalId) : resolvedProps;
35104
+ await refuseUnlicensedGeneratedName.call(this, {
35105
+ logicalId,
35106
+ resourceType,
35107
+ stackName,
35108
+ createdVia: createDecision.provisionedBy,
35109
+ resolvedProps
35110
+ });
34200
35111
  await refuseTakenCreateName.call(this, {
34201
35112
  logicalId,
34202
35113
  resourceType,
@@ -34207,7 +35118,15 @@ async function provisionCreate(logicalId, change, stateResources, stackName, tem
34207
35118
  secrets: createSecrets,
34208
35119
  stateResources
34209
35120
  });
34210
- const result = await this.withRetry(() => withCurrentResourceSecrets(createSecrets, () => createProvider.create(logicalId, resourceType, createProps, { maskSecrets: createSecretMasker(createSecrets) })), logicalId, void 0, void 0, createProvider);
35121
+ this.generatedNameGuard?.noteSent(logicalId);
35122
+ let result;
35123
+ try {
35124
+ result = await this.withRetry(() => withCurrentResourceSecrets(createSecrets, () => createProvider.create(logicalId, resourceType, createProps, { maskSecrets: createSecretMasker(createSecrets) })), logicalId, void 0, void 0, createProvider);
35125
+ } catch (error) {
35126
+ this.generatedNameGuard?.noteFailed(logicalId, error);
35127
+ throw error;
35128
+ }
35129
+ this.generatedNameGuard?.noteReturned(logicalId);
34211
35130
  this.registerNoEchoAttributes(logicalId, result, createSecrets, resolvedProps);
34212
35131
  const dependencies = this.extractAllDependencies(template, logicalId);
34213
35132
  const templateAttrs = this.extractTemplateAttributes(template, logicalId);
@@ -34235,6 +35154,46 @@ async function provisionCreate(logicalId, change, stateResources, stackName, tem
34235
35154
  renderer.removeTask(logicalId);
34236
35155
  this.logger.info(`${createPrefix}${formatResourceLine("created", logicalId, resourceType)}`);
34237
35156
  }
35157
+ async function refuseUnlicensedGeneratedName(input) {
35158
+ const { logicalId, resourceType } = input;
35159
+ const asked = this.generatedNameGuard?.candidate(logicalId);
35160
+ if (asked === void 0 || input.createdVia === "cc-api") return;
35161
+ const verdict = await this.generatedNameGuard.admit(logicalId, input.resolvedProps);
35162
+ if (verdict === void 0) return;
35163
+ const subject = `${displaySafe(logicalId)} (${displaySafe(resourceType)})`;
35164
+ const named = `the cdkd-generated ${asked.property ?? "name"} ${displaySafe(asked.name)}`;
35165
+ const adoptsText = "its create API hands back or overwrites an existing resource of that name instead of refusing it";
35166
+ const refuse = (message) => {
35167
+ throw markNonRetryable(markRefusedBeforeApplying(new CdkdError(message, "GENERATED_NAME_HELD")));
35168
+ };
35169
+ switch (verdict.kind) {
35170
+ case "free":
35171
+ case "licensed": return;
35172
+ case "unchecked":
35173
+ this.logger.warn(safeMsg`${subject} is created with ${named} without checking whether another resource already ` + safeMsg`holds it: the lookup was refused (${describeAwsFailure(verdict.error).summary}). ` + "Grant this identity the type's Describe / List permission to have cdkd refuse taking over another deployment's resource.");
35174
+ return;
35175
+ case "failed":
35176
+ if (isInterruptedWaitError(verdict.error)) throw verdict.error;
35177
+ return refuse(`${subject} is created with ${named}, and ${adoptsText}, but cdkd could not check whether another resource already holds it (${describeAwsFailure(verdict.error).summary}). ${displaySafe(logicalId)} was not created. Re-run the deploy once the check can succeed.`);
35178
+ case "held": {
35179
+ const nested = input.stackName.includes("~");
35180
+ const top = input.stackName.split("~")[0];
35181
+ const importLine = nested ? `adopt it through the top-level stack ${quotedOrDescribed(top, "stack name")} with \`cdkd import\`` : `adopt it with \`${pasteableCommand("cdkd import", [{
35182
+ value: input.stackName,
35183
+ hole: "stack"
35184
+ }, {
35185
+ flag: "--resource",
35186
+ value: `${logicalId}=${verdict.holder}`,
35187
+ hole: "logicalId=physicalId"
35188
+ }], recoveryCommandFlags(this.options.refusalRecovery).flags).command}\``;
35189
+ if (verdict.ownIntent !== void 0) {
35190
+ const why = verdict.ownIntent === "abandoned" ? "a deploy of this stack that was killed or force-unlocked" : "a create of this stack that came back failed (a timeout or a server error)";
35191
+ return refuse(`${subject} would be created with ${named}, which an existing resource (${displaySafe(verdict.holder)}) already holds. This stack's create-token ledger records that ${why} sent this name, but this type reports no creation time, so cdkd cannot tell that resource is the one it made rather than another deployment's created since. Since ${adoptsText}, creating it would take that resource over. ${displaySafe(logicalId)} was not created. The likely cause: the resource is this stack's own, from that deploy. If it is, ${importLine} and re-run; if it is not, this stack is also deployed under another state backend (go-to-k/cdkd#4705).`);
35192
+ }
35193
+ return refuse(`${subject} would be created with ${named}, which an existing resource (${displaySafe(verdict.holder)}) already holds, and nothing this stack records names that resource: not its state, its rollback journal or create-token ledger, nor what a destroy of it under this state prefix kept. Since ${adoptsText}, creating it would take that resource over, as CloudFormation refuses with "already exists". ${displaySafe(logicalId)} was not created. The likely cause: this stack is also deployed under another state backend (another --state-prefix, --state-bucket or account), where cdkd generates the same names. One stack name per account and region is supported (go-to-k/cdkd#4705): deploy it under one backend only, or give this stack another name. If the resource is this stack's own, ${importLine} and re-run.`);
35194
+ }
35195
+ }
35196
+ }
34238
35197
  async function refuseTakenCreateName(input) {
34239
35198
  const { logicalId, resourceType, secrets } = input;
34240
35199
  const question = createNameQuestion({
@@ -34517,6 +35476,9 @@ function isResourceNotFoundError(error) {
34517
35476
 
34518
35477
  //#endregion
34519
35478
  //#region src/provisioning/providers/dynamodb-globaltable-provider.ts
35479
+ function isTableName(id) {
35480
+ return /^[A-Za-z0-9_.-]{3,255}$/.test(id);
35481
+ }
34520
35482
  const DRIFT_STRIPPED_INDEX_MEMBERS = [{
34521
35483
  listKey: "GlobalSecondaryIndexes",
34522
35484
  member: "WarmThroughput"
@@ -34757,8 +35719,9 @@ var DynamoDBGlobalTableProvider = class {
34757
35719
  toSdkReplicaThroughputOverrides(replica, billingMode, diagnostics, {}, maskSecrets);
34758
35720
  }
34759
35721
  this.reportThroughputDiagnostics(diagnostics, logicalId, tableName, maskSecrets);
35722
+ let createdTableId;
34760
35723
  try {
34761
- await (await this.getCreateClient()).send(new CreateTableCommand(createParams));
35724
+ createdTableId = (await (await this.getCreateClient()).send(new CreateTableCommand(createParams)))?.TableDescription?.TableId;
34762
35725
  log.debug(`CreateTable initiated for ${log.value(tableName)}, waiting for ACTIVE`);
34763
35726
  } catch (error) {
34764
35727
  const cause = error instanceof Error ? error : void 0;
@@ -34834,7 +35797,7 @@ var DynamoDBGlobalTableProvider = class {
34834
35797
  markAuxiliaryFailure(wiringError, logicalId);
34835
35798
  const cause = wiringError instanceof Error ? wiringError : void 0;
34836
35799
  const thrown = this.wrapMaskedError(log, wiringError, (text) => new ProvisioningError(`Failed to create DynamoDB GlobalTable ${logicalId}: ${text}`, resourceType, logicalId, tableName, cause));
34837
- if (tableLeftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, tableName);
35800
+ if (tableLeftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, tableName, createdTableId);
34838
35801
  throw thrown;
34839
35802
  }
34840
35803
  }
@@ -35770,7 +36733,8 @@ var DynamoDBGlobalTableProvider = class {
35770
36733
  if (error instanceof ResourceNotFoundException$1) {
35771
36734
  const clientRegion = await this.dynamoDBClient.config.region();
35772
36735
  assertRegionMatch(clientRegion, context?.expectedRegion, resourceType, logicalId, physicalId);
35773
- this.logger.debug(`DynamoDB GlobalTable ${physicalId} does not exist, skipping`);
36736
+ if (context?.failedCreateOrphan === true) this.logger.info(safeMsg` DynamoDB GlobalTable ${physicalId} (${logicalId}), which a failed deploy created, is already gone; nothing to delete`);
36737
+ else this.logger.debug(`DynamoDB GlobalTable ${physicalId} does not exist, skipping`);
35774
36738
  this.deleteBudgets.release(deleteBudgetKey(physicalId, context?.expectedRegion));
35775
36739
  this.protectionFlips.release(deleteBudgetKey(physicalId, context?.expectedRegion));
35776
36740
  return;
@@ -35779,6 +36743,36 @@ var DynamoDBGlobalTableProvider = class {
35779
36743
  throw new ProvisioningError(`Failed to delete DynamoDB GlobalTable ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
35780
36744
  }
35781
36745
  }
36746
+ async isSameResource(journaledPhysicalId, record, resourceType, context) {
36747
+ if (resourceType !== "AWS::DynamoDB::GlobalTable") return "unknown";
36748
+ if (!isTableName(journaledPhysicalId) || !isTableName(record.physicalId)) return "unknown";
36749
+ if (journaledPhysicalId === record.physicalId) return "same";
36750
+ if (await this.dynamoDBClient.config.region() !== context.expectedRegion) return "unknown";
36751
+ const recordTableId = await this.readTableIdIfExists(record.physicalId);
36752
+ if (recordTableId === void 0) return "unknown";
36753
+ const journaledTableId = await this.readTableIdIfExists(journaledPhysicalId);
36754
+ if (journaledTableId === void 0) return "different";
36755
+ return journaledTableId === recordTableId ? "same" : "different";
36756
+ }
36757
+ async resourceIdentity(physicalId, resourceType, context) {
36758
+ if (resourceType !== "AWS::DynamoDB::GlobalTable" || !isTableName(physicalId)) return;
36759
+ if (await this.dynamoDBClient.config.region() !== context.expectedRegion) return void 0;
36760
+ const live = await this.readTableIdIfExists(physicalId);
36761
+ return live === void 0 ? RESOURCE_NOT_FOUND : live;
36762
+ }
36763
+ async readTableIdIfExists(tableName) {
36764
+ let response;
36765
+ try {
36766
+ response = await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }));
36767
+ } catch (error) {
36768
+ if (error instanceof ResourceNotFoundException$1) return void 0;
36769
+ throw error;
36770
+ }
36771
+ if (response.Table?.TableName !== tableName) throw new Error("DescribeTable answered for another table than the one asked for");
36772
+ const tableId = response.Table.TableId;
36773
+ if (typeof tableId !== "string" || tableId === "") throw new Error("DescribeTable returned no TableId");
36774
+ return tableId;
36775
+ }
35782
36776
  async getAttribute(physicalId, _resourceType, attributeName) {
35783
36777
  const cacheKey = injectiveKey(physicalId, attributeName);
35784
36778
  if (this.attributeCache.has(cacheKey)) return this.attributeCache.get(cacheKey);
@@ -37450,11 +38444,11 @@ const NESTED_STACK_TYPE$1 = "AWS::CloudFormation::Stack";
37450
38444
  async function requireDeploymentApproval(args) {
37451
38445
  const level = args.options.requireApproval ?? "never";
37452
38446
  const approve = args.options.approveDeployment;
37453
- if (level === "never" || approve === void 0) return;
38447
+ if (level === "never" || approve === void 0) return false;
37454
38448
  const changes = [...args.changes].filter((c) => c.changeType !== "NO_CHANGE" && !isNoEchoPromotionOnly(c));
37455
38449
  const destructiveChanges = findDestructiveChanges(args.stackName, changes, args.records, args.template, new Set(args.recreateTargetIds ?? []));
37456
38450
  const onlyNestedUpdates = changes.every((c) => c.changeType === "UPDATE" && c.resourceType === NESTED_STACK_TYPE$1);
37457
- if (!(level === "destructive" ? destructiveChanges.length > 0 : changes.length > 0 && !onlyNestedUpdates)) return;
38451
+ if (!(level === "destructive" ? destructiveChanges.length > 0 : changes.length > 0 && !onlyNestedUpdates)) return false;
37458
38452
  const count = (type) => changes.filter((c) => c.changeType === type).length;
37459
38453
  if (enclosingDeadlineExpired()) throw approvalAfterTimeout(args.stackName);
37460
38454
  let approved;
@@ -37473,6 +38467,16 @@ async function requireDeploymentApproval(args) {
37473
38467
  throw error instanceof Error ? markNonRetryable(error) : error;
37474
38468
  }
37475
38469
  if (!approved) throw markNonRetryable(new CdkdError(safeMsg`Deployment of stack ${args.stackName} was not approved (--require-approval=${level}). Nothing was changed.`, "DEPLOY_NOT_APPROVED"));
38470
+ return approve.autoApproves !== true;
38471
+ }
38472
+ async function checkDestructivePlan(args) {
38473
+ const hook = args.options.onDestructivePlan;
38474
+ if (hook === void 0) return;
38475
+ const changes = [...args.changes].filter((c) => c.changeType !== "NO_CHANGE" && !isNoEchoPromotionOnly(c));
38476
+ const destroying = findDestructiveChanges(args.stackName, changes, args.records, args.template, new Set(args.recreateTargetIds ?? [])).filter((c) => c.impact !== "WILL_ORPHAN");
38477
+ const nestedRowChanges = changes.some((c) => c.resourceType === NESTED_STACK_TYPE$1 && (c.changeType === "CREATE" || c.changeType === "UPDATE"));
38478
+ if (destroying.length === 0 && !nestedRowChanges) return;
38479
+ await hook(args.stackName, destroying);
37476
38480
  }
37477
38481
  async function approveLateReplacement(args) {
37478
38482
  const level = args.options.requireApproval ?? "never";
@@ -37480,7 +38484,7 @@ async function approveLateReplacement(args) {
37480
38484
  if (level === "never" || approve === void 0) return true;
37481
38485
  if (enclosingDeadlineExpired()) return false;
37482
38486
  try {
37483
- return await whileEnclosingDeadlinesPaused(() => approve({
38487
+ const approved = await whileEnclosingDeadlinesPaused(() => approve({
37484
38488
  stackName: args.stackName,
37485
38489
  level,
37486
38490
  counts: {
@@ -37490,6 +38494,8 @@ async function approveLateReplacement(args) {
37490
38494
  },
37491
38495
  destructiveChanges: findDestructiveChanges(args.stackName, [args.change], args.records, args.template)
37492
38496
  }));
38497
+ if (approve.autoApproves !== true) args.onAsked?.();
38498
+ return approved;
37493
38499
  } catch {
37494
38500
  return false;
37495
38501
  }
@@ -37836,27 +38842,42 @@ async function provisionUpdate(logicalId, change, stateResources, stackName, tem
37836
38842
  };
37837
38843
  let replacedAnyway = false;
37838
38844
  let lateApproval;
38845
+ let lateCrossPrefixRefused = false;
37839
38846
  const approveReplacementOf = (pc) => {
37840
38847
  if (replacedAnyway) return Promise.resolve(true);
37841
38848
  const { noEchoPromoted: _promoted, ...asReplacement } = pc;
37842
- lateApproval ??= approveLateReplacement({
37843
- options: this.options,
37844
- stackName,
37845
- change: {
37846
- ...change,
37847
- changeType: "UPDATE",
37848
- propertyChanges: [{
37849
- ...asReplacement,
37850
- requiresReplacement: true
37851
- }]
37852
- },
37853
- records: stateResources,
37854
- template
37855
- });
38849
+ const asChange = {
38850
+ ...change,
38851
+ changeType: "UPDATE",
38852
+ propertyChanges: [{
38853
+ ...asReplacement,
38854
+ requiresReplacement: true
38855
+ }]
38856
+ };
38857
+ lateApproval ??= (async () => {
38858
+ const crossPrefixGate = this.options.onDestructivePlan;
38859
+ if (crossPrefixGate !== void 0) try {
38860
+ await crossPrefixGate(stackName, findDestructiveChanges(stackName, [asChange], stateResources, template), "late");
38861
+ } catch (error) {
38862
+ if (!(error instanceof CdkdError) || error.code !== "STACK_UNDER_OTHER_STATE_PREFIX") throw error;
38863
+ lateCrossPrefixRefused = true;
38864
+ if (counts) counts.crossPrefixKept = (counts.crossPrefixKept ?? 0) + 1;
38865
+ this.logger.warn(error.message);
38866
+ return false;
38867
+ }
38868
+ return approveLateReplacement({
38869
+ options: this.options,
38870
+ stackName,
38871
+ change: asChange,
38872
+ records: stateResources,
38873
+ template,
38874
+ onAsked: () => this.generatedNameGuard?.noteApprovalPrompted()
38875
+ });
38876
+ })();
37856
38877
  return lateApproval;
37857
38878
  };
37858
38879
  const lateReplacementDeclined = /* @__PURE__ */ new Set();
37859
- const differsWhy = (key) => lateReplacementDeclined.has(key) ? `differs; the replacement was not approved (--require-approval=${this.options.requireApproval ?? "never"})` : "differs; the provider is not known to report this property exactly, so the difference may be its normalization";
38880
+ const differsWhy = (key) => !lateReplacementDeclined.has(key) ? "differs; the provider is not known to report this property exactly, so the difference may be its normalization" : lateCrossPrefixRefused ? "differs; the replacement was refused: see the state-prefix warning above" : `differs; the replacement was not approved (--require-approval=${this.options.requireApproval ?? "never"})`;
37860
38881
  if (unmarkedRecord && !typeChanged && echoCandidates.length > 0 && hasAddressablePhysicalId(currentResource)) await readOnce();
37861
38882
  if (!typeChanged && !suppliesFreshMaskOnlyValue && lostChild === void 0 && this.recreateDirectionFor(stackName, logicalId) === void 0 && recordMatchesDesired) {
37862
38883
  if (change.attributeChanges && change.attributeChanges.length > 0) {
@@ -38649,8 +39670,83 @@ async function updateInPlace({ conditions, counts, currentProps, currentPropsAsW
38649
39670
  this.logger.info(`${updatePrefix}${formatResourceLine("updated", logicalId, resourceType)}`);
38650
39671
  }
38651
39672
 
39673
+ //#endregion
39674
+ //#region src/deployment/retained-readoption.ts
39675
+ function keptForReadoption(resource) {
39676
+ if (!replacementCreateAdoptsName(resource.resourceType, resource.provisionedBy)) return false;
39677
+ if (typeof resource.physicalId !== "string" || resource.physicalId === "") return false;
39678
+ const property = explicitNamePropertyFor(resource.resourceType);
39679
+ const properties = resource.properties;
39680
+ return property === void 0 || !properties?.[property];
39681
+ }
39682
+ async function recordRetainedForReadoption(backend, stackName, region, kept, logger, earlier) {
39683
+ if (kept.length === 0) {
39684
+ try {
39685
+ await backend.ensureRetainedTombstone(stackName, region);
39686
+ } catch (error) {
39687
+ logger.warn(safeMsg`Could not write the empty kept-resource record of ${displayStackName(stackName)} ` + safeMsg`(${describeAwsFailure(error).summary}). Until one exists, a later deploy here may ` + `take back a resource an older cdkd kept; 'cdkd state orphan' writes it.`);
39688
+ }
39689
+ return;
39690
+ }
39691
+ try {
39692
+ const keptIds = new Set(kept.map((k) => k.logicalId));
39693
+ const before = await (earlier ?? backend.loadRetainedRecord(stackName, region)) ?? [];
39694
+ await backend.saveRetainedResources(stackName, region, [...before.filter((e) => !keptIds.has(e.logicalId)), ...kept]);
39695
+ } catch (error) {
39696
+ if (error instanceof RetainedTimeUnconfirmedError) {
39697
+ 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.`);
39698
+ return;
39699
+ }
39700
+ 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.`);
39701
+ }
39702
+ }
39703
+ var KeptForReadoption = class {
39704
+ entries = [];
39705
+ earlier;
39706
+ inFlight;
39707
+ backend;
39708
+ stackName;
39709
+ region;
39710
+ logger;
39711
+ constructor(backend, stackName, region, logger) {
39712
+ this.backend = backend;
39713
+ this.stackName = stackName;
39714
+ this.region = region;
39715
+ this.logger = logger;
39716
+ }
39717
+ get size() {
39718
+ return this.entries.length;
39719
+ }
39720
+ add(entry) {
39721
+ this.entries.push(entry);
39722
+ if (this.earlier === void 0) {
39723
+ this.earlier = Promise.resolve().then(() => this.backend.loadRetainedRecord(this.stackName, this.region));
39724
+ this.earlier.catch(() => void 0);
39725
+ }
39726
+ }
39727
+ async flush() {
39728
+ if (this.inFlight !== void 0) await this.inFlight;
39729
+ if (this.entries.length === 0) return;
39730
+ const kept = this.entries;
39731
+ const earlier = this.earlier;
39732
+ this.entries = [];
39733
+ this.earlier = void 0;
39734
+ const write = recordRetainedForReadoption(this.backend, this.stackName, this.region, kept, this.logger, earlier);
39735
+ this.inFlight = write;
39736
+ try {
39737
+ await write;
39738
+ } finally {
39739
+ if (this.inFlight === write) this.inFlight = void 0;
39740
+ }
39741
+ }
39742
+ };
39743
+
38652
39744
  //#endregion
38653
39745
  //#region src/deployment/deploy-engine/delete.ts
39746
+ const keptByEngine = /* @__PURE__ */ new WeakMap();
39747
+ async function flushKeptForReadoption(engine) {
39748
+ await keptByEngine.get(engine)?.flush();
39749
+ }
38654
39750
  async function provisionDelete(logicalId, change, stateResources, stackName, template, counts, progress) {
38655
39751
  const resourceType = change.resourceType;
38656
39752
  const existingState = stateResources[logicalId];
@@ -38662,6 +39758,18 @@ async function provisionDelete(logicalId, change, stateResources, stackName, tem
38662
39758
  this.logger.info(`Retaining ${logicalId} (${resourceType}) - DeletionPolicy: ${deletionPolicy}`);
38663
39759
  delete stateResources[logicalId];
38664
39760
  await noteRetainedResource(resourceType, logicalId);
39761
+ if (keptForReadoption(currentResource)) {
39762
+ let kept = keptByEngine.get(this);
39763
+ if (kept === void 0) {
39764
+ kept = new KeptForReadoption(this.stateBackend, stackName, this.stackRegion, this.logger);
39765
+ keptByEngine.set(this, kept);
39766
+ }
39767
+ kept.add({
39768
+ logicalId,
39769
+ resourceType,
39770
+ physicalId: currentResource.physicalId
39771
+ });
39772
+ }
38665
39773
  return;
38666
39774
  }
38667
39775
  if (!(currentResource.resourceType === "AWS::CloudFormation::Stack" && currentResource.provisionedBy !== "cc-api") && !hasAddressablePhysicalId(currentResource)) {
@@ -39548,6 +40656,707 @@ function journaledOrphanPolicy(policy) {
39548
40656
  return policy === "Delete" || policy === "Retain" || policy === "Snapshot" || policy === "RetainExceptOnCreate" ? policy : "Retain";
39549
40657
  }
39550
40658
 
40659
+ //#endregion
40660
+ //#region src/provisioning/name-lookup.ts
40661
+ const inFlight = /* @__PURE__ */ new Map();
40662
+ const waiting = /* @__PURE__ */ new Map();
40663
+ async function withApiLimit(api, max, fn) {
40664
+ if ((inFlight.get(api) ?? 0) >= max) await new Promise((resolve) => {
40665
+ const queue = waiting.get(api) ?? [];
40666
+ queue.push(resolve);
40667
+ waiting.set(api, queue);
40668
+ });
40669
+ else inFlight.set(api, (inFlight.get(api) ?? 0) + 1);
40670
+ try {
40671
+ return await fn();
40672
+ } finally {
40673
+ const next = waiting.get(api)?.shift();
40674
+ if (next) next();
40675
+ else inFlight.set(api, (inFlight.get(api) ?? 1) - 1);
40676
+ }
40677
+ }
40678
+ function chunks(items, size) {
40679
+ const out = [];
40680
+ for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
40681
+ return out;
40682
+ }
40683
+ async function lookupEachName(names, api, concurrency, lookupOne) {
40684
+ const found = /* @__PURE__ */ new Map();
40685
+ await Promise.all(names.map((name) => withApiLimit(api, concurrency, async () => {
40686
+ const holder = await lookupOne(name);
40687
+ if (holder !== void 0) found.set(name, holder);
40688
+ })));
40689
+ return found;
40690
+ }
40691
+ function isAccessDeniedError(error) {
40692
+ const name = error?.name;
40693
+ const status = error?.$metadata?.httpStatusCode;
40694
+ return name === "AccessDenied" || name === "AccessDeniedException" || name === "AuthorizationError" || name === "UnauthorizedOperation" || status === 403;
40695
+ }
40696
+
40697
+ //#endregion
40698
+ //#region src/deployment/generated-name-guard.ts
40699
+ const GROUP_WINDOW_MS = 15;
40700
+ const DEFAULT_TIMING = {
40701
+ now: () => Date.now(),
40702
+ sleep: (ms) => new Promise((resolve) => {
40703
+ setTimeout(resolve, ms).unref?.();
40704
+ }),
40705
+ cooldownMs: 65e3,
40706
+ cooldownStepMs: 1e4
40707
+ };
40708
+ const STILL_LISTED_WHILE_DELETING = /* @__PURE__ */ new Set(["AWS::SQS::Queue", "AWS::S3::Bucket"]);
40709
+ const PER_NAME_CONCURRENCY = { "AWS::SNS::Topic": 4 };
40710
+ const DEFAULT_PER_NAME_CONCURRENCY = 10;
40711
+ const KEPT_AT_SKEW_MS = 6e4;
40712
+ function provenNothingCreated(error, logicalId, resourceType) {
40713
+ if (createdBeforeFailure(error, logicalId, resourceType) !== void 0) return false;
40714
+ if (isAmbiguousOutcomeError(error) || isThrottlingError(error)) return false;
40715
+ let current = error;
40716
+ for (let depth = 0; depth < 8 && current !== null && typeof current === "object"; depth++) {
40717
+ const status = current.$metadata?.httpStatusCode;
40718
+ if (typeof status === "number") return status >= 400 && status < 500 && status !== 408 && status !== 429;
40719
+ current = current.cause;
40720
+ }
40721
+ return false;
40722
+ }
40723
+ var GeneratedNameGuard = class GeneratedNameGuard {
40724
+ verdicts = /* @__PURE__ */ new Map();
40725
+ decidedAt = /* @__PURE__ */ new Map();
40726
+ candidates = /* @__PURE__ */ new Map();
40727
+ recorded = /* @__PURE__ */ new Set();
40728
+ sent = /* @__PURE__ */ new Set();
40729
+ returned = /* @__PURE__ */ new Set();
40730
+ rejected = /* @__PURE__ */ new Set();
40731
+ failedAt = /* @__PURE__ */ new Map();
40732
+ firstVerdicts = /* @__PURE__ */ new Map();
40733
+ plannedWrites;
40734
+ plannedIds = /* @__PURE__ */ new Set();
40735
+ groupPending = [];
40736
+ groupNext;
40737
+ groupInFlight = Promise.resolve();
40738
+ queued = [];
40739
+ flushing;
40740
+ writes = /* @__PURE__ */ new Set();
40741
+ evidenceRead;
40742
+ abandonedRead;
40743
+ historyRead;
40744
+ recordedRead;
40745
+ input;
40746
+ timing;
40747
+ constructor(input) {
40748
+ this.input = input;
40749
+ this.timing = {
40750
+ ...DEFAULT_TIMING,
40751
+ ...input.timing
40752
+ };
40753
+ }
40754
+ static start(input) {
40755
+ const candidates = [];
40756
+ for (const change of input.changes) {
40757
+ if (change.changeType !== "CREATE") continue;
40758
+ const properties = change.desiredProperties ?? {};
40759
+ let decision;
40760
+ let name;
40761
+ let deferred = false;
40762
+ try {
40763
+ decision = input.providerFor({
40764
+ resourceType: change.resourceType,
40765
+ properties
40766
+ });
40767
+ if (!replacementCreateAdoptsName(change.resourceType, decision.provisionedBy)) continue;
40768
+ name = decision.provider.generatedCreateName?.(change.resourceType, change.logicalId, properties);
40769
+ deferred = decision.provider.lookupNeedsResolvedProperties?.(change.resourceType, properties) === true;
40770
+ } catch {
40771
+ continue;
40772
+ }
40773
+ if (typeof name !== "string" || name === "") continue;
40774
+ candidates.push({
40775
+ logicalId: change.logicalId,
40776
+ resourceType: change.resourceType,
40777
+ name,
40778
+ property: explicitNamePropertyFor(change.resourceType),
40779
+ provider: decision.provider,
40780
+ properties,
40781
+ deferred
40782
+ });
40783
+ }
40784
+ if (candidates.length === 0) return void 0;
40785
+ const guard = new GeneratedNameGuard(input);
40786
+ for (const c of candidates) guard.candidates.set(c.logicalId, c);
40787
+ guard.ask(candidates.filter((c) => !c.deferred));
40788
+ return guard;
40789
+ }
40790
+ ask(candidates) {
40791
+ const { verdicts, first } = this.resolve(candidates);
40792
+ for (const [logicalId, verdict] of verdicts) this.verdicts.set(logicalId, verdict);
40793
+ for (const [logicalId, verdict] of first) this.firstVerdicts.set(logicalId, verdict);
40794
+ }
40795
+ resolve(candidates) {
40796
+ const out = /* @__PURE__ */ new Map();
40797
+ const first = /* @__PURE__ */ new Map();
40798
+ const byType = /* @__PURE__ */ new Map();
40799
+ for (const c of candidates) byType.set(c.resourceType, [...byType.get(c.resourceType) ?? [], c]);
40800
+ for (const group of byType.values()) {
40801
+ const decided = this.run(group).catch((error) => ({
40802
+ verdicts: new Map(group.map((c) => [c.logicalId, {
40803
+ kind: "failed",
40804
+ error
40805
+ }])),
40806
+ deleting: []
40807
+ }));
40808
+ let waited;
40809
+ for (const c of group) {
40810
+ first.set(c.logicalId, decided.then(({ verdicts }) => verdicts.get(c.logicalId) ?? { kind: "free" }));
40811
+ out.set(c.logicalId, decided.then(async ({ verdicts, deleting }) => {
40812
+ let verdict = verdicts.get(c.logicalId) ?? { kind: "free" };
40813
+ if (deleting.includes(c)) {
40814
+ waited ??= this.waitOutDeletion(deleting);
40815
+ verdict = (await waited).get(c.logicalId) ?? verdict;
40816
+ }
40817
+ this.decidedAt.set(c.logicalId, this.timing.now());
40818
+ return verdict;
40819
+ }));
40820
+ }
40821
+ }
40822
+ return {
40823
+ verdicts: out,
40824
+ first
40825
+ };
40826
+ }
40827
+ noteApprovalPrompted() {
40828
+ const stale = [...this.decidedAt.keys()].map((id) => this.candidates.get(id)).filter((c) => !c.deferred && !this.sent.has(c.logicalId));
40829
+ if (stale.length === 0) return;
40830
+ const previous = new Map(stale.map((c) => [c.logicalId, this.verdicts.get(c.logicalId)]));
40831
+ const { verdicts, first } = this.resolve(stale);
40832
+ const keepEarlier = (id, fresh, warn) => fresh.then(async (v) => {
40833
+ if (v.kind !== "failed" && v.kind !== "unchecked") return v;
40834
+ const earlier = await previous.get(id);
40835
+ if (earlier.kind !== "free" && earlier.kind !== "licensed") return v;
40836
+ if (warn) this.input.warn?.(`${displaySafe(id)}: could not re-read whether a resource holds its generated name after the approval prompt; acting on the earlier lookup.`);
40837
+ return earlier;
40838
+ });
40839
+ for (const [id, fresh] of verdicts) this.verdicts.set(id, keepEarlier(id, fresh, true));
40840
+ for (const [id, fresh] of first) this.firstVerdicts.set(id, keepEarlier(id, fresh, false));
40841
+ }
40842
+ recordPlannedIntents() {
40843
+ if (this.plannedWrites !== void 0) return;
40844
+ this.plannedWrites = /* @__PURE__ */ new Map();
40845
+ const byType = /* @__PURE__ */ new Map();
40846
+ for (const c of this.candidates.values()) {
40847
+ if (c.deferred) continue;
40848
+ byType.set(c.resourceType, [...byType.get(c.resourceType) ?? [], c]);
40849
+ }
40850
+ let typesWaiting = byType.size;
40851
+ let allArrived;
40852
+ const arrived = new Promise((resolve) => allArrived = resolve);
40853
+ const window = new Promise((resolve) => {
40854
+ setTimeout(resolve, GROUP_WINDOW_MS).unref?.();
40855
+ });
40856
+ this.groupInFlight = Promise.race([arrived, window]);
40857
+ const arrive = () => {
40858
+ typesWaiting--;
40859
+ if (typesWaiting === 0) allArrived();
40860
+ };
40861
+ for (const [resourceType, group] of byType) {
40862
+ const write = (async () => {
40863
+ let verdicts;
40864
+ try {
40865
+ verdicts = await Promise.all(group.map((c) => this.firstVerdicts.get(c.logicalId) ?? this.verdicts.get(c.logicalId)));
40866
+ } finally {
40867
+ arrive();
40868
+ }
40869
+ const batch = group.filter((_c, i) => verdicts[i].kind === "free" || verdicts[i].kind === "licensed");
40870
+ if (batch.length === 0) return;
40871
+ for (const c of batch) this.plannedIds.add(c.logicalId);
40872
+ await this.commitPlanned(batch);
40873
+ })();
40874
+ this.plannedWrites.set(resourceType, write);
40875
+ this.writes.add(write);
40876
+ write.catch(() => void 0);
40877
+ }
40878
+ }
40879
+ get size() {
40880
+ return this.candidates.size;
40881
+ }
40882
+ verdict(logicalId) {
40883
+ return this.verdicts.get(logicalId);
40884
+ }
40885
+ candidate(logicalId) {
40886
+ const c = this.candidates.get(logicalId);
40887
+ return c === void 0 ? void 0 : {
40888
+ name: c.name,
40889
+ property: c.property
40890
+ };
40891
+ }
40892
+ async admit(logicalId, resolvedProperties) {
40893
+ const c = this.candidates.get(logicalId);
40894
+ if (c === void 0) return void 0;
40895
+ if (c.deferred && !this.verdicts.has(logicalId)) this.ask([{
40896
+ ...c,
40897
+ properties: resolvedProperties,
40898
+ deferred: false
40899
+ }]);
40900
+ const verdict = await this.verdicts.get(logicalId);
40901
+ if (verdict.kind !== "free" && verdict.kind !== "licensed") return verdict;
40902
+ try {
40903
+ const planned = this.plannedWrites?.get(c.resourceType);
40904
+ if (planned !== void 0) try {
40905
+ await planned;
40906
+ } catch (error) {
40907
+ if (this.plannedIds.has(logicalId)) throw error;
40908
+ }
40909
+ await this.recordIntent(c);
40910
+ } catch (error) {
40911
+ return {
40912
+ kind: "failed",
40913
+ error
40914
+ };
40915
+ }
40916
+ return verdict;
40917
+ }
40918
+ noteSent(logicalId) {
40919
+ if (this.candidates.has(logicalId)) this.sent.add(logicalId);
40920
+ }
40921
+ noteReturned(logicalId) {
40922
+ if (this.candidates.has(logicalId)) this.returned.add(logicalId);
40923
+ }
40924
+ noteFailed(logicalId, error) {
40925
+ const c = this.candidates.get(logicalId);
40926
+ if (c === void 0) return;
40927
+ if (provenNothingCreated(error, logicalId, c.resourceType)) this.rejected.add(logicalId);
40928
+ else if (!this.failedAt.has(logicalId)) this.failedAt.set(logicalId, this.timing.now());
40929
+ }
40930
+ async settle() {
40931
+ await Promise.all([...this.writes].map((w) => w.catch(() => void 0)));
40932
+ const drop = [...this.recorded].filter((id) => !this.sent.has(id) || this.returned.has(id) || this.rejected.has(id));
40933
+ const failedAt = new Map([...this.failedAt].filter(([id]) => this.recorded.has(id) && !drop.includes(id)));
40934
+ if (drop.length === 0 && failedAt.size === 0) return;
40935
+ let lastError;
40936
+ for (let attempt = 0; attempt < 2; attempt++) try {
40937
+ await settleAdoptingCreates(drop, failedAt);
40938
+ return;
40939
+ } catch (error) {
40940
+ lastError = error;
40941
+ }
40942
+ const bounded = await noteAbandonedRun(this.timing.now());
40943
+ this.input.warn?.(safeMsg`Could not clear this deploy's unsent name-adopting creates from the stack's create-token ledger (${describeAwsFailure(lastError).summary}). ` + (bounded ? "They license only what existed by now; a later deploy may refuse a resource of those names, which `cdkd import` adopts." : "Nor could the ledger record when this deploy ended: a later deploy may take back a resource of those names that another deployment creates. They are cleared by the next successful deploy."));
40944
+ }
40945
+ async readoptedFromRetained() {
40946
+ const out = [];
40947
+ for (const [logicalId, verdict] of this.verdicts) {
40948
+ if (!this.returned.has(logicalId)) continue;
40949
+ const v = await verdict;
40950
+ if (v.kind === "licensed" && (v.via === "retained" || v.via === "history")) out.push(logicalId);
40951
+ }
40952
+ return out;
40953
+ }
40954
+ recordIntent(c) {
40955
+ if (this.recorded.has(c.logicalId)) return Promise.resolve();
40956
+ this.queued.push(c);
40957
+ if (this.flushing === void 0) {
40958
+ const flush = new Promise((resolve) => setImmediate(resolve)).then(async () => {
40959
+ const batch = this.queued;
40960
+ this.queued = [];
40961
+ if (this.flushing === flush) this.flushing = void 0;
40962
+ await this.writeIntents(batch);
40963
+ });
40964
+ this.flushing = flush;
40965
+ this.writes.add(flush);
40966
+ }
40967
+ return this.flushing;
40968
+ }
40969
+ commitPlanned(batch) {
40970
+ this.groupPending.push(...batch);
40971
+ if (this.groupNext === void 0) {
40972
+ const next = this.groupInFlight.then(() => void 0, () => void 0).then(async () => {
40973
+ const carried = this.groupPending;
40974
+ this.groupPending = [];
40975
+ this.groupNext = void 0;
40976
+ await this.writeIntents(carried);
40977
+ });
40978
+ this.groupNext = next;
40979
+ this.groupInFlight = next;
40980
+ }
40981
+ return this.groupNext;
40982
+ }
40983
+ async writeIntents(batch) {
40984
+ await recordAdoptingCreates(batch.map((b) => ({
40985
+ logicalId: b.logicalId,
40986
+ resourceType: b.resourceType,
40987
+ name: b.name
40988
+ })));
40989
+ for (const b of batch) this.recorded.add(b.logicalId);
40990
+ }
40991
+ recordedIntents() {
40992
+ this.recordedRead ??= recordedAdoptingCreates();
40993
+ return this.recordedRead;
40994
+ }
40995
+ async run(candidates) {
40996
+ const input = this.input;
40997
+ const out = /* @__PURE__ */ new Map();
40998
+ const recorded = this.recordedIntents().then((r) => ({
40999
+ ok: true,
41000
+ value: r
41001
+ }), (error) => ({
41002
+ ok: false,
41003
+ error
41004
+ }));
41005
+ const { found, typeFailures } = await lookupAll(input, candidates);
41006
+ let evidence;
41007
+ if (found.size > 0) {
41008
+ this.evidenceRead ??= loadEvidence(input);
41009
+ evidence = await this.evidenceRead.catch((error) => ({ error }));
41010
+ }
41011
+ const recordedResult = await recorded;
41012
+ const unlicensed = [];
41013
+ for (const c of candidates) {
41014
+ const failure = typeFailures.get(c.resourceType);
41015
+ if (failure !== void 0) {
41016
+ out.set(c.logicalId, isAccessDeniedError(failure) ? {
41017
+ kind: "unchecked",
41018
+ error: failure
41019
+ } : {
41020
+ kind: "failed",
41021
+ error: failure
41022
+ });
41023
+ continue;
41024
+ }
41025
+ const holder = found.get(c.logicalId);
41026
+ if (holder === void 0) {
41027
+ out.set(c.logicalId, { kind: "free" });
41028
+ continue;
41029
+ }
41030
+ if (!recordedResult.ok) {
41031
+ out.set(c.logicalId, {
41032
+ kind: "failed",
41033
+ error: recordedResult.error
41034
+ });
41035
+ continue;
41036
+ }
41037
+ const intent = recordedResult.value?.get(c.logicalId);
41038
+ let ownIntent;
41039
+ if (intent !== void 0 && intent.resourceType === c.resourceType && intent.name === c.name) {
41040
+ const abandonedAt = await this.abandonedRunAt();
41041
+ const byIntent = await this.licenseIfCreatedAfterIntent(c, holder, intent, abandonedAt !== void 0 && intent.firstSentAt <= abandonedAt ? abandonedAt : void 0);
41042
+ if (byIntent.kind !== "held") {
41043
+ out.set(c.logicalId, byIntent);
41044
+ continue;
41045
+ }
41046
+ ownIntent = byIntent.ownIntent;
41047
+ }
41048
+ if (evidence === void 0 || "error" in evidence) {
41049
+ out.set(c.logicalId, {
41050
+ kind: "failed",
41051
+ error: evidence !== void 0 && "error" in evidence ? evidence.error : /* @__PURE__ */ new Error("no evidence")
41052
+ });
41053
+ continue;
41054
+ }
41055
+ const named = evidence.namedBy(c.resourceType, holder);
41056
+ if (named !== void 0) {
41057
+ out.set(c.logicalId, await this.licenseIfKeptBefore(c, holder, named));
41058
+ if (out.get(c.logicalId).kind === "held") unlicensed.push(c);
41059
+ continue;
41060
+ }
41061
+ out.set(c.logicalId, {
41062
+ kind: "held",
41063
+ holder,
41064
+ ...ownIntent && { ownIntent }
41065
+ });
41066
+ unlicensed.push(c);
41067
+ }
41068
+ if (unlicensed.length > 0 && input.loadKeptInHistory !== void 0 && evidence !== void 0 && !("error" in evidence) && evidence.retainedAbsent) {
41069
+ let kept;
41070
+ try {
41071
+ this.historyRead ??= input.loadKeptInHistory();
41072
+ kept = await this.historyRead;
41073
+ } catch {
41074
+ kept = [];
41075
+ }
41076
+ for (const c of [...unlicensed]) {
41077
+ const holder = found.get(c.logicalId);
41078
+ const match = kept.find((k) => k.logicalId === c.logicalId && k.resourceType === c.resourceType && probeFoundSameId(c.resourceType, k.physicalId, holder));
41079
+ if (match === void 0) continue;
41080
+ const verdict = await this.licenseIfKeptBefore(c, holder, {
41081
+ via: "history",
41082
+ ...match.keptAt !== void 0 && { keptAt: match.keptAt }
41083
+ });
41084
+ out.set(c.logicalId, verdict);
41085
+ if (verdict.kind !== "held") unlicensed.splice(unlicensed.indexOf(c), 1);
41086
+ }
41087
+ }
41088
+ return {
41089
+ verdicts: out,
41090
+ deleting: unlicensed.filter((c) => STILL_LISTED_WHILE_DELETING.has(c.resourceType))
41091
+ };
41092
+ }
41093
+ async licenseIfKeptBefore(c, holder, named) {
41094
+ const licensed = {
41095
+ kind: "licensed",
41096
+ holder,
41097
+ via: named.via
41098
+ };
41099
+ if (named.via !== "retained" && named.via !== "history" || named.keptAt === void 0) return licensed;
41100
+ if (c.provider.holderCreatedAt === void 0) return licensed;
41101
+ let createdAt;
41102
+ try {
41103
+ createdAt = await withSkipPrefix(true, () => c.provider.holderCreatedAt(c.resourceType, holder));
41104
+ } catch (error) {
41105
+ if (!isAccessDeniedError(error)) return {
41106
+ kind: "failed",
41107
+ error
41108
+ };
41109
+ this.creationTimeDenied(c, holder, error);
41110
+ return licensed;
41111
+ }
41112
+ if (createdAt === void 0 || createdAt <= named.keptAt + KEPT_AT_SKEW_MS) return licensed;
41113
+ return {
41114
+ kind: "held",
41115
+ holder
41116
+ };
41117
+ }
41118
+ async licenseIfCreatedAfterIntent(c, holder, intent, abandonedAt) {
41119
+ const licensed = {
41120
+ kind: "licensed",
41121
+ holder,
41122
+ via: "ledger"
41123
+ };
41124
+ const held = {
41125
+ kind: "held",
41126
+ holder
41127
+ };
41128
+ const byName = abandonedAt !== void 0 ? {
41129
+ kind: "held",
41130
+ holder,
41131
+ ownIntent: "abandoned"
41132
+ } : intent.failedAt !== void 0 ? {
41133
+ kind: "held",
41134
+ holder,
41135
+ ownIntent: "failed"
41136
+ } : licensed;
41137
+ if (c.provider.holderCreatedAt === void 0) return byName;
41138
+ let createdAt;
41139
+ try {
41140
+ createdAt = await withSkipPrefix(true, () => c.provider.holderCreatedAt(c.resourceType, holder));
41141
+ } catch (error) {
41142
+ if (!isAccessDeniedError(error)) return {
41143
+ kind: "failed",
41144
+ error
41145
+ };
41146
+ this.creationTimeDenied(c, holder, error);
41147
+ return byName;
41148
+ }
41149
+ if (createdAt === void 0) return byName;
41150
+ const { firstSentAt, failedAt } = intent;
41151
+ if (Number.isFinite(firstSentAt) && createdAt < firstSentAt - KEPT_AT_SKEW_MS) return held;
41152
+ for (const until of [abandonedAt, failedAt]) if (until !== void 0 && createdAt > until + KEPT_AT_SKEW_MS) return held;
41153
+ return licensed;
41154
+ }
41155
+ creationTimeDenied(c, holder, error) {
41156
+ this.input.warn?.(safeMsg`${c.logicalId}: could not read when ${holder} was created (${describeAwsFailure(error).summary}); ` + "deciding by its name alone, as for a type that reports no creation time. Grant the type's Describe permission to narrow that.");
41157
+ }
41158
+ async abandonedRunAt() {
41159
+ this.abandonedRead ??= ledgerAbandonedAt().catch(() => void 0).then((fromLedger) => {
41160
+ const at = [fromLedger, this.input.abandonedRunAt].filter((v) => typeof v === "number" && Number.isFinite(v));
41161
+ return at.length > 0 ? Math.max(...at) : void 0;
41162
+ });
41163
+ return this.abandonedRead;
41164
+ }
41165
+ async waitOutDeletion(candidates) {
41166
+ const out = /* @__PURE__ */ new Map();
41167
+ let pending = [...candidates];
41168
+ const until = this.timing.now() + this.timing.cooldownMs;
41169
+ const watch = startInterruptWatch("generated-name deletion cooldown");
41170
+ try {
41171
+ while (pending.length > 0 && this.timing.now() < until) {
41172
+ await this.sleepUnlessInterrupted(this.timing.cooldownStepMs, watch);
41173
+ if (watch.isInterrupted()) {
41174
+ for (const c of pending) out.set(c.logicalId, {
41175
+ kind: "failed",
41176
+ error: watch.onInterrupted()
41177
+ });
41178
+ return out;
41179
+ }
41180
+ const { found, typeFailures } = await lookupAll(this.input, pending);
41181
+ pending = pending.filter((c) => {
41182
+ if (typeFailures.has(c.resourceType)) return true;
41183
+ if (found.has(c.logicalId)) return true;
41184
+ out.set(c.logicalId, { kind: "free" });
41185
+ return false;
41186
+ });
41187
+ }
41188
+ return out;
41189
+ } finally {
41190
+ watch.dispose();
41191
+ }
41192
+ }
41193
+ sleepUnlessInterrupted(ms, watch) {
41194
+ if (watch.isInterrupted()) return Promise.resolve();
41195
+ let timer;
41196
+ const interrupted = new Promise((resolve) => {
41197
+ timer = setInterval(() => {
41198
+ if (watch.isInterrupted()) resolve();
41199
+ }, 200);
41200
+ timer.unref?.();
41201
+ });
41202
+ return Promise.race([this.timing.sleep(ms), interrupted]).finally(() => clearInterval(timer));
41203
+ }
41204
+ };
41205
+ function isPermanentRedirect(error) {
41206
+ const e = error;
41207
+ return e?.name === "PermanentRedirect" || e?.$metadata?.httpStatusCode === 301 || e?.$response?.statusCode === 301;
41208
+ }
41209
+ async function lookupAll(input, candidates) {
41210
+ const byType = /* @__PURE__ */ new Map();
41211
+ for (const c of candidates) byType.set(c.resourceType, [...byType.get(c.resourceType) ?? [], c]);
41212
+ const found = /* @__PURE__ */ new Map();
41213
+ const typeFailures = /* @__PURE__ */ new Map();
41214
+ await Promise.all([...byType].map(async ([resourceType, group]) => {
41215
+ try {
41216
+ const holders = await lookupType(input, resourceType, group);
41217
+ for (const c of group) {
41218
+ const holder = holders.get(c.name);
41219
+ if (holder !== void 0) found.set(c.logicalId, holder);
41220
+ }
41221
+ } catch (error) {
41222
+ typeFailures.set(resourceType, error);
41223
+ }
41224
+ }));
41225
+ return {
41226
+ found,
41227
+ typeFailures
41228
+ };
41229
+ }
41230
+ async function loadEvidence(input) {
41231
+ const [journal, retained] = await Promise.all([input.loadJournal(), input.loadRetained()]);
41232
+ const sources = [];
41233
+ for (const record of Object.values(input.records)) if (typeof record?.physicalId === "string") sources.push({
41234
+ via: "record",
41235
+ type: record.resourceType,
41236
+ id: record.physicalId
41237
+ });
41238
+ for (const orphan of input.orphans ?? []) {
41239
+ const s = orphan.state;
41240
+ if (typeof s?.physicalId === "string") sources.push({
41241
+ via: "orphan",
41242
+ type: s.resourceType,
41243
+ id: s.physicalId
41244
+ });
41245
+ }
41246
+ for (const segment of journal?.segments ?? []) for (const op of [...segment.operations ?? [], ...segment.failedOperations ?? []]) if (typeof op.physicalId === "string" && op.physicalId !== "") sources.push({
41247
+ via: "journal",
41248
+ type: op.resourceType,
41249
+ id: op.physicalId
41250
+ });
41251
+ for (const r of retained ?? []) sources.push({
41252
+ via: "retained",
41253
+ type: r.resourceType,
41254
+ id: r.physicalId,
41255
+ ...r.keptAt !== void 0 && { keptAt: r.keptAt }
41256
+ });
41257
+ return {
41258
+ retainedAbsent: retained === null,
41259
+ namedBy: (resourceType, holder) => {
41260
+ const hit = sources.find((s) => s.type === resourceType && probeFoundSameId(resourceType, s.id, holder));
41261
+ return hit === void 0 ? void 0 : {
41262
+ via: hit.via,
41263
+ ...hit.keptAt !== void 0 && { keptAt: hit.keptAt }
41264
+ };
41265
+ }
41266
+ };
41267
+ }
41268
+ async function lookupType(input, resourceType, group) {
41269
+ const provider = group[0].provider;
41270
+ const names = group.map((c) => c.name);
41271
+ return withSkipPrefix(true, async () => {
41272
+ if (provider.lookupNames !== void 0) return provider.lookupNames(resourceType, names, {
41273
+ region: input.region,
41274
+ stackName: input.stackName,
41275
+ propertiesByName: new Map(group.map((c) => [c.name, c.properties]))
41276
+ });
41277
+ const lookup = provider.import?.bind(provider);
41278
+ if (lookup === void 0) return /* @__PURE__ */ new Map();
41279
+ const byName = new Map(group.map((c) => [c.name, c]));
41280
+ const account = ["AWS::SNS::Topic", "AWS::StepFunctions::StateMachine"].includes(resourceType) ? await input.accountInfo() : void 0;
41281
+ const concurrency = PER_NAME_CONCURRENCY[resourceType] ?? DEFAULT_PER_NAME_CONCURRENCY;
41282
+ return lookupEachName(names, `${resourceType}:import`, concurrency, async (name) => {
41283
+ const c = byName.get(name);
41284
+ const arn = account !== void 0 ? createLookupArn(resourceType, name, account) : void 0;
41285
+ if (arn !== void 0 && "unbuildable" in arn) throw new Error(`cannot build the ARN ${resourceType} name ${name} would take`);
41286
+ let found;
41287
+ try {
41288
+ found = await lookup({
41289
+ logicalId: c.logicalId,
41290
+ resourceType,
41291
+ stackName: input.stackName,
41292
+ region: input.region,
41293
+ properties: c.property === void 0 ? c.properties : {
41294
+ ...c.properties,
41295
+ [c.property]: name
41296
+ },
41297
+ ...arn !== void 0 && { knownPhysicalId: arn.arn }
41298
+ });
41299
+ } catch (error) {
41300
+ if (resourceType === "AWS::S3::Bucket" && isPermanentRedirect(error)) return name;
41301
+ throw error;
41302
+ }
41303
+ return found?.physicalId;
41304
+ });
41305
+ });
41306
+ }
41307
+
41308
+ //#endregion
41309
+ //#region src/deployment/kept-in-history.ts
41310
+ function keptInEarlierRecords(records) {
41311
+ const out = [];
41312
+ for (const record of records) for (const [logicalId, raw] of Object.entries(record.resources)) {
41313
+ if (raw === null || typeof raw !== "object") continue;
41314
+ const r = raw;
41315
+ if (typeof r.resourceType !== "string" || typeof r.physicalId !== "string") continue;
41316
+ if (r.physicalId === "") continue;
41317
+ if (!shouldRetainResource(r.deletionPolicy)) continue;
41318
+ out.push({
41319
+ logicalId,
41320
+ resourceType: r.resourceType,
41321
+ physicalId: r.physicalId,
41322
+ ...record.writtenAt !== void 0 && { keptAt: record.writtenAt }
41323
+ });
41324
+ }
41325
+ return out;
41326
+ }
41327
+ function keptInEventHistory(runs, stackName) {
41328
+ const made = /* @__PURE__ */ new Map();
41329
+ const out = [];
41330
+ for (const events of runs) for (const e of events) {
41331
+ if (e.stackName !== stackName || typeof e.logicalId !== "string") continue;
41332
+ if (typeof e.resourceType !== "string") continue;
41333
+ if (e.eventType === "RESOURCE_SUCCEEDED" && (e.operation === "CREATE" || e.operation === "UPDATE") && typeof e.physicalId === "string" && e.physicalId !== "") made.set(e.logicalId, {
41334
+ resourceType: e.resourceType,
41335
+ physicalId: e.physicalId
41336
+ });
41337
+ else if (e.eventType === "RESOURCE_RETAINED") {
41338
+ const m = made.get(e.logicalId);
41339
+ if (m !== void 0 && m.resourceType === e.resourceType) {
41340
+ const keptAt = Date.parse(e.timestamp);
41341
+ out.push({
41342
+ logicalId: e.logicalId,
41343
+ ...m,
41344
+ ...Number.isFinite(keptAt) && { keptAt }
41345
+ });
41346
+ }
41347
+ }
41348
+ }
41349
+ return out;
41350
+ }
41351
+ async function loadKeptInHistory(sources, stackName, region) {
41352
+ const runsOf = stackName.split("~")[0];
41353
+ const [records, runs] = await Promise.all([sources.earlierStateResources(stackName, region).catch(() => []), (async () => {
41354
+ const listed = (await sources.listRuns(runsOf, region)).slice(0, 20);
41355
+ return (await Promise.all(listed.map((r) => sources.readRunEvents(runsOf, region, r.runId).catch(() => null)))).filter((e) => e !== null).reverse();
41356
+ })().catch(() => [])]);
41357
+ return [...keptInEarlierRecords(records), ...keptInEventHistory(runs, stackName)];
41358
+ }
41359
+
39551
41360
  //#endregion
39552
41361
  //#region src/provisioning/canonicalize-properties.ts
39553
41362
  function makeCanonicalizePropertiesFn(registry) {
@@ -40108,6 +41917,9 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
40108
41917
  process.removeListener("SIGINT", sigintHandler);
40109
41918
  throw error;
40110
41919
  }
41920
+ let firstDeployStateGate;
41921
+ let firstDeploy = false;
41922
+ let failed = false;
40111
41923
  try {
40112
41924
  renderer.start();
40113
41925
  const currentStateData = await this.stateBackend.getState(stackName, this.stackRegion);
@@ -40131,7 +41943,14 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
40131
41943
  refuseMalformedOrphanRecords(currentState, stackName, this.stackRegion, this.options.refusalRecovery);
40132
41944
  const migrationPending = currentStateData?.migrationPending ?? false;
40133
41945
  this.logger.debug(`Loaded current state: ${Object.keys(currentState.resources).length} resources`);
40134
- if (this.options.onCurrentStateLoaded) await this.options.onCurrentStateLoaded(stackName, currentStateData?.state);
41946
+ if (this.options.onCurrentStateLoaded) {
41947
+ const gate = this.options.onCurrentStateLoaded(stackName, currentStateData?.state);
41948
+ if (currentStateData === null || currentStateData === void 0) {
41949
+ firstDeployStateGate = gate;
41950
+ firstDeploy = true;
41951
+ gate.catch(() => void 0);
41952
+ } else await gate;
41953
+ }
40135
41954
  try {
40136
41955
  const loaded = await this.stateBackend.loadRollbackJournal(stackName, this.stackRegion);
40137
41956
  const journal = loaded && {
@@ -40262,6 +42081,11 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
40262
42081
  conditions,
40263
42082
  forceStatefulRecreation: this.options.forceStatefulRecreation === true
40264
42083
  });
42084
+ if (firstDeployStateGate !== void 0) {
42085
+ const gate = firstDeployStateGate;
42086
+ firstDeployStateGate = void 0;
42087
+ await gate;
42088
+ }
40265
42089
  if (!this.diffCalculator.hasChanges(changes)) {
40266
42090
  this.logger.info("No changes detected. Stack is up to date.");
40267
42091
  let persistedOutputs = currentState.outputs ?? {};
@@ -40369,7 +42193,34 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
40369
42193
  const readers = [...changes.values()].filter(isNoEchoPromotionOnly).length;
40370
42194
  this.logger.info(safeMsg`No changes detected in the template. Comparing ${String(readers)} resource(s) that read a NoEcho parameter with AWS.`);
40371
42195
  }
40372
- await requireDeploymentApproval({
42196
+ const abandonedRunAt = this.lockManager.abandonedRunEndedBy?.(stackName, this.stackRegion);
42197
+ if (abandonedRunAt !== void 0) await noteAbandonedRun(abandonedRunAt);
42198
+ this.generatedNameGuard = GeneratedNameGuard.start({
42199
+ stackName,
42200
+ region: this.stackRegion,
42201
+ changes: changes.values(),
42202
+ providerFor: (input) => this.providerRegistry.getProviderFor({
42203
+ ...input,
42204
+ quiet: true
42205
+ }),
42206
+ records: currentState.resources,
42207
+ orphans: currentState.orphans,
42208
+ loadJournal: () => this.stateBackend.loadRollbackJournal(stackName, this.stackRegion),
42209
+ loadRetained: () => this.stateBackend.loadRetainedRecord(stackName, this.stackRegion),
42210
+ loadKeptInHistory: () => {
42211
+ const reader = new DeploymentEventsReader(this.stateBackend);
42212
+ return loadKeptInHistory({
42213
+ earlierStateResources: (s, r) => this.stateBackend.earlierStateResources(s, r),
42214
+ listRuns: (s, r) => reader.listRuns(s, r),
42215
+ readRunEvents: (s, r, id) => reader.readRunEvents(s, r, id)
42216
+ }, stackName, this.stackRegion);
42217
+ },
42218
+ accountInfo: () => getAccountInfo(this.stackRegion),
42219
+ warn: (message) => this.logger.warn(message),
42220
+ ...abandonedRunAt !== void 0 && { abandonedRunAt }
42221
+ });
42222
+ if (this.generatedNameGuard !== void 0) this.logger.debug(safeMsg`Generated-name check: looking up ${String(this.generatedNameGuard.size)} planned create(s)`);
42223
+ await checkDestructivePlan({
40373
42224
  options: this.options,
40374
42225
  stackName,
40375
42226
  changes: changes.values(),
@@ -40377,6 +42228,15 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
40377
42228
  template: effectiveTemplate,
40378
42229
  recreateTargetIds: recreateTargetIdsFor(this.options.recreateTargets, stackName)
40379
42230
  });
42231
+ if (await requireDeploymentApproval({
42232
+ options: this.options,
42233
+ stackName,
42234
+ changes: changes.values(),
42235
+ records: currentState.resources,
42236
+ template: effectiveTemplate,
42237
+ recreateTargetIds: recreateTargetIdsFor(this.options.recreateTargets, stackName)
42238
+ })) this.generatedNameGuard?.noteApprovalPrompted();
42239
+ this.generatedNameGuard?.recordPlannedIntents();
40380
42240
  this.resolver.resetPhysicalIdFallbackCount();
40381
42241
  const progress = {
40382
42242
  current: 0,
@@ -40384,16 +42244,29 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
40384
42244
  };
40385
42245
  const { state: newState, actualCounts, completedOperations } = await withProducerRegions(() => this.producerRegionEvidence(currentState), () => this.executeDeployment(effectiveTemplate, currentState, changes, dag, executionLevels, stackName, outputsDigestSource, parameterValues, conditions, currentEtag, progress, migrationPending));
40386
42246
  await this.drainObservedCaptures(newState.resources);
40387
- const newEtag = await this.stateBackend.saveState(stackName, this.stackRegion, this.withParentInfo(conditionVerdicts ? {
42247
+ const [newEtag] = await Promise.all([this.stateBackend.saveState(stackName, this.stackRegion, this.withParentInfo(conditionVerdicts ? {
40388
42248
  ...newState,
40389
42249
  conditionVerdicts
40390
- } : newState));
42250
+ } : newState)), flushKeptForReadoption(this)]);
40391
42251
  this.logger.debug(`State saved (ETag: ${newEtag})`);
40392
- await forgetRecordedCreateTokens(Object.keys(newState.resources));
40393
- const [journaledOrphansLeft] = await Promise.all([this.settleJournalAfterSuccess(stackName, completedOperations, currentState, newState.resources, currentEtag === void 0), this.exportIndexStore ? this.exportIndexStore.updateForStack(stackName, this.stackRegion, importableOutputs({
40394
- ...newState,
40395
- outputs: this.redactOutputs(newState.outputs)
40396
- })) : Promise.resolve()]);
42252
+ const ledgerAndKeptCleanup = (async () => {
42253
+ await forgetRecordedCreateTokens(Object.keys(newState.resources));
42254
+ const readopted = await this.generatedNameGuard?.readoptedFromRetained() ?? [];
42255
+ if (readopted.length > 0) try {
42256
+ const kept = await this.stateBackend.loadRetainedResources(stackName, this.stackRegion);
42257
+ await this.stateBackend.saveRetainedResources(stackName, this.stackRegion, kept.filter((entry) => !readopted.includes(entry.logicalId)));
42258
+ } catch (error) {
42259
+ this.logger.debug(safeMsg`Could not clear re-adopted resources from the kept-resource record: ${describeAwsFailure(error).summary}`);
42260
+ }
42261
+ })();
42262
+ const [journaledOrphansLeft] = await Promise.all([
42263
+ this.settleJournalAfterSuccess(stackName, completedOperations, currentState, newState.resources, currentEtag === void 0),
42264
+ this.exportIndexStore ? this.exportIndexStore.updateForStack(stackName, this.stackRegion, importableOutputs({
42265
+ ...newState,
42266
+ outputs: this.redactOutputs(newState.outputs)
42267
+ })) : Promise.resolve(),
42268
+ ledgerAndKeptCleanup
42269
+ ]);
40397
42270
  const durationMs = Date.now() - startTime;
40398
42271
  const unchangedCount = this.diffCalculator.filterByType(changes, "NO_CHANGE").length + actualCounts.skipped;
40399
42272
  return {
@@ -40404,17 +42277,33 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
40404
42277
  deleteSkipped: actualCounts.deleteSkipped + journaledOrphansLeft,
40405
42278
  updatePartial: actualCounts.updatePartial,
40406
42279
  nestedUpdatePartial: actualCounts.nestedUpdatePartial,
42280
+ ...(actualCounts.crossPrefixKept ?? 0) > 0 && { crossPrefixKept: actualCounts.crossPrefixKept },
40407
42281
  unchanged: unchangedCount,
40408
42282
  durationMs,
40409
42283
  outputs: this.buildDisplayOutputs(template, this.redactOutputs(newState.outputs ?? {})),
40410
42284
  attributeFallbackCount: this.resolver.getPhysicalIdFallbackCount()
40411
42285
  };
42286
+ } catch (error) {
42287
+ failed = true;
42288
+ if (firstDeployStateGate !== void 0) await firstDeployStateGate;
42289
+ throw error;
40412
42290
  } finally {
40413
42291
  try {
40414
42292
  renderer.stop();
40415
42293
  } catch {}
40416
42294
  process.removeListener("SIGINT", sigintHandler);
40417
42295
  this.observedCaptureTasks.clear();
42296
+ try {
42297
+ await flushKeptForReadoption(this);
42298
+ await this.generatedNameGuard?.settle();
42299
+ } catch {}
42300
+ if (firstDeploy && failed && this.options.onFirstDeployLeftNoRecord && !this.options.dryRun) try {
42301
+ const left = await this.stateBackend.getState(stackName, this.stackRegion);
42302
+ const journal = left === null ? await this.stateBackend.loadRollbackJournal(stackName, this.stackRegion) : void 0;
42303
+ if (left === null && !journalCanOwnResources(journal ?? null)) await this.options.onFirstDeployLeftNoRecord(stackName);
42304
+ } catch (error) {
42305
+ this.logger.debug(safeMsg`Could not check whether the failed first deploy left a record: ${describeAwsFailure(error).summary}`);
42306
+ }
40418
42307
  try {
40419
42308
  await this.lockManager.releaseLock(stackName, this.stackRegion);
40420
42309
  this.logger.debug("Lock released");
@@ -40668,6 +42557,7 @@ var DeployEngine = class {
40668
42557
  constructPathTemplate;
40669
42558
  outputsSourceUsable = true;
40670
42559
  attemptedResolvedProps = /* @__PURE__ */ new Map();
42560
+ generatedNameGuard;
40671
42561
  liveTaskLabels = /* @__PURE__ */ new Map();
40672
42562
  retainedOldOnReplacement = /* @__PURE__ */ new Set();
40673
42563
  recreatedUnderSameId = /* @__PURE__ */ new Set();
@@ -40725,6 +42615,7 @@ var DeployEngine = class {
40725
42615
  this.fingerprintChildTemplates = void 0;
40726
42616
  this.perResourceInputFingerprints = /* @__PURE__ */ new Map();
40727
42617
  this.attemptedResolvedProps = /* @__PURE__ */ new Map();
42618
+ this.generatedNameGuard = void 0;
40728
42619
  this.liveTaskLabels = /* @__PURE__ */ new Map();
40729
42620
  this.outputSecrets = /* @__PURE__ */ new Map();
40730
42621
  this.outputsPassSecretMaps = [];
@@ -40955,5 +42846,5 @@ DeployEngine.prototype.replacementNameOrigin = replacementNameOrigin;
40955
42846
  DeployEngine.prototype.orphanedNameCollisionAdvice = orphanedNameCollisionAdvice;
40956
42847
 
40957
42848
  //#endregion
40958
- export { gray as $, prefetchCreateOnlyPropertyPaths as $a, refuseMalformedOutputs as $i, tagPlanWarning as $n, wholeStringLeavesOf as $o, getAccountInfo as $r, partitionSensitiveEnv as $s, redactedDeleteAddressSkip as $t, perType as A, NO_CC_HANDLERS_REASON as Aa, decodeListingKey as Ac, malformedLocalResourceEntriesWarning as Ai, buildFinalSnapshotIdentifier as An, shareLogOnlyValues as Ao, poisonRenderedSpellingsCollidingIn as Ar, rewriteTemplateAssetReferences as As, awaitInFlightSibling as At, INDEX_SETTLE_POLL_INTERVAL_MS as B, isWarmThroughputDecrease as Ba, hasPlainPrototype$1 as Bc, malformedResourceEntriesWarning as Bi, formatDestructiveChange as Bn, isUncertifiedBaselineMaskPosition as Bo, AWS_CREDENTIAL_ENV_KEYS as Br, validateContainerRepoName as Bs, planRollback as Bt, noEchoComparisonForTemplate as C, unreadableOrphanRecords as Ca, ssmResolvedValueType as Cc, hasReadableResources as Ci, maskEventTextWithBoundBags as Cn, createUnionSecretMasker as Co, isTerminationProtectionPropagationError as Cr, AssetPublisher as Cs, isHandledOrphan as Ct, recordedProtectionNote as D, findSilentDropProperties as Da, findLargeInlineResources as Dc, malformedExportNamesWarning as Di, extractDeploymentEventError as Dn, printingCorpusOf as Do, DiffCalculator as Dr, buildAssetRedirectMap as Ds, sameJournaledOrphans as Dt, recordedProtectionEvidence as E, findActionableSilentDrops as Ea, MIGRATE_TMP_PREFIX as Ec, malformedDriftResourcePropertiesWarning as Ei, stateSecretNameNeedles as En, logOnlyValueCount as Eo, reEnableInstanceApiTermination as Er, WorkGraph as Es, makeForeignHolderScan as Et, compensateRemovedDeletionProtection as F, isStatefulRecreateTargetForReplace as Fa, AssemblyReader as Fc, malformedOrphansRefusalMessage as Fi, refusesFinalSnapshot as Fn, markSameGenerationBag as Fo, assertFlagRoleArn as Fr, getBootstrapMarkerKey as Fs, recordAfterRollbackUpdate as Ft, hasCdkAutoDeleteTag as G, configBooleanRefusal as Ga, displayAssemblyPath as Gc, previewableOrphanRecords as Gi, withCreateTokenLedger as Gn, recoverMaskedOutput as Go, s3BucketArn as Gr, describeDockerExecFailure as Gs, seedsDeleteFirstGuard as Gt, waitForIndexesSettled as H, toFiniteNumber as Ha, ownValue as Hc, malformedResourcesWarning as Hi, earliestDefined as Hn, identityKeyFor as Ho, callerEnvCredentials as Hr, buildDockerImage as Hs, recordUnderIdIsNotOwn as Ht, deleteBudgetKey as I, isStatefulRecreateTargetSync as Ia, StageLoadError as Ic, malformedOrphansWarning as Ii, unsupportedFinalSnapshotError as In, redactSecretsForState as Io, assertSendableRoleArn as Ir, isCrossRegionRedirect as Is, demoteSupersededOrphans as It, markRefusedBeforeApplying as J, readConfigString as Ja, clearBucketRegionCache as Jc, refuseMalformedOrphanRecords as Ji, DBPROXY_TAG_OPTIONS as Jn, recordMaskOnlyValue as Jo, s3BucketRegionalDomainName as Jr, finchSecretArgvRefusal as Js, getCurrentProducerRegions as Jt, isTruthyCfnBoolean as K, configIntegerRefusal as Ka, renderAssemblyPathEscape as Kc, refuseDivergentRecordRegionForDestroy as Ki, acquireIdempotencyToken as Kn, carriesSecretMask as Ko, s3BucketDomainName as Kr, describeDockerFailure as Ks, orphanDeleteNeedsIdentity as Kt, resolveDynamoDbDeleteBudgetClock as L, renderStatefulReason as La, processStackMessages as Lc, malformedOutputsRefusalMessage as Li, maskingRetryLogger as Ln, scrubResourceRecord as Lo, explicitRoleArnOrThrow as Lr, parseBootstrapMarker as Ls, isJournaledOrphan as Lt, DynamoDBGlobalTableProvider as M, findReplacedReadersOfRecreateTargets as Ma, PARTITION_TABLE as Mc, malformedOrphanRecordsRefusalMessage as Mi, createPreDeleteFinalSnapshot as Mn, errorCauseChain as Mo, producerRegionsFromState as Mr, BOOTSTRAP_MARKER_PREFIX as Ms, getStackRecords as Mt, DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS as N, shownType as Na, canonicalizeRegion as Nc, malformedOrphanRecordsWarning as Ni, effectiveDeletionPolicy as Nn, maskSecretsInError as No, regionLessSecretName as Nr, assertAssetBucketRegion as Ns, withStackRecords as Nt, PROTECTION_PROPERTY_BY_TYPE as O, unwrittenCreateOnlyReplacement as Oa, uploadCfnTemplate as Oc, malformedExportSourceWarning as Oi, ATOMIC_FINAL_SNAPSHOT_TYPES as On, recordLogOnlyParameterValue as Oo, INTRINSIC_KEYS as Or, createAssetRedirectResolver as Os, replayFailedOperations as Ot, beginDeleteWait as P, MULTI_REGION_RECREATE_BLOCKED_TYPES as Pa, derivePartitionAndUrlSuffix as Pc, malformedOrphanRowsKeptWarning as Pi, isFinalSnapshotError as Pn, maskSecretsInText as Po, applyRoleArnIfSet as Pr, ensureAssetStorage as Ps, removeProtectionTypeList as Pt, cyan as Q, requireConfigString as Qa, refuseMalformedOrphansForOrphan as Qi, tagMapAsList as Qn, recordProvenPublicExpression as Qo, parameterTypeMayLoseSecretIdentity as Qr, isMalformedEnvKey as Qs, redactedDeleteAddressFields as Qt, resolveDynamoDbDeleteBudgetMs as R, WARM_THROUGHPUT_MEMBERS as Ra, defineOwnKey as Rc, malformedOutputsWarning as Ri, constructPathOf as Rn, DYNAMIC_REFERENCE_TOKEN_SCAN as Ro, isIamRoleArn as Rr, readBootstrapMarkerBody as Rs, isReplacementOrphan as Rt, noEchoAttributeNamesOf as S, unpreviewableOrphanRecords as Sa, warnDeprecatedNoPrefixCliFlag as Sc, hasReadableOutputs as Si, journaledOrphanPrintingBag as Sn, carryLogOnlyValues as So, ec2InstanceProtectionSite as Sr, shouldRetainResource as Ss, dropFailedHint as St, noEchoParameterNamesOf as T, withheldAccountClause as Ta, CFN_TEMPLATE_URL_LIMIT as Tc, isReadableResourceEntry as Ti, secretNamePrintingBag as Tn, literalSplitDelimitersOf as To, reDisableInstanceApiTermination as Tr, stringifyValue as Ts, loadJournaledOrphans as Tt, ECR_AUTO_DELETE_IMAGES_TAG as U, coerceCfnBoolean as Ua, absoluteAssemblyPathEscape as Uc, malformedScrubResourceEntriesRefusalMessage as Ui, ledgerForStack as Un, inheritNestedStackParameterAssociations as Uo, envCredentialsAreAssumedRole as Ur, resolveDockerContextDirectory as Us, replacementNeverSwapped as Ut, deleteTableWithIndexBusyRetry as V, toCfnInteger as Va, nullPrototypeRecord as Vc, malformedResourcePropertiesWarning as Vi, RollbackInlinePolicyWriters as Vn, pathCrossesDottedKey as Vo, applyCallerIdentityCredentials as Vr, buildDenyExternalAccessPolicy as Vs, recheckFailedPlan as Vt, S3_AUTO_DELETE_OBJECTS_TAG as W, coerceCfnInteger as Wa, describeFileReadFailure as Wc, malformedStateRefusalMessage as Wi, reserveStackCreateToken as Wn, recordNestedStackParameterExpressions as Wo, isSettledInstanceState as Wr, describeDockerCapturedOutput as Ws, resolveReplacementOldType as Wt, createReadOnlyAttributeHealerFactory as X, requireConfigArray as Xa, refuseMalformedOrphans as Xi, readTagList as Xn, recordNoEchoAttributeValues as Xo, coerceParameterTypedValue as Xr, getDockerCmd as Xs, REDACTED_DELETE_ADDRESS_SKIP_REASON as Xt, formatResourceLine as Y, replayWarn as Ya, resolveBucketRegion as Yc, refuseMalformedOrphanRecordsForDestroy as Yi, planTagDiff as Yn, recordMaskOnlyValuesIn as Yo, s3BucketWebsiteUrl as Yr, formatDockerLoginError as Ys, inheritProducerRegions as Yt, bold as Z, requireConfigObject as Za, refuseMalformedOrphansForDestroy as Zi, refuseMalformedDesiredTags as Zn, recordNoEchoParameterFreshValue as Zo, isUnboundTemplateParameter as Zr, isDockerClientEnvKey as Zs, isRedactedRecordedValue as Zt, makeCanonicalizePropertiesFn as _, repairMalformedOrphansForReadOnly as _a, resolveSkipPrefix as _c, UNREADABLE_RESOURCES_MAP_ROW as _i, isExportAliasCollision as _n, passedNoEchoParametersOf as _o, deleteSkipReason as _r, importableOutputKeys as _s, revertedNestedRowIds as _t, findNestedTemplateTreeDefect as a, refuseMalformedResourceEntriesForImport as aa, escapeRegExp$3 as ac, AWS_NO_VALUE as ai, displayTextOrWithheld as an, sameLambdaFunctionAddress as ao, isCustomResource as ar, isSingleDynamicReferenceToken as as, reachableDivergentParameters as at, serviceTokenBlockingReason as b, repairMalformedResourcePropertiesForReadOnly as ba, resolveUseCdkBootstrapAssets as bc, displayLogicalId as bi, DEFAULT_RESOURCE_WARN_AFTER_MS as bn, valueAtCoordinate as bo, markWaitAbandoned as br, orphansAfterRollback as bs, withNestedStackContext as bt, renderNestedTemplateTreeDefect as c, refuseMalformedResourcePropertiesForDestroy as ca, getDockerImageBySourceHash as cc, refStateLookupFromResource as ci, secretBearingStateKeyWarning as cn, canonicalCoordinates as co, readsNoEchoDeleteValues as cr, S3StateBackend as cs, planOrphanAdoption as ct, conditionInputsFrom as d, refuseMalformedResourcePropertiesForOrphan as da, getDefaultStateBucketName as dc, parameterInputsFor as di, exportNameNoEchoParameters as dn, maskReadbackAtCoordinates as do, withPrintingSecrets as dr, acceptedCreateOnlyDropsOf as ds, displacedPhysicalIdShown as dt, refuseMalformedOutputsForDestroy as ea, redactDockerArgvValues as ec, isImpossibleEmptyStoredAttribute as ei, updatePartialMessage as en, templateResourceTypes as eo, definedAttributes as er, STATE_SOURCED_BASELINE_RULES as es, green as et, parentSuppliedValues as f, refuseMalformedResourcesForDeploy as fa, getLegacyStateBucketName as fc, passedParameterClassesOf as fi, exportNameSecretExposure as fn, maskWholeValue as fo, CloudControlProvider as fr, describeRegionValueKind as fs, dropSettledNestedJournals as ft, mergeNoChangeOutputs as g, repairMalformedOrphanRecordsForReadOnly as ga, resolveCaptureObservedState as gc, UNREADABLE_ORPHANS_CONTAINER_ROW as gi, collectPublishedOutputNames as gn, noEchoOutputsComparison as go, deleteIndeterminateGuards as gr, hasUnverifiableParameterRefusal as gs, revertNestedChildFromJournal as gt, keptWholeReasonText as h, refuseMalformedState as ha, resolveAutoAssetStorage as hc, STATE_RESOURCES_MALFORMED as hi, collectDeclaredOutputNames as hn, noEchoLeavesOf as ho, UNSPECIFIED_SKIP_REASON as hr, hasReasonlessBaselineRefusal as hs, recordDisplacedSkips as ht, childTemplateLoader as i, refuseMalformedResourceEntriesForDestroy as ia, warnFinchArgvExposure as ic, keepsSecretReferenceToken as ii, WITHHELD_NAME_DISPLAY as in, lambdaFunctionNameForMask as io, ccBrokenReason as ir, WHOLE_DYNAMIC_REFERENCE_PATTERN as is, namesAnyDeclaredParameter as it, readProtection as j, hasNoCloudControlHandlers as ja, expectedOwnerParam as jc, malformedLocalResourcesWarning as ji, ccRoutedFinalSnapshotError as jn, createSecretMasker as jo, classifyReplaySecretRegion as jr, AssetModeResolver as js, destroyStackRecordsView as jt, isProtectionValueActive as k, withoutSilentDropProperties as ka, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as kc, malformedLocalOutputsWarning as ki, PRE_DELETE_SNAPSHOT_TYPES as kn, recordLogOnlyValue as ko, IntrinsicFunctionResolver as kr, loadPublishableAssetManifest as ks, replayRollback as kt, templateIdentity as l, refuseMalformedResourcePropertiesForDrift as la, Synthesizer as lc, parseWebACLArn as li, secretSafeKeyDisplay as ln, isMarkedCoordinate as lo, isCustomResourceType as lr, rebuildClientForBucketRegion as ls, NESTED_PENDING_PARENT_REASON as lt, isSecretBearingReferenceString as m, refuseMalformedResourcesForExport as ma, resolveApp as mc, STATE_REGION_DIVERGED as mi, noEchoParameterValueSeed as mn, noEchoCoordinatesOf as mo, unchangedBehindSecretReference as mr, hasReadableExportSet as ms, nestedChildStackName as mt, IMPLICIT_DELETE_DEPENDENCIES as n, refuseMalformedResourceEntries as na, runDockerForeground as nc, carriesFetchableDynamicReference as ni, DeploymentEventsReader as nn, withRetry as no, CC_BROKEN_REASON as nr, STATE_SOURCED_READBACK_RULES as ns, yellow as nt, isAbsoluteAssetPath as o, refuseMalformedResourceEntriesForImportSave as oa, stripControlChars as oc, cfnRefValueFromPhysicalId as oi, exportAliasCollisionScrubWarning as on, DagBuilder as oo, wouldReturnToSdkProvider as or, LockManager as os, resourcesNamingDeclaredParameter as ot, readRecordedConditionVerdicts as p, refuseMalformedResourcesForDestroy as pa, loadCdkJson as pc, JSON_LISTING_HOLE_VALUE as pi, isWholeDynamicReferenceValue as pn, maskedLeafCoordinatesOf as po, slowCcOperationTimeoutMs as pr, exportNamesCarriedFrom as ps, getNestedRevertRun as pt, getPriorAttempts as q, configStringRefusal as qa, resolveAssemblyPath as qc, refuseMalformedNestedChildOutputs as qi, DBPROXY_TAGS_WHAT as qn, provenPublicValue as qo, s3BucketDualStackDomainName as qr, dockerSpawnEnvWithSensitive as qs, findNestedStackTypeChanges as qt, computeImplicitDeleteEdges as r, refuseMalformedResourceEntriesForDeploy as ra, runDockerStreaming as rc, isNamelessDynamicReferenceError as ri, DeploymentEventsStore as rn, canonicalLambdaFunctionName as ro, ProviderRegistry as rr, TEMPLATE_SOURCED_RULES as rs, computeParameterTaint as rt, listNestedTemplateRows as s, refuseMalformedResourceEntriesForOrphan as sa, AssetManifestLoader as sc, isStalePlaceholderArnAttribute as si, secretBearing as sn, TemplateParser as so, TemplateNoEchoReresolver as sr, isLockInfoExpired as ss, makeSiblingClaimReader as st, DeployEngine as t, refuseMalformedResourceAttributesForOrphan as ta, redactedDockerCause as tc, carriesDynamicReference as ti, updatePartialReason as tn, describeTypeWithThrottleRetry as to, stringifyIfAssigned as tr, STATE_SOURCED_CROSS_GENERATION_RULES as ts, red as tt, conditionFingerprint as u, refuseMalformedResourcePropertiesForExport as ua, synthesisStatusMessage as uc, withSharedDrainBudget as ui, carriedExportAliasExposure as un, maskAtCoordinates as uo, getCurrentResourceSecrets as ur, splitImportedOps as us, displacedOpClause as ut, deferredServiceTokenWarning as v, repairMalformedOutputsForReadOnly as va, resolveStateBucketWithDefault as vc, accountArgs as vi, bindingSkippedOutputs as vn, readbackPathFor as vo, withIndeterminateGuard as vr, importableOutputs as vs, withNestedRevertRun as vt, noEchoExactEchoLeavesOf as w, unreadableResourceEntries as wa, CFN_TEMPLATE_BODY_LIMIT as wc, isReadableOrphanRecord as wi, orphanRecordsPrintingBag as wn, hasMaskableValues as wo, observeThenDisableInstanceApiTermination as wr, isSensitiveAttributeName as ws, journaledOrphanLines as wt, withResourceDeadline as x, repairMalformedResourcesForReadOnly as xa, stateBucketExistenceConfirmed as xc, hasReadableOrphans as xi, completedReplayEntries as xn, liveMatchesUnresolvedTokenFrame as xo, disableInstanceApiTermination as xr, orphansCarriedFrom as xs, deleteJournaledOrphans as xt, findServiceTokenRefusals as y, repairMalformedResourceEntriesForReadOnly as ya, resolveStateBucketWithDefaultAndSource as yc, deployRefusesOrphanRowsReason as yi, DEFAULT_RESOURCE_TIMEOUT_MS as yn, readsNoEchoSource as yo, isWaitAbandonedError as yr, isReadableBag as ys, getCurrentNestedStackContext as yt, DELETE_INDEX_WAIT_PROCEED_NOTE as z, coerceWarmThroughput as za, hasOwnKey as zc, malformedRenderedContainersWarning as zi, findDestructiveChanges as zn, dynamicReferenceTokens as zo, sendableAssumeRoleCommand as zr, validateAssetBucketName as zs, planFailedOps as zt };
40959
- //# sourceMappingURL=deploy-engine-D4FdwPCi.js.map
42849
+ export { getPriorAttempts as $, configBooleanRefusal as $a, hasOwnKey as $c, previewableOrphanRecords as $i, acquireIdempotencyToken as $n, recoverMaskedOutput as $o, s3BucketArn as $r, validateAssetBucketName as $s, orphanDeleteNeedsIdentity as $t, noEchoExactEchoLeavesOf as A, unpreviewableOrphanRecords as Aa, resolveCaptureObservedState as Ac, hasReadableOutputs as Ai, maskEventTextWithBoundBags as An, carryLogOnlyValues as Ao, ec2InstanceProtectionSite as Ar, hasUnverifiableParameterRefusal as As, isHandledOrphan as At, beginDeleteWait as B, findReplacedReadersOfRecreateTargets as Ba, MIGRATE_TMP_PREFIX as Bc, malformedOrphanRecordsRefusalMessage as Bi, effectiveDeletionPolicy as Bn, errorCauseChain as Bo, producerRegionsFromState as Br, WorkGraph as Bs, withStackRecords as Bt, recordRetainedForReadoption as C, refuseMalformedState as Ca, Synthesizer as Cc, STATE_RESOURCES_MALFORMED as Ci, collectPublishedOutputNames as Cn, noEchoLeavesOf as Co, deleteIndeterminateGuards as Cr, isNotImplemented as Cs, revertedNestedRowIds as Ct, withResourceDeadline as D, repairMalformedResourceEntriesForReadOnly as Da, loadCdkJson as Dc, deployRefusesOrphanRowsReason as Di, DEFAULT_RESOURCE_WARN_AFTER_MS as Dn, readsNoEchoSource as Do, isWaitAbandonedError as Dr, exportNamesCarriedFrom as Ds, withNestedStackContext as Dt, serviceTokenBlockingReason as E, repairMalformedOutputsForReadOnly as Ea, getLegacyStateBucketName as Ec, accountArgs as Ei, DEFAULT_RESOURCE_TIMEOUT_MS as En, readbackPathFor as Eo, withIndeterminateGuard as Er, describeRegionValueKind as Es, getCurrentNestedStackContext as Et, isProtectionValueActive as F, findSilentDropProperties as Fa, stateBucketExistenceConfirmed as Fc, malformedExportNamesWarning as Fi, ATOMIC_FINAL_SNAPSHOT_TYPES as Fn, printingCorpusOf as Fo, DiffCalculator as Fr, orphansCarriedFrom as Fs, replayFailedOperations as Ft, DELETE_INDEX_WAIT_PROCEED_NOTE as G, renderStatefulReason as Ga, expectedOwnerParam as Gc, malformedOutputsRefusalMessage as Gi, constructPathOf as Gn, scrubResourceRecord as Go, explicitRoleArnOrThrow as Gr, AssetModeResolver as Gs, isReplacementOrphan as Gt, deleteBudgetKey as H, MULTI_REGION_RECREATE_BLOCKED_TYPES as Ha, uploadCfnTemplate as Hc, malformedOrphanRowsKeptWarning as Hi, refusesFinalSnapshot as Hn, maskSecretsInText as Ho, applyRoleArnIfSet as Hr, createAssetRedirectResolver as Hs, recordAfterRollbackUpdate as Ht, perType as I, unwrittenCreateOnlyReplacement as Ia, warnDeprecatedNoPrefixCliFlag as Ic, malformedExportSourceWarning as Ii, PRE_DELETE_SNAPSHOT_TYPES as In, recordLogOnlyParameterValue as Io, INTRINSIC_KEYS as Ir, shouldRetainResource as Is, replayRollback as It, waitForIndexesSettled as J, isWarmThroughputDecrease as Ja, derivePartitionAndUrlSuffix as Jc, malformedResourceEntriesWarning as Ji, RollbackInlinePolicyWriters as Jn, isUncertifiedBaselineMaskPosition as Jo, AWS_CREDENTIAL_ENV_KEYS as Jr, ensureAssetStorage as Js, recheckFailedPlan as Jt, INDEX_SETTLE_POLL_INTERVAL_MS as K, WARM_THROUGHPUT_MEMBERS as Ka, PARTITION_TABLE as Kc, malformedOutputsWarning as Ki, findDestructiveChanges as Kn, DYNAMIC_REFERENCE_TOKEN_SCAN as Ko, isIamRoleArn as Kr, BOOTSTRAP_MARKER_PREFIX as Ks, planFailedOps as Kt, readProtection as L, withoutSilentDropProperties as La, ssmResolvedValueType as Lc, malformedLocalOutputsWarning as Li, buildFinalSnapshotIdentifier as Ln, recordLogOnlyValue as Lo, IntrinsicFunctionResolver as Lr, AssetPublisher as Ls, awaitInFlightSibling as Lt, recordedProtectionEvidence as M, unreadableResourceEntries as Ma, resolveStateBucketWithDefault as Mc, isReadableOrphanRecord as Mi, secretNamePrintingBag as Mn, hasMaskableValues as Mo, observeThenDisableInstanceApiTermination as Mr, importableOutputs as Ms, loadJournaledOrphans as Mt, recordedProtectionNote as N, withheldAccountClause as Na, resolveStateBucketWithDefaultAndSource as Nc, isReadableResourceEntry as Ni, stateSecretNameNeedles as Nn, literalSplitDelimitersOf as No, reDisableInstanceApiTermination as Nr, isReadableBag as Ns, makeForeignHolderScan as Nt, noEchoAttributeNamesOf as O, repairMalformedResourcePropertiesForReadOnly as Oa, resolveApp as Oc, displayLogicalId as Oi, completedReplayEntries as On, valueAtCoordinate as Oo, markWaitAbandoned as Or, hasReadableExportSet as Os, deleteJournaledOrphans as Ot, PROTECTION_PROPERTY_BY_TYPE as P, findActionableSilentDrops as Pa, resolveUseCdkBootstrapAssets as Pc, malformedDriftResourcePropertiesWarning as Pi, extractDeploymentEventError as Pn, logOnlyValueCount as Po, reEnableInstanceApiTermination as Pr, orphansAfterRollback as Ps, sameJournaledOrphans as Pt, isTruthyCfnBoolean as Q, coerceCfnInteger as Qa, defineOwnKey as Qc, malformedStateRefusalMessage as Qi, withCreateTokenLedger as Qn, recordNestedStackParameterExpressions as Qo, isSettledInstanceState as Qr, readBootstrapMarkerBody as Qs, seedsDeleteFirstGuard as Qt, DynamoDBGlobalTableProvider as R, NO_CC_HANDLERS_REASON as Ra, CFN_TEMPLATE_BODY_LIMIT as Rc, malformedLocalResourceEntriesWarning as Ri, ccRoutedFinalSnapshotError as Rn, shareLogOnlyValues as Ro, poisonRenderedSpellingsCollidingIn as Rr, isSensitiveAttributeName as Rs, destroyStackRecordsView as Rt, keptForReadoption as S, refuseMalformedResourcesForExport as Sa, getDockerImageBySourceHash as Sc, STATE_REGION_DIVERGED as Si, collectDeclaredOutputNames as Sn, noEchoCoordinatesOf as So, UNSPECIFIED_SKIP_REASON as Sr, isAccessDenied as Ss, revertNestedChildFromJournal as St, findServiceTokenRefusals as T, repairMalformedOrphansForReadOnly as Ta, getDefaultStateBucketName as Tc, UNREADABLE_RESOURCES_MAP_ROW as Ti, bindingSkippedOutputs as Tn, passedNoEchoParametersOf as To, deleteSkipReason as Tr, acceptedCreateOnlyDropsOf as Ts, collectNestedChildUnaddressed as Tt, resolveDynamoDbDeleteBudgetClock as U, isStatefulRecreateTargetForReplace as Ua, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Uc, malformedOrphansRefusalMessage as Ui, unsupportedFinalSnapshotError as Un, markSameGenerationBag as Uo, assertFlagRoleArn as Ur, loadPublishableAssetManifest as Us, demoteSupersededOrphans as Ut, compensateRemovedDeletionProtection as V, shownType as Va, findLargeInlineResources as Vc, malformedOrphanRecordsWarning as Vi, isFinalSnapshotError as Vn, maskSecretsInError as Vo, regionLessSecretName as Vr, buildAssetRedirectMap as Vs, removeProtectionTypeList as Vt, resolveDynamoDbDeleteBudgetMs as W, isStatefulRecreateTargetSync as Wa, decodeListingKey as Wc, malformedOrphansWarning as Wi, maskingRetryLogger as Wn, redactSecretsForState as Wo, assertSendableRoleArn as Wr, rewriteTemplateAssetReferences as Ws, isJournaledOrphan as Wt, S3_AUTO_DELETE_OBJECTS_TAG as X, toFiniteNumber as Xa, StageLoadError as Xc, malformedResourcesWarning as Xi, ledgerForStack as Xn, identityKeyFor as Xo, callerEnvCredentials as Xr, isCrossRegionRedirect as Xs, replacementNeverSwapped as Xt, ECR_AUTO_DELETE_IMAGES_TAG as Y, toCfnInteger as Ya, AssemblyReader as Yc, malformedResourcePropertiesWarning as Yi, earliestDefined as Yn, pathCrossesDottedKey as Yo, applyCallerIdentityCredentials as Yr, getBootstrapMarkerKey as Ys, recordUnderIdIsNotOwn as Yt, hasCdkAutoDeleteTag as Z, coerceCfnBoolean as Za, processStackMessages as Zc, malformedScrubResourceEntriesRefusalMessage as Zi, reserveStackCreateToken as Zn, inheritNestedStackParameterAssociations as Zo, envCredentialsAreAssumedRole as Zr, parseBootstrapMarker as Zs, resolveReplacementOldType as Zt, makeCanonicalizePropertiesFn as _, refuseMalformedResourcePropertiesForDrift as _a, runDockerStreaming as _c, parseWebACLArn as _i, carriedExportAliasExposure as _n, isMarkedCoordinate as _o, getCurrentResourceSecrets as _r, rebuildClientForBucketRegion as _s, displacedPhysicalIdShown as _t, findNestedTemplateTreeDefect as a, refuseMalformedOrphansForDestroy as aa, describeDockerExecFailure as ac, isUnboundTemplateParameter as ai, displayAssemblyPath as al, redactedDeleteAddressFields as an, requireConfigObject as ao, tagMapAsList as ar, recordNoEchoParameterFreshValue as as, gray as at, lookupEachName as b, refuseMalformedResourcesForDeploy as ba, stripControlChars as bc, passedParameterClassesOf as bi, isWholeDynamicReferenceValue as bn, maskWholeValue as bo, slowCcOperationTimeoutMs as br, applyCrossPrefixScan as bs, nestedChildStackName as bt, renderNestedTemplateTreeDefect as c, refuseMalformedOutputsForDestroy as ca, finchSecretArgvRefusal as cc, isImpossibleEmptyStoredAttribute as ci, clearBucketRegionCache as cl, updatePartialReason as cn, templateResourceTypes as co, stringifyIfAssigned as cr, STATE_SOURCED_BASELINE_RULES as cs, yellow as ct, conditionInputsFrom as d, refuseMalformedResourceEntriesForDeploy as da, isDockerClientEnvKey as dc, isNamelessDynamicReferenceError as di, WITHHELD_NAME_DISPLAY as dn, canonicalLambdaFunctionName as do, ccBrokenReason as dr, TEMPLATE_SOURCED_RULES as ds, reachableDivergentParameters as dt, refuseDivergentRecordRegionForDestroy as ea, validateContainerRepoName as ec, s3BucketDomainName as ei, hasPlainPrototype$1 as el, findNestedStackTypeChanges as en, configIntegerRefusal as eo, DBPROXY_TAGS_WHAT as er, carriesSecretMask as es, markRefusedBeforeApplying as et, parentSuppliedValues as f, refuseMalformedResourceEntriesForDestroy as fa, isMalformedEnvKey as fc, keepsSecretReferenceToken as fi, displayTextOrWithheld as fn, lambdaFunctionNameForMask as fo, isCustomResource as fr, WHOLE_DYNAMIC_REFERENCE_PATTERN as fs, resourcesNamingDeclaredParameter as ft, mergeNoChangeOutputs as g, refuseMalformedResourcePropertiesForDestroy as ga, runDockerForeground as gc, refStateLookupFromResource as gi, secretSafeKeyDisplay as gn, canonicalCoordinates as go, isCustomResourceType as gr, S3StateBackend as gs, displacedOpClause as gt, keptWholeReasonText as h, refuseMalformedResourceEntriesForOrphan as ha, redactedDockerCause as hc, isStalePlaceholderArnAttribute as hi, secretBearingStateKeyWarning as hn, TemplateParser as ho, readsNoEchoDeleteValues as hr, isLockInfoExpired as hs, NESTED_PENDING_PARENT_REASON as ht, childTemplateLoader as i, refuseMalformedOrphans as ia, describeDockerCapturedOutput as ic, coerceParameterTypedValue as ii, describeFileReadFailure as il, isRedactedRecordedValue as in, requireConfigArray as io, refuseMalformedDesiredTags as ir, recordNoEchoAttributeValues as is, cyan as it, noEchoParameterNamesOf as j, unreadableOrphanRecords as ja, resolveSkipPrefix as jc, hasReadableResources as ji, orphanRecordsPrintingBag as jn, createUnionSecretMasker as jo, isTerminationProtectionPropagationError as jr, importableOutputKeys as js, journaledOrphanLines as jt, noEchoComparisonForTemplate as k, repairMalformedResourcesForReadOnly as ka, resolveAutoAssetStorage as kc, hasReadableOrphans as ki, journaledOrphanPrintingBag as kn, liveMatchesUnresolvedTokenFrame as ko, disableInstanceApiTermination as kr, hasReasonlessBaselineRefusal as ks, dropFailedHint as kt, templateIdentity as l, refuseMalformedResourceAttributesForOrphan as la, formatDockerLoginError as lc, carriesDynamicReference as li, resolveBucketRegion as ll, DeploymentEventsReader as ln, describeTypeWithThrottleRetry as lo, CC_BROKEN_REASON as lr, STATE_SOURCED_CROSS_GENERATION_RULES as ls, computeParameterTaint as lt, isSecretBearingReferenceString as m, refuseMalformedResourceEntriesForImportSave as ma, redactDockerArgvValues as mc, cfnRefValueFromPhysicalId as mi, secretBearing as mn, DagBuilder as mo, TemplateNoEchoReresolver as mr, LockManager as ms, planOrphanAdoption as mt, IMPLICIT_DELETE_DEPENDENCIES as n, refuseMalformedOrphanRecords as na, buildDockerImage as nc, s3BucketRegionalDomainName as ni, ownValue as nl, inheritProducerRegions as nn, readConfigString as no, planTagDiff as nr, recordMaskOnlyValue as ns, createReadOnlyAttributeHealerFactory as nt, isAbsoluteAssetPath as o, refuseMalformedOrphansForOrphan as oa, describeDockerFailure as oc, parameterTypeMayLoseSecretIdentity as oi, renderAssemblyPathEscape as ol, redactedDeleteAddressSkip as on, requireConfigString as oo, tagPlanWarning as or, recordProvenPublicExpression as os, green as ot, readRecordedConditionVerdicts as p, refuseMalformedResourceEntriesForImport as pa, partitionSensitiveEnv as pc, AWS_NO_VALUE as pi, exportAliasCollisionScrubWarning as pn, sameLambdaFunctionAddress as po, wouldReturnToSdkProvider as pr, isSingleDynamicReferenceToken as ps, makeSiblingClaimReader as pt, deleteTableWithIndexBusyRetry as q, coerceWarmThroughput as qa, canonicalizeRegion as qc, malformedRenderedContainersWarning as qi, formatDestructiveChange as qn, dynamicReferenceTokens as qo, sendableAssumeRoleCommand as qr, assertAssetBucketRegion as qs, planRollback as qt, computeImplicitDeleteEdges as r, refuseMalformedOrphanRecordsForDestroy as ra, resolveDockerContextDirectory as rc, s3BucketWebsiteUrl as ri, absoluteAssemblyPathEscape as rl, REDACTED_DELETE_ADDRESS_SKIP_REASON as rn, replayWarn as ro, readTagList as rr, recordMaskOnlyValuesIn as rs, bold as rt, listNestedTemplateRows as s, refuseMalformedOutputs as sa, dockerSpawnEnvWithSensitive as sc, getAccountInfo as si, resolveAssemblyPath as sl, updatePartialMessage as sn, prefetchCreateOnlyPropertyPaths as so, definedAttributes as sr, wholeStringLeavesOf as ss, red as st, DeployEngine as t, refuseMalformedNestedChildOutputs as ta, buildDenyExternalAccessPolicy as tc, s3BucketDualStackDomainName as ti, nullPrototypeRecord as tl, getCurrentProducerRegions as tn, configStringRefusal as to, DBPROXY_TAG_OPTIONS as tr, provenPublicValue as ts, formatResourceLine as tt, conditionFingerprint as u, refuseMalformedResourceEntries as ua, getDockerCmd as uc, carriesFetchableDynamicReference as ui, DeploymentEventsStore as un, withRetry as uo, ProviderRegistry as ur, STATE_SOURCED_READBACK_RULES as us, namesAnyDeclaredParameter as ut, chunks as v, refuseMalformedResourcePropertiesForExport as va, warnFinchArgvExposure as vc, withSharedDrainBudget as vi, exportNameNoEchoParameters as vn, maskAtCoordinates as vo, withPrintingSecrets as vr, CrossPrefixReadError as vs, dropSettledNestedJournals as vt, deferredServiceTokenWarning as w, repairMalformedOrphanRecordsForReadOnly as wa, synthesisStatusMessage as wc, UNREADABLE_ORPHANS_CONTAINER_ROW as wi, isExportAliasCollision as wn, noEchoOutputsComparison as wo, deleteLeftInPlace as wr, splitImportedOps as ws, withNestedRevertRun as wt, withApiLimit as x, refuseMalformedResourcesForDestroy as xa, AssetManifestLoader as xc, JSON_LISTING_HOLE_VALUE as xi, noEchoParameterValueSeed as xn, maskedLeafCoordinatesOf as xo, unchangedBehindSecretReference as xr, crossPrefixDeniedWarning as xs, recordDisplacedSkips as xt, isAccessDeniedError as y, refuseMalformedResourcePropertiesForOrphan as ya, escapeRegExp$3 as yc, parameterInputsFor as yi, exportNameSecretExposure as yn, maskReadbackAtCoordinates as yo, CloudControlProvider as yr, CrossPrefixScanCache as ys, getNestedRevertRun as yt, DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS as z, hasNoCloudControlHandlers as za, CFN_TEMPLATE_URL_LIMIT as zc, malformedLocalResourcesWarning as zi, createPreDeleteFinalSnapshot as zn, createSecretMasker as zo, classifyReplaySecretRegion as zr, stringifyValue as zs, getStackRecords as zt };
42850
+ //# sourceMappingURL=deploy-engine-BuPc2Dfc.js.map