@go-to-k/cdkd 0.219.7 → 0.219.8

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.
@@ -3763,6 +3763,22 @@ var TemplateParser = class {
3763
3763
  return dependencies;
3764
3764
  }
3765
3765
  /**
3766
+ * Extract resource references (Ref / Fn::GetAtt / Fn::Sub / Fn::Join /
3767
+ * Fn::Select / Fn::Split) from an arbitrary template value.
3768
+ *
3769
+ * Public wrapper over the same recursive extraction `extractDependencies`
3770
+ * uses for Properties — exposed so the diff phase can compute
3771
+ * per-property reference edges (issue #807 replacement propagation)
3772
+ * without duplicating the intrinsic walk. Unlike `extractDependencies`
3773
+ * this does NOT include `DependsOn` (which is pure ordering, not a data
3774
+ * reference — a replaced dependency carries no new value to propagate).
3775
+ */
3776
+ extractReferences(value) {
3777
+ const references = /* @__PURE__ */ new Set();
3778
+ this.extractRefsFromValue(value, references);
3779
+ return references;
3780
+ }
3781
+ /**
3766
3782
  * Recursively extract Ref and Fn::GetAtt from a value
3767
3783
  */
3768
3784
  extractRefsFromValue(value, dependencies) {
@@ -4585,6 +4601,7 @@ var ReplacementRulesRegistry = class {
4585
4601
  var DiffCalculator = class DiffCalculator {
4586
4602
  logger = getLogger().child("DiffCalculator");
4587
4603
  replacementRules = new ReplacementRulesRegistry();
4604
+ parser = new TemplateParser();
4588
4605
  /**
4589
4606
  * Calculate changes needed to reach desired state
4590
4607
  *
@@ -4674,11 +4691,103 @@ var DiffCalculator = class DiffCalculator {
4674
4691
  });
4675
4692
  this.logger.debug(`DELETE: ${logicalId} (${currentResource.resourceType})`);
4676
4693
  }
4694
+ this.promoteReplacementDependents(changes, desiredTemplate);
4677
4695
  const summary = this.getSummary(changes);
4678
4696
  this.logger.debug(`Diff calculated: ${summary.create} CREATE, ${summary.update} UPDATE, ${summary.delete} DELETE, ${summary.noChange} NO_CHANGE`);
4679
4697
  return changes;
4680
4698
  }
4681
4699
  /**
4700
+ * Promote transitive dependents of to-be-replaced resources from
4701
+ * NO_CHANGE to UPDATE (issue #807).
4702
+ *
4703
+ * Diff-time intrinsic resolution runs against the CURRENT state, so a
4704
+ * dependent referencing a resource that will be REPLACED (new physical
4705
+ * ID — e.g. an `AWS::ECS::TaskDefinition` revision) compares equal and
4706
+ * stays NO_CHANGE; the deploy engine then never re-points it at the new
4707
+ * physical resource (for ECS: `UpdateService` is never issued and the
4708
+ * service keeps running the old, now-deregistered revision).
4709
+ * CloudFormation propagates the new physical ID to dependents — mirror
4710
+ * that here by walking reverse reference edges (`Ref` / `Fn::GetAtt` /
4711
+ * `Fn::Sub` and intrinsics nesting them — the same extraction the DAG
4712
+ * builder uses) from every replacement-triggering UPDATE and promoting
4713
+ * NO_CHANGE dependents to UPDATE.
4714
+ *
4715
+ * Promotion is safe even when speculative: the deploy engine re-resolves
4716
+ * the promoted resource's properties against the in-flight state map
4717
+ * (which the DAG guarantees already carries the dependency's new
4718
+ * physical ID) and skips the provider call if nothing actually changed.
4719
+ *
4720
+ * Promotion is transitive: each referencing property of a promoted
4721
+ * dependent is re-evaluated against the replacement rules, and if the
4722
+ * dependent is itself replacement-triggering (the referencing property
4723
+ * is immutable for its type), its own dependents are promoted in turn.
4724
+ * The same re-evaluation also applies to dependents that already had
4725
+ * their own (non-replacement) property changes — their referencing
4726
+ * property gains a synthetic PropertyChange so a replacement cascade is
4727
+ * not masked by an unrelated in-place change.
4728
+ */
4729
+ promoteReplacementDependents(changes, desiredTemplate) {
4730
+ const queue = [];
4731
+ for (const [logicalId, change] of changes) if (change.changeType === "UPDATE" && change.propertyChanges?.some((pc) => pc.requiresReplacement)) queue.push(logicalId);
4732
+ if (queue.length === 0) return;
4733
+ const dependentsOf = /* @__PURE__ */ new Map();
4734
+ for (const [logicalId, resource] of Object.entries(desiredTemplate.Resources)) {
4735
+ if (resource.Type === "AWS::CDK::Metadata") continue;
4736
+ for (const [propKey, propValue] of Object.entries(resource.Properties ?? {})) for (const referencedId of this.parser.extractReferences(propValue)) {
4737
+ if (referencedId === logicalId) continue;
4738
+ let dependents = dependentsOf.get(referencedId);
4739
+ if (!dependents) {
4740
+ dependents = /* @__PURE__ */ new Map();
4741
+ dependentsOf.set(referencedId, dependents);
4742
+ }
4743
+ let propKeys = dependents.get(logicalId);
4744
+ if (!propKeys) {
4745
+ propKeys = /* @__PURE__ */ new Set();
4746
+ dependents.set(logicalId, propKeys);
4747
+ }
4748
+ propKeys.add(propKey);
4749
+ }
4750
+ }
4751
+ const enqueued = new Set(queue);
4752
+ while (queue.length > 0) {
4753
+ const replacedId = queue.shift();
4754
+ const dependents = dependentsOf.get(replacedId);
4755
+ if (!dependents) continue;
4756
+ for (const [dependentId, refPropKeys] of dependents) {
4757
+ const change = changes.get(dependentId);
4758
+ if (!change) continue;
4759
+ if (change.changeType !== "NO_CHANGE" && change.changeType !== "UPDATE") continue;
4760
+ const existingPaths = new Set((change.propertyChanges ?? []).map((pc) => pc.path));
4761
+ const syntheticChanges = [];
4762
+ for (const propKey of refPropKeys) {
4763
+ if (existingPaths.has(propKey)) continue;
4764
+ const oldValue = change.currentProperties?.[propKey];
4765
+ const newValue = change.desiredProperties?.[propKey];
4766
+ syntheticChanges.push({
4767
+ path: propKey,
4768
+ oldValue,
4769
+ newValue,
4770
+ replacementPropagated: true,
4771
+ requiresReplacement: this.replacementRules.requiresReplacement(change.resourceType, propKey, void 0, void 0)
4772
+ });
4773
+ }
4774
+ if (syntheticChanges.length === 0) continue;
4775
+ if (change.changeType === "NO_CHANGE") {
4776
+ change.changeType = "UPDATE";
4777
+ change.propertyChanges = syntheticChanges;
4778
+ this.logger.debug(`UPDATE (promoted): ${dependentId} references replaced resource ${replacedId} via ${[...refPropKeys].join(", ")}`);
4779
+ } else {
4780
+ change.propertyChanges = [...change.propertyChanges ?? [], ...syntheticChanges];
4781
+ this.logger.debug(`UPDATE (augmented): ${dependentId} references replaced resource ${replacedId} via ${[...refPropKeys].join(", ")}`);
4782
+ }
4783
+ if (!enqueued.has(dependentId) && change.propertyChanges.some((pc) => pc.requiresReplacement)) {
4784
+ enqueued.add(dependentId);
4785
+ queue.push(dependentId);
4786
+ }
4787
+ }
4788
+ }
4789
+ }
4790
+ /**
4682
4791
  * Best-effort resolution of template property intrinsics against current state.
4683
4792
  *
4684
4793
  * Iterates top-level properties and resolves each independently: if resolution
@@ -12379,4 +12488,4 @@ var DeployEngine = class {
12379
12488
 
12380
12489
  //#endregion
12381
12490
  export { WorkGraph as A, resolveCaptureObservedState as B, DagBuilder as C, shouldRetainResource as D, S3StateBackend as E, runDockerStreaming as F, CFN_TEMPLATE_BODY_LIMIT as G, resolveStateBucketWithDefault as H, Synthesizer as I, findLargeInlineResources as J, CFN_TEMPLATE_URL_LIMIT as K, getDefaultStateBucketName as L, formatDockerLoginError as M, getDockerCmd as N, AssetPublisher as O, runDockerForeground as P, resolveBucketRegion as Q, getLegacyStateBucketName as R, DiffCalculator as S, LockManager as T, resolveStateBucketWithDefaultAndSource as U, resolveSkipPrefix as V, warnDeprecatedNoPrefixCliFlag as W, AssemblyReader as X, uploadCfnTemplate as Y, clearBucketRegionCache as Z, ProviderRegistry as _, withRetry as a, IntrinsicFunctionResolver as b, formatResourceLine as c, gray as d, green as f, collectInlinePolicyNamesManagedBySiblings as g, IAMRoleProvider as h, withResourceDeadline as i, buildDockerImage as j, stringifyValue as k, bold as l, yellow as m, DEFAULT_RESOURCE_WARN_AFTER_MS as n, isRetryableTransientError as o, red as p, MIGRATE_TMP_PREFIX as q, DeployEngine as r, IMPLICIT_DELETE_DEPENDENCIES as s, DEFAULT_RESOURCE_TIMEOUT_MS as t, cyan as u, findActionableSilentDrops as v, TemplateParser as w, applyRoleArnIfSet as x, CloudControlProvider as y, resolveApp as z };
12382
- //# sourceMappingURL=deploy-engine-BWfGeCMs.js.map
12491
+ //# sourceMappingURL=deploy-engine-Bl8wyosc.js.map