@datalayer/core 1.2.18 → 1.2.20

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 (228) 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.d.ts +10 -11
  6. package/lib/components/avatars/UserAvatar.js +23 -52
  7. package/lib/components/avatars/UserProfileAvatar.js +1 -1
  8. package/lib/components/avatars/avatarChoice.d.ts +42 -0
  9. package/lib/components/avatars/avatarChoice.js +65 -0
  10. package/lib/components/avatars/index.d.ts +1 -0
  11. package/lib/components/avatars/index.js +1 -0
  12. package/lib/components/billing/BillingEntitySelect.js +6 -48
  13. package/lib/components/checkout/StripeCheckout.js +33 -217
  14. package/lib/components/collaboration/LiveEditorCollaborators.d.ts +3 -4
  15. package/lib/components/collaboration/LiveEditorCollaborators.js +7 -5
  16. package/lib/components/contents/ContentSourceCard.js +2 -11
  17. package/lib/components/contents/HomeFolderObjectsBrowser.d.ts +1 -1
  18. package/lib/components/contents/HomeFolderObjectsBrowser.js +11 -35
  19. package/lib/components/display/CenteredSpinner.js +1 -6
  20. package/lib/components/display/DatalayerBox.js +2 -14
  21. package/lib/components/emoji/faceEmojis.js +48 -0
  22. package/lib/components/emoji/fluent/loaders.js +46 -0
  23. package/lib/components/emoji/fluent/svg/1f30a.d.ts +2 -0
  24. package/lib/components/emoji/fluent/svg/1f30a.js +5 -0
  25. package/lib/components/emoji/fluent/svg/1f310.d.ts +2 -0
  26. package/lib/components/emoji/fluent/svg/1f310.js +5 -0
  27. package/lib/components/emoji/fluent/svg/1f324.d.ts +2 -0
  28. package/lib/components/emoji/fluent/svg/1f324.js +5 -0
  29. package/lib/components/emoji/fluent/svg/1f326.d.ts +2 -0
  30. package/lib/components/emoji/fluent/svg/1f326.js +5 -0
  31. package/lib/components/emoji/fluent/svg/1f327.d.ts +2 -0
  32. package/lib/components/emoji/fluent/svg/1f327.js +5 -0
  33. package/lib/components/emoji/fluent/svg/1f33e.d.ts +2 -0
  34. package/lib/components/emoji/fluent/svg/1f33e.js +5 -0
  35. package/lib/components/emoji/fluent/svg/1f372.d.ts +2 -0
  36. package/lib/components/emoji/fluent/svg/1f372.js +5 -0
  37. package/lib/components/emoji/fluent/svg/1f373.d.ts +2 -0
  38. package/lib/components/emoji/fluent/svg/1f373.js +5 -0
  39. package/lib/components/emoji/fluent/svg/1f39b.d.ts +2 -0
  40. package/lib/components/emoji/fluent/svg/1f39b.js +5 -0
  41. package/lib/components/emoji/fluent/svg/1f3ab.d.ts +2 -0
  42. package/lib/components/emoji/fluent/svg/1f3ab.js +5 -0
  43. package/lib/components/emoji/fluent/svg/1f41b.d.ts +2 -0
  44. package/lib/components/emoji/fluent/svg/1f41b.js +5 -0
  45. package/lib/components/emoji/fluent/svg/1f441.d.ts +2 -0
  46. package/lib/components/emoji/fluent/svg/1f441.js +5 -0
  47. package/lib/components/emoji/fluent/svg/1f465.d.ts +2 -0
  48. package/lib/components/emoji/fluent/svg/1f465.js +5 -0
  49. package/lib/components/emoji/fluent/svg/1f4b9.d.ts +2 -0
  50. package/lib/components/emoji/fluent/svg/1f4b9.js +5 -0
  51. package/lib/components/emoji/fluent/svg/1f4be.d.ts +2 -0
  52. package/lib/components/emoji/fluent/svg/1f4be.js +5 -0
  53. package/lib/components/emoji/fluent/svg/1f4c1.d.ts +2 -0
  54. package/lib/components/emoji/fluent/svg/1f4c1.js +5 -0
  55. package/lib/components/emoji/fluent/svg/1f4c2.d.ts +2 -0
  56. package/lib/components/emoji/fluent/svg/1f4c2.js +5 -0
  57. package/lib/components/emoji/fluent/svg/1f4c4.d.ts +2 -0
  58. package/lib/components/emoji/fluent/svg/1f4c4.js +5 -0
  59. package/lib/components/emoji/fluent/svg/1f4d3.d.ts +2 -0
  60. package/lib/components/emoji/fluent/svg/1f4d3.js +5 -0
  61. package/lib/components/emoji/fluent/svg/1f4d6.d.ts +2 -0
  62. package/lib/components/emoji/fluent/svg/1f4d6.js +5 -0
  63. package/lib/components/emoji/fluent/svg/1f4dc.d.ts +2 -0
  64. package/lib/components/emoji/fluent/svg/1f4dc.js +5 -0
  65. package/lib/components/emoji/fluent/svg/1f4e1.d.ts +2 -0
  66. package/lib/components/emoji/fluent/svg/1f4e1.js +5 -0
  67. package/lib/components/emoji/fluent/svg/1f4e2.d.ts +2 -0
  68. package/lib/components/emoji/fluent/svg/1f4e2.js +5 -0
  69. package/lib/components/emoji/fluent/svg/1f4e7.d.ts +2 -0
  70. package/lib/components/emoji/fluent/svg/1f4e7.js +5 -0
  71. package/lib/components/emoji/fluent/svg/1f500.d.ts +2 -0
  72. package/lib/components/emoji/fluent/svg/1f500.js +5 -0
  73. package/lib/components/emoji/fluent/svg/1f501.d.ts +2 -0
  74. package/lib/components/emoji/fluent/svg/1f501.js +5 -0
  75. package/lib/components/emoji/fluent/svg/1f504.d.ts +2 -0
  76. package/lib/components/emoji/fluent/svg/1f504.js +5 -0
  77. package/lib/components/emoji/fluent/svg/1f52d.d.ts +2 -0
  78. package/lib/components/emoji/fluent/svg/1f52d.js +5 -0
  79. package/lib/components/emoji/fluent/svg/1f552.d.ts +2 -0
  80. package/lib/components/emoji/fluent/svg/1f552.js +5 -0
  81. package/lib/components/emoji/fluent/svg/1f58b.d.ts +2 -0
  82. package/lib/components/emoji/fluent/svg/1f58b.js +5 -0
  83. package/lib/components/emoji/fluent/svg/1f5b2.d.ts +2 -0
  84. package/lib/components/emoji/fluent/svg/1f5b2.js +5 -0
  85. package/lib/components/emoji/fluent/svg/1f5c3.d.ts +2 -0
  86. package/lib/components/emoji/fluent/svg/1f5c3.js +5 -0
  87. package/lib/components/emoji/fluent/svg/1f5d1.d.ts +2 -0
  88. package/lib/components/emoji/fluent/svg/1f5d1.js +5 -0
  89. package/lib/components/emoji/fluent/svg/1f5dc.d.ts +2 -0
  90. package/lib/components/emoji/fluent/svg/1f5dc.js +5 -0
  91. package/lib/components/emoji/fluent/svg/1f604.d.ts +2 -0
  92. package/lib/components/emoji/fluent/svg/1f604.js +5 -0
  93. package/lib/components/emoji/fluent/svg/1f917.d.ts +2 -0
  94. package/lib/components/emoji/fluent/svg/1f917.js +5 -0
  95. package/lib/components/emoji/fluent/svg/1f9d1-200d-2696.d.ts +2 -0
  96. package/lib/components/emoji/fluent/svg/1f9d1-200d-2696.js +5 -0
  97. package/lib/components/emoji/fluent/svg/1fa79.d.ts +2 -0
  98. package/lib/components/emoji/fluent/svg/1fa79.js +5 -0
  99. package/lib/components/emoji/fluent/svg/1fa9d.d.ts +2 -0
  100. package/lib/components/emoji/fluent/svg/1fa9d.js +5 -0
  101. package/lib/components/emoji/fluent/svg/1fa9f.d.ts +2 -0
  102. package/lib/components/emoji/fluent/svg/1fa9f.js +5 -0
  103. package/lib/components/emoji/fluent/svg/23f3.d.ts +2 -0
  104. package/lib/components/emoji/fluent/svg/23f3.js +5 -0
  105. package/lib/components/emoji/fluent/svg/2611.d.ts +2 -0
  106. package/lib/components/emoji/fluent/svg/2611.js +5 -0
  107. package/lib/components/emoji/fluent/svg/2702.d.ts +2 -0
  108. package/lib/components/emoji/fluent/svg/2702.js +5 -0
  109. package/lib/components/emoji/fluent/svg/270f.d.ts +2 -0
  110. package/lib/components/emoji/fluent/svg/270f.js +5 -0
  111. package/lib/components/emoji/fluent/svg/2728.d.ts +2 -0
  112. package/lib/components/emoji/fluent/svg/2728.js +5 -0
  113. package/lib/components/emoji/fluent/svg/2b1c.d.ts +2 -0
  114. package/lib/components/emoji/fluent/svg/2b1c.js +5 -0
  115. package/lib/components/flashes/FlashClosable.js +2 -17
  116. package/lib/components/mcp/ClientBadge.js +1 -1
  117. package/lib/components/mcp/McpErrorBlankslate.js +1 -1
  118. package/lib/components/mcp/ScopeList.js +1 -1
  119. package/lib/components/notebooks/JupyterNotebook.js +1 -2
  120. package/lib/components/principal/Principal.js +2 -10
  121. package/lib/components/principal/PrincipalAppearance.d.ts +24 -1
  122. package/lib/components/principal/PrincipalAppearance.js +107 -80
  123. package/lib/components/principal/PrincipalAvatar.d.ts +5 -7
  124. package/lib/components/principal/PrincipalAvatar.js +12 -38
  125. package/lib/components/principal/PrincipalBadge.js +3 -13
  126. package/lib/components/principal/PrincipalBanner.js +4 -32
  127. package/lib/components/principal/PrincipalDetailsOverlay.d.ts +4 -7
  128. package/lib/components/principal/PrincipalDetailsOverlay.js +8 -24
  129. package/lib/components/principal/PrincipalHoverCard.js +15 -18
  130. package/lib/components/principal/PrincipalSwitcherMenu.js +15 -83
  131. package/lib/components/progress/ConsumptionBar.js +1 -1
  132. package/lib/components/sharing/ShareAccessComponent.js +21 -229
  133. package/lib/components/sharing/SharingEditor.js +1 -1
  134. package/lib/components/spaces/SpaceDetailsOverlay.js +4 -35
  135. package/lib/components/spaces/SpaceDisplay.js +1 -8
  136. package/lib/components/storage/ContentsBrowser.js +3 -26
  137. package/lib/components/storage/ContentsTable.js +4 -23
  138. package/lib/components/students/StudentResultCircle.js +7 -29
  139. package/lib/components/timeline/Timeline.js +4 -36
  140. package/lib/hooks/layouts/LayoutBackdrop.js +2 -1
  141. package/lib/hooks/layouts/LayoutScreencapture.js +1 -1
  142. package/lib/hooks/libraryApp.d.ts +6 -1
  143. package/lib/hooks/libraryApp.js +10 -1
  144. package/lib/hooks/unlinkIAMProvider.d.ts +22 -0
  145. package/lib/hooks/unlinkIAMProvider.js +24 -0
  146. package/lib/hooks/useAuthorization.js +6 -1
  147. package/lib/hooks/useCache.d.ts +8 -1
  148. package/lib/hooks/useCache.js +35 -4
  149. package/lib/hooks/useNavigate.js +8 -2
  150. package/lib/mocks/components/FlashMock.js +3 -8
  151. package/lib/mocks/views/DashboardMock.js +6 -72
  152. package/lib/mocks/views/FormMock.js +3 -2
  153. package/lib/mocks/views/TableMock.js +3 -2
  154. package/lib/mocks/views/WipMock.js +2 -1
  155. package/lib/models/IAMProviderLinked.d.ts +9 -0
  156. package/lib/models/IAMProviderLinked.js +34 -5
  157. package/lib/models/McpTask.d.ts +18 -1
  158. package/lib/models/McpTask.js +15 -0
  159. package/lib/models/Person.d.ts +9 -0
  160. package/lib/models/Person.js +23 -0
  161. package/lib/models/Profile.d.ts +2 -2
  162. package/lib/models/User.js +5 -12
  163. package/lib/models/UserOnboarding.d.ts +9 -0
  164. package/lib/models/UserOnboarding.js +33 -0
  165. package/lib/otel/views/OtelLive.js +4 -62
  166. package/lib/otel/views/OtelLiveSpans.js +4 -29
  167. package/lib/otel/views/OtelLogsList.js +13 -64
  168. package/lib/otel/views/OtelMetricsChart.js +4 -19
  169. package/lib/otel/views/OtelMetricsList.js +8 -77
  170. package/lib/otel/views/OtelSearchBar.js +3 -12
  171. package/lib/otel/views/OtelSpanDetail.js +9 -79
  172. package/lib/otel/views/OtelSpanTree.js +7 -57
  173. package/lib/otel/views/OtelSqlView.js +5 -99
  174. package/lib/otel/views/OtelSystemView.js +4 -34
  175. package/lib/otel/views/OtelTimeline.js +7 -37
  176. package/lib/otel/views/OtelTimelineRangeSlider.js +7 -140
  177. package/lib/otel/views/OtelTracesList.js +12 -76
  178. package/lib/portable/index.d.ts +9 -0
  179. package/lib/portable/index.js +14 -0
  180. package/lib/portable/states.d.ts +39 -0
  181. package/lib/portable/states.js +71 -0
  182. package/lib/state/substates/NavigationState.d.ts +10 -6
  183. package/lib/state/substates/NavigationState.js +18 -8
  184. package/lib/views/api-keys/APIKeyEdit.js +2 -11
  185. package/lib/views/api-keys/APIKeyNew.js +3 -19
  186. package/lib/views/api-keys/APIKeys.js +6 -11
  187. package/lib/views/api-keys/APIKeysStandalone.js +1 -1
  188. package/lib/views/datasources/DatasourceDetail.d.ts +1 -1
  189. package/lib/views/datasources/DatasourceDetail.js +24 -17
  190. package/lib/views/datasources/DatasourceNew.js +2 -6
  191. package/lib/views/datasources/Datasources.js +2 -14
  192. package/lib/views/iam/SignInSimple.js +8 -64
  193. package/lib/views/mcp/AdmittedClients.js +3 -16
  194. package/lib/views/mcp/AlertDestinations.js +3 -11
  195. package/lib/views/mcp/AlertRules.js +4 -9
  196. package/lib/views/mcp/ApprovalQueue.js +5 -22
  197. package/lib/views/mcp/AuditLog.js +7 -44
  198. package/lib/views/mcp/ConnectedAgents.js +1 -6
  199. package/lib/views/mcp/EnterpriseConsole.js +11 -24
  200. package/lib/views/mcp/IdentityProviders.js +7 -31
  201. package/lib/views/mcp/McpDashboard.js +15 -31
  202. package/lib/views/mcp/McpHome.js +8 -55
  203. package/lib/views/mcp/McpObservability.js +14 -86
  204. package/lib/views/mcp/NotebookRuns.js +6 -13
  205. package/lib/views/mcp/OrganizationPolicy.js +3 -9
  206. package/lib/views/mcp/OrganizationUsage.js +5 -12
  207. package/lib/views/mcp/PersonalPolicy.js +3 -3
  208. package/lib/views/mcp/Policies.js +6 -47
  209. package/lib/views/mcp/PolicyForm.js +1 -9
  210. package/lib/views/mcp/PolicyHistory.js +2 -8
  211. package/lib/views/mcp/RunDetail.d.ts +1 -1
  212. package/lib/views/mcp/RunDetail.js +7 -26
  213. package/lib/views/mcp/ScimProvisioning.js +4 -16
  214. package/lib/views/mcp/ServiceAgents.js +4 -17
  215. package/lib/views/mcp/TeamPolicies.js +7 -19
  216. package/lib/views/mcp/ToolAccess.js +16 -23
  217. package/lib/views/mcp/TraceTimeline.js +8 -23
  218. package/lib/views/otel/DashboardView.js +2 -2
  219. package/lib/views/otel/LogsView.js +2 -2
  220. package/lib/views/otel/MetricsView.js +2 -2
  221. package/lib/views/otel/OtelHeader.js +6 -40
  222. package/lib/views/otel/SqlView.js +2 -2
  223. package/lib/views/otel/SystemView.js +2 -2
  224. package/lib/views/otel/TracesView.js +2 -2
  225. package/lib/views/profile/UserBadge.js +6 -98
  226. package/lib/views/secrets/SecretEdit.js +2 -11
  227. package/lib/views/secrets/SecretNew.js +1 -1
  228. package/package.json +1 -1
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Loading, empty and problem states as a person is told them (MOBILE M-013b):
3
+ * what went wrong in their words, the diagnostic detail kept behind
4
+ * *Details* (never a token, a prompt or tool arguments, never a query value),
5
+ * whether trying again can help, and the words around them. A renderer on
6
+ * any platform draws these; it does not word them itself.
7
+ *
8
+ * @module portable/states
9
+ */
10
+ /** What went wrong, as a person is told it. */
11
+ export type ProblemKind = 'unauthenticated' | 'forbidden' | 'not-found' | 'conflict' | 'rate-limited' | 'unavailable' | 'timeout' | 'offline' | 'unexpected';
12
+ export declare const PROBLEM_WORDS: Record<ProblemKind, string>;
13
+ /** The words around a state: loading, nothing yet, trying again, the detail. */
14
+ export declare const STATE_WORDS: {
15
+ readonly loading: "Loading";
16
+ readonly empty: "Nothing yet.";
17
+ readonly retry: "Try again";
18
+ readonly details: "Details";
19
+ readonly hideDetails: "Hide details";
20
+ readonly showDetailsLabel: "Show diagnostic details";
21
+ readonly hideDetailsLabel: "Hide diagnostic details";
22
+ };
23
+ /** A failed request: the person's words, and the detail behind *Details*. */
24
+ export declare class Problem extends Error {
25
+ readonly kind: ProblemKind;
26
+ readonly status?: number;
27
+ /** For *Details* only; never a token, a prompt or tool arguments. */
28
+ readonly detail: string;
29
+ constructor(kind: ProblemKind, detail: string, status?: number);
30
+ }
31
+ export declare const isProblem: (error: unknown) => error is Problem;
32
+ /** A thrown value as a Problem: anything unknown is *unexpected*. */
33
+ export declare const problemOf: (error: unknown) => Problem;
34
+ /** The kind of an HTTP status that failed. */
35
+ export declare function kindOfStatus(status: number): ProblemKind;
36
+ /** Whether trying again can help: not for what is gone or not theirs. */
37
+ export declare const retries: (problem: Problem) => boolean;
38
+ /** The detail a server gave, without query values that could carry a token. */
39
+ export declare function detailOf(method: string, url: string, status: number, body: unknown): string;
@@ -0,0 +1,71 @@
1
+ /*
2
+ * Copyright (c) 2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ export const PROBLEM_WORDS = {
6
+ unauthenticated: 'Your session has ended. Sign in again.',
7
+ forbidden: 'You do not have access to this.',
8
+ 'not-found': 'This item is no longer available.',
9
+ conflict: 'This changed somewhere else. It has been refreshed.',
10
+ 'rate-limited': 'Too many requests. Try again in a moment.',
11
+ unavailable: 'Datalayer is not answering right now. Try again.',
12
+ timeout: 'This is taking too long. Try again.',
13
+ offline: 'You are offline. Connect to continue.',
14
+ unexpected: 'Something went wrong.',
15
+ };
16
+ /** The words around a state: loading, nothing yet, trying again, the detail. */
17
+ export const STATE_WORDS = {
18
+ loading: 'Loading',
19
+ empty: 'Nothing yet.',
20
+ retry: 'Try again',
21
+ details: 'Details',
22
+ hideDetails: 'Hide details',
23
+ showDetailsLabel: 'Show diagnostic details',
24
+ hideDetailsLabel: 'Hide diagnostic details',
25
+ };
26
+ /** A failed request: the person's words, and the detail behind *Details*. */
27
+ export class Problem extends Error {
28
+ kind;
29
+ status;
30
+ /** For *Details* only; never a token, a prompt or tool arguments. */
31
+ detail;
32
+ constructor(kind, detail, status) {
33
+ super(PROBLEM_WORDS[kind]);
34
+ this.name = 'Problem';
35
+ this.kind = kind;
36
+ this.detail = detail;
37
+ this.status = status;
38
+ }
39
+ }
40
+ export const isProblem = (error) => error instanceof Problem;
41
+ /** A thrown value as a Problem: anything unknown is *unexpected*. */
42
+ export const problemOf = (error) => isProblem(error)
43
+ ? error
44
+ : new Problem('unexpected', error instanceof Error ? error.message : String(error));
45
+ /** The kind of an HTTP status that failed. */
46
+ export function kindOfStatus(status) {
47
+ if (status === 401)
48
+ return 'unauthenticated';
49
+ if (status === 403)
50
+ return 'forbidden';
51
+ if (status === 404)
52
+ return 'not-found';
53
+ if (status === 409)
54
+ return 'conflict';
55
+ if (status === 429)
56
+ return 'rate-limited';
57
+ if (status === 502 || status === 503 || status === 504)
58
+ return 'unavailable';
59
+ return 'unexpected';
60
+ }
61
+ /** Whether trying again can help: not for what is gone or not theirs. */
62
+ export const retries = (problem) => problem.kind !== 'not-found' && problem.kind !== 'forbidden';
63
+ /** The detail a server gave, without query values that could carry a token. */
64
+ export function detailOf(method, url, status, body) {
65
+ const path = url.split('?')[0];
66
+ const said = body && typeof body === 'object'
67
+ ? (body.detail ??
68
+ body.message)
69
+ : undefined;
70
+ return `${method} ${path} → ${status}${typeof said === 'string' ? `: ${said}` : ''}`;
71
+ }
@@ -1,17 +1,21 @@
1
- /** The signed-in shell: the platform, the business cases, or the admin. */
2
1
  /**
3
- * The five shells of the signed-in application.
2
+ * The six shells of the signed-in application.
4
3
  *
5
4
  * `studio` is where an agentic UI application is composed, validated and
6
5
  * shipped — the default for somebody who has not chosen, and what the root
7
6
  * shows. `home` is the whole platform, shown as *Power*, at `/power`: the key
8
7
  * is kept so a cookie written before the Studio existed still names the
9
8
  * 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`.
9
+ * platform's administration. `apps` is the applications: AI applications
10
+ * built by spec, by drag and drop or in Python — the Studio's app pages under
11
+ * a sidebar of their own, every address under `/apps`, and the workspace at
12
+ * `/apps` itself. It was `loop`, at `/loop`: a cookie still saying `loop`
13
+ * names no shell and is ignored, the reader opening on the Studio.
14
+ * `research` is the research areas, each one a space of the
15
+ * `datalayer-research` organization, at `/research` (the public papers, read
16
+ * signed in, at `/research/papers/…` beside them).
13
17
  */
14
- export type NavigationView = 'studio' | 'home' | 'agentify' | 'admin' | 'loop';
18
+ export type NavigationView = 'studio' | 'home' | 'agentify' | 'research' | 'admin' | 'apps';
15
19
  /** The shells, and where each one opens when it has not been visited. */
16
20
  export declare const NAVIGATION_VIEW_HOMES: Record<NavigationView, string>;
17
21
  /** Well-known view identifiers for {@link NavigationState.tabsByView}. */
@@ -34,13 +34,16 @@ import { registerSessionState } from '../sessionEnd.js';
34
34
  /** The shells, and where each one opens when it has not been visited. */
35
35
  export const NAVIGATION_VIEW_HOMES = {
36
36
  studio: '/studio',
37
- // Not `/`: the root is the Studio's, so the Power shell has an address of
38
- // its own to be sent to.
39
- home: '/power',
37
+ // Not `/`: the root is the Studio's, so the Platform shell has an address
38
+ // of its own to be sent to. It was `/power` until 2026-10-09, when the
39
+ // shell took the name it is called by everywhere else.
40
+ home: '/platform',
40
41
  agentify: '/agentify',
42
+ // The research areas and their spaces.
43
+ research: '/research',
41
44
  admin: '/admin',
42
- // The applications: what a builder opens LOOP for.
43
- loop: '/loop/apps',
45
+ // The applications: what a builder opens the Apps shell for.
46
+ apps: '/apps/apps',
44
47
  };
45
48
  /** Well-known view identifiers for {@link NavigationState.tabsByView}. */
46
49
  export const LIBRARY_VIEW = 'library';
@@ -58,8 +61,9 @@ const COOKIE_MAX_AGE_DAYS = 365;
58
61
  const isView = (value) => value === 'studio' ||
59
62
  value === 'home' ||
60
63
  value === 'agentify' ||
64
+ value === 'research' ||
61
65
  value === 'admin' ||
62
- value === 'loop';
66
+ value === 'apps';
63
67
  function readCookie(name) {
64
68
  if (typeof document === 'undefined') {
65
69
  return undefined;
@@ -244,11 +248,17 @@ export function viewForRoute(route, current) {
244
248
  if (path === '/agentify' || path.startsWith('/agentify/')) {
245
249
  return 'agentify';
246
250
  }
251
+ // The research areas — and the papers, read signed in under
252
+ // `/research/papers/…`, with the areas' sidebar beside them.
253
+ if (path === '/research' || path.startsWith('/research/')) {
254
+ return 'research';
255
+ }
247
256
  if (path === '/admin' || path.startsWith('/admin/')) {
248
257
  return 'admin';
249
258
  }
250
- if (path === '/loop' || path.startsWith('/loop/')) {
251
- return 'loop';
259
+ // The Apps shell, and the hosted applications at `/apps/<slug>` with it.
260
+ if (path === '/apps' || path.startsWith('/apps/')) {
261
+ return 'apps';
252
262
  }
253
263
  return 'home';
254
264
  }
@@ -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;
@@ -88,12 +88,7 @@ const LiveDatasourcesTable = ({ datasourcesListRoute, spaceUid, showInlineLoadin
88
88
  if (datasourcesQuery.isError && datasources.length === 0) {
89
89
  return (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Visual, { children: _jsx(DatabaseIcon, { size: 24 }) }), _jsx(Blankslate.Heading, { children: "Datasources could not be listed" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: datasourcesQuery.error.message }) }), _jsx(Blankslate.PrimaryAction, { onClick: () => datasourcesQuery.refetch(), children: "Try again" })] }));
90
90
  }
91
- return showInitialSpinner ? (_jsx(Blankslate, { border: true, spacious: true, children: showInlineLoadingIndicator ? (_jsx(Box, { sx: {
92
- display: 'flex',
93
- alignItems: 'center',
94
- justifyContent: 'center',
95
- minHeight: '40px',
96
- }, children: _jsx(Spinner, {}) })) : (_jsxs(_Fragment, { children: [_jsx(Blankslate.Heading, { children: "Datasources" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "Loading datasources..." }) })] })) })) : (_jsx(DatasourcesTable, { datasources: datasources, datasourcesListRoute: datasourcesListRoute }));
91
+ return showInitialSpinner ? (_jsx(Blankslate, { border: true, spacious: true, children: showInlineLoadingIndicator ? (_jsx(Box, { display: "flex", alignItems: "center", justifyContent: "center", minHeight: "40px", children: _jsx(Spinner, {}) })) : (_jsxs(_Fragment, { children: [_jsx(Blankslate.Heading, { children: "Datasources" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "Loading datasources..." }) })] })) })) : (_jsx(DatasourcesTable, { datasources: datasources, datasourcesListRoute: datasourcesListRoute }));
97
92
  };
98
93
  /**
99
94
  * The Datasources of the Contents catalog.
@@ -104,14 +99,7 @@ const LiveDatasourcesTable = ({ datasourcesListRoute, spaceUid, showInlineLoadin
104
99
  */
105
100
  export const Datasources = ({ newDatasourceRoute = '/datasources/new', datasourcesListRoute, spaceUid, showInlineLoadingIndicator = true, mock = false, embedded = false, } = {}) => {
106
101
  const navigate = useNavigate();
107
- const body = (_jsxs(Box, { sx: { maxWidth: embedded ? undefined : 960, mx: 'auto', width: '100%' }, children: [_jsxs(Box, { sx: {
108
- display: 'flex',
109
- alignItems: 'flex-start',
110
- justifyContent: 'space-between',
111
- gap: 3,
112
- flexWrap: 'wrap',
113
- mb: embedded ? 2 : 4,
114
- }, children: [_jsx(Box, { children: !embedded && (_jsxs(_Fragment, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Datasources" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Databases, warehouses and query services your notebooks and agents query through Datalayer, without holding the credential." })] })) }), _jsx(Button, { size: "small", variant: "primary", leadingVisual: DatabaseIcon, disabled: mock, onClick: e => navigate(newDatasourceRoute, e), children: "Connect Datasource" })] }), mock ? (_jsx(DatasourcesTable, { datasources: DATASOURCES_MOCK.map(toDatasourceRow), mock: true })) : (_jsx(LiveDatasourcesTable, { datasourcesListRoute: datasourcesListRoute, spaceUid: spaceUid, showInlineLoadingIndicator: showInlineLoadingIndicator }))] }));
102
+ const body = (_jsxs(Box, { maxWidth: embedded ? undefined : 960, mx: "auto", width: "100%", children: [_jsxs(Box, { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 3, flexWrap: "wrap", mb: embedded ? 2 : 4, children: [_jsx(Box, { children: !embedded && (_jsxs(_Fragment, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Datasources" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Databases, warehouses and query services your notebooks and agents query through Datalayer, without holding the credential." })] })) }), _jsx(Button, { size: "small", variant: "primary", leadingVisual: DatabaseIcon, disabled: mock, onClick: e => navigate(newDatasourceRoute, e), children: "Connect Datasource" })] }), mock ? (_jsx(DatasourcesTable, { datasources: DATASOURCES_MOCK.map(toDatasourceRow), mock: true })) : (_jsx(LiveDatasourcesTable, { datasourcesListRoute: datasourcesListRoute, spaceUid: spaceUid, showInlineLoadingIndicator: showInlineLoadingIndicator }))] }));
115
103
  if (embedded) {
116
104
  return body;
117
105
  }