@64mb/freelens-power 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.
Files changed (58) hide show
  1. package/README.md +4 -4
  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 +10 -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/details/resource-health-details.js +8 -4
  17. package/out/renderer/details/resource-health-details.js.map +1 -1
  18. package/out/renderer/diagnostics-state.js +7 -5
  19. package/out/renderer/diagnostics-state.js.map +1 -1
  20. package/out/renderer/diagnostics.css.js +2 -2
  21. package/out/renderer/diagnostics.css.js.map +1 -1
  22. package/out/renderer/diagnostics.js +127 -51
  23. package/out/renderer/diagnostics.js.map +1 -1
  24. package/out/renderer/gateway-relationships-client.js +107 -0
  25. package/out/renderer/gateway-relationships-client.js.map +1 -0
  26. package/out/renderer/index.js +2 -2
  27. package/out/renderer/index.js.map +1 -1
  28. package/out/renderer/monitoring.js +15 -0
  29. package/out/renderer/monitoring.js.map +1 -1
  30. package/out/renderer/preferences/monitoring-preferences.js +11 -1
  31. package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
  32. package/out/renderer/preferences/preferences.js +16 -6
  33. package/out/renderer/preferences/preferences.js.map +1 -1
  34. package/out/renderer/resource-relationships.css.js +1 -1
  35. package/out/renderer/resource-relationships.css.js.map +1 -1
  36. package/out/renderer/resource-relationships.js +71 -18
  37. package/out/renderer/resource-relationships.js.map +1 -1
  38. package/out/renderer/resource-search-client.js +116 -0
  39. package/out/renderer/resource-search-client.js.map +1 -0
  40. package/out/renderer/resource-search-discovery.js +79 -0
  41. package/out/renderer/resource-search-discovery.js.map +1 -0
  42. package/out/renderer/resource-search-model.js +2 -1
  43. package/out/renderer/resource-search-model.js.map +1 -1
  44. package/out/renderer/resource-search-routing.js +78 -0
  45. package/out/renderer/resource-search-routing.js.map +1 -0
  46. package/out/renderer/resource-search-sync.js +199 -0
  47. package/out/renderer/resource-search-sync.js.map +1 -0
  48. package/out/renderer/resource-search.js +279 -54
  49. package/out/renderer/resource-search.js.map +1 -1
  50. package/out/renderer/src/common/gateway-relationships.js +122 -0
  51. package/out/renderer/src/common/gateway-relationships.js.map +1 -0
  52. package/out/renderer/src/common/resource-relationships.js +8 -0
  53. package/out/renderer/src/common/resource-relationships.js.map +1 -1
  54. package/out/renderer/src/common/resource-search.js +6 -0
  55. package/out/renderer/src/common/resource-search.js.map +1 -0
  56. package/out/renderer/src/common/store/monitoring-preferences-store.js +10 -0
  57. package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
  58. package/package.json +6 -2
@@ -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":";AA4BA,IAAa,kBAAkB,MAAwB,cACrD,GAAG,KAAK,QAAQ,SAAS,mBAAmB,KAAK,KAAK,EAAE,GAAG,mBAAmB,KAAK,OAAO,MAAM,QAAQ,mBAAmB,KAAK,OAAO,MAAM,YAAY,eAAe,mBAAmB,SAAS,MAAM,GAAG,GAAG,mBAAmB,KAAK,QAAQ;AAElP,SAAS,WAAW,UAA6B,OAAe,SAAqC;CACnG,MAAM,OAAO,SAAS,QAAQ,SAAS;CACvC,MAAM,OAAO,SAAS,QAAQ,SAAS,cAAc;CACrD,IAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK,SAAS,GAAG,KAAK,CAAC,SAAS,OAAO,SAAS,MAAM,GAAG,OAAO,CAAC;CACvF,OAAO,CACL;EACE;EACA;EACA,UAAU;EACV;EACA,YAAY,SAAS,cAAc,SAAS,UAAU;EACtD,SAAS,CAAC,GAAI,SAAS,cAAc,CAAC,GAAI,GAAI,SAAS,eAAe,CAAC,SAAS,YAAY,IAAI,CAAC,CAAE;CACrG,CACF;AACF;AACA,eAAsB,0BACpB,WACA,oBAA6C,CAAC,GACjB;CAC7B,MAAM,wBAAQ,IAAI,IAA8B;CAChD,MAAM,OAAO,UAA8B;EACzC,KAAK,MAAM,QAAQ,OACjB,IAAI,CAAC,MAAM,IAAI,GAAG,KAAK,MAAM,GAAG,KAAK,UAAU,GAAG,MAAM,IAAI,GAAG,KAAK,MAAM,GAAG,KAAK,YAAY,IAAI;CACtG;CACA,KAAK,MAAM,QAAQ,CAAC,QAAQ,OAAO,GAAG;EACpC,IAAI;EAKJ,IAAI;GACF,WAAW,MAAM,UAAU,IAAI,MAAM,KAAA,GAAW,EAC9C,QAAQ,yFACV,CAAC;EACH,QAAQ;GACN,YAAY,IAAI;GAChB;EACF;EACA,IAAI,SAAS,OAAO;GAClB,KAAK,MAAM,SAAS,SAAS,OAC3B,KAAK,MAAM,WAAW,MAAM,YAAY,CAAC,GAAG;IAC1C,IAAI,QAAQ,cAAc,SAAS,YAAY,GAAG,MAAM,UAAU,QAAQ,GAAG,GAAG,QAAQ,SAAS;IACjG,KACG,QAAQ,aAAa,CAAC,EAAA,CAAG,SAAS,aACjC,WAAW,UAAU,MAAM,UAAU,QAAQ,IAAI,QAAQ,OAAO,CAClE,CACF;GACF;GACF;EACF;EACA,MAAM,SACJ,SAAS,SACL,CAAC;GAAE,MAAM;GAAI,WAAW,SAAS,YAAY,CAAC,IAAI,EAAA,CAAG,KAAK,aAAa,EAAE,QAAQ,EAAE;EAAE,CAAC,IACrF,SAAS,UAAU,CAAC;EAC3B,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,WAAW,CACf,GAAI,sBAAsB,SAAS,MAAM,mBAAmB,CAAC,MAAM,gBAAgB,IAAI,CAAC,GACxF,GAAI,MAAM,YAAY,CAAC,CACzB;GACA,KAAK,MAAM,EAAE,aAAa,UAAU;IAClC,MAAM,OAAO,MAAM,OAAO,SAAS,MAAM,KAAK,GAAG,YAAY,QAAQ;IACrE,IAAI;KAEF,MAAK,MADc,UAAU,IAAyC,IAAI,EAAA,CAChE,aAAa,CAAC,EAAA,CAAG,SAAS,aAAa,WAAW,UAAU,MAAM,MAAM,OAAO,CAAC,CAAC;IAC7F,QAAQ;KACN,YAAY,IAAI;IAClB;GACF;EACF;CACF;CACA,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC;AAC3B;AAKA,SAAgB,oBAAoB,MAAwB,MAAsC;CAChG,QAAQ,KAAK,SAAS,CAAC,EAAA,CAAG,SAAS,EAAE,eACnC,UAAU,OACN,CACE;EACE,SAAS,KAAK;EACd,OAAO,KAAK;EACZ,SAAS,KAAK;EACd,UAAU,KAAK;EACf,MAAM,KAAK;EACX,MAAM,SAAS;EACf,WAAW,SAAS;EACpB,KAAK,SAAS;EACd,QAAQ,OAAO,YACb,OAAO,QAAQ,SAAS,UAAU,CAAC,CAAC,CAAC,CAAC,QACnC,UAAqC,OAAO,MAAM,OAAO,QAC5D,CACF;EACA,MAAM,GAAG,eAAe,MAAM,SAAS,SAAS,EAAE,GAAG,mBAAmB,SAAS,IAAI;CACvF,CACF,IACA,CAAC,CACP;AACF;AACA,IAAa,qBAAqB,UAA2B;CAC3D,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;CAChD,MAAM,QAAQ;CACd,OAAO,MAAM,UAAU,MAAM,UAAU,UAAU,MAAM,QAAQ;AACjE"}
@@ -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;
@@ -1 +1 @@
1
- {"version":3,"file":"resource-search-model.js","names":[],"sources":["../../src/renderer/resource-search-model.ts"],"sourcesContent":["import { boundedNameDistance } from \"./resource-search-distance\";\n\nimport type { SearchField } from \"./resource-search-fields\";\nexport interface SearchResource {\n kind: string;\n name: string;\n namespace?: string;\n labels: Record<string, string>;\n path: string;\n fields?: SearchField[];\n}\n\nexport const RESOURCE_SEARCH_MAX_DOCUMENT_CHARS = 4096;\nexport const queryTokens = (query: string) =>\n query.slice(0, 300).toLocaleLowerCase().trim().split(/\\s+/).filter(Boolean);\nconst metadataText = (resource: SearchResource) =>\n [\n resource.kind,\n resource.name,\n resource.namespace ?? \"\",\n ...Object.entries(resource.labels).map(([key, value]) => `${key}=${value}`),\n ].join(\" \");\nfunction rankResources(resources: SearchResource[], tokens: string[]) {\n const score = (resource: SearchResource) =>\n tokens.reduce((total, token) => {\n const name = resource.name.toLocaleLowerCase();\n const kind = resource.kind.toLocaleLowerCase();\n const namespace = (resource.namespace ?? \"\").toLocaleLowerCase();\n return (\n total +\n (name === token\n ? 0\n : name.startsWith(token)\n ? 1\n : kind === token\n ? 2\n : name.includes(token)\n ? 3\n : kind.includes(token)\n ? 4\n : namespace.includes(token)\n ? 5\n : 6)\n );\n }, 0);\n\n return resources.sort((a, b) => score(a) - score(b) || a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name));\n}\n\nexport function createResourceSearchIndex(resources: SearchResource[]) {\n const documents = resources.filter((resource) => resource.kind !== \"Secret\" && resource.kind !== \"ConfigMap\");\n const vocabulary = new Map<string, Set<number>>();\n const names = new Map<number, Map<string, Set<number>>>();\n let lastQuery: string | undefined;\n let lastResults: SearchResource[] = [];\n let similarPaths = new Set<string>();\n let partsPaths = new Set<string>();\n const nameParts = new Map<number, string[]>();\n const partVocabulary = new Map<string, Set<number>>();\n const snippets = new Map<string, SearchField[]>();\n const metadata = new Map<string, string>();\n documents.forEach((resource, id) => {\n const parts = resource.name.toLocaleLowerCase().split(/[-._]/).filter(Boolean).slice(0, 16);\n nameParts.set(id, parts);\n for (const part of parts) {\n let ids = partVocabulary.get(part);\n if (!ids) {\n ids = new Set();\n partVocabulary.set(part, ids);\n }\n ids.add(id);\n }\n for (const name of new Set(\n [resource.name, ...resource.name.split(/[-.]/)].map((name) => name.toLocaleLowerCase()),\n )) {\n if (name.length < 3 || name.length > 64) continue;\n let bucket = names.get(name.length);\n if (!bucket) {\n bucket = new Map();\n names.set(name.length, bucket);\n }\n let ids = bucket.get(name);\n if (!ids) {\n ids = new Set();\n bucket.set(name, ids);\n }\n ids.add(id);\n }\n const identity = metadataText(resource).slice(0, RESOURCE_SEARCH_MAX_DOCUMENT_CHARS);\n metadata.set(resource.path, identity.toLocaleLowerCase());\n let remaining = RESOURCE_SEARCH_MAX_DOCUMENT_CHARS - identity.length;\n const fields: SearchField[] = [];\n for (const field of resource.fields ?? []) {\n if (remaining <= 1) break;\n const text = field.text.slice(0, remaining - 1);\n fields.push({ label: field.label, text });\n remaining -= text.length + 1;\n }\n snippets.set(resource.path, fields);\n for (const word of new Set(\n [identity, ...fields.map((field) => field.text)].join(\" \").toLocaleLowerCase().split(/\\s+/),\n )) {\n if (!word) continue;\n let ids = vocabulary.get(word);\n if (!ids) {\n ids = new Set();\n vocabulary.set(word, ids);\n }\n ids.add(id);\n }\n });\n return {\n search(query: string): SearchResource[] {\n query = query.slice(0, 300);\n if (lastQuery === query) return lastResults;\n lastQuery = query;\n similarPaths = new Set();\n partsPaths = new Set();\n lastResults = [];\n const tokens = queryTokens(query);\n const penalties = new Map<number, number>();\n let candidates: Set<number> | undefined;\n for (const token of tokens) {\n const found = new Set<number>();\n for (const [word, ids] of vocabulary) if (word.includes(token)) for (const id of ids) found.add(id);\n if (/^[a-z0-9]+(?:[-._][a-z0-9]+)+$/.test(token)) {\n const parts = token.split(/[-._]/);\n if (parts.length <= 16) {\n let possible: Set<number> | undefined;\n for (const part of parts) {\n const ids = partVocabulary.get(part) ?? new Set<number>();\n possible = possible === undefined ? ids : new Set([...possible].filter((id) => ids.has(id)));\n }\n for (const id of possible ?? []) {\n if (found.has(id)) continue;\n let offset = 0;\n for (const part of nameParts.get(id) ?? []) if (part === parts[offset]) offset++;\n if (offset === parts.length) {\n found.add(id);\n penalties.set(id, (penalties.get(id) ?? 0) + 1);\n partsPaths.add(documents[id].path);\n }\n }\n }\n }\n const exact = new Set(found);\n const limit = token.length >= 7 && token.length <= 64 ? 2 : token.length >= 4 && token.length <= 6 ? 1 : 0;\n if (limit) {\n const distances = new Map<number, number>();\n for (let length = token.length - limit; length <= token.length + limit; length++) {\n for (const [name, ids] of names.get(length) ?? []) {\n const distance = boundedNameDistance(token, name, limit);\n if (distance > limit) continue;\n for (const id of ids)\n if (!exact.has(id)) distances.set(id, Math.min(distances.get(id) ?? Infinity, distance));\n }\n }\n for (const [id, distance] of distances) {\n found.add(id);\n penalties.set(id, (penalties.get(id) ?? 0) + 10 * distance);\n similarPaths.add(documents[id].path);\n }\n }\n candidates = candidates === undefined ? found : new Set([...candidates].filter((id) => found.has(id)));\n if (!candidates.size) return [];\n }\n lastResults = rankResources(\n candidates === undefined ? [...documents] : [...candidates].map((id) => documents[id]),\n tokens,\n );\n const penaltyByPath = new Map([...penalties].map(([id, penalty]) => [documents[id].path, penalty]));\n lastResults.sort((left, right) => (penaltyByPath.get(left.path) ?? 0) - (penaltyByPath.get(right.path) ?? 0));\n return lastResults;\n },\n nameParts(resource: SearchResource): boolean {\n return partsPaths.has(resource.path);\n },\n similarName(resource: SearchResource): boolean {\n return similarPaths.has(resource.path);\n },\n snippet(resource: SearchResource, query: string): SearchField | undefined {\n const tokens = queryTokens(query);\n const identity = metadata.get(resource.path) ?? \"\";\n const bodyTokens = tokens.filter((token) => !identity.includes(token));\n if (!bodyTokens.length) return;\n const field = snippets\n .get(resource.path)\n ?.find((item) => bodyTokens.some((token) => item.text.toLocaleLowerCase().includes(token)));\n if (!field) return;\n const offset = Math.max(\n 0,\n Math.min(\n ...bodyTokens.map((token) => field.text.toLocaleLowerCase().indexOf(token)).filter((index) => index >= 0),\n ) - 35,\n );\n return {\n label: field.label,\n text:\n (offset ? \"…\" : \"\") + field.text.slice(offset, offset + 150) + (field.text.length > offset + 150 ? \"…\" : \"\"),\n };\n },\n };\n}\n\nexport function matchResources(resources: SearchResource[], query: string): SearchResource[] {\n return createResourceSearchIndex(resources).search(query);\n}\n\nexport function createDoubleShift() {\n let last = 0;\n let held = false;\n const otherKeys = new Set<string>();\n return (\n event: Pick<KeyboardEvent, \"key\" | \"type\" | \"repeat\" | \"altKey\" | \"ctrlKey\" | \"metaKey\"> &\n Partial<Pick<KeyboardEvent, \"code\">>,\n now: number,\n ) => {\n const physicalKey = event.code || event.key;\n if (event.type === \"keyup\") {\n if (event.key === \"Shift\") held = false;\n else otherKeys.delete(physicalKey);\n if (event.key === \"Meta\") {\n otherKeys.clear();\n held = false;\n last = 0;\n }\n return false;\n }\n if (event.key !== \"Shift\") otherKeys.add(physicalKey);\n if (event.key !== \"Shift\" || otherKeys.size > 0 || event.altKey || event.ctrlKey || event.metaKey) {\n last = 0;\n return false;\n }\n if (event.repeat || held) return false;\n held = true;\n const hit = last > 0 && now - last <= 400;\n last = hit ? 0 : now;\n return hit;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"resource-search-model.js","names":[],"sources":["../../src/renderer/resource-search-model.ts"],"sourcesContent":["import { boundedNameDistance } from \"./resource-search-distance\";\n\nimport type { SearchField } from \"./resource-search-fields\";\nexport interface SearchResource {\n kind: string;\n aliases?: string[];\n name: string;\n namespace?: string;\n labels: Record<string, string>;\n path: string;\n fields?: SearchField[];\n}\n\nexport const RESOURCE_SEARCH_MAX_DOCUMENT_CHARS = 4096;\nexport const queryTokens = (query: string) =>\n query.slice(0, 300).toLocaleLowerCase().trim().split(/\\s+/).filter(Boolean);\nconst metadataText = (resource: SearchResource) =>\n [\n resource.kind,\n ...(resource.aliases ?? []),\n resource.name,\n resource.namespace ?? \"\",\n ...Object.entries(resource.labels).map(([key, value]) => `${key}=${value}`),\n ].join(\" \");\nfunction rankResources(resources: SearchResource[], tokens: string[]) {\n const score = (resource: SearchResource) =>\n tokens.reduce((total, token) => {\n const name = resource.name.toLocaleLowerCase();\n const kind = resource.kind.toLocaleLowerCase();\n const namespace = (resource.namespace ?? \"\").toLocaleLowerCase();\n return (\n total +\n (name === token\n ? 0\n : name.startsWith(token)\n ? 1\n : kind === token\n ? 2\n : name.includes(token)\n ? 3\n : kind.includes(token)\n ? 4\n : namespace.includes(token)\n ? 5\n : 6)\n );\n }, 0);\n\n return resources.sort((a, b) => score(a) - score(b) || a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name));\n}\n\nexport function createResourceSearchIndex(resources: SearchResource[]) {\n const documents = resources;\n const vocabulary = new Map<string, Set<number>>();\n const names = new Map<number, Map<string, Set<number>>>();\n let lastQuery: string | undefined;\n let lastResults: SearchResource[] = [];\n let similarPaths = new Set<string>();\n let partsPaths = new Set<string>();\n const nameParts = new Map<number, string[]>();\n const partVocabulary = new Map<string, Set<number>>();\n const snippets = new Map<string, SearchField[]>();\n const metadata = new Map<string, string>();\n documents.forEach((resource, id) => {\n const parts = resource.name.toLocaleLowerCase().split(/[-._]/).filter(Boolean).slice(0, 16);\n nameParts.set(id, parts);\n for (const part of parts) {\n let ids = partVocabulary.get(part);\n if (!ids) {\n ids = new Set();\n partVocabulary.set(part, ids);\n }\n ids.add(id);\n }\n for (const name of new Set(\n [resource.name, ...resource.name.split(/[-.]/)].map((name) => name.toLocaleLowerCase()),\n )) {\n if (name.length < 3 || name.length > 64) continue;\n let bucket = names.get(name.length);\n if (!bucket) {\n bucket = new Map();\n names.set(name.length, bucket);\n }\n let ids = bucket.get(name);\n if (!ids) {\n ids = new Set();\n bucket.set(name, ids);\n }\n ids.add(id);\n }\n const identity = metadataText(resource).slice(0, RESOURCE_SEARCH_MAX_DOCUMENT_CHARS);\n metadata.set(resource.path, identity.toLocaleLowerCase());\n let remaining = RESOURCE_SEARCH_MAX_DOCUMENT_CHARS - identity.length;\n const fields: SearchField[] = [];\n for (const field of resource.fields ?? []) {\n if (remaining <= 1) break;\n const text = field.text.slice(0, remaining - 1);\n fields.push({ label: field.label, text });\n remaining -= text.length + 1;\n }\n snippets.set(resource.path, fields);\n for (const word of new Set(\n [identity, ...fields.map((field) => field.text)].join(\" \").toLocaleLowerCase().split(/\\s+/),\n )) {\n if (!word) continue;\n let ids = vocabulary.get(word);\n if (!ids) {\n ids = new Set();\n vocabulary.set(word, ids);\n }\n ids.add(id);\n }\n });\n return {\n search(query: string): SearchResource[] {\n query = query.slice(0, 300);\n if (lastQuery === query) return lastResults;\n lastQuery = query;\n similarPaths = new Set();\n partsPaths = new Set();\n lastResults = [];\n const tokens = queryTokens(query);\n const penalties = new Map<number, number>();\n let candidates: Set<number> | undefined;\n for (const token of tokens) {\n const found = new Set<number>();\n for (const [word, ids] of vocabulary) if (word.includes(token)) for (const id of ids) found.add(id);\n if (/^[a-z0-9]+(?:[-._][a-z0-9]+)+$/.test(token)) {\n const parts = token.split(/[-._]/);\n if (parts.length <= 16) {\n let possible: Set<number> | undefined;\n for (const part of parts) {\n const ids = partVocabulary.get(part) ?? new Set<number>();\n possible = possible === undefined ? ids : new Set([...possible].filter((id) => ids.has(id)));\n }\n for (const id of possible ?? []) {\n if (found.has(id)) continue;\n let offset = 0;\n for (const part of nameParts.get(id) ?? []) if (part === parts[offset]) offset++;\n if (offset === parts.length) {\n found.add(id);\n penalties.set(id, (penalties.get(id) ?? 0) + 1);\n partsPaths.add(documents[id].path);\n }\n }\n }\n }\n const exact = new Set(found);\n const limit = token.length >= 7 && token.length <= 64 ? 2 : token.length >= 4 && token.length <= 6 ? 1 : 0;\n if (limit) {\n const distances = new Map<number, number>();\n for (let length = token.length - limit; length <= token.length + limit; length++) {\n for (const [name, ids] of names.get(length) ?? []) {\n const distance = boundedNameDistance(token, name, limit);\n if (distance > limit) continue;\n for (const id of ids)\n if (!exact.has(id)) distances.set(id, Math.min(distances.get(id) ?? Infinity, distance));\n }\n }\n for (const [id, distance] of distances) {\n found.add(id);\n penalties.set(id, (penalties.get(id) ?? 0) + 10 * distance);\n similarPaths.add(documents[id].path);\n }\n }\n candidates = candidates === undefined ? found : new Set([...candidates].filter((id) => found.has(id)));\n if (!candidates.size) return [];\n }\n lastResults = rankResources(\n candidates === undefined ? [...documents] : [...candidates].map((id) => documents[id]),\n tokens,\n );\n const penaltyByPath = new Map([...penalties].map(([id, penalty]) => [documents[id].path, penalty]));\n lastResults.sort((left, right) => (penaltyByPath.get(left.path) ?? 0) - (penaltyByPath.get(right.path) ?? 0));\n return lastResults;\n },\n nameParts(resource: SearchResource): boolean {\n return partsPaths.has(resource.path);\n },\n similarName(resource: SearchResource): boolean {\n return similarPaths.has(resource.path);\n },\n snippet(resource: SearchResource, query: string): SearchField | undefined {\n const tokens = queryTokens(query);\n const identity = metadata.get(resource.path) ?? \"\";\n const bodyTokens = tokens.filter((token) => !identity.includes(token));\n if (!bodyTokens.length) return;\n const field = snippets\n .get(resource.path)\n ?.find((item) => bodyTokens.some((token) => item.text.toLocaleLowerCase().includes(token)));\n if (!field) return;\n const offset = Math.max(\n 0,\n Math.min(\n ...bodyTokens.map((token) => field.text.toLocaleLowerCase().indexOf(token)).filter((index) => index >= 0),\n ) - 35,\n );\n return {\n label: field.label,\n text:\n (offset ? \"…\" : \"\") + field.text.slice(offset, offset + 150) + (field.text.length > offset + 150 ? \"…\" : \"\"),\n };\n },\n };\n}\n\nexport function matchResources(resources: SearchResource[], query: string): SearchResource[] {\n return createResourceSearchIndex(resources).search(query);\n}\n\nexport function createDoubleShift() {\n let last = 0;\n let held = false;\n const otherKeys = new Set<string>();\n return (\n event: Pick<KeyboardEvent, \"key\" | \"type\" | \"repeat\" | \"altKey\" | \"ctrlKey\" | \"metaKey\"> &\n Partial<Pick<KeyboardEvent, \"code\">>,\n now: number,\n ) => {\n const physicalKey = event.code || event.key;\n if (event.type === \"keyup\") {\n if (event.key === \"Shift\") held = false;\n else otherKeys.delete(physicalKey);\n if (event.key === \"Meta\") {\n otherKeys.clear();\n held = false;\n last = 0;\n }\n return false;\n }\n if (event.key !== \"Shift\") otherKeys.add(physicalKey);\n if (event.key !== \"Shift\" || otherKeys.size > 0 || event.altKey || event.ctrlKey || event.metaKey) {\n last = 0;\n return false;\n }\n if (event.repeat || held) return false;\n held = true;\n const hit = last > 0 && now - last <= 400;\n last = hit ? 0 : now;\n return hit;\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
2
+ const require_resource_search_client = require("./resource-search-client.js");
3
+ const require_resource_search_discovery = require("./resource-search-discovery.js");
4
+ //#region src/renderer/resource-search-routing.ts
5
+ var fallback;
6
+ var openingEpoch = 0;
7
+ function closeSearchResourceFallback() {
8
+ openingEpoch++;
9
+ fallback?.remove();
10
+ fallback = void 0;
11
+ }
12
+ async function openSearchResource(resource) {
13
+ closeSearchResourceFallback();
14
+ const epoch = openingEpoch;
15
+ const contextId = require_resource_search_client.searchContextId();
16
+ const clusterId = require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid;
17
+ if (!clusterId) throw new Error("No active cluster");
18
+ const valid = () => epoch === openingEpoch && require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid === clusterId && require_resource_search_client.searchContextId() === contextId;
19
+ await new Promise((resolve) => window.setTimeout(resolve, 0));
20
+ if (!valid()) return;
21
+ const selfLink = decodeURIComponent(resource.path);
22
+ const nativePath = new URL(selfLink, window.location.origin);
23
+ const api = nativePath.pathname === selfLink && !nativePath.search && !nativePath.hash ? require_extensions.Renderer.K8sApi.apiManager.getApi(selfLink) : void 0;
24
+ if (api && require_extensions.Renderer.K8sApi.apiManager.getStore(api)) {
25
+ await require_extensions.Renderer.Navigation.showDetails(selfLink);
26
+ return;
27
+ }
28
+ const transport = require_resource_search_client.createResourceSearchTransport(clusterId);
29
+ let metadata;
30
+ try {
31
+ metadata = await transport.get(resource.path, void 0, { Accept: "application/json;as=PartialObjectMetadata;g=meta.k8s.io;v=v1" });
32
+ } catch (error) {
33
+ if (!valid()) return;
34
+ if (require_resource_search_discovery.searchErrorStatus(error) !== 406 || !resource.group) throw error;
35
+ metadata = await transport.get(resource.path, void 0, { Accept: "application/json" });
36
+ }
37
+ if (!valid()) return;
38
+ const safe = require_resource_search_discovery.projectMetadataPage({
39
+ ...resource,
40
+ namespaced: !!resource.namespace,
41
+ aliases: []
42
+ }, { items: [metadata] })[0];
43
+ if (!safe) throw new Error("Resource metadata unavailable");
44
+ closeSearchResourceFallback();
45
+ const dialog = document.createElement("dialog");
46
+ fallback = dialog;
47
+ dialog.setAttribute("aria-label", `${safe.kind} ${safe.name}`);
48
+ dialog.style.cssText = "max-width:640px;width:80%;padding:24px;background:var(--mainBackground,#222);color:var(--textColorPrimary,#eee);border:1px solid var(--borderColor,#555);border-radius:8px";
49
+ const title = document.createElement("h2");
50
+ title.textContent = `${safe.kind}: ${safe.name}`;
51
+ const description = document.createElement("p");
52
+ description.textContent = "Resource metadata. A native detail view is unavailable for this API.";
53
+ const fields = document.createElement("pre");
54
+ fields.style.cssText = "white-space:pre-wrap;overflow-wrap:anywhere";
55
+ fields.textContent = JSON.stringify({
56
+ apiVersion: [safe.group, safe.version].filter(Boolean).join("/"),
57
+ kind: safe.kind,
58
+ metadata: {
59
+ name: safe.name,
60
+ namespace: safe.namespace,
61
+ uid: safe.uid,
62
+ labels: safe.labels
63
+ }
64
+ }, null, 2);
65
+ const close = document.createElement("button");
66
+ close.type = "button";
67
+ close.textContent = "Close";
68
+ close.onclick = closeSearchResourceFallback;
69
+ dialog.append(title, description, fields, close);
70
+ dialog.addEventListener("close", closeSearchResourceFallback, { once: true });
71
+ document.body.append(dialog);
72
+ dialog.showModal();
73
+ }
74
+ //#endregion
75
+ exports.closeSearchResourceFallback = closeSearchResourceFallback;
76
+ exports.openSearchResource = openSearchResource;
77
+
78
+ //# sourceMappingURL=resource-search-routing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-search-routing.js","names":[],"sources":["../../src/renderer/resource-search-routing.ts"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { createResourceSearchTransport, searchContextId } from \"./resource-search-client\";\nimport { projectMetadataPage, searchErrorStatus } from \"./resource-search-discovery\";\n\nimport type { SearchDocument } from \"../common/resource-search\";\n\nlet fallback: HTMLDialogElement | undefined;\nlet openingEpoch = 0;\nexport function closeSearchResourceFallback() {\n openingEpoch++;\n fallback?.remove();\n fallback = undefined;\n}\nexport async function openSearchResource(resource: SearchDocument): Promise<void> {\n closeSearchResourceFallback();\n const epoch = openingEpoch;\n const contextId = searchContextId();\n const clusterId = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n if (!clusterId) throw new Error(\"No active cluster\");\n const valid = () =>\n epoch === openingEpoch &&\n Renderer.Catalog.activeCluster.get()?.metadata.uid === clusterId &&\n searchContextId() === contextId;\n await new Promise<void>((resolve) => window.setTimeout(resolve, 0));\n if (!valid()) return;\n // native stores parse raw self-links without decoding names, while HTTP requests use the encoded path\n const selfLink = decodeURIComponent(resource.path);\n const nativePath = new URL(selfLink, window.location.origin);\n const nativeCompatible = nativePath.pathname === selfLink && !nativePath.search && !nativePath.hash;\n const api = nativeCompatible ? Renderer.K8sApi.apiManager.getApi(selfLink) : undefined;\n if (api && Renderer.K8sApi.apiManager.getStore(api)) {\n await Renderer.Navigation.showDetails(selfLink);\n return;\n }\n const transport = createResourceSearchTransport(clusterId);\n let metadata: { metadata?: { name?: string; namespace?: string; uid?: string; labels?: Record<string, unknown> } };\n try {\n metadata = await transport.get(resource.path, undefined, {\n Accept: \"application/json;as=PartialObjectMetadata;g=meta.k8s.io;v=v1\",\n });\n } catch (error) {\n if (!valid()) return;\n if (searchErrorStatus(error) !== 406 || !resource.group) throw error;\n metadata = await transport.get(resource.path, undefined, { Accept: \"application/json\" });\n }\n if (!valid()) return;\n const safe = projectMetadataPage(\n { ...resource, namespaced: !!resource.namespace, aliases: [] },\n { items: [metadata] },\n )[0];\n if (!safe) throw new Error(\"Resource metadata unavailable\");\n closeSearchResourceFallback();\n const dialog = document.createElement(\"dialog\");\n fallback = dialog;\n dialog.setAttribute(\"aria-label\", `${safe.kind} ${safe.name}`);\n dialog.style.cssText =\n \"max-width:640px;width:80%;padding:24px;background:var(--mainBackground,#222);color:var(--textColorPrimary,#eee);border:1px solid var(--borderColor,#555);border-radius:8px\";\n const title = document.createElement(\"h2\");\n title.textContent = `${safe.kind}: ${safe.name}`;\n const description = document.createElement(\"p\");\n description.textContent = \"Resource metadata. A native detail view is unavailable for this API.\";\n const fields = document.createElement(\"pre\");\n fields.style.cssText = \"white-space:pre-wrap;overflow-wrap:anywhere\";\n fields.textContent = JSON.stringify(\n {\n apiVersion: [safe.group, safe.version].filter(Boolean).join(\"/\"),\n kind: safe.kind,\n metadata: { name: safe.name, namespace: safe.namespace, uid: safe.uid, labels: safe.labels },\n },\n null,\n 2,\n );\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.textContent = \"Close\";\n close.onclick = closeSearchResourceFallback;\n dialog.append(title, description, fields, close);\n dialog.addEventListener(\"close\", closeSearchResourceFallback, { once: true });\n document.body.append(dialog);\n dialog.showModal();\n}\n"],"mappings":";;;;AAMA,IAAI;AACJ,IAAI,eAAe;AACnB,SAAgB,8BAA8B;CAC5C;CACA,UAAU,OAAO;CACjB,WAAW,KAAA;AACb;AACA,eAAsB,mBAAmB,UAAyC;CAChF,4BAA4B;CAC5B,MAAM,QAAQ;CACd,MAAM,YAAY,+BAAA,gBAAgB;CAClC,MAAM,YAAY,mBAAA,SAAS,QAAQ,cAAc,IAAI,CAAC,EAAE,SAAS;CACjE,IAAI,CAAC,WAAW,MAAM,IAAI,MAAM,mBAAmB;CACnD,MAAM,cACJ,UAAU,gBACV,mBAAA,SAAS,QAAQ,cAAc,IAAI,CAAC,EAAE,SAAS,QAAQ,aACvD,+BAAA,gBAAgB,MAAM;CACxB,MAAM,IAAI,SAAe,YAAY,OAAO,WAAW,SAAS,CAAC,CAAC;CAClE,IAAI,CAAC,MAAM,GAAG;CAEd,MAAM,WAAW,mBAAmB,SAAS,IAAI;CACjD,MAAM,aAAa,IAAI,IAAI,UAAU,OAAO,SAAS,MAAM;CAE3D,MAAM,MADmB,WAAW,aAAa,YAAY,CAAC,WAAW,UAAU,CAAC,WAAW,OAChE,mBAAA,SAAS,OAAO,WAAW,OAAO,QAAQ,IAAI,KAAA;CAC7E,IAAI,OAAO,mBAAA,SAAS,OAAO,WAAW,SAAS,GAAG,GAAG;EACnD,MAAM,mBAAA,SAAS,WAAW,YAAY,QAAQ;EAC9C;CACF;CACA,MAAM,YAAY,+BAAA,8BAA8B,SAAS;CACzD,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,UAAU,IAAI,SAAS,MAAM,KAAA,GAAW,EACvD,QAAQ,+DACV,CAAC;CACH,SAAS,OAAO;EACd,IAAI,CAAC,MAAM,GAAG;EACd,IAAI,kCAAA,kBAAkB,KAAK,MAAM,OAAO,CAAC,SAAS,OAAO,MAAM;EAC/D,WAAW,MAAM,UAAU,IAAI,SAAS,MAAM,KAAA,GAAW,EAAE,QAAQ,mBAAmB,CAAC;CACzF;CACA,IAAI,CAAC,MAAM,GAAG;CACd,MAAM,OAAO,kCAAA,oBACX;EAAE,GAAG;EAAU,YAAY,CAAC,CAAC,SAAS;EAAW,SAAS,CAAC;CAAE,GAC7D,EAAE,OAAO,CAAC,QAAQ,EAAE,CACtB,CAAC,CAAC;CACF,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,+BAA+B;CAC1D,4BAA4B;CAC5B,MAAM,SAAS,SAAS,cAAc,QAAQ;CAC9C,WAAW;CACX,OAAO,aAAa,cAAc,GAAG,KAAK,KAAK,GAAG,KAAK,MAAM;CAC7D,OAAO,MAAM,UACX;CACF,MAAM,QAAQ,SAAS,cAAc,IAAI;CACzC,MAAM,cAAc,GAAG,KAAK,KAAK,IAAI,KAAK;CAC1C,MAAM,cAAc,SAAS,cAAc,GAAG;CAC9C,YAAY,cAAc;CAC1B,MAAM,SAAS,SAAS,cAAc,KAAK;CAC3C,OAAO,MAAM,UAAU;CACvB,OAAO,cAAc,KAAK,UACxB;EACE,YAAY,CAAC,KAAK,OAAO,KAAK,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;EAC/D,MAAM,KAAK;EACX,UAAU;GAAE,MAAM,KAAK;GAAM,WAAW,KAAK;GAAW,KAAK,KAAK;GAAK,QAAQ,KAAK;EAAO;CAC7F,GACA,MACA,CACF;CACA,MAAM,QAAQ,SAAS,cAAc,QAAQ;CAC7C,MAAM,OAAO;CACb,MAAM,cAAc;CACpB,MAAM,UAAU;CAChB,OAAO,OAAO,OAAO,aAAa,QAAQ,KAAK;CAC/C,OAAO,iBAAiB,SAAS,6BAA6B,EAAE,MAAM,KAAK,CAAC;CAC5E,SAAS,KAAK,OAAO,MAAM;CAC3B,OAAO,UAAU;AACnB"}
@@ -0,0 +1,199 @@
1
+ const require_resource_search_discovery = require("./resource-search-discovery.js");
2
+ //#region src/renderer/resource-search-sync.ts
3
+ var SEARCH_STORAGE_FULL_MESSAGE = "Search storage is full; clear the local search cache to continue";
4
+ var isSearchStorageFull = (error) => error instanceof Error && error.message.endsWith("Search storage is full; clear the local search cache to continue");
5
+ var metadataAccept = { Accept: "application/json;as=PartialObjectMetadataList;g=meta.k8s.io;v=v1" };
6
+ function createSearchSynchronizer(options) {
7
+ const { transport, sink, signal } = options;
8
+ const status = {
9
+ scanned: 0,
10
+ discovered: 0,
11
+ denied: 0,
12
+ unavailable: 0,
13
+ unsupported: 0,
14
+ indexing: true
15
+ };
16
+ const emit = () => options.onStatus({ ...status });
17
+ const delay = options.delay ?? ((milliseconds) => new Promise((resolve) => {
18
+ if (signal.aborted) {
19
+ resolve();
20
+ return;
21
+ }
22
+ const done = () => {
23
+ clearTimeout(timer);
24
+ signal.removeEventListener("abort", done);
25
+ resolve();
26
+ };
27
+ const timer = setTimeout(done, milliseconds);
28
+ signal.addEventListener("abort", done, { once: true });
29
+ }));
30
+ let sequence = 0;
31
+ let nextRequest = 0;
32
+ const request = async (path, query, headers) => {
33
+ await delay(Math.max(0, nextRequest - Date.now()));
34
+ if (signal.aborted) throw new Error("Search synchronization canceled");
35
+ nextRequest = Date.now() + 500;
36
+ for (let attempt = 0;; attempt++) try {
37
+ return await transport.get(path, query, headers);
38
+ } catch (error) {
39
+ const code = require_resource_search_discovery.searchErrorStatus(error);
40
+ if (signal.aborted || attempt >= 3 || code !== 429 && code < 500) throw error;
41
+ const retry = error.retryAfter;
42
+ const retryMilliseconds = retry ? /^\d+$/.test(retry) ? Number(retry) * 1e3 : Date.parse(retry) - Date.now() : NaN;
43
+ await delay(Number.isFinite(retryMilliseconds) ? Math.min(864e5, Math.max(0, retryMilliseconds)) : Math.min(3e4, 1e3 * 2 ** attempt) + Math.random() * 250);
44
+ }
45
+ };
46
+ const limited = { get: request };
47
+ const scan = async (type, namespace) => {
48
+ const scope = `${type.group}/${type.resource}/${namespace ?? "*"}`;
49
+ for (let restart = 0; restart < 2; restart++) {
50
+ const scanId = `${Date.now()}-${++sequence}`;
51
+ await sink.begin(scope, scanId);
52
+ let continuation = "";
53
+ let pageSize = 250;
54
+ const seen = /* @__PURE__ */ new Set();
55
+ try {
56
+ do {
57
+ let page;
58
+ for (;;) {
59
+ const query = {
60
+ limit: pageSize,
61
+ ...continuation ? { continue: continuation } : {}
62
+ };
63
+ try {
64
+ try {
65
+ page = await request(require_resource_search_discovery.collectionPath(type, namespace), query, metadataAccept);
66
+ } catch (error) {
67
+ if (require_resource_search_discovery.searchErrorStatus(error) !== 406 || !type.group) throw error;
68
+ page = await request(require_resource_search_discovery.collectionPath(type, namespace), query, { Accept: "application/json" });
69
+ }
70
+ break;
71
+ } catch (error) {
72
+ if (!error.oversized || pageSize === 1) throw error;
73
+ pageSize = Math.max(1, Math.floor(pageSize / 2));
74
+ }
75
+ }
76
+ if (signal.aborted) return;
77
+ const documents = require_resource_search_discovery.projectMetadataPage(type, page);
78
+ status.namespaces = [.../* @__PURE__ */ new Set([...status.namespaces ?? [], ...documents.flatMap((document) => document.namespace ? [document.namespace] : [])])].sort();
79
+ await sink.append(scope, scanId, documents);
80
+ continuation = page.metadata?.continue ?? "";
81
+ if (continuation && seen.has(continuation)) throw new Error("Repeated inventory continuation");
82
+ seen.add(continuation);
83
+ } while (continuation);
84
+ if (!signal.aborted) await sink.complete(scope, scanId);
85
+ return;
86
+ } catch (error) {
87
+ const code = require_resource_search_discovery.searchErrorStatus(error);
88
+ if (code === 410 && restart === 0) continue;
89
+ if (code === 403) await sink.invalidate(scope);
90
+ throw error;
91
+ }
92
+ }
93
+ };
94
+ return { async run() {
95
+ try {
96
+ while (!signal.aborted) {
97
+ status.indexing = true;
98
+ status.scanned = 0;
99
+ status.denied = 0;
100
+ status.unavailable = 0;
101
+ status.unsupported = 0;
102
+ status.message = void 0;
103
+ emit();
104
+ let discoveryFailures = 0;
105
+ const collections = await require_resource_search_discovery.discoverSearchCollections(limited, () => {
106
+ discoveryFailures++;
107
+ status.unavailable++;
108
+ emit();
109
+ });
110
+ if (signal.aborted) return;
111
+ if (discoveryFailures === 0) await sink.reconcile?.(collections.map((type) => `${type.group}/${type.resource}`));
112
+ else status.message = "API discovery incomplete; cached resource types may be stale";
113
+ status.kinds = [...new Set(collections.map((type) => type.kind))].sort();
114
+ status.discovered = collections.length;
115
+ emit();
116
+ let fallbackNamespaces;
117
+ for (const type of collections) {
118
+ if (signal.aborted) return;
119
+ try {
120
+ await scan(type);
121
+ } catch (error) {
122
+ if (signal.aborted) return;
123
+ if (isSearchStorageFull(error)) throw error;
124
+ if (require_resource_search_discovery.searchErrorStatus(error) === 403) {
125
+ status.denied++;
126
+ if (type.namespaced) {
127
+ let namespaces = fallbackNamespaces ?? options.namespaces?.() ?? [];
128
+ if (fallbackNamespaces === void 0) {
129
+ try {
130
+ const namespaceType = {
131
+ group: "",
132
+ version: "v1",
133
+ resource: "namespaces",
134
+ kind: "Namespace",
135
+ namespaced: false,
136
+ aliases: []
137
+ };
138
+ let token = "";
139
+ let limit = 250;
140
+ const seen = /* @__PURE__ */ new Set();
141
+ do {
142
+ let page;
143
+ for (;;) try {
144
+ page = await request(require_resource_search_discovery.collectionPath(namespaceType), {
145
+ limit,
146
+ ...token ? { continue: token } : {}
147
+ }, metadataAccept);
148
+ break;
149
+ } catch (error) {
150
+ if (!error.oversized || limit === 1) throw error;
151
+ limit = Math.max(1, Math.floor(limit / 2));
152
+ }
153
+ namespaces = [.../* @__PURE__ */ new Set([...namespaces, ...(page.items ?? []).flatMap((item) => item.metadata?.name ? [item.metadata.name] : [])])];
154
+ token = page.metadata?.continue ?? "";
155
+ if (token && seen.has(token)) throw new Error("Repeated namespace continuation");
156
+ seen.add(token);
157
+ } while (token);
158
+ } catch {
159
+ status.message = "Namespace discovery denied or incomplete; add a known namespace to continue";
160
+ }
161
+ fallbackNamespaces = namespaces;
162
+ }
163
+ for (const namespace of namespaces) {
164
+ if (signal.aborted) return;
165
+ try {
166
+ await scan(type, namespace);
167
+ } catch (failure) {
168
+ if (isSearchStorageFull(failure)) throw failure;
169
+ if (require_resource_search_discovery.searchErrorStatus(failure) === 403) status.denied++;
170
+ else status.unavailable++;
171
+ }
172
+ }
173
+ }
174
+ } else if (require_resource_search_discovery.searchErrorStatus(error) === 406) status.unsupported++;
175
+ else status.unavailable++;
176
+ }
177
+ status.scanned++;
178
+ emit();
179
+ }
180
+ status.indexing = false;
181
+ status.completedAt = Date.now();
182
+ emit();
183
+ await delay(options.refreshMs ?? 600 * 1e3);
184
+ }
185
+ } catch (error) {
186
+ if (!isSearchStorageFull(error)) throw error;
187
+ status.indexing = false;
188
+ status.storageFull = true;
189
+ status.message = SEARCH_STORAGE_FULL_MESSAGE;
190
+ emit();
191
+ }
192
+ } };
193
+ }
194
+ //#endregion
195
+ exports.SEARCH_STORAGE_FULL_MESSAGE = SEARCH_STORAGE_FULL_MESSAGE;
196
+ exports.createSearchSynchronizer = createSearchSynchronizer;
197
+ exports.isSearchStorageFull = isSearchStorageFull;
198
+
199
+ //# sourceMappingURL=resource-search-sync.js.map