@pilotiq/pilotiq 0.23.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 (500) hide show
  1. package/CHANGELOG.md +91 -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/dist/actions/exportFactory.d.ts +10 -0
  15. package/dist/actions/exportFactory.d.ts.map +1 -1
  16. package/dist/actions/exportFactory.js +10 -0
  17. package/dist/actions/exportFactory.js.map +1 -1
  18. package/dist/react/CollabRoomContext.d.ts +5 -5
  19. package/dist/react/index.d.ts +0 -1
  20. package/dist/react/index.d.ts.map +1 -1
  21. package/dist/react/index.js +0 -1
  22. package/dist/react/index.js.map +1 -1
  23. package/dist/routes/helpers.d.ts.map +1 -1
  24. package/dist/routes/helpers.js +6 -2
  25. package/dist/routes/helpers.js.map +1 -1
  26. package/dist/routes/relations.d.ts.map +1 -1
  27. package/dist/routes/relations.js +12 -0
  28. package/dist/routes/relations.js.map +1 -1
  29. package/package.json +6 -1
  30. package/.turbo/turbo-build.log +0 -8
  31. package/CLAUDE.md +0 -265
  32. package/dist/react/useCollabSeed.d.ts +0 -23
  33. package/dist/react/useCollabSeed.d.ts.map +0 -1
  34. package/dist/react/useCollabSeed.js +0 -82
  35. package/dist/react/useCollabSeed.js.map +0 -1
  36. package/src/Cluster.test.ts +0 -283
  37. package/src/Cluster.ts +0 -83
  38. package/src/Column.test.ts +0 -199
  39. package/src/Column.ts +0 -710
  40. package/src/Global.test.ts +0 -367
  41. package/src/Global.ts +0 -169
  42. package/src/Page.test.ts +0 -114
  43. package/src/Page.ts +0 -208
  44. package/src/Pilotiq.perf.test.ts +0 -252
  45. package/src/Pilotiq.test.ts +0 -129
  46. package/src/Pilotiq.ts +0 -1158
  47. package/src/PilotiqRegistry.ts +0 -36
  48. package/src/PilotiqServiceProvider.ts +0 -121
  49. package/src/RelationManager.test.ts +0 -400
  50. package/src/RelationManager.ts +0 -527
  51. package/src/RenderHook.test.ts +0 -252
  52. package/src/RenderHook.ts +0 -242
  53. package/src/Resource.test.ts +0 -284
  54. package/src/Resource.ts +0 -526
  55. package/src/RightPanel.test.ts +0 -202
  56. package/src/RightPanel.ts +0 -132
  57. package/src/Tab.test.ts +0 -91
  58. package/src/Tab.ts +0 -156
  59. package/src/UserMenuItem.ts +0 -145
  60. package/src/actions/Action.test.ts +0 -2526
  61. package/src/actions/Action.ts +0 -1515
  62. package/src/actions/ActionGroup.test.ts +0 -112
  63. package/src/actions/ActionGroup.ts +0 -173
  64. package/src/actions/attachFactory.ts +0 -172
  65. package/src/actions/bulkFactories.ts +0 -168
  66. package/src/actions/crudFactories.ts +0 -220
  67. package/src/actions/exportFactory.ts +0 -215
  68. package/src/actions/factoryHelpers.ts +0 -177
  69. package/src/actions/importFactory.ts +0 -243
  70. package/src/actions/index.ts +0 -17
  71. package/src/actions/m2mFactories.ts +0 -193
  72. package/src/actions/relationFactories.ts +0 -372
  73. package/src/applyPageHooks.test.ts +0 -463
  74. package/src/applyPageHooks.ts +0 -330
  75. package/src/authorization.test.ts +0 -483
  76. package/src/breadcrumbs.test.ts +0 -238
  77. package/src/cells/coerce.test.ts +0 -85
  78. package/src/cells/coerce.ts +0 -84
  79. package/src/clusterPaths.ts +0 -35
  80. package/src/columns/BadgeColumn.test.ts +0 -54
  81. package/src/columns/BadgeColumn.ts +0 -32
  82. package/src/columns/BooleanColumn.test.ts +0 -41
  83. package/src/columns/BooleanColumn.ts +0 -18
  84. package/src/columns/ColorColumn.test.ts +0 -37
  85. package/src/columns/ColorColumn.ts +0 -38
  86. package/src/columns/IconColumn.test.ts +0 -54
  87. package/src/columns/IconColumn.ts +0 -37
  88. package/src/columns/ImageColumn.test.ts +0 -41
  89. package/src/columns/ImageColumn.ts +0 -28
  90. package/src/columns/SelectColumn.ts +0 -98
  91. package/src/columns/TextColumn.test.ts +0 -190
  92. package/src/columns/TextColumn.ts +0 -20
  93. package/src/columns/TextInputColumn.ts +0 -68
  94. package/src/columns/ToggleColumn.ts +0 -46
  95. package/src/columns/editableColumns.test.ts +0 -238
  96. package/src/columns/index.ts +0 -9
  97. package/src/defaultGlobalPages.ts +0 -95
  98. package/src/defaultPages.test.ts +0 -634
  99. package/src/defaultPages.ts +0 -617
  100. package/src/defaultViewPage.test.ts +0 -147
  101. package/src/elements/Form.test.ts +0 -223
  102. package/src/elements/Form.ts +0 -416
  103. package/src/elements/ListTabs.ts +0 -28
  104. package/src/elements/Table.test.ts +0 -422
  105. package/src/elements/Table.ts +0 -850
  106. package/src/elements/TableGroup.test.ts +0 -260
  107. package/src/elements/TableGroup.ts +0 -334
  108. package/src/elements/dispatchAction.test.ts +0 -463
  109. package/src/elements/dispatchAction.ts +0 -355
  110. package/src/elements/dispatchForm.test.ts +0 -477
  111. package/src/elements/dispatchForm.ts +0 -1993
  112. package/src/elements/dispatchTable.test.ts +0 -1514
  113. package/src/elements/dispatchTable.ts +0 -745
  114. package/src/elements/index.ts +0 -21
  115. package/src/entries/BadgeEntry.ts +0 -39
  116. package/src/entries/CodeEntry.test.ts +0 -40
  117. package/src/entries/CodeEntry.ts +0 -52
  118. package/src/entries/ColorEntry.ts +0 -63
  119. package/src/entries/ComponentEntry.test.ts +0 -173
  120. package/src/entries/ComponentEntry.ts +0 -95
  121. package/src/entries/Entry.ts +0 -304
  122. package/src/entries/IconEntry.ts +0 -49
  123. package/src/entries/ImageEntry.ts +0 -61
  124. package/src/entries/KeyValueEntry.ts +0 -47
  125. package/src/entries/RepeatableEntry.test.ts +0 -239
  126. package/src/entries/RepeatableEntry.ts +0 -173
  127. package/src/entries/TextEntry.test.ts +0 -394
  128. package/src/entries/TextEntry.ts +0 -60
  129. package/src/entries/index.ts +0 -12
  130. package/src/entries/leaves.test.ts +0 -306
  131. package/src/entries/registry.ts +0 -54
  132. package/src/fields/BuilderField.test.ts +0 -1188
  133. package/src/fields/BuilderField.ts +0 -605
  134. package/src/fields/BuilderRelationship.test.ts +0 -811
  135. package/src/fields/CheckboxField.test.ts +0 -44
  136. package/src/fields/CheckboxField.ts +0 -27
  137. package/src/fields/CheckboxListField.test.ts +0 -99
  138. package/src/fields/CheckboxListField.ts +0 -66
  139. package/src/fields/ColorPickerField.test.ts +0 -33
  140. package/src/fields/ColorPickerField.ts +0 -25
  141. package/src/fields/DateField.ts +0 -54
  142. package/src/fields/DateTimeField.test.ts +0 -55
  143. package/src/fields/EmailField.ts +0 -16
  144. package/src/fields/Field.test.ts +0 -654
  145. package/src/fields/Field.ts +0 -817
  146. package/src/fields/FileUploadField.test.ts +0 -143
  147. package/src/fields/FileUploadField.ts +0 -159
  148. package/src/fields/HiddenField.test.ts +0 -27
  149. package/src/fields/HiddenField.ts +0 -28
  150. package/src/fields/KeyValueField.test.ts +0 -105
  151. package/src/fields/KeyValueField.ts +0 -55
  152. package/src/fields/MarkdownField.test.ts +0 -167
  153. package/src/fields/MarkdownField.ts +0 -162
  154. package/src/fields/NumberField.ts +0 -33
  155. package/src/fields/RadioField.test.ts +0 -94
  156. package/src/fields/RadioField.ts +0 -67
  157. package/src/fields/RepeaterField.test.ts +0 -1806
  158. package/src/fields/RepeaterField.ts +0 -939
  159. package/src/fields/RepeaterRelationship.test.ts +0 -1923
  160. package/src/fields/RepeaterSimple.test.ts +0 -248
  161. package/src/fields/RowButton.test.ts +0 -219
  162. package/src/fields/RowButton.ts +0 -135
  163. package/src/fields/SelectField.test.ts +0 -192
  164. package/src/fields/SelectField.ts +0 -235
  165. package/src/fields/SliderField.test.ts +0 -50
  166. package/src/fields/SliderField.ts +0 -53
  167. package/src/fields/SlugField.ts +0 -24
  168. package/src/fields/TagsInputField.test.ts +0 -154
  169. package/src/fields/TagsInputField.ts +0 -133
  170. package/src/fields/TextField.test.ts +0 -213
  171. package/src/fields/TextField.ts +0 -177
  172. package/src/fields/TextareaField.test.ts +0 -58
  173. package/src/fields/TextareaField.ts +0 -59
  174. package/src/fields/ToggleButtonsField.test.ts +0 -106
  175. package/src/fields/ToggleButtonsField.ts +0 -59
  176. package/src/fields/ToggleField.ts +0 -16
  177. package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +0 -319
  178. package/src/fields/optionsResolver.ts +0 -95
  179. package/src/fields/resolveField.ts +0 -28
  180. package/src/filters/BooleanFilter.ts +0 -35
  181. package/src/filters/DateRangeFilter.test.ts +0 -194
  182. package/src/filters/DateRangeFilter.ts +0 -148
  183. package/src/filters/Filter.test.ts +0 -268
  184. package/src/filters/Filter.ts +0 -184
  185. package/src/filters/FormFilter.test.ts +0 -238
  186. package/src/filters/FormFilter.ts +0 -215
  187. package/src/filters/MultiSelectFilter.test.ts +0 -119
  188. package/src/filters/MultiSelectFilter.ts +0 -78
  189. package/src/filters/QueryBuilderFilter.test.ts +0 -662
  190. package/src/filters/QueryBuilderFilter.ts +0 -398
  191. package/src/filters/SelectFilter.ts +0 -46
  192. package/src/filters/TernaryFilter.test.ts +0 -160
  193. package/src/filters/TernaryFilter.ts +0 -72
  194. package/src/filters/TrashedFilter.test.ts +0 -149
  195. package/src/filters/TrashedFilter.ts +0 -55
  196. package/src/filters/queryBuilder/BooleanConstraint.ts +0 -31
  197. package/src/filters/queryBuilder/Constraint.ts +0 -115
  198. package/src/filters/queryBuilder/DateConstraint.ts +0 -69
  199. package/src/filters/queryBuilder/NumberConstraint.ts +0 -66
  200. package/src/filters/queryBuilder/SelectConstraint.ts +0 -72
  201. package/src/filters/queryBuilder/TextConstraint.ts +0 -64
  202. package/src/filters/queryBuilder/index.ts +0 -12
  203. package/src/icons/index.ts +0 -2
  204. package/src/icons/lucide.ts +0 -204
  205. package/src/icons/registry.test.ts +0 -56
  206. package/src/icons/registry.ts +0 -41
  207. package/src/icons/types.ts +0 -47
  208. package/src/index.ts +0 -525
  209. package/src/io/csv.test.ts +0 -142
  210. package/src/io/csv.ts +0 -170
  211. package/src/nestedRelationManagerData.test.ts +0 -547
  212. package/src/notifications/Notification.test.ts +0 -210
  213. package/src/notifications/Notification.ts +0 -354
  214. package/src/notifications/broadcast.test.ts +0 -110
  215. package/src/notifications/broadcast.ts +0 -95
  216. package/src/notifications/database.test.ts +0 -383
  217. package/src/notifications/database.ts +0 -398
  218. package/src/notifications/databaseNotifications.test.ts +0 -187
  219. package/src/notifications/dispatchNotificationAction.test.ts +0 -341
  220. package/src/notifications/dispatchNotificationAction.ts +0 -142
  221. package/src/notifications/flash.test.ts +0 -89
  222. package/src/notifications/flash.ts +0 -71
  223. package/src/notifications/index.ts +0 -45
  224. package/src/notifications/registerBroadcastAuth.test.ts +0 -134
  225. package/src/notifications/registerBroadcastAuth.ts +0 -100
  226. package/src/notifications/resolveSavedNotification.test.ts +0 -82
  227. package/src/notifications/resolveSavedNotification.ts +0 -59
  228. package/src/notifications/types.ts +0 -93
  229. package/src/orm/m2mAccessor.ts +0 -66
  230. package/src/orm/modelDefaults.test.ts +0 -633
  231. package/src/orm/modelDefaults.ts +0 -666
  232. package/src/pageData/breadcrumbs.ts +0 -288
  233. package/src/pageData/forms.ts +0 -578
  234. package/src/pageData/helpers.ts +0 -857
  235. package/src/pageData/misc.ts +0 -347
  236. package/src/pageData/navigation.ts +0 -842
  237. package/src/pageData/relationPages.ts +0 -1248
  238. package/src/pageData/relationTabs.ts +0 -286
  239. package/src/pageData/resourcePages.ts +0 -609
  240. package/src/pageData.test.ts +0 -1545
  241. package/src/pageData.ts +0 -341
  242. package/src/plugins/index.ts +0 -8
  243. package/src/plugins/themeEditor.test.ts +0 -36
  244. package/src/plugins/themeEditor.ts +0 -45
  245. package/src/react/AppShell.tsx +0 -251
  246. package/src/react/CollabExtensionFactoryRegistry.ts +0 -55
  247. package/src/react/CollabRoomContext.ts +0 -98
  248. package/src/react/CollabTextRendererRegistry.ts +0 -102
  249. package/src/react/CommandPalette.tsx +0 -375
  250. package/src/react/CurrentUserContext.tsx +0 -50
  251. package/src/react/CustomPageWrapperGate.tsx +0 -69
  252. package/src/react/CustomPageWrapperRegistry.ts +0 -45
  253. package/src/react/FieldFocusReporterRegistry.ts +0 -37
  254. package/src/react/FieldLabelSlotRegistry.ts +0 -30
  255. package/src/react/FieldPresenceRegistry.ts +0 -46
  256. package/src/react/FormCollabBindingRegistry.ts +0 -242
  257. package/src/react/FormStateContext.tsx +0 -591
  258. package/src/react/HeadHooks.tsx +0 -126
  259. package/src/react/MarkdownEditorRegistry.test.ts +0 -38
  260. package/src/react/MarkdownEditorRegistry.ts +0 -107
  261. package/src/react/NotificationActionStrip.tsx +0 -263
  262. package/src/react/NotificationBell.tsx +0 -426
  263. package/src/react/PendingSuggestionApplierRegistry.test.ts +0 -97
  264. package/src/react/PendingSuggestionApplierRegistry.ts +0 -98
  265. package/src/react/PendingSuggestionOverlayRegistry.ts +0 -54
  266. package/src/react/PendingSuggestionsContext.tsx +0 -172
  267. package/src/react/RecordWrapperGate.tsx +0 -58
  268. package/src/react/RecordWrapperRegistry.ts +0 -39
  269. package/src/react/RenderHookSlot.tsx +0 -32
  270. package/src/react/RightSidebar.tsx +0 -257
  271. package/src/react/RightSidebarContext.tsx +0 -234
  272. package/src/react/RightSidebarTrigger.tsx +0 -53
  273. package/src/react/RowCoordsContext.tsx +0 -23
  274. package/src/react/SchemaRenderer.tsx +0 -549
  275. package/src/react/SearchTrigger.tsx +0 -46
  276. package/src/react/ThemeProvider.tsx +0 -93
  277. package/src/react/ThemeSettingsPage.tsx +0 -579
  278. package/src/react/ThemeToggle.tsx +0 -20
  279. package/src/react/Toaster.tsx +0 -158
  280. package/src/react/UserMenu.tsx +0 -196
  281. package/src/react/WidgetDataContext.tsx +0 -157
  282. package/src/react/cells/EditableCell.tsx +0 -389
  283. package/src/react/component-slots.test.ts +0 -103
  284. package/src/react/component-slots.ts +0 -116
  285. package/src/react/fieldJsHandler.test.ts +0 -166
  286. package/src/react/fieldJsHandler.ts +0 -79
  287. package/src/react/fields/BuilderInput.tsx +0 -1078
  288. package/src/react/fields/CheckboxInput.tsx +0 -39
  289. package/src/react/fields/CheckboxListInput.tsx +0 -102
  290. package/src/react/fields/ColorInput.tsx +0 -71
  291. package/src/react/fields/DateFieldInput.tsx +0 -70
  292. package/src/react/fields/DateTimeInput.tsx +0 -62
  293. package/src/react/fields/FieldShell.tsx +0 -348
  294. package/src/react/fields/FileUploadInput.tsx +0 -639
  295. package/src/react/fields/HiddenInput.tsx +0 -17
  296. package/src/react/fields/KeyValueInput.tsx +0 -230
  297. package/src/react/fields/MarkdownInput.tsx +0 -560
  298. package/src/react/fields/RadioInput.tsx +0 -81
  299. package/src/react/fields/RepeaterInput.test.ts +0 -116
  300. package/src/react/fields/RepeaterInput.tsx +0 -1420
  301. package/src/react/fields/SelectFieldInput.tsx +0 -280
  302. package/src/react/fields/SliderInput.tsx +0 -81
  303. package/src/react/fields/TagsInput.tsx +0 -283
  304. package/src/react/fields/TextLikeInput.tsx +0 -256
  305. package/src/react/fields/ToggleButtonsInput.tsx +0 -60
  306. package/src/react/fields/ToggleFieldInput.tsx +0 -56
  307. package/src/react/fields/relationshipRenameDispatch.test.ts +0 -106
  308. package/src/react/fields/relationshipRenameDispatch.ts +0 -97
  309. package/src/react/fields/repeaterReconcile.test.ts +0 -114
  310. package/src/react/fields/repeaterReconcile.ts +0 -104
  311. package/src/react/fields/rowChromeButton.tsx +0 -336
  312. package/src/react/fields/rowState.ts +0 -106
  313. package/src/react/fields/syncRowGates.test.ts +0 -202
  314. package/src/react/fields/syncRowGates.ts +0 -66
  315. package/src/react/fields/textInputControls.tsx +0 -238
  316. package/src/react/fields/useRowReorderDnd.ts +0 -78
  317. package/src/react/formStateHelpers.test.ts +0 -508
  318. package/src/react/formStateHelpers.ts +0 -381
  319. package/src/react/hooks/use-mobile.ts +0 -19
  320. package/src/react/icon-context.tsx +0 -60
  321. package/src/react/index.ts +0 -195
  322. package/src/react/layouts/SidebarLayout.tsx +0 -250
  323. package/src/react/layouts/TopbarLayout.tsx +0 -258
  324. package/src/react/navigate.tsx +0 -37
  325. package/src/react/onProviderSynced.test.ts +0 -90
  326. package/src/react/parseRecordEditUrl.test.ts +0 -122
  327. package/src/react/parseRecordEditUrl.ts +0 -94
  328. package/src/react/persistedState.ts +0 -40
  329. package/src/react/registry.ts +0 -48
  330. package/src/react/right-panel-registry.tsx +0 -47
  331. package/src/react/schemaRenderer/AlertRenderer.tsx +0 -112
  332. package/src/react/schemaRenderer/EntryRenderer.tsx +0 -501
  333. package/src/react/schemaRenderer/SectionRenderer.tsx +0 -120
  334. package/src/react/schemaRenderer/SimpleElements.tsx +0 -306
  335. package/src/react/schemaRenderer/TabsRenderer.tsx +0 -62
  336. package/src/react/schemaRenderer/WizardRenderer.tsx +0 -338
  337. package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +0 -177
  338. package/src/react/schemaRenderer/action/ActionModalDialog.tsx +0 -273
  339. package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +0 -61
  340. package/src/react/schemaRenderer/action/HandlerActionButton.tsx +0 -43
  341. package/src/react/schemaRenderer/action/MethodActionButton.tsx +0 -64
  342. package/src/react/schemaRenderer/action/buttons.tsx +0 -99
  343. package/src/react/schemaRenderer/action/helpers.ts +0 -140
  344. package/src/react/schemaRenderer/action/renderAction.tsx +0 -245
  345. package/src/react/schemaRenderer/columnFormat.ts +0 -65
  346. package/src/react/schemaRenderer/constants.ts +0 -50
  347. package/src/react/schemaRenderer/form/FormRenderer.tsx +0 -274
  348. package/src/react/schemaRenderer/form/renderField.tsx +0 -511
  349. package/src/react/schemaRenderer/helpers.tsx +0 -81
  350. package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +0 -308
  351. package/src/react/schemaRenderer/table/TableRenderer.tsx +0 -123
  352. package/src/react/schemaRenderer/table/TableRendererBody.tsx +0 -974
  353. package/src/react/schemaRenderer/table/filters.tsx +0 -1233
  354. package/src/react/schemaRenderer/table/formatCell.tsx +0 -264
  355. package/src/react/schemaRenderer/table/links.tsx +0 -112
  356. package/src/react/schemaRenderer/table/renderRowActions.tsx +0 -52
  357. package/src/react/schemaRenderer/table/url.tsx +0 -143
  358. package/src/react/theme-preview/apply.ts +0 -99
  359. package/src/react/theme-preview/build-html.ts +0 -436
  360. package/src/react/ui/button.tsx +0 -51
  361. package/src/react/ui/calendar.tsx +0 -67
  362. package/src/react/ui/checkbox.tsx +0 -29
  363. package/src/react/ui/dialog.tsx +0 -108
  364. package/src/react/ui/dropdown-menu.tsx +0 -97
  365. package/src/react/ui/input.tsx +0 -20
  366. package/src/react/ui/label.tsx +0 -21
  367. package/src/react/ui/popover.tsx +0 -50
  368. package/src/react/ui/select.tsx +0 -169
  369. package/src/react/ui/separator.tsx +0 -25
  370. package/src/react/ui/sheet.tsx +0 -136
  371. package/src/react/ui/sidebar.tsx +0 -723
  372. package/src/react/ui/skeleton.tsx +0 -13
  373. package/src/react/ui/slider.tsx +0 -34
  374. package/src/react/ui/switch.tsx +0 -28
  375. package/src/react/ui/table.tsx +0 -105
  376. package/src/react/ui/tabs.tsx +0 -63
  377. package/src/react/ui/textarea.tsx +0 -18
  378. package/src/react/ui/tooltip.tsx +0 -64
  379. package/src/react/useCollabSeed.ts +0 -86
  380. package/src/react/useResizableWidth.ts +0 -139
  381. package/src/react/utils.ts +0 -6
  382. package/src/react/widgetRegistry.test.ts +0 -43
  383. package/src/react/widgetRegistry.ts +0 -50
  384. package/src/react/widgets/StatsOverviewRenderer.tsx +0 -232
  385. package/src/react/widgets/TableWidgetRenderer.tsx +0 -231
  386. package/src/react/widgets/ViewRenderer.tsx +0 -71
  387. package/src/relationManagerData.test.ts +0 -1595
  388. package/src/richtext/index.ts +0 -8
  389. package/src/richtext/registry.ts +0 -89
  390. package/src/routes/globals.ts +0 -148
  391. package/src/routes/guard.test.ts +0 -325
  392. package/src/routes/helpers.ts +0 -700
  393. package/src/routes/pages.ts +0 -175
  394. package/src/routes/panel.ts +0 -204
  395. package/src/routes/relations.ts +0 -1227
  396. package/src/routes/resources.ts +0 -781
  397. package/src/routes/theme.ts +0 -91
  398. package/src/routes-nested-relations.test.ts +0 -676
  399. package/src/routes-relations.test.ts +0 -972
  400. package/src/routes.test.ts +0 -2027
  401. package/src/routes.ts +0 -303
  402. package/src/schema/Alert.test.ts +0 -109
  403. package/src/schema/Alert.ts +0 -131
  404. package/src/schema/Block.ts +0 -169
  405. package/src/schema/Breadcrumbs.ts +0 -40
  406. package/src/schema/Card.ts +0 -35
  407. package/src/schema/Divider.ts +0 -20
  408. package/src/schema/Element.ts +0 -219
  409. package/src/schema/EmptyState.test.ts +0 -37
  410. package/src/schema/EmptyState.ts +0 -63
  411. package/src/schema/Fieldset.ts +0 -43
  412. package/src/schema/Grid.ts +0 -43
  413. package/src/schema/Group.ts +0 -30
  414. package/src/schema/Heading.ts +0 -39
  415. package/src/schema/Html.ts +0 -67
  416. package/src/schema/Icon.ts +0 -54
  417. package/src/schema/Image.ts +0 -57
  418. package/src/schema/LinkTag.ts +0 -41
  419. package/src/schema/Markdown.ts +0 -85
  420. package/src/schema/MetaTag.ts +0 -41
  421. package/src/schema/RelationTabs.ts +0 -71
  422. package/src/schema/ScriptTag.ts +0 -55
  423. package/src/schema/Section.ts +0 -160
  424. package/src/schema/ServerDataElement.test.ts +0 -140
  425. package/src/schema/ServerDataElement.ts +0 -156
  426. package/src/schema/SlotComponent.test.ts +0 -77
  427. package/src/schema/SlotComponent.ts +0 -71
  428. package/src/schema/Split.ts +0 -50
  429. package/src/schema/Stat.test.ts +0 -118
  430. package/src/schema/Stat.ts +0 -154
  431. package/src/schema/StatsOverview.test.ts +0 -141
  432. package/src/schema/StatsOverview.ts +0 -119
  433. package/src/schema/StyleTag.ts +0 -35
  434. package/src/schema/TableWidget.test.ts +0 -297
  435. package/src/schema/TableWidget.ts +0 -289
  436. package/src/schema/Tabs.ts +0 -79
  437. package/src/schema/Text.ts +0 -58
  438. package/src/schema/UnorderedList.ts +0 -49
  439. package/src/schema/View.test.ts +0 -111
  440. package/src/schema/View.ts +0 -127
  441. package/src/schema/Wizard.ts +0 -220
  442. package/src/schema/containers.test.ts +0 -564
  443. package/src/schema/headTags.test.ts +0 -134
  444. package/src/schema/index.ts +0 -40
  445. package/src/schema/primes.test.ts +0 -269
  446. package/src/schema/resolveSchema.test.ts +0 -379
  447. package/src/schema/resolveSchema.ts +0 -917
  448. package/src/schema/sanitize.ts +0 -58
  449. package/src/search.test.ts +0 -446
  450. package/src/search.ts +0 -178
  451. package/src/sessionFilters.test.ts +0 -375
  452. package/src/sessionFilters.ts +0 -143
  453. package/src/slot-components/index.ts +0 -10
  454. package/src/slot-components/registry.ts +0 -56
  455. package/src/styles/file-upload.css +0 -13
  456. package/src/summarizers/Summarizer.test.ts +0 -84
  457. package/src/summarizers/Summarizer.ts +0 -123
  458. package/src/summarizers/index.ts +0 -11
  459. package/src/theme/base-colors.ts +0 -68
  460. package/src/theme/chart-colors.ts +0 -50
  461. package/src/theme/colors.ts +0 -447
  462. package/src/theme/generate-css.test.ts +0 -139
  463. package/src/theme/generate-css.ts +0 -44
  464. package/src/theme/generate-scale.test.ts +0 -106
  465. package/src/theme/generate-scale.ts +0 -97
  466. package/src/theme/icon-map.ts +0 -42
  467. package/src/theme/index.ts +0 -34
  468. package/src/theme/migrate.test.ts +0 -178
  469. package/src/theme/migrate.ts +0 -81
  470. package/src/theme/presets.ts +0 -135
  471. package/src/theme/radius.ts +0 -18
  472. package/src/theme/resolve.test.ts +0 -238
  473. package/src/theme/resolve.ts +0 -96
  474. package/src/theme/spacing.ts +0 -18
  475. package/src/theme/storage.test.ts +0 -126
  476. package/src/theme/storage.ts +0 -106
  477. package/src/theme/theme-colors.ts +0 -88
  478. package/src/theme/types.ts +0 -125
  479. package/src/uploads/UploadAdapter.ts +0 -35
  480. package/src/uploads/index.ts +0 -2
  481. package/src/uploads/localUpload.test.ts +0 -70
  482. package/src/uploads/localUpload.ts +0 -84
  483. package/src/validation/Validator.ts +0 -49
  484. package/src/validation/index.ts +0 -28
  485. package/src/validation/rules.ts +0 -78
  486. package/src/validation/runValidators.ts +0 -435
  487. package/src/validation/uniqueValidator.test.ts +0 -196
  488. package/src/validation/uniqueValidator.ts +0 -133
  489. package/src/validation/validators.test.ts +0 -268
  490. package/src/vite.test.ts +0 -184
  491. package/src/vite.ts +0 -787
  492. package/src/widgets/index.ts +0 -10
  493. package/src/widgets/registry.ts +0 -45
  494. package/src/widgets.test.ts +0 -592
  495. package/tsconfig.build.json +0 -11
  496. package/tsconfig.json +0 -4
  497. package/tsconfig.test.json +0 -10
  498. package/views/react/Dashboard.tsx +0 -27
  499. package/views/react/Resources/Form.tsx +0 -102
  500. 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
- }