@go-to-k/cdkd 0.300.1 → 0.301.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/{asg-provider-BS5i71bK.js → asg-provider-CK2nZE6B.js} +3 -3
  2. package/dist/{asg-provider-BS5i71bK.js.map → asg-provider-CK2nZE6B.js.map} +1 -1
  3. package/dist/aws-failure-text-BEQfs6za.js.map +1 -1
  4. package/dist/cli.js +2 -2
  5. package/dist/{deploy-engine-BK4gG_5A.js → deploy-engine-DEvS2r2M.js} +1697 -1047
  6. package/dist/deploy-engine-DEvS2r2M.js.map +1 -0
  7. package/dist/{iam-policy-targets-DAs3w7QW.js → iam-policy-targets-DFk7zr0Q.js} +27 -2
  8. package/dist/iam-policy-targets-DFk7zr0Q.js.map +1 -0
  9. package/dist/{iam-role-provider-SmvPHPLC.js → iam-role-provider-s7oQ_V1d.js} +4 -10
  10. package/dist/iam-role-provider-s7oQ_V1d.js.map +1 -0
  11. package/dist/index.d.ts +25 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +3 -3
  14. package/dist/{neptune-provider-CqZ1FaqT.js → neptune-provider-BWESr5fm.js} +3 -3
  15. package/dist/{neptune-provider-CqZ1FaqT.js.map → neptune-provider-BWESr5fm.js.map} +1 -1
  16. package/dist/{program-DVPHKsxv.js → program-C_kDHyxY.js} +111 -86
  17. package/dist/program-C_kDHyxY.js.map +1 -0
  18. package/dist/{provider-classes-CDzT2xg0.js → provider-classes-C1xbZd9k.js} +229 -110
  19. package/dist/provider-classes-C1xbZd9k.js.map +1 -0
  20. package/dist/{rds-provider-DraxDuQq.js → rds-provider-BfwQbYRR.js} +3 -3
  21. package/dist/{rds-provider-DraxDuQq.js.map → rds-provider-BfwQbYRR.js.map} +1 -1
  22. package/dist/{version-DTSDiQcV.js → version-BdWNHoO2.js} +2 -2
  23. package/dist/{version-DTSDiQcV.js.map → version-BdWNHoO2.js.map} +1 -1
  24. package/package.json +2 -2
  25. package/dist/deploy-engine-BK4gG_5A.js.map +0 -1
  26. package/dist/iam-policy-targets-DAs3w7QW.js.map +0 -1
  27. package/dist/iam-role-provider-SmvPHPLC.js.map +0 -1
  28. package/dist/program-DVPHKsxv.js.map +0 -1
  29. package/dist/provider-classes-CDzT2xg0.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { r as getAwsClients, s as __exportAll } from "./aws-clients-B_CEQGE1.js";
2
2
  import { A as UNRENDERABLE, B as safeMsg, C as looksLikeCdkdGeneratedName, D as SAFE_MSG_ALTERED_CHAR, E as withoutGeneratedFallbackName, F as displaySafe, H as terminalSafe, I as displayStackName, L as isPasteableIdent, N as displayAwsMessage, O as SECRET_REF_MAX_CODE_POINTS, P as displayIdent, S as getCurrentStackName, T as withStackName, U as truncateCodePoints, a as getLogger, b as generateResourceNameWithFallback, d as installLogLineMaskerSource, f as getLiveRenderer, g as applyDefaultNameForFallback, j as asIsMessage, 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 plainIdentOr } from "./aws-failure-text-BEQfs6za.js";
3
- import { $ as protectedReplacementAdvice, $n as formatRetryClassificationSignals, $t as ambientCredentialConfig, A as isQuotableImportValue, An as SHELL_ACTIVE, At as isAuxiliaryMarkOf, B as isTerminalDeleteFailure, Bn as shellBoundedDisplay, Bt as formatLockOperation, Cn as StateError, Ct as RETRY_AUXILIARY_OWNER, Dn as normalizeAwsError, Dt as createdResourceIdentityBeforeFailure, E as assertRegionMatch, Et as createdBeforeFailure, F as resolveExplicitPhysicalId, Fn as isInertUnquoted, Ft as UNREPRODUCIBLE_LOCK_CLAUSE, G as runDeleteAttempt, Gn as isUpdateUnsupportedError, Gt as DEFAULT_STATE_PREFIX, H as observeThenDisableProtection, Hn as withheldTargetClause, Ht as recoveryCommandFlags, In as pasteableCommand, It as buildForceUnlockCommand, J as DELETION_PROTECTION_DOC_POINTER, Jn as isNameCooldownError, Kn as hasReplayMayCollide, Kt as injectiveKey, L as ProtectionFlipRegistry, Ln as physicalIdShownBesideCommand, M as quotedRemedyLogicalId, Mn as UNSHOWABLE_VALUE, Mt as markCreatedBeforeFailure, N as refusalTypeShown, Nn as commandHole, Ot as hasCreatedBeforeFailure, Pt as replayMadeAnotherResource, Q as pasteableAwsCommand, Qn as markReplayMayCollide, Qt as ambientClientDefaults, R as compensateProtectionFlip, Rn as plainOrDescribed, St as withDerivedNameMasks, T as RESOURCE_NOT_FOUND, U as protectionFlipKey, Ut as sanitizeRecoveryValue, Vn as shellQuote, Vt as formatLockOwner, Wt as CUSTOM_RESOURCE_RESPONSE_PREFIX, Xn as isReplayedNameCollisionFrom, Yn as isRecreateRetryableError, Yt as producerRecordKey, Zt as withPasteableAwsProfile, _t as createMaskedLogSinks, ar as retryClassificationText, at as startInterruptWatch, bn as ResourceUpdateNotSupportedError, bt as maskDeep, cr as isThrottlingError, ct as canonicalizeRoute53QueryName, d as prepareRemovalForUpdate, dr as wrapMaskedAwsError, en as clientDefaultsFor, er as isAmbiguousCcHandlerErrorCode, f as removalWarning, ft as logicalIdShown, gt as stateOrphanRecordRemedy, hn as MalformedProducerRecordRefusalError, ht as segmentAfterAnchor, i as holdsSecretDerivedEntry, in as CdkdError, ir as isTransientServerError, it as isInterruptedWaitError, j as normalizeAwsTagsToCfn, jn as SHELL_ACTIVE_WHY, jt as markAuxiliaryFailure, kt as isAuxiliaryFailure, l as withUnchangedSecretPrincipalLists, ln as DynamicReferenceRegionAmbiguousError, lr as markNonRetryable, m as withoutInjectedRemovals, mn as MacroExpansionError, mt as resourceTypeShown, nn as AccountIdUnavailableError, nr as isIamPropagationError, on as CrossAccountSecretRefusalError, ot as ElapsedBudgetRegistry, pn as LockError, qn as isNameCollisionErrorFrom, rn as AssetError, rr as isRetryableTransientError, sn as DependencyError, sr as isMarkedNonRetryable, st as monotonicNowMs, tn as credentialFingerprint, tr as isAmbiguousOutcomeError, un as IntrinsicResolutionRefusalError, ur as markRedactedCause, ut as compositeIdSeparatorRefusal, vn as ProvisioningError, vt as createMaskedRetryLogger, w as withoutServerErrorRetries, wn as SynthesisError, wt as carryCreatedBeforeFailure, xt as maskerOrIdentity, yn as ResourceTimeoutError, yt as isSecretDerivedValue, z as deleteWithProtectionCompensation, zn as quotedOrDescribed, zt as formatLockExpiry } from "./iam-policy-targets-DAs3w7QW.js";
3
+ import { $ as protectedReplacementAdvice, $t as ambientCredentialConfig, A as isQuotableImportValue, An as SHELL_ACTIVE, At as isAuxiliaryMarkOf, B as isTerminalDeleteFailure, Bn as shellBoundedDisplay, Bt as formatLockOperation, Cn as StateError, Ct as RETRY_AUXILIARY_OWNER, Dn as normalizeAwsError, Dt as createdResourceIdentityBeforeFailure, E as assertRegionMatch, Et as createdBeforeFailure, F as resolveExplicitPhysicalId, Fn as isInertUnquoted, Ft as UNREPRODUCIBLE_LOCK_CLAUSE, G as runDeleteAttempt, Gn as isUpdateUnsupportedError, Gt as DEFAULT_STATE_PREFIX, H as observeThenDisableProtection, Hn as withheldTargetClause, Ht as recoveryCommandFlags, In as pasteableCommand, It as buildForceUnlockCommand, J as DELETION_PROTECTION_DOC_POINTER, Jn as isNameCooldownError, Kn as hasReplayMayCollide, Kt as injectiveKey, L as ProtectionFlipRegistry, Ln as physicalIdShownBesideCommand, M as quotedRemedyLogicalId, Mn as UNSHOWABLE_VALUE, Mt as markCreatedBeforeFailure, N as refusalTypeShown, Nn as commandHole, Ot as hasCreatedBeforeFailure, Pt as replayMadeAnotherResource, Q as pasteableAwsCommand, Qn as markReplayMayCollide, Qt as ambientClientDefaults, R as compensateProtectionFlip, Rn as plainOrDescribed, St as withDerivedNameMasks, T as RESOURCE_NOT_FOUND, U as protectionFlipKey, Ut as sanitizeRecoveryValue, Vn as shellQuote, Vt as formatLockOwner, Wt as CUSTOM_RESOURCE_RESPONSE_PREFIX, Xn as isReplayedNameCollisionFrom, Yn as isRecreateRetryableError, Yt as producerRecordKey, Zt as withPasteableAwsProfile, _t as createMaskedLogSinks, ar as isRetryableTransientError, at as startInterruptWatch, bn as ResourceUpdateNotSupportedError, bt as maskDeep, ct as canonicalizeRoute53QueryName, d as prepareRemovalForUpdate, dr as markNonRetryable, en as clientDefaultsFor, er as formatRetryClassificationSignals, f as removalWarning, fr as markRedactedCause, ft as logicalIdShown, gt as stateOrphanRecordRemedy, hn as MalformedProducerRecordRefusalError, ht as segmentAfterAnchor, i as holdsSecretDerivedEntry, in as CdkdError, ir as isIamPropagationError, it as isInterruptedWaitError, j as normalizeAwsTagsToCfn, jn as SHELL_ACTIVE_WHY, jt as markAuxiliaryFailure, kt as isAuxiliaryFailure, l as withUnchangedSecretPrincipalLists, ln as DynamicReferenceRegionAmbiguousError, lr as isMarkedNonRetryable, m as withoutInjectedRemovals, mn as MacroExpansionError, mt as resourceTypeShown, nn as AccountIdUnavailableError, nr as isAmbiguousOutcomeError, on as CrossAccountSecretRefusalError, or as isTransientServerError, ot as ElapsedBudgetRegistry, pn as LockError, pr as wrapMaskedAwsError, qn as isNameCollisionErrorFrom, rn as AssetError, sn as DependencyError, sr as retryClassificationText, st as monotonicNowMs, tn as credentialFingerprint, tr as isAmbiguousCcHandlerErrorCode, un as IntrinsicResolutionRefusalError, ur as isThrottlingError, ut as compositeIdSeparatorRefusal, vn as ProvisioningError, vt as createMaskedRetryLogger, w as withoutServerErrorRetries, wn as SynthesisError, wt as carryCreatedBeforeFailure, xt as maskerOrIdentity, yn as ResourceTimeoutError, yt as isSecretDerivedValue, z as deleteWithProtectionCompensation, zn as quotedOrDescribed, zt as formatLockExpiry } from "./iam-policy-targets-DFk7zr0Q.js";
4
4
  import { a as setAssumedRoleCredentials, i as getPreAssumeEnvCredentials, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, s as ambientRegion, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-DBXBcg0J.js";
5
- import { t as getCdkdVersion } from "./version-DTSDiQcV.js";
5
+ import { t as getCdkdVersion } from "./version-BdWNHoO2.js";
6
6
  import { createHash, randomUUID } from "node:crypto";
7
7
  import { AsyncLocalStorage } from "node:async_hooks";
8
8
  import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketLifecycleConfigurationCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -2888,6 +2888,12 @@ const DOCKER_CLIENT_ENV_KEYS = /* @__PURE__ */ new Set([
2888
2888
  "USERPROFILE",
2889
2889
  "HOMEDRIVE",
2890
2890
  "HOMEPATH",
2891
+ "COMSPEC",
2892
+ "SYSTEMROOT",
2893
+ "WINDIR",
2894
+ "SYSTEMDRIVE",
2895
+ "PSMODULEPATH",
2896
+ "PSEXECUTIONPOLICYPREFERENCE",
2891
2897
  "DOCKER_HOST",
2892
2898
  "DOCKER_CONTEXT",
2893
2899
  "DOCKER_CONFIG",
@@ -3016,10 +3022,13 @@ const WELL_FORMED_ENV_KEY = /^[^=\0]+$/;
3016
3022
  function isMalformedEnvKey(key) {
3017
3023
  return !WELL_FORMED_ENV_KEY.test(key);
3018
3024
  }
3019
- function partitionSensitiveEnv(env, sensitiveKeys) {
3025
+ function partitionSensitiveEnv(env, sensitiveKeys, options = {}) {
3026
+ const platform = options.platform ?? process.platform;
3020
3027
  const flags = [];
3021
3028
  const sensitiveEnv = {};
3022
3029
  const collisions = [];
3030
+ const names = platform === "win32" ? new Win32EnvNames(Object.keys(process.env)) : void 0;
3031
+ const ambiguous = [];
3023
3032
  for (const [k, v] of Object.entries(env)) {
3024
3033
  if (!sensitiveKeys.has(k)) {
3025
3034
  flags.push("-e", `${k}=${v}`);
@@ -3029,19 +3038,73 @@ function partitionSensitiveEnv(env, sensitiveKeys) {
3029
3038
  collisions.push(k);
3030
3039
  continue;
3031
3040
  }
3041
+ if (names) {
3042
+ if (names.lookup(k).ambiguous) {
3043
+ ambiguous.push(k);
3044
+ continue;
3045
+ }
3046
+ names.replace(k);
3047
+ }
3032
3048
  flags.push("-e", k);
3033
3049
  defineOwnKey(sensitiveEnv, k, v);
3034
3050
  }
3051
+ if (ambiguous.length > 0) getLogger().warn((options.subject === void 0 ? "" : safeMsg`${options.subject}: `) + safeMsg`Sensitive env var(s) ${ambiguous.map((k) => displayIdent(k)).join(", ")} ` + "were NOT passed to the container. On Windows each matches another variable's name (in your environment or another secret) only through a letter-case difference outside A-Z, which cdkd does not treat as the same variable. Rename the secret if the container needs it.");
3052
+ if (names) warnCaseVariantSensitiveKeys(Object.keys(sensitiveEnv), options.subject);
3035
3053
  return {
3036
3054
  flags,
3037
3055
  sensitiveEnv,
3038
3056
  collisions
3039
3057
  };
3040
3058
  }
3041
- function dockerSpawnEnvWithSensitive(sensitiveEnv) {
3059
+ function warnCaseVariantSensitiveKeys(keys, subject) {
3060
+ const byUpper = /* @__PURE__ */ new Map();
3061
+ for (const k of keys) {
3062
+ const upper = asciiUpper(k);
3063
+ const group = byUpper.get(upper);
3064
+ if (group) group.push(k);
3065
+ else byUpper.set(upper, [k]);
3066
+ }
3067
+ for (const group of byUpper.values()) {
3068
+ if (group.length < 2) continue;
3069
+ getLogger().warn((subject === void 0 ? "" : safeMsg`${subject}: `) + safeMsg`Sensitive env vars ${group.map((k) => displayIdent(k)).join(", ")} differ only by letter case. ` + safeMsg`Windows treats them as one variable, so the container receives the value of ` + safeMsg`${displayIdent(group[group.length - 1])} under every one of these names. ` + `Rename them to distinct names if the container needs each value.`);
3070
+ }
3071
+ }
3072
+ function asciiUpper(name) {
3073
+ return name.replace(/[a-z]/g, (c) => c.toUpperCase());
3074
+ }
3075
+ var Win32EnvNames = class {
3076
+ groups = /* @__PURE__ */ new Map();
3077
+ constructor(names) {
3078
+ for (const name of names) {
3079
+ const key = name.toUpperCase();
3080
+ const group = this.groups.get(key);
3081
+ if (group) group.push(name);
3082
+ else this.groups.set(key, [name]);
3083
+ }
3084
+ }
3085
+ lookup(name) {
3086
+ const others = (this.groups.get(name.toUpperCase()) ?? []).filter((n) => n !== name);
3087
+ const folded = asciiUpper(name);
3088
+ return {
3089
+ aliases: others,
3090
+ ambiguous: others.some((n) => asciiUpper(n) !== folded)
3091
+ };
3092
+ }
3093
+ replace(name) {
3094
+ this.groups.set(name.toUpperCase(), [name]);
3095
+ }
3096
+ };
3097
+ function dockerSpawnEnvWithSensitive(sensitiveEnv, platform = process.platform) {
3042
3098
  const env = { ...process.env };
3099
+ const names = platform === "win32" ? new Win32EnvNames(Object.keys(env)) : void 0;
3043
3100
  for (const [k, v] of Object.entries(sensitiveEnv)) {
3044
3101
  if (isMalformedEnvKey(k) || isDockerClientEnvKey(k)) continue;
3102
+ if (names) {
3103
+ const { aliases, ambiguous } = names.lookup(k);
3104
+ if (ambiguous) continue;
3105
+ for (const alias of aliases) delete env[alias];
3106
+ names.replace(k);
3107
+ }
3045
3108
  defineOwnKey(env, k, v);
3046
3109
  }
3047
3110
  return env;
@@ -4895,11 +4958,22 @@ function refuseMalformedOperation(shownStack, where, op) {
4895
4958
  if (o["oldResourceMaySurvive"] !== void 0 && !["kept", "unconfirmed"].includes(o["oldResourceMaySurvive"])) fail("oldResourceMaySurvive", o["oldResourceMaySurvive"], "kept or unconfirmed when present");
4896
4959
  if (o["physicalIdRecoveredFromError"] !== void 0 && typeof o["physicalIdRecoveredFromError"] !== "boolean") fail("physicalIdRecoveredFromError", o["physicalIdRecoveredFromError"], "a boolean when present");
4897
4960
  if (o["physicalIdRecoveredFromError"] !== void 0 && o["changeType"] !== "CREATE") refuseMalformed(shownStack, `${where}.physicalIdRecoveredFromError is only valid on a CREATE (got changeType ${kind(o["changeType"])}).`);
4961
+ const pending = o["pendingCloudControlRequest"];
4962
+ if (pending !== void 0) {
4963
+ const p = pending;
4964
+ if (typeof pending !== "object" || pending === null || Array.isArray(pending) || typeof p["requestToken"] !== "string" || !/^[-A-Za-z0-9+/=]{1,128}$/.test(p["requestToken"]) || typeof p["abandonedAt"] !== "number" || !Number.isFinite(p["abandonedAt"])) fail("pendingCloudControlRequest", pending, "an object with a Cloud Control requestToken and a numeric abandonedAt when present");
4965
+ if (o["changeType"] !== "CREATE" || o["physicalId"] !== void 0 || o["physicalIdRecoveredFromError"] !== void 0) refuseMalformed(shownStack, `${where}.pendingCloudControlRequest is only valid on a CREATE with no physicalId.`);
4966
+ }
4898
4967
  const identity = o["createdResourceIdentity"];
4899
4968
  if (identity !== void 0) {
4900
4969
  if (typeof identity !== "string" || identity === "") fail("createdResourceIdentity", identity, "a non-empty string when present");
4901
4970
  if (typeof o["physicalIdRecoveredFromError"] !== "boolean") refuseMalformed(shownStack, `${where}.createdResourceIdentity is only valid on a CREATE carrying physicalIdRecoveredFromError.`);
4902
4971
  }
4972
+ const unique = o["createdIdentifierUnique"];
4973
+ if (unique !== void 0) {
4974
+ if (typeof unique !== "boolean") fail("createdIdentifierUnique", unique, "a boolean when present");
4975
+ if (typeof o["physicalIdRecoveredFromError"] !== "boolean" || o["provisionedBy"] !== "cc-api") refuseMalformed(shownStack, `${where}.createdIdentifierUnique is only valid on a Cloud Control CREATE carrying physicalIdRecoveredFromError.`);
4976
+ }
4903
4977
  if (o["deletionPolicy"] !== void 0 && ![
4904
4978
  "Delete",
4905
4979
  "Retain",
@@ -6395,6 +6469,25 @@ var S3StateBackend = class S3StateBackend {
6395
6469
  await this.putRawObject(this.getRollbackJournalKey(stackName, region), JSON.stringify(journal, null, 2));
6396
6470
  return removed;
6397
6471
  }
6472
+ async rewriteRollbackJournalFailedOperations(stackName, region, rewrite) {
6473
+ const journal = await this.loadRollbackJournal(stackName, region);
6474
+ if (!journal) return 0;
6475
+ let changed = 0;
6476
+ for (const segment of journal.segments) {
6477
+ if (!segment.failedOperations) continue;
6478
+ const next = [];
6479
+ for (const op of segment.failedOperations) {
6480
+ const out = rewrite(op, segment);
6481
+ if (out !== op) changed++;
6482
+ if (out !== null) next.push(out);
6483
+ }
6484
+ if (next.length === 0) delete segment.failedOperations;
6485
+ else segment.failedOperations = next;
6486
+ }
6487
+ if (changed === 0) return 0;
6488
+ await this.putRawObject(this.getRollbackJournalKey(stackName, region), JSON.stringify(journal, null, 2));
6489
+ return changed;
6490
+ }
6398
6491
  async reduceRollbackJournalToFailedOperations(stackName, region, keep, supersededLogicalIds = [], demote = () => false) {
6399
6492
  const journal = await this.loadRollbackJournal(stackName, region);
6400
6493
  if (!journal) return 0;
@@ -7672,6 +7765,40 @@ function positionByCrossStackSource(bag, source, secrets) {
7672
7765
  return certifiedExpressionForLeaf(secrets, association, bag);
7673
7766
  }
7674
7767
 
7768
+ //#endregion
7769
+ //#region src/deployment/secret-redaction/resolver-outcomes.ts
7770
+ const CONFLICTING = Symbol("cdkd.resolver-outcome.conflicting");
7771
+ const takenBranches = /* @__PURE__ */ new WeakMap();
7772
+ function recordTakenIfBranch(secrets, source, tookTrueBranch) {
7773
+ let taken = takenBranches.get(secrets);
7774
+ if (taken === void 0) {
7775
+ taken = /* @__PURE__ */ new WeakMap();
7776
+ takenBranches.set(secrets, taken);
7777
+ }
7778
+ const previous = taken.get(source);
7779
+ if (previous === void 0) taken.set(source, tookTrueBranch);
7780
+ else if (previous !== tookTrueBranch) taken.set(source, CONFLICTING);
7781
+ }
7782
+ function takenIfBranchOf(secrets, source) {
7783
+ const taken = takenBranches.get(secrets)?.get(source);
7784
+ return taken === CONFLICTING ? void 0 : taken;
7785
+ }
7786
+ const droppedElements = /* @__PURE__ */ new WeakMap();
7787
+ function recordDroppedArrayElements(secrets, source, dropped) {
7788
+ let arrays = droppedElements.get(secrets);
7789
+ if (arrays === void 0) {
7790
+ arrays = /* @__PURE__ */ new WeakMap();
7791
+ droppedElements.set(secrets, arrays);
7792
+ }
7793
+ const previous = arrays.get(source);
7794
+ if (previous === void 0) arrays.set(source, [...dropped]);
7795
+ else if (previous !== CONFLICTING && (previous.length !== dropped.length || previous.some((index, i) => index !== dropped[i]))) arrays.set(source, CONFLICTING);
7796
+ }
7797
+ function droppedArrayElementsOf(secrets, source) {
7798
+ const dropped = droppedElements.get(secrets)?.get(source);
7799
+ return dropped === CONFLICTING ? void 0 : dropped;
7800
+ }
7801
+
7675
7802
  //#endregion
7676
7803
  //#region src/deployment/secret-redaction/physical-name-copies.ts
7677
7804
  const physicalNameCopiesOf = /* @__PURE__ */ new WeakMap();
@@ -8086,6 +8213,34 @@ function positionByInheritedParameter(bag, source, secrets) {
8086
8213
  if (!deepEqualJsonValue(redactSecretsForState(answer, secrets), answer)) return void 0;
8087
8214
  return answer;
8088
8215
  }
8216
+ const REREAD_PSEUDO_PARAMETERS = /* @__PURE__ */ new Set([
8217
+ "AWS::Region",
8218
+ "AWS::Partition",
8219
+ "AWS::URLSuffix"
8220
+ ]);
8221
+ const inheritedPseudoParameters = /* @__PURE__ */ new WeakMap();
8222
+ const POISONED_PSEUDO_PARAMETERS = Symbol("cdkd.inherited-pseudo-parameters.poisoned");
8223
+ function recordInheritedPseudoParameters(inheritedSecrets, values) {
8224
+ let table = inheritedPseudoParameters.get(inheritedSecrets);
8225
+ if (table === POISONED_PSEUDO_PARAMETERS) return;
8226
+ if (table === void 0) {
8227
+ table = /* @__PURE__ */ new Map();
8228
+ inheritedPseudoParameters.set(inheritedSecrets, table);
8229
+ }
8230
+ for (const [name, value] of values) {
8231
+ if (!REREAD_PSEUDO_PARAMETERS.has(name) || typeof value !== "string") continue;
8232
+ const known = table.get(name);
8233
+ if (known !== void 0 && known !== value) {
8234
+ inheritedPseudoParameters.set(inheritedSecrets, POISONED_PSEUDO_PARAMETERS);
8235
+ return;
8236
+ }
8237
+ table.set(name, value);
8238
+ }
8239
+ }
8240
+ function pseudoBindingOf(parentSecrets) {
8241
+ const table = inheritedPseudoParameters.get(parentSecrets);
8242
+ return (name) => table instanceof Map && table.has(name) ? { value: table.get(name) } : void 0;
8243
+ }
8089
8244
  const REREAD_TRIGGERS = /* @__PURE__ */ new Set([
8090
8245
  "Fn::Select",
8091
8246
  "Fn::Split",
@@ -8124,9 +8279,9 @@ function standsForInheritedSecret(parentSecrets, token) {
8124
8279
  return parentSecrets !== void 0 && resolvedPlaintextOf(parentSecrets, token) !== void 0;
8125
8280
  }
8126
8281
  const plainSplit = (text, delimiter) => text.split(delimiter);
8127
- function evaluatePureRead(node, bind, split, choose, depth = 0) {
8282
+ function evaluatePureRead(node, bind, pseudo, split, choose, depth = 0) {
8128
8283
  if (depth > MAX_REREAD_DEPTH) return void 0;
8129
- const evaluate = (child) => evaluatePureRead(child, bind, split, choose, depth + 1);
8284
+ const evaluate = (child) => evaluatePureRead(child, bind, pseudo, split, choose, depth + 1);
8130
8285
  if (typeof node === "string") return node.includes("{{resolve:") ? void 0 : { value: node };
8131
8286
  if (Array.isArray(node)) {
8132
8287
  const out = [];
@@ -8144,13 +8299,15 @@ function evaluatePureRead(node, bind, split, choose, depth = 0) {
8144
8299
  const args = node[key];
8145
8300
  if (key === "Ref") {
8146
8301
  if (args === "AWS::NoValue") return { value: REREAD_NO_VALUE };
8147
- return typeof args === "string" ? bind(args) : void 0;
8302
+ return typeof args === "string" ? bindReadName(args, bind, pseudo) : void 0;
8148
8303
  }
8149
8304
  if (key === "Fn::If") {
8150
8305
  if (!Array.isArray(args) || args.length !== 3 || typeof args[0] !== "string") return void 0;
8151
- return evaluate(choose(args[0]) ? args[1] : args[2]);
8306
+ const tookTrue = choose(args[0], node);
8307
+ if (tookTrue === void 0) return void 0;
8308
+ return evaluate(tookTrue ? args[1] : args[2]);
8152
8309
  }
8153
- if (key === "Fn::Sub") return evaluatePureSub(args, bind, evaluate);
8310
+ if (key === "Fn::Sub") return evaluatePureSub(args, bind, pseudo, evaluate);
8154
8311
  if (!Array.isArray(args) || args.length !== 2) return void 0;
8155
8312
  if (key === "Fn::Select") {
8156
8313
  const index = args[0];
@@ -8174,7 +8331,7 @@ function evaluatePureRead(node, bind, split, choose, depth = 0) {
8174
8331
  return { value: parts.value.join(delimiter) };
8175
8332
  }
8176
8333
  }
8177
- function evaluatePureSub(args, bind, evaluate) {
8334
+ function evaluatePureSub(args, bind, pseudo, evaluate) {
8178
8335
  const [text, variables] = typeof args === "string" ? [args, {}] : Array.isArray(args) && args.length === 2 ? [args[0], args[1]] : [void 0, void 0];
8179
8336
  if (typeof text !== "string" || text.includes("{{resolve:") || !isPlainObject$7(variables)) return;
8180
8337
  const bound = /* @__PURE__ */ new Map();
@@ -8192,8 +8349,8 @@ function evaluatePureSub(args, bind, evaluate) {
8192
8349
  else if (name === "") replacement = match[0];
8193
8350
  else if (bound.has(name)) replacement = bound.get(name);
8194
8351
  else {
8195
- if (name.startsWith("AWS::") || name.includes(".")) return void 0;
8196
- const value = bind(name);
8352
+ if (name.includes(".")) return void 0;
8353
+ const value = bindReadName(name, bind, pseudo);
8197
8354
  if (value === void 0 || typeof value.value !== "string") return void 0;
8198
8355
  replacement = value.value;
8199
8356
  }
@@ -8202,6 +8359,10 @@ function evaluatePureSub(args, bind, evaluate) {
8202
8359
  }
8203
8360
  return { value: out + text.slice(cursor) };
8204
8361
  }
8362
+ function bindReadName(name, bind, pseudo) {
8363
+ if (!name.startsWith("AWS::")) return bind(name);
8364
+ return bind(name) === void 0 ? pseudo(name) : void 0;
8365
+ }
8205
8366
  function collectRereadConditions(node, names, depth = 0) {
8206
8367
  if (depth > MAX_REREAD_DEPTH) return false;
8207
8368
  if (Array.isArray(node)) return node.every((element) => collectRereadConditions(element, names, depth + 1));
@@ -8228,17 +8389,57 @@ function rendersReadTo(spelling, parentSecrets, value) {
8228
8389
  return false;
8229
8390
  }
8230
8391
  const REREAD_UNBOUND = Symbol("cdkd.reread.unbound");
8231
- function rereadAssignments(source, bind, parentSecrets, untakenWhenUnbound = () => false) {
8392
+ const REREAD_PARAMETER_FREE = Symbol("cdkd.reread.parameter-free");
8393
+ function diffSideReading(bind, parentSecrets) {
8394
+ return {
8395
+ bind: (name) => {
8396
+ const bound = bind(name);
8397
+ return bound && { value: redactInheritedParameterValue(parentSecrets, name, bound.value) };
8398
+ },
8399
+ split: (text, delimiter) => splitOutsideDynamicReferences(text, delimiter, (token) => standsForInheritedSecret(parentSecrets, token))
8400
+ };
8401
+ }
8402
+ function rereadUnder(source, bind, parentSecrets, choose) {
8403
+ const pseudo = pseudoBindingOf(parentSecrets);
8404
+ const resolved = evaluatePureRead(source, bind, pseudo, plainSplit, choose);
8405
+ if (resolved === void 0) return void 0;
8406
+ const diffSide = diffSideReading(bind, parentSecrets);
8407
+ const redacted = evaluatePureRead(source, diffSide.bind, pseudo, diffSide.split, choose);
8408
+ if (redacted === void 0) return void 0;
8409
+ if (resolved.value === REREAD_NO_VALUE || redacted.value === REREAD_NO_VALUE) return resolved.value === redacted.value ? REREAD_NO_VALUE : void 0;
8410
+ if (!rendersReadTo(redacted.value, parentSecrets, resolved.value)) return void 0;
8411
+ return {
8412
+ resolved: resolved.value,
8413
+ redacted: redacted.value
8414
+ };
8415
+ }
8416
+ function namesParameterOnBranches(node, choose, isParameter, depth = 0) {
8417
+ if (depth > MAX_REREAD_DEPTH) return true;
8418
+ if (typeof node === "string") return node.includes("{{resolve:");
8419
+ if (Array.isArray(node)) return node.some((element) => namesParameterOnBranches(element, choose, isParameter, depth + 1));
8420
+ if (!isPlainObject$7(node)) return false;
8421
+ if (Object.hasOwn(node, "Fn::If")) {
8422
+ const args = node["Fn::If"];
8423
+ if (Object.keys(node).length !== 1) return true;
8424
+ if (!Array.isArray(args) || args.length !== 3 || typeof args[0] !== "string") return true;
8425
+ const tookTrue = choose(args[0], node);
8426
+ if (tookTrue === void 0) return true;
8427
+ return namesParameterOnBranches(tookTrue ? args[1] : args[2], choose, isParameter, depth + 1);
8428
+ }
8429
+ if (typeof node["Ref"] === "string" && isParameter(node["Ref"])) return true;
8430
+ const sub = node["Fn::Sub"];
8431
+ const text = typeof sub === "string" ? sub : Array.isArray(sub) ? sub[0] : void 0;
8432
+ if (typeof text === "string") {
8433
+ for (const match of text.matchAll(/\$\{([^}!][^}]*)\}/g)) if (isParameter(match[1].trim()) || isParameter(match[1])) return true;
8434
+ }
8435
+ return Object.values(node).some((child) => namesParameterOnBranches(child, choose, isParameter, depth + 1));
8436
+ }
8437
+ function rereadAssignments(source, bind, parentSecrets, untakenWhenUnbound = () => false, parameterFree) {
8232
8438
  if (!holdsRereadTrigger(source)) return void 0;
8233
8439
  const conditions = /* @__PURE__ */ new Set();
8234
8440
  if (!collectRereadConditions(source, conditions)) return void 0;
8235
8441
  if (conditions.size > MAX_REREAD_CONDITIONS) return void 0;
8236
8442
  const names = [...conditions];
8237
- const redactedBind = (name) => {
8238
- const bound = bind(name);
8239
- return bound && { value: redactInheritedParameterValue(parentSecrets, name, bound.value) };
8240
- };
8241
- const tokenSplit = (text, delimiter) => splitOutsideDynamicReferences(text, delimiter, (token) => standsForInheritedSecret(parentSecrets, token));
8242
8443
  const out = [];
8243
8444
  for (let assignment = 0; assignment < 2 ** names.length; assignment++) {
8244
8445
  const choose = (condition) => Math.floor(assignment / 2 ** names.indexOf(condition)) % 2 === 1;
@@ -8248,25 +8449,18 @@ function rereadAssignments(source, bind, parentSecrets, untakenWhenUnbound = ()
8248
8449
  if (bound === void 0 && untakenWhenUnbound(name)) unbound = true;
8249
8450
  return bound;
8250
8451
  };
8251
- const resolved = evaluatePureRead(source, tracked, plainSplit, choose);
8252
- const redacted = resolved && evaluatePureRead(source, redactedBind, tokenSplit, choose);
8452
+ const reread = rereadUnder(source, tracked, parentSecrets, choose);
8253
8453
  if (unbound) out.push(REREAD_UNBOUND);
8254
- else if (resolved === void 0 || redacted === void 0) out.push(void 0);
8255
- else if (resolved.value === REREAD_NO_VALUE || redacted.value === REREAD_NO_VALUE) out.push(resolved.value === redacted.value ? REREAD_NO_VALUE : void 0);
8256
- else if (!rendersReadTo(redacted.value, parentSecrets, resolved.value)) out.push(void 0);
8257
- else out.push({
8258
- resolved: resolved.value,
8259
- redacted: redacted.value
8260
- });
8454
+ else if (reread !== void 0) out.push(reread);
8455
+ else if (parameterFree !== void 0 && !namesParameterOnBranches(source, choose, parameterFree)) out.push(REREAD_PARAMETER_FREE);
8456
+ else out.push(void 0);
8261
8457
  }
8262
8458
  return out;
8263
8459
  }
8264
8460
  function rereadParameterExpression(source, bind, parentSecrets) {
8265
- const assignments = rereadAssignments(source, bind, parentSecrets);
8461
+ const assignments = rereadAssignments(source, bind, parentSecrets, void 0, (name) => bind(name) !== void 0);
8266
8462
  if (assignments === void 0 || assignments.includes(void 0) || assignments.includes(REREAD_UNBOUND)) return;
8267
- const rereads = assignments.filter((entry) => entry !== void 0 && entry !== REREAD_NO_VALUE && entry !== REREAD_UNBOUND);
8268
- if (rereads.length === 0 || ambiguousRereads(rereads)) return void 0;
8269
- return rereads[0];
8463
+ return assignments.find((entry) => entry !== void 0 && entry !== REREAD_NO_VALUE && entry !== REREAD_UNBOUND && entry !== REREAD_PARAMETER_FREE);
8270
8464
  }
8271
8465
  function ambiguousRereads(rereads) {
8272
8466
  return rereads.some((a, i) => rereads.slice(i + 1).some((b) => deepEqualJsonValue(a.resolved, b.resolved) && !deepEqualJsonValue(a.redacted, b.redacted)));
@@ -8275,13 +8469,27 @@ function positionByParameterReread(bag, source, secrets) {
8275
8469
  const reads = inheritedParameterReads.get(secrets);
8276
8470
  if (reads === void 0 || typeof reads === "symbol") return void 0;
8277
8471
  if (typeof bag !== "string" && !Array.isArray(bag)) return void 0;
8278
- const declared = reads.declared;
8279
- const assignments = rereadAssignments(source, (name) => reads.values.has(name) ? { value: reads.values.get(name) } : void 0, reads.parent, (name) => declared?.has(name) === true);
8280
- if (assignments === void 0 || assignments.includes(void 0)) return void 0;
8281
- const matching = assignments.filter((entry) => entry !== void 0 && entry !== REREAD_NO_VALUE && entry !== REREAD_UNBOUND && deepEqualJsonValue(entry.resolved, bag));
8282
- if (matching.length === 0 || ambiguousRereads(matching)) return void 0;
8472
+ if (!holdsRereadTrigger(source)) return void 0;
8473
+ const bind = (name) => reads.values.has(name) ? { value: reads.values.get(name) } : void 0;
8474
+ let unrecorded = false;
8475
+ const recorded = rereadUnder(source, bind, reads.parent, (_, node) => {
8476
+ const taken = takenIfBranchOf(secrets, node);
8477
+ if (taken === void 0) unrecorded = true;
8478
+ return taken;
8479
+ });
8480
+ let answer;
8481
+ if (!unrecorded) {
8482
+ if (recorded === void 0 || recorded === REREAD_NO_VALUE || !deepEqualJsonValue(recorded.resolved, bag)) return;
8483
+ answer = recorded.redacted;
8484
+ } else {
8485
+ const declared = reads.declared;
8486
+ const assignments = rereadAssignments(source, bind, reads.parent, (name) => declared?.has(name) === true);
8487
+ if (assignments === void 0 || assignments.includes(void 0)) return void 0;
8488
+ const matching = assignments.filter((entry) => entry !== void 0 && entry !== REREAD_NO_VALUE && entry !== REREAD_UNBOUND && entry !== REREAD_PARAMETER_FREE && deepEqualJsonValue(entry.resolved, bag));
8489
+ if (matching.length === 0 || ambiguousRereads(matching)) return void 0;
8490
+ answer = matching[0].redacted;
8491
+ }
8283
8492
  if (carriesChildOnlyPlaintext(bag, secrets, reads.parent)) return void 0;
8284
- const answer = matching[0].redacted;
8285
8493
  if (!deepEqualJsonValue(redactSecretsForState(answer, secrets), answer)) return void 0;
8286
8494
  return answer;
8287
8495
  }
@@ -8542,7 +8750,12 @@ function arrayElementDrop(element, depth = 0) {
8542
8750
  if (branches.every((branch) => branch === "always")) return "always";
8543
8751
  return "maybe";
8544
8752
  }
8545
- function sourceElementsKeptFor(source, bagLength) {
8753
+ function sourceElementsKeptFor(source, bagLength, secrets) {
8754
+ const recorded = secrets && droppedArrayElementsOf(secrets, source);
8755
+ if (recorded !== void 0) {
8756
+ if (source.length - recorded.length !== bagLength) return void 0;
8757
+ return source.filter((_, i) => !recorded.includes(i));
8758
+ }
8546
8759
  const drops = source.map((element) => arrayElementDrop(element));
8547
8760
  if (drops.includes("unknown")) return void 0;
8548
8761
  const always = drops.filter((drop) => drop === "always").length;
@@ -8587,8 +8800,7 @@ function corroboratesWhateverResolves(source, depth = 0) {
8587
8800
  }
8588
8801
  function arrayPairsWhateverDrops(source) {
8589
8802
  if (!source.some((element) => isPlainObject$7(element) && (Object.hasOwn(element, "Fn::If") || element["Ref"] === "AWS::NoValue" || Array.isArray(element["Fn::FindInMap"]) && element["Fn::FindInMap"].length > 3))) return true;
8590
- const drops = source.map((element) => arrayElementDrop(element));
8591
- return !drops.includes("unknown") && drops.filter((drop) => drop === "maybe").length <= 1 && source.every((element) => corroboratesWhateverResolves(element));
8803
+ return source.every((element) => corroboratesWhateverResolves(element));
8592
8804
  }
8593
8805
  function redactByPath(bag, source, secrets, rules, secretExpressions, bagIsSameGeneration) {
8594
8806
  if (isDynamicReferenceString(source) && typeof bag === "string") {
@@ -8645,7 +8857,7 @@ function redactByPath(bag, source, secrets, rules, secretExpressions, bagIsSameG
8645
8857
  }
8646
8858
  if (rules.descendArrays && bag.length === source.length) return bag.map((item, i) => redactByPath(item, source[i], secrets, rules, secretExpressions, bagIsSameGeneration));
8647
8859
  if (rules.descendArrays && bag.length < source.length) {
8648
- const kept = sourceElementsKeptFor(source, bag.length);
8860
+ const kept = sourceElementsKeptFor(source, bag.length, secrets);
8649
8861
  if (kept !== void 0 && keptLiteralsCorroborate(kept, bag, secrets)) return bag.map((item, i) => redactByPath(item, kept[i], secrets, rules, secretExpressions, bagIsSameGeneration));
8650
8862
  }
8651
8863
  }
@@ -22925,6 +23137,281 @@ async function resolveSub(subArgs, context, source) {
22925
23137
  return result;
22926
23138
  }
22927
23139
 
23140
+ //#endregion
23141
+ //#region src/deployment/intrinsic-resolver/parameter-secrets.ts
23142
+ function recordInheritedParameterSecrets(parameterName, value, context) {
23143
+ const inherited = context.inheritedSecrets;
23144
+ const recorded = context.recordedSecretValues;
23145
+ if (inherited && recorded) carryLogOnlyValuesCarriedBy(inherited, recorded, value);
23146
+ if (recorded) carryPhysicalNameCopiesCarriedBy(context, recorded, value);
23147
+ if (inherited && recorded) recordInheritedNoEchoListElements(this, inherited, recorded, value, context);
23148
+ if (!inherited || inherited.size === 0 || !recorded) return;
23149
+ const mirror = parameterReadMirrors.get(recorded);
23150
+ const declared = declaredParameterNames(context);
23151
+ recordInheritedParameterValue(recorded, inherited, parameterName, value, declared);
23152
+ if (mirror) recordInheritedParameterValue(mirror, inherited, parameterName, value, declared);
23153
+ for (const [plaintext, expression] of inheritedSecretsCarriedBy(value, inherited)) {
23154
+ const carried = carriedExpressionFor(inherited, parameterName, value, plaintext, expression);
23155
+ recorded.set(plaintext, carried);
23156
+ const parentEntry = context.inheritedReferenceProvenance?.get(carried);
23157
+ if (parentEntry !== void 0) recordReferenceProvenanceEntry(recorded, carried, parentEntry, "read");
23158
+ recordInheritedParameterRead(recorded, inherited, parameterName);
23159
+ if (mirror) recordInheritedParameterRead(mirror, inherited, parameterName);
23160
+ carryFreshNoEchoMark(inherited, recorded, plaintext);
23161
+ }
23162
+ }
23163
+ async function recordInheritedPseudoParameterValues(resolver, inherited) {
23164
+ if (inherited.size === 0) return;
23165
+ const values = /* @__PURE__ */ new Map();
23166
+ for (const name of [
23167
+ "AWS::Region",
23168
+ "AWS::Partition",
23169
+ "AWS::URLSuffix"
23170
+ ]) values.set(name, await resolver.resolvePseudoParameter(name));
23171
+ recordInheritedPseudoParameters(inherited, values);
23172
+ }
23173
+ function declaredParameterNames(context) {
23174
+ const declared = context.template?.Parameters;
23175
+ if (declared === null || typeof declared !== "object") return void 0;
23176
+ let names = declaredNamesCache.get(declared);
23177
+ if (names === void 0) {
23178
+ names = new Set(Object.keys(declared));
23179
+ declaredNamesCache.set(declared, names);
23180
+ }
23181
+ return names;
23182
+ }
23183
+ const declaredNamesCache = /* @__PURE__ */ new WeakMap();
23184
+ const parameterReadMirrors = /* @__PURE__ */ new WeakMap();
23185
+ function mirrorInheritedParameterReads(passSecrets, persistSecrets) {
23186
+ if (passSecrets !== persistSecrets) parameterReadMirrors.set(passSecrets, persistSecrets);
23187
+ }
23188
+ function recordTakenIfBranchIn(context, source, tookTrueBranch) {
23189
+ if (!context.inheritedSecrets || context.inheritedSecrets.size === 0) return;
23190
+ const recorded = context.recordedSecretValues;
23191
+ if (recorded === void 0) return;
23192
+ recordTakenIfBranch(recorded, source, tookTrueBranch);
23193
+ const mirror = parameterReadMirrors.get(recorded);
23194
+ if (mirror) recordTakenIfBranch(mirror, source, tookTrueBranch);
23195
+ }
23196
+ function recordDroppedArrayElementsIn(context, source, dropped) {
23197
+ const recorded = context.recordedSecretValues;
23198
+ if (recorded === void 0) return;
23199
+ recordDroppedArrayElements(recorded, source, dropped);
23200
+ const mirror = parameterReadMirrors.get(recorded);
23201
+ if (mirror) recordDroppedArrayElements(mirror, source, dropped);
23202
+ }
23203
+ function carryPhysicalNameCopiesCarriedBy(context, recorded, value) {
23204
+ const names = context.inheritedPhysicalNameCopies ?? (context.inheritedSecrets && physicalNameCopiesOf.get(context.inheritedSecrets));
23205
+ if (names === void 0 || names.size === 0) return;
23206
+ const leaves = [...wholeStringLeavesOf(value)];
23207
+ for (const name of names) if (leaves.some((leaf) => leaf.includes(name))) recordPhysicalNameCopy(recorded, name);
23208
+ }
23209
+ function carriedExpressionFor(inherited, parameterName, value, plaintext, survivor) {
23210
+ const own = inheritedParameterExpression(inherited, parameterName, plaintext);
23211
+ if (typeof own === "string") return own;
23212
+ return inheritedRenderedToken(inherited, parameterName, value, plaintext) ?? survivor;
23213
+ }
23214
+ function poisonRenderedSpellingsCollidingIn(template, parameterValues, inherited) {
23215
+ if (!inherited || inherited.size === 0 || template === void 0) return;
23216
+ const names = new Set(Object.keys(parameterValues));
23217
+ if (names.size < 2) return;
23218
+ const groups = [];
23219
+ if (template.Resources !== null && typeof template.Resources === "object") for (const resource of Object.values(template.Resources)) groups.push([{
23220
+ node: resource,
23221
+ mode: "resource"
23222
+ }]);
23223
+ if (template.Outputs !== void 0) groups.push(outputsGroupParts(template.Outputs));
23224
+ const bind = (name) => Object.hasOwn(parameterValues, name) ? { value: parameterValues[name] } : void 0;
23225
+ const withdrawn = /* @__PURE__ */ new Set();
23226
+ for (;;) {
23227
+ const fresh = [...collidingRenderedNames(groups.map((parts) => {
23228
+ const reads = /* @__PURE__ */ new Map();
23229
+ for (const { node, mode } of parts) {
23230
+ const reread = mode === "slot" ? void 0 : {
23231
+ bind,
23232
+ inherited
23233
+ };
23234
+ for (const [name, slotRead] of parameterReadsOf(node, names, reread, mode !== "output")) reads.set(name, slotRead || reads.get(name) === true);
23235
+ }
23236
+ return reads;
23237
+ }).filter((reads) => reads.size >= 2), parameterValues, inherited)].filter((name) => !withdrawn.has(name));
23238
+ if (fresh.length === 0) return;
23239
+ for (const name of fresh) {
23240
+ withdrawn.add(name);
23241
+ withdrawRenderedParameterSpelling(inherited, name);
23242
+ }
23243
+ }
23244
+ }
23245
+ function collidingRenderedNames(readsByGroup, parameterValues, inherited) {
23246
+ const withdraw = /* @__PURE__ */ new Set();
23247
+ for (const reads of readsByGroup) {
23248
+ const byPlaintext = /* @__PURE__ */ new Map();
23249
+ const slotReadPlaintexts = /* @__PURE__ */ new Set();
23250
+ for (const [name, slotRead] of reads) {
23251
+ if (!Object.hasOwn(parameterValues, name)) continue;
23252
+ const value = parameterValues[name];
23253
+ for (const [plaintext, survivor] of inheritedSecretsCarriedBy(value, inherited)) {
23254
+ const expression = carriedExpressionFor(inherited, name, value, plaintext, survivor);
23255
+ const rendered = typeof inheritedParameterExpression(inherited, name, plaintext) !== "string" && inheritedRenderedToken(inherited, name, value, plaintext) !== void 0;
23256
+ let byExpression = byPlaintext.get(plaintext);
23257
+ if (byExpression === void 0) {
23258
+ byExpression = /* @__PURE__ */ new Map();
23259
+ byPlaintext.set(plaintext, byExpression);
23260
+ }
23261
+ let renderedNames = byExpression.get(expression);
23262
+ if (renderedNames === void 0) {
23263
+ renderedNames = [];
23264
+ byExpression.set(expression, renderedNames);
23265
+ }
23266
+ if (rendered) renderedNames.push(name);
23267
+ if (slotRead) slotReadPlaintexts.add(plaintext);
23268
+ }
23269
+ }
23270
+ for (const [plaintext, byExpression] of byPlaintext) {
23271
+ if (byExpression.size < 2 || !slotReadPlaintexts.has(plaintext)) continue;
23272
+ for (const renderedNames of byExpression.values()) for (const name of renderedNames) withdraw.add(name);
23273
+ }
23274
+ }
23275
+ return withdraw;
23276
+ }
23277
+ const POSITIONED_INTRINSICS = /* @__PURE__ */ new Set(["Fn::Join", "Fn::Sub"]);
23278
+ function parameterReadsOf(node, names, reread, positionsArrays) {
23279
+ const reads = /* @__PURE__ */ new Map();
23280
+ const add = (name, slotRead) => {
23281
+ reads.set(name, slotRead || reads.get(name) === true);
23282
+ };
23283
+ const visit = (value, slotRead, atLeaf) => {
23284
+ if (Array.isArray(value)) {
23285
+ const mayShrink = atLeaf ? !positionsArrays || !arrayPairsWhateverDrops(value) : value.some((element) => element !== null && typeof element === "object" && !Array.isArray(element) && (Object.hasOwn(element, "Fn::If") || element["Ref"] === "AWS::NoValue"));
23286
+ for (const element of value) visit(element, slotRead || mayShrink, atLeaf && !mayShrink);
23287
+ return;
23288
+ }
23289
+ if (value === null || typeof value !== "object") return;
23290
+ const record = value;
23291
+ const intrinsic = Object.keys(record).some((key) => key === "Ref" || key.startsWith("Fn::"));
23292
+ if (intrinsic && atLeaf && reread !== void 0 && rereadParameterExpression(record, reread.bind, reread.inherited) !== void 0) {
23293
+ for (const name of refNamesIn(record, names)) add(name, false);
23294
+ return;
23295
+ }
23296
+ const ref = record["Ref"];
23297
+ if (typeof ref === "string" && names.has(ref)) add(ref, slotRead);
23298
+ const placeholderSlot = !positionsArrays && unkeyedPlaceholderParts(record, (name) => certifiesPlaceholder(name, reread)) > MAX_UNKEYED_PARTS;
23299
+ const sub = record["Fn::Sub"];
23300
+ const text = typeof sub === "string" ? sub : Array.isArray(sub) ? sub[0] : void 0;
23301
+ if (typeof text === "string") for (const match of text.matchAll(/\$\{([^}!][^}]*)\}/g)) {
23302
+ const name = match[1].trim();
23303
+ if (names.has(name)) add(name, slotRead || placeholderSlot);
23304
+ }
23305
+ for (const [key, child] of Object.entries(record)) visit(child, slotRead || placeholderSlot || key.startsWith("Fn::") && !POSITIONED_INTRINSICS.has(key), atLeaf && !intrinsic);
23306
+ };
23307
+ visit(node, reread === void 0, true);
23308
+ return reads;
23309
+ }
23310
+ const MAX_UNKEYED_PARTS = 1;
23311
+ function certifiesPlaceholder(name, reread) {
23312
+ if (reread === void 0) return false;
23313
+ const bound = reread.bind(name);
23314
+ if (bound === void 0) return false;
23315
+ if (inheritedSecretsCarriedBy(bound.value, reread.inherited).length === 0) return false;
23316
+ return typeof inheritedParameterExpression(reread.inherited, name, bound.value) === "string";
23317
+ }
23318
+ function unkeyedPlaceholderParts(record, certified) {
23319
+ const keyed = (value) => typeof value === "string" || value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 1 && typeof value["Ref"] === "string" && certified(value["Ref"]);
23320
+ if (Object.hasOwn(record, "Fn::Join")) {
23321
+ const args = record["Fn::Join"];
23322
+ if (!Array.isArray(args) || args.length !== 2 || !Array.isArray(args[1])) return Number.POSITIVE_INFINITY;
23323
+ return args[1].filter((part) => !keyed(part)).length;
23324
+ }
23325
+ if (Object.hasOwn(record, "Fn::Sub")) {
23326
+ const args = record["Fn::Sub"];
23327
+ const [template, variables] = typeof args === "string" ? [args, {}] : Array.isArray(args) ? [args[0], args[1]] : [];
23328
+ if (typeof template !== "string" || variables === null || typeof variables !== "object") return Number.POSITIVE_INFINITY;
23329
+ let unkeyed = 0;
23330
+ for (const hit of template.matchAll(/\$\{(!)?([^}]*)\}/g)) {
23331
+ const name = hit[2] ?? "";
23332
+ if (hit[1] === "!" || name === "") continue;
23333
+ if (!(Object.hasOwn(variables, name) ? keyed(variables[name]) : certified(name))) unkeyed += 1;
23334
+ }
23335
+ return unkeyed;
23336
+ }
23337
+ return 0;
23338
+ }
23339
+ function outputsGroupParts(outputs) {
23340
+ if (outputs === null || typeof outputs !== "object" || Array.isArray(outputs)) return [{
23341
+ node: outputs,
23342
+ mode: "slot"
23343
+ }];
23344
+ const parts = [];
23345
+ for (const output of Object.values(outputs)) {
23346
+ if (output === null || typeof output !== "object" || Array.isArray(output)) {
23347
+ parts.push({
23348
+ node: output,
23349
+ mode: "slot"
23350
+ });
23351
+ continue;
23352
+ }
23353
+ const { Value: value, ...rest } = output;
23354
+ parts.push({
23355
+ node: value,
23356
+ mode: "output"
23357
+ }, {
23358
+ node: rest,
23359
+ mode: "slot"
23360
+ });
23361
+ }
23362
+ return parts;
23363
+ }
23364
+ function refNamesIn(node, names) {
23365
+ const found = /* @__PURE__ */ new Set();
23366
+ const visit = (value) => {
23367
+ if (Array.isArray(value)) {
23368
+ for (const element of value) visit(element);
23369
+ return;
23370
+ }
23371
+ if (value === null || typeof value !== "object") return;
23372
+ const record = value;
23373
+ if (typeof record["Ref"] === "string" && names.has(record["Ref"])) found.add(record["Ref"]);
23374
+ const sub = record["Fn::Sub"];
23375
+ const text = typeof sub === "string" ? sub : Array.isArray(sub) ? sub[0] : void 0;
23376
+ if (typeof text === "string") for (const match of text.matchAll(/\$\{([^}!][^}]*)\}/g)) {
23377
+ const name = match[1].trim();
23378
+ if (names.has(name)) found.add(name);
23379
+ }
23380
+ for (const child of Object.values(record)) visit(child);
23381
+ };
23382
+ visit(node);
23383
+ return found;
23384
+ }
23385
+ function recordInheritedNoEchoListElements(resolver, inherited, recorded, value, context) {
23386
+ if (!Array.isArray(value)) return;
23387
+ const parameterValues = [...inherited.keys()].filter((plaintext) => isNoEchoParameterPlaintext(inherited, plaintext));
23388
+ if (parameterValues.length === 0) return;
23389
+ const joined = value.map((element) => String(element)).join(",");
23390
+ const normalized = (plaintext) => plaintext.split(",").map((piece) => piece.trim()).join(",");
23391
+ if (!parameterValues.some((plaintext) => `,${normalized(plaintext)},`.includes(`,${joined},`))) return;
23392
+ for (const element of value) {
23393
+ if (typeof element !== "string" || element.length < 4) continue;
23394
+ recordNoEchoParameterFreshValue(element, recorded, resolver.publicNoEchoTokens(context));
23395
+ }
23396
+ }
23397
+ function recordNoEchoParameterValue(paramDef, value, context) {
23398
+ const bag = context.recordedSecretValues;
23399
+ if (!declaresNoEcho(paramDef) || bag === void 0) return;
23400
+ recordLogOnlyParameterValue(bag, value);
23401
+ recordNoEchoParameterFreshValue(value, bag, this.publicNoEchoTokens(context));
23402
+ }
23403
+ function refuseCoercedInheritedSecret(name, paramDef, userValue, inherited) {
23404
+ if (!inherited || inherited.size === 0) return;
23405
+ const secretPairs = new Map([...inherited].filter(([plaintext]) => !isNoEchoParameterPlaintext(inherited, plaintext)));
23406
+ if (secretPairs.size === 0) return;
23407
+ const carriedBefore = inheritedSecretsCarriedBy(userValue, secretPairs).length;
23408
+ if (carriedBefore === 0) return;
23409
+ if (inheritedSecretsCarriedBy(this.coerceParameterValue(userValue, paramDef.Type), secretPairs).length >= carriedBefore) return;
23410
+ const loggedName = this.displayMasked(name);
23411
+ const loggedType = this.displayMasked(paramDef.Type);
23412
+ throw markNonRetryable(new IntrinsicResolutionRefusalError(`Nested-stack parameter ${quotedRender(loggedName, "'")} is declared ${QUOTABLE_RENDER.test(loggedType) ? `'Type: ${loggedType}'` : "with a Type that is not a plain identifier"}, but the parent stack resolved a SECRET dynamic reference into it. cdkd keeps a resolved secret out of persisted state by rewriting STRING leaves back to their {{resolve:...}} expression; coercing this value to ${quotedRender(loggedType, "'", "that Type")} destroys the plaintext cdkd would have matched on, so the DECRYPTED secret would be left in the child stack's state.json with nothing to redact it back to. Declare ${quotedRender(loggedName, "'", "the parameter")} as 'Type: String' in the nested stack's template (CDK does this by default for cross-stack references), or stop passing a secret reference into it.`, void 0, "NESTED_STACK_SECRET_PARAMETER_TYPE"));
23413
+ }
23414
+
22928
23415
  //#endregion
22929
23416
  //#region src/deployment/intrinsic-resolver/functions.ts
22930
23417
  async function resolveIf(ifArgs, context, source) {
@@ -22946,11 +23433,13 @@ async function resolveIf(ifArgs, context, source) {
22946
23433
  };
22947
23434
  if (!context.conditions || !Object.hasOwn(context.conditions, conditionName)) {
22948
23435
  this.logger.warn(`Condition ${this.displayMasked(String(conditionName), context)} not found in context, assuming false`);
23436
+ recordTakenIfBranchIn(context, source, false);
22949
23437
  return await resolveBranch(valueIfFalse);
22950
23438
  }
22951
23439
  const conditionValue = context.conditions[conditionName];
22952
23440
  const selectedValue = conditionValue ? valueIfTrue : valueIfFalse;
22953
23441
  this.logger.debug(`Resolved Fn::If: condition ${this.logRender(String(conditionName), context)} = ${conditionValue}, selected ${conditionValue ? "true" : "false"} branch`);
23442
+ recordTakenIfBranchIn(context, source, Boolean(conditionValue));
22954
23443
  return await resolveBranch(selectedValue);
22955
23444
  }
22956
23445
  async function resolveEquals(equalsArgs, context) {
@@ -23398,6 +23887,7 @@ function displayLeaf(value, context) {
23398
23887
  //#region src/deployment/intrinsic-resolver/params-conditions.ts
23399
23888
  async function resolveParameters(template, userParameters, options) {
23400
23889
  const inheritedSecrets = options?.inheritedSecrets;
23890
+ if (inheritedSecrets) await recordInheritedPseudoParameterValues(this, inheritedSecrets);
23401
23891
  const maskInherited = (text) => {
23402
23892
  const mask = (value) => inheritedSecrets && hasMaskableValues(inheritedSecrets) ? maskSecretsInText(value, inheritedSecrets) : value;
23403
23893
  return displaySafe(mask(stripControlChars(mask(text))));
@@ -23505,256 +23995,6 @@ async function resolveKeyUnit(key, val, context, abandoned) {
23505
23995
  }
23506
23996
  }
23507
23997
 
23508
- //#endregion
23509
- //#region src/deployment/intrinsic-resolver/parameter-secrets.ts
23510
- function recordInheritedParameterSecrets(parameterName, value, context) {
23511
- const inherited = context.inheritedSecrets;
23512
- const recorded = context.recordedSecretValues;
23513
- if (inherited && recorded) carryLogOnlyValuesCarriedBy(inherited, recorded, value);
23514
- if (recorded) carryPhysicalNameCopiesCarriedBy(context, recorded, value);
23515
- if (inherited && recorded) recordInheritedNoEchoListElements(this, inherited, recorded, value, context);
23516
- if (!inherited || inherited.size === 0 || !recorded) return;
23517
- const mirror = parameterReadMirrors.get(recorded);
23518
- const declared = declaredParameterNames(context);
23519
- recordInheritedParameterValue(recorded, inherited, parameterName, value, declared);
23520
- if (mirror) recordInheritedParameterValue(mirror, inherited, parameterName, value, declared);
23521
- for (const [plaintext, expression] of inheritedSecretsCarriedBy(value, inherited)) {
23522
- const carried = carriedExpressionFor(inherited, parameterName, value, plaintext, expression);
23523
- recorded.set(plaintext, carried);
23524
- const parentEntry = context.inheritedReferenceProvenance?.get(carried);
23525
- if (parentEntry !== void 0) recordReferenceProvenanceEntry(recorded, carried, parentEntry, "read");
23526
- recordInheritedParameterRead(recorded, inherited, parameterName);
23527
- if (mirror) recordInheritedParameterRead(mirror, inherited, parameterName);
23528
- carryFreshNoEchoMark(inherited, recorded, plaintext);
23529
- }
23530
- }
23531
- function declaredParameterNames(context) {
23532
- const declared = context.template?.Parameters;
23533
- if (declared === null || typeof declared !== "object") return void 0;
23534
- let names = declaredNamesCache.get(declared);
23535
- if (names === void 0) {
23536
- names = new Set(Object.keys(declared));
23537
- declaredNamesCache.set(declared, names);
23538
- }
23539
- return names;
23540
- }
23541
- const declaredNamesCache = /* @__PURE__ */ new WeakMap();
23542
- const parameterReadMirrors = /* @__PURE__ */ new WeakMap();
23543
- function mirrorInheritedParameterReads(passSecrets, persistSecrets) {
23544
- if (passSecrets !== persistSecrets) parameterReadMirrors.set(passSecrets, persistSecrets);
23545
- }
23546
- function carryPhysicalNameCopiesCarriedBy(context, recorded, value) {
23547
- const names = context.inheritedPhysicalNameCopies ?? (context.inheritedSecrets && physicalNameCopiesOf.get(context.inheritedSecrets));
23548
- if (names === void 0 || names.size === 0) return;
23549
- const leaves = [...wholeStringLeavesOf(value)];
23550
- for (const name of names) if (leaves.some((leaf) => leaf.includes(name))) recordPhysicalNameCopy(recorded, name);
23551
- }
23552
- function carriedExpressionFor(inherited, parameterName, value, plaintext, survivor) {
23553
- const own = inheritedParameterExpression(inherited, parameterName, plaintext);
23554
- if (typeof own === "string") return own;
23555
- return inheritedRenderedToken(inherited, parameterName, value, plaintext) ?? survivor;
23556
- }
23557
- function poisonRenderedSpellingsCollidingIn(template, parameterValues, inherited) {
23558
- if (!inherited || inherited.size === 0 || template === void 0) return;
23559
- const names = new Set(Object.keys(parameterValues));
23560
- if (names.size < 2) return;
23561
- const groups = [];
23562
- if (template.Resources !== null && typeof template.Resources === "object") for (const resource of Object.values(template.Resources)) groups.push([{
23563
- node: resource,
23564
- mode: "resource"
23565
- }]);
23566
- if (template.Outputs !== void 0) groups.push(outputsGroupParts(template.Outputs));
23567
- const bind = (name) => Object.hasOwn(parameterValues, name) ? { value: parameterValues[name] } : void 0;
23568
- const withdrawn = /* @__PURE__ */ new Set();
23569
- for (;;) {
23570
- const fresh = [...collidingRenderedNames(groups.map((parts) => {
23571
- const reads = /* @__PURE__ */ new Map();
23572
- for (const { node, mode } of parts) {
23573
- const reread = mode === "slot" ? void 0 : {
23574
- bind,
23575
- inherited
23576
- };
23577
- for (const [name, slotRead] of parameterReadsOf(node, names, reread, mode !== "output")) reads.set(name, slotRead || reads.get(name) === true);
23578
- }
23579
- return reads;
23580
- }).filter((reads) => reads.size >= 2), parameterValues, inherited)].filter((name) => !withdrawn.has(name));
23581
- if (fresh.length === 0) return;
23582
- for (const name of fresh) {
23583
- withdrawn.add(name);
23584
- withdrawRenderedParameterSpelling(inherited, name);
23585
- }
23586
- }
23587
- }
23588
- function collidingRenderedNames(readsByGroup, parameterValues, inherited) {
23589
- const withdraw = /* @__PURE__ */ new Set();
23590
- for (const reads of readsByGroup) {
23591
- const byPlaintext = /* @__PURE__ */ new Map();
23592
- const slotReadPlaintexts = /* @__PURE__ */ new Set();
23593
- for (const [name, slotRead] of reads) {
23594
- if (!Object.hasOwn(parameterValues, name)) continue;
23595
- const value = parameterValues[name];
23596
- for (const [plaintext, survivor] of inheritedSecretsCarriedBy(value, inherited)) {
23597
- const expression = carriedExpressionFor(inherited, name, value, plaintext, survivor);
23598
- const rendered = typeof inheritedParameterExpression(inherited, name, plaintext) !== "string" && inheritedRenderedToken(inherited, name, value, plaintext) !== void 0;
23599
- let byExpression = byPlaintext.get(plaintext);
23600
- if (byExpression === void 0) {
23601
- byExpression = /* @__PURE__ */ new Map();
23602
- byPlaintext.set(plaintext, byExpression);
23603
- }
23604
- let renderedNames = byExpression.get(expression);
23605
- if (renderedNames === void 0) {
23606
- renderedNames = [];
23607
- byExpression.set(expression, renderedNames);
23608
- }
23609
- if (rendered) renderedNames.push(name);
23610
- if (slotRead) slotReadPlaintexts.add(plaintext);
23611
- }
23612
- }
23613
- for (const [plaintext, byExpression] of byPlaintext) {
23614
- if (byExpression.size < 2 || !slotReadPlaintexts.has(plaintext)) continue;
23615
- for (const renderedNames of byExpression.values()) for (const name of renderedNames) withdraw.add(name);
23616
- }
23617
- }
23618
- return withdraw;
23619
- }
23620
- const POSITIONED_INTRINSICS = /* @__PURE__ */ new Set(["Fn::Join", "Fn::Sub"]);
23621
- function parameterReadsOf(node, names, reread, positionsArrays) {
23622
- const reads = /* @__PURE__ */ new Map();
23623
- const add = (name, slotRead) => {
23624
- reads.set(name, slotRead || reads.get(name) === true);
23625
- };
23626
- const visit = (value, slotRead, atLeaf) => {
23627
- if (Array.isArray(value)) {
23628
- const mayShrink = atLeaf ? !positionsArrays || !arrayPairsWhateverDrops(value) : value.some((element) => element !== null && typeof element === "object" && !Array.isArray(element) && (Object.hasOwn(element, "Fn::If") || element["Ref"] === "AWS::NoValue"));
23629
- for (const element of value) visit(element, slotRead || mayShrink, atLeaf && !mayShrink);
23630
- return;
23631
- }
23632
- if (value === null || typeof value !== "object") return;
23633
- const record = value;
23634
- const intrinsic = Object.keys(record).some((key) => key === "Ref" || key.startsWith("Fn::"));
23635
- if (intrinsic && atLeaf && reread !== void 0 && rereadParameterExpression(record, reread.bind, reread.inherited) !== void 0) {
23636
- for (const name of refNamesIn(record, names)) add(name, false);
23637
- return;
23638
- }
23639
- const ref = record["Ref"];
23640
- if (typeof ref === "string" && names.has(ref)) add(ref, slotRead);
23641
- const placeholderSlot = !positionsArrays && unkeyedPlaceholderParts(record, (name) => certifiesPlaceholder(name, reread)) > MAX_UNKEYED_PARTS;
23642
- const sub = record["Fn::Sub"];
23643
- const text = typeof sub === "string" ? sub : Array.isArray(sub) ? sub[0] : void 0;
23644
- if (typeof text === "string") for (const match of text.matchAll(/\$\{([^}!][^}]*)\}/g)) {
23645
- const name = match[1].trim();
23646
- if (names.has(name)) add(name, slotRead || placeholderSlot);
23647
- }
23648
- for (const [key, child] of Object.entries(record)) visit(child, slotRead || placeholderSlot || key.startsWith("Fn::") && !POSITIONED_INTRINSICS.has(key), atLeaf && !intrinsic);
23649
- };
23650
- visit(node, reread === void 0, true);
23651
- return reads;
23652
- }
23653
- const MAX_UNKEYED_PARTS = 1;
23654
- function certifiesPlaceholder(name, reread) {
23655
- if (reread === void 0) return false;
23656
- const bound = reread.bind(name);
23657
- if (bound === void 0) return false;
23658
- if (inheritedSecretsCarriedBy(bound.value, reread.inherited).length === 0) return false;
23659
- return typeof inheritedParameterExpression(reread.inherited, name, bound.value) === "string";
23660
- }
23661
- function unkeyedPlaceholderParts(record, certified) {
23662
- const keyed = (value) => typeof value === "string" || value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 1 && typeof value["Ref"] === "string" && certified(value["Ref"]);
23663
- if (Object.hasOwn(record, "Fn::Join")) {
23664
- const args = record["Fn::Join"];
23665
- if (!Array.isArray(args) || args.length !== 2 || !Array.isArray(args[1])) return Number.POSITIVE_INFINITY;
23666
- return args[1].filter((part) => !keyed(part)).length;
23667
- }
23668
- if (Object.hasOwn(record, "Fn::Sub")) {
23669
- const args = record["Fn::Sub"];
23670
- const [template, variables] = typeof args === "string" ? [args, {}] : Array.isArray(args) ? [args[0], args[1]] : [];
23671
- if (typeof template !== "string" || variables === null || typeof variables !== "object") return Number.POSITIVE_INFINITY;
23672
- let unkeyed = 0;
23673
- for (const hit of template.matchAll(/\$\{(!)?([^}]*)\}/g)) {
23674
- const name = hit[2] ?? "";
23675
- if (hit[1] === "!" || name === "") continue;
23676
- if (!(Object.hasOwn(variables, name) ? keyed(variables[name]) : certified(name))) unkeyed += 1;
23677
- }
23678
- return unkeyed;
23679
- }
23680
- return 0;
23681
- }
23682
- function outputsGroupParts(outputs) {
23683
- if (outputs === null || typeof outputs !== "object" || Array.isArray(outputs)) return [{
23684
- node: outputs,
23685
- mode: "slot"
23686
- }];
23687
- const parts = [];
23688
- for (const output of Object.values(outputs)) {
23689
- if (output === null || typeof output !== "object" || Array.isArray(output)) {
23690
- parts.push({
23691
- node: output,
23692
- mode: "slot"
23693
- });
23694
- continue;
23695
- }
23696
- const { Value: value, ...rest } = output;
23697
- parts.push({
23698
- node: value,
23699
- mode: "output"
23700
- }, {
23701
- node: rest,
23702
- mode: "slot"
23703
- });
23704
- }
23705
- return parts;
23706
- }
23707
- function refNamesIn(node, names) {
23708
- const found = /* @__PURE__ */ new Set();
23709
- const visit = (value) => {
23710
- if (Array.isArray(value)) {
23711
- for (const element of value) visit(element);
23712
- return;
23713
- }
23714
- if (value === null || typeof value !== "object") return;
23715
- const record = value;
23716
- if (typeof record["Ref"] === "string" && names.has(record["Ref"])) found.add(record["Ref"]);
23717
- const sub = record["Fn::Sub"];
23718
- const text = typeof sub === "string" ? sub : Array.isArray(sub) ? sub[0] : void 0;
23719
- if (typeof text === "string") for (const match of text.matchAll(/\$\{([^}!][^}]*)\}/g)) {
23720
- const name = match[1].trim();
23721
- if (names.has(name)) found.add(name);
23722
- }
23723
- for (const child of Object.values(record)) visit(child);
23724
- };
23725
- visit(node);
23726
- return found;
23727
- }
23728
- function recordInheritedNoEchoListElements(resolver, inherited, recorded, value, context) {
23729
- if (!Array.isArray(value)) return;
23730
- const parameterValues = [...inherited.keys()].filter((plaintext) => isNoEchoParameterPlaintext(inherited, plaintext));
23731
- if (parameterValues.length === 0) return;
23732
- const joined = value.map((element) => String(element)).join(",");
23733
- const normalized = (plaintext) => plaintext.split(",").map((piece) => piece.trim()).join(",");
23734
- if (!parameterValues.some((plaintext) => `,${normalized(plaintext)},`.includes(`,${joined},`))) return;
23735
- for (const element of value) {
23736
- if (typeof element !== "string" || element.length < 4) continue;
23737
- recordNoEchoParameterFreshValue(element, recorded, resolver.publicNoEchoTokens(context));
23738
- }
23739
- }
23740
- function recordNoEchoParameterValue(paramDef, value, context) {
23741
- const bag = context.recordedSecretValues;
23742
- if (!declaresNoEcho(paramDef) || bag === void 0) return;
23743
- recordLogOnlyParameterValue(bag, value);
23744
- recordNoEchoParameterFreshValue(value, bag, this.publicNoEchoTokens(context));
23745
- }
23746
- function refuseCoercedInheritedSecret(name, paramDef, userValue, inherited) {
23747
- if (!inherited || inherited.size === 0) return;
23748
- const secretPairs = new Map([...inherited].filter(([plaintext]) => !isNoEchoParameterPlaintext(inherited, plaintext)));
23749
- if (secretPairs.size === 0) return;
23750
- const carriedBefore = inheritedSecretsCarriedBy(userValue, secretPairs).length;
23751
- if (carriedBefore === 0) return;
23752
- if (inheritedSecretsCarriedBy(this.coerceParameterValue(userValue, paramDef.Type), secretPairs).length >= carriedBefore) return;
23753
- const loggedName = this.displayMasked(name);
23754
- const loggedType = this.displayMasked(paramDef.Type);
23755
- throw markNonRetryable(new IntrinsicResolutionRefusalError(`Nested-stack parameter ${quotedRender(loggedName, "'")} is declared ${QUOTABLE_RENDER.test(loggedType) ? `'Type: ${loggedType}'` : "with a Type that is not a plain identifier"}, but the parent stack resolved a SECRET dynamic reference into it. cdkd keeps a resolved secret out of persisted state by rewriting STRING leaves back to their {{resolve:...}} expression; coercing this value to ${quotedRender(loggedType, "'", "that Type")} destroys the plaintext cdkd would have matched on, so the DECRYPTED secret would be left in the child stack's state.json with nothing to redact it back to. Declare ${quotedRender(loggedName, "'", "the parameter")} as 'Type: String' in the nested stack's template (CDK does this by default for cross-stack references), or stop passing a secret reference into it.`, void 0, "NESTED_STACK_SECRET_PARAMETER_TYPE"));
23756
- }
23757
-
23758
23998
  //#endregion
23759
23999
  //#region src/deployment/intrinsic-resolver/refs.ts
23760
24000
  function nameIsNeverAResource(logicalId, context) {
@@ -23931,7 +24171,18 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
23931
24171
  if (typeof value === "string" && value.includes("{{resolve:")) return await this.resolveTemplateLeafReferences(value, context);
23932
24172
  return value;
23933
24173
  }
23934
- if (Array.isArray(value)) return (await allSettledKeepingFirstRejection(() => value.map((v) => this.resolveValue(v, context)), (pending) => this.warnAbandonedParts(pending))).filter((v) => v !== AWS_NO_VALUE);
24174
+ if (Array.isArray(value)) {
24175
+ const resolved = await allSettledKeepingFirstRejection(() => value.map((v) => this.resolveValue(v, context)), (pending) => this.warnAbandonedParts(pending));
24176
+ const kept = resolved.filter((v) => v !== AWS_NO_VALUE);
24177
+ if (kept.length !== resolved.length) {
24178
+ const dropped = [];
24179
+ resolved.forEach((v, i) => {
24180
+ if (v === AWS_NO_VALUE) dropped.push(i);
24181
+ });
24182
+ recordDroppedArrayElementsIn(context, value, dropped);
24183
+ }
24184
+ return kept;
24185
+ }
23935
24186
  const obj = value;
23936
24187
  if ("Ref" in obj) {
23937
24188
  const logicalId = obj["Ref"];
@@ -24919,8 +25170,9 @@ function ec2InstanceProtectionSite(instanceId, region) {
24919
25170
  //#endregion
24920
25171
  //#region src/provisioning/wait-abandoned.ts
24921
25172
  const WAIT_ABANDONED_MARKER = Symbol.for("cdkd.waitAbandoned");
25173
+ const ABANDONED_REQUEST = Symbol.for("cdkd.waitAbandoned.request");
24922
25174
  const MAX_CAUSE_CHAIN_DEPTH = 5;
24923
- function markWaitAbandoned(error) {
25175
+ function markWaitAbandoned(error, request) {
24924
25176
  if (!Object.isExtensible(error)) return error;
24925
25177
  Object.defineProperty(error, WAIT_ABANDONED_MARKER, {
24926
25178
  value: true,
@@ -24928,6 +25180,12 @@ function markWaitAbandoned(error) {
24928
25180
  configurable: true,
24929
25181
  writable: false
24930
25182
  });
25183
+ if (request !== void 0) Object.defineProperty(error, ABANDONED_REQUEST, {
25184
+ value: Object.freeze({ ...request }),
25185
+ enumerable: false,
25186
+ configurable: true,
25187
+ writable: false
25188
+ });
24931
25189
  return error;
24932
25190
  }
24933
25191
  function isWaitAbandonedError(error) {
@@ -24938,6 +25196,14 @@ function isWaitAbandonedError(error) {
24938
25196
  }
24939
25197
  return false;
24940
25198
  }
25199
+ function abandonedRequestOf(error) {
25200
+ let current = error;
25201
+ for (let depth = 0; depth < MAX_CAUSE_CHAIN_DEPTH && current != null; depth++) {
25202
+ const request = current[ABANDONED_REQUEST];
25203
+ if (request !== void 0) return request;
25204
+ current = current.cause;
25205
+ }
25206
+ }
24941
25207
 
24942
25208
  //#endregion
24943
25209
  //#region src/provisioning/ec2-volume-delete.ts
@@ -26195,9 +26461,18 @@ var CloudControlWaitAbandonedError = class CloudControlWaitAbandonedError extend
26195
26461
  this.lastSeenIdentifier = lastSeenIdentifier;
26196
26462
  this.name = "CloudControlWaitAbandonedError";
26197
26463
  Object.setPrototypeOf(this, CloudControlWaitAbandonedError.prototype);
26198
- markWaitAbandoned(this);
26464
+ markWaitAbandoned(this, {
26465
+ requestToken,
26466
+ operation: ccOperation
26467
+ });
26199
26468
  }
26200
26469
  };
26470
+ function failedOutcome$1(errorCode, identifier) {
26471
+ return identifier && errorCode !== "AlreadyExists" && errorCode !== "ResourceConflict" ? {
26472
+ status: "failed",
26473
+ remnant: identifier
26474
+ } : { status: "failed" };
26475
+ }
26201
26476
  const POLL_TRANSPORT_ERROR_CODES = /* @__PURE__ */ new Set([
26202
26477
  "ECONNRESET",
26203
26478
  "ECONNREFUSED",
@@ -26525,7 +26800,7 @@ var CloudControlProvider = class {
26525
26800
  const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
26526
26801
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
26527
26802
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
26528
- const { ASGProvider } = await import("./asg-provider-BS5i71bK.js");
26803
+ const { ASGProvider } = await import("./asg-provider-CK2nZE6B.js");
26529
26804
  const delegateRegion = ambientRegion();
26530
26805
  let asgProvider = this.asgDeleteDelegates.get(delegateRegion);
26531
26806
  if (asgProvider === void 0) {
@@ -26631,7 +26906,7 @@ var CloudControlProvider = class {
26631
26906
  const family = SDK_DELETE_FAMILIES.get(resourceType);
26632
26907
  const cached = this.sdkDeleteDelegates.get(family);
26633
26908
  if (cached !== void 0 && cached.region === ccRegion) return cached.provider;
26634
- const provider = family === "rds" ? new (await (import("./rds-provider-DraxDuQq.js"))).RDSProvider({ region: ccRegion }) : new (await (import("./neptune-provider-CqZ1FaqT.js"))).NeptuneProvider({ region: ccRegion });
26909
+ const provider = family === "rds" ? new (await (import("./rds-provider-BfwQbYRR.js"))).RDSProvider({ region: ccRegion }) : new (await (import("./neptune-provider-BWESr5fm.js"))).NeptuneProvider({ region: ccRegion });
26635
26910
  this.sdkDeleteDelegates.set(family, {
26636
26911
  region: ccRegion,
26637
26912
  provider
@@ -26839,9 +27114,10 @@ var CloudControlProvider = class {
26839
27114
  const failureMessage = progressEvent.StatusMessage || "Unknown error";
26840
27115
  throw new CloudControlOperationFailedError(`${operation} failed for ${logicalId}: ${failureMessage}${handlerAuthFailureHint(failureMessage)}`, progressEvent.TypeName || "Unknown", logicalId, progressEvent.Identifier, progressEvent.ErrorCode, operation);
26841
27116
  }
26842
- case "CANCEL_COMPLETE": throw new ProvisioningError(`${operation} cancelled for ${logicalId}`, progressEvent.TypeName || "Unknown", logicalId, progressEvent.Identifier);
27117
+ case "CANCEL_COMPLETE": throw Object.assign(new ProvisioningError(`${operation} cancelled for ${logicalId}`, progressEvent.TypeName || "Unknown", logicalId, progressEvent.Identifier), { ccOperationCancelled: true });
26843
27118
  case "IN_PROGRESS":
26844
27119
  case "PENDING":
27120
+ case "CANCEL_IN_PROGRESS":
26845
27121
  await this.sleep(pollInterval);
26846
27122
  pollInterval = Math.min(Math.ceil(pollInterval * 1.5), this.MAX_POLL_INTERVAL_MS);
26847
27123
  break;
@@ -26853,13 +27129,90 @@ var CloudControlProvider = class {
26853
27129
  }
26854
27130
  throw await this.abandonWait(requestToken, logicalId, operation, resourceType, lastSeenIdentifier, `${operation} timeout for ${logicalId} after ${maxWaitMs / 1e3}s`, lastTransientError);
26855
27131
  }
27132
+ async resumeCreatedResource(logicalId, resourceType, identifier, properties, context) {
27133
+ const mask = maskerOrIdentity(context?.maskSecrets);
27134
+ const attributeNamesLookup = getTopLevelDeployAttributeNames(resourceType);
27135
+ let raw;
27136
+ try {
27137
+ raw = (await this.cloudControlClient.send(new GetResourceCommand({
27138
+ TypeName: resourceType,
27139
+ Identifier: identifier
27140
+ }))).ResourceDescription?.Properties;
27141
+ } catch (error) {
27142
+ if (error.name === "ResourceNotFoundException") throw Object.assign(new ProvisioningError(`${logicalId}'s resource that an earlier CREATE made no longer exists`, resourceType, logicalId, identifier, error instanceof Error ? error : void 0), { resumedResourceGone: true });
27143
+ throw error;
27144
+ }
27145
+ const model = typeof raw === "string" && raw !== "" ? this.parseResourceModel(raw) : {};
27146
+ const modelKeys = Object.keys(model);
27147
+ const enriched = await this.enrichResourceAttributes(resourceType, identifier, model);
27148
+ return {
27149
+ physicalId: identifier,
27150
+ attributes: await this.maskDeployedNonAttributeKeys(enriched, modelKeys, resourceType, identifier, attributeNamesLookup, properties, mask)
27151
+ };
27152
+ }
27153
+ async resumeAbandonedRequest(requestToken, logicalId, resourceType) {
27154
+ let first;
27155
+ try {
27156
+ first = await this.cloudControlClient.send(new GetResourceRequestStatusCommand({ RequestToken: requestToken }));
27157
+ } catch (error) {
27158
+ if (error.name === "RequestTokenNotFoundException") return { status: "not-found" };
27159
+ return {
27160
+ status: "unresolved",
27161
+ error
27162
+ };
27163
+ }
27164
+ let event = first.ProgressEvent;
27165
+ if (event?.TypeName !== void 0 && event.TypeName !== resourceType || event?.Operation !== void 0 && event.Operation !== "CREATE") return {
27166
+ status: "unresolved",
27167
+ error: /* @__PURE__ */ new Error("the request is not a CREATE of the type the journal records")
27168
+ };
27169
+ if (event !== void 0 && ![
27170
+ "SUCCESS",
27171
+ "FAILED",
27172
+ "CANCEL_COMPLETE"
27173
+ ].includes(String(event.OperationStatus))) try {
27174
+ event = await this.waitForOperation(requestToken, logicalId, "CREATE", resourceType);
27175
+ } catch (error) {
27176
+ if (error instanceof CloudControlOperationFailedError) return failedOutcome$1(error.ccErrorCode, error.physicalId);
27177
+ if (error.ccOperationCancelled === true) {
27178
+ const made = error.physicalId;
27179
+ return made ? {
27180
+ status: "cancelled",
27181
+ remnant: made
27182
+ } : { status: "cancelled" };
27183
+ }
27184
+ if (isInterruptedWaitError(error)) throw error;
27185
+ return {
27186
+ status: "unresolved",
27187
+ error
27188
+ };
27189
+ }
27190
+ switch (event?.OperationStatus) {
27191
+ case "SUCCESS": return event.Identifier ? {
27192
+ status: "succeeded",
27193
+ identifier: event.Identifier
27194
+ } : {
27195
+ status: "unresolved",
27196
+ error: /* @__PURE__ */ new Error("the SUCCESS event named no Identifier")
27197
+ };
27198
+ case "FAILED": return failedOutcome$1(event.ErrorCode, event.Identifier);
27199
+ case "CANCEL_COMPLETE": return event.Identifier ? {
27200
+ status: "cancelled",
27201
+ remnant: event.Identifier
27202
+ } : { status: "cancelled" };
27203
+ default: return {
27204
+ status: "unresolved",
27205
+ error: /* @__PURE__ */ new Error(`unexpected operation status ${String(event?.OperationStatus)}`)
27206
+ };
27207
+ }
27208
+ }
26856
27209
  async abandonWait(requestToken, logicalId, operation, resourceType, lastSeenIdentifier, reason, cause) {
26857
27210
  const described = describePollFailure(cause);
26858
27211
  const safeDisplay = displaySafe(described.display);
26859
27212
  const causeText = safeDisplay === "" ? "" : `: ${safeDisplay}`;
26860
27213
  if (described.detail !== "") this.logger.debug(`${operation} ${logicalId}: the abandoned operation's underlying failure was: ${displaySafe(described.detail)}`);
26861
27214
  const identifierClause = lastSeenIdentifier === void 0 ? "" : ` The last status cdkd read named the resource ${displaySafe(lastSeenIdentifier, { asciiOnly: true })}.`;
26862
- const consequence = operation === "CREATE" ? "cdkd has NO state record for it, so any resource it creates is untracked by rollback and by cdkd destroy" : operation === "DELETE" ? "the resource may or may not have been removed; its state record is kept so a re-run can finish the delete" : "its state record still holds the PREVIOUS properties, so the record may no longer describe the live resource";
27215
+ const consequence = operation === "CREATE" ? "cdkd has NO state record for it: when the failed deploy records the request in the stack's rollback journal, the next cdkd deploy, destroy or rollback checks how it ended, and otherwise any resource it creates is untracked" : operation === "DELETE" ? "the resource may or may not have been removed; its state record is kept so a re-run can finish the delete" : "its state record still holds the PREVIOUS properties, so the record may no longer describe the live resource";
26863
27216
  const error = new CloudControlWaitAbandonedError(safeMsg`${operation} of ${logicalId} was accepted by Cloud Control API, but cdkd stopped waiting for it ` + safeMsg`(${reason}${causeText}). The operation may still be running in AWS, and ${consequence}.` + `${identifierClause} Check what it did with:\n ${await this.buildResumeCommand(requestToken)}`, resourceType, logicalId, requestToken, operation, lastSeenIdentifier, cause instanceof Error ? cause : void 0);
26864
27217
  if (described.marker) markRedactedCause(error);
26865
27218
  return operation === "CREATE" ? markNonRetryable(error) : error;
@@ -33266,343 +33619,95 @@ function seedOnlyAliasRefusal(seed) {
33266
33619
  }
33267
33620
 
33268
33621
  //#endregion
33269
- //#region src/state/deployment-events-store.ts
33270
- const DEPLOYMENT_EVENTS_MAX_INDEX_RUNS = 20;
33271
- const FLUSH_INTERVAL_MS = 2e3;
33272
- const FLUSH_EVENT_THRESHOLD = 50;
33273
- function newDeploymentRunId(now = /* @__PURE__ */ new Date()) {
33274
- return `${now.toISOString().replace(/[-:.]/g, "")}-${randomUUID().slice(0, 8)}`;
33275
- }
33276
- function deploymentEventsKey(prefix, stackName, region, runId) {
33277
- return `${prefix}/${stackName}/${region}/deployments/${runId}.jsonl`;
33278
- }
33279
- function deploymentEventsIndexKey(prefix, stackName, region) {
33280
- return `${prefix}/${stackName}/${region}/deployments/index.json`;
33622
+ //#region src/deployment/rollback-executor/orphan-identity.ts
33623
+ const UNIQUE_PHYSICAL_ID_TYPES = /* @__PURE__ */ new Set([
33624
+ "AWS::EC2::VPC",
33625
+ "AWS::EC2::Subnet",
33626
+ "AWS::EC2::EIP",
33627
+ "AWS::EC2::NatGateway",
33628
+ "AWS::EC2::SecurityGroup",
33629
+ "AWS::EC2::Instance",
33630
+ "AWS::EFS::FileSystem",
33631
+ "AWS::EFS::MountTarget",
33632
+ "AWS::FSx::FileSystem",
33633
+ "AWS::KMS::Key",
33634
+ "AWS::CertificateManager::Certificate",
33635
+ "AWS::ElasticLoadBalancingV2::LoadBalancer",
33636
+ "AWS::ElasticLoadBalancingV2::TargetGroup",
33637
+ "AWS::ElasticLoadBalancingV2::Listener",
33638
+ "AWS::EMR::Cluster",
33639
+ "AWS::EMR::InstanceGroupConfig",
33640
+ "AWS::EMR::InstanceFleetConfig",
33641
+ "AWS::Cognito::UserPool",
33642
+ "AWS::Route53::HostedZone",
33643
+ "AWS::ServiceDiscovery::PrivateDnsNamespace",
33644
+ "AWS::ServiceDiscovery::PublicDnsNamespace",
33645
+ "AWS::ServiceDiscovery::HttpNamespace",
33646
+ "AWS::ServiceDiscovery::Service",
33647
+ "AWS::AppSync::GraphQLApi",
33648
+ "AWS::IAM::AccessKey",
33649
+ "AWS::Kinesis::StreamConsumer"
33650
+ ]);
33651
+ const CONTENT_COMPARED_DELETE_TYPES = /* @__PURE__ */ new Set(["AWS::SQS::QueuePolicy", "AWS::SNS::TopicPolicy"]);
33652
+ const CDKD_NAMESPACE_ID_TYPES = /* @__PURE__ */ new Set(["AWS::CloudFormation::Stack"]);
33653
+ function orphanDeleteNeedsIdentity(resourceType) {
33654
+ return !UNIQUE_PHYSICAL_ID_TYPES.has(resourceType) && !CONTENT_COMPARED_DELETE_TYPES.has(resourceType) && !CDKD_NAMESPACE_ID_TYPES.has(resourceType);
33281
33655
  }
33282
- function deploymentsDirPrefix(prefix, stackName, region) {
33283
- return `${prefix}/${stackName}/${region}/deployments/`;
33656
+ function orphanNeedsIdentity(op) {
33657
+ if (op.provisionedBy === "cc-api") return op.createdIdentifierUnique !== true;
33658
+ return orphanDeleteNeedsIdentity(op.resourceType);
33284
33659
  }
33285
- const DEPLOYMENT_EVENTS_OBJECT_DESCRIPTION = "deployment-event streams and their index, whose recorded error messages can quote values the secret mask does not catch";
33286
- async function deleteAndPurgeEventObjects(backend, keys, dirPrefix, scope = "deleted-keys") {
33287
- if (keys.length === 0) return void 0;
33288
- let swept;
33660
+ const RESOURCE_IDENTITY_TIMEOUT_MS = 1e4;
33661
+ async function readResourceIdentity(providerRegistry, target, region, timeoutMs = RESOURCE_IDENTITY_TIMEOUT_MS) {
33662
+ let timer;
33663
+ const timedOut = new Promise((resolve) => {
33664
+ timer = setTimeout(() => resolve(void 0), timeoutMs);
33665
+ });
33289
33666
  try {
33290
- await backend.deleteRawObjects(keys);
33667
+ return await Promise.race([askResourceIdentity(providerRegistry, target, region), timedOut]);
33291
33668
  } finally {
33292
- if (scope === "whole-dir") swept = await backend.purgeNoncurrentVersionsUnderPrefix(dirPrefix, { objectDescription: DEPLOYMENT_EVENTS_OBJECT_DESCRIPTION });
33293
- else await backend.purgeNoncurrentVersions(keys, {
33294
- ...keys.length > 1 && { listPrefix: dirPrefix },
33295
- objectDescription: DEPLOYMENT_EVENTS_OBJECT_DESCRIPTION
33296
- });
33669
+ clearTimeout(timer);
33297
33670
  }
33298
- return swept;
33299
33671
  }
33300
- function runIdTimestampMs(runId) {
33301
- const m = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(\d{3})Z/.exec(runId);
33302
- if (!m) return null;
33303
- const iso = `${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6]}.${m[7]}Z`;
33304
- const ms = Date.parse(iso);
33305
- return Number.isNaN(ms) ? null : ms;
33306
- }
33307
- function runIdFromJsonlKey(key, dirPrefix) {
33308
- if (!key.startsWith(dirPrefix) || !key.endsWith(".jsonl")) return null;
33309
- return key.slice(dirPrefix.length, -6);
33310
- }
33311
- var DeploymentEventsStore = class {
33312
- logger = getLogger().child("DeploymentEvents");
33313
- backend;
33314
- stackName;
33315
- region;
33316
- command;
33317
- runId;
33318
- cdkdVersion;
33319
- startedAt;
33320
- events = [];
33321
- persistedCount = 0;
33322
- flushTimer;
33323
- writeChain = Promise.resolve();
33324
- warnedOnce = false;
33325
- finalized = false;
33326
- constructor(backend, options) {
33327
- this.backend = backend;
33328
- this.stackName = options.stackName;
33329
- this.region = options.region;
33330
- this.command = options.command;
33331
- this.runId = options.runId ?? newDeploymentRunId();
33332
- this.cdkdVersion = options.cdkdVersion ?? getCdkdVersion();
33333
- this.startedAt = (/* @__PURE__ */ new Date()).toISOString();
33334
- }
33335
- record(event) {
33336
- try {
33337
- if (this.finalized) return;
33338
- this.events.push({
33339
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
33340
- ...event
33341
- });
33342
- if (this.events.length - this.persistedCount >= FLUSH_EVENT_THRESHOLD) this.scheduleFlush(0);
33343
- else this.scheduleFlush(FLUSH_INTERVAL_MS);
33344
- } catch {}
33345
- }
33346
- async finalize(result) {
33347
- if (this.finalized) return;
33348
- this.finalized = true;
33349
- if (this.flushTimer) {
33350
- clearTimeout(this.flushTimer);
33351
- this.flushTimer = void 0;
33352
- }
33353
- if (this.events.length === 0) return;
33354
- await this.enqueueWrite(async () => {
33355
- const indexRead = this.readIndexRuns();
33356
- indexRead.catch(() => {});
33357
- await this.doFlush();
33358
- await this.writeIndexAndPrune(result, await indexRead);
33359
- });
33360
- }
33361
- async writeIndexAndPrune(result, existingRuns) {
33362
- const { key, file, runs } = this.buildIndexUpdate(result, existingRuns);
33363
- const keptRunIds = runs.map((r) => r.runId);
33364
- const willPrune = keptRunIds.length >= 20;
33365
- const dirPrefix = deploymentsDirPrefix(this.backend.prefix, this.stackName, this.region);
33366
- const staleKeysRead = willPrune ? this.backend.listRawKeys(dirPrefix) : void 0;
33367
- staleKeysRead?.catch(() => {});
33368
- await this.backend.putRawObject(key, JSON.stringify(file, null, 2));
33369
- if (!staleKeysRead) return;
33370
- await this.pruneSupersededRunFiles(keptRunIds, dirPrefix, await staleKeysRead);
33371
- }
33372
- async drain() {
33373
- await this.writeChain;
33374
- }
33375
- scheduleFlush(delayMs) {
33376
- if (this.flushTimer) {
33377
- if (delayMs > 0) return;
33378
- clearTimeout(this.flushTimer);
33379
- }
33380
- this.flushTimer = setTimeout(() => {
33381
- this.flushTimer = void 0;
33382
- this.enqueueWrite(() => this.doFlush());
33383
- }, delayMs);
33384
- this.flushTimer.unref?.();
33385
- }
33386
- enqueueWrite(op) {
33387
- const next = this.writeChain.then(op).catch((err) => {
33388
- this.warnOnce(`Failed to persist deployment events for run ${this.runId}: ${describeAwsFailure(err).detail}`);
33389
- });
33390
- this.writeChain = next;
33391
- return next;
33392
- }
33393
- async doFlush() {
33394
- if (this.events.length === 0 || this.events.length === this.persistedCount) return;
33395
- const snapshotCount = this.events.length;
33396
- const body = this.events.slice(0, snapshotCount).map((e) => JSON.stringify(e)).join("\n");
33397
- await this.backend.putRawObject(deploymentEventsKey(this.backend.prefix, this.stackName, this.region, this.runId), body + "\n", "application/x-ndjson");
33398
- this.persistedCount = snapshotCount;
33399
- }
33400
- async readIndexRuns() {
33401
- const key = deploymentEventsIndexKey(this.backend.prefix, this.stackName, this.region);
33402
- try {
33403
- const raw = await this.backend.getRawObject(key);
33404
- if (raw !== null) {
33405
- const parsed = JSON.parse(raw);
33406
- if (Array.isArray(parsed.runs)) return parsed.runs;
33407
- }
33408
- } catch (err) {
33409
- this.logger.debug(`Deployment-events index unreadable, rewriting: ${describeAwsFailure(err).detail}`);
33410
- }
33411
- return [];
33412
- }
33413
- buildIndexUpdate(result, existingRuns) {
33414
- const key = deploymentEventsIndexKey(this.backend.prefix, this.stackName, this.region);
33415
- const runs = [{
33416
- runId: this.runId,
33417
- command: this.command,
33418
- cdkdVersion: this.cdkdVersion,
33419
- startedAt: this.startedAt,
33420
- finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
33421
- result,
33422
- eventCount: this.persistedCount
33423
- }, ...existingRuns.filter((r) => r.runId !== this.runId)].slice(0, 20);
33424
- return {
33425
- key,
33426
- file: {
33427
- indexVersion: 1,
33428
- stackName: this.stackName,
33429
- region: this.region,
33430
- runs,
33431
- lastModified: Date.now()
33432
- },
33433
- runs
33434
- };
33435
- }
33436
- async pruneSupersededRunFiles(keptRunIds, dirPrefix, keys) {
33437
- if (keptRunIds.length < 20) return;
33438
- const cutoff = keptRunIds.reduce((min, id) => id < min ? id : min, keptRunIds[0]);
33439
- const stale = keys.filter((k) => {
33440
- const runId = runIdFromJsonlKey(k, dirPrefix);
33441
- return runId !== null && runId < cutoff;
33672
+ async function askResourceIdentity(providerRegistry, target, region) {
33673
+ try {
33674
+ const { provider } = providerRegistry.getProviderFor({
33675
+ resourceType: target.resourceType,
33676
+ provisionedBy: target.provisionedBy
33442
33677
  });
33443
- if (stale.length === 0) return;
33444
- await deleteAndPurgeEventObjects(this.backend, stale, dirPrefix);
33445
- this.logger.debug(`Pruned ${stale.length} superseded deployment-event stream(s) for ${this.stackName} (${this.region})`);
33446
- }
33447
- warnOnce(message) {
33448
- if (this.warnedOnce) {
33449
- this.logger.debug(message);
33450
- return;
33451
- }
33452
- this.warnedOnce = true;
33453
- this.logger.warn(`${message} — continuing; deployment events are best-effort and never block the run.`);
33454
- }
33455
- };
33456
- var DeploymentEventsReader = class {
33457
- backend;
33458
- constructor(backend) {
33459
- this.backend = backend;
33460
- }
33461
- async listRegions(stackName) {
33462
- const prefix = `${this.backend.prefix}/${stackName}/`;
33463
- const keys = await this.backend.listRawKeys(prefix);
33464
- const regions = /* @__PURE__ */ new Set();
33465
- for (const key of keys) {
33466
- const segments = key.slice(prefix.length).split("/");
33467
- if (segments.length === 3 && segments[1] === "deployments" && segments[0]) regions.add(segments[0]);
33468
- }
33469
- return [...regions].sort();
33470
- }
33471
- async listRuns(stackName, region) {
33472
- const key = deploymentEventsIndexKey(this.backend.prefix, stackName, region);
33473
- try {
33474
- const raw = await this.backend.getRawObject(key);
33475
- if (raw !== null) {
33476
- const parsed = JSON.parse(raw);
33477
- if (Array.isArray(parsed.runs)) return parsed.runs;
33478
- }
33479
- } catch {}
33480
- const dirPrefix = `${this.backend.prefix}/${stackName}/${region}/deployments/`;
33481
- const runIds = (await this.backend.listRawKeys(dirPrefix)).filter((k) => k.endsWith(".jsonl")).map((k) => k.slice(dirPrefix.length, -6)).sort().reverse();
33482
- return Promise.all(runIds.map((runId) => this.summarizeRunFromJsonl(stackName, region, runId)));
33483
- }
33484
- async summarizeRunFromJsonl(stackName, region, runId) {
33485
- const fallback = {
33486
- runId,
33487
- command: "deploy",
33488
- cdkdVersion: "unknown",
33489
- startedAt: "",
33490
- finishedAt: "",
33491
- result: "UNKNOWN",
33492
- eventCount: 0
33493
- };
33494
- let events = null;
33495
- try {
33496
- events = await this.readRunEvents(stackName, region, runId);
33497
- } catch {
33498
- return fallback;
33499
- }
33500
- if (events === null || events.length === 0) return fallback;
33501
- const started = events.find((e) => e.eventType === "RUN_STARTED");
33502
- let finished;
33503
- for (const e of events) if (e.eventType === "RUN_FINISHED") finished = e;
33504
- return {
33505
- runId,
33506
- command: started?.command ?? fallback.command,
33507
- cdkdVersion: started?.cdkdVersion ?? fallback.cdkdVersion,
33508
- startedAt: started?.timestamp ?? fallback.startedAt,
33509
- finishedAt: finished?.timestamp ?? fallback.finishedAt,
33510
- result: finished?.result ?? "UNKNOWN",
33511
- eventCount: events.length
33512
- };
33513
- }
33514
- async readRunEvents(stackName, region, runId) {
33515
- const key = deploymentEventsKey(this.backend.prefix, stackName, region, runId);
33516
- const raw = await this.backend.getRawObject(key);
33517
- if (raw === null) return null;
33518
- const events = [];
33519
- for (const line of raw.split("\n")) {
33520
- const trimmed = line.trim();
33521
- if (!trimmed) continue;
33522
- try {
33523
- events.push(JSON.parse(trimmed));
33524
- } catch {}
33525
- }
33526
- return events;
33678
+ if (typeof provider.resourceIdentity !== "function") return void 0;
33679
+ const identity = await provider.resourceIdentity(target.physicalId, target.resourceType, { expectedRegion: region });
33680
+ if (identity === RESOURCE_NOT_FOUND) return RESOURCE_NOT_FOUND;
33681
+ return typeof identity === "string" && identity !== "" ? identity : void 0;
33682
+ } catch {
33683
+ return;
33527
33684
  }
33528
- async pruneRuns(stackName, region, opts = {}) {
33529
- const dirPrefix = deploymentsDirPrefix(this.backend.prefix, stackName, region);
33530
- const keys = await this.backend.listRawKeys(dirPrefix);
33531
- const runIdsDesc = keys.map((k) => runIdFromJsonlKey(k, dirPrefix)).filter((id) => id !== null).sort().reverse();
33532
- const indexKey = deploymentEventsIndexKey(this.backend.prefix, stackName, region);
33533
- const indexExisted = keys.includes(indexKey);
33534
- if (opts.all === true) {
33535
- const toDelete = runIdsDesc.map((id) => deploymentEventsKey(this.backend.prefix, stackName, region, id));
33536
- const earlierVersions = await deleteAndPurgeEventObjects(this.backend, [...toDelete, indexKey], dirPrefix, "whole-dir");
33537
- return {
33538
- deletedRunIds: runIdsDesc,
33539
- remainingRunIds: [],
33540
- indexDeleted: indexExisted,
33541
- ...earlierVersions !== void 0 && { earlierVersions }
33542
- };
33543
- }
33544
- const noGuards = opts.keep === void 0 && opts.olderThanMs === void 0;
33545
- const keep = opts.keep ?? (noGuards ? 20 : void 0);
33546
- const protectedByCount = keep === void 0 ? /* @__PURE__ */ new Set() : new Set(runIdsDesc.slice(0, keep));
33547
- let candidates = runIdsDesc.filter((id) => !protectedByCount.has(id));
33548
- if (opts.olderThanMs !== void 0) {
33549
- const cutoff = (opts.now ?? /* @__PURE__ */ new Date()).getTime() - opts.olderThanMs;
33550
- candidates = candidates.filter((id) => {
33551
- const t = runIdTimestampMs(id);
33552
- return t !== null && t < cutoff;
33553
- });
33554
- }
33555
- if (candidates.length === 0) return {
33556
- deletedRunIds: [],
33557
- remainingRunIds: runIdsDesc,
33558
- indexDeleted: false
33559
- };
33560
- const deletedSet = new Set(candidates);
33561
- const deleteKeys = candidates.map((id) => deploymentEventsKey(this.backend.prefix, stackName, region, id));
33562
- const remainingRunIds = runIdsDesc.filter((id) => !deletedSet.has(id));
33563
- if (remainingRunIds.length === 0) {
33564
- await deleteAndPurgeEventObjects(this.backend, [...deleteKeys, indexKey], dirPrefix);
33565
- return {
33566
- deletedRunIds: candidates,
33567
- remainingRunIds,
33568
- indexDeleted: indexExisted
33569
- };
33570
- }
33571
- await this.rewriteIndexAfterPrune(indexKey, stackName, region, deletedSet);
33572
- await deleteAndPurgeEventObjects(this.backend, deleteKeys, dirPrefix);
33685
+ }
33686
+ async function createdResourceStillThere(op, ctx) {
33687
+ if (op.deletionPolicy === "Retain") return "proven";
33688
+ if (!orphanNeedsIdentity(op)) return "proven";
33689
+ const physicalId = op.physicalId;
33690
+ if (!physicalId) return "unproven";
33691
+ const live = await readResourceIdentity(ctx.providerRegistry, {
33692
+ resourceType: op.resourceType,
33693
+ physicalId,
33694
+ provisionedBy: op.provisionedBy
33695
+ }, ctx.region);
33696
+ if (live === RESOURCE_NOT_FOUND) return "gone";
33697
+ const journaled = op.createdResourceIdentity;
33698
+ if (typeof journaled !== "string" || journaled === "" || typeof live !== "string") return "unproven";
33699
+ return live === journaled ? "proven" : "mismatch";
33700
+ }
33701
+ async function askForeignHolder(foreignHolder, op) {
33702
+ try {
33703
+ return await foreignHolder(op.resourceType, op.physicalId);
33704
+ } catch {
33573
33705
  return {
33574
- deletedRunIds: candidates,
33575
- remainingRunIds,
33576
- indexDeleted: false
33577
- };
33578
- }
33579
- async rewriteIndexAfterPrune(indexKey, stackName, region, deletedRunIds) {
33580
- let raw;
33581
- try {
33582
- raw = await this.backend.getRawObject(indexKey);
33583
- } catch {
33584
- return;
33585
- }
33586
- if (raw === null) return;
33587
- let parsed;
33588
- try {
33589
- parsed = JSON.parse(raw);
33590
- } catch {
33591
- return;
33592
- }
33593
- if (!Array.isArray(parsed.runs)) return;
33594
- const remaining = parsed.runs.filter((r) => !deletedRunIds.has(r.runId));
33595
- if (remaining.length === parsed.runs.length) return;
33596
- const file = {
33597
- indexVersion: 1,
33598
- stackName,
33599
- region,
33600
- runs: remaining,
33601
- lastModified: Date.now()
33706
+ kind: "unreadable",
33707
+ what: "the other stacks' state records (the scan failed)"
33602
33708
  };
33603
- await this.backend.putRawObject(indexKey, JSON.stringify(file, null, 2));
33604
33709
  }
33605
- };
33710
+ }
33606
33711
 
33607
33712
  //#endregion
33608
33713
  //#region src/deployment/rollback-executor/replay-scope.ts
@@ -33912,93 +34017,6 @@ function renderNestedStackTypeChangeRefusal(typeChanges, stackName) {
33912
34017
  return `Refusing to deploy ${stackName}: ` + (typeChanges.length === 1 ? `a resource changes its Type ` : `${typeChanges.length} resources change their Type `) + `into or out of ${NESTED_STACK_RESOURCE_TYPE}, which cdkd does not replace in place.\n${rows.join("\n")}\n Deploy this as two changes instead: give the new resource a DIFFERENT logical id (in CDK, rename the construct) so the existing row is deleted through its own type's provider and the new one is created under its own — or remove the resource in one deploy and add its replacement in the next. There is no flag that overrides this refusal.`;
33913
34018
  }
33914
34019
 
33915
- //#endregion
33916
- //#region src/deployment/rollback-executor/orphan-identity.ts
33917
- const UNIQUE_PHYSICAL_ID_TYPES = /* @__PURE__ */ new Set([
33918
- "AWS::EC2::VPC",
33919
- "AWS::EC2::Subnet",
33920
- "AWS::EC2::EIP",
33921
- "AWS::EC2::NatGateway",
33922
- "AWS::EC2::SecurityGroup",
33923
- "AWS::EC2::Instance",
33924
- "AWS::EFS::FileSystem",
33925
- "AWS::EFS::MountTarget",
33926
- "AWS::FSx::FileSystem",
33927
- "AWS::KMS::Key",
33928
- "AWS::CertificateManager::Certificate",
33929
- "AWS::ElasticLoadBalancingV2::LoadBalancer",
33930
- "AWS::ElasticLoadBalancingV2::TargetGroup",
33931
- "AWS::ElasticLoadBalancingV2::Listener",
33932
- "AWS::EMR::Cluster",
33933
- "AWS::EMR::InstanceGroupConfig",
33934
- "AWS::EMR::InstanceFleetConfig",
33935
- "AWS::Cognito::UserPool",
33936
- "AWS::Route53::HostedZone",
33937
- "AWS::ServiceDiscovery::PrivateDnsNamespace",
33938
- "AWS::ServiceDiscovery::PublicDnsNamespace",
33939
- "AWS::ServiceDiscovery::HttpNamespace",
33940
- "AWS::ServiceDiscovery::Service",
33941
- "AWS::AppSync::GraphQLApi",
33942
- "AWS::IAM::AccessKey",
33943
- "AWS::Kinesis::StreamConsumer"
33944
- ]);
33945
- const CONTENT_COMPARED_DELETE_TYPES = /* @__PURE__ */ new Set(["AWS::SQS::QueuePolicy", "AWS::SNS::TopicPolicy"]);
33946
- const CDKD_NAMESPACE_ID_TYPES = /* @__PURE__ */ new Set(["AWS::CloudFormation::Stack"]);
33947
- function orphanDeleteNeedsIdentity(resourceType) {
33948
- return !UNIQUE_PHYSICAL_ID_TYPES.has(resourceType) && !CONTENT_COMPARED_DELETE_TYPES.has(resourceType) && !CDKD_NAMESPACE_ID_TYPES.has(resourceType);
33949
- }
33950
- const RESOURCE_IDENTITY_TIMEOUT_MS = 1e4;
33951
- async function readResourceIdentity(providerRegistry, target, region, timeoutMs = RESOURCE_IDENTITY_TIMEOUT_MS) {
33952
- let timer;
33953
- const timedOut = new Promise((resolve) => {
33954
- timer = setTimeout(() => resolve(void 0), timeoutMs);
33955
- });
33956
- try {
33957
- return await Promise.race([askResourceIdentity(providerRegistry, target, region), timedOut]);
33958
- } finally {
33959
- clearTimeout(timer);
33960
- }
33961
- }
33962
- async function askResourceIdentity(providerRegistry, target, region) {
33963
- try {
33964
- const { provider } = providerRegistry.getProviderFor({
33965
- resourceType: target.resourceType,
33966
- provisionedBy: target.provisionedBy
33967
- });
33968
- if (typeof provider.resourceIdentity !== "function") return void 0;
33969
- const identity = await provider.resourceIdentity(target.physicalId, target.resourceType, { expectedRegion: region });
33970
- if (identity === RESOURCE_NOT_FOUND) return RESOURCE_NOT_FOUND;
33971
- return typeof identity === "string" && identity !== "" ? identity : void 0;
33972
- } catch {
33973
- return;
33974
- }
33975
- }
33976
- async function createdResourceStillThere(op, ctx) {
33977
- if (op.deletionPolicy === "Retain") return "proven";
33978
- if (!orphanDeleteNeedsIdentity(op.resourceType)) return "proven";
33979
- const physicalId = op.physicalId;
33980
- if (!physicalId) return "unproven";
33981
- const live = await readResourceIdentity(ctx.providerRegistry, {
33982
- resourceType: op.resourceType,
33983
- physicalId,
33984
- provisionedBy: op.provisionedBy
33985
- }, ctx.region);
33986
- if (live === RESOURCE_NOT_FOUND) return "gone";
33987
- const journaled = op.createdResourceIdentity;
33988
- if (typeof journaled !== "string" || journaled === "" || typeof live !== "string") return "unproven";
33989
- return live === journaled ? "proven" : "mismatch";
33990
- }
33991
- async function askForeignHolder(foreignHolder, op) {
33992
- try {
33993
- return await foreignHolder(op.resourceType, op.physicalId);
33994
- } catch {
33995
- return {
33996
- kind: "unreadable",
33997
- what: "the other stacks' state records (the scan failed)"
33998
- };
33999
- }
34000
- }
34001
-
34002
34020
  //#endregion
34003
34021
  //#region src/deployment/rollback-executor/plan.ts
34004
34022
  function journaledOldTypes(op) {
@@ -35455,7 +35473,7 @@ async function replayRollbackUnbound(operations, stateResources, stackName, ctx,
35455
35473
  }
35456
35474
  function protectionRemovalProven(op, deleteProven) {
35457
35475
  if (op.physicalIdRecoveredFromError !== true) return true;
35458
- return deleteProven && (!orphanDeleteNeedsIdentity(op.resourceType) || removeProtectionTypes().includes(op.resourceType));
35476
+ return deleteProven && (!orphanNeedsIdentity(op) || removeProtectionTypes().includes(op.resourceType));
35459
35477
  }
35460
35478
  async function journaledOrphanKeepReason(op, foreignHolder, ctx) {
35461
35479
  const holding = await askForeignHolder(foreignHolder, op);
@@ -35873,6 +35891,344 @@ async function replayFailedOperations(...args) {
35873
35891
  return await withProducerRegions(() => replayProducerRegionEvidence(args[3]), () => withStackRecords(replayStackRecordsView(args[1]), () => replayFailedOperationsUnbound(...args)));
35874
35892
  }
35875
35893
 
35894
+ //#endregion
35895
+ //#region src/provisioning/nested-stack-context.ts
35896
+ const storage$1 = new AsyncLocalStorage();
35897
+ function withNestedStackContext(ctx, fn) {
35898
+ return storage$1.run(ctx, fn);
35899
+ }
35900
+ function getCurrentNestedStackContext() {
35901
+ return storage$1.getStore();
35902
+ }
35903
+ async function collectNestedChildUnaddressed(fn) {
35904
+ const ctx = storage$1.getStore();
35905
+ if (!ctx) return {
35906
+ value: await fn(),
35907
+ unaddressed: void 0
35908
+ };
35909
+ const slot = {};
35910
+ return {
35911
+ value: await storage$1.run({
35912
+ ...ctx,
35913
+ childUnaddressed: slot
35914
+ }, fn),
35915
+ unaddressed: slot.last
35916
+ };
35917
+ }
35918
+
35919
+ //#endregion
35920
+ //#region src/deployment/nested-child-journal.ts
35921
+ const NESTED_PENDING_PARENT_REASON = "nested-pending-parent";
35922
+ const NESTED_STACK_TYPE$2 = "AWS::CloudFormation::Stack";
35923
+ const MAX_NESTED_WALK_DEPTH = 32;
35924
+ function nestedChildStackName(parentStackName, logicalId) {
35925
+ return `${parentStackName}~${logicalId}`;
35926
+ }
35927
+ function nestedPendingSnapshot(state, refusesExportName) {
35928
+ const outputs = { ...isPlainRecord(state.outputs) ? state.outputs : {} };
35929
+ let exportNames = Array.isArray(state.exportNames) ? [...state.exportNames] : void 0;
35930
+ const refused = new Set(importableOutputKeys(state).filter((name) => refusesExportName(name)));
35931
+ if (refused.size > 0) {
35932
+ for (const name of refused) Reflect.deleteProperty(outputs, name);
35933
+ if (exportNames !== void 0) exportNames = exportNames.filter((name) => !refused.has(name));
35934
+ }
35935
+ return {
35936
+ previousOutputs: {
35937
+ outputs,
35938
+ ...exportNames !== void 0 && { exportNames }
35939
+ },
35940
+ previousCrossStackReads: {
35941
+ ...Array.isArray(state.imports) && { imports: [...state.imports] },
35942
+ ...Array.isArray(state.outputReads) && { outputReads: [...state.outputReads] }
35943
+ }
35944
+ };
35945
+ }
35946
+ function displacedPhysicalIdShown(op, logger, physicalId = journalOpPhysicalId(op)) {
35947
+ if (physicalId === void 0) return void 0;
35948
+ const masker = createOpMasker(logger, /* @__PURE__ */ new Map());
35949
+ for (const properties of [
35950
+ op.properties,
35951
+ op.attemptedProperties,
35952
+ op.previousState?.properties
35953
+ ]) {
35954
+ if (properties === void 0) continue;
35955
+ masker.addNamed({
35956
+ resourceType: op.resourceType,
35957
+ properties,
35958
+ logicalId: op.logicalId,
35959
+ physicalIds: [physicalId]
35960
+ });
35961
+ }
35962
+ return displaySafe(String(masker.mask(physicalId)));
35963
+ }
35964
+ function displacedOpClause(op, marks, logger) {
35965
+ const retainedOld = retainedOldPhysicalId(op);
35966
+ if (retainedOld !== void 0 && marks.some((m) => isMarkFor(m, op))) {
35967
+ const shownOld = displacedPhysicalIdShown(op, logger, retainedOld);
35968
+ return `replaced ${shownOld} ${op.oldResourceRetained === true ? "but kept it (UpdateReplacePolicy: Retain)" : op.oldResourceMaySurvive !== void 0 ? "and may have kept it (its delete was not confirmed)" : "and may have kept it (the journal records no verdict)"}; cdkd import adopted the replacement, so ${shownOld} is left untracked`;
35969
+ }
35970
+ const shownId = displacedPhysicalIdShown(op, logger);
35971
+ return shownId !== void 0 ? `recorded ${shownId}, which cdkd import has since replaced under this id` : "recorded no physical id, and cdkd import has since put another resource under this id";
35972
+ }
35973
+ function recordDisplacedSkips(recordEvent, stackName, ops) {
35974
+ for (const op of ops) recordEvent?.({
35975
+ eventType: "ROLLBACK_RESOURCE_SKIPPED",
35976
+ stackName,
35977
+ operation: op.changeType,
35978
+ logicalId: op.logicalId,
35979
+ resourceType: op.resourceType,
35980
+ ...op.provisionedBy && { provisionedBy: op.provisionedBy },
35981
+ reason: "cdkd import has since put another resource under this logical id, so the op was not reverted; check the resource it recorded by hand"
35982
+ });
35983
+ }
35984
+ function revertedNestedRowIds(ops) {
35985
+ return ops.filter((op) => op.resourceType === NESTED_STACK_TYPE$2 && op.changeType === "UPDATE").map((op) => op.logicalId);
35986
+ }
35987
+ const revertRunStorage = new AsyncLocalStorage();
35988
+ function withNestedRevertRun(runId, fn) {
35989
+ const run = {
35990
+ runId,
35991
+ settled: /* @__PURE__ */ new Map(),
35992
+ warnings: 0
35993
+ };
35994
+ return revertRunStorage.run(run, () => fn(run));
35995
+ }
35996
+ function getNestedRevertRun() {
35997
+ return revertRunStorage.getStore();
35998
+ }
35999
+ function errorText(error) {
36000
+ return displaySafe(describeAwsFailure(error).detail);
36001
+ }
36002
+ function isPlainRecord(value) {
36003
+ return typeof value === "object" && value !== null && !Array.isArray(value);
36004
+ }
36005
+ async function dropNestedChildJournals(args) {
36006
+ const { stateBackend, parentStackName, region, resources, logger } = args;
36007
+ const depth = args.depth ?? 0;
36008
+ if (!isPlainRecord(resources)) return;
36009
+ if (depthExceeded(depth, parentStackName, logger)) return;
36010
+ for (const [logicalId, record] of Object.entries(resources)) {
36011
+ if (!isPlainRecord(record) || record["resourceType"] !== NESTED_STACK_TYPE$2) continue;
36012
+ const child = nestedChildStackName(parentStackName, logicalId);
36013
+ try {
36014
+ const data = await stateBackend.getState(child, region);
36015
+ if (data && isPlainRecord(data.state) && isPlainRecord(data.state.resources) && (data.state.stackName === void 0 || data.state.stackName === child)) await dropNestedChildJournals({
36016
+ ...args,
36017
+ parentStackName: child,
36018
+ resources: data.state.resources,
36019
+ depth: depth + 1
36020
+ });
36021
+ } catch (error) {
36022
+ warnUncleared(logger, child, error);
36023
+ }
36024
+ try {
36025
+ if (await withChildLock(args.lockManager, child, region, logger, async () => {
36026
+ if (args.beforeDelete) {
36027
+ if (!await args.beforeDelete(child, await readOwnRecord(stateBackend, child, region))) return false;
36028
+ }
36029
+ let gone;
36030
+ try {
36031
+ gone = await stateBackend.deleteRollbackJournal(child, region);
36032
+ } catch (error) {
36033
+ await args.onDeleteFailed?.(child);
36034
+ throw error;
36035
+ }
36036
+ if (gone === false) {
36037
+ await args.onDeleteFailed?.(child);
36038
+ return false;
36039
+ }
36040
+ return true;
36041
+ })) logger.debug(safeMsg`Deleted the rollback journal of nested stack ${displaySafe(child)}`);
36042
+ } catch (error) {
36043
+ warnUncleared(logger, child, error);
36044
+ }
36045
+ }
36046
+ }
36047
+ async function readOwnRecord(stateBackend, child, region) {
36048
+ try {
36049
+ const data = await stateBackend.getState(child, region);
36050
+ if (data && isPlainRecord(data.state) && isPlainRecord(data.state.resources) && (data.state.stackName === void 0 || data.state.stackName === child)) return data.state;
36051
+ } catch {}
36052
+ }
36053
+ async function dropSettledNestedJournals(args) {
36054
+ const { stateBackend, parentStackName, region, runId, logger } = args;
36055
+ const depth = args.depth ?? 0;
36056
+ if (runId === void 0) return;
36057
+ if (depthExceeded(depth, parentStackName, logger)) return;
36058
+ const isSettled = (segment) => segment.runId === runId && segment.reason === "nested-pending-parent";
36059
+ for (const [logicalId, grandchildren] of args.settled) {
36060
+ const child = nestedChildStackName(parentStackName, logicalId);
36061
+ if (grandchildren.size > 0) await dropSettledNestedJournals({
36062
+ ...args,
36063
+ parentStackName: child,
36064
+ settled: grandchildren,
36065
+ depth: depth + 1
36066
+ });
36067
+ try {
36068
+ const removed = await withChildLock(args.lockManager, child, region, logger, () => stateBackend.dropRollbackJournalSegments(child, region, isSettled));
36069
+ if (removed > 0) logger.debug(safeMsg`Dropped ${removed} settled rollback journal segment(s) of ${displaySafe(child)}`);
36070
+ } catch (error) {
36071
+ warnUncleared(logger, child, error);
36072
+ }
36073
+ }
36074
+ }
36075
+ async function withChildLock(lockManager, child, region, logger, fn) {
36076
+ await lockManager.acquireLockWithRetry(child, region, void 0, "rollback");
36077
+ try {
36078
+ return await fn();
36079
+ } finally {
36080
+ await lockManager.releaseLock(child, region).catch((error) => {
36081
+ logger.warn(safeMsg`Failed to release the lock of nested stack ${displayIdent(child)}: ${errorText(error)}`);
36082
+ });
36083
+ }
36084
+ }
36085
+ function depthExceeded(depth, parentStackName, logger) {
36086
+ if (depth < MAX_NESTED_WALK_DEPTH) return false;
36087
+ logger.warn(safeMsg`Stopped clearing nested rollback journals below ${displayIdent(parentStackName)}: ` + safeMsg`the nesting is deeper than ${MAX_NESTED_WALK_DEPTH} levels.`);
36088
+ return true;
36089
+ }
36090
+ function warnUncleared(logger, child, error) {
36091
+ logger.warn(safeMsg`Could not clear the rollback journal of nested stack ${displayIdent(child)}: ${errorText(error)}. ` + "It is inert to every parent revert (they select segments by run) and the next successful deploy of the top-level stack removes it.");
36092
+ }
36093
+ async function revertNestedChildFromJournal(args) {
36094
+ const { ctx, logicalId, childStackName, region, run, logger } = args;
36095
+ const runId = run.runId;
36096
+ const shownChild = displayIdent(childStackName);
36097
+ const refuse = (detail) => {
36098
+ throw markNonRetryable(/* @__PURE__ */ new Error(`Cannot revert nested stack ${shownChild} (row ${displayIdent(logicalId)}): ${detail} The child was NOT (fully) reverted and may still hold the failed deploy's configuration. Inspect it with 'cdkd state show'; re-run the rollback once the cause is fixed (its journal is kept), or re-deploy the parent to converge.`));
36099
+ };
36100
+ if (runId === void 0) refuse("the rollback carries no deploy run id to select its journal segments by.");
36101
+ await ctx.lockManager.acquireLockWithRetry(childStackName, region, void 0, "rollback");
36102
+ try {
36103
+ const stateData = await ctx.stateBackend.getState(childStackName, region);
36104
+ if (!stateData) refuse("its state.json is missing.");
36105
+ if (stateData.divergentBodyRegion !== void 0) refuse("its state record's region field disagrees with the key it is stored under.");
36106
+ const base = stateData.state;
36107
+ const refusalRecovery = {
36108
+ profile: ctx.destroyOptions?.profile,
36109
+ stateBucket: ctx.stateBucket,
36110
+ statePrefix: ctx.destroyOptions?.statePrefix
36111
+ };
36112
+ refuseMalformedState(base, childStackName, region, refusalRecovery);
36113
+ refuseMalformedOrphans(base, childStackName, region, refusalRecovery);
36114
+ refuseMalformedOrphanRecords(base, childStackName, region, refusalRecovery);
36115
+ const segments = ((await ctx.stateBackend.loadRollbackJournal(childStackName, region))?.segments ?? []).filter((segment) => segment.runId === runId);
36116
+ if (segments.length === 0) refuse("its rollback journal holds no segment for this deploy run, so there is no record of what it held before.");
36117
+ const stateResources = { ...base.resources };
36118
+ const mintedOrphans = [];
36119
+ const restoredOutputs = segments.find((s) => s.previousOutputs)?.previousOutputs;
36120
+ const restoredReads = segments.find((s) => s.previousCrossStackReads)?.previousCrossStackReads;
36121
+ let currentEtag = stateData.etag;
36122
+ let persisted = false;
36123
+ let restoring = false;
36124
+ const { skippedOutputs: _dropped, ...carried } = base;
36125
+ const next = () => ({
36126
+ ...carried,
36127
+ version: 12,
36128
+ region,
36129
+ resources: { ...stateResources },
36130
+ ...orphansAfterRollback(base, mintedOrphans),
36131
+ ...restoring && restoredOutputs && { outputs: { ...restoredOutputs.outputs } },
36132
+ ...restoring && restoredReads && {
36133
+ ...restoredReads.imports && { imports: [...restoredReads.imports] },
36134
+ ...restoredReads.outputReads && { outputReads: [...restoredReads.outputReads] }
36135
+ },
36136
+ ...restoring && restoredOutputs?.exportNames && { exportNames: [...restoredOutputs.exportNames] },
36137
+ lastModified: Date.now()
36138
+ });
36139
+ const nextState = () => {
36140
+ const state = next();
36141
+ if (!restoring) return state;
36142
+ if (restoredOutputs && !restoredOutputs.exportNames) delete state.exportNames;
36143
+ if (restoredOutputs) delete state.outputReferenceProvenance;
36144
+ if (restoredReads && !restoredReads.imports) delete state.imports;
36145
+ if (restoredReads && !restoredReads.outputReads) delete state.outputReads;
36146
+ return state;
36147
+ };
36148
+ const save = async () => {
36149
+ persisted = false;
36150
+ try {
36151
+ currentEtag = await ctx.stateBackend.saveState(childStackName, region, nextState(), { ...currentEtag !== void 0 && { expectedEtag: currentEtag } });
36152
+ persisted = true;
36153
+ } catch {
36154
+ try {
36155
+ const fresh = await ctx.stateBackend.getState(childStackName, region);
36156
+ currentEtag = await ctx.stateBackend.saveState(childStackName, region, nextState(), { ...fresh?.etag !== void 0 && { expectedEtag: fresh.etag } });
36157
+ persisted = true;
36158
+ } catch (retryError) {
36159
+ logger.warn(safeMsg`Failed to persist the state of nested stack ${shownChild} after reverting it: ` + safeMsg`${errorText(retryError)}. The resources were reverted in AWS.`);
36160
+ }
36161
+ }
36162
+ };
36163
+ const producerRegions = inheritProducerRegions(producerRegionsFromState({
36164
+ imports: [...base.imports ?? [], ...restoredReads?.imports ?? []],
36165
+ outputReads: [...base.outputReads ?? [], ...restoredReads?.outputReads ?? []]
36166
+ }), args.inheritedProducerRegions);
36167
+ const execCtx = {
36168
+ providerRegistry: ctx.providerRegistry,
36169
+ region,
36170
+ logger,
36171
+ ...ctx.options?.eventRecorder && { recordEvent: (event) => ctx.options.eventRecorder.record(maskEventTextWithBoundBags(event)) },
36172
+ finalSnapshotClients: ctx.options?.finalSnapshotClients,
36173
+ skipFinalSnapshot: ctx.options?.skipFinalSnapshot === true || ctx.destroyOptions?.skipFinalSnapshot === true,
36174
+ ...ctx.destroyOptions?.removeProtection === true && {
36175
+ removeProtection: true,
36176
+ foreignHolder: makeForeignHolderScan(ctx.stateBackend)({
36177
+ stackName: childStackName,
36178
+ region
36179
+ })
36180
+ },
36181
+ importedProducerRegions: producerRegions.regions,
36182
+ producerRegionsIncomplete: !producerRegions.complete,
36183
+ nestedChildRevert: true
36184
+ };
36185
+ const childCtx = {
36186
+ ...ctx,
36187
+ parentStackName: childStackName,
36188
+ parentRegion: region,
36189
+ nestedTemplates: void 0
36190
+ };
36191
+ let failures = 0;
36192
+ let warnings = 0;
36193
+ const inlinePolicyWriters = new RollbackInlinePolicyWriters();
36194
+ const settledBelow = /* @__PURE__ */ new Map();
36195
+ for (let s = segments.length - 1; s >= 0; s--) {
36196
+ const segment = segments[s];
36197
+ const skipPrefix = segment.skipPrefix ?? getCurrentSkipPrefix();
36198
+ const split = splitImportedOps(segment.operations, segment);
36199
+ const result = await withNestedStackContext(childCtx, () => withSkipPrefix(skipPrefix, () => withStackName(childStackName, () => withCreateTokenLedger(ledgerForStack(ctx.stateBackend, childStackName, region), () => withNestedRevertRun(runId, async (inner) => {
36200
+ const replayed = await withPrintingSecrets(journaledOrphanPrintingBag(completedReplayEntries(split.replay), stateResources), () => replayRollback(split.replay, stateResources, childStackName, execCtx, {
36201
+ afterOp: save,
36202
+ onOrphan: (record) => mintedOrphans.push(record),
36203
+ inlinePolicyWriters,
36204
+ failedOperations: segment.failedOperations
36205
+ }));
36206
+ for (const [id, below] of inner.settled) settledBelow.set(id, below);
36207
+ return {
36208
+ ...replayed,
36209
+ warnings: replayed.warnings + inner.warnings
36210
+ };
36211
+ })))));
36212
+ failures += result.failures;
36213
+ warnings += result.warnings + split.displaced.length;
36214
+ recordDisplacedSkips(execCtx.recordEvent, childStackName, split.displaced);
36215
+ for (const op of split.displaced) logger.warn(safeMsg`Nested stack ${childStackName}: ${op.logicalId} ` + safeMsg`${displacedOpClause(op, segment.importedResources ?? [], logger)}; ` + "not reverted, check that resource by hand");
36216
+ }
36217
+ restoring = failures === 0;
36218
+ await save();
36219
+ if (restoring && persisted && restoredOutputs && ctx.exportIndexStore) await ctx.exportIndexStore.updateForStack(childStackName, region, importableOutputs(nextState()));
36220
+ if (warnings > 0) logger.warn(safeMsg`Nested stack ${shownChild}: ${warnings} operation(s) were skipped with a warning during ` + `its revert (see above); they may need attention by hand.`);
36221
+ if (failures > 0) refuse(`${failures} of its operation(s) failed to revert (see the warnings above).`);
36222
+ if (warnings > 0) run.warnings += warnings;
36223
+ else run.settled.set(logicalId, settledBelow);
36224
+ return { warnings };
36225
+ } finally {
36226
+ await ctx.lockManager.releaseLock(childStackName, region).catch((error) => {
36227
+ logger.warn(safeMsg`Failed to release the lock of nested stack ${shownChild}: ${errorText(error)}`);
36228
+ });
36229
+ }
36230
+ }
36231
+
35876
36232
  //#endregion
35877
36233
  //#region src/deployment/rollback-executor/journaled-orphans.ts
35878
36234
  const NONE = {
@@ -35905,6 +36261,7 @@ async function loadJournaledOrphans(stateBackend, stackName, region, rollbackOrp
35905
36261
  for (const segment of segments) if (options.deployRunId !== void 0 && segment.reason === "nested-pending-parent" && segment.runId === options.deployRunId) for (const op of segment.operations) deployLogicalIds.add(op.logicalId);
35906
36262
  const out = [];
35907
36263
  let count = 0;
36264
+ const pendingRequests = segments.flatMap((segment) => segment.failedOperations ?? []).filter((op) => op.pendingCloudControlRequest !== void 0).length;
35908
36265
  for (let s = segments.length - 1; s >= 0; s--) {
35909
36266
  const segment = segments[s];
35910
36267
  if (segment.reason === "nested-pending-parent") continue;
@@ -35922,7 +36279,8 @@ async function loadJournaledOrphans(stateBackend, stackName, region, rollbackOrp
35922
36279
  return {
35923
36280
  segments: out,
35924
36281
  count,
35925
- deployLogicalIds
36282
+ deployLogicalIds,
36283
+ ...pendingRequests > 0 && { pendingRequests }
35926
36284
  };
35927
36285
  }
35928
36286
  function journaledOrphanLines(orphans, logger) {
@@ -36038,10 +36396,24 @@ async function settleJournaledOrphansOnSuccess(args) {
36038
36396
  keepJournal: true
36039
36397
  };
36040
36398
  }
36041
- if (orphans.count === 0) return {
36042
- unaddressed: 0,
36043
- keepJournal: false
36399
+ const pendingKept = (orphans.pendingRequests ?? 0) > 0;
36400
+ const keepPendingOnly = async () => {
36401
+ try {
36402
+ await stateBackend.reduceRollbackJournalToFailedOperations(stackName, region, (op) => op.pendingCloudControlRequest !== void 0, newerIds, () => false);
36403
+ return true;
36404
+ } catch (err) {
36405
+ logger.warn(safeMsg`Failed to reduce the rollback journal of stack ${stack} to its unsettled Cloud ` + safeMsg`Control requests: ${errorDetail(err)}. The whole journal is kept.`);
36406
+ await markSuperseded(stateBackend, stackName, region, newerIds);
36407
+ return false;
36408
+ }
36044
36409
  };
36410
+ if (orphans.count === 0) {
36411
+ if (pendingKept) await keepPendingOnly();
36412
+ return {
36413
+ unaddressed: 0,
36414
+ keepJournal: pendingKept
36415
+ };
36416
+ }
36045
36417
  const all = orphans.segments.flatMap(({ segment, ops }) => ops.map((op) => ({
36046
36418
  segment,
36047
36419
  op
@@ -36093,15 +36465,18 @@ async function settleJournaledOrphansOnSuccess(args) {
36093
36465
  logger.warn(safeMsg`Failed to remove the settled entries from the rollback journal of stack ${stack}: ` + safeMsg`${errorDetail(err)}. A cdkd rollback or cdkd destroy could act on them again; run a ` + "successful cdkd deploy of the stack first, which settles them again.");
36094
36466
  }
36095
36467
  };
36096
- if (kept.length === 0) return {
36097
- unaddressed: skipped + leftInPlace,
36098
- keepJournal: false,
36099
- ...stripCleared && { stripCleared }
36100
- };
36468
+ if (kept.length === 0) {
36469
+ if (pendingKept && !await keepPendingOnly()) await stripCleared?.();
36470
+ return {
36471
+ unaddressed: skipped + leftInPlace,
36472
+ keepJournal: pendingKept,
36473
+ ...stripCleared && { stripCleared }
36474
+ };
36475
+ }
36101
36476
  const keptIds = new Set(kept.map(({ op }) => op.logicalId));
36102
36477
  const supersededIds = newerIds.filter((id) => !keptIds.has(id));
36103
36478
  const demoted = kept.filter(({ op }) => op.physicalIdRecoveredFromError === false);
36104
- const reduce = () => stateBackend.reduceRollbackJournalToFailedOperations(stackName, region, (op, segment) => isHandledOrphan(kept, segment, op), supersededIds, (op, segment) => isHandledOrphan(demoted, segment, op));
36479
+ const reduce = () => stateBackend.reduceRollbackJournalToFailedOperations(stackName, region, (op, segment) => isHandledOrphan(kept, segment, op) || op.pendingCloudControlRequest !== void 0, supersededIds, (op, segment) => isHandledOrphan(demoted, segment, op));
36105
36480
  let reduced = false;
36106
36481
  for (let attempt = 0; attempt < 2 && !reduced; attempt++) try {
36107
36482
  await reduce();
@@ -36302,342 +36677,496 @@ function sameJournaledOrphans(a, b) {
36302
36677
  }
36303
36678
 
36304
36679
  //#endregion
36305
- //#region src/provisioning/nested-stack-context.ts
36306
- const storage$1 = new AsyncLocalStorage();
36307
- function withNestedStackContext(ctx, fn) {
36308
- return storage$1.run(ctx, fn);
36680
+ //#region src/deployment/cc-pending-requests.ts
36681
+ function hasPendingCloudControlRequest(op) {
36682
+ return op.pendingCloudControlRequest !== void 0;
36309
36683
  }
36310
- function getCurrentNestedStackContext() {
36311
- return storage$1.getStore();
36684
+ function pendingRequestCount(journal) {
36685
+ return pendingRequests(journal ?? null).size;
36312
36686
  }
36313
- async function collectNestedChildUnaddressed(fn) {
36314
- const ctx = storage$1.getStore();
36315
- if (!ctx) return {
36316
- value: await fn(),
36317
- unaddressed: void 0
36318
- };
36319
- const slot = {};
36320
- return {
36321
- value: await storage$1.run({
36322
- ...ctx,
36323
- childUnaddressed: slot
36324
- }, fn),
36325
- unaddressed: slot.last
36687
+ async function resolvePendingCloudControlRequests(params) {
36688
+ const { journal, stackName, region } = params;
36689
+ if (pendingRequests(params.loaded).size === 0) return false;
36690
+ const settle = async () => {
36691
+ await params.beforeSettle?.();
36692
+ let current = params.loaded;
36693
+ if (params.underLock) try {
36694
+ current = await journal.loadRollbackJournal(stackName, region);
36695
+ } catch (error) {
36696
+ throw new CdkdError(safeMsg`cdkd ${params.command} of ${displayStackName(stackName)} refused: its rollback ` + "journal recorded an unsettled Cloud Control CREATE when this run started and cannot " + safeMsg`be read now (${displaySafe(describeAwsFailure(error).summary)}). Nothing was ` + "changed; re-run once it can be read.", "CC_PENDING_REQUEST_UNREADABLE");
36697
+ }
36698
+ const pending = pendingRequests(current);
36699
+ if (pending.size > 0) await settlePending(params, pending);
36700
+ return params.underLock !== void 0 || pending.size > 0;
36326
36701
  };
36702
+ return params.underLock ? params.underLock(settle) : settle();
36327
36703
  }
36328
-
36329
- //#endregion
36330
- //#region src/deployment/nested-child-journal.ts
36331
- const NESTED_PENDING_PARENT_REASON = "nested-pending-parent";
36332
- const NESTED_STACK_TYPE$2 = "AWS::CloudFormation::Stack";
36333
- const MAX_NESTED_WALK_DEPTH = 32;
36334
- function nestedChildStackName(parentStackName, logicalId) {
36335
- return `${parentStackName}~${logicalId}`;
36704
+ function pendingRequests(loaded) {
36705
+ const pending = /* @__PURE__ */ new Map();
36706
+ for (const segment of loaded?.segments ?? []) for (const op of segment.failedOperations ?? []) if (hasPendingCloudControlRequest(op)) pending.set(op.pendingCloudControlRequest.requestToken, op);
36707
+ return pending;
36336
36708
  }
36337
- function nestedPendingSnapshot(state, refusesExportName) {
36338
- const outputs = { ...isPlainRecord(state.outputs) ? state.outputs : {} };
36339
- let exportNames = Array.isArray(state.exportNames) ? [...state.exportNames] : void 0;
36340
- const refused = new Set(importableOutputKeys(state).filter((name) => refusesExportName(name)));
36341
- if (refused.size > 0) {
36342
- for (const name of refused) Reflect.deleteProperty(outputs, name);
36343
- if (exportNames !== void 0) exportNames = exportNames.filter((name) => !refused.has(name));
36344
- }
36345
- return {
36346
- previousOutputs: {
36347
- outputs,
36348
- ...exportNames !== void 0 && { exportNames }
36349
- },
36350
- previousCrossStackReads: {
36351
- ...Array.isArray(state.imports) && { imports: [...state.imports] },
36352
- ...Array.isArray(state.outputReads) && { outputReads: [...state.outputReads] }
36709
+ async function settlePending(params, pending) {
36710
+ const { journal, stackName, region, command, logger } = params;
36711
+ const reader = params.reader();
36712
+ const isUnique = params.identifierUnique ?? ccIdentifierIsUnique;
36713
+ const stackShown = displayStackName(stackName);
36714
+ const settlements = /* @__PURE__ */ new Map();
36715
+ const unresolved = [];
36716
+ for (const [token, op] of pending) {
36717
+ const named = `${logicalIdShown(op.logicalId)} (${resourceTypeShown(op.resourceType)})`;
36718
+ logger.info(safeMsg`A previous run of ${stackShown} stopped waiting for the Cloud Control CREATE of ${named}; ` + "checking how it ended...");
36719
+ const outcome = await reader.resumeAbandonedRequest(token, op.logicalId, op.resourceType);
36720
+ switch (outcome.status) {
36721
+ case "succeeded":
36722
+ case "failed":
36723
+ case "cancelled": {
36724
+ const made = outcome.status === "succeeded" ? outcome.identifier : outcome.remnant;
36725
+ if (made === void 0) {
36726
+ logger.info(safeMsg`The CREATE of ${named} ${outcome.status === "cancelled" ? "was cancelled" : "failed"} ` + "in AWS and made nothing.");
36727
+ settlements.set(token, { kind: "drop" });
36728
+ break;
36729
+ }
36730
+ const madeShown = displacedPhysicalIdShown({
36731
+ ...op,
36732
+ physicalId: made
36733
+ }, logger, made) ?? "a resource";
36734
+ const held = params.stateResources?.[op.logicalId];
36735
+ if (held !== void 0 && held.physicalId === made) {
36736
+ logger.info(safeMsg`The CREATE of ${named} made ${madeShown}, which state already records.`);
36737
+ settlements.set(token, { kind: "drop" });
36738
+ break;
36739
+ }
36740
+ const unique = await isUnique({
36741
+ resourceType: op.resourceType,
36742
+ identifier: made,
36743
+ attemptedProperties: op.attemptedProperties,
36744
+ logicalId: op.logicalId,
36745
+ stackName
36746
+ });
36747
+ const ended = outcome.status === "succeeded" ? "completed" : outcome.status === "cancelled" ? "was cancelled" : "failed";
36748
+ if (outcome.status === "succeeded" && unique && held === void 0 && params.resume?.(op, made)) {
36749
+ logger.info(safeMsg`The CREATE of ${named} completed in AWS after cdkd stopped waiting, and made ` + safeMsg`${madeShown}; this deploy's CREATE of it takes that resource over when it sends ` + "the same properties.");
36750
+ settlements.set(token, { kind: "resume" });
36751
+ break;
36752
+ }
36753
+ if (params.resume !== void 0 && !unique) logger.warn(safeMsg`The CREATE of ${named} ${ended} in AWS after cdkd stopped waiting, and made ` + safeMsg`${madeShown}. cdkd cannot prove its id is unique (it may be a name, which can ` + safeMsg`since belong to another resource), so this deploy does not take it over, and creating ${logicalIdShown(op.logicalId)} ` + "again will ask for the name it holds. Adopt it with cdkd import (--resource " + safeMsg`${logicalIdShown(op.logicalId)}=<its id>), or delete it, then re-run. It is kept in the ` + "rollback journal as a resource the failed deploy created.");
36754
+ else logger.warn(safeMsg`The CREATE of ${named} ${ended} in AWS after cdkd stopped waiting, and made ` + safeMsg`${madeShown}. It is now recorded in the rollback journal as a resource the failed ` + "deploy created: cdkd destroy, cdkd rollback --revert-failed and the next successful deploy delete it as its DeletionPolicy directs when cdkd can prove it is still the resource that create made, and otherwise name it in a warning for you to delete by hand.");
36755
+ settlements.set(token, {
36756
+ kind: "orphan",
36757
+ physicalId: made,
36758
+ unique
36759
+ });
36760
+ break;
36761
+ }
36762
+ case "not-found":
36763
+ logger.warn(safeMsg`Cloud Control no longer has the CREATE request of ${named}: requests expire after ` + "seven days, and a request is not visible from another account or region. cdkd cannot tell whether it created the resource and stops tracking the request. If it exists, adopt it with cdkd import or delete it by hand.");
36764
+ settlements.set(token, { kind: "drop" });
36765
+ break;
36766
+ case "unresolved": {
36767
+ settlements.set(token, { kind: "keep" });
36768
+ const reason = holdsSecretReference(op.attemptedProperties) ? "details withheld: its properties carry a secret" : displaySafe(describeAwsFailure(outcome.error).summary);
36769
+ unresolved.push(safeMsg` - ${named}${reason === "" ? "" : ` (${reason})`}\n` + ` Check it with: ${await reader.buildResumeCommand(token)}`);
36770
+ break;
36771
+ }
36353
36772
  }
36354
- };
36355
- }
36356
- function displacedPhysicalIdShown(op, logger, physicalId = journalOpPhysicalId(op)) {
36357
- if (physicalId === void 0) return void 0;
36358
- const masker = createOpMasker(logger, /* @__PURE__ */ new Map());
36359
- for (const properties of [
36360
- op.properties,
36361
- op.attemptedProperties,
36362
- op.previousState?.properties
36363
- ]) {
36364
- if (properties === void 0) continue;
36365
- masker.addNamed({
36366
- resourceType: op.resourceType,
36367
- properties,
36368
- logicalId: op.logicalId,
36369
- physicalIds: [physicalId]
36370
- });
36371
36773
  }
36372
- return displaySafe(String(masker.mask(physicalId)));
36774
+ if ([...settlements.values()].some((s) => s.kind === "orphan" || s.kind === "drop")) await journal.rewriteRollbackJournalFailedOperations(stackName, region, (op) => {
36775
+ if (!hasPendingCloudControlRequest(op)) return op;
36776
+ const settlement = settlements.get(op.pendingCloudControlRequest.requestToken);
36777
+ if (settlement?.kind === "drop") return withoutPendingRequest(op);
36778
+ if (settlement?.kind !== "orphan") return op;
36779
+ return {
36780
+ ...withoutPendingRequest(op),
36781
+ provisionedBy: "cc-api",
36782
+ physicalId: settlement.physicalId,
36783
+ physicalIdRecoveredFromError: true,
36784
+ ...settlement.unique && { createdIdentifierUnique: true }
36785
+ };
36786
+ });
36787
+ if (unresolved.length > 0) {
36788
+ const kept = [...pending.values()].filter((op) => settlements.get(op.pendingCloudControlRequest.requestToken)?.kind === "keep");
36789
+ const drop = dropFailedCommands(stackName, region, kept.map((op) => op.logicalId));
36790
+ const withheld = drop?.commands.some((c) => c.includes("<")) === true;
36791
+ throw new CdkdError(safeMsg`cdkd ${command} of ${stackShown} refused: a previous run stopped waiting for a Cloud ` + `Control CREATE, and cdkd still cannot tell whether it made its resource. Proceeding could leave that resource untracked. Nothing was changed.
36792
+ ${unresolved.join("\n")}\nRe-run once the request has finished and can be read. If you have reconciled it by hand (cdkd import, or deleted the resource), drop its journal entry with:` + (drop === void 0 ? "" : drop.commands.map((c) => `\n ${c}`).join("")) + (withheld ? "\nA value that is not a plain identifier is shown as a <hole>; read it from the stack's state record and rollback-journal.json." : ""), "CC_PENDING_REQUEST_UNRESOLVED");
36793
+ }
36794
+ }
36795
+ function holdsSecretReference(properties) {
36796
+ if (properties === void 0) return false;
36797
+ return carriesSecretMask(properties) || JSON.stringify(properties).includes("{{resolve:");
36798
+ }
36799
+ const ccIdentifierIsUnique = async ({ resourceType, identifier, attemptedProperties, logicalId, stackName }) => {
36800
+ if (attemptedProperties === void 0 || holdsSecretReference(attemptedProperties)) return false;
36801
+ const [fields, readOnly] = await Promise.all([getPrimaryIdentifierFields(resourceType), getTopLevelReadOnlyProperties(resourceType)]);
36802
+ if (fields === void 0 || readOnly === void 0) return false;
36803
+ const parts = identifier.split("|");
36804
+ if (parts.length !== fields.length) return false;
36805
+ const sent = /* @__PURE__ */ new Set();
36806
+ const collect = (value) => {
36807
+ if (typeof value === "string" || typeof value === "number") for (const piece of nameSegments(String(value))) sent.add(piece);
36808
+ else if (Array.isArray(value)) value.forEach(collect);
36809
+ else if (value !== null && typeof value === "object") Object.values(value).forEach(collect);
36810
+ };
36811
+ collect(attemptedProperties);
36812
+ return parts.some((part, i) => {
36813
+ if (part === "" || !readOnly.has(fields[i])) return false;
36814
+ return !nameSegments(part).some((segment) => sent.has(segment) || looksLikeCdkdGeneratedName(segment, logicalId, stackName));
36815
+ });
36816
+ };
36817
+ function nameSegments(value) {
36818
+ const text = value.toLowerCase();
36819
+ const arn = /^arn:[^:]*:[^:]*:[^:]*:[^:]*:(.*)$/.exec(text);
36820
+ const body = arn ? arn[1] : text;
36821
+ return [
36822
+ text,
36823
+ body,
36824
+ ...body.split(/[:/]/)
36825
+ ].filter((piece) => piece !== "");
36373
36826
  }
36374
- function displacedOpClause(op, marks, logger) {
36375
- const retainedOld = retainedOldPhysicalId(op);
36376
- if (retainedOld !== void 0 && marks.some((m) => isMarkFor(m, op))) {
36377
- const shownOld = displacedPhysicalIdShown(op, logger, retainedOld);
36378
- return `replaced ${shownOld} ${op.oldResourceRetained === true ? "but kept it (UpdateReplacePolicy: Retain)" : op.oldResourceMaySurvive !== void 0 ? "and may have kept it (its delete was not confirmed)" : "and may have kept it (the journal records no verdict)"}; cdkd import adopted the replacement, so ${shownOld} is left untracked`;
36379
- }
36380
- const shownId = displacedPhysicalIdShown(op, logger);
36381
- return shownId !== void 0 ? `recorded ${shownId}, which cdkd import has since replaced under this id` : "recorded no physical id, and cdkd import has since put another resource under this id";
36827
+ function withoutPendingRequest(op) {
36828
+ const { pendingCloudControlRequest: _settled, ...rest } = op;
36829
+ return rest;
36382
36830
  }
36383
- function recordDisplacedSkips(recordEvent, stackName, ops) {
36384
- for (const op of ops) recordEvent?.({
36385
- eventType: "ROLLBACK_RESOURCE_SKIPPED",
36386
- stackName,
36387
- operation: op.changeType,
36388
- logicalId: op.logicalId,
36389
- resourceType: op.resourceType,
36390
- ...op.provisionedBy && { provisionedBy: op.provisionedBy },
36391
- reason: "cdkd import has since put another resource under this logical id, so the op was not reverted; check the resource it recorded by hand"
36392
- });
36831
+
36832
+ //#endregion
36833
+ //#region src/state/deployment-events-store.ts
36834
+ const DEPLOYMENT_EVENTS_MAX_INDEX_RUNS = 20;
36835
+ const FLUSH_INTERVAL_MS = 2e3;
36836
+ const FLUSH_EVENT_THRESHOLD = 50;
36837
+ function newDeploymentRunId(now = /* @__PURE__ */ new Date()) {
36838
+ return `${now.toISOString().replace(/[-:.]/g, "")}-${randomUUID().slice(0, 8)}`;
36393
36839
  }
36394
- function revertedNestedRowIds(ops) {
36395
- return ops.filter((op) => op.resourceType === NESTED_STACK_TYPE$2 && op.changeType === "UPDATE").map((op) => op.logicalId);
36840
+ function deploymentEventsKey(prefix, stackName, region, runId) {
36841
+ return `${prefix}/${stackName}/${region}/deployments/${runId}.jsonl`;
36396
36842
  }
36397
- const revertRunStorage = new AsyncLocalStorage();
36398
- function withNestedRevertRun(runId, fn) {
36399
- const run = {
36400
- runId,
36401
- settled: /* @__PURE__ */ new Map(),
36402
- warnings: 0
36403
- };
36404
- return revertRunStorage.run(run, () => fn(run));
36843
+ function deploymentEventsIndexKey(prefix, stackName, region) {
36844
+ return `${prefix}/${stackName}/${region}/deployments/index.json`;
36405
36845
  }
36406
- function getNestedRevertRun() {
36407
- return revertRunStorage.getStore();
36846
+ function deploymentsDirPrefix(prefix, stackName, region) {
36847
+ return `${prefix}/${stackName}/${region}/deployments/`;
36408
36848
  }
36409
- function errorText(error) {
36410
- return displaySafe(describeAwsFailure(error).detail);
36849
+ const DEPLOYMENT_EVENTS_OBJECT_DESCRIPTION = "deployment-event streams and their index, whose recorded error messages can quote values the secret mask does not catch";
36850
+ async function deleteAndPurgeEventObjects(backend, keys, dirPrefix, scope = "deleted-keys") {
36851
+ if (keys.length === 0) return void 0;
36852
+ let swept;
36853
+ try {
36854
+ await backend.deleteRawObjects(keys);
36855
+ } finally {
36856
+ if (scope === "whole-dir") swept = await backend.purgeNoncurrentVersionsUnderPrefix(dirPrefix, { objectDescription: DEPLOYMENT_EVENTS_OBJECT_DESCRIPTION });
36857
+ else await backend.purgeNoncurrentVersions(keys, {
36858
+ ...keys.length > 1 && { listPrefix: dirPrefix },
36859
+ objectDescription: DEPLOYMENT_EVENTS_OBJECT_DESCRIPTION
36860
+ });
36861
+ }
36862
+ return swept;
36411
36863
  }
36412
- function isPlainRecord(value) {
36413
- return typeof value === "object" && value !== null && !Array.isArray(value);
36864
+ function runIdTimestampMs(runId) {
36865
+ const m = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(\d{3})Z/.exec(runId);
36866
+ if (!m) return null;
36867
+ const iso = `${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6]}.${m[7]}Z`;
36868
+ const ms = Date.parse(iso);
36869
+ return Number.isNaN(ms) ? null : ms;
36414
36870
  }
36415
- async function dropNestedChildJournals(args) {
36416
- const { stateBackend, parentStackName, region, resources, logger } = args;
36417
- const depth = args.depth ?? 0;
36418
- if (!isPlainRecord(resources)) return;
36419
- if (depthExceeded(depth, parentStackName, logger)) return;
36420
- for (const [logicalId, record] of Object.entries(resources)) {
36421
- if (!isPlainRecord(record) || record["resourceType"] !== NESTED_STACK_TYPE$2) continue;
36422
- const child = nestedChildStackName(parentStackName, logicalId);
36871
+ function runIdFromJsonlKey(key, dirPrefix) {
36872
+ if (!key.startsWith(dirPrefix) || !key.endsWith(".jsonl")) return null;
36873
+ return key.slice(dirPrefix.length, -6);
36874
+ }
36875
+ var DeploymentEventsStore = class {
36876
+ logger = getLogger().child("DeploymentEvents");
36877
+ backend;
36878
+ stackName;
36879
+ region;
36880
+ command;
36881
+ runId;
36882
+ cdkdVersion;
36883
+ startedAt;
36884
+ events = [];
36885
+ persistedCount = 0;
36886
+ flushTimer;
36887
+ writeChain = Promise.resolve();
36888
+ warnedOnce = false;
36889
+ finalized = false;
36890
+ constructor(backend, options) {
36891
+ this.backend = backend;
36892
+ this.stackName = options.stackName;
36893
+ this.region = options.region;
36894
+ this.command = options.command;
36895
+ this.runId = options.runId ?? newDeploymentRunId();
36896
+ this.cdkdVersion = options.cdkdVersion ?? getCdkdVersion();
36897
+ this.startedAt = (/* @__PURE__ */ new Date()).toISOString();
36898
+ }
36899
+ record(event) {
36423
36900
  try {
36424
- const data = await stateBackend.getState(child, region);
36425
- if (data && isPlainRecord(data.state) && isPlainRecord(data.state.resources) && (data.state.stackName === void 0 || data.state.stackName === child)) await dropNestedChildJournals({
36426
- ...args,
36427
- parentStackName: child,
36428
- resources: data.state.resources,
36429
- depth: depth + 1
36901
+ if (this.finalized) return;
36902
+ this.events.push({
36903
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
36904
+ ...event
36430
36905
  });
36431
- } catch (error) {
36432
- warnUncleared(logger, child, error);
36906
+ if (this.events.length - this.persistedCount >= FLUSH_EVENT_THRESHOLD) this.scheduleFlush(0);
36907
+ else this.scheduleFlush(FLUSH_INTERVAL_MS);
36908
+ } catch {}
36909
+ }
36910
+ async finalize(result) {
36911
+ if (this.finalized) return;
36912
+ this.finalized = true;
36913
+ if (this.flushTimer) {
36914
+ clearTimeout(this.flushTimer);
36915
+ this.flushTimer = void 0;
36433
36916
  }
36434
- try {
36435
- if (await withChildLock(args.lockManager, child, region, logger, async () => {
36436
- if (args.beforeDelete) {
36437
- if (!await args.beforeDelete(child, await readOwnRecord(stateBackend, child, region))) return false;
36438
- }
36439
- let gone;
36440
- try {
36441
- gone = await stateBackend.deleteRollbackJournal(child, region);
36442
- } catch (error) {
36443
- await args.onDeleteFailed?.(child);
36444
- throw error;
36445
- }
36446
- if (gone === false) {
36447
- await args.onDeleteFailed?.(child);
36448
- return false;
36449
- }
36450
- return true;
36451
- })) logger.debug(safeMsg`Deleted the rollback journal of nested stack ${displaySafe(child)}`);
36452
- } catch (error) {
36453
- warnUncleared(logger, child, error);
36917
+ if (this.events.length === 0) return;
36918
+ await this.enqueueWrite(async () => {
36919
+ const indexRead = this.readIndexRuns();
36920
+ indexRead.catch(() => {});
36921
+ await this.doFlush();
36922
+ await this.writeIndexAndPrune(result, await indexRead);
36923
+ });
36924
+ }
36925
+ async writeIndexAndPrune(result, existingRuns) {
36926
+ const { key, file, runs } = this.buildIndexUpdate(result, existingRuns);
36927
+ const keptRunIds = runs.map((r) => r.runId);
36928
+ const willPrune = keptRunIds.length >= 20;
36929
+ const dirPrefix = deploymentsDirPrefix(this.backend.prefix, this.stackName, this.region);
36930
+ const staleKeysRead = willPrune ? this.backend.listRawKeys(dirPrefix) : void 0;
36931
+ staleKeysRead?.catch(() => {});
36932
+ await this.backend.putRawObject(key, JSON.stringify(file, null, 2));
36933
+ if (!staleKeysRead) return;
36934
+ await this.pruneSupersededRunFiles(keptRunIds, dirPrefix, await staleKeysRead);
36935
+ }
36936
+ async drain() {
36937
+ await this.writeChain;
36938
+ }
36939
+ scheduleFlush(delayMs) {
36940
+ if (this.flushTimer) {
36941
+ if (delayMs > 0) return;
36942
+ clearTimeout(this.flushTimer);
36454
36943
  }
36944
+ this.flushTimer = setTimeout(() => {
36945
+ this.flushTimer = void 0;
36946
+ this.enqueueWrite(() => this.doFlush());
36947
+ }, delayMs);
36948
+ this.flushTimer.unref?.();
36455
36949
  }
36456
- }
36457
- async function readOwnRecord(stateBackend, child, region) {
36458
- try {
36459
- const data = await stateBackend.getState(child, region);
36460
- if (data && isPlainRecord(data.state) && isPlainRecord(data.state.resources) && (data.state.stackName === void 0 || data.state.stackName === child)) return data.state;
36461
- } catch {}
36462
- }
36463
- async function dropSettledNestedJournals(args) {
36464
- const { stateBackend, parentStackName, region, runId, logger } = args;
36465
- const depth = args.depth ?? 0;
36466
- if (runId === void 0) return;
36467
- if (depthExceeded(depth, parentStackName, logger)) return;
36468
- const isSettled = (segment) => segment.runId === runId && segment.reason === "nested-pending-parent";
36469
- for (const [logicalId, grandchildren] of args.settled) {
36470
- const child = nestedChildStackName(parentStackName, logicalId);
36471
- if (grandchildren.size > 0) await dropSettledNestedJournals({
36472
- ...args,
36473
- parentStackName: child,
36474
- settled: grandchildren,
36475
- depth: depth + 1
36950
+ enqueueWrite(op) {
36951
+ const next = this.writeChain.then(op).catch((err) => {
36952
+ this.warnOnce(`Failed to persist deployment events for run ${this.runId}: ${describeAwsFailure(err).detail}`);
36476
36953
  });
36954
+ this.writeChain = next;
36955
+ return next;
36956
+ }
36957
+ async doFlush() {
36958
+ if (this.events.length === 0 || this.events.length === this.persistedCount) return;
36959
+ const snapshotCount = this.events.length;
36960
+ const body = this.events.slice(0, snapshotCount).map((e) => JSON.stringify(e)).join("\n");
36961
+ await this.backend.putRawObject(deploymentEventsKey(this.backend.prefix, this.stackName, this.region, this.runId), body + "\n", "application/x-ndjson");
36962
+ this.persistedCount = snapshotCount;
36963
+ }
36964
+ async readIndexRuns() {
36965
+ const key = deploymentEventsIndexKey(this.backend.prefix, this.stackName, this.region);
36477
36966
  try {
36478
- const removed = await withChildLock(args.lockManager, child, region, logger, () => stateBackend.dropRollbackJournalSegments(child, region, isSettled));
36479
- if (removed > 0) logger.debug(safeMsg`Dropped ${removed} settled rollback journal segment(s) of ${displaySafe(child)}`);
36480
- } catch (error) {
36481
- warnUncleared(logger, child, error);
36967
+ const raw = await this.backend.getRawObject(key);
36968
+ if (raw !== null) {
36969
+ const parsed = JSON.parse(raw);
36970
+ if (Array.isArray(parsed.runs)) return parsed.runs;
36971
+ }
36972
+ } catch (err) {
36973
+ this.logger.debug(`Deployment-events index unreadable, rewriting: ${describeAwsFailure(err).detail}`);
36482
36974
  }
36975
+ return [];
36483
36976
  }
36484
- }
36485
- async function withChildLock(lockManager, child, region, logger, fn) {
36486
- await lockManager.acquireLockWithRetry(child, region, void 0, "rollback");
36487
- try {
36488
- return await fn();
36489
- } finally {
36490
- await lockManager.releaseLock(child, region).catch((error) => {
36491
- logger.warn(safeMsg`Failed to release the lock of nested stack ${displayIdent(child)}: ${errorText(error)}`);
36977
+ buildIndexUpdate(result, existingRuns) {
36978
+ const key = deploymentEventsIndexKey(this.backend.prefix, this.stackName, this.region);
36979
+ const runs = [{
36980
+ runId: this.runId,
36981
+ command: this.command,
36982
+ cdkdVersion: this.cdkdVersion,
36983
+ startedAt: this.startedAt,
36984
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
36985
+ result,
36986
+ eventCount: this.persistedCount
36987
+ }, ...existingRuns.filter((r) => r.runId !== this.runId)].slice(0, 20);
36988
+ return {
36989
+ key,
36990
+ file: {
36991
+ indexVersion: 1,
36992
+ stackName: this.stackName,
36993
+ region: this.region,
36994
+ runs,
36995
+ lastModified: Date.now()
36996
+ },
36997
+ runs
36998
+ };
36999
+ }
37000
+ async pruneSupersededRunFiles(keptRunIds, dirPrefix, keys) {
37001
+ if (keptRunIds.length < 20) return;
37002
+ const cutoff = keptRunIds.reduce((min, id) => id < min ? id : min, keptRunIds[0]);
37003
+ const stale = keys.filter((k) => {
37004
+ const runId = runIdFromJsonlKey(k, dirPrefix);
37005
+ return runId !== null && runId < cutoff;
36492
37006
  });
37007
+ if (stale.length === 0) return;
37008
+ await deleteAndPurgeEventObjects(this.backend, stale, dirPrefix);
37009
+ this.logger.debug(`Pruned ${stale.length} superseded deployment-event stream(s) for ${this.stackName} (${this.region})`);
36493
37010
  }
36494
- }
36495
- function depthExceeded(depth, parentStackName, logger) {
36496
- if (depth < MAX_NESTED_WALK_DEPTH) return false;
36497
- logger.warn(safeMsg`Stopped clearing nested rollback journals below ${displayIdent(parentStackName)}: ` + safeMsg`the nesting is deeper than ${MAX_NESTED_WALK_DEPTH} levels.`);
36498
- return true;
36499
- }
36500
- function warnUncleared(logger, child, error) {
36501
- logger.warn(safeMsg`Could not clear the rollback journal of nested stack ${displayIdent(child)}: ${errorText(error)}. ` + "It is inert to every parent revert (they select segments by run) and the next successful deploy of the top-level stack removes it.");
36502
- }
36503
- async function revertNestedChildFromJournal(args) {
36504
- const { ctx, logicalId, childStackName, region, run, logger } = args;
36505
- const runId = run.runId;
36506
- const shownChild = displayIdent(childStackName);
36507
- const refuse = (detail) => {
36508
- throw markNonRetryable(/* @__PURE__ */ new Error(`Cannot revert nested stack ${shownChild} (row ${displayIdent(logicalId)}): ${detail} The child was NOT (fully) reverted and may still hold the failed deploy's configuration. Inspect it with 'cdkd state show'; re-run the rollback once the cause is fixed (its journal is kept), or re-deploy the parent to converge.`));
36509
- };
36510
- if (runId === void 0) refuse("the rollback carries no deploy run id to select its journal segments by.");
36511
- await ctx.lockManager.acquireLockWithRetry(childStackName, region, void 0, "rollback");
36512
- try {
36513
- const stateData = await ctx.stateBackend.getState(childStackName, region);
36514
- if (!stateData) refuse("its state.json is missing.");
36515
- if (stateData.divergentBodyRegion !== void 0) refuse("its state record's region field disagrees with the key it is stored under.");
36516
- const base = stateData.state;
36517
- const refusalRecovery = {
36518
- profile: ctx.destroyOptions?.profile,
36519
- stateBucket: ctx.stateBucket,
36520
- statePrefix: ctx.destroyOptions?.statePrefix
37011
+ warnOnce(message) {
37012
+ if (this.warnedOnce) {
37013
+ this.logger.debug(message);
37014
+ return;
37015
+ }
37016
+ this.warnedOnce = true;
37017
+ this.logger.warn(`${message} — continuing; deployment events are best-effort and never block the run.`);
37018
+ }
37019
+ };
37020
+ var DeploymentEventsReader = class {
37021
+ backend;
37022
+ constructor(backend) {
37023
+ this.backend = backend;
37024
+ }
37025
+ async listRegions(stackName) {
37026
+ const prefix = `${this.backend.prefix}/${stackName}/`;
37027
+ const keys = await this.backend.listRawKeys(prefix);
37028
+ const regions = /* @__PURE__ */ new Set();
37029
+ for (const key of keys) {
37030
+ const segments = key.slice(prefix.length).split("/");
37031
+ if (segments.length === 3 && segments[1] === "deployments" && segments[0]) regions.add(segments[0]);
37032
+ }
37033
+ return [...regions].sort();
37034
+ }
37035
+ async listRuns(stackName, region) {
37036
+ const key = deploymentEventsIndexKey(this.backend.prefix, stackName, region);
37037
+ try {
37038
+ const raw = await this.backend.getRawObject(key);
37039
+ if (raw !== null) {
37040
+ const parsed = JSON.parse(raw);
37041
+ if (Array.isArray(parsed.runs)) return parsed.runs;
37042
+ }
37043
+ } catch {}
37044
+ const dirPrefix = `${this.backend.prefix}/${stackName}/${region}/deployments/`;
37045
+ const runIds = (await this.backend.listRawKeys(dirPrefix)).filter((k) => k.endsWith(".jsonl")).map((k) => k.slice(dirPrefix.length, -6)).sort().reverse();
37046
+ return Promise.all(runIds.map((runId) => this.summarizeRunFromJsonl(stackName, region, runId)));
37047
+ }
37048
+ async summarizeRunFromJsonl(stackName, region, runId) {
37049
+ const fallback = {
37050
+ runId,
37051
+ command: "deploy",
37052
+ cdkdVersion: "unknown",
37053
+ startedAt: "",
37054
+ finishedAt: "",
37055
+ result: "UNKNOWN",
37056
+ eventCount: 0
36521
37057
  };
36522
- refuseMalformedState(base, childStackName, region, refusalRecovery);
36523
- refuseMalformedOrphans(base, childStackName, region, refusalRecovery);
36524
- refuseMalformedOrphanRecords(base, childStackName, region, refusalRecovery);
36525
- const segments = ((await ctx.stateBackend.loadRollbackJournal(childStackName, region))?.segments ?? []).filter((segment) => segment.runId === runId);
36526
- if (segments.length === 0) refuse("its rollback journal holds no segment for this deploy run, so there is no record of what it held before.");
36527
- const stateResources = { ...base.resources };
36528
- const mintedOrphans = [];
36529
- const restoredOutputs = segments.find((s) => s.previousOutputs)?.previousOutputs;
36530
- const restoredReads = segments.find((s) => s.previousCrossStackReads)?.previousCrossStackReads;
36531
- let currentEtag = stateData.etag;
36532
- let persisted = false;
36533
- let restoring = false;
36534
- const { skippedOutputs: _dropped, ...carried } = base;
36535
- const next = () => ({
36536
- ...carried,
36537
- version: 12,
36538
- region,
36539
- resources: { ...stateResources },
36540
- ...orphansAfterRollback(base, mintedOrphans),
36541
- ...restoring && restoredOutputs && { outputs: { ...restoredOutputs.outputs } },
36542
- ...restoring && restoredReads && {
36543
- ...restoredReads.imports && { imports: [...restoredReads.imports] },
36544
- ...restoredReads.outputReads && { outputReads: [...restoredReads.outputReads] }
36545
- },
36546
- ...restoring && restoredOutputs?.exportNames && { exportNames: [...restoredOutputs.exportNames] },
36547
- lastModified: Date.now()
36548
- });
36549
- const nextState = () => {
36550
- const state = next();
36551
- if (!restoring) return state;
36552
- if (restoredOutputs && !restoredOutputs.exportNames) delete state.exportNames;
36553
- if (restoredOutputs) delete state.outputReferenceProvenance;
36554
- if (restoredReads && !restoredReads.imports) delete state.imports;
36555
- if (restoredReads && !restoredReads.outputReads) delete state.outputReads;
36556
- return state;
37058
+ let events = null;
37059
+ try {
37060
+ events = await this.readRunEvents(stackName, region, runId);
37061
+ } catch {
37062
+ return fallback;
37063
+ }
37064
+ if (events === null || events.length === 0) return fallback;
37065
+ const started = events.find((e) => e.eventType === "RUN_STARTED");
37066
+ let finished;
37067
+ for (const e of events) if (e.eventType === "RUN_FINISHED") finished = e;
37068
+ return {
37069
+ runId,
37070
+ command: started?.command ?? fallback.command,
37071
+ cdkdVersion: started?.cdkdVersion ?? fallback.cdkdVersion,
37072
+ startedAt: started?.timestamp ?? fallback.startedAt,
37073
+ finishedAt: finished?.timestamp ?? fallback.finishedAt,
37074
+ result: finished?.result ?? "UNKNOWN",
37075
+ eventCount: events.length
36557
37076
  };
36558
- const save = async () => {
36559
- persisted = false;
37077
+ }
37078
+ async readRunEvents(stackName, region, runId) {
37079
+ const key = deploymentEventsKey(this.backend.prefix, stackName, region, runId);
37080
+ const raw = await this.backend.getRawObject(key);
37081
+ if (raw === null) return null;
37082
+ const events = [];
37083
+ for (const line of raw.split("\n")) {
37084
+ const trimmed = line.trim();
37085
+ if (!trimmed) continue;
36560
37086
  try {
36561
- currentEtag = await ctx.stateBackend.saveState(childStackName, region, nextState(), { ...currentEtag !== void 0 && { expectedEtag: currentEtag } });
36562
- persisted = true;
36563
- } catch {
36564
- try {
36565
- const fresh = await ctx.stateBackend.getState(childStackName, region);
36566
- currentEtag = await ctx.stateBackend.saveState(childStackName, region, nextState(), { ...fresh?.etag !== void 0 && { expectedEtag: fresh.etag } });
36567
- persisted = true;
36568
- } catch (retryError) {
36569
- logger.warn(safeMsg`Failed to persist the state of nested stack ${shownChild} after reverting it: ` + safeMsg`${errorText(retryError)}. The resources were reverted in AWS.`);
36570
- }
36571
- }
36572
- };
36573
- const producerRegions = inheritProducerRegions(producerRegionsFromState({
36574
- imports: [...base.imports ?? [], ...restoredReads?.imports ?? []],
36575
- outputReads: [...base.outputReads ?? [], ...restoredReads?.outputReads ?? []]
36576
- }), args.inheritedProducerRegions);
36577
- const execCtx = {
36578
- providerRegistry: ctx.providerRegistry,
36579
- region,
36580
- logger,
36581
- ...ctx.options?.eventRecorder && { recordEvent: (event) => ctx.options.eventRecorder.record(maskEventTextWithBoundBags(event)) },
36582
- finalSnapshotClients: ctx.options?.finalSnapshotClients,
36583
- skipFinalSnapshot: ctx.options?.skipFinalSnapshot === true || ctx.destroyOptions?.skipFinalSnapshot === true,
36584
- ...ctx.destroyOptions?.removeProtection === true && {
36585
- removeProtection: true,
36586
- foreignHolder: makeForeignHolderScan(ctx.stateBackend)({
36587
- stackName: childStackName,
36588
- region
36589
- })
36590
- },
36591
- importedProducerRegions: producerRegions.regions,
36592
- producerRegionsIncomplete: !producerRegions.complete,
36593
- nestedChildRevert: true
37087
+ events.push(JSON.parse(trimmed));
37088
+ } catch {}
37089
+ }
37090
+ return events;
37091
+ }
37092
+ async pruneRuns(stackName, region, opts = {}) {
37093
+ const dirPrefix = deploymentsDirPrefix(this.backend.prefix, stackName, region);
37094
+ const keys = await this.backend.listRawKeys(dirPrefix);
37095
+ const runIdsDesc = keys.map((k) => runIdFromJsonlKey(k, dirPrefix)).filter((id) => id !== null).sort().reverse();
37096
+ const indexKey = deploymentEventsIndexKey(this.backend.prefix, stackName, region);
37097
+ const indexExisted = keys.includes(indexKey);
37098
+ if (opts.all === true) {
37099
+ const toDelete = runIdsDesc.map((id) => deploymentEventsKey(this.backend.prefix, stackName, region, id));
37100
+ const earlierVersions = await deleteAndPurgeEventObjects(this.backend, [...toDelete, indexKey], dirPrefix, "whole-dir");
37101
+ return {
37102
+ deletedRunIds: runIdsDesc,
37103
+ remainingRunIds: [],
37104
+ indexDeleted: indexExisted,
37105
+ ...earlierVersions !== void 0 && { earlierVersions }
37106
+ };
37107
+ }
37108
+ const noGuards = opts.keep === void 0 && opts.olderThanMs === void 0;
37109
+ const keep = opts.keep ?? (noGuards ? 20 : void 0);
37110
+ const protectedByCount = keep === void 0 ? /* @__PURE__ */ new Set() : new Set(runIdsDesc.slice(0, keep));
37111
+ let candidates = runIdsDesc.filter((id) => !protectedByCount.has(id));
37112
+ if (opts.olderThanMs !== void 0) {
37113
+ const cutoff = (opts.now ?? /* @__PURE__ */ new Date()).getTime() - opts.olderThanMs;
37114
+ candidates = candidates.filter((id) => {
37115
+ const t = runIdTimestampMs(id);
37116
+ return t !== null && t < cutoff;
37117
+ });
37118
+ }
37119
+ if (candidates.length === 0) return {
37120
+ deletedRunIds: [],
37121
+ remainingRunIds: runIdsDesc,
37122
+ indexDeleted: false
36594
37123
  };
36595
- const childCtx = {
36596
- ...ctx,
36597
- parentStackName: childStackName,
36598
- parentRegion: region,
36599
- nestedTemplates: void 0
37124
+ const deletedSet = new Set(candidates);
37125
+ const deleteKeys = candidates.map((id) => deploymentEventsKey(this.backend.prefix, stackName, region, id));
37126
+ const remainingRunIds = runIdsDesc.filter((id) => !deletedSet.has(id));
37127
+ if (remainingRunIds.length === 0) {
37128
+ await deleteAndPurgeEventObjects(this.backend, [...deleteKeys, indexKey], dirPrefix);
37129
+ return {
37130
+ deletedRunIds: candidates,
37131
+ remainingRunIds,
37132
+ indexDeleted: indexExisted
37133
+ };
37134
+ }
37135
+ await this.rewriteIndexAfterPrune(indexKey, stackName, region, deletedSet);
37136
+ await deleteAndPurgeEventObjects(this.backend, deleteKeys, dirPrefix);
37137
+ return {
37138
+ deletedRunIds: candidates,
37139
+ remainingRunIds,
37140
+ indexDeleted: false
36600
37141
  };
36601
- let failures = 0;
36602
- let warnings = 0;
36603
- const inlinePolicyWriters = new RollbackInlinePolicyWriters();
36604
- const settledBelow = /* @__PURE__ */ new Map();
36605
- for (let s = segments.length - 1; s >= 0; s--) {
36606
- const segment = segments[s];
36607
- const skipPrefix = segment.skipPrefix ?? getCurrentSkipPrefix();
36608
- const split = splitImportedOps(segment.operations, segment);
36609
- const result = await withNestedStackContext(childCtx, () => withSkipPrefix(skipPrefix, () => withStackName(childStackName, () => withCreateTokenLedger(ledgerForStack(ctx.stateBackend, childStackName, region), () => withNestedRevertRun(runId, async (inner) => {
36610
- const replayed = await withPrintingSecrets(journaledOrphanPrintingBag(completedReplayEntries(split.replay), stateResources), () => replayRollback(split.replay, stateResources, childStackName, execCtx, {
36611
- afterOp: save,
36612
- onOrphan: (record) => mintedOrphans.push(record),
36613
- inlinePolicyWriters,
36614
- failedOperations: segment.failedOperations
36615
- }));
36616
- for (const [id, below] of inner.settled) settledBelow.set(id, below);
36617
- return {
36618
- ...replayed,
36619
- warnings: replayed.warnings + inner.warnings
36620
- };
36621
- })))));
36622
- failures += result.failures;
36623
- warnings += result.warnings + split.displaced.length;
36624
- recordDisplacedSkips(execCtx.recordEvent, childStackName, split.displaced);
36625
- for (const op of split.displaced) logger.warn(safeMsg`Nested stack ${childStackName}: ${op.logicalId} ` + safeMsg`${displacedOpClause(op, segment.importedResources ?? [], logger)}; ` + "not reverted, check that resource by hand");
37142
+ }
37143
+ async rewriteIndexAfterPrune(indexKey, stackName, region, deletedRunIds) {
37144
+ let raw;
37145
+ try {
37146
+ raw = await this.backend.getRawObject(indexKey);
37147
+ } catch {
37148
+ return;
36626
37149
  }
36627
- restoring = failures === 0;
36628
- await save();
36629
- if (restoring && persisted && restoredOutputs && ctx.exportIndexStore) await ctx.exportIndexStore.updateForStack(childStackName, region, importableOutputs(nextState()));
36630
- if (warnings > 0) logger.warn(safeMsg`Nested stack ${shownChild}: ${warnings} operation(s) were skipped with a warning during ` + `its revert (see above); they may need attention by hand.`);
36631
- if (failures > 0) refuse(`${failures} of its operation(s) failed to revert (see the warnings above).`);
36632
- if (warnings > 0) run.warnings += warnings;
36633
- else run.settled.set(logicalId, settledBelow);
36634
- return { warnings };
36635
- } finally {
36636
- await ctx.lockManager.releaseLock(childStackName, region).catch((error) => {
36637
- logger.warn(safeMsg`Failed to release the lock of nested stack ${shownChild}: ${errorText(error)}`);
36638
- });
37150
+ if (raw === null) return;
37151
+ let parsed;
37152
+ try {
37153
+ parsed = JSON.parse(raw);
37154
+ } catch {
37155
+ return;
37156
+ }
37157
+ if (!Array.isArray(parsed.runs)) return;
37158
+ const remaining = parsed.runs.filter((r) => !deletedRunIds.has(r.runId));
37159
+ if (remaining.length === parsed.runs.length) return;
37160
+ const file = {
37161
+ indexVersion: 1,
37162
+ stackName,
37163
+ region,
37164
+ runs: remaining,
37165
+ lastModified: Date.now()
37166
+ };
37167
+ await this.backend.putRawObject(indexKey, JSON.stringify(file, null, 2));
36639
37168
  }
36640
- }
37169
+ };
36641
37170
 
36642
37171
  //#endregion
36643
37172
  //#region src/deployment/orphan-adoption.ts
@@ -36943,6 +37472,7 @@ async function settleJournalAfterSuccess(stackName, completedOperations, previou
36943
37472
  const deployRunId = this.options.eventRecorder?.runId;
36944
37473
  const stripOnFailure = /* @__PURE__ */ new Map();
36945
37474
  const [ownLeft] = await Promise.all([(async () => {
37475
+ await orphanUntakenResumes.call(this, stackName, finalResources);
36946
37476
  if (this.absentJournalVersionPurge !== void 0 && this.stateBackend.rollbackJournalReadAbsent(stackName, this.stackRegion)) {
36947
37477
  await this.absentJournalVersionPurge;
36948
37478
  return 0;
@@ -37114,6 +37644,27 @@ async function writeRollbackJournalSegment(stackName, completedOperations, faile
37114
37644
  return false;
37115
37645
  }
37116
37646
  }
37647
+ async function orphanUntakenResumes(stackName, finalResources) {
37648
+ if (this.resumedCreates.size === 0) return;
37649
+ const untaken = new Map([...this.resumedCreates.values()].map((r) => [r.requestToken, r]));
37650
+ this.resumedCreates = /* @__PURE__ */ new Map();
37651
+ try {
37652
+ await this.stateBackend.rewriteRollbackJournalFailedOperations(stackName, this.stackRegion, (op) => {
37653
+ const resumed = op.pendingCloudControlRequest === void 0 ? void 0 : untaken.get(op.pendingCloudControlRequest.requestToken);
37654
+ if (resumed === void 0) return op;
37655
+ if (resumed.gone || finalResources[op.logicalId]?.physicalId === resumed.identifier) return withoutPendingRequest(op);
37656
+ return {
37657
+ ...withoutPendingRequest(op),
37658
+ provisionedBy: "cc-api",
37659
+ physicalId: resumed.identifier,
37660
+ physicalIdRecoveredFromError: true,
37661
+ createdIdentifierUnique: true
37662
+ };
37663
+ });
37664
+ } catch (error) {
37665
+ this.logger.warn(safeMsg`Could not settle the resumed Cloud Control requests in the rollback journal of ` + safeMsg`${displayStackName(stackName)} (${displaySafe(describeAwsFailure(error).summary)}); ` + "the next run settles them.");
37666
+ }
37667
+ }
37117
37668
 
37118
37669
  //#endregion
37119
37670
  //#region src/analyzer/parameter-dependence.ts
@@ -38540,30 +39091,45 @@ async function provisionCreate(logicalId, change, stateResources, stackName, tem
38540
39091
  });
38541
39092
  const createProvider = createDecision.provider;
38542
39093
  const createProps = createDecision.provisionedBy === "cc-api" ? this.preparePropertiesForCcApi(resourceType, resolvedProps, logicalId) : resolvedProps;
38543
- await refuseUnlicensedGeneratedName.call(this, {
38544
- logicalId,
38545
- resourceType,
38546
- stackName,
38547
- createdVia: createDecision.provisionedBy,
38548
- resolvedProps
38549
- });
38550
- await refuseTakenCreateName.call(this, {
39094
+ const resumed = await resumeAbandonedCreate.call(this, {
38551
39095
  logicalId,
38552
39096
  resourceType,
38553
39097
  stackName,
39098
+ resolvedProps,
39099
+ createProps,
38554
39100
  createProvider,
38555
39101
  createdVia: createDecision.provisionedBy,
38556
- createProps,
38557
- secrets: createSecrets,
38558
- stateResources
39102
+ maskSecrets: createSecretMasker(createSecrets)
38559
39103
  });
38560
- this.generatedNameGuard?.noteSent(logicalId);
38561
39104
  let result;
38562
- try {
38563
- result = await this.withRetry(() => withCurrentResourceSecrets(createSecrets, () => createProvider.create(logicalId, resourceType, createProps, { maskSecrets: createSecretMasker(createSecrets) })), logicalId, void 0, void 0, createProvider);
38564
- } catch (error) {
38565
- this.generatedNameGuard?.noteFailed(logicalId, error);
38566
- throw error;
39105
+ if (resumed !== void 0) {
39106
+ this.generatedNameGuard?.noteSent(logicalId);
39107
+ result = resumed;
39108
+ } else {
39109
+ await refuseUnlicensedGeneratedName.call(this, {
39110
+ logicalId,
39111
+ resourceType,
39112
+ stackName,
39113
+ createdVia: createDecision.provisionedBy,
39114
+ resolvedProps
39115
+ });
39116
+ await refuseTakenCreateName.call(this, {
39117
+ logicalId,
39118
+ resourceType,
39119
+ stackName,
39120
+ createProvider,
39121
+ createdVia: createDecision.provisionedBy,
39122
+ createProps,
39123
+ secrets: createSecrets,
39124
+ stateResources
39125
+ });
39126
+ this.generatedNameGuard?.noteSent(logicalId);
39127
+ try {
39128
+ result = await this.withRetry(() => withCurrentResourceSecrets(createSecrets, () => createProvider.create(logicalId, resourceType, createProps, { maskSecrets: createSecretMasker(createSecrets) })), logicalId, void 0, void 0, createProvider);
39129
+ } catch (error) {
39130
+ this.generatedNameGuard?.noteFailed(logicalId, error);
39131
+ throw error;
39132
+ }
38567
39133
  }
38568
39134
  this.generatedNameGuard?.noteReturned(logicalId);
38569
39135
  this.registerNoEchoAttributes(logicalId, result, createSecrets, resolvedProps);
@@ -38693,6 +39259,45 @@ async function refuseTakenCreateName(input) {
38693
39259
  return refuse(`${subject} is created with ${named}, and an existing resource (${shown(holderId)}) already holds that name. Since ${adoptsText}, creating it would take that resource over and record it as this stack's, for a later \`cdkd destroy\` to delete. Nothing was created. Choose a name no other resource holds; ` + ownRemedy + holeNote + (adopt === void 0 ? "" : `
38694
39260
  CONFIRM IT IS YOURS FIRST: another stack or app may own a resource of that name, and adopting it hands that resource to this stack's \`cdkd destroy\`.\nAdopt with: ${adopt.command}`));
38695
39261
  }
39262
+ async function resumeAbandonedCreate(args) {
39263
+ const { logicalId, resourceType } = args;
39264
+ const resumed = this.resumedCreates.get(logicalId);
39265
+ if (resumed === void 0 || resumed.taken || resumed.resourceType !== resourceType) return;
39266
+ const reader = args.createProvider;
39267
+ if (args.createdVia !== "cc-api" || typeof reader.resumeCreatedResource !== "function") return;
39268
+ const probe = {
39269
+ logicalId,
39270
+ changeType: "CREATE",
39271
+ resourceType,
39272
+ attemptedProperties: args.resolvedProps
39273
+ };
39274
+ const [redacted] = this.redactOperationsForJournal([probe]);
39275
+ const sentNow = redacted?.attemptedProperties;
39276
+ const asJson = (value) => value === void 0 ? void 0 : JSON.parse(JSON.stringify(value));
39277
+ if (!isDeepStrictEqual(asJson(sentNow), asJson(resumed.attemptedProperties))) {
39278
+ this.logger.warn(safeMsg`${logicalId}: the template changed since the CREATE cdkd stopped waiting for, so ` + "this deploy creates the resource again; the one that CREATE made is kept in the rollback journal as a resource the failed deploy created.");
39279
+ return;
39280
+ }
39281
+ let result;
39282
+ try {
39283
+ result = await this.withRetry(() => reader.resumeCreatedResource(logicalId, resourceType, resumed.identifier, args.createProps, { maskSecrets: args.maskSecrets }), logicalId, void 0, void 0, args.createProvider);
39284
+ } catch (error) {
39285
+ if (error.resumedResourceGone !== true) throw error;
39286
+ resumed.gone = true;
39287
+ this.logger.warn(safeMsg`${logicalId}: the resource the earlier CREATE made no longer exists; creating it as usual.`);
39288
+ return;
39289
+ }
39290
+ resumed.taken = true;
39291
+ const shown = displacedPhysicalIdShown({
39292
+ logicalId,
39293
+ changeType: "CREATE",
39294
+ resourceType,
39295
+ physicalId: resumed.identifier,
39296
+ attemptedProperties: resumed.attemptedProperties
39297
+ }, this.logger, resumed.identifier) ?? "the resource";
39298
+ this.logger.info(safeMsg`${logicalId}: took over ${shown}, the resource the earlier CREATE made, instead of ` + "creating another.");
39299
+ return result;
39300
+ }
38696
39301
 
38697
39302
  //#endregion
38698
39303
  //#region src/provisioning/data-delete-intent.ts
@@ -45148,11 +45753,20 @@ async function executeDeployment(template, currentState, changes, dag, execution
45148
45753
  record: replaced,
45149
45754
  createdId: replacementCreatedId
45150
45755
  } : void 0;
45756
+ const abandoned = change.changeType === "CREATE" && statePhysicalId === void 0 && createdId === void 0 ? abandonedRequestOf(provisionError) : void 0;
45757
+ const pendingRequest = abandoned?.operation === "CREATE" && /^[-A-Za-z0-9+/=]{1,128}$/.test(abandoned.requestToken) ? {
45758
+ requestToken: abandoned.requestToken,
45759
+ abandonedAt: Date.now()
45760
+ } : void 0;
45151
45761
  const failedOp = {
45152
45762
  logicalId,
45153
45763
  changeType: change.changeType,
45154
45764
  resourceType: change.resourceType,
45155
- provisionedBy: createdId !== void 0 ? "sdk" : newResources[logicalId]?.provisionedBy ?? previousState?.provisionedBy,
45765
+ provisionedBy: createdId !== void 0 ? "sdk" : pendingRequest !== void 0 ? "cc-api" : newResources[logicalId]?.provisionedBy ?? previousState?.provisionedBy,
45766
+ ...pendingRequest !== void 0 && {
45767
+ pendingCloudControlRequest: pendingRequest,
45768
+ deletionPolicy: journaledOrphanPolicy(this.extractTemplateAttributes(template, logicalId).deletionPolicy)
45769
+ },
45156
45770
  ...previousState && { previousState },
45157
45771
  physicalId: createdId ?? statePhysicalId,
45158
45772
  ...createdId !== void 0 && {
@@ -45182,7 +45796,7 @@ async function executeDeployment(template, currentState, changes, dag, execution
45182
45796
  failedOperations.push(replacementOrphanOp);
45183
45797
  }
45184
45798
  const orphanOp = createdId !== void 0 ? failedOp : replacementOrphanOp;
45185
- if (orphanOp?.physicalId !== void 0 && orphanOp.deletionPolicy !== "Retain" && orphanDeleteNeedsIdentity(orphanOp.resourceType)) {
45799
+ if (orphanOp?.physicalId !== void 0 && orphanOp.deletionPolicy !== "Retain" && orphanNeedsIdentity(orphanOp)) {
45186
45800
  const markedIdentity = createdResourceIdentityBeforeFailure(provisionError, logicalId, change.resourceType);
45187
45801
  if (markedIdentity !== void 0) orphanOp.createdResourceIdentity = markedIdentity;
45188
45802
  else if (this.interruptCause !== "user") {
@@ -46861,8 +47475,42 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
46861
47475
  if (exportIndexStore && importableOutputKeys(currentState).length === 0) exportsPrefetch = afterGate(() => exportIndexStore.prefetch());
46862
47476
  }
46863
47477
  this.absentJournalVersionPurge = void 0;
47478
+ let journalAtStart = null;
47479
+ let journalReadError;
47480
+ try {
47481
+ journalAtStart = await this.stateBackend.loadRollbackJournal(stackName, this.stackRegion);
47482
+ } catch (error) {
47483
+ journalReadError = error ?? /* @__PURE__ */ new Error("unreadable rollback journal");
47484
+ }
47485
+ if (!this.options.dryRun && await resolvePendingCloudControlRequests({
47486
+ journal: this.stateBackend,
47487
+ loaded: journalAtStart,
47488
+ reader: () => this.providerRegistry.getCloudControlProvider(),
47489
+ stackName,
47490
+ region: this.stackRegion,
47491
+ command: "deploy",
47492
+ logger: this.logger,
47493
+ stateResources: currentState.resources,
47494
+ resume: (op, identifier) => {
47495
+ const attempted = op.attemptedProperties;
47496
+ if (template.Resources?.[op.logicalId]?.Type !== op.resourceType || attempted === void 0 || carriesSecretMask(attempted) || op.pendingCloudControlRequest === void 0 || Object.values(currentState.resources).some((r) => r.physicalId === identifier && r.resourceType === op.resourceType)) return false;
47497
+ this.resumedCreates.set(op.logicalId, {
47498
+ requestToken: op.pendingCloudControlRequest.requestToken,
47499
+ identifier,
47500
+ resourceType: op.resourceType,
47501
+ attemptedProperties: attempted
47502
+ });
47503
+ return true;
47504
+ },
47505
+ ...gate !== void 0 && { beforeSettle: () => gate }
47506
+ })) try {
47507
+ journalAtStart = await this.stateBackend.loadRollbackJournal(stackName, this.stackRegion);
47508
+ } catch (error) {
47509
+ journalReadError = error ?? /* @__PURE__ */ new Error("unreadable rollback journal");
47510
+ }
46864
47511
  try {
46865
- const loaded = await this.stateBackend.loadRollbackJournal(stackName, this.stackRegion);
47512
+ if (journalReadError !== void 0) throw journalReadError;
47513
+ const loaded = journalAtStart;
46866
47514
  if (loaded === null && !this.options.parentStackInfo && !this.options.dryRun) this.absentJournalVersionPurge = afterGate(() => this.stateBackend.purgeRollbackJournalVersions(stackName, this.stackRegion));
46867
47515
  const journal = loaded && {
46868
47516
  ...loaded,
@@ -47497,6 +48145,7 @@ var DeployEngine = class {
47497
48145
  constructPathTemplate;
47498
48146
  outputsSourceUsable = true;
47499
48147
  attemptedResolvedProps = /* @__PURE__ */ new Map();
48148
+ resumedCreates = /* @__PURE__ */ new Map();
47500
48149
  generatedNameGuard;
47501
48150
  liveTaskLabels = /* @__PURE__ */ new Map();
47502
48151
  retainedOldOnReplacement = /* @__PURE__ */ new Set();
@@ -47569,6 +48218,7 @@ var DeployEngine = class {
47569
48218
  this.fingerprintChildTemplates = void 0;
47570
48219
  this.perResourceInputFingerprints = /* @__PURE__ */ new Map();
47571
48220
  this.attemptedResolvedProps = /* @__PURE__ */ new Map();
48221
+ this.resumedCreates = /* @__PURE__ */ new Map();
47572
48222
  this.generatedNameGuard = void 0;
47573
48223
  this.liveTaskLabels = /* @__PURE__ */ new Map();
47574
48224
  this.outputSecrets = /* @__PURE__ */ new Map();
@@ -47850,5 +48500,5 @@ DeployEngine.prototype.replacementNameOrigin = replacementNameOrigin;
47850
48500
  DeployEngine.prototype.orphanedNameCollisionAdvice = orphanedNameCollisionAdvice;
47851
48501
 
47852
48502
  //#endregion
47853
- export { S3_AUTO_DELETE_OBJECTS_TAG as $, withheldAccountClause as $a, getDockerImageBySourceHash as $c, isReadableResourceEntry as $i, findDestructiveChanges as $n, literalSplitDelimitersOf as $o, applyRoleArnIfSet as $r, splitImportedOps as $s, recordUnderIdIsNotOwn as $t, diffSecurity as A, refuseMalformedResourceEntries as Aa, buildDenyExternalAccessPolicy as Ac, isNamelessDynamicReferenceError as Ai, hasOwnKey as Al, DEFAULT_RESOURCE_TIMEOUT_MS as An, withRetry as Ao, CloudControlProvider as Ar, provenPublicValue as As, withNestedStackContext as At, readProtection as B, refuseMalformedResourcesForDeploy as Ba, isDockerClientEnvKey as Bc, declaresNoEcho as Bi, injectedAwsRegionValue as Bl, storedSecretNameNeedles as Bn, maskWholeValue as Bo, markWaitAbandoned as Br, TEMPLATE_SOURCED_RULES as Bs, awaitInFlightSibling as Bt, withApiLimit as C, refuseMalformedOrphanRecordsForDestroy as Ca, ensureAssetStorage as Cc, coerceParameterTypedValue as Ci, PARTITION_TABLE as Cl, exportNameSecretExposure as Cn, replayWarn as Co, readsNoEchoDeleteValues as Cr, recordNestedStackParameterExpressions as Cs, nestedChildStackName as Ct, findServiceTokenRefusals as D, refuseMalformedOutputs as Da, readBootstrapMarkerBody as Dc, isImpossibleEmptyStoredAttribute as Di, StageLoadError as Dl, collectPublishedOutputNames as Dn, prefetchCreateOnlyPropertyPaths as Do, CUSTOM_RESOURCE_RESPONSE_LIFECYCLE_RULE_ID as Dr, recordPhysicalNameCopy as Ds, withNestedRevertRun as Dt, deferredServiceTokenWarning as E, refuseMalformedOrphansForOrphan as Ea, parseBootstrapMarker as Ec, getAccountInfo as Ei, AssemblyReader as El, collectDeclaredOutputNames as En, requireConfigString as Eo, withPrintingSecrets as Er, recordAttributeIdCopy as Es, revertedNestedRowIds as Et, recordedProtectionEvidence as F, refuseMalformedResourceEntriesForOrphan as Fa, describeDockerFailure as Fc, refStateLookupFromResource as Fi, describeFileReadFailure as Fl, orphanRecordsPrintingBag as Fn, TemplateParser as Fo, deleteIndeterminateGuards as Fr, recordProvenPublicExpression as Fs, loadJournaledOrphans as Ft, deleteBudgetKey as G, repairMalformedOrphansForReadOnly as Ga, redactedDockerCause as Gc, UNREADABLE_RESOURCES_MAP_ROW as Gi, ccRoutedFinalSnapshotError as Gn, passedNoEchoParametersOf as Go, reDisableInstanceApiTermination as Gr, S3StateBackend as Gs, replacedDeleteFailureVerdict as Gt, DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS as H, refuseMalformedResourcesForExport as Ha, partitionSensitiveEnv as Hc, STATE_REGION_DIVERGED as Hi, resolveBucketRegion as Hl, ATOMIC_FINAL_SNAPSHOT_TYPES as Hn, noEchoCoordinatesOf as Ho, ec2InstanceProtectionSite as Hr, isSingleDynamicReferenceToken as Hs, getStackRecords as Ht, recordedProtectionNote as I, refuseMalformedResourcePropertiesForDestroy as Ia, dockerSpawnEnvWithSensitive as Ic, parseWebACLArn as Ii, displayAssemblyPath as Il, secretNamePrintingBag as In, canonicalCoordinates as Io, deleteLeftInPlace as Ir, wholeStringLeavesOf as Is, makeForeignHolderScan as It, DELETE_INDEX_WAIT_PROCEED_NOTE as J, repairMalformedResourcePropertiesForReadOnly as Ja, warnFinchArgvExposure as Jc, displayLogicalId as Ji, isFinalSnapshotError as Jn, valueAtCoordinate as Jo, INTRINSIC_KEYS as Jr, CrossPrefixScanCache as Js, isJournaledOrphan as Jt, resolveDynamoDbDeleteBudgetClock as K, repairMalformedOutputsForReadOnly as Ka, runDockerForeground as Kc, accountArgs as Ki, createPreDeleteFinalSnapshot as Kn, readbackPathFor as Ko, reEnableInstanceApiTermination as Kr, rebuildClientForBucketRegion as Ks, recordAfterRollbackUpdate as Kt, PROTECTION_PROPERTY_BY_TYPE as L, refuseMalformedResourcePropertiesForDrift as La, finchSecretArgvRefusal as Lc, withSharedDrainBudget as Li, renderAssemblyPathEscape as Ll, secretNamePrintingBags as Ln, isMarkedCoordinate as Lo, deleteSkipReason as Lr, STATE_SOURCED_BASELINE_RULES as Ls, sameJournaledOrphans as Lt, noEchoComparisonForTemplate as M, refuseMalformedResourceEntriesForDestroy as Ma, resolveDockerContextDirectory as Mc, AWS_NO_VALUE as Mi, nullPrototypeRecord as Ml, completedReplayEntries as Mn, lambdaFunctionNameForMask as Mo, unchangedBehindSecretReference as Mr, recordMaskOnlyValuesIn as Ms, dropFailedHint as Mt, noEchoExactEchoLeavesOf as N, refuseMalformedResourceEntriesForImport as Na, describeDockerCapturedOutput as Nc, cfnRefValueFromPhysicalId as Ni, ownValue as Nl, journaledOrphanPrintingBag as Nn, sameLambdaFunctionAddress as No, RollbackInlinePolicyWriters as Nr, recordNoEchoAttributeValues as Ns, isHandledOrphan as Nt, serviceTokenBlockingReason as O, refuseMalformedOutputsForDestroy as Oa, validateAssetBucketName as Oc, carriesDynamicReference as Oi, processStackMessages as Ol, isExportAliasCollision as On, templateResourceTypes as Oo, applyCustomResourceResponseExpiration as Or, splitGetAttStringForm as Os, collectNestedChildUnaddressed as Ot, noEchoParameterNamesOf as P, refuseMalformedResourceEntriesForImportSave as Pa, describeDockerExecFailure as Pc, isStalePlaceholderArnAttribute as Pi, absoluteAssemblyPathEscape as Pl, maskEventTextWithBoundBags as Pn, DagBuilder as Po, UNSPECIFIED_SKIP_REASON as Pr, recordNoEchoParameterFreshValue as Ps, journaledOrphanLines as Pt, ECR_AUTO_DELETE_IMAGES_TAG as Q, unreadableResourceEntries as Qa, AssetManifestLoader as Qc, isReadableOrphanRecord as Qi, constructPathOf as Qn, hasMaskableValues as Qo, refuseUnreadableCdkCrossAccountRefs as Qr, isNotImplemented as Qs, recheckFailedPlan as Qt, isProtectionValueActive as R, refuseMalformedResourcePropertiesForExport as Ra, formatDockerLoginError as Rc, parameterInputsFor as Ri, resolveAssemblyPath as Rl, secretNamedRecordNeedles as Rn, maskAtCoordinates as Ro, withIndeterminateGuard as Rr, STATE_SOURCED_CROSS_GENERATION_RULES as Rs, replayFailedOperations as Rt, lookupEachName as S, refuseMalformedOrphanRecords as Sa, assertAssetBucketRegion as Sc, regionLessSecretName as Si, expectedOwnerParam as Sl, exportNameNoEchoParameters as Sn, readConfigString as So, TemplateNoEchoReresolver as Sr, inheritNestedStackParameterAssociations as Ss, getNestedRevertRun as St, recordRetainedForReadoption as T, refuseMalformedOrphansForDestroy as Ta, isCrossRegionRedirect as Tc, parameterTypeMayLoseSecretIdentity as Ti, derivePartitionAndUrlSuffix as Tl, noEchoParameterValueSeed as Tn, requireConfigObject as To, getCurrentResourceSecrets as Tr, physicalNameCopiesOf as Ts, revertNestedChildFromJournal as Tt, beginDeleteWait as U, refuseMalformedState as Ua, redactDockerArgvValues as Uc, STATE_RESOURCES_MALFORMED as Ui, PRE_DELETE_SNAPSHOT_TYPES as Un, noEchoLeavesOf as Uo, isTerminationProtectionPropagationError as Ur, LockManager as Us, withStackRecords as Ut, DynamoDBGlobalTableProvider as V, refuseMalformedResourcesForDestroy as Va, isMalformedEnvKey as Vc, JSON_LISTING_HOLE_VALUE as Vi, clearBucketRegionCache as Vl, extractDeploymentEventError as Vn, maskedLeafCoordinatesOf as Vo, disableInstanceApiTermination as Vr, WHOLE_DYNAMIC_REFERENCE_PATTERN as Vs, destroyStackRecordsView as Vt, compensateRemovedDeletionProtection as W, repairMalformedOrphanRecordsForReadOnly as Wa, redactedCause as Wc, UNREADABLE_ORPHANS_CONTAINER_ROW as Wi, buildFinalSnapshotIdentifier as Wn, noEchoOutputsComparison as Wo, observeThenDisableInstanceApiTermination as Wr, isLockInfoExpired as Ws, removeProtectionTypeList as Wt, deleteTableWithIndexBusyRetry as X, unpreviewableOrphanRecords as Xa, stripControlChars as Xc, hasReadableOutputs as Xi, unsupportedFinalSnapshotError as Xn, carryLogOnlyValues as Xo, poisonRenderedSpellingsCollidingIn as Xr, crossPrefixDeniedWarning as Xs, planFailedOps as Xt, INDEX_SETTLE_POLL_INTERVAL_MS as Y, repairMalformedResourcesForReadOnly as Ya, escapeRegExp$3 as Yc, hasReadableOrphans as Yi, refusesFinalSnapshot as Yn, liveMatchesUnresolvedTokenFrame as Yo, IntrinsicFunctionResolver as Yr, applyCrossPrefixScan as Ys, isReplacementOrphan as Yt, waitForIndexesSettled as Z, unreadableOrphanRecords as Za, stripForDisplay as Zc, hasReadableResources as Zi, maskingRetryLogger as Zn, createUnionSecretMasker as Zo, producerLookupOver as Zr, isAccessDenied$1 as Zs, planRollback as Zt, keptWholeReasonText as _, malformedScrubResourceEntriesRefusalMessage as _a, createAssetRedirectResolver as _c, s3BucketRegionalDomainName as _i, MIGRATE_TMP_PREFIX as _l, exportAliasCollisionScrubWarning as _n, coerceCfnBoolean as _o, CC_BROKEN_REASON as _r, DYNAMIC_REFERENCE_TOKEN_SCAN as _s, planOrphanAdoption as _t, inheritedProvenanceForChildDiff as a, malformedLocalResourcesWarning as aa, hasUnverifiableParameterRefusal as ac, AWS_CREDENTIAL_ENV_KEYS as ai, resolveApp as al, getCurrentProducerRegions as an, hasNoCloudControlHandlers as ao, acquireIdempotencyToken as ar, unionOfSecretBags as as, createReadOnlyAttributeHealerFactory as at, chunks as b, refuseDivergentRecordRegionForDestroy as ba, AssetModeResolver as bc, classifyReplaySecretRegion as bi, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as bl, secretSafeKeyDisplay as bn, configIntegerRefusal as bo, isCustomResource as br, pathCrossesDottedKey as bs, displacedPhysicalIdShown as bt, isAbsoluteAssetPath as c, malformedOrphanRowsKeptWarning as ca, isReadableBag as cc, envCredentialsAreAssumedRole as ci, resolveSkipPrefix as cl, isRedactedRecordedValue as cn, MULTI_REGION_RECREATE_BLOCKED_TYPES as co, DBPROXY_TAGS_WHAT as cr, maskSecretsInError as cs, gray as ct, templateIdentity as d, malformedOutputsRefusalMessage as da, shouldRetainResource as dc, outputReferenceProvenanceOf as di, resolveUseCdkBootstrapAssets as dl, updatePartialMessage as dn, renderStatefulReason as do, readTagList as dr, recordReferenceProvenanceEntry as ds, yellow as dt, malformedDriftResourcePropertiesWarning as ea, acceptedCreateOnlyDropsOf as ec, assertFlagRoleArn as ei, Synthesizer as el, replacementNeverSwapped as en, findActionableSilentDrops as eo, formatDestructiveChange as er, logOnlyValueCount as es, hasCdkAutoDeleteTag as et, conditionFingerprint as f, malformedOutputsWarning as fa, AssetPublisher as fc, recordedOutputReferenceRegions as fi, stateBucketExistenceConfirmed as fl, updatePartialReason as fn, WARM_THROUGHPUT_MEMBERS as fo, refuseMalformedDesiredTags as fr, snapshotPassProvenance as fs, computeParameterTaint as ft, isSecretBearingReferenceString as g, malformedResourcesWarning as ga, buildAssetRedirectMap as gc, s3BucketDualStackDomainName as gi, CFN_TEMPLATE_URL_LIMIT as gl, displayTextOrWithheld as gn, toFiniteNumber as go, stringifyIfAssigned as gr, withoutPhysicalNameCopies as gs, makeSiblingClaimReader as gt, readRecordedConditionVerdicts as h, malformedResourcePropertiesWarning as ha, WorkGraph as hc, s3BucketDomainName as hi, CFN_TEMPLATE_BODY_LIMIT as hl, WITHHELD_NAME_DISPLAY as hn, toCfnInteger as ho, definedAttributes as hr, withPhysicalNameCopies as hs, resourcesNamingDeclaredParameter as ht, crossStackOutputProvenanceSource as i, malformedLocalResourceEntriesWarning as ia, hasReasonlessBaselineRefusal as ic, sendableAssumeRoleCommand as ii, loadCdkJson as il, findNestedStackTypeChanges as in, NO_CC_HANDLERS_REASON as io, withCreateTokenLedger as ir, shareLogOnlyValues as is, formatResourceLine as it, noEchoAttributeNamesOf as j, refuseMalformedResourceEntriesForDeploy as ja, buildDockerImage as jc, keepsSecretReferenceToken as ji, hasPlainPrototype$1 as jl, DEFAULT_RESOURCE_WARN_AFTER_MS as jn, canonicalLambdaFunctionName as jo, slowCcOperationTimeoutMs as jr, recordMaskOnlyValue as js, deleteJournaledOrphans as jt, withResourceDeadline as k, refuseMalformedResourceAttributesForOrphan as ka, validateContainerRepoName as kc, carriesFetchableDynamicReference as ki, defineOwnKey as kl, bindingSkippedOutputs as kn, describeTypeWithThrottleRetry as ko, customResourceResponseExpirationWarning as kr, carriesSecretMask as ks, getCurrentNestedStackContext as kt, listNestedTemplateRows as l, malformedOrphansRefusalMessage as la, orphansAfterRollback as lc, isSettledInstanceState as li, resolveStateBucketWithDefault as ll, redactedDeleteAddressFields as ln, isStatefulRecreateTargetForReplace as lo, DBPROXY_TAG_OPTIONS as lr, maskSecretsInText as ls, green as lt, parentSuppliedValues as m, malformedResourceEntriesWarning as ma, stringifyValue as mc, s3BucketArn as mi, ssmResolvedValueType as ml, DeploymentEventsStore as mn, isWarmThroughputDecrease as mo, tagPlanWarning as mr, scrubResourceRecord as ms, reachableDivergentParameters as mt, IMPLICIT_DELETE_DEPENDENCIES as n, malformedExportSourceWarning as na, exportNamesCarriedFrom as nc, explicitRoleArnOrThrow as ni, getDefaultStateBucketName as nl, seedsDeleteFirstGuard as nn, unwrittenCreateOnlyReplacement as no, ledgerForStack as nr, recordLogOnlyParameterValue as ns, getPriorAttempts as nt, childTemplateLoader as o, malformedOrphanRecordsRefusalMessage as oa, importableOutputKeys as oc, applyCallerIdentityCredentials as oi, resolveAutoAssetStorage as ol, inheritProducerRegions as on, findReplacedReadersOfRecreateTargets as oo, isRefusedByName as or, createSecretMasker as os, bold as ot, conditionInputsFrom as p, malformedRenderedContainersWarning as pa, isSensitiveAttributeName as pc, recordedReferenceRegions as pi, warnDeprecatedNoPrefixCliFlag as pl, DeploymentEventsReader as pn, coerceWarmThroughput as po, tagMapAsList as pr, redactSecretsForState as ps, namesAnyDeclaredParameter as pt, resolveDynamoDbDeleteBudgetMs as q, repairMalformedResourceEntriesForReadOnly as qa, runDockerStreaming as qc, deployRefusesOrphanRowsReason as qi, effectiveDeletionPolicy as qn, readsNoEchoSource as qo, DiffCalculator as qr, CrossPrefixReadError as qs, demoteSupersededOrphans as qt, computeImplicitDeleteEdges as r, malformedLocalOutputsWarning as ra, hasReadableExportSet as rc, isIamRoleArn as ri, getLegacyStateBucketName as rl, orphanDeleteNeedsIdentity as rn, withoutSilentDropProperties as ro, reserveStackCreateToken as rr, recordLogOnlyValue as rs, markRefusedBeforeApplying as rt, findNestedTemplateTreeDefect as s, malformedOrphanRecordsWarning as sa, importableOutputs as sc, callerEnvCredentials as si, resolveCaptureObservedState as sl, REDACTED_DELETE_ADDRESS_SKIP_REASON as sn, shownType as so, markDeleteStarted as sr, errorCauseChain as ss, cyan as st, DeployEngine as t, malformedExportNamesWarning as ta, describeRegionValueKind as tc, assertSendableRoleArn as ti, synthesisStatusMessage as tl, resolveReplacementOldType as tn, findSilentDropProperties as to, earliestDefined as tr, printingCorpusOf as ts, isTruthyCfnBoolean as tt, renderNestedTemplateTreeDefect as u, malformedOrphansWarning as ua, orphansCarriedFrom as uc, keepHeldTokenProvenance as ui, resolveStateBucketWithDefaultAndSource as ul, redactedDeleteAddressSkip as un, isStatefulRecreateTargetSync as uo, planTagDiff as ur, markSameGenerationBag as us, red as ut, mergeNoChangeOutputs as v, malformedStateRefusalMessage as va, loadPublishableAssetManifest as vc, s3BucketWebsiteUrl as vi, findLargeInlineResources as vl, secretBearing as vn, coerceCfnInteger as vo, ProviderRegistry as vr, dynamicReferenceTokens as vs, NESTED_PENDING_PARENT_REASON as vt, keptForReadoption as w, refuseMalformedOrphans as wa, getBootstrapMarkerKey as wc, isUnboundTemplateParameter as wi, canonicalizeRegion as wl, isWholeDynamicReferenceValue as wn, requireConfigArray as wo, isCustomResourceType as wr, recoverMaskedOutput as ws, recordDisplacedSkips as wt, isAccessDeniedError as x, refuseMalformedNestedChildOutputs as xa, BOOTSTRAP_MARKER_PREFIX as xc, producerRegionsFromState as xi, decodeListingKey as xl, carriedExportAliasExposure as xn, configStringRefusal as xo, wouldReturnToSdkProvider as xr, identityKeyFor as xs, dropSettledNestedJournals as xt, makeCanonicalizePropertiesFn as y, previewableOrphanRecords as ya, rewriteTemplateAssetReferences as yc, classifyRecordedSecretRegion as yi, uploadCfnTemplate as yl, secretBearingStateKeyWarning as yn, configBooleanRefusal as yo, ccBrokenReason as yr, isUncertifiedBaselineMaskPosition as ys, displacedOpClause as yt, perType as z, refuseMalformedResourcePropertiesForOrphan as za, getDockerCmd as zc, passedParameterClassesOf as zi, injectAwsRegion as zl, stateSecretNameNeedles as zn, maskReadbackAtCoordinates as zo, isWaitAbandonedError as zr, STATE_SOURCED_READBACK_RULES as zs, replayRollback as zt };
47854
- //# sourceMappingURL=deploy-engine-BK4gG_5A.js.map
48503
+ export { S3_AUTO_DELETE_OBJECTS_TAG as $, unpreviewableOrphanRecords as $a, stripControlChars as $c, hasReadableOutputs as $i, unsupportedFinalSnapshotError as $n, carryLogOnlyValues as $o, poisonRenderedSpellingsCollidingIn as $r, crossPrefixDeniedWarning as $s, isJournaledOrphan as $t, diffSecurity as A, refuseMalformedOutputs as Aa, readBootstrapMarkerBody as Ac, isImpossibleEmptyStoredAttribute as Ai, StageLoadError as Al, collectPublishedOutputNames as An, prefetchCreateOnlyPropertyPaths as Ao, CUSTOM_RESOURCE_RESPONSE_LIFECYCLE_RULE_ID as Ar, recordPhysicalNameCopy as As, NESTED_PENDING_PARENT_REASON as At, readProtection as B, refuseMalformedResourcePropertiesForDrift as Ba, finchSecretArgvRefusal as Bc, withSharedDrainBudget as Bi, renderAssemblyPathEscape as Bl, secretNamePrintingBags as Bn, isMarkedCoordinate as Bo, deleteSkipReason as Br, STATE_SOURCED_BASELINE_RULES as Bs, collectNestedChildUnaddressed as Bt, withApiLimit as C, refuseDivergentRecordRegionForDestroy as Ca, AssetModeResolver as Cc, classifyReplaySecretRegion as Ci, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Cl, secretSafeKeyDisplay as Cn, configIntegerRefusal as Co, isCustomResource as Cr, pathCrossesDottedKey as Cs, dropFailedCommands as Ct, findServiceTokenRefusals as D, refuseMalformedOrphans as Da, getBootstrapMarkerKey as Dc, isUnboundTemplateParameter as Di, canonicalizeRegion as Dl, isWholeDynamicReferenceValue as Dn, requireConfigArray as Do, isCustomResourceType as Dr, recoverMaskedOutput as Ds, loadJournaledOrphans as Dt, deferredServiceTokenWarning as E, refuseMalformedOrphanRecordsForDestroy as Ea, ensureAssetStorage as Ec, coerceParameterTypedValue as Ei, PARTITION_TABLE as El, exportNameSecretExposure as En, replayWarn as Eo, readsNoEchoDeleteValues as Er, recordNestedStackParameterExpressions as Es, journaledOrphanLines as Et, recordedProtectionEvidence as F, refuseMalformedResourceEntriesForDestroy as Fa, resolveDockerContextDirectory as Fc, AWS_NO_VALUE as Fi, nullPrototypeRecord as Fl, completedReplayEntries as Fn, lambdaFunctionNameForMask as Fo, unchangedBehindSecretReference as Fr, recordMaskOnlyValuesIn as Fs, nestedChildStackName as Ft, deleteBudgetKey as G, refuseMalformedResourcesForExport as Ga, partitionSensitiveEnv as Gc, STATE_REGION_DIVERGED as Gi, resolveBucketRegion as Gl, ATOMIC_FINAL_SNAPSHOT_TYPES as Gn, noEchoCoordinatesOf as Go, ec2InstanceProtectionSite as Gr, isSingleDynamicReferenceToken as Gs, awaitInFlightSibling as Gt, DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS as H, refuseMalformedResourcePropertiesForOrphan as Ha, getDockerCmd as Hc, passedParameterClassesOf as Hi, injectAwsRegion as Hl, stateSecretNameNeedles as Hn, maskReadbackAtCoordinates as Ho, isWaitAbandonedError as Hr, STATE_SOURCED_READBACK_RULES as Hs, withNestedStackContext as Ht, recordedProtectionNote as I, refuseMalformedResourceEntriesForImport as Ia, describeDockerCapturedOutput as Ic, cfnRefValueFromPhysicalId as Ii, ownValue as Il, journaledOrphanPrintingBag as In, sameLambdaFunctionAddress as Io, RollbackInlinePolicyWriters as Ir, recordNoEchoAttributeValues as Is, recordDisplacedSkips as It, DELETE_INDEX_WAIT_PROCEED_NOTE as J, repairMalformedOrphansForReadOnly as Ja, redactedDockerCause as Jc, UNREADABLE_RESOURCES_MAP_ROW as Ji, ccRoutedFinalSnapshotError as Jn, passedNoEchoParametersOf as Jo, reDisableInstanceApiTermination as Jr, S3StateBackend as Js, withStackRecords as Jt, resolveDynamoDbDeleteBudgetClock as K, refuseMalformedState as Ka, redactDockerArgvValues as Kc, STATE_RESOURCES_MALFORMED as Ki, PRE_DELETE_SNAPSHOT_TYPES as Kn, noEchoLeavesOf as Ko, isTerminationProtectionPropagationError as Kr, LockManager as Ks, destroyStackRecordsView as Kt, PROTECTION_PROPERTY_BY_TYPE as L, refuseMalformedResourceEntriesForImportSave as La, describeDockerExecFailure as Lc, isStalePlaceholderArnAttribute as Li, absoluteAssemblyPathEscape as Ll, maskEventTextWithBoundBags as Ln, DagBuilder as Lo, UNSPECIFIED_SKIP_REASON as Lr, recordNoEchoParameterFreshValue as Ls, revertNestedChildFromJournal as Lt, noEchoComparisonForTemplate as M, refuseMalformedResourceAttributesForOrphan as Ma, validateContainerRepoName as Mc, carriesFetchableDynamicReference as Mi, defineOwnKey as Ml, bindingSkippedOutputs as Mn, describeTypeWithThrottleRetry as Mo, customResourceResponseExpirationWarning as Mr, carriesSecretMask as Ms, displacedPhysicalIdShown as Mt, noEchoExactEchoLeavesOf as N, refuseMalformedResourceEntries as Na, buildDenyExternalAccessPolicy as Nc, isNamelessDynamicReferenceError as Ni, hasOwnKey as Nl, DEFAULT_RESOURCE_TIMEOUT_MS as Nn, withRetry as No, CloudControlProvider as Nr, provenPublicValue as Ns, dropSettledNestedJournals as Nt, serviceTokenBlockingReason as O, refuseMalformedOrphansForDestroy as Oa, isCrossRegionRedirect as Oc, parameterTypeMayLoseSecretIdentity as Oi, derivePartitionAndUrlSuffix as Ol, noEchoParameterValueSeed as On, requireConfigObject as Oo, getCurrentResourceSecrets as Or, physicalNameCopiesOf as Os, makeForeignHolderScan as Ot, noEchoParameterNamesOf as P, refuseMalformedResourceEntriesForDeploy as Pa, buildDockerImage as Pc, keepsSecretReferenceToken as Pi, hasPlainPrototype$1 as Pl, DEFAULT_RESOURCE_WARN_AFTER_MS as Pn, canonicalLambdaFunctionName as Po, slowCcOperationTimeoutMs as Pr, recordMaskOnlyValue as Ps, getNestedRevertRun as Pt, ECR_AUTO_DELETE_IMAGES_TAG as Q, repairMalformedResourcesForReadOnly as Qa, escapeRegExp$3 as Qc, hasReadableOrphans as Qi, refusesFinalSnapshot as Qn, liveMatchesUnresolvedTokenFrame as Qo, IntrinsicFunctionResolver as Qr, applyCrossPrefixScan as Qs, demoteSupersededOrphans as Qt, isProtectionValueActive as R, refuseMalformedResourceEntriesForOrphan as Ra, describeDockerFailure as Rc, refStateLookupFromResource as Ri, describeFileReadFailure as Rl, orphanRecordsPrintingBag as Rn, TemplateParser as Ro, deleteIndeterminateGuards as Rr, recordProvenPublicExpression as Rs, revertedNestedRowIds as Rt, lookupEachName as S, previewableOrphanRecords as Sa, rewriteTemplateAssetReferences as Sc, classifyRecordedSecretRegion as Si, uploadCfnTemplate as Sl, secretBearingStateKeyWarning as Sn, configBooleanRefusal as So, ccBrokenReason as Sr, isUncertifiedBaselineMaskPosition as Ss, deleteJournaledOrphans as St, recordRetainedForReadoption as T, refuseMalformedOrphanRecords as Ta, assertAssetBucketRegion as Tc, regionLessSecretName as Ti, expectedOwnerParam as Tl, exportNameNoEchoParameters as Tn, readConfigString as To, TemplateNoEchoReresolver as Tr, inheritNestedStackParameterAssociations as Ts, isHandledOrphan as Tt, beginDeleteWait as U, refuseMalformedResourcesForDeploy as Ua, isDockerClientEnvKey as Uc, declaresNoEcho as Ui, injectedAwsRegionValue as Ul, storedSecretNameNeedles as Un, maskWholeValue as Uo, markWaitAbandoned as Ur, TEMPLATE_SOURCED_RULES as Us, replayFailedOperations as Ut, DynamoDBGlobalTableProvider as V, refuseMalformedResourcePropertiesForExport as Va, formatDockerLoginError as Vc, parameterInputsFor as Vi, resolveAssemblyPath as Vl, secretNamedRecordNeedles as Vn, maskAtCoordinates as Vo, withIndeterminateGuard as Vr, STATE_SOURCED_CROSS_GENERATION_RULES as Vs, getCurrentNestedStackContext as Vt, compensateRemovedDeletionProtection as W, refuseMalformedResourcesForDestroy as Wa, isMalformedEnvKey as Wc, JSON_LISTING_HOLE_VALUE as Wi, clearBucketRegionCache as Wl, extractDeploymentEventError as Wn, maskedLeafCoordinatesOf as Wo, disableInstanceApiTermination as Wr, WHOLE_DYNAMIC_REFERENCE_PATTERN as Ws, replayRollback as Wt, deleteTableWithIndexBusyRetry as X, repairMalformedResourceEntriesForReadOnly as Xa, runDockerStreaming as Xc, deployRefusesOrphanRowsReason as Xi, effectiveDeletionPolicy as Xn, readsNoEchoSource as Xo, DiffCalculator as Xr, CrossPrefixReadError as Xs, replacedDeleteFailureVerdict as Xt, INDEX_SETTLE_POLL_INTERVAL_MS as Y, repairMalformedOutputsForReadOnly as Ya, runDockerForeground as Yc, accountArgs as Yi, createPreDeleteFinalSnapshot as Yn, readbackPathFor as Yo, reEnableInstanceApiTermination as Yr, rebuildClientForBucketRegion as Ys, removeProtectionTypeList as Yt, waitForIndexesSettled as Z, repairMalformedResourcePropertiesForReadOnly as Za, warnFinchArgvExposure as Zc, displayLogicalId as Zi, isFinalSnapshotError as Zn, valueAtCoordinate as Zo, INTRINSIC_KEYS as Zr, CrossPrefixScanCache as Zs, recordAfterRollbackUpdate as Zt, keptWholeReasonText as _, malformedResourceEntriesWarning as _a, stringifyValue as _c, s3BucketArn as _i, ssmResolvedValueType as _l, orphanNeedsIdentity as _n, isWarmThroughputDecrease as _o, tagPlanWarning as _r, scrubResourceRecord as _s, planOrphanAdoption as _t, inheritedProvenanceForChildDiff as a, malformedExportSourceWarning as aa, exportNamesCarriedFrom as ac, explicitRoleArnOrThrow as ai, getDefaultStateBucketName as al, replacementNeverSwapped as an, unwrittenCreateOnlyReplacement as ao, ledgerForStack as ar, recordLogOnlyParameterValue as as, createReadOnlyAttributeHealerFactory as at, chunks as b, malformedScrubResourceEntriesRefusalMessage as ba, createAssetRedirectResolver as bc, s3BucketRegionalDomainName as bi, MIGRATE_TMP_PREFIX as bl, exportAliasCollisionScrubWarning as bn, coerceCfnBoolean as bo, CC_BROKEN_REASON as br, DYNAMIC_REFERENCE_TOKEN_SCAN as bs, pendingRequestCount as bt, isAbsoluteAssetPath as c, malformedLocalResourcesWarning as ca, hasUnverifiableParameterRefusal as cc, AWS_CREDENTIAL_ENV_KEYS as ci, resolveApp as cl, findNestedStackTypeChanges as cn, hasNoCloudControlHandlers as co, acquireIdempotencyToken as cr, unionOfSecretBags as cs, gray as ct, templateIdentity as d, malformedOrphanRowsKeptWarning as da, isReadableBag as dc, envCredentialsAreAssumedRole as di, resolveSkipPrefix as dl, REDACTED_DELETE_ADDRESS_SKIP_REASON as dn, MULTI_REGION_RECREATE_BLOCKED_TYPES as do, DBPROXY_TAGS_WHAT as dr, maskSecretsInError as ds, yellow as dt, hasReadableResources as ea, isAccessDenied$1 as ec, producerLookupOver as ei, stripForDisplay as el, isReplacementOrphan as en, unreadableOrphanRecords as eo, maskingRetryLogger as er, createUnionSecretMasker as es, hasCdkAutoDeleteTag as et, conditionFingerprint as f, malformedOrphansRefusalMessage as fa, orphansAfterRollback as fc, isSettledInstanceState as fi, resolveStateBucketWithDefault as fl, isRedactedRecordedValue as fn, isStatefulRecreateTargetForReplace as fo, DBPROXY_TAG_OPTIONS as fr, maskSecretsInText as fs, computeParameterTaint as ft, isSecretBearingReferenceString as g, malformedRenderedContainersWarning as ga, isSensitiveAttributeName as gc, recordedReferenceRegions as gi, warnDeprecatedNoPrefixCliFlag as gl, updatePartialReason as gn, coerceWarmThroughput as go, tagMapAsList as gr, redactSecretsForState as gs, makeSiblingClaimReader as gt, readRecordedConditionVerdicts as h, malformedOutputsWarning as ha, AssetPublisher as hc, recordedOutputReferenceRegions as hi, stateBucketExistenceConfirmed as hl, updatePartialMessage as hn, WARM_THROUGHPUT_MEMBERS as ho, refuseMalformedDesiredTags as hr, snapshotPassProvenance as hs, resourcesNamingDeclaredParameter as ht, crossStackOutputProvenanceSource as i, malformedExportNamesWarning as ia, describeRegionValueKind as ic, assertSendableRoleArn as ii, synthesisStatusMessage as il, recordUnderIdIsNotOwn as in, findSilentDropProperties as io, earliestDefined as ir, printingCorpusOf as is, formatResourceLine as it, noEchoAttributeNamesOf as j, refuseMalformedOutputsForDestroy as ja, validateAssetBucketName as jc, carriesDynamicReference as ji, processStackMessages as jl, isExportAliasCollision as jn, templateResourceTypes as jo, applyCustomResourceResponseExpiration as jr, splitGetAttStringForm as js, displacedOpClause as jt, withResourceDeadline as k, refuseMalformedOrphansForOrphan as ka, parseBootstrapMarker as kc, getAccountInfo as ki, AssemblyReader as kl, collectDeclaredOutputNames as kn, requireConfigString as ko, withPrintingSecrets as kr, recordAttributeIdCopy as ks, sameJournaledOrphans as kt, listNestedTemplateRows as l, malformedOrphanRecordsRefusalMessage as la, importableOutputKeys as lc, applyCallerIdentityCredentials as li, resolveAutoAssetStorage as ll, getCurrentProducerRegions as ln, findReplacedReadersOfRecreateTargets as lo, isRefusedByName as lr, createSecretMasker as ls, green as lt, parentSuppliedValues as m, malformedOutputsRefusalMessage as ma, shouldRetainResource as mc, outputReferenceProvenanceOf as mi, resolveUseCdkBootstrapAssets as ml, redactedDeleteAddressSkip as mn, renderStatefulReason as mo, readTagList as mr, recordReferenceProvenanceEntry as ms, reachableDivergentParameters as mt, IMPLICIT_DELETE_DEPENDENCIES as n, isReadableResourceEntry as na, splitImportedOps as nc, applyRoleArnIfSet as ni, getDockerImageBySourceHash as nl, planRollback as nn, withheldAccountClause as no, findDestructiveChanges as nr, literalSplitDelimitersOf as ns, getPriorAttempts as nt, childTemplateLoader as o, malformedLocalOutputsWarning as oa, hasReadableExportSet as oc, isIamRoleArn as oi, getLegacyStateBucketName as ol, resolveReplacementOldType as on, withoutSilentDropProperties as oo, reserveStackCreateToken as or, recordLogOnlyValue as os, bold as ot, conditionInputsFrom as p, malformedOrphansWarning as pa, orphansCarriedFrom as pc, keepHeldTokenProvenance as pi, resolveStateBucketWithDefaultAndSource as pl, redactedDeleteAddressFields as pn, isStatefulRecreateTargetSync as po, planTagDiff as pr, markSameGenerationBag as ps, namesAnyDeclaredParameter as pt, resolveDynamoDbDeleteBudgetMs as q, repairMalformedOrphanRecordsForReadOnly as qa, redactedCause as qc, UNREADABLE_ORPHANS_CONTAINER_ROW as qi, buildFinalSnapshotIdentifier as qn, noEchoOutputsComparison as qo, observeThenDisableInstanceApiTermination as qr, isLockInfoExpired as qs, getStackRecords as qt, computeImplicitDeleteEdges as r, malformedDriftResourcePropertiesWarning as ra, acceptedCreateOnlyDropsOf as rc, assertFlagRoleArn as ri, Synthesizer as rl, recheckFailedPlan as rn, findActionableSilentDrops as ro, formatDestructiveChange as rr, logOnlyValueCount as rs, markRefusedBeforeApplying as rt, findNestedTemplateTreeDefect as s, malformedLocalResourceEntriesWarning as sa, hasReasonlessBaselineRefusal as sc, sendableAssumeRoleCommand as si, loadCdkJson as sl, seedsDeleteFirstGuard as sn, NO_CC_HANDLERS_REASON as so, withCreateTokenLedger as sr, shareLogOnlyValues as ss, cyan as st, DeployEngine as t, isReadableOrphanRecord as ta, isNotImplemented as tc, refuseUnreadableCdkCrossAccountRefs as ti, AssetManifestLoader as tl, planFailedOps as tn, unreadableResourceEntries as to, constructPathOf as tr, hasMaskableValues as ts, isTruthyCfnBoolean as tt, renderNestedTemplateTreeDefect as u, malformedOrphanRecordsWarning as ua, importableOutputs as uc, callerEnvCredentials as ui, resolveCaptureObservedState as ul, inheritProducerRegions as un, shownType as uo, markDeleteStarted as ur, errorCauseChain as us, red as ut, mergeNoChangeOutputs as v, malformedResourcePropertiesWarning as va, WorkGraph as vc, s3BucketDomainName as vi, CFN_TEMPLATE_BODY_LIMIT as vl, WITHHELD_NAME_DISPLAY as vn, toCfnInteger as vo, definedAttributes as vr, withPhysicalNameCopies as vs, DeploymentEventsReader as vt, keptForReadoption as w, refuseMalformedNestedChildOutputs as wa, BOOTSTRAP_MARKER_PREFIX as wc, producerRegionsFromState as wi, decodeListingKey as wl, carriedExportAliasExposure as wn, configStringRefusal as wo, wouldReturnToSdkProvider as wr, identityKeyFor as ws, dropFailedHint as wt, isAccessDeniedError as x, malformedStateRefusalMessage as xa, loadPublishableAssetManifest as xc, s3BucketWebsiteUrl as xi, findLargeInlineResources as xl, secretBearing as xn, coerceCfnInteger as xo, ProviderRegistry as xr, dynamicReferenceTokens as xs, resolvePendingCloudControlRequests as xt, makeCanonicalizePropertiesFn as y, malformedResourcesWarning as ya, buildAssetRedirectMap as yc, s3BucketDualStackDomainName as yi, CFN_TEMPLATE_URL_LIMIT as yl, displayTextOrWithheld as yn, toFiniteNumber as yo, stringifyIfAssigned as yr, withoutPhysicalNameCopies as ys, DeploymentEventsStore as yt, perType as z, refuseMalformedResourcePropertiesForDestroy as za, dockerSpawnEnvWithSensitive as zc, parseWebACLArn as zi, displayAssemblyPath as zl, secretNamePrintingBag as zn, canonicalCoordinates as zo, deleteLeftInPlace as zr, wholeStringLeavesOf as zs, withNestedRevertRun as zt };
48504
+ //# sourceMappingURL=deploy-engine-DEvS2r2M.js.map