mcp-app-studio 0.5.1 → 0.6.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.
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Platform, ExtendedBridge, ToolResult, HostCapabilities, DisplayMode, FeatureKey, HostContext, ContentBlock } from './core/index.js';
2
- export { AudioContentBlock, CHATGPT_CAPABILITIES, ChatMessage, ContainerDimensions, ContentBlockAnnotations, ContentBlockIcon, HostBridge, HostContextChangedCallback, HostStyles, ImageContentBlock, MCP_CAPABILITIES, ResourceContentBlock, ResourceLinkContentBlock, TeardownCallback, TextContentBlock, Theme, ToolCancelledCallback, ToolInputCallback, ToolInputPartialCallback, ToolResultCallback, hasFeature, imageBlock, textBlock } from './core/index.js';
2
+ export { AudioContentBlock, ChatMessage, ContainerDimensions, ContentBlockAnnotations, ContentBlockIcon, HostBridge, HostContextChangedCallback, HostStyles, ImageContentBlock, MCP_CAPABILITIES, ResourceContentBlock, ResourceLinkContentBlock, TeardownCallback, TextContentBlock, Theme, ToolCancelledCallback, ToolInputCallback, ToolInputPartialCallback, ToolResultCallback, hasFeature, imageBlock, textBlock } from './core/index.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode } from 'react';
5
- import { A as AppCapabilities } from './bridge-BXW_-p2R.js';
5
+ import { A as AppCapabilities } from './bridge-BKLezf-H.js';
6
6
  import '@modelcontextprotocol/ext-apps';
7
7
 
8
8
  /**
@@ -39,17 +39,18 @@ declare function detectPlatformDetailed(): DetectionResult;
39
39
  /**
40
40
  * Detects the current platform the widget is running on.
41
41
  *
42
- * Detection priority:
43
- * 1. `window.openai` existsChatGPT
44
- * 2. URL has `?mcp-host` param → MCP
45
- * 3. `window.__MCP_HOST__` exists → MCP
46
- * 4. iframe has `data-mcp-host` attributeMCP
47
- * 5. None of the above → unknown
42
+ * Host detection:
43
+ * - URL has `?mcp-host` paramMCP
44
+ * - `window.__MCP_HOST__` exists → MCP
45
+ * - iframe has `data-mcp-host` attribute → MCP
46
+ * - None of the aboveunknown
47
+ *
48
+ * Note: `window.openai` is a ChatGPT-only *extensions* layer, not the MCP host protocol.
48
49
  *
49
50
  * **Debugging tip:** Set `window.__MCP_APP_STUDIO_DEBUG__ = true` before
50
51
  * loading your widget to see detailed detection logs in the console.
51
52
  *
52
- * @returns The detected platform: "chatgpt", "mcp", or "unknown"
53
+ * @returns The detected platform: "mcp" or "unknown"
53
54
  *
54
55
  * @example
55
56
  * ```ts
@@ -61,16 +62,16 @@ declare function detectPlatformDetailed(): DetectionResult;
61
62
  */
62
63
  declare function detectPlatform(): Platform;
63
64
  /**
64
- * Returns true if running inside ChatGPT.
65
+ * Returns true if ChatGPT-only extensions are available (`window.openai`).
65
66
  *
66
67
  * @example
67
68
  * ```ts
68
- * if (isChatGPT()) {
69
- * // Use ChatGPT-specific features
69
+ * if (hasChatGPTExtensions()) {
70
+ * // Use ChatGPT-only extensions (e.g. widgetState, file upload)
70
71
  * }
71
72
  * ```
72
73
  */
73
- declare function isChatGPT(): boolean;
74
+ declare function hasChatGPTExtensions(): boolean;
74
75
  /**
75
76
  * Returns true if running inside an MCP host (e.g., Claude Desktop).
76
77
  *
@@ -209,10 +210,10 @@ declare function useCapabilities(): HostCapabilities | null;
209
210
  */
210
211
  declare function useToolInput<T = Record<string, unknown>>(): T | null;
211
212
  /**
212
- * Returns partial tool input as it's being streamed (MCP only).
213
+ * Returns partial tool input as it's being streamed (host-dependent).
213
214
  * Useful for showing real-time feedback as the user types.
214
215
  *
215
- * **Platform support:** MCP only. Returns null on ChatGPT.
216
+ * **Host support:** MCP Apps hosts that stream partial input. Returns null otherwise.
216
217
  *
217
218
  * @typeParam T - The expected shape of the tool input
218
219
  * @returns Partial tool input, or null
@@ -370,26 +371,19 @@ declare function useOpenLink(): (url: string) => Promise<void>;
370
371
  */
371
372
  declare function useSendMessage(): (text: string) => Promise<void>;
372
373
  /**
373
- * Returns a function to update the model's context (MCP only).
374
+ * Returns a function to update the model-visible context.
374
375
  * Use this to provide additional context to the AI model.
375
376
  *
376
- * **Platform support:** MCP only. Logs a warning on ChatGPT.
377
- *
378
377
  * @returns Async function to update model context
379
378
  *
380
379
  * @example
381
380
  * ```tsx
382
381
  * function DataViewer({ data }: { data: object }) {
383
382
  * const updateContext = useUpdateModelContext();
384
- * const platform = usePlatform();
385
383
  *
386
384
  * useEffect(() => {
387
- * if (platform === 'mcp') {
388
- * updateContext({
389
- * structuredContent: { currentData: data }
390
- * });
391
- * }
392
- * }, [data, platform, updateContext]);
385
+ * updateContext({ structuredContent: { currentData: data } });
386
+ * }, [data, updateContext]);
393
387
  *
394
388
  * return <pre>{JSON.stringify(data, null, 2)}</pre>;
395
389
  * }
@@ -400,10 +394,9 @@ declare function useUpdateModelContext(): (ctx: {
400
394
  structuredContent?: Record<string, unknown>;
401
395
  }) => Promise<void>;
402
396
  /**
403
- * Returns persisted widget state and a setter (ChatGPT only).
397
+ * Returns persisted widget state and a setter (ChatGPT extensions only).
404
398
  * State is preserved across page refreshes and conversation turns.
405
- *
406
- * **Platform support:** ChatGPT only. Logs a warning on MCP.
399
+ * Requires `window.openai` (feature-detected by the SDK).
407
400
  *
408
401
  * @typeParam T - The shape of your widget state
409
402
  * @returns Tuple of [state, setState]
@@ -438,10 +431,8 @@ declare function useWidgetState<T = Record<string, unknown>>(): [
438
431
  (state: T | null) => void
439
432
  ];
440
433
  /**
441
- * Returns a function for structured logging (MCP only).
442
- * Logs are sent to the MCP host for debugging and monitoring.
443
- *
444
- * **Platform support:** MCP only. Falls back to console.log on other platforms.
434
+ * Returns a function for structured logging when the current host supports it.
435
+ * Falls back to `console.log` otherwise.
445
436
  *
446
437
  * @returns Log function that accepts level and message
447
438
  *
@@ -505,4 +496,94 @@ declare function useLog(): (level: "debug" | "info" | "warning" | "error", data:
505
496
  */
506
497
  declare function useFeature(feature: FeatureKey): boolean;
507
498
 
508
- export { ContentBlock, type DetectionResult, DisplayMode, ExtendedBridge, FeatureKey, HostCapabilities, HostContext, Platform, ToolResult, UniversalProvider, type UniversalProviderProps, detectPlatform, detectPlatformDetailed, disableDebugMode, enableDebugMode, isChatGPT, isMCP, useCallTool, useCapabilities, useDisplayMode, useFeature, useHostContext, useLog, useOpenLink, usePlatform, useSendMessage, useTheme, useToolInput, useToolInputPartial, useToolResult, useUniversalBridge, useUpdateModelContext, useWidgetState };
499
+ /**
500
+ * ChatGPT-only extensions.
501
+ *
502
+ * ChatGPT is an MCP Apps host (standard `ui/*` bridge). It may also expose
503
+ * optional extras via `window.openai` (widget state, file APIs, host modals…).
504
+ *
505
+ * This module intentionally treats `window.openai` as an *optional* layer:
506
+ * - Never required for MCP Apps interoperability
507
+ * - Always feature-detected before use
508
+ */
509
+ type ChatGPTExtensions = {
510
+ theme?: "light" | "dark";
511
+ locale?: string;
512
+ displayMode?: "pip" | "inline" | "fullscreen";
513
+ previousDisplayMode?: "pip" | "inline" | "fullscreen" | null;
514
+ maxHeight?: number;
515
+ safeArea?: {
516
+ insets: {
517
+ top: number;
518
+ bottom: number;
519
+ left: number;
520
+ right: number;
521
+ };
522
+ };
523
+ userAgent?: Record<string, unknown>;
524
+ view?: {
525
+ mode: "modal" | "inline";
526
+ params: Record<string, unknown> | null;
527
+ } | null;
528
+ userLocation?: Record<string, unknown> | null;
529
+ toolInput?: Record<string, unknown>;
530
+ toolOutput?: Record<string, unknown> | null;
531
+ toolResponseMetadata?: Record<string, unknown> | null;
532
+ widgetState?: Record<string, unknown> | null;
533
+ setWidgetState?: (state: Record<string, unknown> | null) => void;
534
+ callTool?: (name: string, args: Record<string, unknown>) => Promise<unknown>;
535
+ sendFollowUpMessage?: (args: {
536
+ prompt: string;
537
+ }) => Promise<void>;
538
+ openExternal?: (payload: {
539
+ href: string;
540
+ }) => void;
541
+ notifyIntrinsicHeight?: (height: number) => void;
542
+ requestDisplayMode?: (args: {
543
+ mode: string;
544
+ }) => Promise<{
545
+ mode: string;
546
+ }>;
547
+ uploadFile?: (file: File) => Promise<{
548
+ fileId: string;
549
+ }>;
550
+ getFileDownloadUrl?: (args: {
551
+ fileId: string;
552
+ }) => Promise<{
553
+ downloadUrl: string;
554
+ }>;
555
+ setOpenInAppUrl?: (args: {
556
+ href: string;
557
+ }) => void;
558
+ requestCheckout?: (...args: unknown[]) => unknown;
559
+ requestClose?: () => void;
560
+ requestModal?: (options: Record<string, unknown>) => Promise<void>;
561
+ };
562
+ /**
563
+ * Wraps an MCP-first bridge with ChatGPT-only extensions when `window.openai`
564
+ * exists.
565
+ *
566
+ * Note: this function mutates `bridge` in-place so class instances preserve
567
+ * prototype methods.
568
+ *
569
+ * The returned bridge:
570
+ * - Delegates all MCP behavior to `bridge`
571
+ * - Adds ChatGPT-only helpers (widget state, file APIs, modals) when available
572
+ *
573
+ * @param bridge - Base MCP bridge to extend.
574
+ * @returns The same `bridge` instance with ChatGPT-only extensions attached
575
+ * when available.
576
+ */
577
+ declare function withChatGPTExtensions(bridge: ExtendedBridge): ExtendedBridge;
578
+ declare global {
579
+ interface Window {
580
+ /**
581
+ * ChatGPT-only extension layer. Optional.
582
+ *
583
+ * Prefer using the MCP Apps bridge (`ui/*`) for core functionality.
584
+ */
585
+ openai?: ChatGPTExtensions;
586
+ }
587
+ }
588
+
589
+ export { type ChatGPTExtensions, ContentBlock, type DetectionResult, DisplayMode, ExtendedBridge, FeatureKey, HostCapabilities, HostContext, Platform, ToolResult, UniversalProvider, type UniversalProviderProps, detectPlatform, detectPlatformDetailed, disableDebugMode, enableDebugMode, hasChatGPTExtensions, isMCP, useCallTool, useCapabilities, useDisplayMode, useFeature, useHostContext, useLog, useOpenLink, usePlatform, useSendMessage, useTheme, useToolInput, useToolInputPartial, useToolResult, useUniversalBridge, useUpdateModelContext, useWidgetState, withChatGPTExtensions };
package/dist/index.js CHANGED
@@ -2,17 +2,13 @@ import {
2
2
  imageBlock,
3
3
  textBlock
4
4
  } from "./chunk-KRCGOYZ5.js";
5
- import {
6
- ChatGPTBridge
7
- } from "./chunk-EPZCYA26.js";
8
5
  import {
9
6
  MCPBridge
10
- } from "./chunk-2OPSDEPI.js";
7
+ } from "./chunk-IEZAKOIG.js";
11
8
  import {
12
- CHATGPT_CAPABILITIES,
13
9
  MCP_CAPABILITIES,
14
10
  hasFeature
15
- } from "./chunk-4LAH4JH6.js";
11
+ } from "./chunk-QNH5NSRH.js";
16
12
 
17
13
  // src/universal/detect.ts
18
14
  var MCP_MARKERS = {
@@ -42,8 +38,7 @@ function detectPlatformDetailed() {
42
38
  }
43
39
  if (window["openai"]) {
44
40
  checks.windowOpenai = true;
45
- debugLog("detectPlatform: Found window.openai", { platform: "chatgpt" });
46
- return { platform: "chatgpt", detectedBy: "window.openai", checks };
41
+ debugLog("detectPlatform: Found window.openai (extensions)");
47
42
  }
48
43
  try {
49
44
  const params = new URLSearchParams(window.location.search);
@@ -97,8 +92,9 @@ function detectPlatformDetailed() {
97
92
  function detectPlatform() {
98
93
  return detectPlatformDetailed().platform;
99
94
  }
100
- function isChatGPT() {
101
- return detectPlatform() === "chatgpt";
95
+ function hasChatGPTExtensions() {
96
+ if (typeof window === "undefined") return false;
97
+ return Boolean(window["openai"]);
102
98
  }
103
99
  function isMCP() {
104
100
  return detectPlatform() === "mcp";
@@ -124,6 +120,48 @@ import {
124
120
  useEffect,
125
121
  useState
126
122
  } from "react";
123
+
124
+ // src/extensions/chatgpt.ts
125
+ function getChatGPTExtensions() {
126
+ if (typeof window === "undefined") return null;
127
+ const openai = window["openai"];
128
+ if (!openai || typeof openai !== "object") return null;
129
+ return openai;
130
+ }
131
+ function withExtensionsCapabilities(base, openai) {
132
+ return {
133
+ ...base,
134
+ widgetState: typeof openai.setWidgetState === "function",
135
+ fileUpload: typeof openai.uploadFile === "function",
136
+ fileDownload: typeof openai.getFileDownloadUrl === "function",
137
+ closeWidget: typeof openai.requestClose === "function",
138
+ modal: typeof openai.requestModal === "function"
139
+ };
140
+ }
141
+ function withChatGPTExtensions(bridge) {
142
+ const openai = getChatGPTExtensions();
143
+ if (!openai) return bridge;
144
+ bridge.capabilities = withExtensionsCapabilities(bridge.capabilities, openai);
145
+ if (typeof openai.setWidgetState === "function") {
146
+ bridge.setWidgetState = (state) => openai.setWidgetState?.(state);
147
+ }
148
+ bridge.getWidgetState = () => openai.widgetState ?? null;
149
+ if (typeof openai.uploadFile === "function") {
150
+ bridge.uploadFile = (file) => openai.uploadFile(file);
151
+ }
152
+ if (typeof openai.getFileDownloadUrl === "function") {
153
+ bridge.getFileDownloadUrl = (fileId) => openai.getFileDownloadUrl({ fileId });
154
+ }
155
+ if (typeof openai.requestClose === "function") {
156
+ bridge.requestClose = () => openai.requestClose?.();
157
+ }
158
+ if (typeof openai.requestModal === "function") {
159
+ bridge.requestModal = (options) => openai.requestModal(options);
160
+ }
161
+ return bridge;
162
+ }
163
+
164
+ // src/universal/provider.tsx
127
165
  import { jsx } from "react/jsx-runtime";
128
166
  var UniversalContext = createContext(null);
129
167
  var PlatformContext = createContext("unknown");
@@ -137,24 +175,25 @@ function UniversalProvider({
137
175
  const [ready, setReady] = useState(false);
138
176
  useEffect(() => {
139
177
  let cancelled = false;
140
- const detected = detectPlatform();
141
- setPlatform(detected);
142
- let newBridge;
143
- if (detected === "chatgpt") {
144
- newBridge = new ChatGPTBridge();
145
- } else if (detected === "mcp") {
146
- newBridge = new MCPBridge(appInfo, appCapabilities);
147
- } else {
178
+ const detectedPlatform = detectPlatform();
179
+ if (detectedPlatform !== "mcp") {
180
+ setBridge(null);
181
+ setPlatform("unknown");
148
182
  setReady(true);
149
183
  return;
150
184
  }
185
+ const newBridge = withChatGPTExtensions(
186
+ new MCPBridge(appInfo, appCapabilities)
187
+ );
151
188
  newBridge.connect().then(() => {
152
189
  if (cancelled) return;
153
190
  setBridge(newBridge);
191
+ setPlatform("mcp");
154
192
  setReady(true);
155
193
  }).catch((error) => {
156
194
  if (cancelled) return;
157
195
  console.error("[mcp-app-studio] Bridge connection failed:", error);
196
+ setPlatform("unknown");
158
197
  setReady(true);
159
198
  });
160
199
  return () => {
@@ -225,13 +264,11 @@ function useToolInput() {
225
264
  }
226
265
  function useToolInputPartial() {
227
266
  const bridge = useUniversalBridge();
228
- const platform = usePlatform();
229
267
  const [input, setInput] = useState2(null);
230
268
  useEffect2(() => {
231
- if (!bridge || platform !== "mcp") return;
232
- if (!bridge.onToolInputPartial) return;
269
+ if (!bridge?.onToolInputPartial) return;
233
270
  return bridge.onToolInputPartial((args) => setInput(args));
234
- }, [bridge, platform]);
271
+ }, [bridge]);
235
272
  return input;
236
273
  }
237
274
  function useToolResult() {
@@ -312,64 +349,51 @@ function useSendMessage() {
312
349
  }
313
350
  function useUpdateModelContext() {
314
351
  const bridge = useUniversalBridge();
315
- const platform = usePlatform();
316
352
  return useCallback(
317
353
  async (ctx) => {
318
- if (platform !== "mcp") {
354
+ if (!bridge?.updateModelContext) {
319
355
  console.warn(
320
- "useUpdateModelContext: This feature is only available on MCP hosts. On ChatGPT, consider using useWidgetState instead."
356
+ "useUpdateModelContext: updateModelContext not available on the current host."
321
357
  );
322
358
  return;
323
359
  }
324
- if (!bridge?.updateModelContext) {
325
- throw new Error(
326
- "updateModelContext not available on the current bridge."
327
- );
328
- }
329
360
  return bridge.updateModelContext(ctx);
330
361
  },
331
- [bridge, platform]
362
+ [bridge]
332
363
  );
333
364
  }
334
365
  function useWidgetState() {
335
366
  const bridge = useUniversalBridge();
336
- const platform = usePlatform();
337
367
  const [state, setState] = useState2(() => {
338
- if (platform !== "chatgpt" || !bridge?.getWidgetState) return null;
368
+ if (!bridge?.getWidgetState) return null;
339
369
  return bridge.getWidgetState();
340
370
  });
341
371
  const setWidgetState = useCallback(
342
372
  (newState) => {
343
- if (platform !== "chatgpt") {
373
+ if (!bridge?.setWidgetState) {
344
374
  console.warn(
345
- "useWidgetState: This feature is only available on ChatGPT. On MCP hosts, consider using useUpdateModelContext instead."
375
+ "useWidgetState: widget state is not available on the current host."
346
376
  );
347
377
  return;
348
378
  }
349
- if (!bridge?.setWidgetState) return;
350
379
  bridge.setWidgetState(newState);
351
380
  setState(newState);
352
381
  },
353
- [bridge, platform]
382
+ [bridge]
354
383
  );
355
384
  return [state, setWidgetState];
356
385
  }
357
386
  function useLog() {
358
387
  const bridge = useUniversalBridge();
359
- const platform = usePlatform();
360
388
  return useCallback(
361
389
  (level, data) => {
362
- if (platform !== "mcp") {
363
- console.warn(
364
- "useLog: Structured logging is only available on MCP hosts. Falling back to console.log."
365
- );
366
- console.log(`[${level}]`, data);
390
+ if (bridge?.sendLog) {
391
+ bridge.sendLog(level, data);
367
392
  return;
368
393
  }
369
- if (!bridge?.sendLog) return;
370
- bridge.sendLog(level, data);
394
+ console.log(`[${level}]`, data);
371
395
  },
372
- [bridge, platform]
396
+ [bridge]
373
397
  );
374
398
  }
375
399
  function useFeature(feature) {
@@ -377,16 +401,15 @@ function useFeature(feature) {
377
401
  return hasFeature(capabilities, feature);
378
402
  }
379
403
  export {
380
- CHATGPT_CAPABILITIES,
381
404
  MCP_CAPABILITIES,
382
405
  UniversalProvider,
383
406
  detectPlatform,
384
407
  detectPlatformDetailed,
385
408
  disableDebugMode,
386
409
  enableDebugMode,
410
+ hasChatGPTExtensions,
387
411
  hasFeature,
388
412
  imageBlock,
389
- isChatGPT,
390
413
  isMCP,
391
414
  textBlock,
392
415
  useCallTool,
@@ -404,5 +427,6 @@ export {
404
427
  useToolResult,
405
428
  useUniversalBridge,
406
429
  useUpdateModelContext,
407
- useWidgetState
430
+ useWidgetState,
431
+ withChatGPTExtensions
408
432
  };
@@ -1,5 +1,5 @@
1
- import { A as AppCapabilities } from '../../bridge-BXW_-p2R.js';
2
- export { M as MCPBridge, a as MCPBridgeOptions } from '../../bridge-BXW_-p2R.js';
1
+ import { A as AppCapabilities } from '../../bridge-BKLezf-H.js';
2
+ export { M as MCPBridge, a as MCPBridgeOptions } from '../../bridge-BKLezf-H.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode } from 'react';
5
5
  import { ToolResult, DisplayMode, HostContext, ContentBlock } from '../../core/index.js';
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MCPBridge
3
- } from "../../chunk-2OPSDEPI.js";
4
- import "../../chunk-4LAH4JH6.js";
3
+ } from "../../chunk-IEZAKOIG.js";
4
+ import "../../chunk-QNH5NSRH.js";
5
5
 
6
6
  // src/platforms/mcp/hooks.tsx
7
7
  import {
@@ -23,7 +23,17 @@ function MCPProvider({
23
23
  const [bridge] = useState(() => new MCPBridge(appInfo, appCapabilities));
24
24
  const [ready, setReady] = useState(false);
25
25
  useEffect(() => {
26
- bridge.connect().then(() => setReady(true));
26
+ let cancelled = false;
27
+ bridge.connect().catch((error) => {
28
+ if (cancelled) return;
29
+ console.error("[mcp-app-studio] Bridge connection failed:", error);
30
+ }).finally(() => {
31
+ if (cancelled) return;
32
+ setReady(true);
33
+ });
34
+ return () => {
35
+ cancelled = true;
36
+ };
27
37
  }, [bridge]);
28
38
  if (!ready) return null;
29
39
  return /* @__PURE__ */ jsx(MCPContext.Provider, { value: bridge, children });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mcp-app-studio",
3
- "version": "0.5.1",
4
- "description": "Build interactive apps for AI assistants (ChatGPT, Claude, MCP hosts)",
3
+ "version": "0.6.0",
4
+ "description": "Build interactive apps for MCP Apps hosts (ChatGPT, Claude Desktop, etc.)",
5
5
  "keywords": [
6
6
  "chatgpt",
7
7
  "openai",
@@ -42,12 +42,6 @@
42
42
  "default": "./dist/core/index.js"
43
43
  }
44
44
  },
45
- "./chatgpt": {
46
- "import": {
47
- "types": "./dist/platforms/chatgpt/index.d.ts",
48
- "default": "./dist/platforms/chatgpt/index.js"
49
- }
50
- },
51
45
  "./mcp": {
52
46
  "import": {
53
47
  "types": "./dist/platforms/mcp/index.d.ts",
@@ -82,9 +76,11 @@
82
76
  "@modelcontextprotocol/ext-apps": "^1.0.1",
83
77
  "@types/node": "^25.2.0",
84
78
  "@types/react": "^19.2.10",
79
+ "@types/react-dom": "^19.2.0",
85
80
  "@types/tar": "^6.1.13",
86
81
  "@vitest/coverage-v8": "^4.0.18",
87
82
  "react": "^19.2.4",
83
+ "react-dom": "^19.2.4",
88
84
  "tsup": "^8.5.1",
89
85
  "tsx": "^4.21.0",
90
86
  "typescript": "^5.9.3",