@elaraai/e3-ui 1.0.97 → 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 (491) 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 +232 -25
  5. package/dist/src/allocation/index.d.ts.map +1 -1
  6. package/dist/src/allocation/index.js +35 -6
  7. package/dist/src/allocation/index.js.map +1 -1
  8. package/dist/src/allocation/types.d.ts +41 -0
  9. package/dist/src/allocation/types.d.ts.map +1 -1
  10. package/dist/src/allocation/types.js +35 -0
  11. package/dist/src/allocation/types.js.map +1 -1
  12. package/dist/src/bind/data-indexed.d.ts +286 -37
  13. package/dist/src/bind/data-indexed.d.ts.map +1 -1
  14. package/dist/src/bind/data-indexed.js +259 -38
  15. package/dist/src/bind/data-indexed.js.map +1 -1
  16. package/dist/src/bind/data.d.ts +48 -9
  17. package/dist/src/bind/data.d.ts.map +1 -1
  18. package/dist/src/bind/data.js +48 -3
  19. package/dist/src/bind/data.js.map +1 -1
  20. package/dist/src/bind/record.d.ts +61 -24
  21. package/dist/src/bind/record.d.ts.map +1 -1
  22. package/dist/src/bind/record.js +17 -57
  23. package/dist/src/bind/record.js.map +1 -1
  24. package/dist/src/calendar/index.d.ts +522 -36
  25. package/dist/src/calendar/index.d.ts.map +1 -1
  26. package/dist/src/calendar/index.js +53 -13
  27. package/dist/src/calendar/index.js.map +1 -1
  28. package/dist/src/calendar/paged.d.ts +89 -0
  29. package/dist/src/calendar/paged.d.ts.map +1 -0
  30. package/dist/src/calendar/paged.js +148 -0
  31. package/dist/src/calendar/paged.js.map +1 -0
  32. package/dist/src/flowchart/index.d.ts +35 -31
  33. package/dist/src/flowchart/index.d.ts.map +1 -1
  34. package/dist/src/flowchart/index.js +35 -31
  35. package/dist/src/flowchart/index.js.map +1 -1
  36. package/dist/src/flowchart/library.d.ts +108 -84
  37. package/dist/src/flowchart/library.d.ts.map +1 -1
  38. package/dist/src/flowchart/library.js +108 -84
  39. package/dist/src/flowchart/library.js.map +1 -1
  40. package/dist/src/flowchart/payload.d.ts +105 -17
  41. package/dist/src/flowchart/payload.d.ts.map +1 -1
  42. package/dist/src/flowchart/payload.js +3 -3
  43. package/dist/src/flowchart/payload.js.map +1 -1
  44. package/dist/src/flowchart/values.d.ts +35 -31
  45. package/dist/src/flowchart/values.d.ts.map +1 -1
  46. package/dist/src/flowchart/values.js +35 -31
  47. package/dist/src/flowchart/values.js.map +1 -1
  48. package/dist/src/flowchart/window.d.ts.map +1 -1
  49. package/dist/src/flowchart/window.js +3 -2
  50. package/dist/src/flowchart/window.js.map +1 -1
  51. package/dist/src/index.d.ts +13 -4
  52. package/dist/src/index.d.ts.map +1 -1
  53. package/dist/src/index.js +20 -1
  54. package/dist/src/index.js.map +1 -1
  55. package/dist/src/internal.d.ts +18 -8
  56. package/dist/src/internal.d.ts.map +1 -1
  57. package/dist/src/internal.js +40 -10
  58. package/dist/src/internal.js.map +1 -1
  59. package/dist/src/plan/assemble.d.ts +8 -0
  60. package/dist/src/plan/assemble.d.ts.map +1 -1
  61. package/dist/src/plan/assemble.js +1 -1
  62. package/dist/src/plan/assemble.js.map +1 -1
  63. package/dist/src/plan/builders.d.ts +1530 -10
  64. package/dist/src/plan/builders.d.ts.map +1 -1
  65. package/dist/src/plan/builders.js +260 -122
  66. package/dist/src/plan/builders.js.map +1 -1
  67. package/dist/src/plan/event-rows.d.ts +224 -157
  68. package/dist/src/plan/event-rows.d.ts.map +1 -1
  69. package/dist/src/plan/event-rows.js +201 -56
  70. package/dist/src/plan/event-rows.js.map +1 -1
  71. package/dist/src/plan/factories.d.ts +72 -4
  72. package/dist/src/plan/factories.d.ts.map +1 -1
  73. package/dist/src/plan/factories.js +147 -1
  74. package/dist/src/plan/factories.js.map +1 -1
  75. package/dist/src/plan/index.d.ts +152 -125
  76. package/dist/src/plan/index.d.ts.map +1 -1
  77. package/dist/src/plan/index.js +138 -122
  78. package/dist/src/plan/index.js.map +1 -1
  79. package/dist/src/plan/ir.d.ts +1139 -8
  80. package/dist/src/plan/ir.d.ts.map +1 -1
  81. package/dist/src/plan/ir.js +5 -0
  82. package/dist/src/plan/ir.js.map +1 -1
  83. package/dist/src/plan/library.d.ts +65 -42
  84. package/dist/src/plan/library.d.ts.map +1 -1
  85. package/dist/src/plan/library.js +65 -42
  86. package/dist/src/plan/library.js.map +1 -1
  87. package/dist/src/plan/nested-checks.d.ts +146 -0
  88. package/dist/src/plan/nested-checks.d.ts.map +1 -0
  89. package/dist/src/plan/nested-checks.js +468 -0
  90. package/dist/src/plan/nested-checks.js.map +1 -0
  91. package/dist/src/plan/nested-patch.d.ts +35 -0
  92. package/dist/src/plan/nested-patch.d.ts.map +1 -0
  93. package/dist/src/plan/nested-patch.js +143 -0
  94. package/dist/src/plan/nested-patch.js.map +1 -0
  95. package/dist/src/plan/nested-read.d.ts +3250 -0
  96. package/dist/src/plan/nested-read.d.ts.map +1 -0
  97. package/dist/src/plan/nested-read.js +163 -0
  98. package/dist/src/plan/nested-read.js.map +1 -0
  99. package/dist/src/plan/nested.d.ts +280 -0
  100. package/dist/src/plan/nested.d.ts.map +1 -0
  101. package/dist/src/plan/nested.js +6 -0
  102. package/dist/src/plan/nested.js.map +1 -0
  103. package/dist/src/plan/ordered-source.d.ts +3067 -1
  104. package/dist/src/plan/ordered-source.d.ts.map +1 -1
  105. package/dist/src/plan/ordered-source.js +67 -17
  106. package/dist/src/plan/ordered-source.js.map +1 -1
  107. package/dist/src/plan/over.d.ts +111 -75
  108. package/dist/src/plan/over.d.ts.map +1 -1
  109. package/dist/src/plan/over.js +111 -75
  110. package/dist/src/plan/over.js.map +1 -1
  111. package/dist/src/plan/pick.d.ts +15 -3
  112. package/dist/src/plan/pick.d.ts.map +1 -1
  113. package/dist/src/plan/pick.js +75 -36
  114. package/dist/src/plan/pick.js.map +1 -1
  115. package/dist/src/plan/plan.d.ts +3143 -501
  116. package/dist/src/plan/plan.d.ts.map +1 -1
  117. package/dist/src/plan/plan.js +133 -34
  118. package/dist/src/plan/plan.js.map +1 -1
  119. package/dist/src/plan/reads.d.ts +421 -0
  120. package/dist/src/plan/reads.d.ts.map +1 -0
  121. package/dist/src/plan/reads.js +143 -0
  122. package/dist/src/plan/reads.js.map +1 -0
  123. package/dist/src/plan/refs.d.ts +111 -75
  124. package/dist/src/plan/refs.d.ts.map +1 -1
  125. package/dist/src/plan/refs.js +111 -75
  126. package/dist/src/plan/refs.js.map +1 -1
  127. package/dist/src/plan/root.d.ts +14 -7
  128. package/dist/src/plan/root.d.ts.map +1 -1
  129. package/dist/src/plan/root.js +82 -17
  130. package/dist/src/plan/root.js.map +1 -1
  131. package/dist/src/plan/series.d.ts +5153 -4331
  132. package/dist/src/plan/series.d.ts.map +1 -1
  133. package/dist/src/plan/series.js +1082 -266
  134. package/dist/src/plan/series.js.map +1 -1
  135. package/dist/src/plan/shade.d.ts +981 -0
  136. package/dist/src/plan/shade.d.ts.map +1 -0
  137. package/dist/src/plan/shade.js +60 -0
  138. package/dist/src/plan/shade.js.map +1 -0
  139. package/dist/src/plan/types.d.ts +192 -18
  140. package/dist/src/plan/types.d.ts.map +1 -1
  141. package/dist/src/plan/types.js +77 -19
  142. package/dist/src/plan/types.js.map +1 -1
  143. package/dist/src/query/builder.d.ts +3 -3
  144. package/dist/src/query/builder.d.ts.map +1 -1
  145. package/dist/src/query/builder.js +1 -1
  146. package/dist/src/query/library.d.ts +3 -3
  147. package/dist/src/query/library.d.ts.map +1 -1
  148. package/dist/src/query/library.js +1 -1
  149. package/dist/src/query/queries.d.ts +2 -2
  150. package/dist/src/query/queries.js +1 -1
  151. package/dist/src/query/saved.d.ts +8 -4
  152. package/dist/src/query/saved.d.ts.map +1 -1
  153. package/dist/src/query/saved.js +8 -4
  154. package/dist/src/query/saved.js.map +1 -1
  155. package/dist/src/query/types.d.ts +8 -3
  156. package/dist/src/query/types.d.ts.map +1 -1
  157. package/dist/src/query/types.js +8 -3
  158. package/dist/src/query/types.js.map +1 -1
  159. package/dist/src/roster/index.d.ts +583 -39
  160. package/dist/src/roster/index.d.ts.map +1 -1
  161. package/dist/src/roster/index.js +109 -35
  162. package/dist/src/roster/index.js.map +1 -1
  163. package/dist/src/roster/library.d.ts +12 -2
  164. package/dist/src/roster/library.d.ts.map +1 -1
  165. package/dist/src/roster/library.js +8 -0
  166. package/dist/src/roster/library.js.map +1 -1
  167. package/dist/src/roster/lists.d.ts +59 -12
  168. package/dist/src/roster/lists.d.ts.map +1 -1
  169. package/dist/src/roster/lists.js +51 -58
  170. package/dist/src/roster/lists.js.map +1 -1
  171. package/dist/src/roster/targets.d.ts +8 -4
  172. package/dist/src/roster/targets.d.ts.map +1 -1
  173. package/dist/src/roster/targets.js +9 -5
  174. package/dist/src/roster/targets.js.map +1 -1
  175. package/dist/src/roster/times.d.ts +402 -0
  176. package/dist/src/roster/times.d.ts.map +1 -1
  177. package/dist/src/roster/types.d.ts +913 -1
  178. package/dist/src/roster/types.d.ts.map +1 -1
  179. package/dist/src/roster/types.js +43 -3
  180. package/dist/src/roster/types.js.map +1 -1
  181. package/dist/src/schedule/cell-checks.d.ts +85 -0
  182. package/dist/src/schedule/cell-checks.d.ts.map +1 -0
  183. package/dist/src/schedule/cell-checks.js +211 -0
  184. package/dist/src/schedule/cell-checks.js.map +1 -0
  185. package/dist/src/schedule/days.d.ts +1 -1
  186. package/dist/src/schedule/days.js +1 -1
  187. package/dist/src/schedule/events.d.ts +52 -16
  188. package/dist/src/schedule/events.d.ts.map +1 -1
  189. package/dist/src/schedule/events.js +574 -356
  190. package/dist/src/schedule/events.js.map +1 -1
  191. package/dist/src/schedule/fields.d.ts +134 -0
  192. package/dist/src/schedule/fields.d.ts.map +1 -0
  193. package/dist/src/schedule/fields.js +207 -0
  194. package/dist/src/schedule/fields.js.map +1 -0
  195. package/dist/src/schedule/library.d.ts +11 -3
  196. package/dist/src/schedule/library.d.ts.map +1 -1
  197. package/dist/src/schedule/library.js +9 -6
  198. package/dist/src/schedule/library.js.map +1 -1
  199. package/dist/src/schedule/optimise.d.ts +124 -0
  200. package/dist/src/schedule/optimise.d.ts.map +1 -0
  201. package/dist/src/schedule/optimise.js +140 -0
  202. package/dist/src/schedule/optimise.js.map +1 -0
  203. package/dist/src/schedule/pages.d.ts +76 -0
  204. package/dist/src/schedule/pages.d.ts.map +1 -0
  205. package/dist/src/schedule/pages.js +86 -0
  206. package/dist/src/schedule/pages.js.map +1 -0
  207. package/dist/src/schedule/patch.d.ts +1 -1
  208. package/dist/src/schedule/patch.js +1 -1
  209. package/dist/src/schedule/periods.d.ts +8 -4
  210. package/dist/src/schedule/periods.d.ts.map +1 -1
  211. package/dist/src/schedule/periods.js +1 -1
  212. package/dist/src/schedule/periods.js.map +1 -1
  213. package/dist/src/schedule/record-window.d.ts +115 -22
  214. package/dist/src/schedule/record-window.d.ts.map +1 -1
  215. package/dist/src/schedule/record-window.js +140 -62
  216. package/dist/src/schedule/record-window.js.map +1 -1
  217. package/dist/src/schedule/resources.d.ts +9 -5
  218. package/dist/src/schedule/resources.d.ts.map +1 -1
  219. package/dist/src/schedule/resources.js +35 -15
  220. package/dist/src/schedule/resources.js.map +1 -1
  221. package/dist/src/schedule/session.d.ts +15 -1
  222. package/dist/src/schedule/session.d.ts.map +1 -1
  223. package/dist/src/schedule/session.js +30 -18
  224. package/dist/src/schedule/session.js.map +1 -1
  225. package/dist/src/schedule/types.d.ts +455 -37
  226. package/dist/src/schedule/types.d.ts.map +1 -1
  227. package/dist/src/schedule/types.js +79 -18
  228. package/dist/src/schedule/types.js.map +1 -1
  229. package/dist/src/schedule/window.d.ts +30 -9
  230. package/dist/src/schedule/window.d.ts.map +1 -1
  231. package/dist/src/schedule/window.js +101 -49
  232. package/dist/src/schedule/window.js.map +1 -1
  233. package/dist/src/schematic/index.d.ts +287 -0
  234. package/dist/src/schematic/index.d.ts.map +1 -0
  235. package/dist/src/schematic/index.js +216 -0
  236. package/dist/src/schematic/index.js.map +1 -0
  237. package/dist/src/schematic/kinds.d.ts +501 -0
  238. package/dist/src/schematic/kinds.d.ts.map +1 -0
  239. package/dist/src/schematic/kinds.js +1027 -0
  240. package/dist/src/schematic/kinds.js.map +1 -0
  241. package/dist/src/schematic/layers.d.ts +74 -0
  242. package/dist/src/schematic/layers.d.ts.map +1 -0
  243. package/dist/src/schematic/layers.js +99 -0
  244. package/dist/src/schematic/layers.js.map +1 -0
  245. package/dist/src/schematic/library.d.ts +202 -0
  246. package/dist/src/schematic/library.d.ts.map +1 -0
  247. package/dist/src/schematic/library.js +244 -0
  248. package/dist/src/schematic/library.js.map +1 -0
  249. package/dist/src/schematic/payload.d.ts +8310 -0
  250. package/dist/src/schematic/payload.d.ts.map +1 -0
  251. package/dist/src/schematic/payload.js +446 -0
  252. package/dist/src/schematic/payload.js.map +1 -0
  253. package/dist/src/schematic/schematic.d.ts +27 -0
  254. package/dist/src/schematic/schematic.d.ts.map +1 -0
  255. package/dist/src/schematic/schematic.js +17 -0
  256. package/dist/src/schematic/schematic.js.map +1 -0
  257. package/dist/src/schematic/shapes.d.ts +150 -0
  258. package/dist/src/schematic/shapes.d.ts.map +1 -0
  259. package/dist/src/schematic/shapes.js +171 -0
  260. package/dist/src/schematic/shapes.js.map +1 -0
  261. package/dist/src/schematic/templates.d.ts +96 -0
  262. package/dist/src/schematic/templates.d.ts.map +1 -0
  263. package/dist/src/schematic/templates.js +91 -0
  264. package/dist/src/schematic/templates.js.map +1 -0
  265. package/dist/src/schematic/types.d.ts +5766 -0
  266. package/dist/src/schematic/types.d.ts.map +1 -0
  267. package/dist/src/schematic/types.js +688 -0
  268. package/dist/src/schematic/types.js.map +1 -0
  269. package/dist/src/schematic/window.d.ts +213 -0
  270. package/dist/src/schematic/window.d.ts.map +1 -0
  271. package/dist/src/schematic/window.js +340 -0
  272. package/dist/src/schematic/window.js.map +1 -0
  273. package/dist/src/sheet/bridge.d.ts +19 -0
  274. package/dist/src/sheet/bridge.d.ts.map +1 -1
  275. package/dist/src/sheet/bridge.js +28 -21
  276. package/dist/src/sheet/bridge.js.map +1 -1
  277. package/dist/src/sheet/editing-bridge.d.ts +4 -2
  278. package/dist/src/sheet/editing-bridge.d.ts.map +1 -1
  279. package/dist/src/sheet/editing-bridge.js +8 -2
  280. package/dist/src/sheet/editing-bridge.js.map +1 -1
  281. package/dist/src/sheet/editing-types.d.ts +9 -5
  282. package/dist/src/sheet/editing-types.d.ts.map +1 -1
  283. package/dist/src/sheet/editing-types.js +9 -5
  284. package/dist/src/sheet/editing-types.js.map +1 -1
  285. package/dist/src/sheet/index.d.ts +84 -54
  286. package/dist/src/sheet/index.d.ts.map +1 -1
  287. package/dist/src/sheet/index.js +81 -53
  288. package/dist/src/sheet/index.js.map +1 -1
  289. package/dist/src/sheet/library.d.ts +13 -8
  290. package/dist/src/sheet/library.d.ts.map +1 -1
  291. package/dist/src/sheet/library.js +13 -8
  292. package/dist/src/sheet/library.js.map +1 -1
  293. package/dist/src/sheet/optimise.d.ts +69 -0
  294. package/dist/src/sheet/optimise.d.ts.map +1 -0
  295. package/dist/src/sheet/optimise.js +81 -0
  296. package/dist/src/sheet/optimise.js.map +1 -0
  297. package/dist/src/sheet/record.d.ts +1 -1
  298. package/dist/src/sheet/record.js +2 -2
  299. package/dist/src/sheet/record.js.map +1 -1
  300. package/dist/src/sheet/registers.d.ts +157 -133
  301. package/dist/src/sheet/registers.d.ts.map +1 -1
  302. package/dist/src/sheet/registers.js +157 -133
  303. package/dist/src/sheet/registers.js.map +1 -1
  304. package/dist/src/sheet/root.d.ts +6 -1
  305. package/dist/src/sheet/root.d.ts.map +1 -1
  306. package/dist/src/sheet/root.js +17 -5
  307. package/dist/src/sheet/root.js.map +1 -1
  308. package/dist/src/sheet/sheet.d.ts +696 -590
  309. package/dist/src/sheet/sheet.d.ts.map +1 -1
  310. package/dist/src/sheet/sheet.js +49 -15
  311. package/dist/src/sheet/sheet.js.map +1 -1
  312. package/dist/src/sheet/sub-rows.d.ts +106 -76
  313. package/dist/src/sheet/sub-rows.d.ts.map +1 -1
  314. package/dist/src/sheet/sub-rows.js +106 -76
  315. package/dist/src/sheet/sub-rows.js.map +1 -1
  316. package/dist/src/sheet/types.d.ts +57 -20
  317. package/dist/src/sheet/types.d.ts.map +1 -1
  318. package/dist/src/sheet/types.js +4 -8
  319. package/dist/src/sheet/types.js.map +1 -1
  320. package/dist/src/studio/builder.d.ts +3 -153
  321. package/dist/src/studio/builder.d.ts.map +1 -1
  322. package/dist/src/studio/builder.js +1 -1
  323. package/dist/src/studio/canvas.d.ts +5 -4
  324. package/dist/src/studio/canvas.d.ts.map +1 -1
  325. package/dist/src/studio/canvas.js +8 -7
  326. package/dist/src/studio/canvas.js.map +1 -1
  327. package/dist/src/studio/component.d.ts +30 -20
  328. package/dist/src/studio/component.d.ts.map +1 -1
  329. package/dist/src/studio/component.js +26 -61
  330. package/dist/src/studio/component.js.map +1 -1
  331. package/dist/src/studio/index.d.ts +2 -2
  332. package/dist/src/studio/index.d.ts.map +1 -1
  333. package/dist/src/studio/index.js +1 -1
  334. package/dist/src/studio/index.js.map +1 -1
  335. package/dist/src/studio/inspector.d.ts +10 -39
  336. package/dist/src/studio/inspector.d.ts.map +1 -1
  337. package/dist/src/studio/inspector.js +11 -31
  338. package/dist/src/studio/inspector.js.map +1 -1
  339. package/dist/src/studio/library.d.ts +3 -169
  340. package/dist/src/studio/library.d.ts.map +1 -1
  341. package/dist/src/studio/library.js +1 -1
  342. package/dist/src/studio/page.d.ts +0 -8
  343. package/dist/src/studio/page.d.ts.map +1 -1
  344. package/dist/src/studio/pages.d.ts +17 -591
  345. package/dist/src/studio/pages.d.ts.map +1 -1
  346. package/dist/src/studio/pages.js +17 -44
  347. package/dist/src/studio/pages.js.map +1 -1
  348. package/dist/src/studio/palette.d.ts +2 -7
  349. package/dist/src/studio/palette.d.ts.map +1 -1
  350. package/dist/src/studio/palette.js +4 -5
  351. package/dist/src/studio/palette.js.map +1 -1
  352. package/dist/src/studio/publish.d.ts +8 -18
  353. package/dist/src/studio/publish.d.ts.map +1 -1
  354. package/dist/src/studio/publish.js +10 -36
  355. package/dist/src/studio/publish.js.map +1 -1
  356. package/dist/src/utils/derive.d.ts +12 -12
  357. package/dist/src/utils/derive.d.ts.map +1 -1
  358. package/dist/src/utils/derive.js +5 -2
  359. package/dist/src/utils/derive.js.map +1 -1
  360. package/dist/src/utils/keyed-rows.d.ts +41 -0
  361. package/dist/src/utils/keyed-rows.d.ts.map +1 -0
  362. package/dist/src/utils/keyed-rows.js +64 -0
  363. package/dist/src/utils/keyed-rows.js.map +1 -0
  364. package/dist/src/utils/optimise.d.ts +79 -0
  365. package/dist/src/utils/optimise.d.ts.map +1 -0
  366. package/dist/src/utils/optimise.js +46 -0
  367. package/dist/src/utils/optimise.js.map +1 -0
  368. package/dist/src/utils/ordered-source.d.ts.map +1 -1
  369. package/dist/src/utils/ordered-source.js +14 -5
  370. package/dist/src/utils/ordered-source.js.map +1 -1
  371. package/dist/test/allocation/allocation.examples.d.ts +1900 -1610
  372. package/dist/test/allocation/allocation.examples.d.ts.map +1 -1
  373. package/dist/test/allocation/allocation.examples.js +179 -89
  374. package/dist/test/allocation/allocation.examples.js.map +1 -1
  375. package/dist/test/allocation/optimise.examples.d.ts +109 -0
  376. package/dist/test/allocation/optimise.examples.d.ts.map +1 -0
  377. package/dist/test/allocation/optimise.examples.js +223 -0
  378. package/dist/test/allocation/optimise.examples.js.map +1 -0
  379. package/dist/test/bind/data/data.examples.d.ts +1172 -514
  380. package/dist/test/bind/data/data.examples.d.ts.map +1 -1
  381. package/dist/test/bind/data/data.examples.js +284 -164
  382. package/dist/test/bind/data/data.examples.js.map +1 -1
  383. package/dist/test/bind/record/record.examples.d.ts +457 -428
  384. package/dist/test/bind/record/record.examples.d.ts.map +1 -1
  385. package/dist/test/bind/record/record.examples.js +41 -21
  386. package/dist/test/bind/record/record.examples.js.map +1 -1
  387. package/dist/test/calendar/calendar-records.examples.d.ts +162 -170
  388. package/dist/test/calendar/calendar-records.examples.d.ts.map +1 -1
  389. package/dist/test/calendar/calendar-records.examples.js +20 -12
  390. package/dist/test/calendar/calendar-records.examples.js.map +1 -1
  391. package/dist/test/calendar/calendar-windows.examples.d.ts +9 -0
  392. package/dist/test/calendar/calendar-windows.examples.d.ts.map +1 -0
  393. package/dist/test/calendar/calendar-windows.examples.js +111 -0
  394. package/dist/test/calendar/calendar-windows.examples.js.map +1 -0
  395. package/dist/test/calendar/calendar.examples.d.ts +1172 -915
  396. package/dist/test/calendar/calendar.examples.d.ts.map +1 -1
  397. package/dist/test/calendar/calendar.examples.js +211 -124
  398. package/dist/test/calendar/calendar.examples.js.map +1 -1
  399. package/dist/test/calendar/optimise.examples.d.ts +132 -0
  400. package/dist/test/calendar/optimise.examples.d.ts.map +1 -0
  401. package/dist/test/calendar/optimise.examples.js +75 -0
  402. package/dist/test/calendar/optimise.examples.js.map +1 -0
  403. package/dist/test/examples-package.d.ts +49 -16
  404. package/dist/test/examples-package.d.ts.map +1 -1
  405. package/dist/test/examples-package.js +138 -49
  406. package/dist/test/examples-package.js.map +1 -1
  407. package/dist/test/flowchart/flowchart.examples.d.ts +3841 -2840
  408. package/dist/test/flowchart/flowchart.examples.d.ts.map +1 -1
  409. package/dist/test/flowchart/flowchart.examples.js +211 -121
  410. package/dist/test/flowchart/flowchart.examples.js.map +1 -1
  411. package/dist/test/plan/optimise.examples.d.ts +207 -0
  412. package/dist/test/plan/optimise.examples.d.ts.map +1 -0
  413. package/dist/test/plan/optimise.examples.js +98 -0
  414. package/dist/test/plan/optimise.examples.js.map +1 -0
  415. package/dist/test/plan/plan-events.examples.d.ts +4055 -3279
  416. package/dist/test/plan/plan-events.examples.d.ts.map +1 -1
  417. package/dist/test/plan/plan-events.examples.js +492 -319
  418. package/dist/test/plan/plan-events.examples.js.map +1 -1
  419. package/dist/test/plan/plan-nested.examples.d.ts +1829 -0
  420. package/dist/test/plan/plan-nested.examples.d.ts.map +1 -0
  421. package/dist/test/plan/plan-nested.examples.js +321 -0
  422. package/dist/test/plan/plan-nested.examples.js.map +1 -0
  423. package/dist/test/plan/plan-records.examples.d.ts +391 -244
  424. package/dist/test/plan/plan-records.examples.d.ts.map +1 -1
  425. package/dist/test/plan/plan-records.examples.js +186 -65
  426. package/dist/test/plan/plan-records.examples.js.map +1 -1
  427. package/dist/test/plan/plan-windows.examples.d.ts +1978 -803
  428. package/dist/test/plan/plan-windows.examples.d.ts.map +1 -1
  429. package/dist/test/plan/plan-windows.examples.js +269 -99
  430. package/dist/test/plan/plan-windows.examples.js.map +1 -1
  431. package/dist/test/plan/plan.examples.d.ts +2238 -5726
  432. package/dist/test/plan/plan.examples.d.ts.map +1 -1
  433. package/dist/test/plan/plan.examples.js +846 -973
  434. package/dist/test/plan/plan.examples.js.map +1 -1
  435. package/dist/test/query/query.examples.d.ts +5221 -3005
  436. package/dist/test/query/query.examples.d.ts.map +1 -1
  437. package/dist/test/query/query.examples.js +169 -118
  438. package/dist/test/query/query.examples.js.map +1 -1
  439. package/dist/test/roster/domain.examples.d.ts +804 -0
  440. package/dist/test/roster/domain.examples.d.ts.map +1 -1
  441. package/dist/test/roster/domain.examples.js +3 -3
  442. package/dist/test/roster/domain.examples.js.map +1 -1
  443. package/dist/test/roster/optimise.examples.d.ts +485 -0
  444. package/dist/test/roster/optimise.examples.d.ts.map +1 -0
  445. package/dist/test/roster/optimise.examples.js +155 -0
  446. package/dist/test/roster/optimise.examples.js.map +1 -0
  447. package/dist/test/roster/roster-records.examples.d.ts +364 -404
  448. package/dist/test/roster/roster-records.examples.d.ts.map +1 -1
  449. package/dist/test/roster/roster-records.examples.js +40 -26
  450. package/dist/test/roster/roster-records.examples.js.map +1 -1
  451. package/dist/test/roster/roster.examples.d.ts +5091 -1424
  452. package/dist/test/roster/roster.examples.d.ts.map +1 -1
  453. package/dist/test/roster/roster.examples.js +255 -109
  454. package/dist/test/roster/roster.examples.js.map +1 -1
  455. package/dist/test/roster/warehouse.d.ts +150 -7
  456. package/dist/test/roster/warehouse.d.ts.map +1 -1
  457. package/dist/test/roster/warehouse.js +36 -11
  458. package/dist/test/roster/warehouse.js.map +1 -1
  459. package/dist/test/schedule/schedule.examples.d.ts +2646 -2473
  460. package/dist/test/schedule/schedule.examples.d.ts.map +1 -1
  461. package/dist/test/schedule/schedule.examples.js +144 -88
  462. package/dist/test/schedule/schedule.examples.js.map +1 -1
  463. package/dist/test/schematic/schematic-records.examples.d.ts +454 -0
  464. package/dist/test/schematic/schematic-records.examples.d.ts.map +1 -0
  465. package/dist/test/schematic/schematic-records.examples.js +219 -0
  466. package/dist/test/schematic/schematic-records.examples.js.map +1 -0
  467. package/dist/test/schematic/schematic.examples.d.ts +2640 -0
  468. package/dist/test/schematic/schematic.examples.d.ts.map +1 -0
  469. package/dist/test/schematic/schematic.examples.js +559 -0
  470. package/dist/test/schematic/schematic.examples.js.map +1 -0
  471. package/dist/test/sheet/optimise.examples.d.ts +184 -0
  472. package/dist/test/sheet/optimise.examples.d.ts.map +1 -0
  473. package/dist/test/sheet/optimise.examples.js +79 -0
  474. package/dist/test/sheet/optimise.examples.js.map +1 -0
  475. package/dist/test/sheet/sheet-records.examples.d.ts +208 -356
  476. package/dist/test/sheet/sheet-records.examples.d.ts.map +1 -1
  477. package/dist/test/sheet/sheet-records.examples.js +66 -29
  478. package/dist/test/sheet/sheet-records.examples.js.map +1 -1
  479. package/dist/test/sheet/sheet.examples.d.ts +3650 -3377
  480. package/dist/test/sheet/sheet.examples.d.ts.map +1 -1
  481. package/dist/test/sheet/sheet.examples.js +406 -296
  482. package/dist/test/sheet/sheet.examples.js.map +1 -1
  483. package/dist/test/studio/component.examples.js +2 -2
  484. package/dist/test/studio/component.examples.js.map +1 -1
  485. package/dist/test/studio/page.examples.js +6 -6
  486. package/dist/test/studio/page.examples.js.map +1 -1
  487. package/dist/test/studio/studio.examples.d.ts +2329 -873
  488. package/dist/test/studio/studio.examples.d.ts.map +1 -1
  489. package/dist/test/studio/studio.examples.js +155 -107
  490. package/dist/test/studio/studio.examples.js.map +1 -1
  491. 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;
@@ -173,14 +224,49 @@ function segmentOf(keyType) {
173
224
  * @param collection - The collection type the rows are built for
174
225
  * @param rows - The series' rows emitter for that collection
175
226
  * @param fixed - Whether no entry produces the rows
227
+ * @param charts - How many chart rows each entry makes, when every entry makes as many (#1398) — a fixed block's none
176
228
  * @returns The block list's emitter
177
229
  */
178
- function oneBlock(collection, rows, fixed) {
230
+ function oneBlock(collection, rows, fixed, charts) {
231
+ const each = fixed || charts === undefined ? none : some(BigInt(charts));
179
232
  return East.function([collection, PathType, IdOptType], PlanBlocksType, ($, coll, prefix, parent) => {
180
- const block = $.let(East.value({ fixed, parent, rows: rows(coll, prefix, parent) }, PlanBlockType), PlanBlockType);
233
+ const block = $.let(East.value({ fixed, parent, charts: each, rows: rows(coll, prefix, parent) }, PlanBlockType), PlanBlockType);
181
234
  return East.value([block], PlanBlocksType);
182
235
  });
183
236
  }
237
+ /**
238
+ * The chart rows an entry makes across a list of series, when each makes a fixed number (#1398).
239
+ *
240
+ * @param specs - The series
241
+ * @returns Their sum; `undefined` where one of them varies by entry
242
+ */
243
+ function chartsOf(specs) {
244
+ let sum = 0;
245
+ for (const spec of specs) {
246
+ if (spec.charts === undefined)
247
+ return undefined;
248
+ sum += spec.charts;
249
+ }
250
+ return sum;
251
+ }
252
+ /**
253
+ * How many chart rows each entry of a series makes (#1398): its own rows' and those below them — where the rows
254
+ * below hold any, as many as the data's children, which no definition fixes — and a `match` deciding by entry
255
+ * whether it makes them.
256
+ *
257
+ * @param own - The chart rows its entry's own rows make: `undefined` where they vary by entry
258
+ * @param below - The chart rows its children's rows hold for each child: `0` where they hold none, else how many
259
+ * (`undefined` where that varies)
260
+ * @param matched - Whether a `match` decides which entries it takes
261
+ * @returns The entry's chart rows; `undefined` where they vary by entry
262
+ */
263
+ function entryCharts(own, below, matched) {
264
+ // Rows below an entry come one set a child, and the data decides how many children an entry has.
265
+ if (own === undefined || below !== 0)
266
+ return undefined;
267
+ // A match makes an entry's rows or none — as many either way only when they hold no chart.
268
+ return matched && own > 0 ? undefined : own;
269
+ }
184
270
  /** A cache of built functions by East type. */
185
271
  class ByType {
186
272
  entries = [];
@@ -273,85 +359,34 @@ function collectionOps(collection) {
273
359
  put: (c, k, v) => array(c).update(index(k), v),
274
360
  };
275
361
  }
276
- /** The instant fields a kind's elements have (#825): two ends (a run, a chip) or one instant (a tile, a mark). */
277
- function editShapeOf(arm) {
278
- return arm === "span" || arm === "cards" ? "span" : "point";
279
- }
280
- /** The item field types a move can write an instant into (#825). */
281
- 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)];
282
364
  /** Print an item type as the text rows of one item type share (#825) — East's own printer over its type value. */
283
365
  const printType = printFor(EastTypeType);
284
366
  /**
285
- * Check what a series' `edit` declares for a move (#825), against its item
286
- * 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.
287
369
  *
288
- * @param edit - The declaration
289
- * @param itemType - The type of the items in `edit.items`
290
- * @param arm - The series' kind
291
- * @param where - The series, for the message
370
+ * @param read - The elements' read
371
+ * @param item - The collection's item type
292
372
  * @returns The move's fields
293
- * @throws {Error} Naming the field and what is wrong with it
294
373
  */
295
- function moveFieldsOf(edit, itemType, arm, where) {
296
- const shape = editShapeOf(arm);
297
- if (shape === "span" && edit.at !== undefined) {
298
- 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\``);
299
- }
300
- if (shape === "point" && (edit.start !== undefined || edit.end !== undefined)) {
301
- throw new Error(`${where}: a ${arm} element has one instant — name it with \`edit.at\`, not \`start\` / \`end\``);
302
- }
303
- const start = shape === "span" ? edit.start : edit.at;
304
- const end = shape === "span" ? edit.end : undefined;
305
- if (shape === "span" && (start === undefined) !== (end === undefined)) {
306
- throw new Error(`${where}: a move writes both ends of a ${arm} element — give \`edit.start\` and \`edit.end\` together`);
307
- }
308
- if (start === undefined) {
309
- if (edit.create === undefined) {
310
- throw new Error(`${where}: \`edit\` declares neither \`create\` (what a dropped card becomes) nor the item fields a move ` +
311
- `writes (\`key\` and ${shape === "span" ? "`start` / `end`" : "`at`"}) — declare one or both`);
312
- }
313
- return undefined;
314
- }
315
- const item = itemType;
316
- if (item.type !== "Struct") {
317
- throw new Error(`${where}: a move writes fields of the item — the items in "${edit.items}" must be structs`);
318
- }
319
- if (edit.key === undefined) {
320
- throw new Error(`${where}: a move finds its element's item by key — name the item's String key field with \`edit.key\``);
321
- }
322
- const keyType = item.fields[edit.key];
323
- if (keyType === undefined || !isTypeEqual(keyType, StringType)) {
324
- throw new Error(`${where}: \`edit.key\` names "${edit.key}", which is not a String field of the item`);
325
- }
326
- const instant = (name, what) => {
327
- const type = item.fields[name];
328
- if (type === undefined || !INSTANT_FIELD_TYPES.some((t) => isTypeEqual(t, type))) {
329
- throw new Error(`${where}: \`edit.${what}\` names "${name}", which is not an instant field of the item — a DateTime, a Float or ` +
330
- "an Integer, a String, or a Plan.Types.Instant");
331
- }
332
- if (name === edit.key) {
333
- throw new Error(`${where}: \`edit.${what}\` names the key field "${name}" — a move would change the item's identity; name its instant field`);
334
- }
335
- return { name, type };
336
- };
337
- if (end !== undefined && end === start) {
338
- throw new Error(`${where}: \`edit.start\` and \`edit.end\` both name "${start}" — a move writes two different fields`);
339
- }
340
- return {
341
- item,
342
- key: edit.key,
343
- start: instant(start, shape === "span" ? "start" : "at"),
344
- end: end !== undefined ? instant(end, "end") : undefined,
345
- edits: { items: printType(toEastTypeValue(itemType)), resize: shape === "span" },
346
- };
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 };
347
381
  }
348
382
  /** Each instant field type's converter, built once (#825). */
349
383
  const instantConverters = new ByType();
350
384
  /**
351
385
  * The converter from an axis instant into an item field's type (#825): the
352
- * instant on the arm the field holds — a time instant's DateTime, a number
353
- * instant's Float (an Integer field takes it rounded, half away from zero), an
354
- * 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`.
355
390
  *
356
391
  * @param fieldType - The field's type (one of {@link INSTANT_FIELD_TYPES})
357
392
  * @returns `(instant) → Option<field>`
@@ -361,6 +396,7 @@ function instantInto(fieldType) {
361
396
  const out = OptionType(fieldType);
362
397
  const kind = fieldType.type;
363
398
  const whole = isTypeEqual(fieldType, PlanInstantType);
399
+ const optional = isTypeEqual(fieldType, OptionType(DateTimeType));
364
400
  return East.function([PlanInstantType], out, ($, t) => {
365
401
  const result = $.let(none, out);
366
402
  if (whole) {
@@ -371,6 +407,9 @@ function instantInto(fieldType) {
371
407
  time: ($2, d) => {
372
408
  if (kind === "DateTime")
373
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)));
374
413
  },
375
414
  number: ($2, n) => {
376
415
  if (kind === "Float") {
@@ -390,6 +429,570 @@ function instantInto(fieldType) {
390
429
  });
391
430
  });
392
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
+ }
393
996
  /**
394
997
  * The build spec of a data series (span / buckets / chart / heat / table /
395
998
  * cards / events / group).
@@ -399,21 +1002,12 @@ function instantInto(fieldType) {
399
1002
  * row facts through the one envelope) and the membership, collapse and
400
1003
  * children functions, each reified once and CALLED in the block's loop. The
401
1004
  * block walks the collection in order; each matched entry's row is followed by
402
- * 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
403
1006
  * with an explicit pre-order stack (its depth is data), a step-down calls its
404
1007
  * child series' blocks on the child collection.
405
1008
  */
406
1009
  function dataSpec(rowType, cfg, recipe) {
407
1010
  const where = `Plan.series.${recipe.arm} "${cfg.key}"`;
408
- const children = cfg.children;
409
- const steps = children === undefined || typeof children === "function"
410
- ? [] : (Array.isArray(children) ? children : [children]);
411
- for (const step of steps) {
412
- if (!isStepDown(step)) {
413
- throw new Error(`${where}: \`children\` is an accessor for more of this series, or \`Plan.children(of, [series…])\` step-downs`);
414
- }
415
- }
416
- const nested = steps.flatMap((s) => s.series.map((x) => nestedSpec(x, `${where} › children`)));
417
1011
  // A recursive entry reaches the accessors as its NODE (`PlanEntryExpr`),
418
1012
  // so they read its fields directly at every depth.
419
1013
  const recursive = rowType.type === "Recursive";
@@ -422,6 +1016,33 @@ function dataSpec(rowType, cfg, recipe) {
422
1016
  : value;
423
1017
  /** An accessor over entries, applied to an entry value (its node, when recursive). */
424
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);
425
1046
  // The removed verdict, named (#1260) — a plain JS caller would otherwise
426
1047
  // lose it silently.
427
1048
  for (const removed of ["review", "approval"]) {
@@ -429,30 +1050,25 @@ function dataSpec(rowType, cfg, recipe) {
429
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`);
430
1051
  }
431
1052
  }
432
- // What a gesture writes (#880, #825) — a dropped card into `edit.items`, a
433
- // moved element into its item's fields — each a FIELD of the entry (its
434
- // node, when recursive) or of its items, checked here so a misnamed field
435
- // 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.
436
1056
  const edit = recipe.edit;
437
- const nodeType = (recursive ? rowType.node : rowType);
438
- if (edit !== undefined && nodeType.type !== "Struct") {
439
- throw new Error(`${where}: \`edit\` writes a field of the entry — its entries must be structs (a recursive entry's node included)`);
440
- }
441
- const entryFields = nodeType.fields ?? {};
442
- let itemType;
443
- let move;
444
- if (edit !== undefined) {
445
- const t = entryFields[edit.items];
446
- if (t === undefined || t.type !== "Array") {
447
- 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`);
448
- }
449
- itemType = t.value;
450
- move = moveFieldsOf(edit, itemType, recipe.arm, where);
451
- }
452
- 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
453
1068
  ? {
454
- drop: edit.create !== undefined,
1069
+ drop: create !== undefined,
455
1070
  move: move !== undefined ? some(move.edits) : none,
1071
+ values: typedWrites.map((typed) => ({ position: BigInt(typed.position), whole: isTypeEqual(typed.value, IntegerType) })),
456
1072
  }
457
1073
  : undefined;
458
1074
  const rows = new ByType();
@@ -475,7 +1091,7 @@ function dataSpec(rowType, cfg, recipe) {
475
1091
  ...(cfg.value !== undefined ? { value: cfg.value(entry, key) } : {}),
476
1092
  ...(recipe.swatches !== undefined ? { swatches: recipe.swatches } : {}),
477
1093
  }),
478
- kind: recipe.kind(entry, key, hasChildren),
1094
+ kind: recipe.kind(entry, key, hasChildren, value),
479
1095
  collapsed,
480
1096
  ...(recipe.pinned !== undefined ? { pinned: recipe.pinned(entry, key) } : {}),
481
1097
  ...(cfg.status !== undefined ? { status: cfg.status(entry, key) } : {}),
@@ -497,13 +1113,13 @@ function dataSpec(rowType, cfg, recipe) {
497
1113
  ? East.value(collapsed, BooleanType)
498
1114
  : East.value(collapsed(nodeOf(value), key), BooleanType)));
499
1115
  });
500
- /** A bare `children` accessor, built for entries keyed by `kt` — the child collection's type is its output. */
501
- 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)));
502
1118
  /** The subtree walk below one entry — every level's entries in pre-order, over the child collection type. */
503
1119
  const walkFor = (cc) => walks.get(cc, () => {
504
1120
  const at = `${where} › children`;
505
1121
  const { entry, key: kc } = shapeOf(cc, at);
506
- 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)`);
507
1123
  const next = childOf(kc);
508
1124
  const nextType = Expr.type(next).output;
509
1125
  if (!isTypeEqual(nextType, cc)) {
@@ -559,7 +1175,7 @@ function dataSpec(rowType, cfg, recipe) {
559
1175
  const childrenFor = (kt) => kids.get(kt, () => {
560
1176
  if (children === undefined)
561
1177
  return undefined;
562
- if (typeof children === "function") {
1178
+ if (tree !== undefined) {
563
1179
  const of = childOf(kt);
564
1180
  const cc = Expr.type(of).output;
565
1181
  const walk = walkFor(cc);
@@ -567,7 +1183,7 @@ function dataSpec(rowType, cfg, recipe) {
567
1183
  }
568
1184
  const parts = steps.map((step, i) => {
569
1185
  const at = `${where} › children[${i}]`;
570
- const of = building(at, kt, () => reifyAccessor([rowType, kt], onEntry(step.of)));
1186
+ const of = building(at, kt, () => reifyAccessor([rowType, kt], onEntry(stepOf(step))));
571
1187
  const cc = Expr.type(of).output;
572
1188
  const blocks = step.series.map((s) => nestedSpec(s, at).emitter(cc, at));
573
1189
  return { of, cc, blocks };
@@ -583,20 +1199,20 @@ function dataSpec(rowType, cfg, recipe) {
583
1199
  return out;
584
1200
  });
585
1201
  });
586
- // ── Writing a gesture back (#880) ─────────────────────────────────────
1202
+ // ── Writing a gesture back (#880, #1436) ──────────────────────────────
587
1203
  // The entry a writer is handed is DETACHED (the series value's `write`
588
1204
  // copies it), so a nested entry is written in place in its collection and
589
- // a dropped card's item joins its list in place.
1205
+ // an item joins its collection in place, under its id.
590
1206
  const owns = new ByType();
591
1207
  const writers = new ByType();
592
1208
  const entryAccess = (value) => nodeOf(value);
593
1209
  /** The item a dropped card becomes, for entries keyed by `kt`. */
594
- 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)));
595
1211
  /**
596
- * An item at a move's instants (#825) — its declared instant fields set
597
- * from the axis's instants, each on its own arm, every other field as it
598
- * was; `none` when an instant rides an arm the field cannot hold (a time
599
- * 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).
600
1216
  */
601
1217
  const placed = move === undefined ? undefined : (() => {
602
1218
  const m = move;
@@ -628,92 +1244,74 @@ function dataSpec(rowType, cfg, recipe) {
628
1244
  });
629
1245
  })();
630
1246
  /**
631
- * Whether a list holds an item keyed `k` (#825) — the key a move finds its
632
- * item by, so a row's list keeps its keys unique: a card whose item repeats
633
- * one, or a move onto a row already holding the moved item's key, is
634
- * refused rather than leaving two items one key names.
635
- */
636
- const holds = move === undefined ? undefined : (list, k) => list.some((_$, item) => East.equal(item[move.key], k));
637
- /**
638
- * This series' own edit on one of its entries — the row at `path` is its
639
- * own: a dropped card's item into its list, and a moved element's item
640
- * 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.
641
1254
  */
642
1255
  const ownFor = (kt) => owns.get(kt, () => {
643
- if (edit === undefined)
1256
+ const moves = editRead !== undefined && placed !== undefined && move !== undefined;
1257
+ if (!moves && typedWrites.length === 0)
644
1258
  return undefined;
645
- 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;
646
1262
  return East.function([rowType, kt, PathType, PlanGestureType, PlanCarriedType], OptionType(rowType), ($, value, key, path, gesture, carried) => {
647
- const make = create === undefined ? undefined : $.const(create);
648
- const place = placed === undefined ? undefined : $.const(placed);
1263
+ const make = made === undefined ? undefined : $.const(made);
1264
+ const place = moves ? $.const(placed) : undefined;
649
1265
  const result = $.let(none, OptionType(rowType));
650
1266
  $.match(gesture, {
651
1267
  drop: ($2, drop) => {
652
- if (make === undefined || edit === undefined)
1268
+ if (make === undefined)
653
1269
  return;
654
- const list = $2.let(entryAccess(value)[edit.items]);
655
- const item = $2.let(make(drop, value, key), itemType);
656
- const add = ($3) => {
657
- $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)));
658
1274
  $3.assign(result, some(value));
659
- };
660
- // With the item's key declared, a card whose item
661
- // repeats a key in the list is refused (#825).
662
- if (holds === undefined || move === undefined)
663
- add($2);
664
- else
665
- $2.if(holds(list, item[move.key]).not(), add);
1275
+ });
666
1276
  },
667
1277
  move: ($2, mv) => {
668
- if (place === undefined || move === undefined || edit === undefined)
1278
+ if (place === undefined || item === undefined)
669
1279
  return;
670
- const keyField = move.key;
671
- const itemOf = move.item;
672
1280
  const here = $2.let(East.value(variant("entry", { series: cfg.key, path }), PlanRowIdType), PlanRowIdType);
673
- const list = $2.let(entryAccess(value)[edit.items]);
674
- // The element's item, by its key.
675
- 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]);
676
1282
  $2.if(East.equal(here, mv.from).and(() => East.equal(here, mv.to)), ($3) => {
677
- // Along its row, or resized: its instants set where it is.
678
- $3.match(at, {
679
- some: ($4, i) => {
680
- const moved = $4.let(place(list.get(i), mv.start, mv.end), OptionType(itemOf));
681
- $4.match(moved, {
682
- some: ($5, item) => {
683
- $5(list.update(i, item));
684
- $5.assign(result, some(value));
685
- },
686
- });
687
- },
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
+ });
688
1292
  });
689
1293
  }).else(($3) => {
690
- // Its source: taken out of the list, and carried.
1294
+ // Its source: taken out of the collection, and carried.
691
1295
  $3.if(East.equal(here, mv.from), ($4) => {
692
- $4.match(at, {
693
- some: ($5, i) => {
694
- $5(carried.update(East.value(some(East.Blob.encodeBeast(list.get(i), "v2")), OptionType(BlobType))));
695
- const before = $5.let(list.slice(0n, i));
696
- const after = $5.let(list.slice(i.add(1n), list.size()));
697
- $5(list.clear());
698
- $5(list.append(before));
699
- $5(list.append(after));
700
- $5.assign(result, some(value));
701
- },
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));
702
1300
  });
703
1301
  });
704
- // Its target: the carried item put in, at its new
705
- // instants — unless the list already holds its key,
706
- // 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.
707
1305
  $3.if(East.equal(here, mv.to), ($4) => {
708
1306
  const held = $4.let(carried.get(), OptionType(BlobType));
709
1307
  $4.match(held, {
710
1308
  some: ($5, bytes) => {
711
- $5.if(holds(list, mv.key).not(), ($6) => {
712
- const item = $6.let(bytes.decodeBeast(itemOf, "v2"), itemOf);
713
- 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));
714
1312
  $6.match(moved, {
715
- some: ($7, placedItem) => {
716
- $7(list.pushLast(placedItem));
1313
+ some: ($7, next) => {
1314
+ $7(coll.insert(mv.key, next));
717
1315
  $7(carried.update(East.value(none, OptionType(BlobType))));
718
1316
  $7.assign(result, some(value));
719
1317
  },
@@ -724,11 +1322,55 @@ function dataSpec(rowType, cfg, recipe) {
724
1322
  });
725
1323
  });
726
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
+ },
727
1369
  });
728
1370
  return result;
729
1371
  });
730
1372
  });
731
- /** 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. */
732
1374
  const walkWriterFor = (kt) => {
733
1375
  const at = `${where} › children`;
734
1376
  const of = childOf(kt);
@@ -737,10 +1379,10 @@ function dataSpec(rowType, cfg, recipe) {
737
1379
  const own = ownFor(kc);
738
1380
  if (own === undefined)
739
1381
  return undefined;
740
- const ref = childRefOf(rowType, kt, children);
1382
+ const ref = treeField !== undefined ? { kind: "field", field: treeField } : childRefOf(rowType, kt, tree);
741
1383
  if (ref === undefined) {
742
1384
  throw new Error(`${at}: this series takes gestures, and one on a child row is written back into its entry through ` +
743
- "`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");
744
1386
  }
745
1387
  const ops = collectionOps(cc);
746
1388
  const collOf = (entry) => (ref.kind === "field" ? entryAccess(entry)[ref.field] : nodeOf(entry));
@@ -785,7 +1427,7 @@ function dataSpec(rowType, cfg, recipe) {
785
1427
  const stepsWriterFor = (kt) => {
786
1428
  const parts = steps.flatMap((step, i) => {
787
1429
  const at = `${where} › children[${i}]`;
788
- const accessor = step.of;
1430
+ const accessor = stepOf(step);
789
1431
  const of = building(at, kt, () => reifyAccessor([rowType, kt], onEntry(accessor)));
790
1432
  const cc = Expr.type(of).output;
791
1433
  const writes = step.series
@@ -793,11 +1435,11 @@ function dataSpec(rowType, cfg, recipe) {
793
1435
  .filter((w) => w !== undefined);
794
1436
  if (writes.length === 0)
795
1437
  return [];
796
- const ref = childRefOf(rowType, kt, accessor);
1438
+ const ref = step.field !== undefined ? { kind: "field", field: step.field } : childRefOf(rowType, kt, accessor);
797
1439
  if (ref === undefined) {
798
1440
  throw new Error(`${at}: a series under this step-down takes gestures, and one on its rows is written back into ` +
799
- "the entry through `Plan.children(of, …)` — `of` must read a field of the entry (`r => r.presses`) " +
800
- "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");
801
1443
  }
802
1444
  const { entry: childType, key: kc } = shapeOf(cc, at);
803
1445
  return [{ ref, cc, kc, childType, writes }];
@@ -836,7 +1478,7 @@ function dataSpec(rowType, cfg, recipe) {
836
1478
  const writerFor = (kt) => writers.get(kt, () => {
837
1479
  const own = ownFor(kt);
838
1480
  const below = children === undefined ? undefined
839
- : typeof children === "function" ? walkWriterFor(kt) : stepsWriterFor(kt);
1481
+ : tree !== undefined ? walkWriterFor(kt) : stepsWriterFor(kt);
840
1482
  if (own === undefined && below === undefined)
841
1483
  return undefined;
842
1484
  return East.function([rowType, kt, PathType, IntegerType, StringType, PlanGestureType, PlanCarriedType], OptionType(rowType), ($, value, key, path, depth, series, gesture, carried) => {
@@ -861,6 +1503,22 @@ function dataSpec(rowType, cfg, recipe) {
861
1503
  arm: recipe.arm,
862
1504
  nested,
863
1505
  nests: children !== undefined,
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
+ },
864
1522
  emitter(collection, at) {
865
1523
  return emitters.get(collection, () => {
866
1524
  const { entry, key: kt } = shapeOf(collection, at);
@@ -906,7 +1564,7 @@ function dataSpec(rowType, cfg, recipe) {
906
1564
  },
907
1565
  // One block of the entries' rows — what a paged canvas pages (#823).
908
1566
  blocks(collection, at) {
909
- return blockLists.get(collection, () => oneBlock(collection, spec.emitter(collection, at), false));
1567
+ return blockLists.get(collection, () => oneBlock(collection, spec.emitter(collection, at), false, charts));
910
1568
  },
911
1569
  entryParts(entry, kt, at) {
912
1570
  assertEntry(entry, rowType, at);
@@ -953,10 +1611,13 @@ function viewsSpec(rowType, cfg, members) {
953
1611
  const emitters = new ByType();
954
1612
  const blockLists = new ByType();
955
1613
  const writerLists = new ByType();
956
- // ── A bare `children` accessor: more of the views' own entries (#1219) ──
957
- const bare = typeof cfg.children === "function"
958
- ? cfg.children
959
- : 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;
960
1621
  const recursive = rowType.type === "Recursive";
961
1622
  const nodeOf = (value) => recursive
962
1623
  ? value.unwrap()
@@ -975,7 +1636,7 @@ function viewsSpec(rowType, cfg, members) {
975
1636
  const walkFor = (cc) => walks.get(cc, () => {
976
1637
  const at = `${where} › children`;
977
1638
  const { entry, key: kc } = shapeOf(cc, at);
978
- 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)`);
979
1640
  const next = childOf(kc);
980
1641
  const nextType = Expr.type(next).output;
981
1642
  if (!isTypeEqual(nextType, cc)) {
@@ -1083,10 +1744,10 @@ function viewsSpec(rowType, cfg, members) {
1083
1744
  });
1084
1745
  if (owns.length === 0)
1085
1746
  return undefined;
1086
- const ref = childRefOf(rowType, kt, bare);
1747
+ const ref = treeField !== undefined ? { kind: "field", field: treeField } : childRefOf(rowType, kt, bare);
1087
1748
  if (ref === undefined) {
1088
1749
  throw new Error(`${at}: a member takes gestures, and one on a child row is written back into its entry through ` +
1089
- "`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");
1090
1751
  }
1091
1752
  const ops = collectionOps(cc);
1092
1753
  const collOf = (value) => (ref.kind === "field" ? entryAccess(value)[ref.field] : nodeOf(value));
@@ -1129,12 +1790,21 @@ function viewsSpec(rowType, cfg, members) {
1129
1790
  return result;
1130
1791
  });
1131
1792
  });
1793
+ // How many chart rows each entry makes (#1398): a row a member — a chart member's own `match` deciding by entry
1794
+ // whether its row shows — and below them a tree's walk of more of the views' entries, each its members'
1795
+ // rows, or a step-down's series' over the child collection.
1796
+ const memberCharts = chartsOf(memberSpecs);
1797
+ const charts = entryCharts(memberCharts, cfg.children === undefined ? 0 : bare !== undefined ? memberCharts : chartsOf(host.nested), cfg.match !== undefined);
1132
1798
  const spec = {
1133
1799
  key: cfg.key,
1134
1800
  title: cfg.title,
1135
1801
  arm: "views",
1136
1802
  nested: [...memberSpecs, ...host.nested],
1137
1803
  nests: cfg.children !== undefined,
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 },
1138
1808
  emitter(collection, at) {
1139
1809
  return emitters.get(collection, () => {
1140
1810
  const { entry, key: kt } = shapeOf(collection, at);
@@ -1212,7 +1882,7 @@ function viewsSpec(rowType, cfg, members) {
1212
1882
  },
1213
1883
  // One block of the entries' view rows — what a paged canvas pages (#823).
1214
1884
  blocks(collection, at) {
1215
- return blockLists.get(collection, () => oneBlock(collection, spec.emitter(collection, at), false));
1885
+ return blockLists.get(collection, () => oneBlock(collection, spec.emitter(collection, at), false, charts));
1216
1886
  },
1217
1887
  // An entry's view rows are its members' own (each at the entry's path,
1218
1888
  // under its member's key), and the rows below them the views' (#880).
@@ -1275,6 +1945,14 @@ function sectionSpec(cfg, members) {
1275
1945
  arm: "section",
1276
1946
  nested: memberSpecs,
1277
1947
  nests: false,
1948
+ // Each entry makes its members' rows; the header is no entry's (#1398).
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
+ },
1278
1956
  emitter(collection, at) {
1279
1957
  return emitters.get(collection, () => {
1280
1958
  const blocks = memberSpecs.map((m) => m.emitter(collection, `${at} › ${where}`));
@@ -1298,7 +1976,7 @@ function sectionSpec(cfg, members) {
1298
1976
  return East.function([collection, PathType, IdOptType], PlanBlocksType, ($, coll, prefix, parent) => {
1299
1977
  const id = $.let(East.value(variant("section", { series: cfg.key, path: prefix }), PlanRowIdType), PlanRowIdType);
1300
1978
  const under = $.let(East.value(some(id), IdOptType), IdOptType);
1301
- const head = $.let(East.value({ fixed: true, parent, rows: [header(id, parent)] }, PlanBlockType), PlanBlockType);
1979
+ const head = $.let(East.value({ fixed: true, parent, charts: none, rows: [header(id, parent)] }, PlanBlockType), PlanBlockType);
1302
1980
  const out = $.let(East.value([head], PlanBlocksType), PlanBlocksType);
1303
1981
  for (const member of members)
1304
1982
  $(out.append(member(coll, prefix, under)));
@@ -1342,11 +2020,15 @@ function rowsSpec(identity, rows) {
1342
2020
  arm: "rows",
1343
2021
  nested: [],
1344
2022
  nests: false,
2023
+ // Its rows' kinds are an East value the build does not read (#1398): laid out below an entry, their chart rows are
2024
+ // not known.
2025
+ charts: undefined,
2026
+ reads: [],
1345
2027
  emitter,
1346
2028
  // A FIXED block: no entry produces hand-built rows, so every window
1347
2029
  // serves them alike and the canvas draws them once (#823).
1348
2030
  blocks(collection) {
1349
- return blockLists.get(collection, () => oneBlock(collection, emitter(collection), true));
2031
+ return blockLists.get(collection, () => oneBlock(collection, emitter(collection), true, undefined));
1350
2032
  },
1351
2033
  // No entry produces them, so no gesture is written into one (#880).
1352
2034
  writer() {
@@ -1469,17 +2151,34 @@ export function applySeriesValue(s, data) {
1469
2151
  }
1470
2152
  /**
1471
2153
  * Check a TS series list before it is applied — its keys unique across the
1472
- * whole tree. An East expression list is checked at render (a repeated id is
1473
- * 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.
1474
2157
  *
1475
2158
  * @param series - The `series` input
1476
2159
  * @param where - Who is asking, for the message
2160
+ * @param axisKind - The axis's kind, when its declaration says it
1477
2161
  */
1478
- export function checkSeries(series, where) {
2162
+ export function checkSeries(series, where, axisKind) {
1479
2163
  if (!Array.isArray(series))
1480
2164
  return;
1481
2165
  const specs = series.map((s) => specOf(s)).filter((s) => s !== undefined);
1482
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);
1483
2182
  }
1484
2183
  /**
1485
2184
  * Reads a series value's facts off the spec its builder recorded (#1190).
@@ -1495,7 +2194,10 @@ export function planSeriesFacts(series, collection, where) {
1495
2194
  const spec = specOf(series);
1496
2195
  if (spec === undefined)
1497
2196
  return undefined;
1498
- return { arm: spec.arm, key: spec.key, title: spec.title, nests: spec.nests, writes: spec.writer(collection, where) !== undefined };
2197
+ return {
2198
+ arm: spec.arm, key: spec.key, title: spec.title, nests: spec.nests, writes: spec.writer(collection, where) !== undefined,
2199
+ charts: spec.charts,
2200
+ };
1499
2201
  }
1500
2202
  /**
1501
2203
  * The keys of a series list's whole tree, each series' and every series
@@ -1615,9 +2317,6 @@ export function seriesWriteFn(series, collection) {
1615
2317
  return result;
1616
2318
  });
1617
2319
  }
1618
- // ============================================================================
1619
- // Builders — Plan.series.*
1620
- // ============================================================================
1621
2320
  /**
1622
2321
  * A span series — one state-run row per entry; a row with children shows its
1623
2322
  * own runs plus bands over its subtree's runs (`rollup`) — each band sums its
@@ -1626,27 +2325,46 @@ export function seriesWriteFn(series, collection) {
1626
2325
  * @typeParam R - The entry type
1627
2326
  * @typeParam K - The axis kind the runs imply (inferred from `runs`; `never` when erased)
1628
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
1629
2337
  * @param rowType - The entry type (every builder takes it first — the `Slice.config` shape)
1630
- * @param config - The accessors and nesting ({@link PlanSpanSeriesConfig})
1631
- * @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
1632
2340
  */
1633
2341
  export function createSeriesSpan(rowType, config) {
1634
2342
  const cfg = config;
2343
+ const where = `Plan.series.span "${cfg.key}"`;
2344
+ const edit = editRecipeOf(cfg.edit, "runs", "{ from, to }", cfg.runs, where);
1635
2345
  // A row rolls up its children: its own (`children`), or — declared on a
1636
2346
  // views member — the ones the views nest under it (#1219).
1637
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");
1638
2353
  const spec = dataSpec(rowType, cfg, {
1639
2354
  arm: "span",
1640
- kind: (value, key, hasChildren) => spanKind({
1641
- runs: cfg.runs(value, key),
1642
- ...(cfg.decisions !== undefined ? { decisions: cfg.decisions(value, key) } : {}),
1643
- ...(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) } : {}),
1644
2359
  }, rollup === undefined
1645
2360
  ? East.value(none, OptionType(PlanRollupType))
1646
2361
  // A row rolls up only when it HAS child rows — a leaf's bands
1647
2362
  // would repeat its own runs.
1648
2363
  : hasChildren.ifElse(() => East.value(some(rollup), OptionType(PlanRollupType)), () => East.value(none, OptionType(PlanRollupType)))),
1649
- 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") } : {}),
1650
2368
  });
1651
2369
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1652
2370
  }
@@ -1657,21 +2375,33 @@ export function createSeriesSpan(rowType, config) {
1657
2375
  * @typeParam R - The entry type
1658
2376
  * @typeParam K - The axis kind the cells imply (inferred from `cells`; `never` when erased)
1659
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
1660
2385
  * @param rowType - The entry type
1661
- * @param config - The accessors and nesting ({@link PlanHeatSeriesConfig})
1662
- * @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
1663
2388
  */
1664
2389
  export function createSeriesHeat(rowType, config) {
1665
2390
  const cfg = config;
2391
+ const where = `Plan.series.heat "${cfg.key}"`;
1666
2392
  const aggregate = cfg.children !== undefined
1667
2393
  ? East.value(some(resolveTag(cfg.aggregate ?? "mean", PlanAggregateType)), OptionType(PlanAggregateType))
1668
2394
  : East.value(none, OptionType(PlanAggregateType));
2395
+ const cells = consumerField(rowType, cfg.cells, "heat", where, "cells");
1669
2396
  const spec = dataSpec(rowType, cfg, {
1670
2397
  arm: "heat",
1671
- kind: (value, key) => heatKind({
1672
- cells: cfg.cells(value, key),
2398
+ kind: (node, key, _hasChildren, value) => heatKind({
2399
+ ...(cells !== undefined ? { cells: cells.at(node, key, value) } : {}),
1673
2400
  ...(cfg.scale !== undefined ? { scale: cfg.scale } : {}),
1674
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 } : {}),
1675
2405
  });
1676
2406
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1677
2407
  }
@@ -1682,28 +2412,48 @@ export function createSeriesHeat(rowType, config) {
1682
2412
  * @typeParam R - The entry type
1683
2413
  * @typeParam K - The axis kind the cells imply (inferred from `cells` / `series`; `never` when erased)
1684
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
1685
2423
  * @param rowType - The entry type
1686
- * @param config - The accessors and nesting ({@link PlanTableSeriesOfConfig})
1687
- * @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
1688
2426
  */
1689
2427
  export function createSeriesTable(rowType, config) {
1690
2428
  const cfg = config;
2429
+ const where = `Plan.series.table "${cfg.key}"`;
1691
2430
  if (cfg.cells !== undefined && cfg.series !== undefined) {
1692
- 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`);
1693
2432
  }
1694
2433
  const aggregate = cfg.children !== undefined
1695
2434
  ? East.value(some(resolveTag(cfg.aggregate ?? "sum", TableAggregateType)), OptionType(TableAggregateType))
1696
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);
1697
2443
  const spec = dataSpec(rowType, cfg, {
1698
2444
  arm: "table",
1699
- kind: (value, key) => tableKind({
1700
- ...(cfg.cells !== undefined ? { cells: cfg.cells(value, key) } : {}),
2445
+ kind: (node, key, _hasChildren, value) => tableKind({
2446
+ ...(cells !== undefined ? { cells: cells.at(node, key, value) } : {}),
1701
2447
  ...(cfg.fold !== undefined ? { fold: cfg.fold } : {}),
1702
- ...(cfg.series !== undefined ? { series: cfg.series(value, key) } : {}),
2448
+ ...(positions !== undefined ? { series: positions.at(node, key, value) } : {}),
1703
2449
  ...(cfg.split !== undefined ? { split: cfg.split } : {}),
1704
2450
  ...(cfg.format !== undefined ? { format: cfg.format } : {}),
1705
2451
  ...(cfg.emphasis !== undefined ? { emphasis: cfg.emphasis } : {}),
1706
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 } : {}),
1707
2457
  });
1708
2458
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1709
2459
  }
@@ -1713,20 +2463,34 @@ export function createSeriesTable(rowType, config) {
1713
2463
  * @typeParam R - The entry type
1714
2464
  * @typeParam K - The axis kind the tiles imply (inferred from `events` / `markers`; `never` when erased)
1715
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
1716
2473
  * @param rowType - The entry type
1717
- * @param config - The accessors ({@link PlanBucketsSeriesConfig})
1718
- * @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
1719
2476
  */
1720
2477
  export function createSeriesBuckets(rowType, config) {
1721
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");
1722
2485
  const spec = dataSpec(rowType, cfg, {
1723
2486
  arm: "buckets",
1724
- kind: (value, key) => bucketsKind({
1725
- ...(cfg.lanes !== undefined ? { lanes: cfg.lanes(value, key) } : {}),
1726
- events: cfg.events(value, key),
1727
- ...(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) } : {}),
1728
2491
  }),
1729
- edit: cfg.edit,
2492
+ edit,
2493
+ reads: [...events.reads, ...(markers?.reads ?? [])],
1730
2494
  });
1731
2495
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1732
2496
  }
@@ -1736,16 +2500,28 @@ export function createSeriesBuckets(rowType, config) {
1736
2500
  * @typeParam R - The entry type
1737
2501
  * @typeParam K - The axis kind the chips imply (inferred from `chips`; `never` when erased)
1738
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
1739
2509
  * @param rowType - The entry type
1740
- * @param config - The accessors ({@link PlanCardsSeriesConfig})
1741
- * @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
1742
2512
  */
1743
2513
  export function createSeriesCards(rowType, config) {
1744
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`);
1745
2520
  const spec = dataSpec(rowType, cfg, {
1746
2521
  arm: "cards",
1747
- kind: (value, key) => cardsKind(cfg.chips(value, key)),
1748
- edit: cfg.edit,
2522
+ kind: (node, key, _hasChildren, value) => cardsKind(chips.at(node, key, value)),
2523
+ edit,
2524
+ reads: chips.reads,
1749
2525
  });
1750
2526
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1751
2527
  }
@@ -1755,37 +2531,61 @@ export function createSeriesCards(rowType, config) {
1755
2531
  * @typeParam R - The entry type
1756
2532
  * @typeParam K - The axis kind the marks imply (inferred from `marks`; `never` when erased)
1757
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
1758
2540
  * @param rowType - The entry type
1759
- * @param config - The accessors ({@link PlanEventsSeriesConfig})
1760
- * @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
1761
2543
  */
1762
2544
  export function createSeriesEvents(rowType, config) {
1763
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`);
1764
2551
  const spec = dataSpec(rowType, cfg, {
1765
2552
  arm: "events",
1766
- kind: (value, key) => eventsKind(cfg.marks(value, key)),
1767
- edit: cfg.edit,
2553
+ kind: (node, key, _hasChildren, value) => eventsKind(marks.at(node, key, value)),
2554
+ edit,
2555
+ reads: marks.reads,
1768
2556
  });
1769
2557
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1770
2558
  }
1771
2559
  /**
1772
- * A chart series — one chart row per entry, layers built from the entry's own
1773
- * data. Kind-ERASED: a Chart layer's TS face does not expose its x type, so a
1774
- * chart series constrains no axis at compile time — the render-time
1775
- * 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.
1776
2566
  *
1777
2567
  * @typeParam R - The entry type
1778
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
1779
2575
  * @param rowType - The entry type
1780
- * @param config - The accessors and shared axes ({@link PlanChartSeriesConfig})
1781
- * @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
1782
2578
  */
1783
2579
  export function createSeriesChart(rowType, config) {
1784
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`);
1785
2585
  const spec = dataSpec(rowType, cfg, {
1786
2586
  arm: "chart",
1787
- kind: (value, key) => chartKind({
1788
- layers: cfg.layers(value, key),
2587
+ kind: (node, key, _hasChildren, value) => chartKind({
2588
+ layers: layers.at(node, key, value),
1789
2589
  ...(cfg.left !== undefined ? { left: cfg.left } : {}),
1790
2590
  ...(cfg.right !== undefined ? { right: cfg.right } : {}),
1791
2591
  ...(cfg.height !== undefined ? { height: cfg.height } : {}),
@@ -1796,6 +2596,7 @@ export function createSeriesChart(rowType, config) {
1796
2596
  ? { pinned: (value, key) => East.value(cfg.pinned(value, key), BooleanType) }
1797
2597
  : {}),
1798
2598
  ...(cfg.swatches !== undefined ? { swatches: cfg.swatches } : {}),
2599
+ reads: layers.reads,
1799
2600
  });
1800
2601
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1801
2602
  }
@@ -1806,19 +2607,25 @@ export function createSeriesChart(rowType, config) {
1806
2607
  * its member count is derived.
1807
2608
  *
1808
2609
  * @remarks
1809
- * Hierarchy comes from the data's nesting: to group a flat source, reshape it
1810
- * first — inline, one East `groupToDicts` in the canvas function (the entries
1811
- * are then `Dict<String, Row>` and a group's label is its key); a paged source
1812
- * is grouped in the dataflow. The `by` form and the static
1813
- * `group(R, chrome, children)` form are gone — see `Plan.series.section` for a
1814
- * 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.
1815
2615
  *
1816
2616
  * @typeParam R - The entry type
1817
2617
  * @typeParam K - The axis kind the summary cells imply
1818
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
1819
2626
  * @param rowType - The entry type
1820
2627
  * @param config - The strip's accessors and its members ({@link PlanGroupSeriesConfig})
1821
- * @returns A series value, branded with its kind
2628
+ * @returns A series value, branded with its kind and what it reads
1822
2629
  * @throws {Error} When called with the removed `by` or static forms
1823
2630
  */
1824
2631
  export function createSeriesGroup(rowType, config) {
@@ -1834,17 +2641,20 @@ export function createSeriesGroup(rowType, config) {
1834
2641
  if (cfg.children === undefined) {
1835
2642
  throw new Error(`${where}: a group strip needs its members — declare \`children\``);
1836
2643
  }
1837
- const summary = cfg.summary;
1838
- // The strip's ONE summary (#824): the per-entry cells, the declared
1839
- // aggregate, or a plain band — both at once is refused here.
1840
- const declared = summary === undefined ? groupSummary(undefined, cfg.summaryAggregate, where) : undefined;
1841
- if (summary !== undefined && cfg.summaryAggregate !== undefined) {
2644
+ if (cfg.summary !== undefined && cfg.summaryAggregate !== undefined) {
1842
2645
  throw new Error(`${where}: give \`summary\` (explicit strip cells) OR \`summaryAggregate\` (cells derived from the ` +
1843
2646
  "members) — a collapsed strip shows one or the other");
1844
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;
1845
2652
  const spec = dataSpec(rowType, cfg, {
1846
2653
  arm: "group",
1847
- 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") } : {}),
1848
2658
  });
1849
2659
  return seriesValue(rowType, cfg.keyType, spec, cfg);
1850
2660
  }
@@ -1856,10 +2666,11 @@ export function createSeriesGroup(rowType, config) {
1856
2666
  *
1857
2667
  * @typeParam R - The entry type (the members' — they share the section's entries)
1858
2668
  * @typeParam K - The union of the members' axis kinds (inferred; `never` when all are erased)
2669
+ * @typeParam S - The members, as written
1859
2670
  * @param rowType - The entry type
1860
2671
  * @param config - The header ({@link PlanSectionSeriesConfig})
1861
2672
  * @param members - The member series, in order
1862
- * @returns A series value, branded with its members' kinds
2673
+ * @returns A series value, branded with its members' kinds and what they read
1863
2674
  */
1864
2675
  export function createSeriesSection(rowType, config, members) {
1865
2676
  const cfg = config;
@@ -1878,7 +2689,7 @@ export function createSeriesSection(rowType, config, members) {
1878
2689
  * view row's id is its member series' key and the entry's path, and a seek on
1879
2690
  * the entry lands on its first view row.
1880
2691
  *
1881
- * 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
1882
2693
  * of the views' own entries, each drawn as its members' rows, to any depth,
1883
2694
  * the views' `match` and `collapsed` at every level (#1219). A span member that
1884
2695
  * declares `rollup` rolls up the children nested under its row.
@@ -1886,10 +2697,15 @@ export function createSeriesSection(rowType, config, members) {
1886
2697
  * @typeParam R - The entry type
1887
2698
  * @typeParam K - The union of the members' axis kinds (inferred; `never` when all are erased)
1888
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
1889
2705
  * @param rowType - The entry type
1890
2706
  * @param config - Membership, collapse and children ({@link PlanViewsSeriesConfig})
1891
2707
  * @param members - The member series — one row each per entry, in order
1892
- * @returns A series value, branded with its members' kinds
2708
+ * @returns A series value, branded with its members' kinds and what it reads
1893
2709
  */
1894
2710
  export function createSeriesViews(rowType, config, members) {
1895
2711
  const cfg = config;