@go-to-k/cdkd 0.221.2 → 0.221.4

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 AssetPublisher, B as getLegacyStateBucketName, C as DiffCalculator, D as S3StateBackend, E as LockManager, F as getDockerCmd, G as resolveStateBucketWithDefaultAndSource, H as resolveCaptureObservedState, I as runDockerForeground, J as CFN_TEMPLATE_URL_LIMIT, K as warnDeprecatedNoPrefixCliFlag, L as runDockerStreaming, M as WorkGraph, N as buildDockerImage, O as rebuildClientForBucketRegion, P as formatDockerLoginError, Q as AssemblyReader, R as Synthesizer, S as applyRoleArnIfSet, T as TemplateParser, U as resolveSkipPrefix, V as resolveApp, W as resolveStateBucketWithDefault, X as findLargeInlineResources, Y as MIGRATE_TMP_PREFIX, Z as uploadCfnTemplate, _ as collectInlinePolicyNamesManagedBySiblings, a as withRetry, b as CloudControlProvider, c as extractDeploymentEventError, d as cyan, et as resolveBucketRegion, f as gray, g as IAMRoleProvider, h as yellow, i as withResourceDeadline, j as stringifyValue, k as shouldRetainResource, l as formatResourceLine, m as red, n as DEFAULT_RESOURCE_WARN_AFTER_MS, o as isRetryableTransientError, p as green, q as CFN_TEMPLATE_BODY_LIMIT, r as DeployEngine, s as IMPLICIT_DELETE_DEPENDENCIES, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as bold, v as ProviderRegistry, w as DagBuilder, x as IntrinsicFunctionResolver, y as findActionableSilentDrops, z as getDefaultStateBucketName } from "./deploy-engine-B6ARF3_Y.js";
4
+ import { A as AssetPublisher, B as getLegacyStateBucketName, C as DiffCalculator, D as S3StateBackend, E as LockManager, F as getDockerCmd, G as resolveStateBucketWithDefaultAndSource, H as resolveCaptureObservedState, I as runDockerForeground, J as CFN_TEMPLATE_URL_LIMIT, K as warnDeprecatedNoPrefixCliFlag, L as runDockerStreaming, M as WorkGraph, N as buildDockerImage, O as rebuildClientForBucketRegion, P as formatDockerLoginError, Q as AssemblyReader, R as Synthesizer, S as applyRoleArnIfSet, T as TemplateParser, U as resolveSkipPrefix, V as resolveApp, W as resolveStateBucketWithDefault, X as findLargeInlineResources, Y as MIGRATE_TMP_PREFIX, Z as uploadCfnTemplate, _ as collectInlinePolicyNamesManagedBySiblings, a as withRetry, b as CloudControlProvider, c as extractDeploymentEventError, d as cyan, et as resolveBucketRegion, f as gray, g as IAMRoleProvider, h as yellow, i as withResourceDeadline, j as stringifyValue, k as shouldRetainResource, l as formatResourceLine, m as red, n as DEFAULT_RESOURCE_WARN_AFTER_MS, o as isRetryableTransientError, p as green, q as CFN_TEMPLATE_BODY_LIMIT, r as DeployEngine, s as IMPLICIT_DELETE_DEPENDENCIES, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as bold, v as ProviderRegistry, w as DagBuilder, x as IntrinsicFunctionResolver, y as findActionableSilentDrops, z as getDefaultStateBucketName } from "./deploy-engine-DlXVqVTW.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";
@@ -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.221.2";
1556
+ return "0.221.4";
1557
1557
  }
1558
1558
  /**
1559
1559
  * Generate a time-sortable unique run id, e.g.
@@ -53802,7 +53802,7 @@ function reorderArgs(argv) {
53802
53802
  async function main() {
53803
53803
  installPipeCloseHandler();
53804
53804
  const program = new Command();
53805
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.221.2");
53805
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.221.4");
53806
53806
  program.addCommand(createBootstrapCommand());
53807
53807
  program.addCommand(createSynthCommand());
53808
53808
  program.addCommand(createListCommand());
@@ -4080,6 +4080,53 @@ var TemplateParser = class {
4080
4080
  countResources(template) {
4081
4081
  return Object.keys(template.Resources).length;
4082
4082
  }
4083
+ /**
4084
+ * Produce a copy of the template with every resource whose `Condition:`
4085
+ * key resolves to `false` removed from `Resources` (issue #840).
4086
+ *
4087
+ * CloudFormation does not strip condition-gated resources at synth time —
4088
+ * CDK emits `PremiumOnlyParam` into `Resources` with a `Condition: IsPremium`
4089
+ * key REGARDLESS of the parameter value, and the deploy engine (CFn, or in
4090
+ * cdkd's case cdkd itself) is responsible for excluding the resource when
4091
+ * its condition evaluates false. cdkd evaluates the `Conditions` section
4092
+ * (for `Fn::If` resolution) but, before this helper, never consulted the
4093
+ * resource-level `Condition:` key — so a condition-false resource was:
4094
+ * - CREATED anyway on first deploy (it sat in the desired set), and
4095
+ * - never DELETED when its condition flipped true -> false on a redeploy
4096
+ * (it stayed in the desired set, diffing as NO_CHANGE).
4097
+ *
4098
+ * By pruning condition-false resources here, the rest of the pipeline
4099
+ * (type/property validation, DAG build, diff) sees the CFn-effective
4100
+ * resource set: a now-condition-false resource that exists in prior state
4101
+ * but is absent from the pruned template falls through the diff's existing
4102
+ * "in state but not in desired template -> DELETE" path, exactly as CFn
4103
+ * removes it. A resource whose `Condition` key names an unknown condition,
4104
+ * or whose condition evaluated `true`, is kept.
4105
+ *
4106
+ * The returned template shares every untouched object reference with the
4107
+ * input (only `Resources` is rebuilt) — callers must treat it as read-only,
4108
+ * which the deploy pipeline already does.
4109
+ *
4110
+ * @param template The synthesized template (raw, with all condition-gated
4111
+ * resources still present).
4112
+ * @param conditions The evaluated `Conditions` map (name -> boolean) from
4113
+ * `IntrinsicFunctionResolver.evaluateConditions`.
4114
+ */
4115
+ filterResourcesByCondition(template, conditions) {
4116
+ const filteredResources = {};
4117
+ for (const [logicalId, resource] of Object.entries(template.Resources)) {
4118
+ const conditionName = resource.Condition;
4119
+ if (conditionName !== void 0 && conditions[conditionName] === false) {
4120
+ this.logger.debug(`Excluding resource ${logicalId} — condition ${conditionName} is false`);
4121
+ continue;
4122
+ }
4123
+ filteredResources[logicalId] = resource;
4124
+ }
4125
+ return {
4126
+ ...template,
4127
+ Resources: filteredResources
4128
+ };
4129
+ }
4083
4130
  };
4084
4131
 
4085
4132
  //#endregion
@@ -5533,13 +5580,36 @@ var IntrinsicFunctionResolver = class {
5533
5580
  const conditions = {};
5534
5581
  const templateConditions = context.template.Conditions;
5535
5582
  if (!templateConditions || typeof templateConditions !== "object") return conditions;
5536
- for (const [name, definition] of Object.entries(templateConditions)) try {
5537
- const result = await this.resolveValue(definition, context);
5538
- conditions[name] = Boolean(result);
5539
- this.logger.debug(`Evaluated condition ${name} = ${conditions[name]}`);
5583
+ const inProgress = /* @__PURE__ */ new Set();
5584
+ const evaluateByName = async (name) => {
5585
+ if (name in conditions) return conditions[name];
5586
+ if (inProgress.has(name)) throw new Error(`Circular condition reference detected involving condition "${name}"`);
5587
+ const definition = templateConditions[name];
5588
+ if (definition === void 0) {
5589
+ this.logger.warn(`Condition ${name} not found in template, assuming false`);
5590
+ conditions[name] = false;
5591
+ return false;
5592
+ }
5593
+ inProgress.add(name);
5594
+ try {
5595
+ const result = await this.resolveValue(definition, {
5596
+ ...context,
5597
+ conditionResolver: evaluateByName
5598
+ });
5599
+ const value = Boolean(result);
5600
+ conditions[name] = value;
5601
+ this.logger.debug(`Evaluated condition ${name} = ${value}`);
5602
+ return value;
5603
+ } finally {
5604
+ inProgress.delete(name);
5605
+ }
5606
+ };
5607
+ for (const name of Object.keys(templateConditions)) try {
5608
+ await evaluateByName(name);
5540
5609
  } catch (error) {
5541
5610
  this.logger.warn(`Failed to evaluate condition ${name}: ${error instanceof Error ? error.message : String(error)}, assuming false`);
5542
5611
  conditions[name] = false;
5612
+ inProgress.delete(name);
5543
5613
  }
5544
5614
  return conditions;
5545
5615
  }
@@ -5561,6 +5631,7 @@ var IntrinsicFunctionResolver = class {
5561
5631
  if ("Fn::Split" in obj) return await this.resolveSplit(obj["Fn::Split"], context);
5562
5632
  if ("Fn::If" in obj) return await this.resolveIf(obj["Fn::If"], context);
5563
5633
  if ("Fn::Equals" in obj) return await this.resolveEquals(obj["Fn::Equals"], context);
5634
+ if (context.conditionResolver && "Condition" in obj && Object.keys(obj).length === 1 && typeof obj["Condition"] === "string") return await this.resolveConditionReference(obj["Condition"], context);
5564
5635
  if ("Fn::And" in obj) return await this.resolveAnd(obj["Fn::And"], context);
5565
5636
  if ("Fn::Or" in obj) return await this.resolveOr(obj["Fn::Or"], context);
5566
5637
  if ("Fn::Not" in obj) return await this.resolveNot(obj["Fn::Not"], context);
@@ -5874,7 +5945,10 @@ var IntrinsicFunctionResolver = class {
5874
5945
  * Fn::Join: [delimiter, [value1, value2, ...]]
5875
5946
  */
5876
5947
  async resolveJoin(joinArgs, context) {
5877
- const [delimiter, values] = joinArgs;
5948
+ const [delimiter, rawValues] = joinArgs;
5949
+ let values = rawValues;
5950
+ if (!Array.isArray(values)) values = await this.resolveValue(values, context);
5951
+ if (!Array.isArray(values)) throw new Error(`Fn::Join's second argument must be a list (an array literal or a list-returning intrinsic such as Fn::Cidr / Fn::GetAZs / Fn::Split / a Ref to a CommaDelimitedList parameter), but resolved to ${typeof values}`);
5878
5952
  let result = (await Promise.all(values.map(async (v) => {
5879
5953
  const resolved = await this.resolveValue(v, context);
5880
5954
  return String(resolved);
@@ -6003,6 +6077,23 @@ var IntrinsicFunctionResolver = class {
6003
6077
  return result;
6004
6078
  }
6005
6079
  /**
6080
+ * Resolve a `{Condition: <name>}` named-condition reference (issue #840).
6081
+ *
6082
+ * Inside `Fn::And` / `Fn::Or` / `Fn::Not` a CFn Condition may reference
6083
+ * another named condition. When the resolver is mid-`evaluateConditions`
6084
+ * the `conditionResolver` hook is present and lazily evaluates the
6085
+ * referenced condition (recursing + memoizing + cycle-guarding) so the
6086
+ * result is order-independent. Outside that context (which is invalid CFn
6087
+ * but handled defensively) we fall back to the already-evaluated
6088
+ * `conditions` map, matching `Fn::If`'s warn-and-assume-false behavior.
6089
+ */
6090
+ async resolveConditionReference(conditionName, context) {
6091
+ if (context.conditionResolver) return await context.conditionResolver(conditionName);
6092
+ if (context.conditions && conditionName in context.conditions) return context.conditions[conditionName];
6093
+ this.logger.warn(`Condition ${conditionName} not found in context, assuming false`);
6094
+ return false;
6095
+ }
6096
+ /**
6006
6097
  * Resolve Fn::And intrinsic function
6007
6098
  *
6008
6099
  * Returns true if all conditions evaluate to true
@@ -6373,7 +6464,7 @@ var IntrinsicFunctionResolver = class {
6373
6464
  return `arn:aws:cloudformation:${info.region}:${info.accountId}:stack/${context?.stackName ?? "UnknownStack"}/cdkd`;
6374
6465
  }
6375
6466
  case "AWS::URLSuffix": return "amazonaws.com";
6376
- case "AWS::NotificationARNs": return;
6467
+ case "AWS::NotificationARNs": return "";
6377
6468
  case "AWS::NoValue": return AWS_NO_VALUE;
6378
6469
  default: return;
6379
6470
  }
@@ -11571,6 +11662,7 @@ var DeployEngine = class {
11571
11662
  lockManager;
11572
11663
  dagBuilder;
11573
11664
  diffCalculator;
11665
+ templateParser = new TemplateParser();
11574
11666
  providerRegistry;
11575
11667
  options;
11576
11668
  /**
@@ -11804,10 +11896,11 @@ var DeployEngine = class {
11804
11896
  }, stackName);
11805
11897
  const conditions = await this.resolver.evaluateConditions(context);
11806
11898
  this.logger.debug(`Evaluated ${Object.keys(conditions).length} conditions: ${Object.keys(conditions).join(", ")}`);
11807
- const resourceTypes = new Set(Object.values(template.Resources || {}).map((r) => r.Type).filter((type) => type !== "AWS::CDK::Metadata"));
11899
+ const effectiveTemplate = this.templateParser.filterResourcesByCondition(template, conditions);
11900
+ const resourceTypes = new Set(Object.values(effectiveTemplate.Resources || {}).map((r) => r.Type).filter((type) => type !== "AWS::CDK::Metadata"));
11808
11901
  this.providerRegistry.validateResourceTypes(resourceTypes);
11809
11902
  this.logger.debug(`All resource types validated`);
11810
- const resourcesForPropertyCheck = Object.entries(template.Resources || {}).filter(([, r]) => r.Type !== "AWS::CDK::Metadata").map(([logicalId, r]) => ({
11903
+ const resourcesForPropertyCheck = Object.entries(effectiveTemplate.Resources || {}).filter(([, r]) => r.Type !== "AWS::CDK::Metadata").map(([logicalId, r]) => ({
11811
11904
  logicalId,
11812
11905
  resourceType: r.Type,
11813
11906
  properties: r.Properties,
@@ -11815,17 +11908,17 @@ var DeployEngine = class {
11815
11908
  }));
11816
11909
  this.providerRegistry.validateResourceProperties(resourcesForPropertyCheck);
11817
11910
  this.logger.debug(`All resource properties validated`);
11818
- const dag = this.dagBuilder.buildGraph(template);
11911
+ const dag = this.dagBuilder.buildGraph(effectiveTemplate);
11819
11912
  const executionLevels = this.dagBuilder.getExecutionLevels(dag);
11820
11913
  this.logger.debug(`Dependency graph: ${executionLevels.length} execution levels`);
11821
11914
  const diffResolverContext = this.buildResolverContext({
11822
- template,
11915
+ template: effectiveTemplate,
11823
11916
  resources: currentState.resources,
11824
11917
  parameters: parameterValues,
11825
11918
  conditions
11826
11919
  }, stackName);
11827
11920
  const diffResolveFn = (value) => this.resolver.resolve(value, diffResolverContext);
11828
- const changes = await this.diffCalculator.calculateDiff(currentState, template, diffResolveFn);
11921
+ const changes = await this.diffCalculator.calculateDiff(currentState, effectiveTemplate, diffResolveFn);
11829
11922
  if (!this.diffCalculator.hasChanges(changes)) {
11830
11923
  this.logger.info("No changes detected. Stack is up to date.");
11831
11924
  if (!this.options.dryRun && this.observedCaptureTasks.size > 0) {
@@ -11879,7 +11972,7 @@ var DeployEngine = class {
11879
11972
  current: 0,
11880
11973
  total: createChanges.length + updateChanges.length + deleteChanges.length
11881
11974
  };
11882
- const { state: newState, actualCounts } = await this.executeDeployment(template, currentState, changes, dag, executionLevels, stackName, parameterValues, conditions, currentEtag, progress, migrationPending);
11975
+ const { state: newState, actualCounts } = await this.executeDeployment(effectiveTemplate, currentState, changes, dag, executionLevels, stackName, parameterValues, conditions, currentEtag, progress, migrationPending);
11883
11976
  await this.drainObservedCaptures(newState.resources);
11884
11977
  const newEtag = await this.stateBackend.saveState(stackName, this.stackRegion, this.withParentInfo(newState));
11885
11978
  this.logger.debug(`State saved (ETag: ${newEtag})`);
@@ -12797,4 +12890,4 @@ var DeployEngine = class {
12797
12890
 
12798
12891
  //#endregion
12799
12892
  export { clearBucketRegionCache as $, AssetPublisher as A, getLegacyStateBucketName as B, DiffCalculator as C, S3StateBackend as D, LockManager as E, getDockerCmd as F, resolveStateBucketWithDefaultAndSource as G, resolveCaptureObservedState as H, runDockerForeground as I, CFN_TEMPLATE_URL_LIMIT as J, warnDeprecatedNoPrefixCliFlag as K, runDockerStreaming as L, WorkGraph as M, buildDockerImage as N, rebuildClientForBucketRegion as O, formatDockerLoginError as P, AssemblyReader as Q, Synthesizer as R, applyRoleArnIfSet as S, TemplateParser as T, resolveSkipPrefix as U, resolveApp as V, resolveStateBucketWithDefault as W, findLargeInlineResources as X, MIGRATE_TMP_PREFIX as Y, uploadCfnTemplate as Z, collectInlinePolicyNamesManagedBySiblings as _, withRetry as a, CloudControlProvider as b, extractDeploymentEventError as c, cyan as d, resolveBucketRegion as et, gray as f, IAMRoleProvider as g, yellow as h, withResourceDeadline as i, stringifyValue as j, shouldRetainResource as k, formatResourceLine as l, red as m, DEFAULT_RESOURCE_WARN_AFTER_MS as n, isRetryableTransientError as o, green as p, CFN_TEMPLATE_BODY_LIMIT as q, DeployEngine as r, IMPLICIT_DELETE_DEPENDENCIES as s, DEFAULT_RESOURCE_TIMEOUT_MS as t, bold as u, ProviderRegistry as v, DagBuilder as w, IntrinsicFunctionResolver as x, findActionableSilentDrops as y, getDefaultStateBucketName as z };
12800
- //# sourceMappingURL=deploy-engine-B6ARF3_Y.js.map
12893
+ //# sourceMappingURL=deploy-engine-DlXVqVTW.js.map