@intentius/chant-k8s-client 0.41.20 → 0.44.1

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.
package/dist/client.d.ts CHANGED
@@ -153,6 +153,15 @@ export interface K8sClient {
153
153
  * such groupVersion — an answer, not a failure. Cached like `resolve`.
154
154
  */
155
155
  resources(apiVersion: string, signal?: AbortSignal): Promise<ApiResourceInfo[]>;
156
+ /**
157
+ * One groupVersion per API group the cluster serves — each group's own
158
+ * `preferredVersion` (falling back to its first listed version), with the
159
+ * core group's `v1` first (chant #1517). This is the discovery-driven
160
+ * sweep's enumeration unit: one version per group names every kind exactly
161
+ * once, without re-listing the same resources at a second served version.
162
+ * Cached for the client's lifetime, like the rest of discovery.
163
+ */
164
+ preferredGroupVersions(signal?: AbortSignal): Promise<string[]>;
156
165
  /** The API resource lists discovery has been asked for so far, for tests and diagnostics. */
157
166
  discoveryCacheKeys(): string[];
158
167
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/errors.d.ts CHANGED
@@ -30,6 +30,14 @@ export declare class K8sApiError extends Error {
30
30
  /** What was being addressed, e.g. `apps/v1 Deployment prod/web`. */
31
31
  readonly target?: string | undefined;
32
32
  readonly status?: K8sStatus | undefined;
33
+ /**
34
+ * Which cluster the failing read actually talked to, e.g.
35
+ * `context "k3d-fountain-local" (bound by k8s.profiles.local.context)`.
36
+ * Stamped by the client that issued the request (chant #1488) — a
37
+ * `read-failed` that does not name the cluster it read cost an afternoon on
38
+ * a laptop with two k3d clusters, so the failure carries it from birth.
39
+ */
40
+ contextNote?: string;
33
41
  constructor(statusCode: number, reason: string | undefined, apiMessage: string,
34
42
  /** What was being addressed, e.g. `apps/v1 Deployment prod/web`. */
35
43
  target?: string | undefined, status?: K8sStatus | undefined);
@@ -58,6 +66,8 @@ export declare class K8sApiError extends Error {
58
66
  */
59
67
  export declare class K8sTransportError extends Error {
60
68
  readonly target?: string | undefined;
69
+ /** Which cluster context the failed request was aimed at — see {@link K8sApiError.contextNote}. */
70
+ contextNote?: string;
61
71
  constructor(message: string, target?: string | undefined, options?: {
62
72
  cause?: unknown;
63
73
  });
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,+EAA+E;AAC/E,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,WAAY,SAAQ,KAAK;aAElB,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM,GAAG,SAAS;aAC1B,UAAU,EAAE,MAAM;IAClC,oEAAoE;aACpD,MAAM,CAAC,EAAE,MAAM;aACf,MAAM,CAAC,EAAE,SAAS;gBALlB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,UAAU,EAAE,MAAM;IAClC,oEAAoE;IACpD,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,MAAM,CAAC,EAAE,SAAS,YAAA;IASpC,0EAA0E;IAC1E,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,4EAA4E;IAC5E,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,8CAA8C;IAC9C,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW;CAapF;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;aAGxB,MAAM,CAAC,EAAE,MAAM;gBAD/B,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EAC/B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAMhC;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,KAAK,CAAC,EAAE,OAAO;CAS5B;AAED;;;;;;;GAOG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;aAEpC,OAAO,EAAE,MAAM;aACf,OAAO,EAAE,SAAS,MAAM,EAAE;gBAD1B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,MAAM,EAAE;CAU7C;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,4EAA4E;AAC5E,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;aAE3B,YAAY,EAAE,MAAM;gBAApB,YAAY,EAAE,MAAM,EACpC,OAAO,CAAC,EAAE,MAAM;CAKnB"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,+EAA+E;AAC/E,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,WAAY,SAAQ,KAAK;aAWlB,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM,GAAG,SAAS;aAC1B,UAAU,EAAE,MAAM;IAClC,oEAAoE;aACpD,MAAM,CAAC,EAAE,MAAM;aACf,MAAM,CAAC,EAAE,SAAS;IAfpC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;gBAGH,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,UAAU,EAAE,MAAM;IAClC,oEAAoE;IACpD,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,MAAM,CAAC,EAAE,SAAS,YAAA;IASpC,0EAA0E;IAC1E,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,4EAA4E;IAC5E,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,8CAA8C;IAC9C,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW;CAapF;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;aAMxB,MAAM,CAAC,EAAE,MAAM;IALjC,mGAAmG;IACnG,WAAW,CAAC,EAAE,MAAM,CAAC;gBAGnB,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EAC/B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAMhC;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,KAAK,CAAC,EAAE,OAAO;CAS5B;AAED;;;;;;;GAOG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;aAEpC,OAAO,EAAE,MAAM;aACf,OAAO,EAAE,SAAS,MAAM,EAAE;gBAD1B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,MAAM,EAAE;CAU7C;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,4EAA4E;AAC5E,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;aAE3B,YAAY,EAAE,MAAM;gBAApB,YAAY,EAAE,MAAM,EACpC,OAAO,CAAC,EAAE,MAAM;CAKnB"}
package/dist/types.d.ts CHANGED
@@ -115,6 +115,15 @@ export interface K8sClientOptions {
115
115
  concurrency?: number;
116
116
  /** Where the context came from, for provenance. Default `ambient`. */
117
117
  contextSource?: "bound" | "ambient";
118
+ /**
119
+ * How the context was selected, in words — e.g.
120
+ * `bound by k8s.profiles.prod.context` or
121
+ * `ambient; no k8s.profiles.local binding`. Stamped (with the resolved
122
+ * context name) onto every API/transport failure the client throws, so a
123
+ * `read-failed` reason names the cluster it read (chant #1488). Defaults to
124
+ * the bare `contextSource`.
125
+ */
126
+ contextLabel?: string;
118
127
  }
119
128
  /**
120
129
  * `@kubernetes/client-node`'s promise-shaped HTTP library: it receives the
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-k8s-client",
3
- "version": "0.41.20",
3
+ "version": "0.44.1",
4
4
  "description": "Typed Kubernetes API client for chant — the read/write path of the k8s lexicon, kept out of the build path",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant/lexicons/k8s/api-client/",
@@ -117,6 +117,47 @@ describe("createK8sClient — kubeconfig and credential policy", () => {
117
117
  expect(layer.requests).toHaveLength(0);
118
118
  });
119
119
 
120
+ test("an API failure names the context it read (#1488)", async () => {
121
+ const layer = cluster({}, (req) =>
122
+ req.path.endsWith("/deployments/web")
123
+ ? { status: 500, body: statusBody(500, "InternalError", "etcdserver: leader changed") }
124
+ : undefined,
125
+ );
126
+ const c = await client(layer);
127
+ const err = await c.read({ apiVersion: "apps/v1", kind: "Deployment", name: "web", namespace: "prod" }).catch((e: unknown) => e);
128
+ expect(err).toBeInstanceOf(K8sApiError);
129
+ expect((err as K8sApiError).contextNote).toBe('context "test-context" (ambient)');
130
+ });
131
+
132
+ test("a bound client's failures carry the binding story via contextLabel (#1488)", async () => {
133
+ const layer = cluster({}, (req) =>
134
+ req.path.endsWith("/deployments/web") ? { status: 503, body: statusBody(503, "ServiceUnavailable", "apiserver down") } : undefined,
135
+ );
136
+ const c = await createK8sClient({
137
+ kubeconfig: fakeKubeconfig({
138
+ contexts: [{ name: "staging-eks" }, { name: "prod-eks", cluster: "prod", user: "prod-user" }],
139
+ currentContext: "staging-eks",
140
+ }),
141
+ context: "prod-eks",
142
+ contextSource: "bound",
143
+ contextLabel: "bound by k8s.profiles.prod.context",
144
+ requestLayer: layer,
145
+ });
146
+ const err = await c.read({ apiVersion: "apps/v1", kind: "Deployment", name: "web", namespace: "prod" }).catch((e: unknown) => e);
147
+ expect(err).toBeInstanceOf(K8sApiError);
148
+ expect((err as K8sApiError).contextNote).toBe('context "prod-eks" (bound by k8s.profiles.prod.context)');
149
+ });
150
+
151
+ test("a transport failure names the context it was aimed at (#1488)", async () => {
152
+ const layer = fakeRequestLayer(() => {
153
+ throw new Error("connect ECONNREFUSED 127.0.0.1:6443");
154
+ });
155
+ const c = await createK8sClient({ kubeconfig: fakeKubeconfig(), requestLayer: layer });
156
+ const err = await c.read({ apiVersion: "apps/v1", kind: "Deployment", name: "web" }).catch((e: unknown) => e);
157
+ expect(err).toBeInstanceOf(K8sTransportError);
158
+ expect((err as K8sTransportError).contextNote).toBe('context "test-context" (ambient)');
159
+ });
160
+
120
161
  test("an exec credential plugin off the allowlist refuses before it can run", async () => {
121
162
  const layer = cluster();
122
163
  await expect(
@@ -201,6 +242,39 @@ describe("resource resolution through the cluster's own discovery", () => {
201
242
  expect(c.discoveryCacheKeys()).toEqual(["apps/v1"]);
202
243
  });
203
244
 
245
+ // chant #1517 — the runtime-children sweep's enumeration unit: one version
246
+ // per group, so every kind is named exactly once.
247
+ test("preferredGroupVersions returns core v1 plus each group's own preferredVersion, once", async () => {
248
+ const layer = fakeRequestLayer((req) => {
249
+ if (req.path === "/api") return { body: { kind: "APIVersions", versions: ["v1"] } };
250
+ if (req.path === "/apis") {
251
+ return {
252
+ body: {
253
+ kind: "APIGroupList",
254
+ groups: [
255
+ {
256
+ name: "apps",
257
+ preferredVersion: { groupVersion: "apps/v1", version: "v1" },
258
+ versions: [{ groupVersion: "apps/v1" }, { groupVersion: "apps/v1beta1" }],
259
+ },
260
+ // A group reporting no preferredVersion falls back to its first
261
+ // listed version rather than being dropped.
262
+ { name: "widgets.example.com", versions: [{ groupVersion: "widgets.example.com/v1alpha1" }] },
263
+ ],
264
+ },
265
+ };
266
+ }
267
+ return { status: 404, body: statusBody(404, "NotFound", `${req.path} not found`) };
268
+ });
269
+ const c = await client(layer);
270
+
271
+ expect(await c.preferredGroupVersions()).toEqual(["v1", "apps/v1", "widgets.example.com/v1alpha1"]);
272
+
273
+ // Cached: a second call issues no further discovery requests.
274
+ await c.preferredGroupVersions();
275
+ expect(layer.paths().filter((p) => p === "/apis")).toHaveLength(1);
276
+ });
277
+
204
278
  test("kubectl-style resource strings resolve the way kubectl resolves them", async () => {
205
279
  const c = await client(cluster());
206
280
  // plural.group — how waitForReady's callers have always spelled CRDs
package/src/client.ts CHANGED
@@ -215,6 +215,15 @@ export interface K8sClient {
215
215
  * such groupVersion — an answer, not a failure. Cached like `resolve`.
216
216
  */
217
217
  resources(apiVersion: string, signal?: AbortSignal): Promise<ApiResourceInfo[]>;
218
+ /**
219
+ * One groupVersion per API group the cluster serves — each group's own
220
+ * `preferredVersion` (falling back to its first listed version), with the
221
+ * core group's `v1` first (chant #1517). This is the discovery-driven
222
+ * sweep's enumeration unit: one version per group names every kind exactly
223
+ * once, without re-listing the same resources at a second served version.
224
+ * Cached for the client's lifetime, like the rest of discovery.
225
+ */
226
+ preferredGroupVersions(signal?: AbortSignal): Promise<string[]>;
218
227
  /** The API resource lists discovery has been asked for so far, for tests and diagnostics. */
219
228
  discoveryCacheKeys(): string[];
220
229
  }
@@ -231,6 +240,14 @@ interface ApiResourceListResponse {
231
240
  }>;
232
241
  }
233
242
 
243
+ /** The `/api` + `/apis` root discovery documents, kept structured so both the
244
+ * flat enumeration (`servedGroupVersions`) and the one-per-group enumeration
245
+ * (`preferredGroupVersions`, chant #1517) derive from a single cached fetch. */
246
+ interface RootDiscovery {
247
+ coreVersions: string[];
248
+ groups: Array<{ preferred?: string; versions: string[] }>;
249
+ }
250
+
234
251
  /**
235
252
  * Build a client. Nothing is read from the network here — the kubeconfig is
236
253
  * parsed, the context resolved, and the credential policy enforced, all before
@@ -301,12 +318,23 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
301
318
  const defaultNamespace = kc.getContextObject(kc.getCurrentContext())?.namespace || "default";
302
319
  const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
303
320
 
321
+ // Which cluster this client reads, in words, stamped onto every failure it
322
+ // throws (chant #1488). A `read-failed` that does not say which context it
323
+ // read turned "the ambient context moved" into an afternoon of debugging;
324
+ // naming it here — at construction, once — means every read path's
325
+ // classification carries it without each caller remembering to.
326
+ const contextNote = `context "${provenance.context ?? "(unset)"}" (${options.contextLabel ?? provenance.contextSource})`;
327
+ function noted<T extends { contextNote?: string }>(err: T): T {
328
+ err.contextNote = contextNote;
329
+ return err;
330
+ }
331
+
304
332
  // apiVersion → its APIResourceList, or null when the cluster does not serve
305
333
  // that group/version at all. Promises are cached, not values, so N entities
306
334
  // resolved concurrently issue one discovery request between them rather
307
335
  // than N identical ones.
308
336
  const discoveryCache = new Map<string, Promise<ApiResourceListResponse | null>>();
309
- let groupVersionsCache: Promise<string[]> | undefined;
337
+ let rootDiscoveryCache: Promise<RootDiscovery> | undefined;
310
338
 
311
339
  async function send(
312
340
  path: string,
@@ -338,10 +366,10 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
338
366
  .send(ctx)
339
367
  .toPromise()) as unknown as ResponseContextLike;
340
368
  } catch (err) {
341
- throw new K8sTransportError(
342
- err instanceof Error ? err.message : String(err),
343
- opts.target ?? `${method} ${path}`,
344
- { cause: err },
369
+ throw noted(
370
+ new K8sTransportError(err instanceof Error ? err.message : String(err), opts.target ?? `${method} ${path}`, {
371
+ cause: err,
372
+ }),
345
373
  );
346
374
  }
347
375
 
@@ -349,10 +377,12 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
349
377
  try {
350
378
  text = await response.body.text();
351
379
  } catch (err) {
352
- throw new K8sTransportError(
353
- `response body could not be read: ${err instanceof Error ? err.message : String(err)}`,
354
- opts.target ?? `${method} ${path}`,
355
- { cause: err },
380
+ throw noted(
381
+ new K8sTransportError(
382
+ `response body could not be read: ${err instanceof Error ? err.message : String(err)}`,
383
+ opts.target ?? `${method} ${path}`,
384
+ { cause: err },
385
+ ),
356
386
  );
357
387
  }
358
388
  return { status: response.httpStatusCode, body: text };
@@ -365,15 +395,17 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
365
395
  ): Promise<T> {
366
396
  const { status, body } = await send(path, method, opts);
367
397
  if (status < 200 || status > 299) {
368
- throw K8sApiError.fromResponse(status, body, opts.target);
398
+ throw noted(K8sApiError.fromResponse(status, body, opts.target));
369
399
  }
370
400
  try {
371
401
  return JSON.parse(body) as T;
372
402
  } catch (err) {
373
- throw new K8sTransportError(
374
- `the API server returned HTTP ${status} with a body that is not JSON`,
375
- opts.target ?? `${method} ${path}`,
376
- { cause: err },
403
+ throw noted(
404
+ new K8sTransportError(
405
+ `the API server returned HTTP ${status} with a body that is not JSON`,
406
+ opts.target ?? `${method} ${path}`,
407
+ { cause: err },
408
+ ),
377
409
  );
378
410
  }
379
411
  }
@@ -399,15 +431,13 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
399
431
  return pending;
400
432
  }
401
433
 
402
- async function servedGroupVersions(signal?: AbortSignal): Promise<string[]> {
403
- if (groupVersionsCache) return groupVersionsCache;
404
- groupVersionsCache = (async () => {
405
- const out: string[] = [];
434
+ async function rootDiscovery(signal?: AbortSignal): Promise<RootDiscovery> {
435
+ if (rootDiscoveryCache) return rootDiscoveryCache;
436
+ rootDiscoveryCache = (async (): Promise<RootDiscovery> => {
406
437
  const core = await sendJson<{ versions?: string[] }>("/api", "GET", {
407
438
  signal,
408
439
  target: "discovery /api",
409
440
  });
410
- out.push(...(core.versions ?? ["v1"]));
411
441
  const groups = await sendJson<{
412
442
  groups?: Array<{
413
443
  name?: string;
@@ -415,19 +445,43 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
415
445
  versions?: Array<{ groupVersion?: string }>;
416
446
  }>;
417
447
  }>("/apis", "GET", { signal, target: "discovery /apis" });
418
- for (const group of groups.groups ?? []) {
419
- const preferred = group.preferredVersion?.groupVersion;
420
- if (preferred) out.push(preferred);
421
- for (const v of group.versions ?? []) {
422
- if (v.groupVersion && v.groupVersion !== preferred) out.push(v.groupVersion);
423
- }
424
- }
425
- return [...new Set(out)];
448
+ return {
449
+ coreVersions: core.versions ?? ["v1"],
450
+ groups: (groups.groups ?? []).map((group) => ({
451
+ preferred: group.preferredVersion?.groupVersion,
452
+ versions: (group.versions ?? [])
453
+ .map((v) => v.groupVersion)
454
+ .filter((gv): gv is string => typeof gv === "string" && gv.length > 0),
455
+ })),
456
+ };
426
457
  })().catch((err: unknown) => {
427
- groupVersionsCache = undefined;
458
+ rootDiscoveryCache = undefined;
428
459
  throw err;
429
460
  });
430
- return groupVersionsCache;
461
+ return rootDiscoveryCache;
462
+ }
463
+
464
+ async function servedGroupVersions(signal?: AbortSignal): Promise<string[]> {
465
+ const root = await rootDiscovery(signal);
466
+ const out: string[] = [...root.coreVersions];
467
+ for (const group of root.groups) {
468
+ if (group.preferred) out.push(group.preferred);
469
+ for (const gv of group.versions) {
470
+ if (gv !== group.preferred) out.push(gv);
471
+ }
472
+ }
473
+ return [...new Set(out)];
474
+ }
475
+
476
+ async function preferredGroupVersions(signal?: AbortSignal): Promise<string[]> {
477
+ const root = await rootDiscovery(signal);
478
+ const core = root.coreVersions.includes("v1") ? "v1" : root.coreVersions[0];
479
+ const out: string[] = core ? [core] : [];
480
+ for (const group of root.groups) {
481
+ const gv = group.preferred ?? group.versions[0];
482
+ if (gv) out.push(gv);
483
+ }
484
+ return [...new Set(out)];
431
485
  }
432
486
 
433
487
  function toInfo(apiVersion: string, entry: NonNullable<ApiResourceListResponse["resources"]>[number]): ApiResourceInfo {
@@ -618,7 +672,7 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
618
672
  target,
619
673
  });
620
674
  if (status < 200 || status > 299) {
621
- throw K8sApiError.fromResponse(status, body, target);
675
+ throw noted(K8sApiError.fromResponse(status, body, target));
622
676
  }
623
677
  return body;
624
678
  }
@@ -655,6 +709,7 @@ export async function createK8sClient(options: K8sClientOptions = {}): Promise<K
655
709
  delete: remove,
656
710
  concurrently: (items, fn) => mapConcurrent(items, fn, concurrency),
657
711
  resources: resourcesOf,
712
+ preferredGroupVersions,
658
713
  discoveryCacheKeys: () => [...discoveryCache.keys()].sort(),
659
714
  };
660
715
  }
package/src/errors.ts CHANGED
@@ -26,6 +26,15 @@ export interface K8sStatus {
26
26
  * `reason` come from the response, not from parsing text.
27
27
  */
28
28
  export class K8sApiError extends Error {
29
+ /**
30
+ * Which cluster the failing read actually talked to, e.g.
31
+ * `context "k3d-fountain-local" (bound by k8s.profiles.local.context)`.
32
+ * Stamped by the client that issued the request (chant #1488) — a
33
+ * `read-failed` that does not name the cluster it read cost an afternoon on
34
+ * a laptop with two k3d clusters, so the failure carries it from birth.
35
+ */
36
+ contextNote?: string;
37
+
29
38
  constructor(
30
39
  public readonly statusCode: number,
31
40
  public readonly reason: string | undefined,
@@ -90,6 +99,9 @@ export class K8sApiError extends Error {
90
99
  * connect" and "the server said no" are different observations.
91
100
  */
92
101
  export class K8sTransportError extends Error {
102
+ /** Which cluster context the failed request was aimed at — see {@link K8sApiError.contextNote}. */
103
+ contextNote?: string;
104
+
93
105
  constructor(
94
106
  message: string,
95
107
  public readonly target?: string,
package/src/types.ts CHANGED
@@ -125,6 +125,15 @@ export interface K8sClientOptions {
125
125
  concurrency?: number;
126
126
  /** Where the context came from, for provenance. Default `ambient`. */
127
127
  contextSource?: "bound" | "ambient";
128
+ /**
129
+ * How the context was selected, in words — e.g.
130
+ * `bound by k8s.profiles.prod.context` or
131
+ * `ambient; no k8s.profiles.local binding`. Stamped (with the resolved
132
+ * context name) onto every API/transport failure the client throws, so a
133
+ * `read-failed` reason names the cluster it read (chant #1488). Defaults to
134
+ * the bare `contextSource`.
135
+ */
136
+ contextLabel?: string;
128
137
  }
129
138
 
130
139
  /**