@elaraai/east-ui 1.0.80 → 1.0.82

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 (353) hide show
  1. package/dist/src/charts/chart/index.d.ts +4 -0
  2. package/dist/src/charts/chart/index.d.ts.map +1 -1
  3. package/dist/src/charts/spec/index.d.ts +8 -0
  4. package/dist/src/charts/spec/index.d.ts.map +1 -1
  5. package/dist/src/collections/deck/index.d.ts +8 -0
  6. package/dist/src/collections/deck/index.d.ts.map +1 -1
  7. package/dist/src/collections/index.d.ts +2 -2
  8. package/dist/src/collections/index.d.ts.map +1 -1
  9. package/dist/src/collections/index.js.map +1 -1
  10. package/dist/src/collections/library/index.d.ts +4 -0
  11. package/dist/src/collections/library/index.d.ts.map +1 -1
  12. package/dist/src/collections/library/types.d.ts +4 -0
  13. package/dist/src/collections/library/types.d.ts.map +1 -1
  14. package/dist/src/collections/matrix/index.d.ts +56 -19
  15. package/dist/src/collections/matrix/index.d.ts.map +1 -1
  16. package/dist/src/collections/matrix/index.js +38 -24
  17. package/dist/src/collections/matrix/index.js.map +1 -1
  18. package/dist/src/collections/plan/assemble.d.ts +655 -2113
  19. package/dist/src/collections/plan/assemble.d.ts.map +1 -1
  20. package/dist/src/collections/plan/assemble.js +159 -136
  21. package/dist/src/collections/plan/assemble.js.map +1 -1
  22. package/dist/src/collections/plan/builders.d.ts +193 -40
  23. package/dist/src/collections/plan/builders.d.ts.map +1 -1
  24. package/dist/src/collections/plan/builders.js +162 -32
  25. package/dist/src/collections/plan/builders.js.map +1 -1
  26. package/dist/src/collections/plan/factories.d.ts +290 -116
  27. package/dist/src/collections/plan/factories.d.ts.map +1 -1
  28. package/dist/src/collections/plan/factories.js +281 -156
  29. package/dist/src/collections/plan/factories.js.map +1 -1
  30. package/dist/src/collections/plan/index.d.ts +111 -58
  31. package/dist/src/collections/plan/index.d.ts.map +1 -1
  32. package/dist/src/collections/plan/index.js +62 -31
  33. package/dist/src/collections/plan/index.js.map +1 -1
  34. package/dist/src/collections/plan/ir.d.ts +759 -1769
  35. package/dist/src/collections/plan/ir.d.ts.map +1 -1
  36. package/dist/src/collections/plan/ir.js +51 -27
  37. package/dist/src/collections/plan/ir.js.map +1 -1
  38. package/dist/src/collections/plan/pick.d.ts +89 -71
  39. package/dist/src/collections/plan/pick.d.ts.map +1 -1
  40. package/dist/src/collections/plan/pick.js +101 -86
  41. package/dist/src/collections/plan/pick.js.map +1 -1
  42. package/dist/src/collections/plan/root.d.ts +164 -77
  43. package/dist/src/collections/plan/root.d.ts.map +1 -1
  44. package/dist/src/collections/plan/root.js +350 -42
  45. package/dist/src/collections/plan/root.js.map +1 -1
  46. package/dist/src/collections/plan/series.d.ts +12524 -10527
  47. package/dist/src/collections/plan/series.d.ts.map +1 -1
  48. package/dist/src/collections/plan/series.js +1596 -298
  49. package/dist/src/collections/plan/series.js.map +1 -1
  50. package/dist/src/collections/plan/types.d.ts +6115 -2802
  51. package/dist/src/collections/plan/types.d.ts.map +1 -1
  52. package/dist/src/collections/plan/types.js +575 -170
  53. package/dist/src/collections/plan/types.js.map +1 -1
  54. package/dist/src/collections/sheet/bridge.d.ts +154 -43
  55. package/dist/src/collections/sheet/bridge.d.ts.map +1 -1
  56. package/dist/src/collections/sheet/bridge.js +383 -240
  57. package/dist/src/collections/sheet/bridge.js.map +1 -1
  58. package/dist/src/collections/sheet/columns.d.ts +66 -16
  59. package/dist/src/collections/sheet/columns.d.ts.map +1 -1
  60. package/dist/src/collections/sheet/columns.js +25 -25
  61. package/dist/src/collections/sheet/columns.js.map +1 -1
  62. package/dist/src/collections/sheet/context-bridge.d.ts +48 -0
  63. package/dist/src/collections/sheet/context-bridge.d.ts.map +1 -0
  64. package/dist/src/collections/sheet/context-bridge.js +216 -0
  65. package/dist/src/collections/sheet/context-bridge.js.map +1 -0
  66. package/dist/src/collections/sheet/draft-bridge.d.ts +37 -0
  67. package/dist/src/collections/sheet/draft-bridge.d.ts.map +1 -0
  68. package/dist/src/collections/sheet/draft-bridge.js +153 -0
  69. package/dist/src/collections/sheet/draft-bridge.js.map +1 -0
  70. package/dist/src/collections/sheet/drafts.d.ts +14 -0
  71. package/dist/src/collections/sheet/drafts.d.ts.map +1 -0
  72. package/dist/src/collections/sheet/drafts.js +14 -0
  73. package/dist/src/collections/sheet/drafts.js.map +1 -0
  74. package/dist/src/collections/sheet/editing-bridge.d.ts +36 -0
  75. package/dist/src/collections/sheet/editing-bridge.d.ts.map +1 -0
  76. package/dist/src/collections/sheet/editing-bridge.js +196 -0
  77. package/dist/src/collections/sheet/editing-bridge.js.map +1 -0
  78. package/dist/src/collections/sheet/editing-types.d.ts +268 -0
  79. package/dist/src/collections/sheet/editing-types.d.ts.map +1 -0
  80. package/dist/src/collections/sheet/editing-types.js +34 -0
  81. package/dist/src/collections/sheet/editing-types.js.map +1 -0
  82. package/dist/src/collections/sheet/edits.d.ts +37 -0
  83. package/dist/src/collections/sheet/edits.d.ts.map +1 -0
  84. package/dist/src/collections/sheet/edits.js +31 -0
  85. package/dist/src/collections/sheet/edits.js.map +1 -0
  86. package/dist/src/collections/sheet/group.d.ts +165 -0
  87. package/dist/src/collections/sheet/group.d.ts.map +1 -0
  88. package/dist/src/collections/sheet/group.js +52 -0
  89. package/dist/src/collections/sheet/group.js.map +1 -0
  90. package/dist/src/collections/sheet/index.d.ts +171 -27
  91. package/dist/src/collections/sheet/index.d.ts.map +1 -1
  92. package/dist/src/collections/sheet/index.js +99 -17
  93. package/dist/src/collections/sheet/index.js.map +1 -1
  94. package/dist/src/collections/sheet/link.d.ts +22 -16
  95. package/dist/src/collections/sheet/link.d.ts.map +1 -1
  96. package/dist/src/collections/sheet/link.js +18 -12
  97. package/dist/src/collections/sheet/link.js.map +1 -1
  98. package/dist/src/collections/sheet/registers.d.ts +152 -5
  99. package/dist/src/collections/sheet/registers.d.ts.map +1 -1
  100. package/dist/src/collections/sheet/registers.js +154 -5
  101. package/dist/src/collections/sheet/registers.js.map +1 -1
  102. package/dist/src/collections/sheet/root.d.ts +175 -26
  103. package/dist/src/collections/sheet/root.d.ts.map +1 -1
  104. package/dist/src/collections/sheet/root.js +150 -26
  105. package/dist/src/collections/sheet/root.js.map +1 -1
  106. package/dist/src/collections/sheet/seed-bridge.d.ts +14 -0
  107. package/dist/src/collections/sheet/seed-bridge.d.ts.map +1 -0
  108. package/dist/src/collections/sheet/seed-bridge.js +51 -0
  109. package/dist/src/collections/sheet/seed-bridge.js.map +1 -0
  110. package/dist/src/collections/sheet/sub-rows.d.ts +249 -0
  111. package/dist/src/collections/sheet/sub-rows.d.ts.map +1 -0
  112. package/dist/src/collections/sheet/sub-rows.js +222 -0
  113. package/dist/src/collections/sheet/sub-rows.js.map +1 -0
  114. package/dist/src/collections/sheet/transactions.d.ts +76 -0
  115. package/dist/src/collections/sheet/transactions.d.ts.map +1 -0
  116. package/dist/src/collections/sheet/transactions.js +40 -0
  117. package/dist/src/collections/sheet/transactions.js.map +1 -0
  118. package/dist/src/collections/sheet/types.d.ts +12034 -3651
  119. package/dist/src/collections/sheet/types.d.ts.map +1 -1
  120. package/dist/src/collections/sheet/types.js +365 -48
  121. package/dist/src/collections/sheet/types.js.map +1 -1
  122. package/dist/src/collections/table/index.d.ts +98 -123
  123. package/dist/src/collections/table/index.d.ts.map +1 -1
  124. package/dist/src/collections/table/index.js +97 -111
  125. package/dist/src/collections/table/index.js.map +1 -1
  126. package/dist/src/collections/table/types.d.ts +87 -31
  127. package/dist/src/collections/table/types.d.ts.map +1 -1
  128. package/dist/src/collections/table/types.js +51 -26
  129. package/dist/src/collections/table/types.js.map +1 -1
  130. package/dist/src/collections/value-tree/flatten.d.ts.map +1 -1
  131. package/dist/src/collections/value-tree/flatten.js +4 -1
  132. package/dist/src/collections/value-tree/flatten.js.map +1 -1
  133. package/dist/src/component.d.ts +5075 -4079
  134. package/dist/src/component.d.ts.map +1 -1
  135. package/dist/src/component.js +46 -46
  136. package/dist/src/component.js.map +1 -1
  137. package/dist/src/contracts/approval.d.ts +10 -8
  138. package/dist/src/contracts/approval.d.ts.map +1 -1
  139. package/dist/src/contracts/approval.js +10 -8
  140. package/dist/src/contracts/approval.js.map +1 -1
  141. package/dist/src/contracts/drag.d.ts +11 -9
  142. package/dist/src/contracts/drag.d.ts.map +1 -1
  143. package/dist/src/contracts/drag.js +28 -21
  144. package/dist/src/contracts/drag.js.map +1 -1
  145. package/dist/src/contracts/editing.d.ts +1283 -0
  146. package/dist/src/contracts/editing.d.ts.map +1 -0
  147. package/dist/src/contracts/editing.js +711 -0
  148. package/dist/src/contracts/editing.js.map +1 -0
  149. package/dist/src/contracts/index.d.ts +2 -1
  150. package/dist/src/contracts/index.d.ts.map +1 -1
  151. package/dist/src/contracts/index.js +2 -1
  152. package/dist/src/contracts/index.js.map +1 -1
  153. package/dist/src/contracts/pick.d.ts +6 -7
  154. package/dist/src/contracts/pick.d.ts.map +1 -1
  155. package/dist/src/contracts/pick.js +6 -7
  156. package/dist/src/contracts/pick.js.map +1 -1
  157. package/dist/src/contracts/review.d.ts +3 -1
  158. package/dist/src/contracts/review.d.ts.map +1 -1
  159. package/dist/src/contracts/review.js +3 -1
  160. package/dist/src/contracts/review.js.map +1 -1
  161. package/dist/src/contracts/source.d.ts +270 -80
  162. package/dist/src/contracts/source.d.ts.map +1 -1
  163. package/dist/src/contracts/source.js +357 -58
  164. package/dist/src/contracts/source.js.map +1 -1
  165. package/dist/src/display/chip-rail/index.d.ts.map +1 -1
  166. package/dist/src/display/chip-rail/index.js +10 -10
  167. package/dist/src/display/chip-rail/index.js.map +1 -1
  168. package/dist/src/extension.d.ts +2 -1
  169. package/dist/src/extension.d.ts.map +1 -1
  170. package/dist/src/extension.js +2 -1
  171. package/dist/src/extension.js.map +1 -1
  172. package/dist/src/format/index.d.ts +1 -154
  173. package/dist/src/format/index.d.ts.map +1 -1
  174. package/dist/src/format/types.d.ts +35 -23
  175. package/dist/src/format/types.d.ts.map +1 -1
  176. package/dist/src/format/types.js +16 -14
  177. package/dist/src/format/types.js.map +1 -1
  178. package/dist/src/forms/field/index.js +4 -4
  179. package/dist/src/forms/field/index.js.map +1 -1
  180. package/dist/src/forms/file-upload/index.js +16 -16
  181. package/dist/src/forms/file-upload/index.js.map +1 -1
  182. package/dist/src/index.d.ts +4 -2
  183. package/dist/src/index.d.ts.map +1 -1
  184. package/dist/src/index.js +12 -5
  185. package/dist/src/index.js.map +1 -1
  186. package/dist/src/internal.d.ts +8 -3
  187. package/dist/src/internal.d.ts.map +1 -1
  188. package/dist/src/internal.js +11 -3
  189. package/dist/src/internal.js.map +1 -1
  190. package/dist/src/layout/box/index.d.ts.map +1 -1
  191. package/dist/src/layout/box/index.js +47 -47
  192. package/dist/src/layout/box/index.js.map +1 -1
  193. package/dist/src/layout/flex/index.d.ts.map +1 -1
  194. package/dist/src/layout/flex/index.js +47 -47
  195. package/dist/src/layout/flex/index.js.map +1 -1
  196. package/dist/src/layout/grid/index.js +11 -11
  197. package/dist/src/layout/grid/index.js.map +1 -1
  198. package/dist/src/layout/scroll-area/index.d.ts.map +1 -1
  199. package/dist/src/layout/scroll-area/index.js +8 -8
  200. package/dist/src/layout/scroll-area/index.js.map +1 -1
  201. package/dist/src/layout/separator/index.d.ts.map +1 -1
  202. package/dist/src/layout/separator/index.js +6 -6
  203. package/dist/src/layout/separator/index.js.map +1 -1
  204. package/dist/src/layout/stack/index.d.ts.map +1 -1
  205. package/dist/src/layout/stack/index.js +46 -46
  206. package/dist/src/layout/stack/index.js.map +1 -1
  207. package/dist/src/layout/sticky/index.d.ts.map +1 -1
  208. package/dist/src/layout/sticky/index.js +8 -8
  209. package/dist/src/layout/sticky/index.js.map +1 -1
  210. package/dist/src/overlays/action-bar/index.d.ts.map +1 -1
  211. package/dist/src/overlays/action-bar/index.js +8 -8
  212. package/dist/src/overlays/action-bar/index.js.map +1 -1
  213. package/dist/src/platform/index.d.ts +1 -1
  214. package/dist/src/platform/index.d.ts.map +1 -1
  215. package/dist/src/platform/index.js +1 -1
  216. package/dist/src/platform/index.js.map +1 -1
  217. package/dist/src/platform/slice/impl.d.ts +62 -51
  218. package/dist/src/platform/slice/impl.d.ts.map +1 -1
  219. package/dist/src/platform/slice/impl.js +155 -72
  220. package/dist/src/platform/slice/impl.js.map +1 -1
  221. package/dist/src/platform/slice/index.d.ts +3744 -2226
  222. package/dist/src/platform/slice/index.d.ts.map +1 -1
  223. package/dist/src/platform/slice/index.js +43 -1
  224. package/dist/src/platform/slice/index.js.map +1 -1
  225. package/dist/src/runtime/collections/deck.d.ts +4 -0
  226. package/dist/src/runtime/collections/deck.d.ts.map +1 -1
  227. package/dist/src/runtime/collections/matrix.d.ts +10 -9
  228. package/dist/src/runtime/collections/matrix.d.ts.map +1 -1
  229. package/dist/src/runtime/collections/matrix.js +11 -6
  230. package/dist/src/runtime/collections/matrix.js.map +1 -1
  231. package/dist/src/runtime/collections/plan.d.ts +148 -65
  232. package/dist/src/runtime/collections/plan.d.ts.map +1 -1
  233. package/dist/src/runtime/collections/plan.js +155 -67
  234. package/dist/src/runtime/collections/plan.js.map +1 -1
  235. package/dist/src/runtime/collections/sheet.d.ts +56 -23
  236. package/dist/src/runtime/collections/sheet.d.ts.map +1 -1
  237. package/dist/src/runtime/collections/sheet.js +30 -22
  238. package/dist/src/runtime/collections/sheet.js.map +1 -1
  239. package/dist/src/runtime/collections/table.d.ts +18 -11
  240. package/dist/src/runtime/collections/table.d.ts.map +1 -1
  241. package/dist/src/runtime/collections/table.js +7 -4
  242. package/dist/src/runtime/collections/table.js.map +1 -1
  243. package/dist/src/shared/reify.d.ts +4 -46
  244. package/dist/src/shared/reify.d.ts.map +1 -1
  245. package/dist/src/shared/reify.js +4 -61
  246. package/dist/src/shared/reify.js.map +1 -1
  247. package/dist/src/shared/tree.d.ts +88 -0
  248. package/dist/src/shared/tree.d.ts.map +1 -0
  249. package/dist/src/shared/tree.js +134 -0
  250. package/dist/src/shared/tree.js.map +1 -0
  251. package/dist/src/slice/breakdown/index.d.ts +4 -0
  252. package/dist/src/slice/breakdown/index.d.ts.map +1 -1
  253. package/dist/src/slice/breakdown/types.d.ts +4 -0
  254. package/dist/src/slice/breakdown/types.d.ts.map +1 -1
  255. package/dist/src/slice/cohort/index.d.ts +24 -4
  256. package/dist/src/slice/cohort/index.d.ts.map +1 -1
  257. package/dist/src/slice/cohort/index.js +8 -4
  258. package/dist/src/slice/cohort/index.js.map +1 -1
  259. package/dist/src/slice/cohort/types.d.ts +11 -0
  260. package/dist/src/slice/cohort/types.d.ts.map +1 -1
  261. package/dist/src/slice/cohort/types.js +7 -0
  262. package/dist/src/slice/cohort/types.js.map +1 -1
  263. package/dist/src/slice/filter/index.d.ts +4 -0
  264. package/dist/src/slice/filter/index.d.ts.map +1 -1
  265. package/dist/src/slice/filter/types.d.ts +4 -0
  266. package/dist/src/slice/filter/types.d.ts.map +1 -1
  267. package/dist/src/slice/index.d.ts +736 -0
  268. package/dist/src/slice/index.d.ts.map +1 -1
  269. package/dist/src/slice/legend/index.d.ts +4 -0
  270. package/dist/src/slice/legend/index.d.ts.map +1 -1
  271. package/dist/src/slice/legend/types.d.ts +4 -0
  272. package/dist/src/slice/legend/types.d.ts.map +1 -1
  273. package/dist/src/slice/rail/index.d.ts +8 -4
  274. package/dist/src/slice/rail/index.d.ts.map +1 -1
  275. package/dist/src/slice/rail/index.js.map +1 -1
  276. package/dist/src/slice/range/index.d.ts +4 -0
  277. package/dist/src/slice/range/index.d.ts.map +1 -1
  278. package/dist/src/slice/range/types.d.ts +4 -0
  279. package/dist/src/slice/range/types.d.ts.map +1 -1
  280. package/dist/src/slice/search/index.d.ts +4 -0
  281. package/dist/src/slice/search/index.d.ts.map +1 -1
  282. package/dist/src/slice/search/types.d.ts +4 -0
  283. package/dist/src/slice/search/types.d.ts.map +1 -1
  284. package/dist/src/slice/summary/index.d.ts +4 -0
  285. package/dist/src/slice/summary/index.d.ts.map +1 -1
  286. package/dist/src/slice/summary/types.d.ts +4 -0
  287. package/dist/src/slice/summary/types.d.ts.map +1 -1
  288. package/dist/src/typography/code/index.js +1 -1
  289. package/dist/src/typography/code/index.js.map +1 -1
  290. package/dist/src/typography/code-block/index.js +7 -7
  291. package/dist/src/typography/code-block/index.js.map +1 -1
  292. package/dist/src/typography/heading/index.js +2 -2
  293. package/dist/src/typography/heading/index.js.map +1 -1
  294. package/dist/src/typography/list/index.js +1 -1
  295. package/dist/src/typography/list/index.js.map +1 -1
  296. package/dist/src/typography/mark/index.js +1 -1
  297. package/dist/src/typography/mark/index.js.map +1 -1
  298. package/dist/src/typography/note/index.js +1 -1
  299. package/dist/src/typography/note/index.js.map +1 -1
  300. package/dist/src/typography/numeric/index.d.ts +3 -308
  301. package/dist/src/typography/numeric/index.d.ts.map +1 -1
  302. package/dist/src/typography/numeric/index.js +4 -4
  303. package/dist/src/typography/numeric/index.js.map +1 -1
  304. package/dist/src/typography/numeric/types.d.ts +1 -154
  305. package/dist/src/typography/numeric/types.d.ts.map +1 -1
  306. package/dist/src/typography/text/index.js +1 -1
  307. package/dist/src/typography/text/index.js.map +1 -1
  308. package/dist/test/collections/matrix.examples.d.ts +7 -3
  309. package/dist/test/collections/matrix.examples.d.ts.map +1 -1
  310. package/dist/test/collections/matrix.examples.js +55 -24
  311. package/dist/test/collections/matrix.examples.js.map +1 -1
  312. package/dist/test/collections/paged-source.examples.d.ts +4 -0
  313. package/dist/test/collections/paged-source.examples.d.ts.map +1 -1
  314. package/dist/test/collections/paged-source.examples.js +98 -13
  315. package/dist/test/collections/paged-source.examples.js.map +1 -1
  316. package/dist/test/collections/plan.examples.d.ts +101 -26
  317. package/dist/test/collections/plan.examples.d.ts.map +1 -1
  318. package/dist/test/collections/plan.examples.js +1481 -755
  319. package/dist/test/collections/plan.examples.js.map +1 -1
  320. package/dist/test/collections/sheet-link.examples.d.ts +7 -0
  321. package/dist/test/collections/sheet-link.examples.d.ts.map +1 -0
  322. package/dist/test/collections/sheet-link.examples.js +41 -0
  323. package/dist/test/collections/sheet-link.examples.js.map +1 -0
  324. package/dist/test/collections/sheet-transactions.examples.d.ts +7 -0
  325. package/dist/test/collections/sheet-transactions.examples.d.ts.map +1 -0
  326. package/dist/test/collections/sheet-transactions.examples.js +58 -0
  327. package/dist/test/collections/sheet-transactions.examples.js.map +1 -0
  328. package/dist/test/collections/sheet.examples.d.ts +52 -5
  329. package/dist/test/collections/sheet.examples.d.ts.map +1 -1
  330. package/dist/test/collections/sheet.examples.js +500 -158
  331. package/dist/test/collections/sheet.examples.js.map +1 -1
  332. package/dist/test/collections/table.examples.d.ts +31 -3
  333. package/dist/test/collections/table.examples.d.ts.map +1 -1
  334. package/dist/test/collections/table.examples.js +188 -28
  335. package/dist/test/collections/table.examples.js.map +1 -1
  336. package/dist/test/contracts/editing.examples.d.ts +7 -0
  337. package/dist/test/contracts/editing.examples.d.ts.map +1 -0
  338. package/dist/test/contracts/editing.examples.js +58 -0
  339. package/dist/test/contracts/editing.examples.js.map +1 -0
  340. package/dist/test/layout/splitter.examples.d.ts.map +1 -1
  341. package/dist/test/layout/splitter.examples.js +3 -1
  342. package/dist/test/layout/splitter.examples.js.map +1 -1
  343. package/dist/test/platform/slice.examples.d.ts.map +1 -1
  344. package/dist/test/platform/slice.examples.js +1 -0
  345. package/dist/test/platform/slice.examples.js.map +1 -1
  346. package/dist/test/slice/slice.examples.d.ts.map +1 -1
  347. package/dist/test/slice/slice.examples.js +5 -2
  348. package/dist/test/slice/slice.examples.js.map +1 -1
  349. package/package.json +4 -3
  350. package/dist/src/collections/plan/data-forms.d.ts +0 -332
  351. package/dist/src/collections/plan/data-forms.d.ts.map +0 -1
  352. package/dist/src/collections/plan/data-forms.js +0 -212
  353. package/dist/src/collections/plan/data-forms.js.map +0 -1
@@ -15,22 +15,42 @@
15
15
  * - `rowById` — the REAL source row by id: the captured collection or bind
16
16
  * handle on the inline arm, the source's `page` at the row's offset on the
17
17
  * paged arm. Built ONCE per sheet and shared by every wrapper;
18
- * - `decode` — the source row as the base (the row type's default when
19
- * absent — an insert, a proposal), then each column's field from its cell
20
- * by the static tag. A field with no column keeps the base row's value;
21
- * - `bridgeCtx` — the wire context to `Sheet.Types.Context(R, D)`;
18
+ * - `draftDecode` — current cells over the existing field draft, preserving
19
+ * hidden values and retaining missing/invalid input without domain defaults;
20
+ * - `bridgeCtx` — the wire context to `Sheet.Types.DraftContext(R, D)`;
21
+ * - `bridgeReady` — a readiness batch to one `DraftContext` per check, its
22
+ * rows built once (#882);
22
23
  * - the wrappers — a fill provider, a proposer, an arity rule, a member
23
- * check, a custom kind's parse / print, `onEdit` — each the author's typed
24
- * function inside its closed wire twin.
24
+ * check, an options rule, a custom kind's parse / print — each the
25
+ * author's typed function inside its closed wire twin;
26
+ * - the rule cells and sub rows (#844) — a column's `level` / `actual` /
27
+ * `detail` accessors and the `subRows` mappers, each compiled once and
28
+ * called inside the row projection.
29
+ *
30
+ * On a GROUPED sheet (#740) the source row is the group `P` and the columns
31
+ * are declared over the line `L` its lines field holds: `projectRow` /
32
+ * `draftRow` / the patch run over `L`, `rowById` returns the group, and the
33
+ * group half of the bridge ({@link SheetGroupBridge}) projects the group's
34
+ * summary cells and ordered children. Internal child keys preserve identity
35
+ * during local editing; the author's child arrays remain positional. A
36
+ * source with LOOSE rows between its groups (#846) holds entries
37
+ * `Sheet.Types.Entry(P, "lines")`: `rowById` returns the entry, a draft is
38
+ * the entry's draft, and a loose row decodes as a row of `L` whose id is its
39
+ * own.
25
40
  *
26
41
  * The bridge is the one place a string ever names a field: inside the
27
42
  * factory, against the column list it has already validated.
28
43
  *
29
44
  * @packageDocumentation
30
45
  */
31
- import { East, Expr, ArrayType, AsyncFunctionType, BooleanType, DateTimeType, DictType, FloatType, FunctionType, IntegerType, NullType, OptionType, StringType, StructType, defaultValue, variant, some, none, } from "@elaraai/east";
32
- import { SheetCellType, SheetLinkType, SheetContextType, SheetFillType, SheetProposalType, SheetProviderType, SheetProposerType, SheetCheckContextType, SheetCheckType, SheetCountedType, SheetEditType, SheetHalfType, SheetContextTypeFor, SheetCheckContextTypeFor, SheetEditTypeFor, SheetPatchTypeFor, SheetProposalTypeFor, } from "./types.js";
33
- import { SheetMembersType, SheetRegisterMembersType, parseLink, printLink, printLinkWith, EMPTY_LINK } from "./link.js";
46
+ import { East, Expr, ArrayType, AsyncFunctionType, BooleanType, DateTimeType, DictType, FloatType, FunctionType, IntegerType, NullType, OptionType, StringType, StructType, isTypeEqual, variant, some, none, } from "@elaraai/east";
47
+ import { SheetCellType, SheetLinkType, SheetRowType, SheetLineType, SheetContextType, SheetReadyBatchType, SheetFillType, SheetProposalType, SheetProviderType, SheetProposerType, SheetCheckContextType, SheetCheckType, SheetCountedType, SheetHalfType, SheetContextTypeFor, SheetCheckContextTypeFor, SheetPatchTypeFor, SheetProposalTypeFor, SheetGroupContextTypeFor, SheetGroupCheckContextTypeFor, SheetDateLevelType, SheetSubRowType, SheetOptionsRuleType, sheetLinesOf, sheetRuleCell, } from "./types.js";
48
+ import { resolveTag } from "../plan/builders.js";
49
+ import { SheetMembersType, SheetRegisterMembersType, parseLink, EMPTY_LINK } from "./link.js";
50
+ import { buildDraftRowDecoder, buildDraftGroupDecoder, buildDraftEntryDecoder } from "./draft-bridge.js";
51
+ import { buildDraftContextBridge, buildDraftBase, buildReadyContexts } from "./context-bridge.js";
52
+ import { SheetDraftEntryTypeFor, SheetDraftGroupTypeFor } from "./drafts.js";
53
+ import { SheetDraftTypeFor } from "./transactions.js";
34
54
  /** The wire cells dictionary type. */
35
55
  export const SheetCellsType = DictType(StringType, SheetCellType);
36
56
  /** The East type of an erased expression (`Expr.type` cannot infer through `ExprType<EastType>`). */
@@ -51,7 +71,7 @@ export function optionPayload(t) {
51
71
  }
52
72
  /** The cell arm a primitive payload lands in, or `undefined` for a type no cell holds. */
53
73
  export function cellTagOf(t) {
54
- if (t === SheetLinkType)
74
+ if (isTypeEqual(t, SheetLinkType))
55
75
  return "Link";
56
76
  switch (t.type) {
57
77
  case "String": return "String";
@@ -66,6 +86,45 @@ const SCALAR_PAYLOAD = {
66
86
  text: StringType, lookup: StringType, reference: StringType, enum: StringType, stamped: StringType,
67
87
  date: DateTimeType, quantity: FloatType, integer: IntegerType,
68
88
  };
89
+ /** Compile one rule accessor and check its payload — `String` or `DateTime`, or its Option. */
90
+ function ruleCell(key, rule, rowType, accessor, tag) {
91
+ const fn = East.function([rowType], undefined, (_$, r) => accessor(r));
92
+ const out = Expr.type(fn).output;
93
+ const payload = optionPayload(out);
94
+ const unwrapped = payload ?? out;
95
+ if (cellTagOf(unwrapped) !== tag) {
96
+ throw new Error(`Sheet: column "${key}" has a \`${rule}\` rule returning ${describeType(out)} — it must return ${tag} or Option<${tag}>`);
97
+ }
98
+ return { key: sheetRuleCell(rule, key), fn, tag, optional: payload !== undefined };
99
+ }
100
+ /**
101
+ * The unrendered cells a column's rules project (#844) — `detail` on any
102
+ * column, `level` and `actual` on a date column.
103
+ */
104
+ function ruleCellsOf(key, kind, cfg, rowType) {
105
+ const out = [];
106
+ if (cfg.detail !== undefined)
107
+ out.push(ruleCell(key, "detail", rowType, cfg.detail, "String"));
108
+ if (kind !== "date") {
109
+ if (cfg.level !== undefined || cfg.actual !== undefined) {
110
+ throw new Error(`Sheet: column "${key}" is a ${kind} column — \`level\` and \`actual\` are date-column rules`);
111
+ }
112
+ return out;
113
+ }
114
+ if (cfg.level !== undefined) {
115
+ const level = cfg.level;
116
+ const fn = East.function([rowType], StringType, (_$, r) => resolveTag(level(r), SheetDateLevelType).match({
117
+ week: (_$2) => "week",
118
+ day: (_$2) => "day",
119
+ range: (_$2) => "range",
120
+ time: (_$2) => "time",
121
+ }));
122
+ out.push({ key: sheetRuleCell("level", key), fn, tag: "String", optional: false });
123
+ }
124
+ if (cfg.actual !== undefined)
125
+ out.push(ruleCell(key, "actual", rowType, cfg.actual, "DateTime"));
126
+ return out;
127
+ }
69
128
  /**
70
129
  * Describe one column against the row type — the build-time refusals of
71
130
  * §3.12 live here, every one naming the column and the remedy.
@@ -77,6 +136,15 @@ const SCALAR_PAYLOAD = {
77
136
  * @throws Error naming the column when the kind cannot sit on the field
78
137
  */
79
138
  export function describeColumn(key, col, rowType) {
139
+ const meta = describeKind(key, col, rowType);
140
+ const ruleCells = ruleCellsOf(key, col.kind, col.config, rowType);
141
+ if (col.config.options !== undefined && col.kind !== "enum" && col.kind !== "lookup" && col.kind !== "link") {
142
+ throw new Error(`Sheet: column "${key}" is a ${col.kind} column — an \`options\` rule narrows an enum, lookup or link column's members`);
143
+ }
144
+ return ruleCells.length > 0 ? { ...meta, ruleCells } : meta;
145
+ }
146
+ /** {@link describeColumn}'s kind-by-kind half. */
147
+ function describeKind(key, col, rowType) {
80
148
  const fields = rowType.fields;
81
149
  const fieldType = fields[key];
82
150
  if (fieldType === undefined) {
@@ -94,11 +162,11 @@ export function describeColumn(key, col, rowType) {
94
162
  const out = Expr.type(derived).output;
95
163
  const outPayload = optionPayload(out);
96
164
  const outUnwrapped = outPayload ?? out;
97
- if (outUnwrapped !== payloadType) {
165
+ if (!isTypeEqual(outUnwrapped, payloadType)) {
98
166
  throw new Error(`Sheet: column "${key}" is a ${kind} column whose \`value\` projection returns ${outUnwrapped.type} — a ${kind} column's projection must return ${payloadType.type} or its Option`);
99
167
  }
100
168
  return {
101
- key, kind, config: cfg, fieldType, optional, payloadType,
169
+ key, field: key, kind, config: cfg, fieldType, optional, payloadType,
102
170
  cellTag: cellTagOf(payloadType), editable: false,
103
171
  derived, derivedOptional: outPayload !== undefined,
104
172
  ...(col.register !== undefined ? { register: col.register } : {}),
@@ -109,10 +177,10 @@ export function describeColumn(key, col, rowType) {
109
177
  let form;
110
178
  let ownHalf;
111
179
  let otherField;
112
- if (fieldType === SheetLinkType || unwrapped === SheetLinkType) {
180
+ if (isTypeEqual(fieldType, SheetLinkType) || isTypeEqual(unwrapped, SheetLinkType)) {
113
181
  form = "link";
114
182
  }
115
- else if (fieldType === SheetMembersType) {
183
+ else if (isTypeEqual(fieldType, SheetMembersType)) {
116
184
  form = "array";
117
185
  if (kind === "set") {
118
186
  ownHalf = "to";
@@ -128,7 +196,8 @@ export function describeColumn(key, col, rowType) {
128
196
  else {
129
197
  throw new Error(`Sheet: link column "${key}" sits on an Array<Member> field, so it must name the OTHER half's field — \`to: "…"\` when this field holds the from members, \`from: "…"\` when it holds the to members`);
130
198
  }
131
- if (otherField !== undefined && fields[otherField] !== SheetMembersType) {
199
+ const otherType = otherField !== undefined ? fields[otherField] : undefined;
200
+ if (otherField !== undefined && (otherType === undefined || !isTypeEqual(otherType, SheetMembersType))) {
132
201
  throw new Error(`Sheet: link column "${key}" names "${otherField}" as its other half, but that is not an Array<Sheet.Types.Member> field of the row`);
133
202
  }
134
203
  }
@@ -139,7 +208,7 @@ export function describeColumn(key, col, rowType) {
139
208
  throw new Error(`Sheet: ${kind} column "${key}" must sit on a Sheet.Types.Link, Array<Sheet.Types.Member> or String field — got ${fieldType.type}`);
140
209
  }
141
210
  return {
142
- key, kind, config: cfg, fieldType, optional: form === "link" ? unwrapped === SheetLinkType && optional : optional,
211
+ key, field: key, kind, config: cfg, fieldType, optional: form === "link" ? isTypeEqual(unwrapped, SheetLinkType) && optional : optional,
143
212
  payloadType: SheetLinkType, cellTag: "Link", editable: cfg.editable !== false,
144
213
  form,
145
214
  ...(ownHalf !== undefined ? { ownHalf } : {}),
@@ -155,27 +224,50 @@ export function describeColumn(key, col, rowType) {
155
224
  if (parsed === undefined) {
156
225
  throw new Error(`Sheet: custom column "${key}" has a \`parse\` whose output is not an Option — it must return Option<payload>, \`none\` meaning unrecognised`);
157
226
  }
158
- if (parsed !== unwrapped) {
227
+ if (!isTypeEqual(parsed, unwrapped)) {
159
228
  throw new Error(`Sheet: custom column "${key}" parses to ${parsed.type} but sits on a ${unwrapped.type} field — the payload must be the field's type (or its Option's)`);
160
229
  }
161
230
  const tag = cellTagOf(parsed);
162
231
  if (tag === undefined) {
163
232
  throw new Error(`Sheet: custom column "${key}" has payload ${parsed.type}, which no cell holds — use String, Integer, Float, DateTime, Boolean or Sheet.Types.Link`);
164
233
  }
165
- return { key, kind, config: cfg, fieldType, optional, payloadType: parsed, cellTag: tag, editable: cfg.editable !== false };
234
+ return { key, field: key, kind, config: cfg, fieldType, optional, payloadType: parsed, cellTag: tag, editable: cfg.editable !== false };
166
235
  }
167
236
  const payloadType = SCALAR_PAYLOAD[kind];
168
- if (unwrapped !== payloadType) {
237
+ if (!isTypeEqual(unwrapped, payloadType)) {
169
238
  throw new Error(`Sheet: ${kind} column "${key}" must sit on a ${payloadType.type} or Option<${payloadType.type}> field — got ${fieldType.type}`);
170
239
  }
171
240
  return {
172
- key, kind, config: cfg, fieldType, optional, payloadType,
241
+ key, field: key, kind, config: cfg, fieldType, optional, payloadType,
173
242
  cellTag: cellTagOf(payloadType),
174
243
  editable: kind !== "stamped" && cfg.editable !== false,
175
244
  ...(col.register !== undefined ? { register: col.register } : {}),
176
245
  ...(col.driverType !== undefined ? { driverType: col.driverType } : {}),
177
246
  };
178
247
  }
248
+ /**
249
+ * Describe one band cell against the GROUP's row type (#740) — a column
250
+ * declared over the group's field, keyed by the line column it sits under.
251
+ *
252
+ * @param cellKey - The line column the cell sits under, or `$title`
253
+ * @param cell - The built band cell
254
+ * @param groupType - The group's row type
255
+ * @returns The cell's metadata
256
+ * @throws Error naming the band cell when the kind cannot sit on the field, or is not a band kind
257
+ */
258
+ export function describeGroupCell(cellKey, cell, groupType) {
259
+ if (cell.kind === "lookup" || cell.kind === "set" || cell.kind === "link" || cell.kind === "custom") {
260
+ throw new Error(`Sheet: band cell "${cellKey}" is a ${cell.kind} cell — a band draws text, date, quantity, integer, reference, enum and stamped cells only`);
261
+ }
262
+ let meta;
263
+ try {
264
+ meta = describeColumn(cell.field, { kind: cell.kind, config: cell.config, ...(cell.register !== undefined ? { register: cell.register } : {}) }, groupType);
265
+ }
266
+ catch (e) {
267
+ throw new Error(`Sheet: band cell "${cellKey}" — ${e.message.replace(/^Sheet: /, "")}`);
268
+ }
269
+ return { ...meta, key: cellKey, field: cell.field };
270
+ }
179
271
  // ============================================================================
180
272
  // Cells — payload ↔ cell (the static-tag moves)
181
273
  // ============================================================================
@@ -186,11 +278,11 @@ export function cellOfPayload(tag, v) {
186
278
  /** The blank cell. */
187
279
  export const NULL_CELL = East.value(variant("Null", null), SheetCellType);
188
280
  /** A field VALUE (the payload or its Option) as a cell, by the column's static shape. */
189
- function cellOfFieldValue(meta, v, optional) {
281
+ function cellOfFieldValue(tag, v, optional) {
190
282
  if (!optional)
191
- return cellOfPayload(meta.cellTag, v);
283
+ return cellOfPayload(tag, v);
192
284
  return v.match({
193
- some: (_$, x) => cellOfPayload(meta.cellTag, x),
285
+ some: (_$, x) => cellOfPayload(tag, x),
194
286
  none: (_$) => NULL_CELL,
195
287
  });
196
288
  }
@@ -202,13 +294,13 @@ function cellOfFieldValue(meta, v, optional) {
202
294
  function projectCell(meta, r, members) {
203
295
  const row = r;
204
296
  if (meta.derived !== undefined) {
205
- return cellOfFieldValue(meta, meta.derived(r), meta.derivedOptional === true);
297
+ return cellOfFieldValue(meta.cellTag, meta.derived(r), meta.derivedOptional === true);
206
298
  }
207
- const fv = row[meta.key];
299
+ const fv = row[meta.field];
208
300
  if (meta.form === undefined)
209
- return cellOfFieldValue(meta, fv, meta.optional);
301
+ return cellOfFieldValue(meta.cellTag, fv, meta.optional);
210
302
  if (meta.form === "link")
211
- return cellOfFieldValue(meta, fv, meta.optional);
303
+ return cellOfFieldValue(meta.cellTag, fv, meta.optional);
212
304
  if (meta.form === "array") {
213
305
  const half = fv;
214
306
  const empty = East.value([], SheetMembersType);
@@ -227,54 +319,19 @@ function projectCell(meta, r, members) {
227
319
  none: (_$) => NULL_CELL,
228
320
  });
229
321
  }
230
- /** The field value a cell decodes to — the payload or its Option, by the column's static shape. */
231
- function fieldValueOfCell(meta, cell, optional, payloadType) {
232
- if (optional) {
233
- return cell.match({ [meta.cellTag]: (_$, v) => East.value(some(v), OptionType(payloadType)) }, (_$) => East.value(none, OptionType(payloadType)));
234
- }
235
- return cell.match({ [meta.cellTag]: (_$, v) => v }, (_$) => East.value(defaultValue(payloadType), payloadType));
236
- }
237
322
  /**
238
- * The value of THIS column's field from its cell (the base field when the cell
239
- * is absent or the column is read-only). `members` is the column's register,
240
- * bound in the caller's block, for the String link form's canonical print.
323
+ * The id of a row — the id field; on a source with loose rows (#846) the id
324
+ * field of whichever arm the entry holds.
241
325
  */
242
- function decodeOwnField(meta, cellOpt, base, members) {
243
- if (!meta.editable)
244
- return base;
245
- // The String form prints the link back through the grammar — the keys as
246
- // typed, or the register's labels under `store: "canonical"` (B§4.2).
247
- const print = (l) => meta.config.store === "canonical" && members !== undefined ? printLinkWith(l, members) : printLink(l);
248
- return cellOpt.match({
249
- none: (_$) => base,
250
- some: (_$, cell) => {
251
- if (meta.form === undefined)
252
- return fieldValueOfCell(meta, cell, meta.optional, meta.payloadType);
253
- if (meta.form === "link")
254
- return fieldValueOfCell(meta, cell, meta.optional, SheetLinkType);
255
- if (meta.form === "array") {
256
- return cell.match({ Link: (_$2, l) => meta.ownHalf === "from" ? l.from : l.to }, (_$2) => East.value([], SheetMembersType));
257
- }
258
- if (meta.optional) {
259
- return cell.match({ Link: (_$2, l) => East.value(some(print(l)), OptionType(StringType)) }, (_$2) => East.value(none, OptionType(StringType)));
260
- }
261
- return cell.match({ Link: (_$2, l) => print(l) }, (_$2) => East.value("", StringType));
262
- },
263
- });
264
- }
265
- /** The OTHER half's field of an `array`-form link column, from that column's cell. */
266
- function decodeOtherHalf(meta, cellOpt, base) {
267
- if (!meta.editable)
268
- return base;
269
- return cellOpt.match({
270
- none: (_$) => base,
271
- some: (_$, cell) => cell.match({ Link: (_$2, l) => meta.ownHalf === "from" ? l.to : l.from }, (_$2) => East.value([], SheetMembersType)),
272
- });
273
- }
274
- /** The id of a row — the id field, or the key of a keyed window. */
275
326
  function idOf(r, idField) {
276
327
  if (idField === undefined)
277
328
  throw new Error("Sheet: a positional source needs `id` — the String field that identifies a row");
329
+ if (Expr.type(r).type === "Variant") {
330
+ return r.match({
331
+ group: (_$, g) => g[idField],
332
+ row: (_$, l) => l[idField],
333
+ });
334
+ }
278
335
  return r[idField];
279
336
  }
280
337
  /**
@@ -284,23 +341,28 @@ function idOf(r, idField) {
284
341
  * @returns The bridge
285
342
  */
286
343
  export function buildBridge(input) {
287
- const { rowType, idField, metas, registers, driver, source } = input;
344
+ const { rowType, idField, metas, registers, driver, source, group: groupInput, subRows } = input;
288
345
  const driverType = driver !== undefined ? driver.rowType : NullType;
289
- const ctxType = SheetContextTypeFor(rowType, driverType);
290
- const checkCtxType = SheetCheckContextTypeFor(rowType);
291
- const editType = SheetEditTypeFor(rowType);
292
- const patchType = SheetPatchTypeFor(rowType);
293
- const proposalType = SheetProposalTypeFor(rowType);
294
- const fields = rowType.fields;
295
- const byField = new Map(metas.map((m) => [m.key, m]));
296
- const otherHalfBy = new Map();
297
- for (const m of metas)
298
- if (m.otherField !== undefined)
299
- otherHalfBy.set(m.otherField, m);
300
- // R → the wire cells. Registers are bound ONCE per body for the String
346
+ // A grouped sheet's columns are declared over the LINE type; its source
347
+ // rows are groups — or, with loose rows between them (#846), entries of a
348
+ // group or a row of the line type. A flat sheet's line type is its row type.
349
+ const loose = rowType.type === "Variant";
350
+ const groupType = loose ? rowType.cases.group : rowType;
351
+ const shape = groupInput !== undefined ? sheetLinesOf(groupType, groupInput.linesField) : undefined;
352
+ const lineType = shape?.lineType ?? groupType;
353
+ const lineIdField = shape === undefined ? idField : undefined;
354
+ const ctxType = (groupInput !== undefined
355
+ ? SheetGroupContextTypeFor(groupType, groupInput.linesField, driverType)
356
+ : SheetContextTypeFor(lineType, driverType));
357
+ const checkCtxType = (groupInput !== undefined
358
+ ? SheetGroupCheckContextTypeFor(groupType, groupInput.linesField)
359
+ : SheetCheckContextTypeFor(lineType));
360
+ const patchType = SheetPatchTypeFor(lineType);
361
+ const proposalType = SheetProposalTypeFor(lineType);
362
+ // L → the wire cells. Registers are bound ONCE per body for the String
301
363
  // link form (the capture rule: data, never a spliced expression).
302
364
  const stringFormRegisters = [...new Set(metas.filter((m) => m.form === "string" && m.register !== undefined).map((m) => m.register))];
303
- const projectRow = East.function([rowType], SheetCellsType, ($, r) => {
365
+ const projectRow = East.function([lineType], SheetCellsType, ($, r) => {
304
366
  const cells = $.let(new Map(), SheetCellsType);
305
367
  const bound = new Map();
306
368
  for (const name of stringFormRegisters) {
@@ -308,47 +370,41 @@ export function buildBridge(input) {
308
370
  }
309
371
  for (const m of metas) {
310
372
  $(cells.insert(m.key, projectCell(m, r, m.register !== undefined ? bound.get(m.register) : undefined)));
311
- }
312
- return cells;
313
- });
314
- // (id, cells, base) → R: the base row (the type's default when absent),
315
- // every column's field from its cell, the id field from the wire id.
316
- const decode = East.function([StringType, SheetCellsType, OptionType(rowType)], rowType, ($, id, cells, base) => {
317
- const b = $.let(base.match({
318
- some: (_$, x) => x,
319
- none: (_$) => East.value(defaultValue(rowType), rowType),
320
- }), rowType);
321
- // The String link form's registers, bound once per body (the capture rule).
322
- const bound = new Map();
323
- for (const name of stringFormRegisters) {
324
- bound.set(name, $.const(registers[name], SheetRegisterMembersType));
325
- }
326
- const out = {};
327
- for (const f of Object.keys(fields)) {
328
- const baseField = $.let(b[f], fields[f]);
329
- if (f === idField) {
330
- out[f] = id;
331
- continue;
373
+ for (const rc of m.ruleCells ?? []) {
374
+ const rule = $.const(rc.fn);
375
+ $(cells.insert(rc.key, cellOfFieldValue(rc.tag, rule(r), rc.optional)));
332
376
  }
333
- const own = byField.get(f);
334
- if (own !== undefined) {
335
- out[f] = decodeOwnField(own, cells.tryGet(own.key), baseField, own.register !== undefined ? bound.get(own.register) : undefined);
336
- continue;
337
- }
338
- const other = otherHalfBy.get(f);
339
- if (other !== undefined) {
340
- out[f] = decodeOtherHalf(other, cells.tryGet(other.key), baseField);
341
- continue;
342
- }
343
- out[f] = baseField;
344
377
  }
345
- return East.value(out, rowType);
378
+ return cells;
346
379
  });
347
- // (id, offset) → the real source row.
380
+ const projectSubRows = buildSubRowProjection(lineType, subRows);
381
+ // (id, offset) → the real source row. Inline, the row at its offset
382
+ // answers first — the renderer reads a resident row where it sits — and
383
+ // otherwise the nearest match outward from there, so a row that a local
384
+ // edit shifted is found in the steps it moved: a read is never a scan of
385
+ // the collection, which made every context and every readiness check
386
+ // quadratic in the rows (#859). The search starts inside the rows, so a
387
+ // stale offset still finds the row.
348
388
  const rowById = source.kind === "inline"
349
- ? East.function([StringType, IntegerType], OptionType(rowType), ($, id, _offset) => {
389
+ ? East.function([StringType, IntegerType], OptionType(rowType), ($, id, offset) => {
350
390
  const rows = $.const(source.rows, ArrayType(rowType));
351
- return rows.firstMap((_$, r) => idOf(r, idField).equal(id).ifElse((_$2) => East.value(some(r), OptionType(rowType)), (_$2) => East.value(none, OptionType(rowType))));
391
+ const size = $.let(rows.size());
392
+ const start = $.let(offset.less(0n).ifElse(() => 0n, () => offset.greaterEqual(size).ifElse(() => size.subtract(1n), () => offset)));
393
+ const step = $.let(0n);
394
+ $.while(start.subtract(step).greaterEqual(0n).or(() => start.add(step).less(size)), ($2) => {
395
+ const below = $2.let(start.subtract(step));
396
+ $2.if(below.greaterEqual(0n), ($3) => {
397
+ const r = $3.let(rows.get(below));
398
+ $3.if(idOf(r, idField).equal(id), ($4) => { $4.return(East.value(some(r), OptionType(rowType))); });
399
+ });
400
+ const above = $2.let(start.add(step));
401
+ $2.if(step.greater(0n).and(() => above.less(size)), ($3) => {
402
+ const r = $3.let(rows.get(above));
403
+ $3.if(idOf(r, idField).equal(id), ($4) => { $4.return(East.value(some(r), OptionType(rowType))); });
404
+ });
405
+ $2.assign(step, step.add(1n));
406
+ });
407
+ return East.value(none, OptionType(rowType));
352
408
  })
353
409
  : East.function([StringType, IntegerType], OptionType(rowType), ($, id, offset) => {
354
410
  const src = $.const(source.source);
@@ -368,28 +424,46 @@ export function buildBridge(input) {
368
424
  return dict.tryGet(k);
369
425
  })
370
426
  : East.function([StringType], OptionType(driverType), ($, _k) => $.const(none, OptionType(driverType)));
371
- // The wire context → Sheet.Types.Context(R, D).
372
- const bridgeCtx = East.function([SheetContextType], ctxType, ($, ctx) => {
373
- const byId = $.const(rowById);
374
- const dec = $.const(decode);
375
- const row = $.let(dec(ctx.rowId, ctx.row, byId(ctx.rowId, ctx.offset)), rowType);
376
- const rows = $.let(ctx.rows.map((_$, r, i) => dec(r.id, r.cells, byId(r.id, ctx.rowsOffset.add(i)))), ArrayType(rowType));
377
- const lookup = $.const(lookupDriver);
378
- const driverRow = $.let(ctx.driver.match({
379
- none: (_$) => East.value(none, OptionType(driverType)),
380
- some: (_$, k) => lookup(k),
381
- }), OptionType(driverType));
382
- return East.value({
383
- rowIndex: ctx.rowIndex,
384
- row,
385
- rows,
386
- partial: ctx.partial,
387
- driver: driverRow,
388
- today: ctx.today,
389
- }, ctxType);
390
- });
391
- // Sheet.Types.Patch(R) → the set fields as cells.
392
- const encodePatch = East.function([patchType], SheetCellsType, ($, patch) => {
427
+ // The drafts: a line's cells over its draft (no id — a line's identity is
428
+ // its place), and on a source with loose rows (#846) a loose row's, whose
429
+ // id field is its own.
430
+ const draftRow = buildDraftRowDecoder(lineType, metas, registers, lineIdField);
431
+ const looseRow = loose ? buildDraftRowDecoder(lineType, metas, registers, idField) : undefined;
432
+ const decodeGroup = groupInput !== undefined
433
+ ? buildDraftGroupDecoder(groupType, groupInput.linesField, groupInput.cellMetas, draftRow, registers, idField)
434
+ : undefined;
435
+ const flatDraft = SheetDraftTypeFor(groupType);
436
+ const draftDecode = (decodeGroup === undefined
437
+ ? East.function([SheetRowType, OptionType(flatDraft), OptionType(SheetRowType)], flatDraft, ($, row, base, _previous) => {
438
+ const decode = $.const(draftRow);
439
+ return decode(row.id, row.cells, base);
440
+ })
441
+ : looseRow !== undefined && groupInput !== undefined
442
+ ? buildDraftEntryDecoder(rowType, groupInput.linesField, decodeGroup, looseRow)
443
+ : decodeGroup);
444
+ // The group half (#740).
445
+ const group = groupInput !== undefined && shape !== undefined && decodeGroup !== undefined
446
+ ? { ...buildGroupBridge(groupType, groupInput, projectRow, projectSubRows), groupType, loose, decodeGroup, decodeLoose: looseRow }
447
+ : undefined;
448
+ const bridgeCtx = buildDraftContextBridge(rowType, lineType, groupInput?.linesField, ctxType, driverType, rowById, draftDecode, draftRow, lookupDriver, looseRow);
449
+ const bridgeReady = buildReadyContexts(rowType, groupInput?.linesField, ctxType, driverType, rowById, draftDecode, lookupDriver, loose);
450
+ const encodePatch = buildPatchCells(lineType, metas, registers);
451
+ return {
452
+ rowType, lineType, driverType, ctxType, checkCtxType, patchType, proposalType,
453
+ projectRow, projectSubRows, draftDecode, draftRow,
454
+ rowById: rowById,
455
+ bridgeCtx: bridgeCtx,
456
+ bridgeReady,
457
+ encodePatch, seedCells: buildPatchCells(lineType, metas, registers, false),
458
+ ...(group !== undefined ? { group } : {}),
459
+ };
460
+ }
461
+ /** Project supplied constructor/proposal fields without inventing a complete row. @internal */
462
+ export function buildPatchCells(rowType, metas, registers, editableOnly = true) {
463
+ const patchType = SheetPatchTypeFor(rowType);
464
+ const fields = rowType.fields;
465
+ const stringFormRegisters = [...new Set(metas.filter(m => m.form === "string" && m.register !== undefined).map(m => m.register))];
466
+ return East.function([patchType], SheetCellsType, ($, patch) => {
393
467
  const cells = $.let(new Map(), SheetCellsType);
394
468
  const p = patch;
395
469
  const bound = new Map();
@@ -397,12 +471,12 @@ export function buildBridge(input) {
397
471
  bound.set(name, $.const(registers[name], SheetRegisterMembersType));
398
472
  }
399
473
  for (const m of metas) {
400
- if (!m.editable || m.derived !== undefined)
474
+ if ((editableOnly && !m.editable) || m.derived !== undefined)
401
475
  continue;
402
476
  if (m.form === "array") {
403
477
  // The other half is `none` when the column names no other field.
404
478
  const otherKey = m.otherField;
405
- const own = $.let(p[m.key], OptionType(SheetMembersType));
479
+ const own = $.let(p[m.field], OptionType(SheetMembersType));
406
480
  const other = $.let((otherKey !== undefined ? p[otherKey] : none), OptionType(SheetMembersType));
407
481
  $.if(own.hasTag("some").or(() => other.hasTag("some")), ($2) => {
408
482
  const empty = $2.const([], SheetMembersType);
@@ -415,21 +489,85 @@ export function buildBridge(input) {
415
489
  });
416
490
  continue;
417
491
  }
418
- const field = $.let(p[m.key], OptionType(fields[m.key]));
492
+ const field = $.let(p[m.field], OptionType(fields[m.field]));
419
493
  $.if(field.hasTag("some"), ($2) => {
420
- const v = $2.let(field.unwrap("some"), fields[m.key]);
421
- const asRow = { [m.key]: v };
494
+ const v = $2.let(field.unwrap("some"), fields[m.field]);
495
+ const asRow = { [m.field]: v };
422
496
  $2(cells.insert(m.key, projectCell(m, asRow, m.register !== undefined ? bound.get(m.register) : undefined)));
423
497
  });
424
498
  }
425
499
  return cells;
426
500
  });
427
- return {
428
- rowType, driverType, ctxType, checkCtxType, editType, patchType, proposalType,
429
- projectRow, decode,
430
- rowById: rowById,
431
- bridgeCtx, encodePatch,
432
- };
501
+ }
502
+ // ============================================================================
503
+ // Sub rows (#844)
504
+ // ============================================================================
505
+ /**
506
+ * Compile the sub-row sources into one projection over the line type: each
507
+ * mapper is compiled ONCE as `East.function([L, T], SubRow)` and called in
508
+ * an eager map over its field; the sources concatenate in declaration order.
509
+ *
510
+ * @param lineType - The type the columns are built over
511
+ * @param subRows - The `subRows` declaration, if any
512
+ * @returns `L` → its sub rows
513
+ * @throws Error when the declaration was built over another type, or a key is not an array field
514
+ */
515
+ export function buildSubRowProjection(lineType, subRows) {
516
+ const fields = lineType.fields;
517
+ const mappers = [];
518
+ if (subRows !== undefined) {
519
+ if (!isTypeEqual(subRows.rowType, lineType)) {
520
+ throw new Error("Sheet: `subRows` was built over a different type than the columns — pass the columns' type (a grouped sheet: the line type) to `Sheet.subRows(…)`");
521
+ }
522
+ for (const [field, map] of Object.entries(subRows.sources)) {
523
+ if (map === undefined)
524
+ continue;
525
+ const t = fields[field];
526
+ if (t === undefined || t.type !== "Array" || t.value === undefined) {
527
+ throw new Error(`Sheet: \`subRows\` names "${field}", which is ${t === undefined ? "not a field of the row type" : `a ${t.type} field`} — a sub-row source must be an Array field`);
528
+ }
529
+ mappers.push({ field, fn: East.function([lineType, t.value], SheetSubRowType, (_$, row, item) => map(item, row)) });
530
+ }
531
+ }
532
+ return East.function([lineType], ArrayType(SheetSubRowType), ($, r) => {
533
+ const row = r;
534
+ let all = $.const([], ArrayType(SheetSubRowType));
535
+ for (const m of mappers) {
536
+ const map = $.const(m.fn);
537
+ const part = $.let(row[m.field].map((_$2, x) => map(r, x)), ArrayType(SheetSubRowType));
538
+ all = mappers[0] === m ? part : $.let(all.concat(part), ArrayType(SheetSubRowType));
539
+ }
540
+ return all;
541
+ });
542
+ }
543
+ // ============================================================================
544
+ // The group half (#740)
545
+ // ============================================================================
546
+ /**
547
+ * Compile a group's summary cells and ordered children into wire rows.
548
+ *
549
+ * @param rowType - The group's row type
550
+ * @param input - The child field and summary cell metadata
551
+ * @param projectRow - Child row to wire cells
552
+ * @param projectSubRows - Child row to its sub rows
553
+ * @returns The group half
554
+ */
555
+ function buildGroupBridge(rowType, input, projectRow, projectSubRows) {
556
+ const { linesField, cellMetas } = input;
557
+ const linesType = rowType.fields[linesField];
558
+ const projectGroupCells = East.function([rowType], SheetCellsType, ($, p) => {
559
+ const cells = $.let(new Map(), SheetCellsType);
560
+ for (const m of cellMetas)
561
+ $(cells.insert(m.key, projectCell(m, p, undefined)));
562
+ return cells;
563
+ });
564
+ const projectLines = East.function([rowType], ArrayType(SheetLineType), ($, p) => {
565
+ const project = $.const(projectRow);
566
+ const subRowsOf = $.const(projectSubRows);
567
+ const lines = $.const(p[linesField], linesType);
568
+ return lines.map((_$, line, index) => East.value({ key: East.print(index), cells: project(line), subRows: subRowsOf(line) }, SheetLineType));
569
+ });
570
+ return { linesField, cellMetas, projectGroupCells, projectLines };
433
571
  }
434
572
  // ============================================================================
435
573
  // Wrappers — the author's typed functions inside their wire twins
@@ -446,27 +584,34 @@ function pinProvider(bridge, fn, where, expectedOutput) {
446
584
  throw new Error(`Sheet: ${where} must be an East.function or East.asyncFunction — got a ${t.type}`);
447
585
  }
448
586
  const inputs = t.inputs ?? [];
449
- if (inputs.length !== 1 || inputs[0] !== bridge.ctxType) {
450
- throw new Error(`Sheet: ${where} must take exactly this sheet's context — Sheet.Types.Context(RowType${bridge.driverType === NullType ? "" : ", DriverType"}) — as its one argument; a function written over another row or driver type cannot run here`);
587
+ if (inputs.length !== 1 || !isTypeEqual(inputs[0], bridge.ctxType)) {
588
+ throw new Error(`Sheet: ${where} must take exactly this sheet's context — ${contextName(bridge)} — as its one argument; a function written over another row or driver type cannot run here`);
451
589
  }
452
- if (t.output !== expectedOutput) {
590
+ if (t.output === undefined || !isTypeEqual(t.output, expectedOutput)) {
453
591
  throw new Error(`Sheet: ${where} returns the wrong type — it must return ${describeType(expectedOutput)}`);
454
592
  }
455
593
  return { fn: expr, async: t.type === "AsyncFunction" };
456
594
  }
595
+ /** How this sheet's context is spelt, for a refusal message. */
596
+ function contextName(bridge) {
597
+ const driver = isTypeEqual(bridge.driverType, NullType) ? "" : ", DriverType";
598
+ return bridge.group !== undefined
599
+ ? `Sheet.Types.DraftContext(GroupType, "${bridge.group.linesField}"${driver})`
600
+ : `Sheet.Types.DraftContext(RowType${driver})`;
601
+ }
457
602
  /** A short description of an East type for a refusal message. */
458
603
  function describeType(t) {
459
- const v = t;
460
- if (v.type === "Variant" && v.cases !== undefined && optionPayload(t) !== undefined)
461
- return `Option<${describeType(optionPayload(t))}>`;
462
- if (v.type === "Struct" && v.fields !== undefined)
463
- return `{ ${Object.entries(v.fields).map(([k, f]) => `${k}: ${describeType(f)}`).join(", ")} }`;
464
- if (v.type === "Array" && v.value !== undefined)
465
- return `Array<${describeType(v.value)}>`;
466
- return v.type;
604
+ const payload = optionPayload(t);
605
+ if (payload !== undefined)
606
+ return `Option<${describeType(payload)}>`;
607
+ if (t.type === "Struct")
608
+ return `{ ${Object.entries(t.fields).map(([k, f]) => `${k}: ${describeType(f)}`).join(", ")} }`;
609
+ if (t.type === "Array")
610
+ return `Array<${describeType(t.value)}>`;
611
+ return t.type;
467
612
  }
468
613
  /**
469
- * Wrap a fill provider — `Sheet.Types.Context(R, D)` → `Option<Fill(T)>` —
614
+ * Wrap a fill provider — `Sheet.Types.DraftContext(R, D)` → `Option<Fill(T)>` —
470
615
  * into the wire provider variant (sync or async by the function's type).
471
616
  */
472
617
  export function wrapProvider(bridge, meta, fn, index) {
@@ -487,7 +632,7 @@ export function wrapProvider(bridge, meta, fn, index) {
487
632
  return East.value(variant(async ? "async" : "sync", wire), SheetProviderType);
488
633
  }
489
634
  /**
490
- * Wrap a row proposer — `Sheet.Types.Context(R, D)` → `Array<Proposal(R)>` —
635
+ * Wrap a row proposer — `Sheet.Types.DraftContext(R, D)` → `Array<Proposal(R)>` —
491
636
  * into the wire proposer variant.
492
637
  */
493
638
  export function wrapProposer(bridge, fn, index) {
@@ -504,7 +649,7 @@ export function wrapProposer(bridge, fn, index) {
504
649
  : East.function([SheetContextType], ArrayType(SheetProposalType), body);
505
650
  return East.value(variant(async ? "async" : "sync", wire), SheetProposerType);
506
651
  }
507
- /** Wrap an arity rule — `Sheet.Types.Context(R, D)` → `Option<Counted>`. */
652
+ /** Wrap an arity rule — `Sheet.Types.DraftContext(R, D)` → `Option<Counted>`. */
508
653
  export function wrapArity(bridge, meta, fn) {
509
654
  const { fn: author, async } = pinProvider(bridge, fn, `column "${meta.key}" arity rule`, OptionType(SheetCountedType));
510
655
  if (async)
@@ -515,19 +660,80 @@ export function wrapArity(bridge, meta, fn) {
515
660
  return a(bc(ctx));
516
661
  });
517
662
  }
663
+ /**
664
+ * Wrap an options rule (#844) — `Sheet.Types.DraftContext(R, D)` →
665
+ * `Option<Array<String>>` — into the wire rule. Synchronous: the menu reads
666
+ * it as the cell opens.
667
+ */
668
+ export function wrapOptions(bridge, meta, fn) {
669
+ const { fn: author, async } = pinProvider(bridge, fn, `column "${meta.key}" options rule`, OptionType(ArrayType(StringType)));
670
+ if (async)
671
+ throw new Error(`Sheet: column "${meta.key}" options rule must be synchronous — the menu reads it as the cell opens`);
672
+ return East.function([SheetContextType], OptionType(ArrayType(StringType)), ($, ctx) => {
673
+ const a = $.const(author);
674
+ const bc = $.const(bridge.bridgeCtx);
675
+ return a(bc(ctx));
676
+ });
677
+ }
518
678
  /** Wrap an author member check — `Sheet.Types.CheckContext(R)` → `Option<String>` — into the `custom` check arm. */
519
679
  export function wrapCheck(bridge, meta, fn, index) {
520
680
  const expr = East.value(fn);
521
681
  const t = typeOf(expr);
522
- if (t.type !== "Function" || (t.inputs ?? []).length !== 1 || t.inputs[0] !== bridge.checkCtxType || t.output !== OptionType(StringType)) {
523
- throw new Error(`Sheet: column "${meta.key}" check #${index + 1} must be an East.function over Sheet.Types.CheckContext(RowType) returning Option<String>`);
682
+ if (t.type !== "Function" || (t.inputs ?? []).length !== 1 || !isTypeEqual(t.inputs[0], bridge.checkCtxType) ||
683
+ t.output === undefined || !isTypeEqual(t.output, OptionType(StringType))) {
684
+ const spelt = bridge.group !== undefined ? `Sheet.Types.CheckContext(GroupType, "${bridge.group.linesField}")` : "Sheet.Types.CheckContext(RowType)";
685
+ throw new Error(`Sheet: column "${meta.key}" check #${index + 1} must be an East.function over ${spelt} returning Option<String>`);
524
686
  }
687
+ const groupHalf = bridge.group;
688
+ const field = groupHalf?.linesField;
689
+ const entryDraft = SheetDraftEntryTypeFor(bridge.rowType, field);
690
+ const draftType = (groupHalf === undefined ? entryDraft : SheetDraftGroupTypeFor(groupHalf.groupType, groupHalf.linesField));
691
+ const rowDraft = SheetDraftTypeFor(bridge.lineType);
692
+ const base = buildDraftBase(bridge.rowType, field, bridge.rowById);
525
693
  const wire = East.function([SheetCheckContextType], OptionType(StringType), ($, c) => {
526
694
  const a = $.const(expr);
527
- const byId = $.const(bridge.rowById);
528
- const dec = $.const(bridge.decode);
529
- const row = $.let(dec(c.rowId, c.row, byId(c.rowId, c.offset)), bridge.rowType);
530
- return a(East.value({ rowIndex: c.rowIndex, row, half: c.half, member: c.member }, bridge.checkCtxType));
695
+ const read = $.const(base);
696
+ const dec = $.const(bridge.draftRow);
697
+ const original = $.const(read(c.drafts, c.rowId, c.offset), OptionType(entryDraft));
698
+ if (field === undefined || groupHalf === undefined) {
699
+ const row = $.const(dec(c.rowId, c.row, original));
700
+ return a($.const({ rowIndex: c.rowIndex, row, group: none, half: c.half, member: c.member }, bridge.checkCtxType));
701
+ }
702
+ const decGroup = $.const(groupHalf.decodeGroup);
703
+ /** A line's check: the line over its prior draft in its group, the group decoded over the wire group row. */
704
+ const lineCheck = ($2, own) => {
705
+ const group = $2.const(c.group.match({
706
+ none: () => own,
707
+ some: (_$, wire) => some(decGroup(wire, own, some(wire))),
708
+ }), OptionType(draftType));
709
+ const prior = $2.const(group.match({
710
+ none: () => none,
711
+ some: (_$, value) => {
712
+ const children = value[field];
713
+ return c.rowIndex.greaterEqual(0n).and(() => c.rowIndex.less(children.size())).ifElse(() => some(children.get(c.rowIndex)), () => none);
714
+ },
715
+ }), OptionType(rowDraft));
716
+ const row = $2.const(dec("", c.row, prior));
717
+ return a($2.const({ rowIndex: c.rowIndex, row, group, half: c.half, member: c.member }, bridge.checkCtxType));
718
+ };
719
+ const decLoose = groupHalf.decodeLoose;
720
+ if (decLoose === undefined)
721
+ return lineCheck($, original);
722
+ // A source with loose rows (#846): the entry's arms. A loose row has
723
+ // no line address — its own cells over its own draft, and no group.
724
+ const entry = original;
725
+ const decodeLoose = $.const(decLoose);
726
+ return c.line.match({
727
+ some: ($2) => {
728
+ const own = $2.const(entry.match({ none: () => none, some: (_$, e) => e.match({ group: (_$3, g) => some(g), row: () => none }) }), OptionType(draftType));
729
+ return lineCheck($2, own);
730
+ },
731
+ none: ($2) => {
732
+ const own = $2.const(entry.match({ none: () => none, some: (_$, e) => e.match({ row: (_$3, r) => some(r), group: () => none }) }), OptionType(rowDraft));
733
+ const row = $2.const(decodeLoose(c.rowId, c.row, own));
734
+ return a($2.const({ rowIndex: c.rowIndex, row, group: none, half: c.half, member: c.member }, bridge.checkCtxType));
735
+ },
736
+ });
531
737
  });
532
738
  return East.value(variant("custom", wire), SheetCheckType);
533
739
  }
@@ -536,8 +742,8 @@ export function wrapCustomParse(bridge, meta, fn) {
536
742
  const expr = East.value(fn);
537
743
  const t = typeOf(expr);
538
744
  const inputs = t.inputs ?? [];
539
- if (t.type !== "Function" || inputs.length !== 2 || inputs[0] !== StringType || inputs[1] !== bridge.ctxType) {
540
- throw new Error(`Sheet: custom column "${meta.key}" \`parse\` must be an East.function over (String, Sheet.Types.Context(RowType, DriverType)) returning Option<payload>`);
745
+ if (t.type !== "Function" || inputs.length !== 2 || !isTypeEqual(inputs[0], StringType) || !isTypeEqual(inputs[1], bridge.ctxType)) {
746
+ throw new Error(`Sheet: custom column "${meta.key}" \`parse\` must be an East.function over (String, ${contextName(bridge)}) returning Option<payload>`);
541
747
  }
542
748
  return East.function([StringType, SheetContextType], OptionType(SheetCellType), ($, text, ctx) => {
543
749
  const a = $.const(expr);
@@ -553,7 +759,8 @@ export function wrapCustomPrint(meta, fn) {
553
759
  const expr = East.value(fn);
554
760
  const t = typeOf(expr);
555
761
  const inputs = t.inputs ?? [];
556
- if (t.type !== "Function" || inputs.length !== 1 || inputs[0] !== meta.payloadType || t.output !== StringType) {
762
+ if (t.type !== "Function" || inputs.length !== 1 || !isTypeEqual(inputs[0], meta.payloadType) ||
763
+ t.output === undefined || !isTypeEqual(t.output, StringType)) {
557
764
  throw new Error(`Sheet: custom column "${meta.key}" \`print\` must be an East.function over the payload (${describeType(meta.payloadType)}) returning String`);
558
765
  }
559
766
  return East.function([SheetCellType], StringType, ($, cell) => {
@@ -561,70 +768,6 @@ export function wrapCustomPrint(meta, fn) {
561
768
  return cell.match({ [meta.cellTag]: (_$, v) => a(v) }, (_$) => East.value("", StringType));
562
769
  });
563
770
  }
564
- /** Wrap a typed `onEdit` — `Sheet.Types.Edit(R)` → `Null` — into the wire edit handler. */
565
- export function wrapOnEdit(bridge, fn) {
566
- const expr = East.value(fn, FunctionType([bridge.editType], NullType));
567
- return East.function([SheetEditType], NullType, ($, e) => {
568
- const a = $.const(expr);
569
- const byId = $.const(bridge.rowById);
570
- const dec = $.const(bridge.decode);
571
- const noRow = $.const(none, OptionType(bridge.rowType));
572
- const et = bridge.editType;
573
- $(e.match({
574
- commit: (_$, c) => a(East.value(variant("commit", {
575
- rowId: c.rowId, key: c.key, row: dec(c.rowId, c.row.cells, byId(c.rowId, c.offset)), source: c.source,
576
- }), et)),
577
- insert: (_$, i) => a(East.value(variant("insert", {
578
- afterRowId: i.afterRowId, row: dec(i.row.id, i.row.cells, noRow), source: i.source,
579
- }), et)),
580
- remove: (_$, r) => a(East.value(variant("remove", { rowIds: r.rowIds }), et)),
581
- }));
582
- });
583
- }
584
- /**
585
- * Compile `onUpdate` into the wire edit handler (§4.7) — the whole-value
586
- * rebuild: a commit rewrites one row, an insert appends a fresh row after
587
- * `afterRowId`, a remove filters ids; then the author's `onUpdate` receives
588
- * the collection.
589
- */
590
- export function compileOnUpdate(bridge, rows, idField, fn) {
591
- const rowsType = ArrayType(bridge.rowType);
592
- const expr = East.value(fn, FunctionType([rowsType], NullType));
593
- return East.function([SheetEditType], NullType, ($, ev) => {
594
- const current = $.let(rows, rowsType);
595
- const upd = $.const(expr);
596
- const dec = $.const(bridge.decode);
597
- const noRow = $.const(none, OptionType(bridge.rowType));
598
- const next = $.let(ev.match({
599
- commit: (_$, c) => current.map((_$2, r) => idOf(r, idField).equal(c.rowId).ifElse((_$3) => dec(c.rowId, c.row.cells, East.value(some(r), OptionType(bridge.rowType))), (_$3) => r)),
600
- insert: ($2, i) => {
601
- const fresh = $2.let(dec(i.row.id, i.row.cells, noRow), bridge.rowType);
602
- return i.afterRowId.match({
603
- none: (_$3) => current.concat(East.value([fresh], rowsType)),
604
- some: (_$3, after) => current.flatMap((_$4, r) => idOf(r, idField).equal(after).ifElse((_$5) => East.value([r, fresh], rowsType), (_$5) => East.value([r], rowsType))),
605
- });
606
- },
607
- remove: ($2, rm) => {
608
- const ids = $2.let(rm.rowIds.toSet());
609
- return current.filter((_$3, r) => ids.has(idOf(r, idField)).not());
610
- },
611
- }), rowsType);
612
- $(upd(next));
613
- });
614
- }
615
- /** Both channels at once — `onEdit` observes, then `onUpdate` writes. */
616
- export function composeEditHandlers(observe, write) {
617
- if (observe === undefined)
618
- return write;
619
- if (write === undefined)
620
- return observe;
621
- return East.function([SheetEditType], NullType, ($, e) => {
622
- const o = $.const(observe);
623
- const w = $.const(write);
624
- $(o(e));
625
- $(w(e));
626
- });
627
- }
628
771
  /** Re-exported for the root's driver-typed refusals. */
629
772
  export { SheetHalfType, BooleanType, AsyncFunctionType, EMPTY_LINK };
630
773
  //# sourceMappingURL=bridge.js.map