skybridge 0.0.0-dev.e97960f → 0.0.0-dev.e99ee17

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 (439) hide show
  1. package/README.md +126 -124
  2. package/dist/cli/build-helpers.d.ts +11 -0
  3. package/dist/cli/build-helpers.js +107 -0
  4. package/dist/cli/build-helpers.js.map +1 -0
  5. package/dist/cli/build-helpers.test.js +132 -0
  6. package/dist/cli/build-helpers.test.js.map +1 -0
  7. package/dist/cli/build-steps.d.ts +2 -0
  8. package/dist/cli/build-steps.js +73 -0
  9. package/dist/cli/build-steps.js.map +1 -0
  10. package/dist/cli/build-steps.test.js +52 -0
  11. package/dist/cli/build-steps.test.js.map +1 -0
  12. package/dist/cli/detect-port.d.ts +2 -2
  13. package/dist/cli/detect-port.js +8 -20
  14. package/dist/cli/detect-port.js.map +1 -1
  15. package/dist/cli/header.d.ts +1 -1
  16. package/dist/cli/resolve-skybridge-config.d.ts +5 -0
  17. package/dist/cli/resolve-skybridge-config.js +18 -0
  18. package/dist/cli/resolve-skybridge-config.js.map +1 -0
  19. package/dist/cli/run-plain.d.ts +18 -0
  20. package/dist/cli/run-plain.js +90 -0
  21. package/dist/cli/run-plain.js.map +1 -0
  22. package/dist/cli/telemetry.js +2 -0
  23. package/dist/cli/telemetry.js.map +1 -1
  24. package/dist/cli/use-execute-steps.d.ts +1 -1
  25. package/dist/cli/use-nodemon.d.ts +25 -1
  26. package/dist/cli/use-nodemon.js +96 -63
  27. package/dist/cli/use-nodemon.js.map +1 -1
  28. package/dist/cli/use-typescript-check.d.ts +8 -2
  29. package/dist/cli/use-typescript-check.js +71 -68
  30. package/dist/cli/use-typescript-check.js.map +1 -1
  31. package/dist/commands/build.d.ts +0 -3
  32. package/dist/commands/build.js +2 -73
  33. package/dist/commands/build.js.map +1 -1
  34. package/dist/commands/create.d.ts +9 -0
  35. package/dist/commands/create.js +30 -0
  36. package/dist/commands/create.js.map +1 -0
  37. package/dist/commands/dev.d.ts +1 -0
  38. package/dist/commands/dev.js +51 -3
  39. package/dist/commands/dev.js.map +1 -1
  40. package/dist/commands/start.js +7 -1
  41. package/dist/commands/start.js.map +1 -1
  42. package/dist/context-warnings.d.ts +5 -0
  43. package/dist/context-warnings.js +31 -0
  44. package/dist/context-warnings.js.map +1 -0
  45. package/dist/context-warnings.test.js +28 -0
  46. package/dist/context-warnings.test.js.map +1 -0
  47. package/dist/server/app.d.ts +194 -0
  48. package/dist/server/app.js +266 -0
  49. package/dist/server/app.js.map +1 -0
  50. package/dist/server/app.test.js +97 -0
  51. package/dist/server/app.test.js.map +1 -0
  52. package/dist/server/asset-base-url-transform-plugin.d.ts +1 -0
  53. package/dist/server/asset-base-url-transform-plugin.js +16 -1
  54. package/dist/server/asset-base-url-transform-plugin.js.map +1 -1
  55. package/dist/server/asset-base-url-transform-plugin.test.js +51 -1
  56. package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -1
  57. package/dist/server/auth/discovery.d.ts +33 -0
  58. package/dist/server/auth/discovery.js +56 -0
  59. package/dist/server/auth/discovery.js.map +1 -0
  60. package/dist/server/auth/discovery.test.d.ts +1 -0
  61. package/dist/server/auth/discovery.test.js +93 -0
  62. package/dist/server/auth/discovery.test.js.map +1 -0
  63. package/dist/server/auth/index.d.ts +48 -0
  64. package/dist/server/auth/index.js +2 -0
  65. package/dist/server/auth/index.js.map +1 -0
  66. package/dist/server/auth/providers/auth0.d.ts +40 -0
  67. package/dist/server/auth/providers/auth0.js +36 -0
  68. package/dist/server/auth/providers/auth0.js.map +1 -0
  69. package/dist/server/auth/providers/auth0.test.d.ts +1 -0
  70. package/dist/server/auth/providers/auth0.test.js +50 -0
  71. package/dist/server/auth/providers/auth0.test.js.map +1 -0
  72. package/dist/server/auth/providers/authplane.d.ts +49 -0
  73. package/dist/server/auth/providers/authplane.js +57 -0
  74. package/dist/server/auth/providers/authplane.js.map +1 -0
  75. package/dist/server/auth/providers/authplane.test.d.ts +1 -0
  76. package/dist/server/auth/providers/authplane.test.js +143 -0
  77. package/dist/server/auth/providers/authplane.test.js.map +1 -0
  78. package/dist/server/auth/providers/clerk.d.ts +51 -0
  79. package/dist/server/auth/providers/clerk.js +19 -0
  80. package/dist/server/auth/providers/clerk.js.map +1 -0
  81. package/dist/server/auth/providers/clerk.test.d.ts +1 -0
  82. package/dist/server/auth/providers/clerk.test.js +30 -0
  83. package/dist/server/auth/providers/clerk.test.js.map +1 -0
  84. package/dist/server/auth/providers/custom.d.ts +42 -0
  85. package/dist/server/auth/providers/custom.js +59 -0
  86. package/dist/server/auth/providers/custom.js.map +1 -0
  87. package/dist/server/auth/providers/custom.test.d.ts +1 -0
  88. package/dist/server/auth/providers/custom.test.js +161 -0
  89. package/dist/server/auth/providers/custom.test.js.map +1 -0
  90. package/dist/server/auth/providers/descope.d.ts +44 -0
  91. package/dist/server/auth/providers/descope.js +44 -0
  92. package/dist/server/auth/providers/descope.js.map +1 -0
  93. package/dist/server/auth/providers/descope.test.d.ts +1 -0
  94. package/dist/server/auth/providers/descope.test.js +62 -0
  95. package/dist/server/auth/providers/descope.test.js.map +1 -0
  96. package/dist/server/auth/providers/shared.d.ts +2 -0
  97. package/dist/server/auth/providers/shared.js +6 -0
  98. package/dist/server/auth/providers/shared.js.map +1 -0
  99. package/dist/server/auth/providers/shared.test.d.ts +1 -0
  100. package/dist/server/auth/providers/shared.test.js +10 -0
  101. package/dist/server/auth/providers/shared.test.js.map +1 -0
  102. package/dist/server/auth/providers/stytch.d.ts +25 -0
  103. package/dist/server/auth/providers/stytch.js +16 -0
  104. package/dist/server/auth/providers/stytch.js.map +1 -0
  105. package/dist/server/auth/providers/verify-spy.d.ts +12 -0
  106. package/dist/server/auth/providers/verify-spy.js +21 -0
  107. package/dist/server/auth/providers/verify-spy.js.map +1 -0
  108. package/dist/server/auth/providers/workos.d.ts +34 -0
  109. package/dist/server/auth/providers/workos.js +16 -0
  110. package/dist/server/auth/providers/workos.js.map +1 -0
  111. package/dist/server/auth/security-schemes.d.ts +23 -0
  112. package/dist/server/auth/security-schemes.js +64 -0
  113. package/dist/server/auth/security-schemes.js.map +1 -0
  114. package/dist/server/auth/security-schemes.test.d.ts +1 -0
  115. package/dist/server/auth/security-schemes.test.js +99 -0
  116. package/dist/server/auth/security-schemes.test.js.map +1 -0
  117. package/dist/server/auth/setup.d.ts +6 -0
  118. package/dist/server/auth/setup.js +91 -0
  119. package/dist/server/auth/setup.js.map +1 -0
  120. package/dist/server/auth/setup.test.d.ts +1 -0
  121. package/dist/server/auth/setup.test.js +514 -0
  122. package/dist/server/auth/setup.test.js.map +1 -0
  123. package/dist/server/auth/verify.d.ts +48 -0
  124. package/dist/server/auth/verify.js +62 -0
  125. package/dist/server/auth/verify.js.map +1 -0
  126. package/dist/server/auth/verify.test.d.ts +1 -0
  127. package/dist/server/auth/verify.test.js +165 -0
  128. package/dist/server/auth/verify.test.js.map +1 -0
  129. package/dist/server/auth-extra.test-d.js +130 -0
  130. package/dist/server/auth-extra.test-d.js.map +1 -0
  131. package/dist/server/auth.d.ts +57 -0
  132. package/dist/server/auth.js +27 -0
  133. package/dist/server/auth.js.map +1 -0
  134. package/dist/server/build-manifest.test.d.ts +1 -0
  135. package/dist/server/build-manifest.test.js +28 -0
  136. package/dist/server/build-manifest.test.js.map +1 -0
  137. package/dist/server/content-helpers.d.ts +41 -1
  138. package/dist/server/content-helpers.js +33 -0
  139. package/dist/server/content-helpers.js.map +1 -1
  140. package/dist/server/events.d.ts +113 -0
  141. package/dist/server/events.js +318 -0
  142. package/dist/server/events.js.map +1 -0
  143. package/dist/server/events.test.d.ts +1 -0
  144. package/dist/server/events.test.js +172 -0
  145. package/dist/server/events.test.js.map +1 -0
  146. package/dist/server/express.d.ts +23 -3
  147. package/dist/server/express.js +43 -32
  148. package/dist/server/express.js.map +1 -1
  149. package/dist/server/express.test.js +269 -90
  150. package/dist/server/express.test.js.map +1 -1
  151. package/dist/server/file-ref.d.ts +21 -1
  152. package/dist/server/file-ref.js +20 -1
  153. package/dist/server/file-ref.js.map +1 -1
  154. package/dist/server/form-input.d.ts +120 -0
  155. package/dist/server/form-input.js +89 -0
  156. package/dist/server/form-input.js.map +1 -0
  157. package/dist/server/host.d.ts +2 -0
  158. package/dist/server/host.js +11 -0
  159. package/dist/server/host.js.map +1 -0
  160. package/dist/server/host.test.d.ts +1 -0
  161. package/dist/server/host.test.js +13 -0
  162. package/dist/server/host.test.js.map +1 -0
  163. package/dist/server/index.d.ts +19 -2
  164. package/dist/server/index.js +16 -1
  165. package/dist/server/index.js.map +1 -1
  166. package/dist/server/mcp-errors.test.d.ts +1 -0
  167. package/dist/server/mcp-errors.test.js +41 -0
  168. package/dist/server/mcp-errors.test.js.map +1 -0
  169. package/dist/server/middleware.d.ts +51 -13
  170. package/dist/server/middleware.js +33 -0
  171. package/dist/server/middleware.js.map +1 -1
  172. package/dist/server/middleware.test-d.js +1 -1
  173. package/dist/server/middleware.test-d.js.map +1 -1
  174. package/dist/server/middleware.test.js +242 -134
  175. package/dist/server/middleware.test.js.map +1 -1
  176. package/dist/server/register-tool.test.d.ts +1 -0
  177. package/dist/server/register-tool.test.js +230 -0
  178. package/dist/server/register-tool.test.js.map +1 -0
  179. package/dist/server/requestOrigin.d.ts +7 -0
  180. package/dist/server/requestOrigin.js +25 -0
  181. package/dist/server/requestOrigin.js.map +1 -0
  182. package/dist/server/server.d.ts +532 -67
  183. package/dist/server/server.js +646 -315
  184. package/dist/server/server.js.map +1 -1
  185. package/dist/server/skills-integration.test.d.ts +1 -0
  186. package/dist/server/skills-integration.test.js +86 -0
  187. package/dist/server/skills-integration.test.js.map +1 -0
  188. package/dist/server/skills.d.ts +27 -0
  189. package/dist/server/skills.js +188 -0
  190. package/dist/server/skills.js.map +1 -0
  191. package/dist/server/skills.test.d.ts +1 -0
  192. package/dist/server/skills.test.js +243 -0
  193. package/dist/server/skills.test.js.map +1 -0
  194. package/dist/server/templateHelper.d.ts +0 -3
  195. package/dist/server/templateHelper.js.map +1 -1
  196. package/dist/server/templates.generated.js +4 -8
  197. package/dist/server/templates.generated.js.map +1 -1
  198. package/dist/server/tool-extra.test.d.ts +1 -0
  199. package/dist/server/tool-extra.test.js +167 -0
  200. package/dist/server/tool-extra.test.js.map +1 -0
  201. package/dist/server/view-name.test-d.d.ts +1 -0
  202. package/dist/server/view-name.test-d.js +8 -0
  203. package/dist/server/view-name.test-d.js.map +1 -0
  204. package/dist/server/view-resource-resolution.test.d.ts +6 -0
  205. package/dist/server/view-resource-resolution.test.js +176 -0
  206. package/dist/server/view-resource-resolution.test.js.map +1 -0
  207. package/dist/server/viewsDevServer.js +4 -1
  208. package/dist/server/viewsDevServer.js.map +1 -1
  209. package/dist/test/utils.d.ts +9 -11
  210. package/dist/test/utils.js +220 -201
  211. package/dist/test/utils.js.map +1 -1
  212. package/dist/test/view.test.js +123 -106
  213. package/dist/test/view.test.js.map +1 -1
  214. package/dist/views/index.d.ts +10 -0
  215. package/dist/views/index.js +3 -0
  216. package/dist/views/index.js.map +1 -0
  217. package/dist/{web/plugin → views}/scan-views.js +1 -1
  218. package/dist/views/scan-views.js.map +1 -0
  219. package/dist/views/scan-views.test.d.ts +1 -0
  220. package/dist/views/scan-views.test.js.map +1 -0
  221. package/dist/views/validate-view.d.ts +1 -0
  222. package/dist/{web/plugin → views}/validate-view.js +2 -2
  223. package/dist/views/validate-view.js.map +1 -0
  224. package/dist/views/validate-view.test.d.ts +1 -0
  225. package/dist/views/validate-view.test.js.map +1 -0
  226. package/dist/web/bridges/adaptor.d.ts +62 -0
  227. package/dist/web/bridges/adaptor.js +471 -0
  228. package/dist/web/bridges/adaptor.js.map +1 -0
  229. package/dist/web/bridges/adaptor.test.d.ts +1 -0
  230. package/dist/web/bridges/adaptor.test.js +264 -0
  231. package/dist/web/bridges/adaptor.test.js.map +1 -0
  232. package/dist/web/bridges/apps-sdk/bridge.d.ts +6 -1
  233. package/dist/web/bridges/apps-sdk/bridge.js +15 -3
  234. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
  235. package/dist/web/bridges/apps-sdk/index.d.ts +0 -1
  236. package/dist/web/bridges/apps-sdk/index.js +0 -1
  237. package/dist/web/bridges/apps-sdk/index.js.map +1 -1
  238. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +11 -0
  239. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +11 -0
  240. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
  241. package/dist/web/bridges/get-adaptor.d.ts +7 -0
  242. package/dist/web/bridges/get-adaptor.js +9 -7
  243. package/dist/web/bridges/get-adaptor.js.map +1 -1
  244. package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
  245. package/dist/web/bridges/get-adaptor.test.js +32 -0
  246. package/dist/web/bridges/get-adaptor.test.js.map +1 -0
  247. package/dist/web/bridges/mcp-app/bridge.d.ts +15 -2
  248. package/dist/web/bridges/mcp-app/bridge.js +80 -7
  249. package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
  250. package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
  251. package/dist/web/bridges/mcp-app/bridge.test.js +17 -0
  252. package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
  253. package/dist/web/bridges/mcp-app/index.d.ts +0 -1
  254. package/dist/web/bridges/mcp-app/index.js +0 -1
  255. package/dist/web/bridges/mcp-app/index.js.map +1 -1
  256. package/dist/web/bridges/mcp-app/types.d.ts +19 -2
  257. package/dist/web/bridges/mcp-app/types.js.map +1 -1
  258. package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +13 -1
  259. package/dist/web/bridges/mcp-app/use-mcp-app-context.js +12 -0
  260. package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
  261. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +1 -1
  262. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
  263. package/dist/web/bridges/mcp-app/view-tools.test.d.ts +1 -0
  264. package/dist/web/bridges/mcp-app/view-tools.test.js +143 -0
  265. package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
  266. package/dist/web/bridges/types.d.ts +160 -5
  267. package/dist/web/bridges/types.js +14 -1
  268. package/dist/web/bridges/types.js.map +1 -1
  269. package/dist/web/bridges/types.test.d.ts +1 -0
  270. package/dist/web/bridges/types.test.js +19 -0
  271. package/dist/web/bridges/types.test.js.map +1 -0
  272. package/dist/web/bridges/use-host-context.d.ts +5 -0
  273. package/dist/web/bridges/use-host-context.js +5 -0
  274. package/dist/web/bridges/use-host-context.js.map +1 -1
  275. package/dist/web/components/modal-provider.d.ts +1 -1
  276. package/dist/web/components/modal-provider.js +4 -3
  277. package/dist/web/components/modal-provider.js.map +1 -1
  278. package/dist/web/create-store.d.ts +26 -0
  279. package/dist/web/create-store.js +26 -0
  280. package/dist/web/create-store.js.map +1 -1
  281. package/dist/web/create-store.test.js +10 -7
  282. package/dist/web/create-store.test.js.map +1 -1
  283. package/dist/web/data-llm.d.ts +34 -1
  284. package/dist/web/data-llm.js +31 -2
  285. package/dist/web/data-llm.js.map +1 -1
  286. package/dist/web/data-llm.test.js +26 -7
  287. package/dist/web/data-llm.test.js.map +1 -1
  288. package/dist/web/generate-helpers.d.ts +25 -19
  289. package/dist/web/generate-helpers.js +23 -17
  290. package/dist/web/generate-helpers.js.map +1 -1
  291. package/dist/web/generate-helpers.test-d.js +7 -5
  292. package/dist/web/generate-helpers.test-d.js.map +1 -1
  293. package/dist/web/generate-helpers.test.js +2 -2
  294. package/dist/web/generate-helpers.test.js.map +1 -1
  295. package/dist/web/hooks/index.d.ts +8 -1
  296. package/dist/web/hooks/index.js +8 -1
  297. package/dist/web/hooks/index.js.map +1 -1
  298. package/dist/web/hooks/openai-extensions.test.d.ts +1 -0
  299. package/dist/web/hooks/openai-extensions.test.js +107 -0
  300. package/dist/web/hooks/openai-extensions.test.js.map +1 -0
  301. package/dist/web/hooks/test/utils.d.ts +7 -2
  302. package/dist/web/hooks/test/utils.js +14 -3
  303. package/dist/web/hooks/test/utils.js.map +1 -1
  304. package/dist/web/hooks/use-call-tool.d.ts +47 -2
  305. package/dist/web/hooks/use-call-tool.js +43 -10
  306. package/dist/web/hooks/use-call-tool.js.map +1 -1
  307. package/dist/web/hooks/use-call-tool.test.js +75 -25
  308. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  309. package/dist/web/hooks/use-deep-link.d.ts +22 -0
  310. package/dist/web/hooks/use-deep-link.js +34 -0
  311. package/dist/web/hooks/use-deep-link.js.map +1 -0
  312. package/dist/web/hooks/use-display-mode.d.ts +20 -0
  313. package/dist/web/hooks/use-display-mode.js +20 -0
  314. package/dist/web/hooks/use-display-mode.js.map +1 -1
  315. package/dist/web/hooks/use-display-mode.test.js +57 -21
  316. package/dist/web/hooks/use-display-mode.test.js.map +1 -1
  317. package/dist/web/hooks/use-download.d.ts +3 -0
  318. package/dist/web/hooks/use-download.js +7 -0
  319. package/dist/web/hooks/use-download.js.map +1 -0
  320. package/dist/web/hooks/use-download.test.d.ts +1 -0
  321. package/dist/web/hooks/use-download.test.js +103 -0
  322. package/dist/web/hooks/use-download.test.js.map +1 -0
  323. package/dist/web/hooks/use-file-resource.d.ts +37 -0
  324. package/dist/web/hooks/use-file-resource.js +98 -0
  325. package/dist/web/hooks/use-file-resource.js.map +1 -0
  326. package/dist/web/hooks/use-file-resource.test.d.ts +1 -0
  327. package/dist/web/hooks/use-file-resource.test.js +65 -0
  328. package/dist/web/hooks/use-file-resource.test.js.map +1 -0
  329. package/dist/web/hooks/use-files.d.ts +32 -0
  330. package/dist/web/hooks/use-files.js +32 -0
  331. package/dist/web/hooks/use-files.js.map +1 -1
  332. package/dist/web/hooks/use-files.test.js +11 -3
  333. package/dist/web/hooks/use-files.test.js.map +1 -1
  334. package/dist/web/hooks/use-host.d.ts +44 -0
  335. package/dist/web/hooks/use-host.js +45 -0
  336. package/dist/web/hooks/use-host.js.map +1 -0
  337. package/dist/web/hooks/use-host.test.d.ts +1 -0
  338. package/dist/web/hooks/use-host.test.js +59 -0
  339. package/dist/web/hooks/use-host.test.js.map +1 -0
  340. package/dist/web/hooks/use-model-context.d.ts +44 -0
  341. package/dist/web/hooks/use-model-context.js +63 -0
  342. package/dist/web/hooks/use-model-context.js.map +1 -0
  343. package/dist/web/hooks/use-open-external.d.ts +17 -0
  344. package/dist/web/hooks/use-open-external.js +16 -0
  345. package/dist/web/hooks/use-open-external.js.map +1 -1
  346. package/dist/web/hooks/use-open-external.test.js +15 -5
  347. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  348. package/dist/web/hooks/use-open-file.d.ts +20 -0
  349. package/dist/web/hooks/use-open-file.js +25 -0
  350. package/dist/web/hooks/use-open-file.js.map +1 -0
  351. package/dist/web/hooks/use-register-view-tool.d.ts +37 -0
  352. package/dist/web/hooks/use-register-view-tool.js +50 -0
  353. package/dist/web/hooks/use-register-view-tool.js.map +1 -0
  354. package/dist/web/hooks/use-request-close.d.ts +14 -0
  355. package/dist/web/hooks/use-request-close.js +13 -0
  356. package/dist/web/hooks/use-request-close.js.map +1 -1
  357. package/dist/web/hooks/use-request-close.test.js +35 -40
  358. package/dist/web/hooks/use-request-close.test.js.map +1 -1
  359. package/dist/web/hooks/use-request-modal.d.ts +16 -1
  360. package/dist/web/hooks/use-request-modal.js +16 -1
  361. package/dist/web/hooks/use-request-modal.js.map +1 -1
  362. package/dist/web/hooks/use-request-modal.test.js +11 -1
  363. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  364. package/dist/web/hooks/use-request-size.d.ts +17 -0
  365. package/dist/web/hooks/use-request-size.js +16 -0
  366. package/dist/web/hooks/use-request-size.js.map +1 -1
  367. package/dist/web/hooks/use-request-size.test.js +35 -53
  368. package/dist/web/hooks/use-request-size.test.js.map +1 -1
  369. package/dist/web/hooks/use-send-follow-up-message.d.ts +25 -2
  370. package/dist/web/hooks/use-send-follow-up-message.js +23 -0
  371. package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
  372. package/dist/web/hooks/use-set-open-in-app-url.d.ts +17 -0
  373. package/dist/web/hooks/use-set-open-in-app-url.js +17 -0
  374. package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -1
  375. package/dist/web/hooks/use-set-open-in-app-url.test.js +26 -12
  376. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
  377. package/dist/web/hooks/use-tool-info.d.ts +46 -14
  378. package/dist/web/hooks/use-tool-info.js +29 -7
  379. package/dist/web/hooks/use-tool-info.js.map +1 -1
  380. package/dist/web/hooks/use-tool-info.test-d.js +11 -34
  381. package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
  382. package/dist/web/hooks/use-tool-info.test.js +42 -37
  383. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  384. package/dist/web/hooks/use-user.d.ts +5 -2
  385. package/dist/web/hooks/use-user.js +6 -3
  386. package/dist/web/hooks/use-user.js.map +1 -1
  387. package/dist/web/hooks/use-user.test.js +85 -37
  388. package/dist/web/hooks/use-user.test.js.map +1 -1
  389. package/dist/web/hooks/use-view-state.d.ts +21 -0
  390. package/dist/web/hooks/use-view-state.js +19 -9
  391. package/dist/web/hooks/use-view-state.js.map +1 -1
  392. package/dist/web/hooks/use-view-state.test.js +39 -4
  393. package/dist/web/hooks/use-view-state.test.js.map +1 -1
  394. package/dist/web/hooks/use-viewport.d.ts +20 -0
  395. package/dist/web/hooks/use-viewport.js +21 -0
  396. package/dist/web/hooks/use-viewport.js.map +1 -0
  397. package/dist/web/hooks/use-viewport.test.d.ts +1 -0
  398. package/dist/web/hooks/use-viewport.test.js +109 -0
  399. package/dist/web/hooks/use-viewport.test.js.map +1 -0
  400. package/dist/web/mount-view.d.ts +20 -0
  401. package/dist/web/mount-view.js +22 -6
  402. package/dist/web/mount-view.js.map +1 -1
  403. package/dist/web/types.d.ts +4 -0
  404. package/dist/web/types.js.map +1 -1
  405. package/package.json +35 -23
  406. package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -26
  407. package/dist/web/bridges/apps-sdk/adaptor.js +0 -102
  408. package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
  409. package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -50
  410. package/dist/web/bridges/mcp-app/adaptor.js +0 -271
  411. package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
  412. package/dist/web/hooks/use-layout.d.ts +0 -22
  413. package/dist/web/hooks/use-layout.js +0 -23
  414. package/dist/web/hooks/use-layout.js.map +0 -1
  415. package/dist/web/hooks/use-layout.test.js +0 -96
  416. package/dist/web/hooks/use-layout.test.js.map +0 -1
  417. package/dist/web/plugin/data-llm.test.js +0 -81
  418. package/dist/web/plugin/data-llm.test.js.map +0 -1
  419. package/dist/web/plugin/plugin.d.ts +0 -5
  420. package/dist/web/plugin/plugin.js +0 -163
  421. package/dist/web/plugin/plugin.js.map +0 -1
  422. package/dist/web/plugin/scan-views.js.map +0 -1
  423. package/dist/web/plugin/scan-views.test.js.map +0 -1
  424. package/dist/web/plugin/transform-data-llm.d.ts +0 -12
  425. package/dist/web/plugin/transform-data-llm.js +0 -96
  426. package/dist/web/plugin/transform-data-llm.js.map +0 -1
  427. package/dist/web/plugin/transform-data-llm.test.js +0 -81
  428. package/dist/web/plugin/transform-data-llm.test.js.map +0 -1
  429. package/dist/web/plugin/validate-view.d.ts +0 -1
  430. package/dist/web/plugin/validate-view.js.map +0 -1
  431. package/dist/web/plugin/validate-view.test.js.map +0 -1
  432. /package/dist/{web/hooks/use-layout.test.d.ts → cli/build-helpers.test.d.ts} +0 -0
  433. /package/dist/{web/plugin/data-llm.test.d.ts → cli/build-steps.test.d.ts} +0 -0
  434. /package/dist/{web/plugin/scan-views.test.d.ts → context-warnings.test.d.ts} +0 -0
  435. /package/dist/{web/plugin/transform-data-llm.test.d.ts → server/app.test.d.ts} +0 -0
  436. /package/dist/{web/plugin/validate-view.test.d.ts → server/auth-extra.test-d.d.ts} +0 -0
  437. /package/dist/{web/plugin → views}/scan-views.d.ts +0 -0
  438. /package/dist/{web/plugin → views}/scan-views.test.js +0 -0
  439. /package/dist/{web/plugin → views}/validate-view.test.js +0 -0
@@ -1,14 +1,47 @@
1
1
  import { type ReactNode } from "react";
2
+ /** Text content surfaced to the model by a {@link DataLLM} component. */
2
3
  export type DataLLMContent = string;
4
+ /**
5
+ * A node in the {@link DataLLM} tree. Nested `<DataLLM>` elements form a
6
+ * hierarchy that is serialized as an indented bullet list for the model.
7
+ */
3
8
  export interface DataLLMNode {
4
9
  id: string;
5
10
  parentId: string | null;
6
11
  content: string | null;
7
12
  }
13
+ /**
14
+ * Key under which the serialized {@link DataLLM} tree is persisted on the
15
+ * host's `viewState`. The host exposes the value to the model on subsequent
16
+ * turns.
17
+ */
8
18
  export declare const VIEW_CONTEXT_KEY: "__view_context";
9
19
  interface DataLLMProps {
10
20
  content: DataLLMContent | null | undefined;
11
21
  children?: ReactNode;
12
22
  }
13
- export declare function DataLLM({ content, children }: DataLLMProps): import("react/jsx-runtime").JSX.Element;
23
+ /**
24
+ * Surface in-view content to the LLM so it can reason about what the user is
25
+ * seeing without an extra tool call.
26
+ *
27
+ * Each `<DataLLM>` registers `content` as a node in a tree (parented by any
28
+ * enclosing `<DataLLM>`). The flattened tree is serialized as an indented
29
+ * bullet list and persisted on the host's `viewState` under
30
+ * {@link VIEW_CONTEXT_KEY}; the host then surfaces it to the model as part of
31
+ * the next turn's context.
32
+ *
33
+ * Pass `null`/`undefined` for `content` to register only as a structural
34
+ * parent (useful for grouping).
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * <DataLLM content="Active filters">
39
+ * <DataLLM content={`Sort: ${sort}`} />
40
+ * <DataLLM content={`Page: ${page}`} />
41
+ * </DataLLM>
42
+ * ```
43
+ *
44
+ * @see https://docs.skybridge.tech/api-reference/data-llm
45
+ */
46
+ export declare function DataLLM({ content, children }: DataLLMProps): import("react").JSX.Element;
14
47
  export {};
@@ -1,6 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useContext, useEffect, useId, } from "react";
3
3
  import { getAdaptor } from "./bridges/index.js";
4
+ /**
5
+ * Key under which the serialized {@link DataLLM} tree is persisted on the
6
+ * host's `viewState`. The host exposes the value to the model on subsequent
7
+ * turns.
8
+ */
4
9
  export const VIEW_CONTEXT_KEY = "__view_context";
5
10
  const nodes = new Map();
6
11
  function setNode(node) {
@@ -8,8 +13,9 @@ function setNode(node) {
8
13
  onChange();
9
14
  }
10
15
  function removeNode(id) {
11
- nodes.delete(id);
12
- onChange();
16
+ if (nodes.delete(id)) {
17
+ onChange();
18
+ }
13
19
  }
14
20
  function onChange() {
15
21
  const description = getLLMDescriptionString();
@@ -20,6 +26,29 @@ function onChange() {
20
26
  }));
21
27
  }
22
28
  const ParentIdContext = createContext(null);
29
+ /**
30
+ * Surface in-view content to the LLM so it can reason about what the user is
31
+ * seeing without an extra tool call.
32
+ *
33
+ * Each `<DataLLM>` registers `content` as a node in a tree (parented by any
34
+ * enclosing `<DataLLM>`). The flattened tree is serialized as an indented
35
+ * bullet list and persisted on the host's `viewState` under
36
+ * {@link VIEW_CONTEXT_KEY}; the host then surfaces it to the model as part of
37
+ * the next turn's context.
38
+ *
39
+ * Pass `null`/`undefined` for `content` to register only as a structural
40
+ * parent (useful for grouping).
41
+ *
42
+ * @example
43
+ * ```tsx
44
+ * <DataLLM content="Active filters">
45
+ * <DataLLM content={`Sort: ${sort}`} />
46
+ * <DataLLM content={`Page: ${page}`} />
47
+ * </DataLLM>
48
+ * ```
49
+ *
50
+ * @see https://docs.skybridge.tech/api-reference/data-llm
51
+ */
23
52
  export function DataLLM({ content, children }) {
24
53
  const parentId = useContext(ParentIdContext);
25
54
  const id = useId();
@@ -1 +1 @@
1
- {"version":3,"file":"data-llm.js","sourceRoot":"","sources":["../../src/web/data-llm.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useId,\n} from \"react\";\nimport { getAdaptor } from \"./bridges/index.js\";\n\nexport type DataLLMContent = string;\n\nexport interface DataLLMNode {\n id: string;\n parentId: string | null;\n content: string | null;\n}\n\nexport const VIEW_CONTEXT_KEY = \"__view_context\" as const;\n\nconst nodes = new Map<string, DataLLMNode>();\n\nfunction setNode(node: DataLLMNode) {\n nodes.set(node.id, node);\n onChange();\n}\n\nfunction removeNode(id: string) {\n nodes.delete(id);\n onChange();\n}\n\nfunction onChange() {\n const description = getLLMDescriptionString();\n const adaptor = getAdaptor();\n adaptor.setViewState((prevState) => ({\n ...prevState,\n [VIEW_CONTEXT_KEY]: description,\n }));\n}\n\nconst ParentIdContext = createContext<string | null>(null);\n\ninterface DataLLMProps {\n content: DataLLMContent | null | undefined;\n children?: ReactNode;\n}\n\nexport function DataLLM({ content, children }: DataLLMProps) {\n const parentId = useContext(ParentIdContext);\n const id = useId();\n\n useEffect(() => {\n if (content) {\n setNode({\n id,\n parentId,\n content,\n });\n } else {\n removeNode(id);\n }\n\n return () => {\n removeNode(id);\n };\n }, [id, parentId, content]);\n\n return (\n <ParentIdContext.Provider value={id}>{children}</ParentIdContext.Provider>\n );\n}\n\nfunction getLLMDescriptionString(): string {\n const byParent = new Map<string | null, DataLLMNode[]>();\n for (const node of Array.from(nodes.values())) {\n const key = node.parentId ?? null;\n if (!byParent.has(key)) {\n byParent.set(key, []);\n }\n byParent.get(key)?.push(node);\n }\n\n for (const list of byParent.values()) {\n list.sort((a, b) => a.id.localeCompare(b.id));\n }\n\n const lines: string[] = [];\n\n function traverseTree(parentId: string | null, depth: number) {\n const children = byParent.get(parentId);\n if (!children) {\n return;\n }\n\n for (const child of children) {\n if (child.content?.trim()) {\n const indent = \" \".repeat(depth);\n lines.push(`${indent}- ${child.content.trim()}`);\n }\n traverseTree(child.id, depth + 1);\n }\n }\n\n traverseTree(null, 0);\n\n return lines.join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"data-llm.js","sourceRoot":"","sources":["../../src/web/data-llm.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useId,\n} from \"react\";\nimport { getAdaptor } from \"./bridges/index.js\";\n\n/** Text content surfaced to the model by a {@link DataLLM} component. */\nexport type DataLLMContent = string;\n\n/**\n * A node in the {@link DataLLM} tree. Nested `<DataLLM>` elements form a\n * hierarchy that is serialized as an indented bullet list for the model.\n */\nexport interface DataLLMNode {\n id: string;\n parentId: string | null;\n content: string | null;\n}\n\n/**\n * Key under which the serialized {@link DataLLM} tree is persisted on the\n * host's `viewState`. The host exposes the value to the model on subsequent\n * turns.\n */\nexport const VIEW_CONTEXT_KEY = \"__view_context\" as const;\n\nconst nodes = new Map<string, DataLLMNode>();\n\nfunction setNode(node: DataLLMNode) {\n nodes.set(node.id, node);\n onChange();\n}\n\nfunction removeNode(id: string) {\n if (nodes.delete(id)) {\n onChange();\n }\n}\n\nfunction onChange() {\n const description = getLLMDescriptionString();\n const adaptor = getAdaptor();\n adaptor.setViewState((prevState) => ({\n ...prevState,\n [VIEW_CONTEXT_KEY]: description,\n }));\n}\n\nconst ParentIdContext = createContext<string | null>(null);\n\ninterface DataLLMProps {\n content: DataLLMContent | null | undefined;\n children?: ReactNode;\n}\n\n/**\n * Surface in-view content to the LLM so it can reason about what the user is\n * seeing without an extra tool call.\n *\n * Each `<DataLLM>` registers `content` as a node in a tree (parented by any\n * enclosing `<DataLLM>`). The flattened tree is serialized as an indented\n * bullet list and persisted on the host's `viewState` under\n * {@link VIEW_CONTEXT_KEY}; the host then surfaces it to the model as part of\n * the next turn's context.\n *\n * Pass `null`/`undefined` for `content` to register only as a structural\n * parent (useful for grouping).\n *\n * @example\n * ```tsx\n * <DataLLM content=\"Active filters\">\n * <DataLLM content={`Sort: ${sort}`} />\n * <DataLLM content={`Page: ${page}`} />\n * </DataLLM>\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/data-llm\n */\nexport function DataLLM({ content, children }: DataLLMProps) {\n const parentId = useContext(ParentIdContext);\n const id = useId();\n\n useEffect(() => {\n if (content) {\n setNode({\n id,\n parentId,\n content,\n });\n } else {\n removeNode(id);\n }\n\n return () => {\n removeNode(id);\n };\n }, [id, parentId, content]);\n\n return (\n <ParentIdContext.Provider value={id}>{children}</ParentIdContext.Provider>\n );\n}\n\nfunction getLLMDescriptionString(): string {\n const byParent = new Map<string | null, DataLLMNode[]>();\n for (const node of Array.from(nodes.values())) {\n const key = node.parentId ?? null;\n if (!byParent.has(key)) {\n byParent.set(key, []);\n }\n byParent.get(key)?.push(node);\n }\n\n for (const list of byParent.values()) {\n list.sort((a, b) => a.id.localeCompare(b.id));\n }\n\n const lines: string[] = [];\n\n function traverseTree(parentId: string | null, depth: number) {\n const children = byParent.get(parentId);\n if (!children) {\n return;\n }\n\n for (const child of children) {\n if (child.content?.trim()) {\n const indent = \" \".repeat(depth);\n lines.push(`${indent}- ${child.content.trim()}`);\n }\n traverseTree(child.id, depth + 1);\n }\n }\n\n traverseTree(null, 0);\n\n return lines.join(\"\\n\");\n}\n"]}
@@ -1,13 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { cleanup, render } from "@testing-library/react";
3
3
  import { afterEach, beforeEach, describe, expect, it, vi, } from "vitest";
4
- import { McpAppAdaptor, McpAppBridge } from "./bridges/mcp-app/index.js";
4
+ import { VIEW_STATE_WARNING_TOKENS } from "../context-warnings.js";
5
+ import { HostAdaptor } from "./bridges/adaptor.js";
6
+ import { getAdaptor } from "./bridges/get-adaptor.js";
7
+ import { McpAppBridge } from "./bridges/mcp-app/index.js";
5
8
  import { DataLLM } from "./data-llm.js";
6
9
  import { getMcpAppHostPostMessageMock, MockResizeObserver, } from "./hooks/test/utils.js";
7
10
  describe("DataLLM", () => {
8
11
  afterEach(() => {
9
12
  // Clean up React components BEFORE unstubbing globals
10
13
  cleanup();
14
+ HostAdaptor.resetInstance();
11
15
  vi.unstubAllGlobals();
12
16
  vi.resetAllMocks();
13
17
  });
@@ -33,7 +37,7 @@ describe("DataLLM", () => {
33
37
  });
34
38
  }
35
39
  vi.stubGlobal("openai", OpenaiMock);
36
- vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
40
+ vi.stubGlobal("skybridge", {});
37
41
  });
38
42
  afterEach(() => {
39
43
  // Keep the mock available for React cleanup, but reset it
@@ -42,6 +46,12 @@ describe("DataLLM", () => {
42
46
  window.openai.widgetState = { modelContent: {}, privateContent: {} };
43
47
  }
44
48
  });
49
+ it("does not publish context when content-less nodes mount, change or unmount", () => {
50
+ const { rerender, unmount } = render(_jsx(DataLLM, { content: undefined, children: _jsx("div", { children: "Child" }) }));
51
+ rerender(_jsx(DataLLM, { content: "", children: _jsx("div", { children: "Child" }) }));
52
+ unmount();
53
+ expect(OpenaiMock.setWidgetState).not.toHaveBeenCalled();
54
+ });
45
55
  it("should register a node with content and call setViewState", () => {
46
56
  render(_jsx(DataLLM, { content: "Test content", children: _jsx("div", { children: "Child" }) }));
47
57
  expect(OpenaiMock.setWidgetState).toHaveBeenCalled();
@@ -58,6 +68,14 @@ describe("DataLLM", () => {
58
68
  expect(callArgs).toHaveProperty("existingKey", "existingValue");
59
69
  expect(callArgs).toHaveProperty("__view_context");
60
70
  });
71
+ it("warns when DataLLM content reaches the 20K token warning threshold", async () => {
72
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => { });
73
+ render(_jsx(DataLLM, { content: "x".repeat(VIEW_STATE_WARNING_TOKENS * 4), children: _jsx("div", { children: "Child" }) }));
74
+ await vi.waitFor(() => {
75
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("setWidgetState is persisting"));
76
+ });
77
+ warnSpy.mockRestore();
78
+ });
61
79
  it("should handle deeply nested DataLLM components", () => {
62
80
  render(_jsxs(DataLLM, { content: "Level 1", children: [_jsx(DataLLM, { content: "Level 2A" }), _jsx(DataLLM, { content: "Level 2B", children: _jsx(DataLLM, { content: "Level 3", children: _jsx("div", { children: "Content" }) }) })] }));
63
81
  const callArgs = OpenaiMock.setWidgetState.mock.calls[OpenaiMock.setWidgetState.mock.calls.length - 1]?.[0]?.modelContent;
@@ -87,7 +105,8 @@ describe("DataLLM", () => {
87
105
  describe("mcp-app mode", () => {
88
106
  let postMessageMock;
89
107
  beforeEach(() => {
90
- vi.stubGlobal("skybridge", { hostType: "mcp-app" });
108
+ vi.stubGlobal("skybridge", {});
109
+ vi.stubGlobal("openai", undefined);
91
110
  vi.stubGlobal("ResizeObserver", MockResizeObserver);
92
111
  postMessageMock = getMcpAppHostPostMessageMock();
93
112
  vi.stubGlobal("parent", { postMessage: postMessageMock });
@@ -95,10 +114,10 @@ describe("DataLLM", () => {
95
114
  afterEach(() => {
96
115
  cleanup();
97
116
  McpAppBridge.resetInstance();
98
- McpAppAdaptor.resetInstance();
117
+ HostAdaptor.resetInstance();
99
118
  });
100
119
  it("should register a node and update view state via adaptor", async () => {
101
- const adaptor = McpAppAdaptor.getInstance();
120
+ const adaptor = getAdaptor();
102
121
  render(_jsx(DataLLM, { content: "Test content", children: _jsx("div", { children: "Child" }) }));
103
122
  await vi.waitFor(() => {
104
123
  const viewState = adaptor
@@ -108,7 +127,7 @@ describe("DataLLM", () => {
108
127
  });
109
128
  });
110
129
  it("should preserve existing view state when updating context", async () => {
111
- const adaptor = McpAppAdaptor.getInstance();
130
+ const adaptor = getAdaptor();
112
131
  await adaptor.setViewState({ existingKey: "existingValue" });
113
132
  render(_jsx(DataLLM, { content: "Test content", children: _jsx("div", { children: "Child" }) }));
114
133
  await vi.waitFor(() => {
@@ -120,7 +139,7 @@ describe("DataLLM", () => {
120
139
  });
121
140
  });
122
141
  it("should handle multiple DataLLM components sharing state through adaptor", async () => {
123
- const adaptor = McpAppAdaptor.getInstance();
142
+ const adaptor = getAdaptor();
124
143
  render(_jsxs(_Fragment, { children: [_jsx(DataLLM, { content: "First component", children: _jsx("div", { children: "First" }) }), _jsx(DataLLM, { content: "Second component", children: _jsx("div", { children: "Second" }) })] }));
125
144
  await vi.waitFor(() => {
126
145
  const viewState = adaptor
@@ -1 +1 @@
1
- {"version":3,"file":"data-llm.test.js","sourceRoot":"","sources":["../../src/web/data-llm.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import { cleanup, render } from \"@testing-library/react\";\nimport {\n afterEach,\n beforeEach,\n describe,\n expect,\n it,\n type Mock,\n vi,\n} from \"vitest\";\nimport { McpAppAdaptor, McpAppBridge } from \"./bridges/mcp-app/index.js\";\nimport { DataLLM } from \"./data-llm.js\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./hooks/test/utils.js\";\n\ndescribe(\"DataLLM\", () => {\n afterEach(() => {\n // Clean up React components BEFORE unstubbing globals\n cleanup();\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n describe(\"apps-sdk mode\", () => {\n let OpenaiMock: { widgetState: unknown; setWidgetState: Mock };\n\n beforeEach(() => {\n OpenaiMock = {\n widgetState: { modelContent: {} },\n setWidgetState: vi.fn(),\n };\n // Use Object.defineProperty to ensure it persists\n Object.defineProperty(globalThis, \"openai\", {\n value: OpenaiMock,\n writable: true,\n configurable: true,\n });\n // Also set on window for browser-like environment\n if (typeof window !== \"undefined\") {\n Object.defineProperty(window, \"openai\", {\n value: OpenaiMock,\n writable: true,\n configurable: true,\n });\n }\n vi.stubGlobal(\"openai\", OpenaiMock);\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n });\n\n afterEach(() => {\n // Keep the mock available for React cleanup, but reset it\n if (typeof window !== \"undefined\" && window.openai) {\n window.openai.setWidgetState = vi.fn();\n window.openai.widgetState = { modelContent: {}, privateContent: {} };\n }\n });\n\n it(\"should register a node with content and call setViewState\", () => {\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n expect(OpenaiMock.setWidgetState).toHaveBeenCalled();\n const callArgs =\n OpenaiMock.setWidgetState.mock.calls[0]?.[0]?.modelContent;\n expect(callArgs).toHaveProperty(\"__view_context\");\n expect(callArgs?.__view_context).toContain(\"- Test content\");\n });\n it(\"should preserve existing viewState when updating context\", () => {\n OpenaiMock.widgetState = {\n modelContent: { existingKey: \"existingValue\" },\n };\n\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n const callArgs =\n OpenaiMock.setWidgetState.mock.calls[0]?.[0]?.modelContent;\n expect(callArgs).toHaveProperty(\"existingKey\", \"existingValue\");\n expect(callArgs).toHaveProperty(\"__view_context\");\n });\n\n it(\"should handle deeply nested DataLLM components\", () => {\n render(\n <DataLLM content=\"Level 1\">\n <DataLLM content=\"Level 2A\" />\n <DataLLM content=\"Level 2B\">\n <DataLLM content=\"Level 3\">\n <div>Content</div>\n </DataLLM>\n </DataLLM>\n </DataLLM>,\n );\n\n const callArgs =\n OpenaiMock.setWidgetState.mock.calls[\n OpenaiMock.setWidgetState.mock.calls.length - 1\n ]?.[0]?.modelContent;\n const context = callArgs?.__view_context as string;\n expect(context).toContain(\"- Level 1\");\n expect(context).toContain(\" - Level 2A\");\n expect(context).toContain(\" - Level 2B\");\n expect(context).toContain(\" - Level 3\");\n });\n\n it(\"should update context when content changes\", () => {\n const { rerender } = render(\n <DataLLM content=\"Initial content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n const initialCalls = OpenaiMock.setWidgetState.mock.calls.length;\n\n rerender(\n <DataLLM content=\"Updated content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n expect(OpenaiMock.setWidgetState.mock.calls.length).toBeGreaterThan(\n initialCalls,\n );\n const lastCallArgs =\n OpenaiMock.setWidgetState.mock.calls[\n OpenaiMock.setWidgetState.mock.calls.length - 1\n ]?.[0]?.modelContent;\n expect(lastCallArgs?.__view_context).toContain(\"- Updated content\");\n });\n\n it(\"should remove node and update context when component unmounts\", () => {\n const { unmount } = render(\n <DataLLM content=\"Content to remove\">\n <div>Child</div>\n </DataLLM>,\n );\n\n const callsBeforeUnmount = OpenaiMock.setWidgetState.mock.calls.length;\n\n unmount();\n\n expect(OpenaiMock.setWidgetState.mock.calls.length).toBeGreaterThan(\n callsBeforeUnmount,\n );\n const lastCallArgs =\n OpenaiMock.setWidgetState.mock.calls[\n OpenaiMock.setWidgetState.mock.calls.length - 1\n ]?.[0]?.modelContent;\n expect(lastCallArgs?.__view_context).not.toContain(\"Content to remove\");\n });\n });\n\n describe(\"mcp-app mode\", () => {\n let postMessageMock: ReturnType<typeof getMcpAppHostPostMessageMock>;\n\n beforeEach(() => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n postMessageMock = getMcpAppHostPostMessageMock();\n vi.stubGlobal(\"parent\", { postMessage: postMessageMock });\n });\n\n afterEach(() => {\n cleanup();\n McpAppBridge.resetInstance();\n McpAppAdaptor.resetInstance();\n });\n\n it(\"should register a node and update view state via adaptor\", async () => {\n const adaptor = McpAppAdaptor.getInstance();\n\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n const viewState = adaptor\n .getHostContextStore(\"viewState\")\n .getSnapshot();\n expect(viewState?.__view_context).toContain(\"- Test content\");\n });\n });\n\n it(\"should preserve existing view state when updating context\", async () => {\n const adaptor = McpAppAdaptor.getInstance();\n await adaptor.setViewState({ existingKey: \"existingValue\" });\n\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n const viewState = adaptor\n .getHostContextStore(\"viewState\")\n .getSnapshot();\n expect(viewState?.existingKey).toBe(\"existingValue\");\n expect(viewState?.__view_context).toContain(\"- Test content\");\n });\n });\n\n it(\"should handle multiple DataLLM components sharing state through adaptor\", async () => {\n const adaptor = McpAppAdaptor.getInstance();\n\n render(\n <>\n <DataLLM content=\"First component\">\n <div>First</div>\n </DataLLM>\n <DataLLM content=\"Second component\">\n <div>Second</div>\n </DataLLM>\n </>,\n );\n\n await vi.waitFor(() => {\n const viewState = adaptor\n .getHostContextStore(\"viewState\")\n .getSnapshot();\n const context = viewState?.__view_context as string;\n expect(context).toContain(\"- First component\");\n expect(context).toContain(\"- Second component\");\n });\n });\n\n it(\"should call ui/update-model-context when view state changes\", async () => {\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n expect(postMessageMock).toHaveBeenCalledWith(\n expect.objectContaining({ method: \"ui/update-model-context\" }),\n \"*\",\n );\n });\n });\n });\n});\n"]}
1
+ {"version":3,"file":"data-llm.test.js","sourceRoot":"","sources":["../../src/web/data-llm.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import { cleanup, render } from \"@testing-library/react\";\nimport {\n afterEach,\n beforeEach,\n describe,\n expect,\n it,\n type Mock,\n vi,\n} from \"vitest\";\nimport { VIEW_STATE_WARNING_TOKENS } from \"../context-warnings.js\";\nimport { HostAdaptor } from \"./bridges/adaptor.js\";\nimport { getAdaptor } from \"./bridges/get-adaptor.js\";\nimport { McpAppBridge } from \"./bridges/mcp-app/index.js\";\nimport { DataLLM } from \"./data-llm.js\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./hooks/test/utils.js\";\n\ndescribe(\"DataLLM\", () => {\n afterEach(() => {\n // Clean up React components BEFORE unstubbing globals\n cleanup();\n HostAdaptor.resetInstance();\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n describe(\"apps-sdk mode\", () => {\n let OpenaiMock: { widgetState: unknown; setWidgetState: Mock };\n\n beforeEach(() => {\n OpenaiMock = {\n widgetState: { modelContent: {} },\n setWidgetState: vi.fn(),\n };\n // Use Object.defineProperty to ensure it persists\n Object.defineProperty(globalThis, \"openai\", {\n value: OpenaiMock,\n writable: true,\n configurable: true,\n });\n // Also set on window for browser-like environment\n if (typeof window !== \"undefined\") {\n Object.defineProperty(window, \"openai\", {\n value: OpenaiMock,\n writable: true,\n configurable: true,\n });\n }\n vi.stubGlobal(\"openai\", OpenaiMock);\n vi.stubGlobal(\"skybridge\", {});\n });\n\n afterEach(() => {\n // Keep the mock available for React cleanup, but reset it\n if (typeof window !== \"undefined\" && window.openai) {\n window.openai.setWidgetState = vi.fn();\n window.openai.widgetState = { modelContent: {}, privateContent: {} };\n }\n });\n\n it(\"does not publish context when content-less nodes mount, change or unmount\", () => {\n const { rerender, unmount } = render(\n <DataLLM content={undefined}>\n <div>Child</div>\n </DataLLM>,\n );\n rerender(\n <DataLLM content=\"\">\n <div>Child</div>\n </DataLLM>,\n );\n unmount();\n expect(OpenaiMock.setWidgetState).not.toHaveBeenCalled();\n });\n\n it(\"should register a node with content and call setViewState\", () => {\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n expect(OpenaiMock.setWidgetState).toHaveBeenCalled();\n const callArgs =\n OpenaiMock.setWidgetState.mock.calls[0]?.[0]?.modelContent;\n expect(callArgs).toHaveProperty(\"__view_context\");\n expect(callArgs?.__view_context).toContain(\"- Test content\");\n });\n it(\"should preserve existing viewState when updating context\", () => {\n OpenaiMock.widgetState = {\n modelContent: { existingKey: \"existingValue\" },\n };\n\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n const callArgs =\n OpenaiMock.setWidgetState.mock.calls[0]?.[0]?.modelContent;\n expect(callArgs).toHaveProperty(\"existingKey\", \"existingValue\");\n expect(callArgs).toHaveProperty(\"__view_context\");\n });\n\n it(\"warns when DataLLM content reaches the 20K token warning threshold\", async () => {\n const warnSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n render(\n <DataLLM content={\"x\".repeat(VIEW_STATE_WARNING_TOKENS * 4)}>\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n expect(warnSpy).toHaveBeenCalledWith(\n expect.stringContaining(\"setWidgetState is persisting\"),\n );\n });\n\n warnSpy.mockRestore();\n });\n\n it(\"should handle deeply nested DataLLM components\", () => {\n render(\n <DataLLM content=\"Level 1\">\n <DataLLM content=\"Level 2A\" />\n <DataLLM content=\"Level 2B\">\n <DataLLM content=\"Level 3\">\n <div>Content</div>\n </DataLLM>\n </DataLLM>\n </DataLLM>,\n );\n\n const callArgs =\n OpenaiMock.setWidgetState.mock.calls[\n OpenaiMock.setWidgetState.mock.calls.length - 1\n ]?.[0]?.modelContent;\n const context = callArgs?.__view_context as string;\n expect(context).toContain(\"- Level 1\");\n expect(context).toContain(\" - Level 2A\");\n expect(context).toContain(\" - Level 2B\");\n expect(context).toContain(\" - Level 3\");\n });\n\n it(\"should update context when content changes\", () => {\n const { rerender } = render(\n <DataLLM content=\"Initial content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n const initialCalls = OpenaiMock.setWidgetState.mock.calls.length;\n\n rerender(\n <DataLLM content=\"Updated content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n expect(OpenaiMock.setWidgetState.mock.calls.length).toBeGreaterThan(\n initialCalls,\n );\n const lastCallArgs =\n OpenaiMock.setWidgetState.mock.calls[\n OpenaiMock.setWidgetState.mock.calls.length - 1\n ]?.[0]?.modelContent;\n expect(lastCallArgs?.__view_context).toContain(\"- Updated content\");\n });\n\n it(\"should remove node and update context when component unmounts\", () => {\n const { unmount } = render(\n <DataLLM content=\"Content to remove\">\n <div>Child</div>\n </DataLLM>,\n );\n\n const callsBeforeUnmount = OpenaiMock.setWidgetState.mock.calls.length;\n\n unmount();\n\n expect(OpenaiMock.setWidgetState.mock.calls.length).toBeGreaterThan(\n callsBeforeUnmount,\n );\n const lastCallArgs =\n OpenaiMock.setWidgetState.mock.calls[\n OpenaiMock.setWidgetState.mock.calls.length - 1\n ]?.[0]?.modelContent;\n expect(lastCallArgs?.__view_context).not.toContain(\"Content to remove\");\n });\n });\n\n describe(\"mcp-app mode\", () => {\n let postMessageMock: ReturnType<typeof getMcpAppHostPostMessageMock>;\n\n beforeEach(() => {\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"openai\", undefined);\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n postMessageMock = getMcpAppHostPostMessageMock();\n vi.stubGlobal(\"parent\", { postMessage: postMessageMock });\n });\n\n afterEach(() => {\n cleanup();\n McpAppBridge.resetInstance();\n HostAdaptor.resetInstance();\n });\n\n it(\"should register a node and update view state via adaptor\", async () => {\n const adaptor = getAdaptor();\n\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n const viewState = adaptor\n .getHostContextStore(\"viewState\")\n .getSnapshot();\n expect(viewState?.__view_context).toContain(\"- Test content\");\n });\n });\n\n it(\"should preserve existing view state when updating context\", async () => {\n const adaptor = getAdaptor();\n await adaptor.setViewState({ existingKey: \"existingValue\" });\n\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n const viewState = adaptor\n .getHostContextStore(\"viewState\")\n .getSnapshot();\n expect(viewState?.existingKey).toBe(\"existingValue\");\n expect(viewState?.__view_context).toContain(\"- Test content\");\n });\n });\n\n it(\"should handle multiple DataLLM components sharing state through adaptor\", async () => {\n const adaptor = getAdaptor();\n\n render(\n <>\n <DataLLM content=\"First component\">\n <div>First</div>\n </DataLLM>\n <DataLLM content=\"Second component\">\n <div>Second</div>\n </DataLLM>\n </>,\n );\n\n await vi.waitFor(() => {\n const viewState = adaptor\n .getHostContextStore(\"viewState\")\n .getSnapshot();\n const context = viewState?.__view_context as string;\n expect(context).toContain(\"- First component\");\n expect(context).toContain(\"- Second component\");\n });\n });\n\n it(\"should call ui/update-model-context when view state changes\", async () => {\n render(\n <DataLLM content=\"Test content\">\n <div>Child</div>\n </DataLLM>,\n );\n\n await vi.waitFor(() => {\n expect(postMessageMock).toHaveBeenCalledWith(\n expect.objectContaining({ method: \"ui/update-model-context\" }),\n \"*\",\n );\n });\n });\n });\n});\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { InferTools, ToolInput, ToolOutput, ToolResponseMetadata } from "../server/index.js";
2
- import type { CallToolResponse } from "./bridges/types.js";
2
+ import type { CallToolOptions, CallToolResponse } from "./bridges/types.js";
3
3
  import { type CallToolAsyncFn, type CallToolFn, type CallToolState } from "./hooks/use-call-tool.js";
4
4
  import { type ToolState } from "./hooks/use-tool-info.js";
5
5
  import type { Objectify, Prettify } from "./types.js";
@@ -18,24 +18,28 @@ type TypedToolInfoReturn<TInput, TOutput, TResponseMetadata> = ToolState<Objecti
18
18
  * This is the recommended way to use skybridge hooks in your views.
19
19
  * Set this up once in a dedicated file and export the typed hooks for use across your app.
20
20
  *
21
- * @typeParam ServerType - The type of your McpServer instance (use `typeof server`).
22
- * Must be a server instance created with method chaining.
23
- * TypeScript will validate that tools can be inferred from this type.
21
+ * @typeParam AppType - The type of your Skybridge app (use `typeof app`). The
22
+ * handler must return the chained server so the tool
23
+ * types are carried on the app type.
24
24
  *
25
25
  * @example
26
26
  * ```typescript
27
27
  * // src/server.ts
28
- * const server = new McpServer({ name: "my-app", version: "1.0" }, {})
29
- * .registerTool({
30
- * name: "search-trip",
31
- * inputSchema: { destination: z.string() },
32
- * outputSchema: { results: z.array(z.string()) },
33
- * view: { component: "search-trip", description: "Search trips" },
34
- * }, async ({ destination }) => {
35
- * return { content: [{ type: "text", text: `Found trips to ${destination}` }] };
36
- * });
28
+ * export const app = new Skybridge({
29
+ * name: "my-app",
30
+ * version: "1.0",
31
+ * handler: (server) =>
32
+ * server.registerTool({
33
+ * name: "search-trip",
34
+ * inputSchema: { destination: z.string() },
35
+ * outputSchema: { results: z.array(z.string()) },
36
+ * view: { component: "search-trip", description: "Search trips" },
37
+ * }, async ({ destination }) => {
38
+ * return { content: [{ type: "text", text: `Found trips to ${destination}` }] };
39
+ * }),
40
+ * });
37
41
  *
38
- * export type AppType = typeof server;
42
+ * export type AppType = typeof app;
39
43
  * ```
40
44
  *
41
45
  * @example
@@ -64,13 +68,15 @@ type TypedToolInfoReturn<TInput, TOutput, TResponseMetadata> = ToolState<Objecti
64
68
  * // toolInfo.output.structuredContent is typed based on view output schema
65
69
  * }
66
70
  * ```
71
+ *
72
+ * @see https://docs.skybridge.tech/api-reference/generate-helpers
67
73
  */
68
- export declare function generateHelpers<ServerType = never>(): {
74
+ export declare function generateHelpers<AppType = never>(): {
69
75
  /**
70
76
  * Typed version of `useCallTool` that provides autocomplete for tool names
71
77
  * and type inference for inputs and outputs.
72
78
  *
73
- * @param name - The name of the tool to call. Autocompletes based on your server's tool registry.
79
+ * @param name - The name of the tool to call. Autocompletes based on your app's tool registry.
74
80
  * @returns A hook with typed `callTool` function and `data` property.
75
81
  *
76
82
  * @example
@@ -85,12 +91,12 @@ export declare function generateHelpers<ServerType = never>(): {
85
91
  * }
86
92
  * ```
87
93
  */
88
- useCallTool: <ToolName extends keyof InferTools<ServerType> & string>(name: ToolName) => TypedCallToolReturn<ToolInput<ServerType, ToolName>, ToolOutput<ServerType, ToolName>>;
94
+ useCallTool: <ToolName extends keyof InferTools<AppType> & string>(name: ToolName, options?: CallToolOptions) => TypedCallToolReturn<ToolInput<AppType, ToolName>, ToolOutput<AppType, ToolName>>;
89
95
  /**
90
96
  * Typed version of `useToolInfo` that provides autocomplete for tool names
91
97
  * and type inference for inputs, outputs, and responseMetadata.
92
98
  *
93
- * @typeParam K - The name of the tool. Autocompletes based on your server's tool registry.
99
+ * @typeParam K - The name of the tool. Autocompletes based on your app's tool registry.
94
100
  * @returns A discriminated union with `status: "pending" | "success"` that narrows correctly.
95
101
  *
96
102
  * @example
@@ -113,6 +119,6 @@ export declare function generateHelpers<ServerType = never>(): {
113
119
  * }
114
120
  * ```
115
121
  */
116
- useToolInfo: <ToolName extends keyof InferTools<ServerType> & string>() => TypedToolInfoReturn<ToolInput<ServerType, ToolName>, ToolOutput<ServerType, ToolName>, ToolResponseMetadata<ServerType, ToolName>>;
122
+ useToolInfo: <ToolName extends keyof InferTools<AppType> & string>() => TypedToolInfoReturn<ToolInput<AppType, ToolName>, ToolOutput<AppType, ToolName>, ToolResponseMetadata<AppType, ToolName>>;
117
123
  };
118
124
  export {};
@@ -7,24 +7,28 @@ import { useToolInfo } from "./hooks/use-tool-info.js";
7
7
  * This is the recommended way to use skybridge hooks in your views.
8
8
  * Set this up once in a dedicated file and export the typed hooks for use across your app.
9
9
  *
10
- * @typeParam ServerType - The type of your McpServer instance (use `typeof server`).
11
- * Must be a server instance created with method chaining.
12
- * TypeScript will validate that tools can be inferred from this type.
10
+ * @typeParam AppType - The type of your Skybridge app (use `typeof app`). The
11
+ * handler must return the chained server so the tool
12
+ * types are carried on the app type.
13
13
  *
14
14
  * @example
15
15
  * ```typescript
16
16
  * // src/server.ts
17
- * const server = new McpServer({ name: "my-app", version: "1.0" }, {})
18
- * .registerTool({
19
- * name: "search-trip",
20
- * inputSchema: { destination: z.string() },
21
- * outputSchema: { results: z.array(z.string()) },
22
- * view: { component: "search-trip", description: "Search trips" },
23
- * }, async ({ destination }) => {
24
- * return { content: [{ type: "text", text: `Found trips to ${destination}` }] };
25
- * });
17
+ * export const app = new Skybridge({
18
+ * name: "my-app",
19
+ * version: "1.0",
20
+ * handler: (server) =>
21
+ * server.registerTool({
22
+ * name: "search-trip",
23
+ * inputSchema: { destination: z.string() },
24
+ * outputSchema: { results: z.array(z.string()) },
25
+ * view: { component: "search-trip", description: "Search trips" },
26
+ * }, async ({ destination }) => {
27
+ * return { content: [{ type: "text", text: `Found trips to ${destination}` }] };
28
+ * }),
29
+ * });
26
30
  *
27
- * export type AppType = typeof server;
31
+ * export type AppType = typeof app;
28
32
  * ```
29
33
  *
30
34
  * @example
@@ -53,6 +57,8 @@ import { useToolInfo } from "./hooks/use-tool-info.js";
53
57
  * // toolInfo.output.structuredContent is typed based on view output schema
54
58
  * }
55
59
  * ```
60
+ *
61
+ * @see https://docs.skybridge.tech/api-reference/generate-helpers
56
62
  */
57
63
  export function generateHelpers() {
58
64
  return {
@@ -60,7 +66,7 @@ export function generateHelpers() {
60
66
  * Typed version of `useCallTool` that provides autocomplete for tool names
61
67
  * and type inference for inputs and outputs.
62
68
  *
63
- * @param name - The name of the tool to call. Autocompletes based on your server's tool registry.
69
+ * @param name - The name of the tool to call. Autocompletes based on your app's tool registry.
64
70
  * @returns A hook with typed `callTool` function and `data` property.
65
71
  *
66
72
  * @example
@@ -75,14 +81,14 @@ export function generateHelpers() {
75
81
  * }
76
82
  * ```
77
83
  */
78
- useCallTool: (name) => {
79
- return useCallTool(name);
84
+ useCallTool: (name, options) => {
85
+ return useCallTool(name, options);
80
86
  },
81
87
  /**
82
88
  * Typed version of `useToolInfo` that provides autocomplete for tool names
83
89
  * and type inference for inputs, outputs, and responseMetadata.
84
90
  *
85
- * @typeParam K - The name of the tool. Autocompletes based on your server's tool registry.
91
+ * @typeParam K - The name of the tool. Autocompletes based on your app's tool registry.
86
92
  * @returns A discriminated union with `status: "pending" | "success"` that narrows correctly.
87
93
  *
88
94
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"generate-helpers.js","sourceRoot":"","sources":["../../src/web/generate-helpers.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,WAAW,GACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAkB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAoBvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,MAAM,UAAU,eAAe;IAI7B,OAAO;QACL;;;;;;;;;;;;;;;;;;WAkBG;QACH,WAAW,EAAE,CACX,IAAc,EAId,EAAE;YACF,OAAO,WAAW,CAAC,IAAI,CAGtB,CAAC;QACJ,CAAC;QAED;;;;;;;;;;;;;;;;;;;;;;;;;;WA0BG;QACH,WAAW,EAAE,GAIX,EAAE;YACF,OAAO,WAAW,EAIjB,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type {\n InferTools,\n ToolInput,\n ToolOutput,\n ToolResponseMetadata,\n} from \"../server/index.js\";\nimport type { CallToolResponse } from \"./bridges/types.js\";\nimport {\n type CallToolAsyncFn,\n type CallToolFn,\n type CallToolState,\n useCallTool,\n} from \"./hooks/use-call-tool.js\";\nimport { type ToolState, useToolInfo } from \"./hooks/use-tool-info.js\";\nimport type { Objectify, Prettify } from \"./types.js\";\n\ntype TypedCallToolResponse<TOutput> = CallToolResponse & {\n structuredContent: TOutput;\n};\n\ntype TypedCallToolReturn<TInput, TOutput> = Prettify<\n CallToolState<TypedCallToolResponse<TOutput>> & {\n callTool: CallToolFn<TInput, TypedCallToolResponse<TOutput>>;\n callToolAsync: CallToolAsyncFn<TInput, TypedCallToolResponse<TOutput>>;\n }\n>;\n\ntype TypedToolInfoReturn<TInput, TOutput, TResponseMetadata> = ToolState<\n Objectify<TInput>,\n Objectify<TOutput>,\n Objectify<TResponseMetadata>\n>;\n\n/**\n * Creates typed versions of skybridge hooks with full type inference\n * for tool names, inputs, and outputs.\n *\n * This is the recommended way to use skybridge hooks in your views.\n * Set this up once in a dedicated file and export the typed hooks for use across your app.\n *\n * @typeParam ServerType - The type of your McpServer instance (use `typeof server`).\n * Must be a server instance created with method chaining.\n * TypeScript will validate that tools can be inferred from this type.\n *\n * @example\n * ```typescript\n * // src/server.ts\n * const server = new McpServer({ name: \"my-app\", version: \"1.0\" }, {})\n * .registerTool({\n * name: \"search-trip\",\n * inputSchema: { destination: z.string() },\n * outputSchema: { results: z.array(z.string()) },\n * view: { component: \"search-trip\", description: \"Search trips\" },\n * }, async ({ destination }) => {\n * return { content: [{ type: \"text\", text: `Found trips to ${destination}` }] };\n * });\n *\n * export type AppType = typeof server;\n * ```\n *\n * @example\n * ```typescript\n * // src/helpers.ts (one-time setup)\n * import type { AppType } from \"./server\";\n * import { generateHelpers } from \"skybridge/web\";\n *\n * export const { useCallTool, useToolInfo } = generateHelpers<AppType>();\n * ```\n *\n * @example\n * ```typescript\n * // src/views/search.tsx (usage)\n * import { useCallTool, useToolInfo } from \"../helpers\";\n *\n * export function SearchView() {\n * const { callTool, data } = useCallTool(\"search-trip\");\n * // ^ autocomplete for tool names\n * callTool({ destination: \"Spain\" });\n * // ^ autocomplete for input fields\n *\n * const toolInfo = useToolInfo<\"search-trip\">();\n * // ^ autocomplete for view names\n * // toolInfo.input is typed based on view input schema\n * // toolInfo.output.structuredContent is typed based on view output schema\n * }\n * ```\n */\nexport function generateHelpers<ServerType = never>() {\n type Tools = InferTools<ServerType>;\n type ToolNames = keyof Tools & string;\n\n return {\n /**\n * Typed version of `useCallTool` that provides autocomplete for tool names\n * and type inference for inputs and outputs.\n *\n * @param name - The name of the tool to call. Autocompletes based on your server's tool registry.\n * @returns A hook with typed `callTool` function and `data` property.\n *\n * @example\n * ```typescript\n * const { callTool, data, isPending } = useCallTool(\"search-trip\");\n * // TypeScript knows callTool expects { destination: string }\n * callTool({ destination: \"Spain\" });\n *\n * // data.structuredContent is typed based on your outputSchema\n * if (data) {\n * console.log(data.structuredContent.results);\n * }\n * ```\n */\n useCallTool: <ToolName extends ToolNames>(\n name: ToolName,\n ): TypedCallToolReturn<\n ToolInput<ServerType, ToolName>,\n ToolOutput<ServerType, ToolName>\n > => {\n return useCallTool(name) as TypedCallToolReturn<\n ToolInput<ServerType, ToolName>,\n ToolOutput<ServerType, ToolName>\n >;\n },\n\n /**\n * Typed version of `useToolInfo` that provides autocomplete for tool names\n * and type inference for inputs, outputs, and responseMetadata.\n *\n * @typeParam K - The name of the tool. Autocompletes based on your server's tool registry.\n * @returns A discriminated union with `status: \"pending\" | \"success\"` that narrows correctly.\n *\n * @example\n * ```typescript\n * const toolInfo = useToolInfo<\"search-trip\">();\n * // toolInfo.input is typed as { destination: string; ... }\n * // toolInfo.output is typed as { results: Array<...>; ... }\n * // toolInfo.responseMetadata is typed based on _meta in callback return\n * // toolInfo.status narrows correctly: \"pending\" | \"success\"\n *\n * if (toolInfo.isPending) {\n * // TypeScript knows output and responseMetadata are undefined here\n * console.log(toolInfo.input.destination);\n * }\n *\n * if (toolInfo.isSuccess) {\n * // TypeScript knows output and responseMetadata are defined here\n * console.log(toolInfo.output.results);\n * console.log(toolInfo.responseMetadata);\n * }\n * ```\n */\n useToolInfo: <ToolName extends ToolNames>(): TypedToolInfoReturn<\n ToolInput<ServerType, ToolName>,\n ToolOutput<ServerType, ToolName>,\n ToolResponseMetadata<ServerType, ToolName>\n > => {\n return useToolInfo() as TypedToolInfoReturn<\n ToolInput<ServerType, ToolName>,\n ToolOutput<ServerType, ToolName>,\n ToolResponseMetadata<ServerType, ToolName>\n >;\n },\n };\n}\n"]}
1
+ {"version":3,"file":"generate-helpers.js","sourceRoot":"","sources":["../../src/web/generate-helpers.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,WAAW,GACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAkB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAoBvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,MAAM,UAAU,eAAe;IAI7B,OAAO;QACL;;;;;;;;;;;;;;;;;;WAkBG;QACH,WAAW,EAAE,CACX,IAAc,EACd,OAAyB,EAIzB,EAAE;YACF,OAAO,WAAW,CAAC,IAAI,EAAE,OAAO,CAG/B,CAAC;QACJ,CAAC;QAED;;;;;;;;;;;;;;;;;;;;;;;;;;WA0BG;QACH,WAAW,EAAE,GAIX,EAAE;YACF,OAAO,WAAW,EAIjB,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type {\n InferTools,\n ToolInput,\n ToolOutput,\n ToolResponseMetadata,\n} from \"../server/index.js\";\nimport type { CallToolOptions, CallToolResponse } from \"./bridges/types.js\";\nimport {\n type CallToolAsyncFn,\n type CallToolFn,\n type CallToolState,\n useCallTool,\n} from \"./hooks/use-call-tool.js\";\nimport { type ToolState, useToolInfo } from \"./hooks/use-tool-info.js\";\nimport type { Objectify, Prettify } from \"./types.js\";\n\ntype TypedCallToolResponse<TOutput> = CallToolResponse & {\n structuredContent: TOutput;\n};\n\ntype TypedCallToolReturn<TInput, TOutput> = Prettify<\n CallToolState<TypedCallToolResponse<TOutput>> & {\n callTool: CallToolFn<TInput, TypedCallToolResponse<TOutput>>;\n callToolAsync: CallToolAsyncFn<TInput, TypedCallToolResponse<TOutput>>;\n }\n>;\n\ntype TypedToolInfoReturn<TInput, TOutput, TResponseMetadata> = ToolState<\n Objectify<TInput>,\n Objectify<TOutput>,\n Objectify<TResponseMetadata>\n>;\n\n/**\n * Creates typed versions of skybridge hooks with full type inference\n * for tool names, inputs, and outputs.\n *\n * This is the recommended way to use skybridge hooks in your views.\n * Set this up once in a dedicated file and export the typed hooks for use across your app.\n *\n * @typeParam AppType - The type of your Skybridge app (use `typeof app`). The\n * handler must return the chained server so the tool\n * types are carried on the app type.\n *\n * @example\n * ```typescript\n * // src/server.ts\n * export const app = new Skybridge({\n * name: \"my-app\",\n * version: \"1.0\",\n * handler: (server) =>\n * server.registerTool({\n * name: \"search-trip\",\n * inputSchema: { destination: z.string() },\n * outputSchema: { results: z.array(z.string()) },\n * view: { component: \"search-trip\", description: \"Search trips\" },\n * }, async ({ destination }) => {\n * return { content: [{ type: \"text\", text: `Found trips to ${destination}` }] };\n * }),\n * });\n *\n * export type AppType = typeof app;\n * ```\n *\n * @example\n * ```typescript\n * // src/helpers.ts (one-time setup)\n * import type { AppType } from \"./server\";\n * import { generateHelpers } from \"skybridge/web\";\n *\n * export const { useCallTool, useToolInfo } = generateHelpers<AppType>();\n * ```\n *\n * @example\n * ```typescript\n * // src/views/search.tsx (usage)\n * import { useCallTool, useToolInfo } from \"../helpers\";\n *\n * export function SearchView() {\n * const { callTool, data } = useCallTool(\"search-trip\");\n * // ^ autocomplete for tool names\n * callTool({ destination: \"Spain\" });\n * // ^ autocomplete for input fields\n *\n * const toolInfo = useToolInfo<\"search-trip\">();\n * // ^ autocomplete for view names\n * // toolInfo.input is typed based on view input schema\n * // toolInfo.output.structuredContent is typed based on view output schema\n * }\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/generate-helpers\n */\nexport function generateHelpers<AppType = never>() {\n type Tools = InferTools<AppType>;\n type ToolNames = keyof Tools & string;\n\n return {\n /**\n * Typed version of `useCallTool` that provides autocomplete for tool names\n * and type inference for inputs and outputs.\n *\n * @param name - The name of the tool to call. Autocompletes based on your app's tool registry.\n * @returns A hook with typed `callTool` function and `data` property.\n *\n * @example\n * ```typescript\n * const { callTool, data, isPending } = useCallTool(\"search-trip\");\n * // TypeScript knows callTool expects { destination: string }\n * callTool({ destination: \"Spain\" });\n *\n * // data.structuredContent is typed based on your outputSchema\n * if (data) {\n * console.log(data.structuredContent.results);\n * }\n * ```\n */\n useCallTool: <ToolName extends ToolNames>(\n name: ToolName,\n options?: CallToolOptions,\n ): TypedCallToolReturn<\n ToolInput<AppType, ToolName>,\n ToolOutput<AppType, ToolName>\n > => {\n return useCallTool(name, options) as TypedCallToolReturn<\n ToolInput<AppType, ToolName>,\n ToolOutput<AppType, ToolName>\n >;\n },\n\n /**\n * Typed version of `useToolInfo` that provides autocomplete for tool names\n * and type inference for inputs, outputs, and responseMetadata.\n *\n * @typeParam K - The name of the tool. Autocompletes based on your app's tool registry.\n * @returns A discriminated union with `status: \"pending\" | \"success\"` that narrows correctly.\n *\n * @example\n * ```typescript\n * const toolInfo = useToolInfo<\"search-trip\">();\n * // toolInfo.input is typed as { destination: string; ... }\n * // toolInfo.output is typed as { results: Array<...>; ... }\n * // toolInfo.responseMetadata is typed based on _meta in callback return\n * // toolInfo.status narrows correctly: \"pending\" | \"success\"\n *\n * if (toolInfo.isPending) {\n * // TypeScript knows output and responseMetadata are undefined here\n * console.log(toolInfo.input.destination);\n * }\n *\n * if (toolInfo.isSuccess) {\n * // TypeScript knows output and responseMetadata are defined here\n * console.log(toolInfo.output.results);\n * console.log(toolInfo.responseMetadata);\n * }\n * ```\n */\n useToolInfo: <ToolName extends ToolNames>(): TypedToolInfoReturn<\n ToolInput<AppType, ToolName>,\n ToolOutput<AppType, ToolName>,\n ToolResponseMetadata<AppType, ToolName>\n > => {\n return useToolInfo() as TypedToolInfoReturn<\n ToolInput<AppType, ToolName>,\n ToolOutput<AppType, ToolName>,\n ToolResponseMetadata<AppType, ToolName>\n >;\n },\n };\n}\n"]}
@@ -1,8 +1,8 @@
1
1
  import { expectTypeOf, test } from "vitest";
2
- import { createInterfaceTestServer, createTestServer } from "../test/utils.js";
2
+ import { createInterfaceTestApp, createTestApp } from "../test/utils.js";
3
3
  import { generateHelpers } from "./generate-helpers.js";
4
- const server = createTestServer();
5
- const interfaceServer = createInterfaceTestServer();
4
+ const app = createTestApp();
5
+ const interfaceApp = createInterfaceTestApp();
6
6
  test("InferTools extracts the tool registry type (views + registerTool)", () => {
7
7
  expectTypeOf().toHaveProperty("search-trip");
8
8
  expectTypeOf().toHaveProperty("get-trip-details");
@@ -154,11 +154,13 @@ test("generateHelpers provides autocomplete for tool names in useToolInfo (views
154
154
  test("useToolInfo infers input and output types", () => {
155
155
  const { useToolInfo } = generateHelpers();
156
156
  const toolInfo = useToolInfo();
157
- // Input is only available when not in idle state
158
- if (!(toolInfo.status === "idle")) {
157
+ // Input is optional in pending — host may not have delivered args yet
158
+ if (toolInfo.status === "pending") {
159
159
  expectTypeOf(toolInfo.input).toExtend();
160
160
  }
161
161
  if (toolInfo.status === "success") {
162
+ // Input is optional in success — the host may not have surfaced args yet
163
+ expectTypeOf(toolInfo.input).toExtend();
162
164
  expectTypeOf(toolInfo.output).toExtend();
163
165
  expectTypeOf(toolInfo.output.results).toBeArray();
164
166
  expectTypeOf(toolInfo.output.totalCount).toBeNumber();