@pilotiq/pilotiq 0.24.1 → 0.24.3
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 +57 -0
- package/boost/guidelines.md +571 -0
- package/boost/skills/pilotiq-actions/SKILL.md +49 -0
- package/boost/skills/pilotiq-actions/rules/dispatch-modes.md +177 -0
- package/boost/skills/pilotiq-actions/rules/factories.md +130 -0
- package/boost/skills/pilotiq-actions/rules/visibility-and-authorization.md +125 -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/Pilotiq.d.ts +31 -0
- package/dist/Pilotiq.d.ts.map +1 -1
- package/dist/Pilotiq.js +3 -1
- package/dist/Pilotiq.js.map +1 -1
- package/dist/PilotiqRegistry.d.ts +13 -0
- package/dist/PilotiqRegistry.d.ts.map +1 -1
- package/dist/PilotiqRegistry.js +15 -0
- package/dist/PilotiqRegistry.js.map +1 -1
- package/dist/pageData/misc.d.ts.map +1 -1
- package/dist/pageData/misc.js +6 -0
- package/dist/pageData/misc.js.map +1 -1
- package/dist/pageData/navigation.d.ts +1 -0
- package/dist/pageData/navigation.d.ts.map +1 -1
- package/dist/pageData/navigation.js +3 -0
- package/dist/pageData/navigation.js.map +1 -1
- package/dist/pageData/relationPages.d.ts.map +1 -1
- package/dist/pageData/relationPages.js +3 -0
- package/dist/pageData/relationPages.js.map +1 -1
- package/dist/pageData/resourcePages.d.ts.map +1 -1
- package/dist/pageData/resourcePages.js +8 -0
- package/dist/pageData/resourcePages.js.map +1 -1
- package/dist/react/AppShell.d.ts +8 -0
- package/dist/react/AppShell.d.ts.map +1 -1
- package/dist/react/AppShell.js.map +1 -1
- package/dist/react/layouts/SidebarLayout.d.ts.map +1 -1
- package/dist/react/layouts/SidebarLayout.js +10 -2
- package/dist/react/layouts/SidebarLayout.js.map +1 -1
- package/dist/react/widgets/StatsOverviewRenderer.d.ts.map +1 -1
- package/dist/react/widgets/StatsOverviewRenderer.js +32 -18
- package/dist/react/widgets/StatsOverviewRenderer.js.map +1 -1
- package/dist/routes/relations.d.ts.map +1 -1
- package/dist/routes/relations.js +25 -18
- package/dist/routes/relations.js.map +1 -1
- package/dist/routes/resources.js.map +1 -1
- package/package.json +10 -5
- package/.turbo/turbo-build.log +0 -8
- package/CLAUDE.md +0 -265
- 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 -225
- 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 -194
- 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/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 -704
- package/src/routes/pages.ts +0 -175
- package/src/routes/panel.ts +0 -204
- package/src/routes/relations.ts +0 -1243
- 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
package/src/PilotiqRegistry.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { Pilotiq } from './Pilotiq.js'
|
|
2
|
-
|
|
3
|
-
const g = globalThis as Record<string, unknown>
|
|
4
|
-
const STORE_KEY = '__pilotiq_registry'
|
|
5
|
-
if (!g[STORE_KEY]) g[STORE_KEY] = new Map<string, Pilotiq>()
|
|
6
|
-
const map = g[STORE_KEY] as Map<string, Pilotiq>
|
|
7
|
-
|
|
8
|
-
export const PilotiqRegistry = {
|
|
9
|
-
register(panel: Pilotiq): void {
|
|
10
|
-
const name = panel.getConfig().name
|
|
11
|
-
if (map.has(name)) {
|
|
12
|
-
throw new Error(`[Pilotiq] A panel named "${name}" is already registered.`)
|
|
13
|
-
}
|
|
14
|
-
map.set(name, panel)
|
|
15
|
-
},
|
|
16
|
-
|
|
17
|
-
all(): Pilotiq[] {
|
|
18
|
-
return [...map.values()]
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
get(name: string): Pilotiq | undefined {
|
|
22
|
-
return map.get(name)
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
findByPath(path: string): Pilotiq | undefined {
|
|
26
|
-
for (const panel of map.values()) {
|
|
27
|
-
if (panel.getConfig().path === path) return panel
|
|
28
|
-
}
|
|
29
|
-
return undefined
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
/** @internal — for testing and dev hot-reload */
|
|
33
|
-
reset(): void {
|
|
34
|
-
map.clear()
|
|
35
|
-
},
|
|
36
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { ServiceProvider } from '@rudderjs/core'
|
|
2
|
-
import type { Application } from '@rudderjs/core'
|
|
3
|
-
import type { Pilotiq } from './Pilotiq.js'
|
|
4
|
-
import { PilotiqRegistry } from './PilotiqRegistry.js'
|
|
5
|
-
import { registerPilotiqRoutes } from './routes.js'
|
|
6
|
-
import { migrateThemeOverrides } from './theme/migrate.js'
|
|
7
|
-
import { prismaThemeStorage } from './theme/storage.js'
|
|
8
|
-
import type { PanelGlobalDelegate, ThemeStorageAdapter } from './theme/storage.js'
|
|
9
|
-
|
|
10
|
-
// ─── Service Provider ─────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
const autoFallbackWarned = new Set<string>()
|
|
13
|
-
|
|
14
|
-
class PilotiqServiceProvider extends ServiceProvider {
|
|
15
|
-
private panels: Pilotiq[]
|
|
16
|
-
|
|
17
|
-
constructor(app: Application, panels: Pilotiq[]) {
|
|
18
|
-
super(app)
|
|
19
|
-
this.panels = panels
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
register(): void {
|
|
23
|
-
PilotiqRegistry.reset()
|
|
24
|
-
for (const panel of this.panels) {
|
|
25
|
-
PilotiqRegistry.register(panel)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async boot(): Promise<void> {
|
|
30
|
-
const { router } = await import('@rudderjs/router') as {
|
|
31
|
-
router: Parameters<typeof registerPilotiqRoutes>[0]
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
for (const panel of PilotiqRegistry.all()) {
|
|
35
|
-
if (panel.getConfig().themeEditor) {
|
|
36
|
-
await loadThemeOverrides(this.app, panel)
|
|
37
|
-
}
|
|
38
|
-
registerPilotiqRoutes(router, panel)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Resolve the panel's theme storage adapter and hydrate any persisted
|
|
45
|
-
* overrides onto the panel.
|
|
46
|
-
*
|
|
47
|
-
* - Explicit `themeEditor({ storage })`: errors bubble (the user opted
|
|
48
|
-
* in, misconfiguration should surface loudly).
|
|
49
|
-
* - Implicit Prisma fallback: errors swallowed for back-compat with a
|
|
50
|
-
* one-time deprecation warning. Removing this branch is the breaking
|
|
51
|
-
* change scheduled for the next minor.
|
|
52
|
-
*/
|
|
53
|
-
async function loadThemeOverrides(app: Application, panel: Pilotiq): Promise<void> {
|
|
54
|
-
const adapter = resolveThemeStorage(app, panel)
|
|
55
|
-
if (!adapter) return
|
|
56
|
-
|
|
57
|
-
const isExplicit = panel.getConfig().themeStorage === adapter
|
|
58
|
-
if (!isExplicit) panel._setThemeStorage(adapter)
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
const overrides = await adapter.load()
|
|
62
|
-
if (overrides) panel.setThemeOverrides(migrateThemeOverrides(overrides))
|
|
63
|
-
} catch (e) {
|
|
64
|
-
if (isExplicit) throw e
|
|
65
|
-
// Implicit fallback: swallow connection / schema errors. Removed
|
|
66
|
-
// alongside the auto-fallback branch in a future minor.
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function resolveThemeStorage(app: Application, panel: Pilotiq): ThemeStorageAdapter | null {
|
|
71
|
-
const explicit = panel.getConfig().themeStorage
|
|
72
|
-
if (explicit) return explicit
|
|
73
|
-
|
|
74
|
-
let prisma: PanelGlobalDelegate | null
|
|
75
|
-
try {
|
|
76
|
-
prisma = app.make('prisma') as PanelGlobalDelegate
|
|
77
|
-
} catch {
|
|
78
|
-
return null
|
|
79
|
-
}
|
|
80
|
-
if (!prisma || typeof prisma.panelGlobal?.findUnique !== 'function') return null
|
|
81
|
-
|
|
82
|
-
const panelName = panel.getConfig().name
|
|
83
|
-
if (!autoFallbackWarned.has(panelName)) {
|
|
84
|
-
autoFallbackWarned.add(panelName)
|
|
85
|
-
console.warn(
|
|
86
|
-
`[pilotiq] themeEditor() on panel "${panelName}" is using the implicit ` +
|
|
87
|
-
`Prisma fallback for theme persistence. Pass storage explicitly — ` +
|
|
88
|
-
`themeEditor({ storage: prismaThemeStorage(prisma, { slug: '${panelName}__theme' }) }) — ` +
|
|
89
|
-
`the implicit fallback is deprecated and will be removed in a future minor.`,
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
return prismaThemeStorage(prisma, { slug: `${panelName}__theme` })
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** @internal — test seam; resets the "deprecation already warned" memo. */
|
|
96
|
-
export function _resetThemeFallbackWarned(): void {
|
|
97
|
-
autoFallbackWarned.clear()
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// ─── Factory ──────────────────────────────────────────────
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Register one or more Pilotiq panels and mount their view routes.
|
|
104
|
-
*
|
|
105
|
-
* @example
|
|
106
|
-
* import { pilotiq } from '@pilotiq/pilotiq'
|
|
107
|
-
* import { adminPanel } from './app/Pilotiq/AdminPanel.js'
|
|
108
|
-
*
|
|
109
|
-
* export default [
|
|
110
|
-
* pilotiq([adminPanel]),
|
|
111
|
-
* ]
|
|
112
|
-
*/
|
|
113
|
-
export function pilotiq(
|
|
114
|
-
panelList: Pilotiq[],
|
|
115
|
-
): new (app: Application) => ServiceProvider {
|
|
116
|
-
return class PilotiqProvider extends PilotiqServiceProvider {
|
|
117
|
-
constructor(app: Application) {
|
|
118
|
-
super(app, panelList)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
import { describe, it } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
RelationManager,
|
|
6
|
-
RESERVED_RELATIONSHIP_TOKENS,
|
|
7
|
-
safeManagerPolicy,
|
|
8
|
-
normalizeRelationMode,
|
|
9
|
-
type RelationManagerContext,
|
|
10
|
-
} from './RelationManager.js'
|
|
11
|
-
import {
|
|
12
|
-
getMorphRelationDescriptor,
|
|
13
|
-
computeMorphPayload,
|
|
14
|
-
type ModelLike,
|
|
15
|
-
} from './orm/modelDefaults.js'
|
|
16
|
-
import { Form } from './elements/Form.js'
|
|
17
|
-
import { Table } from './elements/Table.js'
|
|
18
|
-
import { Column } from './Column.js'
|
|
19
|
-
import { TextField } from './fields/TextField.js'
|
|
20
|
-
|
|
21
|
-
/** Stub manager-context for tests that don't care about URL templating. */
|
|
22
|
-
const stubCtx: RelationManagerContext = {
|
|
23
|
-
basePath: '/admin',
|
|
24
|
-
parentSlug: 'users',
|
|
25
|
-
parentId: '1',
|
|
26
|
-
relationship: 'posts',
|
|
27
|
-
parentRecord: { id: '1' },
|
|
28
|
-
mode: 'hasMany',
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
describe('RelationManager (static API)', () => {
|
|
32
|
-
it('default form/table/detail are no-ops', () => {
|
|
33
|
-
class M extends RelationManager {
|
|
34
|
-
static override relationship = 'posts'
|
|
35
|
-
}
|
|
36
|
-
const f = M.form(Form.make(), stubCtx)
|
|
37
|
-
const t = M.table(Table.make(), stubCtx)
|
|
38
|
-
assert.equal(f.getChildren(), undefined)
|
|
39
|
-
assert.equal(t.getChildren(), undefined)
|
|
40
|
-
assert.deepEqual(M.detail({}, {}), [])
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('subclass overrides receive the builder and configure it', () => {
|
|
44
|
-
class PostsManager extends RelationManager {
|
|
45
|
-
static override relationship = 'posts'
|
|
46
|
-
static override label = 'Posts'
|
|
47
|
-
|
|
48
|
-
static override form(form: Form): Form {
|
|
49
|
-
return form.schema([TextField.make('title').required()])
|
|
50
|
-
}
|
|
51
|
-
static override table(table: Table): Table {
|
|
52
|
-
return table.columns([Column.make('title').sortable()])
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Subclass overrides may drop trailing parameters they don't need —
|
|
57
|
-
// TypeScript narrows the call signature to the override's, so callers
|
|
58
|
-
// through the subclass type pass only `(form)` / `(table)`. The
|
|
59
|
-
// framework calls via `typeof RelationManager`, which sees the base
|
|
60
|
-
// signature including `ctx`.
|
|
61
|
-
const f = PostsManager.form(Form.make())
|
|
62
|
-
const t = PostsManager.table(Table.make())
|
|
63
|
-
assert.equal(f.getChildren()?.length, 1)
|
|
64
|
-
assert.equal(t.getChildren()?.length, 1)
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('subclass overrides may consume the context to configure the builder', () => {
|
|
68
|
-
let capturedCtx: RelationManagerContext | undefined
|
|
69
|
-
|
|
70
|
-
class PostsManager extends RelationManager {
|
|
71
|
-
static override relationship = 'posts'
|
|
72
|
-
|
|
73
|
-
static override table(table: Table, ctx: RelationManagerContext): Table {
|
|
74
|
-
capturedCtx = ctx
|
|
75
|
-
return table.columns([Column.make('title').sortable()])
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
PostsManager.table(Table.make(), stubCtx)
|
|
80
|
-
assert.equal(capturedCtx?.basePath, '/admin')
|
|
81
|
-
assert.equal(capturedCtx?.parentSlug, 'users')
|
|
82
|
-
assert.equal(capturedCtx?.parentId, '1')
|
|
83
|
-
assert.equal(capturedCtx?.relationship, 'posts')
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it('getRelationship throws when the subclass forgot to set it', () => {
|
|
87
|
-
class Forgot extends RelationManager {}
|
|
88
|
-
assert.throws(
|
|
89
|
-
() => Forgot.getRelationship(),
|
|
90
|
-
/static relationship must be set/,
|
|
91
|
-
)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('getRelationship returns the configured key', () => {
|
|
95
|
-
class M extends RelationManager {
|
|
96
|
-
static override relationship = 'comments'
|
|
97
|
-
}
|
|
98
|
-
assert.equal(M.getRelationship(), 'comments')
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('getLabel falls back to a sentence-cased relationship name', () => {
|
|
102
|
-
class M extends RelationManager {
|
|
103
|
-
static override relationship = 'posts'
|
|
104
|
-
}
|
|
105
|
-
assert.equal(M.getLabel(), 'Posts')
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('getLabel uses the explicit override when set', () => {
|
|
109
|
-
class M extends RelationManager {
|
|
110
|
-
static override relationship = 'lineItems'
|
|
111
|
-
static override label = 'Line items'
|
|
112
|
-
}
|
|
113
|
-
assert.equal(M.getLabel(), 'Line items')
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('getLabelSingular naively strips a trailing s when label not set', () => {
|
|
117
|
-
class Posts extends RelationManager { static override relationship = 'posts' }
|
|
118
|
-
class Categories extends RelationManager { static override relationship = 'categories' }
|
|
119
|
-
|
|
120
|
-
assert.equal(Posts.getLabelSingular(), 'Post')
|
|
121
|
-
// Naive — irregular plurals get the wrong answer; users override.
|
|
122
|
-
assert.equal(Categories.getLabelSingular(), 'Categorie')
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it('getLabelSingular uses the explicit override when set', () => {
|
|
126
|
-
class M extends RelationManager {
|
|
127
|
-
static override relationship = 'children'
|
|
128
|
-
static override labelSingular = 'Child'
|
|
129
|
-
}
|
|
130
|
-
assert.equal(M.getLabelSingular(), 'Child')
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('getIcon and getRecordTitleAttribute return undefined by default', () => {
|
|
134
|
-
class M extends RelationManager { static override relationship = 'posts' }
|
|
135
|
-
assert.equal(M.getIcon(), undefined)
|
|
136
|
-
assert.equal(M.getRecordTitleAttribute(), undefined)
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
it('getIcon and getRecordTitleAttribute pass through configured values', () => {
|
|
140
|
-
class M extends RelationManager {
|
|
141
|
-
static override relationship = 'posts'
|
|
142
|
-
static override icon = 'newspaper'
|
|
143
|
-
static override recordTitleAttribute = 'title'
|
|
144
|
-
}
|
|
145
|
-
assert.equal(M.getIcon(), 'newspaper')
|
|
146
|
-
assert.equal(M.getRecordTitleAttribute(), 'title')
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
describe('authorization predicates', () => {
|
|
150
|
-
it('every can* defaults to true', async () => {
|
|
151
|
-
class M extends RelationManager { static override relationship = 'posts' }
|
|
152
|
-
const parent = { id: 1 }
|
|
153
|
-
const child = { id: 9 }
|
|
154
|
-
|
|
155
|
-
assert.equal(await M.canViewAny(null, parent), true)
|
|
156
|
-
assert.equal(await M.canView(null, child, parent), true)
|
|
157
|
-
assert.equal(await M.canCreate(null, parent), true)
|
|
158
|
-
assert.equal(await M.canEdit(null, child, parent), true)
|
|
159
|
-
assert.equal(await M.canDelete(null, child, parent), true)
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
it('subclass overrides flow through', async () => {
|
|
163
|
-
class M extends RelationManager {
|
|
164
|
-
static override relationship = 'posts'
|
|
165
|
-
static override async canCreate(_user: unknown, parent: unknown): Promise<boolean> {
|
|
166
|
-
return (parent as { canPost?: boolean }).canPost === true
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
assert.equal(await M.canCreate(null, { canPost: true }), true)
|
|
170
|
-
assert.equal(await M.canCreate(null, { canPost: false }), false)
|
|
171
|
-
assert.equal(await M.canCreate(null, {}), false)
|
|
172
|
-
})
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
describe('reserved-token set', () => {
|
|
176
|
-
it('blocks the URL tokens that collide with action / form routes', () => {
|
|
177
|
-
// Sanity: the route table reserves these — managers can't claim them.
|
|
178
|
-
assert.equal(RESERVED_RELATIONSHIP_TOKENS.has('edit'), true)
|
|
179
|
-
assert.equal(RESERVED_RELATIONSHIP_TOKENS.has('delete'), true)
|
|
180
|
-
assert.equal(RESERVED_RELATIONSHIP_TOKENS.has('_form'), true)
|
|
181
|
-
assert.equal(RESERVED_RELATIONSHIP_TOKENS.has('_action'), true)
|
|
182
|
-
assert.equal(RESERVED_RELATIONSHIP_TOKENS.has('_search'), true)
|
|
183
|
-
assert.equal(RESERVED_RELATIONSHIP_TOKENS.has('_uploads'), true)
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
it('allows ordinary relationship names', () => {
|
|
187
|
-
assert.equal(RESERVED_RELATIONSHIP_TOKENS.has('posts'), false)
|
|
188
|
-
assert.equal(RESERVED_RELATIONSHIP_TOKENS.has('comments'), false)
|
|
189
|
-
assert.equal(RESERVED_RELATIONSHIP_TOKENS.has('tags'), false)
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
it('reserves the M2M tokens', () => {
|
|
193
|
-
assert.equal(RESERVED_RELATIONSHIP_TOKENS.has('_attach'), true)
|
|
194
|
-
assert.equal(RESERVED_RELATIONSHIP_TOKENS.has('_detach'), true)
|
|
195
|
-
assert.equal(RESERVED_RELATIONSHIP_TOKENS.has('_bulk-detach'), true)
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
describe('M2M predicates', () => {
|
|
200
|
-
it('canAttach + canDetach default to true', async () => {
|
|
201
|
-
class M extends RelationManager { static override relationship = 'tags' }
|
|
202
|
-
const parent = { id: 1 }
|
|
203
|
-
const child = { id: 9 }
|
|
204
|
-
assert.equal(await M.canAttach(null, parent), true)
|
|
205
|
-
assert.equal(await M.canDetach(null, child, parent), true)
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
it('canAttach override flows through', async () => {
|
|
209
|
-
class M extends RelationManager {
|
|
210
|
-
static override relationship = 'tags'
|
|
211
|
-
static override async canAttach(_user: unknown, parent: unknown): Promise<boolean> {
|
|
212
|
-
return (parent as { canManageTags?: boolean }).canManageTags === true
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
assert.equal(await M.canAttach(null, { canManageTags: true }), true)
|
|
216
|
-
assert.equal(await M.canAttach(null, { canManageTags: false }), false)
|
|
217
|
-
})
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
describe('safeManagerPolicy — M2M short-circuit', () => {
|
|
221
|
-
it('canAttach does NOT fall through to the related Resource', async () => {
|
|
222
|
-
class M extends RelationManager { static override relationship = 'tags' }
|
|
223
|
-
const Related = {
|
|
224
|
-
// If fall-through happened, this would be consulted; we want it ignored.
|
|
225
|
-
canCreate: async () => false,
|
|
226
|
-
}
|
|
227
|
-
// Manager hasn't overridden canAttach (default true). With the
|
|
228
|
-
// managerOnly short-circuit, we should see `true` even though
|
|
229
|
-
// Related.canCreate returns false.
|
|
230
|
-
assert.equal(
|
|
231
|
-
await safeManagerPolicy(M, 'canAttach', Related as never, null, { id: 1 }),
|
|
232
|
-
true,
|
|
233
|
-
)
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
it('canDetach does NOT fall through to the related Resource', async () => {
|
|
237
|
-
class M extends RelationManager { static override relationship = 'tags' }
|
|
238
|
-
const Related = { canDelete: async () => false }
|
|
239
|
-
assert.equal(
|
|
240
|
-
await safeManagerPolicy(M, 'canDetach', Related as never, null, { id: 1 }, { id: 9 }),
|
|
241
|
-
true,
|
|
242
|
-
)
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
it('canAttach manager override is honored over the default-true', async () => {
|
|
246
|
-
class M extends RelationManager {
|
|
247
|
-
static override relationship = 'tags'
|
|
248
|
-
static override async canAttach(): Promise<boolean> { return false }
|
|
249
|
-
}
|
|
250
|
-
assert.equal(
|
|
251
|
-
await safeManagerPolicy(M, 'canAttach', undefined, null, { id: 1 }),
|
|
252
|
-
false,
|
|
253
|
-
)
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
it('throwing canDetach predicate fails closed', async () => {
|
|
257
|
-
class M extends RelationManager {
|
|
258
|
-
static override relationship = 'tags'
|
|
259
|
-
static override async canDetach(): Promise<boolean> { throw new Error('boom') }
|
|
260
|
-
}
|
|
261
|
-
assert.equal(
|
|
262
|
-
await safeManagerPolicy(M, 'canDetach', undefined, null, { id: 1 }, { id: 9 }),
|
|
263
|
-
false,
|
|
264
|
-
)
|
|
265
|
-
})
|
|
266
|
-
})
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
describe('normalizeRelationMode (polymorphic follow-up)', () => {
|
|
270
|
-
it('maps belongsToMany to its own mode', () => {
|
|
271
|
-
assert.equal(normalizeRelationMode('belongsToMany'), 'belongsToMany')
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
it('collapses morphMany and morphOne into morphMany', () => {
|
|
275
|
-
assert.equal(normalizeRelationMode('morphMany'), 'morphMany')
|
|
276
|
-
assert.equal(normalizeRelationMode('morphOne'), 'morphMany')
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
it('maps morphTo to its own mode', () => {
|
|
280
|
-
assert.equal(normalizeRelationMode('morphTo'), 'morphTo')
|
|
281
|
-
})
|
|
282
|
-
|
|
283
|
-
it('maps morphToMany to its own mode (polymorphic M2M owning side)', () => {
|
|
284
|
-
assert.equal(normalizeRelationMode('morphToMany'), 'morphToMany')
|
|
285
|
-
})
|
|
286
|
-
|
|
287
|
-
it('maps morphedByMany to its own mode (polymorphic M2M inverse side)', () => {
|
|
288
|
-
assert.equal(normalizeRelationMode('morphedByMany'), 'morphedByMany')
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
it('falls back to hasMany for hasMany / hasOne / belongsTo / unknown / empty', () => {
|
|
292
|
-
assert.equal(normalizeRelationMode('hasMany'), 'hasMany')
|
|
293
|
-
assert.equal(normalizeRelationMode('hasOne'), 'hasMany')
|
|
294
|
-
assert.equal(normalizeRelationMode('belongsTo'), 'hasMany')
|
|
295
|
-
assert.equal(normalizeRelationMode('unknownType'), 'hasMany')
|
|
296
|
-
assert.equal(normalizeRelationMode(''), 'hasMany')
|
|
297
|
-
})
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
describe('getMorphRelationDescriptor', () => {
|
|
301
|
-
function modelWithRelations(relations: Record<string, unknown>): ModelLike {
|
|
302
|
-
const M: ModelLike = {
|
|
303
|
-
async find() { return null },
|
|
304
|
-
async create() { throw new Error('not used') },
|
|
305
|
-
async update() { throw new Error('not used') },
|
|
306
|
-
async delete() { /* no-op */ },
|
|
307
|
-
query() { throw new Error('not used') },
|
|
308
|
-
}
|
|
309
|
-
Object.assign(M as object, { relations })
|
|
310
|
-
return M
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
it('returns descriptor for morphMany', () => {
|
|
314
|
-
const M = modelWithRelations({
|
|
315
|
-
comments: { type: 'morphMany', model: () => ({} as ModelLike), morphName: 'commentable' },
|
|
316
|
-
})
|
|
317
|
-
const desc = getMorphRelationDescriptor(M, 'comments')
|
|
318
|
-
assert.ok(desc)
|
|
319
|
-
assert.equal(desc.morphName, 'commentable')
|
|
320
|
-
assert.equal(typeof desc.model, 'function')
|
|
321
|
-
})
|
|
322
|
-
|
|
323
|
-
it('returns descriptor for morphOne', () => {
|
|
324
|
-
const M = modelWithRelations({
|
|
325
|
-
avatar: { type: 'morphOne', model: () => ({} as ModelLike), morphName: 'imageable' },
|
|
326
|
-
})
|
|
327
|
-
const desc = getMorphRelationDescriptor(M, 'avatar')
|
|
328
|
-
assert.ok(desc)
|
|
329
|
-
assert.equal(desc.morphName, 'imageable')
|
|
330
|
-
})
|
|
331
|
-
|
|
332
|
-
it('returns undefined for morphTo (no model thunk on the child side)', () => {
|
|
333
|
-
const M = modelWithRelations({
|
|
334
|
-
commentable: { type: 'morphTo', morphName: 'commentable', types: () => [] },
|
|
335
|
-
})
|
|
336
|
-
assert.equal(getMorphRelationDescriptor(M, 'commentable'), undefined)
|
|
337
|
-
})
|
|
338
|
-
|
|
339
|
-
it('returns undefined for non-polymorphic relation types', () => {
|
|
340
|
-
const M = modelWithRelations({
|
|
341
|
-
posts: { type: 'hasMany', model: () => ({} as ModelLike), foreignKey: 'userId' },
|
|
342
|
-
})
|
|
343
|
-
assert.equal(getMorphRelationDescriptor(M, 'posts'), undefined)
|
|
344
|
-
})
|
|
345
|
-
|
|
346
|
-
it('returns undefined when morphName is missing or non-string', () => {
|
|
347
|
-
const M = modelWithRelations({
|
|
348
|
-
bad: { type: 'morphMany', model: () => ({} as ModelLike) },
|
|
349
|
-
})
|
|
350
|
-
assert.equal(getMorphRelationDescriptor(M, 'bad'), undefined)
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
it('honors morphType override in the relation entry', () => {
|
|
354
|
-
const M = modelWithRelations({
|
|
355
|
-
comments: { type: 'morphMany', model: () => ({} as ModelLike), morphName: 'commentable', morphType: 'aliased' },
|
|
356
|
-
})
|
|
357
|
-
const desc = getMorphRelationDescriptor(M, 'comments')!
|
|
358
|
-
assert.equal(desc.morphType, 'aliased')
|
|
359
|
-
})
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
describe('computeMorphPayload', () => {
|
|
363
|
-
function makeRecord(klass: { name?: string; morphAlias?: string; primaryKey?: string }, props: Record<string, unknown>) {
|
|
364
|
-
const rec = { ...props }
|
|
365
|
-
Object.setPrototypeOf(rec, { constructor: klass })
|
|
366
|
-
return rec
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
it('builds {nameId, nameType} from parent.constructor.name and primaryKey', () => {
|
|
370
|
-
const parent = makeRecord({ name: 'Post', primaryKey: 'id' }, { id: 42 })
|
|
371
|
-
const payload = computeMorphPayload(parent, { morphName: 'commentable' })
|
|
372
|
-
assert.deepEqual(payload, { commentableId: 42, commentableType: 'Post' })
|
|
373
|
-
})
|
|
374
|
-
|
|
375
|
-
it('honors parent.constructor.morphAlias over class name', () => {
|
|
376
|
-
const parent = makeRecord({ name: 'BlogPost', morphAlias: 'post', primaryKey: 'id' }, { id: 7 })
|
|
377
|
-
const payload = computeMorphPayload(parent, { morphName: 'commentable' })
|
|
378
|
-
assert.equal(payload['commentableType'], 'post')
|
|
379
|
-
})
|
|
380
|
-
|
|
381
|
-
it('honors descriptor.morphType over the class-level alias', () => {
|
|
382
|
-
const parent = makeRecord({ name: 'Post', morphAlias: 'post', primaryKey: 'id' }, { id: 7 })
|
|
383
|
-
const payload = computeMorphPayload(parent, { morphName: 'commentable', morphType: 'override' })
|
|
384
|
-
assert.equal(payload['commentableType'], 'override')
|
|
385
|
-
})
|
|
386
|
-
|
|
387
|
-
it('uses constructor.primaryKey when set, falls back to id', () => {
|
|
388
|
-
const parent = makeRecord({ name: 'Tag', primaryKey: 'uuid' }, { uuid: 'abc-123' })
|
|
389
|
-
const payload = computeMorphPayload(parent, { morphName: 'taggable' })
|
|
390
|
-
assert.equal(payload['taggableId'], 'abc-123')
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
it('throws when parent primary key is unset', () => {
|
|
394
|
-
const parent = makeRecord({ name: 'Post', primaryKey: 'id' }, {})
|
|
395
|
-
assert.throws(
|
|
396
|
-
() => computeMorphPayload(parent, { morphName: 'commentable' }),
|
|
397
|
-
/parent\.id is unset/,
|
|
398
|
-
)
|
|
399
|
-
})
|
|
400
|
-
})
|