@exponential_app/eui 0.0.0-stage → 0.2.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 (272) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +54 -0
  3. package/README.md +377 -2
  4. package/catalog/a11y.json +705 -0
  5. package/catalog/basic-map.json +104 -0
  6. package/catalog/code.json +58 -0
  7. package/catalog/core.catalog.json +5190 -0
  8. package/catalog/core.schema.json +24542 -0
  9. package/catalog/host.json +95 -0
  10. package/catalog/layout.json +20 -0
  11. package/catalog/limits.json +9 -0
  12. package/catalog/locale.json +48 -0
  13. package/catalog/macros.json +4152 -0
  14. package/catalog/recipes.json +64 -0
  15. package/catalog/strings.json +84 -0
  16. package/catalog/style.json +180 -0
  17. package/catalog/theme.schema.json +47754 -0
  18. package/catalog/tokens.json +47 -0
  19. package/conformance/README.md +218 -0
  20. package/conformance/baseline.ts +101 -0
  21. package/conformance/browser-page.ts +146 -0
  22. package/conformance/browser-suites.ts +699 -0
  23. package/conformance/compare.ts +201 -0
  24. package/conformance/dom.ts +97 -0
  25. package/conformance/dump.ts +163 -0
  26. package/conformance/fonts.json +157 -0
  27. package/conformance/manifest.json +233 -0
  28. package/conformance/parity-compare.ts +191 -0
  29. package/conformance/parity-report.schema.json +73 -0
  30. package/conformance/parity.ts +412 -0
  31. package/conformance/pixel.ts +177 -0
  32. package/conformance/primitives-page.ts +49 -0
  33. package/conformance/report.schema.json +66 -0
  34. package/conformance/run.ts +118 -0
  35. package/conformance/voice-wave-child.ts +102 -0
  36. package/dist/chunks/index-5fmaa0xq.js +8384 -0
  37. package/dist/chunks/index-5fmaa0xq.js.map +10 -0
  38. package/dist/chunks/index-62yzr94z.js +2784 -0
  39. package/dist/chunks/index-62yzr94z.js.map +10 -0
  40. package/dist/chunks/index-bg2jsgjq.js +8541 -0
  41. package/dist/chunks/index-bg2jsgjq.js.map +15 -0
  42. package/dist/chunks/index-fy7y2nz8.js +186 -0
  43. package/dist/chunks/index-fy7y2nz8.js.map +9 -0
  44. package/dist/chunks/index-gtk2gwsb.js +4555 -0
  45. package/dist/chunks/index-gtk2gwsb.js.map +31 -0
  46. package/dist/chunks/index-t6qz1770.js +95 -0
  47. package/dist/chunks/index-t6qz1770.js.map +11 -0
  48. package/dist/conformance.js +230462 -0
  49. package/dist/conformance.js.map +10 -0
  50. package/dist/entries/prompt.js +251 -0
  51. package/dist/entries/prompt.js.map +11 -0
  52. package/dist/entries/schema.js +1725 -0
  53. package/dist/entries/schema.js.map +11 -0
  54. package/dist/host/index.js +194 -0
  55. package/dist/host/index.js.map +9 -0
  56. package/dist/index.js +3840 -0
  57. package/dist/index.js.map +29 -0
  58. package/dist/types/src/a11y.d.ts +21 -0
  59. package/dist/types/src/animation.d.ts +76 -0
  60. package/dist/types/src/basic-map.d.ts +38 -0
  61. package/dist/types/src/builder.d.ts +50 -0
  62. package/dist/types/src/catalog.d.ts +54 -0
  63. package/dist/types/src/catalog.described.d.ts +33 -0
  64. package/dist/types/src/catalog.generated.d.ts +904 -0
  65. package/dist/types/src/chart.d.ts +46 -0
  66. package/dist/types/src/code.d.ts +44 -0
  67. package/dist/types/src/color.d.ts +20 -0
  68. package/dist/types/src/conformance.d.ts +39 -0
  69. package/dist/types/src/connector/app.d.ts +14 -0
  70. package/dist/types/src/connector/connector.d.ts +77 -0
  71. package/dist/types/src/connector/index.d.ts +4 -0
  72. package/dist/types/src/css.d.ts +27 -0
  73. package/dist/types/src/define.d.ts +66 -0
  74. package/dist/types/src/direction.d.ts +16 -0
  75. package/dist/types/src/dynamic.d.ts +161 -0
  76. package/dist/types/src/entries/prompt.d.ts +11 -0
  77. package/dist/types/src/entries/schema.d.ts +8 -0
  78. package/dist/types/src/expr.d.ts +71 -0
  79. package/dist/types/src/extension.d.ts +4 -0
  80. package/dist/types/src/format.d.ts +112 -0
  81. package/dist/types/src/geometry.d.ts +40 -0
  82. package/dist/types/src/host/contract.d.ts +225 -0
  83. package/dist/types/src/host/decoders.d.ts +61 -0
  84. package/dist/types/src/host/index.d.ts +15 -0
  85. package/dist/types/src/host/package.d.ts +102 -0
  86. package/dist/types/src/host/policy.d.ts +107 -0
  87. package/dist/types/src/host/record-store.d.ts +103 -0
  88. package/dist/types/src/host/records.d.ts +45 -0
  89. package/dist/types/src/host/router.d.ts +38 -0
  90. package/dist/types/src/host/runtime.d.ts +213 -0
  91. package/dist/types/src/host/signature.d.ts +52 -0
  92. package/dist/types/src/host/sources.d.ts +22 -0
  93. package/dist/types/src/host/tools.d.ts +42 -0
  94. package/dist/types/src/host/transports.d.ts +109 -0
  95. package/dist/types/src/index.d.ts +69 -0
  96. package/dist/types/src/layout.d.ts +20 -0
  97. package/dist/types/src/limits.d.ts +21 -0
  98. package/dist/types/src/list.d.ts +133 -0
  99. package/dist/types/src/locale.d.ts +22 -0
  100. package/dist/types/src/macros.d.ts +29 -0
  101. package/dist/types/src/overlay.d.ts +27 -0
  102. package/dist/types/src/prompt.d.ts +44 -0
  103. package/dist/types/src/qr-code.d.ts +30 -0
  104. package/dist/types/src/recipes.d.ts +17 -0
  105. package/dist/types/src/reducer.d.ts +41 -0
  106. package/dist/types/src/resizable.d.ts +38 -0
  107. package/dist/types/src/schema.d.ts +7 -0
  108. package/dist/types/src/strings.d.ts +13 -0
  109. package/dist/types/src/style.d.ts +201 -0
  110. package/dist/types/src/test-preload.d.ts +1 -0
  111. package/dist/types/src/text-shimmer.d.ts +24 -0
  112. package/dist/types/src/theme-types.d.ts +162 -0
  113. package/dist/types/src/theme.d.ts +75 -0
  114. package/dist/types/src/themes.d.ts +22 -0
  115. package/dist/types/src/tree-guides.d.ts +17 -0
  116. package/dist/types/src/types.d.ts +320 -0
  117. package/dist/types/src/validate.d.ts +31 -0
  118. package/dist/types/src/voice-wave.d.ts +181 -0
  119. package/dist/types/src/web/base-css.d.ts +1 -0
  120. package/dist/types/src/web/box-css.d.ts +63 -0
  121. package/dist/types/src/web/coerce.d.ts +32 -0
  122. package/dist/types/src/web/custom-icons.generated.d.ts +4 -0
  123. package/dist/types/src/web/data.d.ts +107 -0
  124. package/dist/types/src/web/dates.d.ts +12 -0
  125. package/dist/types/src/web/form.d.ts +44 -0
  126. package/dist/types/src/web/host-types.d.ts +126 -0
  127. package/dist/types/src/web/index.d.ts +35 -0
  128. package/dist/types/src/web/inputs.d.ts +55 -0
  129. package/dist/types/src/web/list.d.ts +16 -0
  130. package/dist/types/src/web/listbox.d.ts +14 -0
  131. package/dist/types/src/web/markdown.d.ts +96 -0
  132. package/dist/types/src/web/media.d.ts +63 -0
  133. package/dist/types/src/web/parts.d.ts +4 -0
  134. package/dist/types/src/web/primitives/alert-dialog.d.ts +10 -0
  135. package/dist/types/src/web/primitives/alert.d.ts +9 -0
  136. package/dist/types/src/web/primitives/avatar-color.d.ts +2 -0
  137. package/dist/types/src/web/primitives/avatar.d.ts +13 -0
  138. package/dist/types/src/web/primitives/badge.d.ts +10 -0
  139. package/dist/types/src/web/primitives/breadcrumb.d.ts +10 -0
  140. package/dist/types/src/web/primitives/button.d.ts +6 -0
  141. package/dist/types/src/web/primitives/calendar.d.ts +43 -0
  142. package/dist/types/src/web/primitives/card.d.ts +9 -0
  143. package/dist/types/src/web/primitives/checkbox.d.ts +5 -0
  144. package/dist/types/src/web/primitives/choice-tiles.d.ts +32 -0
  145. package/dist/types/src/web/primitives/cn.d.ts +3 -0
  146. package/dist/types/src/web/primitives/code-block.d.ts +24 -0
  147. package/dist/types/src/web/primitives/command.d.ts +134 -0
  148. package/dist/types/src/web/primitives/context-ring.d.ts +24 -0
  149. package/dist/types/src/web/primitives/dialog-arms.d.ts +6 -0
  150. package/dist/types/src/web/primitives/dialog.d.ts +24 -0
  151. package/dist/types/src/web/primitives/disclosure-header.d.ts +5 -0
  152. package/dist/types/src/web/primitives/dock.d.ts +42 -0
  153. package/dist/types/src/web/primitives/dropdown-menu.d.ts +14 -0
  154. package/dist/types/src/web/primitives/empty-state.d.ts +15 -0
  155. package/dist/types/src/web/primitives/glass-card.d.ts +4 -0
  156. package/dist/types/src/web/primitives/hover-card.d.ts +7 -0
  157. package/dist/types/src/web/primitives/icon-disc.d.ts +12 -0
  158. package/dist/types/src/web/primitives/index.d.ts +50 -0
  159. package/dist/types/src/web/primitives/input.d.ts +5 -0
  160. package/dist/types/src/web/primitives/kbd.d.ts +4 -0
  161. package/dist/types/src/web/primitives/label.d.ts +3 -0
  162. package/dist/types/src/web/primitives/measure.d.ts +16 -0
  163. package/dist/types/src/web/primitives/menu-surface.d.ts +17 -0
  164. package/dist/types/src/web/primitives/meter.d.ts +21 -0
  165. package/dist/types/src/web/primitives/popover.d.ts +3 -0
  166. package/dist/types/src/web/primitives/progress-ring.d.ts +5 -0
  167. package/dist/types/src/web/primitives/progress.d.ts +8 -0
  168. package/dist/types/src/web/primitives/scroll-area.d.ts +49 -0
  169. package/dist/types/src/web/primitives/segmented-control.d.ts +5 -0
  170. package/dist/types/src/web/primitives/select.d.ts +47 -0
  171. package/dist/types/src/web/primitives/separator.d.ts +3 -0
  172. package/dist/types/src/web/primitives/sheet-chrome.d.ts +4 -0
  173. package/dist/types/src/web/primitives/sheet.d.ts +20 -0
  174. package/dist/types/src/web/primitives/skeleton.d.ts +3 -0
  175. package/dist/types/src/web/primitives/spinner.d.ts +3 -0
  176. package/dist/types/src/web/primitives/switch.d.ts +4 -0
  177. package/dist/types/src/web/primitives/table.d.ts +11 -0
  178. package/dist/types/src/web/primitives/tabs.d.ts +13 -0
  179. package/dist/types/src/web/primitives/textarea.d.ts +3 -0
  180. package/dist/types/src/web/primitives/toast.d.ts +163 -0
  181. package/dist/types/src/web/primitives/tooltip.d.ts +3 -0
  182. package/dist/types/src/web/primitives/truncate.d.ts +7 -0
  183. package/dist/types/src/web/primitives/typeahead-portal.d.ts +12 -0
  184. package/dist/types/src/web/primitives/voice-wave.d.ts +50 -0
  185. package/dist/types/src/web/round5.d.ts +107 -0
  186. package/dist/types/src/web/share.d.ts +6 -0
  187. package/dist/types/src/web/table.d.ts +20 -0
  188. package/dist/types/src/web/theme-css.d.ts +69 -0
  189. package/dist/types/src/web/urls.d.ts +21 -0
  190. package/dist/web/index.js +2565 -0
  191. package/dist/web/index.js.map +28 -0
  192. package/dist/web/primitives/index.js +1476 -0
  193. package/dist/web/primitives/index.js.map +57 -0
  194. package/docs/components.generated.json +6783 -0
  195. package/docs/package-format.md +182 -0
  196. package/docs/round-1-contract.md +679 -0
  197. package/docs/round-2-contract.md +476 -0
  198. package/docs/round-3-contract.md +174 -0
  199. package/docs/round-5-contract.md +268 -0
  200. package/docs/themes.generated.json +3647 -0
  201. package/fixtures/animations.json +2890 -0
  202. package/fixtures/bench-list.json +58 -0
  203. package/fixtures/bind-time.json +54734 -0
  204. package/fixtures/catalog-basic-map.json +1692 -0
  205. package/fixtures/catalog-components.json +10293 -0
  206. package/fixtures/catalog-extension.json +620 -0
  207. package/fixtures/catalog-macros.json +51805 -0
  208. package/fixtures/code-tokens.json +1993 -0
  209. package/fixtures/conformance-baseline.json +59016 -0
  210. package/fixtures/conformance-cases.json +49 -0
  211. package/fixtures/conformance-known.json +2111 -0
  212. package/fixtures/control-geometry.json +5129 -0
  213. package/fixtures/dashboard.data.json +74 -0
  214. package/fixtures/dashboard.json +781 -0
  215. package/fixtures/demo-surface.json +47 -0
  216. package/fixtures/format.json +870 -0
  217. package/fixtures/host-package.json +1262 -0
  218. package/fixtures/host-policy.json +1209 -0
  219. package/fixtures/host-router.json +2034 -0
  220. package/fixtures/host-transport.json +590 -0
  221. package/fixtures/interactions-round1.json +1058 -0
  222. package/fixtures/kitchen-sink.expanded.json +11972 -0
  223. package/fixtures/kitchen-sink.json +3140 -0
  224. package/fixtures/layout-geometry-round1.json +7278 -0
  225. package/fixtures/layout-geometry.json +2072 -0
  226. package/fixtures/overlay-geometry.json +272 -0
  227. package/fixtures/pointer.json +89 -0
  228. package/fixtures/primitive-specimens.json +59 -0
  229. package/fixtures/prompt-budget.json +33 -0
  230. package/fixtures/qr-code.json +480 -0
  231. package/fixtures/resizable.json +714 -0
  232. package/fixtures/round4-contract.json +2985 -0
  233. package/fixtures/specimens.json +11168 -0
  234. package/fixtures/style-conditions.json +336 -0
  235. package/fixtures/template-items.json +2215 -0
  236. package/fixtures/text-direction.json +309 -0
  237. package/fixtures/text-shimmer.json +96 -0
  238. package/fixtures/theme-extends.json +573 -0
  239. package/fixtures/theme-invalid.json +1300 -0
  240. package/fixtures/theme-recipes.json +61762 -0
  241. package/fixtures/theme-resolved.json +25969 -0
  242. package/fixtures/toast-stack.json +215 -0
  243. package/fixtures/tree-guides.json +224 -0
  244. package/fixtures/virtual-list.json +733 -0
  245. package/fixtures/voice-wave.json +19236 -0
  246. package/generated/ExponentialUICatalog.generated.kt +67 -0
  247. package/generated/ExponentialUICatalog.generated.swift +67 -0
  248. package/generated/ExponentialUIThemes.generated.kt +23 -0
  249. package/generated/ExponentialUIThemes.generated.swift +23 -0
  250. package/generated/catalog.descriptions.generated.rs +6 -0
  251. package/generated/catalog.generated.rs +80 -0
  252. package/generated/catalog.themes.generated.rs +34 -0
  253. package/generated/core.descriptions.json +2726 -0
  254. package/generated/core.runtime.json +3944 -0
  255. package/package.json +87 -4
  256. package/themes/exponential.recipes.json +49 -0
  257. package/themes/exponential.theme.json +768 -0
  258. package/themes/neutral.theme.json +6178 -0
  259. package/themes/orange.theme.json +43 -0
  260. package/themes/playful.theme.json +219 -0
  261. package/vendor/a2ui/LICENSE.txt +203 -0
  262. package/vendor/a2ui/README.md +27 -0
  263. package/vendor/a2ui/v0_9/basic_catalog.json +1387 -0
  264. package/vendor/a2ui/v0_9/basic_catalog_rules.txt +5 -0
  265. package/vendor/a2ui/v0_9/client_capabilities.json +97 -0
  266. package/vendor/a2ui/v0_9/client_to_server.json +104 -0
  267. package/vendor/a2ui/v0_9/common_types.json +315 -0
  268. package/vendor/a2ui/v0_9/server_to_client.json +132 -0
  269. package/vendor/json-render/LICENSE.txt +201 -0
  270. package/vendor/json-render/README.md +13 -0
  271. package/vendor/nayuki-qr/LICENSE.txt +20 -0
  272. package/vendor/nayuki-qr/README.md +10 -0
@@ -0,0 +1,2034 @@
1
+ {
2
+ "$comment": "VAPP-91: the host router (catalog/host.json ops): server messages in, ops out, in order. Inputs hand-written; `expected` filled by the generator from src/host/router.ts. A conformance runner creates ONE router per flow with `extensionIds`, installs `packages` (their install issues are `installIssues`), then routes every step's message and compares the ops. VAPP-122: `records` = validateRecord(packages[package].records[kind].schema, data) → issues; `acme.hours` declares two kinds, `acme.badrecords` every schema mistake. Keys of `records` and of every `properties` are written in sorted order: the Rust core iterates its maps sorted, so the issue order is the same on every platform. VAPP-161 (format version 2): `acme.v2` uses every new key validly (formatVersion, a records: binding, the records.* functions, the three tool transports, an empty extensions list); `acme.charts` declares an extension: not available under the default catalogs, installed by the VAPP-161 flow that registers it; `acme.v2broken` makes every tools / extensions / records-source mistake; `acme.future` is a newer format version; `acme.v1` is a version-1 package that stays valid.",
3
+ "packages": {
4
+ "acme.devices": {
5
+ "id": "acme.devices",
6
+ "name": "Devices",
7
+ "version": "1.0.0",
8
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1",
9
+ "templates": {
10
+ "list": {
11
+ "components": [
12
+ {
13
+ "id": "root",
14
+ "component": "Column",
15
+ "children": [
16
+ "title"
17
+ ]
18
+ },
19
+ {
20
+ "id": "title",
21
+ "component": "Text",
22
+ "text": {
23
+ "path": "/title"
24
+ }
25
+ }
26
+ ],
27
+ "data": {
28
+ "title": "Devices",
29
+ "filter": "all"
30
+ },
31
+ "bindings": [
32
+ {
33
+ "path": "/devices",
34
+ "source": "exp:devices"
35
+ }
36
+ ]
37
+ }
38
+ },
39
+ "functions": [
40
+ "harness.toast"
41
+ ]
42
+ },
43
+ "broken": {
44
+ "id": "broken",
45
+ "name": "",
46
+ "version": "1",
47
+ "catalogId": "https://other.example/v9",
48
+ "templates": {
49
+ "t": {
50
+ "components": [
51
+ {
52
+ "id": "a",
53
+ "component": "Text"
54
+ },
55
+ {
56
+ "id": "a",
57
+ "component": "Text"
58
+ },
59
+ {
60
+ "component": "Text"
61
+ }
62
+ ],
63
+ "bindings": [
64
+ {
65
+ "path": "no-slash",
66
+ "source": "nope"
67
+ }
68
+ ]
69
+ }
70
+ },
71
+ "functions": [
72
+ "",
73
+ 3
74
+ ],
75
+ "sources": [
76
+ "",
77
+ 3
78
+ ]
79
+ },
80
+ "acme.scoped": {
81
+ "id": "acme.scoped",
82
+ "name": "Scoped",
83
+ "version": "1.0.0",
84
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1",
85
+ "templates": {
86
+ "list": {
87
+ "components": [
88
+ {
89
+ "id": "root",
90
+ "component": "Text",
91
+ "text": {
92
+ "path": "/devices/count"
93
+ }
94
+ }
95
+ ],
96
+ "bindings": [
97
+ {
98
+ "path": "/devices",
99
+ "source": "exp:devices"
100
+ }
101
+ ]
102
+ }
103
+ },
104
+ "functions": [
105
+ "harness.toast"
106
+ ],
107
+ "sources": [
108
+ "exp:devices",
109
+ "acme.*"
110
+ ]
111
+ },
112
+ "acme.leaky": {
113
+ "id": "acme.leaky",
114
+ "name": "Leaky",
115
+ "version": "1.0.0",
116
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1",
117
+ "templates": {
118
+ "list": {
119
+ "components": [
120
+ {
121
+ "id": "root",
122
+ "component": "Text"
123
+ }
124
+ ],
125
+ "bindings": [
126
+ {
127
+ "path": "/devices",
128
+ "source": "exp:devices"
129
+ },
130
+ {
131
+ "path": "/issues",
132
+ "source": "EXP:issues?board=b1"
133
+ }
134
+ ]
135
+ }
136
+ },
137
+ "sources": [
138
+ "exp:devices"
139
+ ]
140
+ },
141
+ "acme.hours": {
142
+ "id": "acme.hours",
143
+ "name": "Working hours",
144
+ "version": "1.0.0",
145
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1",
146
+ "templates": {
147
+ "month": {
148
+ "components": [
149
+ {
150
+ "id": "root",
151
+ "component": "Column",
152
+ "children": [
153
+ "title"
154
+ ]
155
+ },
156
+ {
157
+ "id": "title",
158
+ "component": "Text",
159
+ "text": {
160
+ "path": "/entries/month/label"
161
+ }
162
+ }
163
+ ],
164
+ "data": {
165
+ "entries": {
166
+ "rows": [],
167
+ "count": 0
168
+ }
169
+ },
170
+ "bindings": [
171
+ {
172
+ "path": "/entries",
173
+ "source": "exp:records?kind=entry&mine=1&by=date&sum=hours"
174
+ }
175
+ ]
176
+ }
177
+ },
178
+ "functions": [
179
+ "harness.records.*",
180
+ "bind"
181
+ ],
182
+ "sources": [
183
+ "exp:records"
184
+ ],
185
+ "records": {
186
+ "entry": {
187
+ "title": "Hours entry",
188
+ "schema": {
189
+ "type": "object",
190
+ "properties": {
191
+ "date": {
192
+ "type": "string",
193
+ "format": "date"
194
+ },
195
+ "hours": {
196
+ "type": "number",
197
+ "minimum": 0,
198
+ "maximum": 24
199
+ },
200
+ "issues": {
201
+ "type": "array",
202
+ "items": {
203
+ "type": "string"
204
+ },
205
+ "maxLength": 20
206
+ },
207
+ "kind": {
208
+ "type": "string",
209
+ "enum": [
210
+ "work",
211
+ "meeting",
212
+ "support"
213
+ ]
214
+ },
215
+ "note": {
216
+ "type": "string",
217
+ "maxLength": 500
218
+ }
219
+ },
220
+ "required": [
221
+ "date",
222
+ "hours"
223
+ ]
224
+ }
225
+ },
226
+ "target": {
227
+ "schema": {
228
+ "type": "object",
229
+ "properties": {
230
+ "hours": {
231
+ "type": "integer",
232
+ "minimum": 0
233
+ },
234
+ "month": {
235
+ "type": "string"
236
+ }
237
+ },
238
+ "required": [
239
+ "month",
240
+ "hours"
241
+ ],
242
+ "additionalProperties": true
243
+ }
244
+ }
245
+ }
246
+ },
247
+ "acme.badrecords": {
248
+ "id": "acme.badrecords",
249
+ "name": "Bad records",
250
+ "version": "1.0.0",
251
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1",
252
+ "templates": {
253
+ "t": {
254
+ "components": [
255
+ {
256
+ "id": "root",
257
+ "component": "Column"
258
+ }
259
+ ]
260
+ }
261
+ },
262
+ "records": {
263
+ "Entry": {
264
+ "schema": {
265
+ "type": "object",
266
+ "properties": {
267
+ "id": {
268
+ "type": "string"
269
+ },
270
+ "mood": {
271
+ "type": "number",
272
+ "enum": [
273
+ "a"
274
+ ]
275
+ },
276
+ "name": {
277
+ "type": "string",
278
+ "items": {
279
+ "type": "string"
280
+ },
281
+ "format": "email"
282
+ },
283
+ "size": {
284
+ "type": "number",
285
+ "maximum": "big"
286
+ },
287
+ "tags": {
288
+ "type": "array"
289
+ },
290
+ "when": {
291
+ "type": "datetime"
292
+ }
293
+ },
294
+ "required": [
295
+ "name",
296
+ "missing"
297
+ ],
298
+ "additionalProperties": "no"
299
+ }
300
+ },
301
+ "list": "nope",
302
+ "ok_kind": {
303
+ "title": "No schema"
304
+ }
305
+ }
306
+ },
307
+ "acme.v2": {
308
+ "formatVersion": 2,
309
+ "id": "acme.v2",
310
+ "name": "Hours v2",
311
+ "version": "2.0.0",
312
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1",
313
+ "templates": {
314
+ "list": {
315
+ "components": [
316
+ {
317
+ "id": "root",
318
+ "component": "Text",
319
+ "text": {
320
+ "path": "/entries/count"
321
+ }
322
+ }
323
+ ],
324
+ "bindings": [
325
+ {
326
+ "path": "/entries",
327
+ "source": "records:entry?mine=1&sort=date"
328
+ }
329
+ ]
330
+ }
331
+ },
332
+ "functions": [
333
+ "records.*",
334
+ "tools.weather.*",
335
+ "tools.lint",
336
+ "tools.files.read"
337
+ ],
338
+ "sources": [
339
+ "records:*"
340
+ ],
341
+ "records": {
342
+ "entry": {
343
+ "schema": {
344
+ "type": "object",
345
+ "properties": {
346
+ "date": {
347
+ "type": "string",
348
+ "format": "date"
349
+ },
350
+ "hours": {
351
+ "type": "number"
352
+ }
353
+ },
354
+ "required": [
355
+ "date"
356
+ ]
357
+ }
358
+ }
359
+ },
360
+ "tools": {
361
+ "files": {
362
+ "kind": "mcp",
363
+ "title": "Files",
364
+ "command": "npx",
365
+ "args": [
366
+ "-y",
367
+ "@modelcontextprotocol/server-filesystem",
368
+ "/tmp/my notes"
369
+ ]
370
+ },
371
+ "lint": {
372
+ "kind": "script",
373
+ "title": "Lint",
374
+ "description": "Checks an entry",
375
+ "command": "bun",
376
+ "args": [
377
+ "lint.ts"
378
+ ],
379
+ "env": [
380
+ "LINT_LEVEL"
381
+ ]
382
+ },
383
+ "weather": {
384
+ "kind": "mcp",
385
+ "title": "Weather",
386
+ "url": "https://mcp.example.com/weather"
387
+ }
388
+ },
389
+ "extensions": []
390
+ },
391
+ "acme.v2broken": {
392
+ "formatVersion": 0,
393
+ "id": "acme.v2broken",
394
+ "name": "Broken v2",
395
+ "version": "1",
396
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1",
397
+ "templates": {
398
+ "t": {
399
+ "components": [
400
+ {
401
+ "id": "root",
402
+ "component": "Text"
403
+ }
404
+ ],
405
+ "bindings": [
406
+ {
407
+ "path": "/a",
408
+ "source": "records:missing"
409
+ },
410
+ {
411
+ "path": "/b",
412
+ "source": "records:entry"
413
+ }
414
+ ]
415
+ }
416
+ },
417
+ "records": {
418
+ "entry": {
419
+ "schema": {
420
+ "type": "object",
421
+ "properties": {
422
+ "n": {
423
+ "type": "number"
424
+ }
425
+ }
426
+ }
427
+ }
428
+ },
429
+ "tools": {
430
+ "Bad": {
431
+ "kind": "mcp",
432
+ "title": "x",
433
+ "url": "https://a.example"
434
+ },
435
+ "both": {
436
+ "kind": "mcp",
437
+ "title": "Both",
438
+ "url": "https://a.example",
439
+ "command": "x"
440
+ },
441
+ "neither": {
442
+ "kind": "mcp",
443
+ "title": "Neither"
444
+ },
445
+ "notool": 3,
446
+ "rel": {
447
+ "kind": "mcp",
448
+ "title": "Relative",
449
+ "url": "/mcp",
450
+ "args": [
451
+ "a"
452
+ ]
453
+ },
454
+ "script": {
455
+ "kind": "script",
456
+ "title": "",
457
+ "url": "https://a.example",
458
+ "env": [
459
+ "OK",
460
+ "1BAD"
461
+ ]
462
+ },
463
+ "weird": {
464
+ "kind": "plugin",
465
+ "title": "Weird",
466
+ "description": 5,
467
+ "command": " ",
468
+ "args": "a b"
469
+ }
470
+ },
471
+ "extensions": [
472
+ {
473
+ "id": "https://acme.example/catalogs/charts/v1",
474
+ "version": "1.0"
475
+ },
476
+ {
477
+ "id": "",
478
+ "version": "1.0.0",
479
+ "components": [
480
+ "Gantt",
481
+ ""
482
+ ]
483
+ },
484
+ {
485
+ "id": "https://ui.exponential.at/catalogs/core/v1",
486
+ "version": "1.0.0"
487
+ },
488
+ {
489
+ "id": "https://ui.exponential.at/catalogs/core/v1",
490
+ "version": "1.0.0"
491
+ },
492
+ "nope"
493
+ ]
494
+ },
495
+ "acme.future": {
496
+ "formatVersion": 3,
497
+ "id": "acme.future",
498
+ "name": "Future",
499
+ "version": "1.0.0",
500
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1",
501
+ "templates": {
502
+ "t": {
503
+ "components": [
504
+ {
505
+ "id": "root",
506
+ "component": "Text",
507
+ "text": "Hi"
508
+ }
509
+ ]
510
+ }
511
+ }
512
+ },
513
+ "acme.v1": {
514
+ "id": "acme.v1",
515
+ "name": "Plain",
516
+ "version": "1.0.0",
517
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1",
518
+ "templates": {
519
+ "t": {
520
+ "components": [
521
+ {
522
+ "id": "root",
523
+ "component": "Text",
524
+ "text": "Hi"
525
+ }
526
+ ]
527
+ }
528
+ },
529
+ "functions": [
530
+ "harness.toast"
531
+ ]
532
+ },
533
+ "acme.charts": {
534
+ "formatVersion": 2,
535
+ "id": "acme.charts",
536
+ "name": "Charts",
537
+ "version": "1.0.0",
538
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1",
539
+ "templates": {
540
+ "t": {
541
+ "components": [
542
+ {
543
+ "id": "root",
544
+ "component": "Text",
545
+ "text": "Hi"
546
+ }
547
+ ]
548
+ }
549
+ },
550
+ "extensions": [
551
+ {
552
+ "id": "https://acme.example/catalogs/charts/v1",
553
+ "version": "1.4.0",
554
+ "components": [
555
+ "Gantt"
556
+ ]
557
+ }
558
+ ]
559
+ }
560
+ },
561
+ "flows": [
562
+ {
563
+ "name": "the A2UI lifecycle",
564
+ "steps": [
565
+ {
566
+ "message": {
567
+ "version": "v0.9",
568
+ "createSurface": {
569
+ "surfaceId": "s1",
570
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
571
+ }
572
+ },
573
+ "expected": [
574
+ {
575
+ "op": "create",
576
+ "surfaceId": "s1",
577
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
578
+ }
579
+ ]
580
+ },
581
+ {
582
+ "message": {
583
+ "version": "v0.9",
584
+ "updateComponents": {
585
+ "surfaceId": "s1",
586
+ "components": [
587
+ {
588
+ "id": "root",
589
+ "component": "Text",
590
+ "text": "Hi"
591
+ }
592
+ ]
593
+ }
594
+ },
595
+ "expected": [
596
+ {
597
+ "op": "components",
598
+ "surfaceId": "s1",
599
+ "components": [
600
+ {
601
+ "id": "root",
602
+ "component": "Text",
603
+ "text": "Hi"
604
+ }
605
+ ]
606
+ }
607
+ ]
608
+ },
609
+ {
610
+ "message": {
611
+ "version": "v0.9",
612
+ "updateDataModel": {
613
+ "surfaceId": "s1",
614
+ "path": "/user/name",
615
+ "value": "Ada"
616
+ }
617
+ },
618
+ "expected": [
619
+ {
620
+ "op": "data",
621
+ "surfaceId": "s1",
622
+ "path": "/user/name",
623
+ "value": "Ada"
624
+ }
625
+ ]
626
+ },
627
+ {
628
+ "message": {
629
+ "version": "v0.9",
630
+ "updateDataModel": {
631
+ "surfaceId": "s1",
632
+ "path": "/user/name"
633
+ }
634
+ },
635
+ "expected": [
636
+ {
637
+ "op": "data",
638
+ "surfaceId": "s1",
639
+ "path": "/user/name"
640
+ }
641
+ ]
642
+ },
643
+ {
644
+ "message": {
645
+ "version": "v0.9",
646
+ "updateDataModel": {
647
+ "surfaceId": "s1",
648
+ "value": {
649
+ "a": 1
650
+ }
651
+ }
652
+ },
653
+ "expected": [
654
+ {
655
+ "op": "data",
656
+ "surfaceId": "s1",
657
+ "path": "",
658
+ "value": {
659
+ "a": 1
660
+ }
661
+ }
662
+ ]
663
+ },
664
+ {
665
+ "message": {
666
+ "version": "v0.9",
667
+ "updateDataModel": {
668
+ "surfaceId": "s1",
669
+ "path": "/",
670
+ "value": {
671
+ "b": 2
672
+ }
673
+ }
674
+ },
675
+ "expected": [
676
+ {
677
+ "op": "data",
678
+ "surfaceId": "s1",
679
+ "path": "",
680
+ "value": {
681
+ "b": 2
682
+ }
683
+ }
684
+ ]
685
+ },
686
+ {
687
+ "message": {
688
+ "version": "v0.9",
689
+ "deleteSurface": {
690
+ "surfaceId": "s1"
691
+ }
692
+ },
693
+ "expected": [
694
+ {
695
+ "op": "delete",
696
+ "surfaceId": "s1"
697
+ }
698
+ ]
699
+ },
700
+ {
701
+ "message": {
702
+ "version": "v0.9",
703
+ "deleteSurface": {
704
+ "surfaceId": "s1"
705
+ }
706
+ },
707
+ "expected": [
708
+ {
709
+ "op": "send",
710
+ "message": {
711
+ "version": "v0.9",
712
+ "error": {
713
+ "code": "SURFACE_NOT_FOUND",
714
+ "surfaceId": "s1",
715
+ "message": "no surface s1; send createSurface first"
716
+ }
717
+ }
718
+ }
719
+ ]
720
+ }
721
+ ]
722
+ },
723
+ {
724
+ "name": "createSurface keeps theme and sendDataModel",
725
+ "steps": [
726
+ {
727
+ "message": {
728
+ "version": "v0.9",
729
+ "createSurface": {
730
+ "surfaceId": "s2",
731
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1",
732
+ "theme": {
733
+ "primaryColor": "#ff0000"
734
+ },
735
+ "sendDataModel": true
736
+ }
737
+ },
738
+ "expected": [
739
+ {
740
+ "op": "create",
741
+ "surfaceId": "s2",
742
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1",
743
+ "theme": {
744
+ "primaryColor": "#ff0000"
745
+ },
746
+ "sendDataModel": true
747
+ }
748
+ ]
749
+ },
750
+ {
751
+ "message": {
752
+ "createSurface": {
753
+ "surfaceId": "s3",
754
+ "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json",
755
+ "sendDataModel": false
756
+ }
757
+ },
758
+ "expected": [
759
+ {
760
+ "op": "create",
761
+ "surfaceId": "s3",
762
+ "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json"
763
+ }
764
+ ]
765
+ }
766
+ ]
767
+ },
768
+ {
769
+ "name": "errors",
770
+ "steps": [
771
+ {
772
+ "message": {
773
+ "version": "v0.9",
774
+ "updateComponents": {
775
+ "surfaceId": "ghost",
776
+ "components": [
777
+ {
778
+ "id": "root",
779
+ "component": "Text",
780
+ "text": "Hi"
781
+ }
782
+ ]
783
+ }
784
+ },
785
+ "expected": [
786
+ {
787
+ "op": "send",
788
+ "message": {
789
+ "version": "v0.9",
790
+ "error": {
791
+ "code": "SURFACE_NOT_FOUND",
792
+ "surfaceId": "ghost",
793
+ "message": "no surface ghost; send createSurface first"
794
+ }
795
+ }
796
+ }
797
+ ]
798
+ },
799
+ {
800
+ "message": {
801
+ "version": "v0.9",
802
+ "updateDataModel": {
803
+ "surfaceId": "ghost",
804
+ "value": 1
805
+ }
806
+ },
807
+ "expected": [
808
+ {
809
+ "op": "send",
810
+ "message": {
811
+ "version": "v0.9",
812
+ "error": {
813
+ "code": "SURFACE_NOT_FOUND",
814
+ "surfaceId": "ghost",
815
+ "message": "no surface ghost; send createSurface first"
816
+ }
817
+ }
818
+ }
819
+ ]
820
+ },
821
+ {
822
+ "message": {
823
+ "version": "v0.9",
824
+ "createSurface": {
825
+ "surfaceId": "s1",
826
+ "catalogId": "https://ui.exponential.at/catalogs/core/v2"
827
+ }
828
+ },
829
+ "expected": [
830
+ {
831
+ "op": "send",
832
+ "message": {
833
+ "version": "v0.9",
834
+ "error": {
835
+ "code": "UNSUPPORTED_CATALOG",
836
+ "surfaceId": "s1",
837
+ "message": "catalog https://ui.exponential.at/catalogs/core/v2 is not supported"
838
+ }
839
+ }
840
+ }
841
+ ]
842
+ },
843
+ {
844
+ "message": {
845
+ "version": "v0.8",
846
+ "createSurface": {
847
+ "surfaceId": "s1",
848
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
849
+ }
850
+ },
851
+ "expected": [
852
+ {
853
+ "op": "send",
854
+ "message": {
855
+ "version": "v0.9",
856
+ "error": {
857
+ "code": "INVALID_MESSAGE",
858
+ "surfaceId": "s1",
859
+ "message": "unsupported version v0.8"
860
+ }
861
+ }
862
+ }
863
+ ]
864
+ },
865
+ {
866
+ "message": {
867
+ "version": "v0.9"
868
+ },
869
+ "expected": [
870
+ {
871
+ "op": "send",
872
+ "message": {
873
+ "version": "v0.9",
874
+ "error": {
875
+ "code": "INVALID_MESSAGE",
876
+ "surfaceId": "",
877
+ "message": "expected exactly one of createSurface, updateComponents, updateDataModel, deleteSurface, applyTemplate, bindDataModel"
878
+ }
879
+ }
880
+ }
881
+ ]
882
+ },
883
+ {
884
+ "message": {
885
+ "version": "v0.9",
886
+ "createSurface": {
887
+ "surfaceId": "s1",
888
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
889
+ },
890
+ "deleteSurface": {
891
+ "surfaceId": "s1"
892
+ }
893
+ },
894
+ "expected": [
895
+ {
896
+ "op": "send",
897
+ "message": {
898
+ "version": "v0.9",
899
+ "error": {
900
+ "code": "INVALID_MESSAGE",
901
+ "surfaceId": "s1",
902
+ "message": "expected exactly one of createSurface, updateComponents, updateDataModel, deleteSurface, applyTemplate, bindDataModel"
903
+ }
904
+ }
905
+ }
906
+ ]
907
+ },
908
+ {
909
+ "message": {
910
+ "version": "v0.9",
911
+ "beginRendering": {
912
+ "surfaceId": "s1"
913
+ }
914
+ },
915
+ "expected": [
916
+ {
917
+ "op": "send",
918
+ "message": {
919
+ "version": "v0.9",
920
+ "error": {
921
+ "code": "INVALID_MESSAGE",
922
+ "surfaceId": "s1",
923
+ "message": "expected exactly one of createSurface, updateComponents, updateDataModel, deleteSurface, applyTemplate, bindDataModel"
924
+ }
925
+ }
926
+ }
927
+ ]
928
+ },
929
+ {
930
+ "message": {
931
+ "version": "v0.9",
932
+ "createSurface": {
933
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
934
+ }
935
+ },
936
+ "expected": [
937
+ {
938
+ "op": "send",
939
+ "message": {
940
+ "version": "v0.9",
941
+ "error": {
942
+ "code": "INVALID_MESSAGE",
943
+ "surfaceId": "",
944
+ "message": "createSurface.surfaceId is required"
945
+ }
946
+ }
947
+ }
948
+ ]
949
+ },
950
+ {
951
+ "message": [
952
+ 1,
953
+ 2
954
+ ],
955
+ "expected": [
956
+ {
957
+ "op": "send",
958
+ "message": {
959
+ "version": "v0.9",
960
+ "error": {
961
+ "code": "INVALID_MESSAGE",
962
+ "surfaceId": "",
963
+ "message": "a message is a JSON object"
964
+ }
965
+ }
966
+ }
967
+ ]
968
+ },
969
+ {
970
+ "message": {
971
+ "version": "v0.9",
972
+ "createSurface": {
973
+ "surfaceId": "s1",
974
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
975
+ }
976
+ },
977
+ "expected": [
978
+ {
979
+ "op": "create",
980
+ "surfaceId": "s1",
981
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
982
+ }
983
+ ]
984
+ },
985
+ {
986
+ "message": {
987
+ "version": "v0.9",
988
+ "updateComponents": {
989
+ "surfaceId": "s1"
990
+ }
991
+ },
992
+ "expected": [
993
+ {
994
+ "op": "send",
995
+ "message": {
996
+ "version": "v0.9",
997
+ "error": {
998
+ "code": "INVALID_MESSAGE",
999
+ "surfaceId": "s1",
1000
+ "message": "updateComponents.components is required"
1001
+ }
1002
+ }
1003
+ }
1004
+ ]
1005
+ },
1006
+ {
1007
+ "message": {
1008
+ "version": "v0.9",
1009
+ "updateDataModel": {
1010
+ "surfaceId": "s1",
1011
+ "path": 3
1012
+ }
1013
+ },
1014
+ "expected": [
1015
+ {
1016
+ "op": "send",
1017
+ "message": {
1018
+ "version": "v0.9",
1019
+ "error": {
1020
+ "code": "INVALID_MESSAGE",
1021
+ "surfaceId": "s1",
1022
+ "message": "updateDataModel.path is a string"
1023
+ }
1024
+ }
1025
+ }
1026
+ ]
1027
+ }
1028
+ ]
1029
+ },
1030
+ {
1031
+ "name": "extension catalogs negotiate",
1032
+ "extensionIds": [
1033
+ "https://acme.example/catalog/v1"
1034
+ ],
1035
+ "steps": [
1036
+ {
1037
+ "message": {
1038
+ "version": "v0.9",
1039
+ "createSurface": {
1040
+ "surfaceId": "x",
1041
+ "catalogId": "https://acme.example/catalog/v1"
1042
+ }
1043
+ },
1044
+ "expected": [
1045
+ {
1046
+ "op": "create",
1047
+ "surfaceId": "x",
1048
+ "catalogId": "https://acme.example/catalog/v1"
1049
+ }
1050
+ ]
1051
+ },
1052
+ {
1053
+ "message": {
1054
+ "version": "v0.9",
1055
+ "createSurface": {
1056
+ "surfaceId": "y",
1057
+ "catalogId": "https://other.example/catalog/v1"
1058
+ }
1059
+ },
1060
+ "expected": [
1061
+ {
1062
+ "op": "send",
1063
+ "message": {
1064
+ "version": "v0.9",
1065
+ "error": {
1066
+ "code": "UNSUPPORTED_CATALOG",
1067
+ "surfaceId": "y",
1068
+ "message": "catalog https://other.example/catalog/v1 is not supported"
1069
+ }
1070
+ }
1071
+ }
1072
+ ]
1073
+ }
1074
+ ]
1075
+ },
1076
+ {
1077
+ "name": "bindDataModel",
1078
+ "steps": [
1079
+ {
1080
+ "message": {
1081
+ "version": "v0.9",
1082
+ "createSurface": {
1083
+ "surfaceId": "s1",
1084
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
1085
+ }
1086
+ },
1087
+ "expected": [
1088
+ {
1089
+ "op": "create",
1090
+ "surfaceId": "s1",
1091
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
1092
+ }
1093
+ ]
1094
+ },
1095
+ {
1096
+ "message": {
1097
+ "version": "v0.9",
1098
+ "bindDataModel": {
1099
+ "surfaceId": "s1",
1100
+ "path": "/devices",
1101
+ "source": "exp:devices?team=t1"
1102
+ }
1103
+ },
1104
+ "expected": [
1105
+ {
1106
+ "op": "bind",
1107
+ "surfaceId": "s1",
1108
+ "path": "/devices",
1109
+ "source": "exp:devices?team=t1"
1110
+ }
1111
+ ]
1112
+ },
1113
+ {
1114
+ "message": {
1115
+ "version": "v0.9",
1116
+ "bindDataModel": {
1117
+ "surfaceId": "s1",
1118
+ "path": "/x",
1119
+ "source": "not a source"
1120
+ }
1121
+ },
1122
+ "expected": [
1123
+ {
1124
+ "op": "send",
1125
+ "message": {
1126
+ "version": "v0.9",
1127
+ "error": {
1128
+ "code": "INVALID_MESSAGE",
1129
+ "surfaceId": "s1",
1130
+ "message": "bindDataModel needs a path and a scheme:name source"
1131
+ }
1132
+ }
1133
+ }
1134
+ ]
1135
+ },
1136
+ {
1137
+ "message": {
1138
+ "version": "v0.9",
1139
+ "bindDataModel": {
1140
+ "surfaceId": "nope",
1141
+ "path": "/x",
1142
+ "source": "exp:devices"
1143
+ }
1144
+ },
1145
+ "expected": [
1146
+ {
1147
+ "op": "send",
1148
+ "message": {
1149
+ "version": "v0.9",
1150
+ "error": {
1151
+ "code": "SURFACE_NOT_FOUND",
1152
+ "surfaceId": "nope",
1153
+ "message": "no surface nope; send createSurface first"
1154
+ }
1155
+ }
1156
+ }
1157
+ ]
1158
+ }
1159
+ ]
1160
+ },
1161
+ {
1162
+ "name": "applyTemplate",
1163
+ "packages": [
1164
+ "acme.devices",
1165
+ "broken"
1166
+ ],
1167
+ "installIssues": {
1168
+ "acme.devices": [],
1169
+ "broken": [
1170
+ {
1171
+ "path": "/name",
1172
+ "message": "name is a required string"
1173
+ },
1174
+ {
1175
+ "path": "/catalogId",
1176
+ "message": "unsupported catalog https://other.example/v9"
1177
+ },
1178
+ {
1179
+ "path": "/templates/t/components/1/id",
1180
+ "message": "duplicate id a"
1181
+ },
1182
+ {
1183
+ "path": "/templates/t/components/2",
1184
+ "message": "a component needs an id and a component"
1185
+ },
1186
+ {
1187
+ "path": "/templates/t/components",
1188
+ "message": "no component has the id root"
1189
+ },
1190
+ {
1191
+ "path": "/templates/t/bindings/0/path",
1192
+ "message": "path is a JSON pointer"
1193
+ },
1194
+ {
1195
+ "path": "/templates/t/bindings/0/source",
1196
+ "message": "source is a scheme:name URI"
1197
+ },
1198
+ {
1199
+ "path": "/functions",
1200
+ "message": "functions is a list of names or prefix* patterns"
1201
+ },
1202
+ {
1203
+ "path": "/sources",
1204
+ "message": "sources is a list of scheme:name sources or prefix* patterns"
1205
+ }
1206
+ ]
1207
+ },
1208
+ "steps": [
1209
+ {
1210
+ "message": {
1211
+ "version": "v0.9",
1212
+ "applyTemplate": {
1213
+ "surfaceId": "d1",
1214
+ "templateId": "list",
1215
+ "data": {
1216
+ "filter": "online"
1217
+ }
1218
+ }
1219
+ },
1220
+ "expected": [
1221
+ {
1222
+ "op": "create",
1223
+ "surfaceId": "d1",
1224
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
1225
+ },
1226
+ {
1227
+ "op": "components",
1228
+ "surfaceId": "d1",
1229
+ "components": [
1230
+ {
1231
+ "id": "root",
1232
+ "component": "Column",
1233
+ "children": [
1234
+ "title"
1235
+ ]
1236
+ },
1237
+ {
1238
+ "id": "title",
1239
+ "component": "Text",
1240
+ "text": {
1241
+ "path": "/title"
1242
+ }
1243
+ }
1244
+ ]
1245
+ },
1246
+ {
1247
+ "op": "data",
1248
+ "surfaceId": "d1",
1249
+ "path": "",
1250
+ "value": {
1251
+ "title": "Devices",
1252
+ "filter": "online"
1253
+ }
1254
+ },
1255
+ {
1256
+ "op": "bind",
1257
+ "surfaceId": "d1",
1258
+ "path": "/devices",
1259
+ "source": "exp:devices"
1260
+ }
1261
+ ]
1262
+ },
1263
+ {
1264
+ "message": {
1265
+ "version": "v0.9",
1266
+ "applyTemplate": {
1267
+ "surfaceId": "d2",
1268
+ "templateId": "list",
1269
+ "packageId": "acme.devices"
1270
+ }
1271
+ },
1272
+ "expected": [
1273
+ {
1274
+ "op": "create",
1275
+ "surfaceId": "d2",
1276
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
1277
+ },
1278
+ {
1279
+ "op": "components",
1280
+ "surfaceId": "d2",
1281
+ "components": [
1282
+ {
1283
+ "id": "root",
1284
+ "component": "Column",
1285
+ "children": [
1286
+ "title"
1287
+ ]
1288
+ },
1289
+ {
1290
+ "id": "title",
1291
+ "component": "Text",
1292
+ "text": {
1293
+ "path": "/title"
1294
+ }
1295
+ }
1296
+ ]
1297
+ },
1298
+ {
1299
+ "op": "data",
1300
+ "surfaceId": "d2",
1301
+ "path": "",
1302
+ "value": {
1303
+ "title": "Devices",
1304
+ "filter": "all"
1305
+ }
1306
+ },
1307
+ {
1308
+ "op": "bind",
1309
+ "surfaceId": "d2",
1310
+ "path": "/devices",
1311
+ "source": "exp:devices"
1312
+ }
1313
+ ]
1314
+ },
1315
+ {
1316
+ "message": {
1317
+ "version": "v0.9",
1318
+ "applyTemplate": {
1319
+ "surfaceId": "d3",
1320
+ "templateId": "missing"
1321
+ }
1322
+ },
1323
+ "expected": [
1324
+ {
1325
+ "op": "send",
1326
+ "message": {
1327
+ "version": "v0.9",
1328
+ "error": {
1329
+ "code": "TEMPLATE_NOT_FOUND",
1330
+ "surfaceId": "d3",
1331
+ "message": "no installed package has the template missing"
1332
+ }
1333
+ }
1334
+ }
1335
+ ]
1336
+ },
1337
+ {
1338
+ "message": {
1339
+ "version": "v0.9",
1340
+ "applyTemplate": {
1341
+ "surfaceId": "d4",
1342
+ "templateId": "t",
1343
+ "packageId": "broken"
1344
+ }
1345
+ },
1346
+ "expected": [
1347
+ {
1348
+ "op": "send",
1349
+ "message": {
1350
+ "version": "v0.9",
1351
+ "error": {
1352
+ "code": "TEMPLATE_NOT_FOUND",
1353
+ "surfaceId": "d4",
1354
+ "message": "no installed package has the template t"
1355
+ }
1356
+ }
1357
+ }
1358
+ ]
1359
+ }
1360
+ ]
1361
+ },
1362
+ {
1363
+ "name": "VAPP-107: a package surface follows only its listed sources",
1364
+ "packages": [
1365
+ "acme.scoped",
1366
+ "acme.leaky"
1367
+ ],
1368
+ "installIssues": {
1369
+ "acme.scoped": [],
1370
+ "acme.leaky": [
1371
+ {
1372
+ "path": "/templates/list/bindings/1/source",
1373
+ "message": "source EXP:issues?board=b1 is not in the package's sources"
1374
+ }
1375
+ ]
1376
+ },
1377
+ "steps": [
1378
+ {
1379
+ "message": {
1380
+ "version": "v0.9",
1381
+ "applyTemplate": {
1382
+ "surfaceId": "s",
1383
+ "templateId": "list",
1384
+ "packageId": "acme.scoped"
1385
+ }
1386
+ },
1387
+ "expected": [
1388
+ {
1389
+ "op": "create",
1390
+ "surfaceId": "s",
1391
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
1392
+ },
1393
+ {
1394
+ "op": "components",
1395
+ "surfaceId": "s",
1396
+ "components": [
1397
+ {
1398
+ "id": "root",
1399
+ "component": "Text",
1400
+ "text": {
1401
+ "path": "/devices/count"
1402
+ }
1403
+ }
1404
+ ]
1405
+ },
1406
+ {
1407
+ "op": "bind",
1408
+ "surfaceId": "s",
1409
+ "path": "/devices",
1410
+ "source": "exp:devices"
1411
+ }
1412
+ ]
1413
+ },
1414
+ {
1415
+ "message": {
1416
+ "version": "v0.9",
1417
+ "bindDataModel": {
1418
+ "surfaceId": "s",
1419
+ "path": "/issues",
1420
+ "source": "exp:issues?board=b1"
1421
+ }
1422
+ },
1423
+ "expected": [
1424
+ {
1425
+ "op": "send",
1426
+ "message": {
1427
+ "version": "v0.9",
1428
+ "error": {
1429
+ "code": "SOURCE_DENIED",
1430
+ "surfaceId": "s",
1431
+ "message": "source exp:issues?board=b1 is not in package acme.scoped's sources",
1432
+ "path": "/issues"
1433
+ }
1434
+ }
1435
+ }
1436
+ ]
1437
+ },
1438
+ {
1439
+ "message": {
1440
+ "version": "v0.9",
1441
+ "bindDataModel": {
1442
+ "surfaceId": "s",
1443
+ "path": "/devices",
1444
+ "source": "EXP:devices?limit=5"
1445
+ }
1446
+ },
1447
+ "expected": [
1448
+ {
1449
+ "op": "bind",
1450
+ "surfaceId": "s",
1451
+ "path": "/devices",
1452
+ "source": "EXP:devices?limit=5"
1453
+ }
1454
+ ]
1455
+ },
1456
+ {
1457
+ "message": {
1458
+ "version": "v0.9",
1459
+ "bindDataModel": {
1460
+ "surfaceId": "s",
1461
+ "path": "/extra",
1462
+ "source": "acme.data:list/items"
1463
+ }
1464
+ },
1465
+ "expected": [
1466
+ {
1467
+ "op": "bind",
1468
+ "surfaceId": "s",
1469
+ "path": "/extra",
1470
+ "source": "acme.data:list/items"
1471
+ }
1472
+ ]
1473
+ },
1474
+ {
1475
+ "message": {
1476
+ "version": "v0.9",
1477
+ "createSurface": {
1478
+ "surfaceId": "free",
1479
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
1480
+ }
1481
+ },
1482
+ "expected": [
1483
+ {
1484
+ "op": "create",
1485
+ "surfaceId": "free",
1486
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
1487
+ }
1488
+ ]
1489
+ },
1490
+ {
1491
+ "message": {
1492
+ "version": "v0.9",
1493
+ "bindDataModel": {
1494
+ "surfaceId": "free",
1495
+ "path": "/issues",
1496
+ "source": "exp:issues"
1497
+ }
1498
+ },
1499
+ "expected": [
1500
+ {
1501
+ "op": "bind",
1502
+ "surfaceId": "free",
1503
+ "path": "/issues",
1504
+ "source": "exp:issues"
1505
+ }
1506
+ ]
1507
+ }
1508
+ ]
1509
+ },
1510
+ {
1511
+ "name": "VAPP-122: the bind action re-follows a path through the package's allowlist",
1512
+ "packages": [
1513
+ "acme.hours"
1514
+ ],
1515
+ "installIssues": {
1516
+ "acme.hours": []
1517
+ },
1518
+ "steps": [
1519
+ {
1520
+ "message": {
1521
+ "version": "v0.9",
1522
+ "applyTemplate": {
1523
+ "surfaceId": "h",
1524
+ "templateId": "month"
1525
+ }
1526
+ },
1527
+ "expected": [
1528
+ {
1529
+ "op": "create",
1530
+ "surfaceId": "h",
1531
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
1532
+ },
1533
+ {
1534
+ "op": "components",
1535
+ "surfaceId": "h",
1536
+ "components": [
1537
+ {
1538
+ "id": "root",
1539
+ "component": "Column",
1540
+ "children": [
1541
+ "title"
1542
+ ]
1543
+ },
1544
+ {
1545
+ "id": "title",
1546
+ "component": "Text",
1547
+ "text": {
1548
+ "path": "/entries/month/label"
1549
+ }
1550
+ }
1551
+ ]
1552
+ },
1553
+ {
1554
+ "op": "data",
1555
+ "surfaceId": "h",
1556
+ "path": "",
1557
+ "value": {
1558
+ "entries": {
1559
+ "rows": [],
1560
+ "count": 0
1561
+ }
1562
+ }
1563
+ },
1564
+ {
1565
+ "op": "bind",
1566
+ "surfaceId": "h",
1567
+ "path": "/entries",
1568
+ "source": "exp:records?kind=entry&mine=1&by=date&sum=hours"
1569
+ }
1570
+ ]
1571
+ },
1572
+ {
1573
+ "message": {
1574
+ "version": "v0.9",
1575
+ "bindDataModel": {
1576
+ "surfaceId": "h",
1577
+ "path": "/entries",
1578
+ "source": "exp:records?kind=entry&mine=1&by=date&sum=hours&month=2026-09"
1579
+ }
1580
+ },
1581
+ "expected": [
1582
+ {
1583
+ "op": "bind",
1584
+ "surfaceId": "h",
1585
+ "path": "/entries",
1586
+ "source": "exp:records?kind=entry&mine=1&by=date&sum=hours&month=2026-09"
1587
+ }
1588
+ ]
1589
+ },
1590
+ {
1591
+ "message": {
1592
+ "version": "v0.9",
1593
+ "bindDataModel": {
1594
+ "surfaceId": "h",
1595
+ "path": "/entries",
1596
+ "source": "exp:issues?board=b1"
1597
+ }
1598
+ },
1599
+ "expected": [
1600
+ {
1601
+ "op": "send",
1602
+ "message": {
1603
+ "version": "v0.9",
1604
+ "error": {
1605
+ "code": "SOURCE_DENIED",
1606
+ "surfaceId": "h",
1607
+ "message": "source exp:issues?board=b1 is not in package acme.hours's sources",
1608
+ "path": "/entries"
1609
+ }
1610
+ }
1611
+ }
1612
+ ]
1613
+ }
1614
+ ]
1615
+ },
1616
+ {
1617
+ "name": "VAPP-161: a version-2 package with a registered extension installs; a newer format does not",
1618
+ "extensionIds": [
1619
+ "https://acme.example/catalogs/charts/v1"
1620
+ ],
1621
+ "packages": [
1622
+ "acme.v2",
1623
+ "acme.charts",
1624
+ "acme.future",
1625
+ "acme.v1"
1626
+ ],
1627
+ "installIssues": {
1628
+ "acme.v2": [],
1629
+ "acme.charts": [],
1630
+ "acme.future": [
1631
+ {
1632
+ "path": "/formatVersion",
1633
+ "message": "format version 3 is newer than this host (2)"
1634
+ }
1635
+ ],
1636
+ "acme.v1": []
1637
+ },
1638
+ "steps": [
1639
+ {
1640
+ "message": {
1641
+ "version": "v0.9",
1642
+ "applyTemplate": {
1643
+ "surfaceId": "h",
1644
+ "packageId": "acme.v2",
1645
+ "templateId": "list"
1646
+ }
1647
+ },
1648
+ "expected": [
1649
+ {
1650
+ "op": "create",
1651
+ "surfaceId": "h",
1652
+ "catalogId": "https://ui.exponential.at/catalogs/core/v1"
1653
+ },
1654
+ {
1655
+ "op": "components",
1656
+ "surfaceId": "h",
1657
+ "components": [
1658
+ {
1659
+ "id": "root",
1660
+ "component": "Text",
1661
+ "text": {
1662
+ "path": "/entries/count"
1663
+ }
1664
+ }
1665
+ ]
1666
+ },
1667
+ {
1668
+ "op": "bind",
1669
+ "surfaceId": "h",
1670
+ "path": "/entries",
1671
+ "source": "records:entry?mine=1&sort=date"
1672
+ }
1673
+ ]
1674
+ }
1675
+ ]
1676
+ }
1677
+ ],
1678
+ "validation": [
1679
+ {
1680
+ "package": "acme.devices",
1681
+ "expected": []
1682
+ },
1683
+ {
1684
+ "package": "broken",
1685
+ "expected": [
1686
+ {
1687
+ "path": "/name",
1688
+ "message": "name is a required string"
1689
+ },
1690
+ {
1691
+ "path": "/catalogId",
1692
+ "message": "unsupported catalog https://other.example/v9"
1693
+ },
1694
+ {
1695
+ "path": "/templates/t/components/1/id",
1696
+ "message": "duplicate id a"
1697
+ },
1698
+ {
1699
+ "path": "/templates/t/components/2",
1700
+ "message": "a component needs an id and a component"
1701
+ },
1702
+ {
1703
+ "path": "/templates/t/components",
1704
+ "message": "no component has the id root"
1705
+ },
1706
+ {
1707
+ "path": "/templates/t/bindings/0/path",
1708
+ "message": "path is a JSON pointer"
1709
+ },
1710
+ {
1711
+ "path": "/templates/t/bindings/0/source",
1712
+ "message": "source is a scheme:name URI"
1713
+ },
1714
+ {
1715
+ "path": "/functions",
1716
+ "message": "functions is a list of names or prefix* patterns"
1717
+ },
1718
+ {
1719
+ "path": "/sources",
1720
+ "message": "sources is a list of scheme:name sources or prefix* patterns"
1721
+ }
1722
+ ]
1723
+ },
1724
+ {
1725
+ "package": "acme.scoped",
1726
+ "expected": []
1727
+ },
1728
+ {
1729
+ "package": "acme.leaky",
1730
+ "expected": [
1731
+ {
1732
+ "path": "/templates/list/bindings/1/source",
1733
+ "message": "source EXP:issues?board=b1 is not in the package's sources"
1734
+ }
1735
+ ]
1736
+ },
1737
+ {
1738
+ "package": "acme.hours",
1739
+ "expected": []
1740
+ },
1741
+ {
1742
+ "package": "acme.badrecords",
1743
+ "expected": [
1744
+ {
1745
+ "path": "/records/Entry",
1746
+ "message": "a kind is lowercase letters, digits and underscores"
1747
+ },
1748
+ {
1749
+ "path": "/records/Entry/schema/properties/id",
1750
+ "message": "id is a reserved record key"
1751
+ },
1752
+ {
1753
+ "path": "/records/Entry/schema/properties/mood/enum",
1754
+ "message": "enum is a non-empty list of strings on a string field"
1755
+ },
1756
+ {
1757
+ "path": "/records/Entry/schema/properties/name/items",
1758
+ "message": "items belongs to an array field"
1759
+ },
1760
+ {
1761
+ "path": "/records/Entry/schema/properties/name/format",
1762
+ "message": "format is date|date-time|time on a string field"
1763
+ },
1764
+ {
1765
+ "path": "/records/Entry/schema/properties/size/maximum",
1766
+ "message": "maximum is a number"
1767
+ },
1768
+ {
1769
+ "path": "/records/Entry/schema/properties/tags/items",
1770
+ "message": "items is {type: string|number|integer|boolean}"
1771
+ },
1772
+ {
1773
+ "path": "/records/Entry/schema/properties/when",
1774
+ "message": "a field is {type: string|number|integer|boolean|array}"
1775
+ },
1776
+ {
1777
+ "path": "/records/Entry/schema/required/1",
1778
+ "message": "missing is not a declared field"
1779
+ },
1780
+ {
1781
+ "path": "/records/Entry/schema/additionalProperties",
1782
+ "message": "additionalProperties is a boolean"
1783
+ },
1784
+ {
1785
+ "path": "/records/list",
1786
+ "message": "a kind is {schema, title?}"
1787
+ },
1788
+ {
1789
+ "path": "/records/ok_kind/schema",
1790
+ "message": "a record schema is {type: \"object\", properties}"
1791
+ }
1792
+ ]
1793
+ },
1794
+ {
1795
+ "package": "acme.v2",
1796
+ "expected": []
1797
+ },
1798
+ {
1799
+ "package": "acme.v2broken",
1800
+ "expected": [
1801
+ {
1802
+ "path": "/formatVersion",
1803
+ "message": "formatVersion is a positive integer"
1804
+ },
1805
+ {
1806
+ "path": "/templates/t/bindings/0/source",
1807
+ "message": "records:missing names no declared kind"
1808
+ },
1809
+ {
1810
+ "path": "/tools/Bad",
1811
+ "message": "a tool id is lowercase letters, digits, - and _"
1812
+ },
1813
+ {
1814
+ "path": "/tools/both",
1815
+ "message": "an mcp tool has a url or a command"
1816
+ },
1817
+ {
1818
+ "path": "/tools/neither",
1819
+ "message": "an mcp tool has a url or a command"
1820
+ },
1821
+ {
1822
+ "path": "/tools/notool",
1823
+ "message": "a tool is {kind, title, url | command}"
1824
+ },
1825
+ {
1826
+ "path": "/tools/rel/url",
1827
+ "message": "url is an absolute http(s) url"
1828
+ },
1829
+ {
1830
+ "path": "/tools/rel/args",
1831
+ "message": "args is a list of strings beside a command"
1832
+ },
1833
+ {
1834
+ "path": "/tools/script/title",
1835
+ "message": "title is a required string"
1836
+ },
1837
+ {
1838
+ "path": "/tools/script/url",
1839
+ "message": "a script runs a command, not a url"
1840
+ },
1841
+ {
1842
+ "path": "/tools/script/command",
1843
+ "message": "command is a non-empty string"
1844
+ },
1845
+ {
1846
+ "path": "/tools/script/env",
1847
+ "message": "env is a list of variable names beside a command"
1848
+ },
1849
+ {
1850
+ "path": "/tools/weird/kind",
1851
+ "message": "kind is mcp or script"
1852
+ },
1853
+ {
1854
+ "path": "/tools/weird/description",
1855
+ "message": "description is a string"
1856
+ },
1857
+ {
1858
+ "path": "/tools/weird/command",
1859
+ "message": "command is a non-empty string"
1860
+ },
1861
+ {
1862
+ "path": "/tools/weird/args",
1863
+ "message": "args is a list of strings beside a command"
1864
+ },
1865
+ {
1866
+ "path": "/extensions/0/version",
1867
+ "message": "version is MAJOR.MINOR.PATCH"
1868
+ },
1869
+ {
1870
+ "path": "/extensions/0/id",
1871
+ "message": "extension https://acme.example/catalogs/charts/v1 is not available on this host"
1872
+ },
1873
+ {
1874
+ "path": "/extensions/1/id",
1875
+ "message": "id is a required string"
1876
+ },
1877
+ {
1878
+ "path": "/extensions/1/components",
1879
+ "message": "components is a list of component names"
1880
+ },
1881
+ {
1882
+ "path": "/extensions/3/id",
1883
+ "message": "duplicate extension https://ui.exponential.at/catalogs/core/v1"
1884
+ },
1885
+ {
1886
+ "path": "/extensions/4",
1887
+ "message": "an extension is {id, version, components?}"
1888
+ }
1889
+ ]
1890
+ },
1891
+ {
1892
+ "package": "acme.future",
1893
+ "expected": [
1894
+ {
1895
+ "path": "/formatVersion",
1896
+ "message": "format version 3 is newer than this host (2)"
1897
+ }
1898
+ ]
1899
+ },
1900
+ {
1901
+ "package": "acme.v1",
1902
+ "expected": []
1903
+ },
1904
+ {
1905
+ "package": "acme.charts",
1906
+ "expected": [
1907
+ {
1908
+ "path": "/extensions/0/id",
1909
+ "message": "extension https://acme.example/catalogs/charts/v1 is not available on this host"
1910
+ }
1911
+ ]
1912
+ }
1913
+ ],
1914
+ "records": [
1915
+ {
1916
+ "name": "a full entry passes",
1917
+ "package": "acme.hours",
1918
+ "kind": "entry",
1919
+ "data": {
1920
+ "date": "2026-10-05",
1921
+ "hours": 3,
1922
+ "note": "leaderboard, meet",
1923
+ "issues": [
1924
+ "MET-1437",
1925
+ "MET-1440"
1926
+ ],
1927
+ "kind": "work"
1928
+ },
1929
+ "expected": []
1930
+ },
1931
+ {
1932
+ "name": "the required fields alone pass; null clears an optional one",
1933
+ "package": "acme.hours",
1934
+ "kind": "entry",
1935
+ "data": {
1936
+ "date": "2026-10-05",
1937
+ "hours": 0,
1938
+ "note": null
1939
+ },
1940
+ "expected": []
1941
+ },
1942
+ {
1943
+ "name": "every check fires",
1944
+ "package": "acme.hours",
1945
+ "kind": "entry",
1946
+ "data": {
1947
+ "date": "5.10.2026",
1948
+ "hours": 25,
1949
+ "note": "x",
1950
+ "issues": [
1951
+ "a",
1952
+ 2
1953
+ ],
1954
+ "kind": "play",
1955
+ "extra": 1
1956
+ },
1957
+ "expected": [
1958
+ {
1959
+ "path": "/date",
1960
+ "message": "a date (YYYY-MM-DD)"
1961
+ },
1962
+ {
1963
+ "path": "/hours",
1964
+ "message": "at most 24"
1965
+ },
1966
+ {
1967
+ "path": "/issues/1",
1968
+ "message": "a string"
1969
+ },
1970
+ {
1971
+ "path": "/kind",
1972
+ "message": "one of work|meeting|support"
1973
+ },
1974
+ {
1975
+ "path": "/extra",
1976
+ "message": "not in the schema"
1977
+ }
1978
+ ]
1979
+ },
1980
+ {
1981
+ "name": "a required field missing, a wrong type",
1982
+ "package": "acme.hours",
1983
+ "kind": "entry",
1984
+ "data": {
1985
+ "hours": "3",
1986
+ "issues": "MET-1"
1987
+ },
1988
+ "expected": [
1989
+ {
1990
+ "path": "/date",
1991
+ "message": "required"
1992
+ },
1993
+ {
1994
+ "path": "/hours",
1995
+ "message": "a number"
1996
+ },
1997
+ {
1998
+ "path": "/issues",
1999
+ "message": "a list"
2000
+ }
2001
+ ]
2002
+ },
2003
+ {
2004
+ "name": "not an object",
2005
+ "package": "acme.hours",
2006
+ "kind": "entry",
2007
+ "data": [
2008
+ 1
2009
+ ],
2010
+ "expected": [
2011
+ {
2012
+ "path": "",
2013
+ "message": "a record is an object"
2014
+ }
2015
+ ]
2016
+ },
2017
+ {
2018
+ "name": "additionalProperties true keeps extra keys; an integer check",
2019
+ "package": "acme.hours",
2020
+ "kind": "target",
2021
+ "data": {
2022
+ "month": "2026-10",
2023
+ "hours": 40.5,
2024
+ "carry": 11.75
2025
+ },
2026
+ "expected": [
2027
+ {
2028
+ "path": "/hours",
2029
+ "message": "an integer"
2030
+ }
2031
+ ]
2032
+ }
2033
+ ]
2034
+ }