@fieldia/core 0.26.1 → 0.28.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 (435) hide show
  1. package/README.md +326 -3
  2. package/package.json +1 -1
  3. package/schema/page.schema.json +230 -24
  4. package/src/index.d.mts +10 -2
  5. package/src/index.d.ts +10 -2
  6. package/src/index.js +26 -1
  7. package/src/index.js.map +1 -1
  8. package/src/index.mjs +11 -2
  9. package/src/index.mjs.map +1 -1
  10. package/src/lib/expression/app-functions.d.mts +47 -0
  11. package/src/lib/expression/app-functions.d.ts +47 -0
  12. package/src/lib/expression/app-functions.js +64 -0
  13. package/src/lib/expression/app-functions.js.map +1 -0
  14. package/src/lib/expression/app-functions.mjs +58 -0
  15. package/src/lib/expression/app-functions.mjs.map +1 -0
  16. package/src/lib/expression/dates.d.mts +21 -0
  17. package/src/lib/expression/dates.d.ts +21 -0
  18. package/src/lib/expression/dates.js +73 -0
  19. package/src/lib/expression/dates.js.map +1 -0
  20. package/src/lib/expression/dates.mjs +67 -0
  21. package/src/lib/expression/dates.mjs.map +1 -0
  22. package/src/lib/expression/evaluator.js +12 -6
  23. package/src/lib/expression/evaluator.js.map +1 -1
  24. package/src/lib/expression/evaluator.mjs +12 -6
  25. package/src/lib/expression/evaluator.mjs.map +1 -1
  26. package/src/lib/expression/expression.d.mts +7 -3
  27. package/src/lib/expression/expression.d.ts +7 -3
  28. package/src/lib/expression/expression.js +8 -2
  29. package/src/lib/expression/expression.js.map +1 -1
  30. package/src/lib/expression/expression.mjs +7 -2
  31. package/src/lib/expression/expression.mjs.map +1 -1
  32. package/src/lib/expression/functions-part.d.mts +7 -0
  33. package/src/lib/expression/functions-part.d.ts +7 -0
  34. package/src/lib/expression/functions-part.js +13 -0
  35. package/src/lib/expression/functions-part.js.map +1 -0
  36. package/src/lib/expression/functions-part.mjs +8 -0
  37. package/src/lib/expression/functions-part.mjs.map +1 -0
  38. package/src/lib/expression/functions.d.mts +60 -3
  39. package/src/lib/expression/functions.d.ts +60 -3
  40. package/src/lib/expression/functions.js +109 -15
  41. package/src/lib/expression/functions.js.map +1 -1
  42. package/src/lib/expression/functions.mjs +103 -16
  43. package/src/lib/expression/functions.mjs.map +1 -1
  44. package/src/lib/expression/kinds.d.mts +31 -0
  45. package/src/lib/expression/kinds.d.ts +31 -0
  46. package/src/lib/expression/kinds.js +159 -0
  47. package/src/lib/expression/kinds.js.map +1 -0
  48. package/src/lib/expression/kinds.mjs +151 -0
  49. package/src/lib/expression/kinds.mjs.map +1 -0
  50. package/src/lib/expression/modifier.d.mts +4 -3
  51. package/src/lib/expression/modifier.d.ts +4 -3
  52. package/src/lib/expression/modifier.js +4 -3
  53. package/src/lib/expression/modifier.js.map +1 -1
  54. package/src/lib/expression/modifier.mjs +4 -3
  55. package/src/lib/expression/modifier.mjs.map +1 -1
  56. package/src/lib/expression/more-functions.d.mts +8 -0
  57. package/src/lib/expression/more-functions.d.ts +8 -0
  58. package/src/lib/expression/more-functions.js +235 -0
  59. package/src/lib/expression/more-functions.js.map +1 -0
  60. package/src/lib/expression/more-functions.mjs +230 -0
  61. package/src/lib/expression/more-functions.mjs.map +1 -0
  62. package/src/lib/expression/parser.d.mts +4 -1
  63. package/src/lib/expression/parser.d.ts +4 -1
  64. package/src/lib/expression/parser.js +6 -3
  65. package/src/lib/expression/parser.js.map +1 -1
  66. package/src/lib/expression/parser.mjs +6 -3
  67. package/src/lib/expression/parser.mjs.map +1 -1
  68. package/src/lib/expression/reads.d.mts +2 -2
  69. package/src/lib/expression/reads.d.ts +2 -2
  70. package/src/lib/expression/reads.js +6 -4
  71. package/src/lib/expression/reads.js.map +1 -1
  72. package/src/lib/expression/reads.mjs +6 -4
  73. package/src/lib/expression/reads.mjs.map +1 -1
  74. package/src/lib/format/actions.d.mts +16 -16
  75. package/src/lib/format/actions.d.ts +16 -16
  76. package/src/lib/format/check-page.d.mts +7 -2
  77. package/src/lib/format/check-page.d.ts +7 -2
  78. package/src/lib/format/check-page.js +10 -4
  79. package/src/lib/format/check-page.js.map +1 -1
  80. package/src/lib/format/check-page.mjs +10 -4
  81. package/src/lib/format/check-page.mjs.map +1 -1
  82. package/src/lib/format/choice-options.d.mts +34 -0
  83. package/src/lib/format/choice-options.d.ts +34 -0
  84. package/src/lib/format/choice-options.js +60 -0
  85. package/src/lib/format/choice-options.js.map +1 -0
  86. package/src/lib/format/choice-options.mjs +51 -0
  87. package/src/lib/format/choice-options.mjs.map +1 -0
  88. package/src/lib/format/endings-checks.d.mts +8 -0
  89. package/src/lib/format/endings-checks.d.ts +8 -0
  90. package/src/lib/format/endings-checks.js +23 -0
  91. package/src/lib/format/endings-checks.js.map +1 -0
  92. package/src/lib/format/endings-checks.mjs +20 -0
  93. package/src/lib/format/endings-checks.mjs.map +1 -0
  94. package/src/lib/format/endings.d.mts +40 -0
  95. package/src/lib/format/endings.d.ts +40 -0
  96. package/src/lib/format/endings.js +15 -0
  97. package/src/lib/format/endings.js.map +1 -0
  98. package/src/lib/format/endings.mjs +12 -0
  99. package/src/lib/format/endings.mjs.map +1 -0
  100. package/src/lib/format/field.d.mts +151 -16
  101. package/src/lib/format/field.d.ts +151 -16
  102. package/src/lib/format/field.js +64 -11
  103. package/src/lib/format/field.js.map +1 -1
  104. package/src/lib/format/field.mjs +63 -10
  105. package/src/lib/format/field.mjs.map +1 -1
  106. package/src/lib/format/head-checks.d.mts +2 -0
  107. package/src/lib/format/head-checks.d.ts +2 -0
  108. package/src/lib/format/head-checks.js +2 -59
  109. package/src/lib/format/head-checks.js.map +1 -1
  110. package/src/lib/format/head-checks.mjs +1 -58
  111. package/src/lib/format/head-checks.mjs.map +1 -1
  112. package/src/lib/format/later-checks.d.mts +17 -0
  113. package/src/lib/format/later-checks.d.ts +17 -0
  114. package/src/lib/format/later-checks.js +20 -0
  115. package/src/lib/format/later-checks.js.map +1 -0
  116. package/src/lib/format/later-checks.mjs +17 -0
  117. package/src/lib/format/later-checks.mjs.map +1 -0
  118. package/src/lib/format/layout.d.mts +264 -190
  119. package/src/lib/format/layout.d.ts +264 -190
  120. package/src/lib/format/layout.js +34 -29
  121. package/src/lib/format/layout.js.map +1 -1
  122. package/src/lib/format/layout.mjs +14 -4
  123. package/src/lib/format/layout.mjs.map +1 -1
  124. package/src/lib/format/layout.types.d.mts +2 -1
  125. package/src/lib/format/layout.types.d.ts +2 -1
  126. package/src/lib/format/limits-checks.d.mts +14 -0
  127. package/src/lib/format/limits-checks.d.ts +14 -0
  128. package/src/lib/format/limits-checks.js +36 -0
  129. package/src/lib/format/limits-checks.js.map +1 -0
  130. package/src/lib/format/limits-checks.mjs +33 -0
  131. package/src/lib/format/limits-checks.mjs.map +1 -0
  132. package/src/lib/format/limits.d.mts +24 -0
  133. package/src/lib/format/limits.d.ts +24 -0
  134. package/src/lib/format/limits.js +35 -0
  135. package/src/lib/format/limits.js.map +1 -0
  136. package/src/lib/format/limits.mjs +32 -0
  137. package/src/lib/format/limits.mjs.map +1 -0
  138. package/src/lib/format/list-checks.d.mts +44 -0
  139. package/src/lib/format/list-checks.d.ts +44 -0
  140. package/src/lib/format/list-checks.js +340 -0
  141. package/src/lib/format/list-checks.js.map +1 -0
  142. package/src/lib/format/list-checks.mjs +336 -0
  143. package/src/lib/format/list-checks.mjs.map +1 -0
  144. package/src/lib/format/map-checks.d.mts +33 -0
  145. package/src/lib/format/map-checks.d.ts +33 -0
  146. package/src/lib/format/map-checks.js +180 -0
  147. package/src/lib/format/map-checks.js.map +1 -0
  148. package/src/lib/format/map-checks.mjs +175 -0
  149. package/src/lib/format/map-checks.mjs.map +1 -0
  150. package/src/lib/format/mapchecks-part.d.mts +5 -0
  151. package/src/lib/format/mapchecks-part.d.ts +5 -0
  152. package/src/lib/format/mapchecks-part.js +11 -0
  153. package/src/lib/format/mapchecks-part.js.map +1 -0
  154. package/src/lib/format/mapchecks-part.mjs +6 -0
  155. package/src/lib/format/mapchecks-part.mjs.map +1 -0
  156. package/src/lib/format/option-checks.d.mts +16 -0
  157. package/src/lib/format/option-checks.d.ts +16 -0
  158. package/src/lib/format/option-checks.js +39 -0
  159. package/src/lib/format/option-checks.js.map +1 -0
  160. package/src/lib/format/option-checks.mjs +36 -0
  161. package/src/lib/format/option-checks.mjs.map +1 -0
  162. package/src/lib/format/options-part.d.mts +6 -0
  163. package/src/lib/format/options-part.d.ts +6 -0
  164. package/src/lib/format/options-part.js +11 -0
  165. package/src/lib/format/options-part.js.map +1 -0
  166. package/src/lib/format/options-part.mjs +7 -0
  167. package/src/lib/format/options-part.mjs.map +1 -0
  168. package/src/lib/format/page.d.mts +154 -71
  169. package/src/lib/format/page.d.ts +154 -71
  170. package/src/lib/format/page.js +5 -0
  171. package/src/lib/format/page.js.map +1 -1
  172. package/src/lib/format/page.mjs +5 -0
  173. package/src/lib/format/page.mjs.map +1 -1
  174. package/src/lib/format/part-checks.d.mts +20 -0
  175. package/src/lib/format/part-checks.d.ts +20 -0
  176. package/src/lib/format/part-checks.js +3 -0
  177. package/src/lib/format/part-checks.js.map +1 -0
  178. package/src/lib/format/part-checks.mjs +2 -0
  179. package/src/lib/format/part-checks.mjs.map +1 -0
  180. package/src/lib/format/references.d.mts +71 -65
  181. package/src/lib/format/references.d.ts +71 -65
  182. package/src/lib/format/references.js +194 -631
  183. package/src/lib/format/references.js.map +1 -1
  184. package/src/lib/format/references.mjs +194 -631
  185. package/src/lib/format/references.mjs.map +1 -1
  186. package/src/lib/format/seats-checks.d.mts +3 -0
  187. package/src/lib/format/seats-checks.d.ts +3 -0
  188. package/src/lib/format/seats-checks.js +13 -0
  189. package/src/lib/format/seats-checks.js.map +1 -0
  190. package/src/lib/format/seats-checks.mjs +10 -0
  191. package/src/lib/format/seats-checks.mjs.map +1 -0
  192. package/src/lib/format/sheet-head-checks.d.mts +4 -0
  193. package/src/lib/format/sheet-head-checks.d.ts +4 -0
  194. package/src/lib/format/sheet-head-checks.js +68 -0
  195. package/src/lib/format/sheet-head-checks.js.map +1 -0
  196. package/src/lib/format/sheet-head-checks.mjs +65 -0
  197. package/src/lib/format/sheet-head-checks.mjs.map +1 -0
  198. package/src/lib/format/signature-checks.d.mts +7 -0
  199. package/src/lib/format/signature-checks.d.ts +7 -0
  200. package/src/lib/format/signature-checks.js +32 -0
  201. package/src/lib/format/signature-checks.js.map +1 -0
  202. package/src/lib/format/signature-checks.mjs +29 -0
  203. package/src/lib/format/signature-checks.mjs.map +1 -0
  204. package/src/lib/format/signature-options.d.mts +24 -0
  205. package/src/lib/format/signature-options.d.ts +24 -0
  206. package/src/lib/format/signature-options.js +30 -0
  207. package/src/lib/format/signature-options.js.map +1 -0
  208. package/src/lib/format/signature-options.mjs +26 -0
  209. package/src/lib/format/signature-options.mjs.map +1 -0
  210. package/src/lib/format/slots-checks.d.mts +4 -0
  211. package/src/lib/format/slots-checks.d.ts +4 -0
  212. package/src/lib/format/slots-checks.js +21 -0
  213. package/src/lib/format/slots-checks.js.map +1 -0
  214. package/src/lib/format/slots-checks.mjs +18 -0
  215. package/src/lib/format/slots-checks.mjs.map +1 -0
  216. package/src/lib/format/statusbar-checks.d.mts +8 -0
  217. package/src/lib/format/statusbar-checks.d.ts +8 -0
  218. package/src/lib/format/statusbar-checks.js +35 -0
  219. package/src/lib/format/statusbar-checks.js.map +1 -0
  220. package/src/lib/format/statusbar-checks.mjs +32 -0
  221. package/src/lib/format/statusbar-checks.mjs.map +1 -0
  222. package/src/lib/format/table-checks.d.mts +21 -0
  223. package/src/lib/format/table-checks.d.ts +21 -0
  224. package/src/lib/format/table-checks.js +86 -0
  225. package/src/lib/format/table-checks.js.map +1 -0
  226. package/src/lib/format/table-checks.mjs +82 -0
  227. package/src/lib/format/table-checks.mjs.map +1 -0
  228. package/src/lib/format/translate.js +2 -2
  229. package/src/lib/format/translate.js.map +1 -1
  230. package/src/lib/format/translate.mjs +2 -2
  231. package/src/lib/format/translate.mjs.map +1 -1
  232. package/src/lib/format/upgrade.d.mts +7 -3
  233. package/src/lib/format/upgrade.d.ts +7 -3
  234. package/src/lib/format/upgrade.js +7 -3
  235. package/src/lib/format/upgrade.js.map +1 -1
  236. package/src/lib/format/upgrade.mjs +7 -3
  237. package/src/lib/format/upgrade.mjs.map +1 -1
  238. package/src/lib/format/validate.d.mts +18 -2
  239. package/src/lib/format/validate.d.ts +18 -2
  240. package/src/lib/format/validate.js +36 -3
  241. package/src/lib/format/validate.js.map +1 -1
  242. package/src/lib/format/validate.mjs +35 -3
  243. package/src/lib/format/validate.mjs.map +1 -1
  244. package/src/lib/format/version.d.mts +2 -2
  245. package/src/lib/format/version.d.ts +2 -2
  246. package/src/lib/format/version.js +2 -2
  247. package/src/lib/format/version.js.map +1 -1
  248. package/src/lib/format/version.mjs +2 -2
  249. package/src/lib/format/version.mjs.map +1 -1
  250. package/src/lib/format/words.d.mts +6 -2
  251. package/src/lib/format/words.d.ts +6 -2
  252. package/src/lib/format/words.js +13 -7
  253. package/src/lib/format/words.js.map +1 -1
  254. package/src/lib/format/words.mjs +13 -7
  255. package/src/lib/format/words.mjs.map +1 -1
  256. package/src/lib/parts.d.mts +45 -0
  257. package/src/lib/parts.d.ts +45 -0
  258. package/src/lib/parts.js +60 -0
  259. package/src/lib/parts.js.map +1 -0
  260. package/src/lib/parts.mjs +53 -0
  261. package/src/lib/parts.mjs.map +1 -0
  262. package/src/lib/record/cells.d.mts +3 -3
  263. package/src/lib/record/cells.d.ts +3 -3
  264. package/src/lib/record/cells.js +16 -15
  265. package/src/lib/record/cells.js.map +1 -1
  266. package/src/lib/record/cells.mjs +16 -15
  267. package/src/lib/record/cells.mjs.map +1 -1
  268. package/src/lib/record/check.js +3 -1
  269. package/src/lib/record/check.js.map +1 -1
  270. package/src/lib/record/check.mjs +3 -1
  271. package/src/lib/record/check.mjs.map +1 -1
  272. package/src/lib/record/compute.d.mts +16 -5
  273. package/src/lib/record/compute.d.ts +16 -5
  274. package/src/lib/record/compute.js +57 -16
  275. package/src/lib/record/compute.js.map +1 -1
  276. package/src/lib/record/compute.mjs +57 -16
  277. package/src/lib/record/compute.mjs.map +1 -1
  278. package/src/lib/record/data-source.d.mts +140 -3
  279. package/src/lib/record/data-source.d.ts +140 -3
  280. package/src/lib/record/data-source.js.map +1 -1
  281. package/src/lib/record/data-source.mjs.map +1 -1
  282. package/src/lib/record/day-words.d.mts +10 -0
  283. package/src/lib/record/day-words.d.ts +10 -0
  284. package/src/lib/record/day-words.js +23 -0
  285. package/src/lib/record/day-words.js.map +1 -0
  286. package/src/lib/record/day-words.mjs +20 -0
  287. package/src/lib/record/day-words.mjs.map +1 -0
  288. package/src/lib/record/drafts.d.mts +19 -0
  289. package/src/lib/record/drafts.d.ts +19 -0
  290. package/src/lib/record/drafts.js +35 -0
  291. package/src/lib/record/drafts.js.map +1 -0
  292. package/src/lib/record/drafts.mjs +31 -0
  293. package/src/lib/record/drafts.mjs.map +1 -0
  294. package/src/lib/record/env.d.mts +5 -1
  295. package/src/lib/record/env.d.ts +5 -1
  296. package/src/lib/record/env.js +9 -1
  297. package/src/lib/record/env.js.map +1 -1
  298. package/src/lib/record/env.mjs +8 -1
  299. package/src/lib/record/env.mjs.map +1 -1
  300. package/src/lib/record/form-limits.d.mts +78 -0
  301. package/src/lib/record/form-limits.d.ts +78 -0
  302. package/src/lib/record/form-limits.js +230 -0
  303. package/src/lib/record/form-limits.js.map +1 -0
  304. package/src/lib/record/form-limits.mjs +223 -0
  305. package/src/lib/record/form-limits.mjs.map +1 -0
  306. package/src/lib/record/form.d.mts +180 -9
  307. package/src/lib/record/form.d.ts +180 -9
  308. package/src/lib/record/form.js +858 -90
  309. package/src/lib/record/form.js.map +1 -1
  310. package/src/lib/record/form.mjs +858 -90
  311. package/src/lib/record/form.mjs.map +1 -1
  312. package/src/lib/record/limits-part.d.mts +6 -0
  313. package/src/lib/record/limits-part.d.ts +6 -0
  314. package/src/lib/record/limits-part.js +12 -0
  315. package/src/lib/record/limits-part.js.map +1 -0
  316. package/src/lib/record/limits-part.mjs +7 -0
  317. package/src/lib/record/limits-part.mjs.map +1 -0
  318. package/src/lib/record/limits.js +4 -2
  319. package/src/lib/record/limits.js.map +1 -1
  320. package/src/lib/record/limits.mjs +4 -2
  321. package/src/lib/record/limits.mjs.map +1 -1
  322. package/src/lib/record/line-choices.d.mts +30 -0
  323. package/src/lib/record/line-choices.d.ts +30 -0
  324. package/src/lib/record/line-choices.js +42 -0
  325. package/src/lib/record/line-choices.js.map +1 -0
  326. package/src/lib/record/line-choices.mjs +36 -0
  327. package/src/lib/record/line-choices.mjs.map +1 -0
  328. package/src/lib/record/locales/ar.js +11 -0
  329. package/src/lib/record/locales/ar.js.map +1 -1
  330. package/src/lib/record/locales/ar.mjs +11 -0
  331. package/src/lib/record/locales/ar.mjs.map +1 -1
  332. package/src/lib/record/locales/de.js +11 -0
  333. package/src/lib/record/locales/de.js.map +1 -1
  334. package/src/lib/record/locales/de.mjs +11 -0
  335. package/src/lib/record/locales/de.mjs.map +1 -1
  336. package/src/lib/record/locales/fr.js +11 -0
  337. package/src/lib/record/locales/fr.js.map +1 -1
  338. package/src/lib/record/locales/fr.mjs +11 -0
  339. package/src/lib/record/locales/fr.mjs.map +1 -1
  340. package/src/lib/record/memory-data-source.d.mts +44 -3
  341. package/src/lib/record/memory-data-source.d.ts +44 -3
  342. package/src/lib/record/memory-data-source.js +102 -40
  343. package/src/lib/record/memory-data-source.js.map +1 -1
  344. package/src/lib/record/memory-data-source.mjs +101 -40
  345. package/src/lib/record/memory-data-source.mjs.map +1 -1
  346. package/src/lib/record/memory-kept.d.mts +20 -0
  347. package/src/lib/record/memory-kept.d.ts +20 -0
  348. package/src/lib/record/memory-kept.js +46 -0
  349. package/src/lib/record/memory-kept.js.map +1 -0
  350. package/src/lib/record/memory-kept.mjs +43 -0
  351. package/src/lib/record/memory-kept.mjs.map +1 -0
  352. package/src/lib/record/memory-later.d.mts +19 -0
  353. package/src/lib/record/memory-later.d.ts +19 -0
  354. package/src/lib/record/memory-later.js +39 -0
  355. package/src/lib/record/memory-later.js.map +1 -0
  356. package/src/lib/record/memory-later.mjs +35 -0
  357. package/src/lib/record/memory-later.mjs.map +1 -0
  358. package/src/lib/record/memory-part.d.mts +7 -0
  359. package/src/lib/record/memory-part.d.ts +7 -0
  360. package/src/lib/record/memory-part.js +13 -0
  361. package/src/lib/record/memory-part.js.map +1 -0
  362. package/src/lib/record/memory-part.mjs +8 -0
  363. package/src/lib/record/memory-part.mjs.map +1 -0
  364. package/src/lib/record/messages.d.mts +27 -2
  365. package/src/lib/record/messages.d.ts +27 -2
  366. package/src/lib/record/messages.js +13 -4
  367. package/src/lib/record/messages.js.map +1 -1
  368. package/src/lib/record/messages.mjs +13 -4
  369. package/src/lib/record/messages.mjs.map +1 -1
  370. package/src/lib/record/option-rules.d.mts +56 -0
  371. package/src/lib/record/option-rules.d.ts +56 -0
  372. package/src/lib/record/option-rules.js +143 -0
  373. package/src/lib/record/option-rules.js.map +1 -0
  374. package/src/lib/record/option-rules.mjs +140 -0
  375. package/src/lib/record/option-rules.mjs.map +1 -0
  376. package/src/lib/record/record-words.d.mts +13 -1
  377. package/src/lib/record/record-words.d.ts +13 -1
  378. package/src/lib/record/record-words.js +29 -1
  379. package/src/lib/record/record-words.js.map +1 -1
  380. package/src/lib/record/record-words.mjs +27 -1
  381. package/src/lib/record/record-words.mjs.map +1 -1
  382. package/src/lib/record/rules.d.mts +2 -2
  383. package/src/lib/record/rules.d.ts +2 -2
  384. package/src/lib/record/rules.js +3 -3
  385. package/src/lib/record/rules.js.map +1 -1
  386. package/src/lib/record/rules.mjs +3 -3
  387. package/src/lib/record/rules.mjs.map +1 -1
  388. package/src/lib/record/run.js +9 -7
  389. package/src/lib/record/run.js.map +1 -1
  390. package/src/lib/record/run.mjs +9 -7
  391. package/src/lib/record/run.mjs.map +1 -1
  392. package/src/lib/record/search.d.mts +12 -2
  393. package/src/lib/record/search.d.ts +12 -2
  394. package/src/lib/record/search.js +29 -4
  395. package/src/lib/record/search.js.map +1 -1
  396. package/src/lib/record/search.mjs +29 -4
  397. package/src/lib/record/search.mjs.map +1 -1
  398. package/src/lib/record/set-when.d.mts +3 -3
  399. package/src/lib/record/set-when.d.ts +3 -3
  400. package/src/lib/record/set-when.js +9 -9
  401. package/src/lib/record/set-when.js.map +1 -1
  402. package/src/lib/record/set-when.mjs +9 -9
  403. package/src/lib/record/set-when.mjs.map +1 -1
  404. package/src/lib/record/signed.d.mts +21 -0
  405. package/src/lib/record/signed.d.ts +21 -0
  406. package/src/lib/record/signed.js +62 -0
  407. package/src/lib/record/signed.js.map +1 -0
  408. package/src/lib/record/signed.mjs +57 -0
  409. package/src/lib/record/signed.mjs.map +1 -0
  410. package/src/lib/record/taking.d.mts +60 -0
  411. package/src/lib/record/taking.d.ts +60 -0
  412. package/src/lib/record/taking.js +58 -0
  413. package/src/lib/record/taking.js.map +1 -0
  414. package/src/lib/record/taking.mjs +50 -0
  415. package/src/lib/record/taking.mjs.map +1 -0
  416. package/src/lib/record/value-map.js +11 -4
  417. package/src/lib/record/value-map.js.map +1 -1
  418. package/src/lib/record/value-map.mjs +11 -4
  419. package/src/lib/record/value-map.mjs.map +1 -1
  420. package/src/lib/record/values.d.mts +20 -0
  421. package/src/lib/record/values.d.ts +20 -0
  422. package/src/lib/record/values.js.map +1 -1
  423. package/src/lib/record/values.mjs.map +1 -1
  424. package/src/lib/record/waits.d.mts +13 -6
  425. package/src/lib/record/waits.d.ts +13 -6
  426. package/src/lib/record/waits.js +9 -12
  427. package/src/lib/record/waits.js.map +1 -1
  428. package/src/lib/record/waits.mjs +9 -12
  429. package/src/lib/record/waits.mjs.map +1 -1
  430. package/src/runtime.d.mts +17 -6
  431. package/src/runtime.d.ts +17 -6
  432. package/src/runtime.js +45 -3
  433. package/src/runtime.js.map +1 -1
  434. package/src/runtime.mjs +17 -1
  435. package/src/runtime.mjs.map +1 -1
package/README.md CHANGED
@@ -28,7 +28,7 @@ list of records — that your app implements;
28
28
  The page format is also a JSON Schema, for editors and other languages:
29
29
  `@fieldia/core/page.schema.json`.
30
30
 
31
- A page says which format it is written in (`"fieldia": "0.5"`, `FORMAT_VERSION`; a page in 0.1, 0.2, 0.3 or 0.4 is read as it is).
31
+ A page says which format it is written in (`"fieldia": "0.7"`, `FORMAT_VERSION`; a page in 0.1, 0.2, 0.3, 0.4, 0.5 or 0.6 is read as it is).
32
32
  One in an older format is brought up to date as it is read — by `checkPage`,
33
33
  `validatePage` and `createForm`, so by the viewer and the designer too — and
34
34
  `upgradePage` does it for the pages your app keeps. One newer than this Fieldia
@@ -78,6 +78,132 @@ and null when it shows its value; the viewer draws it as a dash, said in words
78
78
  ("Fix line 1 of Order lines to see Untaxed amount"). An answer your onchange
79
79
  gives meanwhile is kept, and shown once the line reads.
80
80
 
81
+ It waits the same for an answer the form refuses: one shown that holds a value
82
+ its checks do not take — an area of −50 under its `min` of 0, a day past its
83
+ `max` of `today`, a pattern not met, an answer rule's error — on the record or
84
+ in a line's cell, whether the value is the page's own formula, one of
85
+ Fieldia's functions (`years(built_on, today())`) or your app's: "Fix Area (m²)
86
+ to see Price per m²", never −120,000.00. An empty answer is none, required or
87
+ not, and one no one is shown is never refused. What waits for an answer is told
88
+ before what is asked of your app's server for it.
89
+
90
+ ## Functions, and your app's own
91
+
92
+ A value worked out (`compute`), a value set by a condition (`setWhen`) and every
93
+ condition call functions by name. Fieldia's own: `round`, `roundMoney`, `min`,
94
+ `max`, `abs`, `sum`, `count`, `average` on numbers; `today`, `days`, `age`,
95
+ `years`, `months`, `addDays`, `addMonths`, `addYears` on dates; `contains`,
96
+ `startsWith`, `endsWith`, `upper`, `lower`, `join`, `len` on words; `if`,
97
+ `empty`, `firstOf` to choose.
98
+
99
+ ```json
100
+ "age": { "type": "integer", "label": "Age", "compute": "age(birth_date)" },
101
+ "second_due": { "type": "date", "label": "Second payment", "compute": "addMonths(first_due, 1)" },
102
+ "per_meter": { "type": "monetary", "label": "Price per m²", "compute": "roundMoney(price / area, currency_id)", "currencyField": "currency_id" },
103
+ "code": { "type": "char", "label": "Code", "compute": "upper(join('-', city, firstOf(nickname, name)))" }
104
+ ```
105
+
106
+ - `age(birth, on?)` counts whole years to today on the page's clock (its `timeZone`),
107
+ or to a day given; `years(from, to)` and `months(from, to)` count whole ones,
108
+ negative when the second day comes first.
109
+ - `addMonths(day, n)` ends on a shorter month's last day — 31 January and a month
110
+ is 28 February — and `months` counts the same way, so the two agree. A date and
111
+ time keeps its time. `addYears` moves 29 February to the 28th.
112
+ - `contains`, `startsWith` and `endsWith` read text whatever its case; `contains`
113
+ also finds a value in a list (choices ticked, links).
114
+ - `join(between, values…)` joins the values — lists opened — with the words given
115
+ first, leaving the empty ones out: `join(' ', first_name, last_name)`.
116
+ - `average` takes values or a list, or a field of the lines where a condition holds,
117
+ as `sum` does: `average(lines, 'price', "qty > 0")`.
118
+ - `roundMoney(amount, currency)` rounds to the currency's places — three for a
119
+ Jordanian dinar, none for a yen — the currency a code (`'JOD'`) or a link to one,
120
+ read by its name.
121
+ - `empty(value)` holds for nothing, words of only spaces, an empty list; zero and
122
+ no are values. `firstOf(a, b, …)` is the first that is not empty.
123
+
124
+ Each is read as the page is. **Refused**, by `validatePage`, `checkPage` and so as a
125
+ form is made: a function there is not, or one given too many or too few values.
126
+ **A warning**, never a refusal: a value of a kind a function does not take —
127
+ `age(notes)`: "age takes a date as its first value; "notes" is text". `validatePage`
128
+ passes such a page and says it in `warnings` beside it (`pageWarnings(page)` gives
129
+ them alone; `kindProblems` to your own tools); the designer says it as a fix to make;
130
+ the viewer runs the page as it always did, the call giving nothing — a function given
131
+ a value it cannot use gives nothing, never an error. A page that passed before still
132
+ passes.
133
+
134
+ **Your app's own.** Give the form `functions` — the viewer and the React, Vue and
135
+ Angular components take the same option — and a page calls them by name, beside
136
+ Fieldia's. A plain function will do; say what it takes and gives, and the page's
137
+ checks and the designer read it:
138
+
139
+ ```ts
140
+ createForm({
141
+ page,
142
+ functions: {
143
+ vatRate: { takes: [1, 1], values: ['text'], gives: 'number', tip: 'The VAT rate of a country: vatRate(country)', call: (country) => RATES[country] ?? 0 },
144
+ // One that asks your server: a promise.
145
+ districtPrice: { asks: true, call: (district) => api.get(`/districts/${district}/price`).then((r) => r.perMeter) },
146
+ },
147
+ });
148
+ ```
149
+
150
+ A number one gives is tidied as Fieldia's own arithmetic tidies one — `price * 1.1`
151
+ of 100 is 110, never 110.00000000000001 — and one that is no number (NaN, Infinity) is
152
+ nothing. A function that asks your server gives a promise. What is worked out from it waits
153
+ while it is asked — empty meanwhile, `form.getState().asked` says so by field
154
+ (`{ total: {} }`), `form.waitsFor('total')` gives `{ field: 'total', asking: true }`,
155
+ and the viewer shows an ellipsis, said in words ("Working out Total…") — then its
156
+ answer is worked into everything after it. It is asked once for the same values,
157
+ for the record shown: another record shown, its answer is laid on none. A save waits
158
+ for every answer; one that failed (`{ failed: 'why' }`) is asked again as the person
159
+ saves, and stops the save under its field when it fails again ("Total could not be
160
+ worked out: …"). A condition calling one does not hold while it is asked. A name a
161
+ page cannot call — Fieldia's own, a word of the language — is refused as the form is
162
+ made. `validatePage(page, { functions })` and `checkPage(page, { functions })` read
163
+ pages that call them.
164
+
165
+ ## Options that follow the answers
166
+
167
+ An option of a choice may show only while a condition holds, written as a part's
168
+ `invisible`; and a choice may offer the options picked in an earlier one
169
+ (`optionsFrom: { "field": … }`), its own options after them:
170
+
171
+ ```json
172
+ "features": { "type": "selection", "label": "Features", "multiple": true, "options": [
173
+ { "value": "garden", "label": "Garden", "invisible": "unit_type != 'villa'" },
174
+ { "value": "lift", "label": "Lift", "invisible": "unit_type == 'villa'" },
175
+ { "value": "security", "label": "24-hour security" }
176
+ ] },
177
+ "again": { "type": "selection", "label": "Which would you have again?",
178
+ "options": [{ "value": "none", "label": "None of them" }],
179
+ "optionsFrom": { "field": "treatments" } },
180
+ "try_next": { "type": "selection", "label": "Which would you like to try?", "multiple": true,
181
+ "options": [], "optionsFrom": { "field": "treatments", "picked": false } }
182
+ ```
183
+
184
+ `form.offered(field)` gives the options a choice offers now: its own — or those its
185
+ app's list gave, or those picked where they are carried from (`picked: false`: those
186
+ left unpicked; a "None of these" is never carried) — less those whose condition hides
187
+ them. A chosen option a change no longer offers is taken out of the answer, never
188
+ silently kept: `form.getState().takenOut` says so by path ("Garden is no longer
189
+ offered, so it was taken out"), while it is true: it goes once the value is written
190
+ again, or once what it names is offered again (Villa picked again, the treatment
191
+ ticked again above, the list asked again offering it); one of several offered again
192
+ leaves it naming the rest. The same words for a choice its app's list no longer
193
+ offers, a page's or a line's. One a record held
194
+ as it was opened stays chosen, and the choice says it is no longer offered. The
195
+ page's checks read each option's condition and refuse a choice carried from itself,
196
+ from a field that is no choice, or round a circle.
197
+
198
+ A table line's choice takes conditions on its options too, read on the line's own
199
+ values and the record it is on (`parent.`): a room's "Garden room" only on the
200
+ ground floor, a "Pool house" only for a villa. `form.offered("rooms.k1.use")` gives
201
+ a line's by its cell's path; an option a change of the line, or of the record, hides
202
+ is taken out and said by that path (`takenOut["rooms.k1.use"]`). A line's choice
203
+ carries no options from a question: its own, or one of the app's lists — the same
204
+ `optionsFrom` as a page's own choice (`{ list, dependsOn }`, its `dependsOn` naming
205
+ the line's columns or `parent.` and a field).
206
+
81
207
  ## A public form's bot check
82
208
 
83
209
  A page of responses anyone may send — a sign-up, a contact form — can check that
@@ -115,6 +241,49 @@ viewer draws the hidden box and tells the form when it is filled
115
241
  (`form.botCheck({ honeypot: true })`). A page without `botCheck` sends what it
116
242
  always did.
117
243
 
244
+ ## What a page says once it is sent, and answers in its words
245
+
246
+ A page of responses' `endings` say what it shows once its answers are sent: the
247
+ first whose `when` holds on the answers as they were sent — with what the data
248
+ source answered, the id it gave the response and the values it added — else the
249
+ one with no `when`, shown otherwise, which comes last. `{id}` in its words is
250
+ that reference:
251
+
252
+ ```json
253
+ "endings": [
254
+ { "when": "overall <= 2", "title": "We are sorry, {name}", "text": "{branch} will call you today.", "again": false },
255
+ { "title": "Thank you, {name}", "text": "Your booking is {id}.", "again": "Book another" }
256
+ ]
257
+ ```
258
+
259
+ ```ts
260
+ async submit({ pageId, values }) {
261
+ const kept = await store(pageId, values);
262
+ return { id: kept.reference, values: { confirmed_at: kept.confirmedAt } }; // what the ending reads
263
+ }
264
+ form.getState().sent; // { id: 'B-1043', values: { …the answers sent, confirmed_at } } until reset()
265
+ form.ending(); // the ending for them, or null
266
+ form.endingWords('Your booking is {id}.'); // "Your booking is B-1043."
267
+ ```
268
+
269
+ `{field}` shows an answer in the page's own words too — a field's label and
270
+ help, each choice's words, a step's title and words, a wizard's and a button's
271
+ words — as in a text's and an alert's: `form.words('How was your visit to
272
+ {branch}?')` is "How was your visit to Zamalek?", and the form's messages name
273
+ the field so ("Please answer “How was your visit to Zamalek?”": a label that is
274
+ a question — ending with a question mark, or an ellipsis the answer completes —
275
+ is quoted in the language's marks wherever a sentence names it, `isQuestion` and
276
+ `quotedLabel`, and its required message is `requiredQuestion`). How a value reads
277
+ there is the viewer's (`valueWords` in `createForm`'s options: a choice by its
278
+ label, money with its currency, a day in the page's order); without one, a
279
+ choice's label, a link's name, Yes or No, anything else as held. After a bar,
280
+ the words said while the answer is empty — `{branch|this branch}` reads "this
281
+ branch" until one is chosen, translated with the page's other words; without
282
+ them an empty answer says nothing. A `{name}` that is no field of the page is
283
+ refused by the page check. `createMemoryDataSource`
284
+ answers each response as a server would with ``submitted: (request, count) =>
285
+ ({ id: `B-${1042 + count}` })``.
286
+
118
287
  ## Files stored as they are picked
119
288
 
120
289
  Give the data source `upload(file, request)` and a file or picture field sends
@@ -185,6 +354,20 @@ each date and time field that names none, as the form and the viewer read it;
185
354
  they use. `checkPage` and `validatePage` refuse a zone the browser does not know.
186
355
  Without `timeZone`, the browser's own zone, as before.
187
356
 
357
+ A selection's choices may come from the app's own lists (`optionsFrom: { list,
358
+ dependsOn }`), through the data source's `options({ list, values })`, asked again
359
+ when a field in `dependsOn` changes — by the person, a step or the app: a choice
360
+ the new answer does not offer is then taken out and said (`state.takenOut`, as a
361
+ condition's is, until its value changes again); a record that comes in with one
362
+ keeps it, and its save says why. On a table's line they are that line's:
363
+ asked with the line's values and the record it is on as `parent`
364
+ (`dependsOn` naming the line's columns, or `parent.` and a field), shared by the
365
+ lines whose columns hold the same values, and kept by the cell's path in
366
+ `state.choices` (`form.loadChoices("tool_ids.k1.tool")`), taken out and said the
367
+ same way, by the cell's path, as a column they change with changes on that line. `form.lineChoices(field, key, column,
368
+ values)` asks a line's for a row about to be pasted on it. The memory source's
369
+ `lists` functions take the request after the values.
370
+
188
371
  A date and time drawn as booking slots (`"widget": "slots"`) asks the data
189
372
  source's `slots(request)` for its free times: the days asked (`from`, `to`, in
190
373
  `timeZone`), the schedule its node names (`options.schedule`), the booking's
@@ -240,13 +423,22 @@ source's work, and `saveRefused({ kind: 'rule', message })` refuses it.
240
423
  source for a screen of your own, `form.canFindDuplicates()` and
241
424
  `form.canMerge()` say whether it can; the viewer says each match under the field
242
425
  it matched on, with Open it, "It's someone new" and Merge (`@fieldia/viewer`).
426
+ "It's someone new" is kept on the app's server where the source has both
427
+ `notDuplicates({ model, recordId }) → ids` (asked as a saved record is shown,
428
+ before its matches are said) and `markNotDuplicate({ model, recordId, matchId })`
429
+ (as it is pressed on a saved record, or a new one is saved after it): a pair,
430
+ answered from either side, so another device lets that record be too; without
431
+ both, the person's browser keeps it (the viewer's `preferences`), as before.
432
+ `form.keepsNotDuplicates()`, `form.notDuplicates()` and
433
+ `form.markNotDuplicate(matchId)` reach them.
243
434
  `checkPage` and `validatePage` refuse a field the page has not, one named twice,
244
435
  and one of a kind records are not compared by (`DUPLICATE_KINDS`). Without
245
436
  `duplicates` in the source, nothing is asked. For demos and tests,
246
437
  `alikeRecords()` answers from the memory source's records — the same value as
247
438
  people write them apart (a phone's spaces, an email's case, a link's record) or,
248
- in the name field, a name close enough (`nameLikeness`) — and its `merge`
249
- merges as a backend would:
439
+ in the name field, a name close enough (`nameLikeness`) — its `merge`
440
+ merges as a backend would, and its `notDuplicates` and `markNotDuplicate` keep
441
+ what was let be as pairs (`notDuplicatePairs`, an option to start from):
250
442
 
251
443
  ```ts
252
444
  import { alikeRecords, createMemoryDataSource } from '@fieldia/core';
@@ -254,6 +446,137 @@ import { alikeRecords, createMemoryDataSource } from '@fieldia/core';
254
446
  const dataSource = createMemoryDataSource({ records, duplicates: alikeRecords() });
255
447
  ```
256
448
 
449
+ ## A signature that signs the answers
450
+
451
+ A signature (`"widget": "signature"` on a binary field) may sign answers: `signs`
452
+ names them — `"form"`, every answer shown on the page, or the fields by name —
453
+ and `signerField` the field holding the signer's name (a text, or a link's
454
+ record; left out, the name typed on the pad, else the form's `user`):
455
+
456
+ ```json
457
+ { "type": "field", "id": "f-signature", "field": "signature", "widget": "signature",
458
+ "options": { "signs": ["patient", "treatment", "areas", "risks_read"], "signerField": "patient" } }
459
+ ```
460
+
461
+ Signed, it keeps beside its picture who, when and a fingerprint of the answers
462
+ (`FileValue.signed`, a `SignatureEvidence`), once what is typed in them is read
463
+ — an answer still to give, or text that does not read, is said, and nothing is
464
+ signed:
465
+
466
+ ```json
467
+ { "name": "signature.png", "type": "image/png", "size": 5120, "data": "iVBOR…",
468
+ "signed": { "format": "fieldia-signature/1", "by": "Mona Adel", "at": "2026-10-19T08:42:00.000Z",
469
+ "fields": ["patient", "treatment", "areas", "risks_read"],
470
+ "sha256": "2e3b5fa2ca4b2012eeb1fe7895af5c05dda2c3f5a76246b78fff3b530dbae16d" } }
471
+ ```
472
+
473
+ While a signature with evidence is kept, the form locks the answers it names —
474
+ whatever the page's conditions say (`form.node(id).readonly`,
475
+ `form.fieldReadonly(name)`) — until the person takes it away with Change
476
+ answers, asked first, and signs again. `signedBy(fields, values)` says which
477
+ signature locks which answer; `evidenceOf(value)` reads one. The evidence
478
+ travels in the value: a store that keeps a signature as bytes keeps `signed`
479
+ beside them (a save storing the picture keeps it, as it keeps a typed name's
480
+ `text`). `form.now()`, `form.user()` and `form.shownFields()` are what a
481
+ signature reads — the clock, the person, the answers shown.
482
+
483
+ **The canonical form, `fieldia-signature/1`.** The fingerprint is the SHA-256,
484
+ in small hexadecimal letters, of the UTF-8 bytes of this JSON, written as
485
+ RFC 8785 (JCS) writes JSON — no spaces, each object's keys in the order of their
486
+ UTF-16 code units, numbers as JavaScript writes them:
487
+
488
+ ```json
489
+ {"answers":{"areas":["forehead"],"patient":"Mona Adel","risks_read":true,"treatment":"botox"},"at":"2026-10-19T08:42:00.000Z","by":"Mona Adel","field":"signature","format":"fieldia-signature/1","page":"clinic-consent"}
490
+ ```
491
+
492
+ — the text of the evidence above, whose SHA-256 is its `sha256`.
493
+
494
+ `answers` holds each field the evidence names, as the person saw it as they
495
+ signed: nothing (null, an empty text, no choice) is `null`; yes and no `true`
496
+ and `false`; a number itself; a text, a choice's value, a date and a time their
497
+ text as kept; a date and time kept with its zone, its instant in UTC with
498
+ milliseconds; a link its record's id (several, their ids in order); a reference
499
+ `{ "id", "model" }`; a file `{ "name", "size", "type" }`; a table's lines an
500
+ array of each line's own fields; anything else its JSON by these rules. `by` is
501
+ left out when no one is named; `page` is the page's id and `field` the
502
+ signature's. So an app checks a record it keeps in any language — or with the
503
+ evidence part's own functions (@fieldia/widgets' `evidencePart`, the part a
504
+ page whose signature signs answers loads), in a browser or on a server:
505
+
506
+ ```ts
507
+ import { evidencePart } from '@fieldia/widgets';
508
+
509
+ const { answersMatch, signatureText, sha256Hex } = await evidencePart.load();
510
+ const same = await answersMatch(record.signature.signed, page, 'signature', record); // true while no signed answer changed
511
+ ```
512
+
513
+ Only a signature the page has sign answers locks them (`signsAnswers(node)`):
514
+ a page whose signature no longer says `signs` treats it as a picture, whatever
515
+ its value carries.
516
+
517
+ `checkPage` and `validatePage` refuse `signs` that is neither `"form"` nor a
518
+ list of the page's fields, a signature signing itself, and a `signerField` that
519
+ holds no name. Two people signing in turn is the app's (a flow's) to order: each
520
+ signature signs its answers.
521
+
522
+ ## Opening, closing and limiting a form
523
+
524
+ A page of responses may say when it takes answers, and how many; any choice's
525
+ options may say how many may pick each:
526
+
527
+ ```json
528
+ {
529
+ "limits": { "opens": "2026-11-01T09:00", "closes": "2026-11-20", "maxResponses": 200, "onePerPerson": true, "message": "See you in December." },
530
+ "fields": { "session": { "type": "selection", "label": "Session", "options": [{ "value": "am", "label": "Morning", "seats": 30 }] } }
531
+ }
532
+ ```
533
+
534
+ `opens` and `closes` are read on the page's clock (`timeZone`): a day and a
535
+ time, or a day alone — opening at its first minute, closing at its end. The
536
+ data source's `limits({ pageId, model, recordId, fields, user })` answers
537
+ `{ responses, mine, perChoice }`: the responses sent so far, this person's, and
538
+ for each choice field asked how many hold each option (the record edited not
539
+ counted). The form asks it as it opens — `state.limits.state` is `asking`
540
+ meanwhile where the page counts responses — after a record loads, after every
541
+ send, as it starts again, and just before its answers go. `state.limits` tells
542
+ whether it takes answers now (`open`, `soon`, `closed`, `full`, `sent`), when it
543
+ opens and closes, the responses it takes still, and each choice's places
544
+ (`seats[field][value]`: `{ seats, taken, left }`); `takingWords(page, limits,
545
+ messages)` says why it takes none ("This form closed on 20 Nov 2026."). Every
546
+ message that names a day writes it as the page's date boxes do — its
547
+ `look.dateOrder` (the form puts it on its messages, `messages.dateOrder`), its
548
+ language, a time on its clock: "can’t be after 20 Oct 2026", "Oct 20, 2026"
549
+ month first; `dayWords(day, messages, order?)` is that rule (a time after it: `whenWords`). A full option the person picks is refused,
550
+ said under its field ("Afternoon workshop is full"); one that filled up while
551
+ held, or a page closed since it was opened, stops the send. The app's own
552
+ `setValues` is never refused: the check says it. Without `limits` in the
553
+ source, the page keeps to its dates alone. The memory source counts its own
554
+ responses over its `limits` option's starting counts, and
555
+ `answeredElsewhere(pageId, values)` takes one as someone else would.
556
+
557
+ ## Save and resume on another device
558
+
559
+ A page with `continueLater` keeps its answers on the app's server as they are
560
+ typed — and as a wizard's step is entered — through the data source's
561
+ `saveDraft({ pageId, model, recordId, key, draft, later })`, which answers with
562
+ the draft's key (and, on Continue later, the `link` to carry on from and words
563
+ of its own); `loadDraft({ pageId, key })` gives the draft back. A draft keeps
564
+ the answers, the wizard's step and the steps passed over, text typed that does
565
+ not read (`unread`, as typed, with why) and the fields answered. The form's
566
+ `resume` (the key from the link) opens it — at its step, or the nearest before
567
+ it that still applies, its unread text said again — and a key that names none
568
+ leaves `state.resume.missing`. `form.continueLater()` reads what is typed and
569
+ keeps the draft (`later: true`); `form.canContinueLater()` says whether the
570
+ page and its source can; the `draft` event tells each draft kept. A send
571
+ carries the draft's key (`SubmitRequest.draft`, `SaveRequest.draft`). Without
572
+ the two members, drafts stay in the browser's store (`drafts.store`), as before
573
+ — with their wizard step now; with them, that store keeps only the key.
574
+
575
+ ```ts
576
+ const form = createForm({ page, dataSource, resume: new URLSearchParams(location.search).get('resume') ?? undefined });
577
+ form.on('draft', ({ key, later, saved }) => later && console.log('Continue from', saved.link));
578
+ ```
579
+
257
580
  ## What a page's records are called
258
581
 
259
582
  A page may say what its records are, so Fieldia's own sentences name them as
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fieldia/core",
3
- "version": "0.26.1",
3
+ "version": "0.28.0",
4
4
  "description": "Headless form model for Fieldia: the page format, modifiers, validation and record state. No DOM.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://fieldia.dev",