skybridge 0.0.0-dev.d9ee984 → 0.0.0-dev.da106ed

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.
Files changed (518) hide show
  1. package/LICENSE +21 -674
  2. package/README.md +153 -2
  3. package/bin/run.js +5 -0
  4. package/dist/cli/build-helpers.d.ts +11 -0
  5. package/dist/cli/build-helpers.js +107 -0
  6. package/dist/cli/build-helpers.js.map +1 -0
  7. package/dist/cli/build-helpers.test.d.ts +1 -0
  8. package/dist/cli/build-helpers.test.js +132 -0
  9. package/dist/cli/build-helpers.test.js.map +1 -0
  10. package/dist/cli/build-steps.d.ts +2 -0
  11. package/dist/cli/build-steps.js +73 -0
  12. package/dist/cli/build-steps.js.map +1 -0
  13. package/dist/cli/build-steps.test.d.ts +1 -0
  14. package/dist/cli/build-steps.test.js +52 -0
  15. package/dist/cli/build-steps.test.js.map +1 -0
  16. package/dist/cli/detect-port.d.ts +18 -0
  17. package/dist/cli/detect-port.js +50 -0
  18. package/dist/cli/detect-port.js.map +1 -0
  19. package/dist/cli/header.d.ts +4 -0
  20. package/dist/cli/header.js +6 -0
  21. package/dist/cli/header.js.map +1 -0
  22. package/dist/cli/resolve-skybridge-config.d.ts +5 -0
  23. package/dist/cli/resolve-skybridge-config.js +18 -0
  24. package/dist/cli/resolve-skybridge-config.js.map +1 -0
  25. package/dist/cli/run-command.d.ts +2 -0
  26. package/dist/cli/run-command.js +43 -0
  27. package/dist/cli/run-command.js.map +1 -0
  28. package/dist/cli/run-plain.d.ts +18 -0
  29. package/dist/cli/run-plain.js +89 -0
  30. package/dist/cli/run-plain.js.map +1 -0
  31. package/dist/cli/telemetry.d.ts +7 -0
  32. package/dist/cli/telemetry.js +123 -0
  33. package/dist/cli/telemetry.js.map +1 -0
  34. package/dist/cli/tunnel-control-server.d.ts +9 -0
  35. package/dist/cli/tunnel-control-server.js +31 -0
  36. package/dist/cli/tunnel-control-server.js.map +1 -0
  37. package/dist/cli/tunnel-control-server.test.d.ts +1 -0
  38. package/dist/cli/tunnel-control-server.test.js +39 -0
  39. package/dist/cli/tunnel-control-server.test.js.map +1 -0
  40. package/dist/cli/tunnel-handler.d.ts +3 -0
  41. package/dist/cli/tunnel-handler.js +48 -0
  42. package/dist/cli/tunnel-handler.js.map +1 -0
  43. package/dist/cli/tunnel-handler.test.d.ts +1 -0
  44. package/dist/cli/tunnel-handler.test.js +105 -0
  45. package/dist/cli/tunnel-handler.test.js.map +1 -0
  46. package/dist/cli/tunnel.d.ts +57 -0
  47. package/dist/cli/tunnel.js +154 -0
  48. package/dist/cli/tunnel.js.map +1 -0
  49. package/dist/cli/tunnel.test.d.ts +1 -0
  50. package/dist/cli/tunnel.test.js +190 -0
  51. package/dist/cli/tunnel.test.js.map +1 -0
  52. package/dist/cli/types.d.ts +5 -0
  53. package/dist/cli/types.js +2 -0
  54. package/dist/cli/types.js.map +1 -0
  55. package/dist/cli/use-execute-steps.d.ts +11 -0
  56. package/dist/cli/use-execute-steps.js +36 -0
  57. package/dist/cli/use-execute-steps.js.map +1 -0
  58. package/dist/cli/use-messages.d.ts +3 -0
  59. package/dist/cli/use-messages.js +11 -0
  60. package/dist/cli/use-messages.js.map +1 -0
  61. package/dist/cli/use-nodemon.d.ts +16 -0
  62. package/dist/cli/use-nodemon.js +84 -0
  63. package/dist/cli/use-nodemon.js.map +1 -0
  64. package/dist/cli/use-open-browser.d.ts +1 -0
  65. package/dist/cli/use-open-browser.js +44 -0
  66. package/dist/cli/use-open-browser.js.map +1 -0
  67. package/dist/cli/use-tunnel.d.ts +14 -0
  68. package/dist/cli/use-tunnel.js +131 -0
  69. package/dist/cli/use-tunnel.js.map +1 -0
  70. package/dist/cli/use-typescript-check.d.ts +15 -0
  71. package/dist/cli/use-typescript-check.js +97 -0
  72. package/dist/cli/use-typescript-check.js.map +1 -0
  73. package/dist/commands/build.d.ts +6 -0
  74. package/dist/commands/build.js +31 -0
  75. package/dist/commands/build.js.map +1 -0
  76. package/dist/commands/create.d.ts +9 -0
  77. package/dist/commands/create.js +30 -0
  78. package/dist/commands/create.js.map +1 -0
  79. package/dist/commands/dev.d.ts +13 -0
  80. package/dist/commands/dev.js +128 -0
  81. package/dist/commands/dev.js.map +1 -0
  82. package/dist/commands/start.d.ts +9 -0
  83. package/dist/commands/start.js +55 -0
  84. package/dist/commands/start.js.map +1 -0
  85. package/dist/commands/telemetry/disable.d.ts +5 -0
  86. package/dist/commands/telemetry/disable.js +14 -0
  87. package/dist/commands/telemetry/disable.js.map +1 -0
  88. package/dist/commands/telemetry/enable.d.ts +5 -0
  89. package/dist/commands/telemetry/enable.js +14 -0
  90. package/dist/commands/telemetry/enable.js.map +1 -0
  91. package/dist/commands/telemetry/status.d.ts +5 -0
  92. package/dist/commands/telemetry/status.js +14 -0
  93. package/dist/commands/telemetry/status.js.map +1 -0
  94. package/dist/context-warnings.d.ts +5 -0
  95. package/dist/context-warnings.js +31 -0
  96. package/dist/context-warnings.js.map +1 -0
  97. package/dist/context-warnings.test.d.ts +1 -0
  98. package/dist/context-warnings.test.js +28 -0
  99. package/dist/context-warnings.test.js.map +1 -0
  100. package/dist/server/asset-base-url-transform-plugin.d.ts +11 -0
  101. package/dist/server/asset-base-url-transform-plugin.js +48 -0
  102. package/dist/server/asset-base-url-transform-plugin.js.map +1 -0
  103. package/dist/server/asset-base-url-transform-plugin.test.d.ts +1 -0
  104. package/dist/server/asset-base-url-transform-plugin.test.js +134 -0
  105. package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -0
  106. package/dist/server/auth/discovery.d.ts +32 -0
  107. package/dist/server/auth/discovery.js +56 -0
  108. package/dist/server/auth/discovery.js.map +1 -0
  109. package/dist/server/auth/discovery.test.d.ts +1 -0
  110. package/dist/server/auth/discovery.test.js +93 -0
  111. package/dist/server/auth/discovery.test.js.map +1 -0
  112. package/dist/server/auth/index.d.ts +44 -0
  113. package/dist/server/auth/index.js +2 -0
  114. package/dist/server/auth/index.js.map +1 -0
  115. package/dist/server/auth/providers/auth0.d.ts +40 -0
  116. package/dist/server/auth/providers/auth0.js +31 -0
  117. package/dist/server/auth/providers/auth0.js.map +1 -0
  118. package/dist/server/auth/providers/auth0.test.d.ts +1 -0
  119. package/dist/server/auth/providers/auth0.test.js +50 -0
  120. package/dist/server/auth/providers/auth0.test.js.map +1 -0
  121. package/dist/server/auth/providers/authplane.d.ts +49 -0
  122. package/dist/server/auth/providers/authplane.js +57 -0
  123. package/dist/server/auth/providers/authplane.js.map +1 -0
  124. package/dist/server/auth/providers/authplane.test.d.ts +1 -0
  125. package/dist/server/auth/providers/authplane.test.js +140 -0
  126. package/dist/server/auth/providers/authplane.test.js.map +1 -0
  127. package/dist/server/auth/providers/clerk.d.ts +51 -0
  128. package/dist/server/auth/providers/clerk.js +19 -0
  129. package/dist/server/auth/providers/clerk.js.map +1 -0
  130. package/dist/server/auth/providers/clerk.test.d.ts +1 -0
  131. package/dist/server/auth/providers/clerk.test.js +30 -0
  132. package/dist/server/auth/providers/clerk.test.js.map +1 -0
  133. package/dist/server/auth/providers/custom.d.ts +40 -0
  134. package/dist/server/auth/providers/custom.js +56 -0
  135. package/dist/server/auth/providers/custom.js.map +1 -0
  136. package/dist/server/auth/providers/custom.test.d.ts +1 -0
  137. package/dist/server/auth/providers/custom.test.js +158 -0
  138. package/dist/server/auth/providers/custom.test.js.map +1 -0
  139. package/dist/server/auth/providers/descope.d.ts +44 -0
  140. package/dist/server/auth/providers/descope.js +44 -0
  141. package/dist/server/auth/providers/descope.js.map +1 -0
  142. package/dist/server/auth/providers/descope.test.d.ts +1 -0
  143. package/dist/server/auth/providers/descope.test.js +62 -0
  144. package/dist/server/auth/providers/descope.test.js.map +1 -0
  145. package/dist/server/auth/providers/shared.d.ts +2 -0
  146. package/dist/server/auth/providers/shared.js +6 -0
  147. package/dist/server/auth/providers/shared.js.map +1 -0
  148. package/dist/server/auth/providers/shared.test.d.ts +1 -0
  149. package/dist/server/auth/providers/shared.test.js +10 -0
  150. package/dist/server/auth/providers/shared.test.js.map +1 -0
  151. package/dist/server/auth/providers/stytch.d.ts +25 -0
  152. package/dist/server/auth/providers/stytch.js +16 -0
  153. package/dist/server/auth/providers/stytch.js.map +1 -0
  154. package/dist/server/auth/providers/verify-spy.d.ts +12 -0
  155. package/dist/server/auth/providers/verify-spy.js +21 -0
  156. package/dist/server/auth/providers/verify-spy.js.map +1 -0
  157. package/dist/server/auth/providers/workos.d.ts +33 -0
  158. package/dist/server/auth/providers/workos.js +15 -0
  159. package/dist/server/auth/providers/workos.js.map +1 -0
  160. package/dist/server/auth/security-schemes.d.ts +23 -0
  161. package/dist/server/auth/security-schemes.js +64 -0
  162. package/dist/server/auth/security-schemes.js.map +1 -0
  163. package/dist/server/auth/security-schemes.test.d.ts +1 -0
  164. package/dist/server/auth/security-schemes.test.js +99 -0
  165. package/dist/server/auth/security-schemes.test.js.map +1 -0
  166. package/dist/server/auth/setup.d.ts +6 -0
  167. package/dist/server/auth/setup.js +92 -0
  168. package/dist/server/auth/setup.js.map +1 -0
  169. package/dist/server/auth/setup.test.d.ts +1 -0
  170. package/dist/server/auth/setup.test.js +508 -0
  171. package/dist/server/auth/setup.test.js.map +1 -0
  172. package/dist/server/auth/verify.d.ts +46 -0
  173. package/dist/server/auth/verify.js +62 -0
  174. package/dist/server/auth/verify.js.map +1 -0
  175. package/dist/server/auth/verify.test.d.ts +1 -0
  176. package/dist/server/auth/verify.test.js +149 -0
  177. package/dist/server/auth/verify.test.js.map +1 -0
  178. package/dist/server/auth-extra.test-d.d.ts +1 -0
  179. package/dist/server/auth-extra.test-d.js +82 -0
  180. package/dist/server/auth-extra.test-d.js.map +1 -0
  181. package/dist/server/auth.d.ts +58 -0
  182. package/dist/server/auth.js +28 -0
  183. package/dist/server/auth.js.map +1 -0
  184. package/dist/server/build-manifest.test.d.ts +1 -0
  185. package/dist/server/build-manifest.test.js +27 -0
  186. package/dist/server/build-manifest.test.js.map +1 -0
  187. package/dist/server/content-helpers.d.ts +67 -0
  188. package/dist/server/content-helpers.js +79 -0
  189. package/dist/server/content-helpers.js.map +1 -0
  190. package/dist/server/content-helpers.test.d.ts +1 -0
  191. package/dist/server/content-helpers.test.js +70 -0
  192. package/dist/server/content-helpers.test.js.map +1 -0
  193. package/dist/server/express.d.ts +11 -0
  194. package/dist/server/express.js +101 -0
  195. package/dist/server/express.js.map +1 -0
  196. package/dist/server/express.test.d.ts +1 -0
  197. package/dist/server/express.test.js +491 -0
  198. package/dist/server/express.test.js.map +1 -0
  199. package/dist/server/file-ref.d.ts +28 -0
  200. package/dist/server/file-ref.js +27 -0
  201. package/dist/server/file-ref.js.map +1 -0
  202. package/dist/server/host.d.ts +2 -0
  203. package/dist/server/host.js +11 -0
  204. package/dist/server/host.js.map +1 -0
  205. package/dist/server/host.test.d.ts +1 -0
  206. package/dist/server/host.test.js +13 -0
  207. package/dist/server/host.test.js.map +1 -0
  208. package/dist/server/index.d.ts +19 -3
  209. package/dist/server/index.js +15 -3
  210. package/dist/server/index.js.map +1 -1
  211. package/dist/server/inferUtilityTypes.d.ts +64 -0
  212. package/dist/server/inferUtilityTypes.js +2 -0
  213. package/dist/server/inferUtilityTypes.js.map +1 -0
  214. package/dist/server/metric.d.ts +14 -0
  215. package/dist/server/metric.js +62 -0
  216. package/dist/server/metric.js.map +1 -0
  217. package/dist/server/middleware.d.ts +162 -3
  218. package/dist/server/middleware.js +124 -45
  219. package/dist/server/middleware.js.map +1 -1
  220. package/dist/server/middleware.test-d.d.ts +1 -0
  221. package/dist/server/middleware.test-d.js +75 -0
  222. package/dist/server/middleware.test-d.js.map +1 -0
  223. package/dist/server/middleware.test.d.ts +1 -0
  224. package/dist/server/middleware.test.js +529 -0
  225. package/dist/server/middleware.test.js.map +1 -0
  226. package/dist/server/requestOrigin.d.ts +7 -0
  227. package/dist/server/requestOrigin.js +25 -0
  228. package/dist/server/requestOrigin.js.map +1 -0
  229. package/dist/server/server.d.ts +463 -10
  230. package/dist/server/server.js +729 -57
  231. package/dist/server/server.js.map +1 -1
  232. package/dist/server/skills-integration.test.d.ts +1 -0
  233. package/dist/server/skills-integration.test.js +79 -0
  234. package/dist/server/skills-integration.test.js.map +1 -0
  235. package/dist/server/skills.d.ts +29 -0
  236. package/dist/server/skills.js +195 -0
  237. package/dist/server/skills.js.map +1 -0
  238. package/dist/server/skills.test.d.ts +1 -0
  239. package/dist/server/skills.test.js +247 -0
  240. package/dist/server/skills.test.js.map +1 -0
  241. package/dist/server/templateHelper.d.ts +16 -0
  242. package/dist/server/templateHelper.js +11 -0
  243. package/dist/server/templateHelper.js.map +1 -0
  244. package/dist/server/templates.generated.d.ts +4 -0
  245. package/dist/server/templates.generated.js +47 -0
  246. package/dist/server/templates.generated.js.map +1 -0
  247. package/dist/server/tunnel-proxy-router.d.ts +7 -0
  248. package/dist/server/tunnel-proxy-router.js +110 -0
  249. package/dist/server/tunnel-proxy-router.js.map +1 -0
  250. package/dist/server/tunnel-proxy-router.test.d.ts +1 -0
  251. package/dist/server/tunnel-proxy-router.test.js +229 -0
  252. package/dist/server/tunnel-proxy-router.test.js.map +1 -0
  253. package/dist/server/view-name.test-d.d.ts +1 -0
  254. package/dist/server/view-name.test-d.js +8 -0
  255. package/dist/server/view-name.test-d.js.map +1 -0
  256. package/dist/server/view-resource-resolution.test.d.ts +6 -0
  257. package/dist/server/view-resource-resolution.test.js +171 -0
  258. package/dist/server/view-resource-resolution.test.js.map +1 -0
  259. package/dist/server/viewsDevServer.d.ts +14 -0
  260. package/dist/server/viewsDevServer.js +48 -0
  261. package/dist/server/viewsDevServer.js.map +1 -0
  262. package/dist/test/utils.d.ts +127 -0
  263. package/dist/test/utils.js +247 -0
  264. package/dist/test/utils.js.map +1 -0
  265. package/dist/test/view.test.d.ts +1 -0
  266. package/dist/test/view.test.js +552 -0
  267. package/dist/test/view.test.js.map +1 -0
  268. package/dist/version.d.ts +1 -0
  269. package/dist/version.js +3 -0
  270. package/dist/version.js.map +1 -0
  271. package/dist/web/bridges/adaptor.d.ts +51 -0
  272. package/dist/web/bridges/adaptor.js +327 -0
  273. package/dist/web/bridges/adaptor.js.map +1 -0
  274. package/dist/web/bridges/adaptor.test.d.ts +1 -0
  275. package/dist/web/bridges/adaptor.test.js +225 -0
  276. package/dist/web/bridges/adaptor.test.js.map +1 -0
  277. package/dist/web/bridges/apps-sdk/bridge.d.ts +15 -0
  278. package/dist/web/bridges/apps-sdk/bridge.js +58 -0
  279. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -0
  280. package/dist/web/bridges/apps-sdk/index.d.ts +4 -0
  281. package/dist/web/bridges/apps-sdk/index.js +4 -0
  282. package/dist/web/bridges/apps-sdk/index.js.map +1 -0
  283. package/dist/web/bridges/apps-sdk/types.d.ts +133 -0
  284. package/dist/web/bridges/apps-sdk/types.js +10 -0
  285. package/dist/web/bridges/apps-sdk/types.js.map +1 -0
  286. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +13 -0
  287. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +18 -0
  288. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -0
  289. package/dist/web/bridges/get-adaptor.d.ts +9 -0
  290. package/dist/web/bridges/get-adaptor.js +10 -0
  291. package/dist/web/bridges/get-adaptor.js.map +1 -0
  292. package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
  293. package/dist/web/bridges/get-adaptor.test.js +32 -0
  294. package/dist/web/bridges/get-adaptor.test.js.map +1 -0
  295. package/dist/web/bridges/index.d.ts +5 -0
  296. package/dist/web/bridges/index.js +6 -0
  297. package/dist/web/bridges/index.js.map +1 -0
  298. package/dist/web/bridges/mcp-app/bridge.d.ts +39 -0
  299. package/dist/web/bridges/mcp-app/bridge.js +168 -0
  300. package/dist/web/bridges/mcp-app/bridge.js.map +1 -0
  301. package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
  302. package/dist/web/bridges/mcp-app/bridge.test.js +17 -0
  303. package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
  304. package/dist/web/bridges/mcp-app/index.d.ts +3 -0
  305. package/dist/web/bridges/mcp-app/index.js +3 -0
  306. package/dist/web/bridges/mcp-app/index.js.map +1 -0
  307. package/dist/web/bridges/mcp-app/types.d.ts +10 -0
  308. package/dist/web/bridges/mcp-app/types.js +2 -0
  309. package/dist/web/bridges/mcp-app/types.js.map +1 -0
  310. package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +19 -0
  311. package/dist/web/bridges/mcp-app/use-mcp-app-context.js +19 -0
  312. package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -0
  313. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.d.ts +1 -0
  314. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +26 -0
  315. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -0
  316. package/dist/web/bridges/mcp-app/view-tools.test.d.ts +1 -0
  317. package/dist/web/bridges/mcp-app/view-tools.test.js +143 -0
  318. package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
  319. package/dist/web/bridges/types.d.ts +214 -0
  320. package/dist/web/bridges/types.js +15 -0
  321. package/dist/web/bridges/types.js.map +1 -0
  322. package/dist/web/bridges/types.test.d.ts +1 -0
  323. package/dist/web/bridges/types.test.js +19 -0
  324. package/dist/web/bridges/types.test.js.map +1 -0
  325. package/dist/web/bridges/use-host-context.d.ts +7 -0
  326. package/dist/web/bridges/use-host-context.js +13 -0
  327. package/dist/web/bridges/use-host-context.js.map +1 -0
  328. package/dist/web/components/modal-provider.d.ts +4 -0
  329. package/dist/web/components/modal-provider.js +46 -0
  330. package/dist/web/components/modal-provider.js.map +1 -0
  331. package/dist/web/create-store.d.ts +29 -0
  332. package/dist/web/create-store.js +64 -0
  333. package/dist/web/create-store.js.map +1 -0
  334. package/dist/web/create-store.test.d.ts +1 -0
  335. package/dist/web/create-store.test.js +132 -0
  336. package/dist/web/create-store.test.js.map +1 -0
  337. package/dist/web/data-llm.d.ts +47 -0
  338. package/dist/web/data-llm.js +100 -0
  339. package/dist/web/data-llm.js.map +1 -0
  340. package/dist/web/data-llm.test.d.ts +1 -0
  341. package/dist/web/data-llm.test.js +155 -0
  342. package/dist/web/data-llm.test.js.map +1 -0
  343. package/dist/web/generate-helpers.d.ts +120 -0
  344. package/dist/web/generate-helpers.js +115 -0
  345. package/dist/web/generate-helpers.js.map +1 -0
  346. package/dist/web/generate-helpers.test-d.d.ts +1 -0
  347. package/dist/web/generate-helpers.test-d.js +211 -0
  348. package/dist/web/generate-helpers.test-d.js.map +1 -0
  349. package/dist/web/generate-helpers.test.d.ts +1 -0
  350. package/dist/web/generate-helpers.test.js +17 -0
  351. package/dist/web/generate-helpers.test.js.map +1 -0
  352. package/dist/web/helpers/state.d.ts +7 -0
  353. package/dist/web/helpers/state.js +45 -0
  354. package/dist/web/helpers/state.js.map +1 -0
  355. package/dist/web/helpers/state.test.d.ts +1 -0
  356. package/dist/web/helpers/state.test.js +53 -0
  357. package/dist/web/helpers/state.test.js.map +1 -0
  358. package/dist/web/hooks/index.d.ts +16 -0
  359. package/dist/web/hooks/index.js +17 -0
  360. package/dist/web/hooks/index.js.map +1 -0
  361. package/dist/web/hooks/test/utils.d.ts +21 -0
  362. package/dist/web/hooks/test/utils.js +75 -0
  363. package/dist/web/hooks/test/utils.js.map +1 -0
  364. package/dist/web/hooks/use-call-tool.d.ts +146 -0
  365. package/dist/web/hooks/use-call-tool.js +96 -0
  366. package/dist/web/hooks/use-call-tool.js.map +1 -0
  367. package/dist/web/hooks/use-call-tool.test-d.d.ts +1 -0
  368. package/dist/web/hooks/use-call-tool.test-d.js +104 -0
  369. package/dist/web/hooks/use-call-tool.test-d.js.map +1 -0
  370. package/dist/web/hooks/use-call-tool.test.d.ts +1 -0
  371. package/dist/web/hooks/use-call-tool.test.js +225 -0
  372. package/dist/web/hooks/use-call-tool.test.js.map +1 -0
  373. package/dist/web/hooks/use-display-mode.d.ts +24 -0
  374. package/dist/web/hooks/use-display-mode.js +29 -0
  375. package/dist/web/hooks/use-display-mode.js.map +1 -0
  376. package/dist/web/hooks/use-display-mode.test-d.d.ts +1 -0
  377. package/dist/web/hooks/use-display-mode.test-d.js +8 -0
  378. package/dist/web/hooks/use-display-mode.test-d.js.map +1 -0
  379. package/dist/web/hooks/use-display-mode.test.d.ts +1 -0
  380. package/dist/web/hooks/use-display-mode.test.js +77 -0
  381. package/dist/web/hooks/use-display-mode.test.js.map +1 -0
  382. package/dist/web/hooks/use-download.d.ts +5 -0
  383. package/dist/web/hooks/use-download.js +8 -0
  384. package/dist/web/hooks/use-download.js.map +1 -0
  385. package/dist/web/hooks/use-download.test.d.ts +1 -0
  386. package/dist/web/hooks/use-download.test.js +103 -0
  387. package/dist/web/hooks/use-download.test.js.map +1 -0
  388. package/dist/web/hooks/use-files.d.ts +39 -0
  389. package/dist/web/hooks/use-files.js +42 -0
  390. package/dist/web/hooks/use-files.js.map +1 -0
  391. package/dist/web/hooks/use-files.test.d.ts +1 -0
  392. package/dist/web/hooks/use-files.test.js +62 -0
  393. package/dist/web/hooks/use-files.test.js.map +1 -0
  394. package/dist/web/hooks/use-host-info.d.ts +22 -0
  395. package/dist/web/hooks/use-host-info.js +30 -0
  396. package/dist/web/hooks/use-host-info.js.map +1 -0
  397. package/dist/web/hooks/use-host-info.test.d.ts +1 -0
  398. package/dist/web/hooks/use-host-info.test.js +59 -0
  399. package/dist/web/hooks/use-host-info.test.js.map +1 -0
  400. package/dist/web/hooks/use-layout.d.ts +24 -0
  401. package/dist/web/hooks/use-layout.js +25 -0
  402. package/dist/web/hooks/use-layout.js.map +1 -0
  403. package/dist/web/hooks/use-layout.test.d.ts +1 -0
  404. package/dist/web/hooks/use-layout.test.js +129 -0
  405. package/dist/web/hooks/use-layout.test.js.map +1 -0
  406. package/dist/web/hooks/use-open-external.d.ts +20 -0
  407. package/dist/web/hooks/use-open-external.js +24 -0
  408. package/dist/web/hooks/use-open-external.js.map +1 -0
  409. package/dist/web/hooks/use-open-external.test.d.ts +1 -0
  410. package/dist/web/hooks/use-open-external.test.js +75 -0
  411. package/dist/web/hooks/use-open-external.test.js.map +1 -0
  412. package/dist/web/hooks/use-register-view-tool.d.ts +38 -0
  413. package/dist/web/hooks/use-register-view-tool.js +50 -0
  414. package/dist/web/hooks/use-register-view-tool.js.map +1 -0
  415. package/dist/web/hooks/use-request-close.d.ts +16 -0
  416. package/dist/web/hooks/use-request-close.js +21 -0
  417. package/dist/web/hooks/use-request-close.js.map +1 -0
  418. package/dist/web/hooks/use-request-close.test.d.ts +1 -0
  419. package/dist/web/hooks/use-request-close.test.js +47 -0
  420. package/dist/web/hooks/use-request-close.test.js.map +1 -0
  421. package/dist/web/hooks/use-request-modal.d.ts +24 -0
  422. package/dist/web/hooks/use-request-modal.js +31 -0
  423. package/dist/web/hooks/use-request-modal.js.map +1 -0
  424. package/dist/web/hooks/use-request-modal.test.d.ts +1 -0
  425. package/dist/web/hooks/use-request-modal.test.js +71 -0
  426. package/dist/web/hooks/use-request-modal.test.js.map +1 -0
  427. package/dist/web/hooks/use-request-size.d.ts +20 -0
  428. package/dist/web/hooks/use-request-size.js +24 -0
  429. package/dist/web/hooks/use-request-size.js.map +1 -0
  430. package/dist/web/hooks/use-request-size.test.d.ts +1 -0
  431. package/dist/web/hooks/use-request-size.test.js +47 -0
  432. package/dist/web/hooks/use-request-size.test.js.map +1 -0
  433. package/dist/web/hooks/use-send-follow-up-message.d.ts +19 -0
  434. package/dist/web/hooks/use-send-follow-up-message.js +25 -0
  435. package/dist/web/hooks/use-send-follow-up-message.js.map +1 -0
  436. package/dist/web/hooks/use-set-open-in-app-url.d.ts +18 -0
  437. package/dist/web/hooks/use-set-open-in-app-url.js +25 -0
  438. package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -0
  439. package/dist/web/hooks/use-set-open-in-app-url.test.d.ts +1 -0
  440. package/dist/web/hooks/use-set-open-in-app-url.test.js +63 -0
  441. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -0
  442. package/dist/web/hooks/use-tool-info.d.ts +87 -0
  443. package/dist/web/hooks/use-tool-info.js +49 -0
  444. package/dist/web/hooks/use-tool-info.js.map +1 -0
  445. package/dist/web/hooks/use-tool-info.test-d.d.ts +1 -0
  446. package/dist/web/hooks/use-tool-info.test-d.js +91 -0
  447. package/dist/web/hooks/use-tool-info.test-d.js.map +1 -0
  448. package/dist/web/hooks/use-tool-info.test.d.ts +1 -0
  449. package/dist/web/hooks/use-tool-info.test.js +140 -0
  450. package/dist/web/hooks/use-tool-info.test.js.map +1 -0
  451. package/dist/web/hooks/use-user.d.ts +20 -0
  452. package/dist/web/hooks/use-user.js +37 -0
  453. package/dist/web/hooks/use-user.js.map +1 -0
  454. package/dist/web/hooks/use-user.test.d.ts +1 -0
  455. package/dist/web/hooks/use-user.test.js +168 -0
  456. package/dist/web/hooks/use-user.test.js.map +1 -0
  457. package/dist/web/hooks/use-view-state.d.ts +25 -0
  458. package/dist/web/hooks/use-view-state.js +42 -0
  459. package/dist/web/hooks/use-view-state.js.map +1 -0
  460. package/dist/web/hooks/use-view-state.test.d.ts +1 -0
  461. package/dist/web/hooks/use-view-state.test.js +212 -0
  462. package/dist/web/hooks/use-view-state.test.js.map +1 -0
  463. package/dist/web/index.d.ts +6 -3
  464. package/dist/web/index.js +6 -3
  465. package/dist/web/index.js.map +1 -1
  466. package/dist/web/mount-view.d.ts +20 -0
  467. package/dist/web/mount-view.js +46 -0
  468. package/dist/web/mount-view.js.map +1 -0
  469. package/dist/web/plugin/data-llm.test.d.ts +1 -0
  470. package/dist/web/plugin/data-llm.test.js +81 -0
  471. package/dist/web/plugin/data-llm.test.js.map +1 -0
  472. package/dist/web/plugin/plugin-build.test.d.ts +1 -0
  473. package/dist/web/plugin/plugin-build.test.js +54 -0
  474. package/dist/web/plugin/plugin-build.test.js.map +1 -0
  475. package/dist/web/plugin/plugin.d.ts +39 -0
  476. package/dist/web/plugin/plugin.js +200 -0
  477. package/dist/web/plugin/plugin.js.map +1 -0
  478. package/dist/web/plugin/plugin.test.d.ts +1 -0
  479. package/dist/web/plugin/plugin.test.js +61 -0
  480. package/dist/web/plugin/plugin.test.js.map +1 -0
  481. package/dist/web/plugin/scan-views.d.ts +16 -0
  482. package/dist/web/plugin/scan-views.js +88 -0
  483. package/dist/web/plugin/scan-views.js.map +1 -0
  484. package/dist/web/plugin/scan-views.test.d.ts +1 -0
  485. package/dist/web/plugin/scan-views.test.js +99 -0
  486. package/dist/web/plugin/scan-views.test.js.map +1 -0
  487. package/dist/web/plugin/transform-data-llm.d.ts +12 -0
  488. package/dist/web/plugin/transform-data-llm.js +96 -0
  489. package/dist/web/plugin/transform-data-llm.js.map +1 -0
  490. package/dist/web/plugin/transform-data-llm.test.d.ts +1 -0
  491. package/dist/web/plugin/transform-data-llm.test.js +81 -0
  492. package/dist/web/plugin/transform-data-llm.test.js.map +1 -0
  493. package/dist/web/plugin/validate-view.d.ts +1 -0
  494. package/dist/web/plugin/validate-view.js +9 -0
  495. package/dist/web/plugin/validate-view.js.map +1 -0
  496. package/dist/web/plugin/validate-view.test.d.ts +1 -0
  497. package/dist/web/plugin/validate-view.test.js +24 -0
  498. package/dist/web/plugin/validate-view.test.js.map +1 -0
  499. package/dist/web/proxy.d.ts +1 -0
  500. package/dist/web/proxy.js +52 -0
  501. package/dist/web/proxy.js.map +1 -0
  502. package/dist/web/types.d.ts +17 -92
  503. package/dist/web/types.js +1 -9
  504. package/dist/web/types.js.map +1 -1
  505. package/package.json +88 -21
  506. package/tsconfig.base.json +33 -0
  507. package/dist/server/widgetsDevServer.d.ts +0 -9
  508. package/dist/server/widgetsDevServer.js +0 -36
  509. package/dist/server/widgetsDevServer.js.map +0 -1
  510. package/dist/web/mount-widget.d.ts +0 -1
  511. package/dist/web/mount-widget.js +0 -10
  512. package/dist/web/mount-widget.js.map +0 -1
  513. package/dist/web/use-openai-global.d.ts +0 -2
  514. package/dist/web/use-openai-global.js +0 -21
  515. package/dist/web/use-openai-global.js.map +0 -1
  516. package/dist/web/use-tool-output.d.ts +0 -3
  517. package/dist/web/use-tool-output.js +0 -5
  518. package/dist/web/use-tool-output.js.map +0 -1
@@ -0,0 +1,212 @@
1
+ import { act, renderHook, waitFor } from "@testing-library/react";
2
+ import { StrictMode } from "react";
3
+ import { afterEach, beforeEach, describe, expect, it, vi, } from "vitest";
4
+ import { HostAdaptor } from "../bridges/adaptor.js";
5
+ import { McpAppBridge } from "../bridges/mcp-app/index.js";
6
+ import { fireToolResultNotification, getMcpAppHostPostMessageMock, MockResizeObserver, } from "./test/utils.js";
7
+ import { useViewState } from "./use-view-state.js";
8
+ describe("useViewState", () => {
9
+ let OpenaiMock;
10
+ beforeEach(() => {
11
+ OpenaiMock = {
12
+ widgetState: null,
13
+ setWidgetState: vi.fn().mockResolvedValue(undefined),
14
+ };
15
+ vi.stubGlobal("openai", OpenaiMock);
16
+ vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
17
+ });
18
+ afterEach(() => {
19
+ vi.unstubAllGlobals();
20
+ vi.resetAllMocks();
21
+ HostAdaptor.resetInstance();
22
+ McpAppBridge.resetInstance();
23
+ });
24
+ const defaultState = { count: 0, name: "test" };
25
+ const windowState = { count: 5, name: "window" };
26
+ it("should initialize with default state when window.openai.widgetState is null", () => {
27
+ OpenaiMock.widgetState = null;
28
+ const { result } = renderHook(() => useViewState(defaultState));
29
+ expect(result.current[0]).toEqual(defaultState);
30
+ });
31
+ it("should initialize with window.openai.widgetState when available", () => {
32
+ OpenaiMock.widgetState = { modelContent: windowState };
33
+ const { result } = renderHook(() => useViewState(defaultState));
34
+ expect(result.current[0]).toEqual(windowState);
35
+ });
36
+ it("should call window.openai.setWidgetState when setViewState is called with a new state", async () => {
37
+ const { result } = renderHook(() => useViewState(defaultState));
38
+ const newState = { count: 10, name: "updated" };
39
+ act(() => {
40
+ result.current[1](newState);
41
+ });
42
+ expect(OpenaiMock.setWidgetState).toHaveBeenCalledWith({
43
+ modelContent: newState,
44
+ privateContent: {},
45
+ });
46
+ expect(result.current[0]).toEqual(newState);
47
+ });
48
+ it("should call window.openai.setWidgetState when setViewState is called with a function updater", async () => {
49
+ const { result } = renderHook(() => useViewState(defaultState));
50
+ act(() => {
51
+ result.current[1]((prev) => ({ ...prev, count: prev.count + 1 }));
52
+ });
53
+ expect(OpenaiMock.setWidgetState).toHaveBeenCalledWith({
54
+ modelContent: { count: 1, name: "test" },
55
+ privateContent: {},
56
+ });
57
+ expect(result.current[0]).toEqual({ count: 1, name: "test" });
58
+ });
59
+ it("should call window.openai.setWidgetState once per update under StrictMode", () => {
60
+ const { result } = renderHook(() => useViewState(defaultState), {
61
+ wrapper: StrictMode,
62
+ });
63
+ act(() => {
64
+ result.current[1]({ count: 10, name: "updated" });
65
+ });
66
+ expect(OpenaiMock.setWidgetState).toHaveBeenCalledTimes(1);
67
+ });
68
+ it("should not persist anything on mount", () => {
69
+ renderHook(() => useViewState(defaultState), { wrapper: StrictMode });
70
+ expect(OpenaiMock.setWidgetState).not.toHaveBeenCalled();
71
+ });
72
+ it("should persist state when setViewState and unmount happen in the same tick", () => {
73
+ const { result, unmount } = renderHook(() => useViewState(defaultState));
74
+ act(() => {
75
+ result.current[1]({ count: 10, name: "updated" });
76
+ unmount();
77
+ });
78
+ expect(OpenaiMock.setWidgetState).toHaveBeenCalledTimes(1);
79
+ });
80
+ it("should not persist state when setViewState is called after unmount", () => {
81
+ const { result, unmount } = renderHook(() => useViewState(defaultState));
82
+ const setViewState = result.current[1];
83
+ unmount();
84
+ act(() => {
85
+ setViewState({ count: 10, name: "updated" });
86
+ });
87
+ expect(OpenaiMock.setWidgetState).not.toHaveBeenCalled();
88
+ });
89
+ it("should update state when window.openai.widgetState changes", () => {
90
+ OpenaiMock.widgetState = { modelContent: defaultState };
91
+ const { result, rerender } = renderHook(() => useViewState(defaultState));
92
+ expect(result.current[0]).toEqual(defaultState);
93
+ // Simulate window.openai.widgetState changing
94
+ OpenaiMock.widgetState = { modelContent: windowState };
95
+ // Trigger re-render to simulate the useEffect running
96
+ rerender();
97
+ expect(result.current[0]).toEqual(windowState);
98
+ });
99
+ });
100
+ describe("useViewState (mcp-app host — localStorage persistence)", () => {
101
+ beforeEach(() => {
102
+ vi.stubGlobal("parent", { postMessage: getMcpAppHostPostMessageMock() });
103
+ vi.stubGlobal("skybridge", { hostType: "mcp-app" });
104
+ vi.stubGlobal("openai", undefined);
105
+ vi.stubGlobal("ResizeObserver", MockResizeObserver);
106
+ localStorage.clear();
107
+ });
108
+ afterEach(() => {
109
+ vi.unstubAllGlobals();
110
+ vi.resetAllMocks();
111
+ HostAdaptor.resetInstance();
112
+ McpAppBridge.resetInstance();
113
+ localStorage.clear();
114
+ });
115
+ it("should persist state to localStorage when viewUUID is available", async () => {
116
+ const viewUUID = "test-uuid-123";
117
+ const { result } = renderHook(() => useViewState({ page: 1, zoom: 100 }));
118
+ await act(async () => {
119
+ fireToolResultNotification({
120
+ content: [{ type: "text", text: "result" }],
121
+ structuredContent: {},
122
+ _meta: { viewUUID },
123
+ });
124
+ });
125
+ act(() => {
126
+ result.current[1]({ page: 3, zoom: 150 });
127
+ });
128
+ expect(result.current[0]).toEqual({ page: 3, zoom: 150 });
129
+ expect(localStorage.length).toBe(1);
130
+ });
131
+ it("should restore state from localStorage when viewUUID arrives", async () => {
132
+ const viewUUID = "test-uuid-456";
133
+ // Pre-seed with the sb:{timestamp}:{viewUUID} format
134
+ localStorage.setItem(`sb:1700000000000:${viewUUID}`, JSON.stringify({ page: 5, zoom: 200 }));
135
+ const { result } = renderHook(() => useViewState({ page: 1, zoom: 100 }));
136
+ act(() => {
137
+ fireToolResultNotification({
138
+ content: [{ type: "text", text: "result" }],
139
+ structuredContent: {},
140
+ _meta: { viewUUID },
141
+ });
142
+ });
143
+ await waitFor(() => {
144
+ expect(result.current[0]).toEqual({ page: 5, zoom: 200 });
145
+ });
146
+ });
147
+ it("should not persist when no viewUUID is available", () => {
148
+ const { result } = renderHook(() => useViewState({ page: 1 }));
149
+ act(() => {
150
+ result.current[1]({ page: 2 });
151
+ });
152
+ expect(result.current[0]).toEqual({ page: 2 });
153
+ expect(localStorage.length).toBe(0);
154
+ });
155
+ it("should handle corrupted localStorage data gracefully", async () => {
156
+ const viewUUID = "test-uuid-corrupt";
157
+ localStorage.setItem(`sb:1700000000000:${viewUUID}`, "not valid json{{{");
158
+ const { result } = renderHook(() => useViewState({ page: 1 }));
159
+ act(() => {
160
+ fireToolResultNotification({
161
+ content: [{ type: "text", text: "result" }],
162
+ structuredContent: {},
163
+ _meta: { viewUUID },
164
+ });
165
+ });
166
+ await waitFor(() => {
167
+ expect(result.current[0]).toEqual({ page: 1 });
168
+ });
169
+ });
170
+ it("should refresh localStorage timestamp on each persist (LRU)", async () => {
171
+ const viewUUID = "lru-test-uuid";
172
+ const oldKey = `sb:1000000000000:${viewUUID}`;
173
+ localStorage.setItem(oldKey, JSON.stringify({ page: 1 }));
174
+ const { result } = renderHook(() => useViewState({ page: 1 }));
175
+ await act(async () => {
176
+ fireToolResultNotification({
177
+ content: [{ type: "text", text: "result" }],
178
+ structuredContent: {},
179
+ _meta: { viewUUID },
180
+ });
181
+ });
182
+ act(() => {
183
+ result.current[1]({ page: 2 });
184
+ });
185
+ // Old key removed, replaced with a new one (still just 1 entry)
186
+ expect(localStorage.getItem(oldKey)).toBeNull();
187
+ expect(localStorage.length).toBe(1);
188
+ });
189
+ it("should evict oldest entries when exceeding max storage entries", async () => {
190
+ // Fill localStorage with 200 entries (the max)
191
+ for (let i = 0; i < 200; i++) {
192
+ localStorage.setItem(`sb:${String(1000000000000 + i)}:old-uuid-${String(i).padStart(4, "0")}`, JSON.stringify({ i }));
193
+ }
194
+ expect(localStorage.length).toBe(200);
195
+ const viewUUID = "eviction-test-uuid";
196
+ const { result } = renderHook(() => useViewState({ page: 1 }));
197
+ await act(async () => {
198
+ fireToolResultNotification({
199
+ content: [{ type: "text", text: "result" }],
200
+ structuredContent: {},
201
+ _meta: { viewUUID },
202
+ });
203
+ });
204
+ act(() => {
205
+ result.current[1]({ page: 99 });
206
+ });
207
+ // Should have evicted the oldest entry to stay at 200
208
+ expect(localStorage.length).toBe(200);
209
+ expect(localStorage.getItem("sb:1000000000000:old-uuid-0000")).toBeNull();
210
+ });
211
+ });
212
+ //# sourceMappingURL=use-view-state.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-view-state.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-view-state.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { act, renderHook, waitFor } from \"@testing-library/react\";\nimport { StrictMode } from \"react\";\nimport {\n afterEach,\n beforeEach,\n describe,\n expect,\n it,\n type Mock,\n vi,\n} from \"vitest\";\nimport { HostAdaptor } from \"../bridges/adaptor.js\";\nimport { McpAppBridge } from \"../bridges/mcp-app/index.js\";\nimport {\n fireToolResultNotification,\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./test/utils.js\";\nimport { useViewState } from \"./use-view-state.js\";\n\ndescribe(\"useViewState\", () => {\n let OpenaiMock: { widgetState: unknown; setWidgetState: Mock };\n\n beforeEach(() => {\n OpenaiMock = {\n widgetState: null,\n setWidgetState: vi.fn().mockResolvedValue(undefined),\n };\n vi.stubGlobal(\"openai\", OpenaiMock);\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n });\n\n const defaultState = { count: 0, name: \"test\" };\n const windowState = { count: 5, name: \"window\" };\n\n it(\"should initialize with default state when window.openai.widgetState is null\", () => {\n OpenaiMock.widgetState = null;\n const { result } = renderHook(() => useViewState(defaultState));\n\n expect(result.current[0]).toEqual(defaultState);\n });\n\n it(\"should initialize with window.openai.widgetState when available\", () => {\n OpenaiMock.widgetState = { modelContent: windowState };\n const { result } = renderHook(() => useViewState(defaultState));\n\n expect(result.current[0]).toEqual(windowState);\n });\n\n it(\"should call window.openai.setWidgetState when setViewState is called with a new state\", async () => {\n const { result } = renderHook(() => useViewState(defaultState));\n const newState = { count: 10, name: \"updated\" };\n\n act(() => {\n result.current[1](newState);\n });\n\n expect(OpenaiMock.setWidgetState).toHaveBeenCalledWith({\n modelContent: newState,\n privateContent: {},\n });\n expect(result.current[0]).toEqual(newState);\n });\n\n it(\"should call window.openai.setWidgetState when setViewState is called with a function updater\", async () => {\n const { result } = renderHook(() => useViewState(defaultState));\n\n act(() => {\n result.current[1]((prev) => ({ ...prev, count: prev.count + 1 }));\n });\n\n expect(OpenaiMock.setWidgetState).toHaveBeenCalledWith({\n modelContent: { count: 1, name: \"test\" },\n privateContent: {},\n });\n expect(result.current[0]).toEqual({ count: 1, name: \"test\" });\n });\n\n it(\"should call window.openai.setWidgetState once per update under StrictMode\", () => {\n const { result } = renderHook(() => useViewState(defaultState), {\n wrapper: StrictMode,\n });\n\n act(() => {\n result.current[1]({ count: 10, name: \"updated\" });\n });\n\n expect(OpenaiMock.setWidgetState).toHaveBeenCalledTimes(1);\n });\n\n it(\"should not persist anything on mount\", () => {\n renderHook(() => useViewState(defaultState), { wrapper: StrictMode });\n\n expect(OpenaiMock.setWidgetState).not.toHaveBeenCalled();\n });\n\n it(\"should persist state when setViewState and unmount happen in the same tick\", () => {\n const { result, unmount } = renderHook(() => useViewState(defaultState));\n\n act(() => {\n result.current[1]({ count: 10, name: \"updated\" });\n unmount();\n });\n\n expect(OpenaiMock.setWidgetState).toHaveBeenCalledTimes(1);\n });\n\n it(\"should not persist state when setViewState is called after unmount\", () => {\n const { result, unmount } = renderHook(() => useViewState(defaultState));\n const setViewState = result.current[1];\n\n unmount();\n\n act(() => {\n setViewState({ count: 10, name: \"updated\" });\n });\n\n expect(OpenaiMock.setWidgetState).not.toHaveBeenCalled();\n });\n\n it(\"should update state when window.openai.widgetState changes\", () => {\n OpenaiMock.widgetState = { modelContent: defaultState };\n const { result, rerender } = renderHook(() => useViewState(defaultState));\n\n expect(result.current[0]).toEqual(defaultState);\n\n // Simulate window.openai.widgetState changing\n OpenaiMock.widgetState = { modelContent: windowState };\n // Trigger re-render to simulate the useEffect running\n rerender();\n\n expect(result.current[0]).toEqual(windowState);\n });\n});\n\ndescribe(\"useViewState (mcp-app host — localStorage persistence)\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"parent\", { postMessage: getMcpAppHostPostMessageMock() });\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"openai\", undefined);\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n localStorage.clear();\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n HostAdaptor.resetInstance();\n McpAppBridge.resetInstance();\n localStorage.clear();\n });\n\n it(\"should persist state to localStorage when viewUUID is available\", async () => {\n const viewUUID = \"test-uuid-123\";\n const { result } = renderHook(() => useViewState({ page: 1, zoom: 100 }));\n\n await act(async () => {\n fireToolResultNotification({\n content: [{ type: \"text\", text: \"result\" }],\n structuredContent: {},\n _meta: { viewUUID },\n });\n });\n\n act(() => {\n result.current[1]({ page: 3, zoom: 150 });\n });\n\n expect(result.current[0]).toEqual({ page: 3, zoom: 150 });\n expect(localStorage.length).toBe(1);\n });\n\n it(\"should restore state from localStorage when viewUUID arrives\", async () => {\n const viewUUID = \"test-uuid-456\";\n // Pre-seed with the sb:{timestamp}:{viewUUID} format\n localStorage.setItem(\n `sb:1700000000000:${viewUUID}`,\n JSON.stringify({ page: 5, zoom: 200 }),\n );\n\n const { result } = renderHook(() => useViewState({ page: 1, zoom: 100 }));\n\n act(() => {\n fireToolResultNotification({\n content: [{ type: \"text\", text: \"result\" }],\n structuredContent: {},\n _meta: { viewUUID },\n });\n });\n\n await waitFor(() => {\n expect(result.current[0]).toEqual({ page: 5, zoom: 200 });\n });\n });\n\n it(\"should not persist when no viewUUID is available\", () => {\n const { result } = renderHook(() => useViewState({ page: 1 }));\n\n act(() => {\n result.current[1]({ page: 2 });\n });\n\n expect(result.current[0]).toEqual({ page: 2 });\n expect(localStorage.length).toBe(0);\n });\n\n it(\"should handle corrupted localStorage data gracefully\", async () => {\n const viewUUID = \"test-uuid-corrupt\";\n localStorage.setItem(`sb:1700000000000:${viewUUID}`, \"not valid json{{{\");\n\n const { result } = renderHook(() => useViewState({ page: 1 }));\n\n act(() => {\n fireToolResultNotification({\n content: [{ type: \"text\", text: \"result\" }],\n structuredContent: {},\n _meta: { viewUUID },\n });\n });\n\n await waitFor(() => {\n expect(result.current[0]).toEqual({ page: 1 });\n });\n });\n\n it(\"should refresh localStorage timestamp on each persist (LRU)\", async () => {\n const viewUUID = \"lru-test-uuid\";\n const oldKey = `sb:1000000000000:${viewUUID}`;\n localStorage.setItem(oldKey, JSON.stringify({ page: 1 }));\n\n const { result } = renderHook(() => useViewState({ page: 1 }));\n\n await act(async () => {\n fireToolResultNotification({\n content: [{ type: \"text\", text: \"result\" }],\n structuredContent: {},\n _meta: { viewUUID },\n });\n });\n\n act(() => {\n result.current[1]({ page: 2 });\n });\n\n // Old key removed, replaced with a new one (still just 1 entry)\n expect(localStorage.getItem(oldKey)).toBeNull();\n expect(localStorage.length).toBe(1);\n });\n\n it(\"should evict oldest entries when exceeding max storage entries\", async () => {\n // Fill localStorage with 200 entries (the max)\n for (let i = 0; i < 200; i++) {\n localStorage.setItem(\n `sb:${String(1000000000000 + i)}:old-uuid-${String(i).padStart(4, \"0\")}`,\n JSON.stringify({ i }),\n );\n }\n expect(localStorage.length).toBe(200);\n\n const viewUUID = \"eviction-test-uuid\";\n const { result } = renderHook(() => useViewState({ page: 1 }));\n\n await act(async () => {\n fireToolResultNotification({\n content: [{ type: \"text\", text: \"result\" }],\n structuredContent: {},\n _meta: { viewUUID },\n });\n });\n\n act(() => {\n result.current[1]({ page: 99 });\n });\n\n // Should have evicted the oldest entry to stay at 200\n expect(localStorage.length).toBe(200);\n expect(localStorage.getItem(\"sb:1000000000000:old-uuid-0000\")).toBeNull();\n });\n});\n"]}
@@ -1,4 +1,7 @@
1
- export { useOpenAiGlobal } from "./use-openai-global.js";
2
- export { useToolOutput } from "./use-tool-output.js";
1
+ export * from "./bridges/index.js";
2
+ export { createStore } from "./create-store.js";
3
+ export * from "./data-llm.js";
4
+ export { generateHelpers } from "./generate-helpers.js";
5
+ export * from "./hooks/index.js";
6
+ export { mountView } from "./mount-view.js";
3
7
  export * from "./types.js";
4
- export { mountWidget } from "./mount-widget.js";
package/dist/web/index.js CHANGED
@@ -1,5 +1,8 @@
1
- export { useOpenAiGlobal } from "./use-openai-global.js";
2
- export { useToolOutput } from "./use-tool-output.js";
1
+ export * from "./bridges/index.js";
2
+ export { createStore } from "./create-store.js";
3
+ export * from "./data-llm.js";
4
+ export { generateHelpers } from "./generate-helpers.js";
5
+ export * from "./hooks/index.js";
6
+ export { mountView } from "./mount-view.js";
3
7
  export * from "./types.js";
4
- export { mountWidget } from "./mount-widget.js";
5
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/web/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/web/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,cAAc,YAAY,CAAC","sourcesContent":["export * from \"./bridges/index.js\";\nexport { createStore } from \"./create-store.js\";\nexport * from \"./data-llm.js\";\nexport { generateHelpers } from \"./generate-helpers.js\";\nexport * from \"./hooks/index.js\";\nexport { mountView } from \"./mount-view.js\";\nexport * from \"./types.js\";\n"]}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Mount a view's root React component into `#root`. Each view file's entry
3
+ * point should call this exactly once.
4
+ *
5
+ * Wraps the component in `StrictMode`, applies host-specific providers
6
+ * automatically (e.g. modal support for MCP Apps), and installs the dev-mode
7
+ * logging proxy for `window.openai` calls.
8
+ *
9
+ * @param component - Your root React element (already constructed, e.g. `<App />`).
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * // src/views/search.tsx
14
+ * import { mountView } from "skybridge/web";
15
+ * import { App } from "./App";
16
+ *
17
+ * mountView(<App />);
18
+ * ```
19
+ */
20
+ export declare const mountView: (component: React.ReactNode) => void;
@@ -0,0 +1,46 @@
1
+ /// <reference types="vite/client" />
2
+ import { createElement, StrictMode } from "react";
3
+ import { createRoot } from "react-dom/client";
4
+ import { installOpenAILoggingProxy } from "./proxy.js";
5
+ let rootInstance = null;
6
+ /**
7
+ * Mount a view's root React component into `#root`. Each view file's entry
8
+ * point should call this exactly once.
9
+ *
10
+ * Wraps the component in `StrictMode`, applies host-specific providers
11
+ * automatically (e.g. modal support for MCP Apps), and installs the dev-mode
12
+ * logging proxy for `window.openai` calls.
13
+ *
14
+ * @param component - Your root React element (already constructed, e.g. `<App />`).
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * // src/views/search.tsx
19
+ * import { mountView } from "skybridge/web";
20
+ * import { App } from "./App";
21
+ *
22
+ * mountView(<App />);
23
+ * ```
24
+ */
25
+ export const mountView = (component) => {
26
+ const rootElement = document.getElementById("root");
27
+ if (!rootElement) {
28
+ throw new Error("Root element not found");
29
+ }
30
+ if (!rootInstance) {
31
+ rootInstance = createRoot(rootElement);
32
+ }
33
+ if (import.meta.env.DEV) {
34
+ installOpenAILoggingProxy();
35
+ }
36
+ const hostType = window.skybridge?.hostType;
37
+ (async () => {
38
+ let app = component;
39
+ if (hostType === "mcp-app") {
40
+ const { ModalProvider } = await import("./components/modal-provider.js");
41
+ app = createElement(ModalProvider, null, component);
42
+ }
43
+ rootInstance.render(createElement(StrictMode, null, app));
44
+ })();
45
+ };
46
+ //# sourceMappingURL=mount-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mount-view.js","sourceRoot":"","sources":["../../src/web/mount-view.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAErC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,UAAU,EAAa,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEvD,IAAI,YAAY,GAAgB,IAAI,CAAC;AAErC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,SAA0B,EAAE,EAAE;IACtD,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IACpD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACxB,yBAAyB,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC;IAE5C,CAAC,KAAK,IAAI,EAAE;QACV,IAAI,GAAG,GAAG,SAAS,CAAC;QACpB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAC;YACzE,GAAG,GAAG,aAAa,CAAC,aAAa,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QACtD,CAAC;QACD,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,EAAE,CAAC;AACP,CAAC,CAAC","sourcesContent":["/// <reference types=\"vite/client\" />\n\nimport { createElement, StrictMode } from \"react\";\nimport { createRoot, type Root } from \"react-dom/client\";\nimport { installOpenAILoggingProxy } from \"./proxy.js\";\n\nlet rootInstance: Root | null = null;\n\n/**\n * Mount a view's root React component into `#root`. Each view file's entry\n * point should call this exactly once.\n *\n * Wraps the component in `StrictMode`, applies host-specific providers\n * automatically (e.g. modal support for MCP Apps), and installs the dev-mode\n * logging proxy for `window.openai` calls.\n *\n * @param component - Your root React element (already constructed, e.g. `<App />`).\n *\n * @example\n * ```tsx\n * // src/views/search.tsx\n * import { mountView } from \"skybridge/web\";\n * import { App } from \"./App\";\n *\n * mountView(<App />);\n * ```\n */\nexport const mountView = (component: React.ReactNode) => {\n const rootElement = document.getElementById(\"root\");\n if (!rootElement) {\n throw new Error(\"Root element not found\");\n }\n\n if (!rootInstance) {\n rootInstance = createRoot(rootElement);\n }\n\n if (import.meta.env.DEV) {\n installOpenAILoggingProxy();\n }\n\n const hostType = window.skybridge?.hostType;\n\n (async () => {\n let app = component;\n if (hostType === \"mcp-app\") {\n const { ModalProvider } = await import(\"./components/modal-provider.js\");\n app = createElement(ModalProvider, null, component);\n }\n rootInstance.render(createElement(StrictMode, null, app));\n })();\n};\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,81 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { transform } from "./transform-data-llm.js";
3
+ describe("data-llm plugin", () => {
4
+ it("should transform JSX element with data-llm string attribute", async () => {
5
+ const code = `
6
+ function Component() {
7
+ return <div data-llm="Test description">Content</div>;
8
+ }
9
+ `;
10
+ const result = await transform(code, "test.tsx");
11
+ expect(result).not.toBeNull();
12
+ expect(result?.code).toContain("DataLLM");
13
+ expect(result?.code).toContain('content="Test description"');
14
+ expect(result?.code).not.toContain("data-llm");
15
+ });
16
+ it("should transform JSX element with data-llm expression attribute", async () => {
17
+ const code = `
18
+ function Component() {
19
+ const desc = "Dynamic description";
20
+ return <div data-llm={desc}>Content</div>;
21
+ }
22
+ `;
23
+ const result = await transform(code, "test.tsx");
24
+ expect(result).not.toBeNull();
25
+ expect(result?.code).toContain("DataLLM");
26
+ expect(result?.code).toContain("content={desc}");
27
+ expect(result?.code).not.toContain("data-llm");
28
+ });
29
+ it("should add import for DataLLM when not present", async () => {
30
+ const code = `
31
+ function Component() {
32
+ return <div data-llm="Test">Content</div>;
33
+ }
34
+ `;
35
+ const result = await transform(code, "test.tsx");
36
+ expect(result).not.toBeNull();
37
+ expect(result?.code).toContain('import { DataLLM } from "skybridge/web"');
38
+ });
39
+ it("should handle DataLLM imports correctly", async () => {
40
+ // No duplicate import
41
+ const codeWithImport = `
42
+ import { DataLLM } from "skybridge/web";
43
+ function Component() {
44
+ return <div data-llm="Test">Content</div>;
45
+ }
46
+ `;
47
+ const result1 = await transform(codeWithImport, "test.tsx");
48
+ expect(result1?.code.match(/import.*DataLLM.*from.*skybridge\/web/g)).toHaveLength(1);
49
+ // Preserve other imports and add missing DataLLM
50
+ const codeWithOthers = `
51
+ import React from "react";
52
+ import { useState } from "react";
53
+ function Component() {
54
+ return <div data-llm="Test">Content</div>;
55
+ }
56
+ `;
57
+ const result2 = await transform(codeWithOthers, "test.tsx");
58
+ expect(result2?.code).toContain('import React from "react"');
59
+ expect(result2?.code).toContain('import { useState } from "react"');
60
+ expect(result2?.code).toContain('import { DataLLM } from "skybridge/web"');
61
+ });
62
+ it("should handle complex JSX with multiple data-llm attributes", async () => {
63
+ const code = `
64
+ function Component() {
65
+ return (
66
+ <div>
67
+ <section data-llm="Section 1">
68
+ <p>Content 1</p>
69
+ </section>
70
+ <section data-llm="Section 2">
71
+ <p>Content 2</p>
72
+ </section>
73
+ </div>
74
+ );
75
+ }
76
+ `;
77
+ const result = await transform(code, "test.tsx");
78
+ expect(result).toMatchSnapshot();
79
+ });
80
+ });
81
+ //# sourceMappingURL=data-llm.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-llm.test.js","sourceRoot":"","sources":["../../../src/web/plugin/data-llm.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { transform } from \"./transform-data-llm.js\";\n\ndescribe(\"data-llm plugin\", () => {\n it(\"should transform JSX element with data-llm string attribute\", async () => {\n const code = `\n function Component() {\n return <div data-llm=\"Test description\">Content</div>;\n }\n `;\n\n const result = await transform(code, \"test.tsx\");\n\n expect(result).not.toBeNull();\n expect(result?.code).toContain(\"DataLLM\");\n expect(result?.code).toContain('content=\"Test description\"');\n expect(result?.code).not.toContain(\"data-llm\");\n });\n\n it(\"should transform JSX element with data-llm expression attribute\", async () => {\n const code = `\n function Component() {\n const desc = \"Dynamic description\";\n return <div data-llm={desc}>Content</div>;\n }\n `;\n\n const result = await transform(code, \"test.tsx\");\n\n expect(result).not.toBeNull();\n expect(result?.code).toContain(\"DataLLM\");\n expect(result?.code).toContain(\"content={desc}\");\n expect(result?.code).not.toContain(\"data-llm\");\n });\n\n it(\"should add import for DataLLM when not present\", async () => {\n const code = `\n function Component() {\n return <div data-llm=\"Test\">Content</div>;\n }\n `;\n\n const result = await transform(code, \"test.tsx\");\n\n expect(result).not.toBeNull();\n expect(result?.code).toContain('import { DataLLM } from \"skybridge/web\"');\n });\n\n it(\"should handle DataLLM imports correctly\", async () => {\n // No duplicate import\n const codeWithImport = `\n import { DataLLM } from \"skybridge/web\";\n function Component() {\n return <div data-llm=\"Test\">Content</div>;\n }\n `;\n const result1 = await transform(codeWithImport, \"test.tsx\");\n expect(\n result1?.code.match(/import.*DataLLM.*from.*skybridge\\/web/g),\n ).toHaveLength(1);\n\n // Preserve other imports and add missing DataLLM\n const codeWithOthers = `\n import React from \"react\";\n import { useState } from \"react\";\n function Component() {\n return <div data-llm=\"Test\">Content</div>;\n }\n `;\n const result2 = await transform(codeWithOthers, \"test.tsx\");\n expect(result2?.code).toContain('import React from \"react\"');\n expect(result2?.code).toContain('import { useState } from \"react\"');\n expect(result2?.code).toContain('import { DataLLM } from \"skybridge/web\"');\n });\n\n it(\"should handle complex JSX with multiple data-llm attributes\", async () => {\n const code = `\n function Component() {\n return (\n <div>\n <section data-llm=\"Section 1\">\n <p>Content 1</p>\n </section>\n <section data-llm=\"Section 2\">\n <p>Content 2</p>\n </section>\n </div>\n );\n }\n `;\n\n const result = await transform(code, \"test.tsx\");\n\n expect(result).toMatchSnapshot();\n });\n});\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,54 @@
1
+ // @vitest-environment node
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { build } from "vite";
6
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
+ import { skybridge } from "./plugin.js";
8
+ /**
9
+ * Full `vite build` over a fixture whose stylesheet contains a `url()`. The
10
+ * unit test pins the `renderBuiltUrl` contract; this one proves Vite actually
11
+ * accepts what the plugin returns.
12
+ */
13
+ describe("skybridge plugin build", () => {
14
+ let root;
15
+ beforeEach(() => {
16
+ root = mkdtempSync(join(tmpdir(), "skybridge-build-"));
17
+ mkdirSync(join(root, "views"), { recursive: true });
18
+ mkdirSync(join(root, "src"), { recursive: true });
19
+ writeFileSync(join(root, "src/demo.woff2"), Buffer.alloc(64, 1));
20
+ writeFileSync(join(root, "src/styles.css"), [
21
+ "@font-face {",
22
+ ' font-family: "Demo";',
23
+ ' src: url("./demo.woff2") format("woff2");',
24
+ "}",
25
+ ].join("\n"));
26
+ writeFileSync(join(root, "src/entry.js"), `import "./styles.css";\n`);
27
+ });
28
+ afterEach(() => {
29
+ rmSync(root, { recursive: true, force: true });
30
+ });
31
+ it("builds a view stylesheet that references a font", async () => {
32
+ const result = await build({
33
+ root,
34
+ logLevel: "silent",
35
+ plugins: [skybridge({ viewsDir: join(root, "views") })],
36
+ build: {
37
+ // Force the font to be emitted as a file rather than inlined, so the
38
+ // stylesheet actually carries a `url()`.
39
+ assetsInlineLimit: 0,
40
+ rollupOptions: { input: { entry: join(root, "src/entry.js") } },
41
+ },
42
+ });
43
+ const outputs = (Array.isArray(result) ? result : [result]).flatMap((bundle) => ("output" in bundle ? bundle.output : []));
44
+ const css = outputs.find((chunk) => chunk.type === "asset" && chunk.fileName.endsWith(".css"));
45
+ if (css?.type !== "asset") {
46
+ throw new Error("no CSS asset was emitted");
47
+ }
48
+ // The font resolves relative to the stylesheet, which the server hands out
49
+ // from `${serverUrl}/assets/` — no runtime expression, which CSS can't run.
50
+ expect(String(css.source)).toMatch(/url\(\.\/demo-[\w-]+\.woff2\)/);
51
+ expect(String(css.source)).not.toContain("window.skybridge");
52
+ });
53
+ });
54
+ //# sourceMappingURL=plugin-build.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-build.test.js","sourceRoot":"","sources":["../../../src/web/plugin/plugin-build.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport { mkdirSync, mkdtempSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { build } from \"vite\";\nimport { afterEach, beforeEach, describe, expect, it } from \"vitest\";\nimport { skybridge } from \"./plugin.js\";\n\n/**\n * Full `vite build` over a fixture whose stylesheet contains a `url()`. The\n * unit test pins the `renderBuiltUrl` contract; this one proves Vite actually\n * accepts what the plugin returns.\n */\ndescribe(\"skybridge plugin build\", () => {\n let root: string;\n\n beforeEach(() => {\n root = mkdtempSync(join(tmpdir(), \"skybridge-build-\"));\n mkdirSync(join(root, \"views\"), { recursive: true });\n mkdirSync(join(root, \"src\"), { recursive: true });\n\n writeFileSync(join(root, \"src/demo.woff2\"), Buffer.alloc(64, 1));\n writeFileSync(\n join(root, \"src/styles.css\"),\n [\n \"@font-face {\",\n ' font-family: \"Demo\";',\n ' src: url(\"./demo.woff2\") format(\"woff2\");',\n \"}\",\n ].join(\"\\n\"),\n );\n writeFileSync(join(root, \"src/entry.js\"), `import \"./styles.css\";\\n`);\n });\n\n afterEach(() => {\n rmSync(root, { recursive: true, force: true });\n });\n\n it(\"builds a view stylesheet that references a font\", async () => {\n const result = await build({\n root,\n logLevel: \"silent\",\n plugins: [skybridge({ viewsDir: join(root, \"views\") })],\n build: {\n // Force the font to be emitted as a file rather than inlined, so the\n // stylesheet actually carries a `url()`.\n assetsInlineLimit: 0,\n rollupOptions: { input: { entry: join(root, \"src/entry.js\") } },\n },\n });\n\n const outputs = (Array.isArray(result) ? result : [result]).flatMap(\n (bundle) => (\"output\" in bundle ? bundle.output : []),\n );\n const css = outputs.find(\n (chunk) => chunk.type === \"asset\" && chunk.fileName.endsWith(\".css\"),\n );\n if (css?.type !== \"asset\") {\n throw new Error(\"no CSS asset was emitted\");\n }\n\n // The font resolves relative to the stylesheet, which the server hands out\n // from `${serverUrl}/assets/` — no runtime expression, which CSS can't run.\n expect(String(css.source)).toMatch(/url\\(\\.\\/demo-[\\w-]+\\.woff2\\)/);\n expect(String(css.source)).not.toContain(\"window.skybridge\");\n });\n});\n"]}
@@ -0,0 +1,39 @@
1
+ import type { Plugin } from "vite";
2
+ /** Options for the {@link skybridge} Vite plugin. */
3
+ export interface SkybridgePluginOptions {
4
+ /** Directory scanned for view modules. Defaults to `"src/views"`. */
5
+ viewsDir?: string;
6
+ /**
7
+ * Package names to leave unbundled when `skybridge build` bundles the server
8
+ * for deployment. Use it for dependencies esbuild can't resolve or load —
9
+ * typically optional native modules pulled in by a transitive dependency.
10
+ */
11
+ serverExternal?: string[];
12
+ }
13
+ /**
14
+ * Vite plugin that wires a Skybridge project's view files into Vite.
15
+ *
16
+ * For each `.tsx` / `.jsx` file in `viewsDir` with a default export, the
17
+ * plugin:
18
+ * - exposes a virtual entry that calls {@link mountView} with the view's
19
+ * default export,
20
+ * - generates `.skybridge/views.d.ts` to augment {@link ViewNameRegistry} so
21
+ * {@link ViewName} narrows to the actual view names,
22
+ * - rewrites `<DataLLM>` JSX so the host can extract its content,
23
+ * - warns in dev if a view file is missing a default export.
24
+ *
25
+ * Add it to your `vite.config.ts` alongside `@vitejs/plugin-react`.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * // vite.config.ts
30
+ * import { defineConfig } from "vite";
31
+ * import react from "@vitejs/plugin-react";
32
+ * import { skybridge } from "skybridge/vite";
33
+ *
34
+ * export default defineConfig({
35
+ * plugins: [react(), skybridge({ viewsDir: "src/views" })],
36
+ * });
37
+ * ```
38
+ */
39
+ export declare function skybridge(options?: SkybridgePluginOptions): Plugin;