@go-to-k/cdkd 0.280.18 → 0.280.19

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.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as requireConfigString, $t as processStackMessages, A as red, At as runDockerStreaming, B as isTerminationProtectionPropagationError, Bt as resolveSkipPrefix, C as isStatefulRecreateTargetSync, Ct as parseBootstrapMarker, D as cyan, Dn as normalizeAwsError, Dt as formatDockerLoginError, E as bold, Et as buildDockerImage, F as ProviderRegistry, Ft as getDefaultStateBucketName, G as normalizeAwsTagsToCfn, Gt as warnDeprecatedNoPrefixCliFlag, H as cfnRefValueFromPhysicalId, Ht as resolveStateBucketWithDefaultAndSource, I as findActionableSilentDrops, It as getLegacyStateBucketName, J as configStringRefusal, Jt as MIGRATE_TMP_PREFIX, K as resolveExplicitPhysicalId, Kt as CFN_TEMPLATE_BODY_LIMIT, L as CloudControlProvider, Lt as resolveApp, M as IAMRoleProvider, Mt as getDockerImageBySourceHash, N as collectInlinePolicyNamesManagedBySiblings, Nt as Synthesizer, O as gray, On as withErrorHandling, Ot as getDockerCmd, P as clearOnUpdateRemoval, Pt as synthesisStatusMessage, Q as requireConfigObject, Qt as AssemblyReader, R as slowCcOperationTimeoutMs, Rt as resolveAutoAssetStorage, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as StackTerminationProtectionError, St as getBootstrapMarkerKey, T as formatResourceLine, Tt as validateContainerRepoName, U as refStateLookupFromResource, Ut as resolveUseCdkBootstrapAssets, V as IntrinsicFunctionResolver, Vt as resolveStateBucketWithDefault, W as WAFv2WebACLProvider, Wt as stateBucketExistenceConfirmed, X as replayWarn, Xt as uploadCfnTemplate, Y as readConfigString, Yt as findLargeInlineResources, Z as requireConfigArray, Zt as expectedOwnerParam, _ as createPreDeleteFinalSnapshot, _n as PartialFailureError, _t as loadPublishableAssetManifest, a as DeploymentEventsStore, an as setAwsClients, at as isThrottlingError, b as unsupportedFinalSnapshotError, bn as ResourceUpdateNotSupportedError, bt as BOOTSTRAP_MARKER_PREFIX, c as replayFailedOperations, ct as LockManager, d as IMPLICIT_DELETE_DEPENDENCIES, dn as LocalInvokeBuildError$1, dt as shouldRetainResource, et as applyRoleArnIfSet, f as computeImplicitDeleteEdges, fn as LocalMigrateError, ft as AssetPublisher, g as ccRoutedFinalSnapshotError, gn as NestedStackChildDirectDestroyError, gt as createAssetRedirectResolver, h as buildFinalSnapshotIdentifier, hn as MissingCdkCliError, ht as buildAssetRedirectMap, i as DeploymentEventsReader, in as resetAwsClients, it as isRetryableTransientError, j as yellow, jt as AssetManifestLoader, k as green, kt as runDockerForeground, l as replayRollback, lt as S3StateBackend, m as PRE_DELETE_SNAPSHOT_TYPES, mt as WorkGraph, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as AwsClients, nt as describeTypeWithThrottleRetry, o as planFailedOps, ot as DagBuilder, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalStartServiceError, pt as stringifyValue, q as assertRegionMatch, qt as CFN_TEMPLATE_URL_LIMIT, r as DeployEngine, rn as getAwsClients, rt as withRetry, s as planRollback, sn as CdkdError, st as TemplateParser, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveBucketRegion, tt as DiffCalculator, u as withResourceDeadline, un as DeployCancelledError, ut as rebuildClientForBucketRegion, v as isFinalSnapshotError, vn as ProvisioningError, vt as rewriteTemplateAssetReferences, w as renderStatefulReason, wt as validateAssetBucketName, x as extractDeploymentEventError, xn as StackHasActiveImportsError, xt as ensureAssetStorage, y as refusesFinalSnapshot, yn as ResourceTimeoutError, yt as AssetModeResolver, z as disableInstanceApiTermination, zt as resolveCaptureObservedState } from "./deploy-engine-mvkiu-7h.js";
2
+ import { $ as requireConfigString, $t as processStackMessages, A as red, At as runDockerStreaming, B as isTerminationProtectionPropagationError, Bt as resolveSkipPrefix, C as isStatefulRecreateTargetSync, Ct as parseBootstrapMarker, D as cyan, Dn as normalizeAwsError, Dt as formatDockerLoginError, E as bold, Et as buildDockerImage, F as ProviderRegistry, Ft as getDefaultStateBucketName, G as normalizeAwsTagsToCfn, Gt as warnDeprecatedNoPrefixCliFlag, H as cfnRefValueFromPhysicalId, Ht as resolveStateBucketWithDefaultAndSource, I as findActionableSilentDrops, It as getLegacyStateBucketName, J as configStringRefusal, Jt as MIGRATE_TMP_PREFIX, K as resolveExplicitPhysicalId, Kt as CFN_TEMPLATE_BODY_LIMIT, L as CloudControlProvider, Lt as resolveApp, M as IAMRoleProvider, Mt as getDockerImageBySourceHash, N as collectInlinePolicyNamesManagedBySiblings, Nt as Synthesizer, O as gray, On as withErrorHandling, Ot as getDockerCmd, P as clearOnUpdateRemoval, Pt as synthesisStatusMessage, Q as requireConfigObject, Qt as AssemblyReader, R as slowCcOperationTimeoutMs, Rt as resolveAutoAssetStorage, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as StackTerminationProtectionError, St as getBootstrapMarkerKey, T as formatResourceLine, Tt as validateContainerRepoName, U as refStateLookupFromResource, Ut as resolveUseCdkBootstrapAssets, V as IntrinsicFunctionResolver, Vt as resolveStateBucketWithDefault, W as WAFv2WebACLProvider, Wt as stateBucketExistenceConfirmed, X as replayWarn, Xt as uploadCfnTemplate, Y as readConfigString, Yt as findLargeInlineResources, Z as requireConfigArray, Zt as expectedOwnerParam, _ as createPreDeleteFinalSnapshot, _n as PartialFailureError, _t as loadPublishableAssetManifest, a as DeploymentEventsStore, an as setAwsClients, at as isThrottlingError, b as unsupportedFinalSnapshotError, bn as ResourceUpdateNotSupportedError, bt as BOOTSTRAP_MARKER_PREFIX, c as replayFailedOperations, ct as LockManager, d as IMPLICIT_DELETE_DEPENDENCIES, dn as LocalInvokeBuildError$1, dt as shouldRetainResource, et as applyRoleArnIfSet, f as computeImplicitDeleteEdges, fn as LocalMigrateError, ft as AssetPublisher, g as ccRoutedFinalSnapshotError, gn as NestedStackChildDirectDestroyError, gt as createAssetRedirectResolver, h as buildFinalSnapshotIdentifier, hn as MissingCdkCliError, ht as buildAssetRedirectMap, i as DeploymentEventsReader, in as resetAwsClients, it as isRetryableTransientError, j as yellow, jt as AssetManifestLoader, k as green, kt as runDockerForeground, l as replayRollback, lt as S3StateBackend, m as PRE_DELETE_SNAPSHOT_TYPES, mt as WorkGraph, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as AwsClients, nt as describeTypeWithThrottleRetry, o as planFailedOps, ot as DagBuilder, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalStartServiceError, pt as stringifyValue, q as assertRegionMatch, qt as CFN_TEMPLATE_URL_LIMIT, r as DeployEngine, rn as getAwsClients, rt as withRetry, s as planRollback, sn as CdkdError, st as TemplateParser, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveBucketRegion, tt as DiffCalculator, u as withResourceDeadline, un as DeployCancelledError, ut as rebuildClientForBucketRegion, v as isFinalSnapshotError, vn as ProvisioningError, vt as rewriteTemplateAssetReferences, w as renderStatefulReason, wt as validateAssetBucketName, x as extractDeploymentEventError, xn as StackHasActiveImportsError, xt as ensureAssetStorage, y as refusesFinalSnapshot, yn as ResourceTimeoutError, yt as AssetModeResolver, z as disableInstanceApiTermination, zt as resolveCaptureObservedState } from "./deploy-engine-r4N9-tZD.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BYMEE-BS.js";
4
- import { t as ASGProvider } from "./asg-provider-C6cQkX3h.js";
4
+ import { t as ASGProvider } from "./asg-provider-B62D31jG.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
7
7
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -27873,6 +27873,78 @@ const TARGET_GROUP_ATTRIBUTE_DEFAULTS = {
27873
27873
  "proxy_protocol_v2.enabled": "false"
27874
27874
  };
27875
27875
  /**
27876
+ * Documented AWS defaults for the BOOLEAN / ENUM-valued LoadBalancer and
27877
+ * Listener attributes, used to reset a removed `LoadBalancerAttributes` /
27878
+ * `ListenerAttributes` entry.
27879
+ *
27880
+ * Unlike ModifyTargetGroupAttributes — which rejects an empty `Value` for
27881
+ * EVERY key — these two APIs accept `Value: ''` for the numeric and
27882
+ * free-form string attributes and REJECT it only where the value is
27883
+ * validated against a fixed set. Live A/B 2026-08-11 (issue #1609 item 1),
27884
+ * via the `alb` integ's removal phase:
27885
+ *
27886
+ * idle_timeout.timeout_seconds -> `Value: ''` ACCEPTED (numeric)
27887
+ * deletion_protection.enabled -> "The value of 'deletion_protection.enabled'
27888
+ * must be 'true' or 'false', but was ''"
27889
+ * routing.http.response.server.enabled (Listener)
27890
+ * -> same rejection, same shape
27891
+ *
27892
+ * A rejection fails the whole Modify* call, so ONE removed boolean took the
27893
+ * entire deploy down (and then the rollback with it). Hence: send the
27894
+ * documented default for the validated keys, and keep the empty string for
27895
+ * everything else — which is both these APIs' own "clear the override"
27896
+ * signal and the behaviour every non-boolean key already relied on.
27897
+ *
27898
+ * That fallback is the deliberate DIVERGENCE from
27899
+ * TARGET_GROUP_ATTRIBUTE_DEFAULTS, whose unknown-key arm warns and retains:
27900
+ * there the empty string is never valid, here it is valid for the majority
27901
+ * of keys, so falling back to it preserves working behaviour instead of
27902
+ * silently retaining a value the template asked to drop.
27903
+ *
27904
+ * A key is listed ONLY when ONE unconditional default is established for it —
27905
+ * documented in the SDK model, or (for
27906
+ * `routing.http.response.server.enabled`, which the model leaves unstated)
27907
+ * proven by the `alb` integ's live removal readback. Two exclusion classes
27908
+ * matter, and both are the difference between failing loudly and writing a
27909
+ * wrong value silently:
27910
+ *
27911
+ * - **Default depends on the LOAD BALANCER** — `load_balancing.cross_zone.enabled`
27912
+ * is always-on and unconfigurable for an ALB but defaults to false on an
27913
+ * NLB / GWLB, and `ipv6.deny_all_igw_traffic` is "false for internet-facing
27914
+ * load balancers and true for internal load balancers". cdkd knows neither
27915
+ * the type nor the scheme at diff time, so a hardcoded entry would send the
27916
+ * WRONG value for half of all load balancers — and since it is a valid
27917
+ * boolean, AWS accepts it. For `ipv6.deny_all_igw_traffic` that means
27918
+ * silently un-blocking internet-gateway access on an INTERNAL load balancer.
27919
+ * - **No documented default at all** — `dns_record.client_routing_policy`
27920
+ * enumerates its possible values but states no default.
27921
+ *
27922
+ * Both classes keep the empty-string behaviour they have always had, so a
27923
+ * removal there fails loudly (for a validated key) instead of guessing.
27924
+ *
27925
+ * Source: the `@aws-sdk/client-elastic-load-balancing-v2` model docs for
27926
+ * `ModifyLoadBalancerAttributes` / `ModifyListenerAttributes`, checked per key;
27927
+ * `routing.http.response.server.enabled`'s default is additionally LIVE-proven
27928
+ * by the `alb` integ's removal readback rather than taken from the docs.
27929
+ */
27930
+ const LOAD_BALANCER_ATTRIBUTE_DEFAULTS = {
27931
+ "deletion_protection.enabled": "false",
27932
+ "access_logs.s3.enabled": "false",
27933
+ "connection_logs.s3.enabled": "false",
27934
+ "health_check_logs.s3.enabled": "false",
27935
+ "routing.http.desync_mitigation_mode": "defensive",
27936
+ "routing.http.drop_invalid_header_fields.enabled": "false",
27937
+ "routing.http.preserve_host_header.enabled": "false",
27938
+ "routing.http.x_amzn_tls_version_and_cipher_suite.enabled": "false",
27939
+ "routing.http.xff_client_port.enabled": "false",
27940
+ "routing.http.xff_header_processing.mode": "append",
27941
+ "routing.http2.enabled": "true",
27942
+ "waf.fail_open.enabled": "false",
27943
+ "zonal_shift.config.enabled": "false"
27944
+ };
27945
+ /** @see LOAD_BALANCER_ATTRIBUTE_DEFAULTS — same rule, Listener key table. */
27946
+ const LISTENER_ATTRIBUTE_DEFAULTS = { "routing.http.response.server.enabled": "true" };
27947
+ /**
27876
27948
  * AWS ELBv2 Provider
27877
27949
  *
27878
27950
  * Implements resource provisioning for ELBv2 resources:
@@ -28085,7 +28157,7 @@ var ELBv2Provider = class {
28085
28157
  return out;
28086
28158
  };
28087
28159
  if (JSON.stringify(stripHandled(properties)) !== JSON.stringify(stripHandled(previousProperties))) throw new ResourceUpdateNotSupportedError("AWS::ElasticLoadBalancingV2::LoadBalancer", logicalId, "ELBv2 LoadBalancer Name / Type / Scheme are immutable on AWS — none of the ELBv2 Modify* / Set* APIs accept these fields; they are fixed at creation. cdkd handles LoadBalancerAttributes / Subnets / SubnetMappings / SecurityGroups / IpAddressType / Tags in-place; for Name / Type / Scheme re-deploy with cdkd deploy --replace, or destroy + redeploy the stack.");
28088
- const submittedAttrs = this.diffAttributes(this.normalizeAttributes(properties["LoadBalancerAttributes"]), this.normalizeAttributes(previousProperties["LoadBalancerAttributes"]));
28160
+ const submittedAttrs = this.diffAttributes(this.normalizeAttributes(properties["LoadBalancerAttributes"]), this.normalizeAttributes(previousProperties["LoadBalancerAttributes"]), this.attributeRemovalResolver(LOAD_BALANCER_ATTRIBUTE_DEFAULTS));
28089
28161
  if (submittedAttrs.length > 0) {
28090
28162
  await this.getClient().send(new ModifyLoadBalancerAttributesCommand({
28091
28163
  LoadBalancerArn: physicalId,
@@ -28309,10 +28381,13 @@ var ELBv2Provider = class {
28309
28381
  UnhealthyThresholdCount: properties["UnhealthyThresholdCount"] !== void 0 ? Number(properties["UnhealthyThresholdCount"]) : void 0,
28310
28382
  ...matcher && { Matcher: matcher }
28311
28383
  }));
28312
- const tgAttrDiff = this.diffAttributes(this.normalizeAttributes(properties["TargetGroupAttributes"]), this.normalizeAttributes(previousProperties["TargetGroupAttributes"]), (key) => {
28313
- const fallback = TARGET_GROUP_ATTRIBUTE_DEFAULTS[key];
28314
- if (fallback === void 0) this.logger.warn(`TargetGroup attribute ${key} was removed from the template but has no documented default cdkd can reset it to — the live value is retained. Set the attribute explicitly to change it.`);
28315
- return fallback;
28384
+ const tgAttrDiff = this.diffAttributes(this.normalizeAttributes(properties["TargetGroupAttributes"]), this.normalizeAttributes(previousProperties["TargetGroupAttributes"]), (key, currentValue) => {
28385
+ const fallback = Object.hasOwn(TARGET_GROUP_ATTRIBUTE_DEFAULTS, key) ? TARGET_GROUP_ATTRIBUTE_DEFAULTS[key] : void 0;
28386
+ if (fallback === void 0) {
28387
+ this.logger.warn(`TargetGroup attribute ${key} was removed from the template but has no documented default cdkd can reset it to — the live value is retained. Set the attribute explicitly to change it.`);
28388
+ return;
28389
+ }
28390
+ return currentValue === fallback ? void 0 : fallback;
28316
28391
  });
28317
28392
  if (tgAttrDiff.length > 0) {
28318
28393
  await this.getClient().send(new ModifyTargetGroupAttributesCommand({
@@ -28444,7 +28519,7 @@ var ELBv2Provider = class {
28444
28519
  ...alpnPolicy && alpnPolicy.length > 0 && { AlpnPolicy: alpnPolicy },
28445
28520
  ...mutualAuth !== void 0 && { MutualAuthentication: mutualAuth }
28446
28521
  }));
28447
- const submittedAttrs = this.diffAttributes(this.normalizeAttributes(properties["ListenerAttributes"]), this.normalizeAttributes(previousProperties["ListenerAttributes"]));
28522
+ const submittedAttrs = this.diffAttributes(this.normalizeAttributes(properties["ListenerAttributes"]), this.normalizeAttributes(previousProperties["ListenerAttributes"]), this.attributeRemovalResolver(LISTENER_ATTRIBUTE_DEFAULTS));
28448
28523
  if (submittedAttrs.length > 0) {
28449
28524
  await withRetry(() => this.getClient().send(new ModifyListenerAttributesCommand({
28450
28525
  ListenerArn: physicalId,
@@ -28522,14 +28597,28 @@ var ELBv2Provider = class {
28522
28597
  /**
28523
28598
  * Key-diff two normalized `{Key, Value}` attribute lists into the payload
28524
28599
  * for a Modify*Attributes call: changed values from `newAttrs` win, and
28525
- * keys present only in `oldAttrs` are pushed back through `removalValue` —
28526
- * by default the empty string, which ModifyLoadBalancerAttributes /
28527
- * ModifyListenerAttributes accept as "clear the override". A resolver
28528
- * returning `undefined` SKIPS the entry (the resolver owns any warn).
28529
- * An empty return means nothing changed and the call should be skipped.
28530
- * Shared by the LoadBalancer / TargetGroup / Listener update paths.
28531
- */
28532
- diffAttributes(newAttrs, oldAttrs, removalValue = () => "") {
28600
+ * keys present only in `oldAttrs` are pushed back through `removalValue`.
28601
+ * A resolver returning `undefined` SKIPS the entry (the resolver owns any
28602
+ * warn). An empty return means nothing changed and the call should be
28603
+ * skipped. Shared by the LoadBalancer / TargetGroup / Listener update
28604
+ * paths and all three pass their OWN resolver, because the three APIs
28605
+ * disagree about what resets an attribute:
28606
+ *
28607
+ * - LoadBalancer / Listener: the empty string clears a numeric or
28608
+ * free-form-string override, but a BOOLEAN / ENUM key rejects it and
28609
+ * fails the whole call, so those take a documented default
28610
+ * (`LOAD_BALANCER_ATTRIBUTE_DEFAULTS` / `LISTENER_ATTRIBUTE_DEFAULTS`).
28611
+ * - TargetGroup: the empty string is rejected for EVERY key, so the
28612
+ * documented default is the only reset and an unknown key warns and
28613
+ * retains (`TARGET_GROUP_ATTRIBUTE_DEFAULTS`).
28614
+ *
28615
+ * All three behaviours were live-verified 2026-08-11. `removalValue` is
28616
+ * REQUIRED rather than defaulted: there is no reset value that is correct
28617
+ * for all three APIs, so a caller that forgets to pass one should fail to
28618
+ * compile instead of silently inheriting the empty string that two of the
28619
+ * three reject.
28620
+ */
28621
+ diffAttributes(newAttrs, oldAttrs, removalValue) {
28533
28622
  const newAttrMap = new Map(newAttrs.map((a) => [a.Key, a.Value]));
28534
28623
  const oldAttrMap = new Map(oldAttrs.map((a) => [a.Key, a.Value]));
28535
28624
  const submitted = [];
@@ -28537,8 +28626,8 @@ var ELBv2Provider = class {
28537
28626
  Key: k,
28538
28627
  Value: v
28539
28628
  });
28540
- for (const [k] of oldAttrMap) if (!newAttrMap.has(k)) {
28541
- const value = removalValue(k);
28629
+ for (const [k, currentValue] of oldAttrMap) if (!newAttrMap.has(k)) {
28630
+ const value = removalValue(k, currentValue);
28542
28631
  if (value !== void 0) submitted.push({
28543
28632
  Key: k,
28544
28633
  Value: value
@@ -28547,6 +28636,40 @@ var ELBv2Provider = class {
28547
28636
  return submitted;
28548
28637
  }
28549
28638
  /**
28639
+ * Build the `removalValue` resolver for the LoadBalancer / Listener attribute
28640
+ * arms: reset a removed key to its documented default, or to `''` (which
28641
+ * those two APIs accept as "clear the override") when no default is known.
28642
+ *
28643
+ * **A key already AT its default is SKIPPED, and that is a safety property,
28644
+ * not an optimization.** The previous side is not always a template: `cdkd
28645
+ * drift --revert` calls `update(..., newProperties, outcome.awsProperties)`
28646
+ * (`src/cli/commands/drift.ts`), so `oldAttrs` can be the FULL
28647
+ * `readCurrentState` snapshot — every attribute AWS reports, including the
28648
+ * ~18 the user never templated. Against a state record with no
28649
+ * `observedProperties` the desired side is the template's one or two keys,
28650
+ * so every untemplated key looks REMOVED and would be "reset".
28651
+ *
28652
+ * Writing a documented default there would silently disable deletion
28653
+ * protection, access / connection logs, HTTP/2, WAF fail-open and zonal
28654
+ * shift on a live load balancer. Before the defaults table existed this was
28655
+ * accidentally safe — `''` fails validation, so the whole revert aborted and
28656
+ * changed nothing — so introducing the table without this guard would have
28657
+ * converted a loud refusal into a silent destructive write.
28658
+ *
28659
+ * The skip is exact rather than heuristic: a key the user never templated is
28660
+ * BY DEFINITION sitting at its default, so it is skipped; a key the template
28661
+ * really did set is set to a NON-default value (otherwise templating it
28662
+ * would be a no-op), so its reset still fires. A template that set a key to
28663
+ * exactly its default and then dropped it is skipped too — correctly, since
28664
+ * there is nothing to change.
28665
+ */
28666
+ attributeRemovalResolver(defaults) {
28667
+ return (key, currentValue) => {
28668
+ const fallback = Object.hasOwn(defaults, key) ? defaults[key] : "";
28669
+ return currentValue === fallback ? void 0 : fallback;
28670
+ };
28671
+ }
28672
+ /**
28550
28673
  * Normalize a CFn `Targets` value (an array of `{ Id, Port?,
28551
28674
  * AvailabilityZone? }` objects) into the SDK's `TargetDescription[]` shape.
28552
28675
  * Entries missing an `Id` are dropped (Id is the only required member);
@@ -69797,7 +69920,7 @@ function createMigrateCommand() {
69797
69920
  */
69798
69921
  function buildProgram() {
69799
69922
  const program = new Command();
69800
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.18");
69923
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.19");
69801
69924
  program.hook("preAction", (_thisCommand, actionCommand) => {
69802
69925
  const { profile } = actionCommand.optsWithGlobals();
69803
69926
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;