@go-to-k/cdkd 0.3.6 → 0.4.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.
Binary file
package/dist/index.js CHANGED
@@ -432,6 +432,141 @@ var init_aws_clients = __esm({
432
432
  }
433
433
  });
434
434
 
435
+ // src/utils/live-renderer.ts
436
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
437
+ var FRAME_INTERVAL_MS = 80;
438
+ var ESC = "\x1B[";
439
+ var LiveRenderer = class {
440
+ constructor(stream = process.stdout) {
441
+ this.stream = stream;
442
+ }
443
+ tasks = /* @__PURE__ */ new Map();
444
+ active = false;
445
+ spinnerIndex = 0;
446
+ interval = null;
447
+ linesDrawn = 0;
448
+ cursorHidden = false;
449
+ exitListener = null;
450
+ isActive() {
451
+ return this.active;
452
+ }
453
+ /**
454
+ * Enable the live renderer. No-op if stdout is not a TTY or if
455
+ * `CDKD_NO_LIVE=1`. Returns true if successfully enabled.
456
+ */
457
+ start() {
458
+ if (this.active)
459
+ return true;
460
+ if (!this.stream.isTTY)
461
+ return false;
462
+ if (process.env["CDKD_NO_LIVE"] === "1")
463
+ return false;
464
+ this.active = true;
465
+ this.hideCursor();
466
+ if (!this.exitListener) {
467
+ this.exitListener = () => this.showCursor();
468
+ process.on("exit", this.exitListener);
469
+ }
470
+ this.interval = setInterval(() => this.draw(), FRAME_INTERVAL_MS);
471
+ if (typeof this.interval.unref === "function")
472
+ this.interval.unref();
473
+ return true;
474
+ }
475
+ stop() {
476
+ if (!this.active)
477
+ return;
478
+ if (this.interval) {
479
+ clearInterval(this.interval);
480
+ this.interval = null;
481
+ }
482
+ this.clear();
483
+ this.showCursor();
484
+ if (this.exitListener) {
485
+ process.removeListener("exit", this.exitListener);
486
+ this.exitListener = null;
487
+ }
488
+ this.tasks.clear();
489
+ this.active = false;
490
+ }
491
+ addTask(id, label) {
492
+ this.tasks.set(id, { label, startedAt: Date.now() });
493
+ if (this.active)
494
+ this.draw();
495
+ }
496
+ removeTask(id) {
497
+ if (!this.tasks.delete(id))
498
+ return;
499
+ if (this.active)
500
+ this.draw();
501
+ }
502
+ /**
503
+ * Print content above the live area. Clears the live area, runs the writer,
504
+ * then redraws the live area. When the renderer is inactive, the writer
505
+ * runs directly so callers can use this unconditionally.
506
+ */
507
+ printAbove(write) {
508
+ if (!this.active) {
509
+ write();
510
+ return;
511
+ }
512
+ this.clear();
513
+ write();
514
+ this.draw();
515
+ }
516
+ clear() {
517
+ if (this.linesDrawn === 0)
518
+ return;
519
+ this.stream.write("\r");
520
+ for (let i = 0; i < this.linesDrawn; i++) {
521
+ this.stream.write(`${ESC}1A${ESC}2K`);
522
+ }
523
+ this.linesDrawn = 0;
524
+ }
525
+ draw() {
526
+ if (!this.active)
527
+ return;
528
+ this.clear();
529
+ if (this.tasks.size === 0)
530
+ return;
531
+ const frame = SPINNER_FRAMES[this.spinnerIndex % SPINNER_FRAMES.length];
532
+ this.spinnerIndex++;
533
+ const cols = this.stream.columns ?? 80;
534
+ const lines = [];
535
+ for (const task of this.tasks.values()) {
536
+ const elapsed = ((Date.now() - task.startedAt) / 1e3).toFixed(1);
537
+ const raw = ` ${frame} ${task.label} (${elapsed}s)`;
538
+ lines.push(this.truncate(raw, cols));
539
+ }
540
+ this.stream.write(lines.join("\n") + "\n");
541
+ this.linesDrawn = lines.length;
542
+ }
543
+ truncate(s, maxLen) {
544
+ if (s.length <= maxLen)
545
+ return s;
546
+ if (maxLen <= 1)
547
+ return "\u2026";
548
+ return s.substring(0, maxLen - 1) + "\u2026";
549
+ }
550
+ hideCursor() {
551
+ if (this.cursorHidden)
552
+ return;
553
+ this.stream.write(`${ESC}?25l`);
554
+ this.cursorHidden = true;
555
+ }
556
+ showCursor() {
557
+ if (!this.cursorHidden)
558
+ return;
559
+ this.stream.write(`${ESC}?25h`);
560
+ this.cursorHidden = false;
561
+ }
562
+ };
563
+ var globalRenderer = null;
564
+ function getLiveRenderer() {
565
+ if (!globalRenderer)
566
+ globalRenderer = new LiveRenderer();
567
+ return globalRenderer;
568
+ }
569
+
435
570
  // src/utils/logger.ts
436
571
  var colors = {
437
572
  reset: "\x1B[0m",
@@ -488,22 +623,26 @@ var ConsoleLogger = class {
488
623
  }
489
624
  debug(message, ...args) {
490
625
  if (this.shouldLog("debug")) {
491
- console.debug(this.formatMessage("debug", message, ...args));
626
+ const formatted = this.formatMessage("debug", message, ...args);
627
+ getLiveRenderer().printAbove(() => console.debug(formatted));
492
628
  }
493
629
  }
494
630
  info(message, ...args) {
495
631
  if (this.shouldLog("info")) {
496
- console.info(this.formatMessage("info", message, ...args));
632
+ const formatted = this.formatMessage("info", message, ...args);
633
+ getLiveRenderer().printAbove(() => console.info(formatted));
497
634
  }
498
635
  }
499
636
  warn(message, ...args) {
500
637
  if (this.shouldLog("warn")) {
501
- console.warn(this.formatMessage("warn", message, ...args));
638
+ const formatted = this.formatMessage("warn", message, ...args);
639
+ getLiveRenderer().printAbove(() => console.warn(formatted));
502
640
  }
503
641
  }
504
642
  error(message, ...args) {
505
643
  if (this.shouldLog("error")) {
506
- console.error(this.formatMessage("error", message, ...args));
644
+ const formatted = this.formatMessage("error", message, ...args);
645
+ getLiveRenderer().printAbove(() => console.error(formatted));
507
646
  }
508
647
  }
509
648
  /**
@@ -934,6 +1073,7 @@ var AssemblyReader = class {
934
1073
  }
935
1074
  return {
936
1075
  stackName,
1076
+ displayName: artifact.displayName ?? stackName,
937
1077
  artifactId,
938
1078
  template,
939
1079
  assetManifestPath,
@@ -7247,11 +7387,15 @@ var DeployEngine = class {
7247
7387
  this.logger.debug(`Starting deployment for stack: ${stackName}`);
7248
7388
  setCurrentStackName(stackName);
7249
7389
  await this.lockManager.acquireLockWithRetry(stackName, void 0, "deploy");
7390
+ const renderer = getLiveRenderer();
7391
+ renderer.start();
7250
7392
  this.interrupted = false;
7251
7393
  const sigintHandler = () => {
7252
- process.stderr.write(
7253
- "\nInterrupted \u2014 saving partial state after current operations complete...\n"
7254
- );
7394
+ renderer.printAbove(() => {
7395
+ process.stderr.write(
7396
+ "\nInterrupted \u2014 saving partial state after current operations complete...\n"
7397
+ );
7398
+ });
7255
7399
  this.interrupted = true;
7256
7400
  };
7257
7401
  process.on("SIGINT", sigintHandler);
@@ -7366,6 +7510,7 @@ var DeployEngine = class {
7366
7510
  durationMs
7367
7511
  };
7368
7512
  } finally {
7513
+ renderer.stop();
7369
7514
  process.removeListener("SIGINT", sigintHandler);
7370
7515
  try {
7371
7516
  await this.lockManager.releaseLock(stackName);
@@ -7794,6 +7939,10 @@ var DeployEngine = class {
7794
7939
  async provisionResource(logicalId, change, stateResources, stackName, template, parameterValues, conditions, counts, progress) {
7795
7940
  const resourceType = change.resourceType;
7796
7941
  const provider = this.providerRegistry.getProvider(resourceType);
7942
+ const renderer = getLiveRenderer();
7943
+ const needsReplacement = change.changeType === "UPDATE" && (change.propertyChanges?.some((pc) => pc.requiresReplacement) ?? false);
7944
+ const verb = change.changeType === "CREATE" ? "Creating" : change.changeType === "DELETE" ? "Deleting" : needsReplacement ? "Replacing" : "Updating";
7945
+ renderer.addTask(logicalId, `${verb} ${logicalId} (${resourceType})`);
7797
7946
  try {
7798
7947
  switch (change.changeType) {
7799
7948
  case "CREATE": {
@@ -7825,6 +7974,7 @@ var DeployEngine = class {
7825
7974
  if (progress)
7826
7975
  progress.current++;
7827
7976
  const createPrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
7977
+ renderer.removeTask(logicalId);
7828
7978
  this.logger.info(`${createPrefix}\u2705 ${logicalId} (${resourceType}) created`);
7829
7979
  break;
7830
7980
  }
@@ -7852,9 +8002,9 @@ var DeployEngine = class {
7852
8002
  counts.skipped++;
7853
8003
  break;
7854
8004
  }
7855
- const needsReplacement = change.propertyChanges?.some((pc) => pc.requiresReplacement);
8005
+ const needsReplacement2 = change.propertyChanges?.some((pc) => pc.requiresReplacement);
7856
8006
  const dependencies = this.extractAllDependencies(template, logicalId);
7857
- if (needsReplacement) {
8007
+ if (needsReplacement2) {
7858
8008
  const replacedProps = change.propertyChanges?.filter((pc) => pc.requiresReplacement).map((pc) => pc.path).join(", ");
7859
8009
  this.logger.info(
7860
8010
  `Replacing ${logicalId} (${resourceType}) - immutable properties changed: ${replacedProps}`
@@ -7898,6 +8048,7 @@ var DeployEngine = class {
7898
8048
  if (progress)
7899
8049
  progress.current++;
7900
8050
  const replacePrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
8051
+ renderer.removeTask(logicalId);
7901
8052
  this.logger.info(`${replacePrefix}\u2705 ${logicalId} (${resourceType}) replaced`);
7902
8053
  } else {
7903
8054
  this.logger.debug(`Updating ${logicalId} (${resourceType})`);
@@ -7973,6 +8124,7 @@ var DeployEngine = class {
7973
8124
  if (progress)
7974
8125
  progress.current++;
7975
8126
  const updatePrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
8127
+ renderer.removeTask(logicalId);
7976
8128
  this.logger.info(`${updatePrefix}\u2705 ${logicalId} (${resourceType}) updated`);
7977
8129
  }
7978
8130
  break;
@@ -8018,11 +8170,13 @@ var DeployEngine = class {
8018
8170
  if (progress)
8019
8171
  progress.current++;
8020
8172
  const deletePrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
8173
+ renderer.removeTask(logicalId);
8021
8174
  this.logger.info(`${deletePrefix}\u2705 ${logicalId} (${resourceType}) deleted`);
8022
8175
  break;
8023
8176
  }
8024
8177
  }
8025
8178
  } catch (error) {
8179
+ renderer.removeTask(logicalId);
8026
8180
  const message = error instanceof Error ? error.message : String(error);
8027
8181
  this.logger.error(`Failed to ${change.changeType.toLowerCase()} ${logicalId}: ${message}`);
8028
8182
  throw new ProvisioningError(
@@ -8032,6 +8186,8 @@ var DeployEngine = class {
8032
8186
  stateResources[logicalId]?.physicalId,
8033
8187
  error instanceof Error ? error : void 0
8034
8188
  );
8189
+ } finally {
8190
+ renderer.removeTask(logicalId);
8035
8191
  }
8036
8192
  }
8037
8193
  /**