diff --git a/apps/p99/.prettierrc b/apps/p99/.prettierrc index 2aecfbd4..cea45738 100644 --- a/apps/p99/.prettierrc +++ b/apps/p99/.prettierrc @@ -1,6 +1,20 @@ { - "arrowParens": "avoid", + "arrowParens": "always", + "bracketSpacing": true, + "htmlWhitespaceSensitivity": "css", + "insertPragma": false, + "jsxBracketSameLine": false, + "jsxSingleQuote": false, + "objectWrap": "preserve", + "endOfLine": "lf", + "printWidth": 80, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "requirePragma": false, "semi": true, + "singleAttributePerLine": false, "singleQuote": false, - "trailingComma": "all" -} \ No newline at end of file + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false +} diff --git a/apps/p99/.vscode/extensions.json b/apps/p99/.vscode/extensions.json index 9edd4f1d..c83e2634 100644 --- a/apps/p99/.vscode/extensions.json +++ b/apps/p99/.vscode/extensions.json @@ -1,3 +1,3 @@ { "recommendations": ["esbenp.prettier-vscode"] -} \ No newline at end of file +} diff --git a/apps/p99/eslint.config.js b/apps/p99/eslint.config.js index d94e7deb..f4616740 100644 --- a/apps/p99/eslint.config.js +++ b/apps/p99/eslint.config.js @@ -1,18 +1,18 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' -import { globalIgnores } from 'eslint/config' +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; +import { globalIgnores } from "eslint/config"; export default tseslint.config([ - globalIgnores(['dist']), + globalIgnores(["dist"]), { - files: ['**/*.{ts,tsx}'], + files: ["**/*.{ts,tsx}"], extends: [ js.configs.recommended, tseslint.configs.recommended, - reactHooks.configs['recommended-latest'], + reactHooks.configs["recommended-latest"], reactRefresh.configs.vite, ], languageOptions: { @@ -20,4 +20,4 @@ export default tseslint.config([ globals: globals.browser, }, }, -]) +]); diff --git a/apps/p99/helm/values.yaml b/apps/p99/helm/values.yaml index f2c3a2e1..e6da9b5f 100644 --- a/apps/p99/helm/values.yaml +++ b/apps/p99/helm/values.yaml @@ -14,3 +14,14 @@ ui-base: target: external: uri: https://p99-blueapi.diamond.ac.uk/ + + - id: supergraph + path: /api/supergraph + target: + external: + uri: https://graph.diamond.ac.uk + + authConfigFlags: | + skip_auth_routes = ["GET=^/$", "GET=^/assets"] + api_routes = ["^/oauth2", "^/api"] + skip_provider_button = true diff --git a/apps/p99/package.json b/apps/p99/package.json index 9d129a50..e6de110b 100644 --- a/apps/p99/package.json +++ b/apps/p99/package.json @@ -11,32 +11,44 @@ "relay": "relay-compiler", "test": "vitest run", "test:watch": "vitest", - "coverage": "vitest run --coverage" + "coverage": "vitest run --coverage", + "format": "prettier --config .prettierrc --write \"**/*.{js,ts,tsx,json}\"" }, "dependencies": { + "@atlas/app-shell": "workspace:*", "@atlas/blueapi": "workspace:*", "@atlas/blueapi-query": "workspace:*", - "@diamondlightsource/sci-react-ui": "^0.2.0", + "@diamondlightsource/sci-react-ui": "^0.4.1", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", - "@mui/icons-material": "^6.5.0", - "@mui/material": "<7.0.0", - "@tanstack/react-query": "^5.90.21", + "@jsonforms/core": "3.6.0", + "@jsonforms/material-renderers": "3.6.0", + "@jsonforms/react": "3.6.0", + "@mui/icons-material": "^7.0.0", + "@mui/material": "^7.0.0", + "@tanstack/react-query": "^5.101.0", "@testing-library/react": "^15.0.7", - "msw": "^2.12.10", - "vite-plugin-relay": "^2.1.0" + "ajv": "^8.20.0", + "axios": "^1.18.0", + "lucide-react": "^1.18.0", + "react-relay": "^20.1.1", + "relay-runtime": "^20.1.1" }, "devDependencies": { "@atlas/vitest-conf": "workspace:*", - "@eslint/js": "^9.33.0", - "@mui/material": "^6.5.0", + "@eslint/js": "^9.39.4", + "@types/react-relay": "^18.2.1", + "@types/relay-runtime": "^19.0.3", "@vitejs/plugin-react-swc": "^3.11.0", "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.20", - "globals": "^16.3.0", - "react-router-dom": "^7.8.0", - "vite": "^7.1.2", - "vitest": "*" + "eslint-plugin-react-refresh": "^0.4.26", + "globals": "^16.5.0", + "msw": "^2.10.4", + "react-router-dom": "^7.17.0", + "relay-compiler": "^20.1.1", + "vite": "^7.3.5", + "vite-plugin-relay": "^2.1.0", + "vitest": "4.0.18" }, "msw": { "workerDirectory": [ @@ -44,4 +56,4 @@ ] }, "packageManager": "pnpm@10.12.3+sha512.467df2c586056165580ad6dfb54ceaad94c5a30f80893ebdec5a44c5aa73c205ae4a5bb9d5ed6bb84ea7c249ece786642bbb49d06a307df218d03da41c317417" -} \ No newline at end of file +} diff --git a/apps/p99/public/mockServiceWorker.js b/apps/p99/public/mockServiceWorker.js index 33dde9e7..71a94b1b 100644 --- a/apps/p99/public/mockServiceWorker.js +++ b/apps/p99/public/mockServiceWorker.js @@ -7,114 +7,114 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.14.6' -const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' -const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') -const activeClientIds = new Set() +const PACKAGE_VERSION = "2.14.6"; +const INTEGRITY_CHECKSUM = "4db4a41e972cec1b64cc569c66952d82"; +const IS_MOCKED_RESPONSE = Symbol("isMockedResponse"); +const activeClientIds = new Set(); -addEventListener('install', function () { - self.skipWaiting() -}) +addEventListener("install", function () { + self.skipWaiting(); +}); -addEventListener('activate', function (event) { - event.waitUntil(self.clients.claim()) -}) +addEventListener("activate", function (event) { + event.waitUntil(self.clients.claim()); +}); -addEventListener('message', async function (event) { - const clientId = Reflect.get(event.source || {}, 'id') +addEventListener("message", async function (event) { + const clientId = Reflect.get(event.source || {}, "id"); if (!clientId || !self.clients) { - return + return; } - const client = await self.clients.get(clientId) + const client = await self.clients.get(clientId); if (!client) { - return + return; } const allClients = await self.clients.matchAll({ - type: 'window', - }) + type: "window", + }); switch (event.data) { - case 'KEEPALIVE_REQUEST': { + case "KEEPALIVE_REQUEST": { sendToClient(client, { - type: 'KEEPALIVE_RESPONSE', - }) - break + type: "KEEPALIVE_RESPONSE", + }); + break; } - case 'INTEGRITY_CHECK_REQUEST': { + case "INTEGRITY_CHECK_REQUEST": { sendToClient(client, { - type: 'INTEGRITY_CHECK_RESPONSE', + type: "INTEGRITY_CHECK_RESPONSE", payload: { packageVersion: PACKAGE_VERSION, checksum: INTEGRITY_CHECKSUM, }, - }) - break + }); + break; } - case 'MOCK_ACTIVATE': { - activeClientIds.add(clientId) + case "MOCK_ACTIVATE": { + activeClientIds.add(clientId); sendToClient(client, { - type: 'MOCKING_ENABLED', + type: "MOCKING_ENABLED", payload: { client: { id: client.id, frameType: client.frameType, }, }, - }) - break + }); + break; } - case 'CLIENT_CLOSED': { - activeClientIds.delete(clientId) + case "CLIENT_CLOSED": { + activeClientIds.delete(clientId); const remainingClients = allClients.filter((client) => { - return client.id !== clientId - }) + return client.id !== clientId; + }); // Unregister itself when there are no more clients if (remainingClients.length === 0) { - self.registration.unregister() + self.registration.unregister(); } - break + break; } } -}) +}); -addEventListener('fetch', function (event) { - const requestInterceptedAt = Date.now() +addEventListener("fetch", function (event) { + const requestInterceptedAt = Date.now(); // Bypass navigation requests. - if (event.request.mode === 'navigate') { - return + if (event.request.mode === "navigate") { + return; } // Opening the DevTools triggers the "only-if-cached" request // that cannot be handled by the worker. Bypass such requests. if ( - event.request.cache === 'only-if-cached' && - event.request.mode !== 'same-origin' + event.request.cache === "only-if-cached" && + event.request.mode !== "same-origin" ) { - return + return; } // Bypass all requests when there are no active clients. // Prevents the self-unregistered worked from handling requests // after it's been terminated (still remains active until the next reload). if (activeClientIds.size === 0) { - return + return; } - const requestId = crypto.randomUUID() - event.respondWith(handleRequest(event, requestId, requestInterceptedAt)) -}) + const requestId = crypto.randomUUID(); + event.respondWith(handleRequest(event, requestId, requestInterceptedAt)); +}); /** * @param {FetchEvent} event @@ -122,28 +122,28 @@ addEventListener('fetch', function (event) { * @param {number} requestInterceptedAt */ async function handleRequest(event, requestId, requestInterceptedAt) { - const client = await resolveMainClient(event) - const requestCloneForEvents = event.request.clone() + const client = await resolveMainClient(event); + const requestCloneForEvents = event.request.clone(); const response = await getResponse( event, client, requestId, requestInterceptedAt, - ) + ); // Send back the response clone for the "response:*" life-cycle events. // Ensure MSW is active and ready to handle the message, otherwise // this message will pend indefinitely. if (client && activeClientIds.has(client.id)) { - const serializedRequest = await serializeRequest(requestCloneForEvents) + const serializedRequest = await serializeRequest(requestCloneForEvents); // Clone the response so both the client and the library could consume it. - const responseClone = response.clone() + const responseClone = response.clone(); sendToClient( client, { - type: 'RESPONSE', + type: "RESPONSE", payload: { isMockedResponse: IS_MOCKED_RESPONSE in response, request: { @@ -160,10 +160,10 @@ async function handleRequest(event, requestId, requestInterceptedAt) { }, }, responseClone.body ? [serializedRequest.body, responseClone.body] : [], - ) + ); } - return response + return response; } /** @@ -175,30 +175,30 @@ async function handleRequest(event, requestId, requestInterceptedAt) { * @returns {Promise} */ async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId) + const client = await self.clients.get(event.clientId); if (activeClientIds.has(event.clientId)) { - return client + return client; } - if (client?.frameType === 'top-level') { - return client + if (client?.frameType === "top-level") { + return client; } const allClients = await self.clients.matchAll({ - type: 'window', - }) + type: "window", + }); return allClients .filter((client) => { // Get only those clients that are currently visible. - return client.visibilityState === 'visible' + return client.visibilityState === "visible"; }) .find((client) => { // Find the client ID that's recorded in the // set of clients that have registered the worker. - return activeClientIds.has(client.id) - }) + return activeClientIds.has(client.id); + }); } /** @@ -211,36 +211,36 @@ async function resolveMainClient(event) { async function getResponse(event, client, requestId, requestInterceptedAt) { // Clone the request because it might've been already used // (i.e. its body has been read and sent to the client). - const requestClone = event.request.clone() + const requestClone = event.request.clone(); function passthrough() { // Cast the request headers to a new Headers instance // so the headers can be manipulated with. - const headers = new Headers(requestClone.headers) + const headers = new Headers(requestClone.headers); // Remove the "accept" header value that marked this request as passthrough. // This prevents request alteration and also keeps it compliant with the // user-defined CORS policies. - const acceptHeader = headers.get('accept') + const acceptHeader = headers.get("accept"); if (acceptHeader) { - const values = acceptHeader.split(',').map((value) => value.trim()) + const values = acceptHeader.split(",").map((value) => value.trim()); const filteredValues = values.filter( - (value) => value !== 'msw/passthrough', - ) + (value) => value !== "msw/passthrough", + ); if (filteredValues.length > 0) { - headers.set('accept', filteredValues.join(', ')) + headers.set("accept", filteredValues.join(", ")); } else { - headers.delete('accept') + headers.delete("accept"); } } - return fetch(requestClone, { headers }) + return fetch(requestClone, { headers }); } // Bypass mocking when the client is not active. if (!client) { - return passthrough() + return passthrough(); } // Bypass initial page load requests (i.e. static assets). @@ -248,15 +248,15 @@ async function getResponse(event, client, requestId, requestInterceptedAt) { // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet // and is not ready to handle requests. if (!activeClientIds.has(client.id)) { - return passthrough() + return passthrough(); } // Notify the client that a request has been intercepted. - const serializedRequest = await serializeRequest(event.request) + const serializedRequest = await serializeRequest(event.request); const clientMessage = await sendToClient( client, { - type: 'REQUEST', + type: "REQUEST", payload: { id: requestId, interceptedAt: requestInterceptedAt, @@ -264,19 +264,19 @@ async function getResponse(event, client, requestId, requestInterceptedAt) { }, }, [serializedRequest.body], - ) + ); switch (clientMessage.type) { - case 'MOCK_RESPONSE': { - return respondWithMock(clientMessage.data) + case "MOCK_RESPONSE": { + return respondWithMock(clientMessage.data); } - case 'PASSTHROUGH': { - return passthrough() + case "PASSTHROUGH": { + return passthrough(); } } - return passthrough() + return passthrough(); } /** @@ -287,21 +287,21 @@ async function getResponse(event, client, requestId, requestInterceptedAt) { */ function sendToClient(client, message, transferrables = []) { return new Promise((resolve, reject) => { - const channel = new MessageChannel() + const channel = new MessageChannel(); channel.port1.onmessage = (event) => { if (event.data && event.data.error) { - return reject(event.data.error) + return reject(event.data.error); } - resolve(event.data) - } + resolve(event.data); + }; client.postMessage(message, [ channel.port2, ...transferrables.filter(Boolean), - ]) - }) + ]); + }); } /** @@ -314,17 +314,17 @@ function respondWithMock(response) { // instance will have status code set to 0. Since it's not possible to create // a Response instance with status code 0, handle that use-case separately. if (response.status === 0) { - return Response.error() + return Response.error(); } - const mockedResponse = new Response(response.body, response) + const mockedResponse = new Response(response.body, response); Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { value: true, enumerable: true, - }) + }); - return mockedResponse + return mockedResponse; } /** @@ -345,5 +345,5 @@ async function serializeRequest(request) { referrerPolicy: request.referrerPolicy, body: await request.arrayBuffer(), keepalive: request.keepalive, - } + }; } diff --git a/apps/p99/relay.config.json b/apps/p99/relay.config.json new file mode 100644 index 00000000..cb1be380 --- /dev/null +++ b/apps/p99/relay.config.json @@ -0,0 +1,7 @@ +{ + "src": "./src", + "language": "typescript", + "schema": "./supergraph.graphql", + "exclude": ["**/node_modules/**", "**/__mocks__/**", "**/__generated__/**"], + "eagerEsModules": true +} diff --git a/apps/p99/src/components/P99Navbar.test.tsx b/apps/p99/src/components/P99Navbar.test.tsx deleted file mode 100644 index e8679767..00000000 --- a/apps/p99/src/components/P99Navbar.test.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { render, screen } from "@atlas/vitest-conf"; -import { MemoryRouter } from "react-router-dom"; -import { DiamondTheme, ThemeProvider } from "@diamondlightsource/sci-react-ui"; -import P99Navbar from "./P99Navbar"; - -describe("P99Navbar", () => { - it("renders the Navbar with a Home link", () => { - render( - - - - - , - ); - - expect(screen.getByRole("link", { name: /home/i })).toBeInTheDocument(); - }); -}); diff --git a/apps/p99/src/components/P99Navbar.tsx b/apps/p99/src/components/P99Navbar.tsx deleted file mode 100644 index dec82959..00000000 --- a/apps/p99/src/components/P99Navbar.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Box } from "@mui/material"; -import { Link } from "react-router-dom"; -import { - ColourSchemeButton, - Navbar, - NavLink, - NavLinks, -} from "@diamondlightsource/sci-react-ui"; - -function P99Navbar() { - return ( - - - - Home - - - - } - rightSlot={ - - - - } - /> - ); -} - -export default P99Navbar; diff --git a/apps/p99/src/components/DevicePanel.test.tsx b/apps/p99/src/components/plans/DevicePanel.test.tsx similarity index 100% rename from apps/p99/src/components/DevicePanel.test.tsx rename to apps/p99/src/components/plans/DevicePanel.test.tsx diff --git a/apps/p99/src/components/DevicePanel.tsx b/apps/p99/src/components/plans/DevicePanel.tsx similarity index 100% rename from apps/p99/src/components/DevicePanel.tsx rename to apps/p99/src/components/plans/DevicePanel.tsx diff --git a/apps/p99/src/components/PlanCard.test.tsx b/apps/p99/src/components/plans/PlanCard.test.tsx similarity index 97% rename from apps/p99/src/components/PlanCard.test.tsx rename to apps/p99/src/components/plans/PlanCard.test.tsx index 69fb9770..882574ab 100644 --- a/apps/p99/src/components/PlanCard.test.tsx +++ b/apps/p99/src/components/plans/PlanCard.test.tsx @@ -93,7 +93,7 @@ describe("PlanCard", () => { ); const buttons = screen.getAllByRole("button"); - const runButton = buttons.find(btn => btn.textContent?.includes("Run")); + const runButton = buttons.find((btn) => btn.textContent?.includes("Run")); expect(runButton).not.toBeDisabled(); }); @@ -110,7 +110,7 @@ describe("PlanCard", () => { ); const buttons = screen.getAllByRole("button"); - const runButton = buttons.find(btn => btn.textContent?.includes("Run")); + const runButton = buttons.find((btn) => btn.textContent?.includes("Run")); expect(runButton).toBeDisabled(); }); diff --git a/apps/p99/src/components/PlanCard.tsx b/apps/p99/src/components/plans/PlanCard.tsx similarity index 97% rename from apps/p99/src/components/PlanCard.tsx rename to apps/p99/src/components/plans/PlanCard.tsx index 52ee9701..cbb9604e 100644 --- a/apps/p99/src/components/PlanCard.tsx +++ b/apps/p99/src/components/plans/PlanCard.tsx @@ -66,7 +66,7 @@ export function PlanCard({ const requiredFields = planSchema?.required || []; const handleInputChange = (paramName: string, value: string) => { - setFormValues(prev => ({ ...prev, [paramName]: value })); + setFormValues((prev) => ({ ...prev, [paramName]: value })); }; const handleSubmit = async () => { @@ -86,8 +86,8 @@ export function PlanCard({ if (typeof userValue === "string") { processedParams[key] = userValue .split(",") - .map(item => item.trim()) - .filter(item => item !== ""); + .map((item) => item.trim()) + .filter((item) => item !== ""); } else { processedParams[key] = [userValue]; } @@ -221,7 +221,7 @@ export function PlanCard({ : "text" } value={formValues[key] ?? ""} - onChange={e => handleInputChange(key, e.target.value)} + onChange={(e) => handleInputChange(key, e.target.value)} helperText={ value.type === "array" ? `Enter values separated by commas (e.g. det1, det2). ${value.description || ""}` diff --git a/apps/p99/src/components/WorkerStatusBar.test.tsx b/apps/p99/src/components/plans/WorkerStatusBar.test.tsx similarity index 100% rename from apps/p99/src/components/WorkerStatusBar.test.tsx rename to apps/p99/src/components/plans/WorkerStatusBar.test.tsx diff --git a/apps/p99/src/components/WorkerStatusBar.tsx b/apps/p99/src/components/plans/WorkerStatusBar.tsx similarity index 82% rename from apps/p99/src/components/WorkerStatusBar.tsx rename to apps/p99/src/components/plans/WorkerStatusBar.tsx index e90614c9..5b3b9c10 100644 --- a/apps/p99/src/components/WorkerStatusBar.tsx +++ b/apps/p99/src/components/plans/WorkerStatusBar.tsx @@ -10,6 +10,7 @@ import { import LoopIcon from "@mui/icons-material/Loop"; import type { WorkerState } from "@atlas/blueapi"; +import { getStatusColor } from "./utils"; interface WorkerStatusBarProps { workerState: WorkerState; @@ -20,21 +21,6 @@ interface WorkerStatusBarProps { onInstrumentSessionChange: (session: string) => void; } -const getStatusColor = (state: WorkerState) => { - switch (state) { - case "RUNNING": - return { bg: "#e8f5e9", text: "#2e7d32", border: "#a5d6a7" }; - case "IDLE": - return { bg: "#e3f2fd", text: "#1565c0", border: "#90caf9" }; - case "PAUSED": - return { bg: "#fff3e0", text: "#ef6c00", border: "#ffcc80" }; - case "PANICKED": - case "ABORTING": - return { bg: "#ffebee", text: "#c62828", border: "#ef9a9a" }; - default: - return { bg: "#f5f5f5", text: "#616161", border: "#e0e0e0" }; - } -}; export function WorkerStatusBar({ workerState, activeTaskId, @@ -79,7 +65,7 @@ export function WorkerStatusBar({ label="Session ID" size="small" value={instrumentSession} - onChange={e => onInstrumentSessionChange(e.target.value)} + onChange={(e) => onInstrumentSessionChange(e.target.value)} sx={{ minWidth: 180, bgcolor: "background.paper" }} slotProps={{ inputLabel: { shrink: true } }} /> diff --git a/apps/p99/src/components/plans/utils.ts b/apps/p99/src/components/plans/utils.ts new file mode 100644 index 00000000..7baa3358 --- /dev/null +++ b/apps/p99/src/components/plans/utils.ts @@ -0,0 +1,17 @@ +import type { WorkerState } from "@atlas/blueapi"; + +export const getStatusColor = (state: WorkerState) => { + switch (state) { + case "RUNNING": + return { bg: "#e8f5e9", text: "#2e7d32", border: "#a5d6a7" }; + case "IDLE": + return { bg: "#e3f2fd", text: "#1565c0", border: "#90caf9" }; + case "PAUSED": + return { bg: "#fff3e0", text: "#ef6c00", border: "#ffcc80" }; + case "PANICKED": + case "ABORTING": + return { bg: "#ffebee", text: "#c62828", border: "#ef9a9a" }; + default: + return { bg: "#f5f5f5", text: "#616161", border: "#e0e0e0" }; + } +}; diff --git a/apps/p99/src/components/workflows/RenderSubmittedMessage.tsx b/apps/p99/src/components/workflows/RenderSubmittedMessage.tsx new file mode 100644 index 00000000..9ec988d4 --- /dev/null +++ b/apps/p99/src/components/workflows/RenderSubmittedMessage.tsx @@ -0,0 +1,94 @@ +import { + Accordion, + AccordionDetails, + AccordionSummary, + Typography, +} from "@mui/material"; +import type { + SubmissionGraphQLErrorMessage, + SubmissionNetworkErrorMessage, + SubmissionSuccessMessage, + WorkflowStatus, +} from "../../utils/types"; +import { Link } from "react-router-dom"; +import { ChevronDown } from "lucide-react"; +import type { renderSubmittedMessageFragment$key } from "../../graphql/__generated__/renderSubmittedMessageFragment.graphql"; +import { useFragment } from "react-relay"; +import { renderSubmittedMessageFragment } from "../../graphql/renderSubmittedMessageFragment"; +import { getWorkflowStatusIcon } from "./StatusIcons"; + +const RenderSubmittedMessage = ({ + result, + index, + fragmentRef, +}: { + result: + | SubmissionGraphQLErrorMessage + | SubmissionNetworkErrorMessage + | SubmissionSuccessMessage; + index: number; + fragmentRef?: renderSubmittedMessageFragment$key | null; +}) => { + const data = useFragment(renderSubmittedMessageFragment, fragmentRef); + switch (result.type) { + case "success": + return ( + {}} + > + + + Successfully submitted{" "} + + {result.message} + + + {data + ? getWorkflowStatusIcon(data.status?.__typename as WorkflowStatus) + : getWorkflowStatusIcon("Unknown")} + + + ); + case "networkError": + return ( + + }> + + Submission error type {result.error.name} + + + + + Submission error message {result.error.message} + + + + ); + case "graphQLError": + default: + return ( + + }> + + Submission error type GraphQL + + + + {result.errors.map((e, j) => { + return ( + + Error {j} {e.message} + + ); + })} + + + ); + } +}; + +export default RenderSubmittedMessage; diff --git a/apps/p99/src/components/workflows/StatusIcons.tsx b/apps/p99/src/components/workflows/StatusIcons.tsx new file mode 100644 index 00000000..0a5319f8 --- /dev/null +++ b/apps/p99/src/components/workflows/StatusIcons.tsx @@ -0,0 +1,50 @@ +import { Tooltip } from "@mui/material"; +import type { WorkflowStatus } from "../../utils/types"; +import { + CircleAlert, + CircleCheckBig, + CircleEllipsis, + CircleQuestionMark, + LoaderCircle, +} from "lucide-react"; + +export const getWorkflowStatusIcon = ( + status: WorkflowStatus, + size: number = 25, +) => { + const workflowStatusIconMap: { [key in WorkflowStatus]: React.JSX.Element } = + { + Unknown: ( + + + + ), + WorkflowPendingStatus: ( + + + + ), + WorkflowRunningStatus: ( + + + + ), + WorkflowSucceededStatus: ( + + + + ), + WorkflowFailedStatus: ( + + + + ), + WorkflowErroredStatus: ( + + + + ), + }; + + return workflowStatusIconMap[status]; +}; diff --git a/apps/p99/src/components/workflows/SubmissionForm.tsx b/apps/p99/src/components/workflows/SubmissionForm.tsx new file mode 100644 index 00000000..96fdc8cf --- /dev/null +++ b/apps/p99/src/components/workflows/SubmissionForm.tsx @@ -0,0 +1,83 @@ +import { createAjv, Generate } from "@jsonforms/core"; +import { Divider, Snackbar, Stack, Typography } from "@mui/material"; +import type { workflowTemplateFragment$key } from "../../graphql/__generated__/workflowTemplateFragment.graphql"; +import { useFragment } from "react-relay"; +import { workflowTemplateFragment } from "../../graphql/workflowTemplateFragment"; +import { JsonForms } from "@jsonforms/react"; +import { + materialCells, + materialRenderers, +} from "@jsonforms/material-renderers"; +import { useState } from "react"; +import type { JSONObject } from "../../utils/types"; +import { VisitInput, type Visit } from "@diamondlightsource/sci-react-ui"; +import type { ErrorObject } from "ajv"; + +const SubmissionForm = ({ + template, + onSubmit, + visit, +}: { + template: workflowTemplateFragment$key; + onSubmit: (visit: Visit, parameters: object) => void; + visit?: Visit; +}) => { + const data = useFragment(workflowTemplateFragment, template); + const validator = createAjv({ useDefaults: true, coerceTypes: true }); + const [parameters, setParameters] = useState({}); + const [errors, setErrors] = useState([]); + const [submitted, setSubmitted] = useState(false); + + const onClick = (visit: Visit, parameters?: object) => { + if (errors.length === 0) { + onSubmit(visit, parameters ?? {}); + setSubmitted(true); + } + }; + + const handleCloseSnackbar = () => { + setSubmitted(false); + }; + + return ( + + + {data.title} + + + {data.description} + + + Maintainer: {data.maintainer} + + {data.repository && {data.repository}} + + { + setParameters(data as JSONObject); + setErrors(errors ? errors : []); + }} + /> + + + + ); +}; + +export default SubmissionForm; diff --git a/apps/p99/src/components/workflows/SubmittedMessagesList.tsx b/apps/p99/src/components/workflows/SubmittedMessagesList.tsx new file mode 100644 index 00000000..892a0a4a --- /dev/null +++ b/apps/p99/src/components/workflows/SubmittedMessagesList.tsx @@ -0,0 +1,54 @@ +import { Box, Divider, Paper, Typography } from "@mui/material"; +import type { SubmissionData } from "../../utils/types"; +import RenderSubmittedMessage from "./RenderSubmittedMessage"; +import SubscribeAndRender from "./SubscribeAndRender"; + +const SubmittedMessagesList = ({ + submissionData, +}: { + submissionData: SubmissionData[]; +}) => ( + + + {submissionData.length > 0 && ( + <> + + + Submissions + + + {submissionData.map((data, index) => + data.workflowName ? ( + + ) : ( + + ), + )} + + )} + +); + +export default SubmittedMessagesList; diff --git a/apps/p99/src/components/workflows/SubscribeAndRender.tsx b/apps/p99/src/components/workflows/SubscribeAndRender.tsx new file mode 100644 index 00000000..7c693c54 --- /dev/null +++ b/apps/p99/src/components/workflows/SubscribeAndRender.tsx @@ -0,0 +1,62 @@ +import type { Visit } from "@diamondlightsource/sci-react-ui"; +import type { + SubmissionGraphQLErrorMessage, + SubmissionNetworkErrorMessage, + SubmissionSuccessMessage, +} from "../../utils/types"; +import RenderSubmittedMessage from "./RenderSubmittedMessage"; +import { useMemo, useState } from "react"; +import type { + subscribeAndRenderSubscription$data, + subscribeAndRenderSubscription as SubscribeAndRenderSubscriptionType, +} from "../../graphql/__generated__/subscribeAndRenderSubscription.graphql"; +import { subscribeAndRenderSubscription } from "../../graphql/subscribeAndRenderSubscription"; +import { useSubscription } from "react-relay"; +import type { GraphQLSubscriptionConfig } from "relay-runtime"; + +const SubscribeAndRender = ({ + result, + visit, + workflowName, + index, +}: { + result: + | SubmissionGraphQLErrorMessage + | SubmissionNetworkErrorMessage + | SubmissionSuccessMessage; + visit: Visit; + workflowName: string; + index: number; +}) => { + const [workflowData, setWorkflowData] = + useState(null); + + const subscriptionData: GraphQLSubscriptionConfig = + useMemo( + () => ({ + subscription: subscribeAndRenderSubscription, + variables: { visit, name: workflowName }, + onNext: (response) => { + setWorkflowData(response ?? null); + }, + onError: (error: unknown) => { + console.error("Subscription error:", error); + }, + onCompleted: () => { + console.log("completed"); + }, + }), + [visit, workflowName], + ); + useSubscription(subscriptionData); + + return ( + + ); +}; + +export default SubscribeAndRender; diff --git a/apps/p99/src/components/workflows/TemplateView.tsx b/apps/p99/src/components/workflows/TemplateView.tsx new file mode 100644 index 00000000..daee3cb2 --- /dev/null +++ b/apps/p99/src/components/workflows/TemplateView.tsx @@ -0,0 +1,87 @@ +import { Box } from "@mui/material"; +import { useLazyLoadQuery, useMutation } from "react-relay"; +import type { templateViewQuery as TemplateViewQueryType } from "../../graphql/__generated__/templateViewQuery.graphql"; +import { templateViewQuery } from "../../graphql/templateViewQuery"; +import SubmissionForm from "./SubmissionForm"; +import { visitToText, type Visit } from "@diamondlightsource/sci-react-ui"; +import type { SubmissionData } from "../../utils/types"; +import { useState } from "react"; +import { visitTextToVisit } from "../../utils/common"; +import SubmittedMessagesList from "./SubmittedMessagesList"; +import { mutation } from "../../graphql/submitWorkflowTemplateMutation"; +import type { submitWorkflowTemplateMutation } from "../../graphql/__generated__/submitWorkflowTemplateMutation.graphql"; + +const TemplateView = ({ + templateName, + visit, +}: { + templateName: string; + visit?: Visit; +}) => { + const templateData = useLazyLoadQuery( + templateViewQuery, + { templateName }, + { fetchPolicy: "store-and-network" }, + ); + + const [submissionData, setSubmissionData] = useState([]); + const [commitMutation] = + useMutation(mutation); + + const storedVisit = visitTextToVisit( + localStorage.getItem("instrumentSessionID") ?? "", + ); + + const submitWorkflow = (visit: Visit, parameters: object) => { + commitMutation({ + variables: { templateName, visit, parameters }, + onCompleted: (response, errors) => { + if (errors?.length) { + setSubmissionData((prev) => [ + { submissionResult: { type: "graphQLError", errors }, visit }, + ...prev, + ]); + } else { + const submittedName = response.submitWorkflowTemplate.name; + setSubmissionData((prev) => [ + { + submissionResult: { + type: "success", + message: `${visitToText(visit)}/${submittedName}`, + }, + visit, + workflowName: submittedName, + }, + ...prev, + ]); + localStorage.setItem("instrumentSessionID", visitToText(visit)); + } + }, + onError: (err) => { + setSubmissionData((prev) => [ + { submissionResult: { type: "networkError", error: err }, visit }, + ...prev, + ]); + }, + }); + }; + + return ( + + + + + ); +}; + +export default TemplateView; diff --git a/apps/p99/src/context/userAuth/UserAuthContext.ts b/apps/p99/src/context/userAuth/UserAuthContext.ts new file mode 100644 index 00000000..9ca21c3a --- /dev/null +++ b/apps/p99/src/context/userAuth/UserAuthContext.ts @@ -0,0 +1,6 @@ +import { createContext } from "react"; +import { type UserAuthStatus } from "./authUtils"; + +const userDefault: UserAuthStatus = { person: null, person_status: "PENDING" }; + +export const UserAuthContext = createContext(userDefault); diff --git a/apps/p99/src/context/userAuth/UserAuthProvider.tsx b/apps/p99/src/context/userAuth/UserAuthProvider.tsx new file mode 100644 index 00000000..e5374dc4 --- /dev/null +++ b/apps/p99/src/context/userAuth/UserAuthProvider.tsx @@ -0,0 +1,58 @@ +import { type ReactNode } from "react"; +import { UserAuthContext } from "./UserAuthContext"; +import { getUser, type UserAuthStatus } from "./authUtils"; +import { useQuery } from "@tanstack/react-query"; +import type { AxiosError } from "axios"; + +export const UserAuthProvider = ({ children }: { children: ReactNode }) => { + const query = useQuery({ + queryKey: ["user"], + queryFn: getUser, + retry: (failureCount, error: AxiosError) => { + if ("status" in error && (error.status == 401 || error.status == 403)) { + //dont retry 401/403 + return false; + } + + return failureCount < 2; + }, + }); + + //undefined if pending + let response: UserAuthStatus = { + person: null, + person_status: "PENDING", + }; + + if (query.isError) { + if (query.error.status == 401) { + response = { + person: null, + person_status: "UNAUTHORIZED", + }; + } else if (query.error.status == 403) { + response = { + person: null, + person_status: "FORBIDDEN", + }; + } else { + response = { + person: null, + person_status: "ERROR", + }; + } + } else { + if (query.data) { + response = { + person: query.data.preferredUsername, + person_status: "OK", + }; + } + } + + return ( + + {children} + + ); +}; diff --git a/apps/p99/src/context/userAuth/authUtils.ts b/apps/p99/src/context/userAuth/authUtils.ts new file mode 100644 index 00000000..86289b4b --- /dev/null +++ b/apps/p99/src/context/userAuth/authUtils.ts @@ -0,0 +1,22 @@ +import axios, { type AxiosResponse } from "axios"; + +const userUrl = "oauth2/userinfo"; + +export type Person = { + identifier: string; + accepted_orca_eula: boolean; +}; + +export type UserName = { + preferredUsername: string; +}; + +export type UserAuthStatus = { + person: string | null | undefined; + person_status: "PENDING" | "UNAUTHORIZED" | "FORBIDDEN" | "OK" | "ERROR"; +}; + +export const getUser = async () => { + const { data } = await axios.get>(userUrl); + return data; +}; diff --git a/apps/p99/src/context/userAuth/useUserAuth.ts b/apps/p99/src/context/userAuth/useUserAuth.ts new file mode 100644 index 00000000..ffb75918 --- /dev/null +++ b/apps/p99/src/context/userAuth/useUserAuth.ts @@ -0,0 +1,12 @@ +import { UserAuthContext } from "./UserAuthContext"; +import { useContext } from "react"; + +export const useUserAuth = () => { + const context = useContext(UserAuthContext); + if (!context) { + throw new Error( + "No user context found, is your component without UserAuthProvider?", + ); + } + return context; +}; diff --git a/apps/p99/src/context/workflows/RelayEnvironment.ts b/apps/p99/src/context/workflows/RelayEnvironment.ts new file mode 100644 index 00000000..44991c9d --- /dev/null +++ b/apps/p99/src/context/workflows/RelayEnvironment.ts @@ -0,0 +1,34 @@ +import { + Environment, + Network, + RecordSource, + Store, + type FetchFunction, +} from "relay-runtime"; + +const HTTP_ENDPOINT = "/api/supergraph"; +const fetchFn: FetchFunction = async (request, variables) => { + const resp = await fetch(HTTP_ENDPOINT, { + method: "POST", + headers: { + Accept: + "application/graphql-response+json; charset=utf-8, application/json; charset=utf-8", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: request.text, + variables, + }), + }); + + return await resp.json(); +}; + +function createRelayEnvironment() { + return new Environment({ + network: Network.create(fetchFn), + store: new Store(new RecordSource()), + }); +} + +export const RelayEnvironment = createRelayEnvironment(); diff --git a/apps/p99/src/graphql/__generated__/renderSubmittedMessageFragment.graphql.ts b/apps/p99/src/graphql/__generated__/renderSubmittedMessageFragment.graphql.ts new file mode 100644 index 00000000..78170ae4 --- /dev/null +++ b/apps/p99/src/graphql/__generated__/renderSubmittedMessageFragment.graphql.ts @@ -0,0 +1,58 @@ +/** + * @generated SignedSource<<38bf7fc232db12c20f23bdfba063f4a4>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ReaderFragment } from "relay-runtime"; +import { FragmentRefs } from "relay-runtime"; +export type renderSubmittedMessageFragment$data = { + readonly status: + | { + readonly __typename: string; + } + | null + | undefined; + readonly " $fragmentType": "renderSubmittedMessageFragment"; +}; +export type renderSubmittedMessageFragment$key = { + readonly " $data"?: renderSubmittedMessageFragment$data; + readonly " $fragmentSpreads": FragmentRefs<"renderSubmittedMessageFragment">; +}; + +const node: ReaderFragment = { + argumentDefinitions: [], + kind: "Fragment", + metadata: null, + name: "renderSubmittedMessageFragment", + selections: [ + { + alias: null, + args: null, + concreteType: null, + kind: "LinkedField", + name: "status", + plural: false, + selections: [ + { + alias: null, + args: null, + kind: "ScalarField", + name: "__typename", + storageKey: null, + }, + ], + storageKey: null, + }, + ], + type: "Workflow", + abstractKey: null, +}; + +(node as any).hash = "03e612be6ddc650f978d56b8e18a9818"; + +export default node; diff --git a/apps/p99/src/graphql/__generated__/submitWorkflowTemplateMutation.graphql.ts b/apps/p99/src/graphql/__generated__/submitWorkflowTemplateMutation.graphql.ts new file mode 100644 index 00000000..f056a2de --- /dev/null +++ b/apps/p99/src/graphql/__generated__/submitWorkflowTemplateMutation.graphql.ts @@ -0,0 +1,133 @@ +/** + * @generated SignedSource<<1bf0edfd66484b0fb05d16638676a62b>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from "relay-runtime"; +export type VisitInput = { + number: number; + proposalCode: string; + proposalNumber: number; +}; +export type submitWorkflowTemplateMutation$variables = { + parameters: any; + templateName: string; + visit: VisitInput; +}; +export type submitWorkflowTemplateMutation$data = { + readonly submitWorkflowTemplate: { + readonly name: string; + }; +}; +export type submitWorkflowTemplateMutation = { + response: submitWorkflowTemplateMutation$data; + variables: submitWorkflowTemplateMutation$variables; +}; + +const node: ConcreteRequest = (function () { + var v0 = { + defaultValue: null, + kind: "LocalArgument", + name: "parameters", + }, + v1 = { + defaultValue: null, + kind: "LocalArgument", + name: "templateName", + }, + v2 = { + defaultValue: null, + kind: "LocalArgument", + name: "visit", + }, + v3 = [ + { + kind: "Variable", + name: "name", + variableName: "templateName", + }, + { + kind: "Variable", + name: "parameters", + variableName: "parameters", + }, + { + kind: "Variable", + name: "visit", + variableName: "visit", + }, + ], + v4 = { + alias: null, + args: null, + kind: "ScalarField", + name: "name", + storageKey: null, + }; + return { + fragment: { + argumentDefinitions: [v0 /*: any*/, v1 /*: any*/, v2 /*: any*/], + kind: "Fragment", + metadata: null, + name: "submitWorkflowTemplateMutation", + selections: [ + { + alias: null, + args: v3 /*: any*/, + concreteType: "Workflow", + kind: "LinkedField", + name: "submitWorkflowTemplate", + plural: false, + selections: [v4 /*: any*/], + storageKey: null, + }, + ], + type: "Mutation", + abstractKey: null, + }, + kind: "Request", + operation: { + argumentDefinitions: [v1 /*: any*/, v2 /*: any*/, v0 /*: any*/], + kind: "Operation", + name: "submitWorkflowTemplateMutation", + selections: [ + { + alias: null, + args: v3 /*: any*/, + concreteType: "Workflow", + kind: "LinkedField", + name: "submitWorkflowTemplate", + plural: false, + selections: [ + v4 /*: any*/, + { + alias: null, + args: null, + kind: "ScalarField", + name: "id", + storageKey: null, + }, + ], + storageKey: null, + }, + ], + }, + params: { + cacheID: "4c13758cbc0d34e6d9a014c9f36e641a", + id: null, + metadata: {}, + name: "submitWorkflowTemplateMutation", + operationKind: "mutation", + text: "mutation submitWorkflowTemplateMutation(\n $templateName: String!\n $visit: VisitInput!\n $parameters: JSON!\n) {\n submitWorkflowTemplate(name: $templateName, visit: $visit, parameters: $parameters) {\n name\n id\n }\n}\n", + }, + }; +})(); + +(node as any).hash = "e2ff0d748606ea7f1c8e6dd7daae9206"; + +export default node; diff --git a/apps/p99/src/graphql/__generated__/subscribeAndRenderSubscription.graphql.ts b/apps/p99/src/graphql/__generated__/subscribeAndRenderSubscription.graphql.ts new file mode 100644 index 00000000..fa9e1a61 --- /dev/null +++ b/apps/p99/src/graphql/__generated__/subscribeAndRenderSubscription.graphql.ts @@ -0,0 +1,139 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from "relay-runtime"; +import { FragmentRefs } from "relay-runtime"; +export type VisitInput = { + number: number; + proposalCode: string; + proposalNumber: number; +}; +export type subscribeAndRenderSubscription$variables = { + name: string; + visit: VisitInput; +}; +export type subscribeAndRenderSubscription$data = { + readonly workflow: { + readonly " $fragmentSpreads": FragmentRefs<"renderSubmittedMessageFragment">; + }; +}; +export type subscribeAndRenderSubscription = { + response: subscribeAndRenderSubscription$data; + variables: subscribeAndRenderSubscription$variables; +}; + +const node: ConcreteRequest = (function () { + var v0 = { + defaultValue: null, + kind: "LocalArgument", + name: "name", + }, + v1 = { + defaultValue: null, + kind: "LocalArgument", + name: "visit", + }, + v2 = [ + { + kind: "Variable", + name: "name", + variableName: "name", + }, + { + kind: "Variable", + name: "visit", + variableName: "visit", + }, + ]; + return { + fragment: { + argumentDefinitions: [v0 /*: any*/, v1 /*: any*/], + kind: "Fragment", + metadata: null, + name: "subscribeAndRenderSubscription", + selections: [ + { + alias: null, + args: v2 /*: any*/, + concreteType: "Workflow", + kind: "LinkedField", + name: "workflow", + plural: false, + selections: [ + { + args: null, + kind: "FragmentSpread", + name: "renderSubmittedMessageFragment", + }, + ], + storageKey: null, + }, + ], + type: "Subscription", + abstractKey: null, + }, + kind: "Request", + operation: { + argumentDefinitions: [v1 /*: any*/, v0 /*: any*/], + kind: "Operation", + name: "subscribeAndRenderSubscription", + selections: [ + { + alias: null, + args: v2 /*: any*/, + concreteType: "Workflow", + kind: "LinkedField", + name: "workflow", + plural: false, + selections: [ + { + alias: null, + args: null, + concreteType: null, + kind: "LinkedField", + name: "status", + plural: false, + selections: [ + { + alias: null, + args: null, + kind: "ScalarField", + name: "__typename", + storageKey: null, + }, + ], + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "id", + storageKey: null, + }, + ], + storageKey: null, + }, + ], + }, + params: { + cacheID: "1ca97628bb694c4d5fe00452a2a1d3f8", + id: null, + metadata: {}, + name: "subscribeAndRenderSubscription", + operationKind: "subscription", + text: "subscription subscribeAndRenderSubscription(\n $visit: VisitInput!\n $name: String!\n) {\n workflow(visit: $visit, name: $name) {\n ...renderSubmittedMessageFragment\n id\n }\n}\n\nfragment renderSubmittedMessageFragment on Workflow {\n status {\n __typename\n }\n}\n", + }, + }; +})(); + +(node as any).hash = "c3057cb3b2ce67c2a8d0cf51ff221958"; + +export default node; diff --git a/apps/p99/src/graphql/__generated__/templateViewQuery.graphql.ts b/apps/p99/src/graphql/__generated__/templateViewQuery.graphql.ts new file mode 100644 index 00000000..d284293c --- /dev/null +++ b/apps/p99/src/graphql/__generated__/templateViewQuery.graphql.ts @@ -0,0 +1,149 @@ +/** + * @generated SignedSource<<9ab71ee3d5f3ef84f6b6a375814b3d28>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from "relay-runtime"; +import { FragmentRefs } from "relay-runtime"; +export type templateViewQuery$variables = { + templateName: string; +}; +export type templateViewQuery$data = { + readonly workflowTemplate: { + readonly " $fragmentSpreads": FragmentRefs<"workflowTemplateFragment">; + }; +}; +export type templateViewQuery = { + response: templateViewQuery$data; + variables: templateViewQuery$variables; +}; + +const node: ConcreteRequest = (function () { + var v0 = [ + { + defaultValue: null, + kind: "LocalArgument", + name: "templateName", + }, + ], + v1 = [ + { + kind: "Variable", + name: "name", + variableName: "templateName", + }, + ]; + return { + fragment: { + argumentDefinitions: v0 /*: any*/, + kind: "Fragment", + metadata: null, + name: "templateViewQuery", + selections: [ + { + alias: null, + args: v1 /*: any*/, + concreteType: "WorkflowTemplate", + kind: "LinkedField", + name: "workflowTemplate", + plural: false, + selections: [ + { + args: null, + kind: "FragmentSpread", + name: "workflowTemplateFragment", + }, + ], + storageKey: null, + }, + ], + type: "Query", + abstractKey: null, + }, + kind: "Request", + operation: { + argumentDefinitions: v0 /*: any*/, + kind: "Operation", + name: "templateViewQuery", + selections: [ + { + alias: null, + args: v1 /*: any*/, + concreteType: "WorkflowTemplate", + kind: "LinkedField", + name: "workflowTemplate", + plural: false, + selections: [ + { + alias: null, + args: null, + kind: "ScalarField", + name: "name", + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "maintainer", + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "title", + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "description", + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "arguments", + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "uiSchema", + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "repository", + storageKey: null, + }, + ], + storageKey: null, + }, + ], + }, + params: { + cacheID: "19fc0d9ac63aa4eab9e10aefa178227c", + id: null, + metadata: {}, + name: "templateViewQuery", + operationKind: "query", + text: "query templateViewQuery(\n $templateName: String!\n) {\n workflowTemplate(name: $templateName) {\n ...workflowTemplateFragment\n }\n}\n\nfragment workflowTemplateFragment on WorkflowTemplate {\n name\n maintainer\n title\n description\n arguments\n uiSchema\n repository\n}\n", + }, + }; +})(); + +(node as any).hash = "a095c2f1a9314457db4bf9b26bc541a2"; + +export default node; diff --git a/apps/p99/src/graphql/__generated__/workflowTemplateFragment.graphql.ts b/apps/p99/src/graphql/__generated__/workflowTemplateFragment.graphql.ts new file mode 100644 index 00000000..5427ec99 --- /dev/null +++ b/apps/p99/src/graphql/__generated__/workflowTemplateFragment.graphql.ts @@ -0,0 +1,90 @@ +/** + * @generated SignedSource<<7eb6c59915242a110bb98268ee6f12dc>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ReaderFragment } from "relay-runtime"; +import { FragmentRefs } from "relay-runtime"; +export type workflowTemplateFragment$data = { + readonly arguments: any; + readonly description: string | null | undefined; + readonly maintainer: string; + readonly name: string; + readonly repository: string | null | undefined; + readonly title: string | null | undefined; + readonly uiSchema: any | null | undefined; + readonly " $fragmentType": "workflowTemplateFragment"; +}; +export type workflowTemplateFragment$key = { + readonly " $data"?: workflowTemplateFragment$data; + readonly " $fragmentSpreads": FragmentRefs<"workflowTemplateFragment">; +}; + +const node: ReaderFragment = { + argumentDefinitions: [], + kind: "Fragment", + metadata: null, + name: "workflowTemplateFragment", + selections: [ + { + alias: null, + args: null, + kind: "ScalarField", + name: "name", + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "maintainer", + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "title", + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "description", + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "arguments", + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "uiSchema", + storageKey: null, + }, + { + alias: null, + args: null, + kind: "ScalarField", + name: "repository", + storageKey: null, + }, + ], + type: "WorkflowTemplate", + abstractKey: null, +}; + +(node as any).hash = "959850317e316a423e70b0c89c011f27"; + +export default node; diff --git a/apps/p99/src/graphql/renderSubmittedMessageFragment.ts b/apps/p99/src/graphql/renderSubmittedMessageFragment.ts new file mode 100644 index 00000000..bc1a5f51 --- /dev/null +++ b/apps/p99/src/graphql/renderSubmittedMessageFragment.ts @@ -0,0 +1,9 @@ +import { graphql } from "relay-runtime"; + +export const renderSubmittedMessageFragment = graphql` + fragment renderSubmittedMessageFragment on Workflow { + status { + __typename + } + } +`; diff --git a/apps/p99/src/graphql/submitWorkflowTemplateMutation.ts b/apps/p99/src/graphql/submitWorkflowTemplateMutation.ts new file mode 100644 index 00000000..232bf436 --- /dev/null +++ b/apps/p99/src/graphql/submitWorkflowTemplateMutation.ts @@ -0,0 +1,17 @@ +import { graphql } from "relay-runtime"; + +export const mutation = graphql` + mutation submitWorkflowTemplateMutation( + $templateName: String! + $visit: VisitInput! + $parameters: JSON! + ) { + submitWorkflowTemplate( + name: $templateName + visit: $visit + parameters: $parameters + ) { + name + } + } +`; diff --git a/apps/p99/src/graphql/subscribeAndRenderSubscription.ts b/apps/p99/src/graphql/subscribeAndRenderSubscription.ts new file mode 100644 index 00000000..7ae168c0 --- /dev/null +++ b/apps/p99/src/graphql/subscribeAndRenderSubscription.ts @@ -0,0 +1,12 @@ +import { graphql } from "relay-runtime"; + +export const subscribeAndRenderSubscription = graphql` + subscription subscribeAndRenderSubscription( + $visit: VisitInput! + $name: String! + ) { + workflow(visit: $visit, name: $name) { + ...renderSubmittedMessageFragment + } + } +`; diff --git a/apps/p99/src/graphql/templateViewQuery.ts b/apps/p99/src/graphql/templateViewQuery.ts new file mode 100644 index 00000000..0d0c2c06 --- /dev/null +++ b/apps/p99/src/graphql/templateViewQuery.ts @@ -0,0 +1,9 @@ +import { graphql } from "relay-runtime"; + +export const templateViewQuery = graphql` + query templateViewQuery($templateName: String!) { + workflowTemplate(name: $templateName) { + ...workflowTemplateFragment + } + } +`; diff --git a/apps/p99/src/graphql/workflowTemplateFragment.ts b/apps/p99/src/graphql/workflowTemplateFragment.ts new file mode 100644 index 00000000..2b6a62db --- /dev/null +++ b/apps/p99/src/graphql/workflowTemplateFragment.ts @@ -0,0 +1,13 @@ +import { graphql } from "relay-runtime"; + +export const workflowTemplateFragment = graphql` + fragment workflowTemplateFragment on WorkflowTemplate { + name + maintainer + title + description + arguments + uiSchema + repository + } +`; diff --git a/apps/p99/src/main.tsx b/apps/p99/src/main.tsx index 154eeb72..8e459d5b 100644 --- a/apps/p99/src/main.tsx +++ b/apps/p99/src/main.tsx @@ -1,14 +1,15 @@ import { DiamondTheme, ThemeProvider } from "@diamondlightsource/sci-react-ui"; -import { RouterProvider, createBrowserRouter } from "react-router-dom"; +import { RouterProvider } from "react-router-dom"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; -import Dashboard from "./routes/Dashboard.tsx"; -import { Layout } from "./routes/Layout.tsx"; - import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { BlueapiProvider } from "@atlas/blueapi-query"; import { createApi } from "@atlas/blueapi"; +import { RelayEnvironmentProvider } from "react-relay"; +import { RelayEnvironment } from "./context/workflows/RelayEnvironment.ts"; +import { UserAuthProvider } from "./context/userAuth/UserAuthProvider.tsx"; +import { router } from "./router.tsx"; async function enableMocking() { if (import.meta.env.DEV) { @@ -16,18 +17,6 @@ async function enableMocking() { return worker.start(); } } -const router = createBrowserRouter([ - { - path: "/", - element: , - children: [ - { - index: true, - element: , - }, - ], - }, -]); const queryClient = new QueryClient(); export const api = createApi("/api/blueapi"); @@ -35,11 +24,15 @@ enableMocking().then(() => { createRoot(document.getElementById("root")!).render( - - - - - + + + + + + + + + , ); diff --git a/apps/p99/src/mocks/handlers.ts b/apps/p99/src/mocks/handlers.ts index 70dc0cea..71fc1fde 100644 --- a/apps/p99/src/mocks/handlers.ts +++ b/apps/p99/src/mocks/handlers.ts @@ -1,6 +1,7 @@ -import { http, HttpResponse } from "msw"; +import { http, HttpResponse, graphql } from "msw"; import plansResponse from "./plans-response.json"; import devicesResponse from "./devices-response.json"; +import templateResponse from "./template-response.json"; const fakeTaskId = "46709394"; let workerState = "IDLE"; @@ -36,4 +37,22 @@ export const handlers = [ triggerRunningState(); return HttpResponse.json({ task_id: fakeTaskId }, { status: 201 }); }), + + http.get("/oauth2/userinfo", () => { + return HttpResponse.json({ preferredUsername: "abc123456" }); + }), + + graphql.query("templateViewQuery", async () => { + return HttpResponse.json(templateResponse); + }), + + graphql.mutation("submitWorkflowTemplateMutation", async () => { + return HttpResponse.json({ + data: { + submitWorkflowTemplate: { + name: "ptypy-p99-from-config-1234", + }, + }, + }); + }), ]; diff --git a/apps/p99/src/mocks/server.ts b/apps/p99/src/mocks/server.ts new file mode 100644 index 00000000..7b37f2ac --- /dev/null +++ b/apps/p99/src/mocks/server.ts @@ -0,0 +1,4 @@ +import { setupServer } from "msw/node"; +import { handlers } from "./handlers"; + +export const server = setupServer(...handlers); diff --git a/apps/p99/src/mocks/template-response.json b/apps/p99/src/mocks/template-response.json new file mode 100644 index 00000000..08bc10cf --- /dev/null +++ b/apps/p99/src/mocks/template-response.json @@ -0,0 +1,48 @@ +{ + "data": { + "workflowTemplate": { + "title": "PtyPy Reconstruction for P99", + "name": "ptypy-p99-from-config", + "maintainer": "imaging-ptypy-group", + "description": "Runs a PtyPy reconstruction job inside a container based on data collected at P99.\n", + "repository": "https://github.com/DiamondLightSource/imaging-workflows", + "arguments": { + "properties": { + "id": { + "title": "Scan Number", + "type": "integer" + }, + "inpath": { + "default": "processing/writenData/reconstruction_test_data", + "title": "Path to raw data folder", + "type": "string" + }, + "memory": { + "default": "20Gi", + "pattern": "^[0-9]+[GMK]i$", + "title": "Requested CPU memory", + "type": "string" + }, + "nprocs": { + "default": 1, + "title": "Nr. of processes", + "type": "integer" + }, + "outpath": { + "default": "processing/workflows/ptypy", + "title": "Path to output folder", + "type": "string" + }, + "usegpu": { + "default": false, + "title": "Use GPU", + "type": "boolean" + } + }, + "required": ["id", "inpath", "memory", "nprocs", "outpath", "usegpu"], + "type": "object" + }, + "uiSchema": null + } + } +} diff --git a/apps/p99/src/router.tsx b/apps/p99/src/router.tsx new file mode 100644 index 00000000..4a6c3cbc --- /dev/null +++ b/apps/p99/src/router.tsx @@ -0,0 +1,47 @@ +import { type SectionGroup, createRouter } from "@atlas/app-shell"; +import { FileText, LucideLayoutDashboard, ServerCog } from "lucide-react"; +import Dashboard from "./routes/Dashboard"; +import Plans from "./routes/Plans"; +import Workflows from "./routes/Workflows"; + +const navigation: SectionGroup[] = [ + { + sections: [ + { + name: "Dashboard", + icon: , + path: "dashboard", + pages: [ + { + name: "Dashboard", + element: , + }, + ], + }, + { + name: "Plans", + icon: , + path: "acquisition", + pages: [ + { + name: "Plans", + element: , + }, + ], + }, + { + name: "Workflows", + icon: , + path: "workflows", + pages: [ + { + name: "Workflows", + element: , + }, + ], + }, + ], + }, +]; + +export const router = createRouter({ title: "P99", navigation }); diff --git a/apps/p99/src/routes/Dashboard.tsx b/apps/p99/src/routes/Dashboard.tsx index ea12ed8a..94d72028 100644 --- a/apps/p99/src/routes/Dashboard.tsx +++ b/apps/p99/src/routes/Dashboard.tsx @@ -1,118 +1,31 @@ -import { useState } from "react"; -import { Box, Container, Alert, Grid2 } from "@mui/material"; -import { - usePlans, - useDevices, - useGetWorkerState, - useActiveTask, -} from "@atlas/blueapi-query"; -import type { Plan } from "@atlas/blueapi"; - -import { WorkerStatusBar } from "../components/WorkerStatusBar"; -import { DevicePanel } from "../components/DevicePanel"; -import { PlanCard } from "../components/PlanCard"; - -interface FeedbackState { - type: "success" | "error"; - msg: string; -} - -function Dashboard() { - const { data: plansData, isError } = usePlans(); - const { data: devicesData } = useDevices(); - const { - data: workerState, - isFetching: isWorkerFetching, - refetch: refetchWorkerState, - } = useGetWorkerState(); - const { data: activeTask } = useActiveTask(); - const activeTaskId = activeTask?.task_id ?? null; - - const [feedback, setFeedback] = useState(null); - const [instrumentSession, setInstrumentSession] = useState("p99-session-01"); - - return ( - - {/* Sticky Global Status Header */} - - - {/* Sticky Global Feedback Alert Wrapper */} - {feedback && ( - - setFeedback(null)} - elevation={3} - > - {feedback.msg} - - - )} - - - {isError && ( - - Unauthorized or Service Unreachable. Check proxy configs or Keycloak - authentication. - - )} - - - {/* LEFT COLUMN: Scrollable Plans Library */} - - - {(plansData?.plans || []).map((plan: Plan) => ( - - - setFeedback({ type: "success", msg }) - } - onError={(msg: string) => - setFeedback({ type: "error", msg }) - } - /> - - ))} - - - - {/* RIGHT COLUMN: Persistent Device Panel */} - - - - - - - ); -} +import { Container, Typography, Stack, Button } from "@mui/material"; +import { FileText, ServerCog } from "lucide-react"; +import { Link } from "react-router-dom"; + +const Dashboard = () => ( + + + Welcome to P99! + + + + +); export default Dashboard; diff --git a/apps/p99/src/routes/Layout.tsx b/apps/p99/src/routes/Layout.tsx deleted file mode 100644 index a1dc0119..00000000 --- a/apps/p99/src/routes/Layout.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Outlet } from "react-router-dom"; -import P99Navbar from "../components/P99Navbar"; - -export function Layout() { - return ( -
- - -
- ); -} diff --git a/apps/p99/src/routes/Plans.tsx b/apps/p99/src/routes/Plans.tsx new file mode 100644 index 00000000..3aef019e --- /dev/null +++ b/apps/p99/src/routes/Plans.tsx @@ -0,0 +1,116 @@ +import { + useActiveTask, + useDevices, + useGetWorkerState, + usePlans, +} from "@atlas/blueapi-query"; +import { Alert, Box, Container, Grid } from "@mui/material"; +import { useState } from "react"; +import { WorkerStatusBar } from "../components/plans/WorkerStatusBar"; +import { PlanCard } from "../components/plans/PlanCard"; +import type { Plan } from "@atlas/blueapi"; +import { DevicePanel } from "../components/plans/DevicePanel"; + +interface FeedbackState { + type: "success" | "error"; + msg: string; +} + +const Plans = () => { + const { data: plansData, isError } = usePlans(); + const { data: devicesData } = useDevices(); + const { + data: workerState, + isFetching: isWorkerFetching, + refetch: refetchWorkerState, + } = useGetWorkerState(); + const { data: activeTask } = useActiveTask(); + const activeTaskId = activeTask?.task_id ?? null; + + const [feedback, setFeedback] = useState(null); + const [instrumentSession, setInstrumentSession] = useState("p99-session-01"); + return ( + + {/* Sticky Global Status Header */} + + + {/* Sticky Global Feedback Alert Wrapper */} + {feedback && ( + + setFeedback(null)} + elevation={3} + > + {feedback.msg} + + + )} + + + {isError && ( + + Unauthorized or Service Unreachable. Check proxy configs or Keycloak + authentication. + + )} + + + {/* LEFT COLUMN: Scrollable Plans Library */} + + + {(plansData?.plans || []).map((plan: Plan) => ( + + + setFeedback({ type: "success", msg }) + } + onError={(msg: string) => + setFeedback({ type: "error", msg }) + } + /> + + ))} + + + + {/* RIGHT COLUMN: Persistent Device Panel */} + + + + + + + ); +}; + +export default Plans; diff --git a/apps/p99/src/routes/Workflows.tsx b/apps/p99/src/routes/Workflows.tsx new file mode 100644 index 00000000..d3fe4e2f --- /dev/null +++ b/apps/p99/src/routes/Workflows.tsx @@ -0,0 +1,13 @@ +import { Container } from "@mui/material"; +import { Suspense } from "react"; +import TemplateView from "../components/workflows/TemplateView"; + +const Workflows = () => ( + + + + + +); + +export default Workflows; diff --git a/apps/p99/src/utils/common.ts b/apps/p99/src/utils/common.ts new file mode 100644 index 00000000..e27649f9 --- /dev/null +++ b/apps/p99/src/utils/common.ts @@ -0,0 +1,14 @@ +import { type Visit, regexToVisit } from "@diamondlightsource/sci-react-ui"; + +const visitRegex = /^([a-z]{2})([1-9]\d*)-([1-9]\d*)/; +export const visitWithTemplateRegex = new RegExp(`${visitRegex.source}-(.+)$`); + +export function visitTextToVisit(visitid?: string): Visit | null { + if (visitid) { + const parsedVisit = visitRegex.exec(visitid); + if (parsedVisit != null) { + return regexToVisit(parsedVisit); + } + } + return null; +} diff --git a/apps/p99/src/utils/types.ts b/apps/p99/src/utils/types.ts new file mode 100644 index 00000000..1a15c824 --- /dev/null +++ b/apps/p99/src/utils/types.ts @@ -0,0 +1,46 @@ +import type { Visit } from "@diamondlightsource/sci-react-ui"; +import type { PayloadError } from "relay-runtime"; + +export interface SubmissionSuccessMessage { + type: "success"; + message: string; +} + +export interface SubmissionNetworkErrorMessage { + type: "networkError"; + error: Error; +} + +export interface SubmissionGraphQLErrorMessage { + type: "graphQLError"; + errors: PayloadError[]; +} + +export interface SubmissionData { + submissionResult: + | SubmissionSuccessMessage + | SubmissionGraphQLErrorMessage + | SubmissionNetworkErrorMessage; + visit: Visit; + workflowName?: string; +} + +export type JSONValue = + | string + | number + | boolean + | null + | JSONObject + | JSONValue[]; + +export interface JSONObject { + [key: string]: JSONValue; +} + +export type WorkflowStatus = + | "Unknown" + | "WorkflowPendingStatus" + | "WorkflowRunningStatus" + | "WorkflowSucceededStatus" + | "WorkflowFailedStatus" + | "WorkflowErroredStatus"; diff --git a/apps/p99/supergraph.graphql b/apps/p99/supergraph.graphql new file mode 100644 index 00000000..6fe95ee4 --- /dev/null +++ b/apps/p99/supergraph.graphql @@ -0,0 +1,935 @@ +type Artifact { + """The file name of the artifact""" + name: String! + + """The download URL for the artifact""" + url: Url! + + """The MIME type of the artifact data""" + mimeType: String! +} + +scalar Creator + +""" +Implement the DateTime scalar + +The input/output is a string in RFC3339 format. +""" +scalar DateTime + +""" +The `JSON` scalar type represents JSON values as specified by [ECMA-404](https://ecma-international.org/wp-content/uploads/ECMA-404_2nd_edition_december_2017.pdf). +""" +scalar JSON + +"""A scalar that can represent any JSON Object value.""" +scalar JSONObject + +"""A single log line streamed from a pod""" +type LogEntry { + """The log line content""" + content: String! + + """The name of the pod producing the log""" + podName: String! +} + +"""The root mutation of the service""" +type Mutation { + """submit specific workflow template""" + submitWorkflowTemplate(name: String!, visit: VisitInput!, parameters: JSON!): Workflow! + instrumentSession(proposalNumber: Int!, instrumentSessionNumber: Int!): InstrumentSessionMutations + createOrValidateSamples(input: CreateOrValidateSampleInput!): CreateSamplesResponse! + createSamples(input: CreateSampleInput!): [Sample!]! @deprecated(reason: "Will be replaced by createOrValidateSamples") + sample(sampleId: UUID!): SampleMutations + createExperimentDefinition(input: CreateExperimentDefinitionInput!): ExperimentDefinition + experimentDefinition(id: UUID!): ExperimentDefinitionMutations +} + +"""Represents Relay Node types""" +union NodeValue = Workflow + +"""Information about pagination in a connection""" +type PageInfo { + """When paginating backwards, are there more items?""" + hasPreviousPage: Boolean! + + """When paginating forwards, are there more items?""" + hasNextPage: Boolean! + + """When paginating backwards, the cursor to continue.""" + startCursor: String + + """When paginating forwards, the cursor to continue.""" + endCursor: String +} + +"""The root query of the service""" +type Query { + node(id: ID!): NodeValue + + """Get a single [`Workflow`] by proposal, visit, and name""" + workflow(visit: VisitInput!, name: String!): Workflow! + + """Find all workflows available for a given visit""" + workflows(visit: VisitInput!, cursor: String, limit: Int, filter: WorkflowFilter): WorkflowConnection! + + """Retrieves a single cluster workflow template by name""" + workflowTemplate(name: String!): WorkflowTemplate! + + """ + Retrieves all cluster workflow templates with respective pagination data + """ + workflowTemplates(cursor: String, limit: Int, filter: WorkflowTemplatesFilter): WorkflowTemplateConnection! + + """Get a proposal by its number""" + proposal(proposalNumber: Int!): Proposal + + """Get a list of proposals""" + proposals(proposalCategory: String = null, first: Int = null, last: Int = null, after: String = null, before: String = null): ProposalConnection! + + """Get a instrument session""" + instrumentSession(proposalNumber: Int!, instrumentSessionNumber: Int!): InstrumentSession + + """Get a instrument session""" + instrumentSessions(proposalNumber: Int = null, proposalCategory: String = null): [InstrumentSession!] + + """Get an instrument""" + instrument(instrumentName: String!): Instrument + + """Get a list of instruments""" + instruments(scienceGroup: String = null): [Instrument!]! + + """Get an account""" + account(username: String!): Account + jsonSchema(url: String!): JSONSchema + jsonSchemas(type: String = null, instrument: String = null): [JSONSchema!]! + + """Get a sample by its id""" + sample(sampleId: UUID!): Sample + + """Get a list of samples associated with a given instrument session""" + samples(first: Int!, instrumentSessions: [InstrumentSessionInput!] = null, filter: SampleFilterInput! = {schemaUrl: null, createdTime: null, updatedTime: null, name: null, data: null}, before: String = null, after: String = null, last: Int = null, orderBy: SampleOrder! = {name: null, createdTime: null, updatedTime: null}): SampleConnection! + experimentDefinition(id: UUID!): ExperimentDefinition + experimentDefinitions(instrumentSessions: [InstrumentSessionInput!]!, first: Int = null, last: Int = null, after: String = null, before: String = null): ExperimentDefinitionConnection + experiment(id: UUID!): Experiment + experiments(instrumentSessions: [InstrumentSessionInput!]!, first: Int = null, last: Int = null, after: String = null, before: String = null): ExperimentConnection +} + +"""Supported DLS science groups""" +enum ScienceGroup { + """Macromolecular Crystallography""" + MX + + """Workflows Examples""" + EXAMPLES + + """Magnetic Materials""" + MAGNETIC_MATERIALS + + """Soft Condensed Matter""" + CONDENSED_MATTER + + """Imaging and Microscopy""" + IMAGING + + """Biological Cryo-Imaging""" + BIO_CRYO_IMAGING + + """Structures and Surfaces""" + SURFACES + + """Crystallography""" + CRYSTALLOGRAPHY + + """Spectroscopy""" + SPECTROSCOPY +} + +"""The root mutation of the service""" +type Subscription { + """Processing to subscribe to logs for a single pod of a workflow""" + logs(visit: VisitInput!, workflowName: String!, taskId: String!): LogEntry! + + """Processing to subscribe to data for all workflows in a session""" + workflow(visit: VisitInput!, name: String!): Workflow! +} + +type Task { + """Unique name of the task""" + id: String! + + """Display name of the task""" + name: String! + + """Current status of a task""" + status: TaskStatus! + + """Parent of a task""" + depends: [String!]! + + """Children of a task""" + dependencies: [String!]! + + """Artifacts produced by a task""" + artifacts: [Artifact!]! + + """Node type - Pod, DAG, etc""" + stepType: String! + + """Start time for a task on a workflow""" + startTime: DateTime + + """End time for a task on a workflow""" + endTime: DateTime + + """ + A human readable message indicating details about why this step is in this condition + """ + message: String +} + +enum TaskStatus { + PENDING + RUNNING + SUCCEEDED + SKIPPED + FAILED + ERROR + OMITTED +} + +scalar Template + +"""Information about where the template is stored""" +type TemplateSource { + """The URL of the GitHub repository""" + repositoryUrl: String! + + """The path to the template within the repository""" + path: String! + + """The current tracked branch of the repository""" + targetRevision: String! +} + +""" +URL is a String implementing the [URL Standard](http://url.spec.whatwg.org/) +""" +scalar Url + +"""A visit to an instrument as part of a session""" +type Visit { + """Project Proposal Code""" + proposalCode: String! + + """Project Proposal Number""" + proposalNumber: Int! + + """Session visit Number""" + number: Int! +} + +"""A visit to an instrument as part of a session""" +input VisitInput { + """Project Proposal Code""" + proposalCode: String! + + """Project Proposal Number""" + proposalNumber: Int! + + """Session visit Number""" + number: Int! +} + +type Workflow { + """The unique ID derived from the visit and name""" + id: ID! + + """The name given to the workflow, unique within a given visit""" + name: String! + + """The visit the Workflow was run against""" + visit: Visit! + + """The current status of the workflow""" + status: WorkflowStatus + + """The top-level workflow parameters""" + parameters: JSONObject + + """The name of the template used to run the workflow""" + templateRef: String + + """The workflow creator""" + creator: WorkflowCreator! +} + +type WorkflowConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [WorkflowEdge!]! + + """A list of nodes.""" + nodes: [Workflow!]! +} + +"""Information about the creator of a workflow.""" +type WorkflowCreator { + """ + An identifier unique to the creator of the workflow. + Typically this is the creator's Fed-ID. + """ + creatorId: String! +} + +"""An edge in a connection.""" +type WorkflowEdge { + """The item at the end of the edge""" + node: Workflow! + + """A cursor for use in pagination""" + cursor: String! +} + +"""All tasks in the workflow have errored""" +type WorkflowErroredStatus { + """Time at which this workflow started""" + startTime: DateTime! + + """Time at which this workflow completed""" + endTime: DateTime! + + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String + + """Tasks created by the workflow""" + tasks: [Task!]! +} + +"""All tasks in the workflow have failed""" +type WorkflowFailedStatus { + """Time at which this workflow started""" + startTime: DateTime! + + """Time at which this workflow completed""" + endTime: DateTime! + + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String + + """Tasks created by the workflow""" + tasks: [Task!]! +} + +"""All the supported Workflows filters""" +input WorkflowFilter { + """The status field for a workflow""" + workflowStatusFilter: WorkflowStatusFilter + + """The fedid of the user who created the workflow""" + creator: Creator + + """The name of the workflow template""" + template: Template +} + +type WorkflowPendingStatus { + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String +} + +type WorkflowRunningStatus { + """Time at which this workflow started""" + startTime: DateTime! + + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String + + """Tasks created by the workflow""" + tasks: [Task!]! +} + +"""The status of a workflow""" +union WorkflowStatus = WorkflowPendingStatus | WorkflowRunningStatus | WorkflowSucceededStatus | WorkflowFailedStatus | WorkflowErroredStatus + +"""Represents workflow status filters""" +input WorkflowStatusFilter { + pending: Boolean! = false + running: Boolean! = false + succeeded: Boolean! = false + failed: Boolean! = false + error: Boolean! = false +} + +"""All tasks in the workflow have succeded""" +type WorkflowSucceededStatus { + """Time at which this workflow started""" + startTime: DateTime! + + """Time at which this workflow completed""" + endTime: DateTime! + + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String + + """Tasks created by the workflow""" + tasks: [Task!]! +} + +type WorkflowTemplate { + """The name given to the workflow template, globally unique""" + name: String! + + """The group who maintains the workflow template""" + maintainer: String! + + """A human readable title for the workflow template""" + title: String + + """A human readable description of the workflow which is created""" + description: String + + """The repository storing the code associated with this template.""" + repository: String + + """A JSON Schema describing the arguments of a Workflow Template""" + arguments: JSON! + + """ + A JSON Forms UI Schema describing how to render the arguments of the Workflow Template + """ + uiSchema: JSON + + """Information about where the template is obtained from""" + templateSource: TemplateSource +} + +type WorkflowTemplateConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [WorkflowTemplateEdge!]! + + """A list of nodes.""" + nodes: [WorkflowTemplate!]! +} + +"""An edge in a connection.""" +type WorkflowTemplateEdge { + """The item at the end of the edge""" + node: WorkflowTemplate! + + """A cursor for use in pagination""" + cursor: String! +} + +"""Supported label filters for ClusterWorkflowTemplates""" +input WorkflowTemplatesFilter { + """The science group owning the template eg imaging""" + scienceGroup: [ScienceGroup!] +} + +type Account { + accountId: Int! + username: String! + emailAddress: String + title: String + givenName: String + familyName: String + type: AccountType! + state: AccountState! + proposalRoles: [ProposalAccount!]! + instrumentSessionRoles: [InstrumentSessionRole!]! +} + +enum AccountState { + enabled + disabled +} + +enum AccountType { + user + staff + functional +} + +type Instrument { + name: String! + scienceGroup: String + description: String + proposals: [Proposal!]! + instrumentSessions: [InstrumentSession!]! +} + +type InstrumentSession { + instrumentSessionId: Int! + instrumentSessionNumber: Int! + startTime: DateTime + endTime: DateTime + type: String + state: String + riskRating: String + proposal: Proposal + instrument: Instrument! + roles: [InstrumentSessionRole!]! + + """Samples associated with a given instrument session""" + samples(first: Int!, filter: SampleFilterInput! = {schemaUrl: null, createdTime: null, updatedTime: null, name: null, data: null}, before: String = null, after: String = null, last: Int = null, orderBy: SampleOrder! = {name: null, createdTime: null, updatedTime: null}): SampleConnection! + + """Experiment Definitions""" + experimentDefinitions(first: Int = null, last: Int = null, after: String = null, before: String = null): ExperimentDefinitionConnection! + + """Experiments associated with this session""" + experiments(first: Int = null, last: Int = null, after: String = null, before: String = null): ExperimentConnection! +} + +type InstrumentSessionMutations { + instrumentSessionNumber: Int! + proposalNumber: Int! + + """Create or validate samples associated with this instrument session""" + createOrValidateSamples(input: CreateOrValidateSampleInputBase!): CreateSamplesResponse! +} + +type InstrumentSessionRole { + instrumentSession: InstrumentSession! + account: Account! + role: String! + onSite: Boolean! +} + +type Proposal { + proposalNumber: Int! + proposalCategory: String + title: String + summary: String + state: ProposalState! + instrumentSessions: [InstrumentSession!]! + instruments: [Instrument!]! + roles: [ProposalAccount!]! +} + +type ProposalAccount { + proposal: Proposal! + account: Account! + role: String! +} + +type ProposalConnection { + edges: [ProposalEdge!]! + pageInfo: PageInfo! +} + +type ProposalEdge { + cursor: String! + node: Proposal! +} + +enum ProposalState { + Open + Closed + Cancelled +} + +"""A JSON schema""" +type JSONSchema { + """The identifier of the schema""" + id: String! + + """A URL from which the schema can be accessed""" + url: String! + + """The type of object the shema describes (if known)""" + type: String + + """The title of the schema""" + title: String + + """The version of the schema""" + version: String + + """The instrument the schema was created for""" + instrument: String + + """The description og the schema""" + description: String +} + +input AddSampleEventInput { + description: String! +} + +input CreateOrValidateSampleInput { + """URL of the JSON schema the samples' `data` should be validated against""" + dataSchemaUrl: String! + + """Samples to be created""" + samples: [SampleIn!]! + + """ + Whether or not the provided samples should only be validated and not created + """ + validateOnly: Boolean! = false + + """Number of the proposal the samples should be associated with""" + proposalNumber: Int! + + """Number of the instrument session the samples should be associated with""" + instrumentSessionNumber: Int! +} + +input CreateOrValidateSampleInputBase { + """URL of the JSON schema the samples' `data` should be validated against""" + dataSchemaUrl: String! + + """Samples to be created""" + samples: [SampleIn!]! + + """ + Whether or not the provided samples should only be validated and not created + """ + validateOnly: Boolean! = false +} + +input CreateSampleInput { + proposalNumber: Int! + instrumentSessionNumber: Int! + samples: [SampleInLegacy!]! + validateOnly: Boolean! = false +} + +"""Return type when creating or validating samples""" +type CreateSamplesResponse { + """Whether the operation has succeeded without validation errors""" + success: Boolean! + + """Samples that have been created""" + samples: [Sample!]! + + """Errors that occurred during sample validation""" + errors: [SampleValidationError!]! +} + +input DatetimeOperatorInput { + """ + Will filter to items where the `DateTime` field is greater than (i.e. after) the provided value + """ + gt: DateTime = null + + """ + Will filter to items where the `DateTime` field is less than (i.e. before) the provided value + """ + lt: DateTime = null +} + +"""The details of sample validation error""" +type ErrorDetails { + """The type of error that occurred""" + type: String! + + """ + Tuple of strings identifying where in the sample schema the error occurred. + """ + location: [String!]! + + """A human readable error message.""" + message: String! +} + +"""Values required to uniquely identify an instrument session""" +input InstrumentSessionInput { + proposalNumber: Int! + instrumentSessionNumber: Int! +} + +input JSONOperator @oneOf { + stringOperator: StringOperatorInput = null + datetimeOperator: DatetimeOperatorInput = null + numericOperator: NumericOperatorInput = null +} + +input JSONOperatorInput { + """A JSON path specifying the value to filter. Must start with '$.'""" + path: String! + + """The operator to apply to the JSON field""" + operator: JSONOperator! +} + +input NumericOperatorInput { + """ + Will filter to items where the numeric field is greater than the provided value + """ + gt: Float = null + + """ + Will filter to items where the numeric field is less than the provided value + """ + lt: Float = null +} + +type Sample { + id: UUID! + name: String! + data: JSON! + createdTime: DateTime! + updatedTime: DateTime! + dataSchemaUrl: String! + + """Samples from which this sample is derived""" + parents(first: Int = null, before: String = null, after: String = null, last: Int = null): SampleConnection! + + """Samples derived from this sample""" + children(first: Int = null, before: String = null, after: String = null, last: Int = null): SampleConnection! + + """Events linked to this sample""" + events(first: Int = null, before: String = null, after: String = null, last: Int = null): SampleEventConnection! + + """The JSON schema that the sample's `data` conforms to""" + dataSchema: JSON! + + """The instrument sessions that this sample is associated with""" + instrumentSessions: [InstrumentSession!]! + images: [SampleImage!]! + experiments: [Experiment!]! +} + +type SampleConnection { + edges: [SampleEdge!]! + pageInfo: PageInfo! +} + +type SampleEdge { + cursor: String! + node: Sample! +} + +type SampleEvent { + id: UUID! + timestamp: DateTime! + description: String! +} + +type SampleEventConnection { + edges: [SampleEventEdge!]! + pageInfo: PageInfo! +} + +type SampleEventEdge { + cursor: String! + node: SampleEvent! +} + +input SampleFilterInput { + """Filter on the `schemaUrl` field of `Sample`""" + schemaUrl: StringOperatorInput = null + + """Filter on the `createdTime` field of `Sample`""" + createdTime: DatetimeOperatorInput = null + + """Filter on the `createdTime` field of `Sample`""" + updatedTime: DatetimeOperatorInput = null + + """Filter on the `name` field of `Sample`""" + name: StringOperatorInput = null + + """Filter on the `data` field of `Sample`""" + data: [JSONOperatorInput!] = null +} + +type SampleImage { + url: String! + filename: String! +} + +input SampleIn { + """Name of the sample""" + name: String! + + """Data of the sample""" + data: JSON! +} + +input SampleInLegacy { + name: String! + data: JSON! + dataSchemaUrl: String! + parentIds: [Int!] = null + children: [SampleInLegacy!] = null +} + +type SampleMutations { + sampleId: UUID! + updateSample(input: UpdateSampleInput!): UpdateSampleResponse! + linkInstrumentSessionToSample(proposalNumber: Int!, instrumentSessionNumber: Int!): Void + addSampleEvent(sampleEvent: AddSampleEventInput!): SampleEvent! + createSampleImageUploadUrl(filename: String!, contentType: String!, contentLength: Int!): String! +} + +input SampleOrder { + name: SortingOrder = null + createdTime: SortingOrder = null + updatedTime: SortingOrder = null +} + +"""The details of errors occurred when validating a sample""" +type SampleValidationError { + """ + The index of the sample in CreateSampleInput.samples for which the error occurred + """ + index: Int! + + """Errors that occurred when validating the sample""" + errors: [ErrorDetails!]! +} + +enum SortingOrder { + ASC + DESC +} + +"""Conditions used to filter results based on the value of a String field""" +input StringOperatorInput { + """ + Will filter to items where the `String` field is equal to the provided value + """ + eq: String = null + + """ + Will filter to items where the `String` field is not equal to the provided value + """ + ne: String = null + + """ + Will filter to items where the `String` field is a member of the provided value + """ + in: [String!] = null + + """ + Will filter to items where the `String` field is not a member of the provided value + """ + nin: [String!] = null + + """ + Will filter to items where the `String` field is contains the provided value + """ + contains: String = null +} + +scalar UUID + +input UpdateSampleInput { + """Name of the sample""" + name: String + + """Data of the sample""" + data: JSON + + """URL of the JSON schema the samples' `data` should be validated against""" + dataSchemaUrl: String +} + +"""Return type when creating or validating samples""" +type UpdateSampleResponse { + """Whether the operation has succeeded without validation errors""" + success: Boolean! + + """Sample that has been updated""" + sample: Sample + + """Errors that occurred during sample validation""" + errors: [SampleValidationError!]! +} + +"""Represents NULL values""" +scalar Void + +"""Values required to create an experiment definition""" +input CreateExperimentDefinitionInput { + name: String! + data: JSON! + dataSchemaUrl: String! + proposalNumber: Int! + instrumentSessionNumber: Int! +} + +"""Values required for the createExperiments mutation""" +input CreateExperimentsInput { + experiments: [ExperimentInput!]! +} + +type Experiment { + id: UUID! + name: String! + createdTime: DateTime! + updatedTime: DateTime! + experimentDefinition: ExperimentDefinition! + + """The sample that this experiment is associated with""" + sample: Sample! +} + +type ExperimentConnection { + edges: [ExperimentEdge!]! + pageInfo: PageInfo! +} + +type ExperimentDefinition { + id: UUID! + name: String! + createdTime: DateTime! + updatedTime: DateTime! + data: JSON! + dataSchemaUrl: String! + proposalNumber: Int! + instrumentSessionNumber: Int! + + """ + The instrument session that this experiment definition is associated with + """ + instrumentSession: InstrumentSession! + + """Experiments associated with this experiment definition""" + experiments: [Experiment!]! +} + +type ExperimentDefinitionConnection { + edges: [ExperimentDefinitionEdge!]! + pageInfo: PageInfo! +} + +type ExperimentDefinitionEdge { + cursor: String! + node: ExperimentDefinition! +} + +"""Mutations for a given experiment defintion""" +type ExperimentDefinitionMutations { + createExperiments(input: CreateExperimentsInput!): [Experiment!]! +} + +type ExperimentEdge { + cursor: String! + node: Experiment! +} + +"""Values required to create an experiment""" +input ExperimentInput { + name: String! + sampleId: UUID! +} \ No newline at end of file diff --git a/apps/visr/.prettierrc b/apps/visr/.prettierrc index 2aecfbd4..e75e4246 100644 --- a/apps/visr/.prettierrc +++ b/apps/visr/.prettierrc @@ -3,4 +3,4 @@ "semi": true, "singleQuote": false, "trailingComma": "all" -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62702b06..6cad7a42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,7 +26,7 @@ importers: version: 18.3.7(@types/react@18.3.28) '@vitest/coverage-v8': specifier: ^4.0.13 - version: 4.0.18(vitest@4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0)) + version: 4.0.18(vitest@4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0)) eslint: specifier: ^9.36.0 version: 9.39.3(jiti@2.7.0) @@ -47,7 +47,7 @@ importers: version: 8.56.1(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) vitest: specifier: ^4.0.13 - version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) + version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) apps/i15-1: dependencies: @@ -71,10 +71,10 @@ importers: version: link:../../packages/pvws-config '@diamondlightsource/cs-web-lib': specifier: 0.10.15 - version: 0.10.15(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(graphql@16.14.2)(plotly.js@2.35.3(mapbox-gl@1.13.3)(webpack@5.106.2(postcss@8.5.15)))(react-dom@18.3.1(react@18.3.1))(react-gauge-component@2.0.29(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router@7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 0.10.15(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(graphql@16.14.2)(plotly.js@2.35.3(mapbox-gl@1.13.3)(webpack@5.106.2(postcss@8.5.15)))(react-dom@18.3.1(react@18.3.1))(react-gauge-component@2.0.29(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router@7.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@diamondlightsource/sci-react-ui': specifier: ^0.4.0 - version: 0.4.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@jsonforms/core@3.6.0)(@jsonforms/material-renderers@3.6.0(a2958437c922d885fd4a4f8b5e701907))(@jsonforms/react@3.6.0(@jsonforms/core@3.6.0)(react@18.3.1))(@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 0.4.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@jsonforms/core@3.6.0)(@jsonforms/material-renderers@3.6.0(4ba03d2af706f330f00ef32182c08d47))(@jsonforms/react@3.6.0(@jsonforms/core@3.6.0)(react@18.3.1))(@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@mui/icons-material': specifier: ^6.5.0 version: 6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) @@ -95,7 +95,7 @@ importers: version: 1.17.0(react@18.3.1) material-react-table: specifier: ^3.2.1 - version: 3.2.1(5b559ea0d387c19eea1508254820d836) + version: 3.2.1(c73fd2a924619084f45aa81401e957b8) react-error-boundary: specifier: ^6.0.0 version: 6.1.1(react@18.3.1) @@ -154,6 +154,9 @@ importers: apps/p99: dependencies: + '@atlas/app-shell': + specifier: workspace:* + version: link:../../packages/app-shell '@atlas/blueapi': specifier: workspace:* version: link:../../packages/blueapi @@ -161,60 +164,93 @@ importers: specifier: workspace:* version: link:../../packages/blueapi-query '@diamondlightsource/sci-react-ui': - specifier: ^0.2.0 - version: 0.2.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + specifier: ^0.4.1 + version: 0.4.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@jsonforms/core@3.6.0)(@jsonforms/material-renderers@3.6.0(04ebbcef168a57df243e45f2f15d1af8))(@jsonforms/react@3.6.0(@jsonforms/core@3.6.0)(react@18.3.1))(@mui/icons-material@7.3.11(@mui/material@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@emotion/react': specifier: ^11.14.0 version: 11.14.0(@types/react@18.3.28)(react@18.3.1) '@emotion/styled': specifier: ^11.14.1 version: 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@jsonforms/core': + specifier: 3.6.0 + version: 3.6.0 + '@jsonforms/material-renderers': + specifier: 3.6.0 + version: 3.6.0(04ebbcef168a57df243e45f2f15d1af8) + '@jsonforms/react': + specifier: 3.6.0 + version: 3.6.0(@jsonforms/core@3.6.0)(react@18.3.1) '@mui/icons-material': - specifier: ^6.5.0 - version: 6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + specifier: ^7.0.0 + version: 7.3.11(@mui/material@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@mui/material': - specifier: <7.0.0 - version: 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^7.0.0 + version: 7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tanstack/react-query': - specifier: ^5.90.21 - version: 5.90.21(react@18.3.1) + specifier: ^5.101.0 + version: 5.101.0(react@18.3.1) '@testing-library/react': specifier: ^15.0.7 version: 15.0.7(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - msw: - specifier: ^2.12.10 - version: 2.12.10(@types/node@25.9.2)(typescript@5.9.3) - vite-plugin-relay: - specifier: ^2.1.0 - version: 2.1.0(babel-plugin-relay@20.1.1)(vite@7.3.1(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + ajv: + specifier: ^8.20.0 + version: 8.20.0 + axios: + specifier: ^1.18.0 + version: 1.18.0 + lucide-react: + specifier: ^1.18.0 + version: 1.18.0(react@18.3.1) + react-relay: + specifier: ^20.1.1 + version: 20.1.1(react@18.3.1) + relay-runtime: + specifier: ^20.1.1 + version: 20.1.1 devDependencies: '@atlas/vitest-conf': specifier: workspace:* version: link:../../packages/vitest-conf '@eslint/js': - specifier: ^9.33.0 - version: 9.39.3 + specifier: ^9.39.4 + version: 9.39.4 + '@types/react-relay': + specifier: ^18.2.1 + version: 18.2.1 + '@types/relay-runtime': + specifier: ^19.0.3 + version: 19.0.3 '@vitejs/plugin-react-swc': specifier: ^3.11.0 - version: 3.11.0(vite@7.3.1(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 3.11.0(vite@7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) eslint-plugin-react-hooks: specifier: ^5.2.0 version: 5.2.0(eslint@9.39.3(jiti@2.7.0)) eslint-plugin-react-refresh: - specifier: ^0.4.20 + specifier: ^0.4.26 version: 0.4.26(eslint@9.39.3(jiti@2.7.0)) globals: - specifier: ^16.3.0 + specifier: ^16.5.0 version: 16.5.0 + msw: + specifier: ^2.10.4 + version: 2.14.6(@types/node@25.9.2)(typescript@5.9.3) react-router-dom: - specifier: ^7.8.0 - version: 7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^7.17.0 + version: 7.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + relay-compiler: + specifier: ^20.1.1 + version: 20.1.1 vite: - specifier: ^7.1.2 - version: 7.3.1(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + specifier: ^7.3.5 + version: 7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite-plugin-relay: + specifier: ^2.1.0 + version: 2.1.0(babel-plugin-relay@20.1.1)(vite@7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) vitest: - specifier: '*' - version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) + specifier: 4.0.18 + version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) apps/visr: dependencies: @@ -244,7 +280,7 @@ importers: version: 3.6.0 '@jsonforms/material-renderers': specifier: 3.6.0 - version: 3.6.0(a2958437c922d885fd4a4f8b5e701907) + version: 3.6.0(4ba03d2af706f330f00ef32182c08d47) '@jsonforms/react': specifier: 3.6.0 version: 3.6.0(@jsonforms/core@3.6.0)(react@18.3.1) @@ -259,7 +295,7 @@ importers: version: 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/x-date-pickers': specifier: ^7.17.0 - version: 7.29.4(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 7.29.4(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@react-three/drei': specifier: ^9.105.4 version: 9.122.0(@react-three/fiber@8.18.0(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.164.1))(@types/react@18.3.28)(@types/three@0.164.1)(immer@11.1.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.164.1)(use-sync-external-store@1.6.0(react@18.3.1)) @@ -351,7 +387,7 @@ importers: version: link:../vitest-conf '@diamondlightsource/sci-react-ui': specifier: ^0.4.1 - version: 0.4.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@jsonforms/core@3.6.0)(@jsonforms/material-renderers@3.6.0(77d78ecfc22988be2f298fed3e42b40a))(@jsonforms/react@3.6.0(@jsonforms/core@3.6.0)(react@18.3.1))(@mui/icons-material@7.3.11(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 0.4.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@jsonforms/core@3.6.0)(@jsonforms/material-renderers@3.6.0(fb42e62d0748259b7f47a01c1ee6614e))(@jsonforms/react@3.6.0(@jsonforms/core@3.6.0)(react@18.3.1))(@mui/icons-material@7.3.11(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@mui/material': specifier: ^6.5.0 version: 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -360,7 +396,7 @@ importers: version: 1.17.0(react@18.3.1) vitest: specifier: '*' - version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) + version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) packages/blueapi: dependencies: @@ -373,7 +409,7 @@ importers: version: link:../vitest-conf vitest: specifier: '*' - version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) + version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) packages/blueapi-query: dependencies: @@ -389,7 +425,7 @@ importers: version: link:../vitest-conf vitest: specifier: '*' - version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) + version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) packages/blueapi-ui: dependencies: @@ -404,7 +440,7 @@ importers: version: 3.6.0 '@jsonforms/material-renderers': specifier: 3.6.0 - version: 3.6.0(77d78ecfc22988be2f298fed3e42b40a) + version: 3.6.0(fb42e62d0748259b7f47a01c1ee6614e) '@jsonforms/react': specifier: 3.6.0 version: 3.6.0(@jsonforms/core@3.6.0)(react@18.3.1) @@ -423,13 +459,13 @@ importers: version: 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) vitest: specifier: '*' - version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) + version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) packages/pvws-config: dependencies: '@diamondlightsource/cs-web-lib': specifier: 0.10.15 - version: 0.10.15(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(plotly.js@2.35.3(mapbox-gl@1.13.3)(webpack@5.106.2))(react-dom@18.3.1(react@18.3.1))(react-gauge-component@2.0.29(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router@7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 0.10.15(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(plotly.js@2.35.3(mapbox-gl@1.13.3)(webpack@5.106.2))(react-dom@18.3.1(react@18.3.1))(react-gauge-component@2.0.29(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router@7.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@mui/material': specifier: <7.0.0 version: 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -442,13 +478,13 @@ importers: version: link:../vitest-conf vitest: specifier: '*' - version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) + version: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) packages/vitest-conf: dependencies: vitest: specifier: '*' - version: 4.0.18(@types/node@25.3.3)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.3.3)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) + version: 4.0.18(@types/node@25.3.3)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.3.3)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) devDependencies: '@testing-library/dom': specifier: ^10.4.1 @@ -945,6 +981,10 @@ packages: resolution: {integrity: sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/object-schema@2.1.7': resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1521,6 +1561,9 @@ packages: '@mui/core-downloads-tracker@6.5.0': resolution: {integrity: sha512-LGb8t8i6M2ZtS3Drn3GbTI1DVhDY6FJ9crEey2lZ0aN2EMZo8IZBZj9wRf4vqbZHaWjsYgtbOnJw5V8UWbmK2Q==} + '@mui/core-downloads-tracker@7.3.11': + resolution: {integrity: sha512-a7I/b/nBTdXYz2cOSlEmkQ9WWE1x8FHpqMhFPp+Y1VPFxcOw91G5ELOHARQAGSPy5V+UCgJua6K/1x70bAtQPw==} + '@mui/icons-material@6.5.0': resolution: {integrity: sha512-VPuPqXqbBPlcVSA0BmnoE4knW4/xG6Thazo8vCLWkOKusko6DtwFV6B665MMWJ9j0KFohTIf3yx2zYtYacvG1g==} engines: {node: '>=14.0.0'} @@ -1584,6 +1627,26 @@ packages: '@types/react': optional: true + '@mui/material@7.3.11': + resolution: {integrity: sha512-yq8bPc3LxOwKRWpcjRgDkYFmpM6aKlARfESTmOQcvLYFeJwtHte2tw6hJDrb8sk8wcvpDprHEHVaoUU0MslIkw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material-pigment-css': ^7.3.11 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/material-pigment-css': + optional: true + '@types/react': + optional: true + '@mui/private-theming@6.4.9': resolution: {integrity: sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==} engines: {node: '>=14.0.0'} @@ -1594,6 +1657,16 @@ packages: '@types/react': optional: true + '@mui/private-theming@7.3.11': + resolution: {integrity: sha512-9B+YKms0fRHbNrqp9tOT/DNbNnU5gyvJ1o3qAGXfq8GmZcbJnE3At9x07Zr/o0pkhzg4aDdwXVqe4+AcgtOCPA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@mui/styled-engine@6.5.0': resolution: {integrity: sha512-8woC2zAqF4qUDSPIBZ8v3sakj+WgweolpyM/FXf8jAx6FMls+IE4Y8VDZc+zS805J7PRz31vz73n2SovKGaYgw==} engines: {node: '>=14.0.0'} @@ -1607,6 +1680,19 @@ packages: '@emotion/styled': optional: true + '@mui/styled-engine@7.3.10': + resolution: {integrity: sha512-WxE9SiF8xskAQqGjsp0poXCkCqsoXFEsSr0HBXfApmGHR+DBnXRp+z46Vsltg4gpPM4Z96DeAQRpeAOnhNg7Ng==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/system@6.5.0': resolution: {integrity: sha512-XcbBYxDS+h/lgsoGe78ExXFZXtuIlSBpn/KsZq8PtZcIkUNJInkuDqcLd2rVBQrDC1u+rvVovdaWPf2FHKJf3w==} engines: {node: '>=14.0.0'} @@ -1623,6 +1709,22 @@ packages: '@types/react': optional: true + '@mui/system@7.3.11': + resolution: {integrity: sha512-7izwGWdNawAKpBKcRlx7f2gFnAAjmASBWvMcyX4YYEeLOFsbfGRbUYGInvnAcUeql3rPxI7F9Ft4oY2OLRz44g==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + '@mui/types@7.2.24': resolution: {integrity: sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==} peerDependencies: @@ -1639,6 +1741,14 @@ packages: '@types/react': optional: true + '@mui/types@7.4.12': + resolution: {integrity: sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@mui/utils@6.4.9': resolution: {integrity: sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==} engines: {node: '>=14.0.0'} @@ -1649,6 +1759,16 @@ packages: '@types/react': optional: true + '@mui/utils@7.3.11': + resolution: {integrity: sha512-XTjGnifwteg71/ij+0e7Y7d+hwyntMYP5wPoA/g2drdGH+Flkvjwy0OfrVpKBbaOvofq4zU/LIyUZyKgmWu18g==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@mui/utils@7.3.8': resolution: {integrity: sha512-kZRcE2620CBGr+XI8YMmwPj6WIPwSF7uMJjvSfqd8zXVvlz0MCJbzRRUGNf8NgflCLthdji2DdS643TeyJ3+nA==} engines: {node: '>=14.0.0'} @@ -1717,6 +1837,9 @@ packages: '@open-draft/deferred-promise@2.2.0': resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + '@open-draft/deferred-promise@3.0.0': + resolution: {integrity: sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==} + '@open-draft/logger@0.3.0': resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} @@ -2051,9 +2174,17 @@ packages: resolution: {integrity: sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==} engines: {node: '>=12'} + '@tanstack/query-core@5.101.0': + resolution: {integrity: sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow==} + '@tanstack/query-core@5.90.20': resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==} + '@tanstack/react-query@5.101.0': + resolution: {integrity: sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==} + peerDependencies: + react: ^18 || ^19 + '@tanstack/react-query@5.90.21': resolution: {integrity: sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==} peerDependencies: @@ -2253,6 +2384,9 @@ packages: '@types/relay-runtime@19.0.3': resolution: {integrity: sha512-pvpWWQq5e9KeESF8klQaP2igLLhr2bRd3XxVCxNpGElsPQiP6Mejr59RT9/OGY3O3i8jAGGQsshVe0QCQDbxNg==} + '@types/set-cookie-parser@2.4.10': + resolution: {integrity: sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==} + '@types/stats.js@0.17.4': resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} @@ -2565,6 +2699,10 @@ packages: afterframe@1.0.2: resolution: {integrity: sha512-0JeMZI7dIfVs5guqLgidQNV7c6jBC2HO0QNSekAUB82Hr7PdU9QXNAF3kpFkvATvHYDDTGto7FPsRu1ey+aKJQ==} + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} @@ -2693,6 +2831,9 @@ packages: axios@1.13.6: resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} + axios@1.18.0: + resolution: {integrity: sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==} + babel-plugin-macros@2.8.0: resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} @@ -3704,6 +3845,15 @@ packages: debug: optional: true + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + font-atlas@2.1.0: resolution: {integrity: sha512-kP3AmvX+HJpW4w3d+PiPR2X6E1yvsBXt2yhuCw+yReO9F1WYhvZwx3c95DGZGwg9xYzDGrgJYa885xmVA+28Cg==} @@ -3952,6 +4102,9 @@ packages: headers-polyfill@4.0.3: resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + headers-polyfill@5.0.1: + resolution: {integrity: sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==} + highlight-words@2.0.0: resolution: {integrity: sha512-If5n+IhSBRXTScE7wl16VPmd+44Vy7kof24EdqhjsZsDuHikpv1OCagVcJFpB4fS4UPUniedlWqrjIO8vWOsIQ==} engines: {node: '>= 20', npm: '>= 9'} @@ -3976,6 +4129,10 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -4325,9 +4482,6 @@ packages: lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} - lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} @@ -4362,6 +4516,11 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + lucide-react@1.18.0: + resolution: {integrity: sha512-LZDb7H/0YfM+RJncD0hDQRCAu+vSGODqpe35TuVI8EuXaRjkczbsx7p8dY4J87F/MUSj6bpYqeI8nw8qXaAdmA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -4518,6 +4677,16 @@ packages: typescript: optional: true + msw@2.14.6: + resolution: {integrity: sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + mumath@3.3.4: resolution: {integrity: sha512-VAFIOG6rsxoc7q/IaY3jdjmrsuX9f15KlRLYTHmixASBZkZEKC1IFqE2BC5CdhXmK6WLM1Re33z//AGmeRI6FA==} deprecated: Redundant dependency in your project. @@ -4840,6 +5009,10 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -4979,6 +5152,13 @@ packages: react: '>=18' react-dom: '>=18' + react-router-dom@7.17.0: + resolution: {integrity: sha512-fyU2yjGups/hE6Xz0I5ZYbVL8Gx29eCjgpHaRaTaVU+OOAdfRX05KsvyRm0GO8YQwOkhpU3MurW1jyMUJn+zSw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + react-router@7.13.1: resolution: {integrity: sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==} engines: {node: '>=20.0.0'} @@ -4989,6 +5169,16 @@ packages: react-dom: optional: true + react-router@7.17.0: + resolution: {integrity: sha512-FDELK7rTMlCHO5+reyXsPlmfr7N1F91lPHsWYfMEGQm/KQ+F4JFM8jGoeQDmDvdTs93Fw9aSilH+uKRb4/jXvQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + react-select@5.10.2: resolution: {integrity: sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==} peerDependencies: @@ -5152,6 +5342,9 @@ packages: rettime@0.10.1: resolution: {integrity: sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==} + rettime@0.11.11: + resolution: {integrity: sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -5230,6 +5423,9 @@ packages: set-cookie-parser@2.7.2: resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + set-cookie-parser@3.1.0: + resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} + setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} @@ -5558,6 +5754,10 @@ packages: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + title-case@3.0.3: resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==} @@ -5597,6 +5797,10 @@ packages: resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} engines: {node: '>=16'} + tough-cookie@6.0.1: + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} + engines: {node: '>=16'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -5681,6 +5885,10 @@ packages: resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==} engines: {node: '>=20'} + type-fest@5.7.0: + resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==} + engines: {node: '>=20'} + type@2.7.3: resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} @@ -5854,6 +6062,46 @@ packages: yaml: optional: true + vite@7.3.5: + resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitest@4.0.18: resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6356,7 +6604,7 @@ snapshots: optionalDependencies: dayjs: 1.10.7 - '@diamondlightsource/cs-web-lib@0.10.15(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(graphql@16.14.2)(plotly.js@2.35.3(mapbox-gl@1.13.3)(webpack@5.106.2(postcss@8.5.15)))(react-dom@18.3.1(react@18.3.1))(react-gauge-component@2.0.29(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router@7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@diamondlightsource/cs-web-lib@0.10.15(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(graphql@16.14.2)(plotly.js@2.35.3(mapbox-gl@1.13.3)(webpack@5.106.2(postcss@8.5.15)))(react-dom@18.3.1(react@18.3.1))(react-gauge-component@2.0.29(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router@7.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: '@mui/icons-material': 7.3.11(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@reduxjs/toolkit': 2.12.0(react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) @@ -6370,7 +6618,7 @@ snapshots: react-grid-layout: 2.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-plotly.js: 2.6.0(plotly.js@2.35.3(mapbox-gl@1.13.3)(webpack@5.106.2(postcss@8.5.15)))(react@18.3.1) react-redux: 7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-router: 7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router: 7.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-toastify: 11.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) utf-8-validate: 5.0.10 uuid: 9.0.1 @@ -6379,7 +6627,7 @@ snapshots: - graphql - plotly.js - '@diamondlightsource/cs-web-lib@0.10.15(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(plotly.js@2.35.3(mapbox-gl@1.13.3)(webpack@5.106.2))(react-dom@18.3.1(react@18.3.1))(react-gauge-component@2.0.29(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router@7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@diamondlightsource/cs-web-lib@0.10.15(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(plotly.js@2.35.3(mapbox-gl@1.13.3)(webpack@5.106.2))(react-dom@18.3.1(react@18.3.1))(react-gauge-component@2.0.29(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router@7.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: '@mui/icons-material': 7.3.11(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@reduxjs/toolkit': 2.12.0(react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) @@ -6393,7 +6641,7 @@ snapshots: react-grid-layout: 2.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-plotly.js: 2.6.0(plotly.js@2.35.3(mapbox-gl@1.13.3)(webpack@5.106.2))(react@18.3.1) react-redux: 7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-router: 7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router: 7.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-toastify: 11.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) utf-8-validate: 5.0.10 uuid: 9.0.1 @@ -6443,26 +6691,26 @@ snapshots: react: 18.3.1 react-icons: 5.6.0(react@18.3.1) - '@diamondlightsource/sci-react-ui@0.4.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@jsonforms/core@3.6.0)(@jsonforms/material-renderers@3.6.0(77d78ecfc22988be2f298fed3e42b40a))(@jsonforms/react@3.6.0(@jsonforms/core@3.6.0)(react@18.3.1))(@mui/icons-material@7.3.11(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@diamondlightsource/sci-react-ui@0.4.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@jsonforms/core@3.6.0)(@jsonforms/material-renderers@3.6.0(04ebbcef168a57df243e45f2f15d1af8))(@jsonforms/react@3.6.0(@jsonforms/core@3.6.0)(react@18.3.1))(@mui/icons-material@7.3.11(@mui/material@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: '@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1) '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@jsonforms/core': 3.6.0 - '@jsonforms/material-renderers': 3.6.0(77d78ecfc22988be2f298fed3e42b40a) + '@jsonforms/material-renderers': 3.6.0(04ebbcef168a57df243e45f2f15d1af8) '@jsonforms/react': 3.6.0(@jsonforms/core@3.6.0)(react@18.3.1) - '@mui/icons-material': 7.3.11(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) - '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/icons-material': 7.3.11(@mui/material@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@mui/material': 7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) keycloak-js: 26.2.4 react: 18.3.1 react-icons: 5.6.0(react@18.3.1) utif: 3.1.0 - '@diamondlightsource/sci-react-ui@0.4.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@jsonforms/core@3.6.0)(@jsonforms/material-renderers@3.6.0(a2958437c922d885fd4a4f8b5e701907))(@jsonforms/react@3.6.0(@jsonforms/core@3.6.0)(react@18.3.1))(@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@diamondlightsource/sci-react-ui@0.4.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@jsonforms/core@3.6.0)(@jsonforms/material-renderers@3.6.0(4ba03d2af706f330f00ef32182c08d47))(@jsonforms/react@3.6.0(@jsonforms/core@3.6.0)(react@18.3.1))(@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: '@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1) '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@jsonforms/core': 3.6.0 - '@jsonforms/material-renderers': 3.6.0(a2958437c922d885fd4a4f8b5e701907) + '@jsonforms/material-renderers': 3.6.0(4ba03d2af706f330f00ef32182c08d47) '@jsonforms/react': 3.6.0(@jsonforms/core@3.6.0)(react@18.3.1) '@mui/icons-material': 6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -6471,6 +6719,20 @@ snapshots: react-icons: 5.6.0(react@18.3.1) utif: 3.1.0 + '@diamondlightsource/sci-react-ui@0.4.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@jsonforms/core@3.6.0)(@jsonforms/material-renderers@3.6.0(fb42e62d0748259b7f47a01c1ee6614e))(@jsonforms/react@3.6.0(@jsonforms/core@3.6.0)(react@18.3.1))(@mui/icons-material@7.3.11(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + dependencies: + '@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@jsonforms/core': 3.6.0 + '@jsonforms/material-renderers': 3.6.0(fb42e62d0748259b7f47a01c1ee6614e) + '@jsonforms/react': 3.6.0(@jsonforms/core@3.6.0)(react@18.3.1) + '@mui/icons-material': 7.3.11(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + keycloak-js: 26.2.4 + react: 18.3.1 + react-icons: 5.6.0(react@18.3.1) + utif: 3.1.0 + '@emotion/babel-plugin@11.13.5': dependencies: '@babel/helper-module-imports': 7.28.6 @@ -6688,6 +6950,8 @@ snapshots: '@eslint/js@9.39.3': {} + '@eslint/js@9.39.4': {} + '@eslint/object-schema@2.1.7': {} '@eslint/plugin-kit@0.4.1': @@ -7182,14 +7446,6 @@ snapshots: optionalDependencies: '@types/node': 25.9.2 - '@inquirer/confirm@5.1.21(@types/node@25.3.3)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@25.3.3) - '@inquirer/type': 3.0.10(@types/node@25.3.3) - optionalDependencies: - '@types/node': 25.3.3 - optional: true - '@inquirer/confirm@5.1.21(@types/node@25.9.2)': dependencies: '@inquirer/core': 10.3.2(@types/node@25.9.2) @@ -7197,6 +7453,14 @@ snapshots: optionalDependencies: '@types/node': 25.9.2 + '@inquirer/confirm@6.1.1(@types/node@25.3.3)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@25.3.3) + '@inquirer/type': 4.0.7(@types/node@25.3.3) + optionalDependencies: + '@types/node': 25.3.3 + optional: true + '@inquirer/confirm@6.1.1(@types/node@25.9.2)': dependencies: '@inquirer/core': 11.2.1(@types/node@25.9.2) @@ -7204,32 +7468,31 @@ snapshots: optionalDependencies: '@types/node': 25.9.2 - '@inquirer/core@10.3.2(@types/node@25.3.3)': + '@inquirer/core@10.3.2(@types/node@25.9.2)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.3.3) + '@inquirer/type': 3.0.10(@types/node@25.9.2) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.3.3 - optional: true + '@types/node': 25.9.2 - '@inquirer/core@10.3.2(@types/node@25.9.2)': + '@inquirer/core@11.2.1(@types/node@25.3.3)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.2) + '@inquirer/ansi': 2.0.7 + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.3.3) cli-width: 4.1.0 - mute-stream: 2.0.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 signal-exit: 4.1.0 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.2 + '@types/node': 25.3.3 + optional: true '@inquirer/core@11.2.1(@types/node@25.9.2)': dependencies: @@ -7330,15 +7593,15 @@ snapshots: optionalDependencies: '@types/node': 25.9.2 - '@inquirer/type@3.0.10(@types/node@25.3.3)': - optionalDependencies: - '@types/node': 25.3.3 - optional: true - '@inquirer/type@3.0.10(@types/node@25.9.2)': optionalDependencies: '@types/node': 25.9.2 + '@inquirer/type@4.0.7(@types/node@25.3.3)': + optionalDependencies: + '@types/node': 25.3.3 + optional: true + '@inquirer/type@4.0.7(@types/node@25.9.2)': optionalDependencies: '@types/node': 25.9.2 @@ -7370,25 +7633,25 @@ snapshots: '@jsonforms/core@3.6.0': dependencies: '@types/json-schema': 7.0.15 - ajv: 8.18.0 - ajv-formats: 2.1.1(ajv@8.18.0) - lodash: 4.17.23 + ajv: 8.20.0 + ajv-formats: 2.1.1(ajv@8.20.0) + lodash: 4.18.1 - '@jsonforms/material-renderers@3.6.0(77d78ecfc22988be2f298fed3e42b40a)': + '@jsonforms/material-renderers@3.6.0(04ebbcef168a57df243e45f2f15d1af8)': dependencies: '@date-io/dayjs': 3.2.0(dayjs@1.10.7) '@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1) '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@jsonforms/core': 3.6.0 '@jsonforms/react': 3.6.0(@jsonforms/core@3.6.0)(react@18.3.1) - '@mui/icons-material': 7.3.11(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) - '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mui/x-date-pickers': 7.29.4(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/icons-material': 7.3.11(@mui/material@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@mui/material': 7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/x-date-pickers': 7.29.4(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) dayjs: 1.10.7 - lodash: 4.17.23 + lodash: 4.18.1 react: 18.3.1 - '@jsonforms/material-renderers@3.6.0(a2958437c922d885fd4a4f8b5e701907)': + '@jsonforms/material-renderers@3.6.0(4ba03d2af706f330f00ef32182c08d47)': dependencies: '@date-io/dayjs': 3.2.0(dayjs@1.10.7) '@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1) @@ -7397,15 +7660,29 @@ snapshots: '@jsonforms/react': 3.6.0(@jsonforms/core@3.6.0)(react@18.3.1) '@mui/icons-material': 6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mui/x-date-pickers': 7.29.4(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/x-date-pickers': 7.29.4(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) dayjs: 1.10.7 - lodash: 4.17.23 + lodash: 4.18.1 + react: 18.3.1 + + '@jsonforms/material-renderers@3.6.0(fb42e62d0748259b7f47a01c1ee6614e)': + dependencies: + '@date-io/dayjs': 3.2.0(dayjs@1.10.7) + '@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@jsonforms/core': 3.6.0 + '@jsonforms/react': 3.6.0(@jsonforms/core@3.6.0)(react@18.3.1) + '@mui/icons-material': 7.3.11(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/x-date-pickers': 7.29.4(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + dayjs: 1.10.7 + lodash: 4.18.1 react: 18.3.1 '@jsonforms/react@3.6.0(@jsonforms/core@3.6.0)(react@18.3.1)': dependencies: '@jsonforms/core': 3.6.0 - lodash: 4.17.23 + lodash: 4.18.1 react: 18.3.1 '@mapbox/geojson-rewind@0.5.2': @@ -7479,9 +7756,11 @@ snapshots: '@mui/core-downloads-tracker@6.5.0': {} + '@mui/core-downloads-tracker@7.3.11': {} + '@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.7 '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 optionalDependencies: @@ -7495,6 +7774,14 @@ snapshots: optionalDependencies: '@types/react': 18.3.28 + '@mui/icons-material@7.3.11(@mui/material@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.7 + '@mui/material': 7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.28 + '@mui/lab@6.0.0-beta.22(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.28.6 @@ -7533,6 +7820,27 @@ snapshots: '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@types/react': 18.3.28 + '@mui/material@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.7 + '@mui/core-downloads-tracker': 7.3.11 + '@mui/system': 7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@mui/types': 7.4.12(@types/react@18.3.28) + '@mui/utils': 7.3.11(@types/react@18.3.28)(react@18.3.1) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.12(@types/react@18.3.28) + clsx: 2.1.1 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 19.2.4 + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@types/react': 18.3.28 + '@mui/private-theming@6.4.9(@types/react@18.3.28)(react@18.3.1)': dependencies: '@babel/runtime': 7.28.6 @@ -7542,6 +7850,15 @@ snapshots: optionalDependencies: '@types/react': 18.3.28 + '@mui/private-theming@7.3.11(@types/react@18.3.28)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.7 + '@mui/utils': 7.3.11(@types/react@18.3.28)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.28 + '@mui/styled-engine@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.28.6 @@ -7555,6 +7872,19 @@ snapshots: '@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1) '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@mui/styled-engine@7.3.10(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.7 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1)': dependencies: '@babel/runtime': 7.28.6 @@ -7571,6 +7901,22 @@ snapshots: '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@types/react': 18.3.28 + '@mui/system@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.7 + '@mui/private-theming': 7.3.11(@types/react@18.3.28)(react@18.3.1) + '@mui/styled-engine': 7.3.10(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.4.12(@types/react@18.3.28) + '@mui/utils': 7.3.11(@types/react@18.3.28)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@types/react': 18.3.28 + '@mui/types@7.2.24(@types/react@18.3.28)': optionalDependencies: '@types/react': 18.3.28 @@ -7581,6 +7927,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.28 + '@mui/types@7.4.12(@types/react@18.3.28)': + dependencies: + '@babel/runtime': 7.29.7 + optionalDependencies: + '@types/react': 18.3.28 + '@mui/utils@6.4.9(@types/react@18.3.28)(react@18.3.1)': dependencies: '@babel/runtime': 7.28.6 @@ -7593,6 +7945,18 @@ snapshots: optionalDependencies: '@types/react': 18.3.28 + '@mui/utils@7.3.11(@types/react@18.3.28)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.7 + '@mui/types': 7.4.12(@types/react@18.3.28) + '@types/prop-types': 15.7.15 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 19.2.4 + optionalDependencies: + '@types/react': 18.3.28 + '@mui/utils@7.3.8(@types/react@18.3.28)(react@18.3.1)': dependencies: '@babel/runtime': 7.28.6 @@ -7605,11 +7969,31 @@ snapshots: optionalDependencies: '@types/react': 18.3.28 - '@mui/x-date-pickers@7.29.4(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mui/x-date-pickers@7.29.4(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.28.6 '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@mui/system': 7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + '@mui/utils': 7.3.8(@types/react@18.3.28)(react@18.3.1) + '@mui/x-internals': 7.29.0(@types/react@18.3.28)(react@18.3.1) + '@types/react-transition-group': 4.4.12(@types/react@18.3.28) + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) + dayjs: 1.10.7 + transitivePeerDependencies: + - '@types/react' + + '@mui/x-date-pickers@7.29.4(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.6 + '@mui/material': 7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@mui/utils': 7.3.8(@types/react@18.3.28)(react@18.3.1) '@mui/x-internals': 7.29.0(@types/react@18.3.28)(react@18.3.1) '@types/react-transition-group': 4.4.12(@types/react@18.3.28) @@ -7647,6 +8031,8 @@ snapshots: '@open-draft/deferred-promise@2.2.0': {} + '@open-draft/deferred-promise@3.0.0': {} + '@open-draft/logger@0.3.0': dependencies: is-node-process: 1.2.0 @@ -7960,8 +8346,15 @@ snapshots: dependencies: remove-accents: 0.5.0 + '@tanstack/query-core@5.101.0': {} + '@tanstack/query-core@5.90.20': {} + '@tanstack/react-query@5.101.0(react@18.3.1)': + dependencies: + '@tanstack/query-core': 5.101.0 + react: 18.3.1 + '@tanstack/react-query@5.90.21(react@18.3.1)': dependencies: '@tanstack/query-core': 5.90.20 @@ -8182,6 +8575,10 @@ snapshots: '@types/relay-runtime@19.0.3': {} + '@types/set-cookie-parser@2.4.10': + dependencies: + '@types/node': 25.9.2 + '@types/stats.js@0.17.4': {} '@types/statuses@2.0.6': {} @@ -8448,7 +8845,15 @@ snapshots: transitivePeerDependencies: - '@swc/helpers' - '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0))': + '@vitejs/plugin-react-swc@3.11.0(vite@7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@rolldown/pluginutils': 1.0.0-beta.27 + '@swc/core': 1.15.18 + vite: 7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + transitivePeerDependencies: + - '@swc/helpers' + + '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0))': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.0.18 @@ -8460,7 +8865,7 @@ snapshots: obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) + vitest: 4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0) '@vitest/expect@4.0.18': dependencies: @@ -8469,29 +8874,38 @@ snapshots: '@vitest/spy': 4.0.18 '@vitest/utils': 4.0.18 chai: 6.2.2 - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.0.18(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(vite@7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@vitest/spy': 4.0.18 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + msw: 2.12.10(@types/node@25.9.2)(typescript@5.9.3) + vite: 7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - '@vitest/mocker@4.0.18(msw@2.12.10(@types/node@25.3.3)(typescript@5.9.3))(vite@7.3.1(@types/node@25.3.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@vitest/mocker@4.0.18(msw@2.14.6(@types/node@25.3.3)(typescript@5.9.3))(vite@7.3.5(@types/node@25.3.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.12.10(@types/node@25.3.3)(typescript@5.9.3) - vite: 7.3.1(@types/node@25.3.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + msw: 2.14.6(@types/node@25.3.3)(typescript@5.9.3) + vite: 7.3.5(@types/node@25.3.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - '@vitest/mocker@4.0.18(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(vite@7.3.1(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@vitest/mocker@4.0.18(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(vite@7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.12.10(@types/node@25.9.2)(typescript@5.9.3) - vite: 7.3.1(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + msw: 2.14.6(@types/node@25.9.2)(typescript@5.9.3) + vite: 7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) '@vitest/pretty-format@4.0.18': dependencies: - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 '@vitest/runner@4.0.18': dependencies: @@ -8509,7 +8923,7 @@ snapshots: '@vitest/utils@4.0.18': dependencies: '@vitest/pretty-format': 4.0.18 - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 '@webassemblyjs/ast@1.14.1': dependencies: @@ -8648,11 +9062,13 @@ snapshots: afterframe@1.0.2: {} - agent-base@7.1.4: {} + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color - ajv-formats@2.1.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 + agent-base@7.1.4: {} ajv-formats@2.1.1(ajv@8.20.0): optionalDependencies: @@ -8785,6 +9201,16 @@ snapshots: transitivePeerDependencies: - debug + axios@1.18.0: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + babel-plugin-macros@2.8.0: dependencies: '@babel/runtime': 7.28.6 @@ -9856,6 +10282,8 @@ snapshots: follow-redirects@1.15.11: {} + follow-redirects@1.16.0: {} + font-atlas@2.1.0: dependencies: css-font: 1.2.0 @@ -10155,6 +10583,11 @@ snapshots: headers-polyfill@4.0.3: {} + headers-polyfill@5.0.1: + dependencies: + '@types/set-cookie-parser': 2.4.10 + set-cookie-parser: 3.1.0 + highlight-words@2.0.0: {} hls.js@1.6.15: {} @@ -10178,6 +10611,13 @@ snapshots: transitivePeerDependencies: - supports-color + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -10472,8 +10912,6 @@ snapshots: lodash.sortby@4.7.0: {} - lodash@4.17.23: {} - lodash@4.18.1: {} log-symbols@7.0.1: @@ -10507,6 +10945,10 @@ snapshots: dependencies: react: 18.3.1 + lucide-react@1.18.0(react@18.3.1): + dependencies: + react: 18.3.1 + lz-string@1.5.0: {} maath@0.10.8(@types/three@0.164.1)(three@0.164.1): @@ -10588,13 +11030,13 @@ snapshots: tinyqueue: 3.0.0 vt-pbf: 3.1.3 - material-react-table@3.2.1(5b559ea0d387c19eea1508254820d836): + material-react-table@3.2.1(c73fd2a924619084f45aa81401e957b8): dependencies: '@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1) '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@mui/icons-material': 6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.28)(react@18.3.1) '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mui/x-date-pickers': 7.29.4(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/x-date-pickers': 7.29.4(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@7.3.11(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1))(@types/react@18.3.28)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tanstack/match-sorter-utils': 8.19.4 '@tanstack/react-table': 8.20.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tanstack/react-virtual': 3.11.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -10675,9 +11117,9 @@ snapshots: ms@2.1.3: {} - msw@2.12.10(@types/node@25.3.3)(typescript@5.9.3): + msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3): dependencies: - '@inquirer/confirm': 5.1.21(@types/node@25.3.3) + '@inquirer/confirm': 5.1.21(@types/node@25.9.2) '@mswjs/interceptors': 0.41.3 '@open-draft/deferred-promise': 2.2.0 '@types/statuses': 2.0.6 @@ -10699,26 +11141,51 @@ snapshots: typescript: 5.9.3 transitivePeerDependencies: - '@types/node' + + msw@2.14.6(@types/node@25.3.3)(typescript@5.9.3): + dependencies: + '@inquirer/confirm': 6.1.1(@types/node@25.3.3) + '@mswjs/interceptors': 0.41.3 + '@open-draft/deferred-promise': 3.0.0 + '@types/statuses': 2.0.6 + cookie: 1.1.1 + graphql: 16.14.2 + headers-polyfill: 5.0.1 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + rettime: 0.11.11 + statuses: 2.0.2 + strict-event-emitter: 0.5.1 + tough-cookie: 6.0.1 + type-fest: 5.7.0 + until-async: 3.0.2 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' optional: true - msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3): + msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3): dependencies: - '@inquirer/confirm': 5.1.21(@types/node@25.9.2) + '@inquirer/confirm': 6.1.1(@types/node@25.9.2) '@mswjs/interceptors': 0.41.3 - '@open-draft/deferred-promise': 2.2.0 + '@open-draft/deferred-promise': 3.0.0 '@types/statuses': 2.0.6 cookie: 1.1.1 graphql: 16.14.2 - headers-polyfill: 4.0.3 + headers-polyfill: 5.0.1 is-node-process: 1.2.0 outvariant: 1.4.3 path-to-regexp: 6.3.0 picocolors: 1.1.1 - rettime: 0.10.1 + rettime: 0.11.11 statuses: 2.0.2 strict-event-emitter: 0.5.1 - tough-cookie: 6.0.0 - type-fest: 5.4.4 + tough-cookie: 6.0.1 + type-fest: 5.7.0 until-async: 3.0.2 yargs: 17.7.2 optionalDependencies: @@ -11148,6 +11615,8 @@ snapshots: proxy-from-env@1.1.0: {} + proxy-from-env@2.1.0: {} + punycode@2.3.1: {} queue-microtask@1.2.3: {} @@ -11300,6 +11769,12 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-router: 7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router-dom@7.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 7.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router@7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: cookie: 1.1.1 @@ -11308,6 +11783,14 @@ snapshots: optionalDependencies: react-dom: 18.3.1(react@18.3.1) + react-router@7.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + cookie: 1.1.1 + react: 18.3.1 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + react-select@5.10.2(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.28.6 @@ -11523,6 +12006,8 @@ snapshots: rettime@0.10.1: {} + rettime@0.11.11: {} + reusify@1.1.0: {} rfdc@1.4.1: {} @@ -11613,6 +12098,8 @@ snapshots: set-cookie-parser@2.7.2: {} + set-cookie-parser@3.1.0: {} + setimmediate@1.0.5: {} shallow-copy@0.0.1: {} @@ -11916,6 +12403,8 @@ snapshots: tinyrainbow@3.0.3: {} + tinyrainbow@3.1.0: {} + title-case@3.0.3: dependencies: tslib: 2.8.1 @@ -11954,6 +12443,10 @@ snapshots: dependencies: tldts: 7.0.24 + tough-cookie@6.0.1: + dependencies: + tldts: 7.0.24 + tr46@0.0.3: {} tr46@5.1.1: @@ -12031,6 +12524,10 @@ snapshots: dependencies: tagged-tag: 1.0.0 + type-fest@5.7.0: + dependencies: + tagged-tag: 1.0.0 + type@2.7.3: {} typedarray-pool@1.2.0: @@ -12155,7 +12652,15 @@ snapshots: transitivePeerDependencies: - supports-color - vite@7.3.1(@types/node@25.3.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + vite-plugin-relay@2.1.0(babel-plugin-relay@20.1.1)(vite@7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + dependencies: + '@babel/core': 7.29.0 + babel-plugin-relay: 20.1.1 + vite: 7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + transitivePeerDependencies: + - supports-color + + vite@7.3.1(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) @@ -12163,6 +12668,21 @@ snapshots: postcss: 8.5.8 rollup: 4.59.0 tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 25.9.2 + fsevents: 2.3.3 + jiti: 2.7.0 + terser: 5.48.0 + yaml: 2.9.0 + + vite@7.3.5(@types/node@25.3.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.15 + rollup: 4.59.0 + tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.3.3 fsevents: 2.3.3 @@ -12170,12 +12690,12 @@ snapshots: terser: 5.48.0 yaml: 2.9.0 - vite@7.3.1(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + vite@7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - postcss: 8.5.8 + postcss: 8.5.15 rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: @@ -12185,10 +12705,10 @@ snapshots: terser: 5.48.0 yaml: 2.9.0 - vitest@4.0.18(@types/node@25.3.3)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.3.3)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0): + vitest@4.0.18(@types/node@25.3.3)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.3.3)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(msw@2.12.10(@types/node@25.3.3)(typescript@5.9.3))(vite@7.3.1(@types/node@25.3.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/mocker': 4.0.18(msw@2.14.6(@types/node@25.3.3)(typescript@5.9.3))(vite@7.3.5(@types/node@25.3.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -12204,8 +12724,8 @@ snapshots: tinybench: 2.9.0 tinyexec: 1.0.2 tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.3.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + tinyrainbow: 3.1.0 + vite: 7.3.5(@types/node@25.3.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.3.3 @@ -12226,7 +12746,7 @@ snapshots: vitest@4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(vite@7.3.1(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/mocker': 4.0.18(msw@2.12.10(@types/node@25.9.2)(typescript@5.9.3))(vite@7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -12242,8 +12762,46 @@ snapshots: tinybench: 2.9.0 tinyexec: 1.0.2 tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + tinyrainbow: 3.1.0 + vite: 7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 25.9.2 + jsdom: 26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + + vitest@4.0.18(@types/node@25.9.2)(jiti@2.7.0)(jsdom@26.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(terser@5.48.0)(yaml@2.9.0): + dependencies: + '@vitest/expect': 4.0.18 + '@vitest/mocker': 4.0.18(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))(vite@7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/pretty-format': 4.0.18 + '@vitest/runner': 4.0.18 + '@vitest/snapshot': 4.0.18 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.1.0 + vite: 7.3.5(@types/node@25.9.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.9.2