@go-to-k/cdkd 0.278.33 → 0.278.34

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 withRetry, $t as getAwsClients, A as red, At as synthesisStatusMessage, B as isTerminationProtectionPropagationError, Bt as stateBucketExistenceConfirmed, C as isStatefulRecreateTargetSync, Cn as normalizeAwsError, Ct as formatDockerLoginError, D as cyan, Dt as AssetManifestLoader, E as bold, Et as runDockerStreaming, F as ProviderRegistry, Ft as resolveCaptureObservedState, G as normalizeAwsTagsToCfn, Gt as findLargeInlineResources, H as cfnRefValueFromPhysicalId, Ht as CFN_TEMPLATE_BODY_LIMIT, I as findActionableSilentDrops, It as resolveSkipPrefix, J as readConfigString, Jt as AssemblyReader, K as resolveExplicitPhysicalId, Kt as uploadCfnTemplate, L as CloudControlProvider, Lt as resolveStateBucketWithDefault, M as IAMRoleProvider, Mt as getLegacyStateBucketName, N as collectInlinePolicyNamesManagedBySiblings, Nt as resolveApp, O as gray, Ot as getDockerImageBySourceHash, P as clearOnUpdateRemoval, Pt as resolveAutoAssetStorage, Q as describeTypeWithThrottleRetry, Qt as AwsClients, R as slowCcOperationTimeoutMs, Rt as resolveStateBucketWithDefaultAndSource, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, St as buildDockerImage, T as formatResourceLine, Tt as runDockerForeground, U as refStateLookupFromResource, Ut as CFN_TEMPLATE_URL_LIMIT, V as IntrinsicFunctionResolver, Vt as warnDeprecatedNoPrefixCliFlag, W as WAFv2WebACLProvider, Wt as MIGRATE_TMP_PREFIX, X as applyRoleArnIfSet, Y as requireConfigString, Yt as processStackMessages, Z as DiffCalculator, Zt as resolveBucketRegion, _ as createPreDeleteFinalSnapshot, _n as StackHasActiveImportsError, _t as ensureAssetStorage, a as DeploymentEventsStore, at as S3StateBackend, b as unsupportedFinalSnapshotError, bt as validateAssetBucketName, c as replayFailedOperations, cn as LocalMigrateError, ct as AssetPublisher, d as IMPLICIT_DELETE_DEPENDENCIES, dn as MissingCdkCliError, dt as buildAssetRedirectMap, en as resetAwsClients, et as isRetryableTransientError, f as computeImplicitDeleteEdges, fn as NestedStackChildDirectDestroyError, ft as createAssetRedirectResolver, g as ccRoutedFinalSnapshotError, gn as ResourceUpdateNotSupportedError, gt as BOOTSTRAP_MARKER_PREFIX, h as buildFinalSnapshotIdentifier, hn as ResourceTimeoutError, ht as AssetModeResolver, i as DeploymentEventsReader, it as LockManager, j as yellow, jt as getDefaultStateBucketName, k as green, kt as Synthesizer, l as replayRollback, ln as LocalStartServiceError, lt as stringifyValue, m as PRE_DELETE_SNAPSHOT_TYPES, mn as ProvisioningError, mt as rewriteTemplateAssetReferences, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as DagBuilder, o as planFailedOps, on as DeployCancelledError, ot as rebuildClientForBucketRegion, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as PartialFailureError, pt as loadPublishableAssetManifest, q as assertRegionMatch, qt as expectedOwnerParam, r as DeployEngine, rn as CdkdError, rt as TemplateParser, s as planRollback, sn as LocalInvokeBuildError$1, st as shouldRetainResource, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as setAwsClients, tt as isThrottlingError, u as withResourceDeadline, ut as WorkGraph, v as isFinalSnapshotError, vn as StackTerminationProtectionError, vt as getBootstrapMarkerKey, w as renderStatefulReason, wn as withErrorHandling, wt as getDockerCmd, x as extractDeploymentEventError, xt as validateContainerRepoName, y as refusesFinalSnapshot, yt as parseBootstrapMarker, z as disableInstanceApiTermination, zt as resolveUseCdkBootstrapAssets } from "./deploy-engine-BfPXYVg-.js";
2
+ import { $ as withRetry, $t as getAwsClients, A as red, At as synthesisStatusMessage, B as isTerminationProtectionPropagationError, Bt as stateBucketExistenceConfirmed, C as isStatefulRecreateTargetSync, Cn as normalizeAwsError, Ct as formatDockerLoginError, D as cyan, Dt as AssetManifestLoader, E as bold, Et as runDockerStreaming, F as ProviderRegistry, Ft as resolveCaptureObservedState, G as normalizeAwsTagsToCfn, Gt as findLargeInlineResources, H as cfnRefValueFromPhysicalId, Ht as CFN_TEMPLATE_BODY_LIMIT, I as findActionableSilentDrops, It as resolveSkipPrefix, J as readConfigString, Jt as AssemblyReader, K as resolveExplicitPhysicalId, Kt as uploadCfnTemplate, L as CloudControlProvider, Lt as resolveStateBucketWithDefault, M as IAMRoleProvider, Mt as getLegacyStateBucketName, N as collectInlinePolicyNamesManagedBySiblings, Nt as resolveApp, O as gray, Ot as getDockerImageBySourceHash, P as clearOnUpdateRemoval, Pt as resolveAutoAssetStorage, Q as describeTypeWithThrottleRetry, Qt as AwsClients, R as slowCcOperationTimeoutMs, Rt as resolveStateBucketWithDefaultAndSource, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, St as buildDockerImage, T as formatResourceLine, Tt as runDockerForeground, U as refStateLookupFromResource, Ut as CFN_TEMPLATE_URL_LIMIT, V as IntrinsicFunctionResolver, Vt as warnDeprecatedNoPrefixCliFlag, W as WAFv2WebACLProvider, Wt as MIGRATE_TMP_PREFIX, X as applyRoleArnIfSet, Y as requireConfigString, Yt as processStackMessages, Z as DiffCalculator, Zt as resolveBucketRegion, _ as createPreDeleteFinalSnapshot, _n as StackHasActiveImportsError, _t as ensureAssetStorage, a as DeploymentEventsStore, at as S3StateBackend, b as unsupportedFinalSnapshotError, bt as validateAssetBucketName, c as replayFailedOperations, cn as LocalMigrateError, ct as AssetPublisher, d as IMPLICIT_DELETE_DEPENDENCIES, dn as MissingCdkCliError, dt as buildAssetRedirectMap, en as resetAwsClients, et as isRetryableTransientError, f as computeImplicitDeleteEdges, fn as NestedStackChildDirectDestroyError, ft as createAssetRedirectResolver, g as ccRoutedFinalSnapshotError, gn as ResourceUpdateNotSupportedError, gt as BOOTSTRAP_MARKER_PREFIX, h as buildFinalSnapshotIdentifier, hn as ResourceTimeoutError, ht as AssetModeResolver, i as DeploymentEventsReader, it as LockManager, j as yellow, jt as getDefaultStateBucketName, k as green, kt as Synthesizer, l as replayRollback, ln as LocalStartServiceError, lt as stringifyValue, m as PRE_DELETE_SNAPSHOT_TYPES, mn as ProvisioningError, mt as rewriteTemplateAssetReferences, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as DagBuilder, o as planFailedOps, on as DeployCancelledError, ot as rebuildClientForBucketRegion, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as PartialFailureError, pt as loadPublishableAssetManifest, q as assertRegionMatch, qt as expectedOwnerParam, r as DeployEngine, rn as CdkdError, rt as TemplateParser, s as planRollback, sn as LocalInvokeBuildError$1, st as shouldRetainResource, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as setAwsClients, tt as isThrottlingError, u as withResourceDeadline, ut as WorkGraph, v as isFinalSnapshotError, vn as StackTerminationProtectionError, vt as getBootstrapMarkerKey, w as renderStatefulReason, wn as withErrorHandling, wt as getDockerCmd, x as extractDeploymentEventError, xt as validateContainerRepoName, y as refusesFinalSnapshot, yt as parseBootstrapMarker, z as disableInstanceApiTermination, zt as resolveUseCdkBootstrapAssets } from "./deploy-engine-Dp2u4Ktr.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-3kpNYstr.js";
4
+ import { t as ASGProvider } from "./asg-provider-Cta08VYI.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";
@@ -49259,48 +49259,128 @@ async function runAccept(reports, stateBackend, stateConfig, awsClients, options
49259
49259
  }
49260
49260
  }
49261
49261
  /**
49262
- * Build the `newProperties` object passed to `provider.update` during
49263
- * `--revert`. Strategy:
49262
+ * A top-level property NAME that carries tags.
49264
49263
  *
49265
- * 1. Start from `awsProperties` (the AWS-current snapshot returned
49266
- * by `readCurrentState`, which `runRevert` already passes as the
49267
- * `previousProperties` argument to `provider.update`).
49268
- * 2. For every top-level key whose subtree contains a drifted path,
49269
- * overwrite it with the corresponding sub-shape from
49270
- * `desiredProperties` (the state-recorded `observedProperties`).
49264
+ * The shape test below is deliberately NOT sufficient on its own: a top-level
49265
+ * `[{ Key, Value }]` list is not tag-exclusive — `LoadBalancerAttributes`,
49266
+ * `TargetGroupAttributes` and SSM `Association.Targets` all match it. No such
49267
+ * property can carry an `aws:`-prefixed or `AmazonECSManaged` key today, so the
49268
+ * carve-out would degrade to the identity of the old wholesale overwrite there
49269
+ * — but borrowing `canonicalizeTagListsDeep`'s heuristic for a WRITE decision
49270
+ * is not justified by its use for a SORT: a sort false positive is harmless, an
49271
+ * append is not.
49272
+ */
49273
+ function isTagListKey(key) {
49274
+ return key === "Tags" || key.endsWith("Tags");
49275
+ }
49276
+ /** A CFn-shaped tag list: a non-empty array whose every element has a string `Key`. */
49277
+ function isCfnTagList(value) {
49278
+ return Array.isArray(value) && value.length > 0 && value.every((t) => isPlainRecord(t) && typeof t.Key === "string");
49279
+ }
49280
+ /** The `Key` of every entry in a CFn tag list, in list order. */
49281
+ function tagKeys(tags) {
49282
+ return tags.map((t) => t["Key"]);
49283
+ }
49284
+ /**
49285
+ * AWS-SERVICE-authored tag keys a `--revert` must not strip (issue #1501).
49271
49286
  *
49272
- * Why "AWS-current base + drifted overlay" instead of "drifted-only
49273
- * partial":
49287
+ * `AmazonECSManaged` is attached by ECS when a capacity provider binds an Auto
49288
+ * Scaling group, and managed scaling stops working without it. That entry is
49289
+ * the load-bearing one.
49274
49290
  *
49275
- * Several providers' `update()` implementations diff
49276
- * `newProperties[K]` against `previousProperties[K]` and treat
49277
- * `newVal === undefined` as "remove K from AWS" (e.g.
49278
- * `SNSTopicProvider` calls `SetTopicAttributes(K, '')`,
49279
- * `IAMRoleProvider.updateManagedPolicies` detaches every previously
49280
- * attached policy when the new arg is undefined). Passing a
49281
- * drifted-only partial would silently clear non-drifted attributes
49282
- * on those providers. Sending the AWS-current value back as the
49283
- * "new" value for non-drifted keys keeps `JSON.stringify(newVal) ===
49284
- * JSON.stringify(oldVal)` so the diff is a no-op — no provider
49285
- * changes required.
49291
+ * The `aws:` prefix is DEFENSIVE rather than load-bearing, and the distinction
49292
+ * is worth stating: AWS reserves the prefix and rejects a write of one, and the
49293
+ * 45 providers that route reads through `normalizeAwsTagsToCfn` strip such keys
49294
+ * on the way in — so an `aws:` key can only reach the AWS side of a comparison
49295
+ * via the Cloud Control `readCurrentState` path, which returns the raw model.
49296
+ * It costs nothing to honor and cdkd can never have authored one.
49297
+ */
49298
+ const SERVICE_MANAGED_TAG_KEYS = /* @__PURE__ */ new Set(["AmazonECSManaged"]);
49299
+ /** Whether a tag key is AWS-service-authored, per {@link SERVICE_MANAGED_TAG_KEYS}. */
49300
+ function isServiceManagedTagKey(key) {
49301
+ return SERVICE_MANAGED_TAG_KEYS.has(key) || key.startsWith("aws:");
49302
+ }
49303
+ /**
49304
+ * Revert a TAG LIST, preserving AWS-SERVICE-authored entries (issue #1501).
49286
49305
  *
49287
- * For non-diff providers (e.g. `SQSQueueProvider` blindly pushes
49288
- * every defined key via `SetQueueAttributes`), the AWS-current
49289
- * value still gets serialised back to the same string AWS already
49290
- * has, so the round-trip is a no-op for the AWS resource state.
49291
- * The one exception is `readCurrentState`'s always-emit
49292
- * placeholder values — e.g. SQS `RedrivePolicy: {}` — which AWS
49293
- * rejects as invalid input even though they're round-tripped. That
49294
- * class of value (Class 2 / structurally-incomplete-when-empty) is
49295
- * handled by per-provider sanitize at the wire-layer; see the SQS
49296
- * provider's `serializeRedrivePolicy` helper for the canonical
49297
- * pattern.
49306
+ * Everything in {@link buildRevertNewProperties} overwrites a drifted top-level
49307
+ * key wholesale, which for `Tags` means "AWS ends up with exactly the tags cdkd
49308
+ * state recorded" — so a service-authored tag added after deploy is stripped.
49309
+ * The concrete case: ECS attaches `AmazonECSManaged` to an ASG when a capacity
49310
+ * provider binds it, and that tag is REQUIRED for managed scaling. Because any
49311
+ * CDK ASG declares at least a `Name` tag, `Tags` is template-DECLARED, so the
49312
+ * #1498 carve-out (undeclared + captured-empty) correctly does not apply — and
49313
+ * the tag list is an ARRAY, which {@link findRevertDroppedAwsKeys}'s walk
49314
+ * compares wholesale and never descends into. Post-revert the ASG kept the
49315
+ * capacity provider with its managed scaling silently broken (verified live
49316
+ * 2026-08-10).
49298
49317
  *
49299
- * The drift comparator never produces array-index segments
49300
- * (`Tags[0].Value`) — array drifts surface as a single entry on the
49301
- * parent path — so `path.split('.', 1)` is always safe to extract the
49302
- * top-level key.
49318
+ * The semantic: the baseline still WINS for every ordinary tag — one it has and
49319
+ * AWS lost is re-added, one whose value differs is reset, and a
49320
+ * user/console-added tag AWS alone has is still REMOVED. Only a
49321
+ * {@link isServiceManagedTagKey} entry survives.
49322
+ *
49323
+ * **This is the issue's option 2, not its option 1, and the choice was settled
49324
+ * by a live test rather than by taste.** Option 1 (revert the whole tag list as
49325
+ * a diff, so ANY out-of-band add survives) was implemented first and failed the
49326
+ * `drift-revert` integ at its final assertion: that fixture injects an
49327
+ * `IntegInjected` tag and requires `--revert` to strip it, i.e. "revert removes
49328
+ * a console-added tag" is an established contract with a test behind it.
49329
+ * Option 1 would redefine revert semantics for a whole property class — which
49330
+ * is exactly the design pass the issue said it needed — so the narrow safeguard
49331
+ * ships instead, fixing the reported breakage while leaving ordinary tag revert
49332
+ * untouched.
49333
+ *
49334
+ * Scope: TOP-LEVEL tag lists, which is where `buildRevertNewProperties`
49335
+ * operates. A tag list nested inside another property (an EC2 launch template's
49336
+ * `TagSpecifications`) still reverts wholesale.
49337
+ *
49338
+ * Order: baseline entries first in baseline order, then the preserved
49339
+ * service-managed entries. Providers apply tags as a set and the drift
49340
+ * comparator canonicalizes tag-list order on both sides
49341
+ * (`drift-normalize.ts`), so the order is for readability / determinism only.
49342
+ */
49343
+ function mergeTagListForRevert(baselineTags, awsTags) {
49344
+ const baselineKeys = new Set(tagKeys(baselineTags));
49345
+ const seen = /* @__PURE__ */ new Set();
49346
+ const preserved = awsTags.filter((t) => {
49347
+ const key = t["Key"];
49348
+ if (baselineKeys.has(key) || !isServiceManagedTagKey(key) || seen.has(key)) return false;
49349
+ seen.add(key);
49350
+ return true;
49351
+ });
49352
+ return [...baselineTags, ...preserved];
49353
+ }
49354
+ /**
49355
+ * The AWS-service-authored tag keys a `--revert` will PRESERVE rather than
49356
+ * strip, i.e. those present on the AWS side of a drifted top-level tag list,
49357
+ * absent from the revert baseline, and {@link isServiceManagedTagKey} (issue
49358
+ * #1501).
49359
+ *
49360
+ * Reported in the plan so the carve-out of {@link mergeTagListForRevert} is
49361
+ * visible BEFORE the confirmation prompt: a user who did want the tag gone
49362
+ * learns here that the revert will not do it.
49363
+ *
49364
+ * @returns dotted `<Key>.<TagKey>` paths, sorted, e.g. `['Tags.AmazonECSManaged']`.
49303
49365
  */
49366
+ function findRevertPreservedTagKeys(drifts, desiredProperties, awsProperties) {
49367
+ const preserved = /* @__PURE__ */ new Set();
49368
+ const driftedTopLevelKeys = /* @__PURE__ */ new Set();
49369
+ for (const d of drifts) {
49370
+ const topLevelKey = d.path.split(".", 1)[0];
49371
+ if (topLevelKey) driftedTopLevelKeys.add(topLevelKey);
49372
+ }
49373
+ for (const key of driftedTopLevelKeys) {
49374
+ if (!(key in desiredProperties)) continue;
49375
+ if (!isTagListKey(key)) continue;
49376
+ const desiredValue = desiredProperties[key];
49377
+ const awsValue = awsProperties[key];
49378
+ if (!(isCfnTagList(desiredValue) || Array.isArray(desiredValue) && desiredValue.length === 0) || !isCfnTagList(awsValue)) continue;
49379
+ const baselineKeys = new Set(tagKeys(desiredValue));
49380
+ for (const tagKey of tagKeys(awsValue)) if (!baselineKeys.has(tagKey) && isServiceManagedTagKey(tagKey)) preserved.add(`${key}.${tagKey}`);
49381
+ }
49382
+ return [...preserved].sort();
49383
+ }
49304
49384
  /**
49305
49385
  * The AWS-authored property paths a `--revert` is about to DROP, for a
49306
49386
  * resource whose state predates observed-capture (issue #1478).
@@ -49377,12 +49457,60 @@ function collectMissingPaths(awsValue, desiredValue, path, out) {
49377
49457
  function isPlainRecord(value) {
49378
49458
  return typeof value === "object" && value !== null && !Array.isArray(value);
49379
49459
  }
49460
+ /**
49461
+ * Build the `newProperties` object passed to `provider.update` during
49462
+ * `--revert`. Strategy:
49463
+ *
49464
+ * 1. Start from `awsProperties` (the AWS-current snapshot returned
49465
+ * by `readCurrentState`, which `runRevert` already passes as the
49466
+ * `previousProperties` argument to `provider.update`).
49467
+ * 2. For every top-level key whose subtree contains a drifted path,
49468
+ * overwrite it with the corresponding sub-shape from
49469
+ * `desiredProperties` (the state-recorded `observedProperties`).
49470
+ *
49471
+ * Why "AWS-current base + drifted overlay" instead of "drifted-only
49472
+ * partial":
49473
+ *
49474
+ * Several providers' `update()` implementations diff
49475
+ * `newProperties[K]` against `previousProperties[K]` and treat
49476
+ * `newVal === undefined` as "remove K from AWS" (e.g.
49477
+ * `SNSTopicProvider` calls `SetTopicAttributes(K, '')`,
49478
+ * `IAMRoleProvider.updateManagedPolicies` detaches every previously
49479
+ * attached policy when the new arg is undefined). Passing a
49480
+ * drifted-only partial would silently clear non-drifted attributes
49481
+ * on those providers. Sending the AWS-current value back as the
49482
+ * "new" value for non-drifted keys keeps `JSON.stringify(newVal) ===
49483
+ * JSON.stringify(oldVal)` so the diff is a no-op — no provider
49484
+ * changes required.
49485
+ *
49486
+ * For non-diff providers (e.g. `SQSQueueProvider` blindly pushes
49487
+ * every defined key via `SetQueueAttributes`), the AWS-current
49488
+ * value still gets serialised back to the same string AWS already
49489
+ * has, so the round-trip is a no-op for the AWS resource state.
49490
+ * The one exception is `readCurrentState`'s always-emit
49491
+ * placeholder values — e.g. SQS `RedrivePolicy: {}` — which AWS
49492
+ * rejects as invalid input even though they're round-tripped. That
49493
+ * class of value (Class 2 / structurally-incomplete-when-empty) is
49494
+ * handled by per-provider sanitize at the wire-layer; see the SQS
49495
+ * provider's `serializeRedrivePolicy` helper for the canonical
49496
+ * pattern.
49497
+ *
49498
+ * The drift comparator never produces array-index segments
49499
+ * (`Tags[0].Value`) — array drifts surface as a single entry on the
49500
+ * parent path — so `path.split('.', 1)` is always safe to extract the
49501
+ * top-level key.
49502
+ */
49380
49503
  function buildRevertNewProperties(drifts, desiredProperties, awsProperties) {
49381
49504
  const result = { ...awsProperties };
49382
49505
  for (const d of drifts) {
49383
49506
  const topLevelKey = d.path.split(".", 1)[0];
49384
49507
  if (!topLevelKey) continue;
49385
- if (topLevelKey in desiredProperties) result[topLevelKey] = desiredProperties[topLevelKey];
49508
+ if (topLevelKey in desiredProperties) {
49509
+ const desiredValue = desiredProperties[topLevelKey];
49510
+ const awsValue = awsProperties[topLevelKey];
49511
+ const baselineIsTagList = isCfnTagList(desiredValue) || Array.isArray(desiredValue) && desiredValue.length === 0;
49512
+ result[topLevelKey] = isTagListKey(topLevelKey) && baselineIsTagList && isCfnTagList(awsValue) ? mergeTagListForRevert(desiredValue, awsValue) : desiredValue;
49513
+ }
49386
49514
  }
49387
49515
  return result;
49388
49516
  }
@@ -49505,6 +49633,15 @@ function printRevertPlan(reports) {
49505
49633
  process.stdout.write(` → provider.update on ${o.logicalId} (${o.resourceType}): revert ${o.changes.length} ${word}\n`);
49506
49634
  for (const change of o.changes) process.stdout.write(` ${change.path}: ${formatScalar(change.awsValue)} -> ${formatScalar(change.stateValue)}\n`);
49507
49635
  const stateResource = report.state.resources[o.logicalId];
49636
+ if (stateResource) {
49637
+ const preserved = findRevertPreservedTagKeys(o.changes, stateResource.observedProperties ?? stateResource.properties ?? {}, o.awsProperties);
49638
+ if (preserved.length > 0) {
49639
+ const tagWord = preserved.length === 1 ? "tag" : "tags";
49640
+ process.stdout.write(` ! reverting this tag list KEEPS ${preserved.length} AWS-authored ${tagWord} the baseline does not carry:\n`);
49641
+ for (const path of preserved) process.stdout.write(` ${path}\n`);
49642
+ process.stdout.write(" Every other tag reverts normally. A service may require these (ECS needs AmazonECSManaged for managed scaling); 'aws:'-prefixed keys are AWS-reserved and cannot be removed by hand.\n");
49643
+ }
49644
+ }
49508
49645
  if (stateResource && stateResource.observedProperties === void 0) {
49509
49646
  const dropped = findRevertDroppedAwsKeys(o.changes, stateResource.properties ?? {}, o.awsProperties);
49510
49647
  if (dropped.length > 0) {
@@ -66463,7 +66600,7 @@ function createMigrateCommand() {
66463
66600
  */
66464
66601
  function buildProgram() {
66465
66602
  const program = new Command();
66466
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.33");
66603
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.34");
66467
66604
  program.hook("preAction", (_thisCommand, actionCommand) => {
66468
66605
  const { profile } = actionCommand.optsWithGlobals();
66469
66606
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;