From b71dcb825321a6348d081ccd29ee8c57865fb434 Mon Sep 17 00:00:00 2001 From: Xiyuan Chen <52963600+GareArc@users.noreply.github.com> Date: Mon, 31 Aug 2026 12:15:40 +0000 Subject: [PATCH 01/33] fix(web): reset test-email sender state when reopening the modal (#41515) Co-authored-by: yyh --- .../__tests__/test-email-sender.spec.tsx | 85 ++++ .../delivery-method/test-email-sender.tsx | 411 +++++++++--------- 2 files changed, 294 insertions(+), 202 deletions(-) diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/test-email-sender.spec.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/test-email-sender.spec.tsx index 75fc62c4dd7..459f1d591f2 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/test-email-sender.spec.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/test-email-sender.spec.tsx @@ -11,6 +11,7 @@ import { toast } from '@langgenius/dify-ui/toast' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { fireEvent, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' +import { useState } from 'react' import { useStore as useAppStore } from '@/app/components/app/store' import { HooksStoreContext } from '@/app/components/workflow/hooks-store/provider' import { createHooksStore } from '@/app/components/workflow/hooks-store/store' @@ -135,6 +136,26 @@ const createConfig = (overrides: Partial = {}): EmailConfig => ({ ...overrides, }) +const TestEmailSenderHarness = () => { + const [open, setOpen] = useState(true) + + return ( + <> + + + + ) +} + const createFormInput = (overrides: Partial = {}): FormInputItem => ({ type: InputVarType.paragraph, output_variable_name: 'user_name', @@ -251,6 +272,70 @@ describe('human-input/delivery-method/test-email-sender', () => { expect(handleOpenChange).toHaveBeenCalledWith(false) }) + it('should start a fresh session after closing and reopening', async () => { + const user = userEvent.setup() + const { requests } = setupFetch() + renderWithProviders() + + await user.click( + screen.getByRole('button', { + name: 'workflow.nodes.humanInput.deliveryMethod.emailSender.send', + }), + ) + expect( + await screen.findByText('workflow.nodes.humanInput.deliveryMethod.emailSender.done'), + ).toBeInTheDocument() + + await user.click(screen.getByRole('button', { name: 'common.operation.ok' })) + await waitFor(() => { + expect(screen.queryByRole('dialog')).not.toBeInTheDocument() + }) + await user.click(screen.getByRole('button', { name: 'Open test email sender' })) + + expect( + screen.getByRole('button', { + name: 'workflow.nodes.humanInput.deliveryMethod.emailSender.send', + }), + ).toBeInTheDocument() + expect( + screen.queryByText('workflow.nodes.humanInput.deliveryMethod.emailSender.done'), + ).not.toBeInTheDocument() + + await user.click( + screen.getByRole('button', { + name: 'workflow.nodes.humanInput.deliveryMethod.emailSender.send', + }), + ) + await waitFor(() => { + expect( + requests.filter( + (request) => request.method === 'POST' && request.url.endsWith('/delivery-test'), + ), + ).toHaveLength(2) + }) + }) + + it('should stay open when clicking outside the dialog', async () => { + const user = userEvent.setup() + const handleOpenChange = vi.fn() + + renderWithProviders( + , + ) + + await user.click(document.body) + + expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(handleOpenChange).not.toHaveBeenCalled() + }) + it('should submit variables referenced by dynamic select option sources', async () => { const user = userEvent.setup() const { requests } = setupFetch() diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx index 86f772b15e7..5d730e134cc 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx @@ -45,6 +45,8 @@ type EmailSenderModalProps = { availableNodes?: Node[] } +type EmailSenderContentProps = Omit + const getOriginVar = (valueSelector: string[], list: NodeOutPutVar[]) => { const targetVar = list.find((item) => item.nodeId === valueSelector[0]) if (!targetVar) return undefined @@ -117,10 +119,9 @@ const formatEmailSenderInputs = ( } } -const EmailSenderModal = ({ +const EmailSenderContent = ({ nodeId, deliveryId, - open, onOpenChange, jumpToEmailConfigModal, config, @@ -128,7 +129,7 @@ const EmailSenderModal = ({ formInputs, nodesOutputVars = [], availableNodes = [], -}: EmailSenderModalProps) => { +}: EmailSenderContentProps) => { const { t } = useTranslation() const { data: userProfileEmail } = useSuspenseQuery({ ...userProfileQueryOptions(), @@ -258,142 +259,49 @@ const EmailSenderModal = ({ if (done) { return ( - - -
- - {t(($) => $[`${i18nPrefix}.deliveryMethod.emailSender.done`], { ns: 'workflow' })} - - {debugEnabled && ( -
- $[`${i18nPrefix}.deliveryMethod.emailSender.debugDone`]} - ns="workflow" - components={{ - email: , - }} - values={{ email: userProfileEmail }} - /> -
- )} - {!debugEnabled && onlyWholeTeam && ( -
- $[`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamDone2`]} - ns="workflow" - components={{ - team: , - }} - values={{ team: currentWorkspace.name.replace(/'/g, '’') }} - /> -
- )} - {!debugEnabled && onlySpecificUsers && ( -
- {t(($) => $[`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamDone3`], { - ns: 'workflow', - })} -
- )} - {!debugEnabled && combinedRecipients && ( -
- $[`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamDone1`]} - ns="workflow" - components={{ - team: , - }} - values={{ team: currentWorkspace.name.replace(/'/g, '’') }} - /> -
- )} -
- {(onlySpecificUsers || combinedRecipients) && !debugEnabled && ( -
- +
+ + {t(($) => $[`${i18nPrefix}.deliveryMethod.emailSender.done`], { ns: 'workflow' })} + + {debugEnabled && ( +
+ $[`${i18nPrefix}.deliveryMethod.emailSender.debugDone`]} + ns="workflow" + components={{ + email: , + }} + values={{ email: userProfileEmail }} />
)} -
- -
- -
- ) - } - - return ( - - - $['operation.close'], { ns: 'common' })} - size="lg" - className="absolute inset-e-6 top-6" - > - - - } - /> -
- - {t(($) => $[`${i18nPrefix}.deliveryMethod.emailSender.title`], { ns: 'workflow' })} - - {debugEnabled && ( - <> -
- {t(($) => $[`${i18nPrefix}.deliveryMethod.emailSender.debugModeTip`], { - ns: 'workflow', - })} -
-
- $[`${i18nPrefix}.deliveryMethod.emailSender.debugModeTip2`]} - ns="workflow" - components={{ - email: , - }} - values={{ email: userProfileEmail }} - /> -
- - )} {!debugEnabled && onlyWholeTeam && ( -
+
$[`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamTip2`]} + i18nKey={($) => $[`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamDone2`]} ns="workflow" components={{ - team: , + team: , }} values={{ team: currentWorkspace.name.replace(/'/g, '’') }} />
)} {!debugEnabled && onlySpecificUsers && ( -
- {t(($) => $[`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamTip3`], { +
+ {t(($) => $[`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamDone3`], { ns: 'workflow', })}
)} {!debugEnabled && combinedRecipients && ( -
+
$[`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamTip1`]} + i18nKey={($) => $[`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamDone1`]} ns="workflow" components={{ - team: , + team: , }} values={{ team: currentWorkspace.name.replace(/'/g, '’') }} /> @@ -401,94 +309,193 @@ const EmailSenderModal = ({ )}
{(onlySpecificUsers || combinedRecipients) && !debugEnabled && ( - <> -
- -
-
- $[`${i18nPrefix}.deliveryMethod.emailSender.tip`]} - ns="workflow" - components={{ - strong: ( -
- - )} - {/* vars */} - {generatedInputs.length > 0 && ( - <> -
- -
-
- -
- {t(($) => $[`${i18nPrefix}.deliveryMethod.emailSender.varsTip`], { - ns: 'workflow', - })} -
- {!collapsed && ( -
- {generatedInputs.map((variable, index) => ( -
- handleValueChange(variable.variable, v)} - /> -
- ))} -
- )} -
- +
+ +
)}
- -
+ + ) + } + + return ( + <> + $['operation.close'], { ns: 'common' })} + size="lg" + className="absolute inset-e-6 top-6" + > + + + } + /> +
+ + {t(($) => $[`${i18nPrefix}.deliveryMethod.emailSender.title`], { ns: 'workflow' })} + + {debugEnabled && ( + <> +
+ {t(($) => $[`${i18nPrefix}.deliveryMethod.emailSender.debugModeTip`], { + ns: 'workflow', + })} +
+
+ $[`${i18nPrefix}.deliveryMethod.emailSender.debugModeTip2`]} + ns="workflow" + components={{ + email: , + }} + values={{ email: userProfileEmail }} + /> +
+ + )} + {!debugEnabled && onlyWholeTeam && ( +
+ $[`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamTip2`]} + ns="workflow" + components={{ + team: , + }} + values={{ team: currentWorkspace.name.replace(/'/g, '’') }} + /> +
+ )} + {!debugEnabled && onlySpecificUsers && ( +
+ {t(($) => $[`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamTip3`], { + ns: 'workflow', + })} +
+ )} + {!debugEnabled && combinedRecipients && ( +
+ $[`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamTip1`]} + ns="workflow" + components={{ + team: , + }} + values={{ team: currentWorkspace.name.replace(/'/g, '’') }} + /> +
+ )} +
+ {(onlySpecificUsers || combinedRecipients) && !debugEnabled && ( + <> +
+ +
+
+ $[`${i18nPrefix}.deliveryMethod.emailSender.tip`]} + ns="workflow" + components={{ + strong: ( +
+ + )} + {/* vars */} + {generatedInputs.length > 0 && ( + <> +
+ +
+
+ +
+ {t(($) => $[`${i18nPrefix}.deliveryMethod.emailSender.varsTip`], { + ns: 'workflow', + })} +
+ {!collapsed && ( +
+ {generatedInputs.map((variable, index) => ( +
+ handleValueChange(variable.variable, v)} + /> +
+ ))} +
+ )} +
+ + )} +
+ + +
+ + ) +} + +const EmailSenderModal = ({ open, onOpenChange, ...props }: EmailSenderModalProps) => { + return ( + + + ) From 57144b0094b083815824295a41bcdef7a43c181c Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Mon, 31 Aug 2026 13:37:22 +0000 Subject: [PATCH 02/33] docs(frontend): clarify transient state ownership (#41541) --- .../references/component-architecture.md | 17 ++++++++++++-- .../references/testing.md | 1 + .../skills/how-to-write-component/SKILL.md | 9 ++++---- .../references/interactions.md | 8 ++++--- .../references/ownership.md | 7 +++--- .../references/runtime.md | 2 +- .../references/state.md | 8 ++++--- packages/dify-ui/AGENTS.md | 4 ++-- packages/dify-ui/README.md | 4 ++-- packages/dify-ui/docs/forms.md | 22 ++++++++++++++++--- packages/dify-ui/docs/overlays.md | 20 +++++++++++++++++ web/docs/test.md | 1 + 12 files changed, 80 insertions(+), 23 deletions(-) diff --git a/.agents/skills/frontend-code-review/references/component-architecture.md b/.agents/skills/frontend-code-review/references/component-architecture.md index ffabbf4cd69..9eda6589fd0 100644 --- a/.agents/skills/frontend-code-review/references/component-architecture.md +++ b/.agents/skills/frontend-code-review/references/component-architecture.md @@ -61,7 +61,7 @@ Flag effects that: - Transform props/state for rendering. - Copy one state value into another representing the same concept. - Handle user actions that belong in event handlers. -- Reset state from props when a keyed reset, stable ID, or render-time derivation would work. +- Reset local state from props or visibility when derivation, a stable semantic identity, or the intended mounted owner already expresses the lifecycle. - Fetch data that belongs in framework APIs or TanStack Query. If an effect remains, it must synchronize with a named external system: browser API, subscription, timer, analytics-on-visibility, non-React widget, or imperative DOM integration. @@ -71,11 +71,24 @@ If an effect remains, it must synchronize with a named external system: browser Flag: - Storing derived booleans, disabled flags, default tabs, or loading labels that can be calculated from current query/feature state. +- Per-session state held by a longer-lived visibility coordinator and cleared through an open-state Effect or a generated key when the primitive's mounted-content lifecycle already matches the intended state lifetime. +- A DOM field mirrored into competing prop, default, and React state sources when editing does not require those sources to synchronize. - Local state used to fake server data or generated contract fields. - UI state persisted to localStorage when it is live app state. - Feature-local mock shells wired to unrelated existing APIs before the real API is confirmed. -Prefer render-time derivation. Keep true local state for user choices, transient input, controlled popups, and feature UI state that has no server source. +Review state lifetime before its storage mechanism. For a hidden surface, distinguish the +visibility coordinator from mounted content. State private to one mounted session belongs in that +content owner; promote it only when the draft must survive that content owner's unmount or another +owner coordinates it. A stable semantic identity key may create a new snapshot when the represented +identity changes; a generated key is not a routine reset command. + +Prefer render-time derivation. Keep true local state for user choices, transient input, controlled +popups, and feature UI state that has no server source. Submit-only DOM fields may remain +uncontrolled; use local controlled state when React must own the current value to drive rendering +or coordination. Observing change events or tracking a derived fact such as dirty state does not +require mirroring the field value. Do not flag controlled state by itself without a concrete +competing-source, stale-state, or ownership defect. ## Navigation diff --git a/.agents/skills/frontend-code-review/references/testing.md b/.agents/skills/frontend-code-review/references/testing.md index 2f81d10589e..3bda124c01b 100644 --- a/.agents/skills/frontend-code-review/references/testing.md +++ b/.agents/skills/frontend-code-review/references/testing.md @@ -9,6 +9,7 @@ Flag missing coverage when a change alters a reachable contract such as: - User interaction, navigation, form submission, validation, or permissions. - Query or mutation behavior, URL state, persistence, or one-shot signals. - Loading, error, empty, and recovery states that users can encounter. +- A hidden surface whose close-and-reopen behavior changes whether in-progress state resets or persists. - Accessibility-critical labels, keyboard flow, focus, disabled state, or overlay behavior. - A regression-prone business rule or bug fix that can be reproduced through a public boundary. diff --git a/.agents/skills/how-to-write-component/SKILL.md b/.agents/skills/how-to-write-component/SKILL.md index 2b0e50f538d..b6599743227 100644 --- a/.agents/skills/how-to-write-component/SKILL.md +++ b/.agents/skills/how-to-write-component/SKILL.md @@ -9,10 +9,11 @@ Use this skill to route component architecture decisions to its bundled referenc ## First Decisions -| Question | Default | Promote only when | +| Question | Default | Choose differently when | | --- | --- | --- | | Where should code live? | In the product workflow, route, or feature owner. | Several verticals need the same stable contract. | -| Who owns state and handlers? | The lowest visual owner that consumes them. | A parent coordinates one workflow or consistent snapshot. | +| Who owns state and handlers? | The lowest owner that consumes them and whose lifetime matches the state. | Another owner coordinates the value or it must survive the local owner's unmount. | +| Should React control a value? | Leave submit-only DOM fields uncontrolled. | The workflow must own the current value to drive rendering or coordination. | | Should state enter Jotai? | Keep component and form state local. | Siblings need one source of truth or scoped workflow persistence. | | Who owns URL state? | Next.js route APIs and `nuqs`. | Atoms require a read-only route-identity bridge. | | Who owns remote state? | TanStack Query at the lowest consumer. | Atom state drives the query or shared derivations consume it. | @@ -24,12 +25,12 @@ Use this skill to route component architecture decisions to its bundled referenc - Component moves, module boundaries, props, types, or owner placement: read [`references/ownership.md`][ownership]. - Jotai, form drafts, route identity, URL state, or persistence: read [`references/state.md`][state]. - Generated contracts, nullable API data, Query, mutations, SSR, auth, or workspace state: read [`references/data.md`][data]. -- Hotkeys, focus, dialogs, menus, popovers, or other secondary surfaces: read [`references/interactions.md`][interactions] and the overlay guide it references when applicable. +- Hotkeys, focus, dialogs, menus, popovers, or other secondary surfaces: read [`references/interactions.md`][interactions] and the overlay guide it references when applicable. Also read [`references/state.md`][state] when the surface owns a draft or other local session state. - Effects, navigation, memoization, preloading, or render cost: read [`references/runtime.md`][runtime]. ## Workflow -1. Identify the behavior owner and the public contract being changed. +1. Identify the behavior owner, the required state lifetime, and the public contract being changed. 2. Read the nearby implementation, tests, and only the routed skill references. 3. Implement one coherent vertical slice. Do not expand into equivalent patterns elsewhere unless the current contract cannot be completed without them. 4. Verify observable behavior at the narrowest sufficient boundary, then run the checks documented by the owning package: `web/docs/test.md` or `web/docs/lint.md` for Web, and `packages/dify-ui/docs/testing.md` for Dify UI. diff --git a/.agents/skills/how-to-write-component/references/interactions.md b/.agents/skills/how-to-write-component/references/interactions.md index 68233987b61..ba9315f981e 100644 --- a/.agents/skills/how-to-write-component/references/interactions.md +++ b/.agents/skills/how-to-write-component/references/interactions.md @@ -23,8 +23,10 @@ Read this document when a change involves application hotkeys, focus, dialogs, m - Follow the [overlay contract] for primitive choice and shared mechanics. The nearest consumer `AGENTS.md` owns application-specific composite reuse policy. - Separate behavior ownership from placement ownership: the action may own trigger, open state, and menu content while the caller owns slots, offsets, and alignment. - Keep menu and dialog surfaces as siblings when a menu command opens a dialog. Mount the dialog outside popup content. -- Mount controlled overlays unconditionally unless unmounting is required for performance or reset semantics. Prefer keyed or owner-local reset over conditional wrappers. -- Put query and mutation work inside dialog or alert-dialog content when it should mount only after opening. -- Prefer uncontrolled roots when the primitive can own open state. Use controlled state only for business coordination, analytics, cleanup, or explicit reset behavior. +- Keep overlay open-state ownership separate from content-session ownership. A controlled root does not require controlled fields or root-owned drafts. +- Match transient state to the primitive's content mount lifecycle. State below an unmounting content boundary gets a fresh instance after unmount; intentionally kept-mounted content needs an explicit persistence or reset policy. +- Keep a controlled overlay root at its coordination owner so the primitive can complete exit transitions, focus restoration, and detached-handle behavior. Do not conditionally remove the root to reset content state, and use keys only for stable semantic identity. +- Place query subscriptions and mutation observers at the owner whose lifetime matches when they should run. Mounted-session work may belong inside content; work that must start or stop exactly with `open` needs an explicit open-state condition. +- Prefer primitive-owned open state unless another owner must observe or coordinate it. Analytics callbacks and local cleanup alone do not require a controlled root. [overlay contract]: ../../../../packages/dify-ui/docs/overlays.md diff --git a/.agents/skills/how-to-write-component/references/ownership.md b/.agents/skills/how-to-write-component/references/ownership.md index d8da521aafc..886421ef394 100644 --- a/.agents/skills/how-to-write-component/references/ownership.md +++ b/.agents/skills/how-to-write-component/references/ownership.md @@ -12,8 +12,8 @@ Read this document when adding, moving, splitting, or refactoring React componen ## Component Ownership -- Put state, data access, loading, empty, error, and handlers in the lowest visual owner that uses them. -- Keep coordination in a parent only when it needs one consistent snapshot or coordinates submission, shared selection, batch behavior, navigation, or cross-section loading and errors. +- Put state, data access, loading, empty, error, and handlers in the lowest owner that uses them and whose mounted lifetime matches the required persistence. +- Keep coordination in a parent only when it needs one consistent snapshot, the value must intentionally survive the local owner's unmount, or the parent coordinates submission, shared selection, batch behavior, navigation, or cross-section loading and errors. - Repeated TanStack Query calls in siblings are acceptable when each sibling independently consumes the data; the cache already deduplicates requests. - Pass stable domain identity across boundaries. Do not pass raw server data together with separately derived flags for the same concept. - One pass-through prop layer is acceptable. Repeated forwarding means ownership should move closer to the consumer or into feature-scoped shared state. @@ -23,7 +23,8 @@ Read this document when adding, moving, splitting, or refactoring React componen ## Boundaries - State-heavy wizards, drawers, modals, and secondary workflows can form a small vertical surface with an entrypoint, optional feature-local state, and shallow owners matching real visual regions. -- The entrypoint owns route integration, provider wiring, close behavior, and mounting. Composition owners handle workflow branches; the closest visual owner handles section branches. +- The entrypoint owns route integration, provider wiring, placement, and open-state coordination. A content or session owner keeps state scoped to that mounted surface. +- Judge hook lifetime by the component that declares the hook and the primitive's mount contract, not only by where its rendered controls appear in JSX. - Separate hidden dialogs, dropdowns, and popovers into small local owners when their content obscures the parent flow. - Keep cohesive forms, menu bodies, and one-off helpers local unless they have their own state, reuse, or semantic boundary. - Avoid wrapper components and wrapper DOM that only rename props, pass children through, or hide the real primitive. A wrapper must own behavior, validation, state, accessibility, layout, or library integration. diff --git a/.agents/skills/how-to-write-component/references/runtime.md b/.agents/skills/how-to-write-component/references/runtime.md index 24554d895f5..42131944637 100644 --- a/.agents/skills/how-to-write-component/references/runtime.md +++ b/.agents/skills/how-to-write-component/references/runtime.md @@ -7,7 +7,7 @@ Read this document when a change introduces Effects, navigation side effects, me - Keep render pure: do not read or write `ref.current` during render except for predictable null-guarded lazy initialization. Update interaction-owned refs in event handlers, synchronize external-system refs after commit, and use state or derivation for rendered values. - Use Effects only to synchronize with a named external system such as a browser API, subscription, timer, analytics integration, non-React widget, or imperative DOM API. - Do not use Effects to transform render state, handle user actions, copy query data, reset state from props, or fetch data owned by framework APIs or TanStack Query. -- Initialize query-backed forms with keyed remounts or surface-entry hydration instead of copying data through Effects. +- Initialize query-backed form sessions after their defaults are available instead of copying data through Effects. Use a stable semantic identity key when the represented identity changes; use the intended surface lifecycle for per-session reset. ## Navigation diff --git a/.agents/skills/how-to-write-component/references/state.md b/.agents/skills/how-to-write-component/references/state.md index 0ede0a85e92..680d77fb8b9 100644 --- a/.agents/skills/how-to-write-component/references/state.md +++ b/.agents/skills/how-to-write-component/references/state.md @@ -9,10 +9,12 @@ Read this document when a change involves Jotai, form drafts, route identity, sh - Keep server and cache state in TanStack Query. Use existing feature stores for complex, high-frequency interaction state such as workflow canvas drag, resize, and runtime panels. - Use feature-owned storage only for low-frequency client preferences, dismissed notices, and UI defaults. Live application state does not belong in local storage. -## Forms +## Forms And Sessions -- Prefer uncontrolled Dify UI form and field controls when values are only read at submit time. Initialize query-backed defaults with `defaultValue` and keyed remounts. -- Promote form values to atoms only when another owner reacts to in-progress values, the draft must survive scoped unmounting, or several workflow steps edit the same draft. +- Keep form state in the narrowest owner whose lifetime matches the draft. A draft scoped to one mounted surface belongs to that content or session owner; a draft that must survive its current owner's unmount belongs to an explicit longer-lived feature owner. +- Prefer uncontrolled fields when values are only read at submit time. Use local controlled state only when React must own the current value to drive dependent UI or linked fields; track derived facts such as dirty state without mirroring the field value. Controlledness does not decide whether a draft is local or persisted. +- For query-backed defaults, establish the form session after the required defaults are available. `defaultValue` initializes the current mount; a stable semantic identity key may create a fresh snapshot when the represented identity changes. Do not use a generated key as a routine reset command. +- Promote drafts beyond the session only when another owner reacts to in-progress values, several workflow steps share one draft, or the draft must intentionally survive unmounting. Start with the lowest shared React owner; use feature-scoped atoms only when their coordination or persistence contract is needed. - Keep validation, source priority, fallback behavior, dirty checks, and payload assembly in the workflow that owns submission. ## Route And URL State diff --git a/packages/dify-ui/AGENTS.md b/packages/dify-ui/AGENTS.md index 5d02acdc1e0..2cd05f418f9 100644 --- a/packages/dify-ui/AGENTS.md +++ b/packages/dify-ui/AGENTS.md @@ -20,9 +20,9 @@ then read only the guide for the contract being changed. - Imports, exports, naming, public types, generics, and anatomy: [Public API authoring] - Button and icon-only action behavior: [Button contract] and [Icon Button contract] - Compound input behavior: [Input Group contract] -- Form structure and labels: [Forms] +- Form structure, labels, and value ownership: [Forms] - Picker choice and typed values: [Selection] -- Portals, layering, and floating-surface semantics: [Overlays] +- Portals, presence, layering, and floating-surface semantics: [Overlays] - Tailwind integration and radius mapping: [Styling] - Package test ownership and setup: [Testing and development] diff --git a/packages/dify-ui/README.md b/packages/dify-ui/README.md index 5825f310a82..622b4f556b4 100644 --- a/packages/dify-ui/README.md +++ b/packages/dify-ui/README.md @@ -70,9 +70,9 @@ Upstream behavior remains owned by the [Base UI documentation]. | Guide | Scope | | ------------------------- | ------------------------------------------------------------------------------ | -| [Forms] | Native submit boundaries, fields, labels, grouped controls, and errors. | +| [Forms] | Native submit boundaries, value ownership, fields, labels, and errors. | | [Selection] | Typed values and choosing among segmented controls, pickers, and radio groups. | -| [Overlays] | Portals, root isolation, layering, trigger composition, and semantics. | +| [Overlays] | Portals, presence lifecycles, layering, trigger composition, and semantics. | | [Styling] | Tailwind CSS integration and the Figma radius mapping. | | [Public API authoring] | Subpath exports, naming, public types, generics, and private helpers. | | [Testing and development] | Package commands, test ownership, accessibility, and animation setup. | diff --git a/packages/dify-ui/docs/forms.md b/packages/dify-ui/docs/forms.md index f3d3f21c1fe..862b207ed74 100644 --- a/packages/dify-ui/docs/forms.md +++ b/packages/dify-ui/docs/forms.md @@ -16,6 +16,22 @@ remains correct when another form library owns submission and validation; do not Set [`Button`] submit buttons to `type="submit"` explicitly. Keep every other button inside a form at `type="button"`. +## Value and state ownership + +`Form` owns the submission and validation boundary described above, not each field's draft. + +Choose controlledness from source-of-truth needs, independently from where a draft is stored. +Prefer `defaultValue` when application React code does not need to own the current value. Use +`value` and change handlers when application rendering or coordination must own it while editing. +Listening to change events, tracking dirty state, and native or primitive validation do not by +themselves require controlled state. + +Application code owns the draft in the narrowest component whose lifetime matches it. A value can +be controlled locally without being lifted. An uncontrolled field can participate in a persisted +workflow when that workflow captures its value at an explicit persistence boundary. The +surrounding surface defines its mount lifecycle; owner placement determines whether draft state +lives inside or outside that lifecycle. + ## Fields and labels Use `Field` when a control needs a shared name, label, validation, description, or error state. A @@ -61,9 +77,9 @@ option with `FieldItem` and give it its own label: Every radio belongs to a `RadioGroup`. Use `FieldsetLegend` to name the group and `FieldLabel` to name each option; do not render a standalone `Radio`. -Keep form state, schemas, server validation, and reset behavior outside these primitives. Pass -their observable state through the public field and control props instead of replacing the -semantic structure. +Keep form state, schemas, server validation, and reset behavior outside the primitive internals, +in the nearest application owner with the required lifetime. Pass observable state through the +public field and control props instead of replacing the semantic structure. [Base UI Slider anatomy]: https://base-ui.com/react/components/slider#anatomy [Base UI forms handbook]: https://base-ui.com/react/handbook/forms diff --git a/packages/dify-ui/docs/overlays.md b/packages/dify-ui/docs/overlays.md index 422ce4621f5..1274ce1b274 100644 --- a/packages/dify-ui/docs/overlays.md +++ b/packages/dify-ui/docs/overlays.md @@ -10,6 +10,25 @@ Floating surfaces render through [Base UI Portal] into `document.body`. Convenie such as `DialogContent`, `PopoverContent`, and `SelectContent` own their portals internally; primitives with explicit anatomy expose the constituent portal and content parts. +## Mounting and state lifetime + +An overlay root's React lifetime, its open state, and its portal subtree's presence are separate. +Presence is part of each primitive's contract. Convenience content follows its primitive's +default mount behavior; for example, `DialogContent` owns a [`Dialog.Portal`] whose subtree mounts +when the dialog opens and unmounts after any close transition completes. The `Dialog` root may +remain mounted and controlled independently of that content lifetime. Removing the controlled +root with the same condition that closes it bypasses the primitive's closing lifecycle. + +Application code can use an unmounting content subtree as the owner of state scoped to one mounted +content session. State that must survive the subtree's unmount belongs to an explicit longer-lived +feature owner. +Unmounting resets only DOM and component state owned inside that subtree; state declared by an +ancestor or external store survives. Portal placement alone is not a reset boundary. +Consumers using explicit anatomy may opt into `keepMounted` where that portal supports it; they +must then define which state persists and which state resets instead of relying on a remount. +Check the selected primitive's API rather than assuming every overlay portal has the same presence +options. + The host must establish an isolated stacking context at its application root: ```tsx @@ -58,6 +77,7 @@ spacing unless its API documents a measured exception. [Base UI Portal]: https://base-ui.com/react/overview/quick-start#portals [MDN `isolation`]: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/isolation +[`Dialog.Portal`]: https://base-ui.com/react/components/dialog#portal [`Popover`]: https://base-ui.com/react/components/popover [`PreviewCard`]: https://base-ui.com/react/components/preview-card [`Tooltip`]: https://base-ui.com/react/components/tooltip diff --git a/web/docs/test.md b/web/docs/test.md index df686e8e37b..7de60d55f15 100644 --- a/web/docs/test.md +++ b/web/docs/test.md @@ -59,6 +59,7 @@ Browser Mode remains a focused component or feature test and currently proves Ch - Drive state transitions through props, user interaction, URL changes, or public APIs. - Assert rendered UI, ARIA state, navigation, persistence, network-boundary calls, or another observable result. +- For a reset-or-persistence regression in a hidden surface, exercise the public transition: open, modify, close and wait for the surface to disappear, then reopen. Assert the intended behavior without coupling the test to hook placement, component names, keys, or private mount structure. - Do not inspect React state, refs, hook call order, effect dependencies, or private DOM structure. - Test referential identity only when identity is itself a documented public contract. - One test should describe one behavior. It may contain multiple assertions when they jointly prove that behavior. From ce801f444541e7f7cdcb0da506af9038af0d9695 Mon Sep 17 00:00:00 2001 From: Xiyuan Chen <52963600+GareArc@users.noreply.github.com> Date: Tue, 1 Sep 2026 01:10:03 +0000 Subject: [PATCH 03/33] fix(web): hide Open in Explore for SSO-restricted apps (#41518) Co-authored-by: yyh --- .../components/apps/__tests__/app-card.spec.tsx | 17 ++++++++++++++++- .../components/apps/app-card/interactions.tsx | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/web/app/components/apps/__tests__/app-card.spec.tsx b/web/app/components/apps/__tests__/app-card.spec.tsx index 46a56fb684e..adb020b86b7 100644 --- a/web/app/components/apps/__tests__/app-card.spec.tsx +++ b/web/app/components/apps/__tests__/app-card.spec.tsx @@ -1,5 +1,5 @@ import type { AppPartial } from '@dify/contracts/api/console/apps/types.gen' -import { fireEvent, screen, waitFor } from '@testing-library/react' +import { fireEvent, screen, waitFor, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' import * as React from 'react' import { STEP_BY_STEP_TOUR_TARGETS } from '@/app/components/step-by-step-tour/target-registry' @@ -1291,6 +1291,21 @@ describe('AppCard', () => { expect(screen.getByText('app.openInExplore')).toBeInTheDocument() }) }) + + it('should hide open in explore for SSO-restricted apps', async () => { + mockWebappAuthEnabled = true + const user = userEvent.setup() + const ssoApp = createMockApp({ access_mode: AccessMode.EXTERNAL_MEMBERS }) + + render() + + await user.click(getOperationsTrigger()) + const menu = await screen.findByRole('menu') + + expect( + within(menu).queryByRole('menuitem', { name: 'app.openInExplore' }), + ).not.toBeInTheDocument() + }) }) describe('Workflow Export with Environment Variables', () => { diff --git a/web/app/components/apps/app-card/interactions.tsx b/web/app/components/apps/app-card/interactions.tsx index 137e04c0bdb..0bdb03c2101 100644 --- a/web/app/components/apps/app-card/interactions.tsx +++ b/web/app/components/apps/app-card/interactions.tsx @@ -55,6 +55,7 @@ import { useProviderContext } from '@/context/provider-context' import { userProfileQueryOptions } from '@/features/account-profile/client' import { systemFeaturesQueryOptions } from '@/features/system-features/client' import { useAsyncWindowOpen } from '@/hooks/use-async-window-open' +import { AccessMode } from '@/models/access-control' import dynamic from '@/next/dynamic' import { useRouter } from '@/next/navigation' import { useGetUserCanAccessApp } from '@/service/access-control/use-app-access-control' @@ -136,6 +137,7 @@ function AppCardOperationsMenuItems({ const needsPublishBeforeExplore = requiresPublishedWorkflowInExplore(app) && !app.workflow?.id const shouldShowOpenInExploreOption = !app.has_draft_trigger && + app.access_mode !== AccessMode.EXTERNAL_MEMBERS && (needsPublishBeforeExplore || !systemFeatures.webapp_auth.enabled || (!isGettingUserCanAccessApp && Boolean(userCanAccessApp?.result))) From 72458d57501050267bb59bf90eeffb84f34cf8e0 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Tue, 1 Sep 2026 01:44:43 +0000 Subject: [PATCH 04/33] refactor(workflow): migrate schedule cron input (#41550) --- oxlint-suppressions.json | 7 +------ .../nodes/trigger-schedule/__tests__/panel.spec.tsx | 13 +++++++++++-- .../workflow/nodes/trigger-schedule/panel.tsx | 11 ++++++++--- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index 9cb1ae7a569..ca325a5858a 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -4349,11 +4349,6 @@ "count": 4 } }, - "web/app/components/workflow/nodes/trigger-schedule/panel.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx": { "no-restricted-imports": { "count": 1 @@ -5390,4 +5385,4 @@ "count": 2 } } -} \ No newline at end of file +} diff --git a/web/app/components/workflow/nodes/trigger-schedule/__tests__/panel.spec.tsx b/web/app/components/workflow/nodes/trigger-schedule/__tests__/panel.spec.tsx index edf8dbe62e5..a0c53bbdbfd 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/__tests__/panel.spec.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/__tests__/panel.spec.tsx @@ -204,7 +204,12 @@ describe('TriggerSchedulePanel', () => { renderPanel('node-3', createData({ mode: 'cron' })) - fireEvent.change(screen.getByDisplayValue('0 0 * * *'), { target: { value: '*/5 * * * *' } }) + fireEvent.change( + screen.getByRole('textbox', { + name: 'workflow.nodes.triggerSchedule.cronExpression', + }), + { target: { value: '*/5 * * * *' } }, + ) expect(handleCronExpressionChange).toHaveBeenCalledWith('*/5 * * * *') }) @@ -255,7 +260,11 @@ describe('TriggerSchedulePanel', () => { panelProps={panelProps} />, ) - expect(screen.getByRole('textbox')).toHaveValue('') + expect( + screen.getByRole('textbox', { + name: 'workflow.nodes.triggerSchedule.cronExpression', + }), + ).toHaveValue('') }) it('should render the hourly minute selector when the frequency is hourly', async () => { diff --git a/web/app/components/workflow/nodes/trigger-schedule/panel.tsx b/web/app/components/workflow/nodes/trigger-schedule/panel.tsx index e12b13815a5..838e4d740e5 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/panel.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/panel.tsx @@ -1,10 +1,10 @@ import type { FC } from 'react' import type { ScheduleTriggerNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' +import { Input } from '@langgenius/dify-ui/input' import * as React from 'react' import { useTranslation } from 'react-i18next' import TimePicker from '@/app/components/base/date-and-time-picker/time-picker' -import Input from '@/app/components/base/input' import Field from '@/app/components/workflow/nodes/_base/components/field' import FrequencySelector from './components/frequency-selector' import ModeToggle from './components/mode-toggle' @@ -18,6 +18,7 @@ const i18nPrefix = 'nodes.triggerSchedule' const Panel: FC> = ({ id, data }) => { const { t } = useTranslation() + const cronExpressionId = React.useId() const { inputs, setInputs, @@ -112,12 +113,16 @@ const Panel: FC> = ({ id, data }) => { {inputs.mode === 'cron' && (
-
-
+
+ setSubject(e.target.value)} + onValueChange={setSubject} placeholder={t( ($) => $[`${i18nPrefix}.deliveryMethod.emailConfigure.subjectPlaceholder`], { ns: 'workflow' }, From 8f2ff3a8fb8f8294bcc1f9f57ad27b9229fa1530 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Tue, 1 Sep 2026 01:44:44 +0000 Subject: [PATCH 06/33] refactor(plugins): migrate credential rename input (#41552) --- oxlint-suppressions.json | 3 --- .../plugin-auth/authorized/__tests__/item.spec.tsx | 11 +++++------ .../plugins/plugin-auth/authorized/item.tsx | 8 ++++---- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index 7ed1922796b..eb9226e0607 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -2626,9 +2626,6 @@ }, "jsx-a11y/no-static-element-interactions": { "count": 1 - }, - "no-restricted-imports": { - "count": 1 } }, "web/app/components/plugins/plugin-auth/hooks/use-get-api.ts": { diff --git a/web/app/components/plugins/plugin-auth/authorized/__tests__/item.spec.tsx b/web/app/components/plugins/plugin-auth/authorized/__tests__/item.spec.tsx index 36354d807ed..edc6ccfd410 100644 --- a/web/app/components/plugins/plugin-auth/authorized/__tests__/item.spec.tsx +++ b/web/app/components/plugins/plugin-auth/authorized/__tests__/item.spec.tsx @@ -184,8 +184,7 @@ describe('Item Component', () => { ) const enterRenameMode = () => { - const firstButton = result.container.querySelectorAll('button')[0] as HTMLElement - fireEvent.click(firstButton) + fireEvent.click(screen.getByRole('button', { name: 'common.operation.rename' })) } return { ...result, onRename, enterRenameMode } @@ -196,7 +195,7 @@ describe('Item Component', () => { enterRenameMode() - expect(screen.getByRole('textbox')).toBeInTheDocument() + expect(screen.getByRole('textbox', { name: 'common.operation.rename' })).toBeInTheDocument() }) it('should show save and cancel buttons in rename mode', () => { @@ -213,7 +212,7 @@ describe('Item Component', () => { enterRenameMode() - const input = screen.getByRole('textbox') + const input = screen.getByRole('textbox', { name: 'common.operation.rename' }) fireEvent.change(input, { target: { value: 'New Name' } }) fireEvent.click(screen.getByText('common.operation.save')) @@ -228,7 +227,7 @@ describe('Item Component', () => { const { enterRenameMode } = renderWithRenameEnabled() enterRenameMode() - expect(screen.getByRole('textbox')).toBeInTheDocument() + expect(screen.getByRole('textbox', { name: 'common.operation.rename' })).toBeInTheDocument() fireEvent.click(screen.getByText('common.operation.cancel')) @@ -240,7 +239,7 @@ describe('Item Component', () => { enterRenameMode() - const input = screen.getByRole('textbox') + const input = screen.getByRole('textbox', { name: 'common.operation.rename' }) fireEvent.change(input, { target: { value: 'Updated Value' } }) expect(input).toHaveValue('Updated Value') diff --git a/web/app/components/plugins/plugin-auth/authorized/item.tsx b/web/app/components/plugins/plugin-auth/authorized/item.tsx index 76da4b84066..7bef9a46149 100644 --- a/web/app/components/plugins/plugin-auth/authorized/item.tsx +++ b/web/app/components/plugins/plugin-auth/authorized/item.tsx @@ -2,6 +2,7 @@ import type { Credential } from '../types' import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { IconButton } from '@langgenius/dify-ui/icon-button' +import { Input } from '@langgenius/dify-ui/input' import { StatusDot } from '@langgenius/dify-ui/status-dot' import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { RiInformationLine } from '@remixicon/react' @@ -9,7 +10,6 @@ import { useSuspenseQuery } from '@tanstack/react-query' import { memo, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge' -import Input from '@/app/components/base/input' import { userProfileQueryOptions } from '@/features/account-profile/client' import { useCredentialPermissions } from '@/hooks/use-credential-permissions' import { CredentialTypeEnum } from '../types' @@ -85,10 +85,10 @@ const Item = ({ {renaming && (
$['operation.rename'], { ns: 'common' })} + className="h-6 grow" value={renameValue} - onChange={(e) => setRenameValue(e.target.value)} + onValueChange={setRenameValue} placeholder={t(($) => $['placeholder.input'], { ns: 'common' })} onClick={(e) => e.stopPropagation()} /> From 62221bf8b8081eada18668a44e8b5e7057985434 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Tue, 1 Sep 2026 02:36:21 +0000 Subject: [PATCH 07/33] refactor(web): align skills card semantics (#41559) --- web/features/skills/__tests__/page.spec.tsx | 310 +++++++++++- web/features/skills/page.tsx | 508 +++++++++++++------- 2 files changed, 629 insertions(+), 189 deletions(-) diff --git a/web/features/skills/__tests__/page.spec.tsx b/web/features/skills/__tests__/page.spec.tsx index 12c1071a752..89378fad9ac 100644 --- a/web/features/skills/__tests__/page.spec.tsx +++ b/web/features/skills/__tests__/page.spec.tsx @@ -237,14 +237,13 @@ function createAgentReference( } } -function renderSkillsPage() { - const queryClient = new QueryClient({ - defaultOptions: { - mutations: { retry: false }, - queries: { retry: false }, - }, +function createTestQueryClient() { + return new QueryClient({ + defaultOptions: { mutations: { retry: false }, queries: { retry: false } }, }) +} +function renderSkillsPage(queryClient = createTestQueryClient()) { return render( @@ -310,12 +309,97 @@ describe('SkillsPage', () => { }) }) + it('exposes loading as a status without presenting skeletons as skill items', () => { + mocks.skillsQueryOptions.mockImplementation((options) => ({ + queryKey: ['skills-pending', options], + queryFn: () => new Promise(() => {}), + getNextPageParam: options.getNextPageParam, + initialPageParam: options.initialPageParam, + })) + + renderSkillsPage() + + const skillRegion = screen.getByRole('region', { + name: 'skill.skillManagement.listLabel', + }) + expect(skillRegion).toHaveAttribute('aria-busy', 'true') + expect(within(skillRegion).getByRole('status')).toHaveTextContent('common.loading') + expect(within(skillRegion).queryByRole('list')).not.toBeInTheDocument() + expect(within(skillRegion).queryByRole('listitem')).not.toBeInTheDocument() + }) + + it('exposes a failed skill request as an alert without a stale list', async () => { + mocks.skillsQueryOptions.mockImplementation((options) => ({ + queryKey: ['skills-error', options], + queryFn: () => Promise.reject(new Error('skills request failed')), + getNextPageParam: options.getNextPageParam, + initialPageParam: options.initialPageParam, + })) + + renderSkillsPage() + + const skillRegion = screen.getByRole('region', { + name: 'skill.skillManagement.listLabel', + }) + expect(await within(skillRegion).findByRole('alert')).toHaveTextContent( + 'skill.skillManagement.loadingError', + ) + expect( + within(skillRegion).getByRole('button', { name: 'common.operation.retry' }), + ).toBeInTheDocument() + expect(within(skillRegion).queryByRole('list')).not.toBeInTheDocument() + }) + + it('replaces a cached empty state with a retryable error when refetch fails', async () => { + const user = userEvent.setup() + const queryKey = ['skills-cached-empty-refetch-error'] + let requestCount = 0 + mocks.skillsQueryOptions.mockImplementation((options) => ({ + queryKey, + queryFn: () => { + requestCount += 1 + return Promise.reject(new Error('skills refetch failed')) + }, + getNextPageParam: options.getNextPageParam, + initialPageParam: options.initialPageParam, + })) + const queryClient = createTestQueryClient() + queryClient.setQueryData(queryKey, { + pages: [{ data: [], has_more: false, page: 1, total: 0 }], + pageParams: [1], + }) + + renderSkillsPage(queryClient) + + const skillRegion = screen.getByRole('region', { + name: 'skill.skillManagement.listLabel', + }) + const error = await within(skillRegion).findByRole('alert') + expect(error).toHaveTextContent('skill.skillManagement.loadingError') + expect(within(skillRegion).queryByText('skill.skillManagement.empty')).not.toBeInTheDocument() + expect( + within(skillRegion).queryByRole('button', { + name: 'skill.skillManagement.emptyAction.createTitle', + }), + ).not.toBeInTheDocument() + expect(within(skillRegion).queryByRole('list')).not.toBeInTheDocument() + + await user.click(within(error).getByRole('button', { name: 'common.operation.retry' })) + await waitFor(() => { + expect(requestCount).toBe(2) + }) + }) + it('renders skills with tags, reference count, and detail links', async () => { renderSkillsPage() const skillLink = await screen.findByRole('link', { name: /Refund approval/ }) - expect(screen.getByRole('article', { name: 'Refund approval' })).toBeInTheDocument() + const skillList = screen.getByRole('list') + expect(screen.getByRole('listitem', { name: 'Refund approval' })).toBeInTheDocument() + expect(skillList).toContainElement(skillLink) expect(skillLink).toHaveAttribute('href', '/skills/skill-1') + expect(skillLink).toHaveAccessibleDescription('Handle refund requests.') + expect(within(skillLink).queryByRole('button')).not.toBeInTheDocument() expect(screen.getByText('refund-approval')).toBeInTheDocument() expect(screen.getByText('Handle refund requests.')).toBeInTheDocument() expect(screen.getByText('support')).toBeInTheDocument() @@ -350,8 +434,12 @@ describe('SkillsPage', () => { renderSkillsPage() + const skillLink = await screen.findByRole('link', { name: 'Refund approval' }) + expect(skillLink).toHaveAccessibleDescription( + 'skill.skillManagement.draft Handle refund requests.', + ) expect( - await screen.findByText('skill.skillManagement.editedAt:{"time":"2 hours ago"}'), + screen.getByText('skill.skillManagement.editedAt:{"time":"2 hours ago"}'), ).toBeInTheDocument() }) @@ -424,8 +512,28 @@ describe('SkillsPage', () => { name: 'skill-21', display_name: 'Skill 21', }) - mocks.skills = firstPageSkills - mocks.skillPages = [firstPageSkills, [nextPageSkill]] + let resolveNextPage: + | ((page: { data: SkillResponse[]; has_more: boolean; page: number; total: number }) => void) + | undefined + mocks.skillsQueryOptions.mockImplementation((options) => ({ + queryKey: ['skills-deferred-next-page', options], + queryFn: async ({ pageParam }: { pageParam: unknown }) => { + if (Number(pageParam) === 1) { + return { + data: firstPageSkills, + has_more: true, + page: 1, + total: 21, + } + } + + return new Promise((resolve) => { + resolveNextPage = resolve + }) + }, + getNextPageParam: options.getNextPageParam, + initialPageParam: options.initialPageParam, + })) renderSkillsPage() @@ -433,7 +541,7 @@ describe('SkillsPage', () => { name: 'skill.skillManagement.listLabel', }) await screen.findByRole('heading', { name: 'Skill 1' }) - expect(within(skillList).getAllByRole('article')).toHaveLength(20) + expect(within(skillList).getAllByRole('listitem')).toHaveLength(20) const scrollViewport = skillList.parentElement?.parentElement expect(scrollViewport).not.toBeNull() @@ -444,8 +552,19 @@ describe('SkillsPage', () => { }) fireEvent.scroll(scrollViewport!) + const paginationLoading = await within(skillList).findByRole('status') + expect(paginationLoading).toHaveTextContent('common.loading') + expect(paginationLoading).toBeVisible() + expect(within(skillList).getAllByRole('listitem')).toHaveLength(20) + + resolveNextPage?.({ + data: [nextPageSkill], + has_more: false, + page: 2, + total: 21, + }) expect(await screen.findByRole('heading', { name: 'Skill 21' })).toBeInTheDocument() - expect(within(skillList).getAllByRole('article')).toHaveLength(21) + expect(within(skillList).getAllByRole('listitem')).toHaveLength(21) expect(mocks.skillsQueryOptions.mock.lastCall?.[0].input(2)).toEqual({ query: { limit: 20, @@ -454,6 +573,165 @@ describe('SkillsPage', () => { }) }) + it('waits for a cached first page to refetch before auto-loading the next page', async () => { + const queryKey = ['skills-cached-refetch'] + const staleFirstPage = Array.from({ length: 20 }, (_, index) => + createSkill({ + id: `stale-skill-${index + 1}`, + name: `stale-skill-${index + 1}`, + display_name: `Stale Skill ${index + 1}`, + }), + ) + const freshFirstPage = staleFirstPage.map((skill, index) => + createSkill({ + ...skill, + display_name: `Fresh Skill ${index + 1}`, + }), + ) + let resolveFirstPageRefetch: + | ((page: { data: SkillResponse[]; has_more: boolean; page: number; total: number }) => void) + | undefined + let nextPageRequestCount = 0 + mocks.skillsQueryOptions.mockImplementation((options) => ({ + queryKey, + queryFn: ({ pageParam }: { pageParam: unknown }) => { + if (Number(pageParam) === 1) { + return new Promise((resolve) => { + resolveFirstPageRefetch = resolve + }) + } + + nextPageRequestCount += 1 + return Promise.resolve({ + data: [createSkill({ id: 'skill-21', name: 'skill-21', display_name: 'Fresh Skill 21' })], + has_more: false, + page: 2, + total: 21, + }) + }, + getNextPageParam: options.getNextPageParam, + initialPageParam: options.initialPageParam, + })) + const queryClient = createTestQueryClient() + queryClient.setQueryData(queryKey, { + pages: [ + { + data: staleFirstPage, + has_more: true, + page: 1, + total: 21, + }, + ], + pageParams: [1], + }) + + renderSkillsPage(queryClient) + + const skillRegion = screen.getByRole('region', { + name: 'skill.skillManagement.listLabel', + }) + expect(await screen.findByRole('heading', { name: 'Stale Skill 1' })).toBeInTheDocument() + const scrollViewport = skillRegion.parentElement?.parentElement + expect(scrollViewport).not.toBeNull() + Object.defineProperties(scrollViewport!, { + clientHeight: { configurable: true, value: 600 }, + scrollHeight: { configurable: true, value: 1200 }, + scrollTop: { configurable: true, value: 560 }, + }) + fireEvent.scroll(scrollViewport!) + + expect(nextPageRequestCount).toBe(0) + expect(screen.getByRole('heading', { name: 'Stale Skill 1' })).toBeInTheDocument() + + resolveFirstPageRefetch?.({ + data: freshFirstPage, + has_more: true, + page: 1, + total: 21, + }) + expect(await screen.findByRole('heading', { name: 'Fresh Skill 1' })).toBeInTheDocument() + expect(await screen.findByRole('heading', { name: 'Fresh Skill 21' })).toBeInTheDocument() + expect(nextPageRequestCount).toBe(1) + }) + + it('keeps loaded skills visible when the next page fails and exposes retry', async () => { + const user = userEvent.setup() + const firstPageSkills = Array.from({ length: 20 }, (_, index) => + createSkill({ + id: `skill-${index + 1}`, + name: `skill-${index + 1}`, + display_name: `Skill ${index + 1}`, + }), + ) + let nextPageRequestCount = 0 + let resolveRetry: + | ((page: { data: SkillResponse[]; has_more: boolean; page: number; total: number }) => void) + | undefined + mocks.skillsQueryOptions.mockImplementation((options) => ({ + queryKey: ['skills-next-page-error', options], + queryFn: async ({ pageParam }: { pageParam: unknown }) => { + if (Number(pageParam) === 1) { + return { + data: firstPageSkills, + has_more: true, + page: 1, + total: 21, + } + } + + nextPageRequestCount += 1 + if (nextPageRequestCount === 1) throw new Error('next skill page failed') + + return new Promise((resolve) => { + resolveRetry = resolve + }) + }, + getNextPageParam: options.getNextPageParam, + initialPageParam: options.initialPageParam, + })) + + renderSkillsPage() + + const skillRegion = await screen.findByRole('region', { + name: 'skill.skillManagement.listLabel', + }) + await screen.findByRole('heading', { name: 'Skill 1' }) + const scrollViewport = skillRegion.parentElement?.parentElement + expect(scrollViewport).not.toBeNull() + Object.defineProperties(scrollViewport!, { + clientHeight: { configurable: true, value: 600 }, + scrollHeight: { configurable: true, value: 600 }, + scrollTop: { configurable: true, value: 0 }, + }) + fireEvent.scroll(scrollViewport!) + + const paginationError = await within(skillRegion).findByRole('alert') + expect(within(skillRegion).getAllByRole('listitem')).toHaveLength(20) + expect(paginationError).toHaveTextContent('skill.skillManagement.loadingError') + expect(nextPageRequestCount).toBe(1) + + const retryButton = within(paginationError).getByRole('button', { + name: 'common.operation.retry', + }) + await user.click(retryButton) + await waitFor(() => { + expect(nextPageRequestCount).toBe(2) + }) + expect(retryButton).toHaveAttribute('aria-disabled', 'true') + expect(retryButton).toHaveFocus() + expect(within(skillRegion).queryByRole('status')).not.toBeInTheDocument() + expect(within(skillRegion).getAllByRole('listitem')).toHaveLength(20) + + resolveRetry?.({ + data: [createSkill({ id: 'skill-21', name: 'skill-21', display_name: 'Recovered Skill 21' })], + has_more: false, + page: 2, + total: 21, + }) + expect(await screen.findByRole('heading', { name: 'Recovered Skill 21' })).toBeInTheDocument() + expect(within(skillRegion).getAllByRole('listitem')).toHaveLength(21) + }) + it('creates a placeholder skill and navigates to its detail page', async () => { const user = userEvent.setup() const invalidateQueries = vi.spyOn(QueryClient.prototype, 'invalidateQueries') @@ -825,7 +1103,13 @@ describe('SkillsPage', () => { renderSkillsPage() - expect(await screen.findByText('skill.skillManagement.emptySearch')).toBeInTheDocument() + const skillRegion = screen.getByRole('region', { + name: 'skill.skillManagement.listLabel', + }) + const emptySearchTitle = await within(skillRegion).findByText( + 'skill.skillManagement.emptySearch', + ) + expect(emptySearchTitle.closest('[role="status"]')).toBeInTheDocument() expect( screen.queryByText('skill.skillManagement.emptyAction.createTitle'), ).not.toBeInTheDocument() diff --git a/web/features/skills/page.tsx b/web/features/skills/page.tsx index 6c3ba9a71a9..3cc34a36e00 100644 --- a/web/features/skills/page.tsx +++ b/web/features/skills/page.tsx @@ -22,6 +22,7 @@ import { DropdownMenuTrigger, } from '@langgenius/dify-ui/dropdown-menu' import { Field, FieldLabel } from '@langgenius/dify-ui/field' +import { IconButton } from '@langgenius/dify-ui/icon-button' import { InputGroup, InputGroupAddon, InputGroupInput } from '@langgenius/dify-ui/input-group' import { ScrollArea, @@ -38,6 +39,7 @@ import { useEffect, useId, useMemo, useRef, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import { SearchInput } from '@/app/components/base/search-input' import { SkeletonRectangle } from '@/app/components/base/skeleton' +import { MAIN_NAV_APP_CARD_GRID_CLASS_NAME } from '@/app/components/main-nav/app-card-grid' import { SkillCardTags } from '@/features/tag-management/components/skill-card-tags' import { TagFilter } from '@/features/tag-management/components/tag-filter' import useDocumentTitle from '@/hooks/use-document-title' @@ -59,6 +61,7 @@ const placeholderCardIds = Array.from( ) const skeletonRows = ['primary', 'secondary', 'tertiary'] as const const SKILLS_PAGE_SIZE = 20 +const SKILL_GRID_CLASS_NAME = cn('gap-2.5', MAIN_NAV_APP_CARD_GRID_CLASS_NAME) function skillsListQueryKey(type: 'infinite' | 'query') { return consoleQuery.workspaces.current.skills.get.key({ type }) @@ -80,7 +83,7 @@ function SkillIcon() { ) } -function SkillCardSkeleton() { +function SkillCardSkeletonCards() { return ( <> {skeletonRows.map((row) => ( @@ -109,38 +112,60 @@ function SkillCardSkeleton() { ) } -function SkillPlaceholderState({ - canEdit, - creating, - importing, - isEmptySearch, - onCreate, - onImport, - title, -}: { - canEdit?: boolean - creating?: boolean - importing?: boolean - isEmptySearch?: boolean - onCreate?: () => void - onImport?: () => void +function SkillCardSkeleton() { + const { t } = useTranslation('common') + + return ( + <> + + {t(($) => $.loading)} + + + + ) +} + +type SkillPlaceholderActions = { + creating: boolean + importing: boolean + onCreate: () => void + onImport: () => void +} + +type SkillPlaceholderStateProps = { + role?: 'alert' | 'status' title: string -}) { +} & ( + | { actions?: SkillPlaceholderActions; isRetrying?: never; onRetry?: never } + | { actions?: never; isRetrying: boolean; onRetry: () => void } +) + +function SkillPlaceholderState({ + actions, + isRetrying, + onRetry, + role, + title, +}: SkillPlaceholderStateProps) { const { t } = useTranslation('skill') + const { t: tCommon } = useTranslation('common') return (
-
+
{placeholderCardIds.map((id) => (
))}
-
+
@@ -156,21 +181,26 @@ function SkillPlaceholderState({ > {title} + {onRetry && ( + + )}
- {!isEmptySearch && canEdit !== false && ( + {actions && (
+
+ ) +} + +function SkillGridPagination({ + state, +}: { + state: Extract< + Extract['content'], + { kind: 'list' } + >['pagination'] +}) { + const { t } = useTranslation('common') + + if (state.status === 'none') return null + if (state.status === 'error') return + + return ( +
+ {t(($) => $.loading)} + +
+ ) +} + +function SkillGrid({ state }: SkillGridProps) { + const { t } = useTranslation('skill') + const isBusy = + state.status === 'pending' || + (state.status === 'error' && state.isRetrying) || + (state.status === 'ready' && state.isFetching) + const readyContent = state.status === 'ready' ? state.content : undefined + + return ( +
$['skillManagement.listLabel'])} aria-busy={isBusy}> + {state.status === 'pending' && ( +
+ +
)} - {!isPending && !isError && skills.length === 0 && ( + {state.status === 'error' && ( $['skillManagement.loadingError'])} + /> + )} + {readyContent?.kind === 'empty' && ( + $['skillManagement.emptySearch']) : t(($) => $['skillManagement.empty']) } /> )} - {!isPending && - !isError && - skills.map((skill) => ( - - ))} - {!isPending && !isError && isFetchingNextPage && } + {readyContent?.kind === 'list' && ( + <> + {readyContent.refresh.status === 'error' && ( + + )} + {/* Safari list semantics: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/list-style#accessibility */} + {/* oxlint-disable-next-line jsx-a11y/no-redundant-roles -- Dify's preflight removes list markers. */} +
    + {readyContent.skills.map((skill) => ( + + ))} +
+ + + )}
) } @@ -864,16 +959,91 @@ export default function SkillsPage() { const handleListScroll = (event: UIEvent) => { const target = event.currentTarget const scrollBottom = target.scrollHeight - target.scrollTop - target.clientHeight - if (scrollBottom < 80 && hasNextPage && !isFetchingNextPage) void fetchNextPage() + if ( + scrollBottom < 80 && + hasNextPage && + !skillsQuery.isFetching && + !skillsQuery.isFetchNextPageError + ) + void fetchNextPage() } useEffect(() => { const viewport = listViewportRef.current - if (!viewport || viewport.clientHeight === 0 || isPending || isFetchingNextPage || !hasNextPage) + if ( + !viewport || + viewport.clientHeight === 0 || + isPending || + skillsQuery.isFetching || + skillsQuery.isFetchNextPageError || + !hasNextPage + ) return - if (viewport.scrollHeight - viewport.clientHeight < 80) void fetchNextPage() - }, [fetchNextPage, hasNextPage, isFetchingNextPage, isPending, skills.length]) + if (viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight < 80) + void fetchNextPage() + }, [ + fetchNextPage, + hasNextPage, + isPending, + skills.length, + skillsQuery.isFetching, + skillsQuery.isFetchNextPageError, + ]) + + const isFiltered = !!debouncedKeyword || selectedTags.length > 0 + const skillGridState: SkillGridState = isPending + ? { status: 'pending' } + : skillsQuery.isLoadingError || (skills.length === 0 && skillsQuery.isRefetchError) + ? { + status: 'error', + isRetrying: skillsQuery.isFetching, + onRetry: () => void skillsQuery.refetch(), + } + : { + status: 'ready', + content: + skills.length === 0 + ? { + kind: 'empty', + emptyState: isFiltered ? 'filtered' : 'skills', + actions: + canEdit && !isFiltered + ? { + creating: createMutation.isPending, + importing: importMutation.isPending, + onCreate: handleCreate, + onImport: () => importInputRef.current?.click(), + } + : undefined, + } + : { + kind: 'list', + skills, + pagination: skillsQuery.isFetchNextPageError + ? { + status: 'error', + isRetrying: isFetchingNextPage, + onRetry: () => void fetchNextPage(), + } + : isFetchingNextPage + ? { status: 'loading' } + : { status: 'none' }, + refresh: skillsQuery.isRefetchError + ? { + status: 'error', + isRetrying: skillsQuery.isFetching, + onRetry: () => void skillsQuery.refetch(), + } + : { status: 'none' }, + cardActions: { + canDelete, + canEdit, + onOpenTagManagement: () => setShowTagManagementModal(true), + }, + }, + isFetching: skillsQuery.isFetching, + } return (
@@ -911,21 +1081,7 @@ export default function SkillsPage() { onScroll={handleListScroll} > - 0} - isError={skillsQuery.isError} - isFetching={skillsQuery.isFetching} - isFetchingNextPage={skillsQuery.isFetchingNextPage} - isPending={skillsQuery.isPending} - onCreate={handleCreate} - onImport={() => importInputRef.current?.click()} - onOpenTagManagement={() => setShowTagManagementModal(true)} - /> + From 765e6338d21167528c835915022ab64d1bf27e5f Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Tue, 1 Sep 2026 03:34:23 +0000 Subject: [PATCH 08/33] fix(plugin): prevent install dialog error overflow (#41561) --- .../plugins/install-plugin/base/installed.tsx | 11 ++++++++--- .../install-plugin/install-from-github/index.tsx | 10 ++++++---- .../__tests__/index.spec.tsx | 3 +++ .../install-from-local-package/index.tsx | 12 +++++++----- .../install-from-marketplace/index.tsx | 6 +++--- 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/web/app/components/plugins/install-plugin/base/installed.tsx b/web/app/components/plugins/install-plugin/base/installed.tsx index 1da5b0b1e0b..34f42fcf6db 100644 --- a/web/app/components/plugins/install-plugin/base/installed.tsx +++ b/web/app/components/plugins/install-plugin/base/installed.tsx @@ -80,8 +80,13 @@ const Installed: FC = ({ } return ( <> -
-

+

+

{isFailed && errMsg ? ( errMsg ) : categoryTarget ? ( @@ -125,7 +130,7 @@ const Installed: FC = ({ )}

{/* Action Buttons */} -
+
{categoryTarget ? ( = ({ @@ -201,7 +201,9 @@ const InstallFromGitHub: React.FC = ({
-
{getTitle()}
+ + {getTitle()} +
{![ InstallStepFromGitHub.uploadFailed, diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/__tests__/index.spec.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/__tests__/index.spec.tsx index 7fb5d9c3239..ababf3dfb69 100644 --- a/web/app/components/plugins/install-plugin/install-from-local-package/__tests__/index.spec.tsx +++ b/web/app/components/plugins/install-plugin/install-from-local-package/__tests__/index.spec.tsx @@ -419,6 +419,9 @@ describe('InstallFromLocalPackage', () => { await waitFor(() => { expect(screen.getByTestId('ready-to-install-package')).toBeInTheDocument() expect(screen.getByTestId('package-step')).toHaveTextContent('uploadFailed') + expect( + screen.getByRole('dialog', { name: 'plugin.installModal.uploadFailed' }), + ).toBeInTheDocument() }) }) diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx index f669cc7bfa9..825484b0d4b 100644 --- a/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx +++ b/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { Dependency, PluginCategoryEnum, PluginDeclaration } from '../../types' import { cn } from '@langgenius/dify-ui/cn' -import { Dialog, DialogClose, DialogContent } from '@langgenius/dify-ui/dialog' +import { Dialog, DialogClose, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog' import { IconButton } from '@langgenius/dify-ui/icon-button' import * as React from 'react' import { useCallback, useState } from 'react' @@ -90,10 +90,10 @@ const InstallFromLocalPackage: React.FC = ({ @@ -109,8 +109,10 @@ const InstallFromLocalPackage: React.FC = ({ } /> -
-
{getTitle()}
+
+ + {getTitle()} +
{step === InstallStep.uploading && ( = ({ -
+
{getTitle()} From 5cbc42cd389b97abd1e714434e6a52e5dc9288f8 Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 1 Sep 2026 03:46:03 +0000 Subject: [PATCH 09/33] chore: Revert "feat: use app.acl.access_point_manage to control access point page access" (#41563) --- api/controllers/console/agent/roster.py | 12 -- api/services/enterprise/rbac_service.py | 3 - .../console/agent/test_agent_controllers.py | 109 +----------------- .../[appId]/__tests__/layout-main.spec.tsx | 48 +------- .../(appDetailLayout)/[appId]/layout-main.tsx | 47 ++++---- .../__tests__/app-detail-section.spec.tsx | 18 +-- .../app-sidebar/app-detail-section.tsx | 16 +-- .../environment-deployment-flow.spec.tsx | 2 - .../app-publisher/__tests__/sections.spec.tsx | 41 ------- .../built-in-publisher/actions-section.tsx | 20 ++-- .../actions-section.tsx | 20 ++-- .../environment-deployment-flow/index.tsx | 3 - .../components/app/app-publisher/index.tsx | 8 +- .../app-publisher/publisher-content/index.tsx | 4 - .../app/deploy/__tests__/index.spec.tsx | 16 +-- .../built-in-environment-card/index.tsx | 6 +- .../app/deploy/environment-table/index.tsx | 3 - .../app/deploy/environment-table/row.tsx | 8 +- web/app/components/app/deploy/index.tsx | 13 +-- .../__tests__/access-point-icon.spec.tsx | 35 ------ .../app/deploy/shared/access-point-icon.tsx | 8 +- .../__tests__/index.spec.tsx | 1 - .../continue-work/__tests__/item.spec.tsx | 13 +-- web/i18n/ar-TN/permission-keys.json | 1 - web/i18n/de-DE/permission-keys.json | 1 - web/i18n/en-US/permission-keys.json | 1 - web/i18n/es-ES/permission-keys.json | 1 - web/i18n/fa-IR/permission-keys.json | 1 - web/i18n/fr-FR/permission-keys.json | 1 - web/i18n/hi-IN/permission-keys.json | 1 - web/i18n/id-ID/permission-keys.json | 1 - web/i18n/it-IT/permission-keys.json | 1 - web/i18n/ja-JP/permission-keys.json | 1 - web/i18n/ko-KR/permission-keys.json | 1 - web/i18n/lo-LA/permission-keys.json | 1 - web/i18n/nl-NL/permission-keys.json | 1 - web/i18n/pl-PL/permission-keys.json | 1 - web/i18n/pt-BR/permission-keys.json | 1 - web/i18n/ro-RO/permission-keys.json | 1 - web/i18n/ru-RU/permission-keys.json | 1 - web/i18n/sl-SI/permission-keys.json | 1 - web/i18n/th-TH/permission-keys.json | 1 - web/i18n/tr-TR/permission-keys.json | 1 - web/i18n/uk-UA/permission-keys.json | 1 - web/i18n/vi-VN/permission-keys.json | 1 - web/i18n/zh-Hans/permission-keys.json | 1 - web/i18n/zh-Hant/permission-keys.json | 1 - web/utils/app-redirection.spec.ts | 30 +---- web/utils/app-redirection.ts | 4 +- web/utils/permission.spec.ts | 10 -- web/utils/permission.ts | 7 -- 51 files changed, 79 insertions(+), 450 deletions(-) delete mode 100644 web/app/components/app/deploy/shared/__tests__/access-point-icon.spec.tsx diff --git a/api/controllers/console/agent/roster.py b/api/controllers/console/agent/roster.py index 730dd7b1a57..f95f9d8b00f 100644 --- a/api/controllers/console/agent/roster.py +++ b/api/controllers/console/agent/roster.py @@ -7,7 +7,6 @@ from pydantic import AliasChoices, BaseModel, Field, field_validator from sqlalchemy import func, or_, select from sqlalchemy.orm import Session -from configs import dify_config from controllers.common.schema import ( query_params_from_model, query_params_from_request, @@ -79,11 +78,9 @@ from services.agent.observability_service import ( ) from services.agent.roster_service import AgentRosterService from services.app_service import AgentAppPublicationCounts, AppListParams, AppService, CreateAppParams -from services.enterprise import rbac_service as enterprise_rbac_service from services.enterprise.enterprise_service import EnterpriseService from services.entities.agent_entities import ComposerSavePayload, RosterListQuery from services.feature_service import FeatureService -from tasks.initialize_created_app_rbac_access_task import initialize_created_app_rbac_access_task AgentPublicationStatus = Literal["published", "drafts"] @@ -687,15 +684,6 @@ class AgentAppListApi(Resource): ) app = AppService().create_app(current_tenant_id, params, current_user, session=session) - if dify_config.RBAC_ENABLED: - enterprise_rbac_service.RBACService.AppAccess.replace_whitelist( - current_tenant_id, - current_user.id, - str(app.id), - enterprise_rbac_service.ReplaceMemberBindings(automatic_include_workspace_members=True), - ) - initialize_created_app_rbac_access_task.delay(current_tenant_id, current_user.id, app_id=app.id) - return _serialize_agent_app_detail(session, app, current_user=current_user), 201 diff --git a/api/services/enterprise/rbac_service.py b/api/services/enterprise/rbac_service.py index bf95b8650b1..fd0acf280b2 100644 --- a/api/services/enterprise/rbac_service.py +++ b/api/services/enterprise/rbac_service.py @@ -477,7 +477,6 @@ _LEGACY_WORKSPACE_DATASET_OPERATOR_KEYS: list[str] = [ _LEGACY_APP_OWNER_KEYS: list[str] = [ "app.acl.preview", - "app.acl.access_point_manage", "app.acl.view_layout", "app.acl.test_and_run", "app.acl.edit", @@ -493,7 +492,6 @@ _LEGACY_APP_OWNER_KEYS: list[str] = [ _LEGACY_APP_ADMIN_KEYS: list[str] = [ "app.acl.preview", "app.acl.view_layout", - "app.acl.access_point_manage", "app.acl.test_and_run", "app.acl.edit", "app.acl.import_export_dsl", @@ -508,7 +506,6 @@ _LEGACY_APP_ADMIN_KEYS: list[str] = [ _LEGACY_APP_EDITOR_KEYS: list[str] = [ "app.acl.preview", - "app.acl.access_point_manage", "app.acl.view_layout", "app.acl.test_and_run", "app.acl.edit", diff --git a/api/tests/unit_tests/controllers/console/agent/test_agent_controllers.py b/api/tests/unit_tests/controllers/console/agent/test_agent_controllers.py index 27a543fd2a2..c2b43d8cb22 100644 --- a/api/tests/unit_tests/controllers/console/agent/test_agent_controllers.py +++ b/api/tests/unit_tests/controllers/console/agent/test_agent_controllers.py @@ -1,4 +1,3 @@ -from collections.abc import Callable from datetime import datetime from inspect import getsource, unwrap from types import SimpleNamespace @@ -309,15 +308,9 @@ def account_id() -> str: def test_agent_app_list_and_create_use_agent_route( - app: Flask, - monkeypatch: pytest.MonkeyPatch, - account_id: str, - sqlite_session: Session, - config_overrides: Callable[..., None], + app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str, sqlite_session: Session ) -> None: captured: dict[str, object] = {} - replace_whitelist = MagicMock() - initialize_access = MagicMock() class FakeAppService: def get_app(self, app_obj: object, *, session: object) -> object: @@ -403,9 +396,7 @@ def test_agent_app_list_and_create_use_agent_route( lambda _self, **kwargs: {"agent-list": "debug-conversation-list"}, ) monkeypatch.setattr( - roster_controller.AgentRosterService, - "count_agent_app_debug_conversation_messages", - lambda _self, **kwargs: 0, + roster_controller.AgentRosterService, "count_agent_app_debug_conversation_messages", lambda _self, **kwargs: 0 ) def get_or_create_debug_conversation(_self: object, **kwargs: object) -> str: @@ -422,13 +413,6 @@ def test_agent_app_list_and_create_use_agent_route( "get_system_features", lambda: SimpleNamespace(webapp_auth=SimpleNamespace(enabled=False)), ) - config_overrides(RBAC_ENABLED=True) - monkeypatch.setattr( - roster_controller.enterprise_rbac_service.RBACService.AppAccess, - "replace_whitelist", - replace_whitelist, - ) - monkeypatch.setattr(roster_controller.initialize_created_app_rbac_access_task, "delay", initialize_access) with app.test_request_context( "/console/api/agent?page=1&limit=10&mode=workflow&sort_by=recently_created" "&is_created_by_me=true&publication_status=published" @@ -469,22 +453,12 @@ def test_agent_app_list_and_create_use_agent_route( assert count_params.agent_is_published is True with app.test_request_context( "/console/api/agent", - json={ - "name": "Iris", - "description": "Agent app", - "role": "Coordinator", - "icon_type": "emoji", - "icon": "robot", - }, + json={"name": "Iris", "description": "Agent app", "role": "Coordinator", "icon_type": "emoji", "icon": "robot"}, ): created, status = unwrap(AgentAppListApi.post)( AgentAppListApi(), AgentAppCreatePayload( - name="Iris", - description="Agent app", - role="Coordinator", - icon_type="emoji", - icon="robot", + name="Iris", description="Agent app", role="Coordinator", icon_type="emoji", icon="robot" ), sqlite_session, "tenant-1", @@ -507,81 +481,6 @@ def test_agent_app_list_and_create_use_agent_route( "account_id": account_id, "commit": False, } - replace_whitelist.assert_called_once() - assert replace_whitelist.call_args.args[:3] == ("tenant-1", account_id, "app-created") - replace_payload = replace_whitelist.call_args.args[3] - assert replace_payload.automatic_include_workspace_members is True - initialize_access.assert_called_once_with("tenant-1", account_id, app_id="app-created") - - -def test_agent_app_create_skips_rbac_access_initialization_when_rbac_is_disabled( - app: Flask, - monkeypatch: pytest.MonkeyPatch, - account_id: str, - sqlite_session: Session, - config_overrides: Callable[..., None], -) -> None: - replace_whitelist = MagicMock() - initialize_access = MagicMock() - - class FakeAppService: - def get_app(self, app_obj: object, *, session: object) -> object: - return app_obj - - def create_app(self, tenant_id: str, params, current_user: object, *, session: object) -> object: - return _app_detail_obj(id="app-created", bound_agent_id="agent-created") - - monkeypatch.setattr(roster_controller, "AppService", FakeAppService) - monkeypatch.setattr( - roster_controller.AgentRosterService, - "get_app_backing_agent", - lambda _self, **kwargs: Agent( - id="agent-created", - app_id="app-created", - backing_app_id=None, - role="Created role", - active_config_snapshot_id=None, - ), - ) - monkeypatch.setattr( - roster_controller.AgentRosterService, - "get_or_create_build_conversation", - lambda _self, **kwargs: "debug-conversation-created", - ) - monkeypatch.setattr( - roster_controller.AgentRosterService, "count_agent_app_debug_conversation_messages", lambda _self, **kwargs: 0 - ) - monkeypatch.setattr( - roster_controller.FeatureService, - "get_system_features", - lambda: SimpleNamespace(webapp_auth=SimpleNamespace(enabled=False)), - ) - config_overrides(RBAC_ENABLED=False) - monkeypatch.setattr( - roster_controller.enterprise_rbac_service.RBACService.AppAccess, - "replace_whitelist", - replace_whitelist, - ) - monkeypatch.setattr(roster_controller.initialize_created_app_rbac_access_task, "delay", initialize_access) - - with app.test_request_context( - "/console/api/agent", - json={"name": "Iris", "description": "Agent app", "role": "Coordinator", "icon_type": "emoji", "icon": "robot"}, - ): - created, status = unwrap(AgentAppListApi.post)( - AgentAppListApi(), - AgentAppCreatePayload( - name="Iris", description="Agent app", role="Coordinator", icon_type="emoji", icon="robot" - ), - sqlite_session, - "tenant-1", - _account(account_id=account_id), - ) - - assert status == 201 - assert created["id"] == "agent-created" - replace_whitelist.assert_not_called() - initialize_access.assert_not_called() def test_agent_app_create_payload_allows_optional_role() -> None: diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/__tests__/layout-main.spec.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/__tests__/layout-main.spec.tsx index 9a52e32d0e7..8c6acc78866 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/__tests__/layout-main.spec.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/__tests__/layout-main.spec.tsx @@ -238,11 +238,9 @@ describe('AppDetailLayout', () => { expect(useStore.getState().appDetail?.id).toBe('app-1') }) - it('should allow users with access point permission to open access point directly', async () => { + it('should allow access point pages without app deploy or app ACL permissions', async () => { mockPathname = '/app/app-1/access-point' - mockFetchAppDetailDirect.mockResolvedValue( - createAppDetail({ permission_keys: [AppACLPermission.AccessPoint] }), - ) + mockFetchAppDetailDirect.mockResolvedValue(createAppDetail({ permission_keys: [] })) render( @@ -256,44 +254,6 @@ describe('AppDetailLayout', () => { expect(useStore.getState().appDetail?.id).toBe('app-1') }) - it('should redirect access point pages when access point permission is missing', async () => { - mockPathname = '/app/app-1/access-point' - mockFetchAppDetailDirect.mockResolvedValue( - createAppDetail({ permission_keys: [AppACLPermission.Monitor] }), - ) - - render( - -
App page content
-
, - ) - - await waitFor(() => { - expect(mockReplace).toHaveBeenCalledWith('/app/app-1/overview') - }) - expect(screen.queryByText('App page content')).not.toBeInTheDocument() - expect(useStore.getState().appDetail).toBeUndefined() - }) - - it('should keep access point content hidden while redirecting cached app data without permission', async () => { - mockPathname = '/app/app-1/access-point' - useStore - .getState() - .setAppDetail(createAppDetail({ permission_keys: [AppACLPermission.Monitor] })) - - render( - -
App page content
-
, - ) - - expect(screen.queryByText('App page content')).not.toBeInTheDocument() - await waitFor(() => { - expect(mockReplace).toHaveBeenCalledWith('/app/app-1/overview') - }) - expect(mockFetchAppDetailDirect).not.toHaveBeenCalled() - }) - it('should redirect deploy pages when app deploy ACL permission is missing', async () => { mockPathname = '/app/app-1/deploy' mockFetchAppDetailDirect.mockResolvedValue( @@ -357,7 +317,7 @@ describe('AppDetailLayout', () => { ) await waitFor(() => { - expect(mockReplace).toHaveBeenCalledWith('/apps') + expect(mockReplace).toHaveBeenCalledWith('/app/app-1/access-point') }) expect(screen.queryByText('App page content')).not.toBeInTheDocument() expect(useStore.getState().appDetail).toBeUndefined() @@ -528,7 +488,7 @@ describe('AppDetailLayout', () => { ) await waitFor(() => { - expect(mockReplace).toHaveBeenCalledWith('/apps') + expect(mockReplace).toHaveBeenCalledWith('/app/app-1/access-point') }) expect(screen.queryByText('App page content')).not.toBeInTheDocument() expect(useStore.getState().appDetail).toBeUndefined() diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx index df55021f289..7f9f6973ca8 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx @@ -78,28 +78,8 @@ const AppDetailLayout: FC = (props) => { appDetail?.id === appId ? appDetail : appDetailRes?.id === appId ? appDetailRes : null const pageTitle = appDetailPageTitle(pathname, t) const appName = routeAppDetail?.id === appId ? routeAppDetail.name : undefined - const isAppACLContextReady = - !!routeAppDetail && - !!currentWorkspace.id && - !isLoadingCurrentWorkspace && - !isLoadingWorkspacePermissionKeys && - !isLoadingAppDetail - const appACLCapabilities = React.useMemo( - () => - routeAppDetail && isAppACLContextReady - ? getAppACLCapabilities(routeAppDetail.permission_keys, { - currentUserId, - resourceMaintainer: routeAppDetail.maintainer, - workspacePermissionKeys, - isRbacEnabled, - }) - : null, - [currentUserId, isAppACLContextReady, isRbacEnabled, routeAppDetail, workspacePermissionKeys], - ) const shouldBlockAgentResourceAccess = routeAppDetail?.mode === AppModeEnum.AGENT && pathname.endsWith('/access-config') - const shouldBlockAccessPointAccess = - pathname.endsWith('/access-point') && !appACLCapabilities?.canAccessPoint useDocumentTitle(`${pageTitle} · ${appName || t(($) => $['menus.appDetail'], { ns: 'common' })}`) @@ -140,16 +120,28 @@ const AppDetailLayout: FC = (props) => { }, [appId, router, setAppDetail]) useEffect(() => { - if (!routeAppDetail || !isAppACLContextReady || !appACLCapabilities) return + if ( + !routeAppDetail || + !currentWorkspace.id || + isLoadingCurrentWorkspace || + isLoadingWorkspacePermissionKeys || + isLoadingAppDetail + ) + return if (routeAppDetail.id !== appId) return + const appACLCapabilities = getAppACLCapabilities(routeAppDetail.permission_keys, { + currentUserId, + resourceMaintainer: routeAppDetail.maintainer, + workspacePermissionKeys, + isRbacEnabled, + }) const isLayoutPath = pathname.endsWith('configuration') || pathname.endsWith('workflow') const isLogsPath = pathname.endsWith('logs') const isAnnotationsPath = pathname.endsWith('annotations') const isOverviewPath = pathname.endsWith('overview') const isAccessConfigPath = pathname.endsWith('access-config') const isDeployPath = pathname.endsWith('deploy') - const isAccessPointPath = pathname.endsWith('access-point') if ( (isLayoutPath && !appACLCapabilities.canAccessLayout) || (isLogsPath && !appACLCapabilities.canAccessLogAndAnnotation) || @@ -158,8 +150,7 @@ const AppDetailLayout: FC = (props) => { (isAccessConfigPath && (routeAppDetail.mode === AppModeEnum.AGENT || !appACLCapabilities.canAccessConfig)) || (isDeployPath && - (routeAppDetail.mode !== AppModeEnum.WORKFLOW || !appACLCapabilities.canDeploy)) || - (isAccessPointPath && !appACLCapabilities.canAccessPoint) + (routeAppDetail.mode !== AppModeEnum.WORKFLOW || !appACLCapabilities.canDeploy)) ) { router.replace( getRedirectionPath(routeAppDetail, { @@ -189,12 +180,14 @@ const AppDetailLayout: FC = (props) => { if (appDetailRes && appDetail?.id !== appDetailRes.id) setAppDetail({ ...appDetailRes, enable_sso: false }) }, [ - appACLCapabilities, appDetail?.id, appDetailRes, appId, currentUserId, - isAppACLContextReady, + currentWorkspace.id, + isLoadingAppDetail, + isLoadingCurrentWorkspace, + isLoadingWorkspacePermissionKeys, isRbacEnabled, pathname, routeAppDetail, @@ -205,7 +198,7 @@ const AppDetailLayout: FC = (props) => { const isWorkflowPage = pathname.endsWith('/workflow') const content = - !appDetail || shouldBlockAgentResourceAccess || shouldBlockAccessPointAccess ? ( + !appDetail || shouldBlockAgentResourceAccess ? (
diff --git a/web/app/components/app-sidebar/__tests__/app-detail-section.spec.tsx b/web/app/components/app-sidebar/__tests__/app-detail-section.spec.tsx index 0ff08042648..e84d925dbdc 100644 --- a/web/app/components/app-sidebar/__tests__/app-detail-section.spec.tsx +++ b/web/app/components/app-sidebar/__tests__/app-detail-section.spec.tsx @@ -186,10 +186,7 @@ describe('AppDetailSection', () => { ).not.toBeInTheDocument() }) - it('should render access point navigation when access point permission is granted', () => { - // Arrange - mockAppPermissionKeys = [AppACLPermission.AccessPoint] - + it('should render access point navigation using its app route', () => { // Act render() @@ -203,19 +200,6 @@ describe('AppDetailSection', () => { ).not.toBeInTheDocument() }) - it('should hide access point navigation when access point permission is missing', () => { - // Arrange - mockAppPermissionKeys = [AppACLPermission.Monitor] - - // Act - render() - - // Assert - expect( - screen.queryByRole('link', { name: 'common.appMenus.accessPoint' }), - ).not.toBeInTheDocument() - }) - it('should render deploy navigation with app deploy ACL regardless of the legacy workspace role', () => { // Arrange mockAppMode = 'workflow' diff --git a/web/app/components/app-sidebar/app-detail-section.tsx b/web/app/components/app-sidebar/app-detail-section.tsx index 677ac50aea4..3176e2cac21 100644 --- a/web/app/components/app-sidebar/app-detail-section.tsx +++ b/web/app/components/app-sidebar/app-detail-section.tsx @@ -120,16 +120,12 @@ const AppDetailSection = ({ expand = true }: AppDetailSectionProps) => { }, ] : []), - ...(appACLCapabilities.canAccessPoint - ? [ - { - name: t(($) => $['appMenus.accessPoint'], { ns: 'common' }), - href: `/app/${appId}/access-point`, - icon: accessPointNavIcon, - selectedIcon: accessPointNavIcon, - }, - ] - : []), + { + name: t(($) => $['appMenus.accessPoint'], { ns: 'common' }), + href: `/app/${appId}/access-point`, + icon: accessPointNavIcon, + selectedIcon: accessPointNavIcon, + }, ...(supportsAppDeploy && appACLCapabilities.canDeploy ? [ { diff --git a/web/app/components/app/app-publisher/__tests__/environment-deployment-flow.spec.tsx b/web/app/components/app/app-publisher/__tests__/environment-deployment-flow.spec.tsx index 9c80ad25f11..8aa12ed5457 100644 --- a/web/app/components/app/app-publisher/__tests__/environment-deployment-flow.spec.tsx +++ b/web/app/components/app/app-publisher/__tests__/environment-deployment-flow.spec.tsx @@ -295,7 +295,6 @@ function renderFlow( return render( ({ default: ({ @@ -315,7 +314,6 @@ describe('app-publisher sections', () => { description: 'Workflow description', }} appURL="https://example.com/app" - canAccessPoint disabledFunctionButton={false} disabledFunctionTooltip="disabled" handleOpenRunConfig={handleOpenRunConfig} @@ -496,7 +494,6 @@ describe('app-publisher sections', () => { mode: AppModeEnum.WORKFLOW, }} appURL="https://example.com/app" - canAccessPoint disabledFunctionButton={false} hasHumanInputNode={false} hasTriggerNode @@ -520,49 +517,11 @@ describe('app-publisher sections', () => { ) }) - it('should hide the built-in Access Point action without permission', () => { - render( - , - ) - - expect(screen.queryByText(/(?:^|\.)appMenus\.accessPoint(?=$|:)/)).not.toBeInTheDocument() - expect(screen.getByRole('link', { name: /appMenus\.deploy\b/ })).toHaveAttribute( - 'href', - '/app/workflow-app/deploy', - ) - }) - - it('should hide the environment Access Point action without permission', () => { - render( - , - ) - - expect(screen.queryByText(/(?:^|\.)appMenus\.accessPoint(?=$|:)/)).not.toBeInTheDocument() - expect(screen.getByText(/(?:^|\.)appMenus\.deploy(?=$|:)/)).toBeInTheDocument() - }) - it('should expose unavailable quick links as disabled buttons before the first publish', () => { render( void @@ -42,7 +41,6 @@ type PublisherActionsSectionProps = Pick< export function PublisherActionsSection({ appDetail, appURL, - canAccessPoint = false, disabledFunctionButton, disabledFunctionTooltip, handleOpenRunConfig, @@ -116,16 +114,14 @@ export function PublisherActionsSection({ {disabledFunctionTooltip} )} - {canAccessPoint && ( - $['common.accessPointDescription'], { ns: 'workflow' })} - link={appId ? `/app/${appId}/access-point` : undefined} - icon={} - > - {t(($) => $['appMenus.accessPoint'], { ns: 'common' })} - - )} + $['common.accessPointDescription'], { ns: 'workflow' })} + link={appId ? `/app/${appId}/access-point` : undefined} + icon={} + > + {t(($) => $['appMenus.accessPoint'], { ns: 'common' })} + {showDeploy && ( - {canAccessPoint && ( - $['common.accessPointDescription'], { ns: 'workflow' })} - link={accessPointHref} - icon={} - > - {t(($) => $['appMenus.accessPoint'], { ns: 'common' })} - - )} + $['common.accessPointDescription'], { ns: 'workflow' })} + link={accessPointHref} + icon={} + > + {t(($) => $['appMenus.accessPoint'], { ns: 'common' })} + $['common.deployDescription'], { ns: 'workflow' })} diff --git a/web/app/components/app/app-publisher/environment-deployment-flow/index.tsx b/web/app/components/app/app-publisher/environment-deployment-flow/index.tsx index 9f0b1201993..dce08832bc0 100644 --- a/web/app/components/app/app-publisher/environment-deployment-flow/index.tsx +++ b/web/app/components/app/app-publisher/environment-deployment-flow/index.tsx @@ -15,7 +15,6 @@ import { PublisherEnvironmentSummarySection } from './summary-section' type PublisherEnvironmentFlowProps = { appId?: string - canAccessPoint?: boolean deployment?: EnvironmentDeployment environmentId: string environmentName: string @@ -29,7 +28,6 @@ type PublisherEnvironmentFlowProps = { export function PublisherEnvironmentFlow({ appId, - canAccessPoint = false, deployment, environmentId, environmentName, @@ -93,7 +91,6 @@ export function PublisherEnvironmentFlow({ /> diff --git a/web/app/components/app/app-publisher/index.tsx b/web/app/components/app/app-publisher/index.tsx index 38e9b724d1e..48278b77cfc 100644 --- a/web/app/components/app/app-publisher/index.tsx +++ b/web/app/components/app/app-publisher/index.tsx @@ -17,13 +17,12 @@ export function AppPublisher(props: AppPublisherProps) { select: (data) => data.profile.id, }) const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom) - const appACLCapabilities = getAppACLCapabilities(appDetail?.permission_keys, { + const canDeploy = getAppACLCapabilities(appDetail?.permission_keys, { currentUserId, resourceMaintainer: appDetail?.maintainer, workspacePermissionKeys, - }) - const supportsMultiEnvironment = - appDetail?.mode === AppModeEnum.WORKFLOW && appACLCapabilities.canDeploy + }).canDeploy + const supportsMultiEnvironment = appDetail?.mode === AppModeEnum.WORKFLOW && canDeploy return ( void } export function PublisherContent({ - canAccessPoint, crossAxisOffset = 0, debugWithMultipleModel = false, disabled = false, @@ -214,7 +212,6 @@ export function PublisherContent({ actions: { appDetail, appURL, - canAccessPoint, disabledFunctionButton, disabledFunctionTooltip, handleOpenRunConfig: workflowLaunch.openDialog, @@ -239,7 +236,6 @@ export function PublisherContent({ disabled={disabled} environmentPublisher={{ appId: appDetail?.id, - canAccessPoint, deployment: selectedEnvironmentDeployment, environmentId: selectedEnvironmentId, environmentName: diff --git a/web/app/components/app/deploy/__tests__/index.spec.tsx b/web/app/components/app/deploy/__tests__/index.spec.tsx index 79c1f082294..11888a09817 100644 --- a/web/app/components/app/deploy/__tests__/index.spec.tsx +++ b/web/app/components/app/deploy/__tests__/index.spec.tsx @@ -650,7 +650,7 @@ function render( return renderWithConsoleQuery(ui, { queryClient }) } -let appPermissionKeys: string[] = [AppACLPermission.AccessPoint, AppACLPermission.Deploy] +let appPermissionKeys: string[] = [AppACLPermission.Deploy] let appDetailAvailable = true const mockConsoleState = vi.hoisted(() => ({ workspacePermissionKeys: [] as string[], @@ -744,7 +744,7 @@ vi.mock('@langgenius/dify-ui/toast', () => ({ describe('AppDeploy', () => { beforeEach(() => { vi.clearAllMocks() - appPermissionKeys = [AppACLPermission.AccessPoint, AppACLPermission.Deploy] + appPermissionKeys = [AppACLPermission.Deploy] appDetailAvailable = true mockBuiltInEnvironment.appDetail.enable_api = false mockBuiltInEnvironment.appDetail.enable_site = true @@ -815,18 +815,6 @@ describe('AppDeploy', () => { ).toHaveAttribute('href', '/app/app-1/access-point?environment=canary&accessPoint=serviceApi') }) - it('keeps active access points non-navigable without access point permission', () => { - appPermissionKeys = [AppACLPermission.Deploy] - - render() - - const canaryRow = within(screen.getByRole('row', { name: /Canary/ })) - const webAppLabel = - 'agentV2.agentDetail.access.webApp.title · agentV2.agentDetail.access.status.inService' - expect(canaryRow.queryByRole('link', { name: webAppLabel })).not.toBeInTheDocument() - expect(canaryRow.getByRole('button', { name: webAppLabel })).toBeDisabled() - }) - it('renders the built-in version, access points, and publisher from live app data', () => { render() diff --git a/web/app/components/app/deploy/built-in-environment-card/index.tsx b/web/app/components/app/deploy/built-in-environment-card/index.tsx index 607250e7ce6..d04dce259a8 100644 --- a/web/app/components/app/deploy/built-in-environment-card/index.tsx +++ b/web/app/components/app/deploy/built-in-environment-card/index.tsx @@ -19,7 +19,7 @@ function Divider() { return
} -export function BuiltInEnvironmentCard({ canAccessPoint = false }: { canAccessPoint?: boolean }) { +export function BuiltInEnvironmentCard() { const { t } = useTranslation('deployments') const { formatTime } = useTimestamp() const appDetail = useAppStore((state) => state.appDetail) @@ -90,9 +90,7 @@ export function BuiltInEnvironmentCard({ canAccessPoint = false }: { canAccessPo key={accessPoint} accessPoint={accessPoint} active={activeAccessPoints[accessPoint]} - href={ - canAccessPoint ? getAccessPointHref(appId, 'built-in', accessPoint) : undefined - } + href={getAccessPointHref(appId, 'built-in', accessPoint)} /> ))}
diff --git a/web/app/components/app/deploy/environment-table/index.tsx b/web/app/components/app/deploy/environment-table/index.tsx index cc9b9451e59..cbee27225ed 100644 --- a/web/app/components/app/deploy/environment-table/index.tsx +++ b/web/app/components/app/deploy/environment-table/index.tsx @@ -23,7 +23,6 @@ import { EnvironmentRow } from './row' type EnvironmentTableProps = { appId: string - canAccessPoint?: boolean onChangeVersion?: (deployment: EnvironmentDeployment) => void onDeployLatest?: (deployment: EnvironmentDeployment) => void onDeployToEnvironment?: (environment: AppEnvironment) => void @@ -33,7 +32,6 @@ type EnvironmentTableProps = { export function EnvironmentTable({ appId, - canAccessPoint = false, onChangeVersion, onDeployLatest, onDeployToEnvironment, @@ -134,7 +132,6 @@ export function EnvironmentTable({ void onDeployLatest?: (deployment: EnvironmentDeployment) => void @@ -62,11 +60,7 @@ export function EnvironmentRow({ key={accessPoint} accessPoint={accessPoint} active={isAccessPointActive(accessPoint)} - href={ - canAccessPoint - ? getAccessPointHref(appId, row.environment.id, accessPoint) - : undefined - } + href={getAccessPointHref(appId, row.environment.id, accessPoint)} /> ))}
diff --git a/web/app/components/app/deploy/index.tsx b/web/app/components/app/deploy/index.tsx index 4f1bee0ed1e..c22034584dd 100644 --- a/web/app/components/app/deploy/index.tsx +++ b/web/app/components/app/deploy/index.tsx @@ -22,7 +22,7 @@ import { useRefreshAppEnvironmentsAfterDeploymentPolling } from './use-refresh-a import { useUndeployWorkflow } from './use-undeploy-workflow' import { toDeploymentVersion } from './version' -function AppDeployContent({ appId, canAccessPoint }: { appId: string; canAccessPoint: boolean }) { +function AppDeployContent({ appId }: { appId: string }) { const { t } = useTranslation('deployments') const { t: tCommon } = useTranslation('common') const { t: tWorkflow } = useTranslation('workflow') @@ -86,10 +86,9 @@ function AppDeployContent({ appId, canAccessPoint }: { appId: string; canAccessP
- + setDeploymentRequest({ environment: environment.display_name, @@ -140,17 +139,17 @@ export default function AppDeploy() { if (!appDetail) return - const appACLCapabilities = getAppACLCapabilities(appDetail.permission_keys, { + const canDeploy = getAppACLCapabilities(appDetail.permission_keys, { currentUserId, resourceMaintainer: appDetail.maintainer, workspacePermissionKeys, - }) + }).canDeploy - if (appDetail.mode !== AppModeEnum.WORKFLOW || !appACLCapabilities.canDeploy) return null + if (appDetail.mode !== AppModeEnum.WORKFLOW || !canDeploy) return null return ( - + ) } diff --git a/web/app/components/app/deploy/shared/__tests__/access-point-icon.spec.tsx b/web/app/components/app/deploy/shared/__tests__/access-point-icon.spec.tsx deleted file mode 100644 index 80d18eaaee6..00000000000 --- a/web/app/components/app/deploy/shared/__tests__/access-point-icon.spec.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { screen } from '@testing-library/react' -import { renderWithConsoleQuery as render } from '@/test/console/query-data' -import { AccessPointIcon } from '../access-point-icon' - -describe('AccessPointIcon', () => { - it('links active access points when navigation is allowed', () => { - render( - , - ) - - expect(screen.getByRole('link')).toHaveAttribute( - 'href', - '/app/app-1/access-point?environment=built-in&accessPoint=webApp', - ) - }) - - it('keeps active access points visually active when navigation is not allowed', () => { - render() - - expect(screen.queryByRole('link')).not.toBeInTheDocument() - expect(screen.getByRole('button')).toBeDisabled() - expect(screen.getByRole('button')).not.toHaveClass('opacity-30') - }) - - it('dims inactive access points', () => { - render() - - expect(screen.getByRole('button')).toBeDisabled() - expect(screen.getByRole('button')).toHaveClass('opacity-30') - }) -}) diff --git a/web/app/components/app/deploy/shared/access-point-icon.tsx b/web/app/components/app/deploy/shared/access-point-icon.tsx index 253429f03fc..734e9b694ee 100644 --- a/web/app/components/app/deploy/shared/access-point-icon.tsx +++ b/web/app/components/app/deploy/shared/access-point-icon.tsx @@ -32,7 +32,7 @@ export function AccessPointIcon({ }: { active: boolean accessPoint: AccessPoint - href?: string + href: string }) { const { t } = useTranslation('agentV2') const labels = useAccessPointLabels() @@ -40,11 +40,9 @@ export function AccessPointIcon({ ? t(($) => $['agentDetail.access.status.inService']) : t(($) => $['agentDetail.access.status.outOfService']) const label = `${labels[accessPoint]} · ${status}` - const canNavigate = active && Boolean(href) const triggerClassName = cn( 'flex size-5 shrink-0 items-center justify-center rounded-md border border-divider-regular text-text-secondary shadow-xs outline-hidden focus-visible:ring-2 focus-visible:ring-state-accent-solid', - active && (canNavigate ? 'cursor-pointer hover:bg-state-base-hover' : 'cursor-default'), - !active && 'cursor-not-allowed opacity-30', + active ? 'cursor-pointer hover:bg-state-base-hover' : 'cursor-not-allowed opacity-30', ) const icon = ( @@ -54,7 +52,7 @@ export function AccessPointIcon({ {icon} diff --git a/web/app/components/header/account-setting/access-rules-page/permission-set-modal/__tests__/index.spec.tsx b/web/app/components/header/account-setting/access-rules-page/permission-set-modal/__tests__/index.spec.tsx index 764b51cb52c..7c4b00da20e 100644 --- a/web/app/components/header/account-setting/access-rules-page/permission-set-modal/__tests__/index.spec.tsx +++ b/web/app/components/header/account-setting/access-rules-page/permission-set-modal/__tests__/index.spec.tsx @@ -19,7 +19,6 @@ const expectedAppACLPermissionKeys = [ 'app.acl.tracing_config', 'app.acl.log_and_annotation', 'app.acl.access_config', - 'app.acl.access_point_manage', ] const getPermissionKeyMatcher = (permissionKey: string) => diff --git a/web/features/home/continue-work/__tests__/item.spec.tsx b/web/features/home/continue-work/__tests__/item.spec.tsx index 37b74a4b944..ee61d10d6ce 100644 --- a/web/features/home/continue-work/__tests__/item.spec.tsx +++ b/web/features/home/continue-work/__tests__/item.spec.tsx @@ -168,15 +168,10 @@ describe('ContinueWorkItem', () => { ) }) - it('should fall back to access point when RBAC is disabled for an access-config app with access point permission', () => { - renderItem( - createApp({ - permission_keys: [AppACLPermission.AccessConfig, AppACLPermission.AccessPoint], - }), - { - rbac_enabled: false, - }, - ) + it('should fall back to access point when RBAC is disabled for an access-config-only app', () => { + renderItem(createApp({ permission_keys: [AppACLPermission.AccessConfig] }), { + rbac_enabled: false, + }) expect(screen.getByRole('link', { name: /Continue App/ })).toHaveAttribute( 'href', diff --git a/web/i18n/ar-TN/permission-keys.json b/web/i18n/ar-TN/permission-keys.json index 18321e3b376..a7f129455c5 100644 --- a/web/i18n/ar-TN/permission-keys.json +++ b/web/i18n/ar-TN/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "إدارة إعدادات امتداد API", "app.access_config": "تكوين أذونات الوصول إلى التطبيق", "app.acl.access_config": "عرض أذونات الوصول وإدارتها", - "app.acl.access_point_manage": "عرض نقاط الوصول وإدارتها", "app.acl.delete": "حذف التطبيق", "app.acl.deploy": "نشر التطبيق", "app.acl.edit": "تعديل معلومات التطبيق وتنسيقه", diff --git a/web/i18n/de-DE/permission-keys.json b/web/i18n/de-DE/permission-keys.json index 2d546c0e082..fb32d92c699 100644 --- a/web/i18n/de-DE/permission-keys.json +++ b/web/i18n/de-DE/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "API-Erweiterungskonfiguration verwalten", "app.access_config": "App-Zugriffsberechtigungen konfigurieren", "app.acl.access_config": "Zugriffsberechtigungen anzeigen und verwalten", - "app.acl.access_point_manage": "Zugangspunkte anzeigen und verwalten", "app.acl.delete": "App löschen", "app.acl.deploy": "App bereitstellen", "app.acl.edit": "App-Informationen bearbeiten und App orchestrieren", diff --git a/web/i18n/en-US/permission-keys.json b/web/i18n/en-US/permission-keys.json index e69f68bb0b3..2344caa11e1 100644 --- a/web/i18n/en-US/permission-keys.json +++ b/web/i18n/en-US/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "Manage API extension configuration", "app.access_config": "Configure app access permissions", "app.acl.access_config": "View and manage access permissions", - "app.acl.access_point_manage": "View and manage access points", "app.acl.delete": "Delete app", "app.acl.deploy": "Deploy app", "app.acl.edit": "Edit app information and orchestrate app", diff --git a/web/i18n/es-ES/permission-keys.json b/web/i18n/es-ES/permission-keys.json index d7d5e9d57c8..af3336ceea7 100644 --- a/web/i18n/es-ES/permission-keys.json +++ b/web/i18n/es-ES/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "Gestionar la configuración de la extensión de API", "app.access_config": "Configurar los permisos de acceso de la app", "app.acl.access_config": "Ver y gestionar los permisos de acceso", - "app.acl.access_point_manage": "Ver y gestionar los puntos de acceso", "app.acl.delete": "Eliminar app", "app.acl.deploy": "Desplegar la app", "app.acl.edit": "Editar la información y orquestar la app", diff --git a/web/i18n/fa-IR/permission-keys.json b/web/i18n/fa-IR/permission-keys.json index 5e739bed336..372374ed3b8 100644 --- a/web/i18n/fa-IR/permission-keys.json +++ b/web/i18n/fa-IR/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "مدیریت پیکربندی افزونه API", "app.access_config": "پیکربندی مجوزهای دسترسی برنامه", "app.acl.access_config": "مشاهده و مدیریت مجوزهای دسترسی", - "app.acl.access_point_manage": "مشاهده و مدیریت نقاط دسترسی", "app.acl.delete": "حذف برنامه", "app.acl.deploy": "استقرار برنامه", "app.acl.edit": "ویرایش اطلاعات برنامه و هماهنگ‌سازی برنامه", diff --git a/web/i18n/fr-FR/permission-keys.json b/web/i18n/fr-FR/permission-keys.json index c547052586d..074da133fae 100644 --- a/web/i18n/fr-FR/permission-keys.json +++ b/web/i18n/fr-FR/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "Gérer la configuration de l'extension API", "app.access_config": "Configurer les autorisations d'accès à l'application", "app.acl.access_config": "Afficher et gérer les autorisations d'accès", - "app.acl.access_point_manage": "Afficher et gérer les points d’accès", "app.acl.delete": "Supprimer l'application", "app.acl.deploy": "Déployer l'application", "app.acl.edit": "Modifier les informations et orchestrer l'application", diff --git a/web/i18n/hi-IN/permission-keys.json b/web/i18n/hi-IN/permission-keys.json index 0779d870342..314cbfba84b 100644 --- a/web/i18n/hi-IN/permission-keys.json +++ b/web/i18n/hi-IN/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "API एक्सटेंशन कॉन्फ़िगरेशन प्रबंधित करें", "app.access_config": "ऐप एक्सेस अनुमतियाँ कॉन्फ़िगर करें", "app.acl.access_config": "एक्सेस अनुमतियाँ देखें और प्रबंधित करें", - "app.acl.access_point_manage": "एक्सेस पॉइंट देखें और प्रबंधित करें", "app.acl.delete": "ऐप हटाएं", "app.acl.deploy": "ऐप डिप्लॉय करें", "app.acl.edit": "ऐप की जानकारी संपादित करें और ऐप को ऑर्केस्ट्रेट करें", diff --git a/web/i18n/id-ID/permission-keys.json b/web/i18n/id-ID/permission-keys.json index 349bff75538..4b04ea96f00 100644 --- a/web/i18n/id-ID/permission-keys.json +++ b/web/i18n/id-ID/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "Kelola konfigurasi ekstensi API", "app.access_config": "Konfigurasikan izin akses aplikasi", "app.acl.access_config": "Lihat dan kelola izin akses", - "app.acl.access_point_manage": "Lihat dan kelola titik akses", "app.acl.delete": "Hapus aplikasi", "app.acl.deploy": "Deploy aplikasi", "app.acl.edit": "Edit informasi aplikasi dan orkestrasikan aplikasi", diff --git a/web/i18n/it-IT/permission-keys.json b/web/i18n/it-IT/permission-keys.json index b5f3ebf8094..899adce084b 100644 --- a/web/i18n/it-IT/permission-keys.json +++ b/web/i18n/it-IT/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "Gestisci la configurazione delle estensioni API", "app.access_config": "Configura i permessi di accesso all'app", "app.acl.access_config": "Visualizza e gestisci i permessi di accesso", - "app.acl.access_point_manage": "Visualizza e gestisci i punti di accesso", "app.acl.delete": "Elimina app", "app.acl.deploy": "Distribuisci app", "app.acl.edit": "Modifica le informazioni e orchestra l'app", diff --git a/web/i18n/ja-JP/permission-keys.json b/web/i18n/ja-JP/permission-keys.json index 53033e2dc10..1b0c567f0e8 100644 --- a/web/i18n/ja-JP/permission-keys.json +++ b/web/i18n/ja-JP/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "API拡張設定を管理", "app.access_config": "アプリアクセス権限を設定", "app.acl.access_config": "アクセス権限の表示と管理", - "app.acl.access_point_manage": "アクセスポイントの表示と管理", "app.acl.delete": "アプリを削除", "app.acl.deploy": "アプリをデプロイ", "app.acl.edit": "アプリ情報の編集とアプリのオーケストレーション", diff --git a/web/i18n/ko-KR/permission-keys.json b/web/i18n/ko-KR/permission-keys.json index 45517f6acb9..3a9981a602a 100644 --- a/web/i18n/ko-KR/permission-keys.json +++ b/web/i18n/ko-KR/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "API 확장 구성 관리", "app.access_config": "앱 접근 권한 구성", "app.acl.access_config": "접근 권한 보기 및 관리", - "app.acl.access_point_manage": "액세스 지점 보기 및 관리", "app.acl.delete": "앱 삭제", "app.acl.deploy": "앱 배포", "app.acl.edit": "앱 정보 편집 및 앱 오케스트레이션", diff --git a/web/i18n/lo-LA/permission-keys.json b/web/i18n/lo-LA/permission-keys.json index e04ef1f59ec..bfba51cc7e2 100644 --- a/web/i18n/lo-LA/permission-keys.json +++ b/web/i18n/lo-LA/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "ຈັດການການຕັ້ງຄ່າ API extension", "app.access_config": "ຕັ້ງຄ່າສິດການເຂົ້າເຖິງແອັບ", "app.acl.access_config": "ເບິ່ງ ແລະ ຈັດການສິດການເຂົ້າເຖິງ", - "app.acl.access_point_manage": "ເບິ່ງ ແລະ ຈັດການຈຸດເຂົ້າເຖິງ", "app.acl.delete": "ລຶບແອັບ", "app.acl.deploy": "ຕິດຕັ້ງແອັບ", "app.acl.edit": "ແກ້ໄຂຂໍ້ມູນແອັບ ແລະ ຈັດການລະບົບແອັບ", diff --git a/web/i18n/nl-NL/permission-keys.json b/web/i18n/nl-NL/permission-keys.json index 8266b38ae22..94fdf9d182c 100644 --- a/web/i18n/nl-NL/permission-keys.json +++ b/web/i18n/nl-NL/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "API-extensieconfiguratie beheren", "app.access_config": "Toegangsrechten voor app configureren", "app.acl.access_config": "Toegangsrechten bekijken en beheren", - "app.acl.access_point_manage": "Toegangspunten bekijken en beheren", "app.acl.delete": "App verwijderen", "app.acl.deploy": "App implementeren", "app.acl.edit": "App-informatie bewerken en app orkestreren", diff --git a/web/i18n/pl-PL/permission-keys.json b/web/i18n/pl-PL/permission-keys.json index 392f470914e..55619735f16 100644 --- a/web/i18n/pl-PL/permission-keys.json +++ b/web/i18n/pl-PL/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "Zarządzaj konfiguracją rozszerzenia API", "app.access_config": "Konfiguruj uprawnienia dostępu do aplikacji", "app.acl.access_config": "Wyświetlaj uprawnienia dostępu i zarządzaj nimi", - "app.acl.access_point_manage": "Wyświetlaj punkty dostępu i zarządzaj nimi", "app.acl.delete": "Usuń aplikację", "app.acl.deploy": "Wdróż aplikację", "app.acl.edit": "Edytuj informacje o aplikacji i orkiestruj aplikację", diff --git a/web/i18n/pt-BR/permission-keys.json b/web/i18n/pt-BR/permission-keys.json index 36dd03d5719..32dda95b517 100644 --- a/web/i18n/pt-BR/permission-keys.json +++ b/web/i18n/pt-BR/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "Gerenciar configuração de extensão de API", "app.access_config": "Configurar permissões de acesso ao aplicativo", "app.acl.access_config": "Visualizar e gerenciar permissões de acesso", - "app.acl.access_point_manage": "Visualizar e gerenciar pontos de acesso", "app.acl.delete": "Excluir aplicativo", "app.acl.deploy": "Implantar aplicativo", "app.acl.edit": "Editar informações e orquestrar o aplicativo", diff --git a/web/i18n/ro-RO/permission-keys.json b/web/i18n/ro-RO/permission-keys.json index 73225f7cd60..2610e185492 100644 --- a/web/i18n/ro-RO/permission-keys.json +++ b/web/i18n/ro-RO/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "Gestionează configurația extensiei API", "app.access_config": "Configurează permisiunile de acces ale aplicației", "app.acl.access_config": "Vizualizează și gestionează permisiunile de acces", - "app.acl.access_point_manage": "Vizualizează și gestionează punctele de acces", "app.acl.delete": "Șterge aplicația", "app.acl.deploy": "Implementează aplicația", "app.acl.edit": "Editează informațiile aplicației și orchestrează aplicația", diff --git a/web/i18n/ru-RU/permission-keys.json b/web/i18n/ru-RU/permission-keys.json index bd986d65e95..574f0e96add 100644 --- a/web/i18n/ru-RU/permission-keys.json +++ b/web/i18n/ru-RU/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "Управление конфигурацией API-расширений", "app.access_config": "Настройка прав доступа к приложению", "app.acl.access_config": "Просмотр и управление правами доступа", - "app.acl.access_point_manage": "Просмотр и управление точками доступа", "app.acl.delete": "Удаление приложения", "app.acl.deploy": "Развертывание приложения", "app.acl.edit": "Редактирование информации о приложении и оркестрация приложения", diff --git a/web/i18n/sl-SI/permission-keys.json b/web/i18n/sl-SI/permission-keys.json index 4a49494a2c2..544c6f92a8d 100644 --- a/web/i18n/sl-SI/permission-keys.json +++ b/web/i18n/sl-SI/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "Upravljanje konfiguracije razširitve API", "app.access_config": "Konfiguracija dovoljenj za dostop do aplikacije", "app.acl.access_config": "Ogled in upravljanje dovoljenj za dostop", - "app.acl.access_point_manage": "Ogled in upravljanje dostopnih točk", "app.acl.delete": "Izbriši aplikacijo", "app.acl.deploy": "Uvedi aplikacijo", "app.acl.edit": "Uredi podatke o aplikaciji in orkestriraj aplikacijo", diff --git a/web/i18n/th-TH/permission-keys.json b/web/i18n/th-TH/permission-keys.json index 0ad4047ef26..b7b9854abf0 100644 --- a/web/i18n/th-TH/permission-keys.json +++ b/web/i18n/th-TH/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "จัดการการกําหนดค่าส่วนขยาย API", "app.access_config": "กําหนดค่าสิทธิ์การเข้าถึงแอป", "app.acl.access_config": "ดูและจัดการสิทธิ์การเข้าถึง", - "app.acl.access_point_manage": "ดูและจัดการจุดเข้าถึง", "app.acl.delete": "ลบแอป", "app.acl.deploy": "ปรับใช้แอป", "app.acl.edit": "แก้ไขข้อมูลแอปและจัดวางแอป", diff --git a/web/i18n/tr-TR/permission-keys.json b/web/i18n/tr-TR/permission-keys.json index 781d9d00d38..36ba8ec9709 100644 --- a/web/i18n/tr-TR/permission-keys.json +++ b/web/i18n/tr-TR/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "API uzantısı yapılandırmasını yönet", "app.access_config": "Uygulama erişim izinlerini yapılandır", "app.acl.access_config": "Erişim izinlerini görüntüle ve yönet", - "app.acl.access_point_manage": "Erişim noktalarını görüntüle ve yönet", "app.acl.delete": "Uygulamayı sil", "app.acl.deploy": "Uygulamayı dağıt", "app.acl.edit": "Uygulama bilgilerini düzenle ve uygulamayı orkestre et", diff --git a/web/i18n/uk-UA/permission-keys.json b/web/i18n/uk-UA/permission-keys.json index 8cfd28d2548..861c83a4367 100644 --- a/web/i18n/uk-UA/permission-keys.json +++ b/web/i18n/uk-UA/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "Керування конфігурацією розширення API", "app.access_config": "Налаштування дозволів доступу до застосунку", "app.acl.access_config": "Переглядати дозволи доступу та керувати ними", - "app.acl.access_point_manage": "Переглядати точки доступу та керувати ними", "app.acl.delete": "Видалити застосунок", "app.acl.deploy": "Розгорнути застосунок", "app.acl.edit": "Редагувати інформацію про застосунок та оркеструвати застосунок", diff --git a/web/i18n/vi-VN/permission-keys.json b/web/i18n/vi-VN/permission-keys.json index 2290d6362e0..1e6662a9304 100644 --- a/web/i18n/vi-VN/permission-keys.json +++ b/web/i18n/vi-VN/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "Quản lý cấu hình phần mở rộng API", "app.access_config": "Cấu hình quyền truy cập ứng dụng", "app.acl.access_config": "Xem và quản lý quyền truy cập", - "app.acl.access_point_manage": "Xem và quản lý điểm truy cập", "app.acl.delete": "Xóa ứng dụng", "app.acl.deploy": "Triển khai ứng dụng", "app.acl.edit": "Chỉnh sửa thông tin và điều phối ứng dụng", diff --git a/web/i18n/zh-Hans/permission-keys.json b/web/i18n/zh-Hans/permission-keys.json index db8184ee533..91d9afb2cc8 100644 --- a/web/i18n/zh-Hans/permission-keys.json +++ b/web/i18n/zh-Hans/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "管理API扩展", "app.access_config": "配置应用访问权限", "app.acl.access_config": "查看与管理访问权限", - "app.acl.access_point_manage": "查看与管理访问点", "app.acl.delete": "删除应用", "app.acl.deploy": "部署应用", "app.acl.edit": "编辑应用信息与编排应用", diff --git a/web/i18n/zh-Hant/permission-keys.json b/web/i18n/zh-Hant/permission-keys.json index 8a74b37f086..43350a59ada 100644 --- a/web/i18n/zh-Hant/permission-keys.json +++ b/web/i18n/zh-Hant/permission-keys.json @@ -3,7 +3,6 @@ "api_extension.manage": "管理API擴充配置", "app.access_config": "配置應用訪問權限", "app.acl.access_config": "檢視與管理存取權限", - "app.acl.access_point_manage": "檢視與管理存取點", "app.acl.delete": "刪除應用", "app.acl.deploy": "部署應用", "app.acl.edit": "編輯應用資訊與編排應用", diff --git a/web/utils/app-redirection.spec.ts b/web/utils/app-redirection.spec.ts index 521a500652d..d736ed5a428 100644 --- a/web/utils/app-redirection.spec.ts +++ b/web/utils/app-redirection.spec.ts @@ -14,20 +14,10 @@ describe('app-redirection', () => { * - App mode (workflow, advanced-chat, chat, completion, agent-chat) */ describe('getRedirectionPath', () => { - it('returns access point path when app access point permission is granted', () => { - const app = { - id: 'app-123', - mode: AppModeEnum.CHAT, - permission_keys: [AppACLPermission.AccessPoint], - } - const result = getRedirectionPath(app) - expect(result).toBe('/app/app-123/access-point') - }) - - it('returns apps list path when app ACL cannot access guarded pages or access point', () => { + it('returns access point path when app ACL cannot access guarded pages', () => { const app = { id: 'app-123', mode: AppModeEnum.CHAT, permission_keys: [] } const result = getRedirectionPath(app) - expect(result).toBe('/apps') + expect(result).toBe('/app/app-123/access-point') }) it('returns workflow path for workflow mode when app ACL can access layout', () => { @@ -102,11 +92,7 @@ describe('app-redirection', () => { }) it('handles different app IDs', () => { - const app1 = { - id: 'abc-123', - mode: AppModeEnum.CHAT, - permission_keys: [AppACLPermission.AccessPoint], - } + const app1 = { id: 'abc-123', mode: AppModeEnum.CHAT, permission_keys: [] } const app2 = { id: 'xyz-789', mode: AppModeEnum.WORKFLOW, @@ -143,7 +129,7 @@ describe('app-redirection', () => { const app = { id: 'app-123', mode: AppModeEnum.CHAT, - permission_keys: [AppACLPermission.AccessConfig, AppACLPermission.AccessPoint], + permission_keys: [AppACLPermission.AccessConfig], } expect(getRedirectionPath(app, { isRbacEnabled: false })).toBe('/app/app-123/access-point') @@ -187,12 +173,8 @@ describe('app-redirection', () => { /** * Tests that the redirection function is called with the correct path */ - it('calls redirection function with access point path when access point permission is granted', () => { - const app = { - id: 'app-123', - mode: AppModeEnum.CHAT, - permission_keys: [AppACLPermission.AccessPoint], - } + it('calls redirection function with access point path when app ACL cannot access guarded pages', () => { + const app = { id: 'app-123', mode: AppModeEnum.CHAT, permission_keys: [] } const mockRedirect = vi.fn() getRedirection(app, mockRedirect) diff --git a/web/utils/app-redirection.ts b/web/utils/app-redirection.ts index d73a350b3f3..4bdfce9a266 100644 --- a/web/utils/app-redirection.ts +++ b/web/utils/app-redirection.ts @@ -34,9 +34,7 @@ export const getRedirectionPath = ( if (app.mode === AppModeEnum.WORKFLOW && appACLCapabilities.canDeploy) return `/app/${app.id}/deploy` - if (appACLCapabilities.canAccessPoint) return `/app/${app.id}/access-point` - - return '/apps' + return `/app/${app.id}/access-point` } export const getRedirection = ( diff --git a/web/utils/permission.spec.ts b/web/utils/permission.spec.ts index 487c9c891b1..75dff3e7afe 100644 --- a/web/utils/permission.spec.ts +++ b/web/utils/permission.spec.ts @@ -50,15 +50,6 @@ describe('permission', () => { expect(releaseCapabilities.canDeploy).toBe(false) }) - it('keeps access point permission independent from other app ACL permissions', () => { - const accessPointCapabilities = getAppACLCapabilities([AppACLPermission.AccessPoint]) - const layoutCapabilities = getAppACLCapabilities([AppACLPermission.ViewLayout]) - - expect(accessPointCapabilities.canAccessPoint).toBe(true) - expect(accessPointCapabilities.canAccessLayout).toBe(false) - expect(layoutCapabilities.canAccessPoint).toBe(false) - }) - it('keeps monitor, tracing config, and log/annotation permissions independent', () => { const monitorCapabilities = getAppACLCapabilities([AppACLPermission.Monitor]) const tracingCapabilities = getAppACLCapabilities([AppACLPermission.TracingConfig]) @@ -118,7 +109,6 @@ describe('permission', () => { }) expect(capabilities.canViewLayout).toBe(true) - expect(capabilities.canAccessPoint).toBe(true) expect(capabilities.canTestAndRun).toBe(true) expect(capabilities.canEdit).toBe(true) expect(capabilities.canImportExportDSL).toBe(true) diff --git a/web/utils/permission.ts b/web/utils/permission.ts index 30b0b3e0cbe..063878b6607 100644 --- a/web/utils/permission.ts +++ b/web/utils/permission.ts @@ -2,7 +2,6 @@ import type { PermissionKey } from '@/models/access-control' export const AppACLPermission = { Preview: 'app.acl.preview', - AccessPoint: 'app.acl.access_point_manage', ViewLayout: 'app.acl.view_layout', TestAndRun: 'app.acl.test_and_run', Edit: 'app.acl.edit', @@ -39,7 +38,6 @@ export type ResourceMaintainerPermissionOptions = { } type AppACLCapabilities = { - canAccessPoint: boolean canViewLayout: boolean canTestAndRun: boolean canEdit: boolean @@ -137,11 +135,6 @@ export const getAppACLCapabilities = ( ) return { - canAccessPoint: hasResourcePermission( - permissionKeys, - AppACLPermission.AccessPoint, - hasMaintainerPermissions, - ), canViewLayout, canTestAndRun, canEdit, From 520e5e2bebb9b54a1d81d7d7056a1c893c0701e3 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Tue, 1 Sep 2026 04:19:06 +0000 Subject: [PATCH 10/33] chore(deps): upgrade workspace dependencies (#41564) --- .github/workflows/translate-i18n-claude.yml | 2 +- package.json | 2 +- pnpm-lock.yaml | 2328 +++++++++-------- pnpm-workspace.yaml | 68 +- .../markdown-blocks/__tests__/img.spec.tsx | 2 +- .../prompt-editor/__tests__/slash.spec.tsx | 2 +- .../skills/__tests__/detail-page.spec.tsx | 2 +- web/features/skills/__tests__/page.spec.tsx | 2 +- .../detail/__tests__/file-tree-items.spec.tsx | 2 +- .../detail/__tests__/markdown-editor.spec.tsx | 2 +- .../detail/__tests__/publish-bar.spec.tsx | 2 +- .../detail/__tests__/reference-chip.spec.ts | 2 +- .../skills/detail/__tests__/shared.spec.ts | 2 +- .../skills/detail/__tests__/shell.spec.tsx | 2 +- .../detail/__tests__/upload-workflow.spec.ts | 2 +- .../__tests__/skill-card-tags.spec.tsx | 2 +- .../__tests__/plural-selector.spec.ts | 2 +- web/next.config.ts | 5 - 18 files changed, 1270 insertions(+), 1161 deletions(-) diff --git a/.github/workflows/translate-i18n-claude.yml b/.github/workflows/translate-i18n-claude.yml index 20446914e6e..e24e98f356f 100644 --- a/.github/workflows/translate-i18n-claude.yml +++ b/.github/workflows/translate-i18n-claude.yml @@ -162,7 +162,7 @@ jobs: - name: Run Claude Code for Translation Sync if: steps.context.outputs.CHANGED_FILES != '' - uses: anthropics/claude-code-action@a874e9ecd7bb36efdad65429c6b35815f5a08f10 # v1.0.210 + uses: anthropics/claude-code-action@833fb0f8c9f6686b33d963a8bae0a94f4936ab2a # v1.0.211 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/package.json b/package.json index 88fed453dd4..a1efeaf28b9 100644 --- a/package.json +++ b/package.json @@ -63,5 +63,5 @@ "engines": { "node": "^22.22.1" }, - "packageManager": "pnpm@11.23.0" + "packageManager": "pnpm@11.25.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 114f415e7a1..ca6b1e758eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,11 +7,11 @@ settings: catalogs: default: '@amplitude/analytics-browser': - specifier: 2.45.6 - version: 2.45.6 + specifier: 2.45.8 + version: 2.45.8 '@amplitude/plugin-session-replay-browser': - specifier: 1.33.8 - version: 1.33.8 + specifier: 1.35.0 + version: 1.35.0 '@axe-core/playwright': specifier: 4.13.0 version: 4.13.0 @@ -79,8 +79,8 @@ catalogs: specifier: 0.47.0 version: 0.47.0 '@mediabunny/mp3-encoder': - specifier: 1.55.2 - version: 1.55.2 + specifier: 1.55.5 + version: 1.55.5 '@monaco-editor/react': specifier: 4.7.0 version: 4.7.0 @@ -109,8 +109,8 @@ catalogs: specifier: 4.2.3 version: 4.2.3 '@sentry/react': - specifier: 10.70.0 - version: 10.70.0 + specifier: 10.73.0 + version: 10.73.0 '@storybook/addon-a11y': specifier: 10.5.10 version: 10.5.10 @@ -157,14 +157,14 @@ catalogs: specifier: 4.3.3 version: 4.3.3 '@tanstack/eslint-plugin-query': - specifier: 5.102.2 - version: 5.102.2 + specifier: 5.102.8 + version: 5.102.8 '@tanstack/form-core': specifier: 1.33.5 version: 1.33.5 '@tanstack/query-core': - specifier: 5.102.2 - version: 5.102.2 + specifier: 5.102.8 + version: 5.102.8 '@tanstack/react-form': specifier: 1.33.5 version: 1.33.5 @@ -172,8 +172,8 @@ catalogs: specifier: 0.10.0 version: 0.10.0 '@tanstack/react-query': - specifier: 5.102.2 - version: 5.102.2 + specifier: 5.102.8 + version: 5.102.8 '@tanstack/react-virtual': specifier: 3.14.10 version: 3.14.10 @@ -184,8 +184,8 @@ catalogs: specifier: 6.9.1 version: 6.9.1 '@testing-library/react': - specifier: 16.3.2 - version: 16.3.2 + specifier: 16.3.3 + version: 16.3.3 '@testing-library/user-event': specifier: 14.6.6 version: 14.6.6 @@ -223,14 +223,14 @@ catalogs: specifier: 1.15.9 version: 1.15.9 '@typescript-eslint/parser': - specifier: 8.67.0 - version: 8.67.0 + specifier: 8.69.0 + version: 8.69.0 '@typescript/native': specifier: npm:typescript@7.0.2 version: 7.0.2 '@vitejs/plugin-react': - specifier: 6.1.0 - version: 6.1.0 + specifier: 6.1.1 + version: 6.1.1 '@vitejs/plugin-rsc': specifier: 0.5.34 version: 0.5.34 @@ -307,11 +307,11 @@ catalogs: specifier: 5.6.0 version: 5.6.0 es-toolkit: - specifier: 1.51.0 - version: 1.51.0 + specifier: 1.52.0 + version: 1.52.0 eslint: - specifier: 10.9.0 - version: 10.9.0 + specifier: 10.9.1 + version: 10.9.1 eslint-markdown: specifier: 0.12.1 version: 0.12.1 @@ -343,8 +343,8 @@ catalogs: specifier: 1.3.1 version: 1.3.1 eslint-plugin-perfectionist: - specifier: 5.10.1 - version: 5.10.1 + specifier: 5.11.0 + version: 5.11.0 eslint-plugin-pnpm: specifier: 1.8.0 version: 1.8.0 @@ -370,20 +370,20 @@ catalogs: specifier: 3.1.3 version: 3.1.3 foxact: - specifier: 0.3.9 - version: 0.3.9 + specifier: 0.3.10 + version: 0.3.10 fuse.js: specifier: 7.5.0 version: 7.5.0 happy-dom: - specifier: 20.11.6 - version: 20.11.6 + specifier: 20.12.0 + version: 20.12.0 hast-util-to-jsx-runtime: specifier: 2.3.6 version: 2.3.6 hono: - specifier: 4.13.3 - version: 4.13.3 + specifier: 4.13.5 + version: 4.13.5 html-entities: specifier: 2.6.0 version: 2.6.0 @@ -403,8 +403,8 @@ catalogs: specifier: 11.1.18 version: 11.1.18 jotai: - specifier: 2.20.2 - version: 2.20.2 + specifier: 2.20.3 + version: 2.20.3 jotai-scope: specifier: 0.11.0 version: 0.11.0 @@ -415,8 +415,8 @@ catalogs: specifier: 3.0.8 version: 3.0.8 js-yaml: - specifier: 5.3.0 - version: 5.3.0 + specifier: 5.4.1 + version: 5.4.1 jsonschema: specifier: 1.5.0 version: 1.5.0 @@ -424,11 +424,11 @@ catalogs: specifier: 0.17.0 version: 0.17.0 knip: - specifier: 6.32.2 - version: 6.32.2 + specifier: 6.34.0 + version: 6.34.0 ky: - specifier: 2.0.2 - version: 2.0.2 + specifier: 2.1.0 + version: 2.1.0 lexical: specifier: 0.47.0 version: 0.47.0 @@ -436,14 +436,14 @@ catalogs: specifier: 1.0.4 version: 1.0.4 loro-crdt: - specifier: 1.14.1 - version: 1.14.1 + specifier: 1.15.1 + version: 1.15.1 mediabunny: - specifier: 1.55.2 - version: 1.55.2 + specifier: 1.55.5 + version: 1.55.5 mermaid: - specifier: 11.17.0 - version: 11.17.0 + specifier: 11.17.2 + version: 11.17.2 mime: specifier: 4.1.0 version: 4.1.0 @@ -457,17 +457,17 @@ catalogs: specifier: 1.1.0 version: 1.1.0 next: - specifier: 16.3.3 - version: 16.3.3 + specifier: 16.3.4 + version: 16.3.4 next-themes: specifier: 0.4.6 version: 0.4.6 nuqs: - specifier: 2.10.0 - version: 2.10.0 + specifier: 2.10.1 + version: 2.10.1 open: - specifier: 11.0.1 - version: 11.0.1 + specifier: 11.0.2 + version: 11.0.2 ora: specifier: 9.4.1 version: 9.4.1 @@ -487,8 +487,8 @@ catalogs: specifier: 4.2.0 version: 4.2.0 qs: - specifier: 6.15.3 - version: 6.15.3 + specifier: 6.16.0 + version: 6.16.0 react: specifier: 19.2.8 version: 19.2.8 @@ -532,8 +532,8 @@ catalogs: specifier: 0.0.1 version: 0.0.1 shiki: - specifier: 4.4.2 - version: 4.4.2 + specifier: 4.4.3 + version: 4.4.3 socket.io-client: specifier: 4.8.3 version: 4.8.3 @@ -547,8 +547,8 @@ catalogs: specifier: 10.5.10 version: 10.5.10 streamdown: - specifier: 2.5.0 - version: 2.5.0 + specifier: 2.6.0 + version: 2.6.0 string-ts: specifier: 2.3.1 version: 2.3.1 @@ -559,11 +559,11 @@ catalogs: specifier: 4.3.3 version: 4.3.3 tldts: - specifier: 7.4.10 - version: 7.4.10 + specifier: 7.4.11 + version: 7.4.11 tsx: - specifier: 4.23.12 - version: 4.23.12 + specifier: 4.23.13 + version: 4.23.13 typescript: specifier: npm:@typescript/typescript6@6.0.2 version: 6.0.2 @@ -604,8 +604,8 @@ catalogs: specifier: 1.1.5 version: 1.1.5 zod: - specifier: 4.4.3 - version: 4.4.3 + specifier: 4.5.4 + version: 4.5.4 zundo: specifier: 2.3.0 version: 2.3.0 @@ -622,11 +622,11 @@ overrides: esbuild@>=0.27.3 <0.28.1: ^0.28.2 is-core-module: npm:@nolyfill/is-core-module@^1.0.39 js-yaml@<=4.1.1: ^4.1.2 - picomatch@>=4.0.0 <4.0.4: 4.0.5 + picomatch@>=4.0.0 <4.0.4: 4.0.7 postcss-selector-parser@>=6.0.0 <6.1.3: 6.1.4 postcss-selector-parser@>=7.0.0 <7.1.3: 7.1.5 postcss@<8.5.10: ^8.5.10 - rollup@>=4.0.0 <4.59.0: 4.62.5 + rollup@>=4.0.0 <4.59.0: 4.63.1 safer-buffer: npm:@nolyfill/safer-buffer@^1.0.44 side-channel: npm:@nolyfill/side-channel@^1.0.44 solid-js: 1.9.15 @@ -644,10 +644,10 @@ importers: devDependencies: '@eslint-community/eslint-plugin-eslint-comments': specifier: 'catalog:' - version: 4.7.2(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + version: 4.7.2(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) '@eslint-react/eslint-plugin': specifier: 'catalog:' - version: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + version: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) '@eslint/markdown': specifier: 'catalog:' version: 8.0.3(supports-color@11.0.0) @@ -659,13 +659,13 @@ importers: version: 1.2.10 '@tanstack/eslint-plugin-query': specifier: 'catalog:' - version: 5.102.2(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + version: 5.102.8(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) '@types/node': specifier: 'catalog:' version: 25.9.5 '@typescript-eslint/parser': specifier: 'catalog:' - version: 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + version: 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) '@typescript/native': specifier: 'catalog:' version: typescript@7.0.2 @@ -674,61 +674,61 @@ importers: version: 10.0.5 eslint: specifier: 'catalog:' - version: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + version: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) eslint-markdown: specifier: 'catalog:' - version: 0.12.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + version: 0.12.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) eslint-plugin-antfu: specifier: 'catalog:' - version: 3.2.3(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + version: 3.2.3(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) eslint-plugin-better-tailwindcss: specifier: 'catalog:' - version: 4.7.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(oxlint@1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(tailwindcss@4.3.3) + version: 4.7.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(oxlint@1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(tailwindcss@4.3.3) eslint-plugin-command: specifier: 'catalog:' - version: 3.5.3(@typescript-eslint/typescript-estree@8.67.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0))(@typescript-eslint/utils@8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + version: 3.5.3(@typescript-eslint/typescript-estree@8.69.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0))(@typescript-eslint/utils@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) eslint-plugin-erasable-syntax-only: specifier: 'catalog:' - version: 0.4.2(@typescript-eslint/parser@8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + version: 0.4.2(@typescript-eslint/parser@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) eslint-plugin-jsdoc: specifier: 'catalog:' - version: 63.3.3(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + version: 63.3.3(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) eslint-plugin-jsonc: specifier: 'catalog:' - version: 3.4.2(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + version: 3.4.2(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) eslint-plugin-markdown-preferences: specifier: 'catalog:' - version: 0.41.1(@eslint/markdown@8.0.3(supports-color@11.0.0))(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + version: 0.41.1(@eslint/markdown@8.0.3(supports-color@11.0.0))(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) eslint-plugin-n: specifier: 'catalog:' - version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) eslint-plugin-no-barrel-files: specifier: 'catalog:' - version: 1.3.1(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + version: 1.3.1(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) eslint-plugin-perfectionist: specifier: 'catalog:' - version: 5.10.1(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + version: 5.11.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) eslint-plugin-pnpm: specifier: 'catalog:' - version: 1.8.0(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + version: 1.8.0(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) eslint-plugin-regexp: specifier: 'catalog:' - version: 3.1.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + version: 3.1.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) eslint-plugin-storybook: specifier: 'catalog:' - version: 10.5.10(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + version: 10.5.10(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) eslint-plugin-toml: specifier: 'catalog:' - version: 1.5.0(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + version: 1.5.0(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) eslint-plugin-unicorn: specifier: 'catalog:' - version: 71.1.0(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + version: 71.1.0(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) eslint-plugin-yml: specifier: 'catalog:' - version: 3.8.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + version: 3.8.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) knip: specifier: 'catalog:' - version: 6.32.2 + version: 6.34.0 node: specifier: runtime:^22.22.1 version: runtime:22.23.2 @@ -737,10 +737,10 @@ importers: version: '@typescript/typescript6@6.0.2' vite: specifier: 'catalog:' - version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) + version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) cli: dependencies: @@ -767,13 +767,13 @@ importers: version: 3.1.1 js-yaml: specifier: 'catalog:' - version: 5.3.0 + version: 5.4.1 lockfile: specifier: 'catalog:' version: 1.0.4 open: specifier: 'catalog:' - version: 11.0.1 + version: 11.0.2 ora: specifier: 'catalog:' version: 9.4.1 @@ -788,14 +788,14 @@ importers: version: 7.29.0 zod: specifier: 'catalog:' - version: 4.4.3 + version: 4.5.4 devDependencies: '@dify/tsconfig': specifier: workspace:* version: link:../packages/tsconfig '@hono/node-server': specifier: 'catalog:' - version: 2.1.1(hono@4.13.3) + version: 2.1.1(hono@4.13.5) '@types/lockfile': specifier: 'catalog:' version: 1.0.4 @@ -810,19 +810,19 @@ importers: version: 4.1.11(@vitest/browser@4.1.11)(vitest@4.1.11) hono: specifier: 'catalog:' - version: 4.13.3 + version: 4.13.5 typescript: specifier: 'catalog:' version: '@typescript/typescript6@6.0.2' vite: specifier: 'catalog:' - version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) + version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) e2e: devDependencies: @@ -852,7 +852,7 @@ importers: version: 1.62.1 '@t3-oss/env-core': specifier: 'catalog:' - version: 0.13.11(@typescript/typescript6@6.0.2)(valibot@1.4.2(@typescript/typescript6@6.0.2))(zod@4.4.3) + version: 0.13.11(@typescript/typescript6@6.0.2)(valibot@1.4.2(@typescript/typescript6@6.0.2))(zod@4.5.4) '@types/node': specifier: 'catalog:' version: 25.9.5 @@ -864,22 +864,22 @@ importers: version: 3.1.1 tsx: specifier: 'catalog:' - version: 4.23.12 + version: 4.23.13 typescript: specifier: 'catalog:' version: '@typescript/typescript6@6.0.2' vite: specifier: 'catalog:' - version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) + version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) zod: specifier: 'catalog:' - version: 4.4.3 + version: 4.5.4 packages/contracts: dependencies: @@ -888,7 +888,7 @@ importers: version: 1.15.0 zod: specifier: 'catalog:' - version: 4.4.3 + version: 4.5.4 devDependencies: '@dify/tsconfig': specifier: workspace:* @@ -904,25 +904,25 @@ importers: version: typescript@7.0.2 js-yaml: specifier: 'catalog:' - version: 5.3.0 + version: 5.4.1 typescript: specifier: 'catalog:' version: '@typescript/typescript6@6.0.2' vite: specifier: 'catalog:' - version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) + version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) packages/dev-proxy: dependencies: '@hono/node-server': specifier: 'catalog:' - version: 2.1.1(hono@4.13.3) + version: 2.1.1(hono@4.13.5) c12: specifier: 'catalog:' version: 4.0.0-beta.5(chokidar@5.0.0)(dotenv@17.4.2)(giget@3.3.0)(jiti@2.7.0)(magicast@0.5.3) @@ -931,7 +931,7 @@ importers: version: 5.0.0 hono: specifier: 'catalog:' - version: 4.13.3 + version: 4.13.5 devDependencies: '@dify/tsconfig': specifier: workspace:* @@ -944,13 +944,13 @@ importers: version: typescript@7.0.2 vite: specifier: 'catalog:' - version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0)' + version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0)' vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0) + version: 0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(happy-dom@20.11.6) + version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(happy-dom@20.12.0) packages/dify-ui: dependencies: @@ -966,7 +966,7 @@ importers: version: 1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@chromatic-com/storybook': specifier: 'catalog:' - version: 5.3.0(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + version: 5.3.0(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) '@dify/tsconfig': specifier: workspace:* version: link:../tsconfig @@ -978,25 +978,25 @@ importers: version: 1.2.10 '@storybook/addon-a11y': specifier: 'catalog:' - version: 10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + version: 10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) '@storybook/addon-docs': specifier: 'catalog:' - version: 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + version: 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) '@storybook/addon-links': specifier: 'catalog:' - version: 10.5.10(@types/react@19.2.18)(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + version: 10.5.10(@types/react@19.2.18)(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) '@storybook/addon-themes': specifier: 'catalog:' - version: 10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + version: 10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) '@storybook/addon-vitest': specifier: 'catalog:' - version: 10.5.10(@vitest/browser-playwright@4.1.11)(@vitest/browser@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.11))(@vitest/runner@4.1.11)(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(vitest@4.1.11) + version: 10.5.10(@vitest/browser-playwright@4.1.11)(@vitest/browser@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@4.1.11))(@vitest/runner@4.1.11)(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(vitest@4.1.11) '@storybook/react-vite': specifier: 'catalog:' - version: 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(supports-color@11.0.0) + version: 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(supports-color@11.0.0) '@tailwindcss/vite': specifier: 'catalog:' - version: 4.3.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + version: 4.3.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) '@tanstack/react-hotkeys': specifier: 'catalog:' version: 0.10.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -1014,10 +1014,10 @@ importers: version: typescript@7.0.2 '@vitejs/plugin-react': specifier: 'catalog:' - version: 6.1.0(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + version: 6.1.1(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/browser-playwright': specifier: 'catalog:' - version: 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) + version: 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) '@vitest/coverage-v8': specifier: 'catalog:' version: 4.1.11(@vitest/browser@4.1.11)(vitest@4.1.11) @@ -1035,7 +1035,7 @@ importers: version: 19.2.8(react@19.2.8) storybook: specifier: 'catalog:' - version: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + version: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) tailwindcss: specifier: 'catalog:' version: 4.3.3 @@ -1044,13 +1044,13 @@ importers: version: '@typescript/typescript6@6.0.2' vite: specifier: 'catalog:' - version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) + version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) vitest-browser-react: specifier: 'catalog:' version: 2.2.0(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.11) @@ -1062,7 +1062,7 @@ importers: version: 0.2.0(supports-color@11.0.0) tsx: specifier: 'catalog:' - version: 4.23.12 + version: 4.23.13 packages/jotai-tanstack-form: devDependencies: @@ -1077,19 +1077,19 @@ importers: version: typescript@7.0.2 jotai: specifier: 'catalog:' - version: 2.20.2(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8) + version: 2.20.3(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8) typescript: specifier: 'catalog:' version: '@typescript/typescript6@6.0.2' vite: specifier: 'catalog:' - version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) + version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) packages/migrate-no-unchecked-indexed-access: dependencies: @@ -1108,10 +1108,10 @@ importers: version: 25.9.5 vite: specifier: 'catalog:' - version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) + version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) packages/tsconfig: {} @@ -1134,22 +1134,22 @@ importers: version: '@typescript/typescript6@6.0.2' vite: specifier: 'catalog:' - version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) + version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) web: dependencies: '@amplitude/analytics-browser': specifier: 'catalog:' - version: 2.45.6 + version: 2.45.8 '@amplitude/plugin-session-replay-browser': specifier: 'catalog:' - version: 1.33.8(@amplitude/rrweb@2.1.1) + version: 1.35.0(@amplitude/rrweb@2.1.1) '@base-ui/react': specifier: 'catalog:' version: 1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -1188,7 +1188,7 @@ importers: version: 0.47.0(@typescript/typescript6@6.0.2) '@mediabunny/mp3-encoder': specifier: 'catalog:' - version: 1.55.2(mediabunny@1.55.2) + version: 1.55.5(mediabunny@1.55.5) '@monaco-editor/react': specifier: 'catalog:' version: 4.7.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -1203,13 +1203,13 @@ importers: version: 1.15.0 '@orpc/tanstack-query': specifier: 'catalog:' - version: 1.15.0(@orpc/client@1.15.0)(@tanstack/query-core@5.102.2) + version: 1.15.0(@orpc/client@1.15.0)(@tanstack/query-core@5.102.8) '@remixicon/react': specifier: 'catalog:' version: 4.9.0(react@19.2.8) '@sentry/react': specifier: 'catalog:' - version: 10.70.0(react@19.2.8) + version: 10.73.0(react@19.2.8) '@streamdown/math': specifier: 'catalog:' version: 1.0.2(react@19.2.8)(supports-color@11.0.0) @@ -1218,10 +1218,10 @@ importers: version: 3.2.8 '@t3-oss/env-nextjs': specifier: 'catalog:' - version: 0.13.11(@typescript/typescript6@6.0.2)(valibot@1.4.2(@typescript/typescript6@6.0.2))(zod@4.4.3) + version: 0.13.11(@typescript/typescript6@6.0.2)(valibot@1.4.2(@typescript/typescript6@6.0.2))(zod@4.5.4) '@tanstack/query-core': specifier: 'catalog:' - version: 5.102.2 + version: 5.102.8 '@tanstack/react-form': specifier: 'catalog:' version: 1.33.5(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -1230,7 +1230,7 @@ importers: version: 0.10.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@tanstack/react-query': specifier: 'catalog:' - version: 5.102.2(react@19.2.8) + version: 5.102.8(react@19.2.8) '@tanstack/react-virtual': specifier: 'catalog:' version: 3.14.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -1281,13 +1281,13 @@ importers: version: 5.6.0 es-toolkit: specifier: 'catalog:' - version: 1.51.0 + version: 1.52.0 fast-deep-equal: specifier: 'catalog:' version: 3.1.3 foxact: specifier: 'catalog:' - version: 0.3.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 0.3.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8) fuse.js: specifier: 'catalog:' version: 7.5.0 @@ -1311,19 +1311,19 @@ importers: version: 11.1.18 jotai: specifier: 'catalog:' - version: 2.20.2(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8) + version: 2.20.3(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8) jotai-scope: specifier: 'catalog:' - version: 0.11.0(jotai@2.20.2(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8))(react@19.2.8) + version: 0.11.0(jotai@2.20.3(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8))(react@19.2.8) jotai-tanstack-query: specifier: 'catalog:' - version: 0.11.0(@tanstack/query-core@5.102.2)(@tanstack/react-query@5.102.2(react@19.2.8))(jotai@2.20.2(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8))(react@19.2.8) + version: 0.11.0(@tanstack/query-core@5.102.8)(@tanstack/react-query@5.102.8(react@19.2.8))(jotai@2.20.3(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8))(react@19.2.8) js-cookie: specifier: 'catalog:' version: 3.0.8 js-yaml: specifier: 'catalog:' - version: 5.3.0 + version: 5.4.1 jsonschema: specifier: 'catalog:' version: 1.5.0 @@ -1332,19 +1332,19 @@ importers: version: 0.17.0 ky: specifier: 'catalog:' - version: 2.0.2 + version: 2.1.0 lexical: specifier: 'catalog:' version: 0.47.0(@typescript/typescript6@6.0.2) loro-crdt: specifier: 'catalog:' - version: 1.14.1 + version: 1.15.1 mediabunny: specifier: 'catalog:' - version: 1.55.2 + version: 1.55.5 mermaid: specifier: 'catalog:' - version: 11.17.0 + version: 11.17.2 mime: specifier: 'catalog:' version: 4.1.0 @@ -1359,13 +1359,13 @@ importers: version: 1.1.0 next: specifier: 'catalog:' - version: 16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) next-themes: specifier: 'catalog:' version: 0.4.6(react-dom@19.2.8(react@19.2.8))(react@19.2.8) nuqs: specifier: 'catalog:' - version: 2.10.0(next@16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) + version: 2.10.1(next@16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) pinyin-pro: specifier: 'catalog:' version: 3.29.3 @@ -1374,7 +1374,7 @@ importers: version: 4.2.0(react@19.2.8) qs: specifier: 'catalog:' - version: 6.15.3 + version: 6.16.0 react: specifier: 'catalog:' version: 19.2.8 @@ -1416,7 +1416,7 @@ importers: version: 0.0.1 shiki: specifier: 'catalog:' - version: 4.4.2 + version: 4.4.3 socket.io-client: specifier: 'catalog:' version: 4.8.3(supports-color@11.0.0) @@ -1428,13 +1428,13 @@ importers: version: 1.0.8 streamdown: specifier: 'catalog:' - version: 2.5.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@11.0.0) + version: 2.6.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@11.0.0) string-ts: specifier: 'catalog:' version: 2.3.1 tldts: specifier: 'catalog:' - version: 7.4.10 + version: 7.4.11 unist-util-visit: specifier: 'catalog:' version: 5.1.0 @@ -1446,7 +1446,7 @@ importers: version: 14.0.2 zod: specifier: 'catalog:' - version: 4.4.3 + version: 4.5.4 zundo: specifier: 'catalog:' version: 2.3.0(zustand@5.0.15(@types/react@19.2.18)(immer@11.1.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8))) @@ -1456,7 +1456,7 @@ importers: devDependencies: '@chromatic-com/storybook': specifier: 'catalog:' - version: 5.3.0(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + version: 5.3.0(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) '@dify/contracts': specifier: workspace:* version: link:../packages/contracts @@ -1486,28 +1486,28 @@ importers: version: 4.2.3 '@storybook/addon-docs': specifier: 'catalog:' - version: 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + version: 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) '@storybook/addon-links': specifier: 'catalog:' - version: 10.5.10(@types/react@19.2.18)(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + version: 10.5.10(@types/react@19.2.18)(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) '@storybook/addon-onboarding': specifier: 'catalog:' - version: 10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + version: 10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) '@storybook/addon-themes': specifier: 'catalog:' - version: 10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + version: 10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) '@storybook/nextjs-vite': specifier: 'catalog:' - version: 10.5.10(@babel/core@7.29.7(supports-color@11.0.0))(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(next@16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(supports-color@11.0.0) + version: 10.5.10(@babel/core@7.29.7(supports-color@11.0.0))(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(next@16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(supports-color@11.0.0) '@storybook/react': specifier: 'catalog:' - version: 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(supports-color@11.0.0) + version: 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(supports-color@11.0.0) '@tailwindcss/postcss': specifier: 'catalog:' version: 4.3.3 '@tailwindcss/vite': specifier: 'catalog:' - version: 4.3.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + version: 4.3.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) '@testing-library/dom': specifier: 'catalog:' version: 10.4.1 @@ -1516,7 +1516,7 @@ importers: version: 6.9.1 '@testing-library/react': specifier: 'catalog:' - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@testing-library/user-event': specifier: 'catalog:' version: 14.6.6(@testing-library/dom@10.4.1) @@ -1555,13 +1555,13 @@ importers: version: typescript@7.0.2 '@vitejs/plugin-react': specifier: 'catalog:' - version: 6.1.0(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + version: 6.1.1(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitejs/plugin-rsc': specifier: 'catalog:' - version: 0.5.34(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) + version: 0.5.34(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) '@vitest/browser-playwright': specifier: 'catalog:' - version: 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) + version: 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) '@vitest/coverage-v8': specifier: 'catalog:' version: 4.1.11(@vitest/browser@4.1.11)(vitest@4.1.11) @@ -1573,7 +1573,7 @@ importers: version: 1.6.6(supports-color@11.0.0) happy-dom: specifier: 'catalog:' - version: 20.11.6 + version: 20.12.0 playwright: specifier: 'catalog:' version: 1.62.1 @@ -1585,13 +1585,13 @@ importers: version: 19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8) storybook: specifier: 'catalog:' - version: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + version: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) tailwindcss: specifier: 'catalog:' version: 4.3.3 tsx: specifier: 'catalog:' - version: 4.23.12 + version: 4.23.13 typescript: specifier: 'catalog:' version: '@typescript/typescript6@6.0.2' @@ -1600,19 +1600,19 @@ importers: version: 3.19.3 vinext: specifier: 'catalog:' - version: 1.0.0-beta.8(@vitejs/plugin-react@6.1.0(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(@vitejs/plugin-rsc@0.5.34(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8))(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(next@16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) + version: 1.0.0-beta.8(@vitejs/plugin-react@6.1.1(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(@vitejs/plugin-rsc@0.5.34(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8))(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(next@16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) vite: specifier: 'catalog:' - version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' vite-plugin-inspect: specifier: 'catalog:' - version: 12.0.2(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(srvx@0.12.5) + version: 12.0.2(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(srvx@0.12.5) vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) + version: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + version: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) vitest-browser-react: specifier: 'catalog:' version: 2.2.0(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.11) @@ -1629,50 +1629,47 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@amplitude/analytics-browser@2.45.6': - resolution: {integrity: sha512-sq03ROnttLP6i3I8LXGkAqtZAoi2Y7k5Mhx3PLYiB0mwTPCcbYaW4CyR0V52NI+7BFcpsD8DxF2ZeCoxBdWGqQ==} - - '@amplitude/analytics-client-common@2.4.60': - resolution: {integrity: sha512-YdvEsxkqPk4rkw3zdJGP92wE+CQwjqDU0hI3eX/GABqivZ8HZ8VBmot7WyZPOSzGSr+FxuwwC3ZpVTY5K8OEjQ==} + '@amplitude/analytics-browser@2.45.8': + resolution: {integrity: sha512-BdRLCcKGwQq1+1x2QOdHu5PJDMkIr+fvbbcQwO6lCsTCG44PzpL38n5jW9JNDhP88nHoz+lKyNkLDN3yd98pJA==} '@amplitude/analytics-connector@1.6.4': resolution: {integrity: sha512-SpIv0IQMNIq6SH3UqFGiaZyGSc7PBZwRdq7lvP0pBxW8i4Ny+8zwI0pV+VMfMHQwWY3wdIbWw5WQphNjpdq1/Q==} - '@amplitude/analytics-core@2.54.2': - resolution: {integrity: sha512-kXFatnKxQMABvr9D43BV0C6TI4gAdksXS925e9dvAbu76QwLQhWYttJHCXMdC4jYn7+QdshhCNFp6677mQNSLA==} + '@amplitude/analytics-core@2.55.0': + resolution: {integrity: sha512-fzjXEWX2o5I58Tl5jYaw2G5Tp5ejfcDkayWMogEThCl/fd6AqRKkrl/dVVqGV3h/GS3HZws7J4baIWkaq92jkQ==} '@amplitude/analytics-types@2.11.1': resolution: {integrity: sha512-wFEgb0t99ly2uJKm5oZ28Lti0Kh5RecR5XBkwfUpDzn84IoCIZ8GJTsMw/nThu8FZFc7xFDA4UAt76zhZKrs9A==} - '@amplitude/element-selector@0.2.1': - resolution: {integrity: sha512-QZYvhO2cyaw6B7gFWu95QrE/l2a5oSCHgYR2S6DqZ6/NVDWIzbBtEjwLcxZFPXbO+DM1Rng6GUNljMNFtdlFDw==} + '@amplitude/element-selector@0.3.0': + resolution: {integrity: sha512-rfjXGjXqQiitxse3ZcoPLny2Uy8AXYnWAqB6oqZj39zYVuieoxhfWT50HZks3F4Ny4Zyf45tJqyLCnfvGGMs4A==} '@amplitude/experiment-core@0.7.2': resolution: {integrity: sha512-Wc2NWvgQ+bLJLeF0A9wBSPIaw0XuqqgkPKsoNFQrmS7r5Djd56um75In05tqmVntPJZRvGKU46pAp8o5tdf4mA==} - '@amplitude/plugin-autocapture-browser@1.28.11': - resolution: {integrity: sha512-hmdzNrWgFt0y35RHUxohJx2jgaN7m/bJUvSWv4hIz4LZ8yRA0cPfPnWhdF58HaTnpk7GjplwOvxDcjO1BBHCRQ==} + '@amplitude/plugin-autocapture-browser@1.29.0': + resolution: {integrity: sha512-S+9qjMV0jGu7KoYjPeLoeGavKjdjyNqx2gMLnJQQMTi1HaaOZZFT0Y36TyRfbYuqVL3osSe3aUnoejmuDTx14w==} - '@amplitude/plugin-custom-enrichment-browser@0.1.21': - resolution: {integrity: sha512-B5JxPtA8/Bt6Ypdx9p44Y/yyDQOINyQk0MbEeiN/OGU4kkbgiPgSMNe7X4QxDlXWtViYOwGn25RrIQyXnajjwA==} + '@amplitude/plugin-custom-enrichment-browser@0.1.22': + resolution: {integrity: sha512-KDTVCOmivrLzp1Q9EJuU61Yzw0Mip9DU/VnMnfROD37rCv0QX78O7RuYVzN+R6sprgUw+tkmS512PmkbVLOFsA==} - '@amplitude/plugin-event-property-attribution-browser@0.2.13': - resolution: {integrity: sha512-7T8Ci3p8o/9UVhyeHXhWQossPVTgou8KwFAD8ntVKUkN9PvZD6I2YJgFWqtmygJfGUGpgv34OcGyAgbofsFiAQ==} + '@amplitude/plugin-event-property-attribution-browser@0.2.14': + resolution: {integrity: sha512-uFuKp4sADFMLgh6vM/+c8RC6AXpMwcYVD9ZKkTjQn6tTz20cct1AFEjnnFc7/n9oT0D8EKvUUE7SsLg+V55QvQ==} - '@amplitude/plugin-network-capture-browser@1.10.13': - resolution: {integrity: sha512-X6KynnE6wxL3dT1A23T5FQzHpKAbR0vRx69yGi3CHFSL1IVZNbQR4MMh7dVg6Ev+JGnJs7tCn+EZ8a5mYD4lIg==} + '@amplitude/plugin-network-capture-browser@1.10.14': + resolution: {integrity: sha512-MGRgT89KaPDvrZXMp+5rWgF5GTC7YkCj/gIgHZ22FgH1CrejwHhA5FWTurW28f48rOgZ+V1Ruqab70/sst4KJQ==} - '@amplitude/plugin-page-url-enrichment-browser@0.7.23': - resolution: {integrity: sha512-c3ISLxTeYlghnyH4axNZh4Jex30QwetYEL5J7kubs3S9MYqwVCFF5RoHTFwhm3WrTjOYJk2W6qWindjXbrS85A==} + '@amplitude/plugin-page-url-enrichment-browser@0.7.24': + resolution: {integrity: sha512-kYwEBh+ssdbdqslGHVV3/m0ao139HWjd4SOwBZ47EQjeYXRDzNAw752iQIr1vx/+zRRdEOLQHO3GS1JGdWSIKw==} - '@amplitude/plugin-page-view-tracking-browser@2.11.13': - resolution: {integrity: sha512-cv+crjWw51mjX0DJ4QRNGZruZC91Lhn5MSdc9XaO6h8YqDokWMbg3wpLzcU2ds0d5l3iep9qfjsx6vzcuBd1uw==} + '@amplitude/plugin-page-view-tracking-browser@2.11.14': + resolution: {integrity: sha512-FgdBz7o0XOwfgXFppIj3O+ENeGYhmkQW6LUOEcKQwR1/kKgT+2GhOLD/u9rWnGXYcjYrWH8jtmr7D62OvlF6Dg==} - '@amplitude/plugin-session-replay-browser@1.33.8': - resolution: {integrity: sha512-M91dPju4qEAPpKd0qN22cH8/QbbKlqdFOtvGZGFIBTozEv+8/bekgfpyxBlTdQaR5+NKx/eUtoneM6xIhUWdfA==} + '@amplitude/plugin-session-replay-browser@1.35.0': + resolution: {integrity: sha512-MZJkDaykiCye9QBWqm+fVf5LO+jMxzL1iovdtYxiKe1LTnbcZ8zBFcy0vpWmSfV688bmEfYrmtgzGJQK5m8M6g==} - '@amplitude/plugin-web-vitals-browser@1.1.45': - resolution: {integrity: sha512-Qsi8gpdfgXalHmio/KplZnSjYPkGbvwQUK9rcCW3vGpZo1+jzFSQVlnxVW/WmZ8ncQAtvhDFhd2xTeqLpAfVjQ==} + '@amplitude/plugin-web-vitals-browser@1.1.46': + resolution: {integrity: sha512-Pko9gf7yDNmiEhGq2aJmJmSFckN155AYJWn/3t8NPQXakzWLagFjmOuxvPx4OefIJtyROU1rYmGM/hwTrBSvBQ==} '@amplitude/rrdom@2.1.0': resolution: {integrity: sha512-2dAtxXL02usBV2CSOnScLd3WoVqWaeiGpxN8LuXJ0r/NpLJkW1k876v2tRKAz5NrxPwSdjihsMmwCIXHpJhHfA==} @@ -1700,8 +1697,8 @@ packages: '@amplitude/rrweb@2.1.1': resolution: {integrity: sha512-6uA+5VE/VHumaXPXTTLGRogd/K9MDwd01jGteppeLzsX0PvqlDyY5aIi35yh9+q1iS6ciPBn/2NRg0lg4cFIlw==} - '@amplitude/session-replay-browser@1.48.2': - resolution: {integrity: sha512-N8NRiUlaEXwNhC25guHDCwhtLCZB5G21gZR7qsXJOtwMIebyd4GeM45+xRxo+77rIFIiiFA/Xx59mEXqGrSNrg==} + '@amplitude/session-replay-browser@1.50.0': + resolution: {integrity: sha512-Cp6jcdKNYyIQ6FrJQ2htLd/0SOarpOTvCFMxK799af2i/Q3/VCcgMInXtSLDO3QRHtfM1b+hLmAx3mPqDDSYcA==} '@amplitude/targeting@0.3.10': resolution: {integrity: sha512-z1Vl10M8qHRPs51/cNBWc8HEo1QrAZZtfKzI9Uuim3hCAE/XlPPZzhIwDfZE1qlgFIXLNq1MeNKJTgdDh7A2hg==} @@ -1949,6 +1946,9 @@ packages: '@emnapi/runtime@1.11.2': resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + '@emnapi/runtime@1.9.2': resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} @@ -2339,160 +2339,160 @@ packages: resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} - '@img/sharp-darwin-arm64@0.35.3': - resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==} + '@img/sharp-darwin-arm64@0.35.4': + resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.35.3': - resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==} + '@img/sharp-darwin-x64@0.35.4': + resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==} engines: {node: '>=20.9.0'} cpu: [x64] os: [darwin] - '@img/sharp-freebsd-wasm32@0.35.3': - resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==} + '@img/sharp-freebsd-wasm32@0.35.4': + resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==} engines: {node: '>=20.9.0'} os: [freebsd] - '@img/sharp-libvips-darwin-arm64@1.3.2': - resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==} + '@img/sharp-libvips-darwin-arm64@1.3.3': + resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.3.2': - resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==} + '@img/sharp-libvips-darwin-x64@1.3.3': + resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.3.2': - resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==} + '@img/sharp-libvips-linux-arm64@1.3.3': + resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.3.2': - resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==} + '@img/sharp-libvips-linux-arm@1.3.3': + resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-ppc64@1.3.2': - resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==} + '@img/sharp-libvips-linux-ppc64@1.3.3': + resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-riscv64@1.3.2': - resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==} + '@img/sharp-libvips-linux-riscv64@1.3.3': + resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.3.2': - resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==} + '@img/sharp-libvips-linux-s390x@1.3.3': + resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-x64@1.3.2': - resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==} + '@img/sharp-libvips-linux-x64@1.3.3': + resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linuxmusl-arm64@1.3.2': - resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==} + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': + resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.3.2': - resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==} + '@img/sharp-libvips-linuxmusl-x64@1.3.3': + resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-linux-arm64@0.35.3': - resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==} + '@img/sharp-linux-arm64@0.35.4': + resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-linux-arm@0.35.3': - resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==} + '@img/sharp-linux-arm@0.35.4': + resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==} engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-linux-ppc64@0.35.3': - resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==} + '@img/sharp-linux-ppc64@0.35.4': + resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==} engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-linux-riscv64@0.35.3': - resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==} + '@img/sharp-linux-riscv64@0.35.4': + resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==} engines: {node: '>=20.9.0'} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-linux-s390x@0.35.3': - resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==} + '@img/sharp-linux-s390x@0.35.4': + resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==} engines: {node: '>=20.9.0'} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-linux-x64@0.35.3': - resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==} + '@img/sharp-linux-x64@0.35.4': + resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-linuxmusl-arm64@0.35.3': - resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==} + '@img/sharp-linuxmusl-arm64@0.35.4': + resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-linuxmusl-x64@0.35.3': - resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==} + '@img/sharp-linuxmusl-x64@0.35.4': + resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-wasm32@0.35.3': - resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==} + '@img/sharp-wasm32@0.35.4': + resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==} engines: {node: '>=20.9.0'} - '@img/sharp-webcontainers-wasm32@0.35.3': - resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==} + '@img/sharp-webcontainers-wasm32@0.35.4': + resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==} engines: {node: '>=20.9.0'} cpu: [wasm32] - '@img/sharp-win32-arm64@0.35.3': - resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==} + '@img/sharp-win32-arm64@0.35.4': + resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.35.3': - resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==} + '@img/sharp-win32-ia32@0.35.4': + resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==} engines: {node: ^20.9.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.35.3': - resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==} + '@img/sharp-win32-x64@0.35.4': + resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==} engines: {node: '>=20.9.0'} cpu: [x64] os: [win32] @@ -2731,8 +2731,8 @@ packages: '@types/react': '>=16' react: '>=16' - '@mediabunny/mp3-encoder@1.55.2': - resolution: {integrity: sha512-T/rNwW/90eF2gaHWr168Z+J030HWDk2DlKB17316v2qbBVfB/EKfD7u6NMvDTFe5TXmK4imAenLPb9S6S4Dr+w==} + '@mediabunny/mp3-encoder@1.55.5': + resolution: {integrity: sha512-WNdWY40KXy6P23iXboIJ8IuLaUsukEF3m+kQ4jCXnDwHTOlyRd+hyq4trTDq7g9kVwB+rpj5ZSzsBq8LKsRYwQ==} peerDependencies: mediabunny: ^1.0.0 @@ -2842,57 +2842,57 @@ packages: '@next/env@16.0.0': resolution: {integrity: sha512-s5j2iFGp38QsG1LWRQaE2iUY3h1jc014/melHFfLdrsMJPqxqDQwWNwyQTcNoUSGZlCVZuM7t7JDMmSyRilsnA==} - '@next/env@16.3.3': - resolution: {integrity: sha512-U2eYQRwXj+dsqxV79zFqExDdatnNY/ZWc2nsJU1p/OgT7fd3dXwlF6OjYaFQCfMoeTA19PWq+wVmYgimVA+V+g==} + '@next/env@16.3.4': + resolution: {integrity: sha512-cjWZnUUa6jZq2kFaNe/ZyJdZonOZ/QoN0Zka2nz/FLOrfx14pQuM9c5RaSVkWMqgdt4ksgPAMWPyHSs/CyV48Q==} - '@next/swc-darwin-arm64@16.3.3': - resolution: {integrity: sha512-8Hiv32QJPwdV6KYJ8meR9SBA061tQqnIKTJDocvOXlEQqib0xMFpzArosuffFUUc0sslbh7QQ8a3Yey1QV8EIw==} + '@next/swc-darwin-arm64@16.3.4': + resolution: {integrity: sha512-iBr3I5LZNk5/bgl5//iTgD2tcym14MX0Xo7fD//u9dYAEgGzza1y9oywluPtf74YnOswVdH1908aK9xVz7zQTw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.3.3': - resolution: {integrity: sha512-A1lgKgwVchRYmSe467zdwhxT9040dd8lH+o65sL5Jet8fjB4kegw/rDyPIpYVRb6jAqwXFOJpjIXJLxQKLiE3A==} + '@next/swc-darwin-x64@16.3.4': + resolution: {integrity: sha512-2dpiSyl2Jw/NrBPaU2MAKGSa+2MR82pJIn4Sm5Rjr+gxAeuh0z158Su3Z2O8zn7UNNq+ej4bToed6RcRN/Lydg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.3.3': - resolution: {integrity: sha512-bf0FIssMFueU2dm7vQEWWxk0c8UjKTdW0yzuh0sQsD8pf1+KCLDdaqhYZNMYGmXwEOiHAUzgBKudovIlcvvBjg==} + '@next/swc-linux-arm64-gnu@16.3.4': + resolution: {integrity: sha512-+t+U8HZT+fApePCS5h89CSH3datz29MkzyfCn+6fpsZBG/oiEOhINcb9rtkv6sdpToLGFn2e6146NzaKCXkqrA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.3.3': - resolution: {integrity: sha512-W7viwCk9JY/cAkdz/A273rd5bb3RgT/IHwR7Upv90tunjBWNtAAhGhoecHh+teRNRSinuAFmE+l7fwZ4YKkrXg==} + '@next/swc-linux-arm64-musl@16.3.4': + resolution: {integrity: sha512-mx03GNs1ocQA5JQ4FxDMmIsNkdrZh8cuezKCrId28e5/gIPU/l7Kcy2+vmCCzdjnnmXJy+iOAu+7K0QppO6Urg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.3.3': - resolution: {integrity: sha512-0W46zw1N3ODpI6n0GeivHvvob1pooozgZVqy65k0mh4/7vr+FbY9+WpHzNVXjHipJf/A3FDheBG19H1s5A25rA==} + '@next/swc-linux-x64-gnu@16.3.4': + resolution: {integrity: sha512-YIhGY6fSMfha52bnVxnzc9zaVBzJg+cqQTOD8tXIBSx4fuv0pVMxQTE0PaS59YhnMOiYiG09IMwxJAf/CFm/Dw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.3.3': - resolution: {integrity: sha512-H4mBso8ZTMBPtdT0PN0pBx2ayTvQuTuvS6qT13d77yVFJXAPCxkyIhLTmdMaGTJs0krQYI/qpzdHijCeihXhbg==} + '@next/swc-linux-x64-musl@16.3.4': + resolution: {integrity: sha512-+eaaX6axpDb0yF1GCpiERe6njplvdC+nks/fKfcHu3XPGRrald8P3/X7yv7QLdjA51knnxwl9pxdIJsg+w1L+Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.3.3': - resolution: {integrity: sha512-cTMUJpcEGmeywofCUfhR+rSsoE33+rVPnPEYNTNdLNlsOeEg/vktOsKUSTb28vUGqD2jkm4Zaskcwn7OCI6FQg==} + '@next/swc-win32-arm64-msvc@16.3.4': + resolution: {integrity: sha512-0jcXW7Xs/uzICrmgV3MhDYDeRy++1CqnpDIerlPIqYO4bhzB4WNbX/aRnQclustsAyTkFKB0z6rbcjmNg5tR8A==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.3.3': - resolution: {integrity: sha512-2VR4cTBzHXaBjnGsuH6GyJjENzQOmHeAh11uY1iUhjm3j5dEUrVJuUj+VL78jaGi/Dik8xS76zEj18BsFhlVZQ==} + '@next/swc-win32-x64-msvc@16.3.4': + resolution: {integrity: sha512-vvBzwu1pYQCp92maZCFCIw/XgOTMR5tur9GjakwIo2cmwRTMKajRZZDS9+e4KsUZWKu1E007WUeAFXRRjZeuzw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -2963,8 +2963,8 @@ packages: cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm-eabi@0.143.0': - resolution: {integrity: sha512-n9uozULWflPqBtdmI8lAabLqGKNgLVNN0ZH8HfgCwpKGNtzRzauB76jTiW/3YLkcA7N1zskpi9GdVnZuu1SAvg==} + '@oxc-parser/binding-android-arm-eabi@0.147.0': + resolution: {integrity: sha512-fOtoGvIoirkvxQVw9J1WJPxz571XPgLsPf9uhRD+PJteUnvrJHMDmK9pw2yZEGGyismtRoEsp+JcXUdF/JDMDw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] @@ -2975,8 +2975,8 @@ packages: cpu: [arm64] os: [android] - '@oxc-parser/binding-android-arm64@0.143.0': - resolution: {integrity: sha512-9BbdjHETk6O3zH/DDid9IgBtF0GlpLabNKN231uraXpRDSfY+iiZxTP5bk1Z63GBownVdhdINFIeddmMz4MzpQ==} + '@oxc-parser/binding-android-arm64@0.147.0': + resolution: {integrity: sha512-emjQHOYJaomo4ykaXQ1EItunr/I94Nk01oqBmU4dSkKSTupIDx6OysVDf2e8Eytm77rb+4ZxzgElyWP7rcEX7A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] @@ -2987,8 +2987,8 @@ packages: cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-arm64@0.143.0': - resolution: {integrity: sha512-gh+6ecoHUy4/sUcolBl/1qPXKBbYNxFY0Pk0ujgQvINTMSftJY7o4yb8gOkDJPeZeB8+a+u7xTe6umoP8N5HFA==} + '@oxc-parser/binding-darwin-arm64@0.147.0': + resolution: {integrity: sha512-kXvBPJL7RmDPJ2mze/vXPPVQimCDtFr9OFLjf7dyhV5Dx64cgcXh9KKrA1sMWvCObvJll9CZZUO0FBlFwD0l6A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] @@ -2999,8 +2999,8 @@ packages: cpu: [x64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.143.0': - resolution: {integrity: sha512-qd1hl2d+lXgHv/VQ/M9qm8TrMC5T4RqDBwtOnl+1D0QMjwcz+8AaB4JSg8STgeag0GP6a6L74XEGAsrTSJWNzQ==} + '@oxc-parser/binding-darwin-x64@0.147.0': + resolution: {integrity: sha512-mgFF8pLU6R64LbT27lSrtVRspVC/3IcZ0qyIikzmi78Y3Ik2OPnlAHHI0UEBRcC3qmNgtjaef7zkFt7/uPxIcw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] @@ -3011,8 +3011,8 @@ packages: cpu: [x64] os: [freebsd] - '@oxc-parser/binding-freebsd-x64@0.143.0': - resolution: {integrity: sha512-M5XXcNa7aOqLPKTR41msfghKu2yQ4xWvCm11/gwU0JzOzHNk5sgW//rVEjJ+LO48+VDAMzXTSzurUVxIDKwozw==} + '@oxc-parser/binding-freebsd-x64@0.147.0': + resolution: {integrity: sha512-v38aiF11qufOTBcCAKL4skgQf0zJ4NEvRlivq7B5kHrlyvjCLjvNrMtNWDTz1SDUL6/xVsJRmLDxv2e+Cp4oWw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] @@ -3023,8 +3023,8 @@ packages: cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-gnueabihf@0.143.0': - resolution: {integrity: sha512-T/GXusuOkPNQhCQCSBbcU/N8j0rAypuDBl1IyFK+lyYT594XsVz80clPC/OtbSSpBGyJxj8uYEfctxVuxVYoww==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.147.0': + resolution: {integrity: sha512-AeIiBbwUaP0H1+4/qGW9l5qHecS/+XA5iMuieVcGb1T+tyc2dVGspFW13BWk/XrLsiGP/CiDJTJqAPLLCzZHkw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -3035,8 +3035,8 @@ packages: cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.143.0': - resolution: {integrity: sha512-oKu4RcBlXSqo3OC62dp6YTnQaZIurNDpCX3BnAM3+bJxt7s8J2TJKMnC0UYer1qhlRaDCg6wkTaTw+2IlsZ12w==} + '@oxc-parser/binding-linux-arm-musleabihf@0.147.0': + resolution: {integrity: sha512-/41MKPW4RgPY4DJco0NCF0RYX3IMZaVlRNMNzvhaxRavc7tN3Txm+qllZbh0aMRs0VHdgUlbI8TcAOiTai4TKg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -3048,8 +3048,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-gnu@0.143.0': - resolution: {integrity: sha512-WJBbD186AZmMGaSIhlktC+rPl8L3peCTXAh88Ih9uEvK0en2mPojGyCGYiL6mHtV1RPV3JyfJW5t6n5hh0lXhA==} + '@oxc-parser/binding-linux-arm64-gnu@0.147.0': + resolution: {integrity: sha512-bmpw/RPhVXgZbtb3xBDuwW5s8+LvZYdqcDSX/sP2ltL77aTio3DP/B5ZTwwgoJ6Mr9vJs4RrmgEKW9XkLNUU1g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -3062,8 +3062,8 @@ packages: os: [linux] libc: [musl] - '@oxc-parser/binding-linux-arm64-musl@0.143.0': - resolution: {integrity: sha512-t1AcYOwEzgceadT4v5e+vaCCb0AncCA3v5AyzfBAz/tMq11qzVccXKzNHtkWdjBsgvTKwRkaUF3QvT4kot8vcQ==} + '@oxc-parser/binding-linux-arm64-musl@0.147.0': + resolution: {integrity: sha512-gd7VX/FDVOw6mjQcu45iIcp4QkgybgJwh3a0OFG2NxmPCj628mQWD96QGu1kK8+mZF9qK4b/gIEyC63vQoB7+Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -3076,8 +3076,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-ppc64-gnu@0.143.0': - resolution: {integrity: sha512-RsnO/NoD8376LMJq8JS8TwI0ieNaFRTuNe2GVJntQg6gwZNMENZsEbknHdVwjpOmxdGLGodcwaGSbAeRr5Bgjw==} + '@oxc-parser/binding-linux-ppc64-gnu@0.147.0': + resolution: {integrity: sha512-HnAzcfki7dSUNHf510Q2NmbJlz8Ys7rn8l9l588Pkx0tYe1BHLZnmELIgqizJ4WPhHGSwN8Ce+B/menVxS3odA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] @@ -3090,8 +3090,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.143.0': - resolution: {integrity: sha512-48fSVfR9TZi5CASZFyv0VC6z6BCoeihFsX031mAD/oSH7d9PYsPgIqza7d9mjP7Z2KTEpTFyH6SIu0Ui6R1vdg==} + '@oxc-parser/binding-linux-riscv64-gnu@0.147.0': + resolution: {integrity: sha512-qlkOL6wT44U+fT5s/+sR6Shx0OdwvQF83JyIPZUxG/ovqZF5/7atOtjH+JPZ5/7ATQLbFBBSmghcy/+2NVB/ew==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] @@ -3104,8 +3104,8 @@ packages: os: [linux] libc: [musl] - '@oxc-parser/binding-linux-riscv64-musl@0.143.0': - resolution: {integrity: sha512-T8CpdD+SfE01DnIOD4HpVxu0ZJOfMJ/VhCvikKfaXAxkZ+9veyLM/D2hpi7Y2hFUyPmVQO3FNZHmYzV/WlVR4g==} + '@oxc-parser/binding-linux-riscv64-musl@0.147.0': + resolution: {integrity: sha512-DlefD7L7sMXs/3hIBH23Egk0phj8kG0SA81dVGOQ3S1ekjOlmTLH2E+F2Thwfh1slKx6aH+lNc5fQYAw0GU7/g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] @@ -3118,8 +3118,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-s390x-gnu@0.143.0': - resolution: {integrity: sha512-QLdeMsCcacenPEFsfxnBUDF1y6opyz5+fmOz9bfD5Y7fiGCMupUCuB3KTPQhNwshIG1P9fPqar9MHxuBDd4bwQ==} + '@oxc-parser/binding-linux-s390x-gnu@0.147.0': + resolution: {integrity: sha512-Xqpagk/031IvZ4svrk2FF01YEqM/iN3MJV3SVZadKg/CsGlDCGoREqKHXYnoV5+8SfGe/m6RM1szXFLusTu/Uw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] @@ -3132,8 +3132,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.143.0': - resolution: {integrity: sha512-659ujfqLy6k7cuH3sbzhd8b+ztSq+i6E2E9pG78Q0BmHjAExfGIdgc8cGgMdwAozDXeZFHkJ+LXYJdWsaGdgyw==} + '@oxc-parser/binding-linux-x64-gnu@0.147.0': + resolution: {integrity: sha512-QioQOeUbI4ATUr0S2z88uA3Cds2R3Mm5Ge7U8XNYtlTb2GJF3rWlcj70z0AJhhOlbdm0YgVjqPBldUNbFylDIg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -3146,8 +3146,8 @@ packages: os: [linux] libc: [musl] - '@oxc-parser/binding-linux-x64-musl@0.143.0': - resolution: {integrity: sha512-/Mw/9j4TfZcnKphPrzOE6t4MMknXadcAAuVUlDRTF/ETWB5xOgQvOJV2Mh9We/bWxZdoxaGAdc+hy4GuYwQ2yQ==} + '@oxc-parser/binding-linux-x64-musl@0.147.0': + resolution: {integrity: sha512-NXy1tv/OdC+pPTwf9RiCZWPK53V/Xq/2cjSnjOSyKopajdaDqMIkgtDY+jXZemp2e8px5FeWfY2L2LwhKZQovg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -3159,8 +3159,8 @@ packages: cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-openharmony-arm64@0.143.0': - resolution: {integrity: sha512-8rIKWR2BFuifbIK/1XB9wTaSdtuJ25dlE7ZQYDnEwj/2xH2vHsxnvIjHT3ZjSVuLLwGGlSslIG/fbOJ8TV8rTw==} + '@oxc-parser/binding-openharmony-arm64@0.147.0': + resolution: {integrity: sha512-GpGWZ6oKz4bjCWW9Mz5pCaGPyk2Aaze6zEoaslIQqpSLtpx5pXj/ap5gUNb5Jn2LIbqWyjkGLX9yv3NMuNcBVQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] @@ -3176,8 +3176,8 @@ packages: cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-arm64-msvc@0.143.0': - resolution: {integrity: sha512-5U9kQYMfRRI6Zq7KDxgbIP0RMnKrfn3gLepRMgJuRkPSUALTiRCk9d/uyhb4lGDjUdzwK7mBkKqhLgzBPCmLpQ==} + '@oxc-parser/binding-win32-arm64-msvc@0.147.0': + resolution: {integrity: sha512-a8mlt7CC8z7LUdCfaxhff4kCd+vSjE+NEFL0cxA8ukfuSnvAto/pWTjytW4BuVLnQGcCVdHJwcRKOfs++H+tjw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] @@ -3188,8 +3188,8 @@ packages: cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.143.0': - resolution: {integrity: sha512-25P7AaHk4R88Yv2XH4gToDVmh0cOu+bEURQU10CRrmvgabfRArSGAP5osmwUKeSUHj0VS50upbpbRWWW/m7mHA==} + '@oxc-parser/binding-win32-ia32-msvc@0.147.0': + resolution: {integrity: sha512-M5ViVDBcFLnl2632AuuWuP35zEL5oikK1jTx8r3+902VEDeSNHxFZAB6RZyfZ7MU6Oi5QTOG8MmMkIeHsudSaw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] @@ -3200,8 +3200,8 @@ packages: cpu: [x64] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.143.0': - resolution: {integrity: sha512-ORMh3JE1s6V7ySicdRK7vgaDQnn5o+UHg9ct989PlWHbel8O9ARrmWXM6kZjrBMtNucxNayQ8g69G0VfWzhANw==} + '@oxc-parser/binding-win32-x64-msvc@0.147.0': + resolution: {integrity: sha512-DUaE13OwnUSlHpLZNcC/nuT10ivlWqc5EZgsfgXuAmWYw0r3nDxGeLD1zlGwYwIgVk1/ZMAxoXpV+05stvbHaA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -3213,12 +3213,12 @@ packages: '@oxc-project/types@0.127.0': resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} - '@oxc-project/types@0.143.0': - resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==} - '@oxc-project/types@0.146.0': resolution: {integrity: sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==} + '@oxc-project/types@0.147.0': + resolution: {integrity: sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==} + '@oxc-resolver/binding-android-arm-eabi@11.21.2': resolution: {integrity: sha512-xQoCRv+gKax9KTdwdaQNnAFOai8neay7g3jExDIORzhbrejwGSJaZNTdOJHR5ziLg2joMxOCMOFMo4zuxza2uQ==} cpu: [arm] @@ -3774,71 +3774,71 @@ packages: resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: 4.62.5 + rollup: 4.63.1 peerDependenciesMeta: rollup: optional: true - '@sentry/browser-utils@10.70.0': - resolution: {integrity: sha512-IvjhafF5NFXrPCg5EHbAPGDwIhHxgUcLlHTklZ3DAdA6ky88BJYMfevbcnOEmgavf4nylhCaquRUFNB5+szj+A==} + '@sentry/browser-utils@10.73.0': + resolution: {integrity: sha512-qQygxJZ+RV779+iL1+lrJ4f4sZLgbgW0/JWPNp0YlcEAE62yCsdKbqoTEjB/EugdS4mSjBMX0chZC6rblu2Ycw==} engines: {node: '>=18'} - '@sentry/browser@10.70.0': - resolution: {integrity: sha512-IK6+J+8H06tZe+A8L37TT5ZxxwNtyQatW8zl5RYYJ/e9CsjrM8fPi8I1OT7uquTw8UtjqFHt7bEef/Vy63ksPg==} + '@sentry/browser@10.73.0': + resolution: {integrity: sha512-HqTe1S5RrWLufhX2LaFP3yNoMxfNDroh120bq1zdGHZfFDBMJQ0CDXxHO+L4UJfQ5dWdCCzWbXIAiZuWGa/DFQ==} engines: {node: '>=18'} '@sentry/conventions@0.16.0': resolution: {integrity: sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==} engines: {node: '>=14'} - '@sentry/core@10.70.0': - resolution: {integrity: sha512-ozhCTDqg89oB4XmWfAwuHshABpvT7AkRpaPnogopPfMAaI61G1t8EKCJ4W7aum8JSBonlfyjPCyW5oYZFm0KvA==} + '@sentry/core@10.73.0': + resolution: {integrity: sha512-FLO1UgH19RyasVpofu612WCOgb2nEH0dZy+R72d7p65XU9i0wxlMKm3+sgfwKmiSJp1Qhilaaxs4Jg6BbiM5HA==} engines: {node: '>=18'} - '@sentry/feedback@10.70.0': - resolution: {integrity: sha512-6VQn2ETJjHkk4QQDdx/587/JDfXsk3yBTLZ3UZOMtBVrkmwgk+1FJZ8ULJ3ud1xZcuh2icunIkc7tGVv2axdnw==} + '@sentry/feedback@10.73.0': + resolution: {integrity: sha512-D6nSngX+e46Mae2/oh2bxBvxNK1z2NERbuMAhB5sx9x4xMBWIyGnYYTECehvEqV9+AqGAgxxhOZoYIG3AmRwww==} engines: {node: '>=18'} - '@sentry/react@10.70.0': - resolution: {integrity: sha512-j1d/4hvoaUVKs5GRrfmwUCkiEmkfaeHsk+xgausZlzg5YvmwpF+gyevBLNP26GFEH7nyHXW4l8dbbo0eNieJmw==} + '@sentry/react@10.73.0': + resolution: {integrity: sha512-wJrzS98ddPvhGS/MKNHZyE8X7ecd4KwKdz7fHino2qkqBBrF4cxWr+q/uLTIk5PYWMkeJ4oKMjHAjOqmzGR4tg==} engines: {node: '>=18'} peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x - '@sentry/replay-canvas@10.70.0': - resolution: {integrity: sha512-irzpw22bK5CF3jbecDa0gBUcfjv7tgeUoLAvtIfeHOP5ajmf3o4Cp99a9RQqkfgvkcMeRZBUwE91SQhp6Ank+w==} + '@sentry/replay-canvas@10.73.0': + resolution: {integrity: sha512-sxa2lKkHPfF/j5xFpW7gocthWXRqyoHz8KCPy6yGc8plT477nl57iGSKhQDYsx1Ny10TLjs7YkIuPP1GY/Ax2Q==} engines: {node: '>=18'} - '@sentry/replay@10.70.0': - resolution: {integrity: sha512-xMnSGzJn9Xd29rYd32lkx/gFW+5mtgqADJ2FiZvis0MBGZuDlNRwPn0/Cs1xA3JNXKV3NGfhdmmvs90w4dHSmw==} + '@sentry/replay@10.73.0': + resolution: {integrity: sha512-nN2wjN/Y0J5BOJV5hqRHUEBfxwUsipp1PKjcDHh6Fpxnrtfldu3Y99E8cQInseo5heFdzEvrOHBBrqWZXOHVKQ==} engines: {node: '>=18'} - '@shikijs/core@4.4.2': - resolution: {integrity: sha512-StyzbAyxg2/tBGf78gwbBkGyeQ73lf8UiJArFaQhTQIDqQOCKPCQFanvrs4/Yv3Yfyc+ONInJM6K+FMIf+P+kA==} + '@shikijs/core@4.4.3': + resolution: {integrity: sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==} engines: {node: '>=20'} - '@shikijs/engine-javascript@4.4.2': - resolution: {integrity: sha512-MnIkeqWdVPUWsxlx8gKLVCJFTsqrQJgpTPBPpQwaFeJ56lOnJxj5aN2LUFnfxUEcvOQuNocmbaMnVrCEln6rkw==} + '@shikijs/engine-javascript@4.4.3': + resolution: {integrity: sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==} engines: {node: '>=20'} - '@shikijs/engine-oniguruma@4.4.2': - resolution: {integrity: sha512-GLhowz1+jixjz+wiZ3wMnOn1jTxiFCGl2PkXufivbnwPHKuyw1AYqu5/hbWhZZ2oAb0NP05WUJhYeigY14drnw==} + '@shikijs/engine-oniguruma@4.4.3': + resolution: {integrity: sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==} engines: {node: '>=20'} - '@shikijs/langs@4.4.2': - resolution: {integrity: sha512-8DfeusD+Zdv/eYIDdXyJTUnSMHt+aAWjAOCXV20HNGAHRlInXpG8wh421v6B91WOm9TFwRLN+b/LG5F2NAIojg==} + '@shikijs/langs@4.4.3': + resolution: {integrity: sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==} engines: {node: '>=20'} - '@shikijs/primitive@4.4.2': - resolution: {integrity: sha512-l6fQQKsOMlz72n38fztmSgZ76MO6KSWuw8o+GJ+FhmqrpC9pIOJNQNXGgbb5yX2AwpzlEHwsaLPnk/8o4Fm+rA==} + '@shikijs/primitive@4.4.3': + resolution: {integrity: sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==} engines: {node: '>=20'} - '@shikijs/themes@4.4.2': - resolution: {integrity: sha512-H0CFoL07ddDC2Dd6EdrPYNkRhUR6YCkJlnuYFceYYUJJA5TIm2b5B33qqiDYryBExgbKMndFJPb2u1gTuqO37g==} + '@shikijs/themes@4.4.3': + resolution: {integrity: sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==} engines: {node: '>=20'} - '@shikijs/types@4.4.2': - resolution: {integrity: sha512-PFYitV4vpDr/iPCIhnHp+Q4ftic5N5VeNJ3KQ1O8gn3h2ar8qgwMAXF7tq4m1CWaMS60fV4VqF6vfnWH4F7vqQ==} + '@shikijs/types@4.4.3': + resolution: {integrity: sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==} engines: {node: '>=20'} '@shikijs/vscode-textmate@10.0.2': @@ -3923,7 +3923,7 @@ packages: resolution: {integrity: sha512-TaCLBrqVEr767+w58QDotDUiCTuE5cyRJuRcDlsKQyUIyGBv+lYD3lu8wBiVCYxgIjB/gu9HmqiC+0yx1rHzaw==} peerDependencies: esbuild: ^0.28.2 - rollup: 4.62.5 + rollup: 4.63.1 storybook: ^10.5.10 vite: '*' webpack: '*' @@ -4154,8 +4154,8 @@ packages: engines: {node: '>=18'} hasBin: true - '@tanstack/eslint-plugin-query@5.102.2': - resolution: {integrity: sha512-0uRcRXvrZN3JLtGDMXU8Qp/or/fp1VhDZEVcD8WrUc7CF0uWbJbloJ88dfFDhiocYP31wXX4Buar/WC31fS5Ug==} + '@tanstack/eslint-plugin-query@5.102.8': + resolution: {integrity: sha512-zRjG2PL3zvoqnsSNJFyfX5Mo+OwRwTbLERwZVO3oNQQn82V949INwaLrprzsOW/ivuow7gASeAUUC24xH7kwSQ==} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ^5.6.0 || ^6.0.0 || ^7.0.0 @@ -4174,8 +4174,8 @@ packages: resolution: {integrity: sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w==} engines: {node: '>=18'} - '@tanstack/query-core@5.102.2': - resolution: {integrity: sha512-zQ5794PXBlV5Wl7N23SR1/Ss+wPE/h2Ye4XlKpm3omN8i8b/Fd4DAdqpLS2AXj5M/RMObt3k5qy3E7kzt/AvBA==} + '@tanstack/query-core@5.102.8': + resolution: {integrity: sha512-ZNjkJ33CqvPNec/6lZBnHqLc3EVGPZ9ySLhYahU9TcuRFdmwXewuj0c4hwSWcGHqEUwcSrKeZ+oGcvPBqXcQcg==} '@tanstack/react-form@1.33.5': resolution: {integrity: sha512-LlRB28qJwO/QCGaHvWnbdh4haBgTFiZVmzA2uzxSBS3YA7/IqrQ6HOBK70CkFQ+DbflZ7NawsmSln13h5iIdTA==} @@ -4193,8 +4193,8 @@ packages: react: '>=16.8' react-dom: '>=16.8' - '@tanstack/react-query@5.102.2': - resolution: {integrity: sha512-KxU8ZyOEuJ81eTSgXa8GQbk/jO/rz0elYtNKt3VMtM2pRjeO8ADIu7sqqmEjFKJKqco9h2N1ojIDuHs6VfDItQ==} + '@tanstack/react-query@5.102.8': + resolution: {integrity: sha512-TYBea4OuXWD7MhaSHq069TWbFe7rcwWN6kzT7JF0OKi1K6c1gTv2IzD6A6ExJsCMozdkqBWeuIUZmu4KQg0O5A==} peerDependencies: react: ^18 || ^19 @@ -4228,8 +4228,8 @@ packages: resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - '@testing-library/react@16.3.2': - resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + '@testing-library/react@16.3.3': + resolution: {integrity: sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==} engines: {node: '>=18'} peerDependencies: '@testing-library/dom': ^10.0.0 @@ -4458,6 +4458,9 @@ packages: '@types/node@26.0.1': resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==} + '@types/node@26.4.0': + resolution: {integrity: sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==} + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -4502,55 +4505,55 @@ packages: '@types/zen-observable@0.8.3': resolution: {integrity: sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==} - '@typescript-eslint/parser@8.67.0': - resolution: {integrity: sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==} + '@typescript-eslint/parser@8.69.0': + resolution: {integrity: sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.67.0': - resolution: {integrity: sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==} + '@typescript-eslint/project-service@8.69.0': + resolution: {integrity: sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.67.0': - resolution: {integrity: sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==} + '@typescript-eslint/scope-manager@8.69.0': + resolution: {integrity: sha512-ewfspqWvSxKSOaplqAUNbaSFO0eB6w1EtQ+esfYFRm3614Ty4uNtExkcbgd6nWsXphbqKyf9ZYdbZdv2xEoWEQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.67.0': - resolution: {integrity: sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==} + '@typescript-eslint/tsconfig-utils@8.69.0': + resolution: {integrity: sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.67.0': - resolution: {integrity: sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==} + '@typescript-eslint/type-utils@8.69.0': + resolution: {integrity: sha512-ZfoJAVg3JZndQEpEl9petVlxau3lRuElc4HRMuAlLCf8to04/iHz692RUSNmXKDjEuJmIL+KZ2/BsOcBc16dsA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.67.0': - resolution: {integrity: sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==} + '@typescript-eslint/types@8.69.0': + resolution: {integrity: sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.67.0': - resolution: {integrity: sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==} + '@typescript-eslint/typescript-estree@8.69.0': + resolution: {integrity: sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.67.0': - resolution: {integrity: sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==} + '@typescript-eslint/utils@8.69.0': + resolution: {integrity: sha512-tUbx60BBqQa31kXF5MCsOOLL5E/WzUuxIn7YpAvq+eaUlqvk8/NXnXMBNAdLCr0icjkzem7iUA5QqWHe/hJ1aw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.67.0': - resolution: {integrity: sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==} + '@typescript-eslint/visitor-keys@8.69.0': + resolution: {integrity: sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript/typescript-aix-ppc64@7.0.2': @@ -4714,8 +4717,8 @@ packages: peerDependencies: vite: '*' - '@vitejs/plugin-react@6.1.0': - resolution: {integrity: sha512-qd2BzUBehkov86WFhg0JkEFEYyCLG9uPCe6qWTY/kRlss9OvJrOF2UbIWT7p+8IzZHkEu0DNGHc4HSv+JdDLsw==} + '@vitejs/plugin-react@6.1.1': + resolution: {integrity: sha512-yxLaQV9gkhS8ezJqCM6+ndU7mDY6gqAg75NQ+0IjwEI8IYOmQCgkRwHKVSfWXW076DsqMo0Dk+0FK1U+M5RgFw==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 @@ -5196,6 +5199,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + baseline-browser-mapping@2.11.20: + resolution: {integrity: sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==} + engines: {node: '>=6.0.0'} + hasBin: true + birecord@0.1.2: resolution: {integrity: sha512-5PAPTTmMpMEb+GuMb5DebfBkipRGyIW9+gtwEBSoDA9xkhHILm04+hZQ702pMksu3d8YAuGkmgTzQWcKqTPScA==} @@ -5221,6 +5229,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.28.8: + resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} @@ -5284,6 +5297,9 @@ packages: caniuse-lite@1.0.30001799: resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + canvas@3.2.3: resolution: {integrity: sha512-PzE5nJZPz72YUAfo8oTp0u3fqqY7IzlTubneAihqDYAUcBk7ryeCmBbdJBEdaH0bptSOe2VT2Zwcb3UaFyaSWw==} engines: {node: ^18.12.0 || >= 20.9.0} @@ -5433,6 +5449,10 @@ packages: resolution: {integrity: sha512-0h+uSNtQGW3D98eQt3jJ8L06Fves8hncB4V/PKdw/Qb8Hnk19VaKuTr55UNRYiSoVa7WwrFls+rh3ux9agmkeQ==} engines: {node: '>= 12.0.0'} + comment-parser@1.4.8: + resolution: {integrity: sha512-rKZTGo4fzKYna8UcL0isTg5wkBNla7bxTypLwZQXjIdi++IdP1OJ41rI5Mti3/jltkPujbu4i9LIARYA+zpotQ==} + engines: {node: '>= 12.0.0'} + compare-versions@6.1.1: resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} @@ -5461,8 +5481,9 @@ packages: copy-to-clipboard@4.0.2: resolution: {integrity: sha512-gklSft7IuhriZKHKpuoA1fpJSLPNgvUMWMo5BlnzAJm0zNKnznoSv23IjtNqclx8eKi6ZcdvFFzYEER/+U1LoQ==} - core-js-compat@3.49.0: - resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + core-js-compat@3.50.0: + resolution: {integrity: sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==} + engines: {node: '>=6.4.0'} cose-base@1.0.3: resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} @@ -5729,6 +5750,10 @@ packages: resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} engines: {node: '>=18'} + default-browser@5.5.1: + resolution: {integrity: sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==} + engines: {node: '>=18'} + define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} @@ -5819,6 +5844,9 @@ packages: electron-to-chromium@1.5.380: resolution: {integrity: sha512-W6d5AbuEoRayO447cqrg6lKJIlscgRnnxOZl/08kfV71BQDoEBC7Wwis68z87LjyK6f4kWyTaubuDbhHKrZkbA==} + electron-to-chromium@1.5.417: + resolution: {integrity: sha512-4T+DTDWuMPM4aHlHwWdAVCVWwp7LDilnhzkj+c/Lbj91XSQrLuOmZSLtS9Q4iIqjlPUbPOnC624zDVVHCHaolQ==} + elkjs@0.11.1: resolution: {integrity: sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg==} @@ -5912,6 +5940,9 @@ packages: es-toolkit@1.51.0: resolution: {integrity: sha512-zC2lQGkM7QX+Gm6iM3+WIdZJzthsEd14LvRNJneSO2hzyz/zNBENR8+YXWo1cKxgPBtV6ksPYHELbcwBRzmdCw==} + es-toolkit@1.52.0: + resolution: {integrity: sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==} + esbuild@0.28.2: resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} engines: {node: '>=18'} @@ -6034,8 +6065,8 @@ packages: peerDependencies: eslint: ^8.0.0 || ^9.0.0 || ^10.0.0 - eslint-plugin-perfectionist@5.10.1: - resolution: {integrity: sha512-Kprsp9Us0GqAesYaAIzUViw57xYp5WBqzXrcE0Mtww++E5fexWXYBipMuuD7yvyH4vvpBH0+oJ+OMAmZ0oYXkw==} + eslint-plugin-perfectionist@5.11.0: + resolution: {integrity: sha512-kZV1otBcu4xT5R1p+0x1N1wRv4pS+OIbxrA47n5a1fTnN3MWbexOx5eQgbQhGyCNbpvF/rqrbnpkGjumHF+Y0Q==} engines: {node: ^20.0.0 || >=22.0.0} peerDependencies: eslint: ^8.45.0 || ^9.0.0 || ^10.0.0 @@ -6128,8 +6159,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.9.0: - resolution: {integrity: sha512-5KeEOJZBfEVA47boFiBsf+6MmmJpffM7qEBg4pLla2e4nlKgdKlqCW0oSLOGsT8Wl5uCGJptLV1bkaiShj90Gw==} + eslint@10.9.1: + resolution: {integrity: sha512-9VaAkDURekixUQJy0oJYl2DcN6oKMfxay7XzaGYAWQwsb6qfKf+x76R2k1L8kb1boc+FyCAaTA9GmiKaaiaF+A==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -6227,7 +6258,7 @@ packages: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} peerDependencies: - picomatch: 4.0.5 + picomatch: 4.0.7 peerDependenciesMeta: picomatch: optional: true @@ -6259,20 +6290,20 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.4.2: - resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} - formatly@0.3.0: - resolution: {integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==} + formatly@0.7.0: + resolution: {integrity: sha512-7CXJtIIA0zy/u12StsYk25qVKxvdLA2ep2sTNxK3ov0mGNIIDqIvAXDSgTnAfDJFsPfWjuz0WjfYSdpvnLA5Tg==} engines: {node: '>=18.3.0'} hasBin: true - foxact@0.3.9: - resolution: {integrity: sha512-uxOL+WzfsUAqkhBR44+om67yslYTHM9ymBsIZpr4EbPEV13oDL1XtFJQOUInci2nHmqBd3aolSC+NthQIsFbUQ==} + foxact@0.3.10: + resolution: {integrity: sha512-5pyt7M2ngc9PyGX5soP3q/L6Z+JkNPEeGTyc5t200szvEiGofWy41bxNGUE/jpuhIDByLLyuumbc712HCdJWTg==} peerDependencies: react: '*' react-dom: '*' @@ -6336,8 +6367,8 @@ packages: get-tsconfig@4.14.0: resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} - get-tsconfig@4.14.1: - resolution: {integrity: sha512-Dz/6HxkrxgNehhxLVeyv8sad9UzF2xBVeaKBQNDfJ5XiSXmp2gTR0eO0RWiT2NCKS5aGP9jjkOMggTN90qU50A==} + get-tsconfig@4.14.3: + resolution: {integrity: sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==} giget@3.3.0: resolution: {integrity: sha512-gzi2D96p+AMfDcmJHGDj3KJ9NRiwvlFAU5yfa3ROwWZmFUjX4P43x3BcyRaOMMLto1vUo7C+86+MFhYTl6Ryiw==} @@ -6369,8 +6400,8 @@ packages: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} - globals@17.7.0: - resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==} + globals@17.11.0: + resolution: {integrity: sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==} engines: {node: '>=18'} globrex@0.1.2: @@ -6392,8 +6423,8 @@ packages: hachure-fill@0.5.2: resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} - happy-dom@20.11.6: - resolution: {integrity: sha512-Hldbg8AdAa5a5oDcZpjqnGitp7JB0hqWmfv/8qr+kft4vzSD8BHsbdRfzYvL/0QcbKcURC/yyoygbeDQarPvYg==} + happy-dom@20.12.0: + resolution: {integrity: sha512-7uMYJu2SEwwL8vVcKp0C0lnt6d2LSGGe+T+oY79PiCJNNSgFpbxW8n5KuzpDQvrU4mt+fYiK1+Jy7Z2v39YR6g==} engines: {node: '>=20.0.0'} has-ansi@6.0.2: @@ -6450,8 +6481,8 @@ packages: resolution: {integrity: sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==} engines: {node: '>=6'} - hono@4.13.3: - resolution: {integrity: sha512-r8AO2mYHoLxSHkgafNeC/BXyb2vWRxD3jem4Ts+ptav8oTG5FIRifAjuJEmZI4bSvvc2ns0GxmIYiZnHqN3mMw==} + hono@4.13.5: + resolution: {integrity: sha512-O6+/eCYRkzzzy0rPWwKLiGBR1nFuUPZynnwjxN1MBA62NNqbT0wQEzQyK2gSO5yDIDB336sXQleAhOHrzlYyKw==} engines: {node: '>=16.9.0'} hosted-git-info@9.0.3: @@ -6511,8 +6542,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + ignore@7.0.8: + resolution: {integrity: sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==} engines: {node: '>= 4'} image-size@2.0.2: @@ -6679,8 +6710,8 @@ packages: react: optional: true - jotai@2.20.2: - resolution: {integrity: sha512-aHB4CNb9qRcyf0mwSB6EO5bCGAjx8cTwFgOFCE2leOnTzqACbnSWG8XoWB3LxCT1Qoj03I1OWAHszDmN4uHb/w==} + jotai@2.20.3: + resolution: {integrity: sha512-N8L9FUbeGDP+0qNJw1FebOxt+5hk+jTOwUA2LlRE4C081jUcY6F1T/FSETp4DT2/INMtiSRZyNm2D+yZdzrSgA==} engines: {node: '>=12.20.0'} peerDependencies: '@babel/core': ^7.29.1 @@ -6716,14 +6747,18 @@ packages: resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} hasBin: true - js-yaml@5.3.0: - resolution: {integrity: sha512-muutsYr+e2+d3rTgUGslq5rxbBlUy3cJ61IsHag2QNDQV+7zXWjkUpmALIajhrlLlrgRUiymj6U3zUr/TMK84Q==} + js-yaml@5.4.1: + resolution: {integrity: sha512-28R/k+NAjeuf7+CKlTxWZVExJGwVVLwY06DgEnOMz2gEpfNkDcD7QvyiVPT0xy0XXhU8vHsd4Ot42OOPdJG7dQ==} hasBin: true jsdoc-type-pratt-parser@7.2.0: resolution: {integrity: sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==} engines: {node: '>=20.0.0'} + jsdoc-type-pratt-parser@7.3.0: + resolution: {integrity: sha512-DoyJXo7x/n48M3NsGOs9QnEws0ft0tV3YsSgvWMNxz2hZtz+Q6fpqUD96lVYX4a+jcEkzHeFNDJOn68TzXfbdA==} + engines: {node: '>=20.0.0'} + jsdoc-type-pratt-parser@8.0.0: resolution: {integrity: sha512-uQu/fXVqVaMg6gM8/E5G5+eygVcZ1NV0Z51CvqhNa2bDWxvHMl484ETr6vph4oPyC+KUcbP/w2W2pewfCiR9aQ==} engines: {node: '>=20.0.0'} @@ -6774,8 +6809,8 @@ packages: khroma@2.1.0: resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} - knip@6.32.2: - resolution: {integrity: sha512-WXTXbmocrw7gqm1A1TQvFN0OgJ7hUSU6E1g6SPRIzzHFogUBhXByc7cYeOFVtJ2uODg7DP4VbESYBYnfbtBYsg==} + knip@6.34.0: + resolution: {integrity: sha512-bbHIrnGspYwe4EBPjjx+lvkUor0F2qfKQc5BPzPI4SOAImYA+k2ueVpIcF7d/W1LEVT7XoJUPt6zELeGZhXBgA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -6785,8 +6820,8 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - ky@2.0.2: - resolution: {integrity: sha512-/GmXpo9F9W+f8n4Ivr2iH+7h7wL7jLbLKWkMlpflcCRb6kGjBfTlASEXaZ9qUgNTn4VgS0P2pwxxzQ4EM6Ulgg==} + ky@2.1.0: + resolution: {integrity: sha512-nIKwelw+7qVqKOlX4Eht6GXfEXlDHcdIjyLWV3s119kYt5s+70ayTnYS+0fRNmtgGRRAfqdeWQbh8YizqkS0ng==} engines: {node: '>=22'} launch-ide@1.4.5: @@ -7004,8 +7039,8 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - loro-crdt@1.14.1: - resolution: {integrity: sha512-1BQ7neoQeJdCbD8gxlLHsy8tqCLYIJGrXSZic4s3t7toIsiFfks8Z0nGWvuEWr5miZ7giJx19x5ffkBaw+g9KQ==} + loro-crdt@1.15.1: + resolution: {integrity: sha512-J3568cXG75MNotTJg2XVhlIEW8POjAZRFhK+3yXYBaQVB1LHn/Ksk3DIqoTOBUf+cgE4yVeqPbh7VUTX2QLV2g==} loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} @@ -7118,15 +7153,15 @@ packages: mdn-data@2.29.0: resolution: {integrity: sha512-pVxQFCcaYUEAH853+v7yoI/qzhxXSq1bTb9obMYGYAN1c3Hen+XDCEvr296XhstrwlSTNgOR7mCSD4JPjbJe5A==} - mediabunny@1.55.2: - resolution: {integrity: sha512-EEx4O6qYddAdCyWPMZNDwI7uc5hewNHrPAf9jLcVhIbXoPsiqNQ+D9i1pfadmGkjN2V318jSrZljkpoziYm6Lg==} + mediabunny@1.55.5: + resolution: {integrity: sha512-m0v6y8FGXiK+HKOc3AZqU+kJPLYsSKaLitmQNQIIHZKIGlTwQ34OF+X6Ul0K8iV4b03oPse10apwuoqbvmKeAA==} merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - mermaid@11.17.0: - resolution: {integrity: sha512-Jo9N377Wb4MSnHFPTbLi2SxFpsQl4eVHoxnW5U1Md9EazvgMp3s+4ohDxr81YNTgbn5Kj7HJ3yslrSJ52kwpbA==} + mermaid@11.17.2: + resolution: {integrity: sha512-V6K3C8EBdEsPFZXSKMJe6ppQOENxuHARr9GvHX4hh47lAbhMRD9qf4oEK7LoaRQxULMa80/qt5gHO73aCleBBg==} micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -7327,8 +7362,8 @@ packages: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - next@16.3.3: - resolution: {integrity: sha512-tuRTx1nQ/yVw83cwJBo9F+njGUgMn3UHQycreWHB8XsStvvAh1AthbI8/4IpKnFaF58F+iSiHejYOlMQ/eq83g==} + next@16.3.4: + resolution: {integrity: sha512-/Ztf6CeRH+ejEXUrYtqI4gkS66eFIHuSwqi60RgcpWKodxFZx2/dqVCMKBwILfAHXQ+F1b1vAudgj3mnxqtoIA==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -7359,6 +7394,10 @@ packages: resolution: {integrity: sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==} engines: {node: '>=18'} + node-releases@2.0.54: + resolution: {integrity: sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==} + engines: {node: '>=18'} + node@runtime:22.23.2: resolution: type: variations @@ -7514,8 +7553,8 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - nuqs@2.10.0: - resolution: {integrity: sha512-uy62jWCXiU1mcGfoClUFNTfrbaxfPQ8WuiomYlkXw5llVdN8kRZVYxOQ2LD70k3DzVQz5bZczE9ClVXwOC22Dw==} + nuqs@2.10.1: + resolution: {integrity: sha512-7lPZrPJVOsD0VvfQSKtodYBBPGPLsLzkFU8ueYIap9yMMJvSw6UC12p8luA5NW7aKe7AbPHKxcxb3OluH6iAJA==} peerDependencies: '@remix-run/react': '>=2' '@tanstack/react-router': ^1 @@ -7574,6 +7613,10 @@ packages: resolution: {integrity: sha512-NzwMUB6C1D0+Kd+9iMS/H4k+Ck3cTX6Ckyfr/gAGlmvSE1LUQZnEZvWBi4PYmMwH/S5SMeTXnE+9uAz8uF+pWw==} engines: {node: '>=20'} + open@11.0.2: + resolution: {integrity: sha512-RWqF+pBSkqecEvCKOn8QYhaNdRMJDZRIrlS/7rTDdLHaPcfXGCZ/h8zb413NfvdeAV0MR7T1yJcA34/q+CSm1Q==} + engines: {node: '>=20'} + openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} @@ -7589,8 +7632,8 @@ packages: resolution: {integrity: sha512-bkgD4qHlN7WxLdX8bLXdaU54TtQtAIg/ZBAfm0aje/mo3MRDo3P0hZSgr4U7O3xfX+fQmR5AP04JS/TGcZLcFA==} engines: {node: ^20.19.0 || >=22.12.0} - oxc-parser@0.143.0: - resolution: {integrity: sha512-ov0NzaDCOInknS7mP1cwKdJERt3utPW8ldjtdUXQ8Ty0GEFD08wk422vCUN0d7pST6kqtV7dxoI9w1Zi0l/9TA==} + oxc-parser@0.147.0: + resolution: {integrity: sha512-5xaug6t7GfV3BO5Iv+xHW1rmQkDEQ3BEu3L8g3InsvWO5i8CYGc4tCZ2X985QcwWNycFJam+aOns6Nr2XAThTA==} engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.21.2: @@ -7648,6 +7691,9 @@ packages: package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + pad-right@0.2.2: resolution: {integrity: sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g==} engines: {node: '>=0.10.0'} @@ -7739,6 +7785,10 @@ packages: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + pinyin-pro@3.29.3: resolution: {integrity: sha512-+UU9bx6vfDw8amOJGHm0TE0rdQl8VPylsDWviQ5OOQ3e+on1xRP4OqDbiDuMT5OISgvfl/Y6ez1BBRaIP80GLQ==} @@ -7798,6 +7848,10 @@ packages: resolution: {integrity: sha512-ZlsFlG7MtSFCoc5xreOvBAozCJ6Pf06opgJjh9ONEv418xpZSAzNjstD36C6+JwOnfSqOW/9uDkqKjezTdxZhw==} engines: {node: '>=20'} + powershell-utils@0.2.1: + resolution: {integrity: sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==} + engines: {node: '>=20'} + prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} @@ -7833,8 +7887,8 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - qs@6.15.3: - resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} + qs@6.16.0: + resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} engines: {node: '>=0.6'} quansync@0.2.11: @@ -8048,8 +8102,8 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} - remend@1.3.0: - resolution: {integrity: sha512-iIhggPkhW3hFImKtB10w0dz4EZbs28mV/dmbcYVonWEJ6UGHHpP+bFZnTh6GNWJONg5m+U56JrL+8IxZRdgWjw==} + remend@1.3.1: + resolution: {integrity: sha512-N3DiY5qbRPoa5vkxn1oDLMyXOVTeo6Hp+XOj6SIqJAYUgLS0Q587gILPMom/qm86AQ/ZrcOdwEIzCz8V3J0nxQ==} repeat-string@1.6.1: resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} @@ -8148,8 +8202,8 @@ packages: server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} - sharp@0.35.3: - resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==} + sharp@0.35.4: + resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==} engines: {node: '>=20.9.0'} peerDependencies: '@types/node': '*' @@ -8169,8 +8223,8 @@ packages: resolution: {integrity: sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==} engines: {node: '>= 0.4'} - shiki@4.4.2: - resolution: {integrity: sha512-P8F/dFhRevaw2uSdeIYlq/5SXZNY85DPtmXQ947gD1Zj2JqO5AkNvVVBar0Me9JkFx3uzVud/qOtP5ek9NEQGA==} + shiki@4.4.3: + resolution: {integrity: sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==} engines: {node: '>=20'} siginfo@2.0.0: @@ -8196,8 +8250,8 @@ packages: size-sensor@1.0.3: resolution: {integrity: sha512-+k9mJ2/rQMiRmQUcjn+qznch260leIXY8r4FyYKKyRBO/s5UoeMAHGkCJyE1R/4wrIhTJONfyloY55SkE7ve3A==} - smol-toml@1.7.1: - resolution: {integrity: sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==} + smol-toml@1.8.0: + resolution: {integrity: sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==} engines: {node: '>= 18'} socket.io-client@4.8.3: @@ -8285,8 +8339,8 @@ packages: vite-plus: optional: true - streamdown@2.5.0: - resolution: {integrity: sha512-/tTnURfIOxZK/pqJAxsfCvETG/XCJHoWnk3jq9xLcuz6CSpnjjuxSRBTTL4PKGhxiZQf0lqPxGhImdpwcZ2XwA==} + streamdown@2.6.0: + resolution: {integrity: sha512-nQZVUn4GvB2R5SAlDNph20iKZeW+RM4gv6G1H3ACypEnmQf8PgTHZ/1Ta2OACRwQ2coK7aW5AeIAa7Ls5zk+RA==} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 @@ -8472,11 +8526,11 @@ packages: resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} - tldts-core@7.4.10: - resolution: {integrity: sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==} + tldts-core@7.4.11: + resolution: {integrity: sha512-CW3WN2rIIE/Of21mulhgnGOwoDyEFNygyIBOONSdyAuSATgMMUCpLeUlB+E8sAwA5xRV9hYPl+kyZ9citHCaKg==} - tldts@7.4.10: - resolution: {integrity: sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==} + tldts@7.4.11: + resolution: {integrity: sha512-aBiNayCfTQxuIJBm06M+xR14cYaYlDlSXZbgsnKzKNxDKUVq7KFwTjwBSsb7m9Y5xO8WfPnBc63WaYFMTGlvqw==} hasBin: true to-regex-range@5.0.1: @@ -8552,8 +8606,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.23.12: - resolution: {integrity: sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==} + tsx@4.23.13: + resolution: {integrity: sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==} engines: {node: '>=18.0.0'} hasBin: true @@ -8663,6 +8717,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.3.2: + resolution: {integrity: sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -9023,6 +9083,9 @@ packages: zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + zod@4.5.4: + resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==} + zrender@6.1.0: resolution: {integrity: sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==} @@ -9073,28 +9136,21 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@amplitude/analytics-browser@2.45.6': + '@amplitude/analytics-browser@2.45.8': dependencies: - '@amplitude/analytics-core': 2.54.2 - '@amplitude/plugin-autocapture-browser': 1.28.11 - '@amplitude/plugin-custom-enrichment-browser': 0.1.21 - '@amplitude/plugin-event-property-attribution-browser': 0.2.13 - '@amplitude/plugin-network-capture-browser': 1.10.13 - '@amplitude/plugin-page-url-enrichment-browser': 0.7.23 - '@amplitude/plugin-page-view-tracking-browser': 2.11.13 - '@amplitude/plugin-web-vitals-browser': 1.1.45 - tslib: 2.8.1 - - '@amplitude/analytics-client-common@2.4.60': - dependencies: - '@amplitude/analytics-connector': 1.6.4 - '@amplitude/analytics-core': 2.54.2 - '@amplitude/analytics-types': 2.11.1 + '@amplitude/analytics-core': 2.55.0 + '@amplitude/plugin-autocapture-browser': 1.29.0 + '@amplitude/plugin-custom-enrichment-browser': 0.1.22 + '@amplitude/plugin-event-property-attribution-browser': 0.2.14 + '@amplitude/plugin-network-capture-browser': 1.10.14 + '@amplitude/plugin-page-url-enrichment-browser': 0.7.24 + '@amplitude/plugin-page-view-tracking-browser': 2.11.14 + '@amplitude/plugin-web-vitals-browser': 1.1.46 tslib: 2.8.1 '@amplitude/analytics-connector@1.6.4': {} - '@amplitude/analytics-core@2.54.2': + '@amplitude/analytics-core@2.55.0': dependencies: '@amplitude/analytics-connector': 1.6.4 '@types/zen-observable': 0.8.3 @@ -9104,7 +9160,7 @@ snapshots: '@amplitude/analytics-types@2.11.1': {} - '@amplitude/element-selector@0.2.1': + '@amplitude/element-selector@0.3.0': dependencies: tslib: 2.8.1 @@ -9112,52 +9168,51 @@ snapshots: dependencies: js-base64: 3.8.0 - '@amplitude/plugin-autocapture-browser@1.28.11': + '@amplitude/plugin-autocapture-browser@1.29.0': dependencies: - '@amplitude/analytics-core': 2.54.2 - '@amplitude/element-selector': 0.2.1 + '@amplitude/analytics-core': 2.55.0 + '@amplitude/element-selector': 0.3.0 tslib: 2.8.1 - '@amplitude/plugin-custom-enrichment-browser@0.1.21': + '@amplitude/plugin-custom-enrichment-browser@0.1.22': dependencies: - '@amplitude/analytics-core': 2.54.2 + '@amplitude/analytics-core': 2.55.0 tslib: 2.8.1 - '@amplitude/plugin-event-property-attribution-browser@0.2.13': + '@amplitude/plugin-event-property-attribution-browser@0.2.14': dependencies: - '@amplitude/analytics-core': 2.54.2 + '@amplitude/analytics-core': 2.55.0 tslib: 2.8.1 - '@amplitude/plugin-network-capture-browser@1.10.13': + '@amplitude/plugin-network-capture-browser@1.10.14': dependencies: - '@amplitude/analytics-core': 2.54.2 + '@amplitude/analytics-core': 2.55.0 tslib: 2.8.1 - '@amplitude/plugin-page-url-enrichment-browser@0.7.23': + '@amplitude/plugin-page-url-enrichment-browser@0.7.24': dependencies: - '@amplitude/analytics-core': 2.54.2 + '@amplitude/analytics-core': 2.55.0 tslib: 2.8.1 - '@amplitude/plugin-page-view-tracking-browser@2.11.13': + '@amplitude/plugin-page-view-tracking-browser@2.11.14': dependencies: - '@amplitude/analytics-core': 2.54.2 + '@amplitude/analytics-core': 2.55.0 tslib: 2.8.1 - '@amplitude/plugin-session-replay-browser@1.33.8(@amplitude/rrweb@2.1.1)': + '@amplitude/plugin-session-replay-browser@1.35.0(@amplitude/rrweb@2.1.1)': dependencies: - '@amplitude/analytics-client-common': 2.4.60 - '@amplitude/analytics-core': 2.54.2 + '@amplitude/analytics-core': 2.55.0 '@amplitude/analytics-types': 2.11.1 '@amplitude/rrweb-plugin-console-record': 2.0.0-alpha.40(@amplitude/rrweb@2.1.1) '@amplitude/rrweb-record': 2.0.0-alpha.40 - '@amplitude/session-replay-browser': 1.48.2(@amplitude/rrweb@2.1.1) + '@amplitude/session-replay-browser': 1.50.0(@amplitude/rrweb@2.1.1) tslib: 2.8.1 transitivePeerDependencies: - '@amplitude/rrweb' - '@amplitude/plugin-web-vitals-browser@1.1.45': + '@amplitude/plugin-web-vitals-browser@1.1.46': dependencies: - '@amplitude/analytics-core': 2.54.2 + '@amplitude/analytics-core': 2.55.0 tslib: 2.8.1 web-vitals: 5.1.0 @@ -9195,9 +9250,9 @@ snapshots: base64-arraybuffer: 1.0.2 mitt: 3.0.1 - '@amplitude/session-replay-browser@1.48.2(@amplitude/rrweb@2.1.1)': + '@amplitude/session-replay-browser@1.50.0(@amplitude/rrweb@2.1.1)': dependencies: - '@amplitude/analytics-core': 2.54.2 + '@amplitude/analytics-core': 2.55.0 '@amplitude/analytics-types': 2.11.1 '@amplitude/rrweb-plugin-console-record': 2.0.0-alpha.40(@amplitude/rrweb@2.1.1) '@amplitude/rrweb-record': 2.0.0-alpha.40 @@ -9360,12 +9415,12 @@ snapshots: '@chevrotain/types@11.1.2': {} - '@chromatic-com/storybook@5.3.0(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))': + '@chromatic-com/storybook@5.3.0(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: '@neoconfetti/react': 1.0.0 chromatic: 18.2.0 jsonfile: 6.2.1 - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) strip-ansi: 7.2.0 transitivePeerDependencies: - '@chromatic-com/cypress' @@ -9567,6 +9622,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.9.2': dependencies: tslib: 2.8.1 @@ -9587,7 +9647,7 @@ snapshots: '@es-joy/jsdoccomment@0.88.0': dependencies: '@types/estree': 1.0.9 - '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/types': 8.69.0 comment-parser: 1.4.7 esquery: 1.7.0 jsdoc-type-pratt-parser: 7.2.0 @@ -9595,7 +9655,7 @@ snapshots: '@es-joy/jsdoccomment@0.91.0': dependencies: '@types/estree': 1.0.9 - '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/types': 8.69.0 comment-parser: 1.4.7 esquery: 1.7.0 jsdoc-type-pratt-parser: 8.0.0 @@ -9680,100 +9740,100 @@ snapshots: '@esbuild/win32-x64@0.28.2': optional: true - '@eslint-community/eslint-plugin-eslint-comments@4.7.2(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))': + '@eslint-community/eslint-plugin-eslint-comments@4.7.2(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))': dependencies: escape-string-regexp: 4.0.0 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) - ignore: 7.0.5 + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) + ignore: 7.0.8 - '@eslint-community/eslint-utils@4.9.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))': dependencies: - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint-react/ast@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': + '@eslint-react/ast@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': dependencies: - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/typescript-estree': 8.67.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/typescript-estree': 8.69.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) string-ts: 2.3.1 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@eslint-react/core@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': + '@eslint-react/core@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': dependencies: - '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/var': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@typescript-eslint/scope-manager': 8.67.0 - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/var': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/scope-manager': 8.69.0 + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) ts-pattern: 5.9.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@eslint-react/eslint-plugin@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': + '@eslint-react/eslint-plugin@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': dependencies: - '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) - eslint-plugin-react-dom: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint-plugin-react-jsx: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint-plugin-react-naming-convention: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint-plugin-react-rsc: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint-plugin-react-web-api: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint-plugin-react-x: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) + eslint-plugin-react-dom: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint-plugin-react-jsx: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint-plugin-react-naming-convention: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint-plugin-react-rsc: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint-plugin-react-web-api: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint-plugin-react-x: 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@eslint-react/eslint@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': + '@eslint-react/eslint@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': dependencies: - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@eslint-react/jsx@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': + '@eslint-react/jsx@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': dependencies: - '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/var': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/var': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) ts-pattern: 5.9.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@eslint-react/shared@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': + '@eslint-react/shared@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': dependencies: - '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) ts-pattern: 5.9.0 typescript: '@typescript/typescript6@6.0.2' - zod: 4.4.3 + zod: 4.5.4 transitivePeerDependencies: - supports-color - '@eslint-react/var@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': + '@eslint-react/var@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': dependencies: - '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@typescript-eslint/scope-manager': 8.67.0 - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/scope-manager': 8.69.0 + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) ts-pattern: 5.9.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -9931,9 +9991,9 @@ snapshots: '@hey-api/types@0.1.4': {} - '@hono/node-server@2.1.1(hono@4.13.3)': + '@hono/node-server@2.1.1(hono@4.13.5)': dependencies: - hono: 4.13.3 + hono: 4.13.5 '@humanfs/core@0.19.2': dependencies: @@ -9997,119 +10057,119 @@ snapshots: '@img/colour@1.1.0': optional: true - '@img/sharp-darwin-arm64@0.35.3': + '@img/sharp-darwin-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.3.2 + '@img/sharp-libvips-darwin-arm64': 1.3.3 optional: true - '@img/sharp-darwin-x64@0.35.3': + '@img/sharp-darwin-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.3.2 + '@img/sharp-libvips-darwin-x64': 1.3.3 optional: true - '@img/sharp-freebsd-wasm32@0.35.3': + '@img/sharp-freebsd-wasm32@0.35.4': dependencies: - '@img/sharp-wasm32': 0.35.3 + '@img/sharp-wasm32': 0.35.4 optional: true - '@img/sharp-libvips-darwin-arm64@1.3.2': + '@img/sharp-libvips-darwin-arm64@1.3.3': optional: true - '@img/sharp-libvips-darwin-x64@1.3.2': + '@img/sharp-libvips-darwin-x64@1.3.3': optional: true - '@img/sharp-libvips-linux-arm64@1.3.2': + '@img/sharp-libvips-linux-arm64@1.3.3': optional: true - '@img/sharp-libvips-linux-arm@1.3.2': + '@img/sharp-libvips-linux-arm@1.3.3': optional: true - '@img/sharp-libvips-linux-ppc64@1.3.2': + '@img/sharp-libvips-linux-ppc64@1.3.3': optional: true - '@img/sharp-libvips-linux-riscv64@1.3.2': + '@img/sharp-libvips-linux-riscv64@1.3.3': optional: true - '@img/sharp-libvips-linux-s390x@1.3.2': + '@img/sharp-libvips-linux-s390x@1.3.3': optional: true - '@img/sharp-libvips-linux-x64@1.3.2': + '@img/sharp-libvips-linux-x64@1.3.3': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.3.2': + '@img/sharp-libvips-linuxmusl-x64@1.3.3': optional: true - '@img/sharp-linux-arm64@0.35.3': + '@img/sharp-linux-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.3.2 + '@img/sharp-libvips-linux-arm64': 1.3.3 optional: true - '@img/sharp-linux-arm@0.35.3': + '@img/sharp-linux-arm@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.3.2 + '@img/sharp-libvips-linux-arm': 1.3.3 optional: true - '@img/sharp-linux-ppc64@0.35.3': + '@img/sharp-linux-ppc64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.3.2 + '@img/sharp-libvips-linux-ppc64': 1.3.3 optional: true - '@img/sharp-linux-riscv64@0.35.3': + '@img/sharp-linux-riscv64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.3.2 + '@img/sharp-libvips-linux-riscv64': 1.3.3 optional: true - '@img/sharp-linux-s390x@0.35.3': + '@img/sharp-linux-s390x@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.3.2 + '@img/sharp-libvips-linux-s390x': 1.3.3 optional: true - '@img/sharp-linux-x64@0.35.3': + '@img/sharp-linux-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.3.2 + '@img/sharp-libvips-linux-x64': 1.3.3 optional: true - '@img/sharp-linuxmusl-arm64@0.35.3': + '@img/sharp-linuxmusl-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 optional: true - '@img/sharp-linuxmusl-x64@0.35.3': + '@img/sharp-linuxmusl-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 optional: true - '@img/sharp-wasm32@0.35.3': + '@img/sharp-wasm32@0.35.4': dependencies: - '@emnapi/runtime': 1.11.2 + '@emnapi/runtime': 1.11.3 optional: true - '@img/sharp-webcontainers-wasm32@0.35.3': + '@img/sharp-webcontainers-wasm32@0.35.4': dependencies: - '@img/sharp-wasm32': 0.35.3 + '@img/sharp-wasm32': 0.35.4 optional: true - '@img/sharp-win32-arm64@0.35.3': + '@img/sharp-win32-arm64@0.35.4': optional: true - '@img/sharp-win32-ia32@0.35.3': + '@img/sharp-win32-ia32@0.35.4': optional: true - '@img/sharp-win32-x64@0.35.3': + '@img/sharp-win32-x64@0.35.4': optional: true '@isaacs/fs-minipass@4.0.1': dependencies: minipass: 7.1.3 - '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))': dependencies: glob: 13.0.6 react-docgen-typescript: 2.4.0(@typescript/typescript6@6.0.2) - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' optionalDependencies: typescript: '@typescript/typescript6@6.0.2' @@ -10370,9 +10430,9 @@ snapshots: '@types/react': 19.2.18 react: 19.2.8 - '@mediabunny/mp3-encoder@1.55.2(mediabunny@1.55.2)': + '@mediabunny/mp3-encoder@1.55.5(mediabunny@1.55.5)': dependencies: - mediabunny: 1.55.2 + mediabunny: 1.55.5 '@mermaid-js/parser@1.2.1': dependencies: @@ -10464,30 +10524,30 @@ snapshots: '@next/env@16.0.0': {} - '@next/env@16.3.3': {} + '@next/env@16.3.4': {} - '@next/swc-darwin-arm64@16.3.3': + '@next/swc-darwin-arm64@16.3.4': optional: true - '@next/swc-darwin-x64@16.3.3': + '@next/swc-darwin-x64@16.3.4': optional: true - '@next/swc-linux-arm64-gnu@16.3.3': + '@next/swc-linux-arm64-gnu@16.3.4': optional: true - '@next/swc-linux-arm64-musl@16.3.3': + '@next/swc-linux-arm64-musl@16.3.4': optional: true - '@next/swc-linux-x64-gnu@16.3.3': + '@next/swc-linux-x64-gnu@16.3.4': optional: true - '@next/swc-linux-x64-musl@16.3.3': + '@next/swc-linux-x64-musl@16.3.4': optional: true - '@next/swc-win32-arm64-msvc@16.3.3': + '@next/swc-win32-arm64-msvc@16.3.4': optional: true - '@next/swc-win32-x64-msvc@16.3.3': + '@next/swc-win32-x64-msvc@16.3.4': optional: true '@nodelib/fs.scandir@2.1.5': @@ -10560,11 +10620,11 @@ snapshots: transitivePeerDependencies: - '@opentelemetry/api' - '@orpc/tanstack-query@1.15.0(@orpc/client@1.15.0)(@tanstack/query-core@5.102.2)': + '@orpc/tanstack-query@1.15.0(@orpc/client@1.15.0)(@tanstack/query-core@5.102.8)': dependencies: '@orpc/client': 1.15.0 '@orpc/shared': 1.15.0 - '@tanstack/query-core': 5.102.2 + '@tanstack/query-core': 5.102.8 transitivePeerDependencies: - '@opentelemetry/api' @@ -10573,97 +10633,97 @@ snapshots: '@oxc-parser/binding-android-arm-eabi@0.127.0': optional: true - '@oxc-parser/binding-android-arm-eabi@0.143.0': + '@oxc-parser/binding-android-arm-eabi@0.147.0': optional: true '@oxc-parser/binding-android-arm64@0.127.0': optional: true - '@oxc-parser/binding-android-arm64@0.143.0': + '@oxc-parser/binding-android-arm64@0.147.0': optional: true '@oxc-parser/binding-darwin-arm64@0.127.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.143.0': + '@oxc-parser/binding-darwin-arm64@0.147.0': optional: true '@oxc-parser/binding-darwin-x64@0.127.0': optional: true - '@oxc-parser/binding-darwin-x64@0.143.0': + '@oxc-parser/binding-darwin-x64@0.147.0': optional: true '@oxc-parser/binding-freebsd-x64@0.127.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.143.0': + '@oxc-parser/binding-freebsd-x64@0.147.0': optional: true '@oxc-parser/binding-linux-arm-gnueabihf@0.127.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.143.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.147.0': optional: true '@oxc-parser/binding-linux-arm-musleabihf@0.127.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.143.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.147.0': optional: true '@oxc-parser/binding-linux-arm64-gnu@0.127.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.143.0': + '@oxc-parser/binding-linux-arm64-gnu@0.147.0': optional: true '@oxc-parser/binding-linux-arm64-musl@0.127.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.143.0': + '@oxc-parser/binding-linux-arm64-musl@0.147.0': optional: true '@oxc-parser/binding-linux-ppc64-gnu@0.127.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.143.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.147.0': optional: true '@oxc-parser/binding-linux-riscv64-gnu@0.127.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.143.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.147.0': optional: true '@oxc-parser/binding-linux-riscv64-musl@0.127.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.143.0': + '@oxc-parser/binding-linux-riscv64-musl@0.147.0': optional: true '@oxc-parser/binding-linux-s390x-gnu@0.127.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.143.0': + '@oxc-parser/binding-linux-s390x-gnu@0.147.0': optional: true '@oxc-parser/binding-linux-x64-gnu@0.127.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.143.0': + '@oxc-parser/binding-linux-x64-gnu@0.147.0': optional: true '@oxc-parser/binding-linux-x64-musl@0.127.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.143.0': + '@oxc-parser/binding-linux-x64-musl@0.147.0': optional: true '@oxc-parser/binding-openharmony-arm64@0.127.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.143.0': + '@oxc-parser/binding-openharmony-arm64@0.147.0': optional: true '@oxc-parser/binding-wasm32-wasi@0.127.0': @@ -10676,29 +10736,29 @@ snapshots: '@oxc-parser/binding-win32-arm64-msvc@0.127.0': optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.143.0': + '@oxc-parser/binding-win32-arm64-msvc@0.147.0': optional: true '@oxc-parser/binding-win32-ia32-msvc@0.127.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.143.0': + '@oxc-parser/binding-win32-ia32-msvc@0.147.0': optional: true '@oxc-parser/binding-win32-x64-msvc@0.127.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.143.0': + '@oxc-parser/binding-win32-x64-msvc@0.147.0': optional: true '@oxc-project/runtime@0.146.0': {} '@oxc-project/types@0.127.0': {} - '@oxc-project/types@0.143.0': {} - '@oxc-project/types@0.146.0': {} + '@oxc-project/types@0.147.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.21.2': optional: true @@ -11057,83 +11117,83 @@ snapshots: dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 - picomatch: 4.0.5 + picomatch: 4.0.7 - '@sentry/browser-utils@10.70.0': + '@sentry/browser-utils@10.73.0': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/core': 10.70.0 + '@sentry/core': 10.73.0 - '@sentry/browser@10.70.0': + '@sentry/browser@10.73.0': dependencies: - '@sentry/browser-utils': 10.70.0 + '@sentry/browser-utils': 10.73.0 '@sentry/conventions': 0.16.0 - '@sentry/core': 10.70.0 - '@sentry/feedback': 10.70.0 - '@sentry/replay': 10.70.0 - '@sentry/replay-canvas': 10.70.0 + '@sentry/core': 10.73.0 + '@sentry/feedback': 10.73.0 + '@sentry/replay': 10.73.0 + '@sentry/replay-canvas': 10.73.0 '@sentry/conventions@0.16.0': {} - '@sentry/core@10.70.0': + '@sentry/core@10.73.0': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/feedback@10.70.0': + '@sentry/feedback@10.73.0': dependencies: - '@sentry/core': 10.70.0 + '@sentry/core': 10.73.0 - '@sentry/react@10.70.0(react@19.2.8)': + '@sentry/react@10.73.0(react@19.2.8)': dependencies: - '@sentry/browser': 10.70.0 + '@sentry/browser': 10.73.0 '@sentry/conventions': 0.16.0 - '@sentry/core': 10.70.0 + '@sentry/core': 10.73.0 react: 19.2.8 - '@sentry/replay-canvas@10.70.0': + '@sentry/replay-canvas@10.73.0': dependencies: - '@sentry/core': 10.70.0 - '@sentry/replay': 10.70.0 + '@sentry/core': 10.73.0 + '@sentry/replay': 10.73.0 - '@sentry/replay@10.70.0': + '@sentry/replay@10.73.0': dependencies: - '@sentry/browser-utils': 10.70.0 - '@sentry/core': 10.70.0 + '@sentry/browser-utils': 10.73.0 + '@sentry/core': 10.73.0 - '@shikijs/core@4.4.2': + '@shikijs/core@4.4.3': dependencies: - '@shikijs/primitive': 4.4.2 - '@shikijs/types': 4.4.2 + '@shikijs/primitive': 4.4.3 + '@shikijs/types': 4.4.3 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.5 hast-util-to-html: 9.0.5 - '@shikijs/engine-javascript@4.4.2': + '@shikijs/engine-javascript@4.4.3': dependencies: - '@shikijs/types': 4.4.2 + '@shikijs/types': 4.4.3 '@shikijs/vscode-textmate': 10.0.2 oniguruma-to-es: 4.3.6 - '@shikijs/engine-oniguruma@4.4.2': + '@shikijs/engine-oniguruma@4.4.3': dependencies: - '@shikijs/types': 4.4.2 + '@shikijs/types': 4.4.3 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@4.4.2': + '@shikijs/langs@4.4.3': dependencies: - '@shikijs/types': 4.4.2 + '@shikijs/types': 4.4.3 - '@shikijs/primitive@4.4.2': + '@shikijs/primitive@4.4.3': dependencies: - '@shikijs/types': 4.4.2 + '@shikijs/types': 4.4.3 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.5 - '@shikijs/themes@4.4.2': + '@shikijs/themes@4.4.3': dependencies: - '@shikijs/types': 4.4.2 + '@shikijs/types': 4.4.3 - '@shikijs/types@4.4.2': + '@shikijs/types@4.4.3': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.5 @@ -11151,21 +11211,21 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@storybook/addon-a11y@10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))': + '@storybook/addon-a11y@10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: '@storybook/global': 5.0.0 axe-core: 4.13.0 - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) - '@storybook/addon-docs@10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))': + '@storybook/addon-docs@10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: '@mdx-js/react': 3.1.1(@types/react@19.2.18)(react@19.2.8) - '@storybook/csf-plugin': 10.5.10(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + '@storybook/csf-plugin': 10.5.10(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) '@storybook/icons': 2.1.0(react@19.2.8) - '@storybook/react-dom-shim': 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + '@storybook/react-dom-shim': 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) ts-dedent: 2.3.0 optionalDependencies: '@types/react': 19.2.18 @@ -11176,54 +11236,54 @@ snapshots: - vite - webpack - '@storybook/addon-links@10.5.10(@types/react@19.2.18)(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))': + '@storybook/addon-links@10.5.10(@types/react@19.2.18)(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: '@storybook/global': 5.0.0 - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) optionalDependencies: '@types/react': 19.2.18 react: 19.2.8 - '@storybook/addon-onboarding@10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))': + '@storybook/addon-onboarding@10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) - '@storybook/addon-themes@10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))': + '@storybook/addon-themes@10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) ts-dedent: 2.3.0 - '@storybook/addon-vitest@10.5.10(@vitest/browser-playwright@4.1.11)(@vitest/browser@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.11))(@vitest/runner@4.1.11)(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(vitest@4.1.11)': + '@storybook/addon-vitest@10.5.10(@vitest/browser-playwright@4.1.11)(@vitest/browser@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@4.1.11))(@vitest/runner@4.1.11)(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(vitest@4.1.11)': dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.1.0(react@19.2.8) - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) optionalDependencies: - '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.11) - '@vitest/browser-playwright': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) + '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@4.1.11) + '@vitest/browser-playwright': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) '@vitest/runner': 4.1.11 - vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) transitivePeerDependencies: - react - '@storybook/builder-vite@10.5.10(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))': + '@storybook/builder-vite@10.5.10(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: - '@storybook/csf-plugin': 10.5.10(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + '@storybook/csf-plugin': 10.5.10(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) ts-dedent: 2.3.0 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' transitivePeerDependencies: - esbuild - rollup - webpack - '@storybook/csf-plugin@10.5.10(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))': + '@storybook/csf-plugin@10.5.10(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) unplugin: 2.3.11 optionalDependencies: esbuild: 0.28.2 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' '@storybook/global@5.0.0': {} @@ -11231,18 +11291,18 @@ snapshots: dependencies: react: 19.2.8 - '@storybook/nextjs-vite@10.5.10(@babel/core@7.29.7(supports-color@11.0.0))(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(next@16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(supports-color@11.0.0)': + '@storybook/nextjs-vite@10.5.10(@babel/core@7.29.7(supports-color@11.0.0))(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(next@16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(supports-color@11.0.0)': dependencies: - '@storybook/builder-vite': 10.5.10(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) - '@storybook/react': 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(supports-color@11.0.0) - '@storybook/react-vite': 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(supports-color@11.0.0) - next: 16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@storybook/builder-vite': 10.5.10(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) + '@storybook/react': 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(supports-color@11.0.0) + '@storybook/react-vite': 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(supports-color@11.0.0) + next: 16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@11.0.0))(react@19.2.8) - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' - vite-plugin-storybook-nextjs: 3.3.0(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(next@16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(supports-color@11.0.0) + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' + vite-plugin-storybook-nextjs: 3.3.0(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(next@16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(supports-color@11.0.0) optionalDependencies: '@types/react': 19.2.18 '@types/react-dom': 19.2.5(@types/react@19.2.18) @@ -11255,30 +11315,30 @@ snapshots: - supports-color - webpack - '@storybook/react-dom-shim@10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))': + '@storybook/react-dom-shim@10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) optionalDependencies: '@types/react': 19.2.18 '@types/react-dom': 19.2.5(@types/react@19.2.18) - '@storybook/react-vite@10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(supports-color@11.0.0)': + '@storybook/react-vite@10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(supports-color@11.0.0)': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) '@rollup/pluginutils': 5.4.0 - '@storybook/builder-vite': 10.5.10(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) - '@storybook/react': 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(supports-color@11.0.0) + '@storybook/builder-vite': 10.5.10(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) + '@storybook/react': 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(supports-color@11.0.0) empathic: 2.0.1 magic-string: 0.30.21 react: 19.2.8 react-docgen: 8.0.3(supports-color@11.0.0) react-dom: 19.2.8(react@19.2.8) resolve: 1.22.12 - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) tsconfig-paths: 4.2.0 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -11289,15 +11349,15 @@ snapshots: - supports-color - webpack - '@storybook/react@10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(supports-color@11.0.0)': + '@storybook/react@10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(@typescript/typescript6@6.0.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(supports-color@11.0.0)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))) + '@storybook/react-dom-shim': 10.5.10(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) react: 19.2.8 react-docgen: 8.0.3(supports-color@11.0.0) react-docgen-typescript: 2.4.0(@typescript/typescript6@6.0.2) react-dom: 19.2.8(react@19.2.8) - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) optionalDependencies: '@types/react': 19.2.18 '@types/react-dom': 19.2.5(@types/react@19.2.18) @@ -11320,19 +11380,19 @@ snapshots: dependencies: tslib: 2.8.1 - '@t3-oss/env-core@0.13.11(@typescript/typescript6@6.0.2)(valibot@1.4.2(@typescript/typescript6@6.0.2))(zod@4.4.3)': + '@t3-oss/env-core@0.13.11(@typescript/typescript6@6.0.2)(valibot@1.4.2(@typescript/typescript6@6.0.2))(zod@4.5.4)': optionalDependencies: typescript: '@typescript/typescript6@6.0.2' valibot: 1.4.2(@typescript/typescript6@6.0.2) - zod: 4.4.3 + zod: 4.5.4 - '@t3-oss/env-nextjs@0.13.11(@typescript/typescript6@6.0.2)(valibot@1.4.2(@typescript/typescript6@6.0.2))(zod@4.4.3)': + '@t3-oss/env-nextjs@0.13.11(@typescript/typescript6@6.0.2)(valibot@1.4.2(@typescript/typescript6@6.0.2))(zod@4.5.4)': dependencies: - '@t3-oss/env-core': 0.13.11(@typescript/typescript6@6.0.2)(valibot@1.4.2(@typescript/typescript6@6.0.2))(zod@4.4.3) + '@t3-oss/env-core': 0.13.11(@typescript/typescript6@6.0.2)(valibot@1.4.2(@typescript/typescript6@6.0.2))(zod@4.5.4) optionalDependencies: typescript: '@typescript/typescript6@6.0.2' valibot: 1.4.2(@typescript/typescript6@6.0.2) - zod: 4.4.3 + zod: 4.5.4 '@tailwindcss/node@4.3.3': dependencies: @@ -11403,19 +11463,19 @@ snapshots: postcss: 8.5.26 tailwindcss: 4.3.3 - '@tailwindcss/vite@4.3.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.3 '@tailwindcss/oxide': 4.3.3 tailwindcss: 4.3.3 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' '@tanstack/devtools-event-client@0.4.4': {} - '@tanstack/eslint-plugin-query@5.102.2(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': + '@tanstack/eslint-plugin-query@5.102.8(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': dependencies: - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -11433,7 +11493,7 @@ snapshots: '@tanstack/pacer-lite@0.1.1': {} - '@tanstack/query-core@5.102.2': {} + '@tanstack/query-core@5.102.8': {} '@tanstack/react-form@1.33.5(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: @@ -11450,9 +11510,9 @@ snapshots: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@tanstack/react-query@5.102.2(react@19.2.8)': + '@tanstack/react-query@5.102.8(react@19.2.8)': dependencies: - '@tanstack/query-core': 5.102.2 + '@tanstack/query-core': 5.102.8 react: 19.2.8 '@tanstack/react-store@0.11.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': @@ -11494,7 +11554,7 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@testing-library/react@16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: '@babel/runtime': 7.29.7 '@testing-library/dom': 10.4.1 @@ -11748,6 +11808,10 @@ snapshots: dependencies: undici-types: 8.3.0 + '@types/node@26.4.0': + dependencies: + undici-types: 8.3.0 + '@types/normalize-package-data@2.4.4': {} '@types/papaparse@5.5.2': @@ -11778,7 +11842,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 26.0.1 + '@types/node': 26.4.0 '@types/yauzl@2.10.3': dependencies: @@ -11787,56 +11851,56 @@ snapshots: '@types/zen-observable@0.8.3': {} - '@typescript-eslint/parser@8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': + '@typescript-eslint/parser@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': dependencies: - '@typescript-eslint/scope-manager': 8.67.0 - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/typescript-estree': 8.67.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) - '@typescript-eslint/visitor-keys': 8.67.0 + '@typescript-eslint/scope-manager': 8.69.0 + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/typescript-estree': 8.69.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) + '@typescript-eslint/visitor-keys': 8.69.0 debug: 4.4.3(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.67.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0)': + '@typescript-eslint/project-service@8.69.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.67.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/tsconfig-utils': 8.69.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/types': 8.69.0 debug: 4.4.3(supports-color@11.0.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.67.0': + '@typescript-eslint/scope-manager@8.69.0': dependencies: - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/visitor-keys': 8.67.0 + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/visitor-keys': 8.69.0 - '@typescript-eslint/tsconfig-utils@8.67.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/tsconfig-utils@8.69.0(@typescript/typescript6@6.0.2)': dependencies: typescript: '@typescript/typescript6@6.0.2' - '@typescript-eslint/type-utils@8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': + '@typescript-eslint/type-utils@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': dependencies: - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/typescript-estree': 8.67.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/typescript-estree': 8.69.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) debug: 4.4.3(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.67.0': {} + '@typescript-eslint/types@8.69.0': {} - '@typescript-eslint/typescript-estree@8.67.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0)': + '@typescript-eslint/typescript-estree@8.69.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0)': dependencies: - '@typescript-eslint/project-service': 8.67.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) - '@typescript-eslint/tsconfig-utils': 8.67.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/visitor-keys': 8.67.0 + '@typescript-eslint/project-service': 8.69.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) + '@typescript-eslint/tsconfig-utils': 8.69.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/visitor-keys': 8.69.0 debug: 4.4.3(supports-color@11.0.0) minimatch: 10.2.5 semver: 7.8.5 @@ -11846,20 +11910,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': + '@typescript-eslint/utils@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) - '@typescript-eslint/scope-manager': 8.67.0 - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/typescript-estree': 8.67.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) + '@typescript-eslint/scope-manager': 8.69.0 + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/typescript-estree': 8.69.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.67.0': + '@typescript-eslint/visitor-keys@8.69.0': dependencies: - '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/types': 8.69.0 eslint-visitor-keys: 5.0.1 '@typescript/typescript-aix-ppc64@7.0.2': @@ -11932,13 +11996,13 @@ snapshots: dependencies: unpic: 4.2.2 - '@unpic/react@1.0.2(next@16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@unpic/react@1.0.2(next@16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: '@unpic/core': 1.0.3 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - next: 16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + next: 16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@upsetjs/venn.js@2.0.0': optionalDependencies: @@ -11956,24 +12020,24 @@ snapshots: '@vinext/types@1.0.0-beta.2': {} - '@vitejs/devtools-kit@0.4.1(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(srvx@0.12.5)': + '@vitejs/devtools-kit@0.4.1(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(srvx@0.12.5)': dependencies: '@devframes/hub': 0.6.2(devframe@0.6.2(@typescript/typescript6@6.0.2)(srvx@0.12.5)) devframe: 0.6.2(@typescript/typescript6@6.0.2)(srvx@0.12.5) mlly: 1.8.2 nostics: 1.2.0 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' transitivePeerDependencies: - '@modelcontextprotocol/sdk' - srvx - typescript - '@vitejs/plugin-react@6.1.0(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))': + '@vitejs/plugin-react@6.1.1(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' - '@vitejs/plugin-rsc@0.5.34(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)': + '@vitejs/plugin-rsc@0.5.34(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)': dependencies: '@rolldown/pluginutils': 1.0.1 es-module-lexer: 2.3.1 @@ -11984,31 +12048,31 @@ snapshots: srvx: 0.12.5 strip-literal: 3.1.0 turbo-stream: 3.2.0 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' - vitefu: 1.1.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' + vitefu: 1.1.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) optionalDependencies: react-server-dom-webpack: 19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@vitest/browser-playwright@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11)': + '@vitest/browser-playwright@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11)': dependencies: - '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.11) - '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@4.1.11) + '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) playwright: 1.62.1 tinyrainbow: 3.1.0 - vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser-playwright@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11)': + '@vitest/browser-playwright@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11)': dependencies: - '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11) - '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0)) + '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11) + '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0)) playwright: 1.62.1 tinyrainbow: 3.1.0 - vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(happy-dom@20.11.6) + vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(happy-dom@20.12.0) transitivePeerDependencies: - bufferutil - msw @@ -12016,40 +12080,40 @@ snapshots: - vite optional: true - '@vitest/browser-preview@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.11)': + '@vitest/browser-preview@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@4.1.11)': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.6(@testing-library/dom@10.4.1) - '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.11) - vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@4.1.11) + vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser-preview@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11)': + '@vitest/browser-preview@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11)': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.6(@testing-library/dom@10.4.1) - '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11) - vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(happy-dom@20.11.6) + '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11) + vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(happy-dom@20.12.0) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.11)': + '@vitest/browser@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@4.1.11)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/utils': 4.1.11 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) ws: 8.21.3 transitivePeerDependencies: - bufferutil @@ -12057,16 +12121,16 @@ snapshots: - utf-8-validate - vite - '@vitest/browser@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11)': + '@vitest/browser@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0)) + '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0)) '@vitest/utils': 4.1.11 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(happy-dom@20.11.6) + vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(happy-dom@20.12.0) ws: 8.21.3 transitivePeerDependencies: - bufferutil @@ -12086,9 +12150,9 @@ snapshots: obug: 2.1.3 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) optionalDependencies: - '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.11) + '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@4.1.11) '@vitest/expect@3.2.4': dependencies: @@ -12107,21 +12171,21 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))': + '@vitest/mocker@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' - '@vitest/mocker@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))': + '@vitest/mocker@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0)' '@vitest/pretty-format@3.2.4': dependencies: @@ -12161,7 +12225,7 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)': + '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)': dependencies: '@oxc-project/runtime': 0.146.0 '@oxc-project/types': 0.146.0 @@ -12182,11 +12246,11 @@ snapshots: esbuild: 0.28.2 fsevents: 2.3.3 jiti: 2.7.0 - tsx: 4.23.12 + tsx: 4.23.13 typescript: '@typescript/typescript6@6.0.2' yaml: 2.9.0 - '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0)': + '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0)': dependencies: '@oxc-project/runtime': 0.146.0 '@oxc-project/types': 0.146.0 @@ -12207,7 +12271,7 @@ snapshots: esbuild: 0.28.2 fsevents: 2.3.3 jiti: 2.7.0 - tsx: 4.23.12 + tsx: 4.23.13 typescript: 7.0.2 yaml: 2.9.0 @@ -12438,6 +12502,8 @@ snapshots: baseline-browser-mapping@2.10.40: {} + baseline-browser-mapping@2.11.20: {} + birecord@0.1.2: {} birpc@4.0.0: {} @@ -12467,13 +12533,21 @@ snapshots: node-releases: 2.0.50 update-browserslist-db: 1.2.3(browserslist@4.28.4) + browserslist@4.28.8: + dependencies: + baseline-browser-mapping: 2.11.20 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.417 + node-releases: 2.0.54 + update-browserslist-db: 1.3.2(browserslist@4.28.8) + buffer-crc32@0.2.13: {} buffer-from@1.1.2: {} buffer-image-size@0.6.4: dependencies: - '@types/node': 26.0.1 + '@types/node': 26.4.0 buffer@5.7.1: dependencies: @@ -12527,6 +12601,8 @@ snapshots: caniuse-lite@1.0.30001799: {} + caniuse-lite@1.0.30001810: {} + canvas@3.2.3: dependencies: node-addon-api: 7.1.1 @@ -12667,6 +12743,8 @@ snapshots: comment-parser@1.4.7: {} + comment-parser@1.4.8: {} + compare-versions@6.1.1: {} concurrently@10.0.5: @@ -12690,9 +12768,9 @@ snapshots: copy-to-clipboard@4.0.2: {} - core-js-compat@3.49.0: + core-js-compat@3.50.0: dependencies: - browserslist: 4.28.4 + browserslist: 4.28.8 cose-base@1.0.3: dependencies: @@ -12977,6 +13055,11 @@ snapshots: bundle-name: 4.1.0 default-browser-id: 5.0.1 + default-browser@5.5.1: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + define-lazy-prop@3.0.0: {} defu@6.1.7: {} @@ -13066,6 +13149,8 @@ snapshots: electron-to-chromium@1.5.380: {} + electron-to-chromium@1.5.417: {} + elkjs@0.11.1: {} embla-carousel-autoplay@8.6.0(embla-carousel@8.6.0): @@ -13146,6 +13231,8 @@ snapshots: es-toolkit@1.51.0: {} + es-toolkit@1.52.0: {} + esbuild@0.28.2: optionalDependencies: '@esbuild/aix-ppc64': 0.28.2 @@ -13183,32 +13270,32 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-compat-utils@0.5.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)): + eslint-compat-utils@0.5.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)): dependencies: - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) semver: 7.8.5 - eslint-json-compat-utils@0.2.3(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(jsonc-eslint-parser@3.3.0): + eslint-json-compat-utils@0.2.3(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(jsonc-eslint-parser@3.3.0): dependencies: - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) esquery: 1.7.0 jsonc-eslint-parser: 3.3.0 - eslint-markdown@0.12.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-markdown@0.12.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: '@eslint/markdown': 7.5.1(supports-color@11.0.0) micromark-util-normalize-identifier: 2.0.1 parse5: 8.0.1 optionalDependencies: - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) transitivePeerDependencies: - supports-color - eslint-plugin-antfu@3.2.3(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)): + eslint-plugin-antfu@3.2.3(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)): dependencies: - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) - eslint-plugin-better-tailwindcss@4.7.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(oxlint@1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(tailwindcss@4.3.3): + eslint-plugin-better-tailwindcss@4.7.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(oxlint@1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(tailwindcss@4.3.3): dependencies: '@eslint/css-tree': 4.0.5 '@valibot/to-json-schema': 1.7.1(valibot@1.4.2(@typescript/typescript6@6.0.2)) @@ -13220,37 +13307,37 @@ snapshots: tsconfig-paths-webpack-plugin: 4.2.0 valibot: 1.4.2(@typescript/typescript6@6.0.2) optionalDependencies: - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) - oxlint: 1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) + oxlint: 1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) transitivePeerDependencies: - '@eslint/css' - typescript - eslint-plugin-command@3.5.3(@typescript-eslint/typescript-estree@8.67.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0))(@typescript-eslint/utils@8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)): + eslint-plugin-command@3.5.3(@typescript-eslint/typescript-estree@8.69.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0))(@typescript-eslint/utils@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)): dependencies: '@es-joy/jsdoccomment': 0.88.0 - '@typescript-eslint/typescript-estree': 8.67.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@typescript-eslint/typescript-estree': 8.69.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) - eslint-plugin-erasable-syntax-only@0.4.2(@typescript-eslint/parser@8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-erasable-syntax-only@0.4.2(@typescript-eslint/parser@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0))(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: - '@typescript-eslint/parser': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/parser': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) cached-factory: 0.3.0 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - eslint-plugin-es-x@7.8.0(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)): + eslint-plugin-es-x@7.8.0(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) '@eslint-community/regexpp': 4.12.2 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) - eslint-compat-utils: 0.5.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) + eslint-compat-utils: 0.5.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) - eslint-plugin-jsdoc@63.3.3(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-jsdoc@63.3.3(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: '@es-joy/jsdoccomment': 0.91.0 '@es-joy/resolve.exports': 1.2.0 @@ -13258,7 +13345,7 @@ snapshots: comment-parser: 1.4.7 debug: 4.4.3(supports-color@11.0.0) escape-string-regexp: 4.0.0 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) espree: 11.2.0 esquery: 1.7.0 html-entities: 2.6.0 @@ -13270,27 +13357,27 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-jsonc@3.4.2(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)): + eslint-plugin-jsonc@3.4.2(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 '@ota-meshi/ast-token-store': 0.3.0 diff-sequences: 29.6.3 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) - eslint-json-compat-utils: 0.2.3(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(jsonc-eslint-parser@3.3.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) + eslint-json-compat-utils: 0.2.3(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(jsonc-eslint-parser@3.3.0) jsonc-eslint-parser: 3.3.0 natural-compare: 1.4.0 synckit: 0.11.13 transitivePeerDependencies: - '@eslint/json' - eslint-plugin-markdown-preferences@0.41.1(@eslint/markdown@8.0.3(supports-color@11.0.0))(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-markdown-preferences@0.41.1(@eslint/markdown@8.0.3(supports-color@11.0.0))(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: '@eslint/markdown': 8.0.3(supports-color@11.0.0) diff-sequences: 29.6.3 emoji-regex-xs: 2.0.1 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) mdast-util-from-markdown: 2.0.3(supports-color@11.0.0) mdast-util-frontmatter: 2.0.1(supports-color@11.0.0) mdast-util-gfm: 3.1.0(supports-color@11.0.0) @@ -13305,13 +13392,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-n@18.3.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)): + eslint-plugin-n@18.3.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) enhanced-resolve: 5.24.1 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) - eslint-plugin-es-x: 7.8.0(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) - get-tsconfig: 4.14.1 + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) + eslint-plugin-es-x: 7.8.0(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) + get-tsconfig: 4.14.3 globals: 15.15.0 globrex: 0.1.2 ignore: 5.3.2 @@ -13319,28 +13406,28 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - eslint-plugin-no-barrel-files@1.3.1(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-no-barrel-files@1.3.1(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) transitivePeerDependencies: - supports-color - typescript - eslint-plugin-perfectionist@5.10.1(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-perfectionist@5.11.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) natural-orderby: 5.0.0 transitivePeerDependencies: - supports-color - typescript - eslint-plugin-pnpm@1.8.0(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)): + eslint-plugin-pnpm@1.8.0(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)): dependencies: empathic: 2.0.1 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) - eslint-json-compat-utils: 0.2.3(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(jsonc-eslint-parser@3.3.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) + eslint-json-compat-utils: 0.2.3(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(jsonc-eslint-parser@3.3.0) jsonc-eslint-parser: 3.3.0 pathe: 2.0.3 pnpm-workspace-yaml: 1.8.0 @@ -13350,94 +13437,94 @@ snapshots: transitivePeerDependencies: - '@eslint/json' - eslint-plugin-react-dom@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-react-dom@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: - '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/jsx': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/jsx': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) compare-versions: 6.1.1 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - eslint-plugin-react-jsx@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-react-jsx@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: - '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/core': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/jsx': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/core': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/jsx': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - eslint-plugin-react-naming-convention@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-react-naming-convention@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: - '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/core': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/var': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/core': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/var': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) ts-pattern: 5.9.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - eslint-plugin-react-rsc@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-react-rsc@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: - '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/core': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/var': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/core': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/var': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - eslint-plugin-react-web-api@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-react-web-api@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: - '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/core': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/var': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/core': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/var': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) birecord: 0.1.2 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) ts-pattern: 5.9.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - eslint-plugin-react-x@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-react-x@5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: - '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/core': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/jsx': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@eslint-react/var': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@typescript-eslint/scope-manager': 8.67.0 - '@typescript-eslint/type-utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/typescript-estree': 8.67.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/ast': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/core': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/eslint': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/jsx': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/shared': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@eslint-react/var': 5.18.6(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/scope-manager': 8.69.0 + '@typescript-eslint/type-utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/typescript-estree': 8.69.0(@typescript/typescript6@6.0.2)(supports-color@11.0.0) + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) compare-versions: 6.1.1 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) string-ts: 2.3.1 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) ts-pattern: 5.9.0 @@ -13445,48 +13532,48 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-regexp@3.1.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)): + eslint-plugin-regexp@3.1.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) '@eslint-community/regexpp': 4.12.2 - comment-parser: 1.4.7 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) - jsdoc-type-pratt-parser: 7.2.0 + comment-parser: 1.4.8 + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) + jsdoc-type-pratt-parser: 7.3.0 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-storybook@10.5.10(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-storybook@10.5.10(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: - '@typescript-eslint/types': 8.67.0 - '@typescript-eslint/utils': 8.67.0(@typescript/typescript6@6.0.2)(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) transitivePeerDependencies: - supports-color - typescript - eslint-plugin-toml@1.5.0(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-toml@1.5.0(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 '@ota-meshi/ast-token-store': 0.3.0 debug: 4.4.3(supports-color@11.0.0) - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) toml-eslint-parser: 1.0.3 transitivePeerDependencies: - supports-color - eslint-plugin-unicorn@71.1.0(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)): + eslint-plugin-unicorn@71.1.0(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) browserslist: 4.28.4 change-case: 5.4.4 ci-info: 4.4.0 - core-js-compat: 3.49.0 + core-js-compat: 3.50.0 detect-indent: 7.0.2 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) find-up-simple: 1.0.1 - globals: 17.7.0 + globals: 17.11.0 indent-string: 5.0.0 is-builtin-module: 5.0.0 is-identifier: 1.1.0 @@ -13497,14 +13584,14 @@ snapshots: semver: 7.8.5 strip-indent: 4.1.1 - eslint-plugin-yml@3.8.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)): + eslint-plugin-yml@3.8.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)): dependencies: '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 '@ota-meshi/ast-token-store': 0.3.0 diff-sequences: 29.6.3 escape-string-regexp: 5.0.0 - eslint: 10.9.0(jiti@2.7.0)(supports-color@11.0.0) + eslint: 10.9.1(jiti@2.7.0)(supports-color@11.0.0) natural-compare: 1.4.0 yaml-eslint-parser: 2.1.0 @@ -13519,9 +13606,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0): + eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.0(jiti@2.7.0)(supports-color@11.0.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.9.1(jiti@2.7.0)(supports-color@11.0.0)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5(supports-color@11.0.0) '@eslint/config-helpers': 0.7.0 @@ -13637,9 +13724,9 @@ snapshots: dependencies: walk-up-path: 4.0.0 - fdir@6.5.0(picomatch@4.0.5): + fdir@6.5.0(picomatch@4.0.7): optionalDependencies: - picomatch: 4.0.5 + picomatch: 4.0.7 fflate@0.7.4: {} @@ -13664,18 +13751,19 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.4.2 + flatted: 3.4.4 keyv: 4.5.4 - flatted@3.4.2: {} + flatted@3.4.4: {} format@0.2.2: {} - formatly@0.3.0: + formatly@0.7.0: dependencies: fd-package-json: 2.0.0 + package-manager-detector: 1.8.0 - foxact@0.3.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + foxact@0.3.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: client-only: 0.0.1 event-target-bus: 1.1.0 @@ -13720,7 +13808,7 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 - get-tsconfig@4.14.1: + get-tsconfig@4.14.3: dependencies: resolve-pkg-maps: 1.0.0 @@ -13751,7 +13839,7 @@ snapshots: globals@15.15.0: {} - globals@17.7.0: {} + globals@17.11.0: {} globrex@0.1.2: {} @@ -13766,9 +13854,9 @@ snapshots: hachure-fill@0.5.2: {} - happy-dom@20.11.6: + happy-dom@20.12.0: dependencies: - '@types/node': 26.0.1 + '@types/node': 26.4.0 '@types/whatwg-mimetype': 3.0.2 '@types/ws': 8.18.1 buffer-image-size: 0.6.4 @@ -13913,7 +14001,7 @@ snapshots: hex-rgb@4.3.0: {} - hono@4.13.3: {} + hono@4.13.5: {} hosted-git-info@9.0.3: dependencies: @@ -13969,7 +14057,7 @@ snapshots: ignore@5.3.2: {} - ignore@7.0.5: {} + ignore@7.0.8: {} image-size@2.0.2: {} @@ -14073,20 +14161,20 @@ snapshots: jiti@2.7.0: {} - jotai-scope@0.11.0(jotai@2.20.2(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8))(react@19.2.8): + jotai-scope@0.11.0(jotai@2.20.3(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8))(react@19.2.8): dependencies: - jotai: 2.20.2(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8) + jotai: 2.20.3(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 - jotai-tanstack-query@0.11.0(@tanstack/query-core@5.102.2)(@tanstack/react-query@5.102.2(react@19.2.8))(jotai@2.20.2(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8))(react@19.2.8): + jotai-tanstack-query@0.11.0(@tanstack/query-core@5.102.8)(@tanstack/react-query@5.102.8(react@19.2.8))(jotai@2.20.3(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8))(react@19.2.8): dependencies: - '@tanstack/query-core': 5.102.2 - jotai: 2.20.2(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8) + '@tanstack/query-core': 5.102.8 + jotai: 2.20.3(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8) optionalDependencies: - '@tanstack/react-query': 5.102.2(react@19.2.8) + '@tanstack/react-query': 5.102.8(react@19.2.8) react: 19.2.8 - jotai@2.20.2(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8): + jotai@2.20.3(@babel/core@7.29.7(supports-color@11.0.0))(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8): optionalDependencies: '@babel/core': 7.29.7(supports-color@11.0.0) '@babel/template': 7.29.7 @@ -14107,12 +14195,14 @@ snapshots: dependencies: argparse: 2.0.1 - js-yaml@5.3.0: + js-yaml@5.4.1: dependencies: argparse: 2.0.1 jsdoc-type-pratt-parser@7.2.0: {} + jsdoc-type-pratt-parser@7.3.0: {} + jsdoc-type-pratt-parser@8.0.0: {} jsesc@3.1.0: {} @@ -14155,16 +14245,16 @@ snapshots: khroma@2.1.0: {} - knip@6.32.2: + knip@6.34.0: dependencies: - fdir: 6.5.0(picomatch@4.0.5) - formatly: 0.3.0 - get-tsconfig: 4.14.1 + fdir: 6.5.0(picomatch@4.0.7) + formatly: 0.7.0 + get-tsconfig: 4.14.3 jiti: 2.7.0 - oxc-parser: 0.143.0 + oxc-parser: 0.147.0 oxc-resolver: 11.24.2 - picomatch: 4.0.5 - smol-toml: 1.7.1 + picomatch: 4.0.7 + smol-toml: 1.8.0 strip-json-comments: 5.0.3 tinyglobby: 0.2.17 unbash: 4.0.10 @@ -14177,7 +14267,7 @@ snapshots: kolorist@1.8.0: {} - ky@2.0.2: {} + ky@2.1.0: {} launch-ide@1.4.5: dependencies: @@ -14342,7 +14432,7 @@ snapshots: dependencies: js-tokens: 4.0.0 - loro-crdt@1.14.1: {} + loro-crdt@1.15.1: {} loupe@3.2.1: {} @@ -14583,14 +14673,14 @@ snapshots: mdn-data@2.29.0: {} - mediabunny@1.55.2: + mediabunny@1.55.5: dependencies: '@types/dom-mediacapture-transform': 0.1.11 '@types/dom-webcodecs': 0.1.13 merge2@1.4.1: {} - mermaid@11.17.0: + mermaid@11.17.2: dependencies: '@braintree/sanitize-url': 7.1.2 '@iconify/utils': 3.1.3 @@ -14915,9 +15005,9 @@ snapshots: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - next@16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + next@16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: - '@next/env': 16.3.3 + '@next/env': 16.3.4 '@swc/helpers': 0.5.23 baseline-browser-mapping: 2.10.40 caniuse-lite: 1.0.30001799 @@ -14926,16 +15016,16 @@ snapshots: react-dom: 19.2.8(react@19.2.8) styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@11.0.0))(react@19.2.8) optionalDependencies: - '@next/swc-darwin-arm64': 16.3.3 - '@next/swc-darwin-x64': 16.3.3 - '@next/swc-linux-arm64-gnu': 16.3.3 - '@next/swc-linux-arm64-musl': 16.3.3 - '@next/swc-linux-x64-gnu': 16.3.3 - '@next/swc-linux-x64-musl': 16.3.3 - '@next/swc-win32-arm64-msvc': 16.3.3 - '@next/swc-win32-x64-msvc': 16.3.3 + '@next/swc-darwin-arm64': 16.3.4 + '@next/swc-darwin-x64': 16.3.4 + '@next/swc-linux-arm64-gnu': 16.3.4 + '@next/swc-linux-arm64-musl': 16.3.4 + '@next/swc-linux-x64-gnu': 16.3.4 + '@next/swc-linux-x64-musl': 16.3.4 + '@next/swc-win32-arm64-msvc': 16.3.4 + '@next/swc-win32-x64-msvc': 16.3.4 '@playwright/test': 1.62.1 - sharp: 0.35.3(@types/node@25.9.5) + sharp: 0.35.4(@types/node@25.9.5) transitivePeerDependencies: - '@babel/core' - '@types/node' @@ -14951,6 +15041,8 @@ snapshots: node-releases@2.0.50: {} + node-releases@2.0.54: {} + node@runtime:22.23.2: {} normalize-package-data@8.0.0: @@ -14967,12 +15059,12 @@ snapshots: dependencies: boolbase: 1.0.0 - nuqs@2.10.0(next@16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8): + nuqs@2.10.1(next@16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8): dependencies: '@standard-schema/spec': 1.1.0 react: 19.2.8 optionalDependencies: - next: 16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + next: 16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) object-assign@4.1.1: {} @@ -15023,6 +15115,15 @@ snapshots: powershell-utils: 0.2.0 wsl-utils: 1.0.0 + open@11.0.2: + dependencies: + default-browser: 5.5.1 + define-lazy-prop: 3.0.0 + is-in-ssh: 1.0.0 + is-inside-container: 1.0.0 + powershell-utils: 0.2.1 + wsl-utils: 1.0.0 + openapi-types@12.1.3: {} optionator@0.9.4: @@ -15070,29 +15171,29 @@ snapshots: '@oxc-parser/binding-win32-ia32-msvc': 0.127.0 '@oxc-parser/binding-win32-x64-msvc': 0.127.0 - oxc-parser@0.143.0: + oxc-parser@0.147.0: dependencies: - '@oxc-project/types': 0.143.0 + '@oxc-project/types': 0.147.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.143.0 - '@oxc-parser/binding-android-arm64': 0.143.0 - '@oxc-parser/binding-darwin-arm64': 0.143.0 - '@oxc-parser/binding-darwin-x64': 0.143.0 - '@oxc-parser/binding-freebsd-x64': 0.143.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.143.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.143.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.143.0 - '@oxc-parser/binding-linux-arm64-musl': 0.143.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.143.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.143.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.143.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.143.0 - '@oxc-parser/binding-linux-x64-gnu': 0.143.0 - '@oxc-parser/binding-linux-x64-musl': 0.143.0 - '@oxc-parser/binding-openharmony-arm64': 0.143.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.143.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.143.0 - '@oxc-parser/binding-win32-x64-msvc': 0.143.0 + '@oxc-parser/binding-android-arm-eabi': 0.147.0 + '@oxc-parser/binding-android-arm64': 0.147.0 + '@oxc-parser/binding-darwin-arm64': 0.147.0 + '@oxc-parser/binding-darwin-x64': 0.147.0 + '@oxc-parser/binding-freebsd-x64': 0.147.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.147.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.147.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.147.0 + '@oxc-parser/binding-linux-arm64-musl': 0.147.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.147.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.147.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.147.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.147.0 + '@oxc-parser/binding-linux-x64-gnu': 0.147.0 + '@oxc-parser/binding-linux-x64-musl': 0.147.0 + '@oxc-parser/binding-openharmony-arm64': 0.147.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.147.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.147.0 + '@oxc-parser/binding-win32-x64-msvc': 0.147.0 oxc-resolver@11.21.2: optionalDependencies: @@ -15138,7 +15239,7 @@ snapshots: '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 - oxfmt@0.64.0(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)): + oxfmt@0.64.0(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)): dependencies: tinypool: 2.1.0 optionalDependencies: @@ -15161,9 +15262,9 @@ snapshots: '@oxfmt/binding-win32-arm64-msvc': 0.64.0 '@oxfmt/binding-win32-ia32-msvc': 0.64.0 '@oxfmt/binding-win32-x64-msvc': 0.64.0 - vite-plus: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) + vite-plus: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) - oxfmt@0.64.0(vite-plus@0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0)): + oxfmt@0.64.0(vite-plus@0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0)): dependencies: tinypool: 2.1.0 optionalDependencies: @@ -15186,7 +15287,7 @@ snapshots: '@oxfmt/binding-win32-arm64-msvc': 0.64.0 '@oxfmt/binding-win32-ia32-msvc': 0.64.0 '@oxfmt/binding-win32-x64-msvc': 0.64.0 - vite-plus: 0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0) + vite-plus: 0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0) oxlint-tsgolint@7.0.2001: optionalDependencies: @@ -15197,7 +15298,7 @@ snapshots: '@oxlint-tsgolint/win32-arm64': 7.0.2001 '@oxlint-tsgolint/win32-x64': 7.0.2001 - oxlint@1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)): + oxlint@1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)): optionalDependencies: '@oxlint/binding-android-arm-eabi': 1.79.0 '@oxlint/binding-android-arm64': 1.79.0 @@ -15219,9 +15320,9 @@ snapshots: '@oxlint/binding-win32-ia32-msvc': 1.79.0 '@oxlint/binding-win32-x64-msvc': 1.79.0 oxlint-tsgolint: 7.0.2001 - vite-plus: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) + vite-plus: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) - oxlint@1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0)): + oxlint@1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0)): optionalDependencies: '@oxlint/binding-android-arm-eabi': 1.79.0 '@oxlint/binding-android-arm64': 1.79.0 @@ -15243,7 +15344,7 @@ snapshots: '@oxlint/binding-win32-ia32-msvc': 1.79.0 '@oxlint/binding-win32-x64-msvc': 1.79.0 oxlint-tsgolint: 7.0.2001 - vite-plus: 0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0) + vite-plus: 0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0) p-event@6.0.1: dependencies: @@ -15261,6 +15362,8 @@ snapshots: package-manager-detector@1.6.0: {} + package-manager-detector@1.8.0: {} + pad-right@0.2.2: dependencies: repeat-string: 1.6.1 @@ -15350,6 +15453,8 @@ snapshots: picomatch@4.0.5: {} + picomatch@4.0.7: {} + pinyin-pro@3.29.3: {} pkg-types@1.3.1: @@ -15412,6 +15517,8 @@ snapshots: powershell-utils@0.2.0: {} + powershell-utils@0.2.1: {} + prebuild-install@7.1.3: dependencies: detect-libc: 2.1.2 @@ -15457,7 +15564,7 @@ snapshots: dependencies: react: 19.2.8 - qs@6.15.3: + qs@6.16.0: dependencies: es-define-property: 1.0.1 side-channel: '@nolyfill/side-channel@1.0.44' @@ -15753,7 +15860,7 @@ snapshots: mdast-util-to-markdown: 2.1.2 unified: 11.0.5 - remend@1.3.0: {} + remend@1.3.1: {} repeat-string@1.6.1: {} @@ -15843,37 +15950,37 @@ snapshots: server-only@0.0.1: {} - sharp@0.35.3(@types/node@25.9.5): + sharp@0.35.4(@types/node@25.9.5): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.8.5 optionalDependencies: - '@img/sharp-darwin-arm64': 0.35.3 - '@img/sharp-darwin-x64': 0.35.3 - '@img/sharp-freebsd-wasm32': 0.35.3 - '@img/sharp-libvips-darwin-arm64': 1.3.2 - '@img/sharp-libvips-darwin-x64': 1.3.2 - '@img/sharp-libvips-linux-arm': 1.3.2 - '@img/sharp-libvips-linux-arm64': 1.3.2 - '@img/sharp-libvips-linux-ppc64': 1.3.2 - '@img/sharp-libvips-linux-riscv64': 1.3.2 - '@img/sharp-libvips-linux-s390x': 1.3.2 - '@img/sharp-libvips-linux-x64': 1.3.2 - '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 - '@img/sharp-libvips-linuxmusl-x64': 1.3.2 - '@img/sharp-linux-arm': 0.35.3 - '@img/sharp-linux-arm64': 0.35.3 - '@img/sharp-linux-ppc64': 0.35.3 - '@img/sharp-linux-riscv64': 0.35.3 - '@img/sharp-linux-s390x': 0.35.3 - '@img/sharp-linux-x64': 0.35.3 - '@img/sharp-linuxmusl-arm64': 0.35.3 - '@img/sharp-linuxmusl-x64': 0.35.3 - '@img/sharp-webcontainers-wasm32': 0.35.3 - '@img/sharp-win32-arm64': 0.35.3 - '@img/sharp-win32-ia32': 0.35.3 - '@img/sharp-win32-x64': 0.35.3 + '@img/sharp-darwin-arm64': 0.35.4 + '@img/sharp-darwin-x64': 0.35.4 + '@img/sharp-freebsd-wasm32': 0.35.4 + '@img/sharp-libvips-darwin-arm64': 1.3.3 + '@img/sharp-libvips-darwin-x64': 1.3.3 + '@img/sharp-libvips-linux-arm': 1.3.3 + '@img/sharp-libvips-linux-arm64': 1.3.3 + '@img/sharp-libvips-linux-ppc64': 1.3.3 + '@img/sharp-libvips-linux-riscv64': 1.3.3 + '@img/sharp-libvips-linux-s390x': 1.3.3 + '@img/sharp-libvips-linux-x64': 1.3.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 + '@img/sharp-linux-arm': 0.35.4 + '@img/sharp-linux-arm64': 0.35.4 + '@img/sharp-linux-ppc64': 0.35.4 + '@img/sharp-linux-riscv64': 0.35.4 + '@img/sharp-linux-s390x': 0.35.4 + '@img/sharp-linux-x64': 0.35.4 + '@img/sharp-linuxmusl-arm64': 0.35.4 + '@img/sharp-linuxmusl-x64': 0.35.4 + '@img/sharp-webcontainers-wasm32': 0.35.4 + '@img/sharp-win32-arm64': 0.35.4 + '@img/sharp-win32-ia32': 0.35.4 + '@img/sharp-win32-x64': 0.35.4 '@types/node': 25.9.5 optional: true @@ -15885,14 +15992,14 @@ snapshots: shell-quote@1.9.0: {} - shiki@4.4.2: + shiki@4.4.3: dependencies: - '@shikijs/core': 4.4.2 - '@shikijs/engine-javascript': 4.4.2 - '@shikijs/engine-oniguruma': 4.4.2 - '@shikijs/langs': 4.4.2 - '@shikijs/themes': 4.4.2 - '@shikijs/types': 4.4.2 + '@shikijs/core': 4.4.3 + '@shikijs/engine-javascript': 4.4.3 + '@shikijs/engine-oniguruma': 4.4.3 + '@shikijs/langs': 4.4.3 + '@shikijs/themes': 4.4.3 + '@shikijs/types': 4.4.3 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.5 @@ -15920,7 +16027,7 @@ snapshots: size-sensor@1.0.3: {} - smol-toml@1.7.1: {} + smol-toml@1.8.0: {} socket.io-client@4.8.3(supports-color@11.0.0): dependencies: @@ -15988,7 +16095,7 @@ snapshots: stdin-discarder@0.3.2: {} - storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)): + storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)): dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.1.0(react@19.2.8) @@ -16009,19 +16116,18 @@ snapshots: ws: 8.21.3 optionalDependencies: '@types/react': 19.2.18 - vite-plus: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) + vite-plus: 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) transitivePeerDependencies: - bufferutil - react - utf-8-validate - streamdown@2.5.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@11.0.0): + streamdown@2.6.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@11.0.0): dependencies: clsx: 2.1.1 hast-util-to-jsx-runtime: 2.3.6(supports-color@11.0.0) html-url-attributes: 3.0.1 marked: 17.0.6 - mermaid: 11.17.0 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) rehype-harden: 1.1.8 @@ -16030,7 +16136,7 @@ snapshots: remark-gfm: 4.0.1(supports-color@11.0.0) remark-parse: 11.0.0(supports-color@11.0.0) remark-rehype: 11.1.2 - remend: 1.3.0 + remend: 1.3.1 tailwind-merge: 3.6.0 unified: 11.0.5 unist-util-visit: 5.1.0 @@ -16184,8 +16290,8 @@ snapshots: tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.5) - picomatch: 4.0.5 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 tinypool@2.1.0: {} @@ -16195,11 +16301,11 @@ snapshots: tinyspy@4.0.4: {} - tldts-core@7.4.10: {} + tldts-core@7.4.11: {} - tldts@7.4.10: + tldts@7.4.11: dependencies: - tldts-core: 7.4.10 + tldts-core: 7.4.11 to-regex-range@5.0.1: dependencies: @@ -16257,7 +16363,7 @@ snapshots: tslib@2.8.1: {} - tsx@4.23.12: + tsx@4.23.13: dependencies: esbuild: 0.28.2 optionalDependencies: @@ -16382,7 +16488,7 @@ snapshots: dependencies: '@jridgewell/remapping': 2.3.5 acorn: 8.17.0 - picomatch: 4.0.5 + picomatch: 4.0.7 webpack-virtual-modules: 0.6.2 update-browserslist-db@1.2.3(browserslist@4.28.4): @@ -16391,6 +16497,12 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.3.2(browserslist@4.28.8): + dependencies: + browserslist: 4.28.8 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -16456,21 +16568,21 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vinext@1.0.0-beta.8(@vitejs/plugin-react@6.1.0(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(@vitejs/plugin-rsc@0.5.34(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8))(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(next@16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8): + vinext@1.0.0-beta.8(@vitejs/plugin-react@6.1.1(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(@vitejs/plugin-rsc@0.5.34(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8))(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(next@16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8): dependencies: - '@unpic/react': 1.0.2(next@16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@unpic/react': 1.0.2(next@16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@vercel/og': 0.8.6 '@vinext/types': 1.0.0-beta.2 - '@vitejs/plugin-react': 6.1.0(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + '@vitejs/plugin-react': 6.1.1(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) ipaddr.js: 2.4.0 magic-string: 0.30.21 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' vite-plugin-commonjs: 0.10.4 web-vitals: 4.2.4 optionalDependencies: - '@vitejs/plugin-rsc': 0.5.34(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) + '@vitejs/plugin-rsc': 0.5.34(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(react-dom@19.2.8(react@19.2.8))(react-server-dom-webpack@19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) react-server-dom-webpack: 19.2.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8) transitivePeerDependencies: - next @@ -16488,9 +16600,9 @@ snapshots: fast-glob: 3.3.3 magic-string: 0.30.21 - vite-plugin-inspect@12.0.2(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(srvx@0.12.5): + vite-plugin-inspect@12.0.2(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(srvx@0.12.5): dependencies: - '@vitejs/devtools-kit': 0.4.1(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(srvx@0.12.5) + '@vitejs/devtools-kit': 0.4.1(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(srvx@0.12.5) ansis: 4.3.1 error-stack-parser-es: 2.0.1 obug: 2.1.3 @@ -16499,47 +16611,47 @@ snapshots: perfect-debounce: 2.1.0 sirv: 3.0.2 unplugin-utils: 0.3.2 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' transitivePeerDependencies: - '@modelcontextprotocol/sdk' - srvx - typescript - vite-plugin-storybook-nextjs@3.3.0(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(next@16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)))(supports-color@11.0.0): + vite-plugin-storybook-nextjs@3.3.0(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(next@16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)))(supports-color@11.0.0): dependencies: '@next/env': 16.0.0 image-size: 2.0.2 magic-string: 0.30.21 module-alias: 2.3.4 - next: 16.3.3(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + next: 16.3.4(@babel/core@7.29.7(supports-color@11.0.0))(@playwright/test@1.62.1)(@types/node@25.9.5)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) ts-dedent: 2.3.0 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' - vite-tsconfig-paths: 5.1.4(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(supports-color@11.0.0) + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' + vite-tsconfig-paths: 5.1.4(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(supports-color@11.0.0) transitivePeerDependencies: - supports-color - typescript - vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0): + vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0): dependencies: '@oxc-project/types': 0.146.0 '@oxlint/plugins': 1.79.0 - '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.11) - '@vitest/browser-preview': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.11) + '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@4.1.11) + '@vitest/browser-preview': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@4.1.11) '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 '@vitest/spy': 4.1.11 '@vitest/utils': 4.1.11 - '@voidzero-dev/vite-plus-core': 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0) - oxfmt: 0.64.0(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) - oxlint: 1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + '@voidzero-dev/vite-plus-core': 0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0) + oxfmt: 0.64.0(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) + oxlint: 1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) oxlint-tsgolint: 7.0.2001 - vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) optionalDependencies: - '@vitest/browser-playwright': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) + '@vitest/browser-playwright': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) '@voidzero-dev/vite-plus-darwin-arm64': 0.3.0 '@voidzero-dev/vite-plus-darwin-x64': 0.3.0 '@voidzero-dev/vite-plus-linux-arm64-gnu': 0.3.0 @@ -16579,26 +16691,26 @@ snapshots: - vite - yaml - vite-plus@0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0): + vite-plus@0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0): dependencies: '@oxc-project/types': 0.146.0 '@oxlint/plugins': 1.79.0 - '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11) - '@vitest/browser-preview': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11) + '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11) + '@vitest/browser-preview': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11) '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0)) + '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 '@vitest/spy': 4.1.11 '@vitest/utils': 4.1.11 - '@voidzero-dev/vite-plus-core': 0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0) - oxfmt: 0.64.0(vite-plus@0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0)) - oxlint: 1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.11.6)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0)) + '@voidzero-dev/vite-plus-core': 0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0) + oxfmt: 0.64.0(vite-plus@0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0)) + oxlint: 1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(esbuild@0.28.2)(happy-dom@20.12.0)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0)) oxlint-tsgolint: 7.0.2001 - vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(happy-dom@20.11.6) + vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(happy-dom@20.12.0) optionalDependencies: - '@vitest/browser-playwright': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) + '@vitest/browser-playwright': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) '@voidzero-dev/vite-plus-darwin-arm64': 0.3.0 '@voidzero-dev/vite-plus-darwin-x64': 0.3.0 '@voidzero-dev/vite-plus-linux-arm64-gnu': 0.3.0 @@ -16638,26 +16750,26 @@ snapshots: - vite - yaml - vite-tsconfig-paths@5.1.4(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(supports-color@11.0.0): + vite-tsconfig-paths@5.1.4(@typescript/typescript6@6.0.2)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(supports-color@11.0.0): dependencies: debug: 4.4.3(supports-color@11.0.0) globrex: 0.1.2 tsconfck: 3.1.6(@typescript/typescript6@6.0.2) optionalDependencies: - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' transitivePeerDependencies: - supports-color - typescript - vitefu@1.1.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)): + vitefu@1.1.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)): optionalDependencies: - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' vitest-browser-react@2.2.0(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.11): dependencies: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) optionalDependencies: '@types/react': 19.2.18 '@types/react-dom': 19.2.5(@types/react@19.2.18) @@ -16666,12 +16778,12 @@ snapshots: dependencies: cssfontparser: 1.2.1 moo-color: 1.0.3 - vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6) + vitest: 4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0) - vitest@4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(happy-dom@20.11.6): + vitest@4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(happy-dom@20.12.0): dependencies: '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)) + '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -16682,27 +16794,27 @@ snapshots: magic-string: 0.30.21 obug: 2.1.3 pathe: 2.0.3 - picomatch: 4.0.5 + picomatch: 4.0.7 std-env: 4.1.0 tinybench: 2.9.0 tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)' why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.9.5 - '@vitest/browser-playwright': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) - '@vitest/browser-preview': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(yaml@2.9.0))(vitest@4.1.11) + '@vitest/browser-playwright': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) + '@vitest/browser-preview': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(@typescript/typescript6@6.0.2)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@4.1.11) '@vitest/coverage-v8': 4.1.11(@vitest/browser@4.1.11)(vitest@4.1.11) - happy-dom: 20.11.6 + happy-dom: 20.12.0 transitivePeerDependencies: - msw - vitest@4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(happy-dom@20.11.6): + vitest@4.1.11(@types/node@25.9.5)(@vitest/browser-playwright@4.1.11)(@vitest/browser-preview@4.1.11)(@vitest/coverage-v8@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(happy-dom@20.12.0): dependencies: '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0)) + '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -16713,20 +16825,20 @@ snapshots: magic-string: 0.30.21 obug: 2.1.3 pathe: 2.0.3 - picomatch: 4.0.5 + picomatch: 4.0.7 std-env: 4.1.0 tinybench: 2.9.0 tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0)' why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.9.5 - '@vitest/browser-playwright': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) - '@vitest/browser-preview': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11) + '@vitest/browser-playwright': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(playwright@1.62.1)(vitest@4.1.11) + '@vitest/browser-preview': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@25.9.5)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.11) '@vitest/coverage-v8': 4.1.11(@vitest/browser@4.1.11)(vitest@4.1.11) - happy-dom: 20.11.6 + happy-dom: 20.12.0 transitivePeerDependencies: - msw @@ -16873,6 +16985,8 @@ snapshots: zod@4.4.3: {} + zod@4.5.4: {} + zrender@6.1.0: dependencies: tslib: 2.3.0 @@ -16899,8 +17013,8 @@ snapshots: zwitch@2.0.4: {} time: - '@amplitude/analytics-browser@2.45.6': '2026-08-12T17:50:48.412Z' - '@amplitude/plugin-session-replay-browser@1.33.8': '2026-08-12T17:46:12.315Z' + '@amplitude/analytics-browser@2.45.8': '2026-08-27T22:21:54.725Z' + '@amplitude/plugin-session-replay-browser@1.35.0': '2026-08-31T19:27:50.780Z' '@axe-core/playwright@4.13.0': '2026-08-11T17:07:40.763Z' '@base-ui/react@1.7.0': '2026-08-04T09:55:20.089Z' '@chromatic-com/storybook@5.3.0': '2026-08-06T08:33:46.580Z' @@ -16923,7 +17037,7 @@ time: '@lexical/selection@0.47.0': '2026-07-09T21:14:16.798Z' '@lexical/text@0.47.0': '2026-07-09T21:14:30.676Z' '@lexical/utils@0.47.0': '2026-07-09T21:14:36.426Z' - '@mediabunny/mp3-encoder@1.55.2': '2026-08-21T18:21:45.711Z' + '@mediabunny/mp3-encoder@1.55.5': '2026-08-31T12:17:39.218Z' '@monaco-editor/react@4.7.0': '2025-02-13T16:13:41.390Z' '@napi-rs/keyring@1.3.0': '2026-04-30T09:56:44.246Z' '@orpc/client@1.15.0': '2026-08-08T13:52:09.241Z' @@ -16933,7 +17047,7 @@ time: '@playwright/test@1.62.1': '2026-07-30T16:36:55.324Z' '@remixicon/react@4.9.0': '2026-01-29T10:53:18.993Z' '@rgrove/parse-xml@4.2.3': '2026-07-26T23:12:17.833Z' - '@sentry/react@10.70.0': '2026-08-10T11:05:59.874Z' + '@sentry/react@10.73.0': '2026-08-31T16:57:44.443Z' '@storybook/addon-a11y@10.5.10': '2026-08-20T10:45:18.990Z' '@storybook/addon-docs@10.5.10': '2026-08-20T10:48:20.529Z' '@storybook/addon-links@10.5.10': '2026-08-20T10:44:25.582Z' @@ -16949,16 +17063,16 @@ time: '@t3-oss/env-nextjs@0.13.11': '2026-03-22T19:16:09.026Z' '@tailwindcss/postcss@4.3.3': '2026-07-16T12:03:56.054Z' '@tailwindcss/vite@4.3.3': '2026-07-16T12:04:06.316Z' - '@tanstack/eslint-plugin-query@5.102.2': '2026-08-23T18:00:24.776Z' + '@tanstack/eslint-plugin-query@5.102.8': '2026-08-27T16:07:09.822Z' '@tanstack/form-core@1.33.5': '2026-08-11T12:45:38.255Z' - '@tanstack/query-core@5.102.2': '2026-08-23T18:00:26.529Z' + '@tanstack/query-core@5.102.8': '2026-08-27T16:06:49.682Z' '@tanstack/react-form@1.33.5': '2026-08-11T12:45:38.642Z' '@tanstack/react-hotkeys@0.10.0': '2026-04-25T12:28:06.989Z' - '@tanstack/react-query@5.102.2': '2026-08-23T18:00:30.589Z' + '@tanstack/react-query@5.102.8': '2026-08-27T16:06:57.089Z' '@tanstack/react-virtual@3.14.10': '2026-08-18T15:06:28.045Z' '@testing-library/dom@10.4.1': '2025-07-27T13:23:37.151Z' '@testing-library/jest-dom@6.9.1': '2025-10-01T20:04:22.720Z' - '@testing-library/react@16.3.2': '2026-01-19T10:59:08.185Z' + '@testing-library/react@16.3.3': '2026-08-27T17:41:18.735Z' '@testing-library/user-event@14.6.6': '2026-08-22T02:06:46.844Z' '@tsslint/cli@3.1.4': '2026-06-16T18:21:29.075Z' '@tsslint/compat-eslint@3.1.4': '2026-06-16T18:21:23.786Z' @@ -16971,9 +17085,9 @@ time: '@types/react-dom@19.2.5': '2026-08-23T21:05:23.671Z' '@types/react@19.2.18': '2026-07-30T21:54:03.456Z' '@types/sortablejs@1.15.9': '2025-10-24T04:31:45.132Z' - '@typescript-eslint/parser@8.67.0': '2026-08-10T17:22:16.082Z' + '@typescript-eslint/parser@8.69.0': '2026-08-31T17:09:13.713Z' '@typescript/typescript6@6.0.2': '2026-07-06T18:06:47.459Z' - '@vitejs/plugin-react@6.1.0': '2026-08-20T02:49:46.306Z' + '@vitejs/plugin-react@6.1.1': '2026-08-28T03:30:56.619Z' '@vitejs/plugin-rsc@0.5.34': '2026-08-07T07:38:06.175Z' '@vitest/browser-playwright@4.1.11': '2026-08-18T14:22:33.004Z' '@vitest/coverage-v8@4.1.11': '2026-08-18T14:22:18.896Z' @@ -17000,7 +17114,7 @@ time: embla-carousel-fade@8.6.0: '2025-04-04T17:37:50.278Z' embla-carousel-react@8.6.0: '2025-04-04T17:37:53.976Z' emoji-mart@5.6.0: '2024-04-25T14:22:21.440Z' - es-toolkit@1.51.0: '2026-08-17T02:49:29.399Z' + es-toolkit@1.52.0: '2026-08-28T07:22:17.687Z' eslint-markdown@0.12.1: '2026-07-10T23:35:26.055Z' eslint-plugin-antfu@3.2.3: '2026-05-11T02:24:38.348Z' eslint-plugin-better-tailwindcss@4.7.0: '2026-07-19T13:26:01.366Z' @@ -17011,21 +17125,21 @@ time: eslint-plugin-markdown-preferences@0.41.1: '2026-04-09T23:28:41.552Z' eslint-plugin-n@18.3.0: '2026-08-08T14:03:55.663Z' eslint-plugin-no-barrel-files@1.3.1: '2026-04-12T18:28:18.653Z' - eslint-plugin-perfectionist@5.10.1: '2026-08-04T05:40:14.988Z' + eslint-plugin-perfectionist@5.11.0: '2026-08-31T09:02:33.026Z' eslint-plugin-pnpm@1.8.0: '2026-08-13T06:26:51.536Z' eslint-plugin-regexp@3.1.1: '2026-06-25T23:07:43.989Z' eslint-plugin-storybook@10.5.10: '2026-08-20T10:46:43.128Z' eslint-plugin-toml@1.5.0: '2026-07-25T01:46:13.957Z' eslint-plugin-unicorn@71.1.0: '2026-07-06T22:07:48.695Z' eslint-plugin-yml@3.8.1: '2026-08-05T03:50:22.441Z' - eslint@10.9.0: '2026-08-21T13:07:47.620Z' + eslint@10.9.1: '2026-08-24T18:21:58.062Z' eventsource-parser@3.1.1: '2026-08-10T15:55:29.938Z' fast-deep-equal@3.1.3: '2020-06-08T07:27:28.474Z' - foxact@0.3.9: '2026-08-22T13:25:04.932Z' + foxact@0.3.10: '2026-08-27T16:19:48.187Z' fuse.js@7.5.0: '2026-07-13T17:23:36.705Z' - happy-dom@20.11.6: '2026-08-19T23:45:39.849Z' + happy-dom@20.12.0: '2026-08-29T16:12:16.364Z' hast-util-to-jsx-runtime@2.3.6: '2025-03-05T11:30:29.166Z' - hono@4.13.3: '2026-08-18T10:56:09.752Z' + hono@4.13.5: '2026-08-26T02:00:22.990Z' html-entities@2.6.0: '2025-03-30T15:40:10.885Z' html-to-image@1.11.13: '2025-02-14T01:43:48.709Z' i18next-resources-to-backend@1.2.3: '2026-07-31T15:07:13.021Z' @@ -17034,34 +17148,34 @@ time: immer@11.1.18: '2026-08-19T07:25:22.934Z' jotai-scope@0.11.0: '2026-05-13T18:43:15.331Z' jotai-tanstack-query@0.11.0: '2025-08-01T02:55:49.826Z' - jotai@2.20.2: '2026-07-14T13:52:11.083Z' + jotai@2.20.3: '2026-08-24T07:26:18.202Z' js-cookie@3.0.8: '2026-05-29T10:51:39.065Z' - js-yaml@5.3.0: '2026-08-14T09:31:39.879Z' + js-yaml@5.4.1: '2026-08-26T19:31:37.034Z' jsonschema@1.5.0: '2025-01-07T15:09:11.287Z' katex@0.17.0: '2026-05-22T08:06:26.967Z' - knip@6.32.2: '2026-08-11T20:34:19.949Z' - ky@2.0.2: '2026-04-21T08:58:46.923Z' + knip@6.34.0: '2026-08-31T22:54:33.744Z' + ky@2.1.0: '2026-08-28T13:10:48.983Z' lexical-code-no-prism@0.41.0: '2026-03-08T16:50:40.266Z' lexical@0.47.0: '2026-07-09T21:11:46.237Z' lockfile@1.0.4: '2018-04-17T00:36:12.565Z' - loro-crdt@1.14.1: '2026-08-10T11:39:06.910Z' - mediabunny@1.55.2: '2026-08-21T18:24:30.681Z' - mermaid@11.17.0: '2026-08-19T09:20:08.814Z' + loro-crdt@1.15.1: '2026-08-29T09:52:09.072Z' + mediabunny@1.55.5: '2026-08-31T12:20:23.255Z' + mermaid@11.17.2: '2026-08-25T11:52:39.930Z' mime@4.1.0: '2025-09-12T17:53:01.376Z' mitt@3.0.1: '2023-07-04T17:31:47.638Z' motion@12.43.0: '2026-07-28T14:29:31.291Z' negotiator@1.1.0: '2026-08-20T16:45:05.235Z' next-themes@0.4.6: '2025-03-11T21:02:05.882Z' - next@16.3.3: '2026-08-25T15:32:19.558Z' - nuqs@2.10.0: '2026-08-20T08:10:12.660Z' - open@11.0.1: '2026-08-13T23:39:43.829Z' + next@16.3.4: '2026-08-31T20:00:51.381Z' + nuqs@2.10.1: '2026-08-25T10:59:58.402Z' + open@11.0.2: '2026-08-29T13:12:01.838Z' ora@9.4.1: '2026-06-22T12:24:49.225Z' picocolors@1.1.1: '2024-10-16T18:20:03.921Z' pinyin-pro@3.29.3: '2026-08-19T01:06:36.944Z' playwright@1.62.1: '2026-07-30T16:38:49.134Z' postcss@8.5.26: '2026-08-06T08:33:00.043Z' qrcode.react@4.2.0: '2024-12-11T17:22:40.569Z' - qs@6.15.3: '2026-06-24T20:03:49.752Z' + qs@6.16.0: '2026-08-29T23:50:15.803Z' react-dom@19.2.8: '2026-07-21T15:41:41.267Z' react-easy-crop@6.2.3: '2026-07-24T14:14:31.126Z' react-i18next@17.0.12: '2026-08-20T10:15:43.090Z' @@ -17076,17 +17190,17 @@ time: remark-directive@4.0.0: '2025-02-27T15:15:20.630Z' scheduler@0.27.0: '2025-10-01T21:39:15.208Z' server-only@0.0.1: '2022-09-03T01:07:26.139Z' - shiki@4.4.2: '2026-08-05T00:41:14.729Z' + shiki@4.4.3: '2026-08-10T04:57:41.135Z' socket.io-client@4.8.3: '2025-12-23T16:39:16.428Z' sortablejs@1.15.7: '2026-02-11T22:42:31.720Z' std-semver@1.0.8: '2026-03-09T17:23:55.795Z' storybook@10.5.10: '2026-08-20T10:52:53.637Z' - streamdown@2.5.0: '2026-03-17T17:35:05.216Z' + streamdown@2.6.0: '2026-08-24T14:07:33.027Z' string-ts@2.3.1: '2025-11-28T17:33:10.099Z' tailwind-merge@3.6.0: '2026-05-10T12:56:43.142Z' tailwindcss@4.3.3: '2026-07-16T12:03:35.267Z' - tldts@7.4.10: '2026-07-30T23:11:08.153Z' - tsx@4.23.12: '2026-08-10T03:41:31.093Z' + tldts@7.4.11: '2026-08-24T07:31:22.093Z' + tsx@4.23.13: '2026-08-30T00:46:16.265Z' typescript@7.0.2: '2026-07-08T15:55:18.431Z' uglify-js@3.19.3: '2024-08-29T13:49:01.316Z' undici@7.29.0: '2026-07-24T12:52:58.701Z' @@ -17099,6 +17213,6 @@ time: vitest-browser-react@2.2.0: '2026-04-05T06:56:34.635Z' vitest-canvas-mock@1.1.5: '2026-08-13T07:03:06.784Z' vitest@4.1.11: '2026-08-18T14:27:07.240Z' - zod@4.4.3: '2026-05-04T07:06:40.819Z' + zod@4.5.4: '2026-08-29T17:55:42.775Z' zundo@2.3.0: '2024-11-17T16:35:11.372Z' zustand@5.0.15: '2026-08-13T00:39:55.466Z' diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a2538f668af..28b40c1b9cc 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -46,11 +46,11 @@ overrides: esbuild@>=0.27.3 <0.28.1: ^0.28.2 is-core-module: npm:@nolyfill/is-core-module@^1.0.39 js-yaml@<=4.1.1: ^4.1.2 - picomatch@>=4.0.0 <4.0.4: 4.0.5 + picomatch@>=4.0.0 <4.0.4: 4.0.7 postcss-selector-parser@>=6.0.0 <6.1.3: 6.1.4 postcss-selector-parser@>=7.0.0 <7.1.3: 7.1.5 postcss@<8.5.10: ^8.5.10 - rollup@>=4.0.0 <4.59.0: 4.62.5 + rollup@>=4.0.0 <4.59.0: 4.63.1 safer-buffer: npm:@nolyfill/safer-buffer@^1.0.44 side-channel: npm:@nolyfill/side-channel@^1.0.44 solid-js: 1.9.15 @@ -62,8 +62,8 @@ overrides: yaml@>=2.0.0 <2.8.3: 2.9.0 yauzl@<3.2.1: 3.2.1 catalog: - '@amplitude/analytics-browser': 2.45.6 - '@amplitude/plugin-session-replay-browser': 1.33.8 + '@amplitude/analytics-browser': 2.45.8 + '@amplitude/plugin-session-replay-browser': 1.35.0 '@axe-core/playwright': 4.13.0 '@base-ui/react': 1.7.0 '@chromatic-com/storybook': 5.3.0 @@ -87,7 +87,7 @@ catalog: '@lexical/selection': 0.47.0 '@lexical/text': 0.47.0 '@lexical/utils': 0.47.0 - '@mediabunny/mp3-encoder': 1.55.2 + '@mediabunny/mp3-encoder': 1.55.5 '@monaco-editor/react': 4.7.0 '@napi-rs/keyring': 1.3.0 '@orpc/client': 1.15.0 @@ -97,7 +97,7 @@ catalog: '@playwright/test': 1.62.1 '@remixicon/react': 4.9.0 '@rgrove/parse-xml': 4.2.3 - '@sentry/react': 10.70.0 + '@sentry/react': 10.73.0 '@storybook/addon-a11y': 10.5.10 '@storybook/addon-docs': 10.5.10 '@storybook/addon-links': 10.5.10 @@ -113,16 +113,16 @@ catalog: '@t3-oss/env-nextjs': 0.13.11 '@tailwindcss/postcss': 4.3.3 '@tailwindcss/vite': 4.3.3 - '@tanstack/eslint-plugin-query': 5.102.2 + '@tanstack/eslint-plugin-query': 5.102.8 '@tanstack/form-core': 1.33.5 - '@tanstack/query-core': 5.102.2 + '@tanstack/query-core': 5.102.8 '@tanstack/react-form': 1.33.5 '@tanstack/react-hotkeys': 0.10.0 - '@tanstack/react-query': 5.102.2 + '@tanstack/react-query': 5.102.8 '@tanstack/react-virtual': 3.14.10 '@testing-library/dom': 10.4.1 '@testing-library/jest-dom': 6.9.1 - '@testing-library/react': 16.3.2 + '@testing-library/react': 16.3.3 '@testing-library/user-event': 14.6.6 '@tsslint/cli': 3.1.4 '@tsslint/compat-eslint': 3.1.4 @@ -135,9 +135,9 @@ catalog: '@types/react': 19.2.18 '@types/react-dom': 19.2.5 '@types/sortablejs': 1.15.9 - '@typescript-eslint/parser': 8.67.0 + '@typescript-eslint/parser': 8.69.0 '@typescript/native': npm:typescript@7.0.2 - '@vitejs/plugin-react': 6.1.0 + '@vitejs/plugin-react': 6.1.1 '@vitejs/plugin-rsc': 0.5.34 '@vitest/browser-playwright': 4.1.11 '@vitest/coverage-v8': 4.1.11 @@ -163,8 +163,8 @@ catalog: embla-carousel-fade: 8.6.0 embla-carousel-react: 8.6.0 emoji-mart: 5.6.0 - es-toolkit: 1.51.0 - eslint: 10.9.0 + es-toolkit: 1.52.0 + eslint: 10.9.1 eslint-markdown: 0.12.1 eslint-plugin-antfu: 3.2.3 eslint-plugin-better-tailwindcss: 4.7.0 @@ -175,7 +175,7 @@ catalog: eslint-plugin-markdown-preferences: 0.41.1 eslint-plugin-n: 18.3.0 eslint-plugin-no-barrel-files: 1.3.1 - eslint-plugin-perfectionist: 5.10.1 + eslint-plugin-perfectionist: 5.11.0 eslint-plugin-pnpm: 1.8.0 eslint-plugin-regexp: 3.1.1 eslint-plugin-storybook: 10.5.10 @@ -184,46 +184,46 @@ catalog: eslint-plugin-yml: 3.8.1 eventsource-parser: 3.1.1 fast-deep-equal: 3.1.3 - foxact: 0.3.9 + foxact: 0.3.10 fuse.js: 7.5.0 - happy-dom: 20.11.6 + happy-dom: 20.12.0 hast-util-to-jsx-runtime: 2.3.6 - hono: 4.13.3 + hono: 4.13.5 html-entities: 2.6.0 html-to-image: 1.11.13 i18next: 26.4.0 i18next-resources-to-backend: 1.2.3 iconify-import-svg: 0.2.0 immer: 11.1.18 - jotai: 2.20.2 + jotai: 2.20.3 jotai-scope: 0.11.0 jotai-tanstack-query: 0.11.0 js-cookie: 3.0.8 - js-yaml: 5.3.0 + js-yaml: 5.4.1 jsonschema: 1.5.0 katex: 0.17.0 - knip: 6.32.2 - ky: 2.0.2 + knip: 6.34.0 + ky: 2.1.0 lexical: 0.47.0 lockfile: 1.0.4 - loro-crdt: 1.14.1 - mediabunny: 1.55.2 - mermaid: 11.17.0 + loro-crdt: 1.15.1 + mediabunny: 1.55.5 + mermaid: 11.17.2 mime: 4.1.0 mitt: 3.0.1 motion: 12.43.0 negotiator: 1.1.0 - next: 16.3.3 + next: 16.3.4 next-themes: 0.4.6 - nuqs: 2.10.0 - open: 11.0.1 + nuqs: 2.10.1 + open: 11.0.2 ora: 9.4.1 picocolors: 1.1.1 pinyin-pro: 3.29.3 playwright: 1.62.1 postcss: 8.5.26 qrcode.react: 4.2.0 - qs: 6.15.3 + qs: 6.16.0 react: 19.2.8 react-dom: 19.2.8 react-easy-crop: 6.2.3 @@ -238,17 +238,17 @@ catalog: remark-directive: 4.0.0 scheduler: 0.27.0 server-only: 0.0.1 - shiki: 4.4.2 + shiki: 4.4.3 socket.io-client: 4.8.3 sortablejs: 1.15.7 std-semver: 1.0.8 storybook: 10.5.10 - streamdown: 2.5.0 + streamdown: 2.6.0 string-ts: 2.3.1 tailwind-merge: 3.6.0 tailwindcss: 4.3.3 - tldts: 7.4.10 - tsx: 4.23.12 + tldts: 7.4.11 + tsx: 4.23.13 typescript: npm:@typescript/typescript6@6.0.2 uglify-js: 3.19.3 undici: 7.29.0 @@ -262,7 +262,7 @@ catalog: vitest: 4.1.11 vitest-browser-react: 2.2.0 vitest-canvas-mock: 1.1.5 - zod: 4.4.3 + zod: 4.5.4 zundo: 2.3.0 zustand: 5.0.15 peerDependencyRules: diff --git a/web/app/components/base/markdown-blocks/__tests__/img.spec.tsx b/web/app/components/base/markdown-blocks/__tests__/img.spec.tsx index ed703ae844d..09da808d1c1 100644 --- a/web/app/components/base/markdown-blocks/__tests__/img.spec.tsx +++ b/web/app/components/base/markdown-blocks/__tests__/img.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import { vi } from 'vitest' +import { vi } from 'vite-plus/test' import { Img } from '..' vi.mock('@/app/components/base/image-gallery', () => ({ diff --git a/web/features/agent-v2/agent-detail/configure/components/orchestrate/prompt-editor/__tests__/slash.spec.tsx b/web/features/agent-v2/agent-detail/configure/components/orchestrate/prompt-editor/__tests__/slash.spec.tsx index a35ac10f899..7ef45fc1e97 100644 --- a/web/features/agent-v2/agent-detail/configure/components/orchestrate/prompt-editor/__tests__/slash.spec.tsx +++ b/web/features/agent-v2/agent-detail/configure/components/orchestrate/prompt-editor/__tests__/slash.spec.tsx @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it, vi } from 'vite-plus/test' import { AgentPromptSlashMenu } from '../slash' describe('AgentPromptSlashMenu', () => { diff --git a/web/features/skills/__tests__/detail-page.spec.tsx b/web/features/skills/__tests__/detail-page.spec.tsx index 1ba179b29b0..58293182aed 100644 --- a/web/features/skills/__tests__/detail-page.spec.tsx +++ b/web/features/skills/__tests__/detail-page.spec.tsx @@ -12,7 +12,7 @@ import { act, fireEvent, render, screen, waitFor, within } from '@testing-librar import userEvent from '@testing-library/user-event' import copy from 'copy-to-clipboard' import { StrictMode } from 'react' -import { beforeEach, describe, expect, it, vi } from 'vitest' +import { beforeEach, describe, expect, it, vi } from 'vite-plus/test' import { gotoAnythingDialogHandle } from '@/app/components/goto-anything/dialog-handle' import SkillDetailPage from '../detail-page' diff --git a/web/features/skills/__tests__/page.spec.tsx b/web/features/skills/__tests__/page.spec.tsx index 89378fad9ac..07ec0652ea3 100644 --- a/web/features/skills/__tests__/page.spec.tsx +++ b/web/features/skills/__tests__/page.spec.tsx @@ -9,7 +9,7 @@ import { toast } from '@langgenius/dify-ui/toast' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { fireEvent, render, screen, waitFor, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { beforeEach, describe, expect, it, vi } from 'vitest' +import { beforeEach, describe, expect, it, vi } from 'vite-plus/test' import SkillsPage from '../page' type SkillsInfiniteOptions = { diff --git a/web/features/skills/detail/__tests__/file-tree-items.spec.tsx b/web/features/skills/detail/__tests__/file-tree-items.spec.tsx index 858d71618e3..30ec3ccc629 100644 --- a/web/features/skills/detail/__tests__/file-tree-items.spec.tsx +++ b/web/features/skills/detail/__tests__/file-tree-items.spec.tsx @@ -5,7 +5,7 @@ import type { import type { FileTreeNode } from '../shared' import { render, screen, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it, vi } from 'vite-plus/test' import { FileTreeItem, FileTreeNameInput } from '../file-tree-items' const skillFile: SkillFileResponse = { diff --git a/web/features/skills/detail/__tests__/markdown-editor.spec.tsx b/web/features/skills/detail/__tests__/markdown-editor.spec.tsx index c2173c53859..c8a58d4528b 100644 --- a/web/features/skills/detail/__tests__/markdown-editor.spec.tsx +++ b/web/features/skills/detail/__tests__/markdown-editor.spec.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from 'react' import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { createRef } from 'react' -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it, vi } from 'vite-plus/test' import { MarkdownBodyReferencePreview, MarkdownLiveBodyEditor } from '../markdown-editor' vi.mock('@/app/components/base/markdown', () => ({ diff --git a/web/features/skills/detail/__tests__/publish-bar.spec.tsx b/web/features/skills/detail/__tests__/publish-bar.spec.tsx index 4b5201a65e3..0be27952665 100644 --- a/web/features/skills/detail/__tests__/publish-bar.spec.tsx +++ b/web/features/skills/detail/__tests__/publish-bar.spec.tsx @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { beforeEach, describe, expect, it, vi } from 'vitest' +import { beforeEach, describe, expect, it, vi } from 'vite-plus/test' import { SkillPublishBar } from '../publish-bar' describe('SkillPublishBar', () => { diff --git a/web/features/skills/detail/__tests__/reference-chip.spec.ts b/web/features/skills/detail/__tests__/reference-chip.spec.ts index 972e610325c..4f658c59cfe 100644 --- a/web/features/skills/detail/__tests__/reference-chip.spec.ts +++ b/web/features/skills/detail/__tests__/reference-chip.spec.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest' +import { describe, expect, it } from 'vite-plus/test' import { renderMarkdownLiveEditorContent, serializeMarkdownLiveEditorNode } from '../shared' describe('markdown reference chip', () => { diff --git a/web/features/skills/detail/__tests__/shared.spec.ts b/web/features/skills/detail/__tests__/shared.spec.ts index c60e48ba3b4..da353608bde 100644 --- a/web/features/skills/detail/__tests__/shared.spec.ts +++ b/web/features/skills/detail/__tests__/shared.spec.ts @@ -1,5 +1,5 @@ import { QueryClient } from '@tanstack/react-query' -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it, vi } from 'vite-plus/test' import { consoleQuery } from '@/service/client' import { createUploadItemId, diff --git a/web/features/skills/detail/__tests__/shell.spec.tsx b/web/features/skills/detail/__tests__/shell.spec.tsx index 7059a04d14e..585aa57a194 100644 --- a/web/features/skills/detail/__tests__/shell.spec.tsx +++ b/web/features/skills/detail/__tests__/shell.spec.tsx @@ -1,5 +1,5 @@ import { render } from '@testing-library/react' -import { describe, expect, it } from 'vitest' +import { describe, expect, it } from 'vite-plus/test' import { DetailSkeleton } from '../shell' describe('Skill detail shell', () => { diff --git a/web/features/skills/detail/__tests__/upload-workflow.spec.ts b/web/features/skills/detail/__tests__/upload-workflow.spec.ts index 04c3401be06..91ce0e597f7 100644 --- a/web/features/skills/detail/__tests__/upload-workflow.spec.ts +++ b/web/features/skills/detail/__tests__/upload-workflow.spec.ts @@ -1,5 +1,5 @@ import type { SkillFileResponse } from '@dify/contracts/api/console/workspaces/types.gen' -import { describe, expect, it } from 'vitest' +import { describe, expect, it } from 'vite-plus/test' import { buildUploadReviewItems, createAvailableUploadPath, diff --git a/web/features/tag-management/__tests__/skill-card-tags.spec.tsx b/web/features/tag-management/__tests__/skill-card-tags.spec.tsx index c9ca8b69ede..9e5528fb654 100644 --- a/web/features/tag-management/__tests__/skill-card-tags.spec.tsx +++ b/web/features/tag-management/__tests__/skill-card-tags.spec.tsx @@ -2,7 +2,7 @@ import type { TagResponse as Tag } from '@dify/contracts/api/console/tags/types. import type { ComponentProps } from 'react' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { beforeEach, describe, expect, it, vi } from 'vitest' +import { beforeEach, describe, expect, it, vi } from 'vite-plus/test' import { SkillCardTags } from '../components/skill-card-tags' const mocks = vi.hoisted(() => ({ diff --git a/web/i18n-config/__tests__/plural-selector.spec.ts b/web/i18n-config/__tests__/plural-selector.spec.ts index 9e98128f67e..ceeed3aeaa4 100644 --- a/web/i18n-config/__tests__/plural-selector.spec.ts +++ b/web/i18n-config/__tests__/plural-selector.spec.ts @@ -1,6 +1,6 @@ import type { SelectorParam } from 'i18next' import { createInstance } from 'i18next' -import { describe, expect, it } from 'vitest' +import { describe, expect, it } from 'vite-plus/test' import agentV2 from '../../i18n/en-US/agent-v-2.json' import skill from '../../i18n/en-US/skill.json' import { getInitOptions } from '../settings' diff --git a/web/next.config.ts b/web/next.config.ts index aff1c791d67..adceb7f630f 100644 --- a/web/next.config.ts +++ b/web/next.config.ts @@ -17,11 +17,6 @@ const nextConfig: NextConfig = { bundler: 'turbopack', }), }, - experimental: { - // TODO: Remove when the `typescript` package can point to TypeScript 7. - // Next.js resolves that package, while compiler-API consumers still require TypeScript 6. - useTypeScriptCli: false, - }, productionBrowserSourceMaps: false, // enable browser source map generation during the production build typescript: { // https://nextjs.org/docs/api-reference/next.config.js/ignoring-typescript-errors From 1977355dd1c64d773d9a840b60659abecb1d6f94 Mon Sep 17 00:00:00 2001 From: Eddy ZHANG Date: Tue, 1 Sep 2026 04:24:45 +0000 Subject: [PATCH 11/33] refactor(console): dep-inject model provider payloads with @model_validate (#41539) --- .../console/workspace/model_providers.py | 53 +++----- .../console/workspace/test_model_providers.py | 118 +++++++++++++++--- 2 files changed, 119 insertions(+), 52 deletions(-) diff --git a/api/controllers/console/workspace/model_providers.py b/api/controllers/console/workspace/model_providers.py index ca0b536f85e..fe844204d27 100644 --- a/api/controllers/console/workspace/model_providers.py +++ b/api/controllers/console/workspace/model_providers.py @@ -1,7 +1,7 @@ import io from typing import Any, Literal -from flask import request, send_file +from flask import send_file from flask_restx import Resource from pydantic import BaseModel, Field, field_validator from sqlalchemy.orm import Session @@ -15,6 +15,7 @@ from controllers.console.wraps import ( RBACResourceScope, account_initialization_required, is_admin_or_owner_required, + model_validate, rbac_permission_required, setup_required, with_current_tenant_id, @@ -154,10 +155,8 @@ class ModelProviderListApi(Resource): @login_required @account_initialization_required @with_current_tenant_id - def get(self, tenant_id: str): - payload = request.args.to_dict(flat=True) - args = ParserModelList.model_validate(payload) - + @model_validate(ParserModelList) + def get(self, args: ParserModelList, tenant_id: str): model_provider_service = ModelProviderService() provider_list = model_provider_service.get_provider_list(tenant_id=tenant_id, model_type=args.model_type) @@ -212,12 +211,10 @@ class ModelProviderCredentialApi(Resource): @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_MANAGE, resource_required=False) @account_initialization_required @with_current_tenant_id - def get(self, tenant_id: str, provider: str): - # if credential_id is not provided, return current used credential - payload = request.args.to_dict(flat=True) - args = ParserCredentialId.model_validate(payload) - + @model_validate(ParserCredentialId) + def get(self, args: ParserCredentialId, tenant_id: str, provider: str): model_provider_service = ModelProviderService() + # if credential_id is not provided, return current used credential credentials = model_provider_service.get_provider_credential( tenant_id=tenant_id, provider=provider, credential_id=args.credential_id ) @@ -232,10 +229,8 @@ class ModelProviderCredentialApi(Resource): @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_CREATE, resource_required=False) @account_initialization_required @with_current_tenant_id - def post(self, current_tenant_id: str, provider: str): - payload = console_ns.payload or {} - args = ParserCredentialCreate.model_validate(payload) - + @model_validate(ParserCredentialCreate) + def post(self, args: ParserCredentialCreate, current_tenant_id: str, provider: str): model_provider_service = ModelProviderService() try: @@ -258,10 +253,8 @@ class ModelProviderCredentialApi(Resource): @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_MANAGE, resource_required=False) @account_initialization_required @with_current_tenant_id - def put(self, current_tenant_id: str, provider: str): - payload = console_ns.payload or {} - args = ParserCredentialUpdate.model_validate(payload) - + @model_validate(ParserCredentialUpdate) + def put(self, args: ParserCredentialUpdate, current_tenant_id: str, provider: str): model_provider_service = ModelProviderService() try: @@ -285,10 +278,8 @@ class ModelProviderCredentialApi(Resource): @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_MANAGE, resource_required=False) @account_initialization_required @with_current_tenant_id - def delete(self, current_tenant_id: str, provider: str): - payload = console_ns.payload or {} - args = ParserCredentialDelete.model_validate(payload) - + @model_validate(ParserCredentialDelete) + def delete(self, args: ParserCredentialDelete, current_tenant_id: str, provider: str): model_provider_service = ModelProviderService() model_provider_service.remove_provider_credential( tenant_id=current_tenant_id, provider=provider, credential_id=args.credential_id @@ -307,10 +298,8 @@ class ModelProviderCredentialSwitchApi(Resource): @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_USE, resource_required=False) @account_initialization_required @with_current_tenant_id - def post(self, current_tenant_id: str, provider: str): - payload = console_ns.payload or {} - args = ParserCredentialSwitch.model_validate(payload) - + @model_validate(ParserCredentialSwitch) + def post(self, args: ParserCredentialSwitch, current_tenant_id: str, provider: str): service = ModelProviderService() service.switch_active_provider_credential( tenant_id=current_tenant_id, @@ -332,10 +321,8 @@ class ModelProviderValidateApi(Resource): @login_required @account_initialization_required @with_current_tenant_id - def post(self, current_tenant_id: str, provider: str): - payload = console_ns.payload or {} - args = ParserCredentialValidate.model_validate(payload) - + @model_validate(ParserCredentialValidate) + def post(self, args: ParserCredentialValidate, current_tenant_id: str, provider: str): tenant_id = current_tenant_id model_provider_service = ModelProviderService() @@ -388,10 +375,8 @@ class PreferredProviderTypeUpdateApi(Resource): @rbac_permission_required(RBACResourceScope.WORKSPACE, RBACPermission.CREDENTIAL_USE, resource_required=False) @account_initialization_required @with_current_tenant_id - def post(self, tenant_id: str, provider: str): - payload = console_ns.payload or {} - args = ParserPreferredProviderType.model_validate(payload) - + @model_validate(ParserPreferredProviderType) + def post(self, args: ParserPreferredProviderType, tenant_id: str, provider: str): model_provider_service = ModelProviderService() model_provider_service.switch_preferred_provider( tenant_id=tenant_id, provider=provider, preferred_provider_type=args.preferred_provider_type diff --git a/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py b/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py index a4f14e5255d..2af0ce3db7a 100644 --- a/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py +++ b/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py @@ -1,11 +1,13 @@ +from collections.abc import Callable from inspect import unwrap +from types import SimpleNamespace from unittest.mock import patch import pytest -from flask import Flask, g +from flask import Flask, g, request from pydantic_core import ValidationError from sqlalchemy.orm import Session -from werkzeug.exceptions import Forbidden +from werkzeug.exceptions import Forbidden, UnprocessableEntity from configs import dify_config from controllers.console.workspace.model_providers import ( @@ -17,6 +19,14 @@ from controllers.console.workspace.model_providers import ( ModelProviderPaymentCheckoutUrlApi, ModelProviderSummaryListApi, ModelProviderValidateApi, + ParserCredentialCreate, + ParserCredentialDelete, + ParserCredentialId, + ParserCredentialSwitch, + ParserCredentialUpdate, + ParserCredentialValidate, + ParserModelList, + ParserPreferredProviderType, PreferredProviderTypeUpdateApi, ) from core.entities.provider_entities import CredentialConfiguration @@ -26,6 +36,7 @@ from graphon.model_runtime.entities.model_entities import ModelType from graphon.model_runtime.entities.provider_entities import ConfigurateMethod from graphon.model_runtime.errors.validate import CredentialsValidateFailedError from models import Account +from models.account import TenantAccountRole from models.provider import ProviderType from services.entities.model_provider_entities import ( CustomConfigurationResponse, @@ -116,6 +127,19 @@ def expected_provider_payload() -> dict[str, object]: } +def _payload() -> dict[str, object]: + """Mirror the source the ``@model_validate`` decorator reads for the request in scope. + + The tests build their contexts with ``test_request_context``, which defaults to GET even for + handlers mounted on POST, so fall back to the JSON body whenever the query string is empty. + """ + args: dict[str, object] = dict(request.args.to_dict(flat=True)) + if args: + return args + body = request.get_json(silent=True) + return dict(body) if isinstance(body, dict) else {} + + class TestModelProviderListApi: def test_get_success(self, app: Flask): api = ModelProviderListApi() @@ -129,7 +153,7 @@ class TestModelProviderListApi: return_value=[provider], ) as get_provider_list, ): - result = method(api, "tenant1") + result = method(api, ParserModelList.model_validate(_payload()), "tenant1") get_provider_list.assert_called_once_with(tenant_id="tenant1", model_type=ModelType.LLM) assert result == {"data": [expected_provider_payload()]} @@ -145,7 +169,7 @@ class TestModelProviderListApi: return_value=[], ) as get_provider_list, ): - result = method(api, "tenant1") + result = method(api, ParserModelList.model_validate(_payload()), "tenant1") get_provider_list.assert_called_once_with(tenant_id="tenant1", model_type=None) assert result == {"data": []} @@ -297,7 +321,7 @@ class TestModelProviderCredentialApi: }, ) as get_provider_credential, ): - result = method(api, "tenant1", provider="openai") + result = method(api, ParserCredentialId.model_validate(_payload()), "tenant1", provider="openai") get_provider_credential.assert_called_once_with( tenant_id="tenant1", provider="openai", credential_id=VALID_UUID @@ -321,7 +345,7 @@ class TestModelProviderCredentialApi: return_value=None, ) as get_provider_credential, ): - result = method(api, "tenant1", provider="openai") + result = method(api, ParserCredentialId.model_validate(_payload()), "tenant1", provider="openai") get_provider_credential.assert_called_once_with(tenant_id="tenant1", provider="openai", credential_id=None) assert result == {"credentials": None} @@ -332,7 +356,7 @@ class TestModelProviderCredentialApi: with app.test_request_context(f"/?credential_id={INVALID_UUID}"): with pytest.raises(ValidationError): - method(api, "tenant1", provider="openai") + method(api, ParserCredentialId.model_validate(_payload()), "tenant1", provider="openai") def test_post_create_success(self, app: Flask): api = ModelProviderCredentialApi() @@ -347,7 +371,9 @@ class TestModelProviderCredentialApi: return_value=None, ) as create_provider_credential, ): - result, status = method(api, "tenant1", provider="openai") + result, status = method( + api, ParserCredentialCreate.model_validate(_payload()), "tenant1", provider="openai" + ) create_provider_credential.assert_called_once_with( tenant_id="tenant1", @@ -372,7 +398,7 @@ class TestModelProviderCredentialApi: ), ): with pytest.raises(ValueError): - method(api, "tenant1", provider="openai") + method(api, ParserCredentialCreate.model_validate(_payload()), "tenant1", provider="openai") def test_put_update_success(self, app: Flask): api = ModelProviderCredentialApi() @@ -387,7 +413,7 @@ class TestModelProviderCredentialApi: return_value=None, ) as update_provider_credential, ): - result = method(api, "tenant1", provider="openai") + result = method(api, ParserCredentialUpdate.model_validate(_payload()), "tenant1", provider="openai") update_provider_credential.assert_called_once_with( tenant_id="tenant1", @@ -406,7 +432,7 @@ class TestModelProviderCredentialApi: with app.test_request_context("/", json=payload): with pytest.raises(ValidationError): - method(api, "tenant1", provider="openai") + method(api, ParserCredentialUpdate.model_validate(_payload()), "tenant1", provider="openai") def test_delete_success(self, app: Flask): api = ModelProviderCredentialApi() @@ -421,7 +447,9 @@ class TestModelProviderCredentialApi: return_value=None, ) as remove_provider_credential, ): - result, status = method(api, "tenant1", provider="openai") + result, status = method( + api, ParserCredentialDelete.model_validate(_payload()), "tenant1", provider="openai" + ) remove_provider_credential.assert_called_once_with( tenant_id="tenant1", provider="openai", credential_id=VALID_UUID @@ -444,7 +472,7 @@ class TestModelProviderCredentialSwitchApi: return_value=None, ) as switch_active_provider_credential, ): - result = method(api, "tenant1", provider="openai") + result = method(api, ParserCredentialSwitch.model_validate(_payload()), "tenant1", provider="openai") switch_active_provider_credential.assert_called_once_with( tenant_id="tenant1", @@ -461,7 +489,7 @@ class TestModelProviderCredentialSwitchApi: with app.test_request_context("/", json=payload): with pytest.raises(ValidationError): - method(api, "tenant1", provider="openai") + method(api, ParserCredentialSwitch.model_validate(_payload()), "tenant1", provider="openai") class TestModelProviderValidateApi: @@ -478,7 +506,7 @@ class TestModelProviderValidateApi: return_value=None, ) as validate_provider_credentials, ): - result = method(api, "tenant1", provider="openai") + result = method(api, ParserCredentialValidate.model_validate(_payload()), "tenant1", provider="openai") validate_provider_credentials.assert_called_once_with( tenant_id="tenant1", provider="openai", credentials={"a": "b"} @@ -498,7 +526,7 @@ class TestModelProviderValidateApi: side_effect=CredentialsValidateFailedError("bad"), ), ): - result = method(api, "tenant1", provider="openai") + result = method(api, ParserCredentialValidate.model_validate(_payload()), "tenant1", provider="openai") assert result == {"result": "error", "error": "bad"} @@ -549,7 +577,7 @@ class TestPreferredProviderTypeUpdateApi: return_value=None, ) as switch_preferred_provider, ): - result = method(api, "tenant1", provider="openai") + result = method(api, ParserPreferredProviderType.model_validate(_payload()), "tenant1", provider="openai") switch_preferred_provider.assert_called_once_with( tenant_id="tenant1", provider="openai", preferred_provider_type="custom" @@ -564,7 +592,7 @@ class TestPreferredProviderTypeUpdateApi: with app.test_request_context("/", json=payload): with pytest.raises(ValidationError): - method(api, "tenant1", provider="openai") + method(api, ParserPreferredProviderType.model_validate(_payload()), "tenant1", provider="openai") class TestModelProviderPaymentCheckoutUrlApi: @@ -618,3 +646,57 @@ class TestModelProviderPaymentCheckoutUrlApi: api.get(provider="anthropic") get_model_provider_payment_link.assert_not_called() + + +class TestModelValidateDecorator: + """The tests above unwrap the view, so this is what covers the decorators themselves.""" + + EMPTY_BODY: dict[str, object] = {} + EMPTY_KWARGS: dict[str, object] = {} + + @pytest.mark.parametrize( + ("verb", "url", "body", "call"), + [ + ("GET", "/?model_type=not-a-model-type", None, lambda: ModelProviderListApi().get()), + ( + "GET", + "/?credential_id=not-a-uuid", + None, + lambda: ModelProviderCredentialApi().get(provider="openai"), + ), + ("POST", "/", EMPTY_BODY, lambda: ModelProviderCredentialApi().post(provider="openai")), + ("PUT", "/", EMPTY_BODY, lambda: ModelProviderCredentialApi().put(provider="openai")), + ("DELETE", "/", EMPTY_BODY, lambda: ModelProviderCredentialApi().delete(provider="openai")), + ("POST", "/", EMPTY_BODY, lambda: ModelProviderCredentialSwitchApi().post(provider="openai")), + ("POST", "/", EMPTY_BODY, lambda: ModelProviderValidateApi().post(provider="openai")), + ("POST", "/", EMPTY_BODY, lambda: PreferredProviderTypeUpdateApi().post(provider="openai")), + ], + ) + def test_invalid_input_is_rejected_before_the_handler_runs( + self, + app: Flask, + verb: str, + url: str, + body: dict[str, object] | None, + call: Callable[[], object], + ) -> None: + account = make_account() + account.role = TenantAccountRole.OWNER + + with ( + app.test_request_context(url, method=verb, json=body), + config_overrides_context(LOGIN_DISABLED=True, RBAC_ENABLED=False), + patch("controllers.console.wraps._is_setup_completed", return_value=True), + patch( + "controllers.console.wraps.current_account_with_tenant", + return_value=(account, "tenant1"), + ), + patch("libs.login.current_user", SimpleNamespace(_get_current_object=lambda: account)), + patch("controllers.console.workspace.model_providers.ModelProviderService") as service, + ): + g._login_user = account + with pytest.raises(UnprocessableEntity) as exc_info: + call() + + assert exc_info.value.code == 422 + service.assert_not_called() From 6411d6235cb5c3565da7d4646323daffeb240741 Mon Sep 17 00:00:00 2001 From: Eddy ZHANG Date: Tue, 1 Sep 2026 05:55:50 +0000 Subject: [PATCH 12/33] refactor(service_api): dep-inject query params with @model_validate (#41562) --- api/controllers/service_api/app/annotation.py | 5 ++-- .../service_api/app/conversation.py | 9 +++--- .../service_api/app/file_preview.py | 9 +++--- api/controllers/service_api/app/message.py | 9 +++--- .../service_api/app/test_annotation.py | 13 +++++---- .../service_api/app/test_conversation.py | 17 +++++++++-- .../service_api/app/test_message.py | 29 +++++++++++++++---- 7 files changed, 61 insertions(+), 30 deletions(-) diff --git a/api/controllers/service_api/app/annotation.py b/api/controllers/service_api/app/annotation.py index 832190e08b2..0b54d133661 100644 --- a/api/controllers/service_api/app/annotation.py +++ b/api/controllers/service_api/app/annotation.py @@ -1,7 +1,6 @@ from typing import Literal from uuid import UUID -from flask import request from flask_restx import Resource from flask_restx.api import HTTPStatus from pydantic import BaseModel, Field, TypeAdapter @@ -207,9 +206,9 @@ class AnnotationListApi(Resource): ) @validate_app_token @with_session(write=False) - def get(self, session: Session, app_model: App): + @model_validate(AnnotationListQuery) + def get(self, query: AnnotationListQuery, session: Session, app_model: App): """List annotations for the application.""" - query = AnnotationListQuery.model_validate(request.args.to_dict(flat=True)) annotation_list, total = AppAnnotationService.get_annotation_list_by_app_id( app_model.id, query.page, query.limit, query.keyword, session diff --git a/api/controllers/service_api/app/conversation.py b/api/controllers/service_api/app/conversation.py index 163a50e959b..124da072be9 100644 --- a/api/controllers/service_api/app/conversation.py +++ b/api/controllers/service_api/app/conversation.py @@ -2,7 +2,6 @@ from datetime import datetime from typing import Annotated, Any, Literal from uuid import UUID -from flask import request from flask_restx import Resource from pydantic import BaseModel, Field, TypeAdapter, WithJsonSchema, field_validator from sqlalchemy.orm import sessionmaker @@ -185,7 +184,8 @@ class ConversationApi(Resource): service_api_ns.models[ConversationInfiniteScrollPagination.__name__], ) @validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY)) - def get(self, app_model: App, end_user: EndUser): + @model_validate(ConversationListQuery) + def get(self, query_args: ConversationListQuery, app_model: App, end_user: EndUser): """List all conversations for the current user. Supports pagination using last_id and limit parameters. @@ -194,7 +194,6 @@ class ConversationApi(Resource): if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT}: raise NotChatAppError() - query_args = ConversationListQuery.model_validate(request.args.to_dict()) last_id = query_args.last_id or None try: @@ -343,7 +342,8 @@ class ConversationVariablesApi(Resource): service_api_ns.models[ConversationVariableInfiniteScrollPaginationResponse.__name__], ) @validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY)) - def get(self, app_model: App, end_user: EndUser, conversation_id: UUID): + @model_validate(ConversationVariablesQuery) + def get(self, query_args: ConversationVariablesQuery, app_model: App, end_user: EndUser, conversation_id: UUID): """List all variables for a conversation. Conversational variables are only available for chat applications. @@ -355,7 +355,6 @@ class ConversationVariablesApi(Resource): conversation_id_str = str(conversation_id) - query_args = ConversationVariablesQuery.model_validate(request.args.to_dict()) last_id = query_args.last_id or None try: diff --git a/api/controllers/service_api/app/file_preview.py b/api/controllers/service_api/app/file_preview.py index b315e190fa3..ad96ac96ce1 100644 --- a/api/controllers/service_api/app/file_preview.py +++ b/api/controllers/service_api/app/file_preview.py @@ -2,7 +2,7 @@ import logging from urllib.parse import quote from uuid import UUID -from flask import Response, request +from flask import Response from flask_restx import Resource from pydantic import BaseModel, Field from sqlalchemy import select @@ -10,6 +10,7 @@ from sqlalchemy import select from controllers.common.fields import BinaryFileResponse from controllers.common.file_response import enforce_download_for_html from controllers.common.schema import query_params_from_model, register_response_schema_model, register_schema_model +from controllers.console.wraps import model_validate from controllers.service_api import service_api_ns from controllers.service_api.app.error import ( FileAccessDeniedError, @@ -86,7 +87,8 @@ class FilePreviewApi(Resource): ) @service_api_ns.response(200, "File retrieved successfully") @validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY)) - def get(self, app_model: App, end_user: EndUser, file_id: UUID): + @model_validate(FilePreviewQuery) + def get(self, args: FilePreviewQuery, app_model: App, end_user: EndUser, file_id: UUID): """ Preview/Download a file that was uploaded via Service API. @@ -95,9 +97,6 @@ class FilePreviewApi(Resource): """ file_id_str = str(file_id) - # Parse query parameters - args = FilePreviewQuery.model_validate(request.args.to_dict()) - # Validate file ownership and get file objects _, upload_file = self._validate_file_ownership(file_id_str, app_model.id) diff --git a/api/controllers/service_api/app/message.py b/api/controllers/service_api/app/message.py index d3443371313..63bf87709d9 100644 --- a/api/controllers/service_api/app/message.py +++ b/api/controllers/service_api/app/message.py @@ -2,7 +2,6 @@ import logging from typing import Annotated from uuid import UUID -from flask import request from flask_restx import Resource from pydantic import BaseModel, Field, TypeAdapter, WithJsonSchema from werkzeug.exceptions import BadRequest, InternalServerError, NotFound @@ -102,7 +101,8 @@ class MessageListApi(Resource): service_api_ns.models[MessageInfiniteScrollPagination.__name__], ) @validate_app_token(fetch_user_arg=FetchUserArg(fetch_from=WhereisUserArg.QUERY)) - def get(self, app_model: App, end_user: EndUser): + @model_validate(MessageListQuery) + def get(self, query_args: MessageListQuery, app_model: App, end_user: EndUser): """List messages in a conversation. Retrieves messages with pagination support using first_id. @@ -111,7 +111,6 @@ class MessageListApi(Resource): if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT, AppMode.AGENT}: raise NotChatAppError() - query_args = MessageListQuery.model_validate(request.args.to_dict()) conversation_id = query_args.conversation_id first_id = query_args.first_id or None @@ -212,12 +211,12 @@ class AppGetFeedbacksApi(Resource): service_api_ns.models[AppFeedbackListResponse.__name__], ) @validate_app_token - def get(self, app_model: App): + @model_validate(FeedbackListQuery) + def get(self, query_args: FeedbackListQuery, app_model: App): """Get all feedbacks for the application. Returns paginated list of all feedback submitted for messages in this app. """ - query_args = FeedbackListQuery.model_validate(request.args.to_dict()) feedbacks = MessageService.get_all_messages_feedbacks( app_model, page=query_args.page, limit=query_args.limit, session=db.session() ) diff --git a/api/tests/unit_tests/controllers/service_api/app/test_annotation.py b/api/tests/unit_tests/controllers/service_api/app/test_annotation.py index 4bd305008bc..b27c478ba6d 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_annotation.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_annotation.py @@ -229,7 +229,8 @@ class TestAnnotationListApi: handler = unwrap(api.get) app_model = SimpleNamespace(id="app") with app.test_request_context("/apps/annotations", method="GET"): - response = handler(api, MagicMock(), app_model=app_model) + query = AnnotationListQuery.model_validate(request.args.to_dict(flat=True)) + response = handler(api, query, MagicMock(), app_model=app_model) assert response["page"] == 1 assert response["limit"] == 20 session = get_mock.call_args.args[-1] @@ -244,7 +245,8 @@ class TestAnnotationListApi: handler = unwrap(api.get) app_model = SimpleNamespace(id="app") with app.test_request_context("/apps/annotations?page=2&limit=5&keyword=refund", method="GET"): - response = handler(api, MagicMock(), app_model=app_model) + query = AnnotationListQuery.model_validate(request.args.to_dict(flat=True)) + response = handler(api, query, MagicMock(), app_model=app_model) assert response["total"] == 1 assert response["page"] == 2 assert response["limit"] == 5 @@ -259,11 +261,12 @@ class TestAnnotationListApi: get_mock = Mock(return_value=([], 0)) monkeypatch.setattr(AppAnnotationService, "get_annotation_list_by_app_id", get_mock) api = AnnotationListApi() - handler = unwrap(api.get) - app_model = SimpleNamespace(id="app") + # The parse moved into @model_validate, so build the query the way the decorator does and + # assert it is what rejects the input, before the view is ever reached. with app.test_request_context(f"/apps/annotations?{query_string}", method="GET"): with pytest.raises(ValidationError): - handler(api, MagicMock(), app_model=app_model) + AnnotationListQuery.model_validate(request.args.to_dict(flat=True)) + assert unwrap(api.get) is not api.get get_mock.assert_not_called() def test_create(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: diff --git a/api/tests/unit_tests/controllers/service_api/app/test_conversation.py b/api/tests/unit_tests/controllers/service_api/app/test_conversation.py index e163ebf4be5..17feffd4400 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_conversation.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_conversation.py @@ -539,7 +539,12 @@ class TestConversationApiController: with app.test_request_context("/conversations", method="GET"): with pytest.raises(NotChatAppError): - handler(api, app_model=app_model, end_user=end_user) + handler( + api, + ConversationListQuery.model_validate(request.args.to_dict(flat=True)), + app_model=app_model, + end_user=end_user, + ) def test_list_last_not_found( self, @@ -566,7 +571,12 @@ class TestConversationApiController: method="GET", ): with pytest.raises(NotFound): - handler(api, app_model=app_model, end_user=end_user) + handler( + api, + ConversationListQuery.model_validate(request.args.to_dict(flat=True)), + app_model=app_model, + end_user=end_user, + ) class TestConversationDetailApiController: @@ -647,6 +657,7 @@ class TestConversationVariablesApiController: with pytest.raises(NotChatAppError): handler( api, + ConversationVariablesQuery.model_validate(request.args.to_dict(flat=True)), app_model=app_model, end_user=end_user, conversation_id="00000000-0000-0000-0000-000000000001", @@ -671,6 +682,7 @@ class TestConversationVariablesApiController: with pytest.raises(NotFound): handler( api, + ConversationVariablesQuery.model_validate(request.args.to_dict(flat=True)), app_model=app_model, end_user=end_user, conversation_id="00000000-0000-0000-0000-000000000001", @@ -708,6 +720,7 @@ class TestConversationVariablesApiController: ): result = handler( api, + ConversationVariablesQuery.model_validate(request.args.to_dict(flat=True)), app_model=app_model, end_user=end_user, conversation_id="00000000-0000-0000-0000-000000000001", diff --git a/api/tests/unit_tests/controllers/service_api/app/test_message.py b/api/tests/unit_tests/controllers/service_api/app/test_message.py index a1e1f699bbe..95c6a49b186 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_message.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_message.py @@ -415,9 +415,16 @@ class TestMessageListApi: app_model = SimpleNamespace(mode=AppMode.COMPLETION.value) end_user = SimpleNamespace() - with app.test_request_context("/messages?conversation_id=cid", method="GET"): + # @model_validate parses ahead of the app-mode guard, so the id has to be well-formed to + # reach the branch this test is about. + with app.test_request_context("/messages?conversation_id=00000000-0000-0000-0000-000000000001", method="GET"): with pytest.raises(NotChatAppError): - handler(api, app_model=app_model, end_user=end_user) + handler( + api, + MessageListQuery.model_validate(request.args.to_dict(flat=True)), + app_model=app_model, + end_user=end_user, + ) def test_conversation_not_found(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr( @@ -436,7 +443,12 @@ class TestMessageListApi: method="GET", ): with pytest.raises(NotFound): - handler(api, app_model=app_model, end_user=end_user) + handler( + api, + MessageListQuery.model_validate(request.args.to_dict(flat=True)), + app_model=app_model, + end_user=end_user, + ) def test_first_message_not_found(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr( @@ -455,7 +467,12 @@ class TestMessageListApi: method="GET", ): with pytest.raises(NotFound): - handler(api, app_model=app_model, end_user=end_user) + handler( + api, + MessageListQuery.model_validate(request.args.to_dict(flat=True)), + app_model=app_model, + end_user=end_user, + ) class TestMessageFeedbackApi: @@ -503,7 +520,9 @@ class TestAppGetFeedbacksApi: app_model = SimpleNamespace() with app.test_request_context("/app/feedbacks?page=1&limit=20", method="GET"): - response = handler(api, app_model=app_model) + response = handler( + api, FeedbackListQuery.model_validate(request.args.to_dict(flat=True)), app_model=app_model + ) assert response == {"data": [feedback]} From fdca3e2a1a1a03b0ae0c2dd95238de916f16839d Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 1 Sep 2026 06:18:53 +0000 Subject: [PATCH 13/33] chore: enforce titles on CSS-truncated elements (#41438) --- AGENTS.md | 6 + lint.config.ts | 13 + .../__tests__/access-point-card.spec.tsx | 5 + .../access-point/shared/access-point-card.tsx | 6 +- .../index.tsx | 5 +- .../remove-annotation-confirm-modal/index.tsx | 5 +- .../workflow-tool-action/index.tsx | 5 +- .../app/deploy/shared/deployment-status.tsx | 4 +- .../app/overview/__tests__/app-chart.spec.tsx | 2 +- web/app/components/app/overview/app-chart.tsx | 5 +- .../external-api/external-api-modal/index.tsx | 5 +- .../rag-pipeline/components/conversion.tsx | 5 +- .../components/remove-effect-var-confirm.tsx | 5 +- .../__tests__/large-data-alert.spec.tsx | 10 +- .../variable-inspect/large-data-alert.tsx | 4 +- web/docs/truncated-text-disclosure.md | 73 ++ .../__tests__/access-surface-card.spec.tsx | 1 + .../access/components/access-surface-card.tsx | 6 +- .../advanced/content-moderation.tsx | 4 +- .../agent-v2/roster/__tests__/page.spec.tsx | 5 +- web/features/agent-v2/roster/page.tsx | 1 + web/plugins/eslint/index.js | 2 + .../rules/fixtures/truncation.module.css | 23 + .../rules/require-title-for-truncated-text.js | 743 ++++++++++++++++++ .../require-title-for-truncated-text.test.js | 316 ++++++++ 25 files changed, 1242 insertions(+), 17 deletions(-) create mode 100644 web/docs/truncated-text-disclosure.md create mode 100644 web/plugins/eslint/rules/fixtures/truncation.module.css create mode 100644 web/plugins/eslint/rules/require-title-for-truncated-text.js create mode 100644 web/plugins/eslint/rules/require-title-for-truncated-text.test.js diff --git a/AGENTS.md b/AGENTS.md index 6f5673b316a..d7b7320aa2d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,3 +7,9 @@ Dify is an open-source platform for building LLM applications, agentic workflows - Run backend commands through `uv run --project api `. - Backend integration tests are CI-only and are not expected to run locally. - Keep `docker/.env.example` limited to variables required for a default Docker Compose deployment to start. Put optional and provider-specific settings in the matching `docker/envs/*.env.example` file; `docker/.env` overrides those service-specific env files. + +## Frontend Workflow + +- For truncated text disclosure and native `title` decisions, follow [Truncated Text Disclosure]. + +[Truncated Text Disclosure]: web/docs/truncated-text-disclosure.md diff --git a/lint.config.ts b/lint.config.ts index b7689553430..2528c3e7c38 100644 --- a/lint.config.ts +++ b/lint.config.ts @@ -1117,6 +1117,19 @@ export const lintConfig = { 'eslint-react/use-memo': 'error', }, }, + { + files: ['web/**/*.{jsx,tsx}'], + excludeFiles: [ + 'web/**/__tests__/**', + 'web/**/*.spec.{jsx,tsx}', + 'web/**/*.test.{jsx,tsx}', + 'web/**/*.stories.{jsx,tsx}', + 'web/**/*.story.{jsx,tsx}', + ], + rules: { + 'dify/require-title-for-truncated-text': 'warn', + }, + }, { files: [ 'web/**/__tests__/**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}', diff --git a/web/app/components/app/access-point/__tests__/access-point-card.spec.tsx b/web/app/components/app/access-point/__tests__/access-point-card.spec.tsx index 5f740babef4..c800deded33 100644 --- a/web/app/components/app/access-point/__tests__/access-point-card.spec.tsx +++ b/web/app/components/app/access-point/__tests__/access-point-card.spec.tsx @@ -21,6 +21,11 @@ describe('AccessPointCard', () => { 'data-highlighted', 'true', ) + expect(screen.getByRole('heading', { name: 'Web App' })).toHaveAttribute('title', 'Web App') + expect(screen.getByText('Web application access')).toHaveAttribute( + 'title', + 'Web application access', + ) }) it.each<[AccessPointStatus, string, boolean]>([ diff --git a/web/app/components/app/access-point/shared/access-point-card.tsx b/web/app/components/app/access-point/shared/access-point-card.tsx index 664b21a0500..3e4430dea85 100644 --- a/web/app/components/app/access-point/shared/access-point-card.tsx +++ b/web/app/components/app/access-point/shared/access-point-card.tsx @@ -72,10 +72,12 @@ export function AccessPointCard({ icon )} -

+

{title}

- {description} + + {description} +
{showStatus && ( <> diff --git a/web/app/components/app/annotation/clear-all-annotations-confirm-modal/index.tsx b/web/app/components/app/annotation/clear-all-annotations-confirm-modal/index.tsx index 5cbd69dfc6a..48943656d07 100644 --- a/web/app/components/app/annotation/clear-all-annotations-confirm-modal/index.tsx +++ b/web/app/components/app/annotation/clear-all-annotations-confirm-modal/index.tsx @@ -26,7 +26,10 @@ const ClearAllAnnotationsConfirmModal: FC = ({ isShow, onHide, onConfirm !open && onHide()}>
- + {title}
diff --git a/web/app/components/app/annotation/remove-annotation-confirm-modal/index.tsx b/web/app/components/app/annotation/remove-annotation-confirm-modal/index.tsx index 55e347dfdfa..dbaa091659f 100644 --- a/web/app/components/app/annotation/remove-annotation-confirm-modal/index.tsx +++ b/web/app/components/app/annotation/remove-annotation-confirm-modal/index.tsx @@ -25,7 +25,10 @@ const RemoveAnnotationConfirmModal: FC = ({ isShow, onHide, onRemove }) = !open && onHide()}>
- + {title}
diff --git a/web/app/components/app/app-publisher/workflow-tool-action/index.tsx b/web/app/components/app/app-publisher/workflow-tool-action/index.tsx index aeed4d0bf54..bf9cc3fe444 100644 --- a/web/app/components/app/app-publisher/workflow-tool-action/index.tsx +++ b/web/app/components/app/app-publisher/workflow-tool-action/index.tsx @@ -74,7 +74,10 @@ const WorkflowToolAction = ({
- + {workflowToolLabel} diff --git a/web/app/components/app/deploy/shared/deployment-status.tsx b/web/app/components/app/deploy/shared/deployment-status.tsx index de7cac3dc53..4f4aabd2076 100644 --- a/web/app/components/app/deploy/shared/deployment-status.tsx +++ b/web/app/components/app/deploy/shared/deployment-status.tsx @@ -68,7 +68,9 @@ export function DeploymentStatus({ status }: { status?: DeploymentStatusValue }) ) : ( )} - {label} + + {label} + ) } diff --git a/web/app/components/app/overview/__tests__/app-chart.spec.tsx b/web/app/components/app/overview/__tests__/app-chart.spec.tsx index aa0e503ff09..07b0b666996 100644 --- a/web/app/components/app/overview/__tests__/app-chart.spec.tsx +++ b/web/app/components/app/overview/__tests__/app-chart.spec.tsx @@ -40,7 +40,7 @@ describe('app-chart', () => { />, ) - expect(screen.getByText('Cost title'))!.toBeInTheDocument() + expect(screen.getByText('Cost title')).toHaveAttribute('title', 'Cost title') expect(screen.getByText('300'))!.toBeInTheDocument() expect(screen.queryByText('Last 7 days'))!.not.toBeInTheDocument() expect(screen.getByText(/\$3\.7500/))!.toBeInTheDocument() diff --git a/web/app/components/app/overview/app-chart.tsx b/web/app/components/app/overview/app-chart.tsx index 7596c7140ec..72f881a71c3 100644 --- a/web/app/components/app/overview/app-chart.tsx +++ b/web/app/components/app/overview/app-chart.tsx @@ -96,7 +96,10 @@ const Chart: React.FC = ({ >
-
+
{title}
{explanation && ( diff --git a/web/app/components/datasets/external-api/external-api-modal/index.tsx b/web/app/components/datasets/external-api/external-api-modal/index.tsx index 21567090075..2b1e39a7075 100644 --- a/web/app/components/datasets/external-api/external-api-modal/index.tsx +++ b/web/app/components/datasets/external-api/external-api-modal/index.tsx @@ -246,7 +246,10 @@ const AddExternalAPIModal: FC = ({ >
- + Warning diff --git a/web/app/components/rag-pipeline/components/conversion.tsx b/web/app/components/rag-pipeline/components/conversion.tsx index 0e873ff1990..2c2b6d6077e 100644 --- a/web/app/components/rag-pipeline/components/conversion.tsx +++ b/web/app/components/rag-pipeline/components/conversion.tsx @@ -119,7 +119,10 @@ const Conversion = () => { >
- + {confirmTitle} diff --git a/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx b/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx index 1da22c8f4ec..4560813f628 100644 --- a/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx +++ b/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx @@ -28,7 +28,10 @@ const RemoveVarConfirm: FC = ({ isShow, onConfirm, onCancel }) => { !open && onCancel()}>
- + {title} diff --git a/web/app/components/workflow/variable-inspect/__tests__/large-data-alert.spec.tsx b/web/app/components/workflow/variable-inspect/__tests__/large-data-alert.spec.tsx index d2a28ffe225..45938af5124 100644 --- a/web/app/components/workflow/variable-inspect/__tests__/large-data-alert.spec.tsx +++ b/web/app/components/workflow/variable-inspect/__tests__/large-data-alert.spec.tsx @@ -7,7 +7,10 @@ describe('LargeDataAlert', () => { , ) - expect(screen.getByText('workflow.debug.variableInspect.largeData')).toBeInTheDocument() + expect(screen.getByText('workflow.debug.variableInspect.largeData')).toHaveAttribute( + 'title', + 'workflow.debug.variableInspect.largeData', + ) expect(screen.getByText('workflow.debug.variableInspect.export')).toBeInTheDocument() expect(container.firstChild).toHaveClass('extra-alert') }) @@ -15,7 +18,10 @@ describe('LargeDataAlert', () => { it('should render the no-export message and omit the export action when the URL is missing', () => { render() - expect(screen.getByText('workflow.debug.variableInspect.largeDataNoExport')).toBeInTheDocument() + expect(screen.getByText('workflow.debug.variableInspect.largeDataNoExport')).toHaveAttribute( + 'title', + 'workflow.debug.variableInspect.largeDataNoExport', + ) expect(screen.queryByText('workflow.debug.variableInspect.export')).not.toBeInTheDocument() }) }) diff --git a/web/app/components/workflow/variable-inspect/large-data-alert.tsx b/web/app/components/workflow/variable-inspect/large-data-alert.tsx index 540b45665bc..134aadc9366 100644 --- a/web/app/components/workflow/variable-inspect/large-data-alert.tsx +++ b/web/app/components/workflow/variable-inspect/large-data-alert.tsx @@ -25,7 +25,9 @@ const LargeDataAlert: FC = ({ textHasNoExport, downloadUrl, className }) >
-
{text}
+
+ {text} +
{downloadUrl && (
diff --git a/web/docs/truncated-text-disclosure.md b/web/docs/truncated-text-disclosure.md new file mode 100644 index 00000000000..cb159e2cfbc --- /dev/null +++ b/web/docs/truncated-text-disclosure.md @@ -0,0 +1,73 @@ +# Truncated Text Disclosure + +Treat native `title` as an opt-in, supplemental product behavior. Do not treat it as a mechanical companion to `truncate`, `text-overflow`, or `line-clamp-*`. + +Missing `title` is not by itself an accessibility defect. Native title tooltips must not: + +- be the only way to access essential information; +- replace a visible label, accessible name, or accessible description; +- compete with an existing hover, focus, pointer, expand, copy, or detail interaction. + +Before making a change: + +1. Trace the value to the final rendered DOM element. +2. Identify the existing owner of full-content disclosure. +3. Classify the candidate as `AUTO`, `COVERED`, `SKIP`, or `REVIEW`. +4. Modify only `AUTO` candidates. Report the others without changing code. + +## AUTO + +Automatically add `title` only when every condition below is satisfied: + +- The target is a native, non-editable, pointer-reachable text container whose own hit area receives the pointer, or a documented component that forwards `title` unchanged to that final DOM element. +- The final element intentionally implements single-line truncation. A truncation-related class alone is not sufficient evidence. +- The full content is a bounded, non-sensitive, single-line plain string. +- “Bounded” means fixed text, an enum, or a value with an explicit owner-level maximum length. +- The exact already-evaluated display value can be reused without repeating a function call, getter, conversion, mutation, async operation, or other potentially effectful expression. +- The final rendered element does not already receive an equivalent title through its props, wrapper, child component, or covering interaction target. +- No Tooltip, PreviewCard, Popover, expandable content, “show more” action, detail view, copy/reveal action, or other full-content owner exists. +- The native tooltip will not compete with another hover, focus, pointer, or keyboard interaction. + +## COVERED + +Classify as `COVERED` and make no change when another component or interaction already owns full-content disclosure, including: + +- Tooltip, PreviewCard, or Popover; +- expandable or “show more” content; +- a detail view opened from the current surface; +- a copy or reveal action; +- a parent or covering interaction target that already provides the equivalent title. + +## SKIP + +Classify as `SKIP` and make no change when any condition below applies: + +- The value contains or may contain a secret, token, API key, password, credential, or other sensitive data. +- The value is unbounded user content, multiline content, a comment, prompt, generated output, log body, or another potentially large string. +- The visible content is JSX, `ReactNode`, structured content, or would require a stringification helper. +- Producing the title would repeat or relocate evaluation of a function call, getter, conversion, mutation, async operation, or other potentially effectful expression. +- The element is an input, textarea, editable surface, `pointer-events-none`, covered by another element, or not the actual pointer target. +- An existing `title`, including `title=""`, would need to be overwritten or removed. +- The content uses `line-clamp-*` and already has an expand or detail interaction. + +## REVIEW + +Classify as `REVIEW`, make no code change, and report the reason when: + +- the correct disclosure owner is ambiguous or product-specific; +- the value bound or sensitivity cannot be proven; +- the final DOM element or pointer owner cannot be traced; +- the component is interactive; +- the content uses `line-clamp-*` without an existing full-content path; +- actual truncation or the intended product behavior cannot be established from the owner contract. + +Before adding `title` to a custom component, inspect its implementation and trace the prop to the final DOM element. A `value`, `label`, or similar prop may already provide the native title; do not add a duplicate at the call site. + +Do not introduce: + +- a repository-wide missing-title lint error; +- a mass autofix or migration; +- a `ReactNode`-to-string helper; +- suppressions for candidates outside the allowlist. + +Test the feature-owned disclosure behavior through its public interface. Assert native `title` only when it is an explicitly accepted product contract. Do not use `getByTitle` or `toHaveAttribute('title', ...)` merely to prove a migration, and do not use title-based selectors to test unrelated interactions. diff --git a/web/features/agent-v2/agent-detail/access/components/__tests__/access-surface-card.spec.tsx b/web/features/agent-v2/agent-detail/access/components/__tests__/access-surface-card.spec.tsx index c6512469795..3e909668afd 100644 --- a/web/features/agent-v2/agent-detail/access/components/__tests__/access-surface-card.spec.tsx +++ b/web/features/agent-v2/agent-detail/access/components/__tests__/access-surface-card.spec.tsx @@ -42,6 +42,7 @@ describe('AccessSurfaceCard', () => { ) expect(screen.getByRole('article', { name: 'Web app' })).toBeInTheDocument() + expect(screen.getByRole('heading', { name: 'Web app' })).toHaveAttribute('title', 'Web app') }) it('should copy the endpoint and render copied state from the clipboard hook', async () => { diff --git a/web/features/agent-v2/agent-detail/access/components/access-surface-card.tsx b/web/features/agent-v2/agent-detail/access/components/access-surface-card.tsx index 55008713526..5f266ea0156 100644 --- a/web/features/agent-v2/agent-detail/access/components/access-surface-card.tsx +++ b/web/features/agent-v2/agent-detail/access/components/access-surface-card.tsx @@ -90,7 +90,11 @@ export function AccessSurfaceCard({ > -

+

{title}

{badge} diff --git a/web/features/agent-v2/agent-detail/configure/components/orchestrate/advanced/content-moderation.tsx b/web/features/agent-v2/agent-detail/configure/components/orchestrate/advanced/content-moderation.tsx index 44401bc3f6c..bd53a1f7ad7 100644 --- a/web/features/agent-v2/agent-detail/configure/components/orchestrate/advanced/content-moderation.tsx +++ b/web/features/agent-v2/agent-detail/configure/components/orchestrate/advanced/content-moderation.tsx @@ -166,7 +166,9 @@ function AgentContentModerationSettingsContent() {
{t(($) => $['feature.moderation.contentEnableLabel'], { ns: 'appDebug' })}
-
{enabledContent}
+
+ {enabledContent} +
) : ( diff --git a/web/features/agent-v2/roster/__tests__/page.spec.tsx b/web/features/agent-v2/roster/__tests__/page.spec.tsx index bae86e97646..e1ac7fa07eb 100644 --- a/web/features/agent-v2/roster/__tests__/page.spec.tsx +++ b/web/features/agent-v2/roster/__tests__/page.spec.tsx @@ -129,7 +129,10 @@ describe('RosterPage', () => { it('uses the localized roster title for the page heading', () => { render() - expect(screen.getByRole('heading', { name: 'agentV2.roster.title' })).toBeInTheDocument() + expect(screen.getByRole('heading', { name: 'agentV2.roster.title' })).toHaveAttribute( + 'title', + 'agentV2.roster.title', + ) expect(screen.getByRole('region', { name: 'agentV2.roster.title' })).toBeInTheDocument() }) diff --git a/web/features/agent-v2/roster/page.tsx b/web/features/agent-v2/roster/page.tsx index c36f3c3fd11..c7f13199c7c 100644 --- a/web/features/agent-v2/roster/page.tsx +++ b/web/features/agent-v2/roster/page.tsx @@ -112,6 +112,7 @@ export default function RosterPage() {

{pageTitle}

diff --git a/web/plugins/eslint/index.js b/web/plugins/eslint/index.js index 09af24bbf7b..2fd685cb217 100644 --- a/web/plugins/eslint/index.js +++ b/web/plugins/eslint/index.js @@ -2,6 +2,7 @@ import consistentPlaceholders from './rules/consistent-placeholders.js' import i18nFlatKey from './rules/i18n-flat-key.js' import noExtraKeys from './rules/no-extra-keys.js' import preferTailwindIcons from './rules/prefer-tailwind-icons.js' +import requireTitleForTruncatedText from './rules/require-title-for-truncated-text.js' /** @type {import('eslint').ESLint.Plugin} */ const plugin = { @@ -14,6 +15,7 @@ const plugin = { 'i18n-flat-key': i18nFlatKey, 'no-extra-keys': noExtraKeys, 'prefer-tailwind-icons': preferTailwindIcons, + 'require-title-for-truncated-text': requireTitleForTruncatedText, }, } diff --git a/web/plugins/eslint/rules/fixtures/truncation.module.css b/web/plugins/eslint/rules/fixtures/truncation.module.css new file mode 100644 index 00000000000..fb81f59ed30 --- /dev/null +++ b/web/plugins/eslint/rules/fixtures/truncation.module.css @@ -0,0 +1,23 @@ +.singleLine { + @apply truncate; +} + +.multipleLines { + -webkit-line-clamp: 2; +} + +.noClamp { + line-clamp: none; +} + +.zeroClamp { + -webkit-line-clamp: 0; +} + +.unsetClamp { + line-clamp: unset !important; +} + +.regular { + overflow: hidden; +} diff --git a/web/plugins/eslint/rules/require-title-for-truncated-text.js b/web/plugins/eslint/rules/require-title-for-truncated-text.js new file mode 100644 index 00000000000..d0c77ba52d9 --- /dev/null +++ b/web/plugins/eslint/rules/require-title-for-truncated-text.js @@ -0,0 +1,743 @@ +import { readFileSync, statSync } from 'node:fs' +import { dirname, resolve } from 'node:path' + +const DEFAULT_TRUNCATION_CLASSES = new Set(['text-ellipsis', 'truncate']) +const cssModuleClassCache = new Map() +const UNSAFE_TITLE_EXPRESSION = Symbol('unsafe-title-expression') +const STYLE_WRAPPER_TYPES = new Set([ + 'ChainExpression', + 'TSAsExpression', + 'TSInstantiationExpression', + 'TSNonNullExpression', + 'TSSatisfiesExpression', + 'TSTypeAssertion', +]) + +function getJsxName(node) { + if (!node) return null + if (node.type === 'JSXIdentifier') return node.name + if (node.type === 'JSXMemberExpression') { + const objectName = getJsxName(node.object) + const propertyName = getJsxName(node.property) + return objectName && propertyName ? `${objectName}.${propertyName}` : null + } + if (node.type === 'JSXNamespacedName') { + const namespaceName = getJsxName(node.namespace) + const name = getJsxName(node.name) + return namespaceName && name ? `${namespaceName}:${name}` : null + } + return null +} + +function getAttribute(openingElement, attributeName) { + return openingElement.attributes.find( + (attribute) => + attribute.type === 'JSXAttribute' && + attribute.name.type === 'JSXIdentifier' && + attribute.name.name === attributeName, + ) +} + +function unwrapExpression(node) { + let current = node + while (current && STYLE_WRAPPER_TYPES.has(current.type)) current = current.expression + return current +} + +function getStaticString(node) { + const current = unwrapExpression(node) + if (!current) return null + if (current.type === 'Literal' && typeof current.value === 'string') return current.value + if (current.type === 'TemplateLiteral' && current.expressions.length === 0) + return current.quasis[0]?.value.cooked ?? current.quasis[0]?.value.raw ?? '' + return null +} + +function getUnprefixedClassName(token) { + let bracketDepth = 0 + let lastVariantSeparator = -1 + + for (let index = 0; index < token.length; index++) { + const character = token[index] + if (character === '[' || character === '(') bracketDepth++ + else if (character === ']' || character === ')') bracketDepth = Math.max(0, bracketDepth - 1) + else if (character === ':' && bracketDepth === 0) lastVariantSeparator = index + } + + return token + .slice(lastVariantSeparator + 1) + .replace(/^!/u, '') + .replace(/!$/u, '') +} + +function isTruncationClassToken(token) { + const className = getUnprefixedClassName(token) + if (DEFAULT_TRUNCATION_CLASSES.has(className)) return true + if (className.startsWith('line-clamp-') && className !== 'line-clamp-none') return true + + const normalizedArbitraryClass = className.replaceAll(' ', '').toLowerCase() + return ( + normalizedArbitraryClass === '[text-overflow:ellipsis]' || + /^\[(?:-webkit-)?line-clamp:(?!none(?:\]|$))[^\]]+\]$/u.test(normalizedArbitraryClass) + ) +} + +function stringContainsTruncationClass(value) { + return value.split(/\s+/u).some((token) => token && isTruncationClassToken(token)) +} + +function hasActiveLineClampDeclaration(cssText) { + const lineClampPattern = /(?:-webkit-)?line-clamp\s*:\s*([^;}]+)/giu + return [...cssText.matchAll(lineClampPattern)].some((match) => { + const value = (match[1] ?? '').trim() + return value !== '' && !/^(?:0|none|unset)\b/iu.test(value) + }) +} + +function getTruncatingCssModuleClassNames(cssText) { + const classNames = new Set() + const classBlockPattern = /\.([A-Z_a-z][\w-]*)\s*\{([^{}]*)\}/gu + + for (const match of cssText.matchAll(classBlockPattern)) { + const [, className, body = ''] = match + const hasTextEllipsis = /text-overflow\s*:\s*ellipsis\b/iu.test(body) + const hasLineClamp = hasActiveLineClampDeclaration(body) + const hasTruncationApply = [...body.matchAll(/@apply\s+([^;}]+)/gu)].some((applyMatch) => + stringContainsTruncationClass(applyMatch[1] ?? ''), + ) + + if (hasTextEllipsis || hasLineClamp || hasTruncationApply) classNames.add(className) + } + + return classNames +} + +function getCssModuleClassNames(cssModulePath) { + try { + const modifiedTime = statSync(cssModulePath).mtimeMs + const cached = cssModuleClassCache.get(cssModulePath) + if (cached?.modifiedTime === modifiedTime) return cached.classNames + + const classNames = getTruncatingCssModuleClassNames(readFileSync(cssModulePath, 'utf8')) + cssModuleClassCache.set(cssModulePath, { classNames, modifiedTime }) + return classNames + } catch { + return new Set() + } +} + +function getPropertyName(property) { + if (!property || property.type !== 'Property') return null + if (!property.computed && property.key.type === 'Identifier') return property.key.name + if (property.key.type === 'Literal' && typeof property.key.value === 'string') + return property.key.value + return null +} + +function getVariableByName(scope, name) { + let currentScope = scope + while (currentScope) { + const variable = currentScope.set.get(name) + if (variable) return variable + currentScope = currentScope.upper + } + return null +} + +function getVariableInitializer(identifier, sourceCode) { + const variable = getVariableByName(sourceCode.getScope(identifier), identifier.name) + if (!variable || variable.defs.length !== 1) return null + + const [definition] = variable.defs + if ( + definition.type !== 'Variable' || + definition.node.type !== 'VariableDeclarator' || + definition.node.id.type !== 'Identifier' || + !definition.node.init + ) + return null + + return { initializer: definition.node.init, variable } +} + +function expressionContainsTruncationClass( + node, + sourceCode, + cssModuleBindings, + seenVariables = new Set(), +) { + const current = unwrapExpression(node) + if (!current) return false + + const staticString = getStaticString(current) + if (staticString !== null) return stringContainsTruncationClass(staticString) + + switch (current.type) { + case 'Identifier': { + const resolvedVariable = getVariableInitializer(current, sourceCode) + if (!resolvedVariable || seenVariables.has(resolvedVariable.variable)) return false + const nextSeenVariables = new Set(seenVariables) + nextSeenVariables.add(resolvedVariable.variable) + return expressionContainsTruncationClass( + resolvedVariable.initializer, + sourceCode, + cssModuleBindings, + nextSeenVariables, + ) + } + case 'MemberExpression': { + const object = unwrapExpression(current.object) + if (object?.type !== 'Identifier') return false + const propertyName = current.computed + ? getStaticString(current.property) + : current.property.type === 'Identifier' + ? current.property.name + : null + const variable = getVariableByName(sourceCode.getScope(object), object.name) + return propertyName !== null && cssModuleBindings.get(variable)?.has(propertyName) + } + case 'TemplateLiteral': + return ( + current.quasis.some((quasi) => + stringContainsTruncationClass(quasi.value.cooked ?? quasi.value.raw), + ) || + current.expressions.some((expression) => + expressionContainsTruncationClass( + expression, + sourceCode, + cssModuleBindings, + seenVariables, + ), + ) + ) + case 'TaggedTemplateExpression': + return expressionContainsTruncationClass( + current.quasi, + sourceCode, + cssModuleBindings, + seenVariables, + ) + case 'CallExpression': + case 'NewExpression': + return ( + expressionContainsTruncationClass( + current.callee, + sourceCode, + cssModuleBindings, + seenVariables, + ) || + current.arguments.some((argument) => + argument.type === 'SpreadElement' + ? expressionContainsTruncationClass( + argument.argument, + sourceCode, + cssModuleBindings, + seenVariables, + ) + : expressionContainsTruncationClass( + argument, + sourceCode, + cssModuleBindings, + seenVariables, + ), + ) + ) + case 'ConditionalExpression': + return ( + expressionContainsTruncationClass( + current.consequent, + sourceCode, + cssModuleBindings, + seenVariables, + ) || + expressionContainsTruncationClass( + current.alternate, + sourceCode, + cssModuleBindings, + seenVariables, + ) + ) + case 'LogicalExpression': + case 'BinaryExpression': + return ( + expressionContainsTruncationClass( + current.left, + sourceCode, + cssModuleBindings, + seenVariables, + ) || + expressionContainsTruncationClass( + current.right, + sourceCode, + cssModuleBindings, + seenVariables, + ) + ) + case 'ArrayExpression': + return current.elements.some( + (element) => + element && + (element.type === 'SpreadElement' + ? expressionContainsTruncationClass( + element.argument, + sourceCode, + cssModuleBindings, + seenVariables, + ) + : expressionContainsTruncationClass( + element, + sourceCode, + cssModuleBindings, + seenVariables, + )), + ) + case 'ObjectExpression': + return current.properties.some((property) => { + if (property.type === 'SpreadElement') + return expressionContainsTruncationClass( + property.argument, + sourceCode, + cssModuleBindings, + seenVariables, + ) + + const propertyName = getPropertyName(property) + return ( + (propertyName !== null && stringContainsTruncationClass(propertyName)) || + expressionContainsTruncationClass( + property.value, + sourceCode, + cssModuleBindings, + seenVariables, + ) + ) + }) + case 'SequenceExpression': + return current.expressions.some((expression) => + expressionContainsTruncationClass(expression, sourceCode, cssModuleBindings, seenVariables), + ) + default: + return false + } +} + +function isActiveLineClampValue(node) { + const current = unwrapExpression(node) + if (!current) return false + if (current.type === 'Literal') { + if (current.value === null || current.value === 0) return false + if (typeof current.value === 'string') + return !['', '0', 'none', 'unset'].includes(current.value.trim().toLowerCase()) + return true + } + return current.type !== 'Identifier' || current.name !== 'undefined' +} + +function expressionContainsTruncationStyle(node, sourceCode, seenVariables = new Set()) { + const current = unwrapExpression(node) + if (!current) return false + + if (current.type === 'Identifier') { + const resolvedVariable = getVariableInitializer(current, sourceCode) + if (!resolvedVariable || seenVariables.has(resolvedVariable.variable)) return false + const nextSeenVariables = new Set(seenVariables) + nextSeenVariables.add(resolvedVariable.variable) + return expressionContainsTruncationStyle( + resolvedVariable.initializer, + sourceCode, + nextSeenVariables, + ) + } + + if (current.type === 'ConditionalExpression') { + return ( + expressionContainsTruncationStyle(current.consequent, sourceCode, seenVariables) || + expressionContainsTruncationStyle(current.alternate, sourceCode, seenVariables) + ) + } + + if (current.type === 'LogicalExpression') { + return ( + expressionContainsTruncationStyle(current.left, sourceCode, seenVariables) || + expressionContainsTruncationStyle(current.right, sourceCode, seenVariables) + ) + } + + if (current.type === 'ArrayExpression') { + return current.elements.some( + (element) => + element && + expressionContainsTruncationStyle( + element.type === 'SpreadElement' ? element.argument : element, + sourceCode, + seenVariables, + ), + ) + } + + if (current.type !== 'ObjectExpression') return false + + return current.properties.some((property) => { + if (property.type === 'SpreadElement') + return expressionContainsTruncationStyle(property.argument, sourceCode, seenVariables) + + const propertyName = getPropertyName(property) + if (propertyName === 'textOverflow' || propertyName === 'text-overflow') + return getStaticString(property.value)?.trim().toLowerCase() === 'ellipsis' + if ( + ['WebkitLineClamp', 'webkitLineClamp', 'lineClamp', '-webkit-line-clamp'].includes( + propertyName, + ) + ) + return isActiveLineClampValue(property.value) + return false + }) +} + +function hasTruncation(openingElement, sourceCode, cssModuleBindings) { + const classAttribute = + getAttribute(openingElement, 'className') ?? getAttribute(openingElement, 'class') + if (classAttribute?.value) { + if ( + classAttribute.value.type === 'Literal' && + typeof classAttribute.value.value === 'string' && + stringContainsTruncationClass(classAttribute.value.value) + ) + return true + if ( + classAttribute.value.type === 'JSXExpressionContainer' && + expressionContainsTruncationClass( + classAttribute.value.expression, + sourceCode, + cssModuleBindings, + ) + ) + return true + } + + const styleAttribute = getAttribute(openingElement, 'style') + return ( + styleAttribute?.value?.type === 'JSXExpressionContainer' && + expressionContainsTruncationStyle(styleAttribute.value.expression, sourceCode) + ) +} + +function isEmptyTitleAttribute(attribute) { + if (!attribute || !attribute.value) return true + if (attribute.value.type === 'Literal') + return typeof attribute.value.value !== 'string' || attribute.value.value.trim() === '' + if (attribute.value.type !== 'JSXExpressionContainer') return false + + const expression = unwrapExpression(attribute.value.expression) + if (!expression || expression.type === 'JSXEmptyExpression') return true + if (expression.type === 'Literal') + return expression.value === null || String(expression.value).trim() === '' + if (expression.type === 'TemplateLiteral' && expression.expressions.length === 0) + return (expression.quasis[0]?.value.cooked ?? '').trim() === '' + return expression.type === 'Identifier' && expression.name === 'undefined' +} + +function isRenderableTitleExpression(node) { + const current = unwrapExpression(node) + if (!current) return false + return ![ + 'ArrowFunctionExpression', + 'FunctionExpression', + 'JSXElement', + 'JSXEmptyExpression', + 'JSXFragment', + 'ObjectExpression', + 'SequenceExpression', + ].includes(current.type) +} + +function isSafeToDuplicateTitleExpression(node) { + const current = unwrapExpression(node) + if (!current) return false + + if (current.type === 'Identifier') return current.name !== 'undefined' + if (current.type === 'Literal') + return ( + current.value !== null && !['boolean', 'object', 'undefined'].includes(typeof current.value) + ) + if (current.type === 'TemplateLiteral' && current.expressions.length === 0) + return (current.quasis[0]?.value.cooked ?? '').trim() !== '' + return false +} + +function getMeaningfulChildren(element) { + return element.children.filter((child) => { + if (child.type === 'JSXText') return child.value.trim() !== '' + if (child.type === 'JSXExpressionContainer') + return child.expression.type !== 'JSXEmptyExpression' + return true + }) +} + +function getSingleTextChild(element) { + const meaningfulChildren = getMeaningfulChildren(element) + if (meaningfulChildren.length !== 1) return null + + const child = meaningfulChildren[0] + if (child.type === 'JSXElement') return getSingleTextChild(child) + return child +} + +function getExpressionTextChildCount(node) { + const current = unwrapExpression(node) + if (!current) return 0 + + if (current.type === 'JSXElement' || current.type === 'JSXFragment') + return getTextChildCount(current) + if (current.type === 'ConditionalExpression') + return Math.max( + getExpressionTextChildCount(current.consequent), + getExpressionTextChildCount(current.alternate), + ) + if (current.type === 'LogicalExpression') { + if (current.operator === '&&') return getExpressionTextChildCount(current.right) + return Math.max( + getExpressionTextChildCount(current.left), + getExpressionTextChildCount(current.right), + ) + } + if (current.type === 'ArrayExpression') { + let count = 0 + for (const element of current.elements) { + if (!element) continue + count += getExpressionTextChildCount( + element.type === 'SpreadElement' ? element.argument : element, + ) + if (count > 1) return count + } + return count + } + if (current.type === 'Literal') { + if (current.value === null || typeof current.value === 'boolean') return 0 + if (typeof current.value === 'string') return current.value.trim() === '' ? 0 : 1 + } + if (current.type === 'TemplateLiteral' && current.expressions.length === 0) + return (current.quasis[0]?.value.cooked ?? '').trim() === '' ? 0 : 1 + if (current.type === 'Identifier' && current.name === 'undefined') return 0 + return isRenderableTitleExpression(current) ? 1 : 0 +} + +function getTextChildCount(element) { + let count = 0 + + for (const child of getMeaningfulChildren(element)) { + if (child.type === 'JSXText') count++ + else if (child.type === 'JSXElement' || child.type === 'JSXFragment') + count += getTextChildCount(child) + else if (child.type === 'JSXExpressionContainer') + count += getExpressionTextChildCount(child.expression) + + if (count > 1) return count + } + + return count +} + +function hasMultipleTextChildren(openingElement) { + const element = openingElement.parent + return element?.type === 'JSXElement' && getTextChildCount(element) > 1 +} + +function getTitleFixTextFromAttribute(attribute, sourceCode) { + if (!attribute?.value) return null + if (attribute.value.type === 'Literal') { + if (typeof attribute.value.value !== 'string' || attribute.value.value.trim() === '') + return null + return `title=${JSON.stringify(attribute.value.value)}` + } + if (attribute.value.type !== 'JSXExpressionContainer') return null + if (!isSafeToDuplicateTitleExpression(attribute.value.expression)) return UNSAFE_TITLE_EXPRESSION + return `title={${sourceCode.getText(attribute.value.expression)}}` +} + +function getTitleFixText(openingElement, sourceCode) { + const element = openingElement.parent + if (!element || element.type !== 'JSXElement') return null + + const child = getSingleTextChild(element) + if (child?.type === 'JSXText') { + const value = child.value.trim().replace(/\s+/gu, ' ') + return value ? `title=${JSON.stringify(value)}` : null + } + let hasUnsafeTitleExpression = false + if (child?.type === 'JSXExpressionContainer') { + if (isSafeToDuplicateTitleExpression(child.expression)) + return `title={${sourceCode.getText(child.expression)}}` + hasUnsafeTitleExpression = true + } + + for (const attributeName of ['value', 'placeholder', 'content', 'label', 'name', 'aria-label']) { + const attributeFixText = getTitleFixTextFromAttribute( + getAttribute(openingElement, attributeName), + sourceCode, + ) + if (attributeFixText === UNSAFE_TITLE_EXPRESSION) { + hasUnsafeTitleExpression = true + continue + } + if (attributeFixText) return attributeFixText + } + + return hasUnsafeTitleExpression ? UNSAFE_TITLE_EXPRESSION : null +} + +function hasTitledSingleChild(openingElement) { + const element = openingElement.parent + if (!element || element.type !== 'JSXElement') return false + const meaningfulChildren = getMeaningfulChildren(element) + if (meaningfulChildren.length !== 1 || meaningfulChildren[0].type !== 'JSXElement') return null + const titleAttribute = getAttribute(meaningfulChildren[0].openingElement, 'title') + return !!titleAttribute && !isEmptyTitleAttribute(titleAttribute) +} + +function isTooltipTriggerOpening(openingElement, tooltipTriggerNames) { + const name = getJsxName(openingElement.name) + return name !== null && tooltipTriggerNames.has(name) +} + +function isInsideTooltipTrigger(openingElement, tooltipTriggerNames) { + let current = openingElement.parent + while (current) { + if ( + current.type === 'JSXElement' && + isTooltipTriggerOpening(current.openingElement, tooltipTriggerNames) + ) + return true + if ( + current.type === 'JSXAttribute' && + current.name.type === 'JSXIdentifier' && + current.name.name === 'render' && + current.parent?.type === 'JSXOpeningElement' && + isTooltipTriggerOpening(current.parent, tooltipTriggerNames) + ) + return true + current = current.parent + } + return false +} + +function getOwningVariableName(openingElement) { + let current = openingElement.parent + while (current) { + if (current.type === 'VariableDeclarator') + return current.id.type === 'Identifier' ? current.id.name : null + if ( + current.type === 'FunctionDeclaration' || + current.type === 'FunctionExpression' || + current.type === 'ArrowFunctionExpression' + ) + return null + current = current.parent + } + return null +} + +function collectReferencedIdentifiers(node, names) { + const current = unwrapExpression(node) + if (!current) return + if (current.type === 'Identifier') { + names.add(current.name) + return + } + if (current.type === 'ConditionalExpression') { + collectReferencedIdentifiers(current.consequent, names) + collectReferencedIdentifiers(current.alternate, names) + } else if (current.type === 'LogicalExpression') { + collectReferencedIdentifiers(current.left, names) + collectReferencedIdentifiers(current.right, names) + } +} + +/** @type {import('eslint').Rule.RuleModule} */ +export default { + meta: { + type: 'suggestion', + docs: { + description: 'Flag truncated JSX text for manual full-content disclosure review', + }, + messages: { + emptyTitle: + 'Review this truncated text against the disclosure policy; do not replace its existing title automatically.', + missingTitle: + 'Review this truncated text against the AUTO, COVERED, SKIP, and REVIEW disclosure policy.', + }, + schema: [], + }, + create(context) { + const candidates = [] + const renderIdentifiers = new Set() + const tooltipTriggerNames = new Set() + const cssModuleBindings = new Map() + + function recordCssModuleImport(node) { + if (typeof node.source.value !== 'string' || !node.source.value.endsWith('.module.css')) + return + const defaultSpecifier = node.specifiers.find( + (specifier) => specifier.type === 'ImportDefaultSpecifier', + ) + if (!defaultSpecifier) return + + const filename = context.filename || context.getFilename() + if (!filename || filename.startsWith('<')) return + const cssModulePath = resolve(dirname(filename), node.source.value) + const classNames = getCssModuleClassNames(cssModulePath) + const variable = context.sourceCode.getDeclaredVariables(defaultSpecifier)[0] + if (classNames.size > 0 && variable) cssModuleBindings.set(variable, classNames) + } + + return { + ImportDeclaration(node) { + recordCssModuleImport(node) + for (const specifier of node.specifiers) { + if ( + specifier.type === 'ImportSpecifier' && + (specifier.imported.name ?? specifier.imported.value) === 'TooltipTrigger' + ) + tooltipTriggerNames.add(specifier.local.name) + } + }, + JSXOpeningElement(node) { + if (isTooltipTriggerOpening(node, tooltipTriggerNames)) { + const renderAttribute = getAttribute(node, 'render') + if (renderAttribute?.value?.type === 'JSXExpressionContainer') + collectReferencedIdentifiers(renderAttribute.value.expression, renderIdentifiers) + } + + if ( + getAttribute(node, 'className') || + getAttribute(node, 'class') || + getAttribute(node, 'style') + ) + candidates.push(node) + }, + 'Program:exit': function () { + for (const openingElement of candidates) { + if (!hasTruncation(openingElement, context.sourceCode, cssModuleBindings)) continue + if (isInsideTooltipTrigger(openingElement, tooltipTriggerNames)) continue + + const owningVariableName = getOwningVariableName(openingElement) + if (owningVariableName && renderIdentifiers.has(owningVariableName)) continue + + if (hasMultipleTextChildren(openingElement)) continue + if (hasTitledSingleChild(openingElement)) continue + + const titleAttribute = getAttribute(openingElement, 'title') + if (titleAttribute && !isEmptyTitleAttribute(titleAttribute)) continue + + const fixText = getTitleFixText(openingElement, context.sourceCode) + if (fixText === UNSAFE_TITLE_EXPRESSION) continue + + context.report({ + node: titleAttribute ?? openingElement.name, + messageId: titleAttribute ? 'emptyTitle' : 'missingTitle', + }) + } + }, + } + }, +} diff --git a/web/plugins/eslint/rules/require-title-for-truncated-text.test.js b/web/plugins/eslint/rules/require-title-for-truncated-text.test.js new file mode 100644 index 00000000000..80fb1cbb62e --- /dev/null +++ b/web/plugins/eslint/rules/require-title-for-truncated-text.test.js @@ -0,0 +1,316 @@ +import assert from 'node:assert/strict' +import { resolve } from 'node:path' +import tsParser from '@typescript-eslint/parser' +import { Linter } from 'eslint' +import { it } from 'vitest' +import rule from './require-title-for-truncated-text.js' +import './fixtures/truncation.module.css' + +const plugin = { + rules: { + 'require-title-for-truncated-text': rule, + }, +} + +function verifyAndFix(code, filename = 'test.tsx') { + const linter = new Linter({ cwd: resolve('..') }) + return linter.verifyAndFix( + code, + [ + { + files: ['**/*.tsx'], + languageOptions: { + parser: tsParser, + parserOptions: { + ecmaFeatures: { jsx: true }, + ecmaVersion: 'latest', + sourceType: 'module', + }, + }, + plugins: { dify: plugin }, + rules: { 'dify/require-title-for-truncated-text': 'warn' }, + }, + ], + { filename }, + ) +} + +it('accepts an explicit title and line-clamp-none', () => { + const result = verifyAndFix(` + const name = 'Dify' + export const Example = () => <> + {name} + {name} + + `) + + assert.equal(result.messages.length, 0) + assert.equal(result.fixed, false) +}) + +it('accepts Dify UI TooltipTrigger children and render elements', () => { + const result = verifyAndFix(` + import { TooltipTrigger as Trigger } from '@langgenius/dify-ui/tooltip' + const name = 'Dify' + export const Example = () => <> + {name} + {name}} /> + + `) + + assert.equal(result.messages.length, 0) + assert.equal(result.fixed, false) +}) + +it('reports static classes, class helpers, constants, and inline styles without fixing', () => { + const code = ` + const truncatedClassName = cn('min-w-0', condition && 'md:truncate') + export const Example = ({ itemName, label }) => <> + {itemName} +

{label}

+
Description
+ + ` + const result = verifyAndFix(code) + + assert.equal(result.fixed, false) + assert.equal(result.messages.length, 3) + assert.ok(result.messages.every((message) => message.severity === 1)) + assert.equal(result.output, code) +}) + +it('resolves truncation variables from the active lexical scope', () => { + const shadowedParameterCode = ` + const className = 'truncate' + const style = { textOverflow: 'ellipsis' } + export const Example = ({ className, style, label }) => <> + {label} + {label} + + ` + const shadowedParameterResult = verifyAndFix(shadowedParameterCode) + + assert.equal(shadowedParameterResult.fixed, false) + assert.equal(shadowedParameterResult.messages.length, 0) + assert.equal(shadowedParameterResult.output, shadowedParameterCode) + + const nestedBindingResult = verifyAndFix(` + const className = 'regular' + const style = { color: 'red' } + export const Example = ({ label }) => { + const className = 'truncate' + const style = { textOverflow: 'ellipsis' } + return <> + {label} + {label} + + } + `) + + assert.equal(nestedBindingResult.fixed, false) + assert.equal(nestedBindingResult.messages.length, 2) + assert.ok(nestedBindingResult.messages.every((message) => message.severity === 1)) +}) + +it('reports an empty title without replacing it', () => { + const code = ` + export const Example = ({ name }) => {name} + ` + const result = verifyAndFix(code) + + assert.equal(result.fixed, false) + assert.equal(result.messages.length, 1) + assert.equal(result.messages[0].severity, 1) + assert.equal(result.messages[0].messageId, 'emptyTitle') + assert.equal(result.output, code) +}) + +it('reports custom components without fixing them', () => { + const code = ` + export const Example = ({ name }) => ( + {name} + ) + ` + const result = verifyAndFix(code) + + assert.equal(result.fixed, false) + assert.equal(result.messages.length, 1) + assert.equal(result.messages[0].severity, 1) + assert.equal(result.output, code) +}) + +it('ignores expressions that may execute user code', () => { + const code = ` + export const Example = ({ item }) => { + let index = 0 + return <> + {item.name} + {getLabel()} + {index++} + + + } + ` + const result = verifyAndFix(code) + + assert.equal(result.fixed, false) + assert.equal(result.messages.length, 0) + assert.equal(result.output, code) +}) + +it('reports a single nested text child or a text-bearing prop without fixing', () => { + const result = verifyAndFix(` + export const Example = ({ name, content }) => <> +
{name}
+ + + `) + + assert.equal(result.fixed, false) + assert.equal(result.messages.length, 2) + assert.ok(result.messages.every((message) => message.severity === 1)) +}) + +it('checks Dify UI single text but ignores multiple text children', () => { + const filename = resolve('../packages/dify-ui/src/example.tsx') + const result = verifyAndFix( + ` + export const Example = ({ primary, secondary }) => <> + {primary} + {primary}{secondary} +
+ {primary} + {secondary} +
+ + `, + filename, + ) + + assert.equal(result.fixed, false) + assert.equal(result.messages.length, 1) + assert.equal(result.messages[0].severity, 1) +}) + +it('does not count non-text conditional children as additional text', () => { + const result = verifyAndFix(` + export const Example = ({ primary, showOverlay }) => ( + + {primary} + {showOverlay && } + + ) + `) + + assert.equal(result.fixed, false) + assert.equal(result.messages.length, 1) + assert.equal(result.messages[0].messageId, 'missingTitle') +}) + +it('resolves truncation classes imported from CSS modules', () => { + const filename = resolve('plugins/eslint/rules/fixtures/example.tsx') + const result = verifyAndFix( + ` + import styles from './truncation.module.css' + export const Example = ({ name }) => <> + {name} + {name} + {name} + {name} + {name} + {name} + + export const Shadowed = ({ styles, name }) => ( + {name} + ) + `, + filename, + ) + + assert.equal(result.fixed, false) + assert.equal(result.messages.length, 2) + assert.ok(result.messages.every((message) => message.severity === 1)) +}) + +it('reports dynamic truncation class and style expressions without fixing', () => { + const code = ` + const regularClassName = 'regular' + const dynamicClassNames = ['regular'] + const classMap = { regular: true } + const regularStyle = { color: 'red' } + const styles = [regularStyle] + const tag = strings => strings[0] + export const Example = ({ condition, name }) => <> + {name} + {name} + {name} + {name} + {name} + {name} + {name} + {name} + {name} + {name} + {name} + {name} + {name} + {name} + + ` + const result = verifyAndFix(code) + + assert.equal(result.fixed, false) + assert.equal(result.messages.length, 11) + assert.ok(result.messages.every((message) => message.messageId === 'missingTitle')) + assert.equal(result.output, code) +}) + +it('reports statically empty title forms without replacing them', () => { + const code = ` + export const Example = ({ name }) => <> + {name} + {name} + {name} + {name} + {name} + + ` + const result = verifyAndFix(code) + + assert.equal(result.fixed, false) + assert.equal(result.messages.length, 5) + assert.ok(result.messages.every((message) => message.messageId === 'emptyTitle')) + assert.equal(result.output, code) +}) + +it('reports safe literal content but accepts a titled single child', () => { + const result = verifyAndFix(` + export const Example = ({ name }) => <> + {42} + {\`Dify\`} + {name} +
{name}
+ + `) + + assert.equal(result.fixed, false) + assert.equal(result.messages.length, 3) + assert.deepEqual( + result.messages.map((message) => message.messageId), + ['missingTitle', 'missingTitle', 'emptyTitle'], + ) +}) + +it('accepts referenced TooltipTrigger render candidates', () => { + const result = verifyAndFix(` + import { TooltipTrigger as Trigger } from '@langgenius/dify-ui/tooltip' + export const Example = ({ condition, name }) => { + const primaryTrigger = {name} + const fallbackTrigger = {name} + return + } + `) + + assert.equal(result.fixed, false) + assert.equal(result.messages.length, 0) +}) From 3ed28dce33bd4b07c69f1c78c958c575551dc18e Mon Sep 17 00:00:00 2001 From: elyar1124 <86278979+elyar1124@users.noreply.github.com> Date: Tue, 1 Sep 2026 06:24:04 +0000 Subject: [PATCH 14/33] refactor(models): pass session into DocumentSegment and AppDatasetJoin accessors (#41568) --- api/models/dataset.py | 15 ++--- .../models/test_dataset_models.py | 4 +- .../unit_tests/models/test_dataset_models.py | 57 +++++++++++++++++++ 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/api/models/dataset.py b/api/models/dataset.py index 891d949c8e6..926b8969ed8 100644 --- a/api/models/dataset.py +++ b/api/models/dataset.py @@ -974,17 +974,15 @@ class DocumentSegment(TypeBase): """Load the owning document with the caller-owned database session.""" return session.get(Document, self.document_id) - @property - def previous_segment(self): - return db.session.scalar( + def previous_segment(self, session: Session) -> "DocumentSegment | None": + return session.scalar( select(DocumentSegment).where( DocumentSegment.document_id == self.document_id, DocumentSegment.position == self.position - 1 ) ) - @property - def next_segment(self): - return db.session.scalar( + def next_segment(self, session: Session) -> "DocumentSegment | None": + return session.scalar( select(DocumentSegment).where( DocumentSegment.document_id == self.document_id, DocumentSegment.position == self.position + 1 ) @@ -1204,9 +1202,8 @@ class AppDatasetJoin(TypeBase): DateTime, nullable=False, server_default=sa.func.current_timestamp(), init=False ) - @property - def app(self): - return db.session.get(App, self.app_id) + def app(self, session: Session) -> App | None: + return session.get(App, self.app_id) class DatasetQuery(TypeBase): diff --git a/api/tests/test_containers_integration_tests/models/test_dataset_models.py b/api/tests/test_containers_integration_tests/models/test_dataset_models.py index a3bbf196576..53c3ca33e0b 100644 --- a/api/tests/test_containers_integration_tests/models/test_dataset_models.py +++ b/api/tests/test_containers_integration_tests/models/test_dataset_models.py @@ -426,7 +426,7 @@ class TestDocumentSegmentNavigationProperties: db_session_with_containers.flush() # Act - prev_seg = segment.previous_segment + prev_seg = segment.previous_segment(session=db_session_with_containers) # Assert assert prev_seg is not None @@ -483,7 +483,7 @@ class TestDocumentSegmentNavigationProperties: db_session_with_containers.flush() # Act - next_seg = segment.next_segment + next_seg = segment.next_segment(session=db_session_with_containers) # Assert assert next_seg is not None diff --git a/api/tests/unit_tests/models/test_dataset_models.py b/api/tests/unit_tests/models/test_dataset_models.py index 5c058c42b10..4d691214d2b 100644 --- a/api/tests/unit_tests/models/test_dataset_models.py +++ b/api/tests/unit_tests/models/test_dataset_models.py @@ -1660,3 +1660,60 @@ class TestChildChunkSessionAccessors: assert child_chunk.dataset(session=sqlite_session) is None assert child_chunk.document(session=sqlite_session) is None assert child_chunk.segment(session=sqlite_session) is None + + +class TestDocumentSegmentNeighborAccessors: + """Regression coverage for ``DocumentSegment.previous_segment`` and ``next_segment`` refactored + to take a caller-provided session. + + These were ``@property`` accessors reaching for the global ``db.session`` internally; they are now + plain methods accepting a ``Session`` explicitly. + """ + + def test_accessors_resolve_neighboring_segments_via_caller_session(self, sqlite_session: Session): + dataset = _make_dataset(dataset_id=str(uuid4()), tenant_id=str(uuid4())) + document = _make_document(document_id=str(uuid4()), dataset_id=dataset.id, tenant_id=dataset.tenant_id) + segments = _make_segments(document, [0, 0, 0]) + sqlite_session.add_all([dataset, document, *segments]) + sqlite_session.flush() + + middle_segment = segments[1] + assert middle_segment.previous_segment(session=sqlite_session) is segments[0] + assert middle_segment.next_segment(session=sqlite_session) is segments[2] + + def test_accessors_return_none_when_neighbors_are_absent(self, sqlite_session: Session): + dataset = _make_dataset(dataset_id=str(uuid4()), tenant_id=str(uuid4())) + document = _make_document(document_id=str(uuid4()), dataset_id=dataset.id, tenant_id=dataset.tenant_id) + segment = _make_segments(document, [0])[0] + sqlite_session.add_all([dataset, document, segment]) + sqlite_session.flush() + + assert segment.previous_segment(session=sqlite_session) is None + assert segment.next_segment(session=sqlite_session) is None + + +class TestAppDatasetJoinSessionAccessors: + """Regression coverage for ``AppDatasetJoin.app`` refactored to take a caller-provided session. + + ``AppDatasetJoin.app`` was an ``@property`` accessor reaching for the global ``db.session`` internally; + it is now a plain method accepting a ``Session`` explicitly. + """ + + def test_app_accessor_resolves_app_via_caller_session(self, sqlite_session: Session): + app = _make_app(app_id=str(uuid4())) + join = AppDatasetJoin( + app_id=app.id, + dataset_id=str(uuid4()), + ) + sqlite_session.add_all([app, join]) + sqlite_session.flush() + + assert join.app(session=sqlite_session) is app + + def test_app_accessor_returns_none_when_app_absent(self, sqlite_session: Session): + join = AppDatasetJoin( + app_id=str(uuid4()), + dataset_id=str(uuid4()), + ) + + assert join.app(session=sqlite_session) is None From 566b8857dd0ae0519803abb5f16e6b386d560056 Mon Sep 17 00:00:00 2001 From: Keith <148296621+keith991001@users.noreply.github.com> Date: Tue, 1 Sep 2026 06:28:24 +0000 Subject: [PATCH 15/33] refactor(models): remove legacy db.session property wrappers on Dataset (#41569) --- .../rag_pipeline/rag_pipeline_datasets.py | 7 ++++--- api/models/dataset.py | 20 ------------------- .../models/test_dataset_models.py | 4 ++-- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_datasets.py b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_datasets.py index 3bbbecaecfd..094adc42022 100644 --- a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_datasets.py +++ b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_datasets.py @@ -17,7 +17,7 @@ from controllers.console.wraps import ( with_current_user, ) from extensions.ext_database import db -from fields.dataset_fields import DatasetDetailResponse +from fields.dataset_fields import DatasetDetailResponse, dataset_detail_response_source from libs.helper import dump_response from libs.login import login_required from models import Account @@ -116,6 +116,7 @@ class CreateEmptyRagPipelineDatasetApi(Resource): # The role of the current user in the ta table must be admin, owner, or editor, or dataset_operator if not current_user.is_dataset_editor: raise Forbidden() + session = db.session() dataset = DatasetService.create_empty_rag_pipeline_dataset( tenant_id=current_tenant_id, rag_pipeline_dataset_create_entity=RagPipelineDatasetCreateEntity( @@ -129,6 +130,6 @@ class CreateEmptyRagPipelineDatasetApi(Resource): permission=DatasetPermissionEnum.ONLY_ME, partial_member_list=None, ), - session=db.session(), + session=session, ) - return dump_response(DatasetDetailResponse, dataset), 201 + return dump_response(DatasetDetailResponse, dataset_detail_response_source(dataset, session=session)), 201 diff --git a/api/models/dataset.py b/api/models/dataset.py index 926b8969ed8..ddcc8020ed8 100644 --- a/api/models/dataset.py +++ b/api/models/dataset.py @@ -212,17 +212,9 @@ class Dataset(Base): enable_api = mapped_column(sa.Boolean, nullable=False, server_default=sa.text("true")) is_multimodal = mapped_column(sa.Boolean, default=False, nullable=False, server_default=sa.text("false")) - @property - def total_documents(self) -> int: - return self.get_total_documents(session=db.session()) - def get_total_documents(self, *, session: Session) -> int: return self.get_document_count(session=session) - @property - def total_available_documents(self) -> int: - return self.get_total_available_documents(session=db.session()) - def get_total_available_documents(self, *, session: Session) -> int: return ( session.scalar( @@ -258,20 +250,12 @@ class Dataset(Base): def get_created_by_account(self, *, session: Session) -> Account | None: return session.get(Account, self.created_by) - @property - def author_name(self) -> str | None: - return self.get_author_name(session=db.session()) - def get_author_name(self, *, session: Session) -> str | None: account = self.get_created_by_account(session=session) if account: return account.name return None - @property - def latest_process_rule(self): - return self.get_latest_process_rule(session=db.session()) - def get_latest_process_rule(self, *, session: Session) -> "DatasetProcessRule | None": return session.scalar( select(DatasetProcessRule) @@ -391,10 +375,6 @@ class Dataset(Base): return tags or [] - @property - def external_knowledge_info(self) -> dict[str, Any] | None: - return self.get_external_knowledge_info(session=db.session()) - def get_external_knowledge_info(self, *, session: Session) -> dict[str, Any] | None: if self.provider != "external": return None diff --git a/api/tests/test_containers_integration_tests/models/test_dataset_models.py b/api/tests/test_containers_integration_tests/models/test_dataset_models.py index 53c3ca33e0b..d817894b1de 100644 --- a/api/tests/test_containers_integration_tests/models/test_dataset_models.py +++ b/api/tests/test_containers_integration_tests/models/test_dataset_models.py @@ -49,7 +49,7 @@ class TestDatasetDocumentProperties: db_session_with_containers.add(doc) db_session_with_containers.flush() - assert dataset.total_documents == 3 + assert dataset.get_total_documents(session=db_session_with_containers) == 3 def test_dataset_available_documents_count(self, db_session_with_containers: Session) -> None: """Test dataset can count available documents.""" @@ -104,7 +104,7 @@ class TestDatasetDocumentProperties: db_session_with_containers.add_all([doc_available, doc_pending, doc_disabled]) db_session_with_containers.flush() - assert dataset.total_available_documents == 1 + assert dataset.get_total_available_documents(session=db_session_with_containers) == 1 def test_dataset_word_count_aggregation(self, db_session_with_containers: Session) -> None: """Test dataset can aggregate word count from documents.""" From 770b83d9ccea357142831fea921a2069e5a4c01b Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 1 Sep 2026 06:37:25 +0000 Subject: [PATCH 16/33] fix: handle published workflow request failures in access points (#41572) --- .../__tests__/built-in-access-points.spec.tsx | 37 ++++++++++++++++++- .../built-in-access-points/index.tsx | 16 ++++++-- web/service/use-workflow.ts | 10 ++++- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/web/app/components/app/access-point/__tests__/built-in-access-points.spec.tsx b/web/app/components/app/access-point/__tests__/built-in-access-points.spec.tsx index 5cefe0aeeb7..b88bb755384 100644 --- a/web/app/components/app/access-point/__tests__/built-in-access-points.spec.tsx +++ b/web/app/components/app/access-point/__tests__/built-in-access-points.spec.tsx @@ -12,6 +12,7 @@ const mocks = vi.hoisted(() => ({ } as Record, workflow: { data: null as Record | null, + isError: false, isPending: false, }, webCard: vi.fn(), @@ -23,6 +24,7 @@ const mocks = vi.hoisted(() => ({ }, mcpCard: vi.fn(), triggerCard: vi.fn(), + useAppWorkflow: vi.fn(), })) vi.mock('react-i18next', async () => { @@ -60,7 +62,10 @@ vi.mock('@/context/i18n', () => ({ })) vi.mock('@/service/use-workflow', () => ({ - useAppWorkflow: () => mocks.workflow, + useAppWorkflow: (...args: unknown[]) => { + mocks.useAppWorkflow(...args) + return mocks.workflow + }, })) vi.mock('@/utils/permission', () => ({ @@ -118,6 +123,7 @@ describe('BuiltInAccessPoints', () => { } mocks.workflow = { data: null, + isError: false, isPending: false, } mocks.capabilities = { @@ -150,6 +156,7 @@ describe('BuiltInAccessPoints', () => { nodes: [{ data: { type: 'start' } }], }, }, + isError: false, isPending: false, } @@ -197,6 +204,7 @@ describe('BuiltInAccessPoints', () => { nodes: [{ data: { type: 'trigger-webhook' } }], }, }, + isError: false, isPending: false, } @@ -222,6 +230,7 @@ describe('BuiltInAccessPoints', () => { it('keeps all cards visible while the published workflow is loading', () => { mocks.workflow = { data: null, + isError: false, isPending: true, } @@ -233,4 +242,30 @@ describe('BuiltInAccessPoints', () => { expect.objectContaining({ availability: 'loading' }), ) }) + + it('does not show the unpublished card when loading the published workflow fails', () => { + mocks.workflow = { + data: null, + isError: true, + isPending: false, + } + + render() + + expect( + screen.queryByText('deployments.studio.accessPoint.noPublishedTitle'), + ).not.toBeInTheDocument() + }) + + it('does not retry forbidden published workflow requests', () => { + render() + + const options = mocks.useAppWorkflow.mock.calls.at(-1)?.[1] as { + retry: (failureCount: number, error: unknown) => boolean + } + + expect(options.retry(0, new Response(null, { status: 403 }))).toBe(false) + expect(options.retry(0, new Response(null, { status: 500 }))).toBe(true) + expect(options.retry(3, new Response(null, { status: 500 }))).toBe(false) + }) }) diff --git a/web/app/components/app/access-point/built-in-access-points/index.tsx b/web/app/components/app/access-point/built-in-access-points/index.tsx index 95847013f29..5f739e69ea2 100644 --- a/web/app/components/app/access-point/built-in-access-points/index.tsx +++ b/web/app/components/app/access-point/built-in-access-points/index.tsx @@ -39,9 +39,17 @@ export function BuiltInAccessPoints({ appId, highlightedAccessPoint }: BuiltInAc const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom) const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions()) const shouldFetchWorkflow = Boolean(appInfo && isAdvancedApp(appInfo)) - const { data: workflow, isPending: workflowLoading } = useAppWorkflow( - shouldFetchWorkflow ? appId : '', - ) + const { + data: workflow, + isError: workflowError, + isPending: workflowLoading, + } = useAppWorkflow(shouldFetchWorkflow ? appId : '', { + retry: (failureCount, error) => { + if (error instanceof Response && error.status === 403) return false + + return failureCount < 3 + }, + }) const capabilities = useMemo( () => getAppACLCapabilities(appInfo?.permission_keys, { @@ -72,7 +80,7 @@ export function BuiltInAccessPoints({ appId, highlightedAccessPoint }: BuiltInAc return (
- {workflowState.isUnpublished && !workflowLoading && ( + {workflowState.isUnpublished && !workflowLoading && !workflowError && (
diff --git a/web/service/use-workflow.ts b/web/service/use-workflow.ts index 4d400bba27f..6a887e110d0 100644 --- a/web/service/use-workflow.ts +++ b/web/service/use-workflow.ts @@ -1,3 +1,4 @@ +import type { UseQueryOptions } from '@tanstack/react-query' import type { CommonResponse } from '@/models/common' import type { FlowType } from '@/types/common' import type { @@ -18,8 +19,13 @@ import { appWorkflowQueryOptions, appWorkflowVersionsInfiniteQueryKey } from './ const NAME_SPACE = 'workflow' -export const useAppWorkflow = (appID: string) => { - return useQuery(appWorkflowQueryOptions(appID)) +type UseAppWorkflowOptions = Pick + +export const useAppWorkflow = (appID: string, options?: UseAppWorkflowOptions) => { + return useQuery({ + ...appWorkflowQueryOptions(appID), + ...options, + }) } const WorkflowRunHistoryKey = [NAME_SPACE, 'runHistory'] From fe39211aac117e82c401a9c1b4485ed594baa125 Mon Sep 17 00:00:00 2001 From: zyssyz123 <916125788@qq.com> Date: Tue, 1 Sep 2026 06:47:16 +0000 Subject: [PATCH 17/33] fix(agent): bound Redis run event streams (#41566) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- dify-agent/.example.env | 13 +- dify-agent/docs/dify-agent/guide/index.md | 21 +- .../src/dify_agent/runtime/event_coalescer.py | 143 +++++++++++ .../src/dify_agent/runtime/run_scheduler.py | 16 ++ dify-agent/src/dify_agent/runtime/runner.py | 31 ++- dify-agent/src/dify_agent/server/app.py | 4 + dify-agent/src/dify_agent/server/settings.py | 16 +- .../src/dify_agent/storage/redis_run_store.py | 36 ++- .../test_working_environment.py | 1 + .../storage/test_terminal_finalization.py | 48 ++++ .../runtime/test_event_coalescer.py | 227 ++++++++++++++++++ .../dify_agent/runtime/test_run_scheduler.py | 8 +- .../tests/local/dify_agent/server/test_app.py | 17 ++ .../local/dify_agent/server/test_settings.py | 53 +++- .../storage/test_redis_run_store.py | 82 ++++++- .../envs/core-services/dify-agent.env.example | 6 +- 16 files changed, 690 insertions(+), 32 deletions(-) create mode 100644 dify-agent/src/dify_agent/runtime/event_coalescer.py create mode 100644 dify-agent/tests/local/dify_agent/runtime/test_event_coalescer.py diff --git a/dify-agent/.example.env b/dify-agent/.example.env index 50294c9bf85..24d3390f7ec 100644 --- a/dify-agent/.example.env +++ b/dify-agent/.example.env @@ -11,8 +11,17 @@ DIFY_AGENT_REDIS_PREFIX=dify-agent # Shutdown and retention # Seconds to wait for active local runs during graceful shutdown before cancellation. DIFY_AGENT_SHUTDOWN_GRACE_SECONDS=30 -# Seconds to retain Redis run records and per-run event streams (default: 3 days). -DIFY_AGENT_RUN_RETENTION_SECONDS=259200 +# Seconds to retain Redis run records and per-run event streams after the last write (default: 2 hours). +DIFY_AGENT_RUN_RETENTION_SECONDS=7200 +# Approximate target maximum for replayable events retained in each per-run Redis Stream. +DIFY_AGENT_RUN_EVENT_STREAM_MAX_LENGTH=5000 +# Set false to publish every text delta without coalescing. +DIFY_AGENT_STREAM_TEXT_DELTA_COALESCING_ENABLED=true +# Soft debounce interval for compatible text deltas. Already-ready events may continue +# to merge past this interval; a waiting source triggers the timed flush. +DIFY_AGENT_STREAM_TEXT_DELTA_FLUSH_INTERVAL_MS=100 +# Flush a text-delta batch immediately once it reaches this many characters. +DIFY_AGENT_STREAM_TEXT_DELTA_MAX_CHARS=4096 # Plugin daemon # Base URL for the Dify plugin daemon used by local runs. diff --git a/dify-agent/docs/dify-agent/guide/index.md b/dify-agent/docs/dify-agent/guide/index.md index de72c0ba286..99ff7b0ac28 100644 --- a/dify-agent/docs/dify-agent/guide/index.md +++ b/dify-agent/docs/dify-agent/guide/index.md @@ -34,7 +34,11 @@ also reads `.env` and `dify-agent/.env` when present. | `DIFY_AGENT_REDIS_URL` | `redis://localhost:6379/0` | Redis connection URL. | | `DIFY_AGENT_REDIS_PREFIX` | `dify-agent` | Prefix for Redis record and event keys. | | `DIFY_AGENT_SHUTDOWN_GRACE_SECONDS` | `30` | Seconds to wait for active local runs during graceful shutdown before cancellation. | -| `DIFY_AGENT_RUN_RETENTION_SECONDS` | `259200` | Seconds to retain Redis run records and per-run event streams; defaults to 3 days. | +| `DIFY_AGENT_RUN_RETENTION_SECONDS` | `7200` | Seconds to retain Redis run records and per-run event streams after their last write; defaults to 2 hours. | +| `DIFY_AGENT_RUN_EVENT_STREAM_MAX_LENGTH` | `5000` | Approximate target maximum for replayable events retained in each per-run Redis Stream. | +| `DIFY_AGENT_STREAM_TEXT_DELTA_COALESCING_ENABLED` | `true` | Set `false` to publish each text delta without coalescing. | +| `DIFY_AGENT_STREAM_TEXT_DELTA_FLUSH_INTERVAL_MS` | `100` | Soft debounce interval for compatible text deltas. Already-ready source events may continue to merge after this interval; a waiting source triggers the timed flush. Must be greater than zero; use `DIFY_AGENT_STREAM_TEXT_DELTA_COALESCING_ENABLED=false` to disable coalescing. | +| `DIFY_AGENT_STREAM_TEXT_DELTA_MAX_CHARS` | `4096` | Character threshold that flushes a buffered text-delta event immediately. | | `DIFY_AGENT_RUN_TIMEOUT_SECONDS` | `3600` | Wall-clock deadline in seconds for the Pydantic AI `agent.run(...)` model/tool loop. Deadline failures use `agent_run_limit_exceeded`. Its default intentionally matches `DIFY_AGENT_E2B_ACTIVE_TIMEOUT_SECONDS`, but the settings are independently configurable. | | `DIFY_AGENT_BINDING_FILE_DOWNLOAD_COMMAND_TIMEOUT_SECONDS` | `210` | Shell command deadline for running the sandbox `dify-agent file upload --no-download-link` conversion. Keep it above the CLI's 180-second upload deadline. | | `DIFY_AGENT_API_TOKEN` | empty | Optional Bearer token required by private run, Execution Binding, Home Snapshot, and Binding file control-plane routes. Must match Dify API `AGENT_BACKEND_API_TOKEN`. | @@ -75,7 +79,10 @@ Example `.env`: DIFY_AGENT_REDIS_URL=redis://localhost:6379/0 DIFY_AGENT_REDIS_PREFIX=dify-agent-dev DIFY_AGENT_SHUTDOWN_GRACE_SECONDS=30 -DIFY_AGENT_RUN_RETENTION_SECONDS=259200 +DIFY_AGENT_RUN_RETENTION_SECONDS=7200 +DIFY_AGENT_RUN_EVENT_STREAM_MAX_LENGTH=5000 +DIFY_AGENT_STREAM_TEXT_DELTA_FLUSH_INTERVAL_MS=100 +DIFY_AGENT_STREAM_TEXT_DELTA_MAX_CHARS=4096 DIFY_AGENT_RUN_TIMEOUT_SECONDS=3600 DIFY_AGENT_API_TOKEN=replace-with-agent-backend-token DIFY_AGENT_PLUGIN_DAEMON_URL=http://localhost:5002 @@ -138,7 +145,15 @@ and lifecycle contract. Run records and event streams use the same retention. Status writes refresh the record TTL, and event writes refresh both the stream TTL and the corresponding -record TTL so active runs that keep producing events remain observable. +record TTL so active runs that keep producing events remain observable. Text +deltas for the same response part and provider metadata are coalesced within a +soft debounce/size window before being published. The debounce timer flushes +when reading the next source event would block; already-ready events may keep +merging until the character threshold or a non-compatible event is reached. +Every Redis Stream write also applies the configured approximate maximum length. A reconnect cursor older than the +retained window resumes from the oldest remaining event, so callers must treat +the terminal snapshot and application-owned history as authoritative rather +than relying on the Agent event stream as long-term history. ## Validate the E2B Compose deployment diff --git a/dify-agent/src/dify_agent/runtime/event_coalescer.py b/dify-agent/src/dify_agent/runtime/event_coalescer.py new file mode 100644 index 00000000000..a35d2c52fe3 --- /dev/null +++ b/dify-agent/src/dify_agent/runtime/event_coalescer.py @@ -0,0 +1,143 @@ +"""Bound high-frequency text deltas before they reach the shared event sink.""" + +import asyncio +from collections.abc import AsyncIterable, AsyncIterator +from contextlib import suppress +from dataclasses import replace + +from pydantic_ai.messages import AgentStreamEvent, PartDeltaEvent, TextPartDelta + + +DEFAULT_TEXT_DELTA_FLUSH_INTERVAL_SECONDS = 0.1 +DEFAULT_TEXT_DELTA_MAX_CHARS = 4096 + + +async def coalesce_agent_stream_events( + events: AsyncIterable[AgentStreamEvent], + *, + enabled: bool = True, + flush_interval_seconds: float = DEFAULT_TEXT_DELTA_FLUSH_INTERVAL_SECONDS, + max_chars: int = DEFAULT_TEXT_DELTA_MAX_CHARS, +) -> AsyncIterator[AgentStreamEvent]: + """Merge compatible text deltas with a soft debounce interval. + + One source event is read ahead while a text delta is buffered. Non-text events + flush the buffer first, preserving the public Pydantic AI event ordering. The + timer starts with the first buffered delta instead of sliding on every token. + Already-ready source events may continue to merge after the interval; the + interval triggers a flush once reading the next source event would block. + """ + if flush_interval_seconds <= 0: + raise ValueError("flush_interval_seconds must be positive") + if max_chars <= 0: + raise ValueError("max_chars must be positive") + if not enabled: + async for event in events: + yield event + return + + iterator = aiter(events) + next_event_task: asyncio.Future[AgentStreamEvent] | None = asyncio.ensure_future(anext(iterator)) + buffered: PartDeltaEvent | None = None + flush_deadline: float | None = None + loop = asyncio.get_running_loop() + + try: + while next_event_task is not None: + timeout = None if flush_deadline is None else max(0.0, flush_deadline - loop.time()) + try: + done, _pending = await asyncio.wait((next_event_task,), timeout=timeout) + except asyncio.CancelledError: + if buffered is not None: + yield buffered + raise + + if not done: + assert buffered is not None + yield buffered + buffered = None + flush_deadline = None + continue + + try: + event = next_event_task.result() + except StopAsyncIteration: + next_event_task = None + if buffered is not None: + yield buffered + return + except BaseException: + next_event_task = None + if buffered is not None: + yield buffered + raise + + next_event_task = asyncio.ensure_future(anext(iterator)) + text_delta = _text_delta(event) + if text_delta is None: + if buffered is not None: + yield buffered + buffered = None + flush_deadline = None + yield event + continue + + if buffered is not None and _can_merge(buffered, event): + buffered = _merge_text_deltas(buffered, event) + else: + if buffered is not None: + yield buffered + assert isinstance(event, PartDeltaEvent) + buffered = event + flush_deadline = loop.time() + flush_interval_seconds + + buffered_delta = _text_delta(buffered) + assert buffered_delta is not None + if len(buffered_delta.content_delta) >= max_chars: + yield buffered + buffered = None + flush_deadline = None + finally: + if next_event_task is not None and not next_event_task.done(): + next_event_task.cancel() + with suppress(asyncio.CancelledError, StopAsyncIteration): + _ = await next_event_task + + +def _text_delta(event: AgentStreamEvent) -> TextPartDelta | None: + if isinstance(event, PartDeltaEvent) and isinstance(event.delta, TextPartDelta): + return event.delta + return None + + +def _can_merge(left: PartDeltaEvent, right: AgentStreamEvent) -> bool: + right_delta = _text_delta(right) + if right_delta is None: + return False + left_delta = left.delta + assert isinstance(left_delta, TextPartDelta) + assert isinstance(right, PartDeltaEvent) + return ( + left.index == right.index + and left_delta.provider_name == right_delta.provider_name + and left_delta.provider_details == right_delta.provider_details + ) + + +def _merge_text_deltas(left: PartDeltaEvent, right: AgentStreamEvent) -> PartDeltaEvent: + left_delta = left.delta + right_delta = _text_delta(right) + assert isinstance(left_delta, TextPartDelta) + assert right_delta is not None + merged_delta = replace( + left_delta, + content_delta=left_delta.content_delta + right_delta.content_delta, + ) + return replace(left, delta=merged_delta) + + +__all__ = [ + "DEFAULT_TEXT_DELTA_FLUSH_INTERVAL_SECONDS", + "DEFAULT_TEXT_DELTA_MAX_CHARS", + "coalesce_agent_stream_events", +] diff --git a/dify-agent/src/dify_agent/runtime/run_scheduler.py b/dify-agent/src/dify_agent/runtime/run_scheduler.py index a0bf260bd25..5a57999547f 100644 --- a/dify-agent/src/dify_agent/runtime/run_scheduler.py +++ b/dify-agent/src/dify_agent/runtime/run_scheduler.py @@ -24,6 +24,10 @@ from agenton.compositor import CompositorSessionSnapshot, LayerProviderInput from dify_agent.protocol.schemas import AgentRunUsage, CancelRunRequest, CancelRunResponse, CreateRunRequest, RunStatus from dify_agent.runtime.cancellation import RunCancellationIntent from dify_agent.runtime.compositor_factory import create_default_layer_providers +from dify_agent.runtime.event_coalescer import ( + DEFAULT_TEXT_DELTA_FLUSH_INTERVAL_SECONDS, + DEFAULT_TEXT_DELTA_MAX_CHARS, +) from dify_agent.runtime.event_sink import RunEventSink, RunFinalizationResult, emit_run_failed from dify_agent.runtime.runner import DEFAULT_AGENT_RUN_TIMEOUT_SECONDS, AgentRunRunner from dify_agent.server.schemas import RunRecord @@ -106,6 +110,9 @@ class RunScheduler: store: RunStore shutdown_grace_seconds: float run_timeout_seconds: float + stream_text_delta_coalescing_enabled: bool + stream_text_delta_flush_interval_seconds: float + stream_text_delta_max_chars: int active_tasks: dict[str, asyncio.Task[None]] stopping: bool runner_factory: RunRunnerFactory | None @@ -122,12 +129,18 @@ class RunScheduler: dify_api_http_client: httpx.AsyncClient, shutdown_grace_seconds: float = 30, run_timeout_seconds: float = DEFAULT_AGENT_RUN_TIMEOUT_SECONDS, + stream_text_delta_coalescing_enabled: bool = True, + stream_text_delta_flush_interval_seconds: float = DEFAULT_TEXT_DELTA_FLUSH_INTERVAL_SECONDS, + stream_text_delta_max_chars: int = DEFAULT_TEXT_DELTA_MAX_CHARS, layer_providers: tuple[LayerProviderInput, ...] | None = None, runner_factory: RunRunnerFactory | None = None, ) -> None: self.store = store self.shutdown_grace_seconds = shutdown_grace_seconds self.run_timeout_seconds = run_timeout_seconds + self.stream_text_delta_coalescing_enabled = stream_text_delta_coalescing_enabled + self.stream_text_delta_flush_interval_seconds = stream_text_delta_flush_interval_seconds + self.stream_text_delta_max_chars = stream_text_delta_max_chars self.active_tasks = {} self.stopping = False self.plugin_daemon_http_client = plugin_daemon_http_client @@ -304,6 +317,9 @@ class RunScheduler: layer_providers=self.layer_providers, is_cancelled=is_cancelled, run_timeout_seconds=self.run_timeout_seconds, + stream_text_delta_coalescing_enabled=self.stream_text_delta_coalescing_enabled, + stream_text_delta_flush_interval_seconds=self.stream_text_delta_flush_interval_seconds, + stream_text_delta_max_chars=self.stream_text_delta_max_chars, ) def _discard_active_run(self, run_id: str) -> None: diff --git a/dify-agent/src/dify_agent/runtime/runner.py b/dify-agent/src/dify_agent/runtime/runner.py index b7dcd127df1..b20edcbb74f 100644 --- a/dify-agent/src/dify_agent/runtime/runner.py +++ b/dify-agent/src/dify_agent/runtime/runner.py @@ -7,8 +7,9 @@ policy is validated: - model runs: enter a fresh ``CompositorRun`` (or resume one from a snapshot), pass the current Dify system prompts as run-level instructions, run pydantic-ai with either the current ``run.user_prompts`` or deferred external - tool results, emit raw stream events with agent-message delta annotations, apply - request-level ``on_exit`` signals, and publish a terminal success or failure event; + tool results, emit stream events with bounded text-delta coalescing and + agent-message annotations, apply request-level ``on_exit`` signals, and publish + a terminal success or failure event; The Pydantic AI model is resolved from the active Agenton layer named by ``DIFY_AGENT_MODEL_LAYER_ID``. An optional history layer contributes stored message history only through session state. Once pydantic-ai binds and builds @@ -67,6 +68,11 @@ from dify_agent.runtime.agenton_validation import is_agenton_enter_validation_ru from dify_agent.runtime.compositor_factory import build_pydantic_ai_compositor, create_default_layer_providers from dify_agent.runtime.compaction import build_compaction_capability from dify_agent.runtime_backend import BindingLostError +from dify_agent.runtime.event_coalescer import ( + DEFAULT_TEXT_DELTA_FLUSH_INTERVAL_SECONDS, + DEFAULT_TEXT_DELTA_MAX_CHARS, + coalesce_agent_stream_events, +) from dify_agent.runtime.event_sink import ( RunEventSink, emit_pydantic_ai_event, @@ -187,6 +193,9 @@ class AgentRunRunner: dify_api_http_client: httpx.AsyncClient is_cancelled: Callable[[], bool] run_timeout_seconds: float + stream_text_delta_coalescing_enabled: bool + stream_text_delta_flush_interval_seconds: float + stream_text_delta_max_chars: int _terminal_session_snapshot: CompositorSessionSnapshot | None _terminal_usage: AgentRunUsage | None @@ -201,7 +210,14 @@ class AgentRunRunner: layer_providers: tuple[LayerProviderInput, ...] | None = None, is_cancelled: Callable[[], bool] | None = None, run_timeout_seconds: float = DEFAULT_AGENT_RUN_TIMEOUT_SECONDS, + stream_text_delta_coalescing_enabled: bool = True, + stream_text_delta_flush_interval_seconds: float = DEFAULT_TEXT_DELTA_FLUSH_INTERVAL_SECONDS, + stream_text_delta_max_chars: int = DEFAULT_TEXT_DELTA_MAX_CHARS, ) -> None: + if stream_text_delta_flush_interval_seconds <= 0: + raise ValueError("stream_text_delta_flush_interval_seconds must be positive") + if stream_text_delta_max_chars <= 0: + raise ValueError("stream_text_delta_max_chars must be positive") self.sink = sink self.request = request self.run_id = run_id @@ -210,6 +226,9 @@ class AgentRunRunner: self.layer_providers = layer_providers if layer_providers is not None else create_default_layer_providers() self.is_cancelled = is_cancelled or (lambda: False) self.run_timeout_seconds = run_timeout_seconds + self.stream_text_delta_coalescing_enabled = stream_text_delta_coalescing_enabled + self.stream_text_delta_flush_interval_seconds = stream_text_delta_flush_interval_seconds + self.stream_text_delta_max_chars = stream_text_delta_max_chars self._terminal_session_snapshot = None self._terminal_usage = None @@ -320,7 +339,13 @@ class AgentRunRunner: raise AgentRunValidationError(EMPTY_USER_PROMPTS_ERROR) async def handle_events(_ctx: object, events: AsyncIterable[AgentStreamEvent]) -> None: - async for event in events: + published_events = coalesce_agent_stream_events( + events, + enabled=self.stream_text_delta_coalescing_enabled, + flush_interval_seconds=self.stream_text_delta_flush_interval_seconds, + max_chars=self.stream_text_delta_max_chars, + ) + async for event in published_events: if self.is_cancelled(): raise asyncio.CancelledError text_delta = _extract_agent_message_delta(event) diff --git a/dify-agent/src/dify_agent/server/app.py b/dify-agent/src/dify_agent/server/app.py index 8144c66ae20..4b1d4ce3782 100644 --- a/dify-agent/src/dify_agent/server/app.py +++ b/dify-agent/src/dify_agent/server/app.py @@ -105,6 +105,7 @@ def create_app(settings: ServerSettings | None = None) -> FastAPI: redis, prefix=resolved_settings.redis_prefix, run_retention_seconds=resolved_settings.run_retention_seconds, + run_event_stream_max_length=resolved_settings.run_event_stream_max_length, ) scheduler = RunScheduler( store=store, @@ -112,6 +113,9 @@ def create_app(settings: ServerSettings | None = None) -> FastAPI: dify_api_http_client=dify_api_inner_http_client, shutdown_grace_seconds=resolved_settings.shutdown_grace_seconds, run_timeout_seconds=resolved_settings.run_timeout_seconds, + stream_text_delta_coalescing_enabled=resolved_settings.stream_text_delta_coalescing_enabled, + stream_text_delta_flush_interval_seconds=(resolved_settings.stream_text_delta_flush_interval_ms / 1000), + stream_text_delta_max_chars=resolved_settings.stream_text_delta_max_chars, layer_providers=layer_providers, ) state["store"] = store diff --git a/dify-agent/src/dify_agent/server/settings.py b/dify-agent/src/dify_agent/server/settings.py index 3d27e4439b7..80669d6bf19 100644 --- a/dify-agent/src/dify_agent/server/settings.py +++ b/dify-agent/src/dify_agent/server/settings.py @@ -21,6 +21,10 @@ from dify_agent.agent_stub.protocol.agent_stub import normalize_agent_stub_api_b from dify_agent.agent_stub.server.agent_stub_config import DifyApiAgentStubConfigRequestHandler from dify_agent.agent_stub.server.agent_stub_files import DifyApiAgentStubFileRequestHandler from dify_agent.agent_stub.server.tokens.agent_stub import AgentStubTokenCodec, decode_server_secret_key +from dify_agent.runtime.event_coalescer import ( + DEFAULT_TEXT_DELTA_FLUSH_INTERVAL_SECONDS, + DEFAULT_TEXT_DELTA_MAX_CHARS, +) from dify_agent.runtime.runner import DEFAULT_AGENT_RUN_TIMEOUT_SECONDS from dify_agent.runtime_backend import RuntimeBackendProfile from dify_agent.runtime_backend.e2b import E2B_MAX_ACTIVE_TIMEOUT_SECONDS @@ -32,7 +36,8 @@ from dify_agent.runtime_backend.profile import ( create_runtime_backend_profile, ) -DEFAULT_RUN_RETENTION_SECONDS = 3 * 24 * 60 * 60 +DEFAULT_RUN_RETENTION_SECONDS = 2 * 60 * 60 +DEFAULT_RUN_EVENT_STREAM_MAX_LENGTH = 5000 class ServerSettings(BaseSettings): @@ -42,6 +47,13 @@ class ServerSettings(BaseSettings): redis_prefix: str = "dify-agent" shutdown_grace_seconds: float = 30 run_retention_seconds: int = Field(default=DEFAULT_RUN_RETENTION_SECONDS, ge=1) + run_event_stream_max_length: int = Field(default=DEFAULT_RUN_EVENT_STREAM_MAX_LENGTH, ge=1) + stream_text_delta_coalescing_enabled: bool = True + stream_text_delta_flush_interval_ms: int = Field( + default=int(DEFAULT_TEXT_DELTA_FLUSH_INTERVAL_SECONDS * 1000), + ge=1, + ) + stream_text_delta_max_chars: int = Field(default=DEFAULT_TEXT_DELTA_MAX_CHARS, ge=1) run_timeout_seconds: float = Field(default=DEFAULT_AGENT_RUN_TIMEOUT_SECONDS, gt=0) plugin_daemon_url: str = "http://localhost:5002" plugin_daemon_api_key: str = "" @@ -255,4 +267,4 @@ class ServerSettings(BaseSettings): ) -__all__ = ["DEFAULT_RUN_RETENTION_SECONDS", "ServerSettings"] +__all__ = ["DEFAULT_RUN_EVENT_STREAM_MAX_LENGTH", "DEFAULT_RUN_RETENTION_SECONDS", "ServerSettings"] diff --git a/dify-agent/src/dify_agent/storage/redis_run_store.py b/dify-agent/src/dify_agent/storage/redis_run_store.py index e9f708e22de..a2a61cd9231 100644 --- a/dify-agent/src/dify_agent/storage/redis_run_store.py +++ b/dify-agent/src/dify_agent/storage/redis_run_store.py @@ -35,7 +35,7 @@ from dify_agent.runtime.event_sink import ( terminal_event_status_fields, ) from dify_agent.server.schemas import RunRecord, new_run_id -from dify_agent.server.settings import DEFAULT_RUN_RETENTION_SECONDS +from dify_agent.server.settings import DEFAULT_RUN_EVENT_STREAM_MAX_LENGTH, DEFAULT_RUN_RETENTION_SECONDS from dify_agent.storage.redis_keys import run_cancel_intent_key, run_events_key, run_record_key _TERMINAL_RUN_EVENT_TYPES = {"run_succeeded", "run_failed", "run_cancelled"} @@ -75,7 +75,7 @@ end local ttl = tonumber(ARGV[8]) local updated_record_json = cjson.encode(record) -local event_id = redis.call("XADD", KEYS[2], "*", "payload", ARGV[7]) +local event_id = redis.call("XADD", KEYS[2], "MAXLEN", "~", ARGV[9], "*", "payload", ARGV[7]) redis.call("EXPIRE", KEYS[2], ttl) redis.call("SET", KEYS[1], updated_record_json, "EX", ttl) return {1, ARGV[1], event_id} @@ -100,7 +100,7 @@ if redis.call("EXISTS", KEYS[2]) == 1 then end local ttl = tonumber(ARGV[2]) -redis.call("XADD", KEYS[2], "*", "payload", ARGV[1]) +redis.call("XADD", KEYS[2], "MAXLEN", "1", "*", "payload", ARGV[1]) redis.call("EXPIRE", KEYS[2], ttl) redis.call("EXPIRE", KEYS[1], ttl) redis.call("EXPIRE", KEYS[3], ttl) @@ -135,7 +135,7 @@ end record.error_type = cjson.null local ttl = tonumber(ARGV[5]) -local event_id = redis.call("XADD", KEYS[3], "*", "payload", ARGV[4]) +local event_id = redis.call("XADD", KEYS[3], "MAXLEN", "~", ARGV[6], "*", "payload", ARGV[4]) redis.call("DEL", KEYS[2]) redis.call("EXPIRE", KEYS[3], ttl) redis.call("SET", KEYS[1], cjson.encode(record), "EX", ttl) @@ -147,16 +147,19 @@ class RedisRunStore(RunEventSink): """Async Redis implementation for run records and event logs. ``run_retention_seconds`` is applied to both the run record key and the - per-run Redis stream. Event writes run ``XADD`` and both TTL refreshes in one - Redis transaction so a newly created stream is not left without expiration if - the client is interrupted between commands. Event writes also refresh the - record TTL so long-running runs that keep producing events do not lose their - status record mid-run. + per-run Redis stream. Every public stream write applies an approximate + maximum length before refreshing both TTLs, so active runs cannot grow one + Redis key without bound without paying the CPU cost of exact per-entry + trimming. The transaction also ensures a newly created stream is not left + without expiration if the client is interrupted between commands. + Event writes refresh the record TTL so long-running runs that keep producing + events do not lose their status record mid-run. """ redis: Redis prefix: str run_retention_seconds: int + run_event_stream_max_length: int def __init__( self, @@ -164,12 +167,16 @@ class RedisRunStore(RunEventSink): *, prefix: str = "dify-agent", run_retention_seconds: int = DEFAULT_RUN_RETENTION_SECONDS, + run_event_stream_max_length: int = DEFAULT_RUN_EVENT_STREAM_MAX_LENGTH, ) -> None: if run_retention_seconds <= 0: raise ValueError("run_retention_seconds must be positive") + if run_event_stream_max_length <= 0: + raise ValueError("run_event_stream_max_length must be positive") self.redis = redis self.prefix = prefix self.run_retention_seconds = run_retention_seconds + self.run_event_stream_max_length = run_event_stream_max_length async def create_run(self) -> RunRecord: """Persist a running run record without storing the create request.""" @@ -199,6 +206,8 @@ class RedisRunStore(RunEventSink): _ = pipeline.xadd( events_key, {"payload": payload}, + maxlen=self.run_event_stream_max_length, + approximate=True, ) _ = pipeline.expire(events_key, self.run_retention_seconds) _ = pipeline.expire(run_record_key(self.prefix, event.run_id), self.run_retention_seconds) @@ -226,6 +235,7 @@ class RedisRunStore(RunEventSink): error_type.value if error_type is not None else "", payload, str(self.run_retention_seconds), + str(self.run_event_stream_max_length), ), ) raw_result = await evaluation @@ -316,6 +326,7 @@ class RedisRunStore(RunEventSink): error or "", payload, str(self.run_retention_seconds), + str(self.run_event_stream_max_length), ), ) result = cast(list[object], await evaluation) @@ -385,4 +396,9 @@ def _decode_redis_text(value: object) -> str: return value.decode() if isinstance(value, bytes) else str(value) -__all__ = ["DEFAULT_RUN_RETENTION_SECONDS", "RedisRunStore", "RunNotFoundError"] +__all__ = [ + "DEFAULT_RUN_EVENT_STREAM_MAX_LENGTH", + "DEFAULT_RUN_RETENTION_SECONDS", + "RedisRunStore", + "RunNotFoundError", +] diff --git a/dify-agent/tests/integration/dify_agent/runtime_backend/test_working_environment.py b/dify-agent/tests/integration/dify_agent/runtime_backend/test_working_environment.py index 93a67c2e816..41deb4dfa3d 100644 --- a/dify-agent/tests/integration/dify_agent/runtime_backend/test_working_environment.py +++ b/dify-agent/tests/integration/dify_agent/runtime_backend/test_working_environment.py @@ -34,6 +34,7 @@ async def _run(lease, script: str, *, cwd: str) -> str: env={"HOME": lease.layout.home_dir}, timeout=30.0, max_output_bytes=4096, + mode="stdio", ) assert result.exit_code == 0 assert result.output_complete diff --git a/dify-agent/tests/integration/dify_agent/storage/test_terminal_finalization.py b/dify-agent/tests/integration/dify_agent/storage/test_terminal_finalization.py index af3a77c687d..5bf579bee03 100644 --- a/dify-agent/tests/integration/dify_agent/storage/test_terminal_finalization.py +++ b/dify-agent/tests/integration/dify_agent/storage/test_terminal_finalization.py @@ -153,6 +153,50 @@ def test_success_and_cancel_intent_commit_exactly_one_matching_terminal(redis_ur asyncio.run(scenario()) +def test_event_stream_is_trimmed_and_keeps_the_terminal_event(redis_url: str) -> None: + async def scenario() -> None: + client = Redis.from_url(redis_url) + prefix = f"bounded-events-{uuid4().hex}" + max_length = 100 + store = RedisRunStore( + client, + prefix=prefix, + run_retention_seconds=60, + run_event_stream_max_length=max_length, + ) + run_id: str | None = None + try: + record = await store.create_run() + run_id = record.run_id + for _ in range(1000): + _ = await store.append_event(RunStartedEvent(run_id=record.run_id)) + result = await store.finalize_run( + RunSucceededEvent( + run_id=record.run_id, + data=RunSucceededEventData( + output="done", + session_snapshot=CompositorSessionSnapshot(layers=[]), + ), + ) + ) + + assert result.applied is True + stream_length = await client.xlen(run_events_key(prefix, record.run_id)) + assert max_length <= stream_length <= max_length * 2 + events = await store.get_events(record.run_id, limit=max_length * 2) + assert events.events[-1].type == "run_succeeded" + finally: + if run_id is not None: + await client.delete( + run_record_key(prefix, run_id), + run_events_key(prefix, run_id), + run_cancel_intent_key(prefix, run_id), + ) + await client.aclose() + + asyncio.run(scenario()) + + @pytest.mark.parametrize("terminal_status", ["succeeded", "failed"]) def test_terminal_first_rejects_late_cancellation(redis_url: str, terminal_status: str) -> None: async def scenario() -> None: @@ -303,6 +347,10 @@ def test_non_owner_scheduler_cancellation_stops_owner_runner(redis_url: str) -> def terminal_session_snapshot(self) -> None: return None + @property + def terminal_usage(self) -> None: + return None + async def run(self) -> None: self.started.set() try: diff --git a/dify-agent/tests/local/dify_agent/runtime/test_event_coalescer.py b/dify-agent/tests/local/dify_agent/runtime/test_event_coalescer.py new file mode 100644 index 00000000000..2ebc475e0de --- /dev/null +++ b/dify-agent/tests/local/dify_agent/runtime/test_event_coalescer.py @@ -0,0 +1,227 @@ +import asyncio +from collections.abc import AsyncIterator +from typing import Any + +import pytest +from pydantic_ai.messages import AgentStreamEvent, PartDeltaEvent, PartEndEvent, TextPart, TextPartDelta + +from dify_agent.runtime.event_coalescer import coalesce_agent_stream_events + + +def _delta(content: str, *, index: int = 0, provider_details: dict[str, Any] | None = None) -> PartDeltaEvent: + return PartDeltaEvent( + index=index, + delta=TextPartDelta(content, provider_name="test", provider_details=provider_details), + ) + + +def _part_end(content: str = "done") -> PartEndEvent: + return PartEndEvent(index=0, part=TextPart(content)) + + +async def _events(*events: PartDeltaEvent | PartEndEvent) -> AsyncIterator[PartDeltaEvent | PartEndEvent]: + for event in events: + yield event + + +async def _collect(events: AsyncIterator[AgentStreamEvent]) -> list[AgentStreamEvent]: + return [event async for event in events] + + +def _delta_content(event: AgentStreamEvent) -> str: + assert isinstance(event, PartDeltaEvent) + assert isinstance(event.delta, TextPartDelta) + return event.delta.content_delta + + +def test_coalesces_compatible_text_deltas_and_preserves_non_text_order() -> None: + async def scenario() -> list[AgentStreamEvent]: + return await _collect( + coalesce_agent_stream_events( + _events(_delta("hel"), _delta("lo"), _part_end()), + flush_interval_seconds=10, + max_chars=100, + ) + ) + + result = asyncio.run(scenario()) + + assert len(result) == 2 + assert isinstance(result[0], PartDeltaEvent) + assert isinstance(result[0].delta, TextPartDelta) + assert result[0].delta.content_delta == "hello" + assert isinstance(result[1], PartEndEvent) + + +def test_disabled_coalescing_preserves_each_source_event() -> None: + async def scenario() -> list[AgentStreamEvent]: + return await _collect( + coalesce_agent_stream_events( + _events(_delta("hel"), _delta("lo"), _part_end()), + enabled=False, + flush_interval_seconds=10, + max_chars=100, + ) + ) + + result = asyncio.run(scenario()) + + assert len(result) == 3 + assert [_delta_content(event) for event in result[:2]] == ["hel", "lo"] + assert isinstance(result[2], PartEndEvent) + + +def test_does_not_merge_different_parts_or_provider_details() -> None: + async def scenario() -> list[AgentStreamEvent]: + return await _collect( + coalesce_agent_stream_events( + _events( + _delta("a", index=0), + _delta("b", index=1), + _delta("c", index=1, provider_details={"token": 1}), + ), + flush_interval_seconds=10, + max_chars=100, + ) + ) + + result = asyncio.run(scenario()) + + assert [_delta_content(event) for event in result] == ["a", "b", "c"] + + +def test_flushes_when_character_limit_is_reached() -> None: + async def scenario() -> list[AgentStreamEvent]: + return await _collect( + coalesce_agent_stream_events( + _events(_delta("ab"), _delta("cd"), _delta("ef")), + flush_interval_seconds=10, + max_chars=4, + ) + ) + + result = asyncio.run(scenario()) + + assert [_delta_content(event) for event in result] == ["abcd", "ef"] + + +def test_high_volume_text_deltas_are_reduced_to_size_bounded_batches() -> None: + async def scenario() -> list[AgentStreamEvent]: + async def many_deltas() -> AsyncIterator[AgentStreamEvent]: + for _ in range(20_000): + yield _delta("x") + + return await _collect( + coalesce_agent_stream_events( + many_deltas(), + flush_interval_seconds=10, + max_chars=4096, + ) + ) + + result = asyncio.run(scenario()) + + assert len(result) == 5 + assert sum(len(_delta_content(event)) for event in result) == 20_000 + + +def test_flushes_on_deadline_while_source_is_idle() -> None: + async def scenario() -> tuple[PartDeltaEvent, PartEndEvent]: + release_source = asyncio.Event() + + async def delayed_events() -> AsyncIterator[PartDeltaEvent | PartEndEvent]: + yield _delta("ready") + await release_source.wait() + yield _part_end() + + events = coalesce_agent_stream_events( + delayed_events(), + flush_interval_seconds=0.01, + max_chars=100, + ) + first = await asyncio.wait_for(anext(events), timeout=0.2) + release_source.set() + second = await asyncio.wait_for(anext(events), timeout=0.2) + with pytest.raises(StopAsyncIteration): + _ = await anext(events) + assert isinstance(first, PartDeltaEvent) + assert isinstance(second, PartEndEvent) + return first, second + + first, second = asyncio.run(scenario()) + + assert isinstance(first.delta, TextPartDelta) + assert first.delta.content_delta == "ready" + assert isinstance(second.part, TextPart) + assert second.part.content == "done" + + +def test_flushes_buffer_before_propagating_source_failure() -> None: + async def scenario() -> PartDeltaEvent: + async def failing_events() -> AsyncIterator[AgentStreamEvent]: + yield _delta("partial") + raise RuntimeError("stream failed") + + events = coalesce_agent_stream_events( + failing_events(), + flush_interval_seconds=10, + max_chars=100, + ) + first = await anext(events) + with pytest.raises(RuntimeError, match="stream failed"): + _ = await anext(events) + assert isinstance(first, PartDeltaEvent) + return first + + first = asyncio.run(scenario()) + + assert isinstance(first.delta, TextPartDelta) + assert first.delta.content_delta == "partial" + + +def test_flushes_buffer_before_consumer_cancellation() -> None: + async def scenario() -> list[AgentStreamEvent]: + source_waiting = asyncio.Event() + emitted: list[AgentStreamEvent] = [] + + async def blocked_events() -> AsyncIterator[AgentStreamEvent]: + yield _delta("partial") + source_waiting.set() + await asyncio.Event().wait() + + async def consume() -> None: + async for event in coalesce_agent_stream_events( + blocked_events(), + flush_interval_seconds=10, + max_chars=100, + ): + emitted.append(event) + + task = asyncio.create_task(consume()) + await asyncio.wait_for(source_waiting.wait(), timeout=0.2) + task.cancel() + with pytest.raises(asyncio.CancelledError): + await task + return emitted + + emitted = asyncio.run(scenario()) + + assert len(emitted) == 1 + assert _delta_content(emitted[0]) == "partial" + + +@pytest.mark.parametrize( + ("flush_interval_seconds", "max_chars", "message"), + [(-0.1, 100, "positive"), (0, 100, "positive"), (0.1, 0, "positive")], +) +def test_rejects_invalid_bounds(flush_interval_seconds: float, max_chars: int, message: str) -> None: + async def scenario() -> None: + events = coalesce_agent_stream_events( + _events(_delta("a")), + flush_interval_seconds=flush_interval_seconds, + max_chars=max_chars, + ) + with pytest.raises(ValueError, match=message): + _ = await anext(events) + + asyncio.run(scenario()) diff --git a/dify-agent/tests/local/dify_agent/runtime/test_run_scheduler.py b/dify-agent/tests/local/dify_agent/runtime/test_run_scheduler.py index 45e788d0c7e..7c00424f243 100644 --- a/dify-agent/tests/local/dify_agent/runtime/test_run_scheduler.py +++ b/dify-agent/tests/local/dify_agent/runtime/test_run_scheduler.py @@ -461,7 +461,7 @@ class FinalizeSuccessOnCancellationRunner(SnapshotlessRunner): assert result.applied is True -def test_default_runner_factory_passes_run_timeout_to_runner() -> None: +def test_default_runner_factory_passes_runtime_limits_to_runner() -> None: async def scenario() -> None: store = FakeStore() record = await store.create_run() @@ -471,12 +471,18 @@ def test_default_runner_factory_passes_run_timeout_to_runner() -> None: plugin_daemon_http_client=client, dify_api_http_client=client, run_timeout_seconds=17, + stream_text_delta_coalescing_enabled=False, + stream_text_delta_flush_interval_seconds=0.25, + stream_text_delta_max_chars=2048, ) runner = scheduler._default_runner_factory(record, _request(), is_cancelled=lambda: False) assert isinstance(runner, AgentRunRunner) assert runner.run_timeout_seconds == 17 + assert runner.stream_text_delta_coalescing_enabled is False + assert runner.stream_text_delta_flush_interval_seconds == 0.25 + assert runner.stream_text_delta_max_chars == 2048 asyncio.run(scenario()) diff --git a/dify-agent/tests/local/dify_agent/server/test_app.py b/dify-agent/tests/local/dify_agent/server/test_app.py index 14d49f9f3be..7fa209c8bd1 100644 --- a/dify-agent/tests/local/dify_agent/server/test_app.py +++ b/dify-agent/tests/local/dify_agent/server/test_app.py @@ -69,6 +69,9 @@ class FakeRunScheduler: store: object shutdown_grace_seconds: float run_timeout_seconds: float + stream_text_delta_coalescing_enabled: bool + stream_text_delta_flush_interval_seconds: float + stream_text_delta_max_chars: int layer_providers: tuple[DifyAgentLayerProvider, ...] plugin_daemon_http_client: FakePluginDaemonHttpClient dify_api_http_client: FakePluginDaemonHttpClient @@ -82,11 +85,17 @@ class FakeRunScheduler: dify_api_http_client: FakePluginDaemonHttpClient, shutdown_grace_seconds: float, run_timeout_seconds: float, + stream_text_delta_coalescing_enabled: bool, + stream_text_delta_flush_interval_seconds: float, + stream_text_delta_max_chars: int, layer_providers: tuple[DifyAgentLayerProvider, ...], ) -> None: self.store = store self.shutdown_grace_seconds = shutdown_grace_seconds self.run_timeout_seconds = run_timeout_seconds + self.stream_text_delta_coalescing_enabled = stream_text_delta_coalescing_enabled + self.stream_text_delta_flush_interval_seconds = stream_text_delta_flush_interval_seconds + self.stream_text_delta_max_chars = stream_text_delta_max_chars self.layer_providers = layer_providers self.plugin_daemon_http_client = plugin_daemon_http_client self.dify_api_http_client = dify_api_http_client @@ -203,6 +212,10 @@ def test_create_app_creates_scheduler_and_closes_after_shutdown(monkeypatch: pyt shutdown_grace_seconds=5, run_timeout_seconds=17, run_retention_seconds=7, + run_event_stream_max_length=23, + stream_text_delta_coalescing_enabled=False, + stream_text_delta_flush_interval_ms=250, + stream_text_delta_max_chars=2048, plugin_daemon_url="http://plugin-daemon", plugin_daemon_api_key="daemon-secret", inner_api_url="http://dify-api", @@ -226,6 +239,9 @@ def test_create_app_creates_scheduler_and_closes_after_shutdown(monkeypatch: pyt scheduler = FakeRunScheduler.created[0] assert scheduler.shutdown_grace_seconds == 5 assert scheduler.run_timeout_seconds == 17 + assert scheduler.stream_text_delta_coalescing_enabled is False + assert scheduler.stream_text_delta_flush_interval_seconds == 0.25 + assert scheduler.stream_text_delta_max_chars == 2048 layer_providers = scheduler.layer_providers assert isinstance(layer_providers, tuple) execution_context_provider = next( @@ -284,6 +300,7 @@ def test_create_app_creates_scheduler_and_closes_after_shutdown(monkeypatch: pyt store = scheduler.store assert isinstance(store, RedisRunStore) assert store.run_retention_seconds == 7 + assert store.run_event_stream_max_length == 23 assert any(getattr(route, "path", None) == "/agent-stub/connections" for route in create_app(settings).routes) assert any( getattr(route, "path", None) == "/agent-stub/files/upload-request" for route in create_app(settings).routes diff --git a/dify-agent/tests/local/dify_agent/server/test_settings.py b/dify-agent/tests/local/dify_agent/server/test_settings.py index b94dc033122..465c6a3b304 100644 --- a/dify-agent/tests/local/dify_agent/server/test_settings.py +++ b/dify-agent/tests/local/dify_agent/server/test_settings.py @@ -8,7 +8,11 @@ from pydantic import ValidationError from dify_agent.agent_stub.server.agent_stub_files import DifyApiAgentStubFileRequestHandler from dify_agent.agent_stub.server.tokens.agent_stub import AgentStubTokenCodec -from dify_agent.server.settings import ServerSettings +from dify_agent.server.settings import ( + DEFAULT_RUN_EVENT_STREAM_MAX_LENGTH, + DEFAULT_RUN_RETENTION_SECONDS, + ServerSettings, +) from dify_agent.runtime.runner import DEFAULT_AGENT_RUN_TIMEOUT_SECONDS from dify_agent.runtime_backend.e2b import E2B_MAX_ACTIVE_TIMEOUT_SECONDS, E2BExecutionBindingBackend from dify_agent.runtime_backend.enterprise import EnterpriseExecutionBindingBackend, EnterpriseHomeSnapshotBackend @@ -79,6 +83,53 @@ def test_server_settings_rejects_non_positive_run_timeout() -> None: _ = ServerSettings(run_timeout_seconds=0) +def test_server_settings_defaults_to_bounded_short_lived_run_events( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + monkeypatch.delenv("DIFY_AGENT_RUN_RETENTION_SECONDS", raising=False) + monkeypatch.delenv("DIFY_AGENT_RUN_EVENT_STREAM_MAX_LENGTH", raising=False) + monkeypatch.delenv("DIFY_AGENT_STREAM_TEXT_DELTA_COALESCING_ENABLED", raising=False) + monkeypatch.delenv("DIFY_AGENT_STREAM_TEXT_DELTA_FLUSH_INTERVAL_MS", raising=False) + monkeypatch.delenv("DIFY_AGENT_STREAM_TEXT_DELTA_MAX_CHARS", raising=False) + monkeypatch.chdir(tmp_path) + + settings = ServerSettings() + + assert settings.run_retention_seconds == DEFAULT_RUN_RETENTION_SECONDS == 7200 + assert settings.run_event_stream_max_length == DEFAULT_RUN_EVENT_STREAM_MAX_LENGTH == 5000 + assert settings.stream_text_delta_coalescing_enabled is True + assert settings.stream_text_delta_flush_interval_ms == 100 + assert settings.stream_text_delta_max_chars == 4096 + + +def test_server_settings_reads_run_event_bounds_from_env(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("DIFY_AGENT_RUN_EVENT_STREAM_MAX_LENGTH", "1234") + monkeypatch.setenv("DIFY_AGENT_STREAM_TEXT_DELTA_COALESCING_ENABLED", "false") + monkeypatch.setenv("DIFY_AGENT_STREAM_TEXT_DELTA_FLUSH_INTERVAL_MS", "250") + monkeypatch.setenv("DIFY_AGENT_STREAM_TEXT_DELTA_MAX_CHARS", "2048") + + settings = ServerSettings() + + assert settings.run_event_stream_max_length == 1234 + assert settings.stream_text_delta_coalescing_enabled is False + assert settings.stream_text_delta_flush_interval_ms == 250 + assert settings.stream_text_delta_max_chars == 2048 + + +@pytest.mark.parametrize( + ("field", "value"), + [ + ("run_event_stream_max_length", 0), + ("stream_text_delta_flush_interval_ms", 0), + ("stream_text_delta_max_chars", 0), + ], +) +def test_server_settings_rejects_invalid_run_event_bounds(field: str, value: int) -> None: + with pytest.raises(ValidationError): + _ = ServerSettings(**{field: value}) # pyright: ignore[reportArgumentType] + + def test_server_settings_reads_binding_file_download_command_timeout_from_env( monkeypatch: pytest.MonkeyPatch, ) -> None: diff --git a/dify-agent/tests/local/dify_agent/storage/test_redis_run_store.py b/dify-agent/tests/local/dify_agent/storage/test_redis_run_store.py index a39b960c7cc..68641aa7a10 100644 --- a/dify-agent/tests/local/dify_agent/storage/test_redis_run_store.py +++ b/dify-agent/tests/local/dify_agent/storage/test_redis_run_store.py @@ -24,7 +24,12 @@ from dify_agent.protocol.schemas import ( ) from dify_agent.runtime.cancellation import RunCancellationIntent from dify_agent.runtime.event_sink import RunFinalizationResult -from dify_agent.storage.redis_run_store import DEFAULT_RUN_RETENTION_SECONDS, RedisRunStore, RunNotFoundError +from dify_agent.storage.redis_run_store import ( + DEFAULT_RUN_EVENT_STREAM_MAX_LENGTH, + DEFAULT_RUN_RETENTION_SECONDS, + RedisRunStore, + RunNotFoundError, +) class FakeRedis: @@ -48,18 +53,34 @@ class FakeRedis: self.commands.append(("get", key)) return self.values.get(key) - async def xadd(self, key: str, fields: Mapping[str, object]) -> str: - self.commands.append(("xadd", key, dict(fields))) - return self._append_stream_entry(key, fields) + async def xadd( + self, + key: str, + fields: Mapping[str, object], + *, + maxlen: int | None = None, + approximate: bool = True, + ) -> str: + self.commands.append(("xadd", key, dict(fields), maxlen, approximate)) + return self._append_stream_entry(key, fields, maxlen=maxlen) def pipeline(self, transaction: bool = True, shard_hint: str | None = None) -> "FakeRedisPipeline": self.commands.append(("pipeline", transaction, shard_hint)) return FakeRedisPipeline(self) - def _append_stream_entry(self, key: str, fields: Mapping[str, object]) -> str: + def _append_stream_entry( + self, + key: str, + fields: Mapping[str, object], + *, + maxlen: int | None = None, + ) -> str: entries = self.streams.setdefault(key, []) - event_id = f"{len(entries) + 1}-0" + next_sequence = self._stream_id_value(entries[-1][0])[0] + 1 if entries else 1 + event_id = f"{next_sequence}-0" entries.append((event_id, dict(fields))) + if maxlen is not None and len(entries) > maxlen: + del entries[: len(entries) - maxlen] self.stream_changed.set() return event_id @@ -137,9 +158,16 @@ class FakeRedisPipeline: async def __aexit__(self, exc_type: object, exc: object, traceback: object) -> None: del exc_type, exc, traceback - def xadd(self, key: str, fields: Mapping[str, object]) -> "FakeRedisPipeline": - self.redis.commands.append(("xadd", key, dict(fields))) - self.results.append(self.redis._append_stream_entry(key, fields)) + def xadd( + self, + key: str, + fields: Mapping[str, object], + *, + maxlen: int | None = None, + approximate: bool = True, + ) -> "FakeRedisPipeline": + self.redis.commands.append(("xadd", key, dict(fields), maxlen, approximate)) + self.results.append(self.redis._append_stream_entry(key, fields, maxlen=maxlen)) return self def expire(self, key: str, seconds: int) -> "FakeRedisPipeline": @@ -184,6 +212,15 @@ def test_create_run_writes_running_record_without_job_queue_and_with_retention() assert "request" not in str(redis.commands[0][2]) +@pytest.mark.parametrize( + "kwargs", + [{"run_retention_seconds": 0}, {"run_event_stream_max_length": 0}], +) +def test_rejects_non_positive_run_storage_bounds(kwargs: dict[str, int]) -> None: + with pytest.raises(ValueError, match="must be positive"): + _ = RedisRunStore(FakeRedis(), **kwargs) # pyright: ignore[reportArgumentType] + + def test_get_run_accepts_legacy_record_without_error_type() -> None: redis = FakeRedis() store = RedisRunStore(redis, prefix="test") # pyright: ignore[reportArgumentType] @@ -223,6 +260,7 @@ def test_request_cancellation_maps_eval_result_and_arguments() -> None: assert intent_payload["reason"] == "workflow_aborted" assert intent_payload["message"] == "workflow stopped" assert eval_command[7] == "60" + assert '"MAXLEN", "1"' in cast(str, eval_command[1]) def test_finalize_cancellation_maps_eval_result_and_arguments() -> None: @@ -262,6 +300,8 @@ def test_finalize_cancellation_maps_eval_result_and_arguments() -> None: assert payload["data"]["usage"]["completion_tokens"] == 8 assert payload["data"]["usage"]["total_tokens"] == 21 assert eval_command[10] == "60" + assert eval_command[11] == str(DEFAULT_RUN_EVENT_STREAM_MAX_LENGTH) + assert '"MAXLEN", "~", ARGV[6]' in cast(str, eval_command[1]) def test_finalize_failed_run_maps_eval_result_and_arguments() -> None: @@ -290,6 +330,9 @@ def test_finalize_failed_run_maps_eval_result_and_arguments() -> None: assert eval_command[8:12] == ("1", "model failed", "1", "agent_run_limit_exceeded") payload = json.loads(cast(str, eval_command[12])) assert payload["data"]["error_type"] == "agent_run_limit_exceeded" + assert eval_command[13] == str(DEFAULT_RUN_RETENTION_SECONDS) + assert eval_command[14] == str(DEFAULT_RUN_EVENT_STREAM_MAX_LENGTH) + assert '"MAXLEN", "~", ARGV[9]' in cast(str, eval_command[1]) def test_request_cancellation_raises_when_record_is_missing() -> None: @@ -368,6 +411,7 @@ def test_append_event_serializes_typed_event_without_id_and_expires_run_keys() - assert isinstance(fields, dict) assert '"id"' not in str(fields["payload"]) assert '"type":"run_started"' in str(fields["payload"]) + assert xadd_commands[0][3:] == (DEFAULT_RUN_EVENT_STREAM_MAX_LENGTH, True) expire_commands = {command for command in redis.commands if command[0] == "expire"} assert expire_commands == { ("expire", "test:runs:run-1:events", 60), @@ -376,6 +420,26 @@ def test_append_event_serializes_typed_event_without_id_and_expires_run_keys() - assert ("execute",) in redis.commands +def test_append_event_keeps_only_the_configured_number_of_entries() -> None: + redis = FakeRedis() + store = RedisRunStore( + redis, # pyright: ignore[reportArgumentType] + prefix="test", + run_retention_seconds=60, + run_event_stream_max_length=2, + ) + + async def scenario() -> None: + for _ in range(3): + _ = await store.append_event(RunStartedEvent(run_id="run-1")) + + asyncio.run(scenario()) + + assert len(redis.streams["test:runs:run-1:events"]) == 2 + xadd_commands = [command for command in redis.commands if command[0] == "xadd"] + assert all(command[3:] == (2, True) for command in xadd_commands) + + def test_get_events_round_trips_run_succeeded_output_and_session_snapshot() -> None: redis = FakeRedis() store = RedisRunStore(redis, prefix="test", run_retention_seconds=60) # pyright: ignore[reportArgumentType] diff --git a/docker/envs/core-services/dify-agent.env.example b/docker/envs/core-services/dify-agent.env.example index e4ab19342a3..c7ac2458c4e 100644 --- a/docker/envs/core-services/dify-agent.env.example +++ b/docker/envs/core-services/dify-agent.env.example @@ -8,7 +8,11 @@ AGENT_BACKEND_BASE_URL=http://agent_backend:5050 DIFY_AGENT_REDIS_URL= DIFY_AGENT_REDIS_PREFIX=dify-agent DIFY_AGENT_SHUTDOWN_GRACE_SECONDS=30 -DIFY_AGENT_RUN_RETENTION_SECONDS=259200 +DIFY_AGENT_RUN_RETENTION_SECONDS=7200 +DIFY_AGENT_RUN_EVENT_STREAM_MAX_LENGTH=5000 +DIFY_AGENT_STREAM_TEXT_DELTA_COALESCING_ENABLED=true +DIFY_AGENT_STREAM_TEXT_DELTA_FLUSH_INTERVAL_MS=100 +DIFY_AGENT_STREAM_TEXT_DELTA_MAX_CHARS=4096 # Internal deadline for the Agent Backend model/tool loop. The effective run # limit is whichever expires first: this value or the relevant API/Workflow # outer execution limit. To allow every path to run for one hour, also set From 8e064ffe98daa81c5b31bafe7b10dfea3355cc69 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Tue, 1 Sep 2026 06:57:38 +0000 Subject: [PATCH 18/33] chore: upgrade Node.js runtime to 24 LTS (#41573) --- .devcontainer/devcontainer.json | 4 +- .github/actions/setup-web/action.yml | 2 +- .github/labeler.yml | 1 - .github/workflows/main-ci.yml | 3 - .github/workflows/post-merge.yml | 1 - .github/workflows/style.yml | 2 - .github/workflows/tool-test-sdks.yaml | 2 +- .nvmrc | 1 - api/Dockerfile | 7 +- cli/AGENTS.md | 2 +- cli/package.json | 2 +- cli/vite.config.ts | 2 +- cli/vitest.e2e.config.ts | 2 +- dify-agent-runtime/docker/Dockerfile | 2 +- package.json | 4 +- packages/dev-proxy/package.json | 2 +- packages/dev-proxy/vite.config.ts | 2 +- .../vite.config.ts | 2 +- pnpm-lock.yaml | 90 +++++++++---------- web/Dockerfile | 3 +- web/Dockerfile.dockerignore | 1 - web/README.md | 4 +- 22 files changed, 61 insertions(+), 80 deletions(-) delete mode 100644 .nvmrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 3998a69c36a..05712d24189 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -12,7 +12,7 @@ "features": { "ghcr.io/devcontainers/features/node:1": { "nodeGypDependencies": true, - "version": "lts" + "version": "24.20.0" }, "ghcr.io/devcontainers-extra/features/npm-package:1": { "package": "typescript", @@ -46,4 +46,4 @@ // Configure tool-specific properties. // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. -} \ No newline at end of file +} diff --git a/.github/actions/setup-web/action.yml b/.github/actions/setup-web/action.yml index 68c330d0e2c..dbc20df50af 100644 --- a/.github/actions/setup-web/action.yml +++ b/.github/actions/setup-web/action.yml @@ -11,6 +11,6 @@ runs: - name: Setup Vite+ uses: voidzero-dev/setup-vp@1b32467adbe183473499fd9d5d372c3ed9641754 # v1.18.0 with: - node-version-file: .nvmrc + node-version-file: package.json cache: true run-install: true diff --git a/.github/labeler.yml b/.github/labeler.yml index c2ea8873781..911aa34e37a 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -6,7 +6,6 @@ web: - 'package.json' - 'pnpm-lock.yaml' - 'pnpm-workspace.yaml' - - '.nvmrc' e2e: - changed-files: diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index 7ad86aa41b8..6023aa7fae2 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -82,7 +82,6 @@ jobs: - 'pnpm-workspace.yaml' - 'lint.config.ts' - '.npmrc' - - '.nvmrc' - '.github/workflows/cli-tests.yml' - '.github/actions/setup-web/**' web: @@ -91,7 +90,6 @@ jobs: - 'package.json' - 'pnpm-lock.yaml' - 'pnpm-workspace.yaml' - - '.nvmrc' - '.github/workflows/main-ci.yml' - '.github/workflows/web-tests.yml' - '.github/actions/setup-web/**' @@ -105,7 +103,6 @@ jobs: - 'package.json' - 'pnpm-lock.yaml' - 'pnpm-workspace.yaml' - - '.nvmrc' - 'docker/docker-compose.middleware.yaml' - 'docker/envs/middleware.env.example' - '.github/workflows/web-e2e.yml' diff --git a/.github/workflows/post-merge.yml b/.github/workflows/post-merge.yml index de1fce7977c..8f410b887be 100644 --- a/.github/workflows/post-merge.yml +++ b/.github/workflows/post-merge.yml @@ -39,7 +39,6 @@ jobs: - 'e2e/tsx-register.js' - 'package.json' - 'pnpm-lock.yaml' - - '.nvmrc' - '.github/workflows/post-merge.yml' - '.github/workflows/web-e2e.yml' - '.github/actions/setup-web/**' diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index e2dc5504205..ca9d93d136e 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -114,7 +114,6 @@ jobs: pnpm-workspace.yaml knip.config.ts scripts/check-web-production-unused-after-knip-fix.mjs - .nvmrc .github/workflows/style.yml .github/actions/setup-web/** @@ -170,7 +169,6 @@ jobs: package.json pnpm-lock.yaml pnpm-workspace.yaml - .nvmrc vite.config.ts lint.config.ts eslint.config.mjs diff --git a/.github/workflows/tool-test-sdks.yaml b/.github/workflows/tool-test-sdks.yaml index 2d0133131eb..e6ab2459edf 100644 --- a/.github/workflows/tool-test-sdks.yaml +++ b/.github/workflows/tool-test-sdks.yaml @@ -31,7 +31,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 22 + node-version-file: package.json cache: '' cache-dependency-path: 'pnpm-lock.yaml' diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index 2bd5a0a98a3..00000000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -22 diff --git a/api/Dockerfile b/api/Dockerfile index 86eef5d329f..7c9b5d1a158 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -53,15 +53,14 @@ WORKDIR /app/api # Create non-root user ARG dify_uid=1001 -ARG NODE_MAJOR=22 -ARG NODE_PACKAGE_VERSION=22.21.0-1nodesource1 +ARG NODE_PACKAGE_VERSION=24.20.0-1nodesource1 ARG NODESOURCE_KEY_FPR=6F71F525282841EEDAF851B42F59B5F99B1BE0B4 RUN groupadd -r -g ${dify_uid} dify && \ useradd -r -u ${dify_uid} -g ${dify_uid} -s /bin/bash dify && \ chown -R dify:dify /app -RUN \ - apt-get update \ +RUN NODE_MAJOR="${NODE_PACKAGE_VERSION%%.*}" \ + && apt-get update \ && apt-get install -y --no-install-recommends \ ca-certificates \ curl \ diff --git a/cli/AGENTS.md b/cli/AGENTS.md index 2b579f401ff..322325d7c5d 100644 --- a/cli/AGENTS.md +++ b/cli/AGENTS.md @@ -1,6 +1,6 @@ # AGENTS.md — difyctl (TypeScript CLI) -This package is the Node 22+, ESM TypeScript implementation of `difyctl`. Development also requires the Bun version pinned in `.bun-version`; command-tree generation and the `dev`, `test`, and `build` pre-scripts invoke it. Read [`ARD.md`] before adding a command or changing shared CLI infrastructure. Read `src/commands/AGENTS.md` for command-folder and registry rules. +This package is the Node 24+, ESM TypeScript implementation of `difyctl`. Development also requires the Bun version pinned in `.bun-version`; command-tree generation and the `dev`, `test`, and `build` pre-scripts invoke it. Read [`ARD.md`] before adding a command or changing shared CLI infrastructure. Read `src/commands/AGENTS.md` for command-folder and registry rules. ## Architecture Boundaries diff --git a/cli/package.json b/cli/package.json index 1c49f13137a..a2b750adb5f 100644 --- a/cli/package.json +++ b/cli/package.json @@ -66,7 +66,7 @@ "vitest": "catalog:" }, "engines": { - "node": "^22.22.1" + "node": "^24.20.0" }, "difyctl": { "channel": "stable", diff --git a/cli/vite.config.ts b/cli/vite.config.ts index 85b0e264cbe..451c0312d63 100644 --- a/cli/vite.config.ts +++ b/cli/vite.config.ts @@ -22,7 +22,7 @@ export default defineConfig({ sourcemap: true, treeshake: false, outDir: 'dist', - target: 'node22', + target: 'node24', define: { __DIFYCTL_VERSION__: JSON.stringify(buildInfo.version), __DIFYCTL_COMMIT__: JSON.stringify(buildInfo.commit), diff --git a/cli/vitest.e2e.config.ts b/cli/vitest.e2e.config.ts index 5ea77e18c1e..bc56c232f9c 100644 --- a/cli/vitest.e2e.config.ts +++ b/cli/vitest.e2e.config.ts @@ -43,7 +43,7 @@ export default defineConfig({ entry: ['src/index.ts'], format: ['esm'], outDir: 'dist', - target: 'node22', + target: 'node24', define: { __DIFYCTL_VERSION__: JSON.stringify(buildInfo.version), __DIFYCTL_COMMIT__: JSON.stringify(buildInfo.commit), diff --git a/dify-agent-runtime/docker/Dockerfile b/dify-agent-runtime/docker/Dockerfile index f8d38413e94..a8a25659e02 100644 --- a/dify-agent-runtime/docker/Dockerfile +++ b/dify-agent-runtime/docker/Dockerfile @@ -20,7 +20,7 @@ RUN CGO_ENABLED=0 go build -o /bin/shellctl ./cmd/shellctl && \ # ── Runtime stage ──────────────────────────────────────────────────────────── FROM python:3.12-slim-bookworm AS production -ARG NODE_VERSION=22.22.1 +ARG NODE_VERSION=24.20.0 ARG PNPM_VERSION=11.9.0 ARG UV_VERSION=0.8.9 diff --git a/package.json b/package.json index a1efeaf28b9..68ec273fbba 100644 --- a/package.json +++ b/package.json @@ -56,12 +56,12 @@ "devEngines": { "runtime": { "name": "node", - "version": "^22.22.1", + "version": "24.20.0", "onFail": "download" } }, "engines": { - "node": "^22.22.1" + "node": "^24.20.0" }, "packageManager": "pnpm@11.25.0" } diff --git a/packages/dev-proxy/package.json b/packages/dev-proxy/package.json index d249d0cf50f..ebdec46de78 100644 --- a/packages/dev-proxy/package.json +++ b/packages/dev-proxy/package.json @@ -39,6 +39,6 @@ "vitest": "catalog:" }, "engines": { - "node": "^22.22.1" + "node": "^24.20.0" } } diff --git a/packages/dev-proxy/vite.config.ts b/packages/dev-proxy/vite.config.ts index 2208c5108a6..d459331d695 100644 --- a/packages/dev-proxy/vite.config.ts +++ b/packages/dev-proxy/vite.config.ts @@ -11,7 +11,7 @@ export default defineConfig({ outDir: 'dist', platform: 'node', sourcemap: true, - target: 'node22', + target: 'node24', treeshake: true, }, test: { diff --git a/packages/migrate-no-unchecked-indexed-access/vite.config.ts b/packages/migrate-no-unchecked-indexed-access/vite.config.ts index ac4aed1a064..2e167da96a0 100644 --- a/packages/migrate-no-unchecked-indexed-access/vite.config.ts +++ b/packages/migrate-no-unchecked-indexed-access/vite.config.ts @@ -11,7 +11,7 @@ export default defineConfig({ outDir: 'dist', platform: 'node', sourcemap: true, - target: 'node22', + target: 'node24', treeshake: true, }, }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca6b1e758eb..429790217f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -730,8 +730,8 @@ importers: specifier: 'catalog:' version: 6.34.0 node: - specifier: runtime:^22.22.1 - version: runtime:22.23.2 + specifier: runtime:24.20.0 + version: runtime:24.20.0 typescript: specifier: 'catalog:' version: '@typescript/typescript6@6.0.2' @@ -7398,7 +7398,7 @@ packages: resolution: {integrity: sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==} engines: {node: '>=18'} - node@runtime:22.23.2: + node@runtime:24.20.0: resolution: type: variations variants: @@ -7406,9 +7406,9 @@ packages: archive: tarball bin: node: bin/node - integrity: sha256-d8YwMv1KuOmOCufbnXnjlmWeBK+Vpe344cqGnkJsBKU= + integrity: sha256-FLW9etQKtfRxX7Ti+WSFjYINlCX+KR09ZfmJJua4nB4= type: binary - url: https://nodejs.org/download/release/v22.23.2/node-v22.23.2-aix-ppc64.tar.gz + url: https://nodejs.org/download/release/v24.20.0/node-v24.20.0-aix-ppc64.tar.gz targets: - cpu: ppc64 os: aix @@ -7416,9 +7416,9 @@ packages: archive: tarball bin: node: bin/node - integrity: sha256-YRMPOUwWMNIR3VCuzENT03lIDzbTrJE82F27oa7VhcY= + integrity: sha256-QOVgfl7LPbkZJyN3baLXXZZiYPx0p6nnMcG9Z92pa8g= type: binary - url: https://nodejs.org/download/release/v22.23.2/node-v22.23.2-darwin-arm64.tar.gz + url: https://nodejs.org/download/release/v24.20.0/node-v24.20.0-darwin-arm64.tar.gz targets: - cpu: arm64 os: darwin @@ -7426,9 +7426,9 @@ packages: archive: tarball bin: node: bin/node - integrity: sha256-WOmQIsL/iTlVdsx/1NmM6iS7aAgUddX4i4Ae6HKfsCY= + integrity: sha256-nlsmRM8Qe++2rvymdrltMpa8EBOAlvAi7TeNYjPtgfQ= type: binary - url: https://nodejs.org/download/release/v22.23.2/node-v22.23.2-darwin-x64.tar.gz + url: https://nodejs.org/download/release/v24.20.0/node-v24.20.0-darwin-x64.tar.gz targets: - cpu: x64 os: darwin @@ -7436,9 +7436,9 @@ packages: archive: tarball bin: node: bin/node - integrity: sha256-ATtZz9KBlwOm9KFKuJH8RvwqTj9bzZLeP7SSm0PjWzA= + integrity: sha256-NRVgPiSHh5o5vHVxbxoq/9AnUAxkulDoRc9yyzMhkBM= type: binary - url: https://nodejs.org/download/release/v22.23.2/node-v22.23.2-linux-arm64.tar.gz + url: https://nodejs.org/download/release/v24.20.0/node-v24.20.0-linux-arm64.tar.gz targets: - cpu: arm64 os: linux @@ -7446,19 +7446,9 @@ packages: archive: tarball bin: node: bin/node - integrity: sha256-Ki9Z64/Z3sJ7O+4XxykTHR/T5tmUPUefEVbOOK+M1Zk= + integrity: sha256-pzSzbI0dFs5FXA65wymwfdEhwshVQ1UGSphhvJbDrcw= type: binary - url: https://nodejs.org/download/release/v22.23.2/node-v22.23.2-linux-armv7l.tar.gz - targets: - - cpu: armv7l - os: linux - - resolution: - archive: tarball - bin: - node: bin/node - integrity: sha256-ZciqnmRxlvMNNWS7ayATBrG1IPcvDdMnjaMmAUoBg3k= - type: binary - url: https://nodejs.org/download/release/v22.23.2/node-v22.23.2-linux-ppc64le.tar.gz + url: https://nodejs.org/download/release/v24.20.0/node-v24.20.0-linux-ppc64le.tar.gz targets: - cpu: ppc64le os: linux @@ -7466,9 +7456,9 @@ packages: archive: tarball bin: node: bin/node - integrity: sha256-mGuEQbHRoBxw7XELqyAXQf0NGW0wKn5DOP79XhwQ5h4= + integrity: sha256-2MIktG7wHweKUj2bfbeSgjBvrigemGoVotv/6UfFMB4= type: binary - url: https://nodejs.org/download/release/v22.23.2/node-v22.23.2-linux-s390x.tar.gz + url: https://nodejs.org/download/release/v24.20.0/node-v24.20.0-linux-s390x.tar.gz targets: - cpu: s390x os: linux @@ -7476,9 +7466,20 @@ packages: archive: tarball bin: node: bin/node - integrity: sha256-spSlVuY51kM4gjkg5YZsIcAnQXQtLhUp7hoiXB7JJSo= + integrity: sha256-1ubRu7mtSssW1YC4m+ipyafkkJJuk708MKINRuFSv4o= type: binary - url: https://nodejs.org/download/release/v22.23.2/node-v22.23.2-linux-x64.tar.gz + url: https://nodejs.org/download/release/v24.20.0/node-v24.20.0-linux-x64-musl.tar.gz + targets: + - cpu: x64 + os: linux + libc: musl + - resolution: + archive: tarball + bin: + node: bin/node + integrity: sha256-hV1YH4pOsagRfjQm3iX+AncFkv68+zE2mu4f+/7p6Ow= + type: binary + url: https://nodejs.org/download/release/v24.20.0/node-v24.20.0-linux-x64.tar.gz targets: - cpu: x64 os: linux @@ -7486,10 +7487,10 @@ packages: archive: zip bin: node: node.exe - integrity: sha256-/sAlptoxdX47avhMWhYo6dOEQsqZohYQkdePL8+jXvM= - prefix: node-v22.23.2-win-arm64 + integrity: sha256-McZ5l0TeilRgFkMJgEDGjDaX5WyU5AfWHQ5fpfNBkdc= + prefix: node-v24.20.0-win-arm64 type: binary - url: https://nodejs.org/download/release/v22.23.2/node-v22.23.2-win-arm64.zip + url: https://nodejs.org/download/release/v24.20.0/node-v24.20.0-win-arm64.zip targets: - cpu: arm64 os: win32 @@ -7497,31 +7498,20 @@ packages: archive: zip bin: node: node.exe - integrity: sha256-EXe0E3ulrapWNUrkDxCAx0UOiuCc7LR9pFnRxSrJn5c= - prefix: node-v22.23.2-win-x64 + integrity: sha256-bKyf+8qPakcJHktcdy4GBgScOHHLZ9kAwM7d5jDlRbo= + prefix: node-v24.20.0-win-x64 type: binary - url: https://nodejs.org/download/release/v22.23.2/node-v22.23.2-win-x64.zip + url: https://nodejs.org/download/release/v24.20.0/node-v24.20.0-win-x64.zip targets: - cpu: x64 os: win32 - - resolution: - archive: zip - bin: - node: node.exe - integrity: sha256-clyeK90cIBa0HJlagfT6Ns5OLuVlt0Vdj4iRgnJ99kc= - prefix: node-v22.23.2-win-x86 - type: binary - url: https://nodejs.org/download/release/v22.23.2/node-v22.23.2-win-x86.zip - targets: - - cpu: x86 - os: win32 - resolution: archive: tarball bin: node: bin/node - integrity: sha256-t6GiscfHbkdVDxd2RnaTmEDgpktNBL3TdaSsFLzKqNg= + integrity: sha256-LIxQfMsPIIEtlSa6jKRUsWUqre9o/IutBvB/sRIt0e8= type: binary - url: https://unofficial-builds.nodejs.org/download/release/v22.23.2/node-v22.23.2-linux-arm64-musl.tar.gz + url: https://unofficial-builds.nodejs.org/download/release/v24.20.0/node-v24.20.0-linux-arm64-musl.tar.gz targets: - cpu: arm64 os: linux @@ -7530,14 +7520,14 @@ packages: archive: tarball bin: node: bin/node - integrity: sha256-OW4R7mCesuXLmQ8EXE0DeqR7LCR/POywHFwWLjP/qa8= + integrity: sha256-muE5n+9L2JkOFXc84TJ7M2oguel9jHVJ9PQspzxD9WI= type: binary - url: https://unofficial-builds.nodejs.org/download/release/v22.23.2/node-v22.23.2-linux-x64-musl.tar.gz + url: https://unofficial-builds.nodejs.org/download/release/v24.20.0/node-v24.20.0-linux-x64-musl.tar.gz targets: - cpu: x64 os: linux libc: musl - version: 22.23.2 + version: 24.20.0 hasBin: true normalize-package-data@8.0.0: @@ -15043,7 +15033,7 @@ snapshots: node-releases@2.0.54: {} - node@runtime:22.23.2: {} + node@runtime:24.20.0: {} normalize-package-data@8.0.0: dependencies: diff --git a/web/Dockerfile b/web/Dockerfile index 9ddb06b8ed5..b60983d5665 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,5 +1,6 @@ # base image -FROM node:22.22.1-alpine AS base +ARG NODE_VERSION=24.20.0 +FROM node:${NODE_VERSION}-alpine AS base LABEL maintainer="takatost@gmail.com" # if you located in China, you can use aliyun mirror to speed up diff --git a/web/Dockerfile.dockerignore b/web/Dockerfile.dockerignore index 115f4303fa9..4a75679e91c 100644 --- a/web/Dockerfile.dockerignore +++ b/web/Dockerfile.dockerignore @@ -2,7 +2,6 @@ !package.json !pnpm-lock.yaml !pnpm-workspace.yaml -!.nvmrc !web/ !web/** !e2e/ diff --git a/web/README.md b/web/README.md index cd4cc161b1e..cb5abb83bc4 100644 --- a/web/README.md +++ b/web/README.md @@ -6,7 +6,7 @@ This is a [Next.js] application with [vinext] as the default local development s ### Run by source code -The required Node.js and pnpm versions are pinned by the repository root `.nvmrc` and `packageManager` field. [Vite+] is also available for repository checks and tests; use its official documentation as the installation reference. +The required Node.js and pnpm versions are pinned by the repository root `devEngines.runtime` and `packageManager` fields. [Vite+] is also available for repository checks and tests; use its official documentation as the installation reference. - [Node.js] - [pnpm] @@ -20,7 +20,7 @@ pnpm install ``` > [!NOTE] -> JavaScript dependencies are managed by the workspace files at the repository root: `package.json`, `pnpm-lock.yaml`, `pnpm-workspace.yaml`, and `.nvmrc`. +> JavaScript dependencies are managed by the workspace files at the repository root: `package.json`, `pnpm-lock.yaml`, and `pnpm-workspace.yaml`. > Install dependencies and run the commands below from the repository root. Then, configure the environment variables. From 27c2f058febe50cb5c6a54375ef392bf20cd53d4 Mon Sep 17 00:00:00 2001 From: QuantumGhost Date: Tue, 1 Sep 2026 07:52:07 +0000 Subject: [PATCH 19/33] fix(web): publish workflow before creating tool (#41528) --- .../__tests__/features-wrapper.spec.tsx | 23 +++++ .../app-publisher/__tests__/index.spec.tsx | 93 ++++++++++++++++++- .../app/app-publisher/features-wrapper.tsx | 5 +- .../app-publisher/publisher-content/index.tsx | 2 +- .../use-publish-controller.ts | 76 +++++++++------ web/app/components/app/app-publisher/types.ts | 11 ++- .../__tests__/use-configure-button.spec.ts | 41 ++++++++ .../hooks/use-configure-button.ts | 3 +- .../__tests__/features-trigger.spec.tsx | 32 +++++++ .../workflow-header/features-trigger.tsx | 11 ++- 10 files changed, 258 insertions(+), 39 deletions(-) diff --git a/web/app/components/app/app-publisher/__tests__/features-wrapper.spec.tsx b/web/app/components/app/app-publisher/__tests__/features-wrapper.spec.tsx index c51ab48015c..bc074e482ea 100644 --- a/web/app/components/app/app-publisher/__tests__/features-wrapper.spec.tsx +++ b/web/app/components/app/app-publisher/__tests__/features-wrapper.spec.tsx @@ -39,6 +39,12 @@ vi.mock('@/app/components/app/app-publisher', () => ({ + @@ -107,6 +113,23 @@ describe('FeaturesWrappedAppPublisher', () => { }) }) + it('should pass publish notification options through to onPublish', async () => { + render( + , + ) + + fireEvent.click(screen.getByText('publish-silently-through-wrapper')) + + await waitFor(() => { + expect(mockOnPublish).toHaveBeenCalledWith(undefined, mockFeatures, { + showSuccessToast: false, + }) + }) + }) + it('should restore published features after confirmation', async () => { render( ({ })) const mockPublishToCreatorsPlatform = vi.fn() +const mockCreateWorkflowToolProvider = vi.fn() vi.mock('@/service/apps', () => ({ publishToCreatorsPlatform: (...args: unknown[]) => mockPublishToCreatorsPlatform(...args), })) +vi.mock('@/service/tools', () => ({ + createWorkflowToolProvider: (...args: unknown[]) => mockCreateWorkflowToolProvider(...args), + saveWorkflowToolProvider: vi.fn(), +})) + vi.mock('@/service/use-workflow', () => ({ useAppWorkflow: () => ({ data: mockPublishedWorkflow, @@ -167,9 +173,26 @@ vi.mock('@/app/components/base/amplitude', () => ({ })) vi.mock('@/app/components/tools/workflow-tool', () => ({ - WorkflowToolDrawer: ({ onHide }: { onHide: () => void }) => ( + WorkflowToolDrawer: ({ + onCreate, + onHide, + }: { + onCreate?: (payload: Record) => void + onHide: () => void + }) => (
workflow tool drawer + @@ -818,6 +841,74 @@ describe('AppPublisher', () => { expect(screen.getByRole('dialog', { name: 'Workflow tool drawer' })).toBeInTheDocument() }) + it('should show one success toast when automatically publishing a workflow tool', async () => { + mockAppDetail = { + ...mockAppDetail, + mode: AppModeEnum.WORKFLOW, + } + mockOnPublish.mockImplementation(async (_params, options?: { showSuccessToast?: boolean }) => { + if (options?.showSuccessToast !== false) mockToastSuccess('common.api.actionSuccess') + }) + mockCreateWorkflowToolProvider.mockResolvedValue({}) + + render() + + fireEvent.click(screen.getByText(/(?:^|\.)common\.publish(?=$|:)/)) + fireEvent.click(screen.getByText('publisher-workflow-tool')) + fireEvent.click(screen.getByRole('button', { name: 'create-workflow-tool' })) + + await waitFor(() => { + expect(mockCreateWorkflowToolProvider).toHaveBeenCalledOnce() + }) + expect(mockOnPublish).toHaveBeenCalledWith(undefined, { showSuccessToast: false }) + expect(mockToastSuccess).toHaveBeenCalledOnce() + }) + + it('should not show a success toast when workflow tool creation fails after publishing', async () => { + mockAppDetail = { + ...mockAppDetail, + mode: AppModeEnum.WORKFLOW, + } + mockOnPublish.mockImplementation(async (_params, options?: { showSuccessToast?: boolean }) => { + if (options?.showSuccessToast !== false) mockToastSuccess('common.api.actionSuccess') + }) + mockCreateWorkflowToolProvider.mockRejectedValue(new Error('create failed')) + + render() + + fireEvent.click(screen.getByText(/(?:^|\.)common\.publish(?=$|:)/)) + fireEvent.click(screen.getByText('publisher-workflow-tool')) + fireEvent.click(screen.getByRole('button', { name: 'create-workflow-tool' })) + + await waitFor(() => { + expect(mockToastError).toHaveBeenCalledWith('create failed') + }) + expect(mockOnPublish).toHaveBeenCalledWith(undefined, { showSuccessToast: false }) + expect(mockToastSuccess).not.toHaveBeenCalled() + }) + + it('should not create a workflow tool when automatic publishing fails', async () => { + const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined) + mockAppDetail = { + ...mockAppDetail, + mode: AppModeEnum.WORKFLOW, + } + mockOnPublish.mockRejectedValueOnce(new Error('publish failed')) + + render() + + fireEvent.click(screen.getByText(/(?:^|\.)common\.publish(?=$|:)/)) + fireEvent.click(screen.getByText('publisher-workflow-tool')) + fireEvent.click(screen.getByRole('button', { name: 'create-workflow-tool' })) + + await waitFor(() => { + expect(mockOnPublish).toHaveBeenCalledOnce() + }) + expect(mockCreateWorkflowToolProvider).not.toHaveBeenCalled() + expect(mockToastError).toHaveBeenCalledWith('publish failed') + consoleWarnSpy.mockRestore() + }) + it('should not open workflow tool drawer without tool.manage', () => { mockWorkspacePermissionKeys = [] mockAppDetail = { diff --git a/web/app/components/app/app-publisher/features-wrapper.tsx b/web/app/components/app/app-publisher/features-wrapper.tsx index 4114aadb50a..ffb85da6b44 100644 --- a/web/app/components/app/app-publisher/features-wrapper.tsx +++ b/web/app/components/app/app-publisher/features-wrapper.tsx @@ -1,5 +1,6 @@ import type { AppPublisherProps, + AppPublisherPublishOptions, AppPublisherPublishParams, } from '@/app/components/app/app-publisher/types' import type { ConfigurationPublishConfig } from '@/app/components/app/configuration/hooks/configuration-lifecycle/types' @@ -26,6 +27,7 @@ type Props = Omit & { onPublish?: ( params?: AppPublisherPublishParams, features?: Features, + options?: AppPublisherPublishOptions, ) => Promise | unknown publishedConfig: ConfigurationPublishConfig resetAppConfig?: () => void @@ -88,7 +90,8 @@ const FeaturesWrappedAppPublisher = (props: Props) => { } const handlePublish = useCallback( - (params?: AppPublisherPublishParams) => { + (params?: AppPublisherPublishParams, options?: AppPublisherPublishOptions) => { + if (options) return props.onPublish?.(params, features, options) return props.onPublish?.(params, features) }, [features, props], diff --git a/web/app/components/app/app-publisher/publisher-content/index.tsx b/web/app/components/app/app-publisher/publisher-content/index.tsx index 945e879331b..c5b7e489db1 100644 --- a/web/app/components/app/app-publisher/publisher-content/index.tsx +++ b/web/app/components/app/app-publisher/publisher-content/index.tsx @@ -130,7 +130,7 @@ export function PublisherContent({ hasTriggerNode, inputs, onClosePublisher: closePublisher, - onPublish: publish.handlePublish, + onPublish: publish.publishWorkflowTool, onRefreshData, outputs, toolPublished, diff --git a/web/app/components/app/app-publisher/publisher-content/use-publish-controller.ts b/web/app/components/app/app-publisher/publisher-content/use-publish-controller.ts index 0fc2f7acb3d..6737b74ee17 100644 --- a/web/app/components/app/app-publisher/publisher-content/use-publish-controller.ts +++ b/web/app/components/app/app-publisher/publisher-content/use-publish-controller.ts @@ -1,5 +1,9 @@ import type { QueryClient } from '@tanstack/react-query' -import type { AppPublisherProps, AppPublisherPublishParams } from '../types' +import type { + AppPublisherProps, + AppPublisherPublishOptions, + AppPublisherPublishParams, +} from '../types' import type { CollaborationUpdate } from '@/app/components/workflow/collaboration/types/collaboration' import { useHotkey } from '@tanstack/react-hotkeys' import { useQueryClient } from '@tanstack/react-query' @@ -81,43 +85,54 @@ export function usePublishController({ : publishedAt const hasPublishedVersion = Boolean(currentPublishedAt) + async function publishApp( + params?: AppPublisherPublishParams, + options?: AppPublisherPublishOptions, + ) { + await onPublish?.(params, options) + setPublished(true) + + const socket = appId ? webSocketClient.getSocket(appId) : null + if (appId) { + invalidateAppWorkflow(appId) + if (supportsMultiEnvironment) refreshAppDeploymentData(queryClient, appId) + } else { + console.warn('[app-publisher] missing appId, skip workflow invalidate and socket emit') + } + if (socket) { + const timestamp = Date.now() + socket.emit('collaboration_event', { + type: 'app_publish_update', + data: { + action: 'published', + timestamp, + }, + timestamp, + }) + } else if (appId) { + console.warn('[app-publisher] socket not ready, skip collaboration_event emit', { appId }) + } + + trackEvent('app_published_time', { + action_mode: 'app', + app_id: appId, + app_name: appName, + }) + } + async function handlePublish(params?: AppPublisherPublishParams) { try { - await onPublish?.(params) - setPublished(true) - - const socket = appId ? webSocketClient.getSocket(appId) : null - if (appId) { - invalidateAppWorkflow(appId) - if (supportsMultiEnvironment) refreshAppDeploymentData(queryClient, appId) - } else { - console.warn('[app-publisher] missing appId, skip workflow invalidate and socket emit') - } - if (socket) { - const timestamp = Date.now() - socket.emit('collaboration_event', { - type: 'app_publish_update', - data: { - action: 'published', - timestamp, - }, - timestamp, - }) - } else if (appId) { - console.warn('[app-publisher] socket not ready, skip collaboration_event emit', { appId }) - } - - trackEvent('app_published_time', { - action_mode: 'app', - app_id: appId, - app_name: appName, - }) + await publishApp(params) } catch (error) { console.warn('[app-publisher] publish failed', error) setPublished(false) } } + async function publishWorkflowTool(params?: AppPublisherPublishParams) { + await publishApp(params, { showSuccessToast: false }) + } + async function handleRestore() { try { await onRestore?.() @@ -164,6 +179,7 @@ export function usePublishController({ isWorkflowApp, published, publishedWorkflow, + publishWorkflowTool, resetPublished: () => setPublished(false), } } diff --git a/web/app/components/app/app-publisher/types.ts b/web/app/components/app/app-publisher/types.ts index 8c8e9917cde..903d45097f0 100644 --- a/web/app/components/app/app-publisher/types.ts +++ b/web/app/components/app/app-publisher/types.ts @@ -4,9 +4,16 @@ import type { PublishWorkflowParams } from '@/types/workflow' export type AppPublisherPublishParams = ModelAndParameter | PublishWorkflowParams +export type AppPublisherPublishOptions = { + showSuccessToast?: boolean +} + type AppPublisherPublishHandler = - | ((params?: AppPublisherPublishParams) => Promise | unknown) - | ((params?: unknown) => Promise | unknown) + | (( + params?: AppPublisherPublishParams, + options?: AppPublisherPublishOptions, + ) => Promise | unknown) + | ((params?: unknown, options?: AppPublisherPublishOptions) => Promise | unknown) type AppPublisherRestoreHandler = () => Promise | unknown diff --git a/web/app/components/tools/workflow-tool/hooks/__tests__/use-configure-button.spec.ts b/web/app/components/tools/workflow-tool/hooks/__tests__/use-configure-button.spec.ts index 0f8720f10be..702fd85d77f 100644 --- a/web/app/components/tools/workflow-tool/hooks/__tests__/use-configure-button.spec.ts +++ b/web/app/components/tools/workflow-tool/hooks/__tests__/use-configure-button.spec.ts @@ -370,6 +370,46 @@ describe('useConfigureButton', () => { // Mutation handlers describe('handleCreate', () => { + it('should publish before creating the provider', async () => { + mockCreateWorkflowToolProvider.mockResolvedValue({}) + const handlePublish = vi.fn().mockResolvedValue(undefined) + const { result } = renderHook(() => + useConfigureButton(createDefaultOptions({ handlePublish })), + ) + + await act(async () => { + await result.current.handleCreate( + createMockRequest({ workflow_app_id: 'app-123' }) as WorkflowToolProviderRequest & { + workflow_app_id: string + }, + ) + }) + + expect(handlePublish).toHaveBeenCalledOnce() + expect(mockCreateWorkflowToolProvider).toHaveBeenCalledOnce() + expect(handlePublish.mock.invocationCallOrder[0]).toBeLessThan( + mockCreateWorkflowToolProvider.mock.invocationCallOrder[0]!, + ) + }) + + it('should not create the provider when publishing fails', async () => { + const handlePublish = vi.fn().mockRejectedValue(new Error('Publish failed')) + const { result } = renderHook(() => + useConfigureButton(createDefaultOptions({ handlePublish })), + ) + + await act(async () => { + await result.current.handleCreate( + createMockRequest({ workflow_app_id: 'app-123' }) as WorkflowToolProviderRequest & { + workflow_app_id: string + }, + ) + }) + + expect(mockCreateWorkflowToolProvider).not.toHaveBeenCalled() + expect(mockToastNotify).toHaveBeenCalledWith({ type: 'error', message: 'Publish failed' }) + }) + it('should create provider, invalidate caches, refresh, and notify configured', async () => { mockCreateWorkflowToolProvider.mockResolvedValue({}) const onRefreshData = vi.fn() @@ -439,6 +479,7 @@ describe('useConfigureButton', () => { expect(onRefreshData).toHaveBeenCalled() expect(mockInvalidateAllWorkflowTools).toHaveBeenCalled() expect(mockInvalidateWorkflowToolDetailByAppID).toHaveBeenCalledWith('app-123') + expect(mockToastNotify).toHaveBeenCalledWith({ type: 'success', message: expect.any(String) }) expect(onConfigured).toHaveBeenCalled() }) diff --git a/web/app/components/tools/workflow-tool/hooks/use-configure-button.ts b/web/app/components/tools/workflow-tool/hooks/use-configure-button.ts index 6c8e982c624..a0d15c68ac9 100644 --- a/web/app/components/tools/workflow-tool/hooks/use-configure-button.ts +++ b/web/app/components/tools/workflow-tool/hooks/use-configure-button.ts @@ -121,7 +121,6 @@ export function useConfigureButton(options: UseConfigureButtonOptions) { onRefreshData, onConfigured, } = options - const { t } = useTranslation() // Data fetching via React Query @@ -180,6 +179,7 @@ export function useConfigureButton(options: UseConfigureButtonOptions) { // Mutation handlers (not memoized — only used in conditionally-rendered modal) const handleCreate = async (data: WorkflowToolProviderRequest & { workflow_app_id: string }) => { try { + await handlePublish() await createWorkflowToolProvider(data) invalidateAllWorkflowTools() onRefreshData?.() @@ -204,6 +204,7 @@ export function useConfigureButton(options: UseConfigureButtonOptions) { onRefreshData?.() invalidateAllWorkflowTools() invalidateDetail(workflowAppId) + toast.success(t(($) => $['api.actionSuccess'], { ns: 'common' })) onConfigured?.() } catch (e) { toast.error((e as Error).message) diff --git a/web/app/components/workflow-app/components/workflow-header/__tests__/features-trigger.spec.tsx b/web/app/components/workflow-app/components/workflow-header/__tests__/features-trigger.spec.tsx index cafae7b2526..ea6550b541c 100644 --- a/web/app/components/workflow-app/components/workflow-header/__tests__/features-trigger.spec.tsx +++ b/web/app/components/workflow-app/components/workflow-header/__tests__/features-trigger.spec.tsx @@ -182,6 +182,23 @@ vi.mock('@/app/components/app/app-publisher', () => ({ > publisher-publish +