@64mb/freelens-power 2.1.0 → 2.2.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 (106) hide show
  1. package/README.md +4 -4
  2. package/out/main/common/power-logs.js.map +1 -1
  3. package/out/main/common/resource-search.js.map +1 -1
  4. package/out/main/power-logs-child.js +3 -3
  5. package/out/main/power-logs-child.js.map +1 -1
  6. package/out/main/power-logs-database.js +165 -7
  7. package/out/main/power-logs-database.js.map +1 -1
  8. package/out/main/power-logs-ipc.js +12 -3
  9. package/out/main/power-logs-ipc.js.map +1 -1
  10. package/out/main/power-logs-worker.js +29 -7
  11. package/out/main/power-logs-worker.js.map +1 -1
  12. package/out/main/resource-search-database.js +3 -2
  13. package/out/main/resource-search-database.js.map +1 -1
  14. package/out/main/resource-search.js +2 -1
  15. package/out/main/resource-search.js.map +1 -1
  16. package/out/renderer/_virtual/_global-external_react.js +2 -1
  17. package/out/renderer/ai-context-mutations.js +14 -0
  18. package/out/renderer/ai-context-mutations.js.map +1 -0
  19. package/out/renderer/ai-copy-answer.js +18 -15
  20. package/out/renderer/ai-copy-answer.js.map +1 -1
  21. package/out/renderer/ai-diagnostic-handoff.js +2 -2
  22. package/out/renderer/ai-diagnostic-handoff.js.map +1 -1
  23. package/out/renderer/ai-extras.js +41 -38
  24. package/out/renderer/ai-extras.js.map +1 -1
  25. package/out/renderer/ai-message.js +50 -39
  26. package/out/renderer/ai-message.js.map +1 -1
  27. package/out/renderer/ai-provider-icon.js +39 -32
  28. package/out/renderer/ai-provider-icon.js.map +1 -1
  29. package/out/renderer/ai-resize.js +40 -37
  30. package/out/renderer/ai-resize.js.map +1 -1
  31. package/out/renderer/ai-shortcuts.js +20 -17
  32. package/out/renderer/ai-shortcuts.js.map +1 -1
  33. package/out/renderer/ai-terminal-context.js +42 -48
  34. package/out/renderer/ai-terminal-context.js.map +1 -1
  35. package/out/renderer/ai.js +137 -111
  36. package/out/renderer/ai.js.map +1 -1
  37. package/out/renderer/cluster-icon-colors.js +10 -7
  38. package/out/renderer/cluster-icon-colors.js.map +1 -1
  39. package/out/renderer/cluster-icons.js +47 -40
  40. package/out/renderer/cluster-icons.js.map +1 -1
  41. package/out/renderer/details/service-diagnostics-details.js +25 -23
  42. package/out/renderer/details/service-diagnostics-details.js.map +1 -1
  43. package/out/renderer/diagnostic-explain-button.js +7 -4
  44. package/out/renderer/diagnostic-explain-button.js.map +1 -1
  45. package/out/renderer/diagnostics-screen-finding.js +51 -0
  46. package/out/renderer/diagnostics-screen-finding.js.map +1 -0
  47. package/out/renderer/diagnostics.css.js +1 -1
  48. package/out/renderer/diagnostics.css.js.map +1 -1
  49. package/out/renderer/diagnostics.js +146 -107
  50. package/out/renderer/diagnostics.js.map +1 -1
  51. package/out/renderer/incident-copy-button.js +24 -21
  52. package/out/renderer/incident-copy-button.js.map +1 -1
  53. package/out/renderer/index.js +2 -0
  54. package/out/renderer/index.js.map +1 -1
  55. package/out/renderer/monitoring.js +19 -22
  56. package/out/renderer/monitoring.js.map +1 -1
  57. package/out/renderer/native-tooltip.js +22 -0
  58. package/out/renderer/native-tooltip.js.map +1 -0
  59. package/out/renderer/pod-details-enhancements.js +106 -0
  60. package/out/renderer/pod-details-enhancements.js.map +1 -0
  61. package/out/renderer/power-log-json-cell.js +44 -38
  62. package/out/renderer/power-log-json-cell.js.map +1 -1
  63. package/out/renderer/power-log-json.js +17 -10
  64. package/out/renderer/power-log-json.js.map +1 -1
  65. package/out/renderer/power-log-sql-editor.js +31 -2
  66. package/out/renderer/power-log-sql-editor.js.map +1 -1
  67. package/out/renderer/power-logs-client.js +5 -0
  68. package/out/renderer/power-logs-client.js.map +1 -1
  69. package/out/renderer/power-logs-entrypoints.js +10 -36
  70. package/out/renderer/power-logs-entrypoints.js.map +1 -1
  71. package/out/renderer/power-logs.css.js +9 -2
  72. package/out/renderer/power-logs.css.js.map +1 -1
  73. package/out/renderer/power-logs.js +376 -214
  74. package/out/renderer/power-logs.js.map +1 -1
  75. package/out/renderer/preferences/dashboard-preferences.js +42 -35
  76. package/out/renderer/preferences/dashboard-preferences.js.map +1 -1
  77. package/out/renderer/preferences/logs-preferences.js +1 -1
  78. package/out/renderer/preferences/logs-preferences.js.map +1 -1
  79. package/out/renderer/preferences/monitoring-preferences.js +7 -4
  80. package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
  81. package/out/renderer/preferences/preferences.js +2 -2
  82. package/out/renderer/preferences/preferences.js.map +1 -1
  83. package/out/renderer/preferences/settings-icon-action.js +12 -11
  84. package/out/renderer/preferences/settings-icon-action.js.map +1 -1
  85. package/out/renderer/relationship-map-finding.js +53 -0
  86. package/out/renderer/relationship-map-finding.js.map +1 -0
  87. package/out/renderer/resource-relationships.css.js +6 -0
  88. package/out/renderer/resource-relationships.css.js.map +1 -1
  89. package/out/renderer/resource-relationships.js +94 -47
  90. package/out/renderer/resource-relationships.js.map +1 -1
  91. package/out/renderer/resource-search-client.js.map +1 -1
  92. package/out/renderer/resource-search.js +88 -35
  93. package/out/renderer/resource-search.js.map +1 -1
  94. package/out/renderer/src/common/gateway-relationships.js +8 -7
  95. package/out/renderer/src/common/gateway-relationships.js.map +1 -1
  96. package/out/renderer/src/common/power-log-json-filter-sql.js +12 -0
  97. package/out/renderer/src/common/power-log-json-filter-sql.js.map +1 -1
  98. package/out/renderer/src/common/power-log-json-preset.js +2 -2
  99. package/out/renderer/src/common/power-log-json-preset.js.map +1 -1
  100. package/out/renderer/src/common/power-logs.js.map +1 -1
  101. package/out/renderer/src/common/resource-relationships.js +10 -8
  102. package/out/renderer/src/common/resource-relationships.js.map +1 -1
  103. package/out/renderer/src/common/resource-search.js.map +1 -1
  104. package/out/renderer/welcome.js +16 -1
  105. package/out/renderer/welcome.js.map +1 -1
  106. package/package.json +1 -1
@@ -6,6 +6,7 @@ const require__global_external_react = require("./_virtual/_global-external_reac
6
6
  const require__global_external_react_dom = require("./_virtual/_global-external_react-dom.js");
7
7
  const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
8
8
  const require_ai_icon = require("./ai-icon.js");
9
+ const require_native_tooltip = require("./native-tooltip.js");
9
10
  const require_ai_diagnostic_handoff = require("./ai-diagnostic-handoff.js");
10
11
  const require_diagnostic_explain_button = require("./diagnostic-explain-button.js");
11
12
  const require_react_select_esm = require("./node_modules/.pnpm/react-select@5.10.2_@types_react@17.0.93_react-dom@17.0.2_react@17.0.2__react@17.0.2/node_modules/react-select/dist/react-select.esm.js");
@@ -95,12 +96,14 @@ function SearchFilterSelect({ value, options, ...props }) {
95
96
  }
96
97
  require__global_external_mobx_react.observer(() => {
97
98
  if (!require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate().resourceSearchEnabled) return null;
98
- return /* @__PURE__ */ require_jsx_runtime.jsx("button", {
99
- type: "button",
99
+ return /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
100
100
  title: "Search resources (double Shift)",
101
- "aria-label": "Search resources",
102
- onClick: () => window.dispatchEvent(new Event(OPEN)),
103
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "search" })
101
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("button", {
102
+ type: "button",
103
+ "aria-label": "Search resources",
104
+ onClick: () => window.dispatchEvent(new Event(OPEN)),
105
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "search" })
106
+ })
104
107
  });
105
108
  });
106
109
  function SearchSnippet({ field, query }) {
@@ -145,7 +148,29 @@ function SearchDialog({ close, clusterId }) {
145
148
  if (selectedPath !== void 0 && selectedPath !== selectedResource?.path) setSelectedPath(void 0);
146
149
  }, [selectedPath, selectedResource?.path]);
147
150
  const [coverage, setCoverage] = require__global_external_react.useState(currentCoverage);
148
- const [limit, setLimit] = require__global_external_react.useState(100);
151
+ const searchKey = JSON.stringify([
152
+ query,
153
+ kindFilter,
154
+ namespaceFilter
155
+ ]);
156
+ const [allFor, setAllFor] = require__global_external_react.useState();
157
+ const fetchAll = allFor === searchKey;
158
+ const [allRequests, setAllRequests] = require__global_external_react.useState(0);
159
+ const inventoryRevision = `${coverage.scanned}/${coverage.indexing}/${coverage.completedAt ?? ""}`;
160
+ const [allInventoryRevision, setAllInventoryRevision] = require__global_external_react.useState(inventoryRevision);
161
+ require__global_external_react.useEffect(() => {
162
+ if (!fetchAll || !loading && !coverage.indexing) setAllInventoryRevision(inventoryRevision);
163
+ }, [
164
+ fetchAll,
165
+ loading,
166
+ coverage.indexing,
167
+ inventoryRevision
168
+ ]);
169
+ const queryInventoryRevision = fetchAll ? allInventoryRevision : inventoryRevision;
170
+ const [progress, setProgress] = require__global_external_react.useState({
171
+ loaded: 0,
172
+ total: 0
173
+ });
149
174
  const [hasMore, setHasMore] = require__global_external_react.useState(false);
150
175
  require__global_external_react.useEffect(() => {
151
176
  const previous = document.activeElement;
@@ -159,21 +184,41 @@ function SearchDialog({ close, clusterId }) {
159
184
  }, []);
160
185
  require__global_external_react.useEffect(() => {
161
186
  let live = true;
187
+ setLoading(true);
162
188
  const client = require_resource_search_client.createResourceSearchClient(clusterId, require_resource_search_client.searchContextId());
163
189
  const timer = window.setTimeout(() => {
164
- client.query(query, limit, {
165
- ...kindFilter ? { kind: coverage.kinds?.find((kind) => kind.toLocaleLowerCase() === kindFilter.toLocaleLowerCase()) ?? kindFilter } : {},
166
- ...namespaceFilter ? { namespace: namespaceFilter === "(cluster)" ? "" : namespaceFilter } : {}
167
- }).then((result) => {
168
- if (!live || activeId() !== clusterId) return;
169
- setResources(result.documents);
170
- setHasMore(result.hasMore);
190
+ (async () => {
191
+ const filters = {
192
+ ...kindFilter ? { kind: coverage.kinds?.find((kind) => kind.toLocaleLowerCase() === kindFilter.toLocaleLowerCase()) ?? kindFilter } : {},
193
+ ...namespaceFilter ? { namespace: namespaceFilter === "(cluster)" ? "" : namespaceFilter } : {}
194
+ };
195
+ const documents = /* @__PURE__ */ new Map();
196
+ let offset = 0;
197
+ do {
198
+ const result = await client.query(query, fetchAll ? 1e3 : 100, {
199
+ ...filters,
200
+ ...offset ? { offset } : {}
201
+ });
202
+ if (!live || activeId() !== clusterId) return;
203
+ const previousCount = documents.size;
204
+ for (const document of result.documents) documents.set(document.path, document);
205
+ if (!fetchAll || documents.size >= resources.length || !result.hasMore) setResources([...documents.values()]);
206
+ setProgress({
207
+ loaded: documents.size,
208
+ total: result.total
209
+ });
210
+ setHasMore(result.hasMore);
211
+ offset += result.documents.length;
212
+ if (!fetchAll || !result.hasMore) break;
213
+ if (documents.size === previousCount) throw new Error("Search pagination made no progress");
214
+ } while (live);
215
+ if (!live) return;
171
216
  setLoading(false);
172
217
  setFailed(0);
173
218
  client.status?.().then((status) => {
174
219
  if (live) setCachedCount(status.count);
175
220
  }).catch(() => {});
176
- }).catch(() => {
221
+ })().catch(() => {
177
222
  if (live) {
178
223
  setFailed(1);
179
224
  setLoading(false);
@@ -187,11 +232,11 @@ function SearchDialog({ close, clusterId }) {
187
232
  }, [
188
233
  clusterId,
189
234
  query,
190
- limit,
235
+ fetchAll,
236
+ allRequests,
191
237
  kindFilter,
192
238
  namespaceFilter,
193
- coverage.scanned,
194
- coverage.indexing
239
+ queryInventoryRevision
195
240
  ]);
196
241
  require__global_external_react.useEffect(() => {
197
242
  document.getElementById(`power-resource-result-${selected}`)?.scrollIntoView?.({ block: "nearest" });
@@ -261,12 +306,14 @@ function SearchDialog({ close, clusterId }) {
261
306
  display: "flex",
262
307
  gap: 4
263
308
  },
264
- children: [/* @__PURE__ */ require_jsx_runtime.jsx("button", {
265
- type: "button",
266
- "aria-label": "Refresh resource inventory",
309
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
267
310
  title: "Refresh resource inventory",
268
- onClick: () => restartSearchSync(),
269
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "refresh" })
311
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("button", {
312
+ type: "button",
313
+ "aria-label": "Refresh resource inventory",
314
+ onClick: () => restartSearchSync(),
315
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "refresh" })
316
+ })
270
317
  }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
271
318
  type: "button",
272
319
  "aria-label": "Close resource search",
@@ -417,27 +464,33 @@ function SearchDialog({ close, clusterId }) {
417
464
  })]
418
465
  }, resource.path)), !results.length && /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: loading ? "Searching…" : "No matching resources in the available inventory" })]
419
466
  }),
420
- hasMore && /* @__PURE__ */ require_jsx_runtime.jsx("button", {
467
+ (hasMore || loading && fetchAll) && /* @__PURE__ */ require_jsx_runtime.jsx("button", {
421
468
  type: "button",
422
- disabled: limit >= 1e3,
423
- onClick: () => setLimit((value) => Math.min(1e3, value + 100)),
424
- children: "Show more matches"
469
+ disabled: loading,
470
+ "aria-busy": loading,
471
+ onClick: () => {
472
+ setLoading(true);
473
+ setAllFor(searchKey);
474
+ setAllRequests((value) => value + 1);
475
+ },
476
+ children: loading ? `Loading matches… ${progress.loaded} / ${progress.total}` : "Show all matches"
425
477
  }),
426
- hasMore && limit >= 1e3 && /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "Showing up to 1000 candidates. Refine the name, type or namespace to continue." }),
427
478
  /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
428
479
  className: "power-resource-search-footer",
429
480
  children: [/* @__PURE__ */ require_jsx_runtime.jsx("p", {
430
481
  className: "power-resource-search-shortcuts",
431
482
  children: "↑↓ Navigate · Enter Open · Esc Close"
432
- }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
433
- type: "button",
434
- className: "power-resource-search-clear",
483
+ }), /* @__PURE__ */ require_jsx_runtime.jsx(require_native_tooltip.NativeTooltip, {
435
484
  title: "Clear local search cache",
436
- "aria-label": "Clear local search cache",
437
- onClick: clearCache,
438
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
439
- material: "delete_sweep",
440
- size: 16
485
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("button", {
486
+ type: "button",
487
+ className: "power-resource-search-clear",
488
+ "aria-label": "Clear local search cache",
489
+ onClick: clearCache,
490
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
491
+ material: "delete_sweep",
492
+ size: 16
493
+ })
441
494
  })
442
495
  })]
443
496
  })
@@ -1 +1 @@
1
- {"version":3,"file":"resource-search.js","names":[],"sources":["../../src/renderer/resource-search.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport { observer } from \"mobx-react\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport Select from \"react-select\";\nimport { resourceKindPriority } from \"../common/resource-search-kind-priority\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport { explainDiagnosticFinding } from \"./ai-diagnostic-handoff\";\nimport { AiIcon } from \"./ai-icon\";\nimport { DiagnosticExplainButton } from \"./diagnostic-explain-button\";\nimport { createResourceSearchClient, createResourceSearchTransport, searchContextId } from \"./resource-search-client\";\nimport { projectSearchFields } from \"./resource-search-fields\";\nimport {\n createDoubleShift,\n createResourceSearchIndex,\n queryTokens,\n type SearchResource,\n} from \"./resource-search-model\";\nimport { closeSearchResourceFallback, openSearchResource } from \"./resource-search-routing\";\nimport { createSearchSynchronizer, type SearchCoverage } from \"./resource-search-sync\";\n\nimport type { SearchDocument } from \"../common/resource-search\";\n\nconst OPEN = \"power-resource-search\";\nconst activeId = () => Renderer.Catalog.activeCluster.get()?.metadata.uid;\nconst inventory = () =>\n [\n [\"Pod\", Renderer.K8sApi.podsStore, \"view_in_ar\"],\n [\"Node\", Renderer.K8sApi.nodesStore, \"dns\"],\n [\"Deployment\", Renderer.K8sApi.deploymentStore, \"layers\"],\n [\"StatefulSet\", Renderer.K8sApi.statefulSetStore, \"layers\"],\n [\"DaemonSet\", Renderer.K8sApi.daemonSetStore, \"layers\"],\n [\"ReplicaSet\", Renderer.K8sApi.replicaSetStore, \"layers\"],\n [\"Job\", Renderer.K8sApi.jobStore, \"work\"],\n [\"CronJob\", Renderer.K8sApi.cronJobStore, \"schedule\"],\n [\"Service\", Renderer.K8sApi.serviceStore, \"settings_ethernet\"],\n [\"Ingress\", Renderer.K8sApi.ingressStore, \"alt_route\"],\n [\"Namespace\", Renderer.K8sApi.namespaceStore, \"folder\"],\n [\"HorizontalPodAutoscaler\", Renderer.K8sApi.hpaStore, \"swap_vert\"],\n ] as const;\nconst icons = new Map(inventory().map(([kind, , icon]) => [kind as string, icon]));\n\nfunction SearchFilterSelect({\n value,\n options,\n ...props\n}: {\n value: string;\n options: { value: string; label: string }[];\n \"aria-label\": string;\n onMenuOpen(): void;\n onMenuClose(): void;\n onChange(option: { value: string; label: string } | null): void;\n}) {\n return (\n <Select\n {...props}\n className={`Select theme-${Renderer.Theme?.activeTheme?.get()?.type ?? \"dark\"}`}\n classNamePrefix=\"Select\"\n value={options.find((option) => option.value === value) ?? null}\n options={options}\n isClearable={false}\n tabSelectsValue={false}\n menuPlacement=\"auto\"\n />\n );\n}\n\nexport const ResourceSearchLauncher = observer(() => {\n if (!MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>().resourceSearchEnabled) return null;\n return (\n <button\n type=\"button\"\n title=\"Search resources (double Shift)\"\n aria-label=\"Search resources\"\n onClick={() => window.dispatchEvent(new Event(OPEN))}\n >\n <AiIcon material=\"search\" />\n </button>\n );\n});\n\nfunction SearchSnippet({ field, query }: { field?: { label: string; text: string }; query: string }) {\n if (!field) return null;\n const token = queryTokens(query).find((token) => field.text.toLocaleLowerCase().includes(token));\n const offset = token ? field.text.toLocaleLowerCase().indexOf(token) : -1;\n return (\n <small className=\"power-resource-search-snippet\">\n {field.label}:{\" \"}\n {offset < 0 || !token ? (\n field.text\n ) : (\n <>\n {field.text.slice(0, offset)}\n <mark>{field.text.slice(offset, offset + token.length)}</mark>\n {field.text.slice(offset + token.length)}\n </>\n )}\n </small>\n );\n}\n\nfunction SearchDialog({ close, clusterId }: { close: () => void; clusterId: string }) {\n const [query, setQuery] = useState(\"\");\n const [kindFilter, setKindFilter] = useState(\"\");\n const [namespaceFilter, setNamespaceFilter] = useState(\"\");\n const [resources, setResources] = useState<SearchResource[]>([]);\n const [loading, setLoading] = useState(true);\n const [failed, setFailed] = useState(0);\n const [cachedCount, setCachedCount] = useState<number>();\n const [selectedPath, setSelectedPath] = useState<string>();\n const [error, setError] = useState(\"\");\n const dialog = useRef<HTMLDivElement>(null);\n const input = useRef<HTMLInputElement>(null);\n const openFilterMenus = useRef(new Set<string>());\n const escapeFromOpenMenu = useRef(false);\n const searchIndex = useMemo(() => createResourceSearchIndex(resources), [resources]);\n const matches = searchIndex.search(query);\n const selectedResource = matches.find((resource) => resource.path === selectedPath) ?? matches[0];\n const results = matches;\n if (selectedResource && !results.includes(selectedResource)) results[results.length - 1] = selectedResource;\n const selected = results.findIndex((resource) => resource.path === selectedResource?.path);\n useEffect(() => {\n if (selectedPath !== undefined && selectedPath !== selectedResource?.path) setSelectedPath(undefined);\n }, [selectedPath, selectedResource?.path]);\n const [coverage, setCoverage] = useState<SearchCoverage>(currentCoverage);\n const [limit, setLimit] = useState(100);\n const [hasMore, setHasMore] = useState(false);\n useEffect(() => {\n const previous = document.activeElement as HTMLElement | null;\n input.current?.focus();\n const update = () => setCoverage({ ...currentCoverage });\n window.addEventListener(\"power-search-coverage\", update);\n return () => {\n window.removeEventListener(\"power-search-coverage\", update);\n if (previous?.isConnected) previous.focus();\n };\n }, []);\n useEffect(() => {\n let live = true;\n const client = createResourceSearchClient(clusterId, searchContextId());\n const timer = window.setTimeout(() => {\n void client\n .query(query, limit, {\n ...(kindFilter\n ? {\n kind:\n coverage.kinds?.find((kind) => kind.toLocaleLowerCase() === kindFilter.toLocaleLowerCase()) ??\n kindFilter,\n }\n : {}),\n ...(namespaceFilter ? { namespace: namespaceFilter === \"(cluster)\" ? \"\" : namespaceFilter } : {}),\n })\n .then((result) => {\n if (!live || activeId() !== clusterId) return;\n setResources(result.documents);\n setHasMore(result.hasMore);\n setLoading(false);\n setFailed(0);\n void client\n .status?.()\n .then((status) => {\n if (live) setCachedCount(status.count);\n })\n .catch(() => {});\n })\n .catch(() => {\n if (live) {\n setFailed(1);\n setLoading(false);\n }\n });\n }, 100);\n return () => {\n live = false;\n window.clearTimeout(timer);\n };\n }, [clusterId, query, limit, kindFilter, namespaceFilter, coverage.scanned, coverage.indexing]);\n useEffect(() => {\n document.getElementById(`power-resource-result-${selected}`)?.scrollIntoView?.({ block: \"nearest\" });\n }, [selected, selectedResource?.path]);\n const clearCache = () => {\n void createResourceSearchClient(clusterId, searchContextId())\n .clear()\n .then(() => {\n setResources([]);\n restartSearchSync();\n })\n .catch(() => setError(\"Could not clear the local search cache. Try again.\"));\n };\n const open = (resource: SearchResource) => {\n if (activeId() !== clusterId) return;\n void Promise.resolve()\n .then(() => openSearchResource(resource as SearchDocument))\n .then(() => {\n if (activeId() === clusterId) close();\n })\n .catch(() => setError(\"Could not open this resource. Try again or refresh the inventory.\"));\n };\n return (\n <div\n className=\"power-resource-search-backdrop\"\n data-freelens-power-ai-overlay=\"\"\n onClick={(event) => event.stopPropagation()}\n onMouseDown={(event) => {\n event.stopPropagation();\n if (event.target === event.currentTarget) close();\n }}\n >\n <div\n ref={dialog}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"power-resource-search-title\"\n className=\"power-resource-search-dialog\"\n onKeyDownCapture={(event) => {\n if (event.key === \"Escape\") escapeFromOpenMenu.current = openFilterMenus.current.size > 0;\n }}\n onKeyDown={(event) => {\n if (event.key === \"Escape\" && !escapeFromOpenMenu.current) {\n event.stopPropagation();\n close();\n }\n if (event.key === \"Tab\") {\n const controls = Array.from(\n dialog.current?.querySelectorAll<HTMLElement>(\"input, button:not(:disabled), select\") ?? [],\n );\n const index = controls.indexOf(document.activeElement as HTMLElement);\n if (event.shiftKey && index <= 0) {\n event.preventDefault();\n controls.at(-1)?.focus();\n } else if (!event.shiftKey && index === controls.length - 1) {\n event.preventDefault();\n controls[0]?.focus();\n }\n }\n }}\n >\n <div className=\"power-resource-search-heading\">\n <strong id=\"power-resource-search-title\" style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>\n <AiIcon material=\"search\" size={18} />\n Search resources\n </strong>\n <div style={{ display: \"flex\", gap: 4 }}>\n <button\n type=\"button\"\n aria-label=\"Refresh resource inventory\"\n title=\"Refresh resource inventory\"\n onClick={() => restartSearchSync()}\n >\n <AiIcon material=\"refresh\" />\n </button>\n <button type=\"button\" aria-label=\"Close resource search\" onClick={close}>\n <AiIcon material=\"close\" />\n </button>\n </div>\n </div>\n <input\n ref={input}\n aria-label=\"Resource name, namespace, kind, label or indexed text\"\n aria-controls=\"power-resource-search-results\"\n placeholder=\"Name, namespace, label, image, condition…\"\n maxLength={300}\n value={query}\n onChange={(event) => {\n setQuery(event.target.value);\n setSelectedPath(undefined);\n }}\n onKeyDown={(event) => {\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n const next = results.length\n ? (selected + (event.key === \"ArrowDown\" ? 1 : -1) + results.length) % results.length\n : 0;\n setSelectedPath(results[next]?.path);\n }\n if (event.key === \"Enter\" && !event.nativeEvent.isComposing && results[selected]) {\n event.preventDefault();\n open(results[selected]);\n }\n }}\n />\n <div className=\"power-resource-search-filters\">\n <label className=\"power-resource-search-filter\">\n <span>Kind</span>\n <SearchFilterSelect\n aria-label=\"Kind\"\n onMenuOpen={() => openFilterMenus.current.add(\"kind\")}\n onMenuClose={() => openFilterMenus.current.delete(\"kind\")}\n value={kindFilter}\n options={[\n { value: \"\", label: \"All types\" },\n ...[...(coverage.kinds ?? [])]\n .sort(\n (first, second) =>\n resourceKindPriority(first) - resourceKindPriority(second) || first.localeCompare(second, \"en\"),\n )\n .map((kind) => ({ value: kind, label: kind })),\n ]}\n onChange={(option) => {\n setKindFilter(option?.value ?? \"\");\n setSelectedPath(undefined);\n }}\n />\n </label>\n <label className=\"power-resource-search-filter\">\n <span>Namespace</span>\n <SearchFilterSelect\n aria-label=\"Namespace\"\n onMenuOpen={() => openFilterMenus.current.add(\"namespace\")}\n onMenuClose={() => openFilterMenus.current.delete(\"namespace\")}\n value={namespaceFilter}\n options={[\n { value: \"\", label: \"All namespaces\" },\n { value: \"(cluster)\", label: \"Cluster scoped\" },\n ...(coverage.namespaces ?? []).map((namespace) => ({ value: namespace, label: namespace })),\n ]}\n onChange={(option) => {\n setNamespaceFilter(option?.value ?? \"\");\n setSelectedPath(undefined);\n }}\n />\n </label>\n </div>\n <p role=\"status\">\n {coverage.indexing\n ? `Indexing ${coverage.scanned}/${coverage.discovered} types`\n : coverage.completedAt\n ? `Updated ${new Date(coverage.completedAt).toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" })}`\n : \"Cached inventory\"}\n {cachedCount !== undefined && ` · ${cachedCount.toLocaleString()} resources`}\n {coverage.denied > 0 && ` · ${coverage.denied} restricted scopes`}\n {coverage.unavailable + coverage.unsupported > 0 &&\n ` · ${coverage.unavailable + coverage.unsupported} APIs unavailable`}\n {failed > 0 && \" · Search cache unavailable\"}\n </p>\n {(coverage.message || coverage.denied > 0 || coverage.unavailable + coverage.unsupported > 0) && (\n <details>\n <summary>Inventory coverage</summary>\n <p>\n {coverage.message ?? \"Results include the resources your current permissions and available APIs allow.\"}\n </p>\n </details>\n )}\n {coverage.storageFull && (\n <div role=\"alert\">\n <p>Local search storage is full. Cached results are partial; indexing is paused.</p>\n </div>\n )}\n {error && <p role=\"alert\">{error}</p>}\n <div\n id=\"power-resource-search-results\"\n role=\"list\"\n aria-label=\"Matching resources\"\n className=\"power-resource-search-results\"\n >\n {results.map((resource, index) => (\n <div\n key={resource.path}\n id={`power-resource-result-${index}`}\n role=\"listitem\"\n className={`power-resource-search-row ${selected === index ? \"selected\" : \"\"}`}\n >\n <button className=\"power-resource-search-open\" type=\"button\" onClick={() => open(resource)}>\n <AiIcon material={icons.get(resource.kind) ?? \"view_in_ar\"} />\n <span>\n <strong>{resource.name}</strong>\n <small>\n {resource.kind} · {resource.namespace ?? \"cluster scoped\"}\n </small>\n {searchIndex.nameParts(resource) && <small>Name parts</small>}\n {searchIndex.similarName(resource) && <small>Similar name</small>}\n <SearchSnippet field={searchIndex.snippet(resource, query)} query={query} />\n </span>\n </button>\n <DiagnosticExplainButton\n className=\"power-resource-search-ai\"\n aria-label={`Ask AI about ${resource.kind} ${resource.name}`}\n title=\"Ask AI about this resource\"\n onClick={() => {\n if (activeId() !== clusterId) return;\n close();\n explainDiagnosticFinding({\n clusterId,\n title: `${resource.kind}: ${resource.name}`,\n summary: `Resource selected from search. API group: ${(resource as SearchDocument).group || \"core\"}; version: ${(resource as SearchDocument).version}; resource: ${(resource as SearchDocument).resource}; kind: ${resource.kind}; namespace: ${resource.namespace ?? \"cluster scoped\"}; name: ${resource.name}; UID: ${(resource as SearchDocument).uid ?? \"unknown\"}; path: ${resource.path}. No live diagnostic data was collected.`,\n resource: { kind: resource.kind, name: resource.name, namespace: resource.namespace },\n });\n }}\n >\n <AiIcon material=\"auto_awesome\" />\n </DiagnosticExplainButton>\n </div>\n ))}\n {!results.length && <p>{loading ? \"Searching…\" : \"No matching resources in the available inventory\"}</p>}\n </div>\n {hasMore && (\n <button\n type=\"button\"\n disabled={limit >= 1000}\n onClick={() => setLimit((value) => Math.min(1000, value + 100))}\n >\n Show more matches\n </button>\n )}\n {hasMore && limit >= 1000 && (\n <p>Showing up to 1000 candidates. Refine the name, type or namespace to continue.</p>\n )}\n <div className=\"power-resource-search-footer\">\n <p className=\"power-resource-search-shortcuts\">↑↓ Navigate · Enter Open · Esc Close</p>\n <button\n type=\"button\"\n className=\"power-resource-search-clear\"\n title=\"Clear local search cache\"\n aria-label=\"Clear local search cache\"\n onClick={clearCache}\n >\n <AiIcon material=\"delete_sweep\" size={16} />\n </button>\n </div>\n </div>\n </div>\n );\n}\n\nlet currentCoverage: SearchCoverage = {\n scanned: 0,\n discovered: 0,\n denied: 0,\n unavailable: 0,\n unsupported: 0,\n indexing: true,\n};\nlet restartSearchSync = () => {};\n\nexport function startResourceSearch(): () => void {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n let stop = preferences.resourceSearchEnabled ? startEnabledResourceSearch() : undefined;\n const dispose = reaction(\n () => preferences.resourceSearchEnabled,\n (enabled) => {\n stop?.();\n stop = enabled ? startEnabledResourceSearch() : undefined;\n },\n );\n return () => {\n dispose();\n stop?.();\n stop = undefined;\n };\n}\n\nfunction startEnabledResourceSearch(): () => void {\n let syncAbort: AbortController | undefined;\n const sync = () => {\n syncAbort?.abort();\n closeSearchResourceFallback();\n const clusterId = activeId();\n if (!clusterId || window.top === window) return;\n const phase = Renderer.Catalog.activeCluster.get()?.status?.phase;\n if (phase && phase !== \"connected\") {\n currentCoverage = {\n ...currentCoverage,\n indexing: false,\n message: \"Cluster disconnected; cached results may be stale\",\n };\n window.dispatchEvent(new Event(\"power-search-coverage\"));\n return;\n }\n const controller = new AbortController();\n syncAbort = controller;\n const client = createResourceSearchClient(clusterId, searchContextId());\n const liveStores = new Map(inventory().map(([kind, store]) => [kind as string, store]));\n const liveObjects = new Map<string, Map<string, unknown>>();\n const sink = {\n ...client,\n append: (scope: string, scanId: string, documents: SearchDocument[]) =>\n client.append(\n scope,\n scanId,\n documents.map((item) => {\n const store = liveStores.get(item.kind);\n if (!store) return item;\n let objects = liveObjects.get(item.kind);\n if (!objects) {\n objects = new Map(store.items.map((object) => [object.selfLink, object] as const));\n liveObjects.set(item.kind, objects);\n }\n const object = objects.get(item.path);\n return object ? { ...item, fields: projectSearchFields(item.kind, object) } : item;\n }),\n ),\n };\n let wasIndexing = false;\n const task = createSearchSynchronizer({\n transport: createResourceSearchTransport(clusterId, controller.signal),\n sink,\n signal: controller.signal,\n namespaces: () => Renderer.Catalog.activeCluster.get()?.spec?.accessibleNamespaces ?? [],\n onStatus: (status) => {\n if (!controller.signal.aborted) {\n if (status.indexing && !wasIndexing) liveObjects.clear();\n wasIndexing = status.indexing;\n currentCoverage = status;\n window.dispatchEvent(new Event(\"power-search-coverage\"));\n }\n },\n });\n void task.run().catch(() => {\n if (!controller.signal.aborted) {\n currentCoverage = { ...currentCoverage, indexing: false, unavailable: currentCoverage.unavailable + 1 };\n window.dispatchEvent(new Event(\"power-search-coverage\"));\n }\n });\n };\n restartSearchSync = sync;\n sync();\n const container = document.createElement(\"div\");\n const style = document.createElement(\"style\");\n style.textContent = `.power-resource-search-backdrop{position:fixed;inset:0;z-index:10000;background:#0007;display:flex;align-items:flex-start;justify-content:center;padding:8vh 20px}.power-resource-search-dialog{width:680px;max-width:100%;max-height:75vh;display:flex;flex-direction:column;padding:20px;gap:12px;background:var(--mainBackground,#222);color:var(--textColorPrimary,#eee);border:1px solid var(--borderColor,#555);border-radius:8px;box-shadow:0 16px 60px #0008}.power-resource-search-heading{display:flex;align-items:center;justify-content:space-between}.power-resource-search-filters{display:flex;align-items:end;gap:8px;flex-wrap:wrap}.power-resource-search-filter{display:flex;flex-direction:column;gap:4px;flex:1;min-width:180px;font-size:12px;color:var(--textColorSecondary)}.power-resource-search-filter .Select{width:100%;min-width:0}.power-resource-search-filter .Select__control{min-height:30px;height:30px;font-size:12px}.power-resource-search-filter .Select__value-container{padding:0 8px;min-width:0}.power-resource-search-filter .Select__input-container{margin:0;padding:0}.power-resource-search-filter .Select__indicator{padding:4px 8px}.power-resource-search-filter .Select__single-value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.power-resource-search-dialog > input,.power-resource-search-dialog details input{padding:12px;font-size:16px;background:var(--inputControlBackground,#333);color:inherit;border:1px solid var(--borderColor,#666);border-radius:4px}.power-resource-search-dialog p{margin:0;font-size:12px;color:var(--textColorSecondary,#bbb)}.power-resource-search-results{overflow:auto;min-height:180px;flex:1}.power-resource-search-row{display:flex;align-items:center;gap:12px;padding:4px;border-radius:4px}.power-resource-search-row{border-left:3px solid transparent}.power-resource-search-row.selected{background:color-mix(in srgb,var(--primary,#668fff) 16%,var(--mainBackground,#222));color:var(--tableSelectedRowColor,var(--textColorPrimary,#eee));border-left-color:var(--primary,#668fff)}.power-resource-search-row.selected .Icon{color:var(--primary,#8daaff)}.power-resource-search-row:hover{background:var(--tableBgcSelected,var(--contentColor,#292d32))}.power-resource-search-dialog button:hover:not(:disabled){background:#ffffff12;border-radius:4px}.power-resource-search-row>button:last-child{border:1px solid var(--borderColor,#555);border-radius:4px}.power-resource-search-footer{display:flex;align-items:center;justify-content:space-between;gap:8px;padding-top:8px;border-top:1px solid var(--borderColor,#555)}.power-resource-search-shortcuts{flex:1}.power-resource-search-footer .power-resource-search-clear{display:inline-flex;align-items:center;justify-content:center;min-width:28px;min-height:28px;width:28px;height:28px;padding:0}.power-resource-search-dialog button{min-width:32px;min-height:32px;padding:6px;color:inherit;background:transparent;border:0;cursor:pointer}.power-resource-search-dialog button:disabled{opacity:.45;cursor:default}.power-resource-search-open{display:flex;align-items:center;gap:12px;flex:1;text-align:left;min-width:0}.power-resource-search-open span{min-width:0}.power-resource-search-open strong,.power-resource-search-open small{display:block;overflow-wrap:anywhere}.power-resource-search-open small{opacity:.7}.power-resource-search-row>button.power-resource-search-ai{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:34px;height:34px;min-width:34px;padding:0;background:var(--contentColor,#292d32);border:1px solid var(--borderColor,#666)}.power-resource-search-row.selected>button.power-resource-search-ai{background:var(--mainBackground,#202326);border-color:color-mix(in srgb,var(--textColorPrimary,#ddd) 55%,var(--borderColor,#666));box-shadow:0 1px 3px #0002}.power-resource-search-row.selected>button.power-resource-search-ai .Icon{color:var(--textColorAccent,#eee)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled){background:var(--contentColor,#30343a);border-color:var(--primary,#8daaff);color:var(--primary,#8daaff)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled) .Icon{color:inherit}.power-resource-search-dialog button:focus-visible{outline:2px solid var(--primary,#668fff)}`;\n document.head.append(style);\n document.body.append(container);\n let visible = false;\n const close = () => {\n if (visible) {\n visible = false;\n ReactDOM.unmountComponentAtNode(container);\n }\n };\n const show = () => {\n const clusterId = activeId();\n if (\n window.top === window ||\n !clusterId ||\n visible ||\n document.querySelector('[aria-modal=\"true\"], .Dialog.is-opened')\n )\n return;\n visible = true;\n ReactDOM.render(<SearchDialog close={close} clusterId={clusterId} />, container);\n };\n let shift = createDoubleShift();\n const resetShift = () => {\n shift = createDoubleShift();\n };\n const key = (event: KeyboardEvent) => {\n if (event.isComposing || event.key === \"Process\") {\n resetShift();\n return;\n }\n if (shift(event, performance.now())) show();\n };\n window.addEventListener(\"blur\", resetShift);\n window.addEventListener(\"keydown\", key);\n window.addEventListener(\"keyup\", key);\n window.addEventListener(OPEN, show);\n const dispose = reaction(\n () => `${searchContextId()}/${Renderer.Catalog.activeCluster.get()?.status?.phase}`,\n () => {\n close();\n sync();\n },\n );\n return () => {\n close();\n syncAbort?.abort();\n closeSearchResourceFallback();\n restartSearchSync = () => {};\n dispose();\n window.removeEventListener(\"blur\", resetShift);\n window.removeEventListener(\"keydown\", key);\n window.removeEventListener(\"keyup\", key);\n window.removeEventListener(OPEN, show);\n container.remove();\n style.remove();\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"resource-search.js","names":[],"sources":["../../src/renderer/resource-search.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport { observer } from \"mobx-react\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport Select from \"react-select\";\nimport { resourceKindPriority } from \"../common/resource-search-kind-priority\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport { explainDiagnosticFinding } from \"./ai-diagnostic-handoff\";\nimport { AiIcon } from \"./ai-icon\";\nimport { DiagnosticExplainButton } from \"./diagnostic-explain-button\";\nimport { NativeTooltip } from \"./native-tooltip\";\nimport { createResourceSearchClient, createResourceSearchTransport, searchContextId } from \"./resource-search-client\";\nimport { projectSearchFields } from \"./resource-search-fields\";\nimport {\n createDoubleShift,\n createResourceSearchIndex,\n queryTokens,\n type SearchResource,\n} from \"./resource-search-model\";\nimport { closeSearchResourceFallback, openSearchResource } from \"./resource-search-routing\";\nimport { createSearchSynchronizer, type SearchCoverage } from \"./resource-search-sync\";\n\nimport type { SearchDocument } from \"../common/resource-search\";\n\nconst OPEN = \"power-resource-search\";\nconst activeId = () => Renderer.Catalog.activeCluster.get()?.metadata.uid;\nconst inventory = () =>\n [\n [\"Pod\", Renderer.K8sApi.podsStore, \"view_in_ar\"],\n [\"Node\", Renderer.K8sApi.nodesStore, \"dns\"],\n [\"Deployment\", Renderer.K8sApi.deploymentStore, \"layers\"],\n [\"StatefulSet\", Renderer.K8sApi.statefulSetStore, \"layers\"],\n [\"DaemonSet\", Renderer.K8sApi.daemonSetStore, \"layers\"],\n [\"ReplicaSet\", Renderer.K8sApi.replicaSetStore, \"layers\"],\n [\"Job\", Renderer.K8sApi.jobStore, \"work\"],\n [\"CronJob\", Renderer.K8sApi.cronJobStore, \"schedule\"],\n [\"Service\", Renderer.K8sApi.serviceStore, \"settings_ethernet\"],\n [\"Ingress\", Renderer.K8sApi.ingressStore, \"alt_route\"],\n [\"Namespace\", Renderer.K8sApi.namespaceStore, \"folder\"],\n [\"HorizontalPodAutoscaler\", Renderer.K8sApi.hpaStore, \"swap_vert\"],\n ] as const;\nconst icons = new Map(inventory().map(([kind, , icon]) => [kind as string, icon]));\n\nfunction SearchFilterSelect({\n value,\n options,\n ...props\n}: {\n value: string;\n options: { value: string; label: string }[];\n \"aria-label\": string;\n onMenuOpen(): void;\n onMenuClose(): void;\n onChange(option: { value: string; label: string } | null): void;\n}) {\n return (\n <Select\n {...props}\n className={`Select theme-${Renderer.Theme?.activeTheme?.get()?.type ?? \"dark\"}`}\n classNamePrefix=\"Select\"\n value={options.find((option) => option.value === value) ?? null}\n options={options}\n isClearable={false}\n tabSelectsValue={false}\n menuPlacement=\"auto\"\n />\n );\n}\n\nexport const ResourceSearchLauncher = observer(() => {\n if (!MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>().resourceSearchEnabled) return null;\n return (\n <NativeTooltip title=\"Search resources (double Shift)\">\n <button type=\"button\" aria-label=\"Search resources\" onClick={() => window.dispatchEvent(new Event(OPEN))}>\n <AiIcon material=\"search\" />\n </button>\n </NativeTooltip>\n );\n});\n\nfunction SearchSnippet({ field, query }: { field?: { label: string; text: string }; query: string }) {\n if (!field) return null;\n const token = queryTokens(query).find((token) => field.text.toLocaleLowerCase().includes(token));\n const offset = token ? field.text.toLocaleLowerCase().indexOf(token) : -1;\n return (\n <small className=\"power-resource-search-snippet\">\n {field.label}:{\" \"}\n {offset < 0 || !token ? (\n field.text\n ) : (\n <>\n {field.text.slice(0, offset)}\n <mark>{field.text.slice(offset, offset + token.length)}</mark>\n {field.text.slice(offset + token.length)}\n </>\n )}\n </small>\n );\n}\n\nfunction SearchDialog({ close, clusterId }: { close: () => void; clusterId: string }) {\n const [query, setQuery] = useState(\"\");\n const [kindFilter, setKindFilter] = useState(\"\");\n const [namespaceFilter, setNamespaceFilter] = useState(\"\");\n const [resources, setResources] = useState<SearchResource[]>([]);\n const [loading, setLoading] = useState(true);\n const [failed, setFailed] = useState(0);\n const [cachedCount, setCachedCount] = useState<number>();\n const [selectedPath, setSelectedPath] = useState<string>();\n const [error, setError] = useState(\"\");\n const dialog = useRef<HTMLDivElement>(null);\n const input = useRef<HTMLInputElement>(null);\n const openFilterMenus = useRef(new Set<string>());\n const escapeFromOpenMenu = useRef(false);\n const searchIndex = useMemo(() => createResourceSearchIndex(resources), [resources]);\n const matches = searchIndex.search(query);\n const selectedResource = matches.find((resource) => resource.path === selectedPath) ?? matches[0];\n const results = matches;\n if (selectedResource && !results.includes(selectedResource)) results[results.length - 1] = selectedResource;\n const selected = results.findIndex((resource) => resource.path === selectedResource?.path);\n useEffect(() => {\n if (selectedPath !== undefined && selectedPath !== selectedResource?.path) setSelectedPath(undefined);\n }, [selectedPath, selectedResource?.path]);\n const [coverage, setCoverage] = useState<SearchCoverage>(currentCoverage);\n const searchKey = JSON.stringify([query, kindFilter, namespaceFilter]);\n const [allFor, setAllFor] = useState<string>();\n const fetchAll = allFor === searchKey;\n const [allRequests, setAllRequests] = useState(0);\n const inventoryRevision = `${coverage.scanned}/${coverage.indexing}/${coverage.completedAt ?? \"\"}`;\n const [allInventoryRevision, setAllInventoryRevision] = useState(inventoryRevision);\n useEffect(() => {\n // finish pagination before reconciling the completed inventory\n if (!fetchAll || (!loading && !coverage.indexing)) setAllInventoryRevision(inventoryRevision);\n }, [fetchAll, loading, coverage.indexing, inventoryRevision]);\n const queryInventoryRevision = fetchAll ? allInventoryRevision : inventoryRevision;\n const [progress, setProgress] = useState({ loaded: 0, total: 0 });\n const [hasMore, setHasMore] = useState(false);\n useEffect(() => {\n const previous = document.activeElement as HTMLElement | null;\n input.current?.focus();\n const update = () => setCoverage({ ...currentCoverage });\n window.addEventListener(\"power-search-coverage\", update);\n return () => {\n window.removeEventListener(\"power-search-coverage\", update);\n if (previous?.isConnected) previous.focus();\n };\n }, []);\n useEffect(() => {\n let live = true;\n setLoading(true);\n const client = createResourceSearchClient(clusterId, searchContextId());\n const timer = window.setTimeout(() => {\n void (async () => {\n const filters = {\n ...(kindFilter\n ? {\n kind:\n coverage.kinds?.find((kind) => kind.toLocaleLowerCase() === kindFilter.toLocaleLowerCase()) ??\n kindFilter,\n }\n : {}),\n ...(namespaceFilter ? { namespace: namespaceFilter === \"(cluster)\" ? \"\" : namespaceFilter } : {}),\n };\n const documents = new Map<string, SearchDocument>();\n let offset = 0;\n do {\n const result = await client.query(query, fetchAll ? 1000 : 100, {\n ...filters,\n ...(offset ? { offset } : {}),\n });\n if (!live || activeId() !== clusterId) return;\n const previousCount = documents.size;\n for (const document of result.documents) documents.set(document.path, document);\n if (!fetchAll || documents.size >= resources.length || !result.hasMore) {\n setResources([...documents.values()]);\n }\n setProgress({ loaded: documents.size, total: result.total });\n setHasMore(result.hasMore);\n offset += result.documents.length;\n if (!fetchAll || !result.hasMore) break;\n if (documents.size === previousCount) throw new Error(\"Search pagination made no progress\");\n } while (live);\n if (!live) return;\n setLoading(false);\n setFailed(0);\n void client\n .status?.()\n .then((status) => {\n if (live) setCachedCount(status.count);\n })\n .catch(() => {});\n })().catch(() => {\n if (live) {\n setFailed(1);\n setLoading(false);\n }\n });\n }, 100);\n return () => {\n live = false;\n window.clearTimeout(timer);\n };\n }, [clusterId, query, fetchAll, allRequests, kindFilter, namespaceFilter, queryInventoryRevision]);\n useEffect(() => {\n document.getElementById(`power-resource-result-${selected}`)?.scrollIntoView?.({ block: \"nearest\" });\n }, [selected, selectedResource?.path]);\n const clearCache = () => {\n void createResourceSearchClient(clusterId, searchContextId())\n .clear()\n .then(() => {\n setResources([]);\n restartSearchSync();\n })\n .catch(() => setError(\"Could not clear the local search cache. Try again.\"));\n };\n const open = (resource: SearchResource) => {\n if (activeId() !== clusterId) return;\n void Promise.resolve()\n .then(() => openSearchResource(resource as SearchDocument))\n .then(() => {\n if (activeId() === clusterId) close();\n })\n .catch(() => setError(\"Could not open this resource. Try again or refresh the inventory.\"));\n };\n return (\n <div\n className=\"power-resource-search-backdrop\"\n data-freelens-power-ai-overlay=\"\"\n onClick={(event) => event.stopPropagation()}\n onMouseDown={(event) => {\n event.stopPropagation();\n if (event.target === event.currentTarget) close();\n }}\n >\n <div\n ref={dialog}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"power-resource-search-title\"\n className=\"power-resource-search-dialog\"\n onKeyDownCapture={(event) => {\n if (event.key === \"Escape\") escapeFromOpenMenu.current = openFilterMenus.current.size > 0;\n }}\n onKeyDown={(event) => {\n if (event.key === \"Escape\" && !escapeFromOpenMenu.current) {\n event.stopPropagation();\n close();\n }\n if (event.key === \"Tab\") {\n const controls = Array.from(\n dialog.current?.querySelectorAll<HTMLElement>(\"input, button:not(:disabled), select\") ?? [],\n );\n const index = controls.indexOf(document.activeElement as HTMLElement);\n if (event.shiftKey && index <= 0) {\n event.preventDefault();\n controls.at(-1)?.focus();\n } else if (!event.shiftKey && index === controls.length - 1) {\n event.preventDefault();\n controls[0]?.focus();\n }\n }\n }}\n >\n <div className=\"power-resource-search-heading\">\n <strong id=\"power-resource-search-title\" style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>\n <AiIcon material=\"search\" size={18} />\n Search resources\n </strong>\n <div style={{ display: \"flex\", gap: 4 }}>\n <NativeTooltip title=\"Refresh resource inventory\">\n <button type=\"button\" aria-label=\"Refresh resource inventory\" onClick={() => restartSearchSync()}>\n <AiIcon material=\"refresh\" />\n </button>\n </NativeTooltip>\n <button type=\"button\" aria-label=\"Close resource search\" onClick={close}>\n <AiIcon material=\"close\" />\n </button>\n </div>\n </div>\n <input\n ref={input}\n aria-label=\"Resource name, namespace, kind, label or indexed text\"\n aria-controls=\"power-resource-search-results\"\n placeholder=\"Name, namespace, label, image, condition…\"\n maxLength={300}\n value={query}\n onChange={(event) => {\n setQuery(event.target.value);\n setSelectedPath(undefined);\n }}\n onKeyDown={(event) => {\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n const next = results.length\n ? (selected + (event.key === \"ArrowDown\" ? 1 : -1) + results.length) % results.length\n : 0;\n setSelectedPath(results[next]?.path);\n }\n if (event.key === \"Enter\" && !event.nativeEvent.isComposing && results[selected]) {\n event.preventDefault();\n open(results[selected]);\n }\n }}\n />\n <div className=\"power-resource-search-filters\">\n <label className=\"power-resource-search-filter\">\n <span>Kind</span>\n <SearchFilterSelect\n aria-label=\"Kind\"\n onMenuOpen={() => openFilterMenus.current.add(\"kind\")}\n onMenuClose={() => openFilterMenus.current.delete(\"kind\")}\n value={kindFilter}\n options={[\n { value: \"\", label: \"All types\" },\n ...[...(coverage.kinds ?? [])]\n .sort(\n (first, second) =>\n resourceKindPriority(first) - resourceKindPriority(second) || first.localeCompare(second, \"en\"),\n )\n .map((kind) => ({ value: kind, label: kind })),\n ]}\n onChange={(option) => {\n setKindFilter(option?.value ?? \"\");\n setSelectedPath(undefined);\n }}\n />\n </label>\n <label className=\"power-resource-search-filter\">\n <span>Namespace</span>\n <SearchFilterSelect\n aria-label=\"Namespace\"\n onMenuOpen={() => openFilterMenus.current.add(\"namespace\")}\n onMenuClose={() => openFilterMenus.current.delete(\"namespace\")}\n value={namespaceFilter}\n options={[\n { value: \"\", label: \"All namespaces\" },\n { value: \"(cluster)\", label: \"Cluster scoped\" },\n ...(coverage.namespaces ?? []).map((namespace) => ({ value: namespace, label: namespace })),\n ]}\n onChange={(option) => {\n setNamespaceFilter(option?.value ?? \"\");\n setSelectedPath(undefined);\n }}\n />\n </label>\n </div>\n <p role=\"status\">\n {coverage.indexing\n ? `Indexing ${coverage.scanned}/${coverage.discovered} types`\n : coverage.completedAt\n ? `Updated ${new Date(coverage.completedAt).toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" })}`\n : \"Cached inventory\"}\n {cachedCount !== undefined && ` · ${cachedCount.toLocaleString()} resources`}\n {coverage.denied > 0 && ` · ${coverage.denied} restricted scopes`}\n {coverage.unavailable + coverage.unsupported > 0 &&\n ` · ${coverage.unavailable + coverage.unsupported} APIs unavailable`}\n {failed > 0 && \" · Search cache unavailable\"}\n </p>\n {(coverage.message || coverage.denied > 0 || coverage.unavailable + coverage.unsupported > 0) && (\n <details>\n <summary>Inventory coverage</summary>\n <p>\n {coverage.message ?? \"Results include the resources your current permissions and available APIs allow.\"}\n </p>\n </details>\n )}\n {coverage.storageFull && (\n <div role=\"alert\">\n <p>Local search storage is full. Cached results are partial; indexing is paused.</p>\n </div>\n )}\n {error && <p role=\"alert\">{error}</p>}\n <div\n id=\"power-resource-search-results\"\n role=\"list\"\n aria-label=\"Matching resources\"\n className=\"power-resource-search-results\"\n >\n {results.map((resource, index) => (\n <div\n key={resource.path}\n id={`power-resource-result-${index}`}\n role=\"listitem\"\n className={`power-resource-search-row ${selected === index ? \"selected\" : \"\"}`}\n >\n <button className=\"power-resource-search-open\" type=\"button\" onClick={() => open(resource)}>\n <AiIcon material={icons.get(resource.kind) ?? \"view_in_ar\"} />\n <span>\n <strong>{resource.name}</strong>\n <small>\n {resource.kind} · {resource.namespace ?? \"cluster scoped\"}\n </small>\n {searchIndex.nameParts(resource) && <small>Name parts</small>}\n {searchIndex.similarName(resource) && <small>Similar name</small>}\n <SearchSnippet field={searchIndex.snippet(resource, query)} query={query} />\n </span>\n </button>\n <DiagnosticExplainButton\n className=\"power-resource-search-ai\"\n aria-label={`Ask AI about ${resource.kind} ${resource.name}`}\n title=\"Ask AI about this resource\"\n onClick={() => {\n if (activeId() !== clusterId) return;\n close();\n explainDiagnosticFinding({\n clusterId,\n title: `${resource.kind}: ${resource.name}`,\n summary: `Resource selected from search. API group: ${(resource as SearchDocument).group || \"core\"}; version: ${(resource as SearchDocument).version}; resource: ${(resource as SearchDocument).resource}; kind: ${resource.kind}; namespace: ${resource.namespace ?? \"cluster scoped\"}; name: ${resource.name}; UID: ${(resource as SearchDocument).uid ?? \"unknown\"}; path: ${resource.path}. No live diagnostic data was collected.`,\n resource: { kind: resource.kind, name: resource.name, namespace: resource.namespace },\n });\n }}\n >\n <AiIcon material=\"auto_awesome\" />\n </DiagnosticExplainButton>\n </div>\n ))}\n {!results.length && <p>{loading ? \"Searching…\" : \"No matching resources in the available inventory\"}</p>}\n </div>\n {(hasMore || (loading && fetchAll)) && (\n <button\n type=\"button\"\n disabled={loading}\n aria-busy={loading}\n onClick={() => {\n setLoading(true);\n setAllFor(searchKey);\n setAllRequests((value) => value + 1);\n }}\n >\n {loading ? `Loading matches… ${progress.loaded} / ${progress.total}` : \"Show all matches\"}\n </button>\n )}\n <div className=\"power-resource-search-footer\">\n <p className=\"power-resource-search-shortcuts\">↑↓ Navigate · Enter Open · Esc Close</p>\n <NativeTooltip title=\"Clear local search cache\">\n <button\n type=\"button\"\n className=\"power-resource-search-clear\"\n aria-label=\"Clear local search cache\"\n onClick={clearCache}\n >\n <AiIcon material=\"delete_sweep\" size={16} />\n </button>\n </NativeTooltip>\n </div>\n </div>\n </div>\n );\n}\n\nlet currentCoverage: SearchCoverage = {\n scanned: 0,\n discovered: 0,\n denied: 0,\n unavailable: 0,\n unsupported: 0,\n indexing: true,\n};\nlet restartSearchSync = () => {};\n\nexport function startResourceSearch(): () => void {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n let stop = preferences.resourceSearchEnabled ? startEnabledResourceSearch() : undefined;\n const dispose = reaction(\n () => preferences.resourceSearchEnabled,\n (enabled) => {\n stop?.();\n stop = enabled ? startEnabledResourceSearch() : undefined;\n },\n );\n return () => {\n dispose();\n stop?.();\n stop = undefined;\n };\n}\n\nfunction startEnabledResourceSearch(): () => void {\n let syncAbort: AbortController | undefined;\n const sync = () => {\n syncAbort?.abort();\n closeSearchResourceFallback();\n const clusterId = activeId();\n if (!clusterId || window.top === window) return;\n const phase = Renderer.Catalog.activeCluster.get()?.status?.phase;\n if (phase && phase !== \"connected\") {\n currentCoverage = {\n ...currentCoverage,\n indexing: false,\n message: \"Cluster disconnected; cached results may be stale\",\n };\n window.dispatchEvent(new Event(\"power-search-coverage\"));\n return;\n }\n const controller = new AbortController();\n syncAbort = controller;\n const client = createResourceSearchClient(clusterId, searchContextId());\n const liveStores = new Map(inventory().map(([kind, store]) => [kind as string, store]));\n const liveObjects = new Map<string, Map<string, unknown>>();\n const sink = {\n ...client,\n append: (scope: string, scanId: string, documents: SearchDocument[]) =>\n client.append(\n scope,\n scanId,\n documents.map((item) => {\n const store = liveStores.get(item.kind);\n if (!store) return item;\n let objects = liveObjects.get(item.kind);\n if (!objects) {\n objects = new Map(store.items.map((object) => [object.selfLink, object] as const));\n liveObjects.set(item.kind, objects);\n }\n const object = objects.get(item.path);\n return object ? { ...item, fields: projectSearchFields(item.kind, object) } : item;\n }),\n ),\n };\n let wasIndexing = false;\n const task = createSearchSynchronizer({\n transport: createResourceSearchTransport(clusterId, controller.signal),\n sink,\n signal: controller.signal,\n namespaces: () => Renderer.Catalog.activeCluster.get()?.spec?.accessibleNamespaces ?? [],\n onStatus: (status) => {\n if (!controller.signal.aborted) {\n if (status.indexing && !wasIndexing) liveObjects.clear();\n wasIndexing = status.indexing;\n currentCoverage = status;\n window.dispatchEvent(new Event(\"power-search-coverage\"));\n }\n },\n });\n void task.run().catch(() => {\n if (!controller.signal.aborted) {\n currentCoverage = { ...currentCoverage, indexing: false, unavailable: currentCoverage.unavailable + 1 };\n window.dispatchEvent(new Event(\"power-search-coverage\"));\n }\n });\n };\n restartSearchSync = sync;\n sync();\n const container = document.createElement(\"div\");\n const style = document.createElement(\"style\");\n style.textContent = `.power-resource-search-backdrop{position:fixed;inset:0;z-index:10000;background:#0007;display:flex;align-items:flex-start;justify-content:center;padding:8vh 20px}.power-resource-search-dialog{width:680px;max-width:100%;max-height:75vh;display:flex;flex-direction:column;padding:20px;gap:12px;background:var(--mainBackground,#222);color:var(--textColorPrimary,#eee);border:1px solid var(--borderColor,#555);border-radius:8px;box-shadow:0 16px 60px #0008}.power-resource-search-heading{display:flex;align-items:center;justify-content:space-between}.power-resource-search-filters{display:flex;align-items:end;gap:8px;flex-wrap:wrap}.power-resource-search-filter{display:flex;flex-direction:column;gap:4px;flex:1;min-width:180px;font-size:12px;color:var(--textColorSecondary)}.power-resource-search-filter .Select{width:100%;min-width:0}.power-resource-search-filter .Select__control{min-height:30px;height:30px;font-size:12px}.power-resource-search-filter .Select__value-container{padding:0 8px;min-width:0}.power-resource-search-filter .Select__input-container{margin:0;padding:0}.power-resource-search-filter .Select__indicator{padding:4px 8px}.power-resource-search-filter .Select__single-value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.power-resource-search-dialog > input,.power-resource-search-dialog details input{padding:12px;font-size:16px;background:var(--inputControlBackground,#333);color:inherit;border:1px solid var(--borderColor,#666);border-radius:4px}.power-resource-search-dialog p{margin:0;font-size:12px;color:var(--textColorSecondary,#bbb)}.power-resource-search-results{overflow:auto;min-height:180px;flex:1}.power-resource-search-row{display:flex;align-items:center;gap:12px;padding:4px;border-radius:4px}.power-resource-search-row{border-left:3px solid transparent}.power-resource-search-row.selected{background:color-mix(in srgb,var(--primary,#668fff) 16%,var(--mainBackground,#222));color:var(--tableSelectedRowColor,var(--textColorPrimary,#eee));border-left-color:var(--primary,#668fff)}.power-resource-search-row.selected .Icon{color:var(--primary,#8daaff)}.power-resource-search-row:hover{background:var(--tableBgcSelected,var(--contentColor,#292d32))}.power-resource-search-dialog button:hover:not(:disabled){background:#ffffff12;border-radius:4px}.power-resource-search-row>button:last-child{border:1px solid var(--borderColor,#555);border-radius:4px}.power-resource-search-footer{display:flex;align-items:center;justify-content:space-between;gap:8px;padding-top:8px;border-top:1px solid var(--borderColor,#555)}.power-resource-search-shortcuts{flex:1}.power-resource-search-footer .power-resource-search-clear{display:inline-flex;align-items:center;justify-content:center;min-width:28px;min-height:28px;width:28px;height:28px;padding:0}.power-resource-search-dialog button{min-width:32px;min-height:32px;padding:6px;color:inherit;background:transparent;border:0;cursor:pointer}.power-resource-search-dialog button:disabled{opacity:.45;cursor:default}.power-resource-search-open{display:flex;align-items:center;gap:12px;flex:1;text-align:left;min-width:0}.power-resource-search-open span{min-width:0}.power-resource-search-open strong,.power-resource-search-open small{display:block;overflow-wrap:anywhere}.power-resource-search-open small{opacity:.7}.power-resource-search-row>button.power-resource-search-ai{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:34px;height:34px;min-width:34px;padding:0;background:var(--contentColor,#292d32);border:1px solid var(--borderColor,#666)}.power-resource-search-row.selected>button.power-resource-search-ai{background:var(--mainBackground,#202326);border-color:color-mix(in srgb,var(--textColorPrimary,#ddd) 55%,var(--borderColor,#666));box-shadow:0 1px 3px #0002}.power-resource-search-row.selected>button.power-resource-search-ai .Icon{color:var(--textColorAccent,#eee)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled){background:var(--contentColor,#30343a);border-color:var(--primary,#8daaff);color:var(--primary,#8daaff)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled) .Icon{color:inherit}.power-resource-search-dialog button:focus-visible{outline:2px solid var(--primary,#668fff)}`;\n document.head.append(style);\n document.body.append(container);\n let visible = false;\n const close = () => {\n if (visible) {\n visible = false;\n ReactDOM.unmountComponentAtNode(container);\n }\n };\n const show = () => {\n const clusterId = activeId();\n if (\n window.top === window ||\n !clusterId ||\n visible ||\n document.querySelector('[aria-modal=\"true\"], .Dialog.is-opened')\n )\n return;\n visible = true;\n ReactDOM.render(<SearchDialog close={close} clusterId={clusterId} />, container);\n };\n let shift = createDoubleShift();\n const resetShift = () => {\n shift = createDoubleShift();\n };\n const key = (event: KeyboardEvent) => {\n if (event.isComposing || event.key === \"Process\") {\n resetShift();\n return;\n }\n if (shift(event, performance.now())) show();\n };\n window.addEventListener(\"blur\", resetShift);\n window.addEventListener(\"keydown\", key);\n window.addEventListener(\"keyup\", key);\n window.addEventListener(OPEN, show);\n const dispose = reaction(\n () => `${searchContextId()}/${Renderer.Catalog.activeCluster.get()?.status?.phase}`,\n () => {\n close();\n sync();\n },\n );\n return () => {\n close();\n syncAbort?.abort();\n closeSearchResourceFallback();\n restartSearchSync = () => {};\n dispose();\n window.removeEventListener(\"blur\", resetShift);\n window.removeEventListener(\"keydown\", key);\n window.removeEventListener(\"keyup\", key);\n window.removeEventListener(OPEN, show);\n container.remove();\n style.remove();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAyBA,IAAM,OAAO;AACb,IAAM,iBAAiB,mBAAA,SAAS,QAAQ,cAAc,IAAI,CAAC,EAAE,SAAS;AACtE,IAAM,kBACJ;CACE;EAAC;EAAO,mBAAA,SAAS,OAAO;EAAW;CAAY;CAC/C;EAAC;EAAQ,mBAAA,SAAS,OAAO;EAAY;CAAK;CAC1C;EAAC;EAAc,mBAAA,SAAS,OAAO;EAAiB;CAAQ;CACxD;EAAC;EAAe,mBAAA,SAAS,OAAO;EAAkB;CAAQ;CAC1D;EAAC;EAAa,mBAAA,SAAS,OAAO;EAAgB;CAAQ;CACtD;EAAC;EAAc,mBAAA,SAAS,OAAO;EAAiB;CAAQ;CACxD;EAAC;EAAO,mBAAA,SAAS,OAAO;EAAU;CAAM;CACxC;EAAC;EAAW,mBAAA,SAAS,OAAO;EAAc;CAAU;CACpD;EAAC;EAAW,mBAAA,SAAS,OAAO;EAAc;CAAmB;CAC7D;EAAC;EAAW,mBAAA,SAAS,OAAO;EAAc;CAAW;CACrD;EAAC;EAAa,mBAAA,SAAS,OAAO;EAAgB;CAAQ;CACtD;EAAC;EAA2B,mBAAA,SAAS,OAAO;EAAU;CAAW;AACnE;AACF,IAAM,QAAQ,IAAI,IAAI,UAAU,CAAC,CAAC,KAAK,CAAC,QAAQ,UAAU,CAAC,MAAgB,IAAI,CAAC,CAAC;AAEjF,SAAS,mBAAmB,EAC1B,OACA,SACA,GAAG,SAQF;CACD,OACE,oCAAA,IAAC,yBAAA,SAAD;EACE,GAAI;EACJ,WAAW,gBAAgB,mBAAA,SAAS,OAAO,aAAa,IAAI,CAAC,EAAE,QAAQ;EACvE,iBAAgB;EAChB,OAAO,QAAQ,MAAM,WAAW,OAAO,UAAU,KAAK,KAAK;EAClD;EACT,aAAa;EACb,iBAAiB;EACjB,eAAc;CACf,CAAA;AAEL;AAEsC,oCAAA,eAAe;CACnD,IAAI,CAAC,qCAAA,2BAA2B,oBAAgD,CAAC,CAAC,uBAAuB,OAAO;CAChH,OACE,oCAAA,IAAC,uBAAA,eAAD;EAAe,OAAM;YACnB,oCAAA,IAAC,UAAD;GAAQ,MAAK;GAAS,cAAW;GAAmB,eAAe,OAAO,cAAc,IAAI,MAAM,IAAI,CAAC;aACrG,oCAAA,IAAC,gBAAA,QAAD,EAAQ,UAAS,SAAU,CAAA;EACrB,CAAA;CACK,CAAA;AAEnB,CAAC;AAED,SAAS,cAAc,EAAE,OAAO,SAAqE;CACnG,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,QAAQ,8BAAA,YAAY,KAAK,CAAC,CAAC,MAAM,UAAU,MAAM,KAAK,kBAAkB,CAAC,CAAC,SAAS,KAAK,CAAC;CAC/F,MAAM,SAAS,QAAQ,MAAM,KAAK,kBAAkB,CAAC,CAAC,QAAQ,KAAK,IAAI;CACvE,OACE,oCAAA,KAAC,SAAD;EAAO,WAAU;YAAjB;GACG,MAAM;GAAM;GAAE;GACd,SAAS,KAAK,CAAC,QACd,MAAM,OAEN,oCAAA,KAAA,oBAAA,UAAA,EAAA,UAAA;IACG,MAAM,KAAK,MAAM,GAAG,MAAM;IAC3B,oCAAA,IAAC,QAAD,EAAA,UAAO,MAAM,KAAK,MAAM,QAAQ,SAAS,MAAM,MAAM,EAAQ,CAAA;IAC5D,MAAM,KAAK,MAAM,SAAS,MAAM,MAAM;GACvC,EAAA,CAAA;EAEC;;AAEX;AAEA,SAAS,aAAa,EAAE,OAAO,aAAuD;CACpF,MAAM,CAAC,OAAO,YAAY,+BAAA,SAAS,EAAE;CACrC,MAAM,CAAC,YAAY,iBAAiB,+BAAA,SAAS,EAAE;CAC/C,MAAM,CAAC,iBAAiB,sBAAsB,+BAAA,SAAS,EAAE;CACzD,MAAM,CAAC,WAAW,gBAAgB,+BAAA,SAA2B,CAAC,CAAC;CAC/D,MAAM,CAAC,SAAS,cAAc,+BAAA,SAAS,IAAI;CAC3C,MAAM,CAAC,QAAQ,aAAa,+BAAA,SAAS,CAAC;CACtC,MAAM,CAAC,aAAa,kBAAkB,+BAAA,SAAiB;CACvD,MAAM,CAAC,cAAc,mBAAmB,+BAAA,SAAiB;CACzD,MAAM,CAAC,OAAO,YAAY,+BAAA,SAAS,EAAE;CACrC,MAAM,SAAS,+BAAA,OAAuB,IAAI;CAC1C,MAAM,QAAQ,+BAAA,OAAyB,IAAI;CAC3C,MAAM,kBAAkB,+BAAA,uBAAO,IAAI,IAAY,CAAC;CAChD,MAAM,qBAAqB,+BAAA,OAAO,KAAK;CACvC,MAAM,cAAc,+BAAA,cAAc,8BAAA,0BAA0B,SAAS,GAAG,CAAC,SAAS,CAAC;CACnF,MAAM,UAAU,YAAY,OAAO,KAAK;CACxC,MAAM,mBAAmB,QAAQ,MAAM,aAAa,SAAS,SAAS,YAAY,KAAK,QAAQ;CAC/F,MAAM,UAAU;CAChB,IAAI,oBAAoB,CAAC,QAAQ,SAAS,gBAAgB,GAAG,QAAQ,QAAQ,SAAS,KAAK;CAC3F,MAAM,WAAW,QAAQ,WAAW,aAAa,SAAS,SAAS,kBAAkB,IAAI;CACzF,+BAAA,gBAAgB;EACd,IAAI,iBAAiB,KAAA,KAAa,iBAAiB,kBAAkB,MAAM,gBAAgB,KAAA,CAAS;CACtG,GAAG,CAAC,cAAc,kBAAkB,IAAI,CAAC;CACzC,MAAM,CAAC,UAAU,eAAe,+BAAA,SAAyB,eAAe;CACxE,MAAM,YAAY,KAAK,UAAU;EAAC;EAAO;EAAY;CAAe,CAAC;CACrE,MAAM,CAAC,QAAQ,aAAa,+BAAA,SAAiB;CAC7C,MAAM,WAAW,WAAW;CAC5B,MAAM,CAAC,aAAa,kBAAkB,+BAAA,SAAS,CAAC;CAChD,MAAM,oBAAoB,GAAG,SAAS,QAAQ,GAAG,SAAS,SAAS,GAAG,SAAS,eAAe;CAC9F,MAAM,CAAC,sBAAsB,2BAA2B,+BAAA,SAAS,iBAAiB;CAClF,+BAAA,gBAAgB;EAEd,IAAI,CAAC,YAAa,CAAC,WAAW,CAAC,SAAS,UAAW,wBAAwB,iBAAiB;CAC9F,GAAG;EAAC;EAAU;EAAS,SAAS;EAAU;CAAiB,CAAC;CAC5D,MAAM,yBAAyB,WAAW,uBAAuB;CACjE,MAAM,CAAC,UAAU,eAAe,+BAAA,SAAS;EAAE,QAAQ;EAAG,OAAO;CAAE,CAAC;CAChE,MAAM,CAAC,SAAS,cAAc,+BAAA,SAAS,KAAK;CAC5C,+BAAA,gBAAgB;EACd,MAAM,WAAW,SAAS;EAC1B,MAAM,SAAS,MAAM;EACrB,MAAM,eAAe,YAAY,EAAE,GAAG,gBAAgB,CAAC;EACvD,OAAO,iBAAiB,yBAAyB,MAAM;EACvD,aAAa;GACX,OAAO,oBAAoB,yBAAyB,MAAM;GAC1D,IAAI,UAAU,aAAa,SAAS,MAAM;EAC5C;CACF,GAAG,CAAC,CAAC;CACL,+BAAA,gBAAgB;EACd,IAAI,OAAO;EACX,WAAW,IAAI;EACf,MAAM,SAAS,+BAAA,2BAA2B,WAAW,+BAAA,gBAAgB,CAAC;EACtE,MAAM,QAAQ,OAAO,iBAAiB;GACpC,CAAM,YAAY;IAChB,MAAM,UAAU;KACd,GAAI,aACA,EACE,MACE,SAAS,OAAO,MAAM,SAAS,KAAK,kBAAkB,MAAM,WAAW,kBAAkB,CAAC,KAC1F,WACJ,IACA,CAAC;KACL,GAAI,kBAAkB,EAAE,WAAW,oBAAoB,cAAc,KAAK,gBAAgB,IAAI,CAAC;IACjG;IACA,MAAM,4BAAY,IAAI,IAA4B;IAClD,IAAI,SAAS;IACb,GAAG;KACD,MAAM,SAAS,MAAM,OAAO,MAAM,OAAO,WAAW,MAAO,KAAK;MAC9D,GAAG;MACH,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;KAC7B,CAAC;KACD,IAAI,CAAC,QAAQ,SAAS,MAAM,WAAW;KACvC,MAAM,gBAAgB,UAAU;KAChC,KAAK,MAAM,YAAY,OAAO,WAAW,UAAU,IAAI,SAAS,MAAM,QAAQ;KAC9E,IAAI,CAAC,YAAY,UAAU,QAAQ,UAAU,UAAU,CAAC,OAAO,SAC7D,aAAa,CAAC,GAAG,UAAU,OAAO,CAAC,CAAC;KAEtC,YAAY;MAAE,QAAQ,UAAU;MAAM,OAAO,OAAO;KAAM,CAAC;KAC3D,WAAW,OAAO,OAAO;KACzB,UAAU,OAAO,UAAU;KAC3B,IAAI,CAAC,YAAY,CAAC,OAAO,SAAS;KAClC,IAAI,UAAU,SAAS,eAAe,MAAM,IAAI,MAAM,oCAAoC;IAC5F,SAAS;IACT,IAAI,CAAC,MAAM;IACX,WAAW,KAAK;IAChB,UAAU,CAAC;IACX,OACG,SAAS,CAAC,CACV,MAAM,WAAW;KAChB,IAAI,MAAM,eAAe,OAAO,KAAK;IACvC,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;GACnB,EAAA,CAAG,CAAC,CAAC,YAAY;IACf,IAAI,MAAM;KACR,UAAU,CAAC;KACX,WAAW,KAAK;IAClB;GACF,CAAC;EACH,GAAG,GAAG;EACN,aAAa;GACX,OAAO;GACP,OAAO,aAAa,KAAK;EAC3B;CACF,GAAG;EAAC;EAAW;EAAO;EAAU;EAAa;EAAY;EAAiB;CAAsB,CAAC;CACjG,+BAAA,gBAAgB;EACd,SAAS,eAAe,yBAAyB,UAAU,CAAC,EAAE,iBAAiB,EAAE,OAAO,UAAU,CAAC;CACrG,GAAG,CAAC,UAAU,kBAAkB,IAAI,CAAC;CACrC,MAAM,mBAAmB;EACvB,+BAAK,2BAA2B,WAAW,+BAAA,gBAAgB,CAAC,CAAC,CAC1D,MAAM,CAAC,CACP,WAAW;GACV,aAAa,CAAC,CAAC;GACf,kBAAkB;EACpB,CAAC,CAAC,CACD,YAAY,SAAS,oDAAoD,CAAC;CAC/E;CACA,MAAM,QAAQ,aAA6B;EACzC,IAAI,SAAS,MAAM,WAAW;EAC9B,QAAa,QAAQ,CAAC,CACnB,WAAW,gCAAA,mBAAmB,QAA0B,CAAC,CAAC,CAC1D,WAAW;GACV,IAAI,SAAS,MAAM,WAAW,MAAM;EACtC,CAAC,CAAC,CACD,YAAY,SAAS,mEAAmE,CAAC;CAC9F;CACA,OACE,oCAAA,IAAC,OAAD;EACE,WAAU;EACV,kCAA+B;EAC/B,UAAU,UAAU,MAAM,gBAAgB;EAC1C,cAAc,UAAU;GACtB,MAAM,gBAAgB;GACtB,IAAI,MAAM,WAAW,MAAM,eAAe,MAAM;EAClD;YAEA,oCAAA,KAAC,OAAD;GACE,KAAK;GACL,MAAK;GACL,cAAW;GACX,mBAAgB;GAChB,WAAU;GACV,mBAAmB,UAAU;IAC3B,IAAI,MAAM,QAAQ,UAAU,mBAAmB,UAAU,gBAAgB,QAAQ,OAAO;GAC1F;GACA,YAAY,UAAU;IACpB,IAAI,MAAM,QAAQ,YAAY,CAAC,mBAAmB,SAAS;KACzD,MAAM,gBAAgB;KACtB,MAAM;IACR;IACA,IAAI,MAAM,QAAQ,OAAO;KACvB,MAAM,WAAW,MAAM,KACrB,OAAO,SAAS,iBAA8B,sCAAsC,KAAK,CAAC,CAC5F;KACA,MAAM,QAAQ,SAAS,QAAQ,SAAS,aAA4B;KACpE,IAAI,MAAM,YAAY,SAAS,GAAG;MAChC,MAAM,eAAe;MACrB,SAAS,GAAG,EAAE,CAAC,EAAE,MAAM;KACzB,OAAO,IAAI,CAAC,MAAM,YAAY,UAAU,SAAS,SAAS,GAAG;MAC3D,MAAM,eAAe;MACrB,SAAS,EAAE,EAAE,MAAM;KACrB;IACF;GACF;aA3BF;IA6BE,oCAAA,KAAC,OAAD;KAAK,WAAU;eAAf,CACE,oCAAA,KAAC,UAAD;MAAQ,IAAG;MAA8B,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAU,KAAK;MAAE;gBAAhG,CACE,oCAAA,IAAC,gBAAA,QAAD;OAAQ,UAAS;OAAS,MAAM;MAAK,CAAA,GAAC,kBAEhC;SACR,oCAAA,KAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,KAAK;MAAE;gBAAtC,CACE,oCAAA,IAAC,uBAAA,eAAD;OAAe,OAAM;iBACnB,oCAAA,IAAC,UAAD;QAAQ,MAAK;QAAS,cAAW;QAA6B,eAAe,kBAAkB;kBAC7F,oCAAA,IAAC,gBAAA,QAAD,EAAQ,UAAS,UAAW,CAAA;OACtB,CAAA;MACK,CAAA,GACf,oCAAA,IAAC,UAAD;OAAQ,MAAK;OAAS,cAAW;OAAwB,SAAS;iBAChE,oCAAA,IAAC,gBAAA,QAAD,EAAQ,UAAS,QAAS,CAAA;MACpB,CAAA,CACL;OACF;;IACL,oCAAA,IAAC,SAAD;KACE,KAAK;KACL,cAAW;KACX,iBAAc;KACd,aAAY;KACZ,WAAW;KACX,OAAO;KACP,WAAW,UAAU;MACnB,SAAS,MAAM,OAAO,KAAK;MAC3B,gBAAgB,KAAA,CAAS;KAC3B;KACA,YAAY,UAAU;MACpB,IAAI,MAAM,QAAQ,eAAe,MAAM,QAAQ,WAAW;OACxD,MAAM,eAAe;OAIrB,gBAAgB,QAHH,QAAQ,UAChB,YAAY,MAAM,QAAQ,cAAc,IAAI,MAAM,QAAQ,UAAU,QAAQ,SAC7E,EACyB,EAAE,IAAI;MACrC;MACA,IAAI,MAAM,QAAQ,WAAW,CAAC,MAAM,YAAY,eAAe,QAAQ,WAAW;OAChF,MAAM,eAAe;OACrB,KAAK,QAAQ,SAAS;MACxB;KACF;IACD,CAAA;IACD,oCAAA,KAAC,OAAD;KAAK,WAAU;eAAf,CACE,oCAAA,KAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,oCAAA,IAAC,QAAD,EAAA,UAAM,OAAU,CAAA,GAChB,oCAAA,IAAC,oBAAD;OACE,cAAW;OACX,kBAAkB,gBAAgB,QAAQ,IAAI,MAAM;OACpD,mBAAmB,gBAAgB,QAAQ,OAAO,MAAM;OACxD,OAAO;OACP,SAAS,CACP;QAAE,OAAO;QAAI,OAAO;OAAY,GAChC,GAAG,CAAC,GAAI,SAAS,SAAS,CAAC,CAAE,CAAC,CAC3B,MACE,OAAO,WACN,sCAAA,qBAAqB,KAAK,IAAI,sCAAA,qBAAqB,MAAM,KAAK,MAAM,cAAc,QAAQ,IAAI,CAClG,CAAC,CACA,KAAK,UAAU;QAAE,OAAO;QAAM,OAAO;OAAK,EAAE,CACjD;OACA,WAAW,WAAW;QACpB,cAAc,QAAQ,SAAS,EAAE;QACjC,gBAAgB,KAAA,CAAS;OAC3B;MACD,CAAA,CACI;SACP,oCAAA,KAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,oCAAA,IAAC,QAAD,EAAA,UAAM,YAAe,CAAA,GACrB,oCAAA,IAAC,oBAAD;OACE,cAAW;OACX,kBAAkB,gBAAgB,QAAQ,IAAI,WAAW;OACzD,mBAAmB,gBAAgB,QAAQ,OAAO,WAAW;OAC7D,OAAO;OACP,SAAS;QACP;SAAE,OAAO;SAAI,OAAO;QAAiB;QACrC;SAAE,OAAO;SAAa,OAAO;QAAiB;QAC9C,IAAI,SAAS,cAAc,CAAC,EAAA,CAAG,KAAK,eAAe;SAAE,OAAO;SAAW,OAAO;QAAU,EAAE;OAC5F;OACA,WAAW,WAAW;QACpB,mBAAmB,QAAQ,SAAS,EAAE;QACtC,gBAAgB,KAAA,CAAS;OAC3B;MACD,CAAA,CACI;OACJ;;IACL,oCAAA,KAAC,KAAD;KAAG,MAAK;eAAR;MACG,SAAS,WACN,YAAY,SAAS,QAAQ,GAAG,SAAS,WAAW,UACpD,SAAS,cACP,WAAW,IAAI,KAAK,SAAS,WAAW,CAAC,CAAC,mBAAmB,CAAC,GAAG;OAAE,MAAM;OAAW,QAAQ;MAAU,CAAC,MACvG;MACL,gBAAgB,KAAA,KAAa,MAAM,YAAY,eAAe,EAAE;MAChE,SAAS,SAAS,KAAK,MAAM,SAAS,OAAO;MAC7C,SAAS,cAAc,SAAS,cAAc,KAC7C,MAAM,SAAS,cAAc,SAAS,YAAY;MACnD,SAAS,KAAK;KACd;;KACD,SAAS,WAAW,SAAS,SAAS,KAAK,SAAS,cAAc,SAAS,cAAc,MACzF,oCAAA,KAAC,WAAD,EAAA,UAAA,CACE,oCAAA,IAAC,WAAD,EAAA,UAAS,qBAA2B,CAAA,GACpC,oCAAA,IAAC,KAAD,EAAA,UACG,SAAS,WAAW,mFACpB,CAAA,CACI,EAAA,CAAA;IAEV,SAAS,eACR,oCAAA,IAAC,OAAD;KAAK,MAAK;eACR,oCAAA,IAAC,KAAD,EAAA,UAAG,gFAAgF,CAAA;IAChF,CAAA;IAEN,SAAS,oCAAA,IAAC,KAAD;KAAG,MAAK;eAAS;IAAS,CAAA;IACpC,oCAAA,KAAC,OAAD;KACE,IAAG;KACH,MAAK;KACL,cAAW;KACX,WAAU;eAJZ,CAMG,QAAQ,KAAK,UAAU,UACtB,oCAAA,KAAC,OAAD;MAEE,IAAI,yBAAyB;MAC7B,MAAK;MACL,WAAW,6BAA6B,aAAa,QAAQ,aAAa;gBAJ5E,CAME,oCAAA,KAAC,UAAD;OAAQ,WAAU;OAA6B,MAAK;OAAS,eAAe,KAAK,QAAQ;iBAAzF,CACE,oCAAA,IAAC,gBAAA,QAAD,EAAQ,UAAU,MAAM,IAAI,SAAS,IAAI,KAAK,aAAe,CAAA,GAC7D,oCAAA,KAAC,QAAD,EAAA,UAAA;QACE,oCAAA,IAAC,UAAD,EAAA,UAAS,SAAS,KAAa,CAAA;QAC/B,oCAAA,KAAC,SAAD,EAAA,UAAA;SACG,SAAS;SAAK;SAAI,SAAS,aAAa;QACpC,EAAA,CAAA;QACN,YAAY,UAAU,QAAQ,KAAK,oCAAA,IAAC,SAAD,EAAA,UAAO,aAAiB,CAAA;QAC3D,YAAY,YAAY,QAAQ,KAAK,oCAAA,IAAC,SAAD,EAAA,UAAO,eAAmB,CAAA;QAChE,oCAAA,IAAC,eAAD;SAAe,OAAO,YAAY,QAAQ,UAAU,KAAK;SAAU;QAAQ,CAAA;OACvE,EAAA,CAAA,CACA;UACR,oCAAA,IAAC,kCAAA,yBAAD;OACE,WAAU;OACV,cAAY,gBAAgB,SAAS,KAAK,GAAG,SAAS;OACtD,OAAM;OACN,eAAe;QACb,IAAI,SAAS,MAAM,WAAW;QAC9B,MAAM;QACN,8BAAA,yBAAyB;SACvB;SACA,OAAO,GAAG,SAAS,KAAK,IAAI,SAAS;SACrC,SAAS,6CAA8C,SAA4B,SAAS,OAAO,aAAc,SAA4B,QAAQ,cAAe,SAA4B,SAAS,UAAU,SAAS,KAAK,eAAe,SAAS,aAAa,iBAAiB,UAAU,SAAS,KAAK,SAAU,SAA4B,OAAO,UAAU,UAAU,SAAS,KAAK;SAC9X,UAAU;UAAE,MAAM,SAAS;UAAM,MAAM,SAAS;UAAM,WAAW,SAAS;SAAU;QACtF,CAAC;OACH;iBAEA,oCAAA,IAAC,gBAAA,QAAD,EAAQ,UAAS,eAAgB,CAAA;MACV,CAAA,CACtB;QAlCE,SAAS,IAkCX,CACN,GACA,CAAC,QAAQ,UAAU,oCAAA,IAAC,KAAD,EAAA,UAAI,UAAU,eAAe,mDAAsD,CAAA,CACpG;;KACH,WAAY,WAAW,aACvB,oCAAA,IAAC,UAAD;KACE,MAAK;KACL,UAAU;KACV,aAAW;KACX,eAAe;MACb,WAAW,IAAI;MACf,UAAU,SAAS;MACnB,gBAAgB,UAAU,QAAQ,CAAC;KACrC;eAEC,UAAU,oBAAoB,SAAS,OAAO,KAAK,SAAS,UAAU;IACjE,CAAA;IAEV,oCAAA,KAAC,OAAD;KAAK,WAAU;eAAf,CACE,oCAAA,IAAC,KAAD;MAAG,WAAU;gBAAkC;KAAuC,CAAA,GACtF,oCAAA,IAAC,uBAAA,eAAD;MAAe,OAAM;gBACnB,oCAAA,IAAC,UAAD;OACE,MAAK;OACL,WAAU;OACV,cAAW;OACX,SAAS;iBAET,oCAAA,IAAC,gBAAA,QAAD;QAAQ,UAAS;QAAe,MAAM;OAAK,CAAA;MACrC,CAAA;KACK,CAAA,CACZ;;GACF;;CACF,CAAA;AAET;AAEA,IAAI,kBAAkC;CACpC,SAAS;CACT,YAAY;CACZ,QAAQ;CACR,aAAa;CACb,aAAa;CACb,UAAU;AACZ;AACA,IAAI,0BAA0B,CAAC;AAE/B,SAAgB,sBAAkC;CAChD,MAAM,cAAc,qCAAA,2BAA2B,oBAAgD;CAC/F,IAAI,OAAO,YAAY,wBAAwB,2BAA2B,IAAI,KAAA;CAC9E,MAAM,UAAU,8BAAA,eACR,YAAY,wBACjB,YAAY;EACX,OAAO;EACP,OAAO,UAAU,2BAA2B,IAAI,KAAA;CAClD,CACF;CACA,aAAa;EACX,QAAQ;EACR,OAAO;EACP,OAAO,KAAA;CACT;AACF;AAEA,SAAS,6BAAyC;CAChD,IAAI;CACJ,MAAM,aAAa;EACjB,WAAW,MAAM;EACjB,gCAAA,4BAA4B;EAC5B,MAAM,YAAY,SAAS;EAC3B,IAAI,CAAC,aAAa,OAAO,QAAQ,QAAQ;EACzC,MAAM,QAAQ,mBAAA,SAAS,QAAQ,cAAc,IAAI,CAAC,EAAE,QAAQ;EAC5D,IAAI,SAAS,UAAU,aAAa;GAClC,kBAAkB;IAChB,GAAG;IACH,UAAU;IACV,SAAS;GACX;GACA,OAAO,cAAc,IAAI,MAAM,uBAAuB,CAAC;GACvD;EACF;EACA,MAAM,aAAa,IAAI,gBAAgB;EACvC,YAAY;EACZ,MAAM,SAAS,+BAAA,2BAA2B,WAAW,+BAAA,gBAAgB,CAAC;EACtE,MAAM,aAAa,IAAI,IAAI,UAAU,CAAC,CAAC,KAAK,CAAC,MAAM,WAAW,CAAC,MAAgB,KAAK,CAAC,CAAC;EACtF,MAAM,8BAAc,IAAI,IAAkC;EAC1D,MAAM,OAAO;GACX,GAAG;GACH,SAAS,OAAe,QAAgB,cACtC,OAAO,OACL,OACA,QACA,UAAU,KAAK,SAAS;IACtB,MAAM,QAAQ,WAAW,IAAI,KAAK,IAAI;IACtC,IAAI,CAAC,OAAO,OAAO;IACnB,IAAI,UAAU,YAAY,IAAI,KAAK,IAAI;IACvC,IAAI,CAAC,SAAS;KACZ,UAAU,IAAI,IAAI,MAAM,MAAM,KAAK,WAAW,CAAC,OAAO,UAAU,MAAM,CAAU,CAAC;KACjF,YAAY,IAAI,KAAK,MAAM,OAAO;IACpC;IACA,MAAM,SAAS,QAAQ,IAAI,KAAK,IAAI;IACpC,OAAO,SAAS;KAAE,GAAG;KAAM,QAAQ,+BAAA,oBAAoB,KAAK,MAAM,MAAM;IAAE,IAAI;GAChF,CAAC,CACH;EACJ;EACA,IAAI,cAAc;EAelB,6BAda,yBAAyB;GACpC,WAAW,+BAAA,8BAA8B,WAAW,WAAW,MAAM;GACrE;GACA,QAAQ,WAAW;GACnB,kBAAkB,mBAAA,SAAS,QAAQ,cAAc,IAAI,CAAC,EAAE,MAAM,wBAAwB,CAAC;GACvF,WAAW,WAAW;IACpB,IAAI,CAAC,WAAW,OAAO,SAAS;KAC9B,IAAI,OAAO,YAAY,CAAC,aAAa,YAAY,MAAM;KACvD,cAAc,OAAO;KACrB,kBAAkB;KAClB,OAAO,cAAc,IAAI,MAAM,uBAAuB,CAAC;IACzD;GACF;EACF,CACK,CAAA,CAAK,IAAI,CAAC,CAAC,YAAY;GAC1B,IAAI,CAAC,WAAW,OAAO,SAAS;IAC9B,kBAAkB;KAAE,GAAG;KAAiB,UAAU;KAAO,aAAa,gBAAgB,cAAc;IAAE;IACtG,OAAO,cAAc,IAAI,MAAM,uBAAuB,CAAC;GACzD;EACF,CAAC;CACH;CACA,oBAAoB;CACpB,KAAK;CACL,MAAM,YAAY,SAAS,cAAc,KAAK;CAC9C,MAAM,QAAQ,SAAS,cAAc,OAAO;CAC5C,MAAM,cAAc;CACpB,SAAS,KAAK,OAAO,KAAK;CAC1B,SAAS,KAAK,OAAO,SAAS;CAC9B,IAAI,UAAU;CACd,MAAM,cAAc;EAClB,IAAI,SAAS;GACX,UAAU;GACV,mCAAA,QAAS,uBAAuB,SAAS;EAC3C;CACF;CACA,MAAM,aAAa;EACjB,MAAM,YAAY,SAAS;EAC3B,IACE,OAAO,QAAQ,UACf,CAAC,aACD,WACA,SAAS,cAAc,0CAAwC,GAE/D;EACF,UAAU;EACV,mCAAA,QAAS,OAAO,oCAAA,IAAC,cAAD;GAAqB;GAAkB;EAAY,CAAA,GAAG,SAAS;CACjF;CACA,IAAI,QAAQ,8BAAA,kBAAkB;CAC9B,MAAM,mBAAmB;EACvB,QAAQ,8BAAA,kBAAkB;CAC5B;CACA,MAAM,OAAO,UAAyB;EACpC,IAAI,MAAM,eAAe,MAAM,QAAQ,WAAW;GAChD,WAAW;GACX;EACF;EACA,IAAI,MAAM,OAAO,YAAY,IAAI,CAAC,GAAG,KAAK;CAC5C;CACA,OAAO,iBAAiB,QAAQ,UAAU;CAC1C,OAAO,iBAAiB,WAAW,GAAG;CACtC,OAAO,iBAAiB,SAAS,GAAG;CACpC,OAAO,iBAAiB,MAAM,IAAI;CAClC,MAAM,UAAU,8BAAA,eACR,GAAG,+BAAA,gBAAgB,EAAE,GAAG,mBAAA,SAAS,QAAQ,cAAc,IAAI,CAAC,EAAE,QAAQ,eACtE;EACJ,MAAM;EACN,KAAK;CACP,CACF;CACA,aAAa;EACX,MAAM;EACN,WAAW,MAAM;EACjB,gCAAA,4BAA4B;EAC5B,0BAA0B,CAAC;EAC3B,QAAQ;EACR,OAAO,oBAAoB,QAAQ,UAAU;EAC7C,OAAO,oBAAoB,WAAW,GAAG;EACzC,OAAO,oBAAoB,SAAS,GAAG;EACvC,OAAO,oBAAoB,MAAM,IAAI;EACrC,UAAU,OAAO;EACjB,MAAM,OAAO;CACf;AACF"}
@@ -40,10 +40,11 @@ function attachment(route, gateway, listener, inventory) {
40
40
  return false;
41
41
  }) ? "allowedRoutes namespace selector matches" : "allowedRoutes namespace selector rejects";
42
42
  }
43
- function gatewayRelationships(service, inventory) {
43
+ function gatewayRelationships(service, inventory, showAll = false) {
44
44
  const nodes = [];
45
45
  const edges = [];
46
46
  let omitted = 0;
47
+ const limit = showAll ? Infinity : 5;
47
48
  const add = (object, suffix = "", detail = "", evidence) => {
48
49
  const id = `gateway-api/${object.kind}/${object.metadata.namespace ?? ""}/${object.metadata.name}${suffix}`;
49
50
  if (!nodes.some((node) => node.id === id)) nodes.push({
@@ -65,7 +66,7 @@ function gatewayRelationships(service, inventory) {
65
66
  "TLSRoute",
66
67
  "UDPRoute"
67
68
  ].includes(object.kind) && object.spec?.rules?.some((rule) => rule.backendRefs?.some((backend) => (backend.group ?? "") === "" && (backend.kind ?? "Service") === "Service" && backend.name === service.metadata.name && (backend.namespace ?? object.metadata.namespace) === service.metadata.namespace)));
68
- for (const route of routes.slice(0, 5)) {
69
+ for (const route of routes.slice(0, limit)) {
69
70
  const backends = route.spec?.rules?.flatMap((rule) => rule.backendRefs ?? []).filter((backend) => (backend.group ?? "") === "" && (backend.kind ?? "Service") === "Service" && backend.name === service.metadata.name && (backend.namespace ?? route.metadata.namespace) === service.metadata.namespace) ?? [];
70
71
  const summaries = route.spec?.rules?.flatMap((rule) => rule.matches?.map((match) => match.path?.value ?? [match.method?.service, match.method?.method].filter(Boolean).join("/")) ?? []) ?? [];
71
72
  const routeId = add(route, "", [
@@ -79,14 +80,14 @@ function gatewayRelationships(service, inventory) {
79
80
  label: "backend reference (not reachability)"
80
81
  });
81
82
  const parents = (route.spec?.parentRefs ?? []).filter((parent) => (parent.group ?? GROUP) === GROUP && (parent.kind ?? "Gateway") === "Gateway");
82
- omitted += Math.max(0, parents.length - 5);
83
- for (const parent of parents.slice(0, 5)) {
83
+ omitted += Math.max(0, parents.length - limit);
84
+ for (const parent of parents.slice(0, limit)) {
84
85
  const gateway = inventory.objects.find((object) => object.kind === "Gateway" && object.metadata.name === parent.name && object.metadata.namespace === (parent.namespace ?? route.metadata.namespace));
85
86
  if (!gateway) continue;
86
87
  const status = route.status?.parents?.filter((item) => (item.parentRef.group ?? GROUP) === GROUP && (item.parentRef.kind ?? "Gateway") === "Gateway" && item.parentRef.name === parent.name && (item.parentRef.namespace ?? route.metadata.namespace) === (parent.namespace ?? route.metadata.namespace) && item.parentRef.sectionName === parent.sectionName && item.parentRef.port === parent.port);
87
88
  const listeners = (gateway.spec?.listeners ?? []).filter((item) => (!parent.sectionName || parent.sectionName === item.name) && (!parent.port || parent.port === item.port));
88
- omitted += Math.max(0, listeners.length - 5);
89
- for (const listener of listeners.slice(0, 5)) {
89
+ omitted += Math.max(0, listeners.length - limit);
90
+ for (const listener of listeners.slice(0, limit)) {
90
91
  const listenerStatus = gateway.status?.listeners?.find((item) => item.name === listener.name);
91
92
  const id = add(gateway, `/${listener.name}`, `${listener.protocol}:${listener.port}`, [`Listener ${conditions(listenerStatus?.conditions, gateway.metadata.generation, [
92
93
  "Accepted",
@@ -113,7 +114,7 @@ function gatewayRelationships(service, inventory) {
113
114
  return {
114
115
  nodes,
115
116
  edges,
116
- omitted: omitted + Math.max(0, routes.length - 5)
117
+ omitted: omitted + Math.max(0, routes.length - limit)
117
118
  };
118
119
  }
119
120
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-relationships.js","names":[],"sources":["../../../../src/common/gateway-relationships.ts"],"sourcesContent":["import type { RelationshipEdge, RelationshipNode } from \"./resource-relationships\";\n\nconst GROUP = \"gateway.networking.k8s.io\";\ninterface Condition {\n type: string;\n status: string;\n reason?: string;\n observedGeneration?: number;\n}\ninterface Reference {\n name: string;\n namespace?: string;\n group?: string;\n kind?: string;\n sectionName?: string;\n port?: number;\n weight?: number;\n}\ninterface Listener {\n name: string;\n port: number;\n protocol: string;\n allowedRoutes?: {\n kinds?: { group?: string; kind: string }[];\n namespaces?: {\n from?: string;\n selector?: {\n matchLabels?: Record<string, string>;\n matchExpressions?: { key: string; operator: string; values?: string[] }[];\n };\n };\n };\n}\nexport interface GatewayObject {\n apiVersion?: string;\n kind: string;\n selfLink?: string;\n metadata: { name?: string; namespace?: string; uid?: string; generation?: number; labels?: Record<string, string> };\n spec?: {\n gatewayClassName?: string;\n listeners?: Listener[];\n parentRefs?: Reference[];\n hostnames?: string[];\n rules?: {\n backendRefs?: Reference[];\n matches?: { path?: { value?: string }; method?: { service?: string; method?: string } }[];\n }[];\n from?: { group: string; kind: string; namespace: string }[];\n to?: { group: string; kind: string; name?: string }[];\n };\n status?: {\n conditions?: Condition[];\n listeners?: { name: string; conditions?: Condition[] }[];\n parents?: { parentRef: Reference; controllerName?: string; conditions?: Condition[] }[];\n };\n}\nexport interface GatewayInventory {\n objects: GatewayObject[];\n grants?: GatewayObject[];\n grantNamespaces?: string[];\n namespaces?: GatewayObject[];\n gaps: string[];\n}\nfunction conditions(items: Condition[] | undefined, generation?: number, types = [\"Accepted\", \"ResolvedRefs\"]) {\n return types\n .map((type) => {\n const condition = items?.find((item) => item.type === type);\n const stale = generation !== undefined && condition?.observedGeneration !== generation;\n return `${type}: ${condition ? `${condition.status}${condition.reason ? ` (${condition.reason})` : \"\"}${stale ? \" · stale/unknown generation\" : \"\"}` : \"unknown\"}`;\n })\n .join(\" · \");\n}\nfunction grantEvidence(route: GatewayObject, backend: Reference, inventory: GatewayInventory) {\n const namespace = backend.namespace ?? route.metadata.namespace;\n if (namespace === route.metadata.namespace) return \"same-namespace backend\";\n if (!inventory.grants || (inventory.grantNamespaces && !inventory.grantNamespaces.includes(namespace ?? \"\")))\n return \"cross-namespace ReferenceGrant unknown\";\n const granted = inventory.grants.some(\n (grant) =>\n grant.metadata.namespace === namespace &&\n grant.spec?.from?.some(\n (from) => from.group === GROUP && from.kind === route.kind && from.namespace === route.metadata.namespace,\n ) &&\n grant.spec?.to?.some((to) => to.group === \"\" && to.kind === \"Service\" && (!to.name || to.name === backend.name)),\n );\n return granted\n ? \"cross-namespace ReferenceGrant permits backend\"\n : \"cross-namespace backend not permitted by observed ReferenceGrants\";\n}\nfunction attachment(route: GatewayObject, gateway: GatewayObject, listener: Listener, inventory: GatewayInventory) {\n const kinds = listener.allowedRoutes?.kinds;\n if (kinds && !kinds.some((kind) => (kind.group ?? GROUP) === GROUP && kind.kind === route.kind))\n return \"listener rejects route kind\";\n if (!kinds) {\n const supported: Record<string, string[]> = {\n HTTP: [\"HTTPRoute\", \"GRPCRoute\"],\n HTTPS: [\"HTTPRoute\", \"GRPCRoute\"],\n TCP: [\"TCPRoute\"],\n TLS: [\"TLSRoute\"],\n UDP: [\"UDPRoute\"],\n };\n if (!supported[listener.protocol]?.includes(route.kind))\n return \"listener rejects route kind or protocol support unknown\";\n }\n const ns = listener.allowedRoutes?.namespaces;\n if (!ns || !ns.from || ns.from === \"Same\")\n return route.metadata.namespace === gateway.metadata.namespace\n ? \"allowedRoutes permits namespace\"\n : \"allowedRoutes rejects namespace\";\n if (ns.from === \"All\") return \"allowedRoutes permits all namespaces\";\n if (ns.from !== \"Selector\") return \"allowedRoutes namespace unknown\";\n const namespace = inventory.namespaces?.find((item) => item.metadata.name === route.metadata.namespace);\n if (!namespace) return \"allowedRoutes namespace selector unknown\";\n const labels = namespace.metadata.labels ?? {};\n const selected =\n Object.entries(ns.selector?.matchLabels ?? {}).every(([key, value]) => labels[key] === value) &&\n (ns.selector?.matchExpressions ?? []).every(({ key, operator, values = [] }) => {\n if (operator === \"In\") return labels[key] !== undefined && values.includes(labels[key]);\n if (operator === \"NotIn\") return labels[key] === undefined || !values.includes(labels[key]);\n if (operator === \"Exists\") return labels[key] !== undefined;\n if (operator === \"DoesNotExist\") return labels[key] === undefined;\n return false;\n });\n return selected ? \"allowedRoutes namespace selector matches\" : \"allowedRoutes namespace selector rejects\";\n}\nexport function gatewayRelationships(\n service: { metadata: { name?: string; namespace?: string } },\n inventory: GatewayInventory,\n) {\n const nodes: RelationshipNode[] = [];\n const edges: RelationshipEdge[] = [];\n let omitted = 0;\n const add = (object: GatewayObject, suffix = \"\", detail = \"\", evidence?: string[]) => {\n const id = `gateway-api/${object.kind}/${object.metadata.namespace ?? \"\"}/${object.metadata.name}${suffix}`;\n if (!nodes.some((node) => node.id === id))\n nodes.push({\n id,\n kind: object.kind,\n name: `${object.metadata.name}${suffix}`,\n role: \"upstream\",\n tone: \"neutral\",\n detail,\n evidence,\n selfLink: object.selfLink,\n });\n return id;\n };\n const routes = inventory.objects.filter(\n (object) =>\n [\"HTTPRoute\", \"GRPCRoute\", \"TCPRoute\", \"TLSRoute\", \"UDPRoute\"].includes(object.kind) &&\n object.spec?.rules?.some((rule) =>\n rule.backendRefs?.some(\n (backend) =>\n (backend.group ?? \"\") === \"\" &&\n (backend.kind ?? \"Service\") === \"Service\" &&\n backend.name === service.metadata.name &&\n (backend.namespace ?? object.metadata.namespace) === service.metadata.namespace,\n ),\n ),\n );\n for (const route of routes.slice(0, 5)) {\n const backends =\n route.spec?.rules\n ?.flatMap((rule) => rule.backendRefs ?? [])\n .filter(\n (backend) =>\n (backend.group ?? \"\") === \"\" &&\n (backend.kind ?? \"Service\") === \"Service\" &&\n backend.name === service.metadata.name &&\n (backend.namespace ?? route.metadata.namespace) === service.metadata.namespace,\n ) ?? [];\n const summaries =\n route.spec?.rules?.flatMap(\n (rule) =>\n rule.matches?.map(\n (match) => match.path?.value ?? [match.method?.service, match.method?.method].filter(Boolean).join(\"/\"),\n ) ?? [],\n ) ?? [];\n const routeId = add(\n route,\n \"\",\n [\n ...(route.spec?.hostnames ?? []),\n ...summaries,\n ...backends.map((backend) => `weight ${backend.weight ?? 1} · ${grantEvidence(route, backend, inventory)}`),\n ].join(\" · \"),\n );\n edges.push({ from: routeId, to: \"service\", label: \"backend reference (not reachability)\" });\n const parents = (route.spec?.parentRefs ?? []).filter(\n (parent) => (parent.group ?? GROUP) === GROUP && (parent.kind ?? \"Gateway\") === \"Gateway\",\n );\n omitted += Math.max(0, parents.length - 5);\n for (const parent of parents.slice(0, 5)) {\n const gateway = inventory.objects.find(\n (object) =>\n object.kind === \"Gateway\" &&\n object.metadata.name === parent.name &&\n object.metadata.namespace === (parent.namespace ?? route.metadata.namespace),\n );\n if (!gateway) continue;\n const status = route.status?.parents?.filter(\n (item) =>\n (item.parentRef.group ?? GROUP) === GROUP &&\n (item.parentRef.kind ?? \"Gateway\") === \"Gateway\" &&\n item.parentRef.name === parent.name &&\n (item.parentRef.namespace ?? route.metadata.namespace) === (parent.namespace ?? route.metadata.namespace) &&\n item.parentRef.sectionName === parent.sectionName &&\n item.parentRef.port === parent.port,\n );\n const listeners = (gateway.spec?.listeners ?? []).filter(\n (item) =>\n (!parent.sectionName || parent.sectionName === item.name) && (!parent.port || parent.port === item.port),\n );\n omitted += Math.max(0, listeners.length - 5);\n for (const listener of listeners.slice(0, 5)) {\n const listenerStatus = gateway.status?.listeners?.find((item) => item.name === listener.name);\n const id = add(gateway, `/${listener.name}`, `${listener.protocol}:${listener.port}`, [\n `Listener ${conditions(listenerStatus?.conditions, gateway.metadata.generation, [\"Accepted\", \"ResolvedRefs\", \"Programmed\"])}`,\n `Gateway ${conditions(gateway.status?.conditions, gateway.metadata.generation, [\"Accepted\", \"Programmed\"])}`,\n ]);\n edges.push({\n from: id,\n to: routeId,\n label: \"parent reference\",\n evidence: `${attachment(route, gateway, listener, inventory)} · ${status?.length ? status.map((item) => `${item.controllerName ?? \"unknown controller\"}: ${conditions(item.conditions, route.metadata.generation)}`).join(\" · \") : \"parent reference · acceptance unknown\"}`,\n });\n const gatewayClass = inventory.objects.find(\n (object) => object.kind === \"GatewayClass\" && object.metadata.name === gateway.spec?.gatewayClassName,\n );\n if (gatewayClass)\n edges.push({\n from: add(\n gatewayClass,\n \"\",\n conditions(gatewayClass.status?.conditions, gatewayClass.metadata.generation, [\"Accepted\"]),\n ),\n to: id,\n label: \"class reference\",\n });\n }\n }\n }\n const order = (kind: string) => (kind === \"GatewayClass\" ? 0 : kind === \"Gateway\" ? 1 : 2);\n nodes.sort((a, b) => order(a.kind) - order(b.kind));\n return { nodes, edges, omitted: omitted + Math.max(0, routes.length - 5) };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gateway-relationships.js","names":[],"sources":["../../../../src/common/gateway-relationships.ts"],"sourcesContent":["import type { RelationshipEdge, RelationshipNode } from \"./resource-relationships\";\n\nconst GROUP = \"gateway.networking.k8s.io\";\ninterface Condition {\n type: string;\n status: string;\n reason?: string;\n observedGeneration?: number;\n}\ninterface Reference {\n name: string;\n namespace?: string;\n group?: string;\n kind?: string;\n sectionName?: string;\n port?: number;\n weight?: number;\n}\ninterface Listener {\n name: string;\n port: number;\n protocol: string;\n allowedRoutes?: {\n kinds?: { group?: string; kind: string }[];\n namespaces?: {\n from?: string;\n selector?: {\n matchLabels?: Record<string, string>;\n matchExpressions?: { key: string; operator: string; values?: string[] }[];\n };\n };\n };\n}\nexport interface GatewayObject {\n apiVersion?: string;\n kind: string;\n selfLink?: string;\n metadata: { name?: string; namespace?: string; uid?: string; generation?: number; labels?: Record<string, string> };\n spec?: {\n gatewayClassName?: string;\n listeners?: Listener[];\n parentRefs?: Reference[];\n hostnames?: string[];\n rules?: {\n backendRefs?: Reference[];\n matches?: { path?: { value?: string }; method?: { service?: string; method?: string } }[];\n }[];\n from?: { group: string; kind: string; namespace: string }[];\n to?: { group: string; kind: string; name?: string }[];\n };\n status?: {\n conditions?: Condition[];\n listeners?: { name: string; conditions?: Condition[] }[];\n parents?: { parentRef: Reference; controllerName?: string; conditions?: Condition[] }[];\n };\n}\nexport interface GatewayInventory {\n objects: GatewayObject[];\n grants?: GatewayObject[];\n grantNamespaces?: string[];\n namespaces?: GatewayObject[];\n gaps: string[];\n}\nfunction conditions(items: Condition[] | undefined, generation?: number, types = [\"Accepted\", \"ResolvedRefs\"]) {\n return types\n .map((type) => {\n const condition = items?.find((item) => item.type === type);\n const stale = generation !== undefined && condition?.observedGeneration !== generation;\n return `${type}: ${condition ? `${condition.status}${condition.reason ? ` (${condition.reason})` : \"\"}${stale ? \" · stale/unknown generation\" : \"\"}` : \"unknown\"}`;\n })\n .join(\" · \");\n}\nfunction grantEvidence(route: GatewayObject, backend: Reference, inventory: GatewayInventory) {\n const namespace = backend.namespace ?? route.metadata.namespace;\n if (namespace === route.metadata.namespace) return \"same-namespace backend\";\n if (!inventory.grants || (inventory.grantNamespaces && !inventory.grantNamespaces.includes(namespace ?? \"\")))\n return \"cross-namespace ReferenceGrant unknown\";\n const granted = inventory.grants.some(\n (grant) =>\n grant.metadata.namespace === namespace &&\n grant.spec?.from?.some(\n (from) => from.group === GROUP && from.kind === route.kind && from.namespace === route.metadata.namespace,\n ) &&\n grant.spec?.to?.some((to) => to.group === \"\" && to.kind === \"Service\" && (!to.name || to.name === backend.name)),\n );\n return granted\n ? \"cross-namespace ReferenceGrant permits backend\"\n : \"cross-namespace backend not permitted by observed ReferenceGrants\";\n}\nfunction attachment(route: GatewayObject, gateway: GatewayObject, listener: Listener, inventory: GatewayInventory) {\n const kinds = listener.allowedRoutes?.kinds;\n if (kinds && !kinds.some((kind) => (kind.group ?? GROUP) === GROUP && kind.kind === route.kind))\n return \"listener rejects route kind\";\n if (!kinds) {\n const supported: Record<string, string[]> = {\n HTTP: [\"HTTPRoute\", \"GRPCRoute\"],\n HTTPS: [\"HTTPRoute\", \"GRPCRoute\"],\n TCP: [\"TCPRoute\"],\n TLS: [\"TLSRoute\"],\n UDP: [\"UDPRoute\"],\n };\n if (!supported[listener.protocol]?.includes(route.kind))\n return \"listener rejects route kind or protocol support unknown\";\n }\n const ns = listener.allowedRoutes?.namespaces;\n if (!ns || !ns.from || ns.from === \"Same\")\n return route.metadata.namespace === gateway.metadata.namespace\n ? \"allowedRoutes permits namespace\"\n : \"allowedRoutes rejects namespace\";\n if (ns.from === \"All\") return \"allowedRoutes permits all namespaces\";\n if (ns.from !== \"Selector\") return \"allowedRoutes namespace unknown\";\n const namespace = inventory.namespaces?.find((item) => item.metadata.name === route.metadata.namespace);\n if (!namespace) return \"allowedRoutes namespace selector unknown\";\n const labels = namespace.metadata.labels ?? {};\n const selected =\n Object.entries(ns.selector?.matchLabels ?? {}).every(([key, value]) => labels[key] === value) &&\n (ns.selector?.matchExpressions ?? []).every(({ key, operator, values = [] }) => {\n if (operator === \"In\") return labels[key] !== undefined && values.includes(labels[key]);\n if (operator === \"NotIn\") return labels[key] === undefined || !values.includes(labels[key]);\n if (operator === \"Exists\") return labels[key] !== undefined;\n if (operator === \"DoesNotExist\") return labels[key] === undefined;\n return false;\n });\n return selected ? \"allowedRoutes namespace selector matches\" : \"allowedRoutes namespace selector rejects\";\n}\nexport function gatewayRelationships(\n service: { metadata: { name?: string; namespace?: string } },\n inventory: GatewayInventory,\n showAll = false,\n) {\n const nodes: RelationshipNode[] = [];\n const edges: RelationshipEdge[] = [];\n let omitted = 0;\n const limit = showAll ? Infinity : 5;\n const add = (object: GatewayObject, suffix = \"\", detail = \"\", evidence?: string[]) => {\n const id = `gateway-api/${object.kind}/${object.metadata.namespace ?? \"\"}/${object.metadata.name}${suffix}`;\n if (!nodes.some((node) => node.id === id))\n nodes.push({\n id,\n kind: object.kind,\n name: `${object.metadata.name}${suffix}`,\n role: \"upstream\",\n tone: \"neutral\",\n detail,\n evidence,\n selfLink: object.selfLink,\n });\n return id;\n };\n const routes = inventory.objects.filter(\n (object) =>\n [\"HTTPRoute\", \"GRPCRoute\", \"TCPRoute\", \"TLSRoute\", \"UDPRoute\"].includes(object.kind) &&\n object.spec?.rules?.some((rule) =>\n rule.backendRefs?.some(\n (backend) =>\n (backend.group ?? \"\") === \"\" &&\n (backend.kind ?? \"Service\") === \"Service\" &&\n backend.name === service.metadata.name &&\n (backend.namespace ?? object.metadata.namespace) === service.metadata.namespace,\n ),\n ),\n );\n for (const route of routes.slice(0, limit)) {\n const backends =\n route.spec?.rules\n ?.flatMap((rule) => rule.backendRefs ?? [])\n .filter(\n (backend) =>\n (backend.group ?? \"\") === \"\" &&\n (backend.kind ?? \"Service\") === \"Service\" &&\n backend.name === service.metadata.name &&\n (backend.namespace ?? route.metadata.namespace) === service.metadata.namespace,\n ) ?? [];\n const summaries =\n route.spec?.rules?.flatMap(\n (rule) =>\n rule.matches?.map(\n (match) => match.path?.value ?? [match.method?.service, match.method?.method].filter(Boolean).join(\"/\"),\n ) ?? [],\n ) ?? [];\n const routeId = add(\n route,\n \"\",\n [\n ...(route.spec?.hostnames ?? []),\n ...summaries,\n ...backends.map((backend) => `weight ${backend.weight ?? 1} · ${grantEvidence(route, backend, inventory)}`),\n ].join(\" · \"),\n );\n edges.push({ from: routeId, to: \"service\", label: \"backend reference (not reachability)\" });\n const parents = (route.spec?.parentRefs ?? []).filter(\n (parent) => (parent.group ?? GROUP) === GROUP && (parent.kind ?? \"Gateway\") === \"Gateway\",\n );\n omitted += Math.max(0, parents.length - limit);\n for (const parent of parents.slice(0, limit)) {\n const gateway = inventory.objects.find(\n (object) =>\n object.kind === \"Gateway\" &&\n object.metadata.name === parent.name &&\n object.metadata.namespace === (parent.namespace ?? route.metadata.namespace),\n );\n if (!gateway) continue;\n const status = route.status?.parents?.filter(\n (item) =>\n (item.parentRef.group ?? GROUP) === GROUP &&\n (item.parentRef.kind ?? \"Gateway\") === \"Gateway\" &&\n item.parentRef.name === parent.name &&\n (item.parentRef.namespace ?? route.metadata.namespace) === (parent.namespace ?? route.metadata.namespace) &&\n item.parentRef.sectionName === parent.sectionName &&\n item.parentRef.port === parent.port,\n );\n const listeners = (gateway.spec?.listeners ?? []).filter(\n (item) =>\n (!parent.sectionName || parent.sectionName === item.name) && (!parent.port || parent.port === item.port),\n );\n omitted += Math.max(0, listeners.length - limit);\n for (const listener of listeners.slice(0, limit)) {\n const listenerStatus = gateway.status?.listeners?.find((item) => item.name === listener.name);\n const id = add(gateway, `/${listener.name}`, `${listener.protocol}:${listener.port}`, [\n `Listener ${conditions(listenerStatus?.conditions, gateway.metadata.generation, [\"Accepted\", \"ResolvedRefs\", \"Programmed\"])}`,\n `Gateway ${conditions(gateway.status?.conditions, gateway.metadata.generation, [\"Accepted\", \"Programmed\"])}`,\n ]);\n edges.push({\n from: id,\n to: routeId,\n label: \"parent reference\",\n evidence: `${attachment(route, gateway, listener, inventory)} · ${status?.length ? status.map((item) => `${item.controllerName ?? \"unknown controller\"}: ${conditions(item.conditions, route.metadata.generation)}`).join(\" · \") : \"parent reference · acceptance unknown\"}`,\n });\n const gatewayClass = inventory.objects.find(\n (object) => object.kind === \"GatewayClass\" && object.metadata.name === gateway.spec?.gatewayClassName,\n );\n if (gatewayClass)\n edges.push({\n from: add(\n gatewayClass,\n \"\",\n conditions(gatewayClass.status?.conditions, gatewayClass.metadata.generation, [\"Accepted\"]),\n ),\n to: id,\n label: \"class reference\",\n });\n }\n }\n }\n const order = (kind: string) => (kind === \"GatewayClass\" ? 0 : kind === \"Gateway\" ? 1 : 2);\n nodes.sort((a, b) => order(a.kind) - order(b.kind));\n return { nodes, edges, omitted: omitted + Math.max(0, routes.length - limit) };\n}\n"],"mappings":";AAEA,IAAM,QAAQ;AA6Dd,SAAS,WAAW,OAAgC,YAAqB,QAAQ,CAAC,YAAY,cAAc,GAAG;CAC7G,OAAO,MACJ,KAAK,SAAS;EACb,MAAM,YAAY,OAAO,MAAM,SAAS,KAAK,SAAS,IAAI;EAC1D,MAAM,QAAQ,eAAe,KAAA,KAAa,WAAW,uBAAuB;EAC5E,OAAO,GAAG,KAAK,IAAI,YAAY,GAAG,UAAU,SAAS,UAAU,SAAS,KAAK,UAAU,OAAO,KAAK,KAAK,QAAQ,gCAAgC,OAAO;CACzJ,CAAC,CAAC,CACD,KAAK,KAAK;AACf;AACA,SAAS,cAAc,OAAsB,SAAoB,WAA6B;CAC5F,MAAM,YAAY,QAAQ,aAAa,MAAM,SAAS;CACtD,IAAI,cAAc,MAAM,SAAS,WAAW,OAAO;CACnD,IAAI,CAAC,UAAU,UAAW,UAAU,mBAAmB,CAAC,UAAU,gBAAgB,SAAS,aAAa,EAAE,GACxG,OAAO;CAST,OARgB,UAAU,OAAO,MAC9B,UACC,MAAM,SAAS,cAAc,aAC7B,MAAM,MAAM,MAAM,MACf,SAAS,KAAK,UAAU,SAAS,KAAK,SAAS,MAAM,QAAQ,KAAK,cAAc,MAAM,SAAS,SAClG,KACA,MAAM,MAAM,IAAI,MAAM,OAAO,GAAG,UAAU,MAAM,GAAG,SAAS,cAAc,CAAC,GAAG,QAAQ,GAAG,SAAS,QAAQ,KAAK,CAE5G,IACH,mDACA;AACN;AACA,SAAS,WAAW,OAAsB,SAAwB,UAAoB,WAA6B;CACjH,MAAM,QAAQ,SAAS,eAAe;CACtC,IAAI,SAAS,CAAC,MAAM,MAAM,UAAU,KAAK,SAAS,WAAW,SAAS,KAAK,SAAS,MAAM,IAAI,GAC5F,OAAO;CACT,IAAI,CAAC;MAQC,CAAC;GANH,MAAM,CAAC,aAAa,WAAW;GAC/B,OAAO,CAAC,aAAa,WAAW;GAChC,KAAK,CAAC,UAAU;GAChB,KAAK,CAAC,UAAU;GAChB,KAAK,CAAC,UAAU;EAEb,EAAU,SAAS,SAAS,EAAE,SAAS,MAAM,IAAI,GACpD,OAAO;CAAA;CAEX,MAAM,KAAK,SAAS,eAAe;CACnC,IAAI,CAAC,MAAM,CAAC,GAAG,QAAQ,GAAG,SAAS,QACjC,OAAO,MAAM,SAAS,cAAc,QAAQ,SAAS,YACjD,oCACA;CACN,IAAI,GAAG,SAAS,OAAO,OAAO;CAC9B,IAAI,GAAG,SAAS,YAAY,OAAO;CACnC,MAAM,YAAY,UAAU,YAAY,MAAM,SAAS,KAAK,SAAS,SAAS,MAAM,SAAS,SAAS;CACtG,IAAI,CAAC,WAAW,OAAO;CACvB,MAAM,SAAS,UAAU,SAAS,UAAU,CAAC;CAU7C,OARE,OAAO,QAAQ,GAAG,UAAU,eAAe,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,WAAW,OAAO,SAAS,KAAK,MAC3F,GAAG,UAAU,oBAAoB,CAAC,EAAA,CAAG,OAAO,EAAE,KAAK,UAAU,SAAS,CAAC,QAAQ;EAC9E,IAAI,aAAa,MAAM,OAAO,OAAO,SAAS,KAAA,KAAa,OAAO,SAAS,OAAO,IAAI;EACtF,IAAI,aAAa,SAAS,OAAO,OAAO,SAAS,KAAA,KAAa,CAAC,OAAO,SAAS,OAAO,IAAI;EAC1F,IAAI,aAAa,UAAU,OAAO,OAAO,SAAS,KAAA;EAClD,IAAI,aAAa,gBAAgB,OAAO,OAAO,SAAS,KAAA;EACxD,OAAO;CACT,CAAC,IACe,6CAA6C;AACjE;AACA,SAAgB,qBACd,SACA,WACA,UAAU,OACV;CACA,MAAM,QAA4B,CAAC;CACnC,MAAM,QAA4B,CAAC;CACnC,IAAI,UAAU;CACd,MAAM,QAAQ,UAAU,WAAW;CACnC,MAAM,OAAO,QAAuB,SAAS,IAAI,SAAS,IAAI,aAAwB;EACpF,MAAM,KAAK,eAAe,OAAO,KAAK,GAAG,OAAO,SAAS,aAAa,GAAG,GAAG,OAAO,SAAS,OAAO;EACnG,IAAI,CAAC,MAAM,MAAM,SAAS,KAAK,OAAO,EAAE,GACtC,MAAM,KAAK;GACT;GACA,MAAM,OAAO;GACb,MAAM,GAAG,OAAO,SAAS,OAAO;GAChC,MAAM;GACN,MAAM;GACN;GACA;GACA,UAAU,OAAO;EACnB,CAAC;EACH,OAAO;CACT;CACA,MAAM,SAAS,UAAU,QAAQ,QAC9B,WACC;EAAC;EAAa;EAAa;EAAY;EAAY;CAAU,CAAC,CAAC,SAAS,OAAO,IAAI,KACnF,OAAO,MAAM,OAAO,MAAM,SACxB,KAAK,aAAa,MACf,aACE,QAAQ,SAAS,QAAQ,OACzB,QAAQ,QAAQ,eAAe,aAChC,QAAQ,SAAS,QAAQ,SAAS,SACjC,QAAQ,aAAa,OAAO,SAAS,eAAe,QAAQ,SAAS,SAC1E,CACF,CACJ;CACA,KAAK,MAAM,SAAS,OAAO,MAAM,GAAG,KAAK,GAAG;EAC1C,MAAM,WACJ,MAAM,MAAM,OACR,SAAS,SAAS,KAAK,eAAe,CAAC,CAAC,CAAC,CAC1C,QACE,aACE,QAAQ,SAAS,QAAQ,OACzB,QAAQ,QAAQ,eAAe,aAChC,QAAQ,SAAS,QAAQ,SAAS,SACjC,QAAQ,aAAa,MAAM,SAAS,eAAe,QAAQ,SAAS,SACzE,KAAK,CAAC;EACV,MAAM,YACJ,MAAM,MAAM,OAAO,SAChB,SACC,KAAK,SAAS,KACX,UAAU,MAAM,MAAM,SAAS,CAAC,MAAM,QAAQ,SAAS,MAAM,QAAQ,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,CACxG,KAAK,CAAC,CACV,KAAK,CAAC;EACR,MAAM,UAAU,IACd,OACA,IACA;GACE,GAAI,MAAM,MAAM,aAAa,CAAC;GAC9B,GAAG;GACH,GAAG,SAAS,KAAK,YAAY,UAAU,QAAQ,UAAU,EAAE,KAAK,cAAc,OAAO,SAAS,SAAS,GAAG;EAC5G,CAAC,CAAC,KAAK,KAAK,CACd;EACA,MAAM,KAAK;GAAE,MAAM;GAAS,IAAI;GAAW,OAAO;EAAuC,CAAC;EAC1F,MAAM,WAAW,MAAM,MAAM,cAAc,CAAC,EAAA,CAAG,QAC5C,YAAY,OAAO,SAAS,WAAW,UAAU,OAAO,QAAQ,eAAe,SAClF;EACA,WAAW,KAAK,IAAI,GAAG,QAAQ,SAAS,KAAK;EAC7C,KAAK,MAAM,UAAU,QAAQ,MAAM,GAAG,KAAK,GAAG;GAC5C,MAAM,UAAU,UAAU,QAAQ,MAC/B,WACC,OAAO,SAAS,aAChB,OAAO,SAAS,SAAS,OAAO,QAChC,OAAO,SAAS,eAAe,OAAO,aAAa,MAAM,SAAS,UACtE;GACA,IAAI,CAAC,SAAS;GACd,MAAM,SAAS,MAAM,QAAQ,SAAS,QACnC,UACE,KAAK,UAAU,SAAS,WAAW,UACnC,KAAK,UAAU,QAAQ,eAAe,aACvC,KAAK,UAAU,SAAS,OAAO,SAC9B,KAAK,UAAU,aAAa,MAAM,SAAS,gBAAgB,OAAO,aAAa,MAAM,SAAS,cAC/F,KAAK,UAAU,gBAAgB,OAAO,eACtC,KAAK,UAAU,SAAS,OAAO,IACnC;GACA,MAAM,aAAa,QAAQ,MAAM,aAAa,CAAC,EAAA,CAAG,QAC/C,UACE,CAAC,OAAO,eAAe,OAAO,gBAAgB,KAAK,UAAU,CAAC,OAAO,QAAQ,OAAO,SAAS,KAAK,KACvG;GACA,WAAW,KAAK,IAAI,GAAG,UAAU,SAAS,KAAK;GAC/C,KAAK,MAAM,YAAY,UAAU,MAAM,GAAG,KAAK,GAAG;IAChD,MAAM,iBAAiB,QAAQ,QAAQ,WAAW,MAAM,SAAS,KAAK,SAAS,SAAS,IAAI;IAC5F,MAAM,KAAK,IAAI,SAAS,IAAI,SAAS,QAAQ,GAAG,SAAS,SAAS,GAAG,SAAS,QAAQ,CACpF,YAAY,WAAW,gBAAgB,YAAY,QAAQ,SAAS,YAAY;KAAC;KAAY;KAAgB;IAAY,CAAC,KAC1H,WAAW,WAAW,QAAQ,QAAQ,YAAY,QAAQ,SAAS,YAAY,CAAC,YAAY,YAAY,CAAC,GAC3G,CAAC;IACD,MAAM,KAAK;KACT,MAAM;KACN,IAAI;KACJ,OAAO;KACP,UAAU,GAAG,WAAW,OAAO,SAAS,UAAU,SAAS,EAAE,KAAK,QAAQ,SAAS,OAAO,KAAK,SAAS,GAAG,KAAK,kBAAkB,qBAAqB,IAAI,WAAW,KAAK,YAAY,MAAM,SAAS,UAAU,GAAG,CAAC,CAAC,KAAK,KAAK,IAAI;IACrO,CAAC;IACD,MAAM,eAAe,UAAU,QAAQ,MACpC,WAAW,OAAO,SAAS,kBAAkB,OAAO,SAAS,SAAS,QAAQ,MAAM,gBACvF;IACA,IAAI,cACF,MAAM,KAAK;KACT,MAAM,IACJ,cACA,IACA,WAAW,aAAa,QAAQ,YAAY,aAAa,SAAS,YAAY,CAAC,UAAU,CAAC,CAC5F;KACA,IAAI;KACJ,OAAO;IACT,CAAC;GACL;EACF;CACF;CACA,MAAM,SAAS,SAAkB,SAAS,iBAAiB,IAAI,SAAS,YAAY,IAAI;CACxF,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,IAAI,IAAI,MAAM,EAAE,IAAI,CAAC;CAClD,OAAO;EAAE;EAAO;EAAO,SAAS,UAAU,KAAK,IAAI,GAAG,OAAO,SAAS,KAAK;CAAE;AAC/E"}
@@ -16,6 +16,18 @@ var descendants = (name, root) => `${name}(path, value, type) AS (
16
16
  )`;
17
17
  function predicate(filter) {
18
18
  if (!filter || typeof filter.column !== "string" || !filter.column || filter.column.includes("\0") || !Array.isArray(filter.path) || filter.path.length > 128 || filter.operator !== "eq" && filter.operator !== "neq" || !require_power_log_field_filters.jsonValuesEqual(filter.value, filter.value)) throw new Error("Invalid JSON field filter");
19
+ if (filter.path.every((part) => typeof part === "number" && Number.isSafeInteger(part) && part >= 0 || typeof part === "string" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(part)) && (filter.value === null || typeof filter.value !== "object")) {
20
+ const source = `"__power_rows".${quoteIdentifier(filter.column)}`;
21
+ const path = literal("$" + filter.path.map((part) => typeof part === "number" ? `[${part}]` : `.${part}`).join(""));
22
+ const type = `json_type(${source}, ${path})`;
23
+ const value = `json_extract(${source}, ${path})`;
24
+ let equal;
25
+ if (filter.value === null) equal = `${type} IS 'null'`;
26
+ else if (typeof filter.value === "boolean") equal = `${type} IS '${filter.value}'`;
27
+ else if (typeof filter.value === "number") equal = `(${type} IN ('integer', 'real') AND CAST(${value} AS REAL) IS ${filter.value})`;
28
+ else equal = `(${type} IS 'text' AND ${value} IS ${literal(filter.value)})`;
29
+ return `${filter.operator === "neq" ? "NOT " : ""}(CASE WHEN json_valid(${source}) THEN ${equal} ELSE 0 END)`;
30
+ }
19
31
  const steps = [];
20
32
  for (const [depth, part] of filter.path.entries()) {
21
33
  if (typeof part !== "string" && !(typeof part === "number" && Number.isSafeInteger(part) && part >= 0)) throw new Error("Invalid JSON field path");
@@ -1 +1 @@
1
- {"version":3,"file":"power-log-json-filter-sql.js","names":[],"sources":["../../../../src/common/power-log-json-filter-sql.ts"],"sourcesContent":["import { type JsonFieldFilter, jsonValuesEqual } from \"./power-log-field-filters\";\n\nexport interface SqlJsonFieldFilter extends JsonFieldFilter {\n column: string;\n}\n\nconst maxSqlLength = 65536;\nconst normalizedType = (type: string) => `CASE WHEN ${type} IN ('integer', 'real') THEN 'number' ELSE ${type} END`;\nconst quoteIdentifier = (value: string) => `\"${value.replace(/\"/g, '\"\"')}\"`;\nconst literal = (value: string): string =>\n value\n .split(\"\\0\")\n .map((part) => `'${part.replace(/'/g, \"''\")}'`)\n .join(\" || char(0) || \");\nconst tree = (name: string) => `SELECT path, ${normalizedType(\"type\")}, CASE\n WHEN type IN ('object', 'array') THEN NULL\n WHEN type IN ('integer', 'real') THEN CAST(value AS REAL)\n ELSE value END FROM ${name}`;\nconst descendants = (name: string, root: string) => `${name}(path, value, type) AS (\n ${root}\n UNION ALL\n SELECT json_insert(parent.path, '$[#]', j.key), j.value, j.type\n FROM ${name} AS parent, json_each(CASE WHEN parent.type IN ('object', 'array') THEN parent.value ELSE NULL END) AS j\n )`;\n\nfunction predicate(filter: SqlJsonFieldFilter): string {\n if (\n !filter ||\n typeof filter.column !== \"string\" ||\n !filter.column ||\n filter.column.includes(\"\\0\") ||\n !Array.isArray(filter.path) ||\n filter.path.length > 128 ||\n (filter.operator !== \"eq\" && filter.operator !== \"neq\") ||\n !jsonValuesEqual(filter.value, filter.value)\n )\n throw new Error(\"Invalid JSON field filter\");\n const steps: string[] = [];\n for (const [depth, part] of filter.path.entries()) {\n if (typeof part !== \"string\" && !(typeof part === \"number\" && Number.isSafeInteger(part) && part >= 0)) {\n throw new Error(\"Invalid JSON field path\");\n }\n steps.push(\n `(walk.depth = ${depth} AND typeof(j.key) = '${typeof part === \"string\" ? \"text\" : \"integer\"}' AND j.key = ${typeof part === \"string\" ? literal(part) : part})`,\n );\n }\n let encoded: string;\n try {\n encoded = JSON.stringify(filter.value);\n } catch {\n throw new Error(\"JSON field filter value cannot be encoded\");\n }\n const source = `\"__power_rows\".${quoteIdentifier(filter.column)}`;\n const safe = `CASE WHEN json_valid(${source}) THEN ${source} ELSE NULL END`;\n const recursion = steps.length\n ? `\n UNION ALL\n SELECT walk.depth + 1, j.value, j.type\n FROM walk, json_each(CASE WHEN walk.type IN ('object', 'array') THEN walk.value ELSE NULL END) AS j\n WHERE ${steps.join(\" OR \")}`\n : \"\";\n const leftTree = tree(\"actual_tree\");\n const rightTree = tree(\"expected_tree\");\n const equal = `EXISTS (\n SELECT 1 FROM walk, wanted WHERE walk.depth = ${filter.path.length}\n AND CASE\n WHEN walk.type IS NULL OR ${normalizedType(\"walk.type\")} != ${normalizedType(\"wanted.type\")} THEN 0\n WHEN walk.type IN ('object', 'array') THEN\n NOT EXISTS (${leftTree} EXCEPT ${rightTree}) AND NOT EXISTS (${rightTree} EXCEPT ${leftTree})\n WHEN walk.type IN ('integer', 'real') THEN CAST(walk.value AS REAL) IS CAST(wanted.value AS REAL)\n ELSE walk.value IS wanted.value\n END\n )`;\n return `(WITH RECURSIVE walk(depth, value, type) AS (\n SELECT 0, json_extract(${safe}, '$'), json_type(${safe})${recursion}\n ), wanted(value, type) AS (\n SELECT json_extract(${literal(encoded)}, '$'), json_type(${literal(encoded)})\n ), ${descendants(\"actual_tree\", `SELECT json_array(), value, type FROM walk WHERE depth = ${filter.path.length}`)},\n ${descendants(\"expected_tree\", \"SELECT json_array(), value, type FROM wanted\")}\n SELECT ${filter.operator === \"neq\" ? \"NOT \" : \"\"}${equal})`;\n}\n\nfunction withoutFinalSemicolon(sql: string): string {\n let lastCode = -1;\n for (let index = 0; index < sql.length; ) {\n const character = sql[index];\n if (/\\s/.test(character)) {\n index++;\n continue;\n }\n if (sql.startsWith(\"--\", index)) {\n const newline = sql.indexOf(\"\\n\", index + 2);\n index = newline < 0 ? sql.length : newline + 1;\n continue;\n }\n if (sql.startsWith(\"/*\", index)) {\n const end = sql.indexOf(\"*/\", index + 2);\n index = end < 0 ? sql.length : end + 2;\n continue;\n }\n lastCode = index;\n if (character === \"'\" || character === '\"' || character === \"`\" || character === \"[\") {\n const closing = character === \"[\" ? \"]\" : character;\n for (index++; index < sql.length; index++) {\n if (sql[index] !== closing) continue;\n if (closing !== \"]\" && sql[index + 1] === closing) {\n index++;\n continue;\n }\n index++;\n break;\n }\n } else index++;\n }\n return sql[lastCode] === \";\" ? sql.slice(0, lastCode) + sql.slice(lastCode + 1) : sql;\n}\n\nexport function buildJsonFilterSql(baseSql: string, filters: SqlJsonFieldFilter[]): string {\n if (typeof baseSql !== \"string\" || !Array.isArray(filters)) throw new Error(\"Invalid JSON filter query\");\n if (!filters.length) return baseSql;\n const base = withoutFinalSemicolon(baseSql.trim());\n if (!base) throw new Error(\"JSON filter query must not be empty\");\n let sql = `SELECT * FROM (${base}\\n) AS \"__power_rows\" WHERE `;\n for (const [index, filter] of filters.entries()) {\n sql += `${index ? \" AND \" : \"\"}${predicate(filter)}`;\n if (sql.length > maxSqlLength) throw new Error(\"JSON filter query exceeds 65536 characters\");\n }\n return sql;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"power-log-json-filter-sql.js","names":[],"sources":["../../../../src/common/power-log-json-filter-sql.ts"],"sourcesContent":["import { type JsonFieldFilter, jsonValuesEqual } from \"./power-log-field-filters\";\n\nexport interface SqlJsonFieldFilter extends JsonFieldFilter {\n column: string;\n}\n\nconst maxSqlLength = 65536;\nconst normalizedType = (type: string) => `CASE WHEN ${type} IN ('integer', 'real') THEN 'number' ELSE ${type} END`;\nconst quoteIdentifier = (value: string) => `\"${value.replace(/\"/g, '\"\"')}\"`;\nconst literal = (value: string): string =>\n value\n .split(\"\\0\")\n .map((part) => `'${part.replace(/'/g, \"''\")}'`)\n .join(\" || char(0) || \");\nconst tree = (name: string) => `SELECT path, ${normalizedType(\"type\")}, CASE\n WHEN type IN ('object', 'array') THEN NULL\n WHEN type IN ('integer', 'real') THEN CAST(value AS REAL)\n ELSE value END FROM ${name}`;\nconst descendants = (name: string, root: string) => `${name}(path, value, type) AS (\n ${root}\n UNION ALL\n SELECT json_insert(parent.path, '$[#]', j.key), j.value, j.type\n FROM ${name} AS parent, json_each(CASE WHEN parent.type IN ('object', 'array') THEN parent.value ELSE NULL END) AS j\n )`;\n\nfunction predicate(filter: SqlJsonFieldFilter): string {\n if (\n !filter ||\n typeof filter.column !== \"string\" ||\n !filter.column ||\n filter.column.includes(\"\\0\") ||\n !Array.isArray(filter.path) ||\n filter.path.length > 128 ||\n (filter.operator !== \"eq\" && filter.operator !== \"neq\") ||\n !jsonValuesEqual(filter.value, filter.value)\n )\n throw new Error(\"Invalid JSON field filter\");\n const simplePath = filter.path.every(\n (part) =>\n (typeof part === \"number\" && Number.isSafeInteger(part) && part >= 0) ||\n (typeof part === \"string\" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(part)),\n );\n if (simplePath && (filter.value === null || typeof filter.value !== \"object\")) {\n const source = `\"__power_rows\".${quoteIdentifier(filter.column)}`;\n const path = literal(\n \"$\" + filter.path.map((part) => (typeof part === \"number\" ? `[${part}]` : `.${part}`)).join(\"\"),\n );\n const type = `json_type(${source}, ${path})`;\n const value = `json_extract(${source}, ${path})`;\n let equal: string;\n if (filter.value === null) equal = `${type} IS 'null'`;\n else if (typeof filter.value === \"boolean\") equal = `${type} IS '${filter.value}'`;\n else if (typeof filter.value === \"number\")\n equal = `(${type} IN ('integer', 'real') AND CAST(${value} AS REAL) IS ${filter.value})`;\n else equal = `(${type} IS 'text' AND ${value} IS ${literal(filter.value)})`;\n return `${filter.operator === \"neq\" ? \"NOT \" : \"\"}(CASE WHEN json_valid(${source}) THEN ${equal} ELSE 0 END)`;\n }\n const steps: string[] = [];\n for (const [depth, part] of filter.path.entries()) {\n if (typeof part !== \"string\" && !(typeof part === \"number\" && Number.isSafeInteger(part) && part >= 0)) {\n throw new Error(\"Invalid JSON field path\");\n }\n steps.push(\n `(walk.depth = ${depth} AND typeof(j.key) = '${typeof part === \"string\" ? \"text\" : \"integer\"}' AND j.key = ${typeof part === \"string\" ? literal(part) : part})`,\n );\n }\n let encoded: string;\n try {\n encoded = JSON.stringify(filter.value);\n } catch {\n throw new Error(\"JSON field filter value cannot be encoded\");\n }\n const source = `\"__power_rows\".${quoteIdentifier(filter.column)}`;\n const safe = `CASE WHEN json_valid(${source}) THEN ${source} ELSE NULL END`;\n const recursion = steps.length\n ? `\n UNION ALL\n SELECT walk.depth + 1, j.value, j.type\n FROM walk, json_each(CASE WHEN walk.type IN ('object', 'array') THEN walk.value ELSE NULL END) AS j\n WHERE ${steps.join(\" OR \")}`\n : \"\";\n const leftTree = tree(\"actual_tree\");\n const rightTree = tree(\"expected_tree\");\n const equal = `EXISTS (\n SELECT 1 FROM walk, wanted WHERE walk.depth = ${filter.path.length}\n AND CASE\n WHEN walk.type IS NULL OR ${normalizedType(\"walk.type\")} != ${normalizedType(\"wanted.type\")} THEN 0\n WHEN walk.type IN ('object', 'array') THEN\n NOT EXISTS (${leftTree} EXCEPT ${rightTree}) AND NOT EXISTS (${rightTree} EXCEPT ${leftTree})\n WHEN walk.type IN ('integer', 'real') THEN CAST(walk.value AS REAL) IS CAST(wanted.value AS REAL)\n ELSE walk.value IS wanted.value\n END\n )`;\n return `(WITH RECURSIVE walk(depth, value, type) AS (\n SELECT 0, json_extract(${safe}, '$'), json_type(${safe})${recursion}\n ), wanted(value, type) AS (\n SELECT json_extract(${literal(encoded)}, '$'), json_type(${literal(encoded)})\n ), ${descendants(\"actual_tree\", `SELECT json_array(), value, type FROM walk WHERE depth = ${filter.path.length}`)},\n ${descendants(\"expected_tree\", \"SELECT json_array(), value, type FROM wanted\")}\n SELECT ${filter.operator === \"neq\" ? \"NOT \" : \"\"}${equal})`;\n}\n\nfunction withoutFinalSemicolon(sql: string): string {\n let lastCode = -1;\n for (let index = 0; index < sql.length; ) {\n const character = sql[index];\n if (/\\s/.test(character)) {\n index++;\n continue;\n }\n if (sql.startsWith(\"--\", index)) {\n const newline = sql.indexOf(\"\\n\", index + 2);\n index = newline < 0 ? sql.length : newline + 1;\n continue;\n }\n if (sql.startsWith(\"/*\", index)) {\n const end = sql.indexOf(\"*/\", index + 2);\n index = end < 0 ? sql.length : end + 2;\n continue;\n }\n lastCode = index;\n if (character === \"'\" || character === '\"' || character === \"`\" || character === \"[\") {\n const closing = character === \"[\" ? \"]\" : character;\n for (index++; index < sql.length; index++) {\n if (sql[index] !== closing) continue;\n if (closing !== \"]\" && sql[index + 1] === closing) {\n index++;\n continue;\n }\n index++;\n break;\n }\n } else index++;\n }\n return sql[lastCode] === \";\" ? sql.slice(0, lastCode) + sql.slice(lastCode + 1) : sql;\n}\n\nexport function buildJsonFilterSql(baseSql: string, filters: SqlJsonFieldFilter[]): string {\n if (typeof baseSql !== \"string\" || !Array.isArray(filters)) throw new Error(\"Invalid JSON filter query\");\n if (!filters.length) return baseSql;\n const base = withoutFinalSemicolon(baseSql.trim());\n if (!base) throw new Error(\"JSON filter query must not be empty\");\n let sql = `SELECT * FROM (${base}\\n) AS \"__power_rows\" WHERE `;\n for (const [index, filter] of filters.entries()) {\n sql += `${index ? \" AND \" : \"\"}${predicate(filter)}`;\n if (sql.length > maxSqlLength) throw new Error(\"JSON filter query exceeds 65536 characters\");\n }\n return sql;\n}\n"],"mappings":"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"}