@intentius/chant-lexicon-k8s 0.38.0 → 0.41.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.
Files changed (152) hide show
  1. package/dist/codegen/generate-list-map-keys.d.ts +53 -0
  2. package/dist/codegen/generate-list-map-keys.d.ts.map +1 -0
  3. package/dist/codegen/generate.d.ts.map +1 -1
  4. package/dist/composites/adot-collector.d.ts +7 -1
  5. package/dist/composites/adot-collector.d.ts.map +1 -1
  6. package/dist/composites/agic-ingress.d.ts +3 -1
  7. package/dist/composites/agic-ingress.d.ts.map +1 -1
  8. package/dist/composites/aks-external-dns-agent.d.ts +6 -1
  9. package/dist/composites/aks-external-dns-agent.d.ts.map +1 -1
  10. package/dist/composites/alb-ingress.d.ts +3 -1
  11. package/dist/composites/alb-ingress.d.ts.map +1 -1
  12. package/dist/composites/autoscaled-service.d.ts +6 -1
  13. package/dist/composites/autoscaled-service.d.ts.map +1 -1
  14. package/dist/composites/azure-disk-storage-class.d.ts +3 -1
  15. package/dist/composites/azure-disk-storage-class.d.ts.map +1 -1
  16. package/dist/composites/azure-file-storage-class.d.ts +3 -1
  17. package/dist/composites/azure-file-storage-class.d.ts.map +1 -1
  18. package/dist/composites/azure-monitor-collector.d.ts +7 -1
  19. package/dist/composites/azure-monitor-collector.d.ts.map +1 -1
  20. package/dist/composites/batch-job.d.ts +1 -1
  21. package/dist/composites/batch-job.d.ts.map +1 -1
  22. package/dist/composites/cockroachdb-cluster.d.ts +13 -1
  23. package/dist/composites/cockroachdb-cluster.d.ts.map +1 -1
  24. package/dist/composites/config-connector-context.d.ts +3 -1
  25. package/dist/composites/config-connector-context.d.ts.map +1 -1
  26. package/dist/composites/configured-app.d.ts +1 -1
  27. package/dist/composites/configured-app.d.ts.map +1 -1
  28. package/dist/composites/cron-workload.d.ts +6 -1
  29. package/dist/composites/cron-workload.d.ts.map +1 -1
  30. package/dist/composites/ebs-storage-class.d.ts +3 -1
  31. package/dist/composites/ebs-storage-class.d.ts.map +1 -1
  32. package/dist/composites/efs-storage-class.d.ts +3 -1
  33. package/dist/composites/efs-storage-class.d.ts.map +1 -1
  34. package/dist/composites/external-dns-agent.d.ts +6 -1
  35. package/dist/composites/external-dns-agent.d.ts.map +1 -1
  36. package/dist/composites/filestore-storage-class.d.ts +3 -1
  37. package/dist/composites/filestore-storage-class.d.ts.map +1 -1
  38. package/dist/composites/fluent-bit-agent.d.ts +7 -1
  39. package/dist/composites/fluent-bit-agent.d.ts.map +1 -1
  40. package/dist/composites/gce-ingress.d.ts +3 -1
  41. package/dist/composites/gce-ingress.d.ts.map +1 -1
  42. package/dist/composites/gce-pd-storage-class.d.ts +3 -1
  43. package/dist/composites/gce-pd-storage-class.d.ts.map +1 -1
  44. package/dist/composites/gke-external-dns-agent.d.ts +6 -1
  45. package/dist/composites/gke-external-dns-agent.d.ts.map +1 -1
  46. package/dist/composites/gke-fluent-bit-agent.d.ts +7 -1
  47. package/dist/composites/gke-fluent-bit-agent.d.ts.map +1 -1
  48. package/dist/composites/gke-gateway.d.ts +4 -1
  49. package/dist/composites/gke-gateway.d.ts.map +1 -1
  50. package/dist/composites/gke-otel-collector.d.ts +7 -1
  51. package/dist/composites/gke-otel-collector.d.ts.map +1 -1
  52. package/dist/composites/irsa-service-account.d.ts +1 -1
  53. package/dist/composites/irsa-service-account.d.ts.map +1 -1
  54. package/dist/composites/metrics-server.d.ts +10 -1
  55. package/dist/composites/metrics-server.d.ts.map +1 -1
  56. package/dist/composites/monitored-service.d.ts +1 -1
  57. package/dist/composites/monitored-service.d.ts.map +1 -1
  58. package/dist/composites/namespace-env.d.ts +1 -1
  59. package/dist/composites/namespace-env.d.ts.map +1 -1
  60. package/dist/composites/network-isolated-app.d.ts +5 -1
  61. package/dist/composites/network-isolated-app.d.ts.map +1 -1
  62. package/dist/composites/node-agent.d.ts +1 -1
  63. package/dist/composites/node-agent.d.ts.map +1 -1
  64. package/dist/composites/ray-cluster.d.ts +10 -1
  65. package/dist/composites/ray-cluster.d.ts.map +1 -1
  66. package/dist/composites/ray-job.d.ts +8 -1
  67. package/dist/composites/ray-job.d.ts.map +1 -1
  68. package/dist/composites/ray-service.d.ts +10 -1
  69. package/dist/composites/ray-service.d.ts.map +1 -1
  70. package/dist/composites/secure-ingress.d.ts +1 -1
  71. package/dist/composites/secure-ingress.d.ts.map +1 -1
  72. package/dist/composites/sidecar-app.d.ts +4 -1
  73. package/dist/composites/sidecar-app.d.ts.map +1 -1
  74. package/dist/composites/stateful-app.d.ts +1 -1
  75. package/dist/composites/stateful-app.d.ts.map +1 -1
  76. package/dist/composites/web-app.d.ts +1 -1
  77. package/dist/composites/web-app.d.ts.map +1 -1
  78. package/dist/composites/worker-pool.d.ts +1 -1
  79. package/dist/composites/worker-pool.d.ts.map +1 -1
  80. package/dist/composites/workload-identity-sa.d.ts +1 -1
  81. package/dist/composites/workload-identity-sa.d.ts.map +1 -1
  82. package/dist/composites/workload-identity-service-account.d.ts +1 -1
  83. package/dist/composites/workload-identity-service-account.d.ts.map +1 -1
  84. package/dist/config-schema.d.ts +40 -0
  85. package/dist/config-schema.d.ts.map +1 -0
  86. package/dist/config.d.ts +22 -1
  87. package/dist/config.d.ts.map +1 -1
  88. package/dist/deep-observe-hooks.d.ts +15 -18
  89. package/dist/deep-observe-hooks.d.ts.map +1 -1
  90. package/dist/deep-observe.d.ts.map +1 -1
  91. package/dist/describe-resources.d.ts +1 -0
  92. package/dist/describe-resources.d.ts.map +1 -1
  93. package/dist/integrity.json +2 -2
  94. package/dist/manifest.json +1 -1
  95. package/dist/plugin.d.ts.map +1 -1
  96. package/dist/spec/parse.d.ts +17 -0
  97. package/dist/spec/parse.d.ts.map +1 -1
  98. package/package.json +3 -2
  99. package/src/codegen/generate-list-map-keys.test.ts +112 -0
  100. package/src/codegen/generate-list-map-keys.ts +99 -0
  101. package/src/codegen/generate.ts +9 -1
  102. package/src/composites/adot-collector.ts +1 -1
  103. package/src/composites/agic-ingress.ts +1 -1
  104. package/src/composites/aks-external-dns-agent.ts +1 -1
  105. package/src/composites/alb-ingress.ts +1 -1
  106. package/src/composites/autoscaled-service.ts +1 -1
  107. package/src/composites/azure-disk-storage-class.ts +1 -1
  108. package/src/composites/azure-file-storage-class.ts +1 -1
  109. package/src/composites/azure-monitor-collector.ts +1 -1
  110. package/src/composites/batch-job.ts +1 -1
  111. package/src/composites/cockroachdb-cluster.ts +1 -1
  112. package/src/composites/config-connector-context.ts +1 -1
  113. package/src/composites/configured-app.ts +1 -1
  114. package/src/composites/cron-workload.ts +1 -1
  115. package/src/composites/ebs-storage-class.ts +1 -1
  116. package/src/composites/efs-storage-class.ts +1 -1
  117. package/src/composites/external-dns-agent.ts +1 -1
  118. package/src/composites/filestore-storage-class.ts +1 -1
  119. package/src/composites/fluent-bit-agent.ts +1 -1
  120. package/src/composites/gce-ingress.ts +1 -1
  121. package/src/composites/gce-pd-storage-class.ts +1 -1
  122. package/src/composites/gke-external-dns-agent.ts +1 -1
  123. package/src/composites/gke-fluent-bit-agent.ts +1 -1
  124. package/src/composites/gke-gateway.ts +1 -1
  125. package/src/composites/gke-otel-collector.ts +1 -1
  126. package/src/composites/irsa-service-account.ts +1 -1
  127. package/src/composites/metrics-server.ts +1 -1
  128. package/src/composites/monitored-service.ts +1 -1
  129. package/src/composites/namespace-env.ts +1 -1
  130. package/src/composites/network-isolated-app.ts +1 -1
  131. package/src/composites/node-agent.ts +1 -1
  132. package/src/composites/ray-cluster.ts +1 -1
  133. package/src/composites/ray-job.ts +1 -1
  134. package/src/composites/ray-service.ts +1 -1
  135. package/src/composites/secure-ingress.ts +1 -1
  136. package/src/composites/sidecar-app.ts +1 -1
  137. package/src/composites/stateful-app.ts +1 -1
  138. package/src/composites/web-app.ts +1 -1
  139. package/src/composites/worker-pool.ts +1 -1
  140. package/src/composites/workload-identity-sa.ts +1 -1
  141. package/src/composites/workload-identity-service-account.ts +1 -1
  142. package/src/config-schema.ts +46 -0
  143. package/src/config.ts +25 -1
  144. package/src/deep-observe-hooks.ts +90 -10
  145. package/src/deep-observe.ts +7 -0
  146. package/src/describe-resources.test.ts +138 -1
  147. package/src/describe-resources.ts +98 -0
  148. package/src/generated/list-map-keys.json +166 -0
  149. package/src/list-map-order-key.test.ts +119 -0
  150. package/src/plugin.ts +2 -0
  151. package/src/spec/parse.test.ts +42 -0
  152. package/src/spec/parse.ts +51 -2
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "e1b9c06ba0f93f43759899498d03e5d7b897feffa6fa6eb46c37d0b3efecb540",
4
+ "manifest.json": "4c4e0afb787bdb40c3ecae9bcb1018002a85bcd41dcbf5ac12478d1ba092c3d6",
5
5
  "meta.json": "c0b141b882c51483c1d1aead748a0eb171aceca7432edd2a0404b062bf6d7831",
6
6
  "types/index.d.ts": "3be2af7494aca94adfa16269cb9c4e7a155529872207a3d70b0c4d39914f9eec",
7
7
  "rules/argo-appset-single-project.ts": "afa9f310753aa2d475f35012b13135b2dfaebed2a35d44edbe185ebc07673674",
@@ -50,5 +50,5 @@
50
50
  "skills/chant-k8s-aks.md": "e18f0e2b055f72cd7a37deaf258d7027c2d4d3e286e8fd4975b27a1f981a3ad9",
51
51
  "skills/chant-k8s-argo.md": "b1a0b826559d8c5033a479c5781efaf650320f0aee4419d8841170bd3393cea5"
52
52
  },
53
- "composite": "a5edc9fe263c66fd77cc3306db813050c864e270564bcd50b0e1a1ae9109c84f"
53
+ "composite": "5f8423c25fc4a50e91f0e7f2a8a8064fc8cc36b150f4b0c630a6cc56e0924995"
54
54
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "k8s",
3
- "version": "0.38.0",
3
+ "version": "0.41.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "K8s",
6
6
  "intrinsics": [],
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAqC,MAAM,0BAA0B,CAAC;AAuBjG,eAAO,MAAM,SAAS,EAAE,aA4mBvB,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAqC,MAAM,0BAA0B,CAAC;AAwBjG,eAAO,MAAM,SAAS,EAAE,aA6mBvB,CAAC"}
@@ -15,6 +15,10 @@ export interface ParsedProperty {
15
15
  description?: string;
16
16
  enum?: string[];
17
17
  constraints: PropertyConstraints;
18
+ /** `x-kubernetes-list-type` — `map`, `set` or `atomic`. Absent when the spec does not say (chant #1441). */
19
+ listType?: string;
20
+ /** `x-kubernetes-list-map-keys` — the fields that jointly identify one element. Present only alongside `listType: "map"`. */
21
+ listMapKeys?: string[];
18
22
  }
19
23
  export interface ParsedPropertyType {
20
24
  name: string;
@@ -81,6 +85,19 @@ type SwaggerPathItem = Record<string, SwaggerOperation | unknown>;
81
85
  */
82
86
  export declare function parseK8sSwagger(data: string | Buffer): K8sParseResult[];
83
87
  /** Stable key for a GVK, used to join the `paths` pass onto the `definitions` pass. */
88
+ /**
89
+ * Every `(property name, list-map keys)` pair the spec declares, across ALL
90
+ * definitions (chant #1441).
91
+ *
92
+ * Deliberately independent of {@link parseK8sSwagger}'s resource/property-type
93
+ * extraction. That extraction keeps 257 resources and 320 property types out
94
+ * of roughly a thousand definitions, so collecting merge keys from its output
95
+ * alone finds 18 of the 32 properties the spec annotates — and misses
96
+ * `ServicePort.port`, whose absence would silently demote every Service port
97
+ * list to the hand-written fallback. A definition is worth reading for its
98
+ * merge semantics whether or not chant emits a type for it.
99
+ */
100
+ export declare function specListMapKeyPairs(data: string | Buffer): Array<[string, string[]]>;
84
101
  export declare function gvkKey(gvk: GroupVersionKind): string;
85
102
  /**
86
103
  * Derive the operation surface from the OpenAPI `paths` — chant #1074.
@@ -1 +1 @@
1
- {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/spec/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAShF,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,mBAAmB,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,YAAY,GAAG,SAAS,CAAC;IAChC,8EAA8E;IAC9E,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,cAAc,CAAC;IACzB,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,GAAG,EAAE,gBAAgB,CAAC;IACtB,uEAAuE;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sEAAsE;IACtE,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B;AAkCD,UAAU,gBAAgB;IACxB,iCAAiC,CAAC,EAAE,gBAAgB,CAAC;IACrD,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC;AA+DlE;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,cAAc,EAAE,CAoCvE;AAED,uFAAuF;AACvF,wBAAgB,MAAM,CAAC,GAAG,EAAE,gBAAgB,GAAG,MAAM,CAEpD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAqChH;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,gBAAgB,GAAG,MAAM,CAG3D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,gBAAgB,GAAG,MAAM,CAK7D;AA6PD;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGvD"}
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.38.0",
3
+ "version": "0.41.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",
@@ -78,7 +78,8 @@
78
78
  "typescript": "^5.9.3"
79
79
  },
80
80
  "peerDependencies": {
81
- "@intentius/chant": "^0.38.0",
81
+ "zod": "^4.3.6",
82
+ "@intentius/chant": "^0.41.0",
82
83
  "typescript": "^5.9.3"
83
84
  }
84
85
  }
@@ -0,0 +1,112 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { buildListMapKeyTable, parsedListMapKeyPairs, generateListMapKeysJSON } from "./generate-list-map-keys";
3
+ import type { K8sParseResult, ParsedProperty } from "../spec/parse";
4
+
5
+ /**
6
+ * chant #1441 — the table the drift engine reads instead of six hardcoded
7
+ * property names.
8
+ */
9
+
10
+ function prop(name: string, over: Partial<ParsedProperty> = {}): ParsedProperty {
11
+ return { name, tsType: "any[]", required: false, constraints: {}, ...over };
12
+ }
13
+
14
+ function result(properties: ParsedProperty[], propertyTypeProps: ParsedProperty[] = []): K8sParseResult {
15
+ return {
16
+ resource: { typeName: "K8s::Test", properties, attributes: [], deprecatedProperties: [] },
17
+ propertyTypes: propertyTypeProps.length
18
+ ? [{ name: "Nested", defType: "io.k8s.Nested", properties: propertyTypeProps }]
19
+ : [],
20
+ enums: [],
21
+ gvk: { group: "", version: "v1", kind: "Test" },
22
+ };
23
+ }
24
+
25
+ describe("buildListMapKeyTable", () => {
26
+ test("folds pairs into a by-name table with sorted key fields", () => {
27
+ expect(buildListMapKeyTable([["ports", ["protocol", "containerPort"]]])).toEqual({
28
+ ports: [["containerPort", "protocol"]],
29
+ });
30
+ });
31
+
32
+ test("keeps both variants when one name is keyed two ways", () => {
33
+ // The real case: container ports vs Service ports. Collapsing these would
34
+ // silently mis-identify one of them.
35
+ const table = buildListMapKeyTable([
36
+ ["ports", ["containerPort", "protocol"]],
37
+ ["ports", ["port", "protocol"]],
38
+ ]);
39
+ expect(table.ports).toEqual([
40
+ ["containerPort", "protocol"],
41
+ ["port", "protocol"],
42
+ ]);
43
+ });
44
+
45
+ test("deduplicates the same key set seen on many definitions", () => {
46
+ // `conditions` is keyed by `type` at 31 sites in v1.36.2.
47
+ const pairs: Array<[string, string[]]> = Array.from({ length: 31 }, () => ["conditions", ["type"]]);
48
+ expect(buildListMapKeyTable(pairs)).toEqual({ conditions: [["type"]] });
49
+ });
50
+
51
+ test("orders candidates longest-first, so a specific set is tried before a shorter one", () => {
52
+ const table = buildListMapKeyTable([
53
+ ["devices", ["driver"]],
54
+ ["devices", ["device", "driver", "pool", "shareID"]],
55
+ ]);
56
+ expect(table.devices[0]).toHaveLength(4);
57
+ });
58
+
59
+ test("merges independent sources", () => {
60
+ const table = buildListMapKeyTable([["containers", ["name"]]], [["conditions", ["type"]]]);
61
+ expect(Object.keys(table).sort()).toEqual(["conditions", "containers"]);
62
+ });
63
+
64
+ test("names are emitted in sorted order, so regeneration produces a stable file", () => {
65
+ const table = buildListMapKeyTable([
66
+ ["zeta", ["name"]],
67
+ ["alpha", ["name"]],
68
+ ]);
69
+ expect(Object.keys(table)).toEqual(["alpha", "zeta"]);
70
+ });
71
+ });
72
+
73
+ describe("parsedListMapKeyPairs", () => {
74
+ test("collects from resource properties and nested property types alike", () => {
75
+ const pairs = parsedListMapKeyPairs([
76
+ result(
77
+ [prop("conditions", { listType: "map", listMapKeys: ["type"] })],
78
+ [prop("containers", { listType: "map", listMapKeys: ["name"] })],
79
+ ),
80
+ ]);
81
+ expect(pairs).toEqual([
82
+ ["conditions", ["type"]],
83
+ ["containers", ["name"]],
84
+ ]);
85
+ });
86
+
87
+ test("ignores atomic and set lists, and map lists with no keys", () => {
88
+ const pairs = parsedListMapKeyPairs([
89
+ result([
90
+ prop("args", { listType: "atomic" }),
91
+ prop("finalizers", { listType: "set" }),
92
+ prop("broken", { listType: "map" }),
93
+ prop("plain"),
94
+ ]),
95
+ ]);
96
+ expect(pairs).toEqual([]);
97
+ });
98
+ });
99
+
100
+ describe("generateListMapKeysJSON", () => {
101
+ test("emits both sources and ends with a newline", () => {
102
+ const json = generateListMapKeysJSON(
103
+ [["ports", ["port", "protocol"]]],
104
+ [result([prop("myItems", { listType: "map", listMapKeys: ["id"] })])],
105
+ );
106
+ expect(json.endsWith("\n")).toBe(true);
107
+ expect(JSON.parse(json)).toEqual({
108
+ myItems: [["id"]],
109
+ ports: [["port", "protocol"]],
110
+ });
111
+ });
112
+ });
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Associative-list key table, derived from the spec rather than hardcoded
3
+ * (chant #1441).
4
+ *
5
+ * The Kubernetes OpenAPI spec states, per property, how the API server merges
6
+ * a list: `x-kubernetes-list-type: map` plus `x-kubernetes-list-map-keys`
7
+ * naming the fields that jointly identify one element. chant's drift engine
8
+ * needs exactly that to compare `containers` by name rather than by index.
9
+ * Before this, `k8sListMapOrderKey` (core's `managed-fields.ts`) hardcoded six
10
+ * properties; the spec annotates 32.
11
+ *
12
+ * ## Why the table is keyed by property name
13
+ *
14
+ * The consumer is `DeepNormalizationHooks.orderKey`, which receives an
15
+ * index-erased `pattern` (`spec.template.spec.containers`) and keys off its
16
+ * LAST segment. A by-name table matches that lookup exactly and needs no
17
+ * resolution of the definition graph at generation time.
18
+ *
19
+ * The cost is that two definitions declaring the same property name with
20
+ * different keys collapse into one entry. That is real but small, and it is
21
+ * represented rather than lost: each name maps to a LIST of candidate key
22
+ * sets, and the consumer picks the first whose fields are all present on the
23
+ * element. In v1.36.2 exactly one property needs it — `ports`, keyed
24
+ * `(containerPort, protocol)` on a container and `(port, protocol)` on a
25
+ * Service — which is the same dual shape the hardcoded implementation already
26
+ * special-cased by hand.
27
+ *
28
+ * ## Two sources
29
+ *
30
+ * The whole spec is read directly, because chant emits types for 257 of
31
+ * roughly a thousand definitions and the merge semantics of a definition are
32
+ * worth having whether or not it becomes a type. Reading only the parsed
33
+ * results finds 18 of the 32 properties and misses `ServicePort.port`.
34
+ *
35
+ * Parsed results are read too, because CRDs arrive that way — an operator CRD
36
+ * that declares these extensions contributes its keys for free.
37
+ */
38
+
39
+ import type { K8sParseResult, ParsedProperty } from "../spec/parse";
40
+
41
+ /** Property name → candidate key-field sets, most-specific first. */
42
+ export type ListMapKeyTable = Record<string, string[][]>;
43
+
44
+ function everyProperty(results: K8sParseResult[]): ParsedProperty[] {
45
+ const out: ParsedProperty[] = [];
46
+ for (const r of results) {
47
+ out.push(...r.resource.properties);
48
+ for (const pt of r.propertyTypes) out.push(...pt.properties);
49
+ }
50
+ return out;
51
+ }
52
+
53
+ /** The `(name, keys)` pairs carried on parsed properties — core types and every CRD. */
54
+ export function parsedListMapKeyPairs(results: K8sParseResult[]): Array<[string, string[]]> {
55
+ const pairs: Array<[string, string[]]> = [];
56
+ for (const p of everyProperty(results)) {
57
+ if (p.listType === "map" && p.listMapKeys?.length) pairs.push([p.name, p.listMapKeys]);
58
+ }
59
+ return pairs;
60
+ }
61
+
62
+ /**
63
+ * Fold `(name, keys)` pairs into a by-name table.
64
+ *
65
+ * Candidate sets are deduplicated by content and ordered longest-first, so a
66
+ * more specific key set is tried before a shorter one whose fields a longer
67
+ * element would also satisfy.
68
+ */
69
+ export function buildListMapKeyTable(...sources: Array<Array<[string, string[]]>>): ListMapKeyTable {
70
+ const byName = new Map<string, Map<string, string[]>>();
71
+
72
+ for (const pairs of sources) {
73
+ for (const [name, rawKeys] of pairs) {
74
+ const keys = [...rawKeys].sort();
75
+ const seen = byName.get(name) ?? new Map<string, string[]>();
76
+ seen.set(keys.join(" "), keys);
77
+ byName.set(name, seen);
78
+ }
79
+ }
80
+
81
+ const table: ListMapKeyTable = {};
82
+ for (const name of [...byName.keys()].sort()) {
83
+ const variants = byName.get(name);
84
+ if (!variants) continue;
85
+ const sets = [...variants.values()];
86
+ sets.sort((a, b) => b.length - a.length || a.join().localeCompare(b.join()));
87
+ table[name] = sets;
88
+ }
89
+ return table;
90
+ }
91
+
92
+ /** Serialize the `list-map-keys.json` generated artifact. */
93
+ export function generateListMapKeysJSON(
94
+ specPairs: Array<[string, string[]]>,
95
+ results: K8sParseResult[],
96
+ ): string {
97
+ const table = buildListMapKeyTable(specPairs, parsedListMapKeyPairs(results));
98
+ return `${JSON.stringify(table, null, 2)}\n`;
99
+ }
@@ -12,12 +12,13 @@ import {
12
12
  type GeneratePipelineConfig,
13
13
  } from "@intentius/chant/codegen/generate";
14
14
  import { fetchSchemas } from "../spec/fetch";
15
- import { parseK8sSwagger, k8sShortName, type K8sParseResult } from "../spec/parse";
15
+ import { parseK8sSwagger, specListMapKeyPairs, k8sShortName, type K8sParseResult } from "../spec/parse";
16
16
  import { loadMultipleCRDs } from "../crd/loader";
17
17
  import { CRD_SOURCES } from "../crd/crd-sources";
18
18
  import { NamingStrategy, propertyTypeName, extractDefName } from "./naming";
19
19
  import { generateLexiconJSON } from "./generate-lexicon";
20
20
  import { generateOperationsJSON } from "./generate-operations";
21
+ import { generateListMapKeysJSON } from "./generate-list-map-keys";
21
22
  import { generateTypeScriptDeclarations } from "./generate-typescript";
22
23
  import {
23
24
  generateRuntimeIndex as coreGenerateRuntimeIndex,
@@ -38,6 +39,9 @@ export interface K8sGenerateOptions extends GenerateOptions {
38
39
  export async function generate(opts: K8sGenerateOptions = {}): Promise<GenerateResult> {
39
40
  // Pipeline state captured in closure — no module-level mutation
40
41
  let pendingResults: K8sParseResult[] = [];
42
+ // chant #1441 — read off the raw document in `parseSchema`, since the
43
+ // result set covers only the definitions chant emits types for.
44
+ let specPairs: Array<[string, string[]]> = [];
41
45
 
42
46
  const config: GeneratePipelineConfig<K8sParseResult> = {
43
47
  fetchSchemas: async (fetchOpts) => {
@@ -49,6 +53,7 @@ export async function generate(opts: K8sGenerateOptions = {}): Promise<GenerateR
49
53
  // The pipeline calls this once per schema entry. We return the first result
50
54
  // and use augmentResults to inject the rest.
51
55
  const results = parseK8sSwagger(data);
56
+ specPairs = specListMapKeyPairs(data);
52
57
  if (results.length === 0) return null;
53
58
  // Return the first result; stash the rest for augmentResults
54
59
  pendingResults = results.slice(1);
@@ -105,6 +110,9 @@ export async function generate(opts: K8sGenerateOptions = {}): Promise<GenerateR
105
110
  // differently from how the declarable surface names it.
106
111
  generateExtraArtifacts: (results) => ({
107
112
  "operations.json": generateOperationsJSON(results),
113
+ // chant #1441 — the spec's own associative-list keys, so drift compares
114
+ // list elements by identity instead of by index.
115
+ "list-map-keys.json": generateListMapKeysJSON(specPairs, results),
108
116
  }),
109
117
  };
110
118
 
@@ -67,7 +67,7 @@ export interface AdotCollectorResult {
67
67
  * });
68
68
  * ```
69
69
  */
70
- export const AdotCollector = Composite<AdotCollectorProps>((props) => {
70
+ export const AdotCollector = Composite((props: AdotCollectorProps) => {
71
71
  const {
72
72
  region,
73
73
  clusterName,
@@ -75,7 +75,7 @@ export interface AgicIngressResult {
75
75
  * });
76
76
  * ```
77
77
  */
78
- export const AgicIngress = Composite<AgicIngressProps>((props) => {
78
+ export const AgicIngress = Composite((props: AgicIngressProps) => {
79
79
  const {
80
80
  name,
81
81
  hosts,
@@ -66,7 +66,7 @@ export interface AksExternalDnsAgentResult {
66
66
  * });
67
67
  * ```
68
68
  */
69
- export const AksExternalDnsAgent = Composite<AksExternalDnsAgentProps>((props) => {
69
+ export const AksExternalDnsAgent = Composite((props: AksExternalDnsAgentProps) => {
70
70
  const {
71
71
  clientId,
72
72
  resourceGroup,
@@ -79,7 +79,7 @@ export interface AlbIngressResult {
79
79
  * });
80
80
  * ```
81
81
  */
82
- export const AlbIngress = Composite<AlbIngressProps>((props) => {
82
+ export const AlbIngress = Composite((props: AlbIngressProps) => {
83
83
  const {
84
84
  name,
85
85
  hosts,
@@ -104,7 +104,7 @@ export interface AutoscaledServiceResult {
104
104
  * });
105
105
  * ```
106
106
  */
107
- export const AutoscaledService = Composite<AutoscaledServiceProps>((props) => {
107
+ export const AutoscaledService = Composite((props: AutoscaledServiceProps) => {
108
108
  const {
109
109
  name,
110
110
  image,
@@ -49,7 +49,7 @@ export interface AzureDiskStorageClassResult {
49
49
  * });
50
50
  * ```
51
51
  */
52
- export const AzureDiskStorageClass = Composite<AzureDiskStorageClassProps>((props) => {
52
+ export const AzureDiskStorageClass = Composite((props: AzureDiskStorageClassProps) => {
53
53
  const {
54
54
  name,
55
55
  skuName = "Premium_LRS",
@@ -47,7 +47,7 @@ export interface AzureFileStorageClassResult {
47
47
  * });
48
48
  * ```
49
49
  */
50
- export const AzureFileStorageClass = Composite<AzureFileStorageClassProps>((props) => {
50
+ export const AzureFileStorageClass = Composite((props: AzureFileStorageClassProps) => {
51
51
  const {
52
52
  name,
53
53
  skuName = "Premium_LRS",
@@ -64,7 +64,7 @@ export interface AzureMonitorCollectorResult {
64
64
  * });
65
65
  * ```
66
66
  */
67
- export const AzureMonitorCollector = Composite<AzureMonitorCollectorProps>((props) => {
67
+ export const AzureMonitorCollector = Composite((props: AzureMonitorCollectorProps) => {
68
68
  const {
69
69
  workspaceId,
70
70
  clusterName,
@@ -103,7 +103,7 @@ export interface BatchJobResult {
103
103
  * });
104
104
  * ```
105
105
  */
106
- export const BatchJob = Composite<BatchJobProps>((props) => {
106
+ export const BatchJob = Composite((props: BatchJobProps) => {
107
107
  const {
108
108
  name,
109
109
  image,
@@ -133,7 +133,7 @@ export interface CockroachDbClusterResult {
133
133
  * });
134
134
  * ```
135
135
  */
136
- export const CockroachDbCluster = Composite<CockroachDbClusterProps>((props) => {
136
+ export const CockroachDbCluster = Composite((props: CockroachDbClusterProps) => {
137
137
  const {
138
138
  name,
139
139
  namespace,
@@ -42,7 +42,7 @@ export interface ConfigConnectorContextResult {
42
42
  * });
43
43
  * ```
44
44
  */
45
- export const ConfigConnectorContext = Composite<ConfigConnectorContextProps>((props) => {
45
+ export const ConfigConnectorContext = Composite((props: ConfigConnectorContextProps) => {
46
46
  const {
47
47
  name = "configconnectorcontext.core.cnrm.cloud.google.com",
48
48
  googleServiceAccountEmail,
@@ -89,7 +89,7 @@ export interface ConfiguredAppResult {
89
89
  * });
90
90
  * ```
91
91
  */
92
- export const ConfiguredApp = Composite<ConfiguredAppProps>((props) => {
92
+ export const ConfiguredApp = Composite((props: ConfiguredAppProps) => {
93
93
  const {
94
94
  name,
95
95
  image,
@@ -74,7 +74,7 @@ export interface CronWorkloadResult {
74
74
  * });
75
75
  * ```
76
76
  */
77
- export const CronWorkload = Composite<CronWorkloadProps>((props) => {
77
+ export const CronWorkload = Composite((props: CronWorkloadProps) => {
78
78
  const {
79
79
  name,
80
80
  image,
@@ -56,7 +56,7 @@ export interface EbsStorageClassResult {
56
56
  * });
57
57
  * ```
58
58
  */
59
- export const EbsStorageClass = Composite<EbsStorageClassProps>((props) => {
59
+ export const EbsStorageClass = Composite((props: EbsStorageClassProps) => {
60
60
  const {
61
61
  name,
62
62
  type = "gp3",
@@ -48,7 +48,7 @@ export interface EfsStorageClassResult {
48
48
  * });
49
49
  * ```
50
50
  */
51
- export const EfsStorageClass = Composite<EfsStorageClassProps>((props) => {
51
+ export const EfsStorageClass = Composite((props: EfsStorageClassProps) => {
52
52
  const {
53
53
  name,
54
54
  fileSystemId,
@@ -59,7 +59,7 @@ export interface ExternalDnsAgentResult {
59
59
  * });
60
60
  * ```
61
61
  */
62
- export const ExternalDnsAgent = Composite<ExternalDnsAgentProps>((props) => {
62
+ export const ExternalDnsAgent = Composite((props: ExternalDnsAgentProps) => {
63
63
  const {
64
64
  iamRoleArn,
65
65
  domainFilters,
@@ -47,7 +47,7 @@ export interface FilestoreStorageClassResult {
47
47
  * });
48
48
  * ```
49
49
  */
50
- export const FilestoreStorageClass = Composite<FilestoreStorageClassProps>((props) => {
50
+ export const FilestoreStorageClass = Composite((props: FilestoreStorageClassProps) => {
51
51
  const {
52
52
  name,
53
53
  tier = "standard",
@@ -67,7 +67,7 @@ export interface FluentBitAgentResult {
67
67
  * });
68
68
  * ```
69
69
  */
70
- export const FluentBitAgent = Composite<FluentBitAgentProps>((props) => {
70
+ export const FluentBitAgent = Composite((props: FluentBitAgentProps) => {
71
71
  const {
72
72
  name = "fluent-bit",
73
73
  image = "public.ecr.aws/aws-observability/aws-for-fluent-bit:stable",
@@ -74,7 +74,7 @@ export interface GceIngressResult {
74
74
  * });
75
75
  * ```
76
76
  */
77
- export const GceIngress = Composite<GceIngressProps>((props) => {
77
+ export const GceIngress = Composite((props: GceIngressProps) => {
78
78
  const {
79
79
  name,
80
80
  hosts,
@@ -49,7 +49,7 @@ export interface GcePdStorageClassResult {
49
49
  * });
50
50
  * ```
51
51
  */
52
- export const GcePdStorageClass = Composite<GcePdStorageClassProps>((props) => {
52
+ export const GcePdStorageClass = Composite((props: GcePdStorageClassProps) => {
53
53
  const {
54
54
  name,
55
55
  type = "pd-balanced",
@@ -60,7 +60,7 @@ export interface GkeExternalDnsAgentResult {
60
60
  * });
61
61
  * ```
62
62
  */
63
- export const GkeExternalDnsAgent = Composite<GkeExternalDnsAgentProps>((props) => {
63
+ export const GkeExternalDnsAgent = Composite((props: GkeExternalDnsAgentProps) => {
64
64
  const {
65
65
  gcpServiceAccountEmail,
66
66
  gcpProjectId,
@@ -65,7 +65,7 @@ export interface GkeFluentBitAgentResult {
65
65
  * });
66
66
  * ```
67
67
  */
68
- export const GkeFluentBitAgent = Composite<GkeFluentBitAgentProps>((props) => {
68
+ export const GkeFluentBitAgent = Composite((props: GkeFluentBitAgentProps) => {
69
69
  const {
70
70
  clusterName,
71
71
  projectId,
@@ -68,7 +68,7 @@ export interface GkeGatewayResult {
68
68
  * });
69
69
  * ```
70
70
  */
71
- export const GkeGateway = Composite<GkeGatewayProps>((props) => {
71
+ export const GkeGateway = Composite((props: GkeGatewayProps) => {
72
72
  const {
73
73
  name,
74
74
  gatewayClassName = "gke-l7-global-external-managed",
@@ -65,7 +65,7 @@ export interface GkeOtelCollectorResult {
65
65
  * });
66
66
  * ```
67
67
  */
68
- export const GkeOtelCollector = Composite<GkeOtelCollectorProps>((props) => {
68
+ export const GkeOtelCollector = Composite((props: GkeOtelCollectorProps) => {
69
69
  const {
70
70
  clusterName,
71
71
  projectId,
@@ -55,7 +55,7 @@ export interface IrsaServiceAccountResult {
55
55
  * });
56
56
  * ```
57
57
  */
58
- export const IrsaServiceAccount = Composite<IrsaServiceAccountProps>((props) => {
58
+ export const IrsaServiceAccount = Composite((props: IrsaServiceAccountProps) => {
59
59
  const {
60
60
  name,
61
61
  iamRoleArn,
@@ -57,7 +57,7 @@ export interface MetricsServerResult {
57
57
  * const ms = MetricsServer({});
58
58
  * ```
59
59
  */
60
- export const MetricsServer = Composite<MetricsServerProps>((props) => {
60
+ export const MetricsServer = Composite((props: MetricsServerProps) => {
61
61
  const {
62
62
  name = "metrics-server",
63
63
  image = "registry.k8s.io/metrics-server/metrics-server:v0.7.2",
@@ -91,7 +91,7 @@ export interface MonitoredServiceResult {
91
91
  * });
92
92
  * ```
93
93
  */
94
- export const MonitoredService = Composite<MonitoredServiceProps>((props) => {
94
+ export const MonitoredService = Composite((props: MonitoredServiceProps) => {
95
95
  const {
96
96
  name,
97
97
  image,
@@ -65,7 +65,7 @@ export interface NamespaceEnvResult {
65
65
  * });
66
66
  * ```
67
67
  */
68
- export const NamespaceEnv = Composite<NamespaceEnvProps>((props) => {
68
+ export const NamespaceEnv = Composite((props: NamespaceEnvProps) => {
69
69
  const {
70
70
  name,
71
71
  cpuQuota,
@@ -85,7 +85,7 @@ export interface NetworkIsolatedAppResult {
85
85
  * });
86
86
  * ```
87
87
  */
88
- export const NetworkIsolatedApp = Composite<NetworkIsolatedAppProps>((props) => {
88
+ export const NetworkIsolatedApp = Composite((props: NetworkIsolatedAppProps) => {
89
89
  const {
90
90
  name,
91
91
  image,