@tai42/studio-sdk 15.0.0 → 16.0.1

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 (322) hide show
  1. package/dist/components/explorer-body.d.ts +49 -0
  2. package/dist/components/explorer-body.d.ts.map +1 -0
  3. package/dist/components/explorer-body.js +67 -0
  4. package/dist/components/explorer-body.js.map +1 -0
  5. package/dist/components/explorer-page-size.d.ts +4 -0
  6. package/dist/components/explorer-page-size.d.ts.map +1 -0
  7. package/dist/components/explorer-page-size.js +36 -0
  8. package/dist/components/explorer-page-size.js.map +1 -0
  9. package/dist/components/explorer-tag-filter.d.ts +17 -0
  10. package/dist/components/explorer-tag-filter.d.ts.map +1 -0
  11. package/dist/components/explorer-tag-filter.js +29 -0
  12. package/dist/components/explorer-tag-filter.js.map +1 -0
  13. package/dist/components/explorer-tags.d.ts +22 -0
  14. package/dist/components/explorer-tags.d.ts.map +1 -0
  15. package/dist/components/explorer-tags.js +51 -0
  16. package/dist/components/explorer-tags.js.map +1 -0
  17. package/dist/components/explorer-view.d.ts +3 -17
  18. package/dist/components/explorer-view.d.ts.map +1 -1
  19. package/dist/components/explorer-view.js +53 -170
  20. package/dist/components/explorer-view.js.map +1 -1
  21. package/dist/components/extension-combo-builder.d.ts +0 -5
  22. package/dist/components/extension-combo-builder.d.ts.map +1 -1
  23. package/dist/components/extension-combo-builder.js +4 -42
  24. package/dist/components/extension-combo-builder.js.map +1 -1
  25. package/dist/components/extension-combo-parts.d.ts +44 -0
  26. package/dist/components/extension-combo-parts.d.ts.map +1 -0
  27. package/dist/components/extension-combo-parts.js +57 -0
  28. package/dist/components/extension-combo-parts.js.map +1 -0
  29. package/dist/components/fleet-report.d.ts.map +1 -1
  30. package/dist/components/fleet-report.js +51 -37
  31. package/dist/components/fleet-report.js.map +1 -1
  32. package/dist/components/icons/chrome-marks.d.ts +50 -0
  33. package/dist/components/icons/chrome-marks.d.ts.map +1 -0
  34. package/dist/components/icons/chrome-marks.js +51 -0
  35. package/dist/components/icons/chrome-marks.js.map +1 -0
  36. package/dist/components/icons/icon-frame.d.ts +29 -0
  37. package/dist/components/icons/icon-frame.d.ts.map +1 -0
  38. package/dist/components/icons/icon-frame.js +19 -0
  39. package/dist/components/icons/icon-frame.js.map +1 -0
  40. package/dist/components/icons/index.d.ts +40 -0
  41. package/dist/components/icons/index.d.ts.map +1 -0
  42. package/dist/components/icons/index.js +6 -0
  43. package/dist/components/icons/index.js.map +1 -0
  44. package/dist/components/icons/nav-map.d.ts +14 -0
  45. package/dist/components/icons/nav-map.d.ts.map +1 -0
  46. package/dist/components/icons/nav-map.js +28 -0
  47. package/dist/components/icons/nav-map.js.map +1 -0
  48. package/dist/components/icons/nav-marks.d.ts +44 -0
  49. package/dist/components/icons/nav-marks.d.ts.map +1 -0
  50. package/dist/components/icons/nav-marks.js +45 -0
  51. package/dist/components/icons/nav-marks.js.map +1 -0
  52. package/dist/components/icons/status-marks.d.ts +21 -0
  53. package/dist/components/icons/status-marks.d.ts.map +1 -0
  54. package/dist/components/icons/status-marks.js +22 -0
  55. package/dist/components/icons/status-marks.js.map +1 -0
  56. package/dist/components/icons/theme-marks.d.ts +9 -0
  57. package/dist/components/icons/theme-marks.d.ts.map +1 -0
  58. package/dist/components/icons/theme-marks.js +10 -0
  59. package/dist/components/icons/theme-marks.js.map +1 -0
  60. package/dist/components/json-diff.d.ts.map +1 -1
  61. package/dist/components/json-diff.js +1 -3
  62. package/dist/components/json-diff.js.map +1 -1
  63. package/dist/components/json-tree-context.d.ts +13 -0
  64. package/dist/components/json-tree-context.d.ts.map +1 -0
  65. package/dist/components/json-tree-context.js +15 -0
  66. package/dist/components/json-tree-context.js.map +1 -0
  67. package/dist/components/json-tree-model.d.ts +56 -0
  68. package/dist/components/json-tree-model.d.ts.map +1 -0
  69. package/dist/components/json-tree-model.js +125 -0
  70. package/dist/components/json-tree-model.js.map +1 -0
  71. package/dist/components/json-tree-nodes.d.ts +30 -0
  72. package/dist/components/json-tree-nodes.d.ts.map +1 -0
  73. package/dist/components/json-tree-nodes.js +118 -0
  74. package/dist/components/json-tree-nodes.js.map +1 -0
  75. package/dist/components/json-tree.d.ts.map +1 -1
  76. package/dist/components/json-tree.js +7 -295
  77. package/dist/components/json-tree.js.map +1 -1
  78. package/dist/components/json-value-format.d.ts +20 -0
  79. package/dist/components/json-value-format.d.ts.map +1 -0
  80. package/dist/components/json-value-format.js +63 -0
  81. package/dist/components/json-value-format.js.map +1 -0
  82. package/dist/components/markdown-blocks.d.ts +38 -0
  83. package/dist/components/markdown-blocks.d.ts.map +1 -0
  84. package/dist/components/markdown-blocks.js +168 -0
  85. package/dist/components/markdown-blocks.js.map +1 -0
  86. package/dist/components/markdown-inline.d.ts +24 -0
  87. package/dist/components/markdown-inline.d.ts.map +1 -0
  88. package/dist/components/markdown-inline.js +257 -0
  89. package/dist/components/markdown-inline.js.map +1 -0
  90. package/dist/components/markdown.d.ts +3 -52
  91. package/dist/components/markdown.d.ts.map +1 -1
  92. package/dist/components/markdown.js +4 -376
  93. package/dist/components/markdown.js.map +1 -1
  94. package/dist/components/observer-registry.d.ts +38 -0
  95. package/dist/components/observer-registry.d.ts.map +1 -0
  96. package/dist/components/observer-registry.js +131 -0
  97. package/dist/components/observer-registry.js.map +1 -0
  98. package/dist/components/overflow-measure.d.ts +38 -0
  99. package/dist/components/overflow-measure.d.ts.map +1 -0
  100. package/dist/components/overflow-measure.js +54 -0
  101. package/dist/components/overflow-measure.js.map +1 -0
  102. package/dist/components/prose-regions.d.ts +81 -0
  103. package/dist/components/prose-regions.d.ts.map +1 -0
  104. package/dist/components/prose-regions.js +128 -0
  105. package/dist/components/prose-regions.js.map +1 -0
  106. package/dist/components/scroll-region.d.ts.map +1 -1
  107. package/dist/components/scroll-region.js +3 -298
  108. package/dist/components/scroll-region.js.map +1 -1
  109. package/dist/components/tab-stop-parse.d.ts +53 -0
  110. package/dist/components/tab-stop-parse.d.ts.map +1 -0
  111. package/dist/components/tab-stop-parse.js +202 -0
  112. package/dist/components/tab-stop-parse.js.map +1 -0
  113. package/dist/components/templated-text-field.d.ts.map +1 -1
  114. package/dist/components/templated-text-field.js +10 -148
  115. package/dist/components/templated-text-field.js.map +1 -1
  116. package/dist/components/templated-text-kwargs-editor.d.ts +24 -0
  117. package/dist/components/templated-text-kwargs-editor.d.ts.map +1 -0
  118. package/dist/components/templated-text-kwargs-editor.js +48 -0
  119. package/dist/components/templated-text-kwargs-editor.js.map +1 -0
  120. package/dist/components/templated-text-kwargs.d.ts +37 -0
  121. package/dist/components/templated-text-kwargs.d.ts.map +1 -0
  122. package/dist/components/templated-text-kwargs.js +66 -0
  123. package/dist/components/templated-text-kwargs.js.map +1 -0
  124. package/dist/components/templated-text-source.d.ts +33 -0
  125. package/dist/components/templated-text-source.d.ts.map +1 -0
  126. package/dist/components/templated-text-source.js +53 -0
  127. package/dist/components/templated-text-source.js.map +1 -0
  128. package/dist/components/tool-picker-parts.d.ts +39 -0
  129. package/dist/components/tool-picker-parts.d.ts.map +1 -0
  130. package/dist/components/tool-picker-parts.js +87 -0
  131. package/dist/components/tool-picker-parts.js.map +1 -0
  132. package/dist/components/tool-picker.d.ts +1 -2
  133. package/dist/components/tool-picker.d.ts.map +1 -1
  134. package/dist/components/tool-picker.js +4 -66
  135. package/dist/components/tool-picker.js.map +1 -1
  136. package/dist/components/version-history-panel.d.ts.map +1 -1
  137. package/dist/components/version-history-panel.js +7 -34
  138. package/dist/components/version-history-panel.js.map +1 -1
  139. package/dist/components/version-history-rows.d.ts +48 -0
  140. package/dist/components/version-history-rows.d.ts.map +1 -0
  141. package/dist/components/version-history-rows.js +51 -0
  142. package/dist/components/version-history-rows.js.map +1 -0
  143. package/dist/guards.d.ts +6 -0
  144. package/dist/guards.d.ts.map +1 -0
  145. package/dist/guards.js +8 -0
  146. package/dist/guards.js.map +1 -0
  147. package/dist/hooks/interactions-connection.d.ts +43 -0
  148. package/dist/hooks/interactions-connection.d.ts.map +1 -0
  149. package/dist/hooks/interactions-connection.js +171 -0
  150. package/dist/hooks/interactions-connection.js.map +1 -0
  151. package/dist/hooks/interactions-stream.d.ts +80 -0
  152. package/dist/hooks/interactions-stream.d.ts.map +1 -0
  153. package/dist/hooks/interactions-stream.js +233 -0
  154. package/dist/hooks/interactions-stream.js.map +1 -0
  155. package/dist/hooks/useApi.d.ts +2409 -2293
  156. package/dist/hooks/useApi.d.ts.map +1 -1
  157. package/dist/hooks/useSse.d.ts +3 -6
  158. package/dist/hooks/useSse.d.ts.map +1 -1
  159. package/dist/hooks/useSse.js +38 -451
  160. package/dist/hooks/useSse.js.map +1 -1
  161. package/dist/navigation/app-link.d.ts +25 -0
  162. package/dist/navigation/app-link.d.ts.map +1 -0
  163. package/dist/navigation/app-link.js +33 -0
  164. package/dist/navigation/app-link.js.map +1 -0
  165. package/dist/navigation/context.d.ts +3 -20
  166. package/dist/navigation/context.d.ts.map +1 -1
  167. package/dist/navigation/context.js +7 -267
  168. package/dist/navigation/context.js.map +1 -1
  169. package/dist/navigation/entry-state.d.ts +23 -0
  170. package/dist/navigation/entry-state.d.ts.map +1 -0
  171. package/dist/navigation/entry-state.js +39 -0
  172. package/dist/navigation/entry-state.js.map +1 -0
  173. package/dist/navigation/guard-registry.d.ts +30 -0
  174. package/dist/navigation/guard-registry.d.ts.map +1 -0
  175. package/dist/navigation/guard-registry.js +46 -0
  176. package/dist/navigation/guard-registry.js.map +1 -0
  177. package/dist/navigation/guarded-navigation.d.ts +24 -0
  178. package/dist/navigation/guarded-navigation.d.ts.map +1 -0
  179. package/dist/navigation/guarded-navigation.js +102 -0
  180. package/dist/navigation/guarded-navigation.js.map +1 -0
  181. package/dist/navigation/index.d.ts +3 -2
  182. package/dist/navigation/index.d.ts.map +1 -1
  183. package/dist/navigation/index.js +2 -1
  184. package/dist/navigation/index.js.map +1 -1
  185. package/dist/navigation/types.d.ts +8 -11
  186. package/dist/navigation/types.d.ts.map +1 -1
  187. package/dist/navigation/use-back-forward-guard.d.ts +5 -0
  188. package/dist/navigation/use-back-forward-guard.d.ts.map +1 -0
  189. package/dist/navigation/use-back-forward-guard.js +82 -0
  190. package/dist/navigation/use-back-forward-guard.js.map +1 -0
  191. package/dist/plugin/registry.d.ts +6 -15
  192. package/dist/plugin/registry.d.ts.map +1 -1
  193. package/dist/plugin/registry.js +83 -75
  194. package/dist/plugin/registry.js.map +1 -1
  195. package/dist/schema-editor/SchemaEditor.d.ts +8 -1
  196. package/dist/schema-editor/SchemaEditor.d.ts.map +1 -1
  197. package/dist/schema-editor/SchemaEditor.js +2 -2
  198. package/dist/schema-editor/SchemaEditor.js.map +1 -1
  199. package/dist/schema-form/SecretRefField.d.ts +2 -3
  200. package/dist/schema-form/SecretRefField.d.ts.map +1 -1
  201. package/dist/schema-form/SecretRefField.js +23 -12
  202. package/dist/schema-form/SecretRefField.js.map +1 -1
  203. package/dist/schema-form/classify.d.ts +1 -122
  204. package/dist/schema-form/classify.d.ts.map +1 -1
  205. package/dist/schema-form/classify.js +174 -237
  206. package/dist/schema-form/classify.js.map +1 -1
  207. package/dist/schema-form/default-value.d.ts.map +1 -1
  208. package/dist/schema-form/default-value.js +42 -19
  209. package/dist/schema-form/default-value.js.map +1 -1
  210. package/dist/schema-form/enum-field.d.ts +1 -1
  211. package/dist/schema-form/enum-field.d.ts.map +1 -1
  212. package/dist/schema-form/field-model.d.ts +121 -0
  213. package/dist/schema-form/field-model.d.ts.map +1 -0
  214. package/dist/schema-form/field-model.js +2 -0
  215. package/dist/schema-form/field-model.js.map +1 -0
  216. package/dist/schema-form/field-node.d.ts.map +1 -1
  217. package/dist/schema-form/field-node.js +25 -14
  218. package/dist/schema-form/field-node.js.map +1 -1
  219. package/dist/schema-form/media-field.d.ts +1 -1
  220. package/dist/schema-form/media-field.d.ts.map +1 -1
  221. package/dist/schema-form/media-field.js +47 -28
  222. package/dist/schema-form/media-field.js.map +1 -1
  223. package/dist/schema-form/string-annotations.d.ts +20 -0
  224. package/dist/schema-form/string-annotations.d.ts.map +1 -0
  225. package/dist/schema-form/string-annotations.js +105 -0
  226. package/dist/schema-form/string-annotations.js.map +1 -0
  227. package/dist/schema-form/string-field.d.ts +1 -1
  228. package/dist/schema-form/string-field.d.ts.map +1 -1
  229. package/dist/schema-form/union-field.d.ts +1 -1
  230. package/dist/schema-form/union-field.d.ts.map +1 -1
  231. package/dist/schema-form/union-field.js.map +1 -1
  232. package/dist/schema-form/union.d.ts +1 -1
  233. package/dist/schema-form/union.d.ts.map +1 -1
  234. package/dist/schema-form/validate.d.ts.map +1 -1
  235. package/dist/schema-form/validate.js +126 -105
  236. package/dist/schema-form/validate.js.map +1 -1
  237. package/dist/state-binding/AdapterMapping.d.ts.map +1 -1
  238. package/dist/state-binding/AdapterMapping.js +0 -0
  239. package/dist/state-binding/AdapterMapping.js.map +1 -1
  240. package/dist/state-binding/StateAttachRow.d.ts.map +1 -1
  241. package/dist/state-binding/StateAttachRow.js +52 -36
  242. package/dist/state-binding/StateAttachRow.js.map +1 -1
  243. package/dist/state-binding/adapter-mapping-row.d.ts +17 -0
  244. package/dist/state-binding/adapter-mapping-row.d.ts.map +1 -0
  245. package/dist/state-binding/adapter-mapping-row.js +87 -0
  246. package/dist/state-binding/adapter-mapping-row.js.map +1 -0
  247. package/dist/structured-output/StructuredOutput.d.ts.map +1 -1
  248. package/dist/structured-output/StructuredOutput.js +1 -3
  249. package/dist/structured-output/StructuredOutput.js.map +1 -1
  250. package/package.json +2 -2
  251. package/src/components/explorer-body.tsx +239 -0
  252. package/src/components/explorer-page-size.ts +39 -0
  253. package/src/components/explorer-tag-filter.tsx +80 -0
  254. package/src/components/explorer-tags.ts +64 -0
  255. package/src/components/explorer-view.tsx +114 -366
  256. package/src/components/extension-combo-builder.tsx +35 -152
  257. package/src/components/extension-combo-parts.tsx +237 -0
  258. package/src/components/fleet-report.tsx +97 -54
  259. package/src/components/icons/chrome-marks.tsx +195 -0
  260. package/src/components/icons/icon-frame.tsx +50 -0
  261. package/src/components/icons/index.ts +84 -0
  262. package/src/components/icons/nav-map.ts +55 -0
  263. package/src/components/icons/nav-marks.tsx +205 -0
  264. package/src/components/icons/status-marks.tsx +46 -0
  265. package/src/components/icons/theme-marks.tsx +33 -0
  266. package/src/components/json-diff.tsx +1 -4
  267. package/src/components/json-tree-context.ts +26 -0
  268. package/src/components/json-tree-model.ts +139 -0
  269. package/src/components/json-tree-nodes.tsx +224 -0
  270. package/src/components/json-tree.tsx +15 -434
  271. package/src/components/json-value-format.ts +64 -0
  272. package/src/components/markdown-blocks.ts +198 -0
  273. package/src/components/markdown-inline.tsx +365 -0
  274. package/src/components/markdown.tsx +5 -471
  275. package/src/components/observer-registry.ts +139 -0
  276. package/src/components/overflow-measure.ts +69 -0
  277. package/src/components/prose-regions.ts +153 -0
  278. package/src/components/scroll-region.tsx +20 -360
  279. package/src/components/tab-stop-parse.ts +217 -0
  280. package/src/components/templated-text-field.tsx +38 -330
  281. package/src/components/templated-text-kwargs-editor.tsx +147 -0
  282. package/src/components/templated-text-kwargs.ts +92 -0
  283. package/src/components/templated-text-source.tsx +226 -0
  284. package/src/components/tool-picker-parts.tsx +184 -0
  285. package/src/components/tool-picker.tsx +22 -114
  286. package/src/components/version-history-panel.tsx +38 -142
  287. package/src/components/version-history-rows.tsx +220 -0
  288. package/src/guards.ts +7 -0
  289. package/src/hooks/interactions-connection.ts +227 -0
  290. package/src/hooks/interactions-stream.ts +274 -0
  291. package/src/hooks/useSse.ts +50 -477
  292. package/src/navigation/app-link.tsx +70 -0
  293. package/src/navigation/context.tsx +11 -330
  294. package/src/navigation/entry-state.ts +42 -0
  295. package/src/navigation/guard-registry.ts +65 -0
  296. package/src/navigation/guarded-navigation.ts +147 -0
  297. package/src/navigation/index.ts +2 -2
  298. package/src/navigation/types.ts +8 -11
  299. package/src/navigation/use-back-forward-guard.ts +90 -0
  300. package/src/plugin/registry.ts +111 -90
  301. package/src/schema-editor/SchemaEditor.tsx +9 -1
  302. package/src/schema-form/SecretRefField.tsx +121 -71
  303. package/src/schema-form/classify.ts +244 -395
  304. package/src/schema-form/default-value.ts +48 -17
  305. package/src/schema-form/enum-field.tsx +1 -1
  306. package/src/schema-form/field-model.ts +133 -0
  307. package/src/schema-form/field-node.tsx +94 -38
  308. package/src/schema-form/media-field.tsx +134 -65
  309. package/src/schema-form/string-annotations.ts +111 -0
  310. package/src/schema-form/string-field.tsx +1 -1
  311. package/src/schema-form/union-field.tsx +2 -1
  312. package/src/schema-form/union.ts +1 -1
  313. package/src/schema-form/validate.ts +184 -103
  314. package/src/state-binding/AdapterMapping.tsx +0 -0
  315. package/src/state-binding/StateAttachRow.tsx +222 -147
  316. package/src/state-binding/adapter-mapping-row.tsx +232 -0
  317. package/src/structured-output/StructuredOutput.tsx +1 -4
  318. package/dist/components/icons.d.ts +0 -160
  319. package/dist/components/icons.d.ts.map +0 -1
  320. package/dist/components/icons.js +0 -160
  321. package/dist/components/icons.js.map +0 -1
  322. package/src/components/icons.tsx +0 -586
@@ -17,133 +17,10 @@ import {
17
17
  variantTag,
18
18
  type EnumOption,
19
19
  } from './resolve';
20
+ import type { ClassifiedField, UnionVariant } from './field-model';
21
+ import { expressionAnnotation, mediaUpload } from './string-annotations';
20
22
  import type { JsonSchema } from './types';
21
23
 
22
- /**
23
- * How a string field accepts binary content as a JSON-serializable string.
24
- * Derived from a field's JSON-Schema media annotations (see the `SchemaForm` module).
25
- */
26
- export interface MediaUpload {
27
- /**
28
- * `base64` — the emitted value is the raw base64 body (no `data:` prefix),
29
- * matching `contentEncoding: "base64"`. `data-url` — the emitted value is a
30
- * full `data:<mime>;base64,<body>` URL, matching `format: "data-url"`.
31
- */
32
- readonly encoding: 'base64' | 'data-url';
33
- /**
34
- * The `contentMediaType` constraint (e.g. `image/*`, `application/pdf`), used
35
- * for MIME validation and the file picker's `accept`. `undefined` for a bare
36
- * `format: "data-url"` field with no declared type.
37
- */
38
- readonly mediaType: string | undefined;
39
- /** A per-field byte cap from `contentMaxBytes`, if the schema pins one. */
40
- readonly maxBytes: number | undefined;
41
- }
42
-
43
- /** One top-level key of the expression's input document, with a one-line gloss. */
44
- export interface ExpressionAnnotationKey {
45
- readonly name: string;
46
- readonly gloss: string;
47
- }
48
-
49
- /**
50
- * The `x-tai42-expression` annotation on a string schema: the server declares
51
- * that the field's value is authored in a pipeline expression language (only jq
52
- * today) and optionally describes what `.` is for that expression. The renderer
53
- * uses it to open the visual-editor door instead of a plain text box. The shape
54
- * mirrors jq-studio's input-shape descriptor WITHOUT importing it — classification
55
- * stays a pure schema concern; the renderer maps this onto a jq field declaration.
56
- */
57
- export interface ExpressionAnnotation {
58
- /** The declared language. An unknown language never reaches here — it classifies
59
- * as a plain string, so an older client renders a newer server's field as the
60
- * ordinary text input it always was. */
61
- readonly language: 'jq';
62
- /** Short chip label for what `.` is (e.g. "node envelope"). */
63
- readonly label: string | undefined;
64
- /** One sentence describing what `.` is in this field. */
65
- readonly blurb: string | undefined;
66
- /** The top-level keys of `.`, each with a one-liner. */
67
- readonly keys: readonly ExpressionAnnotationKey[] | undefined;
68
- /** What the expression must return (e.g. "true or false"). */
69
- readonly returns: string | undefined;
70
- /** Per-field caveats. */
71
- readonly caveats: readonly string[] | undefined;
72
- /** True when the annotation carries a `sample` — tracked separately because a
73
- * legitimate sample may be JSON `null`. */
74
- readonly hasSample: boolean;
75
- /** A static skeleton of `.` for the Test panel. Meaningful only when
76
- * {@link hasSample} is true. */
77
- readonly sample: unknown;
78
- }
79
-
80
- /** One selectable variant of a union field. */
81
- export interface UnionVariant {
82
- readonly label: string;
83
- /** The discriminator tag value, or `undefined` for a non-discriminated union. */
84
- readonly tag: unknown;
85
- /** The variant schema as written (unresolved — the renderer resolves it). */
86
- readonly schema: JsonSchema;
87
- }
88
-
89
- /** The renderer-facing classification of a schema node. */
90
- export type FieldModel =
91
- | { readonly kind: 'const'; readonly value: unknown }
92
- | { readonly kind: 'enum'; readonly options: readonly EnumOption[] }
93
- | {
94
- readonly kind: 'string';
95
- readonly format: string | undefined;
96
- /** Present when media annotations opt the field into the upload control. */
97
- readonly media: MediaUpload | undefined;
98
- /** Present when a well-formed `x-tai42-expression` annotation opts the
99
- * field into the jq expression editor (see {@link ExpressionAnnotation}). */
100
- readonly expression: ExpressionAnnotation | undefined;
101
- }
102
- | { readonly kind: 'number'; readonly integer: boolean }
103
- | { readonly kind: 'boolean' }
104
- | { readonly kind: 'array'; readonly items: JsonSchema }
105
- | {
106
- readonly kind: 'object';
107
- readonly properties: readonly (readonly [string, JsonSchema])[];
108
- readonly required: ReadonlySet<string>;
109
- }
110
- | {
111
- // A string-keyed map typed by `additionalProperties` (no fixed
112
- // `properties`), edited as key/value rows. `values` is the entry value
113
- // schema AS WRITTEN — the entry editor resolves any `$ref` itself, exactly
114
- // as `array.items` is.
115
- readonly kind: 'record';
116
- readonly values: JsonSchema;
117
- }
118
- | {
119
- readonly kind: 'union';
120
- readonly variants: readonly UnionVariant[];
121
- readonly discriminator: string | undefined;
122
- }
123
- | {
124
- // A shape with no structured editor to build — a property-less object, a
125
- // bare `additionalProperties`-open object, an items-less array, an empty/
126
- // multi-type/`allOf` schema. It is NOT undroppable: every one of these still
127
- // describes JSON, so the renderer edits it as free-form JSON in a mono
128
- // textarea rather than dead-ending on a badge. `jsonType` is the container
129
- // the schema commits to, so a valid buffer must parse to it (`'any'` = no
130
- // constraint beyond "is JSON"); `reason` records which shape fell through.
131
- readonly kind: 'json';
132
- readonly jsonType: 'object' | 'array' | 'any';
133
- readonly reason: string;
134
- };
135
-
136
- /** A fully classified field: its model plus resolved metadata. */
137
- export interface ClassifiedField {
138
- readonly model: FieldModel;
139
- /** True when JSON `null` is an accepted value (a null-union member). */
140
- readonly nullable: boolean;
141
- /** The effective (ref-resolved) schema this classification describes. */
142
- readonly schema: JsonSchema;
143
- readonly title: string | undefined;
144
- readonly description: string | undefined;
145
- }
146
-
147
24
  function firstString(...values: readonly unknown[]): string | undefined {
148
25
  for (const value of values) {
149
26
  if (typeof value === 'string' && value.length > 0) return value;
@@ -151,91 +28,6 @@ function firstString(...values: readonly unknown[]): string | undefined {
151
28
  return undefined;
152
29
  }
153
30
 
154
- /**
155
- * Detect the media-upload annotations on a (ref-resolved) string schema. A field
156
- * opts into the upload control when it carries `contentEncoding: "base64"` plus a
157
- * `contentMediaType`, OR `format: "data-url"`. Anything else is a plain string.
158
- */
159
- function mediaUpload(schema: JsonSchema): MediaUpload | undefined {
160
- const mediaType =
161
- typeof schema.contentMediaType === 'string' ? schema.contentMediaType : undefined;
162
- const maxBytes =
163
- typeof schema.contentMaxBytes === 'number' && schema.contentMaxBytes > 0
164
- ? schema.contentMaxBytes
165
- : undefined;
166
- if (schema.format === 'data-url') {
167
- return { encoding: 'data-url', mediaType, maxBytes };
168
- }
169
- if (schema.contentEncoding === 'base64' && mediaType !== undefined) {
170
- return { encoding: 'base64', mediaType, maxBytes };
171
- }
172
- return undefined;
173
- }
174
-
175
- const EXPRESSION_KEYWORD = 'x-tai42-expression';
176
-
177
- /** A parsed-JSON plain object (arrays and `null` are `typeof 'object'` too). */
178
- function isRecord(value: unknown): value is Record<string, unknown> {
179
- return typeof value === 'object' && value !== null && !Array.isArray(value);
180
- }
181
-
182
- /**
183
- * Detect the `x-tai42-expression` annotation on a (ref-resolved) string schema,
184
- * mirroring {@link mediaUpload}: a well-formed annotation opts the field into the
185
- * jq expression editor; anything else — absent, non-object, unknown language, or
186
- * any wrongly-typed member — yields `undefined`, so the field renders EXACTLY as
187
- * the plain string input it is today. Malformed payloads NEVER throw: a bad
188
- * annotation is a server-side authoring bug, and degrading to the ordinary text
189
- * box keeps the form usable while staying byte-identical to the unannotated
190
- * rendering.
191
- */
192
- function expressionAnnotation(schema: JsonSchema): ExpressionAnnotation | undefined {
193
- const raw: unknown = schema[EXPRESSION_KEYWORD];
194
- if (!isRecord(raw)) return undefined;
195
- // The language gate doubles as the forward-compatibility valve: a future
196
- // language this client does not understand degrades to a plain string field.
197
- if (raw.language !== 'jq') return undefined;
198
-
199
- const { label, blurb, returns } = raw;
200
- if (label !== undefined && typeof label !== 'string') return undefined;
201
- if (blurb !== undefined && typeof blurb !== 'string') return undefined;
202
- if (returns !== undefined && typeof returns !== 'string') return undefined;
203
-
204
- let keys: ExpressionAnnotationKey[] | undefined;
205
- if (raw.keys !== undefined) {
206
- if (!Array.isArray(raw.keys)) return undefined;
207
- keys = [];
208
- for (const entry of raw.keys as readonly unknown[]) {
209
- if (!isRecord(entry) || typeof entry.name !== 'string' || typeof entry.gloss !== 'string') {
210
- return undefined;
211
- }
212
- keys.push({ name: entry.name, gloss: entry.gloss });
213
- }
214
- }
215
-
216
- let caveats: string[] | undefined;
217
- if (raw.caveats !== undefined) {
218
- if (!Array.isArray(raw.caveats)) return undefined;
219
- caveats = [];
220
- for (const entry of raw.caveats as readonly unknown[]) {
221
- if (typeof entry !== 'string') return undefined;
222
- caveats.push(entry);
223
- }
224
- }
225
-
226
- // `sample` is any JSON — including `null` — so presence is tracked, not typed.
227
- return {
228
- language: 'jq',
229
- label,
230
- blurb,
231
- keys,
232
- returns,
233
- caveats,
234
- hasSample: 'sample' in raw,
235
- sample: raw.sample,
236
- };
237
- }
238
-
239
31
  /**
240
32
  * Land a shape the form has no structured editor for on the free-form JSON
241
33
  * fallback (a mono JSON textarea) rather than a dead badge — a field is never
@@ -254,132 +46,250 @@ function jsonFallback(
254
46
  return { model: { kind: 'json', jsonType, reason }, nullable, schema, title, description };
255
47
  }
256
48
 
257
- /**
258
- * Classify a schema node (resolving `$ref` first). `wrapper` metadata (a
259
- * ref-site title/description) is threaded down so a `{ $ref, title }` or a
260
- * null-union wrapper keeps its property-level label.
261
- */
262
- export function classifySchema(raw: JsonSchema, root: JsonSchema): ClassifiedField {
263
- const resolved = resolveRef(raw, root);
264
- const title = firstString(raw.title, resolved.title);
265
- const description = firstString(raw.description, resolved.description);
49
+ // const pins a single value. Null-acceptance is fully decidable here: the one
50
+ // permitted value either IS null or it is not. Without this, a `const: null`
51
+ // field would seed to null and then fail the validator's upstream "must not be
52
+ // null" gate before the equality check ever ran — permanently invalid.
53
+ function classifyConst(
54
+ resolved: JsonSchema,
55
+ title: string | undefined,
56
+ description: string | undefined,
57
+ ): ClassifiedField {
58
+ return {
59
+ model: { kind: 'const', value: resolved.const },
60
+ nullable: resolved.const === null,
61
+ schema: resolved,
62
+ title,
63
+ description,
64
+ };
65
+ }
266
66
 
267
- // const pins a single value. Null-acceptance is fully decidable here: the one
268
- // permitted value either IS null or it is not. Without this, a `const: null`
269
- // field would seed to null and then fail the validator's upstream "must not be
270
- // null" gate before the equality check ever ran — permanently invalid.
271
- if ('const' in resolved) {
272
- return {
273
- model: { kind: 'const', value: resolved.const },
274
- nullable: resolved.const === null,
275
- schema: resolved,
67
+ // enum → a fixed choice list.
68
+ function classifyEnum(
69
+ resolved: JsonSchema,
70
+ title: string | undefined,
71
+ description: string | undefined,
72
+ ): ClassifiedField {
73
+ const enumValues = resolved.enum ?? [];
74
+ const options: EnumOption[] = enumValues.map((value) => ({ value, label: scalarLabel(value) }));
75
+ const nullable = enumValues.some((value) => value === null);
76
+ return { model: { kind: 'enum', options }, nullable, schema: resolved, title, description };
77
+ }
78
+
79
+ // anyOf/oneOf union normalization.
80
+ function classifyUnion(
81
+ resolved: JsonSchema,
82
+ members: readonly JsonSchema[],
83
+ root: JsonSchema,
84
+ title: string | undefined,
85
+ description: string | undefined,
86
+ ): ClassifiedField {
87
+ const nonNull = members.filter((member) => !isNullSchema(resolveRef(member, root)));
88
+ const nullable = nonNull.length !== members.length;
89
+ const soleNonNull = nonNull[0];
90
+ if (soleNonNull === undefined) {
91
+ // Every member is `null` — degenerate, but `null` is still valid, so edit
92
+ // it as JSON (nullable) rather than dead-ending on a badge.
93
+ return jsonFallback(
94
+ resolved,
95
+ 'any',
96
+ 'union with no non-null members',
276
97
  title,
277
98
  description,
278
- };
99
+ true,
100
+ );
279
101
  }
280
-
281
- // enum → a fixed choice list.
282
- if (resolved.enum !== undefined) {
283
- const options: EnumOption[] = resolved.enum.map((value) => ({
284
- value,
285
- label: scalarLabel(value),
286
- }));
287
- const nullable = resolved.enum.some((value) => value === null);
288
- return { model: { kind: 'enum', options }, nullable, schema: resolved, title, description };
102
+ if (nonNull.length === 1) {
103
+ return classifySoleUnionMember(soleNonNull, resolved, root, nullable, title, description);
289
104
  }
105
+ const discriminator = resolved.discriminator?.propertyName;
106
+ const variants: UnionVariant[] = nonNull.map((member, index) => {
107
+ const tag = discriminator === undefined ? undefined : variantTag(member, discriminator, root);
108
+ return { label: variantLabel(member, index, tag, root), tag, schema: member };
109
+ });
110
+ return {
111
+ model: { kind: 'union', variants, discriminator },
112
+ nullable,
113
+ schema: resolved,
114
+ title,
115
+ description,
116
+ };
117
+ }
290
118
 
291
- // anyOf/oneOf union normalization.
292
- const members = unionMembers(resolved);
293
- if (members !== undefined) {
294
- const nonNull = members.filter((member) => !isNullSchema(resolveRef(member, root)));
295
- const nullable = nonNull.length !== members.length;
296
- const soleNonNull = nonNull[0];
297
- if (soleNonNull === undefined) {
298
- // Every member is `null` — degenerate, but `null` is still valid, so edit
299
- // it as JSON (nullable) rather than dead-ending on a badge.
300
- return jsonFallback(
301
- resolved,
302
- 'any',
303
- 'union with no non-null members',
304
- title,
305
- description,
306
- true,
307
- );
308
- }
309
- if (nonNull.length === 1) {
310
- const inner = classifySchema(soleNonNull, root);
311
- // Media annotations (`contentEncoding`/`contentMediaType`/`format: data-url`)
312
- // that land on the OUTER null-union wrapper of an OPTIONAL field would
313
- // otherwise be lost when delegating to the inner string member, silently
314
- // degrading an optional upload field to a plain text box. Re-detect them on
315
- // the wrapper and attach to the inner string model. The expression
316
- // annotation gets the same treatment for the same reason: an optional
317
- // annotated field must keep its editor door.
318
- const model =
319
- inner.model.kind === 'string'
320
- ? {
321
- ...inner.model,
322
- media: inner.model.media ?? mediaUpload(resolved),
323
- expression: inner.model.expression ?? expressionAnnotation(resolved),
324
- }
325
- : inner.model;
326
- return {
327
- model,
328
- nullable: nullable || inner.nullable,
329
- schema: inner.schema,
330
- title: title ?? inner.title,
331
- description: description ?? inner.description,
332
- };
333
- }
334
- const discriminator = resolved.discriminator?.propertyName;
335
- const variants: UnionVariant[] = nonNull.map((member, index) => {
336
- const tag = discriminator === undefined ? undefined : variantTag(member, discriminator, root);
337
- return { label: variantLabel(member, index, tag, root), tag, schema: member };
338
- });
119
+ // A null-union of exactly one non-null member: delegate to that member.
120
+ function classifySoleUnionMember(
121
+ soleNonNull: JsonSchema,
122
+ resolved: JsonSchema,
123
+ root: JsonSchema,
124
+ nullable: boolean,
125
+ title: string | undefined,
126
+ description: string | undefined,
127
+ ): ClassifiedField {
128
+ const inner = classifySchema(soleNonNull, root);
129
+ // Media annotations (`contentEncoding`/`contentMediaType`/`format: data-url`)
130
+ // that land on the OUTER null-union wrapper of an OPTIONAL field would
131
+ // otherwise be lost when delegating to the inner string member, silently
132
+ // degrading an optional upload field to a plain text box. Re-detect them on
133
+ // the wrapper and attach to the inner string model. The expression
134
+ // annotation gets the same treatment for the same reason: an optional
135
+ // annotated field must keep its editor door.
136
+ const model =
137
+ inner.model.kind === 'string'
138
+ ? {
139
+ ...inner.model,
140
+ media: inner.model.media ?? mediaUpload(resolved),
141
+ expression: inner.model.expression ?? expressionAnnotation(resolved),
142
+ }
143
+ : inner.model;
144
+ return {
145
+ model,
146
+ nullable: nullable || inner.nullable,
147
+ schema: inner.schema,
148
+ title: title ?? inner.title,
149
+ description: description ?? inner.description,
150
+ };
151
+ }
152
+
153
+ // allOf: Pydantic wraps a single $ref this way; a multi-member intersection is
154
+ // not a form-renderable construct.
155
+ function classifyAllOf(
156
+ resolved: JsonSchema,
157
+ root: JsonSchema,
158
+ title: string | undefined,
159
+ description: string | undefined,
160
+ ): ClassifiedField {
161
+ const allOf = resolved.allOf ?? [];
162
+ const soleAllOf = allOf[0];
163
+ if (allOf.length === 1 && soleAllOf !== undefined) {
164
+ const inner = classifySchema(soleAllOf, root);
339
165
  return {
340
- model: { kind: 'union', variants, discriminator },
341
- nullable,
342
- schema: resolved,
166
+ model: inner.model,
167
+ nullable: inner.nullable,
168
+ schema: inner.schema,
169
+ title: title ?? inner.title,
170
+ description: description ?? inner.description,
171
+ };
172
+ }
173
+ // Whether the intersection accepts `null` is not statically decidable here
174
+ // (it does iff EVERY member does), and the json escape hatch only enforces
175
+ // what it KNOWS — so stay permissive like `jsonType: 'any'` itself and let
176
+ // the server-side contract be the authority, rather than rejecting a null
177
+ // the schema may well permit (e.g. an intersection of open schemas).
178
+ return jsonFallback(
179
+ resolved,
180
+ 'any',
181
+ 'allOf intersection of multiple schemas',
182
+ title,
183
+ description,
184
+ true,
185
+ );
186
+ }
187
+
188
+ function classifyString(
189
+ resolved: JsonSchema,
190
+ title: string | undefined,
191
+ description: string | undefined,
192
+ nullable: boolean,
193
+ ): ClassifiedField {
194
+ return {
195
+ model: {
196
+ kind: 'string',
197
+ format: resolved.format,
198
+ media: mediaUpload(resolved),
199
+ expression: expressionAnnotation(resolved),
200
+ },
201
+ nullable,
202
+ schema: resolved,
203
+ title,
204
+ description,
205
+ };
206
+ }
207
+
208
+ function classifyArray(
209
+ resolved: JsonSchema,
210
+ title: string | undefined,
211
+ description: string | undefined,
212
+ nullable: boolean,
213
+ ): ClassifiedField {
214
+ if (resolved.items === undefined) {
215
+ // A bare array with no item schema: no per-item editor to build, but the
216
+ // value is still a JSON array — edit it whole as free-form JSON.
217
+ return jsonFallback(
218
+ resolved,
219
+ 'array',
220
+ 'array without an items schema',
343
221
  title,
344
222
  description,
345
- };
223
+ nullable,
224
+ );
346
225
  }
226
+ return {
227
+ model: { kind: 'array', items: resolved.items },
228
+ nullable,
229
+ schema: resolved,
230
+ title,
231
+ description,
232
+ };
233
+ }
347
234
 
348
- // allOf: Pydantic wraps a single $ref this way; a multi-member intersection is
349
- // not a form-renderable construct.
350
- if (resolved.allOf !== undefined) {
351
- const soleAllOf = resolved.allOf[0];
352
- if (resolved.allOf.length === 1 && soleAllOf !== undefined) {
353
- const inner = classifySchema(soleAllOf, root);
235
+ function classifyObject(
236
+ resolved: JsonSchema,
237
+ title: string | undefined,
238
+ description: string | undefined,
239
+ nullable: boolean,
240
+ ): ClassifiedField {
241
+ if (resolved.properties === undefined) {
242
+ // A free-form object typed by a VALUE SCHEMA (`additionalProperties` is a
243
+ // schema, not `false`/`true`/absent) is a string→X map — renderable as
244
+ // key/value rows. A boolean/absent `additionalProperties` carries no value
245
+ // type to build an entry editor from, so it falls through to the free-form
246
+ // JSON editor (constrained to a JSON object).
247
+ // Read as `unknown`: the value is parsed JSON, where `additionalProperties:
248
+ // null` is possible and `typeof null === 'object'` would otherwise classify
249
+ // it as a record with a `null` value schema.
250
+ const additional: unknown = resolved.additionalProperties;
251
+ if (typeof additional === 'object' && additional !== null) {
354
252
  return {
355
- model: inner.model,
356
- nullable: inner.nullable,
357
- schema: inner.schema,
358
- title: title ?? inner.title,
359
- description: description ?? inner.description,
253
+ model: { kind: 'record', values: additional as JsonSchema },
254
+ nullable,
255
+ schema: resolved,
256
+ title,
257
+ description,
360
258
  };
361
259
  }
362
- // Whether the intersection accepts `null` is not statically decidable here
363
- // (it does iff EVERY member does), and the json escape hatch only enforces
364
- // what it KNOWS — so stay permissive like `jsonType: 'any'` itself and let
365
- // the server-side contract be the authority, rather than rejecting a null
366
- // the schema may well permit (e.g. an intersection of open schemas).
367
260
  return jsonFallback(
368
261
  resolved,
369
- 'any',
370
- 'allOf intersection of multiple schemas',
262
+ 'object',
263
+ 'free-form object with no property schema',
371
264
  title,
372
265
  description,
373
- true,
266
+ nullable,
374
267
  );
375
268
  }
269
+ const properties: (readonly [string, JsonSchema])[] = Object.entries(resolved.properties);
270
+ const required = new Set(resolved.required ?? []);
271
+ return {
272
+ model: { kind: 'object', properties, required },
273
+ nullable,
274
+ schema: resolved,
275
+ title,
276
+ description,
277
+ };
278
+ }
376
279
 
377
- // Type-driven classification.
280
+ // Type-driven classification: a single non-null JSON type maps to its structured
281
+ // editor; no type, several types, or an unrecognized one lands on the JSON fallback.
282
+ function classifyByType(
283
+ resolved: JsonSchema,
284
+ title: string | undefined,
285
+ description: string | undefined,
286
+ ): ClassifiedField {
378
287
  const types = typeList(resolved);
379
288
  const nonNullTypes = types.filter((type) => type !== 'null');
380
289
  const nullableByType = nonNullTypes.length !== types.length;
381
290
 
382
- if (nonNullTypes.length === 0) {
291
+ const type = nonNullTypes[0];
292
+ if (nonNullTypes.length === 0 || type === undefined) {
383
293
  // No `type` at all (an open `{}` schema, or `type: "null"` alone) accepts any
384
294
  // JSON — the classic "any" field. Edit it as free-form JSON. Nullable is
385
295
  // always true here: `type: "null"` declares it, and an open schema with no
@@ -404,32 +314,9 @@ export function classifySchema(raw: JsonSchema, root: JsonSchema): ClassifiedFie
404
314
  nullableByType,
405
315
  );
406
316
  }
407
-
408
- const type = nonNullTypes[0];
409
- if (type === undefined) {
410
- return jsonFallback(
411
- resolved,
412
- 'any',
413
- 'schema declares no renderable type',
414
- title,
415
- description,
416
- nullableByType,
417
- );
418
- }
419
317
  switch (type) {
420
318
  case 'string':
421
- return {
422
- model: {
423
- kind: 'string',
424
- format: resolved.format,
425
- media: mediaUpload(resolved),
426
- expression: expressionAnnotation(resolved),
427
- },
428
- nullable: nullableByType,
429
- schema: resolved,
430
- title,
431
- description,
432
- };
319
+ return classifyString(resolved, title, description, nullableByType);
433
320
  case 'number':
434
321
  return {
435
322
  model: { kind: 'number', integer: false },
@@ -454,66 +341,10 @@ export function classifySchema(raw: JsonSchema, root: JsonSchema): ClassifiedFie
454
341
  title,
455
342
  description,
456
343
  };
457
- case 'array': {
458
- if (resolved.items === undefined) {
459
- // A bare array with no item schema: no per-item editor to build, but the
460
- // value is still a JSON array — edit it whole as free-form JSON.
461
- return jsonFallback(
462
- resolved,
463
- 'array',
464
- 'array without an items schema',
465
- title,
466
- description,
467
- nullableByType,
468
- );
469
- }
470
- return {
471
- model: { kind: 'array', items: resolved.items },
472
- nullable: nullableByType,
473
- schema: resolved,
474
- title,
475
- description,
476
- };
477
- }
478
- case 'object': {
479
- if (resolved.properties === undefined) {
480
- // A free-form object typed by a VALUE SCHEMA (`additionalProperties` is a
481
- // schema, not `false`/`true`/absent) is a string→X map — renderable as
482
- // key/value rows. A boolean/absent `additionalProperties` carries no value
483
- // type to build an entry editor from, so it falls through to the free-form
484
- // JSON editor (constrained to a JSON object).
485
- // Read as `unknown`: the value is parsed JSON, where `additionalProperties:
486
- // null` is possible and `typeof null === 'object'` would otherwise classify
487
- // it as a record with a `null` value schema.
488
- const additional: unknown = resolved.additionalProperties;
489
- if (typeof additional === 'object' && additional !== null) {
490
- return {
491
- model: { kind: 'record', values: additional as JsonSchema },
492
- nullable: nullableByType,
493
- schema: resolved,
494
- title,
495
- description,
496
- };
497
- }
498
- return jsonFallback(
499
- resolved,
500
- 'object',
501
- 'free-form object with no property schema',
502
- title,
503
- description,
504
- nullableByType,
505
- );
506
- }
507
- const properties: (readonly [string, JsonSchema])[] = Object.entries(resolved.properties);
508
- const required = new Set(resolved.required ?? []);
509
- return {
510
- model: { kind: 'object', properties, required },
511
- nullable: nullableByType,
512
- schema: resolved,
513
- title,
514
- description,
515
- };
516
- }
344
+ case 'array':
345
+ return classifyArray(resolved, title, description, nullableByType);
346
+ case 'object':
347
+ return classifyObject(resolved, title, description, nullableByType);
517
348
  default:
518
349
  return jsonFallback(
519
350
  resolved,
@@ -525,3 +356,21 @@ export function classifySchema(raw: JsonSchema, root: JsonSchema): ClassifiedFie
525
356
  );
526
357
  }
527
358
  }
359
+
360
+ /**
361
+ * Classify a schema node (resolving `$ref` first). `wrapper` metadata (a
362
+ * ref-site title/description) is threaded down so a `{ $ref, title }` or a
363
+ * null-union wrapper keeps its property-level label.
364
+ */
365
+ export function classifySchema(raw: JsonSchema, root: JsonSchema): ClassifiedField {
366
+ const resolved = resolveRef(raw, root);
367
+ const title = firstString(raw.title, resolved.title);
368
+ const description = firstString(raw.description, resolved.description);
369
+
370
+ if ('const' in resolved) return classifyConst(resolved, title, description);
371
+ if (resolved.enum !== undefined) return classifyEnum(resolved, title, description);
372
+ const members = unionMembers(resolved);
373
+ if (members !== undefined) return classifyUnion(resolved, members, root, title, description);
374
+ if (resolved.allOf !== undefined) return classifyAllOf(resolved, root, title, description);
375
+ return classifyByType(resolved, title, description);
376
+ }