@go-to-k/cdkd 0.220.5 → 0.221.0

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
@@ -1553,7 +1553,7 @@ const FLUSH_INTERVAL_MS = 2e3;
1553
1553
  const FLUSH_EVENT_THRESHOLD = 50;
1554
1554
  /** Build-time cdkd version, with a dev fallback for non-built contexts. */
1555
1555
  function getCdkdVersion() {
1556
- return "0.220.5";
1556
+ return "0.221.0";
1557
1557
  }
1558
1558
  /**
1559
1559
  * Generate a time-sortable unique run id, e.g.
@@ -34416,7 +34416,8 @@ async function runDestroyForStack(stackName, state, ctx) {
34416
34416
  skippedEmpty: false,
34417
34417
  deletedCount: 0,
34418
34418
  retainedCount: 0,
34419
- errorCount: 0
34419
+ errorCount: 0,
34420
+ interrupted: false
34420
34421
  };
34421
34422
  const resourceCount = Object.keys(state.resources).length;
34422
34423
  const regionForState = state.region ?? ctx.baseRegion;
@@ -34509,6 +34510,20 @@ async function runDestroyForStack(stackName, state, ctx) {
34509
34510
  };
34510
34511
  const renderer = getLiveRenderer();
34511
34512
  renderer.start();
34513
+ let draining = false;
34514
+ const sigintHandler = () => {
34515
+ if (draining) {
34516
+ ctx.lockManager.releaseLock(stackName, regionForState).catch(() => {});
34517
+ process.stderr.write(`\nForce-quit: stack lock may not be released. If the next run reports a lock, run: cdkd force-unlock ${stackName}\n`);
34518
+ process.exit(130);
34519
+ }
34520
+ draining = true;
34521
+ renderer.printAbove(() => {
34522
+ process.stderr.write("\nInterrupted — finishing in-flight deletes, then flushing state and releasing the lock (press Ctrl-C again to force-quit)...\n");
34523
+ });
34524
+ };
34525
+ process.setMaxListeners(Math.max(process.getMaxListeners(), 100));
34526
+ process.on("SIGINT", sigintHandler);
34512
34527
  try {
34513
34528
  logger.info("Building dependency graph...");
34514
34529
  const template = {
@@ -34550,11 +34565,16 @@ async function runDestroyForStack(stackName, state, ctx) {
34550
34565
  const executionLevels = dagBuilder.getExecutionLevels(graph);
34551
34566
  logger.debug(`Dependency graph: ${executionLevels.length} level(s)`);
34552
34567
  for (let levelIndex = executionLevels.length - 1; levelIndex >= 0; levelIndex--) {
34568
+ if (draining) {
34569
+ logger.debug("Interrupted (draining) — not scheduling further deletion levels");
34570
+ break;
34571
+ }
34553
34572
  const level = executionLevels[levelIndex];
34554
34573
  if (!level) continue;
34555
34574
  logger.debug(`Deletion level ${executionLevels.length - levelIndex}/${executionLevels.length} (${level.length} resources)`);
34556
34575
  const stackRegion = state.region ?? ctx.baseRegion;
34557
34576
  const deletePromises = level.map(async (logicalId) => {
34577
+ if (draining) return;
34558
34578
  const resource = state.resources[logicalId];
34559
34579
  if (!resource) {
34560
34580
  logger.warn(`Resource ${logicalId} not found in state, skipping`);
@@ -34691,8 +34711,10 @@ async function runDestroyForStack(stackName, state, ctx) {
34691
34711
  });
34692
34712
  await Promise.all(deletePromises);
34693
34713
  }
34714
+ result.interrupted = draining;
34694
34715
  await saveChain;
34695
- if (result.errorCount === 0) {
34716
+ const preserveState = result.errorCount > 0 || result.interrupted;
34717
+ if (!preserveState) {
34696
34718
  await ctx.stateBackend.deleteState(stackName, regionForState);
34697
34719
  logger.debug("State deleted");
34698
34720
  if (ctx.exportIndexStore) await ctx.exportIndexStore.removeStack(stackName, regionForState);
@@ -34702,12 +34724,15 @@ async function runDestroyForStack(stackName, state, ctx) {
34702
34724
  } catch (error) {
34703
34725
  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.`);
34704
34726
  }
34705
- logger.warn(`${result.errorCount} resource(s) failed to delete. State preserved.`);
34727
+ if (result.interrupted) logger.warn(`Destroy interrupted — ${Object.keys(remainingResources).length} resource(s) not deleted. State preserved.`);
34728
+ else logger.warn(`${result.errorCount} resource(s) failed to delete. State preserved.`);
34706
34729
  }
34707
34730
  const retainedSuffix = result.retainedCount > 0 ? `, ${result.retainedCount} retained` : "";
34708
- if (result.errorCount === 0) logger.info(`\n${green("✓")} ${bold(`Stack ${stackName} destroyed`)} (${green(result.deletedCount)} deleted${retainedSuffix}, ${result.errorCount} errors)`);
34731
+ if (!preserveState) logger.info(`\n${green("✓")} ${bold(`Stack ${stackName} destroyed`)} (${green(result.deletedCount)} deleted${retainedSuffix}, ${result.errorCount} errors)`);
34732
+ else if (result.interrupted && result.errorCount === 0) logger.warn(`\n${yellow("⚠")} ${bold(`Stack ${stackName} destroy interrupted`)} (${green(result.deletedCount)} deleted${retainedSuffix}, ${result.errorCount} errors). State preserved — re-run 'cdkd destroy' / 'cdkd state destroy' to finish.`);
34709
34733
  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.`);
34710
34734
  } finally {
34735
+ process.removeListener("SIGINT", sigintHandler);
34711
34736
  renderer.stop();
34712
34737
  await saveChain;
34713
34738
  logger.debug("Releasing lock...");
@@ -38322,6 +38347,7 @@ async function destroyCommand(stackArgs, options) {
38322
38347
  } else throw new Error("Could not determine which stacks belong to this app. Specify stack names explicitly, use --all, or ensure --app / cdk.json is configured.");
38323
38348
  const stackPatterns = stackArgs.length > 0 ? stackArgs : options.stack ? [options.stack] : [];
38324
38349
  let totalErrors = 0;
38350
+ let interrupted = false;
38325
38351
  let stackNames;
38326
38352
  if (options.all) stackNames = candidateStacks.map((s) => s.stackName);
38327
38353
  else if (stackPatterns.length > 0) stackNames = matchStacks(candidateStacks, stackPatterns).map((s) => s.stackName);
@@ -38443,6 +38469,7 @@ async function destroyCommand(stackArgs, options) {
38443
38469
  eventRecorder
38444
38470
  }));
38445
38471
  totalErrors += result.errorCount;
38472
+ if (result.interrupted) interrupted = true;
38446
38473
  destroyRunResult = result.errorCount > 0 ? "FAILED" : "SUCCEEDED";
38447
38474
  eventRecorder.record({
38448
38475
  eventType: "RUN_FINISHED",
@@ -38462,8 +38489,10 @@ async function destroyCommand(stackArgs, options) {
38462
38489
  } finally {
38463
38490
  await eventRecorder.finalize(destroyRunResult);
38464
38491
  }
38492
+ if (interrupted) break;
38465
38493
  }
38466
38494
  if (totalErrors > 0) throw new PartialFailureError(`Destroy completed with ${totalErrors} resource error(s). State preserved — inspect 'cdkd state show <stack>' and re-run 'cdkd destroy' to retry.`);
38495
+ if (interrupted) throw new PartialFailureError(`Destroy interrupted by Ctrl-C. State preserved — re-run 'cdkd destroy' to finish.`);
38467
38496
  } finally {
38468
38497
  awsClients.destroy();
38469
38498
  }
@@ -42900,6 +42929,7 @@ async function stateDestroyCommand(stackArgs, options) {
42900
42929
  }
42901
42930
  logger.info(`Found ${stackNames.length} stack(s) to destroy: ${stackNames.join(", ")}`);
42902
42931
  let totalErrors = 0;
42932
+ let interrupted = false;
42903
42933
  for (const stackName of stackNames) {
42904
42934
  const refs = stateRefs.filter((r) => r.stackName === stackName);
42905
42935
  let targets;
@@ -42957,9 +42987,13 @@ async function stateDestroyCommand(stackArgs, options) {
42957
42987
  ...options.resourceTimeout?.perTypeMs && { resourceTimeoutByType: options.resourceTimeout.perTypeMs }
42958
42988
  }));
42959
42989
  totalErrors += result.errorCount;
42990
+ if (result.interrupted) interrupted = true;
42991
+ if (interrupted) break;
42960
42992
  }
42993
+ if (interrupted) break;
42961
42994
  }
42962
42995
  if (totalErrors > 0) throw new PartialFailureError(`Destroy completed with ${totalErrors} resource error(s). State preserved — inspect 'cdkd state show <stack>' and re-run 'cdkd state destroy' to retry.`);
42996
+ if (interrupted) throw new PartialFailureError(`Destroy interrupted by Ctrl-C. State preserved — re-run 'cdkd state destroy' to finish.`);
42963
42997
  } finally {
42964
42998
  setup.dispose();
42965
42999
  }
@@ -53790,7 +53824,7 @@ function reorderArgs(argv) {
53790
53824
  async function main() {
53791
53825
  installPipeCloseHandler();
53792
53826
  const program = new Command();
53793
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.220.5");
53827
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.221.0");
53794
53828
  program.addCommand(createBootstrapCommand());
53795
53829
  program.addCommand(createSynthCommand());
53796
53830
  program.addCommand(createListCommand());