@massdriver/backstage-plugin 1.0.0
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/LICENSE +202 -0
- package/README.md +196 -0
- package/dist/MassdriverRouter.esm.js +78 -0
- package/dist/MassdriverRouter.esm.js.map +1 -0
- package/dist/api.esm.js +119 -0
- package/dist/api.esm.js.map +1 -0
- package/dist/assets/massdriver-logo-dark.svg +55 -0
- package/dist/assets/massdriver-logo-light.svg +50 -0
- package/dist/components/AttributesColumn.esm.js +95 -0
- package/dist/components/AttributesColumn.esm.js.map +1 -0
- package/dist/components/AttributesSummaryCell.esm.js +103 -0
- package/dist/components/AttributesSummaryCell.esm.js.map +1 -0
- package/dist/components/AuthedIcon.esm.js +26 -0
- package/dist/components/AuthedIcon.esm.js.map +1 -0
- package/dist/components/ConditionsCell.esm.js +48 -0
- package/dist/components/ConditionsCell.esm.js.map +1 -0
- package/dist/components/DisabledAction.esm.js +30 -0
- package/dist/components/DisabledAction.esm.js.map +1 -0
- package/dist/components/EntityMassdriverContent/EntityMassdriverContent.esm.js +80 -0
- package/dist/components/EntityMassdriverContent/EntityMassdriverContent.esm.js.map +1 -0
- package/dist/components/EntityMassdriverContent/index.esm.js +6 -0
- package/dist/components/EntityMassdriverContent/index.esm.js.map +1 -0
- package/dist/components/EntityMassdriverOverviewCard/EntityMassdriverOverviewCard.esm.js +75 -0
- package/dist/components/EntityMassdriverOverviewCard/EntityMassdriverOverviewCard.esm.js.map +1 -0
- package/dist/components/EntityMassdriverOverviewCard/index.esm.js +6 -0
- package/dist/components/EntityMassdriverOverviewCard/index.esm.js.map +1 -0
- package/dist/components/ForkPill.esm.js +84 -0
- package/dist/components/ForkPill.esm.js.map +1 -0
- package/dist/components/InstanceStatusIndicator.esm.js +21 -0
- package/dist/components/InstanceStatusIndicator.esm.js.map +1 -0
- package/dist/components/InstanceStatusPill.esm.js +67 -0
- package/dist/components/InstanceStatusPill.esm.js.map +1 -0
- package/dist/components/MassdriverIcon.esm.js +13 -0
- package/dist/components/MassdriverIcon.esm.js.map +1 -0
- package/dist/components/MassdriverSidebarItem.esm.js +45 -0
- package/dist/components/MassdriverSidebarItem.esm.js.map +1 -0
- package/dist/components/NotFound.esm.js +44 -0
- package/dist/components/NotFound.esm.js.map +1 -0
- package/dist/components/OpenInMassdriverButton.esm.js +24 -0
- package/dist/components/OpenInMassdriverButton.esm.js.map +1 -0
- package/dist/components/PageLayout.esm.js +88 -0
- package/dist/components/PageLayout.esm.js.map +1 -0
- package/dist/components/RouterLinkAdapter.esm.js +12 -0
- package/dist/components/RouterLinkAdapter.esm.js.map +1 -0
- package/dist/components/VersionBadge.esm.js +57 -0
- package/dist/components/VersionBadge.esm.js.map +1 -0
- package/dist/components/grantColumns.esm.js +56 -0
- package/dist/components/grantColumns.esm.js.map +1 -0
- package/dist/components/icons/BundleIcon.esm.js +7 -0
- package/dist/components/icons/BundleIcon.esm.js.map +1 -0
- package/dist/components/icons/FolderOutlinedIcon.esm.js +7 -0
- package/dist/components/icons/FolderOutlinedIcon.esm.js.map +1 -0
- package/dist/components/icons/TokenIcon.esm.js +7 -0
- package/dist/components/icons/TokenIcon.esm.js.map +1 -0
- package/dist/entity.esm.js +23 -0
- package/dist/entity.esm.js.map +1 -0
- package/dist/features/environment/CompareDeploymentsDialog/CompareDeploymentsDialog.esm.js +115 -0
- package/dist/features/environment/CompareDeploymentsDialog/CompareDeploymentsDialog.esm.js.map +1 -0
- package/dist/features/environment/CompareDeploymentsDialog/ResultsStep.esm.js +165 -0
- package/dist/features/environment/CompareDeploymentsDialog/ResultsStep.esm.js.map +1 -0
- package/dist/features/environment/CompareDeploymentsDialog/SelectStep.esm.js +216 -0
- package/dist/features/environment/CompareDeploymentsDialog/SelectStep.esm.js.map +1 -0
- package/dist/features/environment/CompareDeploymentsDialog/compareColumns.esm.js +86 -0
- package/dist/features/environment/CompareDeploymentsDialog/compareColumns.esm.js.map +1 -0
- package/dist/features/environment/CompareDeploymentsDialog/flattenDeploymentComparison.esm.js +57 -0
- package/dist/features/environment/CompareDeploymentsDialog/flattenDeploymentComparison.esm.js.map +1 -0
- package/dist/features/environment/CompareDeploymentsDialog/queries.esm.js +53 -0
- package/dist/features/environment/CompareDeploymentsDialog/queries.esm.js.map +1 -0
- package/dist/features/environment/CompareEnvironmentsDialog/CompareEnvironmentsDialog.esm.js +94 -0
- package/dist/features/environment/CompareEnvironmentsDialog/CompareEnvironmentsDialog.esm.js.map +1 -0
- package/dist/features/environment/CompareEnvironmentsDialog/ComparisonValueCell.esm.js +67 -0
- package/dist/features/environment/CompareEnvironmentsDialog/ComparisonValueCell.esm.js.map +1 -0
- package/dist/features/environment/CompareEnvironmentsDialog/ResultsStep.esm.js +202 -0
- package/dist/features/environment/CompareEnvironmentsDialog/ResultsStep.esm.js.map +1 -0
- package/dist/features/environment/CompareEnvironmentsDialog/SelectStep.esm.js +183 -0
- package/dist/features/environment/CompareEnvironmentsDialog/SelectStep.esm.js.map +1 -0
- package/dist/features/environment/CompareEnvironmentsDialog/compareColumns.esm.js +73 -0
- package/dist/features/environment/CompareEnvironmentsDialog/compareColumns.esm.js.map +1 -0
- package/dist/features/environment/CompareEnvironmentsDialog/flattenComparison.esm.js +70 -0
- package/dist/features/environment/CompareEnvironmentsDialog/flattenComparison.esm.js.map +1 -0
- package/dist/features/environment/CompareEnvironmentsDialog/queries.esm.js +32 -0
- package/dist/features/environment/CompareEnvironmentsDialog/queries.esm.js.map +1 -0
- package/dist/features/environment/EnvironmentGraphPage.esm.js +131 -0
- package/dist/features/environment/EnvironmentGraphPage.esm.js.map +1 -0
- package/dist/features/environment/GraphHeader/EnvironmentDefaults.esm.js +252 -0
- package/dist/features/environment/GraphHeader/EnvironmentDefaults.esm.js.map +1 -0
- package/dist/features/environment/GraphHeader/GraphHeader.esm.js +247 -0
- package/dist/features/environment/GraphHeader/GraphHeader.esm.js.map +1 -0
- package/dist/features/environment/GraphHeader/HeaderActions.esm.js +127 -0
- package/dist/features/environment/GraphHeader/HeaderActions.esm.js.map +1 -0
- package/dist/features/environment/GraphHeader/Switcher.esm.js +75 -0
- package/dist/features/environment/GraphHeader/Switcher.esm.js.map +1 -0
- package/dist/features/environment/GraphHeader/queries.esm.js +60 -0
- package/dist/features/environment/GraphHeader/queries.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/AlarmCard.esm.js +151 -0
- package/dist/features/environment/InstanceDrawer/AlarmCard.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/InstanceDrawer.esm.js +165 -0
- package/dist/features/environment/InstanceDrawer/InstanceDrawer.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/InstanceDrawerHeader.esm.js +241 -0
- package/dist/features/environment/InstanceDrawer/InstanceDrawerHeader.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/InstanceTabs.esm.js +119 -0
- package/dist/features/environment/InstanceDrawer/InstanceTabs.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/TabState.esm.js +30 -0
- package/dist/features/environment/InstanceDrawer/TabState.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/formsDataSource.esm.js +25 -0
- package/dist/features/environment/InstanceDrawer/formsDataSource.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/helpers.esm.js +282 -0
- package/dist/features/environment/InstanceDrawer/helpers.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/queries.esm.js +281 -0
- package/dist/features/environment/InstanceDrawer/queries.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/tabs/ConfigTab.esm.js +170 -0
- package/dist/features/environment/InstanceDrawer/tabs/ConfigTab.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/tabs/DependenciesTab.esm.js +413 -0
- package/dist/features/environment/InstanceDrawer/tabs/DependenciesTab.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/tabs/DeploymentLogsPanel.esm.js +191 -0
- package/dist/features/environment/InstanceDrawer/tabs/DeploymentLogsPanel.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/tabs/DeploymentReadOnlyActions.esm.js +63 -0
- package/dist/features/environment/InstanceDrawer/tabs/DeploymentReadOnlyActions.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/tabs/GuideTab.esm.js +48 -0
- package/dist/features/environment/InstanceDrawer/tabs/GuideTab.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/tabs/HistoryTab.esm.js +446 -0
- package/dist/features/environment/InstanceDrawer/tabs/HistoryTab.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/tabs/MonitorTab.esm.js +146 -0
- package/dist/features/environment/InstanceDrawer/tabs/MonitorTab.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/tabs/OverviewTab.esm.js +572 -0
- package/dist/features/environment/InstanceDrawer/tabs/OverviewTab.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/tabs/ResourcesTab.esm.js +471 -0
- package/dist/features/environment/InstanceDrawer/tabs/ResourcesTab.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/tabs/SecretsTab.esm.js +109 -0
- package/dist/features/environment/InstanceDrawer/tabs/SecretsTab.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/tabs/ViewDeploymentDetails.esm.js +384 -0
- package/dist/features/environment/InstanceDrawer/tabs/ViewDeploymentDetails.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/tabs/flattenParams.esm.js +23 -0
- package/dist/features/environment/InstanceDrawer/tabs/flattenParams.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/useInstanceApiQuery.esm.js +6 -0
- package/dist/features/environment/InstanceDrawer/useInstanceApiQuery.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/useOverflowMeasure.esm.js +55 -0
- package/dist/features/environment/InstanceDrawer/useOverflowMeasure.esm.js.map +1 -0
- package/dist/features/environment/InstanceDrawer/useResizableWidth.esm.js +44 -0
- package/dist/features/environment/InstanceDrawer/useResizableWidth.esm.js.map +1 -0
- package/dist/features/environment/components/InstanceStatusPill.esm.js +54 -0
- package/dist/features/environment/components/InstanceStatusPill.esm.js.map +1 -0
- package/dist/features/environment/graph/Diagram.esm.js +143 -0
- package/dist/features/environment/graph/Diagram.esm.js.map +1 -0
- package/dist/features/environment/graph/DiagramNode.constants.esm.js +5 -0
- package/dist/features/environment/graph/DiagramNode.constants.esm.js.map +1 -0
- package/dist/features/environment/graph/DiagramNode.esm.js +221 -0
- package/dist/features/environment/graph/DiagramNode.esm.js.map +1 -0
- package/dist/features/environment/graph/NodeMeta.esm.js +182 -0
- package/dist/features/environment/graph/NodeMeta.esm.js.map +1 -0
- package/dist/features/environment/graph/NodeVersionBadges.esm.js +150 -0
- package/dist/features/environment/graph/NodeVersionBadges.esm.js.map +1 -0
- package/dist/features/environment/graph/diagramFactory.esm.js +151 -0
- package/dist/features/environment/graph/diagramFactory.esm.js.map +1 -0
- package/dist/features/environment/graph/handles/ExpandableHandleWrapper.esm.js +174 -0
- package/dist/features/environment/graph/handles/ExpandableHandleWrapper.esm.js.map +1 -0
- package/dist/features/environment/graph/handles/NodeHandle.esm.js +45 -0
- package/dist/features/environment/graph/handles/NodeHandle.esm.js.map +1 -0
- package/dist/features/environment/graph/handles/NodeHandleTooltip.esm.js +134 -0
- package/dist/features/environment/graph/handles/NodeHandleTooltip.esm.js.map +1 -0
- package/dist/features/environment/graph/handles/handleTypes.esm.js +170 -0
- package/dist/features/environment/graph/handles/handleTypes.esm.js.map +1 -0
- package/dist/features/environment/graph/handles/useAnimationFrameSync.esm.js +19 -0
- package/dist/features/environment/graph/handles/useAnimationFrameSync.esm.js.map +1 -0
- package/dist/features/environment/graph/handles/useRadialPositions.esm.js +37 -0
- package/dist/features/environment/graph/handles/useRadialPositions.esm.js.map +1 -0
- package/dist/features/environment/graph/handles/useTransitionLifecycle.esm.js +19 -0
- package/dist/features/environment/graph/handles/useTransitionLifecycle.esm.js.map +1 -0
- package/dist/features/environment/graph/hasUndeployedPlan.esm.js +43 -0
- package/dist/features/environment/graph/hasUndeployedPlan.esm.js.map +1 -0
- package/dist/features/environment/graph/queries.esm.js +103 -0
- package/dist/features/environment/graph/queries.esm.js.map +1 -0
- package/dist/features/environment/graph/useDiagramSnapshot.esm.js +43 -0
- package/dist/features/environment/graph/useDiagramSnapshot.esm.js.map +1 -0
- package/dist/features/environment/realtime/RealtimeProvider.esm.js +43 -0
- package/dist/features/environment/realtime/RealtimeProvider.esm.js.map +1 -0
- package/dist/features/environment/realtime/queries.esm.js +76 -0
- package/dist/features/environment/realtime/queries.esm.js.map +1 -0
- package/dist/features/environment/realtime/useLiveRelayQuery.esm.js +36 -0
- package/dist/features/environment/realtime/useLiveRelayQuery.esm.js.map +1 -0
- package/dist/features/project/ProjectDetailsPage.esm.js +101 -0
- package/dist/features/project/ProjectDetailsPage.esm.js.map +1 -0
- package/dist/features/project/tabs/ComponentsTab.esm.js +149 -0
- package/dist/features/project/tabs/ComponentsTab.esm.js.map +1 -0
- package/dist/features/project/tabs/EnvStatus.esm.js +149 -0
- package/dist/features/project/tabs/EnvStatus.esm.js.map +1 -0
- package/dist/features/project/tabs/EnvironmentsTab.esm.js +199 -0
- package/dist/features/project/tabs/EnvironmentsTab.esm.js.map +1 -0
- package/dist/features/project/tabs/OverviewTab.esm.js +420 -0
- package/dist/features/project/tabs/OverviewTab.esm.js.map +1 -0
- package/dist/features/projects/ProjectsListPage.esm.js +50 -0
- package/dist/features/projects/ProjectsListPage.esm.js.map +1 -0
- package/dist/features/projects/ProjectsListPageLazy.esm.js +12 -0
- package/dist/features/projects/ProjectsListPageLazy.esm.js.map +1 -0
- package/dist/features/projects/projectColumns.esm.js +38 -0
- package/dist/features/projects/projectColumns.esm.js.map +1 -0
- package/dist/features/projects/useProjects.esm.js +48 -0
- package/dist/features/projects/useProjects.esm.js.map +1 -0
- package/dist/features/repo/DeploymentLogsDrawer.esm.js +213 -0
- package/dist/features/repo/DeploymentLogsDrawer.esm.js.map +1 -0
- package/dist/features/repo/DeploymentLogsDrawer.loading.esm.js +43 -0
- package/dist/features/repo/DeploymentLogsDrawer.loading.esm.js.map +1 -0
- package/dist/features/repo/FileTree.esm.js +179 -0
- package/dist/features/repo/FileTree.esm.js.map +1 -0
- package/dist/features/repo/FileViewer.esm.js +115 -0
- package/dist/features/repo/FileViewer.esm.js.map +1 -0
- package/dist/features/repo/FileViewer.helpers.esm.js +54 -0
- package/dist/features/repo/FileViewer.helpers.esm.js.map +1 -0
- package/dist/features/repo/RepoDetailsPage.esm.js +127 -0
- package/dist/features/repo/RepoDetailsPage.esm.js.map +1 -0
- package/dist/features/repo/RepoEmptyState.esm.js +58 -0
- package/dist/features/repo/RepoEmptyState.esm.js.map +1 -0
- package/dist/features/repo/RepoNoVersionsState.esm.js +29 -0
- package/dist/features/repo/RepoNoVersionsState.esm.js.map +1 -0
- package/dist/features/repo/RepoTabHeader.esm.js +30 -0
- package/dist/features/repo/RepoTabHeader.esm.js.map +1 -0
- package/dist/features/repo/RepoTabLayout.esm.js +18 -0
- package/dist/features/repo/RepoTabLayout.esm.js.map +1 -0
- package/dist/features/repo/RepoVersionSelect.esm.js +176 -0
- package/dist/features/repo/RepoVersionSelect.esm.js.map +1 -0
- package/dist/features/repo/ViewDeploymentDetailsDialog.esm.js +427 -0
- package/dist/features/repo/ViewDeploymentDetailsDialog.esm.js.map +1 -0
- package/dist/features/repo/deploymentHelpers.esm.js +74 -0
- package/dist/features/repo/deploymentHelpers.esm.js.map +1 -0
- package/dist/features/repo/fileTreeHelpers.esm.js +45 -0
- package/dist/features/repo/fileTreeHelpers.esm.js.map +1 -0
- package/dist/features/repo/flattenParams.esm.js +23 -0
- package/dist/features/repo/flattenParams.esm.js.map +1 -0
- package/dist/features/repo/queries.esm.js +239 -0
- package/dist/features/repo/queries.esm.js.map +1 -0
- package/dist/features/repo/repoFilter.esm.js +9 -0
- package/dist/features/repo/repoFilter.esm.js.map +1 -0
- package/dist/features/repo/repoTabs.esm.js +22 -0
- package/dist/features/repo/repoTabs.esm.js.map +1 -0
- package/dist/features/repo/resolveVersion.esm.js +22 -0
- package/dist/features/repo/resolveVersion.esm.js.map +1 -0
- package/dist/features/repo/tabs/DeploymentRow.esm.js +201 -0
- package/dist/features/repo/tabs/DeploymentRow.esm.js.map +1 -0
- package/dist/features/repo/tabs/DeploymentsTab.esm.js +188 -0
- package/dist/features/repo/tabs/DeploymentsTab.esm.js.map +1 -0
- package/dist/features/repo/tabs/DeploymentsTab.helpers.esm.js +46 -0
- package/dist/features/repo/tabs/DeploymentsTab.helpers.esm.js.map +1 -0
- package/dist/features/repo/tabs/DeploymentsTab.loading.esm.js +37 -0
- package/dist/features/repo/tabs/DeploymentsTab.loading.esm.js.map +1 -0
- package/dist/features/repo/tabs/FilesTab.esm.js +142 -0
- package/dist/features/repo/tabs/FilesTab.esm.js.map +1 -0
- package/dist/features/repo/tabs/InstanceRow.esm.js +121 -0
- package/dist/features/repo/tabs/InstanceRow.esm.js.map +1 -0
- package/dist/features/repo/tabs/InstancesTab.esm.js +147 -0
- package/dist/features/repo/tabs/InstancesTab.esm.js.map +1 -0
- package/dist/features/repo/tabs/InstancesTab.helpers.esm.js +44 -0
- package/dist/features/repo/tabs/InstancesTab.helpers.esm.js.map +1 -0
- package/dist/features/repo/tabs/InstancesTab.loading.esm.js +33 -0
- package/dist/features/repo/tabs/InstancesTab.loading.esm.js.map +1 -0
- package/dist/features/repo/tabs/OverviewTab.esm.js +77 -0
- package/dist/features/repo/tabs/OverviewTab.esm.js.map +1 -0
- package/dist/features/repo/tabs/PermissionsTab.esm.js +56 -0
- package/dist/features/repo/tabs/PermissionsTab.esm.js.map +1 -0
- package/dist/features/repo/tabs/VersionsTab.esm.js +152 -0
- package/dist/features/repo/tabs/VersionsTab.esm.js.map +1 -0
- package/dist/features/repo/useInfiniteRelayList.esm.js +92 -0
- package/dist/features/repo/useInfiniteRelayList.esm.js.map +1 -0
- package/dist/features/repo/useInfiniteScroll.esm.js +21 -0
- package/dist/features/repo/useInfiniteScroll.esm.js.map +1 -0
- package/dist/features/repos/ReposListPage.esm.js +53 -0
- package/dist/features/repos/ReposListPage.esm.js.map +1 -0
- package/dist/features/repos/queries.esm.js +40 -0
- package/dist/features/repos/queries.esm.js.map +1 -0
- package/dist/features/repos/repoColumns.esm.js +121 -0
- package/dist/features/repos/repoColumns.esm.js.map +1 -0
- package/dist/features/repos/useRepos.esm.js +22 -0
- package/dist/features/repos/useRepos.esm.js.map +1 -0
- package/dist/features/resource/OriginChip.esm.js +42 -0
- package/dist/features/resource/OriginChip.esm.js.map +1 -0
- package/dist/features/resource/ResourceDetailsPage.esm.js +167 -0
- package/dist/features/resource/ResourceDetailsPage.esm.js.map +1 -0
- package/dist/features/resource/queries.esm.js +141 -0
- package/dist/features/resource/queries.esm.js.map +1 -0
- package/dist/features/resource/tabs/GeneralTab.esm.js +247 -0
- package/dist/features/resource/tabs/GeneralTab.esm.js.map +1 -0
- package/dist/features/resource/tabs/GeneralTab.loading.esm.js +76 -0
- package/dist/features/resource/tabs/GeneralTab.loading.esm.js.map +1 -0
- package/dist/features/resource/tabs/PermissionsTab.esm.js +57 -0
- package/dist/features/resource/tabs/PermissionsTab.esm.js.map +1 -0
- package/dist/features/resource/tabs/SettingsTabLayout.esm.js +18 -0
- package/dist/features/resource/tabs/SettingsTabLayout.esm.js.map +1 -0
- package/dist/features/resource/tabs/TabHeader.esm.js +30 -0
- package/dist/features/resource/tabs/TabHeader.esm.js.map +1 -0
- package/dist/features/resource/tabs/UsagePanel.esm.js +35 -0
- package/dist/features/resource/tabs/UsagePanel.esm.js.map +1 -0
- package/dist/features/resource/tabs/UsageTab.esm.js +91 -0
- package/dist/features/resource/tabs/UsageTab.esm.js.map +1 -0
- package/dist/features/resource/tabs/UsageTab.helpers.esm.js +168 -0
- package/dist/features/resource/tabs/UsageTab.helpers.esm.js.map +1 -0
- package/dist/features/resources/ResourcesEmptyState.esm.js +148 -0
- package/dist/features/resources/ResourcesEmptyState.esm.js.map +1 -0
- package/dist/features/resources/ResourcesListPage.esm.js +115 -0
- package/dist/features/resources/ResourcesListPage.esm.js.map +1 -0
- package/dist/features/resources/queries.esm.js +50 -0
- package/dist/features/resources/queries.esm.js.map +1 -0
- package/dist/features/resources/resourceColumns.esm.js +158 -0
- package/dist/features/resources/resourceColumns.esm.js.map +1 -0
- package/dist/features/resources/resourceConstants.esm.js +7 -0
- package/dist/features/resources/resourceConstants.esm.js.map +1 -0
- package/dist/features/resources/toResourceRow.esm.js +29 -0
- package/dist/features/resources/toResourceRow.esm.js.map +1 -0
- package/dist/hooks/useAuthedSvg.esm.js +34 -0
- package/dist/hooks/useAuthedSvg.esm.js.map +1 -0
- package/dist/hooks/useCursorPagination.esm.js +40 -0
- package/dist/hooks/useCursorPagination.esm.js.map +1 -0
- package/dist/hooks/useMassdriverSubscription.esm.js +61 -0
- package/dist/hooks/useMassdriverSubscription.esm.js.map +1 -0
- package/dist/hooks/usePaginatedRelayQuery.esm.js +68 -0
- package/dist/hooks/usePaginatedRelayQuery.esm.js.map +1 -0
- package/dist/index.d.ts +256 -0
- package/dist/index.esm.js +8 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/internalRoutes.esm.js +29 -0
- package/dist/internalRoutes.esm.js.map +1 -0
- package/dist/plugin.esm.js +57 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/queries.esm.js +54 -0
- package/dist/queries.esm.js.map +1 -0
- package/dist/routes.esm.js +6 -0
- package/dist/routes.esm.js.map +1 -0
- package/dist/shell/MassdriverHeader.esm.js +37 -0
- package/dist/shell/MassdriverHeader.esm.js.map +1 -0
- package/dist/shell/MassdriverShell.esm.js +30 -0
- package/dist/shell/MassdriverShell.esm.js.map +1 -0
- package/dist/theme/MassdriverThemeScope.esm.js +30 -0
- package/dist/theme/MassdriverThemeScope.esm.js.map +1 -0
- package/dist/theme/ThemeModeContext.esm.js +35 -0
- package/dist/theme/ThemeModeContext.esm.js.map +1 -0
- package/dist/theme/muiClassNameAudit.esm.js +11 -0
- package/dist/theme/muiClassNameAudit.esm.js.map +1 -0
- package/dist/useInstances.esm.js +39 -0
- package/dist/useInstances.esm.js.map +1 -0
- package/dist/utils/attributes.esm.js +28 -0
- package/dist/utils/attributes.esm.js.map +1 -0
- package/dist/utils/conditions.esm.js +20 -0
- package/dist/utils/conditions.esm.js.map +1 -0
- package/dist/utils/formatRelativeTime.esm.js +40 -0
- package/dist/utils/formatRelativeTime.esm.js.map +1 -0
- package/dist/utils/instanceStatuses.esm.js +74 -0
- package/dist/utils/instanceStatuses.esm.js.map +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { useApi } from '@backstage/frontend-plugin-api';
|
|
3
|
+
import { resourcesUrl } from '@massdriver/backstage-plugin-common';
|
|
4
|
+
import Alert from '@massdriver/ui/Alert';
|
|
5
|
+
import Box from '@massdriver/ui/Box';
|
|
6
|
+
import DataList from '@massdriver/ui/DataList';
|
|
7
|
+
import Select from '@massdriver/ui/Select';
|
|
8
|
+
import stylin from '@massdriver/ui/stylin';
|
|
9
|
+
import { useState } from 'react';
|
|
10
|
+
import { massdriverApiRef } from '../../api.esm.js';
|
|
11
|
+
import { OpenInMassdriverButton } from '../../components/OpenInMassdriverButton.esm.js';
|
|
12
|
+
import { PageLayout } from '../../components/PageLayout.esm.js';
|
|
13
|
+
import { usePaginatedRelayQuery } from '../../hooks/usePaginatedRelayQuery.esm.js';
|
|
14
|
+
import { RESOURCES_QUERY } from './queries.esm.js';
|
|
15
|
+
import { buildResourceColumns } from './resourceColumns.esm.js';
|
|
16
|
+
import { ResourcesEmptyState } from './ResourcesEmptyState.esm.js';
|
|
17
|
+
import { toResourceRow } from './toResourceRow.esm.js';
|
|
18
|
+
|
|
19
|
+
const ORIGIN_OPTIONS = [
|
|
20
|
+
{ value: "all", label: "All resources" },
|
|
21
|
+
{ value: "IMPORTED", label: "Imported only" },
|
|
22
|
+
{ value: "PROVISIONED", label: "Provisioned only" }
|
|
23
|
+
];
|
|
24
|
+
const ResourcesListPage = () => {
|
|
25
|
+
const api = useApi(massdriverApiRef);
|
|
26
|
+
const [origin, setOrigin] = useState("all");
|
|
27
|
+
const importUrl = `${resourcesUrl(
|
|
28
|
+
api.appUrl,
|
|
29
|
+
api.organizationId
|
|
30
|
+
)}?importResource=true`;
|
|
31
|
+
return /* @__PURE__ */ jsx(
|
|
32
|
+
PageLayout,
|
|
33
|
+
{
|
|
34
|
+
title: "Resources",
|
|
35
|
+
description: "All resources for your organization \u2014 both imported and provisioned. Resources flow into Massdriver automatically as instances deploy, or you can import existing resources by hand.",
|
|
36
|
+
actions: /* @__PURE__ */ jsx(OpenInMassdriverButton, { url: importUrl, children: "Import Resource" }),
|
|
37
|
+
children: /* @__PURE__ */ jsx(
|
|
38
|
+
ResourcesList,
|
|
39
|
+
{
|
|
40
|
+
origin,
|
|
41
|
+
onOriginChange: setOrigin,
|
|
42
|
+
importUrl
|
|
43
|
+
},
|
|
44
|
+
origin
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
const ResourcesList = ({
|
|
50
|
+
origin,
|
|
51
|
+
onOriginChange,
|
|
52
|
+
importUrl
|
|
53
|
+
}) => {
|
|
54
|
+
const baseFilter = origin === "all" ? void 0 : { origin: { eq: origin } };
|
|
55
|
+
const { items, loading, error, hasMore, dataListParams } = usePaginatedRelayQuery(RESOURCES_QUERY, {
|
|
56
|
+
responseKey: "resources",
|
|
57
|
+
sortFieldMap: { name: "NAME", createdAt: "CREATED_AT" },
|
|
58
|
+
defaultSort: { field: "name", direction: "asc" },
|
|
59
|
+
pageSize: 25,
|
|
60
|
+
baseFilter
|
|
61
|
+
});
|
|
62
|
+
const api = useApi(massdriverApiRef);
|
|
63
|
+
const columns = buildResourceColumns({
|
|
64
|
+
appUrl: api.appUrl,
|
|
65
|
+
organizationId: api.organizationId
|
|
66
|
+
});
|
|
67
|
+
const rows = items.map(toResourceRow);
|
|
68
|
+
const filtersActive = origin !== "all" || Boolean(dataListParams.state.search);
|
|
69
|
+
const showEmptyState = !loading && !error && !filtersActive && rows.length === 0;
|
|
70
|
+
if (showEmptyState) {
|
|
71
|
+
return /* @__PURE__ */ jsx(ResourcesEmptyState, { importUrl });
|
|
72
|
+
}
|
|
73
|
+
return /* @__PURE__ */ jsxs(Wrap, { children: [
|
|
74
|
+
/* @__PURE__ */ jsx(Toolbar, { children: /* @__PURE__ */ jsx(
|
|
75
|
+
FilterSelect,
|
|
76
|
+
{
|
|
77
|
+
size: "small",
|
|
78
|
+
label: "Show",
|
|
79
|
+
options: ORIGIN_OPTIONS,
|
|
80
|
+
value: origin,
|
|
81
|
+
onChange: (event) => onOriginChange(event.target.value)
|
|
82
|
+
}
|
|
83
|
+
) }),
|
|
84
|
+
error ? /* @__PURE__ */ jsx(Alert, { severity: "error", children: String(error.message ?? error) }) : /* @__PURE__ */ jsx(
|
|
85
|
+
DataList,
|
|
86
|
+
{
|
|
87
|
+
rows,
|
|
88
|
+
columns,
|
|
89
|
+
loading,
|
|
90
|
+
serverSide: true,
|
|
91
|
+
searchable: true,
|
|
92
|
+
searchPlaceholder: "Search resources\u2026",
|
|
93
|
+
hasMore,
|
|
94
|
+
emptyMessage: "No resources match.",
|
|
95
|
+
variant: "outlined",
|
|
96
|
+
...dataListParams
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
] });
|
|
100
|
+
};
|
|
101
|
+
const Wrap = stylin(Box)(({ theme }) => ({
|
|
102
|
+
display: "flex",
|
|
103
|
+
flexDirection: "column",
|
|
104
|
+
gap: theme.spacing(2)
|
|
105
|
+
}));
|
|
106
|
+
const Toolbar = stylin(Box)({
|
|
107
|
+
display: "flex",
|
|
108
|
+
alignItems: "center"
|
|
109
|
+
});
|
|
110
|
+
const FilterSelect = stylin(Select)(({ theme }) => ({
|
|
111
|
+
minWidth: theme.spacing(28)
|
|
112
|
+
}));
|
|
113
|
+
|
|
114
|
+
export { ResourcesListPage };
|
|
115
|
+
//# sourceMappingURL=ResourcesListPage.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourcesListPage.esm.js","sources":["../../../src/features/resources/ResourcesListPage.tsx"],"sourcesContent":["import { useApi } from '@backstage/frontend-plugin-api';\nimport { resourcesUrl } from '@massdriver/backstage-plugin-common';\nimport Alert from '@massdriver/ui/Alert';\nimport Box from '@massdriver/ui/Box';\nimport DataList from '@massdriver/ui/DataList';\nimport Select from '@massdriver/ui/Select';\nimport stylin from '@massdriver/ui/stylin';\nimport { useState } from 'react';\nimport { massdriverApiRef } from '../../api';\nimport { OpenInMassdriverButton } from '../../components/OpenInMassdriverButton';\nimport { PageLayout } from '../../components/PageLayout';\nimport { usePaginatedRelayQuery } from '../../hooks/usePaginatedRelayQuery';\nimport { RESOURCES_QUERY } from './queries';\nimport { buildResourceColumns } from './resourceColumns';\nimport { ResourcesEmptyState } from './ResourcesEmptyState';\nimport { ResourceListItem, toResourceRow } from './toResourceRow';\n\n// Ported from apps/web/features/resources/pages/ResourcesPage.js and\n// sections/ResourcesList/. The web app's \"Import Resource\" opens a dialog; here\n// it deep-links into Massdriver (read-only parity).\n\ntype Origin = 'all' | 'IMPORTED' | 'PROVISIONED';\n\nconst ORIGIN_OPTIONS = [\n { value: 'all', label: 'All resources' },\n { value: 'IMPORTED', label: 'Imported only' },\n { value: 'PROVISIONED', label: 'Provisioned only' },\n];\n\n/** Read-only resources list, mirroring the web app's Resources page. */\nexport const ResourcesListPage = () => {\n const api = useApi(massdriverApiRef);\n const [origin, setOrigin] = useState<Origin>('all');\n const importUrl = `${resourcesUrl(\n api.appUrl,\n api.organizationId,\n )}?importResource=true`;\n\n return (\n <PageLayout\n title=\"Resources\"\n description=\"All resources for your organization — both imported and provisioned. Resources flow into Massdriver automatically as instances deploy, or you can import existing resources by hand.\"\n actions={\n <OpenInMassdriverButton url={importUrl}>\n Import Resource\n </OpenInMassdriverButton>\n }\n >\n {/* Remount on origin change so paging state resets (mirrors the web's\n UsagePanel key pattern). */}\n <ResourcesList\n key={origin}\n origin={origin}\n onOriginChange={setOrigin}\n importUrl={importUrl}\n />\n </PageLayout>\n );\n};\n\nconst ResourcesList = ({\n origin,\n onOriginChange,\n importUrl,\n}: {\n origin: Origin;\n onOriginChange: (origin: Origin) => void;\n importUrl: string;\n}) => {\n const baseFilter = origin === 'all' ? undefined : { origin: { eq: origin } };\n\n const { items, loading, error, hasMore, dataListParams } =\n usePaginatedRelayQuery<ResourceListItem>(RESOURCES_QUERY, {\n responseKey: 'resources',\n sortFieldMap: { name: 'NAME', createdAt: 'CREATED_AT' },\n defaultSort: { field: 'name', direction: 'asc' },\n pageSize: 25,\n baseFilter,\n });\n\n const api = useApi(massdriverApiRef);\n const columns = buildResourceColumns({\n appUrl: api.appUrl,\n organizationId: api.organizationId,\n });\n const rows = items.map(toResourceRow);\n\n const filtersActive =\n origin !== 'all' || Boolean(dataListParams.state.search);\n const showEmptyState =\n !loading && !error && !filtersActive && rows.length === 0;\n\n // With no resources and no active filters, the web shows a catalog-onboarding\n // card in place of the toolbar + table.\n if (showEmptyState) {\n return <ResourcesEmptyState importUrl={importUrl} />;\n }\n\n return (\n <Wrap>\n <Toolbar>\n <FilterSelect\n size=\"small\"\n label=\"Show\"\n options={ORIGIN_OPTIONS}\n value={origin}\n onChange={(event: { target: { value: Origin } }) =>\n onOriginChange(event.target.value)\n }\n />\n </Toolbar>\n {error ? (\n <Alert severity=\"error\">{String(error.message ?? error)}</Alert>\n ) : (\n <DataList\n rows={rows}\n columns={columns}\n loading={loading}\n serverSide\n searchable\n searchPlaceholder=\"Search resources…\"\n hasMore={hasMore}\n emptyMessage=\"No resources match.\"\n variant=\"outlined\"\n {...dataListParams}\n />\n )}\n </Wrap>\n );\n};\n\nconst Wrap = stylin(Box)(({ theme }: { theme: any }) => ({\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(2),\n}));\n\nconst Toolbar = stylin(Box)({\n display: 'flex',\n alignItems: 'center',\n});\n\nconst FilterSelect = stylin(Select)(({ theme }: { theme: any }) => ({\n minWidth: theme.spacing(28),\n}));\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const RESOURCES_QUERY = `
|
|
2
|
+
query MassdriverResourcesList(
|
|
3
|
+
$organizationId: ID!
|
|
4
|
+
$filter: ResourcesFilter
|
|
5
|
+
$sort: ResourcesSort
|
|
6
|
+
$cursor: Cursor
|
|
7
|
+
) {
|
|
8
|
+
resources(
|
|
9
|
+
organizationId: $organizationId
|
|
10
|
+
filter: $filter
|
|
11
|
+
sort: $sort
|
|
12
|
+
cursor: $cursor
|
|
13
|
+
) {
|
|
14
|
+
cursor {
|
|
15
|
+
next
|
|
16
|
+
previous
|
|
17
|
+
}
|
|
18
|
+
items {
|
|
19
|
+
id
|
|
20
|
+
name
|
|
21
|
+
origin
|
|
22
|
+
formats
|
|
23
|
+
attributes
|
|
24
|
+
effectiveAttributes
|
|
25
|
+
createdAt
|
|
26
|
+
updatedAt
|
|
27
|
+
resourceType {
|
|
28
|
+
id
|
|
29
|
+
name
|
|
30
|
+
icon
|
|
31
|
+
}
|
|
32
|
+
instance {
|
|
33
|
+
id
|
|
34
|
+
name
|
|
35
|
+
environment {
|
|
36
|
+
id
|
|
37
|
+
name
|
|
38
|
+
project {
|
|
39
|
+
id
|
|
40
|
+
name
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
export { RESOURCES_QUERY };
|
|
50
|
+
//# sourceMappingURL=queries.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.esm.js","sources":["../../../src/features/resources/queries.ts"],"sourcesContent":["// Ported from apps/web/features/resources/hooks/useResourcesQuery.js\n\n/** Paginated resources list. `$organizationId` is injected by the relay. */\nexport const RESOURCES_QUERY = `\n query MassdriverResourcesList(\n $organizationId: ID!\n $filter: ResourcesFilter\n $sort: ResourcesSort\n $cursor: Cursor\n ) {\n resources(\n organizationId: $organizationId\n filter: $filter\n sort: $sort\n cursor: $cursor\n ) {\n cursor {\n next\n previous\n }\n items {\n id\n name\n origin\n formats\n attributes\n effectiveAttributes\n createdAt\n updatedAt\n resourceType {\n id\n name\n icon\n }\n instance {\n id\n name\n environment {\n id\n name\n project {\n id\n name\n }\n }\n }\n }\n }\n }\n`;\n"],"names":[],"mappings":"AAGO,MAAM,eAAA,GAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import Box from '@massdriver/ui/Box';
|
|
3
|
+
import { col } from '@massdriver/ui/DataList';
|
|
4
|
+
import IconTile from '@massdriver/ui/IconTile';
|
|
5
|
+
import Typography from '@massdriver/ui/Typography';
|
|
6
|
+
import DeleteIcon from '@massdriver/ui/icons/DeleteIcon';
|
|
7
|
+
import EditIcon from '@massdriver/ui/icons/EditIcon';
|
|
8
|
+
import ExtensionIcon from '@massdriver/ui/icons/ExtensionIcon';
|
|
9
|
+
import stylin from '@massdriver/ui/stylin';
|
|
10
|
+
import { resourceUrl } from '@massdriver/backstage-plugin-common';
|
|
11
|
+
import { buildAttributesColumn, Code } from '../../components/AttributesColumn.esm.js';
|
|
12
|
+
import { RouterLinkAdapter } from '../../components/RouterLinkAdapter.esm.js';
|
|
13
|
+
import { internalRoutes } from '../../internalRoutes.esm.js';
|
|
14
|
+
import { formatAbsoluteTime } from '../../utils/formatRelativeTime.esm.js';
|
|
15
|
+
import { PROVISIONED_EDIT_TOOLTIP, IMPORTED_EDIT_TOOLTIP, PROVISIONED_DELETE_TOOLTIP, IMPORTED_DELETE_TOOLTIP } from './resourceConstants.esm.js';
|
|
16
|
+
|
|
17
|
+
const renderType = (_value, row) => /* @__PURE__ */ jsxs(TypeCell, { children: [
|
|
18
|
+
/* @__PURE__ */ jsx(
|
|
19
|
+
IconTile,
|
|
20
|
+
{
|
|
21
|
+
src: row.resourceTypeIcon,
|
|
22
|
+
alt: row.resourceTypeName,
|
|
23
|
+
size: "small",
|
|
24
|
+
fallback: /* @__PURE__ */ jsx(FallbackIcon, {})
|
|
25
|
+
}
|
|
26
|
+
),
|
|
27
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", noWrap: true, title: row.resourceTypeName, children: row.resourceTypeName })
|
|
28
|
+
] });
|
|
29
|
+
const renderLocation = (_value, row) => {
|
|
30
|
+
const { location } = row;
|
|
31
|
+
if (!location) {
|
|
32
|
+
return /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: "\u2014" });
|
|
33
|
+
}
|
|
34
|
+
const { projectId, environmentId, projectName, environmentName } = location;
|
|
35
|
+
return /* @__PURE__ */ jsxs(LocationCell, { title: `${projectName} : ${environmentName}`, children: [
|
|
36
|
+
/* @__PURE__ */ jsx(
|
|
37
|
+
LocationLink,
|
|
38
|
+
{
|
|
39
|
+
component: RouterLinkAdapter,
|
|
40
|
+
href: internalRoutes.project(projectId),
|
|
41
|
+
children: projectName
|
|
42
|
+
}
|
|
43
|
+
),
|
|
44
|
+
/* @__PURE__ */ jsx(LocationSeparator, { children: " : " }),
|
|
45
|
+
environmentId ? /* @__PURE__ */ jsx(
|
|
46
|
+
LocationLink,
|
|
47
|
+
{
|
|
48
|
+
component: RouterLinkAdapter,
|
|
49
|
+
href: internalRoutes.environment(projectId, environmentId),
|
|
50
|
+
children: environmentName
|
|
51
|
+
}
|
|
52
|
+
) : /* @__PURE__ */ jsx("span", { children: environmentName })
|
|
53
|
+
] });
|
|
54
|
+
};
|
|
55
|
+
const buildActionsColumn = ({
|
|
56
|
+
appUrl,
|
|
57
|
+
organizationId
|
|
58
|
+
}) => col.actions([
|
|
59
|
+
{
|
|
60
|
+
icon: /* @__PURE__ */ jsx(EditIcon, { fontSize: "small" }),
|
|
61
|
+
tooltip: (row) => row.origin === "PROVISIONED" ? PROVISIONED_EDIT_TOOLTIP : IMPORTED_EDIT_TOOLTIP,
|
|
62
|
+
href: (row) => resourceUrl(appUrl, organizationId, row.id),
|
|
63
|
+
target: "_blank",
|
|
64
|
+
rel: "noopener noreferrer",
|
|
65
|
+
disabled: (row) => row.origin === "PROVISIONED"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
icon: /* @__PURE__ */ jsx(DeleteIcon, { fontSize: "small" }),
|
|
69
|
+
tooltip: (row) => row.origin === "PROVISIONED" ? PROVISIONED_DELETE_TOOLTIP : IMPORTED_DELETE_TOOLTIP,
|
|
70
|
+
href: (row) => resourceUrl(appUrl, organizationId, row.id),
|
|
71
|
+
target: "_blank",
|
|
72
|
+
rel: "noopener noreferrer",
|
|
73
|
+
disabled: (row) => row.origin === "PROVISIONED",
|
|
74
|
+
color: "error"
|
|
75
|
+
}
|
|
76
|
+
]);
|
|
77
|
+
const buildResourceColumns = ({
|
|
78
|
+
appUrl,
|
|
79
|
+
organizationId
|
|
80
|
+
}) => [
|
|
81
|
+
col.link(
|
|
82
|
+
"name",
|
|
83
|
+
"Name",
|
|
84
|
+
(row) => internalRoutes.resourceTab(row.id, "general"),
|
|
85
|
+
{
|
|
86
|
+
flex: 2,
|
|
87
|
+
minWidth: 180,
|
|
88
|
+
LinkComponent: RouterLinkAdapter
|
|
89
|
+
}
|
|
90
|
+
),
|
|
91
|
+
col.custom("resourceTypeName", "Type", renderType, {
|
|
92
|
+
flex: 1.4,
|
|
93
|
+
minWidth: 160,
|
|
94
|
+
sortable: false
|
|
95
|
+
}),
|
|
96
|
+
col.custom("location", "Project : Environment", renderLocation, {
|
|
97
|
+
flex: 1.4,
|
|
98
|
+
minWidth: 160,
|
|
99
|
+
sortable: false,
|
|
100
|
+
searchable: false
|
|
101
|
+
}),
|
|
102
|
+
buildAttributesColumn({
|
|
103
|
+
directText: "Key-value attributes assigned directly to this resource.",
|
|
104
|
+
effectiveText: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
105
|
+
"The full attribute map the authorization system evaluates policies against for this resource \u2014 the resource's own user attributes plus auto-injected ",
|
|
106
|
+
/* @__PURE__ */ jsx(Code, { children: "md-*" }),
|
|
107
|
+
" system attributes."
|
|
108
|
+
] }),
|
|
109
|
+
flex: 1.4,
|
|
110
|
+
minWidth: 180
|
|
111
|
+
}),
|
|
112
|
+
col.text("originLabel", "Origin", {
|
|
113
|
+
flex: 0.8,
|
|
114
|
+
minWidth: 124,
|
|
115
|
+
sortable: false,
|
|
116
|
+
searchable: false
|
|
117
|
+
}),
|
|
118
|
+
col.text("createdAtRelative", "Created", {
|
|
119
|
+
flex: 1,
|
|
120
|
+
minWidth: 120,
|
|
121
|
+
sortable: false,
|
|
122
|
+
searchable: false,
|
|
123
|
+
tooltip: (_value, row) => formatAbsoluteTime(row.createdAt)
|
|
124
|
+
}),
|
|
125
|
+
buildActionsColumn({ appUrl, organizationId })
|
|
126
|
+
];
|
|
127
|
+
const TypeCell = stylin(Box)(({ theme }) => ({
|
|
128
|
+
display: "flex",
|
|
129
|
+
alignItems: "center",
|
|
130
|
+
gap: theme.spacing(1),
|
|
131
|
+
minWidth: 0
|
|
132
|
+
}));
|
|
133
|
+
const FallbackIcon = stylin(ExtensionIcon)({
|
|
134
|
+
fontSize: 16
|
|
135
|
+
});
|
|
136
|
+
const LocationCell = stylin(Box)({
|
|
137
|
+
display: "-webkit-box",
|
|
138
|
+
WebkitLineClamp: 2,
|
|
139
|
+
WebkitBoxOrient: "vertical",
|
|
140
|
+
overflow: "hidden",
|
|
141
|
+
textOverflow: "ellipsis",
|
|
142
|
+
wordBreak: "break-word",
|
|
143
|
+
fontSize: "0.875rem",
|
|
144
|
+
lineHeight: 1.43,
|
|
145
|
+
minWidth: 0
|
|
146
|
+
});
|
|
147
|
+
const LocationLink = stylin(Box)(({ theme }) => ({
|
|
148
|
+
color: theme.palette.text.primary,
|
|
149
|
+
textDecoration: "none",
|
|
150
|
+
"&:hover": { textDecoration: "underline" }
|
|
151
|
+
}));
|
|
152
|
+
const LocationSeparator = stylin("span")(({ theme }) => ({
|
|
153
|
+
color: theme.palette.text.secondary,
|
|
154
|
+
margin: `0 ${theme.spacing(0.5)}`
|
|
155
|
+
}));
|
|
156
|
+
|
|
157
|
+
export { buildResourceColumns };
|
|
158
|
+
//# sourceMappingURL=resourceColumns.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resourceColumns.esm.js","sources":["../../../src/features/resources/resourceColumns.tsx"],"sourcesContent":["import Box from '@massdriver/ui/Box';\nimport { col } from '@massdriver/ui/DataList';\nimport IconTile from '@massdriver/ui/IconTile';\nimport Typography from '@massdriver/ui/Typography';\nimport DeleteIcon from '@massdriver/ui/icons/DeleteIcon';\nimport EditIcon from '@massdriver/ui/icons/EditIcon';\nimport ExtensionIcon from '@massdriver/ui/icons/ExtensionIcon';\nimport stylin from '@massdriver/ui/stylin';\nimport { resourceUrl } from '@massdriver/backstage-plugin-common';\nimport { buildAttributesColumn, Code } from '../../components/AttributesColumn';\nimport { RouterLinkAdapter } from '../../components/RouterLinkAdapter';\nimport { internalRoutes } from '../../internalRoutes';\nimport { formatAbsoluteTime } from '../../utils/formatRelativeTime';\nimport {\n IMPORTED_DELETE_TOOLTIP,\n IMPORTED_EDIT_TOOLTIP,\n PROVISIONED_DELETE_TOOLTIP,\n PROVISIONED_EDIT_TOOLTIP,\n} from './resourceConstants';\nimport type { ResourceRow } from './toResourceRow';\n\n// Ported from apps/web/features/resources/hooks/useResourceColumns.js. The web\n// app's Edit/Delete row actions mutate: for imported resources they deep-link\n// out to the resource in the Massdriver web app; for provisioned resources they\n// stay disabled with the same tooltips the web app shows (it disables them too).\n\nconst renderType = (_value: unknown, row: ResourceRow) => (\n <TypeCell>\n <IconTile\n src={row.resourceTypeIcon}\n alt={row.resourceTypeName}\n size=\"small\"\n fallback={<FallbackIcon />}\n />\n <Typography variant=\"body2\" noWrap title={row.resourceTypeName}>\n {row.resourceTypeName}\n </Typography>\n </TypeCell>\n);\n\nconst renderLocation = (_value: unknown, row: ResourceRow) => {\n const { location } = row;\n if (!location) {\n return (\n <Typography variant=\"body2\" color=\"text.secondary\">\n —\n </Typography>\n );\n }\n const { projectId, environmentId, projectName, environmentName } = location;\n return (\n <LocationCell title={`${projectName} : ${environmentName}`}>\n <LocationLink\n component={RouterLinkAdapter}\n href={internalRoutes.project(projectId)}\n >\n {projectName}\n </LocationLink>\n <LocationSeparator> : </LocationSeparator>\n {environmentId ? (\n <LocationLink\n component={RouterLinkAdapter}\n href={internalRoutes.environment(projectId, environmentId)}\n >\n {environmentName}\n </LocationLink>\n ) : (\n <span>{environmentName}</span>\n )}\n </LocationCell>\n );\n};\n\nconst buildActionsColumn = ({\n appUrl,\n organizationId,\n}: {\n appUrl: string;\n organizationId: string;\n}) =>\n col.actions([\n {\n icon: <EditIcon fontSize=\"small\" />,\n tooltip: (row: ResourceRow) =>\n row.origin === 'PROVISIONED'\n ? PROVISIONED_EDIT_TOOLTIP\n : IMPORTED_EDIT_TOOLTIP,\n href: (row: ResourceRow) => resourceUrl(appUrl, organizationId, row.id),\n target: '_blank',\n rel: 'noopener noreferrer',\n disabled: (row: ResourceRow) => row.origin === 'PROVISIONED',\n },\n {\n icon: <DeleteIcon fontSize=\"small\" />,\n tooltip: (row: ResourceRow) =>\n row.origin === 'PROVISIONED'\n ? PROVISIONED_DELETE_TOOLTIP\n : IMPORTED_DELETE_TOOLTIP,\n href: (row: ResourceRow) => resourceUrl(appUrl, organizationId, row.id),\n target: '_blank',\n rel: 'noopener noreferrer',\n disabled: (row: ResourceRow) => row.origin === 'PROVISIONED',\n color: 'error',\n },\n ]);\n\nexport const buildResourceColumns = ({\n appUrl,\n organizationId,\n}: {\n appUrl: string;\n organizationId: string;\n}) => [\n col.link(\n 'name',\n 'Name',\n (row: ResourceRow) => internalRoutes.resourceTab(row.id, 'general'),\n {\n flex: 2,\n minWidth: 180,\n LinkComponent: RouterLinkAdapter,\n },\n ),\n col.custom('resourceTypeName', 'Type', renderType, {\n flex: 1.4,\n minWidth: 160,\n sortable: false,\n }),\n col.custom('location', 'Project : Environment', renderLocation, {\n flex: 1.4,\n minWidth: 160,\n sortable: false,\n searchable: false,\n }),\n buildAttributesColumn({\n directText: 'Key-value attributes assigned directly to this resource.',\n effectiveText: (\n <>\n The full attribute map the authorization system evaluates policies\n against for this resource — the resource's own user attributes plus\n auto-injected <Code>md-*</Code> system attributes.\n </>\n ),\n flex: 1.4,\n minWidth: 180,\n }),\n col.text('originLabel', 'Origin', {\n flex: 0.8,\n minWidth: 124,\n sortable: false,\n searchable: false,\n }),\n col.text('createdAtRelative', 'Created', {\n flex: 1,\n minWidth: 120,\n sortable: false,\n searchable: false,\n tooltip: (_value: unknown, row: ResourceRow) =>\n formatAbsoluteTime(row.createdAt),\n }),\n buildActionsColumn({ appUrl, organizationId }),\n];\n\nconst TypeCell = stylin(Box)(({ theme }: { theme: any }) => ({\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n minWidth: 0,\n}));\n\nconst FallbackIcon = stylin(ExtensionIcon)({\n fontSize: 16,\n});\n\nconst LocationCell = stylin(Box)({\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n wordBreak: 'break-word',\n fontSize: '0.875rem',\n lineHeight: 1.43,\n minWidth: 0,\n});\n\nconst LocationLink = stylin(Box)(({ theme }: { theme: any }) => ({\n color: theme.palette.text.primary,\n textDecoration: 'none',\n '&:hover': { textDecoration: 'underline' },\n}));\n\nconst LocationSeparator = stylin('span')(({ theme }: { theme: any }) => ({\n color: theme.palette.text.secondary,\n margin: `0 ${theme.spacing(0.5)}`,\n}));\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const PROVISIONED_EDIT_TOOLTIP = "You can't rename a provisioned resource. Edit its instance configuration instead.";
|
|
2
|
+
const PROVISIONED_DELETE_TOOLTIP = "You can't delete a provisioned resource. Decommission its instance instead.";
|
|
3
|
+
const IMPORTED_EDIT_TOOLTIP = "Resources are edited in Massdriver";
|
|
4
|
+
const IMPORTED_DELETE_TOOLTIP = "Resources are deleted in Massdriver";
|
|
5
|
+
|
|
6
|
+
export { IMPORTED_DELETE_TOOLTIP, IMPORTED_EDIT_TOOLTIP, PROVISIONED_DELETE_TOOLTIP, PROVISIONED_EDIT_TOOLTIP };
|
|
7
|
+
//# sourceMappingURL=resourceConstants.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resourceConstants.esm.js","sources":["../../../src/features/resources/resourceConstants.ts"],"sourcesContent":["// Ported from apps/web/features/resources/utils/resourceConstants.js\n\nexport const PROVISIONED_EDIT_TOOLTIP =\n \"You can't rename a provisioned resource. Edit its instance configuration instead.\";\n\nexport const PROVISIONED_DELETE_TOOLTIP =\n \"You can't delete a provisioned resource. Decommission its instance instead.\";\n\n// Read-only parity: the web app's edit/delete row actions mutate, so here they\n// stay visible but disabled with a tooltip pointing at Massdriver.\nexport const IMPORTED_EDIT_TOOLTIP = 'Resources are edited in Massdriver';\n\nexport const IMPORTED_DELETE_TOOLTIP = 'Resources are deleted in Massdriver';\n"],"names":[],"mappings":"AAEO,MAAM,wBAAA,GACX;AAEK,MAAM,0BAAA,GACX;AAIK,MAAM,qBAAA,GAAwB;AAE9B,MAAM,uBAAA,GAA0B;;;;"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { parseInstanceId } from '@massdriver/backstage-plugin-common';
|
|
2
|
+
import { formatRelativeTime } from '../../utils/formatRelativeTime.esm.js';
|
|
3
|
+
|
|
4
|
+
const ORIGIN_LABELS = {
|
|
5
|
+
IMPORTED: "Imported",
|
|
6
|
+
PROVISIONED: "Provisioned"
|
|
7
|
+
};
|
|
8
|
+
const buildLocation = (instance) => {
|
|
9
|
+
if (!instance?.id) return null;
|
|
10
|
+
const { projectId, scopedEnvironmentId } = parseInstanceId(instance.id);
|
|
11
|
+
if (!projectId || !scopedEnvironmentId) return null;
|
|
12
|
+
return {
|
|
13
|
+
projectId,
|
|
14
|
+
environmentId: instance.environment?.id ?? null,
|
|
15
|
+
projectName: instance.environment?.project?.name ?? projectId,
|
|
16
|
+
environmentName: instance.environment?.name ?? scopedEnvironmentId
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
const toResourceRow = (resource) => ({
|
|
20
|
+
...resource,
|
|
21
|
+
resourceTypeName: resource.resourceType?.name ?? "\u2014",
|
|
22
|
+
resourceTypeIcon: resource.resourceType?.icon ?? null,
|
|
23
|
+
originLabel: ORIGIN_LABELS[resource.origin] ?? resource.origin,
|
|
24
|
+
location: buildLocation(resource.instance),
|
|
25
|
+
createdAtRelative: formatRelativeTime(resource.createdAt)
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export { toResourceRow };
|
|
29
|
+
//# sourceMappingURL=toResourceRow.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toResourceRow.esm.js","sources":["../../../src/features/resources/toResourceRow.ts"],"sourcesContent":["import { parseInstanceId } from '@massdriver/backstage-plugin-common';\nimport { formatRelativeTime } from '../../utils/formatRelativeTime';\n\n// Ported from apps/web/features/resources/utils/resourceColumns.js\n// (transformResourcesResponse + buildLocation).\n\nconst ORIGIN_LABELS: Record<string, string> = {\n IMPORTED: 'Imported',\n PROVISIONED: 'Provisioned',\n};\n\n/** Where a provisioned resource lives, resolved for the Project : Environment cell. */\nexport interface ResourceLocation {\n projectId: string;\n /** Full composite environment id, for `internalRoutes.environment`. */\n environmentId: string | null;\n projectName: string;\n environmentName: string;\n}\n\n/** A resource as returned by the list query. */\nexport interface ResourceListItem {\n id: string;\n name: string;\n origin: string;\n formats?: string[] | null;\n attributes?: unknown;\n effectiveAttributes?: unknown;\n createdAt?: string | null;\n updatedAt?: string | null;\n resourceType?: {\n id: string;\n name?: string | null;\n icon?: string | null;\n } | null;\n instance?: {\n id: string;\n name?: string | null;\n environment?: {\n id: string;\n name?: string | null;\n project?: { id: string; name?: string | null } | null;\n } | null;\n } | null;\n}\n\n/** A resource row as rendered in the DataList. */\nexport interface ResourceRow extends ResourceListItem {\n resourceTypeName: string;\n resourceTypeIcon: string | null;\n originLabel: string;\n location: ResourceLocation | null;\n createdAtRelative: string;\n}\n\nconst buildLocation = (\n instance: ResourceListItem['instance'],\n): ResourceLocation | null => {\n if (!instance?.id) return null;\n const { projectId, scopedEnvironmentId } = parseInstanceId(instance.id);\n if (!projectId || !scopedEnvironmentId) return null;\n return {\n projectId,\n environmentId: instance.environment?.id ?? null,\n projectName: instance.environment?.project?.name ?? projectId,\n environmentName: instance.environment?.name ?? scopedEnvironmentId,\n };\n};\n\n/** Transform a raw resource into a display row (mirrors `transformResourcesResponse`). */\nexport const toResourceRow = (resource: ResourceListItem): ResourceRow => ({\n ...resource,\n resourceTypeName: resource.resourceType?.name ?? '—',\n resourceTypeIcon: resource.resourceType?.icon ?? null,\n originLabel: ORIGIN_LABELS[resource.origin] ?? resource.origin,\n location: buildLocation(resource.instance),\n createdAtRelative: formatRelativeTime(resource.createdAt),\n});\n"],"names":[],"mappings":";;;AAMA,MAAM,aAAA,GAAwC;AAAA,EAC5C,QAAA,EAAU,UAAA;AAAA,EACV,WAAA,EAAa;AACf,CAAA;AA8CA,MAAM,aAAA,GAAgB,CACpB,QAAA,KAC4B;AAC5B,EAAA,IAAI,CAAC,QAAA,EAAU,EAAA,EAAI,OAAO,IAAA;AAC1B,EAAA,MAAM,EAAE,SAAA,EAAW,mBAAA,EAAoB,GAAI,eAAA,CAAgB,SAAS,EAAE,CAAA;AACtE,EAAA,IAAI,CAAC,SAAA,IAAa,CAAC,mBAAA,EAAqB,OAAO,IAAA;AAC/C,EAAA,OAAO;AAAA,IACL,SAAA;AAAA,IACA,aAAA,EAAe,QAAA,CAAS,WAAA,EAAa,EAAA,IAAM,IAAA;AAAA,IAC3C,WAAA,EAAa,QAAA,CAAS,WAAA,EAAa,OAAA,EAAS,IAAA,IAAQ,SAAA;AAAA,IACpD,eAAA,EAAiB,QAAA,CAAS,WAAA,EAAa,IAAA,IAAQ;AAAA,GACjD;AACF,CAAA;AAGO,MAAM,aAAA,GAAgB,CAAC,QAAA,MAA6C;AAAA,EACzE,GAAG,QAAA;AAAA,EACH,gBAAA,EAAkB,QAAA,CAAS,YAAA,EAAc,IAAA,IAAQ,QAAA;AAAA,EACjD,gBAAA,EAAkB,QAAA,CAAS,YAAA,EAAc,IAAA,IAAQ,IAAA;AAAA,EACjD,WAAA,EAAa,aAAA,CAAc,QAAA,CAAS,MAAM,KAAK,QAAA,CAAS,MAAA;AAAA,EACxD,QAAA,EAAU,aAAA,CAAc,QAAA,CAAS,QAAQ,CAAA;AAAA,EACzC,iBAAA,EAAmB,kBAAA,CAAmB,QAAA,CAAS,SAAS;AAC1D,CAAA;;;;"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useApi } from '@backstage/frontend-plugin-api';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { massdriverApiRef } from '../api.esm.js';
|
|
4
|
+
|
|
5
|
+
const useAuthedSvg = (url) => {
|
|
6
|
+
const api = useApi(massdriverApiRef);
|
|
7
|
+
const [svg, setSvg] = useState(null);
|
|
8
|
+
const [loading, setLoading] = useState(false);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (!url) {
|
|
11
|
+
setSvg(null);
|
|
12
|
+
setLoading(false);
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
let cancelled = false;
|
|
16
|
+
setLoading(true);
|
|
17
|
+
api.fetchText(url).then((text) => {
|
|
18
|
+
if (cancelled) return;
|
|
19
|
+
setSvg(text);
|
|
20
|
+
setLoading(false);
|
|
21
|
+
}).catch(() => {
|
|
22
|
+
if (cancelled) return;
|
|
23
|
+
setSvg(null);
|
|
24
|
+
setLoading(false);
|
|
25
|
+
});
|
|
26
|
+
return () => {
|
|
27
|
+
cancelled = true;
|
|
28
|
+
};
|
|
29
|
+
}, [api, url]);
|
|
30
|
+
return { svg, loading };
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { useAuthedSvg as default, useAuthedSvg };
|
|
34
|
+
//# sourceMappingURL=useAuthedSvg.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAuthedSvg.esm.js","sources":["../../src/hooks/useAuthedSvg.ts"],"sourcesContent":["import { useApi } from '@backstage/frontend-plugin-api';\nimport { useEffect, useState } from 'react';\nimport { massdriverApiRef } from '../api';\n\n// Ported from apps/web/shared/hooks/useAuthedSvg.js. The web app fetches the\n// SVG directly with the browser's bearer token; here the fetch goes through\n// the backend content proxy (api.fetchText) since the browser holds no token.\nexport const useAuthedSvg = (url?: string | null) => {\n const api = useApi(massdriverApiRef);\n const [svg, setSvg] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n\n useEffect(() => {\n if (!url) {\n setSvg(null);\n setLoading(false);\n return undefined;\n }\n\n let cancelled = false;\n setLoading(true);\n\n api\n .fetchText(url)\n .then(text => {\n if (cancelled) return;\n setSvg(text);\n setLoading(false);\n })\n .catch(() => {\n if (cancelled) return;\n setSvg(null);\n setLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [api, url]);\n\n return { svg, loading };\n};\n\nexport default useAuthedSvg;\n"],"names":[],"mappings":";;;;AAOO,MAAM,YAAA,GAAe,CAAC,GAAA,KAAwB;AACnD,EAAA,MAAM,GAAA,GAAM,OAAO,gBAAgB,CAAA;AACnC,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAI,SAAwB,IAAI,CAAA;AAClD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAE5C,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,MAAA,CAAO,IAAI,CAAA;AACX,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,UAAA,CAAW,IAAI,CAAA;AAEf,IAAA,GAAA,CACG,SAAA,CAAU,GAAG,CAAA,CACb,IAAA,CAAK,CAAA,IAAA,KAAQ;AACZ,MAAA,IAAI,SAAA,EAAW;AACf,MAAA,MAAA,CAAO,IAAI,CAAA;AACX,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AACX,MAAA,IAAI,SAAA,EAAW;AACf,MAAA,MAAA,CAAO,IAAI,CAAA;AACX,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB,CAAC,CAAA;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,GAAA,EAAK,GAAG,CAAC,CAAA;AAEb,EAAA,OAAO,EAAE,KAAK,OAAA,EAAQ;AACxB;;;;"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
const mapSort = (sort, fieldMap) => {
|
|
4
|
+
if (!sort) {
|
|
5
|
+
return void 0;
|
|
6
|
+
}
|
|
7
|
+
const field = fieldMap[sort.field];
|
|
8
|
+
if (!field) {
|
|
9
|
+
return void 0;
|
|
10
|
+
}
|
|
11
|
+
return { field, order: sort.direction === "asc" ? "ASC" : "DESC" };
|
|
12
|
+
};
|
|
13
|
+
const useCursorPagination = ({
|
|
14
|
+
state,
|
|
15
|
+
sortFieldMap = {}
|
|
16
|
+
}) => {
|
|
17
|
+
const cursorsRef = useRef(/* @__PURE__ */ new Map());
|
|
18
|
+
const prevSortKeyRef = useRef("");
|
|
19
|
+
const sort = mapSort(state.sort, sortFieldMap);
|
|
20
|
+
const sortKey = `${sort?.field}-${sort?.order}-${state.pageSize}-${state.search}`;
|
|
21
|
+
if (sortKey !== prevSortKeyRef.current) {
|
|
22
|
+
cursorsRef.current = /* @__PURE__ */ new Map();
|
|
23
|
+
prevSortKeyRef.current = sortKey;
|
|
24
|
+
}
|
|
25
|
+
const next = state.page > 0 ? cursorsRef.current.get(state.page) : void 0;
|
|
26
|
+
const cursor = {
|
|
27
|
+
limit: state.pageSize,
|
|
28
|
+
...next ? { next } : {}
|
|
29
|
+
};
|
|
30
|
+
const processPage = (page) => {
|
|
31
|
+
if (page?.cursor?.next) {
|
|
32
|
+
cursorsRef.current.set(state.page + 1, page.cursor.next);
|
|
33
|
+
}
|
|
34
|
+
return { hasMore: !!page?.cursor?.next };
|
|
35
|
+
};
|
|
36
|
+
return { cursor, sort, processPage };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export { useCursorPagination };
|
|
40
|
+
//# sourceMappingURL=useCursorPagination.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCursorPagination.esm.js","sources":["../../src/hooks/useCursorPagination.ts"],"sourcesContent":["import { useRef } from 'react';\n\nexport interface DataListSort {\n field: string;\n direction: 'asc' | 'desc';\n}\nexport interface DataListState {\n search: string;\n page: number;\n pageSize: number;\n sort: DataListSort | null;\n}\n\ninterface PageResult {\n cursor?: { next?: string | null } | null;\n}\n\nconst mapSort = (\n sort: DataListSort | null,\n fieldMap: Record<string, string>,\n): { field: string; order: 'ASC' | 'DESC' } | undefined => {\n if (!sort) {\n return undefined;\n }\n const field = fieldMap[sort.field];\n if (!field) {\n return undefined;\n }\n return { field, order: sort.direction === 'asc' ? 'ASC' : 'DESC' };\n};\n\n/**\n * Cursor pagination bridge for DataList's page-index model, ported from the web\n * app. Tracks the next-cursor for each page and resets when sort/pageSize\n * change.\n */\nexport const useCursorPagination = ({\n state,\n sortFieldMap = {},\n}: {\n state: DataListState;\n sortFieldMap?: Record<string, string>;\n}) => {\n const cursorsRef = useRef(new Map<number, string>());\n const prevSortKeyRef = useRef('');\n\n const sort = mapSort(state.sort, sortFieldMap);\n\n // Cursors are only valid for the exact query shape that produced them —\n // reset the map when sort, pageSize, or search change (a cursor from one\n // filter set is meaningless in another).\n const sortKey = `${sort?.field}-${sort?.order}-${state.pageSize}-${state.search}`;\n if (sortKey !== prevSortKeyRef.current) {\n cursorsRef.current = new Map();\n prevSortKeyRef.current = sortKey;\n }\n\n const next = state.page > 0 ? cursorsRef.current.get(state.page) : undefined;\n const cursor = {\n limit: state.pageSize,\n ...(next ? { next } : {}),\n };\n\n const processPage = (page: PageResult | null | undefined) => {\n if (page?.cursor?.next) {\n cursorsRef.current.set(state.page + 1, page.cursor.next);\n }\n return { hasMore: !!page?.cursor?.next };\n };\n\n return { cursor, sort, processPage };\n};\n"],"names":[],"mappings":";;AAiBA,MAAM,OAAA,GAAU,CACd,IAAA,EACA,QAAA,KACyD;AACzD,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,IAAA,CAAK,KAAK,CAAA;AACjC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,KAAK,SAAA,KAAc,KAAA,GAAQ,QAAQ,MAAA,EAAO;AACnE,CAAA;AAOO,MAAM,sBAAsB,CAAC;AAAA,EAClC,KAAA;AAAA,EACA,eAAe;AACjB,CAAA,KAGM;AACJ,EAAA,MAAM,UAAA,GAAa,MAAA,iBAAO,IAAI,GAAA,EAAqB,CAAA;AACnD,EAAA,MAAM,cAAA,GAAiB,OAAO,EAAE,CAAA;AAEhC,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,KAAA,CAAM,IAAA,EAAM,YAAY,CAAA;AAK7C,EAAA,MAAM,OAAA,GAAU,CAAA,EAAG,IAAA,EAAM,KAAK,CAAA,CAAA,EAAI,IAAA,EAAM,KAAK,CAAA,CAAA,EAAI,KAAA,CAAM,QAAQ,CAAA,CAAA,EAAI,KAAA,CAAM,MAAM,CAAA,CAAA;AAC/E,EAAA,IAAI,OAAA,KAAY,eAAe,OAAA,EAAS;AACtC,IAAA,UAAA,CAAW,OAAA,uBAAc,GAAA,EAAI;AAC7B,IAAA,cAAA,CAAe,OAAA,GAAU,OAAA;AAAA,EAC3B;AAEA,EAAA,MAAM,IAAA,GAAO,MAAM,IAAA,GAAO,CAAA,GAAI,WAAW,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,IAAI,CAAA,GAAI,MAAA;AACnE,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,OAAO,KAAA,CAAM,QAAA;AAAA,IACb,GAAI,IAAA,GAAO,EAAE,IAAA,KAAS;AAAC,GACzB;AAEA,EAAA,MAAM,WAAA,GAAc,CAAC,IAAA,KAAwC;AAC3D,IAAA,IAAI,IAAA,EAAM,QAAQ,IAAA,EAAM;AACtB,MAAA,UAAA,CAAW,QAAQ,GAAA,CAAI,KAAA,CAAM,OAAO,CAAA,EAAG,IAAA,CAAK,OAAO,IAAI,CAAA;AAAA,IACzD;AACA,IAAA,OAAO,EAAE,OAAA,EAAS,CAAC,CAAC,IAAA,EAAM,QAAQ,IAAA,EAAK;AAAA,EACzC,CAAA;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,IAAA,EAAM,WAAA,EAAY;AACrC;;;;"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useRef, useEffect } from 'react';
|
|
2
|
+
import { useApi } from '@backstage/frontend-plugin-api';
|
|
3
|
+
import { massdriverApiRef } from '../api.esm.js';
|
|
4
|
+
|
|
5
|
+
const MAX_BACKOFF_MS = 1e4;
|
|
6
|
+
const backoffDelay = (attempt) => Math.min(1e3 * 2 ** Math.max(0, attempt - 1), MAX_BACKOFF_MS);
|
|
7
|
+
const sleep = (ms, signal) => new Promise((resolve) => {
|
|
8
|
+
const timer = setTimeout(resolve, ms);
|
|
9
|
+
signal.addEventListener(
|
|
10
|
+
"abort",
|
|
11
|
+
() => {
|
|
12
|
+
clearTimeout(timer);
|
|
13
|
+
resolve();
|
|
14
|
+
},
|
|
15
|
+
{ once: true }
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
const useMassdriverSubscription = (query, variables, {
|
|
19
|
+
onData,
|
|
20
|
+
onError,
|
|
21
|
+
skip
|
|
22
|
+
}) => {
|
|
23
|
+
const api = useApi(massdriverApiRef);
|
|
24
|
+
const onDataRef = useRef(onData);
|
|
25
|
+
const onErrorRef = useRef(onError);
|
|
26
|
+
onDataRef.current = onData;
|
|
27
|
+
onErrorRef.current = onError;
|
|
28
|
+
const variablesKey = JSON.stringify(variables ?? {});
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (skip) return void 0;
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const attempt = { count: 0 };
|
|
33
|
+
const fatal = { seen: false };
|
|
34
|
+
const handleData = (data) => {
|
|
35
|
+
attempt.count = 0;
|
|
36
|
+
onDataRef.current(data);
|
|
37
|
+
};
|
|
38
|
+
const handleError = (error) => {
|
|
39
|
+
if (error.fatal) fatal.seen = true;
|
|
40
|
+
onErrorRef.current?.(error);
|
|
41
|
+
};
|
|
42
|
+
const run = async () => {
|
|
43
|
+
while (!controller.signal.aborted) {
|
|
44
|
+
await api.subscribe(
|
|
45
|
+
query,
|
|
46
|
+
variables,
|
|
47
|
+
{ onData: handleData, onError: handleError },
|
|
48
|
+
controller.signal
|
|
49
|
+
);
|
|
50
|
+
if (controller.signal.aborted || fatal.seen) break;
|
|
51
|
+
attempt.count += 1;
|
|
52
|
+
await sleep(backoffDelay(attempt.count), controller.signal);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
void run();
|
|
56
|
+
return () => controller.abort();
|
|
57
|
+
}, [api, query, variablesKey, skip]);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export { useMassdriverSubscription as default, useMassdriverSubscription };
|
|
61
|
+
//# sourceMappingURL=useMassdriverSubscription.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMassdriverSubscription.esm.js","sources":["../../src/hooks/useMassdriverSubscription.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport { useApi } from '@backstage/frontend-plugin-api';\nimport { massdriverApiRef, type MassdriverSubscriptionError } from '../api';\n\nconst MAX_BACKOFF_MS = 10_000;\n\nconst backoffDelay = (attempt: number): number =>\n Math.min(1000 * 2 ** Math.max(0, attempt - 1), MAX_BACKOFF_MS);\n\n// Abortable sleep — resolves after `ms`, or immediately if the signal aborts.\nconst sleep = (ms: number, signal: AbortSignal): Promise<void> =>\n new Promise<void>(resolve => {\n const timer = setTimeout(resolve, ms);\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n resolve();\n },\n { once: true },\n );\n });\n\n/**\n * Subscribe to a Massdriver GraphQL subscription through the backend SSE relay.\n *\n * Opens one SSE stream per (query, variables) and re-subscribes when they\n * change. `onData` / `onError` are held in refs so passing fresh inline\n * callbacks each render does not tear the stream down. If the stream ends\n * (server close or transport error) it reconnects with exponential backoff\n * — a healthy message resets the backoff. Aborted on unmount / when `skip`\n * flips true.\n */\nexport const useMassdriverSubscription = <T = unknown>(\n query: string,\n variables: Record<string, unknown> | undefined,\n {\n onData,\n onError,\n skip,\n }: {\n onData: (data: T) => void;\n onError?: (error: Error) => void;\n skip?: boolean;\n },\n) => {\n const api = useApi(massdriverApiRef);\n const onDataRef = useRef(onData);\n const onErrorRef = useRef(onError);\n onDataRef.current = onData;\n onErrorRef.current = onError;\n\n const variablesKey = JSON.stringify(variables ?? {});\n\n useEffect(() => {\n if (skip) return undefined;\n const controller = new AbortController();\n const attempt = { count: 0 };\n // Fatal errors (auth/validation — see MassdriverSubscriptionError) can't\n // heal on retry; stop the loop instead of reconnecting forever.\n const fatal = { seen: false };\n\n const handleData = (data: T) => {\n attempt.count = 0; // healthy stream → reset backoff\n onDataRef.current(data);\n };\n const handleError = (error: MassdriverSubscriptionError) => {\n if (error.fatal) fatal.seen = true;\n onErrorRef.current?.(error);\n };\n\n const run = async () => {\n while (!controller.signal.aborted) {\n await api.subscribe<T>(\n query,\n variables,\n { onData: handleData, onError: handleError },\n controller.signal,\n );\n if (controller.signal.aborted || fatal.seen) break;\n attempt.count += 1;\n await sleep(backoffDelay(attempt.count), controller.signal);\n }\n };\n\n void run();\n return () => controller.abort();\n // `variables` is compared by value via `variablesKey`.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [api, query, variablesKey, skip]);\n};\n\nexport default useMassdriverSubscription;\n"],"names":[],"mappings":";;;;AAIA,MAAM,cAAA,GAAiB,GAAA;AAEvB,MAAM,YAAA,GAAe,CAAC,OAAA,KACpB,IAAA,CAAK,GAAA,CAAI,GAAA,GAAO,CAAA,IAAK,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAA,GAAU,CAAC,GAAG,cAAc,CAAA;AAG/D,MAAM,QAAQ,CAAC,EAAA,EAAY,MAAA,KACzB,IAAI,QAAc,CAAA,OAAA,KAAW;AAC3B,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,OAAA,EAAS,EAAE,CAAA;AACpC,EAAA,MAAA,CAAO,gBAAA;AAAA,IACL,OAAA;AAAA,IACA,MAAM;AACJ,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,OAAA,EAAQ;AAAA,IACV,CAAA;AAAA,IACA,EAAE,MAAM,IAAA;AAAK,GACf;AACF,CAAC,CAAA;AAYI,MAAM,yBAAA,GAA4B,CACvC,KAAA,EACA,SAAA,EACA;AAAA,EACE,MAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,KAKG;AACH,EAAA,MAAM,GAAA,GAAM,OAAO,gBAAgB,CAAA;AACnC,EAAA,MAAM,SAAA,GAAY,OAAO,MAAM,CAAA;AAC/B,EAAA,MAAM,UAAA,GAAa,OAAO,OAAO,CAAA;AACjC,EAAA,SAAA,CAAU,OAAA,GAAU,MAAA;AACpB,EAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AAErB,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,SAAA,CAAU,SAAA,IAAa,EAAE,CAAA;AAEnD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,MAAM,OAAO,MAAA;AACjB,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,OAAA,GAAU,EAAE,KAAA,EAAO,CAAA,EAAE;AAG3B,IAAA,MAAM,KAAA,GAAQ,EAAE,IAAA,EAAM,KAAA,EAAM;AAE5B,IAAA,MAAM,UAAA,GAAa,CAAC,IAAA,KAAY;AAC9B,MAAA,OAAA,CAAQ,KAAA,GAAQ,CAAA;AAChB,MAAA,SAAA,CAAU,QAAQ,IAAI,CAAA;AAAA,IACxB,CAAA;AACA,IAAA,MAAM,WAAA,GAAc,CAAC,KAAA,KAAuC;AAC1D,MAAA,IAAI,KAAA,CAAM,KAAA,EAAO,KAAA,CAAM,IAAA,GAAO,IAAA;AAC9B,MAAA,UAAA,CAAW,UAAU,KAAK,CAAA;AAAA,IAC5B,CAAA;AAEA,IAAA,MAAM,MAAM,YAAY;AACtB,MAAA,OAAO,CAAC,UAAA,CAAW,MAAA,CAAO,OAAA,EAAS;AACjC,QAAA,MAAM,GAAA,CAAI,SAAA;AAAA,UACR,KAAA;AAAA,UACA,SAAA;AAAA,UACA,EAAE,MAAA,EAAQ,UAAA,EAAY,OAAA,EAAS,WAAA,EAAY;AAAA,UAC3C,UAAA,CAAW;AAAA,SACb;AACA,QAAA,IAAI,UAAA,CAAW,MAAA,CAAO,OAAA,IAAW,KAAA,CAAM,IAAA,EAAM;AAC7C,QAAA,OAAA,CAAQ,KAAA,IAAS,CAAA;AACjB,QAAA,MAAM,MAAM,YAAA,CAAa,OAAA,CAAQ,KAAK,CAAA,EAAG,WAAW,MAAM,CAAA;AAAA,MAC5D;AAAA,IACF,CAAA;AAEA,IAAA,KAAK,GAAA,EAAI;AACT,IAAA,OAAO,MAAM,WAAW,KAAA,EAAM;AAAA,EAGhC,GAAG,CAAC,GAAA,EAAK,KAAA,EAAO,YAAA,EAAc,IAAI,CAAC,CAAA;AACrC;;;;"}
|