@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.
- package/CHANGELOG.md +91 -0
- package/boost/guidelines.md +566 -0
- package/boost/skills/pilotiq-fields/SKILL.md +47 -0
- package/boost/skills/pilotiq-fields/rules/field-catalog.md +288 -0
- package/boost/skills/pilotiq-fields/rules/reactive-fields.md +199 -0
- package/boost/skills/pilotiq-fields/rules/validation.md +198 -0
- package/boost/skills/pilotiq-relations/SKILL.md +47 -0
- package/boost/skills/pilotiq-relations/rules/relation-managers.md +256 -0
- package/boost/skills/pilotiq-relations/rules/repeater-relationship.md +177 -0
- package/boost/skills/pilotiq-resource/SKILL.md +61 -0
- package/boost/skills/pilotiq-resource/rules/authorization.md +242 -0
- package/boost/skills/pilotiq-resource/rules/defining-resources.md +228 -0
- package/boost/skills/pilotiq-resource/rules/page-overrides.md +296 -0
- package/dist/actions/exportFactory.d.ts +10 -0
- package/dist/actions/exportFactory.d.ts.map +1 -1
- package/dist/actions/exportFactory.js +10 -0
- package/dist/actions/exportFactory.js.map +1 -1
- package/dist/react/CollabRoomContext.d.ts +5 -5
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +0 -1
- package/dist/react/index.js.map +1 -1
- package/dist/routes/helpers.d.ts.map +1 -1
- package/dist/routes/helpers.js +6 -2
- package/dist/routes/helpers.js.map +1 -1
- package/dist/routes/relations.d.ts.map +1 -1
- package/dist/routes/relations.js +12 -0
- package/dist/routes/relations.js.map +1 -1
- package/package.json +6 -1
- package/.turbo/turbo-build.log +0 -8
- package/CLAUDE.md +0 -265
- package/dist/react/useCollabSeed.d.ts +0 -23
- package/dist/react/useCollabSeed.d.ts.map +0 -1
- package/dist/react/useCollabSeed.js +0 -82
- package/dist/react/useCollabSeed.js.map +0 -1
- package/src/Cluster.test.ts +0 -283
- package/src/Cluster.ts +0 -83
- package/src/Column.test.ts +0 -199
- package/src/Column.ts +0 -710
- package/src/Global.test.ts +0 -367
- package/src/Global.ts +0 -169
- package/src/Page.test.ts +0 -114
- package/src/Page.ts +0 -208
- package/src/Pilotiq.perf.test.ts +0 -252
- package/src/Pilotiq.test.ts +0 -129
- package/src/Pilotiq.ts +0 -1158
- package/src/PilotiqRegistry.ts +0 -36
- package/src/PilotiqServiceProvider.ts +0 -121
- package/src/RelationManager.test.ts +0 -400
- package/src/RelationManager.ts +0 -527
- package/src/RenderHook.test.ts +0 -252
- package/src/RenderHook.ts +0 -242
- package/src/Resource.test.ts +0 -284
- package/src/Resource.ts +0 -526
- package/src/RightPanel.test.ts +0 -202
- package/src/RightPanel.ts +0 -132
- package/src/Tab.test.ts +0 -91
- package/src/Tab.ts +0 -156
- package/src/UserMenuItem.ts +0 -145
- package/src/actions/Action.test.ts +0 -2526
- package/src/actions/Action.ts +0 -1515
- package/src/actions/ActionGroup.test.ts +0 -112
- package/src/actions/ActionGroup.ts +0 -173
- package/src/actions/attachFactory.ts +0 -172
- package/src/actions/bulkFactories.ts +0 -168
- package/src/actions/crudFactories.ts +0 -220
- package/src/actions/exportFactory.ts +0 -215
- package/src/actions/factoryHelpers.ts +0 -177
- package/src/actions/importFactory.ts +0 -243
- package/src/actions/index.ts +0 -17
- package/src/actions/m2mFactories.ts +0 -193
- package/src/actions/relationFactories.ts +0 -372
- package/src/applyPageHooks.test.ts +0 -463
- package/src/applyPageHooks.ts +0 -330
- package/src/authorization.test.ts +0 -483
- package/src/breadcrumbs.test.ts +0 -238
- package/src/cells/coerce.test.ts +0 -85
- package/src/cells/coerce.ts +0 -84
- package/src/clusterPaths.ts +0 -35
- package/src/columns/BadgeColumn.test.ts +0 -54
- package/src/columns/BadgeColumn.ts +0 -32
- package/src/columns/BooleanColumn.test.ts +0 -41
- package/src/columns/BooleanColumn.ts +0 -18
- package/src/columns/ColorColumn.test.ts +0 -37
- package/src/columns/ColorColumn.ts +0 -38
- package/src/columns/IconColumn.test.ts +0 -54
- package/src/columns/IconColumn.ts +0 -37
- package/src/columns/ImageColumn.test.ts +0 -41
- package/src/columns/ImageColumn.ts +0 -28
- package/src/columns/SelectColumn.ts +0 -98
- package/src/columns/TextColumn.test.ts +0 -190
- package/src/columns/TextColumn.ts +0 -20
- package/src/columns/TextInputColumn.ts +0 -68
- package/src/columns/ToggleColumn.ts +0 -46
- package/src/columns/editableColumns.test.ts +0 -238
- package/src/columns/index.ts +0 -9
- package/src/defaultGlobalPages.ts +0 -95
- package/src/defaultPages.test.ts +0 -634
- package/src/defaultPages.ts +0 -617
- package/src/defaultViewPage.test.ts +0 -147
- package/src/elements/Form.test.ts +0 -223
- package/src/elements/Form.ts +0 -416
- package/src/elements/ListTabs.ts +0 -28
- package/src/elements/Table.test.ts +0 -422
- package/src/elements/Table.ts +0 -850
- package/src/elements/TableGroup.test.ts +0 -260
- package/src/elements/TableGroup.ts +0 -334
- package/src/elements/dispatchAction.test.ts +0 -463
- package/src/elements/dispatchAction.ts +0 -355
- package/src/elements/dispatchForm.test.ts +0 -477
- package/src/elements/dispatchForm.ts +0 -1993
- package/src/elements/dispatchTable.test.ts +0 -1514
- package/src/elements/dispatchTable.ts +0 -745
- package/src/elements/index.ts +0 -21
- package/src/entries/BadgeEntry.ts +0 -39
- package/src/entries/CodeEntry.test.ts +0 -40
- package/src/entries/CodeEntry.ts +0 -52
- package/src/entries/ColorEntry.ts +0 -63
- package/src/entries/ComponentEntry.test.ts +0 -173
- package/src/entries/ComponentEntry.ts +0 -95
- package/src/entries/Entry.ts +0 -304
- package/src/entries/IconEntry.ts +0 -49
- package/src/entries/ImageEntry.ts +0 -61
- package/src/entries/KeyValueEntry.ts +0 -47
- package/src/entries/RepeatableEntry.test.ts +0 -239
- package/src/entries/RepeatableEntry.ts +0 -173
- package/src/entries/TextEntry.test.ts +0 -394
- package/src/entries/TextEntry.ts +0 -60
- package/src/entries/index.ts +0 -12
- package/src/entries/leaves.test.ts +0 -306
- package/src/entries/registry.ts +0 -54
- package/src/fields/BuilderField.test.ts +0 -1188
- package/src/fields/BuilderField.ts +0 -605
- package/src/fields/BuilderRelationship.test.ts +0 -811
- package/src/fields/CheckboxField.test.ts +0 -44
- package/src/fields/CheckboxField.ts +0 -27
- package/src/fields/CheckboxListField.test.ts +0 -99
- package/src/fields/CheckboxListField.ts +0 -66
- package/src/fields/ColorPickerField.test.ts +0 -33
- package/src/fields/ColorPickerField.ts +0 -25
- package/src/fields/DateField.ts +0 -54
- package/src/fields/DateTimeField.test.ts +0 -55
- package/src/fields/EmailField.ts +0 -16
- package/src/fields/Field.test.ts +0 -654
- package/src/fields/Field.ts +0 -817
- package/src/fields/FileUploadField.test.ts +0 -143
- package/src/fields/FileUploadField.ts +0 -159
- package/src/fields/HiddenField.test.ts +0 -27
- package/src/fields/HiddenField.ts +0 -28
- package/src/fields/KeyValueField.test.ts +0 -105
- package/src/fields/KeyValueField.ts +0 -55
- package/src/fields/MarkdownField.test.ts +0 -167
- package/src/fields/MarkdownField.ts +0 -162
- package/src/fields/NumberField.ts +0 -33
- package/src/fields/RadioField.test.ts +0 -94
- package/src/fields/RadioField.ts +0 -67
- package/src/fields/RepeaterField.test.ts +0 -1806
- package/src/fields/RepeaterField.ts +0 -939
- package/src/fields/RepeaterRelationship.test.ts +0 -1923
- package/src/fields/RepeaterSimple.test.ts +0 -248
- package/src/fields/RowButton.test.ts +0 -219
- package/src/fields/RowButton.ts +0 -135
- package/src/fields/SelectField.test.ts +0 -192
- package/src/fields/SelectField.ts +0 -235
- package/src/fields/SliderField.test.ts +0 -50
- package/src/fields/SliderField.ts +0 -53
- package/src/fields/SlugField.ts +0 -24
- package/src/fields/TagsInputField.test.ts +0 -154
- package/src/fields/TagsInputField.ts +0 -133
- package/src/fields/TextField.test.ts +0 -213
- package/src/fields/TextField.ts +0 -177
- package/src/fields/TextareaField.test.ts +0 -58
- package/src/fields/TextareaField.ts +0 -59
- package/src/fields/ToggleButtonsField.test.ts +0 -106
- package/src/fields/ToggleButtonsField.ts +0 -59
- package/src/fields/ToggleField.ts +0 -16
- package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +0 -319
- package/src/fields/optionsResolver.ts +0 -95
- package/src/fields/resolveField.ts +0 -28
- package/src/filters/BooleanFilter.ts +0 -35
- package/src/filters/DateRangeFilter.test.ts +0 -194
- package/src/filters/DateRangeFilter.ts +0 -148
- package/src/filters/Filter.test.ts +0 -268
- package/src/filters/Filter.ts +0 -184
- package/src/filters/FormFilter.test.ts +0 -238
- package/src/filters/FormFilter.ts +0 -215
- package/src/filters/MultiSelectFilter.test.ts +0 -119
- package/src/filters/MultiSelectFilter.ts +0 -78
- package/src/filters/QueryBuilderFilter.test.ts +0 -662
- package/src/filters/QueryBuilderFilter.ts +0 -398
- package/src/filters/SelectFilter.ts +0 -46
- package/src/filters/TernaryFilter.test.ts +0 -160
- package/src/filters/TernaryFilter.ts +0 -72
- package/src/filters/TrashedFilter.test.ts +0 -149
- package/src/filters/TrashedFilter.ts +0 -55
- package/src/filters/queryBuilder/BooleanConstraint.ts +0 -31
- package/src/filters/queryBuilder/Constraint.ts +0 -115
- package/src/filters/queryBuilder/DateConstraint.ts +0 -69
- package/src/filters/queryBuilder/NumberConstraint.ts +0 -66
- package/src/filters/queryBuilder/SelectConstraint.ts +0 -72
- package/src/filters/queryBuilder/TextConstraint.ts +0 -64
- package/src/filters/queryBuilder/index.ts +0 -12
- package/src/icons/index.ts +0 -2
- package/src/icons/lucide.ts +0 -204
- package/src/icons/registry.test.ts +0 -56
- package/src/icons/registry.ts +0 -41
- package/src/icons/types.ts +0 -47
- package/src/index.ts +0 -525
- package/src/io/csv.test.ts +0 -142
- package/src/io/csv.ts +0 -170
- package/src/nestedRelationManagerData.test.ts +0 -547
- package/src/notifications/Notification.test.ts +0 -210
- package/src/notifications/Notification.ts +0 -354
- package/src/notifications/broadcast.test.ts +0 -110
- package/src/notifications/broadcast.ts +0 -95
- package/src/notifications/database.test.ts +0 -383
- package/src/notifications/database.ts +0 -398
- package/src/notifications/databaseNotifications.test.ts +0 -187
- package/src/notifications/dispatchNotificationAction.test.ts +0 -341
- package/src/notifications/dispatchNotificationAction.ts +0 -142
- package/src/notifications/flash.test.ts +0 -89
- package/src/notifications/flash.ts +0 -71
- package/src/notifications/index.ts +0 -45
- package/src/notifications/registerBroadcastAuth.test.ts +0 -134
- package/src/notifications/registerBroadcastAuth.ts +0 -100
- package/src/notifications/resolveSavedNotification.test.ts +0 -82
- package/src/notifications/resolveSavedNotification.ts +0 -59
- package/src/notifications/types.ts +0 -93
- package/src/orm/m2mAccessor.ts +0 -66
- package/src/orm/modelDefaults.test.ts +0 -633
- package/src/orm/modelDefaults.ts +0 -666
- package/src/pageData/breadcrumbs.ts +0 -288
- package/src/pageData/forms.ts +0 -578
- package/src/pageData/helpers.ts +0 -857
- package/src/pageData/misc.ts +0 -347
- package/src/pageData/navigation.ts +0 -842
- package/src/pageData/relationPages.ts +0 -1248
- package/src/pageData/relationTabs.ts +0 -286
- package/src/pageData/resourcePages.ts +0 -609
- package/src/pageData.test.ts +0 -1545
- package/src/pageData.ts +0 -341
- package/src/plugins/index.ts +0 -8
- package/src/plugins/themeEditor.test.ts +0 -36
- package/src/plugins/themeEditor.ts +0 -45
- package/src/react/AppShell.tsx +0 -251
- package/src/react/CollabExtensionFactoryRegistry.ts +0 -55
- package/src/react/CollabRoomContext.ts +0 -98
- package/src/react/CollabTextRendererRegistry.ts +0 -102
- package/src/react/CommandPalette.tsx +0 -375
- package/src/react/CurrentUserContext.tsx +0 -50
- package/src/react/CustomPageWrapperGate.tsx +0 -69
- package/src/react/CustomPageWrapperRegistry.ts +0 -45
- package/src/react/FieldFocusReporterRegistry.ts +0 -37
- package/src/react/FieldLabelSlotRegistry.ts +0 -30
- package/src/react/FieldPresenceRegistry.ts +0 -46
- package/src/react/FormCollabBindingRegistry.ts +0 -242
- package/src/react/FormStateContext.tsx +0 -591
- package/src/react/HeadHooks.tsx +0 -126
- package/src/react/MarkdownEditorRegistry.test.ts +0 -38
- package/src/react/MarkdownEditorRegistry.ts +0 -107
- package/src/react/NotificationActionStrip.tsx +0 -263
- package/src/react/NotificationBell.tsx +0 -426
- package/src/react/PendingSuggestionApplierRegistry.test.ts +0 -97
- package/src/react/PendingSuggestionApplierRegistry.ts +0 -98
- package/src/react/PendingSuggestionOverlayRegistry.ts +0 -54
- package/src/react/PendingSuggestionsContext.tsx +0 -172
- package/src/react/RecordWrapperGate.tsx +0 -58
- package/src/react/RecordWrapperRegistry.ts +0 -39
- package/src/react/RenderHookSlot.tsx +0 -32
- package/src/react/RightSidebar.tsx +0 -257
- package/src/react/RightSidebarContext.tsx +0 -234
- package/src/react/RightSidebarTrigger.tsx +0 -53
- package/src/react/RowCoordsContext.tsx +0 -23
- package/src/react/SchemaRenderer.tsx +0 -549
- package/src/react/SearchTrigger.tsx +0 -46
- package/src/react/ThemeProvider.tsx +0 -93
- package/src/react/ThemeSettingsPage.tsx +0 -579
- package/src/react/ThemeToggle.tsx +0 -20
- package/src/react/Toaster.tsx +0 -158
- package/src/react/UserMenu.tsx +0 -196
- package/src/react/WidgetDataContext.tsx +0 -157
- package/src/react/cells/EditableCell.tsx +0 -389
- package/src/react/component-slots.test.ts +0 -103
- package/src/react/component-slots.ts +0 -116
- package/src/react/fieldJsHandler.test.ts +0 -166
- package/src/react/fieldJsHandler.ts +0 -79
- package/src/react/fields/BuilderInput.tsx +0 -1078
- package/src/react/fields/CheckboxInput.tsx +0 -39
- package/src/react/fields/CheckboxListInput.tsx +0 -102
- package/src/react/fields/ColorInput.tsx +0 -71
- package/src/react/fields/DateFieldInput.tsx +0 -70
- package/src/react/fields/DateTimeInput.tsx +0 -62
- package/src/react/fields/FieldShell.tsx +0 -348
- package/src/react/fields/FileUploadInput.tsx +0 -639
- package/src/react/fields/HiddenInput.tsx +0 -17
- package/src/react/fields/KeyValueInput.tsx +0 -230
- package/src/react/fields/MarkdownInput.tsx +0 -560
- package/src/react/fields/RadioInput.tsx +0 -81
- package/src/react/fields/RepeaterInput.test.ts +0 -116
- package/src/react/fields/RepeaterInput.tsx +0 -1420
- package/src/react/fields/SelectFieldInput.tsx +0 -280
- package/src/react/fields/SliderInput.tsx +0 -81
- package/src/react/fields/TagsInput.tsx +0 -283
- package/src/react/fields/TextLikeInput.tsx +0 -256
- package/src/react/fields/ToggleButtonsInput.tsx +0 -60
- package/src/react/fields/ToggleFieldInput.tsx +0 -56
- package/src/react/fields/relationshipRenameDispatch.test.ts +0 -106
- package/src/react/fields/relationshipRenameDispatch.ts +0 -97
- package/src/react/fields/repeaterReconcile.test.ts +0 -114
- package/src/react/fields/repeaterReconcile.ts +0 -104
- package/src/react/fields/rowChromeButton.tsx +0 -336
- package/src/react/fields/rowState.ts +0 -106
- package/src/react/fields/syncRowGates.test.ts +0 -202
- package/src/react/fields/syncRowGates.ts +0 -66
- package/src/react/fields/textInputControls.tsx +0 -238
- package/src/react/fields/useRowReorderDnd.ts +0 -78
- package/src/react/formStateHelpers.test.ts +0 -508
- package/src/react/formStateHelpers.ts +0 -381
- package/src/react/hooks/use-mobile.ts +0 -19
- package/src/react/icon-context.tsx +0 -60
- package/src/react/index.ts +0 -195
- package/src/react/layouts/SidebarLayout.tsx +0 -250
- package/src/react/layouts/TopbarLayout.tsx +0 -258
- package/src/react/navigate.tsx +0 -37
- package/src/react/onProviderSynced.test.ts +0 -90
- package/src/react/parseRecordEditUrl.test.ts +0 -122
- package/src/react/parseRecordEditUrl.ts +0 -94
- package/src/react/persistedState.ts +0 -40
- package/src/react/registry.ts +0 -48
- package/src/react/right-panel-registry.tsx +0 -47
- package/src/react/schemaRenderer/AlertRenderer.tsx +0 -112
- package/src/react/schemaRenderer/EntryRenderer.tsx +0 -501
- package/src/react/schemaRenderer/SectionRenderer.tsx +0 -120
- package/src/react/schemaRenderer/SimpleElements.tsx +0 -306
- package/src/react/schemaRenderer/TabsRenderer.tsx +0 -62
- package/src/react/schemaRenderer/WizardRenderer.tsx +0 -338
- package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +0 -177
- package/src/react/schemaRenderer/action/ActionModalDialog.tsx +0 -273
- package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +0 -61
- package/src/react/schemaRenderer/action/HandlerActionButton.tsx +0 -43
- package/src/react/schemaRenderer/action/MethodActionButton.tsx +0 -64
- package/src/react/schemaRenderer/action/buttons.tsx +0 -99
- package/src/react/schemaRenderer/action/helpers.ts +0 -140
- package/src/react/schemaRenderer/action/renderAction.tsx +0 -245
- package/src/react/schemaRenderer/columnFormat.ts +0 -65
- package/src/react/schemaRenderer/constants.ts +0 -50
- package/src/react/schemaRenderer/form/FormRenderer.tsx +0 -274
- package/src/react/schemaRenderer/form/renderField.tsx +0 -511
- package/src/react/schemaRenderer/helpers.tsx +0 -81
- package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +0 -308
- package/src/react/schemaRenderer/table/TableRenderer.tsx +0 -123
- package/src/react/schemaRenderer/table/TableRendererBody.tsx +0 -974
- package/src/react/schemaRenderer/table/filters.tsx +0 -1233
- package/src/react/schemaRenderer/table/formatCell.tsx +0 -264
- package/src/react/schemaRenderer/table/links.tsx +0 -112
- package/src/react/schemaRenderer/table/renderRowActions.tsx +0 -52
- package/src/react/schemaRenderer/table/url.tsx +0 -143
- package/src/react/theme-preview/apply.ts +0 -99
- package/src/react/theme-preview/build-html.ts +0 -436
- package/src/react/ui/button.tsx +0 -51
- package/src/react/ui/calendar.tsx +0 -67
- package/src/react/ui/checkbox.tsx +0 -29
- package/src/react/ui/dialog.tsx +0 -108
- package/src/react/ui/dropdown-menu.tsx +0 -97
- package/src/react/ui/input.tsx +0 -20
- package/src/react/ui/label.tsx +0 -21
- package/src/react/ui/popover.tsx +0 -50
- package/src/react/ui/select.tsx +0 -169
- package/src/react/ui/separator.tsx +0 -25
- package/src/react/ui/sheet.tsx +0 -136
- package/src/react/ui/sidebar.tsx +0 -723
- package/src/react/ui/skeleton.tsx +0 -13
- package/src/react/ui/slider.tsx +0 -34
- package/src/react/ui/switch.tsx +0 -28
- package/src/react/ui/table.tsx +0 -105
- package/src/react/ui/tabs.tsx +0 -63
- package/src/react/ui/textarea.tsx +0 -18
- package/src/react/ui/tooltip.tsx +0 -64
- package/src/react/useCollabSeed.ts +0 -86
- package/src/react/useResizableWidth.ts +0 -139
- package/src/react/utils.ts +0 -6
- package/src/react/widgetRegistry.test.ts +0 -43
- package/src/react/widgetRegistry.ts +0 -50
- package/src/react/widgets/StatsOverviewRenderer.tsx +0 -232
- package/src/react/widgets/TableWidgetRenderer.tsx +0 -231
- package/src/react/widgets/ViewRenderer.tsx +0 -71
- package/src/relationManagerData.test.ts +0 -1595
- package/src/richtext/index.ts +0 -8
- package/src/richtext/registry.ts +0 -89
- package/src/routes/globals.ts +0 -148
- package/src/routes/guard.test.ts +0 -325
- package/src/routes/helpers.ts +0 -700
- package/src/routes/pages.ts +0 -175
- package/src/routes/panel.ts +0 -204
- package/src/routes/relations.ts +0 -1227
- package/src/routes/resources.ts +0 -781
- package/src/routes/theme.ts +0 -91
- package/src/routes-nested-relations.test.ts +0 -676
- package/src/routes-relations.test.ts +0 -972
- package/src/routes.test.ts +0 -2027
- package/src/routes.ts +0 -303
- package/src/schema/Alert.test.ts +0 -109
- package/src/schema/Alert.ts +0 -131
- package/src/schema/Block.ts +0 -169
- package/src/schema/Breadcrumbs.ts +0 -40
- package/src/schema/Card.ts +0 -35
- package/src/schema/Divider.ts +0 -20
- package/src/schema/Element.ts +0 -219
- package/src/schema/EmptyState.test.ts +0 -37
- package/src/schema/EmptyState.ts +0 -63
- package/src/schema/Fieldset.ts +0 -43
- package/src/schema/Grid.ts +0 -43
- package/src/schema/Group.ts +0 -30
- package/src/schema/Heading.ts +0 -39
- package/src/schema/Html.ts +0 -67
- package/src/schema/Icon.ts +0 -54
- package/src/schema/Image.ts +0 -57
- package/src/schema/LinkTag.ts +0 -41
- package/src/schema/Markdown.ts +0 -85
- package/src/schema/MetaTag.ts +0 -41
- package/src/schema/RelationTabs.ts +0 -71
- package/src/schema/ScriptTag.ts +0 -55
- package/src/schema/Section.ts +0 -160
- package/src/schema/ServerDataElement.test.ts +0 -140
- package/src/schema/ServerDataElement.ts +0 -156
- package/src/schema/SlotComponent.test.ts +0 -77
- package/src/schema/SlotComponent.ts +0 -71
- package/src/schema/Split.ts +0 -50
- package/src/schema/Stat.test.ts +0 -118
- package/src/schema/Stat.ts +0 -154
- package/src/schema/StatsOverview.test.ts +0 -141
- package/src/schema/StatsOverview.ts +0 -119
- package/src/schema/StyleTag.ts +0 -35
- package/src/schema/TableWidget.test.ts +0 -297
- package/src/schema/TableWidget.ts +0 -289
- package/src/schema/Tabs.ts +0 -79
- package/src/schema/Text.ts +0 -58
- package/src/schema/UnorderedList.ts +0 -49
- package/src/schema/View.test.ts +0 -111
- package/src/schema/View.ts +0 -127
- package/src/schema/Wizard.ts +0 -220
- package/src/schema/containers.test.ts +0 -564
- package/src/schema/headTags.test.ts +0 -134
- package/src/schema/index.ts +0 -40
- package/src/schema/primes.test.ts +0 -269
- package/src/schema/resolveSchema.test.ts +0 -379
- package/src/schema/resolveSchema.ts +0 -917
- package/src/schema/sanitize.ts +0 -58
- package/src/search.test.ts +0 -446
- package/src/search.ts +0 -178
- package/src/sessionFilters.test.ts +0 -375
- package/src/sessionFilters.ts +0 -143
- package/src/slot-components/index.ts +0 -10
- package/src/slot-components/registry.ts +0 -56
- package/src/styles/file-upload.css +0 -13
- package/src/summarizers/Summarizer.test.ts +0 -84
- package/src/summarizers/Summarizer.ts +0 -123
- package/src/summarizers/index.ts +0 -11
- package/src/theme/base-colors.ts +0 -68
- package/src/theme/chart-colors.ts +0 -50
- package/src/theme/colors.ts +0 -447
- package/src/theme/generate-css.test.ts +0 -139
- package/src/theme/generate-css.ts +0 -44
- package/src/theme/generate-scale.test.ts +0 -106
- package/src/theme/generate-scale.ts +0 -97
- package/src/theme/icon-map.ts +0 -42
- package/src/theme/index.ts +0 -34
- package/src/theme/migrate.test.ts +0 -178
- package/src/theme/migrate.ts +0 -81
- package/src/theme/presets.ts +0 -135
- package/src/theme/radius.ts +0 -18
- package/src/theme/resolve.test.ts +0 -238
- package/src/theme/resolve.ts +0 -96
- package/src/theme/spacing.ts +0 -18
- package/src/theme/storage.test.ts +0 -126
- package/src/theme/storage.ts +0 -106
- package/src/theme/theme-colors.ts +0 -88
- package/src/theme/types.ts +0 -125
- package/src/uploads/UploadAdapter.ts +0 -35
- package/src/uploads/index.ts +0 -2
- package/src/uploads/localUpload.test.ts +0 -70
- package/src/uploads/localUpload.ts +0 -84
- package/src/validation/Validator.ts +0 -49
- package/src/validation/index.ts +0 -28
- package/src/validation/rules.ts +0 -78
- package/src/validation/runValidators.ts +0 -435
- package/src/validation/uniqueValidator.test.ts +0 -196
- package/src/validation/uniqueValidator.ts +0 -133
- package/src/validation/validators.test.ts +0 -268
- package/src/vite.test.ts +0 -184
- package/src/vite.ts +0 -787
- package/src/widgets/index.ts +0 -10
- package/src/widgets/registry.ts +0 -45
- package/src/widgets.test.ts +0 -592
- package/tsconfig.build.json +0 -11
- package/tsconfig.json +0 -4
- package/tsconfig.test.json +0 -10
- package/views/react/Dashboard.tsx +0 -27
- package/views/react/Resources/Form.tsx +0 -102
- 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,215 +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
|
-
|
|
10
|
-
import { Table } from '../elements/Table.js'
|
|
11
|
-
import { Column } from '../Column.js'
|
|
12
|
-
import type { ActionContext } from './Action.js'
|
|
13
|
-
import { encodeCsv, type CsvColumn } from '../io/csv.js'
|
|
14
|
-
import { parseTableQuery, type QueryParams } from '../elements/dispatchTable.js'
|
|
15
|
-
|
|
16
|
-
export type ExportFormat = 'csv' | 'json'
|
|
17
|
-
|
|
18
|
-
export type ExportColumnSpec =
|
|
19
|
-
| string
|
|
20
|
-
| {
|
|
21
|
-
key: string
|
|
22
|
-
label?: string
|
|
23
|
-
format?: (value: unknown, record: Record<string, unknown>) => string
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface ExportOptions {
|
|
27
|
-
/** Columns to include. Strings → bare `key` (label = key). Objects
|
|
28
|
-
* may add `label` and `format(value, record) => string`. When omitted,
|
|
29
|
-
* defaults to every `Column` returned by `R.table()` in declaration
|
|
30
|
-
* order, with header label = `Column.getLabel()`. */
|
|
31
|
-
columns?: ExportColumnSpec[]
|
|
32
|
-
/** Output filename. Function form receives the action context (so the
|
|
33
|
-
* filename can include the user, current filters, etc). Default:
|
|
34
|
-
* `${R.slug}-${YYYY-MM-DD}.${format}`. */
|
|
35
|
-
filename?: string | ((ctx: ActionContext) => string)
|
|
36
|
-
/** Output format. Default `'csv'`. */
|
|
37
|
-
format?: ExportFormat
|
|
38
|
-
/** Records scope:
|
|
39
|
-
* - `'all'` — ignore the current URL state (filters/search/sort)
|
|
40
|
-
* - `'filtered'` — apply the current URL state (default; matches the
|
|
41
|
-
* list page the user is looking at)
|
|
42
|
-
* - `'page'` — only export the visible page
|
|
43
|
-
*
|
|
44
|
-
* Bulk variant ignores `scope` and always uses `ctx.records`.
|
|
45
|
-
*/
|
|
46
|
-
scope?: 'all' | 'filtered' | 'page'
|
|
47
|
-
/** Hard cap on the row count. Aborts with an error notification when
|
|
48
|
-
* exceeded. Default `50_000`. */
|
|
49
|
-
maxRows?: number
|
|
50
|
-
/** Pagination chunk size when walking the records handler. Default
|
|
51
|
-
* `1_000`. */
|
|
52
|
-
chunkSize?: number
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
interface ResolvedColumn {
|
|
56
|
-
key: string
|
|
57
|
-
label: string
|
|
58
|
-
format?: (value: unknown, record: Record<string, unknown>) => string
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const DEFAULT_MAX_ROWS = 50_000
|
|
62
|
-
const DEFAULT_CHUNK = 1_000
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Resolve the column spec into a uniform `{ key, label, format? }[]`,
|
|
66
|
-
* defaulting to every `Column` child on `R.table(Table.make())` when
|
|
67
|
-
* the spec is omitted.
|
|
68
|
-
*/
|
|
69
|
-
export function resolveExportColumns(
|
|
70
|
-
spec: ExportOptions['columns'],
|
|
71
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
-
R: { table?(t: any): any },
|
|
73
|
-
): ResolvedColumn[] {
|
|
74
|
-
if (spec && spec.length > 0) {
|
|
75
|
-
return spec.map(s => typeof s === 'string'
|
|
76
|
-
? { key: s, label: s }
|
|
77
|
-
: { key: s.key, label: s.label ?? s.key, ...(s.format ? { format: s.format } : {}) })
|
|
78
|
-
}
|
|
79
|
-
const tableInst = R.table?.(Table.make())
|
|
80
|
-
if (!tableInst) return []
|
|
81
|
-
const cols: Column[] = tableInst.getColumns?.() ?? []
|
|
82
|
-
return cols.map(c => ({ key: c.name, label: c.getLabel?.() ?? c.name }))
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Read the URL query record off `ctx.request`. Routes pass a Hono-like
|
|
87
|
-
* request object whose `.query` is a `Record<string, string>`. Fall
|
|
88
|
-
* back to `{}` when missing (programmatic dispatches in tests).
|
|
89
|
-
*/
|
|
90
|
-
export function readRequestQuery(ctx: ActionContext): QueryParams {
|
|
91
|
-
const req = ctx.request as { query?: unknown } | undefined
|
|
92
|
-
const q = req?.query
|
|
93
|
-
if (q && typeof q === 'object') return q as QueryParams
|
|
94
|
-
return {}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Drive the table's `records()` handler in pages until we run out of
|
|
99
|
-
* rows or hit `maxRows`. Returns the collected raw records (not yet
|
|
100
|
-
* mapped to row objects).
|
|
101
|
-
*
|
|
102
|
-
* Scope behavior:
|
|
103
|
-
* - `'all'`: empty TableContext (no filters/search/sort applied)
|
|
104
|
-
* - `'filtered'`: TableContext built from the URL query (filters /
|
|
105
|
-
* search / sort all flow through)
|
|
106
|
-
* - `'page'`: same as `'filtered'` but only the visible page
|
|
107
|
-
*/
|
|
108
|
-
export async function collectExportRows(
|
|
109
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
110
|
-
table: any,
|
|
111
|
-
ctx: ActionContext,
|
|
112
|
-
scope: ExportOptions['scope'] = 'filtered',
|
|
113
|
-
maxRows: number = DEFAULT_MAX_ROWS,
|
|
114
|
-
chunkSize: number = DEFAULT_CHUNK,
|
|
115
|
-
): Promise<unknown[]> {
|
|
116
|
-
const handler = table.getRecords?.()
|
|
117
|
-
if (!handler) {
|
|
118
|
-
throw new Error('Resource table has no records() handler — cannot export. Set R.model or Table.records(fn).')
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const baseCtx = scope === 'all' ? {} : buildTableCtxFromQuery(readRequestQuery(ctx), table)
|
|
122
|
-
|
|
123
|
-
const collected: unknown[] = []
|
|
124
|
-
let page = scope === 'page' ? (baseCtx.page ?? 1) : 1
|
|
125
|
-
const effectivePerPage = scope === 'page' ? (baseCtx.perPage ?? chunkSize) : chunkSize
|
|
126
|
-
|
|
127
|
-
while (collected.length <= maxRows) {
|
|
128
|
-
const result = await handler({ ...baseCtx, page, perPage: effectivePerPage })
|
|
129
|
-
const rows: unknown[] = Array.isArray(result) ? result : (result?.rows ?? [])
|
|
130
|
-
collected.push(...rows)
|
|
131
|
-
if (rows.length < effectivePerPage) break
|
|
132
|
-
if (scope === 'page') break
|
|
133
|
-
page++
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return collected
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Build a `TableContext` from a URL-query record by reusing the same
|
|
141
|
-
* parsing the list page does. Filters mirror onto the table's Filter
|
|
142
|
-
* children so any `Filter.query()` overrides see them on subsequent
|
|
143
|
-
* runs (defensive — `loadTableRecords` does the same).
|
|
144
|
-
*/
|
|
145
|
-
function buildTableCtxFromQuery(
|
|
146
|
-
query: QueryParams,
|
|
147
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
148
|
-
table: any,
|
|
149
|
-
): {
|
|
150
|
-
search?: string
|
|
151
|
-
sort?: { column: string; direction: 'asc' | 'desc' }
|
|
152
|
-
filters?: Record<string, string>
|
|
153
|
-
page?: number
|
|
154
|
-
perPage?: number
|
|
155
|
-
} {
|
|
156
|
-
const { search, sort, page, perPage } = parseTableQuery(query)
|
|
157
|
-
|
|
158
|
-
const tableFilters = table.getFilters?.() ?? []
|
|
159
|
-
const filterValues: Record<string, string> = {}
|
|
160
|
-
const reserved = new Set(['search', 'sort', 'page', 'perPage', 'group', 'groupKey'])
|
|
161
|
-
for (const f of tableFilters) {
|
|
162
|
-
const name = f.name as string
|
|
163
|
-
if (reserved.has(name)) continue
|
|
164
|
-
const v = (query as Record<string, unknown>)[name]
|
|
165
|
-
if (typeof v === 'string' && v !== '') filterValues[name] = v
|
|
166
|
-
}
|
|
167
|
-
for (const f of tableFilters) {
|
|
168
|
-
const v = filterValues[f.name as string]
|
|
169
|
-
if (v !== undefined && typeof f.withValue === 'function') f.withValue(v)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const out: ReturnType<typeof buildTableCtxFromQuery> = {}
|
|
173
|
-
if (search !== undefined) out.search = search
|
|
174
|
-
if (sort !== undefined) out.sort = sort
|
|
175
|
-
if (Object.keys(filterValues).length > 0) out.filters = filterValues
|
|
176
|
-
if (page !== undefined) out.page = page
|
|
177
|
-
if (perPage !== undefined) out.perPage = perPage
|
|
178
|
-
return out
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/** Build a single CSV/JSON-shaped row by reading each resolved column's
|
|
182
|
-
* key off the record (with `format` applied when set). */
|
|
183
|
-
export function buildExportRow(
|
|
184
|
-
record: unknown,
|
|
185
|
-
cols: ReadonlyArray<ResolvedColumn>,
|
|
186
|
-
): Record<string, unknown> {
|
|
187
|
-
const r = record as Record<string, unknown>
|
|
188
|
-
const out: Record<string, unknown> = {}
|
|
189
|
-
for (const col of cols) {
|
|
190
|
-
const raw = r[col.key]
|
|
191
|
-
out[col.key] = col.format ? col.format(raw, r) : raw
|
|
192
|
-
}
|
|
193
|
-
return out
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/** Encode `rows` as the requested format. Object values that survive
|
|
197
|
-
* to here go through the CSV escape rules / `JSON.stringify`. */
|
|
198
|
-
export function encodeExport(
|
|
199
|
-
rows: ReadonlyArray<Record<string, unknown>>,
|
|
200
|
-
cols: ReadonlyArray<ResolvedColumn>,
|
|
201
|
-
format: ExportFormat,
|
|
202
|
-
): { body: string; contentType: string } {
|
|
203
|
-
if (format === 'json') {
|
|
204
|
-
return { body: JSON.stringify(rows, null, 2), contentType: 'application/json' }
|
|
205
|
-
}
|
|
206
|
-
const csvCols: CsvColumn[] = cols.map(c => ({ key: c.key, label: c.label }))
|
|
207
|
-
return { body: encodeCsv(rows, csvCols), contentType: 'text/csv; charset=utf-8' }
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/** Default filename: `${slug}-${YYYY-MM-DD}.${ext}`. */
|
|
211
|
-
export function defaultExportFilename(slug: string, format: ExportFormat): string {
|
|
212
|
-
const date = new Date().toISOString().slice(0, 10)
|
|
213
|
-
const ext = format === 'json' ? 'json' : 'csv'
|
|
214
|
-
return `${slug}-${date}.${ext}`
|
|
215
|
-
}
|
|
@@ -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
|
-
}
|