@fieldia/core 0.30.1 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (551) hide show
  1. package/README.md +362 -1
  2. package/package.json +1 -1
  3. package/schema/page.schema.json +758 -1
  4. package/src/index.d.mts +13 -2
  5. package/src/index.d.ts +13 -2
  6. package/src/index.js +45 -2
  7. package/src/index.js.map +1 -1
  8. package/src/index.mjs +18 -2
  9. package/src/index.mjs.map +1 -1
  10. package/src/lib/format/actions.d.mts +16 -16
  11. package/src/lib/format/actions.d.ts +16 -16
  12. package/src/lib/format/actions.js +3 -0
  13. package/src/lib/format/actions.js.map +1 -1
  14. package/src/lib/format/actions.mjs +3 -0
  15. package/src/lib/format/actions.mjs.map +1 -1
  16. package/src/lib/format/address-search.js +3 -0
  17. package/src/lib/format/address-search.js.map +1 -1
  18. package/src/lib/format/address-search.mjs +3 -0
  19. package/src/lib/format/address-search.mjs.map +1 -1
  20. package/src/lib/format/duplicates.js +3 -0
  21. package/src/lib/format/duplicates.js.map +1 -1
  22. package/src/lib/format/duplicates.mjs +3 -0
  23. package/src/lib/format/duplicates.mjs.map +1 -1
  24. package/src/lib/format/endings.js +3 -0
  25. package/src/lib/format/endings.js.map +1 -1
  26. package/src/lib/format/endings.mjs +3 -0
  27. package/src/lib/format/endings.mjs.map +1 -1
  28. package/src/lib/format/field.d.mts +669 -0
  29. package/src/lib/format/field.d.ts +669 -0
  30. package/src/lib/format/field.js +42 -4
  31. package/src/lib/format/field.js.map +1 -1
  32. package/src/lib/format/field.mjs +42 -4
  33. package/src/lib/format/field.mjs.map +1 -1
  34. package/src/lib/format/flow-checks.d.mts +14 -0
  35. package/src/lib/format/flow-checks.d.ts +14 -0
  36. package/src/lib/format/flow-checks.js +61 -0
  37. package/src/lib/format/flow-checks.js.map +1 -0
  38. package/src/lib/format/flow-checks.mjs +58 -0
  39. package/src/lib/format/flow-checks.mjs.map +1 -0
  40. package/src/lib/format/flow.d.mts +43 -0
  41. package/src/lib/format/flow.d.ts +43 -0
  42. package/src/lib/format/flow.js +22 -0
  43. package/src/lib/format/flow.js.map +1 -0
  44. package/src/lib/format/flow.mjs +19 -0
  45. package/src/lib/format/flow.mjs.map +1 -0
  46. package/src/lib/format/from-schema.d.mts +70 -0
  47. package/src/lib/format/from-schema.d.ts +70 -0
  48. package/src/lib/format/from-schema.js +599 -0
  49. package/src/lib/format/from-schema.js.map +1 -0
  50. package/src/lib/format/from-schema.mjs +591 -0
  51. package/src/lib/format/from-schema.mjs.map +1 -0
  52. package/src/lib/format/jitless.d.mts +1 -0
  53. package/src/lib/format/jitless.d.ts +1 -0
  54. package/src/lib/format/jitless.js +27 -0
  55. package/src/lib/format/jitless.js.map +1 -0
  56. package/src/lib/format/jitless.mjs +24 -0
  57. package/src/lib/format/jitless.mjs.map +1 -0
  58. package/src/lib/format/json-schema.js +3 -0
  59. package/src/lib/format/json-schema.js.map +1 -1
  60. package/src/lib/format/json-schema.mjs +3 -0
  61. package/src/lib/format/json-schema.mjs.map +1 -1
  62. package/src/lib/format/json.js +3 -0
  63. package/src/lib/format/json.js.map +1 -1
  64. package/src/lib/format/json.mjs +3 -0
  65. package/src/lib/format/json.mjs.map +1 -1
  66. package/src/lib/format/languages-checks.d.mts +2 -1
  67. package/src/lib/format/languages-checks.d.ts +2 -1
  68. package/src/lib/format/languages-checks.js +8 -2
  69. package/src/lib/format/languages-checks.js.map +1 -1
  70. package/src/lib/format/languages-checks.mjs +8 -2
  71. package/src/lib/format/languages-checks.mjs.map +1 -1
  72. package/src/lib/format/later-checks.d.mts +3 -1
  73. package/src/lib/format/later-checks.d.ts +3 -1
  74. package/src/lib/format/later-checks.js +3 -1
  75. package/src/lib/format/later-checks.js.map +1 -1
  76. package/src/lib/format/later-checks.mjs +3 -1
  77. package/src/lib/format/later-checks.mjs.map +1 -1
  78. package/src/lib/format/layout.d.mts +13192 -7550
  79. package/src/lib/format/layout.d.ts +13192 -7550
  80. package/src/lib/format/layout.js +61 -3
  81. package/src/lib/format/layout.js.map +1 -1
  82. package/src/lib/format/layout.mjs +59 -1
  83. package/src/lib/format/layout.mjs.map +1 -1
  84. package/src/lib/format/layout.types.d.mts +3 -1
  85. package/src/lib/format/layout.types.d.ts +3 -1
  86. package/src/lib/format/limits.js +3 -0
  87. package/src/lib/format/limits.js.map +1 -1
  88. package/src/lib/format/limits.mjs +3 -0
  89. package/src/lib/format/limits.mjs.map +1 -1
  90. package/src/lib/format/list-checks.d.mts +1 -1
  91. package/src/lib/format/list-checks.d.ts +1 -1
  92. package/src/lib/format/list-checks.js +67 -22
  93. package/src/lib/format/list-checks.js.map +1 -1
  94. package/src/lib/format/list-checks.mjs +67 -22
  95. package/src/lib/format/list-checks.mjs.map +1 -1
  96. package/src/lib/format/list-rows-checks.d.mts +11 -0
  97. package/src/lib/format/list-rows-checks.d.ts +11 -0
  98. package/src/lib/format/list-rows-checks.js +62 -0
  99. package/src/lib/format/list-rows-checks.js.map +1 -0
  100. package/src/lib/format/list-rows-checks.mjs +59 -0
  101. package/src/lib/format/list-rows-checks.mjs.map +1 -0
  102. package/src/lib/format/media-options.d.mts +84 -0
  103. package/src/lib/format/media-options.d.ts +84 -0
  104. package/src/lib/format/media-options.js +86 -0
  105. package/src/lib/format/media-options.js.map +1 -0
  106. package/src/lib/format/media-options.mjs +80 -0
  107. package/src/lib/format/media-options.mjs.map +1 -0
  108. package/src/lib/format/outbox-checks.d.mts +11 -0
  109. package/src/lib/format/outbox-checks.d.ts +11 -0
  110. package/src/lib/format/outbox-checks.js +12 -0
  111. package/src/lib/format/outbox-checks.js.map +1 -0
  112. package/src/lib/format/outbox-checks.mjs +9 -0
  113. package/src/lib/format/outbox-checks.mjs.map +1 -0
  114. package/src/lib/format/page.d.mts +3057 -966
  115. package/src/lib/format/page.d.ts +3057 -966
  116. package/src/lib/format/page.js +15 -0
  117. package/src/lib/format/page.js.map +1 -1
  118. package/src/lib/format/page.mjs +15 -0
  119. package/src/lib/format/page.mjs.map +1 -1
  120. package/src/lib/format/part-checks.d.mts +3 -0
  121. package/src/lib/format/part-checks.d.ts +3 -0
  122. package/src/lib/format/payment-checks.d.mts +13 -0
  123. package/src/lib/format/payment-checks.d.ts +13 -0
  124. package/src/lib/format/payment-checks.js +30 -0
  125. package/src/lib/format/payment-checks.js.map +1 -0
  126. package/src/lib/format/payment-checks.mjs +27 -0
  127. package/src/lib/format/payment-checks.mjs.map +1 -0
  128. package/src/lib/format/payment.d.mts +37 -0
  129. package/src/lib/format/payment.d.ts +37 -0
  130. package/src/lib/format/payment.js +15 -0
  131. package/src/lib/format/payment.js.map +1 -0
  132. package/src/lib/format/payment.mjs +12 -0
  133. package/src/lib/format/payment.mjs.map +1 -0
  134. package/src/lib/format/phone.js +3 -0
  135. package/src/lib/format/phone.js.map +1 -1
  136. package/src/lib/format/phone.mjs +3 -0
  137. package/src/lib/format/phone.mjs.map +1 -1
  138. package/src/lib/format/picker-options.d.mts +12 -0
  139. package/src/lib/format/picker-options.d.ts +12 -0
  140. package/src/lib/format/picker-options.js +21 -3
  141. package/src/lib/format/picker-options.js.map +1 -1
  142. package/src/lib/format/picker-options.mjs +20 -2
  143. package/src/lib/format/picker-options.mjs.map +1 -1
  144. package/src/lib/format/prefill-checks.d.mts +13 -0
  145. package/src/lib/format/prefill-checks.d.ts +13 -0
  146. package/src/lib/format/prefill-checks.js +35 -0
  147. package/src/lib/format/prefill-checks.js.map +1 -0
  148. package/src/lib/format/prefill-checks.mjs +32 -0
  149. package/src/lib/format/prefill-checks.mjs.map +1 -0
  150. package/src/lib/format/prefill.d.mts +33 -0
  151. package/src/lib/format/prefill.d.ts +33 -0
  152. package/src/lib/format/prefill.js +20 -0
  153. package/src/lib/format/prefill.js.map +1 -0
  154. package/src/lib/format/prefill.mjs +17 -0
  155. package/src/lib/format/prefill.mjs.map +1 -0
  156. package/src/lib/format/quiz.d.mts +58 -0
  157. package/src/lib/format/quiz.d.ts +58 -0
  158. package/src/lib/format/quiz.js +24 -0
  159. package/src/lib/format/quiz.js.map +1 -0
  160. package/src/lib/format/quiz.mjs +21 -0
  161. package/src/lib/format/quiz.mjs.map +1 -0
  162. package/src/lib/format/records.js +3 -0
  163. package/src/lib/format/records.js.map +1 -1
  164. package/src/lib/format/records.mjs +3 -0
  165. package/src/lib/format/records.mjs.map +1 -1
  166. package/src/lib/format/references.d.mts +1 -1
  167. package/src/lib/format/references.d.ts +1 -1
  168. package/src/lib/format/references.js +56 -7
  169. package/src/lib/format/references.js.map +1 -1
  170. package/src/lib/format/references.mjs +57 -8
  171. package/src/lib/format/references.mjs.map +1 -1
  172. package/src/lib/format/schedule-checks.d.mts +8 -0
  173. package/src/lib/format/schedule-checks.d.ts +8 -0
  174. package/src/lib/format/schedule-checks.js +16 -0
  175. package/src/lib/format/schedule-checks.js.map +1 -0
  176. package/src/lib/format/schedule-checks.mjs +13 -0
  177. package/src/lib/format/schedule-checks.mjs.map +1 -0
  178. package/src/lib/format/signature-checks.d.mts +8 -0
  179. package/src/lib/format/signature-checks.d.ts +8 -0
  180. package/src/lib/format/signature-checks.js +21 -0
  181. package/src/lib/format/signature-checks.js.map +1 -1
  182. package/src/lib/format/signature-checks.mjs +20 -0
  183. package/src/lib/format/signature-checks.mjs.map +1 -1
  184. package/src/lib/format/signature-options.js +3 -0
  185. package/src/lib/format/signature-options.js.map +1 -1
  186. package/src/lib/format/signature-options.mjs +3 -0
  187. package/src/lib/format/signature-options.mjs.map +1 -1
  188. package/src/lib/format/slots-options.js +3 -0
  189. package/src/lib/format/slots-options.js.map +1 -1
  190. package/src/lib/format/slots-options.mjs +3 -0
  191. package/src/lib/format/slots-options.mjs.map +1 -1
  192. package/src/lib/format/templates-checks.d.mts +6 -0
  193. package/src/lib/format/templates-checks.d.ts +6 -0
  194. package/src/lib/format/templates-checks.js +16 -0
  195. package/src/lib/format/templates-checks.js.map +1 -0
  196. package/src/lib/format/templates-checks.mjs +13 -0
  197. package/src/lib/format/templates-checks.mjs.map +1 -0
  198. package/src/lib/format/translate.d.mts +2 -0
  199. package/src/lib/format/translate.d.ts +2 -0
  200. package/src/lib/format/translate.js +6 -2
  201. package/src/lib/format/translate.js.map +1 -1
  202. package/src/lib/format/translate.mjs +4 -2
  203. package/src/lib/format/translate.mjs.map +1 -1
  204. package/src/lib/format/typed-options.d.mts +43 -0
  205. package/src/lib/format/typed-options.d.ts +43 -0
  206. package/src/lib/format/typed-options.js +50 -0
  207. package/src/lib/format/typed-options.js.map +1 -0
  208. package/src/lib/format/typed-options.mjs +46 -0
  209. package/src/lib/format/typed-options.mjs.map +1 -0
  210. package/src/lib/format/upgrade.d.mts +5 -1
  211. package/src/lib/format/upgrade.d.ts +5 -1
  212. package/src/lib/format/upgrade.js +5 -1
  213. package/src/lib/format/upgrade.js.map +1 -1
  214. package/src/lib/format/upgrade.mjs +5 -1
  215. package/src/lib/format/upgrade.mjs.map +1 -1
  216. package/src/lib/format/validate.js +3 -0
  217. package/src/lib/format/validate.js.map +1 -1
  218. package/src/lib/format/validate.mjs +3 -0
  219. package/src/lib/format/validate.mjs.map +1 -1
  220. package/src/lib/format/verify-checks.d.mts +10 -0
  221. package/src/lib/format/verify-checks.d.ts +10 -0
  222. package/src/lib/format/verify-checks.js +44 -0
  223. package/src/lib/format/verify-checks.js.map +1 -0
  224. package/src/lib/format/verify-checks.mjs +41 -0
  225. package/src/lib/format/verify-checks.mjs.map +1 -0
  226. package/src/lib/format/version.d.mts +2 -2
  227. package/src/lib/format/version.d.ts +2 -2
  228. package/src/lib/format/version.js +2 -2
  229. package/src/lib/format/version.js.map +1 -1
  230. package/src/lib/format/version.mjs +2 -2
  231. package/src/lib/format/version.mjs.map +1 -1
  232. package/src/lib/format/week.d.mts +18 -0
  233. package/src/lib/format/week.d.ts +18 -0
  234. package/src/lib/format/week.js +58 -0
  235. package/src/lib/format/week.js.map +1 -0
  236. package/src/lib/format/week.mjs +55 -0
  237. package/src/lib/format/week.mjs.map +1 -0
  238. package/src/lib/record/approvals-part.d.mts +1 -1
  239. package/src/lib/record/approvals-part.d.ts +1 -1
  240. package/src/lib/record/approvals-part.js +2 -2
  241. package/src/lib/record/approvals-part.js.map +1 -1
  242. package/src/lib/record/approvals-part.mjs +1 -1
  243. package/src/lib/record/approvals-part.mjs.map +1 -1
  244. package/src/lib/record/data-source.d.mts +331 -0
  245. package/src/lib/record/data-source.d.ts +331 -0
  246. package/src/lib/record/data-source.js.map +1 -1
  247. package/src/lib/record/data-source.mjs.map +1 -1
  248. package/src/lib/record/day-words.d.mts +22 -1
  249. package/src/lib/record/day-words.d.ts +22 -1
  250. package/src/lib/record/day-words.js +29 -2
  251. package/src/lib/record/day-words.js.map +1 -1
  252. package/src/lib/record/day-words.mjs +24 -2
  253. package/src/lib/record/day-words.mjs.map +1 -1
  254. package/src/lib/record/files-part.d.mts +1 -0
  255. package/src/lib/record/files-part.d.ts +1 -0
  256. package/src/lib/record/files-part.js +7 -1
  257. package/src/lib/record/files-part.js.map +1 -1
  258. package/src/lib/record/files-part.mjs +5 -1
  259. package/src/lib/record/files-part.mjs.map +1 -1
  260. package/src/lib/record/form-approvals.d.mts +12 -17
  261. package/src/lib/record/form-approvals.d.ts +12 -17
  262. package/src/lib/record/form-approvals.js +14 -8
  263. package/src/lib/record/form-approvals.js.map +1 -1
  264. package/src/lib/record/form-approvals.mjs +13 -7
  265. package/src/lib/record/form-approvals.mjs.map +1 -1
  266. package/src/lib/record/form-limits.d.mts +4 -28
  267. package/src/lib/record/form-limits.d.ts +4 -28
  268. package/src/lib/record/form-limits.js +8 -5
  269. package/src/lib/record/form-limits.js.map +1 -1
  270. package/src/lib/record/form-limits.mjs +8 -5
  271. package/src/lib/record/form-limits.mjs.map +1 -1
  272. package/src/lib/record/form.d.mts +115 -3
  273. package/src/lib/record/form.d.ts +115 -3
  274. package/src/lib/record/form.js +262 -65
  275. package/src/lib/record/form.js.map +1 -1
  276. package/src/lib/record/form.mjs +263 -66
  277. package/src/lib/record/form.mjs.map +1 -1
  278. package/src/lib/record/history-steps.d.mts +38 -0
  279. package/src/lib/record/history-steps.d.ts +38 -0
  280. package/src/lib/record/history-steps.js +63 -0
  281. package/src/lib/record/history-steps.js.map +1 -0
  282. package/src/lib/record/history-steps.mjs +58 -0
  283. package/src/lib/record/history-steps.mjs.map +1 -0
  284. package/src/lib/record/history.d.mts +16 -0
  285. package/src/lib/record/history.d.ts +16 -0
  286. package/src/lib/record/history.js +3 -0
  287. package/src/lib/record/history.js.map +1 -0
  288. package/src/lib/record/history.mjs +2 -0
  289. package/src/lib/record/history.mjs.map +1 -0
  290. package/src/lib/record/input-rules.d.mts +2 -0
  291. package/src/lib/record/input-rules.d.ts +2 -0
  292. package/src/lib/record/input-rules.js +4 -1
  293. package/src/lib/record/input-rules.js.map +1 -1
  294. package/src/lib/record/input-rules.mjs +2 -0
  295. package/src/lib/record/input-rules.mjs.map +1 -1
  296. package/src/lib/record/joins.d.mts +143 -0
  297. package/src/lib/record/joins.d.ts +143 -0
  298. package/src/lib/record/joins.js +28 -0
  299. package/src/lib/record/joins.js.map +1 -0
  300. package/src/lib/record/joins.mjs +24 -0
  301. package/src/lib/record/joins.mjs.map +1 -0
  302. package/src/lib/record/limits.js +7 -4
  303. package/src/lib/record/limits.js.map +1 -1
  304. package/src/lib/record/limits.mjs +8 -5
  305. package/src/lib/record/limits.mjs.map +1 -1
  306. package/src/lib/record/link-values.d.mts +31 -0
  307. package/src/lib/record/link-values.d.ts +31 -0
  308. package/src/lib/record/link-values.js +132 -0
  309. package/src/lib/record/link-values.js.map +1 -0
  310. package/src/lib/record/link-values.mjs +125 -0
  311. package/src/lib/record/link-values.mjs.map +1 -0
  312. package/src/lib/record/live-later.d.mts +15 -0
  313. package/src/lib/record/live-later.d.ts +15 -0
  314. package/src/lib/record/live-later.js +10 -0
  315. package/src/lib/record/live-later.js.map +1 -0
  316. package/src/lib/record/live-later.mjs +7 -0
  317. package/src/lib/record/live-later.mjs.map +1 -0
  318. package/src/lib/record/live-part.d.mts +6 -0
  319. package/src/lib/record/live-part.d.ts +6 -0
  320. package/src/lib/record/live-part.js +12 -0
  321. package/src/lib/record/live-part.js.map +1 -0
  322. package/src/lib/record/live-part.mjs +7 -0
  323. package/src/lib/record/live-part.mjs.map +1 -0
  324. package/src/lib/record/live.d.mts +8 -0
  325. package/src/lib/record/live.d.ts +8 -0
  326. package/src/lib/record/live.js +133 -0
  327. package/src/lib/record/live.js.map +1 -0
  328. package/src/lib/record/live.mjs +129 -0
  329. package/src/lib/record/live.mjs.map +1 -0
  330. package/src/lib/record/locales/all.d.mts +5 -5
  331. package/src/lib/record/locales/all.d.ts +5 -5
  332. package/src/lib/record/locales/all.js +3 -3
  333. package/src/lib/record/locales/all.js.map +1 -1
  334. package/src/lib/record/locales/all.mjs +3 -3
  335. package/src/lib/record/locales/all.mjs.map +1 -1
  336. package/src/lib/record/locales/ar.js +8 -0
  337. package/src/lib/record/locales/ar.js.map +1 -1
  338. package/src/lib/record/locales/ar.mjs +8 -0
  339. package/src/lib/record/locales/ar.mjs.map +1 -1
  340. package/src/lib/record/locales/de.js +8 -0
  341. package/src/lib/record/locales/de.js.map +1 -1
  342. package/src/lib/record/locales/de.mjs +8 -0
  343. package/src/lib/record/locales/de.mjs.map +1 -1
  344. package/src/lib/record/locales/es.d.mts +8 -0
  345. package/src/lib/record/locales/es.d.ts +8 -0
  346. package/src/lib/record/locales/es.js +111 -0
  347. package/src/lib/record/locales/es.js.map +1 -0
  348. package/src/lib/record/locales/es.mjs +108 -0
  349. package/src/lib/record/locales/es.mjs.map +1 -0
  350. package/src/lib/record/locales/fa.d.mts +5 -0
  351. package/src/lib/record/locales/fa.d.ts +5 -0
  352. package/src/lib/record/locales/fa.js +117 -0
  353. package/src/lib/record/locales/fa.js.map +1 -0
  354. package/src/lib/record/locales/fa.mjs +114 -0
  355. package/src/lib/record/locales/fa.mjs.map +1 -0
  356. package/src/lib/record/locales/fr.js +8 -0
  357. package/src/lib/record/locales/fr.js.map +1 -1
  358. package/src/lib/record/locales/fr.mjs +8 -0
  359. package/src/lib/record/locales/fr.mjs.map +1 -1
  360. package/src/lib/record/locales/later.d.mts +8 -0
  361. package/src/lib/record/locales/later.d.ts +8 -0
  362. package/src/lib/record/locales/later.js +16 -0
  363. package/src/lib/record/locales/later.js.map +1 -0
  364. package/src/lib/record/locales/later.mjs +13 -0
  365. package/src/lib/record/locales/later.mjs.map +1 -0
  366. package/src/lib/record/locales/tr.d.mts +6 -0
  367. package/src/lib/record/locales/tr.d.ts +6 -0
  368. package/src/lib/record/locales/tr.js +118 -0
  369. package/src/lib/record/locales/tr.js.map +1 -0
  370. package/src/lib/record/locales/tr.mjs +115 -0
  371. package/src/lib/record/locales/tr.mjs.map +1 -0
  372. package/src/lib/record/locales/ur.d.mts +9 -0
  373. package/src/lib/record/locales/ur.d.ts +9 -0
  374. package/src/lib/record/locales/ur.js +118 -0
  375. package/src/lib/record/locales/ur.js.map +1 -0
  376. package/src/lib/record/locales/ur.mjs +115 -0
  377. package/src/lib/record/locales/ur.mjs.map +1 -0
  378. package/src/lib/record/memory-codes.d.mts +37 -0
  379. package/src/lib/record/memory-codes.d.ts +37 -0
  380. package/src/lib/record/memory-codes.js +50 -0
  381. package/src/lib/record/memory-codes.js.map +1 -0
  382. package/src/lib/record/memory-codes.mjs +47 -0
  383. package/src/lib/record/memory-codes.mjs.map +1 -0
  384. package/src/lib/record/memory-data-source.d.mts +56 -2
  385. package/src/lib/record/memory-data-source.d.ts +56 -2
  386. package/src/lib/record/memory-data-source.js +67 -1
  387. package/src/lib/record/memory-data-source.js.map +1 -1
  388. package/src/lib/record/memory-data-source.mjs +67 -1
  389. package/src/lib/record/memory-data-source.mjs.map +1 -1
  390. package/src/lib/record/memory-kept.d.mts +3 -1
  391. package/src/lib/record/memory-kept.d.ts +3 -1
  392. package/src/lib/record/memory-kept.js +7 -2
  393. package/src/lib/record/memory-kept.js.map +1 -1
  394. package/src/lib/record/memory-kept.mjs +7 -2
  395. package/src/lib/record/memory-kept.mjs.map +1 -1
  396. package/src/lib/record/memory-later.d.mts +3 -1
  397. package/src/lib/record/memory-later.d.ts +3 -1
  398. package/src/lib/record/memory-later.js +9 -5
  399. package/src/lib/record/memory-later.js.map +1 -1
  400. package/src/lib/record/memory-later.mjs +8 -4
  401. package/src/lib/record/memory-later.mjs.map +1 -1
  402. package/src/lib/record/memory-live.d.mts +8 -0
  403. package/src/lib/record/memory-live.d.ts +8 -0
  404. package/src/lib/record/memory-live.js +70 -0
  405. package/src/lib/record/memory-live.js.map +1 -0
  406. package/src/lib/record/memory-live.mjs +67 -0
  407. package/src/lib/record/memory-live.mjs.map +1 -0
  408. package/src/lib/record/memory-views.d.mts +11 -0
  409. package/src/lib/record/memory-views.d.ts +11 -0
  410. package/src/lib/record/memory-views.js +60 -0
  411. package/src/lib/record/memory-views.js.map +1 -0
  412. package/src/lib/record/memory-views.mjs +57 -0
  413. package/src/lib/record/memory-views.mjs.map +1 -0
  414. package/src/lib/record/messages.d.mts +21 -3
  415. package/src/lib/record/messages.d.ts +21 -3
  416. package/src/lib/record/messages.js +15 -2
  417. package/src/lib/record/messages.js.map +1 -1
  418. package/src/lib/record/messages.mjs +15 -2
  419. package/src/lib/record/messages.mjs.map +1 -1
  420. package/src/lib/record/order.d.mts +21 -0
  421. package/src/lib/record/order.d.ts +21 -0
  422. package/src/lib/record/order.js +129 -0
  423. package/src/lib/record/order.js.map +1 -0
  424. package/src/lib/record/order.mjs +122 -0
  425. package/src/lib/record/order.mjs.map +1 -0
  426. package/src/lib/record/outbox-form.d.mts +79 -0
  427. package/src/lib/record/outbox-form.d.ts +79 -0
  428. package/src/lib/record/outbox-form.js +291 -0
  429. package/src/lib/record/outbox-form.js.map +1 -0
  430. package/src/lib/record/outbox-form.mjs +286 -0
  431. package/src/lib/record/outbox-form.mjs.map +1 -0
  432. package/src/lib/record/outbox-part.d.mts +6 -0
  433. package/src/lib/record/outbox-part.d.ts +6 -0
  434. package/src/lib/record/outbox-part.js +13 -0
  435. package/src/lib/record/outbox-part.js.map +1 -0
  436. package/src/lib/record/outbox-part.mjs +7 -0
  437. package/src/lib/record/outbox-part.mjs.map +1 -0
  438. package/src/lib/record/pay-part.d.mts +6 -0
  439. package/src/lib/record/pay-part.d.ts +6 -0
  440. package/src/lib/record/pay-part.js +11 -0
  441. package/src/lib/record/pay-part.js.map +1 -0
  442. package/src/lib/record/pay-part.mjs +7 -0
  443. package/src/lib/record/pay-part.mjs.map +1 -0
  444. package/src/lib/record/payment-form.d.mts +25 -0
  445. package/src/lib/record/payment-form.d.ts +25 -0
  446. package/src/lib/record/payment-form.js +98 -0
  447. package/src/lib/record/payment-form.js.map +1 -0
  448. package/src/lib/record/payment-form.mjs +95 -0
  449. package/src/lib/record/payment-form.mjs.map +1 -0
  450. package/src/lib/record/prefill-part.d.mts +7 -0
  451. package/src/lib/record/prefill-part.d.ts +7 -0
  452. package/src/lib/record/prefill-part.js +15 -0
  453. package/src/lib/record/prefill-part.js.map +1 -0
  454. package/src/lib/record/prefill-part.mjs +8 -0
  455. package/src/lib/record/prefill-part.mjs.map +1 -0
  456. package/src/lib/record/press-undo.d.mts +42 -0
  457. package/src/lib/record/press-undo.d.ts +42 -0
  458. package/src/lib/record/press-undo.js +115 -0
  459. package/src/lib/record/press-undo.js.map +1 -0
  460. package/src/lib/record/press-undo.mjs +110 -0
  461. package/src/lib/record/press-undo.mjs.map +1 -0
  462. package/src/lib/record/quiz.d.mts +36 -0
  463. package/src/lib/record/quiz.d.ts +36 -0
  464. package/src/lib/record/quiz.js +90 -0
  465. package/src/lib/record/quiz.js.map +1 -0
  466. package/src/lib/record/quiz.mjs +87 -0
  467. package/src/lib/record/quiz.mjs.map +1 -0
  468. package/src/lib/record/record-words.js +1 -1
  469. package/src/lib/record/record-words.js.map +1 -1
  470. package/src/lib/record/record-words.mjs +1 -1
  471. package/src/lib/record/record-words.mjs.map +1 -1
  472. package/src/lib/record/run.d.mts +54 -2
  473. package/src/lib/record/run.d.ts +54 -2
  474. package/src/lib/record/run.js +30 -6
  475. package/src/lib/record/run.js.map +1 -1
  476. package/src/lib/record/run.mjs +28 -5
  477. package/src/lib/record/run.mjs.map +1 -1
  478. package/src/lib/record/survey-kit.d.mts +37 -0
  479. package/src/lib/record/survey-kit.d.ts +37 -0
  480. package/src/lib/record/survey-kit.js +99 -0
  481. package/src/lib/record/survey-kit.js.map +1 -0
  482. package/src/lib/record/survey-kit.mjs +96 -0
  483. package/src/lib/record/survey-kit.mjs.map +1 -0
  484. package/src/lib/record/survey.d.mts +16 -0
  485. package/src/lib/record/survey.d.ts +16 -0
  486. package/src/lib/record/survey.js +17 -0
  487. package/src/lib/record/survey.js.map +1 -0
  488. package/src/lib/record/survey.mjs +13 -0
  489. package/src/lib/record/survey.mjs.map +1 -0
  490. package/src/lib/record/surveyflow-part.d.mts +9 -0
  491. package/src/lib/record/surveyflow-part.d.ts +9 -0
  492. package/src/lib/record/surveyflow-part.js +20 -0
  493. package/src/lib/record/surveyflow-part.js.map +1 -0
  494. package/src/lib/record/surveyflow-part.mjs +10 -0
  495. package/src/lib/record/surveyflow-part.mjs.map +1 -0
  496. package/src/lib/record/timing.d.mts +76 -0
  497. package/src/lib/record/timing.d.ts +76 -0
  498. package/src/lib/record/timing.js +112 -0
  499. package/src/lib/record/timing.js.map +1 -0
  500. package/src/lib/record/timing.mjs +104 -0
  501. package/src/lib/record/timing.mjs.map +1 -0
  502. package/src/lib/record/undo-later.d.mts +9 -0
  503. package/src/lib/record/undo-later.d.ts +9 -0
  504. package/src/lib/record/undo-later.js +10 -0
  505. package/src/lib/record/undo-later.js.map +1 -0
  506. package/src/lib/record/undo-later.mjs +7 -0
  507. package/src/lib/record/undo-later.mjs.map +1 -0
  508. package/src/lib/record/undo-part.d.mts +8 -0
  509. package/src/lib/record/undo-part.d.ts +8 -0
  510. package/src/lib/record/undo-part.js +17 -0
  511. package/src/lib/record/undo-part.js.map +1 -0
  512. package/src/lib/record/undo-part.mjs +9 -0
  513. package/src/lib/record/undo-part.mjs.map +1 -0
  514. package/src/lib/record/upload-to-later.d.mts +10 -0
  515. package/src/lib/record/upload-to-later.d.ts +10 -0
  516. package/src/lib/record/upload-to-later.js +15 -0
  517. package/src/lib/record/upload-to-later.js.map +1 -0
  518. package/src/lib/record/upload-to-later.mjs +12 -0
  519. package/src/lib/record/upload-to-later.mjs.map +1 -0
  520. package/src/lib/record/uploads.d.mts +5 -0
  521. package/src/lib/record/uploads.d.ts +5 -0
  522. package/src/lib/record/uploads.js.map +1 -1
  523. package/src/lib/record/uploads.mjs.map +1 -1
  524. package/src/lib/record/values.d.mts +6 -0
  525. package/src/lib/record/values.d.ts +6 -0
  526. package/src/lib/record/values.js.map +1 -1
  527. package/src/lib/record/values.mjs.map +1 -1
  528. package/src/lib/record/verify-form.d.mts +24 -0
  529. package/src/lib/record/verify-form.d.ts +24 -0
  530. package/src/lib/record/verify-form.js +65 -0
  531. package/src/lib/record/verify-form.js.map +1 -0
  532. package/src/lib/record/verify-form.mjs +62 -0
  533. package/src/lib/record/verify-form.mjs.map +1 -0
  534. package/src/lib/record/verify-part.d.mts +6 -0
  535. package/src/lib/record/verify-part.d.ts +6 -0
  536. package/src/lib/record/verify-part.js +11 -0
  537. package/src/lib/record/verify-part.js.map +1 -0
  538. package/src/lib/record/verify-part.mjs +7 -0
  539. package/src/lib/record/verify-part.mjs.map +1 -0
  540. package/src/lib/record/words-chunks.d.mts +37 -0
  541. package/src/lib/record/words-chunks.d.ts +37 -0
  542. package/src/lib/record/words-chunks.js +94 -0
  543. package/src/lib/record/words-chunks.js.map +1 -0
  544. package/src/lib/record/words-chunks.mjs +86 -0
  545. package/src/lib/record/words-chunks.mjs.map +1 -0
  546. package/src/runtime.d.mts +28 -6
  547. package/src/runtime.d.ts +28 -6
  548. package/src/runtime.js +35 -6
  549. package/src/runtime.js.map +1 -1
  550. package/src/runtime.mjs +15 -3
  551. package/src/runtime.mjs.map +1 -1
@@ -22,13 +22,14 @@ import { askKey, cellOf, listedColumns, notOffered } from './line-choices.mjs';
22
22
  import { loadedRecordOf, saveProblemOf, } from './data-source.mjs';
23
23
  import { formLimitsPart, hasLimits } from './taking.mjs';
24
24
  import { formLanguagesPart, hasLanguages } from './languages.mjs';
25
- import { formApprovalsPart } from './approving.mjs';
26
25
  import { draftRead, resumeStep } from './drafts.mjs';
27
26
  import { waitingOn, workedOutFrom } from './waits.mjs';
28
27
  import { createUploads, fileBytesIn, uploadGoing, withoutFileBytes } from './uploads.mjs';
29
28
  import { hostScheduler } from './scheduler.mjs';
30
29
  import { signedBy } from './signed.mjs';
31
- import { createMoments, createRunner } from './run.mjs';
30
+ import { formJoins } from './joins.mjs';
31
+ import { pressUndoPart } from './undo-later.mjs';
32
+ import { createMoments, createRunner, takenNow } from './run.mjs';
32
33
  import { emptyValue, initialValues, isEmpty, lineKind, structuredCopy, } from './values.mjs';
33
34
  /**
34
35
  * Whether leaving the record now would lose the person's work: changes not
@@ -89,7 +90,9 @@ function innerForm(options, within, place, outer) {
89
90
  // A field's label small inside a message's sentence where the page asks (`lowerLabels`).
90
91
  // …and a day written in the page's order where it says one (`look.dateOrder`): "can't be after Oct 20, 2026".
91
92
  const dateOrder = page.look?.dateOrder;
92
- const messages = page.lowerLabels || dateOrder ? { ...(options.messages ?? MESSAGES.en), lowerLabels: page.lowerLabels, dateOrder } : (options.messages ?? MESSAGES.en);
93
+ // The page's ask over the language's messages, each read through to them as it is said: a language whose words come in a
94
+ // chunk of their own fills its table in place once they have come (words-chunks.ts).
95
+ const messages = page.lowerLabels || dateOrder ? Object.assign(Object.create(options.messages ?? MESSAGES.en), { lowerLabels: page.lowerLabels, dateOrder }) : (options.messages ?? MESSAGES.en);
93
96
  /** One of the form's sentences in the words the page names its records by (`records`), else as the language says it plainly. */
94
97
  const named = (noun, plain) => recordSentence(page.records, noun, plain, messages.locale);
95
98
  /** The app's functions the page's expressions call: a name none may call is refused now. */
@@ -186,7 +189,23 @@ function innerForm(options, within, place, outer) {
186
189
  placeholderOf('#title', page.layout.title);
187
190
  /** Whether any rule only warns: without one, there are never warnings to look for. */
188
191
  const warns = [...nodeRules.values()].some((rules) => rules.some((compiled) => compiled.rule.level === 'warning'));
189
- const steps = page.layout.type === 'wizard' ? page.layout.children.map((step) => step.id) : [];
192
+ const layoutSteps = page.layout.type === 'wizard' ? page.layout.children.map((step) => step.id) : [];
193
+ /** The wizard's steps in the order shown: the page's, or the order drawn for this respondent (`state.order`). */
194
+ let steps = layoutSteps;
195
+ /**
196
+ * The parts that join this form where its page or its source asks (`formJoins`: an offline outbox, the fields its link
197
+ * fills, proof by a code, a payment, a survey's flow, a record's approvals, the record made live), each made as it
198
+ * comes — at once where it has — and kept in their order: holes until then. While one has not come, a send waits for it
199
+ * (`joining`); a draft laid before one came is laid on it as it does (`laid`).
200
+ */
201
+ const joins = [];
202
+ const joinParts = formJoins(page, options).filter((part) => !outer || part.name !== 'approvals');
203
+ let joining;
204
+ let laid = null;
205
+ /** A part's side of this form, by its name, once it came. */
206
+ const joined = (name) => (joins[joinParts.findIndex((part) => part.name === name)] ?? null);
207
+ /** Whether a part that joins the form says so of a part of the page, or of a field. */
208
+ const anyJoin = (says) => joins.some((join) => join && says(join));
190
209
  const storeKey = () => `fieldia:draft:${page.id}:${state.recordId ?? 'new'}`;
191
210
  /** Today, on the clock of the page's time zone (the browser's own when it names none). */
192
211
  const today = () => dayIn(options.now?.() ?? new Date(), page.timeZone);
@@ -364,6 +383,9 @@ function innerForm(options, within, place, outer) {
364
383
  limits: null,
365
384
  resume: null,
366
385
  approval: null,
386
+ order: null,
387
+ timers: {},
388
+ timeUp: [],
367
389
  };
368
390
  started = true;
369
391
  /**
@@ -456,6 +478,8 @@ function innerForm(options, within, place, outer) {
456
478
  filesChanged();
457
479
  },
458
480
  track,
481
+ // Through the parts that join the form: a photo taken without signal kept as its bytes, on a page with an outbox.
482
+ through,
459
483
  });
460
484
  /**
461
485
  * Files on their way changed, here or in a saved form placed here: the
@@ -553,6 +577,87 @@ function innerForm(options, within, place, outer) {
553
577
  const listed = listedColumns(page.fields);
554
578
  /** Whether the values set now are a change someone made (`writeValues`), after which a line's choice no longer offered is cleared. */
555
579
  let edit = false;
580
+ /**
581
+ * The one kit the parts that work inside the form are given of it (joins.ts): the parts that join it, its limits, the
582
+ * undo part's press.
583
+ */
584
+ const inner = {
585
+ get form() {
586
+ return form;
587
+ },
588
+ page,
589
+ fields,
590
+ options,
591
+ messages,
592
+ scheduler,
593
+ state: () => state,
594
+ set,
595
+ reading,
596
+ sending: () => (page.data.kind === 'responses' ? answersSent() : sentValues()),
597
+ baseline: () => baseline,
598
+ same: (name, one, other) => sameValue(fields[name], one, other),
599
+ conflictOf,
600
+ store,
601
+ settled: savesSettled,
602
+ track,
603
+ shownNow,
604
+ generation: () => generation,
605
+ emit,
606
+ replaced,
607
+ clock,
608
+ started: () => started,
609
+ listened: () => listeners.size > 0,
610
+ disposed: () => disposed,
611
+ keep: () => void ((options.drafts || onServer()) && scheduleDraft()),
612
+ order: (list) => void (steps = list ?? layoutSteps),
613
+ refuse(name, message) {
614
+ // Said until the field changes from what it holds, as a refused save's words are.
615
+ serverErrors[name] = { message, value: JSON.stringify(state.values[name] ?? null) };
616
+ set({ errors: { ...state.errors, [name]: message } });
617
+ },
618
+ reload: () => load(true),
619
+ };
620
+ /** The person's changes to the record shown, as made (history.ts); and whether one is being taken back or made again now. */
621
+ let noted = [];
622
+ let replaying = false;
623
+ /**
624
+ * A person's change written to `field` (on `line`), noted for Undo with what it held `before`: typing on joins it
625
+ * (`joins`). The undo part takes them as they come, asked for with the first: in by the time the person presses Ctrl+Z.
626
+ */
627
+ const noteChange = (field, line, before, joins) => {
628
+ if (replaying)
629
+ return;
630
+ noted.push([field, line, before ?? null, state.values[field] ?? null, joins]);
631
+ const part = pressUndoPart.loaded;
632
+ if (part)
633
+ part.takeNoted(noted, inner.same);
634
+ // Kept while it comes; where it cannot, the oldest let go.
635
+ else if (noted.length > 500)
636
+ noted.shift();
637
+ else
638
+ pressUndoPart.load().catch(() => undefined);
639
+ };
640
+ /**
641
+ * Undo (`back`) or Redo, once the undo part has come: the change taken, written as the person's own change back —
642
+ * the page's `change` steps and the source's onchange run as for any — once its field still holds what the change
643
+ * left and may be changed.
644
+ */
645
+ function replay(back) {
646
+ const step = editing ? pressUndoPart.loaded?.takeStep(noted, inner.same, back, (one, left) => !form.fieldReadonly(one.field) && sameValue(fields[one.field], state.values[one.field], left)) : null;
647
+ if (!step)
648
+ return null;
649
+ const { value, ...taken } = step;
650
+ replaying = true;
651
+ try {
652
+ writeValues({ ...state.values, [taken.field]: structuredCopy(value) });
653
+ afterEdit(taken.field);
654
+ changed(taken.field, 'person', taken.line === undefined ? {} : { line: taken.line });
655
+ }
656
+ finally {
657
+ replaying = false;
658
+ }
659
+ return taken;
660
+ }
556
661
  function set(patch) {
557
662
  const was = state.values;
558
663
  const choicesWas = state.choices;
@@ -586,6 +691,10 @@ function innerForm(options, within, place, outer) {
586
691
  part.inner.show(inside(state.errors, part.name));
587
692
  for (const listener of [...listeners])
588
693
  listener(state);
694
+ // What the parts that join the form follow of it: the record shown, watched for changes made elsewhere and for who
695
+ // else has it open, asked again as that changes.
696
+ for (const join of joins)
697
+ join?.changed?.();
589
698
  // Choices that change with a value that just changed are asked for again.
590
699
  if (state.values !== was) {
591
700
  // After someone's change, a choice its new choices do not offer is taken out and said, as a line's is.
@@ -625,6 +734,7 @@ function innerForm(options, within, place, outer) {
625
734
  /** Another record is shown: nothing of the last one's is the person's work any more, and its files on their way stop. */
626
735
  function newGeneration() {
627
736
  generation++;
737
+ noted = [];
628
738
  // Another record's lines ask the source anew.
629
739
  lineAsks = new Map();
630
740
  answers = new Map();
@@ -737,6 +847,9 @@ function innerForm(options, within, place, outer) {
737
847
  return;
738
848
  told = step;
739
849
  emit('step', { step });
850
+ // A step with a time limit: its clock starts as it is first entered.
851
+ for (const join of joins)
852
+ join?.entered?.(step);
740
853
  const list = page.on?.show?.[step];
741
854
  if (list)
742
855
  void track(moment(`on.show.${step}`, list, chain, true));
@@ -1108,13 +1221,13 @@ function innerForm(options, within, place, outer) {
1108
1221
  if (!node)
1109
1222
  throw new Error(`The page has no element "${id}"`);
1110
1223
  const { context: ctx, env } = reading();
1111
- const hidden = (at) => (at ? !rolesAllow(at.roles, userRoles) || at.invisible.evaluate(ctx, env) : false);
1224
+ const hidden = (at) => (at ? anyJoin((join) => join.hides?.(at.id)) || !rolesAllow(at.roles, userRoles) || at.invisible.evaluate(ctx, env) : false);
1112
1225
  let invisible = hidden(node);
1113
1226
  for (let parent = node.parent; !invisible && parent; parent = index.get(parent)?.parent ?? null) {
1114
1227
  invisible = hidden(index.get(parent));
1115
1228
  }
1116
- // A section read-only right now makes everything inside it read-only too.
1117
- let readonly = node.readonly.evaluate(ctx, env);
1229
+ // A section read-only right now makes everything inside it read-only too; a page's, or a step's, time run out too.
1230
+ let readonly = node.readonly.evaluate(ctx, env) || anyJoin((join) => join.locks?.(id));
1118
1231
  for (let parent = node.parent; !readonly && parent; parent = index.get(parent)?.parent ?? null) {
1119
1232
  readonly = index.get(parent)?.readonly.evaluate(ctx, env) ?? false;
1120
1233
  }
@@ -1132,7 +1245,7 @@ function innerForm(options, within, place, outer) {
1132
1245
  if (answer !== undefined && signedNow().has(answer))
1133
1246
  readonly = true;
1134
1247
  // A record waiting for approval: locked but for the fields the page leaves open (the approvals part).
1135
- if (answer !== undefined && approvalKit?.locks(answer))
1248
+ if (answer !== undefined && anyJoin((join) => join.locksField?.(answer)))
1136
1249
  readonly = true;
1137
1250
  return {
1138
1251
  invisible,
@@ -1274,6 +1387,9 @@ function innerForm(options, within, place, outer) {
1274
1387
  const errors = {};
1275
1388
  const seen = new Set();
1276
1389
  for (const node of visibleFieldNodes(root)) {
1390
+ // The answers of a time run out go as they stood: their checks refuse nothing.
1391
+ if (anyJoin((join) => join.locks?.(node.id)))
1392
+ continue;
1277
1393
  const name = node.field;
1278
1394
  seen.add(name);
1279
1395
  const end = rangeEnd(node);
@@ -1582,6 +1698,7 @@ function innerForm(options, within, place, outer) {
1582
1698
  return {
1583
1699
  values,
1584
1700
  ...(steps.length ? { step: state.step, skipped: [...state.skipped] } : {}),
1701
+ ...Object.assign({}, ...joins.map((join) => join?.kept?.())),
1585
1702
  ...(Object.keys(state.unread).length ? { unread: structuredCopy(state.unread) } : {}),
1586
1703
  answered: Object.keys(fields).filter((name) => state.dirty.includes(name)),
1587
1704
  savedAt: new Date(clock()).toISOString(),
@@ -1637,6 +1754,10 @@ function innerForm(options, within, place, outer) {
1637
1754
  /** A draft laid on the form: its answers as a starting point, its wizard step (or the nearest before it that applies), its text that does not read. */
1638
1755
  function applyDraft(draft) {
1639
1756
  found = null;
1757
+ // Its draw and its clocks, by the survey flow's part: now, or once it came.
1758
+ laid = draft;
1759
+ for (const join of joins)
1760
+ join?.lay?.(draft);
1640
1761
  writeValues(withDraft(draft.values ?? {}), { draft: null }, true);
1641
1762
  const known = (draft.skipped ?? []).filter((id) => steps.includes(id));
1642
1763
  // Text typed that did not read stays as typed, said as it was, where its field is still on the page.
@@ -1731,27 +1852,8 @@ function innerForm(options, within, place, outer) {
1731
1852
  function limitsMade(part) {
1732
1853
  if (limitsKit || disposed)
1733
1854
  return limitsKit;
1734
- const kit = part.formLimits({
1735
- page,
1736
- dataSource: options.dataSource,
1737
- user: options.user,
1738
- messages,
1739
- scheduler,
1740
- state: () => state,
1741
- set,
1742
- clock,
1743
- started: () => started,
1744
- shownNow,
1745
- generation: () => generation,
1746
- replaced: () => named(messages.recordReplacedNoun, messages.recordReplaced),
1747
- listened: () => listeners.size > 0,
1748
- disposed: () => disposed,
1749
- refuse(name, message) {
1750
- // Said until the field changes from what it holds, as a refused save's words are.
1751
- serverErrors[name] = { message, value: JSON.stringify(state.values[name] ?? null) };
1752
- set({ errors: { ...state.errors, [name]: message } });
1753
- },
1754
- });
1855
+ // The one kit, made at the limits' own moment: once the form knows it keeps them (joins.ts).
1856
+ const kit = part.formLimits(inner);
1755
1857
  limitsKit = kit;
1756
1858
  kit.show();
1757
1859
  if (kit.asks() && (state.recordId == null || loadedOnce))
@@ -1772,15 +1874,6 @@ function innerForm(options, within, place, outer) {
1772
1874
  }
1773
1875
  /** A choice of a full option, picked by the person, refused under its field: the field keeps what it held. */
1774
1876
  const refusedFull = (name, value) => !!limitsKit?.refusedFull(name, value);
1775
- // ---- a record's approvals (the approvals part: form-approvals.ts) ---------------------------------------------------
1776
- /** The approvals made for this form from the approvals part, once it came: none before, nor on a page without them. */
1777
- let approvalKit = null;
1778
- /** Whether the form asks for approvals: a page with `approval` (a record's own, as its check says), its source telling them. */
1779
- const keepsApprovals = !outer && !!page.approval && !!options.dataSource?.approvals;
1780
- /** The approvals, made from their part once it came; null without them, or where it could not come. */
1781
- const approvalsCode = () => keepsApprovals ? formApprovalsPart.load().then((part) => (approvalKit ??= part.formApprovals({ form, dataSource: options.dataSource, messages, set, reload: () => load(true) })), () => null) : Promise.resolve(null);
1782
- /** Ask the record shown's approvals, once their part came; nothing on a page without them. */
1783
- const askApprovals = () => approvalsCode().then((kit) => kit?.ask());
1784
1877
  function scheduleAutosave() {
1785
1878
  const autosave = options.autosave;
1786
1879
  // A record the person may not change is never saved unasked: it would only be refused.
@@ -1857,9 +1950,24 @@ function innerForm(options, within, place, outer) {
1857
1950
  }
1858
1951
  return shown;
1859
1952
  }
1953
+ /** A response's answers as they go: those shown, and what the parts that join the form add (a link's hidden fields). */
1954
+ function answersSent() {
1955
+ const values = shownValues();
1956
+ for (const join of joins)
1957
+ join.values?.(values);
1958
+ return values;
1959
+ }
1860
1960
  async function save(chain = [], yes) {
1861
1961
  return (await saving(chain, yes)) === null;
1862
1962
  }
1963
+ /** A send — a response's, a save's, an upload's — through the parts that join the form, each around the next. Null: kept, not sent. */
1964
+ function through(kind, sent, send) {
1965
+ return joins.reduce((go, join) => (join.around ? (one) => join.around(kind, one, go) : go), send)(sent);
1966
+ }
1967
+ /** Started again, or another record opened: the parts that join the form let go of what the one before had. */
1968
+ const joinsReset = () => joins.forEach((join) => join.reset?.());
1969
+ /** The record loaded, or saved: what the parts that join the form ask again (a record's approvals). */
1970
+ const joinsLoaded = () => joins.forEach((join) => join.loaded?.());
1863
1971
  /** Every value, for a source that saves whole records: those of fields hidden from the person, which they never had, left out. */
1864
1972
  function sentValues() {
1865
1973
  const values = structuredCopy(state.values);
@@ -1967,8 +2075,22 @@ function innerForm(options, within, place, outer) {
1967
2075
  const refused = await stillTaking(asked);
1968
2076
  if (refused)
1969
2077
  return refused;
1970
- // A file that did not reach the store stops it, under its field, with the form's own checks.
2078
+ // The parts that join the form (`formJoins`), waited for where they have not come.
2079
+ if (joining) {
2080
+ await joining;
2081
+ if (asked !== generation)
2082
+ return elsewhere();
2083
+ }
2084
+ // A file that did not reach the store stops it, under its field, with the form's own checks — and what the parts that
2085
+ // join the form find: an email or a phone not proved by a code; a payment not taken sends nothing.
1971
2086
  const errors = { ...collectErrors(), ...fileProblems() };
2087
+ for (const join of joins) {
2088
+ // A part that never came (a hole) has nothing to say.
2089
+ if ((await join?.before?.(errors)) === false)
2090
+ return { reason: 'save' };
2091
+ if (asked !== generation)
2092
+ return elsewhere();
2093
+ }
1972
2094
  if (Object.keys(errors).length) {
1973
2095
  set({ errors, ...unstored(errors) });
1974
2096
  return { reason: 'save', message: Object.values(errors)[0] };
@@ -1998,13 +2120,18 @@ function innerForm(options, within, place, outer) {
1998
2120
  if (page.data.kind === 'responses') {
1999
2121
  if (!source?.submit)
2000
2122
  throw new Error('This page has no data source to send its answers to');
2001
- const sent = shownValues();
2123
+ const sent = answersSent();
2002
2124
  const before = state.values;
2003
2125
  at = generation;
2004
2126
  const resetAt = resets;
2005
2127
  const seen = botReport();
2128
+ // A quiz's marking, in before the answers go: what the person sees of their results.
2006
2129
  // What the source answered: the id it gave the response, and the values it added — what an ending reads.
2007
- const answer = (await source.submit({ pageId: page.id, values: sent, ...(seen ? { bot: seen } : {}), ...draft })) ?? {};
2130
+ const request = { pageId: page.id, values: sent, ...(seen ? { bot: seen } : {}), ...draft };
2131
+ // Through the parts that join the form: kept on the device where an outbox finds no signal (null), the form going
2132
+ // on as though it went.
2133
+ const got = await through('submit', request, (one) => source.submit(one));
2134
+ const answer = got ?? {};
2008
2135
  // The captcha's token is spent once sent: the next send waits for a new one.
2009
2136
  if (seen?.captcha)
2010
2137
  bot.captcha = null;
@@ -2016,8 +2143,13 @@ function innerForm(options, within, place, outer) {
2016
2143
  sentAs = before;
2017
2144
  const added = answer.values && typeof answer.values === 'object' ? structuredCopy(answer.values) : {};
2018
2145
  // Started again meanwhile (`reset`): a new response is on screen, and no ending is the old one's.
2019
- set({ status: 'saved', unsent: unsentNow(), ...(resets === resetAt ? { sent: { id: answer.id ?? null, values: { ...structuredCopy(sent), ...added } } } : {}) });
2146
+ // A quiz's results: the source's marking, else the page's, on the answers as sent.
2147
+ const quiz = answer.quiz ?? joined('surveyflow')?.marked(sent);
2148
+ set({ status: 'saved', unsent: unsentNow(), ...(resets === resetAt ? { sent: { id: answer.id ?? null, values: { ...structuredCopy(sent), ...added }, ...(quiz ? { quiz } : {}) } } : {}) });
2020
2149
  emit('send', { values: sent, ...(seen ? { bot: seen } : {}) });
2150
+ // Kept, not sent: its counts and its after-save steps wait for a send that went.
2151
+ if (got === null)
2152
+ return null;
2021
2153
  // Counted again: one response fewer to take, this person's sent.
2022
2154
  if (asksLimits())
2023
2155
  void track(askLimits());
@@ -2030,7 +2162,7 @@ function innerForm(options, within, place, outer) {
2030
2162
  const sent = state.values;
2031
2163
  at = generation;
2032
2164
  const resetAt = resets;
2033
- const result = await source.save({
2165
+ const request = {
2034
2166
  model: page.data.model,
2035
2167
  id: state.recordId,
2036
2168
  fields,
@@ -2039,7 +2171,10 @@ function innerForm(options, within, place, outer) {
2039
2171
  // The version it was opened at, for a source that refuses a save over someone else's.
2040
2172
  ...(state.recordId != null && state.version ? { version: state.version } : {}),
2041
2173
  ...draft,
2042
- });
2174
+ };
2175
+ const got = await through('save', request, (one) => source.save(one));
2176
+ // Kept on the device (null): the record as it was, its version too.
2177
+ const result = got ?? { id: state.recordId, version: state.version ?? undefined };
2043
2178
  // Answered for a record no longer shown — another opened, or this one loaded again: the source has it, and the
2044
2179
  // form lays nothing of it over the record it shows now.
2045
2180
  if (at !== generation)
@@ -2058,11 +2193,11 @@ function innerForm(options, within, place, outer) {
2058
2193
  scheduleAutosave();
2059
2194
  }
2060
2195
  // A save may have sent it for approval, or moved it on: its approvals asked again.
2061
- if (keepsApprovals)
2062
- void track(askApprovals());
2196
+ joinsLoaded();
2063
2197
  // The record as this save left it: a change made while it was on its way is no value of it.
2064
2198
  emit('save', { recordId: state.recordId, values: baseline });
2065
- void track(moment('on.afterSave', page.on?.afterSave, chain, false));
2199
+ if (got)
2200
+ void track(moment('on.afterSave', page.on?.afterSave, chain, false));
2066
2201
  return null;
2067
2202
  }
2068
2203
  catch (error) {
@@ -2168,7 +2303,8 @@ function innerForm(options, within, place, outer) {
2168
2303
  }
2169
2304
  function addLine(field, values, by) {
2170
2305
  const def = lineField(field);
2171
- const current = state.values[field] ?? [];
2306
+ const before = state.values[field];
2307
+ const current = before ?? [];
2172
2308
  // A key no line holds: lines given (a line's own lines, back from its form) may hold new-… keys already.
2173
2309
  let key = `new-${++lineKeys}`;
2174
2310
  while (current.some((line) => line.key === key))
@@ -2181,6 +2317,8 @@ function innerForm(options, within, place, outer) {
2181
2317
  }
2182
2318
  const lines = [...current, line];
2183
2319
  writeValues({ ...state.values, [field]: lines });
2320
+ if (by === 'person')
2321
+ noteChange(field, key, before, false);
2184
2322
  afterEdit(field);
2185
2323
  changed(field, by, by === 'person' ? { line: key } : { keep: true });
2186
2324
  return key;
@@ -2325,8 +2463,7 @@ function innerForm(options, within, place, outer) {
2325
2463
  if (asksLimits())
2326
2464
  void track(askLimits());
2327
2465
  // Its approvals: where it stands, what was decided, what this person may decide — its fields locked while it waits.
2328
- if (keepsApprovals)
2329
- void track(askApprovals());
2466
+ joinsLoaded();
2330
2467
  if (again)
2331
2468
  return;
2332
2469
  offerDraft();
@@ -2362,6 +2499,7 @@ function innerForm(options, within, place, outer) {
2362
2499
  const fresh = { dirty: [], errors: {}, warning: null, warningField: null, error: null, draft: null, step: steps[0] ?? null, skipped: [], saveProblem: null, version: null, can: null, conflict: null, setAside: null, unsent: [], unread: {}, takenOut: {}, resume: null, approval: null };
2363
2500
  if (id != null) {
2364
2501
  set({ ...fresh, recordId: id, status: 'idle' });
2502
+ joinsReset();
2365
2503
  await load();
2366
2504
  return true;
2367
2505
  }
@@ -2369,6 +2507,7 @@ function innerForm(options, within, place, outer) {
2369
2507
  baseline = startFrom(startingValues(true, false));
2370
2508
  const values = syncParts(structuredCopy(baseline), 'start');
2371
2509
  set({ ...fresh, recordId: null, status: 'ready', values, warnings: withValues(values, collectWarnings) });
2510
+ joinsReset();
2372
2511
  if (asksLimits())
2373
2512
  void track(askLimits());
2374
2513
  offerDraft();
@@ -2693,8 +2832,12 @@ function innerForm(options, within, place, outer) {
2693
2832
  const builtin = 'builtin' in source && !list.length ? source.builtin : undefined;
2694
2833
  if (builtin)
2695
2834
  list.push({ do: builtin });
2696
- const asked = builtin === 'archive' ? named(messages.archiveConfirmNoun, messages.archiveConfirm) : builtin === 'delete' ? named(messages.deleteConfirmNoun, messages.deleteConfirm) : undefined;
2835
+ // Undo after it, in place of asking first: a built-in with `undo` asks nothing unless it says so.
2836
+ const offers = 'undo' in source ? source.undo : undefined;
2837
+ const asked = offers !== undefined ? undefined : builtin === 'archive' ? named(messages.archiveConfirmNoun, messages.archiveConfirm) : builtin === 'delete' ? named(messages.deleteConfirmNoun, messages.deleteConfirm) : undefined;
2697
2838
  const confirm = 'confirm' in source && source.confirm !== undefined ? source.confirm : asked;
2839
+ if (offers !== undefined)
2840
+ scope.taken = takenNow(state.values);
2698
2841
  // Its question's Yes, and its ask steps', say what it does: its own words, else Archive or Delete.
2699
2842
  scope.yes ??= source.label || (builtin === 'archive' ? messages.archiveYes : builtin === 'delete' ? messages.deleteYes : undefined);
2700
2843
  // On the record it was pressed on: another shown while its question is asked, it does nothing to that one.
@@ -2728,9 +2871,21 @@ function innerForm(options, within, place, outer) {
2728
2871
  return result;
2729
2872
  }
2730
2873
  }
2731
- return runTop(list, scope);
2874
+ const result = await runTop(list, scope);
2875
+ if (scope.taken)
2876
+ offerUndo(source, scope, result, offers);
2877
+ return result;
2732
2878
  })());
2733
2879
  }
2880
+ /**
2881
+ * A press that offers Undo has ended: its words said — the last with Undo, where what it did can be taken back — by
2882
+ * the undo part (press-undo.ts), which a page whose buttons offer Undo asks for first; plainly where it cannot come.
2883
+ */
2884
+ function offerUndo(source, scope, result, offers) {
2885
+ const taken = scope.taken;
2886
+ scope.taken = undefined;
2887
+ void track(pressUndoPart.load().then((part) => part.offerUndo(inner, { kit: runner.kit, source, scope, taken, result, offers }), () => taken.said.forEach((words) => options.host?.say(words.message, words.tone))));
2888
+ }
2734
2889
  const form = {
2735
2890
  page,
2736
2891
  ...(table ? { functions: table } : {}),
@@ -2755,7 +2910,10 @@ function innerForm(options, within, place, outer) {
2755
2910
  // A choice whose places are all taken is shown, and refused when the person picks it: said under its field.
2756
2911
  if ((how?.by ?? 'person') === 'person' && refusedFull(name, value))
2757
2912
  return;
2913
+ const before = state.values[name];
2758
2914
  writeValues({ ...state.values, [name]: structuredCopy(value) });
2915
+ if ((how?.by ?? 'person') === 'person')
2916
+ noteChange(name, undefined, before, true);
2759
2917
  afterEdit(name);
2760
2918
  changed(name, how?.by ?? 'person');
2761
2919
  },
@@ -2780,7 +2938,7 @@ function innerForm(options, within, place, outer) {
2780
2938
  // Nothing of the record changed; what shows of it may have.
2781
2939
  set({});
2782
2940
  },
2783
- fieldReadonly: (name) => lockedField(fieldDef(name)) || state.can?.edit === false || state.can?.readonlyFields?.includes(name) === true || signedNow().has(name) || !!approvalKit?.locks(name),
2941
+ fieldReadonly: (name) => lockedField(fieldDef(name)) || state.can?.edit === false || state.can?.readonlyFields?.includes(name) === true || signedNow().has(name) || anyJoin((join) => join.locksField?.(name)),
2784
2942
  loadChoices: (field) => loadChoices(field),
2785
2943
  loadDefinitions,
2786
2944
  addDefinition,
@@ -2821,8 +2979,9 @@ function innerForm(options, within, place, outer) {
2821
2979
  return null;
2822
2980
  if (state.unread[name])
2823
2981
  return state.unread[name].message;
2824
- // A property's text left unread answers for its field.
2825
- if (page.fields[name].type === 'properties') {
2982
+ // A property's text left unread answers for its field, and so does a box of a value drawn in parts (a repeat
2983
+ // rule's number, an opening hour's time: `field.part`).
2984
+ if (['properties', 'json', 'char'].includes(page.fields[name].type)) {
2826
2985
  const row = Object.keys(state.unread).find((path) => path.startsWith(`${name}.`));
2827
2986
  if (row)
2828
2987
  return state.unread[row].message;
@@ -2836,7 +2995,9 @@ function innerForm(options, within, place, outer) {
2836
2995
  const field = path.split('.')[0];
2837
2996
  // A line's cell (`field.lineKey.subfield`), or a property (`field.property`).
2838
2997
  const def = fieldDef(field);
2839
- if (path.includes('.') && def.type !== 'properties')
2998
+ // A line's cell, a property, or one of the boxes a value is drawn in (a repeat rule's number, an opening hour's
2999
+ // time: `field.part`, of a char or a json field).
3000
+ if (path.includes('.') && def.type !== 'properties' && !((def.type === 'json' || def.type === 'char') && path.split('.').length === 2))
2840
3001
  lineField(field);
2841
3002
  const had = state.unread[path];
2842
3003
  if (!unread && !had)
@@ -2859,6 +3020,7 @@ function innerForm(options, within, place, outer) {
2859
3020
  set({ unread: next, errors });
2860
3021
  },
2861
3022
  boxLeft(path) {
3023
+ noted.push([path.split('.')[0]]);
2862
3024
  const problem = leftProblem(path);
2863
3025
  if (!problem || state.errors[path] === problem)
2864
3026
  return;
@@ -2881,10 +3043,11 @@ function innerForm(options, within, place, outer) {
2881
3043
  if (seen.captcha !== undefined)
2882
3044
  bot.captcha = seen.captcha;
2883
3045
  },
3046
+ joined: (name) => (joins[joinParts.findIndex((part) => part.name === name)] ?? null),
2884
3047
  reading: () => readingNow > 0 || [...attached.values()].some((part) => part.inner.form.reading()),
2885
- validate() {
3048
+ validate(within) {
2886
3049
  readTyped();
2887
- const errors = collectErrors();
3050
+ const errors = collectErrors(within);
2888
3051
  set({ errors });
2889
3052
  return Object.keys(errors).length === 0;
2890
3053
  },
@@ -2977,6 +3140,7 @@ function innerForm(options, within, place, outer) {
2977
3140
  holding = setWhen.holding(baseline);
2978
3141
  // The person's own Discard: nothing of theirs is left at risk, and a save on its way lands as the record it stored.
2979
3142
  resets++;
3143
+ noted = [];
2980
3144
  personal.clear();
2981
3145
  personalLines.clear();
2982
3146
  filed.clear();
@@ -2985,6 +3149,9 @@ function innerForm(options, within, place, outer) {
2985
3149
  bot = { ...bot, honeypot: false, since: clock() };
2986
3150
  const values = syncParts(structuredCopy(baseline), 'start');
2987
3151
  set({ values, dirty: [], errors: {}, warnings: withValues(values, collectWarnings), warning: null, warningField: null, draft: null, step: steps[0] ?? null, skipped: [], saveProblem: null, conflict: null, unsent: [], unread: {}, takenOut: {}, sent: null });
3152
+ // Started again: a new response proves and pays its own; a new respondent is drawn anew (unless the app or the
3153
+ // person's id is the seed), on the first step drawn, no clock running.
3154
+ joinsReset();
2988
3155
  // Started again: whether the page takes another response, and each choice's places, asked again.
2989
3156
  if (asksLimits())
2990
3157
  void track(askLimits());
@@ -3004,8 +3171,10 @@ function innerForm(options, within, place, outer) {
3004
3171
  const def = lineField(field);
3005
3172
  if (!def.fields[name])
3006
3173
  throw new Error(`The lines of "${field}" have no field "${name}"`);
3007
- const lines = (state.values[field] ?? []).map((line) => line.key === key ? { ...line, values: { ...line.values, [name]: structuredCopy(value) } } : line);
3174
+ const before = state.values[field];
3175
+ const lines = (before ?? []).map((line) => line.key === key ? { ...line, values: { ...line.values, [name]: structuredCopy(value) } } : line);
3008
3176
  writeValues({ ...state.values, [field]: lines });
3177
+ noteChange(field, key, before, true);
3009
3178
  afterEdit(field);
3010
3179
  changed(field, 'person', { line: key });
3011
3180
  },
@@ -3035,7 +3204,9 @@ function innerForm(options, within, place, outer) {
3035
3204
  const value = trusted ? (i >= low && i <= high ? pool[i - low] : line.values[sequence]) : i + 1;
3036
3205
  return value === line.values[sequence] ? line : { ...line, values: { ...line.values, [sequence]: value } };
3037
3206
  });
3207
+ const was = state.values[field];
3038
3208
  writeValues({ ...state.values, [field]: renumbered });
3209
+ noteChange(field, undefined, was, false);
3039
3210
  afterEdit(field);
3040
3211
  changed(field, 'person');
3041
3212
  },
@@ -3049,8 +3220,10 @@ function innerForm(options, within, place, outer) {
3049
3220
  throw new Error(`The line "${key}" of "${field}" cannot be deleted now: its table's lineDelete does not hold on it`);
3050
3221
  }
3051
3222
  }
3052
- const lines = (state.values[field] ?? []).filter((line) => line.key !== key);
3223
+ const before = state.values[field];
3224
+ const lines = (before ?? []).filter((line) => line.key !== key);
3053
3225
  writeValues({ ...state.values, [field]: lines });
3226
+ noteChange(field, key, before, false);
3054
3227
  afterEdit(field);
3055
3228
  changed(field, 'person', { line: key });
3056
3229
  },
@@ -3117,6 +3290,8 @@ function innerForm(options, within, place, outer) {
3117
3290
  set({ step: previous, errors: {} });
3118
3291
  return true;
3119
3292
  },
3293
+ startTimer: () => joined('surveyflow')?.start(),
3294
+ countdown: () => joined('surveyflow')?.countdown() ?? null,
3120
3295
  skip() {
3121
3296
  const current = state.step;
3122
3297
  if (!current || !optionalStep(current))
@@ -3278,14 +3453,18 @@ function innerForm(options, within, place, outer) {
3278
3453
  return saved;
3279
3454
  },
3280
3455
  refreshLimits: () => askLimits(),
3281
- refreshApprovals: askApprovals,
3456
+ async refreshApprovals() {
3457
+ await joining;
3458
+ await joined('approvals')?.ask();
3459
+ },
3282
3460
  async decide(level, decision, reason) {
3283
- const kit = await approvalsCode();
3461
+ await joining;
3462
+ const kit = joined('approvals');
3284
3463
  if (!kit)
3285
3464
  throw new Error('No approvals on this page');
3286
3465
  return track(kit.decide(level, decision, reason));
3287
3466
  },
3288
- approvalLock: (name) => approvalKit?.lockWords(name) ?? null,
3467
+ approvalLock: (name) => joined('approvals')?.lockWords(name) ?? null,
3289
3468
  async reloadTheirs() {
3290
3469
  // A save on its way first: its answer is not dropped by the load.
3291
3470
  const asked = shownNow();
@@ -3385,6 +3564,9 @@ function innerForm(options, within, place, outer) {
3385
3564
  if (state.setAside)
3386
3565
  set({ setAside: null });
3387
3566
  },
3567
+ undo: () => replay(true),
3568
+ redo: () => replay(false),
3569
+ history: () => pressUndoPart.loaded?.nextSteps(noted, inner.same) ?? { undo: null, redo: null },
3388
3570
  async settled() {
3389
3571
  while (pending.size)
3390
3572
  await Promise.allSettled([...pending]);
@@ -3451,6 +3633,8 @@ function innerForm(options, within, place, outer) {
3451
3633
  dispose() {
3452
3634
  // An answer that lands after is for no record shown.
3453
3635
  disposed = true;
3636
+ for (const join of joins)
3637
+ join?.dispose?.();
3454
3638
  newGeneration();
3455
3639
  clearFiles();
3456
3640
  if (draftTimer !== null)
@@ -3470,9 +3654,22 @@ function innerForm(options, within, place, outer) {
3470
3654
  // choice's places as the source tells them: a record's own once it is loaded, its own choices not counted against it.
3471
3655
  if (keepsLimits())
3472
3656
  void track(limitsCode());
3473
- // A record's approvals: their part asked for as the form is made, ready by the time its record is loaded.
3474
- if (keepsApprovals)
3475
- void track(approvalsCode());
3657
+ // The parts its page or its source asks to join it — a survey's draw made before a draft is laid, which lays its own
3658
+ // draw over it; a record's approvals ready by the time its record is loaded; the record made live — each made at once
3659
+ // where it has come, else as it does, a draft laid meanwhile laid on it.
3660
+ if (joinParts.length) {
3661
+ const made = (at) => (part) => {
3662
+ if (disposed || joins[at])
3663
+ return;
3664
+ const join = (joins[at] = part.join(inner));
3665
+ if (laid)
3666
+ join.lay?.(laid);
3667
+ // Its record loaded before it came: what it asks of a loaded record, asked now.
3668
+ if (loadedOnce)
3669
+ join.loaded?.();
3670
+ };
3671
+ joining = Promise.all(joinParts.map((part, at) => (part.loaded ? made(at)(part.loaded) : track(part.load().then(made(at), () => undefined)))));
3672
+ }
3476
3673
  // A new form has its values now — a draft continued from its key once it is laid — and opens once whoever made it
3477
3674
  // has had the chance to listen.
3478
3675
  if (state.recordId == null) {