@intentius/chant-lexicon-k8s 0.44.3 → 0.44.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.
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/crd/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAwD,MAAM,eAAe,CAAC;AAC1G,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAsEvC;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAqB1D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,EAAE,CAoD5D"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/crd/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAwD,MAAM,eAAe,CAAC;AAC1G,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAKvC;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAqB1D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,EAAE,CAoD5D"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * API group → `K8s::{Namespace}::{Kind}` namespace segment — the ONE copy.
3
+ *
4
+ * Four surfaces build entityTypes from a group string: CRD codegen
5
+ * (`crd/parser.ts`), the swagger pass (`spec/parse.ts`), live discovery
6
+ * (`describe-resources.ts` via `gvkToTypeName`), and YAML import
7
+ * (`import/parser.ts`). Until #1628 each had its own copy of the rule and only
8
+ * CRD codegen consulted the overrides, so a Flux Kustomization arriving through
9
+ * a kustomize root or `chant import` was typed `K8s::Kustomize::Kustomization`
10
+ * — a name that matches nothing in operations.json (NOT-OBSERVED live) and
11
+ * nothing downstream that dispatches on kind. Every path resolves here now;
12
+ * the declared, rendered, imported and discovered spellings of a kind cannot
13
+ * skew.
14
+ */
15
+ /**
16
+ * Group names whose first segment doesn't yield the conventional namespace.
17
+ * "argoproj.io" → "Argo" (not "Argoproj") to match the Argo CD vocabulary
18
+ * and the ArgoAppFor / ArgoAppSetForRegions composites.
19
+ *
20
+ * The Flux toolkit spreads across five `*.toolkit.fluxcd.io` groups plus the
21
+ * Flux Operator's `fluxcd.controlplane.io`; all six collapse to a single `Flux`
22
+ * namespace so a GitRepository and a Kustomization read as `K8s::Flux::*`
23
+ * siblings rather than scattering to Source / Kustomize / Helm / Notification /
24
+ * Image / Fluxcd. (`helm.toolkit.fluxcd.io` → `Helm` would also collide
25
+ * confusingly with the separate helm lexicon.)
26
+ *
27
+ * KubeMicroVM's `lambda.aws.amazon.com` would take `Lambda` by the
28
+ * first-segment rule, which reads as AWS Lambda proper and would sit
29
+ * confusingly beside the aws lexicon's real Lambda functions. These are
30
+ * Kubernetes CRs belonging to a community operator, so they take the
31
+ * operator's own name. `MicroVM` alone was the alternative and was rejected
32
+ * for stuttering: `K8s::MicroVM::MicroVM`.
33
+ *
34
+ * Note the official AWS controller uses a different group,
35
+ * `lambdamicrovms.services.k8s.aws`, so it can coexist here and will want its
36
+ * own entry rather than sharing this one.
37
+ */
38
+ export declare const GROUP_NAMESPACE_OVERRIDES: Record<string, string>;
39
+ /**
40
+ * Normalize an API group to its PascalCase namespace segment.
41
+ * "" → "Core", overrides first ("argoproj.io" → "Argo"), then the first
42
+ * dot-segment kebab→PascalCased: "cert-manager.io" → "CertManager",
43
+ * "rbac.authorization.k8s.io" → "Rbac", "apps" → "Apps".
44
+ */
45
+ export declare function namespaceSegmentForGroup(group: string): string;
46
+ //# sourceMappingURL=group-namespace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-namespace.d.ts","sourceRoot":"","sources":["../src/group-namespace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAuB5D,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS9D"}
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/import/parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAc,MAAM,gCAAgC,CAAC;AAwH7F;;;;;GAKG;AACH,qBAAa,SAAU,YAAW,cAAc;IAC9C,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU;IAsClC,OAAO,CAAC,aAAa;CA8BtB"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/import/parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAc,MAAM,gCAAgC,CAAC;AAyH7F;;;;;GAKG;AACH,qBAAa,SAAU,YAAW,cAAc;IAC9C,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU;IAsClC,OAAO,CAAC,aAAa;CA8BtB"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "d94d393cea4fe4e3bebaa9590a85d8966e15e27d6cf2233ed07395182146501b",
4
+ "manifest.json": "994f303cfb6edefd9d0eb5d18406f784172b3fb0bd5b524e7e3255a96f4bf338",
5
5
  "meta.json": "f568fb8a0cbd4adf041baccaa3e1164c2e3c5c0090186236f887f0084f5521a8",
6
6
  "types/index.d.ts": "ef2c9df6a4d1ebfbe33f96c094fbbe3fc7909522ea38e086666fbf3c8ac477fd",
7
7
  "rules/argo-appset-single-project.ts": "afa9f310753aa2d475f35012b13135b2dfaebed2a35d44edbe185ebc07673674",
@@ -54,5 +54,5 @@
54
54
  "skills/chant-k8s-argo.md": "b1a0b826559d8c5033a479c5781efaf650320f0aee4419d8841170bd3393cea5",
55
55
  "skills/chant-k8s-flux.md": "9670efc2e9854bd14686b5adeb707396b9030a4dcaee6529b30abf56efe2c50c"
56
56
  },
57
- "composite": "1031cf6d53da6ad5ddf631aaa8160f1a5ca914c95a124b0827e42b40b99ab270"
57
+ "composite": "815ab02fe7f7325c6fbec07be9f9f8423df8d9771ddd23babef1af5ae60c0acd"
58
58
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "k8s",
3
- "version": "0.44.3",
3
+ "version": "0.44.4",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "K8s",
6
6
  "intrinsics": [],
@@ -1 +1 @@
1
- {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/spec/parse.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/spec/parse.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-k8s",
3
- "version": "0.44.3",
3
+ "version": "0.44.4",
4
4
  "description": "Kubernetes lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -76,7 +76,7 @@
76
76
  "@types/js-yaml": "^4.0.9"
77
77
  },
78
78
  "optionalDependencies": {
79
- "@intentius/chant-k8s-client": "^0.44.3"
79
+ "@intentius/chant-k8s-client": "^0.44.4"
80
80
  },
81
81
  "devDependencies": {
82
82
  "@intentius/chant": "*",
@@ -84,7 +84,7 @@
84
84
  },
85
85
  "peerDependencies": {
86
86
  "zod": "^4.3.6",
87
- "@intentius/chant": "^0.44.3",
87
+ "@intentius/chant": "^0.44.4",
88
88
  "typescript": "^5.9.3"
89
89
  }
90
90
  }
package/src/crd/parser.ts CHANGED
@@ -9,75 +9,10 @@
9
9
 
10
10
  import type { K8sParseResult, ParsedProperty, ParsedPropertyType, GroupVersionKind } from "../spec/parse";
11
11
  import type { CRDSpec } from "./types";
12
+ import { namespaceSegmentForGroup } from "../group-namespace";
12
13
  import type { PropertyConstraints } from "@intentius/chant/codegen/json-schema";
13
14
  import { loadAll } from "js-yaml";
14
15
 
15
- /**
16
- * Group names whose first segment doesn't yield the conventional namespace.
17
- * "argoproj.io" → "Argo" (not "Argoproj") to match the Argo CD vocabulary
18
- * and the ArgoAppFor / ArgoAppSetForRegions composites.
19
- *
20
- * The Flux toolkit spreads across five `*.toolkit.fluxcd.io` groups plus the
21
- * Flux Operator's `fluxcd.controlplane.io`; all six collapse to a single `Flux`
22
- * namespace so a GitRepository and a Kustomization read as `K8s::Flux::*`
23
- * siblings rather than scattering to Source / Kustomize / Helm / Notification /
24
- * Image / Fluxcd. (`helm.toolkit.fluxcd.io` → `Helm` would also collide
25
- * confusingly with the separate helm lexicon.)
26
- *
27
- * KubeMicroVM's `lambda.aws.amazon.com` would take `Lambda` by the
28
- * first-segment rule, which reads as AWS Lambda proper and would sit
29
- * confusingly beside the aws lexicon's real Lambda functions. These are
30
- * Kubernetes CRs belonging to a community operator, so they take the
31
- * operator's own name. `MicroVM` alone was the alternative and was rejected
32
- * for stuttering: `K8s::MicroVM::MicroVM`.
33
- *
34
- * Note the official AWS controller uses a different group,
35
- * `lambdamicrovms.services.k8s.aws`, so it can coexist here and will want its
36
- * own entry rather than sharing this one.
37
- */
38
- const GROUP_NAMESPACE_OVERRIDES: Record<string, string> = {
39
- "argoproj.io": "Argo",
40
- "source.toolkit.fluxcd.io": "Flux",
41
- "kustomize.toolkit.fluxcd.io": "Flux",
42
- "helm.toolkit.fluxcd.io": "Flux",
43
- "notification.toolkit.fluxcd.io": "Flux",
44
- "image.toolkit.fluxcd.io": "Flux",
45
- "fluxcd.controlplane.io": "Flux",
46
- "lambda.aws.amazon.com": "KubeMicroVM",
47
- // CNPG and its barman-cloud plugin ship under two groups but are one thing to
48
- // an author: a Cluster's `plugins[]` names an ObjectStore. The first-segment
49
- // rule would scatter them into `Postgresql` and `Barmancloud`.
50
- "postgresql.cnpg.io": "Cnpg",
51
- "barmancloud.cnpg.io": "Cnpg",
52
- // Not `Secrets`: `K8s::Secrets::InfisicalSecret` reads like a core Secret,
53
- // and `K8s::Core::Secret` is right there to be confused with.
54
- "secrets.infisical.com": "Infisical",
55
- // k3s's bundled controllers ship under two groups but are one thing to an
56
- // author — the k3s auto-deploy surface. The first-segment rule would give
57
- // `K8s::Helm::HelmChart`, which reads like it belongs to the helm lexicon,
58
- // and would split HelmChart from the Addon that tracks its deployment.
59
- "helm.cattle.io": "K3s",
60
- "k3s.cattle.io": "K3s",
61
- };
62
-
63
- /**
64
- * Normalize a CRD group to a PascalCase namespace segment.
65
- * "cert-manager.io" → "CertManager"
66
- * "monitoring.coreos.com" → "Monitoring"
67
- * "argoproj.io" → "Argo" (override)
68
- */
69
- function normalizeGroupName(group: string): string {
70
- const override = GROUP_NAMESPACE_OVERRIDES[group];
71
- if (override) return override;
72
- // Take the first segment before the first dot
73
- const firstSegment = group.split(".")[0];
74
- // Convert kebab-case to PascalCase
75
- return firstSegment
76
- .split("-")
77
- .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
78
- .join("");
79
- }
80
-
81
16
  /**
82
17
  * Parse a CRD YAML document string into K8sParseResult entries.
83
18
  * Returns one result per served version in the CRD.
@@ -111,7 +46,7 @@ export function parseCRD(content: string): K8sParseResult[] {
111
46
  */
112
47
  export function parseCRDSpec(spec: CRDSpec): K8sParseResult[] {
113
48
  const results: K8sParseResult[] = [];
114
- const groupNs = normalizeGroupName(spec.group);
49
+ const groupNs = namespaceSegmentForGroup(spec.group);
115
50
 
116
51
  // Find the storage version (the canonical version)
117
52
  const storageVersion = spec.versions.find((v) => v.storage && v.served);
@@ -731,7 +731,7 @@ describe("k8s describeResources", () => {
731
731
  );
732
732
 
733
733
  expect(result.resources["microvm-demo/kmv-dev-a-vm-42sgf"]).toMatchObject({
734
- type: "K8s::Lambda::MicroVM",
734
+ type: "K8s::KubeMicroVM::MicroVM",
735
735
  physicalId: "vm-uid",
736
736
  ownerChain: { root: "declared", entity: "workloadReplicaSet" },
737
737
  });
@@ -0,0 +1,64 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { namespaceSegmentForGroup } from "./group-namespace";
3
+ import { renderedManifestEntity } from "./kustomize/rendered-entity";
4
+ import { K8sParser } from "./import/parser";
5
+
6
+ describe("namespaceSegmentForGroup", () => {
7
+ test("the empty core group is Core", () => {
8
+ expect(namespaceSegmentForGroup("")).toBe("Core");
9
+ });
10
+
11
+ test("a single-segment group PascalCases", () => {
12
+ expect(namespaceSegmentForGroup("apps")).toBe("Apps");
13
+ });
14
+
15
+ test("only the first dot-segment counts", () => {
16
+ expect(namespaceSegmentForGroup("rbac.authorization.k8s.io")).toBe("Rbac");
17
+ });
18
+
19
+ test("a hyphenated first segment kebab→Pascals", () => {
20
+ expect(namespaceSegmentForGroup("cert-manager.io")).toBe("CertManager");
21
+ });
22
+
23
+ test("argoproj.io takes the Argo override", () => {
24
+ expect(namespaceSegmentForGroup("argoproj.io")).toBe("Argo");
25
+ });
26
+
27
+ test("the toolkit groups collapse to Flux", () => {
28
+ expect(namespaceSegmentForGroup("kustomize.toolkit.fluxcd.io")).toBe("Flux");
29
+ });
30
+ });
31
+
32
+ // #1628: rendered and imported entityTypes used to bypass the overrides, so a
33
+ // Flux CR arriving through a kustomize root or `chant import` was typed to a
34
+ // name that matches nothing in operations.json.
35
+ describe("the shared rule reaches the render and import paths", () => {
36
+ test("a rendered Flux Kustomization is typed K8s::Flux::Kustomization", () => {
37
+ const entity = renderedManifestEntity(
38
+ {
39
+ apiVersion: "kustomize.toolkit.fluxcd.io/v1",
40
+ kind: "Kustomization",
41
+ metadata: { name: "apps", namespace: "flux-system" },
42
+ spec: { path: "./apps/prod", prune: true },
43
+ },
44
+ "clusters/prod",
45
+ );
46
+
47
+ expect(entity?.entityType).toBe("K8s::Flux::Kustomization");
48
+ });
49
+
50
+ test("an imported Argo Application is typed K8s::Argo::Application", () => {
51
+ const ir = new K8sParser().parse(`
52
+ apiVersion: argoproj.io/v1alpha1
53
+ kind: Application
54
+ metadata:
55
+ name: guestbook
56
+ namespace: argocd
57
+ spec:
58
+ project: default
59
+ `);
60
+
61
+ expect(ir.resources.length).toBe(1);
62
+ expect(ir.resources[0].type).toBe("K8s::Argo::Application");
63
+ });
64
+ });
@@ -0,0 +1,79 @@
1
+ /**
2
+ * API group → `K8s::{Namespace}::{Kind}` namespace segment — the ONE copy.
3
+ *
4
+ * Four surfaces build entityTypes from a group string: CRD codegen
5
+ * (`crd/parser.ts`), the swagger pass (`spec/parse.ts`), live discovery
6
+ * (`describe-resources.ts` via `gvkToTypeName`), and YAML import
7
+ * (`import/parser.ts`). Until #1628 each had its own copy of the rule and only
8
+ * CRD codegen consulted the overrides, so a Flux Kustomization arriving through
9
+ * a kustomize root or `chant import` was typed `K8s::Kustomize::Kustomization`
10
+ * — a name that matches nothing in operations.json (NOT-OBSERVED live) and
11
+ * nothing downstream that dispatches on kind. Every path resolves here now;
12
+ * the declared, rendered, imported and discovered spellings of a kind cannot
13
+ * skew.
14
+ */
15
+
16
+ /**
17
+ * Group names whose first segment doesn't yield the conventional namespace.
18
+ * "argoproj.io" → "Argo" (not "Argoproj") to match the Argo CD vocabulary
19
+ * and the ArgoAppFor / ArgoAppSetForRegions composites.
20
+ *
21
+ * The Flux toolkit spreads across five `*.toolkit.fluxcd.io` groups plus the
22
+ * Flux Operator's `fluxcd.controlplane.io`; all six collapse to a single `Flux`
23
+ * namespace so a GitRepository and a Kustomization read as `K8s::Flux::*`
24
+ * siblings rather than scattering to Source / Kustomize / Helm / Notification /
25
+ * Image / Fluxcd. (`helm.toolkit.fluxcd.io` → `Helm` would also collide
26
+ * confusingly with the separate helm lexicon.)
27
+ *
28
+ * KubeMicroVM's `lambda.aws.amazon.com` would take `Lambda` by the
29
+ * first-segment rule, which reads as AWS Lambda proper and would sit
30
+ * confusingly beside the aws lexicon's real Lambda functions. These are
31
+ * Kubernetes CRs belonging to a community operator, so they take the
32
+ * operator's own name. `MicroVM` alone was the alternative and was rejected
33
+ * for stuttering: `K8s::MicroVM::MicroVM`.
34
+ *
35
+ * Note the official AWS controller uses a different group,
36
+ * `lambdamicrovms.services.k8s.aws`, so it can coexist here and will want its
37
+ * own entry rather than sharing this one.
38
+ */
39
+ export const GROUP_NAMESPACE_OVERRIDES: Record<string, string> = {
40
+ "argoproj.io": "Argo",
41
+ "source.toolkit.fluxcd.io": "Flux",
42
+ "kustomize.toolkit.fluxcd.io": "Flux",
43
+ "helm.toolkit.fluxcd.io": "Flux",
44
+ "notification.toolkit.fluxcd.io": "Flux",
45
+ "image.toolkit.fluxcd.io": "Flux",
46
+ "fluxcd.controlplane.io": "Flux",
47
+ "lambda.aws.amazon.com": "KubeMicroVM",
48
+ // CNPG and its barman-cloud plugin ship under two groups but are one thing to
49
+ // an author: a Cluster's `plugins[]` names an ObjectStore. The first-segment
50
+ // rule would scatter them into `Postgresql` and `Barmancloud`.
51
+ "postgresql.cnpg.io": "Cnpg",
52
+ "barmancloud.cnpg.io": "Cnpg",
53
+ // Not `Secrets`: `K8s::Secrets::InfisicalSecret` reads like a core Secret,
54
+ // and `K8s::Core::Secret` is right there to be confused with.
55
+ "secrets.infisical.com": "Infisical",
56
+ // k3s's bundled controllers ship under two groups but are one thing to an
57
+ // author — the k3s auto-deploy surface. The first-segment rule would give
58
+ // `K8s::Helm::HelmChart`, which reads like it belongs to the helm lexicon,
59
+ // and would split HelmChart from the Addon that tracks its deployment.
60
+ "helm.cattle.io": "K3s",
61
+ "k3s.cattle.io": "K3s",
62
+ };
63
+
64
+ /**
65
+ * Normalize an API group to its PascalCase namespace segment.
66
+ * "" → "Core", overrides first ("argoproj.io" → "Argo"), then the first
67
+ * dot-segment kebab→PascalCased: "cert-manager.io" → "CertManager",
68
+ * "rbac.authorization.k8s.io" → "Rbac", "apps" → "Apps".
69
+ */
70
+ export function namespaceSegmentForGroup(group: string): string {
71
+ if (!group || group === "") return "Core";
72
+ const override = GROUP_NAMESPACE_OVERRIDES[group];
73
+ if (override) return override;
74
+ const firstSegment = group.split(".")[0];
75
+ return firstSegment
76
+ .split("-")
77
+ .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
78
+ .join("");
79
+ }
@@ -8,6 +8,7 @@
8
8
 
9
9
  import type { TemplateParser, TemplateIR, ResourceIR } from "@intentius/chant/import/parser";
10
10
  import { parseYAML } from "@intentius/chant/yaml";
11
+ import { namespaceSegmentForGroup } from "../group-namespace";
11
12
 
12
13
  // ── GVK to type name mapping ───────────────────────────────────────
13
14
 
@@ -91,20 +92,20 @@ function resolveTypeName(apiVersion: string, kind: string): string {
91
92
  }
92
93
 
93
94
  /**
94
- * Extract PascalCase group name from an apiVersion string.
95
+ * Extract the namespace segment from an apiVersion string.
95
96
  * "apps/v1" → "Apps"
96
97
  * "v1" → "Core"
97
- * "networking.k8s.io/v1" → "Networking"
98
98
  * "rbac.authorization.k8s.io/v1" → "Rbac"
99
+ * "argoproj.io/v1alpha1" → "Argo" (override)
100
+ *
101
+ * Splits the version off and hands the group to the shared rule, so an
102
+ * imported manifest is typed exactly as a declared one is.
99
103
  */
100
104
  function apiVersionToGroup(apiVersion: string): string {
101
105
  const slashIdx = apiVersion.indexOf("/");
102
106
  if (slashIdx === -1) return "Core"; // core group (v1)
103
107
 
104
- const groupStr = apiVersion.slice(0, slashIdx);
105
- const firstSegment = groupStr.split(".")[0];
106
- if (firstSegment === "rbac") return "Rbac";
107
- return firstSegment.charAt(0).toUpperCase() + firstSegment.slice(1);
108
+ return namespaceSegmentForGroup(apiVersion.slice(0, slashIdx));
108
109
  }
109
110
 
110
111
  /**
@@ -94,7 +94,9 @@ describe("renderKustomizeRoots", () => {
94
94
  // The generated operation surface's naming, so `lifecycle diff --live`
95
95
  // observes a rendered Deployment through the same reader a declared one uses.
96
96
  expect(deployment.entityType).toBe("K8s::Apps::Deployment");
97
- expect(entities.get("overlays/prod/certificateProdWebTls")!.entityType).toBe("K8s::Cert-manager::Certificate");
97
+ // Hyphenated groups PascalCase the same way the declarable surface does
98
+ // (#1628), so this Certificate matches the reader a declared one gets.
99
+ expect(entities.get("overlays/prod/certificateProdWebTls")!.entityType).toBe("K8s::CertManager::Certificate");
98
100
 
99
101
  const props = (deployment as unknown as { props: Record<string, unknown> }).props;
100
102
  const metadata = props.metadata as { annotations: Record<string, string> };
package/src/spec/parse.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  primaryType,
14
14
  type JsonSchemaProperty,
15
15
  } from "@intentius/chant/codegen/json-schema";
16
+ import { namespaceSegmentForGroup } from "../group-namespace";
16
17
 
17
18
  // ── Types ──────────────────────────────────────────────────────────
18
19
 
@@ -343,22 +344,13 @@ export function gvkToApiVersion(gvk: GroupVersionKind): string {
343
344
  }
344
345
 
345
346
  /**
346
- * Normalize API group to a PascalCase segment.
347
- * Empty group (core API) "Core"
348
- * "apps" "Apps"
349
- * "batch" "Batch"
350
- * "rbac.authorization.k8s.io" → "Rbac"
351
- * "networking.k8s.io" → "Networking"
347
+ * Normalize API group to a PascalCase segment — the shared rule, so the
348
+ * swagger-generated surface, kustomize-rendered entities and live discovery
349
+ * (all three reach here through `gvkToTypeName`) spell a kind exactly as CRD
350
+ * codegen and `chant import` do. See `../group-namespace`.
352
351
  */
353
352
  function normalizeGroup(group: string): string {
354
- if (!group || group === "") return "Core";
355
-
356
- // Take the first segment before any dots
357
- const firstSegment = group.split(".")[0];
358
- // Special-case RBAC
359
- if (firstSegment === "rbac") return "Rbac";
360
- // PascalCase the first segment
361
- return firstSegment.charAt(0).toUpperCase() + firstSegment.slice(1);
353
+ return namespaceSegmentForGroup(group);
362
354
  }
363
355
 
364
356
  /**