@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,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
- }