@assistant-ui/react-generative-ui 0.0.23 → 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 +5 -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 -152
  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 -530
  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 -319
  306. package/src/temporal.ts +0 -259
  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 -1148
  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 -1048
  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,134 +0,0 @@
1
- import { copyBounded } from "./copyBounded";
2
-
3
- /** The maximum traversal depth, counted in visited values, not tree levels. */
4
- export const MAX_TRAVERSAL_DEPTH = 64;
5
-
6
- /**
7
- * The element-nesting ceiling a caller observes. `boundNode` visits an element
8
- * and its `children` array separately, so one level of nesting spends two of
9
- * {@link MAX_TRAVERSAL_DEPTH}; it runs before the conversion walk and is never
10
- * the looser of the two. A root array costs one more level than a root object.
11
- */
12
- export const MAX_ELEMENT_DEPTH = MAX_TRAVERSAL_DEPTH / 2;
13
-
14
- /**
15
- * The maximum number of entries kept at any single level (root, or a
16
- * `children` array at any depth) of the raw spec, before it ever reaches
17
- * `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass
18
- * can never be made to walk further than this regardless of what the array
19
- * claims about itself.
20
- */
21
- export const CHILDREN_CAP = 200;
22
-
23
- /**
24
- * The total number of nodes {@link boundSpec} will visit across one walk,
25
- * regardless of how many times a shared reference recurs. Bounds the
26
- * combinatorial work a DAG of shared or self-referential nodes would
27
- * otherwise force even though each individual array stays within
28
- * {@link CHILDREN_CAP}.
29
- */
30
- export const NODE_BUDGET = 5000;
31
-
32
- export type ClampReason = "children" | "budget" | "cycle" | "depth";
33
-
34
- /** Renders the warning detail a protocol reports for one {@link ClampReason}. */
35
- export function clampReasonDetail(reason: ClampReason): string {
36
- if (reason === "budget") {
37
- return `the tree was truncated after ${NODE_BUDGET} nodes.`;
38
- }
39
- if (reason === "cycle") return "a self-referencing node was dropped.";
40
- if (reason === "depth") {
41
- return `nodes deeper than ${MAX_ELEMENT_DEPTH} levels were dropped.`;
42
- }
43
- return `children were clamped to ${CHILDREN_CAP} entries.`;
44
- }
45
-
46
- interface BoundState {
47
- remaining: number;
48
- exhausted: boolean;
49
- }
50
-
51
- function boundNode(
52
- value: unknown,
53
- depth: number,
54
- onClamp: (reason: ClampReason) => void,
55
- state: BoundState,
56
- ancestors: WeakSet<object>,
57
- ): unknown {
58
- if (state.remaining <= 0) {
59
- if (!state.exhausted) {
60
- state.exhausted = true;
61
- onClamp("budget");
62
- }
63
- return null;
64
- }
65
- state.remaining -= 1;
66
- if (depth > MAX_TRAVERSAL_DEPTH) {
67
- onClamp("depth");
68
- return null;
69
- }
70
- if (Array.isArray(value)) {
71
- if (ancestors.has(value)) {
72
- onClamp("cycle");
73
- return null;
74
- }
75
- ancestors.add(value);
76
- const { items: bounded, truncated } = copyBounded(value, CHILDREN_CAP);
77
- if (truncated) onClamp("children");
78
- const result = bounded.map((item) =>
79
- boundNode(item, depth + 1, onClamp, state, ancestors),
80
- );
81
- ancestors.delete(value);
82
- return result;
83
- }
84
- if (value !== null && typeof value === "object") {
85
- const record = value as Record<string, unknown>;
86
- const children = record["children"];
87
- // The spread performs a `children` read of its own, so the copy states the
88
- // bounded value even when there is none.
89
- if (children === undefined) return { ...record, children: undefined };
90
- if (ancestors.has(value)) {
91
- onClamp("cycle");
92
- return null;
93
- }
94
- ancestors.add(value);
95
- const result = {
96
- ...record,
97
- children: boundNode(children, depth + 1, onClamp, state, ancestors),
98
- };
99
- ancestors.delete(value);
100
- return result;
101
- }
102
- return value;
103
- }
104
-
105
- /**
106
- * Produces a bounded plain copy of a raw generative-ui spec before it
107
- * reaches `normalizeSpec`, whose own traversal of the root array or any
108
- * `children` array walks the full reported length of a hostile proxied
109
- * array before any per-field cap downstream ever applies. Every array
110
- * (root, or `children` at any depth) is capped to {@link CHILDREN_CAP}
111
- * entries by index, which bounds a proxy however it fabricates `length` or
112
- * replaces its array methods; recursion itself is capped at
113
- * {@link MAX_TRAVERSAL_DEPTH}. Every object is returned as a plain copy whose
114
- * `children` came from a single read, because a record that answers
115
- * `[[HasProperty]]` and `[[Get]]`, or two `[[Get]]`s, differently would
116
- * otherwise hand the bound one value and `normalizeSpec` another, skipping
117
- * every bound here for that subtree. `onClamp` fires once per level that was
118
- * truncated, receiving the reason for that truncation: `"children"`,
119
- * `"depth"`, `"budget"`, or `"cycle"`. The walk also spends a total budget of
120
- * {@link NODE_BUDGET} nodes, so shared references cannot multiply work
121
- * exponentially, and a node that is its own ancestor is cut to `null`.
122
- */
123
- export function boundSpec(
124
- spec: unknown,
125
- onClamp: (reason: ClampReason) => void,
126
- ): unknown {
127
- return boundNode(
128
- spec,
129
- 0,
130
- onClamp,
131
- { remaining: NODE_BUDGET, exhausted: false },
132
- new WeakSet(),
133
- );
134
- }
@@ -1,131 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { copyBounded } from "./copyBounded";
3
-
4
- const hostileSpecies = (real: unknown[], injected: unknown[]) => {
5
- function HostileCtor() {
6
- return { map: () => injected, some: () => true, length: 0 };
7
- }
8
- const constructor = { [Symbol.species]: HostileCtor };
9
- return new Proxy(real, {
10
- get: (target, prop, receiver) =>
11
- prop === "constructor"
12
- ? constructor
13
- : Reflect.get(target, prop, receiver),
14
- });
15
- };
16
-
17
- describe("copyBounded", () => {
18
- it("copies every entry of a shorter array", () => {
19
- expect(copyBounded(["a", "b"], 5).items).toEqual(["a", "b"]);
20
- });
21
-
22
- it("stops at the cap", () => {
23
- expect(copyBounded(["a", "b", "c"], 2).items).toEqual(["a", "b"]);
24
- });
25
-
26
- it("returns a fresh array rather than the input", () => {
27
- const input = ["a"];
28
- expect(copyBounded(input, 5).items).not.toBe(input);
29
- });
30
-
31
- it("leaves an absent index absent, as slice does", () => {
32
- const sparse: unknown[] = [];
33
- sparse[0] = "a";
34
- sparse[2] = "c";
35
-
36
- const { items: result } = copyBounded(sparse, 5);
37
-
38
- expect(result).toHaveLength(3);
39
- expect(1 in result).toBe(false);
40
- expect(result).toEqual(sparse.slice(0, 5));
41
- });
42
-
43
- it("does not read an index the input reports as absent", () => {
44
- const reads: string[] = [];
45
- const probe = new Proxy(["a", "b"], {
46
- has: (target, prop) => prop !== "1" && Reflect.has(target, prop),
47
- get: (target, prop, receiver) => {
48
- if (typeof prop === "string" && /^\d+$/.test(prop)) reads.push(prop);
49
- return Reflect.get(target, prop, receiver);
50
- },
51
- });
52
-
53
- const { items: result } = copyBounded(probe, 2);
54
-
55
- expect(reads).toEqual(["0"]);
56
- expect(1 in result).toBe(false);
57
- });
58
-
59
- it("reports truncation from the same length read that bounds the copy", () => {
60
- let reads = 0;
61
- const shifty = new Proxy(["a", "b", "c"], {
62
- get: (target, prop, receiver) => {
63
- if (prop === "length") {
64
- reads += 1;
65
- return reads === 1 ? 3 : 1;
66
- }
67
- return Reflect.get(target, prop, receiver);
68
- },
69
- });
70
-
71
- const { items, truncated } = copyBounded(shifty, 2);
72
-
73
- expect(items).toHaveLength(2);
74
- expect(truncated).toBe(true);
75
- });
76
-
77
- it("matches slice for every shape of reported length", () => {
78
- const reportedLengths = [2.5, Infinity, NaN, -1, "3", 2, 0, -0.5, 1e21];
79
- const lying = (length: unknown) =>
80
- new Proxy(["a", "b", "c"], {
81
- get: (target, prop, receiver) =>
82
- prop === "length" ? length : Reflect.get(target, prop, receiver),
83
- });
84
-
85
- for (const cap of [0, 1, 2, 5]) {
86
- for (const length of reportedLengths) {
87
- const { items, truncated } = copyBounded(lying(length), cap);
88
-
89
- expect(items).toEqual(
90
- Array.prototype.slice.call(lying(length), 0, cap),
91
- );
92
- expect(truncated).toBe(
93
- Array.prototype.slice.call(lying(length), 0, cap + 1).length > cap,
94
- );
95
- }
96
- }
97
- });
98
-
99
- it("bounds an array whose reported length is fabricated", () => {
100
- const hostile = new Proxy(["a", "b"], {
101
- get: (target, prop, receiver) =>
102
- prop === "length"
103
- ? Number.MAX_SAFE_INTEGER
104
- : Reflect.get(target, prop, receiver),
105
- });
106
-
107
- expect(copyBounded(hostile, 3).items).toEqual(["a", "b", undefined]);
108
- });
109
-
110
- it("bounds an array that replaces its slice", () => {
111
- const hostile = new Proxy(["a", "b"], {
112
- get: (target, prop, receiver) =>
113
- prop === "slice"
114
- ? () => Array(500).fill("injected")
115
- : Reflect.get(target, prop, receiver),
116
- });
117
-
118
- expect(copyBounded(hostile, 5).items).toEqual(["a", "b"]);
119
- });
120
-
121
- it("bounds an array that redirects Symbol.species to a foreign object", () => {
122
- const injected = Array(500).fill("injected");
123
- const { items: result } = copyBounded(
124
- hostileSpecies(["a", "b"], injected),
125
- 5,
126
- );
127
-
128
- expect(Array.isArray(result)).toBe(true);
129
- expect(result).toEqual(["a", "b"]);
130
- });
131
- });
@@ -1,38 +0,0 @@
1
- /**
2
- * Copies at most `cap` entries out of `value` by index.
3
- *
4
- * Every array method dispatches on the input: a replaced `slice` is called
5
- * directly, and the intrinsic `Array.prototype.slice.call` still reaches a
6
- * `constructor` with a `@@species` whose `[[Construct]]` returns an arbitrary
7
- * object, which then supplies the `map` or `some` the caller runs next. Reading
8
- * indices consults neither, so it is the only form that bounds a hostile array.
9
- *
10
- * Absent indices stay absent, matching what `slice` produces for a sparse
11
- * input, so callers that skip holes still skip them. `truncated` comes from the
12
- * same `length` read as the bound, so an input cannot report one figure to the
13
- * cap and another to the caller's warning.
14
- *
15
- * A reported `length` is normalized the way `ToLength` does before it reaches
16
- * `count`, because a fractional one would otherwise reach `items.length` and
17
- * throw `RangeError` where `slice` returns a bounded result. `truncated` reads
18
- * that same normalized figure, so a `length` whose `valueOf` answers differently
19
- * on a second conversion cannot separate the warning from the bound.
20
- */
21
- export const copyBounded = <T>(
22
- value: readonly T[],
23
- cap: number,
24
- ): { readonly items: T[]; readonly truncated: boolean } => {
25
- const reported = value.length;
26
- const integral = Math.trunc(Number(reported));
27
- const usable =
28
- Number.isNaN(integral) || integral <= 0
29
- ? 0
30
- : Math.min(integral, Number.MAX_SAFE_INTEGER);
31
- const count = Math.min(cap, usable);
32
- const items: T[] = [];
33
- for (let index = 0; index < count; index += 1) {
34
- if (index in value) items[index] = value[index] as T;
35
- }
36
- items.length = count;
37
- return { items, truncated: usable > cap };
38
- };
@@ -1,6 +0,0 @@
1
- import type { NormalizedUIElement, NormalizedUINode } from "../ir";
2
-
3
- export const isElement = (
4
- node: NormalizedUINode,
5
- ): node is NormalizedUIElement =>
6
- typeof node === "object" && node !== null && !Array.isArray(node);
@@ -1,64 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import type { NormalizedUIElement, NormalizedUINode } from "../ir";
3
- import { takeRun } from "./takeRun";
4
-
5
- const element = (type: string): NormalizedUIElement => ({ type, props: {} });
6
-
7
- const isFact = (candidate: NormalizedUIElement) => candidate.type === "Fact";
8
-
9
- describe("takeRun", () => {
10
- it("collects the contiguous matching run and reports the resume index", () => {
11
- const fact = element("Fact");
12
- const nodes = [fact, fact, element("Text"), fact];
13
-
14
- expect(takeRun(nodes, 0, isFact)).toEqual({ run: [fact, fact], next: 2 });
15
- });
16
-
17
- it("starts at the given index rather than the head of the list", () => {
18
- const fact = element("Fact");
19
- const nodes = [element("Text"), fact, fact];
20
-
21
- expect(takeRun(nodes, 1, isFact)).toEqual({ run: [fact, fact], next: 3 });
22
- });
23
-
24
- it("stops at the end of the list", () => {
25
- const fact = element("Fact");
26
-
27
- expect(takeRun([fact, fact], 0, isFact)).toEqual({
28
- run: [fact, fact],
29
- next: 2,
30
- });
31
- });
32
-
33
- it("returns an empty run without advancing when the first node does not match", () => {
34
- expect(takeRun([element("Text"), element("Fact")], 0, isFact)).toEqual({
35
- run: [],
36
- next: 0,
37
- });
38
- });
39
-
40
- it("stops on every non-element node shape", () => {
41
- const fact = element("Fact");
42
- const nonElements: (NormalizedUINode | undefined)[] = [
43
- undefined,
44
- null,
45
- "",
46
- "text",
47
- 0,
48
- 1,
49
- [],
50
- [fact],
51
- ];
52
-
53
- for (const nonElement of nonElements) {
54
- expect(takeRun([fact, nonElement, fact], 0, isFact)).toEqual({
55
- run: [fact],
56
- next: 1,
57
- });
58
- }
59
- });
60
-
61
- it("stops past the end of the list", () => {
62
- expect(takeRun([element("Fact")], 1, isFact)).toEqual({ run: [], next: 1 });
63
- });
64
- });
@@ -1,19 +0,0 @@
1
- import type { NormalizedUIElement, NormalizedUINode } from "../ir";
2
- import { isElement } from "./isElement";
3
-
4
- export function takeRun(
5
- nodes: readonly (NormalizedUINode | undefined)[],
6
- index: number,
7
- matches: (element: NormalizedUIElement) => boolean,
8
- ): { run: NormalizedUIElement[]; next: number } {
9
- const run: NormalizedUIElement[] = [];
10
- let next = index;
11
- while (next < nodes.length) {
12
- const candidate = nodes[next];
13
- if (candidate === undefined || !isElement(candidate) || !matches(candidate))
14
- break;
15
- run.push(candidate);
16
- next += 1;
17
- }
18
- return { run, next };
19
- }
@@ -1,38 +0,0 @@
1
- import type { GenerativeUILibrary } from "./types";
2
-
3
- /**
4
- * Authoring helper for a `"use generative"` generative-UI library — the set of
5
- * components the model may render. Each component colocates its `properties`
6
- * schema (kept on every build, drives the tool parameters) with its `render`
7
- * (kept only on the client). Pass the result to {@link JSONGenerativeUI}:
8
- *
9
- * ```tsx
10
- * "use generative";
11
- * const generative = new JSONGenerativeUI({
12
- * library: defineGenerativeComponents({
13
- * Card: {
14
- * description: "A card.",
15
- * properties: z.object({ title: z.string() }),
16
- * render: (props) => <Card {...props} />,
17
- * },
18
- * }),
19
- * });
20
- * ```
21
- *
22
- * Unlike {@link defineToolkit}, it has **no runtime implementation**. A
23
- * `"use generative"` compiler unwraps the `defineGenerativeComponents(...)` call
24
- * per build, dropping each `render` (and its client-only imports) from the server
25
- * build. Reaching it at runtime means the module wasn't compiled (the directive
26
- * is missing, or it was used outside a `"use generative"` file), so it throws
27
- * rather than shipping client `render` code to the server.
28
- */
29
- export function defineGenerativeComponents(
30
- _library: GenerativeUILibrary,
31
- ): GenerativeUILibrary {
32
- throw new Error(
33
- "[assistant-ui] defineGenerativeComponents() has no runtime implementation " +
34
- "— it is stripped at build time by the use-generative compiler. Reaching " +
35
- "it means this module was not compiled (e.g. used outside a " +
36
- '"use generative" file). Add the directive, or do not use it here.',
37
- );
38
- }
@@ -1,66 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { hasFieldReference, resolveFieldReferences } from "./fieldReferences";
3
-
4
- describe("field references", () => {
5
- it("resolve from own fields only and drop what does not resolve", () => {
6
- const fields = Object.assign(Object.create({ inherited: "no" }), {
7
- note: "hi",
8
- empty: undefined,
9
- }) as Record<string, unknown>;
10
-
11
- expect(
12
- resolveFieldReferences(
13
- {
14
- note: { $field: "note" },
15
- list: [
16
- { $field: "note" },
17
- { $field: "empty" },
18
- { $field: "inherited" },
19
- ],
20
- literal: { $field: "note", other: 1 },
21
- },
22
- fields,
23
- ),
24
- ).toEqual({
25
- note: "hi",
26
- list: ["hi"],
27
- literal: { $field: "note", other: 1 },
28
- });
29
- });
30
-
31
- it("fall back to the reference's own fallback, and read only $field and fallback as a reference", () => {
32
- expect(
33
- resolveFieldReferences(
34
- {
35
- filled: { $field: "note", fallback: "old" },
36
- missing: { $field: "gone", fallback: "kept" },
37
- empty: { $field: "gone" },
38
- list: [{ $field: "gone", fallback: 0 }],
39
- data: { $field: "note", fallback: "old", extra: true },
40
- },
41
- { note: "new" },
42
- ),
43
- ).toEqual({
44
- filled: "new",
45
- missing: "kept",
46
- list: [0],
47
- data: { $field: "note", fallback: "old", extra: true },
48
- });
49
- });
50
-
51
- it("terminate on a cyclic value and keep the cycle", () => {
52
- const cyclic: Record<string, unknown> = { note: { $field: "note" } };
53
- cyclic["self"] = cyclic;
54
- const acyclic: Record<string, unknown> = { a: 1 };
55
- acyclic["self"] = { back: [acyclic] };
56
-
57
- expect(hasFieldReference(cyclic)).toBe(true);
58
- expect(hasFieldReference(acyclic)).toBe(false);
59
- const resolved = resolveFieldReferences(cyclic, { note: "hi" }) as Record<
60
- string,
61
- unknown
62
- >;
63
- expect(resolved["note"]).toBe("hi");
64
- expect(resolved["self"]).toBe(cyclic);
65
- });
66
- });
@@ -1,83 +0,0 @@
1
- const isRecord = (value: unknown): value is Record<string, unknown> =>
2
- typeof value === "object" && value !== null && !Array.isArray(value);
3
-
4
- const MAX_DEPTH = 64;
5
-
6
- const isFieldReference = (
7
- value: unknown,
8
- ): value is { readonly $field: string; readonly fallback?: unknown } =>
9
- isRecord(value) &&
10
- typeof value["$field"] === "string" &&
11
- Object.keys(value).every((key) => key === "$field" || key === "fallback");
12
-
13
- /**
14
- * Whether `value` contains a `{ "$field": name }` or `{ "$field": name, "fallback": value }` reference within 64 levels.
15
- */
16
- export const hasFieldReference = (value: unknown): boolean => {
17
- const seen = new Set<object>();
18
- const visit = (entry: unknown, depth: number): boolean => {
19
- if (
20
- typeof entry !== "object" ||
21
- entry === null ||
22
- depth > MAX_DEPTH ||
23
- seen.has(entry)
24
- ) {
25
- return false;
26
- }
27
- if (isFieldReference(entry)) return true;
28
- seen.add(entry);
29
- return Object.values(entry).some((item) => visit(item, depth + 1));
30
- };
31
- return visit(value, 0);
32
- };
33
-
34
- /**
35
- * Replaces each `{ "$field": name }` reference inside `value` with the own property `name` of `fields`, or with the reference's own `fallback` when that is `undefined`. A reference that still resolves to `undefined` is dropped from its object or array, and a cyclic reference or anything nested deeper than 64 levels is kept as is.
36
- */
37
- export const resolveFieldReferences = (
38
- value: unknown,
39
- fields: Readonly<Record<string, unknown>>,
40
- ): unknown => {
41
- const ancestors = new Set<object>();
42
- const resolve = (entry: unknown, depth: number): unknown => {
43
- if (isFieldReference(entry)) {
44
- const current = Object.hasOwn(fields, entry.$field)
45
- ? fields[entry.$field]
46
- : undefined;
47
- if (current !== undefined) return current;
48
- return Object.hasOwn(entry, "fallback") ? entry.fallback : undefined;
49
- }
50
- if (
51
- typeof entry !== "object" ||
52
- entry === null ||
53
- depth > MAX_DEPTH ||
54
- ancestors.has(entry)
55
- ) {
56
- return entry;
57
- }
58
- ancestors.add(entry);
59
- try {
60
- if (Array.isArray(entry)) {
61
- return entry
62
- .map((item) => resolve(item, depth + 1))
63
- .filter((item) => item !== undefined);
64
- }
65
- const result: Record<string, unknown> = {};
66
- for (const [key, item] of Object.entries(entry)) {
67
- const resolved = resolve(item, depth + 1);
68
- if (resolved !== undefined) {
69
- Object.defineProperty(result, key, {
70
- value: resolved,
71
- enumerable: true,
72
- configurable: true,
73
- writable: true,
74
- });
75
- }
76
- }
77
- return result;
78
- } finally {
79
- ancestors.delete(entry);
80
- }
81
- };
82
- return resolve(value, 0);
83
- };