@datalayer/core 1.1.41 → 1.1.45

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.
Files changed (56) hide show
  1. package/README.md +10 -121
  2. package/lib/client/auth/AuthenticationManager.d.ts +3 -3
  3. package/lib/client/auth/AuthenticationManager.js +13 -13
  4. package/lib/client/auth/strategies.d.ts +2 -2
  5. package/lib/client/auth/strategies.js +6 -6
  6. package/lib/client/base.d.ts +9 -9
  7. package/lib/client/base.js +17 -17
  8. package/lib/client/mixins/IAMMixin.js +26 -26
  9. package/lib/components/auth/Login.js +2 -2
  10. package/lib/components/billing/BillingEntitySelect.d.ts +35 -0
  11. package/lib/components/billing/{BillableAccountSelect.js → BillingEntitySelect.js} +39 -41
  12. package/lib/components/billing/index.d.ts +1 -1
  13. package/lib/components/billing/index.js +1 -1
  14. package/lib/components/checkout/StripeCheckout.js +5 -5
  15. package/lib/components/display/VisuallyHidden.d.ts +1 -1
  16. package/lib/components/display/index.d.ts +0 -1
  17. package/lib/components/display/index.js +0 -1
  18. package/lib/components/principal/Principal.js +4 -2
  19. package/lib/components/principal/PrincipalBadge.js +21 -0
  20. package/lib/components/principal/PrincipalDetailsOverlay.js +72 -37
  21. package/lib/components/principal/PrincipalSwitcherMenu.d.ts +3 -3
  22. package/lib/components/principal/PrincipalSwitcherMenu.js +133 -114
  23. package/lib/components/sharing/ShareAccessComponent.js +14 -14
  24. package/lib/config/Configuration.d.ts +13 -13
  25. package/lib/config/Configuration.js +5 -5
  26. package/lib/hooks/index.d.ts +2 -2
  27. package/lib/hooks/index.js +2 -2
  28. package/lib/hooks/useBillingEntityStore.d.ts +31 -0
  29. package/lib/hooks/useBillingEntityStore.js +53 -0
  30. package/lib/hooks/useCache.d.ts +34 -33
  31. package/lib/hooks/useCache.js +298 -282
  32. package/lib/hooks/useIAM.js +1 -1
  33. package/lib/hooks/useSelectedBillingEntity.d.ts +14 -0
  34. package/lib/hooks/{useSelectedBillableAccount.js → useSelectedBillingEntity.js} +14 -14
  35. package/lib/hooks/useSelectedPrincipal.d.ts +1 -1
  36. package/lib/hooks/useSelectedPrincipal.js +1 -1
  37. package/lib/models/UserSettings.d.ts +1 -1
  38. package/lib/models/UserSettings.js +1 -1
  39. package/lib/otel/client/OtelClient.d.ts +2 -2
  40. package/lib/otel/client/OtelClient.js +2 -2
  41. package/lib/otel/hooks/index.js +5 -5
  42. package/lib/otel/types.d.ts +1 -1
  43. package/lib/otel/views/OtelLive.js +1 -1
  44. package/lib/state/substates/CoreState.js +13 -13
  45. package/lib/state/substates/IAMState.d.ts +1 -1
  46. package/lib/state/substates/IAMState.js +14 -14
  47. package/lib/state/substates/SurveysState.d.ts +1 -1
  48. package/lib/state/substates/SurveysState.js +10 -10
  49. package/lib/views/iam/SignInSimple.js +6 -6
  50. package/package.json +4 -4
  51. package/lib/components/billing/BillableAccountSelect.d.ts +0 -35
  52. package/lib/components/display/LiveRelativeTime.d.ts +0 -1
  53. package/lib/components/display/LiveRelativeTime.js +0 -10
  54. package/lib/hooks/useBillableAccountStore.d.ts +0 -31
  55. package/lib/hooks/useBillableAccountStore.js +0 -53
  56. package/lib/hooks/useSelectedBillableAccount.d.ts +0 -14
@@ -4,14 +4,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  * Distributed under the terms of the Modified BSD License.
5
5
  */
6
6
  import { useEffect, useMemo, useState } from 'react';
7
- import { ActionList, ActionMenu, Box, Button, Label, Text } from '@primer/react';
7
+ import { ActionList, ActionMenu, Box, Button, Label, Text, ThemeProvider, } from '@primer/react';
8
8
  import { OrganizationIcon, PeopleIcon, PersonIcon, TriangleDownIcon, } from '@primer/octicons-react';
9
9
  import { useCache, useAuthorization } from '../../hooks/index.js';
10
10
  import { useCoreStore } from '../../state/index.js';
11
11
  import { useIAMStore } from '../../state/substates/index.js';
12
12
  import { memberships as fetchMemberships } from '../../api/iam/profile.js';
13
13
  import { usePrincipalStore } from '../../hooks/usePrincipalStore.js';
14
- import { useBillableAccountStore } from '../../hooks/useBillableAccountStore.js';
14
+ import { useBillingEntityStore } from '../../hooks/useBillingEntityStore.js';
15
15
  import { useSelectedPrincipal } from '../../hooks/useSelectedPrincipal.js';
16
16
  import { DisplayHandle, displayHandleText } from '../display/DisplayHandle.js';
17
17
  function truncatePrincipalLabel(label, maxChars) {
@@ -26,13 +26,13 @@ function truncatePrincipalLabel(label, maxChars) {
26
26
  }
27
27
  /**
28
28
  * PrincipalSwitcherMenu — the *only* component allowed to write to the
29
- * principal store and the billable account store. It keeps both stores in
29
+ * principal store and the billing entity store. It keeps both stores in
30
30
  * sync per the rule:
31
- * - selecting a user/org principal → billable account = same user/org
32
- * - selecting a team principal → billable account = the team's parent org
31
+ * - selecting a user/org principal → billing entity = same user/org
32
+ * - selecting a team principal → billing entity = the team's parent org
33
33
  */
34
34
  export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, showClosedBorder = true, }) {
35
- const { user, token, iamRunUrl } = useIAMStore();
35
+ const { user, token, iamUrl } = useIAMStore();
36
36
  const { configuration } = useCoreStore();
37
37
  const { checkIsPlatformAdmin } = useAuthorization();
38
38
  const { useUserOrganizations } = useCache();
@@ -45,17 +45,17 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
45
45
  const selectPersonalPrincipal = usePrincipalStore(state => state.selectPersonalPrincipal);
46
46
  const selectOrganizationPrincipal = usePrincipalStore(state => state.selectOrganizationPrincipal);
47
47
  const selectTeamPrincipal = usePrincipalStore(state => state.selectTeamPrincipal);
48
- const setBillableAccount = useBillableAccountStore(state => state.setBillableAccount);
48
+ const setBillingEntity = useBillingEntityStore(state => state.setBillingEntity);
49
49
  const { selectedPrincipalKind, selectedPrincipalUid, selectedPrincipalHandle, selectedTeamParentOrganizationHandle, } = useSelectedPrincipal();
50
50
  const personalUid = user?.uid || user?.id || '';
51
51
  const personalHandle = user?.handle || '';
52
52
  const selectUser = (uid, handle) => {
53
53
  selectPersonalPrincipal(uid, handle);
54
- setBillableAccount({ kind: 'user', uid, handle });
54
+ setBillingEntity({ kind: 'user', uid, handle });
55
55
  };
56
56
  const selectOrganization = (uid, handle) => {
57
57
  selectOrganizationPrincipal(uid, handle);
58
- setBillableAccount({ kind: 'organization', uid, handle });
58
+ setBillingEntity({ kind: 'organization', uid, handle });
59
59
  };
60
60
  const selectTeam = (team, orgHandle) => {
61
61
  if (!team.organizationUid) {
@@ -67,7 +67,7 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
67
67
  organizationUid: team.organizationUid,
68
68
  organizationHandle: orgHandle,
69
69
  });
70
- setBillableAccount({
70
+ setBillingEntity({
71
71
  kind: 'organization',
72
72
  uid: team.organizationUid,
73
73
  handle: orgHandle,
@@ -85,7 +85,7 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
85
85
  }
86
86
  setTeamsLoading(true);
87
87
  try {
88
- const baseUrl = iamRunUrl || configuration.iamRunUrl;
88
+ const baseUrl = iamUrl || configuration.iamUrl;
89
89
  const response = await fetchMemberships(token, baseUrl);
90
90
  const rawMemberships = Array.isArray(response?.memberships)
91
91
  ? response.memberships
@@ -95,6 +95,8 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
95
95
  .map((membership) => ({
96
96
  uid: String(membership?.uid || membership?.id || '').trim(),
97
97
  handle: String(membership?.handle || '').trim(),
98
+ name: String(membership?.name || '').trim() || undefined,
99
+ displayName: String(membership?.display_name || membership?.displayName || '').trim() || undefined,
98
100
  organizationUid: String(membership?.organization_uid || '').trim() || undefined,
99
101
  organizationHandle: String(membership?.organization_handle ||
100
102
  membership?.organization?.handle ||
@@ -120,7 +122,7 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
120
122
  return () => {
121
123
  cancelled = true;
122
124
  };
123
- }, [token, iamRunUrl, configuration.iamRunUrl]);
125
+ }, [token, iamUrl, configuration.iamUrl]);
124
126
  useEffect(() => {
125
127
  if (!personalUid || !personalHandle) {
126
128
  return;
@@ -132,7 +134,8 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
132
134
  // Keep explicit organization/team selections stable across route transitions,
133
135
  // even while memberships/organizations are still loading or temporarily
134
136
  // unavailable for a given view.
135
- if (selectedPrincipalKind === 'organization' || selectedPrincipalKind === 'team') {
137
+ if (selectedPrincipalKind === 'organization' ||
138
+ selectedPrincipalKind === 'team') {
136
139
  return;
137
140
  }
138
141
  if (selectedPrincipalKind === 'personal' &&
@@ -203,122 +206,138 @@ export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, sh
203
206
  textTransform: 'lowercase',
204
207
  lineHeight: 1.2,
205
208
  };
206
- return (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: showClosedBorder ? (_jsxs(Box, { as: "button", type: "button", "aria-label": "Switch principal", sx: {
207
- width: fullWidth ? '100%' : 'auto',
208
- boxSizing: 'border-box',
209
- px: 2,
210
- py: 2,
211
- fontSize: 0,
212
- display: 'flex',
213
- alignItems: 'center',
214
- justifyContent: 'space-between',
215
- gap: 2,
216
- bg: 'canvas.subtle',
217
- border: '1px solid',
218
- borderColor: 'border.default',
219
- borderRadius: 2,
220
- cursor: 'pointer',
221
- textAlign: 'left',
222
- transition: 'background-color 120ms ease',
223
- ':hover': {
224
- bg: 'canvas.subtle',
225
- },
226
- ':focus-visible': {
227
- bg: 'canvas.subtle',
228
- outline: '2px solid',
229
- outlineColor: 'accent.emphasis',
230
- outlineOffset: '2px',
231
- },
232
- }, children: [_jsx(Box, { sx: {
233
- display: 'inline-flex',
234
- alignItems: 'center',
235
- color: 'fg.muted',
236
- flexShrink: 0,
237
- }, children: selectedPrincipalKind === 'organization' ? (_jsx(OrganizationIcon, { size: 16 })) : selectedPrincipalKind === 'team' ? (_jsx(PeopleIcon, { size: 16 })) : (_jsx(PersonIcon, { size: 16 })) }), _jsx(Box, { sx: { minWidth: 0, flex: 1 }, children: _jsx(Text, { sx: {
238
- display: 'block',
239
- color: 'accent.fg',
240
- fontWeight: 'semibold',
241
- fontSize: 'inherit',
242
- whiteSpace: 'nowrap',
243
- overflow: 'hidden',
244
- textOverflow: 'ellipsis',
245
- maxWidth: '100%',
246
- }, children: selectedPrincipalLabelClosed }) }), isPlatformAdmin && isCurrentUserPrincipal ? (_jsx(Box, { sx: {
247
- flexShrink: 0,
248
- display: 'inline-flex',
249
- alignItems: 'center',
250
- }, children: _jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" }) })) : null, _jsx(Box, { sx: {
251
- display: 'inline-flex',
252
- alignItems: 'center',
253
- color: 'fg.muted',
254
- flexShrink: 0,
255
- ml: isPlatformAdmin && isCurrentUserPrincipal ? 1 : 0,
256
- }, children: _jsx(TriangleDownIcon, { size: 12 }) })] })) : (_jsx(Button, { variant: "invisible", size: "small", "aria-label": "Switch principal", trailingVisual: TriangleDownIcon, sx: {
257
- maxWidth: fullWidth ? '100%' : ['200px', '260px', '360px'],
258
- width: fullWidth ? '100%' : 'auto',
259
- }, children: _jsxs(Box, { sx: {
260
- display: 'inline-flex',
209
+ return (_jsx(ThemeProvider, { children: _jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: showClosedBorder ? (_jsxs(Box, { as: "button", type: "button", "aria-label": "Switch principal", sx: {
210
+ width: fullWidth ? '100%' : 'auto',
211
+ boxSizing: 'border-box',
212
+ px: 2,
213
+ py: 2,
214
+ fontSize: 0,
215
+ display: 'flex',
261
216
  alignItems: 'center',
262
- gap: 1,
263
- minWidth: 0,
217
+ justifyContent: 'space-between',
218
+ gap: 2,
219
+ bg: 'canvas.subtle',
220
+ border: '1px solid',
221
+ borderColor: 'border.default',
222
+ borderRadius: 2,
223
+ cursor: 'pointer',
224
+ textAlign: 'left',
225
+ transition: 'background-color 120ms ease',
226
+ ':hover': {
227
+ bg: 'canvas.subtle',
228
+ },
229
+ ':focus-visible': {
230
+ bg: 'canvas.subtle',
231
+ outline: '2px solid',
232
+ outlineColor: 'accent.emphasis',
233
+ outlineOffset: '2px',
234
+ },
264
235
  }, children: [_jsx(Box, { sx: {
265
236
  display: 'inline-flex',
266
237
  alignItems: 'center',
267
238
  color: 'fg.muted',
268
239
  flexShrink: 0,
269
- }, children: selectedPrincipalKind === 'organization' ? (_jsx(OrganizationIcon, { size: 16 })) : selectedPrincipalKind === 'team' ? (_jsx(PeopleIcon, { size: 16 })) : (_jsx(PersonIcon, { size: 16 })) }), _jsx(Box, { sx: {
270
- minWidth: 0,
271
- overflow: 'hidden',
272
- textOverflow: 'ellipsis',
273
- whiteSpace: 'nowrap',
274
- }, children: _jsx(Text, { sx: {
240
+ }, children: selectedPrincipalKind === 'organization' ? (_jsx(PeopleIcon, { size: 16 })) : selectedPrincipalKind === 'team' ? (_jsx(OrganizationIcon, { size: 16 })) : (_jsx(PersonIcon, { size: 16 })) }), _jsx(Box, { sx: { minWidth: 0, flex: 1 }, children: _jsx(Text, { sx: {
241
+ display: 'block',
275
242
  color: 'accent.fg',
276
243
  fontWeight: 'semibold',
277
- }, children: selectedPrincipalLabelClosed }) }), isPlatformAdmin && isCurrentUserPrincipal ? (_jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" })) : null] }) })) }), _jsx(ActionMenu.Overlay, { width: "medium", children: _jsxs(ActionList, { children: [_jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Personal" }), _jsxs(ActionList.Item, { disabled: isCurrentUserPrincipal, selected: isCurrentUserPrincipal, sx: isCurrentUserPrincipal ? selectedItemSx : undefined, onSelect: () => {
278
- if (isCurrentUserPrincipal) {
279
- return;
280
- }
281
- if (personalUid && personalHandle) {
282
- selectUser(personalUid, personalHandle);
283
- }
284
- }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PersonIcon, {}) }), _jsx(DisplayHandle, { handle: personalHandle || 'me' }), isPlatformAdmin ? (_jsx(ActionList.TrailingVisual, { children: _jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" }) })) : null] })] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Organizations" }), organizations.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No organizations" })) : (organizations.map((organization) => {
285
- const organizationUid = getOrganizationUid(organization);
286
- const isCurrentOrganizationPrincipal = selectedPrincipalKind === 'organization' &&
287
- selectedPrincipalUid === organizationUid;
288
- return (_jsxs(ActionList.Item, { disabled: isCurrentOrganizationPrincipal, selected: isCurrentOrganizationPrincipal, sx: isCurrentOrganizationPrincipal
289
- ? selectedItemSx
290
- : undefined, onSelect: () => {
291
- if (isCurrentOrganizationPrincipal) {
244
+ fontSize: 'inherit',
245
+ whiteSpace: 'nowrap',
246
+ overflow: 'hidden',
247
+ textOverflow: 'ellipsis',
248
+ maxWidth: '100%',
249
+ }, children: selectedPrincipalLabelClosed }) }), isPlatformAdmin && isCurrentUserPrincipal ? (_jsx(Box, { sx: {
250
+ flexShrink: 0,
251
+ display: 'inline-flex',
252
+ alignItems: 'center',
253
+ }, children: _jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" }) })) : null, _jsx(Box, { sx: {
254
+ display: 'inline-flex',
255
+ alignItems: 'center',
256
+ color: 'fg.muted',
257
+ flexShrink: 0,
258
+ ml: isPlatformAdmin && isCurrentUserPrincipal ? 1 : 0,
259
+ }, children: _jsx(TriangleDownIcon, { size: 12 }) })] })) : (_jsx(Button, { variant: "invisible", size: "small", "aria-label": "Switch principal", trailingVisual: TriangleDownIcon, sx: {
260
+ maxWidth: fullWidth ? '100%' : ['200px', '260px', '360px'],
261
+ width: fullWidth ? '100%' : 'auto',
262
+ }, children: _jsxs(Box, { sx: {
263
+ display: 'inline-flex',
264
+ alignItems: 'center',
265
+ gap: 1,
266
+ minWidth: 0,
267
+ }, children: [_jsx(Box, { sx: {
268
+ display: 'inline-flex',
269
+ alignItems: 'center',
270
+ color: 'fg.muted',
271
+ flexShrink: 0,
272
+ }, children: selectedPrincipalKind === 'organization' ? (_jsx(PeopleIcon, { size: 16 })) : selectedPrincipalKind === 'team' ? (_jsx(OrganizationIcon, { size: 16 })) : (_jsx(PersonIcon, { size: 16 })) }), _jsx(Box, { sx: {
273
+ minWidth: 0,
274
+ overflow: 'hidden',
275
+ textOverflow: 'ellipsis',
276
+ whiteSpace: 'nowrap',
277
+ }, children: _jsx(Text, { sx: {
278
+ color: 'accent.fg',
279
+ fontWeight: 'semibold',
280
+ }, children: selectedPrincipalLabelClosed }) }), isPlatformAdmin && isCurrentUserPrincipal ? (_jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" })) : null] }) })) }), _jsx(ActionMenu.Overlay, { width: "large", children: _jsxs(ActionList, { children: [_jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Personal" }), _jsxs(ActionList.Item, { disabled: isCurrentUserPrincipal, selected: isCurrentUserPrincipal, sx: isCurrentUserPrincipal ? selectedItemSx : undefined, onSelect: () => {
281
+ if (isCurrentUserPrincipal) {
292
282
  return;
293
283
  }
294
- if (organizationUid && organization.handle) {
295
- selectOrganization(organizationUid, organization.handle);
296
- }
297
- }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(OrganizationIcon, {}) }), _jsx(Box, { as: "span", title: displayHandleText(organization.handle), sx: {
298
- display: 'block',
299
- minWidth: 0,
300
- overflow: 'hidden',
301
- textOverflow: 'ellipsis',
302
- whiteSpace: 'nowrap',
303
- }, children: _jsx(DisplayHandle, { handle: organization.handle }) })] }, organizationUid));
304
- }))] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Teams" }), teams.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No teams" })) : (teams.map(team => {
305
- const isCurrentTeamPrincipal = selectedPrincipalKind === 'team' &&
306
- selectedPrincipalUid === team.uid;
307
- const orgHandle = resolveTeamOrganizationHandle(team) ||
308
- personalHandle ||
309
- 'organization';
310
- return (_jsxs(ActionList.Item, { disabled: isCurrentTeamPrincipal, selected: isCurrentTeamPrincipal, sx: isCurrentTeamPrincipal ? selectedItemSx : undefined, onSelect: () => {
311
- if (isCurrentTeamPrincipal) {
312
- return;
284
+ if (personalUid && personalHandle) {
285
+ selectUser(personalUid, personalHandle);
313
286
  }
314
- selectTeam(team, orgHandle);
315
- }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PeopleIcon, {}) }), _jsxs(Box, { as: "span", title: `${displayHandleText(orgHandle)}/${displayHandleText(team.handle, { withAt: false })}`, sx: {
287
+ }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PersonIcon, {}) }), _jsxs(Box, { as: "span", sx: {
316
288
  display: 'block',
317
289
  minWidth: 0,
318
290
  overflow: 'hidden',
319
291
  textOverflow: 'ellipsis',
320
292
  whiteSpace: 'nowrap',
321
- }, children: [_jsx(DisplayHandle, { handle: orgHandle }), "/", _jsx(DisplayHandle, { handle: team.handle, withAt: false })] })] }, team.uid));
322
- }))] })] }) })] }));
293
+ }, children: [String(user?.friendlyName ||
294
+ user?.displayName ||
295
+ personalHandle ||
296
+ 'Me').trim(), _jsxs(Box, { as: "span", sx: { color: 'fg.muted' }, children: [' ', "\u00B7 ", _jsx(DisplayHandle, { handle: personalHandle || 'me' })] })] }), isPlatformAdmin ? (_jsx(ActionList.TrailingVisual, { children: _jsx(Label, { variant: "secondary", size: "small", sx: adminBadgeSx, children: "admin" }) })) : null] })] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Organizations" }), organizations.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No organizations" })) : (organizations.map((organization) => {
297
+ const organizationUid = getOrganizationUid(organization);
298
+ const isCurrentOrganizationPrincipal = selectedPrincipalKind === 'organization' &&
299
+ selectedPrincipalUid === organizationUid;
300
+ return (_jsxs(ActionList.Item, { disabled: isCurrentOrganizationPrincipal, selected: isCurrentOrganizationPrincipal, sx: isCurrentOrganizationPrincipal
301
+ ? selectedItemSx
302
+ : undefined, onSelect: () => {
303
+ if (isCurrentOrganizationPrincipal) {
304
+ return;
305
+ }
306
+ if (organizationUid && organization.handle) {
307
+ selectOrganization(organizationUid, organization.handle);
308
+ }
309
+ }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PeopleIcon, {}) }), _jsxs(Box, { as: "span", title: displayHandleText(organization.handle), sx: {
310
+ display: 'block',
311
+ minWidth: 0,
312
+ overflow: 'hidden',
313
+ textOverflow: 'ellipsis',
314
+ whiteSpace: 'nowrap',
315
+ }, children: [String(organization?.display_name ||
316
+ organization?.displayName ||
317
+ organization?.name ||
318
+ organization?.handle ||
319
+ 'Organization').trim(), _jsxs(Box, { as: "span", sx: { color: 'fg.muted' }, children: [' ', "\u00B7 ", _jsx(DisplayHandle, { handle: organization.handle })] })] })] }, organizationUid));
320
+ }))] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Teams" }), teams.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No teams" })) : (teams.map(team => {
321
+ const isCurrentTeamPrincipal = selectedPrincipalKind === 'team' &&
322
+ selectedPrincipalUid === team.uid;
323
+ const orgHandle = resolveTeamOrganizationHandle(team) ||
324
+ personalHandle ||
325
+ 'organization';
326
+ return (_jsxs(ActionList.Item, { disabled: isCurrentTeamPrincipal, selected: isCurrentTeamPrincipal, sx: isCurrentTeamPrincipal ? selectedItemSx : undefined, onSelect: () => {
327
+ if (isCurrentTeamPrincipal) {
328
+ return;
329
+ }
330
+ selectTeam(team, orgHandle);
331
+ }, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(OrganizationIcon, {}) }), _jsxs(Box, { as: "span", title: `${displayHandleText(orgHandle)}/${displayHandleText(team.handle, { withAt: false })}`, sx: {
332
+ display: 'block',
333
+ minWidth: 0,
334
+ overflow: 'hidden',
335
+ textOverflow: 'ellipsis',
336
+ whiteSpace: 'nowrap',
337
+ }, children: [String(team.displayName ||
338
+ team.name ||
339
+ team.handle ||
340
+ 'Team').trim(), _jsxs(Box, { as: "span", sx: { color: 'fg.muted' }, children: [' ', "\u00B7 ", _jsx(DisplayHandle, { handle: orgHandle }), "/", _jsx(DisplayHandle, { handle: team.handle, withAt: false })] })] })] }, team.uid));
341
+ }))] })] }) })] }) }));
323
342
  }
324
343
  export default PrincipalSwitcherMenu;
@@ -433,8 +433,8 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
433
433
  ]);
434
434
  // ----- Derived -----
435
435
  const canRequest = Boolean(requestUrl && token);
436
- const canSearchPrincipals = Boolean(configuration?.iamRunUrl && token);
437
- const iamRunUrl = configuration?.iamRunUrl;
436
+ const canSearchPrincipals = Boolean(configuration?.iamUrl && token);
437
+ const iamUrl = configuration?.iamUrl;
438
438
  const principalKindsSet = useMemo(() => new Set(principalKinds), [principalKinds]);
439
439
  const principalKindsKey = useMemo(() => [...principalKinds].sort().join('|'), [principalKinds]);
440
440
  const aclEntries = useMemo(() => buildAclEntries(access, principalKinds), [access, principalKinds]);
@@ -592,14 +592,14 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
592
592
  ]);
593
593
  // ----- Fetch shareable principals on open -----
594
594
  useEffect(() => {
595
- if (!isOpen || !canSearchPrincipals || !iamRunUrl || !token) {
595
+ if (!isOpen || !canSearchPrincipals || !iamUrl || !token) {
596
596
  return;
597
597
  }
598
598
  let cancelled = false;
599
599
  const run = async () => {
600
600
  setIsLoadingShareable(true);
601
601
  try {
602
- const response = await fetch(`${iamRunUrl}/api/iam/v1/principals/shareable`, {
602
+ const response = await fetch(`${iamUrl}/api/iam/v1/principals/shareable`, {
603
603
  method: 'GET',
604
604
  headers: {
605
605
  'Content-Type': 'application/json',
@@ -671,7 +671,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
671
671
  return () => {
672
672
  cancelled = true;
673
673
  };
674
- }, [isOpen, canSearchPrincipals, iamRunUrl, token, mergePrincipalCacheEntry]);
674
+ }, [isOpen, canSearchPrincipals, iamUrl, token, mergePrincipalCacheEntry]);
675
675
  // ----- Debounce search query -----
676
676
  useEffect(() => {
677
677
  const timeout = window.setTimeout(() => {
@@ -683,7 +683,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
683
683
  }, [searchQuery]);
684
684
  // ----- Run search against /principals/search -----
685
685
  useEffect(() => {
686
- if (!isOpen || !canSearchPrincipals || !iamRunUrl || !token) {
686
+ if (!isOpen || !canSearchPrincipals || !iamUrl || !token) {
687
687
  setSearchResults([]);
688
688
  return;
689
689
  }
@@ -704,7 +704,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
704
704
  let response;
705
705
  let payload;
706
706
  try {
707
- response = await fetch(`${iamRunUrl}/api/iam/v1/principals/search`, {
707
+ response = await fetch(`${iamUrl}/api/iam/v1/principals/search`, {
708
708
  method: 'POST',
709
709
  headers: {
710
710
  'Content-Type': 'application/json',
@@ -837,7 +837,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
837
837
  }, [
838
838
  isOpen,
839
839
  canSearchPrincipals,
840
- iamRunUrl,
840
+ iamUrl,
841
841
  token,
842
842
  normalizedDebouncedSearch,
843
843
  principalKindsKey,
@@ -847,7 +847,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
847
847
  ]);
848
848
  // ----- Hydrate ACL user uids in bulk -----
849
849
  useEffect(() => {
850
- if (!isOpen || !canSearchPrincipals || !iamRunUrl || !token) {
850
+ if (!isOpen || !canSearchPrincipals || !iamUrl || !token) {
851
851
  return;
852
852
  }
853
853
  const userUids = Array.from(new Set([
@@ -881,7 +881,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
881
881
  return acc;
882
882
  }, {}));
883
883
  try {
884
- const response = await fetch(`${iamRunUrl}/api/iam/v1/users/bulk`, {
884
+ const response = await fetch(`${iamUrl}/api/iam/v1/users/bulk`, {
885
885
  method: 'POST',
886
886
  headers: {
887
887
  'Content-Type': 'application/json',
@@ -942,7 +942,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
942
942
  }, [
943
943
  isOpen,
944
944
  canSearchPrincipals,
945
- iamRunUrl,
945
+ iamUrl,
946
946
  token,
947
947
  aclEntries,
948
948
  ownerPrincipals,
@@ -951,7 +951,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
951
951
  ]);
952
952
  // ----- Hydrate ACL team uids individually -----
953
953
  useEffect(() => {
954
- if (!isOpen || !canSearchPrincipals || !iamRunUrl || !token) {
954
+ if (!isOpen || !canSearchPrincipals || !iamUrl || !token) {
955
955
  return;
956
956
  }
957
957
  const unknownTeams = aclEntries.filter(entry => {
@@ -967,7 +967,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
967
967
  let cancelled = false;
968
968
  void Promise.all(unknownTeams.map(async (entry) => {
969
969
  try {
970
- const response = await fetch(`${iamRunUrl}/api/iam/v1/teams/${encodeURIComponent(entry.uid)}`, {
970
+ const response = await fetch(`${iamUrl}/api/iam/v1/teams/${encodeURIComponent(entry.uid)}`, {
971
971
  method: 'GET',
972
972
  headers: {
973
973
  'Content-Type': 'application/json',
@@ -1001,7 +1001,7 @@ export function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resour
1001
1001
  }, [
1002
1002
  isOpen,
1003
1003
  canSearchPrincipals,
1004
- iamRunUrl,
1004
+ iamUrl,
1005
1005
  token,
1006
1006
  aclEntries,
1007
1007
  principalCache,
@@ -12,7 +12,7 @@ export type IDatalayerCoreConfig = {
12
12
  * The base URL for the Datalayer platform API.
13
13
  * @example "https://prod1.datalayer.run"
14
14
  */
15
- runUrl: string;
15
+ datalayerUrl: string;
16
16
  /**
17
17
  * Datalayer API authentication token.
18
18
  * Used for authenticating requests to the Datalayer platform.
@@ -46,51 +46,51 @@ export type IDatalayerCoreConfig = {
46
46
  /**
47
47
  * IAM API URL.
48
48
  */
49
- iamRunUrl: string;
49
+ iamUrl: string;
50
50
  /**
51
51
  * Runtimes API URL.
52
52
  */
53
- runtimesRunUrl: string;
53
+ runtimesUrl: string;
54
54
  /**
55
55
  * Spacer API URL.
56
56
  */
57
- spacerRunUrl: string;
57
+ spacerUrl: string;
58
58
  /**
59
59
  * Library API URL.
60
60
  */
61
- libraryRunUrl: string;
61
+ libraryUrl: string;
62
62
  /**
63
63
  * AI Agents API URL.
64
64
  */
65
- aiagentsRunUrl: string;
65
+ aiAgentsUrl: string;
66
66
  /**
67
67
  * AI Inference API URL.
68
68
  */
69
- aiinferenceRunUrl: string;
69
+ aiInferenceUrl: string;
70
70
  /**
71
71
  * MCP Servers API URL.
72
72
  */
73
- mcpserversRunUrl: string;
73
+ mcpServersUrl: string;
74
74
  /**
75
75
  * OTEL (OpenTelemetry) API URL.
76
76
  */
77
- otelRunUrl: string;
77
+ otelUrl: string;
78
78
  /**
79
79
  * Growth API URL.
80
80
  */
81
- growthRunUrl: string;
81
+ growthUrl: string;
82
82
  /**
83
83
  * Inbounds API URL.
84
84
  */
85
- inboundsRunUrl: string;
85
+ inboundsUrl: string;
86
86
  /**
87
87
  * Success API URL.
88
88
  */
89
- successRunUrl: string;
89
+ successUrl: string;
90
90
  /**
91
91
  * Support API URL.
92
92
  */
93
- supportRunUrl: string;
93
+ supportUrl: string;
94
94
  /**
95
95
  * Load configuration from server.
96
96
  */
@@ -26,7 +26,7 @@ export class DatalayerConfiguration {
26
26
  * Default configuration values for Datalayer
27
27
  */
28
28
  export const DEFAULT_DATALAYER_CONFIG = {
29
- runUrl: 'https://oss.datalayer.run',
29
+ datalayerUrl: 'https://oss.datalayer.run',
30
30
  credits: 100,
31
31
  cpuEnvironment: 'ai-agents-env',
32
32
  gpuEnvironment: 'ai-env',
@@ -37,7 +37,7 @@ export const DEFAULT_DATALAYER_CONFIG = {
37
37
  export function isDatalayerConfig(config) {
38
38
  return (config &&
39
39
  typeof config === 'object' &&
40
- typeof config.runUrl === 'string' &&
40
+ typeof config.datalayerUrl === 'string' &&
41
41
  typeof config.token === 'string' &&
42
42
  typeof config.credits === 'number' &&
43
43
  typeof config.cpuEnvironment === 'string' &&
@@ -51,10 +51,10 @@ export function isDatalayerConfig(config) {
51
51
  export function mergeConfigWithDefaults(config) {
52
52
  if (!config)
53
53
  return undefined;
54
- // If we have required fields (token and runUrl), merge with defaults for optional fields
55
- if (config.token && config.runUrl) {
54
+ // If we have required fields (token and datalayerUrl), merge with defaults for optional fields
55
+ if (config.token && config.datalayerUrl) {
56
56
  return {
57
- runUrl: config.runUrl,
57
+ datalayerUrl: config.datalayerUrl,
58
58
  token: config.token,
59
59
  credits: config.credits ?? DEFAULT_DATALAYER_CONFIG.credits,
60
60
  cpuEnvironment: config.cpuEnvironment ?? DEFAULT_DATALAYER_CONFIG.cpuEnvironment,
@@ -28,8 +28,8 @@ export * from './useProjectStore';
28
28
  export * from './useMobile';
29
29
  export * from './useUsageRefreshStore';
30
30
  export * from './usePrincipalStore';
31
- export * from './useBillableAccountStore';
31
+ export * from './useBillingEntityStore';
32
32
  export * from './useSelectedPrincipal';
33
- export * from './useSelectedBillableAccount';
33
+ export * from './useSelectedBillingEntity';
34
34
  export * from './useVisibilityObserver';
35
35
  export * from './useWindowSize';
@@ -33,8 +33,8 @@ export * from './useProjectStore.js';
33
33
  export * from './useMobile.js';
34
34
  export * from './useUsageRefreshStore.js';
35
35
  export * from './usePrincipalStore.js';
36
- export * from './useBillableAccountStore.js';
36
+ export * from './useBillingEntityStore.js';
37
37
  export * from './useSelectedPrincipal.js';
38
- export * from './useSelectedBillableAccount.js';
38
+ export * from './useSelectedBillingEntity.js';
39
39
  export * from './useVisibilityObserver.js';
40
40
  export * from './useWindowSize.js';
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The kind of billing entity currently in scope.
3
+ *
4
+ * An account is the *billing* principal — only users and organizations
5
+ * are accounts. Teams are NOT accounts; when a team is the selected
6
+ * principal, the billing entity is the team's parent organization.
7
+ */
8
+ export type BillingEntityKind = 'user' | 'organization';
9
+ export type BillingEntityState = {
10
+ billingEntityKind: BillingEntityKind;
11
+ billingEntityUid?: string;
12
+ billingEntityHandle?: string;
13
+ setBillingEntity: (args: {
14
+ kind: BillingEntityKind;
15
+ uid: string;
16
+ handle: string;
17
+ }) => void;
18
+ resetBillingEntity: () => void;
19
+ };
20
+ export declare const useBillingEntityStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<BillingEntityState>, "persist"> & {
21
+ persist: {
22
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<BillingEntityState, unknown>>) => void;
23
+ clearStorage: () => void;
24
+ rehydrate: () => Promise<void> | void;
25
+ hasHydrated: () => boolean;
26
+ onHydrate: (fn: (state: BillingEntityState) => void) => () => void;
27
+ onFinishHydration: (fn: (state: BillingEntityState) => void) => () => void;
28
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<BillingEntityState, unknown>>;
29
+ };
30
+ }>;
31
+ export default useBillingEntityStore;