@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,220 +0,0 @@
1
- /**
2
- * CRUD single-row Action factories — `create / edit / view / delete /
3
- * replicate / restore / forceDelete`, plus the notification `markAsRead`
4
- * factory.
5
- *
6
- * Each factory builds and returns a configured `Action`. The matching
7
- * `static` methods on the `Action` class are thin delegators that call
8
- * into these functions, preserving the public `Action.create(R, base)`
9
- * call shape.
10
- *
11
- * See `docs/plans/action-split.md` for the split plan.
12
- */
13
-
14
- import { Action, type ReplicateOptions, type ResourceLike } from './Action.js'
15
- import { buildReplica, callPredicate, isTrashed, resourceBase } from './factoryHelpers.js'
16
-
17
- /** Create-action factory — link to `${basePath}/${R.slug}/create`.
18
- * Auto-hides when `R.canCreate(user)` returns false. */
19
- export function createAction(R: ResourceLike, basePath: string): Action {
20
- return Action.make('create')
21
- .label(`New ${R.labelSingular}`)
22
- .href(`${resourceBase(basePath, R)}/create`)
23
- .visible(({ user }) => callPredicate(R.canCreate, user))
24
- }
25
-
26
- /**
27
- * Edit-action factory — link to the resource's edit page.
28
- *
29
- * Pass `recordId` when building actions for a single-record context
30
- * (e.g. `ViewPage.getActions()`); the URL is baked at config time.
31
- * Omit `recordId` for row context (`Table.recordActions(...)`); the
32
- * URL keeps the `:id` template and the renderer substitutes per-row.
33
- *
34
- * Auto-hides when `R.canEdit(user, record)` returns false. For row
35
- * context the per-row record threads in via `loadTableRecords`'s
36
- * per-row eval; for view-page context, `resolveSchema` provides the
37
- * resolved record on the eval context.
38
- */
39
- export function editAction(R: ResourceLike, basePath: string, recordId?: string): Action {
40
- const id = recordId ?? ':id'
41
- return Action.make('edit')
42
- .label('Edit')
43
- .href(`${resourceBase(basePath, R)}/${id}/edit`)
44
- .visible(({ user, record }) => callPredicate(R.canEdit, user, record))
45
- }
46
-
47
- /** View-action factory — link to the resource's view page. See `editAction` for the `recordId` semantics.
48
- * Auto-hides when `R.canView(user, record)` returns false. */
49
- export function viewAction(R: ResourceLike, basePath: string, recordId?: string): Action {
50
- const id = recordId ?? ':id'
51
- return Action.make('view')
52
- .label('View')
53
- .href(`${resourceBase(basePath, R)}/${id}`)
54
- .visible(({ user, record }) => callPredicate(R.canView, user, record))
55
- }
56
-
57
- /**
58
- * Delete-action factory — POSTs to the resource's delete route,
59
- * destructive style, with a confirmation prompt referencing the
60
- * resource label. Same `recordId` semantics as `editAction`.
61
- * Auto-hides when `R.canDelete(user, record)` returns false.
62
- *
63
- * Plan #13 — when `R.softDeletes = true`, additionally hides on
64
- * rows whose `deletedAtColumn` is set (already-trashed rows get the
65
- * Restore + ForceDelete pair instead, surfaced via the matching
66
- * factories below).
67
- */
68
- export function deleteAction(R: ResourceLike, basePath: string, recordId?: string): Action {
69
- const id = recordId ?? ':id'
70
- return Action.make('delete')
71
- .label('Delete')
72
- .destructive()
73
- .method('post')
74
- .action(`${resourceBase(basePath, R)}/${id}/delete`)
75
- .confirm(`Delete this ${R.labelSingular.toLowerCase()}?`)
76
- .visible(async ({ user, record }) => {
77
- if (R.softDeletes && isTrashed(record, R)) return false
78
- return callPredicate(R.canDelete, user, record)
79
- })
80
- }
81
-
82
- /**
83
- * Replicate-action factory — handler-style. Loads the source record
84
- * from `ctx.record` (the `_action/:actionName` route already resolves
85
- * it through `R.query(ctx)` for row + single-target placements),
86
- * strips PK + soft-delete column + any `opts.excludeAttributes`,
87
- * optionally runs `opts.beforeReplicaSaved`, and creates a new row
88
- * via `R.model.create(...)`. Redirects to the new record's edit page
89
- * on success so the user can review + tweak before saving again.
90
- *
91
- * `recordId` kept in the signature for parity with `delete / edit /
92
- * view` so users can swap factories without rewriting call sites; the
93
- * dispatcher resolves the source record from the URL and hands it to
94
- * the handler as `ctx.record`, so we don't reference `recordId` here.
95
- *
96
- * Auto-hides when `R.canCreate(user)` returns false — replicating
97
- * writes a new row, so the gate is `canCreate`, not `canView`.
98
- */
99
- export function replicateAction(
100
- R: ResourceLike,
101
- basePath: string,
102
- recordId?: string,
103
- opts: ReplicateOptions = {},
104
- ): Action {
105
- void recordId
106
- return Action.make('replicate')
107
- .label('Replicate')
108
- .handler(async (ctx) => {
109
- const source = ctx.record
110
- if (!source || typeof source !== 'object') {
111
- return { notify: { title: 'Replicate failed: source record missing', type: 'error' } as never }
112
- }
113
- const M = R.model
114
- if (!M || typeof M.create !== 'function') {
115
- return { notify: { title: 'Replicate not configured (resource has no model.create)', type: 'error' } as never }
116
- }
117
-
118
- let replica: Record<string, unknown>
119
- let pkCol: string
120
- try {
121
- ({ replica, pkCol } = await buildReplica(source, M, {
122
- excludeAttributes: opts.excludeAttributes,
123
- deletedAtColumn: R.deletedAtColumn,
124
- beforeReplicaSaved: opts.beforeReplicaSaved,
125
- }))
126
- } catch (err) {
127
- return { notify: { title: `Replicate failed: ${err instanceof Error ? err.message : String(err)}`, type: 'error' } as never }
128
- }
129
-
130
- let created: unknown
131
- try {
132
- created = await M.create(replica)
133
- } catch (err) {
134
- return { notify: { title: `Replicate failed: ${err instanceof Error ? err.message : String(err)}`, type: 'error' } as never }
135
- }
136
-
137
- const newId = (created as Record<string, unknown> | null | undefined)?.[pkCol]
138
- const defaultRedirect = newId !== undefined && newId !== null
139
- ? `${resourceBase(basePath, R)}/${String(newId)}/edit`
140
- : `${resourceBase(basePath, R)}`
141
- // `!== undefined` rather than `??` so an override returning
142
- // `null`/empty-string isn't silently swallowed (see
143
- // feedback_nullish_swallows_explicit_null).
144
- const overrideRedirect = opts.getRedirectUrl
145
- ? await opts.getRedirectUrl({ replica: created, source })
146
- : undefined
147
- const redirect = overrideRedirect !== undefined ? overrideRedirect : defaultRedirect
148
- const overrideTitle = opts.getCreatedNotificationTitle
149
- ? await opts.getCreatedNotificationTitle({ replica: created, source })
150
- : undefined
151
- const title = overrideTitle !== undefined ? overrideTitle : `${R.labelSingular} replicated`
152
- return {
153
- redirect,
154
- notify: { title, type: 'success' } as never,
155
- }
156
- })
157
- .visible(({ user }) => callPredicate(R.canCreate, user))
158
- }
159
-
160
- /**
161
- * Plan #13 — Restore factory. POSTs to the resource's restore route,
162
- * success-styled, no confirm prompt (restoration is reversible).
163
- * Auto-hides on live (non-trashed) rows AND when `R.canRestore(user,
164
- * record)` returns false. Same `recordId` semantics as `editAction`.
165
- */
166
- export function restoreAction(R: ResourceLike, basePath: string, recordId?: string): Action {
167
- const id = recordId ?? ':id'
168
- return Action.make('restore')
169
- .label('Restore')
170
- .color('success')
171
- .method('post')
172
- .action(`${resourceBase(basePath, R)}/${id}/restore`)
173
- .visible(async ({ user, record }) => {
174
- if (!isTrashed(record, R)) return false
175
- return callPredicate(R.canRestore, user, record)
176
- })
177
- }
178
-
179
- /**
180
- * Plan #13 — Force-delete factory. POSTs to the resource's
181
- * force-delete route, destructive-styled, with a stricter confirm
182
- * prompt referencing permanence. Auto-hides on live (non-trashed)
183
- * rows AND when `R.canForceDelete(user, record)` returns false.
184
- */
185
- export function forceDeleteAction(R: ResourceLike, basePath: string, recordId?: string): Action {
186
- const id = recordId ?? ':id'
187
- return Action.make('forceDelete')
188
- .label('Delete forever')
189
- .destructive()
190
- .method('post')
191
- .action(`${resourceBase(basePath, R)}/${id}/force-delete`)
192
- .confirm(`Permanently delete this ${R.labelSingular.toLowerCase()}? This cannot be undone.`)
193
- .visible(async ({ user, record }) => {
194
- if (!isTrashed(record, R)) return false
195
- return callPredicate(R.canForceDelete, user, record)
196
- })
197
- }
198
-
199
- /**
200
- * Mark-as-read factory — POSTs to the panel's notification read
201
- * endpoint for the given notification id. The endpoint
202
- * (`${base}/_notifications/:id/read`) is mounted by
203
- * `Pilotiq.databaseNotifications()`, so calling this without
204
- * opting into the bell surface produces an Action whose POST 404s.
205
- *
206
- * `notificationId` is baked at config time. For row context where
207
- * the id varies per row, omit it and the URL keeps the `:id`
208
- * template; the renderer substitutes per-row at render time
209
- * (parallel to `editAction`'s row form).
210
- *
211
- * No auto-visibility. Wrap in `.visible(({ record }) => !record.readAt)`
212
- * to hide on already-read rows.
213
- */
214
- export function markAsReadAction(basePath: string, notificationId?: string): Action {
215
- const id = notificationId ?? ':id'
216
- return Action.make('markAsRead')
217
- .label('Mark as read')
218
- .method('post')
219
- .action(`${basePath}/_notifications/${id}/read`)
220
- }
@@ -1,225 +0,0 @@
1
- /**
2
- * Internals for the `Action.export` / `Action.bulkExport` factories.
3
- *
4
- * Lives in its own module to keep the cycle between `Action.ts` and
5
- * `Table.ts` honest — `Action.export` calls into here via dynamic
6
- * import inside the handler, so module-eval order never reads either
7
- * side's exports too early.
8
- *
9
- * The `R: { table?(t: any): any }` shape on `resolveExportColumns`
10
- * (and the matching parameter in `collectExportRows`) is the load-bearing
11
- * piece of this isolation: the real `Resource.table()` signature is
12
- * `(t: Table) => Table`, but importing `Table` here from a static-top
13
- * import would resurrect the cycle this file exists to break. Loosening
14
- * the parameter to `any` is the price — call sites in `Action.ts` are
15
- * typed against the proper `ResourceLike`, so the leak doesn't escape
16
- * this module. Don't widen `R`'s annotation; do narrow inside the body
17
- * if you need stricter local typing.
18
- */
19
-
20
- import { Table } from '../elements/Table.js'
21
- import { Column } from '../Column.js'
22
- import type { ActionContext } from './Action.js'
23
- import { encodeCsv, type CsvColumn } from '../io/csv.js'
24
- import { parseTableQuery, type QueryParams } from '../elements/dispatchTable.js'
25
-
26
- export type ExportFormat = 'csv' | 'json'
27
-
28
- export type ExportColumnSpec =
29
- | string
30
- | {
31
- key: string
32
- label?: string
33
- format?: (value: unknown, record: Record<string, unknown>) => string
34
- }
35
-
36
- export interface ExportOptions {
37
- /** Columns to include. Strings → bare `key` (label = key). Objects
38
- * may add `label` and `format(value, record) => string`. When omitted,
39
- * defaults to every `Column` returned by `R.table()` in declaration
40
- * order, with header label = `Column.getLabel()`. */
41
- columns?: ExportColumnSpec[]
42
- /** Output filename. Function form receives the action context (so the
43
- * filename can include the user, current filters, etc). Default:
44
- * `${R.slug}-${YYYY-MM-DD}.${format}`. */
45
- filename?: string | ((ctx: ActionContext) => string)
46
- /** Output format. Default `'csv'`. */
47
- format?: ExportFormat
48
- /** Records scope:
49
- * - `'all'` — ignore the current URL state (filters/search/sort)
50
- * - `'filtered'` — apply the current URL state (default; matches the
51
- * list page the user is looking at)
52
- * - `'page'` — only export the visible page
53
- *
54
- * Bulk variant ignores `scope` and always uses `ctx.records`.
55
- */
56
- scope?: 'all' | 'filtered' | 'page'
57
- /** Hard cap on the row count. Aborts with an error notification when
58
- * exceeded. Default `50_000`. */
59
- maxRows?: number
60
- /** Pagination chunk size when walking the records handler. Default
61
- * `1_000`. */
62
- chunkSize?: number
63
- }
64
-
65
- interface ResolvedColumn {
66
- key: string
67
- label: string
68
- format?: (value: unknown, record: Record<string, unknown>) => string
69
- }
70
-
71
- const DEFAULT_MAX_ROWS = 50_000
72
- const DEFAULT_CHUNK = 1_000
73
-
74
- /**
75
- * Resolve the column spec into a uniform `{ key, label, format? }[]`,
76
- * defaulting to every `Column` child on `R.table(Table.make())` when
77
- * the spec is omitted.
78
- */
79
- export function resolveExportColumns(
80
- spec: ExportOptions['columns'],
81
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
- R: { table?(t: any): any },
83
- ): ResolvedColumn[] {
84
- if (spec && spec.length > 0) {
85
- return spec.map(s => typeof s === 'string'
86
- ? { key: s, label: s }
87
- : { key: s.key, label: s.label ?? s.key, ...(s.format ? { format: s.format } : {}) })
88
- }
89
- const tableInst = R.table?.(Table.make())
90
- if (!tableInst) return []
91
- const cols: Column[] = tableInst.getColumns?.() ?? []
92
- return cols.map(c => ({ key: c.name, label: c.getLabel?.() ?? c.name }))
93
- }
94
-
95
- /**
96
- * Read the URL query record off `ctx.request`. Routes pass a Hono-like
97
- * request object whose `.query` is a `Record<string, string>`. Fall
98
- * back to `{}` when missing (programmatic dispatches in tests).
99
- */
100
- export function readRequestQuery(ctx: ActionContext): QueryParams {
101
- const req = ctx.request as { query?: unknown } | undefined
102
- const q = req?.query
103
- if (q && typeof q === 'object') return q as QueryParams
104
- return {}
105
- }
106
-
107
- /**
108
- * Drive the table's `records()` handler in pages until we run out of
109
- * rows or hit `maxRows`. Returns the collected raw records (not yet
110
- * mapped to row objects).
111
- *
112
- * Scope behavior:
113
- * - `'all'`: empty TableContext (no filters/search/sort applied)
114
- * - `'filtered'`: TableContext built from the URL query (filters /
115
- * search / sort all flow through)
116
- * - `'page'`: same as `'filtered'` but only the visible page
117
- */
118
- export async function collectExportRows(
119
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
120
- table: any,
121
- ctx: ActionContext,
122
- scope: ExportOptions['scope'] = 'filtered',
123
- maxRows: number = DEFAULT_MAX_ROWS,
124
- chunkSize: number = DEFAULT_CHUNK,
125
- ): Promise<unknown[]> {
126
- const handler = table.getRecords?.()
127
- if (!handler) {
128
- throw new Error('Resource table has no records() handler — cannot export. Set R.model or Table.records(fn).')
129
- }
130
-
131
- const baseCtx = scope === 'all' ? {} : buildTableCtxFromQuery(readRequestQuery(ctx), table)
132
-
133
- const collected: unknown[] = []
134
- let page = scope === 'page' ? (baseCtx.page ?? 1) : 1
135
- const effectivePerPage = scope === 'page' ? (baseCtx.perPage ?? chunkSize) : chunkSize
136
-
137
- while (collected.length <= maxRows) {
138
- const result = await handler({ ...baseCtx, page, perPage: effectivePerPage })
139
- const rows: unknown[] = Array.isArray(result) ? result : (result?.rows ?? [])
140
- collected.push(...rows)
141
- if (rows.length < effectivePerPage) break
142
- if (scope === 'page') break
143
- page++
144
- }
145
-
146
- return collected
147
- }
148
-
149
- /**
150
- * Build a `TableContext` from a URL-query record by reusing the same
151
- * parsing the list page does. Filters mirror onto the table's Filter
152
- * children so any `Filter.query()` overrides see them on subsequent
153
- * runs (defensive — `loadTableRecords` does the same).
154
- */
155
- function buildTableCtxFromQuery(
156
- query: QueryParams,
157
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
- table: any,
159
- ): {
160
- search?: string
161
- sort?: { column: string; direction: 'asc' | 'desc' }
162
- filters?: Record<string, string>
163
- page?: number
164
- perPage?: number
165
- } {
166
- const { search, sort, page, perPage } = parseTableQuery(query)
167
-
168
- const tableFilters = table.getFilters?.() ?? []
169
- const filterValues: Record<string, string> = {}
170
- const reserved = new Set(['search', 'sort', 'page', 'perPage', 'group', 'groupKey'])
171
- for (const f of tableFilters) {
172
- const name = f.name as string
173
- if (reserved.has(name)) continue
174
- const v = (query as Record<string, unknown>)[name]
175
- if (typeof v === 'string' && v !== '') filterValues[name] = v
176
- }
177
- for (const f of tableFilters) {
178
- const v = filterValues[f.name as string]
179
- if (v !== undefined && typeof f.withValue === 'function') f.withValue(v)
180
- }
181
-
182
- const out: ReturnType<typeof buildTableCtxFromQuery> = {}
183
- if (search !== undefined) out.search = search
184
- if (sort !== undefined) out.sort = sort
185
- if (Object.keys(filterValues).length > 0) out.filters = filterValues
186
- if (page !== undefined) out.page = page
187
- if (perPage !== undefined) out.perPage = perPage
188
- return out
189
- }
190
-
191
- /** Build a single CSV/JSON-shaped row by reading each resolved column's
192
- * key off the record (with `format` applied when set). */
193
- export function buildExportRow(
194
- record: unknown,
195
- cols: ReadonlyArray<ResolvedColumn>,
196
- ): Record<string, unknown> {
197
- const r = record as Record<string, unknown>
198
- const out: Record<string, unknown> = {}
199
- for (const col of cols) {
200
- const raw = r[col.key]
201
- out[col.key] = col.format ? col.format(raw, r) : raw
202
- }
203
- return out
204
- }
205
-
206
- /** Encode `rows` as the requested format. Object values that survive
207
- * to here go through the CSV escape rules / `JSON.stringify`. */
208
- export function encodeExport(
209
- rows: ReadonlyArray<Record<string, unknown>>,
210
- cols: ReadonlyArray<ResolvedColumn>,
211
- format: ExportFormat,
212
- ): { body: string; contentType: string } {
213
- if (format === 'json') {
214
- return { body: JSON.stringify(rows, null, 2), contentType: 'application/json' }
215
- }
216
- const csvCols: CsvColumn[] = cols.map(c => ({ key: c.key, label: c.label }))
217
- return { body: encodeCsv(rows, csvCols), contentType: 'text/csv; charset=utf-8' }
218
- }
219
-
220
- /** Default filename: `${slug}-${YYYY-MM-DD}.${ext}`. */
221
- export function defaultExportFilename(slug: string, format: ExportFormat): string {
222
- const date = new Date().toISOString().slice(0, 10)
223
- const ext = format === 'json' ? 'json' : 'csv'
224
- return `${slug}-${date}.${ext}`
225
- }
@@ -1,177 +0,0 @@
1
- /**
2
- * Shared helpers for the Action factory modules — `crudFactories.ts`,
3
- * `bulkFactories.ts`, `relationFactories.ts`, `m2mFactories.ts`.
4
- *
5
- * Lives in its own file so the per-phase factory modules stay focused
6
- * on their own factory bodies. Anything consumed by 2+ phase files
7
- * lands here; phase-local helpers stay alongside their phase's
8
- * factories.
9
- *
10
- * See `docs/plans/action-split.md` for the split plan.
11
- */
12
-
13
- import type { ResourceLike } from './Action.js'
14
- import type { RelationManagerContext } from '../RelationManager.js'
15
-
16
- /** Cluster-aware resource base path. Mirrors `clusterPaths.resourceBasePath`
17
- * but uses the structural `ResourceLike` shape so `Action.ts` stays
18
- * cycle-free against `Resource.ts`. */
19
- export function resourceBase(basePath: string, R: ResourceLike): string {
20
- if (R.cluster) return `${basePath}/${R.cluster.getSlug()}/${R.getSlug()}`
21
- return `${basePath}/${R.getSlug()}`
22
- }
23
-
24
- /** Call a (possibly undefined) Resource predicate. When unset, the
25
- * predicate is treated as "allowed" (returns true) so the factory
26
- * doesn't hide actions on Resources that haven't opted into Plan #10. */
27
- export function callPredicate(
28
- fn: ((user: unknown, record?: unknown) => boolean | Promise<boolean>) | undefined,
29
- user: unknown,
30
- record?: unknown,
31
- ): boolean | Promise<boolean> {
32
- if (!fn) return true
33
- return fn(user, record)
34
- }
35
-
36
- /** Read `record[R.deletedAtColumn ?? 'deletedAt']` and return true when
37
- * the row is currently trashed (soft-deleted). Permissive on shape —
38
- * bare `null` / `undefined` count as live; any other truthy value is
39
- * trashed. */
40
- export function isTrashed(record: unknown, R: ResourceLike): boolean {
41
- if (!record || typeof record !== 'object') return false
42
- const col = R.deletedAtColumn ?? 'deletedAt'
43
- const v = (record as Record<string, unknown>)[col]
44
- return v !== null && v !== undefined
45
- }
46
-
47
- /** True when a `RelationManagerContext.mode` denotes a pivot-mutation
48
- * shape — i.e. a many-to-many relation. All three modes share the
49
- * `attach` / `detach` / `sync` accessor surface (the rudder ORM stamps
50
- * + filters the polymorphic discriminator transparently for the morph
51
- * variants). The `relationCreate / Edit / Delete` factories auto-hide
52
- * under any of these modes because per-pivot-row create / edit / delete
53
- * is meaningless — users create the related record via its own Resource,
54
- * then attach via `relationAttach`. */
55
- export function isM2MMode(mode: RelationManagerContext['mode']): boolean {
56
- return mode === 'belongsToMany' || mode === 'morphToMany' || mode === 'morphedByMany'
57
- }
58
-
59
- /**
60
- * Build the URL prefix for a relation factory action. Without
61
- * a `chain` (depth-1 manager), this is the familiar
62
- * `${base}/${parentSlug}/${parentId}/${relationship}`. With a chain
63
- * (depth-2 nested manager), it threads the outer record + relationship
64
- * between the parent slug and the leaf parent id:
65
- *
66
- * `${base}/${parentSlug}/${chain[0].recordId}/${chain[0].relationship}/${parentId}/${relationship}`
67
- *
68
- * Pure; takes a `RelationManagerContext` and emits a string. The leaf
69
- * record id (and trailing `/edit`, `/delete`, etc.) gets appended by
70
- * the caller.
71
- */
72
- export function relationUrlPrefix(ctx: RelationManagerContext): string {
73
- const head = `${ctx.basePath}/${ctx.parentSlug}`
74
- const chain = ctx.chain ?? []
75
- let mid = ''
76
- for (const step of chain) {
77
- mid += `/${step.recordId}/${step.relationship}`
78
- }
79
- return `${head}${mid}/${ctx.parentId}/${ctx.relationship}`
80
- }
81
-
82
- /** Options bag for `buildReplica`. Optional fields explicitly accept
83
- * `undefined` so call sites can pass `opts.excludeAttributes` through
84
- * unconditionally under `exactOptionalPropertyTypes: true`. */
85
- export interface BuildReplicaOptions {
86
- /** Attribute keys to drop from the replicated payload IN ADDITION TO
87
- * the model's primary key and soft-delete column. */
88
- excludeAttributes?: readonly string[] | undefined
89
- /** Soft-delete column name on the source Resource. Defaults to
90
- * `'deletedAt'`. Read separately from the model because the column
91
- * lives on the Resource shape (`R.deletedAtColumn`) rather than on
92
- * the model itself. */
93
- deletedAtColumn?: string | undefined
94
- /** Force-pinned columns applied AFTER the strip and BEFORE the user
95
- * `beforeReplicaSaved` mutator. Used by the relation replicate
96
- * factories to re-stamp the parent attachment FK / morph columns
97
- * so a tampered source row can't slip a different parent in. */
98
- pin?: Record<string, unknown> | undefined
99
- /** Optional user mutator. Runs after the strip + pin. */
100
- beforeReplicaSaved?: ((
101
- replica: Record<string, unknown>,
102
- source: unknown,
103
- ) => Record<string, unknown> | Promise<Record<string, unknown>>) | undefined
104
- }
105
-
106
- /**
107
- * Build a replica payload from a source record. Used by every replicate
108
- * factory (`replicateAction / bulkReplicateAction / relationReplicateAction
109
- * / relationBulkReplicateAction`).
110
- *
111
- * Strips the model's primary key (`model.primaryKey`, defaulting to
112
- * `'id'`), the soft-delete column (defaulting to `'deletedAt'`), and any
113
- * `excludeAttributes` keys. Applies `pin` columns (parent attachment
114
- * for relation factories), then runs the optional `beforeReplicaSaved`
115
- * user mutator. Returns the replica AND the resolved primary-key column
116
- * name (callers need it to read `created[pkCol]` for redirect URLs).
117
- *
118
- * Does NOT call `model.create` — callers wrap their own create + error
119
- * handling around the returned replica.
120
- */
121
- export async function buildReplica(
122
- source: unknown,
123
- model: { primaryKey?: string },
124
- opts: BuildReplicaOptions = {},
125
- ): Promise<{ replica: Record<string, unknown>; pkCol: string }> {
126
- const pkCol = model.primaryKey ?? 'id'
127
- const trashedCol = opts.deletedAtColumn ?? 'deletedAt'
128
- const skip = new Set<string>([pkCol, trashedCol, ...(opts.excludeAttributes ?? [])])
129
- let replica: Record<string, unknown> = {}
130
- for (const [k, v] of Object.entries(source as Record<string, unknown>)) {
131
- if (skip.has(k)) continue
132
- replica[k] = v
133
- }
134
- if (opts.pin) Object.assign(replica, opts.pin)
135
- if (opts.beforeReplicaSaved) {
136
- replica = await opts.beforeReplicaSaved(replica, source)
137
- }
138
- return { replica, pkCol }
139
- }
140
-
141
- /**
142
- * Iterate `records`, run the policy probe in parallel up-front (only
143
- * allowed rows enter the serial work loop), call `op(id, record)` per
144
- * allowed row, swallow per-row throws (the aggregate notification shows
145
- * the count succeeded). Returns the success count.
146
- *
147
- * Used by every bulk handler (`bulkDeleteAction / bulkRestoreAction /
148
- * bulkForceDeleteAction / bulkReplicateAction / relationBulkReplicateAction
149
- * / relationBulkDetachAction`-style pattern).
150
- *
151
- * Rows whose `record.id` coerces to an empty string are skipped without
152
- * counting them as an attempt. The policy probe runs via `Promise.all`
153
- * so backend round-trips parallelize, but the write loop stays serial
154
- * (no transaction in v1 — concurrent writes would muddy failure
155
- * semantics).
156
- */
157
- export async function forEachAllowed(
158
- records: readonly unknown[],
159
- isAllowed: (record: unknown, index: number) => boolean | Promise<boolean>,
160
- op: (id: string, record: unknown, index: number) => Promise<void>,
161
- ): Promise<number> {
162
- const allowedFlags = await Promise.all(
163
- records.map((r, i) => isAllowed(r, i)),
164
- )
165
- let n = 0
166
- for (let i = 0; i < records.length; i++) {
167
- if (!allowedFlags[i]) continue
168
- const record = records[i]
169
- const id = String((record as { id?: unknown } | null | undefined)?.id ?? '')
170
- if (!id) continue
171
- try {
172
- await op(id, record, i)
173
- n++
174
- } catch { /* skip — agg notify shows total */ }
175
- }
176
- return n
177
- }