@intentius/chant-lexicon-k8s 0.30.0 → 0.32.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 (137) hide show
  1. package/dist/api/classify.d.ts +65 -0
  2. package/dist/api/classify.d.ts.map +1 -0
  3. package/dist/api/connect.d.ts +58 -0
  4. package/dist/api/connect.d.ts.map +1 -0
  5. package/dist/api/fake-cluster.d.ts +55 -0
  6. package/dist/api/fake-cluster.d.ts.map +1 -0
  7. package/dist/api/operation-surface.d.ts +64 -0
  8. package/dist/api/operation-surface.d.ts.map +1 -0
  9. package/dist/api/owner-chain.d.ts +46 -0
  10. package/dist/api/owner-chain.d.ts.map +1 -0
  11. package/dist/api/sweep-types.d.ts +17 -0
  12. package/dist/api/sweep-types.d.ts.map +1 -0
  13. package/dist/codegen/generate-operations.d.ts +29 -0
  14. package/dist/codegen/generate-operations.d.ts.map +1 -0
  15. package/dist/codegen/generate.d.ts.map +1 -1
  16. package/dist/config.d.ts +17 -0
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/crd/parser.d.ts.map +1 -1
  19. package/dist/crd/types.d.ts +7 -0
  20. package/dist/crd/types.d.ts.map +1 -1
  21. package/dist/deep-observe-hooks.d.ts +86 -0
  22. package/dist/deep-observe-hooks.d.ts.map +1 -0
  23. package/dist/deep-observe.d.ts +146 -0
  24. package/dist/deep-observe.d.ts.map +1 -0
  25. package/dist/describe-resources.d.ts +58 -26
  26. package/dist/describe-resources.d.ts.map +1 -1
  27. package/dist/export-resources.d.ts +31 -1
  28. package/dist/export-resources.d.ts.map +1 -1
  29. package/dist/integrity.json +2 -2
  30. package/dist/kube/apply.d.ts +26 -0
  31. package/dist/kube/apply.d.ts.map +1 -0
  32. package/dist/kube/connect.d.ts +30 -0
  33. package/dist/kube/connect.d.ts.map +1 -0
  34. package/dist/kube/delete.d.ts +27 -0
  35. package/dist/kube/delete.d.ts.map +1 -0
  36. package/dist/kube/describe.d.ts +19 -0
  37. package/dist/kube/describe.d.ts.map +1 -0
  38. package/dist/kube/events.d.ts +34 -0
  39. package/dist/kube/events.d.ts.map +1 -0
  40. package/dist/kube/flags.d.ts +40 -0
  41. package/dist/kube/flags.d.ts.map +1 -0
  42. package/dist/kube/get.d.ts +21 -0
  43. package/dist/kube/get.d.ts.map +1 -0
  44. package/dist/kube/group.d.ts +19 -0
  45. package/dist/kube/group.d.ts.map +1 -0
  46. package/dist/kube/logs.d.ts +16 -0
  47. package/dist/kube/logs.d.ts.map +1 -0
  48. package/dist/kube/project.d.ts +59 -0
  49. package/dist/kube/project.d.ts.map +1 -0
  50. package/dist/kube/render.d.ts +75 -0
  51. package/dist/kube/render.d.ts.map +1 -0
  52. package/dist/kube/source.d.ts +19 -0
  53. package/dist/kube/source.d.ts.map +1 -0
  54. package/dist/kube/target.d.ts +52 -0
  55. package/dist/kube/target.d.ts.map +1 -0
  56. package/dist/kube/testing.d.ts +21 -0
  57. package/dist/kube/testing.d.ts.map +1 -0
  58. package/dist/kube/top.d.ts +23 -0
  59. package/dist/kube/top.d.ts.map +1 -0
  60. package/dist/kube/verdict.d.ts +45 -0
  61. package/dist/kube/verdict.d.ts.map +1 -0
  62. package/dist/kube/wait.d.ts +23 -0
  63. package/dist/kube/wait.d.ts.map +1 -0
  64. package/dist/manifest.json +1 -1
  65. package/dist/op/activities/index.d.ts +7 -4
  66. package/dist/op/activities/index.d.ts.map +1 -1
  67. package/dist/op/activities/kubectl.d.ts +120 -2
  68. package/dist/op/activities/kubectl.d.ts.map +1 -1
  69. package/dist/op/activities/wait-for-ready.d.ts +30 -3
  70. package/dist/op/activities/wait-for-ready.d.ts.map +1 -1
  71. package/dist/plugin.d.ts.map +1 -1
  72. package/dist/spec/parse.d.ts +42 -0
  73. package/dist/spec/parse.d.ts.map +1 -1
  74. package/package.json +5 -2
  75. package/src/api/classify.test.ts +133 -0
  76. package/src/api/classify.ts +144 -0
  77. package/src/api/connect.ts +104 -0
  78. package/src/api/fake-cluster.ts +218 -0
  79. package/src/api/operation-surface.test.ts +116 -0
  80. package/src/api/operation-surface.ts +129 -0
  81. package/src/api/owner-chain.test.ts +171 -0
  82. package/src/api/owner-chain.ts +144 -0
  83. package/src/api/sweep-types.ts +36 -0
  84. package/src/codegen/generate-operations.ts +56 -0
  85. package/src/codegen/generate.ts +9 -0
  86. package/src/config.ts +17 -0
  87. package/src/crd/parser.ts +8 -0
  88. package/src/crd/types.ts +7 -0
  89. package/src/deep-observe-hooks.ts +125 -0
  90. package/src/deep-observe.test.ts +668 -0
  91. package/src/deep-observe.ts +299 -0
  92. package/src/describe-resources.test.ts +619 -191
  93. package/src/describe-resources.ts +218 -117
  94. package/src/export-resources-io.test.ts +76 -51
  95. package/src/export-resources.ts +47 -35
  96. package/src/generated/operations.json +2156 -0
  97. package/src/kube/apply.test.ts +105 -0
  98. package/src/kube/apply.ts +101 -0
  99. package/src/kube/connect.ts +54 -0
  100. package/src/kube/delete.test.ts +123 -0
  101. package/src/kube/delete.ts +174 -0
  102. package/src/kube/describe.test.ts +106 -0
  103. package/src/kube/describe.ts +195 -0
  104. package/src/kube/events.test.ts +115 -0
  105. package/src/kube/events.ts +160 -0
  106. package/src/kube/flags.test.ts +65 -0
  107. package/src/kube/flags.ts +114 -0
  108. package/src/kube/get.test.ts +195 -0
  109. package/src/kube/get.ts +190 -0
  110. package/src/kube/group.test.ts +33 -0
  111. package/src/kube/group.ts +125 -0
  112. package/src/kube/logs.test.ts +126 -0
  113. package/src/kube/logs.ts +96 -0
  114. package/src/kube/project.test.ts +57 -0
  115. package/src/kube/project.ts +106 -0
  116. package/src/kube/render.test.ts +142 -0
  117. package/src/kube/render.ts +252 -0
  118. package/src/kube/source.test.ts +138 -0
  119. package/src/kube/source.ts +139 -0
  120. package/src/kube/target.test.ts +67 -0
  121. package/src/kube/target.ts +89 -0
  122. package/src/kube/testing.ts +42 -0
  123. package/src/kube/top.test.ts +149 -0
  124. package/src/kube/top.ts +120 -0
  125. package/src/kube/verdict.test.ts +48 -0
  126. package/src/kube/verdict.ts +77 -0
  127. package/src/kube/wait.test.ts +125 -0
  128. package/src/kube/wait.ts +160 -0
  129. package/src/lifecycle-integration.test.ts +132 -92
  130. package/src/op/activities/index.ts +7 -3
  131. package/src/op/activities/kubectl.test.ts +420 -0
  132. package/src/op/activities/kubectl.ts +379 -13
  133. package/src/op/activities/wait-for-ready.test.ts +94 -0
  134. package/src/op/activities/wait-for-ready.ts +66 -15
  135. package/src/plugin.test.ts +58 -1
  136. package/src/plugin.ts +29 -0
  137. package/src/spec/parse.ts +93 -1
@@ -1,34 +1,41 @@
1
1
  /**
2
2
  * Cross-lexicon lifecycle integration (#163) — Kubernetes row.
3
3
  *
4
- * Drives the REAL k8sPlugin through core's live-import driver and the changeset
5
- * path, with the `kubectl` edge mocked.
4
+ * Drives the REAL k8sPlugin through core's live-import driver and the
5
+ * changeset path, with the cluster edge faked at
6
+ * `@kubernetes/client-node`'s request layer (chant #1074, previously at
7
+ * `kubectl`). The plugin's own `describeResources` / `exportResources` are what
8
+ * run; only the socket is replaced, and only a literal kubeconfig is ever read.
6
9
  */
7
10
  import { describe, test, expect, vi, beforeEach } from "vitest";
8
11
  import { mkdtempSync, rmSync, readdirSync, readFileSync } from "node:fs";
9
12
  import { tmpdir } from "node:os";
10
13
  import { join } from "node:path";
11
14
 
12
- const execMock = vi.fn();
13
- vi.mock("node:child_process", async () => {
14
- const actual = await vi.importActual<typeof import("node:child_process")>("node:child_process");
15
+ /**
16
+ * The plugin reaches the cluster through `./api/connect`'s default connector,
17
+ * which is the one thing a test must not let run for real: it would read the
18
+ * developer's kubeconfig. Replacing the module swaps in a connector backed by
19
+ * `fakeCluster`, which still builds a real client over a literal kubeconfig.
20
+ */
21
+ const clusterState: { objects: Record<string, unknown>; fail?: (path: string) => { status: number; body: unknown } | undefined } = {
22
+ objects: {},
23
+ };
24
+
25
+ vi.mock("./api/connect", async () => {
26
+ const { fakeCluster } = await import("./api/fake-cluster");
15
27
  return {
16
- ...actual,
17
- exec: (
18
- cmd: string,
19
- cb: (err: Error | null, out: { stdout: string; stderr: string }) => void,
20
- ) => {
21
- const r = execMock(cmd);
22
- queueMicrotask(() =>
23
- r instanceof Error
24
- ? cb(r, { stdout: "", stderr: "" })
25
- : cb(null, r as { stdout: string; stderr: string }),
26
- );
27
- },
28
+ defaultK8sConnector: (options: unknown) =>
29
+ fakeCluster({
30
+ objects: clusterState.objects as never,
31
+ respond: (req) => clusterState.fail?.(req.path),
32
+ }).connector(options as never),
28
33
  };
29
34
  });
30
35
 
31
36
  const { k8sPlugin } = await import("./plugin");
37
+ const { objectKey } = await import("./api/fake-cluster");
38
+ const { statusBody } = await import("@intentius/chant-k8s-client/testing");
32
39
  const { liveImportFromPlugins } = await import("@intentius/chant/cli/commands/import");
33
40
  const { buildChangeSet } = await import("@intentius/chant/lifecycle/change-set");
34
41
  const { normalizeObservation } = await import("@intentius/chant/observation");
@@ -42,24 +49,24 @@ const liveDeployment = {
42
49
  spec: { replicas: 3, selector: { matchLabels: { app: "web" } } },
43
50
  };
44
51
 
45
- const emptyList = { stdout: JSON.stringify({ items: [] }), stderr: "" };
52
+ const observedWeb = {
53
+ apiVersion: "apps/v1",
54
+ kind: "Deployment",
55
+ metadata: { name: "web", namespace: "prod", uid: "uid-1" },
56
+ status: { readyReplicas: 3, replicas: 3 },
57
+ };
46
58
 
47
59
  describe("k8s lifecycle integration (#163)", () => {
48
- beforeEach(() => execMock.mockReset());
60
+ beforeEach(() => {
61
+ clusterState.objects = {};
62
+ clusterState.fail = undefined;
63
+ });
49
64
 
50
65
  test("live-import driver: real exportResources → IR → generated source", async () => {
51
- execMock.mockImplementation((cmd?: string) =>
52
- cmd?.includes("get deployment.apps")
53
- ? { stdout: JSON.stringify({ items: [liveDeployment] }), stderr: "" }
54
- : emptyList,
55
- );
66
+ clusterState.objects = { [objectKey("apps/v1", "Deployment", "web", "default")]: liveDeployment };
56
67
  const output = mkdtempSync(join(tmpdir(), "chant-k8s-li-"));
57
68
  try {
58
- const result = await liveImportFromPlugins([k8sPlugin], {
59
- environment: "prod",
60
- output,
61
- force: true,
62
- });
69
+ const result = await liveImportFromPlugins([k8sPlugin], { environment: "prod", output, force: true });
63
70
  expect(result.success).toBe(true);
64
71
  expect(result.generatedFiles.length).toBeGreaterThan(0);
65
72
  const all = readdirSync(output)
@@ -73,17 +80,7 @@ describe("k8s lifecycle integration (#163)", () => {
73
80
  });
74
81
 
75
82
  test("changeset path: real describeResources → buildChangeSet verdicts", async () => {
76
- execMock.mockImplementation((cmd?: string) =>
77
- cmd?.includes("deployment.apps web")
78
- ? {
79
- stdout: JSON.stringify({
80
- metadata: { name: "web", namespace: "prod", uid: "uid-1" },
81
- status: { readyReplicas: 3, replicas: 3 },
82
- }),
83
- stderr: "",
84
- }
85
- : new Error("not found"),
86
- );
83
+ clusterState.objects = { [objectKey("apps/v1", "Deployment", "web", "prod")]: observedWeb };
87
84
 
88
85
  const { resources: observedNow } = normalizeObservation(
89
86
  await k8sPlugin.describeResources!({
@@ -97,45 +94,32 @@ describe("k8s lifecycle integration (#163)", () => {
97
94
  );
98
95
  expect(observedNow.web?.type).toBe("K8s::Apps::Deployment");
99
96
 
100
- const cs = buildChangeSet("prod", {
101
- declared: new Set(["webSvc"]),
102
- observedNow,
103
- observedThen: undefined,
104
- });
97
+ const cs = buildChangeSet("prod", { declared: new Set(["webSvc"]), observedNow, observedThen: undefined });
105
98
  const byName = Object.fromEntries(cs.entries.map((e) => [e.name, e.action]));
106
99
  expect(byName.webSvc).toBe("create");
107
100
  expect(byName.web).toBe("adopt");
108
101
 
109
- const cs2 = buildChangeSet("prod", {
110
- declared: new Set(["web"]),
111
- observedNow,
112
- observedThen: undefined,
113
- });
102
+ const cs2 = buildChangeSet("prod", { declared: new Set(["web"]), observedNow, observedThen: undefined });
114
103
  expect(cs2.entries.find((e) => e.name === "web")!.action).toBe("noop");
115
104
  });
116
105
 
117
106
  /**
118
- * The #1089 chain, end to end on the real plugin: a declared CRD the lexicon
119
- * has no reader for goes describe plan component status without ever
120
- * turning into a create or a "stale" component.
107
+ * The #1089 chain, end to end on the real plugin. The entity that used to
108
+ * demonstrate it a CRD with no `KUBECTL_RESOURCE` entry is no longer a
109
+ * hole, because #1074 removed the map; an RBAC-denied read is what produces
110
+ * a hole now, and it has to survive describe → plan → status the same way.
121
111
  */
122
- test("tri-state chain: an unreadable CRD stays unobserved through describe → plan → status (#1089)", async () => {
123
- execMock.mockImplementation((cmd?: string) =>
124
- cmd?.includes("deployment.apps web")
125
- ? {
126
- stdout: JSON.stringify({
127
- metadata: { name: "web", namespace: "prod", uid: "uid-1" },
128
- status: { readyReplicas: 3, replicas: 3 },
129
- }),
130
- stderr: "",
131
- }
132
- : new Error('Error from server (NotFound): deployments.apps "gone" not found'),
133
- );
112
+ test("tri-state chain: an unreadable entity stays unobserved through describe → plan → status (#1089)", async () => {
113
+ clusterState.objects = { [objectKey("apps/v1", "Deployment", "web", "prod")]: observedWeb };
114
+ clusterState.fail = (path) =>
115
+ path.endsWith("/deployments/widget")
116
+ ? { status: 403, body: statusBody(403, "Forbidden", 'deployments.apps "widget" is forbidden') }
117
+ : undefined;
134
118
 
135
119
  const entities = new Map([
136
120
  ["web", { entityType: "K8s::Apps::Deployment", props: { metadata: { name: "web", namespace: "prod" } } }],
137
121
  ["gone", { entityType: "K8s::Apps::Deployment", props: { metadata: { name: "gone", namespace: "prod" } } }],
138
- ["widget", { entityType: "K8s::Example::Widget", props: { metadata: { name: "widget", namespace: "prod" } } }],
122
+ ["widget", { entityType: "K8s::Apps::Deployment", props: { metadata: { name: "widget", namespace: "prod" } } }],
139
123
  ]);
140
124
 
141
125
  // 1. describe — three declared entities, three different verdicts.
@@ -148,10 +132,11 @@ describe("k8s lifecycle integration (#163)", () => {
148
132
  }),
149
133
  );
150
134
  expect(Object.keys(observed.resources)).toEqual(["web"]);
151
- expect(observed.unobserved.widget.reason).toBe("unsupported-kind");
135
+ expect(observed.unobserved.widget.reason).toBe("no-credentials");
152
136
  expect(observed.unobserved.gone).toBeUndefined(); // NotFound is an absence
153
137
 
154
- // 2. plan — the CRD is `unobserved`; only the confirmed-absent one is a create.
138
+ // 2. plan — the unreadable one is `unobserved`; only the confirmed-absent
139
+ // one is a create.
155
140
  const cs = buildChangeSet("prod", {
156
141
  declared: new Set(entities.keys()),
157
142
  observedNow: observed.resources,
@@ -163,15 +148,64 @@ describe("k8s lifecycle integration (#163)", () => {
163
148
 
164
149
  // 3. status — a recorded component whose entity was never read reports
165
150
  // `unknown`, not `stale`, and carries no `live` boolean at all.
166
- const rows = reconcileStatus("prod", [
167
- { component: "widget", env: "prod", digest: "sha256:abc", gitSha: "g", runId: "r", timestamp: "2026-01-01T00:00:00Z", actor: "ci" },
168
- ], { liveEvidence: liveEvidenceFromChangeSet(cs) });
151
+ const rows = reconcileStatus(
152
+ "prod",
153
+ [
154
+ {
155
+ component: "widget",
156
+ env: "prod",
157
+ digest: "sha256:abc",
158
+ gitSha: "g",
159
+ runId: "r",
160
+ timestamp: "2026-01-01T00:00:00Z",
161
+ actor: "ci",
162
+ },
163
+ ],
164
+ { liveEvidence: liveEvidenceFromChangeSet(cs) },
165
+ );
169
166
  const widgetRow = rows.find((r) => r.component === "widget")!;
170
167
  expect(widgetRow.reconciliation).toBe("unknown");
171
168
  expect(widgetRow.live).toBeUndefined();
172
- expect(widgetRow.unobserved?.reason).toBe("unsupported-kind");
169
+ expect(widgetRow.unobserved?.reason).toBe("no-credentials");
173
170
  expect(widgetRow.detail).toContain("could not be observed");
174
171
  });
172
+
173
+ /**
174
+ * chant #1074's coverage claim, on the real plugin: a CRD that the old path
175
+ * warn-skipped as `unsupported-kind` now reads like anything else.
176
+ */
177
+ test("a CRD is observed, not skipped (chant #1074)", async () => {
178
+ clusterState.objects = {
179
+ [objectKey("ray.io/v1", "RayCluster", "ml", "ray")]: {
180
+ apiVersion: "ray.io/v1",
181
+ kind: "RayCluster",
182
+ metadata: { name: "ml", namespace: "ray", uid: "uid-ray" },
183
+ status: { phase: "ready" },
184
+ },
185
+ };
186
+
187
+ const observed = normalizeObservation(
188
+ await k8sPlugin.describeResources!({
189
+ environment: "prod",
190
+ buildOutput: "",
191
+ entityNames: ["ml"],
192
+ entities: new Map([
193
+ ["ml", { entityType: "K8s::Ray::RayCluster", props: { metadata: { name: "ml", namespace: "ray" } } }],
194
+ ]),
195
+ }),
196
+ );
197
+
198
+ expect(observed.unobserved).toEqual({});
199
+ expect(observed.resources.ml?.physicalId).toBe("uid-ray");
200
+
201
+ const cs = buildChangeSet("prod", {
202
+ declared: new Set(["ml"]),
203
+ observedNow: observed.resources,
204
+ observedThen: undefined,
205
+ unobserved: observed.unobserved,
206
+ });
207
+ expect(cs.entries.find((e) => e.name === "ml")!.action).toBe("noop");
208
+ });
175
209
  });
176
210
 
177
211
  // The shared conformance suite (#1089) — every observing lexicon runs it.
@@ -179,18 +213,22 @@ describeObservationConformance({
179
213
  lexicon: "k8s",
180
214
  scenarios: [
181
215
  {
182
- name: "a CRD kind with no reader",
216
+ name: "an RBAC-denied read alongside a confirmed absence",
183
217
  declared: ["widget", "gone"],
184
218
  expectUnobserved: ["widget"],
185
219
  expectAbsent: ["gone"],
186
220
  run: () => {
187
- execMock.mockImplementation(() => new Error('Error from server (NotFound): deployments.apps "gone" not found'));
221
+ clusterState.objects = {};
222
+ clusterState.fail = (path) =>
223
+ path.endsWith("/deployments/widget")
224
+ ? { status: 403, body: statusBody(403, "Forbidden", "forbidden") }
225
+ : undefined;
188
226
  return k8sPlugin.describeResources!({
189
227
  environment: "prod",
190
228
  buildOutput: "",
191
229
  entityNames: ["widget", "gone"],
192
230
  entities: new Map([
193
- ["widget", { entityType: "K8s::Example::Widget", props: { metadata: { name: "widget" } } }],
231
+ ["widget", { entityType: "K8s::Apps::Deployment", props: { metadata: { name: "widget" } } }],
194
232
  ["gone", { entityType: "K8s::Apps::Deployment", props: { metadata: { name: "gone" } } }],
195
233
  ]),
196
234
  });
@@ -201,18 +239,15 @@ describeObservationConformance({
201
239
  declared: ["web"],
202
240
  expectUnobserved: ["web"],
203
241
  run: () => {
204
- execMock.mockImplementation(() =>
205
- Object.assign(new Error("kubectl failed"), {
206
- stderr: "The connection to the server 127.0.0.1:6443 was refused - did you specify the right host or port?",
207
- }),
208
- );
242
+ clusterState.objects = {};
243
+ clusterState.fail = () => {
244
+ throw new Error("connect ECONNREFUSED 127.0.0.1:6443");
245
+ };
209
246
  return k8sPlugin.describeResources!({
210
247
  environment: "prod",
211
248
  buildOutput: "",
212
249
  entityNames: ["web"],
213
- entities: new Map([
214
- ["web", { entityType: "K8s::Apps::Deployment", props: { metadata: { name: "web" } } }],
215
- ]),
250
+ entities: new Map([["web", { entityType: "K8s::Apps::Deployment", props: { metadata: { name: "web" } } }]]),
216
251
  });
217
252
  },
218
253
  },
@@ -221,20 +256,25 @@ describeObservationConformance({
221
256
  declared: ["web"],
222
257
  expectPresent: ["web"],
223
258
  run: () => {
224
- execMock.mockImplementation(() => ({
225
- stdout: JSON.stringify({
226
- metadata: { name: "web", namespace: "prod", uid: "uid-1", labels: { "app.kubernetes.io/managed-by": "chant" } },
259
+ clusterState.fail = undefined;
260
+ clusterState.objects = {
261
+ [objectKey("apps/v1", "Deployment", "web", "default")]: {
262
+ apiVersion: "apps/v1",
263
+ kind: "Deployment",
264
+ metadata: {
265
+ name: "web",
266
+ namespace: "default",
267
+ uid: "uid-1",
268
+ labels: { "app.kubernetes.io/managed-by": "chant" },
269
+ },
227
270
  status: { readyReplicas: 1, replicas: 1 },
228
- }),
229
- stderr: "",
230
- }));
271
+ },
272
+ };
231
273
  return k8sPlugin.describeResources!({
232
274
  environment: "prod",
233
275
  buildOutput: "",
234
276
  entityNames: ["web"],
235
- entities: new Map([
236
- ["web", { entityType: "K8s::Apps::Deployment", props: { metadata: { name: "web" } } }],
237
- ]),
277
+ entities: new Map([["web", { entityType: "K8s::Apps::Deployment", props: { metadata: { name: "web" } } }]]),
238
278
  });
239
279
  },
240
280
  },
@@ -2,7 +2,10 @@
2
2
  * k8s Op activities — resolved by the core activity registry when a project's
3
3
  * `chant.config.ts` lists the `k8s` lexicon. Relocated from the temporal lexicon
4
4
  * (#809) so Kubernetes-facing imperative activities live with their product:
5
- * - kubectlApply — `kubectl apply` a rendered manifest
5
+ * - kubectlApply — server-side apply a rendered manifest (and, since chant
6
+ * #1075, prune chant-owned objects it no longer declares; `applyManifest`
7
+ * is the same work with a report of what it did, which is what the
8
+ * Temporal lexicon's `nativeApply` dispatcher calls for a kubectl target)
6
9
  * - k3dUp / k3dDown — boot/tear down a local k3d cluster
7
10
  * - waitForArgoSync — block until an Argo CD Application is Healthy && Synced
8
11
  *
@@ -11,8 +14,8 @@
11
14
  * dependency-light — it shells out to a CLI and does not import the k8s declarable
12
15
  * surface — so a Temporal worker loads it cheaply.
13
16
  */
14
- export { kubectlApply } from "./kubectl";
15
- export type { KubectlApplyArgs } from "./kubectl";
17
+ export { kubectlApply, applyManifest, readManifestDocuments } from "./kubectl";
18
+ export type { KubectlApplyArgs, ApplyManifestResult, AppliedRef, ApplyDeleteMode } from "./kubectl";
16
19
 
17
20
  export { k3dUp, k3dDown, k3dUpCommand, k3dDownCommand, k3dExistsCommand } from "./k3d";
18
21
  export type { K3dUpArgs, K3dDownArgs } from "./k3d";
@@ -23,6 +26,7 @@ export type { WaitForArgoSyncArgs, ArgoAppStatus, ArgoStatusFetcher } from "./ar
23
26
  export {
24
27
  waitForReady,
25
28
  defaultResourceFetcher,
29
+ apiResourceFetcher,
26
30
  ReadinessFailedError,
27
31
  readinessFor,
28
32
  isReady,