@intentius/chant-lexicon-helm 0.46.0 → 0.50.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 (122) hide show
  1. package/dist/archive-render.d.ts +94 -0
  2. package/dist/archive-render.d.ts.map +1 -0
  3. package/dist/commands.d.ts +54 -0
  4. package/dist/commands.d.ts.map +1 -0
  5. package/dist/config.d.ts +135 -0
  6. package/dist/config.d.ts.map +1 -0
  7. package/dist/deep-observe-hooks.d.ts +2 -0
  8. package/dist/deep-observe-hooks.d.ts.map +1 -0
  9. package/dist/deep-observe.d.ts +54 -0
  10. package/dist/deep-observe.d.ts.map +1 -0
  11. package/dist/describe-resources.d.ts +17 -0
  12. package/dist/describe-resources.d.ts.map +1 -0
  13. package/dist/index.d.ts +19 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/integrity.json +5 -3
  16. package/dist/lint/audit-catalog.d.ts.map +1 -1
  17. package/dist/lint/post-synth/index.d.ts.map +1 -1
  18. package/dist/lint/post-synth/whm503.d.ts +34 -0
  19. package/dist/lint/post-synth/whm503.d.ts.map +1 -0
  20. package/dist/lint/post-synth/whm504.d.ts +20 -0
  21. package/dist/lint/post-synth/whm504.d.ts.map +1 -0
  22. package/dist/manifest.json +25 -13
  23. package/dist/okf/index.md +2 -0
  24. package/dist/okf/rules/WHM503.md +15 -0
  25. package/dist/okf/rules/WHM504.md +11 -0
  26. package/dist/okf/types/Values.md +1 -0
  27. package/dist/op/activities/cluster-probe.d.ts +44 -0
  28. package/dist/op/activities/cluster-probe.d.ts.map +1 -0
  29. package/dist/op/activities/helm.d.ts +179 -5
  30. package/dist/op/activities/helm.d.ts.map +1 -1
  31. package/dist/op/activities/index.d.ts +4 -2
  32. package/dist/op/activities/index.d.ts.map +1 -1
  33. package/dist/op/builders.d.ts +47 -0
  34. package/dist/op/builders.d.ts.map +1 -0
  35. package/dist/pinnability/actions.d.ts +76 -0
  36. package/dist/pinnability/actions.d.ts.map +1 -0
  37. package/dist/pinnability/classify.d.ts +129 -0
  38. package/dist/pinnability/classify.d.ts.map +1 -0
  39. package/dist/pinnability/conditions.d.ts +109 -0
  40. package/dist/pinnability/conditions.d.ts.map +1 -0
  41. package/dist/pinnability/index.d.ts +17 -0
  42. package/dist/pinnability/index.d.ts.map +1 -0
  43. package/dist/pinnability/localize.d.ts +124 -0
  44. package/dist/pinnability/localize.d.ts.map +1 -0
  45. package/dist/pinnability/render-stream.d.ts +27 -0
  46. package/dist/pinnability/render-stream.d.ts.map +1 -0
  47. package/dist/pinnability/values.d.ts +69 -0
  48. package/dist/pinnability/values.d.ts.map +1 -0
  49. package/dist/plugin.d.ts.map +1 -1
  50. package/dist/release-observe.d.ts +109 -0
  51. package/dist/release-observe.d.ts.map +1 -0
  52. package/dist/render-diff.d.ts +192 -0
  53. package/dist/render-diff.d.ts.map +1 -0
  54. package/dist/render-digest.d.ts +135 -0
  55. package/dist/render-digest.d.ts.map +1 -0
  56. package/dist/render-store.d.ts +310 -0
  57. package/dist/render-store.d.ts.map +1 -0
  58. package/dist/render-wrapper.d.ts +140 -0
  59. package/dist/render-wrapper.d.ts.map +1 -0
  60. package/dist/render.d.ts +81 -0
  61. package/dist/render.d.ts.map +1 -1
  62. package/dist/rules/whm503.ts +101 -0
  63. package/dist/rules/whm504.ts +52 -0
  64. package/dist/skills/chant-helm.md +7 -0
  65. package/dist/values-probe.d.ts +177 -0
  66. package/dist/values-probe.d.ts.map +1 -0
  67. package/dist/wrapper-chart.d.ts +61 -0
  68. package/dist/wrapper-chart.d.ts.map +1 -0
  69. package/package.json +10 -4
  70. package/src/archive-render.test.ts +201 -0
  71. package/src/archive-render.ts +119 -0
  72. package/src/commands.test.ts +487 -0
  73. package/src/commands.ts +445 -0
  74. package/src/config.test.ts +130 -0
  75. package/src/config.ts +220 -0
  76. package/src/deep-observe-hooks.ts +20 -0
  77. package/src/deep-observe.test.ts +165 -0
  78. package/src/deep-observe.ts +121 -0
  79. package/src/describe-resources.test.ts +255 -0
  80. package/src/describe-resources.ts +198 -0
  81. package/src/index.ts +106 -2
  82. package/src/lifecycle-integration.test.ts +289 -0
  83. package/src/lint/audit-catalog.ts +2 -0
  84. package/src/lint/post-synth/index.ts +4 -0
  85. package/src/lint/post-synth/whm503.test.ts +134 -0
  86. package/src/lint/post-synth/whm503.ts +101 -0
  87. package/src/lint/post-synth/whm504.test.ts +105 -0
  88. package/src/lint/post-synth/whm504.ts +52 -0
  89. package/src/op/activities/cluster-probe.test.ts +66 -0
  90. package/src/op/activities/cluster-probe.ts +148 -0
  91. package/src/op/activities/helm.test.ts +824 -0
  92. package/src/op/activities/helm.ts +628 -5
  93. package/src/op/activities/index.ts +17 -2
  94. package/src/op/builders.test.ts +70 -0
  95. package/src/op/builders.ts +66 -0
  96. package/src/pinnability/actions.ts +263 -0
  97. package/src/pinnability/classify.ts +579 -0
  98. package/src/pinnability/conditions.ts +0 -0
  99. package/src/pinnability/index.ts +77 -0
  100. package/src/pinnability/localize.test.ts +234 -0
  101. package/src/pinnability/localize.ts +644 -0
  102. package/src/pinnability/pinnability.test.ts +560 -0
  103. package/src/pinnability/render-stream.ts +59 -0
  104. package/src/pinnability/values.ts +205 -0
  105. package/src/plugin.test.ts +3 -1
  106. package/src/plugin.ts +97 -12
  107. package/src/release-observe.ts +392 -0
  108. package/src/render-diff.test.ts +366 -0
  109. package/src/render-diff.ts +407 -0
  110. package/src/render-digest.test.ts +186 -0
  111. package/src/render-digest.ts +207 -0
  112. package/src/render-store.test.ts +412 -0
  113. package/src/render-store.ts +621 -0
  114. package/src/render-wrapper.test.ts +259 -0
  115. package/src/render-wrapper.ts +274 -0
  116. package/src/render.test.ts +383 -4
  117. package/src/render.ts +319 -8
  118. package/src/skills/chant-helm.md +7 -0
  119. package/src/values-probe.test.ts +400 -0
  120. package/src/values-probe.ts +627 -0
  121. package/src/wrapper-chart.test.ts +110 -0
  122. package/src/wrapper-chart.ts +124 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapper-chart.d.ts","sourceRoot":"","sources":["../src/wrapper-chart.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAKH,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErE,wFAAwF;AACxF,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC;IACZ,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,YAAY,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,yFAAyF;IACzF,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOtD;AAYD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,wBAAwB,CAwCnG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-helm",
3
- "version": "0.46.0",
3
+ "version": "0.50.0",
4
4
  "description": "Helm chart lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -49,6 +49,11 @@
49
49
  "types": "./dist/components/index.d.ts",
50
50
  "default": "./src/components/index.ts"
51
51
  },
52
+ "./pinnability": {
53
+ "development": "./src/pinnability/index.ts",
54
+ "types": "./dist/pinnability/index.d.ts",
55
+ "default": "./src/pinnability/index.ts"
56
+ },
52
57
  "./lint/post-synth": {
53
58
  "development": "./src/lint/post-synth/index.ts",
54
59
  "types": "./dist/lint/post-synth/index.d.ts",
@@ -73,7 +78,7 @@
73
78
  },
74
79
  "dependencies": {
75
80
  "@intentius/chant-lexicon-k8s": "*",
76
- "js-yaml": "^4.1.0"
81
+ "js-yaml": "^4.3.1"
77
82
  },
78
83
  "devDependencies": {
79
84
  "@intentius/chant": "*",
@@ -81,7 +86,8 @@
81
86
  "@types/js-yaml": "^4.0.9"
82
87
  },
83
88
  "peerDependencies": {
84
- "@intentius/chant": "^0.46.0",
85
- "typescript": "^5.9.3"
89
+ "@intentius/chant": "^0.50.0",
90
+ "typescript": "^5.9.3",
91
+ "zod": "^4.3.6"
86
92
  }
87
93
  }
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Tests `addArchiveHelmRender` (#1240, epic #1228 Phase 3) — folding a
3
+ * pinned render into a build archive's manifest, and the media-type story
4
+ * that lets an OCI referrer lookup classify signatures/provenance/SBOMs
5
+ * attached to a render's digest exactly as it would for an image.
6
+ */
7
+
8
+ import { describe, expect, it } from "vitest";
9
+
10
+ import {
11
+ addArchiveEntry,
12
+ createBuildArchiveManifest,
13
+ findArchiveEntry,
14
+ templateEntries,
15
+ } from "@intentius/chant/components/verbs/build-archive";
16
+ import { createOrasReferrerLookup } from "@intentius/chant/lifecycle/oras-referrer-lookup";
17
+ import type { ProcessRunner } from "@intentius/chant/components/verbs/process-runner";
18
+
19
+ import { addArchiveHelmRender, HELM_RENDER_MEDIA_TYPE } from "./archive-render";
20
+ import type { RenderManifest } from "./render-store";
21
+
22
+ const RENDER_CONTENT_DIGEST =
23
+ "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
24
+
25
+ function fixtureRenderManifest(overrides: Partial<RenderManifest> = {}): RenderManifest {
26
+ return {
27
+ version: 1,
28
+ chart: "external-secrets",
29
+ chartVersion: "0.10.4",
30
+ repo: "https://charts.external-secrets.io",
31
+ releaseName: "external-secrets",
32
+ namespace: "external-secrets",
33
+ valuesDigest: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
34
+ inputDigest: "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
35
+ capabilityProfile: { cluster: "prod", kubeVersion: "1.29.0", apiVersions: [] },
36
+ contentDigest: RENDER_CONTENT_DIGEST,
37
+ docCount: 3,
38
+ documents: [],
39
+ renderedAt: "2026-01-01T00:00:00.000Z",
40
+ helmVersion: "3.14.0",
41
+ chantVersion: "0.46.0",
42
+ sourceRef: null,
43
+ coalescedValuesDigest: null,
44
+ valueSources: null,
45
+ ...overrides,
46
+ };
47
+ }
48
+
49
+ describe("addArchiveHelmRender (#1240)", () => {
50
+ it("folds the render into BuildArchiveManifest.contents as a template-kind entry with the render media type", () => {
51
+ const render = fixtureRenderManifest();
52
+ const { manifest, digest } = addArchiveHelmRender({
53
+ path: "external-secrets.render.yaml",
54
+ render,
55
+ });
56
+
57
+ expect(digest).toBe(RENDER_CONTENT_DIGEST);
58
+ const entry = findArchiveEntry(manifest, "external-secrets.render.yaml");
59
+ expect(entry).toMatchObject({
60
+ kind: "template",
61
+ path: "external-secrets.render.yaml",
62
+ digest: RENDER_CONTENT_DIGEST,
63
+ mediaType: HELM_RENDER_MEDIA_TYPE,
64
+ });
65
+ expect(templateEntries(manifest)).toHaveLength(1);
66
+ });
67
+
68
+ it("reuses the render's own contentDigest rather than recomputing one from bytes", () => {
69
+ const render = fixtureRenderManifest({
70
+ contentDigest: "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
71
+ });
72
+ const { manifest } = addArchiveHelmRender({ path: "chart.render.yaml", render });
73
+ expect(findArchiveEntry(manifest, "chart.render.yaml")?.digest).toBe(render.contentDigest);
74
+ });
75
+
76
+ it("the render rolls into manifestDigest — adding it changes the aggregate digest", () => {
77
+ const empty = createBuildArchiveManifest("my-component");
78
+ const { manifest } = addArchiveHelmRender({
79
+ path: "chart.render.yaml",
80
+ render: fixtureRenderManifest(),
81
+ manifest: empty,
82
+ });
83
+ expect(manifest.manifestDigest).not.toBe(empty.manifestDigest);
84
+ });
85
+
86
+ it("accumulates onto a caller-supplied manifest alongside other archive entries", () => {
87
+ const staged = addArchiveEntry(createBuildArchiveManifest("my-component"), {
88
+ kind: "image",
89
+ path: "app.tar",
90
+ digest: "sha256:img1",
91
+ });
92
+
93
+ const { manifest } = addArchiveHelmRender({
94
+ path: "chart.render.yaml",
95
+ render: fixtureRenderManifest(),
96
+ manifest: staged,
97
+ });
98
+
99
+ expect(manifest.contents.map((e) => e.kind).sort()).toEqual(["image", "template"]);
100
+ });
101
+
102
+ it("records provenance from an explicit sourceRef", () => {
103
+ const { manifest } = addArchiveHelmRender({
104
+ path: "chart.render.yaml",
105
+ render: fixtureRenderManifest(),
106
+ sourceRef: "abc123",
107
+ });
108
+ const entry = findArchiveEntry(manifest, "chart.render.yaml");
109
+ expect(entry?.provenance).toEqual({ sourceRef: "abc123", artifactDigest: RENDER_CONTENT_DIGEST });
110
+ });
111
+
112
+ it("falls back to the render's own sourceRef when the caller omits one", () => {
113
+ const { manifest } = addArchiveHelmRender({
114
+ path: "chart.render.yaml",
115
+ render: fixtureRenderManifest({ sourceRef: "deadbeef" }),
116
+ });
117
+ expect(findArchiveEntry(manifest, "chart.render.yaml")?.provenance).toEqual({
118
+ sourceRef: "deadbeef",
119
+ artifactDigest: RENDER_CONTENT_DIGEST,
120
+ });
121
+ });
122
+
123
+ it("records no provenance when neither the caller nor the render supplies a sourceRef", () => {
124
+ const { manifest } = addArchiveHelmRender({ path: "chart.render.yaml", render: fixtureRenderManifest() });
125
+ expect(findArchiveEntry(manifest, "chart.render.yaml")?.provenance).toBeUndefined();
126
+ });
127
+
128
+ it("assigns the template-kind default reproducibility basis (deterministic-synthesis)", () => {
129
+ const { manifest } = addArchiveHelmRender({ path: "chart.render.yaml", render: fixtureRenderManifest() });
130
+ expect(findArchiveEntry(manifest, "chart.render.yaml")?.reproducibility).toEqual({
131
+ basis: "deterministic-synthesis",
132
+ verifyBy: "re-synth",
133
+ });
134
+ });
135
+ });
136
+
137
+ describe("a render's digest is classified by the referrer lookup exactly like an image's (#1240)", () => {
138
+ const REPO = "123.dkr.ecr.us-east-1.amazonaws.com/external-secrets";
139
+
140
+ function stubRunner(stdout: string): ProcessRunner {
141
+ return {
142
+ async run() {
143
+ return { stdout, stderr: "" };
144
+ },
145
+ async available() {
146
+ return true;
147
+ },
148
+ };
149
+ }
150
+
151
+ it("discovers and classifies an SBOM referrer attached to the render's own contentDigest", async () => {
152
+ const render = fixtureRenderManifest();
153
+ // The archive entry this referrer would attach to — its `kind: "template"` /
154
+ // `HELM_RENDER_MEDIA_TYPE` never enter the lookup below, which discovers by digest alone.
155
+ addArchiveHelmRender({ path: "chart.render.yaml", render });
156
+
157
+ const runner = stubRunner(
158
+ JSON.stringify({
159
+ manifests: [
160
+ {
161
+ digest: "sha256:sbom1",
162
+ mediaType: "application/vnd.oci.image.manifest.v1+json",
163
+ artifactType: "application/spdx+json",
164
+ },
165
+ ],
166
+ }),
167
+ );
168
+ const lookup = createOrasReferrerLookup({ repo: REPO, runner });
169
+
170
+ const referrers = await lookup.discover(render.contentDigest);
171
+
172
+ expect(referrers).toEqual([
173
+ { kind: "sbom", mediaType: "application/spdx+json", digest: "sha256:sbom1", location: `${REPO}@sha256:sbom1` },
174
+ ]);
175
+ });
176
+
177
+ it("discovers a provenance and a signature referrer the same way, unaffected by the subject entry's own template kind/media type", async () => {
178
+ const render = fixtureRenderManifest();
179
+ const runner = stubRunner(
180
+ JSON.stringify({
181
+ manifests: [
182
+ {
183
+ digest: "sha256:prov1",
184
+ mediaType: "application/vnd.oci.image.manifest.v1+json",
185
+ artifactType: "application/vnd.in-toto+json",
186
+ },
187
+ {
188
+ digest: "sha256:sig1",
189
+ mediaType: "application/vnd.dev.cosign.simplesigning.v1+json",
190
+ artifactType: "application/vnd.dev.cosign.signature",
191
+ },
192
+ ],
193
+ }),
194
+ );
195
+ const lookup = createOrasReferrerLookup({ repo: REPO, runner });
196
+
197
+ const referrers = await lookup.discover(render.contentDigest);
198
+
199
+ expect(referrers.map((r) => r.kind)).toEqual(["provenance", "signature"]);
200
+ });
201
+ });
@@ -0,0 +1,119 @@
1
+ /**
2
+ * `addArchiveHelmRender` — fold a pinned render into a `BuildArchiveManifest`
3
+ * (#1240, epic #1228 Phase 3).
4
+ *
5
+ * #1238 gave a pinned render durable, content-addressed storage
6
+ * (./render-store.ts's `RenderManifest`, modeled on core's
7
+ * `BuildArchiveManifest`). This module is the seam between the two: it folds
8
+ * one render into a *build archive*'s manifest, the same self-contained
9
+ * bundle `docker-build`/`addArchiveTemplate` write into
10
+ * (packages/core/src/components/verbs/build-archive.ts), so a component that
11
+ * composes a `HelmRender` alongside an image/template gets one archive that
12
+ * carries all of it.
13
+ *
14
+ * A rendered chart is a template: it reuses `kind: "template"` rather than
15
+ * adding a new `BuildArchiveEntryKind`. What distinguishes a render from a
16
+ * synthesized CloudFormation/other IaC template is only its media type,
17
+ * `HELM_RENDER_MEDIA_TYPE` — and that is enough. Core's
18
+ * `lifecycle/oras-referrer-lookup.ts` classifies *referrers* (SBOM/
19
+ * provenance/signature) by the referrer's own media type, keyed only by the
20
+ * subject *digest* it discovers against — never by the subject entry's own
21
+ * `kind`/`mediaType`. A render's digest is therefore attachable exactly like
22
+ * an image's: `oras discover <repo>@<renderDigest>` finds the same
23
+ * referrers a signing/attestation pipeline would attach to any other
24
+ * artifact, with no change to that lookup needed (see
25
+ * ./archive-render.test.ts for the referrer-classification proof).
26
+ *
27
+ * Mirrors `addArchiveTemplate` (packages/core/src/components/verbs/build.ts)
28
+ * in every way that matters: a plain, pure function (not a registered
29
+ * capability — rendering happens at chant's existing build/synth time, not
30
+ * as a new deploy-time verb), accumulates onto a caller-supplied manifest so
31
+ * a component's whole build phase shares one archive, and threads an
32
+ * optional `sourceRef` into a `provenance` link (#614). It differs in one
33
+ * respect: rather than recomputing a digest from raw content bytes (core's
34
+ * `contentDigest`), it reuses the render's own `contentDigest` — the real
35
+ * `sha256:` over the canonical rendered stream that `helmContentDigest`
36
+ * already computed and `persistHelmRender` already wrote to the render
37
+ * store — so the archive entry's identity and the render store's identity
38
+ * are the same digest, not two independent hashes of the same bytes.
39
+ */
40
+
41
+ import {
42
+ addArchiveEntry,
43
+ createBuildArchiveManifest,
44
+ type BuildArchiveManifest,
45
+ } from "@intentius/chant/components/verbs/build-archive";
46
+ import type { ProvenanceLink } from "@intentius/chant/components/verbs/reproducibility";
47
+
48
+ import type { RenderManifest } from "./render-store";
49
+
50
+ /**
51
+ * Media type for a pinned helm render folded into a build archive as a
52
+ * `template`-kind entry. `v1` names the canonical-stream shape
53
+ * `canonicalizeRender` (./render-digest.ts) produces — a versioned suffix so
54
+ * a future incompatible canonicalization can be told apart from this one
55
+ * without guessing from content alone.
56
+ */
57
+ export const HELM_RENDER_MEDIA_TYPE = "application/vnd.chant.helm-render.v1+yaml";
58
+
59
+ export interface AddArchiveHelmRenderInput {
60
+ /** Where the render is written inside the build archive (e.g. `"external-secrets.render.yaml"`). */
61
+ path: string;
62
+ /**
63
+ * The persisted render this entry describes — see ./render-store.ts's
64
+ * `persistHelmRender`/`loadRenderManifest`. Supplies the entry's digest
65
+ * (`RenderManifest.contentDigest`, the same identity the render store
66
+ * keys its content-addressed entry under).
67
+ */
68
+ render: RenderManifest;
69
+ /**
70
+ * Manifest to extend, so a component's whole build phase (image/template +
71
+ * render) accumulates one manifest — the same accumulation convention
72
+ * `addArchiveTemplate`/`generate-sbom`'s `manifest` input uses. Omit to
73
+ * start a new manifest holding just this render.
74
+ */
75
+ manifest?: BuildArchiveManifest;
76
+ /**
77
+ * Source ref/commit this render was produced from (#614), recorded as the
78
+ * entry's `provenance.sourceRef`. Defaults to `render.sourceRef` when the
79
+ * caller omits it — the render already carries this when known; no
80
+ * `provenance` is recorded when neither supplies one.
81
+ */
82
+ sourceRef?: string;
83
+ }
84
+
85
+ export interface AddArchiveHelmRenderOutput {
86
+ /** The render's content digest — `render.contentDigest`, unchanged; returned for symmetry with `addArchiveTemplate`'s output. */
87
+ digest: string;
88
+ /** The build archive's manifest, now including this render's entry. */
89
+ manifest: BuildArchiveManifest;
90
+ }
91
+
92
+ /**
93
+ * Fold one pinned render into a build archive's manifest as a
94
+ * `template`-kind entry with `mediaType: HELM_RENDER_MEDIA_TYPE` — the
95
+ * render peer of `addArchiveTemplate`. The entry's digest is the render's
96
+ * own `contentDigest` (real `sha256:` over the canonical rendered stream,
97
+ * ./render-digest.ts's `helmContentDigest`), so the same digest that keys
98
+ * the render store's `sha256-<hex>/` entry (./render-store.ts) also keys
99
+ * this archive entry — one identity, not a second hash of the same bytes.
100
+ * Adding the entry recomputes `manifest.manifestDigest`
101
+ * (`computeManifestDigest`), so the render rolls into the archive's own
102
+ * aggregate identity exactly as an image or template does.
103
+ */
104
+ export function addArchiveHelmRender(input: AddArchiveHelmRenderInput): AddArchiveHelmRenderOutput {
105
+ const digest = input.render.contentDigest;
106
+ const base = input.manifest ?? createBuildArchiveManifest("unknown");
107
+ const sourceRef = input.sourceRef ?? input.render.sourceRef ?? undefined;
108
+ const provenance: ProvenanceLink | undefined = sourceRef
109
+ ? { sourceRef, artifactDigest: digest }
110
+ : undefined;
111
+ const manifest = addArchiveEntry(base, {
112
+ kind: "template",
113
+ path: input.path,
114
+ digest,
115
+ mediaType: HELM_RENDER_MEDIA_TYPE,
116
+ ...(provenance ? { provenance } : {}),
117
+ });
118
+ return { digest, manifest };
119
+ }