@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
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { describe, it, beforeEach } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
|
|
4
|
-
import { Notification, _resetNotificationIdSeq, serializeForNotification } from './Notification.js'
|
|
5
|
-
import { Action } from '../actions/Action.js'
|
|
6
|
-
|
|
7
|
-
beforeEach(() => _resetNotificationIdSeq())
|
|
8
|
-
|
|
9
|
-
describe('Notification builder', () => {
|
|
10
|
-
it('defaults to type:info', () => {
|
|
11
|
-
const meta = Notification.make('Hello').toMeta()
|
|
12
|
-
assert.equal(meta.type, 'info')
|
|
13
|
-
assert.equal(meta.title, 'Hello')
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
it('title via constructor or .title()', () => {
|
|
17
|
-
assert.equal(Notification.make('A').toMeta().title, 'A')
|
|
18
|
-
assert.equal(Notification.make().title('B').toMeta().title, 'B')
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('type sugar — info / success / warning / error', () => {
|
|
22
|
-
assert.equal(Notification.make().info().toMeta().type, 'info')
|
|
23
|
-
assert.equal(Notification.make().success().toMeta().type, 'success')
|
|
24
|
-
assert.equal(Notification.make().warning().toMeta().type, 'warning')
|
|
25
|
-
assert.equal(Notification.make().error().toMeta().type, 'error')
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('body / icon / duration round-trip', () => {
|
|
29
|
-
const meta = Notification.make('Saved').body('All good.').icon('check-circle-2').duration(2500).toMeta()
|
|
30
|
-
assert.equal(meta.body, 'All good.')
|
|
31
|
-
assert.equal(meta.icon, 'check-circle-2')
|
|
32
|
-
assert.equal(meta.duration, 2500)
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('id is auto-generated when not set', () => {
|
|
36
|
-
const a = Notification.make('A').toMeta()
|
|
37
|
-
const b = Notification.make('B').toMeta()
|
|
38
|
-
assert.notEqual(a.id, b.id)
|
|
39
|
-
assert.match(a.id, /^n-\d+-\d+$/)
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('explicit .id() overrides the auto id', () => {
|
|
43
|
-
const meta = Notification.make('A').id('my-id').toMeta()
|
|
44
|
-
assert.equal(meta.id, 'my-id')
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('omits body / icon / duration when not set', () => {
|
|
48
|
-
const meta = Notification.make('A').toMeta()
|
|
49
|
-
assert.equal(meta.body, undefined)
|
|
50
|
-
assert.equal(meta.icon, undefined)
|
|
51
|
-
assert.equal(meta.duration, undefined)
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
describe('Notification.actions([…])', () => {
|
|
56
|
-
it('emits no `actions` key when the slot is empty', () => {
|
|
57
|
-
const meta = Notification.make('A').toMeta()
|
|
58
|
-
assert.equal(meta.actions, undefined)
|
|
59
|
-
|
|
60
|
-
const data = Notification.make('A').toDatabase()
|
|
61
|
-
assert.equal(data['actions'], undefined)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('serializes a url-mode action through both transports', () => {
|
|
65
|
-
const n = Notification.make('Hi').actions([
|
|
66
|
-
Action.make('view').url('/p/123').color('primary').icon('eye'),
|
|
67
|
-
])
|
|
68
|
-
const meta = n.toMeta().actions
|
|
69
|
-
const data = n.toDatabase()['actions'] as unknown[]
|
|
70
|
-
assert.deepEqual(meta, [{
|
|
71
|
-
name: 'view',
|
|
72
|
-
label: 'View',
|
|
73
|
-
url: '/p/123',
|
|
74
|
-
color: 'primary',
|
|
75
|
-
icon: 'eye',
|
|
76
|
-
}])
|
|
77
|
-
// toDatabase() emits the same shape (no closure leakage).
|
|
78
|
-
assert.deepEqual(data, meta)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('serializes a method-post action with action-url', () => {
|
|
82
|
-
const meta = Notification.make('Hi').actions([
|
|
83
|
-
Action.make('archive').label('Archive').method('post').action('/p/123/archive'),
|
|
84
|
-
]).toMeta().actions
|
|
85
|
-
|
|
86
|
-
assert.deepEqual(meta, [{
|
|
87
|
-
name: 'archive',
|
|
88
|
-
label: 'Archive',
|
|
89
|
-
post: '/p/123/archive',
|
|
90
|
-
}])
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
it('serializes a registry-handler action with payload', () => {
|
|
94
|
-
const meta = Notification.make('Hi').actions([
|
|
95
|
-
Action.make('archive')
|
|
96
|
-
.label('Archive')
|
|
97
|
-
.handler('archive-project')
|
|
98
|
-
.payload({ projectId: 123 })
|
|
99
|
-
.markAsRead(),
|
|
100
|
-
]).toDatabase()['actions'] as unknown[]
|
|
101
|
-
|
|
102
|
-
assert.deepEqual(meta, [{
|
|
103
|
-
name: 'archive',
|
|
104
|
-
label: 'Archive',
|
|
105
|
-
handler: 'archive-project',
|
|
106
|
-
payload: { projectId: 123 },
|
|
107
|
-
markAsRead: true,
|
|
108
|
-
}])
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
it('omits empty payload when the slot is unset', () => {
|
|
112
|
-
const meta = Notification.make('Hi').actions([
|
|
113
|
-
Action.make('archive').handler('archive-project'),
|
|
114
|
-
]).toMeta().actions
|
|
115
|
-
assert.equal(meta?.[0]?.payload, undefined)
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
it('toMeta tolerates closure handlers (transient toast escape hatch)', () => {
|
|
119
|
-
// Closures don't ride the wire — the toaster reads them off
|
|
120
|
-
// Notification.getActions(). The wire shape carries the action
|
|
121
|
-
// name as the dispatch key.
|
|
122
|
-
const meta = Notification.make('Hi').actions([
|
|
123
|
-
Action.make('do').handler(async () => undefined),
|
|
124
|
-
]).toMeta().actions
|
|
125
|
-
assert.equal(meta?.[0]?.handler, 'do')
|
|
126
|
-
assert.equal(meta?.[0]?.url, undefined)
|
|
127
|
-
assert.equal(meta?.[0]?.post, undefined)
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('toDatabase rejects closure handlers with a clear error', () => {
|
|
131
|
-
const n = Notification.make('Hi').actions([
|
|
132
|
-
Action.make('do').handler(async () => undefined),
|
|
133
|
-
])
|
|
134
|
-
assert.throws(() => n.toDatabase(), /closure handler/)
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
it('rejects modal-form actions at config time', () => {
|
|
138
|
-
const n = Notification.make('Hi').actions([
|
|
139
|
-
Action.make('edit').schema([]).handler('x'),
|
|
140
|
-
])
|
|
141
|
-
assert.throws(() => n.toMeta(), /modal-form/)
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
it('rejects submit actions at config time', () => {
|
|
145
|
-
const n = Notification.make('Hi').actions([
|
|
146
|
-
Action.make('save').submit(),
|
|
147
|
-
])
|
|
148
|
-
assert.throws(() => n.toMeta(), /submit button/)
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
it('rejects bulk-placement actions at config time', () => {
|
|
152
|
-
const n = Notification.make('Hi').actions([
|
|
153
|
-
Action.make('bulk').placement('bulk').handler('x'),
|
|
154
|
-
])
|
|
155
|
-
assert.throws(() => n.toMeta(), /bulk-placed/)
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
it('rejects actions with no dispatch target', () => {
|
|
159
|
-
const n = Notification.make('Hi').actions([
|
|
160
|
-
Action.make('orphan'),
|
|
161
|
-
])
|
|
162
|
-
assert.throws(() => n.toMeta(), /no dispatch target/)
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
it('passes through chrome (color / outlined / size / openUrlInNewTab)', () => {
|
|
166
|
-
const meta = serializeForNotification(
|
|
167
|
-
Action.make('view').url('/x').color('destructive').size('lg').openUrlInNewTab(),
|
|
168
|
-
{ transient: true },
|
|
169
|
-
)
|
|
170
|
-
assert.equal(meta.color, 'destructive')
|
|
171
|
-
assert.equal(meta.size, 'lg')
|
|
172
|
-
assert.equal(meta.openUrlInNewTab, true)
|
|
173
|
-
|
|
174
|
-
const outlined = serializeForNotification(
|
|
175
|
-
Action.make('view').url('/x').color('primary').outlined(),
|
|
176
|
-
{ transient: true },
|
|
177
|
-
)
|
|
178
|
-
assert.equal(outlined.outlined, true)
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
it('Action.markAsRead() chain modifier sets the wire flag', () => {
|
|
182
|
-
const meta = serializeForNotification(
|
|
183
|
-
Action.make('view').url('/x').markAsRead(),
|
|
184
|
-
{ transient: true },
|
|
185
|
-
)
|
|
186
|
-
assert.equal(meta.markAsRead, true)
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
it('Action.markAsRead(false) clears the flag', () => {
|
|
190
|
-
const meta = serializeForNotification(
|
|
191
|
-
Action.make('view').url('/x').markAsRead().markAsRead(false),
|
|
192
|
-
{ transient: true },
|
|
193
|
-
)
|
|
194
|
-
assert.equal(meta.markAsRead, undefined)
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
it('Action.handler() switches between closure and string', () => {
|
|
198
|
-
const a = Action.make('a').handler(async () => undefined)
|
|
199
|
-
assert.equal(typeof a.getHandler(), 'function')
|
|
200
|
-
assert.equal(a.getHandlerName(), undefined)
|
|
201
|
-
|
|
202
|
-
a.handler('archive')
|
|
203
|
-
assert.equal(a.getHandler(), undefined)
|
|
204
|
-
assert.equal(a.getHandlerName(), 'archive')
|
|
205
|
-
|
|
206
|
-
a.handler(async () => undefined)
|
|
207
|
-
assert.equal(typeof a.getHandler(), 'function')
|
|
208
|
-
assert.equal(a.getHandlerName(), undefined)
|
|
209
|
-
})
|
|
210
|
-
})
|
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Notification — fluent builder for toast/flash messages emitted from
|
|
3
|
-
* action handlers, form lifecycle hooks, or the route layer. Serializes
|
|
4
|
-
* to a `NotificationMeta` that the client `<Toaster>` consumes.
|
|
5
|
-
*
|
|
6
|
-
* The same builder also doubles as the entry-point for persistent
|
|
7
|
-
* notifications: `notification.sendToDatabase(user)` writes a row on
|
|
8
|
-
* the `notification` table shipped by `@rudderjs/notification`. The
|
|
9
|
-
* panel's bell-icon dropdown reads from that table so the same call
|
|
10
|
-
* site that emits a toast can also drop a row into the user's inbox.
|
|
11
|
-
*/
|
|
12
|
-
import type { Notifiable, NotificationActionMeta } from './types.js'
|
|
13
|
-
import type { Action } from '../actions/Action.js'
|
|
14
|
-
import { persist as persistDatabaseNotification } from './database.js'
|
|
15
|
-
import {
|
|
16
|
-
push as pushBroadcast,
|
|
17
|
-
notificationChannel,
|
|
18
|
-
} from './broadcast.js'
|
|
19
|
-
|
|
20
|
-
export type NotificationType = 'info' | 'success' | 'warning' | 'error'
|
|
21
|
-
|
|
22
|
-
export interface NotificationMeta {
|
|
23
|
-
/** Stable id; used by the client to dedupe and dismiss. Auto-generated
|
|
24
|
-
* when not supplied. */
|
|
25
|
-
id: string
|
|
26
|
-
type: NotificationType
|
|
27
|
-
title: string
|
|
28
|
-
body?: string
|
|
29
|
-
icon?: string
|
|
30
|
-
/** Auto-dismiss duration in ms. `0` keeps the toast until manually
|
|
31
|
-
* dismissed. Default 5000. */
|
|
32
|
-
duration?: number
|
|
33
|
-
/** Filament-style action strip rendered below the body. Same shape
|
|
34
|
-
* on transient toasts and persisted bell rows. Closure-handler
|
|
35
|
-
* actions are valid on toasts but rejected at `sendToDatabase` time
|
|
36
|
-
* (closures don't survive serialization). */
|
|
37
|
-
actions?: NotificationActionMeta[]
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
let _idSeq = 0
|
|
41
|
-
function nextId(): string {
|
|
42
|
-
_idSeq += 1
|
|
43
|
-
return `n-${_idSeq}-${Date.now()}`
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export class Notification {
|
|
47
|
-
protected _id?: string
|
|
48
|
-
protected _type: NotificationType = 'info'
|
|
49
|
-
protected _title = ''
|
|
50
|
-
protected _body?: string
|
|
51
|
-
protected _icon?: string
|
|
52
|
-
protected _duration?: number
|
|
53
|
-
protected _actions: Action[] = []
|
|
54
|
-
|
|
55
|
-
private constructor() {}
|
|
56
|
-
|
|
57
|
-
static make(title?: string): Notification {
|
|
58
|
-
const n = new Notification()
|
|
59
|
-
if (title) n._title = title
|
|
60
|
-
return n
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// ─── Type sugar ───────────────────────────────────────
|
|
64
|
-
|
|
65
|
-
info(): this { this._type = 'info'; return this }
|
|
66
|
-
success(): this { this._type = 'success'; return this }
|
|
67
|
-
warning(): this { this._type = 'warning'; return this }
|
|
68
|
-
error(): this { this._type = 'error'; return this }
|
|
69
|
-
|
|
70
|
-
// ─── Content ──────────────────────────────────────────
|
|
71
|
-
|
|
72
|
-
title(s: string): this { this._title = s; return this }
|
|
73
|
-
body(s: string): this { this._body = s; return this }
|
|
74
|
-
icon(i: string): this { this._icon = i; return this }
|
|
75
|
-
|
|
76
|
-
// ─── Behavior ─────────────────────────────────────────
|
|
77
|
-
|
|
78
|
-
/** Auto-dismiss timeout in ms. `0` means persistent. Default 5000. */
|
|
79
|
-
duration(ms: number): this { this._duration = ms; return this }
|
|
80
|
-
|
|
81
|
-
/** Override the auto-generated id (rarely needed). */
|
|
82
|
-
id(s: string): this { this._id = s; return this }
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Filament-style action strip — buttons rendered below the body on
|
|
86
|
-
* both transient toasts and persisted bell rows.
|
|
87
|
-
*
|
|
88
|
-
* Three serializable dispatch modes:
|
|
89
|
-
* - `Action.make('view').url('/p/123')` — href anchor
|
|
90
|
-
* - `Action.make('post').method('post').action(u)` — form-POST
|
|
91
|
-
* - `Action.make('archive').handler('name').payload({…})`
|
|
92
|
-
* — registered
|
|
93
|
-
* handler
|
|
94
|
-
*
|
|
95
|
-
* Closure handlers (`.handler(async ctx => …)`) work on transient
|
|
96
|
-
* toasts (dispatched against the page's `_action/:name`) but are
|
|
97
|
-
* rejected at `sendToDatabase()` time — closures don't round-trip
|
|
98
|
-
* through the `data` JSON column. Use the named-registry path
|
|
99
|
-
* (`Pilotiq.notificationHandlers({…})`) for persistent rows.
|
|
100
|
-
*
|
|
101
|
-
* Pair with `.markAsRead()` on any action to flip the row's
|
|
102
|
-
* `read_at` when the action fires:
|
|
103
|
-
*
|
|
104
|
-
* Notification.make('New project assigned')
|
|
105
|
-
* .actions([
|
|
106
|
-
* Action.make('view').url('/p/123').markAsRead(),
|
|
107
|
-
* Action.make('archive').handler('archive-project')
|
|
108
|
-
* .payload({ projectId: 123 }).markAsRead(),
|
|
109
|
-
* ])
|
|
110
|
-
* .sendToDatabase(currentUser)
|
|
111
|
-
*/
|
|
112
|
-
actions(actions: Action[]): this {
|
|
113
|
-
this._actions = actions
|
|
114
|
-
return this
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Internal — exposed so the toaster / dispatcher can read the
|
|
118
|
-
* authored Action instances directly when needed (rare). Most
|
|
119
|
-
* consumers should read from `toMeta().actions` instead. */
|
|
120
|
-
getActions(): readonly Action[] { return this._actions }
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Optional click-through URL. When set on a row that's been written
|
|
124
|
-
* to the database via `sendToDatabase()`, the bell-icon dropdown
|
|
125
|
-
* navigates here when the row is clicked (and marks it read in the
|
|
126
|
-
* same step). Ignored on transient toasts.
|
|
127
|
-
*/
|
|
128
|
-
url(href: string): this { this._url = href; return this }
|
|
129
|
-
|
|
130
|
-
protected _url?: string
|
|
131
|
-
|
|
132
|
-
// ─── Serialization ────────────────────────────────────
|
|
133
|
-
|
|
134
|
-
toMeta(): NotificationMeta {
|
|
135
|
-
const actions = this._actions.length > 0
|
|
136
|
-
? this._actions.map(a => serializeForNotification(a, { transient: true }))
|
|
137
|
-
: undefined
|
|
138
|
-
return {
|
|
139
|
-
id: this._id ?? nextId(),
|
|
140
|
-
type: this._type,
|
|
141
|
-
title: this._title,
|
|
142
|
-
...(this._body !== undefined ? { body: this._body } : {}),
|
|
143
|
-
...(this._icon !== undefined ? { icon: this._icon } : {}),
|
|
144
|
-
...(this._duration !== undefined ? { duration: this._duration } : {}),
|
|
145
|
-
...(actions !== undefined ? { actions } : {}),
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Build the JSON payload stored in the `notification.data` column.
|
|
151
|
-
* Mirrors the toast meta but always includes `type` (so the bell
|
|
152
|
-
* dropdown can apply the matching tint chip) and `url` when set.
|
|
153
|
-
*
|
|
154
|
-
* Action strip: closure-handler actions are rejected here — their
|
|
155
|
-
* dispatch target only exists on the page that authored the notify,
|
|
156
|
-
* and the row may outlive that page by days. The named-registry path
|
|
157
|
-
* (`Pilotiq.notificationHandlers({…})`) is the persistence-safe
|
|
158
|
-
* escape hatch.
|
|
159
|
-
*/
|
|
160
|
-
toDatabase(): Record<string, unknown> {
|
|
161
|
-
const data: Record<string, unknown> = {
|
|
162
|
-
type: this._type,
|
|
163
|
-
title: this._title,
|
|
164
|
-
}
|
|
165
|
-
if (this._body !== undefined) data['body'] = this._body
|
|
166
|
-
if (this._icon !== undefined) data['icon'] = this._icon
|
|
167
|
-
if (this._url !== undefined) data['url'] = this._url
|
|
168
|
-
if (this._actions.length > 0) {
|
|
169
|
-
data['actions'] = this._actions.map(a => serializeForNotification(a, { transient: false }))
|
|
170
|
-
}
|
|
171
|
-
return data
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Persist this notification on the `notification` table for
|
|
176
|
-
* `recipient`. The bell-icon dropdown surfaces it on the recipient's
|
|
177
|
-
* next poll (or immediately on broadcast — see Phase 2).
|
|
178
|
-
*
|
|
179
|
-
* Throws when no `@rudderjs/orm` adapter is registered, with a clear
|
|
180
|
-
* message pointing at the providers list.
|
|
181
|
-
*
|
|
182
|
-
* `notifiableType` lets the caller override the column value when an
|
|
183
|
-
* app stores notifications scoped to a non-`'users'` notifiable
|
|
184
|
-
* (teams, projects, etc). Default `'users'` matches the table layout
|
|
185
|
-
* `@rudderjs/notification`'s `DatabaseChannel` writes.
|
|
186
|
-
*
|
|
187
|
-
* await Notification.make('Saved successfully')
|
|
188
|
-
* .body('Changes to the post have been saved.')
|
|
189
|
-
* .success()
|
|
190
|
-
* .sendToDatabase(currentUser)
|
|
191
|
-
*/
|
|
192
|
-
async sendToDatabase(
|
|
193
|
-
recipient: Notifiable,
|
|
194
|
-
opts: { notifiableType?: string; broadcast?: boolean } = {},
|
|
195
|
-
): Promise<{ id: string }> {
|
|
196
|
-
const data = this.toDatabase() as NotificationMeta & Record<string, unknown>
|
|
197
|
-
const result = await persistDatabaseNotification({
|
|
198
|
-
notifiableType: opts.notifiableType ?? 'users',
|
|
199
|
-
notifiableId: String(recipient.id),
|
|
200
|
-
data,
|
|
201
|
-
})
|
|
202
|
-
// Phase 2: push the same payload over WebSocket so the bell client
|
|
203
|
-
// can refetch immediately. Soft-fails when `@rudderjs/broadcast`
|
|
204
|
-
// isn't installed (or when the provider hasn't booted) — apps still
|
|
205
|
-
// get the persisted row via polling.
|
|
206
|
-
if (opts.broadcast) {
|
|
207
|
-
await pushBroadcast({
|
|
208
|
-
recipientId: recipient.id,
|
|
209
|
-
payload: { ...data, id: result.id, createdAt: Date.now() },
|
|
210
|
-
})
|
|
211
|
-
}
|
|
212
|
-
return result
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Push this notification over WebSocket without persisting it. Pairs
|
|
217
|
-
* with `sendToDatabase()` (or runs standalone for ephemeral pushes
|
|
218
|
-
* like "user-X started typing"). Soft-fails when `@rudderjs/broadcast`
|
|
219
|
-
* isn't installed.
|
|
220
|
-
*
|
|
221
|
-
* Channel: `private-pilotiq-notifications.${recipient.id}`. The bell
|
|
222
|
-
* client subscribes to this channel automatically when broadcast is
|
|
223
|
-
* enabled in `Pilotiq.databaseNotifications({ broadcast: true })`.
|
|
224
|
-
*
|
|
225
|
-
* await Notification.make('Live update')
|
|
226
|
-
* .info()
|
|
227
|
-
* .broadcast(currentUser)
|
|
228
|
-
*/
|
|
229
|
-
async broadcast(recipient: Notifiable): Promise<{ ok: boolean }> {
|
|
230
|
-
return pushBroadcast({
|
|
231
|
-
recipientId: recipient.id,
|
|
232
|
-
payload: this.toDatabase(),
|
|
233
|
-
})
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/** Re-export so callers can build channel names without reaching into
|
|
238
|
-
* the internal broadcast module. Useful for apps that want to push
|
|
239
|
-
* custom events from their own code (e.g. presence pings). */
|
|
240
|
-
export { notificationChannel }
|
|
241
|
-
|
|
242
|
-
/** @internal — reset id sequence; tests use this for stability. */
|
|
243
|
-
export function _resetNotificationIdSeq(): void {
|
|
244
|
-
_idSeq = 0
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Walk an `Action` instance and emit the slim `NotificationActionMeta`
|
|
249
|
-
* wire shape — the same format on transient toasts (`toMeta()`) and
|
|
250
|
-
* persisted bell rows (`toDatabase()`).
|
|
251
|
-
*
|
|
252
|
-
* Rejects Action features that don't fit a notification context:
|
|
253
|
-
*
|
|
254
|
-
* - Modal / form-modal actions (`.schema()`, `.modal*()`,
|
|
255
|
-
* `.slideOver()`) — notifications aren't Resource pages, the modal
|
|
256
|
-
* dispatcher's row/page context isn't available.
|
|
257
|
-
* - Submit-button actions (`.submit()` / `.formField()`) — there's no
|
|
258
|
-
* surrounding `<form>` to submit.
|
|
259
|
-
* - Bulk placement — notifications are per-row, no `records[]`.
|
|
260
|
-
* - Visibility callbacks (`.visible(fn)` / `.disabled(fn)`) — need
|
|
261
|
-
* a render context the notification doesn't carry. Use the
|
|
262
|
-
* server-side filter (only emit the action when relevant) instead.
|
|
263
|
-
*
|
|
264
|
-
* `transient: true` (used by `toMeta()`) tolerates closure handlers —
|
|
265
|
-
* they dispatch through the current page's `_action/:name` endpoint.
|
|
266
|
-
* `transient: false` (used by `toDatabase()`) rejects closures with a
|
|
267
|
-
* clear message pointing at the named-registry pattern.
|
|
268
|
-
*/
|
|
269
|
-
export function serializeForNotification(
|
|
270
|
-
action: Action,
|
|
271
|
-
opts: { transient: boolean },
|
|
272
|
-
): NotificationActionMeta {
|
|
273
|
-
// Reject incompatible Action features at config time (works on both
|
|
274
|
-
// transports; the row's wire shape can't carry these even if we
|
|
275
|
-
// wanted it to).
|
|
276
|
-
if (action.hasModal()) {
|
|
277
|
-
throw new Error(
|
|
278
|
-
`[Pilotiq] Notification.actions: action "${action.name}" carries a modal-form ` +
|
|
279
|
-
`(.schema() / .modalHeading() / .slideOver()). Modal-form actions aren't supported ` +
|
|
280
|
-
`inside notifications in v1 — drop the modal or move the workflow to a Resource action.`,
|
|
281
|
-
)
|
|
282
|
-
}
|
|
283
|
-
if (action.isSubmit()) {
|
|
284
|
-
throw new Error(
|
|
285
|
-
`[Pilotiq] Notification.actions: action "${action.name}" is a submit button (.submit()). ` +
|
|
286
|
-
`Notifications have no surrounding <form>; use .url() / .method() / .handler() instead.`,
|
|
287
|
-
)
|
|
288
|
-
}
|
|
289
|
-
if (action.getPlacement() === 'bulk') {
|
|
290
|
-
throw new Error(
|
|
291
|
-
`[Pilotiq] Notification.actions: action "${action.name}" is bulk-placed. ` +
|
|
292
|
-
`Notifications are per-row — bulk placement has no recipient context.`,
|
|
293
|
-
)
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const handlerFn = action.getHandler()
|
|
297
|
-
const handlerName = action.getHandlerName()
|
|
298
|
-
const href = action.getHref()
|
|
299
|
-
const method = action.getMethod()
|
|
300
|
-
const actionUrl = action.getActionUrl()
|
|
301
|
-
|
|
302
|
-
// Closure handler on a persisted row — reject loudly. The caller
|
|
303
|
-
// explicitly asked to `sendToDatabase`; silently dropping the action
|
|
304
|
-
// would surprise.
|
|
305
|
-
if (!opts.transient && handlerFn && !handlerName) {
|
|
306
|
-
throw new Error(
|
|
307
|
-
`[Pilotiq] Notification.actions: action "${action.name}" has a closure handler ` +
|
|
308
|
-
`but is being persisted via .sendToDatabase(). Closures don't survive serialization. ` +
|
|
309
|
-
`Use Pilotiq.notificationHandlers({ '${action.name}': async ctx => … }) and call ` +
|
|
310
|
-
`Action.handler('${action.name}') instead.`,
|
|
311
|
-
)
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const meta: NotificationActionMeta = {
|
|
315
|
-
name: action.name,
|
|
316
|
-
label: action.getLabel(),
|
|
317
|
-
}
|
|
318
|
-
// Dispatch mode — exactly one of url / post / handler. Precedence
|
|
319
|
-
// matches the Action surface: explicit href wins over method-form.
|
|
320
|
-
if (href) {
|
|
321
|
-
meta.url = href
|
|
322
|
-
} else if (method && actionUrl) {
|
|
323
|
-
meta.post = actionUrl
|
|
324
|
-
} else if (handlerName) {
|
|
325
|
-
meta.handler = handlerName
|
|
326
|
-
const payload = action.getPayload()
|
|
327
|
-
if (Object.keys(payload).length > 0) meta.payload = payload
|
|
328
|
-
} else if (handlerFn && opts.transient) {
|
|
329
|
-
// Transient toast with a closure handler — defer dispatch to the
|
|
330
|
-
// current page's `_action/:name`. Wire shape doesn't carry the
|
|
331
|
-
// closure itself; the toaster reads `Notification.getActions()`
|
|
332
|
-
// for closures and falls back to the page-level dispatch URL.
|
|
333
|
-
meta.handler = action.name
|
|
334
|
-
} else {
|
|
335
|
-
throw new Error(
|
|
336
|
-
`[Pilotiq] Notification.actions: action "${action.name}" has no dispatch target. ` +
|
|
337
|
-
`Set one of .url() / .method().action() / .handler(closureOrName).`,
|
|
338
|
-
)
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Chrome — copy through the subset that makes sense in a strip.
|
|
342
|
-
const color = action.getColor()
|
|
343
|
-
const icon = action.getIcon()
|
|
344
|
-
const outlined = action.isOutlined()
|
|
345
|
-
const size = action.getSize()
|
|
346
|
-
if (color !== undefined) meta.color = color
|
|
347
|
-
if (icon !== undefined) meta.icon = icon
|
|
348
|
-
if (outlined) meta.outlined = true
|
|
349
|
-
if (size !== undefined) meta.size = size
|
|
350
|
-
if (action.isOpenUrlInNewTab()) meta.openUrlInNewTab = true
|
|
351
|
-
if (action.isMarkAsReadOnFire()) meta.markAsRead = true
|
|
352
|
-
|
|
353
|
-
return meta
|
|
354
|
-
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Broadcast push helpers — unit tests. The real `@rudderjs/broadcast`
|
|
3
|
-
* package isn't a peer dep here, so we inject a fake module via the
|
|
4
|
-
* `_setTestBroadcast` seam in `broadcast.ts` and assert call shape.
|
|
5
|
-
*/
|
|
6
|
-
import { describe, it, beforeEach, afterEach } from 'node:test'
|
|
7
|
-
import assert from 'node:assert/strict'
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
push,
|
|
11
|
-
notificationChannel,
|
|
12
|
-
NOTIFICATION_CREATED_EVENT,
|
|
13
|
-
_setTestBroadcast,
|
|
14
|
-
} from './broadcast.js'
|
|
15
|
-
import { Notification } from './Notification.js'
|
|
16
|
-
|
|
17
|
-
interface BroadcastCall {
|
|
18
|
-
channel: string
|
|
19
|
-
event: string
|
|
20
|
-
data: unknown
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function makeFakeBroadcast() {
|
|
24
|
-
const calls: BroadcastCall[] = []
|
|
25
|
-
return {
|
|
26
|
-
calls,
|
|
27
|
-
mod: {
|
|
28
|
-
broadcast(channel: string, event: string, data: unknown) {
|
|
29
|
-
calls.push({ channel, event, data })
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
describe('notificationChannel()', () => {
|
|
36
|
-
it('builds the private channel name with the user id', () => {
|
|
37
|
-
assert.equal(notificationChannel(1), 'private-pilotiq-notifications.1')
|
|
38
|
-
assert.equal(notificationChannel('42'), 'private-pilotiq-notifications.42')
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('coerces non-string ids', () => {
|
|
42
|
-
assert.equal(notificationChannel(123n as unknown as number), 'private-pilotiq-notifications.123')
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
describe('push()', () => {
|
|
47
|
-
afterEach(() => _setTestBroadcast(undefined))
|
|
48
|
-
|
|
49
|
-
it('returns ok:false when broadcast module is unavailable', async () => {
|
|
50
|
-
_setTestBroadcast(null)
|
|
51
|
-
const r = await push({ recipientId: 1, payload: { id: 'n1', title: 'X' } })
|
|
52
|
-
assert.equal(r.ok, false)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('calls broadcast() with the default channel + event', async () => {
|
|
56
|
-
const fake = makeFakeBroadcast()
|
|
57
|
-
_setTestBroadcast(fake.mod)
|
|
58
|
-
const r = await push({ recipientId: 1, payload: { id: 'n1', title: 'X' } })
|
|
59
|
-
assert.equal(r.ok, true)
|
|
60
|
-
assert.equal(fake.calls.length, 1)
|
|
61
|
-
assert.equal(fake.calls[0]!.channel, 'private-pilotiq-notifications.1')
|
|
62
|
-
assert.equal(fake.calls[0]!.event, NOTIFICATION_CREATED_EVENT)
|
|
63
|
-
assert.deepEqual(fake.calls[0]!.data, { id: 'n1', title: 'X' })
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('honors channel + event overrides', async () => {
|
|
67
|
-
const fake = makeFakeBroadcast()
|
|
68
|
-
_setTestBroadcast(fake.mod)
|
|
69
|
-
await push({
|
|
70
|
-
recipientId: 1,
|
|
71
|
-
channel: 'public-firehose',
|
|
72
|
-
event: 'custom',
|
|
73
|
-
payload: {},
|
|
74
|
-
})
|
|
75
|
-
assert.equal(fake.calls[0]!.channel, 'public-firehose')
|
|
76
|
-
assert.equal(fake.calls[0]!.event, 'custom')
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('returns ok:false when broadcast() throws', async () => {
|
|
80
|
-
_setTestBroadcast({
|
|
81
|
-
broadcast() { throw new Error('nope') },
|
|
82
|
-
})
|
|
83
|
-
const r = await push({ recipientId: 1, payload: {} })
|
|
84
|
-
assert.equal(r.ok, false)
|
|
85
|
-
})
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
describe('Notification.broadcast(recipient)', () => {
|
|
89
|
-
beforeEach(() => _setTestBroadcast(null))
|
|
90
|
-
afterEach(() => _setTestBroadcast(undefined))
|
|
91
|
-
|
|
92
|
-
it('soft-fails when @rudderjs/broadcast isn\'t installed', async () => {
|
|
93
|
-
const r = await Notification.make('Hi').info().broadcast({ id: 1 })
|
|
94
|
-
assert.equal(r.ok, false)
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
it('pushes the toDatabase() payload on the user\'s private channel', async () => {
|
|
98
|
-
const fake = makeFakeBroadcast()
|
|
99
|
-
_setTestBroadcast(fake.mod)
|
|
100
|
-
await Notification.make('Hi').success().body('Body').url('/x').broadcast({ id: 99 })
|
|
101
|
-
assert.equal(fake.calls.length, 1)
|
|
102
|
-
assert.equal(fake.calls[0]!.channel, 'private-pilotiq-notifications.99')
|
|
103
|
-
assert.equal(fake.calls[0]!.event, NOTIFICATION_CREATED_EVENT)
|
|
104
|
-
const data = fake.calls[0]!.data as Record<string, unknown>
|
|
105
|
-
assert.equal(data['type'], 'success')
|
|
106
|
-
assert.equal(data['title'], 'Hi')
|
|
107
|
-
assert.equal(data['body'], 'Body')
|
|
108
|
-
assert.equal(data['url'], '/x')
|
|
109
|
-
})
|
|
110
|
-
})
|