@pilotiq/pilotiq 0.24.1 → 0.24.2

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 (480) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/boost/guidelines.md +566 -0
  3. package/boost/skills/pilotiq-fields/SKILL.md +47 -0
  4. package/boost/skills/pilotiq-fields/rules/field-catalog.md +288 -0
  5. package/boost/skills/pilotiq-fields/rules/reactive-fields.md +199 -0
  6. package/boost/skills/pilotiq-fields/rules/validation.md +198 -0
  7. package/boost/skills/pilotiq-relations/SKILL.md +47 -0
  8. package/boost/skills/pilotiq-relations/rules/relation-managers.md +256 -0
  9. package/boost/skills/pilotiq-relations/rules/repeater-relationship.md +177 -0
  10. package/boost/skills/pilotiq-resource/SKILL.md +61 -0
  11. package/boost/skills/pilotiq-resource/rules/authorization.md +242 -0
  12. package/boost/skills/pilotiq-resource/rules/defining-resources.md +228 -0
  13. package/boost/skills/pilotiq-resource/rules/page-overrides.md +296 -0
  14. package/package.json +6 -1
  15. package/.turbo/turbo-build.log +0 -8
  16. package/CLAUDE.md +0 -265
  17. package/src/Cluster.test.ts +0 -283
  18. package/src/Cluster.ts +0 -83
  19. package/src/Column.test.ts +0 -199
  20. package/src/Column.ts +0 -710
  21. package/src/Global.test.ts +0 -367
  22. package/src/Global.ts +0 -169
  23. package/src/Page.test.ts +0 -114
  24. package/src/Page.ts +0 -208
  25. package/src/Pilotiq.perf.test.ts +0 -252
  26. package/src/Pilotiq.test.ts +0 -129
  27. package/src/Pilotiq.ts +0 -1158
  28. package/src/PilotiqRegistry.ts +0 -36
  29. package/src/PilotiqServiceProvider.ts +0 -121
  30. package/src/RelationManager.test.ts +0 -400
  31. package/src/RelationManager.ts +0 -527
  32. package/src/RenderHook.test.ts +0 -252
  33. package/src/RenderHook.ts +0 -242
  34. package/src/Resource.test.ts +0 -284
  35. package/src/Resource.ts +0 -526
  36. package/src/RightPanel.test.ts +0 -202
  37. package/src/RightPanel.ts +0 -132
  38. package/src/Tab.test.ts +0 -91
  39. package/src/Tab.ts +0 -156
  40. package/src/UserMenuItem.ts +0 -145
  41. package/src/actions/Action.test.ts +0 -2526
  42. package/src/actions/Action.ts +0 -1515
  43. package/src/actions/ActionGroup.test.ts +0 -112
  44. package/src/actions/ActionGroup.ts +0 -173
  45. package/src/actions/attachFactory.ts +0 -172
  46. package/src/actions/bulkFactories.ts +0 -168
  47. package/src/actions/crudFactories.ts +0 -220
  48. package/src/actions/exportFactory.ts +0 -225
  49. package/src/actions/factoryHelpers.ts +0 -177
  50. package/src/actions/importFactory.ts +0 -243
  51. package/src/actions/index.ts +0 -17
  52. package/src/actions/m2mFactories.ts +0 -193
  53. package/src/actions/relationFactories.ts +0 -372
  54. package/src/applyPageHooks.test.ts +0 -463
  55. package/src/applyPageHooks.ts +0 -330
  56. package/src/authorization.test.ts +0 -483
  57. package/src/breadcrumbs.test.ts +0 -238
  58. package/src/cells/coerce.test.ts +0 -85
  59. package/src/cells/coerce.ts +0 -84
  60. package/src/clusterPaths.ts +0 -35
  61. package/src/columns/BadgeColumn.test.ts +0 -54
  62. package/src/columns/BadgeColumn.ts +0 -32
  63. package/src/columns/BooleanColumn.test.ts +0 -41
  64. package/src/columns/BooleanColumn.ts +0 -18
  65. package/src/columns/ColorColumn.test.ts +0 -37
  66. package/src/columns/ColorColumn.ts +0 -38
  67. package/src/columns/IconColumn.test.ts +0 -54
  68. package/src/columns/IconColumn.ts +0 -37
  69. package/src/columns/ImageColumn.test.ts +0 -41
  70. package/src/columns/ImageColumn.ts +0 -28
  71. package/src/columns/SelectColumn.ts +0 -98
  72. package/src/columns/TextColumn.test.ts +0 -190
  73. package/src/columns/TextColumn.ts +0 -20
  74. package/src/columns/TextInputColumn.ts +0 -68
  75. package/src/columns/ToggleColumn.ts +0 -46
  76. package/src/columns/editableColumns.test.ts +0 -238
  77. package/src/columns/index.ts +0 -9
  78. package/src/defaultGlobalPages.ts +0 -95
  79. package/src/defaultPages.test.ts +0 -634
  80. package/src/defaultPages.ts +0 -617
  81. package/src/defaultViewPage.test.ts +0 -147
  82. package/src/elements/Form.test.ts +0 -223
  83. package/src/elements/Form.ts +0 -416
  84. package/src/elements/ListTabs.ts +0 -28
  85. package/src/elements/Table.test.ts +0 -422
  86. package/src/elements/Table.ts +0 -850
  87. package/src/elements/TableGroup.test.ts +0 -260
  88. package/src/elements/TableGroup.ts +0 -334
  89. package/src/elements/dispatchAction.test.ts +0 -463
  90. package/src/elements/dispatchAction.ts +0 -355
  91. package/src/elements/dispatchForm.test.ts +0 -477
  92. package/src/elements/dispatchForm.ts +0 -1993
  93. package/src/elements/dispatchTable.test.ts +0 -1514
  94. package/src/elements/dispatchTable.ts +0 -745
  95. package/src/elements/index.ts +0 -21
  96. package/src/entries/BadgeEntry.ts +0 -39
  97. package/src/entries/CodeEntry.test.ts +0 -40
  98. package/src/entries/CodeEntry.ts +0 -52
  99. package/src/entries/ColorEntry.ts +0 -63
  100. package/src/entries/ComponentEntry.test.ts +0 -173
  101. package/src/entries/ComponentEntry.ts +0 -95
  102. package/src/entries/Entry.ts +0 -304
  103. package/src/entries/IconEntry.ts +0 -49
  104. package/src/entries/ImageEntry.ts +0 -61
  105. package/src/entries/KeyValueEntry.ts +0 -47
  106. package/src/entries/RepeatableEntry.test.ts +0 -239
  107. package/src/entries/RepeatableEntry.ts +0 -173
  108. package/src/entries/TextEntry.test.ts +0 -394
  109. package/src/entries/TextEntry.ts +0 -60
  110. package/src/entries/index.ts +0 -12
  111. package/src/entries/leaves.test.ts +0 -306
  112. package/src/entries/registry.ts +0 -54
  113. package/src/fields/BuilderField.test.ts +0 -1188
  114. package/src/fields/BuilderField.ts +0 -605
  115. package/src/fields/BuilderRelationship.test.ts +0 -811
  116. package/src/fields/CheckboxField.test.ts +0 -44
  117. package/src/fields/CheckboxField.ts +0 -27
  118. package/src/fields/CheckboxListField.test.ts +0 -99
  119. package/src/fields/CheckboxListField.ts +0 -66
  120. package/src/fields/ColorPickerField.test.ts +0 -33
  121. package/src/fields/ColorPickerField.ts +0 -25
  122. package/src/fields/DateField.ts +0 -54
  123. package/src/fields/DateTimeField.test.ts +0 -55
  124. package/src/fields/EmailField.ts +0 -16
  125. package/src/fields/Field.test.ts +0 -654
  126. package/src/fields/Field.ts +0 -817
  127. package/src/fields/FileUploadField.test.ts +0 -143
  128. package/src/fields/FileUploadField.ts +0 -159
  129. package/src/fields/HiddenField.test.ts +0 -27
  130. package/src/fields/HiddenField.ts +0 -28
  131. package/src/fields/KeyValueField.test.ts +0 -105
  132. package/src/fields/KeyValueField.ts +0 -55
  133. package/src/fields/MarkdownField.test.ts +0 -167
  134. package/src/fields/MarkdownField.ts +0 -162
  135. package/src/fields/NumberField.ts +0 -33
  136. package/src/fields/RadioField.test.ts +0 -94
  137. package/src/fields/RadioField.ts +0 -67
  138. package/src/fields/RepeaterField.test.ts +0 -1806
  139. package/src/fields/RepeaterField.ts +0 -939
  140. package/src/fields/RepeaterRelationship.test.ts +0 -1923
  141. package/src/fields/RepeaterSimple.test.ts +0 -248
  142. package/src/fields/RowButton.test.ts +0 -219
  143. package/src/fields/RowButton.ts +0 -135
  144. package/src/fields/SelectField.test.ts +0 -192
  145. package/src/fields/SelectField.ts +0 -235
  146. package/src/fields/SliderField.test.ts +0 -50
  147. package/src/fields/SliderField.ts +0 -53
  148. package/src/fields/SlugField.ts +0 -24
  149. package/src/fields/TagsInputField.test.ts +0 -154
  150. package/src/fields/TagsInputField.ts +0 -133
  151. package/src/fields/TextField.test.ts +0 -213
  152. package/src/fields/TextField.ts +0 -177
  153. package/src/fields/TextareaField.test.ts +0 -58
  154. package/src/fields/TextareaField.ts +0 -59
  155. package/src/fields/ToggleButtonsField.test.ts +0 -106
  156. package/src/fields/ToggleButtonsField.ts +0 -59
  157. package/src/fields/ToggleField.ts +0 -16
  158. package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +0 -319
  159. package/src/fields/optionsResolver.ts +0 -95
  160. package/src/fields/resolveField.ts +0 -28
  161. package/src/filters/BooleanFilter.ts +0 -35
  162. package/src/filters/DateRangeFilter.test.ts +0 -194
  163. package/src/filters/DateRangeFilter.ts +0 -148
  164. package/src/filters/Filter.test.ts +0 -268
  165. package/src/filters/Filter.ts +0 -184
  166. package/src/filters/FormFilter.test.ts +0 -238
  167. package/src/filters/FormFilter.ts +0 -215
  168. package/src/filters/MultiSelectFilter.test.ts +0 -119
  169. package/src/filters/MultiSelectFilter.ts +0 -78
  170. package/src/filters/QueryBuilderFilter.test.ts +0 -662
  171. package/src/filters/QueryBuilderFilter.ts +0 -398
  172. package/src/filters/SelectFilter.ts +0 -46
  173. package/src/filters/TernaryFilter.test.ts +0 -160
  174. package/src/filters/TernaryFilter.ts +0 -72
  175. package/src/filters/TrashedFilter.test.ts +0 -149
  176. package/src/filters/TrashedFilter.ts +0 -55
  177. package/src/filters/queryBuilder/BooleanConstraint.ts +0 -31
  178. package/src/filters/queryBuilder/Constraint.ts +0 -115
  179. package/src/filters/queryBuilder/DateConstraint.ts +0 -69
  180. package/src/filters/queryBuilder/NumberConstraint.ts +0 -66
  181. package/src/filters/queryBuilder/SelectConstraint.ts +0 -72
  182. package/src/filters/queryBuilder/TextConstraint.ts +0 -64
  183. package/src/filters/queryBuilder/index.ts +0 -12
  184. package/src/icons/index.ts +0 -2
  185. package/src/icons/lucide.ts +0 -204
  186. package/src/icons/registry.test.ts +0 -56
  187. package/src/icons/registry.ts +0 -41
  188. package/src/icons/types.ts +0 -47
  189. package/src/index.ts +0 -525
  190. package/src/io/csv.test.ts +0 -142
  191. package/src/io/csv.ts +0 -170
  192. package/src/nestedRelationManagerData.test.ts +0 -547
  193. package/src/notifications/Notification.test.ts +0 -210
  194. package/src/notifications/Notification.ts +0 -354
  195. package/src/notifications/broadcast.test.ts +0 -110
  196. package/src/notifications/broadcast.ts +0 -95
  197. package/src/notifications/database.test.ts +0 -383
  198. package/src/notifications/database.ts +0 -398
  199. package/src/notifications/databaseNotifications.test.ts +0 -187
  200. package/src/notifications/dispatchNotificationAction.test.ts +0 -341
  201. package/src/notifications/dispatchNotificationAction.ts +0 -142
  202. package/src/notifications/flash.test.ts +0 -89
  203. package/src/notifications/flash.ts +0 -71
  204. package/src/notifications/index.ts +0 -45
  205. package/src/notifications/registerBroadcastAuth.test.ts +0 -134
  206. package/src/notifications/registerBroadcastAuth.ts +0 -100
  207. package/src/notifications/resolveSavedNotification.test.ts +0 -82
  208. package/src/notifications/resolveSavedNotification.ts +0 -59
  209. package/src/notifications/types.ts +0 -93
  210. package/src/orm/m2mAccessor.ts +0 -66
  211. package/src/orm/modelDefaults.test.ts +0 -633
  212. package/src/orm/modelDefaults.ts +0 -666
  213. package/src/pageData/breadcrumbs.ts +0 -288
  214. package/src/pageData/forms.ts +0 -578
  215. package/src/pageData/helpers.ts +0 -857
  216. package/src/pageData/misc.ts +0 -347
  217. package/src/pageData/navigation.ts +0 -842
  218. package/src/pageData/relationPages.ts +0 -1248
  219. package/src/pageData/relationTabs.ts +0 -286
  220. package/src/pageData/resourcePages.ts +0 -609
  221. package/src/pageData.test.ts +0 -1545
  222. package/src/pageData.ts +0 -341
  223. package/src/plugins/index.ts +0 -8
  224. package/src/plugins/themeEditor.test.ts +0 -36
  225. package/src/plugins/themeEditor.ts +0 -45
  226. package/src/react/AppShell.tsx +0 -251
  227. package/src/react/CollabExtensionFactoryRegistry.ts +0 -55
  228. package/src/react/CollabRoomContext.ts +0 -98
  229. package/src/react/CollabTextRendererRegistry.ts +0 -102
  230. package/src/react/CommandPalette.tsx +0 -375
  231. package/src/react/CurrentUserContext.tsx +0 -50
  232. package/src/react/CustomPageWrapperGate.tsx +0 -69
  233. package/src/react/CustomPageWrapperRegistry.ts +0 -45
  234. package/src/react/FieldFocusReporterRegistry.ts +0 -37
  235. package/src/react/FieldLabelSlotRegistry.ts +0 -30
  236. package/src/react/FieldPresenceRegistry.ts +0 -46
  237. package/src/react/FormCollabBindingRegistry.ts +0 -242
  238. package/src/react/FormStateContext.tsx +0 -591
  239. package/src/react/HeadHooks.tsx +0 -126
  240. package/src/react/MarkdownEditorRegistry.test.ts +0 -38
  241. package/src/react/MarkdownEditorRegistry.ts +0 -107
  242. package/src/react/NotificationActionStrip.tsx +0 -263
  243. package/src/react/NotificationBell.tsx +0 -426
  244. package/src/react/PendingSuggestionApplierRegistry.test.ts +0 -97
  245. package/src/react/PendingSuggestionApplierRegistry.ts +0 -98
  246. package/src/react/PendingSuggestionOverlayRegistry.ts +0 -54
  247. package/src/react/PendingSuggestionsContext.tsx +0 -172
  248. package/src/react/RecordWrapperGate.tsx +0 -58
  249. package/src/react/RecordWrapperRegistry.ts +0 -39
  250. package/src/react/RenderHookSlot.tsx +0 -32
  251. package/src/react/RightSidebar.tsx +0 -257
  252. package/src/react/RightSidebarContext.tsx +0 -234
  253. package/src/react/RightSidebarTrigger.tsx +0 -53
  254. package/src/react/RowCoordsContext.tsx +0 -23
  255. package/src/react/SchemaRenderer.tsx +0 -549
  256. package/src/react/SearchTrigger.tsx +0 -46
  257. package/src/react/ThemeProvider.tsx +0 -93
  258. package/src/react/ThemeSettingsPage.tsx +0 -579
  259. package/src/react/ThemeToggle.tsx +0 -20
  260. package/src/react/Toaster.tsx +0 -158
  261. package/src/react/UserMenu.tsx +0 -196
  262. package/src/react/WidgetDataContext.tsx +0 -157
  263. package/src/react/cells/EditableCell.tsx +0 -389
  264. package/src/react/component-slots.test.ts +0 -103
  265. package/src/react/component-slots.ts +0 -116
  266. package/src/react/fieldJsHandler.test.ts +0 -166
  267. package/src/react/fieldJsHandler.ts +0 -79
  268. package/src/react/fields/BuilderInput.tsx +0 -1078
  269. package/src/react/fields/CheckboxInput.tsx +0 -39
  270. package/src/react/fields/CheckboxListInput.tsx +0 -102
  271. package/src/react/fields/ColorInput.tsx +0 -71
  272. package/src/react/fields/DateFieldInput.tsx +0 -70
  273. package/src/react/fields/DateTimeInput.tsx +0 -62
  274. package/src/react/fields/FieldShell.tsx +0 -348
  275. package/src/react/fields/FileUploadInput.tsx +0 -639
  276. package/src/react/fields/HiddenInput.tsx +0 -17
  277. package/src/react/fields/KeyValueInput.tsx +0 -230
  278. package/src/react/fields/MarkdownInput.tsx +0 -560
  279. package/src/react/fields/RadioInput.tsx +0 -81
  280. package/src/react/fields/RepeaterInput.test.ts +0 -116
  281. package/src/react/fields/RepeaterInput.tsx +0 -1420
  282. package/src/react/fields/SelectFieldInput.tsx +0 -280
  283. package/src/react/fields/SliderInput.tsx +0 -81
  284. package/src/react/fields/TagsInput.tsx +0 -283
  285. package/src/react/fields/TextLikeInput.tsx +0 -256
  286. package/src/react/fields/ToggleButtonsInput.tsx +0 -60
  287. package/src/react/fields/ToggleFieldInput.tsx +0 -56
  288. package/src/react/fields/relationshipRenameDispatch.test.ts +0 -106
  289. package/src/react/fields/relationshipRenameDispatch.ts +0 -97
  290. package/src/react/fields/repeaterReconcile.test.ts +0 -114
  291. package/src/react/fields/repeaterReconcile.ts +0 -104
  292. package/src/react/fields/rowChromeButton.tsx +0 -336
  293. package/src/react/fields/rowState.ts +0 -106
  294. package/src/react/fields/syncRowGates.test.ts +0 -202
  295. package/src/react/fields/syncRowGates.ts +0 -66
  296. package/src/react/fields/textInputControls.tsx +0 -238
  297. package/src/react/fields/useRowReorderDnd.ts +0 -78
  298. package/src/react/formStateHelpers.test.ts +0 -508
  299. package/src/react/formStateHelpers.ts +0 -381
  300. package/src/react/hooks/use-mobile.ts +0 -19
  301. package/src/react/icon-context.tsx +0 -60
  302. package/src/react/index.ts +0 -194
  303. package/src/react/layouts/SidebarLayout.tsx +0 -250
  304. package/src/react/layouts/TopbarLayout.tsx +0 -258
  305. package/src/react/navigate.tsx +0 -37
  306. package/src/react/onProviderSynced.test.ts +0 -90
  307. package/src/react/parseRecordEditUrl.test.ts +0 -122
  308. package/src/react/parseRecordEditUrl.ts +0 -94
  309. package/src/react/persistedState.ts +0 -40
  310. package/src/react/registry.ts +0 -48
  311. package/src/react/right-panel-registry.tsx +0 -47
  312. package/src/react/schemaRenderer/AlertRenderer.tsx +0 -112
  313. package/src/react/schemaRenderer/EntryRenderer.tsx +0 -501
  314. package/src/react/schemaRenderer/SectionRenderer.tsx +0 -120
  315. package/src/react/schemaRenderer/SimpleElements.tsx +0 -306
  316. package/src/react/schemaRenderer/TabsRenderer.tsx +0 -62
  317. package/src/react/schemaRenderer/WizardRenderer.tsx +0 -338
  318. package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +0 -177
  319. package/src/react/schemaRenderer/action/ActionModalDialog.tsx +0 -273
  320. package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +0 -61
  321. package/src/react/schemaRenderer/action/HandlerActionButton.tsx +0 -43
  322. package/src/react/schemaRenderer/action/MethodActionButton.tsx +0 -64
  323. package/src/react/schemaRenderer/action/buttons.tsx +0 -99
  324. package/src/react/schemaRenderer/action/helpers.ts +0 -140
  325. package/src/react/schemaRenderer/action/renderAction.tsx +0 -245
  326. package/src/react/schemaRenderer/columnFormat.ts +0 -65
  327. package/src/react/schemaRenderer/constants.ts +0 -50
  328. package/src/react/schemaRenderer/form/FormRenderer.tsx +0 -274
  329. package/src/react/schemaRenderer/form/renderField.tsx +0 -511
  330. package/src/react/schemaRenderer/helpers.tsx +0 -81
  331. package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +0 -308
  332. package/src/react/schemaRenderer/table/TableRenderer.tsx +0 -123
  333. package/src/react/schemaRenderer/table/TableRendererBody.tsx +0 -974
  334. package/src/react/schemaRenderer/table/filters.tsx +0 -1233
  335. package/src/react/schemaRenderer/table/formatCell.tsx +0 -264
  336. package/src/react/schemaRenderer/table/links.tsx +0 -112
  337. package/src/react/schemaRenderer/table/renderRowActions.tsx +0 -52
  338. package/src/react/schemaRenderer/table/url.tsx +0 -143
  339. package/src/react/theme-preview/apply.ts +0 -99
  340. package/src/react/theme-preview/build-html.ts +0 -436
  341. package/src/react/ui/button.tsx +0 -51
  342. package/src/react/ui/calendar.tsx +0 -67
  343. package/src/react/ui/checkbox.tsx +0 -29
  344. package/src/react/ui/dialog.tsx +0 -108
  345. package/src/react/ui/dropdown-menu.tsx +0 -97
  346. package/src/react/ui/input.tsx +0 -20
  347. package/src/react/ui/label.tsx +0 -21
  348. package/src/react/ui/popover.tsx +0 -50
  349. package/src/react/ui/select.tsx +0 -169
  350. package/src/react/ui/separator.tsx +0 -25
  351. package/src/react/ui/sheet.tsx +0 -136
  352. package/src/react/ui/sidebar.tsx +0 -723
  353. package/src/react/ui/skeleton.tsx +0 -13
  354. package/src/react/ui/slider.tsx +0 -34
  355. package/src/react/ui/switch.tsx +0 -28
  356. package/src/react/ui/table.tsx +0 -105
  357. package/src/react/ui/tabs.tsx +0 -63
  358. package/src/react/ui/textarea.tsx +0 -18
  359. package/src/react/ui/tooltip.tsx +0 -64
  360. package/src/react/useResizableWidth.ts +0 -139
  361. package/src/react/utils.ts +0 -6
  362. package/src/react/widgetRegistry.test.ts +0 -43
  363. package/src/react/widgetRegistry.ts +0 -50
  364. package/src/react/widgets/StatsOverviewRenderer.tsx +0 -232
  365. package/src/react/widgets/TableWidgetRenderer.tsx +0 -231
  366. package/src/react/widgets/ViewRenderer.tsx +0 -71
  367. package/src/relationManagerData.test.ts +0 -1595
  368. package/src/richtext/index.ts +0 -8
  369. package/src/richtext/registry.ts +0 -89
  370. package/src/routes/globals.ts +0 -148
  371. package/src/routes/guard.test.ts +0 -325
  372. package/src/routes/helpers.ts +0 -704
  373. package/src/routes/pages.ts +0 -175
  374. package/src/routes/panel.ts +0 -204
  375. package/src/routes/relations.ts +0 -1243
  376. package/src/routes/resources.ts +0 -781
  377. package/src/routes/theme.ts +0 -91
  378. package/src/routes-nested-relations.test.ts +0 -676
  379. package/src/routes-relations.test.ts +0 -972
  380. package/src/routes.test.ts +0 -2027
  381. package/src/routes.ts +0 -303
  382. package/src/schema/Alert.test.ts +0 -109
  383. package/src/schema/Alert.ts +0 -131
  384. package/src/schema/Block.ts +0 -169
  385. package/src/schema/Breadcrumbs.ts +0 -40
  386. package/src/schema/Card.ts +0 -35
  387. package/src/schema/Divider.ts +0 -20
  388. package/src/schema/Element.ts +0 -219
  389. package/src/schema/EmptyState.test.ts +0 -37
  390. package/src/schema/EmptyState.ts +0 -63
  391. package/src/schema/Fieldset.ts +0 -43
  392. package/src/schema/Grid.ts +0 -43
  393. package/src/schema/Group.ts +0 -30
  394. package/src/schema/Heading.ts +0 -39
  395. package/src/schema/Html.ts +0 -67
  396. package/src/schema/Icon.ts +0 -54
  397. package/src/schema/Image.ts +0 -57
  398. package/src/schema/LinkTag.ts +0 -41
  399. package/src/schema/Markdown.ts +0 -85
  400. package/src/schema/MetaTag.ts +0 -41
  401. package/src/schema/RelationTabs.ts +0 -71
  402. package/src/schema/ScriptTag.ts +0 -55
  403. package/src/schema/Section.ts +0 -160
  404. package/src/schema/ServerDataElement.test.ts +0 -140
  405. package/src/schema/ServerDataElement.ts +0 -156
  406. package/src/schema/SlotComponent.test.ts +0 -77
  407. package/src/schema/SlotComponent.ts +0 -71
  408. package/src/schema/Split.ts +0 -50
  409. package/src/schema/Stat.test.ts +0 -118
  410. package/src/schema/Stat.ts +0 -154
  411. package/src/schema/StatsOverview.test.ts +0 -141
  412. package/src/schema/StatsOverview.ts +0 -119
  413. package/src/schema/StyleTag.ts +0 -35
  414. package/src/schema/TableWidget.test.ts +0 -297
  415. package/src/schema/TableWidget.ts +0 -289
  416. package/src/schema/Tabs.ts +0 -79
  417. package/src/schema/Text.ts +0 -58
  418. package/src/schema/UnorderedList.ts +0 -49
  419. package/src/schema/View.test.ts +0 -111
  420. package/src/schema/View.ts +0 -127
  421. package/src/schema/Wizard.ts +0 -220
  422. package/src/schema/containers.test.ts +0 -564
  423. package/src/schema/headTags.test.ts +0 -134
  424. package/src/schema/index.ts +0 -40
  425. package/src/schema/primes.test.ts +0 -269
  426. package/src/schema/resolveSchema.test.ts +0 -379
  427. package/src/schema/resolveSchema.ts +0 -917
  428. package/src/schema/sanitize.ts +0 -58
  429. package/src/search.test.ts +0 -446
  430. package/src/search.ts +0 -178
  431. package/src/sessionFilters.test.ts +0 -375
  432. package/src/sessionFilters.ts +0 -143
  433. package/src/slot-components/index.ts +0 -10
  434. package/src/slot-components/registry.ts +0 -56
  435. package/src/styles/file-upload.css +0 -13
  436. package/src/summarizers/Summarizer.test.ts +0 -84
  437. package/src/summarizers/Summarizer.ts +0 -123
  438. package/src/summarizers/index.ts +0 -11
  439. package/src/theme/base-colors.ts +0 -68
  440. package/src/theme/chart-colors.ts +0 -50
  441. package/src/theme/colors.ts +0 -447
  442. package/src/theme/generate-css.test.ts +0 -139
  443. package/src/theme/generate-css.ts +0 -44
  444. package/src/theme/generate-scale.test.ts +0 -106
  445. package/src/theme/generate-scale.ts +0 -97
  446. package/src/theme/icon-map.ts +0 -42
  447. package/src/theme/index.ts +0 -34
  448. package/src/theme/migrate.test.ts +0 -178
  449. package/src/theme/migrate.ts +0 -81
  450. package/src/theme/presets.ts +0 -135
  451. package/src/theme/radius.ts +0 -18
  452. package/src/theme/resolve.test.ts +0 -238
  453. package/src/theme/resolve.ts +0 -96
  454. package/src/theme/spacing.ts +0 -18
  455. package/src/theme/storage.test.ts +0 -126
  456. package/src/theme/storage.ts +0 -106
  457. package/src/theme/theme-colors.ts +0 -88
  458. package/src/theme/types.ts +0 -125
  459. package/src/uploads/UploadAdapter.ts +0 -35
  460. package/src/uploads/index.ts +0 -2
  461. package/src/uploads/localUpload.test.ts +0 -70
  462. package/src/uploads/localUpload.ts +0 -84
  463. package/src/validation/Validator.ts +0 -49
  464. package/src/validation/index.ts +0 -28
  465. package/src/validation/rules.ts +0 -78
  466. package/src/validation/runValidators.ts +0 -435
  467. package/src/validation/uniqueValidator.test.ts +0 -196
  468. package/src/validation/uniqueValidator.ts +0 -133
  469. package/src/validation/validators.test.ts +0 -268
  470. package/src/vite.test.ts +0 -184
  471. package/src/vite.ts +0 -787
  472. package/src/widgets/index.ts +0 -10
  473. package/src/widgets/registry.ts +0 -45
  474. package/src/widgets.test.ts +0 -592
  475. package/tsconfig.build.json +0 -11
  476. package/tsconfig.json +0 -4
  477. package/tsconfig.test.json +0 -10
  478. package/views/react/Dashboard.tsx +0 -27
  479. package/views/react/Resources/Form.tsx +0 -102
  480. package/views/react/Resources/Index.tsx +0 -49
@@ -1,49 +0,0 @@
1
- /**
2
- * Validation primitives.
3
- *
4
- * A `Validator` is a function: `(value, ctx?) => string | null | Promise<string | null>`.
5
- * It returns an error message when invalid, or `null` when the value
6
- * passes. Validators may carry an optional `serialized` descriptor — a
7
- * JSON-safe shape mirrored to the client via `Field.toMeta()` so the
8
- * browser can run the same rule for live UX before submit.
9
- *
10
- * The runtime is server-of-truth: client-side mirroring is best-effort.
11
- * If `serialized` is omitted, the validator runs only on the server.
12
- *
13
- * Async validators (e.g. `unique()` probing the DB) return a `Promise`;
14
- * the pipeline awaits each result before collecting the next field's
15
- * errors. They are NOT mirrored to the client — the descriptor is omitted
16
- * or marks itself `async: true` so the client skips the live check.
17
- */
18
-
19
- /** JSON-safe descriptor of a built-in rule. Mirrored to the client. */
20
- export interface SerializedRule {
21
- rule: string
22
- message?: string
23
- [key: string]: unknown
24
- }
25
-
26
- /**
27
- * Optional context passed to a validator. `values` is the full submit
28
- * payload — useful for cross-field rules (e.g. confirm-password). `record`
29
- * is the persisted record under edit, when present.
30
- */
31
- export interface ValidatorContext {
32
- values?: Record<string, unknown>
33
- record?: unknown
34
- }
35
-
36
- export type ValidatorFn = (
37
- value: unknown,
38
- ctx?: ValidatorContext,
39
- ) => string | null | Promise<string | null>
40
-
41
- /** A validator function, optionally tagged with a serialized descriptor. */
42
- export type Validator = ValidatorFn & { serialized?: SerializedRule }
43
-
44
- /** Wrap a `ValidatorFn` and attach an optional serialized descriptor. */
45
- export function makeValidator(fn: ValidatorFn, serialized?: SerializedRule): Validator {
46
- const v = fn as Validator
47
- if (serialized) v.serialized = serialized
48
- return v
49
- }
@@ -1,28 +0,0 @@
1
- export {
2
- makeValidator,
3
- type Validator,
4
- type ValidatorFn,
5
- type ValidatorContext,
6
- type SerializedRule,
7
- } from './Validator.js'
8
-
9
- export {
10
- required,
11
- email,
12
- minLength,
13
- maxLength,
14
- min,
15
- max,
16
- pattern,
17
- } from './rules.js'
18
-
19
- export {
20
- validateSchema,
21
- isValid,
22
- type ValidationErrors,
23
- } from './runValidators.js'
24
-
25
- export {
26
- unique,
27
- type UniqueOptions,
28
- } from './uniqueValidator.js'
@@ -1,78 +0,0 @@
1
- import { makeValidator, type Validator } from './Validator.js'
2
-
3
- const isEmpty = (v: unknown): boolean => v === undefined || v === null || v === ''
4
-
5
- export function required(message = 'This field is required'): Validator {
6
- return makeValidator(
7
- v => (isEmpty(v) ? message : null),
8
- { rule: 'required', message },
9
- )
10
- }
11
-
12
- const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
13
-
14
- export function email(message = 'Must be a valid email'): Validator {
15
- return makeValidator(
16
- v => {
17
- if (isEmpty(v)) return null
18
- if (typeof v !== 'string' || !EMAIL_RE.test(v)) return message
19
- return null
20
- },
21
- { rule: 'email', message },
22
- )
23
- }
24
-
25
- export function minLength(n: number, message = `Must be at least ${n} characters`): Validator {
26
- return makeValidator(
27
- v => {
28
- if (isEmpty(v)) return null
29
- if (typeof v !== 'string') return null
30
- return v.length < n ? message : null
31
- },
32
- { rule: 'minLength', value: n, message },
33
- )
34
- }
35
-
36
- export function maxLength(n: number, message = `Must be at most ${n} characters`): Validator {
37
- return makeValidator(
38
- v => {
39
- if (isEmpty(v)) return null
40
- if (typeof v !== 'string') return null
41
- return v.length > n ? message : null
42
- },
43
- { rule: 'maxLength', value: n, message },
44
- )
45
- }
46
-
47
- export function min(n: number, message = `Must be at least ${n}`): Validator {
48
- return makeValidator(
49
- v => {
50
- if (isEmpty(v)) return null
51
- if (typeof v !== 'number') return null
52
- return v < n ? message : null
53
- },
54
- { rule: 'min', value: n, message },
55
- )
56
- }
57
-
58
- export function max(n: number, message = `Must be at most ${n}`): Validator {
59
- return makeValidator(
60
- v => {
61
- if (isEmpty(v)) return null
62
- if (typeof v !== 'number') return null
63
- return v > n ? message : null
64
- },
65
- { rule: 'max', value: n, message },
66
- )
67
- }
68
-
69
- export function pattern(regex: RegExp, message = 'Invalid format'): Validator {
70
- return makeValidator(
71
- v => {
72
- if (isEmpty(v)) return null
73
- if (typeof v !== 'string') return null
74
- return regex.test(v) ? null : message
75
- },
76
- { rule: 'pattern', source: regex.source, flags: regex.flags, message },
77
- )
78
- }
@@ -1,435 +0,0 @@
1
- import { Element } from '../schema/Element.js'
2
- import { Field, type DistinctOptions } from '../fields/Field.js'
3
- import { RepeaterField } from '../fields/RepeaterField.js'
4
- import { BuilderField } from '../fields/BuilderField.js'
5
-
6
- export interface ValidationErrors {
7
- [fieldName: string]: string[]
8
- }
9
-
10
- /**
11
- * Walk an Element tree, run every Field's validators against the matching
12
- * value in `values`, and return a `{ name -> errors[] }` map.
13
- *
14
- * The map is empty when every field passes. Fields with no validators
15
- * (and no `required` flag) never appear in the output. The caller decides
16
- * whether a non-empty map should reject the submit.
17
- *
18
- * `record` is the persisted record under edit, when present — it propagates
19
- * to each validator's `ctx` for cross-field rules that need it.
20
- *
21
- * Plan #14 — Repeater fields validate each row's inner schema recursively.
22
- * Per-row errors are flat-keyed as `${fieldName}.${i}.${childName}` so the
23
- * client can surface them inline on the right row. `minItems` / `maxItems`
24
- * land under the bare repeater name.
25
- *
26
- * Async — `Field.runValidators` is awaited per field, so async validators
27
- * (e.g. `unique()` probing the DB) work transparently. The walker is still
28
- * sequential per field within the same scope to preserve declaration order
29
- * in the error output; cross-field parallelism would only matter on forms
30
- * with many DB-probing validators and is left for a future profile.
31
- */
32
- export async function validateSchema(
33
- elements: Element[],
34
- values: Record<string, unknown>,
35
- record?: unknown,
36
- ): Promise<ValidationErrors> {
37
- const errors: ValidationErrors = {}
38
- const fields: Field[] = []
39
- const repeaters: RepeaterField[] = []
40
- const builders: BuilderField[] = []
41
-
42
- // Two-pass to preserve order: collect first (sync walk), then await each.
43
- walk(elements, el => {
44
- if (el instanceof RepeaterField) { repeaters.push(el); return }
45
- if (el instanceof BuilderField) { builders.push(el); return }
46
- if (el instanceof Field) fields.push(el)
47
- })
48
-
49
- for (const el of fields) {
50
- const value = values[el.name]
51
- const ctx: { values: Record<string, unknown>; record?: unknown } = { values }
52
- if (record !== undefined) ctx.record = record
53
- const fieldErrors = await el.runValidators(value, ctx)
54
- if (fieldErrors.length > 0) errors[el.name] = fieldErrors
55
- }
56
-
57
- for (const rep of repeaters) {
58
- const raw = values[rep.name]
59
- const rows = Array.isArray(raw) ? raw : foldFlatRepeaterRows(values, rep.name)
60
- await validateRepeater(rep, rows, record, errors)
61
- }
62
-
63
- for (const bld of builders) {
64
- const raw = values[bld.name]
65
- const rows = Array.isArray(raw) ? raw : foldFlatBuilderRows(values, bld.name)
66
- await validateBuilder(bld, rows, record, errors)
67
- }
68
-
69
- return errors
70
- }
71
-
72
- async function validateRepeater(
73
- field: RepeaterField,
74
- raw: unknown,
75
- record: unknown,
76
- errors: ValidationErrors,
77
- ): Promise<void> {
78
- const rows = Array.isArray(raw) ? raw : []
79
- const baseErrors: string[] = []
80
-
81
- const min = field.getMinItems()
82
- if (min !== undefined && rows.length < min) {
83
- baseErrors.push(min === 1
84
- ? 'At least 1 item is required'
85
- : `At least ${min} items are required`)
86
- }
87
-
88
- const max = field.getMaxItems()
89
- if (max !== undefined && rows.length > max) {
90
- baseErrors.push(max === 1
91
- ? 'At most 1 item is allowed'
92
- : `At most ${max} items are allowed`)
93
- }
94
-
95
- if (baseErrors.length > 0) errors[field.name] = baseErrors
96
-
97
- const inner = field.getInnerSchema()
98
- const simpleInner = field.getSimpleInnerField()
99
- for (let i = 0; i < rows.length; i++) {
100
- const row = rows[i]
101
- // For `simple()` repeaters, wrap primitive entries inline so per-field
102
- // validators run against the standard `{ <innerName>: v }` shape. A
103
- // primitive in a non-simple Repeater is genuinely malformed and gets
104
- // skipped (the prior posture).
105
- let rowValues: Record<string, unknown> | null
106
- if (row && typeof row === 'object' && !Array.isArray(row)) {
107
- rowValues = row as Record<string, unknown>
108
- } else if (simpleInner && row !== undefined) {
109
- rowValues = { [simpleInner.name]: row }
110
- } else {
111
- rowValues = null
112
- }
113
- if (!rowValues) continue
114
- const rowErrors = await validateSchema(inner, rowValues, record)
115
- for (const [childName, msgs] of Object.entries(rowErrors)) {
116
- errors[`${field.name}.${i}.${childName}`] = msgs
117
- }
118
- }
119
-
120
- checkDistinctRows({
121
- fieldName: field.name,
122
- innerFields: collectDistinctFields(inner),
123
- rows,
124
- errorKey: (rowIdx, childName) => `${field.name}.${rowIdx}.${childName}`,
125
- // `simple()` repeaters can hand bare primitive rows to the comparator;
126
- // when the row matches the inner field, treat the primitive itself as
127
- // the value. Object rows still read by child key as normal.
128
- extractValue: (row, childName) => {
129
- if (row && typeof row === 'object' && !Array.isArray(row)) {
130
- return (row as Record<string, unknown>)[childName]
131
- }
132
- if (simpleInner && simpleInner.name === childName) return row
133
- return undefined
134
- },
135
- rowMatches: () => true,
136
- errors,
137
- })
138
- }
139
-
140
- /**
141
- * Validate a single Builder field's rows. Per-row errors are flat-keyed
142
- * `${field.name}.${i}.data.${childName}` so the client can surface them
143
- * inline on the right row's inner field. `minItems` / `maxItems` /
144
- * `Block.maxItems` (per-block-type ceiling) and unknown-block-type
145
- * errors land under the bare builder name.
146
- */
147
- async function validateBuilder(
148
- field: BuilderField,
149
- raw: unknown,
150
- record: unknown,
151
- errors: ValidationErrors,
152
- ): Promise<void> {
153
- const rows = Array.isArray(raw) ? raw : []
154
- const baseErrors: string[] = []
155
-
156
- const min = field.getMinItems()
157
- if (min !== undefined && rows.length < min) {
158
- baseErrors.push(min === 1
159
- ? 'At least 1 item is required'
160
- : `At least ${min} items are required`)
161
- }
162
-
163
- const max = field.getMaxItems()
164
- if (max !== undefined && rows.length > max) {
165
- baseErrors.push(max === 1
166
- ? 'At most 1 item is allowed'
167
- : `At most ${max} items are allowed`)
168
- }
169
-
170
- // Per-block-type ceiling (`Block.maxItems`) is enforced post-row-walk
171
- // so we can count the rows of each type in one pass.
172
- const typeCounts = new Map<string, number>()
173
- for (let i = 0; i < rows.length; i++) {
174
- const row = rows[i]
175
- if (!row || typeof row !== 'object' || Array.isArray(row)) continue
176
- const t = (row as Record<string, unknown>)['type']
177
- if (typeof t === 'string' && t !== '') {
178
- typeCounts.set(t, (typeCounts.get(t) ?? 0) + 1)
179
- }
180
- }
181
- for (const block of field.getBlocks()) {
182
- const cap = block.getMaxItems()
183
- if (cap === undefined) continue
184
- const count = typeCounts.get(block.name) ?? 0
185
- if (count > cap) {
186
- baseErrors.push(cap === 1
187
- ? `At most 1 "${block.getLabel()}" block is allowed`
188
- : `At most ${cap} "${block.getLabel()}" blocks are allowed`)
189
- }
190
- }
191
-
192
- for (let i = 0; i < rows.length; i++) {
193
- const row = rows[i]
194
- if (!row || typeof row !== 'object' || Array.isArray(row)) continue
195
- const r = row as Record<string, unknown>
196
- const t = r['type']
197
- if (typeof t !== 'string' || t === '') {
198
- errors[`${field.name}.${i}`] = ['Block type is required']
199
- continue
200
- }
201
- const block = field.getBlock(t)
202
- if (!block) {
203
- errors[`${field.name}.${i}`] = [`Unknown block type "${t}"`]
204
- continue
205
- }
206
- const dataRaw = r['data']
207
- const data: Record<string, unknown> = (dataRaw && typeof dataRaw === 'object' && !Array.isArray(dataRaw))
208
- ? (dataRaw as Record<string, unknown>)
209
- : {}
210
- const rowErrors = await validateSchema(block.getSchema(), data, record)
211
- for (const [childName, msgs] of Object.entries(rowErrors)) {
212
- errors[`${field.name}.${i}.data.${childName}`] = msgs
213
- }
214
- }
215
-
216
- // Distinct check is per-block-type — comparing a `heading.title` against a
217
- // `paragraph.text` is meaningless (different schemas, different fields).
218
- // We re-walk the rows once per block type so each pass only sees rows
219
- // matching that type.
220
- for (const block of field.getBlocks()) {
221
- const distinctFields = collectDistinctFields(block.getSchema())
222
- if (distinctFields.length === 0) continue
223
- checkDistinctRows({
224
- fieldName: field.name,
225
- innerFields: distinctFields,
226
- rows,
227
- errorKey: (rowIdx, childName) => `${field.name}.${rowIdx}.data.${childName}`,
228
- extractValue: (row, childName) => {
229
- if (!row || typeof row !== 'object' || Array.isArray(row)) return undefined
230
- const r = row as Record<string, unknown>
231
- const data = r['data']
232
- if (!data || typeof data !== 'object' || Array.isArray(data)) return undefined
233
- return (data as Record<string, unknown>)[childName]
234
- },
235
- rowMatches: row => {
236
- if (!row || typeof row !== 'object' || Array.isArray(row)) return false
237
- return (row as Record<string, unknown>)['type'] === block.name
238
- },
239
- errors,
240
- })
241
- }
242
-
243
- if (baseErrors.length > 0) errors[field.name] = baseErrors
244
- }
245
-
246
- /**
247
- * Walk the inner schema for fields with `.distinct()` set. Stops at any
248
- * nested Repeater / Builder so the outer field's distinct pass doesn't
249
- * accidentally walk into a child array's leaves (those rows are
250
- * validated by their own array-field pass when they're submitted).
251
- */
252
- function collectDistinctFields(elements: Element[]): Array<{ field: Field; opts: DistinctOptions }> {
253
- const out: Array<{ field: Field; opts: DistinctOptions }> = []
254
- const visit = (els: Element[]): void => {
255
- for (const el of els) {
256
- if (el instanceof RepeaterField) continue
257
- if (el instanceof BuilderField) continue
258
- if (el instanceof Field) {
259
- const opts = el.getDistinct()
260
- if (opts) out.push({ field: el, opts })
261
- }
262
- const children = el.getChildren()
263
- if (children && children.length > 0) visit(children)
264
- }
265
- }
266
- visit(elements)
267
- return out
268
- }
269
-
270
- /**
271
- * Cross-row distinct comparison shared by Repeater + Builder. The first
272
- * occurrence of each value passes; subsequent rows with the same value
273
- * (after `caseInsensitive` / `ignoreNulls` normalization) get the
274
- * configured rejection message under their flat-keyed error path.
275
- *
276
- * `rowMatches` lets the Builder caller scope the comparison to a single
277
- * block type; Repeater always passes `() => true`.
278
- */
279
- function checkDistinctRows(args: {
280
- fieldName: string
281
- innerFields: Array<{ field: Field; opts: DistinctOptions }>
282
- rows: unknown[]
283
- errorKey: (rowIdx: number, childName: string) => string
284
- extractValue: (row: unknown, childName: string) => unknown
285
- rowMatches: (row: unknown) => boolean
286
- errors: ValidationErrors
287
- }): void {
288
- if (args.innerFields.length === 0) return
289
- for (const { field: child, opts } of args.innerFields) {
290
- const ignoreNulls = opts.ignoreNulls !== false // default true
291
- const seen = new Map<unknown, number>()
292
- for (let i = 0; i < args.rows.length; i++) {
293
- const row = args.rows[i]
294
- if (!args.rowMatches(row)) continue
295
- const value = args.extractValue(row, child.name)
296
- if (ignoreNulls && (value === undefined || value === null || value === '')) continue
297
- const key = (opts.caseInsensitive && typeof value === 'string')
298
- ? value.toLowerCase()
299
- : value
300
- if (!seen.has(key)) {
301
- seen.set(key, i)
302
- continue
303
- }
304
- const errKey = args.errorKey(i, child.name)
305
- const message = opts.message ?? 'Must be unique'
306
- const existing = args.errors[errKey]
307
- if (existing) existing.push(message)
308
- else args.errors[errKey] = [message]
309
- }
310
- }
311
- }
312
-
313
- /**
314
- * Group flat-key form-encoded Builder rows into an array of row bodies
315
- * shaped `{ __id?, type, data: {…} }`. Mirrors `coerceBuilderValue` in
316
- * `dispatchForm.ts`. Validation runs before coercion so we can't reuse
317
- * the coerced array directly.
318
- */
319
- function foldFlatBuilderRows(
320
- values: Record<string, unknown>,
321
- fieldName: string,
322
- ): Array<Record<string, unknown>> {
323
- const prefix = `${fieldName}.`
324
- const grouped = new Map<number, { __id?: string; type: string; data: Record<string, unknown> }>()
325
- let maxIdx = -1
326
- for (const key of Object.keys(values)) {
327
- if (!key.startsWith(prefix)) continue
328
- const rest = key.slice(prefix.length)
329
- const dot = rest.indexOf('.')
330
- if (dot < 0) continue
331
- const idxStr = rest.slice(0, dot)
332
- const tail = rest.slice(dot + 1)
333
- const idx = Number(idxStr)
334
- if (!Number.isInteger(idx) || idx < 0) continue
335
- if (idx > maxIdx) maxIdx = idx
336
- let row = grouped.get(idx)
337
- if (!row) { row = { type: '', data: {} }; grouped.set(idx, row) }
338
- const value = values[key]
339
- if (tail === '__id') {
340
- if (typeof value === 'string') row.__id = value
341
- } else if (tail === 'type') {
342
- if (typeof value === 'string') row.type = value
343
- } else if (tail.startsWith('data.')) {
344
- row.data[tail.slice('data.'.length)] = value
345
- }
346
- }
347
- if (maxIdx < 0) return []
348
- const out: Array<Record<string, unknown>> = []
349
- for (let i = 0; i <= maxIdx; i++) {
350
- const row = grouped.get(i) ?? { type: '', data: {} }
351
- const obj: Record<string, unknown> = { type: row.type, data: row.data }
352
- if (typeof row.__id === 'string') obj['__id'] = row.__id
353
- out.push(obj)
354
- }
355
- while (out.length > 0 && isBuilderRowEmpty(out[out.length - 1]!)) out.pop()
356
- return out
357
- }
358
-
359
- function isBuilderRowEmpty(row: Record<string, unknown>): boolean {
360
- const data = row['data']
361
- if (!data || typeof data !== 'object' || Array.isArray(data)) return true
362
- for (const [, v] of Object.entries(data as Record<string, unknown>)) {
363
- if (v === undefined || v === null || v === '') continue
364
- return false
365
- }
366
- return true
367
- }
368
-
369
- /** True when no field returned any error. */
370
- export function isValid(errors: ValidationErrors): boolean {
371
- return Object.keys(errors).length === 0
372
- }
373
-
374
- /**
375
- * Group flat-key form-encoded Repeater rows into an array of row bodies.
376
- * Mirrors the same fold path as `coerceRepeaterValue` in
377
- * `dispatchForm.ts` but kept private here so validation works against
378
- * raw bodies (validation runs before coercion in `dispatchFormSubmit`).
379
- *
380
- * Returns an empty array when no matching keys exist — the Repeater's
381
- * `minItems` validator then surfaces the right error.
382
- */
383
- function foldFlatRepeaterRows(
384
- values: Record<string, unknown>,
385
- fieldName: string,
386
- ): Array<Record<string, unknown>> {
387
- const prefix = `${fieldName}.`
388
- const grouped = new Map<number, Record<string, unknown>>()
389
- let maxIdx = -1
390
- for (const key of Object.keys(values)) {
391
- if (!key.startsWith(prefix)) continue
392
- const rest = key.slice(prefix.length)
393
- const dot = rest.indexOf('.')
394
- if (dot < 0) continue
395
- const idxStr = rest.slice(0, dot)
396
- const childKey = rest.slice(dot + 1)
397
- const idx = Number(idxStr)
398
- if (!Number.isInteger(idx) || idx < 0) continue
399
- if (idx > maxIdx) maxIdx = idx
400
- let row = grouped.get(idx)
401
- if (!row) { row = {}; grouped.set(idx, row) }
402
- row[childKey] = values[key]
403
- }
404
- if (maxIdx < 0) return []
405
- // Trim trailing untouched rows so `minItems` lines up with what
406
- // coercion will eventually persist.
407
- const out: Array<Record<string, unknown>> = []
408
- for (let i = 0; i <= maxIdx; i++) out.push(grouped.get(i) ?? {})
409
- while (out.length > 0 && isRowEmpty(out[out.length - 1]!)) out.pop()
410
- return out
411
- }
412
-
413
- function isRowEmpty(row: Record<string, unknown>): boolean {
414
- for (const [k, v] of Object.entries(row)) {
415
- if (k === '__id') continue
416
- if (v === undefined || v === null || v === '') continue
417
- return false
418
- }
419
- return true
420
- }
421
-
422
- function walk(elements: Element[], visit: (el: Element) => void): void {
423
- for (const el of elements) {
424
- visit(el)
425
- // Plan #14 — don't recurse into Repeater / Builder children. The
426
- // visitor handles per-row validation by calling `validateSchema`
427
- // recursively against the row's local values map (per-block schema
428
- // for Builder). Recursing here would validate inner fields against
429
- // the parent `values`, missing the row scope entirely.
430
- if (el instanceof RepeaterField) continue
431
- if (el instanceof BuilderField) continue
432
- const children = el.getChildren()
433
- if (children && children.length > 0) walk(children, visit)
434
- }
435
- }