@conduction/nextcloud-vue 2.56.0-beta.4 → 2.56.0-beta.5
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/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +12 -2
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +1 -2
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +0 -3
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -2
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +12 -5
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +2 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +9 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +6 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +116 -15
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +27 -14
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js +13 -6
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js.map +1 -1
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js +2 -0
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +1 -2
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +3 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +17 -17
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js +4 -21
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +0 -13
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js +8 -8
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js +3 -2
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +67 -13
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +15 -8
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/manifestActionDispatch.js +31 -11
- package/dist/esm/components/CnIndexPage/manifestActionDispatch.js.map +1 -1
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue2.js.map +1 -1
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js +1 -2
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +0 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js +2 -17
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js +35 -11
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js +3 -2
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +23 -5
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectList/CnObjectList.vue2.js +19 -3
- package/dist/esm/components/CnObjectList/CnObjectList.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectList/CnObjectList.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +45 -15
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +24 -6
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +0 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +4 -2
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +7 -2
- package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
- package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js +5 -3
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +3 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +1 -0
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js +6 -0
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js +68 -53
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js +17 -0
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js +21 -19
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js +3 -6
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js +4 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js +6 -3
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js.map +1 -1
- package/dist/esm/l10n/en.json.js +1 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +1 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +71 -7
- package/dist/esm/utils/actionLink.js +5 -0
- package/dist/esm/utils/actionLink.js.map +1 -1
- package/dist/esm/utils/actionsDispatcher.js +16 -1
- package/dist/esm/utils/actionsDispatcher.js.map +1 -1
- package/dist/esm/utils/linkNavigation.js +6 -3
- package/dist/esm/utils/linkNavigation.js.map +1 -1
- package/dist/esm/utils/rowAuxClick.js +30 -5
- package/dist/esm/utils/rowAuxClick.js.map +1 -1
- package/dist/esm/utils/safeHref.js +18 -3
- package/dist/esm/utils/safeHref.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +809 -383
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +71 -7
- package/l10n/en.json +1 -0
- package/l10n/nl.json +1 -0
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
- package/src/components/CnActionsMenu/CnActionsMenu.vue +0 -3
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
- package/src/components/CnBoardView/CnBoardView.vue +13 -5
- package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
- package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
- package/src/components/CnDataTable/CnDataTable.md +1 -1
- package/src/components/CnDataTable/CnDataTable.vue +131 -17
- package/src/components/CnDateAxisView/CnDateAxisView.vue +15 -6
- package/src/components/CnDetailPage/CnDetailPage.md +1 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +29 -1
- package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -2
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +5 -3
- package/src/components/CnFilesWidget/CnFilesWidget.vue +4 -21
- package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +8 -21
- package/src/components/CnFlowsPage/CnFlowsPage.vue +2 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +75 -14
- package/src/components/CnIndexPage/manifestActionDispatch.js +31 -11
- package/src/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue +1 -2
- package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +0 -1
- package/src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue +2 -17
- package/src/components/CnLogsPage/CnLogsPage.vue +37 -12
- package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
- package/src/components/CnObjectList/CnObjectList.vue +19 -3
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +47 -15
- package/src/components/CnObjectRow/CnObjectRow.vue +25 -7
- package/src/components/CnObjectSidebar/CnFilesTab.vue +0 -1
- package/src/components/CnPageRenderer/CnPageRenderer.vue +2 -0
- package/src/components/CnRowActions/CnRowActions.vue +7 -2
- package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
- package/src/components/CnTabs/CnTabs.vue +7 -3
- package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
- package/src/css/table.css +26 -0
- package/src/integrations/builtin/analytics/CnAnalyticsCard.vue +3 -2
- package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +5 -6
- package/src/integrations/builtin/bookmarks/CnBookmarksCard.vue +12 -1
- package/src/integrations/builtin/bookmarks/CnBookmarksTab.vue +23 -6
- package/src/integrations/builtin/bookmarks/__tests__/CnBookmarksTab.spec.js +7 -2
- package/src/integrations/builtin/collectives/CnCollectivesCard.vue +3 -2
- package/src/integrations/builtin/collectives/CnCollectivesTab.vue +5 -6
- package/src/integrations/builtin/cospend/CnCospendCard.vue +2 -2
- package/src/integrations/builtin/cospend/CnCospendTab.vue +3 -4
- package/src/integrations/builtin/deck/CnDeckCard.vue +2 -2
- package/src/integrations/builtin/deck/CnDeckTab.vue +3 -4
- package/src/integrations/builtin/email/CnEmailCard.vue +6 -2
- package/src/integrations/builtin/email/CnEmailTab.vue +7 -4
- package/src/integrations/builtin/flow/CnFlowCard.vue +2 -2
- package/src/integrations/builtin/flow/CnFlowTab.vue +3 -4
- package/src/integrations/builtin/forms/CnFormsCard.vue +3 -2
- package/src/integrations/builtin/forms/CnFormsTab.vue +5 -6
- package/src/integrations/builtin/maps/CnMapsCard.vue +3 -2
- package/src/integrations/builtin/maps/CnMapsTab.vue +5 -6
- package/src/integrations/builtin/maps/__tests__/CnMapsTab.spec.js +9 -0
- package/src/integrations/builtin/openproject/CnOpenprojectCard.vue +3 -2
- package/src/integrations/builtin/openproject/CnOpenprojectTab.vue +5 -6
- package/src/integrations/builtin/photos/CnPhotosCard.vue +2 -2
- package/src/integrations/builtin/photos/CnPhotosTab.vue +3 -4
- package/src/integrations/builtin/polls/CnPollsCard.vue +2 -2
- package/src/integrations/builtin/polls/CnPollsTab.vue +3 -4
- package/src/integrations/builtin/shares/CnSharesTab.vue +3 -6
- package/src/integrations/builtin/talk/CnTalkCard.vue +3 -2
- package/src/integrations/builtin/talk/CnTalkTab.vue +5 -6
- package/src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue +2 -2
- package/src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue +3 -4
- package/src/integrations/builtin/xwiki/CnXwikiCard.vue +4 -2
- package/src/integrations/builtin/xwiki/CnXwikiTab.vue +7 -7
- package/src/integrations/builtin/xwiki/__tests__/CnXwikiTab.spec.js +9 -0
- package/src/utils/actionLink.js +5 -0
- package/src/utils/actionsDispatcher.js +15 -0
- package/src/utils/linkNavigation.js +6 -3
- package/src/utils/rowAuxClick.js +29 -4
- package/src/utils/safeHref.js +18 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actionsDispatcher.js","sources":["../../../src/utils/actionsDispatcher.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2024 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * actionsDispatcher — unified action dispatcher for v2 manifest actions.\n *\n * Dispatches an action object against a context. All dispatch types are\n * handled without throwing — errors surface as console.warn calls.\n * Types: handler | open-modal | open-page | navigate | object-op (the\n * declarative OpenRegister mutation verb added by ADR-049 — dispatched\n * via the shared object store, intent-only, RBAC stays server-side) |\n * export (opens the shared CnMassExportDialog export launcher configured\n * via the action's `entities[]` / `formats[]` — Wave 1 of\n * nextcloud-vue#91; the host page provides `context.openExport`) |\n * open-form (Wave 3 — schema-driven create dialog; the rendering host\n * provides `context.openForm`) | refresh (Wave 3 — bumps the page-level\n * refresh signal on the `cn:page:refresh` event-bus channel) | api-call\n * (Wave 3 — POST/PUT a configured app endpoint with success/error toasts\n * and an automatic page refresh; any `confirm` on the action is INTENT\n * consumed by the rendering surface BEFORE dispatch, like object-op) |\n * agent (run a governed hermiq agent against the page object via\n * POST /apps/hermiq/api/agents/{agent}/run-on-object — hermiq#41; a\n * first-class companion to api-call that resolves the register/schema/\n * objectId context for the author and fail-closes when hermiq is absent) |\n * run-node (manifest-run-node-action — invoke ONE OpenRegister flow node\n * directly against a subject object via\n * POST /api/flows/{flowId}/nodes/{nodeId}/run; the rendering surface\n * provides context.openRunNode, mirroring open-form, and resolves the\n * node's own declared config form before opening a dialog — see or-flow-run-node).\n *\n * `api-call`'s request body prefers `payload` (DEEP @-token resolution at\n * any nesting depth — object/array, e.g. a Filinq-style\n * `{ dataRefs: [{ id: '@objectId' }] }` body) over the legacy `params`\n * (shallow, one-level filter-map resolution, kept for back-compat). Set\n * `download: true` to request a binary blob response and trigger a\n * browser file download instead of a JSON toast+refresh cycle (filename:\n * `Content-Disposition` header, else the token-resolved `filename`, else\n * `'download.pdf'`; no auto-refresh unless `refresh: true` is explicit).\n *\n * `toggle` (Wave 3) is deliberately NOT dispatchable: a toggle is a\n * stateful two-way control (GET state on mount, write on click) rendered\n * by the header-actions surface (CnActionButtons); dispatching it here\n * warns and no-ops.\n *\n * Spec: REQ-MVR-011 (manifest-v2-renderer) — unified actions dispatcher\n * / ADR-036 Decision 7 / ADR-049 Decision 2 / #91 Wave 3\n */\n\nimport { emit } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { parseDispositionFilename, triggerBlobDownload } from '../components/CnIndexPage/selfModeIO.js'\nimport { interpolateUrlTokens } from '../composables/useEndpointSource.js'\nimport { routeHref } from './actionLink.js'\nimport {\n\tdropOptionalUnresolved,\n\tdropOptionalUnresolvedDeep,\n\thasUnresolvedDeepTokens,\n\thasUnresolvedTokens,\n\tresolveDeepTokens,\n\tresolveFilterTokens,\n} from './resolveFilterTokens.js'\nimport { schemaRefSlug } from './schemaRefSlug.js'\n\n/** Event-bus channel the page-level Refresh signal broadcasts on (Wave 2). */\nconst PAGE_REFRESH_CHANNEL = 'cn:page:refresh'\n\n/**\n * Whether a `navigate` target leaves the app: it is `http(s)://`,\n * protocol-relative (`//`), or a `mailto:` / `tel:` link. Anything else is an\n * in-app path for the router.\n *\n * vue-router happily accepts an absolute URL as a PATH — `router.push` on\n * `https://youtu.be/x?v=1` matches no route and lands on the fallback with the\n * query carried over, which reads as the app ignoring the link.\n *\n * An allowlist, not a generic `<scheme>://` match: manifests are\n * admin-editable app-wide config, and an external target renders into an\n * anchor `href` and a dispatcher `window.open` — a generic scheme match would\n * accept `javascript://` too.\n *\n * @param {unknown} target The action target to test.\n * @return {boolean} True when the target is external.\n */\nexport function isExternalActionTarget(target) {\n\tif (typeof target !== 'string') {\n\t\treturn false\n\t}\n\treturn /^\\/\\//.test(target) || /^(https?|mailto|tel):/i.test(target)\n}\n\n/**\n * Resolve the object-store type slug for a widget `source` register/schema\n * pair. Reuses an already-registered type whose config matches (so an app's\n * own caches stay coherent); otherwise registers a deterministic\n * `<register>/<schema>` slug on the fly.\n *\n * `source.schema` is routed through {@link schemaRefSlug} before it ever\n * reaches the store or the objects API: a caller that passes a `$ref`\n * schema TITLE (`ReportPeriod`) rather than its slug (`report-period`)\n * would otherwise register — and fetch — a 404 (defect 7, learniq round\n * 1). Idempotent for a caller that already passes a correct slug.\n *\n * @param {object} store The object store instance (useObjectStore shape).\n * @param {{register: (string|number), schema: (string|number)}} source The widget source.\n * @return {string} The type slug to use for store CRUD calls.\n */\nexport function resolveObjectOpType(store, source) {\n\tconst register = String(source.register)\n\tconst schema = String(schemaRefSlug(source.schema))\n\tconst registry = store.objectTypeRegistry || {}\n\tfor (const [slug, config] of Object.entries(registry)) {\n\t\tif (!config) {\n\t\t\tcontinue\n\t\t}\n\t\tif ((String(config.register) === register && String(config.schema) === schema)\n\t\t\t|| (config.registerSlug === register && config.schemaSlug === schema)) {\n\t\t\treturn slug\n\t\t}\n\t}\n\tconst slug = `${register}/${schema}`\n\tif (!registry[slug]) {\n\t\tstore.registerObjectType(slug, schema, register)\n\t}\n\treturn slug\n}\n\n/**\n * Extract an OpenRegister object's id from a row (top-level `id` wins,\n * `@self.id` metadata fallback).\n *\n * @param {object} row The row object.\n * @return {string|number|null} The object id, or null when absent.\n */\nfunction rowObjectId(row) {\n\tif (!row || typeof row !== 'object') {\n\t\treturn null\n\t}\n\tif (row.id !== undefined && row.id !== null) {\n\t\treturn row.id\n\t}\n\tconst self = row['@self']\n\tif (self && typeof self === 'object' && self.id !== undefined && self.id !== null) {\n\t\treturn self.id\n\t}\n\treturn null\n}\n\n/**\n * Extract a saved OpenRegister object's id for a post-save deep-link:\n * top-level `id`, then `uuid`, then the `@self.id` metadata fallback.\n *\n * @param {object} saved The saved record returned by the object store.\n * @return {string|number|null} The object id, or null when absent.\n */\nexport function savedObjectId(saved) {\n\tif (!saved || typeof saved !== 'object') {\n\t\treturn null\n\t}\n\tif (saved.id !== undefined && saved.id !== null) {\n\t\treturn saved.id\n\t}\n\tif (saved.uuid !== undefined && saved.uuid !== null) {\n\t\treturn saved.uuid\n\t}\n\tconst self = saved['@self']\n\tif (self && typeof self === 'object' && self.id !== undefined && self.id !== null) {\n\t\treturn self.id\n\t}\n\treturn null\n}\n\n/**\n * Resolve a manifest-named FUNCTION out of the v2 registry, falling back to the\n * legacy customComponents map.\n *\n * A manifest names a behaviour the same way it names a component — by string —\n * so every surface that dispatches one (`actions[].handler`,\n * `bulkActions[].handler`, `headerActions[].handler`, `config.createOverride`)\n * has to agree on where that string is looked up and what counts as a match.\n * Recognised shapes, in order:\n * 1. a registry entry exposing the function as `.handler` / `.fn` (the\n * `kind: 'handler'` and `kind: 'create-override'` entries),\n * 2. a directly function-valued registry entry,\n * 3. a function-valued legacy `customComponents` entry.\n *\n * The legacy map stays last so an app that has not migrated is unchanged, and\n * so an app mid-migration can move its handlers over one at a time.\n *\n * @param {string} name The registered handler name.\n * @param {object} registry The v2 component registry.\n * @param {object} customComponents The legacy customComponents map.\n * @return {?((scope?: object) => unknown)} The handler, or null when nothing of that name is a function.\n */\nexport function resolveRegisteredHandler(name, registry, customComponents) {\n\tif (typeof name !== 'string' || name === '') {\n\t\treturn null\n\t}\n\tconst entry = (registry || {})[name]\n\tif (typeof entry === 'function') {\n\t\treturn entry\n\t}\n\tif (entry && typeof entry === 'object') {\n\t\tif (typeof entry.handler === 'function') {\n\t\t\treturn entry.handler\n\t\t}\n\t\tif (typeof entry.fn === 'function') {\n\t\t\treturn entry.fn\n\t\t}\n\t}\n\tconst legacy = (customComponents || {})[name]\n\treturn typeof legacy === 'function' ? legacy : null\n}\n\n/**\n * Resolve a named create-override handler out of the v2 registry (or the\n * legacy customComponents map) by value shape.\n *\n * A create-override is a plain async function that replaces the default\n * `objectStore.saveObject` persist path — needed whenever a schema requires a\n * field the create form cannot supply on its own (a server-minted foreign key,\n * say).\n *\n * Shared by CnPageRenderer (page-level `config.createOverride` → CnIndexPage's\n * prop) and CnActionButtons (per-action `createOverride` on an `open-form`), so\n * the two surfaces cannot drift on what counts as a valid handler.\n *\n * @param {string} name The registered handler name.\n * @param {object} registry The v2 component registry.\n * @param {object} customComponents The legacy customComponents map.\n * @return {?((props?: object) => Promise<unknown>)} The async create handler, or null when unresolved.\n */\nexport function resolveCreateOverrideHandler(name, registry, customComponents) {\n\treturn resolveRegisteredHandler(name, registry, customComponents)\n}\n\n/**\n * Build the vue-router push location for an `open-form` action's\n * `onSuccessRoute`, merging the saved object's id into the route params so\n * the post-save navigation can deep-link to the created object's detail page\n * (#91).\n *\n * `onSuccessRoute` may be:\n * - a STRING route NAME → `{ name, params: { id } }`. The saved id lands\n * under the default `id` param; a route without an `:id` segment simply\n * ignores the extra param, so a bare-name route keeps working unchanged\n * (backward compatible).\n * - an OBJECT `{ name, paramField?, objectParam? }` → the id is placed\n * under `paramField` (default `id`), and — when `objectParam` is set —\n * the whole saved object is passed under that param key too (so a\n * `props: true` detail route can render the record without a refetch).\n *\n * The id is read via {@link savedObjectId} (`saved.id` → `saved.uuid` →\n * `saved['@self'].id`). Returns `null` when no route name is resolvable, so\n * the caller can skip the navigation.\n *\n * @param {(string|{name: string, paramField?: string, objectParam?: string})} onSuccessRoute The action's onSuccessRoute config.\n * @param {object} saved The saved OpenRegister object.\n * @return {{name: string, params: object}|null} The router push location, or null.\n */\nexport function buildOnSuccessRoute(onSuccessRoute, saved) {\n\tconst spec = typeof onSuccessRoute === 'string'\n\t\t? { name: onSuccessRoute }\n\t\t: (onSuccessRoute && typeof onSuccessRoute === 'object' ? onSuccessRoute : null)\n\tif (!spec || typeof spec.name !== 'string' || spec.name === '') {\n\t\treturn null\n\t}\n\tconst params = {}\n\tconst id = savedObjectId(saved)\n\tif (id !== null && id !== undefined) {\n\t\tparams[spec.paramField || 'id'] = id\n\t}\n\tif (spec.objectParam && saved && typeof saved === 'object') {\n\t\tparams[spec.objectParam] = saved\n\t}\n\treturn { name: spec.name, params }\n}\n\n/**\n * Run a manifest-authored toast message through the host translate function.\n *\n * `successMessage` / `errorMessage` are English source strings authored in the\n * manifest (ADR-007), so they need the SAME translator the rest of the page\n * chrome uses — the consumer's bound `t()`, handed to the dispatcher by the\n * rendering surface as `context.translate` (which itself comes from the\n * `cnTranslate` CnAppRoot provides).\n *\n * A pure pass-through: with no translator, no message, or a catalogue that\n * lacks the key, the message is returned byte-identical. Server-supplied\n * messages are data and are never routed through here.\n *\n * @param {unknown} message The manifest-authored message (may be undefined).\n * @param {object} context The dispatch context (`context.translate` optional).\n * @return {unknown} The translated message, or the input unchanged.\n */\nfunction translateMessage(message, context) {\n\tconst fn = context && context.translate\n\tif (!message || typeof fn !== 'function') {\n\t\treturn message\n\t}\n\treturn fn(message)\n}\n\n/**\n * Interpolate the `api-call` URL/filename token grammar inside a string:\n * the `{objectId}` BRACE convention (a literal placeholder some manifests\n * use for path templates, matching the OpenRegister credential-broker path\n * style) as well as the full `@objectId` / `@object.<field>` /\n * `@workspace.<key>` / `@config.<key>` grammar {@link interpolateUrlTokens}\n * already understands. Unresolved `@`-tokens and an unresolved\n * `{objectId}` both collapse to an empty string (never leak a literal\n * placeholder into a request URL or a downloaded filename).\n *\n * @param {string} str The raw string (an action's `url` or `filename`).\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object}} ctx Token context.\n * @return {string} The interpolated string.\n */\nfunction interpolateActionString(str, ctx) {\n\tif (typeof str !== 'string') {\n\t\treturn str\n\t}\n\tconst braced = str.replace(/\\{objectId\\}/g, () => {\n\t\tconst id = ctx.objectId\n\t\treturn (id === undefined || id === null) ? '' : String(id)\n\t})\n\treturn interpolateUrlTokens(braced, ctx)\n}\n\n/**\n * Interpolate an action's `target` URL — the same grammar `url` runs.\n *\n * Exported because the rendering surface resolves a target BEFORE deciding\n * whether it is an external link, and that decision happens there rather than\n * in the dispatcher.\n *\n * @param {string} target The action's raw `target`.\n * @param {object} ctx Token context (`{ objectId?, object?, workspace?, config? }`).\n *\n * @return {string} The interpolated target.\n */\nexport function interpolateActionTarget(target, ctx) {\n\treturn interpolateActionString(target || '', ctx || {})\n}\n\n/**\n * The link an action renders as when its only job is to navigate to a target\n * known at render time: `navigate` (token-interpolated like the dispatcher)\n * and `open-page`. Anything else, a `confirm: true` action, or an in-app\n * target the router cannot resolve answers null and keeps dispatching.\n *\n * `to` is what a plain click hands the router (null for an external URL,\n * which the browser opens in a new tab like the dispatcher's `window.open`).\n *\n * @param {object} action The manifest action.\n * @param {{router?: object, tokenCtx?: object}} [context] The dispatch context.\n * @return {{href: string, to: (string|object|null), external: boolean}|null} The link, or null.\n */\nexport function actionLink(action, context = {}) {\n\tif (!action || typeof action !== 'object' || action.confirm) {\n\t\treturn null\n\t}\n\tif (action.type === 'navigate') {\n\t\tconst target = interpolateActionString(action.target || '', context.tokenCtx || {})\n\t\tif (!target) {\n\t\t\treturn null\n\t\t}\n\t\tif (isExternalActionTarget(target)) {\n\t\t\treturn { href: target, to: null, external: true }\n\t\t}\n\t\tconst href = routeHref(target, context.router)\n\t\treturn href ? { href, to: target, external: false } : null\n\t}\n\tif (action.type === 'open-page') {\n\t\tif (typeof action.target !== 'string' || action.target.length === 0) {\n\t\t\treturn null\n\t\t}\n\t\tconst to = { name: action.target }\n\t\tconst href = routeHref(to, context.router)\n\t\treturn href ? { href, to, external: false } : null\n\t}\n\treturn null\n}\n\n/**\n * Execute a Wave-3 `api-call` action: POST/PUT the configured app endpoint\n * (URL + body run the SAME @-token grammar endpoint sources use), toast the\n * outcome via @nextcloud/dialogs, then — unless `action.refresh` is `false`\n * (or, for a `download` action, unless `action.refresh` is explicitly\n * `true`) — bump the page-level refresh signal so every endpoint-bound\n * widget refetches. Confirm-gating is the RENDERING SURFACE's job (the\n * `confirm` field is intent, object-op precedent): this runs after any\n * confirmation already happened.\n *\n * The request body prefers `action.payload` (DEEP token resolution, any\n * nesting — {@link resolveDeepTokens}) over the legacy `action.params`\n * (shallow, one-level filter-map resolution — unchanged for back-compat).\n * A required token left unresolved anywhere in the body BLOCKS the call\n * (error toast) rather than sending a literal `@objectId` to the server.\n *\n * `action.download === true` requests the response as a binary blob\n * (`responseType: 'blob'`) and triggers a browser file download — the\n * filename comes from the response's `Content-Disposition` header, else\n * the token-resolved `action.filename`, else `'download.pdf'`.\n *\n * Never throws: a failed call resolves `{ ok: false, error }` after the\n * error toast (so a confirm dialog can await the outcome).\n *\n * @param {object} action The api-call action (`url`, `method?`, `payload?`,\n * `params?`, `download?`, `filename?`, `successMessage?`, `errorMessage?`,\n * `refresh?`).\n * @param {object} context Runtime context; `context.tokenCtx` is the token\n * context (`{ objectId?, object?, workspace?, config? }`) the URL/body\n * resolve against.\n * @return {Promise<{ok: boolean, data?: unknown, error?: unknown}>} The call outcome.\n */\nasync function executeApiCall(action, context) {\n\tconst tokenCtx = context.tokenCtx || {}\n\tconst url = interpolateActionString(action.url || '', tokenCtx)\n\n\tconst hasPayload = action.payload && typeof action.payload === 'object' && !Array.isArray(action.payload)\n\tconst body = hasPayload\n\t\t? dropOptionalUnresolvedDeep(resolveDeepTokens(action.payload, tokenCtx))\n\t\t: dropOptionalUnresolved(resolveFilterTokens(action.params || {}, tokenCtx))\n\tconst bodyBlocked = hasPayload ? hasUnresolvedDeepTokens(body) : hasUnresolvedTokens(body)\n\n\tif (!url || bodyBlocked) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn('[dispatchAction] api-call is missing its url or a required token is unresolved — skipping.', action)\n\t\treturn { ok: false, error: new Error('api-call blocked') }\n\t}\n\tconst method = String(action.method || 'POST').toUpperCase() === 'PUT' ? 'put' : 'post'\n\tconst isDownload = action.download === true\n\tconst [{ default: axios }, { generateUrl }, dialogs] = await Promise.all([\n\t\timport('@nextcloud/axios'),\n\t\timport('@nextcloud/router'),\n\t\timport('@nextcloud/dialogs'),\n\t])\n\tconst target = /^https?:\\/\\//i.test(url) ? url : generateUrl(url)\n\ttry {\n\t\t// Only pass a third axios config arg for a download call — an\n\t\t// explicit `undefined` third argument would otherwise change the\n\t\t// call shape for every existing (non-download) api-call consumer/test.\n\t\tconst res = isDownload\n\t\t\t? await axios[method](target, body, { responseType: 'blob' })\n\t\t\t: await axios[method](target, body)\n\t\tif (isDownload) {\n\t\t\tconst filename = parseDispositionFilename(\n\t\t\t\tres && res.headers && res.headers['content-disposition'],\n\t\t\t\tinterpolateActionString(action.filename, tokenCtx) || 'download.pdf',\n\t\t\t)\n\t\t\ttriggerBlobDownload(res.data, filename)\n\t\t}\n\t\tif (typeof dialogs.showSuccess === 'function') {\n\t\t\tdialogs.showSuccess(translateMessage(action.successMessage, context) || t('nextcloud-vue', 'Action completed.'))\n\t\t}\n\t\tconst shouldRefresh = isDownload ? action.refresh === true : action.refresh !== false\n\t\tif (shouldRefresh) {\n\t\t\temit(PAGE_REFRESH_CHANNEL, {})\n\t\t}\n\t\treturn { ok: true, data: res && res.data }\n\t} catch (error) {\n\t\tconst serverMessage = error && error.response && error.response.data\n\t\t\t&& (error.response.data.error || error.response.data.message)\n\t\tif (typeof dialogs.showError === 'function') {\n\t\t\tdialogs.showError(translateMessage(action.errorMessage, context) || serverMessage || t('nextcloud-vue', 'Action failed.'))\n\t\t}\n\t\treturn { ok: false, error }\n\t}\n}\n\n/**\n * Resolve an agent-action object reference (`register` / `schema` / `objectId`):\n * an explicitly-declared action value WINS (it may itself be an @-token string,\n * interpolated against the token context), otherwise it falls back to the page\n * object context default (`tokenCtx.register` / `.schema` / `.objectId`). An\n * unresolved token collapses to an empty string (never leaks a literal `@objectId`\n * to the server); a `{ slug | id }` object (a schema holder) is flattened to its\n * slug/id.\n *\n * @param {unknown} actionVal The action's explicit value (may be undefined or an @-token).\n * @param {unknown} ctxDefault The page-context default (`tokenCtx.<field>`).\n * @param {object} tokenCtx The token context the @-tokens resolve against.\n * @return {string} The resolved reference, or '' when unresolved/absent.\n */\nfunction resolveAgentRef(actionVal, ctxDefault, tokenCtx) {\n\tlet v = actionVal\n\tif (v === undefined || v === null || v === '') {\n\t\tv = ctxDefault\n\t} else if (typeof v === 'string') {\n\t\tv = interpolateActionString(v, tokenCtx)\n\t}\n\tif (v && typeof v === 'object') {\n\t\treturn String(v.slug || v.id || '')\n\t}\n\tif (v === undefined || v === null) {\n\t\treturn ''\n\t}\n\treturn String(v)\n}\n\n/**\n * Execute an `agent` action: run a governed hermiq agent against the page's\n * OpenRegister object. Resolves the object context (`register` / `schema` /\n * `objectId` — explicit action fields override, else default to the page's\n * `@register` / `@schema` / `@objectId` context) and POSTs\n * `{ register, schema, objectId, resultField?, skill?, prompt? }` to hermiq's\n * object-RBAC-scoped run endpoint `/apps/hermiq/api/agents/{agent}/run-on-object`\n * (hermiq#41), which dispatches the governed `AgentRunRequestedEvent`.\n *\n * A first-class companion to `type:\"api-call\"` — the author declares \"run agent\n * here\" without hand-wiring the URL/body. `type:\"api-call\"` remains the escape\n * hatch for a bespoke body.\n *\n * Fail-closed. An unresolved REQUIRED `@objectId` (or a missing agent/register/\n * schema) BLOCKS the call (warn) rather than POSTing a literal token. On the\n * server's 202 it toasts `successMessage` (default 'Run queued') and — unless\n * `refresh: false` — bumps the page refresh signal. A 403 / 404 with a structured\n * error body fail-closes with that message; a 404 with NO structured body is read\n * as \"hermiq absent\" and toasts a graceful 'agent runtime unavailable' — the\n * dispatcher NEVER hard-requires hermiq to be installed. Never throws.\n *\n * @param {object} action The agent action (`agent`, `skill?`, `prompt?`,\n * `resultField?`, `register?`, `schema?`, `objectId?`, `successMessage?`,\n * `errorMessage?`, `refresh?`).\n * @param {object} context Runtime context; `context.tokenCtx` is the token\n * context (`{ objectId?, object?, register?, schema?, workspace?, config? }`).\n * @return {Promise<{ok: boolean, data?: unknown, error?: unknown}>} The call outcome.\n */\nasync function executeAgentAction(action, context) {\n\tconst tokenCtx = context.tokenCtx || {}\n\tconst agent = resolveAgentRef(action.agent, undefined, tokenCtx)\n\tconst register = resolveAgentRef(action.register, tokenCtx.register, tokenCtx)\n\tconst schema = resolveAgentRef(action.schema, tokenCtx.schema, tokenCtx)\n\tconst objectId = resolveAgentRef(action.objectId, tokenCtx.objectId, tokenCtx)\n\n\tif (!agent || !register || !schema || !objectId) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn('[dispatchAction] agent action is missing its agent or a required object-context token (register/schema/objectId) is unresolved — skipping.', action)\n\t\treturn { ok: false, error: new Error('agent action blocked') }\n\t}\n\n\tconst body = { register, schema, objectId }\n\tconst resultField = resolveAgentRef(action.resultField, undefined, tokenCtx)\n\tif (resultField) {\n\t\tbody.resultField = resultField\n\t}\n\tconst skill = resolveAgentRef(action.skill, undefined, tokenCtx)\n\tif (skill) {\n\t\tbody.skill = skill\n\t}\n\tif (typeof action.prompt === 'string' && action.prompt !== '') {\n\t\tbody.prompt = interpolateActionString(action.prompt, tokenCtx)\n\t}\n\n\tconst [{ default: axios }, { generateUrl }, dialogs] = await Promise.all([\n\t\timport('@nextcloud/axios'),\n\t\timport('@nextcloud/router'),\n\t\timport('@nextcloud/dialogs'),\n\t])\n\tconst target = generateUrl(`/apps/hermiq/api/agents/${encodeURIComponent(agent)}/run-on-object`)\n\ttry {\n\t\tconst res = await axios.post(target, body)\n\t\tif (typeof dialogs.showSuccess === 'function') {\n\t\t\tdialogs.showSuccess(translateMessage(action.successMessage, context) || t('nextcloud-vue', 'Run queued'))\n\t\t}\n\t\tif (action.refresh !== false) {\n\t\t\temit(PAGE_REFRESH_CHANNEL, {})\n\t\t}\n\t\treturn { ok: true, data: res && res.data }\n\t} catch (error) {\n\t\tconst response = error && error.response\n\t\tconst status = response && response.status\n\t\tconst serverMessage = response && response.data\n\t\t\t&& (response.data.error || response.data.message)\n\t\t// A 404 with NO structured error body = hermiq is not installed (the app\n\t\t// route 404s at the NC level) — surface a graceful \"runtime unavailable\"\n\t\t// rather than a fail-closed authorization message.\n\t\tconst hermiqAbsent = status === 404 && !serverMessage\n\t\tif (typeof dialogs.showError === 'function') {\n\t\t\tconst msg = hermiqAbsent\n\t\t\t\t? t('nextcloud-vue', 'Agent runtime unavailable')\n\t\t\t\t: (translateMessage(action.errorMessage, context) || serverMessage || t('nextcloud-vue', 'Action failed.'))\n\t\t\tdialogs.showError(msg)\n\t\t}\n\t\treturn { ok: false, error }\n\t}\n}\n\n/**\n * POST the run-node call: `POST /apps/openregister/api/flows/{flowId}/nodes/{nodeId}/run`.\n *\n * This is the SECOND half of `run-node` (manifest-run-node-action / or-flow-run-node) —\n * the first half is `context.openRunNode` in the `dispatchAction` switch below, which\n * only OPENS the config dialog (or decides none is needed) and never itself waits for\n * the person. Once the rendering surface has a `config` object (from the dialog's\n * submit, or `{}` when the node's declared form was empty), it calls this directly —\n * it is not reached through `dispatchAction`'s switch, because by that point there is\n * no further \"type\" dispatch to do, only the call itself, same as `executeAgentAction`\n * is called directly by `case 'api-call'`'s sibling rather than re-entering the switch.\n *\n * Fail-closed exactly like `executeAgentAction`: an unresolved REQUIRED subject\n * (`action.subject`, defaulting to the page's `@objectId`) or a missing\n * `flowId`/`nodeId` BLOCKS the call (warn) rather than POSTing a literal token or an\n * incomplete subject reference. The subject sent is `{ uuid, register, schema }` —\n * OpenRegister's `FlowNodeRunController::resolveAuthorizedSubject()` needs all three\n * to resolve the object and evaluate the caller's object-RBAC permission on it; `register`\n * / `schema` default to the page's own `@register` / `@schema` context, the same\n * defaulting `agent` already uses.\n *\n * A 403 here means the caller holds no update permission on the subject\n * (OpenRegister's object-RBAC, evaluated server-side — RN-1(c)) — this function does not\n * special-case it beyond surfacing the server's message, the same fail-closed handling\n * `api-call` / `agent` already give a rejected call.\n *\n * @param {object} action The run-node action (`flowId`, `nodeId`, `subject?`,\n * `register?`, `schema?`, `successMessage?`, `errorMessage?`, `refresh?`).\n * @param {object} context Runtime context — needs `context.tokenCtx` and, for a\n * localised toast, `context.translate`.\n * @param {object} [config] The node's collected config (from its `configForm()`\n * dialog), or `{}` for a node with no declared fields.\n * @return {Promise<{ok: boolean, data?: object, error?: Error}>} The call outcome —\n * `data` is the created `FlowRun` on success.\n *\n * @spec openspec/changes/manifest-run-node-action/specs/manifest-run-node-action/spec.md#requirement-a-run-node-action-invokes-one-flow-node-against-the-page-object\n */\nexport async function postRunNode(action, context, config = {}) {\n\tconst tokenCtx = context.tokenCtx || {}\n\tconst flowId = resolveAgentRef(action.flowId, undefined, tokenCtx)\n\tconst nodeId = resolveAgentRef(action.nodeId, undefined, tokenCtx)\n\tconst uuid = resolveAgentRef(action.subject, tokenCtx.objectId, tokenCtx)\n\tconst register = resolveAgentRef(action.register, tokenCtx.register, tokenCtx)\n\tconst schema = resolveAgentRef(action.schema, tokenCtx.schema, tokenCtx)\n\n\tif (!flowId || !nodeId || !uuid || !register || !schema) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn('[dispatchAction] run-node is missing flowId/nodeId or a required subject token (objectId/register/schema) is unresolved — skipping.', action)\n\t\treturn { ok: false, error: new Error('run-node blocked') }\n\t}\n\n\tconst [{ default: axios }, { generateUrl }, dialogs] = await Promise.all([\n\t\timport('@nextcloud/axios'),\n\t\timport('@nextcloud/router'),\n\t\timport('@nextcloud/dialogs'),\n\t])\n\tconst target = generateUrl(`/apps/openregister/api/flows/${encodeURIComponent(flowId)}/nodes/${encodeURIComponent(nodeId)}/run`)\n\ttry {\n\t\tconst res = await axios.post(target, { subject: { uuid, register, schema }, config: config || {} })\n\t\tif (typeof dialogs.showSuccess === 'function') {\n\t\t\tdialogs.showSuccess(translateMessage(action.successMessage, context) || t('nextcloud-vue', 'Run completed.'))\n\t\t}\n\t\tif (action.refresh !== false) {\n\t\t\temit(PAGE_REFRESH_CHANNEL, {})\n\t\t}\n\t\treturn { ok: true, data: res && res.data }\n\t} catch (error) {\n\t\tconst response = error && error.response\n\t\tconst serverMessage = response && response.data\n\t\t\t&& (response.data.error || response.data.message)\n\t\tif (typeof dialogs.showError === 'function') {\n\t\t\tdialogs.showError(translateMessage(action.errorMessage, context) || serverMessage || t('nextcloud-vue', 'Action failed.'))\n\t\t}\n\t\treturn { ok: false, error }\n\t}\n}\n\n/**\n * Dispatch a v2 manifest action.\n *\n * @param {object} action The action object from the manifest.\n * @param {string} [action.type] Dispatch type: \"handler\" | \"open-modal\" | \"open-page\" | \"navigate\" |\n * \"object-op\" | \"export\" | \"open-form\" | \"refresh\" | \"api-call\" | \"agent\".\n * When absent, treated as \"handler\" for v1 backward compatibility.\n * @param {string} [action.handler] Registry key for \"handler\" type. For \"export\": the optional\n * handler invoked by the host with the dialog's confirm payload (`{ format, entity }`).\n * @param {Array} [action.args] Arguments spread to the handler function.\n * @param {string} [action.target] Target for \"open-modal\", \"open-page\", and \"navigate\" types.\n * @param {object} [action.props] Props forwarded to modal for \"open-modal\" type.\n * @param {string} [action.op] \"object-op\" only: mutation verb — \"patch\" | \"delete\" | \"create\".\n * @param {object} [action.values] \"object-op\" only: the verb's payload — the partial object\n * merged over the row for \"patch\", the new object's properties for \"create\"; ignored for \"delete\".\n * @param {boolean} [action.confirm] \"object-op\" only: confirm-gating INTENT consumed by the\n * rendering host (CnWidgetObjectTable routes it through CnConfirmDialog) — the dispatcher\n * itself never gates; it is called after any confirmation already happened.\n * @param {Array<{id: string, label: string}>} [action.entities] \"export\" only: selectable\n * entity types offered by the export launcher (e.g. Leads / Requests). Optional — when\n * absent the dialog shows only the format picker.\n * @param {Array<string|{id: string, label: string}>} [action.formats] \"export\" only: the\n * offered export formats (bare ids like \"csv\" or full `{id, label}` entries). Optional —\n * the dialog's built-in Excel/CSV defaults apply when absent.\n * @param {string} [action.description] \"export\" only: pre-translated description shown\n * above the pickers.\n * @param {string} [action.url] \"api-call\" only: the app endpoint — app-relative (routed\n * through generateUrl) or absolute. May interpolate the shared URL tokens\n * (`@objectId`, `@object.<field>`, `@workspace.<key>`, `@config.<key>`, and the\n * `{objectId}` brace form).\n * @param {string} [action.method] \"api-call\" only: \"POST\" (default) | \"PUT\".\n * @param {object} [action.payload] \"api-call\" only: the JSON body — preferred over `params`.\n * Values resolve the shared `@`-token grammar RECURSIVELY at any nesting depth (objects\n * and arrays of objects, e.g. `{ dataRefs: [{ id: '@objectId' }] }`); optional (`…?`)\n * tokens drop when unresolved anywhere in the tree, an unresolved REQUIRED token blocks\n * the call. When set, `params` is ignored.\n * @param {object} [action.params] \"api-call\" only: legacy JSON body (ignored when `payload`\n * is set). Values pass the shared filter-token grammar ONE level deep; optional (`…?`)\n * tokens drop when unresolved, an unresolved REQUIRED token blocks the call.\n * @param {boolean} [action.download] \"api-call\" only: request the response as a binary blob\n * (`responseType: 'blob'`) and trigger a browser file download instead of a JSON\n * toast+refresh cycle. Default `false`.\n * @param {string} [action.filename] \"api-call\" `download: true` only: fallback filename when\n * the response carries no `Content-Disposition` header (falls back further to\n * `'download.pdf'`). Interpolates the same token grammar as `url`.\n * @param {string} [action.successMessage] \"api-call\" / \"open-form\": pre-translated success\n * toast text (a library default applies when absent).\n * @param {string} [action.errorMessage] \"api-call\" / \"open-form\": pre-translated error toast\n * text (falls back to the server's `error`/`message`, then a library default).\n * @param {boolean} [action.refresh] \"api-call\" / \"agent\" only: bump the `cn:page:refresh` signal\n * after a successful call. Default `true` — EXCEPT for a `download: true` api-call, whose\n * default is `false` (a file download normally shouldn't also force-refetch every widget); set\n * the flag explicitly either way to override.\n * @param {string} [action.agent] \"agent\" only (REQUIRED): the hermiq agent uuid to run against\n * the page object. POSTed to `/apps/hermiq/api/agents/{agent}/run-on-object` (hermiq#41).\n * @param {string} [action.skill] \"agent\" only: optional skill id folded into the governed run.\n * @param {string} [action.prompt] \"agent\" only: optional prompt (interpolates the shared `@`-token\n * grammar inline — `@objectId`, `@object.<field>`, `@workspace.<key>`, `@config.<key>`).\n * @param {string} [action.resultField] \"agent\" only: object field the agent's result is written to.\n * @param {string} [action.register] \"agent\" / \"open-form\": OpenRegister register slug (agent:\n * defaults to the page object context's `@register`).\n * @param {string} [action.schema] \"agent\" / \"open-form\": OpenRegister schema slug (agent: defaults\n * to the page object context's `@schema`).\n * @param {string} [action.objectId] \"agent\" only: the target object id (defaults to the page\n * object context's `@objectId`). An unresolved required token BLOCKS the call (fail-closed).\n *\n * @param {object} context Runtime context.\n * @param {object} [context.router] Vue Router instance. Required for \"open-page\" and \"navigate\".\n * @param {object} [context.registry] Component registry (Record<string, { kind, component }>).\n * Required for \"open-modal\" type.\n * @param {object} [context.handlers] Map of handler name → function. Required for \"handler\" type.\n * @param {(key: string, props?: object) => void} [context.openModal] Opens a modal.\n * Required for \"open-modal\" type.\n * @param {(action: object) => void} [context.openExport] Opens the shared\n * CnMassExportDialog configured from the action. Required for \"export\" type —\n * CnPageRenderer pre-binds it in the `cnDispatchAction` context.\n * @param {(action: object) => void} [context.openForm] Opens the schema-driven\n * create dialog. Required for \"open-form\" type — the rendering surface\n * (CnActionButtons) provides it, mirroring `openExport`. On a successful save the\n * surface navigates to `action.onSuccessRoute` (a route NAME string, or\n * `{name, paramField?, objectParam?}`) via {@link buildOnSuccessRoute}, which merges\n * the saved object's id into the route params so the navigation can deep-link to the\n * created object.\n * @param {(action: object) => void} [context.openRunNode] Opens the\n * node-config dialog (or runs immediately for an empty config form) and\n * POSTs to OpenRegister's direct-invoke endpoint on submit. Required for\n * \"run-node\" type — the rendering surface (CnActionButtons) provides it,\n * mirroring `openForm`.\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object}} [context.tokenCtx]\n * Token context \"api-call\" URLs/params resolve against (the same shape\n * `resolveFilterTokens` / `interpolateUrlTokens` take).\n * @param {(app: string, text: string, vars?: object) => string} [context.translate] The consumer's bound `t()`, the same\n * `cnTranslate` CnAppRoot provides to the page chrome. Applied to the\n * manifest-authored `successMessage` / `errorMessage` of \"api-call\" and\n * \"agent\" so their toasts follow the user's language. Omitted (or a\n * catalogue miss) leaves the message byte-identical.\n * @param {object} [context.objectStore] Object store instance (useObjectStore shape).\n * Required for \"object-op\" type. All mutations go through `saveObject` / `deleteObject`\n * so store caches (and their no-mutation-on-error semantics) apply.\n * @param {{register: (string|number), schema: (string|number)}} [context.source] The widget's\n * declarative source the mutation targets. Required for \"object-op\" type.\n * @param {object} [context.row] The row object a row-scoped \"object-op\" (patch/delete) mutates.\n *\n * @return {Promise<object|boolean|null>|undefined} For \"object-op\": the store call's promise —\n * the saved object (or `null` on a rejected write) for patch/create, `true`/`false` for\n * delete. The store only mutates its local caches on SUCCESS, so a backend (RBAC) rejection\n * surfaces via the store's `errors[type]` without any local state change. For \"api-call\":\n * a promise of `{ ok, data?, error? }` (toasts + refresh already handled). All other types\n * return undefined.\n */\nexport function dispatchAction(action, context = {}) {\n\tif (!action || typeof action !== 'object') {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn('[dispatchAction] Received invalid action:', action)\n\t\treturn\n\t}\n\n\t// Missing type is treated as \"handler\" for v1 backward compatibility\n\tconst type = action.type || 'handler'\n\n\tswitch (type) {\n\t\tcase 'handler': {\n\t\t\tconst handlerName = action.handler\n\t\t\tconst handlers = context.handlers || {}\n\n\t\t\tif (!handlerName || typeof handlers[handlerName] !== 'function') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[dispatchAction] Handler \"${handlerName}\" not found in context.handlers.`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\thandlers[handlerName](...(action.args ?? []))\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'open-modal': {\n\t\t\tconst target = action.target\n\t\t\tconst registry = context.registry || {}\n\t\t\tconst entry = registry[target]\n\n\t\t\tif (!entry) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[dispatchAction] open-modal target \"${target}\" not found in registry.`)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (entry.kind !== 'modal') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[dispatchAction] open-modal target \"${target}\" has kind \"${entry.kind}\" (expected \"modal\").`)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (typeof context.openModal !== 'function') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] open-modal requires context.openModal to be a function.')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tcontext.openModal(target, action.props ?? {})\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'open-page': {\n\t\t\tif (!context.router) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] open-page requires context.router to be a Vue Router instance.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontext.router.push({ name: action.target })\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'navigate': {\n\t\t\t// A target is a URL exactly as `url` is, so it runs the same token\n\t\t\t// grammar. Without this a manifest's `{objectId}` reached the router\n\t\t\t// as those nine literal characters and landed in the address bar.\n\t\t\tconst target = interpolateActionString(action.target || '', context.tokenCtx || {})\n\n\t\t\t// An external target is not a route. Rendering surfaces should give it\n\t\t\t// to the browser as a real link (CnActionButtons does); this is the\n\t\t\t// fallback for a programmatic dispatch, and it must not reach the\n\t\t\t// router — see isExternalActionTarget.\n\t\t\tif (isExternalActionTarget(target)) {\n\t\t\t\twindow.open(target, '_blank', 'noopener,noreferrer')\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif (!context.router) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] navigate requires context.router to be a Vue Router instance.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontext.router.push(target)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'export': {\n\t\t// Export launcher (Wave 1, nextcloud-vue#91): the host page opens the\n\t\t// shared CnMassExportDialog configured via the action's entities[] /\n\t\t// formats[]; the dialog's confirm payload routes to the action's\n\t\t// optional `handler` (the app's export service does the download).\n\t\t\tif (typeof context.openExport !== 'function') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] export requires context.openExport to be a function.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontext.openExport(action)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'open-form': {\n\t\t// Schema-driven create dialog (Wave 3, nextcloud-vue#91): the\n\t\t// rendering surface (CnActionButtons) mounts the shared\n\t\t// CnAdvancedFormDialog and handles the save — mirroring how\n\t\t// `export` delegates to the host's CnMassExportDialog.\n\t\t\tif (typeof context.openForm !== 'function') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] open-form requires context.openForm to be a function.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontext.openForm(action)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'run-node': {\n\t\t// Direct flow-node invocation (manifest-run-node-action / RN-4):\n\t\t// SAME shape as open-form, deliberately — the rendering surface\n\t\t// (CnActionButtons) resolves the node's own config form, opens a\n\t\t// generic dialog when it has fields (skips it when empty), and\n\t\t// POSTs subject+config to OpenRegister's\n\t\t// /api/flows/{flowId}/nodes/{nodeId}/run on submit. This case\n\t\t// resolves as soon as the dialog OPENS (or the empty-form call\n\t\t// starts) — it does NOT wait for the person to answer, matching\n\t\t// every other consumer's assumption that dispatchAction's Promise\n\t\t// means \"the call happened\", not \"the human decided\" (see design.md\n\t\t// RN-4 for why an awaiting-on-UI shape was rejected).\n\t\t\tif (typeof context.openRunNode !== 'function') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] run-node requires context.openRunNode to be a function.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontext.openRunNode(action)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'refresh': {\n\t\t// Page-level refresh (Wave 3, nextcloud-vue#91): bump the SAME\n\t\t// `cn:page:refresh` event-bus signal the page overflow menu's\n\t\t// Refresh item broadcasts — every endpoint-bound / bus-subscribed\n\t\t// widget on the page force-refetches past its shared cache.\n\t\t\temit(PAGE_REFRESH_CHANNEL, {})\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'api-call': {\n\t\t// POST/PUT an app endpoint + toast + refresh (Wave 3). Any\n\t\t// `confirm` on the action is INTENT the rendering surface consumed\n\t\t// BEFORE calling the dispatcher (object-op precedent) — no gating\n\t\t// happens here.\n\t\t\treturn executeApiCall(action, context)\n\t\t}\n\n\t\tcase 'agent': {\n\t\t// Run a governed hermiq agent against the page object (hermiq#41). A\n\t\t// first-class companion to api-call: resolves the object context\n\t\t// (register/schema/objectId) and POSTs run-on-object. Any `confirm`\n\t\t// on the action is INTENT the rendering surface consumed BEFORE\n\t\t// dispatch (api-call / object-op precedent) — no gating here. hermiq\n\t\t// is NOT hard-required: an app-level 404 surfaces a graceful toast.\n\t\t\treturn executeAgentAction(action, context)\n\t\t}\n\n\t\tcase 'toggle': {\n\t\t// A toggle is a stateful two-way control (GET state on mount,\n\t\t// write on click) — it is RENDERED by the header-actions surface\n\t\t// (CnActionButtons), never dispatched as a one-shot action.\n\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn('[dispatchAction] \"toggle\" is a stateful header-actions control rendered by CnActionButtons — it cannot be dispatched.')\n\t\t\treturn\n\t\t}\n\n\t\tcase 'object-op': {\n\t\t// Declarative mutation of an OpenRegister object, dispatched via the\n\t\t// shared object store (ADR-049). The manifest declares INTENT only:\n\t\t// authorization-shaped fields on the action (`role`, `allow`, …) are\n\t\t// NEVER consulted here — OpenRegister RBAC is the single authority\n\t\t// (ADR-022 / ADR-023) and a forbidden mutation is rejected server-side.\n\t\t// The store mutates its caches only on success, so a rejected write\n\t\t// surfaces as an error with no local state change.\n\t\t\tconst op = action.op\n\t\t\tif (op !== 'patch' && op !== 'delete' && op !== 'create') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[dispatchAction] object-op has invalid op \"${op}\" (expected patch | delete | create).`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst store = context.objectStore\n\t\t\tif (!store || typeof store.saveObject !== 'function' || typeof store.deleteObject !== 'function') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] object-op requires context.objectStore (useObjectStore shape).')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst source = context.source\n\t\t\tif (!source || !source.register || !source.schema) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] object-op requires context.source with register + schema.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst type = resolveObjectOpType(store, source)\n\n\t\t\tif (op === 'create') {\n\t\t\t\treturn store.saveObject(type, { ...(action.values || {}) })\n\t\t\t}\n\n\t\t\tconst row = context.row\n\t\t\tconst id = rowObjectId(row)\n\t\t\tif (id === null) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[dispatchAction] object-op \"${op}\" is row-scoped and requires context.row with an id.`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (op === 'delete') {\n\t\t\t\treturn store.deleteObject(type, id)\n\t\t\t}\n\t\t\t// patch: the row's object merged with the action's values.\n\t\t\treturn store.saveObject(type, { ...row, ...(action.values || {}), id })\n\t\t}\n\n\t\tdefault:\n\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn(`[dispatchAction] Unknown action type \"${type}\".`)\n\t}\n}\n"],"names":["t"],"mappings":"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C,oBAAoB,EAAE,EAAE,EAAC;AACjC,GAAG;AACH,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE;AAC5C,EAAE,CAAC,OAAO,KAAK,EAAE;AACjB,EAAE,MAAM,QAAQ,GAAG,KAAK,IAAI,KAAK,CAAC,SAAQ;AAC1C,EAAE,MAAM,aAAa,GAAG,QAAQ,IAAI,QAAQ,CAAC,IAAI;AACjD,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAC;AACpD,EAAE,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,UAAU,EAAE;AAC/C,GAAG,OAAO,CAAC,SAAS,CAAC,gBAAgB,CAAC,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,aAAa,IAAIA,SAAC,CAAC,eAAe,EAAE,gBAAgB,CAAC,EAAC;AAC7H,GAAG;AACH,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE;AAC7B,EAAE;AACF,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,EAAE,EAAE;AACrD,CAAC,IAAI,C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"}
|
|
1
|
+
{"version":3,"file":"actionsDispatcher.js","sources":["../../../src/utils/actionsDispatcher.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2024 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * actionsDispatcher — unified action dispatcher for v2 manifest actions.\n *\n * Dispatches an action object against a context. All dispatch types are\n * handled without throwing — errors surface as console.warn calls.\n * Types: handler | open-modal | open-page | navigate | object-op (the\n * declarative OpenRegister mutation verb added by ADR-049 — dispatched\n * via the shared object store, intent-only, RBAC stays server-side) |\n * export (opens the shared CnMassExportDialog export launcher configured\n * via the action's `entities[]` / `formats[]` — Wave 1 of\n * nextcloud-vue#91; the host page provides `context.openExport`) |\n * open-form (Wave 3 — schema-driven create dialog; the rendering host\n * provides `context.openForm`) | refresh (Wave 3 — bumps the page-level\n * refresh signal on the `cn:page:refresh` event-bus channel) | api-call\n * (Wave 3 — POST/PUT a configured app endpoint with success/error toasts\n * and an automatic page refresh; any `confirm` on the action is INTENT\n * consumed by the rendering surface BEFORE dispatch, like object-op) |\n * agent (run a governed hermiq agent against the page object via\n * POST /apps/hermiq/api/agents/{agent}/run-on-object — hermiq#41; a\n * first-class companion to api-call that resolves the register/schema/\n * objectId context for the author and fail-closes when hermiq is absent) |\n * run-node (manifest-run-node-action — invoke ONE OpenRegister flow node\n * directly against a subject object via\n * POST /api/flows/{flowId}/nodes/{nodeId}/run; the rendering surface\n * provides context.openRunNode, mirroring open-form, and resolves the\n * node's own declared config form before opening a dialog — see or-flow-run-node).\n *\n * `api-call`'s request body prefers `payload` (DEEP @-token resolution at\n * any nesting depth — object/array, e.g. a Filinq-style\n * `{ dataRefs: [{ id: '@objectId' }] }` body) over the legacy `params`\n * (shallow, one-level filter-map resolution, kept for back-compat). Set\n * `download: true` to request a binary blob response and trigger a\n * browser file download instead of a JSON toast+refresh cycle (filename:\n * `Content-Disposition` header, else the token-resolved `filename`, else\n * `'download.pdf'`; no auto-refresh unless `refresh: true` is explicit).\n *\n * `toggle` (Wave 3) is deliberately NOT dispatchable: a toggle is a\n * stateful two-way control (GET state on mount, write on click) rendered\n * by the header-actions surface (CnActionButtons); dispatching it here\n * warns and no-ops.\n *\n * Spec: REQ-MVR-011 (manifest-v2-renderer) — unified actions dispatcher\n * / ADR-036 Decision 7 / ADR-049 Decision 2 / #91 Wave 3\n */\n\nimport { emit } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { parseDispositionFilename, triggerBlobDownload } from '../components/CnIndexPage/selfModeIO.js'\nimport { interpolateUrlTokens } from '../composables/useEndpointSource.js'\nimport { routeHref } from './actionLink.js'\nimport {\n\tdropOptionalUnresolved,\n\tdropOptionalUnresolvedDeep,\n\thasUnresolvedDeepTokens,\n\thasUnresolvedTokens,\n\tresolveDeepTokens,\n\tresolveFilterTokens,\n} from './resolveFilterTokens.js'\nimport { schemaRefSlug } from './schemaRefSlug.js'\n\n/** Event-bus channel the page-level Refresh signal broadcasts on (Wave 2). */\nconst PAGE_REFRESH_CHANNEL = 'cn:page:refresh'\n\n/**\n * Whether a `navigate` target leaves the app: it is `http(s)://`,\n * protocol-relative (`//`), or a `mailto:` / `tel:` link. Anything else is an\n * in-app path for the router.\n *\n * vue-router happily accepts an absolute URL as a PATH — `router.push` on\n * `https://youtu.be/x?v=1` matches no route and lands on the fallback with the\n * query carried over, which reads as the app ignoring the link.\n *\n * An allowlist, not a generic `<scheme>://` match: manifests are\n * admin-editable app-wide config, and an external target renders into an\n * anchor `href` and a dispatcher `window.open` — a generic scheme match would\n * accept `javascript://` too.\n *\n * @param {unknown} target The action target to test.\n * @return {boolean} True when the target is external.\n */\nexport function isExternalActionTarget(target) {\n\tif (typeof target !== 'string') {\n\t\treturn false\n\t}\n\treturn /^\\/\\//.test(target) || /^(https?|mailto|tel):/i.test(target)\n}\n\n/**\n * Resolve the object-store type slug for a widget `source` register/schema\n * pair. Reuses an already-registered type whose config matches (so an app's\n * own caches stay coherent); otherwise registers a deterministic\n * `<register>/<schema>` slug on the fly.\n *\n * `source.schema` is routed through {@link schemaRefSlug} before it ever\n * reaches the store or the objects API: a caller that passes a `$ref`\n * schema TITLE (`ReportPeriod`) rather than its slug (`report-period`)\n * would otherwise register — and fetch — a 404 (defect 7, learniq round\n * 1). Idempotent for a caller that already passes a correct slug.\n *\n * @param {object} store The object store instance (useObjectStore shape).\n * @param {{register: (string|number), schema: (string|number)}} source The widget source.\n * @return {string} The type slug to use for store CRUD calls.\n */\nexport function resolveObjectOpType(store, source) {\n\tconst register = String(source.register)\n\tconst schema = String(schemaRefSlug(source.schema))\n\tconst registry = store.objectTypeRegistry || {}\n\tfor (const [slug, config] of Object.entries(registry)) {\n\t\tif (!config) {\n\t\t\tcontinue\n\t\t}\n\t\tif ((String(config.register) === register && String(config.schema) === schema)\n\t\t\t|| (config.registerSlug === register && config.schemaSlug === schema)) {\n\t\t\treturn slug\n\t\t}\n\t}\n\tconst slug = `${register}/${schema}`\n\tif (!registry[slug]) {\n\t\tstore.registerObjectType(slug, schema, register)\n\t}\n\treturn slug\n}\n\n/**\n * Extract an OpenRegister object's id from a row (top-level `id` wins,\n * `@self.id` metadata fallback).\n *\n * @param {object} row The row object.\n * @return {string|number|null} The object id, or null when absent.\n */\nfunction rowObjectId(row) {\n\tif (!row || typeof row !== 'object') {\n\t\treturn null\n\t}\n\tif (row.id !== undefined && row.id !== null) {\n\t\treturn row.id\n\t}\n\tconst self = row['@self']\n\tif (self && typeof self === 'object' && self.id !== undefined && self.id !== null) {\n\t\treturn self.id\n\t}\n\treturn null\n}\n\n/**\n * Extract a saved OpenRegister object's id for a post-save deep-link:\n * top-level `id`, then `uuid`, then the `@self.id` metadata fallback.\n *\n * @param {object} saved The saved record returned by the object store.\n * @return {string|number|null} The object id, or null when absent.\n */\nexport function savedObjectId(saved) {\n\tif (!saved || typeof saved !== 'object') {\n\t\treturn null\n\t}\n\tif (saved.id !== undefined && saved.id !== null) {\n\t\treturn saved.id\n\t}\n\tif (saved.uuid !== undefined && saved.uuid !== null) {\n\t\treturn saved.uuid\n\t}\n\tconst self = saved['@self']\n\tif (self && typeof self === 'object' && self.id !== undefined && self.id !== null) {\n\t\treturn self.id\n\t}\n\treturn null\n}\n\n/**\n * Resolve a manifest-named FUNCTION out of the v2 registry, falling back to the\n * legacy customComponents map.\n *\n * A manifest names a behaviour the same way it names a component — by string —\n * so every surface that dispatches one (`actions[].handler`,\n * `bulkActions[].handler`, `headerActions[].handler`, `config.createOverride`)\n * has to agree on where that string is looked up and what counts as a match.\n * Recognised shapes, in order:\n * 1. a registry entry exposing the function as `.handler` / `.fn` (the\n * `kind: 'handler'` and `kind: 'create-override'` entries),\n * 2. a directly function-valued registry entry,\n * 3. a function-valued legacy `customComponents` entry.\n *\n * The legacy map stays last so an app that has not migrated is unchanged, and\n * so an app mid-migration can move its handlers over one at a time.\n *\n * @param {string} name The registered handler name.\n * @param {object} registry The v2 component registry.\n * @param {object} customComponents The legacy customComponents map.\n * @return {?((scope?: object) => unknown)} The handler, or null when nothing of that name is a function.\n */\nexport function resolveRegisteredHandler(name, registry, customComponents) {\n\tif (typeof name !== 'string' || name === '') {\n\t\treturn null\n\t}\n\tconst entry = (registry || {})[name]\n\tif (typeof entry === 'function') {\n\t\treturn entry\n\t}\n\tif (entry && typeof entry === 'object') {\n\t\tif (typeof entry.handler === 'function') {\n\t\t\treturn entry.handler\n\t\t}\n\t\tif (typeof entry.fn === 'function') {\n\t\t\treturn entry.fn\n\t\t}\n\t}\n\tconst legacy = (customComponents || {})[name]\n\treturn typeof legacy === 'function' ? legacy : null\n}\n\n/**\n * Resolve a named create-override handler out of the v2 registry (or the\n * legacy customComponents map) by value shape.\n *\n * A create-override is a plain async function that replaces the default\n * `objectStore.saveObject` persist path — needed whenever a schema requires a\n * field the create form cannot supply on its own (a server-minted foreign key,\n * say).\n *\n * Shared by CnPageRenderer (page-level `config.createOverride` → CnIndexPage's\n * prop) and CnActionButtons (per-action `createOverride` on an `open-form`), so\n * the two surfaces cannot drift on what counts as a valid handler.\n *\n * @param {string} name The registered handler name.\n * @param {object} registry The v2 component registry.\n * @param {object} customComponents The legacy customComponents map.\n * @return {?((props?: object) => Promise<unknown>)} The async create handler, or null when unresolved.\n */\nexport function resolveCreateOverrideHandler(name, registry, customComponents) {\n\treturn resolveRegisteredHandler(name, registry, customComponents)\n}\n\n/**\n * Build the vue-router push location for an `open-form` action's\n * `onSuccessRoute`, merging the saved object's id into the route params so\n * the post-save navigation can deep-link to the created object's detail page\n * (#91).\n *\n * `onSuccessRoute` may be:\n * - a STRING route NAME → `{ name, params: { id } }`. The saved id lands\n * under the default `id` param; a route without an `:id` segment simply\n * ignores the extra param, so a bare-name route keeps working unchanged\n * (backward compatible).\n * - an OBJECT `{ name, paramField?, objectParam? }` → the id is placed\n * under `paramField` (default `id`), and — when `objectParam` is set —\n * the whole saved object is passed under that param key too (so a\n * `props: true` detail route can render the record without a refetch).\n *\n * The id is read via {@link savedObjectId} (`saved.id` → `saved.uuid` →\n * `saved['@self'].id`). Returns `null` when no route name is resolvable, so\n * the caller can skip the navigation.\n *\n * @param {(string|{name: string, paramField?: string, objectParam?: string})} onSuccessRoute The action's onSuccessRoute config.\n * @param {object} saved The saved OpenRegister object.\n * @return {{name: string, params: object}|null} The router push location, or null.\n */\nexport function buildOnSuccessRoute(onSuccessRoute, saved) {\n\tconst spec = typeof onSuccessRoute === 'string'\n\t\t? { name: onSuccessRoute }\n\t\t: (onSuccessRoute && typeof onSuccessRoute === 'object' ? onSuccessRoute : null)\n\tif (!spec || typeof spec.name !== 'string' || spec.name === '') {\n\t\treturn null\n\t}\n\tconst params = {}\n\tconst id = savedObjectId(saved)\n\tif (id !== null && id !== undefined) {\n\t\tparams[spec.paramField || 'id'] = id\n\t}\n\tif (spec.objectParam && saved && typeof saved === 'object') {\n\t\tparams[spec.objectParam] = saved\n\t}\n\treturn { name: spec.name, params }\n}\n\n/**\n * Run a manifest-authored toast message through the host translate function.\n *\n * `successMessage` / `errorMessage` are English source strings authored in the\n * manifest (ADR-007), so they need the SAME translator the rest of the page\n * chrome uses — the consumer's bound `t()`, handed to the dispatcher by the\n * rendering surface as `context.translate` (which itself comes from the\n * `cnTranslate` CnAppRoot provides).\n *\n * A pure pass-through: with no translator, no message, or a catalogue that\n * lacks the key, the message is returned byte-identical. Server-supplied\n * messages are data and are never routed through here.\n *\n * @param {unknown} message The manifest-authored message (may be undefined).\n * @param {object} context The dispatch context (`context.translate` optional).\n * @return {unknown} The translated message, or the input unchanged.\n */\nfunction translateMessage(message, context) {\n\tconst fn = context && context.translate\n\tif (!message || typeof fn !== 'function') {\n\t\treturn message\n\t}\n\treturn fn(message)\n}\n\n/**\n * Interpolate the `api-call` URL/filename token grammar inside a string:\n * the `{objectId}` BRACE convention (a literal placeholder some manifests\n * use for path templates, matching the OpenRegister credential-broker path\n * style) as well as the full `@objectId` / `@object.<field>` /\n * `@workspace.<key>` / `@config.<key>` grammar {@link interpolateUrlTokens}\n * already understands. Unresolved `@`-tokens and an unresolved\n * `{objectId}` both collapse to an empty string (never leak a literal\n * placeholder into a request URL or a downloaded filename).\n *\n * @param {string} str The raw string (an action's `url` or `filename`).\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object}} ctx Token context.\n * @return {string} The interpolated string.\n */\nfunction interpolateActionString(str, ctx) {\n\tif (typeof str !== 'string') {\n\t\treturn str\n\t}\n\tconst braced = str.replace(/\\{objectId\\}/g, () => {\n\t\tconst id = ctx.objectId\n\t\treturn (id === undefined || id === null) ? '' : String(id)\n\t})\n\treturn interpolateUrlTokens(braced, ctx)\n}\n\n/**\n * Interpolate an action's `target` URL — the same grammar `url` runs.\n *\n * Exported because the rendering surface resolves a target BEFORE deciding\n * whether it is an external link, and that decision happens there rather than\n * in the dispatcher.\n *\n * @param {string} target The action's raw `target`.\n * @param {object} ctx Token context (`{ objectId?, object?, workspace?, config? }`).\n *\n * @return {string} The interpolated target.\n */\nexport function interpolateActionTarget(target, ctx) {\n\treturn interpolateActionString(target || '', ctx || {})\n}\n\n// The tokens interpolateActionString resolves; a bare `@` or `{` is not one.\nconst ACTION_TARGET_TOKEN = /@(?:objectId|object\\.|page\\.|workspace\\.|config\\.|range\\.)|\\{objectId\\}/\n\n/**\n * Whether an action target carries a token that interpolation would resolve,\n * so it can only be known once the page context is. A `mailto:` address or a\n * URL with an `@handle` in it has none.\n *\n * @param {string} target The action's raw `target`.\n * @return {boolean}\n */\nexport function hasActionTargetTokens(target) {\n\treturn typeof target === 'string' && ACTION_TARGET_TOKEN.test(target)\n}\n\n/**\n * The link an action renders as when its only job is to navigate to a target\n * known at render time: `navigate` (token-interpolated like the dispatcher)\n * and `open-page`. Anything else, a `confirm: true` action, or an in-app\n * target the router cannot resolve answers null and keeps dispatching.\n *\n * `to` is what a plain click hands the router (null for an external URL,\n * which the browser opens in a new tab like the dispatcher's `window.open`).\n *\n * @param {object} action The manifest action.\n * @param {{router?: object, tokenCtx?: object}} [context] The dispatch context.\n * @return {{href: string, to: (string|object|null), external: boolean}|null} The link, or null.\n */\nexport function actionLink(action, context = {}) {\n\tif (!action || typeof action !== 'object' || action.confirm) {\n\t\treturn null\n\t}\n\tif (action.type === 'navigate') {\n\t\tconst target = interpolateActionString(action.target || '', context.tokenCtx || {})\n\t\tif (!target) {\n\t\t\treturn null\n\t\t}\n\t\tif (isExternalActionTarget(target)) {\n\t\t\treturn { href: target, to: null, external: true }\n\t\t}\n\t\tconst href = routeHref(target, context.router)\n\t\treturn href ? { href, to: target, external: false } : null\n\t}\n\tif (action.type === 'open-page') {\n\t\tif (typeof action.target !== 'string' || action.target.length === 0) {\n\t\t\treturn null\n\t\t}\n\t\tconst to = { name: action.target }\n\t\tconst href = routeHref(to, context.router)\n\t\treturn href ? { href, to, external: false } : null\n\t}\n\treturn null\n}\n\n/**\n * Execute a Wave-3 `api-call` action: POST/PUT the configured app endpoint\n * (URL + body run the SAME @-token grammar endpoint sources use), toast the\n * outcome via @nextcloud/dialogs, then — unless `action.refresh` is `false`\n * (or, for a `download` action, unless `action.refresh` is explicitly\n * `true`) — bump the page-level refresh signal so every endpoint-bound\n * widget refetches. Confirm-gating is the RENDERING SURFACE's job (the\n * `confirm` field is intent, object-op precedent): this runs after any\n * confirmation already happened.\n *\n * The request body prefers `action.payload` (DEEP token resolution, any\n * nesting — {@link resolveDeepTokens}) over the legacy `action.params`\n * (shallow, one-level filter-map resolution — unchanged for back-compat).\n * A required token left unresolved anywhere in the body BLOCKS the call\n * (error toast) rather than sending a literal `@objectId` to the server.\n *\n * `action.download === true` requests the response as a binary blob\n * (`responseType: 'blob'`) and triggers a browser file download — the\n * filename comes from the response's `Content-Disposition` header, else\n * the token-resolved `action.filename`, else `'download.pdf'`.\n *\n * Never throws: a failed call resolves `{ ok: false, error }` after the\n * error toast (so a confirm dialog can await the outcome).\n *\n * @param {object} action The api-call action (`url`, `method?`, `payload?`,\n * `params?`, `download?`, `filename?`, `successMessage?`, `errorMessage?`,\n * `refresh?`).\n * @param {object} context Runtime context; `context.tokenCtx` is the token\n * context (`{ objectId?, object?, workspace?, config? }`) the URL/body\n * resolve against.\n * @return {Promise<{ok: boolean, data?: unknown, error?: unknown}>} The call outcome.\n */\nasync function executeApiCall(action, context) {\n\tconst tokenCtx = context.tokenCtx || {}\n\tconst url = interpolateActionString(action.url || '', tokenCtx)\n\n\tconst hasPayload = action.payload && typeof action.payload === 'object' && !Array.isArray(action.payload)\n\tconst body = hasPayload\n\t\t? dropOptionalUnresolvedDeep(resolveDeepTokens(action.payload, tokenCtx))\n\t\t: dropOptionalUnresolved(resolveFilterTokens(action.params || {}, tokenCtx))\n\tconst bodyBlocked = hasPayload ? hasUnresolvedDeepTokens(body) : hasUnresolvedTokens(body)\n\n\tif (!url || bodyBlocked) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn('[dispatchAction] api-call is missing its url or a required token is unresolved — skipping.', action)\n\t\treturn { ok: false, error: new Error('api-call blocked') }\n\t}\n\tconst method = String(action.method || 'POST').toUpperCase() === 'PUT' ? 'put' : 'post'\n\tconst isDownload = action.download === true\n\tconst [{ default: axios }, { generateUrl }, dialogs] = await Promise.all([\n\t\timport('@nextcloud/axios'),\n\t\timport('@nextcloud/router'),\n\t\timport('@nextcloud/dialogs'),\n\t])\n\tconst target = /^https?:\\/\\//i.test(url) ? url : generateUrl(url)\n\ttry {\n\t\t// Only pass a third axios config arg for a download call — an\n\t\t// explicit `undefined` third argument would otherwise change the\n\t\t// call shape for every existing (non-download) api-call consumer/test.\n\t\tconst res = isDownload\n\t\t\t? await axios[method](target, body, { responseType: 'blob' })\n\t\t\t: await axios[method](target, body)\n\t\tif (isDownload) {\n\t\t\tconst filename = parseDispositionFilename(\n\t\t\t\tres && res.headers && res.headers['content-disposition'],\n\t\t\t\tinterpolateActionString(action.filename, tokenCtx) || 'download.pdf',\n\t\t\t)\n\t\t\ttriggerBlobDownload(res.data, filename)\n\t\t}\n\t\tif (typeof dialogs.showSuccess === 'function') {\n\t\t\tdialogs.showSuccess(translateMessage(action.successMessage, context) || t('nextcloud-vue', 'Action completed.'))\n\t\t}\n\t\tconst shouldRefresh = isDownload ? action.refresh === true : action.refresh !== false\n\t\tif (shouldRefresh) {\n\t\t\temit(PAGE_REFRESH_CHANNEL, {})\n\t\t}\n\t\treturn { ok: true, data: res && res.data }\n\t} catch (error) {\n\t\tconst serverMessage = error && error.response && error.response.data\n\t\t\t&& (error.response.data.error || error.response.data.message)\n\t\tif (typeof dialogs.showError === 'function') {\n\t\t\tdialogs.showError(translateMessage(action.errorMessage, context) || serverMessage || t('nextcloud-vue', 'Action failed.'))\n\t\t}\n\t\treturn { ok: false, error }\n\t}\n}\n\n/**\n * Resolve an agent-action object reference (`register` / `schema` / `objectId`):\n * an explicitly-declared action value WINS (it may itself be an @-token string,\n * interpolated against the token context), otherwise it falls back to the page\n * object context default (`tokenCtx.register` / `.schema` / `.objectId`). An\n * unresolved token collapses to an empty string (never leaks a literal `@objectId`\n * to the server); a `{ slug | id }` object (a schema holder) is flattened to its\n * slug/id.\n *\n * @param {unknown} actionVal The action's explicit value (may be undefined or an @-token).\n * @param {unknown} ctxDefault The page-context default (`tokenCtx.<field>`).\n * @param {object} tokenCtx The token context the @-tokens resolve against.\n * @return {string} The resolved reference, or '' when unresolved/absent.\n */\nfunction resolveAgentRef(actionVal, ctxDefault, tokenCtx) {\n\tlet v = actionVal\n\tif (v === undefined || v === null || v === '') {\n\t\tv = ctxDefault\n\t} else if (typeof v === 'string') {\n\t\tv = interpolateActionString(v, tokenCtx)\n\t}\n\tif (v && typeof v === 'object') {\n\t\treturn String(v.slug || v.id || '')\n\t}\n\tif (v === undefined || v === null) {\n\t\treturn ''\n\t}\n\treturn String(v)\n}\n\n/**\n * Execute an `agent` action: run a governed hermiq agent against the page's\n * OpenRegister object. Resolves the object context (`register` / `schema` /\n * `objectId` — explicit action fields override, else default to the page's\n * `@register` / `@schema` / `@objectId` context) and POSTs\n * `{ register, schema, objectId, resultField?, skill?, prompt? }` to hermiq's\n * object-RBAC-scoped run endpoint `/apps/hermiq/api/agents/{agent}/run-on-object`\n * (hermiq#41), which dispatches the governed `AgentRunRequestedEvent`.\n *\n * A first-class companion to `type:\"api-call\"` — the author declares \"run agent\n * here\" without hand-wiring the URL/body. `type:\"api-call\"` remains the escape\n * hatch for a bespoke body.\n *\n * Fail-closed. An unresolved REQUIRED `@objectId` (or a missing agent/register/\n * schema) BLOCKS the call (warn) rather than POSTing a literal token. On the\n * server's 202 it toasts `successMessage` (default 'Run queued') and — unless\n * `refresh: false` — bumps the page refresh signal. A 403 / 404 with a structured\n * error body fail-closes with that message; a 404 with NO structured body is read\n * as \"hermiq absent\" and toasts a graceful 'agent runtime unavailable' — the\n * dispatcher NEVER hard-requires hermiq to be installed. Never throws.\n *\n * @param {object} action The agent action (`agent`, `skill?`, `prompt?`,\n * `resultField?`, `register?`, `schema?`, `objectId?`, `successMessage?`,\n * `errorMessage?`, `refresh?`).\n * @param {object} context Runtime context; `context.tokenCtx` is the token\n * context (`{ objectId?, object?, register?, schema?, workspace?, config? }`).\n * @return {Promise<{ok: boolean, data?: unknown, error?: unknown}>} The call outcome.\n */\nasync function executeAgentAction(action, context) {\n\tconst tokenCtx = context.tokenCtx || {}\n\tconst agent = resolveAgentRef(action.agent, undefined, tokenCtx)\n\tconst register = resolveAgentRef(action.register, tokenCtx.register, tokenCtx)\n\tconst schema = resolveAgentRef(action.schema, tokenCtx.schema, tokenCtx)\n\tconst objectId = resolveAgentRef(action.objectId, tokenCtx.objectId, tokenCtx)\n\n\tif (!agent || !register || !schema || !objectId) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn('[dispatchAction] agent action is missing its agent or a required object-context token (register/schema/objectId) is unresolved — skipping.', action)\n\t\treturn { ok: false, error: new Error('agent action blocked') }\n\t}\n\n\tconst body = { register, schema, objectId }\n\tconst resultField = resolveAgentRef(action.resultField, undefined, tokenCtx)\n\tif (resultField) {\n\t\tbody.resultField = resultField\n\t}\n\tconst skill = resolveAgentRef(action.skill, undefined, tokenCtx)\n\tif (skill) {\n\t\tbody.skill = skill\n\t}\n\tif (typeof action.prompt === 'string' && action.prompt !== '') {\n\t\tbody.prompt = interpolateActionString(action.prompt, tokenCtx)\n\t}\n\n\tconst [{ default: axios }, { generateUrl }, dialogs] = await Promise.all([\n\t\timport('@nextcloud/axios'),\n\t\timport('@nextcloud/router'),\n\t\timport('@nextcloud/dialogs'),\n\t])\n\tconst target = generateUrl(`/apps/hermiq/api/agents/${encodeURIComponent(agent)}/run-on-object`)\n\ttry {\n\t\tconst res = await axios.post(target, body)\n\t\tif (typeof dialogs.showSuccess === 'function') {\n\t\t\tdialogs.showSuccess(translateMessage(action.successMessage, context) || t('nextcloud-vue', 'Run queued'))\n\t\t}\n\t\tif (action.refresh !== false) {\n\t\t\temit(PAGE_REFRESH_CHANNEL, {})\n\t\t}\n\t\treturn { ok: true, data: res && res.data }\n\t} catch (error) {\n\t\tconst response = error && error.response\n\t\tconst status = response && response.status\n\t\tconst serverMessage = response && response.data\n\t\t\t&& (response.data.error || response.data.message)\n\t\t// A 404 with NO structured error body = hermiq is not installed (the app\n\t\t// route 404s at the NC level) — surface a graceful \"runtime unavailable\"\n\t\t// rather than a fail-closed authorization message.\n\t\tconst hermiqAbsent = status === 404 && !serverMessage\n\t\tif (typeof dialogs.showError === 'function') {\n\t\t\tconst msg = hermiqAbsent\n\t\t\t\t? t('nextcloud-vue', 'Agent runtime unavailable')\n\t\t\t\t: (translateMessage(action.errorMessage, context) || serverMessage || t('nextcloud-vue', 'Action failed.'))\n\t\t\tdialogs.showError(msg)\n\t\t}\n\t\treturn { ok: false, error }\n\t}\n}\n\n/**\n * POST the run-node call: `POST /apps/openregister/api/flows/{flowId}/nodes/{nodeId}/run`.\n *\n * This is the SECOND half of `run-node` (manifest-run-node-action / or-flow-run-node) —\n * the first half is `context.openRunNode` in the `dispatchAction` switch below, which\n * only OPENS the config dialog (or decides none is needed) and never itself waits for\n * the person. Once the rendering surface has a `config` object (from the dialog's\n * submit, or `{}` when the node's declared form was empty), it calls this directly —\n * it is not reached through `dispatchAction`'s switch, because by that point there is\n * no further \"type\" dispatch to do, only the call itself, same as `executeAgentAction`\n * is called directly by `case 'api-call'`'s sibling rather than re-entering the switch.\n *\n * Fail-closed exactly like `executeAgentAction`: an unresolved REQUIRED subject\n * (`action.subject`, defaulting to the page's `@objectId`) or a missing\n * `flowId`/`nodeId` BLOCKS the call (warn) rather than POSTing a literal token or an\n * incomplete subject reference. The subject sent is `{ uuid, register, schema }` —\n * OpenRegister's `FlowNodeRunController::resolveAuthorizedSubject()` needs all three\n * to resolve the object and evaluate the caller's object-RBAC permission on it; `register`\n * / `schema` default to the page's own `@register` / `@schema` context, the same\n * defaulting `agent` already uses.\n *\n * A 403 here means the caller holds no update permission on the subject\n * (OpenRegister's object-RBAC, evaluated server-side — RN-1(c)) — this function does not\n * special-case it beyond surfacing the server's message, the same fail-closed handling\n * `api-call` / `agent` already give a rejected call.\n *\n * @param {object} action The run-node action (`flowId`, `nodeId`, `subject?`,\n * `register?`, `schema?`, `successMessage?`, `errorMessage?`, `refresh?`).\n * @param {object} context Runtime context — needs `context.tokenCtx` and, for a\n * localised toast, `context.translate`.\n * @param {object} [config] The node's collected config (from its `configForm()`\n * dialog), or `{}` for a node with no declared fields.\n * @return {Promise<{ok: boolean, data?: object, error?: Error}>} The call outcome —\n * `data` is the created `FlowRun` on success.\n *\n * @spec openspec/changes/manifest-run-node-action/specs/manifest-run-node-action/spec.md#requirement-a-run-node-action-invokes-one-flow-node-against-the-page-object\n */\nexport async function postRunNode(action, context, config = {}) {\n\tconst tokenCtx = context.tokenCtx || {}\n\tconst flowId = resolveAgentRef(action.flowId, undefined, tokenCtx)\n\tconst nodeId = resolveAgentRef(action.nodeId, undefined, tokenCtx)\n\tconst uuid = resolveAgentRef(action.subject, tokenCtx.objectId, tokenCtx)\n\tconst register = resolveAgentRef(action.register, tokenCtx.register, tokenCtx)\n\tconst schema = resolveAgentRef(action.schema, tokenCtx.schema, tokenCtx)\n\n\tif (!flowId || !nodeId || !uuid || !register || !schema) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn('[dispatchAction] run-node is missing flowId/nodeId or a required subject token (objectId/register/schema) is unresolved — skipping.', action)\n\t\treturn { ok: false, error: new Error('run-node blocked') }\n\t}\n\n\tconst [{ default: axios }, { generateUrl }, dialogs] = await Promise.all([\n\t\timport('@nextcloud/axios'),\n\t\timport('@nextcloud/router'),\n\t\timport('@nextcloud/dialogs'),\n\t])\n\tconst target = generateUrl(`/apps/openregister/api/flows/${encodeURIComponent(flowId)}/nodes/${encodeURIComponent(nodeId)}/run`)\n\ttry {\n\t\tconst res = await axios.post(target, { subject: { uuid, register, schema }, config: config || {} })\n\t\tif (typeof dialogs.showSuccess === 'function') {\n\t\t\tdialogs.showSuccess(translateMessage(action.successMessage, context) || t('nextcloud-vue', 'Run completed.'))\n\t\t}\n\t\tif (action.refresh !== false) {\n\t\t\temit(PAGE_REFRESH_CHANNEL, {})\n\t\t}\n\t\treturn { ok: true, data: res && res.data }\n\t} catch (error) {\n\t\tconst response = error && error.response\n\t\tconst serverMessage = response && response.data\n\t\t\t&& (response.data.error || response.data.message)\n\t\tif (typeof dialogs.showError === 'function') {\n\t\t\tdialogs.showError(translateMessage(action.errorMessage, context) || serverMessage || t('nextcloud-vue', 'Action failed.'))\n\t\t}\n\t\treturn { ok: false, error }\n\t}\n}\n\n/**\n * Dispatch a v2 manifest action.\n *\n * @param {object} action The action object from the manifest.\n * @param {string} [action.type] Dispatch type: \"handler\" | \"open-modal\" | \"open-page\" | \"navigate\" |\n * \"object-op\" | \"export\" | \"open-form\" | \"refresh\" | \"api-call\" | \"agent\".\n * When absent, treated as \"handler\" for v1 backward compatibility.\n * @param {string} [action.handler] Registry key for \"handler\" type. For \"export\": the optional\n * handler invoked by the host with the dialog's confirm payload (`{ format, entity }`).\n * @param {Array} [action.args] Arguments spread to the handler function.\n * @param {string} [action.target] Target for \"open-modal\", \"open-page\", and \"navigate\" types.\n * @param {object} [action.props] Props forwarded to modal for \"open-modal\" type.\n * @param {string} [action.op] \"object-op\" only: mutation verb — \"patch\" | \"delete\" | \"create\".\n * @param {object} [action.values] \"object-op\" only: the verb's payload — the partial object\n * merged over the row for \"patch\", the new object's properties for \"create\"; ignored for \"delete\".\n * @param {boolean} [action.confirm] \"object-op\" only: confirm-gating INTENT consumed by the\n * rendering host (CnWidgetObjectTable routes it through CnConfirmDialog) — the dispatcher\n * itself never gates; it is called after any confirmation already happened.\n * @param {Array<{id: string, label: string}>} [action.entities] \"export\" only: selectable\n * entity types offered by the export launcher (e.g. Leads / Requests). Optional — when\n * absent the dialog shows only the format picker.\n * @param {Array<string|{id: string, label: string}>} [action.formats] \"export\" only: the\n * offered export formats (bare ids like \"csv\" or full `{id, label}` entries). Optional —\n * the dialog's built-in Excel/CSV defaults apply when absent.\n * @param {string} [action.description] \"export\" only: pre-translated description shown\n * above the pickers.\n * @param {string} [action.url] \"api-call\" only: the app endpoint — app-relative (routed\n * through generateUrl) or absolute. May interpolate the shared URL tokens\n * (`@objectId`, `@object.<field>`, `@workspace.<key>`, `@config.<key>`, and the\n * `{objectId}` brace form).\n * @param {string} [action.method] \"api-call\" only: \"POST\" (default) | \"PUT\".\n * @param {object} [action.payload] \"api-call\" only: the JSON body — preferred over `params`.\n * Values resolve the shared `@`-token grammar RECURSIVELY at any nesting depth (objects\n * and arrays of objects, e.g. `{ dataRefs: [{ id: '@objectId' }] }`); optional (`…?`)\n * tokens drop when unresolved anywhere in the tree, an unresolved REQUIRED token blocks\n * the call. When set, `params` is ignored.\n * @param {object} [action.params] \"api-call\" only: legacy JSON body (ignored when `payload`\n * is set). Values pass the shared filter-token grammar ONE level deep; optional (`…?`)\n * tokens drop when unresolved, an unresolved REQUIRED token blocks the call.\n * @param {boolean} [action.download] \"api-call\" only: request the response as a binary blob\n * (`responseType: 'blob'`) and trigger a browser file download instead of a JSON\n * toast+refresh cycle. Default `false`.\n * @param {string} [action.filename] \"api-call\" `download: true` only: fallback filename when\n * the response carries no `Content-Disposition` header (falls back further to\n * `'download.pdf'`). Interpolates the same token grammar as `url`.\n * @param {string} [action.successMessage] \"api-call\" / \"open-form\": pre-translated success\n * toast text (a library default applies when absent).\n * @param {string} [action.errorMessage] \"api-call\" / \"open-form\": pre-translated error toast\n * text (falls back to the server's `error`/`message`, then a library default).\n * @param {boolean} [action.refresh] \"api-call\" / \"agent\" only: bump the `cn:page:refresh` signal\n * after a successful call. Default `true` — EXCEPT for a `download: true` api-call, whose\n * default is `false` (a file download normally shouldn't also force-refetch every widget); set\n * the flag explicitly either way to override.\n * @param {string} [action.agent] \"agent\" only (REQUIRED): the hermiq agent uuid to run against\n * the page object. POSTed to `/apps/hermiq/api/agents/{agent}/run-on-object` (hermiq#41).\n * @param {string} [action.skill] \"agent\" only: optional skill id folded into the governed run.\n * @param {string} [action.prompt] \"agent\" only: optional prompt (interpolates the shared `@`-token\n * grammar inline — `@objectId`, `@object.<field>`, `@workspace.<key>`, `@config.<key>`).\n * @param {string} [action.resultField] \"agent\" only: object field the agent's result is written to.\n * @param {string} [action.register] \"agent\" / \"open-form\": OpenRegister register slug (agent:\n * defaults to the page object context's `@register`).\n * @param {string} [action.schema] \"agent\" / \"open-form\": OpenRegister schema slug (agent: defaults\n * to the page object context's `@schema`).\n * @param {string} [action.objectId] \"agent\" only: the target object id (defaults to the page\n * object context's `@objectId`). An unresolved required token BLOCKS the call (fail-closed).\n *\n * @param {object} context Runtime context.\n * @param {object} [context.router] Vue Router instance. Required for \"open-page\" and \"navigate\".\n * @param {object} [context.registry] Component registry (Record<string, { kind, component }>).\n * Required for \"open-modal\" type.\n * @param {object} [context.handlers] Map of handler name → function. Required for \"handler\" type.\n * @param {(key: string, props?: object) => void} [context.openModal] Opens a modal.\n * Required for \"open-modal\" type.\n * @param {(action: object) => void} [context.openExport] Opens the shared\n * CnMassExportDialog configured from the action. Required for \"export\" type —\n * CnPageRenderer pre-binds it in the `cnDispatchAction` context.\n * @param {(action: object) => void} [context.openForm] Opens the schema-driven\n * create dialog. Required for \"open-form\" type — the rendering surface\n * (CnActionButtons) provides it, mirroring `openExport`. On a successful save the\n * surface navigates to `action.onSuccessRoute` (a route NAME string, or\n * `{name, paramField?, objectParam?}`) via {@link buildOnSuccessRoute}, which merges\n * the saved object's id into the route params so the navigation can deep-link to the\n * created object.\n * @param {(action: object) => void} [context.openRunNode] Opens the\n * node-config dialog (or runs immediately for an empty config form) and\n * POSTs to OpenRegister's direct-invoke endpoint on submit. Required for\n * \"run-node\" type — the rendering surface (CnActionButtons) provides it,\n * mirroring `openForm`.\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object}} [context.tokenCtx]\n * Token context \"api-call\" URLs/params resolve against (the same shape\n * `resolveFilterTokens` / `interpolateUrlTokens` take).\n * @param {(app: string, text: string, vars?: object) => string} [context.translate] The consumer's bound `t()`, the same\n * `cnTranslate` CnAppRoot provides to the page chrome. Applied to the\n * manifest-authored `successMessage` / `errorMessage` of \"api-call\" and\n * \"agent\" so their toasts follow the user's language. Omitted (or a\n * catalogue miss) leaves the message byte-identical.\n * @param {object} [context.objectStore] Object store instance (useObjectStore shape).\n * Required for \"object-op\" type. All mutations go through `saveObject` / `deleteObject`\n * so store caches (and their no-mutation-on-error semantics) apply.\n * @param {{register: (string|number), schema: (string|number)}} [context.source] The widget's\n * declarative source the mutation targets. Required for \"object-op\" type.\n * @param {object} [context.row] The row object a row-scoped \"object-op\" (patch/delete) mutates.\n *\n * @return {Promise<object|boolean|null>|undefined} For \"object-op\": the store call's promise —\n * the saved object (or `null` on a rejected write) for patch/create, `true`/`false` for\n * delete. The store only mutates its local caches on SUCCESS, so a backend (RBAC) rejection\n * surfaces via the store's `errors[type]` without any local state change. For \"api-call\":\n * a promise of `{ ok, data?, error? }` (toasts + refresh already handled). All other types\n * return undefined.\n */\nexport function dispatchAction(action, context = {}) {\n\tif (!action || typeof action !== 'object') {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn('[dispatchAction] Received invalid action:', action)\n\t\treturn\n\t}\n\n\t// Missing type is treated as \"handler\" for v1 backward compatibility\n\tconst type = action.type || 'handler'\n\n\tswitch (type) {\n\t\tcase 'handler': {\n\t\t\tconst handlerName = action.handler\n\t\t\tconst handlers = context.handlers || {}\n\n\t\t\tif (!handlerName || typeof handlers[handlerName] !== 'function') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[dispatchAction] Handler \"${handlerName}\" not found in context.handlers.`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\thandlers[handlerName](...(action.args ?? []))\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'open-modal': {\n\t\t\tconst target = action.target\n\t\t\tconst registry = context.registry || {}\n\t\t\tconst entry = registry[target]\n\n\t\t\tif (!entry) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[dispatchAction] open-modal target \"${target}\" not found in registry.`)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (entry.kind !== 'modal') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[dispatchAction] open-modal target \"${target}\" has kind \"${entry.kind}\" (expected \"modal\").`)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (typeof context.openModal !== 'function') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] open-modal requires context.openModal to be a function.')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tcontext.openModal(target, action.props ?? {})\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'open-page': {\n\t\t\tif (!context.router) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] open-page requires context.router to be a Vue Router instance.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontext.router.push({ name: action.target })\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'navigate': {\n\t\t\t// A target is a URL exactly as `url` is, so it runs the same token\n\t\t\t// grammar. Without this a manifest's `{objectId}` reached the router\n\t\t\t// as those nine literal characters and landed in the address bar.\n\t\t\tconst target = interpolateActionString(action.target || '', context.tokenCtx || {})\n\n\t\t\t// An external target is not a route. Rendering surfaces should give it\n\t\t\t// to the browser as a real link (CnActionButtons does); this is the\n\t\t\t// fallback for a programmatic dispatch, and it must not reach the\n\t\t\t// router — see isExternalActionTarget.\n\t\t\tif (isExternalActionTarget(target)) {\n\t\t\t\twindow.open(target, '_blank', 'noopener,noreferrer')\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif (!context.router) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] navigate requires context.router to be a Vue Router instance.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontext.router.push(target)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'export': {\n\t\t// Export launcher (Wave 1, nextcloud-vue#91): the host page opens the\n\t\t// shared CnMassExportDialog configured via the action's entities[] /\n\t\t// formats[]; the dialog's confirm payload routes to the action's\n\t\t// optional `handler` (the app's export service does the download).\n\t\t\tif (typeof context.openExport !== 'function') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] export requires context.openExport to be a function.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontext.openExport(action)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'open-form': {\n\t\t// Schema-driven create dialog (Wave 3, nextcloud-vue#91): the\n\t\t// rendering surface (CnActionButtons) mounts the shared\n\t\t// CnAdvancedFormDialog and handles the save — mirroring how\n\t\t// `export` delegates to the host's CnMassExportDialog.\n\t\t\tif (typeof context.openForm !== 'function') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] open-form requires context.openForm to be a function.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontext.openForm(action)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'run-node': {\n\t\t// Direct flow-node invocation (manifest-run-node-action / RN-4):\n\t\t// SAME shape as open-form, deliberately — the rendering surface\n\t\t// (CnActionButtons) resolves the node's own config form, opens a\n\t\t// generic dialog when it has fields (skips it when empty), and\n\t\t// POSTs subject+config to OpenRegister's\n\t\t// /api/flows/{flowId}/nodes/{nodeId}/run on submit. This case\n\t\t// resolves as soon as the dialog OPENS (or the empty-form call\n\t\t// starts) — it does NOT wait for the person to answer, matching\n\t\t// every other consumer's assumption that dispatchAction's Promise\n\t\t// means \"the call happened\", not \"the human decided\" (see design.md\n\t\t// RN-4 for why an awaiting-on-UI shape was rejected).\n\t\t\tif (typeof context.openRunNode !== 'function') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] run-node requires context.openRunNode to be a function.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontext.openRunNode(action)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'refresh': {\n\t\t// Page-level refresh (Wave 3, nextcloud-vue#91): bump the SAME\n\t\t// `cn:page:refresh` event-bus signal the page overflow menu's\n\t\t// Refresh item broadcasts — every endpoint-bound / bus-subscribed\n\t\t// widget on the page force-refetches past its shared cache.\n\t\t\temit(PAGE_REFRESH_CHANNEL, {})\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'api-call': {\n\t\t// POST/PUT an app endpoint + toast + refresh (Wave 3). Any\n\t\t// `confirm` on the action is INTENT the rendering surface consumed\n\t\t// BEFORE calling the dispatcher (object-op precedent) — no gating\n\t\t// happens here.\n\t\t\treturn executeApiCall(action, context)\n\t\t}\n\n\t\tcase 'agent': {\n\t\t// Run a governed hermiq agent against the page object (hermiq#41). A\n\t\t// first-class companion to api-call: resolves the object context\n\t\t// (register/schema/objectId) and POSTs run-on-object. Any `confirm`\n\t\t// on the action is INTENT the rendering surface consumed BEFORE\n\t\t// dispatch (api-call / object-op precedent) — no gating here. hermiq\n\t\t// is NOT hard-required: an app-level 404 surfaces a graceful toast.\n\t\t\treturn executeAgentAction(action, context)\n\t\t}\n\n\t\tcase 'toggle': {\n\t\t// A toggle is a stateful two-way control (GET state on mount,\n\t\t// write on click) — it is RENDERED by the header-actions surface\n\t\t// (CnActionButtons), never dispatched as a one-shot action.\n\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn('[dispatchAction] \"toggle\" is a stateful header-actions control rendered by CnActionButtons — it cannot be dispatched.')\n\t\t\treturn\n\t\t}\n\n\t\tcase 'object-op': {\n\t\t// Declarative mutation of an OpenRegister object, dispatched via the\n\t\t// shared object store (ADR-049). The manifest declares INTENT only:\n\t\t// authorization-shaped fields on the action (`role`, `allow`, …) are\n\t\t// NEVER consulted here — OpenRegister RBAC is the single authority\n\t\t// (ADR-022 / ADR-023) and a forbidden mutation is rejected server-side.\n\t\t// The store mutates its caches only on success, so a rejected write\n\t\t// surfaces as an error with no local state change.\n\t\t\tconst op = action.op\n\t\t\tif (op !== 'patch' && op !== 'delete' && op !== 'create') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[dispatchAction] object-op has invalid op \"${op}\" (expected patch | delete | create).`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst store = context.objectStore\n\t\t\tif (!store || typeof store.saveObject !== 'function' || typeof store.deleteObject !== 'function') {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] object-op requires context.objectStore (useObjectStore shape).')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst source = context.source\n\t\t\tif (!source || !source.register || !source.schema) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[dispatchAction] object-op requires context.source with register + schema.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst type = resolveObjectOpType(store, source)\n\n\t\t\tif (op === 'create') {\n\t\t\t\treturn store.saveObject(type, { ...(action.values || {}) })\n\t\t\t}\n\n\t\t\tconst row = context.row\n\t\t\tconst id = rowObjectId(row)\n\t\t\tif (id === null) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[dispatchAction] object-op \"${op}\" is row-scoped and requires context.row with an id.`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (op === 'delete') {\n\t\t\t\treturn store.deleteObject(type, id)\n\t\t\t}\n\t\t\t// patch: the row's object merged with the action's values.\n\t\t\treturn store.saveObject(type, { ...row, ...(action.values || {}), id })\n\t\t}\n\n\t\tdefault:\n\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn(`[dispatchAction] Unknown action type \"${type}\".`)\n\t}\n}\n"],"names":["t"],"mappings":"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"}
|
|
@@ -70,8 +70,11 @@ function resolveHref(target, router) {
|
|
|
70
70
|
* click is routed through the router; a modified click, or one a handler
|
|
71
71
|
* already prevented, is left to the browser.
|
|
72
72
|
*
|
|
73
|
+
* Unlike `resolveHref`, a string target is a router path, so pair the two
|
|
74
|
+
* with a location object (or `{ path }`) for the href and click to agree.
|
|
75
|
+
*
|
|
73
76
|
* @param {MouseEvent} event The click event.
|
|
74
|
-
* @param {string|object} target The router location the link points at.
|
|
77
|
+
* @param {string|object} target The router location the link points at; a string is a router path.
|
|
75
78
|
* @param {object} [router] The app's router.
|
|
76
79
|
* @return {boolean} True when the app navigated.
|
|
77
80
|
*/
|
|
@@ -97,8 +100,8 @@ function followLinkClick(event, target, router) {
|
|
|
97
100
|
*
|
|
98
101
|
* A click that came from a control inside the surface (a button, a link, a
|
|
99
102
|
* checkbox) is left to that control. Opening a new tab marks the event with
|
|
100
|
-
* `
|
|
101
|
-
*
|
|
103
|
+
* `markNewTabHandled`, and an event already marked is skipped, so two
|
|
104
|
+
* listeners on the same click never open two tabs.
|
|
102
105
|
*
|
|
103
106
|
* @param {MouseEvent|KeyboardEvent|null|undefined} event The click event.
|
|
104
107
|
* @param {string|object} target A URL, or a vue-router location.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linkNavigation.js","sources":["../../../src/utils/linkNavigation.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n *\n * linkNavigation — make navigation behave like a link.\n *\n * A control whose only job is to go somewhere should be a real `<a href>`, so\n * it can be middle-clicked, opened in a new tab and copied. Two helpers cover\n * the two shapes that needs:\n *\n * - `followLinkClick` for a real `<a href>` that routes inside the app: a\n * plain click goes through the router, any other click is left to the\n * browser.\n * - `openRowTarget` for a surface that cannot be an `<a>` (a table row, a\n * draggable card): a plain click navigates in place, a ctrl/cmd/shift or\n * middle click opens the target in a new tab. Bind it to both `@click` and\n * `@auxclick`, since a middle click fires only the latter.\n *\n * A string target is a finished URL. Pass a router path as `{ path }` so the\n * router adds the app's base (`/index.php/apps/<app>`) to its href.\n *\n * @module utils/linkNavigation\n */\n\nimport { isFromNestedControl, isNewTabClick, isNewTabHandled, markNewTabHandled } from './rowAuxClick.js'\n\n/**\n * Whether the browser should handle this click itself (new tab, new window,\n * download) instead of the app navigating in place.\n *\n * @param {MouseEvent|KeyboardEvent|null|undefined} event The click event.\n * @return {boolean} True for a modifier key or a non-primary button.\n */\nexport function isModifiedClick(event) {\n\tif (!event) {\n\t\treturn false\n\t}\n\treturn Boolean(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey\n\t\t|| (event.button !== undefined && event.button !== 0))\n}\n\n/**\n * The href for a navigation target. A string is returned as is, so a router\n * path must be passed as `{ path }` to get the app's base.\n *\n * @param {string|object|null|undefined} target A URL, or a vue-router location.\n * @param {object} [router] The app's router, needed for a location.\n * @return {string} The href, or '' when it cannot be resolved.\n */\nexport function resolveHref(target, router) {\n\tif (!target) {\n\t\treturn ''\n\t}\n\tif (typeof target === 'string') {\n\t\treturn target\n\t}\n\tif (!router || typeof router.resolve !== 'function') {\n\t\treturn ''\n\t}\n\ttry {\n\t\treturn router.resolve(target).href || ''\n\t} catch {\n\t\treturn ''\n\t}\n}\n\n/**\n * Click handler for a real `<a href>` that routes inside the app. A plain\n * click is routed through the router; a modified click, or one a handler\n * already prevented, is left to the browser.\n *\n * @param {MouseEvent} event The click event.\n * @param {string|object} target The router location the link points at.\n * @param {object} [router] The app's router.\n * @return {boolean} True when the app navigated.\n */\nexport function followLinkClick(event, target, router) {\n\tif (!target || !router || typeof router.push !== 'function') {\n\t\treturn false\n\t}\n\tif (event && (event.defaultPrevented || isModifiedClick(event))) {\n\t\treturn false\n\t}\n\tif (event) {\n\t\tevent.preventDefault()\n\t}\n\t// vue-router rejects the promise on a blocked or duplicate navigation.\n\trouter.push(target).catch(() => {})\n\treturn true\n}\n\n/**\n * Navigate from a surface that cannot be an `<a>` (a table row, a card),\n * the way a link would: a ctrl/cmd/shift or middle click opens the target\n * in a new tab, a plain click navigates in place.\n *\n * A click that came from a control inside the surface (a button, a link, a\n * checkbox) is left to that control. Opening a new tab marks the event with\n * `
|
|
1
|
+
{"version":3,"file":"linkNavigation.js","sources":["../../../src/utils/linkNavigation.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n *\n * linkNavigation — make navigation behave like a link.\n *\n * A control whose only job is to go somewhere should be a real `<a href>`, so\n * it can be middle-clicked, opened in a new tab and copied. Two helpers cover\n * the two shapes that needs:\n *\n * - `followLinkClick` for a real `<a href>` that routes inside the app: a\n * plain click goes through the router, any other click is left to the\n * browser.\n * - `openRowTarget` for a surface that cannot be an `<a>` (a table row, a\n * draggable card): a plain click navigates in place, a ctrl/cmd/shift or\n * middle click opens the target in a new tab. Bind it to both `@click` and\n * `@auxclick`, since a middle click fires only the latter.\n *\n * A string target is a finished URL. Pass a router path as `{ path }` so the\n * router adds the app's base (`/index.php/apps/<app>`) to its href.\n *\n * @module utils/linkNavigation\n */\n\nimport { isFromNestedControl, isNewTabClick, isNewTabHandled, markNewTabHandled } from './rowAuxClick.js'\n\n/**\n * Whether the browser should handle this click itself (new tab, new window,\n * download) instead of the app navigating in place.\n *\n * @param {MouseEvent|KeyboardEvent|null|undefined} event The click event.\n * @return {boolean} True for a modifier key or a non-primary button.\n */\nexport function isModifiedClick(event) {\n\tif (!event) {\n\t\treturn false\n\t}\n\treturn Boolean(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey\n\t\t|| (event.button !== undefined && event.button !== 0))\n}\n\n/**\n * The href for a navigation target. A string is returned as is, so a router\n * path must be passed as `{ path }` to get the app's base.\n *\n * @param {string|object|null|undefined} target A URL, or a vue-router location.\n * @param {object} [router] The app's router, needed for a location.\n * @return {string} The href, or '' when it cannot be resolved.\n */\nexport function resolveHref(target, router) {\n\tif (!target) {\n\t\treturn ''\n\t}\n\tif (typeof target === 'string') {\n\t\treturn target\n\t}\n\tif (!router || typeof router.resolve !== 'function') {\n\t\treturn ''\n\t}\n\ttry {\n\t\treturn router.resolve(target).href || ''\n\t} catch {\n\t\treturn ''\n\t}\n}\n\n/**\n * Click handler for a real `<a href>` that routes inside the app. A plain\n * click is routed through the router; a modified click, or one a handler\n * already prevented, is left to the browser.\n *\n * Unlike `resolveHref`, a string target is a router path, so pair the two\n * with a location object (or `{ path }`) for the href and click to agree.\n *\n * @param {MouseEvent} event The click event.\n * @param {string|object} target The router location the link points at; a string is a router path.\n * @param {object} [router] The app's router.\n * @return {boolean} True when the app navigated.\n */\nexport function followLinkClick(event, target, router) {\n\tif (!target || !router || typeof router.push !== 'function') {\n\t\treturn false\n\t}\n\tif (event && (event.defaultPrevented || isModifiedClick(event))) {\n\t\treturn false\n\t}\n\tif (event) {\n\t\tevent.preventDefault()\n\t}\n\t// vue-router rejects the promise on a blocked or duplicate navigation.\n\trouter.push(target).catch(() => {})\n\treturn true\n}\n\n/**\n * Navigate from a surface that cannot be an `<a>` (a table row, a card),\n * the way a link would: a ctrl/cmd/shift or middle click opens the target\n * in a new tab, a plain click navigates in place.\n *\n * A click that came from a control inside the surface (a button, a link, a\n * checkbox) is left to that control. Opening a new tab marks the event with\n * `markNewTabHandled`, and an event already marked is skipped, so two\n * listeners on the same click never open two tabs.\n *\n * @param {MouseEvent|KeyboardEvent|null|undefined} event The click event.\n * @param {string|object} target A URL, or a vue-router location.\n * @param {object} [router] The app's router, needed for a location.\n * @return {boolean} True when it navigated.\n */\nexport function openRowTarget(event, target, router) {\n\tif (!target || isNewTabHandled(event) || isFromNestedControl(event)) {\n\t\treturn false\n\t}\n\tif (isNewTabClick(event)) {\n\t\tconst href = resolveHref(target, router)\n\t\tif (!href) {\n\t\t\treturn false\n\t\t}\n\t\twindow.open(href, '_blank', 'noopener,noreferrer')\n\t\tmarkNewTabHandled(event, true)\n\t\treturn true\n\t}\n\t// Only the primary button navigates in place; a right click opens the menu.\n\tif (event && event.button !== undefined && event.button !== 0) {\n\t\treturn false\n\t}\n\tif (typeof target === 'string') {\n\t\twindow.location.assign(target)\n\t\treturn true\n\t}\n\tif (!router || typeof router.push !== 'function') {\n\t\treturn false\n\t}\n\trouter.push(target).catch(() => {})\n\treturn true\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -9,13 +9,17 @@
|
|
|
9
9
|
* also fire `auxclick` for the right button, so a row that listens to it must
|
|
10
10
|
* filter both out itself.
|
|
11
11
|
*
|
|
12
|
-
* Whoever opens a row in a new tab
|
|
13
|
-
* second listener for the same click (a host behind a library default) can
|
|
12
|
+
* Whoever opens a row in a new tab marks the event with `markNewTabHandled`, so
|
|
13
|
+
* a second listener for the same click (a host behind a library default) can
|
|
14
14
|
* check `isNewTabHandled` and not open a second tab.
|
|
15
15
|
*
|
|
16
16
|
* @module utils/rowAuxClick
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
// Events that opened a new tab. Not `defaultPrevented`, which any unrelated
|
|
20
|
+
// handler can set on a modified or middle click.
|
|
21
|
+
const newTabHandled = new WeakSet();
|
|
22
|
+
|
|
19
23
|
const NESTED_CONTROL = 'a, button, input, select, textarea, label, summary, [role="button"], [role="checkbox"], [role="link"], [contenteditable="true"]';
|
|
20
24
|
|
|
21
25
|
/**
|
|
@@ -49,6 +53,21 @@ function isRowMiddleClick(event) {
|
|
|
49
53
|
return Boolean(event && event.button === 1 && !isFromNestedControl(event))
|
|
50
54
|
}
|
|
51
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Cancel a middle-button press on a clickable row or card body, so the
|
|
58
|
+
* browser does not start autoscroll (Chrome/Edge on Windows) instead of the
|
|
59
|
+
* middle click opening the row. A press on a nested control, a link
|
|
60
|
+
* included, keeps its default.
|
|
61
|
+
*
|
|
62
|
+
* @param {MouseEvent} event The mousedown event.
|
|
63
|
+
* @return {void}
|
|
64
|
+
*/
|
|
65
|
+
function preventMiddleClickAutoscroll(event) {
|
|
66
|
+
if (event && event.button === 1 && !isFromNestedControl(event) && typeof event.preventDefault === 'function') {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
52
71
|
/**
|
|
53
72
|
* Whether `openRowTarget` would open this click in a new tab.
|
|
54
73
|
*
|
|
@@ -66,21 +85,27 @@ function isNewTabClick(event) {
|
|
|
66
85
|
* @return {boolean}
|
|
67
86
|
*/
|
|
68
87
|
function isNewTabHandled(event) {
|
|
69
|
-
return Boolean(isNewTabClick(event) && event
|
|
88
|
+
return Boolean(isNewTabClick(event) && newTabHandled.has(event))
|
|
70
89
|
}
|
|
71
90
|
|
|
72
91
|
/**
|
|
73
92
|
* Mark a new-tab click as handled after opening it, see `isNewTabHandled`.
|
|
93
|
+
* Also prevents the event's default, so the browser does not act on the
|
|
94
|
+
* click as well.
|
|
74
95
|
*
|
|
75
96
|
* @param {MouseEvent|KeyboardEvent|null|undefined} event The click event.
|
|
76
97
|
* @param {boolean} opened Whether the target was opened.
|
|
77
98
|
* @return {void}
|
|
78
99
|
*/
|
|
79
100
|
function markNewTabHandled(event, opened) {
|
|
80
|
-
if (opened
|
|
101
|
+
if (!opened || !isNewTabClick(event)) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
newTabHandled.add(event);
|
|
105
|
+
if (typeof event.preventDefault === 'function') {
|
|
81
106
|
event.preventDefault();
|
|
82
107
|
}
|
|
83
108
|
}
|
|
84
109
|
|
|
85
|
-
export { isFromNestedControl, isNewTabClick, isNewTabHandled, isRowMiddleClick, markNewTabHandled };
|
|
110
|
+
export { isFromNestedControl, isNewTabClick, isNewTabHandled, isRowMiddleClick, markNewTabHandled, preventMiddleClickAutoscroll };
|
|
86
111
|
//# sourceMappingURL=rowAuxClick.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rowAuxClick.js","sources":["../../../src/utils/rowAuxClick.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n *\n * rowAuxClick — decide whether an `auxclick` on a clickable row or card is a\n * middle click meant for the row itself.\n *\n * `@click.stop` on a nested control does not stop `auxclick`, and browsers\n * also fire `auxclick` for the right button, so a row that listens to it must\n * filter both out itself.\n *\n * Whoever opens a row in a new tab
|
|
1
|
+
{"version":3,"file":"rowAuxClick.js","sources":["../../../src/utils/rowAuxClick.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n *\n * rowAuxClick — decide whether an `auxclick` on a clickable row or card is a\n * middle click meant for the row itself.\n *\n * `@click.stop` on a nested control does not stop `auxclick`, and browsers\n * also fire `auxclick` for the right button, so a row that listens to it must\n * filter both out itself.\n *\n * Whoever opens a row in a new tab marks the event with `markNewTabHandled`, so\n * a second listener for the same click (a host behind a library default) can\n * check `isNewTabHandled` and not open a second tab.\n *\n * @module utils/rowAuxClick\n */\n\n// Events that opened a new tab. Not `defaultPrevented`, which any unrelated\n// handler can set on a modified or middle click.\nconst newTabHandled = new WeakSet()\n\nconst NESTED_CONTROL = 'a, button, input, select, textarea, label, summary, [role=\"button\"], [role=\"checkbox\"], [role=\"link\"], [contenteditable=\"true\"]'\n\n/**\n * Whether the event started on a control nested inside the element that\n * listens for it (not the listening element itself).\n *\n * @param {Event} event The click or auxclick event.\n * @return {boolean}\n */\nexport function isFromNestedControl(event) {\n\tconst target = event && event.target\n\tconst root = event && event.currentTarget\n\tif (!target || typeof target.closest !== 'function') {\n\t\treturn false\n\t}\n\tconst control = target.closest(NESTED_CONTROL)\n\tif (!control || control === root) {\n\t\treturn false\n\t}\n\treturn !root || typeof root.contains !== 'function' || root.contains(control)\n}\n\n/**\n * Whether an `auxclick` is a middle click on the row body, which should open\n * the row's target in a new tab.\n *\n * @param {MouseEvent} event The auxclick event.\n * @return {boolean}\n */\nexport function isRowMiddleClick(event) {\n\treturn Boolean(event && event.button === 1 && !isFromNestedControl(event))\n}\n\n/**\n * Cancel a middle-button press on a clickable row or card body, so the\n * browser does not start autoscroll (Chrome/Edge on Windows) instead of the\n * middle click opening the row. A press on a nested control, a link\n * included, keeps its default.\n *\n * @param {MouseEvent} event The mousedown event.\n * @return {void}\n */\nexport function preventMiddleClickAutoscroll(event) {\n\tif (event && event.button === 1 && !isFromNestedControl(event) && typeof event.preventDefault === 'function') {\n\t\tevent.preventDefault()\n\t}\n}\n\n/**\n * Whether `openRowTarget` would open this click in a new tab.\n *\n * @param {MouseEvent|KeyboardEvent|null|undefined} event The click event.\n * @return {boolean}\n */\nexport function isNewTabClick(event) {\n\treturn Boolean(event && (event.metaKey || event.ctrlKey || event.shiftKey || event.button === 1))\n}\n\n/**\n * Whether an earlier listener already opened this click in a new tab.\n *\n * @param {MouseEvent|KeyboardEvent|null|undefined} event The click event.\n * @return {boolean}\n */\nexport function isNewTabHandled(event) {\n\treturn Boolean(isNewTabClick(event) && newTabHandled.has(event))\n}\n\n/**\n * Mark a new-tab click as handled after opening it, see `isNewTabHandled`.\n * Also prevents the event's default, so the browser does not act on the\n * click as well.\n *\n * @param {MouseEvent|KeyboardEvent|null|undefined} event The click event.\n * @param {boolean} opened Whether the target was opened.\n * @return {void}\n */\nexport function markNewTabHandled(event, opened) {\n\tif (!opened || !isNewTabClick(event)) {\n\t\treturn\n\t}\n\tnewTabHandled.add(event)\n\tif (typeof event.preventDefault === 'function') {\n\t\tevent.preventDefault()\n\t}\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,aAAa,GAAG,IAAI,OAAO,GAAE;AACnC;AACA,MAAM,cAAc,GAAG,kIAAiI;AACxJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,mBAAmB,CAAC,KAAK,EAAE;AAC3C,CAAC,MAAM,MAAM,GAAG,KAAK,IAAI,KAAK,CAAC,OAAM;AACrC,CAAC,MAAM,IAAI,GAAG,KAAK,IAAI,KAAK,CAAC,cAAa;AAC1C,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,UAAU,EAAE;AACtD,EAAE,OAAO,KAAK;AACd,EAAE;AACF,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,EAAC;AAC/C,CAAC,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,IAAI,EAAE;AACnC,EAAE,OAAO,KAAK;AACd,EAAE;AACF,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,UAAU,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;AAC9E,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,KAAK,EAAE;AACxC,CAAC,OAAO,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,4BAA4B,CAAC,KAAK,EAAE;AACpD,CAAC,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,cAAc,KAAK,UAAU,EAAE;AAC/G,EAAE,KAAK,CAAC,cAAc,GAAE;AACxB,EAAE;AACF,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,aAAa,CAAC,KAAK,EAAE;AACrC,CAAC,OAAO,OAAO,CAAC,KAAK,KAAK,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;AAClG,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,KAAK,EAAE;AACvC,CAAC,OAAO,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACjE,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE;AACjD,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE;AACvC,EAAE,MAAM;AACR,EAAE;AACF,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,EAAC;AACzB,CAAC,IAAI,OAAO,KAAK,CAAC,cAAc,KAAK,UAAU,EAAE;AACjD,EAAE,KAAK,CAAC,cAAc,GAAE;AACxB,EAAE;AACF;;;;"}
|
|
@@ -18,6 +18,20 @@
|
|
|
18
18
|
* @module utils/safeHref
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Whether a URL is protocol-relative the way a browser reads it: leading
|
|
23
|
+
* control characters and spaces are dropped, tabs and newlines are ignored
|
|
24
|
+
* anywhere, and a backslash counts as `/`.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} url The URL.
|
|
27
|
+
* @return {boolean}
|
|
28
|
+
*/
|
|
29
|
+
function isProtocolRelative(url) {
|
|
30
|
+
// eslint-disable-next-line no-control-regex
|
|
31
|
+
const normalized = url.replace(/[\t\n\r]/g, '').replace(/^[\u0000-\u0020]+/, '');
|
|
32
|
+
return /^[/\\]{2}/.test(normalized)
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
/**
|
|
22
36
|
* Validate a URL and return it only if the scheme is safe for `:href`.
|
|
23
37
|
*
|
|
@@ -30,7 +44,8 @@
|
|
|
30
44
|
* - `javascript:...`
|
|
31
45
|
* - `data:...`
|
|
32
46
|
* - `vbscript:...`
|
|
33
|
-
* - Protocol-relative `//attacker.com/...` (ambiguous origin)
|
|
47
|
+
* - Protocol-relative `//attacker.com/...` (ambiguous origin), also as
|
|
48
|
+
* `/\attacker.com` or with leading whitespace
|
|
34
49
|
* - Any other unrecognised scheme
|
|
35
50
|
* - `null`, `undefined`, empty string
|
|
36
51
|
*
|
|
@@ -52,7 +67,7 @@ function safeHref(url) {
|
|
|
52
67
|
}
|
|
53
68
|
// Reject protocol-relative URLs — `//attacker.com/x` looks like a relative
|
|
54
69
|
// path but resolves to an arbitrary origin, bypassing same-origin intent.
|
|
55
|
-
if (url
|
|
70
|
+
if (isProtocolRelative(url)) {
|
|
56
71
|
return '#'
|
|
57
72
|
}
|
|
58
73
|
// Allow root-relative paths (same-origin navigation)
|
|
@@ -97,7 +112,7 @@ function safeImageSrc(url) {
|
|
|
97
112
|
return url
|
|
98
113
|
}
|
|
99
114
|
// Reject protocol-relative
|
|
100
|
-
if (url
|
|
115
|
+
if (isProtocolRelative(url)) {
|
|
101
116
|
return ''
|
|
102
117
|
}
|
|
103
118
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safeHref.js","sources":["../../../src/utils/safeHref.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n *\n * safeHref — URL scheme validation for `:href` bindings.\n *\n * Components that bind user-controlled URLs to `:href` attributes must\n * validate the scheme before rendering. Without this, a malicious data\n * source returning `javascript:alert(1)` or `data:text/html,...` as a\n * URL would execute code when the user clicks the link.\n *\n * This utility returns the original URL when the scheme is safe\n * (`https:`, `http:`, `mailto:`) or when the URL is a relative path\n * starting with `/`. It returns `'#'` for all other inputs including\n * `javascript:`, `data:`, `vbscript:`, protocol-relative (`//`), and any\n * other unrecognised scheme.\n *\n * @module utils/safeHref\n */\n\n/**\n * Validate a URL and return it only if the scheme is safe for `:href`.\n *\n * Safe inputs:\n * - `https://...` and `http://...` absolute URLs\n * - `mailto:...` links\n * - Relative paths starting with `/` but NOT `//` (same-origin)\n *\n * Unsafe inputs (returns `'#'`):\n * - `javascript:...`\n * - `data:...`\n * - `vbscript:...`\n * - Protocol-relative `//attacker.com/...` (ambiguous origin)\n * - Any other unrecognised scheme\n * - `null`, `undefined`, empty string\n *\n * @param {string|null|undefined} url The URL to validate.\n * @return {string} The original URL if safe, or `'#'` otherwise.\n *\n * @example\n * safeHref('https://example.com') // -> 'https://example.com'\n * safeHref('/apps/files') // -> '/apps/files'\n * safeHref('mailto:info@example.com') // -> 'mailto:info@example.com'\n * safeHref('javascript:alert(1)') // -> '#'\n * safeHref('data:text/html,<h1>x</h1>') // -> '#'\n * safeHref('//attacker.com/x') // -> '#'\n * safeHref(null) // -> '#'\n */\nexport function safeHref(url) {\n\tif (!url || typeof url !== 'string') {\n\t\treturn '#'\n\t}\n\t// Reject protocol-relative URLs — `//attacker.com/x` looks like a relative\n\t// path but resolves to an arbitrary origin, bypassing same-origin intent.\n\tif (url
|
|
1
|
+
{"version":3,"file":"safeHref.js","sources":["../../../src/utils/safeHref.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n *\n * safeHref — URL scheme validation for `:href` bindings.\n *\n * Components that bind user-controlled URLs to `:href` attributes must\n * validate the scheme before rendering. Without this, a malicious data\n * source returning `javascript:alert(1)` or `data:text/html,...` as a\n * URL would execute code when the user clicks the link.\n *\n * This utility returns the original URL when the scheme is safe\n * (`https:`, `http:`, `mailto:`) or when the URL is a relative path\n * starting with `/`. It returns `'#'` for all other inputs including\n * `javascript:`, `data:`, `vbscript:`, protocol-relative (`//`), and any\n * other unrecognised scheme.\n *\n * @module utils/safeHref\n */\n\n/**\n * Whether a URL is protocol-relative the way a browser reads it: leading\n * control characters and spaces are dropped, tabs and newlines are ignored\n * anywhere, and a backslash counts as `/`.\n *\n * @param {string} url The URL.\n * @return {boolean}\n */\nfunction isProtocolRelative(url) {\n\t// eslint-disable-next-line no-control-regex\n\tconst normalized = url.replace(/[\\t\\n\\r]/g, '').replace(/^[\\u0000-\\u0020]+/, '')\n\treturn /^[/\\\\]{2}/.test(normalized)\n}\n\n/**\n * Validate a URL and return it only if the scheme is safe for `:href`.\n *\n * Safe inputs:\n * - `https://...` and `http://...` absolute URLs\n * - `mailto:...` links\n * - Relative paths starting with `/` but NOT `//` (same-origin)\n *\n * Unsafe inputs (returns `'#'`):\n * - `javascript:...`\n * - `data:...`\n * - `vbscript:...`\n * - Protocol-relative `//attacker.com/...` (ambiguous origin), also as\n * `/\\attacker.com` or with leading whitespace\n * - Any other unrecognised scheme\n * - `null`, `undefined`, empty string\n *\n * @param {string|null|undefined} url The URL to validate.\n * @return {string} The original URL if safe, or `'#'` otherwise.\n *\n * @example\n * safeHref('https://example.com') // -> 'https://example.com'\n * safeHref('/apps/files') // -> '/apps/files'\n * safeHref('mailto:info@example.com') // -> 'mailto:info@example.com'\n * safeHref('javascript:alert(1)') // -> '#'\n * safeHref('data:text/html,<h1>x</h1>') // -> '#'\n * safeHref('//attacker.com/x') // -> '#'\n * safeHref(null) // -> '#'\n */\nexport function safeHref(url) {\n\tif (!url || typeof url !== 'string') {\n\t\treturn '#'\n\t}\n\t// Reject protocol-relative URLs — `//attacker.com/x` looks like a relative\n\t// path but resolves to an arbitrary origin, bypassing same-origin intent.\n\tif (isProtocolRelative(url)) {\n\t\treturn '#'\n\t}\n\t// Allow root-relative paths (same-origin navigation)\n\tif (url.startsWith('/')) {\n\t\treturn url\n\t}\n\ttry {\n\t\tconst parsed = new URL(url, window.location.origin)\n\t\tif (['https:', 'http:', 'mailto:'].includes(parsed.protocol)) {\n\t\t\treturn url\n\t\t}\n\t} catch {\n\t\t// URL constructor threw — not a valid absolute URL and not a\n\t\t// relative path either; treat as unsafe.\n\t}\n\treturn '#'\n}\n\n/**\n * Validate a URL for use in `src` attributes of `<img>` elements.\n *\n * Allows `https:`, `http:`, and safe `data:image/...` URIs for inline\n * images (PNG, JPEG, GIF, WebP). Rejects all other schemes including\n * `javascript:`, `data:text/html,...`, and protocol-relative URLs.\n *\n * @param {string|null|undefined} url The image URL to validate.\n * @return {string} The original URL if safe, or `''` otherwise.\n *\n * @example\n * safeImageSrc('https://example.com/logo.png') // -> 'https://...'\n * safeImageSrc('data:image/png;base64,iVBOR...') // -> 'data:image/...'\n * safeImageSrc('data:text/html,<script>alert(1)</script>') // -> ''\n * safeImageSrc('javascript:alert(1)') // -> ''\n * safeImageSrc(null) // -> ''\n */\nexport function safeImageSrc(url) {\n\tif (!url || typeof url !== 'string') {\n\t\treturn ''\n\t}\n\t// Allow safe inline image data URIs (png/jpeg/gif/webp only)\n\tif (/^data:image\\/(png|jpe?g|gif|webp);base64,/i.test(url)) {\n\t\treturn url\n\t}\n\t// Reject protocol-relative\n\tif (isProtocolRelative(url)) {\n\t\treturn ''\n\t}\n\ttry {\n\t\tconst parsed = new URL(url, window.location.origin)\n\t\tif (['https:', 'http:'].includes(parsed.protocol)) {\n\t\t\treturn url\n\t\t}\n\t} catch {\n\t\t// Not a valid URL — unsafe\n\t}\n\treturn ''\n}\n\n/**\n * SVG path `d` attribute allowlist validator.\n *\n * Restricts a path string to the characters used by SVG path commands:\n * letters (commands), digits, decimal points, commas, whitespace, and\n * the minus sign. Rejects any string containing other characters (e.g.\n * embedded script, unicode trickery).\n *\n * Returns the empty string for invalid inputs so a `:d` binding receives\n * an inert value rather than an attacker-controlled string.\n *\n * @param {string|null|undefined} pathData The SVG path `d` value.\n * @return {string} The original string if valid, or `''` otherwise.\n *\n * @example\n * safeSvgPath('M12 2 L20 20 Z') // -> 'M12 2 L20 20 Z'\n * safeSvgPath('M0,0<script>') // -> ''\n */\nexport function safeSvgPath(pathData) {\n\tif (!pathData || typeof pathData !== 'string') {\n\t\treturn ''\n\t}\n\t// Allowlist: SVG path command letters, digits, decimal points,\n\t// commas, whitespace, minus (for negative coordinates), and `e`/`E`\n\t// for scientific notation in arc radii.\n\tif (/^[MmLlHhVvCcSsQqTtAaZz0-9.,\\s\\-eE]+$/.test(pathData)) {\n\t\treturn pathData\n\t}\n\treturn ''\n}\n"],"names":[],"mappings":"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"}
|