@64mb/freelens-power 1.4.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.
Files changed (46) hide show
  1. package/README.md +3 -3
  2. package/out/main/common/resource-search.js +61 -0
  3. package/out/main/common/resource-search.js.map +1 -0
  4. package/out/main/common/store/monitoring-preferences-store.js +5 -0
  5. package/out/main/common/store/monitoring-preferences-store.js.map +1 -1
  6. package/out/main/index.js +2 -0
  7. package/out/main/index.js.map +1 -1
  8. package/out/main/resource-search-database.js +209 -0
  9. package/out/main/resource-search-database.js.map +1 -0
  10. package/out/main/resource-search-worker.js +56 -0
  11. package/out/main/resource-search-worker.js.map +1 -0
  12. package/out/main/resource-search.js +155 -0
  13. package/out/main/resource-search.js.map +1 -0
  14. package/out/renderer/appearance.js +1 -1
  15. package/out/renderer/appearance.js.map +1 -1
  16. package/out/renderer/gateway-relationships-client.js +107 -0
  17. package/out/renderer/gateway-relationships-client.js.map +1 -0
  18. package/out/renderer/preferences/monitoring-preferences.js +10 -0
  19. package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
  20. package/out/renderer/preferences/preferences.js +3 -3
  21. package/out/renderer/preferences/preferences.js.map +1 -1
  22. package/out/renderer/resource-relationships.css.js +1 -1
  23. package/out/renderer/resource-relationships.css.js.map +1 -1
  24. package/out/renderer/resource-relationships.js +46 -8
  25. package/out/renderer/resource-relationships.js.map +1 -1
  26. package/out/renderer/resource-search-client.js +116 -0
  27. package/out/renderer/resource-search-client.js.map +1 -0
  28. package/out/renderer/resource-search-discovery.js +79 -0
  29. package/out/renderer/resource-search-discovery.js.map +1 -0
  30. package/out/renderer/resource-search-model.js +2 -1
  31. package/out/renderer/resource-search-model.js.map +1 -1
  32. package/out/renderer/resource-search-routing.js +78 -0
  33. package/out/renderer/resource-search-routing.js.map +1 -0
  34. package/out/renderer/resource-search-sync.js +199 -0
  35. package/out/renderer/resource-search-sync.js.map +1 -0
  36. package/out/renderer/resource-search.js +279 -54
  37. package/out/renderer/resource-search.js.map +1 -1
  38. package/out/renderer/src/common/gateway-relationships.js +122 -0
  39. package/out/renderer/src/common/gateway-relationships.js.map +1 -0
  40. package/out/renderer/src/common/resource-relationships.js +8 -0
  41. package/out/renderer/src/common/resource-relationships.js.map +1 -1
  42. package/out/renderer/src/common/resource-search.js +6 -0
  43. package/out/renderer/src/common/resource-search.js.map +1 -0
  44. package/out/renderer/src/common/store/monitoring-preferences-store.js +5 -0
  45. package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
  46. package/package.json +6 -2
@@ -4,7 +4,10 @@ const require_monitoring_preferences_store = require("./src/common/store/monitor
4
4
  const require__global_external_react = require("./_virtual/_global-external_react.js");
5
5
  const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
6
6
  const require_resource_relationships = require("./src/common/resource-relationships.js");
7
+ const require_gateway_relationships_client = require("./gateway-relationships-client.js");
7
8
  const require_resource_relationships_css = require("./resource-relationships.css.js");
9
+ const require_resource_search_client = require("./resource-search-client.js");
10
+ const require_resource_search_routing = require("./resource-search-routing.js");
8
11
  //#region src/renderer/resource-relationships.tsx
9
12
  var { Icon, Select } = require_extensions.Renderer.Component;
10
13
  var LIMIT = 100;
@@ -86,15 +89,18 @@ async function loadRelationshipInventory(namespace, service, signal) {
86
89
  list("Ingress", () => require_extensions.Renderer.K8sApi.ingressApi.list(options, query)),
87
90
  ...ownerApis.map((api) => list("Controller", () => api.list(options, query)))
88
91
  ]);
92
+ const clusterId = require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid;
93
+ const gateway = clusterId ? await require_gateway_relationships_client.loadGatewayInventory(require_resource_search_client.createResourceSearchTransport(clusterId, signal), signal, { focusedNamespace: namespace }) : void 0;
89
94
  return {
90
95
  pods,
91
96
  slices,
92
97
  ingresses,
93
98
  owners: owners.flatMap((items) => items ?? []),
94
- gaps
99
+ gaps,
100
+ gateway
95
101
  };
96
102
  }
97
- function ResourceCard({ node, incoming, onOpen, onHighlight }) {
103
+ function ResourceCard({ node, incoming, incomingEvidence, onOpen, onHighlight }) {
98
104
  return /* @__PURE__ */ require_jsx_runtime.jsxs("article", {
99
105
  className: "card",
100
106
  "data-node-id": node.id,
@@ -139,6 +145,10 @@ function ResourceCard({ node, incoming, onOpen, onHighlight }) {
139
145
  " ",
140
146
  node.detail
141
147
  ]
148
+ }),
149
+ !!(node.evidence?.length || incomingEvidence?.length) && /* @__PURE__ */ require_jsx_runtime.jsxs("details", {
150
+ className: "gateway-evidence",
151
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("summary", { children: "API evidence" }), [...node.evidence ?? [], ...incomingEvidence ?? []].map((evidence) => /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: evidence }, evidence))]
142
152
  })
143
153
  ]
144
154
  });
@@ -160,6 +170,8 @@ function roundedConnector(points) {
160
170
  return `${path} L ${last[0]} ${last[1]}`;
161
171
  }
162
172
  function RelationshipGraph({ graph, onOpen, ingressAvailable, hideUpstreams = false }) {
173
+ const upstreamIds = new Set(graph.nodes.filter((node) => node.role === "upstream").map((node) => node.id));
174
+ const upstreamGutter = !hideUpstreams && graph.edges.some((edge) => upstreamIds.has(edge.from) && upstreamIds.has(edge.to));
163
175
  const visibleRoles = roles.filter((role) => !hideUpstreams || role.id !== "upstream");
164
176
  const element = require__global_external_react.useRef(null);
165
177
  const [paths, setPaths] = require__global_external_react.useState([]);
@@ -221,7 +233,7 @@ function RelationshipGraph({ graph, onOpen, ingressAvailable, hideUpstreams = fa
221
233
  className: "graph-scroll",
222
234
  children: /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
223
235
  ref: element,
224
- className: `graph${hideUpstreams ? " hide-upstreams" : ""}`,
236
+ className: `graph${hideUpstreams ? " hide-upstreams" : ""}${upstreamGutter ? " upstream-gutter" : ""}`,
225
237
  style: { gridTemplateRows: `auto repeat(${Math.max(1, ...visibleRoles.map((role) => graph.nodes.filter((node) => node.role === role.id).length))}, auto)` },
226
238
  "aria-label": "Service resource relationship graph",
227
239
  children: [/* @__PURE__ */ require_jsx_runtime.jsxs("svg", {
@@ -263,12 +275,13 @@ function RelationshipGraph({ graph, onOpen, ingressAvailable, hideUpstreams = fa
263
275
  graph.nodes.filter((node) => node.role === role.id).map((node) => /* @__PURE__ */ require_jsx_runtime.jsx(ResourceCard, {
264
276
  node,
265
277
  incoming: [...new Set(graph.edges.filter((edge) => edge.to === node.id).map((edge) => edge.label))],
278
+ incomingEvidence: graph.edges.filter((edge) => edge.to === node.id && edge.evidence).map((edge) => edge.evidence),
266
279
  onOpen,
267
280
  onHighlight: setHighlight
268
281
  }, node.id)),
269
282
  !graph.nodes.some((node) => node.role === role.id) && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
270
283
  className: "card neutral",
271
- children: role.id === "upstream" ? ingressAvailable ? "No matching Ingress references observed" : "Ingress evidence unavailable" : "No relationships observed in available evidence"
284
+ children: role.id === "upstream" ? ingressAvailable ? "No matching upstream references observed" : "Ingress evidence unavailable" : "No relationships observed in available evidence"
272
285
  })
273
286
  ]
274
287
  }, role.id))]
@@ -387,7 +400,31 @@ function EnabledResourceRelationshipsPage() {
387
400
  const currentCluster = () => mounted.current && require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid === clusterId.current;
388
401
  setOpenError("");
389
402
  Promise.resolve().then(() => {
390
- if (currentCluster()) return require_extensions.Renderer.Navigation.showDetails(path);
403
+ if (!currentCluster()) return;
404
+ const gatewayPath = path.match(/^\/apis\/(gateway\.networking\.k8s\.io)\/([^/]+)\/(?:namespaces\/([^/]+)\/)?([^/]+)\/([^/]+)$/);
405
+ if (gatewayPath) {
406
+ const [, group, version, namespace, resource, name] = gatewayPath;
407
+ const kind = {
408
+ gateways: "Gateway",
409
+ gatewayclasses: "GatewayClass",
410
+ httproutes: "HTTPRoute",
411
+ grpcroutes: "GRPCRoute",
412
+ tcproutes: "TCPRoute",
413
+ tlsroutes: "TLSRoute",
414
+ udproutes: "UDPRoute"
415
+ }[resource];
416
+ return require_resource_search_routing.openSearchResource({
417
+ path,
418
+ group,
419
+ version,
420
+ namespace,
421
+ resource,
422
+ name,
423
+ kind,
424
+ labels: {}
425
+ });
426
+ }
427
+ return require_extensions.Renderer.Navigation.showDetails(path);
391
428
  }).catch(() => {
392
429
  if (currentCluster()) setOpenError("Resource details could not be opened; the object may be unavailable");
393
430
  });
@@ -402,7 +439,7 @@ function EnabledResourceRelationshipsPage() {
402
439
  }),
403
440
  /* @__PURE__ */ require_jsx_runtime.jsx("p", {
404
441
  className: "intro",
405
- children: "Follow one Service from upstream Ingress references to selected pods, published EndpointSlices and controller ownership. Connections show Kubernetes API evidence; they do not prove network reachability."
442
+ children: "Follow one Service from upstream Ingress and Gateway API references to selected pods, published EndpointSlices and controller ownership. Connections show Kubernetes API evidence; they do not prove network reachability."
406
443
  }),
407
444
  /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
408
445
  className: "controls",
@@ -547,13 +584,14 @@ function EnabledResourceRelationshipsPage() {
547
584
  edge.label,
548
585
  " →",
549
586
  " ",
550
- graph.nodes.find((node) => node.id === edge.to)?.name
587
+ graph.nodes.find((node) => node.id === edge.to)?.name,
588
+ edge.evidence && /* @__PURE__ */ require_jsx_runtime.jsx("div", { children: edge.evidence })
551
589
  ] }, `${edge.from}/${edge.to}/${edge.label}`))
552
590
  })] }),
553
591
  /* @__PURE__ */ require_jsx_runtime.jsxs("p", {
554
592
  className: "scope",
555
593
  children: [
556
- /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Snapshot from namespace-scoped read-only API requests." }),
594
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Snapshot from read-only API requests; Gateway discovery and references include other namespaces." }),
557
595
  /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Owner and node references can point to objects that were not queried." }),
558
596
  /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Endpoint counts can include separate address families." }),
559
597
  /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "No Secret or ConfigMap data is read." })
@@ -1 +1 @@
1
- {"version":3,"file":"resource-relationships.js","names":[],"sources":["../../src/renderer/resource-relationships.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { observer } from \"mobx-react\";\nimport { useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport {\n buildResourceRelationships,\n type RelationshipInventory,\n type RelationshipNode,\n type RelationshipObject,\n} from \"../common/resource-relationships\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport { resourceRelationshipsStyles } from \"./resource-relationships.css\";\n\nconst { Icon, Select } = Renderer.Component;\nconst LIMIT = 100;\ntype Service = Renderer.K8sApi.Service;\ninterface InventoryState {\n namespace: string;\n service: string;\n data: RelationshipInventory;\n}\nconst roles = [\n { id: \"upstream\", name: \"Upstream references\", relation: \"references backend\" },\n { id: \"service\", name: \"Focused Service\", relation: \"API configuration\" },\n { id: \"backend\", name: \"Selected & published backends\", relation: \"selector matches / published in\" },\n { id: \"placement\", name: \"Ownership & placement\", relation: \"owned by / assigned to\" },\n] as const;\nconst icons: Record<string, string> = {\n Ingress: \"login\",\n Service: \"share\",\n Pod: \"view_in_ar\",\n EndpointSlice: \"hub\",\n Node: \"dns\",\n ReplicaSet: \"layers\",\n Deployment: \"layers\",\n StatefulSet: \"layers\",\n DaemonSet: \"layers\",\n Job: \"work\",\n CronJob: \"schedule\",\n};\nexport async function loadRelationshipInventory(\n namespace: string,\n service: Service,\n signal: AbortSignal,\n): Promise<RelationshipInventory> {\n const gaps: string[] = [];\n const list = async <T,>(name: string, request: () => Promise<T[] | null>) => {\n try {\n const items = await request();\n if (!items) {\n gaps.push(`${name} inventory unavailable`);\n return undefined;\n }\n if (items.length > LIMIT)\n gaps.push(`${name} inventory capped at ${LIMIT} objects; additional relationships may be omitted`);\n return items.slice(0, LIMIT);\n } catch {\n if (!signal.aborted) gaps.push(`${name} inventory unavailable; access or API availability may be restricted`);\n return undefined;\n }\n };\n const options = { namespace, reqInit: { signal } };\n const query = { limit: LIMIT + 1 };\n const selector = Object.entries(service.spec.selector ?? {})\n .map(([key, value]) => `${key}=${value}`)\n .join(\",\");\n const external = service.spec.type === \"ExternalName\";\n const ownerApis = external\n ? []\n : [\n Renderer.K8sApi.replicaSetApi,\n Renderer.K8sApi.deploymentApi,\n Renderer.K8sApi.statefulSetApi,\n Renderer.K8sApi.daemonSetApi,\n Renderer.K8sApi.jobApi,\n Renderer.K8sApi.cronJobApi,\n ];\n const [pods, slices, ingresses, ...owners] = await Promise.all([\n selector && !external\n ? list(\"Pod\", () => Renderer.K8sApi.podsApi.list(options, { ...query, labelSelector: selector }))\n : Promise.resolve([]),\n service.spec.type === \"ExternalName\"\n ? Promise.resolve([])\n : list(\"EndpointSlice\", () =>\n Renderer.K8sApi.endpointSliceApi.list(options, {\n ...query,\n labelSelector: `kubernetes.io/service-name=${service.getName()}`,\n }),\n ),\n list(\"Ingress\", () => Renderer.K8sApi.ingressApi.list(options, query)),\n ...ownerApis.map((api) => list<RelationshipObject>(\"Controller\", () => api.list(options, query))),\n ]);\n return { pods, slices, ingresses, owners: owners.flatMap((items) => items ?? []), gaps };\n}\nfunction ResourceCard({\n node,\n incoming,\n onOpen,\n onHighlight,\n}: {\n onHighlight: (id?: string) => void;\n node: RelationshipNode;\n incoming: string[];\n onOpen: (path: string) => void;\n}) {\n return (\n <article\n className=\"card\"\n data-node-id={node.id}\n onMouseEnter={() => onHighlight(node.id)}\n onMouseLeave={() => onHighlight()}\n onFocus={() => onHighlight(node.id)}\n onBlur={() => onHighlight()}\n >\n <div className=\"relation-label\">{incoming.join(\" · \") || \"Focus\"}</div>\n <div className=\"card-kind\">\n <Icon material={icons[node.kind] ?? \"account_tree\"} size={16} />\n {node.kind}\n {node.inferred && \" · reference\"}\n </div>\n {node.selfLink ? (\n <button\n type=\"button\"\n className=\"resource-name\"\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n onOpen(node.selfLink as string);\n }}\n title={`Open ${node.kind} ${node.name}`}\n >\n {node.name}\n </button>\n ) : (\n <strong>{node.name}</strong>\n )}\n <p className={node.tone}>\n <Icon\n material={node.tone === \"warning\" ? \"warning\" : node.tone === \"healthy\" ? \"check_circle\" : \"info\"}\n size={14}\n />{\" \"}\n {node.detail}\n </p>\n </article>\n );\n}\nfunction roundedConnector(points: [number, number][]) {\n let path = `M ${points[0][0]} ${points[0][1]}`;\n for (let index = 1; index < points.length - 1; index++) {\n const [x, y] = points[index];\n const [px, py] = points[index - 1];\n const [nx, ny] = points[index + 1];\n const before = Math.hypot(x - px, y - py);\n const after = Math.hypot(nx - x, ny - y);\n if (!before || !after) continue;\n const radius = Math.min(6, before / 2, after / 2);\n path += ` L ${x + ((px - x) * radius) / before} ${y + ((py - y) * radius) / before}`;\n path += ` Q ${x} ${y} ${x + ((nx - x) * radius) / after} ${y + ((ny - y) * radius) / after}`;\n }\n const last = points[points.length - 1];\n return `${path} L ${last[0]} ${last[1]}`;\n}\nexport function RelationshipGraph({\n graph,\n onOpen,\n ingressAvailable,\n hideUpstreams = false,\n}: {\n graph: ReturnType<typeof buildResourceRelationships>;\n onOpen: (path: string) => void;\n ingressAvailable: boolean;\n hideUpstreams?: boolean;\n}) {\n const visibleRoles = roles.filter((role) => !hideUpstreams || role.id !== \"upstream\");\n const element = useRef<HTMLDivElement>(null);\n const [paths, setPaths] = useState<{ from: string; to: string; label: string; path: string }[]>([]);\n const [highlight, setHighlight] = useState<string>();\n useLayoutEffect(() => {\n const container = element.current;\n if (!container) return;\n const measure = () => {\n const origin = container.getBoundingClientRect();\n const cards = new Map(\n Array.from(container.querySelectorAll<HTMLElement>(\"[data-node-id]\")).map((card) => [\n card.dataset.nodeId,\n card.getBoundingClientRect(),\n ]),\n );\n setPaths(\n graph.edges.flatMap((edge, index) => {\n const from = cards.get(edge.from);\n const to = cards.get(edge.to);\n if (!from || !to) return [];\n const sy = from.top + from.height / 2 - origin.top;\n const ty = to.top + to.height / 2 - origin.top;\n const sameColumn = Math.abs(from.left - to.left) < 2;\n const sx = (sameColumn ? from.left : from.right) - origin.left;\n const tx = to.left - origin.left;\n const laneOffset = ((index % 3) - 1) * 8;\n const gutter = sameColumn ? sx - 28 + laneOffset : (sx + tx) / 2 + laneOffset;\n const crossesColumn = Array.from(cards.values()).some(\n (card) => card.left > from.left + 2 && card.right < to.left - 2,\n );\n let path =\n !sameColumn && sy === ty\n ? `M ${sx} ${sy} H ${tx}`\n : roundedConnector([\n [sx, sy],\n [gutter, sy],\n [gutter, ty],\n [tx, ty],\n ]);\n if (crossesColumn) {\n const bottom =\n Math.max(...Array.from(cards.values(), (card) => card.bottom)) - origin.top + 24 + (index % 3) * 8;\n const sourceGutter = sx + 32 + laneOffset;\n const targetGutter = tx - 32 + laneOffset;\n path = roundedConnector([\n [sx, sy],\n [sourceGutter, sy],\n [sourceGutter, bottom],\n [targetGutter, bottom],\n [targetGutter, ty],\n [tx, ty],\n ]);\n }\n return [{ ...edge, path }];\n }),\n );\n };\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(container);\n for (const card of container.querySelectorAll(\"[data-node-id]\")) observer?.observe(card);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener(\"resize\", measure);\n };\n }, [graph, hideUpstreams]);\n return (\n <div className=\"graph-scroll\">\n <div\n ref={element}\n className={`graph${hideUpstreams ? \" hide-upstreams\" : \"\"}`}\n style={{\n gridTemplateRows: `auto repeat(${Math.max(1, ...visibleRoles.map((role) => graph.nodes.filter((node) => node.role === role.id).length))}, auto)`,\n }}\n aria-label=\"Service resource relationship graph\"\n >\n <svg className=\"resource-connections\" width=\"100%\" height=\"100%\" aria-hidden=\"true\">\n <defs>\n <marker id=\"relationship-arrow\" markerWidth=\"5\" markerHeight=\"5\" refX=\"4\" refY=\"2.5\" orient=\"auto\">\n <path d=\"M0 0L5 2.5L0 5\" fill=\"currentColor\" />\n </marker>\n </defs>\n {paths.map((edge) => (\n <path\n key={`${edge.from}/${edge.to}/${edge.label}`}\n data-edge-from={edge.from}\n data-edge-to={edge.to}\n className={\n highlight\n ? edge.from === highlight || edge.to === highlight\n ? \"connection highlighted\"\n : \"connection dimmed\"\n : \"connection\"\n }\n d={edge.path}\n markerEnd=\"url(#relationship-arrow)\"\n >\n <title>\n {graph.nodes.find((node) => node.id === edge.from)?.name} → {edge.label} →{\" \"}\n {graph.nodes.find((node) => node.id === edge.to)?.name}\n </title>\n </path>\n ))}\n </svg>\n {visibleRoles.map((role) => (\n <section className=\"column\" key={role.id} aria-label={role.name}>\n <h3>{role.name}</h3>\n {graph.nodes\n .filter((node) => node.role === role.id)\n .map((node) => (\n <ResourceCard\n key={node.id}\n node={node}\n incoming={[...new Set(graph.edges.filter((edge) => edge.to === node.id).map((edge) => edge.label))]}\n onOpen={onOpen}\n onHighlight={setHighlight}\n />\n ))}\n {!graph.nodes.some((node) => node.role === role.id) && (\n <div className=\"card neutral\">\n {role.id === \"upstream\"\n ? ingressAvailable\n ? \"No matching Ingress references observed\"\n : \"Ingress evidence unavailable\"\n : \"No relationships observed in available evidence\"}\n </div>\n )}\n </section>\n ))}\n </div>\n </div>\n );\n}\nexport const ResourceRelationshipsPage = observer(({ enabled = true }: { enabled?: boolean }) => {\n if (!enabled)\n return (\n <div className=\"power-relationships\">\n <p>Resource relationships are disabled in Power settings</p>\n </div>\n );\n return <EnabledResourceRelationshipsPage key={Renderer.Catalog.activeCluster.get()?.metadata.uid ?? \"cluster\"} />;\n});\nfunction EnabledResourceRelationshipsPage() {\n const mounted = useRef(true);\n const clusterId = useRef(Renderer.Catalog.activeCluster.get()?.metadata.uid);\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n const [namespaces, setNamespaces] = useState<string[]>([]);\n const [namespace, setNamespace] = useState(\"\");\n const [services, setServices] = useState<Service[]>([]);\n const [serviceName, setServiceName] = useState(\"\");\n const [focusedPod, setFocusedPod] = useState(\"\");\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n const [hideUpstreams, setHideUpstreams] = useState(() =>\n preferences.hiddenUpstreamClusters.includes(clusterId.current ?? \"\"),\n );\n const toggleUpstreams = () => {\n const id = clusterId.current;\n if (!id || Renderer.Catalog.activeCluster.get()?.metadata.uid !== id) return;\n const hidden = !hideUpstreams;\n const otherClusters = preferences.hiddenUpstreamClusters.filter((cluster) => cluster !== id);\n preferences.hiddenUpstreamClusters = hidden ? [...otherClusters, id].slice(-100) : otherClusters;\n setHideUpstreams(hidden);\n };\n const [inventory, setInventory] = useState<InventoryState>();\n const [refresh, setRefresh] = useState(0);\n const [scopeRefresh, setScopeRefresh] = useState(0);\n const [namespaceMessage, setNamespaceMessage] = useState(\"\");\n const [scopeLoading, setScopeLoading] = useState(true);\n const [loading, setLoading] = useState(false);\n const [scopeMessage, setScopeMessage] = useState(\"\");\n const [openError, setOpenError] = useState(\"\");\n useEffect(() => {\n const controller = new AbortController();\n setScopeLoading(true);\n setNamespaceMessage(\"\");\n void Renderer.K8sApi.namespacesApi\n .list({ reqInit: { signal: controller.signal } }, { limit: LIMIT + 1 })\n .then((items) => {\n if (controller.signal.aborted) return;\n const names = (items ?? [])\n .slice(0, LIMIT)\n .map((item) => item.getName())\n .sort();\n setNamespaces(names);\n setNamespace((current) => (names.includes(current) ? current : (names[0] ?? \"\")));\n if ((items?.length ?? 0) > LIMIT) setNamespaceMessage(`Namespace selector is capped at ${LIMIT} entries`);\n else if (!names.length) setScopeMessage(\"No visible namespaces returned by the API\");\n })\n .catch(() => {\n if (!controller.signal.aborted)\n setScopeMessage(\"Namespace inventory unavailable; check access to namespace listing\");\n })\n .finally(() => {\n if (!controller.signal.aborted) setScopeLoading(false);\n });\n return () => controller.abort();\n }, [scopeRefresh]);\n useEffect(() => {\n if (!namespace) return;\n const controller = new AbortController();\n setLoading(false);\n setServices([]);\n setServiceName(\"\");\n setInventory(undefined);\n setScopeLoading(true);\n setScopeMessage(\"\");\n void Renderer.K8sApi.serviceApi\n .list({ namespace, reqInit: { signal: controller.signal } }, { limit: LIMIT + 1 })\n .then((items) => {\n if (controller.signal.aborted) return;\n const visible = (items ?? []).slice(0, LIMIT).sort((a, b) => a.getName().localeCompare(b.getName()));\n setServices(visible);\n setServiceName(visible[0]?.getName() ?? \"\");\n if ((items?.length ?? 0) > LIMIT)\n setScopeMessage(`Service selector is capped at ${LIMIT} entries; additional Services are not shown`);\n else if (!visible.length) setScopeMessage(\"No visible Services in this namespace\");\n })\n .catch(() => {\n if (!controller.signal.aborted) setScopeMessage(\"Service inventory unavailable; check namespace access\");\n })\n .finally(() => {\n if (!controller.signal.aborted) setScopeLoading(false);\n });\n return () => controller.abort();\n }, [namespace, scopeRefresh]);\n const service = services.find((item) => item.getName() === serviceName);\n useEffect(() => {\n if (!service || !namespace || service.metadata.namespace !== namespace) return;\n const controller = new AbortController();\n setLoading(true);\n setInventory(undefined);\n setFocusedPod(\"\");\n setOpenError(\"\");\n void loadRelationshipInventory(namespace, service, controller.signal)\n .then((data) => {\n if (!controller.signal.aborted) setInventory({ namespace, service: service.getName(), data });\n })\n .finally(() => {\n if (!controller.signal.aborted) setLoading(false);\n });\n return () => controller.abort();\n }, [namespace, service, refresh]);\n const current = inventory?.namespace === namespace && inventory.service === serviceName ? inventory : undefined;\n const graph =\n current && service ? buildResourceRelationships(service, current.data, focusedPod || undefined) : undefined;\n const openResource = (path: string) => {\n const currentCluster = () =>\n mounted.current && Renderer.Catalog.activeCluster.get()?.metadata.uid === clusterId.current;\n setOpenError(\"\");\n void Promise.resolve()\n .then(() => {\n if (currentCluster()) return Renderer.Navigation.showDetails(path);\n })\n .catch(() => {\n if (currentCluster()) setOpenError(\"Resource details could not be opened; the object may be unavailable\");\n });\n };\n return (\n <div className=\"power-relationships\">\n <style>{resourceRelationshipsStyles}</style>\n <div className=\"page-heading\">\n <h2>Resource relationships</h2>\n </div>\n <p className=\"intro\">\n Follow one Service from upstream Ingress references to selected pods, published EndpointSlices and controller\n ownership. Connections show Kubernetes API evidence; they do not prove network reachability.\n </p>\n <div className=\"controls\">\n <label className=\"control\">\n <span>Namespace</span>\n <Select\n aria-label=\"Relationship namespace\"\n value={namespace}\n options={namespaces.map((name) => ({ value: name, label: name }))}\n onChange={(option) => setNamespace(option?.value ?? \"\")}\n isDisabled={!namespaces.length}\n />\n </label>\n <label className=\"control\">\n <span>Service</span>\n <Select\n aria-label=\"Relationship Service\"\n value={serviceName}\n options={services.map((item) => ({ value: item.getName(), label: item.getName() }))}\n onChange={(option) => setServiceName(option?.value ?? \"\")}\n isDisabled={!services.length || scopeLoading}\n />\n </label>\n <label className=\"control\">\n <span>Pod focus</span>\n <Select\n aria-label=\"Relationship pod focus\"\n value={focusedPod}\n options={[\n { value: \"\", label: \"All selected pods (up to 8)\" },\n ...(graph?.podOptions ?? []).map((name) => ({ value: name, label: name })),\n ]}\n onChange={(option) => setFocusedPod(option?.value ?? \"\")}\n isDisabled={!graph?.podOptions.length}\n />\n </label>\n <button\n type=\"button\"\n className=\"graph-option\"\n aria-pressed={hideUpstreams}\n onClick={toggleUpstreams}\n title={hideUpstreams ? \"Show upstream references\" : \"Hide upstream references\"}\n disabled={!clusterId.current}\n >\n <Icon material={hideUpstreams ? \"visibility_off\" : \"visibility\"} size={16} />\n Hide Upstream\n </button>\n <button\n type=\"button\"\n className=\"refresh\"\n onClick={() => (service ? setRefresh((value) => value + 1) : setScopeRefresh((value) => value + 1))}\n disabled={loading || scopeLoading}\n title=\"Refresh scoped API evidence\"\n >\n <Icon material=\"refresh\" size={18} />\n Refresh\n </button>\n </div>\n {namespaceMessage && (\n <div className=\"notice\" role=\"status\">\n {namespaceMessage}\n </div>\n )}\n {scopeMessage && (\n <div className=\"notice\" role=\"status\">\n {scopeMessage}\n </div>\n )}\n {openError && (\n <div className=\"notice warning\" role=\"alert\">\n {openError}\n </div>\n )}\n {(scopeLoading || loading) && (\n <div className=\"empty\" role=\"status\">\n Loading scoped relationship evidence…\n </div>\n )}\n {!scopeLoading && !loading && !service && (\n <div className=\"empty\">Select a namespace with a visible Service to inspect relationships</div>\n )}\n {graph && !loading && (\n <>\n <div className=\"notice relationship-summary\">\n <strong>{graph.diagnostics.summary}</strong>\n <div className=\"ports\">\n {graph.diagnostics.ports.map((port) => (\n <span key={port.name}>\n {port.name} · {port.protocol} → {port.target} · published:{\" \"}\n {port.endpointPorts.join(\", \") || \"unknown\"}\n {port.unresolved ? ` · ${port.unresolved} unresolved named targets` : \"\"}\n </span>\n ))}\n </div>\n </div>\n {graph.gaps.length > 0 && (\n <details open className=\"notice\">\n <summary>Partial coverage ({graph.gaps.length})</summary>\n <ul>\n {graph.gaps.map((gap) => (\n <li key={gap}>{gap}</li>\n ))}\n </ul>\n </details>\n )}\n <RelationshipGraph\n graph={graph}\n onOpen={openResource}\n ingressAvailable={current?.data.ingresses !== undefined}\n hideUpstreams={hideUpstreams}\n />\n {graph.omitted > 0 && (\n <div className=\"notice\">\n {graph.omitted} additional visible resources omitted to keep the graph readable. Focus a pod to inspect\n its ownership and placement.\n </div>\n )}\n <details>\n <summary>Accessible connection list ({graph.edges.length})</summary>\n <ul className=\"edge-list\">\n {graph.edges.map((edge) => (\n <li key={`${edge.from}/${edge.to}/${edge.label}`}>\n {graph.nodes.find((node) => node.id === edge.from)?.name} → {edge.label} →{\" \"}\n {graph.nodes.find((node) => node.id === edge.to)?.name}\n </li>\n ))}\n </ul>\n </details>\n <p className=\"scope\">\n <span>Snapshot from namespace-scoped read-only API requests.</span>\n <span>Owner and node references can point to objects that were not queried.</span>\n <span>Endpoint counts can include separate address families.</span>\n <span>No Secret or ConfigMap data is read.</span>\n </p>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"resource-relationships.js","names":[],"sources":["../../src/renderer/resource-relationships.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { observer } from \"mobx-react\";\nimport { useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport {\n buildResourceRelationships,\n type RelationshipInventory,\n type RelationshipNode,\n type RelationshipObject,\n} from \"../common/resource-relationships\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport { loadGatewayInventory } from \"./gateway-relationships-client\";\nimport { resourceRelationshipsStyles } from \"./resource-relationships.css\";\nimport { createResourceSearchTransport } from \"./resource-search-client\";\nimport { openSearchResource } from \"./resource-search-routing\";\n\nconst { Icon, Select } = Renderer.Component;\nconst LIMIT = 100;\ntype Service = Renderer.K8sApi.Service;\ninterface InventoryState {\n namespace: string;\n service: string;\n data: RelationshipInventory;\n}\nconst roles = [\n { id: \"upstream\", name: \"Upstream references\", relation: \"references backend\" },\n { id: \"service\", name: \"Focused Service\", relation: \"API configuration\" },\n { id: \"backend\", name: \"Selected & published backends\", relation: \"selector matches / published in\" },\n { id: \"placement\", name: \"Ownership & placement\", relation: \"owned by / assigned to\" },\n] as const;\nconst icons: Record<string, string> = {\n Ingress: \"login\",\n Service: \"share\",\n Pod: \"view_in_ar\",\n EndpointSlice: \"hub\",\n Node: \"dns\",\n ReplicaSet: \"layers\",\n Deployment: \"layers\",\n StatefulSet: \"layers\",\n DaemonSet: \"layers\",\n Job: \"work\",\n CronJob: \"schedule\",\n};\nexport async function loadRelationshipInventory(\n namespace: string,\n service: Service,\n signal: AbortSignal,\n): Promise<RelationshipInventory> {\n const gaps: string[] = [];\n const list = async <T,>(name: string, request: () => Promise<T[] | null>) => {\n try {\n const items = await request();\n if (!items) {\n gaps.push(`${name} inventory unavailable`);\n return undefined;\n }\n if (items.length > LIMIT)\n gaps.push(`${name} inventory capped at ${LIMIT} objects; additional relationships may be omitted`);\n return items.slice(0, LIMIT);\n } catch {\n if (!signal.aborted) gaps.push(`${name} inventory unavailable; access or API availability may be restricted`);\n return undefined;\n }\n };\n const options = { namespace, reqInit: { signal } };\n const query = { limit: LIMIT + 1 };\n const selector = Object.entries(service.spec.selector ?? {})\n .map(([key, value]) => `${key}=${value}`)\n .join(\",\");\n const external = service.spec.type === \"ExternalName\";\n const ownerApis = external\n ? []\n : [\n Renderer.K8sApi.replicaSetApi,\n Renderer.K8sApi.deploymentApi,\n Renderer.K8sApi.statefulSetApi,\n Renderer.K8sApi.daemonSetApi,\n Renderer.K8sApi.jobApi,\n Renderer.K8sApi.cronJobApi,\n ];\n const [pods, slices, ingresses, ...owners] = await Promise.all([\n selector && !external\n ? list(\"Pod\", () => Renderer.K8sApi.podsApi.list(options, { ...query, labelSelector: selector }))\n : Promise.resolve([]),\n service.spec.type === \"ExternalName\"\n ? Promise.resolve([])\n : list(\"EndpointSlice\", () =>\n Renderer.K8sApi.endpointSliceApi.list(options, {\n ...query,\n labelSelector: `kubernetes.io/service-name=${service.getName()}`,\n }),\n ),\n list(\"Ingress\", () => Renderer.K8sApi.ingressApi.list(options, query)),\n ...ownerApis.map((api) => list<RelationshipObject>(\"Controller\", () => api.list(options, query))),\n ]);\n const clusterId = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n const gateway = clusterId\n ? await loadGatewayInventory(createResourceSearchTransport(clusterId, signal), signal, {\n focusedNamespace: namespace,\n })\n : undefined;\n return { pods, slices, ingresses, owners: owners.flatMap((items) => items ?? []), gaps, gateway };\n}\nfunction ResourceCard({\n node,\n incoming,\n incomingEvidence,\n onOpen,\n onHighlight,\n}: {\n onHighlight: (id?: string) => void;\n node: RelationshipNode;\n incoming: string[];\n incomingEvidence?: string[];\n onOpen: (path: string) => void;\n}) {\n return (\n <article\n className=\"card\"\n data-node-id={node.id}\n onMouseEnter={() => onHighlight(node.id)}\n onMouseLeave={() => onHighlight()}\n onFocus={() => onHighlight(node.id)}\n onBlur={() => onHighlight()}\n >\n <div className=\"relation-label\">{incoming.join(\" · \") || \"Focus\"}</div>\n <div className=\"card-kind\">\n <Icon material={icons[node.kind] ?? \"account_tree\"} size={16} />\n {node.kind}\n {node.inferred && \" · reference\"}\n </div>\n {node.selfLink ? (\n <button\n type=\"button\"\n className=\"resource-name\"\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n onOpen(node.selfLink as string);\n }}\n title={`Open ${node.kind} ${node.name}`}\n >\n {node.name}\n </button>\n ) : (\n <strong>{node.name}</strong>\n )}\n <p className={node.tone}>\n <Icon\n material={node.tone === \"warning\" ? \"warning\" : node.tone === \"healthy\" ? \"check_circle\" : \"info\"}\n size={14}\n />{\" \"}\n {node.detail}\n </p>\n {!!(node.evidence?.length || incomingEvidence?.length) && (\n <details className=\"gateway-evidence\">\n <summary>API evidence</summary>\n {[...(node.evidence ?? []), ...(incomingEvidence ?? [])].map((evidence) => (\n <p key={evidence}>{evidence}</p>\n ))}\n </details>\n )}\n </article>\n );\n}\nfunction roundedConnector(points: [number, number][]) {\n let path = `M ${points[0][0]} ${points[0][1]}`;\n for (let index = 1; index < points.length - 1; index++) {\n const [x, y] = points[index];\n const [px, py] = points[index - 1];\n const [nx, ny] = points[index + 1];\n const before = Math.hypot(x - px, y - py);\n const after = Math.hypot(nx - x, ny - y);\n if (!before || !after) continue;\n const radius = Math.min(6, before / 2, after / 2);\n path += ` L ${x + ((px - x) * radius) / before} ${y + ((py - y) * radius) / before}`;\n path += ` Q ${x} ${y} ${x + ((nx - x) * radius) / after} ${y + ((ny - y) * radius) / after}`;\n }\n const last = points[points.length - 1];\n return `${path} L ${last[0]} ${last[1]}`;\n}\nexport function RelationshipGraph({\n graph,\n onOpen,\n ingressAvailable,\n hideUpstreams = false,\n}: {\n graph: ReturnType<typeof buildResourceRelationships>;\n onOpen: (path: string) => void;\n ingressAvailable: boolean;\n hideUpstreams?: boolean;\n}) {\n const upstreamIds = new Set(graph.nodes.filter((node) => node.role === \"upstream\").map((node) => node.id));\n const upstreamGutter =\n !hideUpstreams && graph.edges.some((edge) => upstreamIds.has(edge.from) && upstreamIds.has(edge.to));\n const visibleRoles = roles.filter((role) => !hideUpstreams || role.id !== \"upstream\");\n const element = useRef<HTMLDivElement>(null);\n const [paths, setPaths] = useState<{ from: string; to: string; label: string; path: string }[]>([]);\n const [highlight, setHighlight] = useState<string>();\n useLayoutEffect(() => {\n const container = element.current;\n if (!container) return;\n const measure = () => {\n const origin = container.getBoundingClientRect();\n const cards = new Map(\n Array.from(container.querySelectorAll<HTMLElement>(\"[data-node-id]\")).map((card) => [\n card.dataset.nodeId,\n card.getBoundingClientRect(),\n ]),\n );\n setPaths(\n graph.edges.flatMap((edge, index) => {\n const from = cards.get(edge.from);\n const to = cards.get(edge.to);\n if (!from || !to) return [];\n const sy = from.top + from.height / 2 - origin.top;\n const ty = to.top + to.height / 2 - origin.top;\n const sameColumn = Math.abs(from.left - to.left) < 2;\n const sx = (sameColumn ? from.left : from.right) - origin.left;\n const tx = to.left - origin.left;\n const laneOffset = ((index % 3) - 1) * 8;\n const gutter = sameColumn ? sx - 28 + laneOffset : (sx + tx) / 2 + laneOffset;\n const crossesColumn = Array.from(cards.values()).some(\n (card) => card.left > from.left + 2 && card.right < to.left - 2,\n );\n let path =\n !sameColumn && sy === ty\n ? `M ${sx} ${sy} H ${tx}`\n : roundedConnector([\n [sx, sy],\n [gutter, sy],\n [gutter, ty],\n [tx, ty],\n ]);\n if (crossesColumn) {\n const bottom =\n Math.max(...Array.from(cards.values(), (card) => card.bottom)) - origin.top + 24 + (index % 3) * 8;\n const sourceGutter = sx + 32 + laneOffset;\n const targetGutter = tx - 32 + laneOffset;\n path = roundedConnector([\n [sx, sy],\n [sourceGutter, sy],\n [sourceGutter, bottom],\n [targetGutter, bottom],\n [targetGutter, ty],\n [tx, ty],\n ]);\n }\n return [{ ...edge, path }];\n }),\n );\n };\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(container);\n for (const card of container.querySelectorAll(\"[data-node-id]\")) observer?.observe(card);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener(\"resize\", measure);\n };\n }, [graph, hideUpstreams]);\n return (\n <div className=\"graph-scroll\">\n <div\n ref={element}\n className={`graph${hideUpstreams ? \" hide-upstreams\" : \"\"}${upstreamGutter ? \" upstream-gutter\" : \"\"}`}\n style={{\n gridTemplateRows: `auto repeat(${Math.max(1, ...visibleRoles.map((role) => graph.nodes.filter((node) => node.role === role.id).length))}, auto)`,\n }}\n aria-label=\"Service resource relationship graph\"\n >\n <svg className=\"resource-connections\" width=\"100%\" height=\"100%\" aria-hidden=\"true\">\n <defs>\n <marker id=\"relationship-arrow\" markerWidth=\"5\" markerHeight=\"5\" refX=\"4\" refY=\"2.5\" orient=\"auto\">\n <path d=\"M0 0L5 2.5L0 5\" fill=\"currentColor\" />\n </marker>\n </defs>\n {paths.map((edge) => (\n <path\n key={`${edge.from}/${edge.to}/${edge.label}`}\n data-edge-from={edge.from}\n data-edge-to={edge.to}\n className={\n highlight\n ? edge.from === highlight || edge.to === highlight\n ? \"connection highlighted\"\n : \"connection dimmed\"\n : \"connection\"\n }\n d={edge.path}\n markerEnd=\"url(#relationship-arrow)\"\n >\n <title>\n {graph.nodes.find((node) => node.id === edge.from)?.name} → {edge.label} →{\" \"}\n {graph.nodes.find((node) => node.id === edge.to)?.name}\n </title>\n </path>\n ))}\n </svg>\n {visibleRoles.map((role) => (\n <section className=\"column\" key={role.id} aria-label={role.name}>\n <h3>{role.name}</h3>\n {graph.nodes\n .filter((node) => node.role === role.id)\n .map((node) => (\n <ResourceCard\n key={node.id}\n node={node}\n incoming={[...new Set(graph.edges.filter((edge) => edge.to === node.id).map((edge) => edge.label))]}\n incomingEvidence={graph.edges\n .filter((edge) => edge.to === node.id && edge.evidence)\n .map((edge) => edge.evidence as string)}\n onOpen={onOpen}\n onHighlight={setHighlight}\n />\n ))}\n {!graph.nodes.some((node) => node.role === role.id) && (\n <div className=\"card neutral\">\n {role.id === \"upstream\"\n ? ingressAvailable\n ? \"No matching upstream references observed\"\n : \"Ingress evidence unavailable\"\n : \"No relationships observed in available evidence\"}\n </div>\n )}\n </section>\n ))}\n </div>\n </div>\n );\n}\nexport const ResourceRelationshipsPage = observer(({ enabled = true }: { enabled?: boolean }) => {\n if (!enabled)\n return (\n <div className=\"power-relationships\">\n <p>Resource relationships are disabled in Power settings</p>\n </div>\n );\n return <EnabledResourceRelationshipsPage key={Renderer.Catalog.activeCluster.get()?.metadata.uid ?? \"cluster\"} />;\n});\nfunction EnabledResourceRelationshipsPage() {\n const mounted = useRef(true);\n const clusterId = useRef(Renderer.Catalog.activeCluster.get()?.metadata.uid);\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n const [namespaces, setNamespaces] = useState<string[]>([]);\n const [namespace, setNamespace] = useState(\"\");\n const [services, setServices] = useState<Service[]>([]);\n const [serviceName, setServiceName] = useState(\"\");\n const [focusedPod, setFocusedPod] = useState(\"\");\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n const [hideUpstreams, setHideUpstreams] = useState(() =>\n preferences.hiddenUpstreamClusters.includes(clusterId.current ?? \"\"),\n );\n const toggleUpstreams = () => {\n const id = clusterId.current;\n if (!id || Renderer.Catalog.activeCluster.get()?.metadata.uid !== id) return;\n const hidden = !hideUpstreams;\n const otherClusters = preferences.hiddenUpstreamClusters.filter((cluster) => cluster !== id);\n preferences.hiddenUpstreamClusters = hidden ? [...otherClusters, id].slice(-100) : otherClusters;\n setHideUpstreams(hidden);\n };\n const [inventory, setInventory] = useState<InventoryState>();\n const [refresh, setRefresh] = useState(0);\n const [scopeRefresh, setScopeRefresh] = useState(0);\n const [namespaceMessage, setNamespaceMessage] = useState(\"\");\n const [scopeLoading, setScopeLoading] = useState(true);\n const [loading, setLoading] = useState(false);\n const [scopeMessage, setScopeMessage] = useState(\"\");\n const [openError, setOpenError] = useState(\"\");\n useEffect(() => {\n const controller = new AbortController();\n setScopeLoading(true);\n setNamespaceMessage(\"\");\n void Renderer.K8sApi.namespacesApi\n .list({ reqInit: { signal: controller.signal } }, { limit: LIMIT + 1 })\n .then((items) => {\n if (controller.signal.aborted) return;\n const names = (items ?? [])\n .slice(0, LIMIT)\n .map((item) => item.getName())\n .sort();\n setNamespaces(names);\n setNamespace((current) => (names.includes(current) ? current : (names[0] ?? \"\")));\n if ((items?.length ?? 0) > LIMIT) setNamespaceMessage(`Namespace selector is capped at ${LIMIT} entries`);\n else if (!names.length) setScopeMessage(\"No visible namespaces returned by the API\");\n })\n .catch(() => {\n if (!controller.signal.aborted)\n setScopeMessage(\"Namespace inventory unavailable; check access to namespace listing\");\n })\n .finally(() => {\n if (!controller.signal.aborted) setScopeLoading(false);\n });\n return () => controller.abort();\n }, [scopeRefresh]);\n useEffect(() => {\n if (!namespace) return;\n const controller = new AbortController();\n setLoading(false);\n setServices([]);\n setServiceName(\"\");\n setInventory(undefined);\n setScopeLoading(true);\n setScopeMessage(\"\");\n void Renderer.K8sApi.serviceApi\n .list({ namespace, reqInit: { signal: controller.signal } }, { limit: LIMIT + 1 })\n .then((items) => {\n if (controller.signal.aborted) return;\n const visible = (items ?? []).slice(0, LIMIT).sort((a, b) => a.getName().localeCompare(b.getName()));\n setServices(visible);\n setServiceName(visible[0]?.getName() ?? \"\");\n if ((items?.length ?? 0) > LIMIT)\n setScopeMessage(`Service selector is capped at ${LIMIT} entries; additional Services are not shown`);\n else if (!visible.length) setScopeMessage(\"No visible Services in this namespace\");\n })\n .catch(() => {\n if (!controller.signal.aborted) setScopeMessage(\"Service inventory unavailable; check namespace access\");\n })\n .finally(() => {\n if (!controller.signal.aborted) setScopeLoading(false);\n });\n return () => controller.abort();\n }, [namespace, scopeRefresh]);\n const service = services.find((item) => item.getName() === serviceName);\n useEffect(() => {\n if (!service || !namespace || service.metadata.namespace !== namespace) return;\n const controller = new AbortController();\n setLoading(true);\n setInventory(undefined);\n setFocusedPod(\"\");\n setOpenError(\"\");\n void loadRelationshipInventory(namespace, service, controller.signal)\n .then((data) => {\n if (!controller.signal.aborted) setInventory({ namespace, service: service.getName(), data });\n })\n .finally(() => {\n if (!controller.signal.aborted) setLoading(false);\n });\n return () => controller.abort();\n }, [namespace, service, refresh]);\n const current = inventory?.namespace === namespace && inventory.service === serviceName ? inventory : undefined;\n const graph =\n current && service ? buildResourceRelationships(service, current.data, focusedPod || undefined) : undefined;\n const openResource = (path: string) => {\n const currentCluster = () =>\n mounted.current && Renderer.Catalog.activeCluster.get()?.metadata.uid === clusterId.current;\n setOpenError(\"\");\n void Promise.resolve()\n .then(() => {\n if (!currentCluster()) return;\n const gatewayPath = path.match(\n /^\\/apis\\/(gateway\\.networking\\.k8s\\.io)\\/([^/]+)\\/(?:namespaces\\/([^/]+)\\/)?([^/]+)\\/([^/]+)$/,\n );\n if (gatewayPath) {\n const [, group, version, namespace, resource, name] = gatewayPath;\n const kind = (\n {\n gateways: \"Gateway\",\n gatewayclasses: \"GatewayClass\",\n httproutes: \"HTTPRoute\",\n grpcroutes: \"GRPCRoute\",\n tcproutes: \"TCPRoute\",\n tlsroutes: \"TLSRoute\",\n udproutes: \"UDPRoute\",\n } as Record<string, string>\n )[resource];\n return openSearchResource({ path, group, version, namespace, resource, name, kind, labels: {} });\n }\n return Renderer.Navigation.showDetails(path);\n })\n .catch(() => {\n if (currentCluster()) setOpenError(\"Resource details could not be opened; the object may be unavailable\");\n });\n };\n return (\n <div className=\"power-relationships\">\n <style>{resourceRelationshipsStyles}</style>\n <div className=\"page-heading\">\n <h2>Resource relationships</h2>\n </div>\n <p className=\"intro\">\n Follow one Service from upstream Ingress and Gateway API references to selected pods, published EndpointSlices\n and controller ownership. Connections show Kubernetes API evidence; they do not prove network reachability.\n </p>\n <div className=\"controls\">\n <label className=\"control\">\n <span>Namespace</span>\n <Select\n aria-label=\"Relationship namespace\"\n value={namespace}\n options={namespaces.map((name) => ({ value: name, label: name }))}\n onChange={(option) => setNamespace(option?.value ?? \"\")}\n isDisabled={!namespaces.length}\n />\n </label>\n <label className=\"control\">\n <span>Service</span>\n <Select\n aria-label=\"Relationship Service\"\n value={serviceName}\n options={services.map((item) => ({ value: item.getName(), label: item.getName() }))}\n onChange={(option) => setServiceName(option?.value ?? \"\")}\n isDisabled={!services.length || scopeLoading}\n />\n </label>\n <label className=\"control\">\n <span>Pod focus</span>\n <Select\n aria-label=\"Relationship pod focus\"\n value={focusedPod}\n options={[\n { value: \"\", label: \"All selected pods (up to 8)\" },\n ...(graph?.podOptions ?? []).map((name) => ({ value: name, label: name })),\n ]}\n onChange={(option) => setFocusedPod(option?.value ?? \"\")}\n isDisabled={!graph?.podOptions.length}\n />\n </label>\n <button\n type=\"button\"\n className=\"graph-option\"\n aria-pressed={hideUpstreams}\n onClick={toggleUpstreams}\n title={hideUpstreams ? \"Show upstream references\" : \"Hide upstream references\"}\n disabled={!clusterId.current}\n >\n <Icon material={hideUpstreams ? \"visibility_off\" : \"visibility\"} size={16} />\n Hide Upstream\n </button>\n <button\n type=\"button\"\n className=\"refresh\"\n onClick={() => (service ? setRefresh((value) => value + 1) : setScopeRefresh((value) => value + 1))}\n disabled={loading || scopeLoading}\n title=\"Refresh scoped API evidence\"\n >\n <Icon material=\"refresh\" size={18} />\n Refresh\n </button>\n </div>\n {namespaceMessage && (\n <div className=\"notice\" role=\"status\">\n {namespaceMessage}\n </div>\n )}\n {scopeMessage && (\n <div className=\"notice\" role=\"status\">\n {scopeMessage}\n </div>\n )}\n {openError && (\n <div className=\"notice warning\" role=\"alert\">\n {openError}\n </div>\n )}\n {(scopeLoading || loading) && (\n <div className=\"empty\" role=\"status\">\n Loading scoped relationship evidence…\n </div>\n )}\n {!scopeLoading && !loading && !service && (\n <div className=\"empty\">Select a namespace with a visible Service to inspect relationships</div>\n )}\n {graph && !loading && (\n <>\n <div className=\"notice relationship-summary\">\n <strong>{graph.diagnostics.summary}</strong>\n <div className=\"ports\">\n {graph.diagnostics.ports.map((port) => (\n <span key={port.name}>\n {port.name} · {port.protocol} → {port.target} · published:{\" \"}\n {port.endpointPorts.join(\", \") || \"unknown\"}\n {port.unresolved ? ` · ${port.unresolved} unresolved named targets` : \"\"}\n </span>\n ))}\n </div>\n </div>\n {graph.gaps.length > 0 && (\n <details open className=\"notice\">\n <summary>Partial coverage ({graph.gaps.length})</summary>\n <ul>\n {graph.gaps.map((gap) => (\n <li key={gap}>{gap}</li>\n ))}\n </ul>\n </details>\n )}\n <RelationshipGraph\n graph={graph}\n onOpen={openResource}\n ingressAvailable={current?.data.ingresses !== undefined}\n hideUpstreams={hideUpstreams}\n />\n {graph.omitted > 0 && (\n <div className=\"notice\">\n {graph.omitted} additional visible resources omitted to keep the graph readable. Focus a pod to inspect\n its ownership and placement.\n </div>\n )}\n <details>\n <summary>Accessible connection list ({graph.edges.length})</summary>\n <ul className=\"edge-list\">\n {graph.edges.map((edge) => (\n <li key={`${edge.from}/${edge.to}/${edge.label}`}>\n {graph.nodes.find((node) => node.id === edge.from)?.name} → {edge.label} →{\" \"}\n {graph.nodes.find((node) => node.id === edge.to)?.name}\n {edge.evidence && <div>{edge.evidence}</div>}\n </li>\n ))}\n </ul>\n </details>\n <p className=\"scope\">\n <span>\n Snapshot from read-only API requests; Gateway discovery and references include other namespaces.\n </span>\n <span>Owner and node references can point to objects that were not queried.</span>\n <span>Endpoint counts can include separate address families.</span>\n <span>No Secret or ConfigMap data is read.</span>\n </p>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+ const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
2
+ const require_resource_search = require("./src/common/resource-search.js");
3
+ let electron = require("electron");
4
+ let node_crypto = require("node:crypto");
5
+ //#region src/renderer/resource-search-client.ts
6
+ function createResourceSearchTransport(clusterId, signal, options = {}) {
7
+ const frameHost = window.location.host;
8
+ if (window.location.protocol !== "https:" || window.location.hostname !== `${clusterId}.renderer.freelens.app` || require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid !== clusterId) throw new Error("Resource requests require the active cluster frame");
9
+ const api = require_extensions.Renderer.K8sApi.KubeJsonApi.forCluster.call(void 0, clusterId);
10
+ return { get: async (path, query, headers) => {
11
+ if (signal?.aborted) throw new Error("Resource request canceled");
12
+ await electron.ipcRenderer.invoke(require_resource_search.RESOURCE_SEARCH_CHANNEL, {
13
+ operation: "permit",
14
+ clusterId,
15
+ contextId: searchContextId()
16
+ });
17
+ if (signal?.aborted) throw new Error("Resource request canceled");
18
+ const requestHeaders = {
19
+ ...Object.fromEntries(Object.entries(headers ?? {}).filter(([key]) => key.toLowerCase() !== "host")),
20
+ Host: frameHost
21
+ };
22
+ const controller = new AbortController();
23
+ let timedOut = false;
24
+ let responseBody;
25
+ const cancel = () => controller.abort();
26
+ signal?.addEventListener("abort", cancel, { once: true });
27
+ let rejectAbort;
28
+ const aborted = new Promise((_resolve, reject) => {
29
+ rejectAbort = reject;
30
+ });
31
+ const onAbort = () => {
32
+ responseBody?.destroy();
33
+ rejectAbort(timedOut ? Object.assign(/* @__PURE__ */ new Error("Resource request timed out"), { timedOut: true }) : /* @__PURE__ */ new Error("Resource request canceled"));
34
+ };
35
+ controller.signal.addEventListener("abort", onAbort, { once: true });
36
+ const timeout = setTimeout(() => {
37
+ timedOut = true;
38
+ controller.abort();
39
+ }, options.timeoutMs ?? 3e4);
40
+ const request = async () => {
41
+ const response = await api.getResponse(path, { query }, {
42
+ signal: controller.signal,
43
+ headers: requestHeaders,
44
+ size: 4 * 1024 * 1024
45
+ });
46
+ if (controller.signal.aborted) {
47
+ response.body?.destroy();
48
+ throw new Error("Resource request canceled");
49
+ }
50
+ responseBody = response.body;
51
+ if (!response.ok) {
52
+ const error = Object.assign(/* @__PURE__ */ new Error(`Resource API returned ${response.status}`), {
53
+ status: response.status,
54
+ retryAfter: response.headers.get("retry-after")
55
+ });
56
+ response.body?.destroy();
57
+ throw error;
58
+ }
59
+ try {
60
+ return JSON.parse(await response.text());
61
+ } catch (error) {
62
+ if (error.type === "max-size") throw Object.assign(/* @__PURE__ */ new Error("Resource page exceeded the response budget"), { oversized: true });
63
+ throw new Error("Resource response could not be parsed");
64
+ }
65
+ };
66
+ try {
67
+ return await Promise.race([request(), aborted]);
68
+ } finally {
69
+ clearTimeout(timeout);
70
+ signal?.removeEventListener("abort", cancel);
71
+ controller.signal.removeEventListener("abort", onAbort);
72
+ }
73
+ } };
74
+ }
75
+ function searchContextId() {
76
+ const cluster = require_extensions.Renderer.Catalog.activeCluster.get();
77
+ return (0, node_crypto.createHash)("sha256").update(JSON.stringify([cluster?.metadata.uid, cluster?.spec?.kubeconfigContext])).digest("hex");
78
+ }
79
+ function createResourceSearchClient(clusterId, contextId) {
80
+ const invoke = (operation, data = {}) => electron.ipcRenderer.invoke(require_resource_search.RESOURCE_SEARCH_CHANNEL, {
81
+ clusterId,
82
+ contextId,
83
+ operation,
84
+ ...data
85
+ });
86
+ return {
87
+ begin: (scope, scanId) => invoke("begin", {
88
+ scope,
89
+ scanId
90
+ }),
91
+ append: (scope, scanId, documents) => invoke("append", {
92
+ scope,
93
+ scanId,
94
+ documents
95
+ }),
96
+ complete: (scope, scanId) => invoke("complete", {
97
+ scope,
98
+ scanId
99
+ }),
100
+ invalidate: (scope) => invoke("invalidate", { scope }),
101
+ reconcile: (resources) => invoke("reconcile", { resources }),
102
+ query: (query, limit = 100, filters = {}) => invoke("query", {
103
+ query,
104
+ limit,
105
+ ...filters
106
+ }),
107
+ clear: () => invoke("clear"),
108
+ status: () => invoke("status")
109
+ };
110
+ }
111
+ //#endregion
112
+ exports.createResourceSearchClient = createResourceSearchClient;
113
+ exports.createResourceSearchTransport = createResourceSearchTransport;
114
+ exports.searchContextId = searchContextId;
115
+
116
+ //# sourceMappingURL=resource-search-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-search-client.js","names":[],"sources":["../../src/renderer/resource-search-client.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { Renderer } from \"@freelensapp/extensions\";\nimport { ipcRenderer } from \"electron\";\nimport {\n RESOURCE_SEARCH_CHANNEL,\n type SearchDocument,\n type SearchIndexStatus,\n type SearchQueryResult,\n} from \"../common/resource-search\";\n\nimport type { SearchTransport } from \"./resource-search-discovery\";\nimport type { SearchSink } from \"./resource-search-sync\";\n\nexport function createResourceSearchTransport(\n clusterId: string,\n signal?: AbortSignal,\n options: { timeoutMs?: number } = {},\n): SearchTransport {\n const frameHost = window.location.host;\n if (\n window.location.protocol !== \"https:\" ||\n window.location.hostname !== `${clusterId}.renderer.freelens.app` ||\n Renderer.Catalog.activeCluster.get()?.metadata.uid !== clusterId\n )\n throw new Error(\"Resource requests require the active cluster frame\");\n const api = Renderer.K8sApi.KubeJsonApi.forCluster.call(undefined, clusterId);\n return {\n get: async <T>(path: string, query?: Record<string, string | number>, headers?: Record<string, string>) => {\n if (signal?.aborted) throw new Error(\"Resource request canceled\");\n await ipcRenderer.invoke(RESOURCE_SEARCH_CHANNEL, {\n operation: \"permit\",\n clusterId,\n contextId: searchContextId(),\n });\n if (signal?.aborted) throw new Error(\"Resource request canceled\");\n const requestHeaders = {\n ...Object.fromEntries(Object.entries(headers ?? {}).filter(([key]) => key.toLowerCase() !== \"host\")),\n Host: frameHost,\n };\n const controller = new AbortController();\n let timedOut = false;\n let responseBody: { destroy(): void } | null | undefined;\n const cancel = () => controller.abort();\n signal?.addEventListener(\"abort\", cancel, { once: true });\n let rejectAbort!: (error: Error) => void;\n const aborted = new Promise<never>((_resolve, reject) => {\n rejectAbort = reject;\n });\n const onAbort = () => {\n responseBody?.destroy();\n rejectAbort(\n timedOut\n ? Object.assign(new Error(\"Resource request timed out\"), { timedOut: true })\n : new Error(\"Resource request canceled\"),\n );\n };\n controller.signal.addEventListener(\"abort\", onAbort, { once: true });\n const timeout = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, options.timeoutMs ?? 30000);\n const request = async (): Promise<T> => {\n const response = await api.getResponse(\n path,\n { query },\n { signal: controller.signal, headers: requestHeaders, ...{ size: 4 * 1024 * 1024 } },\n );\n if (controller.signal.aborted) {\n response.body?.destroy();\n throw new Error(\"Resource request canceled\");\n }\n responseBody = response.body;\n if (!response.ok) {\n const error = Object.assign(new Error(`Resource API returned ${response.status}`), {\n status: response.status,\n retryAfter: response.headers.get(\"retry-after\"),\n });\n response.body?.destroy();\n throw error;\n }\n try {\n return JSON.parse(await response.text()) as T;\n } catch (error) {\n if ((error as { type?: string }).type === \"max-size\")\n throw Object.assign(new Error(\"Resource page exceeded the response budget\"), { oversized: true });\n throw new Error(\"Resource response could not be parsed\");\n }\n };\n try {\n return await Promise.race([request(), aborted]);\n } finally {\n clearTimeout(timeout);\n signal?.removeEventListener(\"abort\", cancel);\n controller.signal.removeEventListener(\"abort\", onAbort);\n }\n },\n };\n}\nexport function searchContextId(): string {\n const cluster = Renderer.Catalog.activeCluster.get();\n return createHash(\"sha256\")\n .update(JSON.stringify([cluster?.metadata.uid, cluster?.spec?.kubeconfigContext]))\n .digest(\"hex\");\n}\nexport function createResourceSearchClient(\n clusterId: string,\n contextId: string,\n): SearchSink & {\n query(query: string, limit?: number, filters?: { kind?: string; namespace?: string }): Promise<SearchQueryResult>;\n clear(): Promise<unknown>;\n status(): Promise<SearchIndexStatus>;\n} {\n const invoke = (operation: string, data: object = {}) =>\n ipcRenderer.invoke(RESOURCE_SEARCH_CHANNEL, { clusterId, contextId, operation, ...data });\n return {\n begin: (scope, scanId) => invoke(\"begin\", { scope, scanId }),\n append: (scope, scanId, documents: SearchDocument[]) => invoke(\"append\", { scope, scanId, documents }),\n complete: (scope, scanId) => invoke(\"complete\", { scope, scanId }),\n invalidate: (scope) => invoke(\"invalidate\", { scope }),\n reconcile: (resources) => invoke(\"reconcile\", { resources }),\n query: (query, limit = 100, filters = {}) => invoke(\"query\", { query, limit, ...filters }),\n clear: () => invoke(\"clear\"),\n status: () => invoke(\"status\"),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ //#region src/renderer/resource-search-discovery.ts
2
+ var collectionPath = (type, namespace) => `${type.group ? `/apis/${encodeURIComponent(type.group)}/${encodeURIComponent(type.version)}` : `/api/${encodeURIComponent(type.version)}`}${namespace ? `/namespaces/${encodeURIComponent(namespace)}` : ""}/${encodeURIComponent(type.resource)}`;
3
+ function collection(resource, group, version) {
4
+ const name = resource.name ?? resource.resource;
5
+ const kind = resource.kind ?? resource.responseKind?.kind;
6
+ if (!name || !kind || name.includes("/") || !resource.verbs?.includes("list")) return [];
7
+ return [{
8
+ group,
9
+ version,
10
+ resource: name,
11
+ kind,
12
+ namespaced: resource.namespaced ?? resource.scope === "Namespaced",
13
+ aliases: [...resource.shortNames ?? [], ...resource.singularName ? [resource.singularName] : []]
14
+ }];
15
+ }
16
+ async function discoverSearchCollections(transport, unavailable = () => {}) {
17
+ const types = /* @__PURE__ */ new Map();
18
+ const add = (items) => {
19
+ for (const item of items) if (!types.has(`${item.group}/${item.resource}`)) types.set(`${item.group}/${item.resource}`, item);
20
+ };
21
+ for (const root of ["/api", "/apis"]) {
22
+ let response;
23
+ try {
24
+ response = await transport.get(root, void 0, { Accept: "application/json;g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList, application/json" });
25
+ } catch {
26
+ unavailable(root);
27
+ continue;
28
+ }
29
+ if (response.items) {
30
+ for (const group of response.items) for (const version of group.versions ?? []) {
31
+ if (version.freshness === "Stale") unavailable(`${group.metadata?.name ?? ""}/${version.version}`);
32
+ add((version.resources ?? []).flatMap((resource) => collection(resource, group.metadata?.name ?? "", version.version)));
33
+ }
34
+ continue;
35
+ }
36
+ const groups = root === "/api" ? [{
37
+ name: "",
38
+ versions: (response.versions ?? ["v1"]).map((version) => ({ version }))
39
+ }] : response.groups ?? [];
40
+ for (const group of groups) {
41
+ const versions = [..."preferredVersion" in group && group.preferredVersion ? [group.preferredVersion] : [], ...group.versions ?? []];
42
+ for (const { version } of versions) {
43
+ const path = group.name ? `/apis/${group.name}/${version}` : `/api/${version}`;
44
+ try {
45
+ add(((await transport.get(path)).resources ?? []).flatMap((resource) => collection(resource, group.name, version)));
46
+ } catch {
47
+ unavailable(path);
48
+ }
49
+ }
50
+ }
51
+ }
52
+ return [...types.values()];
53
+ }
54
+ function projectMetadataPage(type, page) {
55
+ return (page.items ?? []).flatMap(({ metadata }) => metadata?.name ? [{
56
+ aliases: type.aliases,
57
+ group: type.group,
58
+ version: type.version,
59
+ resource: type.resource,
60
+ kind: type.kind,
61
+ name: metadata.name,
62
+ namespace: metadata.namespace,
63
+ uid: metadata.uid,
64
+ labels: Object.fromEntries(Object.entries(metadata.labels ?? {}).filter((entry) => typeof entry[1] === "string")),
65
+ path: `${collectionPath(type, metadata.namespace)}/${encodeURIComponent(metadata.name)}`
66
+ }] : []);
67
+ }
68
+ var searchErrorStatus = (error) => {
69
+ if (!error || typeof error !== "object") return 0;
70
+ const value = error;
71
+ return value.status ?? value.response?.status ?? value.code ?? 0;
72
+ };
73
+ //#endregion
74
+ exports.collectionPath = collectionPath;
75
+ exports.discoverSearchCollections = discoverSearchCollections;
76
+ exports.projectMetadataPage = projectMetadataPage;
77
+ exports.searchErrorStatus = searchErrorStatus;
78
+
79
+ //# sourceMappingURL=resource-search-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-search-discovery.js","names":[],"sources":["../../src/renderer/resource-search-discovery.ts"],"sourcesContent":["import type { SearchDocument } from \"../common/resource-search\";\n\nexport interface SearchCollection {\n group: string;\n version: string;\n resource: string;\n kind: string;\n namespaced: boolean;\n aliases: string[];\n}\nexport interface SearchTransport {\n get<T>(path: string, query?: Record<string, string | number>, headers?: Record<string, string>): Promise<T>;\n}\ninterface DiscoveryResource {\n name?: string;\n resource?: string;\n kind?: string;\n responseKind?: { kind?: string };\n namespaced?: boolean;\n scope?: string;\n verbs?: string[];\n shortNames?: string[];\n singularName?: string;\n}\ninterface GroupDiscovery {\n metadata?: { name?: string };\n versions?: { version: string; freshness?: string; resources?: DiscoveryResource[] }[];\n}\nexport const collectionPath = (type: SearchCollection, namespace?: string) =>\n `${type.group ? `/apis/${encodeURIComponent(type.group)}/${encodeURIComponent(type.version)}` : `/api/${encodeURIComponent(type.version)}`}${namespace ? `/namespaces/${encodeURIComponent(namespace)}` : \"\"}/${encodeURIComponent(type.resource)}`;\n\nfunction collection(resource: DiscoveryResource, group: string, version: string): SearchCollection[] {\n const name = resource.name ?? resource.resource;\n const kind = resource.kind ?? resource.responseKind?.kind;\n if (!name || !kind || name.includes(\"/\") || !resource.verbs?.includes(\"list\")) return [];\n return [\n {\n group,\n version,\n resource: name,\n kind,\n namespaced: resource.namespaced ?? resource.scope === \"Namespaced\",\n aliases: [...(resource.shortNames ?? []), ...(resource.singularName ? [resource.singularName] : [])],\n },\n ];\n}\nexport async function discoverSearchCollections(\n transport: SearchTransport,\n unavailable: (scope: string) => void = () => {},\n): Promise<SearchCollection[]> {\n const types = new Map<string, SearchCollection>();\n const add = (items: SearchCollection[]) => {\n for (const item of items)\n if (!types.has(`${item.group}/${item.resource}`)) types.set(`${item.group}/${item.resource}`, item);\n };\n for (const root of [\"/api\", \"/apis\"]) {\n let response: {\n items?: GroupDiscovery[];\n versions?: string[];\n groups?: { name: string; preferredVersion?: { version: string }; versions?: { version: string }[] }[];\n };\n try {\n response = await transport.get(root, undefined, {\n Accept: \"application/json;g=apidiscovery.k8s.io;v=v2;as=APIGroupDiscoveryList, application/json\",\n });\n } catch {\n unavailable(root);\n continue;\n }\n if (response.items) {\n for (const group of response.items)\n for (const version of group.versions ?? []) {\n if (version.freshness === \"Stale\") unavailable(`${group.metadata?.name ?? \"\"}/${version.version}`);\n add(\n (version.resources ?? []).flatMap((resource) =>\n collection(resource, group.metadata?.name ?? \"\", version.version),\n ),\n );\n }\n continue;\n }\n const groups =\n root === \"/api\"\n ? [{ name: \"\", versions: (response.versions ?? [\"v1\"]).map((version) => ({ version })) }]\n : (response.groups ?? []);\n for (const group of groups) {\n const versions = [\n ...(\"preferredVersion\" in group && group.preferredVersion ? [group.preferredVersion] : []),\n ...(group.versions ?? []),\n ];\n for (const { version } of versions) {\n const path = group.name ? `/apis/${group.name}/${version}` : `/api/${version}`;\n try {\n const data = await transport.get<{ resources?: DiscoveryResource[] }>(path);\n add((data.resources ?? []).flatMap((resource) => collection(resource, group.name, version)));\n } catch {\n unavailable(path);\n }\n }\n }\n }\n return [...types.values()];\n}\nexport interface MetadataPage {\n metadata?: { continue?: string; resourceVersion?: string };\n items?: { metadata?: { name?: string; namespace?: string; uid?: string; labels?: Record<string, unknown> } }[];\n}\nexport function projectMetadataPage(type: SearchCollection, page: MetadataPage): SearchDocument[] {\n return (page.items ?? []).flatMap(({ metadata }) =>\n metadata?.name\n ? [\n {\n aliases: type.aliases,\n group: type.group,\n version: type.version,\n resource: type.resource,\n kind: type.kind,\n name: metadata.name,\n namespace: metadata.namespace,\n uid: metadata.uid,\n labels: Object.fromEntries(\n Object.entries(metadata.labels ?? {}).filter(\n (entry): entry is [string, string] => typeof entry[1] === \"string\",\n ),\n ),\n path: `${collectionPath(type, metadata.namespace)}/${encodeURIComponent(metadata.name)}`,\n },\n ]\n : [],\n );\n}\nexport const searchErrorStatus = (error: unknown): number => {\n if (!error || typeof error !== \"object\") return 0;\n const value = error as { status?: number; code?: number; response?: { status?: number } };\n return value.status ?? value.response?.status ?? value.code ?? 0;\n};\n"],"mappings":"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"}
@@ -4,6 +4,7 @@ var RESOURCE_SEARCH_MAX_DOCUMENT_CHARS = 4096;
4
4
  var queryTokens = (query) => query.slice(0, 300).toLocaleLowerCase().trim().split(/\s+/).filter(Boolean);
5
5
  var metadataText = (resource) => [
6
6
  resource.kind,
7
+ ...resource.aliases ?? [],
7
8
  resource.name,
8
9
  resource.namespace ?? "",
9
10
  ...Object.entries(resource.labels).map(([key, value]) => `${key}=${value}`)
@@ -18,7 +19,7 @@ function rankResources(resources, tokens) {
18
19
  return resources.sort((a, b) => score(a) - score(b) || a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name));
19
20
  }
20
21
  function createResourceSearchIndex(resources) {
21
- const documents = resources.filter((resource) => resource.kind !== "Secret" && resource.kind !== "ConfigMap");
22
+ const documents = resources;
22
23
  const vocabulary = /* @__PURE__ */ new Map();
23
24
  const names = /* @__PURE__ */ new Map();
24
25
  let lastQuery;