@xplane/utils 1.3.0 → 1.5.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.
- package/dist/cli.mjs +34 -98
- package/dist/cli.mjs.map +1 -1
- package/dist/{xr-watcher-EiWHVp3o.d.mts → index-Dp92t3xP.d.mts} +94 -2
- package/dist/index-Dp92t3xP.d.mts.map +1 -0
- package/dist/index.d.mts +64 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +80 -3
- package/dist/index.mjs.map +1 -0
- package/dist/{xr-watcher-1etyvp-6.mjs → kubeconfig-BO2X-qXU.mjs} +234 -147
- package/dist/kubeconfig-BO2X-qXU.mjs.map +1 -0
- package/dist/render/index.d.mts +2 -85
- package/dist/render/index.mjs +1 -1
- package/dist/{render-Cnhpyf1X.mjs → render-BMhfzOc2.mjs} +105 -4
- package/dist/render-BMhfzOc2.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/render/index.d.mts.map +0 -1
- package/dist/render-Cnhpyf1X.mjs.map +0 -1
- package/dist/tree-DaHkojq8.mjs +0 -96
- package/dist/tree-DaHkojq8.mjs.map +0 -1
- package/dist/xr-watcher-1etyvp-6.mjs.map +0 -1
- package/dist/xr-watcher-EiWHVp3o.d.mts.map +0 -1
|
@@ -1,16 +1,123 @@
|
|
|
1
|
-
import { CoreV1Api, CustomObjectsApi, KubeConfig, Watch } from "@kubernetes/client-node";
|
|
2
|
-
//#region src/
|
|
1
|
+
import { ApiextensionsV1Api, CoreV1Api, CustomObjectsApi, KubeConfig, Watch } from "@kubernetes/client-node";
|
|
2
|
+
//#region src/cli/discovery.ts
|
|
3
|
+
let crdCache;
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* applied.
|
|
5
|
+
* Fetch the cluster's CRDs and project them into a flat list of resolved
|
|
6
|
+
* resources (one entry per served version). Cached per `KubeConfig` instance.
|
|
7
7
|
*/
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
async function listCrdResources(kc) {
|
|
9
|
+
crdCache ??= /* @__PURE__ */ new WeakMap();
|
|
10
|
+
const cached = crdCache.get(kc);
|
|
11
|
+
if (cached) return cached;
|
|
12
|
+
const promise = fetchCrdResources(kc).catch((err) => {
|
|
13
|
+
crdCache?.delete(kc);
|
|
14
|
+
throw err;
|
|
15
|
+
});
|
|
16
|
+
crdCache.set(kc, promise);
|
|
17
|
+
return promise;
|
|
18
|
+
}
|
|
19
|
+
async function fetchCrdResources(kc) {
|
|
20
|
+
const list = await kc.makeApiClient(ApiextensionsV1Api).listCustomResourceDefinition();
|
|
21
|
+
const out = [];
|
|
22
|
+
for (const item of list.items ?? []) {
|
|
23
|
+
const spec = item.spec;
|
|
24
|
+
if (!spec?.group || !spec.names?.kind || !spec.names.plural) continue;
|
|
25
|
+
const namespaced = spec.scope === "Namespaced";
|
|
26
|
+
for (const v of spec.versions ?? []) {
|
|
27
|
+
if (!v.name || v.served === false) continue;
|
|
28
|
+
out.push({
|
|
29
|
+
group: spec.group,
|
|
30
|
+
version: v.name,
|
|
31
|
+
kind: spec.names.kind,
|
|
32
|
+
plural: spec.names.plural,
|
|
33
|
+
namespaced
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve a `<resource>[.group][.version]` user hint to a single CRD-backed
|
|
41
|
+
* resource. Throws when nothing matches or when the hint is ambiguous.
|
|
42
|
+
*/
|
|
43
|
+
async function resolveResource(kc, hint) {
|
|
44
|
+
const all = await listCrdResources(kc);
|
|
45
|
+
const r = hint.resource.toLowerCase();
|
|
46
|
+
const matches = all.filter((c) => {
|
|
47
|
+
const kindLc = c.kind.toLowerCase();
|
|
48
|
+
const pluralLc = c.plural.toLowerCase();
|
|
49
|
+
if (kindLc !== r && pluralLc !== r) return false;
|
|
50
|
+
if (hint.group !== void 0 && c.group !== hint.group) return false;
|
|
51
|
+
if (hint.version !== void 0 && c.version !== hint.version) return false;
|
|
52
|
+
return true;
|
|
53
|
+
});
|
|
54
|
+
if (matches.length === 0) {
|
|
55
|
+
const detail = [
|
|
56
|
+
hint.resource,
|
|
57
|
+
hint.group,
|
|
58
|
+
hint.version
|
|
59
|
+
].filter(Boolean).join(".");
|
|
60
|
+
throw new Error(`No CRD found matching "${detail}". Available kinds: ${summarise(all)}.`);
|
|
61
|
+
}
|
|
62
|
+
if (matches.length > 1) {
|
|
63
|
+
if (hint.version === void 0) {
|
|
64
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
65
|
+
for (const m of matches) grouped.set(`${m.group}/${m.kind}`, [...grouped.get(`${m.group}/${m.kind}`) ?? [], m]);
|
|
66
|
+
if (grouped.size === 1) return [...matches].sort((a, b) => b.version.localeCompare(a.version))[0];
|
|
67
|
+
}
|
|
68
|
+
const candidates = matches.map((m) => `${m.plural}.${m.version}.${m.group}`).join(", ");
|
|
69
|
+
throw new Error(`Ambiguous resource hint "${hint.resource}". Candidates: ${candidates}. Disambiguate with .group or .version.group.`);
|
|
70
|
+
}
|
|
71
|
+
return matches[0];
|
|
72
|
+
}
|
|
73
|
+
function summarise(all) {
|
|
74
|
+
const seen = /* @__PURE__ */ new Set();
|
|
75
|
+
const out = [];
|
|
76
|
+
for (const r of all) {
|
|
77
|
+
const k = `${r.plural}.${r.group}`;
|
|
78
|
+
if (seen.has(k)) continue;
|
|
79
|
+
seen.add(k);
|
|
80
|
+
out.push(k);
|
|
81
|
+
if (out.length >= 20) {
|
|
82
|
+
out.push("…");
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return out.join(", ");
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/client/lists.ts
|
|
90
|
+
/** Initial-list for a single XR via `CustomObjectsApi`, filtered by name. */
|
|
91
|
+
async function listXrCollection(kc, ref) {
|
|
92
|
+
const api = kc.makeApiClient(CustomObjectsApi);
|
|
93
|
+
const fieldSelector = `metadata.name=${ref.name}`;
|
|
94
|
+
const res = await (ref.namespaced && ref.namespace ? api.listNamespacedCustomObject({
|
|
95
|
+
group: ref.group,
|
|
96
|
+
version: ref.version,
|
|
97
|
+
namespace: ref.namespace,
|
|
98
|
+
plural: ref.plural,
|
|
99
|
+
fieldSelector
|
|
100
|
+
}) : api.listClusterCustomObject({
|
|
101
|
+
group: ref.group,
|
|
102
|
+
version: ref.version,
|
|
103
|
+
plural: ref.plural,
|
|
104
|
+
fieldSelector
|
|
105
|
+
}));
|
|
106
|
+
return {
|
|
107
|
+
resourceVersion: res.metadata?.resourceVersion ?? "",
|
|
108
|
+
items: res.items ?? []
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Initial-list of Kubernetes Events scoped to a namespace + field selector. */
|
|
112
|
+
async function listXrEvents(kc, namespace, fieldSelector) {
|
|
113
|
+
const res = await kc.makeApiClient(CoreV1Api).listNamespacedEvent({
|
|
114
|
+
namespace,
|
|
115
|
+
fieldSelector
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
resourceVersion: res.metadata?.resourceVersion ?? "",
|
|
119
|
+
items: res.items ?? []
|
|
120
|
+
};
|
|
14
121
|
}
|
|
15
122
|
//#endregion
|
|
16
123
|
//#region src/watcher/await-ready.ts
|
|
@@ -39,106 +146,6 @@ async function awaitReady(watcher, opts = {}) {
|
|
|
39
146
|
}
|
|
40
147
|
}
|
|
41
148
|
//#endregion
|
|
42
|
-
//#region src/watcher/readiness.ts
|
|
43
|
-
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
44
|
-
const asString = (v) => typeof v === "string" ? v : void 0;
|
|
45
|
-
/**
|
|
46
|
-
* Build an `XrSnapshot` from a raw Kubernetes object. Parses the `Ready` condition,
|
|
47
|
-
* the optional `status.xplane` payload and `status.resourceRefs`.
|
|
48
|
-
*/
|
|
49
|
-
function buildSnapshot(object) {
|
|
50
|
-
const status = isRecord(object.status) ? object.status : void 0;
|
|
51
|
-
const conditions = [];
|
|
52
|
-
if (status && Array.isArray(status.conditions)) {
|
|
53
|
-
for (const c of status.conditions) if (isRecord(c)) conditions.push({
|
|
54
|
-
type: asString(c.type),
|
|
55
|
-
status: asString(c.status),
|
|
56
|
-
reason: asString(c.reason),
|
|
57
|
-
message: asString(c.message)
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
const ready = conditions.find((c) => c.type === "Ready");
|
|
61
|
-
const responsive = conditions.find((c) => c.type === "Responsive");
|
|
62
|
-
const synced = conditions.find((c) => c.type === "Synced");
|
|
63
|
-
const snapshot = {
|
|
64
|
-
object,
|
|
65
|
-
ready: ready?.status === "True",
|
|
66
|
-
resourceRefs: parseResourceRefs(status)
|
|
67
|
-
};
|
|
68
|
-
if (ready?.reason !== void 0) snapshot.readyReason = ready.reason;
|
|
69
|
-
if (ready?.message !== void 0) snapshot.readyMessage = ready.message;
|
|
70
|
-
if (responsive?.status === "False" && responsive.reason === "WatchCircuitOpen") snapshot.updatesThrottled = true;
|
|
71
|
-
if (synced?.status === "False" && synced.reason === "ReconcileError") snapshot.syncError = {
|
|
72
|
-
reason: synced.reason,
|
|
73
|
-
message: synced.message ?? ""
|
|
74
|
-
};
|
|
75
|
-
const xplane = parseXplane(status);
|
|
76
|
-
if (xplane) snapshot.xplane = xplane;
|
|
77
|
-
return snapshot;
|
|
78
|
-
}
|
|
79
|
-
function parseResourceRefs(status) {
|
|
80
|
-
const out = [];
|
|
81
|
-
if (!status) return out;
|
|
82
|
-
const refs = status.resourceRefs;
|
|
83
|
-
if (!Array.isArray(refs)) return out;
|
|
84
|
-
for (const r of refs) {
|
|
85
|
-
if (!isRecord(r)) continue;
|
|
86
|
-
const apiVersion = asString(r.apiVersion) ?? "";
|
|
87
|
-
const kind = asString(r.kind) ?? "";
|
|
88
|
-
const name = asString(r.name) ?? "";
|
|
89
|
-
if (name) out.push({
|
|
90
|
-
apiVersion,
|
|
91
|
-
kind,
|
|
92
|
-
name
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
return out;
|
|
96
|
-
}
|
|
97
|
-
function parseXplane(status) {
|
|
98
|
-
if (!status || !isRecord(status.xplane)) return void 0;
|
|
99
|
-
const x = status.xplane;
|
|
100
|
-
const emitted = [];
|
|
101
|
-
if (Array.isArray(x.emittedResources)) for (const r of x.emittedResources) {
|
|
102
|
-
if (!isRecord(r)) continue;
|
|
103
|
-
const nodePath = asString(r.nodePath);
|
|
104
|
-
if (!nodePath) continue;
|
|
105
|
-
const name = asString(r.name);
|
|
106
|
-
const namespace = asString(r.namespace);
|
|
107
|
-
emitted.push({
|
|
108
|
-
apiVersion: asString(r.apiVersion) ?? "",
|
|
109
|
-
kind: asString(r.kind) ?? "",
|
|
110
|
-
nodePath,
|
|
111
|
-
...name ? { name } : {},
|
|
112
|
-
...namespace ? { namespace } : {},
|
|
113
|
-
ready: r.ready === true
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
const blocked = [];
|
|
117
|
-
if (Array.isArray(x.blockedResources)) for (const r of x.blockedResources) {
|
|
118
|
-
if (!isRecord(r)) continue;
|
|
119
|
-
const nodePath = asString(r.nodePath);
|
|
120
|
-
if (!nodePath) continue;
|
|
121
|
-
const name = asString(r.name);
|
|
122
|
-
const namespace = asString(r.namespace);
|
|
123
|
-
const entry = {
|
|
124
|
-
apiVersion: asString(r.apiVersion) ?? "",
|
|
125
|
-
kind: asString(r.kind) ?? "",
|
|
126
|
-
nodePath,
|
|
127
|
-
...name ? { name } : {},
|
|
128
|
-
...namespace ? { namespace } : {}
|
|
129
|
-
};
|
|
130
|
-
if (Array.isArray(r.waitingFor)) {
|
|
131
|
-
const wf = r.waitingFor.filter((s) => typeof s === "string");
|
|
132
|
-
if (wf.length > 0) entry.waitingFor = wf;
|
|
133
|
-
}
|
|
134
|
-
blocked.push(entry);
|
|
135
|
-
}
|
|
136
|
-
return {
|
|
137
|
-
emittedResources: emitted,
|
|
138
|
-
blockedResources: blocked
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
//#endregion
|
|
142
149
|
//#region src/watcher/target.ts
|
|
143
150
|
function parseTarget(input) {
|
|
144
151
|
const slash = input.indexOf("/");
|
|
@@ -170,40 +177,6 @@ function parseTarget(input) {
|
|
|
170
177
|
};
|
|
171
178
|
}
|
|
172
179
|
//#endregion
|
|
173
|
-
//#region src/client/lists.ts
|
|
174
|
-
/** Initial-list for a single XR via `CustomObjectsApi`, filtered by name. */
|
|
175
|
-
async function listXrCollection(kc, ref) {
|
|
176
|
-
const api = kc.makeApiClient(CustomObjectsApi);
|
|
177
|
-
const fieldSelector = `metadata.name=${ref.name}`;
|
|
178
|
-
const res = await (ref.namespaced && ref.namespace ? api.listNamespacedCustomObject({
|
|
179
|
-
group: ref.group,
|
|
180
|
-
version: ref.version,
|
|
181
|
-
namespace: ref.namespace,
|
|
182
|
-
plural: ref.plural,
|
|
183
|
-
fieldSelector
|
|
184
|
-
}) : api.listClusterCustomObject({
|
|
185
|
-
group: ref.group,
|
|
186
|
-
version: ref.version,
|
|
187
|
-
plural: ref.plural,
|
|
188
|
-
fieldSelector
|
|
189
|
-
}));
|
|
190
|
-
return {
|
|
191
|
-
resourceVersion: res.metadata?.resourceVersion ?? "",
|
|
192
|
-
items: res.items ?? []
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
/** Initial-list of Kubernetes Events scoped to a namespace + field selector. */
|
|
196
|
-
async function listXrEvents(kc, namespace, fieldSelector) {
|
|
197
|
-
const res = await kc.makeApiClient(CoreV1Api).listNamespacedEvent({
|
|
198
|
-
namespace,
|
|
199
|
-
fieldSelector
|
|
200
|
-
});
|
|
201
|
-
return {
|
|
202
|
-
resourceVersion: res.metadata?.resourceVersion ?? "",
|
|
203
|
-
items: res.items ?? []
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
//#endregion
|
|
207
180
|
//#region src/client/watch.ts
|
|
208
181
|
/**
|
|
209
182
|
* Run a robust list-then-watch loop. The initial list is supplied by the caller
|
|
@@ -347,6 +320,106 @@ var AsyncQueue = class {
|
|
|
347
320
|
}
|
|
348
321
|
};
|
|
349
322
|
//#endregion
|
|
323
|
+
//#region src/watcher/readiness.ts
|
|
324
|
+
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
325
|
+
const asString = (v) => typeof v === "string" ? v : void 0;
|
|
326
|
+
/**
|
|
327
|
+
* Build an `XrSnapshot` from a raw Kubernetes object. Parses the `Ready` condition,
|
|
328
|
+
* the optional `status.xplane` payload and `status.resourceRefs`.
|
|
329
|
+
*/
|
|
330
|
+
function buildSnapshot(object) {
|
|
331
|
+
const status = isRecord(object.status) ? object.status : void 0;
|
|
332
|
+
const conditions = [];
|
|
333
|
+
if (status && Array.isArray(status.conditions)) {
|
|
334
|
+
for (const c of status.conditions) if (isRecord(c)) conditions.push({
|
|
335
|
+
type: asString(c.type),
|
|
336
|
+
status: asString(c.status),
|
|
337
|
+
reason: asString(c.reason),
|
|
338
|
+
message: asString(c.message)
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
const ready = conditions.find((c) => c.type === "Ready");
|
|
342
|
+
const responsive = conditions.find((c) => c.type === "Responsive");
|
|
343
|
+
const synced = conditions.find((c) => c.type === "Synced");
|
|
344
|
+
const snapshot = {
|
|
345
|
+
object,
|
|
346
|
+
ready: ready?.status === "True",
|
|
347
|
+
resourceRefs: parseResourceRefs(status)
|
|
348
|
+
};
|
|
349
|
+
if (ready?.reason !== void 0) snapshot.readyReason = ready.reason;
|
|
350
|
+
if (ready?.message !== void 0) snapshot.readyMessage = ready.message;
|
|
351
|
+
if (responsive?.status === "False" && responsive.reason === "WatchCircuitOpen") snapshot.updatesThrottled = true;
|
|
352
|
+
if (synced?.status === "False" && synced.reason === "ReconcileError") snapshot.syncError = {
|
|
353
|
+
reason: synced.reason,
|
|
354
|
+
message: synced.message ?? ""
|
|
355
|
+
};
|
|
356
|
+
const xplane = parseXplane(status);
|
|
357
|
+
if (xplane) snapshot.xplane = xplane;
|
|
358
|
+
return snapshot;
|
|
359
|
+
}
|
|
360
|
+
function parseResourceRefs(status) {
|
|
361
|
+
const out = [];
|
|
362
|
+
if (!status) return out;
|
|
363
|
+
const refs = status.resourceRefs;
|
|
364
|
+
if (!Array.isArray(refs)) return out;
|
|
365
|
+
for (const r of refs) {
|
|
366
|
+
if (!isRecord(r)) continue;
|
|
367
|
+
const apiVersion = asString(r.apiVersion) ?? "";
|
|
368
|
+
const kind = asString(r.kind) ?? "";
|
|
369
|
+
const name = asString(r.name) ?? "";
|
|
370
|
+
if (name) out.push({
|
|
371
|
+
apiVersion,
|
|
372
|
+
kind,
|
|
373
|
+
name
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
return out;
|
|
377
|
+
}
|
|
378
|
+
function parseXplane(status) {
|
|
379
|
+
if (!status || !isRecord(status.xplane)) return void 0;
|
|
380
|
+
const x = status.xplane;
|
|
381
|
+
const emitted = [];
|
|
382
|
+
if (Array.isArray(x.emittedResources)) for (const r of x.emittedResources) {
|
|
383
|
+
if (!isRecord(r)) continue;
|
|
384
|
+
const nodePath = asString(r.nodePath);
|
|
385
|
+
if (!nodePath) continue;
|
|
386
|
+
const name = asString(r.name);
|
|
387
|
+
const namespace = asString(r.namespace);
|
|
388
|
+
emitted.push({
|
|
389
|
+
apiVersion: asString(r.apiVersion) ?? "",
|
|
390
|
+
kind: asString(r.kind) ?? "",
|
|
391
|
+
nodePath,
|
|
392
|
+
...name ? { name } : {},
|
|
393
|
+
...namespace ? { namespace } : {},
|
|
394
|
+
ready: r.ready === true
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
const blocked = [];
|
|
398
|
+
if (Array.isArray(x.blockedResources)) for (const r of x.blockedResources) {
|
|
399
|
+
if (!isRecord(r)) continue;
|
|
400
|
+
const nodePath = asString(r.nodePath);
|
|
401
|
+
if (!nodePath) continue;
|
|
402
|
+
const name = asString(r.name);
|
|
403
|
+
const namespace = asString(r.namespace);
|
|
404
|
+
const entry = {
|
|
405
|
+
apiVersion: asString(r.apiVersion) ?? "",
|
|
406
|
+
kind: asString(r.kind) ?? "",
|
|
407
|
+
nodePath,
|
|
408
|
+
...name ? { name } : {},
|
|
409
|
+
...namespace ? { namespace } : {}
|
|
410
|
+
};
|
|
411
|
+
if (Array.isArray(r.waitingFor)) {
|
|
412
|
+
const wf = r.waitingFor.filter((s) => typeof s === "string");
|
|
413
|
+
if (wf.length > 0) entry.waitingFor = wf;
|
|
414
|
+
}
|
|
415
|
+
blocked.push(entry);
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
emittedResources: emitted,
|
|
419
|
+
blockedResources: blocked
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
//#endregion
|
|
350
423
|
//#region src/watcher/xr-watcher.ts
|
|
351
424
|
/**
|
|
352
425
|
* Subscribe to live updates of a single XR. Emits a `snapshot` on every observed
|
|
@@ -527,6 +600,20 @@ function toKubernetesEvent(obj) {
|
|
|
527
600
|
return event;
|
|
528
601
|
}
|
|
529
602
|
//#endregion
|
|
530
|
-
|
|
603
|
+
//#region src/client/kubeconfig.ts
|
|
604
|
+
/**
|
|
605
|
+
* Load a `KubeConfig` from disk using `client-node`'s default rules (with
|
|
606
|
+
* optional explicit overrides). The returned config has its current context
|
|
607
|
+
* applied.
|
|
608
|
+
*/
|
|
609
|
+
function loadKubeConfig(opts = {}) {
|
|
610
|
+
const kc = new KubeConfig();
|
|
611
|
+
if (opts.kubeconfig) kc.loadFromFile(opts.kubeconfig);
|
|
612
|
+
else kc.loadFromDefault();
|
|
613
|
+
if (opts.context) kc.setCurrentContext(opts.context);
|
|
614
|
+
return kc;
|
|
615
|
+
}
|
|
616
|
+
//#endregion
|
|
617
|
+
export { awaitReady as a, parseTarget as i, createXrWatcher as n, listXrCollection as o, buildSnapshot as r, resolveResource as s, loadKubeConfig as t };
|
|
531
618
|
|
|
532
|
-
//# sourceMappingURL=
|
|
619
|
+
//# sourceMappingURL=kubeconfig-BO2X-qXU.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kubeconfig-BO2X-qXU.mjs","names":[],"sources":["../src/cli/discovery.ts","../src/client/lists.ts","../src/watcher/await-ready.ts","../src/watcher/target.ts","../src/client/watch.ts","../src/watcher/queue.ts","../src/watcher/readiness.ts","../src/watcher/xr-watcher.ts","../src/client/kubeconfig.ts"],"sourcesContent":["import { ApiextensionsV1Api, type KubeConfig } from '@kubernetes/client-node';\n\n/** Resolved API resource metadata. */\nexport interface ResolvedResource {\n group: string;\n version: string;\n kind: string;\n plural: string;\n namespaced: boolean;\n}\n\nlet crdCache: WeakMap<KubeConfig, Promise<ResolvedResource[]>> | undefined;\n\n/**\n * Fetch the cluster's CRDs and project them into a flat list of resolved\n * resources (one entry per served version). Cached per `KubeConfig` instance.\n */\nexport async function listCrdResources(kc: KubeConfig): Promise<ResolvedResource[]> {\n crdCache ??= new WeakMap();\n const cached = crdCache.get(kc);\n if (cached) return cached;\n const promise = fetchCrdResources(kc).catch((err) => {\n crdCache?.delete(kc);\n throw err;\n });\n crdCache.set(kc, promise);\n return promise;\n}\n\nasync function fetchCrdResources(kc: KubeConfig): Promise<ResolvedResource[]> {\n const api = kc.makeApiClient(ApiextensionsV1Api);\n const list = await api.listCustomResourceDefinition();\n const out: ResolvedResource[] = [];\n for (const item of list.items ?? []) {\n const spec = item.spec;\n if (!spec?.group || !spec.names?.kind || !spec.names.plural) continue;\n const namespaced = spec.scope === 'Namespaced';\n for (const v of spec.versions ?? []) {\n if (!v.name || v.served === false) continue;\n out.push({\n group: spec.group,\n version: v.name,\n kind: spec.names.kind,\n plural: spec.names.plural,\n namespaced,\n });\n }\n }\n return out;\n}\n\n/**\n * Resolve a `<resource>[.group][.version]` user hint to a single CRD-backed\n * resource. Throws when nothing matches or when the hint is ambiguous.\n */\nexport async function resolveResource(\n kc: KubeConfig,\n hint: { resource: string; group?: string; version?: string },\n): Promise<ResolvedResource> {\n const all = await listCrdResources(kc);\n const r = hint.resource.toLowerCase();\n const matches = all.filter((c) => {\n const kindLc = c.kind.toLowerCase();\n const pluralLc = c.plural.toLowerCase();\n if (kindLc !== r && pluralLc !== r) return false;\n if (hint.group !== undefined && c.group !== hint.group) return false;\n if (hint.version !== undefined && c.version !== hint.version) return false;\n return true;\n });\n if (matches.length === 0) {\n const detail = [hint.resource, hint.group, hint.version].filter(Boolean).join('.');\n throw new Error(`No CRD found matching \"${detail}\". Available kinds: ${summarise(all)}.`);\n }\n if (matches.length > 1) {\n if (hint.version === undefined) {\n const grouped = new Map<string, ResolvedResource[]>();\n for (const m of matches)\n grouped.set(`${m.group}/${m.kind}`, [...(grouped.get(`${m.group}/${m.kind}`) ?? []), m]);\n if (grouped.size === 1) {\n // Multiple served versions of the same kind/group — pick the lexicographically highest.\n const sorted = [...matches].sort((a, b) => b.version.localeCompare(a.version));\n return sorted[0] as ResolvedResource;\n }\n }\n const candidates = matches.map((m) => `${m.plural}.${m.version}.${m.group}`).join(', ');\n throw new Error(\n `Ambiguous resource hint \"${hint.resource}\". Candidates: ${candidates}. Disambiguate with .group or .version.group.`,\n );\n }\n return matches[0] as ResolvedResource;\n}\n\nfunction summarise(all: ResolvedResource[]): string {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const r of all) {\n const k = `${r.plural}.${r.group}`;\n if (seen.has(k)) continue;\n seen.add(k);\n out.push(k);\n if (out.length >= 20) {\n out.push('…');\n break;\n }\n }\n return out.join(', ');\n}\n","import {\n CoreV1Api,\n CustomObjectsApi,\n type KubeConfig,\n type KubernetesObject,\n} from '@kubernetes/client-node';\nimport type { XrRef } from '../watcher/types.js';\nimport type { ListResult } from './watch.js';\n\ninterface CustomObjectList {\n metadata?: { resourceVersion?: string };\n items?: KubernetesObject[];\n}\n\n/** Initial-list for a single XR via `CustomObjectsApi`, filtered by name. */\nexport async function listXrCollection(kc: KubeConfig, ref: XrRef): Promise<ListResult> {\n const api = kc.makeApiClient(CustomObjectsApi);\n const fieldSelector = `metadata.name=${ref.name}`;\n const res = (await (ref.namespaced && ref.namespace\n ? api.listNamespacedCustomObject({\n group: ref.group,\n version: ref.version,\n namespace: ref.namespace,\n plural: ref.plural,\n fieldSelector,\n })\n : api.listClusterCustomObject({\n group: ref.group,\n version: ref.version,\n plural: ref.plural,\n fieldSelector,\n }))) as CustomObjectList;\n return {\n resourceVersion: res.metadata?.resourceVersion ?? '',\n items: res.items ?? [],\n };\n}\n\n/** Initial-list of Kubernetes Events scoped to a namespace + field selector. */\nexport async function listXrEvents(\n kc: KubeConfig,\n namespace: string,\n fieldSelector: string,\n): Promise<ListResult> {\n const api = kc.makeApiClient(CoreV1Api);\n const res = await api.listNamespacedEvent({ namespace, fieldSelector });\n return {\n resourceVersion: res.metadata?.resourceVersion ?? '',\n items: (res.items ?? []) as unknown as KubernetesObject[],\n };\n}\n","import type { XrSnapshot } from './types.js';\nimport type { XrWatcher } from './xr-watcher.js';\n\nexport interface AwaitReadyOptions {\n /** Maximum time in ms to wait for `Ready=True` before rejecting. */\n timeoutMs?: number;\n}\n\n/**\n * Resolve when the watcher observes its first `Ready=True` snapshot. Rejects on\n * the watcher's first error, if the stream ends without becoming ready, or on\n * `timeoutMs`.\n *\n * Uses the watcher's dedicated `ready` promise — it does NOT consume queue\n * events, so it composes safely with a concurrent renderer iterating the\n * watcher.\n *\n * The watcher itself is not stopped on resolution — call `watcher.stop()` from\n * a `.finally()` if you want the underlying connections torn down.\n */\nexport async function awaitReady(\n watcher: XrWatcher,\n opts: AwaitReadyOptions = {},\n): Promise<XrSnapshot> {\n if (opts.timeoutMs === undefined) return watcher.ready;\n let timer: NodeJS.Timeout | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`Timed out after ${opts.timeoutMs}ms waiting for Ready`)),\n opts.timeoutMs,\n );\n });\n try {\n return await Promise.race([watcher.ready, timeout]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n","/**\n * Parse a kubectl-style resource target of the form:\n * <resource>/<name>\n * where `<resource>` is one of:\n * - `kind` (e.g. `xprojects`, `XProject`)\n * - `kind.group` (e.g. `xprojects.platform.example.com`)\n * - `kind.version.group` (e.g. `xprojects.v1alpha1.platform.example.com`)\n *\n * The `kind` token is returned as-is; resolving it to an API group/version/plural\n * is the discovery layer's responsibility (see `client/discovery.ts`).\n */\nexport interface ParsedTarget {\n /** Resource hint: kind, plural, or short name as typed by the user. */\n resource: string;\n /** Optional API group hint extracted from `kind.group` or `kind.version.group`. */\n group?: string;\n /** Optional API version hint extracted from `kind.version.group`. */\n version?: string;\n /** Object name. */\n name: string;\n}\n\nexport function parseTarget(input: string): ParsedTarget {\n const slash = input.indexOf('/');\n if (slash < 0) {\n throw new Error(\n `Invalid target \"${input}\": expected kubectl-style \"<resource>/<name>\" (e.g. \"xprojects/foo\" or \"xprojects.platform.example.com/foo\").`,\n );\n }\n const left = input.slice(0, slash);\n const name = input.slice(slash + 1);\n if (left.length === 0 || name.length === 0) {\n throw new Error(`Invalid target \"${input}\": both resource and name are required.`);\n }\n\n const dot = left.indexOf('.');\n if (dot < 0) return { resource: left, name };\n\n const resource = left.slice(0, dot);\n const rest = left.slice(dot + 1);\n\n // Heuristic: a Kubernetes API version token starts with `v` followed by a digit\n // (`v1`, `v1alpha1`, `v2beta3`). If the first segment of `rest` matches, treat\n // it as the version and the remainder as the group.\n const restDot = rest.indexOf('.');\n if (restDot > 0) {\n const maybeVersion = rest.slice(0, restDot);\n if (/^v\\d/.test(maybeVersion)) {\n return { resource, version: maybeVersion, group: rest.slice(restDot + 1), name };\n }\n }\n return { resource, group: rest, name };\n}\n","import type { KubeConfig, KubernetesObject } from '@kubernetes/client-node';\nimport { Watch } from '@kubernetes/client-node';\n\n/** Watch event phase as published by the Kubernetes watch protocol. */\nexport type WatchPhase = 'ADDED' | 'MODIFIED' | 'DELETED' | 'BOOKMARK' | 'ERROR';\n\n/** Result of an initial list call. */\nexport interface ListResult<T extends KubernetesObject = KubernetesObject> {\n resourceVersion: string;\n items: T[];\n}\n\nexport interface ListWatchOptions {\n /** Watch URL path, e.g. `/apis/group/v1/namespaces/ns/plural`. */\n path: string;\n /** Optional field selector applied to the watch stream. */\n fieldSelector?: string;\n /** Optional label selector applied to the watch stream. */\n labelSelector?: string;\n /** Aborts both the initial list and any subsequent watch streams. */\n signal: AbortSignal;\n /** Caller-provided initial list. Use a typed client (`CoreV1Api`, `CustomObjectsApi`, …). */\n list: () => Promise<ListResult>;\n /** Invoked for each observed object (after the initial list and on each watch event). */\n onEvent: (phase: WatchPhase, obj: KubernetesObject) => void;\n /** Invoked when a stream ends in error and a reconnect is about to be attempted. */\n onError?: (err: Error) => void;\n}\n\ninterface ReconnectableState {\n resourceVersion: string;\n}\n\n/**\n * Run a robust list-then-watch loop. The initial list is supplied by the caller\n * (so it can use a typed Kubernetes client); the watch stream uses the raw\n * `Watch` class because it is the only API surface that supports a\n * `fieldSelector` for a single named object.\n *\n * Behaviour:\n * 1. Call `opts.list()` and emit each item as `ADDED`.\n * 2. Start a `Watch` stream from the returned `resourceVersion`. Each event is forwarded.\n * 3. When the stream ends with an error or the server closes it, sleep briefly and reconnect.\n * 4. The loop exits cleanly when the supplied `AbortSignal` fires.\n */\nexport async function listAndWatch(kc: KubeConfig, opts: ListWatchOptions): Promise<void> {\n const state: ReconnectableState = { resourceVersion: '' };\n const watcher = new Watch(kc);\n\n while (!opts.signal.aborted) {\n try {\n const list = await opts.list();\n state.resourceVersion = list.resourceVersion;\n for (const item of list.items) opts.onEvent('ADDED', item);\n\n await runWatchOnce(watcher, opts, state);\n } catch (err) {\n if (opts.signal.aborted) return;\n const e = err instanceof Error ? err : new Error(String(err));\n opts.onError?.(e);\n await sleep(1000, opts.signal);\n }\n }\n}\n\nfunction runWatchOnce(\n watcher: Watch,\n opts: ListWatchOptions,\n state: ReconnectableState,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const queryParams: Record<string, string | boolean> = { watch: true };\n if (opts.fieldSelector) queryParams.fieldSelector = opts.fieldSelector;\n if (opts.labelSelector) queryParams.labelSelector = opts.labelSelector;\n if (state.resourceVersion) queryParams.resourceVersion = state.resourceVersion;\n queryParams.allowWatchBookmarks = true;\n\n let controllerRef: AbortController | undefined;\n let settled = false;\n const resolveOnce = () => {\n if (settled) return;\n settled = true;\n opts.signal.removeEventListener('abort', onAbort);\n resolve();\n };\n const rejectOnce = (err: unknown) => {\n if (settled) return;\n settled = true;\n opts.signal.removeEventListener('abort', onAbort);\n reject(err instanceof Error ? err : new Error(String(err)));\n };\n const onAbort = () => {\n controllerRef?.abort();\n resolveOnce();\n };\n opts.signal.addEventListener('abort', onAbort, { once: true });\n\n watcher\n .watch(\n opts.path,\n queryParams,\n (phase, apiObj) => {\n const obj = apiObj as KubernetesObject;\n const objRv = (obj.metadata?.resourceVersion as string | undefined) ?? '';\n if (objRv) state.resourceVersion = objRv;\n opts.onEvent(phase as WatchPhase, obj);\n },\n (err) => {\n if (err) rejectOnce(err);\n else resolveOnce();\n },\n )\n .then((controller) => {\n controllerRef = controller;\n if (opts.signal.aborted) {\n controller.abort();\n resolveOnce();\n }\n })\n .catch((err) => rejectOnce(err));\n });\n}\n\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise<void>((resolve) => {\n if (signal.aborted) {\n resolve();\n return;\n }\n const t = setTimeout(() => {\n signal.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(t);\n resolve();\n };\n signal.addEventListener('abort', onAbort, { once: true });\n });\n}\n","/**\n * Single-consumer async queue used by the XR watcher to expose its event\n * stream as both a callback API and a `for await` async iterable.\n *\n * Memory-bounded only by the producer. Designed for low-frequency control-plane\n * events (Kubernetes watch + status reconciliations), not high-throughput data.\n */\nexport class AsyncQueue<T> implements AsyncIterable<T> {\n private buffer: T[] = [];\n private waiters: Array<(r: IteratorResult<T>) => void> = [];\n private closed = false;\n\n push(value: T): void {\n if (this.closed) return;\n const waiter = this.waiters.shift();\n if (waiter) waiter({ value, done: false });\n else this.buffer.push(value);\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n for (const w of this.waiters.splice(0)) w({ value: undefined, done: true });\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n const v = this.buffer.shift();\n if (v !== undefined) return Promise.resolve({ value: v, done: false });\n if (this.closed) return Promise.resolve({ value: undefined, done: true });\n return new Promise<IteratorResult<T>>((resolve) => this.waiters.push(resolve));\n },\n return: (): Promise<IteratorResult<T>> => {\n this.close();\n return Promise.resolve({ value: undefined, done: true });\n },\n };\n }\n}\n","import type { KubernetesObject } from '@kubernetes/client-node';\nimport type { ResourceRef, XplaneStatus, XrSnapshot } from './types.js';\n\ninterface Condition {\n type?: string;\n status?: string;\n reason?: string;\n message?: string;\n}\n\nconst isRecord = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v);\n\nconst asString = (v: unknown): string | undefined => (typeof v === 'string' ? v : undefined);\n\n/**\n * Build an `XrSnapshot` from a raw Kubernetes object. Parses the `Ready` condition,\n * the optional `status.xplane` payload and `status.resourceRefs`.\n */\nexport function buildSnapshot(object: KubernetesObject): XrSnapshot {\n const status = isRecord((object as { status?: unknown }).status)\n ? (object as { status: Record<string, unknown> }).status\n : undefined;\n\n const conditions: Condition[] = [];\n if (status && Array.isArray(status.conditions)) {\n for (const c of status.conditions as unknown[]) {\n if (isRecord(c)) {\n conditions.push({\n type: asString(c.type),\n status: asString(c.status),\n reason: asString(c.reason),\n message: asString(c.message),\n });\n }\n }\n }\n const ready = conditions.find((c) => c.type === 'Ready');\n const responsive = conditions.find((c) => c.type === 'Responsive');\n const synced = conditions.find((c) => c.type === 'Synced');\n\n const snapshot: XrSnapshot = {\n object,\n ready: ready?.status === 'True',\n resourceRefs: parseResourceRefs(status),\n };\n if (ready?.reason !== undefined) snapshot.readyReason = ready.reason;\n if (ready?.message !== undefined) snapshot.readyMessage = ready.message;\n if (responsive?.status === 'False' && responsive.reason === 'WatchCircuitOpen') {\n snapshot.updatesThrottled = true;\n }\n if (synced?.status === 'False' && synced.reason === 'ReconcileError') {\n snapshot.syncError = {\n reason: synced.reason,\n message: synced.message ?? '',\n };\n }\n\n const xplane = parseXplane(status);\n if (xplane) snapshot.xplane = xplane;\n\n return snapshot;\n}\n\nfunction parseResourceRefs(status: Record<string, unknown> | undefined): ResourceRef[] {\n const out: ResourceRef[] = [];\n if (!status) return out;\n const refs = status.resourceRefs;\n if (!Array.isArray(refs)) return out;\n for (const r of refs as unknown[]) {\n if (!isRecord(r)) continue;\n const apiVersion = asString(r.apiVersion) ?? '';\n const kind = asString(r.kind) ?? '';\n const name = asString(r.name) ?? '';\n if (name) out.push({ apiVersion, kind, name });\n }\n return out;\n}\n\nfunction parseXplane(status: Record<string, unknown> | undefined): XplaneStatus | undefined {\n if (!status || !isRecord(status.xplane)) return undefined;\n const x = status.xplane;\n const emitted: XplaneStatus['emittedResources'] = [];\n if (Array.isArray(x.emittedResources)) {\n for (const r of x.emittedResources as unknown[]) {\n if (!isRecord(r)) continue;\n const nodePath = asString(r.nodePath);\n if (!nodePath) continue;\n const name = asString(r.name);\n const namespace = asString(r.namespace);\n emitted.push({\n apiVersion: asString(r.apiVersion) ?? '',\n kind: asString(r.kind) ?? '',\n nodePath,\n ...(name ? { name } : {}),\n ...(namespace ? { namespace } : {}),\n ready: r.ready === true,\n });\n }\n }\n const blocked: XplaneStatus['blockedResources'] = [];\n if (Array.isArray(x.blockedResources)) {\n for (const r of x.blockedResources as unknown[]) {\n if (!isRecord(r)) continue;\n const nodePath = asString(r.nodePath);\n if (!nodePath) continue;\n const name = asString(r.name);\n const namespace = asString(r.namespace);\n const entry: XplaneStatus['blockedResources'][number] = {\n apiVersion: asString(r.apiVersion) ?? '',\n kind: asString(r.kind) ?? '',\n nodePath,\n ...(name ? { name } : {}),\n ...(namespace ? { namespace } : {}),\n };\n if (Array.isArray(r.waitingFor)) {\n const wf = (r.waitingFor as unknown[]).filter((s): s is string => typeof s === 'string');\n if (wf.length > 0) entry.waitingFor = wf;\n }\n blocked.push(entry);\n }\n }\n return { emittedResources: emitted, blockedResources: blocked };\n}\n","import type { KubeConfig, KubernetesObject } from '@kubernetes/client-node';\nimport { listXrCollection, listXrEvents } from '../client/lists.js';\nimport { listAndWatch } from '../client/watch.js';\nimport { AsyncQueue } from './queue.js';\nimport { buildSnapshot } from './readiness.js';\nimport type { KubernetesEvent, XrEvent, XrRef, XrSnapshot } from './types.js';\n\nexport interface CreateXrWatcherOptions {\n kubeConfig: KubeConfig;\n ref: XrRef;\n /** Disable subscribing to Kubernetes Events for the XR. Defaults to `false`. */\n disableEvents?: boolean;\n /** Aborts both the XR and Events watches and closes the iterable. */\n signal?: AbortSignal;\n}\n\nexport interface XrWatcher extends AsyncIterable<XrEvent> {\n /** Resolves with the first ready snapshot. Rejects on error or end-before-ready. */\n readonly ready: Promise<XrSnapshot>;\n /** Resolves when the watcher's background tasks have all settled. */\n readonly done: Promise<void>;\n /** Aborts the watcher and closes the iterable. Idempotent. */\n stop(): void;\n}\n\n/**\n * Subscribe to live updates of a single XR. Emits a `snapshot` on every observed\n * change, a one-shot `ready` when the XR's `Ready` condition first becomes True,\n * and (unless disabled) `k8s-event` items for Kubernetes Events targeting the XR.\n *\n * The watcher uses list-then-watch with auto-reconnect — no polling.\n */\nexport function createXrWatcher(opts: CreateXrWatcherOptions): XrWatcher {\n const queue = new AsyncQueue<XrEvent>();\n const controller = new AbortController();\n const userSignal = opts.signal;\n if (userSignal) {\n if (userSignal.aborted) controller.abort();\n else userSignal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n\n let readyEmitted = false;\n let uid: string | undefined;\n let eventsStarted = false;\n let eventsTask: Promise<void> = Promise.resolve();\n const seenEventUids = new Set<string>();\n\n let resolveReady!: (snap: XrSnapshot) => void;\n let rejectReady!: (err: Error) => void;\n let readySettled = false;\n const ready = new Promise<XrSnapshot>((res, rej) => {\n resolveReady = (s) => {\n if (readySettled) return;\n readySettled = true;\n res(s);\n };\n rejectReady = (e) => {\n if (readySettled) return;\n readySettled = true;\n rej(e);\n };\n });\n // Avoid unhandled rejection if nobody awaits `ready`.\n ready.catch(() => undefined);\n\n const handleXrEvent = (phase: string, obj: KubernetesObject) => {\n if (phase === 'DELETED') {\n const err = new Error(`XR ${opts.ref.kind}/${opts.ref.name} was deleted`);\n queue.push({ type: 'error', error: err });\n rejectReady(err);\n controller.abort();\n return;\n }\n if (phase === 'BOOKMARK' || phase === 'ERROR') return;\n const snapshot = buildSnapshot(obj);\n queue.push({ type: 'snapshot', snapshot });\n if (snapshot.syncError) {\n const err = new Error(\n `XR ${opts.ref.kind}/${opts.ref.name} reconcile failed: ${snapshot.syncError.message || snapshot.syncError.reason}`,\n );\n queue.push({ type: 'error', error: err });\n rejectReady(err);\n controller.abort();\n return;\n }\n if (!readyEmitted && snapshot.ready) {\n readyEmitted = true;\n queue.push({ type: 'ready', snapshot });\n resolveReady(snapshot);\n }\n const newUid = obj.metadata?.uid as string | undefined;\n if (newUid && !uid) {\n uid = newUid;\n if (!opts.disableEvents && opts.ref.namespaced && opts.ref.namespace) {\n eventsStarted = true;\n eventsTask = startEventsWatch(opts, uid, controller.signal, queue, seenEventUids);\n }\n }\n };\n\n const xrPath = buildResourcePath(opts.ref);\n let firstListSeenItem = false;\n const xrTask = listAndWatch(opts.kubeConfig, {\n path: xrPath,\n fieldSelector: `metadata.name=${opts.ref.name}`,\n signal: controller.signal,\n list: async () => {\n const result = await listXrCollection(opts.kubeConfig, opts.ref);\n if (!firstListSeenItem) {\n if (result.items.length > 0) {\n firstListSeenItem = true;\n } else {\n const where = opts.ref.namespace ? ` in namespace ${opts.ref.namespace}` : '';\n const err = new Error(`XR ${opts.ref.kind}/${opts.ref.name} not found${where}`);\n queue.push({ type: 'error', error: err });\n rejectReady(err);\n controller.abort();\n }\n }\n return result;\n },\n onEvent: handleXrEvent,\n onError: (err) => {\n queue.push({ type: 'error', error: err });\n rejectReady(err);\n },\n });\n\n const done = (async () => {\n try {\n await xrTask;\n } finally {\n if (eventsStarted) {\n try {\n await eventsTask;\n } catch {\n /* already surfaced via onError */\n }\n }\n rejectReady(new Error('Watcher ended before XR became Ready'));\n queue.push({ type: 'end' });\n queue.close();\n }\n })();\n\n return {\n ready,\n done,\n stop: () => controller.abort(),\n [Symbol.asyncIterator]: () => queue[Symbol.asyncIterator](),\n };\n}\n\nfunction buildResourcePath(ref: XrRef): string {\n const base = ref.group ? `/apis/${ref.group}/${ref.version}` : `/api/${ref.version}`;\n if (ref.namespaced) {\n if (!ref.namespace) {\n throw new Error(`Namespace is required for namespaced resource ${ref.kind}/${ref.name}`);\n }\n return `${base}/namespaces/${ref.namespace}/${ref.plural}`;\n }\n return `${base}/${ref.plural}`;\n}\n\nasync function startEventsWatch(\n opts: CreateXrWatcherOptions,\n uid: string,\n signal: AbortSignal,\n queue: AsyncQueue<XrEvent>,\n seen: Set<string>,\n): Promise<void> {\n if (!opts.ref.namespace) return;\n const namespace = opts.ref.namespace;\n const path = `/api/v1/namespaces/${namespace}/events`;\n const fieldSelector = `involvedObject.uid=${uid}`;\n await listAndWatch(opts.kubeConfig, {\n path,\n fieldSelector,\n signal,\n list: () => listXrEvents(opts.kubeConfig, namespace, fieldSelector),\n onEvent: (phase, obj) => {\n if (phase !== 'ADDED' && phase !== 'MODIFIED') return;\n const eventUid = obj.metadata?.uid as string | undefined;\n if (eventUid && seen.has(eventUid)) return;\n if (eventUid) seen.add(eventUid);\n const event = toKubernetesEvent(obj);\n if (event) queue.push({ type: 'k8s-event', event });\n },\n onError: (err) => queue.push({ type: 'error', error: err }),\n });\n}\n\nfunction toKubernetesEvent(obj: KubernetesObject): KubernetesEvent | undefined {\n const raw = obj as KubernetesObject & {\n type?: string;\n reason?: string;\n message?: string;\n count?: number;\n firstTimestamp?: string;\n lastTimestamp?: string;\n eventTime?: string;\n involvedObject?: { kind?: string; name?: string };\n };\n if (!raw.message && !raw.reason) return undefined;\n const event: KubernetesEvent = {\n type: raw.type ?? 'Normal',\n reason: raw.reason ?? '',\n message: raw.message ?? '',\n count: raw.count ?? 1,\n };\n const first = raw.firstTimestamp ?? raw.eventTime;\n const last = raw.lastTimestamp ?? raw.eventTime ?? first;\n if (first) event.firstTimestamp = first;\n if (last) event.lastTimestamp = last;\n if (raw.involvedObject?.kind) event.involvedKind = raw.involvedObject.kind;\n if (raw.involvedObject?.name) event.involvedName = raw.involvedObject.name;\n return event;\n}\n","import { KubeConfig } from '@kubernetes/client-node';\n\nexport interface LoadKubeConfigOptions {\n /** Explicit kubeconfig file path. Falls back to `KUBECONFIG` / default discovery. */\n kubeconfig?: string;\n /** Override the active context. */\n context?: string;\n}\n\n/**\n * Load a `KubeConfig` from disk using `client-node`'s default rules (with\n * optional explicit overrides). The returned config has its current context\n * applied.\n */\nexport function loadKubeConfig(opts: LoadKubeConfigOptions = {}): KubeConfig {\n const kc = new KubeConfig();\n if (opts.kubeconfig) {\n kc.loadFromFile(opts.kubeconfig);\n } else {\n kc.loadFromDefault();\n }\n if (opts.context) {\n kc.setCurrentContext(opts.context);\n }\n return kc;\n}\n"],"mappings":"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"}
|
package/dist/render/index.d.mts
CHANGED
|
@@ -1,85 +1,2 @@
|
|
|
1
|
-
import { d as
|
|
2
|
-
|
|
3
|
-
//#region src/render/ci.d.ts
|
|
4
|
-
interface CiRendererOptions {
|
|
5
|
-
ref: XrRef;
|
|
6
|
-
/** Destination stream. Defaults to `process.stdout`. */
|
|
7
|
-
out?: NodeJS.WritableStream;
|
|
8
|
-
/** Heartbeat interval (ms). When no changes are observed within this window, emit a "no change" line so CI can see the job is alive. Defaults to 30000. Set to 0 to disable. */
|
|
9
|
-
heartbeatMs?: number;
|
|
10
|
-
/** When true, K8s Events are echoed inline. Defaults to false in CI (too noisy). */
|
|
11
|
-
showEvents?: boolean;
|
|
12
|
-
/** Every N consecutive idle heartbeats, expand the heartbeat line into a snapshot of unready + blocked resources. Defaults to 10. Set to 0 to disable. */
|
|
13
|
-
snapshotEveryHeartbeats?: number;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Append-only CI renderer.
|
|
17
|
-
*
|
|
18
|
-
* 1. Print a one-line "watching …" header on startup.
|
|
19
|
-
* 2. On the first snapshot, print a full snapshot block.
|
|
20
|
-
* 3. On every subsequent snapshot, print only the delta.
|
|
21
|
-
* 4. If nothing is printed within `heartbeatMs`, emit a single liveness line.
|
|
22
|
-
*/
|
|
23
|
-
declare function renderCI(watcher: XrWatcher, opts: CiRendererOptions): Promise<void>;
|
|
24
|
-
//#endregion
|
|
25
|
-
//#region src/render/format.d.ts
|
|
26
|
-
/** Single-character glyph for the status of a tree node. */
|
|
27
|
-
declare function statusGlyph(state: 'ready' | 'pending' | 'blocked'): string;
|
|
28
|
-
/** Compact header line summarising the XR. */
|
|
29
|
-
declare function formatHeader(snapshot: XrSnapshot, ref: {
|
|
30
|
-
kind: string;
|
|
31
|
-
name: string;
|
|
32
|
-
namespace?: string;
|
|
33
|
-
}): string;
|
|
34
|
-
/** Inline progress representation, e.g. `5/12 ready · 3 blocked`. */
|
|
35
|
-
declare function formatProgress(ready: number, total: number, blocked: number): string;
|
|
36
|
-
/** Format a Kubernetes Event as a single line. */
|
|
37
|
-
declare function formatEvent(ev: KubernetesEvent): string;
|
|
38
|
-
//#endregion
|
|
39
|
-
//#region src/render/tty.d.ts
|
|
40
|
-
interface TtyRendererOptions {
|
|
41
|
-
ref: XrRef;
|
|
42
|
-
/** Destination stream. Defaults to `process.stdout`. */
|
|
43
|
-
out?: NodeJS.WriteStream;
|
|
44
|
-
/** Maximum number of recent Kubernetes events shown in the tail. Defaults to 5. */
|
|
45
|
-
eventTailSize?: number;
|
|
46
|
-
/** Allows callers (and tests) to provide a custom `log-update` instance. */
|
|
47
|
-
logger?: {
|
|
48
|
-
(frame: string): void;
|
|
49
|
-
clear: () => void;
|
|
50
|
-
done: () => void;
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Live full-screen renderer using `log-update` to repaint a single frame.
|
|
55
|
-
* Resolves when the watcher ends.
|
|
56
|
-
*/
|
|
57
|
-
declare function renderTTY(watcher: XrWatcher, opts: TtyRendererOptions): Promise<void>;
|
|
58
|
-
//#endregion
|
|
59
|
-
//#region src/render/index.d.ts
|
|
60
|
-
type RendererMode = 'tty' | 'ci';
|
|
61
|
-
/** Auto-select a renderer based on whether the destination is an interactive TTY. */
|
|
62
|
-
declare function selectRenderer(stream: NodeJS.WriteStream | NodeJS.WritableStream): RendererMode;
|
|
63
|
-
interface RunRendererOptions {
|
|
64
|
-
ref: XrRef;
|
|
65
|
-
/** Forces a renderer instead of auto-detecting. */
|
|
66
|
-
mode?: RendererMode;
|
|
67
|
-
/** Destination stream. Defaults to `process.stdout`. */
|
|
68
|
-
out?: NodeJS.WriteStream;
|
|
69
|
-
/** Max number of recent events kept in the TTY tail. */
|
|
70
|
-
eventTailSize?: number;
|
|
71
|
-
/** CI: heartbeat interval (ms) for liveness lines. 0 disables. */
|
|
72
|
-
heartbeatMs?: number;
|
|
73
|
-
/** CI: include K8s Events inline. Off by default. */
|
|
74
|
-
showEvents?: boolean;
|
|
75
|
-
/** CI: every N idle heartbeats, expand into a snapshot of unready + blocked resources. 0 disables. */
|
|
76
|
-
snapshotEveryHeartbeats?: number;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Drive either renderer against the supplied watcher. Resolves when the
|
|
80
|
-
* watcher's event stream ends.
|
|
81
|
-
*/
|
|
82
|
-
declare function runRenderer(watcher: XrWatcher, opts: RunRendererOptions): Promise<void>;
|
|
83
|
-
//#endregion
|
|
84
|
-
export { type CiRendererOptions, RendererMode, RunRendererOptions, type TtyRendererOptions, formatEvent, formatHeader, formatProgress, renderCI, renderTTY, runRenderer, selectRenderer, statusGlyph };
|
|
85
|
-
//# sourceMappingURL=index.d.mts.map
|
|
1
|
+
import { a as TtyRendererOptions, c as formatHeader, d as CiRendererOptions, f as renderCI, i as selectRenderer, l as formatProgress, n as RunRendererOptions, o as renderTTY, r as runRenderer, s as formatEvent, t as RendererMode, u as statusGlyph } from "../index-Dp92t3xP.mjs";
|
|
2
|
+
export { type CiRendererOptions, RendererMode, RunRendererOptions, type TtyRendererOptions, formatEvent, formatHeader, formatProgress, renderCI, renderTTY, runRenderer, selectRenderer, statusGlyph };
|
package/dist/render/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as renderCI, c as formatProgress, l as statusGlyph, n as selectRenderer, o as formatEvent, r as renderTTY, s as formatHeader, t as runRenderer } from "../render-BMhfzOc2.mjs";
|
|
2
2
|
export { formatEvent, formatHeader, formatProgress, renderCI, renderTTY, runRenderer, selectRenderer, statusGlyph };
|