@go-to-k/cdkd 0.219.7 → 0.219.9

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
2
  import { A as withErrorHandling, B as generateResourceNameWithFallback, C as StackHasActiveImportsError, F as getLiveRenderer, H as withStackName, I as PATTERN_B_NAME_PROPERTIES, L as PATTERN_B_RESOURCE_TYPES, M as getLogger, P as runStackBuffered, S as ResourceUpdateNotSupportedError, V as withSkipPrefix, _ as MissingCdkCliError, a as assertRegionMatch, b as ProvisioningError, f as LocalInvokeBuildError$1, i as resolveExplicitPhysicalId, k as normalizeAwsError, l as CdkdError, m as LocalStartServiceError, n as matchesCdkPath, o as disableInstanceApiTermination, p as LocalMigrateError, r as normalizeAwsTagsToCfn, s as isTerminationProtectionPropagationError, t as CDK_PATH_TAG, v as NestedStackChildDirectDestroyError, w as StackTerminationProtectionError, x as ResourceTimeoutError, y as PartialFailureError, z as generateResourceName } from "./import-helpers-wLipXr5g.js";
3
3
  import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-pjPwZz1r.js";
4
- import { A as WorkGraph, B as resolveCaptureObservedState, C as DagBuilder, D as shouldRetainResource, E as S3StateBackend, F as runDockerStreaming, G as CFN_TEMPLATE_BODY_LIMIT, H as resolveStateBucketWithDefault, I as Synthesizer, J as findLargeInlineResources, K as CFN_TEMPLATE_URL_LIMIT, L as getDefaultStateBucketName, M as formatDockerLoginError, N as getDockerCmd, O as AssetPublisher, P as runDockerForeground, Q as resolveBucketRegion, R as getLegacyStateBucketName, S as DiffCalculator, T as LockManager, U as resolveStateBucketWithDefaultAndSource, V as resolveSkipPrefix, W as warnDeprecatedNoPrefixCliFlag, X as AssemblyReader, Y as uploadCfnTemplate, _ as ProviderRegistry, a as withRetry, b as IntrinsicFunctionResolver, c as formatResourceLine, d as gray, f as green, g as collectInlinePolicyNamesManagedBySiblings, h as IAMRoleProvider, i as withResourceDeadline, j as buildDockerImage, k as stringifyValue, l as bold, m as yellow, n as DEFAULT_RESOURCE_WARN_AFTER_MS, o as isRetryableTransientError, p as red, q as MIGRATE_TMP_PREFIX, r as DeployEngine, s as IMPLICIT_DELETE_DEPENDENCIES, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as cyan, v as findActionableSilentDrops, w as TemplateParser, x as applyRoleArnIfSet, y as CloudControlProvider, z as resolveApp } from "./deploy-engine-BWfGeCMs.js";
4
+ import { A as WorkGraph, B as resolveCaptureObservedState, C as DagBuilder, D as shouldRetainResource, E as S3StateBackend, F as runDockerStreaming, G as CFN_TEMPLATE_BODY_LIMIT, H as resolveStateBucketWithDefault, I as Synthesizer, J as findLargeInlineResources, K as CFN_TEMPLATE_URL_LIMIT, L as getDefaultStateBucketName, M as formatDockerLoginError, N as getDockerCmd, O as AssetPublisher, P as runDockerForeground, Q as resolveBucketRegion, R as getLegacyStateBucketName, S as DiffCalculator, T as LockManager, U as resolveStateBucketWithDefaultAndSource, V as resolveSkipPrefix, W as warnDeprecatedNoPrefixCliFlag, X as AssemblyReader, Y as uploadCfnTemplate, _ as ProviderRegistry, a as withRetry, b as IntrinsicFunctionResolver, c as formatResourceLine, d as gray, f as green, g as collectInlinePolicyNamesManagedBySiblings, h as IAMRoleProvider, i as withResourceDeadline, j as buildDockerImage, k as stringifyValue, l as bold, m as yellow, n as DEFAULT_RESOURCE_WARN_AFTER_MS, o as isRetryableTransientError, p as red, q as MIGRATE_TMP_PREFIX, r as DeployEngine, s as IMPLICIT_DELETE_DEPENDENCIES, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as cyan, v as findActionableSilentDrops, w as TemplateParser, x as applyRoleArnIfSet, y as CloudControlProvider, z as resolveApp } from "./deploy-engine-DVP4vDXG.js";
5
5
  import { t as ASGProvider } from "./asg-provider-B_hrCxRx.js";
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
7
  import { randomBytes, randomUUID } from "node:crypto";
@@ -33762,8 +33762,20 @@ function countProtectedResources(state) {
33762
33762
  * - Switches `process.env.AWS_REGION` for the duration of the destroy when
33763
33763
  * the stack's recorded region differs from `baseRegion`. Restored in the
33764
33764
  * `finally` block.
33765
+ * - Persists state incrementally during the delete loop (issue #804):
33766
+ * each successfully deleted resource is removed from the state object
33767
+ * and the trimmed state is written back to S3, mirroring deploy's
33768
+ * `saveStateAfterResource`. An interrupted destroy therefore leaves a
33769
+ * state file listing only resources that still exist, so a re-run does
33770
+ * not replay deletes against already-deleted resources. Persist
33771
+ * failures are logged and never fail the destroy. Every persisted
33772
+ * destroy snapshot CLEARS `outputs` (and drops `imports` / `outputReads`)
33773
+ * so a partial-destroy state never advertises an export / import whose
33774
+ * backing resource is gone — the in-memory `state` the strong-ref check
33775
+ * above reads is untouched.
33765
33776
  * - On full success, deletes the state file. On any failure, the state
33766
- * file is preserved so the user can retry.
33777
+ * file is preserved (trimmed to the remaining resources, outputs/imports
33778
+ * cleared) so the user can retry.
33767
33779
  */
33768
33780
  async function runDestroyForStack(stackName, state, ctx) {
33769
33781
  const logger = getLogger();
@@ -33843,6 +33855,27 @@ async function runDestroyForStack(stackName, state, ctx) {
33843
33855
  throw new StackHasActiveImportsError(stackName, regionForState, consumers);
33844
33856
  }
33845
33857
  }
33858
+ const remainingResources = { ...state.resources };
33859
+ const buildDestroySnapshot = () => {
33860
+ const { imports: _imports, outputReads: _outputReads, ...rest } = state;
33861
+ return {
33862
+ ...rest,
33863
+ resources: { ...remainingResources },
33864
+ outputs: {},
33865
+ lastModified: Date.now()
33866
+ };
33867
+ };
33868
+ let saveChain = Promise.resolve();
33869
+ const persistStateAfterDelete = (logicalId) => {
33870
+ saveChain = saveChain.then(async () => {
33871
+ try {
33872
+ await ctx.stateBackend.saveState(stackName, regionForState, buildDestroySnapshot());
33873
+ logger.debug(`State persisted after deleting ${logicalId}`);
33874
+ } catch (error) {
33875
+ logger.warn(`Failed to persist state after deleting ${logicalId} (continuing): ${error instanceof Error ? error.message : String(error)}`);
33876
+ }
33877
+ });
33878
+ };
33846
33879
  const renderer = getLiveRenderer();
33847
33880
  renderer.start();
33848
33881
  try {
@@ -33946,12 +33979,16 @@ async function runDestroyForStack(stackName, state, ctx) {
33946
33979
  renderer.removeTask(logicalId);
33947
33980
  logger.info(` ${formatResourceLine("deleted", logicalId, resource.resourceType)}`);
33948
33981
  result.deletedCount++;
33982
+ delete remainingResources[logicalId];
33983
+ persistStateAfterDelete(logicalId);
33949
33984
  } catch (error) {
33950
33985
  renderer.removeTask(logicalId);
33951
33986
  const msg = error instanceof Error ? error.message : String(error);
33952
33987
  if (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException")) {
33953
33988
  logger.debug(` ${logicalId} already deleted, removing from state`);
33954
33989
  result.deletedCount++;
33990
+ delete remainingResources[logicalId];
33991
+ persistStateAfterDelete(logicalId);
33955
33992
  } else if (error instanceof ResourceTimeoutError) {
33956
33993
  const wrapped = new ProvisioningError(error.message, resource.resourceType, logicalId, resource.physicalId, error);
33957
33994
  logger.error(` ✗ Failed to delete ${logicalId}:`, wrapped.message);
@@ -33966,16 +34003,25 @@ async function runDestroyForStack(stackName, state, ctx) {
33966
34003
  });
33967
34004
  await Promise.all(deletePromises);
33968
34005
  }
34006
+ await saveChain;
33969
34007
  if (result.errorCount === 0) {
33970
34008
  await ctx.stateBackend.deleteState(stackName, regionForState);
33971
34009
  logger.debug("State deleted");
33972
34010
  if (ctx.exportIndexStore) await ctx.exportIndexStore.removeStack(stackName, regionForState);
33973
- } else logger.warn(`${result.errorCount} resource(s) failed to delete. State preserved.`);
34011
+ } else {
34012
+ try {
34013
+ await ctx.stateBackend.saveState(stackName, regionForState, buildDestroySnapshot());
34014
+ } catch (error) {
34015
+ logger.warn(`Failed to persist remaining state after partial destroy: ${error instanceof Error ? error.message : String(error)}. The state file may still list already-deleted resources; a re-run resolves them idempotently.`);
34016
+ }
34017
+ logger.warn(`${result.errorCount} resource(s) failed to delete. State preserved.`);
34018
+ }
33974
34019
  const retainedSuffix = result.retainedCount > 0 ? `, ${result.retainedCount} retained` : "";
33975
34020
  if (result.errorCount === 0) logger.info(`\n${green("✓")} ${bold(`Stack ${stackName} destroyed`)} (${green(result.deletedCount)} deleted${retainedSuffix}, ${result.errorCount} errors)`);
33976
34021
  else logger.warn(`\n${yellow("⚠")} ${bold(`Stack ${stackName} partially destroyed`)} (${green(result.deletedCount)} deleted${retainedSuffix}, ${red(result.errorCount)} errors). State preserved — re-run 'cdkd destroy' / 'cdkd state destroy' to clean up.`);
33977
34022
  } finally {
33978
34023
  renderer.stop();
34024
+ await saveChain;
33979
34025
  logger.debug("Releasing lock...");
33980
34026
  await ctx.lockManager.releaseLock(stackName, regionForState);
33981
34027
  if (destroyAwsClients) {
@@ -36341,12 +36387,13 @@ function renderChangeLines(changes, logFn, ccApiRoutes) {
36341
36387
  logFn(` [~] ${logicalId} (${change.resourceType})${annotateRouting(logicalId)}`);
36342
36388
  if (change.propertyChanges && change.propertyChanges.length > 0) for (const propChange of change.propertyChanges) {
36343
36389
  const requiresReplace = propChange.requiresReplacement ? " [requires replacement]" : "";
36390
+ const propagated = propChange.replacementPropagated ? " [replacement propagated]" : "";
36344
36391
  const oldFiltered = stripUnchangedValues(propChange.oldValue, propChange.newValue);
36345
36392
  const newFiltered = stripUnchangedValues(propChange.newValue, propChange.oldValue);
36346
36393
  const indent = " ";
36347
36394
  const oldStr = (JSON.stringify(oldFiltered, null, 2) ?? "undefined").replace(/\n/g, `\n${indent}`);
36348
36395
  const newStr = (JSON.stringify(newFiltered, null, 2) ?? "undefined").replace(/\n/g, `\n${indent}`);
36349
- logFn(` - ${propChange.path}:${requiresReplace}`);
36396
+ logFn(` - ${propChange.path}:${requiresReplace}${propagated}`);
36350
36397
  logFn(` old: ${oldStr}`);
36351
36398
  logFn(` new: ${newStr}`);
36352
36399
  }
@@ -52867,7 +52914,7 @@ function reorderArgs(argv) {
52867
52914
  async function main() {
52868
52915
  installPipeCloseHandler();
52869
52916
  const program = new Command();
52870
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.219.7");
52917
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.219.9");
52871
52918
  program.addCommand(createBootstrapCommand());
52872
52919
  program.addCommand(createSynthCommand());
52873
52920
  program.addCommand(createListCommand());