@ulearning/ai-generative-frame 0.0.0-stage → 1.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -2
  3. package/dist/assistant-ui/entry.d.ts +34 -0
  4. package/dist/assistant-ui/entry.d.ts.map +1 -0
  5. package/dist/assistant-ui/entry.js +50 -0
  6. package/dist/assistant-ui/entry.js.map +1 -0
  7. package/dist/assistant-ui/extension.d.ts +18 -0
  8. package/dist/assistant-ui/extension.d.ts.map +1 -0
  9. package/dist/assistant-ui/extension.js +0 -0
  10. package/dist/assistant-ui/history.d.ts +15 -0
  11. package/dist/assistant-ui/history.d.ts.map +1 -0
  12. package/dist/assistant-ui/history.js +39 -0
  13. package/dist/assistant-ui/history.js.map +1 -0
  14. package/dist/assistant-ui/modelContext.d.ts +45 -0
  15. package/dist/assistant-ui/modelContext.d.ts.map +1 -0
  16. package/dist/assistant-ui/modelContext.js +88 -0
  17. package/dist/assistant-ui/modelContext.js.map +1 -0
  18. package/dist/assistant-ui/snapshotSaver.d.ts +11 -0
  19. package/dist/assistant-ui/snapshotSaver.d.ts.map +1 -0
  20. package/dist/assistant-ui/snapshotSaver.js +83 -0
  21. package/dist/assistant-ui/snapshotSaver.js.map +1 -0
  22. package/dist/assistant-ui/tool-calls.d.ts +21 -0
  23. package/dist/assistant-ui/tool-calls.d.ts.map +1 -0
  24. package/dist/assistant-ui/tool-calls.js +65 -0
  25. package/dist/assistant-ui/tool-calls.js.map +1 -0
  26. package/dist/assistant-ui/toolkit.d.ts +96 -0
  27. package/dist/assistant-ui/toolkit.d.ts.map +1 -0
  28. package/dist/assistant-ui/toolkit.js +456 -0
  29. package/dist/assistant-ui/toolkit.js.map +1 -0
  30. package/dist/assistant-ui/useSharedContext.d.ts +14 -0
  31. package/dist/assistant-ui/useSharedContext.d.ts.map +1 -0
  32. package/dist/assistant-ui/useSharedContext.js +27 -0
  33. package/dist/assistant-ui/useSharedContext.js.map +1 -0
  34. package/dist/assistant-ui.d.ts +3 -0
  35. package/dist/assistant-ui.d.ts.map +1 -0
  36. package/dist/assistant-ui.js +1 -0
  37. package/dist/bootstrap.d.ts +23 -0
  38. package/dist/bootstrap.d.ts.map +1 -0
  39. package/dist/bootstrap.js +78 -0
  40. package/dist/bootstrap.js.map +1 -0
  41. package/dist/csp.d.ts +25 -0
  42. package/dist/csp.d.ts.map +1 -0
  43. package/dist/csp.js +78 -0
  44. package/dist/csp.js.map +1 -0
  45. package/dist/index.d.ts +6 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +5 -0
  48. package/dist/json-schema.d.ts +70 -0
  49. package/dist/json-schema.d.ts.map +1 -0
  50. package/dist/json-schema.js +163 -0
  51. package/dist/json-schema.js.map +1 -0
  52. package/dist/preview.d.ts +30 -0
  53. package/dist/preview.d.ts.map +1 -0
  54. package/dist/preview.js +60 -0
  55. package/dist/preview.js.map +1 -0
  56. package/dist/prompts/guidance.d.ts +41 -0
  57. package/dist/prompts/guidance.d.ts.map +1 -0
  58. package/dist/prompts/guidance.js +268 -0
  59. package/dist/prompts/guidance.js.map +1 -0
  60. package/dist/prompts.d.ts +2 -0
  61. package/dist/prompts.d.ts.map +1 -0
  62. package/dist/prompts.js +1 -0
  63. package/dist/protocol.d.ts +114 -0
  64. package/dist/protocol.d.ts.map +1 -0
  65. package/dist/protocol.js +49 -0
  66. package/dist/protocol.js.map +1 -0
  67. package/dist/repair/repair.d.ts +72 -0
  68. package/dist/repair/repair.d.ts.map +1 -0
  69. package/dist/repair/repair.js +103 -0
  70. package/dist/repair/repair.js.map +1 -0
  71. package/dist/repair.d.ts +2 -0
  72. package/dist/repair.d.ts.map +1 -0
  73. package/dist/repair.js +1 -0
  74. package/dist/rpc.d.ts +60 -0
  75. package/dist/rpc.d.ts.map +1 -0
  76. package/dist/rpc.js +127 -0
  77. package/dist/rpc.js.map +1 -0
  78. package/dist/runtime/diagnostics.d.ts +17 -0
  79. package/dist/runtime/diagnostics.d.ts.map +1 -0
  80. package/dist/runtime/diagnostics.js +128 -0
  81. package/dist/runtime/diagnostics.js.map +1 -0
  82. package/dist/runtime/generated.d.ts +2 -0
  83. package/dist/runtime/generated.d.ts.map +1 -0
  84. package/dist/runtime/generated.js +5 -0
  85. package/dist/runtime/generated.js.map +1 -0
  86. package/dist/runtime/index.d.ts +7 -0
  87. package/dist/runtime/index.d.ts.map +1 -0
  88. package/dist/runtime/index.js +264 -0
  89. package/dist/runtime/index.js.map +1 -0
  90. package/dist/runtime/morph.d.ts +51 -0
  91. package/dist/runtime/morph.d.ts.map +1 -0
  92. package/dist/runtime/morph.js +251 -0
  93. package/dist/runtime/morph.js.map +1 -0
  94. package/dist/runtime/screenshot.d.ts +23 -0
  95. package/dist/runtime/screenshot.d.ts.map +1 -0
  96. package/dist/runtime/screenshot.js +188 -0
  97. package/dist/runtime/screenshot.js.map +1 -0
  98. package/dist/runtime/size.d.ts +10 -0
  99. package/dist/runtime/size.d.ts.map +1 -0
  100. package/dist/runtime/size.js +21 -0
  101. package/dist/runtime/size.js.map +1 -0
  102. package/dist/runtime/storage.d.ts +4 -0
  103. package/dist/runtime/storage.d.ts.map +1 -0
  104. package/dist/runtime/storage.js +38 -0
  105. package/dist/runtime/storage.js.map +1 -0
  106. package/dist/spec/actions.d.ts +40 -0
  107. package/dist/spec/actions.d.ts.map +1 -0
  108. package/dist/spec/actions.js +53 -0
  109. package/dist/spec/actions.js.map +1 -0
  110. package/dist/spec/assistant-ui/thread.d.ts +66 -0
  111. package/dist/spec/assistant-ui/thread.d.ts.map +1 -0
  112. package/dist/spec/assistant-ui/thread.js +252 -0
  113. package/dist/spec/assistant-ui/thread.js.map +1 -0
  114. package/dist/spec/assistant-ui/toolkit.d.ts +33 -0
  115. package/dist/spec/assistant-ui/toolkit.d.ts.map +1 -0
  116. package/dist/spec/assistant-ui/toolkit.js +142 -0
  117. package/dist/spec/assistant-ui/toolkit.js.map +1 -0
  118. package/dist/spec/assistant-ui/web.d.ts +6 -0
  119. package/dist/spec/assistant-ui/web.d.ts.map +1 -0
  120. package/dist/spec/assistant-ui/web.js +48 -0
  121. package/dist/spec/assistant-ui/web.js.map +1 -0
  122. package/dist/spec/assistant-ui.d.ts +3 -0
  123. package/dist/spec/assistant-ui.d.ts.map +1 -0
  124. package/dist/spec/assistant-ui.js +1 -0
  125. package/dist/spec/calc.d.ts +8 -0
  126. package/dist/spec/calc.d.ts.map +1 -0
  127. package/dist/spec/calc.js +498 -0
  128. package/dist/spec/calc.js.map +1 -0
  129. package/dist/spec/catalog.d.ts +52 -0
  130. package/dist/spec/catalog.d.ts.map +1 -0
  131. package/dist/spec/catalog.js +104 -0
  132. package/dist/spec/catalog.js.map +1 -0
  133. package/dist/spec/expressions.d.ts +45 -0
  134. package/dist/spec/expressions.d.ts.map +1 -0
  135. package/dist/spec/expressions.js +199 -0
  136. package/dist/spec/expressions.js.map +1 -0
  137. package/dist/spec/patch.d.ts +20 -0
  138. package/dist/spec/patch.d.ts.map +1 -0
  139. package/dist/spec/patch.js +77 -0
  140. package/dist/spec/patch.js.map +1 -0
  141. package/dist/spec/pending.d.ts +15 -0
  142. package/dist/spec/pending.d.ts.map +1 -0
  143. package/dist/spec/pending.js +33 -0
  144. package/dist/spec/pending.js.map +1 -0
  145. package/dist/spec/pointer.d.ts +17 -0
  146. package/dist/spec/pointer.d.ts.map +1 -0
  147. package/dist/spec/pointer.js +88 -0
  148. package/dist/spec/pointer.js.map +1 -0
  149. package/dist/spec/prompt.d.ts +29 -0
  150. package/dist/spec/prompt.d.ts.map +1 -0
  151. package/dist/spec/prompt.js +145 -0
  152. package/dist/spec/prompt.js.map +1 -0
  153. package/dist/spec/render-spec.d.ts +50 -0
  154. package/dist/spec/render-spec.d.ts.map +1 -0
  155. package/dist/spec/render-spec.js +104 -0
  156. package/dist/spec/render-spec.js.map +1 -0
  157. package/dist/spec/resolve.d.ts +39 -0
  158. package/dist/spec/resolve.d.ts.map +1 -0
  159. package/dist/spec/resolve.js +51 -0
  160. package/dist/spec/resolve.js.map +1 -0
  161. package/dist/spec/resolved.d.ts +3 -0
  162. package/dist/spec/resolved.d.ts.map +1 -0
  163. package/dist/spec/resolved.js +36 -0
  164. package/dist/spec/resolved.js.map +1 -0
  165. package/dist/spec/state.d.ts +34 -0
  166. package/dist/spec/state.d.ts.map +1 -0
  167. package/dist/spec/state.js +111 -0
  168. package/dist/spec/state.js.map +1 -0
  169. package/dist/spec/stream.d.ts +50 -0
  170. package/dist/spec/stream.d.ts.map +1 -0
  171. package/dist/spec/stream.js +168 -0
  172. package/dist/spec/stream.js.map +1 -0
  173. package/dist/spec/tools.d.ts +3 -0
  174. package/dist/spec/tools.d.ts.map +1 -0
  175. package/dist/spec/tools.js +3 -0
  176. package/dist/spec/tree.d.ts +21 -0
  177. package/dist/spec/tree.d.ts.map +1 -0
  178. package/dist/spec/tree.js +27 -0
  179. package/dist/spec/tree.js.map +1 -0
  180. package/dist/spec/types.d.ts +66 -0
  181. package/dist/spec/types.d.ts.map +1 -0
  182. package/dist/spec/types.js +9 -0
  183. package/dist/spec/types.js.map +1 -0
  184. package/dist/spec/validate.d.ts +32 -0
  185. package/dist/spec/validate.d.ts.map +1 -0
  186. package/dist/spec/validate.js +264 -0
  187. package/dist/spec/validate.js.map +1 -0
  188. package/dist/spec/vue/SpecRenderer.d.ts +43 -0
  189. package/dist/spec/vue/SpecRenderer.d.ts.map +1 -0
  190. package/dist/spec/vue/SpecRenderer.js +357 -0
  191. package/dist/spec/vue/SpecRenderer.js.map +1 -0
  192. package/dist/spec/vue/useSpecStream.d.ts +19 -0
  193. package/dist/spec/vue/useSpecStream.d.ts.map +1 -0
  194. package/dist/spec/vue/useSpecStream.js +115 -0
  195. package/dist/spec/vue/useSpecStream.js.map +1 -0
  196. package/dist/spec/vue.d.ts +3 -0
  197. package/dist/spec/vue.d.ts.map +1 -0
  198. package/dist/spec/vue.js +3 -0
  199. package/dist/spec.d.ts +12 -0
  200. package/dist/spec.d.ts.map +1 -0
  201. package/dist/spec.js +8 -0
  202. package/dist/theme.d.ts +38 -0
  203. package/dist/theme.d.ts.map +1 -0
  204. package/dist/theme.js +370 -0
  205. package/dist/theme.js.map +1 -0
  206. package/dist/tools/define.d.ts +43 -0
  207. package/dist/tools/define.d.ts.map +1 -0
  208. package/dist/tools/define.js +31 -0
  209. package/dist/tools/define.js.map +1 -0
  210. package/dist/tools/edits.d.ts +19 -0
  211. package/dist/tools/edits.d.ts.map +1 -0
  212. package/dist/tools/edits.js +55 -0
  213. package/dist/tools/edits.js.map +1 -0
  214. package/dist/tools/registry.d.ts +19 -0
  215. package/dist/tools/registry.d.ts.map +1 -0
  216. package/dist/tools/registry.js +33 -0
  217. package/dist/tools/registry.js.map +1 -0
  218. package/dist/tools/tools.d.ts +88 -0
  219. package/dist/tools/tools.d.ts.map +1 -0
  220. package/dist/tools/tools.js +235 -0
  221. package/dist/tools/tools.js.map +1 -0
  222. package/dist/tools.d.ts +5 -0
  223. package/dist/tools.d.ts.map +1 -0
  224. package/dist/tools.js +5 -0
  225. package/dist/version.d.ts +2 -0
  226. package/dist/version.d.ts.map +1 -0
  227. package/dist/version.js +5 -0
  228. package/dist/version.js.map +1 -0
  229. package/dist/vue/Widget.d.ts +16 -0
  230. package/dist/vue/Widget.d.ts.map +1 -0
  231. package/dist/vue/Widget.js +58 -0
  232. package/dist/vue/Widget.js.map +1 -0
  233. package/dist/vue/sync.d.ts +19 -0
  234. package/dist/vue/sync.d.ts.map +1 -0
  235. package/dist/vue/sync.js +31 -0
  236. package/dist/vue/sync.js.map +1 -0
  237. package/dist/vue/themeStore.d.ts +23 -0
  238. package/dist/vue/themeStore.d.ts.map +1 -0
  239. package/dist/vue/themeStore.js +115 -0
  240. package/dist/vue/themeStore.js.map +1 -0
  241. package/dist/vue/useThemeTokens.d.ts +6 -0
  242. package/dist/vue/useThemeTokens.d.ts.map +1 -0
  243. package/dist/vue/useThemeTokens.js +49 -0
  244. package/dist/vue/useThemeTokens.js.map +1 -0
  245. package/dist/vue/useWidget.d.ts +10 -0
  246. package/dist/vue/useWidget.d.ts.map +1 -0
  247. package/dist/vue/useWidget.js +81 -0
  248. package/dist/vue/useWidget.js.map +1 -0
  249. package/dist/vue.d.ts +5 -0
  250. package/dist/vue.d.ts.map +1 -0
  251. package/dist/vue.js +4 -0
  252. package/dist/widget.d.ts +129 -0
  253. package/dist/widget.d.ts.map +1 -0
  254. package/dist/widget.js +655 -0
  255. package/dist/widget.js.map +1 -0
  256. package/package.json +125 -3
  257. package/src/assistant-ui/entry.ts +124 -0
  258. package/src/assistant-ui/extension.ts +19 -0
  259. package/src/assistant-ui/history.ts +63 -0
  260. package/src/assistant-ui/modelContext.ts +162 -0
  261. package/src/assistant-ui/snapshotSaver.ts +116 -0
  262. package/src/assistant-ui/tool-calls.ts +115 -0
  263. package/src/assistant-ui/toolkit.tsx +747 -0
  264. package/src/assistant-ui/useSharedContext.ts +40 -0
  265. package/src/assistant-ui.ts +14 -0
  266. package/src/bootstrap.ts +112 -0
  267. package/src/csp.ts +96 -0
  268. package/src/index.ts +44 -0
  269. package/src/json-schema.ts +353 -0
  270. package/src/preview.ts +120 -0
  271. package/src/prompts/guidance.ts +369 -0
  272. package/src/prompts.ts +8 -0
  273. package/src/protocol.ts +141 -0
  274. package/src/repair/repair.ts +202 -0
  275. package/src/repair.ts +11 -0
  276. package/src/rpc.ts +228 -0
  277. package/src/runtime/diagnostics.ts +165 -0
  278. package/src/runtime/generated.ts +2 -0
  279. package/src/runtime/index.ts +343 -0
  280. package/src/runtime/morph.ts +352 -0
  281. package/src/runtime/screenshot.ts +288 -0
  282. package/src/runtime/size.ts +17 -0
  283. package/src/runtime/storage.ts +49 -0
  284. package/src/spec/actions.ts +111 -0
  285. package/src/spec/assistant-ui/thread.ts +381 -0
  286. package/src/spec/assistant-ui/toolkit.tsx +295 -0
  287. package/src/spec/assistant-ui/web.tsx +63 -0
  288. package/src/spec/assistant-ui.ts +2 -0
  289. package/src/spec/calc.ts +598 -0
  290. package/src/spec/catalog.ts +196 -0
  291. package/src/spec/expressions.ts +301 -0
  292. package/src/spec/patch.ts +112 -0
  293. package/src/spec/pending.ts +36 -0
  294. package/src/spec/pointer.ts +170 -0
  295. package/src/spec/prompt.ts +243 -0
  296. package/src/spec/render-spec.ts +144 -0
  297. package/src/spec/resolve.ts +123 -0
  298. package/src/spec/resolved.ts +53 -0
  299. package/src/spec/state.ts +193 -0
  300. package/src/spec/stream.ts +230 -0
  301. package/src/spec/tools.ts +17 -0
  302. package/src/spec/tree.ts +37 -0
  303. package/src/spec/types.ts +51 -0
  304. package/src/spec/validate.ts +429 -0
  305. package/src/spec/vue/SpecRenderer.tsx +544 -0
  306. package/src/spec/vue/__tests__/harness.ts +50 -0
  307. package/src/spec/vue/useSpecStream.ts +162 -0
  308. package/src/spec/vue.ts +13 -0
  309. package/src/spec.ts +55 -0
  310. package/src/theme.ts +423 -0
  311. package/src/tools/define.ts +65 -0
  312. package/src/tools/edits.ts +73 -0
  313. package/src/tools/registry.ts +44 -0
  314. package/src/tools/tools.ts +361 -0
  315. package/src/tools.ts +32 -0
  316. package/src/version.ts +4 -0
  317. package/src/vue/Widget.tsx +74 -0
  318. package/src/vue/sync.ts +36 -0
  319. package/src/vue/themeStore.ts +157 -0
  320. package/src/vue/useThemeTokens.ts +71 -0
  321. package/src/vue/useWidget.ts +117 -0
  322. package/src/vue.ts +8 -0
  323. package/src/widget.ts +1080 -0
@@ -0,0 +1,49 @@
1
+ // src/protocol.ts
2
+ const GENFRAME_PROTOCOL_VERSION = "2026-01-26";
3
+ /** Window messages exchanged once, before the private port exists. */
4
+ const READY_MESSAGE = "genframe:ready";
5
+ const INIT_MESSAGE = "genframe:init";
6
+ const METHODS = {
7
+ write: "genframe/write",
8
+ end: "genframe/end",
9
+ replace: "genframe/replace",
10
+ screenshot: "genframe/screenshot",
11
+ inspect: "genframe/inspect",
12
+ clearStorage: "genframe/clear-storage",
13
+ hostContextChanged: "ui/notifications/host-context-changed",
14
+ toolInputPartial: "ui/notifications/tool-input-partial",
15
+ toolInput: "ui/notifications/tool-input",
16
+ toolResult: "ui/notifications/tool-result",
17
+ ready: "genframe/ready",
18
+ initialize: "ui/initialize",
19
+ initialized: "ui/notifications/initialized",
20
+ sizeChanged: "ui/notifications/size-changed",
21
+ openLink: "ui/open-link",
22
+ message: "ui/message",
23
+ callTool: "tools/call",
24
+ requestDisplayMode: "ui/request-display-mode",
25
+ updateModelContext: "ui/update-model-context",
26
+ copyText: "genframe/copy-text",
27
+ widgetState: "genframe/widget-state",
28
+ log: "genframe/log",
29
+ error: "genframe/error"
30
+ };
31
+ const SVG_TAG = /^<svg[\s>/]/i;
32
+ /** Classifies widget code: SVG when it starts with an `<svg` element, otherwise HTML. */
33
+ function detectWidgetKind(code) {
34
+ let rest = code.trimStart();
35
+ if (rest.startsWith("<?xml")) {
36
+ const end = rest.indexOf(">");
37
+ if (end === -1) return "html";
38
+ rest = rest.slice(end + 1).trimStart();
39
+ }
40
+ while (rest.startsWith("<!--")) {
41
+ const end = rest.indexOf("-->", 4);
42
+ if (end === -1) return "html";
43
+ rest = rest.slice(end + 3).trimStart();
44
+ }
45
+ return SVG_TAG.test(rest) ? "svg" : "html";
46
+ }
47
+ export { GENFRAME_PROTOCOL_VERSION, INIT_MESSAGE, METHODS, READY_MESSAGE, detectWidgetKind };
48
+
49
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","names":[],"sources":["../src/protocol.ts"],"sourcesContent":["export const GENFRAME_PROTOCOL_VERSION = \"2026-01-26\";\n\n/** Window messages exchanged once, before the private port exists. */\nexport const READY_MESSAGE = \"genframe:ready\";\nexport const INIT_MESSAGE = \"genframe:init\";\n\nexport const METHODS = {\n // host → frame\n write: \"genframe/write\",\n end: \"genframe/end\",\n replace: \"genframe/replace\",\n screenshot: \"genframe/screenshot\",\n inspect: \"genframe/inspect\",\n clearStorage: \"genframe/clear-storage\",\n hostContextChanged: \"ui/notifications/host-context-changed\",\n toolInputPartial: \"ui/notifications/tool-input-partial\",\n toolInput: \"ui/notifications/tool-input\",\n toolResult: \"ui/notifications/tool-result\",\n // frame → host\n ready: \"genframe/ready\",\n initialize: \"ui/initialize\",\n initialized: \"ui/notifications/initialized\",\n sizeChanged: \"ui/notifications/size-changed\",\n openLink: \"ui/open-link\",\n message: \"ui/message\",\n callTool: \"tools/call\",\n requestDisplayMode: \"ui/request-display-mode\",\n updateModelContext: \"ui/update-model-context\",\n copyText: \"genframe/copy-text\",\n widgetState: \"genframe/widget-state\",\n log: \"genframe/log\",\n error: \"genframe/error\",\n} as const;\n\nexport type WidgetKind = \"html\" | \"svg\";\n\nexport type ColorScheme = \"light\" | \"dark\";\n\nexport type DisplayMode = \"inline\" | \"fullscreen\" | \"pip\";\n\n/**\n * The params of a widget's MCP Apps `ui/initialize` request, as\n * `onInitialized` receives them. A malformed field is omitted, and\n * `availableDisplayModes` keeps only the modes in `DisplayMode`.\n */\nexport type McpAppInitializeParams = {\n appInfo?: { name: string; version: string; [key: string]: unknown };\n appCapabilities?: {\n availableDisplayModes?: DisplayMode[];\n [key: string]: unknown;\n };\n protocolVersion?: string;\n};\n\n/** The MCP Apps host context, plus the fields this package adds. */\nexport type HostContext = {\n theme?: ColorScheme;\n styles?: { variables?: Record<string, string> };\n displayMode?: DisplayMode;\n availableDisplayModes?: DisplayMode[];\n locale?: string;\n platform?: \"web\" | \"desktop\" | \"mobile\";\n containerDimensions?: { width?: number; maxHeight?: number };\n [key: string]: unknown;\n};\n\nexport type InitMessage = {\n type: typeof INIT_MESSAGE;\n context: HostContext;\n state?: unknown;\n};\n\nexport type WidgetErrorKind =\n | \"error\"\n | \"unhandledrejection\"\n | \"csp\"\n | \"resource\"\n | \"script\";\n\nexport type WidgetError = {\n kind: WidgetErrorKind;\n message: string;\n source?: string;\n line?: number;\n column?: number;\n stack?: string;\n};\n\nexport type ConsoleLevel = \"log\" | \"info\" | \"warn\" | \"error\" | \"debug\";\n\nexport type ConsoleEntry = {\n level: ConsoleLevel;\n message: string;\n};\n\nexport type WidgetSize = { width: number; height: number };\n\nexport type EndResult = {\n size: WidgetSize;\n blank: boolean;\n errorCount: number;\n};\n\nexport type ClearStorageResult = {\n /** The storage kinds that were cleared; a kind the browser lacks is left out. */\n cleared: string[];\n};\n\nexport type FrameInspection = {\n kind: WidgetKind;\n ended: boolean;\n size: WidgetSize;\n blank: boolean;\n errors: WidgetError[];\n console: ConsoleEntry[];\n};\n\nexport type ScreenshotOptions = {\n /** Device pixel ratio of the capture. Defaults to the frame's, capped at 2. */\n scale?: number;\n /** CSS color painted behind the widget. Defaults to `--color-background`. */\n background?: string;\n};\n\nconst SVG_TAG = /^<svg[\\s>/]/i;\n\n/** Classifies widget code: SVG when it starts with an `<svg` element, otherwise HTML. */\nexport function detectWidgetKind(code: string): WidgetKind {\n let rest = code.trimStart();\n if (rest.startsWith(\"<?xml\")) {\n const end = rest.indexOf(\">\");\n if (end === -1) return \"html\";\n rest = rest.slice(end + 1).trimStart();\n }\n while (rest.startsWith(\"<!--\")) {\n const end = rest.indexOf(\"-->\", 4);\n if (end === -1) return \"html\";\n rest = rest.slice(end + 3).trimStart();\n }\n return SVG_TAG.test(rest) ? \"svg\" : \"html\";\n}\n"],"mappings":";AAAA,MAAa,4BAA4B;;AAGzC,MAAa,gBAAgB;AAC7B,MAAa,eAAe;AAE5B,MAAa,UAAU;CAErB,OAAO;CACP,KAAK;CACL,SAAS;CACT,YAAY;CACZ,SAAS;CACT,cAAc;CACd,oBAAoB;CACpB,kBAAkB;CAClB,WAAW;CACX,YAAY;CAEZ,OAAO;CACP,YAAY;CACZ,aAAa;CACb,aAAa;CACb,UAAU;CACV,SAAS;CACT,UAAU;CACV,oBAAoB;CACpB,oBAAoB;CACpB,UAAU;CACV,aAAa;CACb,KAAK;CACL,OAAO;AACT;AA4FA,MAAM,UAAU;;AAGhB,SAAgB,iBAAiB,MAA0B;CACzD,IAAI,OAAO,KAAK,UAAU;CAC1B,IAAI,KAAK,WAAW,OAAO,GAAG;EAC5B,MAAM,MAAM,KAAK,QAAQ,GAAG;EAC5B,IAAI,QAAQ,IAAI,OAAO;EACvB,OAAO,KAAK,MAAM,MAAM,CAAC,CAAC,CAAC,UAAU;CACvC;CACA,OAAO,KAAK,WAAW,MAAM,GAAG;EAC9B,MAAM,MAAM,KAAK,QAAQ,OAAO,CAAC;EACjC,IAAI,QAAQ,IAAI,OAAO;EACvB,OAAO,KAAK,MAAM,MAAM,CAAC,CAAC,CAAC,UAAU;CACvC;CACA,OAAO,QAAQ,KAAK,IAAI,IAAI,QAAQ;AACtC"}
@@ -0,0 +1,72 @@
1
+ import type { ConsoleEntry, WidgetError } from "../protocol.js";
2
+ import { type WidgetEdit } from "../tools/edits.js";
3
+ /** What one render of a widget produced; `previewWidget` returns a compatible shape. */
4
+ export type RenderReport = {
5
+ errors: WidgetError[];
6
+ console: ConsoleEntry[];
7
+ blank: boolean;
8
+ height?: number;
9
+ screenshot?: string;
10
+ };
11
+ export type RepairFeedback = {
12
+ ok: boolean;
13
+ round: number;
14
+ errors: WidgetError[];
15
+ /** Warnings and errors from the console, most recent last. */
16
+ console: ConsoleEntry[];
17
+ blank: boolean;
18
+ height?: number;
19
+ screenshot?: string;
20
+ /** The feedback as text for a tool result or follow-up message. */
21
+ text: string;
22
+ };
23
+ export type GenerateContext = {
24
+ round: number;
25
+ /** The code from the previous round; undefined in the first round. */
26
+ previousCode: string | undefined;
27
+ /** Feedback on the previous round; undefined in the first round. */
28
+ feedback: RepairFeedback | undefined;
29
+ };
30
+ /** Either complete new code or exact replacements on the previous round's code. */
31
+ export type GenerateResult = string | {
32
+ edits: WidgetEdit[];
33
+ };
34
+ export type RepairRound = {
35
+ round: number;
36
+ code: string;
37
+ report: RenderReport;
38
+ feedback: RepairFeedback;
39
+ };
40
+ export type RepairLoopOptions = {
41
+ generate(context: GenerateContext): Promise<GenerateResult>;
42
+ render(code: string): Promise<RenderReport>;
43
+ /** Rounds including the first generation. Defaults to 3. */
44
+ maxRounds?: number;
45
+ /** Decides whether a render is good enough. Defaults to no errors and not blank. */
46
+ accept?(report: RenderReport): boolean;
47
+ /** Console entries carried into feedback. Defaults to 20. */
48
+ consoleLimit?: number;
49
+ /** Forward the screenshot to `generate`. Defaults to true. */
50
+ includeScreenshot?: boolean;
51
+ };
52
+ export type RepairLoopResult = {
53
+ ok: boolean;
54
+ code: string;
55
+ report: RenderReport;
56
+ rounds: RepairRound[];
57
+ };
58
+ /** Turns a render report into structured, model-readable feedback. */
59
+ export declare function buildRepairFeedback(report: RenderReport, options?: {
60
+ round?: number;
61
+ ok?: boolean;
62
+ consoleLimit?: number;
63
+ includeScreenshot?: boolean;
64
+ }): RepairFeedback;
65
+ /**
66
+ * Generates a widget, renders it, and feeds structured feedback back into
67
+ * generation until a render is accepted or the round budget runs out. Returns
68
+ * the best attempt: the last accepted one, otherwise the one with the fewest
69
+ * errors.
70
+ */
71
+ export declare function repairLoop(options: RepairLoopOptions): Promise<RepairLoopResult>;
72
+ //# sourceMappingURL=repair.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.d.ts","mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAa;AAC5D,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,mBAAgB;AAElE;AACA,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,WAAW,EAAE;IACrB,OAAO,EAAE,YAAY,EAAE;IACvB,KAAK,EAAE,OAAO;IACd,MAAM,CAAC,EAAE,MAAM;IACf,UAAU,CAAC,EAAE,MAAM;CACpB;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,OAAO;IACX,KAAK,EAAE,MAAM;IACb,MAAM,EAAE,WAAW,EAAE;IACrB;IACA,OAAO,EAAE,YAAY,EAAE;IACvB,KAAK,EAAE,OAAO;IACd,MAAM,CAAC,EAAE,MAAM;IACf,UAAU,CAAC,EAAE,MAAM;IACnB;IACA,IAAI,EAAE,MAAM;CACb;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM;IACb;IACA,YAAY,EAAE,MAAM,GAAG,SAAS;IAChC;IACA,QAAQ,EAAE,cAAc,GAAG,SAAS;CACrC;AAED;AACA,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,KAAK,EAAE,UAAU,EAAE;CAAE;AAE7D,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM;IACb,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,YAAY;IACpB,QAAQ,EAAE,cAAc;CACzB;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IAC3D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAC3C;IACA,SAAS,CAAC,EAAE,MAAM;IAClB;IACA,MAAM,CAAC,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO;IACtC;IACA,YAAY,CAAC,EAAE,MAAM;IACrB;IACA,iBAAiB,CAAC,EAAE,OAAO;CAC5B;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,WAAW,EAAE;CACtB;AAeD;AACA,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,MAAM;IACd,EAAE,CAAC,EAAE,OAAO;IACZ,YAAY,CAAC,EAAE,MAAM;IACrB,iBAAiB,CAAC,EAAE,OAAO;CACvB,GACL,cAAc;AAmDjB;;;;;;AAMA,wBAAsB,UAAU,CAC9B,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,CAAC","names":[],"ignoreList":[],"sources":["../../src/repair/repair.ts"],"sourcesContent":[null]}
@@ -0,0 +1,103 @@
1
+ import { applyWidgetEdits } from "../tools/edits.js";
2
+ // src/repair/repair.ts
3
+ const defaultAccept = (report) => report.errors.length === 0 && !report.blank;
4
+ const describeError = (error) => {
5
+ const location = error.line !== void 0 ? ` (line ${error.line}${error.column !== void 0 ? `:${error.column}` : ""})` : "";
6
+ const source = error.source && error.kind !== "error" ? ` [${error.source}]` : "";
7
+ return `- ${error.kind}: ${error.message}${location}${source}`;
8
+ };
9
+ /** Turns a render report into structured, model-readable feedback. */
10
+ function buildRepairFeedback(report, options = {}) {
11
+ const ok = options.ok ?? defaultAccept(report);
12
+ const consoleExcerpt = report.console.filter((entry) => entry.level === "warn" || entry.level === "error").slice(-(options.consoleLimit ?? 20));
13
+ const lines = [];
14
+ if (ok) lines.push("The widget rendered without errors.");
15
+ else lines.push("The widget did not render cleanly. Fix the causes below.");
16
+ if (report.errors.length > 0) lines.push("", `Errors (${report.errors.length}):`, ...report.errors.map(describeError));
17
+ if (report.blank) lines.push("", "The widget rendered blank: nothing visible ended up on screen. Check that content is not hidden, that script-drawn areas have an explicit height, and that scripts ran without errors.");
18
+ if (consoleExcerpt.length > 0) lines.push("", "Console:", ...consoleExcerpt.map((entry) => `- ${entry.level}: ${entry.message}`));
19
+ if (report.height !== void 0) lines.push("", `Rendered height: ${report.height}px.`);
20
+ const includeScreenshot = options.includeScreenshot ?? true;
21
+ return {
22
+ ok,
23
+ round: options.round ?? 1,
24
+ errors: report.errors,
25
+ console: consoleExcerpt,
26
+ blank: report.blank,
27
+ ...report.height !== void 0 ? { height: report.height } : {},
28
+ ...includeScreenshot && report.screenshot ? { screenshot: report.screenshot } : {},
29
+ text: lines.join("\n")
30
+ };
31
+ }
32
+ /**
33
+ * Generates a widget, renders it, and feeds structured feedback back into
34
+ * generation until a render is accepted or the round budget runs out. Returns
35
+ * the best attempt: the last accepted one, otherwise the one with the fewest
36
+ * errors.
37
+ */
38
+ async function repairLoop(options) {
39
+ const maxRounds = Math.max(1, options.maxRounds ?? 3);
40
+ const accept = options.accept ?? defaultAccept;
41
+ const rounds = [];
42
+ let previousCode;
43
+ let feedback;
44
+ for (let round = 1; round <= maxRounds; round++) {
45
+ const generated = await options.generate({
46
+ round,
47
+ previousCode,
48
+ feedback
49
+ });
50
+ let code;
51
+ let report;
52
+ if (typeof generated === "string") {
53
+ code = generated;
54
+ report = await options.render(code);
55
+ } else {
56
+ const applied = applyWidgetEdits(previousCode ?? "", generated.edits);
57
+ if (applied.ok) {
58
+ code = applied.code;
59
+ report = await options.render(code);
60
+ } else {
61
+ code = previousCode ?? "";
62
+ report = {
63
+ errors: [{
64
+ kind: "error",
65
+ message: `Edits were not applied: ${applied.error}`
66
+ }],
67
+ console: [],
68
+ blank: false
69
+ };
70
+ }
71
+ }
72
+ const ok = accept(report);
73
+ feedback = buildRepairFeedback(report, {
74
+ round,
75
+ ok,
76
+ ...options.consoleLimit !== void 0 ? { consoleLimit: options.consoleLimit } : {},
77
+ ...options.includeScreenshot !== void 0 ? { includeScreenshot: options.includeScreenshot } : {}
78
+ });
79
+ rounds.push({
80
+ round,
81
+ code,
82
+ report,
83
+ feedback
84
+ });
85
+ previousCode = code;
86
+ if (ok) return {
87
+ ok: true,
88
+ code,
89
+ report,
90
+ rounds
91
+ };
92
+ }
93
+ const best = rounds.reduce((a, b) => b.report.errors.length + (b.report.blank ? 1 : 0) < a.report.errors.length + (a.report.blank ? 1 : 0) ? b : a);
94
+ return {
95
+ ok: false,
96
+ code: best.code,
97
+ report: best.report,
98
+ rounds
99
+ };
100
+ }
101
+ export { buildRepairFeedback, repairLoop };
102
+
103
+ //# sourceMappingURL=repair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.js","names":[],"sources":["../../src/repair/repair.ts"],"sourcesContent":["import type { ConsoleEntry, WidgetError } from \"../protocol\";\nimport { applyWidgetEdits, type WidgetEdit } from \"../tools/edits\";\n\n/** What one render of a widget produced; `previewWidget` returns a compatible shape. */\nexport type RenderReport = {\n errors: WidgetError[];\n console: ConsoleEntry[];\n blank: boolean;\n height?: number;\n screenshot?: string;\n};\n\nexport type RepairFeedback = {\n ok: boolean;\n round: number;\n errors: WidgetError[];\n /** Warnings and errors from the console, most recent last. */\n console: ConsoleEntry[];\n blank: boolean;\n height?: number;\n screenshot?: string;\n /** The feedback as text for a tool result or follow-up message. */\n text: string;\n};\n\nexport type GenerateContext = {\n round: number;\n /** The code from the previous round; undefined in the first round. */\n previousCode: string | undefined;\n /** Feedback on the previous round; undefined in the first round. */\n feedback: RepairFeedback | undefined;\n};\n\n/** Either complete new code or exact replacements on the previous round's code. */\nexport type GenerateResult = string | { edits: WidgetEdit[] };\n\nexport type RepairRound = {\n round: number;\n code: string;\n report: RenderReport;\n feedback: RepairFeedback;\n};\n\nexport type RepairLoopOptions = {\n generate(context: GenerateContext): Promise<GenerateResult>;\n render(code: string): Promise<RenderReport>;\n /** Rounds including the first generation. Defaults to 3. */\n maxRounds?: number;\n /** Decides whether a render is good enough. Defaults to no errors and not blank. */\n accept?(report: RenderReport): boolean;\n /** Console entries carried into feedback. Defaults to 20. */\n consoleLimit?: number;\n /** Forward the screenshot to `generate`. Defaults to true. */\n includeScreenshot?: boolean;\n};\n\nexport type RepairLoopResult = {\n ok: boolean;\n code: string;\n report: RenderReport;\n rounds: RepairRound[];\n};\n\nconst defaultAccept = (report: RenderReport) =>\n report.errors.length === 0 && !report.blank;\n\nconst describeError = (error: WidgetError) => {\n const location =\n error.line !== undefined\n ? ` (line ${error.line}${error.column !== undefined ? `:${error.column}` : \"\"})`\n : \"\";\n const source =\n error.source && error.kind !== \"error\" ? ` [${error.source}]` : \"\";\n return `- ${error.kind}: ${error.message}${location}${source}`;\n};\n\n/** Turns a render report into structured, model-readable feedback. */\nexport function buildRepairFeedback(\n report: RenderReport,\n options: {\n round?: number;\n ok?: boolean;\n consoleLimit?: number;\n includeScreenshot?: boolean;\n } = {},\n): RepairFeedback {\n const ok = options.ok ?? defaultAccept(report);\n const consoleExcerpt = report.console\n .filter((entry) => entry.level === \"warn\" || entry.level === \"error\")\n .slice(-(options.consoleLimit ?? 20));\n\n const lines: string[] = [];\n if (ok) {\n lines.push(\"The widget rendered without errors.\");\n } else {\n lines.push(\"The widget did not render cleanly. Fix the causes below.\");\n }\n if (report.errors.length > 0) {\n lines.push(\n \"\",\n `Errors (${report.errors.length}):`,\n ...report.errors.map(describeError),\n );\n }\n if (report.blank) {\n lines.push(\n \"\",\n \"The widget rendered blank: nothing visible ended up on screen. Check that content is not hidden, that script-drawn areas have an explicit height, and that scripts ran without errors.\",\n );\n }\n if (consoleExcerpt.length > 0) {\n lines.push(\n \"\",\n \"Console:\",\n ...consoleExcerpt.map((entry) => `- ${entry.level}: ${entry.message}`),\n );\n }\n if (report.height !== undefined) {\n lines.push(\"\", `Rendered height: ${report.height}px.`);\n }\n\n const includeScreenshot = options.includeScreenshot ?? true;\n return {\n ok,\n round: options.round ?? 1,\n errors: report.errors,\n console: consoleExcerpt,\n blank: report.blank,\n ...(report.height !== undefined ? { height: report.height } : {}),\n ...(includeScreenshot && report.screenshot\n ? { screenshot: report.screenshot }\n : {}),\n text: lines.join(\"\\n\"),\n };\n}\n\n/**\n * Generates a widget, renders it, and feeds structured feedback back into\n * generation until a render is accepted or the round budget runs out. Returns\n * the best attempt: the last accepted one, otherwise the one with the fewest\n * errors.\n */\nexport async function repairLoop(\n options: RepairLoopOptions,\n): Promise<RepairLoopResult> {\n const maxRounds = Math.max(1, options.maxRounds ?? 3);\n const accept = options.accept ?? defaultAccept;\n const rounds: RepairRound[] = [];\n let previousCode: string | undefined;\n let feedback: RepairFeedback | undefined;\n\n for (let round = 1; round <= maxRounds; round++) {\n const generated = await options.generate({ round, previousCode, feedback });\n let code: string;\n let report: RenderReport;\n if (typeof generated === \"string\") {\n code = generated;\n report = await options.render(code);\n } else {\n const applied = applyWidgetEdits(previousCode ?? \"\", generated.edits);\n if (applied.ok) {\n code = applied.code;\n report = await options.render(code);\n } else {\n code = previousCode ?? \"\";\n report = {\n errors: [\n {\n kind: \"error\",\n message: `Edits were not applied: ${applied.error}`,\n },\n ],\n console: [],\n blank: false,\n };\n }\n }\n\n const ok = accept(report);\n feedback = buildRepairFeedback(report, {\n round,\n ok,\n ...(options.consoleLimit !== undefined\n ? { consoleLimit: options.consoleLimit }\n : {}),\n ...(options.includeScreenshot !== undefined\n ? { includeScreenshot: options.includeScreenshot }\n : {}),\n });\n rounds.push({ round, code, report, feedback });\n previousCode = code;\n if (ok) return { ok: true, code, report, rounds };\n }\n\n const best = rounds.reduce((a, b) =>\n b.report.errors.length + (b.report.blank ? 1 : 0) <\n a.report.errors.length + (a.report.blank ? 1 : 0)\n ? b\n : a,\n );\n return { ok: false, code: best.code, report: best.report, rounds };\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { repairLoop, buildRepairFeedback, type GenerateContext, type GenerateResult, type RenderReport, type RepairFeedback, type RepairLoopOptions, type RepairLoopResult, type RepairRound, } from "./repair/repair.js";
2
+ //# sourceMappingURL=repair.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.d.ts","mappings":"AAAA,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,oBAAiB","names":[],"ignoreList":[],"sources":["../src/repair.ts"],"sourcesContent":[null]}
package/dist/repair.js ADDED
@@ -0,0 +1 @@
1
+ export { buildRepairFeedback, repairLoop } from "./repair/repair.js";
package/dist/rpc.d.ts ADDED
@@ -0,0 +1,60 @@
1
+ export type JsonRpcId = string | number;
2
+ export type JsonRpcRequest = {
3
+ jsonrpc: "2.0";
4
+ id: JsonRpcId;
5
+ method: string;
6
+ params?: unknown;
7
+ };
8
+ export type JsonRpcNotification = {
9
+ jsonrpc: "2.0";
10
+ method: string;
11
+ params?: unknown;
12
+ };
13
+ export type JsonRpcResponse = {
14
+ jsonrpc: "2.0";
15
+ id: JsonRpcId;
16
+ result?: unknown;
17
+ error?: {
18
+ code: number;
19
+ message: string;
20
+ data?: unknown;
21
+ };
22
+ };
23
+ export type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcResponse;
24
+ export declare const RPC_ERROR: {
25
+ readonly parseError: -32700;
26
+ readonly invalidRequest: -32600;
27
+ readonly methodNotFound: -32601;
28
+ readonly invalidParams: -32602;
29
+ readonly internalError: -32603;
30
+ };
31
+ export declare class RpcError extends Error {
32
+ readonly code: number;
33
+ readonly data: unknown;
34
+ constructor(code: number, message: string, data?: unknown);
35
+ }
36
+ /** The subset of `MessagePort` the peer uses. */
37
+ export type RpcEndpoint = {
38
+ postMessage(message: unknown): void;
39
+ addEventListener(type: "message", listener: (event: MessageEvent) => void): void;
40
+ removeEventListener(type: "message", listener: (event: MessageEvent) => void): void;
41
+ start?(): void;
42
+ };
43
+ export type RpcHandlers = {
44
+ /** Resolves to the result; throw an `RpcError` to answer with a specific code. */
45
+ onRequest?: (method: string, params: unknown) => unknown;
46
+ onNotification?: (method: string, params: unknown) => void;
47
+ };
48
+ export type RpcPeer = {
49
+ request(method: string, params?: unknown, options?: {
50
+ timeoutMs?: number;
51
+ }): Promise<unknown>;
52
+ notify(method: string, params?: unknown): void;
53
+ /** Feeds a message received outside the endpoint, such as a window message. */
54
+ receive(message: unknown, reply?: (message: JsonRpcMessage) => void): void;
55
+ dispose(): void;
56
+ };
57
+ export declare function isJsonRpcMessage(value: unknown): value is JsonRpcMessage;
58
+ /** A symmetric JSON-RPC 2.0 peer: both sides can send requests and notifications. */
59
+ export declare function createRpcPeer(endpoint: RpcEndpoint, handlers?: RpcHandlers): RpcPeer;
60
+ //# sourceMappingURL=rpc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc.d.ts","mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM;AAEvC,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,KAAK;IACd,EAAE,EAAE,SAAS;IACb,MAAM,EAAE,MAAM;IACd,MAAM,CAAC,EAAE,OAAO;CACjB;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,MAAM;IACd,MAAM,CAAC,EAAE,OAAO;CACjB;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,KAAK;IACd,EAAE,EAAE,SAAS;IACb,MAAM,CAAC,EAAE,OAAO;IAChB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM;QAAE,OAAO,EAAE,MAAM;QAAE,IAAI,CAAC,EAAE,OAAO;KAAE;CAC1D;AAED,MAAM,MAAM,cAAc,GACtB,cAAc,GACd,mBAAmB,GACnB,eAAe;AAEnB,eAAO,MAAM,SAAS;;;;;;CAMZ;AAEV,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,OAAO;gBAEV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;;AAQ3D;AACA,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IACnC,gBAAgB,CACd,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GACtC,IAAI;IACP,mBAAmB,CACjB,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GACtC,IAAI;IACP,KAAK,CAAC,IAAI,IAAI;CACf;AAED,MAAM,MAAM,WAAW,GAAG;IACxB;IACA,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO;IACxD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI;CAC3D;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,OAAO,CACL,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM;KAAE,GAC/B,OAAO,CAAC,OAAO,CAAC;IACnB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI;IAC9C;IACA,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,IAAI;IAC1E,OAAO,IAAI,IAAI;CAChB;AAKD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc;AAkBzE;AACA,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,WAAW,EACrB,QAAQ,GAAE,WAAgB,GACzB,OAAO","names":[],"ignoreList":[],"sources":["../src/rpc.ts"],"sourcesContent":[null]}
package/dist/rpc.js ADDED
@@ -0,0 +1,127 @@
1
+ // src/rpc.ts
2
+ const RPC_ERROR = {
3
+ parseError: -32700,
4
+ invalidRequest: -32600,
5
+ methodNotFound: -32601,
6
+ invalidParams: -32602,
7
+ internalError: -32603
8
+ };
9
+ var RpcError = class extends Error {
10
+ code;
11
+ data;
12
+ constructor(code, message, data) {
13
+ super(message);
14
+ this.name = "RpcError";
15
+ this.code = code;
16
+ this.data = data;
17
+ }
18
+ };
19
+ const isRecord = (value) => typeof value === "object" && value !== null;
20
+ function isJsonRpcMessage(value) {
21
+ return isRecord(value) && value["jsonrpc"] === "2.0";
22
+ }
23
+ const errorPayload = (error) => {
24
+ if (error instanceof RpcError) return {
25
+ code: error.code,
26
+ message: error.message,
27
+ ...error.data !== void 0 ? { data: error.data } : {}
28
+ };
29
+ return {
30
+ code: RPC_ERROR.internalError,
31
+ message: error instanceof Error ? error.message : String(error)
32
+ };
33
+ };
34
+ /** A symmetric JSON-RPC 2.0 peer: both sides can send requests and notifications. */
35
+ function createRpcPeer(endpoint, handlers = {}) {
36
+ let nextId = 1;
37
+ let disposed = false;
38
+ const pending = /* @__PURE__ */ new Map();
39
+ const post = (message) => {
40
+ if (disposed) return;
41
+ endpoint.postMessage(message);
42
+ };
43
+ const handleRequest = async (request, reply) => {
44
+ try {
45
+ if (!handlers.onRequest) throw new RpcError(RPC_ERROR.methodNotFound, `Unknown method: ${request.method}`);
46
+ const result = await handlers.onRequest(request.method, request.params);
47
+ reply({
48
+ jsonrpc: "2.0",
49
+ id: request.id,
50
+ result: result ?? null
51
+ });
52
+ } catch (error) {
53
+ reply({
54
+ jsonrpc: "2.0",
55
+ id: request.id,
56
+ error: errorPayload(error)
57
+ });
58
+ }
59
+ };
60
+ const receive = (message, reply = post) => {
61
+ if (disposed || !isJsonRpcMessage(message)) return;
62
+ const hasId = "id" in message && (typeof message.id === "string" || typeof message.id === "number");
63
+ if ("method" in message && typeof message.method === "string") {
64
+ if (hasId) handleRequest(message, reply);
65
+ else try {
66
+ handlers.onNotification?.(message.method, message.params);
67
+ } catch {}
68
+ return;
69
+ }
70
+ if (!hasId) return;
71
+ const response = message;
72
+ const entry = pending.get(response.id);
73
+ if (!entry) return;
74
+ pending.delete(response.id);
75
+ if (entry.timer !== void 0) clearTimeout(entry.timer);
76
+ if (response.error) entry.reject(new RpcError(response.error.code, response.error.message, response.error.data));
77
+ else entry.resolve(response.result);
78
+ };
79
+ const onMessage = (event) => receive(event.data);
80
+ endpoint.addEventListener("message", onMessage);
81
+ endpoint.start?.();
82
+ return {
83
+ request(method, params, options) {
84
+ if (disposed) return Promise.reject(/* @__PURE__ */ new Error("RPC peer is disposed"));
85
+ const id = nextId++;
86
+ return new Promise((resolve, reject) => {
87
+ const timeoutMs = options?.timeoutMs;
88
+ const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => {
89
+ pending.delete(id);
90
+ reject(/* @__PURE__ */ new Error(`${method} timed out after ${timeoutMs}ms`));
91
+ }, timeoutMs);
92
+ pending.set(id, {
93
+ resolve,
94
+ reject,
95
+ timer
96
+ });
97
+ post({
98
+ jsonrpc: "2.0",
99
+ id,
100
+ method,
101
+ ...params !== void 0 ? { params } : {}
102
+ });
103
+ });
104
+ },
105
+ notify(method, params) {
106
+ post({
107
+ jsonrpc: "2.0",
108
+ method,
109
+ ...params !== void 0 ? { params } : {}
110
+ });
111
+ },
112
+ receive,
113
+ dispose() {
114
+ if (disposed) return;
115
+ disposed = true;
116
+ endpoint.removeEventListener("message", onMessage);
117
+ for (const entry of pending.values()) {
118
+ if (entry.timer !== void 0) clearTimeout(entry.timer);
119
+ entry.reject(/* @__PURE__ */ new Error("RPC peer is disposed"));
120
+ }
121
+ pending.clear();
122
+ }
123
+ };
124
+ }
125
+ export { RPC_ERROR, RpcError, createRpcPeer, isJsonRpcMessage };
126
+
127
+ //# sourceMappingURL=rpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc.js","names":[],"sources":["../src/rpc.ts"],"sourcesContent":["export type JsonRpcId = string | number;\n\nexport type JsonRpcRequest = {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n method: string;\n params?: unknown;\n};\n\nexport type JsonRpcNotification = {\n jsonrpc: \"2.0\";\n method: string;\n params?: unknown;\n};\n\nexport type JsonRpcResponse = {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n result?: unknown;\n error?: { code: number; message: string; data?: unknown };\n};\n\nexport type JsonRpcMessage =\n | JsonRpcRequest\n | JsonRpcNotification\n | JsonRpcResponse;\n\nexport const RPC_ERROR = {\n parseError: -32700,\n invalidRequest: -32600,\n methodNotFound: -32601,\n invalidParams: -32602,\n internalError: -32603,\n} as const;\n\nexport class RpcError extends Error {\n readonly code: number;\n readonly data: unknown;\n\n constructor(code: number, message: string, data?: unknown) {\n super(message);\n this.name = \"RpcError\";\n this.code = code;\n this.data = data;\n }\n}\n\n/** The subset of `MessagePort` the peer uses. */\nexport type RpcEndpoint = {\n postMessage(message: unknown): void;\n addEventListener(\n type: \"message\",\n listener: (event: MessageEvent) => void,\n ): void;\n removeEventListener(\n type: \"message\",\n listener: (event: MessageEvent) => void,\n ): void;\n start?(): void;\n};\n\nexport type RpcHandlers = {\n /** Resolves to the result; throw an `RpcError` to answer with a specific code. */\n onRequest?: (method: string, params: unknown) => unknown;\n onNotification?: (method: string, params: unknown) => void;\n};\n\nexport type RpcPeer = {\n request(\n method: string,\n params?: unknown,\n options?: { timeoutMs?: number },\n ): Promise<unknown>;\n notify(method: string, params?: unknown): void;\n /** Feeds a message received outside the endpoint, such as a window message. */\n receive(message: unknown, reply?: (message: JsonRpcMessage) => void): void;\n dispose(): void;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nexport function isJsonRpcMessage(value: unknown): value is JsonRpcMessage {\n return isRecord(value) && value[\"jsonrpc\"] === \"2.0\";\n}\n\nconst errorPayload = (error: unknown) => {\n if (error instanceof RpcError) {\n return {\n code: error.code,\n message: error.message,\n ...(error.data !== undefined ? { data: error.data } : {}),\n };\n }\n return {\n code: RPC_ERROR.internalError,\n message: error instanceof Error ? error.message : String(error),\n };\n};\n\n/** A symmetric JSON-RPC 2.0 peer: both sides can send requests and notifications. */\nexport function createRpcPeer(\n endpoint: RpcEndpoint,\n handlers: RpcHandlers = {},\n): RpcPeer {\n let nextId = 1;\n let disposed = false;\n const pending = new Map<\n JsonRpcId,\n {\n resolve: (value: unknown) => void;\n reject: (error: unknown) => void;\n timer: ReturnType<typeof setTimeout> | undefined;\n }\n >();\n\n const post = (message: JsonRpcMessage) => {\n if (disposed) return;\n endpoint.postMessage(message);\n };\n\n const handleRequest = async (\n request: JsonRpcRequest,\n reply: (message: JsonRpcMessage) => void,\n ) => {\n try {\n if (!handlers.onRequest) {\n throw new RpcError(\n RPC_ERROR.methodNotFound,\n `Unknown method: ${request.method}`,\n );\n }\n const result = await handlers.onRequest(request.method, request.params);\n reply({ jsonrpc: \"2.0\", id: request.id, result: result ?? null });\n } catch (error) {\n reply({ jsonrpc: \"2.0\", id: request.id, error: errorPayload(error) });\n }\n };\n\n const receive = (\n message: unknown,\n reply: (message: JsonRpcMessage) => void = post,\n ) => {\n if (disposed || !isJsonRpcMessage(message)) return;\n const hasId =\n \"id\" in message &&\n (typeof message.id === \"string\" || typeof message.id === \"number\");\n\n if (\"method\" in message && typeof message.method === \"string\") {\n if (hasId) {\n void handleRequest(message as JsonRpcRequest, reply);\n } else {\n try {\n handlers.onNotification?.(message.method, message.params);\n } catch {\n // A failing notification handler has nobody to answer to.\n }\n }\n return;\n }\n\n if (!hasId) return;\n const response = message as JsonRpcResponse;\n const entry = pending.get(response.id);\n if (!entry) return;\n pending.delete(response.id);\n if (entry.timer !== undefined) clearTimeout(entry.timer);\n if (response.error) {\n entry.reject(\n new RpcError(\n response.error.code,\n response.error.message,\n response.error.data,\n ),\n );\n } else {\n entry.resolve(response.result);\n }\n };\n\n const onMessage = (event: MessageEvent) => receive(event.data);\n endpoint.addEventListener(\"message\", onMessage);\n endpoint.start?.();\n\n return {\n request(method, params, options) {\n if (disposed) {\n return Promise.reject(new Error(\"RPC peer is disposed\"));\n }\n const id = nextId++;\n return new Promise((resolve, reject) => {\n const timeoutMs = options?.timeoutMs;\n const timer =\n timeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n pending.delete(id);\n reject(new Error(`${method} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n pending.set(id, { resolve, reject, timer });\n post({\n jsonrpc: \"2.0\",\n id,\n method,\n ...(params !== undefined ? { params } : {}),\n });\n });\n },\n notify(method, params) {\n post({\n jsonrpc: \"2.0\",\n method,\n ...(params !== undefined ? { params } : {}),\n });\n },\n receive,\n dispose() {\n if (disposed) return;\n disposed = true;\n endpoint.removeEventListener(\"message\", onMessage);\n for (const entry of pending.values()) {\n if (entry.timer !== undefined) clearTimeout(entry.timer);\n entry.reject(new Error(\"RPC peer is disposed\"));\n }\n pending.clear();\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { ConsoleEntry, WidgetError } from "../protocol.js";
2
+ export declare const formatValue: (value: unknown) => string;
3
+ export type Diagnostics = {
4
+ readonly errors: WidgetError[];
5
+ readonly console: ConsoleEntry[];
6
+ record(error: WidgetError): void;
7
+ dispose(): void;
8
+ };
9
+ export type DiagnosticsOptions = {
10
+ onError?: (error: WidgetError) => void;
11
+ onConsole?: (entry: ConsoleEntry) => void;
12
+ };
13
+ /** Captures errors, console output, failed resources, and CSP violations in a window. */
14
+ export declare function installDiagnostics(win: Window & typeof globalThis, options?: DiagnosticsOptions): Diagnostics;
15
+ /** True when the widget rendered nothing a person could see. */
16
+ export declare function isBlankRender(root: HTMLElement): boolean;
17
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAgB,WAAW,EAAE,MAAM,gBAAa;AAiB1E,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,MAe5C;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE;IAC9B,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE;IAChC,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAChC,OAAO,IAAI,IAAI;CAChB;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI;IACtC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI;CAC1C;AAED;AACA,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,GAAG,OAAO,UAAU,EAC/B,OAAO,GAAE,kBAAuB,GAC/B,WAAW;AA4Fd;AACA,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO","names":[],"ignoreList":[],"sources":["../../src/runtime/diagnostics.ts"],"sourcesContent":[null]}
@@ -0,0 +1,128 @@
1
+ // src/runtime/diagnostics.ts
2
+ const MAX_CONSOLE = 200;
3
+ const MAX_ERRORS = 50;
4
+ const MAX_MESSAGE = 1e3;
5
+ const LEVELS = [
6
+ "log",
7
+ "info",
8
+ "warn",
9
+ "error",
10
+ "debug"
11
+ ];
12
+ const truncate = (text) => text.length > MAX_MESSAGE ? `${text.slice(0, MAX_MESSAGE)}…` : text;
13
+ const formatValue = (value) => {
14
+ if (typeof value === "string") return value;
15
+ if (value instanceof Error) return value.stack ?? `${value.name}: ${value.message}`;
16
+ if (typeof value === "object" && value !== null) {
17
+ if (typeof Node !== "undefined" && value instanceof Node) return `<${value.nodeName.toLowerCase()}>`;
18
+ try {
19
+ return JSON.stringify(value);
20
+ } catch {
21
+ return String(value);
22
+ }
23
+ }
24
+ return String(value);
25
+ };
26
+ /** Captures errors, console output, failed resources, and CSP violations in a window. */
27
+ function installDiagnostics(win, options = {}) {
28
+ const errors = [];
29
+ const consoleEntries = [];
30
+ const record = (error) => {
31
+ const normalized = {
32
+ ...error,
33
+ message: truncate(error.message)
34
+ };
35
+ if (errors.length < MAX_ERRORS) errors.push(normalized);
36
+ options.onError?.(normalized);
37
+ };
38
+ const onError = (event) => {
39
+ const target = event.target;
40
+ if (target && target !== win && target instanceof win.Element) {
41
+ const src = target.getAttribute("src") ?? target.getAttribute("href") ?? void 0;
42
+ record({
43
+ kind: "resource",
44
+ message: `Failed to load <${target.localName}>${src ? ` ${src}` : ""}`,
45
+ ...src ? { source: src } : {}
46
+ });
47
+ return;
48
+ }
49
+ const e = event;
50
+ const stack = e.error instanceof Error ? e.error.stack : void 0;
51
+ record({
52
+ kind: "error",
53
+ message: e.message || formatValue(e.error),
54
+ ...e.filename ? { source: e.filename } : {},
55
+ ...e.lineno ? { line: e.lineno } : {},
56
+ ...e.colno ? { column: e.colno } : {},
57
+ ...stack ? { stack: truncate(stack) } : {}
58
+ });
59
+ };
60
+ const onRejection = (event) => {
61
+ const reason = event.reason;
62
+ const stack = reason instanceof Error ? reason.stack : void 0;
63
+ record({
64
+ kind: "unhandledrejection",
65
+ message: reason instanceof Error ? reason.message : formatValue(reason),
66
+ ...stack ? { stack: truncate(stack) } : {}
67
+ });
68
+ };
69
+ const onViolation = (event) => {
70
+ record({
71
+ kind: "csp",
72
+ message: `Blocked by Content Security Policy (${event.effectiveDirective}): ${event.blockedURI || "inline"}`,
73
+ ...event.blockedURI ? { source: event.blockedURI } : {},
74
+ ...event.lineNumber ? { line: event.lineNumber } : {}
75
+ });
76
+ };
77
+ win.addEventListener("error", onError, true);
78
+ win.addEventListener("unhandledrejection", onRejection);
79
+ win.document.addEventListener("securitypolicyviolation", onViolation);
80
+ const originals = /* @__PURE__ */ new Map();
81
+ for (const level of LEVELS) {
82
+ const original = win.console[level];
83
+ originals.set(level, original);
84
+ win.console[level] = (...args) => {
85
+ const entry = {
86
+ level,
87
+ message: truncate(args.map(formatValue).join(" "))
88
+ };
89
+ consoleEntries.push(entry);
90
+ if (consoleEntries.length > MAX_CONSOLE) consoleEntries.shift();
91
+ options.onConsole?.(entry);
92
+ original.apply(win.console, args);
93
+ };
94
+ }
95
+ return {
96
+ errors,
97
+ console: consoleEntries,
98
+ record,
99
+ dispose() {
100
+ win.removeEventListener("error", onError, true);
101
+ win.removeEventListener("unhandledrejection", onRejection);
102
+ win.document.removeEventListener("securitypolicyviolation", onViolation);
103
+ for (const [level, original] of originals) win.console[level] = original;
104
+ }
105
+ };
106
+ }
107
+ const VISUAL_SELECTOR = "svg, canvas, img, video, picture, iframe, input, button, select, textarea, progress, meter";
108
+ /** True when the widget rendered nothing a person could see. */
109
+ function isBlankRender(root) {
110
+ const rect = root.getBoundingClientRect();
111
+ if (rect.height < 2 || rect.width < 2) return true;
112
+ if ((root.textContent ?? "").trim().length > 0) return false;
113
+ for (const el of Array.from(root.querySelectorAll(VISUAL_SELECTOR))) {
114
+ const r = el.getBoundingClientRect();
115
+ if (r.width >= 2 && r.height >= 2) return false;
116
+ }
117
+ for (const el of Array.from(root.querySelectorAll("*"))) {
118
+ const style = root.ownerDocument.defaultView?.getComputedStyle(el);
119
+ if (!style) continue;
120
+ if (!(style.backgroundColor !== "rgba(0, 0, 0, 0)" && style.backgroundColor !== "transparent" || style.backgroundImage !== "none" || parseFloat(style.borderTopWidth) > 0)) continue;
121
+ const r = el.getBoundingClientRect();
122
+ if (r.width >= 2 && r.height >= 2) return false;
123
+ }
124
+ return true;
125
+ }
126
+ export { formatValue, installDiagnostics, isBlankRender };
127
+
128
+ //# sourceMappingURL=diagnostics.js.map