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