diff --git a/gui/.env b/gui/.env
new file mode 100644
index 0000000000..7ce15648fa
--- /dev/null
+++ b/gui/.env
@@ -0,0 +1,8 @@
+VITE_FIRMWARE_TOOL_URL=https://fw-tool-api.slimevr.io
+VITE_FIRMWARE_TOOL_S3_URL=https://fw-tool-bucket.slimevr.io
+FIRMWARE_TOOL_SCHEMA_URL=https://fw-tool-api.slimevr.io/api-json
+
+
+# VITE_FIRMWARE_TOOL_URL=http://localhost:3000
+# VITE_FIRMWARE_TOOL_S3_URL=http://localhost:9000
+# FIRMWARE_TOOL_SCHEMA_URL=http://localhost:3000/api-json
diff --git a/gui/.eslintrc.json b/gui/.eslintrc.json
deleted file mode 100644
index dc120fbd29..0000000000
--- a/gui/.eslintrc.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
- "env": {
- "browser": true,
- "es2021": true,
- "jest": true
- },
- "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:@dword-design/import-alias/recommended"],
- "parser": "@typescript-eslint/parser",
- "parserOptions": {
- "ecmaFeatures": {
- "jsx": true
- },
- "ecmaVersion": "latest",
- "sourceType": "module"
- },
- "plugins": ["react-hooks", "@typescript-eslint"],
- "rules": {
- "react/react-in-jsx-scope": "off",
- "react/prop-types": "off",
- "spaced-comment": "error",
- "quotes": ["error", "single"],
- "no-duplicate-imports": "error",
- "no-inline-styles": "off",
- "@typescript-eslint/no-explicit-any": "off",
- "react/no-unescaped-entities": "off",
- "camelcase": "error",
- "@typescript-eslint/no-unused-vars": [
- "warn",
- {
- "argsIgnorePattern": "^_",
- "varsIgnorePattern": "^_"
- }
- ],
- "@dword-design/import-alias/prefer-alias": [
- "error",
- {
- "alias": {
- "@": "./src/"
- }
- }
- ]
- },
- "settings": {
- "import/resolver": {
- "typescript": {}
- },
- "react": {
- "version": "detect"
- }
- }
-}
diff --git a/gui/.lintstagedrc.mjs b/gui/.lintstagedrc.mjs
index 3f4d417894..cf8c7a097c 100644
--- a/gui/.lintstagedrc.mjs
+++ b/gui/.lintstagedrc.mjs
@@ -1,5 +1,5 @@
export default {
'**/*.{ts,tsx}': () => 'tsc -p tsconfig.json --noEmit',
- '**/*.{js,jsx,ts,tsx}': 'eslint --max-warnings=0 --cache --fix',
+ 'src/**/*.{js,jsx,ts,tsx}': 'eslint --max-warnings=0 --no-warn-ignored --cache --fix',
'**/*.{js,jsx,ts,tsx,css,md,json}': 'prettier --write',
};
diff --git a/gui/eslint.config.js b/gui/eslint.config.js
new file mode 100644
index 0000000000..c4d534218e
--- /dev/null
+++ b/gui/eslint.config.js
@@ -0,0 +1,79 @@
+import { FlatCompat } from '@eslint/eslintrc';
+import eslint from '@eslint/js';
+import globals from 'globals';
+import tseslint from 'typescript-eslint';
+
+const compat = new FlatCompat();
+
+export const gui = [
+ eslint.configs.recommended,
+ ...tseslint.configs.recommended,
+ ...compat.extends('plugin:@dword-design/import-alias/recommended'),
+ ...compat.plugins('eslint-plugin-react-hooks'),
+ // Add import-alias rule inside compat because plugin doesn't like flat configs
+ ...compat.config({
+ rules: {
+ '@dword-design/import-alias/prefer-alias': [
+ 'error',
+ {
+ alias: {
+ '@': './src/',
+ },
+ },
+ ],
+ },
+ }),
+ {
+ languageOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ parser: tseslint.parser,
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ globals: {
+ ...globals.browser,
+ ...globals.jest,
+ },
+ },
+ files: ['src/**/*.{js,jsx,ts,tsx,json}'],
+ plugins: {
+ '@typescript-eslint': tseslint.plugin,
+ },
+ rules: {
+ 'react/react-in-jsx-scope': 'off',
+ 'react/prop-types': 'off',
+ 'spaced-comment': 'error',
+ quotes: ['error', 'single'],
+ 'no-duplicate-imports': 'error',
+ 'no-inline-styles': 'off',
+ '@typescript-eslint/no-explicit-any': 'off',
+ 'react/no-unescaped-entities': 'off',
+ camelcase: 'error',
+ '@typescript-eslint/no-unused-vars': [
+ 'warn',
+ {
+ argsIgnorePattern: '^_',
+ varsIgnorePattern: '^_',
+ ignoreRestSiblings: true,
+ },
+ ],
+ },
+ settings: {
+ 'import/resolver': {
+ typescript: {},
+ },
+ react: {
+ version: 'detect',
+ },
+ },
+ },
+ // Global ignore
+ {
+ ignores: ['**/firmware-tool-api/'],
+ },
+];
+
+export default gui;
diff --git a/gui/openapi-codegen.config.ts b/gui/openapi-codegen.config.ts
new file mode 100644
index 0000000000..3e007342bc
--- /dev/null
+++ b/gui/openapi-codegen.config.ts
@@ -0,0 +1,28 @@
+import {
+ generateSchemaTypes,
+ generateReactQueryComponents,
+} from '@openapi-codegen/typescript';
+import { defineConfig } from '@openapi-codegen/cli';
+import dotenv from 'dotenv';
+
+dotenv.config()
+
+export default defineConfig({
+ firmwareTool: {
+ from: {
+ source: 'url',
+ url: process.env.FIRMWARE_TOOL_SCHEMA_URL ?? 'http://localhost:3000/api-json',
+ },
+ outputDir: 'src/firmware-tool-api',
+ to: async (context) => {
+ const filenamePrefix = 'firmwareTool';
+ const { schemasFiles } = await generateSchemaTypes(context, {
+ filenamePrefix,
+ });
+ await generateReactQueryComponents(context, {
+ filenamePrefix,
+ schemasFiles,
+ });
+ },
+ },
+});
diff --git a/gui/package.json b/gui/package.json
index ad39075d7f..ae77c44501 100644
--- a/gui/package.json
+++ b/gui/package.json
@@ -2,13 +2,16 @@
"name": "slimevr-ui",
"version": "0.5.1",
"private": true,
+ "type": "module",
"dependencies": {
"@fluent/bundle": "^0.18.0",
"@fluent/react": "^0.15.2",
"@fontsource/poppins": "^5.1.0",
"@formatjs/intl-localematcher": "^0.2.32",
+ "@hookform/resolvers": "^3.6.0",
"@react-three/drei": "^9.114.3",
"@react-three/fiber": "^8.17.10",
+ "@tanstack/react-query": "^5.48.0",
"@tauri-apps/api": "^2.0.2",
"@tauri-apps/plugin-dialog": "^2.0.0",
"@tauri-apps/plugin-fs": "^2.0.0",
@@ -34,7 +37,8 @@
"three": "^0.163.0",
"ts-pattern": "^5.4.0",
"typescript": "^5.6.3",
- "use-double-tap": "^1.3.6"
+ "use-double-tap": "^1.3.6",
+ "yup": "^1.4.0"
},
"scripts": {
"start": "vite --force",
@@ -46,10 +50,14 @@
"lint:fix": "tsc --noEmit && eslint --fix --max-warnings=0 \"src/**/*.{js,jsx,ts,tsx,json}\" && pnpm run format",
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,css,scss,md,json}\"",
"preview-vite": "vite preview",
- "javaversion-build": "cd src-tauri/src/ && javac JavaVersion.java && jar cvfe JavaVersion.jar JavaVersion JavaVersion.class"
+ "javaversion-build": "cd src-tauri/src/ && javac JavaVersion.java && jar cvfe JavaVersion.jar JavaVersion JavaVersion.class",
+ "gen:javaversion": "cd src-tauri/src/ && javac JavaVersion.java && jar cvfe JavaVersion.jar JavaVersion JavaVersion.class",
+ "gen:firmware-tool": "openapi-codegen gen firmwareTool"
},
"devDependencies": {
"@dword-design/eslint-plugin-import-alias": "^4.0.9",
+ "@openapi-codegen/cli": "^2.0.2",
+ "@openapi-codegen/typescript": "^8.0.2",
"@tailwindcss/forms": "^0.5.9",
"@tauri-apps/cli": "^2.0.2",
"@types/file-saver": "^2.0.7",
@@ -64,6 +72,7 @@
"@vitejs/plugin-react": "^4.3.2",
"autoprefixer": "^10.4.20",
"cross-env": "^7.0.3",
+ "dotenv": "^16.4.5",
"eslint": "^8.57.1",
"eslint-config-airbnb": "^19.0.4",
"eslint-import-resolver-typescript": "^3.6.3",
@@ -77,6 +86,8 @@
"spdx-satisfies": "^5.0.1",
"tailwind-gradient-mask-image": "^1.2.0",
"tailwindcss": "^3.4.13",
- "vite": "^5.4.8"
+ "vite": "^5.4.8",
+ "globals": "^15.10.0",
+ "typescript-eslint": "^8.8.0"
}
}
diff --git a/gui/public/i18n/en/translation.ftl b/gui/public/i18n/en/translation.ftl
index a4d4739020..d6db7a015f 100644
--- a/gui/public/i18n/en/translation.ftl
+++ b/gui/public/i18n/en/translation.ftl
@@ -45,6 +45,19 @@ body_part-LEFT_UPPER_LEG = Left thigh
body_part-LEFT_LOWER_LEG = Left ankle
body_part-LEFT_FOOT = Left foot
+## BoardType
+board_type-UNKNOWN = Unknown
+board_type-NODEMCU = NodeMCU
+board_type-CUSTOM = Custom Board
+board_type-WROOM32 = WROOM32
+board_type-WEMOSD1MINI = Wemos D1 Mini
+board_type-TTGO_TBASE = TTGO T-Base
+board_type-ESP01 = ESP-01
+board_type-SLIMEVR = SlimeVR
+board_type-LOLIN_C3_MINI = Lolin C3 Mini
+board_type-BEETLE32C3 = Beetle ESP32-C3
+board_type-ES32C3DEVKITM1 = Espressif ESP32-C3 DevKitM-1
+
## Proportions
skeleton_bone-NONE = None
skeleton_bone-HEAD = Head Shift
@@ -294,6 +307,7 @@ settings-sidebar-utils = Utilities
settings-sidebar-serial = Serial console
settings-sidebar-appearance = Appearance
settings-sidebar-notifications = Notifications
+settings-sidebar-firmware-tool = DIY Firmware Tool
settings-sidebar-advanced = Advanced
## SteamVR settings
@@ -672,6 +686,7 @@ onboarding-wifi_creds-submit = Submit!
onboarding-wifi_creds-ssid =
.label = Wi-Fi name
.placeholder = Enter Wi-Fi name
+onboarding-wifi_creds-ssid-required = Wi-Fi name is required
onboarding-wifi_creds-password =
.label = Password
.placeholder = Enter password
@@ -721,6 +736,7 @@ onboarding-connect_tracker-issue-serial = I'm having trouble connecting!
onboarding-connect_tracker-usb = USB Tracker
onboarding-connect_tracker-connection_status-none = Looking for trackers
onboarding-connect_tracker-connection_status-serial_init = Connecting to serial device
+onboarding-connect_tracker-connection_status-optaining_mac_address = Optaining the tracker mac address
onboarding-connect_tracker-connection_status-provisioning = Sending Wi-Fi credentials
onboarding-connect_tracker-connection_status-connecting = Trying to connect to Wi-Fi
onboarding-connect_tracker-connection_status-looking_for_server = Looking for server
@@ -1013,6 +1029,157 @@ status_system-StatusSteamVRDisconnected = { $type ->
status_system-StatusTrackerError = The { $trackerName } tracker has an error.
status_system-StatusUnassignedHMD = The VR headset should be assigned as a head tracker.
+
+## Firmware tool globals
+firmware-tool-next-step = Next Step
+firmware-tool-previous-step = Previous Step
+firmware-tool-ok = Looks good
+firmware-tool-retry = Retry
+
+firmware-tool-loading = Loading...
+
+## Firmware tool Steps
+firmware-tool = DIY Firmware tool
+firmware-tool-description =
+ Allows to configure and flash your DIY trackers
+firmware-tool-not-available = Oops the firmware tool is not available at the moment. Come back later!
+firmware-tool-not-compatible = The firmware tool is not compatible with this version of the server. Please update your server!
+
+firmware-tool-board-step = Select your Board
+firmware-tool-board-step-description = Select one of the boards listed below.
+
+firmware-tool-board-pins-step = Check the pins
+firmware-tool-board-pins-step-description =
+ Please verify that the selected pins are correct.
+ If you followed the SlimeVR documentation the defaults values should be correct
+firmware-tool-board-pins-step-enable-led = Enable LED
+firmware-tool-board-pins-step-led-pin =
+ .label = LED Pin
+ .placeholder = Enter the pin address of the LED
+
+firmware-tool-board-pins-step-battery-type = Select a battery type
+firmware-tool-board-pins-step-battery-type-BAT_EXTERNAL = External battery
+firmware-tool-board-pins-step-battery-type-BAT_INTERNAL = Internal battery
+firmware-tool-board-pins-step-battery-type-BAT_INTERNAL_MCP3021 = Internal MCP3021
+firmware-tool-board-pins-step-battery-type-BAT_MCP3021 = MCP3021
+
+
+firmware-tool-board-pins-step-battery-sensor-pin =
+ .label = Battery sensor Pin
+ .placeholder = Enter the pin address of battery sensor
+firmware-tool-board-pins-step-battery-resistor =
+ .label = Battery Resistor (Ohms)
+ .placeholder = Enter the value of battery resistor
+firmware-tool-board-pins-step-battery-shield-resistor-0 =
+ .label = Battery Shield R1 (Ohms)
+ .placeholder = Enter the value of Battery Shield R1
+firmware-tool-board-pins-step-battery-shield-resistor-1 =
+ .label = Battery Shield R2 (Ohms)
+ .placeholder = Enter the value of Battery Shield R2
+
+firmware-tool-add-imus-step = Declare your IMUs
+firmware-tool-add-imus-step-description =
+ Please add the IMUs that your tracker has
+ If you followed the SlimeVR documentation the defaults values should be correct
+firmware-tool-add-imus-step-imu-type-label = IMU type
+firmware-tool-add-imus-step-imu-type-placeholder = Select the type of IMU
+firmware-tool-add-imus-step-imu-rotation =
+ .label = IMU Rotation (deg)
+ .placeholder = Rotation angle of the IMU
+firmware-tool-add-imus-step-scl-pin =
+ .label = SCL Pin
+ .placeholder = Pin address of SCL
+firmware-tool-add-imus-step-sda-pin =
+ .label = SDA Pin
+ .placeholder = Pin address of SDA
+firmware-tool-add-imus-step-int-pin =
+ .label = INT Pin
+ .placeholder = Pin address of INT
+firmware-tool-add-imus-optional-tracker =
+ .label = Optional tracker
+firmware-tool-add-imus-show-less = Show Less
+firmware-tool-add-imus-show-more = Show More
+firmware-tool-add-imus-add-more = Add more IMUs
+
+firmware-tool-select-firmware-step = Select the firmware version
+firmware-tool-select-firmware-step-description =
+ Please choose what version of the firmware you want to use
+firmware-tool-select-firmware-step-show-third-party =
+ .label = Show third party firmwares
+
+firmware-tool-flash-method-step = Flashing Method
+firmware-tool-flash-method-step-description =
+ Please select the flashing method you want to use
+firmware-tool-flash-method-step-ota =
+ .label = OTA
+ .description = Use the over the air method. Your tracker will use the Wi-Fi to update it's firmware. Works only on already setup trackers.
+firmware-tool-flash-method-step-serial =
+ .label = Serial
+ .description = Use a USB cable to update your tracker.
+
+firmware-tool-flashbtn-step = Press the boot btn
+firmware-tool-flashbtn-step-description = Before going into the next step there is a few things you need to do
+
+firmware-tool-flashbtn-step-board-SLIMEVR = Press the flash button on the pcb before inserting turning on the tracker.
+ If the tracker was already on, simply turn it off and back on while pressing the button or shorting the flash pads.
+ Here are a few pictures on how to do it according to the different revisions of the slimevr tracker
+firmware-tool-flashbtn-step-board-SLIMEVR-r11 = Turn on the tracker while shorting the second rectangular FLASH pad from the edge on the top side of the board, and the metal shield of the microcontroller
+firmware-tool-flashbtn-step-board-SLIMEVR-r12 = Turn on the tracker while shorting the circular FLASH pad on the top side of the board, and the metal shield of the microcontroller
+firmware-tool-flashbtn-step-board-SLIMEVR-r14 = Turn on the tracker while pushing in the FLASH button on the top side of the board
+
+firmware-tool-flashbtn-step-board-OTHER = Before flashing you will probably need to put the tracker into bootloader mode.
+ Most of the time it means pressing the boot button on the board before the flashing process starts.
+ If the flashing process timeout at the begining of the flashing it probably means that the tracker was not in bootloader mode
+ Please refer to the flashing instructions of your board to know how to turn on the boatloader mode
+
+
+
+firmware-tool-flash-method-ota-devices = Detected OTA Devices:
+firmware-tool-flash-method-ota-no-devices = There are no boards that can be updated using OTA, make sure you selected the correct board type
+firmware-tool-flash-method-serial-wifi = Wi-Fi Credentials:
+firmware-tool-flash-method-serial-devices-label = Detected Serial Devices:
+firmware-tool-flash-method-serial-devices-placeholder = Select a serial device
+firmware-tool-flash-method-serial-no-devices = There are no compatible serial devices detected, make sure the tracker is plugged in
+
+firmware-tool-build-step = Building
+firmware-tool-build-step-description =
+ The firmware is building, please wait
+
+firmware-tool-flashing-step = Flashing
+firmware-tool-flashing-step-description =
+ Your trackers are flashing, please follow the instructions on the screen
+firmware-tool-flashing-step-warning = Do not unplug or restart the tracker during the upload process unless told to, it may make your board unusable
+firmware-tool-flashing-step-flash-more = Flash more trackers
+firmware-tool-flashing-step-exit = Exit
+
+## firmware tool build status
+firmware-tool-build-CREATING_BUILD_FOLDER = Creating the build folder
+firmware-tool-build-DOWNLOADING_FIRMWARE = Downloading the firmware
+firmware-tool-build-EXTRACTING_FIRMWARE = Extracting the firmware
+firmware-tool-build-SETTING_UP_DEFINES = Configuring the defines
+firmware-tool-build-BUILDING = Building the firmware
+firmware-tool-build-SAVING = Saving the build
+firmware-tool-build-DONE = Build Complete
+firmware-tool-build-ERROR = Unable to build the firmware
+
+## Firmware update status
+firmware-update-status-DOWNLOADING = Downloading the firmware
+firmware-update-status-NEED_MANUAL_REBOOT = Waiting for the user to reboot the tracker
+firmware-update-status-AUTHENTICATING = Authenticating with the mcu
+firmware-update-status-UPLOADING = Uploading the firmware
+firmware-update-status-SYNCING_WITH_MCU = Syncing with the mcu
+firmware-update-status-REBOOTING = Rebooting the tracker
+firmware-update-status-PROVISIONING = Setting Wi-Fi credentials
+firmware-update-status-DONE = Update complete!
+firmware-update-status-ERROR_DEVICE_NOT_FOUND = Could not find the device
+firmware-update-status-ERROR_TIMEOUT = The update process timed out
+firmware-update-status-ERROR_DOWNLOAD_FAILED = Could not download the firmware
+firmware-update-status-ERROR_AUTHENTICATION_FAILED = Could not authenticate with the mcu
+firmware-update-status-ERROR_UPLOAD_FAILED = Could not upload the firmware
+firmware-update-status-ERROR_PROVISIONING_FAILED = Could not set the Wi-Fi credentials
+firmware-update-status-ERROR_UNSUPPORTED_METHOD = The update method is not supported
+firmware-update-status-ERROR_UNKNOWN = Unknown error
+
## Tray Menu
tray_menu-show = Show
tray_menu-hide = Hide
diff --git a/gui/public/images/R11_board_reset.webp b/gui/public/images/R11_board_reset.webp
new file mode 100644
index 0000000000..c4870d76bd
Binary files /dev/null and b/gui/public/images/R11_board_reset.webp differ
diff --git a/gui/public/images/R12_board_reset.webp b/gui/public/images/R12_board_reset.webp
new file mode 100644
index 0000000000..12aabb61b5
Binary files /dev/null and b/gui/public/images/R12_board_reset.webp differ
diff --git a/gui/public/images/R14_board_reset_sw.webp b/gui/public/images/R14_board_reset_sw.webp
new file mode 100644
index 0000000000..5c1d89a06d
Binary files /dev/null and b/gui/public/images/R14_board_reset_sw.webp differ
diff --git a/gui/src/App.tsx b/gui/src/App.tsx
index 9865a08b91..7923d8a1be 100644
--- a/gui/src/App.tsx
+++ b/gui/src/App.tsx
@@ -51,10 +51,12 @@ import { useBreakpoint, useIsTauri } from './hooks/breakpoint';
import { VRModePage } from './components/vr-mode/VRModePage';
import { InterfaceSettings } from './components/settings/pages/InterfaceSettings';
import { error, log } from './utils/logging';
+import { FirmwareToolSettings } from './components/firmware-tool/FirmwareTool';
import { AppLayout } from './AppLayout';
import { Preload } from './components/Preload';
import { UnknownDeviceModal } from './components/UnknownDeviceModal';
import { useDiscordPresence } from './hooks/discord-presence';
+import { EmptyLayout } from './components/EmptyLayout';
import { AdvancedSettings } from './components/settings/pages/AdvancedSettings';
export const GH_REPO = 'SlimeVR/SlimeVR-Server';
@@ -105,6 +107,7 @@ function Layout() {
}
>
+ } />
} />
} />
} />
@@ -272,19 +275,16 @@ export default function App() {
-
- {!websocketAPI.isConnected && (
- <>
-
-
- {websocketAPI.isFirstConnection
- ? l10n.getString('websocket-connecting')
- : l10n.getString('websocket-connection_lost')}
-
- >
- )}
- {websocketAPI.isConnected &&
}
-
+ {!websocketAPI.isConnected && (
+
+
+ {websocketAPI.isFirstConnection
+ ? l10n.getString('websocket-connecting')
+ : l10n.getString('websocket-connection_lost')}
+
+
+ )}
+ {websocketAPI.isConnected &&
}
diff --git a/gui/src/components/EmptyLayout.scss b/gui/src/components/EmptyLayout.scss
new file mode 100644
index 0000000000..d14e5d1b05
--- /dev/null
+++ b/gui/src/components/EmptyLayout.scss
@@ -0,0 +1,7 @@
+.empty-layout {
+ display: grid;
+ grid-template:
+ 't' var(--topbar-h)
+ 'c' calc(100% - var(--topbar-h))
+ / 100%;
+}
diff --git a/gui/src/components/EmptyLayout.tsx b/gui/src/components/EmptyLayout.tsx
new file mode 100644
index 0000000000..443ea60a8c
--- /dev/null
+++ b/gui/src/components/EmptyLayout.tsx
@@ -0,0 +1,16 @@
+import { ReactNode } from 'react';
+import { TopBar } from './TopBar';
+import './EmptyLayout.scss';
+
+export function EmptyLayout({ children }: { children: ReactNode }) {
+ return (
+
+ );
+}
diff --git a/gui/src/components/SerialDetectionModal.tsx b/gui/src/components/SerialDetectionModal.tsx
index 70b6597a06..0164eb6099 100644
--- a/gui/src/components/SerialDetectionModal.tsx
+++ b/gui/src/components/SerialDetectionModal.tsx
@@ -39,12 +39,6 @@ export function SerialDetectionModal() {
const openWifi = () => {
setShowWifiForm(true);
- // if (!hasWifiCreds) {
- // setShowWifiForm(true);
- // } else {
- // closeModal();
- // nav('/onboarding/connect-trackers', { state: { alonePage: true } });
- // }
};
const modalWifiSubmit = (form: WifiFormData) => {
@@ -58,7 +52,11 @@ export function SerialDetectionModal() {
({ device }: NewSerialDeviceResponseT) => {
if (
config?.watchNewDevices &&
- !['/settings/serial', '/onboarding/connect-trackers'].includes(pathname)
+ ![
+ '/settings/serial',
+ '/onboarding/connect-trackers',
+ '/settings/firmware-tool',
+ ].includes(pathname)
) {
setOpen(device);
}
diff --git a/gui/src/components/UnknownDeviceModal.tsx b/gui/src/components/UnknownDeviceModal.tsx
index 0a291f3d91..847214fd83 100644
--- a/gui/src/components/UnknownDeviceModal.tsx
+++ b/gui/src/components/UnknownDeviceModal.tsx
@@ -25,7 +25,9 @@ export function UnknownDeviceModal() {
RpcMessage.UnknownDeviceHandshakeNotification,
({ macAddress }: UnknownDeviceHandshakeNotificationT) => {
if (
- ['/onboarding/connect-trackers'].includes(pathname) ||
+ ['/onboarding/connect-trackers', '/settings/firmware-tool'].includes(
+ pathname
+ ) ||
state.ignoredTrackers.has(macAddress as string) ||
(currentTracker !== null && currentTracker !== macAddress)
)
diff --git a/gui/src/components/commons/Checkbox.tsx b/gui/src/components/commons/Checkbox.tsx
index 7287403612..ac2d824118 100644
--- a/gui/src/components/commons/Checkbox.tsx
+++ b/gui/src/components/commons/Checkbox.tsx
@@ -2,6 +2,10 @@ import classNames from 'classnames';
import { useMemo } from 'react';
import { Control, Controller } from 'react-hook-form';
+export const CHECKBOX_CLASSES = classNames(
+ 'bg-background-50 border-background-50 rounded-md w-5 h-5 text-accent-background-30 focus:border-accent-background-40 focus:ring-transparent focus:ring-offset-transparent focus:outline-transparent'
+);
+
export function CheckBox({
label,
variant = 'checkbox',
@@ -25,9 +29,7 @@ export function CheckBox({
const classes = useMemo(() => {
const vriantsMap = {
checkbox: {
- checkbox: classNames(
- 'bg-background-50 border-background-50 rounded-md w-5 h-5 text-accent-background-30 focus:border-accent-background-40 focus:ring-transparent focus:ring-offset-transparent focus:outline-transparent'
- ),
+ checkbox: CHECKBOX_CLASSES,
toggle: '',
pin: '',
},
diff --git a/gui/src/components/commons/Input.tsx b/gui/src/components/commons/Input.tsx
index 4c518497cc..0f229ba41e 100644
--- a/gui/src/components/commons/Input.tsx
+++ b/gui/src/components/commons/Input.tsx
@@ -98,7 +98,7 @@ export const InputInside = forwardRef<
>
{type === 'password' && (
diff --git a/gui/src/components/commons/ProgressBar.tsx b/gui/src/components/commons/ProgressBar.tsx
index afc776b75d..61afbae020 100644
--- a/gui/src/components/commons/ProgressBar.tsx
+++ b/gui/src/components/commons/ProgressBar.tsx
@@ -7,12 +7,14 @@ export function ProgressBar({
height = 10,
colorClass = 'bg-accent-background-20',
animated = false,
+ bottom = false,
}: {
progress: number;
parts?: number;
height?: number;
colorClass?: string;
animated?: boolean;
+ bottom?: boolean;
}) {
return (
@@ -25,6 +27,7 @@ export function ProgressBar({
colorClass={colorClass}
animated={animated}
parts={parts}
+ bottom={bottom}
>
))}
@@ -38,6 +41,7 @@ export function Bar({
height,
animated,
colorClass,
+ bottom,
}: {
index: number;
progress: number;
@@ -45,6 +49,7 @@ export function Bar({
height: number;
colorClass: string;
animated: boolean;
+ bottom: boolean;
}) {
const value = useMemo(
() => Math.min(Math.max((progress * parts) / 1 - index, 0), 1),
@@ -52,12 +57,16 @@ export function Bar({
);
return (
(null);
+ const refTop = useRef
(null);
+ const [shouldAnimate, setShouldAnimate] = useState(false);
+ const { height } = useElemSize(ref);
+
+ const isSelected = active === index;
+ const isPrevious = active > index;
+
+ useEffect(() => {
+ if (!refTop.current) return;
+ if (isSelected)
+ setTimeout(() => {
+ if (!refTop.current) return;
+ refTop.current.scrollIntoView({ behavior: 'smooth' });
+ }, 500);
+ }, [isSelected]);
+
+ useLayoutEffect(() => {
+ setShouldAnimate(true);
+ }, [active]);
+
+ // Make it so it wont try to animate the size
+ // if we are not changing active step
+ useDebouncedEffect(
+ () => {
+ setShouldAnimate(false);
+ },
+ [active],
+ 1000
+ );
+
+ return (
+
+
+ {isPrevious ? (
+
+ ) : (
+ {index + 1}
+ )}
+
+
+
+ );
+}
+
+type VerticalStepComponentType = FC<{
+ nextStep: () => void;
+ prevStep: () => void;
+ goTo: (id: string) => void;
+ isActive: boolean;
+}>;
+
+export type VerticalStep = {
+ title: string;
+ id?: string;
+ component: VerticalStepComponentType;
+};
+
+export default function VerticalStepper({ steps }: { steps: VerticalStep[] }) {
+ const [currStep, setStep] = useState(0);
+
+ const nextStep = () => {
+ if (currStep + 1 === steps.length) return;
+ setStep(currStep + 1);
+ };
+
+ const prevStep = () => {
+ if (currStep - 1 < 0) return;
+ setStep(currStep - 1);
+ };
+
+ const goTo = (id: string) => {
+ const step = steps.findIndex(({ id: stepId }) => stepId === id);
+ if (step === -1) throw new Error('step not found');
+
+ setStep(step);
+ };
+
+ return (
+
+ {steps.map(({ title, component: StepComponent }, index) => (
+
+
+
+ ))}
+
+ );
+}
diff --git a/gui/src/components/firmware-tool/AddImusStep.tsx b/gui/src/components/firmware-tool/AddImusStep.tsx
new file mode 100644
index 0000000000..42ce1e3f02
--- /dev/null
+++ b/gui/src/components/firmware-tool/AddImusStep.tsx
@@ -0,0 +1,308 @@
+import { Localized, useLocalization } from '@fluent/react';
+import { Typography } from '@/components/commons/Typography';
+import { LoaderIcon, SlimeState } from '@/components/commons/icon/LoaderIcon';
+import { useFirmwareTool } from '@/hooks/firmware-tool';
+import { Button } from '@/components/commons/Button';
+import { Control, useForm } from 'react-hook-form';
+import {
+ CreateImuConfigDTO,
+ Imudto,
+} from '@/firmware-tool-api/firmwareToolSchemas';
+import { Dropdown } from '@/components/commons/Dropdown';
+import { TrashIcon } from '@/components/commons/icon/TrashIcon';
+import { Input } from '@/components/commons/Input';
+import {
+ ArrowDownIcon,
+ ArrowUpIcon,
+} from '@/components/commons/icon/ArrowIcons';
+import { useEffect, useRef, useState } from 'react';
+import classNames from 'classnames';
+import { useElemSize } from '@/hooks/layout';
+import { useGetFirmwaresImus } from '@/firmware-tool-api/firmwareToolComponents';
+import { CheckBox } from '@/components/commons/Checkbox';
+
+function IMUCard({
+ control,
+ imuTypes,
+ hasIntPin,
+ index,
+ onDelete,
+}: {
+ imuTypes: Imudto[];
+ hasIntPin: boolean;
+ control: Control<{ imus: CreateImuConfigDTO[] }, any>;
+ index: number;
+ onDelete: () => void;
+}) {
+ const { l10n } = useLocalization();
+ const [open, setOpen] = useState(false);
+ const ref = useRef(null);
+ const { height } = useElemSize(ref);
+
+ return (
+
+
+
+
+ {index + 1}
+
+
+
+
+
+
+ ({
+ label: type.split('_').slice(1).join(' '),
+ value: type,
+ }))}
+ variant="secondary"
+ maxHeight="25vh"
+ placeholder={l10n.getString(
+ 'firmware-tool-add-imus-step-imu-type-placeholder'
+ )}
+ direction="down"
+ display="block"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setOpen(!open)}
+ >
+
+ {l10n.getString(
+ open
+ ? 'firmware-tool-add-imus-show-less'
+ : 'firmware-tool-add-imus-show-more'
+ )}
+
+ {!open &&
}
+ {open &&
}
+
+
+ );
+}
+
+export function AddImusStep({
+ nextStep,
+ prevStep,
+ isActive,
+}: {
+ nextStep: () => void;
+ prevStep: () => void;
+ goTo: (id: string) => void;
+ isActive: boolean;
+}) {
+ const { l10n } = useLocalization();
+ const {
+ isStepLoading: isLoading,
+ newConfig,
+ defaultConfig,
+ updateImus,
+ } = useFirmwareTool();
+
+ const {
+ control,
+ formState: { isValid: isValidState },
+ reset,
+ watch,
+ } = useForm<{ imus: CreateImuConfigDTO[] }>({
+ defaultValues: {
+ imus: [],
+ },
+ reValidateMode: 'onChange',
+ mode: 'onChange',
+ });
+
+ useEffect(() => {
+ reset({
+ imus: newConfig?.imusConfig || [],
+ });
+ }, [isActive]);
+
+ const { isFetching, data: imuTypes } = useGetFirmwaresImus({});
+
+ const isAckchuallyLoading = isFetching || isLoading;
+ const form = watch();
+
+ const addImu = () => {
+ if (!newConfig || !defaultConfig) throw new Error('unreachable');
+
+ const imuPinToAdd =
+ defaultConfig.imuDefaults[form.imus.length ?? 0] ??
+ defaultConfig.imuDefaults[0];
+ const imuTypeToAdd: CreateImuConfigDTO['type'] =
+ form.imus[0]?.type ?? 'IMU_BNO085';
+ reset({
+ imus: [...form.imus, { ...imuPinToAdd, type: imuTypeToAdd }],
+ });
+ };
+ const deleteImu = (index: number) => {
+ reset({ imus: form.imus.filter((_, i) => i !== index) });
+ };
+
+ return (
+ <>
+
+
+
+ {l10n.getString('firmware-tool-board-pins-step-description')}
+
+
+
+ {!isAckchuallyLoading && imuTypes && newConfig && (
+ <>
+
+
1
+ ? 'md:grid-cols-2 mobile-settings:grid-cols-1'
+ : 'grid-cols-1'
+ )}
+ >
+ {form.imus.map((imu, index) => (
+ t == imu.type)
+ ?.hasIntPin ?? false
+ }
+ index={index}
+ onDelete={() => deleteImu(index)}
+ >
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ updateImus(form.imus);
+ nextStep();
+ }}
+ >
+
+
+ >
+ )}
+ {isAckchuallyLoading && (
+
+
+
+
+
+
+ )}
+
+
+ >
+ );
+}
diff --git a/gui/src/components/firmware-tool/BoardPinsStep.tsx b/gui/src/components/firmware-tool/BoardPinsStep.tsx
new file mode 100644
index 0000000000..844731a040
--- /dev/null
+++ b/gui/src/components/firmware-tool/BoardPinsStep.tsx
@@ -0,0 +1,198 @@
+import { Localized, useLocalization } from '@fluent/react';
+import { Typography } from '@/components/commons/Typography';
+import { LoaderIcon, SlimeState } from '@/components/commons/icon/LoaderIcon';
+import { useFirmwareTool } from '@/hooks/firmware-tool';
+import { Button } from '@/components/commons/Button';
+import { useForm } from 'react-hook-form';
+import { Input } from '@/components/commons/Input';
+import { useEffect } from 'react';
+import { CheckBox } from '@/components/commons/Checkbox';
+import { CreateBoardConfigDTO } from '@/firmware-tool-api/firmwareToolSchemas';
+import { Dropdown } from '@/components/commons/Dropdown';
+import classNames from 'classnames';
+import { useGetFirmwaresBatteries } from '@/firmware-tool-api/firmwareToolComponents';
+
+export type BoardPinsForm = Omit;
+
+export function BoardPinsStep({
+ nextStep,
+ prevStep,
+}: {
+ nextStep: () => void;
+ prevStep: () => void;
+}) {
+ const { l10n } = useLocalization();
+ const {
+ isStepLoading: isLoading,
+ defaultConfig,
+ updatePins,
+ } = useFirmwareTool();
+ const { isFetching, data: batteryTypes } = useGetFirmwaresBatteries({});
+
+ const { reset, control, watch, formState } = useForm({
+ reValidateMode: 'onChange',
+ defaultValues: {
+ batteryResistances: [0, 0, 0],
+ },
+ mode: 'onChange',
+ });
+
+ const formValue = watch();
+ const ledEnabled = watch('enableLed');
+ const batteryType = watch('batteryType');
+
+ useEffect(() => {
+ if (!defaultConfig) return;
+ const { type, ...resetConfig } = defaultConfig.boardConfig;
+ reset({
+ ...resetConfig,
+ });
+ }, [defaultConfig]);
+
+ return (
+ <>
+
+
+
+ {l10n.getString('firmware-tool-board-pins-step-description')}
+
+
+
+ {!isLoading && !isFetching && batteryTypes && (
+
+ )}
+ {(isLoading || isFetching) && (
+
+
+
+
+
+
+ )}
+
+
+
+
+
+
+ {
+ updatePins(formValue);
+ nextStep();
+ }}
+ >
+
+
+
+ >
+ );
+}
diff --git a/gui/src/components/firmware-tool/BuildStep.tsx b/gui/src/components/firmware-tool/BuildStep.tsx
new file mode 100644
index 0000000000..d299a575b7
--- /dev/null
+++ b/gui/src/components/firmware-tool/BuildStep.tsx
@@ -0,0 +1,111 @@
+import { Localized, useLocalization } from '@fluent/react';
+import { Typography } from '@/components/commons/Typography';
+import { fetchPostFirmwaresBuild } from '@/firmware-tool-api/firmwareToolComponents';
+import { LoaderIcon, SlimeState } from '@/components/commons/icon/LoaderIcon';
+import { useFirmwareTool } from '@/hooks/firmware-tool';
+import {
+ BuildResponseDTO,
+ CreateBuildFirmwareDTO,
+} from '@/firmware-tool-api/firmwareToolSchemas';
+import { useEffect, useMemo } from 'react';
+import { firmwareToolBaseUrl } from '@/firmware-tool-api/firmwareToolFetcher';
+import { Button } from '@/components/commons/Button';
+
+export function BuildStep({
+ isActive,
+ goTo,
+ nextStep,
+}: {
+ nextStep: () => void;
+ prevStep: () => void;
+ goTo: (id: string) => void;
+ isActive: boolean;
+}) {
+ const { l10n } = useLocalization();
+ const { isGlobalLoading, newConfig, setBuildStatus, buildStatus } =
+ useFirmwareTool();
+
+ const startBuild = async () => {
+ try {
+ const res = await fetchPostFirmwaresBuild({
+ body: newConfig as CreateBuildFirmwareDTO,
+ });
+
+ setBuildStatus(res);
+ if (res.status !== 'DONE') {
+ const events = new EventSource(
+ `${firmwareToolBaseUrl}/firmwares/build-status/${res.id}`
+ );
+ events.onmessage = ({ data }) => {
+ const buildEvent: BuildResponseDTO = JSON.parse(data);
+ setBuildStatus(buildEvent);
+ };
+ }
+ } catch (e) {
+ console.error(e);
+ setBuildStatus({ id: '', status: 'ERROR' });
+ }
+ };
+
+ useEffect(() => {
+ if (!isActive) return;
+ startBuild();
+ }, [isActive]);
+
+ useEffect(() => {
+ if (!isActive) return;
+ if (buildStatus.status === 'DONE') {
+ nextStep();
+ }
+ }, [buildStatus]);
+
+ const hasPendingBuild = useMemo(
+ () => !['DONE', 'ERROR'].includes(buildStatus.status),
+ [buildStatus.status]
+ );
+
+ return (
+ <>
+
+
+
+ {l10n.getString('firmware-tool-build-step-description')}
+
+
+
+ {!isGlobalLoading && (
+
+
+
+ {l10n.getString('firmware-tool-build-' + buildStatus.status)}
+
+
+ )}
+ {isGlobalLoading && (
+
+
+
+
+
+
+ )}
+
+
+
+ goTo('FlashingMethod')}
+ >
+
+
+
+ >
+ );
+}
diff --git a/gui/src/components/firmware-tool/DeviceCard.tsx b/gui/src/components/firmware-tool/DeviceCard.tsx
new file mode 100644
index 0000000000..07505077f5
--- /dev/null
+++ b/gui/src/components/firmware-tool/DeviceCard.tsx
@@ -0,0 +1,107 @@
+import { Control, Controller } from 'react-hook-form';
+import { Typography } from '@/components/commons/Typography';
+import { ProgressBar } from '@/components/commons/ProgressBar';
+import { CHECKBOX_CLASSES } from '@/components/commons/Checkbox';
+import classNames from 'classnames';
+import { FirmwareUpdateStatus } from 'solarxr-protocol';
+import { useLocalization } from '@fluent/react';
+import { firmwareUpdateErrorStatus } from '@/hooks/firmware-tool';
+
+interface DeviceCardProps {
+ deviceNames: string[];
+ status?: FirmwareUpdateStatus;
+}
+
+interface DeviceCardControlProps {
+ control?: Control;
+ name?: string;
+ progress?: number;
+}
+
+export function DeviceCardContent({ deviceNames, status }: DeviceCardProps) {
+ const { l10n } = useLocalization();
+
+ return (
+
+
+ {deviceNames.map((name) => (
+
+ {name}
+
+ ))}
+
+ {status && (
+
+ {l10n.getString(
+ 'firmware-update-status-' + FirmwareUpdateStatus[status]
+ )}
+
+ )}
+
+ );
+}
+
+export function DeviceCardControl({
+ control,
+ name,
+ progress = undefined,
+ ...props
+}: DeviceCardControlProps & DeviceCardProps) {
+ return (
+
+ {control && name ? (
+
(
+
+
+
+
+
+
+
+
+
+ )}
+ >
+ ) : (
+
+
+
+ )}
+
+
+ );
+}
diff --git a/gui/src/components/firmware-tool/FirmwareTool.tsx b/gui/src/components/firmware-tool/FirmwareTool.tsx
new file mode 100644
index 0000000000..17b1b28892
--- /dev/null
+++ b/gui/src/components/firmware-tool/FirmwareTool.tsx
@@ -0,0 +1,140 @@
+import { Localized, useLocalization } from '@fluent/react';
+import { Typography } from '@/components/commons/Typography';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import {
+ FirmwareToolContextC,
+ useFirmwareToolContext,
+} from '@/hooks/firmware-tool';
+import { AddImusStep } from './AddImusStep';
+import { SelectBoardStep } from './SelectBoardStep';
+import { BoardPinsStep } from './BoardPinsStep';
+import VerticalStepper from '@/components/commons/VerticalStepper';
+import { LoaderIcon, SlimeState } from '@/components/commons/icon/LoaderIcon';
+import { Button } from '@/components/commons/Button';
+import { SelectFirmwareStep } from './SelectFirmwareStep';
+import { BuildStep } from './BuildStep';
+import { FlashingMethodStep } from './FlashingMethodStep';
+import { FlashingStep } from './FlashingStep';
+import { FlashBtnStep } from './FlashBtnStep';
+import { FirmwareUpdateMethod } from 'solarxr-protocol';
+import { useMemo } from 'react';
+
+function FirmwareToolContent() {
+ const { l10n } = useLocalization();
+ const context = useFirmwareToolContext();
+ const { isError, isGlobalLoading: isLoading, retry, isCompatible } = context;
+
+ const steps = useMemo(() => {
+ const steps = [
+ {
+ id: 'SelectBoard',
+ component: SelectBoardStep,
+ title: l10n.getString('firmware-tool-board-step'),
+ },
+ {
+ component: BoardPinsStep,
+ title: l10n.getString('firmware-tool-board-pins-step'),
+ },
+ {
+ component: AddImusStep,
+ title: l10n.getString('firmware-tool-add-imus-step'),
+ },
+ {
+ id: 'SelectFirmware',
+ component: SelectFirmwareStep,
+ title: l10n.getString('firmware-tool-select-firmware-step'),
+ },
+ {
+ component: FlashingMethodStep,
+ id: 'FlashingMethod',
+ title: l10n.getString('firmware-tool-flash-method-step'),
+ },
+ {
+ component: BuildStep,
+ title: l10n.getString('firmware-tool-build-step'),
+ },
+ {
+ component: FlashingStep,
+ title: l10n.getString('firmware-tool-flashing-step'),
+ },
+ ];
+
+ if (
+ context.defaultConfig?.needBootPress &&
+ context.selectedDevices?.find(
+ ({ type }) => type === FirmwareUpdateMethod.SerialFirmwareUpdate
+ )
+ ) {
+ steps.splice(5, 0, {
+ component: FlashBtnStep,
+ title: l10n.getString('firmware-tool-flashbtn-step'),
+ });
+ }
+ return steps;
+ }, [context.defaultConfig?.needBootPress, context.selectedDevices]);
+
+ return (
+
+
+
+ {l10n.getString('firmware-tool')}
+
+
+ <>
+ {l10n
+ .getString('firmware-tool-description')
+ .split('\n')
+ .map((line, i) => (
+
+ {line}
+
+ ))}
+ >
+
+
+ {isError && (
+
+
+ {!isCompatible ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
+
+
+ )}
+ {isLoading && (
+
+
+
+
+
+
+ )}
+ {!isError && !isLoading &&
}
+
+
+
+ );
+}
+
+export function FirmwareToolSettings() {
+ const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ refetchOnWindowFocus: false, // default: true
+ },
+ },
+ });
+ return (
+
+
+
+ );
+}
diff --git a/gui/src/components/firmware-tool/FlashBtnStep.tsx b/gui/src/components/firmware-tool/FlashBtnStep.tsx
new file mode 100644
index 0000000000..43c5d345d4
--- /dev/null
+++ b/gui/src/components/firmware-tool/FlashBtnStep.tsx
@@ -0,0 +1,86 @@
+import { useLocalization } from '@fluent/react';
+import { Typography } from '@/components/commons/Typography';
+import { Button } from '@/components/commons/Button';
+import {
+ boardTypeToFirmwareToolBoardType,
+ useFirmwareTool,
+} from '@/hooks/firmware-tool';
+import { BoardType } from 'solarxr-protocol';
+
+export function FlashBtnStep({
+ nextStep,
+}: {
+ nextStep: () => void;
+ prevStep: () => void;
+ goTo: (id: string) => void;
+ isActive: boolean;
+}) {
+ const { l10n } = useLocalization();
+ const { defaultConfig } = useFirmwareTool();
+
+ return (
+ <>
+
+
+
+ {l10n.getString('firmware-tool-flashbtn-step-description')}
+
+ {defaultConfig?.boardConfig.type ===
+ boardTypeToFirmwareToolBoardType[BoardType.SLIMEVR] ? (
+ <>
+
+ {l10n.getString('firmware-tool-flashbtn-step-board-SLIMEVR')}
+
+
+
+
R11
+
+ {l10n.getString(
+ 'firmware-tool-flashbtn-step-board-SLIMEVR-r11'
+ )}
+
+
+
+
+
R12
+
+ {l10n.getString(
+ 'firmware-tool-flashbtn-step-board-SLIMEVR-r12'
+ )}
+
+
+
+
+
+
R14
+
+ {l10n.getString(
+ 'firmware-tool-flashbtn-step-board-SLIMEVR-r14'
+ )}
+
+
+
+
+ >
+ ) : (
+ <>
+
+ {l10n.getString('firmware-tool-flashbtn-step-board-OTHER')}
+
+ >
+ )}
+
+ {
+ nextStep();
+ }}
+ >
+ Next Step
+
+
+
+
+ >
+ );
+}
diff --git a/gui/src/components/firmware-tool/FlashingMethodStep.tsx b/gui/src/components/firmware-tool/FlashingMethodStep.tsx
new file mode 100644
index 0000000000..6acb3aa09b
--- /dev/null
+++ b/gui/src/components/firmware-tool/FlashingMethodStep.tsx
@@ -0,0 +1,405 @@
+import { Localized, useLocalization } from '@fluent/react';
+import { Typography } from '@/components/commons/Typography';
+import { LoaderIcon, SlimeState } from '@/components/commons/icon/LoaderIcon';
+import {
+ boardTypeToFirmwareToolBoardType,
+ useFirmwareTool,
+} from '@/hooks/firmware-tool';
+import { Control, UseFormReset, UseFormWatch, useForm } from 'react-hook-form';
+import { Radio } from '@/components/commons/Radio';
+import { useWebsocketAPI } from '@/hooks/websocket-api';
+import { useEffect, useLayoutEffect, useState } from 'react';
+import { yupResolver } from '@hookform/resolvers/yup';
+
+import {
+ BoardType,
+ DeviceDataT,
+ FirmwareUpdateMethod,
+ NewSerialDeviceResponseT,
+ RpcMessage,
+ SerialDeviceT,
+ SerialDevicesRequestT,
+ SerialDevicesResponseT,
+ TrackerStatus,
+} from 'solarxr-protocol';
+import { Button } from '@/components/commons/Button';
+import { useAppContext } from '@/hooks/app';
+import { Input } from '@/components/commons/Input';
+import { Dropdown } from '@/components/commons/Dropdown';
+import { useOnboarding } from '@/hooks/onboarding';
+import { DeviceCardControl } from './DeviceCard';
+import { getTrackerName } from '@/hooks/tracker';
+import { ObjectSchema, object, string } from 'yup';
+
+interface FlashingMethodForm {
+ flashingMethod?: string;
+ serial?: {
+ selectedDevicePort: string;
+ ssid: string;
+ password?: string;
+ };
+ ota?: {
+ selectedDevices: { [key: string]: boolean };
+ };
+}
+
+function SerialDevicesList({
+ control,
+ watch,
+ reset,
+}: {
+ control: Control;
+ watch: UseFormWatch;
+ reset: UseFormReset;
+}) {
+ const { l10n } = useLocalization();
+ const { selectDevices } = useFirmwareTool();
+ const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
+ const [devices, setDevices] = useState>({});
+ const { state, setWifiCredentials } = useOnboarding();
+
+ useLayoutEffect(() => {
+ sendRPCPacket(RpcMessage.SerialDevicesRequest, new SerialDevicesRequestT());
+ selectDevices(null);
+ reset({
+ flashingMethod: FirmwareUpdateMethod.SerialFirmwareUpdate.toString(),
+ serial: {
+ ...state.wifi,
+ selectedDevicePort: undefined,
+ },
+ ota: undefined,
+ });
+ }, []);
+
+ useRPCPacket(
+ RpcMessage.SerialDevicesResponse,
+ (res: SerialDevicesResponseT) => {
+ setDevices((old) =>
+ res.devices.reduce(
+ (curr, device) => ({
+ ...curr,
+ [device?.port?.toString() ?? 'unknown']: device,
+ }),
+ old
+ )
+ );
+ }
+ );
+
+ useRPCPacket(
+ RpcMessage.NewSerialDeviceResponse,
+ ({ device }: NewSerialDeviceResponseT) => {
+ if (device?.port)
+ setDevices((old) => ({
+ ...old,
+ [device?.port?.toString() ?? 'unknown']: device,
+ }));
+ }
+ );
+
+ const serialValues = watch('serial');
+
+ useEffect(() => {
+ if (serialValues) {
+ setWifiCredentials(serialValues.ssid, serialValues.password);
+ if (
+ serialValues.selectedDevicePort &&
+ devices[serialValues.selectedDevicePort]
+ ) {
+ selectDevices([
+ {
+ type: FirmwareUpdateMethod.SerialFirmwareUpdate,
+ deviceId: serialValues.selectedDevicePort,
+ deviceNames: [
+ devices[serialValues.selectedDevicePort].name?.toString() ??
+ 'unknown',
+ ],
+ },
+ ]);
+ } else selectDevices(null);
+ } else selectDevices(null);
+ }, [JSON.stringify(serialValues), devices]);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {Object.keys(devices).length == 0 ? (
+
+
+
+ ) : (
+ ({
+ label: devices[port].name?.toString() ?? 'unknown',
+ value: port,
+ }))}
+ placeholder={l10n.getString(
+ 'firmware-tool-flash-method-serial-devices-placeholder'
+ )}
+ display="block"
+ direction="down"
+ >
+ )}
+ >
+ );
+}
+
+function OTADevicesList({
+ control,
+ watch,
+ reset,
+}: {
+ control: Control;
+ watch: UseFormWatch;
+ reset: UseFormReset;
+}) {
+ const { l10n } = useLocalization();
+ const { selectDevices, newConfig } = useFirmwareTool();
+ const { state } = useAppContext();
+
+ const devices =
+ state.datafeed?.devices.filter(
+ ({ trackers, hardwareInfo }) =>
+ trackers.length > 0 &&
+ boardTypeToFirmwareToolBoardType[
+ (hardwareInfo?.officialBoardType !== BoardType.SLIMEVR_LEGACY &&
+ hardwareInfo?.officialBoardType !== BoardType.SLIMEVR_DEV &&
+ hardwareInfo?.officialBoardType !== BoardType.CUSTOM &&
+ hardwareInfo?.officialBoardType) ||
+ BoardType.UNKNOWN
+ ] == newConfig?.boardConfig?.type &&
+ trackers.every(({ status }) => status == TrackerStatus.OK)
+ ) || [];
+
+ const deviceNames = ({ trackers }: DeviceDataT) =>
+ trackers
+ .map(({ info }) => getTrackerName(l10n, info))
+ .filter((i): i is string => !!i);
+
+ const selectedDevices = watch('ota.selectedDevices');
+
+ useLayoutEffect(() => {
+ reset({
+ flashingMethod: FirmwareUpdateMethod.OTAFirmwareUpdate.toString(),
+ ota: {
+ selectedDevices: devices.reduce(
+ (curr, { id }) => ({ ...curr, [id?.id ?? 0]: false }),
+ {}
+ ),
+ },
+ serial: undefined,
+ });
+ selectDevices(null);
+ }, []);
+
+ useEffect(() => {
+ if (selectedDevices) {
+ selectDevices(
+ Object.keys(selectedDevices)
+ .filter((d) => selectedDevices[d])
+ .map((id) => id.substring('id-'.length))
+ .map((id) => {
+ const device = devices.find(
+ ({ id: dId }) => id == dId?.id.toString()
+ );
+
+ if (!device) throw new Error('no device found');
+ return {
+ type: FirmwareUpdateMethod.OTAFirmwareUpdate,
+ deviceId: id,
+ deviceNames: deviceNames(device),
+ };
+ })
+ );
+ }
+ }, [JSON.stringify(selectedDevices)]);
+
+ return (
+ <>
+
+
+
+ {devices.length == 0 && (
+
+
+
+ )}
+
+ {devices.map((device) => (
+
+ ))}
+
+ >
+ );
+}
+
+export function FlashingMethodStep({
+ nextStep,
+ prevStep,
+}: {
+ nextStep: () => void;
+ prevStep: () => void;
+ isActive: boolean;
+}) {
+ const { l10n } = useLocalization();
+ const { isGlobalLoading, selectedDevices } = useFirmwareTool();
+
+ const {
+ control,
+ watch,
+ reset,
+ formState: { isValid },
+ } = useForm({
+ reValidateMode: 'onChange',
+ mode: 'onChange',
+ resolver: yupResolver(
+ object({
+ flashingMethod: string().optional(),
+ serial: object().when('flashingMethod', {
+ is: FirmwareUpdateMethod.SerialFirmwareUpdate.toString(),
+ then: (s) =>
+ s
+ .shape({
+ selectedDevicePort: string().required(),
+ ssid: string().required(
+ l10n.getString('onboarding-wifi_creds-ssid-required')
+ ),
+ password: string(),
+ })
+ .required(),
+ otherwise: (s) => s.optional(),
+ }),
+ ota: object().when('flashingMethod', {
+ is: FirmwareUpdateMethod.OTAFirmwareUpdate.toString(),
+ then: (s) =>
+ s
+ .shape({
+ selectedDevices: object(),
+ })
+ .required(),
+ otherwise: (s) => s.optional(),
+ }),
+ }) as ObjectSchema
+ ),
+ });
+
+ const flashingMethod = watch('flashingMethod');
+
+ return (
+ <>
+
+
+
+ {l10n.getString('firmware-tool-flash-method-step-description')}
+
+
+
+ {!isGlobalLoading && (
+
+
+
+
+
+
+
+
+
+ {flashingMethod ==
+ FirmwareUpdateMethod.SerialFirmwareUpdate.toString() && (
+
+ )}
+ {flashingMethod ==
+ FirmwareUpdateMethod.OTAFirmwareUpdate.toString() && (
+
+ )}
+
+
+
+
+
+
+
+
+
+ )}
+ {isGlobalLoading && (
+
+
+
+
+
+
+ )}
+
+
+ >
+ );
+}
diff --git a/gui/src/components/firmware-tool/FlashingStep.tsx b/gui/src/components/firmware-tool/FlashingStep.tsx
new file mode 100644
index 0000000000..94f5fd1b3f
--- /dev/null
+++ b/gui/src/components/firmware-tool/FlashingStep.tsx
@@ -0,0 +1,269 @@
+import { Localized, useLocalization } from '@fluent/react';
+import { Typography } from '@/components/commons/Typography';
+import {
+ SelectedDevice,
+ firmwareUpdateErrorStatus,
+ useFirmwareTool,
+} from '@/hooks/firmware-tool';
+import { useEffect, useMemo, useState } from 'react';
+import { useWebsocketAPI } from '@/hooks/websocket-api';
+import {
+ DeviceIdT,
+ DeviceIdTableT,
+ FirmwarePartT,
+ FirmwareUpdateMethod,
+ FirmwareUpdateRequestT,
+ FirmwareUpdateStatus,
+ FirmwareUpdateStatusResponseT,
+ FirmwareUpdateStopQueuesRequestT,
+ OTAFirmwareUpdateT,
+ RpcMessage,
+ SerialDevicePortT,
+ SerialFirmwareUpdateT,
+} from 'solarxr-protocol';
+import { firmwareToolS3BaseUrl } from '@/firmware-tool-api/firmwareToolFetcher';
+import { useOnboarding } from '@/hooks/onboarding';
+import { DeviceCardControl } from './DeviceCard';
+import { WarningBox } from '@/components/commons/TipBox';
+import { Button } from '@/components/commons/Button';
+import { useNavigate } from 'react-router-dom';
+
+export function FlashingStep({
+ goTo,
+ isActive,
+}: {
+ nextStep: () => void;
+ prevStep: () => void;
+ goTo: (id: string) => void;
+ isActive: boolean;
+}) {
+ const nav = useNavigate();
+ const { l10n } = useLocalization();
+ const { selectedDevices, buildStatus, selectDevices, defaultConfig } =
+ useFirmwareTool();
+ const { state: onboardingState } = useOnboarding();
+ const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
+ const [status, setStatus] = useState<{
+ [key: string]: {
+ status: FirmwareUpdateStatus;
+ type: FirmwareUpdateMethod;
+ progress: number;
+ deviceNames: string[];
+ };
+ }>({});
+
+ const clear = () => {
+ setStatus({});
+ sendRPCPacket(
+ RpcMessage.FirmwareUpdateStopQueuesRequest,
+ new FirmwareUpdateStopQueuesRequestT()
+ );
+ };
+
+ const queueFlashing = (devices: SelectedDevice[]) => {
+ clear();
+
+ if (!buildStatus.firmwareFiles)
+ throw new Error('invalid state - no firmware files');
+
+ const firmware = buildStatus.firmwareFiles.find(
+ ({ isFirmware }) => isFirmware
+ );
+ if (!firmware) throw new Error('invalid state - no firmware to find');
+
+ for (const device of devices) {
+ switch (device.type) {
+ case FirmwareUpdateMethod.OTAFirmwareUpdate: {
+ const dId = new DeviceIdT();
+ dId.id = +device.deviceId;
+
+ const part = new FirmwarePartT();
+ part.offset = 0;
+ part.url = firmwareToolS3BaseUrl + '/' + firmware.url;
+
+ const method = new OTAFirmwareUpdateT();
+ method.deviceId = dId;
+ method.firmwarePart = part;
+
+ const req = new FirmwareUpdateRequestT();
+ req.method = method;
+ req.methodType = FirmwareUpdateMethod.OTAFirmwareUpdate;
+ sendRPCPacket(RpcMessage.FirmwareUpdateRequest, req);
+ break;
+ }
+ case FirmwareUpdateMethod.SerialFirmwareUpdate: {
+ const id = new SerialDevicePortT();
+ id.port = device.deviceId.toString();
+
+ if (!onboardingState.wifi?.ssid || !onboardingState.wifi?.password)
+ throw new Error('invalid state, wifi should be set');
+
+ const method = new SerialFirmwareUpdateT();
+ method.deviceId = id;
+ method.ssid = onboardingState.wifi.ssid;
+ method.password = onboardingState.wifi.password;
+ method.needManualReboot = defaultConfig?.needManualReboot ?? false;
+
+ method.firmwarePart = buildStatus.firmwareFiles.map(
+ ({ offset, url }) => {
+ const part = new FirmwarePartT();
+ part.offset = offset;
+ part.url = firmwareToolS3BaseUrl + '/' + url;
+ return part;
+ }
+ );
+
+ const req = new FirmwareUpdateRequestT();
+ req.method = method;
+ req.methodType = FirmwareUpdateMethod.SerialFirmwareUpdate;
+ sendRPCPacket(RpcMessage.FirmwareUpdateRequest, req);
+ break;
+ }
+ default: {
+ throw new Error('unsupported flashing method');
+ }
+ }
+ }
+ };
+
+ useEffect(() => {
+ if (!isActive) return;
+ if (!selectedDevices)
+ throw new Error('invalid state - no selected devices');
+ queueFlashing(selectedDevices);
+ return () => {
+ clear();
+ };
+ }, [isActive]);
+
+ useRPCPacket(
+ RpcMessage.FirmwareUpdateStatusResponse,
+ (data: FirmwareUpdateStatusResponseT) => {
+ if (!data.deviceId) throw new Error('no device id');
+ const id =
+ data.deviceId instanceof DeviceIdTableT
+ ? data.deviceId.id?.id
+ : data.deviceId.port;
+ if (!id) throw new Error('invalid device id');
+
+ const selectedDevice = selectedDevices?.find(
+ ({ deviceId }) => deviceId == id.toString()
+ );
+
+ // We skip the status as it can be old trackers still sending status
+ if (!selectedDevice) return;
+
+ setStatus((last) => ({
+ ...last,
+ [id.toString()]: {
+ progress: data.progress / 100,
+ status: data.status,
+ type: selectedDevice.type,
+ deviceNames: selectedDevice.deviceNames,
+ },
+ }));
+ }
+ );
+
+ const trackerWithErrors = Object.keys(status).filter((id) =>
+ firmwareUpdateErrorStatus.includes(status[id].status)
+ );
+
+ const retryError = () => {
+ const devices = trackerWithErrors.map((id) => {
+ const device = status[id];
+ return {
+ type: device.type,
+ deviceId: id,
+ deviceNames: device.deviceNames,
+ };
+ });
+
+ selectDevices(devices);
+ queueFlashing(devices);
+ };
+
+ const hasPendingTrackers = useMemo(
+ () =>
+ Object.keys(status).filter((id) =>
+ [
+ FirmwareUpdateStatus.DOWNLOADING,
+ FirmwareUpdateStatus.AUTHENTICATING,
+ FirmwareUpdateStatus.REBOOTING,
+ FirmwareUpdateStatus.SYNCING_WITH_MCU,
+ FirmwareUpdateStatus.UPLOADING,
+ FirmwareUpdateStatus.PROVISIONING,
+ ].includes(status[id].status)
+ ).length > 0,
+ [status]
+ );
+
+ const shouldShowRebootWarning = useMemo(
+ () =>
+ Object.keys(status).find((id) =>
+ [
+ FirmwareUpdateStatus.REBOOTING,
+ FirmwareUpdateStatus.UPLOADING,
+ ].includes(status[id].status)
+ ),
+ [status]
+ );
+
+ return (
+ <>
+
+
+
+ {l10n.getString('firmware-tool-flashing-step-description')}
+
+
+
+
+ {shouldShowRebootWarning && (
+
+ Warning
+
+ )}
+
+ {Object.keys(status).map((id) => {
+ const val = status[id];
+
+ return (
+
+ );
+ })}
+
+
+
+
+
+ goTo('FlashingMethod')}
+ >
+
+
+ {
+ clear();
+ nav('/');
+ }}
+ >
+
+
+
+
+ >
+ );
+}
diff --git a/gui/src/components/firmware-tool/SelectBoardStep.tsx b/gui/src/components/firmware-tool/SelectBoardStep.tsx
new file mode 100644
index 0000000000..0c4aa72cc3
--- /dev/null
+++ b/gui/src/components/firmware-tool/SelectBoardStep.tsx
@@ -0,0 +1,91 @@
+import { useLocalization } from '@fluent/react';
+import { Typography } from '@/components/commons/Typography';
+import { LoaderIcon, SlimeState } from '@/components/commons/icon/LoaderIcon';
+import {
+ firmwareToolToBoardType,
+ useFirmwareTool,
+} from '@/hooks/firmware-tool';
+import { CreateBoardConfigDTO } from '@/firmware-tool-api/firmwareToolSchemas';
+import classNames from 'classnames';
+import { Button } from '@/components/commons/Button';
+import { useGetFirmwaresBoards } from '@/firmware-tool-api/firmwareToolComponents';
+import { BoardType } from 'solarxr-protocol';
+
+export function SelectBoardStep({
+ nextStep,
+ goTo,
+}: {
+ nextStep: () => void;
+ prevStep: () => void;
+ goTo: (id: string) => void;
+}) {
+ const { l10n } = useLocalization();
+ const { selectBoard, newConfig, defaultConfig } = useFirmwareTool();
+ const { isFetching, data: boards } = useGetFirmwaresBoards({});
+
+ return (
+ <>
+
+
+
+ {l10n.getString('firmware-tool-board-step-description')}
+
+
+
+ {!isFetching && (
+
+
+ {boards?.map((board) => (
+
{
+ selectBoard(board as CreateBoardConfigDTO['type']);
+ }}
+ >
+ {l10n.getString(
+ `board_type-${
+ BoardType[
+ firmwareToolToBoardType[
+ board as CreateBoardConfigDTO['type']
+ ] ?? BoardType.UNKNOWN
+ ]
+ }`
+ )}
+
+ ))}
+
+
+ {
+ if (defaultConfig?.shouldOnlyUseDefaults)
+ goTo('SelectFirmware');
+ else nextStep();
+ }}
+ >
+ Next Step
+
+
+
+ )}
+ {isFetching && (
+
+
+ Loading ...
+
+ )}
+
+
+ >
+ );
+}
diff --git a/gui/src/components/firmware-tool/SelectFirmwareStep.tsx b/gui/src/components/firmware-tool/SelectFirmwareStep.tsx
new file mode 100644
index 0000000000..7d547870ba
--- /dev/null
+++ b/gui/src/components/firmware-tool/SelectFirmwareStep.tsx
@@ -0,0 +1,117 @@
+import { Localized, useLocalization } from '@fluent/react';
+import { Typography } from '@/components/commons/Typography';
+import { useGetFirmwaresVersions } from '@/firmware-tool-api/firmwareToolComponents';
+import { LoaderIcon, SlimeState } from '@/components/commons/icon/LoaderIcon';
+import { useFirmwareTool } from '@/hooks/firmware-tool';
+import classNames from 'classnames';
+import { Button } from '@/components/commons/Button';
+import { useMemo } from 'react';
+import { CheckBox } from '@/components/commons/Checkbox';
+import { useForm } from 'react-hook-form';
+
+export function SelectFirmwareStep({
+ nextStep,
+ prevStep,
+ goTo,
+}: {
+ nextStep: () => void;
+ prevStep: () => void;
+ goTo: (id: string) => void;
+}) {
+ const { l10n } = useLocalization();
+ const { selectVersion, newConfig, defaultConfig } = useFirmwareTool();
+ const { isFetching, data: firmwares } = useGetFirmwaresVersions({});
+
+ const { control, watch } = useForm<{ thirdParty: boolean }>({});
+
+ const showThirdParty = watch('thirdParty');
+
+ const getName = (name: string) => {
+ return showThirdParty ? name : name.substring(name.indexOf('/') + 1);
+ };
+
+ const filteredFirmwares = useMemo(() => {
+ return firmwares?.filter(
+ ({ name }) => name.split('/')[0] === 'SlimeVR' || showThirdParty
+ );
+ }, [firmwares, showThirdParty]);
+
+ return (
+ <>
+
+
+
+ {l10n.getString('firmware-tool-select-firmware-step-description')}
+
+
+
+
+
+
+
+
+ {!isFetching && (
+
+
+
+ {filteredFirmwares?.map((firmwares) => (
+
{
+ selectVersion(firmwares.name);
+ }}
+ >
+ {getName(firmwares.name)}
+
+ ))}
+
+
+
+
+ {
+ if (defaultConfig?.shouldOnlyUseDefaults) {
+ goTo('SelectBoard');
+ } else {
+ prevStep();
+ }
+ }}
+ >
+
+
+
+
+
+
+ )}
+ {isFetching && (
+
+
+
+
+
+
+ )}
+
+
+ >
+ );
+}
diff --git a/gui/src/components/onboarding/pages/ConnectTracker.tsx b/gui/src/components/onboarding/pages/ConnectTracker.tsx
index cc93ba47fe..71b1214632 100644
--- a/gui/src/components/onboarding/pages/ConnectTracker.tsx
+++ b/gui/src/components/onboarding/pages/ConnectTracker.tsx
@@ -3,11 +3,9 @@ import classNames from 'classnames';
import { useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
- AddUnknownDeviceRequestT,
RpcMessage,
StartWifiProvisioningRequestT,
StopWifiProvisioningRequestT,
- UnknownDeviceHandshakeNotificationT,
WifiProvisioningStatus,
WifiProvisioningStatusResponseT,
} from 'solarxr-protocol';
@@ -97,15 +95,6 @@ export function ConnectTrackersPage() {
}
);
- useRPCPacket(
- RpcMessage.UnknownDeviceHandshakeNotification,
- ({ macAddress }: UnknownDeviceHandshakeNotificationT) =>
- sendRPCPacket(
- RpcMessage.AddUnknownDeviceRequest,
- new AddUnknownDeviceRequestT(macAddress)
- )
- );
-
const isError =
provisioningStatus === WifiProvisioningStatus.CONNECTION_ERROR ||
provisioningStatus === WifiProvisioningStatus.COULD_NOT_FIND_SERVER;
diff --git a/gui/src/components/onboarding/pages/Home.tsx b/gui/src/components/onboarding/pages/Home.tsx
index b9df6460af..3c33c21d3b 100644
--- a/gui/src/components/onboarding/pages/Home.tsx
+++ b/gui/src/components/onboarding/pages/Home.tsx
@@ -13,7 +13,7 @@ export function HomePage() {
return (
<>
-
+
diff --git a/gui/src/components/settings/SettingsLayout.tsx b/gui/src/components/settings/SettingsLayout.tsx
index 491d53a8fc..40b6c37e72 100644
--- a/gui/src/components/settings/SettingsLayout.tsx
+++ b/gui/src/components/settings/SettingsLayout.tsx
@@ -40,6 +40,10 @@ export function SettingSelectorMobile() {
label: l10n.getString('settings-sidebar-serial'),
value: { url: '/settings/serial' },
},
+ {
+ label: l10n.getString('settings-sidebar-firmware-tool'),
+ value: { url: '/settings/firmware-tool' },
+ },
{
label: l10n.getString('settings-sidebar-advanced'),
value: { url: '/settings/advanced' },
@@ -99,7 +103,7 @@ export function SettingsLayout({ children }: { children: ReactNode }) {
-
+
{l10n.getString('settings-sidebar-serial')}
+
+ {l10n.getString('settings-sidebar-firmware-tool')}
+
diff --git a/gui/src/components/tracker/TrackerSettings.tsx b/gui/src/components/tracker/TrackerSettings.tsx
index 8a1f1f7a57..72de235265 100644
--- a/gui/src/components/tracker/TrackerSettings.tsx
+++ b/gui/src/components/tracker/TrackerSettings.tsx
@@ -6,6 +6,7 @@ import { useForm } from 'react-hook-form';
import { useParams } from 'react-router-dom';
import {
AssignTrackerRequestT,
+ BoardType,
BodyPart,
ForgetDeviceRequestT,
ImuType,
@@ -148,6 +149,25 @@ export function TrackerSettingsPage() {
}
}, [firstLoad]);
+ const boarType = useMemo(() => {
+ if (tracker?.device?.hardwareInfo?.officialBoardType) {
+ return l10n.getString(
+ 'board_type-' +
+ BoardType[
+ tracker?.device?.hardwareInfo?.officialBoardType ??
+ BoardType.UNKNOWN
+ ]
+ );
+ } else if (tracker?.device?.hardwareInfo?.boardType) {
+ return tracker?.device?.hardwareInfo?.boardType;
+ } else {
+ return '--';
+ }
+ }, [
+ tracker?.device?.hardwareInfo?.officialBoardType,
+ tracker?.device?.hardwareInfo?.boardType,
+ ]);
+
const macAddress = useMemo(() => {
if (
/(?:[a-zA-Z\d]{2}:){5}[a-zA-Z\d]{2}/.test(
@@ -274,9 +294,7 @@ export function TrackerSettingsPage() {
{l10n.getString('tracker-infos-board_type')}
-
- {tracker?.device?.hardwareInfo?.boardType || '--'}
-
+ {boarType}
diff --git a/gui/src/firmware-tool-api/firmwareToolComponents.ts b/gui/src/firmware-tool-api/firmwareToolComponents.ts
new file mode 100644
index 0000000000..12c65bab12
--- /dev/null
+++ b/gui/src/firmware-tool-api/firmwareToolComponents.ts
@@ -0,0 +1,659 @@
+/**
+ * Generated by @openapi-codegen
+ *
+ * @version 0.0.1
+ */
+import * as reactQuery from '@tanstack/react-query';
+import { useFirmwareToolContext, FirmwareToolContext } from './firmwareToolContext';
+import type * as Fetcher from './firmwareToolFetcher';
+import { firmwareToolFetch } from './firmwareToolFetcher';
+import type * as Schemas from './firmwareToolSchemas';
+
+export type GetIsCompatibleVersionPathParams = {
+ version: string;
+};
+
+export type GetIsCompatibleVersionError = Fetcher.ErrorWrapper;
+
+export type GetIsCompatibleVersionVariables = {
+ pathParams: GetIsCompatibleVersionPathParams;
+} & FirmwareToolContext['fetcherOptions'];
+
+/**
+ * Is this api compatible with the server version given
+ */
+export const fetchGetIsCompatibleVersion = (
+ variables: GetIsCompatibleVersionVariables,
+ signal?: AbortSignal
+) =>
+ firmwareToolFetch<
+ Schemas.VerionCheckResponse,
+ GetIsCompatibleVersionError,
+ undefined,
+ {},
+ {},
+ GetIsCompatibleVersionPathParams
+ >({ url: '/is-compatible/{version}', method: 'get', ...variables, signal });
+
+/**
+ * Is this api compatible with the server version given
+ */
+export const useGetIsCompatibleVersion = (
+ variables: GetIsCompatibleVersionVariables,
+ options?: Omit<
+ reactQuery.UseQueryOptions<
+ Schemas.VerionCheckResponse,
+ GetIsCompatibleVersionError,
+ TData
+ >,
+ 'queryKey' | 'queryFn' | 'initialData'
+ >
+) => {
+ const { fetcherOptions, queryOptions, queryKeyFn } = useFirmwareToolContext(options);
+ return reactQuery.useQuery<
+ Schemas.VerionCheckResponse,
+ GetIsCompatibleVersionError,
+ TData
+ >({
+ queryKey: queryKeyFn({
+ path: '/is-compatible/{version}',
+ operationId: 'getIsCompatibleVersion',
+ variables,
+ }),
+ queryFn: ({ signal }) =>
+ fetchGetIsCompatibleVersion({ ...fetcherOptions, ...variables }, signal),
+ ...options,
+ ...queryOptions,
+ });
+};
+
+export type GetFirmwaresError = Fetcher.ErrorWrapper;
+
+export type GetFirmwaresResponse = Schemas.FirmwareDTO[];
+
+export type GetFirmwaresVariables = FirmwareToolContext['fetcherOptions'];
+
+/**
+ * List all the built firmwares
+ */
+export const fetchGetFirmwares = (
+ variables: GetFirmwaresVariables,
+ signal?: AbortSignal
+) =>
+ firmwareToolFetch({
+ url: '/firmwares',
+ method: 'get',
+ ...variables,
+ signal,
+ });
+
+/**
+ * List all the built firmwares
+ */
+export const useGetFirmwares = (
+ variables: GetFirmwaresVariables,
+ options?: Omit<
+ reactQuery.UseQueryOptions,
+ 'queryKey' | 'queryFn' | 'initialData'
+ >
+) => {
+ const { fetcherOptions, queryOptions, queryKeyFn } = useFirmwareToolContext(options);
+ return reactQuery.useQuery({
+ queryKey: queryKeyFn({
+ path: '/firmwares',
+ operationId: 'getFirmwares',
+ variables,
+ }),
+ queryFn: ({ signal }) =>
+ fetchGetFirmwares({ ...fetcherOptions, ...variables }, signal),
+ ...options,
+ ...queryOptions,
+ });
+};
+
+export type PostFirmwaresBuildError = Fetcher.ErrorWrapper<{
+ status: 400;
+ payload: Schemas.VersionNotFoundExeption;
+}>;
+
+export type PostFirmwaresBuildVariables = {
+ body: Schemas.CreateBuildFirmwareDTO;
+} & FirmwareToolContext['fetcherOptions'];
+
+/**
+ * Build a firmware from the requested configuration
+ */
+export const fetchPostFirmwaresBuild = (
+ variables: PostFirmwaresBuildVariables,
+ signal?: AbortSignal
+) =>
+ firmwareToolFetch<
+ Schemas.BuildResponseDTO,
+ PostFirmwaresBuildError,
+ Schemas.CreateBuildFirmwareDTO,
+ {},
+ {},
+ {}
+ >({ url: '/firmwares/build', method: 'post', ...variables, signal });
+
+/**
+ * Build a firmware from the requested configuration
+ */
+export const usePostFirmwaresBuild = (
+ options?: Omit<
+ reactQuery.UseMutationOptions<
+ Schemas.BuildResponseDTO,
+ PostFirmwaresBuildError,
+ PostFirmwaresBuildVariables
+ >,
+ 'mutationFn'
+ >
+) => {
+ const { fetcherOptions } = useFirmwareToolContext();
+ return reactQuery.useMutation<
+ Schemas.BuildResponseDTO,
+ PostFirmwaresBuildError,
+ PostFirmwaresBuildVariables
+ >({
+ mutationFn: (variables: PostFirmwaresBuildVariables) =>
+ fetchPostFirmwaresBuild({ ...fetcherOptions, ...variables }),
+ ...options,
+ });
+};
+
+export type GetFirmwaresBuildStatusIdPathParams = {
+ id: string;
+};
+
+export type GetFirmwaresBuildStatusIdError = Fetcher.ErrorWrapper;
+
+export type GetFirmwaresBuildStatusIdVariables = {
+ pathParams: GetFirmwaresBuildStatusIdPathParams;
+} & FirmwareToolContext['fetcherOptions'];
+
+/**
+ * Get the build status of a firmware
+ * This is a SSE (Server Sent Event)
+ * you can use the web browser api to check for the build status and update the ui in real time
+ */
+export const fetchGetFirmwaresBuildStatusId = (
+ variables: GetFirmwaresBuildStatusIdVariables,
+ signal?: AbortSignal
+) =>
+ firmwareToolFetch<
+ Schemas.ObservableType,
+ GetFirmwaresBuildStatusIdError,
+ undefined,
+ {},
+ {},
+ GetFirmwaresBuildStatusIdPathParams
+ >({
+ url: '/firmwares/build-status/{id}',
+ method: 'get',
+ ...variables,
+ signal,
+ });
+
+/**
+ * Get the build status of a firmware
+ * This is a SSE (Server Sent Event)
+ * you can use the web browser api to check for the build status and update the ui in real time
+ */
+export const useGetFirmwaresBuildStatusId = (
+ variables: GetFirmwaresBuildStatusIdVariables,
+ options?: Omit<
+ reactQuery.UseQueryOptions<
+ Schemas.ObservableType,
+ GetFirmwaresBuildStatusIdError,
+ TData
+ >,
+ 'queryKey' | 'queryFn' | 'initialData'
+ >
+) => {
+ const { fetcherOptions, queryOptions, queryKeyFn } = useFirmwareToolContext(options);
+ return reactQuery.useQuery<
+ Schemas.ObservableType,
+ GetFirmwaresBuildStatusIdError,
+ TData
+ >({
+ queryKey: queryKeyFn({
+ path: '/firmwares/build-status/{id}',
+ operationId: 'getFirmwaresBuildStatusId',
+ variables,
+ }),
+ queryFn: ({ signal }) =>
+ fetchGetFirmwaresBuildStatusId({ ...fetcherOptions, ...variables }, signal),
+ ...options,
+ ...queryOptions,
+ });
+};
+
+export type GetFirmwaresBoardsError = Fetcher.ErrorWrapper;
+
+export type GetFirmwaresBoardsResponse = string[];
+
+export type GetFirmwaresBoardsVariables = FirmwareToolContext['fetcherOptions'];
+
+/**
+ * List all the possible board types
+ */
+export const fetchGetFirmwaresBoards = (
+ variables: GetFirmwaresBoardsVariables,
+ signal?: AbortSignal
+) =>
+ firmwareToolFetch<
+ GetFirmwaresBoardsResponse,
+ GetFirmwaresBoardsError,
+ undefined,
+ {},
+ {},
+ {}
+ >({ url: '/firmwares/boards', method: 'get', ...variables, signal });
+
+/**
+ * List all the possible board types
+ */
+export const useGetFirmwaresBoards = (
+ variables: GetFirmwaresBoardsVariables,
+ options?: Omit<
+ reactQuery.UseQueryOptions<
+ GetFirmwaresBoardsResponse,
+ GetFirmwaresBoardsError,
+ TData
+ >,
+ 'queryKey' | 'queryFn' | 'initialData'
+ >
+) => {
+ const { fetcherOptions, queryOptions, queryKeyFn } = useFirmwareToolContext(options);
+ return reactQuery.useQuery<
+ GetFirmwaresBoardsResponse,
+ GetFirmwaresBoardsError,
+ TData
+ >({
+ queryKey: queryKeyFn({
+ path: '/firmwares/boards',
+ operationId: 'getFirmwaresBoards',
+ variables,
+ }),
+ queryFn: ({ signal }) =>
+ fetchGetFirmwaresBoards({ ...fetcherOptions, ...variables }, signal),
+ ...options,
+ ...queryOptions,
+ });
+};
+
+export type GetFirmwaresVersionsError = Fetcher.ErrorWrapper;
+
+export type GetFirmwaresVersionsResponse = Schemas.ReleaseDTO[];
+
+export type GetFirmwaresVersionsVariables = FirmwareToolContext['fetcherOptions'];
+
+/**
+ * List all the possible versions to build a firmware from
+ */
+export const fetchGetFirmwaresVersions = (
+ variables: GetFirmwaresVersionsVariables,
+ signal?: AbortSignal
+) =>
+ firmwareToolFetch<
+ GetFirmwaresVersionsResponse,
+ GetFirmwaresVersionsError,
+ undefined,
+ {},
+ {},
+ {}
+ >({ url: '/firmwares/versions', method: 'get', ...variables, signal });
+
+/**
+ * List all the possible versions to build a firmware from
+ */
+export const useGetFirmwaresVersions = (
+ variables: GetFirmwaresVersionsVariables,
+ options?: Omit<
+ reactQuery.UseQueryOptions<
+ GetFirmwaresVersionsResponse,
+ GetFirmwaresVersionsError,
+ TData
+ >,
+ 'queryKey' | 'queryFn' | 'initialData'
+ >
+) => {
+ const { fetcherOptions, queryOptions, queryKeyFn } = useFirmwareToolContext(options);
+ return reactQuery.useQuery<
+ GetFirmwaresVersionsResponse,
+ GetFirmwaresVersionsError,
+ TData
+ >({
+ queryKey: queryKeyFn({
+ path: '/firmwares/versions',
+ operationId: 'getFirmwaresVersions',
+ variables,
+ }),
+ queryFn: ({ signal }) =>
+ fetchGetFirmwaresVersions({ ...fetcherOptions, ...variables }, signal),
+ ...options,
+ ...queryOptions,
+ });
+};
+
+export type GetFirmwaresImusError = Fetcher.ErrorWrapper;
+
+export type GetFirmwaresImusResponse = Schemas.Imudto[];
+
+export type GetFirmwaresImusVariables = FirmwareToolContext['fetcherOptions'];
+
+/**
+ * List all the possible imus to use
+ */
+export const fetchGetFirmwaresImus = (
+ variables: GetFirmwaresImusVariables,
+ signal?: AbortSignal
+) =>
+ firmwareToolFetch<
+ GetFirmwaresImusResponse,
+ GetFirmwaresImusError,
+ undefined,
+ {},
+ {},
+ {}
+ >({ url: '/firmwares/imus', method: 'get', ...variables, signal });
+
+/**
+ * List all the possible imus to use
+ */
+export const useGetFirmwaresImus = (
+ variables: GetFirmwaresImusVariables,
+ options?: Omit<
+ reactQuery.UseQueryOptions,
+ 'queryKey' | 'queryFn' | 'initialData'
+ >
+) => {
+ const { fetcherOptions, queryOptions, queryKeyFn } = useFirmwareToolContext(options);
+ return reactQuery.useQuery({
+ queryKey: queryKeyFn({
+ path: '/firmwares/imus',
+ operationId: 'getFirmwaresImus',
+ variables,
+ }),
+ queryFn: ({ signal }) =>
+ fetchGetFirmwaresImus({ ...fetcherOptions, ...variables }, signal),
+ ...options,
+ ...queryOptions,
+ });
+};
+
+export type GetFirmwaresBatteriesError = Fetcher.ErrorWrapper;
+
+export type GetFirmwaresBatteriesResponse = string[];
+
+export type GetFirmwaresBatteriesVariables = FirmwareToolContext['fetcherOptions'];
+
+/**
+ * List all the battery types
+ */
+export const fetchGetFirmwaresBatteries = (
+ variables: GetFirmwaresBatteriesVariables,
+ signal?: AbortSignal
+) =>
+ firmwareToolFetch<
+ GetFirmwaresBatteriesResponse,
+ GetFirmwaresBatteriesError,
+ undefined,
+ {},
+ {},
+ {}
+ >({ url: '/firmwares/batteries', method: 'get', ...variables, signal });
+
+/**
+ * List all the battery types
+ */
+export const useGetFirmwaresBatteries = (
+ variables: GetFirmwaresBatteriesVariables,
+ options?: Omit<
+ reactQuery.UseQueryOptions<
+ GetFirmwaresBatteriesResponse,
+ GetFirmwaresBatteriesError,
+ TData
+ >,
+ 'queryKey' | 'queryFn' | 'initialData'
+ >
+) => {
+ const { fetcherOptions, queryOptions, queryKeyFn } = useFirmwareToolContext(options);
+ return reactQuery.useQuery<
+ GetFirmwaresBatteriesResponse,
+ GetFirmwaresBatteriesError,
+ TData
+ >({
+ queryKey: queryKeyFn({
+ path: '/firmwares/batteries',
+ operationId: 'getFirmwaresBatteries',
+ variables,
+ }),
+ queryFn: ({ signal }) =>
+ fetchGetFirmwaresBatteries({ ...fetcherOptions, ...variables }, signal),
+ ...options,
+ ...queryOptions,
+ });
+};
+
+export type GetFirmwaresDefaultConfigBoardPathParams = {
+ board:
+ | 'BOARD_SLIMEVR'
+ | 'BOARD_NODEMCU'
+ | 'BOARD_WROOM32'
+ | 'BOARD_WEMOSD1MINI'
+ | 'BOARD_TTGO_TBASE'
+ | 'BOARD_ESP01'
+ | 'BOARD_LOLIN_C3_MINI'
+ | 'BOARD_BEETLE32C3'
+ | 'BOARD_ES32C3DEVKITM1';
+};
+
+export type GetFirmwaresDefaultConfigBoardError = Fetcher.ErrorWrapper;
+
+export type GetFirmwaresDefaultConfigBoardVariables = {
+ pathParams: GetFirmwaresDefaultConfigBoardPathParams;
+} & FirmwareToolContext['fetcherOptions'];
+
+/**
+ * Gives the default pins / configuration of a given board
+ */
+export const fetchGetFirmwaresDefaultConfigBoard = (
+ variables: GetFirmwaresDefaultConfigBoardVariables,
+ signal?: AbortSignal
+) =>
+ firmwareToolFetch<
+ Schemas.DefaultBuildConfigDTO,
+ GetFirmwaresDefaultConfigBoardError,
+ undefined,
+ {},
+ {},
+ GetFirmwaresDefaultConfigBoardPathParams
+ >({
+ url: '/firmwares/default-config/{board}',
+ method: 'get',
+ ...variables,
+ signal,
+ });
+
+/**
+ * Gives the default pins / configuration of a given board
+ */
+export const useGetFirmwaresDefaultConfigBoard = <
+ TData = Schemas.DefaultBuildConfigDTO,
+>(
+ variables: GetFirmwaresDefaultConfigBoardVariables,
+ options?: Omit<
+ reactQuery.UseQueryOptions<
+ Schemas.DefaultBuildConfigDTO,
+ GetFirmwaresDefaultConfigBoardError,
+ TData
+ >,
+ 'queryKey' | 'queryFn' | 'initialData'
+ >
+) => {
+ const { fetcherOptions, queryOptions, queryKeyFn } = useFirmwareToolContext(options);
+ return reactQuery.useQuery<
+ Schemas.DefaultBuildConfigDTO,
+ GetFirmwaresDefaultConfigBoardError,
+ TData
+ >({
+ queryKey: queryKeyFn({
+ path: '/firmwares/default-config/{board}',
+ operationId: 'getFirmwaresDefaultConfigBoard',
+ variables,
+ }),
+ queryFn: ({ signal }) =>
+ fetchGetFirmwaresDefaultConfigBoard({ ...fetcherOptions, ...variables }, signal),
+ ...options,
+ ...queryOptions,
+ });
+};
+
+export type GetFirmwaresIdPathParams = {
+ id: string;
+};
+
+export type GetFirmwaresIdError = Fetcher.ErrorWrapper<{
+ status: 404;
+ payload: Schemas.HttpException;
+}>;
+
+export type GetFirmwaresIdVariables = {
+ pathParams: GetFirmwaresIdPathParams;
+} & FirmwareToolContext['fetcherOptions'];
+
+/**
+ * Get the inforamtions about a firmware from its id
+ * also provide more informations than the simple list, like pins and imus and files
+ */
+export const fetchGetFirmwaresId = (
+ variables: GetFirmwaresIdVariables,
+ signal?: AbortSignal
+) =>
+ firmwareToolFetch<
+ Schemas.FirmwareDetailDTO,
+ GetFirmwaresIdError,
+ undefined,
+ {},
+ {},
+ GetFirmwaresIdPathParams
+ >({ url: '/firmwares/{id}', method: 'get', ...variables, signal });
+
+/**
+ * Get the inforamtions about a firmware from its id
+ * also provide more informations than the simple list, like pins and imus and files
+ */
+export const useGetFirmwaresId = (
+ variables: GetFirmwaresIdVariables,
+ options?: Omit<
+ reactQuery.UseQueryOptions,
+ 'queryKey' | 'queryFn' | 'initialData'
+ >
+) => {
+ const { fetcherOptions, queryOptions, queryKeyFn } = useFirmwareToolContext(options);
+ return reactQuery.useQuery({
+ queryKey: queryKeyFn({
+ path: '/firmwares/{id}',
+ operationId: 'getFirmwaresId',
+ variables,
+ }),
+ queryFn: ({ signal }) =>
+ fetchGetFirmwaresId({ ...fetcherOptions, ...variables }, signal),
+ ...options,
+ ...queryOptions,
+ });
+};
+
+export type GetHealthError = Fetcher.ErrorWrapper;
+
+export type GetHealthVariables = FirmwareToolContext['fetcherOptions'];
+
+/**
+ * Gives the status of the api
+ * this endpoint will always return true
+ */
+export const fetchGetHealth = (variables: GetHealthVariables, signal?: AbortSignal) =>
+ firmwareToolFetch({
+ url: '/health',
+ method: 'get',
+ ...variables,
+ signal,
+ });
+
+/**
+ * Gives the status of the api
+ * this endpoint will always return true
+ */
+export const useGetHealth = (
+ variables: GetHealthVariables,
+ options?: Omit<
+ reactQuery.UseQueryOptions,
+ 'queryKey' | 'queryFn' | 'initialData'
+ >
+) => {
+ const { fetcherOptions, queryOptions, queryKeyFn } = useFirmwareToolContext(options);
+ return reactQuery.useQuery({
+ queryKey: queryKeyFn({
+ path: '/health',
+ operationId: 'getHealth',
+ variables,
+ }),
+ queryFn: ({ signal }) =>
+ fetchGetHealth({ ...fetcherOptions, ...variables }, signal),
+ ...options,
+ ...queryOptions,
+ });
+};
+
+export type QueryOperation =
+ | {
+ path: '/is-compatible/{version}';
+ operationId: 'getIsCompatibleVersion';
+ variables: GetIsCompatibleVersionVariables;
+ }
+ | {
+ path: '/firmwares';
+ operationId: 'getFirmwares';
+ variables: GetFirmwaresVariables;
+ }
+ | {
+ path: '/firmwares/build-status/{id}';
+ operationId: 'getFirmwaresBuildStatusId';
+ variables: GetFirmwaresBuildStatusIdVariables;
+ }
+ | {
+ path: '/firmwares/boards';
+ operationId: 'getFirmwaresBoards';
+ variables: GetFirmwaresBoardsVariables;
+ }
+ | {
+ path: '/firmwares/versions';
+ operationId: 'getFirmwaresVersions';
+ variables: GetFirmwaresVersionsVariables;
+ }
+ | {
+ path: '/firmwares/imus';
+ operationId: 'getFirmwaresImus';
+ variables: GetFirmwaresImusVariables;
+ }
+ | {
+ path: '/firmwares/batteries';
+ operationId: 'getFirmwaresBatteries';
+ variables: GetFirmwaresBatteriesVariables;
+ }
+ | {
+ path: '/firmwares/default-config/{board}';
+ operationId: 'getFirmwaresDefaultConfigBoard';
+ variables: GetFirmwaresDefaultConfigBoardVariables;
+ }
+ | {
+ path: '/firmwares/{id}';
+ operationId: 'getFirmwaresId';
+ variables: GetFirmwaresIdVariables;
+ }
+ | {
+ path: '/health';
+ operationId: 'getHealth';
+ variables: GetHealthVariables;
+ };
diff --git a/gui/src/firmware-tool-api/firmwareToolContext.ts b/gui/src/firmware-tool-api/firmwareToolContext.ts
new file mode 100644
index 0000000000..77e9cd9148
--- /dev/null
+++ b/gui/src/firmware-tool-api/firmwareToolContext.ts
@@ -0,0 +1,99 @@
+import type { QueryKey, UseQueryOptions } from '@tanstack/react-query';
+import { QueryOperation } from './firmwareToolComponents';
+
+export type FirmwareToolContext = {
+ fetcherOptions: {
+ /**
+ * Headers to inject in the fetcher
+ */
+ headers?: {};
+ /**
+ * Query params to inject in the fetcher
+ */
+ queryParams?: {};
+ };
+ queryOptions: {
+ /**
+ * Set this to `false` to disable automatic refetching when the query mounts or changes query keys.
+ * Defaults to `true`.
+ */
+ enabled?: boolean;
+ };
+ /**
+ * Query key manager.
+ */
+ queryKeyFn: (operation: QueryOperation) => QueryKey;
+};
+
+/**
+ * Context injected into every react-query hook wrappers
+ *
+ * @param queryOptions options from the useQuery wrapper
+ */
+export function useFirmwareToolContext<
+ TQueryFnData = unknown,
+ TError = unknown,
+ TData = TQueryFnData,
+ TQueryKey extends QueryKey = QueryKey,
+>(
+ _queryOptions?: Omit<
+ UseQueryOptions,
+ 'queryKey' | 'queryFn'
+ >
+): FirmwareToolContext {
+ return {
+ fetcherOptions: {},
+ queryOptions: {},
+ queryKeyFn,
+ };
+}
+
+export const queryKeyFn = (operation: QueryOperation) => {
+ const queryKey: unknown[] = hasPathParams(operation)
+ ? operation.path
+ .split('/')
+ .filter(Boolean)
+ .map((i) => resolvePathParam(i, operation.variables.pathParams))
+ : operation.path.split('/').filter(Boolean);
+
+ if (hasQueryParams(operation)) {
+ queryKey.push(operation.variables.queryParams);
+ }
+
+ if (hasBody(operation)) {
+ queryKey.push(operation.variables.body);
+ }
+
+ return queryKey;
+};
+// Helpers
+const resolvePathParam = (key: string, pathParams: Record) => {
+ if (key.startsWith('{') && key.endsWith('}')) {
+ return pathParams[key.slice(1, -1)];
+ }
+ return key;
+};
+
+const hasPathParams = (
+ operation: QueryOperation
+): operation is QueryOperation & {
+ variables: { pathParams: Record };
+} => {
+ return Boolean((operation.variables as any).pathParams);
+};
+
+const hasBody = (
+ operation: QueryOperation
+): operation is QueryOperation & {
+ variables: { body: Record };
+} => {
+ return Boolean((operation.variables as any).body);
+};
+
+const hasQueryParams = (
+ operation: QueryOperation
+): operation is QueryOperation & {
+ variables: { queryParams: Record };
+} => {
+ return Boolean((operation.variables as any).queryParams);
+};
diff --git a/gui/src/firmware-tool-api/firmwareToolFetcher.ts b/gui/src/firmware-tool-api/firmwareToolFetcher.ts
new file mode 100644
index 0000000000..c20cc4c6ba
--- /dev/null
+++ b/gui/src/firmware-tool-api/firmwareToolFetcher.ts
@@ -0,0 +1,109 @@
+import { FirmwareToolContext } from './firmwareToolContext';
+
+export const firmwareToolBaseUrl =
+ import.meta.env.VITE_FIRMWARE_TOOL_URL ?? 'http://localhost:3000';
+export const firmwareToolS3BaseUrl =
+ import.meta.env.VITE_FIRMWARE_TOOL_S3_URL ?? 'http://localhost:9099';
+
+export type ErrorWrapper = TError | { status: 'unknown'; payload: string };
+
+export type FirmwareToolFetcherOptions = {
+ url: string;
+ method: string;
+ body?: TBody;
+ headers?: THeaders;
+ queryParams?: TQueryParams;
+ pathParams?: TPathParams;
+ signal?: AbortSignal;
+} & FirmwareToolContext['fetcherOptions'];
+
+export async function firmwareToolFetch<
+ TData,
+ TError,
+ TBody extends {} | FormData | undefined | null,
+ THeaders extends {},
+ TQueryParams extends {},
+ TPathParams extends {},
+>({
+ url,
+ method,
+ body,
+ headers,
+ pathParams,
+ queryParams,
+ signal,
+}: FirmwareToolFetcherOptions<
+ TBody,
+ THeaders,
+ TQueryParams,
+ TPathParams
+>): Promise {
+ try {
+ const requestHeaders: HeadersInit = {
+ 'Content-Type': 'application/json',
+ ...headers,
+ };
+
+ /**
+ * As the fetch API is being used, when multipart/form-data is specified
+ * the Content-Type header must be deleted so that the browser can set
+ * the correct boundary.
+ * https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects#sending_files_using_a_formdata_object
+ */
+ if (requestHeaders['Content-Type'].toLowerCase().includes('multipart/form-data')) {
+ delete requestHeaders['Content-Type'];
+ }
+
+ const response = await window.fetch(
+ `${firmwareToolBaseUrl}${resolveUrl(url, queryParams, pathParams)}`,
+ {
+ signal,
+ method: method.toUpperCase(),
+ body: body
+ ? body instanceof FormData
+ ? body
+ : JSON.stringify(body)
+ : undefined,
+ headers: requestHeaders,
+ }
+ );
+ if (!response.ok) {
+ let error: ErrorWrapper;
+ try {
+ error = await response.json();
+ } catch (e) {
+ error = {
+ status: 'unknown' as const,
+ payload:
+ e instanceof Error ? `Unexpected error (${e.message})` : 'Unexpected error',
+ };
+ }
+
+ throw error;
+ }
+
+ if (response.headers.get('content-type')?.includes('json')) {
+ return await response.json();
+ } else {
+ // if it is not a json response, assume it is a blob and cast it to TData
+ return (await response.blob()) as unknown as TData;
+ }
+ } catch (e) {
+ let errorObject: Error = {
+ name: 'unknown' as const,
+ message: e instanceof Error ? `Network error (${e.message})` : 'Network error',
+ stack: e as string,
+ };
+ throw errorObject;
+ }
+}
+
+const resolveUrl = (
+ url: string,
+ queryParams: Record = {},
+ pathParams: Record = {}
+) => {
+ let query = new URLSearchParams(queryParams).toString();
+ if (query) query = `?${query}`;
+ return url.replace(/\{\w*\}/g, (key) => pathParams[key.slice(1, -1)]) + query;
+};
diff --git a/gui/src/firmware-tool-api/firmwareToolSchemas.ts b/gui/src/firmware-tool-api/firmwareToolSchemas.ts
new file mode 100644
index 0000000000..df109c7214
--- /dev/null
+++ b/gui/src/firmware-tool-api/firmwareToolSchemas.ts
@@ -0,0 +1,608 @@
+/**
+ * Generated by @openapi-codegen
+ *
+ * @version 0.0.1
+ */
+export type VerionCheckResponse = {
+ success: boolean;
+ reason?: {
+ message: string;
+ versions: string;
+ };
+};
+
+/**
+ * Root object declaring a built firmware
+ * this object contains:
+ * - the status of the build
+ * - the the repository and commit used as source
+ */
+export type FirmwareDTO = {
+ /**
+ * UUID of the firmware
+ *
+ * @format uuid
+ */
+ id: string;
+ /**
+ * Id of the firmware version used.
+ * Usually the commit id of the source
+ * used to build the firmware
+ */
+ releaseId: string;
+ /**
+ * Current status of the build
+ * this value will change during the build
+ * process
+ *
+ * BUILDING -> DONE \\ the firmwrare is build and ready
+ * -> FAILED \\ the build failled and will be garbage collected
+ */
+ buildStatus:
+ | 'CREATING_BUILD_FOLDER'
+ | 'DOWNLOADING_FIRMWARE'
+ | 'EXTRACTING_FIRMWARE'
+ | 'SETTING_UP_DEFINES'
+ | 'BUILDING'
+ | 'SAVING'
+ | 'DONE'
+ | 'ERROR';
+ /**
+ * The repository and branch used as source of the firmware
+ */
+ buildVersion: string;
+ /**
+ * The date of creation of this firmware build
+ *
+ * @format date-time
+ */
+ createdAt: string;
+};
+
+export type BuildResponseDTO = {
+ /**
+ * Id of the firmware
+ *
+ * @format uuid
+ */
+ id: string;
+ /**
+ * Build status of the firmware
+ */
+ status:
+ | 'CREATING_BUILD_FOLDER'
+ | 'DOWNLOADING_FIRMWARE'
+ | 'EXTRACTING_FIRMWARE'
+ | 'SETTING_UP_DEFINES'
+ | 'BUILDING'
+ | 'SAVING'
+ | 'DONE'
+ | 'ERROR';
+ /**
+ * List of built firmware files, only set if the build succeeded
+ */
+ firmwareFiles?: FirmwareFileDTO[];
+};
+
+export type FirmwareFileDTO = {
+ /**
+ * Url to the file
+ */
+ url: string;
+ /**
+ * Address of the partition
+ */
+ offset: number;
+ /**
+ * Is this file the main firmware
+ */
+ isFirmware: boolean;
+ /**
+ * Id of the linked firmware
+ *
+ * @format uuid
+ */
+ firmwareId: string;
+};
+
+export type CreateBuildFirmwareDTO = {
+ /**
+ * Repository of the firmware used
+ */
+ version: string;
+ /**
+ * Board config, used to declare the pins used by the board
+ */
+ boardConfig: CreateBoardConfigDTO;
+ /**
+ * Imu config, list of all the imus used and their pins
+ *
+ * @minItems 1
+ */
+ imusConfig: CreateImuConfigDTO[];
+};
+
+export type CreateBoardConfigDTO = {
+ /**
+ * Type of the board
+ */
+ type:
+ | 'BOARD_SLIMEVR'
+ | 'BOARD_NODEMCU'
+ | 'BOARD_WROOM32'
+ | 'BOARD_WEMOSD1MINI'
+ | 'BOARD_TTGO_TBASE'
+ | 'BOARD_ESP01'
+ | 'BOARD_LOLIN_C3_MINI'
+ | 'BOARD_BEETLE32C3'
+ | 'BOARD_ES32C3DEVKITM1';
+ /**
+ * Pin address of the indicator LED
+ */
+ ledPin: string;
+ /**
+ * Is the indicator LED enabled
+ */
+ enableLed: boolean;
+ /**
+ * Is the led inverted
+ */
+ ledInverted: boolean;
+ /**
+ * Pin address of the battery indicator
+ */
+ batteryPin: string;
+ /**
+ * Type of battery
+ */
+ batteryType: 'BAT_EXTERNAL' | 'BAT_INTERNAL' | 'BAT_MCP3021' | 'BAT_INTERNAL_MCP3021';
+ /**
+ * Array of the different battery resistors, [indicator, SHIELD_R1, SHIELD_R2]
+ *
+ * @minItems 3
+ * @maxItems 3
+ */
+ batteryResistances: number[];
+};
+
+export type CreateImuConfigDTO = {
+ /**
+ * Type of the imu
+ */
+ type:
+ | 'IMU_BNO085'
+ | 'IMU_MPU9250'
+ | 'IMU_MPU6500'
+ | 'IMU_BNO080'
+ | 'IMU_BNO055'
+ | 'IMU_BNO086'
+ | 'IMU_MPU6050'
+ | 'IMU_BMI160'
+ | 'IMU_ICM20948'
+ | 'IMU_BMI270';
+ /**
+ * Pin address of the imu int pin
+ * not all imus use it
+ */
+ intPin: string | null;
+ /**
+ * Rotation of the imu in degrees
+ */
+ rotation: number;
+ /**
+ * Pin address of the scl pin
+ */
+ sclPin: string;
+ /**
+ * Pin address of the sda pin
+ */
+ sdaPin: string;
+ /**
+ * Is this imu optionnal
+ * Allows for extensions to be unplugged
+ */
+ optional: boolean;
+};
+
+export type VersionNotFoundExeption = {
+ cause: void;
+ name: string;
+ message: string;
+ stack?: string;
+};
+
+/**
+ * A representation of any set of values over any amount of time. This is the most basic building block
+ * of RxJS.
+ */
+export type ObservableType = {
+ /**
+ * @deprecated true
+ */
+ source?: Observableany;
+ /**
+ * @deprecated true
+ */
+ operator?: OperatoranyType;
+};
+
+/**
+ * A representation of any set of values over any amount of time. This is the most basic building block
+ * of RxJS.
+ */
+export type Observableany = {
+ /**
+ * @deprecated true
+ */
+ source?: Observableany;
+ /**
+ * @deprecated true
+ */
+ operator?: Operatoranyany;
+};
+
+/**
+ * *
+ */
+export type Operatoranyany = {};
+
+/**
+ * *
+ */
+export type OperatoranyType = {};
+
+export type ReleaseDTO = {
+ /**
+ * id of the release, usually the commit id
+ */
+ id: string;
+ /**
+ * url of the release
+ */
+ url: string;
+ /**
+ * name of the release
+ */
+ name: string;
+ /**
+ * url of the source archive
+ */
+ zipball_url: string;
+ /**
+ * Is this release a pre release
+ */
+ prerelease: boolean;
+ /**
+ * Is this release a draft
+ */
+ draft: boolean;
+};
+
+export type Imudto = {
+ /**
+ * Type of the imu
+ */
+ type:
+ | 'IMU_BNO085'
+ | 'IMU_MPU9250'
+ | 'IMU_MPU6500'
+ | 'IMU_BNO080'
+ | 'IMU_BNO055'
+ | 'IMU_BNO086'
+ | 'IMU_MPU6050'
+ | 'IMU_BMI160'
+ | 'IMU_ICM20948'
+ | 'IMU_BMI270';
+ /**
+ * Does that imu type require a int pin
+ */
+ hasIntPin: boolean;
+ /**
+ * First address of the imu
+ */
+ imuStartAddress: number;
+ /**
+ * Increment of the address for each new imus
+ */
+ addressIncrement: number;
+};
+
+export type DefaultBuildConfigDTO = {
+ /**
+ * Default config of the selected board
+ * contains all the default pins information about the selected board
+ */
+ boardConfig: CreateBoardConfigDTO;
+ /**
+ * Inform the flashing utility that the user need to press the boot (or Flash) button
+ * on the tracker
+ */
+ needBootPress?: boolean;
+ /**
+ * Inform the flashing utility that the board will need a reboot after
+ * being flashed
+ */
+ needManualReboot?: boolean;
+ /**
+ * Will use the default values and skip the customisation options
+ */
+ shouldOnlyUseDefaults?: boolean;
+ /**
+ * List of the possible imus pins, usually only two items will be sent
+ *
+ * @minItems 1
+ */
+ imuDefaults: IMUDefaultDTO[];
+ /**
+ * Gives the offset of the firmare file in the eeprom. Used for flashing
+ */
+ application_offset: number;
+};
+
+export type IMUDefaultDTO = {
+ /**
+ * Type of the imu
+ */
+ type?:
+ | 'IMU_BNO085'
+ | 'IMU_MPU9250'
+ | 'IMU_MPU6500'
+ | 'IMU_BNO080'
+ | 'IMU_BNO055'
+ | 'IMU_BNO086'
+ | 'IMU_MPU6050'
+ | 'IMU_BMI160'
+ | 'IMU_ICM20948'
+ | 'IMU_BMI270';
+ /**
+ * Pin address of the imu int pin
+ * not all imus use it
+ */
+ intPin: string | null;
+ /**
+ * Rotation of the imu in degrees
+ */
+ rotation?: number;
+ /**
+ * Pin address of the scl pin
+ */
+ sclPin: string;
+ /**
+ * Pin address of the sda pin
+ */
+ sdaPin: string;
+ /**
+ * Is this imu optionnal
+ * Allows for extensions to be unplugged
+ */
+ optional: boolean;
+};
+
+export type BoardConfigDTONullable = {
+ /**
+ * Unique id of the board config, used for relations
+ *
+ * @format uuid
+ */
+ id: string;
+ /**
+ * Type of the board
+ */
+ type:
+ | 'BOARD_SLIMEVR'
+ | 'BOARD_NODEMCU'
+ | 'BOARD_WROOM32'
+ | 'BOARD_WEMOSD1MINI'
+ | 'BOARD_TTGO_TBASE'
+ | 'BOARD_ESP01'
+ | 'BOARD_LOLIN_C3_MINI'
+ | 'BOARD_BEETLE32C3'
+ | 'BOARD_ES32C3DEVKITM1';
+ /**
+ * Pin address of the indicator LED
+ */
+ ledPin: string;
+ /**
+ * Is the indicator LED enabled
+ */
+ enableLed: boolean;
+ /**
+ * Is the led inverted
+ */
+ ledInverted: boolean;
+ /**
+ * Pin address of the battery indicator
+ */
+ batteryPin: string;
+ /**
+ * Type of battery
+ */
+ batteryType: 'BAT_EXTERNAL' | 'BAT_INTERNAL' | 'BAT_MCP3021' | 'BAT_INTERNAL_MCP3021';
+ /**
+ * Array of the different battery resistors, [indicator, SHIELD_R1, SHIELD_R2]
+ *
+ * @minItems 3
+ * @maxItems 3
+ */
+ batteryResistances: number[];
+ /**
+ * Id of the linked firmware, used for relations
+ *
+ * @format uuid
+ */
+ firmwareId: string;
+};
+
+export type FirmwareDetailDTO = {
+ /**
+ * Pins informations about the board
+ */
+ boardConfig: BoardConfigDTONullable;
+ /**
+ * List of the declared imus, and their pin configuration
+ *
+ * @minItems 1
+ */
+ imusConfig: ImuConfigDTO[];
+ /**
+ * List of the built files / partitions with their url and offsets
+ */
+ firmwareFiles: FirmwareFileDTO[];
+ /**
+ * UUID of the firmware
+ *
+ * @format uuid
+ */
+ id: string;
+ /**
+ * Id of the firmware version used.
+ * Usually the commit id of the source
+ * used to build the firmware
+ */
+ releaseId: string;
+ /**
+ * Current status of the build
+ * this value will change during the build
+ * process
+ *
+ * BUILDING -> DONE \\ the firmwrare is build and ready
+ * -> FAILED \\ the build failled and will be garbage collected
+ */
+ buildStatus:
+ | 'CREATING_BUILD_FOLDER'
+ | 'DOWNLOADING_FIRMWARE'
+ | 'EXTRACTING_FIRMWARE'
+ | 'SETTING_UP_DEFINES'
+ | 'BUILDING'
+ | 'SAVING'
+ | 'DONE'
+ | 'ERROR';
+ /**
+ * The repository and branch used as source of the firmware
+ */
+ buildVersion: string;
+ /**
+ * The date of creation of this firmware build
+ *
+ * @format date-time
+ */
+ createdAt: string;
+};
+
+export type BoardConfigDTO = {
+ /**
+ * Unique id of the board config, used for relations
+ *
+ * @format uuid
+ */
+ id: string;
+ /**
+ * Type of the board
+ */
+ type:
+ | 'BOARD_SLIMEVR'
+ | 'BOARD_NODEMCU'
+ | 'BOARD_WROOM32'
+ | 'BOARD_WEMOSD1MINI'
+ | 'BOARD_TTGO_TBASE'
+ | 'BOARD_ESP01'
+ | 'BOARD_LOLIN_C3_MINI'
+ | 'BOARD_BEETLE32C3'
+ | 'BOARD_ES32C3DEVKITM1';
+ /**
+ * Pin address of the indicator LED
+ */
+ ledPin: string;
+ /**
+ * Is the indicator LED enabled
+ */
+ enableLed: boolean;
+ /**
+ * Is the led inverted
+ */
+ ledInverted: boolean;
+ /**
+ * Pin address of the battery indicator
+ */
+ batteryPin: string;
+ /**
+ * Type of battery
+ */
+ batteryType: 'BAT_EXTERNAL' | 'BAT_INTERNAL' | 'BAT_MCP3021' | 'BAT_INTERNAL_MCP3021';
+ /**
+ * Array of the different battery resistors, [indicator, SHIELD_R1, SHIELD_R2]
+ *
+ * @minItems 3
+ * @maxItems 3
+ */
+ batteryResistances: number[];
+ /**
+ * Id of the linked firmware, used for relations
+ *
+ * @format uuid
+ */
+ firmwareId: string;
+};
+
+export type ImuConfigDTO = {
+ /**
+ * Unique id of the config
+ * this probably will never be shown to the user as it is moslty use for relations
+ *
+ * @format uuid
+ */
+ id: string;
+ /**
+ * Type of the imu
+ */
+ type:
+ | 'IMU_BNO085'
+ | 'IMU_MPU9250'
+ | 'IMU_MPU6500'
+ | 'IMU_BNO080'
+ | 'IMU_BNO055'
+ | 'IMU_BNO086'
+ | 'IMU_MPU6050'
+ | 'IMU_BMI160'
+ | 'IMU_ICM20948'
+ | 'IMU_BMI270';
+ /**
+ * Rotation of the imu in degrees
+ */
+ rotation: number;
+ /**
+ * Pin address of the imu int pin
+ * not all imus use it
+ */
+ intPin: string | null;
+ /**
+ * Pin address of the scl pin
+ */
+ sclPin: string;
+ /**
+ * Pin address of the sda pin
+ */
+ sdaPin: string;
+ /**
+ * Is this imu optionnal
+ * Allows for extensions to be unplugged
+ */
+ optional: boolean;
+ /**
+ * id of the linked firmware, used for relations
+ *
+ * @format uuid
+ */
+ firmwareId: string;
+};
+
+/**
+ * Defines the base Nest HTTP exception, which is handled by the default
+ * Exceptions Handler.
+ */
+export type HttpException = {
+ cause: void;
+ name: string;
+ message: string;
+ stack?: string;
+};
diff --git a/gui/src/firmware-tool-api/firmwareToolUtils.ts b/gui/src/firmware-tool-api/firmwareToolUtils.ts
new file mode 100644
index 0000000000..0b4d2f86df
--- /dev/null
+++ b/gui/src/firmware-tool-api/firmwareToolUtils.ts
@@ -0,0 +1,15 @@
+type ComputeRange<
+ N extends number,
+ Result extends Array = [],
+> = Result['length'] extends N
+ ? Result
+ : ComputeRange;
+
+export type ClientErrorStatus = Exclude<
+ ComputeRange<500>[number],
+ ComputeRange<400>[number]
+>;
+export type ServerErrorStatus = Exclude<
+ ComputeRange<600>[number],
+ ComputeRange<500>[number]
+>;
diff --git a/gui/src/hooks/breakpoint.ts b/gui/src/hooks/breakpoint.ts
index bb70683760..0bd522ea24 100644
--- a/gui/src/hooks/breakpoint.ts
+++ b/gui/src/hooks/breakpoint.ts
@@ -3,9 +3,8 @@ import { useMediaQuery } from 'react-responsive';
import tailwindConfig from '../../tailwind.config';
const fullConfig = resolveConfig(tailwindConfig as any);
-const breakpoints = tailwindConfig.theme.screens;
-type BreakpointKey = keyof typeof breakpoints;
+type BreakpointKey = keyof typeof tailwindConfig.theme.screens;
export function useBreakpoint(breakpointKey: K) {
// FIXME There is a flickering issue caused by this, because isMobile is not resolved fast enough
diff --git a/gui/src/hooks/firmware-tool.ts b/gui/src/hooks/firmware-tool.ts
new file mode 100644
index 0000000000..638646105d
--- /dev/null
+++ b/gui/src/hooks/firmware-tool.ts
@@ -0,0 +1,179 @@
+import { createContext, useContext, useState } from 'react';
+import {
+ fetchGetFirmwaresDefaultConfigBoard,
+ useGetHealth,
+ useGetIsCompatibleVersion,
+} from '@/firmware-tool-api/firmwareToolComponents';
+import {
+ BuildResponseDTO,
+ CreateBoardConfigDTO,
+ CreateBuildFirmwareDTO,
+ DefaultBuildConfigDTO,
+} from '@/firmware-tool-api/firmwareToolSchemas';
+import { BoardPinsForm } from '@/components/firmware-tool/BoardPinsStep';
+import { DeepPartial } from 'react-hook-form';
+import {
+ BoardType,
+ FirmwareUpdateMethod,
+ FirmwareUpdateStatus,
+} from 'solarxr-protocol';
+
+export type PartialBuildFirmware = DeepPartial;
+export type FirmwareBuildStatus = BuildResponseDTO;
+export type SelectedDevice = {
+ type: FirmwareUpdateMethod;
+ deviceId: string | number;
+ deviceNames: string[];
+};
+
+export const boardTypeToFirmwareToolBoardType: Record<
+ Exclude<
+ BoardType,
+ // This boards will not be handled by the firmware tool.
+ // These are either impossible to compile automatically or deprecated
+ BoardType.CUSTOM | BoardType.SLIMEVR_DEV | BoardType.SLIMEVR_LEGACY
+ >,
+ CreateBoardConfigDTO['type'] | null
+> = {
+ [BoardType.UNKNOWN]: null,
+ [BoardType.NODEMCU]: 'BOARD_NODEMCU',
+ [BoardType.WROOM32]: 'BOARD_WROOM32',
+ [BoardType.WEMOSD1MINI]: 'BOARD_WEMOSD1MINI',
+ [BoardType.TTGO_TBASE]: 'BOARD_TTGO_TBASE',
+ [BoardType.ESP01]: 'BOARD_ESP01',
+ [BoardType.SLIMEVR]: 'BOARD_SLIMEVR',
+ [BoardType.LOLIN_C3_MINI]: 'BOARD_LOLIN_C3_MINI',
+ [BoardType.BEETLE32C3]: 'BOARD_BEETLE32C3',
+ [BoardType.ES32C3DEVKITM1]: 'BOARD_ES32C3DEVKITM1',
+};
+
+export const firmwareToolToBoardType: Record =
+ Object.fromEntries(
+ Object.entries(boardTypeToFirmwareToolBoardType).map((a) => a.reverse())
+ );
+
+export const firmwareUpdateErrorStatus = [
+ FirmwareUpdateStatus.ERROR_AUTHENTICATION_FAILED,
+ FirmwareUpdateStatus.ERROR_DEVICE_NOT_FOUND,
+ FirmwareUpdateStatus.ERROR_DOWNLOAD_FAILED,
+ FirmwareUpdateStatus.ERROR_PROVISIONING_FAILED,
+ FirmwareUpdateStatus.ERROR_TIMEOUT,
+ FirmwareUpdateStatus.ERROR_UNKNOWN,
+ FirmwareUpdateStatus.ERROR_UNSUPPORTED_METHOD,
+ FirmwareUpdateStatus.ERROR_UPLOAD_FAILED,
+];
+
+export interface FirmwareToolContext {
+ selectBoard: (boardType: CreateBoardConfigDTO['type']) => Promise;
+ selectVersion: (version: CreateBuildFirmwareDTO['version']) => void;
+ updatePins: (form: BoardPinsForm) => void;
+ updateImus: (imus: CreateBuildFirmwareDTO['imusConfig']) => void;
+ setBuildStatus: (buildStatus: FirmwareBuildStatus) => void;
+ selectDevices: (device: SelectedDevice[] | null) => void;
+ retry: () => void;
+ buildStatus: FirmwareBuildStatus;
+ defaultConfig: DefaultBuildConfigDTO | null;
+ newConfig: PartialBuildFirmware | null;
+ selectedDevices: SelectedDevice[] | null;
+ isStepLoading: boolean;
+ isGlobalLoading: boolean;
+ isCompatible: boolean;
+ isError: boolean;
+}
+
+export const FirmwareToolContextC = createContext(
+ undefined as any
+);
+
+export function useFirmwareTool() {
+ const context = useContext(FirmwareToolContextC);
+ if (!context) {
+ throw new Error('useFirmwareTool must be within a FirmwareToolContext Provider');
+ }
+ return context;
+}
+
+export function useFirmwareToolContext(): FirmwareToolContext {
+ const [defaultConfig, setDefaultConfig] = useState(
+ null
+ );
+ const [selectedDevices, selectDevices] = useState(null);
+ const [newConfig, setNewConfig] = useState({});
+ const [isLoading, setLoading] = useState(false);
+ const { isError, isLoading: isInitialLoading, refetch } = useGetHealth({});
+ const compatibilityCheckEnabled = !!__VERSION_TAG__ || __VERSION_TAG__ !== '';
+ const { isLoading: isCompatibilityLoading, data: compatibilityData } =
+ useGetIsCompatibleVersion(
+ { pathParams: { version: __VERSION_TAG__ } },
+ { enabled: compatibilityCheckEnabled }
+ );
+ const [buildStatus, setBuildStatus] = useState({
+ status: 'CREATING_BUILD_FOLDER',
+ id: '',
+ });
+
+ return {
+ selectBoard: async (boardType: CreateBoardConfigDTO['type']) => {
+ setLoading(true);
+ const boardDefaults = await fetchGetFirmwaresDefaultConfigBoard({
+ pathParams: { board: boardType },
+ });
+ setDefaultConfig(boardDefaults);
+ if (boardDefaults.shouldOnlyUseDefaults) {
+ setNewConfig((currConfig) => ({
+ ...currConfig,
+ ...boardDefaults,
+ imusConfig: boardDefaults.imuDefaults,
+ }));
+ } else {
+ setNewConfig((currConfig) => ({
+ ...currConfig,
+ boardConfig: { ...currConfig.boardConfig, type: boardType },
+ imusConfig: [],
+ }));
+ }
+ setLoading(false);
+ },
+ updatePins: (form: BoardPinsForm) => {
+ setNewConfig((currConfig) => {
+ return {
+ ...currConfig,
+ imusConfig: [...(currConfig?.imusConfig || [])],
+ boardConfig: {
+ ...currConfig.boardConfig,
+ ...form,
+ },
+ };
+ });
+ },
+ updateImus: (imus: CreateBuildFirmwareDTO['imusConfig']) => {
+ setNewConfig((currConfig) => {
+ return {
+ ...currConfig,
+ imusConfig: imus.map(({ rotation, ...fields }) => ({
+ ...fields,
+ rotation: Number(rotation),
+ })), // Make sure that the rotation is handled as number
+ };
+ });
+ },
+ retry: async () => {
+ setLoading(true);
+ await refetch();
+ setLoading(false);
+ },
+ selectVersion: (version: CreateBuildFirmwareDTO['version']) => {
+ setNewConfig((currConfig) => ({ ...currConfig, version }));
+ },
+ setBuildStatus,
+ selectDevices,
+ selectedDevices,
+ buildStatus,
+ defaultConfig,
+ newConfig,
+ isStepLoading: isLoading,
+ isGlobalLoading: isInitialLoading || isCompatibilityLoading,
+ isCompatible: !compatibilityCheckEnabled || (compatibilityData?.success ?? false),
+ isError: isError || (!compatibilityData?.success && compatibilityCheckEnabled),
+ };
+}
diff --git a/gui/src/hooks/onboarding.ts b/gui/src/hooks/onboarding.ts
index 74f6ff7806..0ee82895fd 100644
--- a/gui/src/hooks/onboarding.ts
+++ b/gui/src/hooks/onboarding.ts
@@ -16,7 +16,7 @@ interface OnboardingState {
export interface OnboardingContext {
state: OnboardingState;
applyProgress: (value: number) => void;
- setWifiCredentials: (ssid: string, password: string) => void;
+ setWifiCredentials: (ssid: string, password?: string) => void;
skipSetup: () => void;
}
@@ -68,8 +68,8 @@ export function useProvideOnboarding(): OnboardingContext {
dispatch({ type: 'progress', value });
}, []);
},
- setWifiCredentials: (ssid: string, password: string) => {
- dispatch({ type: 'wifi-creds', ssid, password });
+ setWifiCredentials: (ssid: string, password?: string) => {
+ dispatch({ type: 'wifi-creds', ssid, password: password ?? '' });
},
skipSetup: () => {
setConfig({ doneOnboarding: true });
diff --git a/gui/src/hooks/tracker.ts b/gui/src/hooks/tracker.ts
index 530fc6bc07..93488cb316 100644
--- a/gui/src/hooks/tracker.ts
+++ b/gui/src/hooks/tracker.ts
@@ -1,8 +1,8 @@
import { useEffect, useMemo, useRef, useState } from 'react';
-import { BodyPart, TrackerDataT, TrackerStatus } from 'solarxr-protocol';
+import { BodyPart, TrackerDataT, TrackerInfoT, TrackerStatus } from 'solarxr-protocol';
import { QuaternionFromQuatT, QuaternionToEulerDegrees } from '@/maths/quaternion';
import { useAppContext } from './app';
-import { useLocalization } from '@fluent/react';
+import { ReactLocalization, useLocalization } from '@fluent/react';
import { useDataFeedConfig } from './datafeed-config';
import { Quaternion, Vector3 } from 'three';
import { Vector3FromVec3fT } from '@/maths/vector3';
@@ -36,18 +36,18 @@ export function useTrackers() {
};
}
+export function getTrackerName(l10n: ReactLocalization, info: TrackerInfoT | null) {
+ if (info?.customName) return info?.customName;
+ if (info?.bodyPart) return l10n.getString('body_part-' + BodyPart[info?.bodyPart]);
+ return info?.displayName || 'NONE';
+}
+
export function useTracker(tracker: TrackerDataT) {
const { l10n } = useLocalization();
const { feedMaxTps } = useDataFeedConfig();
return {
- useName: () =>
- useMemo(() => {
- if (tracker.info?.customName) return tracker.info?.customName;
- if (tracker.info?.bodyPart)
- return l10n.getString('body_part-' + BodyPart[tracker.info?.bodyPart]);
- return tracker.info?.displayName || 'NONE';
- }, [tracker.info]),
+ useName: () => useMemo(() => getTrackerName(l10n, tracker.info), [tracker.info]),
useRawRotationEulerDegrees: () =>
useMemo(() => QuaternionToEulerDegrees(tracker?.rotation), [tracker.rotation]),
useRefAdjRotationEulerDegrees: () =>
diff --git a/gui/src/index.scss b/gui/src/index.scss
index 354e141bdc..8ed7c77c73 100644
--- a/gui/src/index.scss
+++ b/gui/src/index.scss
@@ -84,7 +84,7 @@ body {
}
:root {
- overflow: hidden;
+ // overflow: hidden; -- NEVER EVER BRING THIS BACK <3
background: theme('colors.background.20');
--navbar-w: 101px;
diff --git a/gui/tailwind.config.ts b/gui/tailwind.config.ts
index d612158bf7..5753f72134 100644
--- a/gui/tailwind.config.ts
+++ b/gui/tailwind.config.ts
@@ -162,9 +162,11 @@ const config = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
theme: {
screens: {
+ 'mobile-settings': { raw: 'not (min-width: 900px)' },
nsmol: { raw: 'not (min-width: 525px)' },
smol: '525px',
mobile: { raw: 'not (min-width: 800px)' },
+ 'xs-settings': '900px',
xs: '800px',
nsm: { raw: 'not (min-width: 900px)' },
sm: '900px',
diff --git a/gui/vite.config.ts b/gui/vite.config.ts
index c9eb27becc..0d17119671 100644
--- a/gui/vite.config.ts
+++ b/gui/vite.config.ts
@@ -21,7 +21,7 @@ export function i18nHotReload(): PluginOption {
handleHotUpdate({ file, server }) {
if (file.endsWith('.ftl')) {
console.log('Fluent files updated');
- server.ws.send({
+ server.hot.send({
type: 'custom',
event: 'locales-update',
});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 91e8dc5e79..41c4f11312 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -29,12 +29,18 @@ importers:
'@formatjs/intl-localematcher':
specifier: ^0.2.32
version: 0.2.32
+ '@hookform/resolvers':
+ specifier: ^3.6.0
+ version: 3.6.0(react-hook-form@7.53.0(react@18.3.1))
'@react-three/drei':
specifier: ^9.114.3
- version: 9.114.3(@react-three/fiber@8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(@types/react@18.3.11)(@types/three@0.163.0)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0)
+ version: 9.114.5(@react-three/fiber@8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(@types/react@18.3.11)(@types/three@0.163.0)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0)
'@react-three/fiber':
specifier: ^8.17.10
version: 8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0)
+ '@tanstack/react-query':
+ specifier: ^5.48.0
+ version: 5.48.0(react@18.3.1)
'@tauri-apps/api':
specifier: ^2.0.2
version: 2.0.2
@@ -94,35 +100,44 @@ importers:
version: 10.0.0(react@18.3.1)
react-router-dom:
specifier: ^6.26.2
- version: 6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 6.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
semver:
specifier: ^7.6.3
version: 7.6.3
solarxr-protocol:
specifier: file:../solarxr-protocol
- version: file:solarxr-protocol
+ version: link:../solarxr-protocol
three:
specifier: ^0.163.0
version: 0.163.0
ts-pattern:
specifier: ^5.4.0
- version: 5.4.0
+ version: 5.5.0
typescript:
specifier: ^5.6.3
version: 5.6.3
use-double-tap:
specifier: ^1.3.6
version: 1.3.6(react@18.3.1)
+ yup:
+ specifier: ^1.4.0
+ version: 1.4.0
devDependencies:
'@dword-design/eslint-plugin-import-alias':
specifier: ^4.0.9
version: 4.0.9
+ '@openapi-codegen/cli':
+ specifier: ^2.0.2
+ version: 2.0.2(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@openapi-codegen/typescript':
+ specifier: ^8.0.2
+ version: 8.0.2
'@tailwindcss/forms':
specifier: ^0.5.9
- version: 0.5.9(tailwindcss@3.4.13(ts-node@9.1.1(typescript@5.6.3)))
+ version: 0.5.9(tailwindcss@3.4.14(ts-node@9.1.1(typescript@5.6.3)))
'@tauri-apps/cli':
specifier: ^2.0.2
- version: 2.0.2
+ version: 2.0.3
'@types/file-saver':
specifier: ^2.0.7
version: 2.0.7
@@ -152,13 +167,16 @@ importers:
version: 7.18.0(eslint@8.57.1)(typescript@5.6.3)
'@vitejs/plugin-react':
specifier: ^4.3.2
- version: 4.3.2(vite@5.4.8(@types/node@20.14.2)(sass@1.79.4)(terser@5.31.1))
+ version: 4.3.2(vite@5.4.9(@types/node@20.14.2)(sass@1.80.2)(terser@5.31.1))
autoprefixer:
specifier: ^10.4.20
- version: 10.4.20(postcss@8.4.47)
+ version: 10.4.20(postcss@8.4.38)
cross-env:
specifier: ^7.0.3
version: 7.0.3
+ dotenv:
+ specifier: ^16.4.5
+ version: 16.4.5
eslint:
specifier: ^8.57.1
version: 8.57.1
@@ -180,6 +198,9 @@ importers:
eslint-plugin-react-hooks:
specifier: ^4.6.2
version: 4.6.2(eslint@8.57.1)
+ globals:
+ specifier: ^15.10.0
+ version: 15.10.0
prettier:
specifier: ^3.3.3
version: 3.3.3
@@ -188,7 +209,7 @@ importers:
version: 5.12.0(rollup@4.24.0)
sass:
specifier: ^1.79.4
- version: 1.79.4
+ version: 1.80.2
spdx-satisfies:
specifier: ^5.0.1
version: 5.0.1
@@ -197,16 +218,19 @@ importers:
version: 1.2.0
tailwindcss:
specifier: ^3.4.13
- version: 3.4.13(ts-node@9.1.1(typescript@5.6.3))
+ version: 3.4.14(ts-node@9.1.1(typescript@5.6.3))
+ typescript-eslint:
+ specifier: ^8.8.0
+ version: 8.8.0(eslint@8.57.1)(typescript@5.6.3)
vite:
specifier: ^5.4.8
- version: 5.4.8(@types/node@20.14.2)(sass@1.79.4)(terser@5.31.1)
+ version: 5.4.9(@types/node@20.14.2)(sass@1.80.2)(terser@5.31.1)
solarxr-protocol:
dependencies:
flatbuffers:
specifier: ^22.10.26
- version: 22.12.6
+ version: 22.10.26
devDependencies:
'@mgit-at/typescript-flatbuffers-codegen':
specifier: ^0.1.3
@@ -225,95 +249,200 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
+ '@apollo/client@3.10.6':
+ resolution: {integrity: sha512-3lLFGJtzC1/mEnK11BRf+Bf8536kBQUSB1G9yMtcRsxmY+tCKdTPzsP3fMUKy10BPIE0sDUY1pux3iMPIn2vow==}
+ peerDependencies:
+ graphql: ^15.0.0 || ^16.0.0
+ graphql-ws: ^5.5.5
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+ subscriptions-transport-ws: ^0.9.0 || ^0.11.0
+ peerDependenciesMeta:
+ graphql-ws:
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ subscriptions-transport-ws:
+ optional: true
+
+ '@babel/code-frame@7.24.7':
+ resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
+ engines: {node: '>=6.9.0'}
+
'@babel/code-frame@7.25.7':
resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==}
engines: {node: '>=6.9.0'}
- '@babel/compat-data@7.25.7':
- resolution: {integrity: sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==}
+ '@babel/compat-data@7.24.7':
+ resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.25.8':
+ resolution: {integrity: sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.24.7':
+ resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.25.8':
+ resolution: {integrity: sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==}
engines: {node: '>=6.9.0'}
- '@babel/core@7.25.7':
- resolution: {integrity: sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==}
+ '@babel/generator@7.24.7':
+ resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==}
engines: {node: '>=6.9.0'}
'@babel/generator@7.25.7':
resolution: {integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==}
engines: {node: '>=6.9.0'}
+ '@babel/helper-compilation-targets@7.24.7':
+ resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helper-compilation-targets@7.25.7':
resolution: {integrity: sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==}
engines: {node: '>=6.9.0'}
+ '@babel/helper-environment-visitor@7.24.7':
+ resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-function-name@7.24.7':
+ resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-hoist-variables@7.24.7':
+ resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.24.7':
+ resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helper-module-imports@7.25.7':
resolution: {integrity: sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==}
engines: {node: '>=6.9.0'}
+ '@babel/helper-module-transforms@7.24.7':
+ resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
'@babel/helper-module-transforms@7.25.7':
resolution: {integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/helper-plugin-utils@7.25.7':
- resolution: {integrity: sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==}
+ '@babel/helper-plugin-utils@7.24.7':
+ resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-simple-access@7.24.7':
+ resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==}
engines: {node: '>=6.9.0'}
'@babel/helper-simple-access@7.25.7':
resolution: {integrity: sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==}
engines: {node: '>=6.9.0'}
+ '@babel/helper-split-export-declaration@7.24.7':
+ resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.24.7':
+ resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helper-string-parser@7.25.7':
resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==}
engines: {node: '>=6.9.0'}
+ '@babel/helper-validator-identifier@7.24.7':
+ resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helper-validator-identifier@7.25.7':
resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==}
engines: {node: '>=6.9.0'}
+ '@babel/helper-validator-option@7.24.7':
+ resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helper-validator-option@7.25.7':
resolution: {integrity: sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==}
engines: {node: '>=6.9.0'}
+ '@babel/helpers@7.24.7':
+ resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helpers@7.25.7':
resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==}
engines: {node: '>=6.9.0'}
+ '@babel/highlight@7.24.7':
+ resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==}
+ engines: {node: '>=6.9.0'}
+
'@babel/highlight@7.25.7':
resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==}
engines: {node: '>=6.9.0'}
- '@babel/parser@7.25.7':
- resolution: {integrity: sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==}
+ '@babel/parser@7.24.7':
+ resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/parser@7.25.8':
+ resolution: {integrity: sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==}
engines: {node: '>=6.0.0'}
hasBin: true
- '@babel/plugin-transform-react-jsx-self@7.25.7':
- resolution: {integrity: sha512-JD9MUnLbPL0WdVK8AWC7F7tTG2OS6u/AKKnsK+NdRhUiVdnzyR1S3kKQCaRLOiaULvUiqK6Z4JQE635VgtCFeg==}
+ '@babel/plugin-transform-react-jsx-self@7.24.7':
+ resolution: {integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-react-jsx-source@7.25.7':
- resolution: {integrity: sha512-S/JXG/KrbIY06iyJPKfxr0qRxnhNOdkNXYBl/rmwgDd72cQLH9tEGkDm/yJPGvcSIUoikzfjMios9i+xT/uv9w==}
+ '@babel/plugin-transform-react-jsx-source@7.24.7':
+ resolution: {integrity: sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/runtime@7.25.7':
- resolution: {integrity: sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==}
+ '@babel/runtime@7.24.7':
+ resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/template@7.24.7':
+ resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==}
engines: {node: '>=6.9.0'}
'@babel/template@7.25.7':
resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==}
engines: {node: '>=6.9.0'}
+ '@babel/traverse@7.24.7':
+ resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==}
+ engines: {node: '>=6.9.0'}
+
'@babel/traverse@7.25.7':
resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==}
engines: {node: '>=6.9.0'}
- '@babel/types@7.25.7':
- resolution: {integrity: sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==}
+ '@babel/types@7.24.7':
+ resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.25.8':
+ resolution: {integrity: sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==}
engines: {node: '>=6.9.0'}
'@dword-design/dedent@0.7.0':
@@ -474,8 +603,8 @@ packages:
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
- '@eslint-community/regexpp@4.11.1':
- resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==}
+ '@eslint-community/regexpp@4.10.1':
+ resolution: {integrity: sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
'@eslint/eslintrc@2.1.4':
@@ -486,6 +615,9 @@ packages:
resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ '@exodus/schemasafe@1.3.0':
+ resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==}
+
'@fluent/bundle@0.18.0':
resolution: {integrity: sha512-8Wfwu9q8F9g2FNnv82g6Ch/E1AW1wwljsUOolH5NEtdJdv0sZTuWvfCM7c3teB9dzNaJA8rn4khpidpozHWYEA==}
engines: {node: '>=14.0.0', npm: '>=7.0.0'}
@@ -509,6 +641,16 @@ packages:
'@formatjs/intl-localematcher@0.2.32':
resolution: {integrity: sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==}
+ '@graphql-typed-document-node/core@3.2.0':
+ resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==}
+ peerDependencies:
+ graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+
+ '@hookform/resolvers@3.6.0':
+ resolution: {integrity: sha512-UBcpyOX3+RR+dNnqBd0lchXpoL8p4xC21XP8H6Meb8uve5Br1GCnmg0PcBoKKqPKgGu9GHQ/oygcmPrQhetwqw==}
+ peerDependencies:
+ react-hook-form: ^7.0.0
+
'@humanwhocodes/config-array@0.13.0':
resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
engines: {node: '>=10.10.0'}
@@ -541,8 +683,8 @@ packages:
'@jridgewell/source-map@0.3.6':
resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
- '@jridgewell/sourcemap-codec@1.5.0':
- resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+ '@jridgewell/sourcemap-codec@1.4.15':
+ resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
@@ -554,8 +696,8 @@ packages:
resolution: {integrity: sha512-sf9vaoiR/SR0dpV568GhsoLbd6659StJ4Gl9jszZL/bsJJaF5VmLYbI57OSI4JDm+L6d3osVMl9mkchox9j6/g==}
hasBin: true
- '@monogrid/gainmap-js@3.0.6':
- resolution: {integrity: sha512-ireqJg7cw0tUn/JePDG8rAL7RyXgUKSDbjYdiygkrnye1WuKGLAWDBwF/ICwCwJ9iZBAF5caU8gSu+c34HLGdQ==}
+ '@monogrid/gainmap-js@3.0.5':
+ resolution: {integrity: sha512-53sCTG4FaJBaAq/tcufARtVYDMDGqyBT9i7F453pWGhZ5LqubDHDWtYoHo9VhQqMcHTEexdJqSsR58y+9HVmQA==}
peerDependencies:
three: '>= 0.159.0'
@@ -575,6 +717,89 @@ packages:
resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==}
engines: {node: '>=12.4.0'}
+ '@openapi-codegen/cli@2.0.2':
+ resolution: {integrity: sha512-uBk6yOBSBIgGWA2ok/IjBS03UwVAIpnan0lKz2sk3tsSe8rVIjOnQPxGYvSuByfxzdIu+nrPom2meqtcjlMvDQ==}
+ hasBin: true
+
+ '@openapi-codegen/typescript@8.0.2':
+ resolution: {integrity: sha512-7X9WR+qlIMcMxiBgheGzyQcChLSPVqNYf9SAFJdTOJQLWfy+gaXiDonUC8WC7p6Hpz7eM6OLU1i7f/h+2RlH1w==}
+
+ '@parcel/watcher-android-arm64@2.4.1':
+ resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ '@parcel/watcher-darwin-arm64@2.4.1':
+ resolution: {integrity: sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@parcel/watcher-darwin-x64@2.4.1':
+ resolution: {integrity: sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@parcel/watcher-freebsd-x64@2.4.1':
+ resolution: {integrity: sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@parcel/watcher-linux-arm-glibc@2.4.1':
+ resolution: {integrity: sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-glibc@2.4.1':
+ resolution: {integrity: sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-musl@2.4.1':
+ resolution: {integrity: sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-glibc@2.4.1':
+ resolution: {integrity: sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-musl@2.4.1':
+ resolution: {integrity: sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-win32-arm64@2.4.1':
+ resolution: {integrity: sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@parcel/watcher-win32-ia32@2.4.1':
+ resolution: {integrity: sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@parcel/watcher-win32-x64@2.4.1':
+ resolution: {integrity: sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ '@parcel/watcher@2.4.1':
+ resolution: {integrity: sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==}
+ engines: {node: '>= 10.0.0'}
+
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@@ -607,8 +832,8 @@ packages:
'@react-spring/types@9.6.1':
resolution: {integrity: sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==}
- '@react-three/drei@9.114.3':
- resolution: {integrity: sha512-hPKPYmxTb2P1mOdhkouJbKJVcfFK5JmThr/97i4zkweoNzWBHNde090A6r0SFFb4tGaTtHM4/kyfVx5PrzjTMw==}
+ '@react-three/drei@9.114.5':
+ resolution: {integrity: sha512-nXD/wOwQVaaKF1WXG5Ah3ief+Mojm5YInlk91tanzEYdG+5Vhno34AFn3xt0XKMAaHA+Lkjfi+BpqnVama+JPA==}
peerDependencies:
'@react-three/fiber': '>=8.0'
react: '>=18.0'
@@ -643,8 +868,8 @@ packages:
react-native:
optional: true
- '@remix-run/router@1.19.2':
- resolution: {integrity: sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==}
+ '@remix-run/router@1.20.0':
+ resolution: {integrity: sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==}
engines: {node: '>=14.0.0'}
'@rollup/rollup-android-arm-eabi@4.24.0':
@@ -730,76 +955,167 @@ packages:
'@rtsao/scc@1.1.0':
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
+ '@sindresorhus/is@5.6.0':
+ resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==}
+ engines: {node: '>=14.16'}
+
+ '@swc/core-darwin-arm64@1.6.5':
+ resolution: {integrity: sha512-RGQhMdni2v1/ANQ/2K+F+QYdzaucekYBewZcX1ogqJ8G5sbPaBdYdDN1qQ4kHLCIkPtGP6qC7c71qPEqL2RidQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@swc/core-darwin-x64@1.6.5':
+ resolution: {integrity: sha512-/pSN0/Jtcbbb9+ovS9rKxR3qertpFAM3OEJr/+Dh/8yy7jK5G5EFPIrfsw/7Q5987ERPIJIH6BspK2CBB2tgcg==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@swc/core-linux-arm-gnueabihf@1.6.5':
+ resolution: {integrity: sha512-B0g/dROCE747RRegs/jPHuKJgwXLracDhnqQa80kFdgWEMjlcb7OMCgs5OX86yJGRS4qcYbiMGD0Pp7Kbqn3yw==}
+ engines: {node: '>=10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@swc/core-linux-arm64-gnu@1.6.5':
+ resolution: {integrity: sha512-W8meapgXTq8AOtSvDG4yKR8ant2WWD++yOjgzAleB5VAC+oC+aa8YJROGxj8HepurU8kurqzcialwoMeq5SZZQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@swc/core-linux-arm64-musl@1.6.5':
+ resolution: {integrity: sha512-jyCKqoX50Fg8rJUQqh4u5PqnE7nqYKXHjVH2WcYr114/MU21zlsI+YL6aOQU1XP8bJQ2gPQ1rnlnGJdEHiKS/w==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@swc/core-linux-x64-gnu@1.6.5':
+ resolution: {integrity: sha512-G6HmUn/RRIlXC0YYFfBz2qh6OZkHS/KUPkhoG4X9ADcgWXXjOFh6JrefwsYj8VBAJEnr5iewzjNfj+nztwHaeA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@swc/core-linux-x64-musl@1.6.5':
+ resolution: {integrity: sha512-AQpBjBnelQDSbeTJA50AXdS6+CP66LsXIMNTwhPSgUfE7Bx1ggZV11Fsi4Q5SGcs6a8Qw1cuYKN57ZfZC5QOuA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@swc/core-win32-arm64-msvc@1.6.5':
+ resolution: {integrity: sha512-MZTWM8kUwS30pVrtbzSGEXtek46aXNb/mT9D6rsS7NvOuv2w+qZhjR1rzf4LNbbn5f8VnR4Nac1WIOYZmfC5ng==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@swc/core-win32-ia32-msvc@1.6.5':
+ resolution: {integrity: sha512-WZdu4gISAr3yOm1fVwKhhk6+MrP7kVX0KMP7+ZQFTN5zXQEiDSDunEJKVgjMVj3vlR+6mnAqa/L0V9Qa8+zKlQ==}
+ engines: {node: '>=10'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@swc/core-win32-x64-msvc@1.6.5':
+ resolution: {integrity: sha512-ezXgucnMTzlFIxQZw7ls/5r2hseFaRoDL04cuXUOs97E8r+nJSmFsRQm/ygH5jBeXNo59nyZCalrjJAjwfgACA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@swc/core@1.6.5':
+ resolution: {integrity: sha512-tyVvUK/HDOUUsK6/GmWvnqUtD9oDpPUA4f7f7JCOV8hXxtfjMtAZeBKf93yrB1XZet69TDR7EN0hFC6i4MF0Ig==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@swc/helpers': '*'
+ peerDependenciesMeta:
+ '@swc/helpers':
+ optional: true
+
+ '@swc/counter@0.1.3':
+ resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+
+ '@swc/types@0.1.9':
+ resolution: {integrity: sha512-qKnCno++jzcJ4lM4NTfYifm1EFSCeIfKiAHAfkENZAV5Kl9PjJIyd2yeeVv6c/2CckuLyv2NmRC5pv6pm2WQBg==}
+
+ '@szmarczak/http-timer@5.0.1':
+ resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
+ engines: {node: '>=14.16'}
+
'@tailwindcss/forms@0.5.9':
resolution: {integrity: sha512-tM4XVr2+UVTxXJzey9Twx48c1gcxFStqn1pQz0tRsX8o3DvxhN5oY5pvyAbUx7VTaZxpej4Zzvc6h+1RJBzpIg==}
peerDependencies:
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20'
+ '@tanstack/query-core@5.48.0':
+ resolution: {integrity: sha512-lZAfPPeVIqXCswE9SSbG33B6/91XOWt/Iq41bFeWb/mnHwQSIfFRbkS4bfs+WhIk9abRArF9Id2fp0Mgo+hq6Q==}
+
+ '@tanstack/react-query@5.48.0':
+ resolution: {integrity: sha512-GDExbjYWzvDokyRqMSWXdrPiYpp95Aig0oeMIrxTaruOJJgWiWfUP//OAaowm2RrRkGVsavSZdko/XmIrrV2Nw==}
+ peerDependencies:
+ react: ^18.0.0
+
'@tauri-apps/api@2.0.2':
resolution: {integrity: sha512-3wSwmG+1kr6WrgAFKK5ijkNFPp8TT3FLj3YHUb5EwMO+3FxX4uWlfSWkeeBy+Kc1RsKzugtYLuuya+98Flj+3w==}
- '@tauri-apps/cli-darwin-arm64@2.0.2':
- resolution: {integrity: sha512-B+/a8Q6wAqmB4A4HVeK0oQP5TdQGKW60ZLOI9O2ktH2HPr9ETr3XkwXPuJ2uAOuGEgtRZHBgFOIgG000vMnKlg==}
+ '@tauri-apps/cli-darwin-arm64@2.0.3':
+ resolution: {integrity: sha512-jIsbxGWS+As1ZN7umo90nkql/ZAbrDK0GBT6UsgHSz5zSwwArICsZFFwE1pLZip5yoiV5mn3TGG2c1+v+0puzQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
- '@tauri-apps/cli-darwin-x64@2.0.2':
- resolution: {integrity: sha512-kaurhn6XT4gAVCPAQSSHl/CHFxTS0ljc47N7iGTSlYJ03sCWPRZeNuVa/bn6rolz9MA2JfnRnFqB1pUL6jzp9Q==}
+ '@tauri-apps/cli-darwin-x64@2.0.3':
+ resolution: {integrity: sha512-ROITHtLTA1muyrwgyuwyasmaLCGtT4as/Kd1kerXaSDtFcYrnxiM984ZD0+FDUEDl5BgXtYa/sKKkKQFjgmM0A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
- '@tauri-apps/cli-linux-arm-gnueabihf@2.0.2':
- resolution: {integrity: sha512-bVrofjlacMxmGMcqK18iBW05tsZXOd19/MnqruFFcHSVjvkGGIXHMtUbMXnZNXBPkHDsnfytNtkY9SZGfCFaBA==}
+ '@tauri-apps/cli-linux-arm-gnueabihf@2.0.3':
+ resolution: {integrity: sha512-bQ3EZwCFfrLg/ZQ2I8sLuifSxESz4TP56SleTkKsPtTIZgNnKpM88PRDz4neiRroHVOq8NK0X276qi9LjGcXPw==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
- '@tauri-apps/cli-linux-arm64-gnu@2.0.2':
- resolution: {integrity: sha512-7XCBn0TTBVQGnV42dXcbHPLg/9W8kJoVzuliIozvNGyRWxfXqDbQYzpI48HUQG3LgHMabcw8+pVZAfGhevLrCA==}
+ '@tauri-apps/cli-linux-arm64-gnu@2.0.3':
+ resolution: {integrity: sha512-aLfAA8P9OTErVUk3sATxtXqpAtlfDPMPp4fGjDysEELG/MyekGhmh2k/kG/i32OdPeCfO+Nr37wJksARJKubGw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tauri-apps/cli-linux-arm64-musl@2.0.2':
- resolution: {integrity: sha512-1xi2SreGVlpAL68MCsDUY63rdItUdPZreXIAcOVqvUehcJRYOa1XGSBhrV0YXRgZeh0AtKC19z6PRzcv4rosZA==}
+ '@tauri-apps/cli-linux-arm64-musl@2.0.3':
+ resolution: {integrity: sha512-I4MVD7nf6lLLRmNQPpe5beEIFM6q7Zkmh77ROA5BNu/+vHNL5kiTMD+bmd10ZL2r753A6pO7AvqkIxcBuIl0tg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@tauri-apps/cli-linux-x64-gnu@2.0.2':
- resolution: {integrity: sha512-WVjwYzPWFqZVg1fx6KSU5w47Q0VbMyaCp34qs5EcS8EIU0/RnofdzqUoOYqvgGVgNgoz7Pj5dXK2SkS8BHXMmA==}
+ '@tauri-apps/cli-linux-x64-gnu@2.0.3':
+ resolution: {integrity: sha512-C6Jkx2zZGKkoi+sg5FK9GoH/0EvAaOgrZfF5azV5EALGba46g7VpWcZgp9zFUd7K2IzTi+0OOY8TQ2OVfKZgew==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tauri-apps/cli-linux-x64-musl@2.0.2':
- resolution: {integrity: sha512-h5miE2mctgaQNn/BbG9o1pnJcrx+VGBi2A6JFqGu934lFgSV5+s28M8Gc8AF2JgFH4hQV4IuMkeSw8Chu5Dodg==}
+ '@tauri-apps/cli-linux-x64-musl@2.0.3':
+ resolution: {integrity: sha512-qi4ghmTfSAl+EEUDwmwI9AJUiOLNSmU1RgiGgcPRE+7A/W+Am9UnxYySAiRbB/gJgTl9sj/pqH5Y9duP1/sqHg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@tauri-apps/cli-win32-arm64-msvc@2.0.2':
- resolution: {integrity: sha512-2b8oO0+dYonahG5PfA/zoq0zlafLclfmXgqoWDZ++UiPtQHJNpNeEQ8GWbSFKGHQ494Jo6jHvazOojGRE1kqAg==}
+ '@tauri-apps/cli-win32-arm64-msvc@2.0.3':
+ resolution: {integrity: sha512-UXxHkYmFesC97qVmZre4vY7oDxRDtC2OeKNv0bH+iSnuUp/ROxzJYGyaelnv9Ybvgl4YVqDCnxgB28qMM938TA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
- '@tauri-apps/cli-win32-ia32-msvc@2.0.2':
- resolution: {integrity: sha512-axgICLunFi0To3EibdCBgbST5RocsSmtM4c04+CbcX8WQQosJ9ziWlCSrrOTRr+gJERAMSvEyVUS98f6bWMw9A==}
+ '@tauri-apps/cli-win32-ia32-msvc@2.0.3':
+ resolution: {integrity: sha512-D+xoaa35RGlkXDpnL5uDTpj29untuC5Wp6bN9snfgFDagD0wnFfC8+2ZQGu16bD0IteWqDI0OSoIXhNvy+F+wg==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
- '@tauri-apps/cli-win32-x64-msvc@2.0.2':
- resolution: {integrity: sha512-JR17cM6+DyExZRgpXr2/DdqvcFYi/EKvQt8dI5R1/uQoesWd8jeNnrU7c1FG1Zmw9+pTzDztsNqEKsrNq2sNIg==}
+ '@tauri-apps/cli-win32-x64-msvc@2.0.3':
+ resolution: {integrity: sha512-eWV9XWb4dSYHXl13OtYWLjX1JHphUEkHkkGwJrhr8qFBm7RbxXxQvrsUEprSi51ug/dwJenjJgM4zR8By4htfw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
- '@tauri-apps/cli@2.0.2':
- resolution: {integrity: sha512-R4ontHZvXORArERAHIidp5zRfZEshZczTiK+poslBv7AGKpQZoMw+E49zns7mOmP64i2Cq9Ci0pJvi4Rm8Okzw==}
+ '@tauri-apps/cli@2.0.3':
+ resolution: {integrity: sha512-JwEyhc5BAVpn4E8kxzY/h7+bVOiXQdudR1r3ODMfyyumZBfgIWqpD/WuTcPq6Yjchju1BSS+80jAE/oYwI/RKg==}
engines: {node: '>= 10'}
hasBin: true
@@ -818,8 +1134,8 @@ packages:
'@tauri-apps/plugin-store@2.0.0':
resolution: {integrity: sha512-l4xsbxAXrKGdBdYNNswrLfcRv3v1kOatdycOcVPYW+jKwkznCr1HEOrPXkPhXsZLSLyYmNXpgfOmdSZNmcykDg==}
- '@tweenjs/tween.js@23.1.3':
- resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==}
+ '@tweenjs/tween.js@23.1.2':
+ resolution: {integrity: sha512-kMCNaZCJugWI86xiEHaY338CU5JpD0B97p1j1IKNn/Zto8PgACjQx0UxbHjmOcLl/dDOBnItwD07KmCs75pxtQ==}
'@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@@ -845,6 +1161,9 @@ packages:
'@types/file-saver@2.0.7':
resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==}
+ '@types/http-cache-semantics@4.0.4':
+ resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
+
'@types/json5@0.0.29':
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
@@ -854,8 +1173,8 @@ packages:
'@types/offscreencanvas@2019.7.3':
resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==}
- '@types/prop-types@15.7.13':
- resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
+ '@types/prop-types@15.7.12':
+ resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
'@types/react-dom@18.3.0':
resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
@@ -884,8 +1203,11 @@ packages:
'@types/three@0.163.0':
resolution: {integrity: sha512-uIdDhsXRpQiBUkflBS/i1l3JX14fW6Ot9csed60nfbZNXHDTRsnV2xnTVwXcgbvTiboAR4IW+t+lTL5f1rqIqA==}
- '@types/webxr@0.5.20':
- resolution: {integrity: sha512-JGpU6qiIJQKUuVSKx1GtQnHJGxRjtfGIhzO2ilq43VZZS//f1h1Sgexbdk+Lq+7569a6EYhOWrUpIruR/1Enmg==}
+ '@types/webxr@0.5.16':
+ resolution: {integrity: sha512-0E0Cl84FECtzrB4qG19TNTqpunw0F1YF0QZZnFMF6pDw1kNKJtrlTKlVB34stGIsHbZsYQ7H0tNjPfZftkHHoA==}
+
+ '@types/yoga-layout@1.9.2':
+ resolution: {integrity: sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==}
'@typescript-eslint/eslint-plugin@7.18.0':
resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==}
@@ -898,6 +1220,17 @@ packages:
typescript:
optional: true
+ '@typescript-eslint/eslint-plugin@8.8.0':
+ resolution: {integrity: sha512-wORFWjU30B2WJ/aXBfOm1LX9v9nyt9D3jsSOxC3cCaTQGCW5k4jNpmjFv3U7p/7s4yvdjHzwtv2Sd2dOyhjS0A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
'@typescript-eslint/parser@7.18.0':
resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==}
engines: {node: ^18.18.0 || >=20.0.0}
@@ -908,10 +1241,24 @@ packages:
typescript:
optional: true
+ '@typescript-eslint/parser@8.8.0':
+ resolution: {integrity: sha512-uEFUsgR+tl8GmzmLjRqz+VrDv4eoaMqMXW7ruXfgThaAShO9JTciKpEsB+TvnfFfbg5IpujgMXVV36gOJRLtZg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
'@typescript-eslint/scope-manager@7.18.0':
resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==}
engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/scope-manager@8.8.0':
+ resolution: {integrity: sha512-EL8eaGC6gx3jDd8GwEFEV091210U97J0jeEHrAYvIYosmEGet4wJ+g0SYmLu+oRiAwbSA5AVrt6DxLHfdd+bUg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@typescript-eslint/type-utils@7.18.0':
resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==}
engines: {node: ^18.18.0 || >=20.0.0}
@@ -922,10 +1269,23 @@ packages:
typescript:
optional: true
+ '@typescript-eslint/type-utils@8.8.0':
+ resolution: {integrity: sha512-IKwJSS7bCqyCeG4NVGxnOP6lLT9Okc3Zj8hLO96bpMkJab+10HIfJbMouLrlpyOr3yrQ1cA413YPFiGd1mW9/Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
'@typescript-eslint/types@7.18.0':
resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==}
engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/types@8.8.0':
+ resolution: {integrity: sha512-QJwc50hRCgBd/k12sTykOJbESe1RrzmX6COk8Y525C9l7oweZ+1lw9JiU56im7Amm8swlz00DRIlxMYLizr2Vw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@typescript-eslint/typescript-estree@7.18.0':
resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==}
engines: {node: ^18.18.0 || >=20.0.0}
@@ -935,16 +1295,35 @@ packages:
typescript:
optional: true
+ '@typescript-eslint/typescript-estree@8.8.0':
+ resolution: {integrity: sha512-ZaMJwc/0ckLz5DaAZ+pNLmHv8AMVGtfWxZe/x2JVEkD5LnmhWiQMMcYT7IY7gkdJuzJ9P14fRy28lUrlDSWYdw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
'@typescript-eslint/utils@7.18.0':
resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==}
engines: {node: ^18.18.0 || >=20.0.0}
peerDependencies:
eslint: ^8.56.0
+ '@typescript-eslint/utils@8.8.0':
+ resolution: {integrity: sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+
'@typescript-eslint/visitor-keys@7.18.0':
resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==}
engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/visitor-keys@8.8.0':
+ resolution: {integrity: sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@ungap/structured-clone@1.2.0':
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
@@ -962,19 +1341,43 @@ packages:
peerDependencies:
vite: ^4.2.0 || ^5.0.0
+ '@wry/caches@1.0.1':
+ resolution: {integrity: sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==}
+ engines: {node: '>=8'}
+
+ '@wry/context@0.7.4':
+ resolution: {integrity: sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==}
+ engines: {node: '>=8'}
+
+ '@wry/equality@0.5.7':
+ resolution: {integrity: sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==}
+ engines: {node: '>=8'}
+
+ '@wry/trie@0.4.3':
+ resolution: {integrity: sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==}
+ engines: {node: '>=8'}
+
+ '@wry/trie@0.5.0':
+ resolution: {integrity: sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==}
+ engines: {node: '>=8'}
+
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
- acorn@8.12.1:
- resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
+ acorn@8.12.0:
+ resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==}
engines: {node: '>=0.4.0'}
hasBin: true
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+ ansi-escapes@4.3.2:
+ resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
+ engines: {node: '>=8'}
+
ansi-escapes@7.0.0:
resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==}
engines: {node: '>=18'}
@@ -1018,9 +1421,6 @@ packages:
aria-query@5.1.3:
resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
- array-buffer-byte-length@1.0.0:
- resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
-
array-buffer-byte-length@1.0.1:
resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
engines: {node: '>= 0.4'}
@@ -1064,6 +1464,14 @@ packages:
ast-types-flow@0.0.8:
resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==}
+ astral-regex@2.0.0:
+ resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
+ engines: {node: '>=8'}
+
+ auto-bind@4.0.0:
+ resolution: {integrity: sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==}
+ engines: {node: '>=8'}
+
autoprefixer@10.4.20:
resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
engines: {node: ^10 || ^12 || >=14}
@@ -1071,16 +1479,12 @@ packages:
peerDependencies:
postcss: ^8.1.0
- available-typed-arrays@1.0.5:
- resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
- engines: {node: '>= 0.4'}
-
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
- axe-core@4.10.0:
- resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==}
+ axe-core@4.10.1:
+ resolution: {integrity: sha512-qPC9o+kD8Tir0lzNGLeghbOrWMr3ZJpaRlCIb6Uobt/7N4FiEDvqUMnxzCHRHmg8vOg14kr5gVNyScRmbMaJ9g==}
engines: {node: '>=4'}
axobject-query@4.1.0:
@@ -1119,6 +1523,11 @@ packages:
browser-fs-access@0.35.0:
resolution: {integrity: sha512-sLoadumpRfsjprP8XzVjpQc0jK8yqHBx0PtUTGYj2fftT+P/t+uyDAQdMgGAPKD011in/O+YYGh7fIs0oG/viw==}
+ browserslist@4.23.1:
+ resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
browserslist@4.24.0:
resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -1130,6 +1539,14 @@ packages:
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
+ cacheable-lookup@7.0.0:
+ resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==}
+ engines: {node: '>=14.16'}
+
+ cacheable-request@10.2.14:
+ resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==}
+ engines: {node: '>=14.16'}
+
cached-iterable@0.3.0:
resolution: {integrity: sha512-MDqM6TpBVebZD4UDtmlFp8EjVtRcsB6xt9aRdWymjk0fWVUUGgmt/V7o0H0gkI2Tkvv8B0ucjidZm4mLosdlWw==}
engines: {node: '>=8.9.0'}
@@ -1141,6 +1558,9 @@ packages:
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
engines: {node: '>= 0.4'}
+ call-me-maybe@1.0.2:
+ resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
+
callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
@@ -1149,13 +1569,20 @@ packages:
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
engines: {node: '>= 6'}
- camera-controls@2.9.0:
- resolution: {integrity: sha512-TpCujnP0vqPppTXXJRYpvIy0xq9Tro6jQf2iYUxlDpPCNxkvE/XGaTuwIxnhINOkVP/ob2CRYXtY3iVYXeMEzA==}
+ camera-controls@2.8.5:
+ resolution: {integrity: sha512-7VTwRk7Nu1nRKsY7bEt9HVBfKt8DETvzyYhLN4OW26OByBayMDB5fUaNcPI+z++vG23RH5yqn6ZRhZcgLQy2rA==}
peerDependencies:
three: '>=0.126.1'
- caniuse-lite@1.0.30001667:
- resolution: {integrity: sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==}
+ caniuse-lite@1.0.30001636:
+ resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==}
+
+ caniuse-lite@1.0.30001669:
+ resolution: {integrity: sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==}
+
+ case@1.6.3:
+ resolution: {integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==}
+ engines: {node: '>= 0.8.0'}
chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
@@ -1177,17 +1604,42 @@ packages:
resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==}
engines: {node: '>= 14.16.0'}
+ ci-info@2.0.0:
+ resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==}
+
classnames@2.5.1:
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
+ cli-boxes@2.2.1:
+ resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==}
+ engines: {node: '>=6'}
+
+ cli-cursor@3.1.0:
+ resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
+ engines: {node: '>=8'}
+
cli-cursor@5.0.0:
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
engines: {node: '>=18'}
+ cli-highlight@2.1.11:
+ resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==}
+ engines: {node: '>=8.0.0', npm: '>=5.0.0'}
+ hasBin: true
+
+ cli-truncate@2.1.0:
+ resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==}
+ engines: {node: '>=8'}
+
cli-truncate@4.0.0:
resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==}
engines: {node: '>=18'}
+ clipanion@3.2.1:
+ resolution: {integrity: sha512-dYFdjLb7y1ajfxQopN05mylEpK9ZX0sO1/RfMXdfmwjlIsPkbh4p7A682x++zFPLDCo1x3p82dtljHf5cW2LKA==}
+ peerDependencies:
+ typanion: '*'
+
cliui@7.0.4:
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
@@ -1195,6 +1647,10 @@ packages:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
+ code-excerpt@3.0.0:
+ resolution: {integrity: sha512-VHNTVhd7KsLGOqfX3SyeO8RyYPMp1GJOg194VITk04WMYCv4plV68YWe6TJZxd9MhobjtpMRnVky01gqZsalaw==}
+ engines: {node: '>=10'}
+
color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
@@ -1231,6 +1687,10 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+ convert-to-spaces@1.0.2:
+ resolution: {integrity: sha512-cj09EBuObp9gZNQCzc7hByQyrs6jVGE+o9kSJmeUoj+GiPiJvi5LYqEH/Hmme4+MTLHM+Ejtq+FChpjjEnsPdQ==}
+ engines: {node: '>= 4'}
+
create-require@1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
@@ -1280,6 +1740,15 @@ packages:
supports-color:
optional: true
+ debug@4.3.5:
+ resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
debug@4.3.7:
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
engines: {node: '>=6.0'}
@@ -1289,8 +1758,9 @@ packages:
supports-color:
optional: true
- deep-equal@2.2.2:
- resolution: {integrity: sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==}
+ decompress-response@6.0.0:
+ resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
+ engines: {node: '>=10'}
deep-equal@2.2.3:
resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==}
@@ -1303,6 +1773,10 @@ packages:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
+ defer-to-connect@2.0.1:
+ resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
+ engines: {node: '>=10'}
+
define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
@@ -1319,8 +1793,13 @@ packages:
resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==}
engines: {node: '>=10'}
- detect-gpu@5.0.51:
- resolution: {integrity: sha512-7P+5KDthVGXXWS06EuqBIq7YBijxfaNfm+BSFNTRAkZP26J97ASssh5KoR53diWNcBNOEb1ILfdsz2pzesSgYw==}
+ detect-gpu@5.0.38:
+ resolution: {integrity: sha512-36QeGHSXYcJ/RfrnPEScR8GDprbXFG4ZhXsfVNVHztZr38+fRxgHnJl3CjYXXjbeRUhu3ZZBJh6Lg0A9v0Qd8A==}
+
+ detect-libc@1.0.3:
+ resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+ engines: {node: '>=0.10'}
+ hasBin: true
didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
@@ -1344,6 +1823,10 @@ packages:
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'}
+ dotenv@16.4.5:
+ resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
+ engines: {node: '>=12'}
+
draco3d@1.5.7:
resolution: {integrity: sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==}
@@ -1357,11 +1840,14 @@ packages:
resolution: {integrity: sha512-uW2UKSsuty9ANJ3YByIQE4ANkD8nqUPO7r6Fwcc1ADKPe9FRdcPpMl3VEput4JSvKBJ4J86npIC2MLP0pYkCuw==}
hasBin: true
- electron-to-chromium@1.5.34:
- resolution: {integrity: sha512-/TZAiChbAflBNjCg+VvstbcwAtIL/VdMFO3NgRFIzBjpvPzWOTIbbO8kNb6RwU4bt9TP7K+3KqBKw/lOU+Y+GA==}
+ electron-to-chromium@1.4.803:
+ resolution: {integrity: sha512-61H9mLzGOCLLVsnLiRzCbc63uldP0AniRYPV3hbGVtONA1pI7qSGILdbofR7A8TMbOypDocEAjH/e+9k1QIe3g==}
+
+ electron-to-chromium@1.5.41:
+ resolution: {integrity: sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ==}
- emoji-regex@10.4.0:
- resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
+ emoji-regex@10.3.0:
+ resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -1372,8 +1858,8 @@ packages:
end-of-stream@1.4.4:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
- enhanced-resolve@5.17.1:
- resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==}
+ enhanced-resolve@5.17.0:
+ resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==}
engines: {node: '>=10.13.0'}
environment@1.1.0:
@@ -1395,8 +1881,8 @@ packages:
es-get-iterator@1.1.3:
resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==}
- es-iterator-helpers@1.1.0:
- resolution: {integrity: sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==}
+ es-iterator-helpers@1.0.19:
+ resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.0.0:
@@ -1414,13 +1900,16 @@ packages:
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
engines: {node: '>= 0.4'}
+ es6-promise@3.3.1:
+ resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==}
+
esbuild@0.21.5:
resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
engines: {node: '>=12'}
hasBin: true
- escalade@3.1.1:
- resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
+ escalade@3.1.2:
+ resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
engines: {node: '>=6'}
escalade@3.2.0:
@@ -1431,6 +1920,10 @@ packages:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
+ escape-string-regexp@2.0.0:
+ resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
+ engines: {node: '>=8'}
+
escape-string-regexp@4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
@@ -1489,12 +1982,33 @@ packages:
eslint-import-resolver-webpack:
optional: true
- eslint-plugin-import@2.31.0:
- resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
+ eslint-module-utils@2.8.1:
+ resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
- eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9
+ eslint: '*'
+ eslint-import-resolver-node: '*'
+ eslint-import-resolver-typescript: '*'
+ eslint-import-resolver-webpack: '*'
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+ eslint:
+ optional: true
+ eslint-import-resolver-node:
+ optional: true
+ eslint-import-resolver-typescript:
+ optional: true
+ eslint-import-resolver-webpack:
+ optional: true
+
+ eslint-plugin-import@2.31.0:
+ resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ '@typescript-eslint/parser': '*'
+ eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9
peerDependenciesMeta:
'@typescript-eslint/parser':
optional: true
@@ -1535,8 +2049,8 @@ packages:
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- esquery@1.6.0:
- resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+ esquery@1.5.0:
+ resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
engines: {node: '>=0.10'}
esrecurse@4.3.0:
@@ -1581,6 +2095,9 @@ packages:
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+ fast-safe-stringify@2.1.1:
+ resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
+
fastq@1.17.1:
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
@@ -1598,8 +2115,8 @@ packages:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
- find-babel-config@2.1.2:
- resolution: {integrity: sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg==}
+ find-babel-config@2.1.1:
+ resolution: {integrity: sha512-5Ji+EAysHGe1OipH7GN4qDjok5Z1uw5KAwDCbicU/4wyTZY7CqOCzcWbG7J5ad9mazq67k89fXlbc1MuIfl9uA==}
find-up@3.0.0:
resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
@@ -1619,22 +2136,27 @@ packages:
flatbuffers@22.10.26:
resolution: {integrity: sha512-sdO3emf/BlLfOogW6KwHuXg16APR/E86jNacDXfSInPzt8SSEzxlHcqDekfM/IJ1CGC5bvDksfNufNhS8h1FRA==}
- flatbuffers@22.12.6:
- resolution: {integrity: sha512-CEwO0TRo6Z2dQ9iIfVAUC+BipwUnP2g8paCNqnrSsTh/axisRgzOwWyqTiPJMh8Si9QwhROjZ/FsFMfoESP33A==}
-
flatted@3.3.1:
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
for-each@0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
- foreground-child@3.3.0:
- resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
+ foreground-child@3.2.1:
+ resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==}
engines: {node: '>=14'}
+ form-data-encoder@2.1.4:
+ resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==}
+ engines: {node: '>= 14.17'}
+
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
+ fs-extra@10.1.0:
+ resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
+ engines: {node: '>=12'}
+
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
@@ -1676,6 +2198,10 @@ packages:
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
engines: {node: '>=8'}
+ get-stream@6.0.1:
+ resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+ engines: {node: '>=10'}
+
get-stream@8.0.1:
resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
engines: {node: '>=16'}
@@ -1684,8 +2210,8 @@ packages:
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
engines: {node: '>= 0.4'}
- get-tsconfig@4.8.1:
- resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==}
+ get-tsconfig@4.7.5:
+ resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
@@ -1695,8 +2221,9 @@ packages:
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
engines: {node: '>=10.13.0'}
- glob@10.4.5:
- resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+ glob@10.4.1:
+ resolution: {integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==}
+ engines: {node: '>=16 || 14 >=14.18'}
hasBin: true
glob@7.2.3:
@@ -1715,6 +2242,10 @@ packages:
resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
engines: {node: '>=8'}
+ globals@15.10.0:
+ resolution: {integrity: sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==}
+ engines: {node: '>=18'}
+
globalthis@1.0.4:
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'}
@@ -1729,12 +2260,32 @@ packages:
gopd@1.0.1:
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
+ got-fetch@5.1.10:
+ resolution: {integrity: sha512-Gwj/A2htjvLEcY07PKDItv0WCPEs3dV2vWeZ+9TVBSKSTuWEZ4oXaMD0ZAOsajwx2orahQWN4HI0MfRyWSZsbg==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ got: ^12.0.0
+
+ got@12.6.1:
+ resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==}
+ engines: {node: '>=14.16'}
+
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+ graphql-tag@2.12.6:
+ resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
+
+ graphql@15.9.0:
+ resolution: {integrity: sha512-GCOQdvm7XxV1S4U4CGrsdlEN37245eC8P9zaYCMr6K1BG0IPGy5lUwmJsEOGyl1GD6HXjOtl2keCP9asRBwNvA==}
+ engines: {node: '>= 10.x'}
+
has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
@@ -1773,8 +2324,24 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
- hls.js@1.3.5:
- resolution: {integrity: sha512-uybAvKS6uDe0MnWNEPnO0krWVr+8m2R0hJ/viql8H3MVK+itq8gGQuIYoFHL3rECkIpNH98Lw8YuuWMKZxp3Ew==}
+ highlight.js@10.7.3:
+ resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
+
+ hls.js@1.5.17:
+ resolution: {integrity: sha512-wA66nnYFvQa1o4DO/BFgLNRKnBTVXpNeldGRBJ2Y0SvFtdwvFKCbqa9zhHoZLoxHhZ+jYsj3aIBkWQQCPNOhMw==}
+
+ hoist-non-react-statics@3.3.2:
+ resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
+
+ http-cache-semantics@4.1.1:
+ resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
+
+ http2-client@1.3.5:
+ resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==}
+
+ http2-wrapper@2.2.1:
+ resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==}
+ engines: {node: '>=10.19.0'}
human-signals@1.1.1:
resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
@@ -1795,8 +2362,8 @@ packages:
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
- ignore@5.3.2:
- resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ ignore@5.3.1:
+ resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
engines: {node: '>= 4'}
immediate@3.0.6:
@@ -1805,8 +2372,8 @@ packages:
immer@9.0.21:
resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==}
- immutable@4.3.7:
- resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==}
+ immutable@4.3.6:
+ resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==}
import-fresh@3.3.0:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
@@ -1816,6 +2383,10 @@ packages:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
@@ -1823,6 +2394,16 @@ packages:
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+ ink@3.2.0:
+ resolution: {integrity: sha512-firNp1q3xxTzoItj/eOOSZQnYSlyrWks5llCTVX37nJ59K3eXbQ8PtzCguqo8YI19EELo5QxaKnJd4VxzhU8tg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '>=16.8.0'
+ react: '>=16.8.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
internal-slot@1.0.7:
resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
engines: {node: '>= 0.4'}
@@ -1837,9 +2418,6 @@ packages:
resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
engines: {node: '>= 0.4'}
- is-array-buffer@3.0.2:
- resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
-
is-array-buffer@3.0.4:
resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
engines: {node: '>= 0.4'}
@@ -1866,6 +2444,13 @@ packages:
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
engines: {node: '>= 0.4'}
+ is-ci@2.0.0:
+ resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==}
+ hasBin: true
+
+ is-core-module@2.13.1:
+ resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
+
is-core-module@2.15.1:
resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
engines: {node: '>= 0.4'}
@@ -1910,9 +2495,6 @@ packages:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
- is-map@2.0.2:
- resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
-
is-map@2.0.3:
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
engines: {node: '>= 0.4'}
@@ -1940,16 +2522,10 @@ packages:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
engines: {node: '>= 0.4'}
- is-set@2.0.2:
- resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
-
is-set@2.0.3:
resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
engines: {node: '>= 0.4'}
- is-shared-array-buffer@1.0.2:
- resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
-
is-shared-array-buffer@1.0.3:
resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==}
engines: {node: '>= 0.4'}
@@ -1970,17 +2546,10 @@ packages:
resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
engines: {node: '>= 0.4'}
- is-typed-array@1.1.12:
- resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==}
- engines: {node: '>= 0.4'}
-
is-typed-array@1.1.13:
resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
engines: {node: '>= 0.4'}
- is-weakmap@2.0.1:
- resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
-
is-weakmap@2.0.2:
resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
engines: {node: '>= 0.4'}
@@ -1988,9 +2557,6 @@ packages:
is-weakref@1.0.2:
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
- is-weakset@2.0.2:
- resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==}
-
is-weakset@2.0.3:
resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==}
engines: {node: '>= 0.4'}
@@ -2005,17 +2571,17 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
- iterator.prototype@1.1.3:
- resolution: {integrity: sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==}
- engines: {node: '>= 0.4'}
+ iterator.prototype@1.1.2:
+ resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
its-fine@1.2.5:
resolution: {integrity: sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==}
peerDependencies:
react: '>=18.0'
- jackspeak@3.4.3:
- resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+ jackspeak@3.4.0:
+ resolution: {integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==}
+ engines: {node: '>=14'}
jiti@1.21.6:
resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
@@ -2028,6 +2594,11 @@ packages:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
+ jsesc@2.5.2:
+ resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
+ engines: {node: '>=4'}
+ hasBin: true
+
jsesc@3.0.2:
resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==}
engines: {node: '>=6'}
@@ -2051,6 +2622,9 @@ packages:
engines: {node: '>=6'}
hasBin: true
+ jsonfile@6.1.0:
+ resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
jsx-ast-utils@3.3.5:
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
engines: {node: '>=4.0'}
@@ -2118,17 +2692,22 @@ packages:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
- lru-cache@10.4.3:
- resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+ lowercase-keys@3.0.0:
+ resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ lru-cache@10.2.2:
+ resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==}
+ engines: {node: 14 || >=16.14}
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
- maath@0.10.8:
- resolution: {integrity: sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==}
+ maath@0.10.7:
+ resolution: {integrity: sha512-zQ2xd7dNOIVTjAS+hj22fyj1EFYmOJX6tzKjZ92r6WDoq8hyFxjuGA2q950tmR4iC/EKXoMQdSipkaJVuUHDTg==}
peerDependencies:
- '@types/three': '>=0.134.0'
- three: '>=0.134.0'
+ '@types/three': '>=0.144.0'
+ three: '>=0.144.0'
make-error@1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
@@ -2151,6 +2730,10 @@ packages:
meshoptimizer@0.18.1:
resolution: {integrity: sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==}
+ micromatch@4.0.7:
+ resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
+ engines: {node: '>=8.6'}
+
micromatch@4.0.8:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
@@ -2167,6 +2750,14 @@ packages:
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
engines: {node: '>=18'}
+ mimic-response@3.1.0:
+ resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
+ engines: {node: '>=10'}
+
+ mimic-response@4.0.0:
+ resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
mini-svg-data-uri@1.4.4:
resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
hasBin: true
@@ -2178,8 +2769,8 @@ packages:
resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==}
engines: {node: '>=16 || 14 >=14.17'}
- minimatch@9.0.5:
- resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ minimatch@9.0.4:
+ resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
engines: {node: '>=16 || 14 >=14.17'}
minimist@1.2.8:
@@ -2193,6 +2784,9 @@ packages:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
+ ms@2.1.2:
+ resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -2207,6 +2801,28 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+ node-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
+ node-fetch-h2@2.3.0:
+ resolution: {integrity: sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==}
+ engines: {node: 4.x || >=6.0.0}
+
+ node-fetch@2.7.0:
+ resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
+ engines: {node: 4.x || >=6.0.0}
+ peerDependencies:
+ encoding: ^0.1.0
+ peerDependenciesMeta:
+ encoding:
+ optional: true
+
+ node-readfiles@0.2.0:
+ resolution: {integrity: sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==}
+
+ node-releases@2.0.14:
+ resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
+
node-releases@2.0.18:
resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
@@ -2218,6 +2834,10 @@ packages:
resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
engines: {node: '>=0.10.0'}
+ normalize-url@8.0.1:
+ resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
+ engines: {node: '>=14.16'}
+
npm-run-path@4.0.1:
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
engines: {node: '>=8'}
@@ -2226,6 +2846,22 @@ packages:
resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ oas-kit-common@1.0.8:
+ resolution: {integrity: sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==}
+
+ oas-linter@3.2.2:
+ resolution: {integrity: sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==}
+
+ oas-resolver@2.5.6:
+ resolution: {integrity: sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==}
+ hasBin: true
+
+ oas-schema-walker@1.1.5:
+ resolution: {integrity: sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==}
+
+ oas-validator@5.0.8:
+ resolution: {integrity: sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==}
+
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -2234,16 +2870,8 @@ packages:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
engines: {node: '>= 6'}
- object-inspect@1.12.3:
- resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
-
- object-inspect@1.13.2:
- resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
- engines: {node: '>= 0.4'}
-
- object-is@1.1.5:
- resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==}
- engines: {node: '>= 0.4'}
+ object-inspect@1.13.1:
+ resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
object-is@1.1.6:
resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==}
@@ -2295,10 +2923,20 @@ packages:
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
engines: {node: '>=12'}
+ openapi3-ts@2.0.2:
+ resolution: {integrity: sha512-TxhYBMoqx9frXyOgnRHufjQfPXomTIHYKhSKJ6jHfj13kS8OEIhvmE8CTuQyKtjjWttAjX5DPxM1vmalEpo8Qw==}
+
+ optimism@0.18.0:
+ resolution: {integrity: sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==}
+
optionator@0.9.4:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
+ p-cancelable@3.0.0:
+ resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
+ engines: {node: '>=12.20'}
+
p-limit@2.3.0:
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
engines: {node: '>=6'}
@@ -2319,13 +2957,23 @@ packages:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'}
- package-json-from-dist@1.0.1:
- resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
-
parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
+ parse5-htmlparser2-tree-adapter@6.0.1:
+ resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==}
+
+ parse5@5.1.1:
+ resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==}
+
+ parse5@6.0.1:
+ resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
+
+ patch-console@1.0.0:
+ resolution: {integrity: sha512-nxl9nrnLQmh64iTzMfyylSlRozL7kAXIaxw1fVcLYdyhNkJCRUzirRZTikXGJsg+hc4fqpneTK6iU2H1Q8THSA==}
+ engines: {node: '>=10'}
+
path-exists@3.0.0:
resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
engines: {node: '>=4'}
@@ -2362,8 +3010,11 @@ packages:
engines: {node: '>=0.10'}
hasBin: true
- picocolors@1.1.0:
- resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}
+ picocolors@1.0.1:
+ resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
@@ -2386,6 +3037,10 @@ packages:
resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==}
engines: {node: '>=8'}
+ pluralize@8.0.0:
+ resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
+ engines: {node: '>=4'}
+
possible-typed-array-names@1.0.0:
resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
engines: {node: '>= 0.4'}
@@ -2414,19 +3069,23 @@ packages:
ts-node:
optional: true
- postcss-nested@6.2.0:
- resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
+ postcss-nested@6.0.1:
+ resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
engines: {node: '>=12.0'}
peerDependencies:
postcss: ^8.2.14
- postcss-selector-parser@6.1.2:
- resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+ postcss-selector-parser@6.1.0:
+ resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==}
engines: {node: '>=4'}
postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+ postcss@8.4.38:
+ resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
+ engines: {node: ^10 || ^12 || >=14}
+
postcss@8.4.47:
resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==}
engines: {node: ^10 || ^12 || >=14}
@@ -2453,6 +3112,9 @@ packages:
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+ property-expr@2.0.6:
+ resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==}
+
pump@3.0.0:
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
@@ -2463,11 +3125,18 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ quick-lru@5.1.1:
+ resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
+ engines: {node: '>=10'}
+
react-composer@5.0.3:
resolution: {integrity: sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==}
peerDependencies:
react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
+ react-devtools-core@4.28.5:
+ resolution: {integrity: sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA==}
+
react-dom@18.3.1:
resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
peerDependencies:
@@ -2505,6 +3174,12 @@ packages:
react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18
react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18
+ react-reconciler@0.26.2:
+ resolution: {integrity: sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q==}
+ engines: {node: '>=0.10.0'}
+ peerDependencies:
+ react: ^17.0.2
+
react-reconciler@0.27.0:
resolution: {integrity: sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==}
engines: {node: '>=0.10.0'}
@@ -2521,15 +3196,15 @@ packages:
peerDependencies:
react: '>=16.8.0'
- react-router-dom@6.26.2:
- resolution: {integrity: sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==}
+ react-router-dom@6.27.0:
+ resolution: {integrity: sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==}
engines: {node: '>=14.0.0'}
peerDependencies:
react: '>=16.8'
react-dom: '>=16.8'
- react-router@6.26.2:
- resolution: {integrity: sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==}
+ react-router@6.27.0:
+ resolution: {integrity: sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==}
engines: {node: '>=14.0.0'}
peerDependencies:
react: '>=16.8'
@@ -2558,16 +3233,26 @@ packages:
resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==}
engines: {node: '>= 0.4'}
+ reftools@1.1.9:
+ resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==}
+
regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
- regexp.prototype.flags@1.5.1:
- resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==}
+ regexp.prototype.flags@1.5.2:
+ resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
engines: {node: '>= 0.4'}
- regexp.prototype.flags@1.5.3:
- resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==}
- engines: {node: '>= 0.4'}
+ rehackt@0.1.0:
+ resolution: {integrity: sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: '*'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ react:
+ optional: true
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
@@ -2580,6 +3265,9 @@ packages:
reselect@4.1.8:
resolution: {integrity: sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==}
+ resolve-alpn@1.2.1:
+ resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
+
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -2595,6 +3283,18 @@ packages:
resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
hasBin: true
+ response-iterator@0.2.6:
+ resolution: {integrity: sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==}
+ engines: {node: '>=0.8'}
+
+ responselike@3.0.0:
+ resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==}
+ engines: {node: '>=14.16'}
+
+ restore-cursor@3.1.0:
+ resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
+ engines: {node: '>=8'}
+
restore-cursor@5.1.0:
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
engines: {node: '>=18'}
@@ -2629,6 +3329,9 @@ packages:
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+ rxjs@7.8.1:
+ resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
+
safe-array-concat@1.1.2:
resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
engines: {node: '>=0.4'}
@@ -2637,11 +3340,14 @@ packages:
resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
engines: {node: '>= 0.4'}
- sass@1.79.4:
- resolution: {integrity: sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==}
+ sass@1.80.2:
+ resolution: {integrity: sha512-9wXY8cGBlUmoUoT+vwOZOFCiS+naiWVjqlreN9ar9PudXbGwlMTFwCR5K9kB4dFumJ6ib98wZyAObJKsWf1nAA==}
engines: {node: '>=14.0.0'}
hasBin: true
+ scheduler@0.20.2:
+ resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==}
+
scheduler@0.21.0:
resolution: {integrity: sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==}
@@ -2661,10 +3367,6 @@ packages:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
- set-function-name@2.0.1:
- resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==}
- engines: {node: '>= 0.4'}
-
set-function-name@2.0.2:
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
engines: {node: '>= 0.4'}
@@ -2680,8 +3382,26 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
- side-channel@1.0.4:
- resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
+ shell-quote@1.8.1:
+ resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==}
+
+ should-equal@2.0.0:
+ resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==}
+
+ should-format@3.0.3:
+ resolution: {integrity: sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==}
+
+ should-type-adaptors@1.1.0:
+ resolution: {integrity: sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==}
+
+ should-type@1.4.0:
+ resolution: {integrity: sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==}
+
+ should-util@1.0.1:
+ resolution: {integrity: sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==}
+
+ should@13.2.3:
+ resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==}
side-channel@1.0.6:
resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
@@ -2701,6 +3421,14 @@ packages:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
+ slash@4.0.0:
+ resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==}
+ engines: {node: '>=12'}
+
+ slice-ansi@3.0.0:
+ resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
+ engines: {node: '>=8'}
+
slice-ansi@5.0.0:
resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
engines: {node: '>=12'}
@@ -2709,8 +3437,9 @@ packages:
resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==}
engines: {node: '>=18'}
- solarxr-protocol@file:solarxr-protocol:
- resolution: {directory: solarxr-protocol, type: directory}
+ source-map-js@1.2.0:
+ resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
+ engines: {node: '>=0.10.0'}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
@@ -2736,8 +3465,8 @@ packages:
spdx-expression-parse@3.0.1:
resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
- spdx-license-ids@3.0.20:
- resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==}
+ spdx-license-ids@3.0.18:
+ resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==}
spdx-ranges@2.1.1:
resolution: {integrity: sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==}
@@ -2745,6 +3474,10 @@ packages:
spdx-satisfies@5.0.1:
resolution: {integrity: sha512-Nwor6W6gzFp8XX4neaKQ7ChV4wmpSh2sSDemMFSzHxpTw460jxFYeOn+jq4ybnSSw/5sc3pjka9MQPouksQNpw==}
+ stack-utils@2.0.6:
+ resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
+ engines: {node: '>=10'}
+
stats-gl@2.2.8:
resolution: {integrity: sha512-94G5nZvduDmzxBS7K0lYnynYwreZpkknD8g5dZmU6mpwIhy3caCrjAm11Qm1cbyx7mqix7Fp00RkbsonzKWnoQ==}
@@ -2767,12 +3500,13 @@ packages:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
- string-width@7.2.0:
- resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
+ string-width@7.1.0:
+ resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==}
engines: {node: '>=18'}
- string.prototype.includes@2.0.0:
- resolution: {integrity: sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==}
+ string.prototype.includes@2.0.1:
+ resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
+ engines: {node: '>= 0.4'}
string.prototype.matchall@4.0.11:
resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==}
@@ -2838,11 +3572,19 @@ packages:
peerDependencies:
react: '>=17.0'
+ swagger2openapi@7.0.8:
+ resolution: {integrity: sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==}
+ hasBin: true
+
+ symbol-observable@4.0.0:
+ resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==}
+ engines: {node: '>=0.10'}
+
tailwind-gradient-mask-image@1.2.0:
resolution: {integrity: sha512-tUJaGhvqbJFiVKJu6EU5n//KvGdVvY3L3VOFNqjztk13+ifAk00pcSNHBTgHfUiBGOEzDn0gFRbSmsftUV1lXA==}
- tailwindcss@3.4.13:
- resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==}
+ tailwindcss@3.4.14:
+ resolution: {integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -2871,14 +3613,17 @@ packages:
peerDependencies:
three: '>= 0.151.0'
- three-stdlib@2.33.0:
- resolution: {integrity: sha512-V/uycBuqQOP/3Z+FBtpMdj2Ds5PyfJ3VDfMzktEmG4niOIzv7q1y5uMSbMcng0+057m1l0N147FQxsodQo9zBg==}
+ three-stdlib@2.30.3:
+ resolution: {integrity: sha512-rYr8PqMljMza+Ct8kQk90Y7y+YcWoPu1thfYv5YGCp0hytNRbxSQWXY4GpdTGymCj3bDggEBpxso53C3pPwhIw==}
peerDependencies:
three: '>=0.128.0'
three@0.163.0:
resolution: {integrity: sha512-HlMgCb2TF/dTLRtknBnjUTsR8FsDqBY43itYop2+Zg822I+Kd0Ua2vs8CvfBVefXkBdNDrLMoRTGCIIpfCuDew==}
+ tiny-case@1.0.3:
+ resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==}
+
tinycolor2@1.6.0:
resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
@@ -2890,6 +3635,12 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
+ toposort@2.0.2:
+ resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==}
+
+ tr46@0.0.3:
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+
troika-three-text@0.49.1:
resolution: {integrity: sha512-lXGWxgjJP9kw4i4Wh+0k0Q/7cRfS6iOME4knKht/KozPu9GcFA9NnNpRvehIhrUawq9B0ZRw+0oiFHgRO+4Wig==}
peerDependencies:
@@ -2912,6 +3663,10 @@ packages:
ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+ ts-invariant@0.10.3:
+ resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==}
+ engines: {node: '>=8'}
+
ts-node@9.1.1:
resolution: {integrity: sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==}
engines: {node: '>=10.0.0'}
@@ -2919,26 +3674,50 @@ packages:
peerDependencies:
typescript: '>=2.7'
- ts-pattern@5.4.0:
- resolution: {integrity: sha512-hgfOMfjlrARCnYtGD/xEAkFHDXuSyuqjzFSltyQCbN689uNvoQL20TVN2XFcLMjfNuwSsQGU+xtH6MrjIwhwUg==}
+ ts-pattern@5.5.0:
+ resolution: {integrity: sha512-jqbIpTsa/KKTJYWgPNsFNbLVpwCgzXfFJ1ukNn4I8hMwyQzHMJnk/BqWzggB0xpkILuKzaO/aMYhS0SkaJyKXg==}
tsconfig-paths@3.15.0:
resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
- tslib@2.7.0:
- resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
+ tslib@1.14.1:
+ resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
+
+ tslib@2.6.3:
+ resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
+
+ tsutils@3.21.0:
+ resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
+ engines: {node: '>= 6'}
+ peerDependencies:
+ typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
tunnel-rat@0.1.2:
resolution: {integrity: sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==}
+ typanion@3.14.0:
+ resolution: {integrity: sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==}
+
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
+ type-fest@0.12.0:
+ resolution: {integrity: sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==}
+ engines: {node: '>=10'}
+
type-fest@0.20.2:
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
engines: {node: '>=10'}
+ type-fest@0.21.3:
+ resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
+ engines: {node: '>=10'}
+
+ type-fest@2.19.0:
+ resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
+ engines: {node: '>=12.20'}
+
typed-array-buffer@1.0.2:
resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
engines: {node: '>= 0.4'}
@@ -2955,6 +3734,20 @@ packages:
resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
engines: {node: '>= 0.4'}
+ typescript-eslint@8.8.0:
+ resolution: {integrity: sha512-BjIT/VwJ8+0rVO01ZQ2ZVnjE1svFBiRczcpr1t1Yxt7sT25VSbPfrJtDsQ8uQTy2pilX5nI9gwxhUyLULNentw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ typescript@4.8.2:
+ resolution: {integrity: sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==}
+ engines: {node: '>=4.2.0'}
+ hasBin: true
+
typescript@4.8.4:
resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==}
engines: {node: '>=4.2.0'}
@@ -2971,6 +3764,16 @@ packages:
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
+ update-browserslist-db@1.0.16:
+ resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
update-browserslist-db@1.1.1:
resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==}
hasBin: true
@@ -2985,8 +3788,8 @@ packages:
peerDependencies:
react: '>=16.8.0'
- use-sync-external-store@1.2.2:
- resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==}
+ use-sync-external-store@1.2.0:
+ resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -3001,8 +3804,8 @@ packages:
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
hasBin: true
- vite@5.4.8:
- resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==}
+ vite@5.4.9:
+ resolution: {integrity: sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -3041,24 +3844,23 @@ packages:
webgl-sdf-generator@1.1.1:
resolution: {integrity: sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==}
+ webidl-conversions@3.0.1:
+ resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+
+ whatwg-url@5.0.0:
+ resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+
which-boxed-primitive@1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
- which-builtin-type@1.1.4:
- resolution: {integrity: sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==}
+ which-builtin-type@1.1.3:
+ resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==}
engines: {node: '>= 0.4'}
- which-collection@1.0.1:
- resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==}
-
which-collection@1.0.2:
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
engines: {node: '>= 0.4'}
- which-typed-array@1.1.11:
- resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==}
- engines: {node: '>= 0.4'}
-
which-typed-array@1.1.15:
resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==}
engines: {node: '>= 0.4'}
@@ -3068,10 +3870,18 @@ packages:
engines: {node: '>= 8'}
hasBin: true
+ widest-line@3.1.0:
+ resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==}
+ engines: {node: '>=8'}
+
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
+ wrap-ansi@6.2.0:
+ resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
+ engines: {node: '>=8'}
+
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
@@ -3087,6 +3897,18 @@ packages:
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+ ws@7.5.10:
+ resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
+ engines: {node: '>=8.3.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ^5.0.2
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
@@ -3094,6 +3916,15 @@ packages:
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+ yaml@1.10.2:
+ resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
+ engines: {node: '>= 6'}
+
+ yaml@2.4.5:
+ resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==}
+ engines: {node: '>= 14'}
+ hasBin: true
+
yaml@2.5.1:
resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==}
engines: {node: '>= 14'}
@@ -3123,6 +3954,19 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
+ yoga-layout-prebuilt@1.10.0:
+ resolution: {integrity: sha512-YnOmtSbv4MTf7RGJMK0FvZ+KD8OEe/J5BNnR0GHhD8J/XcG/Qvxgszm0Un6FTHWW4uHlTgP0IztiXQnGyIR45g==}
+ engines: {node: '>=8'}
+
+ yup@1.4.0:
+ resolution: {integrity: sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==}
+
+ zen-observable-ts@1.2.5:
+ resolution: {integrity: sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==}
+
+ zen-observable@0.8.15:
+ resolution: {integrity: sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==}
+
zustand@3.7.2:
resolution: {integrity: sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==}
engines: {node: '>=12.7.0'}
@@ -3132,8 +3976,8 @@ packages:
react:
optional: true
- zustand@4.5.5:
- resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==}
+ zustand@4.5.2:
+ resolution: {integrity: sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==}
engines: {node: '>=12.7.0'}
peerDependencies:
'@types/react': '>=16.8'
@@ -3156,58 +4000,154 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
+ '@apollo/client@3.10.6(@types/react@18.3.11)(graphql@15.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@graphql-typed-document-node/core': 3.2.0(graphql@15.9.0)
+ '@wry/caches': 1.0.1
+ '@wry/equality': 0.5.7
+ '@wry/trie': 0.5.0
+ graphql: 15.9.0
+ graphql-tag: 2.12.6(graphql@15.9.0)
+ hoist-non-react-statics: 3.3.2
+ optimism: 0.18.0
+ prop-types: 15.8.1
+ rehackt: 0.1.0(@types/react@18.3.11)(react@18.3.1)
+ response-iterator: 0.2.6
+ symbol-observable: 4.0.0
+ ts-invariant: 0.10.3
+ tslib: 2.6.3
+ zen-observable-ts: 1.2.5
+ optionalDependencies:
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ transitivePeerDependencies:
+ - '@types/react'
+
+ '@babel/code-frame@7.24.7':
+ dependencies:
+ '@babel/highlight': 7.24.7
+ picocolors: 1.0.1
+
'@babel/code-frame@7.25.7':
dependencies:
'@babel/highlight': 7.25.7
- picocolors: 1.1.0
+ picocolors: 1.0.1
+
+ '@babel/compat-data@7.24.7': {}
- '@babel/compat-data@7.25.7': {}
+ '@babel/compat-data@7.25.8': {}
- '@babel/core@7.25.7':
+ '@babel/core@7.24.7':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@babel/code-frame': 7.24.7
+ '@babel/generator': 7.24.7
+ '@babel/helper-compilation-targets': 7.24.7
+ '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+ '@babel/helpers': 7.24.7
+ '@babel/parser': 7.24.7
+ '@babel/template': 7.24.7
+ '@babel/traverse': 7.24.7
+ '@babel/types': 7.24.7
+ convert-source-map: 2.0.0
+ debug: 4.3.5
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/core@7.25.8':
dependencies:
'@ampproject/remapping': 2.3.0
'@babel/code-frame': 7.25.7
'@babel/generator': 7.25.7
'@babel/helper-compilation-targets': 7.25.7
- '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.7)
+ '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8)
'@babel/helpers': 7.25.7
- '@babel/parser': 7.25.7
+ '@babel/parser': 7.25.8
'@babel/template': 7.25.7
'@babel/traverse': 7.25.7
- '@babel/types': 7.25.7
+ '@babel/types': 7.25.8
convert-source-map: 2.0.0
- debug: 4.3.7
+ debug: 4.3.5
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
transitivePeerDependencies:
- supports-color
+ '@babel/generator@7.24.7':
+ dependencies:
+ '@babel/types': 7.24.7
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+ jsesc: 2.5.2
+
'@babel/generator@7.25.7':
dependencies:
- '@babel/types': 7.25.7
+ '@babel/types': 7.25.8
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
jsesc: 3.0.2
+ '@babel/helper-compilation-targets@7.24.7':
+ dependencies:
+ '@babel/compat-data': 7.24.7
+ '@babel/helper-validator-option': 7.24.7
+ browserslist: 4.23.1
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
'@babel/helper-compilation-targets@7.25.7':
dependencies:
- '@babel/compat-data': 7.25.7
+ '@babel/compat-data': 7.25.8
'@babel/helper-validator-option': 7.25.7
browserslist: 4.24.0
lru-cache: 5.1.1
semver: 6.3.1
+ '@babel/helper-environment-visitor@7.24.7':
+ dependencies:
+ '@babel/types': 7.24.7
+
+ '@babel/helper-function-name@7.24.7':
+ dependencies:
+ '@babel/template': 7.24.7
+ '@babel/types': 7.24.7
+
+ '@babel/helper-hoist-variables@7.24.7':
+ dependencies:
+ '@babel/types': 7.24.7
+
+ '@babel/helper-module-imports@7.24.7':
+ dependencies:
+ '@babel/traverse': 7.24.7
+ '@babel/types': 7.24.7
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/helper-module-imports@7.25.7':
dependencies:
'@babel/traverse': 7.25.7
- '@babel/types': 7.25.7
+ '@babel/types': 7.25.8
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)':
+ dependencies:
+ '@babel/core': 7.24.7
+ '@babel/helper-environment-visitor': 7.24.7
+ '@babel/helper-module-imports': 7.24.7
+ '@babel/helper-simple-access': 7.24.7
+ '@babel/helper-split-export-declaration': 7.24.7
+ '@babel/helper-validator-identifier': 7.24.7
transitivePeerDependencies:
- supports-color
- '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.7)':
+ '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.8)':
dependencies:
- '@babel/core': 7.25.7
+ '@babel/core': 7.25.8
'@babel/helper-module-imports': 7.25.7
'@babel/helper-simple-access': 7.25.7
'@babel/helper-validator-identifier': 7.25.7
@@ -3215,70 +4155,130 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@babel/helper-plugin-utils@7.25.7': {}
+ '@babel/helper-plugin-utils@7.24.7': {}
+
+ '@babel/helper-simple-access@7.24.7':
+ dependencies:
+ '@babel/traverse': 7.24.7
+ '@babel/types': 7.24.7
+ transitivePeerDependencies:
+ - supports-color
'@babel/helper-simple-access@7.25.7':
dependencies:
'@babel/traverse': 7.25.7
- '@babel/types': 7.25.7
+ '@babel/types': 7.25.8
transitivePeerDependencies:
- supports-color
+ '@babel/helper-split-export-declaration@7.24.7':
+ dependencies:
+ '@babel/types': 7.24.7
+
+ '@babel/helper-string-parser@7.24.7': {}
+
'@babel/helper-string-parser@7.25.7': {}
+ '@babel/helper-validator-identifier@7.24.7': {}
+
'@babel/helper-validator-identifier@7.25.7': {}
+ '@babel/helper-validator-option@7.24.7': {}
+
'@babel/helper-validator-option@7.25.7': {}
+ '@babel/helpers@7.24.7':
+ dependencies:
+ '@babel/template': 7.24.7
+ '@babel/types': 7.24.7
+
'@babel/helpers@7.25.7':
dependencies:
'@babel/template': 7.25.7
- '@babel/types': 7.25.7
+ '@babel/types': 7.25.8
+
+ '@babel/highlight@7.24.7':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.24.7
+ chalk: 2.4.2
+ js-tokens: 4.0.0
+ picocolors: 1.0.1
'@babel/highlight@7.25.7':
dependencies:
'@babel/helper-validator-identifier': 7.25.7
chalk: 2.4.2
js-tokens: 4.0.0
- picocolors: 1.1.0
+ picocolors: 1.0.1
- '@babel/parser@7.25.7':
+ '@babel/parser@7.24.7':
dependencies:
- '@babel/types': 7.25.7
+ '@babel/types': 7.24.7
- '@babel/plugin-transform-react-jsx-self@7.25.7(@babel/core@7.25.7)':
+ '@babel/parser@7.25.8':
dependencies:
- '@babel/core': 7.25.7
- '@babel/helper-plugin-utils': 7.25.7
+ '@babel/types': 7.25.8
- '@babel/plugin-transform-react-jsx-source@7.25.7(@babel/core@7.25.7)':
+ '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.25.8)':
dependencies:
- '@babel/core': 7.25.7
- '@babel/helper-plugin-utils': 7.25.7
+ '@babel/core': 7.25.8
+ '@babel/helper-plugin-utils': 7.24.7
- '@babel/runtime@7.25.7':
+ '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.25.8)':
+ dependencies:
+ '@babel/core': 7.25.8
+ '@babel/helper-plugin-utils': 7.24.7
+
+ '@babel/runtime@7.24.7':
dependencies:
regenerator-runtime: 0.14.1
+ '@babel/template@7.24.7':
+ dependencies:
+ '@babel/code-frame': 7.24.7
+ '@babel/parser': 7.24.7
+ '@babel/types': 7.24.7
+
'@babel/template@7.25.7':
dependencies:
'@babel/code-frame': 7.25.7
- '@babel/parser': 7.25.7
- '@babel/types': 7.25.7
+ '@babel/parser': 7.25.8
+ '@babel/types': 7.25.8
+
+ '@babel/traverse@7.24.7':
+ dependencies:
+ '@babel/code-frame': 7.24.7
+ '@babel/generator': 7.24.7
+ '@babel/helper-environment-visitor': 7.24.7
+ '@babel/helper-function-name': 7.24.7
+ '@babel/helper-hoist-variables': 7.24.7
+ '@babel/helper-split-export-declaration': 7.24.7
+ '@babel/parser': 7.24.7
+ '@babel/types': 7.24.7
+ debug: 4.3.5
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
'@babel/traverse@7.25.7':
dependencies:
'@babel/code-frame': 7.25.7
'@babel/generator': 7.25.7
- '@babel/parser': 7.25.7
+ '@babel/parser': 7.25.8
'@babel/template': 7.25.7
- '@babel/types': 7.25.7
- debug: 4.3.7
+ '@babel/types': 7.25.8
+ debug: 4.3.5
globals: 11.12.0
transitivePeerDependencies:
- supports-color
- '@babel/types@7.25.7':
+ '@babel/types@7.24.7':
+ dependencies:
+ '@babel/helper-string-parser': 7.24.7
+ '@babel/helper-validator-identifier': 7.24.7
+ to-fast-properties: 2.0.0
+
+ '@babel/types@7.25.8':
dependencies:
'@babel/helper-string-parser': 7.25.7
'@babel/helper-validator-identifier': 7.25.7
@@ -3296,7 +4296,7 @@ snapshots:
'@dword-design/eslint-plugin-import-alias@4.0.9':
dependencies:
- '@babel/core': 7.25.7
+ '@babel/core': 7.24.7
'@dword-design/functions': 5.0.27
babel-plugin-module-resolver: 5.0.2
deepmerge: 4.3.1
@@ -3385,15 +4385,15 @@ snapshots:
eslint: 8.57.1
eslint-visitor-keys: 3.4.3
- '@eslint-community/regexpp@4.11.1': {}
+ '@eslint-community/regexpp@4.10.1': {}
'@eslint/eslintrc@2.1.4':
dependencies:
ajv: 6.12.6
- debug: 4.3.7
+ debug: 4.3.5
espree: 9.6.1
globals: 13.24.0
- ignore: 5.3.2
+ ignore: 5.3.1
import-fresh: 3.3.0
js-yaml: 4.1.0
minimatch: 3.1.2
@@ -3403,6 +4403,8 @@ snapshots:
'@eslint/js@8.57.1': {}
+ '@exodus/schemasafe@1.3.0': {}
+
'@fluent/bundle@0.18.0': {}
'@fluent/react@0.15.2(@fluent/bundle@0.18.0)(react@18.3.1)':
@@ -3420,12 +4422,20 @@ snapshots:
'@formatjs/intl-localematcher@0.2.32':
dependencies:
- tslib: 2.7.0
+ tslib: 2.6.3
+
+ '@graphql-typed-document-node/core@3.2.0(graphql@15.9.0)':
+ dependencies:
+ graphql: 15.9.0
+
+ '@hookform/resolvers@3.6.0(react-hook-form@7.53.0(react@18.3.1))':
+ dependencies:
+ react-hook-form: 7.53.0(react@18.3.1)
'@humanwhocodes/config-array@0.13.0':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
- debug: 4.3.7
+ debug: 4.3.5
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -3446,7 +4456,7 @@ snapshots:
'@jridgewell/gen-mapping@0.3.5':
dependencies:
'@jridgewell/set-array': 1.2.1
- '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/sourcemap-codec': 1.4.15
'@jridgewell/trace-mapping': 0.3.25
'@jridgewell/resolve-uri@3.1.2': {}
@@ -3459,18 +4469,18 @@ snapshots:
'@jridgewell/trace-mapping': 0.3.25
optional: true
- '@jridgewell/sourcemap-codec@1.5.0': {}
+ '@jridgewell/sourcemap-codec@1.4.15': {}
'@jridgewell/trace-mapping@0.3.25':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
- '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/sourcemap-codec': 1.4.15
'@mediapipe/tasks-vision@0.10.8': {}
'@mgit-at/typescript-flatbuffers-codegen@0.1.3':
dependencies:
- deep-equal: 2.2.2
+ deep-equal: 2.2.3
ebnf: 1.9.1
ebnf-parser: 0.1.10
execa: 4.1.0
@@ -3480,7 +4490,7 @@ snapshots:
typescript: 4.8.4
yargs: 16.2.0
- '@monogrid/gainmap-js@3.0.6(three@0.163.0)':
+ '@monogrid/gainmap-js@3.0.5(three@0.163.0)':
dependencies:
promise-worker-transferable: 1.0.4
three: 0.163.0
@@ -3499,6 +4509,105 @@ snapshots:
'@nolyfill/is-core-module@1.0.39': {}
+ '@openapi-codegen/cli@2.0.2(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@apollo/client': 3.10.6(@types/react@18.3.11)(graphql@15.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@swc/core': 1.6.5
+ case: 1.6.3
+ chalk: 5.3.0
+ cli-highlight: 2.1.11
+ clipanion: 3.2.1(typanion@3.14.0)
+ fs-extra: 10.1.0
+ got: 12.6.1
+ got-fetch: 5.1.10(got@12.6.1)
+ graphql: 15.9.0
+ ink: 3.2.0(@types/react@18.3.11)(react@18.3.1)
+ js-yaml: 4.1.0
+ openapi3-ts: 2.0.2
+ prettier: 3.3.3
+ rxjs: 7.8.1
+ slash: 4.0.0
+ swagger2openapi: 7.0.8
+ tslib: 2.6.3
+ typanion: 3.14.0
+ typescript: 4.8.2
+ transitivePeerDependencies:
+ - '@swc/helpers'
+ - '@types/react'
+ - bufferutil
+ - encoding
+ - graphql-ws
+ - react
+ - react-dom
+ - subscriptions-transport-ws
+ - utf-8-validate
+
+ '@openapi-codegen/typescript@8.0.2':
+ dependencies:
+ case: 1.6.3
+ lodash: 4.17.21
+ openapi3-ts: 2.0.2
+ pluralize: 8.0.0
+ tslib: 2.6.3
+ tsutils: 3.21.0(typescript@4.8.2)
+ typescript: 4.8.2
+
+ '@parcel/watcher-android-arm64@2.4.1':
+ optional: true
+
+ '@parcel/watcher-darwin-arm64@2.4.1':
+ optional: true
+
+ '@parcel/watcher-darwin-x64@2.4.1':
+ optional: true
+
+ '@parcel/watcher-freebsd-x64@2.4.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-glibc@2.4.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-glibc@2.4.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-musl@2.4.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-glibc@2.4.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-musl@2.4.1':
+ optional: true
+
+ '@parcel/watcher-win32-arm64@2.4.1':
+ optional: true
+
+ '@parcel/watcher-win32-ia32@2.4.1':
+ optional: true
+
+ '@parcel/watcher-win32-x64@2.4.1':
+ optional: true
+
+ '@parcel/watcher@2.4.1':
+ dependencies:
+ detect-libc: 1.0.3
+ is-glob: 4.0.3
+ micromatch: 4.0.7
+ node-addon-api: 7.1.1
+ optionalDependencies:
+ '@parcel/watcher-android-arm64': 2.4.1
+ '@parcel/watcher-darwin-arm64': 2.4.1
+ '@parcel/watcher-darwin-x64': 2.4.1
+ '@parcel/watcher-freebsd-x64': 2.4.1
+ '@parcel/watcher-linux-arm-glibc': 2.4.1
+ '@parcel/watcher-linux-arm64-glibc': 2.4.1
+ '@parcel/watcher-linux-arm64-musl': 2.4.1
+ '@parcel/watcher-linux-x64-glibc': 2.4.1
+ '@parcel/watcher-linux-x64-musl': 2.4.1
+ '@parcel/watcher-win32-arm64': 2.4.1
+ '@parcel/watcher-win32-ia32': 2.4.1
+ '@parcel/watcher-win32-x64': 2.4.1
+
'@pkgjs/parseargs@0.11.0':
optional: true
@@ -3536,20 +4645,20 @@ snapshots:
'@react-spring/types@9.6.1': {}
- '@react-three/drei@9.114.3(@react-three/fiber@8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(@types/react@18.3.11)(@types/three@0.163.0)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0)':
+ '@react-three/drei@9.114.5(@react-three/fiber@8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(@types/react@18.3.11)(@types/three@0.163.0)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0)':
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.24.7
'@mediapipe/tasks-vision': 0.10.8
- '@monogrid/gainmap-js': 3.0.6(three@0.163.0)
+ '@monogrid/gainmap-js': 3.0.5(three@0.163.0)
'@react-spring/three': 9.6.1(@react-three/fiber@8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(react@18.3.1)(three@0.163.0)
'@react-three/fiber': 8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0)
'@use-gesture/react': 10.3.1(react@18.3.1)
- camera-controls: 2.9.0(three@0.163.0)
+ camera-controls: 2.8.5(three@0.163.0)
cross-env: 7.0.3
- detect-gpu: 5.0.51
+ detect-gpu: 5.0.38
glsl-noise: 0.0.0
- hls.js: 1.3.5
- maath: 0.10.8(@types/three@0.163.0)(three@0.163.0)
+ hls.js: 1.5.17
+ maath: 0.10.7(@types/three@0.163.0)(three@0.163.0)
meshline: 3.3.1(three@0.163.0)
react: 18.3.1
react-composer: 5.0.3(react@18.3.1)
@@ -3558,7 +4667,7 @@ snapshots:
suspend-react: 0.1.3(react@18.3.1)
three: 0.163.0
three-mesh-bvh: 0.7.8(three@0.163.0)
- three-stdlib: 2.33.0(three@0.163.0)
+ three-stdlib: 2.30.3(three@0.163.0)
troika-three-text: 0.49.1(three@0.163.0)
tunnel-rat: 0.1.2(@types/react@18.3.11)(immer@9.0.21)(react@18.3.1)
utility-types: 3.11.0
@@ -3573,10 +4682,10 @@ snapshots:
'@react-three/fiber@8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0)':
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.24.7
'@types/debounce': 1.2.4
'@types/react-reconciler': 0.26.7
- '@types/webxr': 0.5.20
+ '@types/webxr': 0.5.16
base64-js: 1.5.1
buffer: 6.0.3
debounce: 1.2.1
@@ -3590,7 +4699,7 @@ snapshots:
optionalDependencies:
react-dom: 18.3.1(react@18.3.1)
- '@remix-run/router@1.19.2': {}
+ '@remix-run/router@1.20.0': {}
'@rollup/rollup-android-arm-eabi@4.24.0':
optional: true
@@ -3642,55 +4751,120 @@ snapshots:
'@rtsao/scc@1.1.0': {}
- '@tailwindcss/forms@0.5.9(tailwindcss@3.4.13(ts-node@9.1.1(typescript@5.6.3)))':
+ '@sindresorhus/is@5.6.0': {}
+
+ '@swc/core-darwin-arm64@1.6.5':
+ optional: true
+
+ '@swc/core-darwin-x64@1.6.5':
+ optional: true
+
+ '@swc/core-linux-arm-gnueabihf@1.6.5':
+ optional: true
+
+ '@swc/core-linux-arm64-gnu@1.6.5':
+ optional: true
+
+ '@swc/core-linux-arm64-musl@1.6.5':
+ optional: true
+
+ '@swc/core-linux-x64-gnu@1.6.5':
+ optional: true
+
+ '@swc/core-linux-x64-musl@1.6.5':
+ optional: true
+
+ '@swc/core-win32-arm64-msvc@1.6.5':
+ optional: true
+
+ '@swc/core-win32-ia32-msvc@1.6.5':
+ optional: true
+
+ '@swc/core-win32-x64-msvc@1.6.5':
+ optional: true
+
+ '@swc/core@1.6.5':
+ dependencies:
+ '@swc/counter': 0.1.3
+ '@swc/types': 0.1.9
+ optionalDependencies:
+ '@swc/core-darwin-arm64': 1.6.5
+ '@swc/core-darwin-x64': 1.6.5
+ '@swc/core-linux-arm-gnueabihf': 1.6.5
+ '@swc/core-linux-arm64-gnu': 1.6.5
+ '@swc/core-linux-arm64-musl': 1.6.5
+ '@swc/core-linux-x64-gnu': 1.6.5
+ '@swc/core-linux-x64-musl': 1.6.5
+ '@swc/core-win32-arm64-msvc': 1.6.5
+ '@swc/core-win32-ia32-msvc': 1.6.5
+ '@swc/core-win32-x64-msvc': 1.6.5
+
+ '@swc/counter@0.1.3': {}
+
+ '@swc/types@0.1.9':
+ dependencies:
+ '@swc/counter': 0.1.3
+
+ '@szmarczak/http-timer@5.0.1':
+ dependencies:
+ defer-to-connect: 2.0.1
+
+ '@tailwindcss/forms@0.5.9(tailwindcss@3.4.14(ts-node@9.1.1(typescript@5.6.3)))':
dependencies:
mini-svg-data-uri: 1.4.4
- tailwindcss: 3.4.13(ts-node@9.1.1(typescript@5.6.3))
+ tailwindcss: 3.4.14(ts-node@9.1.1(typescript@5.6.3))
+
+ '@tanstack/query-core@5.48.0': {}
+
+ '@tanstack/react-query@5.48.0(react@18.3.1)':
+ dependencies:
+ '@tanstack/query-core': 5.48.0
+ react: 18.3.1
'@tauri-apps/api@2.0.2': {}
- '@tauri-apps/cli-darwin-arm64@2.0.2':
+ '@tauri-apps/cli-darwin-arm64@2.0.3':
optional: true
- '@tauri-apps/cli-darwin-x64@2.0.2':
+ '@tauri-apps/cli-darwin-x64@2.0.3':
optional: true
- '@tauri-apps/cli-linux-arm-gnueabihf@2.0.2':
+ '@tauri-apps/cli-linux-arm-gnueabihf@2.0.3':
optional: true
- '@tauri-apps/cli-linux-arm64-gnu@2.0.2':
+ '@tauri-apps/cli-linux-arm64-gnu@2.0.3':
optional: true
- '@tauri-apps/cli-linux-arm64-musl@2.0.2':
+ '@tauri-apps/cli-linux-arm64-musl@2.0.3':
optional: true
- '@tauri-apps/cli-linux-x64-gnu@2.0.2':
+ '@tauri-apps/cli-linux-x64-gnu@2.0.3':
optional: true
- '@tauri-apps/cli-linux-x64-musl@2.0.2':
+ '@tauri-apps/cli-linux-x64-musl@2.0.3':
optional: true
- '@tauri-apps/cli-win32-arm64-msvc@2.0.2':
+ '@tauri-apps/cli-win32-arm64-msvc@2.0.3':
optional: true
- '@tauri-apps/cli-win32-ia32-msvc@2.0.2':
+ '@tauri-apps/cli-win32-ia32-msvc@2.0.3':
optional: true
- '@tauri-apps/cli-win32-x64-msvc@2.0.2':
+ '@tauri-apps/cli-win32-x64-msvc@2.0.3':
optional: true
- '@tauri-apps/cli@2.0.2':
+ '@tauri-apps/cli@2.0.3':
optionalDependencies:
- '@tauri-apps/cli-darwin-arm64': 2.0.2
- '@tauri-apps/cli-darwin-x64': 2.0.2
- '@tauri-apps/cli-linux-arm-gnueabihf': 2.0.2
- '@tauri-apps/cli-linux-arm64-gnu': 2.0.2
- '@tauri-apps/cli-linux-arm64-musl': 2.0.2
- '@tauri-apps/cli-linux-x64-gnu': 2.0.2
- '@tauri-apps/cli-linux-x64-musl': 2.0.2
- '@tauri-apps/cli-win32-arm64-msvc': 2.0.2
- '@tauri-apps/cli-win32-ia32-msvc': 2.0.2
- '@tauri-apps/cli-win32-x64-msvc': 2.0.2
+ '@tauri-apps/cli-darwin-arm64': 2.0.3
+ '@tauri-apps/cli-darwin-x64': 2.0.3
+ '@tauri-apps/cli-linux-arm-gnueabihf': 2.0.3
+ '@tauri-apps/cli-linux-arm64-gnu': 2.0.3
+ '@tauri-apps/cli-linux-arm64-musl': 2.0.3
+ '@tauri-apps/cli-linux-x64-gnu': 2.0.3
+ '@tauri-apps/cli-linux-x64-musl': 2.0.3
+ '@tauri-apps/cli-win32-arm64-msvc': 2.0.3
+ '@tauri-apps/cli-win32-ia32-msvc': 2.0.3
+ '@tauri-apps/cli-win32-x64-msvc': 2.0.3
'@tauri-apps/plugin-dialog@2.0.0':
dependencies:
@@ -3712,28 +4886,28 @@ snapshots:
dependencies:
'@tauri-apps/api': 2.0.2
- '@tweenjs/tween.js@23.1.3': {}
+ '@tweenjs/tween.js@23.1.2': {}
'@types/babel__core@7.20.5':
dependencies:
- '@babel/parser': 7.25.7
- '@babel/types': 7.25.7
+ '@babel/parser': 7.24.7
+ '@babel/types': 7.24.7
'@types/babel__generator': 7.6.8
'@types/babel__template': 7.4.4
'@types/babel__traverse': 7.20.6
'@types/babel__generator@7.6.8':
dependencies:
- '@babel/types': 7.25.7
+ '@babel/types': 7.24.7
'@types/babel__template@7.4.4':
dependencies:
- '@babel/parser': 7.25.7
- '@babel/types': 7.25.7
+ '@babel/parser': 7.24.7
+ '@babel/types': 7.24.7
'@types/babel__traverse@7.20.6':
dependencies:
- '@babel/types': 7.25.7
+ '@babel/types': 7.24.7
'@types/debounce@1.2.4': {}
@@ -3743,6 +4917,8 @@ snapshots:
'@types/file-saver@2.0.7': {}
+ '@types/http-cache-semantics@4.0.4': {}
+
'@types/json5@0.0.29': {}
'@types/node@20.14.2':
@@ -3752,7 +4928,7 @@ snapshots:
'@types/offscreencanvas@2019.7.3': {}
- '@types/prop-types@15.7.13': {}
+ '@types/prop-types@15.7.12': {}
'@types/react-dom@18.3.0':
dependencies:
@@ -3776,7 +4952,7 @@ snapshots:
'@types/react@18.3.11':
dependencies:
- '@types/prop-types': 15.7.13
+ '@types/prop-types': 15.7.12
csstype: 3.1.3
'@types/semver@7.5.8': {}
@@ -3785,17 +4961,19 @@ snapshots:
'@types/three@0.163.0':
dependencies:
- '@tweenjs/tween.js': 23.1.3
+ '@tweenjs/tween.js': 23.1.2
'@types/stats.js': 0.17.3
- '@types/webxr': 0.5.20
+ '@types/webxr': 0.5.16
fflate: 0.8.2
meshoptimizer: 0.18.1
- '@types/webxr@0.5.20': {}
+ '@types/webxr@0.5.16': {}
+
+ '@types/yoga-layout@1.9.2': {}
'@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)':
dependencies:
- '@eslint-community/regexpp': 4.11.1
+ '@eslint-community/regexpp': 4.10.1
'@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.6.3)
'@typescript-eslint/scope-manager': 7.18.0
'@typescript-eslint/type-utils': 7.18.0(eslint@8.57.1)(typescript@5.6.3)
@@ -3803,7 +4981,25 @@ snapshots:
'@typescript-eslint/visitor-keys': 7.18.0
eslint: 8.57.1
graphemer: 1.4.0
- ignore: 5.3.2
+ ignore: 5.3.1
+ natural-compare: 1.4.0
+ ts-api-utils: 1.3.0(typescript@5.6.3)
+ optionalDependencies:
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/eslint-plugin@8.8.0(@typescript-eslint/parser@8.8.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)':
+ dependencies:
+ '@eslint-community/regexpp': 4.10.1
+ '@typescript-eslint/parser': 8.8.0(eslint@8.57.1)(typescript@5.6.3)
+ '@typescript-eslint/scope-manager': 8.8.0
+ '@typescript-eslint/type-utils': 8.8.0(eslint@8.57.1)(typescript@5.6.3)
+ '@typescript-eslint/utils': 8.8.0(eslint@8.57.1)(typescript@5.6.3)
+ '@typescript-eslint/visitor-keys': 8.8.0
+ eslint: 8.57.1
+ graphemer: 1.4.0
+ ignore: 5.3.1
natural-compare: 1.4.0
ts-api-utils: 1.3.0(typescript@5.6.3)
optionalDependencies:
@@ -3817,7 +5013,20 @@ snapshots:
'@typescript-eslint/types': 7.18.0
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3)
'@typescript-eslint/visitor-keys': 7.18.0
- debug: 4.3.7
+ debug: 4.3.5
+ eslint: 8.57.1
+ optionalDependencies:
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.8.0(eslint@8.57.1)(typescript@5.6.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.8.0
+ '@typescript-eslint/types': 8.8.0
+ '@typescript-eslint/typescript-estree': 8.8.0(typescript@5.6.3)
+ '@typescript-eslint/visitor-keys': 8.8.0
+ debug: 4.3.5
eslint: 8.57.1
optionalDependencies:
typescript: 5.6.3
@@ -3829,11 +5038,16 @@ snapshots:
'@typescript-eslint/types': 7.18.0
'@typescript-eslint/visitor-keys': 7.18.0
+ '@typescript-eslint/scope-manager@8.8.0':
+ dependencies:
+ '@typescript-eslint/types': 8.8.0
+ '@typescript-eslint/visitor-keys': 8.8.0
+
'@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.6.3)':
dependencies:
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3)
'@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.6.3)
- debug: 4.3.7
+ debug: 4.3.5
eslint: 8.57.1
ts-api-utils: 1.3.0(typescript@5.6.3)
optionalDependencies:
@@ -3841,16 +5055,45 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@typescript-eslint/type-utils@8.8.0(eslint@8.57.1)(typescript@5.6.3)':
+ dependencies:
+ '@typescript-eslint/typescript-estree': 8.8.0(typescript@5.6.3)
+ '@typescript-eslint/utils': 8.8.0(eslint@8.57.1)(typescript@5.6.3)
+ debug: 4.3.5
+ ts-api-utils: 1.3.0(typescript@5.6.3)
+ optionalDependencies:
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - eslint
+ - supports-color
+
'@typescript-eslint/types@7.18.0': {}
+ '@typescript-eslint/types@8.8.0': {}
+
'@typescript-eslint/typescript-estree@7.18.0(typescript@5.6.3)':
dependencies:
'@typescript-eslint/types': 7.18.0
'@typescript-eslint/visitor-keys': 7.18.0
- debug: 4.3.7
+ debug: 4.3.5
globby: 11.1.0
is-glob: 4.0.3
- minimatch: 9.0.5
+ minimatch: 9.0.4
+ semver: 7.6.3
+ ts-api-utils: 1.3.0(typescript@5.6.3)
+ optionalDependencies:
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/typescript-estree@8.8.0(typescript@5.6.3)':
+ dependencies:
+ '@typescript-eslint/types': 8.8.0
+ '@typescript-eslint/visitor-keys': 8.8.0
+ debug: 4.3.5
+ fast-glob: 3.3.2
+ is-glob: 4.0.3
+ minimatch: 9.0.4
semver: 7.6.3
ts-api-utils: 1.3.0(typescript@5.6.3)
optionalDependencies:
@@ -3869,11 +5112,27 @@ snapshots:
- supports-color
- typescript
+ '@typescript-eslint/utils@8.8.0(eslint@8.57.1)(typescript@5.6.3)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1)
+ '@typescript-eslint/scope-manager': 8.8.0
+ '@typescript-eslint/types': 8.8.0
+ '@typescript-eslint/typescript-estree': 8.8.0(typescript@5.6.3)
+ eslint: 8.57.1
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
'@typescript-eslint/visitor-keys@7.18.0':
dependencies:
'@typescript-eslint/types': 7.18.0
eslint-visitor-keys: 3.4.3
+ '@typescript-eslint/visitor-keys@8.8.0':
+ dependencies:
+ '@typescript-eslint/types': 8.8.0
+ eslint-visitor-keys: 3.4.3
+
'@ungap/structured-clone@1.2.0': {}
'@use-gesture/core@10.3.1': {}
@@ -3883,22 +5142,42 @@ snapshots:
'@use-gesture/core': 10.3.1
react: 18.3.1
- '@vitejs/plugin-react@4.3.2(vite@5.4.8(@types/node@20.14.2)(sass@1.79.4)(terser@5.31.1))':
+ '@vitejs/plugin-react@4.3.2(vite@5.4.9(@types/node@20.14.2)(sass@1.80.2)(terser@5.31.1))':
dependencies:
- '@babel/core': 7.25.7
- '@babel/plugin-transform-react-jsx-self': 7.25.7(@babel/core@7.25.7)
- '@babel/plugin-transform-react-jsx-source': 7.25.7(@babel/core@7.25.7)
+ '@babel/core': 7.25.8
+ '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.8)
+ '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.8)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
- vite: 5.4.8(@types/node@20.14.2)(sass@1.79.4)(terser@5.31.1)
+ vite: 5.4.9(@types/node@20.14.2)(sass@1.80.2)(terser@5.31.1)
transitivePeerDependencies:
- supports-color
- acorn-jsx@5.3.2(acorn@8.12.1):
+ '@wry/caches@1.0.1':
+ dependencies:
+ tslib: 2.6.3
+
+ '@wry/context@0.7.4':
+ dependencies:
+ tslib: 2.6.3
+
+ '@wry/equality@0.5.7':
+ dependencies:
+ tslib: 2.6.3
+
+ '@wry/trie@0.4.3':
+ dependencies:
+ tslib: 2.6.3
+
+ '@wry/trie@0.5.0':
+ dependencies:
+ tslib: 2.6.3
+
+ acorn-jsx@5.3.2(acorn@8.12.0):
dependencies:
- acorn: 8.12.1
+ acorn: 8.12.0
- acorn@8.12.1: {}
+ acorn@8.12.0: {}
ajv@6.12.6:
dependencies:
@@ -3907,6 +5186,10 @@ snapshots:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
+ ansi-escapes@4.3.2:
+ dependencies:
+ type-fest: 0.21.3
+
ansi-escapes@7.0.0:
dependencies:
environment: 1.1.0
@@ -3942,11 +5225,6 @@ snapshots:
dependencies:
deep-equal: 2.2.3
- array-buffer-byte-length@1.0.0:
- dependencies:
- call-bind: 1.0.2
- is-array-buffer: 3.0.2
-
array-buffer-byte-length@1.0.1:
dependencies:
call-bind: 1.0.7
@@ -4018,23 +5296,25 @@ snapshots:
ast-types-flow@0.0.8: {}
- autoprefixer@10.4.20(postcss@8.4.47):
+ astral-regex@2.0.0: {}
+
+ auto-bind@4.0.0: {}
+
+ autoprefixer@10.4.20(postcss@8.4.38):
dependencies:
browserslist: 4.24.0
- caniuse-lite: 1.0.30001667
+ caniuse-lite: 1.0.30001669
fraction.js: 4.3.7
normalize-range: 0.1.2
- picocolors: 1.1.0
- postcss: 8.4.47
+ picocolors: 1.0.1
+ postcss: 8.4.38
postcss-value-parser: 4.2.0
- available-typed-arrays@1.0.5: {}
-
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.0.0
- axe-core@4.10.0: {}
+ axe-core@4.10.1: {}
axobject-query@4.1.0: {}
@@ -4042,7 +5322,7 @@ snapshots:
babel-plugin-module-resolver@5.0.2:
dependencies:
- find-babel-config: 2.1.2
+ find-babel-config: 2.1.1
glob: 9.3.5
pkg-up: 3.1.0
reselect: 4.1.8
@@ -4073,10 +5353,17 @@ snapshots:
browser-fs-access@0.35.0: {}
+ browserslist@4.23.1:
+ dependencies:
+ caniuse-lite: 1.0.30001636
+ electron-to-chromium: 1.4.803
+ node-releases: 2.0.14
+ update-browserslist-db: 1.0.16(browserslist@4.23.1)
+
browserslist@4.24.0:
dependencies:
- caniuse-lite: 1.0.30001667
- electron-to-chromium: 1.5.34
+ caniuse-lite: 1.0.30001669
+ electron-to-chromium: 1.5.41
node-releases: 2.0.18
update-browserslist-db: 1.1.1(browserslist@4.24.0)
@@ -4087,6 +5374,18 @@ snapshots:
base64-js: 1.5.1
ieee754: 1.2.1
+ cacheable-lookup@7.0.0: {}
+
+ cacheable-request@10.2.14:
+ dependencies:
+ '@types/http-cache-semantics': 4.0.4
+ get-stream: 6.0.1
+ http-cache-semantics: 4.1.1
+ keyv: 4.5.4
+ mimic-response: 4.0.0
+ normalize-url: 8.0.1
+ responselike: 3.0.0
+
cached-iterable@0.3.0: {}
call-bind@1.0.2:
@@ -4102,15 +5401,21 @@ snapshots:
get-intrinsic: 1.2.4
set-function-length: 1.2.2
+ call-me-maybe@1.0.2: {}
+
callsites@3.1.0: {}
camelcase-css@2.0.1: {}
- camera-controls@2.9.0(three@0.163.0):
+ camera-controls@2.8.5(three@0.163.0):
dependencies:
three: 0.163.0
- caniuse-lite@1.0.30001667: {}
+ caniuse-lite@1.0.30001636: {}
+
+ caniuse-lite@1.0.30001669: {}
+
+ case@1.6.3: {}
chalk@2.4.2:
dependencies:
@@ -4141,16 +5446,42 @@ snapshots:
dependencies:
readdirp: 4.0.2
+ ci-info@2.0.0: {}
+
classnames@2.5.1: {}
+ cli-boxes@2.2.1: {}
+
+ cli-cursor@3.1.0:
+ dependencies:
+ restore-cursor: 3.1.0
+
cli-cursor@5.0.0:
dependencies:
restore-cursor: 5.1.0
+ cli-highlight@2.1.11:
+ dependencies:
+ chalk: 4.1.2
+ highlight.js: 10.7.3
+ mz: 2.7.0
+ parse5: 5.1.1
+ parse5-htmlparser2-tree-adapter: 6.0.1
+ yargs: 16.2.0
+
+ cli-truncate@2.1.0:
+ dependencies:
+ slice-ansi: 3.0.0
+ string-width: 4.2.3
+
cli-truncate@4.0.0:
dependencies:
slice-ansi: 5.0.0
- string-width: 7.2.0
+ string-width: 7.1.0
+
+ clipanion@3.2.1(typanion@3.14.0):
+ dependencies:
+ typanion: 3.14.0
cliui@7.0.4:
dependencies:
@@ -4164,6 +5495,10 @@ snapshots:
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
+ code-excerpt@3.0.0:
+ dependencies:
+ convert-to-spaces: 1.0.2
+
color-convert@1.9.3:
dependencies:
color-name: 1.1.3
@@ -4191,6 +5526,8 @@ snapshots:
convert-source-map@2.0.0: {}
+ convert-to-spaces@1.0.2: {}
+
create-require@1.1.1: {}
cross-env@7.0.3:
@@ -4235,30 +5572,17 @@ snapshots:
dependencies:
ms: 2.1.3
+ debug@4.3.5:
+ dependencies:
+ ms: 2.1.2
+
debug@4.3.7:
dependencies:
ms: 2.1.3
- deep-equal@2.2.2:
+ decompress-response@6.0.0:
dependencies:
- array-buffer-byte-length: 1.0.0
- call-bind: 1.0.2
- es-get-iterator: 1.1.3
- get-intrinsic: 1.2.1
- is-arguments: 1.1.1
- is-array-buffer: 3.0.2
- is-date-object: 1.0.5
- is-regex: 1.1.4
- is-shared-array-buffer: 1.0.2
- isarray: 2.0.5
- object-is: 1.1.5
- object-keys: 1.1.1
- object.assign: 4.1.5
- regexp.prototype.flags: 1.5.1
- side-channel: 1.0.4
- which-boxed-primitive: 1.0.2
- which-collection: 1.0.1
- which-typed-array: 1.1.11
+ mimic-response: 3.1.0
deep-equal@2.2.3:
dependencies:
@@ -4275,7 +5599,7 @@ snapshots:
object-is: 1.1.6
object-keys: 1.1.1
object.assign: 4.1.5
- regexp.prototype.flags: 1.5.3
+ regexp.prototype.flags: 1.5.2
side-channel: 1.0.6
which-boxed-primitive: 1.0.2
which-collection: 1.0.2
@@ -4285,6 +5609,8 @@ snapshots:
deepmerge@4.3.1: {}
+ defer-to-connect@2.0.1: {}
+
define-data-property@1.1.4:
dependencies:
es-define-property: 1.0.0
@@ -4301,10 +5627,12 @@ snapshots:
delay@5.0.0: {}
- detect-gpu@5.0.51:
+ detect-gpu@5.0.38:
dependencies:
webgl-constants: 1.1.1
+ detect-libc@1.0.3: {}
+
didyoumean@1.2.2: {}
diff@4.0.2: {}
@@ -4323,6 +5651,8 @@ snapshots:
dependencies:
esutils: 2.0.3
+ dotenv@16.4.5: {}
+
draco3d@1.5.7: {}
eastasianwidth@0.2.0: {}
@@ -4331,9 +5661,11 @@ snapshots:
ebnf@1.9.1: {}
- electron-to-chromium@1.5.34: {}
+ electron-to-chromium@1.4.803: {}
+
+ electron-to-chromium@1.5.41: {}
- emoji-regex@10.4.0: {}
+ emoji-regex@10.3.0: {}
emoji-regex@8.0.0: {}
@@ -4343,7 +5675,7 @@ snapshots:
dependencies:
once: 1.4.0
- enhanced-resolve@5.17.1:
+ enhanced-resolve@5.17.0:
dependencies:
graceful-fs: 4.2.11
tapable: 2.2.1
@@ -4383,10 +5715,10 @@ snapshots:
is-string: 1.0.7
is-typed-array: 1.1.13
is-weakref: 1.0.2
- object-inspect: 1.13.2
+ object-inspect: 1.13.1
object-keys: 1.1.1
object.assign: 4.1.5
- regexp.prototype.flags: 1.5.3
+ regexp.prototype.flags: 1.5.2
safe-array-concat: 1.1.2
safe-regex-test: 1.0.3
string.prototype.trim: 1.2.9
@@ -4407,17 +5739,17 @@ snapshots:
es-get-iterator@1.1.3:
dependencies:
- call-bind: 1.0.2
- get-intrinsic: 1.2.1
+ call-bind: 1.0.7
+ get-intrinsic: 1.2.4
has-symbols: 1.0.3
is-arguments: 1.1.1
- is-map: 2.0.2
- is-set: 2.0.2
+ is-map: 2.0.3
+ is-set: 2.0.3
is-string: 1.0.7
isarray: 2.0.5
stop-iteration-iterator: 1.0.0
- es-iterator-helpers@1.1.0:
+ es-iterator-helpers@1.0.19:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
@@ -4431,7 +5763,7 @@ snapshots:
has-proto: 1.0.3
has-symbols: 1.0.3
internal-slot: 1.0.7
- iterator.prototype: 1.1.3
+ iterator.prototype: 1.1.2
safe-array-concat: 1.1.2
es-object-atoms@1.0.0:
@@ -4454,6 +5786,8 @@ snapshots:
is-date-object: 1.0.5
is-symbol: 1.0.4
+ es6-promise@3.3.1: {}
+
esbuild@0.21.5:
optionalDependencies:
'@esbuild/aix-ppc64': 0.21.5
@@ -4480,12 +5814,14 @@ snapshots:
'@esbuild/win32-ia32': 0.21.5
'@esbuild/win32-x64': 0.21.5
- escalade@3.1.1: {}
+ escalade@3.1.2: {}
escalade@3.2.0: {}
escape-string-regexp@1.0.5: {}
+ escape-string-regexp@2.0.0: {}
+
escape-string-regexp@4.0.0: {}
eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1):
@@ -4519,12 +5855,12 @@ snapshots:
eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1):
dependencies:
'@nolyfill/is-core-module': 1.0.39
- debug: 4.3.7
- enhanced-resolve: 5.17.1
+ debug: 4.3.5
+ enhanced-resolve: 5.17.0
eslint: 8.57.1
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1)
+ eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1)
fast-glob: 3.3.2
- get-tsconfig: 4.8.1
+ get-tsconfig: 4.7.5
is-bun-module: 1.2.1
is-glob: 4.0.3
optionalDependencies:
@@ -4546,6 +5882,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ eslint-module-utils@2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1):
+ dependencies:
+ debug: 3.2.7
+ optionalDependencies:
+ '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.6.3)
+ eslint: 8.57.1
+ eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint-plugin-import@2.31.0)(eslint@8.57.1)
+ transitivePeerDependencies:
+ - supports-color
+
eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
dependencies:
'@rtsao/scc': 1.1.0
@@ -4581,11 +5927,11 @@ snapshots:
array-includes: 3.1.8
array.prototype.flatmap: 1.3.2
ast-types-flow: 0.0.8
- axe-core: 4.10.0
+ axe-core: 4.10.1
axobject-query: 4.1.0
damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2
- es-iterator-helpers: 1.1.0
+ es-iterator-helpers: 1.0.19
eslint: 8.57.1
hasown: 2.0.2
jsx-ast-utils: 3.3.5
@@ -4593,7 +5939,7 @@ snapshots:
minimatch: 3.1.2
object.fromentries: 2.0.8
safe-regex-test: 1.0.3
- string.prototype.includes: 2.0.0
+ string.prototype.includes: 2.0.1
eslint-plugin-react-hooks@4.6.2(eslint@8.57.1):
dependencies:
@@ -4606,7 +5952,7 @@ snapshots:
array.prototype.flatmap: 1.3.2
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
- es-iterator-helpers: 1.1.0
+ es-iterator-helpers: 1.0.19
eslint: 8.57.1
estraverse: 5.3.0
hasown: 2.0.2
@@ -4631,7 +5977,7 @@ snapshots:
eslint@8.57.1:
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1)
- '@eslint-community/regexpp': 4.11.1
+ '@eslint-community/regexpp': 4.10.1
'@eslint/eslintrc': 2.1.4
'@eslint/js': 8.57.1
'@humanwhocodes/config-array': 0.13.0
@@ -4641,13 +5987,13 @@ snapshots:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
- debug: 4.3.7
+ debug: 4.3.5
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.2
eslint-visitor-keys: 3.4.3
espree: 9.6.1
- esquery: 1.6.0
+ esquery: 1.5.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
file-entry-cache: 6.0.1
@@ -4655,7 +6001,7 @@ snapshots:
glob-parent: 6.0.2
globals: 13.24.0
graphemer: 1.4.0
- ignore: 5.3.2
+ ignore: 5.3.1
imurmurhash: 0.1.4
is-glob: 4.0.3
is-path-inside: 3.0.3
@@ -4673,11 +6019,11 @@ snapshots:
espree@9.6.1:
dependencies:
- acorn: 8.12.1
- acorn-jsx: 5.3.2(acorn@8.12.1)
+ acorn: 8.12.0
+ acorn-jsx: 5.3.2(acorn@8.12.0)
eslint-visitor-keys: 3.4.3
- esquery@1.6.0:
+ esquery@1.5.0:
dependencies:
estraverse: 5.3.0
@@ -4725,7 +6071,7 @@ snapshots:
'@nodelib/fs.walk': 1.2.8
glob-parent: 5.1.2
merge2: 1.4.1
- micromatch: 4.0.8
+ micromatch: 4.0.7
fast-json-parse@1.0.3: {}
@@ -4733,6 +6079,8 @@ snapshots:
fast-levenshtein@2.0.6: {}
+ fast-safe-stringify@2.1.1: {}
+
fastq@1.17.1:
dependencies:
reusify: 1.0.4
@@ -4749,9 +6097,10 @@ snapshots:
dependencies:
to-regex-range: 5.0.1
- find-babel-config@2.1.2:
+ find-babel-config@2.1.1:
dependencies:
json5: 2.2.3
+ path-exists: 4.0.0
find-up@3.0.0:
dependencies:
@@ -4772,21 +6121,27 @@ snapshots:
flatbuffers@22.10.26: {}
- flatbuffers@22.12.6: {}
-
flatted@3.3.1: {}
for-each@0.3.3:
dependencies:
is-callable: 1.2.7
- foreground-child@3.3.0:
+ foreground-child@3.2.1:
dependencies:
cross-spawn: 7.0.3
signal-exit: 4.1.0
+ form-data-encoder@2.1.4: {}
+
fraction.js@4.3.7: {}
+ fs-extra@10.1.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.1.0
+ universalify: 2.0.1
+
fs.realpath@1.0.0: {}
fsevents@2.3.3:
@@ -4828,6 +6183,8 @@ snapshots:
dependencies:
pump: 3.0.0
+ get-stream@6.0.1: {}
+
get-stream@8.0.1: {}
get-symbol-description@1.0.2:
@@ -4836,7 +6193,7 @@ snapshots:
es-errors: 1.3.0
get-intrinsic: 1.2.4
- get-tsconfig@4.8.1:
+ get-tsconfig@4.7.5:
dependencies:
resolve-pkg-maps: 1.0.0
@@ -4848,13 +6205,12 @@ snapshots:
dependencies:
is-glob: 4.0.3
- glob@10.4.5:
+ glob@10.4.1:
dependencies:
- foreground-child: 3.3.0
- jackspeak: 3.4.3
- minimatch: 9.0.5
+ foreground-child: 3.2.1
+ jackspeak: 3.4.0
+ minimatch: 9.0.4
minipass: 7.1.2
- package-json-from-dist: 1.0.1
path-scurry: 1.11.1
glob@7.2.3:
@@ -4879,6 +6235,8 @@ snapshots:
dependencies:
type-fest: 0.20.2
+ globals@15.10.0: {}
+
globalthis@1.0.4:
dependencies:
define-properties: 1.2.1
@@ -4889,7 +6247,7 @@ snapshots:
array-union: 2.1.0
dir-glob: 3.0.1
fast-glob: 3.3.2
- ignore: 5.3.2
+ ignore: 5.3.1
merge2: 1.4.1
slash: 3.0.0
@@ -4899,10 +6257,35 @@ snapshots:
dependencies:
get-intrinsic: 1.2.4
+ got-fetch@5.1.10(got@12.6.1):
+ dependencies:
+ got: 12.6.1
+
+ got@12.6.1:
+ dependencies:
+ '@sindresorhus/is': 5.6.0
+ '@szmarczak/http-timer': 5.0.1
+ cacheable-lookup: 7.0.0
+ cacheable-request: 10.2.14
+ decompress-response: 6.0.0
+ form-data-encoder: 2.1.4
+ get-stream: 6.0.1
+ http2-wrapper: 2.2.1
+ lowercase-keys: 3.0.0
+ p-cancelable: 3.0.0
+ responselike: 3.0.0
+
graceful-fs@4.2.11: {}
graphemer@1.4.0: {}
+ graphql-tag@2.12.6(graphql@15.9.0):
+ dependencies:
+ graphql: 15.9.0
+ tslib: 2.6.3
+
+ graphql@15.9.0: {}
+
has-bigints@1.0.2: {}
has-flag@3.0.0: {}
@@ -4933,7 +6316,22 @@ snapshots:
dependencies:
function-bind: 1.1.2
- hls.js@1.3.5: {}
+ highlight.js@10.7.3: {}
+
+ hls.js@1.5.17: {}
+
+ hoist-non-react-statics@3.3.2:
+ dependencies:
+ react-is: 16.13.1
+
+ http-cache-semantics@4.1.1: {}
+
+ http2-client@1.3.5: {}
+
+ http2-wrapper@2.2.1:
+ dependencies:
+ quick-lru: 5.1.1
+ resolve-alpn: 1.2.1
human-signals@1.1.1: {}
@@ -4945,14 +6343,14 @@ snapshots:
ieee754@1.2.1: {}
- ignore@5.3.2: {}
+ ignore@5.3.1: {}
immediate@3.0.6: {}
immer@9.0.21:
optional: true
- immutable@4.3.7: {}
+ immutable@4.3.6: {}
import-fresh@3.3.0:
dependencies:
@@ -4961,6 +6359,8 @@ snapshots:
imurmurhash@0.1.4: {}
+ indent-string@4.0.0: {}
+
inflight@1.0.6:
dependencies:
once: 1.4.0
@@ -4968,6 +6368,38 @@ snapshots:
inherits@2.0.4: {}
+ ink@3.2.0(@types/react@18.3.11)(react@18.3.1):
+ dependencies:
+ ansi-escapes: 4.3.2
+ auto-bind: 4.0.0
+ chalk: 4.1.2
+ cli-boxes: 2.2.1
+ cli-cursor: 3.1.0
+ cli-truncate: 2.1.0
+ code-excerpt: 3.0.0
+ indent-string: 4.0.0
+ is-ci: 2.0.0
+ lodash: 4.17.21
+ patch-console: 1.0.0
+ react: 18.3.1
+ react-devtools-core: 4.28.5
+ react-reconciler: 0.26.2(react@18.3.1)
+ scheduler: 0.20.2
+ signal-exit: 3.0.7
+ slice-ansi: 3.0.0
+ stack-utils: 2.0.6
+ string-width: 4.2.3
+ type-fest: 0.12.0
+ widest-line: 3.1.0
+ wrap-ansi: 6.2.0
+ ws: 7.5.10
+ yoga-layout-prebuilt: 1.10.0
+ optionalDependencies:
+ '@types/react': 18.3.11
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
internal-slot@1.0.7:
dependencies:
es-errors: 1.3.0
@@ -4980,14 +6412,8 @@ snapshots:
is-arguments@1.1.1:
dependencies:
- call-bind: 1.0.2
- has-tostringtag: 1.0.0
-
- is-array-buffer@3.0.2:
- dependencies:
- call-bind: 1.0.2
- get-intrinsic: 1.2.1
- is-typed-array: 1.1.12
+ call-bind: 1.0.7
+ has-tostringtag: 1.0.2
is-array-buffer@3.0.4:
dependencies:
@@ -5017,6 +6443,14 @@ snapshots:
is-callable@1.2.7: {}
+ is-ci@2.0.0:
+ dependencies:
+ ci-info: 2.0.0
+
+ is-core-module@2.13.1:
+ dependencies:
+ hasown: 2.0.2
+
is-core-module@2.15.1:
dependencies:
hasown: 2.0.2
@@ -5053,8 +6487,6 @@ snapshots:
dependencies:
is-extglob: 2.1.1
- is-map@2.0.2: {}
-
is-map@2.0.3: {}
is-negative-zero@2.0.3: {}
@@ -5074,14 +6506,8 @@ snapshots:
call-bind: 1.0.2
has-tostringtag: 1.0.0
- is-set@2.0.2: {}
-
is-set@2.0.3: {}
- is-shared-array-buffer@1.0.2:
- dependencies:
- call-bind: 1.0.2
-
is-shared-array-buffer@1.0.3:
dependencies:
call-bind: 1.0.7
@@ -5098,27 +6524,16 @@ snapshots:
dependencies:
has-symbols: 1.0.3
- is-typed-array@1.1.12:
- dependencies:
- which-typed-array: 1.1.11
-
is-typed-array@1.1.13:
dependencies:
which-typed-array: 1.1.15
- is-weakmap@2.0.1: {}
-
is-weakmap@2.0.2: {}
is-weakref@1.0.2:
dependencies:
call-bind: 1.0.7
- is-weakset@2.0.2:
- dependencies:
- call-bind: 1.0.2
- get-intrinsic: 1.2.1
-
is-weakset@2.0.3:
dependencies:
call-bind: 1.0.7
@@ -5132,7 +6547,7 @@ snapshots:
isexe@2.0.0: {}
- iterator.prototype@1.1.3:
+ iterator.prototype@1.1.2:
dependencies:
define-properties: 1.2.1
get-intrinsic: 1.2.4
@@ -5145,7 +6560,7 @@ snapshots:
'@types/react-reconciler': 0.28.8
react: 18.3.1
- jackspeak@3.4.3:
+ jackspeak@3.4.0:
dependencies:
'@isaacs/cliui': 8.0.2
optionalDependencies:
@@ -5159,6 +6574,8 @@ snapshots:
dependencies:
argparse: 2.0.1
+ jsesc@2.5.2: {}
+
jsesc@3.0.2: {}
json-buffer@3.0.1: {}
@@ -5173,6 +6590,12 @@ snapshots:
json5@2.2.3: {}
+ jsonfile@6.1.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
jsx-ast-utils@3.3.5:
dependencies:
array-includes: 3.1.8
@@ -5256,13 +6679,15 @@ snapshots:
dependencies:
js-tokens: 4.0.0
- lru-cache@10.4.3: {}
+ lowercase-keys@3.0.0: {}
+
+ lru-cache@10.2.2: {}
lru-cache@5.1.1:
dependencies:
yallist: 3.1.1
- maath@0.10.8(@types/three@0.163.0)(three@0.163.0):
+ maath@0.10.7(@types/three@0.163.0)(three@0.163.0):
dependencies:
'@types/three': 0.163.0
three: 0.163.0
@@ -5283,6 +6708,11 @@ snapshots:
meshoptimizer@0.18.1: {}
+ micromatch@4.0.7:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
micromatch@4.0.8:
dependencies:
braces: 3.0.3
@@ -5294,6 +6724,10 @@ snapshots:
mimic-function@5.0.1: {}
+ mimic-response@3.1.0: {}
+
+ mimic-response@4.0.0: {}
+
mini-svg-data-uri@1.4.4: {}
minimatch@3.1.2:
@@ -5304,7 +6738,7 @@ snapshots:
dependencies:
brace-expansion: 2.0.1
- minimatch@9.0.5:
+ minimatch@9.0.4:
dependencies:
brace-expansion: 2.0.1
@@ -5314,6 +6748,8 @@ snapshots:
minipass@7.1.2: {}
+ ms@2.1.2: {}
+
ms@2.1.3: {}
mz@2.7.0:
@@ -5326,12 +6762,30 @@ snapshots:
natural-compare@1.4.0: {}
+ node-addon-api@7.1.1: {}
+
+ node-fetch-h2@2.3.0:
+ dependencies:
+ http2-client: 1.3.5
+
+ node-fetch@2.7.0:
+ dependencies:
+ whatwg-url: 5.0.0
+
+ node-readfiles@0.2.0:
+ dependencies:
+ es6-promise: 3.3.1
+
+ node-releases@2.0.14: {}
+
node-releases@2.0.18: {}
normalize-path@3.0.0: {}
normalize-range@0.1.2: {}
+ normalize-url@8.0.1: {}
+
npm-run-path@4.0.1:
dependencies:
path-key: 3.1.1
@@ -5340,18 +6794,42 @@ snapshots:
dependencies:
path-key: 4.0.0
- object-assign@4.1.1: {}
+ oas-kit-common@1.0.8:
+ dependencies:
+ fast-safe-stringify: 2.1.1
- object-hash@3.0.0: {}
+ oas-linter@3.2.2:
+ dependencies:
+ '@exodus/schemasafe': 1.3.0
+ should: 13.2.3
+ yaml: 1.10.2
- object-inspect@1.12.3: {}
+ oas-resolver@2.5.6:
+ dependencies:
+ node-fetch-h2: 2.3.0
+ oas-kit-common: 1.0.8
+ reftools: 1.1.9
+ yaml: 1.10.2
+ yargs: 17.7.2
- object-inspect@1.13.2: {}
+ oas-schema-walker@1.1.5: {}
- object-is@1.1.5:
+ oas-validator@5.0.8:
dependencies:
- call-bind: 1.0.2
- define-properties: 1.2.1
+ call-me-maybe: 1.0.2
+ oas-kit-common: 1.0.8
+ oas-linter: 3.2.2
+ oas-resolver: 2.5.6
+ oas-schema-walker: 1.1.5
+ reftools: 1.1.9
+ should: 13.2.3
+ yaml: 1.10.2
+
+ object-assign@4.1.1: {}
+
+ object-hash@3.0.0: {}
+
+ object-inspect@1.13.1: {}
object-is@1.1.6:
dependencies:
@@ -5416,6 +6894,17 @@ snapshots:
is-docker: 2.2.1
is-wsl: 2.2.0
+ openapi3-ts@2.0.2:
+ dependencies:
+ yaml: 1.10.2
+
+ optimism@0.18.0:
+ dependencies:
+ '@wry/caches': 1.0.1
+ '@wry/context': 0.7.4
+ '@wry/trie': 0.4.3
+ tslib: 2.6.3
+
optionator@0.9.4:
dependencies:
deep-is: 0.1.4
@@ -5425,6 +6914,8 @@ snapshots:
type-check: 0.4.0
word-wrap: 1.2.5
+ p-cancelable@3.0.0: {}
+
p-limit@2.3.0:
dependencies:
p-try: 2.2.0
@@ -5443,12 +6934,20 @@ snapshots:
p-try@2.2.0: {}
- package-json-from-dist@1.0.1: {}
-
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
+ parse5-htmlparser2-tree-adapter@6.0.1:
+ dependencies:
+ parse5: 6.0.1
+
+ parse5@5.1.1: {}
+
+ parse5@6.0.1: {}
+
+ patch-console@1.0.0: {}
+
path-exists@3.0.0: {}
path-exists@4.0.0: {}
@@ -5463,14 +6962,16 @@ snapshots:
path-scurry@1.11.1:
dependencies:
- lru-cache: 10.4.3
+ lru-cache: 10.2.2
minipass: 7.1.2
path-type@4.0.0: {}
pegjs@0.10.0: {}
- picocolors@1.1.0: {}
+ picocolors@1.0.1: {}
+
+ picocolors@1.1.1: {}
picomatch@2.3.1: {}
@@ -5484,44 +6985,52 @@ snapshots:
dependencies:
find-up: 3.0.0
+ pluralize@8.0.0: {}
+
possible-typed-array-names@1.0.0: {}
- postcss-import@15.1.0(postcss@8.4.47):
+ postcss-import@15.1.0(postcss@8.4.38):
dependencies:
- postcss: 8.4.47
+ postcss: 8.4.38
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.8
- postcss-js@4.0.1(postcss@8.4.47):
+ postcss-js@4.0.1(postcss@8.4.38):
dependencies:
camelcase-css: 2.0.1
- postcss: 8.4.47
+ postcss: 8.4.38
- postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@9.1.1(typescript@5.6.3)):
+ postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@9.1.1(typescript@5.6.3)):
dependencies:
lilconfig: 3.1.2
- yaml: 2.5.1
+ yaml: 2.4.5
optionalDependencies:
- postcss: 8.4.47
+ postcss: 8.4.38
ts-node: 9.1.1(typescript@5.6.3)
- postcss-nested@6.2.0(postcss@8.4.47):
+ postcss-nested@6.0.1(postcss@8.4.38):
dependencies:
- postcss: 8.4.47
- postcss-selector-parser: 6.1.2
+ postcss: 8.4.38
+ postcss-selector-parser: 6.1.0
- postcss-selector-parser@6.1.2:
+ postcss-selector-parser@6.1.0:
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
postcss-value-parser@4.2.0: {}
+ postcss@8.4.38:
+ dependencies:
+ nanoid: 3.3.7
+ picocolors: 1.0.1
+ source-map-js: 1.2.0
+
postcss@8.4.47:
dependencies:
nanoid: 3.3.7
- picocolors: 1.1.0
+ picocolors: 1.1.1
source-map-js: 1.2.1
potpack@1.0.2: {}
@@ -5546,6 +7055,8 @@ snapshots:
object-assign: 4.1.1
react-is: 16.13.1
+ property-expr@2.0.6: {}
+
pump@3.0.0:
dependencies:
end-of-stream: 1.4.4
@@ -5555,11 +7066,21 @@ snapshots:
queue-microtask@1.2.3: {}
+ quick-lru@5.1.1: {}
+
react-composer@5.0.3(react@18.3.1):
dependencies:
prop-types: 15.8.1
react: 18.3.1
+ react-devtools-core@4.28.5:
+ dependencies:
+ shell-quote: 1.8.1
+ ws: 7.5.10
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
react-dom@18.3.1(react@18.3.1):
dependencies:
loose-envify: 1.4.0
@@ -5568,7 +7089,7 @@ snapshots:
react-error-boundary@4.0.13(react@18.3.1):
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.24.7
react: 18.3.1
react-fast-compare@3.2.2: {}
@@ -5598,6 +7119,13 @@ snapshots:
react-lifecycles-compat: 3.0.4
warning: 4.0.3
+ react-reconciler@0.26.2(react@18.3.1):
+ dependencies:
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
+ react: 18.3.1
+ scheduler: 0.20.2
+
react-reconciler@0.27.0(react@18.3.1):
dependencies:
loose-envify: 1.4.0
@@ -5614,16 +7142,16 @@ snapshots:
react: 18.3.1
shallow-equal: 3.1.0
- react-router-dom@6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ react-router-dom@6.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
- '@remix-run/router': 1.19.2
+ '@remix-run/router': 1.20.0
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- react-router: 6.26.2(react@18.3.1)
+ react-router: 6.27.0(react@18.3.1)
- react-router@6.26.2(react@18.3.1):
+ react-router@6.27.0(react@18.3.1):
dependencies:
- '@remix-run/router': 1.19.2
+ '@remix-run/router': 1.20.0
react: 18.3.1
react-side-effect@2.1.2(react@18.3.1):
@@ -5652,45 +7180,59 @@ snapshots:
es-errors: 1.3.0
get-intrinsic: 1.2.4
globalthis: 1.0.4
- which-builtin-type: 1.1.4
+ which-builtin-type: 1.1.3
- regenerator-runtime@0.14.1: {}
+ reftools@1.1.9: {}
- regexp.prototype.flags@1.5.1:
- dependencies:
- call-bind: 1.0.2
- define-properties: 1.2.1
- set-function-name: 2.0.1
+ regenerator-runtime@0.14.1: {}
- regexp.prototype.flags@1.5.3:
+ regexp.prototype.flags@1.5.2:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
es-errors: 1.3.0
set-function-name: 2.0.2
+ rehackt@0.1.0(@types/react@18.3.11)(react@18.3.1):
+ optionalDependencies:
+ '@types/react': 18.3.11
+ react: 18.3.1
+
require-directory@2.1.1: {}
require-from-string@2.0.2: {}
reselect@4.1.8: {}
+ resolve-alpn@1.2.1: {}
+
resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {}
resolve@1.22.8:
dependencies:
- is-core-module: 2.15.1
+ is-core-module: 2.13.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
resolve@2.0.0-next.5:
dependencies:
- is-core-module: 2.15.1
+ is-core-module: 2.13.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
+ response-iterator@0.2.6: {}
+
+ responselike@3.0.0:
+ dependencies:
+ lowercase-keys: 3.0.0
+
+ restore-cursor@3.1.0:
+ dependencies:
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+
restore-cursor@5.1.0:
dependencies:
onetime: 7.0.0
@@ -5739,6 +7281,10 @@ snapshots:
dependencies:
queue-microtask: 1.2.3
+ rxjs@7.8.1:
+ dependencies:
+ tslib: 2.6.3
+
safe-array-concat@1.1.2:
dependencies:
call-bind: 1.0.7
@@ -5752,11 +7298,17 @@ snapshots:
es-errors: 1.3.0
is-regex: 1.1.4
- sass@1.79.4:
+ sass@1.80.2:
dependencies:
+ '@parcel/watcher': 2.4.1
chokidar: 4.0.1
- immutable: 4.3.7
- source-map-js: 1.2.1
+ immutable: 4.3.6
+ source-map-js: 1.2.0
+
+ scheduler@0.20.2:
+ dependencies:
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
scheduler@0.21.0:
dependencies:
@@ -5779,12 +7331,6 @@ snapshots:
gopd: 1.0.1
has-property-descriptors: 1.0.2
- set-function-name@2.0.1:
- dependencies:
- define-data-property: 1.1.4
- functions-have-names: 1.2.3
- has-property-descriptors: 1.0.2
-
set-function-name@2.0.2:
dependencies:
define-data-property: 1.1.4
@@ -5800,18 +7346,40 @@ snapshots:
shebang-regex@3.0.0: {}
- side-channel@1.0.4:
+ shell-quote@1.8.1: {}
+
+ should-equal@2.0.0:
dependencies:
- call-bind: 1.0.2
- get-intrinsic: 1.2.1
- object-inspect: 1.12.3
+ should-type: 1.4.0
+
+ should-format@3.0.3:
+ dependencies:
+ should-type: 1.4.0
+ should-type-adaptors: 1.1.0
+
+ should-type-adaptors@1.1.0:
+ dependencies:
+ should-type: 1.4.0
+ should-util: 1.0.1
+
+ should-type@1.4.0: {}
+
+ should-util@1.0.1: {}
+
+ should@13.2.3:
+ dependencies:
+ should-equal: 2.0.0
+ should-format: 3.0.3
+ should-type: 1.4.0
+ should-type-adaptors: 1.1.0
+ should-util: 1.0.1
side-channel@1.0.6:
dependencies:
call-bind: 1.0.7
es-errors: 1.3.0
get-intrinsic: 1.2.4
- object-inspect: 1.13.2
+ object-inspect: 1.13.1
signal-exit@3.0.7: {}
@@ -5821,6 +7389,14 @@ snapshots:
slash@3.0.0: {}
+ slash@4.0.0: {}
+
+ slice-ansi@3.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ astral-regex: 2.0.0
+ is-fullwidth-code-point: 3.0.0
+
slice-ansi@5.0.0:
dependencies:
ansi-styles: 6.2.1
@@ -5831,9 +7407,7 @@ snapshots:
ansi-styles: 6.2.1
is-fullwidth-code-point: 5.0.0
- solarxr-protocol@file:solarxr-protocol:
- dependencies:
- flatbuffers: 22.10.26
+ source-map-js@1.2.0: {}
source-map-js@1.2.1: {}
@@ -5857,9 +7431,9 @@ snapshots:
spdx-expression-parse@3.0.1:
dependencies:
spdx-exceptions: 2.5.0
- spdx-license-ids: 3.0.20
+ spdx-license-ids: 3.0.18
- spdx-license-ids@3.0.20: {}
+ spdx-license-ids@3.0.18: {}
spdx-ranges@2.1.1: {}
@@ -5869,6 +7443,10 @@ snapshots:
spdx-expression-parse: 3.0.1
spdx-ranges: 2.1.1
+ stack-utils@2.0.6:
+ dependencies:
+ escape-string-regexp: 2.0.0
+
stats-gl@2.2.8:
dependencies:
'@types/three': 0.163.0
@@ -5893,14 +7471,15 @@ snapshots:
emoji-regex: 9.2.2
strip-ansi: 7.1.0
- string-width@7.2.0:
+ string-width@7.1.0:
dependencies:
- emoji-regex: 10.4.0
+ emoji-regex: 10.3.0
get-east-asian-width: 1.2.0
strip-ansi: 7.1.0
- string.prototype.includes@2.0.0:
+ string.prototype.includes@2.0.1:
dependencies:
+ call-bind: 1.0.7
define-properties: 1.2.1
es-abstract: 1.23.3
@@ -5915,7 +7494,7 @@ snapshots:
gopd: 1.0.1
has-symbols: 1.0.3
internal-slot: 1.0.7
- regexp.prototype.flags: 1.5.3
+ regexp.prototype.flags: 1.5.2
set-function-name: 2.0.2
side-channel: 1.0.6
@@ -5963,7 +7542,7 @@ snapshots:
dependencies:
'@jridgewell/gen-mapping': 0.3.5
commander: 4.1.1
- glob: 10.4.5
+ glob: 10.4.1
lines-and-columns: 1.2.4
mz: 2.7.0
pirates: 4.0.6
@@ -5983,9 +7562,27 @@ snapshots:
dependencies:
react: 18.3.1
+ swagger2openapi@7.0.8:
+ dependencies:
+ call-me-maybe: 1.0.2
+ node-fetch: 2.7.0
+ node-fetch-h2: 2.3.0
+ node-readfiles: 0.2.0
+ oas-kit-common: 1.0.8
+ oas-resolver: 2.5.6
+ oas-schema-walker: 1.1.5
+ oas-validator: 5.0.8
+ reftools: 1.1.9
+ yaml: 1.10.2
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - encoding
+
+ symbol-observable@4.0.0: {}
+
tailwind-gradient-mask-image@1.2.0: {}
- tailwindcss@3.4.13(ts-node@9.1.1(typescript@5.6.3)):
+ tailwindcss@3.4.14(ts-node@9.1.1(typescript@5.6.3)):
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
@@ -5997,16 +7594,16 @@ snapshots:
is-glob: 4.0.3
jiti: 1.21.6
lilconfig: 2.1.0
- micromatch: 4.0.8
+ micromatch: 4.0.7
normalize-path: 3.0.0
object-hash: 3.0.0
- picocolors: 1.1.0
- postcss: 8.4.47
- postcss-import: 15.1.0(postcss@8.4.47)
- postcss-js: 4.0.1(postcss@8.4.47)
- postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@9.1.1(typescript@5.6.3))
- postcss-nested: 6.2.0(postcss@8.4.47)
- postcss-selector-parser: 6.1.2
+ picocolors: 1.0.1
+ postcss: 8.4.38
+ postcss-import: 15.1.0(postcss@8.4.38)
+ postcss-js: 4.0.1(postcss@8.4.38)
+ postcss-load-config: 4.0.2(postcss@8.4.38)(ts-node@9.1.1(typescript@5.6.3))
+ postcss-nested: 6.0.1(postcss@8.4.38)
+ postcss-selector-parser: 6.1.0
resolve: 1.22.8
sucrase: 3.35.0
transitivePeerDependencies:
@@ -6017,7 +7614,7 @@ snapshots:
terser@5.31.1:
dependencies:
'@jridgewell/source-map': 0.3.6
- acorn: 8.12.1
+ acorn: 8.12.0
commander: 2.20.3
source-map-support: 0.5.21
optional: true
@@ -6036,11 +7633,11 @@ snapshots:
dependencies:
three: 0.163.0
- three-stdlib@2.33.0(three@0.163.0):
+ three-stdlib@2.30.3(three@0.163.0):
dependencies:
'@types/draco3d': 1.4.10
'@types/offscreencanvas': 2019.7.3
- '@types/webxr': 0.5.20
+ '@types/webxr': 0.5.16
draco3d: 1.5.7
fflate: 0.6.10
potpack: 1.0.2
@@ -6048,6 +7645,8 @@ snapshots:
three@0.163.0: {}
+ tiny-case@1.0.3: {}
+
tinycolor2@1.6.0: {}
to-fast-properties@2.0.0: {}
@@ -6056,6 +7655,10 @@ snapshots:
dependencies:
is-number: 7.0.0
+ toposort@2.0.2: {}
+
+ tr46@0.0.3: {}
+
troika-three-text@0.49.1(three@0.163.0):
dependencies:
bidi-js: 1.0.3
@@ -6076,6 +7679,10 @@ snapshots:
ts-interface-checker@0.1.13: {}
+ ts-invariant@0.10.3:
+ dependencies:
+ tslib: 2.6.3
+
ts-node@9.1.1(typescript@4.8.4):
dependencies:
arg: 4.1.3
@@ -6097,7 +7704,7 @@ snapshots:
yn: 3.1.1
optional: true
- ts-pattern@5.4.0: {}
+ ts-pattern@5.5.0: {}
tsconfig-paths@3.15.0:
dependencies:
@@ -6106,22 +7713,37 @@ snapshots:
minimist: 1.2.8
strip-bom: 3.0.0
- tslib@2.7.0: {}
+ tslib@1.14.1: {}
+
+ tslib@2.6.3: {}
+
+ tsutils@3.21.0(typescript@4.8.2):
+ dependencies:
+ tslib: 1.14.1
+ typescript: 4.8.2
tunnel-rat@0.1.2(@types/react@18.3.11)(immer@9.0.21)(react@18.3.1):
dependencies:
- zustand: 4.5.5(@types/react@18.3.11)(immer@9.0.21)(react@18.3.1)
+ zustand: 4.5.2(@types/react@18.3.11)(immer@9.0.21)(react@18.3.1)
transitivePeerDependencies:
- '@types/react'
- immer
- react
+ typanion@3.14.0: {}
+
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
+ type-fest@0.12.0: {}
+
type-fest@0.20.2: {}
+ type-fest@0.21.3: {}
+
+ type-fest@2.19.0: {}
+
typed-array-buffer@1.0.2:
dependencies:
call-bind: 1.0.7
@@ -6154,6 +7776,19 @@ snapshots:
is-typed-array: 1.1.13
possible-typed-array-names: 1.0.0
+ typescript-eslint@8.8.0(eslint@8.57.1)(typescript@5.6.3):
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 8.8.0(@typescript-eslint/parser@8.8.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)
+ '@typescript-eslint/parser': 8.8.0(eslint@8.57.1)(typescript@5.6.3)
+ '@typescript-eslint/utils': 8.8.0(eslint@8.57.1)(typescript@5.6.3)
+ optionalDependencies:
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - eslint
+ - supports-color
+
+ typescript@4.8.2: {}
+
typescript@4.8.4: {}
typescript@5.6.3: {}
@@ -6168,11 +7803,19 @@ snapshots:
undici-types@5.26.5:
optional: true
+ universalify@2.0.1: {}
+
+ update-browserslist-db@1.0.16(browserslist@4.23.1):
+ dependencies:
+ browserslist: 4.23.1
+ escalade: 3.1.2
+ picocolors: 1.0.1
+
update-browserslist-db@1.1.1(browserslist@4.24.0):
dependencies:
browserslist: 4.24.0
escalade: 3.2.0
- picocolors: 1.1.0
+ picocolors: 1.1.1
uri-js@4.4.1:
dependencies:
@@ -6182,7 +7825,7 @@ snapshots:
dependencies:
react: 18.3.1
- use-sync-external-store@1.2.2(react@18.3.1):
+ use-sync-external-store@1.2.0(react@18.3.1):
dependencies:
react: 18.3.1
@@ -6192,7 +7835,7 @@ snapshots:
uuid@9.0.1: {}
- vite@5.4.8(@types/node@20.14.2)(sass@1.79.4)(terser@5.31.1):
+ vite@5.4.9(@types/node@20.14.2)(sass@1.80.2)(terser@5.31.1):
dependencies:
esbuild: 0.21.5
postcss: 8.4.47
@@ -6200,7 +7843,7 @@ snapshots:
optionalDependencies:
'@types/node': 20.14.2
fsevents: 2.3.3
- sass: 1.79.4
+ sass: 1.80.2
terser: 5.31.1
warning@4.0.3:
@@ -6211,6 +7854,13 @@ snapshots:
webgl-sdf-generator@1.1.1: {}
+ webidl-conversions@3.0.1: {}
+
+ whatwg-url@5.0.0:
+ dependencies:
+ tr46: 0.0.3
+ webidl-conversions: 3.0.1
+
which-boxed-primitive@1.0.2:
dependencies:
is-bigint: 1.0.4
@@ -6219,7 +7869,7 @@ snapshots:
is-string: 1.0.7
is-symbol: 1.0.4
- which-builtin-type@1.1.4:
+ which-builtin-type@1.1.3:
dependencies:
function.prototype.name: 1.1.6
has-tostringtag: 1.0.2
@@ -6234,13 +7884,6 @@ snapshots:
which-collection: 1.0.2
which-typed-array: 1.1.15
- which-collection@1.0.1:
- dependencies:
- is-map: 2.0.2
- is-set: 2.0.2
- is-weakmap: 2.0.1
- is-weakset: 2.0.2
-
which-collection@1.0.2:
dependencies:
is-map: 2.0.3
@@ -6248,14 +7891,6 @@ snapshots:
is-weakmap: 2.0.2
is-weakset: 2.0.3
- which-typed-array@1.1.11:
- dependencies:
- available-typed-arrays: 1.0.5
- call-bind: 1.0.2
- for-each: 0.3.3
- gopd: 1.0.1
- has-tostringtag: 1.0.0
-
which-typed-array@1.1.15:
dependencies:
available-typed-arrays: 1.0.7
@@ -6268,8 +7903,18 @@ snapshots:
dependencies:
isexe: 2.0.0
+ widest-line@3.1.0:
+ dependencies:
+ string-width: 4.2.3
+
word-wrap@1.2.5: {}
+ wrap-ansi@6.2.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
@@ -6285,15 +7930,21 @@ snapshots:
wrap-ansi@9.0.0:
dependencies:
ansi-styles: 6.2.1
- string-width: 7.2.0
+ string-width: 7.1.0
strip-ansi: 7.1.0
wrappy@1.0.2: {}
+ ws@7.5.10: {}
+
y18n@5.0.8: {}
yallist@3.1.1: {}
+ yaml@1.10.2: {}
+
+ yaml@2.4.5: {}
+
yaml@2.5.1: {}
yargs-parser@20.2.9: {}
@@ -6303,7 +7954,7 @@ snapshots:
yargs@16.2.0:
dependencies:
cliui: 7.0.4
- escalade: 3.1.1
+ escalade: 3.1.2
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
@@ -6313,7 +7964,7 @@ snapshots:
yargs@17.7.2:
dependencies:
cliui: 8.0.1
- escalade: 3.2.0
+ escalade: 3.1.2
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
@@ -6324,13 +7975,30 @@ snapshots:
yocto-queue@0.1.0: {}
+ yoga-layout-prebuilt@1.10.0:
+ dependencies:
+ '@types/yoga-layout': 1.9.2
+
+ yup@1.4.0:
+ dependencies:
+ property-expr: 2.0.6
+ tiny-case: 1.0.3
+ toposort: 2.0.2
+ type-fest: 2.19.0
+
+ zen-observable-ts@1.2.5:
+ dependencies:
+ zen-observable: 0.8.15
+
+ zen-observable@0.8.15: {}
+
zustand@3.7.2(react@18.3.1):
optionalDependencies:
react: 18.3.1
- zustand@4.5.5(@types/react@18.3.11)(immer@9.0.21)(react@18.3.1):
+ zustand@4.5.2(@types/react@18.3.11)(immer@9.0.21)(react@18.3.1):
dependencies:
- use-sync-external-store: 1.2.2(react@18.3.1)
+ use-sync-external-store: 1.2.0(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.11
immer: 9.0.21
diff --git a/server/android/build.gradle.kts b/server/android/build.gradle.kts
index 7dd94c33aa..bf9e942c14 100644
--- a/server/android/build.gradle.kts
+++ b/server/android/build.gradle.kts
@@ -57,12 +57,8 @@ tasks.withType {
options.encoding = "UTF-8"
}
-allprojects {
- repositories {
- google()
- mavenCentral()
- maven(url = "https://jitpack.io")
- }
+repositories {
+ google()
}
dependencies {
diff --git a/server/android/src/main/java/dev/slimevr/android/serial/AndroidSerialHandler.kt b/server/android/src/main/java/dev/slimevr/android/serial/AndroidSerialHandler.kt
index 229620566f..d52baa6e87 100644
--- a/server/android/src/main/java/dev/slimevr/android/serial/AndroidSerialHandler.kt
+++ b/server/android/src/main/java/dev/slimevr/android/serial/AndroidSerialHandler.kt
@@ -90,10 +90,16 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
listeners.forEach { it.onNewSerialDevice(port) }
}
+ private fun onDeviceDel(port: SerialPortWrapper) {
+ listeners.forEach { it.onSerialDeviceDeleted(port) }
+ }
+
private fun detectNewPorts() {
- val differences = knownPorts.asSequence() - lastKnownPorts
+ val addDifferences = knownPorts.asSequence() - lastKnownPorts
+ val delDifferences = lastKnownPorts - knownPorts.asSequence().toSet()
lastKnownPorts = knownPorts.asSequence().toSet()
- differences.forEach { onNewDevice(it) }
+ addDifferences.forEach { onNewDevice(it) }
+ delDifferences.forEach { onDeviceDel(it) }
}
override fun addListener(channel: SerialListener) {
@@ -226,12 +232,19 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
}
}
+ override fun write(buff: ByteArray) {
+ LogManager.info("[SerialHandler] WRITING $buff")
+ usbIoManager?.writeAsync(buff)
+ }
+
@Synchronized
override fun setWifi(ssid: String, passwd: String) {
writeSerial("SET WIFI \"${ssid}\" \"${passwd}\"")
addLog("-> SET WIFI \"$ssid\" \"${passwd.replace(".".toRegex(), "*")}\"\n")
}
+ override fun getCurrentPort(): SlimeSerialPort? = this.currentPort
+
private fun addLog(str: String) {
LogManager.info("[Serial] $str")
listeners.forEach { it.onSerialLog(str) }
diff --git a/server/build.gradle.kts b/server/build.gradle.kts
index 645f9c3831..11ddef897f 100644
--- a/server/build.gradle.kts
+++ b/server/build.gradle.kts
@@ -2,8 +2,11 @@ plugins {
id("com.diffplug.spotless")
}
-repositories {
- mavenCentral()
+allprojects {
+ repositories {
+ mavenCentral()
+ maven("https://jitpack.io")
+ }
}
configure {
@@ -35,7 +38,7 @@ configure {
"ktlint_standard_property-naming" to "disabled",
"ij_kotlin_packages_to_use_import_on_demand" to
"java.util.*,kotlin.math.*,dev.slimevr.autobone.errors.*" +
- ",io.github.axisangles.ktmath.*,kotlinx.atomicfu.*" +
+ ",io.github.axisangles.ktmath.*,kotlinx.atomicfu.*,kotlinx.coroutines.*" +
",dev.slimevr.tracking.trackers.*,dev.slimevr.desktop.platform.ProtobufMessages.*" +
",solarxr_protocol.rpc.*,kotlinx.coroutines.*,com.illposed.osc.*,android.app.*",
"ij_kotlin_allow_trailing_comma" to true,
diff --git a/server/core/build.gradle.kts b/server/core/build.gradle.kts
index cc400bc2ef..63938783c4 100644
--- a/server/core/build.gradle.kts
+++ b/server/core/build.gradle.kts
@@ -76,7 +76,14 @@ dependencies {
implementation("org.java-websocket:Java-WebSocket:1.+")
implementation("com.melloware:jintellitype:1.+")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
+ implementation("it.unimi.dsi:fastutil:8.5.12")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
+ implementation("com.mayakapps.kache:kache:2.0.0-rc02")
+
+ api("com.github.loucass003:EspflashKotlin:v0.10.0")
+
+ // Allow the use of reflection
+ implementation(kotlin("reflect"))
// Jitpack
implementation("com.github.SlimeVR:oscquery-kt:566a0cba58")
@@ -87,6 +94,7 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.junit.platform:junit-platform-launcher")
}
+
tasks.test {
useJUnitPlatform()
}
diff --git a/server/core/src/main/java/dev/slimevr/VRServer.kt b/server/core/src/main/java/dev/slimevr/VRServer.kt
index 4bd35390e3..1f253231c8 100644
--- a/server/core/src/main/java/dev/slimevr/VRServer.kt
+++ b/server/core/src/main/java/dev/slimevr/VRServer.kt
@@ -5,6 +5,8 @@ import dev.slimevr.autobone.AutoBoneHandler
import dev.slimevr.bridge.Bridge
import dev.slimevr.bridge.ISteamVRBridge
import dev.slimevr.config.ConfigManager
+import dev.slimevr.firmware.FirmwareUpdateHandler
+import dev.slimevr.firmware.SerialFlashingHandler
import dev.slimevr.osc.OSCHandler
import dev.slimevr.osc.OSCRouter
import dev.slimevr.osc.VMCHandler
@@ -47,9 +49,12 @@ class VRServer @JvmOverloads constructor(
driverBridgeProvider: SteamBridgeProvider = { _, _ -> null },
feederBridgeProvider: (VRServer) -> ISteamVRBridge? = { _ -> null },
serialHandlerProvider: (VRServer) -> SerialHandler = { _ -> SerialHandlerStub() },
+ flashingHandlerProvider: (VRServer) -> SerialFlashingHandler? = { _ -> null },
acquireMulticastLock: () -> Any? = { null },
+ // configPath is used by VRWorkout, do not remove!
configPath: String,
) : Thread("VRServer") {
+
@JvmField
val configManager: ConfigManager
@@ -60,6 +65,7 @@ class VRServer @JvmOverloads constructor(
private val bridges: MutableList = FastList()
private val tasks: Queue = LinkedBlockingQueue()
private val newTrackersConsumers: MutableList> = FastList()
+ private val trackerStatusListeners: MutableList = FastList()
private val onTick: MutableList = FastList()
private val lock = acquireMulticastLock()
val oSCRouter: OSCRouter
@@ -77,6 +83,10 @@ class VRServer @JvmOverloads constructor(
@JvmField
val serialHandler: SerialHandler
+ var serialFlashingHandler: SerialFlashingHandler?
+
+ val firmwareUpdateHandler: FirmwareUpdateHandler
+
@JvmField
val autoBoneHandler: AutoBoneHandler
@@ -108,12 +118,14 @@ class VRServer @JvmOverloads constructor(
configManager.loadConfig()
deviceManager = DeviceManager(this)
serialHandler = serialHandlerProvider(this)
+ serialFlashingHandler = flashingHandlerProvider(this)
provisioningHandler = ProvisioningHandler(this)
resetHandler = ResetHandler()
tapSetupHandler = TapSetupHandler()
humanPoseManager = HumanPoseManager(this)
// AutoBone requires HumanPoseManager first
autoBoneHandler = AutoBoneHandler(this)
+ firmwareUpdateHandler = FirmwareUpdateHandler(this)
protocolAPI = ProtocolAPI(this)
val computedTrackers = humanPoseManager.computedTrackers
@@ -410,6 +422,18 @@ class VRServer @JvmOverloads constructor(
}
}
+ fun trackerStatusChanged(tracker: Tracker, oldStatus: TrackerStatus, newStatus: TrackerStatus) {
+ trackerStatusListeners.forEach { it.onTrackerStatusChanged(tracker, oldStatus, newStatus) }
+ }
+
+ fun addTrackerStatusListener(listener: TrackerStatusListener) {
+ trackerStatusListeners.add(listener)
+ }
+
+ fun removeTrackerStatusListener(listener: TrackerStatusListener) {
+ trackerStatusListeners.removeIf { listener === it }
+ }
+
companion object {
private val nextLocalTrackerId = AtomicInteger()
lateinit var instance: VRServer
diff --git a/server/core/src/main/java/dev/slimevr/firmware/FirmwareUpdateHandler.kt b/server/core/src/main/java/dev/slimevr/firmware/FirmwareUpdateHandler.kt
new file mode 100644
index 0000000000..fc6bdf206f
--- /dev/null
+++ b/server/core/src/main/java/dev/slimevr/firmware/FirmwareUpdateHandler.kt
@@ -0,0 +1,506 @@
+package dev.slimevr.firmware
+
+import com.mayakapps.kache.InMemoryKache
+import com.mayakapps.kache.KacheStrategy
+import dev.llelievr.espflashkotlin.Flasher
+import dev.llelievr.espflashkotlin.FlashingProgressListener
+import dev.slimevr.VRServer
+import dev.slimevr.serial.ProvisioningListener
+import dev.slimevr.serial.ProvisioningStatus
+import dev.slimevr.serial.SerialPort
+import dev.slimevr.tracking.trackers.Tracker
+import dev.slimevr.tracking.trackers.TrackerStatus
+import dev.slimevr.tracking.trackers.TrackerStatusListener
+import dev.slimevr.tracking.trackers.udp.UDPDevice
+import io.eiren.util.logging.LogManager
+import kotlinx.coroutines.*
+import solarxr_protocol.rpc.FirmwarePartT
+import solarxr_protocol.rpc.FirmwareUpdateRequestT
+import java.io.ByteArrayOutputStream
+import java.io.IOException
+import java.io.InputStream
+import java.net.URL
+import java.util.*
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.CopyOnWriteArrayList
+import kotlin.concurrent.scheduleAtFixedRate
+
+data class DownloadedFirmwarePart(
+ val firmware: ByteArray,
+ val offset: Long?,
+) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as DownloadedFirmwarePart
+
+ if (!firmware.contentEquals(other.firmware)) return false
+ if (offset != other.offset) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = firmware.contentHashCode()
+ result = 31 * result + (offset?.hashCode() ?: 0)
+ return result
+ }
+}
+
+class FirmwareUpdateHandler(private val server: VRServer) :
+ TrackerStatusListener,
+ ProvisioningListener,
+ SerialRebootListener {
+
+ private val updateTickTimer = Timer("StatusUpdateTimer")
+ private val runningJobs: MutableList = CopyOnWriteArrayList()
+ private val watchRestartQueue: MutableList, () -> Unit>> =
+ CopyOnWriteArrayList()
+ private val updatingDevicesStatus: MutableMap, UpdateStatusEvent<*>> =
+ ConcurrentHashMap()
+ private val listeners: MutableList = CopyOnWriteArrayList()
+ private val firmwareCache =
+ InMemoryKache>(maxSize = 5 * 1024 * 1024) {
+ strategy = KacheStrategy.LRU
+ sizeCalculator = { _, parts -> parts.sumOf { it.firmware.size }.toLong() }
+ }
+ private val mainScope: CoroutineScope = CoroutineScope(SupervisorJob())
+ private var clearJob: Deferred? = null
+
+ private var serialRebootHandler: SerialRebootHandler = SerialRebootHandler(watchRestartQueue, this)
+
+ fun addListener(channel: FirmwareUpdateListener) {
+ listeners.add(channel)
+ }
+
+ fun removeListener(channel: FirmwareUpdateListener) {
+ listeners.removeIf { channel == it }
+ }
+
+ fun destroy() {
+ mainScope.cancel()
+ }
+
+ init {
+ server.addTrackerStatusListener(this)
+ server.provisioningHandler.addListener(this)
+ server.serialHandler.addListener(serialRebootHandler)
+
+ this.updateTickTimer.scheduleAtFixedRate(0, 1000) {
+ checkUpdateTimeout()
+ }
+ }
+
+ private fun startOtaUpdate(
+ part: DownloadedFirmwarePart,
+ deviceId: UpdateDeviceId,
+ ) {
+ val udpDevice: UDPDevice? =
+ (this.server.deviceManager.devices.find { device -> device is UDPDevice && device.id == deviceId.id }) as UDPDevice?
+
+ if (udpDevice == null) {
+ onStatusChange(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.ERROR_DEVICE_NOT_FOUND,
+ ),
+ )
+ return
+ }
+ OTAUpdateTask(
+ part.firmware,
+ deviceId,
+ udpDevice.ipAddress,
+ this::onStatusChange,
+ ).run()
+ }
+
+ private fun startSerialUpdate(
+ firmwares: Array,
+ deviceId: UpdateDeviceId,
+ needManualReboot: Boolean,
+ ssid: String,
+ password: String,
+ ) {
+ val serialPort = this.server.serialHandler.knownPorts.toList()
+ .find { port -> deviceId.id == port.portLocation }
+
+ if (serialPort == null) {
+ onStatusChange(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.ERROR_DEVICE_NOT_FOUND,
+ ),
+ )
+ return
+ }
+
+ val flashingHandler = this.server.serialFlashingHandler
+
+ if (flashingHandler == null) {
+ onStatusChange(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.ERROR_UNSUPPORTED_METHOD,
+ ),
+ )
+ return
+ }
+
+ try {
+ val flasher = Flasher(flashingHandler)
+
+ for (part in firmwares) {
+ if (part.offset == null) {
+ error("Offset is empty")
+ }
+ flasher.addBin(part.firmware, part.offset.toInt())
+ }
+
+ flasher.addProgressListener(object : FlashingProgressListener {
+ override fun progress(progress: Float) {
+ onStatusChange(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.UPLOADING,
+ (progress * 100).toInt(),
+ ),
+ )
+ }
+ })
+
+ onStatusChange(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.SYNCING_WITH_MCU,
+ ),
+ )
+ flasher.flash(serialPort)
+ if (needManualReboot) {
+ if (watchRestartQueue.find { it.first == deviceId } != null) {
+ LogManager.info("[FirmwareUpdateHandler] Device is already updating, Skipping")
+ }
+
+ onStatusChange(UpdateStatusEvent(deviceId, FirmwareUpdateStatus.NEED_MANUAL_REBOOT))
+ server.serialHandler.openSerial(deviceId.id, false)
+ watchRestartQueue.add(
+ Pair(deviceId) {
+ onStatusChange(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.REBOOTING,
+ ),
+ )
+ server.provisioningHandler.start(
+ ssid,
+ password,
+ serialPort.portLocation,
+ )
+ },
+ )
+ } else {
+ onStatusChange(UpdateStatusEvent(deviceId, FirmwareUpdateStatus.REBOOTING))
+ server.provisioningHandler.start(ssid, password, serialPort.portLocation)
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ onStatusChange(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.ERROR_UPLOAD_FAILED,
+ ),
+ )
+ }
+ }
+
+ fun queueFirmwareUpdate(
+ request: FirmwareUpdateRequestT,
+ deviceId: UpdateDeviceId<*>,
+ ) = mainScope.launch {
+ val method = FirmwareUpdateMethod.getById(request.method.type) ?: error("Unknown method")
+
+ clearJob?.await()
+ if (method == FirmwareUpdateMethod.OTA) {
+ if (watchRestartQueue.find { it.first == deviceId } != null) {
+ LogManager.info("[FirmwareUpdateHandler] Device is already updating, Skipping")
+ }
+
+ onStatusChange(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.NEED_MANUAL_REBOOT,
+ ),
+ )
+ watchRestartQueue.add(
+ Pair(deviceId) {
+ mainScope.launch {
+ startFirmwareUpdateJob(
+ request,
+ deviceId,
+ )
+ }
+ },
+ )
+ } else {
+ if (updatingDevicesStatus[deviceId] != null) {
+ LogManager.info("[FirmwareUpdateHandler] Device is already updating, Skipping")
+ return@launch
+ }
+
+ startFirmwareUpdateJob(
+ request,
+ deviceId,
+ )
+ }
+ }
+
+ fun cancelUpdates() {
+ val oldClearJob = clearJob
+ clearJob = mainScope.async {
+ oldClearJob?.await()
+ watchRestartQueue.clear()
+ runningJobs.forEach { it.cancelAndJoin() }
+ runningJobs.clear()
+ }
+ }
+
+ private fun getFirmwareParts(request: FirmwareUpdateRequestT): ArrayList {
+ val parts = ArrayList()
+ val method = FirmwareUpdateMethod.getById(request.method.type) ?: error("Unknown method")
+ when (method) {
+ FirmwareUpdateMethod.OTA -> {
+ val updateReq = request.method.asOTAFirmwareUpdate()
+ parts.add(updateReq.firmwarePart)
+ }
+
+ FirmwareUpdateMethod.SERIAL -> {
+ val updateReq = request.method.asSerialFirmwareUpdate()
+ parts.addAll(updateReq.firmwarePart)
+ }
+
+ FirmwareUpdateMethod.NONE -> error("Method should not be NONE")
+ }
+ return parts
+ }
+
+ private suspend fun startFirmwareUpdateJob(
+ request: FirmwareUpdateRequestT,
+ deviceId: UpdateDeviceId<*>,
+ ) = coroutineScope {
+ onStatusChange(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.DOWNLOADING,
+ ),
+ )
+
+ try {
+ // We add the firmware to an LRU cache
+ val toDownloadParts = getFirmwareParts(request)
+ val firmwareParts =
+ firmwareCache.getOrPut(toDownloadParts.joinToString("|") { "${it.url}#${it.offset}" }) {
+ withTimeoutOrNull(30_000) {
+ toDownloadParts.map {
+ val firmware = downloadFirmware(it.url)
+ ?: error("unable to download firmware part")
+ DownloadedFirmwarePart(
+ firmware,
+ it.offset,
+ )
+ }.toTypedArray()
+ }
+ }
+
+ val job = launch {
+ withTimeout(2 * 60 * 1000) {
+ if (firmwareParts.isNullOrEmpty()) {
+ onStatusChange(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.ERROR_DOWNLOAD_FAILED,
+ ),
+ )
+ return@withTimeout
+ }
+
+ val method = FirmwareUpdateMethod.getById(request.method.type) ?: error("Unknown method")
+ when (method) {
+ FirmwareUpdateMethod.NONE -> error("unsupported method")
+
+ FirmwareUpdateMethod.OTA -> {
+ if (deviceId.id !is Int) {
+ error("invalid state, the device id is not an int")
+ }
+ if (firmwareParts.size > 1) {
+ error("invalid state, ota only use one firmware file")
+ }
+ startOtaUpdate(
+ firmwareParts.first(),
+ UpdateDeviceId(
+ FirmwareUpdateMethod.OTA,
+ deviceId.id,
+ ),
+ )
+ }
+
+ FirmwareUpdateMethod.SERIAL -> {
+ val req = request.method.asSerialFirmwareUpdate()
+ if (deviceId.id !is String) {
+ error("invalid state, the device id is not a string")
+ }
+ startSerialUpdate(
+ firmwareParts,
+ UpdateDeviceId(
+ FirmwareUpdateMethod.SERIAL,
+ deviceId.id,
+ ),
+ req.needManualReboot,
+ req.ssid,
+ req.password,
+ )
+ }
+ }
+ }
+ }
+ runningJobs.add(job)
+ } catch (e: Exception) {
+ onStatusChange(
+ UpdateStatusEvent(
+ deviceId,
+ if (e is TimeoutCancellationException) FirmwareUpdateStatus.ERROR_TIMEOUT else FirmwareUpdateStatus.ERROR_UNKNOWN,
+ ),
+ )
+ if (e !is TimeoutCancellationException) {
+ e.printStackTrace()
+ }
+ return@coroutineScope
+ }
+ }
+
+ private fun onStatusChange(event: UpdateStatusEvent) {
+ this.updatingDevicesStatus[event.deviceId] = event
+
+ if (event.status == FirmwareUpdateStatus.DONE || event.status.isError()) {
+ this.updatingDevicesStatus.remove(event.deviceId)
+
+ // we remove the device from the restart queue
+ val queuedDevice = watchRestartQueue.find { it.first.id == event.deviceId }
+ if (queuedDevice != null) {
+ watchRestartQueue.remove(queuedDevice)
+ if (event.deviceId.type === FirmwareUpdateMethod.SERIAL && server.serialHandler.isConnected) {
+ server.serialHandler.closeSerial()
+ }
+ }
+
+ // We make sure to stop the provisioning routine if the tracker is done
+ // flashing
+ if (event.deviceId.type === FirmwareUpdateMethod.SERIAL) {
+ this.server.provisioningHandler.stop()
+ }
+ }
+ listeners.forEach { l -> l.onUpdateStatusChange(event) }
+ }
+
+ private fun checkUpdateTimeout() {
+ updatingDevicesStatus.forEach { (id, device) ->
+ // if more than 30s between two events, consider the update as stuck
+ // We do not timeout on the Downloading step as it has it own timeout
+ // We do not timeout on the Done step as it is the end of the update process
+ if (!device.status.isError() && !intArrayOf(FirmwareUpdateStatus.DONE.id, FirmwareUpdateStatus.DOWNLOADING.id).contains(device.status.id) && System.currentTimeMillis() - device.time > 30 * 1000) {
+ onStatusChange(
+ UpdateStatusEvent(
+ id,
+ FirmwareUpdateStatus.ERROR_TIMEOUT,
+ ),
+ )
+ }
+ }
+ }
+
+ // this only works for OTA trackers as the device id
+ // only exists when the usb connection is created
+ override fun onTrackerStatusChanged(
+ tracker: Tracker,
+ oldStatus: TrackerStatus,
+ newStatus: TrackerStatus,
+ ) {
+ val device = tracker.device
+ if (device !is UDPDevice) return
+
+ if (oldStatus == TrackerStatus.DISCONNECTED && newStatus == TrackerStatus.OK) {
+ val queuedDevice = watchRestartQueue.find { it.first.id == device.id }
+
+ if (queuedDevice != null) {
+ queuedDevice.second() // we start the queued update task
+ watchRestartQueue.remove(queuedDevice) // then we remove it from the queue
+ return
+ }
+
+ // We can only filter OTA method here as the device id is only provided when using Wi-Fi
+ val deviceStatusKey =
+ updatingDevicesStatus.keys.find { it.type == FirmwareUpdateMethod.OTA && it.id == device.id }
+ ?: return
+ val updateStatus = updatingDevicesStatus[deviceStatusKey] ?: return
+ // We check for the reconnection of the tracker, once the tracker reconnected we notify the user that the update is completed
+ if (updateStatus.status == FirmwareUpdateStatus.REBOOTING) {
+ onStatusChange(
+ UpdateStatusEvent(
+ updateStatus.deviceId,
+ FirmwareUpdateStatus.DONE,
+ ),
+ )
+ }
+ }
+ }
+
+ override fun onProvisioningStatusChange(
+ status: ProvisioningStatus,
+ port: SerialPort?,
+ ) {
+ fun update(s: FirmwareUpdateStatus) {
+ val deviceStatusKey =
+ updatingDevicesStatus.keys.find { it.type == FirmwareUpdateMethod.SERIAL && it.id == port?.portLocation }
+ ?: return
+ val updateStatus = updatingDevicesStatus[deviceStatusKey] ?: return
+ onStatusChange(UpdateStatusEvent(updateStatus.deviceId, s))
+ }
+
+ if (status == ProvisioningStatus.PROVISIONING) {
+ update(FirmwareUpdateStatus.PROVISIONING)
+ }
+
+ if (status == ProvisioningStatus.DONE) {
+ update(FirmwareUpdateStatus.DONE)
+ }
+
+ if (status == ProvisioningStatus.CONNECTION_ERROR || status == ProvisioningStatus.COULD_NOT_FIND_SERVER) {
+ update(FirmwareUpdateStatus.ERROR_PROVISIONING_FAILED)
+ }
+ }
+
+ override fun onSerialDeviceReconnect(deviceHandle: Pair, () -> Unit>) {
+ deviceHandle.second()
+ watchRestartQueue.remove(deviceHandle)
+ if (server.serialHandler.isConnected) {
+ server.serialHandler.closeSerial()
+ }
+ }
+}
+
+fun downloadFirmware(url: String): ByteArray? {
+ val outputStream = ByteArrayOutputStream()
+
+ try {
+ val chunk = ByteArray(4096)
+ var bytesRead: Int
+ val stream: InputStream = URL(url).openStream()
+ while (stream.read(chunk).also { bytesRead = it } > 0) {
+ outputStream.write(chunk, 0, bytesRead)
+ }
+ } catch (e: IOException) {
+ error("Cant download firmware $url")
+ }
+
+ return outputStream.toByteArray()
+}
diff --git a/server/core/src/main/java/dev/slimevr/firmware/FirmwareUpdateListener.kt b/server/core/src/main/java/dev/slimevr/firmware/FirmwareUpdateListener.kt
new file mode 100644
index 0000000000..3f048828ac
--- /dev/null
+++ b/server/core/src/main/java/dev/slimevr/firmware/FirmwareUpdateListener.kt
@@ -0,0 +1,5 @@
+package dev.slimevr.firmware
+
+interface FirmwareUpdateListener {
+ fun onUpdateStatusChange(event: UpdateStatusEvent<*>)
+}
diff --git a/server/core/src/main/java/dev/slimevr/firmware/FirmwareUpdateMethod.kt b/server/core/src/main/java/dev/slimevr/firmware/FirmwareUpdateMethod.kt
new file mode 100644
index 0000000000..7aaddf974e
--- /dev/null
+++ b/server/core/src/main/java/dev/slimevr/firmware/FirmwareUpdateMethod.kt
@@ -0,0 +1,14 @@
+package dev.slimevr.firmware
+
+enum class FirmwareUpdateMethod(val id: Byte) {
+ NONE(solarxr_protocol.rpc.FirmwareUpdateMethod.NONE),
+ OTA(solarxr_protocol.rpc.FirmwareUpdateMethod.OTAFirmwareUpdate),
+ SERIAL(solarxr_protocol.rpc.FirmwareUpdateMethod.SerialFirmwareUpdate),
+ ;
+
+ companion object {
+ fun getById(id: Byte): FirmwareUpdateMethod? = byId[id]
+ }
+}
+
+private val byId = FirmwareUpdateMethod.entries.associateBy { it.id }
diff --git a/server/core/src/main/java/dev/slimevr/firmware/FirmwareUpdateStatus.kt b/server/core/src/main/java/dev/slimevr/firmware/FirmwareUpdateStatus.kt
new file mode 100644
index 0000000000..4edb320b44
--- /dev/null
+++ b/server/core/src/main/java/dev/slimevr/firmware/FirmwareUpdateStatus.kt
@@ -0,0 +1,29 @@
+package dev.slimevr.firmware
+
+enum class FirmwareUpdateStatus(val id: Int) {
+ DOWNLOADING(solarxr_protocol.rpc.FirmwareUpdateStatus.DOWNLOADING),
+ AUTHENTICATING(solarxr_protocol.rpc.FirmwareUpdateStatus.AUTHENTICATING),
+ UPLOADING(solarxr_protocol.rpc.FirmwareUpdateStatus.UPLOADING),
+ SYNCING_WITH_MCU(solarxr_protocol.rpc.FirmwareUpdateStatus.SYNCING_WITH_MCU),
+ REBOOTING(solarxr_protocol.rpc.FirmwareUpdateStatus.REBOOTING),
+ NEED_MANUAL_REBOOT(solarxr_protocol.rpc.FirmwareUpdateStatus.NEED_MANUAL_REBOOT),
+ PROVISIONING(solarxr_protocol.rpc.FirmwareUpdateStatus.PROVISIONING),
+ DONE(solarxr_protocol.rpc.FirmwareUpdateStatus.DONE),
+ ERROR_DEVICE_NOT_FOUND(solarxr_protocol.rpc.FirmwareUpdateStatus.ERROR_DEVICE_NOT_FOUND),
+ ERROR_TIMEOUT(solarxr_protocol.rpc.FirmwareUpdateStatus.ERROR_TIMEOUT),
+ ERROR_DOWNLOAD_FAILED(solarxr_protocol.rpc.FirmwareUpdateStatus.ERROR_DOWNLOAD_FAILED),
+ ERROR_AUTHENTICATION_FAILED(solarxr_protocol.rpc.FirmwareUpdateStatus.ERROR_AUTHENTICATION_FAILED),
+ ERROR_UPLOAD_FAILED(solarxr_protocol.rpc.FirmwareUpdateStatus.ERROR_UPLOAD_FAILED),
+ ERROR_PROVISIONING_FAILED(solarxr_protocol.rpc.FirmwareUpdateStatus.ERROR_PROVISIONING_FAILED),
+ ERROR_UNSUPPORTED_METHOD(solarxr_protocol.rpc.FirmwareUpdateStatus.ERROR_UNSUPPORTED_METHOD),
+ ERROR_UNKNOWN(solarxr_protocol.rpc.FirmwareUpdateStatus.ERROR_UNKNOWN),
+ ;
+
+ fun isError(): Boolean = id in ERROR_DEVICE_NOT_FOUND.id..ERROR_UNKNOWN.id
+
+ companion object {
+ fun getById(id: Int): FirmwareUpdateStatus? = byId[id]
+ }
+}
+
+private val byId = FirmwareUpdateStatus.entries.associateBy { it.id }
diff --git a/server/core/src/main/java/dev/slimevr/firmware/OTAUpdateTask.kt b/server/core/src/main/java/dev/slimevr/firmware/OTAUpdateTask.kt
new file mode 100644
index 0000000000..ab014038e8
--- /dev/null
+++ b/server/core/src/main/java/dev/slimevr/firmware/OTAUpdateTask.kt
@@ -0,0 +1,180 @@
+package dev.slimevr.firmware
+
+import io.eiren.util.logging.LogManager
+import java.io.DataInputStream
+import java.io.DataOutputStream
+import java.net.DatagramPacket
+import java.net.DatagramSocket
+import java.net.InetAddress
+import java.net.ServerSocket
+import java.security.MessageDigest
+import java.security.NoSuchAlgorithmException
+import java.util.*
+import java.util.function.Consumer
+import kotlin.math.min
+
+class OTAUpdateTask(
+ private val firmware: ByteArray,
+ private val deviceId: UpdateDeviceId,
+ private val deviceIp: InetAddress,
+ private val statusCallback: Consumer>,
+) {
+ private val receiveBuffer: ByteArray = ByteArray(38)
+
+ @Throws(NoSuchAlgorithmException::class)
+ private fun bytesToMd5(bytes: ByteArray): String {
+ val md5 = MessageDigest.getInstance("MD5")
+ md5.update(bytes)
+ val digest = md5.digest()
+ val md5str = StringBuilder()
+ for (b in digest) {
+ md5str.append(String.format("%02x", b))
+ }
+ return md5str.toString()
+ }
+
+ private fun authenticate(localPort: Int): Boolean {
+ try {
+ DatagramSocket().use { socket ->
+ statusCallback.accept(UpdateStatusEvent(deviceId, FirmwareUpdateStatus.AUTHENTICATING))
+ LogManager.info("[OTAUpdate] Sending OTA invitation to: $deviceIp")
+
+ val fileMd5 = bytesToMd5(firmware)
+ val message = "$FLASH $localPort ${firmware.size} $fileMd5\n"
+
+ socket.send(DatagramPacket(message.toByteArray(), message.length, deviceIp, PORT))
+ socket.soTimeout = 10000
+
+ val authPacket = DatagramPacket(receiveBuffer, receiveBuffer.size)
+ socket.receive(authPacket)
+
+ val data = String(authPacket.data, 0, authPacket.length)
+
+ // if we received OK directly from the MCU, we do not need to authenticate
+ if (data == "OK") return true
+
+ val args = data.split(" ")
+
+ // The expected auth payload should look like "AUTH AUTH_TOKEN"
+ // if we have less than those two args it means that we are in an invalid state
+ if (args.size != 2 || args[0] != "AUTH") return false
+
+ LogManager.info("[OTAUpdate] Authenticating...")
+
+ val authToken = args[1]
+ val signature = bytesToMd5(UUID.randomUUID().toString().toByteArray())
+ val hashedPassword = bytesToMd5(PASSWORD.toByteArray())
+ val resultText = "$hashedPassword:$authToken:$signature"
+ val payload = bytesToMd5(resultText.toByteArray())
+
+ val authMessage = "$AUTH $signature $payload\n"
+
+ socket.soTimeout = 10000
+ socket.send(
+ DatagramPacket(
+ authMessage.toByteArray(),
+ authMessage.length,
+ deviceIp,
+ PORT,
+ ),
+ )
+
+ val authResponsePacket = DatagramPacket(receiveBuffer, receiveBuffer.size)
+ socket.receive(authResponsePacket)
+
+ val authResponse = String(authResponsePacket.data, 0, authResponsePacket.length)
+
+ return authResponse == "OK"
+ }
+ } catch (e: Exception) {
+ return false
+ }
+ }
+
+ private fun upload(serverSocket: ServerSocket): Boolean {
+ try {
+ LogManager.info("[OTAUpdate] Starting on: ${serverSocket.localPort}")
+ LogManager.info("[OTAUpdate] Waiting for device...")
+
+ val connection = serverSocket.accept()
+ connection.setSoTimeout(1000)
+
+ val dos = DataOutputStream(connection.getOutputStream())
+ val dis = DataInputStream(connection.getInputStream())
+
+ LogManager.info("[OTAUpdate] Upload size: ${firmware.size} bytes")
+ var offset = 0
+ val chunkSize = 2048
+ while (offset != firmware.size) {
+ statusCallback.accept(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.UPLOADING,
+ ((offset.toDouble() / firmware.size) * 100).toInt(),
+ ),
+ )
+
+ val chunkLen = min(chunkSize, (firmware.size - offset))
+ dos.write(firmware, offset, chunkLen)
+ dos.flush()
+ offset += chunkLen
+
+ // Those skipped bytes are the size written to the MCU. We do not really need that information,
+ // so we simply skip it.
+ // The reason those bytes are skipped here is to not have to skip all of them when checking
+ // for the OK response. Saving time
+ dis.skipNBytes(4)
+ }
+
+ LogManager.info("[OTAUpdate] Waiting for result...")
+ // We set the timeout of the connection bigger as it can take some time for the MCU
+ // to confirm that everything is ok
+ connection.setSoTimeout(10000)
+ val responseBytes = dis.readAllBytes()
+ val response = String(responseBytes)
+
+ return response.contains("OK")
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return false
+ }
+ }
+
+ fun run() {
+ ServerSocket(0).use { serverSocket ->
+ if (!authenticate(serverSocket.localPort)) {
+ statusCallback.accept(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.ERROR_AUTHENTICATION_FAILED,
+ ),
+ )
+ return
+ }
+
+ if (!upload(serverSocket)) {
+ statusCallback.accept(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.ERROR_UPLOAD_FAILED,
+ ),
+ )
+ return
+ }
+
+ statusCallback.accept(
+ UpdateStatusEvent(
+ deviceId,
+ FirmwareUpdateStatus.REBOOTING,
+ ),
+ )
+ }
+ }
+
+ companion object {
+ private const val FLASH = 0
+ private const val PORT = 8266
+ private const val PASSWORD = "SlimeVR-OTA"
+ private const val AUTH = 200
+ }
+}
diff --git a/server/core/src/main/java/dev/slimevr/firmware/SerialFlashingHandler.kt b/server/core/src/main/java/dev/slimevr/firmware/SerialFlashingHandler.kt
new file mode 100644
index 0000000000..1564ab3341
--- /dev/null
+++ b/server/core/src/main/java/dev/slimevr/firmware/SerialFlashingHandler.kt
@@ -0,0 +1,5 @@
+package dev.slimevr.firmware
+
+import dev.llelievr.espflashkotlin.FlasherSerialInterface
+
+interface SerialFlashingHandler : FlasherSerialInterface
diff --git a/server/core/src/main/java/dev/slimevr/firmware/SerialRebootHandler.kt b/server/core/src/main/java/dev/slimevr/firmware/SerialRebootHandler.kt
new file mode 100644
index 0000000000..28cc87c6e8
--- /dev/null
+++ b/server/core/src/main/java/dev/slimevr/firmware/SerialRebootHandler.kt
@@ -0,0 +1,56 @@
+package dev.slimevr.firmware
+
+import dev.slimevr.serial.SerialListener
+import dev.slimevr.serial.SerialPort
+import java.util.concurrent.CopyOnWriteArrayList
+
+interface SerialRebootListener {
+ fun onSerialDeviceReconnect(deviceHandle: Pair, () -> Unit>)
+}
+
+/**
+ * This class watch for a serial device to disconnect then reconnect.
+ * This is used to watch the user progress through the firmware update process
+ */
+class SerialRebootHandler(
+ private val watchRestartQueue: MutableList, () -> Unit>>,
+ // Could be moved to a list of listeners later
+ private val serialRebootListener: SerialRebootListener,
+) : SerialListener {
+
+ private var currentPort: SerialPort? = null
+ private val disconnectedDevices: MutableList = CopyOnWriteArrayList()
+
+ override fun onSerialConnected(port: SerialPort) {
+ currentPort = port
+ }
+
+ override fun onSerialDisconnected() {
+ currentPort = null
+ }
+
+ override fun onSerialLog(str: String) {
+ if (str.contains("starting up...")) {
+ val foundPort = watchRestartQueue.find { it.first.id == currentPort?.portLocation }
+ if (foundPort !== null) {
+ disconnectedDevices.remove(currentPort)
+ serialRebootListener.onSerialDeviceReconnect(foundPort)
+ }
+ }
+ }
+
+ override fun onNewSerialDevice(port: SerialPort) {
+ val foundPort = watchRestartQueue.find { it.first.id == port.portLocation }
+ if (foundPort !== null && disconnectedDevices.contains(port)) {
+ disconnectedDevices.remove(port)
+ serialRebootListener.onSerialDeviceReconnect(foundPort)
+ }
+ }
+
+ override fun onSerialDeviceDeleted(port: SerialPort) {
+ val foundPort = watchRestartQueue.find { it.first.id === port.portLocation }
+ if (foundPort != null) {
+ disconnectedDevices.add(port)
+ }
+ }
+}
diff --git a/server/core/src/main/java/dev/slimevr/firmware/UpdateDeviceId.kt b/server/core/src/main/java/dev/slimevr/firmware/UpdateDeviceId.kt
new file mode 100644
index 0000000000..5bc6dc5899
--- /dev/null
+++ b/server/core/src/main/java/dev/slimevr/firmware/UpdateDeviceId.kt
@@ -0,0 +1,24 @@
+package dev.slimevr.firmware
+
+data class UpdateDeviceId(
+ val type: FirmwareUpdateMethod,
+ val id: T,
+) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as UpdateDeviceId<*>
+
+ if (type != other.type) return false
+ if (id != other.id) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = type.hashCode()
+ result = 31 * result + (id?.hashCode() ?: 0)
+ return result
+ }
+}
diff --git a/server/core/src/main/java/dev/slimevr/firmware/UpdateStatusEvent.kt b/server/core/src/main/java/dev/slimevr/firmware/UpdateStatusEvent.kt
new file mode 100644
index 0000000000..85b485a95b
--- /dev/null
+++ b/server/core/src/main/java/dev/slimevr/firmware/UpdateStatusEvent.kt
@@ -0,0 +1,8 @@
+package dev.slimevr.firmware
+
+data class UpdateStatusEvent(
+ val deviceId: UpdateDeviceId,
+ val status: FirmwareUpdateStatus,
+ val progress: Int = 0,
+ val time: Long = System.currentTimeMillis(),
+)
diff --git a/server/core/src/main/java/dev/slimevr/protocol/ProtocolAPI.java b/server/core/src/main/java/dev/slimevr/protocol/ProtocolAPI.java
index 7bb090e643..7908ecc3a5 100644
--- a/server/core/src/main/java/dev/slimevr/protocol/ProtocolAPI.java
+++ b/server/core/src/main/java/dev/slimevr/protocol/ProtocolAPI.java
@@ -65,4 +65,5 @@ public void registerAPIServer(ProtocolAPIServer server) {
public void removeAPIServer(ProtocolAPIServer server) {
this.servers.remove(server);
}
+
}
diff --git a/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java b/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java
index 3eb480af0d..86c2429625 100644
--- a/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java
+++ b/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java
@@ -38,8 +38,6 @@ public static int createHardwareInfo(FlatBufferBuilder fbb, Device device) {
? fbb.createString(device.getManufacturer())
: 0;
- int boardTypeOffset = fbb.createString(device.getBoardType().toString());
-
int hardwareIdentifierOffset = fbb.createString(device.getHardwareIdentifier());
HardwareInfo.startHardwareInfo(fbb);
@@ -68,7 +66,7 @@ public static int createHardwareInfo(FlatBufferBuilder fbb, Device device) {
// TODO need support: HardwareInfo.addDisplayName(fbb, de);
HardwareInfo.addMcuId(fbb, device.getMcuType().getSolarType());
- HardwareInfo.addBoardType(fbb, boardTypeOffset);
+ HardwareInfo.addOfficialBoardType(fbb, device.getBoardType().getSolarType());
return HardwareInfo.endHardwareInfo(fbb);
}
@@ -349,7 +347,7 @@ public static int createDevicesData(
for (int i = 0; i < devices.size(); i++) {
Device device = devices.get(i);
devicesDataOffsets[i] = DataFeedBuilder
- .createDeviceData(fbb, i, deviceDataMaskT, device);
+ .createDeviceData(fbb, device.getId(), deviceDataMaskT, device);
}
return DataFeedUpdate.createDevicesVector(fbb, devicesDataOffsets);
diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt b/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt
index d9995f647e..850644ea1e 100644
--- a/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt
+++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt
@@ -8,6 +8,7 @@ import dev.slimevr.protocol.ProtocolAPI
import dev.slimevr.protocol.ProtocolHandler
import dev.slimevr.protocol.datafeed.DataFeedBuilder
import dev.slimevr.protocol.rpc.autobone.RPCAutoBoneHandler
+import dev.slimevr.protocol.rpc.firmware.RPCFirmwareUpdateHandler
import dev.slimevr.protocol.rpc.reset.RPCResetHandler
import dev.slimevr.protocol.rpc.serial.RPCProvisioningHandler
import dev.slimevr.protocol.rpc.serial.RPCSerialHandler
@@ -41,6 +42,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler) {
+ val fbb = FlatBufferBuilder(32)
+
+ val dataUnion = FirmwareUpdateDeviceIdUnion()
+ dataUnion.type = event.deviceId.type.id
+ dataUnion.value = createUpdateDeviceId(event.deviceId)
+
+ val deviceIdOffset = FirmwareUpdateDeviceIdUnion.pack(fbb, dataUnion)
+
+ FirmwareUpdateStatusResponse.startFirmwareUpdateStatusResponse(fbb)
+ FirmwareUpdateStatusResponse.addStatus(fbb, event.status.id)
+ FirmwareUpdateStatusResponse.addDeviceIdType(fbb, dataUnion.type)
+ FirmwareUpdateStatusResponse.addDeviceId(fbb, deviceIdOffset)
+ FirmwareUpdateStatusResponse.addProgress(fbb, event.progress.toByte())
+
+ val update = FirmwareUpdateStatusResponse.endFirmwareUpdateStatusResponse(fbb)
+ val outbound = rpcHandler.createRPCMessage(
+ fbb,
+ RpcMessage.FirmwareUpdateStatusResponse,
+ update,
+ )
+ fbb.finish(outbound)
+
+ api
+ .apiServers.forEach { server ->
+ server.apiConnections.forEach { conn ->
+ conn.send(fbb.dataBuffer())
+ }
+ }
+ }
+
+ private fun buildUpdateDeviceId(req: FirmwareUpdateRequestT): UpdateDeviceId? {
+ when (req.method.type) {
+ FirmwareUpdateDeviceId.solarxr_protocol_datatypes_DeviceIdTable -> {
+ return UpdateDeviceId(
+ FirmwareUpdateMethod.OTA,
+ req.method.asOTAFirmwareUpdate().deviceId.id,
+ )
+ }
+
+ FirmwareUpdateDeviceId.SerialDevicePort -> {
+ return UpdateDeviceId(
+ FirmwareUpdateMethod.SERIAL,
+ req.method.asSerialFirmwareUpdate().deviceId.port,
+ )
+ }
+ }
+ return null
+ }
+
+ private fun createUpdateDeviceId(data: UpdateDeviceId<*>): Any = when (data.type) {
+ FirmwareUpdateMethod.NONE -> error("Unsupported method")
+
+ FirmwareUpdateMethod.OTA -> {
+ if (data.id !is Int) {
+ error("Invalid state, the id type should be Int")
+ }
+ DeviceIdTableT().apply {
+ id = DeviceIdT().apply {
+ id = data.id
+ }
+ }
+ }
+
+ FirmwareUpdateMethod.SERIAL -> {
+ if (data.id !is String) {
+ error("Invalid state, the id type should be String")
+ }
+ SerialDevicePortT().apply {
+ port = data.id
+ }
+ }
+ }
+}
diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCProvisioningHandler.java b/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCProvisioningHandler.java
index fa58bbc519..97ae898398 100644
--- a/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCProvisioningHandler.java
+++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCProvisioningHandler.java
@@ -6,6 +6,7 @@
import dev.slimevr.protocol.rpc.RPCHandler;
import dev.slimevr.serial.ProvisioningListener;
import dev.slimevr.serial.ProvisioningStatus;
+import dev.slimevr.serial.SerialPort;
import solarxr_protocol.rpc.*;
import java.util.function.Consumer;
@@ -59,7 +60,7 @@ public void onStopWifiProvisioningRequest(
}
@Override
- public void onProvisioningStatusChange(ProvisioningStatus status) {
+ public void onProvisioningStatusChange(ProvisioningStatus status, SerialPort port) {
FlatBufferBuilder fbb = new FlatBufferBuilder(32);
diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCSerialHandler.java b/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCSerialHandler.java
index 2e1a42ff0f..56496c6c29 100644
--- a/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCSerialHandler.java
+++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCSerialHandler.java
@@ -7,6 +7,7 @@
import dev.slimevr.serial.SerialListener;
import dev.slimevr.serial.SerialPort;
import io.eiren.util.logging.LogManager;
+import org.jetbrains.annotations.NotNull;
import solarxr_protocol.rpc.*;
import java.util.ArrayList;
@@ -274,4 +275,7 @@ public void forAllListeners(Consumer super GenericConnection> action) {
);
}
+ @Override
+ public void onSerialDeviceDeleted(@NotNull SerialPort port) {
+ }
}
diff --git a/server/core/src/main/java/dev/slimevr/serial/ProvisioningHandler.java b/server/core/src/main/java/dev/slimevr/serial/ProvisioningHandler.java
index b65a9da7f3..b87c08e5e6 100644
--- a/server/core/src/main/java/dev/slimevr/serial/ProvisioningHandler.java
+++ b/server/core/src/main/java/dev/slimevr/serial/ProvisioningHandler.java
@@ -2,6 +2,7 @@
import dev.slimevr.VRServer;
import io.eiren.util.logging.LogManager;
+import kotlin.text.Regex;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -80,6 +81,10 @@ public void initSerial(String port) {
}
+ public void tryOptainMacAddress() {
+ this.changeStatus(ProvisioningStatus.OBTAINING_MAC_ADDRESS);
+ vrServer.serialHandler.infoRequest();
+ }
public void tryProvisioning() {
this.changeStatus(ProvisioningStatus.PROVISIONING);
@@ -97,12 +102,16 @@ public void provisioningTick() {
if (System.currentTimeMillis() - this.lastStatusChange > 10000) {
- if (this.provisioningStatus == ProvisioningStatus.NONE)
+ if (
+ this.provisioningStatus == ProvisioningStatus.NONE
+ || this.provisioningStatus == ProvisioningStatus.SERIAL_INIT
+ )
this.initSerial(this.preferredPort);
- else if (this.provisioningStatus == ProvisioningStatus.SERIAL_INIT)
- initSerial(this.preferredPort);
- else if (this.provisioningStatus == ProvisioningStatus.PROVISIONING)
- this.tryProvisioning();
+ else if (
+ this.provisioningStatus == ProvisioningStatus.OBTAINING_MAC_ADDRESS
+ || this.provisioningStatus == ProvisioningStatus.PROVISIONING
+ )
+ this.tryOptainMacAddress();
else if (this.provisioningStatus == ProvisioningStatus.LOOKING_FOR_SERVER)
this.changeStatus(ProvisioningStatus.COULD_NOT_FIND_SERVER);
}
@@ -113,7 +122,7 @@ else if (this.provisioningStatus == ProvisioningStatus.LOOKING_FOR_SERVER)
public void onSerialConnected(@NotNull SerialPort port) {
if (!isRunning)
return;
- this.tryProvisioning();
+ this.tryOptainMacAddress();
}
@Override
@@ -129,6 +138,23 @@ public void onSerialLog(@NotNull String str) {
if (!isRunning)
return;
+ if (
+ provisioningStatus == ProvisioningStatus.OBTAINING_MAC_ADDRESS && str.contains("mac:")
+ ) {
+ var match = new Regex("mac: (?([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})), ")
+ .find(str, str.indexOf("mac:"));
+
+ if (match != null) {
+ var b = match.getGroups().get(1);
+ if (b != null) {
+ vrServer.configManager.getVrConfig().addKnownDevice(b.getValue());
+ vrServer.configManager.saveConfig();
+ this.tryProvisioning();
+ }
+ }
+
+ }
+
if (
provisioningStatus == ProvisioningStatus.PROVISIONING
&& str.contains("New wifi credentials set")
@@ -166,7 +192,11 @@ public void onSerialLog(@NotNull String str) {
public void changeStatus(ProvisioningStatus status) {
this.lastStatusChange = System.currentTimeMillis();
if (this.provisioningStatus != status) {
- this.listeners.forEach((l) -> l.onProvisioningStatusChange(status));
+ this.listeners
+ .forEach(
+ (l) -> l
+ .onProvisioningStatusChange(status, vrServer.serialHandler.getCurrentPort())
+ );
this.provisioningStatus = status;
}
}
@@ -186,4 +216,7 @@ public void removeListener(ProvisioningListener l) {
listeners.removeIf(listener -> l == listener);
}
+ @Override
+ public void onSerialDeviceDeleted(@NotNull SerialPort port) {
+ }
}
diff --git a/server/core/src/main/java/dev/slimevr/serial/ProvisioningListener.java b/server/core/src/main/java/dev/slimevr/serial/ProvisioningListener.java
index 636d62c5c6..a1cf17a03d 100644
--- a/server/core/src/main/java/dev/slimevr/serial/ProvisioningListener.java
+++ b/server/core/src/main/java/dev/slimevr/serial/ProvisioningListener.java
@@ -2,5 +2,5 @@
public interface ProvisioningListener {
- void onProvisioningStatusChange(ProvisioningStatus status);
+ void onProvisioningStatusChange(ProvisioningStatus status, SerialPort port);
}
diff --git a/server/core/src/main/java/dev/slimevr/serial/ProvisioningStatus.java b/server/core/src/main/java/dev/slimevr/serial/ProvisioningStatus.java
index 9038550241..cf897cc9f7 100644
--- a/server/core/src/main/java/dev/slimevr/serial/ProvisioningStatus.java
+++ b/server/core/src/main/java/dev/slimevr/serial/ProvisioningStatus.java
@@ -1,15 +1,19 @@
package dev.slimevr.serial;
+import solarxr_protocol.rpc.WifiProvisioningStatus;
+
+
public enum ProvisioningStatus {
- NONE(0),
- SERIAL_INIT(1),
- PROVISIONING(2),
- CONNECTING(3),
- CONNECTION_ERROR(4),
- LOOKING_FOR_SERVER(5),
- COULD_NOT_FIND_SERVER(6),
- DONE(7);
+ NONE(WifiProvisioningStatus.NONE),
+ SERIAL_INIT(WifiProvisioningStatus.SERIAL_INIT),
+ PROVISIONING(WifiProvisioningStatus.PROVISIONING),
+ OBTAINING_MAC_ADDRESS(WifiProvisioningStatus.OBTAINING_MAC_ADDRESS),
+ CONNECTING(WifiProvisioningStatus.CONNECTING),
+ CONNECTION_ERROR(WifiProvisioningStatus.CONNECTION_ERROR),
+ LOOKING_FOR_SERVER(WifiProvisioningStatus.LOOKING_FOR_SERVER),
+ COULD_NOT_FIND_SERVER(WifiProvisioningStatus.COULD_NOT_FIND_SERVER),
+ DONE(WifiProvisioningStatus.DONE);
public final int id;
diff --git a/server/core/src/main/java/dev/slimevr/serial/SerialHandler.kt b/server/core/src/main/java/dev/slimevr/serial/SerialHandler.kt
index 99346cf5fe..bbd173779e 100644
--- a/server/core/src/main/java/dev/slimevr/serial/SerialHandler.kt
+++ b/server/core/src/main/java/dev/slimevr/serial/SerialHandler.kt
@@ -15,7 +15,9 @@ abstract class SerialHandler {
abstract fun infoRequest()
abstract fun wifiScanRequest()
abstract fun closeSerial()
+ abstract fun write(buff: ByteArray)
abstract fun setWifi(ssid: String, passwd: String)
+ abstract fun getCurrentPort(): SerialPort?
companion object {
val supportedSerial: Set> = setOf(
@@ -65,5 +67,9 @@ class SerialHandlerStub : SerialHandler() {
override fun closeSerial() {}
+ override fun write(buff: ByteArray) {}
+
override fun setWifi(ssid: String, passwd: String) {}
+
+ override fun getCurrentPort(): SerialPort? = null
}
diff --git a/server/core/src/main/java/dev/slimevr/serial/SerialListener.kt b/server/core/src/main/java/dev/slimevr/serial/SerialListener.kt
index 861e770a9e..32fe72087b 100644
--- a/server/core/src/main/java/dev/slimevr/serial/SerialListener.kt
+++ b/server/core/src/main/java/dev/slimevr/serial/SerialListener.kt
@@ -25,4 +25,7 @@ interface SerialListener {
fun onSerialDisconnected()
fun onSerialLog(str: String)
fun onNewSerialDevice(port: SerialPort)
+
+ // This is called when the serial diver does not see the device anymore
+ fun onSerialDeviceDeleted(port: SerialPort)
}
diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/Device.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/Device.kt
index 4fca49688a..79d8c79f1f 100644
--- a/server/core/src/main/java/dev/slimevr/tracking/trackers/Device.kt
+++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/Device.kt
@@ -20,7 +20,7 @@ open class Device(val magSupport: Boolean = false) {
* Implement toString() to return a string that uniquely identifies the board type
* SHOULDN'T RETURN NULL WHEN toString() IS CALLED
*/
- open val boardType: Any = BoardType.UNKNOWN
+ open val boardType: BoardType = BoardType.UNKNOWN
open val mcuType: MCUType = MCUType.UNKNOWN
open val hardwareIdentifier: String = "Unknown"
diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt
index e5d8ab2f58..1902287d50 100644
--- a/server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt
+++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt
@@ -110,6 +110,8 @@ class Tracker @JvmOverloads constructor(
}
checkReportErrorStatus()
checkReportRequireReset()
+
+ VRServer.instance.trackerStatusChanged(this, old, new)
}
}
diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/TrackerStatusListener.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/TrackerStatusListener.kt
new file mode 100644
index 0000000000..4f4069be67
--- /dev/null
+++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/TrackerStatusListener.kt
@@ -0,0 +1,6 @@
+package dev.slimevr.tracking.trackers
+
+interface TrackerStatusListener {
+
+ fun onTrackerStatusChanged(tracker: Tracker, oldStatus: TrackerStatus, newStatus: TrackerStatus)
+}
diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/FirmwareConstants.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/FirmwareConstants.kt
index 164d7d4a64..a82a5f26e9 100644
--- a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/FirmwareConstants.kt
+++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/FirmwareConstants.kt
@@ -42,7 +42,7 @@ enum class BoardType(val id: UInt) {
ESP01(8u),
SLIMEVR(9u),
LOLIN_C3_MINI(10u),
- BEETLE32C32(11u),
+ BEETLE32C3(11u),
ES32C3DEVKITM1(12u),
OWOTRACK(13u),
WRANGLER(14u),
@@ -53,6 +53,8 @@ enum class BoardType(val id: UInt) {
DEV_RESERVED(250u),
;
+ fun getSolarType(): Int = this.id.toInt()
+
override fun toString(): String = when (this) {
UNKNOWN -> "Unknown"
SLIMEVR_LEGACY -> "SlimeVR Legacy"
@@ -65,7 +67,7 @@ enum class BoardType(val id: UInt) {
ESP01 -> "ESP-01"
SLIMEVR -> "SlimeVR"
LOLIN_C3_MINI -> "Lolin C3 Mini"
- BEETLE32C32 -> "Beetle ESP32-C3"
+ BEETLE32C3 -> "Beetle ESP32-C3"
ES32C3DEVKITM1 -> "Espressif ESP32-C3 DevKitM-1"
OWOTRACK -> "owoTrack"
WRANGLER -> "Wrangler Joycons"
diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/Main.kt b/server/desktop/src/main/java/dev/slimevr/desktop/Main.kt
index cc4662d70c..e1719a53d0 100644
--- a/server/desktop/src/main/java/dev/slimevr/desktop/Main.kt
+++ b/server/desktop/src/main/java/dev/slimevr/desktop/Main.kt
@@ -6,6 +6,7 @@ import dev.slimevr.Keybinding
import dev.slimevr.SLIMEVR_IDENTIFIER
import dev.slimevr.VRServer
import dev.slimevr.bridge.ISteamVRBridge
+import dev.slimevr.desktop.firmware.DesktopSerialFlashingHandler
import dev.slimevr.desktop.platform.SteamVRBridge
import dev.slimevr.desktop.platform.linux.UnixSocketBridge
import dev.slimevr.desktop.platform.windows.WindowsNamedPipeBridge
@@ -121,6 +122,7 @@ fun main(args: Array) {
::provideSteamVRBridge,
::provideFeederBridge,
{ _ -> DesktopSerialHandler() },
+ { _ -> DesktopSerialFlashingHandler() },
configPath = configDir,
)
vrServer.start()
diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/firmware/DesktopSerialFlashingHandler.kt b/server/desktop/src/main/java/dev/slimevr/desktop/firmware/DesktopSerialFlashingHandler.kt
new file mode 100644
index 0000000000..7adc3d4ed3
--- /dev/null
+++ b/server/desktop/src/main/java/dev/slimevr/desktop/firmware/DesktopSerialFlashingHandler.kt
@@ -0,0 +1,87 @@
+package dev.slimevr.desktop.firmware
+
+import com.fazecast.jSerialComm.SerialPort
+import dev.slimevr.firmware.SerialFlashingHandler
+import dev.slimevr.serial.SerialPort as SerialPortWrapper
+
+class DesktopSerialFlashingHandler : SerialFlashingHandler {
+ private var port: SerialPort? = null
+
+ override fun openSerial(port: Any) {
+ if (port !is SerialPortWrapper) {
+ error("Not a serial port")
+ }
+ val ports = SerialPort.getCommPorts()
+ val comPort = ports.find { it.portLocation == port.portLocation }
+ ?: error("Unable to find port ${port.portLocation}")
+ if (comPort.isOpen) {
+ comPort.closePort()
+ }
+ if (!comPort.openPort(1000)) {
+ error("unable to open port")
+ }
+ this.port = comPort
+ }
+
+ override fun closeSerial() {
+ val p = port ?: error("no port to close")
+ try {
+ p.closePort()
+ println("Port closed")
+ } catch (e: Exception) {
+ error("unable to close port")
+ }
+ }
+
+ override fun setDTR(value: Boolean) {
+ val p = port ?: error("no port to set DTR")
+ if (value) {
+ p.setDTR()
+ } else {
+ p.clearDTR()
+ }
+ }
+
+ override fun setRTS(value: Boolean) {
+ val p = port ?: error("no port to set RTS")
+ if (value) {
+ p.setRTS()
+ } else {
+ p.clearRTS()
+ }
+ }
+
+ override fun write(data: ByteArray) {
+ val p = port ?: error("no port to write")
+ p.writeBytes(data, data.size)
+ }
+
+ override fun read(length: Int): ByteArray {
+ val p = port ?: error("no port to read")
+ val data = ByteArray(length)
+ p.readBytes(data, length)
+ return data
+ }
+
+ override fun changeBaud(baud: Int) {
+ val p = port ?: error("no port to set the baud")
+ if (!p.setBaudRate(baud)) {
+ error("Unable to change baudrate")
+ }
+ }
+
+ override fun setReadTimeout(timeout: Long) {
+ val p = port ?: error("no port to set the timeout")
+ p.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING, timeout.toInt(), 0)
+ }
+
+ override fun availableBytes(): Int {
+ val p = port ?: error("no port to check available bytes")
+ return p.bytesAvailable()
+ }
+
+ override fun flushIOBuffers() {
+ val p = port ?: error("no port to flush")
+ p.flushIOBuffers()
+ }
+}
diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/serial/DesktopSerialHandler.kt b/server/desktop/src/main/java/dev/slimevr/desktop/serial/DesktopSerialHandler.kt
index 716d4781d5..5e44b32a9e 100644
--- a/server/desktop/src/main/java/dev/slimevr/desktop/serial/DesktopSerialHandler.kt
+++ b/server/desktop/src/main/java/dev/slimevr/desktop/serial/DesktopSerialHandler.kt
@@ -71,10 +71,14 @@ class DesktopSerialHandler :
getDevicesTimer.purge()
}
- fun onNewDevice(port: SerialPort) {
+ private fun onNewDevice(port: SerialPort) {
listeners.forEach { it.onNewSerialDevice(SerialPortWrapper(port)) }
}
+ private fun onDeviceDel(port: SerialPort) {
+ listeners.forEach { it.onSerialDeviceDeleted(SerialPortWrapper(port)) }
+ }
+
override fun addListener(channel: SerialListener) {
listeners.add(channel)
}
@@ -181,6 +185,11 @@ class DesktopSerialHandler :
}
}
+ override fun write(buff: ByteArray) {
+ LogManager.info("[SerialHandler] WRITING $buff")
+ currentPort?.outputStream?.write(buff)
+ }
+
@Synchronized
override fun setWifi(ssid: String, passwd: String) {
val os = currentPort?.outputStream ?: return
@@ -236,13 +245,20 @@ class DesktopSerialHandler :
private fun detectNewPorts() {
try {
- val differences = knownPorts.asSequence() - lastKnownPorts
+ val addDifferences = knownPorts.asSequence() - lastKnownPorts
+ val delDifferences = lastKnownPorts - knownPorts.asSequence().toSet()
lastKnownPorts = SerialPort.getCommPorts().map { SerialPortWrapper(it) }.toSet()
- differences.forEach { onNewDevice(it.port) }
+ addDifferences.forEach { onNewDevice(it.port) }
+ delDifferences.forEach { onDeviceDel(it.port) }
} catch (e: Throwable) {
LogManager
.severe("[SerialHandler] Using serial ports is not supported on this platform", e)
throw RuntimeException("Serial unsupported")
}
}
+
+ override fun getCurrentPort(): dev.slimevr.serial.SerialPort? {
+ val port = this.currentPort ?: return null
+ return SerialPortWrapper(port)
+ }
}