diff --git a/apps/yaak-client/components/HttpAuthenticationEditor.tsx b/apps/yaak-client/components/HttpAuthenticationEditor.tsx index ec08c115..278ea922 100644 --- a/apps/yaak-client/components/HttpAuthenticationEditor.tsx +++ b/apps/yaak-client/components/HttpAuthenticationEditor.tsx @@ -10,14 +10,17 @@ import { HStack, Icon, InlineCode } from "@yaakapp-internal/ui"; import { useCallback } from "react"; import { openFolderSettings } from "../commands/openFolderSettings"; import { openWorkspaceSettings } from "../commands/openWorkspaceSettings"; +import { useAuthDropdownOptions } from "../hooks/useAuthTab"; import { useHttpAuthenticationConfig } from "../hooks/useHttpAuthenticationConfig"; import { useInheritedAuthentication } from "../hooks/useInheritedAuthentication"; import { useRenderTemplate } from "../hooks/useRenderTemplate"; import { resolvedModelName } from "../lib/resolvedModelName"; +import { Button } from "./core/Button"; import { Dropdown, type DropdownItem } from "./core/Dropdown"; import { IconButton } from "./core/IconButton"; import { Input, type InputProps } from "./core/Input"; import { Link } from "./core/Link"; +import { RadioDropdown } from "./core/RadioDropdown"; import { SegmentedControl } from "./core/SegmentedControl"; import { DynamicForm } from "./DynamicForm"; import { EmptyStateText } from "./EmptyStateText"; @@ -35,7 +38,8 @@ export function HttpAuthenticationEditor({ model }: Props) { ); const handleChange = useCallback( - async (authentication: Record) => await patchModel(model, { authentication }), + async (authentication: Record) => + await patchModel(model, { authentication }), [model], ); @@ -47,7 +51,8 @@ export function HttpAuthenticationEditor({ model }: Props) { return (

- Auth plugin not found for {model.authenticationType} + Auth plugin not found for{" "} + {model.authenticationType}

); @@ -56,11 +61,20 @@ export function HttpAuthenticationEditor({ model }: Props) { if (inheritedAuth == null) { if (model.model === "workspace" || model.model === "folder") { return ( - -

- Apply auth to all requests in {resolvedModelName(model)} -

- Documentation + +
+

+ Choose an auth method to apply it to all requests in{" "} + + {resolvedModelName(model)} + + . +

+ + + Documentation + +
); } @@ -83,7 +97,8 @@ export function HttpAuthenticationEditor({ model }: Props) { type="submit" className="underline hover:text-text" onClick={() => { - if (inheritedAuth.model === "folder") openFolderSettings(inheritedAuth.id, "auth"); + if (inheritedAuth.model === "folder") + openFolderSettings(inheritedAuth.id, "auth"); else openWorkspaceSettings("auth"); }} > @@ -103,7 +118,8 @@ export function HttpAuthenticationEditor({ model }: Props) { hideLabel name="enabled" value={ - model.authentication.disabled === false || model.authentication.disabled == null + model.authentication.disabled === false || + model.authentication.disabled == null ? "__TRUE__" : model.authentication.disabled === true ? "__FALSE__" @@ -151,7 +167,9 @@ export function HttpAuthenticationEditor({ model }: Props) { className="w-full" stateKey={`auth.${model.id}.dynamic`} value={model.authentication.disabled} - onChange={(v) => handleChange({ ...model.authentication, disabled: v })} + onChange={(v) => + handleChange({ ...model.authentication, disabled: v }) + } /> )} @@ -169,6 +187,33 @@ export function HttpAuthenticationEditor({ model }: Props) { ); } +function AuthenticationTypeDropdown({ model }: Props) { + const options = useAuthDropdownOptions(model); + + if (options == null) return null; + + return ( + + + + ); +} + function AuthenticationDisabledInput({ value, onChange, @@ -198,7 +243,11 @@ function AuthenticationDisabledInput({ rightSlot={
- {rendered.isPending ? "loading" : rendered.data ? "enabled" : "disabled"} + {rendered.isPending + ? "loading" + : rendered.data + ? "enabled" + : "disabled"}
} diff --git a/apps/yaak-client/components/ModelSettingsEditor.tsx b/apps/yaak-client/components/ModelSettingsEditor.tsx index 8d37913d..bfe2e722 100644 --- a/apps/yaak-client/components/ModelSettingsEditor.tsx +++ b/apps/yaak-client/components/ModelSettingsEditor.tsx @@ -13,6 +13,7 @@ import { modelSupportsSetting, type RequestSettingDefinition, SETTING_FOLLOW_REDIRECTS, + SETTING_REQUEST_MESSAGE_SIZE, SETTING_REQUEST_TIMEOUT, SETTING_SEND_COOKIES, SETTING_STORE_COOKIES, @@ -22,21 +23,44 @@ import { Checkbox } from "./core/Checkbox"; import { PlainInput } from "./core/PlainInput"; import { SettingOverrideRow, + SettingRow, SettingRowBoolean, - SettingRowNumber, SettingsList, SettingsSection, } from "./core/SettingRow"; +const BYTES_PER_MB = 1024 * 1024; +const MAX_REQUEST_MESSAGE_SIZE_BYTES = 2_147_483_647; +const MAX_MESSAGE_SIZE_MB = MAX_REQUEST_MESSAGE_SIZE_BYTES / BYTES_PER_MB; + interface Props { showSectionTitles?: boolean; model: ModelWithSettings; } -type ModelWithSettings = Workspace | Folder | HttpRequest | WebsocketRequest | GrpcRequest; +type ModelWithSettings = + | Workspace + | Folder + | HttpRequest + | WebsocketRequest + | GrpcRequest; type ModelWithHttpSettings = Workspace | Folder | HttpRequest; -type ModelWithTlsSettings = Workspace | Folder | HttpRequest | WebsocketRequest | GrpcRequest; -type ModelWithCookieSettings = Workspace | Folder | HttpRequest | WebsocketRequest; +type ModelWithTlsSettings = + | Workspace + | Folder + | HttpRequest + | WebsocketRequest + | GrpcRequest; +type ModelWithCookieSettings = + | Workspace + | Folder + | HttpRequest + | WebsocketRequest; +type ModelWithMessageSizeSettings = + | Workspace + | Folder + | WebsocketRequest + | GrpcRequest; type BooleanSetting = boolean | InheritedBoolSetting; type IntegerSetting = number | InheritedIntSetting; type CookieSettingsPatch = { @@ -50,12 +74,19 @@ type HttpSettingsPatch = { type TlsSettingsPatch = { settingValidateCertificates?: ModelWithTlsSettings["settingValidateCertificates"]; }; +type MessageSizeSettingsPatch = { + settingRequestMessageSize?: ModelWithMessageSizeSettings["settingRequestMessageSize"]; +}; -export function ModelSettingsEditor({ model, showSectionTitles = false }: Props) { +export function ModelSettingsEditor({ + model, + showSectionTitles = false, +}: Props) { const ancestors = useModelAncestors(model); const supportsHttpSettings = modelSupportsHttpSettings(model); const supportsCookieSettings = modelSupportsCookieSettings(model); const supportsTlsSettings = modelSupportsTlsSettings(model); + const supportsMessageSizeSettings = modelSupportsMessageSizeSettings(model); return ( @@ -77,6 +108,22 @@ export function ModelSettingsEditor({ model, showSectionTitles = false }: Props) } /> )} + {supportsMessageSizeSettings && ( + + patchMessageSizeSettings(model, { + settingRequestMessageSize, + }) + } + /> + )} )} {supportsCookieSettings && ( - + isInheritedSetting(setting) && setting.enabled === true) - .length; + if (modelSupportsMessageSizeSettings(model)) { + settings.push(model.settingRequestMessageSize); + } + + return settings.filter( + (setting) => isInheritedSetting(setting) && setting.enabled === true, + ).length; } -function patchCookieSettings(model: ModelWithCookieSettings, patch: Partial) { - if (model.model === "workspace") return patchModel(model, patch as Partial); - if (model.model === "folder") return patchModel(model, patch as Partial); - if (model.model === "http_request") return patchModel(model, patch as Partial); - if (model.model === "websocket_request") - return patchModel(model, patch as Partial); - throw new Error("Unsupported cookie settings model"); +function patchCookieSettings( + model: ModelWithCookieSettings, + patch: Partial, +) { + switch (model.model) { + case "workspace": + return patchModel(model, patch as Partial); + case "folder": + return patchModel(model, patch as Partial); + case "http_request": + return patchModel(model, patch as Partial); + case "websocket_request": + return patchModel(model, patch as Partial); + } } -function patchHttpSettings(model: ModelWithHttpSettings, patch: Partial) { - if (model.model === "workspace") return patchModel(model, patch as Partial); - if (model.model === "folder") return patchModel(model, patch as Partial); - return patchModel(model, patch as Partial); +function patchHttpSettings( + model: ModelWithHttpSettings, + patch: Partial, +) { + switch (model.model) { + case "workspace": + return patchModel(model, patch as Partial); + case "folder": + return patchModel(model, patch as Partial); + case "http_request": + return patchModel(model, patch as Partial); + } } -function patchTlsSettings(model: ModelWithTlsSettings, patch: Partial) { - if (model.model === "workspace") return patchModel(model, patch as Partial); - if (model.model === "folder") return patchModel(model, patch as Partial); - if (model.model === "http_request") return patchModel(model, patch as Partial); - if (model.model === "websocket_request") - return patchModel(model, patch as Partial); - return patchModel(model, patch as Partial); +function patchTlsSettings( + model: ModelWithTlsSettings, + patch: Partial, +) { + switch (model.model) { + case "workspace": + return patchModel(model, patch as Partial); + case "folder": + return patchModel(model, patch as Partial); + case "http_request": + return patchModel(model, patch as Partial); + case "websocket_request": + return patchModel(model, patch as Partial); + case "grpc_request": + return patchModel(model, patch as Partial); + } } -function modelSupportsHttpSettings(model: ModelWithSettings): model is ModelWithHttpSettings { +function patchMessageSizeSettings( + model: ModelWithMessageSizeSettings, + patch: Partial, +) { + switch (model.model) { + case "workspace": + return patchModel(model, patch as Partial); + case "folder": + return patchModel(model, patch as Partial); + case "websocket_request": + return patchModel(model, patch as Partial); + case "grpc_request": + return patchModel(model, patch as Partial); + } +} + +function modelSupportsHttpSettings( + model: ModelWithSettings, +): model is ModelWithHttpSettings { return modelSupportsSetting(model, SETTING_REQUEST_TIMEOUT); } -function modelSupportsCookieSettings(model: ModelWithSettings): model is ModelWithCookieSettings { +function modelSupportsCookieSettings( + model: ModelWithSettings, +): model is ModelWithCookieSettings { return modelSupportsSetting(model, SETTING_SEND_COOKIES); } -function modelSupportsTlsSettings(model: ModelWithSettings): model is ModelWithTlsSettings { +function modelSupportsTlsSettings( + model: ModelWithSettings, +): model is ModelWithTlsSettings { return modelSupportsSetting(model, SETTING_VALIDATE_CERTIFICATES); } +function modelSupportsMessageSizeSettings( + model: ModelWithSettings, +): model is ModelWithMessageSizeSettings { + return modelSupportsSetting(model, SETTING_REQUEST_MESSAGE_SIZE); +} + function BooleanSettingRow({ inheritedValue, setting, @@ -211,7 +317,11 @@ function BooleanSettingRow({ }) { const inherited = isInheritedSetting(setting); const overridden = inherited ? setting.enabled === true : false; - const value = inherited ? (overridden ? setting.value : inheritedValue) : setting; + const value = inherited + ? overridden + ? setting.value + : inheritedValue + : setting; if (!inherited) { return ( @@ -255,19 +365,28 @@ function IntegerSettingRow({ }) { const inherited = isInheritedSetting(setting); const overridden = inherited ? setting.enabled === true : false; - const value = inherited ? (overridden ? setting.value : inheritedValue) : setting; + const value = inherited + ? overridden + ? setting.value + : inheritedValue + : setting; if (!inherited) { return ( - value === "" || Number.parseInt(value, 10) >= 0} - onChange={(value) => onChange(value)} - /> + > + onChange(parseInteger(value))} + /> + ); } @@ -278,21 +397,18 @@ function IntegerSettingRow({ overridden={overridden} onResetOverride={() => onChange({ ...setting, enabled: false })} > - value === "" || Number.parseInt(value, 10) >= 0} + validate={isValidInteger} onChange={(value) => onChange({ ...setting, enabled: true, - value: Number.parseInt(value, 10) || 0, + value: parseInteger(value), }) } /> @@ -300,6 +416,141 @@ function IntegerSettingRow({ ); } +function MessageSizeSettingRow({ + inheritedValue, + setting, + settingDefinition, + onChange, +}: { + inheritedValue: number; + setting: IntegerSetting; + settingDefinition: RequestSettingDefinition<"settingRequestMessageSize">; + onChange: (setting: IntegerSetting) => void; +}) { + const inherited = isInheritedSetting(setting); + const overridden = inherited ? setting.enabled === true : false; + const value = inherited + ? overridden + ? setting.value + : inheritedValue + : setting; + const displayValue = formatMegabytes(value); + const placeholder = formatMegabytes(settingDefinition.defaultValue); + + if (!inherited) { + return ( + + onChange(parseMegabytes(value))} + /> + + ); + } + + return ( + onChange({ ...setting, enabled: false })} + > + + onChange({ + ...setting, + enabled: true, + value: parseMegabytes(value), + }) + } + /> + + ); +} + +function MessageSizeInput({ + label, + name, + onChange, + placeholder, + value, +}: { + label: string; + name: string; + onChange: (value: string) => void; + placeholder: string; + value: string; +}) { + return ( + + ); +} + +function NumberUnitInput({ + inputMode, + label, + name, + onChange, + placeholder, + step, + unit, + validate, + value, +}: { + inputMode?: "decimal" | "numeric"; + label: string; + name: string; + onChange: (value: string) => void; + placeholder: string; + step?: number | "any"; + unit: string; + validate: (value: string) => boolean; + value: string; +}) { + return ( + + {unit} + + } + onChange={onChange} + /> + ); +} + function isInheritedSetting( setting: T | { enabled?: boolean; value: T }, ): setting is { enabled?: boolean; value: T } { @@ -308,7 +559,7 @@ function isInheritedSetting( function resolveInheritedValue( ancestors: (Folder | Workspace)[], - key: "settingRequestTimeout", + key: "settingRequestTimeout" | "settingRequestMessageSize", fallback: IntegerSetting, ): number; function resolveInheritedValue( @@ -338,10 +589,46 @@ function resolveInheritedValue( type WorkspaceSettings = Pick< Workspace, | "settingFollowRedirects" + | "settingRequestMessageSize" | "settingRequestTimeout" | "settingSendCookies" | "settingStoreCookies" | "settingValidateCertificates" >; -type BooleanWorkspaceSettingKey = Exclude; +type BooleanWorkspaceSettingKey = Exclude< + keyof WorkspaceSettings, + "settingRequestTimeout" | "settingRequestMessageSize" +>; + +function formatMegabytes(bytes: number) { + const megabytes = bytes / BYTES_PER_MB; + return Number.isInteger(megabytes) + ? `${megabytes}` + : megabytes.toFixed(3).replace(/\.?0+$/, ""); +} + +function parseMegabytes(value: string) { + const megabytes = Number(value); + return Number.isFinite(megabytes) ? Math.round(megabytes * BYTES_PER_MB) : 0; +} + +function parseInteger(value: string) { + const parsed = Number(value); + return Number.isFinite(parsed) ? Math.trunc(parsed) : 0; +} + +function isValidInteger(value: string) { + const parsed = Number(value); + return value === "" || (Number.isInteger(parsed) && parsed >= 0); +} + +function isValidMegabytes(value: string) { + if (value === "") return true; + const megabytes = Number(value); + return ( + Number.isFinite(megabytes) && + megabytes >= 0 && + megabytes <= MAX_MESSAGE_SIZE_MB + ); +} diff --git a/apps/yaak-client/components/Settings/SettingsGeneral.tsx b/apps/yaak-client/components/Settings/SettingsGeneral.tsx index 42220d7b..91c16f0c 100644 --- a/apps/yaak-client/components/Settings/SettingsGeneral.tsx +++ b/apps/yaak-client/components/Settings/SettingsGeneral.tsx @@ -2,22 +2,14 @@ import { revealItemInDir } from "@tauri-apps/plugin-opener"; import { patchModel, settingsAtom } from "@yaakapp-internal/models"; import { Heading, VStack } from "@yaakapp-internal/ui"; import { useAtomValue } from "jotai"; -import { activeWorkspaceAtom } from "../../hooks/useActiveWorkspace"; import { useCheckForUpdates } from "../../hooks/useCheckForUpdates"; import { appInfo } from "../../lib/appInfo"; -import { - SETTING_FOLLOW_REDIRECTS, - SETTING_REQUEST_TIMEOUT, - SETTING_SEND_COOKIES, - SETTING_STORE_COOKIES, - SETTING_VALIDATE_CERTIFICATES, -} from "../../lib/requestSettings"; import { revealInFinderText } from "../../lib/reveal"; import { CargoFeature } from "../CargoFeature"; +import { DismissibleBanner } from "../core/DismissibleBanner"; import { IconButton } from "../core/IconButton"; import { ModelSettingRowBoolean, - ModelSettingRowNumber, ModelSettingSelectControl, SettingValue, SettingRow, @@ -27,20 +19,26 @@ import { SettingsSection, } from "../core/SettingRow"; +const WORKSPACE_SETTINGS_MOVED_AT = "2026-06-30"; + export function SettingsGeneral() { - const workspace = useAtomValue(activeWorkspaceAtom); const settings = useAtomValue(settingsAtom); const checkForUpdates = useCheckForUpdates(); - if (settings == null || workspace == null) { + if (settings == null) { return null; } + const showWorkspaceSettingsMovedBanner = + settings.createdAt.slice(0, 10) < WORKSPACE_SETTINGS_MOVED_AT; + return (
General -

Configure general settings for update behavior and more.

+

+ Configure general settings for update behavior and more. +

@@ -76,7 +74,9 @@ export function SettingsGeneral() { description="Choose whether updates are installed automatically or manually." name="autoupdate" value={settings.autoupdate ? "auto" : "manual"} - onChange={(v) => patchModel(settings, { autoupdate: v === "auto" })} + onChange={(v) => + patchModel(settings, { autoupdate: v === "auto" }) + } options={[ { label: "Automatic", value: "auto" }, { label: "Manual", value: "manual" }, @@ -108,54 +108,19 @@ export function SettingsGeneral() {
- - Workspace{" "} - - {workspace.name} - - - } - > - Number.parseInt(value, 10) >= 0} - /> - - - - - - - - - + {showWorkspaceSettingsMovedBanner && ( + +

+ Workspace specific settings have moved to{" "} + Workspace Settings, accessible from the workspace switcher + menu. +

+
+ )} diff --git a/apps/yaak-client/components/WebsocketResponsePane.tsx b/apps/yaak-client/components/WebsocketResponsePane.tsx index e3284db2..42806e4e 100644 --- a/apps/yaak-client/components/WebsocketResponsePane.tsx +++ b/apps/yaak-client/components/WebsocketResponsePane.tsx @@ -105,10 +105,18 @@ function WebsocketEventRow({ : ""; const iconColor = - messageType === "close" || messageType === "open" ? "secondary" : isServer ? "info" : "primary"; + messageType === "error" + ? "warning" + : messageType === "close" || messageType === "open" + ? "secondary" + : isServer + ? "info" + : "primary"; const icon = - messageType === "close" || messageType === "open" + messageType === "error" + ? "alert_triangle" + : messageType === "close" || messageType === "open" ? "info" : isServer ? "arrow_big_down_dash" @@ -119,6 +127,8 @@ function WebsocketEventRow({ "Disconnected from server" ) : messageType === "open" ? ( "Connected to server" + ) : messageType === "error" ? ( + {message} ) : message === "" ? ( No content ) : ( @@ -170,7 +180,9 @@ function WebsocketEventDetail({ ? "Connection Closed" : event.messageType === "open" ? "Connection Open" - : `Message ${event.isServer ? "Received" : "Sent"}`; + : event.messageType === "error" + ? "WebSocket Error" + : `Message ${event.isServer ? "Received" : "Sent"}`; const actions: EventDetailAction[] = message !== "" diff --git a/apps/yaak-client/components/WorkspaceSettingsDialog.tsx b/apps/yaak-client/components/WorkspaceSettingsDialog.tsx index fff5ac95..f90351ba 100644 --- a/apps/yaak-client/components/WorkspaceSettingsDialog.tsx +++ b/apps/yaak-client/components/WorkspaceSettingsDialog.tsx @@ -112,7 +112,9 @@ export function WorkspaceSettingsDialog({ workspaceId, hide, tab }: Props) { onCreateNewWorkspace={hide} onChange={({ filePath }) => patchModel(workspaceMeta, { settingSyncDir: filePath })} /> - +
+ +
diff --git a/apps/yaak-client/components/core/PlainInput.tsx b/apps/yaak-client/components/core/PlainInput.tsx index 4361bba3..0aba672a 100644 --- a/apps/yaak-client/components/core/PlainInput.tsx +++ b/apps/yaak-client/components/core/PlainInput.tsx @@ -1,6 +1,6 @@ import { HStack } from "@yaakapp-internal/ui"; import classNames from "classnames"; -import type { FocusEvent, HTMLAttributes, ReactNode } from "react"; +import type { FocusEvent, InputHTMLAttributes, ReactNode } from "react"; import { forwardRef, useCallback, @@ -28,10 +28,9 @@ export type PlainInputProps = Omit< | "extraExtensions" | "forcedEnvironmentId" > & - Pick, "onKeyDownCapture"> & { - onFocusRaw?: HTMLAttributes["onFocus"]; + Pick, "inputMode" | "onKeyDownCapture" | "step"> & { + onFocusRaw?: InputHTMLAttributes["onFocus"]; type?: "text" | "password" | "number"; - step?: number; hideObscureToggle?: boolean; labelRightSlot?: ReactNode; }; @@ -52,6 +51,7 @@ export const PlainInput = forwardRef<{ focus: () => void }, PlainInputProps>(fun labelClassName, labelPosition = "top", labelRightSlot, + inputMode, leftSlot, name, onBlur, @@ -64,6 +64,7 @@ export const PlainInput = forwardRef<{ focus: () => void }, PlainInputProps>(fun required, rightSlot, size = "md", + step, tint, type = "text", validate, @@ -204,12 +205,14 @@ export const PlainInput = forwardRef<{ focus: () => void }, PlainInputProps>(fun autoComplete="off" autoCapitalize="off" autoCorrect="off" + inputMode={inputMode} onChange={(e) => handleChange(e.target.value)} onPaste={(e) => onPaste?.(e.clipboardData.getData("Text"))} className={classNames(commonClassName, "h-full disabled:opacity-disabled")} onFocus={handleFocus} onBlur={handleBlur} required={required} + step={step} placeholder={placeholder} onKeyDownCapture={onKeyDownCapture} /> diff --git a/apps/yaak-client/hooks/useAuthTab.tsx b/apps/yaak-client/hooks/useAuthTab.tsx index 91e7c99d..e094b6e5 100644 --- a/apps/yaak-client/hooks/useAuthTab.tsx +++ b/apps/yaak-client/hooks/useAuthTab.tsx @@ -5,6 +5,7 @@ import { useMemo } from "react"; import { openFolderSettings } from "../commands/openFolderSettings"; import { openWorkspaceSettings } from "../commands/openWorkspaceSettings"; import { IconTooltip } from "../components/core/IconTooltip"; +import type { RadioDropdownProps } from "../components/core/RadioDropdown"; import type { TabItem } from "../components/core/Tabs/Tabs"; import { capitalize } from "../lib/capitalize"; import { showConfirm } from "../lib/confirm"; @@ -14,156 +15,192 @@ import type { AuthenticatedModel } from "./useInheritedAuthentication"; import { useInheritedAuthentication } from "./useInheritedAuthentication"; import { useModelAncestors } from "./useModelAncestors"; -export function useAuthTab(tabValue: T, model: AuthenticatedModel | null) { +export function useAuthTab( + tabValue: T, + model: AuthenticatedModel | null, +) { + const options = useAuthDropdownOptions(model); + + return useMemo(() => { + if (model == null || options == null) return []; + + const tab: TabItem = { + value: tabValue, + label: "Auth", + options, + }; + + return [tab]; + }, [model, options, tabValue]); +} + +export function useAuthDropdownOptions( + model: AuthenticatedModel | null, +): Omit | null { const authentication = useHttpAuthenticationSummaries(); const inheritedAuth = useInheritedAuthentication(model); const ancestors = useModelAncestors(model); const parentModel = ancestors[0] ?? null; - return useMemo(() => { - if (model == null) return []; + return useMemo(() => { + if (model == null) return null; - const tab: TabItem = { - value: tabValue, - label: "Auth", - options: { - value: model.authenticationType, - items: [ - ...authentication.map((a) => ({ - label: a.label || "UNKNOWN", - shortLabel: a.shortLabel, - value: a.name, - })), - { type: "separator" }, - { - label: "Inherit from Parent", - shortLabel: - inheritedAuth != null && inheritedAuth.authenticationType !== "none" ? ( - - {authentication.find((a) => a.name === inheritedAuth.authenticationType) - ?.shortLabel ?? "UNKNOWN"} - - - ) : ( - "Auth" - ), - value: null, - }, - { label: "No Auth", shortLabel: "No Auth", value: "none" }, - ], - itemsAfter: (() => { - const actions: ( - | { type: "separator"; label: string } - | { label: string; leftSlot: React.ReactNode; onSelect: () => Promise } - )[] = []; - - // Promote: move auth from current model up to parent - if ( - parentModel && - model.authenticationType && - model.authenticationType !== "none" && - (parentModel.authenticationType == null || parentModel.authenticationType === "none") - ) { - actions.push( - { type: "separator", label: "Actions" }, - { - label: `Promote to ${capitalize(parentModel.model)}`, - leftSlot: ( - - ), - onSelect: async () => { - const confirmed = await showConfirm({ - id: "promote-auth-confirm", - title: "Promote Authentication", - confirmText: "Promote", - description: ( - <> - Move authentication config to{" "} - {resolvedModelName(parentModel)}? - - ), - }); - if (confirmed) { - await patchModel(model, { authentication: {}, authenticationType: null }); - await patchModel(parentModel, { - authentication: model.authentication, - authenticationType: model.authenticationType, - }); - - if (parentModel.model === "folder") { - openFolderSettings(parentModel.id, "auth"); - } else { - openWorkspaceSettings("auth"); - } - } - }, - }, - ); - } - - // Copy from ancestor: copy auth config down to current model - const ancestorWithAuth = ancestors.find( - (a) => a.authenticationType != null && a.authenticationType !== "none", - ); - if (ancestorWithAuth) { - if (actions.length === 0) { - actions.push({ type: "separator", label: "Actions" }); + return { + value: model.authenticationType, + items: [ + ...authentication.map((a) => ({ + label: a.label || "UNKNOWN", + shortLabel: a.shortLabel, + value: a.name, + })), + { type: "separator" }, + { + label: "Inherit from Parent", + shortLabel: + inheritedAuth != null && + inheritedAuth.authenticationType !== "none" ? ( + + {authentication.find( + (a) => a.name === inheritedAuth.authenticationType, + )?.shortLabel ?? "UNKNOWN"} + + + ) : ( + "Auth" + ), + value: null, + }, + { label: "No Auth", shortLabel: "No Auth", value: "none" }, + ], + itemsAfter: (() => { + const actions: ( + | { type: "separator"; label: string } + | { + label: string; + leftSlot: React.ReactNode; + onSelect: () => Promise; } - actions.push({ - label: `Copy from ${modelTypeLabel(ancestorWithAuth)}`, + )[] = []; + + // Promote: move auth from current model up to parent + if ( + parentModel && + model.authenticationType && + model.authenticationType !== "none" && + (parentModel.authenticationType == null || + parentModel.authenticationType === "none") + ) { + actions.push( + { type: "separator", label: "Actions" }, + { + label: `Promote to ${capitalize(parentModel.model)}`, leftSlot: ( ), onSelect: async () => { const confirmed = await showConfirm({ - id: "copy-auth-confirm", - title: "Copy Authentication", - confirmText: "Copy", + id: "promote-auth-confirm", + title: "Promote Authentication", + confirmText: "Promote", description: ( <> - Copy{" "} - {authentication.find((a) => a.name === ancestorWithAuth.authenticationType) - ?.label ?? "authentication"}{" "} - config from {resolvedModelName(ancestorWithAuth)}? - This will override the current authentication but will not affect the{" "} - {modelTypeLabel(ancestorWithAuth).toLowerCase()}. + Move authentication config to{" "} + {resolvedModelName(parentModel)}? ), }); if (confirmed) { await patchModel(model, { - authentication: { ...ancestorWithAuth.authentication }, - authenticationType: ancestorWithAuth.authenticationType, + authentication: {}, + authenticationType: null, }); + await patchModel(parentModel, { + authentication: model.authentication, + authenticationType: model.authenticationType, + }); + + if (parentModel.model === "folder") { + openFolderSettings(parentModel.id, "auth"); + } else { + openWorkspaceSettings("auth"); + } } }, - }); - } + }, + ); + } - return actions.length > 0 ? actions : undefined; - })(), - onChange: async (authenticationType) => { - let authentication: Folder["authentication"] = model.authentication; - if (model.authenticationType !== authenticationType) { - authentication = { - // Reset auth if changing types - }; + // Copy from ancestor: copy auth config down to current model + const ancestorWithAuth = ancestors.find( + (a) => + a.authenticationType != null && a.authenticationType !== "none", + ); + if (ancestorWithAuth) { + if (actions.length === 0) { + actions.push({ type: "separator", label: "Actions" }); } - await patchModel(model, { authentication, authenticationType }); - }, + actions.push({ + label: `Copy from ${modelTypeLabel(ancestorWithAuth)}`, + leftSlot: ( + + ), + onSelect: async () => { + const confirmed = await showConfirm({ + id: "copy-auth-confirm", + title: "Copy Authentication", + confirmText: "Copy", + description: ( + <> + Copy{" "} + {authentication.find( + (a) => a.name === ancestorWithAuth.authenticationType, + )?.label ?? "authentication"}{" "} + config from{" "} + + {resolvedModelName(ancestorWithAuth)} + + ? This will override the current authentication but will not + affect the {modelTypeLabel(ancestorWithAuth).toLowerCase()}. + + ), + }); + if (confirmed) { + await patchModel(model, { + authentication: { ...ancestorWithAuth.authentication }, + authenticationType: ancestorWithAuth.authenticationType, + }); + } + }, + }); + } + + return actions.length > 0 ? actions : undefined; + })(), + onChange: async (authenticationType) => { + let authentication: Folder["authentication"] = model.authentication; + if (model.authenticationType !== authenticationType) { + authentication = { + // Reset auth if changing types + }; + } + await patchModel(model, { authentication, authenticationType }); }, }; - - return [tab]; - }, [authentication, inheritedAuth, model, parentModel, tabValue, ancestors]); + }, [authentication, inheritedAuth, model, parentModel, ancestors]); } diff --git a/apps/yaak-client/lib/requestSettings.ts b/apps/yaak-client/lib/requestSettings.ts index f33b3877..0fd8fa94 100644 --- a/apps/yaak-client/lib/requestSettings.ts +++ b/apps/yaak-client/lib/requestSettings.ts @@ -5,6 +5,7 @@ type ModelType = AnyModel["model"]; type WorkspaceRequestSettings = Pick< Workspace, | "settingFollowRedirects" + | "settingRequestMessageSize" | "settingRequestTimeout" | "settingSendCookies" | "settingStoreCookies" @@ -17,7 +18,9 @@ type ModelTypeWithSetting = { [M in ModelType]: K extends keyof ModelForType ? M : never; }[ModelType]; -export type RequestSettingDefinition = { +export type RequestSettingDefinition< + K extends RequestSettingKey = RequestSettingKey, +> = { defaultValue: WorkspaceRequestSettings[K]; description: string; modelKey: K; @@ -41,11 +44,26 @@ export const SETTING_REQUEST_TIMEOUT = defineRequestSetting({ title: "Request Timeout", }); +export const SETTING_REQUEST_MESSAGE_SIZE = defineRequestSetting({ + defaultValue: 64 * 1024 * 1024, + description: + "Maximum gRPC or WebSocket message size in MB. Set to 0 to disable.", + modelKey: "settingRequestMessageSize", + models: ["workspace", "folder", "websocket_request", "grpc_request"], + title: "Message Size Limit", +}); + export const SETTING_VALIDATE_CERTIFICATES = defineRequestSetting({ defaultValue: true, description: "When disabled, skip validation of server certificates.", modelKey: "settingValidateCertificates", - models: ["workspace", "folder", "http_request", "websocket_request", "grpc_request"], + models: [ + "workspace", + "folder", + "http_request", + "websocket_request", + "grpc_request", + ], title: "Validate TLS certificates", }); @@ -59,7 +77,8 @@ export const SETTING_FOLLOW_REDIRECTS = defineRequestSetting({ export const SETTING_SEND_COOKIES = defineRequestSetting({ defaultValue: true, - description: "Attach matching cookies from the active cookie jar to outgoing requests.", + description: + "Attach matching cookies from the active cookie jar to outgoing requests.", modelKey: "settingSendCookies", models: ["workspace", "folder", "http_request", "websocket_request"], title: "Automatically send cookies", @@ -67,7 +86,8 @@ export const SETTING_SEND_COOKIES = defineRequestSetting({ export const SETTING_STORE_COOKIES = defineRequestSetting({ defaultValue: true, - description: "Save cookies from Set-Cookie response headers to the active cookie jar.", + description: + "Save cookies from Set-Cookie response headers to the active cookie jar.", modelKey: "settingStoreCookies", models: ["workspace", "folder", "http_request", "websocket_request"], title: "Automatically store cookies", diff --git a/apps/yaak-client/package.json b/apps/yaak-client/package.json index 05bdbe51..144fbfd9 100644 --- a/apps/yaak-client/package.json +++ b/apps/yaak-client/package.json @@ -102,11 +102,11 @@ "postcss-nesting": "^13.0.2", "rollup": "^4.60.3", "tailwindcss": "^3.4.17", - "vite": "npm:@voidzero-dev/vite-plus-core@^0.1.20", + "vite": "npm:@voidzero-dev/vite-plus-core@^0.2.1", "vite-plugin-static-copy": "^3.3.0", "vite-plugin-svgr": "^4.5.0", "vite-plugin-top-level-await": "^1.5.0", "vite-plugin-wasm": "^3.5.0", - "vite-plus": "^0.1.24" + "vite-plus": "^0.2.1" } } diff --git a/apps/yaak-client/vite.config.ts b/apps/yaak-client/vite.config.ts index 00756360..30f03e4a 100644 --- a/apps/yaak-client/vite.config.ts +++ b/apps/yaak-client/vite.config.ts @@ -39,6 +39,7 @@ export default defineConfig(async () => { }), ], build: { + target: "esnext", sourcemap: true, outDir: "../../dist/apps/yaak-client", emptyOutDir: true, diff --git a/apps/yaak-proxy/package.json b/apps/yaak-proxy/package.json index b9a153ae..a6d12c51 100644 --- a/apps/yaak-proxy/package.json +++ b/apps/yaak-proxy/package.json @@ -31,7 +31,7 @@ "@vitejs/plugin-react": "^6.0.1", "babel-plugin-react-compiler": "^1.0.0", "typescript": "^5.8.3", - "vite": "npm:@voidzero-dev/vite-plus-core@^0.1.20", - "vite-plus": "^0.1.24" + "vite": "npm:@voidzero-dev/vite-plus-core@^0.2.1", + "vite-plus": "^0.2.1" } } diff --git a/crates-tauri/yaak-app-client/src/lib.rs b/crates-tauri/yaak-app-client/src/lib.rs index bf6d02ce..420c60f5 100644 --- a/crates-tauri/yaak-app-client/src/lib.rs +++ b/crates-tauri/yaak-app-client/src/lib.rs @@ -295,7 +295,8 @@ async fn cmd_grpc_reflect( unrendered_request.folder_id.as_deref(), environment_id, )?; - let resolved_settings = app_handle.db().resolve_settings_for_grpc_request(&unrendered_request)?; + let resolved_settings = + app_handle.db().resolve_settings_for_grpc_request(&unrendered_request)?; let plugin_manager = Arc::new((*app_handle.state::()).clone()); let encryption_manager = Arc::new((*app_handle.state::()).clone()); @@ -332,6 +333,7 @@ async fn cmd_grpc_reflect( &metadata, resolved_settings.validate_certificates.value, client_certificate, + resolved_settings.request_message_size.value, ) .await .map_err(|e| GenericError(e.to_string()))?) @@ -353,7 +355,8 @@ async fn cmd_grpc_go( unrendered_request.folder_id.as_deref(), environment_id, )?; - let resolved_settings = app_handle.db().resolve_settings_for_grpc_request(&unrendered_request)?; + let resolved_settings = + app_handle.db().resolve_settings_for_grpc_request(&unrendered_request)?; let plugin_manager = Arc::new((*app_handle.state::()).clone()); let encryption_manager = Arc::new((*app_handle.state::()).clone()); @@ -425,6 +428,7 @@ async fn cmd_grpc_go( &metadata, resolved_settings.validate_certificates.value, client_cert.clone(), + resolved_settings.request_message_size.value, ) .await; @@ -714,7 +718,7 @@ async fn cmd_grpc_go( Some(s) => GrpcEvent { error: Some(s.message().to_string()), status: Some(s.code() as i32), - content: "Failed to connect".to_string(), + content: "Request failed".to_string(), metadata: metadata_to_map(s.metadata().clone()), event_type: GrpcEventType::ConnectionEnd, ..base_event.clone() @@ -722,7 +726,7 @@ async fn cmd_grpc_go( None => GrpcEvent { error: Some(e.message), status: Some(Code::Unknown as i32), - content: "Failed to connect".to_string(), + content: "Request failed".to_string(), event_type: GrpcEventType::ConnectionEnd, ..base_event.clone() }, @@ -738,7 +742,7 @@ async fn cmd_grpc_go( &GrpcEvent { error: Some(e.to_string()), status: Some(Code::Unknown as i32), - content: "Failed to connect".to_string(), + content: "Request failed".to_string(), event_type: GrpcEventType::ConnectionEnd, ..base_event.clone() }, @@ -781,7 +785,7 @@ async fn cmd_grpc_go( Some(s) => GrpcEvent { error: Some(s.message().to_string()), status: Some(s.code() as i32), - content: "Failed to connect".to_string(), + content: "Stream failed".to_string(), metadata: metadata_to_map(s.metadata().clone()), event_type: GrpcEventType::ConnectionEnd, ..base_event.clone() @@ -789,7 +793,7 @@ async fn cmd_grpc_go( None => GrpcEvent { error: Some(e.message), status: Some(Code::Unknown as i32), - content: "Failed to connect".to_string(), + content: "Stream failed".to_string(), event_type: GrpcEventType::ConnectionEnd, ..base_event.clone() }, @@ -806,7 +810,7 @@ async fn cmd_grpc_go( &GrpcEvent { error: Some(e.to_string()), status: Some(Code::Unknown as i32), - content: "Failed to connect".to_string(), + content: "Stream failed".to_string(), event_type: GrpcEventType::ConnectionEnd, ..base_event.clone() }, @@ -878,7 +882,8 @@ async fn cmd_grpc_go( .db() .upsert_grpc_event( &GrpcEvent { - content: status.to_string(), + content: "Stream failed".to_string(), + error: Some(status.message().to_string()), status: Some(status.code() as i32), metadata: metadata_to_map(status.metadata().clone()), event_type: GrpcEventType::ConnectionEnd, @@ -887,6 +892,7 @@ async fn cmd_grpc_go( &UpdateSource::from_window_label(window.label()), ) .unwrap(); + break; } } } diff --git a/crates-tauri/yaak-app-client/src/ws_ext.rs b/crates-tauri/yaak-app-client/src/ws_ext.rs index e627f867..088c7ea1 100644 --- a/crates-tauri/yaak-app-client/src/ws_ext.rs +++ b/crates-tauri/yaak-app-client/src/ws_ext.rs @@ -50,6 +50,37 @@ pub async fn cmd_ws_send( ws_manager: State<'_, Mutex>, ) -> Result { let connection = app_handle.db().get_websocket_connection(connection_id)?; + + match send_websocket_message(&connection, environment_id, &app_handle, &window, &ws_manager) + .await + { + Ok(connection) => Ok(connection), + Err(e) => { + app_handle.db().upsert_websocket_event( + &WebsocketEvent { + connection_id: connection.id.clone(), + request_id: connection.request_id.clone(), + workspace_id: connection.workspace_id.clone(), + is_server: false, + message_type: WebsocketEventType::Error, + message: e.to_string().into(), + ..Default::default() + }, + &UpdateSource::from_window_label(window.label()), + )?; + + Ok(connection) + } + } +} + +async fn send_websocket_message( + connection: &WebsocketConnection, + environment_id: Option<&str>, + app_handle: &AppHandle, + window: &WebviewWindow, + ws_manager: &Mutex, +) -> Result { let unrendered_request = app_handle.db().get_websocket_request(&connection.request_id)?; let environment_chain = app_handle.db().resolve_environments( &unrendered_request.workspace_id, @@ -91,7 +122,7 @@ pub async fn cmd_ws_send( &UpdateSource::from_window_label(window.label()), )?; - Ok(connection) + Ok(connection.clone()) } #[command] @@ -299,6 +330,7 @@ pub async fn cmd_ws_connect( receive_tx, resolved_settings.validate_certificates.value, client_cert, + resolved_settings.request_message_size.value, ) .await { diff --git a/crates/yaak-git/bindings/gen_models.ts b/crates/yaak-git/bindings/gen_models.ts index f6bff97c..fa90a9b6 100644 --- a/crates/yaak-git/bindings/gen_models.ts +++ b/crates/yaak-git/bindings/gen_models.ts @@ -46,6 +46,7 @@ export type Folder = { settingValidateCertificates: InheritedBoolSetting; settingFollowRedirects: InheritedBoolSetting; settingRequestTimeout: InheritedIntSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type GrpcRequest = { @@ -69,6 +70,7 @@ export type GrpcRequest = { */ url: string; settingValidateCertificates: InheritedBoolSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type HttpRequest = { @@ -146,6 +148,7 @@ export type WebsocketRequest = { settingSendCookies: InheritedBoolSetting; settingStoreCookies: InheritedBoolSetting; settingValidateCertificates: InheritedBoolSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type Workspace = { @@ -162,6 +165,7 @@ export type Workspace = { settingValidateCertificates: boolean; settingFollowRedirects: boolean; settingRequestTimeout: number; + settingRequestMessageSize: number; settingDnsOverrides: Array; settingSendCookies: boolean; settingStoreCookies: boolean; diff --git a/crates/yaak-grpc/src/client.rs b/crates/yaak-grpc/src/client.rs index 06b201d1..45dedd19 100644 --- a/crates/yaak-grpc/src/client.rs +++ b/crates/yaak-grpc/src/client.rs @@ -33,15 +33,21 @@ impl AutoReflectionClient { uri: &Uri, validate_certificates: bool, client_cert: Option, + max_message_size: usize, ) -> Result { let client_v1 = v1::server_reflection_client::ServerReflectionClient::with_origin( get_transport(validate_certificates, client_cert.clone())?, uri.clone(), - ); - let client_v1alpha = v1alpha::server_reflection_client::ServerReflectionClient::with_origin( - get_transport(validate_certificates, client_cert.clone())?, - uri.clone(), - ); + ) + .max_decoding_message_size(max_message_size) + .max_encoding_message_size(max_message_size); + let client_v1alpha = + v1alpha::server_reflection_client::ServerReflectionClient::with_origin( + get_transport(validate_certificates, client_cert.clone())?, + uri.clone(), + ) + .max_decoding_message_size(max_message_size) + .max_encoding_message_size(max_message_size); Ok(AutoReflectionClient { use_v1alpha: false, client_v1, client_v1alpha }) } diff --git a/crates/yaak-grpc/src/manager.rs b/crates/yaak-grpc/src/manager.rs index 38720e36..40556bee 100644 --- a/crates/yaak-grpc/src/manager.rs +++ b/crates/yaak-grpc/src/manager.rs @@ -33,16 +33,13 @@ use tonic::transport::Uri; use tonic::{IntoRequest, IntoStreamingRequest, Request, Response, Status, Streaming}; use yaak_tls::ClientCertificateConfig; -/// Maximum size for a single gRPC message (64 MB). -/// Tonic defaults to 4 MB, which is too small for large responses. -const GRPC_MAX_MESSAGE_SIZE: usize = 64 * 1024 * 1024; - #[derive(Clone)] pub struct GrpcConnection { pool: Arc>, conn: Client, BoxBody>, pub uri: Uri, use_reflection: bool, + max_message_size: usize, } #[derive(Default, Debug)] @@ -101,8 +98,15 @@ impl GrpcConnection { client_cert: Option, ) -> Result> { if self.use_reflection { - reflect_types_for_message(self.pool.clone(), &self.uri, message, metadata, client_cert) - .await?; + reflect_types_for_message( + self.pool.clone(), + &self.uri, + message, + metadata, + client_cert, + self.max_message_size, + ) + .await?; } let method = &self.method(&service, &method).await?; let input_message = method.input(); @@ -111,8 +115,7 @@ impl GrpcConnection { let req_message = DynamicMessage::deserialize(input_message, &mut deserializer)?; deserializer.end()?; - let mut client = tonic::client::Grpc::with_origin(self.conn.clone(), self.uri.clone()) - .max_decoding_message_size(GRPC_MAX_MESSAGE_SIZE); + let mut client = grpc_client(self.conn.clone(), self.uri.clone(), self.max_message_size); let mut req = req_message.into_request(); decorate_req(metadata, &mut req)?; @@ -137,6 +140,7 @@ impl GrpcConnection { message, metadata, client_cert, + self.max_message_size, ) .await?; @@ -176,6 +180,7 @@ impl GrpcConnection { let md = metadata.clone(); let use_reflection = self.use_reflection.clone(); let client_cert = client_cert.clone(); + let max_message_size = self.max_message_size; stream .then(move |json| { let pool = pool.clone(); @@ -188,8 +193,15 @@ impl GrpcConnection { let json_clone = json.clone(); async move { if use_reflection { - if let Err(e) = - reflect_types_for_message(pool, &uri, &json, &md, client_cert).await + if let Err(e) = reflect_types_for_message( + pool, + &uri, + &json, + &md, + client_cert, + max_message_size, + ) + .await { warn!("Failed to resolve Any types: {e}"); } @@ -211,8 +223,7 @@ impl GrpcConnection { .filter_map(|x| x) }; - let mut client = tonic::client::Grpc::with_origin(self.conn.clone(), self.uri.clone()) - .max_decoding_message_size(GRPC_MAX_MESSAGE_SIZE); + let mut client = grpc_client(self.conn.clone(), self.uri.clone(), self.max_message_size); let path = method_desc_to_path(method); let codec = DynamicCodec::new(method.clone()); @@ -243,6 +254,7 @@ impl GrpcConnection { let md = metadata.clone(); let use_reflection = self.use_reflection.clone(); let client_cert = client_cert.clone(); + let max_message_size = self.max_message_size; stream .then(move |json| { let pool = pool.clone(); @@ -255,8 +267,15 @@ impl GrpcConnection { let json_clone = json.clone(); async move { if use_reflection { - if let Err(e) = - reflect_types_for_message(pool, &uri, &json, &md, client_cert).await + if let Err(e) = reflect_types_for_message( + pool, + &uri, + &json, + &md, + client_cert, + max_message_size, + ) + .await { warn!("Failed to resolve Any types: {e}"); } @@ -278,8 +297,7 @@ impl GrpcConnection { .filter_map(|x| x) }; - let mut client = tonic::client::Grpc::with_origin(self.conn.clone(), self.uri.clone()) - .max_decoding_message_size(GRPC_MAX_MESSAGE_SIZE); + let mut client = grpc_client(self.conn.clone(), self.uri.clone(), self.max_message_size); let path = method_desc_to_path(method); let codec = DynamicCodec::new(method.clone()); @@ -307,8 +325,7 @@ impl GrpcConnection { let req_message = DynamicMessage::deserialize(input_message, &mut deserializer)?; deserializer.end()?; - let mut client = tonic::client::Grpc::with_origin(self.conn.clone(), self.uri.clone()) - .max_decoding_message_size(GRPC_MAX_MESSAGE_SIZE); + let mut client = grpc_client(self.conn.clone(), self.uri.clone(), self.max_message_size); let mut req = req_message.into_request(); decorate_req(metadata, &mut req)?; @@ -320,6 +337,23 @@ impl GrpcConnection { } } +fn grpc_client( + conn: Client, BoxBody>, + uri: Uri, + max_message_size: usize, +) -> tonic::client::Grpc, BoxBody>> { + tonic::client::Grpc::with_origin(conn, uri) + .max_decoding_message_size(max_message_size) + .max_encoding_message_size(max_message_size) +} + +fn message_size_limit(setting: i32) -> usize { + match setting.try_into() { + Ok(0) | Err(_) => usize::MAX, + Ok(limit) => limit, + } +} + /// Configuration for GrpcHandle to compile proto files #[derive(Clone)] pub struct GrpcConfig { @@ -356,6 +390,7 @@ impl GrpcHandle { metadata: &BTreeMap, validate_certificates: bool, client_cert: Option, + request_message_size: i32, ) -> Result { let server_reflection = proto_files.is_empty(); let key = make_pool_key(id, uri, proto_files); @@ -367,7 +402,14 @@ impl GrpcHandle { let pool = if server_reflection { let full_uri = uri_from_str(uri)?; - fill_pool_from_reflection(&full_uri, metadata, validate_certificates, client_cert).await + fill_pool_from_reflection( + &full_uri, + metadata, + validate_certificates, + client_cert, + message_size_limit(request_message_size), + ) + .await } else { fill_pool_from_files(&self.config, proto_files).await }?; @@ -384,12 +426,21 @@ impl GrpcHandle { metadata: &BTreeMap, validate_certificates: bool, client_cert: Option, + request_message_size: i32, ) -> Result> { // Ensure we have a pool; reflect only if missing if self.get_pool(id, uri, proto_files).is_none() { info!("Reflecting gRPC services for {} at {}", id, uri); - self.reflect(id, uri, proto_files, metadata, validate_certificates, client_cert) - .await?; + self.reflect( + id, + uri, + proto_files, + metadata, + validate_certificates, + client_cert, + request_message_size, + ) + .await?; } let pool = self @@ -429,8 +480,10 @@ impl GrpcHandle { metadata: &BTreeMap, validate_certificates: bool, client_cert: Option, + request_message_size: i32, ) -> Result { let use_reflection = proto_files.is_empty(); + let max_message_size = message_size_limit(request_message_size); if self.get_pool(id, uri, proto_files).is_none() { self.reflect( id, @@ -439,6 +492,7 @@ impl GrpcHandle { metadata, validate_certificates, client_cert.clone(), + request_message_size, ) .await?; } @@ -448,7 +502,13 @@ impl GrpcHandle { .clone(); let uri = uri_from_str(uri)?; let conn = get_transport(validate_certificates, client_cert.clone())?; - Ok(GrpcConnection { pool: Arc::new(RwLock::new(pool)), use_reflection, conn, uri }) + Ok(GrpcConnection { + pool: Arc::new(RwLock::new(pool)), + use_reflection, + conn, + uri, + max_message_size, + }) } fn get_pool(&self, id: &str, uri: &str, proto_files: &Vec) -> Option<&DescriptorPool> { diff --git a/crates/yaak-grpc/src/reflection.rs b/crates/yaak-grpc/src/reflection.rs index 7ea95266..e154d4b8 100644 --- a/crates/yaak-grpc/src/reflection.rs +++ b/crates/yaak-grpc/src/reflection.rs @@ -119,9 +119,11 @@ pub async fn fill_pool_from_reflection( metadata: &BTreeMap, validate_certificates: bool, client_cert: Option, + max_message_size: usize, ) -> Result { let mut pool = DescriptorPool::new(); - let mut client = AutoReflectionClient::new(uri, validate_certificates, client_cert)?; + let mut client = + AutoReflectionClient::new(uri, validate_certificates, client_cert, max_message_size)?; for service in list_services(&mut client, metadata).await? { if service == "grpc.reflection.v1alpha.ServerReflection" { @@ -192,6 +194,7 @@ pub(crate) async fn reflect_types_for_message( json: &str, metadata: &BTreeMap, client_cert: Option, + max_message_size: usize, ) -> Result<()> { // 1. Collect all Any types in the JSON let mut extra_types = Vec::new(); @@ -201,7 +204,7 @@ pub(crate) async fn reflect_types_for_message( return Ok(()); // nothing to do } - let mut client = AutoReflectionClient::new(uri, false, client_cert)?; + let mut client = AutoReflectionClient::new(uri, false, client_cert, max_message_size)?; for extra_type in extra_types { { let guard = pool.read().await; @@ -239,6 +242,7 @@ pub(crate) async fn reflect_types_for_dynamic_message( message: &DynamicMessage, metadata: &BTreeMap, client_cert: Option, + max_message_size: usize, ) -> Result<()> { let mut extra_types = HashSet::new(); collect_any_types_from_dynamic_message(message, &mut extra_types); @@ -247,7 +251,7 @@ pub(crate) async fn reflect_types_for_dynamic_message( return Ok(()); } - let mut client = AutoReflectionClient::new(uri, false, client_cert)?; + let mut client = AutoReflectionClient::new(uri, false, client_cert, max_message_size)?; for extra_type in extra_types { { let guard = pool.read().await; diff --git a/crates/yaak-models/bindings/gen_models.ts b/crates/yaak-models/bindings/gen_models.ts index ec7a4e4b..287e9b4e 100644 --- a/crates/yaak-models/bindings/gen_models.ts +++ b/crates/yaak-models/bindings/gen_models.ts @@ -109,6 +109,7 @@ export type Folder = { settingValidateCertificates: InheritedBoolSetting; settingFollowRedirects: InheritedBoolSetting; settingRequestTimeout: InheritedIntSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type GraphQlIntrospection = { @@ -184,6 +185,7 @@ export type GrpcRequest = { */ url: string; settingValidateCertificates: InheritedBoolSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type HttpRequest = { @@ -456,7 +458,8 @@ export type WebsocketEvent = { messageType: WebsocketEventType; }; -export type WebsocketEventType = "binary" | "close" | "frame" | "open" | "ping" | "pong" | "text"; +export type WebsocketEventType = + "binary" | "close" | "error" | "frame" | "open" | "ping" | "pong" | "text"; export type WebsocketMessageType = "text" | "binary"; @@ -482,6 +485,7 @@ export type WebsocketRequest = { settingSendCookies: InheritedBoolSetting; settingStoreCookies: InheritedBoolSetting; settingValidateCertificates: InheritedBoolSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type Workspace = { @@ -498,6 +502,7 @@ export type Workspace = { settingValidateCertificates: boolean; settingFollowRedirects: boolean; settingRequestTimeout: number; + settingRequestMessageSize: number; settingDnsOverrides: Array; settingSendCookies: boolean; settingStoreCookies: boolean; diff --git a/crates/yaak-models/guest-js/store.ts b/crates/yaak-models/guest-js/store.ts index 1b704799..c402b531 100644 --- a/crates/yaak-models/guest-js/store.ts +++ b/crates/yaak-models/guest-js/store.ts @@ -54,7 +54,7 @@ function trackModelWrite(write: Promise): Promise { } export async function flushAllModelWrites(): Promise { - const results = await Promise.allSettled([...pendingModelWrites]); + const results = await Promise.allSettled(pendingModelWrites); const rejected = results.find((result) => result.status === "rejected"); if (rejected?.status === "rejected") { throw rejected.reason; diff --git a/crates/yaak-models/migrations/20260629000000_request-message-size.sql b/crates/yaak-models/migrations/20260629000000_request-message-size.sql new file mode 100644 index 00000000..a6dd95ab --- /dev/null +++ b/crates/yaak-models/migrations/20260629000000_request-message-size.sql @@ -0,0 +1,7 @@ +ALTER TABLE workspaces ADD COLUMN setting_request_message_size INTEGER DEFAULT 67108864 NOT NULL; + +ALTER TABLE folders ADD COLUMN setting_request_message_size TEXT DEFAULT '{"enabled":false,"value":67108864}' NOT NULL; + +ALTER TABLE websocket_requests ADD COLUMN setting_request_message_size TEXT DEFAULT '{"enabled":false,"value":67108864}' NOT NULL; + +ALTER TABLE grpc_requests ADD COLUMN setting_request_message_size TEXT DEFAULT '{"enabled":false,"value":67108864}' NOT NULL; diff --git a/crates/yaak-models/src/models.rs b/crates/yaak-models/src/models.rs index ac4fa367..4dd59248 100644 --- a/crates/yaak-models/src/models.rs +++ b/crates/yaak-models/src/models.rs @@ -21,6 +21,8 @@ use ts_rs::TS; use yaak_database::{Result as DbResult, UpdateSource}; pub use yaak_database::{UpsertModelInfo, upsert_date}; +pub const DEFAULT_REQUEST_MESSAGE_SIZE: i32 = 64 * 1024 * 1024; + #[macro_export] macro_rules! impl_model { ($t:ty, $variant:ident) => { @@ -120,6 +122,7 @@ pub struct ResolvedHttpRequestSettings { pub validate_certificates: ResolvedSetting, pub follow_redirects: ResolvedSetting, pub request_timeout: ResolvedSetting, + pub request_message_size: ResolvedSetting, pub send_cookies: ResolvedSetting, pub store_cookies: ResolvedSetting, } @@ -130,6 +133,7 @@ impl Default for ResolvedHttpRequestSettings { validate_certificates: ResolvedSetting::default_source(true), follow_redirects: ResolvedSetting::default_source(true), request_timeout: ResolvedSetting::default_source(0), + request_message_size: ResolvedSetting::default_source(DEFAULT_REQUEST_MESSAGE_SIZE), send_cookies: ResolvedSetting::default_source(true), store_cookies: ResolvedSetting::default_source(true), } @@ -400,6 +404,8 @@ pub struct Workspace { #[serde(default = "default_true")] pub setting_follow_redirects: bool, pub setting_request_timeout: i32, + #[serde(default = "default_request_message_size")] + pub setting_request_message_size: i32, #[serde(default)] pub setting_dns_overrides: Vec, #[serde(default = "default_true")] @@ -445,6 +451,7 @@ impl UpsertModelInfo for Workspace { (EncryptionKeyChallenge, self.encryption_key_challenge.into()), (SettingFollowRedirects, self.setting_follow_redirects.into()), (SettingRequestTimeout, self.setting_request_timeout.into()), + (SettingRequestMessageSize, self.setting_request_message_size.into()), (SettingValidateCertificates, self.setting_validate_certificates.into()), (SettingDnsOverrides, serde_json::to_string(&self.setting_dns_overrides)?.into()), (SettingSendCookies, self.setting_send_cookies.into()), @@ -463,7 +470,7 @@ impl UpsertModelInfo for Workspace { WorkspaceIden::EncryptionKeyChallenge, WorkspaceIden::SettingRequestTimeout, WorkspaceIden::SettingFollowRedirects, - WorkspaceIden::SettingRequestTimeout, + WorkspaceIden::SettingRequestMessageSize, WorkspaceIden::SettingValidateCertificates, WorkspaceIden::SettingDnsOverrides, WorkspaceIden::SettingSendCookies, @@ -491,6 +498,7 @@ impl UpsertModelInfo for Workspace { authentication_type: row.get("authentication_type")?, setting_follow_redirects: row.get("setting_follow_redirects")?, setting_request_timeout: row.get("setting_request_timeout")?, + setting_request_message_size: row.get("setting_request_message_size")?, setting_validate_certificates: row.get("setting_validate_certificates")?, setting_dns_overrides: serde_json::from_str(&setting_dns_overrides).unwrap_or_default(), setting_send_cookies: row.get("setting_send_cookies")?, @@ -962,6 +970,8 @@ pub struct Folder { pub setting_validate_certificates: InheritedBoolSetting, pub setting_follow_redirects: InheritedBoolSetting, pub setting_request_timeout: InheritedIntSetting, + #[serde(default = "default_request_message_size_setting")] + pub setting_request_message_size: InheritedIntSetting, } impl UpsertModelInfo for Folder { @@ -1009,6 +1019,10 @@ impl UpsertModelInfo for Folder { ), (SettingFollowRedirects, serde_json::to_string(&self.setting_follow_redirects)?.into()), (SettingRequestTimeout, serde_json::to_string(&self.setting_request_timeout)?.into()), + ( + SettingRequestMessageSize, + serde_json::to_string(&self.setting_request_message_size)?.into(), + ), ]) } @@ -1027,6 +1041,7 @@ impl UpsertModelInfo for Folder { FolderIden::SettingValidateCertificates, FolderIden::SettingFollowRedirects, FolderIden::SettingRequestTimeout, + FolderIden::SettingRequestMessageSize, ] } @@ -1041,6 +1056,7 @@ impl UpsertModelInfo for Folder { let setting_validate_certificates: String = row.get("setting_validate_certificates")?; let setting_follow_redirects: String = row.get("setting_follow_redirects")?; let setting_request_timeout: String = row.get("setting_request_timeout")?; + let setting_request_message_size: String = row.get("setting_request_message_size")?; Ok(Self { id: row.get("id")?, model: row.get("model")?, @@ -1062,6 +1078,8 @@ impl UpsertModelInfo for Folder { .unwrap_or_default(), setting_request_timeout: serde_json::from_str(&setting_request_timeout) .unwrap_or_default(), + setting_request_message_size: serde_json::from_str(&setting_request_message_size) + .unwrap_or_else(|_| default_request_message_size_setting()), }) } } @@ -1398,6 +1416,8 @@ pub struct WebsocketRequest { pub setting_send_cookies: InheritedBoolSetting, pub setting_store_cookies: InheritedBoolSetting, pub setting_validate_certificates: InheritedBoolSetting, + #[serde(default = "default_request_message_size_setting")] + pub setting_request_message_size: InheritedIntSetting, } impl UpsertModelInfo for WebsocketRequest { @@ -1446,6 +1466,10 @@ impl UpsertModelInfo for WebsocketRequest { SettingValidateCertificates, serde_json::to_string(&self.setting_validate_certificates)?.into(), ), + ( + SettingRequestMessageSize, + serde_json::to_string(&self.setting_request_message_size)?.into(), + ), ]) } @@ -1466,6 +1490,7 @@ impl UpsertModelInfo for WebsocketRequest { WebsocketRequestIden::SettingSendCookies, WebsocketRequestIden::SettingStoreCookies, WebsocketRequestIden::SettingValidateCertificates, + WebsocketRequestIden::SettingRequestMessageSize, ] } @@ -1479,6 +1504,7 @@ impl UpsertModelInfo for WebsocketRequest { let setting_send_cookies: String = row.get("setting_send_cookies")?; let setting_store_cookies: String = row.get("setting_store_cookies")?; let setting_validate_certificates: String = row.get("setting_validate_certificates")?; + let setting_request_message_size: String = row.get("setting_request_message_size")?; Ok(Self { id: row.get("id")?, model: row.get("model")?, @@ -1499,6 +1525,8 @@ impl UpsertModelInfo for WebsocketRequest { setting_store_cookies: serde_json::from_str(&setting_store_cookies).unwrap_or_default(), setting_validate_certificates: serde_json::from_str(&setting_validate_certificates) .unwrap_or_default(), + setting_request_message_size: serde_json::from_str(&setting_request_message_size) + .unwrap_or_else(|_| default_request_message_size_setting()), }) } } @@ -1509,6 +1537,7 @@ impl UpsertModelInfo for WebsocketRequest { pub enum WebsocketEventType { Binary, Close, + Error, Frame, Open, Ping, @@ -2039,6 +2068,8 @@ pub struct GrpcRequest { /// Server URL (http for plaintext or https for secure) pub url: String, pub setting_validate_certificates: InheritedBoolSetting, + #[serde(default = "default_request_message_size_setting")] + pub setting_request_message_size: InheritedIntSetting, } impl UpsertModelInfo for GrpcRequest { @@ -2086,6 +2117,10 @@ impl UpsertModelInfo for GrpcRequest { SettingValidateCertificates, serde_json::to_string(&self.setting_validate_certificates)?.into(), ), + ( + SettingRequestMessageSize, + serde_json::to_string(&self.setting_request_message_size)?.into(), + ), ]) } @@ -2105,6 +2140,7 @@ impl UpsertModelInfo for GrpcRequest { GrpcRequestIden::Authentication, GrpcRequestIden::Metadata, GrpcRequestIden::SettingValidateCertificates, + GrpcRequestIden::SettingRequestMessageSize, ] } @@ -2115,6 +2151,7 @@ impl UpsertModelInfo for GrpcRequest { let authentication: String = row.get("authentication")?; let metadata: String = row.get("metadata")?; let setting_validate_certificates: String = row.get("setting_validate_certificates")?; + let setting_request_message_size: String = row.get("setting_request_message_size")?; Ok(Self { id: row.get("id")?, model: row.get("model")?, @@ -2134,6 +2171,8 @@ impl UpsertModelInfo for GrpcRequest { metadata: serde_json::from_str(metadata.as_str()).unwrap_or_default(), setting_validate_certificates: serde_json::from_str(&setting_validate_certificates) .unwrap_or_default(), + setting_request_message_size: serde_json::from_str(&setting_request_message_size) + .unwrap_or_else(|_| default_request_message_size_setting()), }) } } @@ -2684,6 +2723,14 @@ fn default_true() -> bool { true } +fn default_request_message_size() -> i32 { + DEFAULT_REQUEST_MESSAGE_SIZE +} + +fn default_request_message_size_setting() -> InheritedIntSetting { + InheritedIntSetting { enabled: false, value: DEFAULT_REQUEST_MESSAGE_SIZE } +} + fn default_http_method() -> String { "GET".to_string() } diff --git a/crates/yaak-models/src/queries/folders.rs b/crates/yaak-models/src/queries/folders.rs index c2be0823..290bf0c1 100644 --- a/crates/yaak-models/src/queries/folders.rs +++ b/crates/yaak-models/src/queries/folders.rs @@ -180,6 +180,14 @@ impl<'a> ClientDb<'a> { } else { parent.request_timeout }, + request_message_size: if folder.setting_request_message_size.enabled { + ResolvedSetting::from_model( + folder.setting_request_message_size.value, + AnyModel::Folder(folder.clone()), + ) + } else { + parent.request_message_size + }, send_cookies: if folder.setting_send_cookies.enabled { ResolvedSetting::from_model( folder.setting_send_cookies.value, diff --git a/crates/yaak-models/src/queries/grpc_requests.rs b/crates/yaak-models/src/queries/grpc_requests.rs index 4fb96aa3..a074f07b 100644 --- a/crates/yaak-models/src/queries/grpc_requests.rs +++ b/crates/yaak-models/src/queries/grpc_requests.rs @@ -129,6 +129,14 @@ impl<'a> ClientDb<'a> { } else { parent.validate_certificates }, + request_message_size: if grpc_request.setting_request_message_size.enabled { + ResolvedSetting::from_model( + grpc_request.setting_request_message_size.value, + AnyModel::GrpcRequest(grpc_request.clone()), + ) + } else { + parent.request_message_size + }, ..parent }) } diff --git a/crates/yaak-models/src/queries/http_requests.rs b/crates/yaak-models/src/queries/http_requests.rs index b05d9afe..2c50e25d 100644 --- a/crates/yaak-models/src/queries/http_requests.rs +++ b/crates/yaak-models/src/queries/http_requests.rs @@ -131,6 +131,7 @@ impl<'a> ClientDb<'a> { } else { parent.request_timeout }, + request_message_size: parent.request_message_size, send_cookies: if http_request.setting_send_cookies.enabled { ResolvedSetting::from_model( http_request.setting_send_cookies.value, diff --git a/crates/yaak-models/src/queries/websocket_requests.rs b/crates/yaak-models/src/queries/websocket_requests.rs index 6e9c4114..296ea3c4 100644 --- a/crates/yaak-models/src/queries/websocket_requests.rs +++ b/crates/yaak-models/src/queries/websocket_requests.rs @@ -139,6 +139,14 @@ impl<'a> ClientDb<'a> { } else { parent.validate_certificates }, + request_message_size: if websocket_request.setting_request_message_size.enabled { + ResolvedSetting::from_model( + websocket_request.setting_request_message_size.value, + AnyModel::WebsocketRequest(websocket_request.clone()), + ) + } else { + parent.request_message_size + }, send_cookies: if websocket_request.setting_send_cookies.enabled { ResolvedSetting::from_model( websocket_request.setting_send_cookies.value, diff --git a/crates/yaak-models/src/queries/workspaces.rs b/crates/yaak-models/src/queries/workspaces.rs index fd97cc47..c77d7e38 100644 --- a/crates/yaak-models/src/queries/workspaces.rs +++ b/crates/yaak-models/src/queries/workspaces.rs @@ -21,6 +21,7 @@ impl<'a> ClientDb<'a> { &Workspace { name: "Yaak".to_string(), setting_follow_redirects: true, + setting_request_message_size: crate::models::DEFAULT_REQUEST_MESSAGE_SIZE, setting_validate_certificates: true, ..Default::default() }, @@ -102,6 +103,10 @@ impl<'a> ClientDb<'a> { workspace.setting_request_timeout, AnyModel::Workspace(workspace.clone()), ), + request_message_size: ResolvedSetting::from_model( + workspace.setting_request_message_size, + AnyModel::Workspace(workspace.clone()), + ), send_cookies: ResolvedSetting::from_model( workspace.setting_send_cookies, AnyModel::Workspace(workspace.clone()), diff --git a/crates/yaak-plugins/bindings/gen_models.ts b/crates/yaak-plugins/bindings/gen_models.ts index 38647316..d0ba2f1d 100644 --- a/crates/yaak-plugins/bindings/gen_models.ts +++ b/crates/yaak-plugins/bindings/gen_models.ts @@ -108,6 +108,7 @@ export type Folder = { settingValidateCertificates: InheritedBoolSetting; settingFollowRedirects: InheritedBoolSetting; settingRequestTimeout: InheritedIntSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type GraphQlIntrospection = { @@ -183,6 +184,7 @@ export type GrpcRequest = { */ url: string; settingValidateCertificates: InheritedBoolSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type HttpRequest = { @@ -426,7 +428,8 @@ export type WebsocketEvent = { messageType: WebsocketEventType; }; -export type WebsocketEventType = "binary" | "close" | "frame" | "open" | "ping" | "pong" | "text"; +export type WebsocketEventType = + "binary" | "close" | "error" | "frame" | "open" | "ping" | "pong" | "text"; export type WebsocketRequest = { model: "websocket_request"; @@ -450,6 +453,7 @@ export type WebsocketRequest = { settingSendCookies: InheritedBoolSetting; settingStoreCookies: InheritedBoolSetting; settingValidateCertificates: InheritedBoolSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type Workspace = { @@ -466,6 +470,7 @@ export type Workspace = { settingValidateCertificates: boolean; settingFollowRedirects: boolean; settingRequestTimeout: number; + settingRequestMessageSize: number; settingDnsOverrides: Array; settingSendCookies: boolean; settingStoreCookies: boolean; diff --git a/crates/yaak-sync/bindings/gen_models.ts b/crates/yaak-sync/bindings/gen_models.ts index 74b67d22..9f270475 100644 --- a/crates/yaak-sync/bindings/gen_models.ts +++ b/crates/yaak-sync/bindings/gen_models.ts @@ -46,6 +46,7 @@ export type Folder = { settingValidateCertificates: InheritedBoolSetting; settingFollowRedirects: InheritedBoolSetting; settingRequestTimeout: InheritedIntSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type GrpcRequest = { @@ -69,6 +70,7 @@ export type GrpcRequest = { */ url: string; settingValidateCertificates: InheritedBoolSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type HttpRequest = { @@ -159,6 +161,7 @@ export type WebsocketRequest = { settingSendCookies: InheritedBoolSetting; settingStoreCookies: InheritedBoolSetting; settingValidateCertificates: InheritedBoolSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type Workspace = { @@ -175,6 +178,7 @@ export type Workspace = { settingValidateCertificates: boolean; settingFollowRedirects: boolean; settingRequestTimeout: number; + settingRequestMessageSize: number; settingDnsOverrides: Array; settingSendCookies: boolean; settingStoreCookies: boolean; diff --git a/crates/yaak-ws/src/connect.rs b/crates/yaak-ws/src/connect.rs index 84a24109..1c282a18 100644 --- a/crates/yaak-ws/src/connect.rs +++ b/crates/yaak-ws/src/connect.rs @@ -20,6 +20,7 @@ pub async fn ws_connect( headers: HeaderMap, validate_certificates: bool, client_cert: Option, + request_message_size: i32, ) -> Result<(WebSocketStream>, Response)> { info!("Connecting to WS {url}"); let tls_config = get_tls_config(validate_certificates, WITH_ALPN, client_cert.clone())?; @@ -34,7 +35,7 @@ pub async fn ws_connect( let (stream, response) = connect_async_tls_with_config( req, - Some(WebSocketConfig::default()), + Some(websocket_config(request_message_size)), false, Some(Connector::Rustls(Arc::new(tls_config))), ) @@ -48,3 +49,12 @@ pub async fn ws_connect( Ok((stream, response)) } + +fn websocket_config(request_message_size: i32) -> WebSocketConfig { + let max_message_size = message_size_limit(request_message_size); + WebSocketConfig::default().max_message_size(max_message_size).max_frame_size(max_message_size) +} + +pub(crate) fn message_size_limit(setting: i32) -> Option { + setting.try_into().ok().filter(|limit| *limit > 0) +} diff --git a/crates/yaak-ws/src/error.rs b/crates/yaak-ws/src/error.rs index 571c0cef..14036338 100644 --- a/crates/yaak-ws/src/error.rs +++ b/crates/yaak-ws/src/error.rs @@ -4,7 +4,7 @@ use tokio_tungstenite::tungstenite; #[derive(Error, Debug)] pub enum Error { - #[error("WebSocket error: {0}")] + #[error("{0}")] WebSocketErr(#[from] tungstenite::Error), #[error(transparent)] @@ -16,7 +16,7 @@ pub enum Error { #[error(transparent)] TlsError(#[from] yaak_tls::error::Error), - #[error("WebSocket error: {0}")] + #[error("{0}")] GenericError(String), } diff --git a/crates/yaak-ws/src/manager.rs b/crates/yaak-ws/src/manager.rs index 77f4aa75..76f5a201 100644 --- a/crates/yaak-ws/src/manager.rs +++ b/crates/yaak-ws/src/manager.rs @@ -1,4 +1,5 @@ -use crate::connect::ws_connect; +use crate::connect::{message_size_limit, ws_connect}; +use crate::error::Error::GenericError; use crate::error::Result; use futures_util::stream::SplitSink; use futures_util::{SinkExt, StreamExt}; @@ -15,10 +16,16 @@ use tokio_tungstenite::tungstenite::http::HeaderValue; use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; use yaak_tls::ClientCertificateConfig; +type WebsocketSink = SplitSink>, Message>; + +struct WebsocketConnection { + max_message_size: Option, + sink: WebsocketSink, +} + #[derive(Clone)] pub struct WebsocketManager { - connections: - Arc>, Message>>>>, + connections: Arc>>, read_tasks: Arc>>>, } @@ -35,14 +42,20 @@ impl WebsocketManager { receive_tx: mpsc::Sender, validate_certificates: bool, client_cert: Option, + request_message_size: i32, ) -> Result { let tx = receive_tx.clone(); + let max_message_size = message_size_limit(request_message_size); let (stream, response) = - ws_connect(url, headers, validate_certificates, client_cert).await?; + ws_connect(url, headers, validate_certificates, client_cert, request_message_size) + .await?; let (write, mut read) = stream.split(); - self.connections.lock().await.insert(id.to_string(), write); + self.connections + .lock() + .await + .insert(id.to_string(), WebsocketConnection { max_message_size, sink: write }); let handle = { let connection_id = id.to_string(); @@ -70,13 +83,20 @@ impl WebsocketManager { } pub async fn send(&mut self, id: &str, msg: Message) -> Result<()> { - debug!("Send websocket message {msg:?}"); let mut connections = self.connections.lock().await; let connection = match connections.get_mut(id) { None => return Ok(()), Some(c) => c, }; - connection.send(msg).await?; + if let Some(limit) = connection.max_message_size { + let message_size = msg.len(); + if message_size > limit { + return Err(GenericError(format!( + "WebSocket message too large: found {message_size} bytes, the limit is {limit} bytes" + ))); + } + } + connection.sink.send(msg).await?; Ok(()) } @@ -84,7 +104,7 @@ impl WebsocketManager { info!("Closing websocket"); if let Some(mut connection) = self.connections.lock().await.remove(id) { // Wait a maximum of 1 second for the connection to close - if let Err(e) = connection.close().await { + if let Err(e) = connection.sink.close().await { warn!("Failed to close websocket connection {e:?}"); }; } diff --git a/package-lock.json b/package-lock.json index ec5ba5d4..38edb6ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,9 +88,9 @@ "nodejs-file-downloader": "^4.13.0", "npm-run-all": "^4.1.5", "typescript": "^5.8.3", - "vite": "npm:@voidzero-dev/vite-plus-core@^0.1.20", - "vite-plus": "^0.1.24", - "vitest": "npm:@voidzero-dev/vite-plus-test@^0.1.20" + "vite": "npm:@voidzero-dev/vite-plus-core@^0.2.1", + "vite-plus": "^0.2.1", + "vitest": "^4.1.9" } }, "apps/yaak-client": { @@ -190,12 +190,12 @@ "postcss-nesting": "^13.0.2", "rollup": "^4.60.3", "tailwindcss": "^3.4.17", - "vite": "npm:@voidzero-dev/vite-plus-core@^0.1.20", + "vite": "npm:@voidzero-dev/vite-plus-core@^0.2.1", "vite-plugin-static-copy": "^3.3.0", "vite-plugin-svgr": "^4.5.0", "vite-plugin-top-level-await": "^1.5.0", "vite-plugin-wasm": "^3.5.0", - "vite-plus": "^0.1.24" + "vite-plus": "^0.2.1" } }, "apps/yaak-client/node_modules/@types/node": { @@ -310,8 +310,8 @@ "@vitejs/plugin-react": "^6.0.1", "babel-plugin-react-compiler": "^1.0.0", "typescript": "^5.8.3", - "vite": "npm:@voidzero-dev/vite-plus-core@^0.1.20", - "vite-plus": "^0.1.24" + "vite": "npm:@voidzero-dev/vite-plus-core@^0.2.1", + "vite-plus": "^0.2.1" } }, "crates-proxy/yaak-proxy-lib": { @@ -715,6 +715,13 @@ "node": ">=6.9.0" } }, + "node_modules/@blazediff/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@blazediff/core/-/core-1.9.1.tgz", + "integrity": "sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==", + "dev": true, + "license": "MIT" + }, "node_modules/@changesets/changelog-github": { "version": "0.4.8", "resolved": "https://registry.npmjs.org/@changesets/changelog-github/-/changelog-github-0.4.8.tgz", @@ -2128,9 +2135,9 @@ } }, "node_modules/@oxc-project/runtime": { - "version": "0.127.0", - "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.127.0.tgz", - "integrity": "sha512-UQYLxAhDDPHm++szfa4z0RTdcPq5vaywrAoEA2n1YaAKeanXQdjHsoT6x1gP3U97RN8LZ7yHsSOrKPCcA6mCqw==", + "version": "0.136.0", + "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.136.0.tgz", + "integrity": "sha512-u0EutjK5y6NHJkl5jNJCs8zbup1z6A/UEWgajrYzqcEU3UX05HjqybhMQOLhSM0eKGISyM6WfSMMuklYSmH2wA==", "dev": true, "license": "MIT", "engines": { @@ -2138,9 +2145,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.127.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz", - "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==", + "version": "0.136.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.136.0.tgz", + "integrity": "sha512-39Al/B3v9esnHCX7S8l9Se2+s2tb9b2jcMd+bZ2L659VG73kNyGPpPrL5Zi/p0ty7p4pTTU2/Dd+g27hv94XCg==", "dev": true, "license": "MIT", "funding": { @@ -2148,9 +2155,9 @@ } }, "node_modules/@oxfmt/binding-android-arm-eabi": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.52.0.tgz", - "integrity": "sha512-17EMSJnQ9g+upVHrAUYDMfH5lvRKQ9Nvg8WtEoH72oDr1VpWz+7/o3tD97U1EToen2YAQ/68JmtDYkQUi20dfQ==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.55.0.tgz", + "integrity": "sha512-+rFDOqQe5LOWgxrAJaZgLRudr6GQm0wGI6gtu7vVkrdLGjNMUSGbAlaCr8j7F2H2Er97vYQCU8WDb30onqMM1g==", "cpu": [ "arm" ], @@ -2165,9 +2172,9 @@ } }, "node_modules/@oxfmt/binding-android-arm64": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.52.0.tgz", - "integrity": "sha512-A2G1IdwGEW2lLJkIxcvuirRH1CzSl/e0NX11zTlW1gvxJThfwbI/BEoaKrTNpm7M2FchvIf6guvIQU7d5iz+OQ==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.55.0.tgz", + "integrity": "sha512-ctulLq8s3x8Zmvw6+iccB09TIKERAklRSmbJ10gk8mlAn05qZxoyo52dj3Hi9IJcmDSwF54fQaTVh2CbL6PInw==", "cpu": [ "arm64" ], @@ -2182,9 +2189,9 @@ } }, "node_modules/@oxfmt/binding-darwin-arm64": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.52.0.tgz", - "integrity": "sha512-f9+bLvOYxy7NttCLFTvQ7afmqDOWY4wIP9xdvfj5trQ1qj6f2UFAGwZESlfsMjvJNTyRpXfIlOanCI9FOvoeQA==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.55.0.tgz", + "integrity": "sha512-xDQczLH9pw/RBk1h/GH0qcGMm8hQtmtVHBNLSH3lk1gEIR09hZ4L+mJQl4VqiVAvPK9VG9PYrWWuSQLt7xTbiA==", "cpu": [ "arm64" ], @@ -2199,9 +2206,9 @@ } }, "node_modules/@oxfmt/binding-darwin-x64": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.52.0.tgz", - "integrity": "sha512-YSTB9sJ5nnQd/Q0ddHkgof0ZCHPAnWZT1IW2SJ8omz7CP7KluJhO1fNHrpqdxCtpztJwSs4hY1uAee35wKxxaw==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.55.0.tgz", + "integrity": "sha512-JaNoFCkF2CJdGgpPSMbuO9HVyXyoNGIhMHPvp6NYAjeVKw9XEYc0HcUWJLPQa3Q69WV5wMa9m5jPMJPtbLtcRg==", "cpu": [ "x64" ], @@ -2216,9 +2223,9 @@ } }, "node_modules/@oxfmt/binding-freebsd-x64": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.52.0.tgz", - "integrity": "sha512-NIrRNTTPCs4UbmVs0bxLSCDlLCtIRMJIXklNKaXa5Oj2/K1UIMBvgE8+uPVo01Io3N9HF0+GAX+aAHjUgZS7vA==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.55.0.tgz", + "integrity": "sha512-DNbszhpg6S2MIzax5azdHFTTBIVkR5xr8yyRZuA4yoDAwOkzIp3tmldgKZM2+VlT+hJIG0xUksA+elISzMEAfA==", "cpu": [ "x64" ], @@ -2233,9 +2240,9 @@ } }, "node_modules/@oxfmt/binding-linux-arm-gnueabihf": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.52.0.tgz", - "integrity": "sha512-JXUCde8mn3GpgQouz2PXUokgy/uT1QrRJBL2s983VWcSQp62wTFYiNXgTKdeo1Jgbr0IgUnKKvzIk/YBlj/nVQ==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.55.0.tgz", + "integrity": "sha512-2snoaoRfFFyGnbOcKUK36rREBYxe/Xgz3uHbiA5zbCB/s6R4DQj4mHqYAaWWhgizCUSDxV8cE9zAZ0XleNpKGw==", "cpu": [ "arm" ], @@ -2250,9 +2257,9 @@ } }, "node_modules/@oxfmt/binding-linux-arm-musleabihf": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.52.0.tgz", - "integrity": "sha512-psbUXaRZ+V8DaXz10Qf7LSHtdtdKAmC8fxXgeU608jjzrmWK4quamZMOpl6sf+dikoFHA85uE93Q0BqxrCdQrQ==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.55.0.tgz", + "integrity": "sha512-q1aktHF/WRpSK81BX1dE/9vWrS2jGw1Nax2kb4DBLGAewubCLcoNyp4Zl/NSMgbv3vUS46Z33wIQkBVYOP3PYg==", "cpu": [ "arm" ], @@ -2267,16 +2274,13 @@ } }, "node_modules/@oxfmt/binding-linux-arm64-gnu": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.52.0.tgz", - "integrity": "sha512-Jw7MgWUU9lcLCcy82updISP3EthTlfvAwR6gWNxPzqly7+fLvOi2gHQE9xXQjpqaVLm/8P+gOzlv9ODuoVlaaw==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.55.0.tgz", + "integrity": "sha512-VD0y36aENezl/3tsclA/4G53Cc7iV+7Uoh7gz4yvcOTaEYBtJpQsE6PKDGTtUtOvGS4kv51ybfXY/nWZejO5IA==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2287,16 +2291,13 @@ } }, "node_modules/@oxfmt/binding-linux-arm64-musl": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.52.0.tgz", - "integrity": "sha512-wZg6bLjDvh2KibyI3QFUYo8GTXneIFsd0JvehtvJiUmQ8WRPERgxd/VM4ctWb86U5FT1FkqgS8/wZKVB+AZScg==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.55.0.tgz", + "integrity": "sha512-r8xlKJFcsRmn0H5jZrdORae6RX9jDBrZVvOoxF+bCQtampQJClv80aZEHsv+NsLsp2KCE5ql79O7DpPVzYWpXA==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2307,16 +2308,13 @@ } }, "node_modules/@oxfmt/binding-linux-ppc64-gnu": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.52.0.tgz", - "integrity": "sha512-IngE8uxhNvxcMrLjZNDo9xNLY7rEK33AKnaMd2B46he1e/mz2CfcW6If/U1wUjdRZddm1QzQaciqZkuMkdh1FA==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.55.0.tgz", + "integrity": "sha512-GRKv/HXHcwIVld/WU61rF0g0R16hl5EJ+ScKdpjevT57lnLnagj/U2YUbXf2mT+2Pg1uCzWC+mvGicPV3CDdLQ==", "cpu": [ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2327,16 +2325,13 @@ } }, "node_modules/@oxfmt/binding-linux-riscv64-gnu": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.52.0.tgz", - "integrity": "sha512-H3+DdFMv/efN3Efmhsv18jDrpiWWqKG7wsfAlQBqAt6z/E2Bx+TwEj2Nowe51CPOWB8/mFBC2dAMSgVFLvvowA==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.55.0.tgz", + "integrity": "sha512-rdv57enTiPtpSYRMKfAiEbQb0Puw5t9N7isVinDoo5qeLDScro2gznmZqSgSWbVZRzLisTeCTW8Qwgw0bOHv3A==", "cpu": [ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2347,16 +2342,13 @@ } }, "node_modules/@oxfmt/binding-linux-riscv64-musl": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.52.0.tgz", - "integrity": "sha512-zji+1kb7lJKohSDjzC1IsS+K/cKRs1hdVf0ZH0VbdbiakmtLvN9twBoXo/k8VdjFax7kfo+DyPxS7vv52br1aw==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.55.0.tgz", + "integrity": "sha512-7v1nNrlD43VY6+sYQ6efYyb3lE6QY182304PD/768ZxTjOmFd/3dQa3u/nGBUAXYdGSWOQc5N3PnS0QzUXyEIA==", "cpu": [ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2367,16 +2359,13 @@ } }, "node_modules/@oxfmt/binding-linux-s390x-gnu": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.52.0.tgz", - "integrity": "sha512-hcLBYedpCy7ToUvvBidWk7+11Yhg1oAZ4+6hKPic/mQI6NaqXJSXMps5nFlwUuX2ewhtLZZDPg63TI042qGKBg==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.55.0.tgz", + "integrity": "sha512-f4lJLUSPOgScjFl9LiflKCTocyNRwE25JmTMbN4XQdDjoZzEHjqf3wA3VESF1/csg7i8m7+EQLbrZyYDqe10UQ==", "cpu": [ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2387,16 +2376,13 @@ } }, "node_modules/@oxfmt/binding-linux-x64-gnu": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.52.0.tgz", - "integrity": "sha512-IDO2loXK2OtTOhSPchU9MW25mWL2QCDGdJbjN8MXKZVS80qXe5gMTwQWu/gMJ3juoBHbkuUZNB2N1LHzNT7DoA==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.55.0.tgz", + "integrity": "sha512-MihqiPziJNoWy4MqNSV+jVA1g+07iQDjZiR0vaCaDoPgFEiJpCMsxamktzLV07cEeQsSJ04vQaU4CzCQwIvtDA==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2407,16 +2393,13 @@ } }, "node_modules/@oxfmt/binding-linux-x64-musl": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.52.0.tgz", - "integrity": "sha512-mAV2Hjn0SatJ+KoAzKUC3eJhdJ8wv+3m1KyuS0dTsbF0c5weq+QrCt/DRZZM+uj/XiKzCDEUKYsBF30e2qkcyw==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.55.0.tgz", + "integrity": "sha512-Yqghym7KYAVjP9MmSrNZiDeerMuoejNjo0r3ox5H3GDKk8eAfl8VyJm9i+pWCLDCTnAbcTUMMN2ZKjUYXH1v3g==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2427,9 +2410,9 @@ } }, "node_modules/@oxfmt/binding-openharmony-arm64": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.52.0.tgz", - "integrity": "sha512-vd4npaUIwChxp7XzkqmepBWTT9YMcSe/NBApVGPC30/lLyOVaV3dvma1SKo03t8O73BPRAG7EyJzGlN5cJM5hQ==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.55.0.tgz", + "integrity": "sha512-s5SDvVVSbyQl1V5UU3Yl12M+XLUQ3rl5SglNqgAA2K4PXUtQhyNSS00wivONPEnNo5W01rCou8WkDNyvI/RGHg==", "cpu": [ "arm64" ], @@ -2444,9 +2427,9 @@ } }, "node_modules/@oxfmt/binding-win32-arm64-msvc": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.52.0.tgz", - "integrity": "sha512-k2sz6gWQdMfh5HPpIS+Bw/0UEV/kaK2xuqJRrWL233sEHx9WLlsmvlPFM4HUNThkYbSN0U0vPW7LVKZWDS8hPQ==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.55.0.tgz", + "integrity": "sha512-7p9FB5R32tw2KyyNX3wpQrR2WHwEHvMEiBlGXxeTCaRMCVNx3UtFMAUbaQ/pRNWIrEUZmYhJ6tcUH52uPTRYjQ==", "cpu": [ "arm64" ], @@ -2461,9 +2444,9 @@ } }, "node_modules/@oxfmt/binding-win32-ia32-msvc": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.52.0.tgz", - "integrity": "sha512-rhke69GTcArodLHpjMTfNnvjTEBryDeZcUCKK/VjXDMtfTULl6QRh0ymX5/hbCUv2WjYm9h/QbW++q2vE15gWQ==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.55.0.tgz", + "integrity": "sha512-ZYqj3fDnOT1IaVGMP5kpmkQl4F3tQIm2ZyAxvqkJYmI0xgWWak4ss4XYwv3VDfM+TWXeC9K4uQ/wW5jm/5XABA==", "cpu": [ "ia32" ], @@ -2478,9 +2461,9 @@ } }, "node_modules/@oxfmt/binding-win32-x64-msvc": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.52.0.tgz", - "integrity": "sha512-q5xL7oeXkZdEtNZWBdvehJcmt+GRu9l2bK40yJs1jJXlqq+r0Hygb1rTjq+FM2o/2xyt4cufH6KRplHp3Jjsvw==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.55.0.tgz", + "integrity": "sha512-eEYT5tivGnGbPHuOHuQpi6CGLObhh0re/5jcNQHihD2GRYkTM85dyi5a19zjP8Q00t1uqAx+/QGLUGdHeqzWyg==", "cpu": [ "x64" ], @@ -2579,9 +2562,9 @@ ] }, "node_modules/@oxlint/binding-android-arm-eabi": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.67.0.tgz", - "integrity": "sha512-VrSi571rDv1N8HaEDM+DEX8nmT0y9jJo8tzzW13vsOWTx59xQczCIJx68n2zWOXRT5YKZsOZXp4qkHN/10x4mw==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.70.0.tgz", + "integrity": "sha512-zFh0P4cswmRvw6nkyb89dr18rRanuaCPAsEXsFDoQY8WdaquI8Pt4NWFjaMJg6L23cy5NeN8J9cBnREbWzZhaw==", "cpu": [ "arm" ], @@ -2596,9 +2579,9 @@ } }, "node_modules/@oxlint/binding-android-arm64": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.67.0.tgz", - "integrity": "sha512-l6+NdYxMoRohix5r5bbigW16LPicceCwGcQ6LKKuE1kUdjgFfQolJjrJsQYPFetIs78Gxj/G/f5TEGoTCwj9nQ==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.70.0.tgz", + "integrity": "sha512-qI8o4HZjeGiBrWv+pJv4lH0Yi2Gl/JSp/EumBUApezJprIKa5PS4nU0lQsQngtky8k+SplQIOjv6hwu0SSxeyg==", "cpu": [ "arm64" ], @@ -2613,9 +2596,9 @@ } }, "node_modules/@oxlint/binding-darwin-arm64": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.67.0.tgz", - "integrity": "sha512-jOzXxS1AxFxhImLIRbtGIMrEwaXcgMw3gR57WB1cRk8ai+vpr6726kxXqVvlNsrXtJ/FrmOm8RxlC0m8SW24Qg==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.70.0.tgz", + "integrity": "sha512-8KjgVVHI5F9nVwHCRwwA78Ty7zNKP4Wd9OeN5PSv3iu/F/u1RVXoOCgLhWqust6HmwQG6xc8c+RCyaWENy24+w==", "cpu": [ "arm64" ], @@ -2630,9 +2613,9 @@ } }, "node_modules/@oxlint/binding-darwin-x64": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.67.0.tgz", - "integrity": "sha512-3DFAVY94OqjIZHXIPz37yGRSWwOFTAqChQ64/M69GYLawzP0KiwdhDNfqdKKYT0bTR/DNxmMnQsj3ns+8+X/Lg==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.70.0.tgz", + "integrity": "sha512-WVydssv5PSUBXFJTdNBWlmGkbNmvPGaFt/2SUT/EZRB6bq6bEOHmMlbnupZD5jmlEvi9+mZJHi8TCw15lyfSfQ==", "cpu": [ "x64" ], @@ -2647,9 +2630,9 @@ } }, "node_modules/@oxlint/binding-freebsd-x64": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.67.0.tgz", - "integrity": "sha512-e4dDKZuLu8TR9DEBssWSDahlPgZBwojTTHZUvnjBRJfJJbpxYCjfjKfi0Z1+CSLMiJBwI2yCDtRM1XJQaARjmg==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.70.0.tgz", + "integrity": "sha512-hJucmUf8OlinHNb1R7fI4Fw6WsAstOz7i8nmkWQfiHoZXtbufNm+MxiDTIMk1ggh2Ro4vLzgQ+bKvRY54MZoRA==", "cpu": [ "x64" ], @@ -2664,9 +2647,9 @@ } }, "node_modules/@oxlint/binding-linux-arm-gnueabihf": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.67.0.tgz", - "integrity": "sha512-BKytFdcQzbITV3xlnzDUDTEDtbUMCCiC4EaNTDZ4FyT8gdNvBC4gfiLucXp/sQl0XU3p7syTlorUWVVVBZab2g==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.70.0.tgz", + "integrity": "sha512-1BnS7wbCYDSXwWzJJ+mc3NURoha6m6m6RT5c6vgAY3oz7C3OVXP+S0awo2mRq97arrJkVvO3qRQfyAHL+76xtQ==", "cpu": [ "arm" ], @@ -2681,9 +2664,9 @@ } }, "node_modules/@oxlint/binding-linux-arm-musleabihf": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.67.0.tgz", - "integrity": "sha512-XYAv0esBDX7BpTzRDjVX2Vdj+zndd8ll2dFQiaeQ6zTZr7A8GRDTN7fH3FP3jU+O0vCDx85oH/EtG7BzPgAXuw==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.70.0.tgz", + "integrity": "sha512-yKy/UdbR55+M2yEcuiV5DCNC/gdQAjr/GioUy50QwBzSrKm8ueWADqyRLS9Xk+qjNeCYGg6A8FvUBds56ttfqg==", "cpu": [ "arm" ], @@ -2698,16 +2681,13 @@ } }, "node_modules/@oxlint/binding-linux-arm64-gnu": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.67.0.tgz", - "integrity": "sha512-zizRMjA0i6u/2B0evgda04iycu+MoNuf1pBy6Eh+1CjC5wMEG7qN5zdDKTCvFc0KSYSDM9QTG3gjZHirgtQuKg==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.70.0.tgz", + "integrity": "sha512-0A5XJ4alvmqFUFP/4oYSyaO+qLto/HrKEWTSaegiVl+HOufFngK2BjYw9x4RbwBt/du5QG6l5q1zeWiJYYG5yg==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2718,16 +2698,13 @@ } }, "node_modules/@oxlint/binding-linux-arm64-musl": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.67.0.tgz", - "integrity": "sha512-zB/Tf6sUjmmvvbva9Gj3JTJ8rJ9t4I8/U0o6vSRtd0DRIsIuyegBwJAzhSUFQHdMijIRJkW0exs/yBhpw2S20w==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.70.0.tgz", + "integrity": "sha512-JiylyurlB0CLSedNtx1gzv3FvfWPF1h/2Y3BJszPLNt5XQFlBsH5ke0Jle3iJb3uqu5m2e7A/DwzpuCAHdiU+A==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2738,16 +2715,13 @@ } }, "node_modules/@oxlint/binding-linux-ppc64-gnu": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.67.0.tgz", - "integrity": "sha512-kgU40Gt74CK0TCsF51KZymkIwN9U0BajKsMijB52zPqOeZU9NAHkA/NSQkZDHEaCakx42DxhXkODiAqf2b4Gug==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.70.0.tgz", + "integrity": "sha512-J8VPG7I3/HmgaU4u8pNU2kFx2+0U+vPLS1dXFxXOaR/2TQ0f8AC7DRz0SRGRI1bfphnX2hVYTTtLuhL4nYKL+Q==", "cpu": [ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2758,16 +2732,13 @@ } }, "node_modules/@oxlint/binding-linux-riscv64-gnu": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.67.0.tgz", - "integrity": "sha512-tOYhkk/iaG9aD3FvGpBFd1Lrw0x0RaVoJBxjUkfNzS50rC5NS5BteNCwgr8A2zCdADrIIoze6D7u6U5Ic++/iQ==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.70.0.tgz", + "integrity": "sha512-N2+4lV2KLN+oXTIIIwmWDhwkrnvqf5oX7Hw0zPjk+RuIVgiBQSOlJWF7uQoFx2siEYX0ZQ5cfSbEAHm+J3t7Wg==", "cpu": [ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2778,16 +2749,13 @@ } }, "node_modules/@oxlint/binding-linux-riscv64-musl": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.67.0.tgz", - "integrity": "sha512-sEtywrPb+0b+tHYl1SDCrw903fiC4eyKoNqzP3v+f2JT3Xcv4NEYG+P8rj+eEnX7IWhqV/xj8/JmcmVj21CXaA==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.70.0.tgz", + "integrity": "sha512-1e2L7cFCvx9QDzq6NPP+0tABKb5z6nWHyddWTNKprEsjO9xNrAtPowuCGpjNXxkTdsMiZ4jc8YQ5SstZd4XK6g==", "cpu": [ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2798,16 +2766,13 @@ } }, "node_modules/@oxlint/binding-linux-s390x-gnu": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.67.0.tgz", - "integrity": "sha512-BvR8Moa0zCLxroOx4vZaZN9nUfwAUpSTwjZdxZyKy4bv3PrzrXrxKR/ZQ0L9wNSvlPhnMJeZfa3q5w6ZCTuN6Q==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.70.0.tgz", + "integrity": "sha512-Kwu/l/8GcYibCWA9m9N5pRXMIKVSsL/YbgpLzYkqDhWTiqdRfnNJ/+nqIKRKQiFbHWsdlHEhzMwruJK+qcEruA==", "cpu": [ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2818,16 +2783,13 @@ } }, "node_modules/@oxlint/binding-linux-x64-gnu": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.67.0.tgz", - "integrity": "sha512-mm2cxM6fksOpq6l0uFws8BUGKAR4dNa/cZCn37Npq7PFbhD5HDJqWfnoIvTaeRKMy5XdS2tO0MA0qbHDrnXAAA==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.70.0.tgz", + "integrity": "sha512-tap04CsHYOl0nSAQJfPNIuBxqEPB2HnhQqwaOXLg1jnp2XfRo8Fa814dA4QC4zpvTWXCjAAaCY1W5LOORkEQuQ==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2838,16 +2800,13 @@ } }, "node_modules/@oxlint/binding-linux-x64-musl": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.67.0.tgz", - "integrity": "sha512-WmbMuLapKyDlobMkXAaAL0Y+Uczh4LETfIfQsUpbId4Ip8Ai82/jqeYTOoUCkuuhBFapgqP253+d83tLKOksJg==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.70.0.tgz", + "integrity": "sha512-hzJa/WgvtJpbBD9rgfy0qe+MjbxOXNUT0bfR1S6EQQzfTtBFA9xg5q8KSwRrQ2QfSS+TaP4j+4mVPQrfNc6UNg==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2858,9 +2817,9 @@ } }, "node_modules/@oxlint/binding-openharmony-arm64": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.67.0.tgz", - "integrity": "sha512-9g/PqxYJelzzTAOR5Y+RiRqdeydhEuXv2KxNeFcAKQ7UsvnWSY1OP4MsuPMbTO2Pf70tz7mFhl1j13H3fyh+8g==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.70.0.tgz", + "integrity": "sha512-xbsaNSNzVSnaJACCUYr1HQMyY/Q/Q1LkePmHG3UvZPvGCYGNxrsZp9OmtA6ick8xH47ltRRbRrPCM1YXYcyC+A==", "cpu": [ "arm64" ], @@ -2875,9 +2834,9 @@ } }, "node_modules/@oxlint/binding-win32-arm64-msvc": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.67.0.tgz", - "integrity": "sha512-2VhwE6Gatb0vJGnN0TBuQMbKCOiZlSQ/zJvVWYLK4a9d4iDiJOen/yVQkGpmsJ90MuH66fzi0kEKI0jRQMDxGA==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.70.0.tgz", + "integrity": "sha512-icAEsUI7JbW1TMRdEXV83mVAInhRVQYuuAlPpxdGwJ95chNdnCzjloRW8GglT0WvzOEZSio6fnYSk2DJ2Hv7LQ==", "cpu": [ "arm64" ], @@ -2892,9 +2851,9 @@ } }, "node_modules/@oxlint/binding-win32-ia32-msvc": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.67.0.tgz", - "integrity": "sha512-EQ3VExXfeM1InbE5+JjufhZZTWy+kHUwgt3yZR7gQ47Je/mE0WspQPan0OJznh493L5anM210YNJtH1PXjTSFg==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.70.0.tgz", + "integrity": "sha512-FHMSWbVsPVs/f+Jcl04ws4JJ2wUnauyTzlpxWRG/lSO/8GpX08Fo2gQZqdA6CrRFI+zvkxl+N/KwJGWfUwYVZA==", "cpu": [ "ia32" ], @@ -2909,9 +2868,9 @@ } }, "node_modules/@oxlint/binding-win32-x64-msvc": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.67.0.tgz", - "integrity": "sha512-bw24y+/1MHS4QDkons3YyHkPT9uCMoLHHgQhb+mb8NOjTYwub1CZ+K9Ngr8aO5DMrDrkqHwTzlTwFP2vS8Y/ZQ==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.70.0.tgz", + "integrity": "sha512-ptOlKwCz7n4AKs5VweMqG6DAg677FmKOK+vBkkL9DMNgFATIQ+upqUYBTOEwRQyRAx1ncGlPlXleV2hIcm3z4g==", "cpu": [ "x64" ], @@ -2926,9 +2885,9 @@ } }, "node_modules/@oxlint/plugins": { - "version": "1.61.0", - "resolved": "https://registry.npmjs.org/@oxlint/plugins/-/plugins-1.61.0.tgz", - "integrity": "sha512-nkOyZEF1vH527CkdQtOp1HMrVFEM4ResURvI2JFeGoup+h+43J/k/FgdOR9b9Isxg+Yae7qVDa7y3nssE8b3TQ==", + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/@oxlint/plugins/-/plugins-1.68.0.tgz", + "integrity": "sha512-titLmukUt/h8ho7Svlf0xSBjoy2ccZKrXjpXpZCj+v6V4CJccC2KyP45BLSCMx8YIpifMyiDyUptM4+5sruKbQ==", "dev": true, "license": "MIT", "engines": { @@ -4573,6 +4532,47 @@ "@tauri-apps/api": "^2.10.1" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/aws4": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@types/aws4/-/aws4-1.11.6.tgz", @@ -4874,322 +4874,182 @@ } } }, - "node_modules/@voidzero-dev/vite-plus-core": { - "version": "0.1.20", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-core/-/vite-plus-core-0.1.20.tgz", - "integrity": "sha512-4KmzRfzwTeG3JuvDijrdqWusSgRvLMKDPrVsDdtbDVVjEMq0VnM8lSH+Nvepd6Pg+SuSVUP212OIfH/3Yn1bfA==", + "node_modules/@vitest/browser": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.9.tgz", + "integrity": "sha512-j1BKtWmPcqpMhmx/L9EPLgAJpCb0zKfwoWLmqBbxaogCXHjOwHFSEoHCBfnGtx93xKQwilZ26m+UOsHqHMkRNg==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/runtime": "=0.127.0", - "@oxc-project/types": "=0.127.0", - "lightningcss": "^1.30.2", - "postcss": "^8.5.6" + "@blazediff/core": "1.9.1", + "@vitest/mocker": "4.1.9", + "@vitest/utils": "4.1.9", + "magic-string": "^0.30.21", + "pngjs": "^7.0.0", + "sirv": "^3.0.2", + "tinyrainbow": "^3.1.0", + "ws": "^8.19.0" }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" + "funding": { + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@arethetypeswrong/core": "^0.18.1", - "@tsdown/css": "0.21.10", - "@tsdown/exe": "0.21.10", - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "publint": "^0.3.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "typescript": "^5.0.0 || ^6.0.0", - "unplugin-unused": "^0.5.0", - "yaml": "^2.4.2" + "vitest": "4.1.9" + } + }, + "node_modules/@vitest/browser-preview": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/browser-preview/-/browser-preview-4.1.9.tgz", + "integrity": "sha512-a4/OrkMDb/WUnE4OOB/4FJbK3rYVO7YykqtUgcTKG4p2a0R3XcjPVu7SLRHFBs2+NIYhv5yxp1Lz3dbdGBjIow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@testing-library/dom": "^10.4.1", + "@testing-library/user-event": "^14.6.1", + "@vitest/browser": "4.1.9" }, - "peerDependenciesMeta": { - "@arethetypeswrong/core": { - "optional": true - }, - "@tsdown/css": { - "optional": true - }, - "@tsdown/exe": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "publint": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "typescript": { - "optional": true - }, - "unplugin-unused": { - "optional": true - }, - "yaml": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.1.9" } }, - "node_modules/@voidzero-dev/vite-plus-darwin-arm64": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-darwin-arm64/-/vite-plus-darwin-arm64-0.1.24.tgz", - "integrity": "sha512-Hpo9W9piSFlEsJzGkwzfDXhJGrnYByxHXF7NVQZ7g+SLOprddtlfTeM8t+gq9dxcuq0RzM8ddMAhDQP/K3fZQA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@voidzero-dev/vite-plus-darwin-x64": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-darwin-x64/-/vite-plus-darwin-x64-0.1.24.tgz", - "integrity": "sha512-SwnnnZrEFBiU5iKlh/CZAVwn0RFt/Udrvt3kFLtdRxMtN5bKaqTFVA2H8Y/FPCWp1QX9bs4V9ZIAeXAk06zLkw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@voidzero-dev/vite-plus-linux-arm64-gnu": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-linux-arm64-gnu/-/vite-plus-linux-arm64-gnu-0.1.24.tgz", - "integrity": "sha512-ImM3eqDki4DpRuHjW6dEh4St8zvbcfOMR7KQZJX42ArriCLQ/QdaYhDRRbcDi27XsOBqRxm2eqUUEymPrYIHpA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@voidzero-dev/vite-plus-linux-arm64-musl": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-linux-arm64-musl/-/vite-plus-linux-arm64-musl-0.1.24.tgz", - "integrity": "sha512-gj4mzbob/ls8Zs7iTuF9Gr0EFFF7tdpDiPxDPBkH8tJP5OkHABlzWUwJhU+9xxcUbTaXqpHDw68Mil7jm5dpMg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@voidzero-dev/vite-plus-linux-x64-gnu": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-linux-x64-gnu/-/vite-plus-linux-x64-gnu-0.1.24.tgz", - "integrity": "sha512-x7IYK7lI+WuF1n3jSzEYU6FgJxPX/R0rDmTTsOutooGGCU7uShZvfZqIoiTXK0eFnJU5ij5BfBgenenUfsaT/A==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@voidzero-dev/vite-plus-linux-x64-musl": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-linux-x64-musl/-/vite-plus-linux-x64-musl-0.1.24.tgz", - "integrity": "sha512-JCy2w0eSVUlWQlggK5T47MnL+j0o4EY7hLskINVI8gi+aixQF4xnYBDobz0lbxkqz3/IfiLyXUx6TcU3thcsGQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@voidzero-dev/vite-plus-test": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-test/-/vite-plus-test-0.1.24.tgz", - "integrity": "sha512-9NiG6UadG0iOaPL1AMsO5sDKkx6MADHw4/mMOmHWZUhhUwqzfVtnnptMK37vD71e6KyR7yAscx19FrtOWWtjvA==", + "node_modules/@vitest/expect": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", + "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@voidzero-dev/vite-plus-core": "0.1.24", - "es-module-lexer": "^1.7.0", - "obug": "^2.1.1", - "pixelmatch": "^7.1.0", - "pngjs": "^7.0.0", - "sirv": "^3.0.2", - "std-env": "^4.0.0", - "tinybench": "^2.9.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "ws": "^8.18.3" + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" }, - "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@opentelemetry/api": "^1.9.0", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/coverage-istanbul": "4.1.8", - "@vitest/coverage-v8": "4.1.8", - "@vitest/ui": "4.1.8", - "happy-dom": "*", - "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/coverage-istanbul": { - "optional": true - }, - "@vitest/coverage-v8": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - }, - "vite": { - "optional": false - } - } - }, - "node_modules/@voidzero-dev/vite-plus-test/node_modules/@oxc-project/runtime": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.133.0.tgz", - "integrity": "sha512-PkvjA1Lq5++V5S1E6Patr92ZVcieE6EalDr1VJTqv4BnjZdOUC4W3p8k1wMXSd5/2aFP4b/A6N5sg2Bkzcr9vQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@voidzero-dev/vite-plus-test/node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", - "dev": true, - "license": "MIT", "funding": { - "url": "https://github.com/sponsors/Boshen" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@voidzero-dev/vite-plus-test/node_modules/@voidzero-dev/vite-plus-core": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-core/-/vite-plus-core-0.1.24.tgz", - "integrity": "sha512-iXPGBABnQnrDMx89H6MOCGcTZp+QW+3rY4YMVKdE6ydchSvPk2O3MI2vgaRVfOtWJ2IjnxSnf1n2yjP67ZBRFQ==", + "node_modules/@vitest/mocker": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", + "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/runtime": "=0.133.0", - "@oxc-project/types": "=0.133.0", + "@vitest/spy": "4.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", + "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", + "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.9", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", + "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.9", + "@vitest/utils": "4.1.9", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", + "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", + "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.9", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@voidzero-dev/vite-plus-core": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-core/-/vite-plus-core-0.2.1.tgz", + "integrity": "sha512-iWdtOlLezgYcDqIzxZx1yOUhY93vUB+ob+mRYBNr7/3Hf80uRyTQbqVD1WtsYaANbzeUi81SQ1ZoUraXHO+u8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/runtime": "=0.136.0", + "@oxc-project/types": "=0.136.0", "lightningcss": "^1.30.2", "postcss": "^8.5.6" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^20.19.0 || ^22.18.0 || >=24.11.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", - "@tsdown/css": "0.22.1", - "@tsdown/exe": "0.22.1", + "@tsdown/css": "0.22.3", + "@tsdown/exe": "0.22.3", "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", @@ -5267,10 +5127,112 @@ } } }, + "node_modules/@voidzero-dev/vite-plus-darwin-arm64": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-darwin-arm64/-/vite-plus-darwin-arm64-0.2.1.tgz", + "integrity": "sha512-9AfN/5LKRks8gbTaHPiQHT0L4yboy2xB6x6vvCRWxQMWxPS6/ZJLf5kUIZeE7I1z33AEyLKKkDscsZZVMgMLgg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || ^22.18.0 || >=24.11.0" + } + }, + "node_modules/@voidzero-dev/vite-plus-darwin-x64": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-darwin-x64/-/vite-plus-darwin-x64-0.2.1.tgz", + "integrity": "sha512-Q1vyimRbf4M82qIQSWRyr7NJaH9ag5G7vVEfGVVJlQHNprI+Q8zj2Phcs/PGf6QcyjcL8UclLznQTHU9NgnKZw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || ^22.18.0 || >=24.11.0" + } + }, + "node_modules/@voidzero-dev/vite-plus-linux-arm64-gnu": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-linux-arm64-gnu/-/vite-plus-linux-arm64-gnu-0.2.1.tgz", + "integrity": "sha512-WHW3DziqedRfhJ2upq6kC4y/pmdQWYt322DVB7+4Xb4oOa/CT9GtnSrWIiXVJ4PSO42v54+YsSTKPH2HC5RbtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || ^22.18.0 || >=24.11.0" + } + }, + "node_modules/@voidzero-dev/vite-plus-linux-arm64-musl": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-linux-arm64-musl/-/vite-plus-linux-arm64-musl-0.2.1.tgz", + "integrity": "sha512-vUY7hYycZW0qEevpl7ImzZJFnOEKRYCaCOX4TBW0vk6MJZ+zj/xW7e0LOggzJcz2wbYAgLDqp5h+b8wV9dguDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || ^22.18.0 || >=24.11.0" + } + }, + "node_modules/@voidzero-dev/vite-plus-linux-x64-gnu": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-linux-x64-gnu/-/vite-plus-linux-x64-gnu-0.2.1.tgz", + "integrity": "sha512-tFxpToEaykBGxMQHp8M/qmr1yruRRED+c9gA1h9kmplqot04OxuqzRCWu/IiIvMJ0v3JFdOP3gqkyjXLLJhxIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || ^22.18.0 || >=24.11.0" + } + }, + "node_modules/@voidzero-dev/vite-plus-linux-x64-musl": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-linux-x64-musl/-/vite-plus-linux-x64-musl-0.2.1.tgz", + "integrity": "sha512-2scSS7wEbLO2758fqr1/bAULg7nLCFa5V8LO2b5w3g1CrTYdMTDt2WX1ghPesIi+70pYGydRbXo6iaaN43zfMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || ^22.18.0 || >=24.11.0" + } + }, "node_modules/@voidzero-dev/vite-plus-win32-arm64-msvc": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-win32-arm64-msvc/-/vite-plus-win32-arm64-msvc-0.1.24.tgz", - "integrity": "sha512-G+/lhLKVjyn3FmgXX8jeWgq7RcE5O1kdR7QyFayQOdlMX/ZRkvUwQD7bFaqhKzgJM6Oj3a1FH3HQPYk5QOYuCQ==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-win32-arm64-msvc/-/vite-plus-win32-arm64-msvc-0.2.1.tgz", + "integrity": "sha512-3+5FJYhi9SqBszjngI2LBmvoiqEwxJWyQ5UsOUtNz6/d+yDrDw+tOgHLl4OKIh5aVNZeIGXzxvP6h24kcEqIyg==", "cpu": [ "arm64" ], @@ -5281,13 +5243,13 @@ "win32" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^20.19.0 || ^22.18.0 || >=24.11.0" } }, "node_modules/@voidzero-dev/vite-plus-win32-x64-msvc": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-win32-x64-msvc/-/vite-plus-win32-x64-msvc-0.1.24.tgz", - "integrity": "sha512-b0e5XohEV1w/RdzAtv8/Hm6tvHPXouPtBNsljjW/lDJZq3NCLND5s6lqe8H4IenrgmKSoqakHWtlqJqM36cFbw==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-win32-x64-msvc/-/vite-plus-win32-x64-msvc-0.2.1.tgz", + "integrity": "sha512-5sOEwEoU5PW7ObmJ5VCakU09Oh14rYCoLQJkFqvOph6PK30lN5iqWGk0KigEyfcd7Zv+fZg9EmcERDol/3Xl9w==", "cpu": [ "x64" ], @@ -5298,7 +5260,7 @@ "win32" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^20.19.0 || ^22.18.0 || >=24.11.0" } }, "node_modules/@xmldom/xmldom": { @@ -5891,6 +5853,16 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -6402,6 +6374,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -7494,6 +7476,13 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "license": "MIT" }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -7795,9 +7784,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.2.0.tgz", + "integrity": "sha512-3lGxdTXCLfe1MYfTz1y2ksAAUM4NAOP6rPEjxGJVKO7TZ5+tvHCaQWGpC4Y3IXvW3ece0Cz1cIP4FWBxOnGCTQ==", "dev": true, "license": "MIT" }, @@ -7944,6 +7933,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -8037,6 +8036,16 @@ "dev": true, "license": "ISC" }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", @@ -10648,6 +10657,26 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/make-cancellable-promise": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz", @@ -12681,15 +12710,18 @@ } }, "node_modules/obug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } }, "node_modules/on-finished": { "version": "2.4.1", @@ -12804,9 +12836,9 @@ } }, "node_modules/oxfmt": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.52.0.tgz", - "integrity": "sha512-nJlYM35F64zTDMecCNhoHNkf+D/eHv7xcjj9XDSj+bFAVtN93m7v8DQMdHd6nDG6Akf/kEYYHmDUBs2Dz27Sug==", + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.55.0.tgz", + "integrity": "sha512-jSj2wCTakwgPMxkfiVZX0jf+nX+Nz6xlyAZjqNE0qXTFdCBPYlP6JAN+ODjmealw7DXBjOzYbdsqwBMAZnPZ6A==", "dev": true, "license": "MIT", "dependencies": { @@ -12822,25 +12854,25 @@ "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxfmt/binding-android-arm-eabi": "0.52.0", - "@oxfmt/binding-android-arm64": "0.52.0", - "@oxfmt/binding-darwin-arm64": "0.52.0", - "@oxfmt/binding-darwin-x64": "0.52.0", - "@oxfmt/binding-freebsd-x64": "0.52.0", - "@oxfmt/binding-linux-arm-gnueabihf": "0.52.0", - "@oxfmt/binding-linux-arm-musleabihf": "0.52.0", - "@oxfmt/binding-linux-arm64-gnu": "0.52.0", - "@oxfmt/binding-linux-arm64-musl": "0.52.0", - "@oxfmt/binding-linux-ppc64-gnu": "0.52.0", - "@oxfmt/binding-linux-riscv64-gnu": "0.52.0", - "@oxfmt/binding-linux-riscv64-musl": "0.52.0", - "@oxfmt/binding-linux-s390x-gnu": "0.52.0", - "@oxfmt/binding-linux-x64-gnu": "0.52.0", - "@oxfmt/binding-linux-x64-musl": "0.52.0", - "@oxfmt/binding-openharmony-arm64": "0.52.0", - "@oxfmt/binding-win32-arm64-msvc": "0.52.0", - "@oxfmt/binding-win32-ia32-msvc": "0.52.0", - "@oxfmt/binding-win32-x64-msvc": "0.52.0" + "@oxfmt/binding-android-arm-eabi": "0.55.0", + "@oxfmt/binding-android-arm64": "0.55.0", + "@oxfmt/binding-darwin-arm64": "0.55.0", + "@oxfmt/binding-darwin-x64": "0.55.0", + "@oxfmt/binding-freebsd-x64": "0.55.0", + "@oxfmt/binding-linux-arm-gnueabihf": "0.55.0", + "@oxfmt/binding-linux-arm-musleabihf": "0.55.0", + "@oxfmt/binding-linux-arm64-gnu": "0.55.0", + "@oxfmt/binding-linux-arm64-musl": "0.55.0", + "@oxfmt/binding-linux-ppc64-gnu": "0.55.0", + "@oxfmt/binding-linux-riscv64-gnu": "0.55.0", + "@oxfmt/binding-linux-riscv64-musl": "0.55.0", + "@oxfmt/binding-linux-s390x-gnu": "0.55.0", + "@oxfmt/binding-linux-x64-gnu": "0.55.0", + "@oxfmt/binding-linux-x64-musl": "0.55.0", + "@oxfmt/binding-openharmony-arm64": "0.55.0", + "@oxfmt/binding-win32-arm64-msvc": "0.55.0", + "@oxfmt/binding-win32-ia32-msvc": "0.55.0", + "@oxfmt/binding-win32-x64-msvc": "0.55.0" }, "peerDependencies": { "svelte": "^5.0.0", @@ -12856,9 +12888,9 @@ } }, "node_modules/oxlint": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.67.0.tgz", - "integrity": "sha512-blwwaHPdoH8piQ5/z0KHeoHFR7FZgl12WluKJfu4qFLPkZl6mK04PkLE45Fw1NxfBRSlh40Gu7MkxHUw++ociQ==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.70.0.tgz", + "integrity": "sha512-D6JgHtzkhRwvEC+A0Nw5AEc5bk8x5i1pHzvZIEf/a0C4hOzmAACNGtkDGPyFaxxX3ZVGxCPeig3P3rMM8XU3/g==", "dev": true, "license": "MIT", "bin": { @@ -12871,25 +12903,25 @@ "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxlint/binding-android-arm-eabi": "1.67.0", - "@oxlint/binding-android-arm64": "1.67.0", - "@oxlint/binding-darwin-arm64": "1.67.0", - "@oxlint/binding-darwin-x64": "1.67.0", - "@oxlint/binding-freebsd-x64": "1.67.0", - "@oxlint/binding-linux-arm-gnueabihf": "1.67.0", - "@oxlint/binding-linux-arm-musleabihf": "1.67.0", - "@oxlint/binding-linux-arm64-gnu": "1.67.0", - "@oxlint/binding-linux-arm64-musl": "1.67.0", - "@oxlint/binding-linux-ppc64-gnu": "1.67.0", - "@oxlint/binding-linux-riscv64-gnu": "1.67.0", - "@oxlint/binding-linux-riscv64-musl": "1.67.0", - "@oxlint/binding-linux-s390x-gnu": "1.67.0", - "@oxlint/binding-linux-x64-gnu": "1.67.0", - "@oxlint/binding-linux-x64-musl": "1.67.0", - "@oxlint/binding-openharmony-arm64": "1.67.0", - "@oxlint/binding-win32-arm64-msvc": "1.67.0", - "@oxlint/binding-win32-ia32-msvc": "1.67.0", - "@oxlint/binding-win32-x64-msvc": "1.67.0" + "@oxlint/binding-android-arm-eabi": "1.70.0", + "@oxlint/binding-android-arm64": "1.70.0", + "@oxlint/binding-darwin-arm64": "1.70.0", + "@oxlint/binding-darwin-x64": "1.70.0", + "@oxlint/binding-freebsd-x64": "1.70.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.70.0", + "@oxlint/binding-linux-arm-musleabihf": "1.70.0", + "@oxlint/binding-linux-arm64-gnu": "1.70.0", + "@oxlint/binding-linux-arm64-musl": "1.70.0", + "@oxlint/binding-linux-ppc64-gnu": "1.70.0", + "@oxlint/binding-linux-riscv64-gnu": "1.70.0", + "@oxlint/binding-linux-riscv64-musl": "1.70.0", + "@oxlint/binding-linux-s390x-gnu": "1.70.0", + "@oxlint/binding-linux-x64-gnu": "1.70.0", + "@oxlint/binding-linux-x64-musl": "1.70.0", + "@oxlint/binding-openharmony-arm64": "1.70.0", + "@oxlint/binding-win32-arm64-msvc": "1.70.0", + "@oxlint/binding-win32-ia32-msvc": "1.70.0", + "@oxlint/binding-win32-x64-msvc": "1.70.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.22.1", @@ -13251,19 +13283,6 @@ "node": ">= 6" } }, - "node_modules/pixelmatch": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-7.2.0.tgz", - "integrity": "sha512-xhcb4yHu9sM/G7foGzoLtXYcC0zHEaOXXjRKhGup0fw78Nf2Tkiapv4EQyMzrbcmQPsllAI7DbFY2UT7PlI9Pg==", - "dev": true, - "license": "ISC", - "dependencies": { - "pngjs": "^7.0.0" - }, - "bin": { - "pixelmatch": "bin/pixelmatch" - } - }, "node_modules/pkce-challenge": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", @@ -13626,6 +13645,44 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/prismjs": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", @@ -13783,6 +13840,13 @@ "react": "^19.2.3" } }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, "node_modules/react-markdown": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", @@ -14873,6 +14937,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -15019,6 +15090,13 @@ "stackframe": "^1.3.4" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/stackframe": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", @@ -15829,9 +15907,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", - "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "dev": true, "license": "MIT", "engines": { @@ -15864,6 +15942,16 @@ "node": "^20.0.0 || >=22.0.0" } }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-buffer": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", @@ -16468,33 +16556,33 @@ }, "node_modules/vite": { "name": "@voidzero-dev/vite-plus-core", - "version": "0.1.20", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-core/-/vite-plus-core-0.1.20.tgz", - "integrity": "sha512-4KmzRfzwTeG3JuvDijrdqWusSgRvLMKDPrVsDdtbDVVjEMq0VnM8lSH+Nvepd6Pg+SuSVUP212OIfH/3Yn1bfA==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-core/-/vite-plus-core-0.2.1.tgz", + "integrity": "sha512-iWdtOlLezgYcDqIzxZx1yOUhY93vUB+ob+mRYBNr7/3Hf80uRyTQbqVD1WtsYaANbzeUi81SQ1ZoUraXHO+u8A==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/runtime": "=0.127.0", - "@oxc-project/types": "=0.127.0", + "@oxc-project/runtime": "=0.136.0", + "@oxc-project/types": "=0.136.0", "lightningcss": "^1.30.2", "postcss": "^8.5.6" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^20.19.0 || ^22.18.0 || >=24.11.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", - "@tsdown/css": "0.21.10", - "@tsdown/exe": "0.21.10", + "@tsdown/css": "0.22.3", + "@tsdown/exe": "0.22.3", "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", + "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", - "publint": "^0.3.0", + "publint": "^0.3.8", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -16503,6 +16591,7 @@ "tsx": "^4.8.1", "typescript": "^5.0.0 || ^6.0.0", "unplugin-unused": "^0.5.0", + "unrun": "*", "yaml": "^2.4.2" }, "peerDependenciesMeta": { @@ -16557,6 +16646,9 @@ "unplugin-unused": { "optional": true }, + "unrun": { + "optional": true + }, "yaml": { "optional": true } @@ -16641,19 +16733,28 @@ } }, "node_modules/vite-plus": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/vite-plus/-/vite-plus-0.1.24.tgz", - "integrity": "sha512-b3fr6WtCiEhetjuzW/4KcEMOAMuZxoxZATWaXKmPzOLf1upG+pzKJOFZTb94D6wiPBlwcjxoaUtF7C3uAN+VjQ==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vite-plus/-/vite-plus-0.2.1.tgz", + "integrity": "sha512-q5q/Y38UkWFsNg1JO+RyRdPUqoewaSqIlMyK2p83GKNUvf4D38Ntb3PToRTDZbTRh7mWt+B+d0DQBv4nCDpMcQ==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", - "@oxlint/plugins": "=1.61.0", - "@voidzero-dev/vite-plus-core": "0.1.24", - "@voidzero-dev/vite-plus-test": "0.1.24", - "oxfmt": "=0.52.0", - "oxlint": "=1.67.0", - "oxlint-tsgolint": "=0.23.0" + "@oxc-project/types": "=0.136.0", + "@oxlint/plugins": "=1.68.0", + "@vitest/browser": "4.1.9", + "@vitest/browser-preview": "4.1.9", + "@vitest/expect": "4.1.9", + "@vitest/mocker": "4.1.9", + "@vitest/pretty-format": "4.1.9", + "@vitest/runner": "4.1.9", + "@vitest/snapshot": "4.1.9", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", + "@voidzero-dev/vite-plus-core": "0.2.1", + "oxfmt": "=0.55.0", + "oxlint": "=1.70.0", + "oxlint-tsgolint": "=0.23.0", + "vitest": "4.1.9" }, "bin": { "oxfmt": "bin/oxfmt", @@ -16661,170 +16762,78 @@ "vp": "bin/vp" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^20.19.0 || ^22.18.0 || >=24.11.0" }, "optionalDependencies": { - "@voidzero-dev/vite-plus-darwin-arm64": "0.1.24", - "@voidzero-dev/vite-plus-darwin-x64": "0.1.24", - "@voidzero-dev/vite-plus-linux-arm64-gnu": "0.1.24", - "@voidzero-dev/vite-plus-linux-arm64-musl": "0.1.24", - "@voidzero-dev/vite-plus-linux-x64-gnu": "0.1.24", - "@voidzero-dev/vite-plus-linux-x64-musl": "0.1.24", - "@voidzero-dev/vite-plus-win32-arm64-msvc": "0.1.24", - "@voidzero-dev/vite-plus-win32-x64-msvc": "0.1.24" - } - }, - "node_modules/vite-plus/node_modules/@oxc-project/runtime": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.133.0.tgz", - "integrity": "sha512-PkvjA1Lq5++V5S1E6Patr92ZVcieE6EalDr1VJTqv4BnjZdOUC4W3p8k1wMXSd5/2aFP4b/A6N5sg2Bkzcr9vQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/vite-plus/node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/vite-plus/node_modules/@voidzero-dev/vite-plus-core": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-core/-/vite-plus-core-0.1.24.tgz", - "integrity": "sha512-iXPGBABnQnrDMx89H6MOCGcTZp+QW+3rY4YMVKdE6ydchSvPk2O3MI2vgaRVfOtWJ2IjnxSnf1n2yjP67ZBRFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/runtime": "=0.133.0", - "@oxc-project/types": "=0.133.0", - "lightningcss": "^1.30.2", - "postcss": "^8.5.6" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" + "@voidzero-dev/vite-plus-darwin-arm64": "0.2.1", + "@voidzero-dev/vite-plus-darwin-x64": "0.2.1", + "@voidzero-dev/vite-plus-linux-arm64-gnu": "0.2.1", + "@voidzero-dev/vite-plus-linux-arm64-musl": "0.2.1", + "@voidzero-dev/vite-plus-linux-x64-gnu": "0.2.1", + "@voidzero-dev/vite-plus-linux-x64-musl": "0.2.1", + "@voidzero-dev/vite-plus-win32-arm64-msvc": "0.2.1", + "@voidzero-dev/vite-plus-win32-x64-msvc": "0.2.1" }, "peerDependencies": { - "@arethetypeswrong/core": "^0.18.1", - "@tsdown/css": "0.22.1", - "@tsdown/exe": "0.22.1", - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "publint": "^0.3.8", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "typescript": "^5.0.0 || ^6.0.0", - "unplugin-unused": "^0.5.0", - "unrun": "*", - "yaml": "^2.4.2" + "@vitest/browser-playwright": "4.1.9", + "@vitest/browser-webdriverio": "4.1.9" }, "peerDependenciesMeta": { - "@arethetypeswrong/core": { + "@vitest/browser-playwright": { "optional": true }, - "@tsdown/css": { - "optional": true - }, - "@tsdown/exe": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "publint": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "typescript": { - "optional": true - }, - "unplugin-unused": { - "optional": true - }, - "unrun": { - "optional": true - }, - "yaml": { + "@vitest/browser-webdriverio": { "optional": true } } }, "node_modules/vitest": { - "name": "@voidzero-dev/vite-plus-test", - "version": "0.1.20", - "resolved": "https://registry.npmjs.org/@voidzero-dev/vite-plus-test/-/vite-plus-test-0.1.20.tgz", - "integrity": "sha512-vy2dJYw1bhgQ/+BrQrfwPlSKzQ2mm3YLJ9kGF7Yo0UJ2P3XKpshtgFIWLjSg/IASnC93OAx0c/7j3NM0I1RMuA==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", + "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@voidzero-dev/vite-plus-core": "0.1.20", - "es-module-lexer": "^1.7.0", + "@vitest/expect": "4.1.9", + "@vitest/mocker": "4.1.9", + "@vitest/pretty-format": "4.1.9", + "@vitest/runner": "4.1.9", + "@vitest/snapshot": "4.1.9", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", "obug": "^2.1.1", - "pixelmatch": "^7.1.0", - "pngjs": "^7.0.0", - "sirv": "^3.0.2", - "std-env": "^4.0.0", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", - "ws": "^8.18.3" + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" }, "engines": { "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, + "funding": { + "url": "https://opencollective.com/vitest" + }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/coverage-istanbul": "4.1.5", - "@vitest/coverage-v8": "4.1.5", - "@vitest/ui": "4.1.5", + "@vitest/browser-playwright": "4.1.9", + "@vitest/browser-preview": "4.1.9", + "@vitest/browser-webdriverio": "4.1.9", + "@vitest/coverage-istanbul": "4.1.9", + "@vitest/coverage-v8": "4.1.9", + "@vitest/ui": "4.1.9", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -16839,6 +16848,15 @@ "@types/node": { "optional": true }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, "@vitest/coverage-istanbul": { "optional": true }, @@ -17019,6 +17037,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", diff --git a/package.json b/package.json index c997b7b5..28946ad4 100644 --- a/package.json +++ b/package.json @@ -121,14 +121,13 @@ "nodejs-file-downloader": "^4.13.0", "npm-run-all": "^4.1.5", "typescript": "^5.8.3", - "vite": "npm:@voidzero-dev/vite-plus-core@^0.1.20", - "vite-plus": "^0.1.24", - "vitest": "npm:@voidzero-dev/vite-plus-test@^0.1.20" + "vite": "npm:@voidzero-dev/vite-plus-core@^0.2.1", + "vite-plus": "^0.2.1", + "vitest": "^4.1.9" }, "overrides": { "js-yaml": "^4.1.1", - "vite": "npm:@voidzero-dev/vite-plus-core@^0.1.20", - "vitest": "npm:@voidzero-dev/vite-plus-test@^0.1.20" + "vite": "npm:@voidzero-dev/vite-plus-core@^0.2.1" }, "packageManager": "npm@11.11.1" } diff --git a/packages/plugin-runtime-types/src/bindings/gen_models.ts b/packages/plugin-runtime-types/src/bindings/gen_models.ts index 38647316..d0ba2f1d 100644 --- a/packages/plugin-runtime-types/src/bindings/gen_models.ts +++ b/packages/plugin-runtime-types/src/bindings/gen_models.ts @@ -108,6 +108,7 @@ export type Folder = { settingValidateCertificates: InheritedBoolSetting; settingFollowRedirects: InheritedBoolSetting; settingRequestTimeout: InheritedIntSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type GraphQlIntrospection = { @@ -183,6 +184,7 @@ export type GrpcRequest = { */ url: string; settingValidateCertificates: InheritedBoolSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type HttpRequest = { @@ -426,7 +428,8 @@ export type WebsocketEvent = { messageType: WebsocketEventType; }; -export type WebsocketEventType = "binary" | "close" | "frame" | "open" | "ping" | "pong" | "text"; +export type WebsocketEventType = + "binary" | "close" | "error" | "frame" | "open" | "ping" | "pong" | "text"; export type WebsocketRequest = { model: "websocket_request"; @@ -450,6 +453,7 @@ export type WebsocketRequest = { settingSendCookies: InheritedBoolSetting; settingStoreCookies: InheritedBoolSetting; settingValidateCertificates: InheritedBoolSetting; + settingRequestMessageSize: InheritedIntSetting; }; export type Workspace = { @@ -466,6 +470,7 @@ export type Workspace = { settingValidateCertificates: boolean; settingFollowRedirects: boolean; settingRequestTimeout: number; + settingRequestMessageSize: number; settingDnsOverrides: Array; settingSendCookies: boolean; settingStoreCookies: boolean;