Files
hyperframes/packages/studio/package.json
Miguel Ángel 097d901d70 feat(studio): fall back to a WebMCP polyfill where the browser has none (#3514)
* feat(studio): fall back to a WebMCP polyfill where the browser has none

WebMCP is an Origin Trial. Chrome 149 and Edge 150 have it behind a flag,
ChatGPT Desktop ships it, and everything else does not. Without a fallback the
tools registered in the previous change are invisible on stable Chrome, which
is exactly where a bridge extension would connect from.

Adds `@mcp-b/global` (MIT) as a DYNAMIC import, so a browser with native
support never fetches it. Verified in the build output rather than asserted:
the bundle keeps a bare `import("@mcp-b/global")` instead of inlining it.

Chosen over the smaller `@mcp-b/webmcp-polyfill` because that one only defines
`document.modelContext`. `@mcp-b/global` also stands up the in-page MCP server
a bridge extension attaches to, and serving that case is the only reason the
fallback exists at all.

The load is guarded by a module-level promise so two mounts racing share one
load, and an import failure is caught and logged rather than thrown: a missing
agent surface must never stop Studio booting. The registration path re-checks
the abort signal after the await, so unmounting mid-import registers nothing.

Two things the type checker forced, both worth keeping:

Installing the package brings its own global `Document.modelContext`
declaration, which collided with the local one. Studio now reads the property
through a type guard instead of augmenting `Document`, so there is only one
declaration of that global and it is the package's.

Studio keeps its own narrow tool types rather than importing the package's.
Theirs overload `registerTool` to infer argument types from a literal
`inputSchema`, which helps when registering one tool inline and fights a
uniform registration loop. The comment in `types.ts` says so, and names the
drift risk that choice accepts.

The polyfill test asserts promise identity rather than counting imports. The
ESM registry dedupes the import either way, so a call count would pass whether
or not the guard existed.

* fix(studio): observe and retry WebMCP fallback
2026-08-27 00:07:45 -04:00

106 lines
3.0 KiB
JSON

{
"name": "@hyperframes/studio",
"version": "0.8.16",
"description": "",
"repository": {
"type": "git",
"url": "https://github.com/heygen-com/hyperframes",
"directory": "packages/studio"
},
"files": [
"src",
"dist"
],
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": {
"browser": "./src/index.ts",
"bun": "./src/index.ts",
"import": "./src/index.ts",
"types": "./src/index.ts"
},
"./tailwind-preset": {
"bun": "./src/styles/tailwind-preset.ts",
"import": "./src/styles/tailwind-preset.ts",
"types": "./src/styles/tailwind-preset.ts"
},
"./package.json": "./package.json"
},
"publishConfig": {
"access": "public",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./tailwind-preset": {
"import": "./dist/styles/tailwind-preset.js",
"types": "./dist/styles/tailwind-preset.d.ts"
},
"./package.json": "./package.json"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"scripts": {
"dev": "bun --bun ./node_modules/.bin/vite --host 127.0.0.1",
"build": "vite build && tsup",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"test:timeline-virtualization": "TIMELINE_ROW_VIRTUALIZATION=on TIMELINE_ELEMENT_COUNT=50000 node tests/e2e/timeline-virtualization.mjs",
"test:watch": "vitest",
"report:sdk-cutover": "bun src/utils/sdkCutoverPolicy.report.ts",
"test:timeline-default": "bun run test:timeline-virtualization"
},
"dependencies": {
"@codemirror/autocomplete": "^6.20.1",
"@codemirror/commands": "^6.10.3",
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-markdown": "^6.3.4",
"@codemirror/language": "^6.12.2",
"@codemirror/search": "^6.6.0",
"@codemirror/state": "^6.6.0",
"@codemirror/theme-one-dark": "^6.1.2",
"@codemirror/view": "6.40.0",
"@hyperframes/core": "workspace:*",
"@hyperframes/parsers": "workspace:*",
"@hyperframes/player": "workspace:*",
"@hyperframes/sdk": "workspace:*",
"@hyperframes/studio-server": "workspace:*",
"@mcp-b/global": "^5.0.1",
"@phosphor-icons/react": "^2.1.10",
"@tanstack/react-virtual": "^3.14.6",
"bpm-detective": "^2.0.5",
"dompurify": "^3.2.4",
"gsap": "^3.13.0",
"marked": "^14.1.4",
"mediabunny": "^1.45.3"
},
"devDependencies": {
"@hyperframes/producer": "workspace:*",
"@types/react": "19",
"@types/react-dom": "19",
"@vitejs/plugin-react": "^4.0.0",
"autoprefixer": "^10.4.0",
"chokidar": "^4.0.3",
"fake-indexeddb": "^6.2.5",
"postcss": "^8.4.0",
"puppeteer-core": "^25.2.1",
"tailwindcss": "^3.4.0",
"tsup": "^8.0.0",
"typescript": "^5.0.0",
"vite": "^6.4.2",
"vitest": "^3.2.4",
"zustand": "^5.0.0"
},
"peerDependencies": {
"react": "19",
"react-dom": "19",
"zustand": "^4.0.0 || ^5.0.0"
}
}