@pilotiq/pilotiq 0.24.1 → 0.24.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (480) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/boost/guidelines.md +566 -0
  3. package/boost/skills/pilotiq-fields/SKILL.md +47 -0
  4. package/boost/skills/pilotiq-fields/rules/field-catalog.md +288 -0
  5. package/boost/skills/pilotiq-fields/rules/reactive-fields.md +199 -0
  6. package/boost/skills/pilotiq-fields/rules/validation.md +198 -0
  7. package/boost/skills/pilotiq-relations/SKILL.md +47 -0
  8. package/boost/skills/pilotiq-relations/rules/relation-managers.md +256 -0
  9. package/boost/skills/pilotiq-relations/rules/repeater-relationship.md +177 -0
  10. package/boost/skills/pilotiq-resource/SKILL.md +61 -0
  11. package/boost/skills/pilotiq-resource/rules/authorization.md +242 -0
  12. package/boost/skills/pilotiq-resource/rules/defining-resources.md +228 -0
  13. package/boost/skills/pilotiq-resource/rules/page-overrides.md +296 -0
  14. package/package.json +6 -1
  15. package/.turbo/turbo-build.log +0 -8
  16. package/CLAUDE.md +0 -265
  17. package/src/Cluster.test.ts +0 -283
  18. package/src/Cluster.ts +0 -83
  19. package/src/Column.test.ts +0 -199
  20. package/src/Column.ts +0 -710
  21. package/src/Global.test.ts +0 -367
  22. package/src/Global.ts +0 -169
  23. package/src/Page.test.ts +0 -114
  24. package/src/Page.ts +0 -208
  25. package/src/Pilotiq.perf.test.ts +0 -252
  26. package/src/Pilotiq.test.ts +0 -129
  27. package/src/Pilotiq.ts +0 -1158
  28. package/src/PilotiqRegistry.ts +0 -36
  29. package/src/PilotiqServiceProvider.ts +0 -121
  30. package/src/RelationManager.test.ts +0 -400
  31. package/src/RelationManager.ts +0 -527
  32. package/src/RenderHook.test.ts +0 -252
  33. package/src/RenderHook.ts +0 -242
  34. package/src/Resource.test.ts +0 -284
  35. package/src/Resource.ts +0 -526
  36. package/src/RightPanel.test.ts +0 -202
  37. package/src/RightPanel.ts +0 -132
  38. package/src/Tab.test.ts +0 -91
  39. package/src/Tab.ts +0 -156
  40. package/src/UserMenuItem.ts +0 -145
  41. package/src/actions/Action.test.ts +0 -2526
  42. package/src/actions/Action.ts +0 -1515
  43. package/src/actions/ActionGroup.test.ts +0 -112
  44. package/src/actions/ActionGroup.ts +0 -173
  45. package/src/actions/attachFactory.ts +0 -172
  46. package/src/actions/bulkFactories.ts +0 -168
  47. package/src/actions/crudFactories.ts +0 -220
  48. package/src/actions/exportFactory.ts +0 -225
  49. package/src/actions/factoryHelpers.ts +0 -177
  50. package/src/actions/importFactory.ts +0 -243
  51. package/src/actions/index.ts +0 -17
  52. package/src/actions/m2mFactories.ts +0 -193
  53. package/src/actions/relationFactories.ts +0 -372
  54. package/src/applyPageHooks.test.ts +0 -463
  55. package/src/applyPageHooks.ts +0 -330
  56. package/src/authorization.test.ts +0 -483
  57. package/src/breadcrumbs.test.ts +0 -238
  58. package/src/cells/coerce.test.ts +0 -85
  59. package/src/cells/coerce.ts +0 -84
  60. package/src/clusterPaths.ts +0 -35
  61. package/src/columns/BadgeColumn.test.ts +0 -54
  62. package/src/columns/BadgeColumn.ts +0 -32
  63. package/src/columns/BooleanColumn.test.ts +0 -41
  64. package/src/columns/BooleanColumn.ts +0 -18
  65. package/src/columns/ColorColumn.test.ts +0 -37
  66. package/src/columns/ColorColumn.ts +0 -38
  67. package/src/columns/IconColumn.test.ts +0 -54
  68. package/src/columns/IconColumn.ts +0 -37
  69. package/src/columns/ImageColumn.test.ts +0 -41
  70. package/src/columns/ImageColumn.ts +0 -28
  71. package/src/columns/SelectColumn.ts +0 -98
  72. package/src/columns/TextColumn.test.ts +0 -190
  73. package/src/columns/TextColumn.ts +0 -20
  74. package/src/columns/TextInputColumn.ts +0 -68
  75. package/src/columns/ToggleColumn.ts +0 -46
  76. package/src/columns/editableColumns.test.ts +0 -238
  77. package/src/columns/index.ts +0 -9
  78. package/src/defaultGlobalPages.ts +0 -95
  79. package/src/defaultPages.test.ts +0 -634
  80. package/src/defaultPages.ts +0 -617
  81. package/src/defaultViewPage.test.ts +0 -147
  82. package/src/elements/Form.test.ts +0 -223
  83. package/src/elements/Form.ts +0 -416
  84. package/src/elements/ListTabs.ts +0 -28
  85. package/src/elements/Table.test.ts +0 -422
  86. package/src/elements/Table.ts +0 -850
  87. package/src/elements/TableGroup.test.ts +0 -260
  88. package/src/elements/TableGroup.ts +0 -334
  89. package/src/elements/dispatchAction.test.ts +0 -463
  90. package/src/elements/dispatchAction.ts +0 -355
  91. package/src/elements/dispatchForm.test.ts +0 -477
  92. package/src/elements/dispatchForm.ts +0 -1993
  93. package/src/elements/dispatchTable.test.ts +0 -1514
  94. package/src/elements/dispatchTable.ts +0 -745
  95. package/src/elements/index.ts +0 -21
  96. package/src/entries/BadgeEntry.ts +0 -39
  97. package/src/entries/CodeEntry.test.ts +0 -40
  98. package/src/entries/CodeEntry.ts +0 -52
  99. package/src/entries/ColorEntry.ts +0 -63
  100. package/src/entries/ComponentEntry.test.ts +0 -173
  101. package/src/entries/ComponentEntry.ts +0 -95
  102. package/src/entries/Entry.ts +0 -304
  103. package/src/entries/IconEntry.ts +0 -49
  104. package/src/entries/ImageEntry.ts +0 -61
  105. package/src/entries/KeyValueEntry.ts +0 -47
  106. package/src/entries/RepeatableEntry.test.ts +0 -239
  107. package/src/entries/RepeatableEntry.ts +0 -173
  108. package/src/entries/TextEntry.test.ts +0 -394
  109. package/src/entries/TextEntry.ts +0 -60
  110. package/src/entries/index.ts +0 -12
  111. package/src/entries/leaves.test.ts +0 -306
  112. package/src/entries/registry.ts +0 -54
  113. package/src/fields/BuilderField.test.ts +0 -1188
  114. package/src/fields/BuilderField.ts +0 -605
  115. package/src/fields/BuilderRelationship.test.ts +0 -811
  116. package/src/fields/CheckboxField.test.ts +0 -44
  117. package/src/fields/CheckboxField.ts +0 -27
  118. package/src/fields/CheckboxListField.test.ts +0 -99
  119. package/src/fields/CheckboxListField.ts +0 -66
  120. package/src/fields/ColorPickerField.test.ts +0 -33
  121. package/src/fields/ColorPickerField.ts +0 -25
  122. package/src/fields/DateField.ts +0 -54
  123. package/src/fields/DateTimeField.test.ts +0 -55
  124. package/src/fields/EmailField.ts +0 -16
  125. package/src/fields/Field.test.ts +0 -654
  126. package/src/fields/Field.ts +0 -817
  127. package/src/fields/FileUploadField.test.ts +0 -143
  128. package/src/fields/FileUploadField.ts +0 -159
  129. package/src/fields/HiddenField.test.ts +0 -27
  130. package/src/fields/HiddenField.ts +0 -28
  131. package/src/fields/KeyValueField.test.ts +0 -105
  132. package/src/fields/KeyValueField.ts +0 -55
  133. package/src/fields/MarkdownField.test.ts +0 -167
  134. package/src/fields/MarkdownField.ts +0 -162
  135. package/src/fields/NumberField.ts +0 -33
  136. package/src/fields/RadioField.test.ts +0 -94
  137. package/src/fields/RadioField.ts +0 -67
  138. package/src/fields/RepeaterField.test.ts +0 -1806
  139. package/src/fields/RepeaterField.ts +0 -939
  140. package/src/fields/RepeaterRelationship.test.ts +0 -1923
  141. package/src/fields/RepeaterSimple.test.ts +0 -248
  142. package/src/fields/RowButton.test.ts +0 -219
  143. package/src/fields/RowButton.ts +0 -135
  144. package/src/fields/SelectField.test.ts +0 -192
  145. package/src/fields/SelectField.ts +0 -235
  146. package/src/fields/SliderField.test.ts +0 -50
  147. package/src/fields/SliderField.ts +0 -53
  148. package/src/fields/SlugField.ts +0 -24
  149. package/src/fields/TagsInputField.test.ts +0 -154
  150. package/src/fields/TagsInputField.ts +0 -133
  151. package/src/fields/TextField.test.ts +0 -213
  152. package/src/fields/TextField.ts +0 -177
  153. package/src/fields/TextareaField.test.ts +0 -58
  154. package/src/fields/TextareaField.ts +0 -59
  155. package/src/fields/ToggleButtonsField.test.ts +0 -106
  156. package/src/fields/ToggleButtonsField.ts +0 -59
  157. package/src/fields/ToggleField.ts +0 -16
  158. package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +0 -319
  159. package/src/fields/optionsResolver.ts +0 -95
  160. package/src/fields/resolveField.ts +0 -28
  161. package/src/filters/BooleanFilter.ts +0 -35
  162. package/src/filters/DateRangeFilter.test.ts +0 -194
  163. package/src/filters/DateRangeFilter.ts +0 -148
  164. package/src/filters/Filter.test.ts +0 -268
  165. package/src/filters/Filter.ts +0 -184
  166. package/src/filters/FormFilter.test.ts +0 -238
  167. package/src/filters/FormFilter.ts +0 -215
  168. package/src/filters/MultiSelectFilter.test.ts +0 -119
  169. package/src/filters/MultiSelectFilter.ts +0 -78
  170. package/src/filters/QueryBuilderFilter.test.ts +0 -662
  171. package/src/filters/QueryBuilderFilter.ts +0 -398
  172. package/src/filters/SelectFilter.ts +0 -46
  173. package/src/filters/TernaryFilter.test.ts +0 -160
  174. package/src/filters/TernaryFilter.ts +0 -72
  175. package/src/filters/TrashedFilter.test.ts +0 -149
  176. package/src/filters/TrashedFilter.ts +0 -55
  177. package/src/filters/queryBuilder/BooleanConstraint.ts +0 -31
  178. package/src/filters/queryBuilder/Constraint.ts +0 -115
  179. package/src/filters/queryBuilder/DateConstraint.ts +0 -69
  180. package/src/filters/queryBuilder/NumberConstraint.ts +0 -66
  181. package/src/filters/queryBuilder/SelectConstraint.ts +0 -72
  182. package/src/filters/queryBuilder/TextConstraint.ts +0 -64
  183. package/src/filters/queryBuilder/index.ts +0 -12
  184. package/src/icons/index.ts +0 -2
  185. package/src/icons/lucide.ts +0 -204
  186. package/src/icons/registry.test.ts +0 -56
  187. package/src/icons/registry.ts +0 -41
  188. package/src/icons/types.ts +0 -47
  189. package/src/index.ts +0 -525
  190. package/src/io/csv.test.ts +0 -142
  191. package/src/io/csv.ts +0 -170
  192. package/src/nestedRelationManagerData.test.ts +0 -547
  193. package/src/notifications/Notification.test.ts +0 -210
  194. package/src/notifications/Notification.ts +0 -354
  195. package/src/notifications/broadcast.test.ts +0 -110
  196. package/src/notifications/broadcast.ts +0 -95
  197. package/src/notifications/database.test.ts +0 -383
  198. package/src/notifications/database.ts +0 -398
  199. package/src/notifications/databaseNotifications.test.ts +0 -187
  200. package/src/notifications/dispatchNotificationAction.test.ts +0 -341
  201. package/src/notifications/dispatchNotificationAction.ts +0 -142
  202. package/src/notifications/flash.test.ts +0 -89
  203. package/src/notifications/flash.ts +0 -71
  204. package/src/notifications/index.ts +0 -45
  205. package/src/notifications/registerBroadcastAuth.test.ts +0 -134
  206. package/src/notifications/registerBroadcastAuth.ts +0 -100
  207. package/src/notifications/resolveSavedNotification.test.ts +0 -82
  208. package/src/notifications/resolveSavedNotification.ts +0 -59
  209. package/src/notifications/types.ts +0 -93
  210. package/src/orm/m2mAccessor.ts +0 -66
  211. package/src/orm/modelDefaults.test.ts +0 -633
  212. package/src/orm/modelDefaults.ts +0 -666
  213. package/src/pageData/breadcrumbs.ts +0 -288
  214. package/src/pageData/forms.ts +0 -578
  215. package/src/pageData/helpers.ts +0 -857
  216. package/src/pageData/misc.ts +0 -347
  217. package/src/pageData/navigation.ts +0 -842
  218. package/src/pageData/relationPages.ts +0 -1248
  219. package/src/pageData/relationTabs.ts +0 -286
  220. package/src/pageData/resourcePages.ts +0 -609
  221. package/src/pageData.test.ts +0 -1545
  222. package/src/pageData.ts +0 -341
  223. package/src/plugins/index.ts +0 -8
  224. package/src/plugins/themeEditor.test.ts +0 -36
  225. package/src/plugins/themeEditor.ts +0 -45
  226. package/src/react/AppShell.tsx +0 -251
  227. package/src/react/CollabExtensionFactoryRegistry.ts +0 -55
  228. package/src/react/CollabRoomContext.ts +0 -98
  229. package/src/react/CollabTextRendererRegistry.ts +0 -102
  230. package/src/react/CommandPalette.tsx +0 -375
  231. package/src/react/CurrentUserContext.tsx +0 -50
  232. package/src/react/CustomPageWrapperGate.tsx +0 -69
  233. package/src/react/CustomPageWrapperRegistry.ts +0 -45
  234. package/src/react/FieldFocusReporterRegistry.ts +0 -37
  235. package/src/react/FieldLabelSlotRegistry.ts +0 -30
  236. package/src/react/FieldPresenceRegistry.ts +0 -46
  237. package/src/react/FormCollabBindingRegistry.ts +0 -242
  238. package/src/react/FormStateContext.tsx +0 -591
  239. package/src/react/HeadHooks.tsx +0 -126
  240. package/src/react/MarkdownEditorRegistry.test.ts +0 -38
  241. package/src/react/MarkdownEditorRegistry.ts +0 -107
  242. package/src/react/NotificationActionStrip.tsx +0 -263
  243. package/src/react/NotificationBell.tsx +0 -426
  244. package/src/react/PendingSuggestionApplierRegistry.test.ts +0 -97
  245. package/src/react/PendingSuggestionApplierRegistry.ts +0 -98
  246. package/src/react/PendingSuggestionOverlayRegistry.ts +0 -54
  247. package/src/react/PendingSuggestionsContext.tsx +0 -172
  248. package/src/react/RecordWrapperGate.tsx +0 -58
  249. package/src/react/RecordWrapperRegistry.ts +0 -39
  250. package/src/react/RenderHookSlot.tsx +0 -32
  251. package/src/react/RightSidebar.tsx +0 -257
  252. package/src/react/RightSidebarContext.tsx +0 -234
  253. package/src/react/RightSidebarTrigger.tsx +0 -53
  254. package/src/react/RowCoordsContext.tsx +0 -23
  255. package/src/react/SchemaRenderer.tsx +0 -549
  256. package/src/react/SearchTrigger.tsx +0 -46
  257. package/src/react/ThemeProvider.tsx +0 -93
  258. package/src/react/ThemeSettingsPage.tsx +0 -579
  259. package/src/react/ThemeToggle.tsx +0 -20
  260. package/src/react/Toaster.tsx +0 -158
  261. package/src/react/UserMenu.tsx +0 -196
  262. package/src/react/WidgetDataContext.tsx +0 -157
  263. package/src/react/cells/EditableCell.tsx +0 -389
  264. package/src/react/component-slots.test.ts +0 -103
  265. package/src/react/component-slots.ts +0 -116
  266. package/src/react/fieldJsHandler.test.ts +0 -166
  267. package/src/react/fieldJsHandler.ts +0 -79
  268. package/src/react/fields/BuilderInput.tsx +0 -1078
  269. package/src/react/fields/CheckboxInput.tsx +0 -39
  270. package/src/react/fields/CheckboxListInput.tsx +0 -102
  271. package/src/react/fields/ColorInput.tsx +0 -71
  272. package/src/react/fields/DateFieldInput.tsx +0 -70
  273. package/src/react/fields/DateTimeInput.tsx +0 -62
  274. package/src/react/fields/FieldShell.tsx +0 -348
  275. package/src/react/fields/FileUploadInput.tsx +0 -639
  276. package/src/react/fields/HiddenInput.tsx +0 -17
  277. package/src/react/fields/KeyValueInput.tsx +0 -230
  278. package/src/react/fields/MarkdownInput.tsx +0 -560
  279. package/src/react/fields/RadioInput.tsx +0 -81
  280. package/src/react/fields/RepeaterInput.test.ts +0 -116
  281. package/src/react/fields/RepeaterInput.tsx +0 -1420
  282. package/src/react/fields/SelectFieldInput.tsx +0 -280
  283. package/src/react/fields/SliderInput.tsx +0 -81
  284. package/src/react/fields/TagsInput.tsx +0 -283
  285. package/src/react/fields/TextLikeInput.tsx +0 -256
  286. package/src/react/fields/ToggleButtonsInput.tsx +0 -60
  287. package/src/react/fields/ToggleFieldInput.tsx +0 -56
  288. package/src/react/fields/relationshipRenameDispatch.test.ts +0 -106
  289. package/src/react/fields/relationshipRenameDispatch.ts +0 -97
  290. package/src/react/fields/repeaterReconcile.test.ts +0 -114
  291. package/src/react/fields/repeaterReconcile.ts +0 -104
  292. package/src/react/fields/rowChromeButton.tsx +0 -336
  293. package/src/react/fields/rowState.ts +0 -106
  294. package/src/react/fields/syncRowGates.test.ts +0 -202
  295. package/src/react/fields/syncRowGates.ts +0 -66
  296. package/src/react/fields/textInputControls.tsx +0 -238
  297. package/src/react/fields/useRowReorderDnd.ts +0 -78
  298. package/src/react/formStateHelpers.test.ts +0 -508
  299. package/src/react/formStateHelpers.ts +0 -381
  300. package/src/react/hooks/use-mobile.ts +0 -19
  301. package/src/react/icon-context.tsx +0 -60
  302. package/src/react/index.ts +0 -194
  303. package/src/react/layouts/SidebarLayout.tsx +0 -250
  304. package/src/react/layouts/TopbarLayout.tsx +0 -258
  305. package/src/react/navigate.tsx +0 -37
  306. package/src/react/onProviderSynced.test.ts +0 -90
  307. package/src/react/parseRecordEditUrl.test.ts +0 -122
  308. package/src/react/parseRecordEditUrl.ts +0 -94
  309. package/src/react/persistedState.ts +0 -40
  310. package/src/react/registry.ts +0 -48
  311. package/src/react/right-panel-registry.tsx +0 -47
  312. package/src/react/schemaRenderer/AlertRenderer.tsx +0 -112
  313. package/src/react/schemaRenderer/EntryRenderer.tsx +0 -501
  314. package/src/react/schemaRenderer/SectionRenderer.tsx +0 -120
  315. package/src/react/schemaRenderer/SimpleElements.tsx +0 -306
  316. package/src/react/schemaRenderer/TabsRenderer.tsx +0 -62
  317. package/src/react/schemaRenderer/WizardRenderer.tsx +0 -338
  318. package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +0 -177
  319. package/src/react/schemaRenderer/action/ActionModalDialog.tsx +0 -273
  320. package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +0 -61
  321. package/src/react/schemaRenderer/action/HandlerActionButton.tsx +0 -43
  322. package/src/react/schemaRenderer/action/MethodActionButton.tsx +0 -64
  323. package/src/react/schemaRenderer/action/buttons.tsx +0 -99
  324. package/src/react/schemaRenderer/action/helpers.ts +0 -140
  325. package/src/react/schemaRenderer/action/renderAction.tsx +0 -245
  326. package/src/react/schemaRenderer/columnFormat.ts +0 -65
  327. package/src/react/schemaRenderer/constants.ts +0 -50
  328. package/src/react/schemaRenderer/form/FormRenderer.tsx +0 -274
  329. package/src/react/schemaRenderer/form/renderField.tsx +0 -511
  330. package/src/react/schemaRenderer/helpers.tsx +0 -81
  331. package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +0 -308
  332. package/src/react/schemaRenderer/table/TableRenderer.tsx +0 -123
  333. package/src/react/schemaRenderer/table/TableRendererBody.tsx +0 -974
  334. package/src/react/schemaRenderer/table/filters.tsx +0 -1233
  335. package/src/react/schemaRenderer/table/formatCell.tsx +0 -264
  336. package/src/react/schemaRenderer/table/links.tsx +0 -112
  337. package/src/react/schemaRenderer/table/renderRowActions.tsx +0 -52
  338. package/src/react/schemaRenderer/table/url.tsx +0 -143
  339. package/src/react/theme-preview/apply.ts +0 -99
  340. package/src/react/theme-preview/build-html.ts +0 -436
  341. package/src/react/ui/button.tsx +0 -51
  342. package/src/react/ui/calendar.tsx +0 -67
  343. package/src/react/ui/checkbox.tsx +0 -29
  344. package/src/react/ui/dialog.tsx +0 -108
  345. package/src/react/ui/dropdown-menu.tsx +0 -97
  346. package/src/react/ui/input.tsx +0 -20
  347. package/src/react/ui/label.tsx +0 -21
  348. package/src/react/ui/popover.tsx +0 -50
  349. package/src/react/ui/select.tsx +0 -169
  350. package/src/react/ui/separator.tsx +0 -25
  351. package/src/react/ui/sheet.tsx +0 -136
  352. package/src/react/ui/sidebar.tsx +0 -723
  353. package/src/react/ui/skeleton.tsx +0 -13
  354. package/src/react/ui/slider.tsx +0 -34
  355. package/src/react/ui/switch.tsx +0 -28
  356. package/src/react/ui/table.tsx +0 -105
  357. package/src/react/ui/tabs.tsx +0 -63
  358. package/src/react/ui/textarea.tsx +0 -18
  359. package/src/react/ui/tooltip.tsx +0 -64
  360. package/src/react/useResizableWidth.ts +0 -139
  361. package/src/react/utils.ts +0 -6
  362. package/src/react/widgetRegistry.test.ts +0 -43
  363. package/src/react/widgetRegistry.ts +0 -50
  364. package/src/react/widgets/StatsOverviewRenderer.tsx +0 -232
  365. package/src/react/widgets/TableWidgetRenderer.tsx +0 -231
  366. package/src/react/widgets/ViewRenderer.tsx +0 -71
  367. package/src/relationManagerData.test.ts +0 -1595
  368. package/src/richtext/index.ts +0 -8
  369. package/src/richtext/registry.ts +0 -89
  370. package/src/routes/globals.ts +0 -148
  371. package/src/routes/guard.test.ts +0 -325
  372. package/src/routes/helpers.ts +0 -704
  373. package/src/routes/pages.ts +0 -175
  374. package/src/routes/panel.ts +0 -204
  375. package/src/routes/relations.ts +0 -1243
  376. package/src/routes/resources.ts +0 -781
  377. package/src/routes/theme.ts +0 -91
  378. package/src/routes-nested-relations.test.ts +0 -676
  379. package/src/routes-relations.test.ts +0 -972
  380. package/src/routes.test.ts +0 -2027
  381. package/src/routes.ts +0 -303
  382. package/src/schema/Alert.test.ts +0 -109
  383. package/src/schema/Alert.ts +0 -131
  384. package/src/schema/Block.ts +0 -169
  385. package/src/schema/Breadcrumbs.ts +0 -40
  386. package/src/schema/Card.ts +0 -35
  387. package/src/schema/Divider.ts +0 -20
  388. package/src/schema/Element.ts +0 -219
  389. package/src/schema/EmptyState.test.ts +0 -37
  390. package/src/schema/EmptyState.ts +0 -63
  391. package/src/schema/Fieldset.ts +0 -43
  392. package/src/schema/Grid.ts +0 -43
  393. package/src/schema/Group.ts +0 -30
  394. package/src/schema/Heading.ts +0 -39
  395. package/src/schema/Html.ts +0 -67
  396. package/src/schema/Icon.ts +0 -54
  397. package/src/schema/Image.ts +0 -57
  398. package/src/schema/LinkTag.ts +0 -41
  399. package/src/schema/Markdown.ts +0 -85
  400. package/src/schema/MetaTag.ts +0 -41
  401. package/src/schema/RelationTabs.ts +0 -71
  402. package/src/schema/ScriptTag.ts +0 -55
  403. package/src/schema/Section.ts +0 -160
  404. package/src/schema/ServerDataElement.test.ts +0 -140
  405. package/src/schema/ServerDataElement.ts +0 -156
  406. package/src/schema/SlotComponent.test.ts +0 -77
  407. package/src/schema/SlotComponent.ts +0 -71
  408. package/src/schema/Split.ts +0 -50
  409. package/src/schema/Stat.test.ts +0 -118
  410. package/src/schema/Stat.ts +0 -154
  411. package/src/schema/StatsOverview.test.ts +0 -141
  412. package/src/schema/StatsOverview.ts +0 -119
  413. package/src/schema/StyleTag.ts +0 -35
  414. package/src/schema/TableWidget.test.ts +0 -297
  415. package/src/schema/TableWidget.ts +0 -289
  416. package/src/schema/Tabs.ts +0 -79
  417. package/src/schema/Text.ts +0 -58
  418. package/src/schema/UnorderedList.ts +0 -49
  419. package/src/schema/View.test.ts +0 -111
  420. package/src/schema/View.ts +0 -127
  421. package/src/schema/Wizard.ts +0 -220
  422. package/src/schema/containers.test.ts +0 -564
  423. package/src/schema/headTags.test.ts +0 -134
  424. package/src/schema/index.ts +0 -40
  425. package/src/schema/primes.test.ts +0 -269
  426. package/src/schema/resolveSchema.test.ts +0 -379
  427. package/src/schema/resolveSchema.ts +0 -917
  428. package/src/schema/sanitize.ts +0 -58
  429. package/src/search.test.ts +0 -446
  430. package/src/search.ts +0 -178
  431. package/src/sessionFilters.test.ts +0 -375
  432. package/src/sessionFilters.ts +0 -143
  433. package/src/slot-components/index.ts +0 -10
  434. package/src/slot-components/registry.ts +0 -56
  435. package/src/styles/file-upload.css +0 -13
  436. package/src/summarizers/Summarizer.test.ts +0 -84
  437. package/src/summarizers/Summarizer.ts +0 -123
  438. package/src/summarizers/index.ts +0 -11
  439. package/src/theme/base-colors.ts +0 -68
  440. package/src/theme/chart-colors.ts +0 -50
  441. package/src/theme/colors.ts +0 -447
  442. package/src/theme/generate-css.test.ts +0 -139
  443. package/src/theme/generate-css.ts +0 -44
  444. package/src/theme/generate-scale.test.ts +0 -106
  445. package/src/theme/generate-scale.ts +0 -97
  446. package/src/theme/icon-map.ts +0 -42
  447. package/src/theme/index.ts +0 -34
  448. package/src/theme/migrate.test.ts +0 -178
  449. package/src/theme/migrate.ts +0 -81
  450. package/src/theme/presets.ts +0 -135
  451. package/src/theme/radius.ts +0 -18
  452. package/src/theme/resolve.test.ts +0 -238
  453. package/src/theme/resolve.ts +0 -96
  454. package/src/theme/spacing.ts +0 -18
  455. package/src/theme/storage.test.ts +0 -126
  456. package/src/theme/storage.ts +0 -106
  457. package/src/theme/theme-colors.ts +0 -88
  458. package/src/theme/types.ts +0 -125
  459. package/src/uploads/UploadAdapter.ts +0 -35
  460. package/src/uploads/index.ts +0 -2
  461. package/src/uploads/localUpload.test.ts +0 -70
  462. package/src/uploads/localUpload.ts +0 -84
  463. package/src/validation/Validator.ts +0 -49
  464. package/src/validation/index.ts +0 -28
  465. package/src/validation/rules.ts +0 -78
  466. package/src/validation/runValidators.ts +0 -435
  467. package/src/validation/uniqueValidator.test.ts +0 -196
  468. package/src/validation/uniqueValidator.ts +0 -133
  469. package/src/validation/validators.test.ts +0 -268
  470. package/src/vite.test.ts +0 -184
  471. package/src/vite.ts +0 -787
  472. package/src/widgets/index.ts +0 -10
  473. package/src/widgets/registry.ts +0 -45
  474. package/src/widgets.test.ts +0 -592
  475. package/tsconfig.build.json +0 -11
  476. package/tsconfig.json +0 -4
  477. package/tsconfig.test.json +0 -10
  478. package/views/react/Dashboard.tsx +0 -27
  479. package/views/react/Resources/Form.tsx +0 -102
  480. package/views/react/Resources/Index.tsx +0 -49
@@ -1,609 +0,0 @@
1
- import type { Pilotiq } from '../Pilotiq.js'
2
- import { PilotiqRegistry } from '../PilotiqRegistry.js'
3
- import type { Page } from '../Page.js'
4
- import type { ResourceClass } from '../Resource.js'
5
- import { resourceBasePath } from '../clusterPaths.js'
6
- import { Element } from '../schema/Element.js'
7
- import { resolveSchema, type SchemaContext } from '../schema/resolveSchema.js'
8
- import { Form } from '../elements/Form.js'
9
- import { Table } from '../elements/Table.js'
10
- import { Column } from '../Column.js'
11
- import { ListTabs } from '../elements/ListTabs.js'
12
- import { ListTab } from '../Tab.js'
13
- import { TrashedFilter } from '../filters/TrashedFilter.js'
14
- import { loadTableRecords } from '../elements/dispatchTable.js'
15
- import { consumeFlashedNotifications } from '../notifications/flash.js'
16
- import { serializeIcon } from '../icons/types.js'
17
- import {
18
- findRecord, getPrimaryKey, modelLoadRecord, modelSave,
19
- type ModelLike,
20
- } from '../orm/modelDefaults.js'
21
- import {
22
- resourceCreateBreadcrumbs,
23
- resourceEditBreadcrumbs,
24
- resourceListBreadcrumbs,
25
- resourceViewBreadcrumbs,
26
- } from './breadcrumbs.js'
27
- import {
28
- applyEditPageHydrators,
29
- applyFillPipeline,
30
- applyRelationshipBuilderFill,
31
- applyRelationshipRepeaterFill,
32
- callPageSchema,
33
- resolveServerDataElements,
34
- tagActionDispatch,
35
- tagCellEditUrls,
36
- tagFieldAiUrls,
37
- tagFormActions,
38
- tagFormSubresourceUrls,
39
- tagTableDeferred,
40
- tagTableReorderUrls,
41
- tagWidgetUrls,
42
- uploadCtx,
43
- userCtx,
44
- } from './helpers.js'
45
- import { applyRoleHooks, panelInfo, type PanelInfoRoute } from './navigation.js'
46
- import { findForms } from '../elements/dispatchForm.js'
47
- import { buildRelationTabs, deriveParentTitle, safeBool } from './relationTabs.js'
48
-
49
- // ─── Resource page builders ─────────────────────────────────
50
- //
51
- // Resource-scoped GET-route data builders: dashboard, list, table
52
- // (deferred-load shell), create, edit, view, and the optional record
53
- // sub-page roles. Each loads its own context (record / parent / etc.),
54
- // resolves the page schema through `resolveSchema`, stamps URL-tag
55
- // helpers for dispatch endpoints, and returns the wire-shape envelope
56
- // the page renderer consumes.
57
-
58
-
59
- /**
60
- * Per-row stamping spine shared by SSR `resourceIndexData` and the
61
- * deferred-load JSON endpoint `resourceTableData`. Both walk the same
62
- * Table tree and need the same dispatch / active-tab / reorder / cell-edit
63
- * URL stamps in the same order — running them through one helper keeps
64
- * the two paths in lock-step so any future addition (per-row chrome
65
- * stamp, etc.) lands on both surfaces.
66
- *
67
- * Widget URL stamping + the `tagTableDeferred` flag stay outside the
68
- * helper since only `resourceIndexData` mounts widgets / the deferred
69
- * skeleton — the JSON endpoint re-runs without the flag and never
70
- * collects widget metas.
71
- */
72
- async function prepareResourceTable(
73
- elements: Element[],
74
- R: ResourceClass,
75
- indexUrl: string,
76
- query: Record<string, string>,
77
- user: unknown,
78
- ): Promise<void> {
79
- tagActionDispatch(elements, indexUrl)
80
- // Mark the active tab + parallel-eval badges + stamp per-tab URLs
81
- // before the table records run — `loadTableRecords` walks the schema
82
- // for the active tab and splices its `modifyQuery` predicate into the
83
- // ORM chain alongside filters.
84
- await resolveActiveTab(elements, query, indexUrl)
85
- await loadTableRecords(elements, query, indexUrl, user, {
86
- canEdit: (u, record) => R.canEdit(u, record),
87
- })
88
- tagTableReorderUrls(elements, `${indexUrl}/_reorder`)
89
- tagCellEditUrls(elements, indexUrl)
90
- }
91
-
92
- export async function dashboardData(pilotiq: Pilotiq, req?: unknown): Promise<Record<string, unknown>> {
93
- const cfg = pilotiq.getConfig()
94
- const user = await pilotiq.resolveUser(req)
95
- const ctx: SchemaContext = uploadCtx(userCtx({ basePath: cfg.path }, user), cfg)
96
-
97
- // Plan #15 — when `panel.dashboard(P)` was called, resolve P's
98
- // schema instead of the builder-level `cfg.schema`. Page-scoped
99
- // schema means widget elements read like a regular custom page —
100
- // including action dispatch, form-state, and `_widget/:id` polling.
101
- let elements: Element[]
102
- if (cfg.dashboardPage) {
103
- elements = await callPageSchema(cfg.dashboardPage, ctx)
104
- tagFormActions(elements, cfg.path)
105
- tagFormSubresourceUrls(elements, cfg.path)
106
- tagActionDispatch(elements, cfg.path)
107
- } else {
108
- elements = []
109
- if (cfg.schema) {
110
- const def = cfg.schema
111
- elements = typeof def === 'function' ? await def(ctx) : def
112
- }
113
- }
114
-
115
- // Stamp polling URLs on every widget — panel-scope (no pageSlug
116
- // segment) for the dashboard. Done before schema resolve so the URL
117
- // rides on each widget's stamped meta.
118
- tagWidgetUrls(elements, id => `${cfg.path}/_widget/${id}`)
119
-
120
- const widgetData = await resolveServerDataElements(elements, ctx)
121
- const dashRoute: PanelInfoRoute = cfg.dashboardPage ? { page: cfg.dashboardPage } : {}
122
- const [panel, schemaData] = await Promise.all([
123
- panelInfo(pilotiq, req, dashRoute),
124
- resolveSchema(elements, ctx).then(metas => applyRoleHooks(pilotiq, user, 'dashboard', metas, dashRoute)),
125
- ])
126
-
127
- return {
128
- panel,
129
- page: cfg.dashboardPage ? cfg.dashboardPage.toMeta() : undefined,
130
- basePath: cfg.path,
131
- layout: cfg.layout,
132
- schemaData,
133
- _widgetData: widgetData,
134
- notifications: consumeFlashedNotifications(req),
135
- }
136
- }
137
-
138
- export async function resourceIndexData(
139
- pilotiq: Pilotiq,
140
- slug: string,
141
- query: Record<string, string> = {},
142
- req?: unknown,
143
- ): Promise<Record<string, unknown> | null> {
144
- const cfg = pilotiq.getConfig()
145
- const R = pilotiq.findResource(slug)
146
- if (!R) return null
147
-
148
- const pages = R.resolvePages()
149
- if (!pages.index) return null
150
- const PageClass = pages.index
151
-
152
- const indexUrl = resourceBasePath(cfg.path, R)
153
- const user = await pilotiq.resolveUser(req)
154
- const ctx: SchemaContext = uploadCtx(userCtx({ mode: 'table', basePath: cfg.path }, user), cfg)
155
- const elements = await callPageSchema(PageClass, ctx)
156
- // Plan #15 — resource-scope widget polling URL. Stamped before the
157
- // schema resolves so each widget's meta carries its endpoint.
158
- tagWidgetUrls(elements, id => `${indexUrl}/_widget/${id}`)
159
- if (R.deferLoading) tagTableDeferred(elements, `${indexUrl}/_table`)
160
- await prepareResourceTable(elements, R, indexUrl, query, user)
161
- const widgetData = await resolveServerDataElements(elements, ctx)
162
-
163
- const breadcrumbs = resourceListBreadcrumbs(cfg, R)
164
- if (breadcrumbs) elements.unshift(breadcrumbs)
165
-
166
- const listRoute: PanelInfoRoute = { resource: R, page: PageClass }
167
- const [panel, schemaData] = await Promise.all([
168
- panelInfo(pilotiq, req, listRoute),
169
- resolveSchema(elements, ctx).then(metas => applyRoleHooks(pilotiq, user, 'list', metas, listRoute)),
170
- ])
171
-
172
- return {
173
- pageType: 'resource',
174
- panel,
175
- page: PageClass.toMeta(),
176
- resource: { name: R.name, label: R.label, labelSingular: R.labelSingular, slug, icon: serializeIcon(R.icon, R.name) },
177
- basePath: cfg.path,
178
- layout: cfg.layout,
179
- schemaData,
180
- _widgetData: widgetData,
181
- notifications: consumeFlashedNotifications(req),
182
- }
183
- }
184
-
185
- // Deferred-load JSON endpoint payload — `GET {base}/{slug}/_table`
186
- // re-runs the list-page builder without the deferred flag, then returns
187
- // every resolved `TableMeta` as a flat array. Returns null on missing
188
- // resource / index page (route 404s).
189
- export async function resourceTableData(
190
- pilotiq: Pilotiq,
191
- slug: string,
192
- query: Record<string, string> = {},
193
- req?: unknown,
194
- ): Promise<{ tables: Record<string, unknown>[] } | null> {
195
- const cfg = pilotiq.getConfig()
196
- const R = pilotiq.findResource(slug)
197
- if (!R) return null
198
-
199
- const pages = R.resolvePages()
200
- if (!pages.index) return null
201
- const PageClass = pages.index
202
-
203
- const indexUrl = resourceBasePath(cfg.path, R)
204
- const user = await pilotiq.resolveUser(req)
205
- const ctx: SchemaContext = uploadCtx(userCtx({ mode: 'table', basePath: cfg.path }, user), cfg)
206
- const elements = await callPageSchema(PageClass, ctx)
207
- await prepareResourceTable(elements, R, indexUrl, query, user)
208
- const schemaData = await resolveSchema(elements, ctx)
209
-
210
- const tables = collectTableMetas(schemaData)
211
- return { tables }
212
- }
213
-
214
- function collectTableMetas(
215
- metas: ReadonlyArray<Record<string, unknown>>,
216
- ): Record<string, unknown>[] {
217
- const out: Record<string, unknown>[] = []
218
- const walk = (nodes: ReadonlyArray<Record<string, unknown>>): void => {
219
- for (const node of nodes) {
220
- if (node['type'] === 'table') out.push(node)
221
- const children = node['children']
222
- if (Array.isArray(children)) walk(children as Record<string, unknown>[])
223
- }
224
- }
225
- walk(metas)
226
- return out
227
- }
228
-
229
- /**
230
- * Walk the schema for `ListTabs` containers, pick the active tab from
231
- * `?tab=…` (defaulting to the tab marked `.default()` or the first one),
232
- * stamp render-time state (`active` flag, per-tab `?tab=` URL, and
233
- * resolved badge counts) onto each tab. The active tab's query/context
234
- * modifier is NOT applied here — `loadTableRecords` walks for the active
235
- * tab and splices in its modifier when it builds the records-handler
236
- * `TableContext`.
237
- *
238
- * No-op when the page has no `ListTabs`.
239
- */
240
- export async function resolveActiveTab(
241
- elements: ReadonlyArray<Element>,
242
- query: Record<string, string>,
243
- currentPath: string,
244
- ): Promise<void> {
245
- const listTabs = findListTabs(elements)
246
- if (listTabs.length === 0) return
247
-
248
- for (const container of listTabs) {
249
- const children = (container.getChildren() ?? []).filter((c): c is ListTab => c.getType() === 'listTab')
250
- if (children.length === 0) continue
251
-
252
- // Default tab (used both for `?tab=` fallback and to omit the param
253
- // from the canonical URL of that tab — see `buildTabUrl`).
254
- const defaultTab = children.find(t => t.isDefault()) ?? children[0]!
255
-
256
- // Active tab: explicit `?tab=name` → default tab.
257
- const wanted = typeof query['tab'] === 'string' ? query['tab'] : undefined
258
- const active = (wanted && children.find(t => t.name === wanted)) || defaultTab
259
-
260
- // Stamp render-time state on each tab.
261
- children.forEach(t => {
262
- t.withActive(t === active)
263
- t.withUrl(buildTabUrl(currentPath, query, t.name, defaultTab.name))
264
- })
265
-
266
- // Resolve every tab's badge in parallel — failed handlers swallow
267
- // silently (badge omitted) so a flaky count never blanks the page.
268
- await Promise.all(children.map(async (tab) => {
269
- const handler = tab.getBadgeHandler()
270
- if (!handler) return
271
- try {
272
- const v = await handler()
273
- if (v === undefined || v === null) return
274
- tab.withResolvedBadge(String(v))
275
- } catch {
276
- // Per-tab badge errors stay silent.
277
- }
278
- }))
279
- }
280
- }
281
-
282
- function findListTabs(elements: ReadonlyArray<Element>): ListTabs[] {
283
- const out: ListTabs[] = []
284
- const walk = (els: ReadonlyArray<Element>): void => {
285
- for (const el of els) {
286
- if (el.getType() === 'listTabs') out.push(el as ListTabs)
287
- const children = el.getChildren()
288
- if (children) walk(children)
289
- }
290
- }
291
- walk(elements)
292
- return out
293
- }
294
-
295
- function buildTabUrl(
296
- pathname: string,
297
- query: Record<string, string>,
298
- tabName: string,
299
- defaultTabName: string,
300
- ): string {
301
- // Carry forward search/sort/perPage + any filter values; reset page to 1
302
- // (tab change reshapes the result set, page numbers don't translate).
303
- // The default tab gets the canonical, paramless URL — visiting that URL
304
- // already lands on the default, so emitting `?tab=default` would just be
305
- // noise that bookmarks/share-links pick up.
306
- const params = new URLSearchParams()
307
- for (const [k, v] of Object.entries(query)) {
308
- if (v === undefined || v === '' || v === null) continue
309
- if (k === 'tab' || k === 'page') continue
310
- params.set(k, String(v))
311
- }
312
- if (tabName !== defaultTabName) params.set('tab', tabName)
313
- const qs = params.toString()
314
- return qs ? `${pathname}?${qs}` : pathname
315
- }
316
-
317
- export async function resourceCreateData(
318
- pilotiq: Pilotiq,
319
- slug: string,
320
- prefill?: { values?: Record<string, unknown>; errors?: Record<string, string[]> },
321
- req?: unknown,
322
- ): Promise<Record<string, unknown> | null> {
323
- const cfg = pilotiq.getConfig()
324
- const R = pilotiq.findResource(slug)
325
- if (!R) return null
326
- const pages = R.resolvePages()
327
- if (!pages.create) return null
328
- const PageClass = pages.create
329
-
330
- const resourceBase = resourceBasePath(cfg.path, R)
331
- const createUrl = `${resourceBase}/create`
332
- const user = await pilotiq.resolveUser(req)
333
- const ctx: SchemaContext = uploadCtx(userCtx({ mode: 'create', basePath: cfg.path }, user), cfg)
334
- const elements = await callPageSchema(PageClass, ctx)
335
- tagFormActions(elements, createUrl)
336
- tagActionDispatch(elements, createUrl)
337
- tagFormSubresourceUrls(elements, resourceBase)
338
- if (prefill) {
339
- const form = findForms(elements)[0]
340
- if (form) {
341
- if (prefill.values) form.withValues(prefill.values)
342
- if (prefill.errors) form.withErrors(prefill.errors)
343
- }
344
- }
345
-
346
- const breadcrumbs = resourceCreateBreadcrumbs(cfg, R)
347
- if (breadcrumbs) elements.unshift(breadcrumbs)
348
-
349
- const createRoute: PanelInfoRoute = { resource: R, page: PageClass }
350
- const [panel, schemaData] = await Promise.all([
351
- panelInfo(pilotiq, req, createRoute),
352
- resolveSchema(elements, ctx).then(metas => applyRoleHooks(pilotiq, user, 'create', metas, createRoute)),
353
- ])
354
-
355
- return {
356
- panel,
357
- page: PageClass.toMeta(),
358
- resource: { name: R.name, label: R.labelSingular, slug, icon: serializeIcon(R.icon, R.name) },
359
- mode: 'create' as const,
360
- basePath: cfg.path,
361
- layout: cfg.layout,
362
- schemaData,
363
- notifications: consumeFlashedNotifications(req),
364
- ...(prefill?.errors ? { hasErrors: true } : {}),
365
- }
366
- }
367
-
368
- export async function resourceEditData(
369
- pilotiq: Pilotiq,
370
- slug: string,
371
- recordId: string,
372
- prefill?: { values?: Record<string, unknown>; errors?: Record<string, string[]> },
373
- req?: unknown,
374
- ): Promise<Record<string, unknown> | null> {
375
- const cfg = pilotiq.getConfig()
376
- const R = pilotiq.findResource(slug)
377
- if (!R) return null
378
- const pages = R.resolvePages()
379
- if (!pages.edit) return null
380
- const PageClass = pages.edit
381
-
382
- const resourceBase = resourceBasePath(cfg.path, R)
383
- const editUrl = `${resourceBase}/${recordId}/edit`
384
- const user = await pilotiq.resolveUser(req)
385
- const ctx: SchemaContext = uploadCtx(userCtx({ mode: 'edit', recordId, basePath: cfg.path }, user), cfg)
386
- const elements = await callPageSchema(PageClass, ctx)
387
- tagFormActions(elements, editUrl)
388
- tagActionDispatch(elements, editUrl)
389
- tagFormSubresourceUrls(elements, `${resourceBase}/${recordId}`)
390
-
391
- // Locate the primary form, load the record, fill values.
392
- const form = findForms(elements)[0]
393
- let record: unknown = undefined
394
- if (form?.getLoadRecord()) {
395
- try {
396
- record = await form.getLoadRecord()!(recordId, { values: prefill?.values ?? {} })
397
- } catch {
398
- // sentinel/missing record — fall through
399
- }
400
- if (!prefill?.values && record != null) {
401
- const values = await applyFillPipeline(form, record)
402
- const withRelations = await applyRelationshipRepeaterFill(form, values, record, R.model)
403
- const withBuilders = await applyRelationshipBuilderFill(form, withRelations, record, R.model)
404
- // Hydrators run AFTER the standard fill pipeline so they overlay
405
- // on top of DB-row + relationship-row values. Skipped on the
406
- // prefill branch (validation-error round-trip) — overlaying there
407
- // would clobber the user's just-submitted input that the page is
408
- // re-displaying for them to fix.
409
- const hydrators = cfg.editPageHydrators ?? []
410
- const overlay = hydrators.length > 0
411
- ? await applyEditPageHydrators(hydrators, {
412
- resource: R,
413
- recordId,
414
- currentValues: withBuilders,
415
- })
416
- : {}
417
- form.withValues(Object.keys(overlay).length > 0
418
- ? { ...withBuilders, ...overlay }
419
- : withBuilders)
420
- } else if (prefill?.values) {
421
- form.withValues(prefill.values)
422
- }
423
- if (prefill?.errors) form.withErrors(prefill.errors)
424
- }
425
-
426
- // Plan #11 — when the resource has relation managers, prepend a
427
- // navigation strip so users can drill into each manager's table
428
- // without leaving the parent record context. The "Edit" tab is
429
- // active here.
430
- const relationTabsEl = await buildRelationTabs(R, recordId, cfg.path, '__edit', user, record)
431
- if (relationTabsEl) elements.unshift(relationTabsEl)
432
-
433
- const recordTitle = record !== undefined && record !== null
434
- ? deriveParentTitle(R, record)
435
- : recordId
436
- const breadcrumbs = resourceEditBreadcrumbs(cfg, R, recordId, recordTitle)
437
- if (breadcrumbs) elements.unshift(breadcrumbs)
438
-
439
- const editRoute: PanelInfoRoute = { resource: R, page: PageClass, recordId }
440
- const editCtx = record !== undefined ? { ...ctx, record } : ctx
441
- const [panel, schemaData] = await Promise.all([
442
- panelInfo(pilotiq, req, editRoute),
443
- resolveSchema(elements, editCtx).then(metas => applyRoleHooks(pilotiq, user, 'edit', metas, editRoute)),
444
- ])
445
-
446
- tagFieldAiUrls(schemaData as Record<string, unknown>[], `${resourceBase}/${recordId}/_agents`)
447
-
448
- return {
449
- panel,
450
- page: PageClass.toMeta(),
451
- resource: { name: R.name, label: R.labelSingular, slug, icon: serializeIcon(R.icon, R.name) },
452
- mode: 'edit' as const,
453
- recordId,
454
- basePath: cfg.path,
455
- layout: cfg.layout,
456
- schemaData,
457
- notifications: consumeFlashedNotifications(req),
458
- ...(prefill?.errors ? { hasErrors: true } : {}),
459
- }
460
- }
461
-
462
- export async function resourceViewData(
463
- pilotiq: Pilotiq,
464
- slug: string,
465
- recordId: string,
466
- req?: unknown,
467
- ): Promise<Record<string, unknown> | null> {
468
- const cfg = pilotiq.getConfig()
469
- const R = pilotiq.findResource(slug)
470
- if (!R) return null
471
- const pages = R.resolvePages()
472
- if (!pages.view) return null
473
- const PageClass = pages.view
474
-
475
- const user = await pilotiq.resolveUser(req)
476
- const ctx: SchemaContext = uploadCtx(userCtx({ mode: 'view', recordId, basePath: cfg.path }, user), cfg)
477
- const elements = await callPageSchema(PageClass, ctx)
478
- // For the view page we want the record threaded into resolveSchema so
479
- // factory-attached visibility predicates see it. Resource.detail()
480
- // already runs against the loaded record in user code; here we mirror
481
- // that into ctx.record for the action eval pass.
482
- let record: unknown = undefined
483
- if (R.model) {
484
- try { record = await findRecord(R, recordId, { user }) } catch { /* ignore */ }
485
- }
486
-
487
- // Plan #11 — prepend the relation tabs strip with the "Details" tab
488
- // active when the resource has relation managers configured.
489
- const relationTabsEl = await buildRelationTabs(R, recordId, cfg.path, '__view', user, record)
490
- if (relationTabsEl) elements.unshift(relationTabsEl)
491
-
492
- const recordTitle = record !== undefined && record !== null
493
- ? deriveParentTitle(R, record)
494
- : recordId
495
- const breadcrumbs = resourceViewBreadcrumbs(cfg, R, recordTitle)
496
- if (breadcrumbs) elements.unshift(breadcrumbs)
497
-
498
- const viewRoute: PanelInfoRoute = { resource: R, page: PageClass, recordId }
499
- const viewCtx = record !== undefined ? { ...ctx, record } : ctx
500
- const [panel, schemaData] = await Promise.all([
501
- panelInfo(pilotiq, req, viewRoute),
502
- resolveSchema(elements, viewCtx).then(metas => applyRoleHooks(pilotiq, user, 'view', metas, viewRoute)),
503
- ])
504
-
505
- return {
506
- panel,
507
- page: PageClass.toMeta(),
508
- resource: { name: R.name, label: R.labelSingular, slug, icon: serializeIcon(R.icon, R.name) },
509
- mode: 'view' as const,
510
- recordId,
511
- basePath: cfg.path,
512
- layout: cfg.layout,
513
- schemaData,
514
- notifications: consumeFlashedNotifications(req),
515
- }
516
- }
517
-
518
- /**
519
- * Custom record sub-page data builder. Mounted at
520
- * `${resourceBase}/${slug}/:id/${subPageSlug}` for each entry in
521
- * `Resource.pages().record`. Mirrors `resourceViewData`'s shape: load
522
- * the record, run R.canAccess + R.canView (parent-resource gates),
523
- * then SubPage.canAccess(user, record) (sub-page-specific gate),
524
- * then render the sub-page's schema with `ctx.record` set. Tab strip
525
- * carries the sub-page slug as the active key so the matching record
526
- * sub-page tab highlights.
527
- *
528
- * Returns:
529
- * - `null` — resource / sub-page slug not found (404 upstream).
530
- * - `{ ok: false, status: 403 }` — any gate fails or throws.
531
- * - resolved page data — on success.
532
- */
533
- export async function resourceRecordPageData(
534
- pilotiq: Pilotiq,
535
- slug: string,
536
- recordId: string,
537
- subPageSlug: string,
538
- req?: unknown,
539
- ): Promise<Record<string, unknown> | null | { ok: false; status: 403 }> {
540
- const cfg = pilotiq.getConfig()
541
- const R = pilotiq.findResource(slug)
542
- if (!R) return null
543
- const recordPages = R.getRecordPages()
544
- const PageClass = recordPages[subPageSlug]
545
- if (!PageClass) return null
546
-
547
- const user = await pilotiq.resolveUser(req)
548
-
549
- // Load the parent record before gating so canView / SubPage.canAccess
550
- // can branch on record state. Sub-pages without a Resource.model
551
- // still get gated against an `undefined` record — the same posture as
552
- // resourceViewData when no model is bound.
553
- let record: unknown = undefined
554
- if (R.model) {
555
- try { record = await findRecord(R, recordId, { user }) } catch { /* ignore */ }
556
- }
557
- if (record === undefined || record === null) {
558
- // Distinguish "model bound but record missing" (route should 404)
559
- // from "no model bound" (treat record as `{ id: recordId }` so the
560
- // page can still render — same convention as the edit page).
561
- if (R.model) return null
562
- record = { id: recordId }
563
- }
564
-
565
- // Three gates: parent resource access + view, then the sub-page's own
566
- // canAccess. The route would have run R.canAccess upstream, but
567
- // re-running here makes resourceRecordPageData safe to call from
568
- // dispatchPageData (where the SPA path skips the route prelude).
569
- if (!await safeBool(() => R.canAccess(user))) return { ok: false, status: 403 }
570
- if (!await safeBool(() => R.canView(user, record))) return { ok: false, status: 403 }
571
- if (!await safeBool(() => PageClass.canAccess(user, record))) return { ok: false, status: 403 }
572
-
573
- const ctx: SchemaContext = uploadCtx(userCtx({ mode: 'view', recordId, basePath: cfg.path }, user), cfg)
574
- const elements = await callPageSchema(PageClass, ctx)
575
-
576
- // Insert the relation-tabs strip with the sub-page slug active so the
577
- // matching tab highlights. `buildRelationTabs` evaluates per-tab
578
- // gating against `user + record` — record sub-page tabs are gated
579
- // alongside __view/__edit/managers.
580
- const relationTabsEl = await buildRelationTabs(R, recordId, cfg.path, subPageSlug, user, record)
581
- if (relationTabsEl) elements.unshift(relationTabsEl)
582
-
583
- const recordTitle = record !== undefined && record !== null
584
- ? deriveParentTitle(R, record)
585
- : recordId
586
- const breadcrumbs = resourceViewBreadcrumbs(cfg, R, recordTitle)
587
- if (breadcrumbs) elements.unshift(breadcrumbs)
588
-
589
- const recordPageRoute: PanelInfoRoute = { resource: R, page: PageClass, recordId }
590
- const recordCtx = record !== undefined ? { ...ctx, record } : ctx
591
- const [panel, schemaData] = await Promise.all([
592
- panelInfo(pilotiq, req, recordPageRoute),
593
- resolveSchema(elements, recordCtx).then(metas => applyRoleHooks(pilotiq, user, 'view', metas, recordPageRoute)),
594
- ])
595
-
596
- return {
597
- pageType: 'record-page' as const,
598
- panel,
599
- page: PageClass.toMeta(),
600
- resource: { name: R.name, label: R.labelSingular, slug, icon: serializeIcon(R.icon, R.name) },
601
- mode: 'record' as const,
602
- recordId,
603
- subPage: { slug: subPageSlug, label: PageClass.getLabel() },
604
- basePath: cfg.path,
605
- layout: cfg.layout,
606
- schemaData,
607
- notifications: consumeFlashedNotifications(req),
608
- }
609
- }