@datalayer/core 1.1.60 → 1.2.10
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/README.md +41 -18
- package/lib/api/DatalayerApi.d.ts +42 -1
- package/lib/api/DatalayerApi.js +90 -3
- package/lib/api/constants.d.ts +19 -1
- package/lib/api/constants.js +19 -1
- package/lib/api/contents/attachments.d.ts +9 -0
- package/lib/api/contents/attachments.js +39 -0
- package/lib/api/contents/bridges.d.ts +22 -0
- package/lib/api/contents/bridges.js +79 -0
- package/lib/api/contents/capabilities.d.ts +11 -0
- package/lib/api/contents/capabilities.js +21 -0
- package/lib/api/contents/cloudStorage.d.ts +31 -0
- package/lib/api/contents/cloudStorage.js +89 -0
- package/lib/api/contents/dataservers.d.ts +37 -0
- package/lib/api/contents/dataservers.js +121 -0
- package/lib/api/contents/datasets.d.ts +63 -0
- package/lib/api/contents/datasets.js +82 -0
- package/lib/api/contents/datasources.d.ts +70 -0
- package/lib/api/contents/datasources.js +149 -0
- package/lib/api/contents/generated.d.ts +1070 -0
- package/lib/api/contents/index.d.ts +16 -0
- package/lib/api/contents/index.js +20 -0
- package/lib/api/contents/mcp.d.ts +46 -0
- package/lib/api/contents/mcp.js +157 -0
- package/lib/api/contents/objects.d.ts +31 -0
- package/lib/api/contents/objects.js +88 -0
- package/lib/api/contents/operations.d.ts +3 -0
- package/lib/api/contents/operations.js +19 -0
- package/lib/api/contents/runtimeMounts.d.ts +40 -0
- package/lib/api/contents/runtimeMounts.js +59 -0
- package/lib/api/contents/sandboxUid.d.ts +21 -0
- package/lib/api/contents/sandboxUid.js +27 -0
- package/lib/api/contents/sources.d.ts +19 -0
- package/lib/api/contents/sources.js +119 -0
- package/lib/api/contents/sync.d.ts +18 -0
- package/lib/api/contents/sync.js +69 -0
- package/lib/api/contents/transfers.d.ts +38 -0
- package/lib/api/contents/transfers.js +123 -0
- package/lib/api/evals/client.d.ts +402 -0
- package/lib/api/evals/client.js +359 -0
- package/lib/api/evals/index.d.ts +10 -0
- package/lib/api/evals/index.js +18 -0
- package/lib/api/evals/request.d.ts +43 -0
- package/lib/api/evals/request.js +67 -0
- package/lib/api/evals/status.d.ts +27 -0
- package/lib/api/evals/status.js +122 -0
- package/lib/api/evals/types.d.ts +1335 -0
- package/lib/api/evals/types.js +5 -0
- package/lib/api/iam/connectedAgents.d.ts +68 -0
- package/lib/api/iam/connectedAgents.js +87 -0
- package/lib/api/iam/identityProviders.d.ts +76 -0
- package/lib/api/iam/identityProviders.js +187 -0
- package/lib/api/iam/index.d.ts +3 -1
- package/lib/api/iam/index.js +3 -1
- package/lib/api/iam/mcpAlertRules.d.ts +51 -0
- package/lib/api/iam/mcpAlertRules.js +198 -0
- package/lib/api/iam/mcpAuditSettings.d.ts +46 -0
- package/lib/api/iam/mcpAuditSettings.js +120 -0
- package/lib/api/iam/mcpPolicy.d.ts +181 -0
- package/lib/api/iam/mcpPolicy.js +168 -0
- package/lib/api/iam/organizationTeams.d.ts +13 -0
- package/lib/api/iam/organizationTeams.js +43 -0
- package/lib/api/iam/profile.d.ts +17 -1
- package/lib/api/iam/profile.js +27 -0
- package/lib/api/iam/scimTokens.d.ts +79 -0
- package/lib/api/iam/scimTokens.js +141 -0
- package/lib/api/iam/secrets.js +9 -21
- package/lib/api/iam/serviceAgents.d.ts +68 -0
- package/lib/api/iam/serviceAgents.js +119 -0
- package/lib/api/iam/trials.d.ts +38 -0
- package/lib/api/iam/trials.js +66 -0
- package/lib/api/index.d.ts +8 -1
- package/lib/api/index.js +7 -1
- package/lib/api/mcp/activity.d.ts +37 -0
- package/lib/api/mcp/activity.js +26 -0
- package/lib/api/mcp/alerts.d.ts +25 -0
- package/lib/api/mcp/alerts.js +51 -0
- package/lib/api/mcp/audit.d.ts +29 -0
- package/lib/api/mcp/audit.js +55 -0
- package/lib/api/mcp/bindings.d.ts +18 -0
- package/lib/api/mcp/bindings.js +42 -0
- package/lib/api/mcp/forwarding.d.ts +25 -0
- package/lib/api/mcp/forwarding.js +46 -0
- package/lib/api/mcp/gateway.d.ts +16 -0
- package/lib/api/mcp/gateway.js +70 -0
- package/lib/api/mcp/generated.d.ts +22 -0
- package/lib/api/mcp/generated.js +213 -0
- package/lib/api/mcp/index.d.ts +17 -0
- package/lib/api/mcp/index.js +25 -0
- package/lib/api/mcp/observability.d.ts +141 -0
- package/lib/api/mcp/observability.js +267 -0
- package/lib/api/mcp/operations.d.ts +83 -0
- package/lib/api/mcp/operations.js +50 -0
- package/lib/api/mcp/organizations.d.ts +102 -0
- package/lib/api/mcp/organizations.js +46 -0
- package/lib/api/mcp/policy.d.ts +9 -0
- package/lib/api/mcp/policy.js +23 -0
- package/lib/api/mcp/tasks.d.ts +49 -0
- package/lib/api/mcp/tasks.js +147 -0
- package/lib/api/orchestration/client.d.ts +53 -0
- package/lib/api/orchestration/client.js +105 -0
- package/lib/api/orchestration/events.d.ts +88 -0
- package/lib/api/orchestration/events.js +245 -0
- package/lib/api/orchestration/generated.d.ts +693 -0
- package/lib/api/orchestration/generated.js +687 -0
- package/lib/api/orchestration/index.d.ts +30 -0
- package/lib/api/orchestration/index.js +34 -0
- package/lib/api/orchestration/lifecycle.d.ts +39 -0
- package/lib/api/orchestration/lifecycle.js +57 -0
- package/lib/api/orchestration/measures.d.ts +55 -0
- package/lib/api/orchestration/measures.js +99 -0
- package/lib/api/otel/dashboards.d.ts +56 -0
- package/lib/api/otel/dashboards.js +50 -0
- package/lib/api/otel/index.d.ts +1 -0
- package/lib/api/otel/index.js +1 -0
- package/lib/api/otel/metrics.d.ts +16 -0
- package/lib/api/otel/metrics.js +15 -0
- package/lib/api/scheduler/client.d.ts +32 -0
- package/lib/api/scheduler/client.js +42 -0
- package/lib/api/scheduler/index.d.ts +8 -0
- package/lib/api/scheduler/index.js +16 -0
- package/lib/api/scheduler/request.d.ts +28 -0
- package/lib/api/scheduler/request.js +50 -0
- package/lib/api/scheduler/types.d.ts +88 -0
- package/lib/api/scheduler/types.js +5 -0
- package/lib/api/spacer/comments.d.ts +97 -0
- package/lib/api/spacer/comments.js +45 -0
- package/lib/api/spacer/index.d.ts +9 -0
- package/lib/api/spacer/index.js +17 -0
- package/lib/api/spacer/notebooks.d.ts +33 -0
- package/lib/api/spacer/notebooks.js +39 -0
- package/lib/api/spacer/request.d.ts +25 -0
- package/lib/api/spacer/request.js +46 -0
- package/lib/api/spacer/spaces.d.ts +68 -0
- package/lib/api/spacer/spaces.js +45 -0
- package/lib/api/utils/validation.d.ts +4 -4
- package/lib/api/utils/validation.js +10 -7
- package/lib/client/auth/index.js +0 -5
- package/lib/client/auth/storage.js +0 -5
- package/lib/client/auth/strategies.js +0 -5
- package/lib/client/base.d.ts +6 -0
- package/lib/client/base.js +8 -0
- package/lib/client/constants.d.ts +1 -0
- package/lib/client/constants.js +1 -0
- package/lib/client/index.d.ts +0 -10
- package/lib/client/index.js +2 -2
- package/lib/client/mixins/ContentsMixin.d.ts +71 -0
- package/lib/client/mixins/ContentsMixin.js +157 -0
- package/lib/client/mixins/IAMMixin.d.ts +0 -31
- package/lib/client/mixins/IAMMixin.js +0 -58
- package/lib/components/animation/AnimatedText.d.ts +2 -44
- package/lib/components/animation/AnimatedText.js +6 -126
- package/lib/components/anonymous/AnonymousKeyExpired.d.ts +56 -0
- package/lib/components/anonymous/AnonymousKeyExpired.js +96 -0
- package/lib/components/anonymous/AnonymousKeyTimer.d.ts +63 -0
- package/lib/components/anonymous/AnonymousKeyTimer.js +140 -0
- package/lib/components/anonymous/index.d.ts +7 -0
- package/lib/components/anonymous/index.js +15 -0
- package/lib/components/auth/SigInAPIKey.d.ts +1 -0
- package/lib/components/auth/SigInAPIKey.js +0 -6
- package/lib/components/auth/SignIn.d.ts +1 -0
- package/lib/components/auth/SignIn.js +0 -4
- package/lib/components/auth/SignIn.stories.d.ts +2 -2
- package/lib/components/avatars/BoringAvatar.d.ts +1 -1
- package/lib/components/avatars/BoringAvatar.stories.d.ts +1 -1
- package/lib/components/avatars/DLAvatar.d.ts +1 -1
- package/lib/components/avatars/DLAvatar.js +0 -4
- package/lib/components/avatars/UserAvatar.d.ts +16 -1
- package/lib/components/avatars/UserAvatar.js +15 -13
- package/lib/components/avatars/UserProfileAvatar.d.ts +1 -1
- package/lib/components/banners/NoAutomationBanner.d.ts +1 -1
- package/lib/components/billing/BillingEntitySelect.d.ts +16 -0
- package/lib/components/billing/BillingEntitySelect.js +49 -27
- package/lib/components/billing/eligibility.d.ts +22 -0
- package/lib/components/billing/eligibility.js +27 -0
- package/lib/components/billing/index.d.ts +1 -0
- package/lib/components/billing/index.js +1 -4
- package/lib/components/buttons/DownloadCSVButton.d.ts +1 -1
- package/lib/components/buttons/DownloadJsonButton.d.ts +1 -1
- package/lib/components/buttons/LongActionButton.d.ts +1 -0
- package/lib/components/buttons/LongActionButton.js +0 -4
- package/lib/components/buttons/UploadButton.d.ts +1 -0
- package/lib/components/buttons/UploadButton.js +0 -4
- package/lib/components/checkout/StripeCheckout.d.ts +1 -1
- package/lib/components/collaboration/LiveEditorCollaborators.d.ts +36 -0
- package/lib/components/collaboration/LiveEditorCollaborators.js +88 -53
- package/lib/components/collaboration/index.js +0 -4
- package/lib/components/confetti/ConfettiSuccess.d.ts +1 -1
- package/lib/components/contents/ContentSourceCard.d.ts +8 -0
- package/lib/components/contents/ContentSourceCard.js +23 -0
- package/lib/components/contents/HomeFolderObjectsBrowser.d.ts +10 -0
- package/lib/components/contents/HomeFolderObjectsBrowser.js +148 -0
- package/lib/components/contents/index.d.ts +2 -0
- package/lib/components/contents/index.js +6 -0
- package/lib/components/context/OrganizationSelect.d.ts +1 -1
- package/lib/components/display/AvatarSkeleton.d.ts +1 -1
- package/lib/components/display/BusyDots.d.ts +1 -1
- package/lib/components/display/CenteredSpinner.d.ts +1 -1
- package/lib/components/display/CodePreview.d.ts +1 -1
- package/lib/components/display/DatalayerBox.d.ts +7 -3
- package/lib/components/display/DatalayerBox.js +7 -7
- package/lib/components/display/HorizontalCenter.d.ts +1 -1
- package/lib/components/display/JupyterDialog.d.ts +1 -0
- package/lib/components/display/JupyterDialog.js +1 -7
- package/lib/components/display/Markdown.d.ts +1 -0
- package/lib/components/display/NavLink.js +3 -2
- package/lib/components/display/NotebookSkeleton.d.ts +1 -1
- package/lib/components/display/Placeholder.d.ts +1 -1
- package/lib/components/display/ToTopBranded.d.ts +1 -1
- package/lib/components/echarts/EChartsReact.d.ts +1 -1
- package/lib/components/flashes/FlashClosable.d.ts +1 -0
- package/lib/components/flashes/FlashClosable.js +0 -4
- package/lib/components/flashes/FlashGuest.d.ts +1 -1
- package/lib/components/flashes/FlashSurveys.d.ts +1 -1
- package/lib/components/flashes/FlashUnauthorized.d.ts +1 -0
- package/lib/components/flashes/FlashUnauthorized.js +0 -6
- package/lib/components/flashes/surveys/Survey2025_1.d.ts +1 -1
- package/lib/components/iam/ExternalTokenSilentLogin.d.ts +1 -1
- package/lib/components/index.d.ts +2 -0
- package/lib/components/index.js +2 -0
- package/lib/components/labels/StatusLabels.d.ts +2 -9
- package/lib/components/labels/StatusLabels.js +2 -26
- package/lib/components/labels/VisibilityLabel.d.ts +1 -1
- package/lib/components/landings/StepBlock.d.ts +1 -1
- package/lib/components/mcp/ClientBadge.d.ts +41 -0
- package/lib/components/mcp/ClientBadge.js +33 -0
- package/lib/components/mcp/McpErrorBlankslate.d.ts +21 -0
- package/lib/components/mcp/McpErrorBlankslate.js +17 -0
- package/lib/components/mcp/ScopeList.d.ts +28 -0
- package/lib/components/mcp/ScopeList.js +14 -0
- package/lib/components/mcp/index.d.ts +10 -0
- package/lib/components/mcp/index.js +18 -0
- package/lib/components/navbar/NavigationVisbilityObserver.d.ts +2 -1
- package/lib/components/navbar/SubdomainNavBar.d.ts +10 -5
- package/lib/components/navbar/SubdomainNavBar.js +2 -1
- package/lib/components/notebooks/JupyterNotebook.d.ts +1 -1
- package/lib/components/notebooks/JupyterNotebookToolbar.d.ts +1 -1
- package/lib/components/principal/PrincipalAppearance.d.ts +22 -42
- package/lib/components/principal/PrincipalAppearance.js +232 -182
- package/lib/components/principal/PrincipalAvatar.d.ts +9 -1
- package/lib/components/principal/PrincipalAvatar.js +18 -10
- package/lib/components/principal/PrincipalBadge.d.ts +1 -1
- package/lib/components/principal/PrincipalBanner.d.ts +1 -1
- package/lib/components/principal/PrincipalDetailsOverlay.d.ts +38 -1
- package/lib/components/principal/PrincipalDetailsOverlay.js +64 -33
- package/lib/components/principal/PrincipalHoverCard.d.ts +31 -0
- package/lib/components/principal/PrincipalHoverCard.js +52 -0
- package/lib/components/principal/PrincipalSwitcherMenu.d.ts +1 -0
- package/lib/components/principal/PrincipalSwitcherMenu.js +5 -14
- package/lib/components/principal/index.d.ts +2 -1
- package/lib/components/principal/index.js +2 -1
- package/lib/components/progress/ConsumptionBar.d.ts +1 -0
- package/lib/components/progress/ConsumptionBar.js +0 -4
- package/lib/components/progress/ProgressRing.d.ts +1 -0
- package/lib/components/progress/SpinnerBar.d.ts +1 -1
- package/lib/components/progress/Timer.d.ts +1 -0
- package/lib/components/progress/Timer.js +0 -4
- package/lib/components/screencapture/Screencapture.js +46 -3
- package/lib/components/screencapture/ScreencaptureButton.d.ts +1 -1
- package/lib/components/sharing/ShareAccessComponent.d.ts +58 -3
- package/lib/components/sharing/ShareAccessComponent.js +143 -98
- package/lib/components/sharing/SharingEditor.d.ts +21 -0
- package/lib/components/sharing/SharingEditor.js +9 -28
- package/lib/components/sharing/index.d.ts +1 -0
- package/lib/components/sharing/index.js +1 -0
- package/lib/components/sharing/sandboxSharing.d.ts +26 -0
- package/lib/components/sharing/sandboxSharing.js +41 -0
- package/lib/components/spaces/SpaceDetailsOverlay.d.ts +39 -0
- package/lib/components/spaces/SpaceDetailsOverlay.js +80 -0
- package/lib/components/spaces/SpaceDisplay.d.ts +38 -0
- package/lib/components/spaces/SpaceDisplay.js +92 -0
- package/lib/components/spaces/index.d.ts +4 -0
- package/lib/components/spaces/index.js +7 -0
- package/lib/components/spaces/spaceDisplayModel.d.ts +93 -0
- package/lib/components/spaces/spaceDisplayModel.js +170 -0
- package/lib/components/sparklines/Sparklines.js +3 -4
- package/lib/components/sparklines/index.js +0 -4
- package/lib/components/storage/ContentsBrowser.d.ts +25 -1
- package/lib/components/storage/ContentsBrowser.js +145 -52
- package/lib/components/storage/ContentsBrowserMock.d.ts +3 -0
- package/lib/components/storage/ContentsBrowserMock.js +75 -0
- package/lib/components/storage/ContentsItems.d.ts +2 -1
- package/lib/components/storage/ContentsItems.js +0 -4
- package/lib/components/storage/ContentsTable.d.ts +50 -0
- package/lib/components/storage/ContentsTable.js +134 -0
- package/lib/components/storage/HomeFolderDrive.d.ts +71 -0
- package/lib/components/storage/HomeFolderDrive.js +248 -0
- package/lib/components/storage/index.d.ts +2 -0
- package/lib/components/storage/index.js +2 -0
- package/lib/components/students/StudentItemStatus.d.ts +1 -1
- package/lib/components/students/StudentResultCircle.d.ts +1 -1
- package/lib/components/subnav/SubNav.d.ts +5 -5
- package/lib/components/subnav/SubNav.js +11 -9
- package/lib/components/tables/DataTable.d.ts +1 -1
- package/lib/components/text-reveal/TextRevealAnimation.d.ts +2 -2
- package/lib/components/text-reveal/useTextRevealAnimationLines.d.ts +1 -1
- package/lib/components/time/LiveRelativeTime.d.ts +1 -0
- package/lib/components/time/LiveRelativeTime.js +0 -8
- package/lib/components/time/index.js +0 -4
- package/lib/components/timeline/Timeline.d.ts +1 -1
- package/lib/components/timeline/index.js +0 -4
- package/lib/components/users/PeerIndicator.d.ts +1 -0
- package/lib/components/users/PeerIndicator.js +0 -4
- package/lib/config/Configuration.d.ts +15 -17
- package/lib/config/Configuration.js +7 -0
- package/lib/hooks/cacheConverters.d.ts +0 -7
- package/lib/hooks/cacheConverters.js +0 -25
- package/lib/hooks/index.d.ts +3 -0
- package/lib/hooks/index.js +3 -0
- package/lib/hooks/layouts/LayoutBackdrop.d.ts +1 -1
- package/lib/hooks/layouts/LayoutScreencapture.d.ts +1 -1
- package/lib/hooks/useBackdrop.d.ts +1 -1
- package/lib/hooks/useBackdropJupyterLab.d.ts +3 -2
- package/lib/hooks/useBackdropJupyterLab.js +0 -4
- package/lib/hooks/useBillingEntityStore.js +3 -0
- package/lib/hooks/useCache.d.ts +478 -130
- package/lib/hooks/useCache.js +1580 -676
- package/lib/hooks/useContainsFocus.d.ts +1 -1
- package/lib/hooks/useContents.d.ts +334 -0
- package/lib/hooks/useContents.js +1033 -0
- package/lib/hooks/useFocusTrap.d.ts +4 -4
- package/lib/hooks/useFocusTrap.js +1 -1
- package/lib/hooks/useMcp.d.ts +315 -0
- package/lib/hooks/useMcp.js +891 -0
- package/lib/hooks/useMobile.js +0 -5
- package/lib/hooks/useNavigate.d.ts +41 -3
- package/lib/hooks/useNavigate.js +106 -52
- package/lib/hooks/usePrincipalCacheStore.js +3 -0
- package/lib/hooks/usePrincipalStore.js +3 -0
- package/lib/hooks/useProjectStore.d.ts +3 -3
- package/lib/hooks/useProjectStore.js +4 -4
- package/lib/hooks/useRef.d.ts +1 -1
- package/lib/hooks/useRef.js +3 -0
- package/lib/hooks/useScreencapture.d.ts +2 -1
- package/lib/hooks/useScreencapture.js +0 -4
- package/lib/hooks/useSpaceCacheStore.d.ts +50 -0
- package/lib/hooks/useSpaceCacheStore.js +85 -0
- package/lib/hooks/useToast.js +0 -4
- package/lib/hooks/useVisibilityObserver.d.ts +1 -1
- package/lib/index.d.ts +4 -1
- package/lib/index.js +3 -1
- package/lib/mocks/components/FlashMock.d.ts +1 -1
- package/lib/mocks/views/ActionMenuMock.d.ts +1 -1
- package/lib/mocks/views/ChartMock.d.ts +1 -1
- package/lib/mocks/views/DashboardMock.d.ts +1 -1
- package/lib/mocks/views/FormMock.d.ts +1 -1
- package/lib/mocks/views/TableMock.d.ts +1 -1
- package/lib/mocks/views/WipMock.d.ts +1 -1
- package/lib/models/Credits.d.ts +16 -0
- package/lib/models/Credits.js +22 -0
- package/lib/models/CreditsDTO.d.ts +6 -5
- package/lib/models/CreditsDTO.js +7 -5
- package/lib/models/McpAuditEvent.d.ts +52 -0
- package/lib/models/McpAuditEvent.js +5 -0
- package/lib/models/McpBinding.d.ts +57 -0
- package/lib/models/McpBinding.js +5 -0
- package/lib/models/McpPolicy.d.ts +77 -0
- package/lib/models/McpPolicy.js +5 -0
- package/lib/models/McpTask.d.ts +94 -0
- package/lib/models/McpTask.js +7 -0
- package/lib/models/Organization.d.ts +69 -0
- package/lib/models/Organization.js +96 -0
- package/lib/models/Secret.d.ts +1 -2
- package/lib/models/Secret.js +2 -18
- package/lib/models/StartedBy.d.ts +91 -0
- package/lib/models/StartedBy.js +117 -0
- package/lib/models/Team.d.ts +2 -0
- package/lib/models/Team.js +2 -0
- package/lib/models/User.d.ts +4 -0
- package/lib/models/User.js +3 -0
- package/lib/models/UserDTO.js +4 -2
- package/lib/models/UserSettings.d.ts +13 -0
- package/lib/models/UserSettings.js +9 -0
- package/lib/models/contents/__fixtures__/solr-codec.json +57 -0
- package/lib/models/contents/__fixtures__/v1-contracts.json +194 -0
- package/lib/models/contents/fieldCase.d.ts +8 -0
- package/lib/models/contents/fieldCase.js +37 -0
- package/lib/models/contents/index.d.ts +2 -0
- package/lib/models/contents/index.js +6 -0
- package/lib/models/contents/permissions.d.ts +16 -0
- package/lib/models/contents/permissions.js +34 -0
- package/lib/models/index.d.ts +5 -1
- package/lib/models/index.js +5 -1
- package/lib/navigation/components.js +2 -3
- package/lib/routes/publicPaths.js +4 -0
- package/lib/state/index.d.ts +1 -0
- package/lib/state/index.js +1 -0
- package/lib/state/sessionEnd.d.ts +55 -0
- package/lib/state/sessionEnd.js +162 -0
- package/lib/state/substates/CoreState.d.ts +15 -7
- package/lib/state/substates/CoreState.js +21 -17
- package/lib/state/substates/IAMState.d.ts +1 -1
- package/lib/state/substates/IAMState.js +12 -5
- package/lib/state/substates/LayoutState.js +12 -0
- package/lib/state/substates/ProfileState.d.ts +1 -0
- package/lib/state/substates/ProfileState.js +1 -4
- package/lib/state/substates/index.d.ts +0 -1
- package/lib/state/substates/index.js +0 -1
- package/lib/utils/Jwt.d.ts +26 -0
- package/lib/utils/Jwt.js +37 -0
- package/lib/utils/Lazy.d.ts +20 -0
- package/lib/utils/Lazy.js +32 -6
- package/lib/utils/Screencapture.js +3 -0
- package/lib/utils/WithSuspense.d.ts +11 -1
- package/lib/utils/WithSuspense.js +11 -1
- package/lib/views/api-keys/APIKeyEdit.d.ts +1 -1
- package/lib/views/api-keys/APIKeyNew.d.ts +1 -1
- package/lib/views/api-keys/APIKeys.d.ts +1 -1
- package/lib/views/api-keys/APIKeys.js +30 -4
- package/lib/views/api-keys/APIKeysStandalone.d.ts +5 -1
- package/lib/views/api-keys/APIKeysStandalone.js +2 -2
- package/lib/views/datasources/DatasourceDetail.d.ts +12 -5
- package/lib/views/datasources/DatasourceDetail.js +80 -110
- package/lib/views/datasources/DatasourceNew.d.ts +14 -8
- package/lib/views/datasources/DatasourceNew.js +129 -118
- package/lib/views/datasources/Datasources.d.ts +39 -7
- package/lib/views/datasources/Datasources.js +95 -40
- package/lib/views/datasources/DatasourcesMock.d.ts +3 -0
- package/lib/views/datasources/DatasourcesMock.js +57 -0
- package/lib/views/iam/SignInSimple.d.ts +12 -0
- package/lib/views/iam/SignInSimple.js +4 -4
- package/lib/views/index.d.ts +1 -0
- package/lib/views/index.js +1 -0
- package/lib/views/mcp/AdmittedClients.d.ts +44 -0
- package/lib/views/mcp/AdmittedClients.js +59 -0
- package/lib/views/mcp/AlertDestinations.d.ts +34 -0
- package/lib/views/mcp/AlertDestinations.js +75 -0
- package/lib/views/mcp/AlertRules.d.ts +32 -0
- package/lib/views/mcp/AlertRules.js +245 -0
- package/lib/views/mcp/ApprovalQueue.d.ts +66 -0
- package/lib/views/mcp/ApprovalQueue.js +95 -0
- package/lib/views/mcp/AuditLog.d.ts +42 -0
- package/lib/views/mcp/AuditLog.js +144 -0
- package/lib/views/mcp/ConnectedAgents.d.ts +34 -0
- package/lib/views/mcp/ConnectedAgents.js +135 -0
- package/lib/views/mcp/EnterpriseConsole.d.ts +74 -0
- package/lib/views/mcp/EnterpriseConsole.js +195 -0
- package/lib/views/mcp/IdentityProviders.d.ts +33 -0
- package/lib/views/mcp/IdentityProviders.js +299 -0
- package/lib/views/mcp/McpDashboard.d.ts +50 -0
- package/lib/views/mcp/McpDashboard.js +430 -0
- package/lib/views/mcp/McpHome.d.ts +92 -0
- package/lib/views/mcp/McpHome.js +153 -0
- package/lib/views/mcp/McpObservability.d.ts +79 -0
- package/lib/views/mcp/McpObservability.js +236 -0
- package/lib/views/mcp/NotebookRuns.d.ts +39 -0
- package/lib/views/mcp/NotebookRuns.js +44 -0
- package/lib/views/mcp/OrganizationPolicy.d.ts +45 -0
- package/lib/views/mcp/OrganizationPolicy.js +108 -0
- package/lib/views/mcp/OrganizationUsage.d.ts +69 -0
- package/lib/views/mcp/OrganizationUsage.js +113 -0
- package/lib/views/mcp/PersonalPolicy.d.ts +54 -0
- package/lib/views/mcp/PersonalPolicy.js +126 -0
- package/lib/views/mcp/Policies.d.ts +53 -0
- package/lib/views/mcp/Policies.js +210 -0
- package/lib/views/mcp/PolicyForm.d.ts +83 -0
- package/lib/views/mcp/PolicyForm.js +113 -0
- package/lib/views/mcp/PolicyHistory.d.ts +58 -0
- package/lib/views/mcp/PolicyHistory.js +75 -0
- package/lib/views/mcp/RunDetail.d.ts +87 -0
- package/lib/views/mcp/RunDetail.js +186 -0
- package/lib/views/mcp/ScimProvisioning.d.ts +54 -0
- package/lib/views/mcp/ScimProvisioning.js +173 -0
- package/lib/views/mcp/ServiceAgents.d.ts +46 -0
- package/lib/views/mcp/ServiceAgents.js +194 -0
- package/lib/views/mcp/TeamPolicies.d.ts +33 -0
- package/lib/views/mcp/TeamPolicies.js +159 -0
- package/lib/views/mcp/ToolAccess.d.ts +60 -0
- package/lib/views/mcp/ToolAccess.js +101 -0
- package/lib/views/mcp/TraceTimeline.d.ts +81 -0
- package/lib/views/mcp/TraceTimeline.js +132 -0
- package/lib/views/mcp/format.d.ts +25 -0
- package/lib/views/mcp/format.js +77 -0
- package/lib/views/mcp/index.d.ts +34 -0
- package/lib/views/mcp/index.js +42 -0
- package/lib/views/mcp/types.d.ts +66 -0
- package/lib/views/mcp/types.js +5 -0
- package/lib/views/otel/simpleAuthStore.js +3 -0
- package/lib/views/secrets/SecretEdit.d.ts +5 -1
- package/lib/views/secrets/SecretEdit.js +5 -4
- package/lib/views/secrets/SecretNew.d.ts +11 -1
- package/lib/views/secrets/SecretNew.js +7 -3
- package/lib/views/secrets/Secrets.d.ts +12 -1
- package/lib/views/secrets/Secrets.js +2 -2
- package/package.json +269 -260
- package/scripts/generate-contents-types.py +169 -0
- package/scripts/generate-mcp-types.py +233 -0
- package/scripts/generate-orchestration-types.py +920 -0
- package/lib/api/iam/datasources.d.ts +0 -86
- package/lib/api/iam/datasources.js +0 -185
- package/lib/components/toolbars/index.d.ts +0 -1
- package/lib/models/Datasource.d.ts +0 -181
- package/lib/models/Datasource.js +0 -154
- package/lib/state/substates/DatasourceState.d.ts +0 -11
- package/lib/state/substates/DatasourceState.js +0 -16
- /package/lib/{components/toolbars/index.js → api/contents/generated.js} +0 -0
package/lib/hooks/useCache.js
CHANGED
|
@@ -37,14 +37,15 @@
|
|
|
37
37
|
* ```
|
|
38
38
|
*/
|
|
39
39
|
import { useQuery, useQueries, useMutation, useQueryClient, } from '@tanstack/react-query';
|
|
40
|
-
import { BOOTSTRAP_USER_ONBOARDING, asContact,
|
|
41
|
-
import {
|
|
40
|
+
import { BOOTSTRAP_USER_ONBOARDING, asContact, asInvite, asOrganization, asSecret, asTeam, asToken, asUser, } from '../models/index.js';
|
|
41
|
+
import { asSpace } from './cacheConverters.js';
|
|
42
42
|
import { useCoreStore, useIAMStore, profileStore } from '../state/index.js';
|
|
43
43
|
import { asDisplayName, namesAsInitials, asArray } from '../utils/index.js';
|
|
44
44
|
import { useDatalayer } from './useDatalayer.js';
|
|
45
45
|
import { useAuthorization } from './useAuthorization.js';
|
|
46
46
|
import { useUploadForm } from './useUpload.js';
|
|
47
47
|
import { OUTPUTSHOT_PLACEHOLDER_DEFAULT_SVG } from './assets/index.js';
|
|
48
|
+
import { forgetSessionState } from '../state/sessionEnd.js';
|
|
48
49
|
// Kept for potential future use
|
|
49
50
|
// Default query options for all queries
|
|
50
51
|
const DEFAULT_QUERY_OPTIONS = {
|
|
@@ -57,6 +58,53 @@ const DEFAULT_QUERY_OPTIONS = {
|
|
|
57
58
|
// Ensure queries prioritize cache over network when data is fresh
|
|
58
59
|
networkMode: 'online',
|
|
59
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* Refresh what a visitor sees, not just what the editor is looking at.
|
|
63
|
+
*
|
|
64
|
+
* `invalidateQueries` marks queries stale and refetches the **active** ones —
|
|
65
|
+
* the ones some mounted component is watching. Everything else is left to
|
|
66
|
+
* refetch when it is next mounted, which is the usual and correct behaviour
|
|
67
|
+
* and does not work here.
|
|
68
|
+
*
|
|
69
|
+
* It does not work because the app's `QueryClient` is built with
|
|
70
|
+
* `refetchOnMount: false`. A query that mounts holding stale data renders it
|
|
71
|
+
* and asks for nothing. So an administrator on `/admin/library/featured`
|
|
72
|
+
* would reorder the ribbon, watch the admin list reorder itself correctly —
|
|
73
|
+
* that query is active — and then find the home page still showing the old
|
|
74
|
+
* order, because the ribbon was not mounted when the invalidation happened
|
|
75
|
+
* and would not ask again when it was. Only a full page reload, which throws
|
|
76
|
+
* the whole cache away, put it right.
|
|
77
|
+
*
|
|
78
|
+
* `refetchType: 'all'` fetches the inactive ones too, there and then. It
|
|
79
|
+
* belongs on the handful of mutations that change what somebody *else's*
|
|
80
|
+
* screen shows, and nowhere else: for an ordinary edit, refetching every
|
|
81
|
+
* dormant query in the cache is a burst of requests for pages nobody is on.
|
|
82
|
+
*/
|
|
83
|
+
const invalidateEverywhere = (client, queryKey) => {
|
|
84
|
+
void client.invalidateQueries({ queryKey, refetchType: 'all' });
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Forget everything a session was told, without orphaning what is on screen.
|
|
88
|
+
*
|
|
89
|
+
* `queryClient.clear()` is the obvious way to end a session and the wrong
|
|
90
|
+
* one. It destroys the query objects themselves, and an observer whose query
|
|
91
|
+
* is destroyed while its fetch is in flight is never answered and never asks
|
|
92
|
+
* again: it sits at `isLoading` for as long as the page stays open.
|
|
93
|
+
*
|
|
94
|
+
* Signing out is exactly that race. Ending the session swaps the whole tree
|
|
95
|
+
* for the anonymous one, which mounts the home page and starts fetching the
|
|
96
|
+
* featured ribbon; the logout response then lands on top of that fetch. The
|
|
97
|
+
* ribbon stayed a row of empty placeholders until the reader reloaded.
|
|
98
|
+
*
|
|
99
|
+
* So: drop what nobody is looking at, and reset what is on screen. A reset
|
|
100
|
+
* forgets the answer the person who has left was given and asks the question
|
|
101
|
+
* again as whoever is here now, which is what signing out means for a page
|
|
102
|
+
* that is still open.
|
|
103
|
+
*/
|
|
104
|
+
export const forgetSession = (client) => {
|
|
105
|
+
client.removeQueries({ type: 'inactive' });
|
|
106
|
+
client.resetQueries({ type: 'active' });
|
|
107
|
+
};
|
|
60
108
|
// ============================================================================
|
|
61
109
|
// Query Key Factories
|
|
62
110
|
// ============================================================================
|
|
@@ -66,6 +114,155 @@ const DEFAULT_QUERY_OPTIONS = {
|
|
|
66
114
|
* @see https://tanstack.com/query/latest/docs/framework/react/guides/query-keys
|
|
67
115
|
*/
|
|
68
116
|
export const queryKeys = {
|
|
117
|
+
// Contents catalog
|
|
118
|
+
contents: {
|
|
119
|
+
all: () => ['contents'],
|
|
120
|
+
sources: () => [...queryKeys.contents.all(), 'sources'],
|
|
121
|
+
capabilities: () => [...queryKeys.contents.all(), 'capabilities'],
|
|
122
|
+
sourceList: (filters) => [...queryKeys.contents.sources(), 'list', filters ?? {}],
|
|
123
|
+
source: (sourceUid) => [...queryKeys.contents.sources(), 'detail', sourceUid],
|
|
124
|
+
sourceSharing: (sourceUid) => [...queryKeys.contents.source(sourceUid), 'sharing'],
|
|
125
|
+
datasetRevisions: (sourceUid) => [...queryKeys.contents.source(sourceUid), 'revisions'],
|
|
126
|
+
datasetRevision: (sourceUid, revisionUid) => [...queryKeys.contents.datasetRevisions(sourceUid), revisionUid],
|
|
127
|
+
datasetPublications: (sourceUid) => [...queryKeys.contents.source(sourceUid), 'publications'],
|
|
128
|
+
publishedDatasets: () => [...queryKeys.contents.all(), 'publications', 'datasets'],
|
|
129
|
+
cloudObjects: (sourceUid, prefix, cursor) => [
|
|
130
|
+
...queryKeys.contents.source(sourceUid),
|
|
131
|
+
'cloud',
|
|
132
|
+
'objects',
|
|
133
|
+
prefix ?? '',
|
|
134
|
+
cursor ?? '',
|
|
135
|
+
],
|
|
136
|
+
credentialDiagnostics: (sourceUid) => [...queryKeys.contents.source(sourceUid), 'diagnostics'],
|
|
137
|
+
homeFolder: () => [...queryKeys.contents.sources(), 'user-folder'],
|
|
138
|
+
homeFolderQuota: () => [...queryKeys.contents.homeFolder(), 'quota'],
|
|
139
|
+
homeFolderObjects: (filters) => [
|
|
140
|
+
...queryKeys.contents.homeFolder(),
|
|
141
|
+
'objects',
|
|
142
|
+
'list',
|
|
143
|
+
filters ?? {},
|
|
144
|
+
],
|
|
145
|
+
homeFolderObject: (path) => [...queryKeys.contents.homeFolder(), 'objects', 'stat', path],
|
|
146
|
+
homeFolderObjectVersions: (objectUid, cursor) => [
|
|
147
|
+
...queryKeys.contents.homeFolder(),
|
|
148
|
+
'objects',
|
|
149
|
+
objectUid,
|
|
150
|
+
'versions',
|
|
151
|
+
cursor ?? '',
|
|
152
|
+
],
|
|
153
|
+
operations: () => [...queryKeys.contents.all(), 'operations'],
|
|
154
|
+
operation: (operationUid) => [...queryKeys.contents.operations(), operationUid],
|
|
155
|
+
transfers: () => [...queryKeys.contents.all(), 'transfers'],
|
|
156
|
+
transferList: (filters) => [...queryKeys.contents.transfers(), 'list', filters ?? {}],
|
|
157
|
+
transfer: (transferUid) => [...queryKeys.contents.transfers(), transferUid],
|
|
158
|
+
syncSessions: () => [...queryKeys.contents.all(), 'sync'],
|
|
159
|
+
syncSessionList: (filters) => [...queryKeys.contents.syncSessions(), 'list', filters ?? {}],
|
|
160
|
+
syncSession: (sessionUid) => [...queryKeys.contents.syncSessions(), sessionUid],
|
|
161
|
+
syncConflicts: (sessionUid) => [...queryKeys.contents.syncSession(sessionUid), 'conflicts'],
|
|
162
|
+
attachments: () => [...queryKeys.contents.all(), 'attachments'],
|
|
163
|
+
attachmentList: (filters) => [...queryKeys.contents.attachments(), 'list', filters ?? {}],
|
|
164
|
+
attachmentManifest: (sandboxUid) => [...queryKeys.contents.attachments(), 'manifest', sandboxUid],
|
|
165
|
+
// The session of one attachment lives under the attachments key on
|
|
166
|
+
// purpose: revoking or refreshing an attachment refreshes its bridge.
|
|
167
|
+
bridgeSession: (attachmentUid) => [...queryKeys.contents.attachments(), 'bridge', attachmentUid],
|
|
168
|
+
bridges: () => [...queryKeys.contents.all(), 'bridges'],
|
|
169
|
+
bridgeList: (filters) => [...queryKeys.contents.bridges(), 'list', filters ?? {}],
|
|
170
|
+
bridge: (bridgeUid) => [...queryKeys.contents.bridges(), bridgeUid],
|
|
171
|
+
// What a running Runtime has mounted. Under the Contents key rather than
|
|
172
|
+
// a Runtimes one because attaching and detaching a source is what changes
|
|
173
|
+
// it, and those invalidate from here.
|
|
174
|
+
runtimeMounts: (runtimeName) => [...queryKeys.contents.all(), 'runtime-mounts', runtimeName],
|
|
175
|
+
mcpTools: (sourceUid) => [...queryKeys.contents.source(sourceUid), 'mcp', 'tools'],
|
|
176
|
+
mcpSessions: () => [...queryKeys.contents.all(), 'mcp-sessions'],
|
|
177
|
+
mcpSessionList: (filters) => [...queryKeys.contents.mcpSessions(), 'list', filters ?? {}],
|
|
178
|
+
mcpSession: (sessionUid) => [...queryKeys.contents.mcpSessions(), sessionUid],
|
|
179
|
+
mcpCalls: (sessionUid) => [...queryKeys.contents.mcpSession(sessionUid), 'calls'],
|
|
180
|
+
mcpCall: (sessionUid, callUid) => [...queryKeys.contents.mcpCalls(sessionUid), callUid],
|
|
181
|
+
mcpApprovals: () => [...queryKeys.contents.all(), 'mcp-approvals'],
|
|
182
|
+
mcpApprovalList: (filters) => [...queryKeys.contents.mcpApprovals(), 'list', filters ?? {}],
|
|
183
|
+
datasourceSchema: (sourceUid) => [
|
|
184
|
+
...queryKeys.contents.source(sourceUid),
|
|
185
|
+
'datasource',
|
|
186
|
+
'schema',
|
|
187
|
+
],
|
|
188
|
+
datasourceCapabilities: (sourceUid) => [
|
|
189
|
+
...queryKeys.contents.source(sourceUid),
|
|
190
|
+
'datasource',
|
|
191
|
+
'capabilities',
|
|
192
|
+
],
|
|
193
|
+
datasourceQueries: (sourceUid) => [...queryKeys.contents.source(sourceUid), 'queries'],
|
|
194
|
+
// A query is addressed by its own uid, not under its source: a
|
|
195
|
+
// notebook reconnects to one by uid alone.
|
|
196
|
+
queries: () => [...queryKeys.contents.all(), 'queries'],
|
|
197
|
+
query: (queryUid) => [...queryKeys.contents.queries(), queryUid],
|
|
198
|
+
dataserverStatus: (sourceUid) => [
|
|
199
|
+
...queryKeys.contents.source(sourceUid),
|
|
200
|
+
'dataserver',
|
|
201
|
+
'status',
|
|
202
|
+
],
|
|
203
|
+
},
|
|
204
|
+
// The Jupyter MCP Server: what the agents did, and what they may do
|
|
205
|
+
mcp: {
|
|
206
|
+
all: () => ['mcp'],
|
|
207
|
+
tasks: () => [...queryKeys.mcp.all(), 'tasks'],
|
|
208
|
+
taskList: (filters) => [...queryKeys.mcp.tasks(), 'list', filters ?? {}],
|
|
209
|
+
task: (taskUid) => [...queryKeys.mcp.tasks(), taskUid],
|
|
210
|
+
notebookTasks: (notebookUid, filters) => [
|
|
211
|
+
...queryKeys.mcp.tasks(),
|
|
212
|
+
'notebook',
|
|
213
|
+
notebookUid,
|
|
214
|
+
filters ?? {},
|
|
215
|
+
],
|
|
216
|
+
bindings: () => [...queryKeys.mcp.all(), 'bindings'],
|
|
217
|
+
bindingList: (filters) => [...queryKeys.mcp.bindings(), 'list', filters ?? {}],
|
|
218
|
+
activity: (filters) => [...queryKeys.mcp.all(), 'activity', filters ?? {}],
|
|
219
|
+
audit: () => [...queryKeys.mcp.all(), 'audit'],
|
|
220
|
+
auditList: (filters) => [...queryKeys.mcp.audit(), 'list', filters ?? {}],
|
|
221
|
+
policy: (filters) => [...queryKeys.mcp.all(), 'policy', filters ?? {}],
|
|
222
|
+
connectedAgents: () => [...queryKeys.mcp.all(), 'connected-agents'],
|
|
223
|
+
// Service agents are an organization's, never an account's: two
|
|
224
|
+
// organizations' lists must not share a cache entry.
|
|
225
|
+
serviceAgents: (orgUid) => [...queryKeys.mcp.all(), 'service-agents', orgUid],
|
|
226
|
+
// The layer IAM stores, keyed by the scope *and* the subject: an
|
|
227
|
+
// organization's policy and a team's are different documents, and a
|
|
228
|
+
// shared entry would show one under the other's name.
|
|
229
|
+
policyLayer: (scope, subjectUid) => [...queryKeys.mcp.all(), 'policy-layer', scope, subjectUid],
|
|
230
|
+
alertRules: (orgUid) => [...queryKeys.mcp.all(), 'alert-rules', orgUid],
|
|
231
|
+
identityProviders: (orgUid) => [...queryKeys.mcp.all(), 'identity-providers', orgUid],
|
|
232
|
+
scimTokens: (orgUid) => [...queryKeys.mcp.all(), 'scim-tokens', orgUid],
|
|
233
|
+
// Keyed by scope *and* subject, like `policyLayer`: an organization's
|
|
234
|
+
// admitted clients and a team's are different lists, and a shared entry
|
|
235
|
+
// would show one under the other's name.
|
|
236
|
+
admittedClients: (scope, subjectUid) => [...queryKeys.mcp.all(), 'admitted-clients', scope, subjectUid],
|
|
237
|
+
forwarding: (orgUid) => [...queryKeys.mcp.all(), 'audit-forwarding', orgUid],
|
|
238
|
+
auditSettings: (orgUid) => [...queryKeys.mcp.all(), 'audit-settings', orgUid],
|
|
239
|
+
organizationTeams: (orgUid) => [...queryKeys.mcp.all(), 'organization-teams', orgUid],
|
|
240
|
+
// Observability, over the OTEL service, keyed by the task it describes.
|
|
241
|
+
trace: (taskUid) => [...queryKeys.mcp.task(taskUid), 'trace'],
|
|
242
|
+
// A trace named directly: a synchronous call has one and no task.
|
|
243
|
+
traceById: (traceId) => [...queryKeys.mcp.all(), 'traces', traceId],
|
|
244
|
+
logs: (taskUid) => [...queryKeys.mcp.task(taskUid), 'logs'],
|
|
245
|
+
metrics: (filters) => [...queryKeys.mcp.all(), 'metrics', filters ?? {}],
|
|
246
|
+
operations: () => [...queryKeys.mcp.all(), 'operations'],
|
|
247
|
+
workers: () => [...queryKeys.mcp.operations(), 'workers'],
|
|
248
|
+
workflows: () => [...queryKeys.mcp.operations(), 'workflows'],
|
|
249
|
+
gatewayVersion: () => [...queryKeys.mcp.all(), 'version'],
|
|
250
|
+
// The Enterprise console: one answer per organization, narrowed by team.
|
|
251
|
+
orgOverview: (orgUid, filters) => [
|
|
252
|
+
...queryKeys.mcp.all(),
|
|
253
|
+
'organizations',
|
|
254
|
+
orgUid,
|
|
255
|
+
'overview',
|
|
256
|
+
filters ?? {},
|
|
257
|
+
],
|
|
258
|
+
orgUsage: (orgUid, filters) => [
|
|
259
|
+
...queryKeys.mcp.all(),
|
|
260
|
+
'organizations',
|
|
261
|
+
orgUid,
|
|
262
|
+
'usage',
|
|
263
|
+
filters ?? {},
|
|
264
|
+
],
|
|
265
|
+
},
|
|
69
266
|
// Authentication & Profile
|
|
70
267
|
auth: {
|
|
71
268
|
me: () => ['auth', 'me'],
|
|
@@ -148,6 +345,14 @@ export const queryKeys = {
|
|
|
148
345
|
bySpace: (spaceId) => [...queryKeys.documents.all(), 'space', spaceId],
|
|
149
346
|
model: (documentId) => [...queryKeys.documents.detail(documentId), 'model'],
|
|
150
347
|
},
|
|
348
|
+
// Decks
|
|
349
|
+
decks: {
|
|
350
|
+
all: () => ['decks'],
|
|
351
|
+
details: () => [...queryKeys.decks.all(), 'detail'],
|
|
352
|
+
detail: (id) => [...queryKeys.decks.details(), id],
|
|
353
|
+
bySpace: (spaceId) => [...queryKeys.decks.all(), 'space', spaceId],
|
|
354
|
+
model: (deckId) => [...queryKeys.decks.detail(deckId), 'model'],
|
|
355
|
+
},
|
|
151
356
|
// Cells
|
|
152
357
|
cells: {
|
|
153
358
|
all: () => ['cells'],
|
|
@@ -155,13 +360,6 @@ export const queryKeys = {
|
|
|
155
360
|
detail: (id) => [...queryKeys.cells.details(), id],
|
|
156
361
|
bySpace: (spaceId) => [...queryKeys.cells.all(), 'space', spaceId],
|
|
157
362
|
},
|
|
158
|
-
// Datasets
|
|
159
|
-
datasets: {
|
|
160
|
-
all: () => ['datasets'],
|
|
161
|
-
details: () => [...queryKeys.datasets.all(), 'detail'],
|
|
162
|
-
detail: (id) => [...queryKeys.datasets.details(), id],
|
|
163
|
-
bySpace: (spaceId) => [...queryKeys.datasets.all(), 'space', spaceId],
|
|
164
|
-
},
|
|
165
363
|
// Lessons
|
|
166
364
|
lessons: {
|
|
167
365
|
all: () => ['lessons'],
|
|
@@ -201,18 +399,6 @@ export const queryKeys = {
|
|
|
201
399
|
detail: (id) => [...queryKeys.environments.details(), id],
|
|
202
400
|
bySpace: (spaceId) => [...queryKeys.environments.all(), 'space', spaceId],
|
|
203
401
|
},
|
|
204
|
-
// Pages
|
|
205
|
-
pages: {
|
|
206
|
-
all: () => ['pages'],
|
|
207
|
-
details: () => [...queryKeys.pages.all(), 'detail'],
|
|
208
|
-
detail: (id) => [...queryKeys.pages.details(), id],
|
|
209
|
-
},
|
|
210
|
-
// Datasources
|
|
211
|
-
datasources: {
|
|
212
|
-
all: () => ['datasources'],
|
|
213
|
-
details: () => [...queryKeys.datasources.all(), 'detail'],
|
|
214
|
-
detail: (id) => [...queryKeys.datasources.details(), id],
|
|
215
|
-
},
|
|
216
402
|
// Secrets
|
|
217
403
|
secrets: {
|
|
218
404
|
all: () => ['secrets'],
|
|
@@ -270,8 +456,16 @@ export const queryKeys = {
|
|
|
270
456
|
items: {
|
|
271
457
|
all: () => ['items'],
|
|
272
458
|
public: () => [...queryKeys.items.all(), 'public'],
|
|
459
|
+
publicItem: (uid) => [...queryKeys.items.all(), 'public', uid],
|
|
273
460
|
bySpace: (spaceId) => [...queryKeys.items.all(), 'space', spaceId],
|
|
274
461
|
search: (opts) => [...queryKeys.items.all(), 'search', opts],
|
|
462
|
+
// The library: what the home page, the profile pages and "my orbits"
|
|
463
|
+
// read, each invalidated by the mutation that changes it.
|
|
464
|
+
featured: () => [...queryKeys.items.all(), 'featured'],
|
|
465
|
+
orbits: () => [...queryKeys.items.all(), 'orbits'],
|
|
466
|
+
orbiters: (uid) => [...queryKeys.items.all(), 'orbiters', uid],
|
|
467
|
+
derivations: (uid) => [...queryKeys.items.all(), 'derivations', uid],
|
|
468
|
+
byOwner: (handle) => [...queryKeys.items.all(), 'owner', handle],
|
|
275
469
|
},
|
|
276
470
|
// Layout
|
|
277
471
|
layout: {
|
|
@@ -352,6 +546,8 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
352
546
|
const { isLoading: notebookUploadLoading, uploadAndSubmit: uploadNotebook, progress: notebookUploadProgress, reset: resetNotebookUpload, } = useUploadForm(`${coreStore.configuration.spacerUrl}/api/spacer/v1/notebooks`);
|
|
353
547
|
// Hook for document upload/creation
|
|
354
548
|
const { isLoading: documentUploadLoading, uploadAndSubmit: uploadDocument, progress: documentUploadProgress, reset: resetDocumentUpload, } = useUploadForm(`${coreStore.configuration.spacerUrl}/api/spacer/v1/lexicals`);
|
|
549
|
+
// Hook for deck upload/creation
|
|
550
|
+
const { isLoading: deckUploadLoading, uploadAndSubmit: uploadDeck, progress: deckUploadProgress, reset: resetDeckUpload, } = useUploadForm(`${coreStore.configuration.spacerUrl}/api/spacer/v1/decks`);
|
|
355
551
|
// ============================================================================
|
|
356
552
|
// Transformation Functions (kept from original useCache)
|
|
357
553
|
// Note: These functions use 'any' because they handle dynamic API responses
|
|
@@ -451,16 +647,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
451
647
|
const toSpace = (spc) => {
|
|
452
648
|
return asSpace(spc);
|
|
453
649
|
};
|
|
454
|
-
const toPage = (s) => {
|
|
455
|
-
if (s) {
|
|
456
|
-
return asPage(s);
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
const toDatasource = (s) => {
|
|
460
|
-
if (s) {
|
|
461
|
-
return asDatasource(s);
|
|
462
|
-
}
|
|
463
|
-
};
|
|
464
650
|
const toSecret = (s) => {
|
|
465
651
|
if (s) {
|
|
466
652
|
return asSecret(s);
|
|
@@ -486,6 +672,228 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
486
672
|
* is available so the UI hydration hooks (e.g. `useUsersByUids`) can
|
|
487
673
|
* still fill in display data downstream.
|
|
488
674
|
*/
|
|
675
|
+
/**
|
|
676
|
+
* What the library knows about an artifact, on top of what its own service
|
|
677
|
+
* stores: how many people orbit it, whether this visitor does, how often it
|
|
678
|
+
* has been reused, whether it is featured, and what it looks like.
|
|
679
|
+
*
|
|
680
|
+
* Applied in one place — `toItem`, the library and search path — rather than
|
|
681
|
+
* in each mapper: a notebook read from its space has none of this, and
|
|
682
|
+
* inventing zeros there would say "nobody orbits it" where the truth is
|
|
683
|
+
* "this was not the library answering".
|
|
684
|
+
*/
|
|
685
|
+
const withLibraryMeta = (raw) => {
|
|
686
|
+
if (!raw || typeof raw !== 'object') {
|
|
687
|
+
return {};
|
|
688
|
+
}
|
|
689
|
+
const meta = {};
|
|
690
|
+
if (raw.orbits !== undefined)
|
|
691
|
+
meta.orbits = Number(raw.orbits) || 0;
|
|
692
|
+
if (raw.orbited !== undefined)
|
|
693
|
+
meta.orbited = Boolean(raw.orbited);
|
|
694
|
+
if (raw.clones !== undefined)
|
|
695
|
+
meta.clones = Number(raw.clones) || 0;
|
|
696
|
+
if (raw.instantiations !== undefined)
|
|
697
|
+
meta.instantiations = Number(raw.instantiations) || 0;
|
|
698
|
+
if (raw.featured !== undefined)
|
|
699
|
+
meta.featured = Boolean(raw.featured);
|
|
700
|
+
if (raw.featuredRank !== undefined)
|
|
701
|
+
meta.featuredRank = Number(raw.featuredRank);
|
|
702
|
+
if (raw.reuse !== undefined)
|
|
703
|
+
meta.reuse = raw.reuse;
|
|
704
|
+
if (raw.publisher !== undefined)
|
|
705
|
+
meta.publisher = raw.publisher;
|
|
706
|
+
if (raw.image !== undefined)
|
|
707
|
+
meta.image = raw.image;
|
|
708
|
+
/*
|
|
709
|
+
* Where the artifact can be opened for editing.
|
|
710
|
+
*
|
|
711
|
+
* An item is a nested child of its space and carries no handle of its
|
|
712
|
+
* own, so the library resolves the space when it projects one. Without
|
|
713
|
+
* these two a page can link to the public reading of an artifact and has
|
|
714
|
+
* no way to offer the editable one.
|
|
715
|
+
*/
|
|
716
|
+
if (raw.space_handle_s)
|
|
717
|
+
meta.spaceHandle = raw.space_handle_s;
|
|
718
|
+
if (raw.space_owner_handle_s)
|
|
719
|
+
meta.spaceOwnerHandle = raw.space_owner_handle_s;
|
|
720
|
+
/*
|
|
721
|
+
* The absolute address anyone can read it at.
|
|
722
|
+
*
|
|
723
|
+
* Built by the library, not here, because it needs the host the site is
|
|
724
|
+
* served from and the browser only knows the host it happens to be on —
|
|
725
|
+
* which on a development server is `localhost:3063`, the one address that
|
|
726
|
+
* is no use to whoever the link is being copied for.
|
|
727
|
+
*/
|
|
728
|
+
if (raw.publicUrl)
|
|
729
|
+
meta.publicUrl = raw.publicUrl;
|
|
730
|
+
return meta;
|
|
731
|
+
};
|
|
732
|
+
/**
|
|
733
|
+
* A published dataset. Keyed on the dataset it is a publication of, not on
|
|
734
|
+
* the publication: republishing mints a new immutable revision, and the
|
|
735
|
+
* library entry — with the orbits it has collected — is the dataset's.
|
|
736
|
+
*/
|
|
737
|
+
const toDataset = (raw) => {
|
|
738
|
+
const owner = toItemOwner(raw);
|
|
739
|
+
return {
|
|
740
|
+
id: raw.uid,
|
|
741
|
+
type: 'dataset',
|
|
742
|
+
name: raw.name_t,
|
|
743
|
+
description: raw.description_t,
|
|
744
|
+
tags: Array.isArray(raw.tags_ss) ? raw.tags_ss : [],
|
|
745
|
+
public: raw.is_public_b ?? true,
|
|
746
|
+
license: raw.license_s,
|
|
747
|
+
fileCount: raw.file_count_i,
|
|
748
|
+
totalSize: raw.total_size_l,
|
|
749
|
+
publicationId: raw.latest_publication_uid_s,
|
|
750
|
+
revisionId: raw.revision_uid_s,
|
|
751
|
+
creationDate: raw.creation_ts_dt
|
|
752
|
+
? new Date(raw.creation_ts_dt)
|
|
753
|
+
: undefined,
|
|
754
|
+
owner,
|
|
755
|
+
};
|
|
756
|
+
};
|
|
757
|
+
/**
|
|
758
|
+
* An agent as a library artifact: published by Datalayer rather than by a
|
|
759
|
+
* user, and reused by instantiating a runtime from its specification rather
|
|
760
|
+
* than by cloning a document. The specification itself lives in the
|
|
761
|
+
* application's own build; this is only what the library shows of it.
|
|
762
|
+
*/
|
|
763
|
+
/**
|
|
764
|
+
* A published Data Server: its catalog, never its address.
|
|
765
|
+
*
|
|
766
|
+
* What the library holds is what it answers for — the connector kinds and
|
|
767
|
+
* the relations they name. Reusing one attaches it as a Datasource; there
|
|
768
|
+
* is nothing to copy, because a Data Server is a process somebody runs.
|
|
769
|
+
*/
|
|
770
|
+
const toDataserver = (raw) => {
|
|
771
|
+
const owner = toItemOwner(raw);
|
|
772
|
+
return {
|
|
773
|
+
id: raw.uid,
|
|
774
|
+
type: 'dataserver',
|
|
775
|
+
name: raw.name_t,
|
|
776
|
+
description: raw.description_t,
|
|
777
|
+
tags: Array.isArray(raw.tags_ss) ? raw.tags_ss : [],
|
|
778
|
+
public: raw.is_public_b ?? true,
|
|
779
|
+
connectors: Array.isArray(raw.connectors_ss) ? raw.connectors_ss : [],
|
|
780
|
+
relations: Array.isArray(raw.relations_ss) ? raw.relations_ss : [],
|
|
781
|
+
publicationId: raw.latest_publication_uid_s,
|
|
782
|
+
creationDate: raw.creation_ts_dt
|
|
783
|
+
? new Date(raw.creation_ts_dt)
|
|
784
|
+
: undefined,
|
|
785
|
+
owner,
|
|
786
|
+
};
|
|
787
|
+
};
|
|
788
|
+
const toAgent = (raw) => {
|
|
789
|
+
return {
|
|
790
|
+
id: raw.uid,
|
|
791
|
+
type: 'agent',
|
|
792
|
+
name: raw.name_t,
|
|
793
|
+
description: raw.description_t,
|
|
794
|
+
tags: Array.isArray(raw.tags_ss) ? raw.tags_ss : [],
|
|
795
|
+
public: raw.is_public_b ?? true,
|
|
796
|
+
domain: raw.domain_s,
|
|
797
|
+
icon: raw.icon_s,
|
|
798
|
+
emoji: raw.emoji_s,
|
|
799
|
+
model: raw.model_s,
|
|
800
|
+
specId: String(raw.uid || '').replace(/^agent:/, ''),
|
|
801
|
+
specVersion: raw.spec_version_s,
|
|
802
|
+
publisher: raw.publisher_s || 'datalayer',
|
|
803
|
+
creationDate: raw.creation_ts_dt
|
|
804
|
+
? new Date(raw.creation_ts_dt)
|
|
805
|
+
: undefined,
|
|
806
|
+
};
|
|
807
|
+
};
|
|
808
|
+
/**
|
|
809
|
+
* A published benchmark report (B5-01): what it is about and where it
|
|
810
|
+
* stands, and the document it is written in, which the library reads for it
|
|
811
|
+
* and hands over as `model_s` — the field a document's reader looks for.
|
|
812
|
+
*/
|
|
813
|
+
const toReport = (raw) => {
|
|
814
|
+
return {
|
|
815
|
+
id: raw.uid,
|
|
816
|
+
type: 'report',
|
|
817
|
+
name: raw.name_t,
|
|
818
|
+
description: raw.description_t,
|
|
819
|
+
tags: Array.isArray(raw.tags_ss) ? raw.tags_ss : [],
|
|
820
|
+
public: raw.is_public_b ?? true,
|
|
821
|
+
state: raw.state_s,
|
|
822
|
+
version: raw.version_i,
|
|
823
|
+
runCount: raw.run_count_i,
|
|
824
|
+
evalsetId: raw.evalset_uid_s,
|
|
825
|
+
report: raw.report,
|
|
826
|
+
model_s: raw.model_s,
|
|
827
|
+
creationDate: raw.creation_ts_dt
|
|
828
|
+
? new Date(raw.creation_ts_dt)
|
|
829
|
+
: undefined,
|
|
830
|
+
lastPublicationDate: raw.published_ts_dt
|
|
831
|
+
? new Date(raw.published_ts_dt)
|
|
832
|
+
: undefined,
|
|
833
|
+
owner: toItemOwner(raw),
|
|
834
|
+
};
|
|
835
|
+
};
|
|
836
|
+
/**
|
|
837
|
+
* A published investigation (B5-01): its scope, where it stands and what was
|
|
838
|
+
* decided, and the document it is written in, as a report's is.
|
|
839
|
+
*/
|
|
840
|
+
const toInvestigation = (raw) => {
|
|
841
|
+
return {
|
|
842
|
+
id: raw.uid,
|
|
843
|
+
type: 'investigation',
|
|
844
|
+
name: raw.name_t,
|
|
845
|
+
description: raw.description_t,
|
|
846
|
+
tags: Array.isArray(raw.tags_ss) ? raw.tags_ss : [],
|
|
847
|
+
public: raw.is_public_b ?? true,
|
|
848
|
+
scope: raw.scope_s,
|
|
849
|
+
status: raw.status_s,
|
|
850
|
+
evalsetId: raw.evalset_uid_s,
|
|
851
|
+
investigation: raw.investigation,
|
|
852
|
+
model_s: raw.model_s,
|
|
853
|
+
creationDate: raw.creation_ts_dt
|
|
854
|
+
? new Date(raw.creation_ts_dt)
|
|
855
|
+
: undefined,
|
|
856
|
+
lastPublicationDate: raw.published_ts_dt
|
|
857
|
+
? new Date(raw.published_ts_dt)
|
|
858
|
+
: undefined,
|
|
859
|
+
owner: toItemOwner(raw),
|
|
860
|
+
};
|
|
861
|
+
};
|
|
862
|
+
/** An evaluator of the platform's catalogue (B5-01), published as an agent is. */
|
|
863
|
+
const toEvaluator = (raw) => {
|
|
864
|
+
return {
|
|
865
|
+
id: raw.uid,
|
|
866
|
+
type: 'evaluator',
|
|
867
|
+
name: raw.name_t,
|
|
868
|
+
description: raw.description_t,
|
|
869
|
+
tags: Array.isArray(raw.tags_ss) ? raw.tags_ss : [],
|
|
870
|
+
public: raw.is_public_b ?? true,
|
|
871
|
+
specId: String(raw.uid || '').replace(/^evaluator:/, ''),
|
|
872
|
+
specVersion: raw.spec_version_s,
|
|
873
|
+
publisher: raw.publisher_s || 'datalayer',
|
|
874
|
+
creationDate: raw.creation_ts_dt
|
|
875
|
+
? new Date(raw.creation_ts_dt)
|
|
876
|
+
: undefined,
|
|
877
|
+
};
|
|
878
|
+
};
|
|
879
|
+
/**
|
|
880
|
+
* A card of the site's own in the featured ribbon — the run the home page is
|
|
881
|
+
* written about, say. The library knows its name and whether it is
|
|
882
|
+
* featured; what it draws and where it leads are in the application's build,
|
|
883
|
+
* keyed by `cardId`.
|
|
884
|
+
*/
|
|
885
|
+
const toSiteCard = (raw) => {
|
|
886
|
+
return {
|
|
887
|
+
id: raw.uid,
|
|
888
|
+
type: 'sitecard',
|
|
889
|
+
name: raw.name_t,
|
|
890
|
+
description: raw.description_t,
|
|
891
|
+
tags: Array.isArray(raw.tags_ss) ? raw.tags_ss : [],
|
|
892
|
+
public: raw.is_public_b ?? true,
|
|
893
|
+
cardId: String(raw.uid || '').replace(/^sitecard:/, ''),
|
|
894
|
+
publisher: raw.publisher_s || 'datalayer',
|
|
895
|
+
};
|
|
896
|
+
};
|
|
489
897
|
const toItemOwner = (raw) => {
|
|
490
898
|
const uid = String(raw?.creator_uid_s ??
|
|
491
899
|
raw?.creator_uid ??
|
|
@@ -497,6 +905,15 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
497
905
|
const lastName = String(raw?.creator_last_name_t ?? raw?.last_name_t ?? '').trim();
|
|
498
906
|
const email = String(raw?.creator_email_s ?? raw?.email_s ?? '').trim();
|
|
499
907
|
const displayName = firstName || lastName ? asDisplayName(firstName, lastName) : handle;
|
|
908
|
+
/*
|
|
909
|
+
* The face, which the library has been projecting all along and nothing
|
|
910
|
+
* was reading. Without it every published artifact was attributed to a
|
|
911
|
+
* pair of initials, on its own public page as much as anywhere else —
|
|
912
|
+
* the owner is right there in the projection, and this is the one place
|
|
913
|
+
* it was being dropped.
|
|
914
|
+
*/
|
|
915
|
+
const avatarUrl = String(raw?.creator_avatar_url_s ?? raw?.avatar_url_s ?? '').trim();
|
|
916
|
+
const avatarIcon = String(raw?.creator_avatar_icon_s ?? raw?.avatar_icon_s ?? '').trim();
|
|
500
917
|
return {
|
|
501
918
|
id: uid,
|
|
502
919
|
handle,
|
|
@@ -505,6 +922,8 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
505
922
|
lastName,
|
|
506
923
|
initials: namesAsInitials(firstName, lastName),
|
|
507
924
|
displayName,
|
|
925
|
+
avatarUrl: avatarUrl || undefined,
|
|
926
|
+
avatarIcon: avatarIcon || undefined,
|
|
508
927
|
roles: [],
|
|
509
928
|
iamProviders: [],
|
|
510
929
|
setRoles: () => { },
|
|
@@ -516,35 +935,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
516
935
|
};
|
|
517
936
|
// Kept for potential future use
|
|
518
937
|
// Kept for potential future use
|
|
519
|
-
const toDataset = (raw_dataset) => {
|
|
520
|
-
const owner = toItemOwner(raw_dataset);
|
|
521
|
-
return {
|
|
522
|
-
id: raw_dataset.uid,
|
|
523
|
-
type: 'dataset',
|
|
524
|
-
name: raw_dataset.name_t,
|
|
525
|
-
description: raw_dataset.description_t,
|
|
526
|
-
fileName: raw_dataset.file_name_s,
|
|
527
|
-
datasetExtension: raw_dataset.dataset_extension_s,
|
|
528
|
-
contentLength: raw_dataset.content_length_i,
|
|
529
|
-
contentType: raw_dataset.content_type_s,
|
|
530
|
-
mimeType: raw_dataset.mimetype_s,
|
|
531
|
-
path: raw_dataset.s3_path_s,
|
|
532
|
-
cdnUrl: raw_dataset.cdn_url_s,
|
|
533
|
-
creationDate: new Date(raw_dataset.creation_ts_dt),
|
|
534
|
-
public: raw_dataset.is_public_b ?? false,
|
|
535
|
-
lastPublicationDate: raw_dataset.creation_ts_dt
|
|
536
|
-
? new Date(raw_dataset.creation_ts_dt)
|
|
537
|
-
: undefined,
|
|
538
|
-
owner,
|
|
539
|
-
space: {
|
|
540
|
-
handle: raw_dataset.handle_s,
|
|
541
|
-
},
|
|
542
|
-
organization: {
|
|
543
|
-
id: raw_dataset.organization_uid_s,
|
|
544
|
-
handle: raw_dataset.organization_handle_s,
|
|
545
|
-
},
|
|
546
|
-
};
|
|
547
|
-
};
|
|
548
938
|
const toCell = (cl) => {
|
|
549
939
|
const owner = toItemOwner(cl);
|
|
550
940
|
return {
|
|
@@ -552,6 +942,17 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
552
942
|
type: 'cell',
|
|
553
943
|
name: cl.name_t,
|
|
554
944
|
description: cl.description_t,
|
|
945
|
+
/*
|
|
946
|
+
* The words its owner filed it under.
|
|
947
|
+
*
|
|
948
|
+
* Every artifact type the library holds carries `tags_ss`, and the six
|
|
949
|
+
* space items — notebook, document, cell, lesson, exercise, assignment
|
|
950
|
+
* — were the six that never read it. So a notebook could be published
|
|
951
|
+
* under tags, be found by them in a search, and arrive at the card that
|
|
952
|
+
* shows them with none: the field was dropped here, one layer below
|
|
953
|
+
* anything that could notice.
|
|
954
|
+
*/
|
|
955
|
+
tags: Array.isArray(cl.tags_ss) ? cl.tags_ss : [],
|
|
555
956
|
source: cl.source_t,
|
|
556
957
|
creationDate: new Date(cl.creation_ts_dt),
|
|
557
958
|
public: cl.is_public_b ?? false,
|
|
@@ -577,6 +978,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
577
978
|
type: 'notebook',
|
|
578
979
|
name: raw_notebook.name_t,
|
|
579
980
|
description: raw_notebook.description_t,
|
|
981
|
+
tags: Array.isArray(raw_notebook.tags_ss) ? raw_notebook.tags_ss : [],
|
|
580
982
|
nbformat: raw_notebook.model_s
|
|
581
983
|
? JSON.parse(raw_notebook.model_s)
|
|
582
984
|
: undefined,
|
|
@@ -599,6 +1001,49 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
599
1001
|
},
|
|
600
1002
|
};
|
|
601
1003
|
};
|
|
1004
|
+
/**
|
|
1005
|
+
* Put a deck in the detail cache — but only a whole one.
|
|
1006
|
+
*
|
|
1007
|
+
* A listing answers with the space's items, and an item carries no
|
|
1008
|
+
* `model_s`: the specification lives in S3 and only `GET /decks/{uid}`
|
|
1009
|
+
* inlines it. Seeding the detail cache from such a row left `useDeck`
|
|
1010
|
+
* answering instantly with a deck whose `spec` is undefined — and since
|
|
1011
|
+
* these queries stay fresh for five minutes and do not refetch on mount,
|
|
1012
|
+
* an editor opened from the table drew nothing until the tab was reloaded.
|
|
1013
|
+
* A deck without its specification is not the deck this cache promises.
|
|
1014
|
+
*/
|
|
1015
|
+
const seedDeckDetail = (deck) => {
|
|
1016
|
+
if (deck?.spec) {
|
|
1017
|
+
queryClient.setQueryData(queryKeys.decks.detail(deck.id), deck);
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
const toDeck = (raw) => {
|
|
1021
|
+
const owner = toItemOwner(raw);
|
|
1022
|
+
return {
|
|
1023
|
+
id: raw.uid,
|
|
1024
|
+
type: 'deck',
|
|
1025
|
+
name: raw.name_t,
|
|
1026
|
+
description: raw.description_t,
|
|
1027
|
+
tags: Array.isArray(raw.tags_ss) ? raw.tags_ss : [],
|
|
1028
|
+
spec: raw.model_s ? JSON.parse(raw.model_s) : undefined,
|
|
1029
|
+
public: raw.is_public_b ?? false,
|
|
1030
|
+
creationDate: new Date(raw.creation_ts_dt),
|
|
1031
|
+
lastUpdateDate: raw.last_update_ts_dt
|
|
1032
|
+
? new Date(raw.last_update_ts_dt)
|
|
1033
|
+
: undefined,
|
|
1034
|
+
lastPublicationDate: raw.creation_ts_dt
|
|
1035
|
+
? new Date(raw.creation_ts_dt)
|
|
1036
|
+
: undefined,
|
|
1037
|
+
owner,
|
|
1038
|
+
space: {
|
|
1039
|
+
handle: raw.handle_s,
|
|
1040
|
+
},
|
|
1041
|
+
organization: {
|
|
1042
|
+
id: raw.organization_uid_s,
|
|
1043
|
+
handle: raw.organization_handle_s,
|
|
1044
|
+
},
|
|
1045
|
+
};
|
|
1046
|
+
};
|
|
602
1047
|
const toDocument = (doc) => {
|
|
603
1048
|
const owner = toItemOwner(doc);
|
|
604
1049
|
return {
|
|
@@ -606,6 +1051,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
606
1051
|
type: 'document',
|
|
607
1052
|
name: doc.name_t,
|
|
608
1053
|
description: doc.description_t,
|
|
1054
|
+
tags: Array.isArray(doc.tags_ss) ? doc.tags_ss : [],
|
|
609
1055
|
model: doc.model_s ? JSON.parse(doc.model_s) : undefined,
|
|
610
1056
|
public: doc.is_public_b ?? false,
|
|
611
1057
|
creationDate: new Date(doc.creation_ts_dt),
|
|
@@ -632,6 +1078,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
632
1078
|
type: 'lesson',
|
|
633
1079
|
name: raw_lesson.name_t,
|
|
634
1080
|
description: raw_lesson.description_t,
|
|
1081
|
+
tags: Array.isArray(raw_lesson.tags_ss) ? raw_lesson.tags_ss : [],
|
|
635
1082
|
nbformat: raw_lesson.model_s ? JSON.parse(raw_lesson.model_s) : undefined,
|
|
636
1083
|
public: raw_lesson.is_public_b ?? false,
|
|
637
1084
|
creationDate: new Date(raw_lesson.creation_ts_dt),
|
|
@@ -659,6 +1106,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
659
1106
|
type: 'exercise',
|
|
660
1107
|
name: ex.name_t,
|
|
661
1108
|
description: ex.description_t,
|
|
1109
|
+
tags: Array.isArray(ex.tags_ss) ? ex.tags_ss : [],
|
|
662
1110
|
help: ex.help_t,
|
|
663
1111
|
codePre: ex.code_pre_t,
|
|
664
1112
|
codeQuestion: ex.code_question_t,
|
|
@@ -704,6 +1152,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
704
1152
|
type: 'assignment',
|
|
705
1153
|
name: raw_assignment.name_t,
|
|
706
1154
|
description: raw_assignment.description_t,
|
|
1155
|
+
tags: Array.isArray(raw_assignment.tags_ss) ? raw_assignment.tags_ss : [],
|
|
707
1156
|
nbformat: raw_assignment.model_s
|
|
708
1157
|
? JSON.parse(raw_assignment.model_s)
|
|
709
1158
|
: undefined,
|
|
@@ -751,6 +1200,13 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
751
1200
|
};
|
|
752
1201
|
const toEvalset = (raw_evalset) => {
|
|
753
1202
|
const owner = toItemOwner(raw_evalset);
|
|
1203
|
+
const numberOrUndefined = (value) => {
|
|
1204
|
+
if (value === null || value === undefined || value === '') {
|
|
1205
|
+
return undefined;
|
|
1206
|
+
}
|
|
1207
|
+
const parsed = Number(value);
|
|
1208
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
1209
|
+
};
|
|
754
1210
|
return {
|
|
755
1211
|
id: raw_evalset.uid,
|
|
756
1212
|
type: 'evalset',
|
|
@@ -764,6 +1220,70 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
764
1220
|
? new Date(raw_evalset.last_update_ts_dt)
|
|
765
1221
|
: undefined,
|
|
766
1222
|
tags: Array.isArray(raw_evalset.tags_ss) ? raw_evalset.tags_ss : [],
|
|
1223
|
+
/*
|
|
1224
|
+
* What the runs of a benchmark say about it, projected onto its
|
|
1225
|
+
* library card whenever a run moves (BENCHMARK.md, B2-10 and B1-09).
|
|
1226
|
+
*
|
|
1227
|
+
* These were dropped here, so a card in the Library or on the home
|
|
1228
|
+
* page could show a name and a description and nothing that would
|
|
1229
|
+
* help anybody choose between two benchmarks — how big it is, what it
|
|
1230
|
+
* has been run on, how well anyone has done, what a run tends to
|
|
1231
|
+
* cost. The projection has been writing them all along.
|
|
1232
|
+
*/
|
|
1233
|
+
category: raw_evalset.category_s || undefined,
|
|
1234
|
+
caseCount: numberOrUndefined(raw_evalset.case_count_i),
|
|
1235
|
+
runCount: numberOrUndefined(raw_evalset.run_count_i),
|
|
1236
|
+
latestPassRate: numberOrUndefined(raw_evalset.latest_pass_rate_f),
|
|
1237
|
+
bestPassRate: numberOrUndefined(raw_evalset.best_pass_rate_f),
|
|
1238
|
+
lastRunStatus: raw_evalset.last_run_status_s || undefined,
|
|
1239
|
+
lastRunAt: raw_evalset.last_run_at_dt
|
|
1240
|
+
? new Date(raw_evalset.last_run_at_dt)
|
|
1241
|
+
: undefined,
|
|
1242
|
+
estimatedCost: numberOrUndefined(raw_evalset.estimated_cost_f),
|
|
1243
|
+
environment: raw_evalset.environment_s || undefined,
|
|
1244
|
+
subjectRefs: Array.isArray(raw_evalset.subject_refs_ss)
|
|
1245
|
+
? raw_evalset.subject_refs_ss
|
|
1246
|
+
: [],
|
|
1247
|
+
owner,
|
|
1248
|
+
};
|
|
1249
|
+
};
|
|
1250
|
+
/**
|
|
1251
|
+
* A published environment version as a library artifact (PLAN_ENV E2-16).
|
|
1252
|
+
*
|
|
1253
|
+
* Keyed on the version uid, so one published version is one card, and read
|
|
1254
|
+
* from the publication's immutable snapshot: its name, title, README (the
|
|
1255
|
+
* description), the licenses its SBOM names (the tags), its size class and
|
|
1256
|
+
* the variants it was built for. Republishing mints a new version — and a
|
|
1257
|
+
* new card — rather than moving this one.
|
|
1258
|
+
*/
|
|
1259
|
+
const toEnvironmentArtifact = (raw) => {
|
|
1260
|
+
const owner = toItemOwner(raw);
|
|
1261
|
+
const numberOrUndefined = (value) => {
|
|
1262
|
+
if (value === null || value === undefined || value === '') {
|
|
1263
|
+
return undefined;
|
|
1264
|
+
}
|
|
1265
|
+
const parsed = Number(value);
|
|
1266
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
1267
|
+
};
|
|
1268
|
+
return {
|
|
1269
|
+
id: raw.uid,
|
|
1270
|
+
type: 'environment',
|
|
1271
|
+
name: raw.name_t,
|
|
1272
|
+
title: raw.title_t || undefined,
|
|
1273
|
+
description: raw.description_t,
|
|
1274
|
+
tags: Array.isArray(raw.tags_ss) ? raw.tags_ss : [],
|
|
1275
|
+
licenses: Array.isArray(raw.tags_ss) ? raw.tags_ss : [],
|
|
1276
|
+
public: raw.is_public_b ?? true,
|
|
1277
|
+
environmentUid: raw.environment_uid_s || undefined,
|
|
1278
|
+
sizeClass: raw.size_class_s || undefined,
|
|
1279
|
+
variants: Array.isArray(raw.variants_ss) ? raw.variants_ss : [],
|
|
1280
|
+
versionNumber: numberOrUndefined(raw.version_number_i),
|
|
1281
|
+
creationDate: raw.creation_ts_dt
|
|
1282
|
+
? new Date(raw.creation_ts_dt)
|
|
1283
|
+
: undefined,
|
|
1284
|
+
lastUpdateDate: raw.source_update_ts_dt
|
|
1285
|
+
? new Date(raw.source_update_ts_dt)
|
|
1286
|
+
: undefined,
|
|
767
1287
|
owner,
|
|
768
1288
|
};
|
|
769
1289
|
};
|
|
@@ -772,6 +1292,13 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
772
1292
|
console.error('No type_s found on item', item);
|
|
773
1293
|
return {};
|
|
774
1294
|
}
|
|
1295
|
+
// The library answers with the same field names a space item has, plus
|
|
1296
|
+
// what only it knows; the type mapper reads the former and
|
|
1297
|
+
// `withLibraryMeta` adds the latter, so one artifact has one shape
|
|
1298
|
+
// wherever it was searched.
|
|
1299
|
+
return { ...toTypedItem(item), ...withLibraryMeta(item) };
|
|
1300
|
+
};
|
|
1301
|
+
const toTypedItem = (item) => {
|
|
775
1302
|
switch (item.type_s) {
|
|
776
1303
|
case 'assignment':
|
|
777
1304
|
return toAssignment(item);
|
|
@@ -816,8 +1343,10 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
816
1343
|
},
|
|
817
1344
|
};
|
|
818
1345
|
}
|
|
819
|
-
|
|
820
|
-
|
|
1346
|
+
// Without this the mapper answered `{}` for a deck: the card then
|
|
1347
|
+
// read no type and no name, and drew itself as a nameless notebook.
|
|
1348
|
+
case 'deck':
|
|
1349
|
+
return toDeck(item);
|
|
821
1350
|
case 'document':
|
|
822
1351
|
return toDocument(item);
|
|
823
1352
|
case 'exercise':
|
|
@@ -826,10 +1355,24 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
826
1355
|
return toLesson(item);
|
|
827
1356
|
case 'notebook':
|
|
828
1357
|
return toNotebook(item);
|
|
829
|
-
case 'page':
|
|
830
|
-
return toPage(item);
|
|
831
1358
|
case 'evalset':
|
|
832
1359
|
return toEvalset(item);
|
|
1360
|
+
case 'environment':
|
|
1361
|
+
return toEnvironmentArtifact(item);
|
|
1362
|
+
case 'dataset':
|
|
1363
|
+
return toDataset(item);
|
|
1364
|
+
case 'agent':
|
|
1365
|
+
return toAgent(item);
|
|
1366
|
+
case 'dataserver':
|
|
1367
|
+
return toDataserver(item);
|
|
1368
|
+
case 'report':
|
|
1369
|
+
return toReport(item);
|
|
1370
|
+
case 'investigation':
|
|
1371
|
+
return toInvestigation(item);
|
|
1372
|
+
case 'evaluator':
|
|
1373
|
+
return toEvaluator(item);
|
|
1374
|
+
case 'sitecard':
|
|
1375
|
+
return toSiteCard(item);
|
|
833
1376
|
default:
|
|
834
1377
|
return {};
|
|
835
1378
|
}
|
|
@@ -955,8 +1498,8 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
955
1498
|
});
|
|
956
1499
|
},
|
|
957
1500
|
onSuccess: () => {
|
|
958
|
-
|
|
959
|
-
|
|
1501
|
+
forgetSession(queryClient);
|
|
1502
|
+
forgetSessionState();
|
|
960
1503
|
},
|
|
961
1504
|
});
|
|
962
1505
|
};
|
|
@@ -993,11 +1536,11 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
993
1536
|
*/
|
|
994
1537
|
const useUpdateMe = () => {
|
|
995
1538
|
return useMutation({
|
|
996
|
-
mutationFn: async ({ email, firstName, lastName, avatarIcon, banner, }) => {
|
|
1539
|
+
mutationFn: async ({ email, firstName, lastName, avatarIcon, banner, showOrbits, }) => {
|
|
997
1540
|
return requestDatalayer({
|
|
998
1541
|
url: `${configuration.iamUrl}/api/iam/v1/me`,
|
|
999
1542
|
method: 'PUT',
|
|
1000
|
-
body: { email, firstName, lastName, avatarIcon, banner },
|
|
1543
|
+
body: { email, firstName, lastName, avatarIcon, banner, showOrbits },
|
|
1001
1544
|
});
|
|
1002
1545
|
},
|
|
1003
1546
|
onSuccess: () => {
|
|
@@ -1231,19 +1774,33 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
1231
1774
|
const useUpdateUserSettings = () => {
|
|
1232
1775
|
return useMutation({
|
|
1233
1776
|
mutationFn: async ({ userId, settings, }) => {
|
|
1777
|
+
// Only the keys the caller actually set. IAM merges what it
|
|
1778
|
+
// receives into the settings document, so sending a key means
|
|
1779
|
+
// changing it — an undefined one is dropped by `JSON.stringify`
|
|
1780
|
+
// and leaves the stored value alone.
|
|
1781
|
+
const body = {};
|
|
1782
|
+
if (settings.aiAgentsUrl !== undefined) {
|
|
1783
|
+
body.aiAgents_url_s = settings.aiAgentsUrl;
|
|
1784
|
+
}
|
|
1785
|
+
if (settings.canInvite !== undefined) {
|
|
1786
|
+
body.can_invite_b = settings.canInvite;
|
|
1787
|
+
}
|
|
1788
|
+
if (settings.docsInPlace !== undefined) {
|
|
1789
|
+
body.docs_in_place_b = settings.docsInPlace;
|
|
1790
|
+
}
|
|
1234
1791
|
return requestDatalayer({
|
|
1235
1792
|
url: `${configuration.iamUrl}/api/iam/v1/users/${userId}/settings`,
|
|
1236
1793
|
method: 'PUT',
|
|
1237
|
-
body
|
|
1238
|
-
aiAgents_url_s: settings.aiAgentsUrl,
|
|
1239
|
-
can_invite_b: settings.canInvite || false,
|
|
1240
|
-
},
|
|
1794
|
+
body,
|
|
1241
1795
|
});
|
|
1242
1796
|
},
|
|
1243
1797
|
onSuccess: (_, variables) => {
|
|
1244
1798
|
queryClient.invalidateQueries({
|
|
1245
1799
|
queryKey: queryKeys.users.detail(variables.userId),
|
|
1246
1800
|
});
|
|
1801
|
+
// The signed-in user carries these settings, and the application
|
|
1802
|
+
// shell reads them from there.
|
|
1803
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.auth.me() });
|
|
1247
1804
|
},
|
|
1248
1805
|
});
|
|
1249
1806
|
};
|
|
@@ -1682,7 +2239,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
1682
2239
|
};
|
|
1683
2240
|
/**
|
|
1684
2241
|
* Update space with optimistic update.
|
|
1685
|
-
* Any extra fields (e.g.
|
|
2242
|
+
* Any extra fields (e.g. attached_agent_runtime_name_s) are forwarded to the backend.
|
|
1686
2243
|
*/
|
|
1687
2244
|
const useUpdateSpace = () => {
|
|
1688
2245
|
return useMutation({
|
|
@@ -1873,6 +2430,148 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
1873
2430
|
},
|
|
1874
2431
|
});
|
|
1875
2432
|
};
|
|
2433
|
+
/**
|
|
2434
|
+
* Get a deck by id, its specification inlined as `spec`.
|
|
2435
|
+
*/
|
|
2436
|
+
const useDeck = (deckId) => {
|
|
2437
|
+
return useQuery({
|
|
2438
|
+
queryKey: queryKeys.decks.detail(deckId),
|
|
2439
|
+
queryFn: async () => {
|
|
2440
|
+
const resp = await requestDatalayer({
|
|
2441
|
+
url: `${configuration.spacerUrl}/api/spacer/v1/decks/${deckId}`,
|
|
2442
|
+
method: 'GET',
|
|
2443
|
+
});
|
|
2444
|
+
if (resp.success && resp.deck) {
|
|
2445
|
+
return toDeck(resp.deck);
|
|
2446
|
+
}
|
|
2447
|
+
if (resp?.code === 'FORBIDDEN') {
|
|
2448
|
+
throw new Error(resp.message || 'Not authorized to access this deck');
|
|
2449
|
+
}
|
|
2450
|
+
throw new Error(resp.message || 'Failed to fetch deck');
|
|
2451
|
+
},
|
|
2452
|
+
...DEFAULT_QUERY_OPTIONS,
|
|
2453
|
+
enabled: !!deckId,
|
|
2454
|
+
});
|
|
2455
|
+
};
|
|
2456
|
+
/**
|
|
2457
|
+
* Get decks by space
|
|
2458
|
+
*/
|
|
2459
|
+
const useDecksBySpace = (spaceId) => {
|
|
2460
|
+
return useQuery({
|
|
2461
|
+
queryKey: queryKeys.decks.bySpace(spaceId),
|
|
2462
|
+
queryFn: async () => {
|
|
2463
|
+
const resp = await requestDatalayer({
|
|
2464
|
+
url: `${configuration.spacerUrl}/api/spacer/v1/spaces/${spaceId}/items/types/deck`,
|
|
2465
|
+
method: 'GET',
|
|
2466
|
+
});
|
|
2467
|
+
if (resp.success && resp.items) {
|
|
2468
|
+
return resp.items.map((raw) => {
|
|
2469
|
+
const deck = toDeck(raw);
|
|
2470
|
+
seedDeckDetail(deck);
|
|
2471
|
+
return deck;
|
|
2472
|
+
});
|
|
2473
|
+
}
|
|
2474
|
+
return [];
|
|
2475
|
+
},
|
|
2476
|
+
...DEFAULT_QUERY_OPTIONS,
|
|
2477
|
+
enabled: !!spaceId,
|
|
2478
|
+
});
|
|
2479
|
+
};
|
|
2480
|
+
/**
|
|
2481
|
+
* Create a deck — from an uploaded specification, or the starter one.
|
|
2482
|
+
*/
|
|
2483
|
+
const useCreateDeck = () => {
|
|
2484
|
+
return useMutation({
|
|
2485
|
+
mutationFn: async ({ spaceId, name, description, deckType = 'deck', file, }) => {
|
|
2486
|
+
const formData = new FormData();
|
|
2487
|
+
formData.append('space_id', spaceId);
|
|
2488
|
+
// Backward compatibility for endpoints still expecting camelCase.
|
|
2489
|
+
formData.append('spaceId', spaceId);
|
|
2490
|
+
formData.append('deckType', deckType);
|
|
2491
|
+
formData.append('name', name);
|
|
2492
|
+
formData.append('description', description || '');
|
|
2493
|
+
if (file) {
|
|
2494
|
+
formData.append('file', file);
|
|
2495
|
+
}
|
|
2496
|
+
return uploadDeck(formData);
|
|
2497
|
+
},
|
|
2498
|
+
onSuccess: resp => {
|
|
2499
|
+
if (resp.success && resp.deck) {
|
|
2500
|
+
const deck = toDeck(resp.deck);
|
|
2501
|
+
seedDeckDetail(deck);
|
|
2502
|
+
queryClient.refetchQueries({ queryKey: queryKeys.decks.all() });
|
|
2503
|
+
queryClient.refetchQueries({ queryKey: queryKeys.items.all() });
|
|
2504
|
+
}
|
|
2505
|
+
},
|
|
2506
|
+
});
|
|
2507
|
+
};
|
|
2508
|
+
/**
|
|
2509
|
+
* Update a deck's name and description
|
|
2510
|
+
*/
|
|
2511
|
+
const useUpdateDeck = () => {
|
|
2512
|
+
return useMutation({
|
|
2513
|
+
mutationFn: async ({ id, name, description, }) => {
|
|
2514
|
+
return requestDatalayer({
|
|
2515
|
+
url: `${configuration.spacerUrl}/api/spacer/v1/decks/${id}`,
|
|
2516
|
+
method: 'PUT',
|
|
2517
|
+
body: { name, description },
|
|
2518
|
+
});
|
|
2519
|
+
},
|
|
2520
|
+
onSuccess: (resp, deck) => {
|
|
2521
|
+
if (resp.success) {
|
|
2522
|
+
queryClient.invalidateQueries({
|
|
2523
|
+
queryKey: queryKeys.decks.detail(deck.id),
|
|
2524
|
+
});
|
|
2525
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.decks.all() });
|
|
2526
|
+
}
|
|
2527
|
+
},
|
|
2528
|
+
});
|
|
2529
|
+
};
|
|
2530
|
+
/**
|
|
2531
|
+
* Replace a deck's specification, whole.
|
|
2532
|
+
*/
|
|
2533
|
+
const useUpdateDeckModel = () => {
|
|
2534
|
+
return useMutation({
|
|
2535
|
+
mutationFn: async ({ deckId, spec, }) => {
|
|
2536
|
+
return requestDatalayer({
|
|
2537
|
+
url: `${configuration.spacerUrl}/api/spacer/v1/decks/${deckId}/model`,
|
|
2538
|
+
method: 'PUT',
|
|
2539
|
+
body: { spec },
|
|
2540
|
+
});
|
|
2541
|
+
},
|
|
2542
|
+
onSuccess: (_, variables) => {
|
|
2543
|
+
queryClient.invalidateQueries({
|
|
2544
|
+
queryKey: queryKeys.decks.detail(variables.deckId),
|
|
2545
|
+
});
|
|
2546
|
+
queryClient.invalidateQueries({
|
|
2547
|
+
queryKey: queryKeys.decks.model(variables.deckId),
|
|
2548
|
+
});
|
|
2549
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.decks.all() });
|
|
2550
|
+
},
|
|
2551
|
+
});
|
|
2552
|
+
};
|
|
2553
|
+
/**
|
|
2554
|
+
* Clone a deck into the caller's library (or a space they may change).
|
|
2555
|
+
* The spacer writes the specification again under the new uid.
|
|
2556
|
+
*/
|
|
2557
|
+
const useCloneDeck = () => {
|
|
2558
|
+
return useMutation({
|
|
2559
|
+
mutationFn: async (deckId) => {
|
|
2560
|
+
return requestDatalayer({
|
|
2561
|
+
url: `${configuration.spacerUrl}/api/spacer/v1/decks/${deckId}/clone`,
|
|
2562
|
+
method: 'POST',
|
|
2563
|
+
});
|
|
2564
|
+
},
|
|
2565
|
+
onSuccess: resp => {
|
|
2566
|
+
if (resp.success && resp.deck) {
|
|
2567
|
+
const deck = toDeck(resp.deck);
|
|
2568
|
+
seedDeckDetail(deck);
|
|
2569
|
+
queryClient.refetchQueries({ queryKey: queryKeys.decks.all() });
|
|
2570
|
+
queryClient.refetchQueries({ queryKey: queryKeys.items.all() });
|
|
2571
|
+
}
|
|
2572
|
+
},
|
|
2573
|
+
});
|
|
2574
|
+
};
|
|
1876
2575
|
/**
|
|
1877
2576
|
* Clone notebook
|
|
1878
2577
|
*/
|
|
@@ -2068,242 +2767,41 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
2068
2767
|
},
|
|
2069
2768
|
});
|
|
2070
2769
|
};
|
|
2071
|
-
// ============================================================================
|
|
2072
|
-
// Page Hooks
|
|
2073
|
-
// ============================================================================
|
|
2074
2770
|
/**
|
|
2075
|
-
* Get
|
|
2771
|
+
* Get all secrets
|
|
2076
2772
|
*/
|
|
2077
|
-
|
|
2773
|
+
// TODO: Previously this hook pre-populated individual secret caches with setQueryData,
|
|
2774
|
+
// but this prevented useSecret from fetching fresh data (e.g., the value field).
|
|
2775
|
+
// Consider re-adding cache pre-population if the list endpoint returns full secret data,
|
|
2776
|
+
// or use a different query key pattern for partial vs full secret data.
|
|
2777
|
+
const useSecrets = (options) => {
|
|
2778
|
+
const principalUid = options?.principalUid;
|
|
2779
|
+
const principalKind = options?.principalKind;
|
|
2078
2780
|
return useQuery({
|
|
2079
|
-
queryKey:
|
|
2781
|
+
queryKey: [
|
|
2782
|
+
...queryKeys.secrets.all(),
|
|
2783
|
+
principalUid || 'self',
|
|
2784
|
+
principalKind || '',
|
|
2785
|
+
],
|
|
2080
2786
|
queryFn: async () => {
|
|
2081
2787
|
const resp = await requestDatalayer({
|
|
2082
|
-
url: `${configuration.
|
|
2788
|
+
url: withAccountUidQuery(`${configuration.iamUrl}/api/iam/v1/secrets`, principalUid, principalKind),
|
|
2083
2789
|
method: 'GET',
|
|
2084
2790
|
});
|
|
2085
|
-
if (resp.success && resp.
|
|
2086
|
-
|
|
2791
|
+
if (resp.success && resp.secrets) {
|
|
2792
|
+
const secrets = resp.secrets
|
|
2793
|
+
.map((s) => toSecret(s))
|
|
2794
|
+
.filter(Boolean);
|
|
2795
|
+
return secrets;
|
|
2087
2796
|
}
|
|
2088
|
-
|
|
2797
|
+
return [];
|
|
2089
2798
|
},
|
|
2090
2799
|
...DEFAULT_QUERY_OPTIONS,
|
|
2091
|
-
|
|
2800
|
+
refetchOnMount: true, // Override to refetch when stale after invalidation
|
|
2092
2801
|
});
|
|
2093
2802
|
};
|
|
2094
2803
|
/**
|
|
2095
|
-
*
|
|
2096
|
-
*/
|
|
2097
|
-
const usePages = (scope) => {
|
|
2098
|
-
return useQuery({
|
|
2099
|
-
queryKey: [
|
|
2100
|
-
...queryKeys.pages.all(),
|
|
2101
|
-
scope?.selectedPrincipalUid || '',
|
|
2102
|
-
scope?.selectedPrincipalKind || '',
|
|
2103
|
-
],
|
|
2104
|
-
queryFn: async () => {
|
|
2105
|
-
const params = new URLSearchParams();
|
|
2106
|
-
if (scope?.selectedPrincipalUid) {
|
|
2107
|
-
params.set('selected_principal_uid', scope.selectedPrincipalUid);
|
|
2108
|
-
}
|
|
2109
|
-
if (scope?.selectedPrincipalKind) {
|
|
2110
|
-
params.set('selected_principal_kind', scope.selectedPrincipalKind);
|
|
2111
|
-
}
|
|
2112
|
-
const query = params.toString();
|
|
2113
|
-
const resp = await requestDatalayer({
|
|
2114
|
-
url: `${configuration.libraryUrl}/api/library/v1/pages${query ? `?${query}` : ''}`,
|
|
2115
|
-
method: 'GET',
|
|
2116
|
-
});
|
|
2117
|
-
if (resp.success && resp.pages) {
|
|
2118
|
-
const pages = resp.pages
|
|
2119
|
-
.map((p) => {
|
|
2120
|
-
const page = toPage(p);
|
|
2121
|
-
if (page) {
|
|
2122
|
-
queryClient.setQueryData(queryKeys.pages.detail(page.id), page);
|
|
2123
|
-
}
|
|
2124
|
-
return page;
|
|
2125
|
-
})
|
|
2126
|
-
.filter(Boolean);
|
|
2127
|
-
return pages;
|
|
2128
|
-
}
|
|
2129
|
-
return [];
|
|
2130
|
-
},
|
|
2131
|
-
...DEFAULT_QUERY_OPTIONS,
|
|
2132
|
-
});
|
|
2133
|
-
};
|
|
2134
|
-
/**
|
|
2135
|
-
* Create page
|
|
2136
|
-
*/
|
|
2137
|
-
const useCreatePage = () => {
|
|
2138
|
-
return useMutation({
|
|
2139
|
-
mutationFn: async (page) => {
|
|
2140
|
-
return requestDatalayer({
|
|
2141
|
-
url: `${configuration.libraryUrl}/api/library/v1/pages`,
|
|
2142
|
-
method: 'POST',
|
|
2143
|
-
body: { ...page },
|
|
2144
|
-
});
|
|
2145
|
-
},
|
|
2146
|
-
onSuccess: resp => {
|
|
2147
|
-
if (resp.success && resp.page) {
|
|
2148
|
-
const page = toPage(resp.page);
|
|
2149
|
-
if (page) {
|
|
2150
|
-
// Set detail cache
|
|
2151
|
-
queryClient.setQueryData(queryKeys.pages.detail(page.id), page);
|
|
2152
|
-
// Invalidate list to refetch
|
|
2153
|
-
queryClient.invalidateQueries({
|
|
2154
|
-
queryKey: queryKeys.pages.all(),
|
|
2155
|
-
});
|
|
2156
|
-
}
|
|
2157
|
-
}
|
|
2158
|
-
},
|
|
2159
|
-
});
|
|
2160
|
-
};
|
|
2161
|
-
/**
|
|
2162
|
-
* Update page
|
|
2163
|
-
*/
|
|
2164
|
-
const useUpdatePage = () => {
|
|
2165
|
-
return useMutation({
|
|
2166
|
-
mutationFn: async (page) => {
|
|
2167
|
-
return requestDatalayer({
|
|
2168
|
-
url: `${configuration.libraryUrl}/api/library/v1/pages/${page.id}`,
|
|
2169
|
-
method: 'PUT',
|
|
2170
|
-
body: {
|
|
2171
|
-
name: page.name,
|
|
2172
|
-
description: page.description,
|
|
2173
|
-
tags: page.tags,
|
|
2174
|
-
},
|
|
2175
|
-
});
|
|
2176
|
-
},
|
|
2177
|
-
onSuccess: (resp, page) => {
|
|
2178
|
-
if (resp.success) {
|
|
2179
|
-
// Invalidate detail and list queries
|
|
2180
|
-
queryClient.invalidateQueries({
|
|
2181
|
-
queryKey: queryKeys.pages.detail(page.id),
|
|
2182
|
-
});
|
|
2183
|
-
queryClient.invalidateQueries({
|
|
2184
|
-
queryKey: queryKeys.pages.all(),
|
|
2185
|
-
});
|
|
2186
|
-
}
|
|
2187
|
-
},
|
|
2188
|
-
});
|
|
2189
|
-
};
|
|
2190
|
-
/**
|
|
2191
|
-
* Delete page
|
|
2192
|
-
*/
|
|
2193
|
-
const useDeletePage = () => {
|
|
2194
|
-
return useMutation({
|
|
2195
|
-
mutationFn: async (pageId) => {
|
|
2196
|
-
return requestDatalayer({
|
|
2197
|
-
url: `${configuration.libraryUrl}/api/library/v1/pages/${pageId}`,
|
|
2198
|
-
method: 'DELETE',
|
|
2199
|
-
});
|
|
2200
|
-
},
|
|
2201
|
-
onSuccess: (_, pageId) => {
|
|
2202
|
-
// Remove from detail cache
|
|
2203
|
-
queryClient.removeQueries({ queryKey: queryKeys.pages.detail(pageId) });
|
|
2204
|
-
// Invalidate list to refetch
|
|
2205
|
-
queryClient.invalidateQueries({
|
|
2206
|
-
queryKey: queryKeys.pages.all(),
|
|
2207
|
-
});
|
|
2208
|
-
},
|
|
2209
|
-
});
|
|
2210
|
-
};
|
|
2211
|
-
/**
|
|
2212
|
-
* Get all datasources
|
|
2213
|
-
*/
|
|
2214
|
-
const useDatasources = (options) => {
|
|
2215
|
-
const principalUid = options?.principalUid;
|
|
2216
|
-
const principalKind = options?.principalKind;
|
|
2217
|
-
return useQuery({
|
|
2218
|
-
queryKey: [
|
|
2219
|
-
...queryKeys.datasources.all(),
|
|
2220
|
-
principalUid || 'self',
|
|
2221
|
-
principalKind || '',
|
|
2222
|
-
],
|
|
2223
|
-
queryFn: async () => {
|
|
2224
|
-
const resp = await requestDatalayer({
|
|
2225
|
-
url: withAccountUidQuery(`${configuration.iamUrl}/api/iam/v1/datasources`, principalUid, principalKind),
|
|
2226
|
-
method: 'GET',
|
|
2227
|
-
});
|
|
2228
|
-
if (resp.success && resp.datasources) {
|
|
2229
|
-
const datasources = resp.datasources
|
|
2230
|
-
.map((d) => {
|
|
2231
|
-
const datasource = toDatasource(d);
|
|
2232
|
-
if (datasource) {
|
|
2233
|
-
queryClient.setQueryData(queryKeys.datasources.detail(datasource.id), datasource);
|
|
2234
|
-
}
|
|
2235
|
-
return datasource;
|
|
2236
|
-
})
|
|
2237
|
-
.filter(Boolean);
|
|
2238
|
-
return datasources;
|
|
2239
|
-
}
|
|
2240
|
-
return [];
|
|
2241
|
-
},
|
|
2242
|
-
...DEFAULT_QUERY_OPTIONS,
|
|
2243
|
-
});
|
|
2244
|
-
};
|
|
2245
|
-
/**
|
|
2246
|
-
* Create datasource
|
|
2247
|
-
*/
|
|
2248
|
-
const useCreateDatasource = (options) => {
|
|
2249
|
-
const principalUid = options?.principalUid;
|
|
2250
|
-
const principalKind = options?.principalKind;
|
|
2251
|
-
return useMutation({
|
|
2252
|
-
mutationFn: async (datasource) => {
|
|
2253
|
-
return requestDatalayer({
|
|
2254
|
-
url: withAccountUidQuery(`${configuration.iamUrl}/api/iam/v1/datasources`, principalUid, principalKind),
|
|
2255
|
-
method: 'POST',
|
|
2256
|
-
body: { ...datasource },
|
|
2257
|
-
});
|
|
2258
|
-
},
|
|
2259
|
-
onSuccess: resp => {
|
|
2260
|
-
if (resp.success && resp.datasource) {
|
|
2261
|
-
const ds = toDatasource(resp.datasource);
|
|
2262
|
-
if (ds) {
|
|
2263
|
-
queryClient.setQueryData(queryKeys.datasources.detail(ds.id), ds);
|
|
2264
|
-
}
|
|
2265
|
-
}
|
|
2266
|
-
queryClient.invalidateQueries({
|
|
2267
|
-
queryKey: queryKeys.datasources.all(),
|
|
2268
|
-
});
|
|
2269
|
-
},
|
|
2270
|
-
});
|
|
2271
|
-
};
|
|
2272
|
-
/**
|
|
2273
|
-
* Get all secrets
|
|
2274
|
-
*/
|
|
2275
|
-
// TODO: Previously this hook pre-populated individual secret caches with setQueryData,
|
|
2276
|
-
// but this prevented useSecret from fetching fresh data (e.g., the value field).
|
|
2277
|
-
// Consider re-adding cache pre-population if the list endpoint returns full secret data,
|
|
2278
|
-
// or use a different query key pattern for partial vs full secret data.
|
|
2279
|
-
const useSecrets = (options) => {
|
|
2280
|
-
const principalUid = options?.principalUid;
|
|
2281
|
-
const principalKind = options?.principalKind;
|
|
2282
|
-
return useQuery({
|
|
2283
|
-
queryKey: [
|
|
2284
|
-
...queryKeys.secrets.all(),
|
|
2285
|
-
principalUid || 'self',
|
|
2286
|
-
principalKind || '',
|
|
2287
|
-
],
|
|
2288
|
-
queryFn: async () => {
|
|
2289
|
-
const resp = await requestDatalayer({
|
|
2290
|
-
url: withAccountUidQuery(`${configuration.iamUrl}/api/iam/v1/secrets`, principalUid, principalKind),
|
|
2291
|
-
method: 'GET',
|
|
2292
|
-
});
|
|
2293
|
-
if (resp.success && resp.secrets) {
|
|
2294
|
-
const secrets = resp.secrets
|
|
2295
|
-
.map((s) => toSecret(s))
|
|
2296
|
-
.filter(Boolean);
|
|
2297
|
-
return secrets;
|
|
2298
|
-
}
|
|
2299
|
-
return [];
|
|
2300
|
-
},
|
|
2301
|
-
...DEFAULT_QUERY_OPTIONS,
|
|
2302
|
-
refetchOnMount: true, // Override to refetch when stale after invalidation
|
|
2303
|
-
});
|
|
2304
|
-
};
|
|
2305
|
-
/**
|
|
2306
|
-
* Create secret
|
|
2804
|
+
* Create secret
|
|
2307
2805
|
*/
|
|
2308
2806
|
const useCreateSecret = (options) => {
|
|
2309
2807
|
const principalUid = options?.principalUid;
|
|
@@ -2561,10 +3059,8 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
2561
3059
|
queryClient.removeQueries({
|
|
2562
3060
|
queryKey: queryKeys.documents.detail(itemId),
|
|
2563
3061
|
});
|
|
3062
|
+
queryClient.removeQueries({ queryKey: queryKeys.decks.detail(itemId) });
|
|
2564
3063
|
queryClient.removeQueries({ queryKey: queryKeys.cells.detail(itemId) });
|
|
2565
|
-
queryClient.removeQueries({
|
|
2566
|
-
queryKey: queryKeys.datasets.detail(itemId),
|
|
2567
|
-
});
|
|
2568
3064
|
queryClient.removeQueries({
|
|
2569
3065
|
queryKey: queryKeys.lessons.detail(itemId),
|
|
2570
3066
|
});
|
|
@@ -2577,8 +3073,8 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
2577
3073
|
// Invalidate all artifact type queries
|
|
2578
3074
|
queryClient.invalidateQueries({ queryKey: queryKeys.notebooks.all() });
|
|
2579
3075
|
queryClient.invalidateQueries({ queryKey: queryKeys.documents.all() });
|
|
3076
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.decks.all() });
|
|
2580
3077
|
queryClient.invalidateQueries({ queryKey: queryKeys.cells.all() });
|
|
2581
|
-
queryClient.invalidateQueries({ queryKey: queryKeys.datasets.all() });
|
|
2582
3078
|
queryClient.invalidateQueries({ queryKey: queryKeys.lessons.all() });
|
|
2583
3079
|
queryClient.invalidateQueries({ queryKey: queryKeys.exercises.all() });
|
|
2584
3080
|
queryClient.invalidateQueries({
|
|
@@ -2592,59 +3088,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
2592
3088
|
// ============================================================================
|
|
2593
3089
|
// Core CRUD Operations - Refresh & Get Methods
|
|
2594
3090
|
// ============================================================================
|
|
2595
|
-
/**
|
|
2596
|
-
* Get single datasource by ID
|
|
2597
|
-
*/
|
|
2598
|
-
const useDatasource = (datasourceId, options) => {
|
|
2599
|
-
const principalUid = options?.principalUid;
|
|
2600
|
-
const principalKind = options?.principalKind;
|
|
2601
|
-
return useQuery({
|
|
2602
|
-
queryKey: [
|
|
2603
|
-
...queryKeys.datasources.detail(datasourceId),
|
|
2604
|
-
principalUid || 'self',
|
|
2605
|
-
principalKind || '',
|
|
2606
|
-
],
|
|
2607
|
-
queryFn: async () => {
|
|
2608
|
-
const resp = await requestDatalayer({
|
|
2609
|
-
url: withAccountUidQuery(`${configuration.iamUrl}/api/iam/v1/datasources/${datasourceId}`, principalUid, principalKind),
|
|
2610
|
-
method: 'GET',
|
|
2611
|
-
});
|
|
2612
|
-
if (resp.success && resp.datasource) {
|
|
2613
|
-
return toDatasource(resp.datasource);
|
|
2614
|
-
}
|
|
2615
|
-
return null;
|
|
2616
|
-
},
|
|
2617
|
-
...DEFAULT_QUERY_OPTIONS,
|
|
2618
|
-
enabled: !!datasourceId,
|
|
2619
|
-
});
|
|
2620
|
-
};
|
|
2621
|
-
/**
|
|
2622
|
-
* Update datasource
|
|
2623
|
-
*/
|
|
2624
|
-
const useUpdateDatasource = (options) => {
|
|
2625
|
-
const principalUid = options?.principalUid;
|
|
2626
|
-
const principalKind = options?.principalKind;
|
|
2627
|
-
return useMutation({
|
|
2628
|
-
mutationFn: async (datasource) => {
|
|
2629
|
-
return requestDatalayer({
|
|
2630
|
-
url: withAccountUidQuery(`${configuration.iamUrl}/api/iam/v1/datasources/${datasource.id}`, principalUid, principalKind),
|
|
2631
|
-
method: 'PUT',
|
|
2632
|
-
body: { ...datasource },
|
|
2633
|
-
});
|
|
2634
|
-
},
|
|
2635
|
-
onSuccess: resp => {
|
|
2636
|
-
if (resp.success && resp.datasource) {
|
|
2637
|
-
const ds = toDatasource(resp.datasource);
|
|
2638
|
-
if (ds) {
|
|
2639
|
-
queryClient.setQueryData(queryKeys.datasources.detail(ds.id), ds);
|
|
2640
|
-
}
|
|
2641
|
-
}
|
|
2642
|
-
queryClient.invalidateQueries({
|
|
2643
|
-
queryKey: queryKeys.datasources.all(),
|
|
2644
|
-
});
|
|
2645
|
-
},
|
|
2646
|
-
});
|
|
2647
|
-
};
|
|
2648
3091
|
/**
|
|
2649
3092
|
* Get single secret by ID
|
|
2650
3093
|
*/
|
|
@@ -3578,25 +4021,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
3578
4021
|
},
|
|
3579
4022
|
});
|
|
3580
4023
|
};
|
|
3581
|
-
/**
|
|
3582
|
-
* Get public courses
|
|
3583
|
-
*/
|
|
3584
|
-
const usePublicCourses = () => {
|
|
3585
|
-
return useQuery({
|
|
3586
|
-
queryKey: ['courses', 'public'],
|
|
3587
|
-
queryFn: async () => {
|
|
3588
|
-
const resp = await requestDatalayer({
|
|
3589
|
-
url: `${configuration.libraryUrl}/api/library/v1/courses/public`,
|
|
3590
|
-
method: 'GET',
|
|
3591
|
-
});
|
|
3592
|
-
if (resp.success && resp.courses) {
|
|
3593
|
-
return resp.courses.map((course) => toCourse(course));
|
|
3594
|
-
}
|
|
3595
|
-
return [];
|
|
3596
|
-
},
|
|
3597
|
-
...DEFAULT_QUERY_OPTIONS,
|
|
3598
|
-
});
|
|
3599
|
-
};
|
|
3600
4024
|
/**
|
|
3601
4025
|
* Get instructor courses
|
|
3602
4026
|
*/
|
|
@@ -3974,62 +4398,38 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
3974
4398
|
});
|
|
3975
4399
|
};
|
|
3976
4400
|
/**
|
|
3977
|
-
* Get
|
|
4401
|
+
* Get environment by ID
|
|
3978
4402
|
*/
|
|
3979
|
-
const
|
|
4403
|
+
const useEnvironment = (environmentId) => {
|
|
3980
4404
|
return useQuery({
|
|
3981
|
-
queryKey:
|
|
3982
|
-
...queryKeys.datasets.detail(datasetId),
|
|
3983
|
-
scope?.selectedPrincipalUid || '',
|
|
3984
|
-
scope?.selectedPrincipalKind || '',
|
|
3985
|
-
],
|
|
4405
|
+
queryKey: queryKeys.environments.detail(environmentId),
|
|
3986
4406
|
queryFn: async () => {
|
|
3987
|
-
const params = new URLSearchParams();
|
|
3988
|
-
if (scope?.selectedPrincipalUid) {
|
|
3989
|
-
params.set('selected_principal_uid', scope.selectedPrincipalUid);
|
|
3990
|
-
}
|
|
3991
|
-
if (scope?.selectedPrincipalKind) {
|
|
3992
|
-
params.set('selected_principal_kind', scope.selectedPrincipalKind);
|
|
3993
|
-
}
|
|
3994
|
-
const query = params.toString();
|
|
3995
4407
|
const resp = await requestDatalayer({
|
|
3996
|
-
url: `${configuration.
|
|
4408
|
+
url: `${configuration.spacerUrl}/api/spacer/v1/spaces/items/${environmentId}`,
|
|
3997
4409
|
method: 'GET',
|
|
3998
4410
|
});
|
|
3999
4411
|
if (resp.success && resp.item) {
|
|
4000
|
-
return
|
|
4412
|
+
return toEnvironment(resp.item);
|
|
4001
4413
|
}
|
|
4002
4414
|
return undefined;
|
|
4003
4415
|
},
|
|
4004
|
-
enabled: !!
|
|
4416
|
+
enabled: !!environmentId,
|
|
4005
4417
|
...DEFAULT_QUERY_OPTIONS,
|
|
4006
4418
|
});
|
|
4007
4419
|
};
|
|
4008
4420
|
/**
|
|
4009
|
-
* Get
|
|
4421
|
+
* Get environments by space
|
|
4010
4422
|
*/
|
|
4011
|
-
const
|
|
4423
|
+
const useEnvironmentsBySpace = (spaceId) => {
|
|
4012
4424
|
return useQuery({
|
|
4013
|
-
queryKey:
|
|
4014
|
-
...queryKeys.datasets.bySpace(spaceId),
|
|
4015
|
-
scope?.selectedPrincipalUid || '',
|
|
4016
|
-
scope?.selectedPrincipalKind || '',
|
|
4017
|
-
],
|
|
4425
|
+
queryKey: queryKeys.environments.bySpace(spaceId),
|
|
4018
4426
|
queryFn: async () => {
|
|
4019
|
-
const params = new URLSearchParams();
|
|
4020
|
-
if (scope?.selectedPrincipalUid) {
|
|
4021
|
-
params.set('selected_principal_uid', scope.selectedPrincipalUid);
|
|
4022
|
-
}
|
|
4023
|
-
if (scope?.selectedPrincipalKind) {
|
|
4024
|
-
params.set('selected_principal_kind', scope.selectedPrincipalKind);
|
|
4025
|
-
}
|
|
4026
|
-
const query = params.toString();
|
|
4027
4427
|
const resp = await requestDatalayer({
|
|
4028
|
-
url: `${configuration.
|
|
4428
|
+
url: `${configuration.spacerUrl}/api/spacer/v1/spaces/${spaceId}/items/types/environment`,
|
|
4029
4429
|
method: 'GET',
|
|
4030
4430
|
});
|
|
4031
4431
|
if (resp.success && resp.items) {
|
|
4032
|
-
return resp.items.map((item) =>
|
|
4432
|
+
return resp.items.map((item) => toEnvironment(item));
|
|
4033
4433
|
}
|
|
4034
4434
|
return [];
|
|
4035
4435
|
},
|
|
@@ -4038,95 +4438,20 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
4038
4438
|
});
|
|
4039
4439
|
};
|
|
4040
4440
|
/**
|
|
4041
|
-
*
|
|
4441
|
+
* Get lesson by ID
|
|
4042
4442
|
*/
|
|
4043
|
-
const
|
|
4044
|
-
return
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
if (selectedPrincipalKind) {
|
|
4051
|
-
params.set('selected_principal_kind', selectedPrincipalKind);
|
|
4052
|
-
}
|
|
4053
|
-
const query = params.toString();
|
|
4054
|
-
return requestDatalayer({
|
|
4055
|
-
url: `${configuration.runtimesUrl}/api/runtimes/v1/datasets/${id}${query ? `?${query}` : ''}`,
|
|
4056
|
-
method: 'PUT',
|
|
4057
|
-
body: { name, description },
|
|
4443
|
+
const useLesson = (lessonId) => {
|
|
4444
|
+
return useQuery({
|
|
4445
|
+
queryKey: queryKeys.lessons.detail(lessonId),
|
|
4446
|
+
queryFn: async () => {
|
|
4447
|
+
const resp = await requestDatalayer({
|
|
4448
|
+
url: `${configuration.spacerUrl}/api/spacer/v1/lessons/${lessonId}`,
|
|
4449
|
+
method: 'GET',
|
|
4058
4450
|
});
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
if (resp.success) {
|
|
4062
|
-
// Invalidate detail and list queries
|
|
4063
|
-
queryClient.invalidateQueries({
|
|
4064
|
-
queryKey: queryKeys.datasets.detail(id),
|
|
4065
|
-
});
|
|
4066
|
-
if (spaceId) {
|
|
4067
|
-
queryClient.invalidateQueries({
|
|
4068
|
-
queryKey: queryKeys.datasets.bySpace(spaceId),
|
|
4069
|
-
});
|
|
4070
|
-
}
|
|
4451
|
+
if (resp.success && resp.lesson) {
|
|
4452
|
+
return toLesson(resp.lesson);
|
|
4071
4453
|
}
|
|
4072
|
-
|
|
4073
|
-
});
|
|
4074
|
-
};
|
|
4075
|
-
/**
|
|
4076
|
-
* Get environment by ID
|
|
4077
|
-
*/
|
|
4078
|
-
const useEnvironment = (environmentId) => {
|
|
4079
|
-
return useQuery({
|
|
4080
|
-
queryKey: queryKeys.environments.detail(environmentId),
|
|
4081
|
-
queryFn: async () => {
|
|
4082
|
-
const resp = await requestDatalayer({
|
|
4083
|
-
url: `${configuration.spacerUrl}/api/spacer/v1/spaces/items/${environmentId}`,
|
|
4084
|
-
method: 'GET',
|
|
4085
|
-
});
|
|
4086
|
-
if (resp.success && resp.item) {
|
|
4087
|
-
return toEnvironment(resp.item);
|
|
4088
|
-
}
|
|
4089
|
-
return undefined;
|
|
4090
|
-
},
|
|
4091
|
-
enabled: !!environmentId,
|
|
4092
|
-
...DEFAULT_QUERY_OPTIONS,
|
|
4093
|
-
});
|
|
4094
|
-
};
|
|
4095
|
-
/**
|
|
4096
|
-
* Get environments by space
|
|
4097
|
-
*/
|
|
4098
|
-
const useEnvironmentsBySpace = (spaceId) => {
|
|
4099
|
-
return useQuery({
|
|
4100
|
-
queryKey: queryKeys.environments.bySpace(spaceId),
|
|
4101
|
-
queryFn: async () => {
|
|
4102
|
-
const resp = await requestDatalayer({
|
|
4103
|
-
url: `${configuration.spacerUrl}/api/spacer/v1/spaces/${spaceId}/items/types/environment`,
|
|
4104
|
-
method: 'GET',
|
|
4105
|
-
});
|
|
4106
|
-
if (resp.success && resp.items) {
|
|
4107
|
-
return resp.items.map((item) => toEnvironment(item));
|
|
4108
|
-
}
|
|
4109
|
-
return [];
|
|
4110
|
-
},
|
|
4111
|
-
enabled: !!spaceId,
|
|
4112
|
-
...DEFAULT_QUERY_OPTIONS,
|
|
4113
|
-
});
|
|
4114
|
-
};
|
|
4115
|
-
/**
|
|
4116
|
-
* Get lesson by ID
|
|
4117
|
-
*/
|
|
4118
|
-
const useLesson = (lessonId) => {
|
|
4119
|
-
return useQuery({
|
|
4120
|
-
queryKey: queryKeys.lessons.detail(lessonId),
|
|
4121
|
-
queryFn: async () => {
|
|
4122
|
-
const resp = await requestDatalayer({
|
|
4123
|
-
url: `${configuration.spacerUrl}/api/spacer/v1/lessons/${lessonId}`,
|
|
4124
|
-
method: 'GET',
|
|
4125
|
-
});
|
|
4126
|
-
if (resp.success && resp.lesson) {
|
|
4127
|
-
return toLesson(resp.lesson);
|
|
4128
|
-
}
|
|
4129
|
-
return undefined;
|
|
4454
|
+
return undefined;
|
|
4130
4455
|
},
|
|
4131
4456
|
enabled: !!lessonId,
|
|
4132
4457
|
...DEFAULT_QUERY_OPTIONS,
|
|
@@ -4730,6 +5055,31 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
4730
5055
|
/**
|
|
4731
5056
|
* Launch bulk emails for outbound campaign (production mode)
|
|
4732
5057
|
*/
|
|
5058
|
+
/**
|
|
5059
|
+
* Mark an outbound as launched without sending — for launches that
|
|
5060
|
+
* happened outside the browser (curl, the CLI).
|
|
5061
|
+
*/
|
|
5062
|
+
const useMarkOutboundLaunched = () => {
|
|
5063
|
+
const queryClient = useQueryClient();
|
|
5064
|
+
return useMutation({
|
|
5065
|
+
mutationFn: async (outboundId) => {
|
|
5066
|
+
return requestDatalayer({
|
|
5067
|
+
url: `${configuration.growthUrl}/api/growth/v1/outbounds/${outboundId}/mark-launched`,
|
|
5068
|
+
method: 'POST',
|
|
5069
|
+
body: {},
|
|
5070
|
+
});
|
|
5071
|
+
},
|
|
5072
|
+
onSuccess: (_, outboundId) => {
|
|
5073
|
+
queryClient.removeQueries({
|
|
5074
|
+
queryKey: ['outbounds', outboundId],
|
|
5075
|
+
});
|
|
5076
|
+
queryClient.invalidateQueries({
|
|
5077
|
+
queryKey: ['outbounds'],
|
|
5078
|
+
refetchType: 'inactive',
|
|
5079
|
+
});
|
|
5080
|
+
},
|
|
5081
|
+
});
|
|
5082
|
+
};
|
|
4733
5083
|
const useLaunchBulkEmailsOutbounds = () => {
|
|
4734
5084
|
const queryClient = useQueryClient();
|
|
4735
5085
|
return useMutation({
|
|
@@ -5957,35 +6307,721 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
5957
6307
|
/**
|
|
5958
6308
|
* Search public items (notebooks, documents, etc.)
|
|
5959
6309
|
*/
|
|
6310
|
+
/**
|
|
6311
|
+
* Every artifact type a library card is drawn for. One list, so a caller
|
|
6312
|
+
* that wants "everything" cannot accidentally ask for the six types someone
|
|
6313
|
+
* wrote out by hand two releases ago.
|
|
6314
|
+
*
|
|
6315
|
+
* `deck` is here because the administration page's "not in the ribbon" list
|
|
6316
|
+
* asks for this default: without it a published deck was in the library and
|
|
6317
|
+
* could be found by searching, but was never offered to be featured.
|
|
6318
|
+
*/
|
|
6319
|
+
const LIBRARY_ARTIFACT_TYPES = [
|
|
6320
|
+
'notebook',
|
|
6321
|
+
'document',
|
|
6322
|
+
'deck',
|
|
6323
|
+
'cell',
|
|
6324
|
+
'lesson',
|
|
6325
|
+
'exercise',
|
|
6326
|
+
'assignment',
|
|
6327
|
+
'course',
|
|
6328
|
+
'evalset',
|
|
6329
|
+
'dataset',
|
|
6330
|
+
'agent',
|
|
6331
|
+
];
|
|
6332
|
+
const librarySearchQueryString = ({ q, types, max = 20, offset = 0, sort = 'relevance', featured = false, owner, }) => {
|
|
6333
|
+
const normalizedTypes = Array.from(new Set((types || [])
|
|
6334
|
+
.map(type => String(type || '')
|
|
6335
|
+
.trim()
|
|
6336
|
+
.toLowerCase())
|
|
6337
|
+
.filter(Boolean)
|
|
6338
|
+
.map(type => (type === 'eval' ? 'evalset' : type))));
|
|
6339
|
+
const params = {
|
|
6340
|
+
q: q || '*',
|
|
6341
|
+
types: (normalizedTypes.length > 0
|
|
6342
|
+
? normalizedTypes
|
|
6343
|
+
: LIBRARY_ARTIFACT_TYPES).join(' '),
|
|
6344
|
+
max: String(max),
|
|
6345
|
+
offset: String(offset),
|
|
6346
|
+
sort,
|
|
6347
|
+
};
|
|
6348
|
+
if (featured) {
|
|
6349
|
+
params.featured = 'true';
|
|
6350
|
+
}
|
|
6351
|
+
if (owner) {
|
|
6352
|
+
params.owner = owner;
|
|
6353
|
+
}
|
|
6354
|
+
return Object.entries(params)
|
|
6355
|
+
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
|
6356
|
+
.join('&');
|
|
6357
|
+
};
|
|
6358
|
+
const searchLibrary = async (args) => {
|
|
6359
|
+
const resp = await requestDatalayer({
|
|
6360
|
+
url: `${configuration.libraryUrl}/api/library/v1/search?${librarySearchQueryString(args)}`,
|
|
6361
|
+
method: 'GET',
|
|
6362
|
+
});
|
|
6363
|
+
if (!resp.success) {
|
|
6364
|
+
throw new Error(resp.message || 'Failed to search the library');
|
|
6365
|
+
}
|
|
6366
|
+
return {
|
|
6367
|
+
items: (resp.items || [])
|
|
6368
|
+
.map((item) => toItem(item))
|
|
6369
|
+
.filter(Boolean),
|
|
6370
|
+
total: Number(resp.total ?? 0),
|
|
6371
|
+
offset: Number(resp.offset ?? args.offset ?? 0),
|
|
6372
|
+
};
|
|
6373
|
+
};
|
|
6374
|
+
/**
|
|
6375
|
+
* Search the library: one request, whatever mix of artifact types is asked
|
|
6376
|
+
* for, with the total so a caller can page without asking twice.
|
|
6377
|
+
*
|
|
6378
|
+
* Answers `{ items, total, offset }` rather than a bare array — a result
|
|
6379
|
+
* page without its total cannot be paged, and every caller was inventing
|
|
6380
|
+
* "there is probably more" from the length it got.
|
|
6381
|
+
*/
|
|
5960
6382
|
const useSearchPublicItems = () => {
|
|
5961
6383
|
return useMutation({
|
|
5962
|
-
mutationFn: async (
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
6384
|
+
mutationFn: async (args) => searchLibrary(args),
|
|
6385
|
+
});
|
|
6386
|
+
};
|
|
6387
|
+
/**
|
|
6388
|
+
* Everything the library holds, as a query rather than a mutation.
|
|
6389
|
+
*
|
|
6390
|
+
* For the administration page, which has to show what *could* be featured
|
|
6391
|
+
* as well as what is. Publishing the agent catalogue puts artifacts in the
|
|
6392
|
+
* library; without this the page reported "6 agents published" and then
|
|
6393
|
+
* listed none of them, because the only list it had was the featured one.
|
|
6394
|
+
*/
|
|
6395
|
+
const useLibraryArtifacts = (max = 100, options) => {
|
|
6396
|
+
return useQuery({
|
|
6397
|
+
queryKey: [...queryKeys.items.all(), 'library-artifacts', max],
|
|
6398
|
+
queryFn: async () => searchLibrary({ max, sort: 'recent' }),
|
|
6399
|
+
...options,
|
|
6400
|
+
});
|
|
6401
|
+
};
|
|
6402
|
+
/**
|
|
6403
|
+
* The featured artifacts, for the home page's ribbon.
|
|
6404
|
+
*
|
|
6405
|
+
* Anonymous-safe: the library answers this one without a token, and the
|
|
6406
|
+
* service holds it for a minute, so a visit costs the platform nothing.
|
|
6407
|
+
*/
|
|
6408
|
+
const useFeaturedItems = (max = 12, options) => {
|
|
6409
|
+
return useQuery({
|
|
6410
|
+
queryKey: [...queryKeys.items.featured(), max],
|
|
6411
|
+
queryFn: async () => {
|
|
5978
6412
|
const resp = await requestDatalayer({
|
|
5979
|
-
url: `${configuration.libraryUrl}/api/library/v1/
|
|
6413
|
+
url: `${configuration.libraryUrl}/api/library/v1/featured?max=${max}`,
|
|
5980
6414
|
method: 'GET',
|
|
5981
6415
|
});
|
|
5982
6416
|
if (!resp.success) {
|
|
5983
|
-
throw new Error(resp.message || 'Failed to
|
|
6417
|
+
throw new Error(resp.message || 'Failed to get the featured artifacts');
|
|
5984
6418
|
}
|
|
5985
6419
|
return (resp.items || [])
|
|
5986
6420
|
.map((item) => toItem(item))
|
|
5987
6421
|
.filter(Boolean);
|
|
5988
6422
|
},
|
|
6423
|
+
staleTime: 60_000,
|
|
6424
|
+
...options,
|
|
6425
|
+
});
|
|
6426
|
+
};
|
|
6427
|
+
/**
|
|
6428
|
+
* Everything an administrator has actually featured, and nothing else.
|
|
6429
|
+
*
|
|
6430
|
+
* Not `useFeaturedItems`. That one answers for the home page's ribbon: a
|
|
6431
|
+
* public request, capped at what a row can show, ordered for reading. This
|
|
6432
|
+
* one is what the administration page edits — everything featured, in rank
|
|
6433
|
+
* order, however long the list is and whoever it belongs to — so that the
|
|
6434
|
+
* page showing the ribbon's contents is showing the ribbon's contents and
|
|
6435
|
+
* not a page of it. `platform_admin` only.
|
|
6436
|
+
*/
|
|
6437
|
+
const useAllFeaturedItems = (options) => {
|
|
6438
|
+
return useQuery({
|
|
6439
|
+
queryKey: [...queryKeys.items.featured(), 'all'],
|
|
6440
|
+
queryFn: async () => {
|
|
6441
|
+
const resp = await requestDatalayer({
|
|
6442
|
+
url: `${configuration.libraryUrl}/api/library/v1/featured/all`,
|
|
6443
|
+
method: 'GET',
|
|
6444
|
+
});
|
|
6445
|
+
if (!resp.success) {
|
|
6446
|
+
throw new Error(resp.message || 'Failed to get the featured artifacts');
|
|
6447
|
+
}
|
|
6448
|
+
return (resp.items || [])
|
|
6449
|
+
.map((item) => toItem(item))
|
|
6450
|
+
.filter(Boolean);
|
|
6451
|
+
},
|
|
6452
|
+
...options,
|
|
6453
|
+
});
|
|
6454
|
+
};
|
|
6455
|
+
/**
|
|
6456
|
+
* One public artifact by uid, with its content and its library metadata.
|
|
6457
|
+
*
|
|
6458
|
+
* The public pages used to `fetch` this endpoint themselves, which meant no
|
|
6459
|
+
* token was ever sent and the page could not know whether the visitor
|
|
6460
|
+
* orbits what they are reading.
|
|
6461
|
+
*/
|
|
6462
|
+
const usePublicItem = (itemId, options) => {
|
|
6463
|
+
return useQuery({
|
|
6464
|
+
queryKey: queryKeys.items.publicItem(itemId || ''),
|
|
6465
|
+
queryFn: async () => {
|
|
6466
|
+
const resp = await requestDatalayer({
|
|
6467
|
+
url: `${configuration.libraryUrl}/api/library/v1/public/items/${encodeURIComponent(itemId)}`,
|
|
6468
|
+
method: 'GET',
|
|
6469
|
+
});
|
|
6470
|
+
if (!resp.success || !resp.item) {
|
|
6471
|
+
throw new Error(resp.message || 'Failed to get the public artifact');
|
|
6472
|
+
}
|
|
6473
|
+
return toItem(resp.item);
|
|
6474
|
+
},
|
|
6475
|
+
enabled: Boolean(itemId),
|
|
6476
|
+
...options,
|
|
6477
|
+
});
|
|
6478
|
+
};
|
|
6479
|
+
/** The artifacts this account has published: a public profile page. */
|
|
6480
|
+
const useAccountPublications = (handle, options) => {
|
|
6481
|
+
return useQuery({
|
|
6482
|
+
queryKey: queryKeys.items.byOwner(handle || ''),
|
|
6483
|
+
queryFn: async () => searchLibrary({ owner: handle, max: 100, sort: 'recent' }),
|
|
6484
|
+
enabled: Boolean(handle),
|
|
6485
|
+
...options,
|
|
6486
|
+
});
|
|
6487
|
+
};
|
|
6488
|
+
/** The artifacts the signed-in visitor orbits. */
|
|
6489
|
+
const useMyOrbits = (offset = 0, max = 20, options) => {
|
|
6490
|
+
return useQuery({
|
|
6491
|
+
queryKey: [...queryKeys.items.orbits(), offset, max],
|
|
6492
|
+
queryFn: async () => {
|
|
6493
|
+
const resp = await requestDatalayer({
|
|
6494
|
+
url: `${configuration.libraryUrl}/api/library/v1/orbits?offset=${offset}&max=${max}`,
|
|
6495
|
+
method: 'GET',
|
|
6496
|
+
});
|
|
6497
|
+
if (!resp.success) {
|
|
6498
|
+
throw new Error(resp.message || 'Failed to get the orbits');
|
|
6499
|
+
}
|
|
6500
|
+
return {
|
|
6501
|
+
items: (resp.items || [])
|
|
6502
|
+
.map((item) => toItem(item))
|
|
6503
|
+
.filter(Boolean),
|
|
6504
|
+
total: Number(resp.total ?? 0),
|
|
6505
|
+
};
|
|
6506
|
+
},
|
|
6507
|
+
...options,
|
|
6508
|
+
});
|
|
6509
|
+
};
|
|
6510
|
+
/**
|
|
6511
|
+
* What one account orbits, on its public profile.
|
|
6512
|
+
*
|
|
6513
|
+
* Whether it publishes them at all is the service's answer, not this
|
|
6514
|
+
* caller's: `shown` says so, and an account that has not opted in reads the
|
|
6515
|
+
* same as one that orbits nothing.
|
|
6516
|
+
*/
|
|
6517
|
+
const useAccountOrbits = (handle, offset = 0, max = 12, options) => {
|
|
6518
|
+
return useQuery({
|
|
6519
|
+
queryKey: [
|
|
6520
|
+
...queryKeys.items.orbits(),
|
|
6521
|
+
'account',
|
|
6522
|
+
handle || '',
|
|
6523
|
+
offset,
|
|
6524
|
+
max,
|
|
6525
|
+
],
|
|
6526
|
+
queryFn: async () => {
|
|
6527
|
+
const resp = await requestDatalayer({
|
|
6528
|
+
url: `${configuration.libraryUrl}/api/library/v1/accounts/${encodeURIComponent(handle)}/orbits?offset=${offset}&max=${max}`,
|
|
6529
|
+
method: 'GET',
|
|
6530
|
+
});
|
|
6531
|
+
if (!resp.success) {
|
|
6532
|
+
throw new Error(resp.message || 'Failed to get the orbits');
|
|
6533
|
+
}
|
|
6534
|
+
return {
|
|
6535
|
+
items: (resp.items || [])
|
|
6536
|
+
.map((item) => toItem(item))
|
|
6537
|
+
.filter(Boolean),
|
|
6538
|
+
total: Number(resp.total ?? 0),
|
|
6539
|
+
shown: Boolean(resp.shown),
|
|
6540
|
+
};
|
|
6541
|
+
},
|
|
6542
|
+
enabled: Boolean(handle),
|
|
6543
|
+
...options,
|
|
6544
|
+
});
|
|
6545
|
+
};
|
|
6546
|
+
/** Who orbits an artifact. */
|
|
6547
|
+
const useItemOrbiters = (itemId, offset = 0, max = 20, options) => {
|
|
6548
|
+
return useQuery({
|
|
6549
|
+
queryKey: [
|
|
6550
|
+
...queryKeys.items.orbiters(itemId || ''),
|
|
6551
|
+
offset,
|
|
6552
|
+
max,
|
|
6553
|
+
],
|
|
6554
|
+
queryFn: async () => {
|
|
6555
|
+
const resp = await requestDatalayer({
|
|
6556
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/orbiters?offset=${offset}&max=${max}`,
|
|
6557
|
+
method: 'GET',
|
|
6558
|
+
});
|
|
6559
|
+
if (!resp.success) {
|
|
6560
|
+
throw new Error(resp.message || 'Failed to get the orbiters');
|
|
6561
|
+
}
|
|
6562
|
+
return {
|
|
6563
|
+
orbiters: resp.orbiters || [],
|
|
6564
|
+
total: Number(resp.total ?? 0),
|
|
6565
|
+
};
|
|
6566
|
+
},
|
|
6567
|
+
enabled: Boolean(itemId),
|
|
6568
|
+
...options,
|
|
6569
|
+
});
|
|
6570
|
+
};
|
|
6571
|
+
/**
|
|
6572
|
+
* What was made of a public artifact (B5-04): every derivation counted by
|
|
6573
|
+
* kind, and the derived artifacts that are public themselves, newest first.
|
|
6574
|
+
*/
|
|
6575
|
+
const useItemDerivations = (itemId, offset = 0, max = 20, options) => {
|
|
6576
|
+
return useQuery({
|
|
6577
|
+
queryKey: [
|
|
6578
|
+
...queryKeys.items.derivations(itemId || ''),
|
|
6579
|
+
offset,
|
|
6580
|
+
max,
|
|
6581
|
+
],
|
|
6582
|
+
queryFn: async () => {
|
|
6583
|
+
const resp = await requestDatalayer({
|
|
6584
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/derivations?offset=${offset}&max=${max}`,
|
|
6585
|
+
method: 'GET',
|
|
6586
|
+
});
|
|
6587
|
+
if (!resp.success) {
|
|
6588
|
+
throw new Error(resp.message || 'Failed to get the derivations');
|
|
6589
|
+
}
|
|
6590
|
+
return {
|
|
6591
|
+
derivations: (resp.derivations || []).map((derivation) => ({
|
|
6592
|
+
kind: String(derivation.kind || ''),
|
|
6593
|
+
fromVersion: derivation.fromVersion ?? null,
|
|
6594
|
+
derivedAt: derivation.derivedAt,
|
|
6595
|
+
item: toItem(derivation.item),
|
|
6596
|
+
})),
|
|
6597
|
+
counts: (resp.counts || {}),
|
|
6598
|
+
total: Number(resp.total ?? 0),
|
|
6599
|
+
};
|
|
6600
|
+
},
|
|
6601
|
+
enabled: Boolean(itemId),
|
|
6602
|
+
...options,
|
|
6603
|
+
});
|
|
6604
|
+
};
|
|
6605
|
+
/**
|
|
6606
|
+
* Show an artifact as orbited before the server has said so, and put it
|
|
6607
|
+
* back if the server disagrees.
|
|
6608
|
+
*
|
|
6609
|
+
* A star that waits for a round trip feels broken, and the count is on the
|
|
6610
|
+
* button the visitor just pressed. The server answers with a recount, which
|
|
6611
|
+
* is what finally lands in the cache.
|
|
6612
|
+
*/
|
|
6613
|
+
const applyOrbitToCaches = (itemId, orbited, orbits) => {
|
|
6614
|
+
const patch = (item) => {
|
|
6615
|
+
if (!item || String(item.id ?? item.uid) !== String(itemId)) {
|
|
6616
|
+
return item;
|
|
6617
|
+
}
|
|
6618
|
+
const nextOrbits = orbits !== undefined
|
|
6619
|
+
? orbits
|
|
6620
|
+
: Math.max(0, Number(item.orbits || 0) + (orbited ? 1 : -1));
|
|
6621
|
+
return { ...item, orbited, orbits: nextOrbits };
|
|
6622
|
+
};
|
|
6623
|
+
queryClient.setQueriesData({ queryKey: queryKeys.items.all() }, (data) => {
|
|
6624
|
+
if (!data) {
|
|
6625
|
+
return data;
|
|
6626
|
+
}
|
|
6627
|
+
if (Array.isArray(data)) {
|
|
6628
|
+
return data.map(patch);
|
|
6629
|
+
}
|
|
6630
|
+
if (Array.isArray(data.items)) {
|
|
6631
|
+
return { ...data, items: data.items.map(patch) };
|
|
6632
|
+
}
|
|
6633
|
+
if (data.id || data.uid) {
|
|
6634
|
+
return patch(data);
|
|
6635
|
+
}
|
|
6636
|
+
return data;
|
|
6637
|
+
});
|
|
6638
|
+
};
|
|
6639
|
+
const useOrbitItem = () => {
|
|
6640
|
+
return useMutation({
|
|
6641
|
+
mutationFn: async (itemId) => {
|
|
6642
|
+
const resp = await requestDatalayer({
|
|
6643
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/orbit`,
|
|
6644
|
+
method: 'PUT',
|
|
6645
|
+
});
|
|
6646
|
+
if (!resp.success) {
|
|
6647
|
+
throw new Error(resp.message || 'Failed to orbit the artifact');
|
|
6648
|
+
}
|
|
6649
|
+
return { itemId, orbits: Number(resp.orbits ?? 0), orbited: true };
|
|
6650
|
+
},
|
|
6651
|
+
onMutate: async (itemId) => {
|
|
6652
|
+
applyOrbitToCaches(itemId, true);
|
|
6653
|
+
return { itemId };
|
|
6654
|
+
},
|
|
6655
|
+
onError: (_error, itemId) => {
|
|
6656
|
+
applyOrbitToCaches(itemId, false);
|
|
6657
|
+
},
|
|
6658
|
+
onSuccess: result => {
|
|
6659
|
+
applyOrbitToCaches(result.itemId, true, result.orbits);
|
|
6660
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.items.orbits() });
|
|
6661
|
+
},
|
|
6662
|
+
});
|
|
6663
|
+
};
|
|
6664
|
+
const useUnorbitItem = () => {
|
|
6665
|
+
return useMutation({
|
|
6666
|
+
mutationFn: async (itemId) => {
|
|
6667
|
+
const resp = await requestDatalayer({
|
|
6668
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/orbit`,
|
|
6669
|
+
method: 'DELETE',
|
|
6670
|
+
});
|
|
6671
|
+
if (!resp.success) {
|
|
6672
|
+
throw new Error(resp.message || 'Failed to leave the orbit');
|
|
6673
|
+
}
|
|
6674
|
+
return { itemId, orbits: Number(resp.orbits ?? 0), orbited: false };
|
|
6675
|
+
},
|
|
6676
|
+
onMutate: async (itemId) => {
|
|
6677
|
+
applyOrbitToCaches(itemId, false);
|
|
6678
|
+
return { itemId };
|
|
6679
|
+
},
|
|
6680
|
+
onError: (_error, itemId) => {
|
|
6681
|
+
applyOrbitToCaches(itemId, true);
|
|
6682
|
+
},
|
|
6683
|
+
onSuccess: result => {
|
|
6684
|
+
applyOrbitToCaches(result.itemId, false, result.orbits);
|
|
6685
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.items.orbits() });
|
|
6686
|
+
},
|
|
6687
|
+
});
|
|
6688
|
+
};
|
|
6689
|
+
/** Feature an artifact on the home page. `platform_admin` only. */
|
|
6690
|
+
const useFeatureItem = () => {
|
|
6691
|
+
return useMutation({
|
|
6692
|
+
mutationFn: async ({ itemId, rank, }) => {
|
|
6693
|
+
const resp = await requestDatalayer({
|
|
6694
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/featured`,
|
|
6695
|
+
method: 'PUT',
|
|
6696
|
+
body: { rank: rank ?? null },
|
|
6697
|
+
// One toast, not two: the caller says which artifact failed and
|
|
6698
|
+
// why, so the generic report underneath is a duplicate.
|
|
6699
|
+
notifyOnError: false,
|
|
6700
|
+
});
|
|
6701
|
+
if (!resp.success) {
|
|
6702
|
+
throw new Error(resp.message || 'Failed to feature the artifact');
|
|
6703
|
+
}
|
|
6704
|
+
return resp;
|
|
6705
|
+
},
|
|
6706
|
+
onSuccess: () => {
|
|
6707
|
+
invalidateEverywhere(queryClient, queryKeys.items.all());
|
|
6708
|
+
},
|
|
6709
|
+
});
|
|
6710
|
+
};
|
|
6711
|
+
const useUnfeatureItem = () => {
|
|
6712
|
+
return useMutation({
|
|
6713
|
+
mutationFn: async (itemId) => {
|
|
6714
|
+
const resp = await requestDatalayer({
|
|
6715
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/featured`,
|
|
6716
|
+
method: 'DELETE',
|
|
6717
|
+
// One toast, not two: the caller says which artifact failed and
|
|
6718
|
+
// why, so the generic report underneath is a duplicate.
|
|
6719
|
+
notifyOnError: false,
|
|
6720
|
+
});
|
|
6721
|
+
if (!resp.success) {
|
|
6722
|
+
throw new Error(resp.message || 'Failed to unfeature the artifact');
|
|
6723
|
+
}
|
|
6724
|
+
return resp;
|
|
6725
|
+
},
|
|
6726
|
+
onSuccess: () => {
|
|
6727
|
+
invalidateEverywhere(queryClient, queryKeys.items.all());
|
|
6728
|
+
},
|
|
6729
|
+
});
|
|
6730
|
+
};
|
|
6731
|
+
const useSetFeaturedRank = () => {
|
|
6732
|
+
return useMutation({
|
|
6733
|
+
mutationFn: async ({ itemId, rank, }) => {
|
|
6734
|
+
const resp = await requestDatalayer({
|
|
6735
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/featured`,
|
|
6736
|
+
method: 'PATCH',
|
|
6737
|
+
body: { rank },
|
|
6738
|
+
// One toast, not two: the caller says which artifact failed and
|
|
6739
|
+
// why, so the generic report underneath is a duplicate.
|
|
6740
|
+
notifyOnError: false,
|
|
6741
|
+
});
|
|
6742
|
+
if (!resp.success) {
|
|
6743
|
+
throw new Error(resp.message || 'Failed to move the artifact');
|
|
6744
|
+
}
|
|
6745
|
+
return resp;
|
|
6746
|
+
},
|
|
6747
|
+
onSuccess: () => {
|
|
6748
|
+
invalidateEverywhere(queryClient, queryKeys.items.featured());
|
|
6749
|
+
},
|
|
6750
|
+
});
|
|
6751
|
+
};
|
|
6752
|
+
/**
|
|
6753
|
+
* Refresh everything that carries an artifact's public flag or its image.
|
|
6754
|
+
*
|
|
6755
|
+
* `items` and `spaces` cover the library listings, but an artifact opened in
|
|
6756
|
+
* an editor is read through its own per-type query — `notebooks.detail(id)`
|
|
6757
|
+
* for the editor, `notebooks.bySpace(id)` for the table — and neither
|
|
6758
|
+
* shares a prefix with either of those. Leaving them alone is what made the
|
|
6759
|
+
* Publish button keep saying "Publish" after publishing, and "Published"
|
|
6760
|
+
* after withdrawing: the mutation succeeded, the cached copy still held the
|
|
6761
|
+
* old `is_public_b`, and nothing asked for it again for five minutes.
|
|
6762
|
+
*
|
|
6763
|
+
* A course is a space, but it is not read through the `spaces` queries: it
|
|
6764
|
+
* has a namespace of its own, so publishing one from its header left the
|
|
6765
|
+
* course page holding the `public` flag it loaded with and the button
|
|
6766
|
+
* saying the opposite of what had just happened.
|
|
6767
|
+
*
|
|
6768
|
+
* **`refetchType: 'all'`, which is what makes any of the above work.**
|
|
6769
|
+
*
|
|
6770
|
+
* Naming a query is not the same as making it ask again. The default,
|
|
6771
|
+
* `'active'`, refetches the queries something is currently watching and
|
|
6772
|
+
* leaves the rest marked stale — right nearly everywhere, and wrong here,
|
|
6773
|
+
* because this application sets `refetchOnMount: false` (see
|
|
6774
|
+
* `DEFAULT_QUERY_OPTIONS`, and the client the web application builds). A
|
|
6775
|
+
* query that mounts holding stale data renders it and asks for nothing.
|
|
6776
|
+
*
|
|
6777
|
+
* So publishing worked exactly once. The editor it was published from was
|
|
6778
|
+
* mounted and flipped its button; the notebooks table, the library, the
|
|
6779
|
+
* search page and the artifact's own public page were not, and kept the
|
|
6780
|
+
* answer they already had. Come back to the editor after that and *it* is
|
|
6781
|
+
* now the page that was not mounted for the next withdrawal, so its button
|
|
6782
|
+
* is wrong too — which is what "it works the first time" means.
|
|
6783
|
+
*
|
|
6784
|
+
* The whole of each type's namespace, not only its `details()`. A table
|
|
6785
|
+
* lists its artifacts under `bySpace(spaceId)`, which shares no prefix with
|
|
6786
|
+
* `detail(id)` — so publishing from an editor and walking back to the table
|
|
6787
|
+
* showed the row exactly as it was, switch and all.
|
|
6788
|
+
*
|
|
6789
|
+
* The `model` queries are the one thing held out. A notebook's model is its
|
|
6790
|
+
* content, megabytes of it, and it lives *under* `detail(id)` — so
|
|
6791
|
+
* refetching a namespace would pull every open notebook's content over the
|
|
6792
|
+
* wire each time somebody pressed Publish. Whether an artifact is public
|
|
6793
|
+
* says nothing about what is in it.
|
|
6794
|
+
*/
|
|
6795
|
+
const invalidateArtifactVisibility = () => {
|
|
6796
|
+
const refetchType = 'all';
|
|
6797
|
+
/** Everything about an artifact except what is written in it. */
|
|
6798
|
+
const notItsContent = (query) => query.queryKey[query.queryKey.length - 1] !== 'model';
|
|
6799
|
+
queryClient.invalidateQueries({
|
|
6800
|
+
queryKey: queryKeys.items.all(),
|
|
6801
|
+
refetchType,
|
|
6802
|
+
});
|
|
6803
|
+
queryClient.invalidateQueries({ queryKey: ['spaces'], refetchType });
|
|
6804
|
+
for (const namespace of [
|
|
6805
|
+
queryKeys.notebooks.all(),
|
|
6806
|
+
queryKeys.documents.all(),
|
|
6807
|
+
queryKeys.decks.all(),
|
|
6808
|
+
queryKeys.cells.all(),
|
|
6809
|
+
queryKeys.lessons.all(),
|
|
6810
|
+
queryKeys.exercises.all(),
|
|
6811
|
+
queryKeys.assignments.all(),
|
|
6812
|
+
queryKeys.courses.all(),
|
|
6813
|
+
]) {
|
|
6814
|
+
queryClient.invalidateQueries({
|
|
6815
|
+
queryKey: namespace,
|
|
6816
|
+
refetchType,
|
|
6817
|
+
predicate: notItsContent,
|
|
6818
|
+
});
|
|
6819
|
+
}
|
|
6820
|
+
};
|
|
6821
|
+
/**
|
|
6822
|
+
* What an artifact looks like in the library: a capture the publisher took,
|
|
6823
|
+
* the box drawn for its kind, or an icon from the Datalayer set.
|
|
6824
|
+
*/
|
|
6825
|
+
const useSetItemImage = () => {
|
|
6826
|
+
return useMutation({
|
|
6827
|
+
mutationFn: async ({ itemId, kind, ref, data, }) => {
|
|
6828
|
+
const resp = await requestDatalayer({
|
|
6829
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/image`,
|
|
6830
|
+
method: 'PUT',
|
|
6831
|
+
body: { kind, ref, data },
|
|
6832
|
+
});
|
|
6833
|
+
if (!resp.success) {
|
|
6834
|
+
throw new Error(resp.message || 'Failed to set the image');
|
|
6835
|
+
}
|
|
6836
|
+
return resp.image;
|
|
6837
|
+
},
|
|
6838
|
+
onSuccess: invalidateArtifactVisibility,
|
|
6839
|
+
});
|
|
6840
|
+
};
|
|
6841
|
+
const useResetItemImage = () => {
|
|
6842
|
+
return useMutation({
|
|
6843
|
+
mutationFn: async (itemId) => {
|
|
6844
|
+
const resp = await requestDatalayer({
|
|
6845
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/image`,
|
|
6846
|
+
method: 'DELETE',
|
|
6847
|
+
});
|
|
6848
|
+
if (!resp.success) {
|
|
6849
|
+
throw new Error(resp.message || 'Failed to reset the image');
|
|
6850
|
+
}
|
|
6851
|
+
return resp.image;
|
|
6852
|
+
},
|
|
6853
|
+
onSuccess: invalidateArtifactVisibility,
|
|
6854
|
+
});
|
|
6855
|
+
};
|
|
6856
|
+
/**
|
|
6857
|
+
* Tell the library that an artifact was reused: cloned into a space, or
|
|
6858
|
+
* instantiated as a runtime. The reuse itself belongs to the service that
|
|
6859
|
+
* performed it, so this is a count, not the act.
|
|
6860
|
+
*/
|
|
6861
|
+
const useCountItemReuse = () => {
|
|
6862
|
+
return useMutation({
|
|
6863
|
+
mutationFn: async ({ itemId, kind, }) => {
|
|
6864
|
+
const resp = await requestDatalayer({
|
|
6865
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/reused`,
|
|
6866
|
+
method: 'POST',
|
|
6867
|
+
body: { kind },
|
|
6868
|
+
});
|
|
6869
|
+
if (!resp.success) {
|
|
6870
|
+
throw new Error(resp.message || 'Failed to count the reuse');
|
|
6871
|
+
}
|
|
6872
|
+
return resp;
|
|
6873
|
+
},
|
|
6874
|
+
});
|
|
6875
|
+
};
|
|
6876
|
+
/**
|
|
6877
|
+
* Publish the platform's own catalogue — the agents — into the library.
|
|
6878
|
+
* `platform_admin` only; the catalogue lives in this application's build.
|
|
6879
|
+
*/
|
|
6880
|
+
const useSyncArtifactCatalogue = () => {
|
|
6881
|
+
return useMutation({
|
|
6882
|
+
mutationFn: async ({ artifacts, artifactType = 'agent', }) => {
|
|
6883
|
+
const resp = await requestDatalayer({
|
|
6884
|
+
url: `${configuration.libraryUrl}/api/library/v1/artifacts/sync`,
|
|
6885
|
+
method: 'PUT',
|
|
6886
|
+
body: { artifact_type: artifactType, artifacts },
|
|
6887
|
+
// One toast, not two: the caller says which artifact failed and
|
|
6888
|
+
// why, so the generic report underneath is a duplicate.
|
|
6889
|
+
notifyOnError: false,
|
|
6890
|
+
});
|
|
6891
|
+
if (!resp.success) {
|
|
6892
|
+
throw new Error(resp.message || 'Failed to synchronize the catalogue');
|
|
6893
|
+
}
|
|
6894
|
+
return resp;
|
|
6895
|
+
},
|
|
6896
|
+
onSuccess: () => {
|
|
6897
|
+
invalidateEverywhere(queryClient, queryKeys.items.all());
|
|
6898
|
+
},
|
|
6899
|
+
});
|
|
6900
|
+
};
|
|
6901
|
+
/**
|
|
6902
|
+
* Publish an artifact in the library: choose its image, then make it
|
|
6903
|
+
* public.
|
|
6904
|
+
*
|
|
6905
|
+
* The one place that publishes. It used to be a `ToggleSwitch` in each of
|
|
6906
|
+
* seven tables and three editors, each with its own copy of the mutation
|
|
6907
|
+
* and its own idea of what to roll back; publishing is an act with
|
|
6908
|
+
* consequences a switch cannot express — a picture to choose, a public page
|
|
6909
|
+
* that appears, a search it enters.
|
|
6910
|
+
*
|
|
6911
|
+
* The image is set first: a failure there leaves the artifact private, which
|
|
6912
|
+
* is recoverable by trying again, where the reverse would publish something
|
|
6913
|
+
* showing the wrong picture.
|
|
6914
|
+
*/
|
|
6915
|
+
const usePublishArtifact = () => {
|
|
6916
|
+
const setImage = useSetItemImage();
|
|
6917
|
+
return useMutation({
|
|
6918
|
+
mutationFn: async ({ itemId, image, tags, description, }) => {
|
|
6919
|
+
/*
|
|
6920
|
+
* Publish first, then dress it.
|
|
6921
|
+
*
|
|
6922
|
+
* The image used to be set before the artifact was made public, so
|
|
6923
|
+
* that a picture that would not take left it private rather than
|
|
6924
|
+
* published with the wrong one. That order could never work: the
|
|
6925
|
+
* library indexes public artifacts, so a private one has no
|
|
6926
|
+
* projection, and setting an image on it answered "The artifact is
|
|
6927
|
+
* not found" — every first publish with a picture failed.
|
|
6928
|
+
*
|
|
6929
|
+
* The intent survives the reordering. The artifact goes public, which
|
|
6930
|
+
* is what creates the projection; if the picture then will not take,
|
|
6931
|
+
* it is withdrawn again and the message still tells the truth.
|
|
6932
|
+
*/
|
|
6933
|
+
const resp = await requestDatalayer({
|
|
6934
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/public`,
|
|
6935
|
+
method: 'PATCH',
|
|
6936
|
+
body: {
|
|
6937
|
+
is_public: true,
|
|
6938
|
+
// Absent, not null: the service reads "leave it alone" from a
|
|
6939
|
+
// missing key, and a caller that does not know an artifact's
|
|
6940
|
+
// words must not be able to clear them by omission.
|
|
6941
|
+
...(tags === undefined ? {} : { tags }),
|
|
6942
|
+
...(description === undefined ? {} : { description }),
|
|
6943
|
+
},
|
|
6944
|
+
});
|
|
6945
|
+
if (!resp.success) {
|
|
6946
|
+
throw new Error(resp.message || 'Failed to publish the artifact');
|
|
6947
|
+
}
|
|
6948
|
+
if (image) {
|
|
6949
|
+
try {
|
|
6950
|
+
await setImage.mutateAsync({ itemId, ...image });
|
|
6951
|
+
}
|
|
6952
|
+
catch (error) {
|
|
6953
|
+
// Back to private, so the artifact is never left public wearing
|
|
6954
|
+
// the drawing the publisher did not choose.
|
|
6955
|
+
try {
|
|
6956
|
+
await requestDatalayer({
|
|
6957
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/public`,
|
|
6958
|
+
method: 'PATCH',
|
|
6959
|
+
body: { is_public: false },
|
|
6960
|
+
});
|
|
6961
|
+
}
|
|
6962
|
+
catch {
|
|
6963
|
+
// The withdrawal failed too; the error below is still the one
|
|
6964
|
+
// worth showing, and it is the more actionable of the two.
|
|
6965
|
+
}
|
|
6966
|
+
throw new Error(`The image could not be set, so the artifact stays private: ${error?.message || 'unknown error'}`, { cause: error });
|
|
6967
|
+
}
|
|
6968
|
+
}
|
|
6969
|
+
return resp;
|
|
6970
|
+
},
|
|
6971
|
+
onSuccess: invalidateArtifactVisibility,
|
|
6972
|
+
});
|
|
6973
|
+
};
|
|
6974
|
+
/**
|
|
6975
|
+
* Withdraw an artifact from the library. Its orbits are kept: publishing it
|
|
6976
|
+
* again brings them back.
|
|
6977
|
+
*/
|
|
6978
|
+
/**
|
|
6979
|
+
* Rename a published artifact, or change what it says about itself.
|
|
6980
|
+
*
|
|
6981
|
+
* The words belong to the artifact rather than to the library — the
|
|
6982
|
+
* library holds a projection of them — so the library service writes the
|
|
6983
|
+
* artifact's own source and that write carries the change into the index.
|
|
6984
|
+
* Which is why this is one request and not two.
|
|
6985
|
+
*
|
|
6986
|
+
* Every field is optional and every one means "leave it alone" when
|
|
6987
|
+
* omitted, so a caller may change one without knowing the others.
|
|
6988
|
+
*/
|
|
6989
|
+
const useUpdateArtifactDetails = () => {
|
|
6990
|
+
return useMutation({
|
|
6991
|
+
mutationFn: async ({ itemId, name, description, tags, }) => {
|
|
6992
|
+
const resp = await requestDatalayer({
|
|
6993
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}`,
|
|
6994
|
+
method: 'PATCH',
|
|
6995
|
+
body: {
|
|
6996
|
+
...(name === undefined ? {} : { name }),
|
|
6997
|
+
...(description === undefined ? {} : { description }),
|
|
6998
|
+
...(tags === undefined ? {} : { tags }),
|
|
6999
|
+
},
|
|
7000
|
+
});
|
|
7001
|
+
if (!resp.success) {
|
|
7002
|
+
throw new Error(resp.message || 'Failed to update the artifact');
|
|
7003
|
+
}
|
|
7004
|
+
return resp;
|
|
7005
|
+
},
|
|
7006
|
+
// The same fan-out publishing uses: a rename changes the card in every
|
|
7007
|
+
// listing, the editor's own copy, and the artifact's public page.
|
|
7008
|
+
onSuccess: invalidateArtifactVisibility,
|
|
7009
|
+
});
|
|
7010
|
+
};
|
|
7011
|
+
const useUnpublishArtifact = () => {
|
|
7012
|
+
return useMutation({
|
|
7013
|
+
mutationFn: async (itemId) => {
|
|
7014
|
+
const resp = await requestDatalayer({
|
|
7015
|
+
url: `${configuration.libraryUrl}/api/library/v1/items/${encodeURIComponent(itemId)}/public`,
|
|
7016
|
+
method: 'PATCH',
|
|
7017
|
+
body: { is_public: false },
|
|
7018
|
+
});
|
|
7019
|
+
if (!resp.success) {
|
|
7020
|
+
throw new Error(resp.message || 'Failed to withdraw the artifact');
|
|
7021
|
+
}
|
|
7022
|
+
return resp;
|
|
7023
|
+
},
|
|
7024
|
+
onSuccess: invalidateArtifactVisibility,
|
|
5989
7025
|
});
|
|
5990
7026
|
};
|
|
5991
7027
|
// ============================================================================
|
|
@@ -6342,28 +7378,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
6342
7378
|
...options,
|
|
6343
7379
|
});
|
|
6344
7380
|
};
|
|
6345
|
-
/**
|
|
6346
|
-
* Refresh public courses list
|
|
6347
|
-
* @param options - Mutation options
|
|
6348
|
-
*/
|
|
6349
|
-
const useRefreshPublicCourses = (options) => {
|
|
6350
|
-
const queryClient = useQueryClient();
|
|
6351
|
-
return useMutation({
|
|
6352
|
-
mutationFn: async () => {
|
|
6353
|
-
const resp = await requestDatalayer({
|
|
6354
|
-
url: `${configuration.libraryUrl}/api/library/v1/courses/public`,
|
|
6355
|
-
method: 'GET',
|
|
6356
|
-
});
|
|
6357
|
-
return resp;
|
|
6358
|
-
},
|
|
6359
|
-
onSuccess: () => {
|
|
6360
|
-
queryClient.invalidateQueries({
|
|
6361
|
-
queryKey: queryKeys.courses.public(),
|
|
6362
|
-
});
|
|
6363
|
-
},
|
|
6364
|
-
...options,
|
|
6365
|
-
});
|
|
6366
|
-
};
|
|
6367
7381
|
/**
|
|
6368
7382
|
* Refresh instructor courses list
|
|
6369
7383
|
* @param options - Mutation options
|
|
@@ -6572,82 +7586,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
6572
7586
|
...options,
|
|
6573
7587
|
});
|
|
6574
7588
|
};
|
|
6575
|
-
/**
|
|
6576
|
-
* Refresh a specific dataset
|
|
6577
|
-
* @param options - Mutation options
|
|
6578
|
-
*/
|
|
6579
|
-
const useRefreshDataset = (options) => {
|
|
6580
|
-
const queryClient = useQueryClient();
|
|
6581
|
-
return useMutation({
|
|
6582
|
-
mutationFn: async (variables) => {
|
|
6583
|
-
const datasetId = typeof variables === 'string' ? variables : variables.datasetId;
|
|
6584
|
-
const selectedPrincipalUid = typeof variables === 'string'
|
|
6585
|
-
? undefined
|
|
6586
|
-
: variables.selectedPrincipalUid;
|
|
6587
|
-
const selectedPrincipalKind = typeof variables === 'string'
|
|
6588
|
-
? undefined
|
|
6589
|
-
: variables.selectedPrincipalKind;
|
|
6590
|
-
const params = new URLSearchParams();
|
|
6591
|
-
if (selectedPrincipalUid) {
|
|
6592
|
-
params.set('selected_principal_uid', selectedPrincipalUid);
|
|
6593
|
-
}
|
|
6594
|
-
if (selectedPrincipalKind) {
|
|
6595
|
-
params.set('selected_principal_kind', selectedPrincipalKind);
|
|
6596
|
-
}
|
|
6597
|
-
const query = params.toString();
|
|
6598
|
-
const resp = await requestDatalayer({
|
|
6599
|
-
url: `${configuration.runtimesUrl}/api/runtimes/v1/spaces/items/${datasetId}${query ? `?${query}` : ''}`,
|
|
6600
|
-
method: 'GET',
|
|
6601
|
-
});
|
|
6602
|
-
return resp;
|
|
6603
|
-
},
|
|
6604
|
-
onSuccess: (data, variables) => {
|
|
6605
|
-
const datasetId = typeof variables === 'string' ? variables : variables.datasetId;
|
|
6606
|
-
queryClient.invalidateQueries({
|
|
6607
|
-
queryKey: queryKeys.datasets.detail(datasetId),
|
|
6608
|
-
});
|
|
6609
|
-
},
|
|
6610
|
-
...options,
|
|
6611
|
-
});
|
|
6612
|
-
};
|
|
6613
|
-
/**
|
|
6614
|
-
* Refresh space datasets
|
|
6615
|
-
* @param options - Mutation options
|
|
6616
|
-
*/
|
|
6617
|
-
const useRefreshSpaceDatasets = (options) => {
|
|
6618
|
-
const queryClient = useQueryClient();
|
|
6619
|
-
return useMutation({
|
|
6620
|
-
mutationFn: async (variables) => {
|
|
6621
|
-
const spaceId = typeof variables === 'string' ? variables : variables.spaceId;
|
|
6622
|
-
const selectedPrincipalUid = typeof variables === 'string'
|
|
6623
|
-
? undefined
|
|
6624
|
-
: variables.selectedPrincipalUid;
|
|
6625
|
-
const selectedPrincipalKind = typeof variables === 'string'
|
|
6626
|
-
? undefined
|
|
6627
|
-
: variables.selectedPrincipalKind;
|
|
6628
|
-
const params = new URLSearchParams();
|
|
6629
|
-
if (selectedPrincipalUid) {
|
|
6630
|
-
params.set('selected_principal_uid', selectedPrincipalUid);
|
|
6631
|
-
}
|
|
6632
|
-
if (selectedPrincipalKind) {
|
|
6633
|
-
params.set('selected_principal_kind', selectedPrincipalKind);
|
|
6634
|
-
}
|
|
6635
|
-
const query = params.toString();
|
|
6636
|
-
const resp = await requestDatalayer({
|
|
6637
|
-
url: `${configuration.runtimesUrl}/api/runtimes/v1/spaces/${spaceId}/items/types/dataset${query ? `?${query}` : ''}`,
|
|
6638
|
-
method: 'GET',
|
|
6639
|
-
});
|
|
6640
|
-
return resp;
|
|
6641
|
-
},
|
|
6642
|
-
onSuccess: (data, variables) => {
|
|
6643
|
-
const spaceId = typeof variables === 'string' ? variables : variables.spaceId;
|
|
6644
|
-
queryClient.invalidateQueries({
|
|
6645
|
-
queryKey: queryKeys.datasets.bySpace(spaceId),
|
|
6646
|
-
});
|
|
6647
|
-
},
|
|
6648
|
-
...options,
|
|
6649
|
-
});
|
|
6650
|
-
};
|
|
6651
7589
|
/**
|
|
6652
7590
|
* Refresh schools list
|
|
6653
7591
|
* @param options - Mutation options
|
|
@@ -6668,28 +7606,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
6668
7606
|
...options,
|
|
6669
7607
|
});
|
|
6670
7608
|
};
|
|
6671
|
-
/**
|
|
6672
|
-
* Get public items (query hook)
|
|
6673
|
-
* @param options - Query options
|
|
6674
|
-
*/
|
|
6675
|
-
const usePublicItems = (options) => {
|
|
6676
|
-
return useQuery({
|
|
6677
|
-
queryKey: queryKeys.items.public(),
|
|
6678
|
-
queryFn: async () => {
|
|
6679
|
-
const resp = await requestDatalayer({
|
|
6680
|
-
url: `${configuration.libraryUrl}/api/library/v1/search?q=*&max=-1`,
|
|
6681
|
-
method: 'GET',
|
|
6682
|
-
});
|
|
6683
|
-
return {
|
|
6684
|
-
...resp,
|
|
6685
|
-
items: (resp.items || [])
|
|
6686
|
-
.map((item) => toItem(item))
|
|
6687
|
-
.filter(Boolean),
|
|
6688
|
-
};
|
|
6689
|
-
},
|
|
6690
|
-
...options,
|
|
6691
|
-
});
|
|
6692
|
-
};
|
|
6693
7609
|
/**
|
|
6694
7610
|
* Get the current user's own public items (publications).
|
|
6695
7611
|
* @param options - Query options
|
|
@@ -6712,31 +7628,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
6712
7628
|
...options,
|
|
6713
7629
|
});
|
|
6714
7630
|
};
|
|
6715
|
-
/**
|
|
6716
|
-
* Refresh public items
|
|
6717
|
-
* @param options - Mutation options
|
|
6718
|
-
*/
|
|
6719
|
-
const useRefreshPublicItems = (options) => {
|
|
6720
|
-
const queryClient = useQueryClient();
|
|
6721
|
-
return useMutation({
|
|
6722
|
-
mutationFn: async () => {
|
|
6723
|
-
const resp = await requestDatalayer({
|
|
6724
|
-
url: `${configuration.libraryUrl}/api/library/v1/search?q=*&max=-1`,
|
|
6725
|
-
method: 'GET',
|
|
6726
|
-
});
|
|
6727
|
-
return {
|
|
6728
|
-
...resp,
|
|
6729
|
-
items: (resp.items || [])
|
|
6730
|
-
.map((item) => toItem(item))
|
|
6731
|
-
.filter(Boolean),
|
|
6732
|
-
};
|
|
6733
|
-
},
|
|
6734
|
-
onSuccess: () => {
|
|
6735
|
-
queryClient.invalidateQueries({ queryKey: queryKeys.items.public() });
|
|
6736
|
-
},
|
|
6737
|
-
...options,
|
|
6738
|
-
});
|
|
6739
|
-
};
|
|
6740
7631
|
/**
|
|
6741
7632
|
* Refresh space items
|
|
6742
7633
|
* @param options - Mutation options
|
|
@@ -7514,7 +8405,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
7514
8405
|
// Courses
|
|
7515
8406
|
useCourse,
|
|
7516
8407
|
useUpdateCourse,
|
|
7517
|
-
usePublicCourses,
|
|
7518
8408
|
useInstructorCourses,
|
|
7519
8409
|
useCourseEnrollments,
|
|
7520
8410
|
useEnrollStudentToCourse,
|
|
@@ -7524,7 +8414,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
7524
8414
|
useConfirmCourseItemCompletion,
|
|
7525
8415
|
useSetCourseItems,
|
|
7526
8416
|
useRefreshCourse,
|
|
7527
|
-
useRefreshPublicCourses,
|
|
7528
8417
|
useRefreshInstructorCourses,
|
|
7529
8418
|
useRefreshCoursesEnrollments,
|
|
7530
8419
|
useRefreshStudent,
|
|
@@ -7537,6 +8426,13 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
7537
8426
|
useCloneNotebook,
|
|
7538
8427
|
useRefreshNotebook,
|
|
7539
8428
|
useRefreshSpaceNotebooks,
|
|
8429
|
+
// Decks
|
|
8430
|
+
useDeck,
|
|
8431
|
+
useDecksBySpace,
|
|
8432
|
+
useCreateDeck,
|
|
8433
|
+
useUpdateDeck,
|
|
8434
|
+
useUpdateDeckModel,
|
|
8435
|
+
useCloneDeck,
|
|
7540
8436
|
// Documents
|
|
7541
8437
|
useDocument,
|
|
7542
8438
|
useDocumentsBySpace,
|
|
@@ -7553,12 +8449,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
7553
8449
|
useCloneCell,
|
|
7554
8450
|
useRefreshCell,
|
|
7555
8451
|
useRefreshSpaceCells,
|
|
7556
|
-
// Datasets
|
|
7557
|
-
useDataset,
|
|
7558
|
-
useDatasetsBySpace,
|
|
7559
|
-
useUpdateDataset,
|
|
7560
|
-
useRefreshDataset,
|
|
7561
|
-
useRefreshSpaceDatasets,
|
|
7562
8452
|
// Environments
|
|
7563
8453
|
useEnvironment,
|
|
7564
8454
|
useEnvironmentsBySpace,
|
|
@@ -7596,23 +8486,33 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
7596
8486
|
useMakeItemPublic,
|
|
7597
8487
|
useMakeItemPrivate,
|
|
7598
8488
|
useSearchPublicItems,
|
|
7599
|
-
|
|
8489
|
+
useLibraryArtifacts,
|
|
7600
8490
|
usePublications,
|
|
7601
|
-
|
|
8491
|
+
// Library (search, orbits, featuring, images, reuse)
|
|
8492
|
+
useFeaturedItems,
|
|
8493
|
+
useAllFeaturedItems,
|
|
8494
|
+
usePublicItem,
|
|
8495
|
+
useAccountPublications,
|
|
8496
|
+
useMyOrbits,
|
|
8497
|
+
useAccountOrbits,
|
|
8498
|
+
useItemOrbiters,
|
|
8499
|
+
useItemDerivations,
|
|
8500
|
+
useOrbitItem,
|
|
8501
|
+
useUnorbitItem,
|
|
8502
|
+
useFeatureItem,
|
|
8503
|
+
useUnfeatureItem,
|
|
8504
|
+
useSetFeaturedRank,
|
|
8505
|
+
useSetItemImage,
|
|
8506
|
+
useResetItemImage,
|
|
8507
|
+
useCountItemReuse,
|
|
8508
|
+
useSyncArtifactCatalogue,
|
|
8509
|
+
usePublishArtifact,
|
|
8510
|
+
useUnpublishArtifact,
|
|
8511
|
+
useUpdateArtifactDetails,
|
|
7602
8512
|
useRefreshSpaceItems,
|
|
7603
8513
|
useClearCachedPublicItems,
|
|
7604
8514
|
useClearCachedItems,
|
|
7605
8515
|
// Pages
|
|
7606
|
-
usePage,
|
|
7607
|
-
usePages,
|
|
7608
|
-
useCreatePage,
|
|
7609
|
-
useUpdatePage,
|
|
7610
|
-
useDeletePage,
|
|
7611
|
-
// Datasources
|
|
7612
|
-
useDatasource,
|
|
7613
|
-
useDatasources,
|
|
7614
|
-
useCreateDatasource,
|
|
7615
|
-
useUpdateDatasource,
|
|
7616
8516
|
// Secrets
|
|
7617
8517
|
useSecret,
|
|
7618
8518
|
useSecrets,
|
|
@@ -7661,6 +8561,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
7661
8561
|
useDraftBulkEmailsOutbounds,
|
|
7662
8562
|
useTryBulkEmailsOutbounds,
|
|
7663
8563
|
useLaunchBulkEmailsOutbounds,
|
|
8564
|
+
useMarkOutboundLaunched,
|
|
7664
8565
|
useSendOutboundEmailToUser,
|
|
7665
8566
|
useDeleteOutbound,
|
|
7666
8567
|
useSubscribeUserToOutbounds,
|
|
@@ -7710,6 +8611,9 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
7710
8611
|
documentUploadLoading,
|
|
7711
8612
|
documentUploadProgress,
|
|
7712
8613
|
resetDocumentUpload,
|
|
8614
|
+
deckUploadLoading,
|
|
8615
|
+
deckUploadProgress,
|
|
8616
|
+
resetDeckUpload,
|
|
7713
8617
|
};
|
|
7714
8618
|
};
|
|
7715
8619
|
export default useCache;
|