@go-to-k/cdkd 0.252.0 → 0.252.2

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
@@ -1888,7 +1888,7 @@ const FLUSH_INTERVAL_MS = 2e3;
1888
1888
  const FLUSH_EVENT_THRESHOLD = 50;
1889
1889
  /** Build-time cdkd version, with a dev fallback for non-built contexts. */
1890
1890
  function getCdkdVersion() {
1891
- return "0.252.0";
1891
+ return "0.252.2";
1892
1892
  }
1893
1893
  /**
1894
1894
  * Generate a time-sortable unique run id, e.g.
@@ -24565,19 +24565,15 @@ var RDSDBProxyProvider = class {
24565
24565
  return {
24566
24566
  physicalId,
24567
24567
  attributes: {
24568
- DBProxyArn: proxy?.DBProxyArn ?? "",
24569
- Endpoint: proxy?.Endpoint ?? "",
24570
- VpcId: proxy?.VpcId ?? ""
24568
+ ...proxy?.DBProxyArn && { DBProxyArn: proxy.DBProxyArn },
24569
+ ...proxy?.Endpoint && { Endpoint: proxy.Endpoint },
24570
+ ...proxy?.VpcId && { VpcId: proxy.VpcId }
24571
24571
  }
24572
24572
  };
24573
24573
  } catch {
24574
24574
  return {
24575
24575
  physicalId,
24576
- attributes: {
24577
- DBProxyArn: "",
24578
- Endpoint: "",
24579
- VpcId: ""
24580
- }
24576
+ attributes: {}
24581
24577
  };
24582
24578
  }
24583
24579
  }
@@ -24892,21 +24888,16 @@ var RDSDBProxyEndpointProvider = class {
24892
24888
  return {
24893
24889
  physicalId,
24894
24890
  attributes: {
24895
- Endpoint: ep?.Endpoint ?? "",
24896
- DBProxyEndpointArn: ep?.DBProxyEndpointArn ?? "",
24897
- IsDefault: ep?.IsDefault ?? false,
24898
- VpcId: ep?.VpcId ?? ""
24891
+ ...ep?.Endpoint && { Endpoint: ep.Endpoint },
24892
+ ...ep?.DBProxyEndpointArn && { DBProxyEndpointArn: ep.DBProxyEndpointArn },
24893
+ ...ep && { IsDefault: ep.IsDefault ?? false },
24894
+ ...ep?.VpcId && { VpcId: ep.VpcId }
24899
24895
  }
24900
24896
  };
24901
24897
  } catch {
24902
24898
  return {
24903
24899
  physicalId,
24904
- attributes: {
24905
- Endpoint: "",
24906
- DBProxyEndpointArn: "",
24907
- IsDefault: false,
24908
- VpcId: ""
24909
- }
24900
+ attributes: {}
24910
24901
  };
24911
24902
  }
24912
24903
  }
@@ -33774,13 +33765,14 @@ var SchedulerScheduleProvider = class {
33774
33765
  const groupName = this.groupNameOf(properties);
33775
33766
  this.logger.debug(`Creating Schedule ${logicalId}: ${name}${groupName ? ` (group: ${groupName})` : ""}`);
33776
33767
  try {
33768
+ const response = await this.getClient().send(new CreateScheduleCommand({
33769
+ Name: name,
33770
+ ...groupName && { GroupName: groupName },
33771
+ ...this.toSdkFields(properties)
33772
+ }));
33777
33773
  return {
33778
33774
  physicalId: name,
33779
- attributes: { Arn: (await this.getClient().send(new CreateScheduleCommand({
33780
- Name: name,
33781
- ...groupName && { GroupName: groupName },
33782
- ...this.toSdkFields(properties)
33783
- }))).ScheduleArn ?? "" }
33775
+ attributes: response.ScheduleArn ? { Arn: response.ScheduleArn } : {}
33784
33776
  };
33785
33777
  } catch (error) {
33786
33778
  const cause = error instanceof Error ? error : void 0;
@@ -33798,10 +33790,11 @@ var SchedulerScheduleProvider = class {
33798
33790
  ...groupName && { GroupName: groupName },
33799
33791
  ...this.toSdkFields(properties)
33800
33792
  };
33793
+ const response = await this.getClient().send(new UpdateScheduleCommand(input));
33801
33794
  return {
33802
33795
  physicalId,
33803
33796
  wasReplaced: false,
33804
- attributes: { Arn: (await this.getClient().send(new UpdateScheduleCommand(input))).ScheduleArn ?? "" }
33797
+ attributes: response.ScheduleArn ? { Arn: response.ScheduleArn } : {}
33805
33798
  };
33806
33799
  } catch (error) {
33807
33800
  if (error instanceof ResourceUpdateNotSupportedError) throw error;
@@ -33889,12 +33882,13 @@ var SchedulerScheduleProvider = class {
33889
33882
  if (!explicit) return null;
33890
33883
  const groupName = this.groupNameOf(input.properties);
33891
33884
  try {
33885
+ const response = await this.getClient().send(new GetScheduleCommand({
33886
+ Name: explicit,
33887
+ ...groupName && { GroupName: groupName }
33888
+ }));
33892
33889
  return {
33893
33890
  physicalId: explicit,
33894
- attributes: { Arn: (await this.getClient().send(new GetScheduleCommand({
33895
- Name: explicit,
33896
- ...groupName && { GroupName: groupName }
33897
- }))).Arn ?? "" }
33891
+ attributes: response.Arn ? { Arn: response.Arn } : {}
33898
33892
  };
33899
33893
  } catch (error) {
33900
33894
  if (error instanceof ResourceNotFoundException$9) return null;
@@ -35555,6 +35549,38 @@ var FSxFileSystemProvider = class {
35555
35549
  ];
35556
35550
  }
35557
35551
  /**
35552
+ * Plain-string arrays FSx returns as unordered sets.
35553
+ *
35554
+ * - `WindowsConfiguration.Aliases` — DNS alias names (`files.example.com`).
35555
+ * Alternate names the file system answers to; the API documents no
35556
+ * ordering semantics and no name is privileged over another, so a
35557
+ * `DescribeFileSystems` reorder carries no meaning.
35558
+ *
35559
+ * This does not match the shared normalizer's AWS-id / ARN heuristic, so
35560
+ * without the declaration a reorder would surface as phantom drift. Declared
35561
+ * here rather than sorted in `readWindowsConfiguration` so the sort applies
35562
+ * to BOTH comparison sides — see
35563
+ * {@link ResourceProvider.getDriftUnorderedPaths}.
35564
+ *
35565
+ * Deliberately NOT declared:
35566
+ *
35567
+ * - `WindowsConfiguration.SelfManagedActiveDirectoryConfiguration.DnsIps` —
35568
+ * the API reference describes it only as "A list of IP addresses of DNS
35569
+ * servers or domain controllers in the self-managed AD directory"
35570
+ * (https://docs.aws.amazon.com/fsx/latest/APIReference/API_SelfManagedActiveDirectoryConfiguration.html),
35571
+ * with no statement that order is insignificant. DNS resolver lists are
35572
+ * conventionally preference-ordered (primary first), and if FSx honors
35573
+ * that when joining the domain, sorting would HIDE a real reorder. A
35574
+ * false positive is visible and correctable; silently hiding drift is
35575
+ * not. Same reasoning excludes ElastiCache `PreferredAvailabilityZones`.
35576
+ * - `OntapConfiguration.RouteTableIds` / `OpenZFSConfiguration.RouteTableIds`
35577
+ * — their `rtb-` elements already match the shared id heuristic.
35578
+ */
35579
+ getDriftUnorderedPaths(resourceType) {
35580
+ if (resourceType !== "AWS::FSx::FileSystem") return [];
35581
+ return ["WindowsConfiguration.Aliases"];
35582
+ }
35583
+ /**
35558
35584
  * Read the AWS-current file system configuration in CFn-property shape
35559
35585
  * via `DescribeFileSystems`. Lustre data-repository fields (`ImportPath`
35560
35586
  * / `ExportPath` / `AutoImportPolicy` / `ImportedFileChunkSize`) are
@@ -39947,7 +39973,7 @@ var DLMLifecyclePolicyProvider = class {
39947
39973
  const response = await this.getClient().send(new GetLifecyclePolicyCommand$1({ PolicyId: input.knownPhysicalId }));
39948
39974
  return response.Policy?.PolicyId ? {
39949
39975
  physicalId: input.knownPhysicalId,
39950
- attributes: { Arn: response.Policy.PolicyArn ?? "" }
39976
+ attributes: response.Policy.PolicyArn ? { Arn: response.Policy.PolicyArn } : {}
39951
39977
  } : null;
39952
39978
  } catch (err) {
39953
39979
  if (err instanceof ResourceNotFoundException$12) return null;
@@ -44691,6 +44717,19 @@ function createDiffCommand() {
44691
44717
  * on BOTH sides before the diff. These two passes were surfaced by dogfooding
44692
44718
  * the sibling cdk-real-drift (cdkrd) tool, which hit the same false-positive
44693
44719
  * classes against the same kind of AWS-snapshot baseline.
44720
+ *
44721
+ * Plain-string arrays are NOT canonicalized by the two heuristic passes above,
44722
+ * because a scalar list can be order-significant. But several CFn inputs are
44723
+ * semantically unordered sets of plain strings (FSx `WindowsConfiguration.Aliases`,
44724
+ * `...SelfManagedActiveDirectoryConfiguration.DnsIps`, ...), so
44725
+ * {@link canonicalizeUnorderedArraysAtPaths} sorts them at an explicit,
44726
+ * provider-declared path list only — an opt-in seam mirroring
44727
+ * `getDriftUnknownPaths` (see `ResourceProvider.getDriftUnorderedPaths`).
44728
+ * Doing it HERE rather than inside the provider's reverse-mapper is load-bearing:
44729
+ * the normalizer runs on BOTH comparison sides, so it stays correct for the
44730
+ * `properties`-fallback baseline (a resource deployed before observed-capture,
44731
+ * whose baseline is the user's TEMPLATE order). Sorting only the AWS read side
44732
+ * would manufacture drift on exactly that path.
44694
44733
  */
44695
44734
  function canonicalizeTagListsDeep(v) {
44696
44735
  if (Array.isArray(v)) {
@@ -44725,6 +44764,72 @@ function canonicalizeIdArraysDeep(v) {
44725
44764
  }
44726
44765
  return v;
44727
44766
  }
44767
+ /**
44768
+ * Match a dotted property path against a provider-declared path list.
44769
+ *
44770
+ * An entry matches when the path is exactly equal to it, OR when the entry is
44771
+ * a prefix followed by `.`. Every entry is therefore a SUBTREE declaration:
44772
+ * `'VpcConfig'` matches `VpcConfig`, `VpcConfig.SubnetIds`, and anything
44773
+ * deeper. There is no leaf-only form.
44774
+ *
44775
+ * Shared by the two provider-declared path lists so their semantics cannot
44776
+ * drift apart: `getDriftUnknownPaths` (via `isIgnoredPath` in
44777
+ * `drift-calculator.ts`) and `getDriftUnorderedPaths` (via
44778
+ * {@link canonicalizeUnorderedArraysAtPaths} below).
44779
+ */
44780
+ function matchesPathPrefix(path, entries) {
44781
+ for (const entry of entries) {
44782
+ if (path === entry) return true;
44783
+ if (path.startsWith(`${entry}.`)) return true;
44784
+ }
44785
+ return false;
44786
+ }
44787
+ /**
44788
+ * Sort plain-string arrays found at one of the provider-declared `paths`, and
44789
+ * ONLY there. Unlike the two heuristic passes above, this one is opt-in per
44790
+ * resource type via `ResourceProvider.getDriftUnorderedPaths` — a scalar list
44791
+ * is order-significant unless the provider says otherwise.
44792
+ *
44793
+ * Paths use dot-notation for nested keys and are matched by the shared
44794
+ * {@link matchesPathPrefix} rule, the same one `getDriftUnknownPaths` uses. So
44795
+ * `'WindowsConfiguration'` covers every plain-string array in that subtree, and
44796
+ * `'WindowsConfiguration.Aliases'` covers that path and everything beneath it
44797
+ * (every entry is a subtree declaration — there is no leaf-only form).
44798
+ *
44799
+ * One semantic DIVERGENCE from `getDriftUnknownPaths`, required for this pass
44800
+ * to work: `isIgnoredPath` never sees a path that crosses an array, because the
44801
+ * comparator's `diffAt` compares arrays wholesale via `deepEqual` and never
44802
+ * descends into elements. This walk DOES descend into array elements, giving
44803
+ * each element its parent's path (array indices never appear in paths). So
44804
+ * `'Items.Aliases'` is meaningful here — it reaches an `Aliases` array inside
44805
+ * each element of `Items` — while being inert as an ignore-path. The divergence
44806
+ * is strictly more permissive; nothing that matches for ignore-paths fails to
44807
+ * match here.
44808
+ *
44809
+ * Only arrays whose every element is a plain string are sorted, so a
44810
+ * mis-declared path can never reorder object or mixed-type elements. Nested
44811
+ * arrays are also left alone: an array element that is itself an array is not
44812
+ * descended into, so `{P: [['b','a']]}` never has its INNER list sorted by a
44813
+ * `'P'` declaration (no CFn shape in tree is an array-of-arrays, and sorting
44814
+ * one would contradict the plain-string-elements-only rule).
44815
+ */
44816
+ function canonicalizeUnorderedArraysAtPaths(v, paths) {
44817
+ if (paths.length === 0) return v;
44818
+ return walkUnordered(v, "", paths);
44819
+ }
44820
+ function walkUnordered(v, path, paths) {
44821
+ if (Array.isArray(v)) {
44822
+ const mapped = v.map((el) => Array.isArray(el) ? el : walkUnordered(el, path, paths));
44823
+ if (mapped.length > 1 && matchesPathPrefix(path, paths) && mapped.every((el) => typeof el === "string")) return [...mapped].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
44824
+ return mapped;
44825
+ }
44826
+ if (v && typeof v === "object") {
44827
+ const out = {};
44828
+ for (const [k, val] of Object.entries(v)) out[k] = walkUnordered(val, path === "" ? k : `${path}.${k}`, paths);
44829
+ return out;
44830
+ }
44831
+ return v;
44832
+ }
44728
44833
 
44729
44834
  //#endregion
44730
44835
  //#region src/analyzer/drift-calculator.ts
@@ -44784,25 +44889,35 @@ function canonicalizeIdArraysDeep(v) {
44784
44889
  * it is exactly equal to the entry, or when the entry is a prefix followed
44785
44890
  * by `.` — so `'Code'` excludes the whole `Code` subtree, and
44786
44891
  * `'VpcConfig.SubnetIds'` excludes only that leaf.
44892
+ *
44893
+ * `options.unorderedPaths` is supplied by the provider (via
44894
+ * `getDriftUnorderedPaths`) for plain-string arrays that are semantically
44895
+ * UNORDERED sets — e.g. FSx `WindowsConfiguration.Aliases`. Those are sorted on
44896
+ * BOTH sides before comparison so an AWS-side reorder is not phantom drift.
44897
+ * Path matching uses the same prefix rule as `ignorePaths`.
44787
44898
  */
44788
44899
  function calculateResourceDrift(stateProperties, awsProperties, options) {
44789
44900
  const drifts = [];
44790
44901
  const ignore = options?.ignorePaths ?? [];
44791
44902
  const union = options?.unionWalkObjects ?? false;
44792
- stateProperties = canonicalizeIdArraysDeep(canonicalizeTagListsDeep(stateProperties));
44793
- awsProperties = canonicalizeIdArraysDeep(canonicalizeTagListsDeep(awsProperties));
44903
+ const unordered = options?.unorderedPaths ?? [];
44904
+ stateProperties = canonicalizeUnorderedArraysAtPaths(canonicalizeIdArraysDeep(canonicalizeTagListsDeep(stateProperties)), unordered);
44905
+ awsProperties = canonicalizeUnorderedArraysAtPaths(canonicalizeIdArraysDeep(canonicalizeTagListsDeep(awsProperties)), unordered);
44794
44906
  for (const key of Object.keys(stateProperties)) {
44795
44907
  if (isIgnoredPath(key, ignore)) continue;
44796
44908
  diffAt(key, stateProperties[key], awsProperties[key], drifts, ignore, union);
44797
44909
  }
44798
44910
  return drifts;
44799
44911
  }
44912
+ /**
44913
+ * Thin alias over the shared {@link matchesPathPrefix} rule, kept as a named
44914
+ * function because "ignored" is what the path list means at these call sites.
44915
+ * Sharing the implementation with `getDriftUnorderedPaths` is deliberate: both
44916
+ * lists are provider-declared and documented as reading the same way, so they
44917
+ * must not be able to drift apart.
44918
+ */
44800
44919
  function isIgnoredPath(path, ignorePaths) {
44801
- for (const entry of ignorePaths) {
44802
- if (path === entry) return true;
44803
- if (path.startsWith(`${entry}.`)) return true;
44804
- }
44805
- return false;
44920
+ return matchesPathPrefix(path, ignorePaths);
44806
44921
  }
44807
44922
  /**
44808
44923
  * Recursive worker. Pushes drift entries into `out` rather than
@@ -45204,10 +45319,12 @@ async function runDriftForStack(stackName, region, stateBackend, providerRegistr
45204
45319
  continue;
45205
45320
  }
45206
45321
  const ignorePaths = provider.getDriftUnknownPaths ? provider.getDriftUnknownPaths(resource.resourceType) : [];
45322
+ const unorderedPaths = provider.getDriftUnorderedPaths ? provider.getDriftUnorderedPaths(resource.resourceType) : [];
45207
45323
  const useObserved = resource.observedProperties !== void 0;
45208
45324
  const changes = calculateResourceDrift(useObserved ? resource.observedProperties : resource.properties ?? {}, aws, {
45209
45325
  ignorePaths,
45210
- unionWalkObjects: useObserved
45326
+ unionWalkObjects: useObserved,
45327
+ unorderedPaths
45211
45328
  });
45212
45329
  if (changes.length === 0) outcomes.push({
45213
45330
  kind: "clean",
@@ -51901,7 +52018,8 @@ async function importOne(task) {
51901
52018
  logicalId,
51902
52019
  resourceType: resource.Type,
51903
52020
  outcome: "imported",
51904
- physicalId: result.physicalId
52021
+ physicalId: result.physicalId,
52022
+ ...result.attributes !== void 0 && { attributes: result.attributes }
51905
52023
  };
51906
52024
  } catch (error) {
51907
52025
  const msg = error instanceof Error ? error.message : String(error);
@@ -52044,11 +52162,14 @@ function buildStackState(stackName, region, rows, templateParser, template, exis
52044
52162
  const tmplResource = template.Resources[row.logicalId];
52045
52163
  if (!tmplResource) continue;
52046
52164
  const deps = [...templateParser.extractDependencies(tmplResource)].filter((dep) => !parameterNames.has(dep));
52165
+ const prior = existingState?.resources[row.logicalId];
52166
+ const priorAttributes = prior && prior.physicalId === row.physicalId ? prior.attributes : void 0;
52167
+ const rowAttributes = row.attributes && Object.keys(row.attributes).length > 0 ? row.attributes : void 0;
52047
52168
  resources[row.logicalId] = {
52048
52169
  physicalId: row.physicalId,
52049
52170
  resourceType: row.resourceType,
52050
52171
  properties: tmplResource.Properties ?? {},
52051
- attributes: {},
52172
+ attributes: rowAttributes ?? priorAttributes ?? {},
52052
52173
  dependencies: deps,
52053
52174
  provisionedBy: "sdk"
52054
52175
  };
@@ -62017,7 +62138,7 @@ function reorderArgs(argv) {
62017
62138
  async function main() {
62018
62139
  installPipeCloseHandler();
62019
62140
  const program = new Command();
62020
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.252.0");
62141
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.252.2");
62021
62142
  program.addCommand(createBootstrapCommand());
62022
62143
  program.addCommand(createSynthCommand());
62023
62144
  program.addCommand(createListCommand());