@pilotiq/pilotiq 0.24.1 → 0.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/boost/guidelines.md +566 -0
- package/boost/skills/pilotiq-fields/SKILL.md +47 -0
- package/boost/skills/pilotiq-fields/rules/field-catalog.md +288 -0
- package/boost/skills/pilotiq-fields/rules/reactive-fields.md +199 -0
- package/boost/skills/pilotiq-fields/rules/validation.md +198 -0
- package/boost/skills/pilotiq-relations/SKILL.md +47 -0
- package/boost/skills/pilotiq-relations/rules/relation-managers.md +256 -0
- package/boost/skills/pilotiq-relations/rules/repeater-relationship.md +177 -0
- package/boost/skills/pilotiq-resource/SKILL.md +61 -0
- package/boost/skills/pilotiq-resource/rules/authorization.md +242 -0
- package/boost/skills/pilotiq-resource/rules/defining-resources.md +228 -0
- package/boost/skills/pilotiq-resource/rules/page-overrides.md +296 -0
- package/package.json +6 -1
- 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,398 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Database-backed notifications — server-side store helpers.
|
|
3
|
-
*
|
|
4
|
-
* Pilotiq doesn't ship a Prisma model; we read/write the
|
|
5
|
-
* `notification` table that `@rudderjs/notification`'s
|
|
6
|
-
* `NotificationProvider` publishes (`prisma/schema/notification.prisma`).
|
|
7
|
-
* Apps that want bell-icon notifications add `NotificationProvider` to
|
|
8
|
-
* their providers list and run `prisma generate`/`db push`; pilotiq
|
|
9
|
-
* then queries the same table via `@rudderjs/orm`'s `ModelRegistry`
|
|
10
|
-
* adapter so the wire shape matches what
|
|
11
|
-
* `Notifier.send(user, new MyNotification())` already writes.
|
|
12
|
-
*
|
|
13
|
-
* `@rudderjs/orm` is a runtime soft-import — pilotiq has no hard
|
|
14
|
-
* dependency on it. When the host app hasn't installed orm (or no
|
|
15
|
-
* adapter is registered), every helper resolves to a clean
|
|
16
|
-
* "store unavailable" sentinel rather than throwing.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import type { NotificationMeta, NotificationType } from './Notification.js'
|
|
20
|
-
import type { NotificationActionMeta } from './types.js'
|
|
21
|
-
|
|
22
|
-
/** Wire shape returned by the list endpoint to the bell client. */
|
|
23
|
-
export interface DatabaseNotificationMeta {
|
|
24
|
-
/** Stable PK from the `notification.id` column. */
|
|
25
|
-
id: string
|
|
26
|
-
/** Unix-ms timestamp the row was created. */
|
|
27
|
-
createdAt: number
|
|
28
|
-
/** ISO `read_at` timestamp; absent when unread. */
|
|
29
|
-
readAt?: string
|
|
30
|
-
/** Display title — read from the `data` JSON `title` key. */
|
|
31
|
-
title: string
|
|
32
|
-
/** Optional supporting text. */
|
|
33
|
-
body?: string
|
|
34
|
-
/** Optional UI tint. Mirrors the transient toast types so the bell can
|
|
35
|
-
* share badge styling with the toaster. */
|
|
36
|
-
type?: NotificationType
|
|
37
|
-
/** Optional icon (registry name). */
|
|
38
|
-
icon?: string
|
|
39
|
-
/** Optional click-through URL. When set, clicking the row navigates
|
|
40
|
-
* there + marks the notification as read. */
|
|
41
|
-
url?: string
|
|
42
|
-
/** Optional action strip rendered below the body. Round-trips
|
|
43
|
-
* verbatim through the `data.actions` JSON column (`Notification`
|
|
44
|
-
* emits the slim wire shape; `rowToMeta` validates each entry and
|
|
45
|
-
* drops malformed ones with a `console.warn` rather than failing
|
|
46
|
-
* the dropdown). */
|
|
47
|
-
actions?: NotificationActionMeta[]
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Raw row shape on the `notification` table — matches the schema
|
|
51
|
-
* shipped by `@rudderjs/notification`. The `data` column is a
|
|
52
|
-
* JSON-encoded string blob. */
|
|
53
|
-
interface NotificationRow {
|
|
54
|
-
id: string
|
|
55
|
-
notifiable_id: string
|
|
56
|
-
notifiable_type: string
|
|
57
|
-
type: string
|
|
58
|
-
data: string | Record<string, unknown>
|
|
59
|
-
read_at: string | null
|
|
60
|
-
created_at: string
|
|
61
|
-
updated_at: string
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Minimal query surface we use against the orm adapter. */
|
|
65
|
-
interface QB {
|
|
66
|
-
where(column: string, value: unknown): QB
|
|
67
|
-
where(column: string, op: string, value: unknown): QB
|
|
68
|
-
orderBy(column: string, dir?: 'ASC' | 'DESC'): QB
|
|
69
|
-
paginate(page: number, perPage?: number): Promise<{ data: unknown[]; total: number }>
|
|
70
|
-
get?(): Promise<unknown[]>
|
|
71
|
-
count?(): Promise<number>
|
|
72
|
-
update?(data: Record<string, unknown>): Promise<unknown>
|
|
73
|
-
updateAll?(data: Record<string, unknown>): Promise<number>
|
|
74
|
-
create?(data: Record<string, unknown>): Promise<unknown>
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** Test-injected adapter override; `null` falls back to dynamic import. */
|
|
78
|
-
let _testAdapter: { query: <T>(table: string) => QB } | null | 'unset' = 'unset'
|
|
79
|
-
|
|
80
|
-
/** Soft-resolved orm adapter. `null` when no orm adapter is registered.
|
|
81
|
-
*
|
|
82
|
-
* Prefers the rudder service container (`app().make('db')`) — the
|
|
83
|
-
* orm-prisma / orm-drizzle providers register the adapter as a
|
|
84
|
-
* globalThis-rooted singleton there, so this resolution path survives
|
|
85
|
-
* the Vite SSR module-cache duplication that breaks shared singletons
|
|
86
|
-
* in raw ES module land. Falls back to `ModelRegistry.get()` for
|
|
87
|
-
* setups that wired the adapter directly.
|
|
88
|
-
*
|
|
89
|
-
* Both modules are imported indirectly so TypeScript doesn't try to
|
|
90
|
-
* resolve them at type-check time — pilotiq doesn't peer-depend on
|
|
91
|
-
* `@rudderjs/orm` or `@rudderjs/core` (the bell-icon is opt-in).
|
|
92
|
-
*/
|
|
93
|
-
async function adapter(): Promise<{ query: <T>(table: string) => QB } | null> {
|
|
94
|
-
if (_testAdapter !== 'unset') return _testAdapter
|
|
95
|
-
const fromContainer = await resolveFromContainer()
|
|
96
|
-
if (fromContainer) return fromContainer
|
|
97
|
-
return resolveFromOrmModule()
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async function resolveFromContainer(): Promise<{ query: <T>(table: string) => QB } | null> {
|
|
101
|
-
const moduleName = '@rudderjs/core'
|
|
102
|
-
try {
|
|
103
|
-
const mod = await import(/* @vite-ignore */ moduleName) as {
|
|
104
|
-
app?: () => { make(key: string): unknown }
|
|
105
|
-
}
|
|
106
|
-
if (!mod.app) return null
|
|
107
|
-
const adapter = mod.app().make('db')
|
|
108
|
-
if (!adapter || typeof adapter !== 'object') return null
|
|
109
|
-
if (typeof (adapter as { query?: unknown }).query !== 'function') return null
|
|
110
|
-
return adapter as { query: <T>(table: string) => QB }
|
|
111
|
-
} catch {
|
|
112
|
-
return null
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async function resolveFromOrmModule(): Promise<{ query: <T>(table: string) => QB } | null> {
|
|
117
|
-
const moduleName = '@rudderjs/orm'
|
|
118
|
-
try {
|
|
119
|
-
const mod = await import(/* @vite-ignore */ moduleName) as {
|
|
120
|
-
ModelRegistry?: { get(): { query: <T>(table: string) => QB } | null }
|
|
121
|
-
}
|
|
122
|
-
return mod.ModelRegistry?.get() ?? null
|
|
123
|
-
} catch {
|
|
124
|
-
return null
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** Notification-table row JSON-decoded `data` payload. We always store
|
|
129
|
-
* pilotiq's `NotificationMeta` shape; rows written by
|
|
130
|
-
* `@rudderjs/notification.Notifier.send(...)` may carry arbitrary
|
|
131
|
-
* application keys — we surface what we can recognize and ignore the rest. */
|
|
132
|
-
function parseRowData(raw: string | Record<string, unknown>): Record<string, unknown> {
|
|
133
|
-
if (typeof raw !== 'string') return raw
|
|
134
|
-
try { return JSON.parse(raw) as Record<string, unknown> }
|
|
135
|
-
catch { return {} }
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function rowToMeta(row: NotificationRow): DatabaseNotificationMeta {
|
|
139
|
-
const data = parseRowData(row.data)
|
|
140
|
-
const meta: DatabaseNotificationMeta = {
|
|
141
|
-
id: row.id,
|
|
142
|
-
createdAt: Date.parse(row.created_at) || Date.now(),
|
|
143
|
-
title: typeof data['title'] === 'string' ? (data['title'] as string) : '',
|
|
144
|
-
}
|
|
145
|
-
if (row.read_at) meta.readAt = row.read_at
|
|
146
|
-
if (typeof data['body'] === 'string') meta.body = data['body'] as string
|
|
147
|
-
if (typeof data['icon'] === 'string') meta.icon = data['icon'] as string
|
|
148
|
-
if (typeof data['url'] === 'string') meta.url = data['url'] as string
|
|
149
|
-
if (
|
|
150
|
-
data['type'] === 'info' || data['type'] === 'success' ||
|
|
151
|
-
data['type'] === 'warning' || data['type'] === 'error'
|
|
152
|
-
) {
|
|
153
|
-
meta.type = data['type']
|
|
154
|
-
}
|
|
155
|
-
const actions = parseStoredActions(data['actions'])
|
|
156
|
-
if (actions.length > 0) meta.actions = actions
|
|
157
|
-
return meta
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Validate the stored action array shape — the `data.actions` column
|
|
162
|
-
* is end-user controllable through the `Notification` builder, so we
|
|
163
|
-
* defend in depth: malformed entries are dropped with a warning rather
|
|
164
|
-
* than crashing the bell dropdown render.
|
|
165
|
-
*
|
|
166
|
-
* Required: `name: string`, `label: string`, exactly one of `url` /
|
|
167
|
-
* `post` / `handler` set to a string.
|
|
168
|
-
*/
|
|
169
|
-
function parseStoredActions(raw: unknown): NotificationActionMeta[] {
|
|
170
|
-
if (!Array.isArray(raw)) return []
|
|
171
|
-
const out: NotificationActionMeta[] = []
|
|
172
|
-
for (const entry of raw) {
|
|
173
|
-
if (!entry || typeof entry !== 'object') continue
|
|
174
|
-
const e = entry as Record<string, unknown>
|
|
175
|
-
if (typeof e['name'] !== 'string' || !e['name']) { warnDrop(e, 'missing name'); continue }
|
|
176
|
-
if (typeof e['label'] !== 'string' || !e['label']) { warnDrop(e, 'missing label'); continue }
|
|
177
|
-
const url = typeof e['url'] === 'string' ? e['url'] as string : undefined
|
|
178
|
-
const post = typeof e['post'] === 'string' ? e['post'] as string : undefined
|
|
179
|
-
const handler = typeof e['handler'] === 'string' ? e['handler'] as string : undefined
|
|
180
|
-
const dispatchModes = [url, post, handler].filter(v => v !== undefined).length
|
|
181
|
-
if (dispatchModes !== 1) { warnDrop(e, 'must set exactly one of url/post/handler'); continue }
|
|
182
|
-
|
|
183
|
-
const action: NotificationActionMeta = { name: e['name'] as string, label: e['label'] as string }
|
|
184
|
-
if (url !== undefined) action.url = url
|
|
185
|
-
if (post !== undefined) action.post = post
|
|
186
|
-
if (handler !== undefined) action.handler = handler
|
|
187
|
-
if (e['payload'] && typeof e['payload'] === 'object' && !Array.isArray(e['payload'])) {
|
|
188
|
-
action.payload = e['payload'] as Record<string, unknown>
|
|
189
|
-
}
|
|
190
|
-
if (typeof e['color'] === 'string') action.color = e['color'] as never
|
|
191
|
-
if (typeof e['icon'] === 'string') action.icon = e['icon'] as string
|
|
192
|
-
if (e['outlined'] === true) action.outlined = true
|
|
193
|
-
if (typeof e['size'] === 'string') action.size = e['size'] as never
|
|
194
|
-
if (e['openUrlInNewTab'] === true) action.openUrlInNewTab = true
|
|
195
|
-
if (e['markAsRead'] === true) action.markAsRead = true
|
|
196
|
-
out.push(action)
|
|
197
|
-
}
|
|
198
|
-
return out
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function warnDrop(entry: Record<string, unknown>, reason: string): void {
|
|
202
|
-
// Only warn in non-production — keep the bell quiet on prod where
|
|
203
|
-
// a dropped row is preferable to console spam.
|
|
204
|
-
if (typeof process !== 'undefined' && process.env?.['NODE_ENV'] === 'production') return
|
|
205
|
-
|
|
206
|
-
console.warn(
|
|
207
|
-
`[Pilotiq] notification action dropped: ${reason}. Entry:`,
|
|
208
|
-
entry,
|
|
209
|
-
)
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export interface ListOptions {
|
|
213
|
-
/** `notifiable_type` column value. */
|
|
214
|
-
notifiableType: string
|
|
215
|
-
/** `notifiable_id` column value (typically the user's id). */
|
|
216
|
-
notifiableId: string
|
|
217
|
-
/** Hard cap on rows returned. Default 25. */
|
|
218
|
-
limit?: number
|
|
219
|
-
/** When true, only rows with `read_at IS NULL` are returned. */
|
|
220
|
-
unreadOnly?: boolean
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export interface ListResult {
|
|
224
|
-
notifications: DatabaseNotificationMeta[]
|
|
225
|
-
unreadCount: number
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Fetch the latest notifications for a notifiable + the unread count.
|
|
230
|
-
* Returns `{ notifications: [], unreadCount: 0 }` when no orm adapter is
|
|
231
|
-
* registered — apps without a database stay quiet rather than 500.
|
|
232
|
-
*/
|
|
233
|
-
export async function listForUser(opts: ListOptions): Promise<ListResult> {
|
|
234
|
-
const adp = await adapter()
|
|
235
|
-
if (!adp) return { notifications: [], unreadCount: 0 }
|
|
236
|
-
|
|
237
|
-
const limit = opts.limit ?? 25
|
|
238
|
-
let q: QB = adp.query<NotificationRow>('notification')
|
|
239
|
-
.where('notifiable_type', opts.notifiableType)
|
|
240
|
-
.where('notifiable_id', opts.notifiableId)
|
|
241
|
-
if (opts.unreadOnly) q = q.where('read_at', null)
|
|
242
|
-
q = q.orderBy('created_at', 'DESC')
|
|
243
|
-
|
|
244
|
-
const [page, count] = await Promise.all([
|
|
245
|
-
q.paginate(1, limit),
|
|
246
|
-
unreadCount({ notifiableType: opts.notifiableType, notifiableId: opts.notifiableId }),
|
|
247
|
-
])
|
|
248
|
-
const rows = (page.data as NotificationRow[]).map(rowToMeta)
|
|
249
|
-
return { notifications: rows, unreadCount: count }
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Fetch a single row by id, scoped to the notifiable. Returns
|
|
254
|
-
* `undefined` when no row matches (already-deleted / wrong owner /
|
|
255
|
-
* store unavailable). Used by the notification action route to look
|
|
256
|
-
* up the stored `data.actions` array before dispatching.
|
|
257
|
-
*/
|
|
258
|
-
export async function findOneForUser(
|
|
259
|
-
id: string,
|
|
260
|
-
opts: { notifiableType: string; notifiableId: string },
|
|
261
|
-
): Promise<DatabaseNotificationMeta | undefined> {
|
|
262
|
-
const adp = await adapter()
|
|
263
|
-
if (!adp) return undefined
|
|
264
|
-
const q = adp.query<NotificationRow>('notification')
|
|
265
|
-
.where('notifiable_type', opts.notifiableType)
|
|
266
|
-
.where('notifiable_id', opts.notifiableId)
|
|
267
|
-
.where('id', id)
|
|
268
|
-
const page = await q.paginate(1, 1)
|
|
269
|
-
const row = (page.data as NotificationRow[])[0]
|
|
270
|
-
if (!row) return undefined
|
|
271
|
-
return rowToMeta(row)
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/** Count rows with `read_at IS NULL` for the notifiable. */
|
|
275
|
-
export async function unreadCount(opts: {
|
|
276
|
-
notifiableType: string
|
|
277
|
-
notifiableId: string
|
|
278
|
-
}): Promise<number> {
|
|
279
|
-
const adp = await adapter()
|
|
280
|
-
if (!adp) return 0
|
|
281
|
-
const q = adp.query<NotificationRow>('notification')
|
|
282
|
-
.where('notifiable_type', opts.notifiableType)
|
|
283
|
-
.where('notifiable_id', opts.notifiableId)
|
|
284
|
-
.where('read_at', null)
|
|
285
|
-
// Cheap path when the adapter implements `count()`; fall back to
|
|
286
|
-
// paginate(1,1).total which both Prisma and Drizzle adapters support.
|
|
287
|
-
if (q.count) return q.count()
|
|
288
|
-
const page = await q.paginate(1, 1)
|
|
289
|
-
return page.total
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Mark a single notification row as read. Always scoped to
|
|
294
|
-
* `notifiableId` so a tampered POST can't mark another user's row.
|
|
295
|
-
*
|
|
296
|
-
* Returns `true` when a row was updated, `false` when no row matched
|
|
297
|
-
* (already-deleted / wrong owner / store unavailable).
|
|
298
|
-
*/
|
|
299
|
-
export async function markAsRead(
|
|
300
|
-
id: string,
|
|
301
|
-
opts: { notifiableType: string; notifiableId: string },
|
|
302
|
-
): Promise<boolean> {
|
|
303
|
-
const adp = await adapter()
|
|
304
|
-
if (!adp) return false
|
|
305
|
-
const q = adp.query<NotificationRow>('notification')
|
|
306
|
-
.where('notifiable_type', opts.notifiableType)
|
|
307
|
-
.where('notifiable_id', opts.notifiableId)
|
|
308
|
-
.where('id', id)
|
|
309
|
-
if (!q.updateAll) return false
|
|
310
|
-
const n = await q.updateAll({ read_at: new Date().toISOString() })
|
|
311
|
-
return n > 0
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/** Mark a row unread again — used by the bell's "mark unread" affordance. */
|
|
315
|
-
export async function markAsUnread(
|
|
316
|
-
id: string,
|
|
317
|
-
opts: { notifiableType: string; notifiableId: string },
|
|
318
|
-
): Promise<boolean> {
|
|
319
|
-
const adp = await adapter()
|
|
320
|
-
if (!adp) return false
|
|
321
|
-
const q = adp.query<NotificationRow>('notification')
|
|
322
|
-
.where('notifiable_type', opts.notifiableType)
|
|
323
|
-
.where('notifiable_id', opts.notifiableId)
|
|
324
|
-
.where('id', id)
|
|
325
|
-
if (!q.updateAll) return false
|
|
326
|
-
const n = await q.updateAll({ read_at: null })
|
|
327
|
-
return n > 0
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/** Mark every unread notification for the notifiable as read. */
|
|
331
|
-
export async function markAllAsRead(opts: {
|
|
332
|
-
notifiableType: string
|
|
333
|
-
notifiableId: string
|
|
334
|
-
}): Promise<number> {
|
|
335
|
-
const adp = await adapter()
|
|
336
|
-
if (!adp) return 0
|
|
337
|
-
const q = adp.query<NotificationRow>('notification')
|
|
338
|
-
.where('notifiable_type', opts.notifiableType)
|
|
339
|
-
.where('notifiable_id', opts.notifiableId)
|
|
340
|
-
.where('read_at', null)
|
|
341
|
-
if (!q.updateAll) return 0
|
|
342
|
-
return q.updateAll({ read_at: new Date().toISOString() })
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Insert a notification row directly. Powers
|
|
347
|
-
* `Notification.make(...).sendToDatabase(recipient)`. Throws when the
|
|
348
|
-
* orm adapter isn't registered — the caller has explicitly asked to
|
|
349
|
-
* persist, so silent no-op would hide a real config error.
|
|
350
|
-
*/
|
|
351
|
-
export async function persist(opts: {
|
|
352
|
-
notifiableType: string
|
|
353
|
-
notifiableId: string
|
|
354
|
-
type?: string
|
|
355
|
-
data: NotificationMeta & Record<string, unknown>
|
|
356
|
-
}): Promise<{ id: string }> {
|
|
357
|
-
const adp = await adapter()
|
|
358
|
-
if (!adp) {
|
|
359
|
-
throw new Error(
|
|
360
|
-
'[Pilotiq] sendToDatabase() called but no @rudderjs/orm adapter is registered. ' +
|
|
361
|
-
'Add a database provider (orm-prisma / orm-drizzle) to your providers list, ' +
|
|
362
|
-
"or call .sendToDatabase() only when the orm is wired up.",
|
|
363
|
-
)
|
|
364
|
-
}
|
|
365
|
-
const q = adp.query<NotificationRow>('notification')
|
|
366
|
-
if (!q.create) {
|
|
367
|
-
throw new Error('[Pilotiq] orm adapter does not support `query.create()`.')
|
|
368
|
-
}
|
|
369
|
-
const id = `pn_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`
|
|
370
|
-
const now = new Date().toISOString()
|
|
371
|
-
await q.create({
|
|
372
|
-
id,
|
|
373
|
-
notifiable_id: opts.notifiableId,
|
|
374
|
-
notifiable_type: opts.notifiableType,
|
|
375
|
-
type: opts.type ?? 'PilotiqNotification',
|
|
376
|
-
data: JSON.stringify(opts.data),
|
|
377
|
-
read_at: null,
|
|
378
|
-
created_at: now,
|
|
379
|
-
updated_at: now,
|
|
380
|
-
})
|
|
381
|
-
return { id }
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/** @internal — test seam. Inject a fake adapter (or `null` to mimic
|
|
385
|
-
* "store unavailable"). Pass `undefined` to clear and fall back to the
|
|
386
|
-
* dynamic `@rudderjs/orm` import. Tests should clear in `afterEach`. */
|
|
387
|
-
export function _setTestAdapter(
|
|
388
|
-
adp: { query: <T>(table: string) => QB } | null | undefined,
|
|
389
|
-
): void {
|
|
390
|
-
_testAdapter = adp === undefined ? 'unset' : adp
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/** @internal — exposed for tests; lets a unit test inject a fake
|
|
394
|
-
* ModelRegistry without monkey-patching dynamic-import. */
|
|
395
|
-
export const _internal = {
|
|
396
|
-
parseRowData,
|
|
397
|
-
rowToMeta,
|
|
398
|
-
}
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builder + panelInfo() integration tests for
|
|
3
|
-
* `Pilotiq.databaseNotifications()`. The transport layer (the
|
|
4
|
-
* `_notifications` route family) is exercised via the storage tests +
|
|
5
|
-
* `database.test.ts`; here we lock down the wire shape consumers
|
|
6
|
-
* actually depend on.
|
|
7
|
-
*/
|
|
8
|
-
import { describe, it } from 'node:test'
|
|
9
|
-
import assert from 'node:assert/strict'
|
|
10
|
-
|
|
11
|
-
import { Pilotiq } from '../Pilotiq.js'
|
|
12
|
-
import { panelInfo } from '../pageData.js'
|
|
13
|
-
|
|
14
|
-
describe('Pilotiq.databaseNotifications() — builder', () => {
|
|
15
|
-
it('opt-out by default — cfg.databaseNotifications is undefined', () => {
|
|
16
|
-
const cfg = Pilotiq.make('admin').getConfig()
|
|
17
|
-
assert.equal(cfg.databaseNotifications, undefined)
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('toggle without options enables with defaults', () => {
|
|
21
|
-
const cfg = Pilotiq.make('admin').databaseNotifications().getConfig()
|
|
22
|
-
assert.equal(cfg.databaseNotifications?.enabled, true)
|
|
23
|
-
assert.equal(cfg.databaseNotifications?.position, undefined)
|
|
24
|
-
assert.equal(cfg.databaseNotifications?.polling, undefined)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it('options round-trip', () => {
|
|
28
|
-
const cfg = Pilotiq.make('admin').databaseNotifications({
|
|
29
|
-
position: 'sidebar',
|
|
30
|
-
polling: 10,
|
|
31
|
-
pageSize: 50,
|
|
32
|
-
badgeColor: 'success',
|
|
33
|
-
trigger: { icon: 'bell', label: 'Inbox' },
|
|
34
|
-
}).getConfig()
|
|
35
|
-
assert.equal(cfg.databaseNotifications?.position, 'sidebar')
|
|
36
|
-
assert.equal(cfg.databaseNotifications?.polling, 10)
|
|
37
|
-
assert.equal(cfg.databaseNotifications?.pageSize, 50)
|
|
38
|
-
assert.equal(cfg.databaseNotifications?.badgeColor, 'success')
|
|
39
|
-
assert.equal(cfg.databaseNotifications?.trigger?.icon, 'bell')
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('null polling round-trips (disable auto-poll)', () => {
|
|
43
|
-
const cfg = Pilotiq.make('admin').databaseNotifications({ polling: null }).getConfig()
|
|
44
|
-
assert.equal(cfg.databaseNotifications?.polling, null)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('databaseNotificationsPolling sugar updates the slot', () => {
|
|
48
|
-
const cfg = Pilotiq.make('admin')
|
|
49
|
-
.databaseNotifications()
|
|
50
|
-
.databaseNotificationsPolling(120)
|
|
51
|
-
.getConfig()
|
|
52
|
-
assert.equal(cfg.databaseNotifications?.polling, 120)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('databaseNotificationsPolling is a no-op when not enabled', () => {
|
|
56
|
-
const cfg = Pilotiq.make('admin').databaseNotificationsPolling(5).getConfig()
|
|
57
|
-
assert.equal(cfg.databaseNotifications, undefined)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('databaseNotificationsPosition sugar updates the slot', () => {
|
|
61
|
-
const cfg = Pilotiq.make('admin')
|
|
62
|
-
.databaseNotifications()
|
|
63
|
-
.databaseNotificationsPosition('sidebar')
|
|
64
|
-
.getConfig()
|
|
65
|
-
assert.equal(cfg.databaseNotifications?.position, 'sidebar')
|
|
66
|
-
})
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
describe('panelInfo() — databaseNotifications meta', () => {
|
|
70
|
-
it('absent when never opted in', async () => {
|
|
71
|
-
const panel = await panelInfo(Pilotiq.make('admin'))
|
|
72
|
-
assert.equal((panel as Record<string, unknown>)['databaseNotifications'], undefined)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('absent when no user resolves', async () => {
|
|
76
|
-
const p = Pilotiq.make('admin').databaseNotifications()
|
|
77
|
-
const panel = await panelInfo(p)
|
|
78
|
-
assert.equal((panel as Record<string, unknown>)['databaseNotifications'], undefined)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('present with resolver + defaults', async () => {
|
|
82
|
-
const p = Pilotiq.make('admin')
|
|
83
|
-
.user(() => ({ id: 1, name: 'Sue' }))
|
|
84
|
-
.databaseNotifications()
|
|
85
|
-
const panel = await panelInfo(p)
|
|
86
|
-
const dn = (panel as any).databaseNotifications
|
|
87
|
-
assert.ok(dn, 'databaseNotifications is present')
|
|
88
|
-
assert.equal(dn.position, 'topbar')
|
|
89
|
-
assert.equal(dn.polling, 30)
|
|
90
|
-
assert.equal(dn.pageSize, 25)
|
|
91
|
-
assert.equal(dn.badgeColor, 'primary')
|
|
92
|
-
assert.equal(dn.listUrl, '/admin/_notifications')
|
|
93
|
-
assert.equal(dn.readAllUrl, '/admin/_notifications/read-all')
|
|
94
|
-
assert.equal(dn.readUrl, '/admin/_notifications/:id/read')
|
|
95
|
-
assert.equal(dn.unreadUrl, '/admin/_notifications/:id/unread')
|
|
96
|
-
assert.equal(dn.actionUrl, '/admin/_notifications/:id/_action/:actionName')
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it('honors custom path for URL building', async () => {
|
|
100
|
-
const p = Pilotiq.make('admin')
|
|
101
|
-
.path('/dashboard')
|
|
102
|
-
.user(() => ({ id: 1 }))
|
|
103
|
-
.databaseNotifications()
|
|
104
|
-
const panel = await panelInfo(p)
|
|
105
|
-
const dn = (panel as any).databaseNotifications
|
|
106
|
-
assert.equal(dn.listUrl, '/dashboard/_notifications')
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
it('null polling round-trips to the wire', async () => {
|
|
110
|
-
const p = Pilotiq.make('admin')
|
|
111
|
-
.user(() => ({ id: 1 }))
|
|
112
|
-
.databaseNotifications({ polling: null })
|
|
113
|
-
const panel = await panelInfo(p)
|
|
114
|
-
const dn = (panel as any).databaseNotifications
|
|
115
|
-
assert.equal(dn.polling, null)
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
it('per-call options override defaults', async () => {
|
|
119
|
-
const p = Pilotiq.make('admin')
|
|
120
|
-
.user(() => ({ id: 1 }))
|
|
121
|
-
.databaseNotifications({
|
|
122
|
-
position: 'sidebar',
|
|
123
|
-
polling: 5,
|
|
124
|
-
pageSize: 10,
|
|
125
|
-
badgeColor: 'warning',
|
|
126
|
-
})
|
|
127
|
-
const panel = await panelInfo(p)
|
|
128
|
-
const dn = (panel as any).databaseNotifications
|
|
129
|
-
assert.equal(dn.position, 'sidebar')
|
|
130
|
-
assert.equal(dn.polling, 5)
|
|
131
|
-
assert.equal(dn.pageSize, 10)
|
|
132
|
-
assert.equal(dn.badgeColor, 'warning')
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('trigger overrides ride through to the wire', async () => {
|
|
136
|
-
const p = Pilotiq.make('admin')
|
|
137
|
-
.user(() => ({ id: 1 }))
|
|
138
|
-
.databaseNotifications({ trigger: { icon: 'bell', label: 'Inbox' } })
|
|
139
|
-
const panel = await panelInfo(p)
|
|
140
|
-
const dn = (panel as any).databaseNotifications
|
|
141
|
-
assert.deepEqual(dn.trigger, { icon: 'bell', label: 'Inbox' })
|
|
142
|
-
})
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
describe('panelInfo() — broadcast (Phase 2)', () => {
|
|
146
|
-
it('absent when broadcast wasn\'t enabled', async () => {
|
|
147
|
-
const p = Pilotiq.make('admin')
|
|
148
|
-
.user(() => ({ id: 1 }))
|
|
149
|
-
.databaseNotifications()
|
|
150
|
-
const panel = await panelInfo(p)
|
|
151
|
-
const dn = (panel as any).databaseNotifications
|
|
152
|
-
assert.equal(dn.broadcast, undefined)
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
it('present when databaseNotificationsBroadcast() is called', async () => {
|
|
156
|
-
const p = Pilotiq.make('admin')
|
|
157
|
-
.user(() => ({ id: 42 }))
|
|
158
|
-
.databaseNotifications()
|
|
159
|
-
.databaseNotificationsBroadcast()
|
|
160
|
-
const panel = await panelInfo(p)
|
|
161
|
-
const dn = (panel as any).databaseNotifications
|
|
162
|
-
assert.ok(dn.broadcast, 'broadcast meta is present')
|
|
163
|
-
assert.equal(dn.broadcast.channel, 'private-pilotiq-notifications.42')
|
|
164
|
-
assert.equal(dn.broadcast.event, 'notification.created')
|
|
165
|
-
assert.equal(dn.broadcast.wsUrl, '') // empty -> client falls back to same-origin
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
it('honors a custom wsUrl', async () => {
|
|
169
|
-
const p = Pilotiq.make('admin')
|
|
170
|
-
.user(() => ({ id: 'u-1' }))
|
|
171
|
-
.databaseNotifications({ broadcast: { wsUrl: 'wss://x.test/ws' } })
|
|
172
|
-
const panel = await panelInfo(p)
|
|
173
|
-
const dn = (panel as any).databaseNotifications
|
|
174
|
-
assert.equal(dn.broadcast.wsUrl, 'wss://x.test/ws')
|
|
175
|
-
assert.equal(dn.broadcast.channel, 'private-pilotiq-notifications.u-1')
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
it('absent when user has no id (channel cannot be scoped)', async () => {
|
|
179
|
-
const p = Pilotiq.make('admin')
|
|
180
|
-
.user(() => ({ name: 'Anonymous' }) as unknown as { id: string })
|
|
181
|
-
.databaseNotifications()
|
|
182
|
-
.databaseNotificationsBroadcast()
|
|
183
|
-
const panel = await panelInfo(p)
|
|
184
|
-
const dn = (panel as any).databaseNotifications
|
|
185
|
-
assert.equal(dn?.broadcast, undefined)
|
|
186
|
-
})
|
|
187
|
-
})
|