xiaohao-components 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ import { ClassValue } from 'clsx';
2
+ /** 包内唯一类名合并工具(只搬 cn,业务工具留在宿主) */
3
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -0,0 +1,7 @@
1
+ import { ReactNode } from 'react';
2
+ export declare function A2uiPortalProvider({ container, children, }: {
3
+ container: HTMLElement | null;
4
+ children: ReactNode;
5
+ }): import("react").JSX.Element;
6
+ /** 包内 Portal 挂载点;`null` 表示还没就绪(Portal 会挂到 body,通常只在首帧) */
7
+ export declare function useA2uiPortalContainer(): HTMLElement | null;
@@ -0,0 +1,65 @@
1
+ import { ProcessableMessagePayload } from '@a2ui/web_core/v0_9';
2
+ /** v0.9 消息必须带 `version`,缺了引擎按 v0.8 解析(形状完全不同,症状是"一条都渲不出来") */
3
+ export declare const A2UI_VERSION = "v0.9";
4
+ /**
5
+ * 工具结果里的**指令键**(不是数据路径):协同端用它下发「选项列表要换成这些」之类的
6
+ * 增量 `updateComponents`。§2.6 明确它**不参与**「路径必须出现在 outputSchema」的校验,
7
+ * 本端同理——它不能被当成 `/xxx` 塞进数据模型,否则会多出一个永远匹配不到组件的幽灵路径。
8
+ */
9
+ export declare const A2UI_AUX_KEY = "_a2uiMessages";
10
+ /** 一条 `updateDataModel` 消息的形状(只取本端构造时用到的字段) */
11
+ export interface A2uiDataModelMessage {
12
+ version: typeof A2UI_VERSION;
13
+ updateDataModel: {
14
+ surfaceId: string;
15
+ path: string;
16
+ value: unknown;
17
+ };
18
+ }
19
+ /** 解析结果:surface 标识 + 原文消息(顺序已归位) */
20
+ export interface A2uiCardMessages {
21
+ surfaceId: string;
22
+ /** createSurface → updateComponents → 其余(原文逐字保留,客户端不改字段) */
23
+ messages: ProcessableMessagePayload[];
24
+ }
25
+ /**
26
+ * 解析卡片 JSON 原文。
27
+ *
28
+ * **失败一律返回 `null`,不抛、不重试**(重试不会让坏 JSON 变好)。调用方据此退纯文本
29
+ * 并记 `card.a2ui.parse_failed`。
30
+ *
31
+ * 只做两件"结构上必须做"的事:
32
+ * 1. 取出 `messages` 数组——**逐字保留**消息体,不补 `version`、不改 `catalogId`
33
+ * (发布端是唯一真相源,客户端擅自补字段会让"编辑端所见"与"运行时所得"分叉);
34
+ * 2. 按投递顺序归位(§4.4)——数据后投是硬要求,组件先于数据是观感要求。
35
+ */
36
+ export declare function parseA2uiCard(json: string | null | undefined): A2uiCardMessages | null;
37
+ /**
38
+ * 把工具结果的 `structuredContent` 拍平成 `updateDataModel` 的路径条目。
39
+ *
40
+ * 服务端下发的键**已经是绝对路径**(`"/services"`、`"/formValid"`),如
41
+ * `cloud-platform/docs/demo/a2ui/fixtures/structured-content.samples.json`。
42
+ * 这里容忍不带前导 `/` 的写法(补上),因为手写工具时漏斜杠太常见了,
43
+ * 而漏了斜杠的症状是"数据在、卡是空的",极难查。
44
+ *
45
+ * `_a2uiMessages` 走 `auxMessagesOf`,**不进数据模型**。
46
+ */
47
+ export declare function toDataEntries(structuredContent: unknown): {
48
+ path: string;
49
+ value: unknown;
50
+ }[];
51
+ /** 取 `_a2uiMessages`(协同端下发的增量消息);不是数组或为空时返回空数组 */
52
+ export declare function auxMessagesOf(structuredContent: unknown): unknown[];
53
+ /** 数据条目 → 一条条 `updateDataModel`(**一条一路径**,引擎不认批量) */
54
+ export declare function buildDataModelMessages(surfaceId: string, entries: {
55
+ path: string;
56
+ value: unknown;
57
+ }[]): A2uiDataModelMessage[];
58
+ /**
59
+ * 提交结果写回时的冻结判据(§1.3「冻结条件」):
60
+ * 工具回包里的 `/submitted === true` 或 `/formValid === false` ⇒ 整卡冻结。
61
+ *
62
+ * 用**回包数据**判而不是卡类型判:两条规则正好对应"表单已提交"与"表单已失效",
63
+ * 而展示卡的按钮回包不会带这两个键(带了也说明它真的是表单卡)。
64
+ */
65
+ export declare function shouldFreeze(structuredContent: unknown): boolean;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 列出 json 里引用、但 data 没提供的路径(数据齐全返回 `[]`;json 坏返回 `[]`,
3
+ * 解析失败另有专门的兜底路径,这里不重复报)。
4
+ */
5
+ export declare function collectMissingPaths(json: string, data: Record<string, unknown>): string[];
@@ -0,0 +1,56 @@
1
+ /**
2
+ * 远程选项:`options` 写成对象时的解析(对官方 v0.9 的扩展)。
3
+ *
4
+ * 官方 `ChoicePicker.options` 只能是静态数组 `[{label, value}]`,且输入组件没有
5
+ * `onChange`/`action` —— 「选中即调工具」表达不出来;工具想回灌选项只能发
6
+ * `_a2uiMessages` 用同一组件 id 整份替换,而**自动生成的卡拿不到组件 id**
7
+ * (协同端 `A2UI卡片平台化生成-流程方案.md` §5.6 的三条理由)。
8
+ *
9
+ * 故约定这张卡自己声明选项从哪来:
10
+ *
11
+ * ```json
12
+ * "options": { "source": "/services", "label": "name", "value": "id" }
13
+ * ```
14
+ *
15
+ * 工具仍然是纯数据(`"/services": [{"id":"svc_1","name":"订单服务"}]`),
16
+ * 由渲染层(本文件)在投递前解析成官方要求的静态数组。
17
+ *
18
+ * **这是对官方 schema 的协议偏离**:官方 `ChoicePickerApi.schema` 会判它不合法。
19
+ * 手机端已按同一口径实现(`xiaohao_mobile/lib/core/a2ui/catalog/remote_options.dart`,
20
+ * 回退链与本文件逐条对齐),改这里必须同时改那边。
21
+ */
22
+ /** 卡里声明的远程选项(`options` 为对象而非数组) */
23
+ export interface RemoteOptionsDescriptor {
24
+ /** 工具返回的数组在数据模型里的路径(协同端生成器只产绝对路径;相对路径按组件作用域解析) */
25
+ source: string;
26
+ /** 用元素里的哪个属性当显示名;缺省 `name` */
27
+ label?: string;
28
+ /** 用元素里的哪个属性当选中值;缺省 `id` */
29
+ value?: string;
30
+ }
31
+ /** 官方 `options` 的元素形状(渲染层只关心这两项) */
32
+ export interface ChoiceOption {
33
+ label: string;
34
+ value: string;
35
+ }
36
+ /** 缺省取显示名的属性——与协同端生成器的注释、示例卡保持一致 */
37
+ export declare const DEFAULT_LABEL_KEY = "name";
38
+ /** 缺省取值的属性 */
39
+ export declare const DEFAULT_VALUE_KEY = "id";
40
+ /**
41
+ * 判断一个 `options` 是不是远程写法。
42
+ *
43
+ * 只认「非数组的对象且 `source` 是非空字符串」:静态数组、`undefined`、残缺对象
44
+ * 都走官方路径 —— 静态卡的行为一个字节都不能变。
45
+ */
46
+ export declare function isRemoteOptions(value: unknown): value is RemoteOptionsDescriptor;
47
+ /**
48
+ * 把 `source` 指向的数组解析成官方选项数组。
49
+ *
50
+ * 取值的回退链是**故意宽松**的(声明没写 → `name`/`id` → `label`/`value`):
51
+ * 写卡的人少写一个 `label` 就整列空白,排查成本远高于多两条回退。但元素**没有值就跳过**——
52
+ * 选不了的选项不如不出现。
53
+ *
54
+ * 数据还没到(`undefined`)或不是数组 → 空数组,渲染成「暂无选项」。
55
+ */
56
+ export declare function resolveRemoteOptions(data: unknown, descriptor: RemoteOptionsDescriptor): ChoiceOption[];
package/dist/text.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 工具结果的文本提取(纯函数副本,源自桌面 `store/card.ts`)。
3
+ *
4
+ * 包内只用于「JSON 解析失败退纯文本」的兜底文案;宿主可另行 import
5
+ * 同名函数避免两份实现。
6
+ */
7
+ /** 包内认的最小形状(不引宿主的 zod 契约) */
8
+ export interface A2uiToolResultLike {
9
+ content?: unknown;
10
+ }
11
+ /** 从工具结果提取纯文本兜底文案(content[] 里的 text 块拼接;空则 null) */
12
+ export declare function extractToolResultText(toolResult: A2uiToolResultLike | null | undefined): string | null;
@@ -0,0 +1,56 @@
1
+ import { ReactNode } from 'react';
2
+ /**
3
+ * 渲染入参:只保留渲染所需,不引任何宿主的聊天域类型。
4
+ * (桌面 `CardData` → 本类型是 P2 的三行映射,见提案 §4.2。)
5
+ */
6
+ export interface A2uiCardInput {
7
+ /** 动作幂等/日志用 */
8
+ cardInstanceId: string;
9
+ /** 服务端注册名(mcp__{pluginId}__{serverName}__{toolName}),用于按钮短名补前缀 */
10
+ tool: string;
11
+ /** A2UI v0.9 原文(发布端是唯一真相源,包不改写) */
12
+ json: string;
13
+ /** 工具结果里的数据(键是绝对路径,如 "/services") */
14
+ structuredContent?: Record<string, unknown>;
15
+ /** 历史回看派生的「已提交」→ 直接冻结 */
16
+ submitted?: boolean;
17
+ }
18
+ /** 一次按钮动作(工具名已补全成注册名) */
19
+ export interface A2uiActionRequest {
20
+ tool: string;
21
+ args: Record<string, unknown>;
22
+ }
23
+ /** 与 card-tool-calls 200 回包同形(五态 + result + submitted) */
24
+ export interface A2uiActionOutcome {
25
+ outcome: "ok" | "rejected" | "denied" | "unavailable" | "retryable";
26
+ reason?: string;
27
+ result?: {
28
+ structuredContent?: Record<string, unknown>;
29
+ isError?: boolean;
30
+ };
31
+ submitted?: boolean;
32
+ }
33
+ export interface A2uiRendererProps {
34
+ card: A2uiCardInput;
35
+ /**
36
+ * 动作上行。**不传 = 预览模式**:按钮可点、只回调 `onPreviewAction`,
37
+ * 不进状态机、不写回、不冻结、不发请求——「预览不产生任何业务请求」
38
+ * 是结构保证,不靠调用方自觉。
39
+ */
40
+ onAction?: (req: A2uiActionRequest) => Promise<A2uiActionOutcome>;
41
+ /** 预览模式下的按钮点击回显 */
42
+ onPreviewAction?: (req: A2uiActionRequest) => void;
43
+ /** 宿主通知(原 toast);缺省 no-op */
44
+ onNotify?: (n: {
45
+ level: "error" | "info";
46
+ message: string;
47
+ }) => void;
48
+ /** 宿主日志;缺省 no-op。只记事件名与上下文,不记正文 */
49
+ logger?: (event: string, ctx?: Record<string, string | number | boolean>) => void;
50
+ /** 渲染失败时的兜底内容;缺省纯文本节点 */
51
+ renderFallback?: (text: string) => ReactNode;
52
+ /** 深浅主题;`auto` 跟随 prefers-color-scheme(不依赖 next-themes) */
53
+ theme?: "light" | "dark" | "auto";
54
+ /** 追加到根节点(.xa2ui-root)的类名 */
55
+ className?: string;
56
+ }
@@ -0,0 +1,8 @@
1
+ import { Button as ButtonPrimitive } from '@base-ui/react/button';
2
+ import { VariantProps } from 'class-variance-authority';
3
+ declare const buttonVariants: (props?: ({
4
+ variant?: "default" | "outline" | "secondary" | "ghost" | "destructive" | "link" | null | undefined;
5
+ size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
6
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
7
+ declare function Button({ className, variant, size, ...props }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>): import("react").JSX.Element;
8
+ export { Button, buttonVariants };
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+ declare function Card({ className, size, ...props }: React.ComponentProps<"div"> & {
3
+ size?: "default" | "sm";
4
+ }): React.JSX.Element;
5
+ declare function CardHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
6
+ declare function CardTitle({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
7
+ declare function CardDescription({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
8
+ declare function CardAction({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
9
+ declare function CardContent({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
10
+ declare function CardFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
11
+ export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, };
@@ -0,0 +1,3 @@
1
+ import { Checkbox as CheckboxPrimitive } from '@base-ui/react/checkbox';
2
+ declare function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props): import("react").JSX.Element;
3
+ export { Checkbox };
@@ -0,0 +1,17 @@
1
+ import { Dialog as DialogPrimitive } from '@base-ui/react/dialog';
2
+ import * as React from "react";
3
+ declare function Dialog({ ...props }: DialogPrimitive.Root.Props): React.JSX.Element;
4
+ declare function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props): React.JSX.Element;
5
+ declare function DialogPortal({ ...props }: DialogPrimitive.Portal.Props): React.JSX.Element;
6
+ declare function DialogClose({ ...props }: DialogPrimitive.Close.Props): React.JSX.Element;
7
+ declare function DialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props): React.JSX.Element;
8
+ declare function DialogContent({ className, children, showCloseButton, ...props }: DialogPrimitive.Popup.Props & {
9
+ showCloseButton?: boolean;
10
+ }): React.JSX.Element;
11
+ declare function DialogHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
12
+ declare function DialogFooter({ className, showCloseButton, children, ...props }: React.ComponentProps<"div"> & {
13
+ showCloseButton?: boolean;
14
+ }): React.JSX.Element;
15
+ declare function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props): React.JSX.Element;
16
+ declare function DialogDescription({ className, ...props }: DialogPrimitive.Description.Props): React.JSX.Element;
17
+ export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, };
@@ -0,0 +1,3 @@
1
+ import * as React from "react";
2
+ declare function Input({ className, type, ...props }: React.ComponentProps<"input">): React.JSX.Element;
3
+ export { Input };
@@ -0,0 +1,3 @@
1
+ import * as React from "react";
2
+ declare function Label({ className, ...props }: React.ComponentProps<"label">): React.JSX.Element;
3
+ export { Label };
@@ -0,0 +1,15 @@
1
+ import { Select as SelectPrimitive } from '@base-ui/react/select';
2
+ import * as React from "react";
3
+ declare const Select: typeof SelectPrimitive.Root;
4
+ declare function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props): React.JSX.Element;
5
+ declare function SelectValue({ className, ...props }: SelectPrimitive.Value.Props): React.JSX.Element;
6
+ declare function SelectTrigger({ className, size, children, ...props }: SelectPrimitive.Trigger.Props & {
7
+ size?: "sm" | "default";
8
+ }): React.JSX.Element;
9
+ declare function SelectContent({ className, children, side, sideOffset, align, alignOffset, alignItemWithTrigger, ...props }: SelectPrimitive.Popup.Props & Pick<SelectPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger">): React.JSX.Element;
10
+ declare function SelectLabel({ className, ...props }: SelectPrimitive.GroupLabel.Props): React.JSX.Element;
11
+ declare function SelectItem({ className, children, ...props }: SelectPrimitive.Item.Props): React.JSX.Element;
12
+ declare function SelectSeparator({ className, ...props }: SelectPrimitive.Separator.Props): React.JSX.Element;
13
+ declare function SelectScrollUpButton({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>): React.JSX.Element;
14
+ declare function SelectScrollDownButton({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>): React.JSX.Element;
15
+ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, };
@@ -0,0 +1,3 @@
1
+ import { Separator as SeparatorPrimitive } from '@base-ui/react/separator';
2
+ declare function Separator({ className, orientation, ...props }: SeparatorPrimitive.Props): import("react").JSX.Element;
3
+ export { Separator };
@@ -0,0 +1,3 @@
1
+ import { Slider as SliderPrimitive } from '@base-ui/react/slider';
2
+ declare function Slider({ className, defaultValue, value, min, max, ...props }: SliderPrimitive.Root.Props): import("react").JSX.Element;
3
+ export { Slider };
@@ -0,0 +1,10 @@
1
+ import { Tabs as TabsPrimitive } from '@base-ui/react/tabs';
2
+ import { VariantProps } from 'class-variance-authority';
3
+ declare function Tabs({ className, orientation, ...props }: TabsPrimitive.Root.Props): import("react").JSX.Element;
4
+ declare const tabsListVariants: (props?: ({
5
+ variant?: "default" | "line" | null | undefined;
6
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
7
+ declare function TabsList({ className, variant, ...props }: TabsPrimitive.List.Props & VariantProps<typeof tabsListVariants>): import("react").JSX.Element;
8
+ declare function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props): import("react").JSX.Element;
9
+ declare function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props): import("react").JSX.Element;
10
+ export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };
@@ -0,0 +1,3 @@
1
+ import * as React from "react";
2
+ declare function Textarea({ className, ...props }: React.ComponentProps<"textarea">): React.JSX.Element;
3
+ export { Textarea };
@@ -0,0 +1,16 @@
1
+ /** 样例 JSON(A2UI v0.9 原文,字符串) */
2
+ export declare const DISPLAY_SAMPLE_JSON: string;
3
+ export declare const EDITABLE_SAMPLE_JSON: string;
4
+ export declare const EDITABLE_SURFACE_ID: string;
5
+ export declare const FORM_SAMPLE_JSON: string;
6
+ export declare const MULTI_ACTION_SAMPLE_JSON: string;
7
+ export declare const MULTI_ACTION_SURFACE_ID: string;
8
+ export declare const TRACK_SAMPLE_JSON: string;
9
+ export declare const TRACK_SURFACE_ID: string;
10
+ /** 样例数据(键是绝对路径) */
11
+ export declare const DISPLAY_SAMPLE_DATA: Record<string, unknown>;
12
+ export declare const EDITABLE_SAMPLE_DATA: Record<string, unknown>;
13
+ export declare const EDITABLE_UNIT_OPTIONS: Array<{ code: string; name: string }>;
14
+ export declare const FORM_SAMPLE_DATA: Record<string, unknown>;
15
+ export declare const MULTI_ACTION_SAMPLE_DATA: Record<string, unknown>;
16
+ export declare const TRACK_SAMPLE_DATA: Record<string, unknown>;
package/package.json CHANGED
@@ -1,6 +1,85 @@
1
1
  {
2
2
  "name": "xiaohao-components",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "A2UI 卡片的 Web 渲染层:官方引擎装配 + 19 个组件映射 + 动作状态机(React 18/19)",
5
+ "keywords": [
6
+ "a2ui",
7
+ "renderer",
8
+ "react",
9
+ "shadcn",
10
+ "json-ui",
11
+ "generative-ui"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "Hubei Yunzhimeng Technology Co., Ltd.",
15
+ "publishConfig": {
16
+ "access": "public",
17
+ "registry": "https://registry.npmjs.org/"
18
+ },
19
+ "type": "module",
20
+ "sideEffects": [
21
+ "*.css"
22
+ ],
23
+ "main": "./dist/index.js",
24
+ "module": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js"
30
+ },
31
+ "./fixtures": {
32
+ "types": "./fixtures/index.d.ts",
33
+ "import": "./dist/fixtures.js"
34
+ },
35
+ "./styles.css": "./dist/a2ui.css"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "fixtures/index.d.ts",
40
+ "README.md",
41
+ "LICENSE"
42
+ ],
43
+ "scripts": {
44
+ "prepack": "npm run build",
45
+ "build": "npm run build:js && npm run build:css && npm run check:bundle",
46
+ "build:js": "vite build",
47
+ "build:css": "vite build --config vite.styles.config.ts && node scripts/scope-css.mjs",
48
+ "test": "vitest run",
49
+ "test:watch": "vitest",
50
+ "check:bundle": "node scripts/check-bundle.mjs",
51
+ "pack": "npm run build && npm pack",
52
+ "playground": "vite serve playground --config playground/vite.config.ts",
53
+ "typecheck": "tsc --noEmit -p tsconfig.json"
54
+ },
55
+ "peerDependencies": {
56
+ "react": "^18.0.0 || ^19.0.0",
57
+ "react-dom": "^18.0.0 || ^19.0.0"
58
+ },
59
+ "dependencies": {
60
+ "@a2ui/react": "^0.14.0",
61
+ "@a2ui/web_core": "^0.14.0",
62
+ "@base-ui/react": "^1.6.0",
63
+ "class-variance-authority": "^0.7.1",
64
+ "clsx": "^2.1.1",
65
+ "lucide-react": "^1.25.0",
66
+ "tailwind-merge": "^3.6.0",
67
+ "zod": "^4.4.3"
68
+ },
69
+ "devDependencies": {
70
+ "@tailwindcss/vite": "^4.3.3",
71
+ "@types/node": "^26.6.6",
72
+ "@types/react": "^19.1.8",
73
+ "@types/react-dom": "^19.1.6",
74
+ "@vitejs/plugin-react": "^4.6.0",
75
+ "antd": "^5.26.0",
76
+ "jsdom": "^29.1.1",
77
+ "postcss": "^8.5.6",
78
+ "react": "^19.2.7",
79
+ "react-dom": "^19.2.7",
80
+ "tailwindcss": "^4.3.3",
81
+ "vite": "^7.0.4",
82
+ "vite-plugin-dts": "^4.5.4",
83
+ "vitest": "^4.1.10"
84
+ }
85
+ }