@datalayer/core 1.0.1 → 1.0.3

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 (103) hide show
  1. package/README.md +1 -1
  2. package/lib/api/constants.d.ts +3 -0
  3. package/lib/api/constants.js +3 -0
  4. package/lib/api/index.d.ts +1 -0
  5. package/lib/api/index.js +1 -0
  6. package/lib/api/otel/index.d.ts +12 -0
  7. package/lib/api/otel/index.js +16 -0
  8. package/lib/api/otel/logs.d.ts +19 -0
  9. package/lib/api/otel/logs.js +43 -0
  10. package/lib/api/otel/metrics.d.ts +31 -0
  11. package/lib/api/otel/metrics.js +65 -0
  12. package/lib/api/otel/query.d.ts +16 -0
  13. package/lib/api/otel/query.js +37 -0
  14. package/lib/api/otel/services.d.ts +39 -0
  15. package/lib/api/otel/services.js +81 -0
  16. package/lib/api/otel/traces.d.ts +24 -0
  17. package/lib/api/otel/traces.js +53 -0
  18. package/lib/api/otel/types.d.ts +112 -0
  19. package/lib/api/otel/types.js +5 -0
  20. package/lib/api/spacer/index.d.ts +1 -2
  21. package/lib/api/spacer/index.js +1 -2
  22. package/lib/components/avatars/BoringAvatar.d.ts +3 -1
  23. package/lib/components/avatars/BoringAvatar.js +15 -14
  24. package/lib/components/avatars/BoringAvatar.stories.d.ts +2 -1
  25. package/lib/components/storage/ContentsBrowser.d.ts +6 -0
  26. package/lib/components/storage/ContentsBrowser.js +7 -8
  27. package/lib/config/Configuration.d.ts +4 -0
  28. package/lib/hooks/index.d.ts +2 -0
  29. package/lib/hooks/index.js +2 -0
  30. package/lib/hooks/useCache.d.ts +16 -40
  31. package/lib/hooks/useCache.js +28 -233
  32. package/lib/hooks/useProjectStore.d.ts +58 -0
  33. package/lib/hooks/useProjectStore.js +64 -0
  34. package/lib/hooks/useProjects.d.ts +590 -0
  35. package/lib/hooks/useProjects.js +166 -0
  36. package/lib/index.d.ts +2 -1
  37. package/lib/index.js +4 -2
  38. package/lib/models/Page.d.ts +2 -0
  39. package/lib/otel/OtelLive.d.ts +12 -0
  40. package/lib/otel/OtelLive.js +354 -0
  41. package/lib/otel/OtelLogsList.d.ts +11 -0
  42. package/lib/otel/OtelLogsList.js +137 -0
  43. package/lib/otel/OtelMetricsChart.d.ts +22 -0
  44. package/lib/otel/OtelMetricsChart.js +300 -0
  45. package/lib/otel/OtelMetricsList.d.ts +15 -0
  46. package/lib/otel/OtelMetricsList.js +213 -0
  47. package/lib/otel/OtelSearchBar.d.ts +11 -0
  48. package/lib/otel/OtelSearchBar.js +22 -0
  49. package/lib/otel/OtelSpanDetail.d.ts +11 -0
  50. package/lib/otel/OtelSpanDetail.js +172 -0
  51. package/lib/otel/OtelSpanTree.d.ts +11 -0
  52. package/lib/otel/OtelSpanTree.js +176 -0
  53. package/lib/otel/OtelSqlView.d.ts +16 -0
  54. package/lib/otel/OtelSqlView.js +239 -0
  55. package/lib/otel/OtelSystemView.d.ts +15 -0
  56. package/lib/otel/OtelSystemView.js +75 -0
  57. package/lib/otel/OtelTimeline.d.ts +11 -0
  58. package/lib/otel/OtelTimeline.js +101 -0
  59. package/lib/otel/OtelTimelineRangeSlider.d.ts +16 -0
  60. package/lib/otel/OtelTimelineRangeSlider.js +338 -0
  61. package/lib/otel/OtelTracesList.d.ts +13 -0
  62. package/lib/otel/OtelTracesList.js +199 -0
  63. package/lib/otel/hooks.d.ts +172 -0
  64. package/lib/otel/hooks.js +490 -0
  65. package/lib/otel/index.d.ts +25 -0
  66. package/lib/otel/index.js +19 -0
  67. package/lib/otel/types.d.ts +190 -0
  68. package/lib/otel/types.js +5 -0
  69. package/lib/otel/utils.d.ts +33 -0
  70. package/lib/otel/utils.js +181 -0
  71. package/lib/state/storage/IAMStorage.d.ts +2 -1
  72. package/lib/state/substates/CoreState.js +1 -0
  73. package/lib/utils/Jwt.d.ts +42 -0
  74. package/lib/utils/Jwt.js +44 -0
  75. package/lib/utils/index.d.ts +1 -0
  76. package/lib/utils/index.js +1 -0
  77. package/lib/views/iam/SignInSimple.d.ts +38 -0
  78. package/lib/views/iam/SignInSimple.js +80 -0
  79. package/lib/views/iam/index.d.ts +2 -0
  80. package/lib/views/iam/index.js +5 -0
  81. package/lib/views/iam-tokens/IAMTokenEdit.js +53 -4
  82. package/lib/views/iam-tokens/IAMTokens.js +65 -33
  83. package/lib/views/iam-tokens/Tokens.js +64 -32
  84. package/lib/views/index.d.ts +2 -1
  85. package/lib/views/index.js +2 -1
  86. package/lib/views/profile/UserBadge.d.ts +18 -0
  87. package/lib/views/profile/UserBadge.js +101 -0
  88. package/lib/views/profile/index.d.ts +2 -0
  89. package/lib/views/profile/index.js +5 -0
  90. package/lib/views/secrets/Secrets.js +1 -1
  91. package/package.json +27 -3
  92. package/lib/api/spacer/agentSpaces.d.ts +0 -193
  93. package/lib/api/spacer/agentSpaces.js +0 -127
  94. package/lib/theme/DatalayerTheme.d.ts +0 -52
  95. package/lib/theme/DatalayerTheme.js +0 -228
  96. package/lib/theme/DatalayerThemeProvider.d.ts +0 -29
  97. package/lib/theme/DatalayerThemeProvider.js +0 -54
  98. package/lib/theme/Palette.d.ts +0 -4
  99. package/lib/theme/Palette.js +0 -10
  100. package/lib/theme/index.d.ts +0 -4
  101. package/lib/theme/index.js +0 -8
  102. package/lib/theme/useSystemColorMode.d.ts +0 -9
  103. package/lib/theme/useSystemColorMode.js +0 -26
@@ -0,0 +1,5 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ export { SignInSimple } from './SignInSimple';
@@ -8,29 +8,33 @@ import { useParams } from 'react-router-dom';
8
8
  import { PageHeader, Heading, Text, Button, TextInput, FormControl, Textarea, Label, } from '@primer/react';
9
9
  import { Box } from '@datalayer/primer-addons';
10
10
  import { BoringAvatar } from '../../components/avatars';
11
- import { useCache, useToast } from '../../hooks';
11
+ import { useCache, useNavigate, useToast } from '../../hooks';
12
12
  import { useRunStore } from '../../state';
13
13
  export const IAMTokenEdit = () => {
14
14
  const { tokenId } = useParams();
15
15
  const runStore = useRunStore();
16
+ const navigate = useNavigate();
16
17
  const { enqueueToast } = useToast();
17
- const { useUpdateToken, useToken } = useCache();
18
+ const { useUpdateToken, useToken, useDeleteToken } = useCache();
18
19
  const getTokenQuery = useToken(tokenId);
19
20
  const updateTokenMutation = useUpdateToken();
21
+ const deleteTokenMutation = useDeleteToken();
20
22
  const [token, setToken] = useState();
21
23
  const [formValues, setFormValues] = useState({
22
24
  name: token?.name,
25
+ nameConfirm: '',
23
26
  description: token?.description,
24
27
  });
25
28
  const [validationResult, setValidationResult] = useState({
26
29
  name: undefined,
30
+ nameConfirm: undefined,
27
31
  description: undefined,
28
32
  });
29
33
  useEffect(() => {
30
34
  if (getTokenQuery.data) {
31
35
  const token = getTokenQuery.data;
32
36
  setToken(token);
33
- setFormValues({ ...token });
37
+ setFormValues({ ...token, nameConfirm: '' });
34
38
  }
35
39
  }, [getTokenQuery.data]);
36
40
  const nameNameChange = (event) => {
@@ -39,6 +43,12 @@ export const IAMTokenEdit = () => {
39
43
  name: event.target.value,
40
44
  }));
41
45
  };
46
+ const nameConfirmChange = (event) => {
47
+ setFormValues(prevFormValues => ({
48
+ ...prevFormValues,
49
+ nameConfirm: event.target.value,
50
+ }));
51
+ };
42
52
  const nameDescriptionChange = (event) => {
43
53
  setFormValues(prevFormValues => ({
44
54
  ...prevFormValues,
@@ -53,6 +63,7 @@ export const IAMTokenEdit = () => {
53
63
  : formValues.name.length > 2
54
64
  ? true
55
65
  : false,
66
+ nameConfirm: formValues.nameConfirm === token?.name ? true : false,
56
67
  description: formValues.description === undefined
57
68
  ? undefined
58
69
  : formValues.description.length > 2
@@ -81,6 +92,44 @@ export const IAMTokenEdit = () => {
81
92
  },
82
93
  });
83
94
  };
84
- return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(Heading, { sx: { fontSize: 3 }, children: "IAM Token" }) }), _jsxs(Box, { display: "flex", children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: token?.name, size: 100, style: { paddingRight: 10 } }), _jsx(Text, { as: "h2", sx: { paddingTop: 3 }, children: token?.name }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: token?.variant }) })] }), _jsx(Box, { ml: 10, children: _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: nameNameChange }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name must have more than 2 characters." }))] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: nameDescriptionChange, rows: 5 }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Expiration date" }), _jsx(TextInput, { block: true, value: token?.expirationDate.toLocaleDateString(), onChange: nameNameChange, disabled: true })] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name || !validationResult.description, sx: { marginTop: 3 }, onClick: nameSubmit, children: "Update token" })] }) })] })] }));
95
+ const handleDelete = async () => {
96
+ runStore.layout().showBackdrop('Deleting the token...');
97
+ deleteTokenMutation.mutate(token.id, {
98
+ onSuccess: (resp) => {
99
+ if (resp.success) {
100
+ enqueueToast('The token is successfully deleted.', {
101
+ variant: 'success',
102
+ });
103
+ navigate('/settings/iam/tokens');
104
+ }
105
+ else {
106
+ enqueueToast(resp.message || 'Failed to delete token.', {
107
+ variant: 'error',
108
+ });
109
+ }
110
+ },
111
+ onError: () => {
112
+ enqueueToast('Failed to delete token.', { variant: 'error' });
113
+ },
114
+ onSettled: () => {
115
+ runStore.layout().hideBackdrop();
116
+ },
117
+ });
118
+ };
119
+ return (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(Heading, { sx: { fontSize: 3 }, children: "IAM Token" }) }), _jsxs(Box, { display: "flex", children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: token?.name, size: 100, style: { paddingRight: 10 } }), _jsx(Text, { as: "h2", sx: { paddingTop: 3 }, children: token?.name }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: token?.variant }) })] }), _jsxs(Box, { ml: 10, children: [_jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: nameNameChange }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name must have more than 2 characters." }))] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: nameDescriptionChange, rows: 5 }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Expiration date" }), _jsx(TextInput, { block: true, value: token?.expirationDate.toLocaleDateString(), onChange: nameNameChange, disabled: true })] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name || !validationResult.description, sx: { marginTop: 3 }, onClick: nameSubmit, children: "Update token" })] }), _jsxs(Box, { sx: { marginTop: 3 }, children: [_jsx(Heading, { as: "h2", sx: {
120
+ fontSize: 4,
121
+ fontWeight: 'normal',
122
+ color: 'danger.fg',
123
+ mb: 2,
124
+ }, children: "Danger zone" }), _jsxs(Box, { sx: {
125
+ border: '1px solid',
126
+ borderColor: 'danger.emphasis',
127
+ borderRadius: 2,
128
+ p: 3,
129
+ display: 'flex',
130
+ alignItems: 'center',
131
+ justifyContent: 'space-between',
132
+ gap: 3,
133
+ }, children: [_jsxs(Box, { sx: { display: 'grid', gap: 1 }, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold', color: 'danger.fg' }, children: "Confirm the token name to delete" }), _jsx(FormControl, { children: _jsx(TextInput, { block: true, value: formValues.nameConfirm, onChange: nameConfirmChange }) }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "This operation is not reversible." })] }), _jsx(Button, { variant: "danger", disabled: !validationResult.nameConfirm, onClick: handleDelete, children: "Delete token" })] })] })] })] })] }));
85
134
  };
86
135
  export default IAMTokenEdit;
@@ -1,52 +1,84 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  /*
3
3
  * Copyright (c) 2023-2025 Datalayer, Inc.
4
4
  * Distributed under the terms of the Modified BSD License.
5
5
  */
6
- import { useState, useEffect } from 'react';
7
- import { PageLayout, Button, IconButton, Text, Label, RelativeTime, } from '@primer/react';
6
+ import { useState, useEffect, useRef } from 'react';
7
+ import { PageLayout, Button, IconButton, Text, Label, RelativeTime, ConfirmationDialog, } from '@primer/react';
8
8
  import { Blankslate, PageHeader, Table, DataTable, } from '@primer/react/experimental';
9
9
  import { Box } from '@datalayer/primer-addons';
10
10
  import { EditIcon } from '@datalayer/icons-react';
11
- import { useCache, useNavigate } from '../../hooks';
11
+ import { TrashIcon } from '@primer/octicons-react';
12
+ import { useCache, useNavigate, useToast } from '../../hooks';
12
13
  const TokensTable = ({ tokensListRoute }) => {
13
- const { useTokens } = useCache();
14
+ const { useTokens, useDeleteToken } = useCache();
15
+ const { enqueueToast } = useToast();
14
16
  const getTokensQuery = useTokens();
17
+ const deleteTokenMutation = useDeleteToken();
15
18
  const navigate = useNavigate();
16
19
  const [tokens, setTokens] = useState([]);
20
+ const [deletingToken, setDeletingToken] = useState(null);
21
+ const returnFocusRef = useRef(null);
17
22
  useEffect(() => {
18
23
  if (getTokensQuery.data) {
19
24
  setTokens(getTokensQuery.data);
20
25
  }
21
26
  }, [getTokensQuery.data]);
22
- return tokens.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Heading, { children: "IAM Tokens" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No IAM Tokens found." }) })] })) : (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h2", id: "tokens", children: "IAM Tokens" }), _jsx(Table.Subtitle, { as: "p", id: "tokens-subtitle", children: "Your tokens." }), _jsx(DataTable, { "aria-labelledby": "teams", "aria-describedby": "teams-subtitle", data: tokens, columns: [
23
- {
24
- header: 'Type',
25
- field: 'variant',
26
- renderCell: token => _jsx(Label, { children: token.variant }),
27
- },
28
- {
29
- header: 'Name',
30
- field: 'name',
31
- rowHeader: true,
32
- },
33
- {
34
- header: 'Description',
35
- field: 'description',
36
- },
37
- {
38
- header: 'Expiration date',
39
- field: 'expirationDate',
40
- renderCell: token => (_jsx(RelativeTime, { date: new Date(token.expirationDate) })),
41
- },
42
- {
43
- header: '',
44
- field: 'id',
45
- renderCell: token => (_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(tokensListRoute
46
- ? `${tokensListRoute}/${token.id}`
47
- : `${token.id}`, e) })),
48
- },
49
- ] })] }));
27
+ const handleDeleteConfirm = () => {
28
+ if (!deletingToken)
29
+ return;
30
+ deleteTokenMutation.mutate(deletingToken.id, {
31
+ onSuccess: (resp) => {
32
+ if (resp.success) {
33
+ enqueueToast(`Token "${deletingToken.name}" deleted.`, {
34
+ variant: 'success',
35
+ });
36
+ }
37
+ else {
38
+ enqueueToast(resp.message || 'Failed to delete token.', {
39
+ variant: 'error',
40
+ });
41
+ }
42
+ },
43
+ onError: () => {
44
+ enqueueToast('Failed to delete token.', { variant: 'error' });
45
+ },
46
+ onSettled: () => setDeletingToken(null),
47
+ });
48
+ };
49
+ return tokens.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Heading, { children: "IAM Tokens" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No IAM Tokens found." }) })] })) : (_jsxs(_Fragment, { children: [_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h2", id: "tokens", children: "IAM Tokens" }), _jsx(Table.Subtitle, { as: "p", id: "tokens-subtitle", children: "Your tokens." }), _jsx(DataTable, { "aria-labelledby": "teams", "aria-describedby": "teams-subtitle", data: tokens, columns: [
50
+ {
51
+ header: 'Type',
52
+ field: 'variant',
53
+ renderCell: token => _jsx(Label, { children: token.variant }),
54
+ },
55
+ {
56
+ header: 'Name',
57
+ field: 'name',
58
+ rowHeader: true,
59
+ },
60
+ {
61
+ header: 'Description',
62
+ field: 'description',
63
+ },
64
+ {
65
+ header: 'Expiration date',
66
+ field: 'expirationDate',
67
+ renderCell: token => (_jsx(RelativeTime, { date: new Date(token.expirationDate) })),
68
+ },
69
+ {
70
+ header: '',
71
+ field: 'id',
72
+ renderCell: token => (_jsxs(Box, { display: "flex", sx: { gap: 1 }, children: [_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(tokensListRoute
73
+ ? `${tokensListRoute}/${token.id}`
74
+ : `${token.id}`, e) }), _jsx(IconButton, { ref: returnFocusRef, icon: TrashIcon, "aria-label": "Delete", size: "small", variant: "invisible", sx: { color: 'danger.fg' }, onClick: () => setDeletingToken(token) })] })),
75
+ },
76
+ ] })] }), deletingToken && (_jsxs(ConfirmationDialog, { title: "Delete token", onClose: gesture => {
77
+ if (gesture === 'confirm')
78
+ handleDeleteConfirm();
79
+ else
80
+ setDeletingToken(null);
81
+ }, confirmButtonContent: "Delete", confirmButtonType: "danger", children: ["Are you sure you want to delete the token", ' ', _jsx("strong", { children: deletingToken.name }), "? This action cannot be undone."] }))] }));
50
82
  };
51
83
  export const IAMTokens = ({ newTokenRoute = '/new/token', tokensListRoute, } = {}) => {
52
84
  const navigate = useNavigate();
@@ -1,53 +1,85 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  /*
3
3
  * Copyright (c) 2023-2025 Datalayer, Inc.
4
4
  * Distributed under the terms of the Modified BSD License.
5
5
  */
6
- import { useState, useEffect } from 'react';
7
- import { PageLayout, Button, IconButton, Text, Label, RelativeTime, } from '@primer/react';
6
+ import { useState, useEffect, useRef } from 'react';
7
+ import { PageLayout, Button, IconButton, Text, Label, RelativeTime, ConfirmationDialog, } from '@primer/react';
8
8
  import { Blankslate, PageHeader, Table, DataTable, } from '@primer/react/experimental';
9
9
  import { Box } from '@datalayer/primer-addons';
10
10
  import { EditIcon } from '@datalayer/icons-react';
11
- import { useCache, useNavigate } from '../../hooks';
11
+ import { TrashIcon } from '@primer/octicons-react';
12
+ import { useCache, useNavigate, useToast } from '../../hooks';
12
13
  const TokensTable = () => {
13
- const { useTokens } = useCache();
14
+ const { useTokens, useDeleteToken } = useCache();
15
+ const { enqueueToast } = useToast();
14
16
  const getTokensQuery = useTokens();
17
+ const deleteTokenMutation = useDeleteToken();
15
18
  const navigate = useNavigate();
16
19
  const [tokens, setTokens] = useState([]);
20
+ const [deletingToken, setDeletingToken] = useState(null);
21
+ const returnFocusRef = useRef(null);
17
22
  useEffect(() => {
18
23
  if (getTokensQuery.data) {
19
24
  setTokens(getTokensQuery.data);
20
25
  }
21
26
  }, [getTokensQuery.data]);
22
- return tokens.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Heading, { children: "Tokens" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No Tokens found." }) })] })) : (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h2", id: "tokens", children: "Tokens" }), _jsx(Table.Subtitle, { as: "p", id: "tokens-subtitle", children: "Your tokens." }), _jsx(DataTable, { "aria-labelledby": "teams", "aria-describedby": "teams-subtitle", data: tokens, columns: [
23
- {
24
- header: 'Type',
25
- field: 'variant',
26
- renderCell: token => _jsx(Label, { children: token.variant }),
27
- },
28
- {
29
- header: 'Name',
30
- field: 'name',
31
- rowHeader: true,
32
- },
33
- {
34
- header: 'Description',
35
- field: 'description',
36
- },
37
- {
38
- header: 'Expiration date',
39
- field: 'expirationDate',
40
- renderCell: token => (_jsx(RelativeTime, { date: new Date(token.expirationDate) })),
41
- },
42
- {
43
- header: '',
44
- field: 'id',
45
- renderCell: token => (_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(`${token.id}`, e) })),
46
- },
47
- ] })] }));
27
+ const handleDeleteConfirm = () => {
28
+ if (!deletingToken)
29
+ return;
30
+ deleteTokenMutation.mutate(deletingToken.id, {
31
+ onSuccess: (resp) => {
32
+ if (resp.success) {
33
+ enqueueToast(`Token "${deletingToken.name}" deleted.`, {
34
+ variant: 'success',
35
+ });
36
+ }
37
+ else {
38
+ enqueueToast(resp.message || 'Failed to delete token.', {
39
+ variant: 'error',
40
+ });
41
+ }
42
+ },
43
+ onError: () => {
44
+ enqueueToast('Failed to delete token.', { variant: 'error' });
45
+ },
46
+ onSettled: () => setDeletingToken(null),
47
+ });
48
+ };
49
+ return tokens.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Heading, { children: "Tokens" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No Tokens found." }) })] })) : (_jsxs(_Fragment, { children: [_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h2", id: "tokens", children: "Tokens" }), _jsx(Table.Subtitle, { as: "p", id: "tokens-subtitle", children: "Your tokens." }), _jsx(DataTable, { "aria-labelledby": "teams", "aria-describedby": "teams-subtitle", data: tokens, columns: [
50
+ {
51
+ header: 'Type',
52
+ field: 'variant',
53
+ renderCell: token => _jsx(Label, { children: token.variant }),
54
+ },
55
+ {
56
+ header: 'Name',
57
+ field: 'name',
58
+ rowHeader: true,
59
+ },
60
+ {
61
+ header: 'Description',
62
+ field: 'description',
63
+ },
64
+ {
65
+ header: 'Expiration date',
66
+ field: 'expirationDate',
67
+ renderCell: token => (_jsx(RelativeTime, { date: new Date(token.expirationDate) })),
68
+ },
69
+ {
70
+ header: '',
71
+ field: 'id',
72
+ renderCell: token => (_jsxs(Box, { display: "flex", sx: { gap: 1 }, children: [_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(`${token.id}`, e) }), _jsx(IconButton, { ref: returnFocusRef, icon: TrashIcon, "aria-label": "Delete", size: "small", variant: "invisible", sx: { color: 'danger.fg' }, onClick: () => setDeletingToken(token) })] })),
73
+ },
74
+ ] })] }), deletingToken && (_jsxs(ConfirmationDialog, { title: "Delete token", onClose: gesture => {
75
+ if (gesture === 'confirm')
76
+ handleDeleteConfirm();
77
+ else
78
+ setDeletingToken(null);
79
+ }, confirmButtonContent: "Delete", confirmButtonType: "danger", children: ["Are you sure you want to delete the token", ' ', _jsx("strong", { children: deletingToken.name }), "? This action cannot be undone."] }))] }));
48
80
  };
49
81
  export const Tokens = () => {
50
82
  const navigate = useNavigate();
51
- return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Tokens" }) }), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate('/new/token', e), children: "New token" }) })] }) }), _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(TokensTable, {}) }) })] }));
83
+ return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Tokens" }) }), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate('/tokens/new', e), children: "New token" }) })] }) }), _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(TokensTable, {}) }) })] }));
52
84
  };
53
85
  export default Tokens;
@@ -1 +1,2 @@
1
- export {};
1
+ export * from './iam';
2
+ export * from './profile';
@@ -1,5 +1,6 @@
1
- export {};
2
1
  /*
3
2
  * Copyright (c) 2023-2025 Datalayer, Inc.
4
3
  * Distributed under the terms of the Modified BSD License.
5
4
  */
5
+ export * from './iam';
6
+ export * from './profile';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * UserBadge – Shows the authenticated user's display name.
3
+ *
4
+ * On hover, reveals a JWT details popover with email, handle, UID,
5
+ * issued/expiry timestamps, roles, and the raw decoded claims.
6
+ *
7
+ * The trigger text and the popover share a single container so the
8
+ * popover stays open while the mouse travels from the label into it.
9
+ *
10
+ * @module views/profile
11
+ */
12
+ import React from 'react';
13
+ export interface UserBadgeProps {
14
+ /** Raw JWT bearer token. */
15
+ token: string;
16
+ }
17
+ export declare const UserBadge: React.FC<UserBadgeProps>;
18
+ export default UserBadge;
@@ -0,0 +1,101 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, 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
+ * UserBadge – Shows the authenticated user's display name.
8
+ *
9
+ * On hover, reveals a JWT details popover with email, handle, UID,
10
+ * issued/expiry timestamps, roles, and the raw decoded claims.
11
+ *
12
+ * The trigger text and the popover share a single container so the
13
+ * popover stays open while the mouse travels from the label into it.
14
+ *
15
+ * @module views/profile
16
+ */
17
+ import { useState, useRef, useCallback } from 'react';
18
+ import { Box, Text } from '@primer/react';
19
+ import { parseJwtPayload, getDatalayerJwtUser, getDatalayerDisplayName, } from '../../utils/Jwt';
20
+ // ── Component ─────────────────────────────────────────────────────
21
+ export const UserBadge = ({ token }) => {
22
+ const [open, setOpen] = useState(false);
23
+ const closeTimer = useRef(null);
24
+ const handleEnter = useCallback(() => {
25
+ if (closeTimer.current)
26
+ clearTimeout(closeTimer.current);
27
+ setOpen(true);
28
+ }, []);
29
+ const handleLeave = useCallback(() => {
30
+ // Small delay so the mouse can travel from the label into the popover
31
+ // without it closing.
32
+ closeTimer.current = setTimeout(() => setOpen(false), 120);
33
+ }, []);
34
+ const user = getDatalayerJwtUser(token);
35
+ const displayName = getDatalayerDisplayName(user, user?.handle ?? '');
36
+ const claims = parseJwtPayload(token);
37
+ return (_jsxs(Box, { sx: { position: 'relative' }, onMouseEnter: handleEnter, onMouseLeave: handleLeave, children: [_jsx(Text, { sx: {
38
+ fontSize: 1,
39
+ color: 'fg.muted',
40
+ cursor: 'default',
41
+ borderBottom: '1px dashed',
42
+ borderColor: 'border.muted',
43
+ pb: '1px',
44
+ userSelect: 'none',
45
+ }, children: displayName }), open && claims && (_jsxs(Box, { sx: {
46
+ position: 'absolute',
47
+ top: 'calc(100% + 4px)',
48
+ right: 0,
49
+ zIndex: 100,
50
+ width: '400px',
51
+ bg: 'canvas.overlay',
52
+ border: '1px solid',
53
+ borderColor: 'border.default',
54
+ borderRadius: 2,
55
+ boxShadow: 'shadow.large',
56
+ overflow: 'hidden',
57
+ }, children: [_jsx(Box, { sx: {
58
+ px: 3,
59
+ py: 2,
60
+ bg: 'canvas.subtle',
61
+ borderBottom: '1px solid',
62
+ borderColor: 'border.default',
63
+ }, children: _jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1 }, children: "JWT Claims" }) }), claims.user && (_jsx(Box, { sx: {
64
+ px: 3,
65
+ py: 2,
66
+ borderBottom: '1px solid',
67
+ borderColor: 'border.muted',
68
+ }, children: _jsxs(Box, { sx: {
69
+ display: 'grid',
70
+ gridTemplateColumns: '90px 1fr',
71
+ gap: 1,
72
+ fontSize: 0,
73
+ }, children: [claims.user.email && (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { color: 'fg.muted' }, children: "Email" }), _jsx(Text, { sx: { fontFamily: 'mono' }, children: claims.user.email })] })), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Handle" }), _jsx(Text, { sx: { fontFamily: 'mono' }, children: claims.user.handle }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "UID" }), _jsx(Text, { sx: { fontFamily: 'mono', wordBreak: 'break-all' }, children: claims.user.uid }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Issued" }), _jsx(Text, { sx: { fontFamily: 'mono' }, children: new Date(claims.iat * 1000).toISOString() }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Expires" }), _jsx(Text, { sx: { fontFamily: 'mono' }, children: new Date(claims.exp * 1000).toISOString() })] }) })), (claims.user?.roles ?? claims.roles ?? []).length > 0 && (_jsxs(Box, { sx: {
74
+ px: 3,
75
+ py: 2,
76
+ borderBottom: '1px solid',
77
+ borderColor: 'border.muted',
78
+ }, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block', mb: 1 }, children: "Roles" }), _jsx(Box, { sx: { display: 'flex', flexWrap: 'wrap', gap: 1 }, children: (claims.user?.roles ?? claims.roles ?? []).map(r => (_jsx(Box, { sx: {
79
+ fontSize: 0,
80
+ fontFamily: 'mono',
81
+ px: 1,
82
+ py: '1px',
83
+ bg: 'accent.subtle',
84
+ color: 'accent.fg',
85
+ borderRadius: 1,
86
+ border: '1px solid',
87
+ borderColor: 'accent.muted',
88
+ }, children: r }, r))) })] })), _jsx(Box, { as: "pre", sx: {
89
+ m: 0,
90
+ px: 3,
91
+ py: 2,
92
+ fontFamily: 'mono',
93
+ fontSize: 0,
94
+ color: 'fg.default',
95
+ bg: 'canvas.inset',
96
+ overflow: 'auto',
97
+ maxHeight: '220px',
98
+ whiteSpace: 'pre',
99
+ }, children: JSON.stringify(claims, null, 2) })] }))] }));
100
+ };
101
+ export default UserBadge;
@@ -0,0 +1,2 @@
1
+ export { UserBadge } from './UserBadge';
2
+ export type { UserBadgeProps } from './UserBadge';
@@ -0,0 +1,5 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ export { UserBadge } from './UserBadge';
@@ -43,6 +43,6 @@ const SecretsTable = () => {
43
43
  };
44
44
  export const Secrets = () => {
45
45
  const navigate = useNavigate();
46
- return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Secrets" }) }), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate('/new/secret', e), children: "New secret" }) })] }) }), _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(SecretsTable, {}) }) })] }));
46
+ return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Secrets" }) }), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate('/secrets/new', e), children: "New secret" }) })] }) }), _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(SecretsTable, {}) }) })] }));
47
47
  };
48
48
  export default Secrets;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/core",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  ".",
@@ -28,6 +28,30 @@
28
28
  ],
29
29
  "main": "lib/index.js",
30
30
  "types": "lib/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./lib/index.d.ts",
34
+ "default": "./lib/index.js"
35
+ },
36
+ "./otel": {
37
+ "types": "./lib/otel/index.d.ts",
38
+ "default": "./lib/otel/index.js"
39
+ },
40
+ "./views/iam": {
41
+ "types": "./lib/views/iam/index.d.ts",
42
+ "default": "./lib/views/iam/index.js"
43
+ },
44
+ "./views/profile": {
45
+ "types": "./lib/views/profile/index.d.ts",
46
+ "default": "./lib/views/profile/index.js"
47
+ },
48
+ "./utils/jwt": {
49
+ "types": "./lib/utils/Jwt.d.ts",
50
+ "default": "./lib/utils/Jwt.js"
51
+ },
52
+ "./style/*": "./style/*",
53
+ "./*": "./*"
54
+ },
31
55
  "style": "style/index.css",
32
56
  "repository": {
33
57
  "type": "git",
@@ -54,7 +78,7 @@
54
78
  "build": "npm run clean && gulp resources-to-lib && tsc -b && vite build",
55
79
  "build:lib": "npm run clean:lib && gulp resources-to-lib && tsc -b && node scripts/fix-esm-imports.cjs",
56
80
  "build:types": "npm run clean:lib && tsc -b",
57
- "build:nextjs": "npm run build --workspace=nextjs-notebook-example",
81
+ "build:nextjs": "npm run build --workspace=nextjj-example",
58
82
  "build:examples": "npm run build:nextjs",
59
83
  "build:all": "npm run build && npm run build:examples",
60
84
  "lint": "eslint . --quiet",
@@ -90,7 +114,7 @@
90
114
  "type-check:watch": "tsc -b -w --noEmit",
91
115
  "examples": "run-p jupyter:start examples:vite",
92
116
  "examples:vite": "VITE_DATALAYER_RUN_URL=http://localhost:8888 vite --config vite.examples.config.ts",
93
- "examples:nextjs": "npm run dev --workspace=nextjs-notebook-example",
117
+ "examples:nextjs": "npm run dev --workspace=nextjj-example",
94
118
  "jupyter:start": "./dev/sh/start-jupyter-server.sh",
95
119
  "prepare": "husky || true",
96
120
  "kill": "./dev/sh/kill.sh || true",