@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,917 +0,0 @@
1
- import { Element, type ElementMeta, type LayoutContext } from './Element.js'
2
- import { Field } from '../fields/Field.js'
3
- import {
4
- RepeaterField,
5
- type RepeaterItemHiddenRule,
6
- type RepeaterItemCanRule,
7
- type RepeaterRowMeta,
8
- } from '../fields/RepeaterField.js'
9
- import {
10
- BuilderField,
11
- type BuilderRowMeta,
12
- } from '../fields/BuilderField.js'
13
- import type { BlockMeta } from './Block.js'
14
- import { Action, type ActionMeta, type ActionVisibilityContext } from '../actions/Action.js'
15
- import { ActionGroup } from '../actions/ActionGroup.js'
16
- import { Filter } from '../filters/Filter.js'
17
- import { isServerDataElement, stampServerDataMeta } from './ServerDataElement.js'
18
- import { Entry } from '../entries/Entry.js'
19
- import {
20
- RepeatableEntry,
21
- readRepeatableRowId,
22
- type RepeatableEntryRowMeta,
23
- } from '../entries/RepeatableEntry.js'
24
- import { Section } from './Section.js'
25
- import { Wizard, type WizardActionCustomizer } from './Wizard.js'
26
- import { TextField } from '../fields/TextField.js'
27
- import { Form } from '../elements/Form.js'
28
-
29
- export interface SchemaContext {
30
- user?: { name?: string; email?: string; [key: string]: unknown }
31
- [key: string]: unknown
32
- }
33
-
34
- /**
35
- * Render context — extends `SchemaContext` with the rendering mode and
36
- * optional record. Used by the field resolver to evaluate visibility flags
37
- * (`hideFromTable` / `hideFromEdit` / etc.) and condition callbacks
38
- * (`showWhen` / `hideWhen` / `disabledWhen`) against the current page.
39
- *
40
- * `mode` is unset on schema-only routes (custom Pages). Field visibility is
41
- * a no-op in that case.
42
- */
43
- export type RenderMode = 'table' | 'create' | 'edit' | 'view'
44
-
45
- export interface RenderContext extends SchemaContext {
46
- mode?: RenderMode
47
- record?: unknown
48
- /**
49
- * Current form values for the in-progress resolve cycle. Populated by
50
- * the partial-resolve endpoint (Plan #5) and by edit/create page
51
- * builders that prefill from a record. When present, `$get / $set`
52
- * are framework-bound so field condition callbacks and reactive
53
- * `options(fn)` handlers can read/write sibling values.
54
- */
55
- values?: Record<string, unknown>
56
- /** Read a sibling field's current value from the resolve cycle's values map. */
57
- $get?: (name: string) => unknown
58
- /** Mutate a sibling field's value during this resolve cycle. */
59
- $set?: (name: string, value: unknown) => void
60
- /**
61
- * Name of the field whose change triggered this resolve, if any.
62
- * Populated only by the partial-resolve endpoint; undefined on
63
- * initial GET render.
64
- */
65
- changed?: string
66
- /**
67
- * URL the FileUpload field should POST to. Stamped onto every
68
- * page-data ctx by the route handlers / page-data builders so the
69
- * field's `toMeta` doesn't need to know the panel base path. Single
70
- * panel-level URL — no per-field variation.
71
- */
72
- uploadUrl?: string
73
- /**
74
- * `true` when the panel has registered an `UploadAdapter` via
75
- * `Pilotiq.uploads({ adapter })`. Distinct from `uploadUrl` (which is
76
- * always stamped so `FileUpload` can show a clear error if missed) —
77
- * this flag tells fields whose upload integration is *optional* (e.g.
78
- * `MarkdownField`'s `attachFiles` toolbar button) whether to surface
79
- * the affordance at all.
80
- */
81
- hasUploadAdapter?: boolean
82
- /**
83
- * Plan #15 — per-widget filter value rode in on the polling-endpoint
84
- * body. Opaque to the framework: each widget's `getData(ctx)` decodes
85
- * `ctx.filter` however it wants (`'today' | 'week' | 'month'`, JSON
86
- * blob, …). Unset on the initial-render path; set on every polling
87
- * fetch when the user picked something from the per-chart filter
88
- * dropdown.
89
- */
90
- filter?: string
91
- /**
92
- * Plan #14 row-scoped sugar inside a Repeater. When the resolver is
93
- * walking a row's inner schema, `row.index` is the row's position and
94
- * `row.$get / row.$set` are bound to the row's local values map. The
95
- * top-level `ctx.$get / $set` still see the whole form (cross-row
96
- * reads via dotted paths like `items.0.title`); `row.$get(name)` is
97
- * just sugar for the common case of "read the same row's siblings".
98
- */
99
- row?: {
100
- index: number
101
- $get: (name: string) => unknown
102
- $set: (name: string, value: unknown) => void
103
- /**
104
- * Other rows' values maps, excluding the current row at `index`.
105
- * Stamped by `resolveRepeaterRows` (every sibling) and
106
- * `resolveBuilderRows` (only same-block-type siblings; each entry is
107
- * the row's `data` map). Read by
108
- * `disableOptionsWhenSelectedInSiblingRepeaterItems()` to mark
109
- * already-picked options as disabled.
110
- */
111
- siblings?: ReadonlyArray<Record<string, unknown>>
112
- /**
113
- * Present only when the row lives inside a `BuilderField` — the row's
114
- * block name. See `LayoutContext.row.blockType` for the rationale.
115
- */
116
- blockType?: string
117
- }
118
- /**
119
- * Cascading `inlineLabel` default set by an ancestor `Form` or
120
- * `Section` via `.inlineLabel(true)`. Read by `Field.buildMeta` when the
121
- * field hasn't called `inlineLabel()` itself; explicit field-level
122
- * setting always wins. A nested container's `.inlineLabel(false)`
123
- * overrides an outer container's `.inlineLabel(true)` for its subtree.
124
- */
125
- inlineLabelDefault?: boolean
126
- }
127
-
128
- export type SchemaDefinition =
129
- | Element[]
130
- | ((ctx: SchemaContext) => Element[] | Promise<Element[]>)
131
-
132
- /**
133
- * Plugin resolver — replaces the default toMeta+recurse behavior for a
134
- * given element type. Plugins receive the original element, the context,
135
- * and a `recurse` helper that resolves a child array. Useful when a
136
- * pro/feature plugin needs to inject computed data, augment meta, or
137
- * reshape children before serialization.
138
- *
139
- * If no resolver is registered for a type, the default (call `toMeta()`,
140
- * recurse into `getChildren()` if present, attach as `meta.children`) runs.
141
- */
142
- export type ElementResolver = (
143
- el: Element,
144
- ctx: SchemaContext,
145
- recurse: (children: Element[]) => Promise<ElementMeta[]>,
146
- ) => Promise<ElementMeta> | ElementMeta
147
-
148
- const registry = new Map<string, ElementResolver>()
149
-
150
- /** Register a custom resolver for a given element type. Plugins call this at boot. */
151
- export function registerResolver(type: string, fn: ElementResolver): void {
152
- registry.set(type, fn)
153
- }
154
-
155
- /** Internal — used by tests to reset state. Not part of the public API. */
156
- export function _resetResolverRegistry(): void {
157
- registry.clear()
158
- }
159
-
160
- /**
161
- * Resolve a schema definition into serializable metadata.
162
- *
163
- * Walks the Element tree, calling each element's `toMeta()` (or a registered
164
- * custom resolver). Children of container elements are resolved recursively
165
- * and attached as `meta.children`. Fields evaluate visibility flags + record
166
- * conditions against the `RenderContext`; hidden fields are dropped.
167
- *
168
- * Accepts a `SchemaContext` (or the richer `RenderContext`) — the latter is
169
- * required for Field visibility/condition evaluation to do anything useful.
170
- */
171
- export async function resolveSchema(
172
- definition: SchemaDefinition | undefined,
173
- ctx: RenderContext = {},
174
- ): Promise<ElementMeta[]> {
175
- if (!definition) return []
176
-
177
- const elements = typeof definition === 'function'
178
- ? await definition(ctx)
179
- : definition
180
-
181
- return resolveAll(elements, ctx)
182
- }
183
-
184
- async function resolveAll(elements: Element[], ctx: RenderContext): Promise<ElementMeta[]> {
185
- const results = await Promise.all(elements.map(el => resolveOne(el, ctx)))
186
- // Filter null entries from hidden fields.
187
- return results.filter((m): m is ElementMeta => m !== null)
188
- }
189
-
190
- async function resolveOne(el: Element, ctx: RenderContext): Promise<ElementMeta | null> {
191
- // Field visibility — drop the entire element if hidden in the current
192
- // render context. Done before custom resolvers so plugins can't accidentally
193
- // resurrect a hidden field.
194
- if (el instanceof Field && el.isHiddenIn(ctx)) {
195
- return null
196
- }
197
-
198
- // Action visibility — non-row placements evaluate against the page-level
199
- // context here; row-placement actions defer to per-row evaluation in
200
- // `loadTableRecords` (we always include them in the tree). Disabled
201
- // gets stamped on meta either way.
202
- if (el instanceof Action && el.hasVisibilityRules() && el.getPlacement() !== 'row') {
203
- const evalCtx: { record?: unknown; user?: unknown } = {}
204
- if (ctx.record !== undefined) evalCtx.record = ctx.record
205
- if (ctx.user !== undefined) evalCtx.user = ctx.user
206
- const { visible } = await el.evaluate(evalCtx)
207
- if (!visible) return null
208
- }
209
-
210
- // ActionGroup visibility — same shape as Action; the dropdown trigger
211
- // hides when the group rule resolves false. Also drop the group when
212
- // every child action is hidden (group with no usable items is dead UX).
213
- if (el instanceof ActionGroup) {
214
- if (el.hasVisibilityRules()) {
215
- const evalCtx: { record?: unknown; user?: unknown } = {}
216
- if (ctx.record !== undefined) evalCtx.record = ctx.record
217
- if (ctx.user !== undefined) evalCtx.user = ctx.user
218
- const { visible } = await el.evaluate(evalCtx)
219
- if (!visible) return null
220
- }
221
- }
222
-
223
- // Layout-level visibility (Plan #8). Field/Action/ActionGroup own
224
- // their visibility above; this catches every other Element with a
225
- // `.visible(...)` rule (Section, Card, Tabs, Tab, Grid, Split,
226
- // Wizard, Step, Group, Fieldset, Heading, Text, …).
227
- if (!(el instanceof Field) &&
228
- !(el instanceof Action) &&
229
- !(el instanceof ActionGroup) &&
230
- el.hasVisibilityRule()) {
231
- const layoutCtx = buildLayoutContext(ctx)
232
- const visible = await el.evaluateVisibility(layoutCtx)
233
- if (!visible) return null
234
- }
235
-
236
- const type = el.getType()
237
-
238
- const customResolver = registry.get(type)
239
- if (customResolver) {
240
- return customResolver(el, ctx, children => resolveAll(children, ctx))
241
- }
242
-
243
- // Default resolution: toMeta() + recurse children if container. Fields
244
- // get their ctx-aware overload so disabledWhen and reactive subclasses
245
- // (Plan #5) see the full RenderContext. Async toMeta is awaited —
246
- // SelectField with a resolver-style `options(fn)` may be async.
247
- // Filters receive ctx too so subclasses like FormFilter can resolve
248
- // their inner form schemas with the same render context (e.g. the
249
- // active user, for option resolvers inside a filter form). Entries
250
- // (Plan #16) need ctx.record to resolve their state value.
251
- const meta = await Promise.resolve(
252
- el instanceof Field || el instanceof Filter || el instanceof Entry
253
- ? el.toMeta(ctx)
254
- : el.toMeta(),
255
- ) as ElementMeta
256
- meta.type = type // ensure type is always set, even if toMeta forgot
257
-
258
- // Stamp the page-level disabled state on non-row Actions so the renderer
259
- // greys out the button. Row actions get per-row stamping in dispatchTable.
260
- if (el instanceof Action && el.hasVisibilityRules() && el.getPlacement() !== 'row') {
261
- const evalCtx: { record?: unknown; user?: unknown } = {}
262
- if (ctx.record !== undefined) evalCtx.record = ctx.record
263
- if (ctx.user !== undefined) evalCtx.user = ctx.user
264
- const { disabled } = await el.evaluate(evalCtx)
265
- if (disabled) meta['disabled'] = true
266
- }
267
-
268
- // Same for ActionGroup — stamp disabled when the group's rule says so.
269
- if (el instanceof ActionGroup && el.hasVisibilityRules()) {
270
- const evalCtx: { record?: unknown; user?: unknown } = {}
271
- if (ctx.record !== undefined) evalCtx.record = ctx.record
272
- if (ctx.user !== undefined) evalCtx.user = ctx.user
273
- const { disabled } = await el.evaluate(evalCtx)
274
- if (disabled) meta['disabled'] = true
275
- }
276
-
277
- // Plan #8 — emit `_layout` bag when the element used columnSpan/Start/Order.
278
- // Stamped after toMeta() so subclasses don't have to remember.
279
- const layout = el.getLayoutPositioning()
280
- if (layout) meta._layout = layout
281
-
282
- // Plan #15 — stamp `serverData / id / poll / lazy` on every
283
- // ServerDataElement so the renderer can find its `_widgetData[id]`
284
- // payload. The actual data resolution happens in
285
- // `resolveServerDataElements` (a separate pageData pass) — this just
286
- // marks the element on the wire.
287
- if (isServerDataElement(el)) stampServerDataMeta(el, meta)
288
-
289
- // Plan #14 — Repeater rows. Skip the generic `getChildren()` recurse
290
- // below (the inner schema is rendered per-row, not once on the parent),
291
- // and instead populate `meta.rows` + `meta.template` with row-scoped
292
- // contexts so each child sees its own row's values via `$get` / `row`.
293
- if (el instanceof RepeaterField) {
294
- await resolveRepeaterRows(el, ctx, meta)
295
- return meta
296
- }
297
-
298
- // Plan #14 follow-up — Builder rows. Same row-scoped resolve as
299
- // Repeater, but each row's inner schema is the block matching the
300
- // row's submitted `type`. The picker (`meta.blocks`) was already
301
- // stamped by `BuilderField.toMeta()`.
302
- if (el instanceof BuilderField) {
303
- await resolveBuilderRows(el, ctx, meta)
304
- return meta
305
- }
306
-
307
- // Filament v5 — `RepeatableEntry` rows. Read-only sibling of Repeater:
308
- // the array lives on `ctx.record[name]` (no `ctx.values` involvement —
309
- // entries are display-only) and each row's inner schema resolves with
310
- // its own `record` scoped to that row's data, so inner entries read
311
- // their state via the same `record[childName]` lookup they use anywhere
312
- // else.
313
- if (el instanceof RepeatableEntry) {
314
- await resolveRepeatableRows(el, ctx, meta)
315
- return meta
316
- }
317
-
318
- // Push `inlineLabelDefault` into the child ctx when this element is a
319
- // `Form` or `Section` whose `.inlineLabel(...)` was set. Children inherit
320
- // until another container resets the flag. Field-level `inlineLabel(...)`
321
- // calls always win on read (see `Field.buildMeta`).
322
- const childCtx = deriveChildContext(el, ctx)
323
-
324
- const children = el.getChildren()
325
- if (children && children.length > 0) {
326
- meta.children = await resolveAll(children, childCtx)
327
- }
328
-
329
- // Filament v5 — `Section.afterHeader([Action…])` resolves through the
330
- // standard walker so every Action's `.visible() / .disabled()` rule
331
- // evaluates the same way it does anywhere else. Stamped under
332
- // `meta.afterHeader` so the renderer doesn't confuse it with the
333
- // section's body content (`meta.children`).
334
- if (el instanceof Section) {
335
- const afterHeader = el.getAfterHeader()
336
- if (afterHeader && afterHeader.length > 0) {
337
- meta['afterHeader'] = await resolveAll(afterHeader, ctx)
338
- }
339
- }
340
-
341
- // Filament v5 — `Action.modalContentFooter([Element…])` resolves through
342
- // the standard walker so any inner Actions evaluate `.visible() /
343
- // .disabled()` the same way as anywhere else, and non-Action chrome
344
- // (Alert / Text / Heading / …) flows through the same rendering path.
345
- // Stamped under `meta.modalContentFooter` so the renderer can mount it
346
- // between the form body and the Cancel/Submit footer.
347
- if (el instanceof Action) {
348
- const footer = el.getModalContentFooter()
349
- if (footer && footer.length > 0) {
350
- meta['modalContentFooter'] = await resolveAll(footer, ctx)
351
- }
352
- }
353
-
354
- // `Wizard.submitAction() / nextAction() / previousAction()` — build a
355
- // framework default Action for each slot, run the user's customizer
356
- // (or take the explicit Action), then resolve through the standard
357
- // walker so `.visible() / .disabled()` rules evaluate the same way
358
- // as anywhere else. Stamped under `meta.submitAction / nextAction /
359
- // previousAction`; sparse — absent when the user hasn't customized.
360
- if (el instanceof Wizard) {
361
- const submitC = el.getSubmitAction()
362
- const nextC = el.getNextAction()
363
- const previousC = el.getPreviousAction()
364
- if (submitC) {
365
- const [resolved] = await resolveAll([resolveWizardAction(submitC, 'submit')], ctx)
366
- if (resolved) meta['submitAction'] = resolved
367
- }
368
- if (nextC) {
369
- const [resolved] = await resolveAll([resolveWizardAction(nextC, 'next')], ctx)
370
- if (resolved) meta['nextAction'] = resolved
371
- }
372
- if (previousC) {
373
- const [resolved] = await resolveAll([resolveWizardAction(previousC, 'previous')], ctx)
374
- if (resolved) meta['previousAction'] = resolved
375
- }
376
- }
377
-
378
- // `TextField.prefixAction(Action) / suffixAction(Action)` — resolve the
379
- // bound Actions through `resolveAll` so visibility / disabled rules
380
- // evaluate the same way as anywhere else. Hidden Actions are dropped
381
- // from the slot (returning a sparse single-element array → undefined
382
- // collapses cleanly).
383
- if (el instanceof TextField) {
384
- const pre = el.getPrefixAction()
385
- const suf = el.getSuffixAction()
386
- if (pre) {
387
- const [resolved] = await resolveAll([pre], ctx)
388
- if (resolved) meta['prefixAction'] = resolved
389
- }
390
- if (suf) {
391
- const [resolved] = await resolveAll([suf], ctx)
392
- if (resolved) meta['suffixAction'] = resolved
393
- }
394
- }
395
-
396
- return meta
397
- }
398
-
399
- /**
400
- * Per-row resolution for `RepeaterField`. Reads submitted row values from
401
- * `ctx.values?.[field.name]`, falls back to `defaultItems` empty rows on
402
- * fresh renders, resolves the inner schema once per row with a row-scoped
403
- * `RenderContext`, and stamps `meta.rows` + `meta.template`.
404
- *
405
- * `template` is the empty-row blueprint the client clones when the user
406
- * presses "Add row" — resolved with `values: {}` so any `default()` /
407
- * `defaultValue` on inner fields surfaces correctly.
408
- */
409
- async function resolveRepeaterRows(
410
- field: RepeaterField,
411
- ctx: RenderContext,
412
- meta: ElementMeta,
413
- ): Promise<void> {
414
- const inner = field.getInnerSchema()
415
- const submitted = ctx.values?.[field.name]
416
- // For `simple()` repeaters the loaded record / submitted value is a
417
- // flat `[v1, v2, …]` array. Wrap each primitive entry inline using the
418
- // inner field's name so the rest of the resolver path treats it as a
419
- // standard `[{<innerName>: v}]` row. Object entries pass through (e.g.
420
- // when a coerce/state-update has already produced wrapped rows).
421
- const simpleInner = field.getSimpleInnerField()
422
- const rowsInput: Array<Record<string, unknown>> = Array.isArray(submitted)
423
- ? submitted.map(raw => simpleInner ? coerceSimpleRowValues(raw, simpleInner.name) : coerceRowValues(raw))
424
- : Array.from({ length: field.getDefaultItems() }, () => ({}))
425
-
426
- const labelFn = field.getItemLabel()
427
- const hiddenFn = field.getItemHidden()
428
- const canDeleteFn = field.getItemCanDelete()
429
- const canCloneFn = field.getItemCanClone()
430
- const canReorderFn = field.getItemCanReorder()
431
-
432
- const rows = await Promise.all(rowsInput.map(async (rowValues, index) => {
433
- const siblings = rowsInput.filter((_, i) => i !== index)
434
- const rowCtx: RenderContext = {
435
- ...ctx,
436
- values: rowValues,
437
- $get: (name: string) => rowValues[name],
438
- $set: (name: string, value: unknown) => { rowValues[name] = value },
439
- row: {
440
- index,
441
- $get: (name: string) => rowValues[name],
442
- $set: (name: string, value: unknown) => { rowValues[name] = value },
443
- siblings,
444
- },
445
- }
446
- delete rowCtx.changed // changed key is parent-scoped; not meaningful inside the row resolve
447
- const children = await resolveAll(inner, rowCtx)
448
- const id = readRowId(rowValues, field.name, index)
449
- const row: RepeaterRowMeta = { id, children }
450
- if (labelFn) {
451
- try {
452
- const label = labelFn(rowValues)
453
- if (typeof label === 'string') row.itemLabel = label
454
- } catch (err) {
455
- console.warn(`[pilotiq] itemLabel() on Repeater "${field.name}" threw:`, err)
456
- }
457
- }
458
- // Build the layout context lazily — most repeaters configure none of
459
- // {hidden, canDelete, canClone, canReorder}, so the common path skips
460
- // it entirely.
461
- const needsLayoutCtx = hiddenFn !== undefined
462
- || canDeleteFn !== undefined
463
- || canCloneFn !== undefined
464
- || canReorderFn !== undefined
465
- const layoutCtx = needsLayoutCtx ? buildLayoutContext(rowCtx) : undefined
466
- if (hiddenFn !== undefined && layoutCtx !== undefined) {
467
- const hidden = await evalItemHidden(hiddenFn, layoutCtx, `Repeater "${field.name}"`)
468
- if (hidden) row.hidden = true
469
- }
470
- if (canDeleteFn !== undefined && layoutCtx !== undefined) {
471
- const allowed = await evalItemCan(canDeleteFn, layoutCtx, 'itemCanDelete', `Repeater "${field.name}"`)
472
- if (!allowed) row.canDelete = false
473
- }
474
- if (canCloneFn !== undefined && layoutCtx !== undefined) {
475
- const allowed = await evalItemCan(canCloneFn, layoutCtx, 'itemCanClone', `Repeater "${field.name}"`)
476
- if (!allowed) row.canClone = false
477
- }
478
- if (canReorderFn !== undefined && layoutCtx !== undefined) {
479
- const allowed = await evalItemCan(canReorderFn, layoutCtx, 'itemCanReorder', `Repeater "${field.name}"`)
480
- if (!allowed) row.canReorder = false
481
- }
482
- const extras = field.getExtraItemActions()
483
- if (extras.length > 0) {
484
- const resolved = await resolveExtraItemActions(extras, ctx, rowValues)
485
- if (resolved.length > 0) row.extraActions = resolved
486
- }
487
- return row
488
- }))
489
-
490
- const templateCtx: RenderContext = { ...ctx, values: {} }
491
- delete templateCtx.row
492
- delete templateCtx.changed
493
- const template = await resolveAll(inner, templateCtx)
494
-
495
- meta['rows'] = rows
496
- meta['template'] = template
497
- }
498
-
499
- /**
500
- * Resolve a Repeater/Builder field's `extraItemActions` for one row.
501
- *
502
- * Each action's `.visible() / .hidden() / .disabled()` rules see a
503
- * `ActionVisibilityContext` with the parent record + user (so an action
504
- * can authorize against the page's user) plus the row's `values` and
505
- * `row.{ index, id, blockType? }` (so per-row predicates work).
506
- *
507
- * Visibility-eval throwers fail-closed (the action is dropped from the
508
- * row's strip — same posture as page-level Action visibility).
509
- *
510
- * Disabled stamping mirrors row-placement table actions: visibility =
511
- * include/exclude in the strip; disabled = stamp `disabled: true` on
512
- * the meta so the renderer greys it out.
513
- */
514
- async function resolveExtraItemActions(
515
- actions: Action[],
516
- ctx: RenderContext,
517
- rowValues: Record<string, unknown>,
518
- ): Promise<ActionMeta[]> {
519
- const out: ActionMeta[] = []
520
- for (const action of actions) {
521
- const evalCtx: ActionVisibilityContext = { values: rowValues }
522
- if (ctx.record !== undefined) evalCtx.record = ctx.record
523
- if (ctx.user !== undefined) evalCtx.user = ctx.user
524
- let visible = true
525
- let disabled = false
526
- if (action.hasVisibilityRules()) {
527
- const result = await action.evaluate(evalCtx)
528
- visible = result.visible
529
- disabled = result.disabled
530
- }
531
- if (!visible) continue
532
- const meta = action.toMeta()
533
- if (disabled) meta.disabled = true
534
- out.push(meta)
535
- }
536
- return out
537
- }
538
-
539
- function coerceRowValues(raw: unknown): Record<string, unknown> {
540
- if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
541
- return { ...(raw as Record<string, unknown>) }
542
- }
543
- return {}
544
- }
545
-
546
- /**
547
- * Variant of `coerceRowValues` for `Repeater.simple(field)`. Object rows
548
- * pass through (already wrapped); primitive / null / undefined entries
549
- * are wrapped under the inner field's name so the resolve / validate /
550
- * coerce pipeline keeps using the standard `{ <innerName>: value }`
551
- * shape regardless of whether the caller fed flat `[v1, v2]` (loaded
552
- * record, or `withValues({ name: ['x'] })`) or wrapped `[{name: v1}]`
553
- * (post-coerce, post-state-update).
554
- */
555
- function coerceSimpleRowValues(raw: unknown, innerName: string): Record<string, unknown> {
556
- if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
557
- return { ...(raw as Record<string, unknown>) }
558
- }
559
- if (raw === undefined) return {}
560
- return { [innerName]: raw }
561
- }
562
-
563
- // Fail-closed-as-visible: a throwing predicate keeps the row visible —
564
- // inverse of `Element.evaluateVisibility` because silently hiding data the
565
- // user is editing is the worse failure mode.
566
- async function evalItemHidden(
567
- rule: RepeaterItemHiddenRule,
568
- ctx: LayoutContext,
569
- ownerId: string,
570
- ): Promise<boolean> {
571
- if (typeof rule === 'boolean') return rule
572
- try {
573
- return Boolean(await rule(ctx))
574
- } catch (err) {
575
- console.warn(`[pilotiq] itemHidden() on ${ownerId} threw:`, err)
576
- return false
577
- }
578
- }
579
-
580
- /**
581
- * Stable row id. Prefers a round-tripped `__id` posted from the client
582
- * (string-only — anything else is ignored to keep the meta JSON-clean),
583
- * otherwise generates a deterministic id from the field name + row index
584
- * so server re-renders are idempotent. The client renderer (Step 7)
585
- * upgrades fresh rows to crypto-random UUIDs before persisting them
586
- * through the form-state map.
587
- */
588
- function readRowId(row: Record<string, unknown>, fieldName: string, index: number): string {
589
- const raw = row['__id']
590
- if (typeof raw === 'string' && raw.length > 0) return raw
591
- return `${fieldName}-${index}`
592
- }
593
-
594
- /**
595
- * Per-row resolution for `BuilderField`. Each submitted row is
596
- * `{ __id?, type, data?: {…} }`. We pick the block matching `type`,
597
- * resolve its `schema()` against a row-scoped context where `values`
598
- * is the row's `data` body (so `$get('heading')` reads the row's
599
- * heading, not the parent form's), and stamp `meta.rows`.
600
- *
601
- * Rows whose `type` doesn't match a registered block are shipped with
602
- * `unknownType: true` and empty `children` — the renderer falls back to
603
- * a "missing block" placeholder. Values still round-trip through
604
- * FormData (the renderer keeps the row's `__id` + `type` in hidden
605
- * inputs) so a config-rollback doesn't silently drop data.
606
- *
607
- * No template is shipped (`BuilderFieldMeta` doesn't carry one): the
608
- * client builds fresh rows by reading the picked block's children from
609
- * a server-resolved sample after the user picks the type. v1 ships
610
- * empty `data: {}` and lets the inner schema's `default()` values
611
- * surface on the next live re-resolve.
612
- */
613
- async function resolveBuilderRows(
614
- field: BuilderField,
615
- ctx: RenderContext,
616
- meta: ElementMeta,
617
- ): Promise<void> {
618
- const submitted = ctx.values?.[field.name]
619
- const rowsInput = Array.isArray(submitted) ? submitted.map(coerceBuilderRow) : []
620
-
621
- const labelFn = field.getItemLabel()
622
- const hiddenFn = field.getItemHidden()
623
- const canDeleteFn = field.getItemCanDelete()
624
- const canCloneFn = field.getItemCanClone()
625
- const canReorderFn = field.getItemCanReorder()
626
-
627
- const rows = await Promise.all(rowsInput.map(async (rowEntry, index) => {
628
- const blockName = rowEntry.type
629
- const block = blockName ? field.getBlock(blockName) : undefined
630
- const data = rowEntry.data
631
- const id = readBuilderRowId(rowEntry, field.name, index)
632
-
633
- if (!block) {
634
- const unknown: BuilderRowMeta = {
635
- id,
636
- type: blockName ?? '',
637
- children: [],
638
- unknownType: true,
639
- }
640
- return unknown
641
- }
642
-
643
- // Builder siblings are scoped to same-block-type rows only — the
644
- // option pickers in a `heading` block aren't shadowed by picks in a
645
- // `paragraph` block (different schemas, different `name` keys).
646
- // Each entry is the sibling's `data` map (not the envelope) so the
647
- // field's lookup `siblings[i][fieldName]` works the same as Repeater.
648
- const siblings: Record<string, unknown>[] = []
649
- for (let j = 0; j < rowsInput.length; j++) {
650
- if (j === index) continue
651
- const other = rowsInput[j]!
652
- if (other.type !== blockName) continue
653
- siblings.push(other.data)
654
- }
655
- const rowCtx: RenderContext = {
656
- ...ctx,
657
- values: data,
658
- $get: (name: string) => data[name],
659
- $set: (name: string, value: unknown) => { data[name] = value },
660
- row: {
661
- index,
662
- $get: (name: string) => data[name],
663
- $set: (name: string, value: unknown) => { data[name] = value },
664
- siblings,
665
- blockType: block.name,
666
- },
667
- }
668
- delete rowCtx.changed
669
-
670
- const children = await resolveAll(block.getSchema(), rowCtx)
671
- const row: BuilderRowMeta = { id, type: block.name, children }
672
-
673
- if (labelFn) {
674
- try {
675
- const label = labelFn(data, block.name)
676
- if (typeof label === 'string') row.itemLabel = label
677
- } catch (err) {
678
- console.warn(`[pilotiq] itemLabel() on Builder "${field.name}" threw:`, err)
679
- }
680
- }
681
- const needsLayoutCtx = hiddenFn !== undefined
682
- || canDeleteFn !== undefined
683
- || canCloneFn !== undefined
684
- || canReorderFn !== undefined
685
- const layoutCtx = needsLayoutCtx ? buildLayoutContext(rowCtx) : undefined
686
- if (hiddenFn !== undefined && layoutCtx !== undefined) {
687
- const hidden = await evalItemHidden(hiddenFn, layoutCtx, `Builder "${field.name}"`)
688
- if (hidden) row.hidden = true
689
- }
690
- if (canDeleteFn !== undefined && layoutCtx !== undefined) {
691
- const allowed = await evalItemCan(canDeleteFn, layoutCtx, 'itemCanDelete', `Builder "${field.name}"`)
692
- if (!allowed) row.canDelete = false
693
- }
694
- if (canCloneFn !== undefined && layoutCtx !== undefined) {
695
- const allowed = await evalItemCan(canCloneFn, layoutCtx, 'itemCanClone', `Builder "${field.name}"`)
696
- if (!allowed) row.canClone = false
697
- }
698
- if (canReorderFn !== undefined && layoutCtx !== undefined) {
699
- const allowed = await evalItemCan(canReorderFn, layoutCtx, 'itemCanReorder', `Builder "${field.name}"`)
700
- if (!allowed) row.canReorder = false
701
- }
702
- const extras = field.getExtraItemActions()
703
- if (extras.length > 0) {
704
- const resolved = await resolveExtraItemActions(extras, ctx, data)
705
- if (resolved.length > 0) row.extraActions = resolved
706
- }
707
- return row
708
- }))
709
-
710
- // Resolve a fresh-row template per block (empty values map). The
711
- // client uses these blueprints when the user picks a block from the
712
- // picker, so the new row's inputs render with whatever `default()`
713
- // values + visibility state apply to a blank record. Resolved here
714
- // rather than once-and-cached because conditional `options(fn)` can
715
- // depend on the surrounding `record / user / values` context.
716
- //
717
- // `Block.visible(rule)` is evaluated against an outer (not row-scoped)
718
- // LayoutContext — it's a picker-time gate, not a per-row gate. Hidden
719
- // blocks drop from `meta.blocks` so the picker doesn't show them; rows
720
- // already in `submitted` data of that block-type still render above
721
- // (the for-loop over `rowsInput` is independent of `getBlocks()`).
722
- const pickerLayoutCtx = buildLayoutContext(ctx)
723
- const blocksMetaRaw = await Promise.all(field.getBlocks().map(async block => {
724
- const visible = await block.evaluateVisibility(pickerLayoutCtx)
725
- if (!visible) return null
726
- const templateCtx: RenderContext = { ...ctx, values: {} }
727
- delete templateCtx.row
728
- delete templateCtx.changed
729
- const template = await resolveAll(block.getSchema(), templateCtx)
730
- const m = block.toMeta()
731
- m.template = template
732
- return m
733
- }))
734
- const blocksMeta = blocksMetaRaw.filter((m): m is BlockMeta => m !== null)
735
-
736
- meta['rows'] = rows
737
- meta['blocks'] = blocksMeta
738
- }
739
-
740
- /**
741
- * Per-row resolution for `RepeatableEntry`. Reads the array off
742
- * `ctx.record[name]`, resolves the inner schema once per row with `record`
743
- * scoped to that row's data, and stamps `meta.rows` with the resolved
744
- * children.
745
- *
746
- * Non-array / null / undefined / empty-array values stamp an empty
747
- * `meta.rows` — the renderer falls through to the `default()` placeholder
748
- * (inherited from `Entry.default()`).
749
- */
750
- async function resolveRepeatableRows(
751
- entry: RepeatableEntry,
752
- ctx: RenderContext,
753
- meta: ElementMeta,
754
- ): Promise<void> {
755
- const inner = entry.getInnerSchema()
756
- const fieldName = entry.getName()
757
- const sourceArray = readRecordArray(ctx.record, fieldName)
758
- if (sourceArray === undefined || sourceArray.length === 0 || inner.length === 0) {
759
- meta['rows'] = []
760
- return
761
- }
762
-
763
- const rows = await Promise.all(sourceArray.map(async (raw, index) => {
764
- const rowRecord = coerceRowRecord(raw)
765
- const rowCtx: RenderContext = { ...ctx, record: rowRecord }
766
- delete rowCtx.values
767
- delete rowCtx.$get
768
- delete rowCtx.$set
769
- delete rowCtx.changed
770
- delete rowCtx.row
771
- const children = await resolveAll(inner, rowCtx)
772
- const id = readRepeatableRowId(raw, fieldName, index)
773
- const row: RepeatableEntryRowMeta = { id, children }
774
- return row
775
- }))
776
-
777
- meta['rows'] = rows
778
- }
779
-
780
- /** Read a record's array-shaped property by name. Returns `undefined` when
781
- * absent / null / non-array — callers fall through to the empty-state
782
- * placeholder. */
783
- function readRecordArray(record: unknown, name: string): unknown[] | undefined {
784
- if (record === null || record === undefined || typeof record !== 'object') return undefined
785
- const value = (record as Record<string, unknown>)[name]
786
- return Array.isArray(value) ? value : undefined
787
- }
788
-
789
- /** Coerce an arbitrary array element into a `Record<string, unknown>` for
790
- * the row's `RenderContext.record`. Object rows pass through (clone to
791
- * avoid downstream mutation); primitives stash under a reserved `_value`
792
- * key so an inner `TextEntry.make('_value')` can still target them
793
- * (mirrors the read-only equivalent of Repeater's flat-array fallback). */
794
- function coerceRowRecord(raw: unknown): Record<string, unknown> {
795
- if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
796
- return { ...(raw as Record<string, unknown>) }
797
- }
798
- if (raw === undefined || raw === null) return {}
799
- return { _value: raw }
800
- }
801
-
802
- interface BuilderRowEntry {
803
- __id?: string
804
- type: string
805
- data: Record<string, unknown>
806
- }
807
-
808
- /**
809
- * Normalize an arbitrary submitted row entry into the `{ __id?, type, data }`
810
- * envelope. Robust to JSON bodies (already-shaped) and to flat-fold output
811
- * from `coerceBuilderValue` (top-level `__id`/`type` siblings of `data`).
812
- *
813
- * Returns an entry with a possibly-empty `type` ("" → unknownType row) so
814
- * the resolver still emits a `BuilderRowMeta` for shape stability — the
815
- * renderer drops empty-type rows visually.
816
- */
817
- function coerceBuilderRow(raw: unknown): BuilderRowEntry {
818
- if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
819
- return { type: '', data: {} }
820
- }
821
- const r = raw as Record<string, unknown>
822
- const type = typeof r['type'] === 'string' ? (r['type'] as string) : ''
823
- const dataRaw = r['data']
824
- const data: Record<string, unknown> = (dataRaw && typeof dataRaw === 'object' && !Array.isArray(dataRaw))
825
- ? { ...(dataRaw as Record<string, unknown>) }
826
- : {}
827
- const out: BuilderRowEntry = { type, data }
828
- if (typeof r['__id'] === 'string') out.__id = r['__id'] as string
829
- return out
830
- }
831
-
832
- function readBuilderRowId(row: BuilderRowEntry, fieldName: string, index: number): string {
833
- if (typeof row.__id === 'string' && row.__id.length > 0) return row.__id
834
- return `${fieldName}-${index}`
835
- }
836
-
837
- /**
838
- * Evaluate a per-row capability rule (`itemCanDelete / itemCanClone /
839
- * itemCanReorder`) against a row's `LayoutContext`. Returns the boolean
840
- * "is allowed" — `true` keeps the matching button mounted, `false`
841
- * removes it on this row.
842
- *
843
- * Fail-open: when the predicate throws, the capability stays enabled and
844
- * we log a warning. This mirrors `itemHidden`'s posture — a misbehaving
845
- * gate shouldn't silently lock the user out of editing data. For real
846
- * authorization (vs. UX), gate the parent form's lifecycle hooks.
847
- *
848
- * Shared between Repeater + Builder so the failure mode is identical.
849
- */
850
- async function evalItemCan(
851
- rule: RepeaterItemCanRule,
852
- ctx: LayoutContext,
853
- setter: string,
854
- ownerId: string,
855
- ): Promise<boolean> {
856
- if (typeof rule === 'boolean') return rule
857
- try {
858
- return Boolean(await rule(ctx))
859
- } catch (err) {
860
- console.warn(`[pilotiq] ${setter}() on ${ownerId} threw:`, err)
861
- return true
862
- }
863
- }
864
-
865
- /**
866
- * Build a layout-visibility context from the resolver's `RenderContext`.
867
- * Mirrors the `Field.buildConditionContext` shape so layout `visible(fn)`
868
- * callbacks can destructure the same way as `Field.showWhen` callbacks.
869
- */
870
- /**
871
- * Resolve a `WizardActionCustomizer` against the framework default for
872
- * the given slot. The default carries a stable `name` (`wizardSubmit /
873
- * wizardNext / wizardPrevious`) and sensible chrome — the customizer
874
- * may pass through verbatim, mutate, or replace entirely. Returned
875
- * `Action` is then resolved through the standard walker so visibility
876
- * and disabled rules evaluate the same way as any other Action.
877
- */
878
- function resolveWizardAction(customizer: WizardActionCustomizer, slot: 'submit' | 'next' | 'previous'): Action {
879
- const def = slot === 'submit'
880
- ? Action.make('wizardSubmit').label('Submit').color('primary')
881
- : slot === 'next'
882
- ? Action.make('wizardNext').label('Next').color('primary')
883
- : Action.make('wizardPrevious').label('Back').color('ghost')
884
- return typeof customizer === 'function' ? customizer(def) : customizer
885
- }
886
-
887
- function buildLayoutContext(ctx: RenderContext): LayoutContext {
888
- const out: LayoutContext = {}
889
- if (ctx.record !== undefined) out.record = ctx.record
890
- if (ctx.values !== undefined) out.values = ctx.values
891
- if (ctx.$get) out.$get = ctx.$get
892
- if (ctx.$set) out.$set = ctx.$set
893
- if (ctx.user !== undefined) out.user = ctx.user
894
- if (ctx.row !== undefined) out.row = ctx.row
895
- return out
896
- }
897
-
898
- /**
899
- * Derive the render context that this element's children should see.
900
- * Currently only handles the `inlineLabelDefault` cascade — `Form` /
901
- * `Section` with `.inlineLabel(true|false)` push the value into the
902
- * descendant context so every nested `Field.buildMeta` can read it.
903
- * A nested container that re-sets the flag overrides the outer value
904
- * for its subtree (the current ctx's value is replaced, not merged).
905
- *
906
- * Returns the same `ctx` reference when nothing needs to change so
907
- * call sites that pass it down skip a fresh object allocation.
908
- */
909
- function deriveChildContext(el: Element, ctx: RenderContext): RenderContext {
910
- if (el instanceof Form || el instanceof Section) {
911
- const v = (el as Form | Section).getInlineLabel?.()
912
- if (v !== undefined && v !== ctx.inlineLabelDefault) {
913
- return { ...ctx, inlineLabelDefault: v }
914
- }
915
- }
916
- return ctx
917
- }