@intentius/chant-lexicon-k8s 0.34.1 → 0.36.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.
@@ -4,7 +4,8 @@
4
4
 
5
5
  export declare class Alert {
6
6
  constructor(props: {
7
- metadata?: Record<string, unknown>;
7
+ /** Standard object's metadata. */
8
+ metadata?: ObjectMeta;
8
9
  /** AlertSpec defines an alerting rule for events involving a list of objects. */
9
10
  spec?: Record<string, unknown>;
10
11
  });
@@ -89,9 +90,10 @@ export declare class APIVersions {
89
90
 
90
91
  export declare class Application {
91
92
  constructor(props: {
92
- metadata: Record<string, unknown>;
93
93
  /** ApplicationSpec represents desired application state. Contains link to repository with application definition and additional parameters link definition revision. */
94
94
  spec: Record<string, unknown>;
95
+ /** Standard object's metadata. */
96
+ metadata?: ObjectMeta;
95
97
  /** Operation contains information about a requested or running operation */
96
98
  operation?: Record<string, unknown>;
97
99
  });
@@ -103,8 +105,9 @@ export declare class Application {
103
105
 
104
106
  export declare class ApplicationSet {
105
107
  constructor(props: {
106
- metadata: Record<string, unknown>;
107
108
  spec: Record<string, unknown>;
109
+ /** Standard object's metadata. */
110
+ metadata?: ObjectMeta;
108
111
  });
109
112
  readonly name: string;
110
113
  readonly namespace: string;
@@ -114,9 +117,10 @@ export declare class ApplicationSet {
114
117
 
115
118
  export declare class AppProject {
116
119
  constructor(props: {
117
- metadata: Record<string, unknown>;
118
120
  /** AppProjectSpec is the specification of an AppProject */
119
121
  spec: Record<string, unknown>;
122
+ /** Standard object's metadata. */
123
+ metadata?: ObjectMeta;
120
124
  });
121
125
  readonly name: string;
122
126
  readonly namespace: string;
@@ -150,7 +154,8 @@ export declare class Binding {
150
154
 
151
155
  export declare class Bucket {
152
156
  constructor(props: {
153
- metadata?: Record<string, unknown>;
157
+ /** Standard object's metadata. */
158
+ metadata?: ObjectMeta;
154
159
  /** BucketSpec specifies the required configuration to produce an Artifact for
155
160
  an object storage bucket. */
156
161
  spec?: Record<string, unknown>;
@@ -163,7 +168,8 @@ an object storage bucket. */
163
168
 
164
169
  export declare class Certificate {
165
170
  constructor(props: {
166
- metadata?: Record<string, unknown>;
171
+ /** Standard object's metadata. */
172
+ metadata?: ObjectMeta;
167
173
  /** Specification of the desired state of the Certificate resource.
168
174
  https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status */
169
175
  spec?: Record<string, unknown>;
@@ -176,7 +182,8 @@ https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions
176
182
 
177
183
  export declare class CertificateRequest {
178
184
  constructor(props: {
179
- metadata?: Record<string, unknown>;
185
+ /** Standard object's metadata. */
186
+ metadata?: ObjectMeta;
180
187
  /** Specification of the desired state of the CertificateRequest resource.
181
188
  https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status */
182
189
  spec?: Record<string, unknown>;
@@ -211,8 +218,9 @@ export declare class CertificateSigningRequestList {
211
218
 
212
219
  export declare class Challenge {
213
220
  constructor(props: {
214
- metadata: Record<string, unknown>;
215
221
  spec: Record<string, unknown>;
222
+ /** Standard object's metadata. */
223
+ metadata?: ObjectMeta;
216
224
  });
217
225
  readonly name: string;
218
226
  readonly namespace: string;
@@ -224,7 +232,8 @@ export declare class ClusterIssuer {
224
232
  constructor(props: {
225
233
  /** Desired state of the ClusterIssuer resource. */
226
234
  spec: Record<string, unknown>;
227
- metadata?: Record<string, unknown>;
235
+ /** Standard object's metadata. */
236
+ metadata?: ObjectMeta;
228
237
  });
229
238
  readonly name: string;
230
239
  readonly namespace: string;
@@ -416,7 +425,8 @@ export declare class CoreEventList {
416
425
 
417
426
  export declare class CrdbCluster {
418
427
  constructor(props: {
419
- metadata?: Record<string, unknown>;
428
+ /** Standard object's metadata. */
429
+ metadata?: ObjectMeta;
420
430
  /** CrdbClusterSpec defines the desired state of a CockroachDB Cluster that the operator maintains. */
421
431
  spec?: Record<string, unknown>;
422
432
  });
@@ -884,7 +894,8 @@ export declare class FlowSchemaList {
884
894
 
885
895
  export declare class FluxInstance {
886
896
  constructor(props: {
887
- metadata?: Record<string, unknown>;
897
+ /** Standard object's metadata. */
898
+ metadata?: ObjectMeta;
888
899
  /** FluxInstanceSpec defines the desired state of FluxInstance */
889
900
  spec?: Record<string, unknown>;
890
901
  });
@@ -896,7 +907,8 @@ export declare class FluxInstance {
896
907
 
897
908
  export declare class FluxReport {
898
909
  constructor(props: {
899
- metadata?: Record<string, unknown>;
910
+ /** Standard object's metadata. */
911
+ metadata?: ObjectMeta;
900
912
  /** FluxReportSpec defines the observed state of a Flux installation. */
901
913
  spec?: Record<string, unknown>;
902
914
  });
@@ -910,7 +922,8 @@ export declare class Gateway {
910
922
  constructor(props: {
911
923
  /** Spec defines the desired state of Gateway. */
912
924
  spec: Record<string, unknown>;
913
- metadata?: Record<string, unknown>;
925
+ /** Standard object's metadata. */
926
+ metadata?: ObjectMeta;
914
927
  });
915
928
  readonly name: string;
916
929
  readonly namespace: string;
@@ -922,7 +935,8 @@ export declare class GatewayClass {
922
935
  constructor(props: {
923
936
  /** Spec defines the desired state of GatewayClass. */
924
937
  spec: Record<string, unknown>;
925
- metadata?: Record<string, unknown>;
938
+ /** Standard object's metadata. */
939
+ metadata?: ObjectMeta;
926
940
  });
927
941
  readonly name: string;
928
942
  readonly namespace: string;
@@ -932,7 +946,8 @@ export declare class GatewayClass {
932
946
 
933
947
  export declare class GitRepository {
934
948
  constructor(props: {
935
- metadata?: Record<string, unknown>;
949
+ /** Standard object's metadata. */
950
+ metadata?: ObjectMeta;
936
951
  /** GitRepositorySpec specifies the required configuration to produce an
937
952
  Artifact for a Git repository. */
938
953
  spec?: Record<string, unknown>;
@@ -945,7 +960,8 @@ Artifact for a Git repository. */
945
960
 
946
961
  export declare class GRPCRoute {
947
962
  constructor(props: {
948
- metadata?: Record<string, unknown>;
963
+ /** Standard object's metadata. */
964
+ metadata?: ObjectMeta;
949
965
  /** Spec defines the desired state of GRPCRoute. */
950
966
  spec?: Record<string, unknown>;
951
967
  });
@@ -957,7 +973,8 @@ export declare class GRPCRoute {
957
973
 
958
974
  export declare class HelmChart {
959
975
  constructor(props: {
960
- metadata?: Record<string, unknown>;
976
+ /** Standard object's metadata. */
977
+ metadata?: ObjectMeta;
961
978
  /** HelmChartSpec specifies the desired state of a Helm chart. */
962
979
  spec?: Record<string, unknown>;
963
980
  });
@@ -969,7 +986,8 @@ export declare class HelmChart {
969
986
 
970
987
  export declare class HelmRelease {
971
988
  constructor(props: {
972
- metadata?: Record<string, unknown>;
989
+ /** Standard object's metadata. */
990
+ metadata?: ObjectMeta;
973
991
  /** HelmReleaseSpec defines the desired state of a Helm release. */
974
992
  spec?: Record<string, unknown>;
975
993
  });
@@ -981,7 +999,8 @@ export declare class HelmRelease {
981
999
 
982
1000
  export declare class HelmRepository {
983
1001
  constructor(props: {
984
- metadata?: Record<string, unknown>;
1002
+ /** Standard object's metadata. */
1003
+ metadata?: ObjectMeta;
985
1004
  /** HelmRepositorySpec specifies the required configuration to produce an
986
1005
  Artifact for a Helm repository index YAML. */
987
1006
  spec?: Record<string, unknown>;
@@ -1032,7 +1051,8 @@ export declare class HTTPRoute {
1032
1051
  constructor(props: {
1033
1052
  /** Spec defines the desired state of HTTPRoute. */
1034
1053
  spec: Record<string, unknown>;
1035
- metadata?: Record<string, unknown>;
1054
+ /** Standard object's metadata. */
1055
+ metadata?: ObjectMeta;
1036
1056
  });
1037
1057
  readonly name: string;
1038
1058
  readonly namespace: string;
@@ -1042,7 +1062,8 @@ export declare class HTTPRoute {
1042
1062
 
1043
1063
  export declare class ImagePolicy {
1044
1064
  constructor(props: {
1045
- metadata?: Record<string, unknown>;
1065
+ /** Standard object's metadata. */
1066
+ metadata?: ObjectMeta;
1046
1067
  /** ImagePolicySpec defines the parameters for calculating the
1047
1068
  ImagePolicy. */
1048
1069
  spec?: Record<string, unknown>;
@@ -1055,7 +1076,8 @@ ImagePolicy. */
1055
1076
 
1056
1077
  export declare class ImageRepository {
1057
1078
  constructor(props: {
1058
- metadata?: Record<string, unknown>;
1079
+ /** Standard object's metadata. */
1080
+ metadata?: ObjectMeta;
1059
1081
  /** ImageRepositorySpec defines the parameters for scanning an image
1060
1082
  repository, e.g., `fluxcd/flux`. */
1061
1083
  spec?: Record<string, unknown>;
@@ -1068,7 +1090,8 @@ repository, e.g., `fluxcd/flux`. */
1068
1090
 
1069
1091
  export declare class ImageUpdateAutomation {
1070
1092
  constructor(props: {
1071
- metadata?: Record<string, unknown>;
1093
+ /** Standard object's metadata. */
1094
+ metadata?: ObjectMeta;
1072
1095
  /** ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation */
1073
1096
  spec?: Record<string, unknown>;
1074
1097
  });
@@ -1166,7 +1189,8 @@ export declare class Issuer {
1166
1189
  constructor(props: {
1167
1190
  /** Desired state of the Issuer resource. */
1168
1191
  spec: Record<string, unknown>;
1169
- metadata?: Record<string, unknown>;
1192
+ /** Standard object's metadata. */
1193
+ metadata?: ObjectMeta;
1170
1194
  });
1171
1195
  readonly name: string;
1172
1196
  readonly namespace: string;
@@ -1200,7 +1224,8 @@ export declare class JobList {
1200
1224
 
1201
1225
  export declare class Kustomization {
1202
1226
  constructor(props: {
1203
- metadata?: Record<string, unknown>;
1227
+ /** Standard object's metadata. */
1228
+ metadata?: ObjectMeta;
1204
1229
  /** KustomizationSpec defines the configuration to calculate the desired state
1205
1230
  from a Source using Kustomize. */
1206
1231
  spec?: Record<string, unknown>;
@@ -1295,6 +1320,65 @@ export declare class LocalSubjectAccessReview {
1295
1320
  readonly uid: string;
1296
1321
  }
1297
1322
 
1323
+ export declare class MicroVM {
1324
+ constructor(props: {
1325
+ /** Standard object's metadata. */
1326
+ metadata?: ObjectMeta;
1327
+ spec?: Record<string, unknown>;
1328
+ });
1329
+ readonly name: string;
1330
+ readonly namespace: string;
1331
+ readonly status: Record<string, unknown>;
1332
+ readonly uid: string;
1333
+ }
1334
+
1335
+ export declare class MicroVMClass {
1336
+ constructor(props: {
1337
+ /** Standard object's metadata. */
1338
+ metadata?: ObjectMeta;
1339
+ spec?: Record<string, unknown>;
1340
+ });
1341
+ readonly name: string;
1342
+ readonly namespace: string;
1343
+ readonly uid: string;
1344
+ }
1345
+
1346
+ export declare class MicroVMImage {
1347
+ constructor(props: {
1348
+ /** Standard object's metadata. */
1349
+ metadata?: ObjectMeta;
1350
+ spec?: Record<string, unknown>;
1351
+ });
1352
+ readonly name: string;
1353
+ readonly namespace: string;
1354
+ readonly status: Record<string, unknown>;
1355
+ readonly uid: string;
1356
+ }
1357
+
1358
+ export declare class MicroVMNetwork {
1359
+ constructor(props: {
1360
+ /** Standard object's metadata. */
1361
+ metadata?: ObjectMeta;
1362
+ spec?: Record<string, unknown>;
1363
+ });
1364
+ readonly name: string;
1365
+ readonly namespace: string;
1366
+ readonly status: Record<string, unknown>;
1367
+ readonly uid: string;
1368
+ }
1369
+
1370
+ export declare class MicroVMReplicaSet {
1371
+ constructor(props: {
1372
+ /** Standard object's metadata. */
1373
+ metadata?: ObjectMeta;
1374
+ spec?: Record<string, unknown>;
1375
+ });
1376
+ readonly name: string;
1377
+ readonly namespace: string;
1378
+ readonly status: Record<string, unknown>;
1379
+ readonly uid: string;
1380
+ }
1381
+
1298
1382
  export declare class MutatingAdmissionPolicy {
1299
1383
  constructor(props: {
1300
1384
  /** metadata is the standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata. */
@@ -1465,7 +1549,8 @@ export declare class NS {
1465
1549
 
1466
1550
  export declare class OCIRepository {
1467
1551
  constructor(props: {
1468
- metadata?: Record<string, unknown>;
1552
+ /** Standard object's metadata. */
1553
+ metadata?: ObjectMeta;
1469
1554
  /** OCIRepositorySpec defines the desired state of OCIRepository */
1470
1555
  spec?: Record<string, unknown>;
1471
1556
  });
@@ -1477,8 +1562,9 @@ export declare class OCIRepository {
1477
1562
 
1478
1563
  export declare class Order {
1479
1564
  constructor(props: {
1480
- metadata: Record<string, unknown>;
1481
1565
  spec: Record<string, unknown>;
1566
+ /** Standard object's metadata. */
1567
+ metadata?: ObjectMeta;
1482
1568
  });
1483
1569
  readonly name: string;
1484
1570
  readonly namespace: string;
@@ -1700,7 +1786,8 @@ export declare class PrometheusRule {
1700
1786
  constructor(props: {
1701
1787
  /** Specification of desired alerting rule definitions for Prometheus. */
1702
1788
  spec: Record<string, unknown>;
1703
- metadata?: Record<string, unknown>;
1789
+ /** Standard object's metadata. */
1790
+ metadata?: ObjectMeta;
1704
1791
  });
1705
1792
  readonly name: string;
1706
1793
  readonly namespace: string;
@@ -1709,7 +1796,8 @@ export declare class PrometheusRule {
1709
1796
 
1710
1797
  export declare class Provider {
1711
1798
  constructor(props: {
1712
- metadata?: Record<string, unknown>;
1799
+ /** Standard object's metadata. */
1800
+ metadata?: ObjectMeta;
1713
1801
  /** ProviderSpec defines the desired state of the Provider. */
1714
1802
  spec?: Record<string, unknown>;
1715
1803
  });
@@ -1744,7 +1832,8 @@ export declare class PVC {
1744
1832
 
1745
1833
  export declare class RayCluster {
1746
1834
  constructor(props: {
1747
- metadata?: Record<string, unknown>;
1835
+ /** Standard object's metadata. */
1836
+ metadata?: ObjectMeta;
1748
1837
  spec?: Record<string, unknown>;
1749
1838
  });
1750
1839
  readonly name: string;
@@ -1755,7 +1844,8 @@ export declare class RayCluster {
1755
1844
 
1756
1845
  export declare class RayJob {
1757
1846
  constructor(props: {
1758
- metadata?: Record<string, unknown>;
1847
+ /** Standard object's metadata. */
1848
+ metadata?: ObjectMeta;
1759
1849
  spec?: Record<string, unknown>;
1760
1850
  });
1761
1851
  readonly name: string;
@@ -1766,7 +1856,8 @@ export declare class RayJob {
1766
1856
 
1767
1857
  export declare class RayService {
1768
1858
  constructor(props: {
1769
- metadata?: Record<string, unknown>;
1859
+ /** Standard object's metadata. */
1860
+ metadata?: ObjectMeta;
1770
1861
  spec?: Record<string, unknown>;
1771
1862
  });
1772
1863
  readonly name: string;
@@ -1777,7 +1868,8 @@ export declare class RayService {
1777
1868
 
1778
1869
  export declare class Receiver {
1779
1870
  constructor(props: {
1780
- metadata?: Record<string, unknown>;
1871
+ /** Standard object's metadata. */
1872
+ metadata?: ObjectMeta;
1781
1873
  /** ReceiverSpec defines the desired state of the Receiver. */
1782
1874
  spec?: Record<string, unknown>;
1783
1875
  });
@@ -1789,7 +1881,8 @@ export declare class Receiver {
1789
1881
 
1790
1882
  export declare class ReferenceGrant {
1791
1883
  constructor(props: {
1792
- metadata?: Record<string, unknown>;
1884
+ /** Standard object's metadata. */
1885
+ metadata?: ObjectMeta;
1793
1886
  /** Spec defines the desired state of ReferenceGrant. */
1794
1887
  spec?: Record<string, unknown>;
1795
1888
  });
@@ -1922,7 +2015,8 @@ export declare class ResourceQuotaList {
1922
2015
 
1923
2016
  export declare class ResourceSet {
1924
2017
  constructor(props: {
1925
- metadata?: Record<string, unknown>;
2018
+ /** Standard object's metadata. */
2019
+ metadata?: ObjectMeta;
1926
2020
  /** ResourceSetSpec defines the desired state of ResourceSet */
1927
2021
  spec?: Record<string, unknown>;
1928
2022
  });
@@ -1934,7 +2028,8 @@ export declare class ResourceSet {
1934
2028
 
1935
2029
  export declare class ResourceSetInputProvider {
1936
2030
  constructor(props: {
1937
- metadata?: Record<string, unknown>;
2031
+ /** Standard object's metadata. */
2032
+ metadata?: ObjectMeta;
1938
2033
  /** ResourceSetInputProviderSpec defines the desired state of ResourceSetInputProvider */
1939
2034
  spec?: Record<string, unknown>;
1940
2035
  });
@@ -2240,7 +2335,8 @@ export declare class ServiceMonitor {
2240
2335
  /** Specification of desired Service selection for target discovery by
2241
2336
  Prometheus. */
2242
2337
  spec: Record<string, unknown>;
2243
- metadata?: Record<string, unknown>;
2338
+ /** Standard object's metadata. */
2339
+ metadata?: ObjectMeta;
2244
2340
  });
2245
2341
  readonly name: string;
2246
2342
  readonly namespace: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-k8s",
3
- "version": "0.34.1",
3
+ "version": "0.36.0",
4
4
  "description": "Kubernetes lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -1151,6 +1151,13 @@ The lexicon also provides MCP (Model Context Protocol) tools and resources:
1151
1151
  ],
1152
1152
  basePath: "/chant/lexicons/k8s/",
1153
1153
  sidebarExtra: [
1154
+ {
1155
+ label: "Live Cluster",
1156
+ items: [
1157
+ { label: "The API Client", slug: "api-client" },
1158
+ { label: "chant kube", slug: "kube" },
1159
+ ],
1160
+ },
1154
1161
  { label: "CRD-Generated Classes", slug: "crd-classes" },
1155
1162
  { label: "Argo CD Composites", slug: "argo-composites" },
1156
1163
  {
@@ -150,6 +150,28 @@ const FLUX_TOOLKIT_INSTALL = `https://github.com/fluxcd/flux2/releases/download/
150
150
  const FLUX_OPERATOR_VERSION = "v0.54.1";
151
151
  const FLUX_OPERATOR_INSTALL = `https://github.com/controlplaneio-fluxcd/flux-operator/releases/download/${FLUX_OPERATOR_VERSION}/install.yaml`;
152
152
 
153
+ /**
154
+ * KubeMicroVM CRDs — lambda.aws.amazon.com/v1alpha1
155
+ *
156
+ * Produces (the group is mapped to the `KubeMicroVM` namespace — see
157
+ * GROUP_NAMESPACE_OVERRIDES in crd/parser.ts):
158
+ * K8s::KubeMicroVM::MicroVM → kind: MicroVM
159
+ * K8s::KubeMicroVM::MicroVMImage → kind: MicroVMImage
160
+ * K8s::KubeMicroVM::MicroVMNetwork → kind: MicroVMNetwork
161
+ * K8s::KubeMicroVM::MicroVMClass → kind: MicroVMClass
162
+ * K8s::KubeMicroVM::MicroVMReplicaSet → kind: MicroVMReplicaSet
163
+ *
164
+ * Sourced from the chart rather than a raw URL: the operator is built with
165
+ * the Java operator SDK, which generates CRDs at build time, and only
166
+ * `microvmclasses` is committed to their repo. All five are in the published
167
+ * chart.
168
+ *
169
+ * Operator install: helm install kube-microvm-operator
170
+ * oci://ghcr.io/codriverlabs/helm/kube-microvm-operator --version 1.0.11
171
+ */
172
+ const KUBEMICROVM_CHART = "oci://ghcr.io/codriverlabs/helm/kube-microvm-operator";
173
+ const KUBEMICROVM_VERSION = "1.0.11";
174
+
153
175
  export const CRD_SOURCES: CRDSource[] = [
154
176
  { type: "url", url: `${KUBERAY_CRD_BASE}/ray.io_rayclusters.yaml` },
155
177
  { type: "url", url: `${KUBERAY_CRD_BASE}/ray.io_rayjobs.yaml` },
@@ -190,4 +212,10 @@ export const CRD_SOURCES: CRDSource[] = [
190
212
  url: FLUX_OPERATOR_INSTALL,
191
213
  kinds: ["FluxInstance", "FluxReport", "ResourceSet", "ResourceSetInputProvider"],
192
214
  },
215
+ {
216
+ type: "helm",
217
+ chart: KUBEMICROVM_CHART,
218
+ version: KUBEMICROVM_VERSION,
219
+ kinds: ["MicroVM", "MicroVMImage", "MicroVMNetwork", "MicroVMClass", "MicroVMReplicaSet"],
220
+ },
193
221
  ];
@@ -58,3 +58,25 @@ describe("loadCRDs kinds allowlist", () => {
58
58
  expect(all.map((r) => r.gvk.kind).sort()).toEqual(["ExternalArtifact", "GitRepository"]);
59
59
  });
60
60
  });
61
+
62
+ describe("helm CRD source", () => {
63
+ test("requires a chart", async () => {
64
+ await expect(loadCRDs({ type: "helm", version: "1.0.0" })).rejects.toThrow(
65
+ /requires a 'chart' property/,
66
+ );
67
+ });
68
+
69
+ // An unpinned chart makes generated output depend on the day it was
70
+ // generated, which is the property every other source here preserves.
71
+ test("requires a version", async () => {
72
+ await expect(loadCRDs({ type: "helm", chart: "oci://example.invalid/c" })).rejects.toThrow(
73
+ /requires a 'version' property/,
74
+ );
75
+ });
76
+
77
+ test("says what to do when helm is missing or the pull fails", async () => {
78
+ await expect(
79
+ loadCRDs({ type: "helm", chart: "oci://example.invalid/nope", version: "0.0.1" }),
80
+ ).rejects.toThrow(/helm (pull failed|not found on PATH)/);
81
+ });
82
+ });
package/src/crd/loader.ts CHANGED
@@ -47,6 +47,8 @@ async function fetchCRDContent(source: CRDSource): Promise<string> {
47
47
  return loadFromURL(source);
48
48
  case "cluster":
49
49
  return loadFromCluster(source);
50
+ case "helm":
51
+ return loadFromHelmChart(source);
50
52
  default:
51
53
  throw new Error(`Unsupported CRD source type: ${(source as CRDSource).type}`);
52
54
  }
@@ -81,6 +83,87 @@ async function loadFromURL(source: CRDSource): Promise<string> {
81
83
  return response.text();
82
84
  }
83
85
 
86
+ /**
87
+ * Load CRDs out of a Helm chart, by pulling and unpacking it.
88
+ *
89
+ * Helm's own convention is that CRDs live in a chart's `crds/` directory and
90
+ * are applied before templates, so that is the default place to look. Every
91
+ * YAML document found there is concatenated into one multi-doc string, which
92
+ * the parser already handles, and the existing `kinds` allowlist then applies
93
+ * unchanged — a chart carrying more CRDs than a consumer wants is the same
94
+ * situation as Flux's install bundle.
95
+ *
96
+ * Needs the `helm` binary at generation time, the same shape of dependency
97
+ * that type="cluster" has on `kubectl`.
98
+ */
99
+ async function loadFromHelmChart(source: CRDSource): Promise<string> {
100
+ if (!source.chart) {
101
+ throw new Error("CRD source type 'helm' requires a 'chart' property");
102
+ }
103
+ if (!source.version) {
104
+ throw new Error(
105
+ `CRD source type 'helm' requires a 'version' property (chart: ${source.chart}). ` +
106
+ "An unpinned chart makes generated output depend on when it was generated.",
107
+ );
108
+ }
109
+
110
+ const { execFile } = await import("child_process");
111
+ const { promisify } = await import("util");
112
+ const { mkdtempSync, rmSync, readdirSync, readFileSync: read } = await import("fs");
113
+ const { join } = await import("path");
114
+ const { tmpdir } = await import("os");
115
+ const execFileAsync = promisify(execFile);
116
+
117
+ const workdir = mkdtempSync(join(tmpdir(), "chant-crd-helm-"));
118
+ try {
119
+ await execFileAsync("helm", [
120
+ "pull", source.chart,
121
+ "--version", source.version,
122
+ "--untar",
123
+ "--untardir", workdir,
124
+ ]).catch((err: NodeJS.ErrnoException & { stderr?: string }) => {
125
+ if (err.code === "ENOENT") {
126
+ throw new Error(
127
+ `helm not found on PATH, needed to read CRDs from ${source.chart}. ` +
128
+ "Install helm, or vendor the CRDs and use a 'file' source.",
129
+ );
130
+ }
131
+ throw new Error(
132
+ `helm pull failed for ${source.chart} ${source.version}: ${err.stderr?.trim() || err.message}`,
133
+ );
134
+ });
135
+
136
+ // --untar writes a single directory named after the chart, which need not
137
+ // match the last segment of the reference, so read it rather than guess.
138
+ const unpacked = readdirSync(workdir, { withFileTypes: true }).filter((e) => e.isDirectory());
139
+ if (unpacked.length !== 1) {
140
+ throw new Error(
141
+ `expected one unpacked chart directory in ${workdir}, found ${unpacked.length}`,
142
+ );
143
+ }
144
+
145
+ const subdir = source.chartSubdir ?? "crds";
146
+ const crdDir = join(workdir, unpacked[0].name, subdir);
147
+
148
+ let entries: string[];
149
+ try {
150
+ entries = readdirSync(crdDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).sort();
151
+ } catch {
152
+ throw new Error(
153
+ `chart ${source.chart} ${source.version} has no '${subdir}' directory. ` +
154
+ "Charts that template their CRDs instead of shipping them in crds/ need 'chartSubdir'.",
155
+ );
156
+ }
157
+ if (entries.length === 0) {
158
+ throw new Error(`chart ${source.chart} ${source.version} has no CRD YAML in '${subdir}'`);
159
+ }
160
+
161
+ return entries.map((f) => read(join(crdDir, f), "utf8")).join("\n---\n");
162
+ } finally {
163
+ rmSync(workdir, { recursive: true, force: true });
164
+ }
165
+ }
166
+
84
167
  /**
85
168
  * Load CRDs from a live Kubernetes cluster via kubectl.
86
169
  *
@@ -201,6 +201,63 @@ describe("parseCRDSpec", () => {
201
201
  expect(props.some((p) => p.name === "metadata")).toBe(true);
202
202
  });
203
203
 
204
+ test("emits metadata even when the schema declares only spec and status", () => {
205
+ // The shape the Fabric8 CRDGenerator produces, and what KubeMicroVM's five
206
+ // CRDs ship — no `metadata` in openAPIV3Schema at all. Without this the
207
+ // generated class had no way to set a name or a namespace.
208
+ const spec = {
209
+ group: "lambda.aws.amazon.com",
210
+ names: { kind: "MicroVM", plural: "microvms" },
211
+ scope: "Namespaced" as const,
212
+ versions: [
213
+ {
214
+ name: "v1alpha1",
215
+ served: true,
216
+ storage: true,
217
+ schema: {
218
+ openAPIV3Schema: {
219
+ type: "object",
220
+ properties: {
221
+ spec: { type: "object", properties: { imageRef: { type: "string" } } },
222
+ status: { type: "object", properties: { state: { type: "string" } } },
223
+ },
224
+ },
225
+ },
226
+ },
227
+ ],
228
+ };
229
+
230
+ const props = parseCRDSpec(spec)[0].resource.properties;
231
+ const metadata = props.find((p) => p.name === "metadata");
232
+ expect(metadata?.tsType).toBe("ObjectMeta");
233
+ expect(metadata?.required).toBe(false);
234
+ });
235
+
236
+ test("a schema-declared metadata does not produce a duplicate", () => {
237
+ const spec = {
238
+ group: "cert-manager.io",
239
+ names: { kind: "Certificate", plural: "certificates" },
240
+ scope: "Namespaced" as const,
241
+ versions: [
242
+ {
243
+ name: "v1",
244
+ served: true,
245
+ storage: true,
246
+ schema: {
247
+ openAPIV3Schema: {
248
+ type: "object",
249
+ properties: { metadata: { type: "object" }, spec: { type: "object" } },
250
+ },
251
+ },
252
+ },
253
+ ],
254
+ };
255
+
256
+ const metadataProps = parseCRDSpec(spec)[0].resource.properties.filter((p) => p.name === "metadata");
257
+ expect(metadataProps.length).toBe(1);
258
+ expect(metadataProps[0].tsType).toBe("ObjectMeta");
259
+ });
260
+
204
261
  test("normalizeGroupName converts cert-manager.io to CertManager", () => {
205
262
  const spec = {
206
263
  group: "cert-manager.io",