@go-to-k/cdkd 0.278.25 → 0.278.27

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 isThrottlingError, $t as setAwsClients, A as red, At as getLegacyStateBucketName, B as isTerminationProtectionPropagationError, Bt as CFN_TEMPLATE_BODY_LIMIT, C as isStatefulRecreateTargetSync, Ct as runDockerForeground, D as cyan, Dt as Synthesizer, E as bold, Et as getDockerImageBySourceHash, F as ProviderRegistry, Ft as resolveStateBucketWithDefault, G as normalizeAwsTagsToCfn, Gt as expectedOwnerParam, H as cfnRefValueFromPhysicalId, Ht as MIGRATE_TMP_PREFIX, I as findActionableSilentDrops, It as resolveStateBucketWithDefaultAndSource, J as applyRoleArnIfSet, K as resolveExplicitPhysicalId, Kt as AssemblyReader, L as CloudControlProvider, Lt as resolveUseCdkBootstrapAssets, M as IAMRoleProvider, Mt as resolveAutoAssetStorage, N as collectInlinePolicyNamesManagedBySiblings, Nt as resolveCaptureObservedState, O as gray, Ot as synthesisStatusMessage, P as clearOnUpdateRemoval, Pt as resolveSkipPrefix, Q as isRetryableTransientError, Qt as resetAwsClients, R as slowCcOperationTimeoutMs, Rt as stateBucketExistenceConfirmed, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as withErrorHandling, St as getDockerCmd, T as formatResourceLine, Tt as AssetManifestLoader, U as refStateLookupFromResource, Ut as findLargeInlineResources, V as IntrinsicFunctionResolver, Vt as CFN_TEMPLATE_URL_LIMIT, W as WAFv2WebACLProvider, Wt as uploadCfnTemplate, X as describeTypeWithThrottleRetry, Xt as AwsClients, Y as DiffCalculator, Yt as resolveBucketRegion, Z as withRetry, Zt as getAwsClients, _ as createPreDeleteFinalSnapshot, _t as parseBootstrapMarker, a as DeploymentEventsStore, an as LocalInvokeBuildError$1, at as shouldRetainResource, b as unsupportedFinalSnapshotError, bt as buildDockerImage, c as replayFailedOperations, ct as WorkGraph, d as IMPLICIT_DELETE_DEPENDENCIES, dn as PartialFailureError, dt as loadPublishableAssetManifest, et as DagBuilder, f as computeImplicitDeleteEdges, fn as ProvisioningError, ft as rewriteTemplateAssetReferences, g as ccRoutedFinalSnapshotError, gn as StackTerminationProtectionError, gt as getBootstrapMarkerKey, h as buildFinalSnapshotIdentifier, hn as StackHasActiveImportsError, ht as ensureAssetStorage, i as DeploymentEventsReader, in as DeployCancelledError, it as rebuildClientForBucketRegion, j as yellow, jt as resolveApp, k as green, kt as getDefaultStateBucketName, l as replayRollback, ln as MissingCdkCliError, lt as buildAssetRedirectMap, m as PRE_DELETE_SNAPSHOT_TYPES, mn as ResourceUpdateNotSupportedError, mt as BOOTSTRAP_MARKER_PREFIX, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as LockManager, o as planFailedOps, on as LocalMigrateError, ot as AssetPublisher, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as ResourceTimeoutError, pt as AssetModeResolver, q as assertRegionMatch, qt as processStackMessages, r as DeployEngine, rt as S3StateBackend, s as planRollback, sn as LocalStartServiceError, st as stringifyValue, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as CdkdError, tt as TemplateParser, u as withResourceDeadline, un as NestedStackChildDirectDestroyError, ut as createAssetRedirectResolver, v as isFinalSnapshotError, vt as validateAssetBucketName, w as renderStatefulReason, wt as runDockerStreaming, x as extractDeploymentEventError, xn as normalizeAwsError, xt as formatDockerLoginError, y as refusesFinalSnapshot, yt as validateContainerRepoName, z as disableInstanceApiTermination, zt as warnDeprecatedNoPrefixCliFlag } from "./deploy-engine-B65VTlrZ.js";
2
+ import { $ as isThrottlingError, $t as setAwsClients, A as red, At as getLegacyStateBucketName, B as isTerminationProtectionPropagationError, Bt as CFN_TEMPLATE_BODY_LIMIT, C as isStatefulRecreateTargetSync, Ct as runDockerForeground, D as cyan, Dt as Synthesizer, E as bold, Et as getDockerImageBySourceHash, F as ProviderRegistry, Ft as resolveStateBucketWithDefault, G as normalizeAwsTagsToCfn, Gt as expectedOwnerParam, H as cfnRefValueFromPhysicalId, Ht as MIGRATE_TMP_PREFIX, I as findActionableSilentDrops, It as resolveStateBucketWithDefaultAndSource, J as applyRoleArnIfSet, K as resolveExplicitPhysicalId, Kt as AssemblyReader, L as CloudControlProvider, Lt as resolveUseCdkBootstrapAssets, M as IAMRoleProvider, Mt as resolveAutoAssetStorage, N as collectInlinePolicyNamesManagedBySiblings, Nt as resolveCaptureObservedState, O as gray, Ot as synthesisStatusMessage, P as clearOnUpdateRemoval, Pt as resolveSkipPrefix, Q as isRetryableTransientError, Qt as resetAwsClients, R as slowCcOperationTimeoutMs, Rt as stateBucketExistenceConfirmed, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as withErrorHandling, St as getDockerCmd, T as formatResourceLine, Tt as AssetManifestLoader, U as refStateLookupFromResource, Ut as findLargeInlineResources, V as IntrinsicFunctionResolver, Vt as CFN_TEMPLATE_URL_LIMIT, W as WAFv2WebACLProvider, Wt as uploadCfnTemplate, X as describeTypeWithThrottleRetry, Xt as AwsClients, Y as DiffCalculator, Yt as resolveBucketRegion, Z as withRetry, Zt as getAwsClients, _ as createPreDeleteFinalSnapshot, _t as parseBootstrapMarker, a as DeploymentEventsStore, an as LocalInvokeBuildError$1, at as shouldRetainResource, b as unsupportedFinalSnapshotError, bt as buildDockerImage, c as replayFailedOperations, ct as WorkGraph, d as IMPLICIT_DELETE_DEPENDENCIES, dn as PartialFailureError, dt as loadPublishableAssetManifest, et as DagBuilder, f as computeImplicitDeleteEdges, fn as ProvisioningError, ft as rewriteTemplateAssetReferences, g as ccRoutedFinalSnapshotError, gn as StackTerminationProtectionError, gt as getBootstrapMarkerKey, h as buildFinalSnapshotIdentifier, hn as StackHasActiveImportsError, ht as ensureAssetStorage, i as DeploymentEventsReader, in as DeployCancelledError, it as rebuildClientForBucketRegion, j as yellow, jt as resolveApp, k as green, kt as getDefaultStateBucketName, l as replayRollback, ln as MissingCdkCliError, lt as buildAssetRedirectMap, m as PRE_DELETE_SNAPSHOT_TYPES, mn as ResourceUpdateNotSupportedError, mt as BOOTSTRAP_MARKER_PREFIX, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as LockManager, o as planFailedOps, on as LocalMigrateError, ot as AssetPublisher, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as ResourceTimeoutError, pt as AssetModeResolver, q as assertRegionMatch, qt as processStackMessages, r as DeployEngine, rt as S3StateBackend, s as planRollback, sn as LocalStartServiceError, st as stringifyValue, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as CdkdError, tt as TemplateParser, u as withResourceDeadline, un as NestedStackChildDirectDestroyError, ut as createAssetRedirectResolver, v as isFinalSnapshotError, vt as validateAssetBucketName, w as renderStatefulReason, wt as runDockerStreaming, x as extractDeploymentEventError, xn as normalizeAwsError, xt as formatDockerLoginError, y as refusesFinalSnapshot, yt as validateContainerRepoName, z as disableInstanceApiTermination, zt as warnDeprecatedNoPrefixCliFlag } from "./deploy-engine-0SZS30V4.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-BRlawK4H.js";
4
+ import { t as ASGProvider } from "./asg-provider-C2V8DwC0.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";
@@ -33105,10 +33105,10 @@ var GlueProvider = class {
33105
33105
  if (!this.client) this.client = new GlueClient(this.providerRegion ? { region: this.providerRegion } : {});
33106
33106
  return this.client;
33107
33107
  }
33108
- async create(logicalId, resourceType, properties) {
33108
+ async create(logicalId, resourceType, properties, context) {
33109
33109
  switch (resourceType) {
33110
33110
  case "AWS::Glue::Database": return this.createDatabase(logicalId, resourceType, properties);
33111
- case "AWS::Glue::Table": return this.createTable(logicalId, resourceType, properties);
33111
+ case "AWS::Glue::Table": return this.createTable(logicalId, resourceType, properties, context);
33112
33112
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId);
33113
33113
  }
33114
33114
  }
@@ -33191,7 +33191,7 @@ var GlueProvider = class {
33191
33191
  throw new ProvisioningError(`Failed to delete Glue Database ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
33192
33192
  }
33193
33193
  }
33194
- async createTable(logicalId, resourceType, properties) {
33194
+ async createTable(logicalId, resourceType, properties, context) {
33195
33195
  this.logger.debug(`Creating Glue Table ${logicalId}`);
33196
33196
  const databaseName = properties["DatabaseName"];
33197
33197
  if (!databaseName) throw new ProvisioningError(`DatabaseName is required for Glue Table ${logicalId}`, resourceType, logicalId);
@@ -33200,7 +33200,10 @@ var GlueProvider = class {
33200
33200
  const tableName = tableInput["Name"] ?? properties["Name"];
33201
33201
  if (!tableName) throw new ProvisioningError(`TableInput.Name or top-level Name is required for Glue Table ${logicalId}`, resourceType, logicalId);
33202
33202
  const catalogId = properties["CatalogId"];
33203
- assertIcebergTableInputAbsent(logicalId, resourceType, properties);
33203
+ enforceIcebergTableInputAbsent(logicalId, resourceType, properties, {
33204
+ warn: (message) => this.logger.warn(message),
33205
+ replayingState: context?.replayingState === true
33206
+ });
33204
33207
  const openTableFormatInput = properties["OpenTableFormatInput"];
33205
33208
  try {
33206
33209
  await this.getClient().send(new CreateTableCommand$1({
@@ -33990,15 +33993,35 @@ function findIcebergTableInputKey(properties) {
33990
33993
  * produce a late, cryptic AWS error. Failing fast with the working shape spelled
33991
33994
  * out is strictly more useful.
33992
33995
  *
33993
- * **CREATE only — update WARNS instead.** See the comment at the `updateTable`
33994
- * call site: rollback replays from cdkd STATE, and a table created by a
33995
- * pre-#1390 build carries the key in its state record, so refusing on update
33996
- * would make such a table unrollbackable with no template-side remedy. cdkd
33997
- * does not wire Glue's update-only `UpdateOpenTableFormatInput` shape and
33998
- * `buildTableInput` is an explicit allow-list, so warning there forwards
33999
- * nothing and loses no protection. One rollback arm is still exposed — the
34000
- * reverse-replacement re-create replays a state record through `create()`;
34001
- * see issue #1463 and the note at the `updateTable` call site.
33996
+ * **TEMPLATE creates only — every STATE REPLAY warns instead.** The refusal is
33997
+ * conditioned on the ORIGIN of the properties, not on the operation name:
33998
+ *
33999
+ * - `update()` warns unconditionally. Rollback replays from cdkd STATE, and a
34000
+ * table created by a pre-#1390 build carries the key in its state record, so
34001
+ * refusing there would make such a table unrollbackable with no
34002
+ * template-side remedy. cdkd does not wire Glue's update-only
34003
+ * `UpdateOpenTableFormatInput` shape and `buildTableInput` is an explicit
34004
+ * allow-list, so warning forwards nothing and loses no protection.
34005
+ * - `create()` warns when {@link CreateContext.replayingState} is set — the
34006
+ * rollback executor's reverse-replacement arm, which revives the OLD
34007
+ * resource from `previousState.properties` (issue #1463). Same reasoning:
34008
+ * the user cannot edit a state record from the template. Unlike the update
34009
+ * path this one DOES forward the value, so the re-created table is degraded
34010
+ * in exactly the way the original was (the CFn spelling is dropped by the
34011
+ * SDK serializer per #1390; the SDK spelling is sent and Glue rejects it) —
34012
+ * the warning says so, and the fix-forward is a `cdkd deploy`.
34013
+ * - Every deploy-engine `create()` refuses: the CREATE branch, the
34014
+ * property-driven replacement, the `--recreate-via-*` destroy-then-create,
34015
+ * the `--replace` delete-first fallback, and the update-failure replacement.
34016
+ * All are driven by freshly resolved TEMPLATE properties, so the user CAN
34017
+ * fix the input and a fast, actionable refusal is strictly better.
34018
+ *
34019
+ * The asymmetry constrains where this provider may re-create. `GlueProvider`
34020
+ * must never call `this.create()` from inside its own `update()` the way ACM /
34021
+ * IAM / Lambda-permission / SNS-subscription do: those internal re-creates
34022
+ * forward `update()`'s `properties` — a STATE record during a rollback replay —
34023
+ * and `update()` has no context parameter to carry the flag, so this refusal
34024
+ * would fire on a replay with no way to detect it.
34002
34025
  *
34003
34026
  * **Known bypass: the sticky Cloud Control route.** This is a GlueProvider
34004
34027
  * pre-flight, so it only runs on the SDK route. When a table's state record
@@ -34019,19 +34042,23 @@ function findIcebergTableInputKey(properties) {
34019
34042
  * relaxed, the rename MUST be restored in the same change — otherwise the
34020
34043
  * silent-drop bug of #1390 comes straight back.
34021
34044
  */
34022
- function assertIcebergTableInputAbsent(logicalId, resourceType, properties, physicalId) {
34045
+ function enforceIcebergTableInputAbsent(logicalId, resourceType, properties, options) {
34023
34046
  const key = findIcebergTableInputKey(properties);
34024
34047
  if (key === void 0) return;
34025
- throw new ProvisioningError(icebergTableInputRefusalMessage(logicalId, key, "create"), resourceType, logicalId, physicalId);
34048
+ if (options.replayingState) {
34049
+ options.warn(icebergTableInputRefusalMessage(logicalId, key, "create-replay"));
34050
+ return;
34051
+ }
34052
+ throw new ProvisioningError(icebergTableInputRefusalMessage(logicalId, key, "create"), resourceType, logicalId);
34026
34053
  }
34027
34054
  /**
34028
- * The shared #1454 message body, so the CREATE refusal and the UPDATE warning
34029
- * cannot drift apart. `mode` only changes the lead clause — everything after it
34030
- * (the probe evidence and the working shape) is identical, because the user's
34031
- * next action is the same either way.
34055
+ * The shared #1454 message body, so the CREATE refusal, the UPDATE warning and
34056
+ * the rollback-replay CREATE warning cannot drift apart. `mode` only changes
34057
+ * the lead clause — everything after it (the probe evidence and the working
34058
+ * shape) is identical, because the user's next action is the same either way.
34032
34059
  */
34033
34060
  function icebergTableInputRefusalMessage(logicalId, key, mode) {
34034
- return `AWS::Glue::Table ${logicalId}: OpenTableFormatInput.IcebergInput.${key} ${mode === "create" ? `cannot be deployed by AWS in any shape, so cdkd refuses it before calling Glue (issue #1454).` : "cannot be deployed by AWS in any shape and is IGNORED on update — cdkd does not wire Glue's update-only UpdateOpenTableFormatInput shape, so it forwards nothing here and only warns (issue #1454). A CREATE of this table would be refused outright."}\n Live-probed 2026-08-09 in us-east-1 (issue #1408): glue:CreateTable — the API cdkd calls — rejects every Iceberg table spec ("Location information cannot be null while creating an iceberg table" without a TableInput.StorageDescriptor, "Table metadata information present at multiple parts of input request" with one), and CloudFormation rolls the same template back with "Table metadata is expected only via TableInput or via IcebergTableInputProperties inside OpenTableFormatInput" — a property name that exists in neither the CFn registry schema (IcebergInput.IcebergTableInput) nor @aws-sdk/client-glue (IcebergInput.CreateIcebergTableInput).\n Working shape — put the table metadata in TableInput and leave IcebergInput carrying only the create-time directive:\n TableInput: { Name, TableType: 'EXTERNAL_TABLE', StorageDescriptor: { Location: 's3://your-bucket/prefix/', Columns: [...] } }\n OpenTableFormatInput: { IcebergInput: { MetadataOperation: 'CREATE' } } // Version: '2' is also accepted\n Glue then writes the Iceberg metadata itself — the created table comes back with Parameters.table_type = ICEBERG and a populated Parameters.metadata_location.`;
34061
+ return `AWS::Glue::Table ${logicalId}: OpenTableFormatInput.IcebergInput.${key} ${mode === "create" ? `cannot be deployed by AWS in any shape, so cdkd refuses it before calling Glue (issue #1454).` : mode === "create-replay" ? "cannot be deployed by AWS in any shape, so cdkd normally refuses it before calling Glue (issue #1454) — but this create is REPLAYING a historical cdkd STATE record (a rollback reverse-replacement re-creating the OLD table), not your template, and refusing would leave that table unrestorable with no template-side remedy, only a hand-edit of state.json. cdkd therefore warns and proceeds (issue #1463). The re-created table is DEGRADED exactly as the original was: under the CFn spelling IcebergTableInput the AWS SDK serializer drops the unknown member (issue #1390), so the table comes back without its Iceberg metadata; under the SDK spelling CreateIcebergTableInput the value IS sent and Glue rejects the call, failing this one rollback operation. Either way, fix it forward with 'cdkd deploy' using the working shape below." : "cannot be deployed by AWS in any shape and is IGNORED on update — cdkd does not wire Glue's update-only UpdateOpenTableFormatInput shape, so it forwards nothing here and only warns (issue #1454). A CREATE of this table would be refused outright."}\n Live-probed 2026-08-09 in us-east-1 (issue #1408): glue:CreateTable — the API cdkd calls — rejects every Iceberg table spec ("Location information cannot be null while creating an iceberg table" without a TableInput.StorageDescriptor, "Table metadata information present at multiple parts of input request" with one), and CloudFormation rolls the same template back with "Table metadata is expected only via TableInput or via IcebergTableInputProperties inside OpenTableFormatInput" — a property name that exists in neither the CFn registry schema (IcebergInput.IcebergTableInput) nor @aws-sdk/client-glue (IcebergInput.CreateIcebergTableInput).\n Working shape — put the table metadata in TableInput and leave IcebergInput carrying only the create-time directive:\n TableInput: { Name, TableType: 'EXTERNAL_TABLE', StorageDescriptor: { Location: 's3://your-bucket/prefix/', Columns: [...] } }\n OpenTableFormatInput: { IcebergInput: { MetadataOperation: 'CREATE' } } // Version: '2' is also accepted\n Glue then writes the Iceberg metadata itself — the created table comes back with Parameters.table_type = ICEBERG and a populated Parameters.metadata_location.`;
34035
34062
  }
34036
34063
  /**
34037
34064
  * Build the SDK `EncryptionConfiguration` from the CFn-shape input
@@ -48774,6 +48801,82 @@ async function runAccept(reports, stateBackend, stateConfig, awsClients, options
48774
48801
  * parent path — so `path.split('.', 1)` is always safe to extract the
48775
48802
  * top-level key.
48776
48803
  */
48804
+ /**
48805
+ * The AWS-authored property paths a `--revert` is about to DROP, for a
48806
+ * resource whose state predates observed-capture (issue #1478).
48807
+ *
48808
+ * The mechanism, and why this is not a Glue fix: `runRevert` picks the revert
48809
+ * baseline as `observedProperties ?? properties`. When `observedProperties` is
48810
+ * absent, the desired side is the raw TEMPLATE while the previous side is the
48811
+ * AWS-CURRENT snapshot — so {@link buildRevertNewProperties}, which overwrites
48812
+ * each drifted top-level key with the desired sub-shape wholesale, replaces
48813
+ * every AWS-authored key inside that subtree with the template's version. For
48814
+ * a Glue Iceberg table that silently wipes `table_type` /
48815
+ * `metadata_location`, the same outcome as issue #1461 reached by a different
48816
+ * trigger. The exposure is general: ANY resource where AWS writes into a bag
48817
+ * the template does not fully declare, on state written before
48818
+ * observed-capture.
48819
+ *
48820
+ * The user DID ask to push state over AWS, so this is not silent-wrong the way
48821
+ * #1461 was — but it is almost certainly not what they meant, and nothing told
48822
+ * them. So the chosen semantic is **warn and proceed**: it is the cheapest
48823
+ * honest step, preserves today's behavior, and does not foreclose the stricter
48824
+ * options (refuse outright, or treat "no observedProperties" as "previous is
48825
+ * unknown" so the merge preserves).
48826
+ *
48827
+ * Scoped deliberately:
48828
+ *
48829
+ * - **Only drifted top-level keys.** Non-drifted keys keep their AWS-current
48830
+ * value in `buildRevertNewProperties`, so nothing under them is dropped.
48831
+ * - **Only when `observedProperties` is absent.** With it present the desired
48832
+ * side is the deploy-time AWS snapshot, which already carries AWS-authored
48833
+ * fields — dropping one there is a legitimate revert of a real console
48834
+ * change, and warning about it would be noise on every run.
48835
+ * - **Only keys that vanish.** A key present on both sides with a different
48836
+ * VALUE is the drift the user asked to revert, not a silent loss.
48837
+ *
48838
+ * @returns dotted paths, sorted, e.g. `['Parameters.metadata_location']`.
48839
+ */
48840
+ function findRevertDroppedAwsKeys(drifts, desiredProperties, awsProperties) {
48841
+ const dropped = /* @__PURE__ */ new Set();
48842
+ const driftedTopLevelKeys = /* @__PURE__ */ new Set();
48843
+ for (const d of drifts) {
48844
+ const topLevelKey = d.path.split(".", 1)[0];
48845
+ if (topLevelKey) driftedTopLevelKeys.add(topLevelKey);
48846
+ }
48847
+ for (const key of driftedTopLevelKeys) {
48848
+ if (!(key in desiredProperties)) continue;
48849
+ collectMissingPaths(awsProperties[key], desiredProperties[key], key, dropped);
48850
+ }
48851
+ return [...dropped].sort();
48852
+ }
48853
+ /**
48854
+ * Walk the AWS-side value against the desired-side value, recording every path
48855
+ * present on the AWS side and absent on the desired side.
48856
+ *
48857
+ * Only plain objects are descended into. An ARRAY is compared wholesale — the
48858
+ * drift comparator itself treats arrays as single values (its paths never
48859
+ * carry an index), so an element-wise walk here would report positions the
48860
+ * rest of the revert path cannot reason about.
48861
+ */
48862
+ function collectMissingPaths(awsValue, desiredValue, path, out) {
48863
+ if (!isPlainRecord(awsValue)) return;
48864
+ if (!isPlainRecord(desiredValue)) {
48865
+ if (desiredValue === void 0) out.add(path);
48866
+ return;
48867
+ }
48868
+ for (const [key, value] of Object.entries(awsValue)) {
48869
+ const childPath = `${path}.${key}`;
48870
+ if (!(key in desiredValue)) {
48871
+ out.add(childPath);
48872
+ continue;
48873
+ }
48874
+ collectMissingPaths(value, desiredValue[key], childPath, out);
48875
+ }
48876
+ }
48877
+ function isPlainRecord(value) {
48878
+ return typeof value === "object" && value !== null && !Array.isArray(value);
48879
+ }
48777
48880
  function buildRevertNewProperties(drifts, desiredProperties, awsProperties) {
48778
48881
  const result = { ...awsProperties };
48779
48882
  for (const d of drifts) {
@@ -48901,6 +49004,16 @@ function printRevertPlan(reports) {
48901
49004
  const word = o.changes.length === 1 ? "property path" : "property paths";
48902
49005
  process.stdout.write(` → provider.update on ${o.logicalId} (${o.resourceType}): revert ${o.changes.length} ${word}\n`);
48903
49006
  for (const change of o.changes) process.stdout.write(` ${change.path}: ${formatScalar(change.awsValue)} -> ${formatScalar(change.stateValue)}\n`);
49007
+ const stateResource = report.state.resources[o.logicalId];
49008
+ if (stateResource && stateResource.observedProperties === void 0) {
49009
+ const dropped = findRevertDroppedAwsKeys(o.changes, stateResource.properties ?? {}, o.awsProperties);
49010
+ if (dropped.length > 0) {
49011
+ const word = dropped.length === 1 ? "value" : "values";
49012
+ process.stdout.write(` ! this resource has no observed-capture baseline, so the revert pushes the raw TEMPLATE and will DROP ${dropped.length} AWS-authored ${word}:\n`);
49013
+ for (const path of dropped) process.stdout.write(` ${path}\n`);
49014
+ process.stdout.write(" Re-deploy the stack first to populate observedProperties if you want these preserved.\n");
49015
+ }
49016
+ }
48904
49017
  }
48905
49018
  }
48906
49019
  }
@@ -65850,7 +65963,7 @@ function createMigrateCommand() {
65850
65963
  */
65851
65964
  function buildProgram() {
65852
65965
  const program = new Command();
65853
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.25");
65966
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.27");
65854
65967
  program.hook("preAction", (_thisCommand, actionCommand) => {
65855
65968
  const { profile } = actionCommand.optsWithGlobals();
65856
65969
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;