@assistant-ui/react-generative-ui 0.0.24 → 0.1.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 (338) hide show
  1. package/README.md +2 -0
  2. package/dist/a2ui.d.ts +1 -4
  3. package/dist/a2ui.d.ts.map +1 -1
  4. package/dist/a2ui.js +1 -4
  5. package/dist/index.d.ts +1 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -10
  8. package/dist/index.server.d.ts +1 -2
  9. package/dist/index.server.d.ts.map +1 -1
  10. package/dist/index.server.js +1 -10
  11. package/dist/ir.d.ts +1 -111
  12. package/dist/ir.d.ts.map +1 -1
  13. package/dist/ir.js +1 -175
  14. package/dist/slack.d.ts +1 -4
  15. package/dist/slack.d.ts.map +1 -1
  16. package/dist/slack.js +1 -4
  17. package/dist/teams.d.ts +1 -4
  18. package/dist/teams.d.ts.map +1 -1
  19. package/dist/teams.js +1 -4
  20. package/package.json +4 -5
  21. package/src/a2ui.ts +1 -22
  22. package/src/index.server.ts +1 -2
  23. package/src/index.test.ts +75 -0
  24. package/src/index.ts +1 -2
  25. package/src/ir.ts +1 -279
  26. package/src/slack.ts +1 -39
  27. package/src/teams.ts +1 -35
  28. package/dist/JSONGenerativeUI.client.d.ts +0 -28
  29. package/dist/JSONGenerativeUI.client.d.ts.map +0 -1
  30. package/dist/JSONGenerativeUI.client.js +0 -101
  31. package/dist/JSONGenerativeUI.client.js.map +0 -1
  32. package/dist/JSONGenerativeUI.server.d.ts +0 -21
  33. package/dist/JSONGenerativeUI.server.d.ts.map +0 -1
  34. package/dist/JSONGenerativeUI.server.js +0 -32
  35. package/dist/JSONGenerativeUI.server.js.map +0 -1
  36. package/dist/JSONGenerativeUI.shared.d.ts +0 -63
  37. package/dist/JSONGenerativeUI.shared.d.ts.map +0 -1
  38. package/dist/JSONGenerativeUI.shared.js +0 -31
  39. package/dist/JSONGenerativeUI.shared.js.map +0 -1
  40. package/dist/RadioGroupScope.d.ts +0 -6
  41. package/dist/RadioGroupScope.d.ts.map +0 -1
  42. package/dist/RadioGroupScope.js +0 -25
  43. package/dist/RadioGroupScope.js.map +0 -1
  44. package/dist/a2ui/BindingContext.d.ts +0 -11
  45. package/dist/a2ui/BindingContext.d.ts.map +0 -1
  46. package/dist/a2ui/BindingContext.js +0 -12
  47. package/dist/a2ui/BindingContext.js.map +0 -1
  48. package/dist/a2ui/PresentRenderer.d.ts +0 -11
  49. package/dist/a2ui/PresentRenderer.d.ts.map +0 -1
  50. package/dist/a2ui/PresentRenderer.js +0 -39
  51. package/dist/a2ui/PresentRenderer.js.map +0 -1
  52. package/dist/a2ui/convert.d.ts +0 -15
  53. package/dist/a2ui/convert.d.ts.map +0 -1
  54. package/dist/a2ui/convert.js +0 -728
  55. package/dist/a2ui/convert.js.map +0 -1
  56. package/dist/a2ui/dataModel.d.ts +0 -9
  57. package/dist/a2ui/dataModel.d.ts.map +0 -1
  58. package/dist/a2ui/dataModel.js +0 -44
  59. package/dist/a2ui/dataModel.js.map +0 -1
  60. package/dist/a2ui/reducer.d.ts +0 -8
  61. package/dist/a2ui/reducer.d.ts.map +0 -1
  62. package/dist/a2ui/reducer.js +0 -242
  63. package/dist/a2ui/reducer.js.map +0 -1
  64. package/dist/a2ui/snapshot.d.ts +0 -3
  65. package/dist/a2ui/snapshot.d.ts.map +0 -1
  66. package/dist/a2ui/snapshot.js +0 -33
  67. package/dist/a2ui/snapshot.js.map +0 -1
  68. package/dist/a2ui/types.d.ts +0 -88
  69. package/dist/a2ui/types.d.ts.map +0 -1
  70. package/dist/a2ui/types.js +0 -6
  71. package/dist/a2ui/types.js.map +0 -1
  72. package/dist/a2ui/valueFunctions.d.ts +0 -16
  73. package/dist/a2ui/valueFunctions.d.ts.map +0 -1
  74. package/dist/a2ui/valueFunctions.js +0 -332
  75. package/dist/a2ui/valueFunctions.js.map +0 -1
  76. package/dist/actionRegistry.d.ts +0 -29
  77. package/dist/actionRegistry.d.ts.map +0 -1
  78. package/dist/actionRegistry.js +0 -41
  79. package/dist/actionRegistry.js.map +0 -1
  80. package/dist/answeredValues.d.ts +0 -16
  81. package/dist/answeredValues.d.ts.map +0 -1
  82. package/dist/answeredValues.js +0 -37
  83. package/dist/answeredValues.js.map +0 -1
  84. package/dist/buildPresentParameters.d.ts +0 -21
  85. package/dist/buildPresentParameters.d.ts.map +0 -1
  86. package/dist/buildPresentParameters.js +0 -95
  87. package/dist/buildPresentParameters.js.map +0 -1
  88. package/dist/constants.d.ts +0 -15
  89. package/dist/constants.d.ts.map +0 -1
  90. package/dist/constants.js +0 -19
  91. package/dist/constants.js.map +0 -1
  92. package/dist/convert/boundSpec.d.ts +0 -48
  93. package/dist/convert/boundSpec.d.ts.map +0 -1
  94. package/dist/convert/boundSpec.js +0 -108
  95. package/dist/convert/boundSpec.js.map +0 -1
  96. package/dist/convert/copyBounded.d.ts +0 -25
  97. package/dist/convert/copyBounded.d.ts.map +0 -1
  98. package/dist/convert/copyBounded.js +0 -38
  99. package/dist/convert/copyBounded.js.map +0 -1
  100. package/dist/convert/isElement.d.ts +0 -3
  101. package/dist/convert/isElement.d.ts.map +0 -1
  102. package/dist/convert/isElement.js +0 -6
  103. package/dist/convert/isElement.js.map +0 -1
  104. package/dist/convert/takeRun.d.ts +0 -6
  105. package/dist/convert/takeRun.d.ts.map +0 -1
  106. package/dist/convert/takeRun.js +0 -20
  107. package/dist/convert/takeRun.js.map +0 -1
  108. package/dist/defineGenerativeComponents.d.ts +0 -29
  109. package/dist/defineGenerativeComponents.d.ts.map +0 -1
  110. package/dist/defineGenerativeComponents.js +0 -34
  111. package/dist/defineGenerativeComponents.js.map +0 -1
  112. package/dist/fieldReferences.d.ts +0 -9
  113. package/dist/fieldReferences.d.ts.map +0 -1
  114. package/dist/fieldReferences.js +0 -53
  115. package/dist/fieldReferences.js.map +0 -1
  116. package/dist/generativeUIToJSX.d.ts +0 -18
  117. package/dist/generativeUIToJSX.d.ts.map +0 -1
  118. package/dist/generativeUIToJSX.js +0 -76
  119. package/dist/generativeUIToJSX.js.map +0 -1
  120. package/dist/index.shared.d.ts +0 -15
  121. package/dist/index.shared.d.ts.map +0 -1
  122. package/dist/index.shared.js +0 -9
  123. package/dist/ir.js.map +0 -1
  124. package/dist/renderGenerativeUI.d.ts +0 -14
  125. package/dist/renderGenerativeUI.d.ts.map +0 -1
  126. package/dist/renderGenerativeUI.js +0 -83
  127. package/dist/renderGenerativeUI.js.map +0 -1
  128. package/dist/scopeSchema.d.ts +0 -6
  129. package/dist/scopeSchema.d.ts.map +0 -1
  130. package/dist/scopeSchema.js +0 -56
  131. package/dist/scopeSchema.js.map +0 -1
  132. package/dist/slack/constants.d.ts +0 -93
  133. package/dist/slack/constants.d.ts.map +0 -1
  134. package/dist/slack/constants.js +0 -146
  135. package/dist/slack/constants.js.map +0 -1
  136. package/dist/slack/decodeBlockAction.d.ts +0 -10
  137. package/dist/slack/decodeBlockAction.d.ts.map +0 -1
  138. package/dist/slack/decodeBlockAction.js +0 -129
  139. package/dist/slack/decodeBlockAction.js.map +0 -1
  140. package/dist/slack/fromSlackBlocks.d.ts +0 -27
  141. package/dist/slack/fromSlackBlocks.d.ts.map +0 -1
  142. package/dist/slack/fromSlackBlocks.js +0 -483
  143. package/dist/slack/fromSlackBlocks.js.map +0 -1
  144. package/dist/slack/toSlackBlocks.d.ts +0 -4
  145. package/dist/slack/toSlackBlocks.d.ts.map +0 -1
  146. package/dist/slack/toSlackBlocks.js +0 -901
  147. package/dist/slack/toSlackBlocks.js.map +0 -1
  148. package/dist/slack/types.d.ts +0 -204
  149. package/dist/slack/types.d.ts.map +0 -1
  150. package/dist/slack/types.js +0 -0
  151. package/dist/teams/constants.d.ts +0 -34
  152. package/dist/teams/constants.d.ts.map +0 -1
  153. package/dist/teams/constants.js +0 -68
  154. package/dist/teams/constants.js.map +0 -1
  155. package/dist/teams/decodeSubmitData.d.ts +0 -26
  156. package/dist/teams/decodeSubmitData.d.ts.map +0 -1
  157. package/dist/teams/decodeSubmitData.js +0 -70
  158. package/dist/teams/decodeSubmitData.js.map +0 -1
  159. package/dist/teams/temporalId.d.ts +0 -5
  160. package/dist/teams/temporalId.d.ts.map +0 -1
  161. package/dist/teams/temporalId.js +0 -28
  162. package/dist/teams/temporalId.js.map +0 -1
  163. package/dist/teams/toAdaptiveCard.d.ts +0 -45
  164. package/dist/teams/toAdaptiveCard.d.ts.map +0 -1
  165. package/dist/teams/toAdaptiveCard.js +0 -642
  166. package/dist/teams/toAdaptiveCard.js.map +0 -1
  167. package/dist/teams/toTeamsAttachments.d.ts +0 -10
  168. package/dist/teams/toTeamsAttachments.d.ts.map +0 -1
  169. package/dist/teams/toTeamsAttachments.js +0 -99
  170. package/dist/teams/toTeamsAttachments.js.map +0 -1
  171. package/dist/teams/types.d.ts +0 -224
  172. package/dist/teams/types.d.ts.map +0 -1
  173. package/dist/teams/types.js +0 -0
  174. package/dist/temporal.d.ts +0 -33
  175. package/dist/temporal.d.ts.map +0 -1
  176. package/dist/temporal.js +0 -153
  177. package/dist/temporal.js.map +0 -1
  178. package/dist/types.d.ts +0 -99
  179. package/dist/types.d.ts.map +0 -1
  180. package/dist/types.js +0 -0
  181. package/dist/vocabulary/alert.d.ts +0 -32
  182. package/dist/vocabulary/alert.d.ts.map +0 -1
  183. package/dist/vocabulary/alert.js +0 -160
  184. package/dist/vocabulary/alert.js.map +0 -1
  185. package/dist/vocabulary/collectFormValues.d.ts +0 -23
  186. package/dist/vocabulary/collectFormValues.d.ts.map +0 -1
  187. package/dist/vocabulary/collectFormValues.js +0 -44
  188. package/dist/vocabulary/collectFormValues.js.map +0 -1
  189. package/dist/vocabulary/data.d.ts +0 -66
  190. package/dist/vocabulary/data.d.ts.map +0 -1
  191. package/dist/vocabulary/data.js +0 -503
  192. package/dist/vocabulary/data.js.map +0 -1
  193. package/dist/vocabulary/dispatch.d.ts +0 -9
  194. package/dist/vocabulary/dispatch.d.ts.map +0 -1
  195. package/dist/vocabulary/dispatch.js +0 -50
  196. package/dist/vocabulary/dispatch.js.map +0 -1
  197. package/dist/vocabulary/fact.d.ts +0 -19
  198. package/dist/vocabulary/fact.d.ts.map +0 -1
  199. package/dist/vocabulary/fact.js +0 -51
  200. package/dist/vocabulary/fact.js.map +0 -1
  201. package/dist/vocabulary/form.d.ts +0 -11
  202. package/dist/vocabulary/form.d.ts.map +0 -1
  203. package/dist/vocabulary/form.js +0 -23
  204. package/dist/vocabulary/form.js.map +0 -1
  205. package/dist/vocabulary/formatValue.d.ts +0 -7
  206. package/dist/vocabulary/formatValue.d.ts.map +0 -1
  207. package/dist/vocabulary/formatValue.js +0 -76
  208. package/dist/vocabulary/formatValue.js.map +0 -1
  209. package/dist/vocabulary/icon.d.ts +0 -41
  210. package/dist/vocabulary/icon.d.ts.map +0 -1
  211. package/dist/vocabulary/icon.js +0 -272
  212. package/dist/vocabulary/icon.js.map +0 -1
  213. package/dist/vocabulary/index.d.ts +0 -12
  214. package/dist/vocabulary/index.d.ts.map +0 -1
  215. package/dist/vocabulary/index.js +0 -36
  216. package/dist/vocabulary/index.js.map +0 -1
  217. package/dist/vocabulary/interactive.d.ts +0 -230
  218. package/dist/vocabulary/interactive.d.ts.map +0 -1
  219. package/dist/vocabulary/interactive.js +0 -584
  220. package/dist/vocabulary/interactive.js.map +0 -1
  221. package/dist/vocabulary/layout.d.ts +0 -79
  222. package/dist/vocabulary/layout.d.ts.map +0 -1
  223. package/dist/vocabulary/layout.js +0 -139
  224. package/dist/vocabulary/layout.js.map +0 -1
  225. package/dist/vocabulary/list.d.ts +0 -14
  226. package/dist/vocabulary/list.d.ts.map +0 -1
  227. package/dist/vocabulary/list.js +0 -50
  228. package/dist/vocabulary/list.js.map +0 -1
  229. package/dist/vocabulary/media.d.ts +0 -25
  230. package/dist/vocabulary/media.d.ts.map +0 -1
  231. package/dist/vocabulary/media.js +0 -42
  232. package/dist/vocabulary/media.js.map +0 -1
  233. package/dist/vocabulary/text.d.ts +0 -57
  234. package/dist/vocabulary/text.d.ts.map +0 -1
  235. package/dist/vocabulary/text.js +0 -49
  236. package/dist/vocabulary/text.js.map +0 -1
  237. package/dist/vocabulary/toTextContent.d.ts +0 -2
  238. package/dist/vocabulary/toTextContent.d.ts.map +0 -1
  239. package/dist/vocabulary/toTextContent.js +0 -6
  240. package/dist/vocabulary/toTextContent.js.map +0 -1
  241. package/src/JSONGenerativeUI.client.tsx +0 -168
  242. package/src/JSONGenerativeUI.server.tsx +0 -39
  243. package/src/JSONGenerativeUI.shared.ts +0 -90
  244. package/src/JSONGenerativeUI.test.tsx +0 -1298
  245. package/src/RadioGroupScope.server.test.tsx +0 -24
  246. package/src/RadioGroupScope.tsx +0 -34
  247. package/src/a2ui/BindingContext.ts +0 -26
  248. package/src/a2ui/PresentRenderer.tsx +0 -83
  249. package/src/a2ui/convert.test.ts +0 -2923
  250. package/src/a2ui/convert.ts +0 -1207
  251. package/src/a2ui/dataModel.ts +0 -69
  252. package/src/a2ui/reducer.test.ts +0 -422
  253. package/src/a2ui/reducer.ts +0 -363
  254. package/src/a2ui/snapshot.test.ts +0 -52
  255. package/src/a2ui/snapshot.ts +0 -52
  256. package/src/a2ui/types.ts +0 -114
  257. package/src/a2ui/valueFunctions.test.ts +0 -271
  258. package/src/a2ui/valueFunctions.ts +0 -523
  259. package/src/actionRegistry.test.ts +0 -90
  260. package/src/actionRegistry.ts +0 -88
  261. package/src/answeredValues.test.ts +0 -33
  262. package/src/answeredValues.tsx +0 -70
  263. package/src/buildPresentParameters.test.ts +0 -246
  264. package/src/buildPresentParameters.ts +0 -130
  265. package/src/constants.ts +0 -17
  266. package/src/convert/boundSpec.test.ts +0 -192
  267. package/src/convert/boundSpec.ts +0 -134
  268. package/src/convert/copyBounded.test.ts +0 -131
  269. package/src/convert/copyBounded.ts +0 -38
  270. package/src/convert/isElement.ts +0 -6
  271. package/src/convert/takeRun.test.ts +0 -64
  272. package/src/convert/takeRun.ts +0 -19
  273. package/src/defineGenerativeComponents.ts +0 -38
  274. package/src/fieldReferences.test.ts +0 -66
  275. package/src/fieldReferences.ts +0 -83
  276. package/src/generativeUIToJSX.test.ts +0 -268
  277. package/src/generativeUIToJSX.ts +0 -152
  278. package/src/index.server.test.ts +0 -106
  279. package/src/index.shared.ts +0 -61
  280. package/src/ir.test.ts +0 -203
  281. package/src/renderGenerativeUI.test.tsx +0 -373
  282. package/src/renderGenerativeUI.tsx +0 -165
  283. package/src/scopeSchema.test.ts +0 -72
  284. package/src/scopeSchema.ts +0 -71
  285. package/src/slack/a2uiSlack.test.ts +0 -198
  286. package/src/slack/constants.ts +0 -194
  287. package/src/slack/datePicker.test.ts +0 -612
  288. package/src/slack/decodeBlockAction.test.ts +0 -233
  289. package/src/slack/decodeBlockAction.ts +0 -232
  290. package/src/slack/fromSlackBlocks.test.ts +0 -1465
  291. package/src/slack/fromSlackBlocks.ts +0 -789
  292. package/src/slack/toSlackBlocks.test.ts +0 -3065
  293. package/src/slack/toSlackBlocks.ts +0 -1818
  294. package/src/slack/types.ts +0 -264
  295. package/src/teams/constants.ts +0 -93
  296. package/src/teams/decodeSubmitData.test.ts +0 -397
  297. package/src/teams/decodeSubmitData.ts +0 -110
  298. package/src/teams/temporal.test.ts +0 -766
  299. package/src/teams/temporalId.ts +0 -56
  300. package/src/teams/toAdaptiveCard.test.ts +0 -1897
  301. package/src/teams/toAdaptiveCard.ts +0 -1057
  302. package/src/teams/toTeamsAttachments.test.ts +0 -74
  303. package/src/teams/toTeamsAttachments.ts +0 -133
  304. package/src/teams/types.ts +0 -266
  305. package/src/temporal.test.ts +0 -337
  306. package/src/temporal.ts +0 -263
  307. package/src/types.ts +0 -119
  308. package/src/vocabulary/alert.test.tsx +0 -427
  309. package/src/vocabulary/alert.tsx +0 -217
  310. package/src/vocabulary/collectFormValues.test.ts +0 -389
  311. package/src/vocabulary/collectFormValues.ts +0 -116
  312. package/src/vocabulary/data.test.tsx +0 -858
  313. package/src/vocabulary/data.tsx +0 -819
  314. package/src/vocabulary/datepicker.dom.test.tsx +0 -1457
  315. package/src/vocabulary/dispatch.ts +0 -83
  316. package/src/vocabulary/fact.test.tsx +0 -94
  317. package/src/vocabulary/fact.tsx +0 -54
  318. package/src/vocabulary/form.test.tsx +0 -113
  319. package/src/vocabulary/form.tsx +0 -39
  320. package/src/vocabulary/formatValue.test.ts +0 -72
  321. package/src/vocabulary/formatValue.ts +0 -127
  322. package/src/vocabulary/icon.test.tsx +0 -64
  323. package/src/vocabulary/icon.tsx +0 -174
  324. package/src/vocabulary/index.test.ts +0 -65
  325. package/src/vocabulary/index.ts +0 -33
  326. package/src/vocabulary/interactive.dispatch.test.tsx +0 -454
  327. package/src/vocabulary/interactive.dom.test.tsx +0 -1747
  328. package/src/vocabulary/interactive.test.tsx +0 -377
  329. package/src/vocabulary/interactive.tsx +0 -1109
  330. package/src/vocabulary/layout.test.tsx +0 -323
  331. package/src/vocabulary/layout.tsx +0 -247
  332. package/src/vocabulary/list.test.tsx +0 -222
  333. package/src/vocabulary/list.tsx +0 -50
  334. package/src/vocabulary/media.test.tsx +0 -85
  335. package/src/vocabulary/media.tsx +0 -56
  336. package/src/vocabulary/text.test.tsx +0 -102
  337. package/src/vocabulary/text.tsx +0 -60
  338. package/src/vocabulary/toTextContent.ts +0 -2
@@ -1,268 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { generativeUIToJSX } from "./generativeUIToJSX";
3
-
4
- describe("generativeUIToJSX pretty depth bound", () => {
5
- it("tolerates children nested in arrays beyond the depth bound", () => {
6
- let node: unknown = { $type: "Text", value: "leaf" };
7
- for (let i = 0; i < 100; i++) node = [node];
8
- expect(() =>
9
- generativeUIToJSX({ $type: "Card", children: node }, { pretty: true }),
10
- ).not.toThrow();
11
- });
12
- });
13
-
14
- describe("generativeUIToJSX", () => {
15
- it("renders a leaf element as a self-closing tag", () => {
16
- expect(generativeUIToJSX({ $type: "Weather", id: "5d99d2e9" })).toBe(
17
- '<Weather id="5d99d2e9" />',
18
- );
19
- });
20
-
21
- it("renders an element with no props", () => {
22
- expect(generativeUIToJSX({ $type: "Divider" })).toBe("<Divider />");
23
- });
24
-
25
- it("formats prop types as JSX attributes", () => {
26
- expect(
27
- generativeUIToJSX({
28
- $type: "Box",
29
- label: "hi",
30
- count: 3,
31
- open: true,
32
- hidden: false,
33
- data: { a: 1 },
34
- }),
35
- ).toBe('<Box label="hi" count={3} open hidden={false} data={{"a":1}} />');
36
- });
37
-
38
- it("uses the expression form for strings with quotes", () => {
39
- expect(generativeUIToJSX({ $type: "Note", text: 'say "hi"' })).toBe(
40
- '<Note text={"say \\"hi\\""} />',
41
- );
42
- });
43
-
44
- it("emits a model-provided $key as the JSX key attribute", () => {
45
- expect(
46
- generativeUIToJSX({ $type: "Text", $key: "1:Text", children: "hello" }),
47
- ).toBe('<Text key="1:Text">hello</Text>');
48
- expect(generativeUIToJSX({ $type: "Item", $key: 2 })).toBe(
49
- "<Item key={2} />",
50
- );
51
- });
52
-
53
- it("renders string children between tags", () => {
54
- expect(
55
- generativeUIToJSX({ $type: "Text", tone: "muted", children: "hello" }),
56
- ).toBe('<Text tone="muted">hello</Text>');
57
- });
58
-
59
- it("renders nested and arrayed children recursively", () => {
60
- expect(
61
- generativeUIToJSX({
62
- $type: "Card",
63
- title: "Hi",
64
- children: [
65
- { $type: "Text", children: "a" },
66
- { $type: "Text", tone: "muted", children: "b" },
67
- ],
68
- }),
69
- ).toBe('<Card title="Hi"><Text>a</Text><Text tone="muted">b</Text></Card>');
70
- });
71
-
72
- it("bounds deeply nested trees instead of overflowing the stack", () => {
73
- let node: any = { $type: "Text", children: "deep" };
74
- for (let i = 0; i < 5000; i++) node = { $type: "Card", children: node };
75
- expect(() => generativeUIToJSX(node)).not.toThrow();
76
- });
77
-
78
- it("returns empty string for non-renderable nodes", () => {
79
- expect(generativeUIToJSX(null)).toBe("");
80
- expect(generativeUIToJSX(true)).toBe("");
81
- expect(generativeUIToJSX({})).toBe(""); // no $type yet (still streaming)
82
- });
83
- });
84
-
85
- describe("generativeUIToJSX with escape: true", () => {
86
- it.each([false, true])(
87
- "preserves literal entities in string props and keys with pretty=%s",
88
- (pretty) => {
89
- expect(
90
- generativeUIToJSX(
91
- { $type: "Text", $key: "&#65;", value: "&amp; &#65; &#x41;" },
92
- { escape: true, pretty },
93
- ),
94
- ).toBe('<Text key={"&#65;"} value={"&amp; &#65; &#x41;"} />');
95
- },
96
- );
97
-
98
- it("keeps entity spelling in the default display form", () => {
99
- expect(generativeUIToJSX({ $type: "Text", value: "&amp; &#65;" })).toBe(
100
- '<Text value="&amp; &#65;" />',
101
- );
102
- });
103
-
104
- it("escapes a string child containing HTML-like tags", () => {
105
- expect(
106
- generativeUIToJSX(
107
- { $type: "Text", children: "<script>alert(1)</script>" },
108
- { escape: true },
109
- ),
110
- ).toBe('<Text>{"<script>alert(1)</script>"}</Text>');
111
- });
112
-
113
- it("escapes a string child containing braces", () => {
114
- expect(
115
- generativeUIToJSX(
116
- { $type: "Text", children: "a {b} c" },
117
- { escape: true },
118
- ),
119
- ).toBe('<Text>{"a {b} c"}</Text>');
120
- });
121
-
122
- it("escapes a string child containing an ampersand", () => {
123
- expect(
124
- generativeUIToJSX({ $type: "Text", children: "Q&A" }, { escape: true }),
125
- ).toBe('<Text>{"Q&A"}</Text>');
126
- });
127
-
128
- it("leaves a plain text child verbatim", () => {
129
- expect(
130
- generativeUIToJSX(
131
- { $type: "Text", children: "hello world" },
132
- { escape: true },
133
- ),
134
- ).toBe("<Text>hello world</Text>");
135
- });
136
-
137
- it("leaves numbers, arrays, and nested objects unaffected", () => {
138
- expect(
139
- generativeUIToJSX(
140
- {
141
- $type: "Card",
142
- children: [
143
- { $type: "Text", children: 42 },
144
- { $type: "Text", data: { a: 1 }, children: "plain" },
145
- ],
146
- },
147
- { escape: true },
148
- ),
149
- ).toBe('<Card><Text>42</Text><Text data={{"a":1}}>plain</Text></Card>');
150
- });
151
- });
152
-
153
- describe("generativeUIToJSX with pretty: true", () => {
154
- const nestedCard = {
155
- $type: "Card",
156
- title: "Hi",
157
- children: [
158
- { $type: "Text", children: "a" },
159
- {
160
- $type: "Box",
161
- children: { $type: "Text", tone: "muted", children: "b" },
162
- },
163
- ],
164
- };
165
-
166
- it("keeps default output single-line and byte-identical without pretty", () => {
167
- expect(generativeUIToJSX(nestedCard)).toBe(
168
- '<Card title="Hi"><Text>a</Text><Box><Text tone="muted">b</Text></Box></Card>',
169
- );
170
- });
171
-
172
- it("block-nests elements with two-space grandchild indentation", () => {
173
- expect(generativeUIToJSX(nestedCard, { pretty: true })).toBe(
174
- [
175
- '<Card title="Hi">',
176
- " <Text>a</Text>",
177
- " <Box>",
178
- ' <Text tone="muted">b</Text>',
179
- " </Box>",
180
- "</Card>",
181
- ].join("\n"),
182
- );
183
- });
184
-
185
- it("keeps elements with only string or number children on one line", () => {
186
- expect(
187
- generativeUIToJSX(
188
- { $type: "Text", tone: "muted", children: "hello" },
189
- { pretty: true },
190
- ),
191
- ).toBe('<Text tone="muted">hello</Text>');
192
- expect(
193
- generativeUIToJSX({ $type: "Text", children: 42 }, { pretty: true }),
194
- ).toBe("<Text>42</Text>");
195
- expect(generativeUIToJSX({ $type: "Divider" }, { pretty: true })).toBe(
196
- "<Divider />",
197
- );
198
- });
199
-
200
- it("renders a root-level array with each item on its own line", () => {
201
- expect(
202
- generativeUIToJSX(
203
- [
204
- { $type: "Text", children: "a" },
205
- { $type: "Text", children: "b" },
206
- ],
207
- { pretty: true },
208
- ),
209
- ).toBe("<Text>a</Text>\n<Text>b</Text>");
210
- });
211
-
212
- it("puts mixed text and element children each on their own line", () => {
213
- expect(
214
- generativeUIToJSX(
215
- {
216
- $type: "Card",
217
- children: ["hello", { $type: "Text", children: "world" }, 7],
218
- },
219
- { pretty: true },
220
- ),
221
- ).toBe(
222
- ["<Card>", " hello", " <Text>world</Text>", " 7", "</Card>"].join(
223
- "\n",
224
- ),
225
- );
226
- });
227
-
228
- it("skips non-renderable children without blank lines", () => {
229
- expect(
230
- generativeUIToJSX(
231
- {
232
- $type: "Card",
233
- children: [
234
- null,
235
- { $type: "Text", children: "a" },
236
- false,
237
- { $type: "Text", children: "b" },
238
- true,
239
- ],
240
- },
241
- { pretty: true },
242
- ),
243
- ).toBe(
244
- ["<Card>", " <Text>a</Text>", " <Text>b</Text>", "</Card>"].join("\n"),
245
- );
246
- });
247
-
248
- it("composes escape with pretty", () => {
249
- expect(
250
- generativeUIToJSX(
251
- {
252
- $type: "Card",
253
- children: {
254
- $type: "Text",
255
- children: "<script>alert(1)</script>",
256
- },
257
- },
258
- { escape: true, pretty: true },
259
- ),
260
- ).toBe(
261
- [
262
- "<Card>",
263
- ' <Text>{"<script>alert(1)</script>"}</Text>',
264
- "</Card>",
265
- ].join("\n"),
266
- );
267
- });
268
- });
@@ -1,152 +0,0 @@
1
- import { TYPE_KEY } from "./constants";
2
-
3
- /** Options for {@link generativeUIToJSX}. */
4
- export interface GenerativeUIToJSXOptions {
5
- /** Escape string children so the output is safe to paste back into a JSX file. Defaults to `false`. */
6
- escape?: boolean;
7
- /** Pretty-print nested element trees with two-space indentation. Defaults to `false`. */
8
- pretty?: boolean;
9
- }
10
-
11
- /**
12
- * Serializes a generative-UI node to a JSX-like string for display: the "view source" of a model-produced tree. The wire form `{ $type: "Weather", id: "x" }` becomes `<Weather id="x" />`, and nested `children` render between tags: `<Card title="Hi"><Text>hello</Text></Card>`. A model-provided `$key` becomes the JSX `key` attribute.
13
- *
14
- * By default this is a faithful textual rendering, not a parser: text children are emitted verbatim (not HTML/JSX-escaped), so the result is meant to be shown, not re-parsed. Returns `""` for nodes that aren't renderable (no `$type` yet, `null`, booleans).
15
- *
16
- * Pass `{ escape: true }` to make the output safe to paste back into JSX: any string child containing `<`, `>`, `&`, `{`, or `}` is emitted as a JSON-stringified expression (`{JSON.stringify(child)}`) instead of verbatim, while a string child with none of those characters still renders verbatim.
17
- *
18
- * Pass `{ pretty: true }` to pretty-print nested element trees with two-space indentation, while pure text children stay on one line.
19
- */
20
- export function generativeUIToJSX(
21
- node: unknown,
22
- options?: GenerativeUIToJSXOptions,
23
- ): string {
24
- return toJSX(node, 0, options?.escape ?? false, options?.pretty ?? false, 0);
25
- }
26
-
27
- /**
28
- * The deepest tree we serialize. Input is model-produced, so a runaway/
29
- * adversarial response could nest arbitrarily deep and overflow the stack — past
30
- * this depth (far beyond any real UI) we stop. Mirrors the renderer's bound.
31
- */
32
- const MAX_DEPTH = 64;
33
-
34
- /** Characters that would break JSX if emitted verbatim inside a text child. */
35
- const UNSAFE_CHILD_CHARS = /[<>&{}]/;
36
-
37
- function toJSX(
38
- node: unknown,
39
- depth: number,
40
- escape: boolean,
41
- pretty: boolean,
42
- indent: number,
43
- ): string {
44
- if (depth > MAX_DEPTH) return "";
45
- if (node == null || typeof node === "boolean") return "";
46
- if (typeof node === "string") return formatChildText(node, escape);
47
- if (typeof node === "number") return String(node);
48
- if (Array.isArray(node)) {
49
- if (pretty) {
50
- return collectBlockLines(node, depth, escape, indent).join("\n");
51
- }
52
- return node
53
- .map((child) => toJSX(child, depth + 1, escape, false, 0))
54
- .join("");
55
- }
56
- if (typeof node !== "object") return "";
57
-
58
- const {
59
- [TYPE_KEY]: type,
60
- $key,
61
- children,
62
- ...props
63
- } = node as Record<string, unknown>;
64
- if (typeof type !== "string") return "";
65
-
66
- const attrs =
67
- formatAttr("key", $key, escape) +
68
- Object.entries(props)
69
- .map(([key, value]) => formatAttr(key, value, escape))
70
- .join("");
71
-
72
- if (!pretty) {
73
- const inner =
74
- children === undefined
75
- ? ""
76
- : toJSX(children, depth + 1, escape, false, 0);
77
-
78
- return inner === ""
79
- ? `<${type}${attrs} />`
80
- : `<${type}${attrs}>${inner}</${type}>`;
81
- }
82
-
83
- const pad = " ".repeat(indent);
84
-
85
- if (children === undefined || !hasElementChild(children)) {
86
- const inner =
87
- children === undefined
88
- ? ""
89
- : toJSX(children, depth + 1, escape, false, 0);
90
- return inner === ""
91
- ? `${pad}<${type}${attrs} />`
92
- : `${pad}<${type}${attrs}>${inner}</${type}>`;
93
- }
94
-
95
- const lines = collectBlockLines(children, depth + 1, escape, indent + 1);
96
- if (lines.length === 0) {
97
- return `${pad}<${type}${attrs} />`;
98
- }
99
- return `${pad}<${type}${attrs}>\n${lines.join("\n")}\n${pad}</${type}>`;
100
- }
101
-
102
- function hasElementChild(node: unknown, depth = 0): boolean {
103
- if (depth > MAX_DEPTH) return false;
104
- if (Array.isArray(node))
105
- return node.some((child) => hasElementChild(child, depth + 1));
106
- return (
107
- node != null &&
108
- typeof node === "object" &&
109
- typeof (node as Record<string, unknown>)[TYPE_KEY] === "string"
110
- );
111
- }
112
-
113
- function collectBlockLines(
114
- node: unknown,
115
- depth: number,
116
- escape: boolean,
117
- indent: number,
118
- ): string[] {
119
- if (depth > MAX_DEPTH) return [];
120
- if (node == null || typeof node === "boolean") return [];
121
- if (Array.isArray(node)) {
122
- return node.flatMap((child) =>
123
- collectBlockLines(child, depth + 1, escape, indent),
124
- );
125
- }
126
- if (typeof node === "string") {
127
- return [`${" ".repeat(indent)}${formatChildText(node, escape)}`];
128
- }
129
- if (typeof node === "number") {
130
- return [`${" ".repeat(indent)}${String(node)}`];
131
- }
132
- const rendered = toJSX(node, depth, escape, true, indent);
133
- return rendered === "" ? [] : [rendered];
134
- }
135
-
136
- function formatChildText(value: string, escape: boolean): string {
137
- return escape && UNSAFE_CHILD_CHARS.test(value)
138
- ? `{${JSON.stringify(value)}}`
139
- : value;
140
- }
141
-
142
- /** Formats one prop as a JSX attribute (`id="x"`, `count={3}`, `open`, …). */
143
- function formatAttr(key: string, value: unknown, escape: boolean): string {
144
- if (value === undefined) return "";
145
- if (value === true) return ` ${key}`;
146
- if (typeof value === "string") {
147
- return /["\n]/.test(value) || (escape && value.includes("&"))
148
- ? ` ${key}={${JSON.stringify(value)}}`
149
- : ` ${key}="${value}"`;
150
- }
151
- return ` ${key}={${JSON.stringify(value)}}`;
152
- }
@@ -1,106 +0,0 @@
1
- /// <reference types="node" />
2
- import { describe, expect, it } from "vitest";
3
- import { existsSync, readFileSync, statSync } from "node:fs";
4
- import { dirname, join, relative, resolve } from "node:path";
5
-
6
- const SRC_DIR = resolve(__dirname);
7
-
8
- // Next.js fails the build when a module reachable from a Server Component
9
- // names one of these React APIs in an import; the namespace import is allowed.
10
- const CLIENT_ONLY_REACT_APIS = new Set([
11
- "Component",
12
- "PureComponent",
13
- "createContext",
14
- "createFactory",
15
- "useActionState",
16
- "useEffect",
17
- "useEffectEvent",
18
- "useImperativeHandle",
19
- "useInsertionEffect",
20
- "useLayoutEffect",
21
- "useOptimistic",
22
- "useReducer",
23
- "useRef",
24
- "useState",
25
- "useSyncExternalStore",
26
- "useTransition",
27
- ]);
28
-
29
- const RELATIVE_SPECIFIER_RE = /\b(?:from|import)\s*\(?\s*["'](\.[^"']*)["']/g;
30
-
31
- const NAMED_FROM_REACT_RE =
32
- /(?:import|export)\s+(type\s+)?(?:\w+\s*,\s*)?\{([^}]*)\}\s+from\s+["']react["']/g;
33
-
34
- function resolveRelative(fromFile: string, spec: string): string | undefined {
35
- const base = resolve(dirname(fromFile), spec);
36
- return [
37
- base,
38
- `${base}.ts`,
39
- `${base}.tsx`,
40
- join(base, "index.ts"),
41
- join(base, "index.tsx"),
42
- ].find((candidate) => existsSync(candidate) && statSync(candidate).isFile());
43
- }
44
-
45
- function collectGraph(entry: string): Set<string> {
46
- const visited = new Set<string>();
47
- const pending = [entry];
48
- while (pending.length > 0) {
49
- const file = pending.pop()!;
50
- if (visited.has(file)) continue;
51
- visited.add(file);
52
- for (const [, spec] of readFileSync(file, "utf8").matchAll(
53
- RELATIVE_SPECIFIER_RE,
54
- )) {
55
- const resolved = resolveRelative(file, spec!);
56
- if (resolved) pending.push(resolved);
57
- }
58
- }
59
- return visited;
60
- }
61
-
62
- function clientOnlyReactImports(file: string): string[] {
63
- const names: string[] = [];
64
- for (const [, typeOnly, specifiers] of readFileSync(file, "utf8").matchAll(
65
- NAMED_FROM_REACT_RE,
66
- )) {
67
- if (typeOnly) continue;
68
- for (const specifier of specifiers!.split(",")) {
69
- const name = specifier.trim().split(/\s+as\s+/)[0]!;
70
- if (CLIENT_ONLY_REACT_APIS.has(name)) names.push(name);
71
- }
72
- }
73
- return names;
74
- }
75
-
76
- // Server code reaches every entry: the subpaths carry no react-server
77
- // condition, and the docs app resolves the root to its source through
78
- // tsconfig paths, which skip that condition.
79
- const ENTRIES = [
80
- "index.server.ts",
81
- "index.ts",
82
- "ir.ts",
83
- "a2ui.ts",
84
- "slack.ts",
85
- "teams.ts",
86
- ];
87
-
88
- describe("published entries", () => {
89
- const graph = new Set(
90
- ENTRIES.flatMap((entry) => [...collectGraph(join(SRC_DIR, entry))]),
91
- );
92
-
93
- it("reach the vocabulary and the client renderer", () => {
94
- expect(graph).toContain(join(SRC_DIR, "vocabulary", "interactive.tsx"));
95
- expect(graph).toContain(join(SRC_DIR, "JSONGenerativeUI.client.tsx"));
96
- });
97
-
98
- it("never name a client-only React API in an import", () => {
99
- const violations = [...graph].flatMap((file) =>
100
- clientOnlyReactImports(file).map(
101
- (name) => `${relative(SRC_DIR, file)}: ${name}`,
102
- ),
103
- );
104
- expect(violations).toEqual([]);
105
- });
106
- });
@@ -1,61 +0,0 @@
1
- export { renderGenerativeUI } from "./renderGenerativeUI";
2
- export { generativeUIToJSX } from "./generativeUIToJSX";
3
- export type { GenerativeUIToJSXOptions } from "./generativeUIToJSX";
4
- export { buildPresentParameters } from "./buildPresentParameters";
5
- export { defineGenerativeComponents } from "./defineGenerativeComponents";
6
- export { TYPE_KEY } from "./constants";
7
- export { normalizeUINode, normalizeSpec } from "./ir";
8
- export {
9
- TEXT_SIZES,
10
- IMAGE_SIZE_TOKENS,
11
- WEIGHTS,
12
- COLORS,
13
- ALIGNS,
14
- JUSTIFIES,
15
- BUTTON_STYLES,
16
- ALERT_TONES,
17
- ICON_NAMES,
18
- } from "./ir";
19
- export { defaultGenerativeUILibrary } from "./vocabulary";
20
- export { createActionRegistry, emptyActionRegistry } from "./actionRegistry";
21
- export type {
22
- ActionHandler,
23
- ActionRegistry,
24
- ActionDispatchContext,
25
- } from "./actionRegistry";
26
- export type {
27
- JSONGenerativeUIOptions,
28
- PresentTool,
29
- PresentToolOptions,
30
- PromptUserTool,
31
- } from "./JSONGenerativeUI.shared";
32
- export type {
33
- GenerativeUILibrary,
34
- GenerativeUIComponent,
35
- GenerativeUIElement,
36
- GenerativeUIProps,
37
- GenerativeUINode,
38
- GenerativeUIStatus,
39
- GenerativeUIRenderContext,
40
- GenerativeUIAction,
41
- GenerativeUIDispatch,
42
- } from "./types";
43
- export type {
44
- UINode,
45
- UIElement,
46
- UIChildren,
47
- LegacyComponentNode,
48
- Action,
49
- UISpec,
50
- NormalizedUINode,
51
- NormalizedUIElement,
52
- TextSize,
53
- ImageSize,
54
- Weight,
55
- Color,
56
- Align,
57
- Justify,
58
- ButtonStyle,
59
- AlertTone,
60
- IconName,
61
- } from "./ir";