@datalayer/core 1.2.17 → 1.2.19

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 (122) hide show
  1. package/lib/cli-login.js +5 -51
  2. package/lib/components/anonymous/AnonymousKeyExpired.js +1 -15
  3. package/lib/components/anonymous/AnonymousKeyTimer.js +5 -15
  4. package/lib/components/auth/SignIn.js +2 -2
  5. package/lib/components/avatars/UserAvatar.js +3 -27
  6. package/lib/components/billing/BillingEntitySelect.js +6 -48
  7. package/lib/components/checkout/StripeCheckout.js +33 -217
  8. package/lib/components/contents/ContentSourceCard.js +2 -11
  9. package/lib/components/contents/HomeFolderObjectsBrowser.d.ts +1 -1
  10. package/lib/components/contents/HomeFolderObjectsBrowser.js +11 -35
  11. package/lib/components/display/CenteredSpinner.js +1 -6
  12. package/lib/components/display/DatalayerBox.js +2 -14
  13. package/lib/components/flashes/FlashClosable.js +2 -17
  14. package/lib/components/mcp/ClientBadge.js +1 -1
  15. package/lib/components/mcp/McpErrorBlankslate.js +1 -1
  16. package/lib/components/mcp/ScopeList.js +1 -1
  17. package/lib/components/notebooks/JupyterNotebook.js +1 -2
  18. package/lib/components/principal/Principal.js +2 -10
  19. package/lib/components/principal/PrincipalAppearance.js +19 -63
  20. package/lib/components/principal/PrincipalAvatar.js +5 -25
  21. package/lib/components/principal/PrincipalBadge.js +3 -13
  22. package/lib/components/principal/PrincipalBanner.js +4 -32
  23. package/lib/components/principal/PrincipalDetailsOverlay.js +7 -23
  24. package/lib/components/principal/PrincipalHoverCard.js +15 -18
  25. package/lib/components/principal/PrincipalSwitcherMenu.js +15 -83
  26. package/lib/components/progress/ConsumptionBar.js +1 -1
  27. package/lib/components/sharing/ShareAccessComponent.js +21 -229
  28. package/lib/components/sharing/SharingEditor.js +1 -1
  29. package/lib/components/spaces/SpaceDetailsOverlay.js +4 -35
  30. package/lib/components/spaces/SpaceDisplay.js +1 -8
  31. package/lib/components/storage/ContentsBrowser.js +3 -26
  32. package/lib/components/storage/ContentsTable.js +4 -23
  33. package/lib/components/students/StudentResultCircle.js +7 -29
  34. package/lib/components/timeline/Timeline.js +4 -36
  35. package/lib/hooks/layouts/LayoutBackdrop.js +2 -1
  36. package/lib/hooks/layouts/LayoutScreencapture.js +1 -1
  37. package/lib/hooks/libraryApp.d.ts +6 -1
  38. package/lib/hooks/libraryApp.js +10 -1
  39. package/lib/hooks/useAuthorization.js +6 -1
  40. package/lib/mocks/components/FlashMock.js +3 -8
  41. package/lib/mocks/views/DashboardMock.js +6 -72
  42. package/lib/mocks/views/FormMock.js +3 -2
  43. package/lib/mocks/views/TableMock.js +3 -2
  44. package/lib/mocks/views/WipMock.js +2 -1
  45. package/lib/models/Person.d.ts +9 -0
  46. package/lib/models/Person.js +23 -0
  47. package/lib/otel/index.d.ts +4 -0
  48. package/lib/otel/index.js +3 -0
  49. package/lib/otel/live/filter.d.ts +15 -0
  50. package/lib/otel/live/filter.js +16 -0
  51. package/lib/otel/live/index.d.ts +10 -0
  52. package/lib/otel/live/index.js +14 -0
  53. package/lib/otel/live/tracer.d.ts +94 -0
  54. package/lib/otel/live/tracer.js +223 -0
  55. package/lib/otel/live/useOtelLiveSpans.d.ts +6 -0
  56. package/lib/otel/live/useOtelLiveSpans.js +22 -0
  57. package/lib/otel/types.d.ts +17 -0
  58. package/lib/otel/views/OtelLive.js +4 -62
  59. package/lib/otel/views/OtelLiveSpans.d.ts +52 -0
  60. package/lib/otel/views/OtelLiveSpans.js +114 -0
  61. package/lib/otel/views/OtelLogsList.js +13 -64
  62. package/lib/otel/views/OtelMetricsChart.js +4 -19
  63. package/lib/otel/views/OtelMetricsList.js +8 -77
  64. package/lib/otel/views/OtelSearchBar.js +3 -12
  65. package/lib/otel/views/OtelSpanDetail.d.ts +6 -0
  66. package/lib/otel/views/OtelSpanDetail.js +31 -81
  67. package/lib/otel/views/OtelSpanTree.js +7 -57
  68. package/lib/otel/views/OtelSqlView.js +5 -99
  69. package/lib/otel/views/OtelSystemView.js +4 -34
  70. package/lib/otel/views/OtelTimeline.js +7 -37
  71. package/lib/otel/views/OtelTimelineRangeSlider.js +7 -140
  72. package/lib/otel/views/OtelTracesList.js +39 -87
  73. package/lib/otel/views/index.d.ts +3 -0
  74. package/lib/otel/views/index.js +2 -0
  75. package/lib/portable/index.d.ts +8 -0
  76. package/lib/portable/index.js +12 -0
  77. package/lib/state/substates/NavigationState.d.ts +6 -4
  78. package/lib/state/substates/NavigationState.js +6 -5
  79. package/lib/views/api-keys/APIKeyEdit.js +2 -11
  80. package/lib/views/api-keys/APIKeyNew.js +3 -19
  81. package/lib/views/api-keys/APIKeys.js +6 -11
  82. package/lib/views/api-keys/APIKeysStandalone.js +1 -1
  83. package/lib/views/datasources/DatasourceDetail.d.ts +1 -1
  84. package/lib/views/datasources/DatasourceDetail.js +24 -17
  85. package/lib/views/datasources/DatasourceNew.js +2 -6
  86. package/lib/views/datasources/Datasources.js +2 -14
  87. package/lib/views/iam/SignInSimple.js +8 -64
  88. package/lib/views/mcp/AdmittedClients.js +3 -16
  89. package/lib/views/mcp/AlertDestinations.js +2 -10
  90. package/lib/views/mcp/AlertRules.js +4 -9
  91. package/lib/views/mcp/ApprovalQueue.js +4 -21
  92. package/lib/views/mcp/AuditLog.js +7 -44
  93. package/lib/views/mcp/ConnectedAgents.js +1 -6
  94. package/lib/views/mcp/EnterpriseConsole.js +10 -23
  95. package/lib/views/mcp/IdentityProviders.js +7 -31
  96. package/lib/views/mcp/McpDashboard.js +14 -30
  97. package/lib/views/mcp/McpHome.js +7 -54
  98. package/lib/views/mcp/McpObservability.js +13 -85
  99. package/lib/views/mcp/NotebookRuns.js +5 -12
  100. package/lib/views/mcp/OrganizationPolicy.js +3 -9
  101. package/lib/views/mcp/OrganizationUsage.js +4 -11
  102. package/lib/views/mcp/PersonalPolicy.js +3 -3
  103. package/lib/views/mcp/Policies.js +5 -46
  104. package/lib/views/mcp/PolicyForm.js +1 -9
  105. package/lib/views/mcp/PolicyHistory.js +2 -8
  106. package/lib/views/mcp/RunDetail.js +3 -25
  107. package/lib/views/mcp/ScimProvisioning.js +3 -15
  108. package/lib/views/mcp/ServiceAgents.js +4 -17
  109. package/lib/views/mcp/TeamPolicies.js +7 -19
  110. package/lib/views/mcp/ToolAccess.js +15 -22
  111. package/lib/views/mcp/TraceTimeline.js +8 -23
  112. package/lib/views/otel/DashboardView.js +2 -2
  113. package/lib/views/otel/LogsView.js +2 -2
  114. package/lib/views/otel/MetricsView.js +2 -2
  115. package/lib/views/otel/OtelHeader.js +6 -40
  116. package/lib/views/otel/SqlView.js +2 -2
  117. package/lib/views/otel/SystemView.js +2 -2
  118. package/lib/views/otel/TracesView.js +2 -2
  119. package/lib/views/profile/UserBadge.js +6 -98
  120. package/lib/views/secrets/SecretEdit.js +2 -11
  121. package/lib/views/secrets/SecretNew.js +1 -1
  122. package/package.json +1 -1
@@ -14,11 +14,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
14
  * @module otel/OtelTracesList
15
15
  */
16
16
  import { useMemo, useCallback, useState } from 'react';
17
- import { Box, Text, Label, Spinner } from '@primer/react';
17
+ import { Box } from '@datalayer/primer-addons';
18
+ import { Text, Label, Spinner } from '@primer/react';
18
19
  import { Blankslate } from '@primer/react/experimental';
19
20
  import { TelescopeIcon, ChevronRightIcon, ChevronDownIcon, } from '@primer/octicons-react';
20
21
  import { formatDuration, formatTime, buildSpanTree } from '../utils.js';
21
22
  const GRID_COLS = '140px 1fr 160px 90px';
23
+ /** Narrower: for a panel beside other content. */
24
+ const COMPACT_GRID_COLS = '96px minmax(0, 1fr) 96px 64px';
22
25
  // ── Helpers ─────────────────────────────────────────────────────────
23
26
  /** Extract gen_ai token counts from span attributes. */
24
27
  function getTokenUsage(span) {
@@ -35,9 +38,13 @@ function getTokenUsage(span) {
35
38
  return null;
36
39
  return { input, output };
37
40
  }
38
- export const OtelTracesList = ({ spans, loading, selectedSpanId, onSelectSpan, }) => {
39
- // Set of expanded span_ids (show their children)
40
- const [expanded, setExpanded] = useState(new Set());
41
+ export const OtelTracesList = ({ spans, loading, selectedSpanId, onSelectSpan, emptyText, compact = false, defaultExpanded = false, renderSpanMark, describeSpan, }) => {
42
+ // The span_ids unfolded (or, when every span starts unfolded, folded).
43
+ const [toggled, setToggled] = useState(new Set());
44
+ const expanded = useMemo(() => ({
45
+ has: (spanId) => defaultExpanded ? !toggled.has(spanId) : toggled.has(spanId),
46
+ }), [toggled, defaultExpanded]);
47
+ const gridCols = compact ? COMPACT_GRID_COLS : GRID_COLS;
41
48
  // Build the span tree from flat input
42
49
  const roots = useMemo(() => buildSpanTree(spans), [spans]);
43
50
  // Flatten visible rows based on which nodes are expanded
@@ -58,7 +65,7 @@ export const OtelTracesList = ({ spans, loading, selectedSpanId, onSelectSpan, }
58
65
  return rows;
59
66
  }, [roots, expanded]);
60
67
  const toggleExpand = useCallback((spanId) => {
61
- setExpanded(prev => {
68
+ setToggled(prev => {
62
69
  const next = new Set(prev);
63
70
  if (next.has(spanId)) {
64
71
  next.delete(spanId);
@@ -70,23 +77,12 @@ export const OtelTracesList = ({ spans, loading, selectedSpanId, onSelectSpan, }
70
77
  });
71
78
  }, []);
72
79
  if (loading) {
73
- return (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', p: 5 }, children: _jsx(Spinner, { size: "medium" }) }));
80
+ return (_jsx(Box, { display: "flex", justifyContent: "center", p: 5, children: _jsx(Spinner, { size: "medium" }) }));
74
81
  }
75
82
  if (spans.length === 0) {
76
- return (_jsxs(Blankslate, { children: [_jsx(Blankslate.Visual, { children: _jsx(TelescopeIcon, { size: 24 }) }), _jsx(Blankslate.Heading, { children: "No traces found" }), _jsx(Blankslate.Description, { children: "Send some telemetry data first." })] }));
83
+ return (_jsxs(Blankslate, { children: [_jsx(Blankslate.Visual, { children: _jsx(TelescopeIcon, { size: 24 }) }), _jsx(Blankslate.Heading, { children: "No traces found" }), _jsx(Blankslate.Description, { children: emptyText ?? 'Send some telemetry data first.' })] }));
77
84
  }
78
- return (_jsxs(Box, { sx: { width: '100%', flex: 1, minHeight: 0, overflow: 'auto' }, children: [_jsx(Box, { sx: {
79
- display: 'grid',
80
- gridTemplateColumns: GRID_COLS,
81
- gap: 2,
82
- px: 3,
83
- position: 'sticky',
84
- top: 0,
85
- bg: 'canvas.subtle',
86
- zIndex: 1,
87
- borderBottom: '2px solid',
88
- borderColor: 'border.default',
89
- }, children: ['Time', 'Message', 'Scope', 'Duration'].map(h => (_jsx(Text, { sx: {
85
+ return (_jsxs(Box, { width: "100%", flex: 1, minHeight: 0, overflow: "auto", children: [_jsx(Box, { display: "grid", gridTemplateColumns: gridCols, gap: 2, px: 3, position: "sticky", top: 0, bg: "canvas.subtle", zIndex: 1, borderBottom: "2px solid", borderColor: "border.default", children: ['Time', 'Message', 'Scope', 'Duration'].map(h => (_jsx(Text, { sx: {
90
86
  fontWeight: 'bold',
91
87
  fontSize: 0,
92
88
  color: 'fg.muted',
@@ -97,89 +93,45 @@ export const OtelTracesList = ({ spans, loading, selectedSpanId, onSelectSpan, }
97
93
  }, children: h }, h))) }), visibleRows.map(({ span, depth, hasChildren }, idx) => {
98
94
  const isSelected = selectedSpanId === span.span_id;
99
95
  const isExpanded = expanded.has(span.span_id);
100
- const indent = depth * 20;
101
- return (_jsxs(Box, { onClick: () => {
96
+ const indent = depth * (compact ? 12 : 20);
97
+ const description = describeSpan?.(span);
98
+ return (_jsxs(Box, { "data-otel-span": span.span_id, "data-otel-span-name": span.span_name, onClick: () => {
102
99
  if (hasChildren) {
103
100
  toggleExpand(span.span_id);
104
101
  }
105
102
  onSelectSpan?.(span);
106
- }, sx: {
107
- display: 'grid',
108
- gridTemplateColumns: GRID_COLS,
109
- gap: 2,
110
- px: 3,
111
- py: '5px',
112
- cursor: 'pointer',
113
- bg: isSelected
114
- ? 'accent.subtle'
115
- : depth > 0
116
- ? 'canvas.inset'
117
- : 'canvas.default',
118
- borderBottom: '1px solid',
119
- borderColor: 'border.muted',
120
- ':hover': {
121
- bg: isSelected ? 'accent.subtle' : 'canvas.subtle',
122
- },
123
- }, children: [_jsx(Text, { sx: {
124
- fontSize: 1,
103
+ }, display: "grid", gridTemplateColumns: gridCols, gap: 2, px: 3, py: "5px", cursor: "pointer", bg: isSelected
104
+ ? 'accent.subtle'
105
+ : depth > 0
106
+ ? 'canvas.inset'
107
+ : 'canvas.default', borderBottom: "1px solid", borderColor: "border.muted", hover: { bg: isSelected ? 'accent.subtle' : 'canvas.subtle' }, children: [_jsx(Text, { sx: {
108
+ fontSize: compact ? 0 : 1,
125
109
  fontFamily: 'mono',
126
110
  color: 'fg.muted',
127
111
  whiteSpace: 'nowrap',
128
112
  lineHeight: '22px',
129
- }, children: formatTime(span.start_time) }), _jsxs(Box, { sx: {
130
- display: 'flex',
131
- alignItems: 'center',
132
- gap: 1,
133
- overflow: 'hidden',
134
- lineHeight: '22px',
135
- pl: `${indent}px`,
136
- }, children: [hasChildren ? (_jsx(Box, { sx: {
137
- flexShrink: 0,
138
- color: 'fg.muted',
139
- display: 'flex',
140
- alignItems: 'center',
141
- }, children: isExpanded ? (_jsx(ChevronDownIcon, { size: 14 })) : (_jsx(ChevronRightIcon, { size: 14 })) })) : depth > 0 ? (
113
+ }, children: formatTime(span.start_time) }), _jsxs(Box, { display: "flex", alignItems: "center", gap: 1, overflow: "hidden", lineHeight: "22px", pl: `${indent}px`, children: [hasChildren ? (_jsx(Box, { flexShrink: 0, color: "fg.muted", display: "flex", alignItems: "center", children: isExpanded ? (_jsx(ChevronDownIcon, { size: 14 })) : (_jsx(ChevronRightIcon, { size: 14 })) })) : depth > 0 ? (
142
114
  /* Connector dash for child leaves */
143
- _jsx(Box, { sx: {
144
- flexShrink: 0,
145
- width: '14px',
146
- display: 'flex',
147
- alignItems: 'center',
148
- justifyContent: 'center',
149
- color: 'fg.subtle',
150
- fontSize: 0,
151
- }, children: "\u2500" })) : null, _jsx(Text, { sx: {
115
+ _jsx(Box, { flexShrink: 0, width: "14px", display: "flex", alignItems: "center", justifyContent: "center", color: "fg.subtle", fontSize: 0, children: "\u2500" })) : null, renderSpanMark?.(span), _jsx(Text, { sx: {
152
116
  fontSize: 1,
153
117
  overflow: 'hidden',
154
118
  textOverflow: 'ellipsis',
155
119
  whiteSpace: 'nowrap',
156
- }, title: span.span_name, children: span.span_name }), span.status_code === 'ERROR' && (_jsx(Label, { variant: "danger", size: "small", children: "ERROR" })), (() => {
120
+ flexShrink: description ? 0 : 1,
121
+ maxWidth: description ? '60%' : undefined,
122
+ }, title: span.span_name, children: span.span_name }), span.in_progress && (_jsx(Label, { variant: "attention", size: "small", children: "running" })), span.status_code === 'ERROR' && (_jsx(Label, { variant: "danger", size: "small", children: "ERROR" })), (() => {
157
123
  const usage = getTokenUsage(span);
158
124
  if (!usage)
159
125
  return null;
160
- return (_jsxs(Box, { sx: {
161
- display: 'inline-flex',
162
- alignItems: 'center',
163
- gap: '2px',
164
- border: '1px solid',
165
- borderColor: 'border.default',
166
- borderRadius: 2,
167
- px: 1,
168
- fontSize: '10px',
169
- fontFamily: 'mono',
170
- color: 'fg.muted',
171
- flexShrink: 0,
172
- lineHeight: '16px',
173
- }, children: [usage.input != null && (_jsxs(Box, { sx: {
174
- display: 'inline-flex',
175
- alignItems: 'center',
176
- gap: '1px',
177
- }, children: [_jsx(Text, { sx: { fontSize: '9px', color: 'fg.subtle' }, children: "\u2197" }), _jsx(Text, { children: usage.input })] })), usage.output != null && (_jsxs(Box, { sx: {
178
- display: 'inline-flex',
179
- alignItems: 'center',
180
- gap: '1px',
181
- }, children: [_jsx(Text, { sx: { fontSize: '9px', color: 'fg.subtle' }, children: "\u2199" }), _jsx(Text, { children: usage.output })] }))] }));
182
- })(), span.otel_scope_name && (_jsx(Label, { size: "small", variant: "secondary", sx: { flexShrink: 0, fontSize: '10px' }, children: span.otel_scope_name }))] }), _jsx(Text, { sx: {
126
+ return (_jsxs(Box, { display: "inline-flex", alignItems: "center", gap: "2px", border: "1px solid", borderColor: "border.default", borderRadius: 2, px: 1, fontSize: "10px", fontFamily: "mono", color: "fg.muted", flexShrink: 0, lineHeight: "16px", children: [usage.input != null && (_jsxs(Box, { display: "inline-flex", alignItems: "center", gap: "1px", children: [_jsx(Text, { sx: { fontSize: '9px', color: 'fg.subtle' }, children: "\u2197" }), _jsx(Text, { children: usage.input })] })), usage.output != null && (_jsxs(Box, { display: "inline-flex", alignItems: "center", gap: "1px", children: [_jsx(Text, { sx: { fontSize: '9px', color: 'fg.subtle' }, children: "\u2199" }), _jsx(Text, { children: usage.output })] }))] }));
127
+ })(), description && (_jsx(Text, { sx: {
128
+ fontSize: 1,
129
+ color: 'fg.muted',
130
+ overflow: 'hidden',
131
+ textOverflow: 'ellipsis',
132
+ whiteSpace: 'nowrap',
133
+ minWidth: 0,
134
+ }, title: description, "data-otel-span-description": "", children: description })), span.otel_scope_name && !compact && (_jsx(Label, { size: "small", variant: "secondary", sx: { flexShrink: 0, fontSize: '10px' }, children: span.otel_scope_name }))] }), _jsx(Text, { sx: {
183
135
  fontSize: 1,
184
136
  color: 'fg.muted',
185
137
  overflow: 'hidden',
@@ -194,6 +146,6 @@ export const OtelTracesList = ({ spans, loading, selectedSpanId, onSelectSpan, }
194
146
  color: span.duration_ms != null && span.duration_ms > 1000
195
147
  ? 'danger.fg'
196
148
  : 'fg.default',
197
- }, children: formatDuration(span.duration_ms) })] }, `${span.trace_id}-${span.span_id}-${idx}`));
149
+ }, children: span.in_progress ? '…' : formatDuration(span.duration_ms) })] }, `${span.trace_id}-${span.span_id}-${idx}`));
198
150
  })] }));
199
151
  };
@@ -14,6 +14,9 @@ export { OtelSearchBar } from './OtelSearchBar';
14
14
  export { OtelSpanDetail } from './OtelSpanDetail';
15
15
  export { OtelSpanTree } from './OtelSpanTree';
16
16
  export { OtelTimeline } from './OtelTimeline';
17
+ export { OtelLiveSpans, DEFAULT_SPAN_FACETS, filterSpansByFacets, } from './OtelLiveSpans';
18
+ export type { OtelLiveSpansProps, OtelSpanFacet } from './OtelLiveSpans';
19
+ export { unfoldedValue } from './OtelSpanDetail';
17
20
  export { OtelTimelineRangeSlider } from './OtelTimelineRangeSlider';
18
21
  export { OtelSqlView } from './OtelSqlView';
19
22
  export type { OtelSqlViewProps } from './OtelSqlView';
@@ -16,6 +16,8 @@ export { OtelSearchBar } from './OtelSearchBar.js';
16
16
  export { OtelSpanDetail } from './OtelSpanDetail.js';
17
17
  export { OtelSpanTree } from './OtelSpanTree.js';
18
18
  export { OtelTimeline } from './OtelTimeline.js';
19
+ export { OtelLiveSpans, DEFAULT_SPAN_FACETS, filterSpansByFacets, } from './OtelLiveSpans.js';
20
+ export { unfoldedValue } from './OtelSpanDetail.js';
19
21
  export { OtelTimelineRangeSlider } from './OtelTimelineRangeSlider.js';
20
22
  export { OtelSqlView } from './OtelSqlView.js';
21
23
  export { OtelSystemView } from './OtelSystemView.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * What any JavaScript runtime can import from Core: no React, DOM, Jupyter or
3
+ * Node built-in. Re-exports only; the mobile app reaches Core through this
4
+ * entry alone.
5
+ *
6
+ * @module portable
7
+ */
8
+ export { personOf, type Person } from '../models/Person';
@@ -0,0 +1,12 @@
1
+ /*
2
+ * Copyright (c) 2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * What any JavaScript runtime can import from Core: no React, DOM, Jupyter or
7
+ * Node built-in. Re-exports only; the mobile app reaches Core through this
8
+ * entry alone.
9
+ *
10
+ * @module portable
11
+ */
12
+ export { personOf } from '../models/Person.js';
@@ -7,11 +7,13 @@
7
7
  * shows. `home` is the whole platform, shown as *Power*, at `/power`: the key
8
8
  * is kept so a cookie written before the Studio existed still names the
9
9
  * shell it meant. `agentify` is the business cases; `admin` is the
10
- * platform's administration. `loop` is LOOP: AI applications built by spec,
11
- * by drag and drop or in Python — the Studio's app pages under a sidebar of
12
- * their own, and the LOOP workspace at `/loop`.
10
+ * platform's administration. `apps` is the applications: AI applications
11
+ * built by spec, by drag and drop or in Python — the Studio's app pages under
12
+ * a sidebar of their own, every address under `/apps`, and the workspace at
13
+ * `/apps` itself. It was `loop`, at `/loop`: a cookie still saying `loop`
14
+ * names no shell and is ignored, the reader opening on the Studio.
13
15
  */
14
- export type NavigationView = 'studio' | 'home' | 'agentify' | 'admin' | 'loop';
16
+ export type NavigationView = 'studio' | 'home' | 'agentify' | 'admin' | 'apps';
15
17
  /** The shells, and where each one opens when it has not been visited. */
16
18
  export declare const NAVIGATION_VIEW_HOMES: Record<NavigationView, string>;
17
19
  /** Well-known view identifiers for {@link NavigationState.tabsByView}. */
@@ -39,8 +39,8 @@ export const NAVIGATION_VIEW_HOMES = {
39
39
  home: '/power',
40
40
  agentify: '/agentify',
41
41
  admin: '/admin',
42
- // The applications: what a builder opens LOOP for.
43
- loop: '/loop/apps',
42
+ // The applications: what a builder opens the Apps shell for.
43
+ apps: '/apps/apps',
44
44
  };
45
45
  /** Well-known view identifiers for {@link NavigationState.tabsByView}. */
46
46
  export const LIBRARY_VIEW = 'library';
@@ -59,7 +59,7 @@ const isView = (value) => value === 'studio' ||
59
59
  value === 'home' ||
60
60
  value === 'agentify' ||
61
61
  value === 'admin' ||
62
- value === 'loop';
62
+ value === 'apps';
63
63
  function readCookie(name) {
64
64
  if (typeof document === 'undefined') {
65
65
  return undefined;
@@ -247,8 +247,9 @@ export function viewForRoute(route, current) {
247
247
  if (path === '/admin' || path.startsWith('/admin/')) {
248
248
  return 'admin';
249
249
  }
250
- if (path === '/loop' || path.startsWith('/loop/')) {
251
- return 'loop';
250
+ // The Apps shell, and the hosted applications at `/apps/<slug>` with it.
251
+ if (path === '/apps' || path.startsWith('/apps/')) {
252
+ return 'apps';
252
253
  }
253
254
  return 'home';
254
255
  }
@@ -116,20 +116,11 @@ export const APIKeyEdit = ({ apiKeysListRoute = '/settings/iam/api-keys', showTi
116
116
  },
117
117
  });
118
118
  };
119
- return (_jsxs(_Fragment, { children: [showTitle && (_jsx(PageHeader, { children: _jsx(Heading, { sx: { fontSize: 3 }, children: "API Key" }) })), _jsxs(Box, { display: "flex", children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: apiKey?.name, size: 100, style: { paddingRight: 10 } }), _jsx(Text, { as: "h2", sx: { paddingTop: 3 }, children: apiKey?.name }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: apiKey?.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: apiKey?.expirationDate.toLocaleDateString(), onChange: nameNameChange, disabled: true })] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name || !validationResult.description, sx: { marginTop: 3 }, onClick: submitAPIKey, children: "Update API key" })] }), _jsxs(Box, { sx: { marginTop: 3 }, children: [_jsx(Heading, { as: "h2", sx: {
119
+ return (_jsxs(_Fragment, { children: [showTitle && (_jsx(PageHeader, { children: _jsx(Heading, { sx: { fontSize: 3 }, children: "API Key" }) })), _jsxs(Box, { display: "flex", children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: apiKey?.name, size: 100, style: { paddingRight: 10 } }), _jsx(Text, { as: "h2", sx: { paddingTop: 3 }, children: apiKey?.name }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: apiKey?.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: apiKey?.expirationDate.toLocaleDateString(), onChange: nameNameChange, disabled: true })] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name || !validationResult.description, sx: { marginTop: 3 }, onClick: submitAPIKey, children: "Update API key" })] }), _jsxs(Box, { marginTop: 3, children: [_jsx(Heading, { as: "h2", sx: {
120
120
  fontSize: 4,
121
121
  fontWeight: 'normal',
122
122
  color: 'danger.fg',
123
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 API key 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: deleteAPIKey, children: "Delete API key" })] })] })] })] })] }));
124
+ }, children: "Danger zone" }), _jsxs(Box, { border: "1px solid", borderColor: "danger.emphasis", borderRadius: 2, p: 3, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 3, children: [_jsxs(Box, { display: "grid", gap: 1, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold', color: 'danger.fg' }, children: "Confirm the API key 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: deleteAPIKey, children: "Delete API key" })] })] })] })] })] }));
134
125
  };
135
126
  export default APIKeyEdit;
@@ -95,25 +95,9 @@ export const APIKeyNew = ({ apiKeysListRoute = '/settings/iam/api-keys', showTit
95
95
  },
96
96
  });
97
97
  };
98
- return (_jsx(Box, { children: showAPIKey ? (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Your API Key Is Created" }) }) }), _jsxs(Box, { sx: {
99
- border: '1px solid',
100
- borderColor: 'accent.muted',
101
- borderRadius: 2,
102
- p: 3,
103
- bg: 'canvas.subtle',
104
- mb: 3,
105
- }, children: [_jsx(Text, { sx: { fontWeight: 600, color: 'accent.fg' }, children: "Important" }), _jsx(Text, { as: "p", sx: { mt: 1, color: 'fg.muted' }, children: "Save this API key now. You will not be able to see the full value again after leaving this page." })] }), _jsxs(Box, { sx: {
106
- border: '1px solid',
107
- borderColor: 'border.default',
108
- borderRadius: 2,
109
- p: 3,
110
- bg: 'canvas.default',
111
- }, children: [_jsxs(Box, { display: "grid", gridTemplateColumns: "minmax(120px, 180px) 1fr", sx: { rowGap: 2, columnGap: 3, mb: 3 }, children: [_jsx(Text, { sx: { color: 'fg.muted' }, children: "Name" }), _jsx(Text, { sx: { fontWeight: 600 }, children: apiKey?.name || '-' }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Description" }), _jsx(Text, { children: apiKey?.description || '-' }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Expiration date" }), _jsx(Text, { children: apiKey?.expirationDate
98
+ return (_jsx(Box, { children: showAPIKey ? (_jsxs(_Fragment, { children: [_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Your API Key Is Created" }) }) }), _jsxs(Box, { border: "1px solid", borderColor: "accent.muted", borderRadius: 2, p: 3, bg: "canvas.subtle", mb: 3, children: [_jsx(Text, { sx: { fontWeight: 600, color: 'accent.fg' }, children: "Important" }), _jsx(Text, { as: "p", sx: { mt: 1, color: 'fg.muted' }, children: "Save this API key now. You will not be able to see the full value again after leaving this page." })] }), _jsxs(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, p: 3, bg: "canvas.default", children: [_jsxs(Box, { display: "grid", gridTemplateColumns: "minmax(120px, 180px) 1fr", rowGap: 2, columnGap: 3, mb: 3, children: [_jsx(Text, { sx: { color: 'fg.muted' }, children: "Name" }), _jsx(Text, { sx: { fontWeight: 600 }, children: apiKey?.name || '-' }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Description" }), _jsx(Text, { children: apiKey?.description || '-' }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Expiration date" }), _jsx(Text, { children: apiKey?.expirationDate
112
99
  ? `${apiKey.expirationDate.toLocaleString()} (${apiKey.expirationDate.toISOString()})`
113
- : '-' })] }), _jsx(Text, { sx: { color: 'fg.muted', mb: 2 }, children: "API key value" }), _jsxs(Box, { display: "flex", sx: {
114
- alignItems: 'flex-start',
115
- gap: 2,
116
- }, children: [_jsx(Text, { as: "code", sx: {
100
+ : '-' })] }), _jsx(Text, { sx: { color: 'fg.muted', mb: 2 }, children: "API key value" }), _jsxs(Box, { display: "flex", alignItems: "flex-start", gap: 2, children: [_jsx(Text, { as: "code", sx: {
117
101
  color: 'fg.default',
118
102
  bg: 'canvas.inset',
119
103
  border: '1px solid',
@@ -133,7 +117,7 @@ export const APIKeyNew = ({ apiKeysListRoute = '/settings/iam/api-keys', showTit
133
117
  variant: 'success',
134
118
  });
135
119
  }
136
- } })] })] }), _jsx(Box, { mt: 3, children: _jsx(Button, { onClick: e => navigate(apiKeysListRoute, e), children: "List my API Keys" }) })] })) : (_jsxs(_Fragment, { children: [showTitle ? (_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "New API Key" }) }) })) : null, _jsx(Box, { display: "grid", gridTemplateColumns: "1fr 1fr", sx: { gap: 3 }, children: _jsx(Box, { children: _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Type" }), _jsxs(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: [_jsx(Select.Option, { value: "secret", children: "Secret" }), _jsx(Select.Option, { value: "publishable", disabled: true, children: "Publishable (coming soon)" }), _jsx(Select.Option, { value: "restricted", disabled: true, children: "Restricted (coming soon)" }), _jsx(Select.Option, { value: "temporary", disabled: true, children: "Temporary (coming soon)" })] }), _jsx(FormControl.Caption, { children: "Secret is currently available. Additional API key types are coming soon." })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: valueNameChange, autoFocus: true }), _jsx(FormControl.Caption, { children: "Hint: The token name is a short name that identifies in a unique way your token." }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name length must be between 2 and 32 characters." }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Expiration day" }), _jsx(Calendar, { showGoToToday: true, onSelectDate: expirationDateChange, value: formValues.expirationDate, strings: defaultCalendarStrings }), validationResult.expirationDate !== true ? (_jsx(FormControl.Validation, { variant: "error", children: "Pick an expiration date in the future." })) : (_jsxs(FormControl.Validation, { variant: "success", children: ["Expiration date:", ' ', formValues.expirationDate?.toLocaleDateString(), "."] }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: valueDescriptionChange }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name ||
120
+ } })] })] }), _jsx(Box, { mt: 3, children: _jsx(Button, { onClick: e => navigate(apiKeysListRoute, e), children: "List my API Keys" }) })] })) : (_jsxs(_Fragment, { children: [showTitle ? (_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "New API Key" }) }) })) : null, _jsx(Box, { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 3, children: _jsx(Box, { children: _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Type" }), _jsxs(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: [_jsx(Select.Option, { value: "secret", children: "Secret" }), _jsx(Select.Option, { value: "publishable", disabled: true, children: "Publishable (coming soon)" }), _jsx(Select.Option, { value: "restricted", disabled: true, children: "Restricted (coming soon)" }), _jsx(Select.Option, { value: "temporary", disabled: true, children: "Temporary (coming soon)" })] }), _jsx(FormControl.Caption, { children: "Secret is currently available. Additional API key types are coming soon." })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: valueNameChange, autoFocus: true }), _jsx(FormControl.Caption, { children: "Hint: The token name is a short name that identifies in a unique way your token." }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name length must be between 2 and 32 characters." }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Expiration day" }), _jsx(Calendar, { showGoToToday: true, onSelectDate: expirationDateChange, value: formValues.expirationDate, strings: defaultCalendarStrings }), validationResult.expirationDate !== true ? (_jsx(FormControl.Validation, { variant: "error", children: "Pick an expiration date in the future." })) : (_jsxs(FormControl.Validation, { variant: "success", children: ["Expiration date:", ' ', formValues.expirationDate?.toLocaleDateString(), "."] }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: valueDescriptionChange }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsx(Button, { variant: "primary", disabled: !validationResult.name ||
137
121
  !validationResult.description ||
138
122
  !validationResult.expirationDate, sx: { marginTop: 2 }, onClick: e => {
139
123
  e.preventDefault();
@@ -20,10 +20,10 @@ const APIKeysTable = ({ apiKeysListRoute, showTitle = true, }) => {
20
20
  const [deletingAPIKey, setDeletingAPIKey] = useState(null);
21
21
  const [deleteNameConfirm, setDeleteNameConfirm] = useState('');
22
22
  const returnFocusRef = useRef(null);
23
- const showInitialSpinner = apiKeys.length === 0
24
- && (getAPIKeysQuery.isLoading
25
- || getAPIKeysQuery.isFetching
26
- || !Array.isArray(getAPIKeysQuery.data));
23
+ const showInitialSpinner = apiKeys.length === 0 &&
24
+ (getAPIKeysQuery.isLoading ||
25
+ getAPIKeysQuery.isFetching ||
26
+ !Array.isArray(getAPIKeysQuery.data));
27
27
  useEffect(() => {
28
28
  if (getAPIKeysQuery.data) {
29
29
  const normalized = getAPIKeysQuery.data.filter((apiKey) => Boolean(apiKey));
@@ -85,12 +85,7 @@ const APIKeysTable = ({ apiKeysListRoute, showTitle = true, }) => {
85
85
  navigate(routeOf(id));
86
86
  }
87
87
  };
88
- return apiKeys.length === 0 ? (_jsx(Blankslate, { border: true, spacious: true, children: showInitialSpinner ? (_jsx(Box, { sx: {
89
- display: 'flex',
90
- alignItems: 'center',
91
- justifyContent: 'center',
92
- minHeight: '40px',
93
- }, children: _jsx(Spinner, {}) })) : (_jsxs(_Fragment, { children: [showTitle && _jsx(Blankslate.Heading, { children: "API Keys" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No API Keys found." }) })] })) })) : (_jsxs(_Fragment, { children: [_jsxs(Table.Container, { onClick: openClickedRow, sx: {
88
+ return apiKeys.length === 0 ? (_jsx(Blankslate, { border: true, spacious: true, children: showInitialSpinner ? (_jsx(Box, { display: "flex", alignItems: "center", justifyContent: "center", minHeight: "40px", children: _jsx(Spinner, {}) })) : (_jsxs(_Fragment, { children: [showTitle && _jsx(Blankslate.Heading, { children: "API Keys" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No API Keys found." }) })] })) })) : (_jsxs(_Fragment, { children: [_jsxs(Table.Container, { onClick: openClickedRow, sx: {
94
89
  '& tbody tr': { cursor: 'pointer' },
95
90
  '& tbody tr:hover': { bg: 'canvas.subtle' },
96
91
  }, children: [showTitle && (_jsxs(_Fragment, { children: [_jsx(Table.Title, { as: "h2", id: "api-keys", children: "API Keys" }), _jsx(Table.Subtitle, { as: "p", id: "api-keys-subtitle", children: "Your API keys." })] })), _jsx(DataTable, { "aria-labelledby": "api-keys", "aria-describedby": "api-keys-subtitle", data: apiKeys, columns: [
@@ -117,7 +112,7 @@ const APIKeysTable = ({ apiKeysListRoute, showTitle = true, }) => {
117
112
  {
118
113
  header: '',
119
114
  field: 'id',
120
- renderCell: apiKey => (_jsxs(Box, { display: "flex", sx: { gap: 1 }, children: [_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(routeOf(apiKey.id), e) }), _jsx(IconButton, { ref: returnFocusRef, icon: TrashIcon, "aria-label": "Delete", size: "small", variant: "invisible", sx: { color: 'danger.fg' }, onClick: () => {
115
+ renderCell: apiKey => (_jsxs(Box, { display: "flex", gap: 1, children: [_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(routeOf(apiKey.id), e) }), _jsx(IconButton, { ref: returnFocusRef, icon: TrashIcon, "aria-label": "Delete", size: "small", variant: "invisible", sx: { color: 'danger.fg' }, onClick: () => {
121
116
  setDeletingAPIKey(apiKey);
122
117
  setDeleteNameConfirm('');
123
118
  } })] })),
@@ -80,7 +80,7 @@ const APIKeysTable = () => {
80
80
  {
81
81
  header: '',
82
82
  field: 'id',
83
- renderCell: apiKey => (_jsxs(Box, { display: "flex", sx: { gap: 1 }, children: [_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(`${apiKey.id}`, e) }), _jsx(IconButton, { ref: returnFocusRef, icon: TrashIcon, "aria-label": "Delete", size: "small", variant: "invisible", sx: { color: 'danger.fg' }, onClick: () => {
83
+ renderCell: apiKey => (_jsxs(Box, { display: "flex", gap: 1, children: [_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(`${apiKey.id}`, e) }), _jsx(IconButton, { ref: returnFocusRef, icon: TrashIcon, "aria-label": "Delete", size: "small", variant: "invisible", sx: { color: 'danger.fg' }, onClick: () => {
84
84
  setDeletingAPIKey(apiKey);
85
85
  setDeleteNameConfirm('');
86
86
  } })] })),
@@ -11,5 +11,5 @@ export type DatasourceDetailProps = {
11
11
  * two people editing the same record do not overwrite each other. Running
12
12
  * queries is the Contents detail's job, not this page's.
13
13
  */
14
- export declare const DatasourceDetail: ({ sourceUid: givenUid }?: DatasourceDetailProps) => import("react").JSX.Element;
14
+ export declare const DatasourceDetail: ({ sourceUid: givenUid, }?: DatasourceDetailProps) => import("react").JSX.Element;
15
15
  export default DatasourceDetail;
@@ -14,10 +14,10 @@ import { Box } from '@datalayer/primer-addons';
14
14
  import { DATASOURCE_CONNECTOR_LABELS, } from '../../api/contents/index.js';
15
15
  import { BoringAvatar } from '../../components/avatars/index.js';
16
16
  import { useContentSource, useCredentialDiagnostics, useTestDatasource, useToast, useUpdateContentSource, } from '../../hooks/index.js';
17
- function Row({ label, children }) {
18
- return (_jsxs(Box, { sx: { display: 'grid', gridTemplateColumns: ['1fr', '200px 1fr'], gap: 1 }, children: [_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: label }), _jsx(Box, { sx: { fontSize: 1 }, children: children })] }));
17
+ function Row({ label, children, }) {
18
+ return (_jsxs(Box, { display: "grid", gridTemplateColumns: ['1fr', '200px 1fr'], gap: 1, children: [_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: label }), _jsx(Box, { fontSize: 1, children: children })] }));
19
19
  }
20
- const frame = (children) => (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsx(Box, { sx: { maxWidth: 960, mx: 'auto', width: '100%' }, children: children }) }) }));
20
+ const frame = (children) => (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsx(Box, { maxWidth: 960, mx: "auto", width: "100%", children: children }) }) }));
21
21
  /**
22
22
  * A Datasource's settings: what it connects to, and the name it goes by.
23
23
  *
@@ -27,7 +27,7 @@ const frame = (children) => (_jsx(PageLayout, { containerWidth: "full", padding:
27
27
  * two people editing the same record do not overwrite each other. Running
28
28
  * queries is the Contents detail's job, not this page's.
29
29
  */
30
- export const DatasourceDetail = ({ sourceUid: givenUid } = {}) => {
30
+ export const DatasourceDetail = ({ sourceUid: givenUid, } = {}) => {
31
31
  const params = useParams();
32
32
  const sourceUid = givenUid ?? params.sourceUid ?? params.datasourceId;
33
33
  const { enqueueToast } = useToast();
@@ -45,7 +45,9 @@ export const DatasourceDetail = ({ sourceUid: givenUid } = {}) => {
45
45
  if (source && configuration) {
46
46
  setName(source.name);
47
47
  setDescription(source.description ?? '');
48
- setRowLimit(configuration.defaultRowLimit ? String(configuration.defaultRowLimit) : '');
48
+ setRowLimit(configuration.defaultRowLimit
49
+ ? String(configuration.defaultRowLimit)
50
+ : '');
49
51
  setMaxSeconds(configuration.maxSeconds ? String(configuration.maxSeconds) : '');
50
52
  }
51
53
  }, [source, configuration]);
@@ -53,10 +55,13 @@ export const DatasourceDetail = ({ sourceUid: givenUid } = {}) => {
53
55
  return _jsx(_Fragment, {});
54
56
  }
55
57
  if (sourceQuery.isPending) {
56
- return frame(_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', alignItems: 'center', py: 6 }, children: _jsx(Spinner, { size: "large" }) }));
58
+ return frame(_jsx(Box, { display: "flex", justifyContent: "center", alignItems: "center", py: 6, children: _jsx(Spinner, { size: "large" }) }));
57
59
  }
58
- if (sourceQuery.isError || !source || !configuration || source.kind !== 'datasource') {
59
- return frame(_jsx(Box, { sx: { p: 4 }, children: _jsx(Text, { sx: { color: 'danger.fg' }, children: sourceQuery.isError
60
+ if (sourceQuery.isError ||
61
+ !source ||
62
+ !configuration ||
63
+ source.kind !== 'datasource') {
64
+ return frame(_jsx(Box, { p: 4, children: _jsx(Text, { sx: { color: 'danger.fg' }, children: sourceQuery.isError
60
65
  ? sourceQuery.error.message
61
66
  : 'Datasource not found, or this content is not a Datasource.' }) }));
62
67
  }
@@ -86,14 +91,16 @@ export const DatasourceDetail = ({ sourceUid: givenUid } = {}) => {
86
91
  onSuccess: () => enqueueToast('The Datasource is updated.', { variant: 'success' }),
87
92
  });
88
93
  };
89
- const connector = DATASOURCE_CONNECTOR_LABELS[configuration.connectorType]
90
- ?? configuration.connectorType;
91
- return frame(_jsxs(_Fragment, { children: [_jsxs(Box, { sx: { mb: 4 }, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Datasource" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "The connection as Contents holds it. The credential is never shown; the page says whether one is attached and resolvable." })] }), _jsx(Box, { sx: {
92
- border: '1px solid',
93
- borderColor: 'border.default',
94
- borderRadius: 2,
95
- bg: 'canvas.default',
96
- p: 3,
97
- }, children: _jsxs(Box, { sx: { display: 'grid', gridTemplateColumns: ['1fr', '180px 1fr'], gap: 4 }, children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: source.name, size: 100, style: { paddingRight: 10 } }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: connector }) })] }), _jsxs(Box, { sx: { display: 'grid', gap: 3 }, children: [_jsxs(Box, { sx: { display: 'grid', gap: 2 }, children: [_jsx(Row, { label: "Connector", children: _jsx(Text, { children: connector }) }), _jsxs(Row, { label: "Route", children: [_jsx(Label, { variant: configuration.networkRoute === 'dataserver' ? 'accent' : 'secondary', children: configuration.networkRoute === 'dataserver' ? 'Through a Dataserver' : 'Direct' }), configuration.dataServerUid && (_jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0, ml: 2 }, children: configuration.dataServerUid }))] }), configuration.endpoint && (_jsx(Row, { label: "Endpoint", children: _jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: configuration.endpoint }) })), configuration.databaseOrProject && (_jsx(Row, { label: configuration.connectorType === 'bigquery' ? 'Project' : 'Database', children: _jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: configuration.databaseOrProject }) })), _jsx(Row, { label: "Allowed operations", children: _jsx(Text, { children: (configuration.allowedOperations ?? []).join(', ') || '—' }) }), _jsx(Row, { label: "Credential", children: configuration.networkRoute === 'dataserver' ? (_jsx(Text, { sx: { color: 'fg.muted' }, children: "Held by the Dataserver, in your network." })) : configuration.credentialUid ? (_jsxs(Box, { sx: { display: 'flex', gap: 2, alignItems: 'center', flexWrap: 'wrap' }, children: [diagnostics.data ? (_jsx(Label, { variant: diagnostics.data.resolvable ? 'success' : 'danger', children: diagnostics.data.resolvable ? 'Resolvable' : 'Not resolvable' })) : (_jsx(Label, { variant: "success", children: "Attached" })), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: diagnostics.data?.credentialName ?? 'Held in Vault and resolved server-side.' })] })) : (_jsx(Label, { variant: "attention", children: "None" })) }), _jsx(Row, { label: "Connection", children: _jsxs(Box, { sx: { display: 'flex', gap: 2, alignItems: 'center', flexWrap: 'wrap' }, children: [_jsx(Button, { size: "small", disabled: test.isPending || !permissions.execute, onClick: () => test.mutate(source.uid), children: test.isPending ? 'Testing…' : 'Test connection' }), test.data && (_jsxs(_Fragment, { children: [_jsx(Label, { variant: test.data.ok ? 'success' : 'danger', children: test.data.ok ? 'Reachable' : 'Not reachable' }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: test.data.detail })] })), test.isError && (_jsx(Text, { sx: { color: 'danger.fg', fontSize: 0 }, children: test.error.message }))] }) })] }), _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: name, disabled: !permissions.update, onChange: event => setName(event.target.value) }), !nameValid && (_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, rows: 3, value: description, disabled: !permissions.update, onChange: event => setDescription(event.target.value) })] }), _jsxs(Box, { sx: { display: 'grid', gridTemplateColumns: ['1fr', '1fr 1fr'], gap: 3 }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Default row limit" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: rowLimit, placeholder: "Service default", disabled: !permissions.update, onChange: event => setRowLimit(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Max seconds" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: maxSeconds, placeholder: "Service default", disabled: !permissions.update, onChange: event => setMaxSeconds(event.target.value) })] })] }), update.isError && (_jsx(Text, { as: "p", sx: { color: 'danger.fg' }, children: update.error.message })), permissions.update && (_jsx(Box, { sx: { marginTop: 3 }, children: _jsx(Button, { variant: "primary", disabled: !nameValid || update.isPending, onClick: save, children: update.isPending ? 'Saving…' : 'Update Datasource' }) }))] })] })] }) })] }));
94
+ const connector = DATASOURCE_CONNECTOR_LABELS[configuration.connectorType] ?? configuration.connectorType;
95
+ return frame(_jsxs(_Fragment, { children: [_jsxs(Box, { mb: 4, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Datasource" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "The connection as Contents holds it. The credential is never shown; the page says whether one is attached and resolvable." })] }), _jsx(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, bg: "canvas.default", p: 3, children: _jsxs(Box, { display: "grid", gridTemplateColumns: ['1fr', '180px 1fr'], gap: 4, children: [_jsxs(Box, { children: [_jsx(BoringAvatar, { displayName: source.name, size: 100, style: { paddingRight: 10 } }), _jsx(Box, { mt: 3, children: _jsx(Label, { size: "large", children: connector }) })] }), _jsxs(Box, { display: "grid", gap: 3, children: [_jsxs(Box, { display: "grid", gap: 2, children: [_jsx(Row, { label: "Connector", children: _jsx(Text, { children: connector }) }), _jsxs(Row, { label: "Route", children: [_jsx(Label, { variant: configuration.networkRoute === 'dataserver'
96
+ ? 'accent'
97
+ : 'secondary', children: configuration.networkRoute === 'dataserver'
98
+ ? 'Through a Dataserver'
99
+ : 'Direct' }), configuration.dataServerUid && (_jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0, ml: 2 }, children: configuration.dataServerUid }))] }), configuration.endpoint && (_jsx(Row, { label: "Endpoint", children: _jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: configuration.endpoint }) })), configuration.databaseOrProject && (_jsx(Row, { label: configuration.connectorType === 'bigquery'
100
+ ? 'Project'
101
+ : 'Database', children: _jsx(Text, { sx: { fontFamily: 'mono', fontSize: 0 }, children: configuration.databaseOrProject }) })), _jsx(Row, { label: "Allowed operations", children: _jsx(Text, { children: (configuration.allowedOperations ?? []).join(', ') || '—' }) }), _jsx(Row, { label: "Credential", children: configuration.networkRoute === 'dataserver' ? (_jsx(Text, { sx: { color: 'fg.muted' }, children: "Held by the Dataserver, in your network." })) : configuration.credentialUid ? (_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap", children: [diagnostics.data ? (_jsx(Label, { variant: diagnostics.data.resolvable ? 'success' : 'danger', children: diagnostics.data.resolvable
102
+ ? 'Resolvable'
103
+ : 'Not resolvable' })) : (_jsx(Label, { variant: "success", children: "Attached" })), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: diagnostics.data?.credentialName ??
104
+ 'Held in Vault and resolved server-side.' })] })) : (_jsx(Label, { variant: "attention", children: "None" })) }), _jsx(Row, { label: "Connection", children: _jsxs(Box, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap", children: [_jsx(Button, { size: "small", disabled: test.isPending || !permissions.execute, onClick: () => test.mutate(source.uid), children: test.isPending ? 'Testing…' : 'Test connection' }), test.data && (_jsxs(_Fragment, { children: [_jsx(Label, { variant: test.data.ok ? 'success' : 'danger', children: test.data.ok ? 'Reachable' : 'Not reachable' }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: test.data.detail })] })), test.isError && (_jsx(Text, { sx: { color: 'danger.fg', fontSize: 0 }, children: test.error.message }))] }) })] }), _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: name, disabled: !permissions.update, onChange: event => setName(event.target.value) }), !nameValid && (_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, rows: 3, value: description, disabled: !permissions.update, onChange: event => setDescription(event.target.value) })] }), _jsxs(Box, { display: "grid", gridTemplateColumns: ['1fr', '1fr 1fr'], gap: 3, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Default row limit" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: rowLimit, placeholder: "Service default", disabled: !permissions.update, onChange: event => setRowLimit(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Max seconds" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: maxSeconds, placeholder: "Service default", disabled: !permissions.update, onChange: event => setMaxSeconds(event.target.value) })] })] }), update.isError && (_jsx(Text, { as: "p", sx: { color: 'danger.fg' }, children: update.error.message })), permissions.update && (_jsx(Box, { marginTop: 3, children: _jsx(Button, { variant: "primary", disabled: !nameValid || update.isPending, onClick: save, children: update.isPending ? 'Saving…' : 'Update Datasource' }) }))] })] })] }) })] }));
98
105
  };
99
106
  export default DatasourceDetail;
@@ -128,15 +128,11 @@ export const DatasourceNew = ({ datasourcesListRoute = '/datasources', secretsRo
128
128
  // The error is shown under the form; the idempotency key makes a retry safe.
129
129
  }
130
130
  };
131
- return (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsxs(Box, { sx: { maxWidth: 960, mx: 'auto', width: '100%' }, children: [_jsxs(Box, { sx: { mb: 4 }, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Connect a Datasource" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "A database, warehouse or query service your notebooks and agents query through Datalayer. The credential stays in Vault; a query receives a short-lived, scoped connection." }), accountPrincipal && _jsx(Box, { sx: { mt: 2 }, children: accountPrincipal })] }), _jsxs(Box, { as: "form", onSubmit: submit, sx: { display: 'grid', gap: 3 }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Connector" }), _jsx(Select, { value: connectorType, onChange: event => setConnectorType(event.target.value), children: CONNECTORS.map(connector => (_jsx(Select.Option, { value: connector, children: DATASOURCE_CONNECTOR_LABELS[connector] }, connector))) }), _jsx(FormControl.Caption, { children: labels.hint })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: name, onChange: event => setName(event.target.value), autoFocus: true }), name.length > 0 && !nameValid && (_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: description, onChange: event => setDescription(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: labels.endpoint }), _jsx(TextInput, { block: true, monospace: true, value: endpoint, placeholder: connectorType === 'sql' ? 'warehouse.internal:5432' : '', onChange: event => setEndpoint(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: labels.target }), _jsx(TextInput, { block: true, monospace: true, value: target, onChange: event => setTarget(event.target.value) })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Network route" }), _jsxs(Select, { value: route, onChange: event => setRoute(event.target.value), children: [_jsx(Select.Option, { value: "direct", children: "Direct \u2014 Datalayer reaches the endpoint" }), _jsx(Select.Option, { value: "dataserver", children: "Through a Dataserver in your network" })] }), _jsx(FormControl.Caption, { children: "A private endpoint is reached through a Dataserver, which holds the credential in the network the database lives in." })] }), route === 'direct' ? (_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Credential" }), _jsxs(Select, { value: credentialUid, onChange: event => setCredentialUid(event.target.value), children: [_jsx(Select.Option, { value: "", children: "Choose a Secret\u2026" }), (secrets.data ?? []).map(secret => (_jsx(Select.Option, { value: secret.id, children: secret.name }, secret.id)))] }), _jsxs(FormControl.Caption, { children: ["Held in Vault and resolved server-side for each query; never handed to notebook code. Add one under", ' ', _jsx(Link, { href: secretsRoute, onClick: event => {
131
+ return (_jsx(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: _jsx(PageLayout.Content, { children: _jsxs(Box, { maxWidth: 960, mx: "auto", width: "100%", children: [_jsxs(Box, { mb: 4, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Connect a Datasource" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "A database, warehouse or query service your notebooks and agents query through Datalayer. The credential stays in Vault; a query receives a short-lived, scoped connection." }), accountPrincipal && _jsx(Box, { mt: 2, children: accountPrincipal })] }), _jsxs(Box, { as: "form", onSubmit: submit, display: "grid", gap: 3, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Connector" }), _jsx(Select, { value: connectorType, onChange: event => setConnectorType(event.target.value), children: CONNECTORS.map(connector => (_jsx(Select.Option, { value: connector, children: DATASOURCE_CONNECTOR_LABELS[connector] }, connector))) }), _jsx(FormControl.Caption, { children: labels.hint })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: name, onChange: event => setName(event.target.value), autoFocus: true }), name.length > 0 && !nameValid && (_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: description, onChange: event => setDescription(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: labels.endpoint }), _jsx(TextInput, { block: true, monospace: true, value: endpoint, placeholder: connectorType === 'sql' ? 'warehouse.internal:5432' : '', onChange: event => setEndpoint(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: labels.target }), _jsx(TextInput, { block: true, monospace: true, value: target, onChange: event => setTarget(event.target.value) })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Network route" }), _jsxs(Select, { value: route, onChange: event => setRoute(event.target.value), children: [_jsx(Select.Option, { value: "direct", children: "Direct \u2014 Datalayer reaches the endpoint" }), _jsx(Select.Option, { value: "dataserver", children: "Through a Dataserver in your network" })] }), _jsx(FormControl.Caption, { children: "A private endpoint is reached through a Dataserver, which holds the credential in the network the database lives in." })] }), route === 'direct' ? (_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Credential" }), _jsxs(Select, { value: credentialUid, onChange: event => setCredentialUid(event.target.value), children: [_jsx(Select.Option, { value: "", children: "Choose a Secret\u2026" }), (secrets.data ?? []).map(secret => (_jsx(Select.Option, { value: secret.id, children: secret.name }, secret.id)))] }), _jsxs(FormControl.Caption, { children: ["Held in Vault and resolved server-side for each query; never handed to notebook code. Add one under", ' ', _jsx(Link, { href: secretsRoute, onClick: event => {
132
132
  event.preventDefault();
133
133
  navigate(secretsRoute, event);
134
134
  }, children: "Secrets" }), "."] })] })) : (_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Dataserver" }), _jsxs(Select, { value: dataServerUid, onChange: event => setDataServerUid(event.target.value), children: [_jsx(Select.Option, { value: "", children: "Choose a Dataserver\u2026" }), dataserverOptions.map(option => (_jsxs(Select.Option, { value: option.uid, children: [option.name, " (", option.state, ")"] }, option.uid)))] }), _jsx(FormControl.Caption, { children: dataserverOptions.length === 0
135
135
  ? 'No Dataserver is registered yet; register one under Dataservers first.'
136
- : 'The gateway that reaches the endpoint. Contents routes to it only while it is online.' })] })), _jsxs(CheckboxGroup, { children: [_jsx(CheckboxGroup.Label, { children: "Allowed operations" }), DATASOURCE_OPERATIONS.map(operation => (_jsxs(FormControl, { children: [_jsx(Checkbox, { value: operation, checked: operations.includes(operation), onChange: event => toggleOperation(operation, event.target.checked) }), _jsx(FormControl.Label, { sx: { textTransform: 'capitalize' }, children: operation })] }, operation))), _jsx(CheckboxGroup.Caption, { children: "The service refuses any statement outside this list before a query exists. Writes are never allowed through a Datasource." })] }), _jsxs(Box, { sx: {
137
- display: 'grid',
138
- gridTemplateColumns: ['1fr', 'repeat(3, minmax(0, 1fr))'],
139
- gap: 3,
140
- }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Default row limit" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: rowLimit, onChange: event => setRowLimit(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Max bytes" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: maxBytes, placeholder: "Unlimited", onChange: event => setMaxBytes(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Max seconds" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: maxSeconds, onChange: event => setMaxSeconds(event.target.value) })] })] }), createSource.isError && (_jsx(Flash, { variant: "danger", children: createSource.error.message })), _jsxs(Box, { sx: { display: 'flex', gap: 2 }, children: [_jsx(Button, { type: "button", onClick: event => navigate(datasourcesListRoute, event), children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", disabled: !canSubmit, children: createSource.isPending ? 'Connecting…' : 'Connect Datasource' })] })] })] }) }) }));
136
+ : 'The gateway that reaches the endpoint. Contents routes to it only while it is online.' })] })), _jsxs(CheckboxGroup, { children: [_jsx(CheckboxGroup.Label, { children: "Allowed operations" }), DATASOURCE_OPERATIONS.map(operation => (_jsxs(FormControl, { children: [_jsx(Checkbox, { value: operation, checked: operations.includes(operation), onChange: event => toggleOperation(operation, event.target.checked) }), _jsx(FormControl.Label, { sx: { textTransform: 'capitalize' }, children: operation })] }, operation))), _jsx(CheckboxGroup.Caption, { children: "The service refuses any statement outside this list before a query exists. Writes are never allowed through a Datasource." })] }), _jsxs(Box, { display: "grid", gridTemplateColumns: ['1fr', 'repeat(3, minmax(0, 1fr))'], gap: 3, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Default row limit" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: rowLimit, onChange: event => setRowLimit(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Max bytes" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: maxBytes, placeholder: "Unlimited", onChange: event => setMaxBytes(event.target.value) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Max seconds" }), _jsx(TextInput, { block: true, type: "number", min: 1, value: maxSeconds, onChange: event => setMaxSeconds(event.target.value) })] })] }), createSource.isError && (_jsx(Flash, { variant: "danger", children: createSource.error.message })), _jsxs(Box, { display: "flex", gap: 2, children: [_jsx(Button, { type: "button", onClick: event => navigate(datasourcesListRoute, event), children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", disabled: !canSubmit, children: createSource.isPending ? 'Connecting…' : 'Connect Datasource' })] })] })] }) }) }));
141
137
  };
142
138
  export default DatasourceNew;