@datalayer/core 1.1.66 → 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/constants.d.ts +1 -1
- package/lib/api/constants.js +1 -1
- package/lib/api/contents/attachments.js +3 -7
- package/lib/api/contents/datasets.d.ts +48 -0
- package/lib/api/contents/datasets.js +44 -7
- package/lib/api/contents/runtimeMounts.js +1 -1
- package/lib/api/contents/sandboxUid.d.ts +13 -45
- package/lib/api/contents/sandboxUid.js +14 -65
- 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 +29 -0
- package/lib/api/iam/connectedAgents.js +35 -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 +1 -0
- package/lib/api/iam/index.js +1 -0
- package/lib/api/iam/mcpPolicy.d.ts +102 -1
- package/lib/api/iam/mcpPolicy.js +38 -2
- 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/trials.d.ts +38 -0
- package/lib/api/iam/trials.js +66 -0
- package/lib/api/index.d.ts +4 -0
- package/lib/api/index.js +4 -0
- package/lib/api/mcp/observability.d.ts +25 -9
- package/lib/api/mcp/observability.js +63 -50
- 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/constants.d.ts +1 -0
- package/lib/client/constants.js +1 -0
- package/lib/components/animation/AnimatedText.d.ts +1 -44
- package/lib/components/animation/AnimatedText.js +6 -122
- 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/avatars/UserAvatar.d.ts +7 -1
- package/lib/components/avatars/UserAvatar.js +15 -1
- package/lib/components/billing/BillingEntitySelect.d.ts +6 -0
- package/lib/components/billing/BillingEntitySelect.js +49 -10
- 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 -0
- package/lib/components/collaboration/LiveEditorCollaborators.d.ts +5 -0
- package/lib/components/collaboration/LiveEditorCollaborators.js +3 -1
- package/lib/components/display/DatalayerBox.d.ts +2 -3
- package/lib/components/display/DatalayerBox.js +1 -1
- package/lib/components/display/NavLink.js +3 -2
- package/lib/components/labels/StatusLabels.d.ts +2 -10
- package/lib/components/labels/StatusLabels.js +2 -26
- package/lib/components/principal/PrincipalAppearance.d.ts +21 -42
- package/lib/components/principal/PrincipalAppearance.js +232 -174
- package/lib/components/principal/PrincipalAvatar.d.ts +8 -1
- package/lib/components/principal/PrincipalAvatar.js +2 -2
- package/lib/components/principal/PrincipalDetailsOverlay.d.ts +8 -1
- package/lib/components/principal/PrincipalDetailsOverlay.js +2 -2
- package/lib/components/screencapture/Screencapture.js +46 -3
- package/lib/components/sharing/ShareAccessComponent.d.ts +45 -4
- package/lib/components/sharing/ShareAccessComponent.js +110 -78
- package/lib/components/sharing/SharingEditor.d.ts +5 -1
- package/lib/components/sharing/SharingEditor.js +9 -4
- 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/hooks/cacheConverters.d.ts +0 -7
- package/lib/hooks/cacheConverters.js +0 -25
- package/lib/hooks/index.d.ts +1 -0
- package/lib/hooks/index.js +1 -0
- package/lib/hooks/useBillingEntityStore.js +3 -0
- package/lib/hooks/useCache.d.ts +168 -25
- package/lib/hooks/useCache.js +1424 -307
- package/lib/hooks/useContents.d.ts +22 -1
- package/lib/hooks/useContents.js +90 -21
- package/lib/hooks/useMcp.d.ts +87 -1
- package/lib/hooks/useMcp.js +247 -12
- 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/useSpaceCacheStore.d.ts +50 -0
- package/lib/hooks/useSpaceCacheStore.js +85 -0
- package/lib/models/CreditsDTO.d.ts +6 -5
- package/lib/models/CreditsDTO.js +7 -5
- package/lib/models/McpBinding.d.ts +6 -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 +34 -0
- package/lib/models/StartedBy.js +36 -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/UserSettings.d.ts +13 -0
- package/lib/models/UserSettings.js +9 -0
- package/lib/models/contents/__fixtures__/v1-contracts.json +158 -30
- 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.js +3 -3
- package/lib/state/substates/IAMState.js +7 -0
- package/lib/state/substates/LayoutState.js +12 -0
- package/lib/state/substates/ProfileState.d.ts +1 -0
- package/lib/state/substates/ProfileState.js +1 -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/mcp/AdmittedClients.d.ts +44 -0
- package/lib/views/mcp/AdmittedClients.js +59 -0
- package/lib/views/mcp/ApprovalQueue.d.ts +66 -0
- package/lib/views/mcp/ApprovalQueue.js +95 -0
- package/lib/views/mcp/ConnectedAgents.js +38 -3
- package/lib/views/mcp/EnterpriseConsole.d.ts +1 -1
- package/lib/views/mcp/EnterpriseConsole.js +23 -1
- package/lib/views/mcp/IdentityProviders.d.ts +33 -0
- package/lib/views/mcp/IdentityProviders.js +299 -0
- package/lib/views/mcp/McpDashboard.js +90 -7
- package/lib/views/mcp/McpObservability.d.ts +30 -1
- package/lib/views/mcp/McpObservability.js +105 -19
- package/lib/views/mcp/NotebookRuns.d.ts +39 -0
- package/lib/views/mcp/NotebookRuns.js +44 -0
- package/lib/views/mcp/OrganizationPolicy.js +2 -1
- package/lib/views/mcp/PersonalPolicy.js +2 -1
- package/lib/views/mcp/PolicyForm.d.ts +17 -1
- package/lib/views/mcp/PolicyForm.js +25 -3
- package/lib/views/mcp/PolicyHistory.d.ts +13 -0
- package/lib/views/mcp/PolicyHistory.js +14 -1
- 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/TeamPolicies.js +66 -5
- 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/index.d.ts +5 -0
- package/lib/views/mcp/index.js +5 -0
- package/lib/views/otel/simpleAuthStore.js +3 -0
- package/lib/views/secrets/SecretEdit.js +3 -2
- package/lib/views/secrets/SecretNew.js +5 -1
- package/package.json +269 -264
- package/scripts/generate-mcp-types.py +1 -1
- package/scripts/generate-orchestration-types.py +920 -0
|
@@ -4,9 +4,14 @@ import { Box } from '@datalayer/primer-addons';
|
|
|
4
4
|
import { FormControl, Text, Textarea } from '@primer/react';
|
|
5
5
|
export const EMPTY_SHARING_PAYLOAD = {
|
|
6
6
|
access: {
|
|
7
|
-
view: { userUids: [], teamUids: [], organizationUids: [] },
|
|
8
|
-
update: { userUids: [], teamUids: [], organizationUids: [] },
|
|
9
|
-
execute: {
|
|
7
|
+
view: { userUids: [], teamUids: [], organizationUids: [], agentUids: [] },
|
|
8
|
+
update: { userUids: [], teamUids: [], organizationUids: [], agentUids: [] },
|
|
9
|
+
execute: {
|
|
10
|
+
userUids: [],
|
|
11
|
+
teamUids: [],
|
|
12
|
+
organizationUids: [],
|
|
13
|
+
agentUids: [],
|
|
14
|
+
},
|
|
10
15
|
},
|
|
11
16
|
};
|
|
12
17
|
const stringify = (value) => {
|
|
@@ -17,7 +22,7 @@ const stringify = (value) => {
|
|
|
17
22
|
return '{}';
|
|
18
23
|
}
|
|
19
24
|
};
|
|
20
|
-
export function SharingEditor({ value, onChange, label = 'Sharing', caption = 'Edit the sharing payload. Each access level (view/update/execute) can grant access to user, team, and
|
|
25
|
+
export function SharingEditor({ value, onChange, label = 'Sharing', caption = 'Edit the sharing payload. Each access level (view/update/execute) can grant access to user, team, organization, and agent UIDs.', rows = 10, disabled = false, }) {
|
|
21
26
|
const initial = useMemo(() => stringify(value), [value]);
|
|
22
27
|
const [raw, setRaw] = useState(initial);
|
|
23
28
|
const [error, setError] = useState(null);
|
|
@@ -5,5 +5,6 @@ export * from '../principal/PrincipalBanner';
|
|
|
5
5
|
export { buildPrincipalProfilePath, PrincipalDetailsOverlay, } from '../principal/PrincipalDetailsOverlay';
|
|
6
6
|
export type { PrincipalDetailsOverlayProps } from '../principal/PrincipalDetailsOverlay';
|
|
7
7
|
export * from '../principal/PrincipalSwitcherMenu';
|
|
8
|
+
export * from './sandboxSharing';
|
|
8
9
|
export * from './ShareAccessComponent';
|
|
9
10
|
export * from './SharingEditor';
|
|
@@ -8,5 +8,6 @@ export * from '../principal/PrincipalBadge.js';
|
|
|
8
8
|
export * from '../principal/PrincipalBanner.js';
|
|
9
9
|
export { buildPrincipalProfilePath, PrincipalDetailsOverlay, } from '../principal/PrincipalDetailsOverlay.js';
|
|
10
10
|
export * from '../principal/PrincipalSwitcherMenu.js';
|
|
11
|
+
export * from './sandboxSharing.js';
|
|
11
12
|
export * from './ShareAccessComponent.js';
|
|
12
13
|
export * from './SharingEditor.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a sandbox's sharing lives.
|
|
3
|
+
*
|
|
4
|
+
* A sandbox is a runtime underneath, and Runtimes owns who it is shared with:
|
|
5
|
+
* `GET|PUT /api/runtimes/v1/runtimes/{name}/sharing`, answering an `access`
|
|
6
|
+
* document in exactly the shape `ShareAccessDialog` reads. So the dialog
|
|
7
|
+
* talks to Runtimes directly and there is no transport to write.
|
|
8
|
+
*
|
|
9
|
+
* The one thing worth a function of its own is **which name**. A sandbox has
|
|
10
|
+
* two: the handle its session holds (`sb_…`) and the runtime's own, and
|
|
11
|
+
* Runtimes' routes take the second. Sending the first gets a `403` that reads
|
|
12
|
+
* as "you are not the owner" — the confusion that made the CLI's sharing
|
|
13
|
+
* commands unusable until September 2026, and the reason this is written down
|
|
14
|
+
* once rather than spelled out at each call site.
|
|
15
|
+
*
|
|
16
|
+
* @module components/sharing/sandboxSharing
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* The sharing URL for a sandbox, or `undefined` when there is nothing to ask.
|
|
20
|
+
*
|
|
21
|
+
* Answers `undefined` rather than a broken URL for a sandbox with no runtime
|
|
22
|
+
* behind it — a browser or local kernel, or a binding still reserving — so a
|
|
23
|
+
* caller can use the answer itself to decide whether to offer sharing at all.
|
|
24
|
+
*/
|
|
25
|
+
export declare const sandboxSharingUrl: (runtimesUrl: string | undefined | null, runtimeName: string | undefined | null) => string | undefined;
|
|
26
|
+
export default sandboxSharingUrl;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/*
|
|
6
|
+
* Copyright (c) 2023-2026 Datalayer, Inc.
|
|
7
|
+
* Distributed under the terms of the Modified BSD License.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Where a sandbox's sharing lives.
|
|
11
|
+
*
|
|
12
|
+
* A sandbox is a runtime underneath, and Runtimes owns who it is shared with:
|
|
13
|
+
* `GET|PUT /api/runtimes/v1/runtimes/{name}/sharing`, answering an `access`
|
|
14
|
+
* document in exactly the shape `ShareAccessDialog` reads. So the dialog
|
|
15
|
+
* talks to Runtimes directly and there is no transport to write.
|
|
16
|
+
*
|
|
17
|
+
* The one thing worth a function of its own is **which name**. A sandbox has
|
|
18
|
+
* two: the handle its session holds (`sb_…`) and the runtime's own, and
|
|
19
|
+
* Runtimes' routes take the second. Sending the first gets a `403` that reads
|
|
20
|
+
* as "you are not the owner" — the confusion that made the CLI's sharing
|
|
21
|
+
* commands unusable until September 2026, and the reason this is written down
|
|
22
|
+
* once rather than spelled out at each call site.
|
|
23
|
+
*
|
|
24
|
+
* @module components/sharing/sandboxSharing
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* The sharing URL for a sandbox, or `undefined` when there is nothing to ask.
|
|
28
|
+
*
|
|
29
|
+
* Answers `undefined` rather than a broken URL for a sandbox with no runtime
|
|
30
|
+
* behind it — a browser or local kernel, or a binding still reserving — so a
|
|
31
|
+
* caller can use the answer itself to decide whether to offer sharing at all.
|
|
32
|
+
*/
|
|
33
|
+
export const sandboxSharingUrl = (runtimesUrl, runtimeName) => {
|
|
34
|
+
const base = String(runtimesUrl ?? '').trim();
|
|
35
|
+
const name = String(runtimeName ?? '').trim();
|
|
36
|
+
if (!base || !name) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return `${base.replace(/\/+$/, '')}/api/runtimes/v1/runtimes/${encodeURIComponent(name)}/sharing`;
|
|
40
|
+
};
|
|
41
|
+
export default sandboxSharingUrl;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A space's details: the card, and the overlay that opens it from a click on
|
|
3
|
+
* the space's name.
|
|
4
|
+
*
|
|
5
|
+
* Built the way {@link PrincipalDetailsOverlay} is — the name is the anchor,
|
|
6
|
+
* the card is shared — so a table's Owner and Space columns open the same kind
|
|
7
|
+
* of thing and read the same way.
|
|
8
|
+
*
|
|
9
|
+
* @module components/spaces/SpaceDetailsOverlay
|
|
10
|
+
*/
|
|
11
|
+
import type { JSX } from 'react';
|
|
12
|
+
export type SpaceDetailsOverlayProps = {
|
|
13
|
+
uid?: string;
|
|
14
|
+
displayName: string;
|
|
15
|
+
handle?: string;
|
|
16
|
+
accountHandle?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
variant?: string;
|
|
19
|
+
isPublic?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Show a route some other way than through the router of this subtree.
|
|
22
|
+
*
|
|
23
|
+
* The same escape hatch as the principal card's: an editor embedded under a
|
|
24
|
+
* `MemoryRouter` of its own hands the route to the view that has the
|
|
25
|
+
* application's routes. Left out, the route is followed with `useNavigate`.
|
|
26
|
+
*/
|
|
27
|
+
onNavigate?: (path: string) => void;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The card of a space, on its own — its mark, identity, and the way to it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function SpaceDetailsCard({ uid, displayName, handle, accountHandle, description, variant, isPublic, onNavigate, }: SpaceDetailsOverlayProps): JSX.Element;
|
|
33
|
+
/**
|
|
34
|
+
* A space's details, shown from a click on its name.
|
|
35
|
+
*
|
|
36
|
+
* The name is the anchor; clicking it opens the shared {@link SpaceDetailsCard}.
|
|
37
|
+
*/
|
|
38
|
+
export declare function SpaceDetailsOverlay(props: SpaceDetailsOverlayProps): JSX.Element;
|
|
39
|
+
export default SpaceDetailsOverlay;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { ActionMenu, Box, Button, Label, Text, ThemeProvider, } from '@primer/react';
|
|
3
|
+
import { SpaceIcon } from '@primer/octicons-react';
|
|
4
|
+
import { useNavigate } from '../../hooks/index.js';
|
|
5
|
+
import { buildSpaceDetailsPath } from './spaceDisplayModel.js';
|
|
6
|
+
function normalize(value) {
|
|
7
|
+
return (value || '').trim();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The card of a space, on its own — its mark, identity, and the way to it.
|
|
11
|
+
*/
|
|
12
|
+
export function SpaceDetailsCard({ uid, displayName, handle, accountHandle, description, variant, isPublic, onNavigate, }) {
|
|
13
|
+
const navigate = useNavigate();
|
|
14
|
+
const targetPath = buildSpaceDetailsPath({ accountHandle, handle });
|
|
15
|
+
const go = () => {
|
|
16
|
+
if (!targetPath) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (onNavigate) {
|
|
20
|
+
onNavigate(targetPath);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
navigate(targetPath);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const normalizedHandle = normalize(handle);
|
|
27
|
+
const normalizedAccount = normalize(accountHandle).replace(/^@+/, '');
|
|
28
|
+
const normalizedUid = normalize(uid);
|
|
29
|
+
const normalizedDisplayName = normalize(displayName) || normalizedHandle || normalizedUid || 'Space';
|
|
30
|
+
// The address the space is reached at, as it reads in the location bar.
|
|
31
|
+
const address = normalizedAccount && normalizedHandle
|
|
32
|
+
? `@${normalizedAccount}/${normalizedHandle}`
|
|
33
|
+
: normalizedHandle;
|
|
34
|
+
return (_jsxs(Box, { sx: { display: 'grid', gap: 3, p: 4, minWidth: 360 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Box, { "aria-hidden": "true", sx: {
|
|
35
|
+
display: 'inline-flex',
|
|
36
|
+
alignItems: 'center',
|
|
37
|
+
justifyContent: 'center',
|
|
38
|
+
width: 40,
|
|
39
|
+
height: 40,
|
|
40
|
+
flexShrink: 0,
|
|
41
|
+
borderRadius: 2,
|
|
42
|
+
bg: 'accent.subtle',
|
|
43
|
+
color: 'accent.fg',
|
|
44
|
+
}, children: _jsx(SpaceIcon, { size: 20 }) }), _jsxs(Box, { sx: { display: 'grid', gap: 0.5, minWidth: 0 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold' }, children: normalizedDisplayName }), address ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: address })) : null] })] }), _jsxs(Box, { sx: {
|
|
45
|
+
display: 'grid',
|
|
46
|
+
gridTemplateColumns: '110px 1fr',
|
|
47
|
+
gap: 1,
|
|
48
|
+
alignItems: 'baseline',
|
|
49
|
+
}, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Kind" }), _jsx(Text, { sx: { fontSize: 1 }, children: "space" }), normalizedHandle ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Handle" }), _jsx(Text, { sx: { fontSize: 1 }, children: normalizedHandle })] })) : null, normalizedAccount ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Account" }), _jsxs(Text, { sx: { fontSize: 1 }, children: ["@", normalizedAccount] })] })) : null, normalize(variant) ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Variant" }), _jsx(Text, { sx: { fontSize: 1 }, children: normalize(variant) })] })) : null, typeof isPublic === 'boolean' ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Visibility" }), _jsx(Label, { size: "small", variant: isPublic ? 'success' : 'secondary', sx: { justifySelf: 'start', width: 'fit-content' }, children: isPublic ? 'Public' : 'Private' })] })) : null, _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Description" }), _jsx(Text, { sx: { fontSize: 1 }, children: normalize(description) || 'N/A' }), normalizedUid ? (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "UID" }), _jsx(Text, { sx: { fontSize: 1 }, children: normalizedUid })] })) : null] }), _jsx(Box, { sx: { display: 'flex', justifyContent: 'flex-end', gap: 2 }, children: _jsx(Button, { size: "small", onClick: go, disabled: !targetPath, children: "View Space" }) })] }));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A space's details, shown from a click on its name.
|
|
53
|
+
*
|
|
54
|
+
* The name is the anchor; clicking it opens the shared {@link SpaceDetailsCard}.
|
|
55
|
+
*/
|
|
56
|
+
export function SpaceDetailsOverlay(props) {
|
|
57
|
+
const normalizedDisplayName = normalize(props.displayName) ||
|
|
58
|
+
normalize(props.handle) ||
|
|
59
|
+
normalize(props.uid) ||
|
|
60
|
+
'Space';
|
|
61
|
+
return (_jsx(ThemeProvider, { children: _jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Box, { as: "button", type: "button", title: normalizedDisplayName, sx: {
|
|
62
|
+
fontWeight: 'semibold',
|
|
63
|
+
color: 'accent.fg',
|
|
64
|
+
textDecoration: 'underline',
|
|
65
|
+
background: 'transparent',
|
|
66
|
+
border: 0,
|
|
67
|
+
padding: 0,
|
|
68
|
+
margin: 0,
|
|
69
|
+
cursor: 'pointer',
|
|
70
|
+
maxWidth: '100%',
|
|
71
|
+
overflow: 'hidden',
|
|
72
|
+
textOverflow: 'ellipsis',
|
|
73
|
+
whiteSpace: 'nowrap',
|
|
74
|
+
textAlign: 'left',
|
|
75
|
+
':hover': {
|
|
76
|
+
textDecoration: 'underline',
|
|
77
|
+
},
|
|
78
|
+
}, children: normalizedDisplayName }) }), _jsx(ActionMenu.Overlay, { width: "large", children: _jsx(SpaceDetailsCard, { ...props }) })] }) }));
|
|
79
|
+
}
|
|
80
|
+
export default SpaceDetailsOverlay;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpaceDisplay – common display for the space a thing lives in: the space's
|
|
3
|
+
* name, clickable, opening a {@link SpaceDetailsOverlay} whose button goes to
|
|
4
|
+
* the space's page.
|
|
5
|
+
*
|
|
6
|
+
* The companion of {@link Principal}, for a table's Space column, and built
|
|
7
|
+
* for a table's arithmetic: a hundred rows in three spaces cost no request per
|
|
8
|
+
* row. A space is found, in order:
|
|
9
|
+
*
|
|
10
|
+
* 1. among the spaces the account has already loaded (`useUserSpaces`, the
|
|
11
|
+
* query the item tables already run, children included — which is how a
|
|
12
|
+
* row that knows only its item still finds its space);
|
|
13
|
+
* 2. in the in-memory {@link useSpaceCacheStore}, which paints a space that
|
|
14
|
+
* any earlier view resolved;
|
|
15
|
+
* 3. over the network by uid (`useSpace`) — only once the loaded list has
|
|
16
|
+
* answered without it, and every row naming that uid shares the request.
|
|
17
|
+
*
|
|
18
|
+
* @module components/spaces/SpaceDisplay
|
|
19
|
+
*/
|
|
20
|
+
import * as React from 'react';
|
|
21
|
+
import { type SpaceDescriptor } from './spaceDisplayModel';
|
|
22
|
+
export type SpaceDisplayProps = {
|
|
23
|
+
/** Whatever the row knows of its space; see {@link SpaceDescriptor}. */
|
|
24
|
+
space: SpaceDescriptor;
|
|
25
|
+
/** Whether the space mark precedes the name. */
|
|
26
|
+
showIcon?: boolean;
|
|
27
|
+
iconSize?: number;
|
|
28
|
+
gap?: number;
|
|
29
|
+
/** Maximum length of the handle or uid shown before the name resolves. */
|
|
30
|
+
placeholderMaxLength?: number;
|
|
31
|
+
/** What is shown when the row names no space at all. */
|
|
32
|
+
emptyText?: string;
|
|
33
|
+
/** See {@link SpaceDetailsOverlayProps.onNavigate}. */
|
|
34
|
+
onNavigate?: (path: string) => void;
|
|
35
|
+
sx?: any;
|
|
36
|
+
};
|
|
37
|
+
export declare const SpaceDisplay: React.FC<SpaceDisplayProps>;
|
|
38
|
+
export default SpaceDisplay;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* SpaceDisplay – common display for the space a thing lives in: the space's
|
|
8
|
+
* name, clickable, opening a {@link SpaceDetailsOverlay} whose button goes to
|
|
9
|
+
* the space's page.
|
|
10
|
+
*
|
|
11
|
+
* The companion of {@link Principal}, for a table's Space column, and built
|
|
12
|
+
* for a table's arithmetic: a hundred rows in three spaces cost no request per
|
|
13
|
+
* row. A space is found, in order:
|
|
14
|
+
*
|
|
15
|
+
* 1. among the spaces the account has already loaded (`useUserSpaces`, the
|
|
16
|
+
* query the item tables already run, children included — which is how a
|
|
17
|
+
* row that knows only its item still finds its space);
|
|
18
|
+
* 2. in the in-memory {@link useSpaceCacheStore}, which paints a space that
|
|
19
|
+
* any earlier view resolved;
|
|
20
|
+
* 3. over the network by uid (`useSpace`) — only once the loaded list has
|
|
21
|
+
* answered without it, and every row naming that uid shares the request.
|
|
22
|
+
*
|
|
23
|
+
* @module components/spaces/SpaceDisplay
|
|
24
|
+
*/
|
|
25
|
+
import * as React from 'react';
|
|
26
|
+
import { Text } from '@primer/react';
|
|
27
|
+
import { SpaceIcon } from '@primer/octicons-react';
|
|
28
|
+
import { Box } from '@datalayer/primer-addons';
|
|
29
|
+
import { useCache } from '../../hooks/index.js';
|
|
30
|
+
import { useSpaceCacheStore } from '../../hooks/useSpaceCacheStore.js';
|
|
31
|
+
import { useIAMStore } from '../../state/index.js';
|
|
32
|
+
import { SpaceDetailsOverlay } from './SpaceDetailsOverlay.js';
|
|
33
|
+
import { findSpace, indexSpaces, resolveSpace, toCachedSpace, } from './spaceDisplayModel.js';
|
|
34
|
+
export const SpaceDisplay = ({ space, showIcon = true, iconSize = 16, gap = 1, placeholderMaxLength, emptyText = '—', onNavigate, sx, }) => {
|
|
35
|
+
const { uid, rootId, itemUid, handle, accountHandle, displayName } = space;
|
|
36
|
+
const { useUserSpaces, useSpace } = useCache();
|
|
37
|
+
const { user } = useIAMStore();
|
|
38
|
+
// 1. The spaces already loaded, indexed once per answer of the query.
|
|
39
|
+
const spacesQuery = useUserSpaces();
|
|
40
|
+
const index = React.useMemo(() => indexSpaces(spacesQuery.data), [spacesQuery.data]);
|
|
41
|
+
const listed = React.useMemo(() => toCachedSpace(findSpace(index, { uid, rootId, itemUid, handle, accountHandle })), [index, uid, rootId, itemUid, handle, accountHandle]);
|
|
42
|
+
// 2. A snapshot some earlier view resolved.
|
|
43
|
+
const givenUid = String(uid || '').trim();
|
|
44
|
+
const givenRootId = String(rootId || '').trim();
|
|
45
|
+
const cached = useSpaceCacheStore(state => {
|
|
46
|
+
const key = givenUid ||
|
|
47
|
+
listed?.uid ||
|
|
48
|
+
(givenRootId ? state.uidByRootId[givenRootId] : '');
|
|
49
|
+
return key ? state.spaces[key] : undefined;
|
|
50
|
+
});
|
|
51
|
+
const upsertSpace = useSpaceCacheStore(state => state.upsertSpace);
|
|
52
|
+
// 3. The network, last, and not while the list that may know it is loading.
|
|
53
|
+
const fetchUid = user &&
|
|
54
|
+
givenUid &&
|
|
55
|
+
!spacesQuery.isLoading &&
|
|
56
|
+
!listed &&
|
|
57
|
+
!cached?.displayName
|
|
58
|
+
? givenUid
|
|
59
|
+
: '';
|
|
60
|
+
const fetchedQuery = useSpace(fetchUid);
|
|
61
|
+
const fetched = React.useMemo(() => toCachedSpace(fetchedQuery.data), [fetchedQuery.data]);
|
|
62
|
+
const found = listed ?? fetched;
|
|
63
|
+
const resolved = resolveSpace({
|
|
64
|
+
descriptor: space,
|
|
65
|
+
found,
|
|
66
|
+
cached,
|
|
67
|
+
placeholderMaxLength,
|
|
68
|
+
});
|
|
69
|
+
// Remember what was actually resolved — never a placeholder — so the next
|
|
70
|
+
// view paints it at once.
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
if (found?.uid) {
|
|
73
|
+
upsertSpace(found);
|
|
74
|
+
}
|
|
75
|
+
}, [found, upsertSpace]);
|
|
76
|
+
const canName = Boolean(resolved.uid || resolved.handle || String(displayName || '').trim());
|
|
77
|
+
if (!canName) {
|
|
78
|
+
const waiting = Boolean(user) &&
|
|
79
|
+
spacesQuery.isLoading &&
|
|
80
|
+
Boolean(givenRootId || String(itemUid || '').trim());
|
|
81
|
+
return (_jsx(Text, { sx: { color: 'fg.muted', ...sx }, children: waiting ? '…' : emptyText }));
|
|
82
|
+
}
|
|
83
|
+
return (_jsxs(Box, { sx: {
|
|
84
|
+
display: 'inline-flex',
|
|
85
|
+
alignItems: 'center',
|
|
86
|
+
gap,
|
|
87
|
+
minWidth: 0,
|
|
88
|
+
maxWidth: '100%',
|
|
89
|
+
...sx,
|
|
90
|
+
}, children: [showIcon ? (_jsx(Box, { as: "span", "aria-hidden": "true", sx: { display: 'inline-flex', color: 'fg.muted', flexShrink: 0 }, children: _jsx(SpaceIcon, { size: iconSize }) })) : null, _jsx(SpaceDetailsOverlay, { uid: resolved.uid, displayName: resolved.displayName, handle: resolved.handle, accountHandle: resolved.accountHandle, description: resolved.description, variant: resolved.variant, isPublic: resolved.isPublic, onNavigate: onNavigate })] }));
|
|
91
|
+
};
|
|
92
|
+
export default SpaceDisplay;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
export * from './SpaceDisplay.js';
|
|
6
|
+
export { SpaceDetailsCard, SpaceDetailsOverlay } from './SpaceDetailsOverlay.js';
|
|
7
|
+
export * from './spaceDisplayModel.js';
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a space display needs to know, said without rendering anything.
|
|
3
|
+
*
|
|
4
|
+
* Apart from the components so the rules — how a row names its space, how a
|
|
5
|
+
* space is found among the ones already loaded, where its page is — can be
|
|
6
|
+
* tested without a DOM, a router or a query client.
|
|
7
|
+
*
|
|
8
|
+
* @module components/spaces/spaceDisplayModel
|
|
9
|
+
*/
|
|
10
|
+
import type { CachedSpace } from '../../hooks/useSpaceCacheStore';
|
|
11
|
+
/**
|
|
12
|
+
* How a row names the space it belongs to — whatever it happens to know.
|
|
13
|
+
*
|
|
14
|
+
* Tables reach their rows by different roads, and each road knows something
|
|
15
|
+
* different: a table scoped to one space knows that space's uid; a row from
|
|
16
|
+
* the cross-space listing knows only its item, which carries its space's Solr
|
|
17
|
+
* id as `_root_`; a dataset knows its `spaceUid`. Any one of them is enough.
|
|
18
|
+
*/
|
|
19
|
+
export type SpaceDescriptor = {
|
|
20
|
+
/** The space uid. */
|
|
21
|
+
uid?: string | null;
|
|
22
|
+
/** The space's Solr id, which an item carries as `_root_`. */
|
|
23
|
+
rootId?: string | null;
|
|
24
|
+
/** The uid of an item in the space, found among the loaded spaces' children. */
|
|
25
|
+
itemUid?: string | null;
|
|
26
|
+
/**
|
|
27
|
+
* The space handle. Handles repeat across accounts — everybody has a
|
|
28
|
+
* `library` — so it is matched together with `accountHandle`, and only
|
|
29
|
+
* when no uid was given.
|
|
30
|
+
*/
|
|
31
|
+
handle?: string | null;
|
|
32
|
+
/** The account the space is addressed under. */
|
|
33
|
+
accountHandle?: string | null;
|
|
34
|
+
/** A name the row already has, shown until the space is resolved. */
|
|
35
|
+
displayName?: string | null;
|
|
36
|
+
};
|
|
37
|
+
/** Loaded spaces, looked up every way a row can name one. */
|
|
38
|
+
export type SpaceIndex = {
|
|
39
|
+
byUid: ReadonlyMap<string, unknown>;
|
|
40
|
+
byRootId: ReadonlyMap<string, unknown>;
|
|
41
|
+
byItemUid: ReadonlyMap<string, unknown>;
|
|
42
|
+
byAccountAndHandle: ReadonlyMap<string, unknown>;
|
|
43
|
+
};
|
|
44
|
+
/** A space as a display shows it: the cached fields, a name, and its page. */
|
|
45
|
+
export type ResolvedSpace = Omit<CachedSpace, 'uid' | 'updatedAt'> & {
|
|
46
|
+
uid?: string;
|
|
47
|
+
/** The space's name, or a placeholder derived from its handle or uid. */
|
|
48
|
+
displayName: string;
|
|
49
|
+
/** Where the space's page is, when both handles are known. */
|
|
50
|
+
path: string | null;
|
|
51
|
+
/** Whether the name is the space's own rather than a placeholder. */
|
|
52
|
+
resolved: boolean;
|
|
53
|
+
};
|
|
54
|
+
/** The uid of a space as the spacer API returns it, raw or converted. */
|
|
55
|
+
export declare const spaceUidOf: (space: any) => string;
|
|
56
|
+
/**
|
|
57
|
+
* The account a space is addressed under: its organization, else its owner.
|
|
58
|
+
*
|
|
59
|
+
* The same order the web application's `resolveSpaceAccountHandle` reads, so
|
|
60
|
+
* a link built here and a link built there go to the same page.
|
|
61
|
+
*/
|
|
62
|
+
export declare const spaceAccountHandleOf: (space: any) => string;
|
|
63
|
+
/** A space as the cache keeps it, out of a spacer payload, raw or converted. */
|
|
64
|
+
export declare const toCachedSpace: (space: unknown) => CachedSpace | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* Index loaded spaces by uid, Solr id, the uids of their items, and
|
|
67
|
+
* account/handle.
|
|
68
|
+
*
|
|
69
|
+
* Memoised on the array: a query answers with the same array until it
|
|
70
|
+
* refetches, so a table of a hundred rows builds the index once, not a hundred
|
|
71
|
+
* times.
|
|
72
|
+
*/
|
|
73
|
+
export declare const indexSpaces: (spaces: readonly unknown[] | null | undefined) => SpaceIndex;
|
|
74
|
+
/** The loaded space a descriptor names, if any. */
|
|
75
|
+
export declare const findSpace: (index: SpaceIndex, descriptor: SpaceDescriptor) => unknown | undefined;
|
|
76
|
+
/** Where a space's page is: `/<account>/<space>`, or nothing without both. */
|
|
77
|
+
export declare const buildSpaceDetailsPath: ({ accountHandle, handle, }: {
|
|
78
|
+
accountHandle?: string | null;
|
|
79
|
+
handle?: string | null;
|
|
80
|
+
}) => string | null;
|
|
81
|
+
/**
|
|
82
|
+
* What a display shows for a descriptor, given what is known of its space.
|
|
83
|
+
*
|
|
84
|
+
* The space's own fields win over the snapshot remembered from an earlier
|
|
85
|
+
* view, which win over what the row said; the row's name is shown only until
|
|
86
|
+
* the space's own is known, and a handle or uid stands in before either.
|
|
87
|
+
*/
|
|
88
|
+
export declare const resolveSpace: ({ descriptor, found, cached, placeholderMaxLength, }: {
|
|
89
|
+
descriptor: SpaceDescriptor;
|
|
90
|
+
found?: CachedSpace;
|
|
91
|
+
cached?: CachedSpace;
|
|
92
|
+
placeholderMaxLength?: number;
|
|
93
|
+
}) => ResolvedSpace;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_PLACEHOLDER_MAX_LENGTH = 12;
|
|
6
|
+
const text = (value) => typeof value === 'string'
|
|
7
|
+
? value.trim()
|
|
8
|
+
: value === null || value === undefined
|
|
9
|
+
? ''
|
|
10
|
+
: String(value).trim();
|
|
11
|
+
const bareHandle = (value) => text(value).replace(/^@+/, '');
|
|
12
|
+
const firstText = (...values) => {
|
|
13
|
+
for (const value of values) {
|
|
14
|
+
const candidate = text(value);
|
|
15
|
+
if (candidate) {
|
|
16
|
+
return candidate;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return '';
|
|
20
|
+
};
|
|
21
|
+
const truncate = (value, maxLength) => maxLength > 0 && value.length > maxLength
|
|
22
|
+
? `${value.slice(0, maxLength)}…`
|
|
23
|
+
: value;
|
|
24
|
+
/** The uid of a space as the spacer API returns it, raw or converted. */
|
|
25
|
+
export const spaceUidOf = (space) => firstText(space?.uid, space?.uid_s, space?.id);
|
|
26
|
+
/**
|
|
27
|
+
* The account a space is addressed under: its organization, else its owner.
|
|
28
|
+
*
|
|
29
|
+
* The same order the web application's `resolveSpaceAccountHandle` reads, so
|
|
30
|
+
* a link built here and a link built there go to the same page.
|
|
31
|
+
*/
|
|
32
|
+
export const spaceAccountHandleOf = (space) => bareHandle(firstText(space?.accountHandle, space?.organization?.handle, space?.organization?.handle_s, space?.organization_handle, space?.organization_handle_s, space?.account_handle, space?.account_handle_s, space?.owner?.handle, space?.owner?.handle_s, space?.owner_handle, space?.owner_handle_s));
|
|
33
|
+
/** A space as the cache keeps it, out of a spacer payload, raw or converted. */
|
|
34
|
+
export const toCachedSpace = (space) => {
|
|
35
|
+
const raw = space;
|
|
36
|
+
const uid = spaceUidOf(raw);
|
|
37
|
+
if (!uid) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
const isPublic = raw?.public_b ?? raw?.public;
|
|
41
|
+
return {
|
|
42
|
+
uid,
|
|
43
|
+
rootId: text(raw?.id) || undefined,
|
|
44
|
+
handle: firstText(raw?.handle_s, raw?.handle) || undefined,
|
|
45
|
+
displayName: firstText(raw?.name_t, raw?.name) || undefined,
|
|
46
|
+
description: firstText(raw?.description_t, raw?.description) || undefined,
|
|
47
|
+
variant: firstText(raw?.variant_s, raw?.variant) || undefined,
|
|
48
|
+
isPublic: typeof isPublic === 'boolean' ? isPublic : undefined,
|
|
49
|
+
accountHandle: spaceAccountHandleOf(raw) || undefined,
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
const EMPTY_SPACES = Object.freeze([]);
|
|
53
|
+
const INDEXES = new WeakMap();
|
|
54
|
+
/**
|
|
55
|
+
* Index loaded spaces by uid, Solr id, the uids of their items, and
|
|
56
|
+
* account/handle.
|
|
57
|
+
*
|
|
58
|
+
* Memoised on the array: a query answers with the same array until it
|
|
59
|
+
* refetches, so a table of a hundred rows builds the index once, not a hundred
|
|
60
|
+
* times.
|
|
61
|
+
*/
|
|
62
|
+
export const indexSpaces = (spaces) => {
|
|
63
|
+
const list = Array.isArray(spaces) ? spaces : EMPTY_SPACES;
|
|
64
|
+
const known = INDEXES.get(list);
|
|
65
|
+
if (known) {
|
|
66
|
+
return known;
|
|
67
|
+
}
|
|
68
|
+
const byUid = new Map();
|
|
69
|
+
const byRootId = new Map();
|
|
70
|
+
const byItemUid = new Map();
|
|
71
|
+
const byAccountAndHandle = new Map();
|
|
72
|
+
for (const space of list) {
|
|
73
|
+
const raw = space;
|
|
74
|
+
const uid = spaceUidOf(raw);
|
|
75
|
+
if (!uid) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
byUid.set(uid, space);
|
|
79
|
+
const rootId = text(raw?.id);
|
|
80
|
+
if (rootId) {
|
|
81
|
+
byRootId.set(rootId, space);
|
|
82
|
+
}
|
|
83
|
+
const handle = firstText(raw?.handle_s, raw?.handle);
|
|
84
|
+
const account = spaceAccountHandleOf(raw);
|
|
85
|
+
if (handle && account) {
|
|
86
|
+
byAccountAndHandle.set(`${account}/${handle}`, space);
|
|
87
|
+
}
|
|
88
|
+
// The spaces listing carries each space's items as children, which is the
|
|
89
|
+
// only place a row from the cross-space listing can learn its space from
|
|
90
|
+
// when it did not keep `_root_`.
|
|
91
|
+
if (Array.isArray(raw?.items)) {
|
|
92
|
+
for (const item of raw.items) {
|
|
93
|
+
const itemUid = text(item?.uid);
|
|
94
|
+
if (itemUid) {
|
|
95
|
+
byItemUid.set(itemUid, space);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const index = {
|
|
101
|
+
byUid,
|
|
102
|
+
byRootId,
|
|
103
|
+
byItemUid,
|
|
104
|
+
byAccountAndHandle,
|
|
105
|
+
};
|
|
106
|
+
INDEXES.set(list, index);
|
|
107
|
+
return index;
|
|
108
|
+
};
|
|
109
|
+
/** The loaded space a descriptor names, if any. */
|
|
110
|
+
export const findSpace = (index, descriptor) => {
|
|
111
|
+
const uid = text(descriptor.uid);
|
|
112
|
+
if (uid && index.byUid.has(uid)) {
|
|
113
|
+
return index.byUid.get(uid);
|
|
114
|
+
}
|
|
115
|
+
const rootId = text(descriptor.rootId);
|
|
116
|
+
if (rootId && index.byRootId.has(rootId)) {
|
|
117
|
+
return index.byRootId.get(rootId);
|
|
118
|
+
}
|
|
119
|
+
const itemUid = text(descriptor.itemUid);
|
|
120
|
+
if (itemUid && index.byItemUid.has(itemUid)) {
|
|
121
|
+
return index.byItemUid.get(itemUid);
|
|
122
|
+
}
|
|
123
|
+
// A uid is authoritative: when one was given and is not loaded, a space
|
|
124
|
+
// that merely shares its handle is not the same space.
|
|
125
|
+
const handle = text(descriptor.handle);
|
|
126
|
+
const account = bareHandle(descriptor.accountHandle);
|
|
127
|
+
if (!uid && handle && account) {
|
|
128
|
+
return index.byAccountAndHandle.get(`${account}/${handle}`);
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
};
|
|
132
|
+
/** Where a space's page is: `/<account>/<space>`, or nothing without both. */
|
|
133
|
+
export const buildSpaceDetailsPath = ({ accountHandle, handle, }) => {
|
|
134
|
+
const account = bareHandle(accountHandle);
|
|
135
|
+
const space = text(handle);
|
|
136
|
+
if (!account || !space) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return `/${encodeURIComponent(account)}/${encodeURIComponent(space)}`;
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* What a display shows for a descriptor, given what is known of its space.
|
|
143
|
+
*
|
|
144
|
+
* The space's own fields win over the snapshot remembered from an earlier
|
|
145
|
+
* view, which win over what the row said; the row's name is shown only until
|
|
146
|
+
* the space's own is known, and a handle or uid stands in before either.
|
|
147
|
+
*/
|
|
148
|
+
export const resolveSpace = ({ descriptor, found, cached, placeholderMaxLength = DEFAULT_PLACEHOLDER_MAX_LENGTH, }) => {
|
|
149
|
+
const uid = firstText(descriptor.uid, found?.uid, cached?.uid) || undefined;
|
|
150
|
+
const handle = firstText(found?.handle, cached?.handle, descriptor.handle) || undefined;
|
|
151
|
+
const accountHandle = bareHandle(firstText(found?.accountHandle, cached?.accountHandle, descriptor.accountHandle)) || undefined;
|
|
152
|
+
const ownName = firstText(found?.displayName, cached?.displayName);
|
|
153
|
+
const displayName = ownName ||
|
|
154
|
+
text(descriptor.displayName) ||
|
|
155
|
+
truncate(firstText(handle, uid), placeholderMaxLength) ||
|
|
156
|
+
'Space';
|
|
157
|
+
const isPublic = typeof found?.isPublic === 'boolean' ? found.isPublic : cached?.isPublic;
|
|
158
|
+
return {
|
|
159
|
+
uid,
|
|
160
|
+
rootId: firstText(found?.rootId, cached?.rootId, descriptor.rootId) || undefined,
|
|
161
|
+
handle,
|
|
162
|
+
accountHandle,
|
|
163
|
+
displayName,
|
|
164
|
+
description: firstText(found?.description, cached?.description) || undefined,
|
|
165
|
+
variant: firstText(found?.variant, cached?.variant) || undefined,
|
|
166
|
+
isPublic,
|
|
167
|
+
path: buildSpaceDetailsPath({ accountHandle, handle }),
|
|
168
|
+
resolved: Boolean(ownName),
|
|
169
|
+
};
|
|
170
|
+
};
|
|
@@ -5,13 +5,6 @@
|
|
|
5
5
|
* @returns The converted snapshot object.
|
|
6
6
|
*/
|
|
7
7
|
export declare function asCodeSandboxSnapshot(s: any): any;
|
|
8
|
-
/**
|
|
9
|
-
* Convert a raw page payload into its client shape.
|
|
10
|
-
*
|
|
11
|
-
* @param s - The raw page payload from the API.
|
|
12
|
-
* @returns The converted page object.
|
|
13
|
-
*/
|
|
14
|
-
export declare const asPage: (s: any) => any;
|
|
15
8
|
/**
|
|
16
9
|
* Convert a raw space payload into its client shape.
|
|
17
10
|
*
|