@go-to-k/cdkd 0.164.1 → 0.165.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
@@ -1256,6 +1256,67 @@ async function probeAndRevalidateStateful(input) {
1256
1256
  };
1257
1257
  }
1258
1258
 
1259
+ //#endregion
1260
+ //#region src/cli/commands/recreate-downstream-consumers.ts
1261
+ /**
1262
+ * Walk every stack in the cdkd state bucket and find consumers whose
1263
+ * `imports[]` reference the producer `(producerStack, producerRegion)`.
1264
+ *
1265
+ * Skips the producer itself (self-imports are invalid in CFn / cdkd).
1266
+ *
1267
+ * Soft-fails per consumer: an unreadable state file is logged at debug
1268
+ * and skipped — the warn block falls back to "we couldn't enumerate
1269
+ * downstream consumers" rather than blocking the deploy. The caller
1270
+ * always proceeds; this is informational only.
1271
+ */
1272
+ async function findDownstreamConsumers(input) {
1273
+ const logger = getLogger().child("recreate-downstream");
1274
+ let refs;
1275
+ try {
1276
+ refs = await input.stateBackend.listStacks();
1277
+ } catch (err) {
1278
+ logger.debug(`findDownstreamConsumers: listStacks failed; falling back to empty enumeration. ${err instanceof Error ? err.message : String(err)}`);
1279
+ return [];
1280
+ }
1281
+ return (await Promise.all(refs.map(async (ref) => {
1282
+ const region = ref.region ?? input.baseRegion;
1283
+ if (ref.stackName === input.producerStack && region === input.producerRegion) return null;
1284
+ try {
1285
+ const imports = (await input.stateBackend.getState(ref.stackName, region))?.state.imports;
1286
+ if (!imports || imports.length === 0) return null;
1287
+ const matches = imports.filter((entry) => entry.sourceStack === input.producerStack && entry.sourceRegion === input.producerRegion);
1288
+ if (matches.length === 0) return null;
1289
+ return matches.map((entry) => ({
1290
+ consumerStack: ref.stackName,
1291
+ consumerRegion: region,
1292
+ exportName: entry.exportName,
1293
+ intrinsic: "ImportValue"
1294
+ }));
1295
+ } catch (err) {
1296
+ logger.debug(`findDownstreamConsumers: skip ${ref.stackName} (${region}); ${err instanceof Error ? err.message : String(err)}`);
1297
+ return null;
1298
+ }
1299
+ }))).filter((r) => r !== null).flat();
1300
+ }
1301
+ /**
1302
+ * Render the per-consumer subset of the warn block as a multi-line
1303
+ * string suitable for piping into the logger. Returns `null` when the
1304
+ * enumeration is empty (caller skips the subsection in that case).
1305
+ *
1306
+ * Shape:
1307
+ * ```
1308
+ * Downstream consumers of <ProducerStack>'s outputs (will need re-deploy):
1309
+ * - StackB (region) reads ExportName via Fn::ImportValue
1310
+ * - StackC (region) reads OtherExport via Fn::ImportValue
1311
+ * ```
1312
+ */
1313
+ function renderDownstreamConsumers(producerStack, consumers) {
1314
+ if (consumers.length === 0) return null;
1315
+ const lines = [` Downstream consumers of ${producerStack}'s outputs (will need re-deploy after this run):`];
1316
+ for (const c of consumers) lines.push(` - ${c.consumerStack} (${c.consumerRegion}) reads ${c.exportName} via Fn::${c.intrinsic}`);
1317
+ return lines.join("\n");
1318
+ }
1319
+
1259
1320
  //#endregion
1260
1321
  //#region src/cli/commands/recreate-confirm-prompt.ts
1261
1322
  /**
@@ -1295,6 +1356,10 @@ async function promptRecreateConfirm(input) {
1295
1356
  logger.warn(` - ${dataLossPrefix}${t.logicalId} (${t.resourceType})${stateNote}`);
1296
1357
  if (stateful) logger.warn(` DATA: all data in ${t.logicalId} will be lost (no automatic data migration)`);
1297
1358
  }
1359
+ if (input.downstreamConsumers && input.downstreamConsumers.length > 0) {
1360
+ const rendered = renderDownstreamConsumers(input.stackName, input.downstreamConsumers);
1361
+ if (rendered) logger.warn(rendered);
1362
+ }
1298
1363
  logger.warn(" The destroy + recreate cycle is per-resource; sibling resources are unaffected. Downstream consumers of any recreated resource's outputs (Fn::GetStackOutput / Fn::ImportValue) will need a re-deploy to see the new physical id.");
1299
1364
  if (input.yes) return true;
1300
1365
  if (process.stdin.isTTY !== true) throw new Error("--recreate-via-cc-api confirm prompt cannot run in a non-interactive environment. Pass --yes / -y to confirm the destroy + recreate cycle, or run the deploy from a real terminal.");
@@ -33793,10 +33858,17 @@ async function deployCommand(stacks, options) {
33793
33858
  if (errorBlock) throw new CdkdError(errorBlock, "RECREATE_VIA_CC_API_INVALID");
33794
33859
  recreateViaCcApiTargets = new Set(validation.targets.map((t) => t.logicalId));
33795
33860
  if (recreateViaCcApiTargets.size > 0) {
33861
+ const downstreamConsumers = await findDownstreamConsumers({
33862
+ producerStack: stackInfo.stackName,
33863
+ producerRegion: stackRegion,
33864
+ stateBackend: stackStateBackend,
33865
+ baseRegion
33866
+ });
33796
33867
  if (!await promptRecreateConfirm({
33797
33868
  stackName: stackInfo.stackName,
33798
33869
  targets: validation.targets,
33799
- yes: options.yes ?? false
33870
+ yes: options.yes ?? false,
33871
+ downstreamConsumers
33800
33872
  })) return;
33801
33873
  }
33802
33874
  }
@@ -60547,7 +60619,7 @@ function reorderArgs(argv) {
60547
60619
  */
60548
60620
  async function main() {
60549
60621
  const program = new Command();
60550
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.164.1");
60622
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.165.0");
60551
60623
  program.addCommand(createBootstrapCommand());
60552
60624
  program.addCommand(createSynthCommand());
60553
60625
  program.addCommand(createListCommand());