@datalayer/core 1.1.49 → 1.1.56
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 +19 -42
- package/lib/client/auth/AuthenticationManager.js +6 -4
- package/lib/client/auth/strategies.js +2 -0
- package/lib/components/animation/AnimatedText.d.ts +98 -0
- package/lib/components/animation/AnimatedText.js +220 -0
- package/lib/components/animation/index.d.ts +1 -0
- package/lib/components/{tokens → animation}/index.js +1 -1
- package/lib/components/auth/{LoginToken.d.ts → SigInAPIKey.d.ts} +3 -3
- package/lib/components/auth/{LoginToken.js → SigInAPIKey.js} +5 -5
- package/lib/components/auth/{Login.d.ts → SignIn.d.ts} +3 -3
- package/lib/components/auth/{Login.js → SignIn.js} +13 -13
- package/lib/components/auth/{Login.stories.d.ts → SignIn.stories.d.ts} +2 -2
- package/lib/components/auth/{Login.stories.js → SignIn.stories.js} +9 -9
- package/lib/components/auth/index.d.ts +2 -2
- package/lib/components/auth/index.js +2 -2
- package/lib/components/avatars/UserAvatar.d.ts +7 -1
- package/lib/components/avatars/UserAvatar.js +19 -6
- package/lib/components/avatars/UserProfileAvatar.js +2 -6
- package/lib/components/billing/BillingEntitySelect.js +10 -2
- package/lib/components/collaboration/LiveEditorCollaborators.d.ts +31 -0
- package/lib/components/collaboration/LiveEditorCollaborators.js +107 -0
- package/lib/components/collaboration/index.d.ts +1 -0
- package/lib/components/collaboration/index.js +9 -0
- package/lib/components/display/DatalayerBox.d.ts +3 -1
- package/lib/components/display/DatalayerBox.js +7 -7
- package/lib/components/display/JupyterDialog.d.ts +2 -0
- package/lib/components/display/JupyterDialog.js +10 -4
- package/lib/components/iam/ExternalTokenSilentLogin.js +8 -4
- package/lib/components/index.d.ts +2 -0
- package/lib/components/index.js +2 -0
- package/lib/components/labels/StatusLabels.d.ts +42 -0
- package/lib/components/labels/StatusLabels.js +61 -0
- package/lib/components/labels/index.d.ts +1 -0
- package/lib/components/labels/index.js +1 -0
- package/lib/components/principal/Principal.d.ts +2 -0
- package/lib/components/principal/Principal.js +11 -1
- package/lib/components/principal/PrincipalAppearance.d.ts +136 -0
- package/lib/components/principal/PrincipalAppearance.js +203 -0
- package/lib/components/principal/PrincipalAvatar.d.ts +5 -2
- package/lib/components/principal/PrincipalAvatar.js +16 -3
- package/lib/components/principal/PrincipalBadge.js +7 -0
- package/lib/components/principal/PrincipalDetailsOverlay.d.ts +2 -1
- package/lib/components/principal/PrincipalDetailsOverlay.js +2 -2
- package/lib/components/principal/index.d.ts +1 -0
- package/lib/components/principal/index.js +1 -0
- package/lib/components/progress/ConsumptionBar.d.ts +9 -0
- package/lib/components/progress/ConsumptionBar.js +10 -2
- package/lib/components/sharing/ShareAccessComponent.js +6 -1
- package/lib/config/Configuration.d.ts +17 -8
- package/lib/config/Configuration.js +12 -5
- package/lib/hooks/useCache.d.ts +2 -0
- package/lib/hooks/useCache.js +35 -11
- package/lib/hooks/usePrincipalCacheStore.d.ts +2 -0
- package/lib/hooks/usePrincipalCacheStore.js +2 -0
- package/lib/models/IAM.d.ts +10 -0
- package/lib/models/Organization.d.ts +2 -0
- package/lib/models/Organization.js +2 -0
- package/lib/models/Profile.d.ts +2 -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 +4 -0
- package/lib/models/UserDTO.d.ts +6 -0
- package/lib/models/UserDTO.js +8 -0
- package/lib/state/substates/CoreState.js +5 -4
- package/lib/state/substates/IAMState.js +6 -0
- package/lib/state/substates/LayoutState.js +62 -7
- package/lib/state/substates/OrganizationState.d.ts +6 -0
- package/lib/state/substates/OrganizationState.js +5 -0
- package/lib/state/substates/ProfileState.d.ts +34 -0
- package/lib/state/substates/ProfileState.js +68 -0
- package/lib/state/substates/TeamState.d.ts +6 -0
- package/lib/state/substates/TeamState.js +3 -0
- package/lib/state/substates/index.d.ts +1 -0
- package/lib/state/substates/index.js +1 -0
- package/lib/utils/Jwt.d.ts +0 -6
- package/lib/utils/Jwt.js +8 -1
- package/lib/views/iam/SignInSimple.d.ts +1 -1
- package/lib/views/iam/SignInSimple.js +131 -97
- package/lib/views/profile/UserBadge.js +6 -3
- package/package.json +3 -3
- package/lib/components/tokens/index.d.ts +0 -1
|
@@ -7,12 +7,6 @@ export declare const FORCE_ACTIVATE_RUNTIMES_PLUGINS = false;
|
|
|
7
7
|
* and interact with the Datalayer platform services.
|
|
8
8
|
*/
|
|
9
9
|
export type IDatalayerCoreConfig = {
|
|
10
|
-
/**
|
|
11
|
-
* Datalayer RUN URL.
|
|
12
|
-
* The base URL for the Datalayer platform API.
|
|
13
|
-
* @example "https://prod1.datalayer.run"
|
|
14
|
-
*/
|
|
15
|
-
datalayerUrl: string;
|
|
16
10
|
/**
|
|
17
11
|
* Datalayer API authentication token.
|
|
18
12
|
* Used for authenticating requests to the Datalayer platform.
|
|
@@ -47,10 +41,18 @@ export type IDatalayerCoreConfig = {
|
|
|
47
41
|
* IAM API URL.
|
|
48
42
|
*/
|
|
49
43
|
iamUrl: string;
|
|
44
|
+
/**
|
|
45
|
+
* Manager API URL.
|
|
46
|
+
*/
|
|
47
|
+
managerUrl: string;
|
|
50
48
|
/**
|
|
51
49
|
* Runtimes API URL.
|
|
52
50
|
*/
|
|
53
51
|
runtimesUrl: string;
|
|
52
|
+
/**
|
|
53
|
+
* Scheduler API URL.
|
|
54
|
+
*/
|
|
55
|
+
schedulerUrl: string;
|
|
54
56
|
/**
|
|
55
57
|
* Spacer API URL.
|
|
56
58
|
*/
|
|
@@ -68,9 +70,9 @@ export type IDatalayerCoreConfig = {
|
|
|
68
70
|
*/
|
|
69
71
|
aiInferenceUrl: string;
|
|
70
72
|
/**
|
|
71
|
-
* MCP
|
|
73
|
+
* Jupyter MCP Server URL.
|
|
72
74
|
*/
|
|
73
|
-
|
|
75
|
+
jupyterMcpServerUrl: string;
|
|
74
76
|
/**
|
|
75
77
|
* OTEL (OpenTelemetry) API URL.
|
|
76
78
|
*
|
|
@@ -175,6 +177,13 @@ export declare class DatalayerConfiguration {
|
|
|
175
177
|
/**
|
|
176
178
|
* Default configuration values for Datalayer
|
|
177
179
|
*/
|
|
180
|
+
/**
|
|
181
|
+
* Where a service lives when nothing says otherwise.
|
|
182
|
+
*
|
|
183
|
+
* Each service has a URL of its own — `iamUrl`, `runtimesUrl`, … — and this is
|
|
184
|
+
* what each of them falls back to.
|
|
185
|
+
*/
|
|
186
|
+
export declare const DEFAULT_DATALAYER_SERVICE_URL = "https://prod1.datalayer.run";
|
|
178
187
|
export declare const DEFAULT_DATALAYER_CONFIG: Partial<IDatalayerCoreConfig>;
|
|
179
188
|
/**
|
|
180
189
|
* Type guard to check if a config object is a valid IDatalayerConfig
|
|
@@ -25,8 +25,15 @@ export class DatalayerConfiguration {
|
|
|
25
25
|
/**
|
|
26
26
|
* Default configuration values for Datalayer
|
|
27
27
|
*/
|
|
28
|
+
/**
|
|
29
|
+
* Where a service lives when nothing says otherwise.
|
|
30
|
+
*
|
|
31
|
+
* Each service has a URL of its own — `iamUrl`, `runtimesUrl`, … — and this is
|
|
32
|
+
* what each of them falls back to.
|
|
33
|
+
*/
|
|
34
|
+
export const DEFAULT_DATALAYER_SERVICE_URL = 'https://prod1.datalayer.run';
|
|
28
35
|
export const DEFAULT_DATALAYER_CONFIG = {
|
|
29
|
-
|
|
36
|
+
iamUrl: DEFAULT_DATALAYER_SERVICE_URL,
|
|
30
37
|
credits: 100,
|
|
31
38
|
cpuEnvironment: 'ai-agents-env',
|
|
32
39
|
gpuEnvironment: 'ai-env',
|
|
@@ -37,7 +44,7 @@ export const DEFAULT_DATALAYER_CONFIG = {
|
|
|
37
44
|
export function isDatalayerConfig(config) {
|
|
38
45
|
return (config &&
|
|
39
46
|
typeof config === 'object' &&
|
|
40
|
-
typeof config.
|
|
47
|
+
typeof config.iamUrl === 'string' &&
|
|
41
48
|
typeof config.token === 'string' &&
|
|
42
49
|
typeof config.credits === 'number' &&
|
|
43
50
|
typeof config.cpuEnvironment === 'string' &&
|
|
@@ -51,10 +58,10 @@ export function isDatalayerConfig(config) {
|
|
|
51
58
|
export function mergeConfigWithDefaults(config) {
|
|
52
59
|
if (!config)
|
|
53
60
|
return undefined;
|
|
54
|
-
// If we have required fields (token and
|
|
55
|
-
if (config.token && config.
|
|
61
|
+
// If we have required fields (token and iamUrl), merge with defaults for optional fields
|
|
62
|
+
if (config.token && config.iamUrl) {
|
|
56
63
|
return {
|
|
57
|
-
|
|
64
|
+
iamUrl: config.iamUrl,
|
|
58
65
|
token: config.token,
|
|
59
66
|
credits: config.credits ?? DEFAULT_DATALAYER_CONFIG.credits,
|
|
60
67
|
cpuEnvironment: config.cpuEnvironment ?? DEFAULT_DATALAYER_CONFIG.cpuEnvironment,
|
package/lib/hooks/useCache.d.ts
CHANGED
|
@@ -270,6 +270,8 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
|
|
|
270
270
|
email: string;
|
|
271
271
|
firstName: string;
|
|
272
272
|
lastName: string;
|
|
273
|
+
avatarIcon?: string;
|
|
274
|
+
banner?: string;
|
|
273
275
|
}, unknown>;
|
|
274
276
|
useWhoami: () => import("@tanstack/react-query").UseQueryResult<any, Error>;
|
|
275
277
|
useChangePassword: () => import("@tanstack/react-query").UseMutationResult<any, Error, {
|
package/lib/hooks/useCache.js
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
import { useQuery, useQueries, useMutation, useQueryClient, } from '@tanstack/react-query';
|
|
40
40
|
import { BOOTSTRAP_USER_ONBOARDING, asContact, asDatasource, asInvite, asOrganization, asSecret, asTeam, asToken, asUser, } from '../models/index.js';
|
|
41
41
|
import { asPage, asSpace } from './cacheConverters.js';
|
|
42
|
-
import { useCoreStore, useIAMStore } from '../state/index.js';
|
|
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';
|
|
@@ -977,7 +977,11 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
977
977
|
token,
|
|
978
978
|
});
|
|
979
979
|
if (resp.me) {
|
|
980
|
-
|
|
980
|
+
const me = toUser(resp.me);
|
|
981
|
+
// The full profile — banner and avatar included — feeds the
|
|
982
|
+
// profile store every surface reads.
|
|
983
|
+
profileStore.getState().setProfileFromUser(me);
|
|
984
|
+
return me;
|
|
981
985
|
}
|
|
982
986
|
return null;
|
|
983
987
|
},
|
|
@@ -989,15 +993,18 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
989
993
|
*/
|
|
990
994
|
const useUpdateMe = () => {
|
|
991
995
|
return useMutation({
|
|
992
|
-
mutationFn: async ({ email, firstName, lastName, }) => {
|
|
996
|
+
mutationFn: async ({ email, firstName, lastName, avatarIcon, banner, }) => {
|
|
993
997
|
return requestDatalayer({
|
|
994
998
|
url: `${configuration.iamUrl}/api/iam/v1/me`,
|
|
995
999
|
method: 'PUT',
|
|
996
|
-
body: { email, firstName, lastName },
|
|
1000
|
+
body: { email, firstName, lastName, avatarIcon, banner },
|
|
997
1001
|
});
|
|
998
1002
|
},
|
|
999
1003
|
onSuccess: () => {
|
|
1000
1004
|
queryClient.invalidateQueries({ queryKey: queryKeys.auth.me() });
|
|
1005
|
+
// Every cached view of the user — the public profile page, the
|
|
1006
|
+
// admin list and detail, the search seeds — is now out of date.
|
|
1007
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.users.all() });
|
|
1001
1008
|
},
|
|
1002
1009
|
});
|
|
1003
1010
|
};
|
|
@@ -1105,11 +1112,16 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
1105
1112
|
first_name_t: profile.first_name,
|
|
1106
1113
|
last_name_t: profile.last_name,
|
|
1107
1114
|
avatar_url_s: profile.avatar_url,
|
|
1115
|
+
avatar_icon_s: profile.avatar_icon_s,
|
|
1116
|
+
banner_s: profile.banner_s,
|
|
1108
1117
|
origin_s: profile.origin,
|
|
1109
1118
|
});
|
|
1119
|
+
// Stale seeding (updatedAt: 0): the public profile is a partial
|
|
1120
|
+
// shape, and a fresh-marked seed would overwrite a richer detail
|
|
1121
|
+
// entry and be served as the whole truth for the staleTime.
|
|
1110
1122
|
if (mappedUser) {
|
|
1111
|
-
queryClient.setQueryData(queryKeys.users.detail(mappedUser.id), mappedUser);
|
|
1112
|
-
queryClient.setQueryData(queryKeys.users.byHandle(mappedUser.handle), mappedUser);
|
|
1123
|
+
queryClient.setQueryData(queryKeys.users.detail(mappedUser.id), mappedUser, { updatedAt: 0 });
|
|
1124
|
+
queryClient.setQueryData(queryKeys.users.byHandle(mappedUser.handle), mappedUser, { updatedAt: 0 });
|
|
1113
1125
|
}
|
|
1114
1126
|
}
|
|
1115
1127
|
return profile;
|
|
@@ -1135,10 +1147,16 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
1135
1147
|
if (resp.success && resp.users) {
|
|
1136
1148
|
const users = resp.users.map((u) => {
|
|
1137
1149
|
const user = toUser(u);
|
|
1138
|
-
// Pre-populate individual caches
|
|
1150
|
+
// Pre-populate individual caches so a detail view renders
|
|
1151
|
+
// instantly — but as STALE data (updatedAt: 0): a search answer
|
|
1152
|
+
// can be a partial shape (a member's search has no email or
|
|
1153
|
+
// settings), and a fresh-marked partial would be served as the
|
|
1154
|
+
// whole truth for the staleTime. Stale renders and revalidates.
|
|
1139
1155
|
if (user) {
|
|
1140
|
-
queryClient.setQueryData(queryKeys.users.detail(user.id), user
|
|
1141
|
-
|
|
1156
|
+
queryClient.setQueryData(queryKeys.users.detail(user.id), user, {
|
|
1157
|
+
updatedAt: 0,
|
|
1158
|
+
});
|
|
1159
|
+
queryClient.setQueryData(queryKeys.users.byHandle(user.handle), user, { updatedAt: 0 });
|
|
1142
1160
|
}
|
|
1143
1161
|
return user;
|
|
1144
1162
|
});
|
|
@@ -1173,9 +1191,11 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
1173
1191
|
const users = resp.users
|
|
1174
1192
|
.map((u) => {
|
|
1175
1193
|
const user = toUser(u);
|
|
1194
|
+
// Stale seeding, as in useSearchUsers: the bulk answer may be
|
|
1195
|
+
// a partial shape, so it renders but revalidates.
|
|
1176
1196
|
if (user) {
|
|
1177
|
-
queryClient.setQueryData(queryKeys.users.detail(user.id), user);
|
|
1178
|
-
queryClient.setQueryData(queryKeys.users.byHandle(user.handle), user);
|
|
1197
|
+
queryClient.setQueryData(queryKeys.users.detail(user.id), user, { updatedAt: 0 });
|
|
1198
|
+
queryClient.setQueryData(queryKeys.users.byHandle(user.handle), user, { updatedAt: 0 });
|
|
1179
1199
|
}
|
|
1180
1200
|
return user;
|
|
1181
1201
|
})
|
|
@@ -1369,6 +1389,8 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
1369
1389
|
body: {
|
|
1370
1390
|
name: organization.name,
|
|
1371
1391
|
description: organization.description,
|
|
1392
|
+
avatarIcon: organization.avatarIcon,
|
|
1393
|
+
banner: organization.banner,
|
|
1372
1394
|
},
|
|
1373
1395
|
});
|
|
1374
1396
|
},
|
|
@@ -1479,6 +1501,8 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
1479
1501
|
body: {
|
|
1480
1502
|
name: team.name,
|
|
1481
1503
|
description: team.description,
|
|
1504
|
+
avatarIcon: team.avatarIcon,
|
|
1505
|
+
banner: team.banner,
|
|
1482
1506
|
},
|
|
1483
1507
|
});
|
|
1484
1508
|
},
|
|
@@ -19,6 +19,8 @@ export type CachedPrincipal = {
|
|
|
19
19
|
email?: string;
|
|
20
20
|
origin?: string;
|
|
21
21
|
avatarUrl?: string;
|
|
22
|
+
avatarIcon?: string;
|
|
23
|
+
banner?: string;
|
|
22
24
|
/** Team-specific: parent organization handle. */
|
|
23
25
|
organizationHandle?: string;
|
|
24
26
|
/** Team-specific: parent organization display name. */
|
package/lib/models/IAM.d.ts
CHANGED
|
@@ -83,6 +83,8 @@ export interface UserMeResponse {
|
|
|
83
83
|
last_name_t: string;
|
|
84
84
|
/** Avatar URL for the user (may be empty string) */
|
|
85
85
|
avatar_url_s: string;
|
|
86
|
+
avatar_icon_s?: string;
|
|
87
|
+
banner_s?: string;
|
|
86
88
|
/** Array of roles assigned to the user */
|
|
87
89
|
roles_ss: string[];
|
|
88
90
|
/** Allow additional fields that may come from the server */
|
|
@@ -127,6 +129,8 @@ export interface ShareablePrincipal {
|
|
|
127
129
|
description?: string | null;
|
|
128
130
|
email?: string | null;
|
|
129
131
|
avatar_url?: string | null;
|
|
132
|
+
avatar_icon?: string | null;
|
|
133
|
+
banner?: string | null;
|
|
130
134
|
/** Parent organization UID — set when kind === 'team'. */
|
|
131
135
|
organization_uid?: string | null;
|
|
132
136
|
/** Parent organization handle — set when kind === 'team'. */
|
|
@@ -151,6 +155,10 @@ export interface PrincipalSearchUser {
|
|
|
151
155
|
origin?: string;
|
|
152
156
|
avatar_url_s?: string;
|
|
153
157
|
avatarUrl?: string;
|
|
158
|
+
avatar_icon_s?: string;
|
|
159
|
+
avatarIcon?: string;
|
|
160
|
+
banner_s?: string;
|
|
161
|
+
banner?: string;
|
|
154
162
|
[key: string]: any;
|
|
155
163
|
}
|
|
156
164
|
export interface PrincipalSearchTeam {
|
|
@@ -160,6 +168,8 @@ export interface PrincipalSearchTeam {
|
|
|
160
168
|
description_t?: string;
|
|
161
169
|
organization_uid?: string;
|
|
162
170
|
organization_handle_s?: string;
|
|
171
|
+
avatar_icon_s?: string;
|
|
172
|
+
banner_s?: string;
|
|
163
173
|
[key: string]: any;
|
|
164
174
|
}
|
|
165
175
|
export interface PrincipalSearchOrganization {
|
|
@@ -25,6 +25,8 @@ export function asOrganization(org) {
|
|
|
25
25
|
name: org.name_t,
|
|
26
26
|
displayName: org.display_name_t || org.name_t,
|
|
27
27
|
description: org.description_t,
|
|
28
|
+
avatarIcon: org.avatar_icon_s ?? org.avatarIcon,
|
|
29
|
+
banner: org.banner_s ?? org.banner,
|
|
28
30
|
public: org.public_b,
|
|
29
31
|
members,
|
|
30
32
|
teams: [],
|
package/lib/models/Profile.d.ts
CHANGED
package/lib/models/Team.d.ts
CHANGED
package/lib/models/Team.js
CHANGED
package/lib/models/User.d.ts
CHANGED
|
@@ -25,6 +25,8 @@ export declare class User implements IUser {
|
|
|
25
25
|
credits?: number;
|
|
26
26
|
creditsCustomerId?: string;
|
|
27
27
|
avatarUrl?: string;
|
|
28
|
+
avatarIcon?: string;
|
|
29
|
+
banner?: string;
|
|
28
30
|
origin?: string;
|
|
29
31
|
invites?: Array<IInvite>;
|
|
30
32
|
iamProviders: Array<IIAMProviderLinked>;
|
|
@@ -63,6 +65,8 @@ export type IUser = IBaseUser & {
|
|
|
63
65
|
credits?: number;
|
|
64
66
|
creditsCustomerId?: string;
|
|
65
67
|
avatarUrl?: string;
|
|
68
|
+
avatarIcon?: string;
|
|
69
|
+
banner?: string;
|
|
66
70
|
origin?: string;
|
|
67
71
|
invites?: Array<IInvite>;
|
|
68
72
|
iamProviders: Array<IIAMProviderLinked>;
|
package/lib/models/User.js
CHANGED
|
@@ -41,6 +41,8 @@ export class User {
|
|
|
41
41
|
credits;
|
|
42
42
|
creditsCustomerId;
|
|
43
43
|
avatarUrl;
|
|
44
|
+
avatarIcon;
|
|
45
|
+
banner;
|
|
44
46
|
origin;
|
|
45
47
|
invites;
|
|
46
48
|
iamProviders;
|
|
@@ -64,6 +66,8 @@ export class User {
|
|
|
64
66
|
this.friendlyName =
|
|
65
67
|
toFriendlyName(u.first_name_t, u.last_name_t) || u.handle_s;
|
|
66
68
|
this.avatarUrl = u.avatar_url_s;
|
|
69
|
+
this.avatarIcon = u.avatar_icon_s ?? u.avatarIcon;
|
|
70
|
+
this.banner = u.banner_s ?? u.banner;
|
|
67
71
|
this.origin = u.origin_s;
|
|
68
72
|
this.joinDate = u.join_ts_dt ? new Date(u.join_ts_dt) : undefined;
|
|
69
73
|
this.credits = u.credits_i ? Number(u.credits_i) : 0;
|
package/lib/models/UserDTO.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface UserData {
|
|
|
23
23
|
last_name_t: string;
|
|
24
24
|
/** Display name shown in the UI */
|
|
25
25
|
avatar_url_s: string;
|
|
26
|
+
avatar_icon_s?: string;
|
|
27
|
+
banner_s?: string;
|
|
26
28
|
/** Additional fields that may be present in the response */
|
|
27
29
|
[key: string]: any;
|
|
28
30
|
}
|
|
@@ -43,6 +45,8 @@ export interface UserJSON {
|
|
|
43
45
|
handle: string;
|
|
44
46
|
/** URL to the user's avatar image */
|
|
45
47
|
avatarUrl: string;
|
|
48
|
+
avatarIcon?: string;
|
|
49
|
+
banner?: string;
|
|
46
50
|
}
|
|
47
51
|
/**
|
|
48
52
|
* User model representing a Datalayer platform user.
|
|
@@ -65,6 +69,8 @@ export declare class UserDTO {
|
|
|
65
69
|
get lastName(): string;
|
|
66
70
|
get displayName(): string;
|
|
67
71
|
get avatarUrl(): string;
|
|
72
|
+
get avatarIcon(): string | undefined;
|
|
73
|
+
get banner(): string | undefined;
|
|
68
74
|
/**
|
|
69
75
|
* Get user data in camelCase format.
|
|
70
76
|
* Returns only the core fields that consumers need.
|
package/lib/models/UserDTO.js
CHANGED
|
@@ -43,6 +43,12 @@ export class UserDTO {
|
|
|
43
43
|
get avatarUrl() {
|
|
44
44
|
return this._data.avatar_url_s;
|
|
45
45
|
}
|
|
46
|
+
get avatarIcon() {
|
|
47
|
+
return this._data.avatar_icon_s;
|
|
48
|
+
}
|
|
49
|
+
get banner() {
|
|
50
|
+
return this._data.banner_s;
|
|
51
|
+
}
|
|
46
52
|
// ========================================================================
|
|
47
53
|
// Utility Methods
|
|
48
54
|
// ========================================================================
|
|
@@ -63,6 +69,8 @@ export class UserDTO {
|
|
|
63
69
|
email: this.email,
|
|
64
70
|
handle: this.handle,
|
|
65
71
|
avatarUrl: this.avatarUrl,
|
|
72
|
+
avatarIcon: this.avatarIcon,
|
|
73
|
+
banner: this.banner,
|
|
66
74
|
};
|
|
67
75
|
validateJSON(obj, 'User');
|
|
68
76
|
return obj;
|
|
@@ -7,7 +7,6 @@ import { useStore } from 'zustand';
|
|
|
7
7
|
import { configLogger } from '../../utils/Logger.js';
|
|
8
8
|
let loadConfigurationFromServer = true;
|
|
9
9
|
let initialConfiguration = {
|
|
10
|
-
datalayerUrl: 'https://prod1.datalayer.run',
|
|
11
10
|
token: '',
|
|
12
11
|
credits: 0,
|
|
13
12
|
cpuEnvironment: 'ai-agents-env"',
|
|
@@ -17,12 +16,14 @@ let initialConfiguration = {
|
|
|
17
16
|
loadConfigurationFromServer: true,
|
|
18
17
|
jupyterServerless: false,
|
|
19
18
|
iamUrl: 'https://prod1.datalayer.run',
|
|
19
|
+
managerUrl: 'https://prod1.datalayer.run',
|
|
20
20
|
runtimesUrl: 'https://r1.datalayer.run',
|
|
21
|
+
schedulerUrl: 'https://prod1.datalayer.run',
|
|
21
22
|
libraryUrl: 'https://prod1.datalayer.run',
|
|
22
23
|
spacerUrl: 'https://prod1.datalayer.run',
|
|
23
24
|
aiAgentsUrl: 'https://prod1.datalayer.run',
|
|
24
25
|
aiInferenceUrl: 'https://prod1.datalayer.run',
|
|
25
|
-
|
|
26
|
+
jupyterMcpServerUrl: 'https://mcp.datalayer.run/mcp',
|
|
26
27
|
otelUrl: 'https://prod1.datalayer.run',
|
|
27
28
|
// Defaults to prod so telemetry is consumed from the production OTEL service
|
|
28
29
|
// even when runtimes export elsewhere (see `getOtelConsumeUrl` and the
|
|
@@ -34,8 +35,8 @@ let initialConfiguration = {
|
|
|
34
35
|
supportUrl: 'https://prod1.datalayer.run',
|
|
35
36
|
launcher: {
|
|
36
37
|
category: 'Datalayer',
|
|
37
|
-
name: 'Datalayer
|
|
38
|
-
icon:
|
|
38
|
+
name: 'Datalayer',
|
|
39
|
+
icon: null,
|
|
39
40
|
rank: 1,
|
|
40
41
|
},
|
|
41
42
|
brand: {
|
|
@@ -10,6 +10,7 @@ import { getStoredToken, getStoredUser, loadRefreshTokenFromCookie, storeToken,
|
|
|
10
10
|
import { requestDatalayerAPI, } from '../../api/DatalayerApi.js';
|
|
11
11
|
import { getCookie, setCookie, deleteCookie } from '../../utils/index.js';
|
|
12
12
|
import { coreStore } from './CoreState.js';
|
|
13
|
+
import { profileStore } from './ProfileState.js';
|
|
13
14
|
/**
|
|
14
15
|
* Limit to warn about low credits in milliseconds.
|
|
15
16
|
*/
|
|
@@ -66,6 +67,7 @@ export const iamStore = createStore((set, get) => {
|
|
|
66
67
|
logout: () => {
|
|
67
68
|
storeUser();
|
|
68
69
|
storeToken();
|
|
70
|
+
profileStore.getState().clearProfile();
|
|
69
71
|
set({
|
|
70
72
|
credits: undefined,
|
|
71
73
|
creditsReservations: [],
|
|
@@ -106,6 +108,7 @@ export const iamStore = createStore((set, get) => {
|
|
|
106
108
|
...user,
|
|
107
109
|
},
|
|
108
110
|
};
|
|
111
|
+
profileStore.getState().setProfileFromUser(updatedState.user);
|
|
109
112
|
/*
|
|
110
113
|
if (state.user?.email && !updatedState.user.email) {
|
|
111
114
|
updatedState.user.email = state.user.email;
|
|
@@ -200,6 +203,9 @@ export const iamStore = createStore((set, get) => {
|
|
|
200
203
|
setLogin: (user, token) => set((state) => {
|
|
201
204
|
storeUser(user);
|
|
202
205
|
storeToken(token);
|
|
206
|
+
// The profile store is what the surfaces (user menu, sidebars,
|
|
207
|
+
// profile views) read: every flow that learns the user feeds it.
|
|
208
|
+
profileStore.getState().setProfileFromUser(user);
|
|
203
209
|
return {
|
|
204
210
|
user,
|
|
205
211
|
token,
|
|
@@ -4,6 +4,58 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { createStore } from 'zustand/vanilla';
|
|
6
6
|
import { useStore } from 'zustand';
|
|
7
|
+
/**
|
|
8
|
+
* Name of the cookie holding the space last selected by the user.
|
|
9
|
+
*
|
|
10
|
+
* The selection has to survive a reload, as the whole user interface is scoped
|
|
11
|
+
* by it; it lives in a cookie next to the principal context written by
|
|
12
|
+
* `usePrincipalStore`.
|
|
13
|
+
*/
|
|
14
|
+
const SPACE_CONTEXT_COOKIE = 'datalayer-space-context';
|
|
15
|
+
const SPACE_CONTEXT_COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
|
|
16
|
+
const readSpaceContextCookie = () => {
|
|
17
|
+
if (typeof document === 'undefined') {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const escaped = SPACE_CONTEXT_COOKIE.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
|
21
|
+
const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${escaped}=([^;]*)`));
|
|
22
|
+
if (!match?.[1]) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(decodeURIComponent(match[1]));
|
|
27
|
+
if (!parsed || typeof parsed !== 'object' || typeof parsed.id !== 'string') {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
id: parsed.id,
|
|
32
|
+
handle: typeof parsed.handle === 'string' ? parsed.handle : undefined,
|
|
33
|
+
name: typeof parsed.name === 'string' ? parsed.name : undefined,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const writeSpaceContextCookie = (space) => {
|
|
41
|
+
if (typeof document === 'undefined') {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (!space?.id) {
|
|
45
|
+
document.cookie =
|
|
46
|
+
`${SPACE_CONTEXT_COOKIE}=;` + ' path=/; max-age=0; SameSite=Lax';
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const value = encodeURIComponent(JSON.stringify({
|
|
50
|
+
id: space.id,
|
|
51
|
+
handle: space.handle,
|
|
52
|
+
name: space.name,
|
|
53
|
+
}));
|
|
54
|
+
document.cookie =
|
|
55
|
+
`${SPACE_CONTEXT_COOKIE}=${value};` +
|
|
56
|
+
` path=/; max-age=${SPACE_CONTEXT_COOKIE_MAX_AGE}; SameSite=Lax`;
|
|
57
|
+
};
|
|
58
|
+
const initialSpaceContext = readSpaceContextCookie();
|
|
7
59
|
export const layoutStore = createStore((set, get) => ({
|
|
8
60
|
backdrop: undefined,
|
|
9
61
|
banner: undefined,
|
|
@@ -15,7 +67,8 @@ export const layoutStore = createStore((set, get) => ({
|
|
|
15
67
|
organization: undefined,
|
|
16
68
|
rightPortal: undefined,
|
|
17
69
|
screenCapture: undefined,
|
|
18
|
-
space
|
|
70
|
+
// Restored from the cookie; the full space is hydrated once loaded.
|
|
71
|
+
space: initialSpaceContext,
|
|
19
72
|
team: undefined,
|
|
20
73
|
hideBackdrop: () => set((state) => ({
|
|
21
74
|
backdrop: { open: false, message: undefined },
|
|
@@ -65,13 +118,15 @@ export const layoutStore = createStore((set, get) => ({
|
|
|
65
118
|
};
|
|
66
119
|
}),
|
|
67
120
|
updateLayoutSpace: (space) => set((state) => {
|
|
121
|
+
const next = space
|
|
122
|
+
? {
|
|
123
|
+
...state.space,
|
|
124
|
+
...space,
|
|
125
|
+
}
|
|
126
|
+
: undefined;
|
|
127
|
+
writeSpaceContextCookie(next);
|
|
68
128
|
return {
|
|
69
|
-
space:
|
|
70
|
-
? {
|
|
71
|
-
...state.space,
|
|
72
|
-
...space,
|
|
73
|
-
}
|
|
74
|
-
: undefined,
|
|
129
|
+
space: next,
|
|
75
130
|
};
|
|
76
131
|
}),
|
|
77
132
|
setItem: (item) => set((state) => ({ item })),
|
|
@@ -4,6 +4,12 @@ export type IOrganizationState = {
|
|
|
4
4
|
};
|
|
5
5
|
export type OrganizationState = IOrganizationState & {
|
|
6
6
|
updateOrganizations: (organizations: IAnyOrganization[]) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Merge what just changed into the organization of the list — the way a
|
|
9
|
+
* settings save keeps every surface reading this store current (avatar,
|
|
10
|
+
* banner, name…) without refetching the list.
|
|
11
|
+
*/
|
|
12
|
+
updateOrganization: (organization: Partial<IAnyOrganization> & Pick<IAnyOrganization, 'id'>) => void;
|
|
7
13
|
};
|
|
8
14
|
export declare const organizationStore: import("zustand").StoreApi<OrganizationState>;
|
|
9
15
|
export declare function useOrganizationStore(): OrganizationState;
|
|
@@ -9,6 +9,11 @@ export const organizationStore = createStore((set, get) => ({
|
|
|
9
9
|
updateOrganizations: (organizations) => set((state) => ({
|
|
10
10
|
organizations,
|
|
11
11
|
})),
|
|
12
|
+
updateOrganization: organization => set((state) => ({
|
|
13
|
+
organizations: state.organizations.map(known => known.id === organization.id
|
|
14
|
+
? { ...known, ...organization }
|
|
15
|
+
: known),
|
|
16
|
+
})),
|
|
12
17
|
}));
|
|
13
18
|
export function useOrganizationStore(selector) {
|
|
14
19
|
return useStore(organizationStore, selector);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { IUser } from '../../models';
|
|
2
|
+
/** The profile of the authenticated user, as the surfaces show it. */
|
|
3
|
+
export type IProfile = {
|
|
4
|
+
id?: string;
|
|
5
|
+
uid?: string;
|
|
6
|
+
handle?: string;
|
|
7
|
+
email?: string;
|
|
8
|
+
firstName?: string;
|
|
9
|
+
lastName?: string;
|
|
10
|
+
displayName?: string;
|
|
11
|
+
initials?: string;
|
|
12
|
+
avatarUrl?: string;
|
|
13
|
+
avatarIcon?: string;
|
|
14
|
+
banner?: string;
|
|
15
|
+
origin?: string;
|
|
16
|
+
roles?: string[];
|
|
17
|
+
};
|
|
18
|
+
export type ProfileState = {
|
|
19
|
+
profile?: IProfile;
|
|
20
|
+
/** Replace the profile wholesale. */
|
|
21
|
+
setProfile: (profile?: IProfile) => void;
|
|
22
|
+
/** Merge what a flow just learned into the profile. */
|
|
23
|
+
updateProfile: (partial: Partial<IProfile>) => void;
|
|
24
|
+
/** The IAM user as the profile — login and refresh feed through this. */
|
|
25
|
+
setProfileFromUser: (user?: IUser) => void;
|
|
26
|
+
/** Logout. */
|
|
27
|
+
clearProfile: () => void;
|
|
28
|
+
};
|
|
29
|
+
export declare const profileStore: import("zustand").StoreApi<ProfileState>;
|
|
30
|
+
export declare function useProfileStore(): ProfileState;
|
|
31
|
+
export declare function useProfileStore<T>(selector: (state: ProfileState) => T): T;
|
|
32
|
+
/** The profile, for surfaces that show it. */
|
|
33
|
+
export declare const useProfile: () => IProfile | undefined;
|
|
34
|
+
export default useProfileStore;
|