@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.
- package/README.md +4 -4
- package/out/main/common/power-logs.js.map +1 -1
- package/out/main/common/resource-search.js.map +1 -1
- package/out/main/power-logs-child.js +3 -3
- package/out/main/power-logs-child.js.map +1 -1
- package/out/main/power-logs-database.js +165 -7
- package/out/main/power-logs-database.js.map +1 -1
- package/out/main/power-logs-ipc.js +12 -3
- package/out/main/power-logs-ipc.js.map +1 -1
- package/out/main/power-logs-worker.js +29 -7
- package/out/main/power-logs-worker.js.map +1 -1
- package/out/main/resource-search-database.js +3 -2
- package/out/main/resource-search-database.js.map +1 -1
- package/out/main/resource-search.js +2 -1
- package/out/main/resource-search.js.map +1 -1
- package/out/renderer/_virtual/_global-external_react.js +2 -1
- package/out/renderer/ai-context-mutations.js +14 -0
- package/out/renderer/ai-context-mutations.js.map +1 -0
- package/out/renderer/ai-copy-answer.js +18 -15
- package/out/renderer/ai-copy-answer.js.map +1 -1
- package/out/renderer/ai-diagnostic-handoff.js +2 -2
- package/out/renderer/ai-diagnostic-handoff.js.map +1 -1
- package/out/renderer/ai-extras.js +41 -38
- package/out/renderer/ai-extras.js.map +1 -1
- package/out/renderer/ai-message.js +50 -39
- package/out/renderer/ai-message.js.map +1 -1
- package/out/renderer/ai-provider-icon.js +39 -32
- package/out/renderer/ai-provider-icon.js.map +1 -1
- package/out/renderer/ai-resize.js +40 -37
- package/out/renderer/ai-resize.js.map +1 -1
- package/out/renderer/ai-shortcuts.js +20 -17
- package/out/renderer/ai-shortcuts.js.map +1 -1
- package/out/renderer/ai-terminal-context.js +42 -48
- package/out/renderer/ai-terminal-context.js.map +1 -1
- package/out/renderer/ai.js +137 -111
- package/out/renderer/ai.js.map +1 -1
- package/out/renderer/cluster-icon-colors.js +10 -7
- package/out/renderer/cluster-icon-colors.js.map +1 -1
- package/out/renderer/cluster-icons.js +47 -40
- package/out/renderer/cluster-icons.js.map +1 -1
- package/out/renderer/details/service-diagnostics-details.js +25 -23
- package/out/renderer/details/service-diagnostics-details.js.map +1 -1
- package/out/renderer/diagnostic-explain-button.js +7 -4
- package/out/renderer/diagnostic-explain-button.js.map +1 -1
- package/out/renderer/diagnostics-screen-finding.js +51 -0
- package/out/renderer/diagnostics-screen-finding.js.map +1 -0
- package/out/renderer/diagnostics.css.js +1 -1
- package/out/renderer/diagnostics.css.js.map +1 -1
- package/out/renderer/diagnostics.js +146 -107
- package/out/renderer/diagnostics.js.map +1 -1
- package/out/renderer/incident-copy-button.js +24 -21
- package/out/renderer/incident-copy-button.js.map +1 -1
- package/out/renderer/index.js +2 -0
- package/out/renderer/index.js.map +1 -1
- package/out/renderer/monitoring.js +19 -22
- package/out/renderer/monitoring.js.map +1 -1
- package/out/renderer/native-tooltip.js +22 -0
- package/out/renderer/native-tooltip.js.map +1 -0
- package/out/renderer/pod-details-enhancements.js +106 -0
- package/out/renderer/pod-details-enhancements.js.map +1 -0
- package/out/renderer/power-log-json-cell.js +44 -38
- package/out/renderer/power-log-json-cell.js.map +1 -1
- package/out/renderer/power-log-json.js +17 -10
- package/out/renderer/power-log-json.js.map +1 -1
- package/out/renderer/power-log-sql-editor.js +31 -2
- package/out/renderer/power-log-sql-editor.js.map +1 -1
- package/out/renderer/power-logs-client.js +5 -0
- package/out/renderer/power-logs-client.js.map +1 -1
- package/out/renderer/power-logs-entrypoints.js +10 -36
- package/out/renderer/power-logs-entrypoints.js.map +1 -1
- package/out/renderer/power-logs.css.js +9 -2
- package/out/renderer/power-logs.css.js.map +1 -1
- package/out/renderer/power-logs.js +376 -214
- package/out/renderer/power-logs.js.map +1 -1
- package/out/renderer/preferences/dashboard-preferences.js +42 -35
- package/out/renderer/preferences/dashboard-preferences.js.map +1 -1
- package/out/renderer/preferences/logs-preferences.js +1 -1
- package/out/renderer/preferences/logs-preferences.js.map +1 -1
- package/out/renderer/preferences/monitoring-preferences.js +7 -4
- package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
- package/out/renderer/preferences/preferences.js +2 -2
- package/out/renderer/preferences/preferences.js.map +1 -1
- package/out/renderer/preferences/settings-icon-action.js +12 -11
- package/out/renderer/preferences/settings-icon-action.js.map +1 -1
- package/out/renderer/relationship-map-finding.js +53 -0
- package/out/renderer/relationship-map-finding.js.map +1 -0
- package/out/renderer/resource-relationships.css.js +6 -0
- package/out/renderer/resource-relationships.css.js.map +1 -1
- package/out/renderer/resource-relationships.js +94 -47
- package/out/renderer/resource-relationships.js.map +1 -1
- package/out/renderer/resource-search-client.js.map +1 -1
- package/out/renderer/resource-search.js +88 -35
- package/out/renderer/resource-search.js.map +1 -1
- package/out/renderer/src/common/gateway-relationships.js +8 -7
- package/out/renderer/src/common/gateway-relationships.js.map +1 -1
- package/out/renderer/src/common/power-log-json-filter-sql.js +12 -0
- package/out/renderer/src/common/power-log-json-filter-sql.js.map +1 -1
- package/out/renderer/src/common/power-log-json-preset.js +2 -2
- package/out/renderer/src/common/power-log-json-preset.js.map +1 -1
- package/out/renderer/src/common/power-logs.js.map +1 -1
- package/out/renderer/src/common/resource-relationships.js +10 -8
- package/out/renderer/src/common/resource-relationships.js.map +1 -1
- package/out/renderer/src/common/resource-search.js.map +1 -1
- package/out/renderer/welcome.js +16 -1
- package/out/renderer/welcome.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ JOIN json_each(recent.fieldsJson) AS field
|
|
|
5
5
|
WHERE typeof(field.key) = 'text'
|
|
6
6
|
LIMIT 65`;
|
|
7
7
|
function buildJsonProjectionSql(keys, options = {}) {
|
|
8
|
-
if (options.recent) return "SELECT
|
|
8
|
+
if (options.recent) return "SELECT timestamp, level, message, container, pod FROM logs ORDER BY timestamp DESC NULLS LAST LIMIT 200";
|
|
9
9
|
const fallbackSql = "SELECT fieldsJson FROM logs WHERE fieldsJson IS NOT NULL LIMIT 100";
|
|
10
10
|
if (!keys.length || keys.length > 64) return fallbackSql;
|
|
11
11
|
const usable = (key) => typeof key === "string" && key.length <= 128 && !key.includes("\0");
|
|
@@ -23,7 +23,7 @@ function buildJsonProjectionSql(keys, options = {}) {
|
|
|
23
23
|
if (key === void 0 || !usable(key)) return fallbackSql;
|
|
24
24
|
const literal = `'${key.replace(/'/g, "''")}'`;
|
|
25
25
|
const identifier = `"${key.replace(/"/g, "\"\"")}"`;
|
|
26
|
-
return `SELECT\n ${/^[A-Za-z_][A-Za-z0-9_]*$/.test(key) ? `json_extract(logs.fieldsJson, '$.${key}')` : `(SELECT value FROM json_each(logs.fieldsJson) WHERE key = ${literal} LIMIT 1)`} AS ${identifier}\nFROM logs\nWHERE fieldsJson IS NOT NULL
|
|
26
|
+
return `SELECT\n ${/^[A-Za-z_][A-Za-z0-9_]*$/.test(key) ? `json_extract(logs.fieldsJson, '$.${key}')` : `(SELECT value FROM json_each(logs.fieldsJson) WHERE key = ${literal} LIMIT 1)`} AS ${identifier},\n logs.fieldsJson AS "${key.toLowerCase() === "json" ? "fieldsJson" : "json"}"\nFROM logs\nWHERE fieldsJson IS NOT NULL
|
|
27
27
|
ORDER BY logs.timestamp DESC NULLS LAST
|
|
28
28
|
LIMIT 100`;
|
|
29
29
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"power-log-json-preset.js","names":[],"sources":["../../../../src/common/power-log-json-preset.ts"],"sourcesContent":["export const JSON_KEY_DISCOVERY_SQL = `SELECT DISTINCT field.key\nFROM (SELECT fieldsJson FROM logs WHERE fieldsJson IS NOT NULL ORDER BY timestamp DESC NULLS LAST LIMIT 100) AS recent\nJOIN json_each(recent.fieldsJson) AS field\nWHERE typeof(field.key) = 'text'\nLIMIT 65`;\n\nexport function buildJsonProjectionSql(keys: string[], options: { recent?: boolean } = {}): string {\n if (options.recent)\n return \"SELECT
|
|
1
|
+
{"version":3,"file":"power-log-json-preset.js","names":[],"sources":["../../../../src/common/power-log-json-preset.ts"],"sourcesContent":["export const JSON_KEY_DISCOVERY_SQL = `SELECT DISTINCT field.key\nFROM (SELECT fieldsJson FROM logs WHERE fieldsJson IS NOT NULL ORDER BY timestamp DESC NULLS LAST LIMIT 100) AS recent\nJOIN json_each(recent.fieldsJson) AS field\nWHERE typeof(field.key) = 'text'\nLIMIT 65`;\n\nexport function buildJsonProjectionSql(keys: string[], options: { recent?: boolean } = {}): string {\n if (options.recent)\n return \"SELECT timestamp, level, message, container, pod FROM logs ORDER BY timestamp DESC NULLS LAST LIMIT 200\";\n const fallbackSql = \"SELECT fieldsJson FROM logs WHERE fieldsJson IS NOT NULL LIMIT 100\";\n if (!keys.length || keys.length > 64) return fallbackSql;\n const usable = (key: string) => typeof key === \"string\" && key.length <= 128 && !key.includes(\"\\0\");\n const key =\n [\"message\", \"msg\", \"text\", \"Message\", \"RenderedMessage\", \"@m\", \"@mt\", \"body\", \"Body\"].find((preferred) =>\n keys.includes(preferred),\n ) ?? keys.find(usable);\n if (key === undefined || !usable(key)) return fallbackSql;\n const literal = `'${key.replace(/'/g, \"''\")}'`;\n const identifier = `\"${key.replace(/\"/g, '\"\"')}\"`;\n const value = /^[A-Za-z_][A-Za-z0-9_]*$/.test(key)\n ? `json_extract(logs.fieldsJson, '$.${key}')`\n : `(SELECT value FROM json_each(logs.fieldsJson) WHERE key = ${literal} LIMIT 1)`;\n const tail = \"WHERE fieldsJson IS NOT NULL\\nORDER BY logs.timestamp DESC NULLS LAST\\nLIMIT 100\";\n return `SELECT\\n ${value} AS ${identifier},\\n logs.fieldsJson AS \"${key.toLowerCase() === \"json\" ? \"fieldsJson\" : \"json\"}\"\\nFROM logs\\n${tail}`;\n}\n"],"mappings":";AAAA,IAAa,yBAAyB;;;;;AAMtC,SAAgB,uBAAuB,MAAgB,UAAgC,CAAC,GAAW;CACjG,IAAI,QAAQ,QACV,OAAO;CACT,MAAM,cAAc;CACpB,IAAI,CAAC,KAAK,UAAU,KAAK,SAAS,IAAI,OAAO;CAC7C,MAAM,UAAU,QAAgB,OAAO,QAAQ,YAAY,IAAI,UAAU,OAAO,CAAC,IAAI,SAAS,IAAI;CAClG,MAAM,MACJ;EAAC;EAAW;EAAO;EAAQ;EAAW;EAAmB;EAAM;EAAO;EAAQ;CAAM,CAAC,CAAC,MAAM,cAC1F,KAAK,SAAS,SAAS,CACzB,KAAK,KAAK,KAAK,MAAM;CACvB,IAAI,QAAQ,KAAA,KAAa,CAAC,OAAO,GAAG,GAAG,OAAO;CAC9C,MAAM,UAAU,IAAI,IAAI,QAAQ,MAAM,IAAI,EAAE;CAC5C,MAAM,aAAa,IAAI,IAAI,QAAQ,MAAM,MAAI,EAAE;CAK/C,OAAO,aAJO,2BAA2B,KAAK,GAAG,IAC7C,oCAAoC,IAAI,MACxC,6DAA6D,QAAQ,WAE/C,MAAM,WAAW,2BAA2B,IAAI,YAAY,MAAM,SAAS,eAAe,OAAO;;;AAC7H"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"power-logs.js","names":[],"sources":["../../../../src/common/power-logs.ts"],"sourcesContent":["export const POWER_LOGS_CHANNEL = \"freelens-power:logs\";\nexport const POWER_LOGS_DEFAULT_LIMITS = {\n liveRows: 10000,\n liveBytes: 16 * 1024 * 1024,\n batchRows: 1000,\n batchBytes: 2 * 1024 * 1024,\n recordBytes: 64 * 1024,\n captureBytes: 256 * 1024 * 1024,\n captureRows: 1000000,\n captures: 3,\n sources: 200,\n resultRows: 1000,\n resultBytes: 2 * 1024 * 1024,\n queryMs: 15000,\n ttlMs: 60 * 60 * 1000,\n historyLines: 1000,\n historyBytes: 1024 * 1024,\n captureSourceLines: 10000,\n captureSourceBytes: 8 * 1024 * 1024,\n requestsPerSecond: 2,\n bytesPerSecond: 1024 * 1024,\n historyConcurrency: 2,\n liveConcurrency: 4,\n} as const;\n\nexport type PowerLogsLimitKey = keyof typeof POWER_LOGS_DEFAULT_LIMITS;\nexport type PowerLogsLimits = { [Key in PowerLogsLimitKey]: number };\nexport const POWER_LOGS_LIMITS: PowerLogsLimits = { ...POWER_LOGS_DEFAULT_LIMITS };\n\nexport type LogLevel = \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\" | \"unknown\";\nexport type LogWorkloadKind = \"Pod\" | \"Deployment\" | \"DaemonSet\" | \"StatefulSet\" | \"ReplicaSet\" | \"Job\" | \"CronJob\";\nexport interface LogTarget {\n kind: LogWorkloadKind;\n namespace: string;\n name: string;\n uid: string;\n}\nexport interface LogSource {\n namespace: string;\n pod: string;\n podUid: string;\n container: string;\n previous?: boolean;\n}\nexport interface LogRecord {\n timestamp: number | null;\n pod: string;\n podUid: string;\n container: string;\n level: LogLevel;\n message: string;\n fieldsJson: string | null;\n previous?: boolean;\n}\nexport interface LogCapture {\n id: string;\n target: LogTarget;\n createdAt: number;\n expiresAt: number;\n state: \"collecting\" | \"ready\" | \"partial\";\n rows: number;\n bytes: number;\n notes: string[];\n lastSql?: string;\n}\nexport type LogCell = string | number | boolean | null;\nexport interface LogQueryResult {\n error?: string;\n columns: string[];\n rows: LogCell[][];\n truncated: boolean;\n elapsedMs: number;\n}\nexport interface LogPartition {\n clusterId: string;\n contextId: string;\n}\nexport type LogRequest = LogPartition &\n (\n | { operation: \"permit\"; lane: \"start\" | \"bytes\"; bytes?: number }\n | { operation: \"stream-open\"; streamId: string; follow: boolean }\n | { operation: \"stream-close\" | \"stream-renew\"; streamId: string }\n | { operation: \"create\"; target: LogTarget }\n | { operation: \"append\"; captureId: string; records: LogRecord[] }\n | { operation: \"finish\"; captureId: string; partial: boolean; notes: string[] }\n | { operation: \"list\" }\n | { operation: \"delete\"; captureId: string }\n | { operation: \"save-sql\"; captureId: string; sql: string }\n | { operation: \"query\"; captureId: string; queryId: string; sql: string }\n | { operation: \"cancel\"; queryId: string }\n | { operation: \"export\"; captureId: string }\n );\n"],"mappings":";AAAA,IAAa,qBAAqB;AAClC,IAAa,4BAA4B;CACvC,UAAU;CACV,WAAW,KAAK,OAAO;CACvB,WAAW;CACX,YAAY,IAAI,OAAO;CACvB,aAAa,KAAK;CAClB,cAAc,MAAM,OAAO;CAC3B,aAAa;CACb,UAAU;CACV,SAAS;CACT,YAAY;CACZ,aAAa,IAAI,OAAO;CACxB,SAAS;CACT,OAAO,OAAU;CACjB,cAAc;CACd,cAAc,OAAO;CACrB,oBAAoB;CACpB,oBAAoB,IAAI,OAAO;CAC/B,mBAAmB;CACnB,gBAAgB,OAAO;CACvB,oBAAoB;CACpB,iBAAiB;AACnB;AAIA,IAAa,oBAAqC,EAAE,GAAG,0BAA0B"}
|
|
1
|
+
{"version":3,"file":"power-logs.js","names":[],"sources":["../../../../src/common/power-logs.ts"],"sourcesContent":["export const POWER_LOGS_CHANNEL = \"freelens-power:logs\";\nexport const POWER_LOGS_DEFAULT_LIMITS = {\n liveRows: 10000,\n liveBytes: 16 * 1024 * 1024,\n batchRows: 1000,\n batchBytes: 2 * 1024 * 1024,\n recordBytes: 64 * 1024,\n captureBytes: 256 * 1024 * 1024,\n captureRows: 1000000,\n captures: 3,\n sources: 200,\n resultRows: 1000,\n resultBytes: 2 * 1024 * 1024,\n queryMs: 15000,\n ttlMs: 60 * 60 * 1000,\n historyLines: 1000,\n historyBytes: 1024 * 1024,\n captureSourceLines: 10000,\n captureSourceBytes: 8 * 1024 * 1024,\n requestsPerSecond: 2,\n bytesPerSecond: 1024 * 1024,\n historyConcurrency: 2,\n liveConcurrency: 4,\n} as const;\n\nexport type PowerLogsLimitKey = keyof typeof POWER_LOGS_DEFAULT_LIMITS;\nexport type PowerLogsLimits = { [Key in PowerLogsLimitKey]: number };\nexport const POWER_LOGS_LIMITS: PowerLogsLimits = { ...POWER_LOGS_DEFAULT_LIMITS };\n\nexport type LogLevel = \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\" | \"unknown\";\nexport type LogWorkloadKind = \"Pod\" | \"Deployment\" | \"DaemonSet\" | \"StatefulSet\" | \"ReplicaSet\" | \"Job\" | \"CronJob\";\nexport interface LogTarget {\n kind: LogWorkloadKind;\n namespace: string;\n name: string;\n uid: string;\n}\nexport interface LogSource {\n namespace: string;\n pod: string;\n podUid: string;\n container: string;\n previous?: boolean;\n}\nexport interface LogRecord {\n timestamp: number | null;\n pod: string;\n podUid: string;\n container: string;\n level: LogLevel;\n message: string;\n fieldsJson: string | null;\n previous?: boolean;\n}\nexport interface LogCapture {\n id: string;\n target: LogTarget;\n createdAt: number;\n expiresAt: number;\n state: \"collecting\" | \"ready\" | \"partial\";\n rows: number;\n bytes: number;\n notes: string[];\n lastSql?: string;\n persistent?: boolean;\n}\nexport type LogCell = string | number | boolean | null;\nexport interface LogQueryResult {\n error?: string;\n columns: string[];\n rows: LogCell[][];\n truncated: boolean;\n elapsedMs: number;\n}\nexport interface LogPartition {\n clusterId: string;\n contextId: string;\n}\nexport type LogRequest = LogPartition &\n (\n | { operation: \"permit\"; lane: \"start\" | \"bytes\"; bytes?: number }\n | { operation: \"stream-open\"; streamId: string; follow: boolean }\n | { operation: \"stream-close\" | \"stream-renew\"; streamId: string }\n | { operation: \"create\"; target: LogTarget }\n | { operation: \"append\"; captureId: string; records: LogRecord[] }\n | { operation: \"finish\"; captureId: string; partial: boolean; notes: string[] }\n | { operation: \"list\" }\n | { operation: \"clear-persistent\" }\n | { operation: \"delete\"; captureId: string }\n | { operation: \"set-persistent\"; captureId: string; persistent: boolean }\n | { operation: \"save-sql\"; captureId: string; sql: string }\n | { operation: \"query\"; captureId: string; queryId: string; sql: string }\n | { operation: \"cancel\"; queryId: string }\n | { operation: \"export\"; captureId: string }\n );\n"],"mappings":";AAAA,IAAa,qBAAqB;AAClC,IAAa,4BAA4B;CACvC,UAAU;CACV,WAAW,KAAK,OAAO;CACvB,WAAW;CACX,YAAY,IAAI,OAAO;CACvB,aAAa,KAAK;CAClB,cAAc,MAAM,OAAO;CAC3B,aAAa;CACb,UAAU;CACV,SAAS;CACT,YAAY;CACZ,aAAa,IAAI,OAAO;CACxB,SAAS;CACT,OAAO,OAAU;CACjB,cAAc;CACd,cAAc,OAAO;CACrB,oBAAoB;CACpB,oBAAoB,IAAI,OAAO;CAC/B,mBAAmB;CACnB,gBAAgB,OAAO;CACvB,oBAAoB;CACpB,iBAAiB;AACnB;AAIA,IAAa,oBAAqC,EAAE,GAAG,0BAA0B"}
|
|
@@ -9,13 +9,15 @@ function relationshipReferencePath(kind, name, namespace) {
|
|
|
9
9
|
namespace
|
|
10
10
|
});
|
|
11
11
|
}
|
|
12
|
-
function buildResourceRelationships(service, inventory, focusedPod) {
|
|
12
|
+
function buildResourceRelationships(service, inventory, focusedPod, showAll = false) {
|
|
13
13
|
const external = service.spec?.type === "ExternalName";
|
|
14
14
|
const diagnostics = require_service_diagnostics.diagnoseService(service, external ? [] : inventory.pods, external ? [] : inventory.slices);
|
|
15
15
|
const nodes = [];
|
|
16
16
|
const edges = [];
|
|
17
17
|
const gaps = [...inventory.gaps ?? []];
|
|
18
18
|
let omitted = 0;
|
|
19
|
+
const upstreamLimit = showAll ? Infinity : 5;
|
|
20
|
+
const podLimit = showAll ? Infinity : 8;
|
|
19
21
|
const namespace = service.metadata.namespace;
|
|
20
22
|
const add = (node) => {
|
|
21
23
|
if (!nodes.some((n) => n.id === node.id)) nodes.push(node);
|
|
@@ -35,8 +37,8 @@ function buildResourceRelationships(service, inventory, focusedPod) {
|
|
|
35
37
|
ingress.spec?.backend?.serviceName,
|
|
36
38
|
...(ingress.spec?.rules ?? []).flatMap((rule) => (rule.http?.paths ?? []).map((path) => path.backend?.service?.name ?? path.backend?.serviceName))
|
|
37
39
|
].includes(service.metadata.name)) ?? [];
|
|
38
|
-
omitted += Math.max(0, upstream.length -
|
|
39
|
-
for (const ingress of upstream.slice(0,
|
|
40
|
+
omitted += Math.max(0, upstream.length - upstreamLimit);
|
|
41
|
+
for (const ingress of upstream.slice(0, upstreamLimit)) {
|
|
40
42
|
const id = add({
|
|
41
43
|
id: `ingress/${ingress.metadata.uid ?? ingress.metadata.name}`,
|
|
42
44
|
kind: "Ingress",
|
|
@@ -53,15 +55,15 @@ function buildResourceRelationships(service, inventory, focusedPod) {
|
|
|
53
55
|
});
|
|
54
56
|
}
|
|
55
57
|
if (inventory.gateway) {
|
|
56
|
-
const gateway = require_gateway_relationships.gatewayRelationships(service, inventory.gateway);
|
|
58
|
+
const gateway = require_gateway_relationships.gatewayRelationships(service, inventory.gateway, showAll);
|
|
57
59
|
nodes.push(...gateway.nodes);
|
|
58
60
|
edges.push(...gateway.edges);
|
|
59
61
|
omitted += gateway.omitted;
|
|
60
62
|
gaps.push(...inventory.gateway.gaps);
|
|
61
63
|
}
|
|
62
64
|
const pods = diagnostics.matchingPods?.filter((pod) => !focusedPod || pod.metadata.name === focusedPod) ?? [];
|
|
63
|
-
omitted += Math.max(0, pods.length -
|
|
64
|
-
for (const pod of pods.slice(0,
|
|
65
|
+
omitted += Math.max(0, pods.length - podLimit);
|
|
66
|
+
for (const pod of pods.slice(0, podLimit)) {
|
|
65
67
|
const ready = pod.status?.conditions?.find((c) => c.type === "Ready");
|
|
66
68
|
const id = add({
|
|
67
69
|
id: `pod/${pod.metadata.uid ?? pod.metadata.name}`,
|
|
@@ -122,8 +124,8 @@ function buildResourceRelationships(service, inventory, focusedPod) {
|
|
|
122
124
|
}
|
|
123
125
|
}
|
|
124
126
|
const slices = diagnostics.matchingSlices ?? [];
|
|
125
|
-
omitted += Math.max(0, slices.length -
|
|
126
|
-
for (const slice of slices.slice(0,
|
|
127
|
+
omitted += Math.max(0, slices.length - upstreamLimit);
|
|
128
|
+
for (const slice of slices.slice(0, upstreamLimit)) {
|
|
127
129
|
const ready = slice.endpoints?.filter((e) => e.conditions?.ready !== false && !e.conditions?.terminating).length;
|
|
128
130
|
const id = add({
|
|
129
131
|
id: `slice/${slice.metadata.name}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-relationships.js","names":[],"sources":["../../../../src/common/resource-relationships.ts"],"sourcesContent":["import { type GatewayInventory, gatewayRelationships } from \"./gateway-relationships\";\nimport { incidentResourcePath } from \"./incident-summary\";\nimport {\n diagnoseService,\n type ServiceDiagnosticObject,\n type ServiceDiagnosticPod,\n type ServiceDiagnosticSlice,\n} from \"./service-diagnostics\";\n\nexport interface RelationshipMetadata {\n uid?: string;\n name?: string;\n namespace?: string;\n ownerReferences?: { uid: string; name?: string; kind?: string; controller?: boolean }[];\n}\nexport interface RelationshipObject {\n kind?: string;\n metadata: RelationshipMetadata;\n selfLink?: string;\n}\nexport interface RelationshipPod extends ServiceDiagnosticPod {\n metadata: ServiceDiagnosticPod[\"metadata\"] & RelationshipMetadata;\n spec?: ServiceDiagnosticPod[\"spec\"] & { nodeName?: string };\n status?: ServiceDiagnosticPod[\"status\"] & { phase?: string };\n selfLink?: string;\n}\nexport interface RelationshipSlice extends ServiceDiagnosticSlice {\n selfLink?: string;\n}\nexport interface RelationshipIngress extends RelationshipObject {\n spec?: {\n defaultBackend?: { service?: { name: string } };\n backend?: { serviceName?: string };\n rules?: { http?: { paths?: { backend?: { service?: { name: string }; serviceName?: string } }[] } }[];\n };\n}\nexport interface RelationshipNode {\n id: string;\n kind: string;\n name: string;\n role: \"upstream\" | \"service\" | \"backend\" | \"placement\";\n detail: string;\n evidence?: string[];\n tone: \"neutral\" | \"warning\" | \"healthy\";\n selfLink?: string;\n inferred?: boolean;\n}\nexport interface RelationshipEdge {\n from: string;\n to: string;\n label: string;\n evidence?: string;\n}\nexport interface RelationshipInventory {\n pods?: RelationshipPod[];\n slices?: RelationshipSlice[];\n ingresses?: RelationshipIngress[];\n owners?: RelationshipObject[];\n gaps?: string[];\n gateway?: GatewayInventory;\n}\nexport function relationshipReferencePath(kind: string, name: string, namespace?: string) {\n return incidentResourcePath({ kind, name, namespace });\n}\nexport function buildResourceRelationships(\n service: ServiceDiagnosticObject & { selfLink?: string },\n inventory: RelationshipInventory,\n focusedPod?: string,\n) {\n const external = service.spec?.type === \"ExternalName\";\n const diagnostics = diagnoseService(service, external ? [] : inventory.pods, external ? [] : inventory.slices);\n const nodes: RelationshipNode[] = [];\n const edges: RelationshipEdge[] = [];\n const gaps = [...(inventory.gaps ?? [])];\n let omitted = 0;\n const namespace = service.metadata.namespace;\n const add = (node: RelationshipNode) => {\n if (!nodes.some((n) => n.id === node.id)) nodes.push(node);\n return node.id;\n };\n const svc = add({\n id: \"service\",\n kind: \"Service\",\n name: service.metadata.name ?? \"Unnamed\",\n role: \"service\",\n detail: diagnostics.summary,\n tone: \"neutral\",\n selfLink: service.selfLink,\n });\n const upstream =\n inventory.ingresses?.filter(\n (ingress) =>\n ingress.metadata.namespace === namespace &&\n [\n ingress.spec?.defaultBackend?.service?.name,\n ingress.spec?.backend?.serviceName,\n ...(ingress.spec?.rules ?? []).flatMap((rule) =>\n (rule.http?.paths ?? []).map((path) => path.backend?.service?.name ?? path.backend?.serviceName),\n ),\n ].includes(service.metadata.name),\n ) ?? [];\n omitted += Math.max(0, upstream.length - 5);\n for (const ingress of upstream.slice(0, 5)) {\n const id = add({\n id: `ingress/${ingress.metadata.uid ?? ingress.metadata.name}`,\n kind: \"Ingress\",\n name: ingress.metadata.name ?? \"Unnamed\",\n role: \"upstream\",\n detail: \"Backend Service reference\",\n tone: \"neutral\",\n selfLink: ingress.selfLink,\n });\n edges.push({ from: id, to: svc, label: \"references backend\" });\n }\n if (inventory.gateway) {\n const gateway = gatewayRelationships(service, inventory.gateway);\n nodes.push(...gateway.nodes);\n edges.push(...gateway.edges);\n omitted += gateway.omitted;\n gaps.push(...inventory.gateway.gaps);\n }\n const pods = diagnostics.matchingPods?.filter((pod) => !focusedPod || pod.metadata.name === focusedPod) ?? [];\n omitted += Math.max(0, pods.length - 8);\n for (const pod of pods.slice(0, 8)) {\n const ready = pod.status?.conditions?.find((c) => c.type === \"Ready\");\n const id = add({\n id: `pod/${pod.metadata.uid ?? pod.metadata.name}`,\n kind: \"Pod\",\n name: pod.metadata.name ?? \"Unnamed\",\n role: \"backend\",\n detail:\n pod.status?.phase === \"Succeeded\"\n ? \"Completed\"\n : pod.status?.phase === \"Failed\"\n ? \"Failed\"\n : pod.metadata.deletionTimestamp\n ? \"Terminating\"\n : pod.status?.phase === \"Pending\"\n ? \"Pending\"\n : ready\n ? `Ready: ${ready.status}`\n : \"Readiness unknown\",\n tone:\n pod.status?.phase === \"Succeeded\"\n ? \"healthy\"\n : pod.status?.phase === \"Failed\" ||\n pod.status?.phase === \"Pending\" ||\n pod.metadata.deletionTimestamp ||\n ready?.status === \"False\"\n ? \"warning\"\n : ready?.status === \"True\"\n ? \"healthy\"\n : \"neutral\",\n selfLink: pod.selfLink,\n });\n edges.push({ from: svc, to: id, label: \"selector matches\" });\n if (pod.spec?.nodeName) {\n const node = add({\n id: `node/${pod.spec.nodeName}`,\n kind: \"Node\",\n name: pod.spec.nodeName,\n role: \"placement\",\n detail: \"Assignment reference; node health not queried\",\n tone: \"neutral\",\n selfLink: relationshipReferencePath(\"Node\", pod.spec.nodeName),\n inferred: true,\n });\n edges.push({ from: id, to: node, label: \"assigned to\" });\n }\n let child = id;\n let refs = pod.metadata.ownerReferences;\n const visited = new Set<string>();\n for (let depth = 0; depth < 4; depth++) {\n const ref = refs?.find((r) => r.controller) ?? (refs?.length === 1 ? refs[0] : undefined);\n if (!ref || visited.has(ref.uid)) break;\n visited.add(ref.uid);\n const owner = inventory.owners?.find((o) => o.metadata.uid === ref.uid && o.metadata.namespace === namespace);\n const ownerId = add({\n id: `owner/${ref.uid}`,\n kind: ref.kind ?? \"Owner\",\n name: ref.name ?? \"Unnamed\",\n role: \"placement\",\n detail: owner ? \"Controller object observed\" : \"Owner reference only; object not observed\",\n tone: \"neutral\",\n selfLink:\n owner?.selfLink ??\n relationshipReferencePath(ref.kind ?? \"\", ref.name ?? \"\", namespace) ??\n relationshipReferencePath(ref.kind ?? \"\", ref.name ?? \"\"),\n inferred: !owner,\n });\n edges.push({ from: child, to: ownerId, label: \"owned by\" });\n child = ownerId;\n refs = owner?.metadata.ownerReferences;\n }\n }\n const slices = diagnostics.matchingSlices ?? [];\n omitted += Math.max(0, slices.length - 5);\n for (const slice of slices.slice(0, 5)) {\n const ready = slice.endpoints?.filter((e) => e.conditions?.ready !== false && !e.conditions?.terminating).length;\n const id = add({\n id: `slice/${slice.metadata.name}`,\n kind: \"EndpointSlice\",\n name: slice.metadata.name ?? \"Unnamed\",\n role: \"backend\",\n detail: `${slice.addressType} ยท ${ready ?? \"unknown\"} ready endpoint entries`,\n tone: ready === 0 ? \"warning\" : \"neutral\",\n selfLink: slice.selfLink,\n });\n edges.push({ from: svc, to: id, label: \"published in\" });\n }\n if (!external && !inventory.pods) gaps.push(\"Pod inventory unavailable; selector relationships are unknown\");\n if (!inventory.slices && !diagnostics.external)\n gaps.push(\"EndpointSlice inventory unavailable; published backends are unknown\");\n if (!inventory.ingresses) gaps.push(\"Ingress inventory unavailable; upstream references are unknown\");\n return {\n nodes,\n edges: [...new Map(edges.map((edge) => [JSON.stringify([edge.from, edge.to, edge.label]), edge])).values()],\n omitted,\n gaps: [...new Set(gaps)],\n diagnostics,\n podOptions: diagnostics.matchingPods?.map((p) => p.metadata.name).filter((name): name is string => !!name) ?? [],\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resource-relationships.js","names":[],"sources":["../../../../src/common/resource-relationships.ts"],"sourcesContent":["import { type GatewayInventory, gatewayRelationships } from \"./gateway-relationships\";\nimport { incidentResourcePath } from \"./incident-summary\";\nimport {\n diagnoseService,\n type ServiceDiagnosticObject,\n type ServiceDiagnosticPod,\n type ServiceDiagnosticSlice,\n} from \"./service-diagnostics\";\n\nexport interface RelationshipMetadata {\n uid?: string;\n name?: string;\n namespace?: string;\n ownerReferences?: { uid: string; name?: string; kind?: string; controller?: boolean }[];\n}\nexport interface RelationshipObject {\n kind?: string;\n metadata: RelationshipMetadata;\n selfLink?: string;\n}\nexport interface RelationshipPod extends ServiceDiagnosticPod {\n metadata: ServiceDiagnosticPod[\"metadata\"] & RelationshipMetadata;\n spec?: ServiceDiagnosticPod[\"spec\"] & { nodeName?: string };\n status?: ServiceDiagnosticPod[\"status\"] & { phase?: string };\n selfLink?: string;\n}\nexport interface RelationshipSlice extends ServiceDiagnosticSlice {\n selfLink?: string;\n}\nexport interface RelationshipIngress extends RelationshipObject {\n spec?: {\n defaultBackend?: { service?: { name: string } };\n backend?: { serviceName?: string };\n rules?: { http?: { paths?: { backend?: { service?: { name: string }; serviceName?: string } }[] } }[];\n };\n}\nexport interface RelationshipNode {\n id: string;\n kind: string;\n name: string;\n role: \"upstream\" | \"service\" | \"backend\" | \"placement\";\n detail: string;\n evidence?: string[];\n tone: \"neutral\" | \"warning\" | \"healthy\";\n selfLink?: string;\n inferred?: boolean;\n}\nexport interface RelationshipEdge {\n from: string;\n to: string;\n label: string;\n evidence?: string;\n}\nexport interface RelationshipInventory {\n pods?: RelationshipPod[];\n slices?: RelationshipSlice[];\n ingresses?: RelationshipIngress[];\n owners?: RelationshipObject[];\n gaps?: string[];\n gateway?: GatewayInventory;\n}\nexport function relationshipReferencePath(kind: string, name: string, namespace?: string) {\n return incidentResourcePath({ kind, name, namespace });\n}\nexport function buildResourceRelationships(\n service: ServiceDiagnosticObject & { selfLink?: string },\n inventory: RelationshipInventory,\n focusedPod?: string,\n showAll = false,\n) {\n const external = service.spec?.type === \"ExternalName\";\n const diagnostics = diagnoseService(service, external ? [] : inventory.pods, external ? [] : inventory.slices);\n const nodes: RelationshipNode[] = [];\n const edges: RelationshipEdge[] = [];\n const gaps = [...(inventory.gaps ?? [])];\n let omitted = 0;\n const upstreamLimit = showAll ? Infinity : 5;\n const podLimit = showAll ? Infinity : 8;\n const namespace = service.metadata.namespace;\n const add = (node: RelationshipNode) => {\n if (!nodes.some((n) => n.id === node.id)) nodes.push(node);\n return node.id;\n };\n const svc = add({\n id: \"service\",\n kind: \"Service\",\n name: service.metadata.name ?? \"Unnamed\",\n role: \"service\",\n detail: diagnostics.summary,\n tone: \"neutral\",\n selfLink: service.selfLink,\n });\n const upstream =\n inventory.ingresses?.filter(\n (ingress) =>\n ingress.metadata.namespace === namespace &&\n [\n ingress.spec?.defaultBackend?.service?.name,\n ingress.spec?.backend?.serviceName,\n ...(ingress.spec?.rules ?? []).flatMap((rule) =>\n (rule.http?.paths ?? []).map((path) => path.backend?.service?.name ?? path.backend?.serviceName),\n ),\n ].includes(service.metadata.name),\n ) ?? [];\n omitted += Math.max(0, upstream.length - upstreamLimit);\n for (const ingress of upstream.slice(0, upstreamLimit)) {\n const id = add({\n id: `ingress/${ingress.metadata.uid ?? ingress.metadata.name}`,\n kind: \"Ingress\",\n name: ingress.metadata.name ?? \"Unnamed\",\n role: \"upstream\",\n detail: \"Backend Service reference\",\n tone: \"neutral\",\n selfLink: ingress.selfLink,\n });\n edges.push({ from: id, to: svc, label: \"references backend\" });\n }\n if (inventory.gateway) {\n const gateway = gatewayRelationships(service, inventory.gateway, showAll);\n nodes.push(...gateway.nodes);\n edges.push(...gateway.edges);\n omitted += gateway.omitted;\n gaps.push(...inventory.gateway.gaps);\n }\n const pods = diagnostics.matchingPods?.filter((pod) => !focusedPod || pod.metadata.name === focusedPod) ?? [];\n omitted += Math.max(0, pods.length - podLimit);\n for (const pod of pods.slice(0, podLimit)) {\n const ready = pod.status?.conditions?.find((c) => c.type === \"Ready\");\n const id = add({\n id: `pod/${pod.metadata.uid ?? pod.metadata.name}`,\n kind: \"Pod\",\n name: pod.metadata.name ?? \"Unnamed\",\n role: \"backend\",\n detail:\n pod.status?.phase === \"Succeeded\"\n ? \"Completed\"\n : pod.status?.phase === \"Failed\"\n ? \"Failed\"\n : pod.metadata.deletionTimestamp\n ? \"Terminating\"\n : pod.status?.phase === \"Pending\"\n ? \"Pending\"\n : ready\n ? `Ready: ${ready.status}`\n : \"Readiness unknown\",\n tone:\n pod.status?.phase === \"Succeeded\"\n ? \"healthy\"\n : pod.status?.phase === \"Failed\" ||\n pod.status?.phase === \"Pending\" ||\n pod.metadata.deletionTimestamp ||\n ready?.status === \"False\"\n ? \"warning\"\n : ready?.status === \"True\"\n ? \"healthy\"\n : \"neutral\",\n selfLink: pod.selfLink,\n });\n edges.push({ from: svc, to: id, label: \"selector matches\" });\n if (pod.spec?.nodeName) {\n const node = add({\n id: `node/${pod.spec.nodeName}`,\n kind: \"Node\",\n name: pod.spec.nodeName,\n role: \"placement\",\n detail: \"Assignment reference; node health not queried\",\n tone: \"neutral\",\n selfLink: relationshipReferencePath(\"Node\", pod.spec.nodeName),\n inferred: true,\n });\n edges.push({ from: id, to: node, label: \"assigned to\" });\n }\n let child = id;\n let refs = pod.metadata.ownerReferences;\n const visited = new Set<string>();\n for (let depth = 0; depth < 4; depth++) {\n const ref = refs?.find((r) => r.controller) ?? (refs?.length === 1 ? refs[0] : undefined);\n if (!ref || visited.has(ref.uid)) break;\n visited.add(ref.uid);\n const owner = inventory.owners?.find((o) => o.metadata.uid === ref.uid && o.metadata.namespace === namespace);\n const ownerId = add({\n id: `owner/${ref.uid}`,\n kind: ref.kind ?? \"Owner\",\n name: ref.name ?? \"Unnamed\",\n role: \"placement\",\n detail: owner ? \"Controller object observed\" : \"Owner reference only; object not observed\",\n tone: \"neutral\",\n selfLink:\n owner?.selfLink ??\n relationshipReferencePath(ref.kind ?? \"\", ref.name ?? \"\", namespace) ??\n relationshipReferencePath(ref.kind ?? \"\", ref.name ?? \"\"),\n inferred: !owner,\n });\n edges.push({ from: child, to: ownerId, label: \"owned by\" });\n child = ownerId;\n refs = owner?.metadata.ownerReferences;\n }\n }\n const slices = diagnostics.matchingSlices ?? [];\n omitted += Math.max(0, slices.length - upstreamLimit);\n for (const slice of slices.slice(0, upstreamLimit)) {\n const ready = slice.endpoints?.filter((e) => e.conditions?.ready !== false && !e.conditions?.terminating).length;\n const id = add({\n id: `slice/${slice.metadata.name}`,\n kind: \"EndpointSlice\",\n name: slice.metadata.name ?? \"Unnamed\",\n role: \"backend\",\n detail: `${slice.addressType} ยท ${ready ?? \"unknown\"} ready endpoint entries`,\n tone: ready === 0 ? \"warning\" : \"neutral\",\n selfLink: slice.selfLink,\n });\n edges.push({ from: svc, to: id, label: \"published in\" });\n }\n if (!external && !inventory.pods) gaps.push(\"Pod inventory unavailable; selector relationships are unknown\");\n if (!inventory.slices && !diagnostics.external)\n gaps.push(\"EndpointSlice inventory unavailable; published backends are unknown\");\n if (!inventory.ingresses) gaps.push(\"Ingress inventory unavailable; upstream references are unknown\");\n return {\n nodes,\n edges: [...new Map(edges.map((edge) => [JSON.stringify([edge.from, edge.to, edge.label]), edge])).values()],\n omitted,\n gaps: [...new Set(gaps)],\n diagnostics,\n podOptions: diagnostics.matchingPods?.map((p) => p.metadata.name).filter((name): name is string => !!name) ?? [],\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-search.js","names":[],"sources":["../../../../src/common/resource-search.ts"],"sourcesContent":["export const RESOURCE_SEARCH_CHANNEL = \"freelens-power:resource-search\";\nexport interface SearchDocument {\n kind: string;\n name: string;\n namespace?: string;\n labels: Record<string, string>;\n path: string;\n group: string;\n version: string;\n resource: string;\n uid?: string;\n aliases?: string[];\n fields?: { label: string; text: string }[];\n}\nexport interface SearchQueryResult {\n documents: SearchDocument[];\n total: number;\n hasMore: boolean;\n}\nexport interface SearchIndexStatus {\n count: number;\n bytes: number;\n available: boolean;\n}\nexport type SearchIndexRequest = { clusterId: string; contextId: string } & (\n | { operation: \"begin\"; scope: string; scanId: string }\n | { operation: \"complete\"; scope: string; scanId: string }\n | { operation: \"append\"; scope: string; scanId: string; documents: SearchDocument[] }\n | { operation: \"invalidate\"; scope: string }\n | { operation: \"reconcile\"; resources: string[] }\n | { operation: \"permit\" }\n | { operation: \"query\"; query: string; limit?: number; kind?: string; namespace?: string }\n | { operation: \"status\" }\n | { operation: \"clear\" }\n);\n\nconst identifier = (value: unknown, maximum = 253): value is string =>\n typeof value === \"string\" && value.length > 0 && value.length <= maximum && /^[a-zA-Z0-9_.:-]+$/.test(value);\nconst objectName = (value: unknown): value is string =>\n typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= 1024 &&\n value !== \".\" &&\n value !== \"..\" &&\n !/[/%\\x00-\\x1f\\x7f]/.test(value);\n\nexport function validSearchPartition(value: unknown): value is { clusterId: string; contextId: string } {\n const request = value as SearchIndexRequest;\n return !!request && identifier(request.clusterId, 128) && identifier(request.contextId, 256);\n}\n\nexport function projectSearchDocuments(input: unknown): SearchDocument[] {\n if (!Array.isArray(input) || input.length > 500) throw new Error(\"Invalid search batch\");\n return input.map((document: SearchDocument) => {\n if (\n !document ||\n !identifier(document.kind) ||\n !objectName(document.name) ||\n !identifier(document.version) ||\n !identifier(document.resource) ||\n !(document.group === \"\" || identifier(document.group)) ||\n !(document.namespace === undefined || identifier(document.namespace)) ||\n !(document.uid === undefined || identifier(document.uid, 128))\n )\n throw new Error(\"Invalid search identity\");\n const path = `${document.group ? `/apis/${encodeURIComponent(document.group)}` : \"/api\"}/${encodeURIComponent(document.version)}/${document.namespace ? `namespaces/${encodeURIComponent(document.namespace)}/` : \"\"}${encodeURIComponent(document.resource)}/${encodeURIComponent(document.name)}`;\n if (document.path !== path) throw new Error(\"Invalid search path\");\n const labels = Object.fromEntries(\n Object.entries(document.labels ?? {})\n .slice(0, 64)\n .filter(([key, value]) => key.length <= 253 && typeof value === \"string\" && value.length <= 256),\n );\n const reviewedCollections = new Set([\n \"/pods\",\n \"/nodes\",\n \"/services\",\n \"/namespaces\",\n \"apps/deployments\",\n \"apps/statefulsets\",\n \"apps/daemonsets\",\n \"apps/replicasets\",\n \"batch/jobs\",\n \"batch/cronjobs\",\n \"networking.k8s.io/ingresses\",\n \"autoscaling/horizontalpodautoscalers\",\n ]);\n const fields = reviewedCollections.has(`${document.group}/${document.resource}`)\n ? (document.fields ?? [])\n .slice(0, 32)\n .flatMap((field) =>\n field && typeof field.label === \"string\" && typeof field.text === \"string\"\n ? [{ label: field.label.slice(0, 64), text: field.text.slice(0, 512) }]\n : [],\n )\n : [];\n let remaining = 4096;\n return {\n kind: document.kind,\n name: document.name,\n namespace: document.namespace,\n labels,\n path,\n group: document.group,\n version: document.version,\n resource: document.resource,\n uid: document.uid,\n aliases: Array.isArray(document.aliases)\n ? document.aliases\n .filter((alias) => typeof alias === \"string\" && /^[a-zA-Z0-9_.:-]{1,128}$/.test(alias))\n .slice(0, 32)\n : [],\n fields: fields.flatMap((field) => {\n const text = field.text.slice(0, Math.max(0, remaining));\n remaining -= text.length;\n return text ? [{ label: field.label, text }] : [];\n }),\n };\n });\n}\n"],"mappings":";AAAA,IAAa,0BAA0B"}
|
|
1
|
+
{"version":3,"file":"resource-search.js","names":[],"sources":["../../../../src/common/resource-search.ts"],"sourcesContent":["export const RESOURCE_SEARCH_CHANNEL = \"freelens-power:resource-search\";\nexport interface SearchDocument {\n kind: string;\n name: string;\n namespace?: string;\n labels: Record<string, string>;\n path: string;\n group: string;\n version: string;\n resource: string;\n uid?: string;\n aliases?: string[];\n fields?: { label: string; text: string }[];\n}\nexport interface SearchQueryResult {\n documents: SearchDocument[];\n total: number;\n hasMore: boolean;\n}\nexport interface SearchIndexStatus {\n count: number;\n bytes: number;\n available: boolean;\n}\nexport type SearchIndexRequest = { clusterId: string; contextId: string } & (\n | { operation: \"begin\"; scope: string; scanId: string }\n | { operation: \"complete\"; scope: string; scanId: string }\n | { operation: \"append\"; scope: string; scanId: string; documents: SearchDocument[] }\n | { operation: \"invalidate\"; scope: string }\n | { operation: \"reconcile\"; resources: string[] }\n | { operation: \"permit\" }\n | { operation: \"query\"; query: string; limit?: number; offset?: number; kind?: string; namespace?: string }\n | { operation: \"status\" }\n | { operation: \"clear\" }\n);\n\nconst identifier = (value: unknown, maximum = 253): value is string =>\n typeof value === \"string\" && value.length > 0 && value.length <= maximum && /^[a-zA-Z0-9_.:-]+$/.test(value);\nconst objectName = (value: unknown): value is string =>\n typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= 1024 &&\n value !== \".\" &&\n value !== \"..\" &&\n !/[/%\\x00-\\x1f\\x7f]/.test(value);\n\nexport function validSearchPartition(value: unknown): value is { clusterId: string; contextId: string } {\n const request = value as SearchIndexRequest;\n return !!request && identifier(request.clusterId, 128) && identifier(request.contextId, 256);\n}\n\nexport function projectSearchDocuments(input: unknown): SearchDocument[] {\n if (!Array.isArray(input) || input.length > 500) throw new Error(\"Invalid search batch\");\n return input.map((document: SearchDocument) => {\n if (\n !document ||\n !identifier(document.kind) ||\n !objectName(document.name) ||\n !identifier(document.version) ||\n !identifier(document.resource) ||\n !(document.group === \"\" || identifier(document.group)) ||\n !(document.namespace === undefined || identifier(document.namespace)) ||\n !(document.uid === undefined || identifier(document.uid, 128))\n )\n throw new Error(\"Invalid search identity\");\n const path = `${document.group ? `/apis/${encodeURIComponent(document.group)}` : \"/api\"}/${encodeURIComponent(document.version)}/${document.namespace ? `namespaces/${encodeURIComponent(document.namespace)}/` : \"\"}${encodeURIComponent(document.resource)}/${encodeURIComponent(document.name)}`;\n if (document.path !== path) throw new Error(\"Invalid search path\");\n const labels = Object.fromEntries(\n Object.entries(document.labels ?? {})\n .slice(0, 64)\n .filter(([key, value]) => key.length <= 253 && typeof value === \"string\" && value.length <= 256),\n );\n const reviewedCollections = new Set([\n \"/pods\",\n \"/nodes\",\n \"/services\",\n \"/namespaces\",\n \"apps/deployments\",\n \"apps/statefulsets\",\n \"apps/daemonsets\",\n \"apps/replicasets\",\n \"batch/jobs\",\n \"batch/cronjobs\",\n \"networking.k8s.io/ingresses\",\n \"autoscaling/horizontalpodautoscalers\",\n ]);\n const fields = reviewedCollections.has(`${document.group}/${document.resource}`)\n ? (document.fields ?? [])\n .slice(0, 32)\n .flatMap((field) =>\n field && typeof field.label === \"string\" && typeof field.text === \"string\"\n ? [{ label: field.label.slice(0, 64), text: field.text.slice(0, 512) }]\n : [],\n )\n : [];\n let remaining = 4096;\n return {\n kind: document.kind,\n name: document.name,\n namespace: document.namespace,\n labels,\n path,\n group: document.group,\n version: document.version,\n resource: document.resource,\n uid: document.uid,\n aliases: Array.isArray(document.aliases)\n ? document.aliases\n .filter((alias) => typeof alias === \"string\" && /^[a-zA-Z0-9_.:-]{1,128}$/.test(alias))\n .slice(0, 32)\n : [],\n fields: fields.flatMap((field) => {\n const text = field.text.slice(0, Math.max(0, remaining));\n remaining -= text.length;\n return text ? [{ label: field.label, text }] : [];\n }),\n };\n });\n}\n"],"mappings":";AAAA,IAAa,0BAA0B"}
|
package/out/renderer/welcome.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
|
|
2
|
+
const require__global_external_react = require("./_virtual/_global-external_react.js");
|
|
3
|
+
const require__global_external_react_dom = require("./_virtual/_global-external_react-dom.js");
|
|
1
4
|
//#region src/renderer/welcome.ts
|
|
5
|
+
var nextWelcomeFeatureId = 0;
|
|
2
6
|
var WELCOME_SELECTOR = ".Welcome [data-testid=\"welcome-text-container\"]";
|
|
3
7
|
var HOST_ATTRIBUTE = "data-freelens-power-welcome";
|
|
4
8
|
var CONTENT_ATTRIBUTE = "data-freelens-power-welcome-content";
|
|
@@ -37,7 +41,8 @@ function createWelcomeContent() {
|
|
|
37
41
|
const feature = document.createElement("li");
|
|
38
42
|
feature.setAttribute("role", "listitem");
|
|
39
43
|
feature.textContent = labels[index];
|
|
40
|
-
feature.
|
|
44
|
+
feature.id = `power-welcome-feature-${++nextWelcomeFeatureId}`;
|
|
45
|
+
feature.dataset.powerTooltip = text;
|
|
41
46
|
features.append(feature);
|
|
42
47
|
}
|
|
43
48
|
content.append(features);
|
|
@@ -80,6 +85,8 @@ function startWelcomeOverride() {
|
|
|
80
85
|
const restore = (host) => {
|
|
81
86
|
const entry = hosts.get(host);
|
|
82
87
|
if (!entry) return;
|
|
88
|
+
const tooltipRoot = entry.content.querySelector("[data-power-welcome-tooltips]");
|
|
89
|
+
if (tooltipRoot) require__global_external_react_dom.default.unmountComponentAtNode(tooltipRoot);
|
|
83
90
|
entry.content.remove();
|
|
84
91
|
if (entry.previousAttribute === null) host.removeAttribute(HOST_ATTRIBUTE);
|
|
85
92
|
else host.setAttribute(HOST_ATTRIBUTE, entry.previousAttribute);
|
|
@@ -94,6 +101,14 @@ function startWelcomeOverride() {
|
|
|
94
101
|
});
|
|
95
102
|
host.setAttribute(HOST_ATTRIBUTE, "");
|
|
96
103
|
host.prepend(content);
|
|
104
|
+
const tooltipRoot = document.createElement("div");
|
|
105
|
+
tooltipRoot.setAttribute("data-power-welcome-tooltips", "");
|
|
106
|
+
tooltipRoot.style.display = "none";
|
|
107
|
+
content.append(tooltipRoot);
|
|
108
|
+
require__global_external_react_dom.default.render(require__global_external_react.createElement("div", null, ...Array.from(content.querySelectorAll("[data-power-tooltip]")).map((feature) => require__global_external_react.createElement(require_extensions.Renderer.Component.Tooltip, {
|
|
109
|
+
key: feature.id,
|
|
110
|
+
targetId: feature.id
|
|
111
|
+
}, feature.dataset.powerTooltip))), tooltipRoot);
|
|
97
112
|
};
|
|
98
113
|
const scan = (node) => {
|
|
99
114
|
if (node.matches(WELCOME_SELECTOR)) attach(node);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"welcome.js","names":[],"sources":["../../src/renderer/welcome.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\nconst WELCOME_SELECTOR = '.Welcome [data-testid=\"welcome-text-container\"]';\nconst HOST_ATTRIBUTE = \"data-freelens-power-welcome\";\nconst CONTENT_ATTRIBUTE = \"data-freelens-power-welcome-content\";\n\nfunction createWelcomeContent(): HTMLDivElement {\n const content = document.createElement(\"div\");\n content.setAttribute(CONTENT_ATTRIBUTE, \"\");\n\n const heading = document.createElement(\"h2\");\n heading.textContent = \"๐ Freelens Power\";\n content.append(heading);\n\n const introduction = document.createElement(\"p\");\n introduction.textContent = \"Make Freelens your own with practical tools for everyday Kubernetes work.\";\n content.append(introduction);\n\n const features = document.createElement(\"ul\");\n features.className = \"freelens-power-features\";\n features.setAttribute(\"role\", \"list\");\n\n const labels = [\n \"๐ Secure Node Shell with IPv6 and Colima\",\n \"๐ Certificate details in Secrets\",\n \"๐จ Themes and cluster icon studio\",\n \"๐ Website cards with pinning and refresh\",\n \"๐ CPU/RAM metrics with local history\",\n \"๐ Resource usage, timelines and Pending\",\n \"๐ค AI chat with context and read-only tools\",\n \"๐ Read-only controls for Kubernetes\",\n ];\n for (const [index, text] of [\n \"๐ Secure Node Shell with custom commands, IPv6 and automatic Colima support\",\n \"๐ Certificate details in Secrets\",\n \"๐จ Accent themes and matching custom cluster icons\",\n \"๐ Website dashboards with flexible cards, pinned windows and background refresh\",\n \"๐ Cluster, node and pod metrics with optional local history\",\n \"๐ Resource consumers, application timelines and Pending diagnostics\",\n \"๐ค Context-aware AI chat with read-only tools and prompt shortcuts\",\n \"๐ Read-only UI controls for safer browsing\",\n ].entries()) {\n const feature = document.createElement(\"li\");\n feature.setAttribute(\"role\", \"listitem\");\n feature.textContent = labels[index];\n feature.title = text;\n features.append(feature);\n }\n\n content.append(features);\n\n const paragraph = document.createElement(\"p\");\n const link = document.createElement(\"a\");\n link.className = \"link\";\n link.href = \"https://github.com/64mb/freelens-power\";\n link.target = \"_blank\";\n link.rel = \"noopener noreferrer\";\n link.textContent = \"Freelens Power on GitHub\";\n paragraph.append(link);\n content.append(paragraph);\n\n return content;\n}\n\nexport function startWelcomeOverride(): () => void {\n const style = document.createElement(\"style\");\n style.setAttribute(\"data-freelens-power\", \"welcome\");\n style.textContent = `\n .Welcome [${HOST_ATTRIBUTE}] > h2,\n .Welcome [${HOST_ATTRIBUTE}] > p { display: none; }\n .Welcome [${CONTENT_ATTRIBUTE}] { color: var(--textColorSecondary); }\n .Welcome [${CONTENT_ATTRIBUTE}] h2 { color: var(--textColorPrimary); margin: 15px 0 16px; }\n .Welcome [${CONTENT_ATTRIBUTE}] .freelens-power-features { display: grid; grid-template-columns: repeat(2, minmax(0, max-content)); justify-content: center; gap: 14px 48px; list-style: none; padding: 16px 0; margin: 16px 0; border-top: 1px solid var(--borderColor); border-bottom: 1px solid var(--borderColor); text-align: left; line-height: 1.5; }\n .Welcome [${CONTENT_ATTRIBUTE}] .freelens-power-features > li, .Welcome [${CONTENT_ATTRIBUTE}] .freelens-power-features > li:hover { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin: 0; padding: 0; background: none; border: 0; border-radius: 0; box-shadow: none; cursor: default; color: inherit; text-decoration: none; transform: none; transition: none; }\n .Welcome [data-testid=\"welcome-banner-container\"]:has([${HOST_ATTRIBUTE}]) { width: min(960px, calc(100% - 48px)) !important; }\n .Welcome [${HOST_ATTRIBUTE}] { width: 100% !important; max-width: 100%; }\n .Welcome [data-testid=\"no-welcome-banners-icon\"] { display: inline-flex; width: 120px; height: 120px; }\n .Welcome [data-testid=\"no-welcome-banners-icon\"] svg { width: 100%; height: 100%; }\n .Welcome [${HOST_ATTRIBUTE}] [data-testid=\"welcome-menu-container\"] { width: fit-content !important; margin-inline: auto; max-width: 100%; }\n .Welcome [${HOST_ATTRIBUTE}] [data-testid=\"welcome-menu-container\"] > li { display: flex; align-items: center; gap: 8px; padding: 8px 12px; }\n .Welcome [${HOST_ATTRIBUTE}] [data-testid=\"welcome-menu-container\"] .box { flex: 0 0 auto; width: auto; padding: 0; }\n .Welcome [${HOST_ATTRIBUTE}] [data-testid=\"welcome-menu-container\"] a { margin-left: 0; }\n @media (max-width: 640px) {\n .Welcome [${CONTENT_ATTRIBUTE}] .freelens-power-features { grid-template-columns: 1fr; }\n }\n .Welcome [${CONTENT_ATTRIBUTE}] p { margin-top: 14px; }\n `;\n document.head.append(style);\n\n const hosts = new Map<Element, { content: HTMLDivElement; previousAttribute: string | null }>();\n\n const restore = (host: Element) => {\n const entry = hosts.get(host);\n if (!entry) return;\n\n entry.content.remove();\n if (entry.previousAttribute === null) {\n host.removeAttribute(HOST_ATTRIBUTE);\n } else {\n host.setAttribute(HOST_ATTRIBUTE, entry.previousAttribute);\n }\n hosts.delete(host);\n };\n\n const attach = (host: Element) => {\n if (hosts.has(host)) return;\n\n const content = createWelcomeContent();\n hosts.set(host, { content, previousAttribute: host.getAttribute(HOST_ATTRIBUTE) });\n host.setAttribute(HOST_ATTRIBUTE, \"\");\n host.prepend(content);\n };\n\n const scan = (node: Element) => {\n if (node.matches(WELCOME_SELECTOR)) attach(node);\n for (const host of node.querySelectorAll(WELCOME_SELECTOR)) attach(host);\n };\n\n scan(document.body);\n\n const observer = new MutationObserver((mutations) => {\n for (const host of hosts.keys()) {\n if (!host.isConnected) restore(host);\n }\n\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (node instanceof Element && node.isConnected) scan(node);\n }\n }\n });\n observer.observe(document.body, { childList: true, subtree: true });\n\n return () => {\n observer.disconnect();\n for (const host of hosts.keys()) restore(host);\n style.remove();\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"welcome.js","names":[],"sources":["../../src/renderer/welcome.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { Renderer } from \"@freelensapp/extensions\";\nimport { createElement } from \"react\";\nimport ReactDOM from \"react-dom\";\n\nlet nextWelcomeFeatureId = 0;\n\nconst WELCOME_SELECTOR = '.Welcome [data-testid=\"welcome-text-container\"]';\nconst HOST_ATTRIBUTE = \"data-freelens-power-welcome\";\nconst CONTENT_ATTRIBUTE = \"data-freelens-power-welcome-content\";\n\nfunction createWelcomeContent(): HTMLDivElement {\n const content = document.createElement(\"div\");\n content.setAttribute(CONTENT_ATTRIBUTE, \"\");\n\n const heading = document.createElement(\"h2\");\n heading.textContent = \"๐ Freelens Power\";\n content.append(heading);\n\n const introduction = document.createElement(\"p\");\n introduction.textContent = \"Make Freelens your own with practical tools for everyday Kubernetes work.\";\n content.append(introduction);\n\n const features = document.createElement(\"ul\");\n features.className = \"freelens-power-features\";\n features.setAttribute(\"role\", \"list\");\n\n const labels = [\n \"๐ Secure Node Shell with IPv6 and Colima\",\n \"๐ Certificate details in Secrets\",\n \"๐จ Themes and cluster icon studio\",\n \"๐ Website cards with pinning and refresh\",\n \"๐ CPU/RAM metrics with local history\",\n \"๐ Resource usage, timelines and Pending\",\n \"๐ค AI chat with context and read-only tools\",\n \"๐ Read-only controls for Kubernetes\",\n ];\n for (const [index, text] of [\n \"๐ Secure Node Shell with custom commands, IPv6 and automatic Colima support\",\n \"๐ Certificate details in Secrets\",\n \"๐จ Accent themes and matching custom cluster icons\",\n \"๐ Website dashboards with flexible cards, pinned windows and background refresh\",\n \"๐ Cluster, node and pod metrics with optional local history\",\n \"๐ Resource consumers, application timelines and Pending diagnostics\",\n \"๐ค Context-aware AI chat with read-only tools and prompt shortcuts\",\n \"๐ Read-only UI controls for safer browsing\",\n ].entries()) {\n const feature = document.createElement(\"li\");\n feature.setAttribute(\"role\", \"listitem\");\n feature.textContent = labels[index];\n feature.id = `power-welcome-feature-${++nextWelcomeFeatureId}`;\n feature.dataset.powerTooltip = text;\n features.append(feature);\n }\n\n content.append(features);\n\n const paragraph = document.createElement(\"p\");\n const link = document.createElement(\"a\");\n link.className = \"link\";\n link.href = \"https://github.com/64mb/freelens-power\";\n link.target = \"_blank\";\n link.rel = \"noopener noreferrer\";\n link.textContent = \"Freelens Power on GitHub\";\n paragraph.append(link);\n content.append(paragraph);\n\n return content;\n}\n\nexport function startWelcomeOverride(): () => void {\n const style = document.createElement(\"style\");\n style.setAttribute(\"data-freelens-power\", \"welcome\");\n style.textContent = `\n .Welcome [${HOST_ATTRIBUTE}] > h2,\n .Welcome [${HOST_ATTRIBUTE}] > p { display: none; }\n .Welcome [${CONTENT_ATTRIBUTE}] { color: var(--textColorSecondary); }\n .Welcome [${CONTENT_ATTRIBUTE}] h2 { color: var(--textColorPrimary); margin: 15px 0 16px; }\n .Welcome [${CONTENT_ATTRIBUTE}] .freelens-power-features { display: grid; grid-template-columns: repeat(2, minmax(0, max-content)); justify-content: center; gap: 14px 48px; list-style: none; padding: 16px 0; margin: 16px 0; border-top: 1px solid var(--borderColor); border-bottom: 1px solid var(--borderColor); text-align: left; line-height: 1.5; }\n .Welcome [${CONTENT_ATTRIBUTE}] .freelens-power-features > li, .Welcome [${CONTENT_ATTRIBUTE}] .freelens-power-features > li:hover { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin: 0; padding: 0; background: none; border: 0; border-radius: 0; box-shadow: none; cursor: default; color: inherit; text-decoration: none; transform: none; transition: none; }\n .Welcome [data-testid=\"welcome-banner-container\"]:has([${HOST_ATTRIBUTE}]) { width: min(960px, calc(100% - 48px)) !important; }\n .Welcome [${HOST_ATTRIBUTE}] { width: 100% !important; max-width: 100%; }\n .Welcome [data-testid=\"no-welcome-banners-icon\"] { display: inline-flex; width: 120px; height: 120px; }\n .Welcome [data-testid=\"no-welcome-banners-icon\"] svg { width: 100%; height: 100%; }\n .Welcome [${HOST_ATTRIBUTE}] [data-testid=\"welcome-menu-container\"] { width: fit-content !important; margin-inline: auto; max-width: 100%; }\n .Welcome [${HOST_ATTRIBUTE}] [data-testid=\"welcome-menu-container\"] > li { display: flex; align-items: center; gap: 8px; padding: 8px 12px; }\n .Welcome [${HOST_ATTRIBUTE}] [data-testid=\"welcome-menu-container\"] .box { flex: 0 0 auto; width: auto; padding: 0; }\n .Welcome [${HOST_ATTRIBUTE}] [data-testid=\"welcome-menu-container\"] a { margin-left: 0; }\n @media (max-width: 640px) {\n .Welcome [${CONTENT_ATTRIBUTE}] .freelens-power-features { grid-template-columns: 1fr; }\n }\n .Welcome [${CONTENT_ATTRIBUTE}] p { margin-top: 14px; }\n `;\n document.head.append(style);\n\n const hosts = new Map<Element, { content: HTMLDivElement; previousAttribute: string | null }>();\n\n const restore = (host: Element) => {\n const entry = hosts.get(host);\n if (!entry) return;\n\n const tooltipRoot = entry.content.querySelector(\"[data-power-welcome-tooltips]\");\n if (tooltipRoot) ReactDOM.unmountComponentAtNode(tooltipRoot);\n entry.content.remove();\n if (entry.previousAttribute === null) {\n host.removeAttribute(HOST_ATTRIBUTE);\n } else {\n host.setAttribute(HOST_ATTRIBUTE, entry.previousAttribute);\n }\n hosts.delete(host);\n };\n\n const attach = (host: Element) => {\n if (hosts.has(host)) return;\n\n const content = createWelcomeContent();\n hosts.set(host, { content, previousAttribute: host.getAttribute(HOST_ATTRIBUTE) });\n host.setAttribute(HOST_ATTRIBUTE, \"\");\n host.prepend(content);\n const tooltipRoot = document.createElement(\"div\");\n tooltipRoot.setAttribute(\"data-power-welcome-tooltips\", \"\");\n tooltipRoot.style.display = \"none\";\n content.append(tooltipRoot);\n ReactDOM.render(\n createElement(\n \"div\",\n null,\n ...Array.from(content.querySelectorAll<HTMLElement>(\"[data-power-tooltip]\")).map((feature) =>\n createElement(\n Renderer.Component.Tooltip,\n { key: feature.id, targetId: feature.id },\n feature.dataset.powerTooltip,\n ),\n ),\n ),\n tooltipRoot,\n );\n };\n\n const scan = (node: Element) => {\n if (node.matches(WELCOME_SELECTOR)) attach(node);\n for (const host of node.querySelectorAll(WELCOME_SELECTOR)) attach(host);\n };\n\n scan(document.body);\n\n const observer = new MutationObserver((mutations) => {\n for (const host of hosts.keys()) {\n if (!host.isConnected) restore(host);\n }\n\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (node instanceof Element && node.isConnected) scan(node);\n }\n }\n });\n observer.observe(document.body, { childList: true, subtree: true });\n\n return () => {\n observer.disconnect();\n for (const host of hosts.keys()) restore(host);\n style.remove();\n };\n}\n"],"mappings":"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"}
|