diff --git a/arthas/ui-src/package.json b/arthas/ui-src/package.json index e777f2a..9f4b38e 100644 --- a/arthas/ui-src/package.json +++ b/arthas/ui-src/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@flanksource/clicky-ui": "^0.2.1", + "@flanksource/plugin-ui-sdk": "file:vendor/plugin-ui-sdk", "@tanstack/react-query": "^5.80.7", "lucide-react": "^0.545.0", "preact": "^10.24.0" diff --git a/arthas/ui-src/pnpm-lock.yaml b/arthas/ui-src/pnpm-lock.yaml index 9fbef6d..73f180d 100644 --- a/arthas/ui-src/pnpm-lock.yaml +++ b/arthas/ui-src/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@flanksource/clicky-ui': specifier: ^0.2.1 version: 0.2.1(@tanstack/react-query@5.100.9(react@19.2.5))(@vue/compiler-sfc@3.5.33)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.4)(typescript@5.9.3) + '@flanksource/plugin-ui-sdk': + specifier: file:vendor/plugin-ui-sdk + version: file:vendor/plugin-ui-sdk '@tanstack/react-query': specifier: ^5.80.7 version: 5.100.9(react@19.2.5) @@ -366,6 +369,10 @@ packages: shiki: optional: true + '@flanksource/plugin-ui-sdk@file:vendor/plugin-ui-sdk': + resolution: {directory: vendor/plugin-ui-sdk, type: directory} + engines: {node: '>=20'} + '@floating-ui/core@1.7.5': resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} @@ -2481,6 +2488,8 @@ snapshots: - typescript - universal-cookie + '@flanksource/plugin-ui-sdk@file:vendor/plugin-ui-sdk': {} + '@floating-ui/core@1.7.5': dependencies: '@floating-ui/utils': 0.2.11 diff --git a/arthas/ui-src/src/lib/api.ts b/arthas/ui-src/src/lib/api.ts index e6aa725..745fd25 100644 --- a/arthas/ui-src/src/lib/api.ts +++ b/arthas/ui-src/src/lib/api.ts @@ -1,11 +1,6 @@ -export const PLUGIN_NAME = "arthas"; +import { invoke } from "@flanksource/plugin-ui-sdk"; -function operationURL(op: string, configID: string): string { - const base = window.location.pathname.replace(/\/ui\/.*$/, ""); - const url = new URL(base + "/operations/" + op, window.location.origin); - if (configID) url.searchParams.set("config_id", configID); - return url.toString(); -} +export const PLUGIN_NAME = "arthas"; export class OpError extends Error { readonly status: number; @@ -25,13 +20,7 @@ export async function callOp( op: string, params: Record = {}, ): Promise { - const configID = configIDFromURL(); - const res = await fetch(operationURL(op, configID), { - method: "POST", - headers: { "Content-Type": "application/json" }, - credentials: "same-origin", - body: JSON.stringify(params), - }); + const res = await invoke(op, params); if (!res.ok) { const text = await res.text(); let body: unknown = text; diff --git a/arthas/ui-src/src/main.tsx b/arthas/ui-src/src/main.tsx index 44fd3fc..1dcb1ce 100644 --- a/arthas/ui-src/src/main.tsx +++ b/arthas/ui-src/src/main.tsx @@ -1,6 +1,7 @@ import { render } from "preact"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ThemeProvider, DensityProvider } from "@flanksource/clicky-ui"; +import { ready } from "@flanksource/plugin-ui-sdk"; import { App } from "./App"; import { logBanner } from "./version"; import "./styles.css"; @@ -24,3 +25,5 @@ render( , root, ); + +ready(); diff --git a/arthas/ui-src/src/vite-env.d.ts b/arthas/ui-src/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/arthas/ui-src/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/arthas/ui-src/vendor/plugin-ui-sdk/lib/index.d.ts b/arthas/ui-src/vendor/plugin-ui-sdk/lib/index.d.ts new file mode 100644 index 0000000..de9388b --- /dev/null +++ b/arthas/ui-src/vendor/plugin-ui-sdk/lib/index.d.ts @@ -0,0 +1,10 @@ +export type QueryValue = string | number | boolean | null | undefined; +export type QueryParams = Record; +export type InvokeOptions = Omit & { + query?: QueryParams; +}; +export type StreamOptions = EventSourceInit; +export declare function invoke(operation: string, body?: unknown, options?: InvokeOptions): Promise; +export declare function stream(operation: string, query?: QueryParams, options?: StreamOptions): EventSource; +export declare function ready(): void; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/arthas/ui-src/vendor/plugin-ui-sdk/lib/index.d.ts.map b/arthas/ui-src/vendor/plugin-ui-sdk/lib/index.d.ts.map new file mode 100644 index 0000000..fb4c7fa --- /dev/null +++ b/arthas/ui-src/vendor/plugin-ui-sdk/lib/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAEtE,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,UAAU,EAAE,CAAC,CAAC;AAE7E,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG;IACtD,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,eAAe,CAAC;AAS5C,wBAAsB,MAAM,CAC1B,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,QAAQ,CAAC,CAmBnB;AAED,wBAAgB,MAAM,CACpB,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,WAAW,EACnB,OAAO,CAAC,EAAE,aAAa,GACtB,WAAW,CAKb;AAED,wBAAgB,KAAK,IAAI,IAAI,CAE5B"} \ No newline at end of file diff --git a/arthas/ui-src/vendor/plugin-ui-sdk/lib/index.js b/arthas/ui-src/vendor/plugin-ui-sdk/lib/index.js new file mode 100644 index 0000000..944c9c9 --- /dev/null +++ b/arthas/ui-src/vendor/plugin-ui-sdk/lib/index.js @@ -0,0 +1,96 @@ +const readyMessage = { type: "mc.tab.ready" }; +export async function invoke(operation, body, options = {}) { + const { query, ...requestInit } = options; + const hasBody = body !== undefined; + const method = (requestInit.method ?? (hasBody ? "POST" : "GET")).toUpperCase(); + if (hasBody && (method === "GET" || method === "HEAD")) { + throw new Error(`plugin-ui-sdk: ${method} requests cannot include a body; use query params instead`); + } + const headers = new Headers(requestInit.headers); + const encodedBody = hasBody ? encodeBody(body, headers) : undefined; + return fetch(operationURL(operation, query), { + ...requestInit, + method, + credentials: requestInit.credentials ?? "same-origin", + headers, + body: encodedBody, + }); +} +export function stream(operation, query, options) { + if (typeof EventSource === "undefined") { + throw new Error("plugin-ui-sdk: EventSource is not available in this environment"); + } + return new EventSource(operationURL(operation, query), options); +} +export function ready() { + currentWindow().parent.postMessage(readyMessage, "*"); +} +function operationURL(operation, query) { + validateOperation(operation); + const win = currentWindow(); + const ctx = runtimeContext(win); + const url = new URL(`/api/plugins/${ctx.pluginRefSegment}/proxy/${encodeURIComponent(operation)}`, win.location.href); + url.searchParams.set("config_id", ctx.configId); + appendQuery(url.searchParams, query); + return `${url.pathname}${url.search}`; +} +function runtimeContext(win) { + const match = win.location.pathname.match(/^\/api\/plugins\/([^/]+)\/ui(?:\/|$)/); + if (!match) { + throw new Error("plugin-ui-sdk: expected to run under /api/plugins//ui"); + } + const configId = new URLSearchParams(win.location.search).get("config_id"); + if (!configId) { + throw new Error("plugin-ui-sdk: missing config_id in plugin UI URL"); + } + return { + pluginRefSegment: match[1], + configId, + }; +} +function validateOperation(operation) { + if (!operation.trim()) { + throw new Error("plugin-ui-sdk: operation is required"); + } + if (operation.includes("/")) { + throw new Error("plugin-ui-sdk: operation must be a single path segment"); + } +} +function appendQuery(searchParams, query) { + if (!query) + return; + for (const [key, value] of Object.entries(query)) { + if (key === "config_id") + continue; + const values = Array.isArray(value) ? value : [value]; + for (const item of values) { + if (item === null || item === undefined) + continue; + searchParams.append(key, String(item)); + } + } +} +function encodeBody(body, headers) { + if (isBodyInit(body)) + return body; + if (!headers.has("content-type")) { + headers.set("content-type", "application/json"); + } + return JSON.stringify(body); +} +function isBodyInit(value) { + return (typeof value === "string" || + (typeof Blob !== "undefined" && value instanceof Blob) || + (typeof FormData !== "undefined" && value instanceof FormData) || + (typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams) || + (typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer) || + (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value)) || + (typeof ReadableStream !== "undefined" && value instanceof ReadableStream)); +} +function currentWindow() { + if (typeof window === "undefined") { + throw new Error("plugin-ui-sdk: window is not available in this environment"); + } + return window; +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/arthas/ui-src/vendor/plugin-ui-sdk/lib/index.js.map b/arthas/ui-src/vendor/plugin-ui-sdk/lib/index.js.map new file mode 100644 index 0000000..02027eb --- /dev/null +++ b/arthas/ui-src/vendor/plugin-ui-sdk/lib/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/arthas/ui-src/vendor/plugin-ui-sdk/package.json b/arthas/ui-src/vendor/plugin-ui-sdk/package.json new file mode 100644 index 0000000..df0ad1c --- /dev/null +++ b/arthas/ui-src/vendor/plugin-ui-sdk/package.json @@ -0,0 +1,37 @@ +{ + "name": "@flanksource/plugin-ui-sdk", + "version": "0.1.2", + "description": "Browser UI SDK for Mission Control plugin tabs", + "type": "module", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/flanksource/plugin-ui-sdk.git" + }, + "files": [ + "lib", + "README.md", + "LICENSE" + ], + "exports": { + ".": { + "types": "./lib/index.d.ts", + "import": "./lib/index.js" + } + }, + "types": "./lib/index.d.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "check": "tsc -p tsconfig.json --noEmit", + "test": "vitest run", + "clean": "trash lib || true" + }, + "devDependencies": { + "typescript": "^5.8.3", + "vitest": "^3.1.4" + }, + "engines": { + "node": ">=20" + }, + "packageManager": "pnpm@10.11.0" +} diff --git a/inspektor-gadget/ui-src/package.json b/inspektor-gadget/ui-src/package.json index 6e809cf..71f4500 100644 --- a/inspektor-gadget/ui-src/package.json +++ b/inspektor-gadget/ui-src/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "@flanksource/clicky-ui": "^0.2.1", + "@flanksource/plugin-ui-sdk": "file:vendor/plugin-ui-sdk", "@flanksource/icons": "^1.0.56", "@tanstack/react-query": "^5.90.11", "@vitejs/plugin-react": "^5.1.0", diff --git a/inspektor-gadget/ui-src/pnpm-lock.yaml b/inspektor-gadget/ui-src/pnpm-lock.yaml index e7d0911..b0e5f3c 100644 --- a/inspektor-gadget/ui-src/pnpm-lock.yaml +++ b/inspektor-gadget/ui-src/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@flanksource/icons': specifier: ^1.0.56 version: 1.0.58(react@19.2.5) + '@flanksource/plugin-ui-sdk': + specifier: file:vendor/plugin-ui-sdk + version: file:vendor/plugin-ui-sdk '@tanstack/react-query': specifier: ^5.90.11 version: 5.100.9(react@19.2.5) @@ -373,6 +376,10 @@ packages: peerDependencies: react: '*' + '@flanksource/plugin-ui-sdk@file:vendor/plugin-ui-sdk': + resolution: {directory: vendor/plugin-ui-sdk, type: directory} + engines: {node: '>=20'} + '@floating-ui/core@1.7.5': resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} @@ -2383,6 +2390,8 @@ snapshots: dependencies: react: 19.2.5 + '@flanksource/plugin-ui-sdk@file:vendor/plugin-ui-sdk': {} + '@floating-ui/core@1.7.5': dependencies: '@floating-ui/utils': 0.2.11 diff --git a/inspektor-gadget/ui-src/src/main.tsx b/inspektor-gadget/ui-src/src/main.tsx index f290165..e9b8feb 100644 --- a/inspektor-gadget/ui-src/src/main.tsx +++ b/inspektor-gadget/ui-src/src/main.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; import { createRoot } from "react-dom/client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { invoke as sdkInvoke, ready } from "@flanksource/plugin-ui-sdk"; import { Button, DataTable, @@ -169,18 +170,8 @@ function configId() { return new URLSearchParams(window.location.search).get("config_id") || ""; } -function hostOperationUrl(op: string) { - const params = new URLSearchParams(window.location.search); - const id = params.get("config_id") || ""; - return `/api/plugins/inspektor-gadget/operations/${op}${id ? `?config_id=${encodeURIComponent(id)}` : ""}`; -} - async function invoke(op: string, body: unknown = {}): Promise { - const res = await fetch(hostOperationUrl(op), { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(body) - }); + const res = await sdkInvoke(op, body); if (!res.ok) { throw new Error(await res.text()); } @@ -229,7 +220,7 @@ function App() { useEffect(() => { refresh().catch((err) => setError(String(err))); - window.parent?.postMessage({ type: "mc.tab.ready" }, "*"); + ready(); }, []); useEffect(() => { diff --git a/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/lib/index.d.ts b/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/lib/index.d.ts new file mode 100644 index 0000000..de9388b --- /dev/null +++ b/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/lib/index.d.ts @@ -0,0 +1,10 @@ +export type QueryValue = string | number | boolean | null | undefined; +export type QueryParams = Record; +export type InvokeOptions = Omit & { + query?: QueryParams; +}; +export type StreamOptions = EventSourceInit; +export declare function invoke(operation: string, body?: unknown, options?: InvokeOptions): Promise; +export declare function stream(operation: string, query?: QueryParams, options?: StreamOptions): EventSource; +export declare function ready(): void; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/lib/index.d.ts.map b/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/lib/index.d.ts.map new file mode 100644 index 0000000..fb4c7fa --- /dev/null +++ b/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/lib/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAEtE,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,UAAU,EAAE,CAAC,CAAC;AAE7E,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG;IACtD,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,eAAe,CAAC;AAS5C,wBAAsB,MAAM,CAC1B,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,QAAQ,CAAC,CAmBnB;AAED,wBAAgB,MAAM,CACpB,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,WAAW,EACnB,OAAO,CAAC,EAAE,aAAa,GACtB,WAAW,CAKb;AAED,wBAAgB,KAAK,IAAI,IAAI,CAE5B"} \ No newline at end of file diff --git a/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/lib/index.js b/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/lib/index.js new file mode 100644 index 0000000..944c9c9 --- /dev/null +++ b/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/lib/index.js @@ -0,0 +1,96 @@ +const readyMessage = { type: "mc.tab.ready" }; +export async function invoke(operation, body, options = {}) { + const { query, ...requestInit } = options; + const hasBody = body !== undefined; + const method = (requestInit.method ?? (hasBody ? "POST" : "GET")).toUpperCase(); + if (hasBody && (method === "GET" || method === "HEAD")) { + throw new Error(`plugin-ui-sdk: ${method} requests cannot include a body; use query params instead`); + } + const headers = new Headers(requestInit.headers); + const encodedBody = hasBody ? encodeBody(body, headers) : undefined; + return fetch(operationURL(operation, query), { + ...requestInit, + method, + credentials: requestInit.credentials ?? "same-origin", + headers, + body: encodedBody, + }); +} +export function stream(operation, query, options) { + if (typeof EventSource === "undefined") { + throw new Error("plugin-ui-sdk: EventSource is not available in this environment"); + } + return new EventSource(operationURL(operation, query), options); +} +export function ready() { + currentWindow().parent.postMessage(readyMessage, "*"); +} +function operationURL(operation, query) { + validateOperation(operation); + const win = currentWindow(); + const ctx = runtimeContext(win); + const url = new URL(`/api/plugins/${ctx.pluginRefSegment}/proxy/${encodeURIComponent(operation)}`, win.location.href); + url.searchParams.set("config_id", ctx.configId); + appendQuery(url.searchParams, query); + return `${url.pathname}${url.search}`; +} +function runtimeContext(win) { + const match = win.location.pathname.match(/^\/api\/plugins\/([^/]+)\/ui(?:\/|$)/); + if (!match) { + throw new Error("plugin-ui-sdk: expected to run under /api/plugins//ui"); + } + const configId = new URLSearchParams(win.location.search).get("config_id"); + if (!configId) { + throw new Error("plugin-ui-sdk: missing config_id in plugin UI URL"); + } + return { + pluginRefSegment: match[1], + configId, + }; +} +function validateOperation(operation) { + if (!operation.trim()) { + throw new Error("plugin-ui-sdk: operation is required"); + } + if (operation.includes("/")) { + throw new Error("plugin-ui-sdk: operation must be a single path segment"); + } +} +function appendQuery(searchParams, query) { + if (!query) + return; + for (const [key, value] of Object.entries(query)) { + if (key === "config_id") + continue; + const values = Array.isArray(value) ? value : [value]; + for (const item of values) { + if (item === null || item === undefined) + continue; + searchParams.append(key, String(item)); + } + } +} +function encodeBody(body, headers) { + if (isBodyInit(body)) + return body; + if (!headers.has("content-type")) { + headers.set("content-type", "application/json"); + } + return JSON.stringify(body); +} +function isBodyInit(value) { + return (typeof value === "string" || + (typeof Blob !== "undefined" && value instanceof Blob) || + (typeof FormData !== "undefined" && value instanceof FormData) || + (typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams) || + (typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer) || + (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value)) || + (typeof ReadableStream !== "undefined" && value instanceof ReadableStream)); +} +function currentWindow() { + if (typeof window === "undefined") { + throw new Error("plugin-ui-sdk: window is not available in this environment"); + } + return window; +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/lib/index.js.map b/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/lib/index.js.map new file mode 100644 index 0000000..02027eb --- /dev/null +++ b/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/lib/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/package.json b/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/package.json new file mode 100644 index 0000000..df0ad1c --- /dev/null +++ b/inspektor-gadget/ui-src/vendor/plugin-ui-sdk/package.json @@ -0,0 +1,37 @@ +{ + "name": "@flanksource/plugin-ui-sdk", + "version": "0.1.2", + "description": "Browser UI SDK for Mission Control plugin tabs", + "type": "module", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/flanksource/plugin-ui-sdk.git" + }, + "files": [ + "lib", + "README.md", + "LICENSE" + ], + "exports": { + ".": { + "types": "./lib/index.d.ts", + "import": "./lib/index.js" + } + }, + "types": "./lib/index.d.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "check": "tsc -p tsconfig.json --noEmit", + "test": "vitest run", + "clean": "trash lib || true" + }, + "devDependencies": { + "typescript": "^5.8.3", + "vitest": "^3.1.4" + }, + "engines": { + "node": ">=20" + }, + "packageManager": "pnpm@10.11.0" +}