@elaraai/e3-ui 1.0.102 → 1.0.104

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 (326) hide show
  1. package/dist/src/allocation/functions.d.ts +2 -1
  2. package/dist/src/allocation/functions.d.ts.map +1 -1
  3. package/dist/src/allocation/functions.js +3 -2
  4. package/dist/src/allocation/functions.js.map +1 -1
  5. package/dist/src/allocation/index.d.ts +120 -1
  6. package/dist/src/allocation/index.d.ts.map +1 -1
  7. package/dist/src/allocation/index.js +24 -4
  8. package/dist/src/allocation/index.js.map +1 -1
  9. package/dist/src/bind/data-indexed.d.ts +25 -4
  10. package/dist/src/bind/data-indexed.d.ts.map +1 -1
  11. package/dist/src/bind/data-indexed.js +58 -7
  12. package/dist/src/bind/data-indexed.js.map +1 -1
  13. package/dist/src/bind/data.d.ts +12 -14
  14. package/dist/src/bind/data.d.ts.map +1 -1
  15. package/dist/src/bind/data.js +11 -11
  16. package/dist/src/bind/data.js.map +1 -1
  17. package/dist/src/bind/func.d.ts +7 -2
  18. package/dist/src/bind/func.d.ts.map +1 -1
  19. package/dist/src/bind/func.js +10 -3
  20. package/dist/src/bind/func.js.map +1 -1
  21. package/dist/src/bind/record.d.ts +6 -6
  22. package/dist/src/bind/record.d.ts.map +1 -1
  23. package/dist/src/bind/record.js.map +1 -1
  24. package/dist/src/calendar/index.d.ts +148 -1
  25. package/dist/src/calendar/index.d.ts.map +1 -1
  26. package/dist/src/calendar/index.js +55 -5
  27. package/dist/src/calendar/index.js.map +1 -1
  28. package/dist/src/decision/bind.d.ts.map +1 -1
  29. package/dist/src/decision/index.d.ts +218 -218
  30. package/dist/src/decision/index.d.ts.map +1 -1
  31. package/dist/src/decision/types.d.ts.map +1 -1
  32. package/dist/src/flowchart/canvas.d.ts +32 -14
  33. package/dist/src/flowchart/canvas.d.ts.map +1 -1
  34. package/dist/src/flowchart/canvas.js +21 -6
  35. package/dist/src/flowchart/canvas.js.map +1 -1
  36. package/dist/src/flowchart/details.d.ts +469 -0
  37. package/dist/src/flowchart/details.d.ts.map +1 -0
  38. package/dist/src/flowchart/details.js +1035 -0
  39. package/dist/src/flowchart/details.js.map +1 -0
  40. package/dist/src/flowchart/flowchart.d.ts +16 -72
  41. package/dist/src/flowchart/flowchart.d.ts.map +1 -1
  42. package/dist/src/flowchart/flowchart.js +10 -1
  43. package/dist/src/flowchart/flowchart.js.map +1 -1
  44. package/dist/src/flowchart/index.d.ts +282 -251
  45. package/dist/src/flowchart/index.d.ts.map +1 -1
  46. package/dist/src/flowchart/index.js +234 -220
  47. package/dist/src/flowchart/index.js.map +1 -1
  48. package/dist/src/flowchart/kinds.d.ts +438 -0
  49. package/dist/src/flowchart/kinds.d.ts.map +1 -0
  50. package/dist/src/flowchart/kinds.js +799 -0
  51. package/dist/src/flowchart/kinds.js.map +1 -0
  52. package/dist/src/flowchart/library.d.ts +155 -740
  53. package/dist/src/flowchart/library.d.ts.map +1 -1
  54. package/dist/src/flowchart/library.js +191 -673
  55. package/dist/src/flowchart/library.js.map +1 -1
  56. package/dist/src/flowchart/payload.d.ts +3654 -7845
  57. package/dist/src/flowchart/payload.d.ts.map +1 -1
  58. package/dist/src/flowchart/payload.js +331 -469
  59. package/dist/src/flowchart/payload.js.map +1 -1
  60. package/dist/src/flowchart/templates.d.ts +657 -0
  61. package/dist/src/flowchart/templates.d.ts.map +1 -0
  62. package/dist/src/flowchart/templates.js +658 -0
  63. package/dist/src/flowchart/templates.js.map +1 -0
  64. package/dist/src/flowchart/types.d.ts +2258 -214
  65. package/dist/src/flowchart/types.d.ts.map +1 -1
  66. package/dist/src/flowchart/types.js +374 -124
  67. package/dist/src/flowchart/types.js.map +1 -1
  68. package/dist/src/index.d.ts +13 -15
  69. package/dist/src/index.d.ts.map +1 -1
  70. package/dist/src/index.js +24 -29
  71. package/dist/src/index.js.map +1 -1
  72. package/dist/src/internal.d.ts +5 -7
  73. package/dist/src/internal.d.ts.map +1 -1
  74. package/dist/src/internal.js +12 -19
  75. package/dist/src/internal.js.map +1 -1
  76. package/dist/src/ontology/index.d.ts +44 -15
  77. package/dist/src/ontology/index.d.ts.map +1 -1
  78. package/dist/src/ontology/index.js +19 -11
  79. package/dist/src/ontology/index.js.map +1 -1
  80. package/dist/src/plan/builders.d.ts +26 -26
  81. package/dist/src/plan/builders.d.ts.map +1 -1
  82. package/dist/src/plan/builders.js.map +1 -1
  83. package/dist/src/plan/event-rows.d.ts +59 -58
  84. package/dist/src/plan/event-rows.d.ts.map +1 -1
  85. package/dist/src/plan/event-rows.js +6 -4
  86. package/dist/src/plan/event-rows.js.map +1 -1
  87. package/dist/src/plan/factories.js.map +1 -1
  88. package/dist/src/plan/ir.d.ts +1 -0
  89. package/dist/src/plan/ir.d.ts.map +1 -1
  90. package/dist/src/plan/library.d.ts.map +1 -1
  91. package/dist/src/plan/nested-checks.js.map +1 -1
  92. package/dist/src/plan/plan.d.ts +567 -423
  93. package/dist/src/plan/plan.d.ts.map +1 -1
  94. package/dist/src/plan/plan.js +67 -3
  95. package/dist/src/plan/plan.js.map +1 -1
  96. package/dist/src/plan/series.d.ts.map +1 -1
  97. package/dist/src/plan/series.js.map +1 -1
  98. package/dist/src/plan/types.d.ts +173 -172
  99. package/dist/src/plan/types.d.ts.map +1 -1
  100. package/dist/src/query/builder.d.ts +368 -321
  101. package/dist/src/query/builder.d.ts.map +1 -1
  102. package/dist/src/query/builder.js +12 -1
  103. package/dist/src/query/builder.js.map +1 -1
  104. package/dist/src/query/index.d.ts +5 -1
  105. package/dist/src/query/index.d.ts.map +1 -1
  106. package/dist/src/query/index.js +3 -1
  107. package/dist/src/query/index.js.map +1 -1
  108. package/dist/src/query/library.d.ts +6 -6
  109. package/dist/src/query/queries.d.ts +3 -3
  110. package/dist/src/roster/fit.d.ts +5 -2
  111. package/dist/src/roster/fit.d.ts.map +1 -1
  112. package/dist/src/roster/fit.js +5 -3
  113. package/dist/src/roster/fit.js.map +1 -1
  114. package/dist/src/roster/index.d.ts +103 -2
  115. package/dist/src/roster/index.d.ts.map +1 -1
  116. package/dist/src/roster/index.js +45 -6
  117. package/dist/src/roster/index.js.map +1 -1
  118. package/dist/src/runtime/index.d.ts +1 -3
  119. package/dist/src/runtime/index.d.ts.map +1 -1
  120. package/dist/src/runtime/index.js +1 -3
  121. package/dist/src/runtime/index.js.map +1 -1
  122. package/dist/src/schedule/fields.js.map +1 -1
  123. package/dist/src/schedule/library.js.map +1 -1
  124. package/dist/src/schedule/record-window.d.ts +27 -26
  125. package/dist/src/schedule/record-window.d.ts.map +1 -1
  126. package/dist/src/schedule/types.d.ts +196 -196
  127. package/dist/src/schematic/index.d.ts +1 -1
  128. package/dist/src/schematic/index.d.ts.map +1 -1
  129. package/dist/src/schematic/index.js +1 -1
  130. package/dist/src/schematic/index.js.map +1 -1
  131. package/dist/src/schematic/kinds.d.ts +4 -24
  132. package/dist/src/schematic/kinds.d.ts.map +1 -1
  133. package/dist/src/schematic/kinds.js +8 -212
  134. package/dist/src/schematic/kinds.js.map +1 -1
  135. package/dist/src/schematic/library.d.ts.map +1 -1
  136. package/dist/src/schematic/payload.d.ts +1688 -1549
  137. package/dist/src/schematic/payload.d.ts.map +1 -1
  138. package/dist/src/schematic/payload.js +38 -1
  139. package/dist/src/schematic/payload.js.map +1 -1
  140. package/dist/src/schematic/shapes.d.ts.map +1 -1
  141. package/dist/src/schematic/types.d.ts +978 -978
  142. package/dist/src/schematic/types.d.ts.map +1 -1
  143. package/dist/src/schematic/types.js +4 -36
  144. package/dist/src/schematic/types.js.map +1 -1
  145. package/dist/src/schematic/window.d.ts +9 -42
  146. package/dist/src/schematic/window.d.ts.map +1 -1
  147. package/dist/src/schematic/window.js +10 -115
  148. package/dist/src/schematic/window.js.map +1 -1
  149. package/dist/src/sheet/actions.d.ts +388 -0
  150. package/dist/src/sheet/actions.d.ts.map +1 -0
  151. package/dist/src/sheet/actions.js +594 -0
  152. package/dist/src/sheet/actions.js.map +1 -0
  153. package/dist/src/sheet/bridge.d.ts +12 -1
  154. package/dist/src/sheet/bridge.d.ts.map +1 -1
  155. package/dist/src/sheet/bridge.js +30 -6
  156. package/dist/src/sheet/bridge.js.map +1 -1
  157. package/dist/src/sheet/columns.d.ts +5 -2
  158. package/dist/src/sheet/columns.d.ts.map +1 -1
  159. package/dist/src/sheet/columns.js.map +1 -1
  160. package/dist/src/sheet/draft-bridge.d.ts +9 -4
  161. package/dist/src/sheet/draft-bridge.d.ts.map +1 -1
  162. package/dist/src/sheet/draft-bridge.js +22 -7
  163. package/dist/src/sheet/draft-bridge.js.map +1 -1
  164. package/dist/src/sheet/editing-types.d.ts +71 -71
  165. package/dist/src/sheet/fields.d.ts +13 -0
  166. package/dist/src/sheet/fields.d.ts.map +1 -1
  167. package/dist/src/sheet/fields.js +3 -0
  168. package/dist/src/sheet/fields.js.map +1 -1
  169. package/dist/src/sheet/group.js.map +1 -1
  170. package/dist/src/sheet/index.d.ts +32 -2
  171. package/dist/src/sheet/index.d.ts.map +1 -1
  172. package/dist/src/sheet/index.js +18 -3
  173. package/dist/src/sheet/index.js.map +1 -1
  174. package/dist/src/sheet/library.d.ts +8 -0
  175. package/dist/src/sheet/library.d.ts.map +1 -1
  176. package/dist/src/sheet/library.js.map +1 -1
  177. package/dist/src/sheet/link.d.ts.map +1 -1
  178. package/dist/src/sheet/record.js.map +1 -1
  179. package/dist/src/sheet/root.d.ts +23 -1
  180. package/dist/src/sheet/root.d.ts.map +1 -1
  181. package/dist/src/sheet/root.js +40 -5
  182. package/dist/src/sheet/root.js.map +1 -1
  183. package/dist/src/sheet/sheet.d.ts +9332 -5621
  184. package/dist/src/sheet/sheet.d.ts.map +1 -1
  185. package/dist/src/sheet/sheet.js +41 -2
  186. package/dist/src/sheet/sheet.js.map +1 -1
  187. package/dist/src/sheet/templates.d.ts +4 -0
  188. package/dist/src/sheet/templates.d.ts.map +1 -1
  189. package/dist/src/sheet/types.d.ts +13020 -5839
  190. package/dist/src/sheet/types.d.ts.map +1 -1
  191. package/dist/src/sheet/types.js +114 -2
  192. package/dist/src/sheet/types.js.map +1 -1
  193. package/dist/src/studio/builder.d.ts +6 -6
  194. package/dist/src/studio/library.d.ts +9 -9
  195. package/dist/src/studio/pages.d.ts +6 -6
  196. package/dist/src/studio/publish.d.ts +3 -3
  197. package/dist/src/utils/derive.d.ts +1 -1
  198. package/dist/src/utils/derive.js +1 -1
  199. package/dist/src/utils/derive.js.map +1 -1
  200. package/dist/src/utils/key-visibility.d.ts +1 -1
  201. package/dist/src/utils/record-kind.d.ts +625 -0
  202. package/dist/src/utils/record-kind.d.ts.map +1 -0
  203. package/dist/src/utils/record-kind.js +450 -0
  204. package/dist/src/utils/record-kind.js.map +1 -0
  205. package/dist/src/utils/views.d.ts +55 -0
  206. package/dist/src/utils/views.d.ts.map +1 -0
  207. package/dist/src/utils/views.js +72 -0
  208. package/dist/src/utils/views.js.map +1 -0
  209. package/dist/src/utils/vocabulary.d.ts +48 -0
  210. package/dist/src/utils/vocabulary.d.ts.map +1 -0
  211. package/dist/src/utils/vocabulary.js +47 -0
  212. package/dist/src/utils/vocabulary.js.map +1 -0
  213. package/dist/test/allocation/allocation.examples.d.ts +3 -1
  214. package/dist/test/allocation/allocation.examples.d.ts.map +1 -1
  215. package/dist/test/allocation/allocation.examples.js +81 -2
  216. package/dist/test/allocation/allocation.examples.js.map +1 -1
  217. package/dist/test/bind/data/data.examples.d.ts +1 -1
  218. package/dist/test/bind/func/func.examples.d.ts +19 -1
  219. package/dist/test/bind/func/func.examples.d.ts.map +1 -1
  220. package/dist/test/bind/func/func.examples.js +36 -1
  221. package/dist/test/bind/func/func.examples.js.map +1 -1
  222. package/dist/test/calendar/calendar.examples.d.ts +2 -0
  223. package/dist/test/calendar/calendar.examples.d.ts.map +1 -1
  224. package/dist/test/calendar/calendar.examples.js +80 -2
  225. package/dist/test/calendar/calendar.examples.js.map +1 -1
  226. package/dist/test/decision/queue.examples.d.ts +1 -0
  227. package/dist/test/decision/queue.examples.d.ts.map +1 -1
  228. package/dist/test/decision/queue.examples.js +12 -0
  229. package/dist/test/decision/queue.examples.js.map +1 -1
  230. package/dist/test/examples-package.d.ts.map +1 -1
  231. package/dist/test/examples-package.js +5 -4
  232. package/dist/test/examples-package.js.map +1 -1
  233. package/dist/test/flowchart/flowchart.examples.d.ts +1250 -4605
  234. package/dist/test/flowchart/flowchart.examples.d.ts.map +1 -1
  235. package/dist/test/flowchart/flowchart.examples.js +861 -512
  236. package/dist/test/flowchart/flowchart.examples.js.map +1 -1
  237. package/dist/test/ontology/ontology.examples.d.ts +2 -3
  238. package/dist/test/ontology/ontology.examples.d.ts.map +1 -1
  239. package/dist/test/ontology/ontology.examples.js +32 -21
  240. package/dist/test/ontology/ontology.examples.js.map +1 -1
  241. package/dist/test/plan/plan-events.examples.d.ts +1 -0
  242. package/dist/test/plan/plan-events.examples.d.ts.map +1 -1
  243. package/dist/test/plan/plan-events.examples.js +44 -0
  244. package/dist/test/plan/plan-events.examples.js.map +1 -1
  245. package/dist/test/plan/plan-nested.examples.d.ts +15 -15
  246. package/dist/test/plan/plan-nested.examples.d.ts.map +1 -1
  247. package/dist/test/plan/plan-nested.examples.js +5 -5
  248. package/dist/test/plan/plan-nested.examples.js.map +1 -1
  249. package/dist/test/plan/plan-records.examples.d.ts.map +1 -1
  250. package/dist/test/plan/plan-records.examples.js +1 -1
  251. package/dist/test/plan/plan-records.examples.js.map +1 -1
  252. package/dist/test/plan/plan.examples.js.map +1 -1
  253. package/dist/test/query/query.examples.d.ts +2 -0
  254. package/dist/test/query/query.examples.d.ts.map +1 -1
  255. package/dist/test/query/query.examples.js +37 -1
  256. package/dist/test/query/query.examples.js.map +1 -1
  257. package/dist/test/roster/domain.examples.d.ts.map +1 -1
  258. package/dist/test/roster/roster.examples.d.ts +2 -0
  259. package/dist/test/roster/roster.examples.d.ts.map +1 -1
  260. package/dist/test/roster/roster.examples.js +76 -15
  261. package/dist/test/roster/roster.examples.js.map +1 -1
  262. package/dist/test/schematic/schematic-records.examples.d.ts.map +1 -1
  263. package/dist/test/schematic/schematic-records.examples.js +6 -4
  264. package/dist/test/schematic/schematic-records.examples.js.map +1 -1
  265. package/dist/test/schematic/schematic.examples.d.ts +1 -0
  266. package/dist/test/schematic/schematic.examples.d.ts.map +1 -1
  267. package/dist/test/schematic/schematic.examples.js +144 -0
  268. package/dist/test/schematic/schematic.examples.js.map +1 -1
  269. package/dist/test/sheet/sheet-actions.examples.d.ts +1264 -0
  270. package/dist/test/sheet/sheet-actions.examples.d.ts.map +1 -0
  271. package/dist/test/sheet/sheet-actions.examples.js +394 -0
  272. package/dist/test/sheet/sheet-actions.examples.js.map +1 -0
  273. package/dist/test/sheet/sheet-records.examples.d.ts +1 -1
  274. package/dist/test/sheet/sheet.examples.d.ts +1 -0
  275. package/dist/test/sheet/sheet.examples.d.ts.map +1 -1
  276. package/dist/test/sheet/sheet.examples.js +77 -0
  277. package/dist/test/sheet/sheet.examples.js.map +1 -1
  278. package/dist/test/studio/studio.examples.d.ts +1 -0
  279. package/dist/test/studio/studio.examples.d.ts.map +1 -1
  280. package/dist/test/studio/studio.examples.js +18 -8
  281. package/dist/test/studio/studio.examples.js.map +1 -1
  282. package/package.json +14 -13
  283. package/dist/src/diff/index.d.ts +0 -407
  284. package/dist/src/diff/index.d.ts.map +0 -1
  285. package/dist/src/diff/index.js +0 -203
  286. package/dist/src/diff/index.js.map +0 -1
  287. package/dist/src/experiment/index.d.ts +0 -1033
  288. package/dist/src/experiment/index.d.ts.map +0 -1
  289. package/dist/src/experiment/index.js +0 -197
  290. package/dist/src/experiment/index.js.map +0 -1
  291. package/dist/src/experiment/types.d.ts +0 -1093
  292. package/dist/src/experiment/types.d.ts.map +0 -1
  293. package/dist/src/experiment/types.js +0 -423
  294. package/dist/src/experiment/types.js.map +0 -1
  295. package/dist/src/flowchart/over.d.ts +0 -227
  296. package/dist/src/flowchart/over.d.ts.map +0 -1
  297. package/dist/src/flowchart/over.js +0 -213
  298. package/dist/src/flowchart/over.js.map +0 -1
  299. package/dist/src/flowchart/patch.d.ts +0 -64
  300. package/dist/src/flowchart/patch.d.ts.map +0 -1
  301. package/dist/src/flowchart/patch.js +0 -67
  302. package/dist/src/flowchart/patch.js.map +0 -1
  303. package/dist/src/flowchart/values.d.ts +0 -271
  304. package/dist/src/flowchart/values.d.ts.map +0 -1
  305. package/dist/src/flowchart/values.js +0 -220
  306. package/dist/src/flowchart/values.js.map +0 -1
  307. package/dist/src/flowchart/window.d.ts +0 -306
  308. package/dist/src/flowchart/window.d.ts.map +0 -1
  309. package/dist/src/flowchart/window.js +0 -63
  310. package/dist/src/flowchart/window.js.map +0 -1
  311. package/dist/src/runtime/diff.d.ts +0 -36
  312. package/dist/src/runtime/diff.d.ts.map +0 -1
  313. package/dist/src/runtime/diff.js +0 -34
  314. package/dist/src/runtime/diff.js.map +0 -1
  315. package/dist/src/runtime/experiment.d.ts +0 -48
  316. package/dist/src/runtime/experiment.d.ts.map +0 -1
  317. package/dist/src/runtime/experiment.js +0 -10
  318. package/dist/src/runtime/experiment.js.map +0 -1
  319. package/dist/test/diff/diff.examples.d.ts +0 -313
  320. package/dist/test/diff/diff.examples.d.ts.map +0 -1
  321. package/dist/test/diff/diff.examples.js +0 -452
  322. package/dist/test/diff/diff.examples.js.map +0 -1
  323. package/dist/test/experiment/experiment.examples.d.ts +0 -1512
  324. package/dist/test/experiment/experiment.examples.d.ts.map +0 -1
  325. package/dist/test/experiment/experiment.examples.js +0 -609
  326. package/dist/test/experiment/experiment.examples.js.map +0 -1
@@ -0,0 +1,1035 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ /**
6
+ * The Flowchart's details (#1507, `Flowchart Records Spec.md` §3, R18–R21):
7
+ * `Flowchart.details` and its four reads — `Flowchart.figure`, `.histogram`,
8
+ * `.list` and `.tags` — over the app's rows keyed as a kind's are, read only.
9
+ *
10
+ * - **A read is a frozen description behind a phantom brand**, as the Plan's
11
+ * named reads are (`plan/reads.ts`): the row it reads, its kind, where it
12
+ * shows and, for tags, whether the legend lists them. The row is inferred
13
+ * where the read is placed — in a `Flowchart.details` array — so a field
14
+ * name is checked against the row it reads, and a misfit is a type error
15
+ * naming the fields that fit (R20). A read's return type keeps its show and
16
+ * its legend from inference (`NoInfer`), so each is the one given — `"all"`
17
+ * and `false` unless given — wherever the read is placed, and `details`'
18
+ * keeps its row and its reads from the place it is put, so a kind's `details`
19
+ * never infers them in its stead.
20
+ * - **One array, in order** (R21): each kind's `details` is one array of
21
+ * reads, any mix of the four, drawn in its order and shown in full, in the
22
+ * same order, in the inspector, each where its `show` says. A transition's
23
+ * label draws lines, so its tags and histograms take `show: "inspector"`: a
24
+ * type error otherwise, with its words.
25
+ * - **Read as the kind is** (R19): beside a kind read whole the details bind
26
+ * `Data.bind(record)`, read whole; beside one read through its source,
27
+ * `Data.bindPaged(record)`, read by the drawn rows' keys, every key in one
28
+ * request. They are read only (R18): `Record.bind` is refused.
29
+ * - **The row type never reaches the payload**: `values` turns a row's bytes
30
+ * into one value a read, one type whatever the row holds.
31
+ * - **The rail's picks weigh them** (#1522, R43–R46): details that name a
32
+ * source of the slice, `source`, are bound through their record's entries
33
+ * index, and a pick of its Dict field's keys weighs each read the entries
34
+ * hold, added up by kind in East — a figure adds, a histogram adds bin by
35
+ * bin, a list adds by name and tags join — while `weigh: false` keeps a
36
+ * read's own. `fold` turns a row's bytes and its picked entries' into one
37
+ * value a read.
38
+ *
39
+ * What only the built value shows is refused as the Flowchart is built, in
40
+ * the tag's words (§3, "What the editor and the tag say").
41
+ *
42
+ * @packageDocumentation
43
+ */
44
+ import { ArrayType, BlobType, DictType, East, Expr, FloatType, IntegerType, OptionType, SetType, StringType, StructType, isTypeEqual, none, some, variant, } from "@elaraai/east";
45
+ import { ColorSchemeType, StyleVariantType, resolveRowSource, resolveTag, } from "@elaraai/east-ui/internal";
46
+ import { fieldOf } from "../schedule/fields.js";
47
+ import { checkEntries, optionOf, typeText } from "../schedule/window.js";
48
+ import { FlowchartDetailRowType, FlowchartDetailsType, FlowchartTagColorType } from "./types.js";
49
+ /** The phantom brand a read carries: what it reads, its kind, where it shows and whether the legend lists it. */
50
+ export const FlowchartReadBrand = Symbol("FlowchartRead");
51
+ const READS = new WeakMap();
52
+ /** A read made: a frozen description, its spec kept beside it. */
53
+ function described(spec) {
54
+ const read = Object.freeze({ kind: spec.kind, field: spec.field });
55
+ READS.set(read, spec);
56
+ return read;
57
+ }
58
+ /** A read's options, as given: an object, or none. */
59
+ function optionsOf(where, options, required = false) {
60
+ if (options === undefined && !required)
61
+ return {};
62
+ if (typeof options !== "object" || options === null || Array.isArray(options)) {
63
+ throw new Error(`${where}: its options are an object — { label, unit, show, … }`);
64
+ }
65
+ return options;
66
+ }
67
+ /** A read's field, named. */
68
+ function fieldName(where, field) {
69
+ if (typeof field !== "string" || field === "")
70
+ throw new Error(`${where}: name the field it reads — a field of the details' row`);
71
+ return field;
72
+ }
73
+ /** A read's words: a label, a unit, what is counted. */
74
+ function wordOf(where, prop, given) {
75
+ if (given === undefined)
76
+ return undefined;
77
+ if (typeof given !== "string")
78
+ throw new Error(`${where}: \`${prop}\` is a string`);
79
+ return given;
80
+ }
81
+ /** Whether the rail's picks weigh a read (#1522): `undefined` unless given. */
82
+ function weighOf(where, weigh) {
83
+ if (weigh === undefined || typeof weigh === "boolean")
84
+ return weigh;
85
+ throw new Error(`${where}: \`weigh\` is false to keep the row's own beside the rail's picks — a share or an average, which adding would make wrong`);
86
+ }
87
+ /** Where a read shows: "all" unless given. */
88
+ function showOf(where, show) {
89
+ if (show === undefined)
90
+ return "all";
91
+ if (show === "all" || show === "canvas" || show === "inspector")
92
+ return show;
93
+ throw new Error(`${where}: \`show\` is "all", "canvas" or "inspector" — where the read shows`);
94
+ }
95
+ /**
96
+ * A histogram's bins, checked: a literal's edges ascend, refused when built;
97
+ * bins the app binds are checked as they land, by the renderer (R27, R38).
98
+ */
99
+ function binsOf(where, bins) {
100
+ if (bins === undefined)
101
+ return undefined;
102
+ if (bins instanceof Expr) {
103
+ const type = Expr.type(bins);
104
+ if (!isTypeEqual(type, ArrayType(FloatType)))
105
+ throw new Error(`${where}: \`bins\` are the app's edges, an Array of Floats — and these are ${typeText(type)}`);
106
+ return bins;
107
+ }
108
+ if (!Array.isArray(bins))
109
+ throw new Error(`${where}: \`bins\` are the app's edges, ascending — [0, …], or an Array the app binds`);
110
+ if (bins.every((edge) => typeof edge === "number")) {
111
+ const edges = bins;
112
+ if (edges.length === 0)
113
+ throw new Error(`${where}: \`bins\` are the app's edges, ascending — give the first, [0, …]`);
114
+ for (const edge of edges)
115
+ if (!Number.isFinite(edge))
116
+ throw new Error(`${where}: the bins' edges are numbers — and one is ${edge}`);
117
+ for (let i = 1; i < edges.length; i++) {
118
+ if (!(edges[i] > edges[i - 1]))
119
+ throw new Error(`${where}: the bins' edges ascend — ${edges[i - 1]}, then ${edges[i]}`);
120
+ }
121
+ }
122
+ return East.value(bins, ArrayType(FloatType));
123
+ }
124
+ /** The axis's ticks, checked: each a number, or `{ at, label }`. */
125
+ function ticksOf(where, ticks) {
126
+ if (ticks === undefined)
127
+ return undefined;
128
+ const tick = (t) => (typeof t === "number" && Number.isFinite(t))
129
+ || (typeof t === "object" && t !== null && typeof t.at === "number" && Number.isFinite(t.at)
130
+ && typeof t.label === "string");
131
+ if (!Array.isArray(ticks) || !ticks.every(tick))
132
+ throw new Error(`${where}: \`ticks\` lists the edges the axis names — each a number, or { at, label }`);
133
+ return Object.freeze(ticks.map((t) => typeof t === "number" ? t : Object.freeze({ at: t.at, label: t.label })));
134
+ }
135
+ /** The cases of a variant of nulls, as a refusal names them. */
136
+ function casesOf(type) {
137
+ return type.type === "Variant" ? Object.keys(type.cases) : [];
138
+ }
139
+ /**
140
+ * The tags' looks, checked: a literal — each tag's `{ colorPalette, variant }`,
141
+ * first defined, first shown — or an Array of `Flowchart.Types.TagColor` the
142
+ * app binds.
143
+ */
144
+ function colorsOf(where, colors) {
145
+ const Colors = ArrayType(FlowchartTagColorType);
146
+ if (colors === undefined)
147
+ return East.value([], Colors);
148
+ if (colors instanceof Expr) {
149
+ const type = Expr.type(colors);
150
+ if (!isTypeEqual(type, Colors)) {
151
+ throw new Error(`${where}: \`colors\` gives each tag its look — { "Hazmat": { colorPalette, variant } }, or an Array of Flowchart.Types.TagColor ` +
152
+ `the app binds — and this is ${typeText(type)}`);
153
+ }
154
+ return colors;
155
+ }
156
+ if (Array.isArray(colors))
157
+ return East.value(colors, Colors);
158
+ if (typeof colors !== "object" || colors === null) {
159
+ throw new Error(`${where}: \`colors\` gives each tag its look — { "Hazmat": { colorPalette, variant } }, or an Array of Flowchart.Types.TagColor the app binds`);
160
+ }
161
+ const palettes = casesOf(ColorSchemeType);
162
+ const variants = casesOf(StyleVariantType);
163
+ const looks = Object.entries(colors).map(([tag, look]) => {
164
+ const given = look;
165
+ if (typeof given !== "object" || given === null || given.colorPalette === undefined) {
166
+ throw new Error(`${where}: "${tag}"'s look is { colorPalette, variant? }, as a Tag takes it`);
167
+ }
168
+ if (typeof given.colorPalette === "string" && !palettes.includes(given.colorPalette)) {
169
+ throw new Error(`${where}: "${tag}"'s colorPalette is one of east-ui's colour schemes — "danger", "red", "brand", … — and this is "${given.colorPalette}"`);
170
+ }
171
+ if (typeof given.variant === "string" && !variants.includes(given.variant)) {
172
+ throw new Error(`${where}: "${tag}"'s variant is one of a Tag's variants — "subtle", "solid", "outline", … — and this is "${given.variant}"`);
173
+ }
174
+ return {
175
+ tag,
176
+ colorPalette: resolveTag(given.colorPalette, ColorSchemeType),
177
+ variant: resolveTag((given.variant ?? "subtle"), StyleVariantType),
178
+ };
179
+ });
180
+ return East.value(looks, Colors);
181
+ }
182
+ // ============================================================================
183
+ // The four reads (§3)
184
+ // ============================================================================
185
+ /**
186
+ * A figure (R25): a number, or an Option of one — a line on the card or the
187
+ * label, its label and its value with its unit; the inspector's stats.
188
+ *
189
+ * @typeParam R - The details' row type, inferred where the read is placed
190
+ * @typeParam F - The field
191
+ * @typeParam W - Where it shows: `"all"` unless given
192
+ * @param field - The field it reads
193
+ * @param options - Its label — the field's name unless given — its unit, where it shows, and `weigh: false` to keep the
194
+ * row's own beside the rail's picks — a share or an average — which add the entries' otherwise (#1522)
195
+ * @returns The read, for `Flowchart.details`
196
+ * @throws {Error} When the field is named by no string, or an option is of another kind
197
+ */
198
+ export function flowchartFigure(field, options) {
199
+ const where = "Flowchart.figure";
200
+ const given = optionsOf(where, options);
201
+ return described({
202
+ kind: "figure", field: fieldName(where, field), show: showOf(where, given["show"]),
203
+ label: wordOf(where, "label", given["label"]), unit: wordOf(where, "unit", given["unit"]), weigh: weighOf(where, given["weigh"]),
204
+ });
205
+ }
206
+ /**
207
+ * A histogram (R26–R28): a Dict from a duration to its count, both numbers —
208
+ * or, beside the app's `bins`, an Array of counts, one a bin. The card's bars
209
+ * on their axis; the inspector's chart.
210
+ *
211
+ * @typeParam R - The details' row type, inferred where the read is placed
212
+ * @typeParam F - The field
213
+ * @typeParam W - Where it shows: `"all"` unless given
214
+ * @param field - The field it reads
215
+ * @param options - Its unit, its label, what it counts, the app's bins — required beside an Array of counts — how the bars
216
+ * space them, the edges the axis names, where it shows, and `weigh: false` to keep the row's own beside the rail's picks
217
+ * @returns The read, for `Flowchart.details`
218
+ * @throws {Error} When the unit, the scale or a tick is none of its words, and when the bins' edges do not ascend —
219
+ * "The bins' edges ascend — 12, then 4"
220
+ */
221
+ export function flowchartHistogram(field,
222
+ // an Array of counts is a bin each: beside one, the bins are required
223
+ options) {
224
+ const where = "Flowchart.histogram";
225
+ const name = fieldName(where, field);
226
+ const given = optionsOf(where, options, true);
227
+ const unit = given["unit"];
228
+ if (unit !== "minutes" && unit !== "hours" && unit !== "days") {
229
+ throw new Error(`${where}: \`unit\` is the unit "${name}"'s durations are in — "minutes", "hours" or "days"`);
230
+ }
231
+ const scale = given["scale"] ?? "band";
232
+ if (scale !== "band" && scale !== "linear")
233
+ throw new Error(`${where}: \`scale\` is "band", a bar a bin, or "linear", each bar as wide as its span`);
234
+ return described({
235
+ kind: "histogram", field: name, show: showOf(where, given["show"]), label: wordOf(where, "label", given["label"]),
236
+ unit, counts: wordOf(where, "counts", given["counts"]), bins: binsOf(where, given["bins"]), scale, ticks: ticksOf(where, given["ticks"]),
237
+ weigh: weighOf(where, given["weigh"]),
238
+ });
239
+ }
240
+ /**
241
+ * A list (R29): a Dict from an item's name to a number, or to a struct whose
242
+ * `draw` says which number is the value, and a note. Lines on a card, under
243
+ * the list's title, or under a transition's evidence; the inspector's list.
244
+ *
245
+ * @typeParam R - The details' row type, inferred where the read is placed
246
+ * @typeParam F - The field
247
+ * @typeParam W - Where it shows: `"all"` unless given
248
+ * @param field - The field it reads
249
+ * @param options - Its label, its unit, the lines it draws on the canvas — 2 on a card and 1 on a label unless given, at
250
+ * most 3: the inspector lists every item — where it shows, and `weigh: false` to keep the row's own beside the rail's
251
+ * picks, which add the entries' by name otherwise (#1522)
252
+ * @param draw - An item's value and its note, from the item, its name and the row: required for a Dict of structs.
253
+ * Weighed, it draws the picked entries' items added up, each struct's numbers added and its lists joined
254
+ * @returns The read, for `Flowchart.details`
255
+ * @throws {Error} When `top` is not 1, 2 or 3, or `draw` is no function
256
+ */
257
+ export function flowchartList(field, options, // top: the card's or label's lines
258
+ draw) {
259
+ const where = "Flowchart.list";
260
+ const given = optionsOf(where, options);
261
+ const top = given["top"];
262
+ if (top !== undefined && top !== 1 && top !== 2 && top !== 3)
263
+ throw new Error(`${where}: \`top\` is 1, 2 or 3 — the inspector lists every item`);
264
+ if (draw !== undefined && typeof draw !== "function")
265
+ throw new Error(`${where}: \`draw\` gives an item's value and note — (item, name, row) => ({ value, note })`);
266
+ return described({
267
+ kind: "list", field: fieldName(where, field), show: showOf(where, given["show"]), label: wordOf(where, "label", given["label"]),
268
+ unit: wordOf(where, "unit", given["unit"]), top, draw: draw, weigh: weighOf(where, given["weigh"]),
269
+ });
270
+ }
271
+ /**
272
+ * A tags read (R30, R31): an Array or a Set of strings, the row's tags. On a
273
+ * card, a list beside its label, wrapping to three lines; the inspector shows
274
+ * every tag.
275
+ *
276
+ * @typeParam R - The details' row type, inferred where the read is placed
277
+ * @typeParam F - The field
278
+ * @typeParam W - Where it shows: `"all"` unless given
279
+ * @typeParam L - Whether the canvas's legend lists the defined tags
280
+ * @param field - The field it reads
281
+ * @param options - Its label; each tag's look as a Tag takes it — `{ colorPalette, variant }`, first defined, first shown,
282
+ * a literal or an Array the app binds (`Flowchart.Types.TagColor`) — a tag with none neutral and subtle; whether the
283
+ * legend lists the defined tags, for tags on the canvas only; where it shows; and `weigh: false` to keep the row's own
284
+ * beside the rail's picks, whose entries' tags join otherwise (#1522): a Set's as a Set does, in its order, an Array's
285
+ * each once in the entries' key order
286
+ * @returns The read, for `Flowchart.details`
287
+ * @throws {Error} When a look's palette or variant is none of east-ui's, or `legend` is given beside `show: "inspector"` —
288
+ * "Only tags on the canvas go in the legend — show them on the canvas, or drop legend"
289
+ */
290
+ export function flowchartTags(field, options) {
291
+ const where = "Flowchart.tags";
292
+ const given = optionsOf(where, options);
293
+ const show = showOf(where, given["show"]);
294
+ const legend = given["legend"] ?? false;
295
+ if (typeof legend !== "boolean")
296
+ throw new Error(`${where}: \`legend\` is true to list the defined tags in the canvas's legend`);
297
+ if (legend && show === "inspector")
298
+ throw new Error(`${where}: only tags on the canvas go in the legend — show them on the canvas, or drop legend`);
299
+ return described({
300
+ kind: "tags", field: fieldName(where, field), show, label: wordOf(where, "label", given["label"]),
301
+ colors: colorsOf(where, given["colors"]), legend, weigh: weighOf(where, given["weigh"]),
302
+ });
303
+ }
304
+ /** The key a kind's details carry their mark under. */
305
+ export const FLOWCHART_DETAILS = Symbol("Flowchart.details");
306
+ /** The phantom brand a kind's details carry: their row, their reads, and whether a transition's label draws them. */
307
+ export const FlowchartDetailsBrand = Symbol("FlowchartDetails");
308
+ /**
309
+ * Whether something is a kind's details: `Flowchart.details`'.
310
+ *
311
+ * @param value - What a kind's `details` holds
312
+ * @returns Whether it is
313
+ */
314
+ export function isFlowchartDetails(value) {
315
+ return typeof value === "object" && value !== null && FLOWCHART_DETAILS in value
316
+ && value[FLOWCHART_DETAILS] === "details";
317
+ }
318
+ /**
319
+ * The rows an index-selected binding's type pages through (#1522): each
320
+ * entry's joined row and its key, as `Data.bindPaged(record, { indexes })`'s
321
+ * `page` serves them.
322
+ *
323
+ * @param type - The binding's type
324
+ * @returns The row's struct and the key's type; `undefined` for a binding that pages no such entries
325
+ * @internal
326
+ */
327
+ export function flowchartIndexedRows(type) {
328
+ const fields = type.type === "Struct" ? type.fields : {};
329
+ const page = fields["page"];
330
+ const output = page?.type === "Function" ? page.output : undefined;
331
+ const entry = output?.type === "Variant" ? output.cases["some"] : undefined;
332
+ const held = entry?.type === "Array" ? entry.value : undefined;
333
+ const joined = held?.type === "Struct" ? held.fields["row"] : undefined;
334
+ const row = joined === undefined ? undefined : optionOf(joined);
335
+ const key = held?.type === "Struct" ? held.fields["key"] : undefined;
336
+ return row?.type !== "Struct" || key === undefined ? undefined : { row, key };
337
+ }
338
+ /**
339
+ * The details' record bound through its entries index (#1522):
340
+ * `Data.bindPaged(record, { indexes: [its entries index] })`, the rows read by
341
+ * key through its lean rows. Its row type from its entries' joined row, its
342
+ * entries index from the binding's metadata.
343
+ *
344
+ * @throws {Error} When the binding lists no entries index, or several, or holds its rows by another key than a String
345
+ */
346
+ function indexedOf(record, type, where) {
347
+ const fields = type.fields;
348
+ const paged = flowchartIndexedRows(type);
349
+ if (paged === undefined)
350
+ throw new Error(`${where}: Data.bindPaged(record, { indexes }) reads no rows of structs`);
351
+ const { row, key } = paged;
352
+ if (!isTypeEqual(key, StringType)) {
353
+ throw new Error(`${where}: the details' record holds its rows by String key, keyed as the kind's are — and this one by ${typeText(key)}`);
354
+ }
355
+ const indexes = fields["indexes"];
356
+ const listed = indexes?.type === "Struct" ? Object.entries(indexes.fields) : [];
357
+ const entries = listed.flatMap(([index, meta]) => {
358
+ const parts = meta.type === "Struct" ? meta.fields : {};
359
+ const kind = parts["kind"];
360
+ const led = parts["fields"];
361
+ if (kind?.type !== "Variant" || !Object.hasOwn(kind.cases, "entries") || led?.type !== "Struct")
362
+ return [];
363
+ const field = Object.keys(led.fields)[0];
364
+ const dict = field === undefined ? undefined : row.fields[field];
365
+ return field === undefined || dict?.type !== "Dict" ? [] : [{ index, field, valueType: dict.value }];
366
+ });
367
+ if (entries.length === 0) {
368
+ throw new Error(`${where}: the details are read by key through the record's own entries, Data.bindPaged(record) — or, weighed by the rail's ` +
369
+ "picks, through the record's entries index — and this one reads it through other indexes");
370
+ }
371
+ if (entries.length > 1) {
372
+ throw new Error(`${where}: the details are weighed by one entries index, and this binding lists ${entries.map((e) => `'${e.index}'`).join(", ")} — list one`);
373
+ }
374
+ return { form: "paged", handle: record, rowType: row, entries: entries[0] };
375
+ }
376
+ /**
377
+ * Tells the details' binding by its handle's East struct (`bind/sources.ts`'
378
+ * rule): a `page` field is `Data.bindPaged`'s, a `mutate` field
379
+ * `Record.bind`'s, which is refused — the details are read only — and any
380
+ * other `Data.bind`'s.
381
+ */
382
+ function bindingOf(record, where) {
383
+ const forms = "details are read only: bind them with Data.bind(record), or Data.bindPaged(record) beside a kind read through its source";
384
+ if (!(record instanceof Expr))
385
+ throw new Error(`${where}: ${forms}`);
386
+ const type = Expr.type(record);
387
+ const fields = type.type === "Struct" ? type.fields : undefined;
388
+ if (fields === undefined)
389
+ throw new Error(`${where}: ${forms} — and this is ${typeText(type)}`);
390
+ let form;
391
+ let recordType;
392
+ if (fields["page"] !== undefined) {
393
+ form = "paged";
394
+ // Through its entries index, the rail's picks weigh them (#1522).
395
+ if (fields["indexes"] !== undefined)
396
+ return indexedOf(record, type, where);
397
+ const resolved = resolveRowSource(record, where);
398
+ if (resolved.kind !== "paged") {
399
+ throw new Error(`${where}: the details are read by key through the record's own entries, Data.bindPaged(record) — and this one pages an index's window`);
400
+ }
401
+ recordType = resolved.collectionType;
402
+ }
403
+ else {
404
+ if (fields["mutate"] !== undefined)
405
+ throw new Error(`${where}: ${forms}`);
406
+ const read = fields["read"];
407
+ if (read === undefined || read.type !== "Function" || read.inputs.length !== 0 || fields["binding"] === undefined) {
408
+ throw new Error(`${where}: ${forms}`);
409
+ }
410
+ form = "data";
411
+ recordType = read.output;
412
+ }
413
+ if (recordType.type !== "Dict" || !isTypeEqual(recordType.key, StringType) || recordType.value.type !== "Struct") {
414
+ throw new Error(`${where}: the details' record holds its rows by String key, keyed as the kind's are, each a struct — and this one holds ${typeText(recordType)}`);
415
+ }
416
+ const rowType = recordType.value;
417
+ if (form === "paged")
418
+ checkEntries(record, StringType, rowType, `${where}: Data.bindPaged(record)`);
419
+ return { form, handle: record, rowType, entries: undefined };
420
+ }
421
+ /** Whether a type is a number: an Integer or a Float. */
422
+ function isNumber(type) {
423
+ return type !== undefined && (type.type === "Integer" || type.type === "Float");
424
+ }
425
+ /** The fields of a row that fit a read, by what each read takes (R20). */
426
+ const FITS = {
427
+ figure: { fits: (t) => isNumber(t) || isNumber(optionOf(t)), what: "a figure reads a number, or an Option of one" },
428
+ histogram: {
429
+ fits: (t) => (t.type === "Dict" && isNumber(t.key) && isNumber(t.value)) || (t.type === "Array" && isNumber(t.value)),
430
+ what: "a histogram reads a Dict from a duration to its count, both numbers, or an Array of counts beside its bins",
431
+ },
432
+ list: {
433
+ fits: (t) => t.type === "Dict" && isTypeEqual(t.key, StringType) && (isNumber(t.value) || t.value.type === "Struct"),
434
+ what: "a list reads a Dict from an item's name to a number, or to a struct its draw reads",
435
+ },
436
+ tags: {
437
+ fits: (t) => (t.type === "Array" || t.type === "Set") && isTypeEqual((t.type === "Array" ? t.value : t.key), StringType),
438
+ what: "tags read an Array or a Set of strings",
439
+ },
440
+ };
441
+ /** A number as a Float: an Integer converted, a Float as it is. */
442
+ function asFloat(value, type) {
443
+ return (type.type === "Integer" ? value.toFloat() : value);
444
+ }
445
+ /** What a list's draw gives: an item's value, and its note. */
446
+ const DrawnType = StructType({ value: FloatType, note: OptionType(StringType) });
447
+ /** One bin of a histogram's value. */
448
+ const BinType = StructType({ from: FloatType, count: FloatType });
449
+ /** One item of a list's value. */
450
+ const ItemType = StructType({ name: StringType, value: FloatType, note: OptionType(StringType) });
451
+ /** A list's draw, reified once over the item, its name and the row (`shared/reify.ts`' rule), its value a Float. */
452
+ function drawOf(where, field, itemType, rowType, draw) {
453
+ const words = `${where}: "${field}"'s draw gives { value, note? } — value a number, note an Option of a String`;
454
+ return East.function([itemType, StringType, rowType], DrawnType, (_$, item, name, row) => {
455
+ const given = draw(item, name, row);
456
+ if (typeof given !== "object" || given === null || given.value === undefined)
457
+ throw new Error(words);
458
+ let value;
459
+ if (typeof given.value === "number")
460
+ value = East.value(given.value, FloatType);
461
+ else if (typeof given.value === "bigint")
462
+ value = East.value(given.value, IntegerType).toFloat();
463
+ else if (given.value instanceof Expr && isNumber(Expr.type(given.value))) {
464
+ value = asFloat(given.value, Expr.type(given.value));
465
+ }
466
+ else {
467
+ throw new Error(`${words} — and its value is ${given.value instanceof Expr ? typeText(Expr.type(given.value)) : String(given.value)}`);
468
+ }
469
+ if (given.note instanceof Expr && !isTypeEqual(Expr.type(given.note), OptionType(StringType))) {
470
+ throw new Error(`${words} — and its note is ${typeText(Expr.type(given.note))}`);
471
+ }
472
+ const note = given.note === undefined ? East.value(none, OptionType(StringType)) : East.value(given.note, OptionType(StringType));
473
+ return East.value({ value, note }, DrawnType);
474
+ });
475
+ }
476
+ /**
477
+ * A read resolved against the details' row (R20): its field checked, and the
478
+ * value a row holds for it.
479
+ *
480
+ * @throws {Error} Naming the read and the field: a field the row lacks, or one of another type, naming those that fit;
481
+ * an Array of counts without bins; a Dict of structs without a draw
482
+ */
483
+ function resolve(spec, rowType) {
484
+ const where = `Flowchart.${spec.kind}`;
485
+ const fields = rowType.fields;
486
+ const type = fields[spec.field];
487
+ if (type === undefined)
488
+ throw new Error(`${where}: "${spec.field}" is no field of the details' row (${Object.keys(fields).join(", ")})`);
489
+ const { fits, what } = FITS[spec.kind];
490
+ if (!fits(type)) {
491
+ const fitting = Object.keys(fields).filter((name) => fits(fields[name])).map((name) => `"${name}"`);
492
+ throw new Error(`${where}: "${spec.field}" holds ${typeText(type)} — ${what}: ${fitting.length > 0 ? fitting.join(", ") : "the details' row has none"}`);
493
+ }
494
+ switch (spec.kind) {
495
+ case "figure": {
496
+ const inner = optionOf(type);
497
+ return {
498
+ spec,
499
+ value: (_$, row) => {
500
+ const held = fieldOf(row, spec.field);
501
+ if (inner === undefined)
502
+ return variant("figure", some(asFloat(held, type)));
503
+ if (inner.type === "Float")
504
+ return variant("figure", held);
505
+ return variant("figure", held.match({
506
+ none: (_$2) => East.value(none, OptionType(FloatType)),
507
+ some: (_$2, n) => East.value(some(n.toFloat()), OptionType(FloatType)),
508
+ }));
509
+ },
510
+ };
511
+ }
512
+ case "histogram": {
513
+ if (type.type === "Array") {
514
+ const bins = spec.bins;
515
+ if (bins === undefined)
516
+ throw new Error(`${where}: "${spec.field}" is an Array of counts — give the bins it counts in, bins: [0, …]`);
517
+ const countType = type.value;
518
+ return {
519
+ spec,
520
+ // An Array's counts are a bin each, in the bins' order: each at its bin's edge — or, another number of
521
+ // them, each at NaN, which no bin holds, so the row is listed in Issues and its bars drawn absent (R27).
522
+ value: ($, row) => {
523
+ const counts = $.const(fieldOf(row, spec.field));
524
+ const edges = $.const(bins);
525
+ const bars = counts.size().equal(edges.size()).ifElse((_$2) => counts.map((_$3, count, i) => East.value({ from: edges.get(i), count: asFloat(count, countType) }, BinType)), (_$2) => counts.size().equal(0n).ifElse((_$3) => East.value([{ from: NaN, count: 0 }], ArrayType(BinType)), (_$3) => counts.map((_$4, count) => East.value({ from: NaN, count: asFloat(count, countType) }, BinType))));
526
+ return variant("histogram", bars);
527
+ },
528
+ };
529
+ }
530
+ const durations = type;
531
+ const keyType = durations.key;
532
+ const countType = durations.value;
533
+ return {
534
+ spec,
535
+ // A Dict's durations as they are, keyed: the renderer counts each into the bin that holds it (R27).
536
+ value: (_$, row) => variant("histogram", fieldOf(row, spec.field).toArray((_$2, count, from) => East.value({ from: asFloat(from, keyType), count: asFloat(count, countType) }, BinType))),
537
+ };
538
+ }
539
+ case "list": {
540
+ const itemType = type.value;
541
+ if (itemType.type === "Struct" && spec.draw === undefined) {
542
+ const number = Object.entries(itemType.fields).find(([, t]) => isNumber(t))?.[0];
543
+ throw new Error(`${where}: "${spec.field}" holds structs — give a draw, (item) => ({ value: item.${number ?? "…"} })`);
544
+ }
545
+ if (spec.draw !== undefined) {
546
+ const draw = drawOf(where, spec.field, itemType, rowType, spec.draw);
547
+ return {
548
+ spec,
549
+ drawn: draw,
550
+ value: ($, row) => {
551
+ const drawn = $.const(draw);
552
+ return variant("list", fieldOf(row, spec.field).toArray(($2, item, name) => {
553
+ const given = $2.const(drawn(item, name, row));
554
+ return East.value({ name, value: given.value, note: given.note }, ItemType);
555
+ }));
556
+ },
557
+ };
558
+ }
559
+ return {
560
+ spec,
561
+ value: (_$, row) => variant("list", fieldOf(row, spec.field).toArray((_$2, item, name) => East.value({ name, value: asFloat(item, itemType), note: none }, ItemType))),
562
+ };
563
+ }
564
+ case "tags":
565
+ return {
566
+ spec,
567
+ value: (_$, row) => {
568
+ const held = fieldOf(row, spec.field);
569
+ return variant("tags", type.type === "Set" ? held.toArray() : held);
570
+ },
571
+ };
572
+ }
573
+ }
574
+ /** Arrays of strings joined, each once, in their order: what tags join by, and a struct's lists. */
575
+ const joinOnce = East.function([ArrayType(ArrayType(StringType))], ArrayType(StringType), ($, lists) => {
576
+ const joined = $.let([], ArrayType(StringType));
577
+ const seen = $.let(new Set(), SetType(StringType));
578
+ $.for(lists, ($2, list) => {
579
+ $2.for(list, ($3, item) => {
580
+ $3.if(seen.tryInsert(item), ($4) => { $4(joined.pushLast(item)); });
581
+ });
582
+ });
583
+ return joined;
584
+ });
585
+ /** How one field of a list's struct adds across entries: numbers and Options of them add, Sets and Arrays of Strings join; undefined for any other. */
586
+ function addOf(type) {
587
+ if (type.type === "Integer") {
588
+ return East.function([IntegerType, IntegerType], IntegerType, (_$, a, b) => a.add(b));
589
+ }
590
+ if (type.type === "Float")
591
+ return East.function([FloatType, FloatType], FloatType, (_$, a, b) => a.add(b));
592
+ const inner = optionOf(type);
593
+ if (inner !== undefined && isNumber(inner)) {
594
+ // An Option adds its values: none where both are none.
595
+ const add = addOf(inner);
596
+ return East.function([type, type], type, ($, a, b) => {
597
+ const plus = $.const(add);
598
+ return a.match({
599
+ none: (_$2) => b,
600
+ some: (_$2, x) => b.match({
601
+ none: (_$3) => a,
602
+ some: (_$3, y) => East.value(some(plus(x, y)), type),
603
+ }),
604
+ });
605
+ });
606
+ }
607
+ if (type.type === "Set" && isTypeEqual(type.key, StringType)) {
608
+ return East.function([SetType(StringType), SetType(StringType)], SetType(StringType), (_$, a, b) => a.union(b));
609
+ }
610
+ if (type.type === "Array" && isTypeEqual(type.value, StringType)) {
611
+ return East.function([ArrayType(StringType), ArrayType(StringType)], ArrayType(StringType), ($, a, b) => {
612
+ const join = $.const(joinOnce);
613
+ return join(East.value([a, b], ArrayType(ArrayType(StringType))));
614
+ });
615
+ }
616
+ return undefined;
617
+ }
618
+ /**
619
+ * Two of a list's structs added, field by field, by name: numbers and Options
620
+ * of them added, Sets and Arrays of Strings joined.
621
+ *
622
+ * @throws {Error} Naming the field that neither adds nor joins, and `weigh: false`
623
+ */
624
+ function addedStructOf(where, field, itemType) {
625
+ const fields = Object.entries(itemType.fields).map(([name, type]) => {
626
+ const add = addOf(type);
627
+ if (add === undefined) {
628
+ throw new Error(`${where}: "${field}"'s items hold "${name}", ${typeText(type)}, which neither adds nor joins — numbers and Options of them ` +
629
+ "add, Sets and Arrays of Strings join: give the list weigh: false");
630
+ }
631
+ return { name, add };
632
+ });
633
+ return East.function([itemType, itemType], itemType, ($, a, b) => {
634
+ const added = fields.map(({ name, add }) => {
635
+ const plus = $.const(add);
636
+ return [name, plus(fieldOf(a, name), fieldOf(b, name))];
637
+ });
638
+ return East.value(Object.fromEntries(added), itemType);
639
+ });
640
+ }
641
+ /**
642
+ * A read weighed (R45): what it draws from a row's picked entries, the
643
+ * entries' own field added up by kind — a figure adds, an Option adding its
644
+ * values and `none` where every entry's is; a histogram adds bin by bin, an
645
+ * Array of counts position by position and a Dict of durations duration by
646
+ * duration; a list adds by name, a list of structs each struct's numbers by
647
+ * name before its draw; tags join, each once, in the entries' order.
648
+ *
649
+ * @param resolved - The read, resolved against the row
650
+ * @param held - The entries' field's type
651
+ * @param rowType - The details' row type
652
+ * @returns Its weighed value
653
+ * @throws {Error} When the entries hold the field in a type the read does not take, an Array of counts without bins,
654
+ * a list of structs in another type than the row's, or one of whose fields neither adds nor joins
655
+ */
656
+ function weighed(resolved, held, rowType) {
657
+ const spec = resolved.spec;
658
+ const where = `Flowchart.${spec.kind}`;
659
+ const { fits, what } = FITS[spec.kind];
660
+ if (!fits(held))
661
+ throw new Error(`${where}: the entries hold "${spec.field}" as ${typeText(held)} — ${what}: give it weigh: false to draw the row's own`);
662
+ const at = (entry) => fieldOf(entry, spec.field);
663
+ switch (spec.kind) {
664
+ case "figure": {
665
+ const inner = optionOf(held);
666
+ if (inner === undefined) {
667
+ return (_$, entries) => variant("figure", some(entries.reduce((_$2, total, entry) => total.add(asFloat(at(entry), held)), East.value(0, FloatType))));
668
+ }
669
+ return ($, entries) => {
670
+ const values = $.const(entries.filterMap((_$2, entry) => at(entry).match({
671
+ none: (_$3) => East.value(none, OptionType(FloatType)),
672
+ some: (_$3, n) => East.value(some(asFloat(n, inner)), OptionType(FloatType)),
673
+ })));
674
+ return variant("figure", values.size().equal(0n).ifElse((_$2) => East.value(none, OptionType(FloatType)), (_$2) => East.value(some(values.reduce((_$3, total, n) => total.add(n), East.value(0, FloatType))), OptionType(FloatType))));
675
+ };
676
+ }
677
+ case "histogram": {
678
+ if (held.type === "Array") {
679
+ const bins = spec.bins;
680
+ if (bins === undefined)
681
+ throw new Error(`${where}: the entries hold "${spec.field}" as an Array of counts — give the bins it counts in, bins: [0, …]`);
682
+ const countType = held.value;
683
+ // Position by position, each at its bin's edge — or, where an entry holds another number of counts, the total at
684
+ // NaN, which no bin holds, so the row is listed in Issues as the row's own would be (R27).
685
+ return ($, entries) => {
686
+ const edges = $.const(bins);
687
+ const counts = $.const(entries.map((_$2, entry) => at(entry)));
688
+ const fitting = $.const(counts.every((_$2, held_) => held_.size().equal(edges.size())));
689
+ return variant("histogram", fitting.ifElse((_$2) => edges.map((_$3, edge, i) => East.value({
690
+ from: edge,
691
+ count: counts.reduce((_$4, total, held_) => total.add(asFloat(held_.get(i), countType)), East.value(0, FloatType)),
692
+ }, BinType)), (_$2) => East.value([East.value({
693
+ from: NaN,
694
+ count: counts.reduce((_$3, total, held_) => total.add(held_.reduce((_$4, sum, n) => sum.add(asFloat(n, countType)), East.value(0, FloatType))), East.value(0, FloatType)),
695
+ }, BinType)], ArrayType(BinType))));
696
+ };
697
+ }
698
+ const durations = held;
699
+ const keyType = durations.key;
700
+ const countType = durations.value;
701
+ return ($, entries) => {
702
+ const added = $.let(new Map(), DictType(keyType, FloatType));
703
+ $.for(entries, ($2, entry) => {
704
+ const counts = at(entry)
705
+ .map((_$3, count) => asFloat(count, countType));
706
+ $2(added.unionInPlace(counts, (_$3, a, b) => a.add(b)));
707
+ });
708
+ return variant("histogram", added.toArray((_$2, count, from) => East.value({ from: asFloat(from, keyType), count }, BinType)));
709
+ };
710
+ }
711
+ case "list": {
712
+ const itemType = held.value;
713
+ if (itemType.type === "Struct") {
714
+ const rowItem = resolved.drawn;
715
+ if (rowItem === undefined)
716
+ throw new Error(`${where}: the entries hold "${spec.field}" as structs — give the list a draw`);
717
+ // Its draw reads the row's items: the entries' are added up, then drawn by it.
718
+ const own = rowType.fields[spec.field].value;
719
+ if (!isTypeEqual(own, itemType)) {
720
+ throw new Error(`${where}: the entries hold "${spec.field}"'s items as ${typeText(itemType)}, and the row as ${typeText(own)} — ` +
721
+ "its draw reads the row's: give the entries the row's items, or the list weigh: false");
722
+ }
723
+ const add = addedStructOf(where, spec.field, itemType);
724
+ return ($, entries, row) => {
725
+ const plus = $.const(add);
726
+ const drawn = $.const(rowItem);
727
+ const added = $.let(new Map(), DictType(StringType, itemType));
728
+ $.for(entries, ($2, entry) => {
729
+ $2(added.unionInPlace(at(entry), (_$3, a, b) => plus(a, b)));
730
+ });
731
+ return variant("list", added.toArray(($2, item, name) => {
732
+ const given = $2.const(drawn(item, name, row));
733
+ return East.value({ name, value: given.value, note: given.note }, ItemType);
734
+ }));
735
+ };
736
+ }
737
+ return ($, entries) => {
738
+ const added = $.let(new Map(), DictType(StringType, FloatType));
739
+ $.for(entries, ($2, entry) => {
740
+ const values = at(entry)
741
+ .map((_$3, value) => asFloat(value, itemType));
742
+ $2(added.unionInPlace(values, (_$3, a, b) => a.add(b)));
743
+ });
744
+ return variant("list", added.toArray((_$2, value, name) => East.value({ name, value, note: none }, ItemType)));
745
+ };
746
+ }
747
+ case "tags": {
748
+ if (held.type === "Set") {
749
+ // A Set's tags join as a Set does, in its order: every key picked draws the row's own, whatever the entries' order.
750
+ return (_$, entries) => variant("tags", entries.reduce((_$2, joined, entry) => joined.union(at(entry)), East.value(new Set(), SetType(StringType))).toArray());
751
+ }
752
+ // An Array's each once, in the entries' key order.
753
+ return ($, entries) => {
754
+ const join = $.const(joinOnce);
755
+ return variant("tags", join(entries.map((_$2, entry) => at(entry))));
756
+ };
757
+ }
758
+ }
759
+ }
760
+ /** An ordinal, as a refusal names an element. */
761
+ function ordinal(i) {
762
+ const n = i + 1;
763
+ const tens = n % 100;
764
+ const suffix = tens >= 11 && tens <= 13 ? "th" : (["th", "st", "nd", "rd"][n % 10] ?? "th");
765
+ return `${n}${suffix}`;
766
+ }
767
+ /**
768
+ * A read on the wire: how it draws, the words it draws in.
769
+ *
770
+ * @param spec - The read
771
+ * @param top - A list's lines unless given: 2 on a card, 1 on a label
772
+ */
773
+ function wireOf(spec, top) {
774
+ const label = spec.label ?? spec.field;
775
+ const option = (word) => word === undefined ? none : some(word);
776
+ switch (spec.kind) {
777
+ case "figure":
778
+ return variant("figure", { label, unit: option(spec.unit) });
779
+ case "histogram":
780
+ return variant("histogram", {
781
+ label,
782
+ unit: variant(spec.unit, null),
783
+ counts: option(spec.counts),
784
+ bins: spec.bins === undefined ? none : some(spec.bins),
785
+ scale: variant(spec.scale, null),
786
+ ticks: spec.ticks === undefined ? none
787
+ : some(spec.ticks.map((t) => typeof t === "number" ? { at: t, label: none } : { at: t.at, label: some(t.label) })),
788
+ });
789
+ case "list":
790
+ return variant("list", { label, unit: option(spec.unit), top: BigInt(spec.top ?? top) });
791
+ case "tags":
792
+ return variant("tags", { label, colors: spec.colors, legend: spec.legend });
793
+ }
794
+ }
795
+ /**
796
+ * One kind's details over the app's rows (R18, R19, R21), keyed as the kind's
797
+ * are, read only: `Data.bind(record)`, read whole beside a kind read whole,
798
+ * or `Data.bindPaged(record)`, read by the drawn rows' keys beside one read
799
+ * through its source.
800
+ *
801
+ * Bound through an entries index — `Data.bindPaged(record, { indexes: [it] })`
802
+ * — and naming the rail's source over that binding with `source`, the details
803
+ * are weighed by the source's picks of the Dict field the index files (#1522,
804
+ * R44, R45): read by key whatever the kind's read, each row with that field
805
+ * emptied, and each read whose field the entries hold drawing the picked keys'
806
+ * entries added up by kind — a figure adds, a histogram adds bin by bin, a
807
+ * list adds by name, tags join. A read given `weigh: false`, or one whose field
808
+ * the entries lack, draws the row's own; with no pick, every read does. A share
809
+ * or an average is given `weigh: false`, since adding two would make it wrong:
810
+ * `Flowchart.figure("onTime", { label: "On time", weigh: false })` draws each
811
+ * state's own share of its parcels through on time, whatever the picks.
812
+ *
813
+ * @typeParam R - The details' row type, inferred from the binding
814
+ * @typeParam S - The reads, as given
815
+ * @typeParam P - Through an entries index, an index entry's value, inferred from the binding
816
+ * @typeParam M - Through an entries index, the binding's indexes, inferred from it
817
+ * @typeParam IK - Through an entries index, the binding's index keys, inferred from it
818
+ * @typeParam N - Through an entries index, the binding's nested part, inferred from it
819
+ * @typeParam X - Through an entries index, the binding's maintenance part, inferred from it
820
+ * @param binding - The details' record: `Data.bind(record)`, `Data.bindPaged(record)`, or `Data.bindPaged(record, { indexes: [its entries index] })`
821
+ * @param spec - `{ source?, details: [ … ] }`: the rail's source whose picks weigh them, and every read, in the order drawn
822
+ * @returns The details, for a state's or a transition's `details`
823
+ * @throws {Error} Naming the binding or the read (R18–R20): `Record.bind` — "Details are read only: bind them with
824
+ * Data.bind(record), or Data.bindPaged(record) beside a kind read through its source" — or anything but a binding of
825
+ * a record of structs keyed by String; an element that is no read; a read's field the row lacks, or one of another
826
+ * type; an Array of counts without bins; a Dict of structs without a draw. Weighed (#1522): `source` naming the
827
+ * states' or the transitions' own; a source named while bound without an entries index, or an entries index bound
828
+ * with no source named; `weigh` beside details that name no source; a read of the entries' own field; a read whose
829
+ * field the entries hold in a type it doesn't take; a list of structs one of whose fields neither adds nor joins,
830
+ * unless it says `weigh: false`. The Flowchart refuses a source its slice lacks, and one over rows of another type
831
+ * (`Flowchart.payload`)
832
+ *
833
+ * @example
834
+ * ```tsx
835
+ * // .tsx file with the `@jsxImportSource @elaraai/e3-ui` pragma
836
+ * import { ArrayType, DictType, East, IntegerType, StringType, StructType, variant } from "@elaraai/east";
837
+ * import { Box, Reactive, UIComponentType } from "@elaraai/east-ui";
838
+ * import { Data, Flowchart } from "@elaraai/e3-ui";
839
+ * import e3 from "@elaraai/e3";
840
+ *
841
+ * // A support desk's tickets: the states they pass through, the transitions between them, and the minutes
842
+ * // each spent in each state. The app's task counts each state's tickets into the app's own bins — minutes at
843
+ * // first, then hours, a shift and a day — an Array of counts, one a bin, keyed as the states are.
844
+ * export const DeskState = StructType({ name: StringType, stage: StringType });
845
+ * export const DeskTransition = StructType({ from: StringType, to: StringType });
846
+ * export const DeskStage = StructType({ label: StringType, seq: IntegerType });
847
+ * export const deskStates = e3.input("flowchart_desk_states", DictType(StringType, DeskState), variant("value", new Map([
848
+ * ["NEW", { name: "New", stage: "open" }], ["TRI", { name: "Triaged", stage: "open" }],
849
+ * ["WIP", { name: "In progress", stage: "work" }], ["WAI", { name: "Waiting on customer", stage: "work" }],
850
+ * ["RES", { name: "Resolved", stage: "close" }],
851
+ * ])));
852
+ * export const deskTransitions = e3.input("flowchart_desk_transitions", DictType(StringType, DeskTransition), variant("value", new Map([
853
+ * ["t1", { from: "NEW", to: "TRI" }], ["t2", { from: "TRI", to: "WIP" }], ["t3", { from: "WIP", to: "WAI" }],
854
+ * ["t4", { from: "WAI", to: "WIP" }], ["t5", { from: "WIP", to: "RES" }],
855
+ * ])));
856
+ * export const deskStages = e3.input("flowchart_desk_stages", DictType(StringType, DeskStage), variant("value", new Map([
857
+ * ["open", { label: "Open", seq: 1n }], ["work", { label: "Work", seq: 2n }], ["close", { label: "Close", seq: 3n }],
858
+ * ])));
859
+ * export const TicketMinutes = StructType({ minutes: ArrayType(IntegerType) });
860
+ * export const ticketMinutes = e3.input("flowchart_ticket_minutes", DictType(StringType, TicketMinutes), variant("value", new Map([
861
+ * ["NEW", { minutes: [140n, 96n, 120n, 64n, 30n, 22n, 9n, 4n, 2n, 1n, 0n, 0n] }],
862
+ * ["TRI", { minutes: [62n, 88n, 131n, 90n, 41n, 25n, 12n, 6n, 3n, 1n, 0n, 0n] }],
863
+ * ["WIP", { minutes: [3n, 8n, 41n, 96n, 120n, 150n, 132n, 88n, 40n, 18n, 9n, 4n] }],
864
+ * ["WAI", { minutes: [0n, 1n, 2n, 6n, 9n, 14n, 30n, 58n, 74n, 66n, 41n, 22n] }],
865
+ * // A resolved ticket stays resolved: the app counts no time in it, so its row is absent and its card says no data.
866
+ * ])));
867
+ *
868
+ * const flowchartTicketMinutes = East.function([], UIComponentType, (_$) => (
869
+ * <Reactive>{$ => {
870
+ * const states = $.let(Data.bind(deskStates));
871
+ * const transitions = $.let(Data.bind(deskTransitions));
872
+ * const stages = $.let(Data.bind(deskStages));
873
+ * const minutes = $.let(Data.bind(ticketMinutes));
874
+ * return (
875
+ * <Box height="560px">
876
+ * <Flowchart
877
+ * states={Flowchart.states(states, {
878
+ * label: "name", lane: "stage",
879
+ * details: Flowchart.details(minutes, {
880
+ * details: [Flowchart.histogram("minutes", {
881
+ * unit: "minutes",
882
+ * label: "Time in state",
883
+ * counts: "tickets",
884
+ * // The edges, in minutes: an Array of counts is a bin each, in this order.
885
+ * bins: [0, 1, 2, 5, 10, 15, 30, 60, 120, 240, 480, 1440],
886
+ * // A bar a bin, whatever its span — the default; "linear" draws them to scale.
887
+ * scale: "band",
888
+ * // The edges the axis names besides 0: 5m and 1h printed from the unit,
889
+ * // the last two in the app's own words.
890
+ * ticks: [5, 60, { at: 480, label: "a shift" }, { at: 1440, label: "a day" }],
891
+ * })],
892
+ * }),
893
+ * })}
894
+ * transitions={Flowchart.transitions(transitions, { from: "from", to: "to" })}
895
+ * lanes={Flowchart.lanes(stages, { label: "label", order: "seq" })}
896
+ * name="ticketMinutes"
897
+ * />
898
+ * </Box>
899
+ * );
900
+ * }}</Reactive>
901
+ * ));
902
+ * ```
903
+ */
904
+ export function flowchartDetails(binding, spec) {
905
+ const where = "Flowchart.details";
906
+ const bound = bindingOf(binding, where);
907
+ const rowType = bound.rowType;
908
+ const given = typeof spec === "object" && spec !== null ? spec.details : undefined;
909
+ if (!Array.isArray(given))
910
+ throw new Error(`${where}: give the reads as { details: [ … ] } — Flowchart.figure, .histogram, .list or .tags, in the order drawn`);
911
+ // The source whose picks weigh them (#1522, R44): named beside an entries index, and only there.
912
+ const named = spec.source;
913
+ if (named !== undefined && (typeof named !== "string" || named.length === 0)) {
914
+ throw new Error(`${where}: \`source\` names the rail's source whose picks weigh the details — a source of the Flowchart's slice`);
915
+ }
916
+ const source = named;
917
+ const entries = bound.entries;
918
+ if (source !== undefined && entries === undefined) {
919
+ throw new Error(`${where}: the details name the source "${source}", whose picks weigh them through their record's entries index — bind them ` +
920
+ "with Data.bindPaged(record, { indexes: [its entries index] })");
921
+ }
922
+ if (source === undefined && entries !== undefined) {
923
+ throw new Error(`${where}: the details are bound through the entries index '${entries.index}', whose picks weigh them — name the rail's ` +
924
+ `source that offers '${entries.field}', source: "…"`);
925
+ }
926
+ if (entries !== undefined && entries.valueType.type !== "Struct") {
927
+ throw new Error(`${where}: the entries index '${entries.index}' files "${entries.field}", whose entries are ${typeText(entries.valueType)} — ` +
928
+ "the picks weigh the fields of an entry's struct, each named as the row's is");
929
+ }
930
+ const held = entries === undefined ? {} : entries.valueType.fields;
931
+ const reads = given.map((read, i) => {
932
+ const found = typeof read === "object" && read !== null ? READS.get(read) : undefined;
933
+ if (found === undefined) {
934
+ throw new Error(`${where}: \`details\` holds reads — Flowchart.figure, .histogram, .list or .tags — and the ${ordinal(i)} is none`);
935
+ }
936
+ const at = `Flowchart.${found.kind}`;
937
+ if (entries === undefined && found.weigh !== undefined) {
938
+ throw new Error(`${at}: "${found.field}" says weigh, and the details name no source whose picks weigh them — give the details ` +
939
+ "source: \"…\", or leave weigh out");
940
+ }
941
+ if (entries !== undefined && found.field === entries.field) {
942
+ throw new Error(`${at}: "${found.field}" is what the entries index '${entries.index}' files, entry by entry, and each row is read with it ` +
943
+ "emptied — read the fields its entries hold");
944
+ }
945
+ if (found.weigh === true && held[found.field] === undefined) {
946
+ throw new Error(`${at}: "${found.field}" says weigh: true, and the entries of "${entries.field}" hold no "${found.field}" ` +
947
+ `(${Object.keys(held).join(", ")}) — leave weigh out to draw the row's own`);
948
+ }
949
+ return resolve(found, rowType);
950
+ });
951
+ // Each read the picks weigh (R45): one the entries hold, unless it says weigh: false. Any other draws the row's own.
952
+ const weighers = reads.map((read) => {
953
+ const type = held[read.spec.field];
954
+ return type === undefined || read.spec.weigh === false ? undefined : weighed(read, type, rowType);
955
+ });
956
+ // A row's values, from its bytes: one a read, in the reads' order.
957
+ const values = East.function([BlobType], FlowchartDetailRowType, ($, bytes) => {
958
+ const row = $.const(bytes.decodeBeast(rowType, "v2"));
959
+ return East.value(reads.map((read) => read.value($, row)), FlowchartDetailRowType);
960
+ });
961
+ // A row's values weighed by the picks (R45), from its bytes and its picked entries' — each an entry's value, in the
962
+ // entries' key order: a read the picks weigh adds its entries up by kind, any other draws the row's own.
963
+ const entryType = entries?.valueType;
964
+ const fold = entryType === undefined ? undefined : East.function([BlobType, ArrayType(BlobType)], FlowchartDetailRowType, ($, bytes, picked) => {
965
+ const row = $.const(bytes.decodeBeast(rowType, "v2"));
966
+ const taken = $.const(picked.map((_$2, entry) => entry.decodeBeast(entryType, "v2")));
967
+ return East.value(reads.map((read, i) => {
968
+ const weigh = weighers[i];
969
+ return weigh === undefined ? read.value($, row) : weigh($, taken, row);
970
+ }), FlowchartDetailRowType);
971
+ });
972
+ // The details read whole, beside a kind read whole: each row's bytes, by key — built only then, since a paged
973
+ // read has no whole to read.
974
+ const Rows = DictType(StringType, BlobType);
975
+ const whole = () => {
976
+ const read = bound.handle.read;
977
+ return East.function([], OptionType(Rows), (_$) => East.value(some(read().map((_$2, row) => East.Blob.encodeBeast(row, "v2"))), OptionType(Rows)));
978
+ };
979
+ return {
980
+ [FLOWCHART_DETAILS]: "details",
981
+ rowType,
982
+ source,
983
+ inspectorReads: reads.some((r) => r.spec.show === "inspector"),
984
+ check(noun, at) {
985
+ if (noun !== "transitions")
986
+ return;
987
+ // A transition's label draws lines under its evidence: figures and lists, three at most (R21, R32).
988
+ let lines = 0;
989
+ for (const { spec: r } of reads) {
990
+ if (r.show === "inspector")
991
+ continue;
992
+ if (r.kind === "histogram" || r.kind === "tags") {
993
+ throw new Error(`${at}: a transition's label draws lines: a figure or a list — give the ${r.kind === "tags" ? "tags" : "histogram"} show: "inspector"`);
994
+ }
995
+ lines += r.kind === "list" ? (r.top ?? 1) : 1;
996
+ }
997
+ if (lines > 3) {
998
+ throw new Error(`${at}: a transition's label draws three lines at most under its evidence, and these draw ${lines} — ` +
999
+ "give one show: \"inspector\", or lower its top");
1000
+ }
1001
+ },
1002
+ build(noun, at, throughSource) {
1003
+ const top = noun === "states" ? 2 : 1;
1004
+ const wire = reads.map((r) => ({ show: variant(r.spec.show, null), read: wireOf(r.spec, top) }));
1005
+ if (entries !== undefined && fold !== undefined && source !== undefined) {
1006
+ // Weighed by the picks, the details are read by key whatever their kind's read (R44): the drawn rows' keys
1007
+ // through the entries index's lean rows, and the picked keys' ranges through the index.
1008
+ return East.value({
1009
+ reads: wire,
1010
+ rows: none,
1011
+ paged: some(fieldOf(bound.handle, "id")),
1012
+ values,
1013
+ weighed: some({
1014
+ source, field: entries.field, index: entries.index, reads: weighers.map((w) => w !== undefined), fold,
1015
+ }),
1016
+ }, FlowchartDetailsType);
1017
+ }
1018
+ if (throughSource && bound.form === "data") {
1019
+ throw new Error(`${at}: the ${noun} are read through their source, so their details are read by key: bind them with Data.bindPaged(record)`);
1020
+ }
1021
+ if (!throughSource && bound.form === "paged") {
1022
+ throw new Error(`${at}: the ${noun} are read whole, so their details are read whole: bind them with Data.bind(record)`);
1023
+ }
1024
+ return East.value({
1025
+ reads: wire,
1026
+ rows: throughSource ? none : some(whole()),
1027
+ // The paged read the details are read by key through: a reader asks the drawn rows' keys of it, in one request.
1028
+ paged: throughSource ? some(fieldOf(bound.handle, "id")) : none,
1029
+ values,
1030
+ weighed: none,
1031
+ }, FlowchartDetailsType);
1032
+ },
1033
+ };
1034
+ }
1035
+ //# sourceMappingURL=details.js.map