@elaraai/e3-ui 1.0.98 → 1.0.99

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 (381) hide show
  1. package/README.md +0 -2
  2. package/dist/src/allocation/adapters.d.ts +1 -1
  3. package/dist/src/allocation/adapters.js +1 -1
  4. package/dist/src/allocation/index.d.ts +46 -90
  5. package/dist/src/allocation/index.d.ts.map +1 -1
  6. package/dist/src/allocation/index.js +5 -5
  7. package/dist/src/allocation/index.js.map +1 -1
  8. package/dist/src/bind/data-indexed.d.ts +273 -51
  9. package/dist/src/bind/data-indexed.d.ts.map +1 -1
  10. package/dist/src/bind/data-indexed.js +233 -56
  11. package/dist/src/bind/data-indexed.js.map +1 -1
  12. package/dist/src/bind/data.d.ts +48 -9
  13. package/dist/src/bind/data.d.ts.map +1 -1
  14. package/dist/src/bind/data.js +48 -3
  15. package/dist/src/bind/data.js.map +1 -1
  16. package/dist/src/bind/record.d.ts +61 -24
  17. package/dist/src/bind/record.d.ts.map +1 -1
  18. package/dist/src/bind/record.js +17 -57
  19. package/dist/src/bind/record.js.map +1 -1
  20. package/dist/src/calendar/index.d.ts +55 -95
  21. package/dist/src/calendar/index.d.ts.map +1 -1
  22. package/dist/src/calendar/index.js +10 -6
  23. package/dist/src/calendar/index.js.map +1 -1
  24. package/dist/src/flowchart/index.d.ts +35 -31
  25. package/dist/src/flowchart/index.d.ts.map +1 -1
  26. package/dist/src/flowchart/index.js +35 -31
  27. package/dist/src/flowchart/index.js.map +1 -1
  28. package/dist/src/flowchart/library.d.ts +108 -84
  29. package/dist/src/flowchart/library.d.ts.map +1 -1
  30. package/dist/src/flowchart/library.js +108 -84
  31. package/dist/src/flowchart/library.js.map +1 -1
  32. package/dist/src/flowchart/payload.d.ts +39 -107
  33. package/dist/src/flowchart/payload.d.ts.map +1 -1
  34. package/dist/src/flowchart/payload.js +3 -3
  35. package/dist/src/flowchart/payload.js.map +1 -1
  36. package/dist/src/flowchart/values.d.ts +35 -31
  37. package/dist/src/flowchart/values.d.ts.map +1 -1
  38. package/dist/src/flowchart/values.js +35 -31
  39. package/dist/src/flowchart/values.js.map +1 -1
  40. package/dist/src/flowchart/window.d.ts +0 -12
  41. package/dist/src/flowchart/window.d.ts.map +1 -1
  42. package/dist/src/flowchart/window.js +3 -2
  43. package/dist/src/flowchart/window.js.map +1 -1
  44. package/dist/src/index.d.ts +2 -2
  45. package/dist/src/index.d.ts.map +1 -1
  46. package/dist/src/index.js +1 -1
  47. package/dist/src/index.js.map +1 -1
  48. package/dist/src/internal.d.ts +7 -3
  49. package/dist/src/internal.d.ts.map +1 -1
  50. package/dist/src/internal.js +12 -3
  51. package/dist/src/internal.js.map +1 -1
  52. package/dist/src/plan/assemble.d.ts +8 -0
  53. package/dist/src/plan/assemble.d.ts.map +1 -1
  54. package/dist/src/plan/assemble.js +1 -1
  55. package/dist/src/plan/assemble.js.map +1 -1
  56. package/dist/src/plan/builders.d.ts +1518 -2
  57. package/dist/src/plan/builders.d.ts.map +1 -1
  58. package/dist/src/plan/builders.js +248 -114
  59. package/dist/src/plan/builders.js.map +1 -1
  60. package/dist/src/plan/event-rows.d.ts +9 -14
  61. package/dist/src/plan/event-rows.d.ts.map +1 -1
  62. package/dist/src/plan/event-rows.js +6 -46
  63. package/dist/src/plan/event-rows.js.map +1 -1
  64. package/dist/src/plan/factories.d.ts +72 -4
  65. package/dist/src/plan/factories.d.ts.map +1 -1
  66. package/dist/src/plan/factories.js +147 -1
  67. package/dist/src/plan/factories.js.map +1 -1
  68. package/dist/src/plan/index.d.ts +147 -125
  69. package/dist/src/plan/index.d.ts.map +1 -1
  70. package/dist/src/plan/index.js +135 -122
  71. package/dist/src/plan/index.js.map +1 -1
  72. package/dist/src/plan/ir.d.ts +1105 -44
  73. package/dist/src/plan/ir.d.ts.map +1 -1
  74. package/dist/src/plan/ir.js +5 -0
  75. package/dist/src/plan/ir.js.map +1 -1
  76. package/dist/src/plan/library.d.ts +65 -42
  77. package/dist/src/plan/library.d.ts.map +1 -1
  78. package/dist/src/plan/library.js +65 -42
  79. package/dist/src/plan/library.js.map +1 -1
  80. package/dist/src/plan/nested-checks.d.ts +146 -0
  81. package/dist/src/plan/nested-checks.d.ts.map +1 -0
  82. package/dist/src/plan/nested-checks.js +468 -0
  83. package/dist/src/plan/nested-checks.js.map +1 -0
  84. package/dist/src/plan/nested-patch.d.ts +35 -0
  85. package/dist/src/plan/nested-patch.d.ts.map +1 -0
  86. package/dist/src/plan/nested-patch.js +143 -0
  87. package/dist/src/plan/nested-patch.js.map +1 -0
  88. package/dist/src/plan/nested-read.d.ts +3250 -0
  89. package/dist/src/plan/nested-read.d.ts.map +1 -0
  90. package/dist/src/plan/nested-read.js +163 -0
  91. package/dist/src/plan/nested-read.js.map +1 -0
  92. package/dist/src/plan/nested.d.ts +280 -0
  93. package/dist/src/plan/nested.d.ts.map +1 -0
  94. package/dist/src/plan/nested.js +6 -0
  95. package/dist/src/plan/nested.js.map +1 -0
  96. package/dist/src/plan/ordered-source.d.ts +1030 -1
  97. package/dist/src/plan/ordered-source.d.ts.map +1 -1
  98. package/dist/src/plan/ordered-source.js +48 -6
  99. package/dist/src/plan/ordered-source.js.map +1 -1
  100. package/dist/src/plan/over.d.ts +111 -75
  101. package/dist/src/plan/over.d.ts.map +1 -1
  102. package/dist/src/plan/over.js +111 -75
  103. package/dist/src/plan/over.js.map +1 -1
  104. package/dist/src/plan/pick.d.ts +15 -3
  105. package/dist/src/plan/pick.d.ts.map +1 -1
  106. package/dist/src/plan/pick.js +75 -36
  107. package/dist/src/plan/pick.js.map +1 -1
  108. package/dist/src/plan/plan.d.ts +2719 -558
  109. package/dist/src/plan/plan.d.ts.map +1 -1
  110. package/dist/src/plan/plan.js +14 -2
  111. package/dist/src/plan/plan.js.map +1 -1
  112. package/dist/src/plan/reads.d.ts +421 -0
  113. package/dist/src/plan/reads.d.ts.map +1 -0
  114. package/dist/src/plan/reads.js +143 -0
  115. package/dist/src/plan/reads.js.map +1 -0
  116. package/dist/src/plan/refs.d.ts +111 -75
  117. package/dist/src/plan/refs.d.ts.map +1 -1
  118. package/dist/src/plan/refs.js +111 -75
  119. package/dist/src/plan/refs.js.map +1 -1
  120. package/dist/src/plan/root.d.ts +10 -5
  121. package/dist/src/plan/root.d.ts.map +1 -1
  122. package/dist/src/plan/root.js +76 -13
  123. package/dist/src/plan/root.js.map +1 -1
  124. package/dist/src/plan/series.d.ts +5140 -4331
  125. package/dist/src/plan/series.d.ts.map +1 -1
  126. package/dist/src/plan/series.js +1026 -264
  127. package/dist/src/plan/series.js.map +1 -1
  128. package/dist/src/plan/shade.d.ts +981 -0
  129. package/dist/src/plan/shade.d.ts.map +1 -0
  130. package/dist/src/plan/shade.js +60 -0
  131. package/dist/src/plan/shade.js.map +1 -0
  132. package/dist/src/plan/types.d.ts +178 -18
  133. package/dist/src/plan/types.d.ts.map +1 -1
  134. package/dist/src/plan/types.js +69 -19
  135. package/dist/src/plan/types.js.map +1 -1
  136. package/dist/src/query/builder.d.ts +3 -3
  137. package/dist/src/query/builder.d.ts.map +1 -1
  138. package/dist/src/query/builder.js +1 -1
  139. package/dist/src/query/library.d.ts +3 -3
  140. package/dist/src/query/library.d.ts.map +1 -1
  141. package/dist/src/query/library.js +1 -1
  142. package/dist/src/query/queries.d.ts +2 -2
  143. package/dist/src/query/queries.js +1 -1
  144. package/dist/src/query/saved.d.ts +8 -4
  145. package/dist/src/query/saved.d.ts.map +1 -1
  146. package/dist/src/query/saved.js +8 -4
  147. package/dist/src/query/saved.js.map +1 -1
  148. package/dist/src/query/types.d.ts +8 -3
  149. package/dist/src/query/types.d.ts.map +1 -1
  150. package/dist/src/query/types.js +8 -3
  151. package/dist/src/query/types.js.map +1 -1
  152. package/dist/src/roster/index.d.ts +47 -91
  153. package/dist/src/roster/index.d.ts.map +1 -1
  154. package/dist/src/roster/index.js +6 -6
  155. package/dist/src/roster/index.js.map +1 -1
  156. package/dist/src/roster/targets.d.ts +8 -4
  157. package/dist/src/roster/targets.d.ts.map +1 -1
  158. package/dist/src/roster/targets.js +9 -5
  159. package/dist/src/roster/targets.js.map +1 -1
  160. package/dist/src/schedule/cell-checks.js +3 -3
  161. package/dist/src/schedule/cell-checks.js.map +1 -1
  162. package/dist/src/schedule/days.d.ts +1 -1
  163. package/dist/src/schedule/days.js +1 -1
  164. package/dist/src/schedule/events.d.ts +10 -6
  165. package/dist/src/schedule/events.d.ts.map +1 -1
  166. package/dist/src/schedule/events.js +252 -228
  167. package/dist/src/schedule/events.js.map +1 -1
  168. package/dist/src/schedule/patch.d.ts +1 -1
  169. package/dist/src/schedule/patch.js +1 -1
  170. package/dist/src/schedule/periods.d.ts +8 -4
  171. package/dist/src/schedule/periods.d.ts.map +1 -1
  172. package/dist/src/schedule/periods.js +1 -1
  173. package/dist/src/schedule/periods.js.map +1 -1
  174. package/dist/src/schedule/record-window.d.ts +38 -58
  175. package/dist/src/schedule/record-window.d.ts.map +1 -1
  176. package/dist/src/schedule/record-window.js +15 -16
  177. package/dist/src/schedule/record-window.js.map +1 -1
  178. package/dist/src/schedule/resources.d.ts +1 -1
  179. package/dist/src/schedule/session.d.ts +1 -1
  180. package/dist/src/schedule/session.js +1 -1
  181. package/dist/src/schedule/types.d.ts +29 -1
  182. package/dist/src/schedule/types.d.ts.map +1 -1
  183. package/dist/src/schedule/types.js +16 -1
  184. package/dist/src/schedule/types.js.map +1 -1
  185. package/dist/src/schematic/index.d.ts +19 -9
  186. package/dist/src/schematic/index.d.ts.map +1 -1
  187. package/dist/src/schematic/index.js +19 -9
  188. package/dist/src/schematic/index.js.map +1 -1
  189. package/dist/src/schematic/kinds.d.ts +4 -4
  190. package/dist/src/schematic/kinds.js +9 -9
  191. package/dist/src/schematic/kinds.js.map +1 -1
  192. package/dist/src/schematic/payload.d.ts +38 -88
  193. package/dist/src/schematic/payload.d.ts.map +1 -1
  194. package/dist/src/schematic/payload.js +4 -4
  195. package/dist/src/schematic/payload.js.map +1 -1
  196. package/dist/src/schematic/window.d.ts +8 -7
  197. package/dist/src/schematic/window.d.ts.map +1 -1
  198. package/dist/src/schematic/window.js +22 -19
  199. package/dist/src/schematic/window.js.map +1 -1
  200. package/dist/src/sheet/bridge.d.ts +19 -0
  201. package/dist/src/sheet/bridge.d.ts.map +1 -1
  202. package/dist/src/sheet/bridge.js +28 -21
  203. package/dist/src/sheet/bridge.js.map +1 -1
  204. package/dist/src/sheet/index.d.ts +66 -46
  205. package/dist/src/sheet/index.d.ts.map +1 -1
  206. package/dist/src/sheet/index.js +66 -46
  207. package/dist/src/sheet/index.js.map +1 -1
  208. package/dist/src/sheet/library.d.ts +13 -8
  209. package/dist/src/sheet/library.d.ts.map +1 -1
  210. package/dist/src/sheet/library.js +13 -8
  211. package/dist/src/sheet/library.js.map +1 -1
  212. package/dist/src/sheet/record.d.ts +1 -1
  213. package/dist/src/sheet/record.js +2 -2
  214. package/dist/src/sheet/record.js.map +1 -1
  215. package/dist/src/sheet/registers.d.ts +152 -128
  216. package/dist/src/sheet/registers.d.ts.map +1 -1
  217. package/dist/src/sheet/registers.js +152 -128
  218. package/dist/src/sheet/registers.js.map +1 -1
  219. package/dist/src/sheet/root.d.ts +4 -1
  220. package/dist/src/sheet/root.d.ts.map +1 -1
  221. package/dist/src/sheet/root.js +15 -3
  222. package/dist/src/sheet/root.js.map +1 -1
  223. package/dist/src/sheet/sheet.d.ts +39 -113
  224. package/dist/src/sheet/sheet.d.ts.map +1 -1
  225. package/dist/src/sheet/sheet.js +7 -5
  226. package/dist/src/sheet/sheet.js.map +1 -1
  227. package/dist/src/sheet/sub-rows.d.ts +106 -76
  228. package/dist/src/sheet/sub-rows.d.ts.map +1 -1
  229. package/dist/src/sheet/sub-rows.js +106 -76
  230. package/dist/src/sheet/sub-rows.js.map +1 -1
  231. package/dist/src/sheet/types.d.ts +19 -62
  232. package/dist/src/sheet/types.d.ts.map +1 -1
  233. package/dist/src/studio/builder.d.ts +3 -153
  234. package/dist/src/studio/builder.d.ts.map +1 -1
  235. package/dist/src/studio/builder.js +1 -1
  236. package/dist/src/studio/canvas.d.ts +5 -4
  237. package/dist/src/studio/canvas.d.ts.map +1 -1
  238. package/dist/src/studio/canvas.js +8 -7
  239. package/dist/src/studio/canvas.js.map +1 -1
  240. package/dist/src/studio/component.d.ts +30 -20
  241. package/dist/src/studio/component.d.ts.map +1 -1
  242. package/dist/src/studio/component.js +26 -61
  243. package/dist/src/studio/component.js.map +1 -1
  244. package/dist/src/studio/index.d.ts +2 -2
  245. package/dist/src/studio/index.d.ts.map +1 -1
  246. package/dist/src/studio/index.js +1 -1
  247. package/dist/src/studio/index.js.map +1 -1
  248. package/dist/src/studio/inspector.d.ts +10 -39
  249. package/dist/src/studio/inspector.d.ts.map +1 -1
  250. package/dist/src/studio/inspector.js +11 -31
  251. package/dist/src/studio/inspector.js.map +1 -1
  252. package/dist/src/studio/library.d.ts +3 -169
  253. package/dist/src/studio/library.d.ts.map +1 -1
  254. package/dist/src/studio/library.js +1 -1
  255. package/dist/src/studio/page.d.ts +0 -8
  256. package/dist/src/studio/page.d.ts.map +1 -1
  257. package/dist/src/studio/pages.d.ts +17 -591
  258. package/dist/src/studio/pages.d.ts.map +1 -1
  259. package/dist/src/studio/pages.js +17 -44
  260. package/dist/src/studio/pages.js.map +1 -1
  261. package/dist/src/studio/palette.d.ts +2 -7
  262. package/dist/src/studio/palette.d.ts.map +1 -1
  263. package/dist/src/studio/palette.js +4 -5
  264. package/dist/src/studio/palette.js.map +1 -1
  265. package/dist/src/studio/publish.d.ts +8 -18
  266. package/dist/src/studio/publish.d.ts.map +1 -1
  267. package/dist/src/studio/publish.js +10 -36
  268. package/dist/src/studio/publish.js.map +1 -1
  269. package/dist/src/utils/derive.d.ts +12 -12
  270. package/dist/src/utils/derive.d.ts.map +1 -1
  271. package/dist/src/utils/derive.js +5 -2
  272. package/dist/src/utils/derive.js.map +1 -1
  273. package/dist/src/utils/ordered-source.d.ts.map +1 -1
  274. package/dist/src/utils/ordered-source.js +14 -5
  275. package/dist/src/utils/ordered-source.js.map +1 -1
  276. package/dist/test/allocation/allocation.examples.d.ts +1900 -1598
  277. package/dist/test/allocation/allocation.examples.d.ts.map +1 -1
  278. package/dist/test/allocation/allocation.examples.js +179 -89
  279. package/dist/test/allocation/allocation.examples.js.map +1 -1
  280. package/dist/test/allocation/optimise.examples.d.ts.map +1 -1
  281. package/dist/test/allocation/optimise.examples.js +7 -7
  282. package/dist/test/allocation/optimise.examples.js.map +1 -1
  283. package/dist/test/bind/data/data.examples.d.ts +1057 -903
  284. package/dist/test/bind/data/data.examples.d.ts.map +1 -1
  285. package/dist/test/bind/data/data.examples.js +260 -204
  286. package/dist/test/bind/data/data.examples.js.map +1 -1
  287. package/dist/test/bind/record/record.examples.d.ts +457 -426
  288. package/dist/test/bind/record/record.examples.d.ts.map +1 -1
  289. package/dist/test/bind/record/record.examples.js +41 -21
  290. package/dist/test/bind/record/record.examples.js.map +1 -1
  291. package/dist/test/calendar/calendar-records.examples.d.ts +162 -170
  292. package/dist/test/calendar/calendar-records.examples.d.ts.map +1 -1
  293. package/dist/test/calendar/calendar-records.examples.js +20 -12
  294. package/dist/test/calendar/calendar-records.examples.js.map +1 -1
  295. package/dist/test/calendar/calendar-windows.examples.d.ts.map +1 -1
  296. package/dist/test/calendar/calendar-windows.examples.js +12 -12
  297. package/dist/test/calendar/calendar-windows.examples.js.map +1 -1
  298. package/dist/test/calendar/calendar.examples.d.ts +1172 -901
  299. package/dist/test/calendar/calendar.examples.d.ts.map +1 -1
  300. package/dist/test/calendar/calendar.examples.js +211 -124
  301. package/dist/test/calendar/calendar.examples.js.map +1 -1
  302. package/dist/test/calendar/optimise.examples.js +2 -2
  303. package/dist/test/calendar/optimise.examples.js.map +1 -1
  304. package/dist/test/examples-package.d.ts +6 -9
  305. package/dist/test/examples-package.d.ts.map +1 -1
  306. package/dist/test/examples-package.js +11 -32
  307. package/dist/test/examples-package.js.map +1 -1
  308. package/dist/test/flowchart/flowchart.examples.d.ts +3841 -2820
  309. package/dist/test/flowchart/flowchart.examples.d.ts.map +1 -1
  310. package/dist/test/flowchart/flowchart.examples.js +211 -121
  311. package/dist/test/flowchart/flowchart.examples.js.map +1 -1
  312. package/dist/test/plan/optimise.examples.js +2 -2
  313. package/dist/test/plan/optimise.examples.js.map +1 -1
  314. package/dist/test/plan/plan-events.examples.d.ts +4055 -3263
  315. package/dist/test/plan/plan-events.examples.d.ts.map +1 -1
  316. package/dist/test/plan/plan-events.examples.js +492 -319
  317. package/dist/test/plan/plan-events.examples.js.map +1 -1
  318. package/dist/test/plan/plan-nested.examples.d.ts +1829 -0
  319. package/dist/test/plan/plan-nested.examples.d.ts.map +1 -0
  320. package/dist/test/plan/plan-nested.examples.js +321 -0
  321. package/dist/test/plan/plan-nested.examples.js.map +1 -0
  322. package/dist/test/plan/plan-records.examples.d.ts +328 -362
  323. package/dist/test/plan/plan-records.examples.d.ts.map +1 -1
  324. package/dist/test/plan/plan-records.examples.js +81 -57
  325. package/dist/test/plan/plan-records.examples.js.map +1 -1
  326. package/dist/test/plan/plan-windows.examples.d.ts +1756 -1398
  327. package/dist/test/plan/plan-windows.examples.d.ts.map +1 -1
  328. package/dist/test/plan/plan-windows.examples.js +161 -128
  329. package/dist/test/plan/plan-windows.examples.js.map +1 -1
  330. package/dist/test/plan/plan.examples.d.ts +2238 -5712
  331. package/dist/test/plan/plan.examples.d.ts.map +1 -1
  332. package/dist/test/plan/plan.examples.js +839 -968
  333. package/dist/test/plan/plan.examples.js.map +1 -1
  334. package/dist/test/query/query.examples.d.ts +5221 -3005
  335. package/dist/test/query/query.examples.d.ts.map +1 -1
  336. package/dist/test/query/query.examples.js +169 -118
  337. package/dist/test/query/query.examples.js.map +1 -1
  338. package/dist/test/roster/optimise.examples.d.ts.map +1 -1
  339. package/dist/test/roster/optimise.examples.js +7 -7
  340. package/dist/test/roster/optimise.examples.js.map +1 -1
  341. package/dist/test/roster/roster-records.examples.d.ts +364 -392
  342. package/dist/test/roster/roster-records.examples.d.ts.map +1 -1
  343. package/dist/test/roster/roster-records.examples.js +40 -26
  344. package/dist/test/roster/roster-records.examples.js.map +1 -1
  345. package/dist/test/roster/roster.examples.d.ts +4605 -4308
  346. package/dist/test/roster/roster.examples.d.ts.map +1 -1
  347. package/dist/test/roster/roster.examples.js +212 -113
  348. package/dist/test/roster/roster.examples.js.map +1 -1
  349. package/dist/test/schedule/schedule.examples.d.ts +2646 -2471
  350. package/dist/test/schedule/schedule.examples.d.ts.map +1 -1
  351. package/dist/test/schedule/schedule.examples.js +144 -88
  352. package/dist/test/schedule/schedule.examples.js.map +1 -1
  353. package/dist/test/schematic/schematic-records.examples.d.ts +286 -276
  354. package/dist/test/schematic/schematic-records.examples.d.ts.map +1 -1
  355. package/dist/test/schematic/schematic-records.examples.js +52 -38
  356. package/dist/test/schematic/schematic-records.examples.js.map +1 -1
  357. package/dist/test/schematic/schematic.examples.d.ts +1842 -1491
  358. package/dist/test/schematic/schematic.examples.d.ts.map +1 -1
  359. package/dist/test/schematic/schematic.examples.js +240 -134
  360. package/dist/test/schematic/schematic.examples.js.map +1 -1
  361. package/dist/test/sheet/optimise.examples.d.ts +102 -95
  362. package/dist/test/sheet/optimise.examples.d.ts.map +1 -1
  363. package/dist/test/sheet/optimise.examples.js +7 -3
  364. package/dist/test/sheet/optimise.examples.js.map +1 -1
  365. package/dist/test/sheet/sheet-records.examples.d.ts +175 -214
  366. package/dist/test/sheet/sheet-records.examples.d.ts.map +1 -1
  367. package/dist/test/sheet/sheet-records.examples.js +29 -17
  368. package/dist/test/sheet/sheet-records.examples.js.map +1 -1
  369. package/dist/test/sheet/sheet.examples.d.ts +3642 -3353
  370. package/dist/test/sheet/sheet.examples.d.ts.map +1 -1
  371. package/dist/test/sheet/sheet.examples.js +389 -282
  372. package/dist/test/sheet/sheet.examples.js.map +1 -1
  373. package/dist/test/studio/component.examples.js +2 -2
  374. package/dist/test/studio/component.examples.js.map +1 -1
  375. package/dist/test/studio/page.examples.js +6 -6
  376. package/dist/test/studio/page.examples.js.map +1 -1
  377. package/dist/test/studio/studio.examples.d.ts +2329 -873
  378. package/dist/test/studio/studio.examples.d.ts.map +1 -1
  379. package/dist/test/studio/studio.examples.js +155 -107
  380. package/dist/test/studio/studio.examples.js.map +1 -1
  381. package/package.json +15 -11
@@ -34,11 +34,12 @@
34
34
  *
35
35
  * @packageDocumentation
36
36
  */
37
- import { East, Expr, ArrayType, BlobType, BooleanType, DateTimeType, DictType, EastTypeType, FloatType, FunctionType, IntegerType, OptionType, RefType, StringType, StructType, VariantType, isTypeEqual, printFor, toEastTypeValue, variant, some, none, } from "@elaraai/east";
37
+ import { East, Expr, ArrayType, BlobType, BooleanType, DateTimeType, DictType, EastTypeType, FloatType, FunctionType, IntegerType, NullType, OptionType, RefType, StringType, StructType, VariantType, isTypeEqual, isVariant, printFor, printTypeSummary, toEastTypeValue, variant, some, none, } from "@elaraai/east";
38
38
  import { IconType, StatusValueType } from "@elaraai/east-ui";
39
39
  import { TableAggregateType, TickFormatType, reifyAccessor, resolveTag } from "@elaraai/east-ui/internal";
40
- import { PlanAggregateType, PlanExpandType, PlanGroupSummaryType, PlanHeatCellsType, PlanLaneType, PlanBucketEventType, PlanCellMarkerType, PlanChipType, PlanEventMarkType, PlanRunType, PlanDecisionMarkType, PlanPortType, PlanRollupType, PlanRowIdType, PlanRowKindType, PlanRowType, PlanRowsCollectionType, PlanBlockType, PlanBlocksType, PlanGestureType, PlanDropType, PlanInstantType, } from "./types.js";
41
- import { resolveIcon } from "./builders.js";
40
+ import { PlanAggregateType, PlanExpandType, PlanGroupSummaryType, PlanHeatCellsType, PlanLaneType, PlanBucketEventType, PlanCellMarkerType, PlanChipType, PlanEventMarkType, PlanRunType, PlanDecisionMarkType, PlanPortType, PlanRollupType, PlanRowIdType, PlanRowKindType, PlanRowType, PlanRowsCollectionType, PlanBlockType, PlanBlocksType, PlanGestureType, PlanDropType, PlanInstantType, PlanFoldType, } from "./types.js";
41
+ import { PLAN_LOOKS, resolveIcon, resolveInstant, } from "./builders.js";
42
+ import { PlanReadsBrand, PlanUseBrand, planReadOf, planUseOf, } from "./reads.js";
42
43
  import { planRow, planGutter, normalizeRows, REBASE_ROWS } from "./assemble.js";
43
44
  import { spanKind, bucketsKind, chartKind, heatKind, tableKind, cardsKind, eventsKind, groupKind, groupSummary, } from "./factories.js";
44
45
  // ============================================================================
@@ -106,15 +107,35 @@ export const PlanSeriesType = seriesShape;
106
107
  /** Marks a {@link PlanChildren} step-down. */
107
108
  const PLAN_CHILDREN = Symbol("PlanChildren");
108
109
  /**
109
- * Step down from an entry to a child collection of another entry type (#822).
110
+ * Step down from an entry to its child rows (#822, #1436): a `Dict` field of
111
+ * the entry, keyed by an id, its rows laid out by `series` like a top-level
112
+ * list.
110
113
  *
111
114
  * @typeParam R - The parent entry type (inferred from where the step-down is used)
112
- * @typeParam KT - The parent entries' key type
113
- * @param of - The child collection — an `Array` (index order) or a `Dict` (key order) — from the parent entry
114
- * @param series - The series over the child entries, laid out like a top-level list
115
+ * @typeParam F - The field holding the child rows
116
+ * @typeParam S - The series over them, as written
117
+ * @param field - A Dict field of the entry holding the child rows, keyed by an id
118
+ * @param series - The series over the child rows
115
119
  * @returns The step-down, for a series' `children`
116
120
  */
117
- export function createChildren(of, series) {
121
+ export function createChildren(field, series) {
122
+ if (typeof field !== "string") {
123
+ throw new Error("Plan.children: name the field holding the child rows — a Dict of the row, keyed by an id: Plan.children(\"members\", [series…])");
124
+ }
125
+ return { [PLAN_CHILDREN]: true, field, series };
126
+ }
127
+ /**
128
+ * Step down from an entry to a collection an accessor computes (#1194) — the
129
+ * event kinds' own: a resource's events come from another record, so its
130
+ * child rows are no field of it. An author's step-down names its field
131
+ * ({@link createChildren}).
132
+ *
133
+ * @internal
134
+ * @param of - The child collection, from the parent entry's value and key
135
+ * @param series - The series over the child entries
136
+ * @returns The step-down
137
+ */
138
+ export function createChildrenOf(of, series) {
118
139
  return { [PLAN_CHILDREN]: true, of, series };
119
140
  }
120
141
  function isStepDown(x) {
@@ -127,6 +148,36 @@ const PathType = ArrayType(StringType);
127
148
  const IdOptType = OptionType(PlanRowIdType);
128
149
  /** Every series value a builder made, with its build spec. */
129
150
  const SPECS = new WeakMap();
151
+ /** A spec's facts for the nested checks (#1435). */
152
+ function nestFactsOf(spec) {
153
+ return { key: spec.key, arm: spec.arm, reads: spec.reads, nest: spec.nest };
154
+ }
155
+ /**
156
+ * The facts a Plan holds to its rows' nested series index (#1435), of a
157
+ * series value a `Plan.series.*` builder made, and of each series its spec
158
+ * nests: `facts(spec)` for any of them.
159
+ *
160
+ * @param series - The series value
161
+ * @returns Its facts; `undefined` for a value no builder made in place (a bound, picked or stored one)
162
+ * @internal
163
+ */
164
+ export function planSeriesNestOf(series) {
165
+ const spec = specOf(series);
166
+ return spec === undefined ? undefined : nestFactsOf(spec);
167
+ }
168
+ /**
169
+ * The facts of the series a spec nests (#1435), as {@link planSeriesNestOf} gives them.
170
+ *
171
+ * @param nest - A series' nest
172
+ * @returns Its step-downs' series and its members, each with its facts
173
+ * @internal
174
+ */
175
+ export function planNestedSeriesFacts(nest) {
176
+ return {
177
+ steps: nest.steps.map((step) => ({ field: step.field, of: step.of, series: step.series.map(nestFactsOf) })),
178
+ members: nest.members.map(nestFactsOf),
179
+ };
180
+ }
130
181
  /** The spec of a series value a `Plan.series.*` builder made, if it was one. */
131
182
  function specOf(s) {
132
183
  return typeof s === "object" && s !== null ? SPECS.get(s) : undefined;
@@ -308,85 +359,34 @@ function collectionOps(collection) {
308
359
  put: (c, k, v) => array(c).update(index(k), v),
309
360
  };
310
361
  }
311
- /** The instant fields a kind's elements have (#825): two ends (a run, a chip) or one instant (a tile, a mark). */
312
- function editShapeOf(arm) {
313
- return arm === "span" || arm === "cards" ? "span" : "point";
314
- }
315
- /** The item field types a move can write an instant into (#825). */
316
- const INSTANT_FIELD_TYPES = [DateTimeType, FloatType, IntegerType, StringType, PlanInstantType];
362
+ /** The item field types a move can write an instant into (#825, #1436). */
363
+ const INSTANT_FIELD_TYPES = [DateTimeType, FloatType, IntegerType, StringType, PlanInstantType, OptionType(DateTimeType)];
317
364
  /** Print an item type as the text rows of one item type share (#825) — East's own printer over its type value. */
318
365
  const printType = printFor(EastTypeType);
319
366
  /**
320
- * Check what a series' `edit` declares for a move (#825), against its item
321
- * type — `undefined` when it declares no move (only `create`).
367
+ * What a move writes, from the read that names the collection (#1436) — its
368
+ * times, checked against the item type by the read's build.
322
369
  *
323
- * @param edit - The declaration
324
- * @param itemType - The type of the items in `edit.items`
325
- * @param arm - The series' kind
326
- * @param where - The series, for the message
370
+ * @param read - The elements' read
371
+ * @param item - The collection's item type
327
372
  * @returns The move's fields
328
- * @throws {Error} Naming the field and what is wrong with it
329
373
  */
330
- function moveFieldsOf(edit, itemType, arm, where) {
331
- const shape = editShapeOf(arm);
332
- if (shape === "span" && edit.at !== undefined) {
333
- throw new Error(`${where}: \`edit.at\` is a tile's or a mark's instant — a ${arm} element has two ends: name them with \`start\` and \`end\``);
334
- }
335
- if (shape === "point" && (edit.start !== undefined || edit.end !== undefined)) {
336
- throw new Error(`${where}: a ${arm} element has one instant — name it with \`edit.at\`, not \`start\` / \`end\``);
337
- }
338
- const start = shape === "span" ? edit.start : edit.at;
339
- const end = shape === "span" ? edit.end : undefined;
340
- if (shape === "span" && (start === undefined) !== (end === undefined)) {
341
- throw new Error(`${where}: a move writes both ends of a ${arm} element — give \`edit.start\` and \`edit.end\` together`);
342
- }
343
- if (start === undefined) {
344
- if (edit.create === undefined) {
345
- throw new Error(`${where}: \`edit\` declares neither \`create\` (what a dropped card becomes) nor the item fields a move ` +
346
- `writes (\`key\` and ${shape === "span" ? "`start` / `end`" : "`at`"}) — declare one or both`);
347
- }
348
- return undefined;
349
- }
350
- const item = itemType;
351
- if (item.type !== "Struct") {
352
- throw new Error(`${where}: a move writes fields of the item — the items in "${edit.items}" must be structs`);
353
- }
354
- if (edit.key === undefined) {
355
- throw new Error(`${where}: a move finds its element's item by key — name the item's String key field with \`edit.key\``);
356
- }
357
- const keyType = item.fields[edit.key];
358
- if (keyType === undefined || !isTypeEqual(keyType, StringType)) {
359
- throw new Error(`${where}: \`edit.key\` names "${edit.key}", which is not a String field of the item`);
360
- }
361
- const instant = (name, what) => {
362
- const type = item.fields[name];
363
- if (type === undefined || !INSTANT_FIELD_TYPES.some((t) => isTypeEqual(t, type))) {
364
- throw new Error(`${where}: \`edit.${what}\` names "${name}", which is not an instant field of the item — a DateTime, a Float or ` +
365
- "an Integer, a String, or a Plan.Types.Instant");
366
- }
367
- if (name === edit.key) {
368
- throw new Error(`${where}: \`edit.${what}\` names the key field "${name}" — a move would change the item's identity; name its instant field`);
369
- }
370
- return { name, type };
371
- };
372
- if (end !== undefined && end === start) {
373
- throw new Error(`${where}: \`edit.start\` and \`edit.end\` both name "${start}" — a move writes two different fields`);
374
- }
375
- return {
376
- item,
377
- key: edit.key,
378
- start: instant(start, shape === "span" ? "start" : "at"),
379
- end: end !== undefined ? instant(end, "end") : undefined,
380
- edits: { items: printType(toEastTypeValue(itemType)), resize: shape === "span" },
381
- };
374
+ function moveFieldsOf(read, item) {
375
+ const field = (name) => ({ name, type: item.fields[name] });
376
+ const times = read.times;
377
+ const edits = { items: printType(toEastTypeValue(item)), resize: !("at" in times) };
378
+ return "at" in times
379
+ ? { item, start: field(times.at), end: undefined, edits }
380
+ : { item, start: field(times.from), end: field(times.to), edits };
382
381
  }
383
382
  /** Each instant field type's converter, built once (#825). */
384
383
  const instantConverters = new ByType();
385
384
  /**
386
385
  * The converter from an axis instant into an item field's type (#825): the
387
- * instant on the arm the field holds — a time instant's DateTime, a number
388
- * instant's Float (an Integer field takes it rounded, half away from zero), an
389
- * ordinal instant's String, or the instant itself — else `none`.
386
+ * instant on the arm the field holds — a time instant's DateTime (an `Option`
387
+ * of one takes it as `some`, #1436), a number instant's Float (an Integer field
388
+ * takes it rounded, half away from zero), an ordinal instant's String, or the
389
+ * instant itself — else `none`.
390
390
  *
391
391
  * @param fieldType - The field's type (one of {@link INSTANT_FIELD_TYPES})
392
392
  * @returns `(instant) → Option<field>`
@@ -396,6 +396,7 @@ function instantInto(fieldType) {
396
396
  const out = OptionType(fieldType);
397
397
  const kind = fieldType.type;
398
398
  const whole = isTypeEqual(fieldType, PlanInstantType);
399
+ const optional = isTypeEqual(fieldType, OptionType(DateTimeType));
399
400
  return East.function([PlanInstantType], out, ($, t) => {
400
401
  const result = $.let(none, out);
401
402
  if (whole) {
@@ -406,6 +407,9 @@ function instantInto(fieldType) {
406
407
  time: ($2, d) => {
407
408
  if (kind === "DateTime")
408
409
  $2.assign(result, some(d));
410
+ // An Option of a DateTime holds the instant as `some` (#1436).
411
+ if (optional)
412
+ $2.assign(result, some(some(d)));
409
413
  },
410
414
  number: ($2, n) => {
411
415
  if (kind === "Float") {
@@ -425,6 +429,570 @@ function instantInto(fieldType) {
425
429
  });
426
430
  });
427
431
  }
432
+ /** An East type's name as a message reads it: `an Array`, `a Dict`. */
433
+ function anOf(type) {
434
+ const name = type.type;
435
+ return /^[AEIOU]/.test(name) ? `an ${name}` : `a ${name}`;
436
+ }
437
+ /** A row type's shape as a message reads it: its fields, `{ name, load }`, or East's summary of another type. */
438
+ function shapeName(type) {
439
+ const t = type;
440
+ if (t.type === "Recursive" && t.node !== undefined)
441
+ return shapeName(t.node);
442
+ if (t.type === "Struct" && t.fields !== undefined)
443
+ return `{ ${Object.keys(t.fields).join(", ")} }`;
444
+ return printTypeSummary(type);
445
+ }
446
+ /** The fields of an entry type — a struct's own, or a recursive entry's node's. */
447
+ function entryFieldsOf(rowType) {
448
+ const t = rowType;
449
+ if (t.type === "Struct")
450
+ return t.fields;
451
+ if (t.type === "Recursive" && t.node?.type === "Struct")
452
+ return t.node.fields;
453
+ return undefined;
454
+ }
455
+ /** The key types a values collection may be keyed by: its instants. */
456
+ const VALUE_KEY_TYPES = [DateTimeType, FloatType, IntegerType, StringType];
457
+ /**
458
+ * The collection a read names, checked against the entry type (#1436).
459
+ *
460
+ * @param rowType - The entry type
461
+ * @param read - The read
462
+ * @param where - The series, for the messages
463
+ * @returns The collection's shape
464
+ * @throws {Error} In the refusals' words: no Dict field of the row, an items collection keyed by an instant or holding no
465
+ * struct, a time that is no instant field of its items, a values collection keyed by no instant
466
+ */
467
+ function collectionOfRead(rowType, read, where) {
468
+ const what = read.kind === "items" ? "Plan.items" : "Plan.values";
469
+ const fields = entryFieldsOf(rowType);
470
+ const t = fields?.[read.field];
471
+ if (t === undefined) {
472
+ throw new Error(`${where}: ${what} reads "${read.field}", which is no field of its rows — name a Dict field of the row`);
473
+ }
474
+ if (t.type !== "Dict") {
475
+ throw new Error(`${what}: "${read.field}" is ${anOf(t)} — a collection a series reads by name is a Dict, keyed by an id, ` +
476
+ "or by its instant for values");
477
+ }
478
+ const key = t.key;
479
+ const item = t.value;
480
+ const keyName = key.type;
481
+ if (read.kind === "values") {
482
+ if (!VALUE_KEY_TYPES.some((k) => isTypeEqual(k, key))) {
483
+ throw new Error(`Plan.values: "${read.field}" is keyed by ${keyName} — values are keyed by their instant: a DateTime, a Float, ` +
484
+ "an Integer or a String");
485
+ }
486
+ return { dict: t, key, item };
487
+ }
488
+ if (!isTypeEqual(key, StringType)) {
489
+ throw new Error(`Plan.items: "${read.field}" is keyed by ${keyName} — things people drag are keyed by an id, a String: read values ` +
490
+ `with Plan.values("${read.field}", draw)`);
491
+ }
492
+ const struct = item;
493
+ if (struct.type !== "Struct" || struct.fields === undefined) {
494
+ throw new Error(`Plan.items: "${read.field}" holds ${anOf(item)}, not an item — things people drag are structs, their times fields ` +
495
+ `of them: read values with Plan.values("${read.field}", draw)`);
496
+ }
497
+ const names = "at" in read.times ? [read.times.at] : [read.times.from, read.times.to];
498
+ if (names.length === 2 && names[0] === names[1]) {
499
+ throw new Error(`Plan.items: "${read.field}" names "${names[0]}" as both its from and its to — a run's start and end are two fields of its items`);
500
+ }
501
+ for (const name of names) {
502
+ const ft = struct.fields[name];
503
+ if (ft === undefined) {
504
+ throw new Error(`Plan.items: "${read.field}" names its times by "${name}", which is no field of its items — name a time field of the item`);
505
+ }
506
+ if (!INSTANT_FIELD_TYPES.some((k) => isTypeEqual(k, ft))) {
507
+ throw new Error(`Plan.items: "${read.field}" names "${name}", ${anOf(ft)} field of its items, as a time — a time is a DateTime or an ` +
508
+ "Option of one, a Float, an Integer, a String, or a Plan.Types.Instant");
509
+ }
510
+ }
511
+ return { dict: t, key, item };
512
+ }
513
+ /** Each element's look type: its type without its key and its instants. */
514
+ const lookTypes = new Map();
515
+ /** What `draw` gives for an element of `kind`, as an East struct: the element without its key and its instants. */
516
+ function lookTypeOf(kind) {
517
+ let type = lookTypes.get(kind);
518
+ if (type === undefined) {
519
+ const spec = PLAN_LOOKS[kind];
520
+ const omit = [...(spec.key !== undefined ? [spec.key] : []), ...spec.instants];
521
+ type = StructType(Object.fromEntries(Object.entries(spec.type.fields).filter(([f]) => !omit.includes(f))));
522
+ lookTypes.set(kind, type);
523
+ }
524
+ return type;
525
+ }
526
+ /** What each element kind is called where a refusal names it. */
527
+ const ELEMENT_NAMES = {
528
+ run: "runs", decision: "decisions", port: "ports", event: "events", marker: "markers", chip: "chips", mark: "marks",
529
+ heat: "heat cells", weight: "weight cells", segment: "segment cells", table: "table cells", point: "points", band: "band points",
530
+ };
531
+ /** Each read's builds, by what it completes and the entry type. */
532
+ const readBuilds = new WeakMap();
533
+ /**
534
+ * A read built for one entry type (#1436): `(collection, entry) → elements` —
535
+ * each item an element at its times, keyed by its id, or each value one at its
536
+ * instant, its look `draw`'s. The collection is an argument, so the same build
537
+ * draws an inline row's own collection and one read a cell at a time.
538
+ *
539
+ * @param rowType - The entry type
540
+ * @param read - The read
541
+ * @param kind - What each item or value completes to
542
+ * @param where - The series, for messages
543
+ * @returns The built function
544
+ * @throws {Error} When the read does not build for the entry type, in the refusals' words
545
+ */
546
+ function buildRead(rowType, read, kind, where) {
547
+ let byKind = readBuilds.get(read);
548
+ if (byKind === undefined) {
549
+ byKind = new Map();
550
+ readBuilds.set(read, byKind);
551
+ }
552
+ let byType = byKind.get(kind);
553
+ if (byType === undefined) {
554
+ byType = new ByType();
555
+ byKind.set(kind, byType);
556
+ }
557
+ return byType.get(rowType, () => {
558
+ const shape = collectionOfRead(rowType, read, where);
559
+ const spec = PLAN_LOOKS[kind];
560
+ const elemType = spec.type;
561
+ const lookType = lookTypeOf(kind);
562
+ const recursive = rowType.type === "Recursive";
563
+ const what = read.kind === "items" ? `Plan.items("${read.field}", …)` : `Plan.values("${read.field}", …)`;
564
+ // The look, reified once: `(item or value, id or instant, entry) → look`.
565
+ const look = (() => {
566
+ try {
567
+ return East.function([shape.item, shape.key, rowType], lookType, ($, item, key, row) => {
568
+ const node = recursive ? $.let(row.unwrap()) : row;
569
+ const given = read.draw(item, key, node);
570
+ if (given === null || typeof given !== "object" || given instanceof Expr) {
571
+ throw new Error(`its draw gives the look of each of its ${ELEMENT_NAMES[kind]}, an object of their fields`);
572
+ }
573
+ return East.value(spec.look(given), lookType);
574
+ });
575
+ }
576
+ catch (err) {
577
+ const reason = err instanceof Error ? err.message : String(err);
578
+ throw new Error(`${where}: ${what} does not build for its rows (${reason})`);
579
+ }
580
+ })();
581
+ const fieldsOf = (l) => Object.fromEntries(Object.keys(lookType.fields).map((f) => [f, l[f]]));
582
+ const out = ArrayType(elemType);
583
+ if (read.kind === "values") {
584
+ const at = spec.instants[0];
585
+ return East.function([shape.dict, rowType], out, ($, coll, row) => {
586
+ const draw = $.const(look);
587
+ return coll.toArray(($2, value, key) => {
588
+ const l = $2.let(draw(value, key, row));
589
+ return East.value({ [at]: resolveInstant(key, `${what} key`), ...fieldsOf(l) }, elemType);
590
+ });
591
+ });
592
+ }
593
+ // Things people drag: each at its named times, keyed by its id. An item whose
594
+ // start is an Option holding none is drawn nowhere; one whose end holds none is
595
+ // an instant (#1434's filing).
596
+ const item = shape.item;
597
+ const times = read.times;
598
+ const names = "at" in times ? [times.at] : [times.from, times.to];
599
+ if (names.length !== spec.instants.length) {
600
+ throw new Error(spec.instants.length === 2
601
+ ? `${where}: its ${ELEMENT_NAMES[kind]} run from a start to an end — read them with Plan.items("${read.field}", { from, to }, draw)`
602
+ : `${where}: its ${ELEMENT_NAMES[kind]} sit at an instant — read them with Plan.items("${read.field}", { at }, draw)`);
603
+ }
604
+ const optional = (name) => isTypeEqual(item.fields[name], OptionType(DateTimeType));
605
+ const instantOf = (v) => resolveInstant(v, `${what} times`);
606
+ const keyField = spec.key;
607
+ return East.function([shape.dict, rowType], out, ($, coll, row) => {
608
+ const draw = $.const(look);
609
+ const result = $.let([], out);
610
+ $.for(coll, ($2, value, key) => {
611
+ const fields = value;
612
+ const id = key;
613
+ const emit = ($3, instants) => {
614
+ const l = $3.let(draw(value, id, row));
615
+ $3(result.pushLast(East.value({
616
+ [keyField]: id,
617
+ ...Object.fromEntries(spec.instants.map((name, i) => [name, instants[i]])),
618
+ ...fieldsOf(l),
619
+ }, elemType)));
620
+ };
621
+ const withStart = ($3, start) => {
622
+ if (names.length === 1) {
623
+ emit($3, [start]);
624
+ return;
625
+ }
626
+ const to = names[1];
627
+ if (!optional(to)) {
628
+ emit($3, [start, instantOf(fields[to])]);
629
+ return;
630
+ }
631
+ const end = $3.let(fields[to].match({
632
+ some: (_$4, d) => East.value(variant("time", d), PlanInstantType),
633
+ none: () => start,
634
+ }), PlanInstantType);
635
+ emit($3, [start, end]);
636
+ };
637
+ const from = names[0];
638
+ if (!optional(from)) {
639
+ withStart($2, $2.let(instantOf(fields[from]), PlanInstantType));
640
+ return;
641
+ }
642
+ $2.match(fields[from], {
643
+ some: ($3, d) => withStart($3, $3.let(East.value(variant("time", d), PlanInstantType), PlanInstantType)),
644
+ });
645
+ });
646
+ return result;
647
+ });
648
+ });
649
+ }
650
+ /** A collection field of the entry, for one row: the field read off its node. */
651
+ function collectionOn(node, field) {
652
+ return node[field];
653
+ }
654
+ /** The record of a read a series makes. */
655
+ function readRecord(rowType, read, via, where) {
656
+ return { field: read.field, read, key: collectionOfRead(rowType, read, where).key, via };
657
+ }
658
+ /**
659
+ * An element field of a series config, resolved for its entry type (#1436): a
660
+ * function of the row as before, or a read — of items for the things people
661
+ * drag (runs, decisions, tiles, chips, marks), of values for ports and
662
+ * markers.
663
+ *
664
+ * @param rowType - The entry type
665
+ * @param input - The field as given
666
+ * @param kind - What each element is
667
+ * @param where - The series, for messages
668
+ * @param via - The field's name
669
+ * @returns The field, or `undefined` when it is not given
670
+ * @throws {Error} When a read of the other sort, or a consumer, is given
671
+ */
672
+ function elementField(rowType, input, kind, where, via) {
673
+ if (input === undefined)
674
+ return undefined;
675
+ if (typeof input === "function") {
676
+ const accessor = input;
677
+ return { at: (node, key) => accessor(node, key), reads: [] };
678
+ }
679
+ const read = planReadOf(input);
680
+ if (read === undefined) {
681
+ throw new Error(`${where}: ${via} is a function of the row, or a read of its collection — ${PLAN_LOOKS[kind].key !== undefined ? "Plan.items(field, times, draw)" : "Plan.values(field, draw)"}`);
682
+ }
683
+ const keyed = PLAN_LOOKS[kind].key !== undefined;
684
+ if (keyed && read.kind !== "items") {
685
+ throw new Error(`${where}: its ${via} are things people drag, keyed by an id — read them with Plan.items(field, ${PLAN_LOOKS[kind].instants.length === 2 ? "{ from, to }" : "{ at }"}, draw)`);
686
+ }
687
+ if (!keyed && read.kind !== "values") {
688
+ throw new Error(`${where}: its ${via} have no id of their own, so each is keyed by its instant — read them with Plan.values(field, draw)`);
689
+ }
690
+ const built = buildRead(rowType, read, kind, where);
691
+ return { at: (node, _key, value) => built(collectionOn(node, read.field), value), reads: [readRecord(rowType, read, via, where)] };
692
+ }
693
+ /** What each consumer is, where a refusal names what a field takes. */
694
+ const CONSUMER_HINTS = {
695
+ heat: "Plan.heatCells, Plan.weightCells or Plan.segmentCells over Plan.values(field, draw)",
696
+ tableCells: "Plan.tableCells(Plan.values(field, draw))",
697
+ tableSeries: "Plan.tableSeries({ cells: Plan.tableCells(Plan.values(field, draw)) })",
698
+ layer: "Plan.line, Plan.area, Plan.column, Plan.scatter or Plan.band over Plan.values(field, draw)",
699
+ };
700
+ /**
701
+ * A consumer of a values read, resolved for the entry type (#1436): the row's
702
+ * values completed into its cells, and what the consumer makes of them.
703
+ *
704
+ * @param rowType - The entry type
705
+ * @param use - The consumer
706
+ * @param makes - What the field takes
707
+ * @param where - The series, for messages
708
+ * @param via - The field's name
709
+ * @returns The field
710
+ */
711
+ function useField(rowType, use, makes, where, via) {
712
+ if (use.makes !== makes)
713
+ throw new Error(`${where}: ${via} takes ${CONSUMER_HINTS[makes]}`);
714
+ const built = buildRead(rowType, use.read, use.cell, where);
715
+ // The consumer's fold and flag beside the read (#1435): what a parent subtotals its values by.
716
+ const read = { ...readRecord(rowType, use.read, via, where), ...(use.fold !== undefined ? { fold: use.fold } : {}), flagged: use.rollup };
717
+ return {
718
+ at: (node, _key, value) => use.wrap(built(collectionOn(node, use.read.field), value)),
719
+ reads: [read],
720
+ };
721
+ }
722
+ /**
723
+ * A field whose value is one consumer's (cells, a group's summary) resolved
724
+ * for the entry type (#1436): a function of the row as before, or a consumer
725
+ * of a values read.
726
+ *
727
+ * @param rowType - The entry type
728
+ * @param input - The field as given
729
+ * @param makes - What it takes
730
+ * @param where - The series, for messages
731
+ * @param via - The field's name
732
+ * @returns The field, or `undefined` when it is not given
733
+ */
734
+ function consumerField(rowType, input, makes, where, via) {
735
+ if (input === undefined)
736
+ return undefined;
737
+ if (typeof input === "function") {
738
+ const accessor = input;
739
+ return { at: (node, key) => accessor(node, key), reads: [] };
740
+ }
741
+ const use = planUseOf(input);
742
+ if (use !== undefined)
743
+ return useField(rowType, use, makes, where, via);
744
+ const read = planReadOf(input);
745
+ throw new Error(read !== undefined
746
+ ? `${where}: ${via} takes ${CONSUMER_HINTS[makes]} — give "${read.field}" to it, as ${via}: ${CONSUMER_HINTS[makes].split(" over ")[0].split(",")[0]}(Plan.values("${read.field}", draw))`
747
+ : `${where}: ${via} is a function of the row, or ${CONSUMER_HINTS[makes]}`);
748
+ }
749
+ /**
750
+ * A field whose value is a list (a table's positions, a chart's layers)
751
+ * resolved for the entry type (#1436): a function of the row as before, or a
752
+ * TS array whose consumers are built per row and whose other members pass as
753
+ * they are.
754
+ *
755
+ * @param rowType - The entry type
756
+ * @param input - The field as given
757
+ * @param makes - What its consumers make
758
+ * @param where - The series, for messages
759
+ * @param via - The field's name
760
+ * @returns The field, its value a TS array per row (each consumer's value in its place), or `undefined` when not given
761
+ */
762
+ function listField(rowType, input, makes, where, via) {
763
+ if (input === undefined)
764
+ return undefined;
765
+ if (typeof input === "function") {
766
+ const accessor = input;
767
+ return { at: (node, key) => accessor(node, key), reads: [] };
768
+ }
769
+ if (!Array.isArray(input))
770
+ throw new Error(`${where}: ${via} is a function of the row, or a list of ${CONSUMER_HINTS[makes]}`);
771
+ const members = input.map((member, i) => {
772
+ const use = planUseOf(member);
773
+ if (use !== undefined)
774
+ return useField(rowType, use, makes, where, `${via}[${i}]`);
775
+ const read = planReadOf(member);
776
+ if (read !== undefined)
777
+ throw new Error(`${where}: ${via}[${i}] takes ${CONSUMER_HINTS[makes]}, not the read "${read.field}" alone`);
778
+ return { at: () => member, reads: [] };
779
+ });
780
+ return {
781
+ at: (node, key, value) => members.map((m) => m.at(node, key, value)),
782
+ reads: members.flatMap((m) => m.reads),
783
+ };
784
+ }
785
+ /**
786
+ * A table's positions whose values take typed numbers (#1436): its `cells`
787
+ * when the series declares `edit: true`, and each `Plan.tableSeries` position
788
+ * declaring its own — each drawing every value as it is stored.
789
+ *
790
+ * @param rowType - The entry type
791
+ * @param cfg - The table's `cells`, `series` and `edit`, as given
792
+ * @param where - The series, for messages
793
+ * @returns The typed positions, in order
794
+ * @throws {Error} In the refusals' words: `edit` beside cells given as a function, or over a draw giving another number than each value
795
+ */
796
+ function tableEditsOf(rowType, cfg, where) {
797
+ const out = [];
798
+ if (cfg.edit !== undefined) {
799
+ if (cfg.edit !== true)
800
+ throw new Error(`${where}: edit is true — its cells' values take typed numbers — or left out`);
801
+ if (cfg.series !== undefined) {
802
+ throw new Error(`${where}: edit is its cells' — with positions, each declares its own: Plan.tableSeries({ cells, edit: true })`);
803
+ }
804
+ const use = planUseOf(cfg.cells);
805
+ if (use === undefined) {
806
+ throw new Error(`${where}: edit writes the values its cells read — give cells as Plan.tableCells(Plan.values(field, draw))`);
807
+ }
808
+ out.push({ position: 0, read: use.read });
809
+ }
810
+ if (Array.isArray(cfg.series)) {
811
+ cfg.series.forEach((member, position) => {
812
+ const use = planUseOf(member);
813
+ if (use?.edit === true)
814
+ out.push({ position, read: use.read });
815
+ });
816
+ }
817
+ for (const typed of out)
818
+ refuseDrawnOtherwise(rowType, typed.read, where);
819
+ return out;
820
+ }
821
+ /**
822
+ * Whether a values read's cells draw each value as it is stored (#1436,
823
+ * #1435): a `Float`'s `some(v)` (or `v`), an `Option` of a `Float`'s `v`, an
824
+ * `Integer`'s `some(v.toFloat())` (or `v.toFloat()`) — what a typed number is
825
+ * written as, and what e3 keeps a parent's subtotals of. Read off the draw's
826
+ * expression, as an accessor's field is (`childRefOf`).
827
+ *
828
+ * @param rowType - The entry type
829
+ * @param read - The values read
830
+ * @param where - The series, for messages
831
+ * @returns Whether each value draws as it is stored
832
+ * @internal
833
+ */
834
+ export function drawsAsStored(rowType, read, where) {
835
+ const shape = collectionOfRead(rowType, read, where);
836
+ const kind = isTypeEqual(shape.item, FloatType) ? "float"
837
+ : isTypeEqual(shape.item, OptionType(FloatType)) ? "option"
838
+ : isTypeEqual(shape.item, IntegerType) ? "integer" : undefined;
839
+ let asStored = false;
840
+ if (kind !== undefined) {
841
+ const recursive = rowType.type === "Recursive";
842
+ East.function([shape.item, shape.key, rowType], NullType, ($, value, at, row) => {
843
+ const node = recursive ? $.let(row.unwrap()) : row;
844
+ const given = read.draw(value, at, node);
845
+ const param = Expr.ast(value);
846
+ const astOf = (x) => (x instanceof Expr ? Expr.ast(x) : undefined);
847
+ const isValue = (x) => astOf(x) === param;
848
+ const isWidened = (x) => {
849
+ const ast = astOf(x);
850
+ return ast !== undefined && ast.ast_type === "Builtin" && ast.builtin === "IntegerToFloat" && ast.arguments?.[0] === param;
851
+ };
852
+ const drawn = given === null || typeof given !== "object" ? undefined : given.value;
853
+ const wrapped = typeof drawn === "object" && drawn !== null && !(drawn instanceof Expr) && drawn.type === "some"
854
+ ? drawn.value : undefined;
855
+ asStored = kind === "option" ? isValue(drawn)
856
+ : kind === "float" ? isValue(drawn) || isValue(wrapped)
857
+ : isWidened(drawn) || isWidened(wrapped);
858
+ return null;
859
+ });
860
+ }
861
+ return asStored;
862
+ }
863
+ /**
864
+ * Refuse a typed position whose cells draw another number than each value
865
+ * (#1436): a typed number is stored as the value, so each value must draw as
866
+ * it is ({@link drawsAsStored}).
867
+ *
868
+ * @param rowType - The entry type
869
+ * @param read - The position's values read
870
+ * @param where - The series, for the message
871
+ * @throws {Error} In the refusal's words
872
+ */
873
+ function refuseDrawnOtherwise(rowType, read, where) {
874
+ if (!drawsAsStored(rowType, read, where)) {
875
+ throw new Error(`${where}: edit writes a typed number as its readings, and its cells draw another number than each reading — ` +
876
+ "draw value: some(v), and show it another way through format");
877
+ }
878
+ }
879
+ /**
880
+ * A tag given as a literal — its name, or a variant value's case — as the
881
+ * build reads it (#1435); `undefined` for an East expression, known only as
882
+ * the Plan is evaluated.
883
+ *
884
+ * @param given - The tag, as given
885
+ * @param fallback - Its default, where it is not given
886
+ * @returns Its name, or `undefined`
887
+ */
888
+ function tagOf(given, fallback) {
889
+ if (given === undefined)
890
+ return fallback;
891
+ if (typeof given === "string")
892
+ return given;
893
+ return isVariant(given) ? given.type : undefined;
894
+ }
895
+ /**
896
+ * A series config's functions of the row (#1435), each by the field it is
897
+ * given as — its accessors, its element fields and its consumers given as
898
+ * functions, and a list's members that are — what a Plan walks for a
899
+ * collection read in a function.
900
+ *
901
+ * @param cfg - The config, as given
902
+ * @returns Its functions of the row
903
+ */
904
+ function accessorsOf(cfg) {
905
+ const out = [];
906
+ for (const [via, value] of Object.entries(cfg)) {
907
+ if (typeof value === "function")
908
+ out.push({ via, fn: value });
909
+ else if (Array.isArray(value)) {
910
+ value.forEach((member, i) => {
911
+ if (typeof member === "function")
912
+ out.push({ via: `${via}[${i}]`, fn: member });
913
+ });
914
+ }
915
+ }
916
+ return out;
917
+ }
918
+ /**
919
+ * Refuse a tree's `children` field that is not a Dict of the row's own type,
920
+ * keyed by an id (#1436).
921
+ *
922
+ * @param rowType - The entry type
923
+ * @param field - The field `children` names
924
+ * @param where - The series, for the message
925
+ */
926
+ function checkTree(rowType, field, where) {
927
+ const t = entryFieldsOf(rowType)?.[field];
928
+ if (t === undefined) {
929
+ throw new Error(`${where}: children names "${field}", which is no field of its rows — name the Dict field holding more of its ` +
930
+ "rows, or step down to rows of another type with Plan.children(field, [series…])");
931
+ }
932
+ if (t.type !== "Dict") {
933
+ throw new Error(`${where}: children names "${field}", ${anOf(t)} — a tree's children are a Dict of the row's own type, keyed by an id`);
934
+ }
935
+ if (!isTypeEqual(t.value, rowType)) {
936
+ throw new Error(`${where}: children "${field}" holds ${shapeName(t.value)}, not ${shapeName(rowType)} — step down to it with Plan.children("${field}", [series…])`);
937
+ }
938
+ if (!isTypeEqual(t.key, StringType)) {
939
+ throw new Error(`${where}: children "${field}" is keyed by ${t.key.type} — child rows are keyed by an id, a String`);
940
+ }
941
+ }
942
+ /**
943
+ * Refuse a step-down's field that is not a Dict of child rows keyed by an id
944
+ * (#1436).
945
+ *
946
+ * @param rowType - The parent entry type
947
+ * @param field - The field `Plan.children` names
948
+ * @param where - The series, for the message
949
+ */
950
+ function checkStepField(rowType, field, where) {
951
+ const t = entryFieldsOf(rowType)?.[field];
952
+ if (t === undefined) {
953
+ throw new Error(`${where}: Plan.children("${field}", …) steps down to "${field}", which is no field of its rows — name the Dict field holding the child rows`);
954
+ }
955
+ if (t.type !== "Dict") {
956
+ throw new Error(`Plan.children: "${field}" is ${anOf(t)} — child rows are a Dict, keyed by an id`);
957
+ }
958
+ if (!isTypeEqual(t.key, StringType)) {
959
+ throw new Error(`Plan.children: "${field}" is keyed by ${t.key.type} — child rows are keyed by an id, a String`);
960
+ }
961
+ const child = t.value.type;
962
+ if (child !== "Struct" && child !== "Recursive") {
963
+ throw new Error(`Plan.children: "${field}" holds ${anOf(t.value)}, not rows — child rows are structs`);
964
+ }
965
+ }
966
+ /**
967
+ * A series' `edit`, checked against the element field it writes through
968
+ * (#1436): `true` or `{ create }`, beside a read of items.
969
+ *
970
+ * @param edit - The declaration as given
971
+ * @param elements - The element field: `runs`, `events`, `chips` or `marks`
972
+ * @param times - The times those elements take
973
+ * @param input - The element field as given
974
+ * @param where - The series, for the messages
975
+ * @returns The recipe, or `undefined` when no `edit` is declared
976
+ * @throws {Error} In the refusals' words
977
+ */
978
+ function editRecipeOf(edit, elements, times, input, where) {
979
+ if (edit === undefined)
980
+ return undefined;
981
+ const create = typeof edit === "object" && edit !== null ? edit.create : undefined;
982
+ if (edit !== true && (typeof create !== "function" || Object.keys(edit).some((k) => k !== "create"))) {
983
+ throw new Error(`${where}: edit names nothing — its ${elements}' read names the collection and its times: give edit: true, or ` +
984
+ "edit: { create } to take dropped cards too");
985
+ }
986
+ const read = planReadOf(input);
987
+ if (read === undefined) {
988
+ throw new Error(`${where}: edit moves the items its ${elements} name — give ${elements} as Plan.items(field, ${times}, draw)`);
989
+ }
990
+ if (read.kind !== "items") {
991
+ throw new Error(`${where}: edit moves the items its ${elements} name, and they read values — what people move is keyed by an id: ` +
992
+ `give ${elements} as Plan.items(field, ${times}, draw)`);
993
+ }
994
+ return { edit: edit, elements, times, input };
995
+ }
428
996
  /**
429
997
  * The build spec of a data series (span / buckets / chart / heat / table /
430
998
  * cards / events / group).
@@ -434,25 +1002,12 @@ function instantInto(fieldType) {
434
1002
  * row facts through the one envelope) and the membership, collapse and
435
1003
  * children functions, each reified once and CALLED in the block's loop. The
436
1004
  * block walks the collection in order; each matched entry's row is followed by
437
- * its children's rows — a bare `children` accessor walks the entry's subtree
1005
+ * its children's rows — a tree (`children: field`) walks the entry's subtree
438
1006
  * with an explicit pre-order stack (its depth is data), a step-down calls its
439
1007
  * child series' blocks on the child collection.
440
1008
  */
441
1009
  function dataSpec(rowType, cfg, recipe) {
442
1010
  const where = `Plan.series.${recipe.arm} "${cfg.key}"`;
443
- const children = cfg.children;
444
- const steps = children === undefined || typeof children === "function"
445
- ? [] : (Array.isArray(children) ? children : [children]);
446
- for (const step of steps) {
447
- if (!isStepDown(step)) {
448
- throw new Error(`${where}: \`children\` is an accessor for more of this series, or \`Plan.children(of, [series…])\` step-downs`);
449
- }
450
- }
451
- const nested = steps.flatMap((s) => s.series.map((x) => nestedSpec(x, `${where} › children`)));
452
- // How many chart rows each entry makes (#1398): a chart series' own row, and below it a bare accessor's walk of
453
- // more of this series' rows, or a step-down's series' over the child collection.
454
- const ownCharts = recipe.arm === "chart" ? 1 : 0;
455
- const charts = entryCharts(ownCharts, children === undefined ? 0 : typeof children === "function" ? ownCharts : chartsOf(nested), cfg.match !== undefined);
456
1011
  // A recursive entry reaches the accessors as its NODE (`PlanEntryExpr`),
457
1012
  // so they read its fields directly at every depth.
458
1013
  const recursive = rowType.type === "Recursive";
@@ -461,6 +1016,33 @@ function dataSpec(rowType, cfg, recipe) {
461
1016
  : value;
462
1017
  /** An accessor over entries, applied to an entry value (its node, when recursive). */
463
1018
  const onEntry = (accessor) => (v, k) => accessor(nodeOf(v), k);
1019
+ const children = cfg.children;
1020
+ // A tree (#1436): a field of the entry holding more of its entries — or, the
1021
+ // event kinds' own (#1194), an accessor computing them.
1022
+ const treeField = typeof children === "string" ? children : undefined;
1023
+ const tree = treeField !== undefined
1024
+ ? (v) => collectionOn(v, treeField)
1025
+ : typeof children === "function" ? children : undefined;
1026
+ if (treeField !== undefined)
1027
+ checkTree(rowType, treeField, where);
1028
+ const steps = children === undefined || tree !== undefined
1029
+ ? [] : (Array.isArray(children) ? children : [children]);
1030
+ for (const step of steps) {
1031
+ if (!isStepDown(step)) {
1032
+ throw new Error(`${where}: \`children\` is a field of the row holding more of its rows, or Plan.children(field, [series…]) step-downs`);
1033
+ }
1034
+ if (step.field !== undefined)
1035
+ checkStepField(rowType, step.field, where);
1036
+ }
1037
+ /** A step-down's child collection, from the entry (its node). */
1038
+ const stepOf = (step) => (step.field !== undefined
1039
+ ? (v) => collectionOn(v, step.field)
1040
+ : step.of);
1041
+ const nested = steps.flatMap((s) => s.series.map((x) => nestedSpec(x, `${where} › children`)));
1042
+ // How many chart rows each entry makes (#1398): a chart series' own row, and below it a tree's walk of more of
1043
+ // this series' rows, or a step-down's series' over the child collection.
1044
+ const ownCharts = recipe.arm === "chart" ? 1 : 0;
1045
+ const charts = entryCharts(ownCharts, children === undefined ? 0 : tree !== undefined ? ownCharts : chartsOf(nested), cfg.match !== undefined);
464
1046
  // The removed verdict, named (#1260) — a plain JS caller would otherwise
465
1047
  // lose it silently.
466
1048
  for (const removed of ["review", "approval"]) {
@@ -468,30 +1050,25 @@ function dataSpec(rowType, cfg, recipe) {
468
1050
  throw new Error(`${where}: \`${removed}\` is removed (#1260) — a Plan approves and rejects nothing: its changes are drafts of the root's \`editing\` session`);
469
1051
  }
470
1052
  }
471
- // What a gesture writes (#880, #825) — a dropped card into `edit.items`, a
472
- // moved element into its item's fields — each a FIELD of the entry (its
473
- // node, when recursive) or of its items, checked here so a misnamed field
474
- // fails the build.
1053
+ // What a gesture writes (#880, #825, #1436) — through the collection its
1054
+ // elements' read names: a dropped card's item under its minted id, a moved
1055
+ // element's item re-timed under its id, or taken to another row.
475
1056
  const edit = recipe.edit;
476
- const nodeType = (recursive ? rowType.node : rowType);
477
- if (edit !== undefined && nodeType.type !== "Struct") {
478
- throw new Error(`${where}: \`edit\` writes a field of the entry — its entries must be structs (a recursive entry's node included)`);
479
- }
480
- const entryFields = nodeType.fields ?? {};
481
- let itemType;
482
- let move;
483
- if (edit !== undefined) {
484
- const t = entryFields[edit.items];
485
- if (t === undefined || t.type !== "Array") {
486
- throw new Error(`${where}: \`edit.items\` names "${edit.items}", which is not an Array field of the entry — a gesture writes a list of items`);
487
- }
488
- itemType = t.value;
489
- move = moveFieldsOf(edit, itemType, recipe.arm, where);
490
- }
491
- const edits = edit !== undefined
1057
+ const editRead = edit !== undefined ? planReadOf(edit.input) : undefined;
1058
+ const editShape = editRead !== undefined ? collectionOfRead(rowType, editRead, where) : undefined;
1059
+ const itemType = editShape?.item;
1060
+ const move = editRead !== undefined ? moveFieldsOf(editRead, editShape.item) : undefined;
1061
+ const create = edit !== undefined && edit.edit !== true ? edit.edit.create : undefined;
1062
+ // What a typed number writes (#1436): each typed position's collection, keyed by the bucket's start.
1063
+ const typedWrites = (recipe.values ?? []).map((typed) => {
1064
+ const shape = collectionOfRead(rowType, typed.read, where);
1065
+ return { position: typed.position, field: typed.read.field, key: shape.key, value: shape.item, keyAs: instantInto(shape.key) };
1066
+ });
1067
+ const edits = move !== undefined || typedWrites.length > 0
492
1068
  ? {
493
- drop: edit.create !== undefined,
1069
+ drop: create !== undefined,
494
1070
  move: move !== undefined ? some(move.edits) : none,
1071
+ values: typedWrites.map((typed) => ({ position: BigInt(typed.position), whole: isTypeEqual(typed.value, IntegerType) })),
495
1072
  }
496
1073
  : undefined;
497
1074
  const rows = new ByType();
@@ -514,7 +1091,7 @@ function dataSpec(rowType, cfg, recipe) {
514
1091
  ...(cfg.value !== undefined ? { value: cfg.value(entry, key) } : {}),
515
1092
  ...(recipe.swatches !== undefined ? { swatches: recipe.swatches } : {}),
516
1093
  }),
517
- kind: recipe.kind(entry, key, hasChildren),
1094
+ kind: recipe.kind(entry, key, hasChildren, value),
518
1095
  collapsed,
519
1096
  ...(recipe.pinned !== undefined ? { pinned: recipe.pinned(entry, key) } : {}),
520
1097
  ...(cfg.status !== undefined ? { status: cfg.status(entry, key) } : {}),
@@ -536,13 +1113,13 @@ function dataSpec(rowType, cfg, recipe) {
536
1113
  ? East.value(collapsed, BooleanType)
537
1114
  : East.value(collapsed(nodeOf(value), key), BooleanType)));
538
1115
  });
539
- /** A bare `children` accessor, built for entries keyed by `kt` — the child collection's type is its output. */
540
- const childOf = (kt) => building(where, kt, () => reifyAccessor([rowType, kt], onEntry(children)));
1116
+ /** The tree's children, built for entries keyed by `kt` — the child collection's type is its output. */
1117
+ const childOf = (kt) => building(where, kt, () => reifyAccessor([rowType, kt], onEntry(tree)));
541
1118
  /** The subtree walk below one entry — every level's entries in pre-order, over the child collection type. */
542
1119
  const walkFor = (cc) => walks.get(cc, () => {
543
1120
  const at = `${where} › children`;
544
1121
  const { entry, key: kc } = shapeOf(cc, at);
545
- assertEntry(entry, rowType, `${at} (a bare accessor returns more of this series' entries — step down to another type with Plan.children)`);
1122
+ assertEntry(entry, rowType, `${at} (a tree's children are more of this series' rows — step down to another type with Plan.children)`);
546
1123
  const next = childOf(kc);
547
1124
  const nextType = Expr.type(next).output;
548
1125
  if (!isTypeEqual(nextType, cc)) {
@@ -598,7 +1175,7 @@ function dataSpec(rowType, cfg, recipe) {
598
1175
  const childrenFor = (kt) => kids.get(kt, () => {
599
1176
  if (children === undefined)
600
1177
  return undefined;
601
- if (typeof children === "function") {
1178
+ if (tree !== undefined) {
602
1179
  const of = childOf(kt);
603
1180
  const cc = Expr.type(of).output;
604
1181
  const walk = walkFor(cc);
@@ -606,7 +1183,7 @@ function dataSpec(rowType, cfg, recipe) {
606
1183
  }
607
1184
  const parts = steps.map((step, i) => {
608
1185
  const at = `${where} › children[${i}]`;
609
- const of = building(at, kt, () => reifyAccessor([rowType, kt], onEntry(step.of)));
1186
+ const of = building(at, kt, () => reifyAccessor([rowType, kt], onEntry(stepOf(step))));
610
1187
  const cc = Expr.type(of).output;
611
1188
  const blocks = step.series.map((s) => nestedSpec(s, at).emitter(cc, at));
612
1189
  return { of, cc, blocks };
@@ -622,20 +1199,20 @@ function dataSpec(rowType, cfg, recipe) {
622
1199
  return out;
623
1200
  });
624
1201
  });
625
- // ── Writing a gesture back (#880) ─────────────────────────────────────
1202
+ // ── Writing a gesture back (#880, #1436) ──────────────────────────────
626
1203
  // The entry a writer is handed is DETACHED (the series value's `write`
627
1204
  // copies it), so a nested entry is written in place in its collection and
628
- // a dropped card's item joins its list in place.
1205
+ // an item joins its collection in place, under its id.
629
1206
  const owns = new ByType();
630
1207
  const writers = new ByType();
631
1208
  const entryAccess = (value) => nodeOf(value);
632
1209
  /** The item a dropped card becomes, for entries keyed by `kt`. */
633
- const createFor = (kt, create) => building(where, kt, () => East.function([PlanDropType, rowType, kt], itemType, (_$, drop, value, key) => create(drop, nodeOf(value), key)));
1210
+ const createFor = (kt, make) => building(where, kt, () => East.function([PlanDropType, rowType, kt], itemType, (_$, drop, value, key) => make(drop, nodeOf(value), key)));
634
1211
  /**
635
- * An item at a move's instants (#825) — its declared instant fields set
636
- * from the axis's instants, each on its own arm, every other field as it
637
- * was; `none` when an instant rides an arm the field cannot hold (a time
638
- * instant into a Float field).
1212
+ * An item at a move's instants (#825, #1436) — the instant fields its read
1213
+ * names set from the axis's instants, each on its own arm, every other
1214
+ * field as it was; `none` when an instant rides an arm the field cannot
1215
+ * hold (a time instant into a Float field).
639
1216
  */
640
1217
  const placed = move === undefined ? undefined : (() => {
641
1218
  const m = move;
@@ -667,92 +1244,74 @@ function dataSpec(rowType, cfg, recipe) {
667
1244
  });
668
1245
  })();
669
1246
  /**
670
- * Whether a list holds an item keyed `k` (#825) — the key a move finds its
671
- * item by, so a row's list keeps its keys unique: a card whose item repeats
672
- * one, or a move onto a row already holding the moved item's key, is
673
- * refused rather than leaving two items one key names.
674
- */
675
- const holds = move === undefined ? undefined : (list, k) => list.some((_$, item) => East.equal(item[move.key], k));
676
- /**
677
- * This series' own edit on one of its entries — the row at `path` is its
678
- * own: a dropped card's item into its list, and a moved element's item
679
- * re-timed where it is, taken out of the list, or put into it (#825).
1247
+ * This series' own edit on one of its entries (#1436) — the row at `path`
1248
+ * is its own: a dropped card's item into the collection its elements'
1249
+ * read names, under the id the Plan minted; a moved element's item, found
1250
+ * by its id (the element's key), re-timed where it is, taken out of the
1251
+ * collection, or put into it under the same id. A collection already
1252
+ * holding the id refuses the item. A number typed into one of its table
1253
+ * positions sets that position's value at the bucket's start.
680
1254
  */
681
1255
  const ownFor = (kt) => owns.get(kt, () => {
682
- if (edit === undefined)
1256
+ const moves = editRead !== undefined && placed !== undefined && move !== undefined;
1257
+ if (!moves && typedWrites.length === 0)
683
1258
  return undefined;
684
- const create = edit.create !== undefined ? createFor(kt, edit.create) : undefined;
1259
+ const made = moves && create !== undefined ? createFor(kt, create) : undefined;
1260
+ const field = editRead?.field ?? "";
1261
+ const item = move?.item;
685
1262
  return East.function([rowType, kt, PathType, PlanGestureType, PlanCarriedType], OptionType(rowType), ($, value, key, path, gesture, carried) => {
686
- const make = create === undefined ? undefined : $.const(create);
687
- const place = placed === undefined ? undefined : $.const(placed);
1263
+ const make = made === undefined ? undefined : $.const(made);
1264
+ const place = moves ? $.const(placed) : undefined;
688
1265
  const result = $.let(none, OptionType(rowType));
689
1266
  $.match(gesture, {
690
1267
  drop: ($2, drop) => {
691
- if (make === undefined || edit === undefined)
1268
+ if (make === undefined)
692
1269
  return;
693
- const list = $2.let(entryAccess(value)[edit.items]);
694
- const item = $2.let(make(drop, value, key), itemType);
695
- const add = ($3) => {
696
- $3(list.pushLast(item));
1270
+ const coll = $2.let(entryAccess(value)[field]);
1271
+ // The minted id, unless the collection already holds it.
1272
+ $2.if(coll.has(drop.id).not(), ($3) => {
1273
+ $3(coll.insert(drop.id, make(drop, value, key)));
697
1274
  $3.assign(result, some(value));
698
- };
699
- // With the item's key declared, a card whose item
700
- // repeats a key in the list is refused (#825).
701
- if (holds === undefined || move === undefined)
702
- add($2);
703
- else
704
- $2.if(holds(list, item[move.key]).not(), add);
1275
+ });
705
1276
  },
706
1277
  move: ($2, mv) => {
707
- if (place === undefined || move === undefined || edit === undefined)
1278
+ if (place === undefined || item === undefined)
708
1279
  return;
709
- const keyField = move.key;
710
- const itemOf = move.item;
711
1280
  const here = $2.let(East.value(variant("entry", { series: cfg.key, path }), PlanRowIdType), PlanRowIdType);
712
- const list = $2.let(entryAccess(value)[edit.items]);
713
- // The element's item, by its key.
714
- const at = $2.let(list.firstMap(($3, item, i) => East.equal(item[keyField], mv.key).ifElse(() => East.value(some(i), OptionType(IntegerType)), () => East.value(none, OptionType(IntegerType)))), OptionType(IntegerType));
1281
+ const coll = $2.let(entryAccess(value)[field]);
715
1282
  $2.if(East.equal(here, mv.from).and(() => East.equal(here, mv.to)), ($3) => {
716
- // Along its row, or resized: its instants set where it is.
717
- $3.match(at, {
718
- some: ($4, i) => {
719
- const moved = $4.let(place(list.get(i), mv.start, mv.end), OptionType(itemOf));
720
- $4.match(moved, {
721
- some: ($5, item) => {
722
- $5(list.update(i, item));
723
- $5.assign(result, some(value));
724
- },
725
- });
726
- },
1283
+ // Along its row, or resized: its times set where it is.
1284
+ $3.if(coll.has(mv.key), ($4) => {
1285
+ const moved = $4.let(place(coll.get(mv.key), mv.start, mv.end), OptionType(item));
1286
+ $4.match(moved, {
1287
+ some: ($5, next) => {
1288
+ $5(coll.update(mv.key, next));
1289
+ $5.assign(result, some(value));
1290
+ },
1291
+ });
727
1292
  });
728
1293
  }).else(($3) => {
729
- // Its source: taken out of the list, and carried.
1294
+ // Its source: taken out of the collection, and carried.
730
1295
  $3.if(East.equal(here, mv.from), ($4) => {
731
- $4.match(at, {
732
- some: ($5, i) => {
733
- $5(carried.update(East.value(some(East.Blob.encodeBeast(list.get(i), "v2")), OptionType(BlobType))));
734
- const before = $5.let(list.slice(0n, i));
735
- const after = $5.let(list.slice(i.add(1n), list.size()));
736
- $5(list.clear());
737
- $5(list.append(before));
738
- $5(list.append(after));
739
- $5.assign(result, some(value));
740
- },
1296
+ $4.if(coll.has(mv.key), ($5) => {
1297
+ $5(carried.update(East.value(some(East.Blob.encodeBeast(coll.get(mv.key), "v2")), OptionType(BlobType))));
1298
+ $5(coll.delete(mv.key));
1299
+ $5.assign(result, some(value));
741
1300
  });
742
1301
  });
743
- // Its target: the carried item put in, at its new
744
- // instants — unless the list already holds its key,
745
- // when it is put nowhere and the move lands nowhere.
1302
+ // Its target: the carried item put in under its id, at its new
1303
+ // times — unless the collection already holds the id, when it
1304
+ // is put nowhere and the move lands nowhere.
746
1305
  $3.if(East.equal(here, mv.to), ($4) => {
747
1306
  const held = $4.let(carried.get(), OptionType(BlobType));
748
1307
  $4.match(held, {
749
1308
  some: ($5, bytes) => {
750
- $5.if(holds(list, mv.key).not(), ($6) => {
751
- const item = $6.let(bytes.decodeBeast(itemOf, "v2"), itemOf);
752
- const moved = $6.let(place(item, mv.start, mv.end), OptionType(itemOf));
1309
+ $5.if(coll.has(mv.key).not(), ($6) => {
1310
+ const taken = $6.let(bytes.decodeBeast(item, "v2"), item);
1311
+ const moved = $6.let(place(taken, mv.start, mv.end), OptionType(item));
753
1312
  $6.match(moved, {
754
- some: ($7, placedItem) => {
755
- $7(list.pushLast(placedItem));
1313
+ some: ($7, next) => {
1314
+ $7(coll.insert(mv.key, next));
756
1315
  $7(carried.update(East.value(none, OptionType(BlobType))));
757
1316
  $7.assign(result, some(value));
758
1317
  },
@@ -763,11 +1322,55 @@ function dataSpec(rowType, cfg, recipe) {
763
1322
  });
764
1323
  });
765
1324
  },
1325
+ // A number typed into a cell (#1436): its position's value at the bucket's start set, or added
1326
+ // where the collection holds none; cleared, deleted. An Integer collection takes a whole number,
1327
+ // and an instant on an arm its keys cannot hold writes nothing.
1328
+ value: ($2, typed) => {
1329
+ if (typedWrites.length === 0)
1330
+ return;
1331
+ const here = $2.let(East.value(variant("entry", { series: cfg.key, path }), PlanRowIdType), PlanRowIdType);
1332
+ $2.if(East.equal(here, typed.row), ($3) => {
1333
+ for (const write of typedWrites) {
1334
+ $3.if(typed.position.equal(BigInt(write.position)), ($4) => {
1335
+ const keyAs = $4.const(write.keyAs);
1336
+ const at = $4.let(keyAs(typed.at), OptionType(write.key));
1337
+ $4.match(at, {
1338
+ some: ($5, k) => {
1339
+ const coll = $5.let(entryAccess(value)[write.field]);
1340
+ $5.match(typed.value, {
1341
+ some: ($6, n) => {
1342
+ const put = ($7, stored) => {
1343
+ $7(coll.insertOrUpdate(k, stored));
1344
+ $7.assign(result, some(value));
1345
+ };
1346
+ if (isTypeEqual(write.value, IntegerType)) {
1347
+ $6.if(n.remainder(1).equal(0), ($7) => { put($7, n.toInteger()); });
1348
+ }
1349
+ else if (isTypeEqual(write.value, OptionType(FloatType))) {
1350
+ put($6, some(n));
1351
+ }
1352
+ else {
1353
+ put($6, n);
1354
+ }
1355
+ },
1356
+ none: ($6) => {
1357
+ $6.if(coll.has(k), ($7) => {
1358
+ $7(coll.delete(k));
1359
+ $7.assign(result, some(value));
1360
+ });
1361
+ },
1362
+ });
1363
+ },
1364
+ });
1365
+ });
1366
+ }
1367
+ });
1368
+ },
766
1369
  });
767
1370
  return result;
768
1371
  });
769
1372
  });
770
- /** The walk's writer — a bare `children` accessor: the rows below an entry are this series' own. */
1373
+ /** The tree's writer: the rows below an entry are this series' own. */
771
1374
  const walkWriterFor = (kt) => {
772
1375
  const at = `${where} › children`;
773
1376
  const of = childOf(kt);
@@ -776,10 +1379,10 @@ function dataSpec(rowType, cfg, recipe) {
776
1379
  const own = ownFor(kc);
777
1380
  if (own === undefined)
778
1381
  return undefined;
779
- const ref = childRefOf(rowType, kt, children);
1382
+ const ref = treeField !== undefined ? { kind: "field", field: treeField } : childRefOf(rowType, kt, tree);
780
1383
  if (ref === undefined) {
781
1384
  throw new Error(`${at}: this series takes gestures, and one on a child row is written back into its entry through ` +
782
- "`children` — which must read a field of the entry (`r => r.children`), not compute a collection");
1385
+ "`children` — which must read a field of the entry, not compute a collection");
783
1386
  }
784
1387
  const ops = collectionOps(cc);
785
1388
  const collOf = (entry) => (ref.kind === "field" ? entryAccess(entry)[ref.field] : nodeOf(entry));
@@ -824,7 +1427,7 @@ function dataSpec(rowType, cfg, recipe) {
824
1427
  const stepsWriterFor = (kt) => {
825
1428
  const parts = steps.flatMap((step, i) => {
826
1429
  const at = `${where} › children[${i}]`;
827
- const accessor = step.of;
1430
+ const accessor = stepOf(step);
828
1431
  const of = building(at, kt, () => reifyAccessor([rowType, kt], onEntry(accessor)));
829
1432
  const cc = Expr.type(of).output;
830
1433
  const writes = step.series
@@ -832,11 +1435,11 @@ function dataSpec(rowType, cfg, recipe) {
832
1435
  .filter((w) => w !== undefined);
833
1436
  if (writes.length === 0)
834
1437
  return [];
835
- const ref = childRefOf(rowType, kt, accessor);
1438
+ const ref = step.field !== undefined ? { kind: "field", field: step.field } : childRefOf(rowType, kt, accessor);
836
1439
  if (ref === undefined) {
837
1440
  throw new Error(`${at}: a series under this step-down takes gestures, and one on its rows is written back into ` +
838
- "the entry through `Plan.children(of, …)` — `of` must read a field of the entry (`r => r.presses`) " +
839
- "or be the entry itself (`g => g`), not compute a collection");
1441
+ "the entry through the step-down — which must read a field of the entry, or be the entry itself, not " +
1442
+ "compute a collection");
840
1443
  }
841
1444
  const { entry: childType, key: kc } = shapeOf(cc, at);
842
1445
  return [{ ref, cc, kc, childType, writes }];
@@ -875,7 +1478,7 @@ function dataSpec(rowType, cfg, recipe) {
875
1478
  const writerFor = (kt) => writers.get(kt, () => {
876
1479
  const own = ownFor(kt);
877
1480
  const below = children === undefined ? undefined
878
- : typeof children === "function" ? walkWriterFor(kt) : stepsWriterFor(kt);
1481
+ : tree !== undefined ? walkWriterFor(kt) : stepsWriterFor(kt);
879
1482
  if (own === undefined && below === undefined)
880
1483
  return undefined;
881
1484
  return East.function([rowType, kt, PathType, IntegerType, StringType, PlanGestureType, PlanCarriedType], OptionType(rowType), ($, value, key, path, depth, series, gesture, carried) => {
@@ -901,6 +1504,21 @@ function dataSpec(rowType, cfg, recipe) {
901
1504
  nested,
902
1505
  nests: children !== undefined,
903
1506
  charts,
1507
+ reads: recipe.reads ?? [],
1508
+ nest: {
1509
+ rowType,
1510
+ keyType: cfg.keyType ?? StringType,
1511
+ accessors: accessorsOf(cfg),
1512
+ tree: treeField,
1513
+ steps: steps.map((step, i) => ({
1514
+ field: step.field, of: step.of,
1515
+ series: step.series.map((x) => nestedSpec(x, `${where} › children[${i}]`)),
1516
+ })),
1517
+ members: [],
1518
+ aggregate: recipe.aggregate,
1519
+ subtotalsOwn: recipe.subtotalsOwn === true,
1520
+ rollup: recipe.rollup,
1521
+ },
904
1522
  emitter(collection, at) {
905
1523
  return emitters.get(collection, () => {
906
1524
  const { entry, key: kt } = shapeOf(collection, at);
@@ -993,10 +1611,13 @@ function viewsSpec(rowType, cfg, members) {
993
1611
  const emitters = new ByType();
994
1612
  const blockLists = new ByType();
995
1613
  const writerLists = new ByType();
996
- // ── A bare `children` accessor: more of the views' own entries (#1219) ──
997
- const bare = typeof cfg.children === "function"
998
- ? cfg.children
999
- : undefined;
1614
+ // ── A tree: more of the views' own entries (#1219, #1436) — a field of the
1615
+ // entry, or the event kinds' accessor computing them (#1194) ──
1616
+ const children = cfg.children;
1617
+ const treeField = typeof children === "string" ? children : undefined;
1618
+ const bare = treeField !== undefined
1619
+ ? (v) => collectionOn(v, treeField)
1620
+ : typeof children === "function" ? children : undefined;
1000
1621
  const recursive = rowType.type === "Recursive";
1001
1622
  const nodeOf = (value) => recursive
1002
1623
  ? value.unwrap()
@@ -1015,7 +1636,7 @@ function viewsSpec(rowType, cfg, members) {
1015
1636
  const walkFor = (cc) => walks.get(cc, () => {
1016
1637
  const at = `${where} › children`;
1017
1638
  const { entry, key: kc } = shapeOf(cc, at);
1018
- assertEntry(entry, rowType, `${at} (a bare accessor returns more of this series' entries — step down to another type with Plan.children)`);
1639
+ assertEntry(entry, rowType, `${at} (a tree's children are more of this series' rows — step down to another type with Plan.children)`);
1019
1640
  const next = childOf(kc);
1020
1641
  const nextType = Expr.type(next).output;
1021
1642
  if (!isTypeEqual(nextType, cc)) {
@@ -1123,10 +1744,10 @@ function viewsSpec(rowType, cfg, members) {
1123
1744
  });
1124
1745
  if (owns.length === 0)
1125
1746
  return undefined;
1126
- const ref = childRefOf(rowType, kt, bare);
1747
+ const ref = treeField !== undefined ? { kind: "field", field: treeField } : childRefOf(rowType, kt, bare);
1127
1748
  if (ref === undefined) {
1128
1749
  throw new Error(`${at}: a member takes gestures, and one on a child row is written back into its entry through ` +
1129
- "`children` — which must read a field of the entry (`r => r.children`), not compute a collection");
1750
+ "`children` — which must read a field of the entry, not compute a collection");
1130
1751
  }
1131
1752
  const ops = collectionOps(cc);
1132
1753
  const collOf = (value) => (ref.kind === "field" ? entryAccess(value)[ref.field] : nodeOf(value));
@@ -1170,7 +1791,7 @@ function viewsSpec(rowType, cfg, members) {
1170
1791
  });
1171
1792
  });
1172
1793
  // How many chart rows each entry makes (#1398): a row a member — a chart member's own `match` deciding by entry
1173
- // whether its row shows — and below them a bare accessor's walk of more of the views' entries, each its members'
1794
+ // whether its row shows — and below them a tree's walk of more of the views' entries, each its members'
1174
1795
  // rows, or a step-down's series' over the child collection.
1175
1796
  const memberCharts = chartsOf(memberSpecs);
1176
1797
  const charts = entryCharts(memberCharts, cfg.children === undefined ? 0 : bare !== undefined ? memberCharts : chartsOf(host.nested), cfg.match !== undefined);
@@ -1181,6 +1802,9 @@ function viewsSpec(rowType, cfg, members) {
1181
1802
  nested: [...memberSpecs, ...host.nested],
1182
1803
  nests: cfg.children !== undefined,
1183
1804
  charts,
1805
+ reads: [],
1806
+ // Its members read its own entries; its children are the host envelope's (#1435).
1807
+ nest: { ...host.nest, members: memberSpecs },
1184
1808
  emitter(collection, at) {
1185
1809
  return emitters.get(collection, () => {
1186
1810
  const { entry, key: kt } = shapeOf(collection, at);
@@ -1323,6 +1947,12 @@ function sectionSpec(cfg, members) {
1323
1947
  nests: false,
1324
1948
  // Each entry makes its members' rows; the header is no entry's (#1398).
1325
1949
  charts: chartsOf(memberSpecs),
1950
+ reads: [],
1951
+ // Its members read the section's entries; it reads nothing of its own (#1435).
1952
+ nest: {
1953
+ rowType: undefined, keyType: StringType, accessors: [], tree: undefined, steps: [], members: memberSpecs,
1954
+ aggregate: undefined, subtotalsOwn: false, rollup: undefined,
1955
+ },
1326
1956
  emitter(collection, at) {
1327
1957
  return emitters.get(collection, () => {
1328
1958
  const blocks = memberSpecs.map((m) => m.emitter(collection, `${at} › ${where}`));
@@ -1393,6 +2023,7 @@ function rowsSpec(identity, rows) {
1393
2023
  // Its rows' kinds are an East value the build does not read (#1398): laid out below an entry, their chart rows are
1394
2024
  // not known.
1395
2025
  charts: undefined,
2026
+ reads: [],
1396
2027
  emitter,
1397
2028
  // A FIXED block: no entry produces hand-built rows, so every window
1398
2029
  // serves them alike and the canvas draws them once (#823).
@@ -1520,17 +2151,34 @@ export function applySeriesValue(s, data) {
1520
2151
  }
1521
2152
  /**
1522
2153
  * Check a TS series list before it is applied — its keys unique across the
1523
- * whole tree. An East expression list is checked at render (a repeated id is
1524
- * a row diagnostic), since its series are not known until it runs.
2154
+ * whole tree, and its values collections keyed by the axis's instants (#1436).
2155
+ * An East expression list is checked at render (a repeated id is a row
2156
+ * diagnostic), since its series are not known until it runs.
1525
2157
  *
1526
2158
  * @param series - The `series` input
1527
2159
  * @param where - Who is asking, for the message
2160
+ * @param axisKind - The axis's kind, when its declaration says it
1528
2161
  */
1529
- export function checkSeries(series, where) {
2162
+ export function checkSeries(series, where, axisKind) {
1530
2163
  if (!Array.isArray(series))
1531
2164
  return;
1532
2165
  const specs = series.map((s) => specOf(s)).filter((s) => s !== undefined);
1533
2166
  assertUniqueKeys(specs, where);
2167
+ // A values collection is keyed by its instant (#1436): one keyed by String
2168
+ // rides only an ordinal axis.
2169
+ if (axisKind === undefined || axisKind === "ordinal")
2170
+ return;
2171
+ const walk = (spec) => {
2172
+ for (const use of spec.reads) {
2173
+ if (use.read.kind !== "values" || !isTypeEqual(use.key, StringType))
2174
+ continue;
2175
+ throw new Error(`Plan.values: "${use.field}" is keyed by String, and the axis is ${axisKind} — values are keyed by their ` +
2176
+ `instant, ${axisKind === "time" ? "a DateTime" : "a Float or an Integer"} here: read things people drag ` +
2177
+ `with Plan.items("${use.field}", times, draw)`);
2178
+ }
2179
+ spec.nested.forEach(walk);
2180
+ };
2181
+ specs.forEach(walk);
1534
2182
  }
1535
2183
  /**
1536
2184
  * Reads a series value's facts off the spec its builder recorded (#1190).
@@ -1669,9 +2317,6 @@ export function seriesWriteFn(series, collection) {
1669
2317
  return result;
1670
2318
  });
1671
2319
  }
1672
- // ============================================================================
1673
- // Builders — Plan.series.*
1674
- // ============================================================================
1675
2320
  /**
1676
2321
  * A span series — one state-run row per entry; a row with children shows its
1677
2322
  * own runs plus bands over its subtree's runs (`rollup`) — each band sums its
@@ -1680,27 +2325,46 @@ export function seriesWriteFn(series, collection) {
1680
2325
  * @typeParam R - The entry type
1681
2326
  * @typeParam K - The axis kind the runs imply (inferred from `runs`; `never` when erased)
1682
2327
  * @typeParam KT - The entries' key type (`keyType`; `String` by default)
2328
+ * @typeParam FR - The collection its runs read
2329
+ * @typeParam UR - What its runs read
2330
+ * @typeParam UD - What its decisions read
2331
+ * @typeParam UP - What its ports read
2332
+ * @typeParam CT - A tree's field, as `children` names it
2333
+ * @typeParam CU - One step-down's reads
2334
+ * @typeParam CF - The folds of its cells
2335
+ * @typeParam CS - Several step-downs' reads
2336
+ * @typeParam RL - Its `rollup`, as written
1683
2337
  * @param rowType - The entry type (every builder takes it first — the `Slice.config` shape)
1684
- * @param config - The accessors and nesting ({@link PlanSpanSeriesConfig})
1685
- * @returns A series value, branded with its kind
2338
+ * @param config - The reads, accessors and nesting ({@link PlanSpanSeriesConfig})
2339
+ * @returns A series value, branded with its kind and what it reads
1686
2340
  */
1687
2341
  export function createSeriesSpan(rowType, config) {
1688
2342
  const cfg = config;
2343
+ const where = `Plan.series.span "${cfg.key}"`;
2344
+ const edit = editRecipeOf(cfg.edit, "runs", "{ from, to }", cfg.runs, where);
1689
2345
  // A row rolls up its children: its own (`children`), or — declared on a
1690
2346
  // views member — the ones the views nest under it (#1219).
1691
2347
  const rollup = cfg.children !== undefined || cfg.rollup !== undefined ? resolveTag(cfg.rollup ?? "union", PlanRollupType) : undefined;
2348
+ const runs = elementField(rowType, cfg.runs, "run", where, "runs");
2349
+ if (runs === undefined)
2350
+ throw new Error(`${where}: give its runs — Plan.items(field, { from, to }, draw), or a function of the row`);
2351
+ const decisions = elementField(rowType, cfg.decisions, "decision", where, "decisions");
2352
+ const ports = elementField(rowType, cfg.ports, "port", where, "ports");
1692
2353
  const spec = dataSpec(rowType, cfg, {
1693
2354
  arm: "span",
1694
- kind: (value, key, hasChildren) => spanKind({
1695
- runs: cfg.runs(value, key),
1696
- ...(cfg.decisions !== undefined ? { decisions: cfg.decisions(value, key) } : {}),
1697
- ...(cfg.ports !== undefined ? { ports: cfg.ports(value, key) } : {}),
2355
+ kind: (node, key, hasChildren, value) => spanKind({
2356
+ runs: runs.at(node, key, value),
2357
+ ...(decisions !== undefined ? { decisions: decisions.at(node, key, value) } : {}),
2358
+ ...(ports !== undefined ? { ports: ports.at(node, key, value) } : {}),
1698
2359
  }, rollup === undefined
1699
2360
  ? East.value(none, OptionType(PlanRollupType))
1700
2361
  // A row rolls up only when it HAS child rows — a leaf's bands
1701
2362
  // would repeat its own runs.
1702
2363
  : hasChildren.ifElse(() => East.value(some(rollup), OptionType(PlanRollupType)), () => East.value(none, OptionType(PlanRollupType)))),
1703
- edit: cfg.edit,
2364
+ edit,
2365
+ reads: [...runs.reads, ...(decisions?.reads ?? []), ...(ports?.reads ?? [])],
2366
+ // Its runs roll up into its bands where it has children or declares a rollup (#1435).
2367
+ ...(cfg.children !== undefined || cfg.rollup !== undefined ? { rollup: tagOf(cfg.rollup, "union") } : {}),
1704
2368
  });
1705
2369
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1706
2370
  }
@@ -1711,21 +2375,33 @@ export function createSeriesSpan(rowType, config) {
1711
2375
  * @typeParam R - The entry type
1712
2376
  * @typeParam K - The axis kind the cells imply (inferred from `cells`; `never` when erased)
1713
2377
  * @typeParam KT - The entries' key type
2378
+ * @typeParam UC - What its cells read
2379
+ * @typeParam FO - How a bucket folds them
2380
+ * @typeParam A - Its `aggregate`, as written
2381
+ * @typeParam CT - A tree's field, as `children` names it
2382
+ * @typeParam CU - One step-down's reads
2383
+ * @typeParam CF - The folds of its cells
2384
+ * @typeParam CS - Several step-downs' reads
1714
2385
  * @param rowType - The entry type
1715
- * @param config - The accessors and nesting ({@link PlanHeatSeriesConfig})
1716
- * @returns A series value, branded with its kind
2386
+ * @param config - The reads, accessors and nesting ({@link PlanHeatSeriesConfig})
2387
+ * @returns A series value, branded with its kind, what it reads and the fold of its cells
1717
2388
  */
1718
2389
  export function createSeriesHeat(rowType, config) {
1719
2390
  const cfg = config;
2391
+ const where = `Plan.series.heat "${cfg.key}"`;
1720
2392
  const aggregate = cfg.children !== undefined
1721
2393
  ? East.value(some(resolveTag(cfg.aggregate ?? "mean", PlanAggregateType)), OptionType(PlanAggregateType))
1722
2394
  : East.value(none, OptionType(PlanAggregateType));
2395
+ const cells = consumerField(rowType, cfg.cells, "heat", where, "cells");
1723
2396
  const spec = dataSpec(rowType, cfg, {
1724
2397
  arm: "heat",
1725
- kind: (value, key) => heatKind({
1726
- cells: cfg.cells(value, key),
2398
+ kind: (node, key, _hasChildren, value) => heatKind({
2399
+ ...(cells !== undefined ? { cells: cells.at(node, key, value) } : {}),
1727
2400
  ...(cfg.scale !== undefined ? { scale: cfg.scale } : {}),
1728
2401
  }, aggregate),
2402
+ reads: cells?.reads ?? [],
2403
+ // Its row derives its children's numbers by its aggregate: a tree's own values, or a step-down's (#1435).
2404
+ ...(cfg.children !== undefined ? { aggregate: tagOf(cfg.aggregate, "mean"), subtotalsOwn: true } : {}),
1729
2405
  });
1730
2406
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1731
2407
  }
@@ -1736,28 +2412,48 @@ export function createSeriesHeat(rowType, config) {
1736
2412
  * @typeParam R - The entry type
1737
2413
  * @typeParam K - The axis kind the cells imply (inferred from `cells` / `series`; `never` when erased)
1738
2414
  * @typeParam KT - The entries' key type
2415
+ * @typeParam UC - What its `cells` read
2416
+ * @typeParam S - Its `series` positions, as written
2417
+ * @typeParam FO - Its `cells`' fold, as written
2418
+ * @typeParam A - Its `aggregate`, as written
2419
+ * @typeParam CT - A tree's field, as `children` names it
2420
+ * @typeParam CU - One step-down's reads
2421
+ * @typeParam CF - The folds of its cells
2422
+ * @typeParam CS - Several step-downs' reads
1739
2423
  * @param rowType - The entry type
1740
- * @param config - The accessors and nesting ({@link PlanTableSeriesOfConfig})
1741
- * @returns A series value, branded with its kind
2424
+ * @param config - The reads, accessors and nesting ({@link PlanTableSeriesOfConfig})
2425
+ * @returns A series value, branded with its kind, what it reads and the folds of its positions
1742
2426
  */
1743
2427
  export function createSeriesTable(rowType, config) {
1744
2428
  const cfg = config;
2429
+ const where = `Plan.series.table "${cfg.key}"`;
1745
2430
  if (cfg.cells !== undefined && cfg.series !== undefined) {
1746
- throw new Error(`Plan.series.table "${cfg.key}": pass \`cells\` (one plain value series) OR \`series\` — not both`);
2431
+ throw new Error(`${where}: pass \`cells\` (one plain value series) OR \`series\` — not both`);
1747
2432
  }
1748
2433
  const aggregate = cfg.children !== undefined
1749
2434
  ? East.value(some(resolveTag(cfg.aggregate ?? "sum", TableAggregateType)), OptionType(TableAggregateType))
1750
2435
  : East.value(none, OptionType(TableAggregateType));
2436
+ const cells = consumerField(rowType, cfg.cells, "tableCells", where, "cells");
2437
+ // Its cells fold as the row declares, `sum` unless it says (#1436): what a parent subtotals them by (#1435, #1438).
2438
+ const rowFold = tagOf(cfg.fold, "sum");
2439
+ const cellReads = (cells?.reads ?? []).map((read) => (read.fold !== undefined || rowFold === undefined ? read : { ...read, fold: rowFold }));
2440
+ const positions = listField(rowType, cfg.series, "tableSeries", where, "series");
2441
+ // The positions a typed number writes (#1436), each read checked as it built above.
2442
+ const values = tableEditsOf(rowType, cfg, where);
1751
2443
  const spec = dataSpec(rowType, cfg, {
1752
2444
  arm: "table",
1753
- kind: (value, key) => tableKind({
1754
- ...(cfg.cells !== undefined ? { cells: cfg.cells(value, key) } : {}),
2445
+ kind: (node, key, _hasChildren, value) => tableKind({
2446
+ ...(cells !== undefined ? { cells: cells.at(node, key, value) } : {}),
1755
2447
  ...(cfg.fold !== undefined ? { fold: cfg.fold } : {}),
1756
- ...(cfg.series !== undefined ? { series: cfg.series(value, key) } : {}),
2448
+ ...(positions !== undefined ? { series: positions.at(node, key, value) } : {}),
1757
2449
  ...(cfg.split !== undefined ? { split: cfg.split } : {}),
1758
2450
  ...(cfg.format !== undefined ? { format: cfg.format } : {}),
1759
2451
  ...(cfg.emphasis !== undefined ? { emphasis: cfg.emphasis } : {}),
1760
2452
  }, aggregate),
2453
+ reads: [...cellReads, ...(positions?.reads ?? [])],
2454
+ values,
2455
+ // Its row derives its children's numbers by its aggregate: a tree's own values, or a step-down's (#1435).
2456
+ ...(cfg.children !== undefined ? { aggregate: tagOf(cfg.aggregate, "sum"), subtotalsOwn: true } : {}),
1761
2457
  });
1762
2458
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1763
2459
  }
@@ -1767,20 +2463,34 @@ export function createSeriesTable(rowType, config) {
1767
2463
  * @typeParam R - The entry type
1768
2464
  * @typeParam K - The axis kind the tiles imply (inferred from `events` / `markers`; `never` when erased)
1769
2465
  * @typeParam KT - The entries' key type
2466
+ * @typeParam FE - The collection its tiles read
2467
+ * @typeParam UE - What its tiles read
2468
+ * @typeParam UM - What its markers read
2469
+ * @typeParam CT - A tree's field, as `children` names it
2470
+ * @typeParam CU - One step-down's reads
2471
+ * @typeParam CF - The folds of its cells
2472
+ * @typeParam CS - Several step-downs' reads
1770
2473
  * @param rowType - The entry type
1771
- * @param config - The accessors ({@link PlanBucketsSeriesConfig})
1772
- * @returns A series value, branded with its kind
2474
+ * @param config - The reads and accessors ({@link PlanBucketsSeriesConfig})
2475
+ * @returns A series value, branded with its kind and what it reads
1773
2476
  */
1774
2477
  export function createSeriesBuckets(rowType, config) {
1775
2478
  const cfg = config;
2479
+ const where = `Plan.series.buckets "${cfg.key}"`;
2480
+ const edit = editRecipeOf(cfg.edit, "events", "{ at }", cfg.events, where);
2481
+ const events = elementField(rowType, cfg.events, "event", where, "events");
2482
+ if (events === undefined)
2483
+ throw new Error(`${where}: give its events — Plan.items(field, { at }, draw), or a function of the row`);
2484
+ const markers = elementField(rowType, cfg.markers, "marker", where, "markers");
1776
2485
  const spec = dataSpec(rowType, cfg, {
1777
2486
  arm: "buckets",
1778
- kind: (value, key) => bucketsKind({
1779
- ...(cfg.lanes !== undefined ? { lanes: cfg.lanes(value, key) } : {}),
1780
- events: cfg.events(value, key),
1781
- ...(cfg.markers !== undefined ? { markers: cfg.markers(value, key) } : {}),
2487
+ kind: (node, key, _hasChildren, value) => bucketsKind({
2488
+ ...(cfg.lanes !== undefined ? { lanes: cfg.lanes(node, key) } : {}),
2489
+ events: events.at(node, key, value),
2490
+ ...(markers !== undefined ? { markers: markers.at(node, key, value) } : {}),
1782
2491
  }),
1783
- edit: cfg.edit,
2492
+ edit,
2493
+ reads: [...events.reads, ...(markers?.reads ?? [])],
1784
2494
  });
1785
2495
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1786
2496
  }
@@ -1790,16 +2500,28 @@ export function createSeriesBuckets(rowType, config) {
1790
2500
  * @typeParam R - The entry type
1791
2501
  * @typeParam K - The axis kind the chips imply (inferred from `chips`; `never` when erased)
1792
2502
  * @typeParam KT - The entries' key type
2503
+ * @typeParam FC - The collection its chips read
2504
+ * @typeParam UC - What its chips read
2505
+ * @typeParam CT - A tree's field, as `children` names it
2506
+ * @typeParam CU - One step-down's reads
2507
+ * @typeParam CF - The folds of its cells
2508
+ * @typeParam CS - Several step-downs' reads
1793
2509
  * @param rowType - The entry type
1794
- * @param config - The accessors ({@link PlanCardsSeriesConfig})
1795
- * @returns A series value, branded with its kind
2510
+ * @param config - The reads and accessors ({@link PlanCardsSeriesConfig})
2511
+ * @returns A series value, branded with its kind and what it reads
1796
2512
  */
1797
2513
  export function createSeriesCards(rowType, config) {
1798
2514
  const cfg = config;
2515
+ const where = `Plan.series.cards "${cfg.key}"`;
2516
+ const edit = editRecipeOf(cfg.edit, "chips", "{ from, to }", cfg.chips, where);
2517
+ const chips = elementField(rowType, cfg.chips, "chip", where, "chips");
2518
+ if (chips === undefined)
2519
+ throw new Error(`${where}: give its chips — Plan.items(field, { from, to }, draw), or a function of the row`);
1799
2520
  const spec = dataSpec(rowType, cfg, {
1800
2521
  arm: "cards",
1801
- kind: (value, key) => cardsKind(cfg.chips(value, key)),
1802
- edit: cfg.edit,
2522
+ kind: (node, key, _hasChildren, value) => cardsKind(chips.at(node, key, value)),
2523
+ edit,
2524
+ reads: chips.reads,
1803
2525
  });
1804
2526
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1805
2527
  }
@@ -1809,37 +2531,61 @@ export function createSeriesCards(rowType, config) {
1809
2531
  * @typeParam R - The entry type
1810
2532
  * @typeParam K - The axis kind the marks imply (inferred from `marks`; `never` when erased)
1811
2533
  * @typeParam KT - The entries' key type
2534
+ * @typeParam FM - The collection its marks read
2535
+ * @typeParam UM - What its marks read
2536
+ * @typeParam CT - A tree's field, as `children` names it
2537
+ * @typeParam CU - One step-down's reads
2538
+ * @typeParam CF - The folds of its cells
2539
+ * @typeParam CS - Several step-downs' reads
1812
2540
  * @param rowType - The entry type
1813
- * @param config - The accessors ({@link PlanEventsSeriesConfig})
1814
- * @returns A series value, branded with its kind
2541
+ * @param config - The reads and accessors ({@link PlanEventsSeriesConfig})
2542
+ * @returns A series value, branded with its kind and what it reads
1815
2543
  */
1816
2544
  export function createSeriesEvents(rowType, config) {
1817
2545
  const cfg = config;
2546
+ const where = `Plan.series.events "${cfg.key}"`;
2547
+ const edit = editRecipeOf(cfg.edit, "marks", "{ at }", cfg.marks, where);
2548
+ const marks = elementField(rowType, cfg.marks, "mark", where, "marks");
2549
+ if (marks === undefined)
2550
+ throw new Error(`${where}: give its marks — Plan.items(field, { at }, draw), or a function of the row`);
1818
2551
  const spec = dataSpec(rowType, cfg, {
1819
2552
  arm: "events",
1820
- kind: (value, key) => eventsKind(cfg.marks(value, key)),
1821
- edit: cfg.edit,
2553
+ kind: (node, key, _hasChildren, value) => eventsKind(marks.at(node, key, value)),
2554
+ edit,
2555
+ reads: marks.reads,
1822
2556
  });
1823
2557
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1824
2558
  }
1825
2559
  /**
1826
- * A chart series — one chart row per entry, layers built from the entry's own
1827
- * data. Kind-ERASED: a Chart layer's TS face does not expose its x type, so a
1828
- * chart series constrains no axis at compile time — the render-time
1829
- * diagnostic holds it to the axis like any row.
2560
+ * A chart series — one chart row per entry, its layers over the entry's own
2561
+ * values (`Plan.line` … `Plan.band` over `Plan.values`), or built from the
2562
+ * entry by a function. Its layers over values brand it with the axis kind
2563
+ * their keys ride; a Chart layer's face hides its x type, so a series whose
2564
+ * layers are all Chart layers constrains no axis at compile time — the
2565
+ * render-time diagnostic holds it to the axis like any row.
1830
2566
  *
1831
2567
  * @typeParam R - The entry type
1832
2568
  * @typeParam KT - The entries' key type
2569
+ * @typeParam K - The axis kind its layers' values ride
2570
+ * @typeParam L - Its `layers`, as written
2571
+ * @typeParam CT - A tree's field, as `children` names it
2572
+ * @typeParam CU - One step-down's reads
2573
+ * @typeParam CF - The folds of its cells
2574
+ * @typeParam CS - Several step-downs' reads
1833
2575
  * @param rowType - The entry type
1834
- * @param config - The accessors and shared axes ({@link PlanChartSeriesConfig})
1835
- * @returns A series value
2576
+ * @param config - The layers, accessors and shared axes ({@link PlanChartSeriesConfig})
2577
+ * @returns A series value, branded with its kind and what it reads
1836
2578
  */
1837
2579
  export function createSeriesChart(rowType, config) {
1838
2580
  const cfg = config;
2581
+ const where = `Plan.series.chart "${cfg.key}"`;
2582
+ const layers = listField(rowType, cfg.layers, "layer", where, "layers");
2583
+ if (layers === undefined)
2584
+ throw new Error(`${where}: give its layers — Plan.line(Plan.values(field, draw)) and the like, or a function of the row`);
1839
2585
  const spec = dataSpec(rowType, cfg, {
1840
2586
  arm: "chart",
1841
- kind: (value, key) => chartKind({
1842
- layers: cfg.layers(value, key),
2587
+ kind: (node, key, _hasChildren, value) => chartKind({
2588
+ layers: layers.at(node, key, value),
1843
2589
  ...(cfg.left !== undefined ? { left: cfg.left } : {}),
1844
2590
  ...(cfg.right !== undefined ? { right: cfg.right } : {}),
1845
2591
  ...(cfg.height !== undefined ? { height: cfg.height } : {}),
@@ -1850,6 +2596,7 @@ export function createSeriesChart(rowType, config) {
1850
2596
  ? { pinned: (value, key) => East.value(cfg.pinned(value, key), BooleanType) }
1851
2597
  : {}),
1852
2598
  ...(cfg.swatches !== undefined ? { swatches: cfg.swatches } : {}),
2599
+ reads: layers.reads,
1853
2600
  });
1854
2601
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1855
2602
  }
@@ -1860,19 +2607,25 @@ export function createSeriesChart(rowType, config) {
1860
2607
  * its member count is derived.
1861
2608
  *
1862
2609
  * @remarks
1863
- * Hierarchy comes from the data's nesting: to group a flat source, reshape it
1864
- * first — inline, one East `groupToDicts` in the canvas function (the entries
1865
- * are then `Dict<String, Row>` and a group's label is its key); a paged source
1866
- * is grouped in the dataflow. The `by` form and the static
1867
- * `group(R, chrome, children)` form are gone — see `Plan.series.section` for a
1868
- * fixed titled block.
2610
+ * Hierarchy comes from the data's nesting: a group's members are a `Dict` of
2611
+ * the entry (`Plan.children(field, [series…])`), so a flat source is grouped
2612
+ * in the dataflow, or reshaped inline into entries that hold their members.
2613
+ * The `by` form and the static `group(R, chrome, children)` form are gone —
2614
+ * see `Plan.series.section` for a fixed titled block.
1869
2615
  *
1870
2616
  * @typeParam R - The entry type
1871
2617
  * @typeParam K - The axis kind the summary cells imply
1872
2618
  * @typeParam KT - The entries' key type
2619
+ * @typeParam US - What its `summary` reads
2620
+ * @typeParam FO - How a bucket folds its summary cells
2621
+ * @typeParam A - Its `summaryAggregate`, as written
2622
+ * @typeParam CT - A tree's field, as `children` names it
2623
+ * @typeParam CU - One step-down's reads
2624
+ * @typeParam CF - The folds of its cells
2625
+ * @typeParam CS - Several step-downs' reads
1873
2626
  * @param rowType - The entry type
1874
2627
  * @param config - The strip's accessors and its members ({@link PlanGroupSeriesConfig})
1875
- * @returns A series value, branded with its kind
2628
+ * @returns A series value, branded with its kind and what it reads
1876
2629
  * @throws {Error} When called with the removed `by` or static forms
1877
2630
  */
1878
2631
  export function createSeriesGroup(rowType, config) {
@@ -1888,17 +2641,20 @@ export function createSeriesGroup(rowType, config) {
1888
2641
  if (cfg.children === undefined) {
1889
2642
  throw new Error(`${where}: a group strip needs its members — declare \`children\``);
1890
2643
  }
1891
- const summary = cfg.summary;
1892
- // The strip's ONE summary (#824): the per-entry cells, the declared
1893
- // aggregate, or a plain band — both at once is refused here.
1894
- const declared = summary === undefined ? groupSummary(undefined, cfg.summaryAggregate, where) : undefined;
1895
- if (summary !== undefined && cfg.summaryAggregate !== undefined) {
2644
+ if (cfg.summary !== undefined && cfg.summaryAggregate !== undefined) {
1896
2645
  throw new Error(`${where}: give \`summary\` (explicit strip cells) OR \`summaryAggregate\` (cells derived from the ` +
1897
2646
  "members) — a collapsed strip shows one or the other");
1898
2647
  }
2648
+ const summary = consumerField(rowType, cfg.summary, "heat", where, "summary");
2649
+ // The strip's ONE summary (#824): the per-entry cells, the declared
2650
+ // aggregate, or a plain band.
2651
+ const declared = summary === undefined ? groupSummary(undefined, cfg.summaryAggregate, where) : undefined;
1899
2652
  const spec = dataSpec(rowType, cfg, {
1900
2653
  arm: "group",
1901
- kind: (value, key) => groupKind(declared ?? East.value(variant("cells", East.value(summary(value, key), PlanHeatCellsType)), PlanGroupSummaryType)),
2654
+ kind: (node, key, _hasChildren, value) => groupKind(declared ?? East.value(variant("cells", East.value(summary.at(node, key, value), PlanHeatCellsType)), PlanGroupSummaryType)),
2655
+ reads: summary?.reads ?? [],
2656
+ // Its strip derives its members' numbers by its summaryAggregate (#1435).
2657
+ ...(cfg.summaryAggregate !== undefined ? { aggregate: tagOf(cfg.summaryAggregate, "mean") } : {}),
1902
2658
  });
1903
2659
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1904
2660
  }
@@ -1910,10 +2666,11 @@ export function createSeriesGroup(rowType, config) {
1910
2666
  *
1911
2667
  * @typeParam R - The entry type (the members' — they share the section's entries)
1912
2668
  * @typeParam K - The union of the members' axis kinds (inferred; `never` when all are erased)
2669
+ * @typeParam S - The members, as written
1913
2670
  * @param rowType - The entry type
1914
2671
  * @param config - The header ({@link PlanSectionSeriesConfig})
1915
2672
  * @param members - The member series, in order
1916
- * @returns A series value, branded with its members' kinds
2673
+ * @returns A series value, branded with its members' kinds and what they read
1917
2674
  */
1918
2675
  export function createSeriesSection(rowType, config, members) {
1919
2676
  const cfg = config;
@@ -1932,7 +2689,7 @@ export function createSeriesSection(rowType, config, members) {
1932
2689
  * view row's id is its member series' key and the entry's path, and a seek on
1933
2690
  * the entry lands on its first view row.
1934
2691
  *
1935
- * The views' `children` are a step-down's series, or — a bare accessor — more
2692
+ * The views' `children` are a step-down's series, or — a tree's field — more
1936
2693
  * of the views' own entries, each drawn as its members' rows, to any depth,
1937
2694
  * the views' `match` and `collapsed` at every level (#1219). A span member that
1938
2695
  * declares `rollup` rolls up the children nested under its row.
@@ -1940,10 +2697,15 @@ export function createSeriesSection(rowType, config, members) {
1940
2697
  * @typeParam R - The entry type
1941
2698
  * @typeParam K - The union of the members' axis kinds (inferred; `never` when all are erased)
1942
2699
  * @typeParam KT - The entries' key type
2700
+ * @typeParam S - The members, as written
2701
+ * @typeParam CT - A tree's field, as `children` names it
2702
+ * @typeParam CU - One step-down's reads
2703
+ * @typeParam CF - The folds of its cells
2704
+ * @typeParam CS - Several step-downs' reads
1943
2705
  * @param rowType - The entry type
1944
2706
  * @param config - Membership, collapse and children ({@link PlanViewsSeriesConfig})
1945
2707
  * @param members - The member series — one row each per entry, in order
1946
- * @returns A series value, branded with its members' kinds
2708
+ * @returns A series value, branded with its members' kinds and what it reads
1947
2709
  */
1948
2710
  export function createSeriesViews(rowType, config, members) {
1949
2711
  const cfg = config;