@intentius/chant-lexicon-k8s 0.42.1 → 0.44.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.
Files changed (127) hide show
  1. package/dist/api/classify.d.ts.map +1 -1
  2. package/dist/api/connect.d.ts.map +1 -1
  3. package/dist/codegen/docs.d.ts.map +1 -1
  4. package/dist/components/argo-app.d.ts +104 -0
  5. package/dist/components/argo-app.d.ts.map +1 -0
  6. package/dist/components/builders.d.ts +8 -0
  7. package/dist/components/builders.d.ts.map +1 -1
  8. package/dist/components/capability-plugin.d.ts +1 -0
  9. package/dist/components/capability-plugin.d.ts.map +1 -1
  10. package/dist/components/flux-reconcile.d.ts +84 -0
  11. package/dist/components/flux-reconcile.d.ts.map +1 -0
  12. package/dist/components/index.d.ts +2 -0
  13. package/dist/components/index.d.ts.map +1 -1
  14. package/dist/components/kustomize-apply.d.ts +29 -7
  15. package/dist/components/kustomize-apply.d.ts.map +1 -1
  16. package/dist/composites/flux-app.d.ts +132 -0
  17. package/dist/composites/flux-app.d.ts.map +1 -0
  18. package/dist/composites/index.d.ts +2 -0
  19. package/dist/composites/index.d.ts.map +1 -1
  20. package/dist/config-schema.d.ts +3 -0
  21. package/dist/config-schema.d.ts.map +1 -1
  22. package/dist/config.d.ts +32 -5
  23. package/dist/config.d.ts.map +1 -1
  24. package/dist/crd/crd-sources.d.ts.map +1 -1
  25. package/dist/crd/parser.d.ts.map +1 -1
  26. package/dist/describe-resources.d.ts +10 -8
  27. package/dist/describe-resources.d.ts.map +1 -1
  28. package/dist/generated/index.d.ts +15 -4
  29. package/dist/generated/index.d.ts.map +1 -1
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/integrity.json +10 -6
  33. package/dist/kube/connect.d.ts +5 -4
  34. package/dist/kube/connect.d.ts.map +1 -1
  35. package/dist/kube/get.d.ts +4 -4
  36. package/dist/kustomize/render.d.ts +15 -0
  37. package/dist/kustomize/render.d.ts.map +1 -0
  38. package/dist/kustomize/rendered-entity.d.ts +39 -0
  39. package/dist/kustomize/rendered-entity.d.ts.map +1 -0
  40. package/dist/kustomize/root.d.ts +19 -0
  41. package/dist/kustomize/root.d.ts.map +1 -0
  42. package/dist/lint/audit-catalog.d.ts.map +1 -1
  43. package/dist/lint/post-synth/argo-helpers.d.ts +2 -0
  44. package/dist/lint/post-synth/argo-helpers.d.ts.map +1 -1
  45. package/dist/lint/post-synth/flux002.d.ts +14 -0
  46. package/dist/lint/post-synth/flux002.d.ts.map +1 -0
  47. package/dist/lint/post-synth/flux003.d.ts +17 -0
  48. package/dist/lint/post-synth/flux003.d.ts.map +1 -0
  49. package/dist/lint/post-synth/index.d.ts.map +1 -1
  50. package/dist/lint/rules/flux-source-ref-pin.d.ts +16 -0
  51. package/dist/lint/rules/flux-source-ref-pin.d.ts.map +1 -0
  52. package/dist/manifest.json +1 -1
  53. package/dist/meta.json +92 -16
  54. package/dist/okf/index.md +7 -1
  55. package/dist/okf/rules/FLUX001.md +17 -0
  56. package/dist/okf/rules/FLUX002.md +19 -0
  57. package/dist/okf/rules/FLUX003.md +15 -0
  58. package/dist/okf/types/Addon.md +14 -0
  59. package/dist/okf/types/Bucket.md +4 -0
  60. package/dist/okf/types/{HelmChart.md → FluxHelmChart.md} +2 -2
  61. package/dist/okf/types/GitRepository.md +5 -0
  62. package/dist/okf/types/HelmChartConfig.md +14 -0
  63. package/dist/okf/types/K3sHelmChart.md +14 -0
  64. package/dist/okf/types/Kustomization.md +6 -0
  65. package/dist/okf/types/Namespace.md +1 -0
  66. package/dist/okf/types/Node.md +1 -0
  67. package/dist/okf/types/OCIRepository.md +4 -0
  68. package/dist/plugin.d.ts.map +1 -1
  69. package/dist/rules/argo-helpers.ts +2 -0
  70. package/dist/rules/flux-source-ref-pin.ts +52 -0
  71. package/dist/rules/flux002.ts +77 -0
  72. package/dist/rules/flux003.ts +75 -0
  73. package/dist/serializer.d.ts.map +1 -1
  74. package/dist/skills/chant-k8s-flux.md +162 -0
  75. package/dist/types/index.d.ts +41 -3
  76. package/dist/validate.d.ts.map +1 -1
  77. package/package.json +3 -3
  78. package/src/api/classify.test.ts +37 -0
  79. package/src/api/classify.ts +11 -1
  80. package/src/api/connect.ts +10 -0
  81. package/src/codegen/docs.ts +11 -0
  82. package/src/components/argo-app.test.ts +137 -0
  83. package/src/components/argo-app.ts +163 -0
  84. package/src/components/builders.ts +8 -0
  85. package/src/components/capability-plugin.ts +11 -1
  86. package/src/components/flux-reconcile.test.ts +145 -0
  87. package/src/components/flux-reconcile.ts +159 -0
  88. package/src/components/index.ts +13 -0
  89. package/src/components/kustomize-apply.ts +12 -41
  90. package/src/composites/composites.test.ts +122 -0
  91. package/src/composites/flux-app.ts +274 -0
  92. package/src/composites/index.ts +9 -0
  93. package/src/config-augmentation.test.ts +72 -0
  94. package/src/config-schema.ts +5 -0
  95. package/src/config.ts +33 -5
  96. package/src/crd/crd-sources.ts +37 -0
  97. package/src/crd/parser.ts +6 -0
  98. package/src/describe-resources.test.ts +290 -6
  99. package/src/describe-resources.ts +235 -127
  100. package/src/generated/index.d.ts +41 -3
  101. package/src/generated/index.ts +15 -4
  102. package/src/generated/lexicon-k8s.json +92 -16
  103. package/src/generated/operations.json +48 -0
  104. package/src/index.ts +4 -0
  105. package/src/kube/connect.ts +5 -4
  106. package/src/kube/get.ts +4 -4
  107. package/src/kustomize/render.ts +84 -0
  108. package/src/kustomize/rendered-entity.ts +72 -0
  109. package/src/kustomize/root.test.ts +268 -0
  110. package/src/kustomize/root.ts +109 -0
  111. package/src/lint/audit-catalog.ts +2 -0
  112. package/src/lint/post-synth/argo-helpers.ts +2 -0
  113. package/src/lint/post-synth/flux002.ts +77 -0
  114. package/src/lint/post-synth/flux003.ts +75 -0
  115. package/src/lint/post-synth/index.ts +4 -0
  116. package/src/lint/post-synth/post-synth.test.ts +132 -0
  117. package/src/lint/rules/flux-source-ref-pin.ts +52 -0
  118. package/src/lint/rules/rules.test.ts +51 -0
  119. package/src/plugin.test.ts +2 -0
  120. package/src/plugin.ts +47 -0
  121. package/src/serializer.ts +34 -4
  122. package/src/skills/chant-k8s-flux.md +162 -0
  123. package/src/testdata/kustomize-root/base/deployment.yaml +17 -0
  124. package/src/testdata/kustomize-root/base/kustomization.yaml +3 -0
  125. package/src/testdata/kustomize-root/base/service.yaml +9 -0
  126. package/src/testdata/kustomize-root/overlays/prod/kustomization.yaml +6 -0
  127. package/src/validate.ts +4 -0
@@ -51,6 +51,13 @@
51
51
  "kind": "property",
52
52
  "lexicon": "k8s"
53
53
  },
54
+ "Addon": {
55
+ "resourceType": "K8s::K3s::Addon",
56
+ "kind": "resource",
57
+ "lexicon": "k8s",
58
+ "apiVersion": "k3s.cattle.io/v1",
59
+ "gvkKind": "Addon"
60
+ },
54
61
  "Address": {
55
62
  "resourceType": "K8s::Gateway::Gateway.addresses",
56
63
  "kind": "property",
@@ -229,6 +236,11 @@
229
236
  "kind": "property",
230
237
  "lexicon": "k8s"
231
238
  },
239
+ "AuthSecret": {
240
+ "resourceType": "K8s::K3s::HelmChart.authSecret",
241
+ "kind": "property",
242
+ "lexicon": "k8s"
243
+ },
232
244
  "AutoscalerOptions": {
233
245
  "resourceType": "K8s::Ray::RayCluster.autoscalerOptions",
234
246
  "kind": "property",
@@ -1066,6 +1078,11 @@
1066
1078
  "kind": "property",
1067
1079
  "lexicon": "k8s"
1068
1080
  },
1081
+ "DockerRegistrySecret": {
1082
+ "resourceType": "K8s::K3s::HelmChart.dockerRegistrySecret",
1083
+ "kind": "property",
1084
+ "lexicon": "k8s"
1085
+ },
1069
1086
  "DriftDetection": {
1070
1087
  "resourceType": "K8s::Flux::HelmRelease.driftDetection",
1071
1088
  "kind": "property",
@@ -1241,6 +1258,28 @@
1241
1258
  "apiVersion": "flowcontrol.apiserver.k8s.io/v1",
1242
1259
  "gvkKind": "FlowSchemaList"
1243
1260
  },
1261
+ "FluxHelmChart": {
1262
+ "resourceType": "K8s::Flux::HelmChart",
1263
+ "kind": "resource",
1264
+ "lexicon": "k8s",
1265
+ "apiVersion": "source.toolkit.fluxcd.io/v1",
1266
+ "gvkKind": "HelmChart"
1267
+ },
1268
+ "FluxHelmChart_SourceRef": {
1269
+ "resourceType": "K8s::Flux::HelmChart.sourceRef",
1270
+ "kind": "property",
1271
+ "lexicon": "k8s"
1272
+ },
1273
+ "FluxHelmChart_Status": {
1274
+ "resourceType": "K8s::Flux::HelmChart.status",
1275
+ "kind": "property",
1276
+ "lexicon": "k8s"
1277
+ },
1278
+ "FluxHelmChart_Verify": {
1279
+ "resourceType": "K8s::Flux::HelmChart.verify",
1280
+ "kind": "property",
1281
+ "lexicon": "k8s"
1282
+ },
1244
1283
  "FluxInstance": {
1245
1284
  "resourceType": "K8s::Flux::FluxInstance",
1246
1285
  "kind": "resource",
@@ -1527,25 +1566,15 @@
1527
1566
  "kind": "property",
1528
1567
  "lexicon": "k8s"
1529
1568
  },
1530
- "HelmChart": {
1531
- "resourceType": "K8s::Flux::HelmChart",
1569
+ "HelmChartConfig": {
1570
+ "resourceType": "K8s::K3s::HelmChartConfig",
1532
1571
  "kind": "resource",
1533
1572
  "lexicon": "k8s",
1534
- "apiVersion": "source.toolkit.fluxcd.io/v1",
1535
- "gvkKind": "HelmChart"
1573
+ "apiVersion": "helm.cattle.io/v1",
1574
+ "gvkKind": "HelmChartConfig"
1536
1575
  },
1537
- "HelmChart_SourceRef": {
1538
- "resourceType": "K8s::Flux::HelmChart.sourceRef",
1539
- "kind": "property",
1540
- "lexicon": "k8s"
1541
- },
1542
- "HelmChart_Status": {
1543
- "resourceType": "K8s::Flux::HelmChart.status",
1544
- "kind": "property",
1545
- "lexicon": "k8s"
1546
- },
1547
- "HelmChart_Verify": {
1548
- "resourceType": "K8s::Flux::HelmChart.verify",
1576
+ "HelmChartConfig_ValuesSecret": {
1577
+ "resourceType": "K8s::K3s::HelmChartConfig.valuesSecrets",
1549
1578
  "kind": "property",
1550
1579
  "lexicon": "k8s"
1551
1580
  },
@@ -2145,6 +2174,48 @@
2145
2174
  "apiVersion": "batch/v1",
2146
2175
  "gvkKind": "JobList"
2147
2176
  },
2177
+ "K3sHelmChart": {
2178
+ "resourceType": "K8s::K3s::HelmChart",
2179
+ "kind": "resource",
2180
+ "lexicon": "k8s",
2181
+ "apiVersion": "helm.cattle.io/v1",
2182
+ "gvkKind": "HelmChart"
2183
+ },
2184
+ "K3sHelmChart_AuthSecret": {
2185
+ "resourceType": "K8s::K3s::HelmChart.authSecret",
2186
+ "kind": "property",
2187
+ "lexicon": "k8s"
2188
+ },
2189
+ "K3sHelmChart_DockerRegistrySecret": {
2190
+ "resourceType": "K8s::K3s::HelmChart.dockerRegistrySecret",
2191
+ "kind": "property",
2192
+ "lexicon": "k8s"
2193
+ },
2194
+ "K3sHelmChart_PodSecurityContext": {
2195
+ "resourceType": "K8s::K3s::HelmChart.podSecurityContext",
2196
+ "kind": "property",
2197
+ "lexicon": "k8s"
2198
+ },
2199
+ "K3sHelmChart_RepoCAConfigMap": {
2200
+ "resourceType": "K8s::K3s::HelmChart.repoCAConfigMap",
2201
+ "kind": "property",
2202
+ "lexicon": "k8s"
2203
+ },
2204
+ "K3sHelmChart_SecurityContext": {
2205
+ "resourceType": "K8s::K3s::HelmChart.securityContext",
2206
+ "kind": "property",
2207
+ "lexicon": "k8s"
2208
+ },
2209
+ "K3sHelmChart_Status": {
2210
+ "resourceType": "K8s::K3s::HelmChart.status",
2211
+ "kind": "property",
2212
+ "lexicon": "k8s"
2213
+ },
2214
+ "K3sHelmChart_ValuesSecret": {
2215
+ "resourceType": "K8s::K3s::HelmChart.valuesSecrets",
2216
+ "kind": "property",
2217
+ "lexicon": "k8s"
2218
+ },
2148
2219
  "KeyToPath": {
2149
2220
  "resourceType": "K8s::Core::KeyToPath",
2150
2221
  "kind": "property",
@@ -3416,6 +3487,11 @@
3416
3487
  "kind": "property",
3417
3488
  "lexicon": "k8s"
3418
3489
  },
3490
+ "RepoCAConfigMap": {
3491
+ "resourceType": "K8s::K3s::HelmChart.repoCAConfigMap",
3492
+ "kind": "property",
3493
+ "lexicon": "k8s"
3494
+ },
3419
3495
  "Resource": {
3420
3496
  "resourceType": "K8s::Flux::Receiver.resources",
3421
3497
  "kind": "property",
@@ -1665,6 +1665,54 @@
1665
1665
  "watch"
1666
1666
  ]
1667
1667
  },
1668
+ "K8s::K3s::Addon": {
1669
+ "entityType": "K8s::K3s::Addon",
1670
+ "apiVersion": "k3s.cattle.io/v1",
1671
+ "kind": "Addon",
1672
+ "plural": "addons",
1673
+ "scope": "Namespaced",
1674
+ "verbs": [
1675
+ "delete",
1676
+ "get",
1677
+ "list",
1678
+ "patch",
1679
+ "post",
1680
+ "put",
1681
+ "watch"
1682
+ ]
1683
+ },
1684
+ "K8s::K3s::HelmChart": {
1685
+ "entityType": "K8s::K3s::HelmChart",
1686
+ "apiVersion": "helm.cattle.io/v1",
1687
+ "kind": "HelmChart",
1688
+ "plural": "helmcharts",
1689
+ "scope": "Namespaced",
1690
+ "verbs": [
1691
+ "delete",
1692
+ "get",
1693
+ "list",
1694
+ "patch",
1695
+ "post",
1696
+ "put",
1697
+ "watch"
1698
+ ]
1699
+ },
1700
+ "K8s::K3s::HelmChartConfig": {
1701
+ "entityType": "K8s::K3s::HelmChartConfig",
1702
+ "apiVersion": "helm.cattle.io/v1",
1703
+ "kind": "HelmChartConfig",
1704
+ "plural": "helmchartconfigs",
1705
+ "scope": "Namespaced",
1706
+ "verbs": [
1707
+ "delete",
1708
+ "get",
1709
+ "list",
1710
+ "patch",
1711
+ "post",
1712
+ "put",
1713
+ "watch"
1714
+ ]
1715
+ },
1668
1716
  "K8s::KubeMicroVM::MicroVM": {
1669
1717
  "entityType": "K8s::KubeMicroVM::MicroVM",
1670
1718
  "apiVersion": "lambda.aws.amazon.com/v1alpha1",
package/src/index.ts CHANGED
@@ -34,6 +34,7 @@ export {
34
34
  GceIngress, CockroachDbCluster, CockroachDbRegionStack,
35
35
  RayCluster, RayJob, RayService,
36
36
  ArgoAppFor, ArgoAppSetForRegions, registerArgoCluster,
37
+ FluxGitSource, FluxAppFor,
37
38
  AgicIngress, AzureDiskStorageClass, AzureFileStorageClass, AzureMonitorCollector,
38
39
  AksWorkloadIdentityServiceAccount,
39
40
  GkeFluentBitAgent, GkeOtelCollector, GkeExternalDnsAgent, AksExternalDnsAgent,
@@ -64,6 +65,9 @@ export type {
64
65
  ArgoAppForOptions, ArgoAppForResult,
65
66
  ArgoRegionTarget, ArgoAppSetForRegionsOptions, ArgoAppSetForRegionsResult,
66
67
  RegisterArgoClusterOptions, RegisterArgoClusterResult,
68
+ FluxSourceKind, FluxSourceRef,
69
+ FluxGitSourceOptions, FluxGitSourceResult,
70
+ FluxAppForOptions, FluxAppForResult,
67
71
  AgicIngressProps, AgicIngressResult,
68
72
  AzureDiskStorageClassProps, AzureDiskStorageClassResult,
69
73
  AzureFileStorageClassProps, AzureFileStorageClassResult,
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * Connecting to a cluster on `chant kube`'s behalf (chant #1079), honoring
3
3
  * the exact same environment→cluster binding every other observing path
4
- * does (chant #1100/#1155): `--env` resolves `k8s.profiles.<env>.context`
5
- * and refuses loudly on a mismatch, `--context` is an explicit override that
6
- * skips the binding entirely, and neither given falls back to the ambient
7
- * kubeconfig context the same three modes `describeResources` and the Op
4
+ * does (chant #1100/#1155, revised by #1488): `--env` resolves
5
+ * `k8s.profiles.<env>.context` and uses it regardless of the ambient
6
+ * context, `--context` is an explicit override that skips the binding
7
+ * entirely, and neither given falls back to the ambient kubeconfig
8
+ * context — the same three modes `describeResources` and the Op
8
9
  * activities already use, via the same `defaultK8sConnector`. Nothing here
9
10
  * reimplements that resolution; this only turns a connect failure into the
10
11
  * observation tri-state so a verb can render NOT-OBSERVED-with-reason
package/src/kube/get.ts CHANGED
@@ -3,10 +3,10 @@
3
3
  * chant verdict column the issue's acceptance criterion names.
4
4
  *
5
5
  * The cluster edge is the typed client (chant #1074/#1177), reached the same
6
- * way every other read does: `--env` resolves and honors the cluster
7
- * binding (chant #1100/#1155, refusing loudly on a mismatch), `--context` is
8
- * an explicit override, neither given falls back to the ambient kubeconfig
9
- * context. A connect failure, an RBAC-denied read, or a kind the cluster's
6
+ * way every other read does: `--env` resolves and uses the cluster
7
+ * binding (chant #1100/#1155, revised by #1488 the declared context is
8
+ * used regardless of what is ambient), `--context` is an explicit override,
9
+ * neither given falls back to the ambient kubeconfig context. A connect failure, an RBAC-denied read, or a kind the cluster's
10
10
  * discovery has never heard of all render as an honest message rather than
11
11
  * an empty table — "No resources found." is reserved for the one case that
12
12
  * actually means it: the kind is real and there are zero live instances.
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The kustomize renderer (#1548) — one implementation, two consumers.
3
+ *
4
+ * Piece 1 (`components/kustomize-apply.ts`) renders at DEPLOY time in front
5
+ * of the server-side applier; piece 3 (`./root.ts`) renders at BUILD time to
6
+ * give an overlay estate a declared side. Both run the same command with the
7
+ * same fallback through the same injectable runner, so a test asserts the
8
+ * exact invocation without either binary installed and the two paths cannot
9
+ * drift apart on how an overlay is rendered.
10
+ *
11
+ * Renderer resolution: `kustomize build`, falling back to `kubectl
12
+ * kustomize` (the same renderer vendored into kubectl) when the standalone
13
+ * binary is absent. When NEITHER binary exists the failure names both — a
14
+ * missing renderer is an environment problem the operator can fix in one
15
+ * install, and it must read as that, not as a stack trace. A render failure
16
+ * from an existing binary (a broken overlay, a missing base) propagates
17
+ * untouched: that error text is kustomize's own diagnosis and is strictly
18
+ * better than anything this module could wrap it in.
19
+ *
20
+ * The 64MiB maxBuffer mirrors helm-upgrade's — a big overlay renders
21
+ * megabytes.
22
+ */
23
+ import { exec } from "node:child_process";
24
+ import { promisify } from "node:util";
25
+ import { loadAll } from "js-yaml";
26
+
27
+ const execAsync = promisify(exec);
28
+
29
+ /** Injectable shell runner — the helm-upgrade seam, same buffer bound. */
30
+ export type KustomizeRunner = (command: string) => Promise<{ stdout: string }>;
31
+
32
+ export const defaultKustomizeRunner: KustomizeRunner = (command) =>
33
+ execAsync(command, { maxBuffer: 64 * 1024 * 1024 });
34
+
35
+ /** Single-quote shell escaping, as helm-upgrade quotes its argv. */
36
+ function q(v: string): string {
37
+ return `'${v.replace(/'/g, "'\\''")}'`;
38
+ }
39
+
40
+ /** The exact render command, standalone binary first. Pure; exported for tests. */
41
+ export function renderCommand(dir: string, tool: "kustomize" | "kubectl" = "kustomize"): string {
42
+ return tool === "kustomize" ? `kustomize build ${q(dir)}` : `kubectl kustomize ${q(dir)}`;
43
+ }
44
+
45
+ /** Does this error mean "the binary is not installed" (vs "the render failed")? */
46
+ function isMissingBinary(err: unknown): boolean {
47
+ const message = err instanceof Error ? err.message : String(err);
48
+ return /ENOENT|not found|command not found|127/.test(message);
49
+ }
50
+
51
+ /**
52
+ * Render the kustomization at `dir` and parse the emitted YAML into document
53
+ * objects. Falls back to kubectl's vendored kustomize when the standalone
54
+ * binary is missing; when both binaries are missing, fails with a message
55
+ * naming them.
56
+ */
57
+ export async function renderKustomizeDocuments(
58
+ dir: string,
59
+ run: KustomizeRunner = defaultKustomizeRunner,
60
+ ): Promise<Array<Record<string, unknown>>> {
61
+ let stdout: string;
62
+ try {
63
+ ({ stdout } = await run(renderCommand(dir, "kustomize")));
64
+ } catch (err) {
65
+ if (!isMissingBinary(err)) throw err;
66
+ try {
67
+ ({ stdout } = await run(renderCommand(dir, "kubectl")));
68
+ } catch (err2) {
69
+ if (isMissingBinary(err2)) {
70
+ throw new Error(
71
+ `Cannot render kustomization "${dir}": neither the \`kustomize\` binary nor \`kubectl\` ` +
72
+ `(whose vendored \`kubectl kustomize\` is the fallback) was found on PATH. ` +
73
+ `Install kustomize or kubectl and retry.`,
74
+ );
75
+ }
76
+ throw err2;
77
+ }
78
+ }
79
+ const documents: Array<Record<string, unknown>> = [];
80
+ for (const doc of loadAll(stdout)) {
81
+ if (doc && typeof doc === "object" && !Array.isArray(doc)) documents.push(doc as Record<string, unknown>);
82
+ }
83
+ return documents;
84
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * A rendered kustomize document as a chant entity (#1548 piece 3).
3
+ *
4
+ * A kustomize build root's output is a set of finished manifests, not typed
5
+ * chant source — the overlay already decided every field. So the entity that
6
+ * carries one into the build is deliberately verbatim: `props` IS the
7
+ * document, and the serializer emits it as-is (plus the same default-label /
8
+ * ownership merge every discovered resource gets) instead of running the
9
+ * spec-inference heuristics built for typed declarables. The marker is what
10
+ * tells the serializer to take that path — `Symbol.for`, so a lexicon built
11
+ * against a separate copy of core still matches (the #1137 lesson).
12
+ *
13
+ * `entityType` uses the same `K8s::{Group}::{Kind}` convention the generated
14
+ * operation surface speaks, so `lifecycle diff --live` observes a rendered
15
+ * Deployment through exactly the reader a declared one uses. A kind the
16
+ * surface doesn't know reports NOT-OBSERVED with the existing "no generated
17
+ * operation surface" reason — the honest verdict, not a silent hole.
18
+ *
19
+ * Provenance: every rendered document is stamped with an annotation naming
20
+ * the overlay dir it came from, so a consumer (behold#171's overlay boxes)
21
+ * can group by declared origin instead of guessing from paths.
22
+ */
23
+ import { DECLARABLE_MARKER, type Declarable } from "@intentius/chant/declarable";
24
+ import { gvkToTypeName } from "../spec/parse";
25
+
26
+ /** Marks an entity whose `props` are a complete, render-final manifest. */
27
+ export const RENDERED_MANIFEST_MARKER = Symbol.for("chant.k8s.renderedManifest");
28
+
29
+ /** Annotation stamped on every rendered document, valued with the root dir. */
30
+ export const KUSTOMIZE_ROOT_ANNOTATION = "chant.intentius.io/kustomize-root";
31
+
32
+ export interface RenderedManifestEntity extends Declarable {
33
+ readonly kind: "resource";
34
+ readonly props: Record<string, unknown>;
35
+ readonly [RENDERED_MANIFEST_MARKER]: true;
36
+ }
37
+
38
+ export function isRenderedManifestEntity(entity: Declarable): entity is RenderedManifestEntity {
39
+ return (entity as unknown as Record<symbol, unknown>)[RENDERED_MANIFEST_MARKER] === true;
40
+ }
41
+
42
+ /**
43
+ * Wrap one rendered document. Returns null when the document has no string
44
+ * `apiVersion`/`kind` — not a Kubernetes object, nothing to declare.
45
+ */
46
+ export function renderedManifestEntity(
47
+ doc: Record<string, unknown>,
48
+ root: string,
49
+ ): RenderedManifestEntity | null {
50
+ const apiVersion = doc.apiVersion;
51
+ const kind = doc.kind;
52
+ if (typeof apiVersion !== "string" || typeof kind !== "string" || !apiVersion || !kind) return null;
53
+
54
+ const slash = apiVersion.indexOf("/");
55
+ const group = slash === -1 ? "" : apiVersion.slice(0, slash);
56
+ const version = slash === -1 ? apiVersion : apiVersion.slice(slash + 1);
57
+
58
+ const metadata = { ...((doc.metadata as Record<string, unknown> | undefined) ?? {}) };
59
+ metadata.annotations = {
60
+ ...((metadata.annotations as Record<string, unknown> | undefined) ?? {}),
61
+ [KUSTOMIZE_ROOT_ANNOTATION]: root,
62
+ };
63
+
64
+ return {
65
+ lexicon: "k8s",
66
+ entityType: gvkToTypeName({ group, version, kind }),
67
+ kind: "resource",
68
+ props: { ...doc, metadata },
69
+ [DECLARABLE_MARKER]: true,
70
+ [RENDERED_MANIFEST_MARKER]: true,
71
+ };
72
+ }